diff --git a/postfix/.indent.pro b/postfix/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/.printfck b/postfix/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/.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/0README b/postfix/0README new file mode 100644 index 000000000..0ba0a5255 --- /dev/null +++ b/postfix/0README @@ -0,0 +1,148 @@ +Purpose of this document +======================== + +This document provides a road map of the Postfix mail system source +code distribution. I suggest that you take a few minutes to read +it, and then proceed with the installation instructions. + +Introduction +============ + +This is the first public release of the Postfix mail system. Thank +you for your interest in this project. Send me a postcard if you +like it. My postal address is below. + +You must read the LICENSE file, if you didn't do so already. A copy +of the LICENSE must be distributed with every original, modified, +complete, source, or binary copy of this software or parts thereof. +I suggest that you keep a copy of the file in /etc/postfix/LICENSE. + +Purpose of the Postfix mail system +================================== + +Postfix aims to be an alternative to the widely-used sendmail +program. Sendmail is responsible for 70% of all e-mail delivered +on the Internet. With an estimated 100 million users, that's an +estimated 10 billion (10^10) messages daily. A stunning number. + +Although IBM supported the Postfix development, it abstains from +control over its evolution. The goal is to have Postfix installed +on as many systems as possible. To this end, the software is given +away with no strings attached to it, so that it can evolve with +input from and under control by its users. + +In other words, IBM releases Postfix only once. I will be around +to guide its development for a limited time. + +On-line resources devoted to the Postfix mail system +==================================================== + +Web sites: + + http://www.ibm.com/alphaworks/ the original distribution site + http://www.postfix.org/ post-release information + +Mail addresses (please do NOT send mail to my address at work): + + postfix-XXX@postfix.org Postfix mailing lists + wietse@porcupine.org the original author + +In order to subscribe to a mailing list, see http://www.postfix.org/. + +Acknowledgements +================ + +This release could not have happened without the input from a team +of competent alpha testers. Their names appear in numerous places +in the HISTORY file. I appreciate the input from my colleagues at +the IBM Global Security Analysis Laboratory: Paul Karger, Dave +Safford, Douglas Schales, and Leendert van Doorn. I also appreciate +the support by Charles Palmer under whose leadership I began this +project, and who had the privilege to name the software, twice. + +If you wish to express your appreciation for the Postfix software, +you are welcome to send a postcard to: + + Wietse Venema + IBM T.J Watson Research Center + P.O. Box 704, + Yorktown Heights, NY 10598 + USA + +Roadmap of the Postfix source distribution +========================================== + +Point your browser at html/index.html for Postfix documentation, +for manual pages, and for the unavoidable Postfix FAQ. Expect to +see updated versions on-line at http://www.postfix.org/ + +Point your MANPATH environment variable at the `man' directory (use +an absolute path) for UNIX-style on-line manual pages. These pages +are also available through the HTML interface, which allows you to +navigate faster. + +The COMPATIBILITY file lists features that Postfix does or does +not yet implement, and how well it works with other software. + +The HISTORY file gives a detailed log of changes to the software. + +The INSTALL file provides a step-by-step guide for building and +installing Postfix on many popular UNIX platforms. + +The PORTING file discusses how to go about porting Postfix to other +UNIX platforms. Some people are looking into a port to Windows NT. +We'll see. This software uses every trick in the book that I learned +about UNIX. + +The TODO file lists things that still need to be done. If you want +to set your teeth into one of those problems, drop me a note at +wietse@porcupine.org to avoid duplication of effort. + +Documentation: + + html/ HTML format + man/ UNIX on-line manual page format + +Library routines: + + dns/ DNS client library + global/ Postfix-specific support routines + util/ General-purpose support routines + +Command-line utilities: + + postalias/ Alias database management + postcat/ List Postfix queue file + postconf/ Configuration utility + postfix/ Postfix administrative interface + postkick/ Postfix IPC for shell scripts + postlock/ Postfix locking for shell scripts + postlog/ Postfix logging for shell scripts + postmap/ Postfix lookup table management + sendmail/ Sendmail compatibility interface + +Postfix daemons: + + bounce/ Bounce or defer mail + cleanup/ Canonicalize and enqueue mail + local/ Local delivery + master/ Postfix resident superserver + pickup/ Local pickup + pipe/ Pipe delivery + qmgr/ Queue manager + showq/ List Postfix queue status + smtp/ SMTP client + smtpd/ SMTP server + trivial-rewrite/ Address rewriting and resolving + +Test programs: + + fsstone/ Measure file system overhead + smtpstone/ SMTP server torture test + +Miscellaneous: + + bin/ Installed programs + conf/ Sample configuration files + include/ Installed include files + lib/ Installed object libraries diff --git a/postfix/COMPATIBILITY b/postfix/COMPATIBILITY new file mode 100644 index 000000000..1cb895c60 --- /dev/null +++ b/postfix/COMPATIBILITY @@ -0,0 +1,55 @@ +.forward yes (empty files; can enable/disable mail to /file or |command) +/usr/mail yes (compile time option) +/usr/spool/mail yes (compile time option) +/var/mail yes (compile time option) +/var/spool/mail yes (compile time option) +:include: yes (mail to /file and |command is off by default) +aliases yes (can enable/disable mail to /file or |command) +bare newlines yes (but will send CRLF) +blacklisting yes (client name/addr; helo hostname; mail from; rcpt to) +content filter no +db tables yes (compile time option) +dbm tables yes (compile time option) +delivered-to yes +dsn not yet +errors-to: yes +esmtp yes +etrn support yes (flushes entire queue) +fcntl locking yes (compile time) +flock locking yes (compile time) +home mailbox yes +ident lookup no +ldap tables yes (contributed) +luser relay not yet +m4 config no +mail to command yes (configurable for .forward, aliases, :include:) +mail to file yes (configurable for .forward, aliases, :include:) +maildir yes (with procmail) +mailertable yes (it's called transport) +mailq yes +majordomo yes (edit approve script to delete /delivered-to/i) +mime conversion not yet; postfix uses just-send-eight +missing <> yes (most common address forms) +netinfo tables yes (contributed) +newaliases yes (main alias database only) +nis tables yes +nis+ tables not yet +pipeline option yes (server and client) +pop/imap yes (with third-party daemons that use /var[/spool]/mail) +rbl support yes +return-receipt: not yet +sendmail -q yes +sendmail -qRxxx no +sendmail -qSxxx no +sendmail -qtime ignored +sendmail -v no +sendmail.cf no (uses table-driven address rewriting) +size option yes, server and client +smarthost yes +tcp wrapper no (use built-in blacklist facility) +user+extension yes (also: .forward+extension) +user-extension yes (also: .forward-extension) +user.lock yes (compile time) +uucp support yes (sends user@domain recipients) +virtual domains yes +year 2000 safe yes diff --git a/postfix/HISTORY b/postfix/HISTORY new file mode 100644 index 000000000..468f02cc5 --- /dev/null +++ b/postfix/HISTORY @@ -0,0 +1,2001 @@ +In addition to the names listed below, the following people provided +useful inputs on many occasions: Paul D. Robertson, Simon J. Mudd. +Apologies for any names omitted. + +19980105 + + The compiled-in default value for resolve_smtp_sender was + wrong (from the days that it was a boolean), causing smtpd + to dump core when the variable was not set in main.cf. + + The INSTALL instructions now have separate sections for + the three basic ways of running vmailer. + + The INSTALL instructions now have discusses how to deal + with chrooted processes. + + Ported to RedHat 5.0. My, these people have re-organized + their include files quite a bit, haven't they. + +19980106 + + On RedHat Linux 4.2/5.0, when a FIFO listener opens the + FIFO with mode O_RDONLY, the FIFO remains forever readable + after the writer has closed it. Workaround: open the FIFO + mode O_RDWR. + + Test program: util/fifo_rdonly_bug.c + + Unfortunately, the above fix triggers a bug on BSD/OS 3.1 + where opening the FIFO mode O_RDWR causes select() to claim + that the FIFO is readable even before any data is written + to it, causing read() to block or to fail. + + Test program: util/fifo_rdwr_bug.c + + printfck (check arguments of printf-like function calls) + found a missing argument in local/command.c + + Miscellaneous Makefile cleanups that I didn't finish before + the first alpha release. + +19980107 + + Sometimes the DNS will claim that a domain does not exist, + when in fact it does. Thus, it is a bad idea to reject mail + from apparently non-existent domains. I have changed the + smtpd so that it produces a soft error responses when a + resolve_smtp_sender test fails with HOST_NOT_FOUND. Note: + by default, this test is still disabled. + + The DB and DBM read routines will now automagically figure + out if (key, value) pairs were written including a terminating + null byte or not. The DB and DBM write routines will use + this result to determine how to write, and will fall back + to per-system defaults otherwise. + + Renamed the README to MUSINGS, and wrote up a README that + reflects the current status of the software. + + Added -d (don't disconnect) and -c (show running counter) + option to te smtp-source test program. These tools are + great torture tests for the mail software, and for the + system that it runs on. + + Turned down the process_limit parameter (# of parallel smtp + clients or servers) to avoid unpleasant surprises. You can + crank up the process_limit parameter in main.cf. + +19980111 + + Feature: when run by the superuser, mailq now shows the + mail queue even when the mail system is down. To this end, + mailq (sendmail -bp) runs the showq program directly instead + of connecting to the UNIX-domain service socket, and drops + privileges etc. as usual. + +19980119 + + Bugfix: Edwin Kremer spotted an oversight in the negated + host matching code (for name or address patterns prefixed + by !). + + Bugfix: upon receipt of a SIGHUP signal, the master now + disconnects from its child processes, so that the current + generation of child processes commits suicide, and so that + the next generation of child processes will use the new + configuration settings. + + Bugfix: the smtp server now skips the sender DNS domain + lookup test for foo@[address] + + Bugfix: don't append the local domain to foo@[address] + +19980120 + + Bugfix: old low-priority bug in some list walk code that + caused the master to drop core when a service was turned + off in master.cf. + + Robustness: the mail system should be able to start up and + to accept local postings even while the naming service is + down. For this reason, the mail system no longer uses + gethostbyname() to look up its own machine name. Sites + that use short hostnames will have to specify their FQDN + in main.cf (this will eventually be done by the system + installation/configuration procedure). Should the config + language support backtics so one can say `domainname`? What + about $name stuff between the backtics? + + Security: the master now creates FIFOs and UNIX-domain + sockets as the mail owner instead of as root, for better + protection against subverted mail systems. chmod() is + susceptible to race conditions. fchmod(), although safer, + often does not work on sockets. + + Portability: anticipate that all major UNIXes will create + UNIX-domain sockets with permissions modified by the process + umask (required by POSIX). For this reason, we always + chmod() UNIX-domain sockets, unless the system allows us + to use the safer fchmod() instead. + + Portability: the semi-resident servers now properly handle + EWOULDBLOCK returns from accept() in addition to EGAIN + (on some systems, EAGAIN and EWOULDBLOCK have different + values). + + Bugfix: the semi-resident servers now properly handle EINTR + returns From accept(). + + Bugfix: Edwin Kremer found that mynetworks() would compute + (32 - mask) instead of mask. + +19980121 + + Feature: /etc/vmailer/relocated is used by the local delivery + program and specifies what mail should be bounced with a + "user has moved to XXX" message. The main.cf configuration + parameter is "relocated_maps". Just like the "virtual_maps" + config parameter, this feature is off by default, and the + parameter can have values such as "files" or "files, nis" + (on hosts equipped with NIS). + +19980123 + + Cleanup: virtual domain support moved from the queue manager + to the resolve service, where it belongs. + + Feature: /etc/vmailer/canonical is used by the rewrite + service for all addresses, and maps a canonical address + (user@domain) to another address. Typical use is to generate + Firstname.Lastname@domain addresses, or to clean up dirty + addresses from non-RFC 822 mail systems. The main.cf + configuration parameter is "canonical_maps". Just like + the "virtual_maps" config parameter, this feature is off + by default, and the parameter can have values such as + "files" or "files, nis" (on hosts equipped with NIS). + +19980124 + + HPUX10 port and many little fixes from Pieter Schoenmakers. + + Bugfix: isolated an old mysterious bug that could make the + master deaf for new connections while no child process was + running. A typical result was that no pickup daemon would + be started after the previous one had terminated voluntarily. + + Bugfix: the NIS lookup code did not mystrdup() the NIS map + name and would access free()d memory. + +19980125 + + Bugfix: the vstream routines would sometimes ignore flushing + errors. The error would still be reported by vstream_fclose() + and vstream_ferror(). + + Feature: time limit on delivery to shell commands. Config + parameter: command_time_limit. Default value: 100 sec. The + idea is to prevent one bad .forward file or alias file + entry from slowly using up all local delivery process slots. + +19980126 + + Code cleanup: in preparation for SMTP extensions such as + SIZE, allow an extended SMTP command to have a variable + number of options. + +19980127 + + Bugfix: moved canonical map lookups away from the rewriting + module to the cleanup service, so that canonical map lookups + do not interfere with address rewriting on behalf of other + programs. Back to an older trivial-rewrite program version. + + Bugfix: moved virtual map lookups away from the resolver + back to the queue manager, so that virtual domain lookup + does not interfere with address resolution on behalf of + other programs. Back to an older qmgr program version. + +19980131 + + Feature: integrated and adapted Guido van Rooij's SIZE + option (RFC 1870), carefully avoiding potential problems + due to overflow (by multiplying large numbers) or unsigned + underflow (by subtracting numbers). + + Code cleanup: cleaned up the code that parses the server + response to the HELO/EHLO command, so that we can more + reliably recognize what options a server supports. + +19980201 + + Portability: integrated the IRIX 6 port by Oved Ben-Aroya. + + Portability: the software now figures out by itself if a + server should open its FIFO read-write or read-only, to + avoid getting stuck with a FIFO that stays readable forever. + + Bugfix: the cleanup service would terminate with a fatal + vstream_fseek() error when the queue file was too large. + + Bugfix: the cleanup service could be killed by a signal + when the queue file became too large. + +19980203 + + Portability: some systems have statfs(), some have statvfs(), + and the relevant include files are in a different place on + almost every system. + + Portability: the makedefs script now nukes the -O compiler + flag when building on AIX with IBM's own compiler... + +19980204 + + Portability: HP-UX 9.x support by Pieter Schoenmakers. + + Portability: added SYSV-style ulimit() file size limit + support for HP-UX 9.x. + + Portability: added some #includes that appeared to be + missing according to the Digital UNIX cc compiler. + + Bugfix: sys_defs.h now correctly specifies NIS support for + LINUX2, HPUX9 and HPUX10. + + Security: fixed a file descriptor leak in the local delivery + agent that could give shell commands access to the VMailer + IPC streams. This should not cause a vulnerability, given + the design and implementation of the mailer, but it would + be like asking for trouble. + + Bugfix: the sendmail -B (body type) option did not take a + value. + +19980205 + + Bugfix (SUNOS5): should not have deleted the SVID_GETTOD + definition from util/sys_defs.h. + + Bugfix (HPUX9): forgot to specify whether to use statfs() + or statvfs(). + + Bugfix (HPUX9): don't try to raise the file size ulimit. + + Bugfix (HPUX9): must specify file size limit in 512-blocks. + +19980207 + + Robustness: the master process now raises the file size + limit when it is started with a limit that is less than + VMailer's file size limit. File: util/file_limit.c. + + Security: the dns lookup routines now screen all result + names with valid_hostname(). Bad names are treated as + transient errors. + + Feature: qmail compatibility: when the home_mailbox parameter + is set, mail is delivered to ~/$home_mailbox instead of to + /var[/spool]/mail/username. This hopefully makes it easier + to lure people away from qmail :-) + + Robustness: several testers by accident configured relayhost + the same as myhostname. The programs now explicitly check + for this mistake. + + Bugfix: deliver_request_read() would free unallocated memory + when it received an incomplete delivery request from the + queue manager. + + Robustness: local_destination_concurrency=1 prevents parallel + delivery to the same user (with possibly disastrous effects + when that user has an expensive pipeline in the .forward + or procmail config file). Each transport can have its own + XXX_destination_concurrency parameter, to limit the number + of simultaneous deliveries to the same destination. + +19980208 + + Robustness: added "slow open" mode, to gradually increase + the number of simultaneous connections to the same site as + long as delivery succeeds, and to gradually decrease the + number of connections while delivery fails. Brad Knowles + provided the inspiration to do this. + + This also solves the "thundering herd" problem (making a + bunch of connections to a dead host when it was time to + retry that host). Let's see when other mailers fix this. + + Feature: Added $smtpd_banner and $mail_version, for those + who want to show the world what software version they are + running. + + Bugfix: vmailer-script now properly labels each syslog + entry. + +19980210 + + Portability: merged in NEXTSTEP 3 port from Pieter Schoenmakers + + Bugfix: the local delivery program now checks that a + destination is a regular file before locking it. + +19980211 + + Robustness: the local delivery agent sets HOME, LOGNAME, + and SHELL when delivering to a user shell command. PATH is + always set, and TZ is passed through if it is set. + +19980212 + + Feature: mailq (sendmail -bp) now also lists the maildrop + queue (with mail that hasn't been picked up yet). + +19980213 + + Feature: the smtpd now says: 502 HELP not implemented. This + should impress the heck out of the competition :-) + +19980214 + + Feature: local delivery to configurable system-wide command + (e.g. procmail) avoids the need for per-user ~/.forward + shell commands. Config parameter: mailbox_command. + +19980215 + + Performance: avoid running a shell when a command contains + no shell magic characters or built-in shell commands. This + speeds up delivery to all commands. File: util/exec_command.c. + + Bugfix: the local delivery agent, after reading EOF from + a child process, now sends SIGKILL only when the child does + not terminate within a limited amount of time. This avoids + some problems with procmail. File: util/timed_wait.c. + +19980217 + + Portability: folded in NetInfo support from Pieter + Schoenmakers. + +19980218 + + Feature: new vmlock command to run a command while keeping + an exclusive lock on a mailbox. + + Feature: with "recipient_delimiter = +", mail for local + address "user+foo" is delivered to "foo", with a "Delivered-To: + user+foo@domain" message header. Files: qmgr/qmgr_message.c, + local/recipient.c. This must be the cheapest feature. + +19980219 + + Code cleanup: moved error handling into functions that + should always succeed (non_blocking(), close_on_exec()). + +19980223 + + Bugfix: null pointer bug in the cleanup program after + processing a From: header with no mail address (or with + only a comment). + +19980226 + + Robustness: now detects when getpwnam() returns a name that + differs from the requested name. + + Feature: Added %p support to the vbuf_print formatting + module. + + Code cleanup: revamped the alias/include/.forward loop + detection and duplicate suppression code in the local + delivery agent. This must be the fourth iteration, and + again the code has been simplified. + +19980228 + + Robustness: don't treat anything starting with whitespace + as a header record. Instead, explicitly test for leading + whitespace where we permit it. Files: global/is_header.c, + bounce/bounce_flush_service.c, local/delivered.c. + +19980301 + + Compatibility: the sendmail program now accepts the -N + command-line option (delivery status notification) but + ignores it entirely, just like many other sendmail options. + + Bugfix: dns_lookup.c was too conservative with buffer sizes + and would incorrectly report "malformed name server reply". + +19980302 + + Bugfix: the local delivery agent was not null-byte clean. + +19980307 + + Feature: integrated Pieter Schoenmaker's code for transport + lookup tables that list (transport, nexthop) by destination. + +19980309 + + Bugfix: delivery agents no longer rename corrupt queue + files, because programs might fall over each other doing + so. Instead, when a delivery agent detects queue file + corruption, it chmods the queue file, simulates a soft + error, and lets the queue manager take care of the problem. + + Bugfix: the SMTP server implemented VRFY incorrectly. + + Feature: first shot at a pipe mailer, which can be used to + extend VMailer with external mail transports such as UUCP + (provided that the remote site understands domain addressing, + because VMailer version 1 does not rewrite addresses). + + Cleanup: extended the master/child interface so that the + service name (from master.cf) is passed on to the child. + The pipe mailer needs the service name so it can look up + service-specific configuration parameters (privilege level, + recipient limit, time limit, and so on). + +19980310-12 + + Cleanup: factored out the pipe_command() code, so it can + be shared between pipe mailer and local delivery agent. + +19980314 + + Compatibility: the sendmail program now parses each + command-line recipient as if it were an RFC 822 message + header; some MUAs specify comma-separated recipients in a + command-line argument; and some MUAs even specify "word + word
" forms as command-line arguments. + +19980315 + + Bugfix: VMailer's queue processing randomization wasn't + adequate for unloaded systems with small backlogs. + + Bugfix: smtpd now uses double-buffered stream I/O to prevent + loss of input sent ahead of responses. + +19980316 + + Bugfix: the smtpd anti-relay code didn't treat all hosts + listed in $mydestinations as local, so it would accept mail + only for hosts listed in $relay_domains (default: my own + domain). + + Bugfix: smtpd now replies with 502 when given an unknown + command. + +19980318 + + Cleanup: resolve/rewrite clients now automatically disconnect + after a configurable amount of idle time (ipc_idle). + +19980322 + + Tolerance: VRFY now permits user@domain, even though the + RFC requires that special characters such as @ be escaped. + +19980325 + + Bugfix: a recipient delimiter of "-" could interfere with + special addresses such as owner-xxx or double-bounce. + + Tolerance: the SMTP client now permits blank lines in SMTP + server responses. + + Tolerance: the SMTP client now falls back to SMTP when it + apparently mistook an SMTP server as ESMTP capable. + + Bugfix: eliminated strtok() calls in favor of mystrtok(). + Symptom: master.cf parsing would break if $inet_interfaces + was more than one word. + +19980328 + + Bugfix: user->addr patterns in canonical and virtual tables + matched only $myorigin, not hosts listed in $mydestination + or addresses listed in $inet_interfaces. The man pages + were wrong too. File: global/addr_match.c. + +19980401 + + Robustness: FIFO file permissions now default to 0622. On + some systems, opening a FIFO read-only could deafen the + pickup daemon. Only the listener end (which is opened as + root) needs read access anyway, so there should not be a + loss of functionality by making FIFOs non-readable for + non-mail processes. + +19980402 + + Compatibility: sendmail -I and -c options added. + +19980403 + + Feature: virtual lookups are now recursive. File: + qmgr/qmgr_message.c + +19980405 + + Implemented sendmail -bs (stand-alone) mode. This mode runs + as the user and therefore deposits into the maildrop queue. + +19980406 + + The pickup service now removes malformed maildrop files. + +19980407 + + The pickup service now guards against maildrop files with + time stamps dated into the future. + +19980408 + + Bugfix: in the canonical and virtual maps, foo->address + would match foo@$myorigin only. This has been fixed to also + match hosts listed in main.cf:$mydestination and the + addresses listed in main.cf:$inet_interfaces. + + Bugfix: added double buffering support to the VMailer SMTP + server. This makes the SMTP server robust against SMTP + clients that talk ahead of time, and should have been in + there from day one. + +19980409 + + Bugfix: the VMailer SMTP client now recognizes its own + hostname in the SMTP greeting banner only when that name + appears as the first word on the first line. + +19980410 + + Feature: smtpd now logs the local queue ID along with the + client name/address, and pickup now logs the local queue + ID along with the message owner. + + Bugfix: still didn't do virtual/canonical lookups right + (code used the non-case-folded key instead of the case + folded one). + +19980418 + + Bugfix: the SMTP server did not flush the "250 OK queued + as XXXX" message from the SMTP conversation history. + +19980419 + + Bugfix: qmgr would not notice that a malformed message has + multiple senders, and would leak memory (Tom Ptacek). + +19980421 + + Portability: in the mantools scripts, the expr pattern no + longer has ^ at the beginning, and the scripts now use the + expand program instead of my own detab utility. + +19980425 + + NetBSD 1.x patch by Soren S. Jorvang. + +19980511 + + Feature: the SMTP server now logs the protocol (SMTP or + ESMTP) as part of the Received: header. + + Feature: smtpd now logs the last command when a session is + aborted due to timeout, unexpected EOF, or too many client + errors. + +19980514 + + Bugfix: the queue manager did not update the counter for + in-core message structures, so the in-core message limit + had no effect. This can be bad when you have a large backlog + with many messages eligible for delivery. + + Robustness: the queue manager now also limits the total + number of in-core recipient structures, so that it won't + use excessive amounts of memory on sites that have large + mailing lists. + +19980518 + + Bugfix: the SMTP client did not notice that the DNS client + received a truncated response. As a result, a backup MX + host could incorrectly claim that it was the best MX host + and declare a mailer loop. + + Added start_msg/stop_msg entries to the vmailer startup + script, for easy installation. + + Cleanup: VMailer databases are now explicitly specified as + type:name, for example, hash:/etc/aliases or nis:mail.aliases, + instead of implicitly as "files", "nis" and so on. Test + program: util/dict_open. This change allowed me to + eliminate a lot of redundant code from mkmap_xxx.c, and + from everything that does map lookups. + +19980525 + + Bugfix: local/dotforward.c compared the result of opening + a user's ~/.forward against the wrong error value. + +19980526 + + Bugfix: the smtpd VRFY command could look at free()d memory. + + Robustness: the smtpd program had a fixed limit on the + number of token structures. The code now dynamically + allocates token structures. + + Bugfix: the queue manager still used the deprecated parameter + name xxx_deliver_concurrency for concurrency control, but + the documentation talks about the preferred parameter name + xxx_destination_concurrency. Fix: try xxx_destination_concurrency + first, then fall back to xxx_deliver_concurrency. + +19980621-19980702 + + Cleanup: the string read routines now report the last + character read or VSTREAM_EOF. This change is necessary + for the implementation of the long SMTP line bugfix. + + Bugfix: the smtp server exited the DATA command prematurely + when the client sent long lines. Reason: the smtp server + did not remember that it broke long lines, so that '.' + could appear to be the first character on a line when in + fact it wasn't. + + Bugfix: the queue manager made lots of stupid errors while + reading $qmgr_message_recipient_limit chunks of recipients + from a queue file. This code has been restructured. + +19980706 + + Performance: the cleanup program now always adds return-receipt + and errors-to records to a queue file, so that the queue + manager does not have to plow through huge lists of + recipients. + + Robustness: the initial destination concurrency now defaults + to 2, so that one bad message or one bad connection does + not stop all mail to a site. The configuration parameter + is called initial_destination_concurrency. + + Performance: the per-message recipient limit is now enforced + by the queue manager instead of by the transport. Thus, a + large list of recipients for the same site is now mapped + onto several delivery requests which can be handled in + parallel, instead of being mapped onto one delivery request + that is sent to limited numbers of recipients, one group + after the other. + +19980707 + + Cleanup: the queue manager now does an additional recipient + sort after the recipients have been resolved, so that the + code can do better aggregation of recipients by next hop + destination. + + Feature: lines in the master.cf file can now be continued + in the same manner as lines in the main.cf file, i.e. by + starting the next line with whitespace. + + Feature: the smtp client now warns that a message may be + delivered multiple times when the response to "." is not + received (the problem described in RFC 1047). + + Cleanup: when the queue manager changes its little mind + after contacting a delivery agent (for example, it decides + to skip the host because a transport or host goes bad), + the delivery agent no longer complains about premature EOF. + File: global/deliver_request.c + +19980709 + + Bugfix: when breaking long lines, the SMTP client did not + escape leading dots in secondary etc. line fragments. Fix: + don't break lines. This change makes VMailer line-length + transparent. Files: global/smtp_stream.c, smtp/smtp_proto.c. + +19980712 + + Cleanup: the queue manager to deliver agent protocol now + distinguishes between domain-specific soft errors and + recipient-specific soft errors. Result: many soft errors + with SMTP delivery no longer affect other mail the same + domain. + +19980713 + + Feature: the file modification time stamp of deferred queue + files is set to the nearest wakeup time of their recipient + hosts, or if delivery was deferred due to a non-host problem, + the time stamp is set into the future by the configurable + minimal backoff time. + + Bugfix: the SMTP client and the MAILQ command would report + as message size the total queue file size. That would + grossly overestimate the size of a message with many + recipients. + + Bugfix: the 19980709 fix screwed up locally-posted mail + that didn't end in newline. + +19980714 + + Robustness: the makedefs script now defaults to no optimization + when compiling for purify. + +19980715 + + Robustness: the makedefs script now defaults to no optimization + when compiling with gcc 2.8, until this compiler is known + to be OK. + + Workaround: when sending multiple messages over the same + SMTP connection, some SMTP servers need an RSET command + before the second etc. MAIL FROM command. The VMailer SMTP + client now sends a redundant RSET command just in case. + + The queue manager now logs explicitly when delivery is + deferred because of a "dead" message transport. + +19980716 + + Feature: mailq and mail bounces now finally report why mail + was deferred (the reason was logged to the syslog file + only). Changes were made to the bounce service (generalized + to be usable for defer logs), showq service (to show reasons) + and the queue manager. + + As a result the defer directory (with one log per deferred + message) may contain many files; also, this directory is + accessed each time a message is let into the active queue, + in order to delete its old defer log. This means that hashed + directories are now a must. + +19980718-20 + + Feature: configurable timeout for establishing smtp + connections. Parameter: smtp_connect_timeout (default 0, + which means use the timeout as wired into the kernel). + Inspired by code from Lamont Jones. For a clean but far + from trivial implementation, see util/timed_connect.c + + Cleaned up the interfaces that implement read/write deadlines. + Instead of returning -2, the routines now set errno to + ETIMEDOUT; the readable/writable tests are now separate. + +19980722 + + Feature: the default indexed file type (hash, btree, dbm) + is now configurable with the "database_type" parameter. + The default value for this parameter is system specific. + + Feature: selectively turn on verbose logging for hosts that + match the patterns specified via the "debug_peer_list" + config parameter. Syntax is like the "bad_smtp_clients" + parameter (see global/peer_list.c). The verbose logging + level is specified with "debug_peer_level" (default 2). + + Security: the local delivery agent no longer delivers to + files that have execute permission enabled. + +19980723 + + Workarounds for Solaris 2.x UNIX-domain sockets: they lose + data when you close them immediately after writing to them. + This could screw up the delivery agent to queue manager + protocol. + +19980724 + + Cleanup: spent most of the day cleaning up queue manager + code that defers mail when a site or transport dies, and + fixed a few obscure problems in the process. + +19980726 + + Feature: the admin can now configure what classes of problems + result in mail to the postmaster. Configuration parameter: + "notify_classes". Default is backwards compatible: bounce, + policy, protocol, resource, and software. + +19980726-28 + + Feature: the admin can now configure what smtp server access + control restrictions must be applied, and in what order. + Configuration parameters: smtpd_client_restrictions, + smtpd_helo_restrictions, smtpd_mail_restrictions and + smtpd_rcpt_restrictions. Defaults are intended to be + backwards compatible. The bad_senders and bad_clients lists + are gone and have become db (dbm, nis, etc) maps. Files: + smtpd/smtpd_check.c, config/main.cf. + +1998029-31 + + Feature: hashed queues. Rewrote parts of the mail queue + API. Configuration parameters: "hash_queue_names" specifies + what queue directories will be hashed (default: the defer + log drectory), "hash_queue_depth" specifies the number of + subdirectories used for hashing (default 2). + +19980802 + + Bugfix: the pipe mailer should expand command-line arguments + with $recipient once for every recipient (producing one + command-line argument per recipient), instead of replacing + $recipient by of all recipients (i.e. producing only one + command-line argument). This is required for compatibility + with programs that expect to be run from sendmail, such as + uux. Thanks to Ollivier Robert for helping me to get this + right. + + Code cleanup: for the above, cleaned up the macro expansion + code in dict.c and factored out the parsing into a separate + module, mac_parse.c. + +19980803 + + "|command" and /file/name destinations in alias databases + are now executed with the privileges of the database owner + (unless root or vmailer). Thus, with: "alias_maps = + hash:/etc/aliases, hash:/home/majordomo/aliases", and with + /home/majordomo/aliases* owned by the majordomo account, + you no longer need the majordomo set-uid wrapper program, + and you no longer need root privileges in order to install + a new mailing list. + +19980804 + + Added support for the real-time blackhole list. Example: + "client_restrictions = permit_mynetworks, reject_maps_rbl" + + All SMTP server "reject" status codes are now configurable: + unknown_client_reject_code, mynetworks_reject_code, + invalid_hostname_reject_code, unknown_hostname_reject_code, + unknown_address_reject_code, relay_domains_reject_code, + access_map_reject_code, maps_rbl_reject_code. Default values + are documented in the smtpd/smtpd_check.c man page. + +19980806-8 + + Code cleanup: after eye balling line-by line diffs, started + deleting code that duplicated functionality because it was + at the wrong abstraction level (smtp_trouble.c), moved + functionality that was in the wrong place (dictionary + reference counts in maps.c instead of dict.c), simplified + code that was too complex (password-file structure cache) + and fixed some code that was just wrong. + +19980808 + + Robustness: the number of queue manager in-core structures + for dead hosts is limited; the limit scales with the limit + on the number of in-core recipient structures. The idea is + to not run out of memory under conditions of stress. + +19980809 + + Feature: mail to files and commands can now be restricted + by class: alias, forward file or include file. The default + restrictions are: "allow_mail_to_files = alias, forward" + and allow_mail_to_commands = alias, forward". The idea is + to protect against buggy mailing list managers that allow + intruders to subscribe /file/name or "|command". + +19980810-12 + + Cleanup: deleted a couple hundred lines of code from the + local delivery agent. It will never be a great program; + sendmail compatibility is asking a severe toll. + +19980814 + + Cleanup: made the program shut up about some benign error + conditions that were reported by Daniel Eisenbud. + +19980814-7 + + Documentation: made a start of HTML docs that describe all + configuration parameters. + + Feature: while documenting things, added smtpd_helo_required. + +19980817 + + Bugfix: at startup the queue manager now updates the time + stamps of active queue files some time into the future. + This eliminates duplicate deliveries after "vmailer reload". + + Bugfix: the local delivery agent now applies the recipient + delimiter after looking in the alias database, instead of + before. + + Documentation bugfixes by Matt Shibla, Tom Limoncelli, + Eilon Gishri. + +19980819 + + GLIBC fixes from Myrdraal. + + Bugfix: applied showq buffer reallocation workaround in + the wrong place. + + Bugfix: can't use shorts in varargs lists. SunOS 4 has + short uid_t and gid_t. pipe_command() would complain. + + Bugfix: can't use signed char in ctype macros. All ctype + arguments are now casted to unsigned char. Thanks, Casper + Dik. + +19980820 + + Bugfix: save the alias lookup result before looking up the + owner. The previous alpha release did this right. + + Cleanup: mail_trigger() no longer complains when the trigger + FIFO or socket is unavailable. This change is necessary to + shut up the sendmail mail posting program, so that it can + be used on mail clients that mount their maildrop via NFS. + + Experiment: pickup and pipe now run as vmailer most of the + time, and switch to user privileges only temporarily. + Files: util/set_eugid.c global/pipe_command.c pipe/pipe.c + pickup/pickup.c. Is this more secure/ What about someone + manipulating such a process while not root? It still has + ruid == 0. + +19980822 + + Portability: with GNU make, commands such as "(false;true)" + and "while :; do false; done" don't fail. Workaround: use + "set -e" all over the place. Problem found by Jeff Wolfe. + + Feature: "check_XXX_access maptype:mapname" (XXX = client, + helo, sender, recipient). Now you can make recipient and + other SPAM restrictions dependent on client or sender access + tables lookup results. + +19980823 + + Bugfix: smtpd access table lookup keys were case sensitive. + + Added "permit" and "reject" operators. These are useful at + the end of SPAM restriction lists (smtpd_XXX_restrictions). + + Added a first implementation of the permit_mx_backup SPAM + restriction. This permits mail relaying to any domain that + lists this mail system as an MX host (including mail for + the local machine). Thanks to Ollivier Robert for useful + discussions. + +19980824 + + Bugfix: transport table lookup keys were case sensitive. + +19980825 + + Portability: sa_len is some ugly #define on some SGI systems, + so we must rename identifiers (file util/connect.c). + + Bugfix: uucp delivery errors are now sent to the sender. + Thanks, Mark Delany. + + Bugfix: the pipe delivery agent now replaces empty sender + by the mailer daemon address. Mark Delany, again. + + Portability: GNU getopt looks at all command-line arguments. + Fix: insert -- into the pipe/uucp definition in master.cf. + + Bugfix: the smtp server command tokenizer silently discarded + the [] around [text], so that HELO [x.x.x.x] was read as + if the client had sent: HELO x.x.x.x. Thanks, Peter Bivesand. + + Bugfix: the HELO unknown hostname/bad hostname restrictions + would have treated [text] as a domain name anyway. + + Bugfix: the $local_duplicate_filter_limit value was not + picked up by the local delivery agent. This means the local + delivery agent could run out of memory on large mailing + list deliveries. + +19980826 + + Performance: mkmap/mkalias now run with the same speed as + sendmail. VMailer now uses a 4096-entry cache with 1 Mbyte + of memory for DB lookups. File: util/dict_db.c. + +19980902 + + Robustness: the reject_unknown_hostname restriction for + HELO/EHLO hostnames will now permit names that have an MX + record instead of an A record. + +19980903 + + Feature: appending @$myorigin to an unqualified address is + configurable with the boolean append_at_myorigin parameter + (default: yes). + + Feature: appending .$mydomain to user@host is configurable + with the boolean append_dot_mydomain parameter (default: + yes). + + Feature: site!user is rewritten to user@site, under control + of the boolean parameter swap_bangpath (default: yes). + + Feature: permit a naked IP address in HELO commands (i.e. an + address without the enclosing [] as required by the RFC), by + specifying "permit_naked_ip_address" as one of the restrictions + in the "smtpd_helo_restrictions" config parameter. + +19980904 + + Code cleanup: when an SMTP client aborts a session after + sending MAIL FROM, the cleanup service no longer warns that + it is "skipping further client input". Files: cleanup/*.c. + Thanks, Daniel Eisenbud, for prodding. + + Code cleanup: when an SMTP server disconnects in the middle + of a session, don't try to send QUIT over the non-existing + connection. Files: global/smtp_stream.c, smtp/smtp.c. + Thanks, Daniel Eisenbud, for prodding, again. + + Code cleanup: the VMailer version number has moved from + mail_params.h (which is included by lots of modules) to a + separate file global/mail_version.h, so that a version + change no longer results in massive recompilation. + + Bugfix: Errors-To was flagged as a sender address, so + the address never was picked up. + + Code cleanup: support for Errors-To: headers completed. + +19980905 + + Feature: per-message exponential delivery backoff, by + looking at the amount of time a message has been queued. + Thanks, Mark Delany. + +19980906 + + Code cleanup: ripped out the per-host exponential backoff + code. It was broken by 19980818. It was probably a bad idea + anyway, because it required per-host, in-core, state kept + by the queue manager. All we do now is to keep state for + $minimal_backoff_time seconds, but only for a limited number + of hosts. Daniel Eisenbud spotted the problem. + + Lost feature: the SMTP session transcripts now show who + said what. This feature was inadvertently dropped during + development. Thanks, Daniel Eisenbud, for reminding. + + Documentation: the hard-coded rewriting process of the + trivial-rewrite program is described in html/rewrite.html. + + Feature: the local delivery agent now does alias lookups + before and after chopping off the recipient subaddress. + This allows you to forward user-anything to another user, + without losing the ability to redirect specific user-foo + addresses. + +19980909 + + Feature: the smtp client now logs a warning that a server + sends a greeting banner with the client's hostname, which + could imply a mailer loop. + +19980910 + + Feature: separate canonical maps for sender and recipient + address rewriting, so that you can rewrite an ugly sender + address and still forward mail to that same ugly address + without creating a mailer loop. Files: cleanup_envelope.c, + cleanup_message.c, cleanup_rewrite.c. + +19980911 + + Feature: virtual maps now support multiple addresses on + the right-hand side. In the case of virtual domains this + can eliminate the need for address expansion via local + aliases, making virtual domains much easier to administer. + This required that I moved the virtual table lookups from + the queue manager to the cleanup service, so that every + recipient has an on-disk status record. Files: qmgr.c, + qmgr_message.c, cleanup_envelope.c, cleanup_rewrite.c, + cleanup_virtual.c. + + Feature: sendmail/mailq/newaliases pass on the -v flag to + the program that they end up running, to make debugging a + little easier. + +19980914 + + Bugfix: some anti-spam measures didn't recognize some + addresses as local and would do too much work. File: + smtpd_check.c. + + Bugfix: the smtp sender/recipient table lookup restriction + destroyed global data, so that other restrictions could + break. File: smtpd_check.c. + + Bugfix: after vmailer reload, single-threaded servers could + exit before flushing unwritten data to the client. Example: + cleanup would exit before acking success to pickup, so the + message would be delivered twice. Bug reported by Brian Candler. + + Cleanup: removed spurious error output from vmailer-script. + Reported by Brian Candler. + + Tolerance: ignore non-numeric SMTP server responses. There's + lot of brain damage out there on the net. + +19980915 + + Feature: the smtp-sink benchmark tool now announces itself + with a neutral name so that it can be run on the same + machine as VMailer, without causing Postfix to complain + about a mailer loop. + + Robustness: on LINUX, vmailer-script now does chattr +S to + force synchronous directory updates. Fix developed with + Chris Wedgwood. + +19980916 + + Bugfix: when transforming an RFC 822 address to external + form, there is no need to quote " characters in comments. + This didn't break anything, it just looked ugly. File: + global/tok822_parse.c + +19980917 + + Workaround: with deliveries to /file/name, use fsync() and + ftruncate() only on regular files. File: local/file.c + + Workaround: the plumbing code in master_spawn.c didn't + check if it was dup2()/close()ing a descriptor to itself + then closing it. Will have to redo the plumbing later. + +19980918 + + Workaround: on multiprocessor Solaris machines, one-second + rollover appears to happen on different CPUs at slightly + different times. Made the queue manager more tolerant for + such things. Problem reported by Daniel Eisenbud. + + Workaround: in preparation for deployment with a network-shared + maildrop directory. make pickup more tolerant against clock + drift between clients and servers. + +19980921 + + New vstream_popen() module that opens a two-way channel + across a socketpair-based pipe. This module isn't being + used yet; it is here only to complete the vstream code. + +19980922 + + Code cleanup: the xxx_server_main() interface for master + child processes now uses a name-value argument list instead + of an ugly and inflexible data structure. + + Bugfix: moved the test if a non-interactive process is run + by hand, so that the "don't do this" error message can be + printed to stderr before any significant processing. + + Bugfix: smtpd now can talk to unix-domain sockets without + bailing out on a peer lookup problem. Files: smtpd/smtpd.c, + util/peer_name.c. + + Safety: by default, the postmaster is no longer informed + of protocol problems, policy violations or bounces. + + Safety: the SMTP server now sleeps before sending a [45]xx + error response, in order to prevent clients from hammering + the server with a connect/error/disconnect loop. Parameter: + smtpd_error_sleep_time (default: 5). + + Feature: the logging facility is compile-time configurable + (e.g., make makefiles "CCARGS=-DLOG_FACILITY=LOG_LOCAL1"). + +19980923 + + Bugfix: changed virtual/canonical map search order from + (user@domain, @domain, user) to (user@domain, user, @domain) + so the search order is most specific to least specific. + File: global/addr_map.c, lots of documentation. + + Bugfix: after the change of 19980910, cleanup_message + extracted recipients from Reply-To: etc. headers. Found + by Lamont Jones. + +19980925 + + Bugfix: the change in virtual/canonical map search order + broke @domain entries; they would never be looked up if + the address matched $myorigin or $mydestinations. Found + by Chip Christian who now regrets asking for the change. + + Bugfix: cleanup initialized an error mask incorrectly, so + that it would keep writing to a file larger than the queue + file size limit, and so it would treat the error as a + recoverable one instead of sending a bounce. Thanks, Pieter + Schoenmakers. + + Bugfix: the "queue file cleanup on fatal error" action was + no longer enabled in the sendmail mail posting agent. + + Feature: the sendmail mail posting program now returns + EX_UNAVAILABLE when the size of the input exceeds the queue + file size limit. NB THIS CHANGE HAS BEEN WITHDRAWN. + +19980926 + + Code cleanup: the dotlock file locking routine is no longer + derived from Eric Allman's 4.3BSD port of mail.local. + + Code cleanup: the retry strategy of the file locking routines + dot_lockfile() and deliver_flock() is now configurable + (deliver_flock_attempts, deliver_flock_delay, deliver_flock_stale). + + Code cleanup: the master.pid lock file is now created with + symlink paranoia, and is properly locked so that PID rollover + will not cause false matches. + + Bugfix: the vbuf_print() formatting engine did not know + about the '+' format specifier. + + Cleanup: replaced unnecessary instances of stdio calls by + vstream ones. + +19980929-19981002 + + Compatibility: added support for "sendmail -q". This required + a change to the queue manager trigger protocol, and a code + reorganization of the way queue scans were done. The queue + manager socket now has become public. + +10091002 + + SMTPD now logs "lost connection after end-of-message" instead + of "lost connection after DATA". + +10091005 + + More bullet proofing: timeouts on all triggers. + +19981006 + + Bugfix: make the number of cleanup processes unlimited, in + order to avoid deadlock. The number of instances needed is + one per smtp/pickup process, and an indeterminate number + per local delivery agent. Thanks, Thanks, David Miller and + Terry Lorrah for cleueing me in. + + Bugfix: "sendmail -t" extracted recipients weren't subjected + to virtual mapping. Daniel Eisenbud strikes again. + +19981007 + + Compatibility: if the first input line ends in CRLF, the + sendmail posting agent will treat all CRLF as LF. Otherwise, + CRLF is left alone. This is a compromise between sendmail + compatibility (all lines end in CRLF) and binary transparency + (some, but not all, lines contain CRLF). + +19981008 + + Robustness: stop recursive virtual expansion when the + left-hand side appears in its own expansion. + +19981009 + + Portability: trigger servers such as pickup and qmgr can + now use either FIFOs or UNIX-domain sockets; hopefully at + least one of them works properly. Trigger clients were + already capable of using either form of local IPC. + +19981011 + + Feature: masquerading. Strip subdomains from domains listed + in $masquerade_domains. Exception: envelope recipients are + left alone, in order to not screw up routing. + +19981015 + + Code cleanup: moved the recipient duplicate filter from + the user-level sendmail posting agent to the semi-resident + cleanup service, so that the filter operates on the output + from address canonicalization and of virtual expansion, + instead of operating on their inputs. + +19981016 + + Bugfix: after kill()ing a bunch of child processes, wait() + sometimes fails before all children have been reaped, and + must be called again, or the master will SIGSEGV later. + Problem reported by Scott Cotton. + + Workaround: don't log a complaint when an SMTP client goes + away without sending QUIT. + +19981018 + + Workaround: Solaris 2.5 ioctl SIOCGIFCONF returns a hard + error (EINVAL) when the result buffer is not large enough. + This can happen on systems with many real or virtual + interfaces. File: util/inet_addr_local.c. Problem reported + by Scott Cotton. + + Workaround: the optional HELO/EHLO hostname syntax check + now allows a single trailing dot. + + Workaround: with UNIX-domain sockets, LINUX connect() blocks + until the server calls accept(). File: qmgr/qmgr_transport.c. + Terry Lorrah and Scott Cotton provided the necessary evidence. + +19981020 + + Robustness: recursive canonical mapping terminates when + the result stops changing. + + Code cleanup: reorganized the address rewriting and mapping + code in the cleanup service, to make it easier to implement + the previous enhancement. + +19981022 + + Code cleanup: more general queue scanning programming + interface, in preparation for hashed queues. File: + qmgr/qmgr_scan.c. + + Bugfix: a non-FIFO server with a process limit of 1 has a + too short listen queue. Until now this was not a problem + because only FIFO servers had a process limit of 1, and + FIFOs have no listen queue. Fix: always configure a listen + queue of proc_limit or more. File: master/master_listen.c. + +19981023 + + Feature: by popular request, mail delay is logged when + delivering, bouncing or deferring mail. + +19981024 + + Cleanup: double-bounce mail is now absorbed by the queue + manager, instead of the local delivery agent, so that the + mail system will not go mad when no local delivery agent + is configured. + +19981025 + + Cleanup: moved the relocated table from the local delivery + agent to the queue manager, so that the table can also be + used for virtual addresses. + + Code reorg: in order for the queue manager to absorb + recipients, the queue file has to stay open until all + recipients have been assigned to a destination queue. + +19981026 + + vmlogger command, so that vmailer-script logging becomes + consistent with the rest of the VMailer system. + + Code reorg: logger interface now can handle multiple output + handlers (e.g. syslog and stderr stream). + + Bugfix: a first line starting with whitespace is no longer + treated as an extension of our own Received: header. Files: + smtpd/smtpd.c, pickup/pickup.c. + +19981027 + + Bugfix: the bang-path swapping code went into a loop on an + address consisting of just a single !. Eilon Gishri had + the privilege of finding this one. + + Workaround: the non-blocking UNIX-domain socket connect is + now enabled only on systems that need it. It may cause + kernel trouble on Solaris 2.x. + + Bugfix: the resolver didn't implement bangpath swapping, + so that mail for site!user@mydomain would be delivered to + a local user named "site!user". + +19981028 + + Cleanup: a VSTREAM can now use different file descriptors + for reading and writing. This was necessary to prevent + "sendmail -bs" and showq from writing to stdin. Eilon Gishri + observed the problem. + +19981029 + + The RFC 822 address manipulation routines no longer give + special attention to 8-bit data. Files: global/tok822_parse.c, + global/quote_822_local.c. + + Bugfix: host:port and other non-domain stuff is no longer + allowed in mail addresses. File: qmgr/qmgr_message.c. + + Workaround: LINUX accept() wakes up before the three-way + handshake is complete, so it can fail with ECONNRESET. + Files: master/single_server.c, master/multi_server.c. + + Feature: when delivering to user+foo, try ~user/.forward+foo + before trying ~user/.forward. + + Bugfix: smtpd in "sendmail -bs" (stand-alone) mode didn't + clean up when terminated by a signal. + + Bugfix: smtpd in "sendmail -bs" (stand-alone) mode should + not try to enforce spam controls because it cannot access + the address rewriting machinery. + + Cleanup: the percent hack (user%domain -> user@domain) is + now configurable (allow_percent_hack, default: yes). + + Bugfix: daemons in -S (stand-alone) mode didn't change + directory to the queue. This was no problem with daemons + run by the sendmail compatibility program. + +19981030 + + Feature: when virtual/canonical/relocated lookup fails for + an address that contains the optional recipient delimiter + (e.g., user+foo@domain), the search is done again with the + unextended address (e.g., user@domain). File: global/addr_find.c. + + Code reorg: the address searching is now implemented by a + separate module global/addr_find.c, so that the same code + can be used for both (non-mapping) relocated table lookups + and for canonical and virtual mapping. The actual mapping + is still done in the global/addr_map.c module. + + Robustness: the SMTP client now skips hosts that don't send + greeting banner text. File: smtp/smtp_connect.c + + Feature: preliminary support to disable delivered-to. This + is desirable for mailing list managers that don't want to + advertise internal aliases. + + Generic support: when the recipient_feature_delimiter + configuration parameter is set, the local delivery agent + uses it to split the recipient localpart into fields. Any + field that has a known name such as "nodelivered" enables + the corresponding delivery feature. + +19981031 + + Code reorg: address splitting on recipient delimiter is + now centralized in global/split_addr.c, which knows about + all reserved names that should never be split. + + Robustness: when a request for an internal service cannot + be satisfied because the master has terminated, terminate + instead of trying to reach the service every 30 seconds. + + Safety: the local delivery agent now runs as vmailer most + of the time, just like pickup and pipe. Files: local/local.c, + local/mailbox.c + +19981101 + + Compatibility: the tokenizer for alias/forward/etc. + expansion now updates an optional counter with the number + of destinations found; If no destinations is found in a + .forward file, deliver to the mailbox instead. Thanks, + Daniel Eisenbud, for showing the way to go. + + Robustness: the pickup daemon should always include a + posting-time record, even when the sendmail posting agent + didn't. However, just like before, user-provided posting + times will be ignored. Ollivier Robert found this one. + + Robustness: duplicate entries in aliases or maps now cause + a warning instead of a fatal error (and an incomplete file). + + Robustness: mkmap now prints a warning when an entry is + in "key: value" format, which is the format expected for + alias databases, not for maps. + + Portability: on LINUX, prepend "+" to the getopt() options + string so that getopt() will stop at the first non-option + argument. Suggestion by Marco d'Itri. + +19981103 + + Cleaned up the set_eugid() and open_as() implementations, + and added stat_as() and fstat_as() so that the local delivery + agent would look up include files and .forward files with + the right privileges. + +19981104 + + Bugfix: the :include: routine now stat()s/open()s files + included by root-owned aliases as root, not as nobody. + + Bugfix: the master crashed when a service with wakeup timer + was disabled or renamed. Fix: eliminate some pathological + coupling between process management and wakeup management. + + Feature: partial implementation of ETRN (causes a full + deferred queue scan). Thanks Lamont Jones for reminding me + that things can be useful already before they are perfect. + + Cleanup: simplified the SMTPD tokenizer. + + Bugfix: sendmail -bs didn't properly notify the mail system + of new mail. + + Compatibility: the MAIL FROM and RCPT TO commands now accept + the most common address forms without enclosing <>. The <> + is still needed for addresses that contain a "string", an + [address], or a colon (:). + +19981105 + + Bugfix: "master -t" would claim that the master runs when + in fact the pid directory does not exist, causing trouble + with first time startup (reported by several). + + Portability: added a sane_accept() module that maps all + beneficial accept() error results to EAGAIN. According to + private communication with Alan Cox, Linux 2.0.x accept() + can return a variety of error conditions, so we play safe + and allow for any error that may happen because SYN+ACK + could not be sent. + + Portability: NETBSD1 uses dotlock files (Perry Metzger). + + Bugfix: the local delivery agent did not canonicalize + owner-foo sender addresses, so that local users would see + owner-foo instead of owner-foo@$myorigin (Perry Metzger). + + OPENSTEP4 support, similar to NEXTSTEP3 (Gerben Wierda). + +19981106 + + Portability: the master startup would take a long time on + AIX because AIX has a very large per-process open file + limit. Fix is to check the status of only the first couple + hundred file descriptors instead. File: master/master.c. + + Bugfix: mail to user@[net.work.addr.ess] was broken because + of a reversed test. File: qmgr/qmgr_message.c. + +19981107 + + Compatibility: don't clobber the envelope sender address + when an alias has no owner-foo alias (problem diagnosed by + Christophe Kalt). + + Bugfix: mail to local users in include files would be + delivered directly if the alias didn't have an owner-foo + alias, and if the alias database and include file were + owned by root. + + Feature: with user+foo addresses, any +foo address extension + that is not explicitly matched in canonical, virtual or + alias databases is propagated to the table lookup result. + +19981108 + + Bugfix: minor memory leak in the user+foo table lookup code. + + Configurability: specify virtual.domain in the virtual map, + and mail for unknown@virtual.domain will bounce automatically. + The $relay_domains default value now includes $virtual_maps, + so the SMTP server will accept mail for the domain. Marco + d'Itri put me on the right track. + + Configurability: The mydestinations configuration parameter + now accepts /file/name expressions and type:name lookup tables. + + Code cleanup: in order to make the previous two enhancements + possible, revised the string/host/address matching engine + so it can handle any mixture of strings, /file/name patterns + and type:name lookup tables. Files: util/match_{list,ops}.c, + global/{domain,namadr,string}_list.c. + +19981110 + + Code cleanup: replaced remaining isxxx() calls by ISXXX(). + +19981111 + + Bugfix: the "bounce unknown virtual user" code was in the + wrong place. Problem tackled with help of Chip Christian. + + Portability: reportedly, Solaris 2.5.1 can hang waiting + for a UNIX-domain connection to be accepted, to it gets + the same workaround that was designed for LINUX. Problem + reported by Scott Cotton. + +19981112 + + Management: "vmailer stop" now allows delivery agents to + finish what they are doing, like "vmailer reload". + + Management; "vmailer abort" causes immediate termination. + + Workaround: zombie processes pile up with HP-UX. Reason: + select() does not return upon SIGCHLD when SA_RESTART is + specified to sigaction(). Workaround: shorten the select() + timer to 10 seconds, #ifdef BRAINDEAD_SELECT_RESTARTS. + Thanks, Lamont Jones. + +19981117 + + Rename: VMailer is now Postfix. Sigh. + +19981118 + + Cleanup: generalized the safe_open() routine so that it is + no longer limited to mailbox files, lock files, etc. + + Bugfix (found during code review): vstream*printf() could + run off the end of a stream buffer after an I/O error, + because vbuf_print() ignored the result from VBUF_SPACE(). + + Bugfix (found during code review): resolve_local() could + clobber its argument, but the docs didn't say so. + +19981121 + + Cleanup: the is_header() routine now allows 8-bit data in + header labels. + +19981123 + + Bugfix (found during code review): the mail_queue_enter() + path argument wasn't optional. File: global/mail_queue.c + +19981124 + + Cleanup: eliminated redundant tests for a zero result from + vstream_fdopen(). Unlike the stdio fdopen() routine, the + vstream_fdopen() routine either succeeds or never returns. + + Bugfix: the queue manager now looks at the clock before + examining a file time stamp, to avoid spurious complaints + about time warps on busy machines. File: qmgr/qmgr_active.c. + +19981125 + + Compatibility: allow trailing dot at the end of user@domain. + Address canonicalization now strips it off. Issue brought + forward by Eilon Gishri. File: trivial-rewrite/rewrite.c. + + Robustness: changed DNS lookup order of MAIL FROM etc. + domains from MX then A to A then MX, just in case the MX + lookup fails with a server error. + + Renamed vmcat, vmlock, vmlogger, vmtrigger to postcat, + postlock, postlog, postkick. Also renamed mkmap and mkalias + to postmap and postalias. + +19981126 + + Workaround: Lamont Jones found a way for HP-UX to terminate + select() after SIGCHLD. The code is #ifdef USE_SIG_RETURN. + Files: util/sys_defs.h, master/master_sig.c. + + Bugfix: the Delivered-To: loop detection code had stopped + working, when long ago the is_header() routine was changed. + File: local/delivered.c. + +19981128 + + Bugfix: postcat opened queue files read-write, where only + read access was needed. File: postcat/postcat.c. + +19981129 + + Safety: added a sleep(1) to all fatal and panic exits. + File: util/msg.c. + +19981201 + + Robustness: postcat now insists that a file starts with a + time record. + + Consistency: added "-c config_dir" command-line options + where appropriate. + +19981202 + + Man pages, on-line version. + +19981203 + + Man pages, html version; overview documentation. + +19981206 + + Sendmail silently accepted the unsupported -qRsite and + -qSsite options. It now prints an error message and + terminates. + + Separated the contributed tree from the IBM code; moved + the LDAP and NEXTSTEP/OPENSTEP code to the contributed + source tree because obviously I didn't write it. + +19981206-9 + + Had to write a postconf configuration utility in order to + reliably find out about all configuration parameters and + their defaults. + + Documentation bugfixes by Matt Shibla, Scott Drassinower, + Greg A. Woods. + +19981209 + + On machines with short hostnames, postconf -d cored while + reporting a fatal error. It should not report that error + in the first place. Thanks, Eilon Gishri. + + Changed the FAQ entry about rejecting mail for *.my.domain + on a firewall. Chip Christian was right, I was wrong. + +19981214 + + Portability: with GNU getopt, optind is not initially 1, + breaking an assumption in sendmail/sendmail.c. Liviu Daia. + + Annoyance: on non-networked systems, don't warn that only + one network interface was found. File: global/inet_addr_local.c. + Reported by several. + + Bugfix: on non-networked systems, the smtp client assumed + that it was running in virtual host mode, and would bind + to the loopback interface. File smtp/smtp_connect.c. Liviu + Daia, again. + +19981220 + + Robustness: when looking up an A or MX record, do not give + up when the A query fails because of a server error. File + dns/dns_lookup.c. Reported by Scott Drassinower. + +19981221 + + Bugfix: "bounce mail for non-existent virtual user" didn't + work when a non-default relay host was configured in main.cf + or in the transport table. File: qmgr/qmgr_message.c. + + Bugfix: the maildrop directory should not be world-readable. + Files: conf/postfix-script, showq/showq.c. + + Documentation: fixed several omissions and errors. + + Documentation: removed references to the broken recipient + feature delimiter configuration parameter. + + Bugfix: write mailbox file as the recipient, so that file + quota work as expected. + + Bugfix: pickup would die when it tried to remove a non-file + in the maildrop directory (Jeff Wolfe). + +19981222 + + Sendmail no longer logs the queue ID when it is unable to + notify the pickup daemon. This is a late addition to the + "unreadable maildrop queue" patch. + + user.lock files are now created as root, so that postfix + needs no group directory write permission. + +19981224 + + Security: allow queue file link counts > 1, to avoid + non-delivery of maildrop files with links to a non-maildrop + directory. Files: global/mail_open_ok.c, and anything + that calls this code (qmgr, pickup, showq). If multiple + hard links are a problem, see the set-gid "postdrop" utility + below. + +19981225 + + Robustness: the queue manager no longer aborts when a queue + file suddenly disappears (e.g. because the file was removed + by hand). + + Feature: when a writable maildrop directory is a problem, + sites can make the new "postdrop" utility set-gid. This command + is never used when the maildrop directory is world-writable. + + Robustness: make the queue file creation routine more + resistant against denial of service race attack. File: + global/mail_queue.c + +19981226 + + New suid_priv module to enable/disable privileges in a + set-uid/gid program. In the end I decided to not use it. + +19981228 + + Robustness: make the pickup daemon more resistant against + non-file race attack. + + Cleanup: generic mail_stream.c interface for writing queue + file streams to files, daemons or commands. This simplifies + the code in smtpd and in sendmail that must be able to pipe + mail through the postdrop command. The cleanup daemon has + been modified to use the same interface. Result: less code. + + Feature: smtpd now logs the only recipient in Received: + headers. + + Feature: separate command and daemon directories. Both + default to $program_directory. Install conf/postfix-script + if you want to use this feature. + +19981230 + + Patch to avoid conflict with non-writable top-level Makefile + (Lamont Jones). + +19981231 + + Portability: port to UnixWare 7 by Ronald Joe Record, SCO. + +19990104 + + Bugfix: fencepost (Jon Ribbens, Oaktree Internet Solutions + Ltd.) Files: quote_82[12]_local.c. + + Bugfix: wrong default for relay_domains (Juergen Kirschbaum, + Bayerische Landesbank). File: mail_params.h. + + Bugfix: changed 5xx response for "too may recipients" to + 4xx. File: smtpd.c. + +19990106 + + Feature: defer_transports specifies the names of transports + that should be used only when "sendmail -q" (or equivalent) + is issued. For example, "defer_transports = smtp" is useful + for sites that are disconnected most of the time. File: + qmgr_message.c. + +19990107 + + Feature: local_command_shell specifies a non-default shell + for delivery to command by the local delivery agent. For + example, "local_command_shell = /some/where/smrsh -c" + restricts what may appear in "|command" destinations. + File: global/pipe_command.c. + +19990112-16 + + Feature: SMTP command pipelining support based on an initial + version by Jon Ribbens, Oaktree Internet Solutions Ltd. + This one took several days of massaging before I felt + comfortable about it. Files: smtp.c, smtp_proto.c. + + Bugfix: the SMTP server would flush responses one-by-one, + which caused suboptimal performance with pipelined clients. + The vstream routines now flush the write buffer when the + read() routine is called, instead of flushing when the + application changes from writing to reading. Delayed flush + prevents the SMTP server from flushing responses one-by-one + and thus triggering Nagle's algorithm. File: util/vstream.c. + +19990117 + + Bugfixes and enhancements to the smtpstone tools by Drew + Derbyshire, Kendra Electronic Wonderworks: send helo command, + send message headers, format the message content to lines + < 80, work around NT stacks, make "." recognition more + robust. Files: smtp-source.c, smtp-sink.c. + + Strategy: look at the deferred queue only when the incoming + queue is empty; limit the number of recipients read from + a queue file depending on the number of recipients already + in core. Files: qmgr.c, qmgr_message.c. + + Feature: postponed anti-UCE restrictions. The decision to + reject junk mail on the basis of the client name/address, + HELO hostname or sender address can now be postponed until + the RCPT TO command (or HELO or MAIL FROM if you like). + File: smtpd_check.c. + +19990118 + + Feature: incremental updates of alias databases and of + other lookup tables. Both postalias and postmap now take + a -i option for incremental updates from standard input. + Files: global/mkmap_*.c, post{map,alias}/post{map,alias}.c. + + Compatibility: newaliases can now update multiple alias + databases: list them in the "alias_database" parameter in + main.cf. By the same token, postalias can now update multiple + maps in one command. Files: post{map,alias}/post{map,alias}.c + + Feature: mail to <> is now sent to the address specified + with the "empty_address_recipient" configuration parameter + which defaults to MAILER-DAEMON (idea by Lamont Jones, + Hewlett-Packard). File: cleanup/cleanup_envelope.c. + + Compatibility: the transport table now uses .domain.name + to match subdomains, just like sendmail mailer tables + (patch by Lamont Jones, Hewlett-Packard). + + Feature: mailq now ends with a total queue size summary + (Eilon Gishri, Israel Inter University Computation Center). + +19990119 + + Feature: address masquerade exceptions for user names listed + in the "masquerade_exceptions" configuration parameter. + File: cleanup/cleanup_masquerade.c. + + Feature: qmail-style maildir support, based on initial code + by Kevin W. Brown, Quantum Internet Services Inc. + + Workaround: Solaris 2.something connect() fails with + ECONNREFUSED when the system is busy (Chris Cappuccio, + Empire Net). File: global/mail_connect.c. + + Feature: the cleanup service now adds a Return-Path: header + when none is present. This header is needed for some mail + delivery programs (see below). File: cleanup_message.c. + + Feature: the pipe mailer now supports $user, $extension + and $mailbox macros in command-line expansions. This, plus + the Return-Path: header (see above), should be sufficient + to support cyrus IMAP out of the box. Based on initial + code by Joerg Henne, Cogito Informationssysteme GMBH. + File: pipe/pipe.c. + + Bugfix: with address extensions enabled, canonical and + virtual lookups now are done in the proper order: + user+foo@domain, user@domain, user+foo, user, @domain. + File: global/mail_addr_find.c. + +19990119 + + Feature: the local mailer now prepends a Received: message + header with the queue ID to forwarded mail, in order to + make message tracing easier. File: local/forward.c. + + Cleanup: after "postfix reload", no more broken pipe + complaints from resolve/rewrite clients. + +19990121 + + Feature: pickup (again) logs uid and sender address. + On repeated request by Scott Cotton, Internet, IC Group, Inc. + + Portability: doze() function for systems without usleep(). + + Cleanup: clients are logged as host[address]. + +19990122 + + Maildir support changed: specify "home_mailbox = Maildir/". + The magic is the trailing /. Suggested by Daniel Eisenbud, + University of California at Berkeley. + + Maildir support from aliases, :include: and .forward files. + Specify /file/name/ - the trailing / is required. Suggested + by Daniel Eisenbud, University of California at Berkeley. + + Workaround: watchdog timer to prevent the queue manager + from locking up on some systems. + + Bugfix: in Received: headers, the "for " + information was in the wrong place. Pointed out by Jon + Ribbens, Oaktree Internet Solutions Ltd. diff --git a/postfix/INSTALL b/postfix/INSTALL new file mode 100644 index 000000000..8404fa4c3 --- /dev/null +++ b/postfix/INSTALL @@ -0,0 +1,504 @@ +Purpose of this document +======================== + +This document describes how to build, install and configure a +Postfix system so that it can do one of the following: + + - Send mail only, without changing an existing sendmail + installation. + + - Send and receive mail via a virtual host interface, still + without any change to an existing sendmail installation. + + - Replace sendmail altogether. + +Typographical conventions +========================= + +In the instructions below, a command written as + + # command + +should be executed as the superuser. + +A command written as + + % command + +should be executed as an unprivileged user. + +Documentation +============= + +Documentation is available as HTML web pages (point your browser +to html/index.html) and as UNIX-style manpages (point your MANPATH +environment variable to the `man' subdirectory; be sure to use an +absolute path). + +The sample configuration files in the `conf' directory have extensive +comments, but they may not describe every nuance of every feature. + +Many files have their own built-in manual page. Tools to extract +those embedded manual pages are available in the contributed software +from http://www.postfix.org/ + +Building on a supported system +============================== + +If your system is supported, it is one of + + AIX 4.1.x + AIX 4.2.0 + BSD/OS 2.x + BSD/OS 3.x + BSD/OS 4.x + FreeBSD 2.x + FreeBSD 3.x + HP-UX 9.x + HP-UX 10.x + HP-UX 11.x + IRIX 5.x + IRIX 6.x + Linux Debian 1.3.1 + Linux Debian 2.x + Linux RedHat 4.2 + Linux RedHat 5.x + Linux Slackware 3.5 + Linux SuSE 5.x + NEXTSTEP 3.x + NetBSD 1.x + OPENSTEP 4.x + OSF1.V4 aka Digital UNIX V4 + OpenBSD 2.x + SunOS 4.1.x + SunOS 5.4..5.7 (Solaris 2.4..7) + +or something closely resemblant. Some platforms such as Ultrix 4 +might work, but that has not been verified. It is sufficiently +similar to SunOS 4 that my guesses should be mostly right. + +Download the contributed software from http://www.postfix.org/ if +you wish to include support for LDAP (light-weight directory access +protocol) lookups, for NEXTSTEP version 3 or for OPENSTEP version 4. + +If at any time in the build process you get messages like: "make: +don't know how to ..." you should be able to recover by running +the following command from the Postfix top-level directory: + + % make -f Makefile.init makefiles + +If you copied the Postfix source code after building it on another +machine, it is a good idea to cd into the top-level directory and + + % make tidy + +first. This will get rid of any system dependencies left over from +compiling the software elsewhere. + +To build with GCC, or with the native compiler if people told me +that is better for your system, just cd into the top-level Postfix +directory of the source tree and type: + + % make + +To build with a non-default compiler, you need to specify the name +of the compiler: + + % make makefiles CC=/opt/SUNWspro/bin/cc + % make + + % make makefiles CC="purify cc" + % make + +and so on. On 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: + + % make makefiles CCARGS=-DDEF_CONFIG_DIR=\\\\\\\"/some/where\\\\\\\" + % make + +That's seven backslashes :-) But at least this works with sh and csh. + +In any case, if the command + + % make + +produces compiler error messages, it may be time to examine the +FAQ document. + +Porting to on an unsupported system +=================================== + +- Choose a SYSTEMTYPE name for the new system. Please use a name +that includes the major version of the operating system (such as +SUNOS4 or LINUX2), so that different releases of the same system +can be supported without confusion. + +- Add a case statement to the "makedefs" shell script in the +top-level directory that recognizes the new system reliably, and +that emits the right system-specific information. Be sure to make +the code robust against user PATH settings; if the system offers +multiple UNIX flavors (e.g. BSD and SYSV) be sure to build for the +native flavor, not the emulated one. + +- Add an #ifdef SYSTEMTYPE section to the central util/sys_defs.h +include file. You may have to invent new feature macros. Please +choose sensible feature macro names such as HAS_DBM or +FIONREAD_IN_SYS_FILIO_H. I strongly recommend against #ifdef +SYSTEMTYPE dependencies in individual source files. This may seem +to be the quickest solution, but it will create a mess that becomes +increasingly difficult to maintain over time. Moreover, with the +next port you'd have to place #ifdefs all over the source code +again. + +Installing the software after successful compilation +==================================================== + +There is no automated installation procedure. The Postfix system +is sufficiently complex, and UNIX systems are sufficiently different, +that I feel uncomfortable providing an out-of-the-box procedure. + +Installing Postfix by hand takes only a few steps. + +- Configuration directory. This name is wired into the programs, + but it can be overruled by setting the MAIL_CONFIG environment + variable. This text assumes that you have chosen the default + location. + + As superuser, execute the commands: + + # mkdir /etc/postfix + # chmod 755 /etc/postfix + # cp /some/where/postfix/conf/* /etc/postfix + # chmod 644 /etc/postfix/* + # chmod 755 /etc/postfix/postfix-script + + This also installs the LICENSE file, as required. + +- Spool directory. The pathname is configurable in /etc/postfix/main.cf. + This text assumes that you have chosen the default location. + + As superuser, execute the commands: + + # mkdir /var/spool/postfix + # chmod 755 /var/spool/postfix + +- Program directory. The pathname is configurable in /etc/postfix/main.cf. + I recommend that you install the programs in a separate directory, + not in a place that contains other software. + + As superuser, execute the commands: + + # mkdir /some/where/bin + # cp bin/* /some/where/bin + + Alternative 1: leave the programs in the Postfix source tree. + + Alternative 2: use separate program and daemon directories (again, + configurable in /etc/postfix/main.cf): + + # mkdir /some/where/sbin /some/where/libexec + # cp bin/sendmail bin/post* /some/where/sbin + # cp `ls bin/*|egrep -v 'post|fsstone|smtp-|sendmail'` /some/where/libexec + +- On-line manual pages: + + # mkdir /some/where/man + # (cd man; tar cf - .) | (cd /some/where/man; tar xvf -) + + Alternative: leave the manpages in the Postfix source tree. + + You may wish to update your MANPATH so you can view the Postfix + manual pages. For example: + + # export MANPATH + # MANPATH=/some/where/man:/usr/share/man:/usr/local/man + +- Next, review the "To chroot or not to chroot" section, and proceed + to the section on how you wish to run Postfix on your particular + machine: + + - Send mail only, without changing an existing sendmail + installation. + + - Send and receive mail via a virtual host interface, still + without any change to an existing sendmail installation. + + - Replace sendmail altogether. + +To chroot or not to chroot +========================== + +Most Postfix daemons can run in a chroot jail, that is, in a chroot +environment at fixed low privilege. This provides a significant +barrier against intrusion. The barrier is not impenetrable, but +every little bit helps. + +The file /etc/postfix/master.cf by default runs no Postfix daemons +in a chroot jail. However, with the exception of the `local' and +`pipe' daemons, every Postfix daemon can run chrooted. + +- The contributed source code from http://www.postfix.org/ has + scripts for setting up chroot environments for Postfix systems. + +Configuring Postfix to send mail only +===================================== + +If you are going to use Postfix to send mail only, there is no need +to change your sendmail setup. Instead, set up your mail user agent +so that it calls the Postfix sendmail program directly. + +Follow the instructions in the "Mandatory configuration file edits" +section below. + +You must comment out the smtp inet service in /etc/postfix/master.cf, +in order to avoid conflicts with the sendmail daemon. + +Start the Postfix system: + + # postfix start + +or, if you feel nostalgic, + + # /some/where/bin/sendmail -bd -qwhatever + +and watch your syslog file for any error messages. + +When it is run for the first time, the Postfix startup shell script +will create a bunch of subdirectories below the Postfix spool +directory. + +In order to inspect the mail queue, use + + % /some/where/bin/sendmail -bp + +See also the "Care and feeding" section below. + +Configuring Postfix to send and receive mail (virtual interface) +================================================================ + +Alternatively, you can use the Postfix system to send AND receive +mail while leaving your sendmail setup intact, by running Postfix +on a virtual interface address. Simply configure your mail user +agent to directly invoke the Postfix sendmail program. + +The contributed source code from http://www.postfix.org/ gives +examples for setting up virtual interfaces for a variety of UNIX +versions. + +In the /etc/postfix/main.cf file, I would specify + + myhostname = virtual.host.name + inet_interfaces = $myhostname + mydestination = $myhostname + +You still have to do the "Mandatory configuration file edits" +described below. After those edits, start the mail system: + + # /some/where/bin/postfix start + +or, if you feel nostalgic, + + # /some/where/bin/sendmail -bd -qwhatever + +and watch your syslog file for any error messages. + +When it is run for the first time, the Postfix startup shell script +will create a bunch of subdirectories below the Postfix spool +directory. + +In order to inspect the mail queue, use + + % /some/where/bin/sendmail -bp + +See also the "Care and feeding" section below. + +Turning off sendmail forever +============================ + +If you are going to REPLACE sendmail by Postfix, execute the +following commands. The text assumes that your sendmail is in +/usr/sbin, and that mailq and newaliases are in /usr/bin. + +First, move the existing sendmail, mailq and newaliases commands +out of the way: + + # mv /usr/sbin/sendmail /usr/sbin/sendmail.OFF + # mv /usr/bin/mailq /usr/bin/mailq.OFF + # mv /usr/bin/newaliases /usr/bin/newaliases.OFF + # chmod 555 /usr/sbin/sendmail.OFF /usr/bin/mailq.OFF \ + /usr/bin/newaliases.OFF + +Then, drop in the new Postfix sendmail and support programs, and +set up links for the mailq and newaliases commands: + + # ln -s /some/where/bin/sendmail /some/where/bin/post* /usr/sbin + # chmod 755 /usr/sbin/sendmail /usr/sbin/post* + # ln -s /usr/sbin/sendmail /usr/bin/mailq + # ln -s /usr/sbin/sendmail /usr/bin/newaliases + +Be sure to keep the old sendmail running for at least a couple +days to flush any unsent mail. + +After you have visited the "Mandatory configuration file edits" +section below, you can start the Postfix system with + + # postfix start + +But the good old sendmail way works just as well: + + # sendmail -bd -qwhatever + +and watch the syslog file for any complaints from the mail system. + +When it is run for the first time, the Postfix startup shell script +will create a bunch of subdirectories below the Postfix spool +directory. + +See also the "Care and feeding" section below. + +Mandatory configuration file edits +================================== + +By default, all Postfix configuration files are in /etc/postfix, and +must be owned by root. + +Whenever you make a change to a config file, execute the following +command in order to refresh a running mail system: + + # postfix reload + +In /etc/postfix/main.cf you will have to set up a minimal number of +configuration parameters. Postfix configuration parameters +resemble shell variables. You specify a variable as + + parameter = value + +and you use it by putting a $ in front of it: + + other_parameter = $parameter + +You can use $parameter before it is given a value. The Postfix +configuration language uses lazy evaluation, and does not look at +a parameter value until it is needed at runtime. + +First of all you have to specify the userid that owns the Postfix +queue and processes. The default setting, + + mail_owner = postfix + +should be appropriate for your system. I would recommend that you +create a dedicated user account "postfix", that is not in the same +group as other accounts. Make sure it is a locked account that +no-one can log into. It does not need an executable login shell, +nor does it need an existing home directory. + +Secondly, you should specify what domain name will be appended to +a local address. The "myorigin" parameter defaults to the local +hostname, but that is probably OK only for very small sites. + +Some examples: + + myorigin = $myhostname + myorigin = $mydomain + +In the first case, local mail goes out as user@$myhostname, in +the second case the sender address is user@$mydomain. + +Next you nee to specify what mail addresses are local to the Postfix +system. + +Some examples: + + mydestination = $myhostname, localhost.$mydomain + mydestination = $myhostname, localhost.$mydomain, $mydomain + mydestination = $myhostname + +The first example is appropriate for a workstation, the second is +appropriate for the mailserver for an entire domain. The third +example should be used when running on a virtual host interface. + +If you're behind a firewall, you should set up a relayhost. If +you can, specify the organizational domain name so that Postfix +can use DNS lookups, and so that it can fall back to a secondary +MX host when the primary MX host is down. Otherwise just specify +a hard-coded hostname. + +Some examples: + + relayhost = $mydomain + relayhost = mail.$mydomain + +If you haven't used sendmail prior to using Postfix, you will have +to build the alias database (with: sendmail -bi, or: newaliases). + +Finally, specify the program and queue directories. + + program_directory = /some/where/bin + queue_directory = /var/spool/postfix + +For further configuration information I suggest that you browse +the configuration documentation in the html subdirectory. + +Security: writable versus protected maildrop directory +====================================================== + +Postfix offers a choice of submission mechanims. + +1 - Postfix can use a world-writable, sticky, mode 1733 maildrop + directory where local users can submit mail. This approach + avoids the need for set-uid or set-gid software. Mail can be + posted even while the mail system is down. Queue files in the + maildrop directory have no read/write/execute permission for + other users. The maildrop directory is not used for mail + received via the network. + + With directory world write permission come opportunities for + annoyance: a local user can make hard links to someone else's + maildrop files so they don't go away and may be delivered + multiple times; a local user can fill the maildrop directory + with junk and try to crash the mail system; and a local user + can hard link someone else's files into the maildrop directory + and try to have them delivered as mail. However, Postfix queue + files have a specific format; less than one in 10^12 non-Postfix + files would be recognized as a valid Postfix queue file. + + In order to enable this mode, step into /etc/postfix and: + + # cp postfix-script-nosgid postfix-script + +2 - On systems with many users it may be desirable to revoke maildrop + directory world write permission, and to enable set-gid privileges + on a small "postdrop" command that is provided for this purpose. + + In order to revoke world-write permission, create a group + "maildrop" that is unique and that does not share its group ID + with any other user, certainly not with the postfix account, + then execute the following commands to make "postdrop" set-gid, + and to make maildrop non-writable for unprivileged users: + + # chgrp maildrop /var/spool/postfix/maildrop /some/where/postdrop + # chmod 730 /var/spool/postfix/maildrop + # chmod 2755 /some/where/postdrop + + The sendmail posting program will automatically invoke the + postdrop command when maildrop directory write permission is + restricted. + + In order to enable this mode, step into /etc/postfix and: + + # cp postfix-script-sgid postfix-script + +Care and feeding of the Postfix system +====================================== + +The Postfix programs log all problems to the syslog daemon. +Hopefully, the number of problems will be small, but it is a good +idea to run every night before the syslog files are rotated: + + # postfix check + # egrep '(reject|warning|error|fatal|panic):' /some/log/file + +The second line looks for problem reports from the mail software, +and reports how effective the anti-relay and anti-UCE blocks are. diff --git a/postfix/LICENSE b/postfix/LICENSE new file mode 100644 index 000000000..5e83ceb25 --- /dev/null +++ b/postfix/LICENSE @@ -0,0 +1,59 @@ +Please read this carefully. You must agree to the following terms and +conditions before installing the Secure Mailer or any related +documentation ("Software"). If you do not agree to these terms +and conditions, you may not install or use the Software. + +Permission to reproduce and create derivative works from the Software +("Software Derivative Works") is hereby granted to you under the +copyrights of International Business Machines Corporation ("IBM"). IBM +also grants you the right to distribute the Software and Software +Derivative Works. + +You grant IBM a world-wide, royalty-free right to use, copy, +distribute, sublicense and prepare derivative works based upon any +feedback, including materials, error corrections, Software Derivatives, +enhancements, suggestions and the like that you provide to IBM relating +to the Software. + +IBM licenses the Software to you on an "AS IS" basis, without warranty +of any kind. IBM HEREBY EXPRESSLY DISCLAIMS ALL WARRANTIES OR +CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, NON +INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. You are solely +responsible for determining the appropriateness of using this Software +and assume all risks associated with the use and distribution of this +Software, including but not limited to the risks of program errors, +damage to or loss of data, programs or equipment, and unavailability or +interruption of operations. IBM WILL NOT BE LIABLE FOR ANY DIRECT +DAMAGES OR FOR ANY SPECIAL, INCIDENTAL, OR INDIRECT DAMAGES OR FOR ANY +ECONOMIC CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS OR SAVINGS), +EVEN IF IBM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IBM +will not be liable for the loss of, or damage to, your records or data, +or any damages claimed by you based on a third party claim. You may +not use the name "IBM" or any other IBM trademark without the prior +written consent of IBM. + +You agree to distribute the Software and any Software Derivatives under +a license agreement that: 1) is sufficient to notify all licensees of +the Software and Software Derivatives that IBM assumes no liability for +any claim that may arise regarding the Software or Software +Derivatives, and 2) that disclaims all warranties, both express and +implied, from IBM regarding the Software and Software Derivatives. (If +you include this Agreement with any distribution of the Software or +Software Derivatives you will have met this requirement.) You agree +that you will not delete any copyright notices in the Software. + +In the event an intellectual property claim is made or appears likely +to be made with respect to the Software, you agree to permit IBM to +enable you to continue to use the Software, or to modify it, or replace +it with software that is at least functionally equivalent. If IBM +determines that none of these alternatives is reasonably available, you +agree, at IBM's request, upon notice to you, to discontinue further +distribution of the Software and to delete or destroy all copies of the +Software you possess. This is IBM's entire obligation to you regarding +any claim of infringement. + +This Agreement is the exclusive statement of your rights in the +Software as provided by IBM. Except for the licenses granted to you in +the second paragraph above, no other licenses are granted hereunder, by +estoppel, implication or otherwise. diff --git a/postfix/Makefile b/postfix/Makefile new file mode 100644 index 000000000..424db7120 --- /dev/null +++ b/postfix/Makefile @@ -0,0 +1,18 @@ +# Usage: +# make makefiles [CC=compiler] [OPT=compiler-flags] [DEBUG=debug-flags] +# +# The defaults are: CC=gcc, OPT=-O, and DEBUG=-g. Examples: +# +# make makefiles +# make makefiles CC="purify cc" +# make makefiles CC=cc OPT= +# +SHELL = /bin/sh + +default: update + +update depend printfck clean tidy depend_update: Makefiles + $(MAKE) $@ + +makefiles Makefiles: + $(MAKE) -f Makefile.in Makefiles diff --git a/postfix/Makefile.in b/postfix/Makefile.in new file mode 100644 index 000000000..e718d0104 --- /dev/null +++ b/postfix/Makefile.in @@ -0,0 +1,42 @@ +SHELL = /bin/sh +WARN = -Wmissing-prototypes +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 + +default: update + +makefiles Makefiles: + set -e; for i in $(DIRS); do \ + (set -e; echo "[$$i]"; cd $$i; rm -f Makefile; \ + $(MAKE) -f Makefile.in Makefile); \ + done; + rm -f Makefile; (set -e; sh makedefs; cat Makefile.in) >Makefile + +update printfck: + set -e; for i in $(DIRS); do \ + (set -e; echo "[$$i]"; cd $$i; $(MAKE) $(OPTS) $@) || exit 1; \ + done + +depend clean: + set -e; for i in $(DIRS); do \ + (set -e; echo "[$$i]"; cd $$i; $(MAKE) $@) || exit 1; \ + done + +depend_update: + set -e; for i in $(DIRS); do \ + (set -e; echo "[$$i]"; cd $$i; $(MAKE) depend && $(MAKE) $(OPTS) update) \ + || exit 1; \ + done + +tidy: clean + rm -f Makefile */Makefile + cp Makefile.init Makefile + -rm -f *core */*core .nfs* .pure bin/* lib/* include/* */.nfs* */.pure \ + *.out */*.out */*.db */*.a *~ */*~ *- */*- *.orig */*.orig *.bak \ + */*.bak make.err + find . -type s -print | xargs rm -f + find . -type d -print | xargs chmod 755 + find . -type f -print | xargs chmod a+r diff --git a/postfix/Makefile.init b/postfix/Makefile.init new file mode 100644 index 000000000..424db7120 --- /dev/null +++ b/postfix/Makefile.init @@ -0,0 +1,18 @@ +# Usage: +# make makefiles [CC=compiler] [OPT=compiler-flags] [DEBUG=debug-flags] +# +# The defaults are: CC=gcc, OPT=-O, and DEBUG=-g. Examples: +# +# make makefiles +# make makefiles CC="purify cc" +# make makefiles CC=cc OPT= +# +SHELL = /bin/sh + +default: update + +update depend printfck clean tidy depend_update: Makefiles + $(MAKE) $@ + +makefiles Makefiles: + $(MAKE) -f Makefile.in Makefiles diff --git a/postfix/PORTING b/postfix/PORTING new file mode 100644 index 000000000..46df4486f --- /dev/null +++ b/postfix/PORTING @@ -0,0 +1,23 @@ +In order to port software to a new platform: + +- Choose a SYSTEMTYPE name for the new system. Please use a name +that includes the major version of the operating system (such as +SUNOS4 or LINUX2), so that different releases of the same system +can be supported without confusion. + +- Add a case statement to the "makedefs" shell script in the +top-level directory that recognizes the new system reliably, and +that emits the right system-specific information. Be sure to make +the code robust against user PATH settings; if the system offers +multiple UNIX flavors (e.g. BSD and SYSV) be sure to build for the +native flavor, not the emulated one. + +- Add an #ifdef SYSTEMTYPE section to the central util/sys_defs.h +include file. You may have to invent new feature macros. Please +choose sensible feature macro names such as HAS_DBM or +FIONREAD_IN_SYS_FILIO_H. I strongly recommend against #ifdef +SYSTEMTYPE dependencies in individual source files. This may seem +to be the quickest solution, but it will create a mess that becomes +increasingly difficult to maintain over time. Moreover, with the +next port you'd have to place #ifdefs all over the source code +again. diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES new file mode 100644 index 000000000..be41fe925 --- /dev/null +++ b/postfix/RELEASE_NOTES @@ -0,0 +1,62 @@ +This release introduces lots of new functionality in response to feedback +from users. + +Incompatible changes: +===================== + +- The syntax of the transport table has changed. An entry like: + + customer.org smtp:[gateway.customer.org] + + no longer forwards mail for anything.customer.org. For that you + need to specify: + + customer.org smtp:[gateway.customer.org] + .customer.org smtp:[gateway.customer.org] + + This change makes tranport tables more compatible with + sendmail mailer tables. + +- The format of syslog records has changed. A client is now always +logged as hostname[address]; the pickup daemon logs queue file uid +and sender address. + +Major changes over the previous version: +======================================== + +- Junk mail restrictions can now be postoned to the RCPT TO command. +Specify: "smtpd_recipient_restrictions = reject_maps_rbl...". + +- More flexible interface for delivery to e.g., cyrus IMAP without +need for PERL scripts to munge recipient addresses. In addition to +$sender, $nexthop and $recipient, the pipe mailer now also supports +$user, $extension and $mailbox. + +- New mail now has precedence over deferred mail, plus some other +tweaks to make bulk mail go faster. But it ain't no cure for massive +network outages. + +- Watchdog timer for systems that cause the Postfix queue manager +to lock up, so it recovers without human intervention. + +- Delivery to qmail-style maildir files, which is good for NFS +environments. Specify "home_mailbox = Maildir/", or specify +/file/name/ in aliases or in .forward files. The trailing / is +required to turn on maildir delivery. + +- Incremental updates of aliases and maps. Specify "postmap -i +mapname" and it will read new entries from stdin. + +- Newaliases will now update more than one alias database. +Specify the names with the main.cf "alias_database" parameter. + +- Address masquerading exceptions to prevent users from being +masqueraded. Specify "masquerade_exceptions = root". + +- A pipelined SMTP client. Deliveries to Postfix, qmail, LSOFT, +zmailer, and exim (once it's fixed) speed up by some 30% for short +messages with one recipient, with more for multi-recipient mails. + +- Hook for local delivery to "|command" via the smrsh restricted +shell, to restrict what commands may be used in .forward etc. files. +Specify "local_command_shell = /some/where/smrsh -c". diff --git a/postfix/TODO b/postfix/TODO new file mode 100644 index 000000000..280c4c224 --- /dev/null +++ b/postfix/TODO @@ -0,0 +1,274 @@ + +one queue per rcpt hurts when delivering to agents that don't +get stuck on shell commands or mailbox locks + +xxx: bounced as yyy (bounced mail); xxx forwarded as zzz (mail +expanded via :include:). + +postconf -f filename + +more general relocated feature - perhaps better to bounce recipients +at the SMTP port. + +use $mydomain when hostname is not FQDN. + +generic daemon that listens on fifo and runs command + +make sendmail/smtpd/cleanup output directory/fifo configurable + +if postdrop scrutinizes input, skip the overhead in the pickup +daemon. + +luser relay + +add a threshold to sendmail etc. stderr logging, so that class +"info" messages don't go to stderr. + +need a configurable mailbox locking method with system-specific +default, so people don't have to recompile just to turn of fcntl() +locks to work around SUN mailtool. + +implement an UCE control to accept mail if the sender domain sender +lists us as MX host (rafal wiosna). By the same token, implement +a control to accept mail when the client hostname/parent domain +lists us as their MX host. + +with recipient delimiter enabled, append the unmatched recipient +of @virtual.domain patterns as extension to right-hand recipient, +for qmail-like virtual mapping. + +received: headers should be generated by the cleanup daemon, and +client attributes ("with", "from", etc.) should be passed along +with the message. This guarantees that forwarded/aliased mail gets +stamped with the queue ID. + +trivial-rewrite etc.: after reload, close the listen socket and +wait until all clients disconnect. + +In qmgr_entry.c, turn off random walk by default. + +toss double-bounce mail even when mail for the local machine is +redirected to another box. See mail_addr_double_bounce(). + +represent peer as object, not as name + addr arguments + +ignore sender: header when different from envelope? + +smtp client: optionally log every MX host contacted + +remote showq access (cookie in maildrop or print some text to inform +the user) + +defer: explain mail was bounced after N days + +multiple rewrite processes? + +log relay address in addition to host. + +gethostbyaddr() uses native name services, which can be slow. + +can we detect a client that ignores error responses? + +way to block inbound mail based on recipient suffix? + +when client begins with non-SMTP data, log warning + +when non-SMTP follows ".", log warning. + +On linux syslogd needs -/file/name + +can Postfix implement one switchboard instead of having all these +little lookup tables? + +make canonical/virtual/etc. table lookup order configurable + +allow /file/name or maptype_mapname in $mydestination + +make protocol errors soft errore? There are a lot of broken mailers +out there that sometimes croak and sometimes work. + +require @ in sender/rcpt (another restriction) + +figure out a way to pump recipients into qmgr before concurrency +starts to drop. + +pass on client etc/ attributes along with message to delivery agent + +pass on configurable info into external process environment + +scrutinize file opens in delivery agents just like in qmgr (better: +open the file and see if someone compromised the vmailer account +and is racing against us). + +cleanup: don't run out of memory with large amounts of bcc addresses + +cleanup: permit non-empty extra segment, so that mail posting +software can pass in bcc recipients. + +suspend/resume signals + master status (suspended/running) in PID +file. Maybe use FIFO instead. But, that means requests do not +arrive when the master is stuck. + +postedit queue-id command... + +more flexible mail queue list command + +multiple queues may make ETRN processing less painful because there +is less delayed mail to plow through. + +qmgr: configurable incoming/deferred mixing ratio so we can prioritize +new mail over old mail + +Replace [my.own.ip.addr] by domain name so that delivered-to has +the desired effect. + +Received: header and bounce text will be configurable with ${name} +macros. This requires that everything must cope with newlines in +config parameters (including the SMTP greeting bannner, yuck). + +Pass along the client hostname/posting user with queue files, to +be logged by the queue manager. + +showq: don't use mail_open_ok() - it assumes coordinated queue +access. + +trivial-rewrite: optionally, use DNS to fully qualify hostnames. + +smtp: optionally deal with MX records containing an address instead +of a name. + +pickup/cleanup/qmgr/local: add options record to control internal +features such as canonical/virtual mapping, VERPs etcetera. + +smtpd: when deciding if a destination is local, also look at the +virtual map. Perhaps we should move canonical and virtual lookups +back into the rewrite service, but under a different name, so they +do not get in the way if we do not want them. + +Queue manager: do not allocate queue slots when a destination +already has more than some threshold. This is to prevent a dead or +slow destination from filling up the queue manager's active queue, +preventing delivery to other destinations. However, such `fairness' +strategies should not cause Postfix to lose the benchmark race, so +we must be fair and smart at the same time :-) + +Add hook for (domain, user database) support. This is needed if +you have lots of real domains and can't afford a separate master.cf +delivery agent entry for each domain. + +Add support for DBZ databases, using the code from INN. Reportedly, +GDB handles large numbers of keys poorly. + +Make the number of time bits in the queue ID configurable, or at +least a little larger. + +Change the front-end to cleanup protocol so that the front-end +sends the expected message size, and so that the cleanup service +can report if there is enough space. This is useful only for the +SMTP server, because pickup can't produce bounce requests: the +bounce service can't read the maildrop file. + +On systems with functional UNIX-domain sockets, use that instead +of FIFOs to trigger the pickup and qmgr services. This allows for +some coupling between front-end programs and queue manager, so that +a burst of inbound mail does not lock out the queue manager from +accessing the queue, causing outbound delivery to stop. + +There is a need to run `master' services outside the "master" +environment, either for testing (new config files) or for production. +For consistency reasons, programs file names should be taken from +the master.cf file. + + - The showq service. Used by the super user when the mail system + is down. + + - The smtpd service for "sendmail -bs" emulation. Used by some + mail posting agents. Output to the maildrop, so that messages + can be posted even when the mail system is down. + + - The rewrite engine for "sendmail -bt" emulation, for off-line + testing of configuration files. Requires a method to override + the location of the rewriting rules file. Or, perhaps there + should be an official place (/etc/vmailer/testbed?) for playing + with config files. + +postfix-script: detect and/or build missing alias database. In +order to do this we must extract the alias_maps parameter from the +main.cf file, and create any missing files with the right ownerships. + +SunOS 5.4 sendmail seems to include the null byte in alias keys +and values, like almost every UNIX system; SunOS 5.5 sendmail does +not include these nulls. Need to add support for SunOS 5.4. NIS +alias maps always include the null terminator... + +implement the return-receipt-to notification service. + +Implement real address rewriting. + +default alias for mail to non-existent users. How useful is this +when the postmaster already gets notices of mail that could not be +delivered by the local mail system? And how do we pass around the +original envelope recipient once it has been "aliased" to the +address for non-existent users? + +owner-default alias to capture all mailing list errors. Or perhaps +they should just set up the appropriate owner-foo aliases in their +alias database? + +make mail_params module the main config interface; no calls from +config.c to routines in mail_params.c + +resolve/rewrite clients should share connection + +postfix-script: make sure permissions of queue (and anything below) +are sane. + +bounce/defer: provide attribute-value interface, for better logging +(expanded-from etc.) and non-delivery reports. + +Postfix-Options: header, to turn on qmail-like VERPs. But, these +must be accessible only for locally-posted mail (not mail that +arrives via UUCP). + +Maintain per-client short-term host status, so we can slow down +unreasonable clients + +Make archiving delivered mail a REAL option (queue manager). What +about one archive per day. The magic could be put into the mail +queue name routines. Just make it aware of the date. + +Will the mail system be faster when we avoid moving new messages +incoming->active? How would one detect the arrival of new files? + +pickup: pass file descriptor to cleanup instead of copying data. +This violates the principle that all front-end programs protect +the mail system against unreasonably-long inputs. + +True ETRN means kick the host out of the queue manager's "dead +hosts" table & move mail from the "hold" queue for that site to +the incoming queue. + +Option to make a copy of all mail passing through the mail system. + +The message ID is built by concatenating the time of day in seconds +with the queue id. We must ensure that a queue id is unique for at +least one second, otherwise multiple messages will have the same +message ID. Queue ids will always collide after a while. The NFS +generation number for the queue file would be useful, but there is +no portable interface to get it, and we cannot depend on the system +having NFS support enabled. If a 1-microsecond resolution is +sufficient, we could compose the queue ID from the inode number +plus 6 decimal digits or 5 hex ones for the time in microseconds. +Or, use a smarter encoding with more bits per character. + +postfix-script: make sure that each queue file matches its file id +or we might lose mail. + +postfix-script: do database fixups as the unprivileged user + +Put a version file in the conf directory or add option to vmail +control command to print the version (requires vmconf tool that +can query main.cf.). + +Maintain a pool of pre-allocated queue files, to eliminate file +creation and deletion overhead. diff --git a/postfix/bin/.keep b/postfix/bin/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/postfix/bounce/.indent.pro b/postfix/bounce/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/bounce/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/bounce/.printfck b/postfix/bounce/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/bounce/.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/bounce/Makefile.in b/postfix/bounce/Makefile.in new file mode 100644 index 000000000..3279399a6 --- /dev/null +++ b/postfix/bounce/Makefile.in @@ -0,0 +1,121 @@ +SHELL = /bin/sh +SRCS = bounce.c bounce_append_service.c bounce_flush_service.c \ + bounce_cleanup.c +OBJS = bounce.o bounce_append_service.o bounce_flush_service.o \ + bounce_cleanup.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 = bounce +INC_DIR = ../include +LIBS = ../lib/libmaster.a ../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 + cp *.h 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' +bounce.o: bounce.c +bounce.o: ../include/sys_defs.h +bounce.o: ../include/msg.h +bounce.o: ../include/vstring.h +bounce.o: ../include/vbuf.h +bounce.o: ../include/vstream.h +bounce.o: ../include/stringops.h +bounce.o: ../include/mail_proto.h +bounce.o: ../include/iostuff.h +bounce.o: ../include/mail_queue.h +bounce.o: ../include/mail_params.h +bounce.o: ../include/config.h +bounce.o: ../include/bounce.h +bounce.o: ../include/mail_server.h +bounce.o: bounce_service.h +bounce_append_service.o: bounce_append_service.c +bounce_append_service.o: ../include/sys_defs.h +bounce_append_service.o: ../include/msg.h +bounce_append_service.o: ../include/vstring.h +bounce_append_service.o: ../include/vbuf.h +bounce_append_service.o: ../include/vstream.h +bounce_append_service.o: ../include/stringops.h +bounce_append_service.o: ../include/mail_queue.h +bounce_append_service.o: ../include/quote_822_local.h +bounce_append_service.o: ../include/deliver_flock.h +bounce_append_service.o: bounce_service.h +bounce_cleanup.o: bounce_cleanup.c +bounce_cleanup.o: ../include/sys_defs.h +bounce_cleanup.o: ../include/msg.h +bounce_cleanup.o: ../include/mymalloc.h +bounce_cleanup.o: ../include/vstring.h +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 diff --git a/postfix/bounce/bounce.c b/postfix/bounce/bounce.c new file mode 100644 index 000000000..1698130d7 --- /dev/null +++ b/postfix/bounce/bounce.c @@ -0,0 +1,281 @@ +/*++ +/* NAME +/* bounce 8 +/* SUMMARY +/* Postfix message bounce or defer daemon +/* SYNOPSIS +/* \fBbounce\fR [generic Postfix daemon options] +/* DESCRIPTION +/* The \fBbounce\fR daemon maintains per-message log files with +/* non-delivery status information. Each log file is named after the +/* queue file that it corresponds to, and is kept in a queue subdirectory +/* named after the service name in the \fBmaster.cf\fR file (either +/* \fBbounce\fR or \fBdefer\fR). +/* This program expects to be run from the \fBmaster\fR(8) process +/* manager. +/* +/* The \fBbounce\fR daemon processes two types of service requests: +/* .IP \(bu +/* Append a recipient status record to a per-message log file. +/* .IP \(bu +/* Post a bounce message, with a copy of a log file and of the +/* corresponding message. When the bounce is posted successfully, +/* the log file is deleted. +/* .PP +/* The software does a best effort to notify the sender that there +/* was a problem. A notification is sent even when the log file +/* or original message cannot be read. +/* +/* Optionally, a client can request that the per-message log file be +/* deleted when the requested operation fails. +/* This is used by clients that cannot retry transactions by +/* themselves, and that depend on retry logic in their own client. +/* STANDARDS +/* RFC 822 (ARPA Internet Text Messages) +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8). +/* BUGS +/* The log files use an ad-hoc, unstructured format. This will have +/* to change in order to easily support standard delivery status +/* notifications. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. Use the \fBpostfix reload\fR command after +/* a configuration change. +/* .IP \fBbounce_size_limit\fR +/* Limit the amount of original message context that is sent in +/* a non-delivery notification. +/* .IP \fBmail_name\fR +/* Use this mail system name in the introductory text at the +/* start of a bounce message. +/* .IP \fBnotify_classes\fR +/* Notify the postmaster of bounced mail when this parameter +/* includes the \fBbounce\fR class. For privacy reasons, the message +/* body is not included. +/* SEE ALSO +/* master(8) process manager +/* qmgr(8) queue manager +/* syslogd(8) system logging +/* 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 + +/* Global library. */ + +#include +#include +#include +#include +#include + +/* Single-threaded server skeleton. */ + +#include + +/* Application-specific. */ + +#include "bounce_service.h" + + /* + * Tunables. + */ +int var_bounce_limit; +char *var_notify_classes; + + /* + * We're single threaded, so we can avoid some memory allocation overhead. + */ +static VSTRING *queue_id; +static VSTRING *queue_name; +static VSTRING *recipient; +static VSTRING *sender; +static VSTRING *why; + +#define STR vstring_str + +/* bounce_append_proto - bounce_append server protocol */ + +static int bounce_append_proto(char *service_name, VSTREAM *client) +{ + int flags; + + /* + * Read the and validate the client request. + */ + if (mail_command_read(client, "%d %s %s %s", + &flags, queue_id, recipient, why) != 4) { + msg_warn("malformed request"); + return (-1); + } + if (mail_queue_id_ok(STR(queue_id)) == 0) { + msg_warn("malformed queue id: %s", printable(STR(queue_id), '?')); + return (-1); + } + if (msg_verbose) + msg_info("bounce_append_proto: service=%s id=%s to=%s why=%s", + service_name, STR(queue_id), STR(recipient), STR(why)); + + /* + * On request by the client, set up a trap to delete the log file in case + * of errors. + */ + if (flags & BOUNCE_FLAG_CLEAN) + bounce_cleanup_register(service_name, STR(queue_id)); + + /* + * Execute the request. + */ + return (bounce_append_service(service_name, STR(queue_id), + STR(recipient), STR(why))); +} + +/* bounce_flush_proto - bounce_flush server protocol */ + +static int bounce_flush_proto(char *service_name, VSTREAM *client) +{ + int flags; + + /* + * Read and validate the client request. + */ + if (mail_command_read(client, "%d %s %s %s", + &flags, queue_name, queue_id, sender) != 4) { + msg_warn("malformed request"); + return (-1); + } + if (mail_queue_name_ok(STR(queue_name)) == 0) { + msg_warn("malformed queue name: %s", printable(STR(queue_name), '?')); + return (-1); + } + if (mail_queue_id_ok(STR(queue_id)) == 0) { + msg_warn("malformed queue id: %s", printable(STR(queue_id), '?')); + return (-1); + } + if (msg_verbose) + msg_info("bounce_flush_proto: service=%s queue=%s id=%s sender=%s", + service_name, STR(queue_name), STR(queue_id), STR(sender)); + + /* + * On request by the client, set up a trap to delete the log file in case + * of errors. + */ + if (flags & BOUNCE_FLAG_CLEAN) + bounce_cleanup_register(service_name, STR(queue_id)); + + /* + * Execute the request. + */ + return (bounce_flush_service(service_name, STR(queue_name), + STR(queue_id), STR(sender))); +} + +/* bounce_service - parse bounce command type and delegate */ + +static void bounce_service(VSTREAM *client, char *service_name, char **argv) +{ + int command; + int status; + + /* + * Sanity check. This service takes no command-line arguments. The + * service name should be usable as a subdirectory name. + */ + if (argv[0]) + msg_fatal("unexpected command-line argument: %s", argv[0]); + if (mail_queue_name_ok(service_name) == 0) + msg_fatal("malformed service name: %s", service_name); + + /* + * Read and validate the first parameter of the client request. Let the + * request-specific protocol routines take care of the remainder. + */ + 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); + } else if (command == BOUNCE_CMD_APPEND) { + status = bounce_append_proto(service_name, client); + } else { + msg_warn("unknown command: %d", command); + status = -1; + } + + /* + * When the request has completed, send the completion status to the + * client. + */ + mail_print(client, "%d", status); + vstream_fflush(client); + + /* + * When a cleanup trap was set, delete the log file in case of error. + * This includes errors while sending the completion status to the + * client. + */ + if (bounce_cleanup_path) { + if (status || vstream_ferror(client)) + bounce_cleanup_log(); + bounce_cleanup_unregister(); + } +} + +/* post_jail_init - initialize after entering chroot jail */ + +static void post_jail_init(void) +{ + + /* + * Initialize. We're single threaded so we can reuse some memory upon + * successive requests. + */ + queue_id = vstring_alloc(10); + queue_name = vstring_alloc(10); + recipient = vstring_alloc(10); + sender = vstring_alloc(10); + why = vstring_alloc(10); +} + +/* main - the main program */ + +int main(int argc, char **argv) +{ + static CONFIG_INT_TABLE int_table[] = { + VAR_BOUNCE_LIMIT, DEF_BOUNCE_LIMIT, &var_bounce_limit, 1, 0, + 0, + }; + static CONFIG_STR_TABLE str_table[] = { + VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0, + 0, + }; + + /* + * Pass control to the single-threaded service skeleton. + */ + single_server_main(argc, argv, bounce_service, + MAIL_SERVER_INT_TABLE, int_table, + MAIL_SERVER_STR_TABLE, str_table, + MAIL_SERVER_POST_INIT, post_jail_init, + 0); +} diff --git a/postfix/bounce/bounce_append_service.c b/postfix/bounce/bounce_append_service.c new file mode 100644 index 000000000..36e613891 --- /dev/null +++ b/postfix/bounce/bounce_append_service.c @@ -0,0 +1,124 @@ +/*++ +/* NAME +/* bounce_append_service 3 +/* SUMMARY +/* append record to bounce log, server side +/* SYNOPSIS +/* #include "bounce_service.h" +/* +/* int bounce_append_service(queue_id, recipient, why) +/* char *queue_id; +/* char *recipient; +/* char *why; +/* DESCRIPTION +/* This module implements the server side of the bounce_append() +/* (append bounce log) request. This routine either succeeds or +/* it raises a fatal error. +/* DIAGNOSTICS +/* Fatal errors: all file access errors; memory allocation errors. +/* BUGS +/* SEE ALSO +/* bounce(3) basic bounce service client interface +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include + +/* Application-specific. */ + +#include "bounce_service.h" + +/* bounce_append_service - append bounce log */ + +int bounce_append_service(char *service, char *queue_id, + char *recipient, char *why) +{ + VSTRING *in_buf = vstring_alloc(100); + VSTRING *out_buf = vstring_alloc(100); + VSTREAM *log; + long orig_length; + + /* + * This code is paranoid for a good reason. Once the bounce service takes + * responsibility, the mail system will make no further attempts to + * deliver this recipient. Whenever file access fails, assume that the + * system is under stress or that something has been mis-configured, and + * force a backoff by raising a fatal run-time error. + */ + log = mail_queue_open(service, queue_id, + O_WRONLY | O_APPEND | O_CREAT, 0600); + if (log == 0) + msg_fatal("open file %s %s: %m", service, queue_id); + + /* + * Lock out other processes to avoid truncating someone else's data in + * case of trouble. + */ + if (deliver_flock(vstream_fileno(log), (VSTRING *) 0) < 0) + msg_fatal("lock file %s %s: %m", service, queue_id); + + /* + * Now, go for it. Append a record. Truncate the log to the original + * length when the append operation fails. We use the plain stream-lf + * file format because we do not need anything more complicated. As a + * benefit, we can still recover some data when the file is a little + * garbled. + */ + if ((orig_length = vstream_fseek(log, 0L, SEEK_END)) < 0) + msg_fatal("seek file %s %s: %m", service, queue_id); + + if (*recipient) + vstream_fprintf(log, "<%s>: ", + printable(vstring_str(quote_822_local(in_buf, recipient)), '?')); + vstream_fputs(printable(why, '?'), log); + vstream_fputs("\n\n", log); + + if (vstream_fflush(log) != 0 || fsync(vstream_fileno(log)) < 0) { +#ifndef NO_TRUNCATE + if (ftruncate(vstream_fileno(log), (off_t) orig_length) < 0) + msg_fatal("truncate file %s %s: %m", service, queue_id); +#endif + msg_fatal("append file %s %s: %m", service, queue_id); + } + + /* + * Darn. If closing the log detects a problem, the only way to undo the + * damage is to open the log once more, and to truncate the log to the + * original length. But, this could happen only when the log is kept on a + * remote file system, and that is not recommended practice anyway. + */ + if (vstream_fclose(log) != 0) + msg_warn("append file %s %s: %m", service, queue_id); + + vstring_free(in_buf); + vstring_free(out_buf); + return (0); +} diff --git a/postfix/bounce/bounce_cleanup.c b/postfix/bounce/bounce_cleanup.c new file mode 100644 index 000000000..483d21ae1 --- /dev/null +++ b/postfix/bounce/bounce_cleanup.c @@ -0,0 +1,177 @@ +/*++ +/* NAME +/* bounce_cleanup 3 +/* SUMMARY +/* cleanup logfile upon error +/* SYNOPSIS +/* #include "bounce_service.h" +/* +/* int bounce_cleanup_registered() +/* +/* void bounce_cleanup_register(queue_id) +/* char *queue_id; +/* +/* void bounce_cleanup_log(void) +/* +/* void bounce_cleanup_unregister(void) +/* DESCRIPTION +/* This module implements support for deleting the current +/* bounce logfile in case of errors, and upon the arrival +/* of a SIGTERM signal (shutdown). +/* +/* bounce_cleanup_register() registers a callback routine with the +/* run-time error handler, for automatic logfile removal in case +/* of a fatal run-time error. +/* +/* bounce_cleanup_unregister() cleans up storage used by +/* bounce_cleanup_register(). +/* +/* In-between bounce_cleanup_register() and bounce_cleanup_unregister() +/* calls, a call of bounce_cleanup_log() will delete the registered +/* bounce logfile. +/* +/* bounce_cleanup_registered() returns non-zero when a cleanup +/* trap has been set. +/* DIAGNOSTICS +/* Fatal error: all file access errors. Panic: nested calls of +/* bounce_cleanup_register(); any calls of bounce_cleanup_unregister() +/* or bounce_cleanup_log() without preceding bounce_cleanup_register() +/* call. +/* BUGS +/* SEE ALSO +/* master(8) process 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 +/*--*/ + +/* System library. */ + +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include + +/* Application-specific. */ + +#include "bounce_service.h" + + /* + * Support for removing a logfile when an update fails. In order to do this, + * we save a copy of the currently-open logfile name, and register a + * callback function pointer with the run-time error handler. The saved + * pathname is made global so that the application can see whether or not a + * trap was set up. + */ +static MSG_CLEANUP_FN bounce_cleanup_func; /* saved callback */ +VSTRING *bounce_cleanup_path; /* saved path name */ + +/* bounce_cleanup_callback - run-time callback to cleanup logfile */ + +static void bounce_cleanup_callback(void) +{ + + /* + * Remove the logfile. + */ + if (bounce_cleanup_path) + bounce_cleanup_log(); + + /* + * Execute the saved cleanup action. + */ + if (bounce_cleanup_func) + bounce_cleanup_func(); +} + +/* bounce_cleanup_log - clean up the logfile */ + +void bounce_cleanup_log(void) +{ + char *myname = "bounce_cleanup_log"; + + /* + * Sanity checks. + */ + if (bounce_cleanup_path == 0) + msg_panic("%s: no cleanup context", myname); + + /* + * This function may be called before a logfile is created or after it + * has been deleted, so do not complain. + */ + (void) unlink(vstring_str(bounce_cleanup_path)); +} + +/* bounce_cleanup_sig - signal handler */ + +static void bounce_cleanup_sig(int sig) +{ + + /* + * Running as a signal handler - don't do complicated stuff. + */ + if (bounce_cleanup_path) + (void) unlink(vstring_str(bounce_cleanup_path)); + exit(sig); +} + +/* bounce_cleanup_register - register logfile to clean up */ + +void bounce_cleanup_register(char *service, char *queue_id) +{ + char *myname = "bounce_cleanup_register"; + + /* + * Sanity checks. + */ + if (bounce_cleanup_path) + msg_panic("%s: nested call", myname); + + /* + * Save a copy of the logfile path, and of the last callback function + * pointer registered with the run-time error handler. + */ + bounce_cleanup_path = vstring_alloc(10); + (void) mail_queue_path(bounce_cleanup_path, service, queue_id); + bounce_cleanup_func = msg_cleanup(bounce_cleanup_callback); + signal(SIGTERM, bounce_cleanup_sig); +} + +/* bounce_cleanup_unregister - unregister logfile to clean up */ + +void bounce_cleanup_unregister(void) +{ + char *myname = "bounce_cleanup_unregister"; + + /* + * Sanity checks. + */ + if (bounce_cleanup_path == 0) + msg_panic("%s: no cleanup context", myname); + + /* + * Restore the saved callback function pointer, and release storage for + * the saved logfile pathname. + */ + signal(SIGTERM, SIG_DFL); + (void) msg_cleanup(bounce_cleanup_func); + vstring_free(bounce_cleanup_path); + bounce_cleanup_path = 0; +} diff --git a/postfix/bounce/bounce_flush_service.c b/postfix/bounce/bounce_flush_service.c new file mode 100644 index 000000000..32f79d764 --- /dev/null +++ b/postfix/bounce/bounce_flush_service.c @@ -0,0 +1,359 @@ +/*++ +/* NAME +/* bounce_flush_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) +/* char *queue_name; +/* char *queue_id; +/* char *sender; +/* DESCRIPTION +/* This module implements the server side of the bounce_flush() +/* (send bounce message) request. +/* +/* 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 +/* 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. +/* DIAGNOSTICS +/* Fatal error: error opening existing file. Warnings: corrupt +/* message file. A corrupt message is saved to the "corrupt" +/* queue for further inspection. +/* BUGS +/* SEE ALSO +/* bounce(3) basic bounce service client interface +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "bounce_service.h" + +#define STR vstring_str + +/* bounce_header - generate bounce message header */ + +static int bounce_header(VSTREAM *bounce, VSTRING *buf, char *dest) +{ + + /* + * Print a minimal bounce header. The cleanup service will add other + * headers and will make all addresses fully qualified. + */ + 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)); + post_mail_fputs(bounce, ""); + return (vstream_ferror(bounce)); +} + +/* bounce_boilerplate - generate boiler-plate text */ + +static int bounce_boilerplate(VSTREAM *bounce, VSTRING *buf) +{ + + /* + * Print the message body with the problem report. XXX For now, we use a + * fixed bounce template. We could use a site-specific parametrized + * template with ${name} macros and we could do wonderful things such as + * word wrapping to make the text look nicer. No matter how hard we would + * try, receiving bounced mail will always suck. + */ + post_mail_fprintf(bounce, "This is the %s program at host %s.", + var_mail_name, var_myhostname); + post_mail_fputs(bounce, ""); + post_mail_fprintf(bounce, + "I'm sorry to have to inform you that the message returned"); + post_mail_fprintf(bounce, + "below could not be delivered to one or more destinations."); + 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 you do so, please include this problem report. You can"); + post_mail_fprintf(bounce, + "delete your own text from the message returned below."); + post_mail_fputs(bounce, ""); + post_mail_fprintf(bounce, "\t\t\tThe %s program", var_mail_name); + return (vstream_ferror(bounce)); +} + +/* bounce_print - line_wrap callback */ + +static void bounce_print(const char *str, int len, int indent, char *context) +{ + VSTREAM *bounce = (VSTREAM *) context; + + post_mail_fprintf(bounce, "%*s%.*s", indent, "", len, str); +} + +/* bounce_diagnostics - send bounce log report */ + +static int bounce_diagnostics(char *service, VSTREAM *bounce, VSTRING *buf, char *queue_id) +{ + VSTREAM *log; + + /* + * If the bounce log cannot be found, do not raise a fatal run-time + * error. There is nothing we can do about the error, and all we are + * doing is to inform the sender of a delivery problem, Bouncing a + * message does not have to be a perfect job. But if the system IS + * running out of resources, raise a fatal run-time error and force a + * backoff. + */ + if ((log = mail_queue_open(service, queue_id, O_RDONLY, 0)) == 0) { + if (errno != ENOENT) + msg_fatal("open %s %s: %m", service, queue_id); + post_mail_fputs(bounce, ""); + post_mail_fputs(bounce, "\t--- Delivery error report unavailable ---"); + post_mail_fputs(bounce, ""); + } + + /* + * Append a copy of the delivery error log. Again, we're doing a best + * effort, so there is no point raising a fatal run-time error in case of + * a logfile read error. Wrap long lines, filter non-printable + * characters, and prepend one blank, so this data can safely be piped + * into other programs. + */ + else { + +#define LENGTH 79 +#define INDENT 4 + post_mail_fputs(bounce, ""); + post_mail_fputs(bounce, "\t--- Delivery error report follows ---"); + post_mail_fputs(bounce, ""); + while (vstream_ferror(bounce) == 0 && vstring_fgets_nonl(buf, log)) { + printable(STR(buf), '_'); + line_wrap(STR(buf), LENGTH, INDENT, bounce_print, (char *) bounce); + if (vstream_ferror(bounce) != 0) + break; + } + if (vstream_fclose(log)) + msg_warn("read bounce log %s: %m", queue_id); + } + return (vstream_ferror(bounce)); +} + +/* bounce_original - send a copy of the original to the victim */ + +static int bounce_original(char *service, VSTREAM *bounce, VSTRING *buf, + char *queue_name, char *queue_id, int headers_only) +{ + int status = 0; + VSTREAM *src; + int rec_type; + int bounce_length; + + /* + * If the original message cannot be found, do not raise a run-time + * error. There is nothing we can do about the error, and all we are + * doing is to inform the sender of a delivery problem. Bouncing a + * message does not have to be a perfect job. But if the system IS + * running out of resources, raise a fatal run-time error and force a + * backoff. + */ + if ((src = mail_queue_open(queue_name, queue_id, O_RDONLY, 0)) == 0) { + if (errno != ENOENT) + msg_fatal("open %s %s: %m", service, queue_id); + post_mail_fputs(bounce, "\t--- Undelivered message unavailable ---"); + return (vstream_ferror(bounce)); + } + + /* + * Append a copy of the rejected message. + */ + post_mail_fputs(bounce, "\t--- Undelivered message follows ---"); + post_mail_fputs(bounce, ""); + + /* + * Skip over the original message envelope records. If the envelope is + * corrupted just send whatever we can (remember this is a best effort, + * it does not have to be perfect). + */ + while ((rec_type = rec_get(src, buf, 0)) > 0) + if (rec_type == REC_TYPE_MESG) + break; + + /* + * Copy the original message contents. Limit the amount of bounced text + * so there is a better chance of the bounce making it back. We're doing + * raw record output here so that we don't throw away binary transparency + * yet. + */ +#define IS_HEADER(s) (ISSPACE(*(s)) || is_header(s)) + + bounce_length = 0; + while (status == 0 && (rec_type = rec_get(src, buf, 0)) > 0) { + if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT) + break; + if (headers_only && !IS_HEADER(vstring_str(buf))) + break; + if (var_bounce_limit == 0 || bounce_length < var_bounce_limit) { + bounce_length += VSTRING_LEN(buf); + status = (REC_PUT_BUF(bounce, rec_type, buf) != rec_type); + } + } + if (headers_only == 0 && rec_type != REC_TYPE_XTRA) + status |= mark_corrupt(src); + if (vstream_fclose(src)) + msg_warn("read message file %s %s: %m", queue_name, queue_id); + return (status); +} + +/* bounce_flush_service - send a bounce */ + +int bounce_flush_service(char *service, char *queue_name, + char *queue_id, char *recipient) +{ + VSTRING *buf = vstring_alloc(100); + const char *double_bounce_addr; + int status = 1; + VSTREAM *bounce; + +#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. + */ + 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. + */ + 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 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. + */ + if (status == 0 && !TO_POSTMASTER(recipient) + && strcasecmp(recipient, double_bounce_addr) != 0 + && (MAIL_ERROR_BOUNCE & name_mask(mail_error_masks, var_notify_classes))) { + + /* + * 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); + + /* + * Finish the bounce, and update the completion status. + */ + status |= post_mail_fclose(bounce); + } + + /* + * Examine the completion status. Delete the bounce log file only when + * the bounce was posted successfully. + */ + if (status == 0 && mail_queue_remove(service, queue_id) && errno != ENOENT) + msg_fatal("remove %s %s: %m", service, queue_id); + + /* + * Cleanup. + */ + vstring_free(buf); + + return (status); +} diff --git a/postfix/bounce/bounce_service.h b/postfix/bounce/bounce_service.h new file mode 100644 index 000000000..8584b9810 --- /dev/null +++ b/postfix/bounce/bounce_service.h @@ -0,0 +1,45 @@ +/*++ +/* NAME +/* bounce_service 3h +/* SUMMARY +/* bounce message service +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * bounce_append_service.c + */ +extern int bounce_append_service(char *, char *, char *, char *); + + /* + * bounce_flush_service.c + */ +extern int bounce_flush_service(char *, char *, char *, char *); + + /* + * bounce_cleanup.c + */ +extern VSTRING *bounce_cleanup_path; +extern void bounce_cleanup_register(char *, char *); +extern void bounce_cleanup_log(void); +extern void bounce_cleanup_unregister(void); + +#define bounce_cleanup_registered() (bounce_cleanup_path != 0) + +/* 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 +/*--*/ diff --git a/postfix/cleanup/.indent.pro b/postfix/cleanup/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/cleanup/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/cleanup/.printfck b/postfix/cleanup/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/cleanup/.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/cleanup/Makefile.in b/postfix/cleanup/Makefile.in new file mode 100644 index 000000000..1eb83bc7a --- /dev/null +++ b/postfix/cleanup/Makefile.in @@ -0,0 +1,273 @@ +SHELL = /bin/sh +SRCS = cleanup.c cleanup_out.c cleanup_envelope.c cleanup_message.c \ + cleanup_extracted.c cleanup_state.c cleanup_skip.c cleanup_rewrite.c \ + cleanup_map11.c cleanup_map1n.c cleanup_masquerade.c \ + cleanup_out_recipient.c +OBJS = cleanup.o cleanup_out.o cleanup_envelope.o cleanup_message.o \ + cleanup_extracted.o cleanup_state.o cleanup_skip.o cleanup_rewrite.o \ + cleanup_map11.o cleanup_map1n.o cleanup_masquerade.o \ + cleanup_out_recipient.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 = cleanup +INC_DIR = ../include +LIBS = ../lib/libmaster.a ../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 + cp *.h 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' +cleanup.o: cleanup.c +cleanup.o: ../include/sys_defs.h +cleanup.o: ../include/msg.h +cleanup.o: ../include/vstring.h +cleanup.o: ../include/vbuf.h +cleanup.o: ../include/vstream.h +cleanup.o: ../include/mymalloc.h +cleanup.o: ../include/iostuff.h +cleanup.o: ../include/config.h +cleanup.o: ../include/cleanup_user.h +cleanup.o: ../include/mail_queue.h +cleanup.o: ../include/mail_proto.h +cleanup.o: ../include/opened.h +cleanup.o: ../include/bounce.h +cleanup.o: ../include/mail_params.h +cleanup.o: ../include/mail_stream.h +cleanup.o: ../include/mail_addr.h +cleanup.o: ../include/mail_server.h +cleanup.o: cleanup.h +cleanup.o: ../include/argv.h +cleanup.o: ../include/maps.h +cleanup.o: ../include/tok822.h +cleanup.o: ../include/resolve_clnt.h +cleanup.o: ../include/been_here.h +cleanup_envelope.o: cleanup_envelope.c +cleanup_envelope.o: ../include/sys_defs.h +cleanup_envelope.o: ../include/msg.h +cleanup_envelope.o: ../include/vstring.h +cleanup_envelope.o: ../include/vbuf.h +cleanup_envelope.o: ../include/vstream.h +cleanup_envelope.o: ../include/mymalloc.h +cleanup_envelope.o: ../include/record.h +cleanup_envelope.o: ../include/rec_type.h +cleanup_envelope.o: ../include/cleanup_user.h +cleanup_envelope.o: ../include/tok822.h +cleanup_envelope.o: ../include/resolve_clnt.h +cleanup_envelope.o: ../include/mail_params.h +cleanup_envelope.o: cleanup.h +cleanup_envelope.o: ../include/argv.h +cleanup_envelope.o: ../include/maps.h +cleanup_envelope.o: ../include/been_here.h +cleanup_envelope.o: ../include/mail_stream.h +cleanup_extracted.o: cleanup_extracted.c +cleanup_extracted.o: ../include/sys_defs.h +cleanup_extracted.o: ../include/msg.h +cleanup_extracted.o: ../include/vstring.h +cleanup_extracted.o: ../include/vbuf.h +cleanup_extracted.o: ../include/vstream.h +cleanup_extracted.o: ../include/argv.h +cleanup_extracted.o: ../include/mymalloc.h +cleanup_extracted.o: ../include/cleanup_user.h +cleanup_extracted.o: ../include/record.h +cleanup_extracted.o: ../include/rec_type.h +cleanup_extracted.o: cleanup.h +cleanup_extracted.o: ../include/maps.h +cleanup_extracted.o: ../include/tok822.h +cleanup_extracted.o: ../include/resolve_clnt.h +cleanup_extracted.o: ../include/been_here.h +cleanup_extracted.o: ../include/mail_stream.h +cleanup_map11.o: cleanup_map11.c +cleanup_map11.o: ../include/sys_defs.h +cleanup_map11.o: ../include/msg.h +cleanup_map11.o: ../include/vstring.h +cleanup_map11.o: ../include/vbuf.h +cleanup_map11.o: ../include/dict.h +cleanup_map11.o: ../include/vstream.h +cleanup_map11.o: ../include/mymalloc.h +cleanup_map11.o: ../include/cleanup_user.h +cleanup_map11.o: ../include/mail_addr_map.h +cleanup_map11.o: ../include/argv.h +cleanup_map11.o: ../include/maps.h +cleanup_map11.o: ../include/quote_822_local.h +cleanup_map11.o: cleanup.h +cleanup_map11.o: ../include/tok822.h +cleanup_map11.o: ../include/resolve_clnt.h +cleanup_map11.o: ../include/been_here.h +cleanup_map11.o: ../include/mail_stream.h +cleanup_map1n.o: cleanup_map1n.c +cleanup_map1n.o: ../include/sys_defs.h +cleanup_map1n.o: ../include/mymalloc.h +cleanup_map1n.o: ../include/msg.h +cleanup_map1n.o: ../include/argv.h +cleanup_map1n.o: ../include/vstring.h +cleanup_map1n.o: ../include/vbuf.h +cleanup_map1n.o: ../include/dict.h +cleanup_map1n.o: ../include/vstream.h +cleanup_map1n.o: ../include/mail_addr_map.h +cleanup_map1n.o: ../include/maps.h +cleanup_map1n.o: ../include/cleanup_user.h +cleanup_map1n.o: ../include/quote_822_local.h +cleanup_map1n.o: cleanup.h +cleanup_map1n.o: ../include/tok822.h +cleanup_map1n.o: ../include/resolve_clnt.h +cleanup_map1n.o: ../include/been_here.h +cleanup_map1n.o: ../include/mail_stream.h +cleanup_masquerade.o: cleanup_masquerade.c +cleanup_masquerade.o: ../include/sys_defs.h +cleanup_masquerade.o: ../include/msg.h +cleanup_masquerade.o: ../include/vstring.h +cleanup_masquerade.o: ../include/vbuf.h +cleanup_masquerade.o: ../include/argv.h +cleanup_masquerade.o: ../include/htable.h +cleanup_masquerade.o: ../include/mymalloc.h +cleanup_masquerade.o: ../include/stringops.h +cleanup_masquerade.o: ../include/mail_params.h +cleanup_masquerade.o: ../include/tok822.h +cleanup_masquerade.o: ../include/resolve_clnt.h +cleanup_masquerade.o: ../include/quote_822_local.h +cleanup_masquerade.o: cleanup.h +cleanup_masquerade.o: ../include/vstream.h +cleanup_masquerade.o: ../include/maps.h +cleanup_masquerade.o: ../include/been_here.h +cleanup_masquerade.o: ../include/mail_stream.h +cleanup_message.o: cleanup_message.c +cleanup_message.o: ../include/sys_defs.h +cleanup_message.o: ../include/msg.h +cleanup_message.o: ../include/vstring.h +cleanup_message.o: ../include/vbuf.h +cleanup_message.o: ../include/vstream.h +cleanup_message.o: ../include/argv.h +cleanup_message.o: ../include/split_at.h +cleanup_message.o: ../include/mymalloc.h +cleanup_message.o: ../include/record.h +cleanup_message.o: ../include/rec_type.h +cleanup_message.o: ../include/cleanup_user.h +cleanup_message.o: ../include/tok822.h +cleanup_message.o: ../include/resolve_clnt.h +cleanup_message.o: ../include/header_opts.h +cleanup_message.o: ../include/quote_822_local.h +cleanup_message.o: ../include/mail_params.h +cleanup_message.o: ../include/mail_date.h +cleanup_message.o: ../include/mail_addr.h +cleanup_message.o: ../include/is_header.h +cleanup_message.o: cleanup.h +cleanup_message.o: ../include/maps.h +cleanup_message.o: ../include/been_here.h +cleanup_message.o: ../include/mail_stream.h +cleanup_out.o: cleanup_out.c +cleanup_out.o: ../include/sys_defs.h +cleanup_out.o: ../include/msg.h +cleanup_out.o: ../include/vstring.h +cleanup_out.o: ../include/vbuf.h +cleanup_out.o: ../include/vstream.h +cleanup_out.o: ../include/record.h +cleanup_out.o: ../include/rec_type.h +cleanup_out.o: ../include/cleanup_user.h +cleanup_out.o: cleanup.h +cleanup_out.o: ../include/argv.h +cleanup_out.o: ../include/maps.h +cleanup_out.o: ../include/tok822.h +cleanup_out.o: ../include/resolve_clnt.h +cleanup_out.o: ../include/been_here.h +cleanup_out.o: ../include/mail_stream.h +cleanup_out_recipient.o: cleanup_out_recipient.c +cleanup_out_recipient.o: ../include/sys_defs.h +cleanup_out_recipient.o: ../include/argv.h +cleanup_out_recipient.o: ../include/been_here.h +cleanup_out_recipient.o: ../include/mail_params.h +cleanup_out_recipient.o: ../include/rec_type.h +cleanup_out_recipient.o: cleanup.h +cleanup_out_recipient.o: ../include/vstring.h +cleanup_out_recipient.o: ../include/vbuf.h +cleanup_out_recipient.o: ../include/vstream.h +cleanup_out_recipient.o: ../include/maps.h +cleanup_out_recipient.o: ../include/tok822.h +cleanup_out_recipient.o: ../include/resolve_clnt.h +cleanup_out_recipient.o: ../include/mail_stream.h +cleanup_rewrite.o: cleanup_rewrite.c +cleanup_rewrite.o: ../include/sys_defs.h +cleanup_rewrite.o: ../include/msg.h +cleanup_rewrite.o: ../include/vstring.h +cleanup_rewrite.o: ../include/vbuf.h +cleanup_rewrite.o: ../include/tok822.h +cleanup_rewrite.o: ../include/resolve_clnt.h +cleanup_rewrite.o: ../include/rewrite_clnt.h +cleanup_rewrite.o: ../include/quote_822_local.h +cleanup_rewrite.o: cleanup.h +cleanup_rewrite.o: ../include/vstream.h +cleanup_rewrite.o: ../include/argv.h +cleanup_rewrite.o: ../include/maps.h +cleanup_rewrite.o: ../include/been_here.h +cleanup_rewrite.o: ../include/mail_stream.h +cleanup_skip.o: cleanup_skip.c +cleanup_skip.o: ../include/sys_defs.h +cleanup_skip.o: ../include/msg.h +cleanup_skip.o: ../include/vstream.h +cleanup_skip.o: ../include/vbuf.h +cleanup_skip.o: ../include/record.h +cleanup_skip.o: ../include/vstring.h +cleanup_skip.o: ../include/rec_type.h +cleanup_skip.o: cleanup.h +cleanup_skip.o: ../include/argv.h +cleanup_skip.o: ../include/maps.h +cleanup_skip.o: ../include/tok822.h +cleanup_skip.o: ../include/resolve_clnt.h +cleanup_skip.o: ../include/been_here.h +cleanup_skip.o: ../include/mail_stream.h +cleanup_state.o: cleanup_state.c +cleanup_state.o: ../include/sys_defs.h +cleanup_state.o: ../include/mymalloc.h +cleanup_state.o: ../include/vstring.h +cleanup_state.o: ../include/vbuf.h +cleanup_state.o: ../include/vstream.h +cleanup_state.o: ../include/been_here.h +cleanup_state.o: ../include/mail_params.h +cleanup_state.o: cleanup.h +cleanup_state.o: ../include/argv.h +cleanup_state.o: ../include/maps.h +cleanup_state.o: ../include/tok822.h +cleanup_state.o: ../include/resolve_clnt.h +cleanup_state.o: ../include/mail_stream.h diff --git a/postfix/cleanup/cleanup.c b/postfix/cleanup/cleanup.c new file mode 100644 index 000000000..72c64d665 --- /dev/null +++ b/postfix/cleanup/cleanup.c @@ -0,0 +1,443 @@ +/*++ +/* NAME +/* cleanup 8 +/* SUMMARY +/* canonicalize and enqueue Postfix message +/* SYNOPSIS +/* \fBcleanup\fR [generic Postfix daemon options] +/* DESCRIPTION +/* The \fBcleanup\fR daemon processes inbound mail, inserts it +/* into the \fBincoming\fR mail queue, and informs the queue +/* manager of its arrival. +/* +/* The \fBcleanup\fR daemon always performs the following transformations: +/* .IP \(bu +/* Insert missing message headers: (\fBResent-\fR) \fBFrom:\fR, +/* \fBMessage-Id:\fR, and \fBDate:\fR. +/* .IP \(bu +/* Extract envelope recipient addresses from (\fBResent-\fR) \fBTo:\fR, +/* \fBCc:\fR and \fBBcc:\fR message headers when no recipients are +/* specified in the message envelope. +/* .IP \(bu +/* Transform envelope and header addresses to the standard +/* \fIuser@fully-qualified-domain\fR form that is expected by other +/* Postfix programs. +/* This task is delegated to the \fBtrivial-rewrite\fR(8) daemon. +/* .IP \(bu +/* Eliminate duplicate envelope recipient addresses. +/* .PP +/* The following address transformations are optional: +/* .IP \(bu +/* Optionally, rewrite all envelope and header addresses according +/* to the mappings specified in the \fBcanonical\fR(5) lookup tables. +/* .IP \(bu +/* Optionally, masquerade envelope sender addresses and message +/* header addresses (i.e. strip host or domain information below +/* all domains listed in the \fBmasquerade_domains\fR parameter, +/* except for user names listed in \fBmasquerade_exceptions\fR). +/* Address masquerading does not affect envelope recipients. +/* .IP \(bu +/* Optionally, expand envelope recipients according to information +/* found in the \fBvirtual\fR(5) lookup tables. +/* .PP +/* The \fBcleanup\fR daemon performs sanity checks on the content of +/* each message. When it finds a problem, by default it returns a +/* diagnostic status to the client, and leaves it up to the client +/* to deal with the problem. Alternatively, the client can request +/* the \fBcleanup\fR daemon to bounce the message back to the sender +/* in case of trouble. +/* STANDARDS +/* RFC 822 (ARPA Internet Text Messages) +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8). +/* BUGS +/* Table-driven rewriting rules make it hard to express \fBif then +/* else\fR and other logical relationships. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. Use the \fBpostfix reload\fR command after +/* a configuration change. +/* .SH Miscellaneous +/* .ad +/* .fi +/* .IP \fBhopcount_limit\fR +/* Limit the number of \fBReceived:\fR message headers. +/* .SH "Address transformations" +/* .ad +/* .fi +/* .IP \fBempty_address_recipient\fR +/* The destination for undeliverable mail from <>. This +/* substitution is done before all other address rewriting. +/* .IP \fBcanonical_maps\fR +/* Address mapping lookup table for sender and recipient addresses +/* in envelopes and headers. +/* .IP \fBrecipient_canonical_maps\fR +/* Address mapping lookup table for envelope and header recipient +/* addresses. +/* .IP \fBsender_canonical_maps\fR +/* Address mapping lookup table for envelope and header sender +/* addresses. +/* .IP \fBmasquerade_domains\fR +/* List of domains that hide their subdomain structure. +/* .IP \fBmasquerade_exceptions\fR +/* List of user names that are not subject to address masquerading. +/* .IP \fBvirtual_maps\fR +/* Address mapping lookup table for envelope recipient addresses. +/* .SH "Resource controls" +/* .ad +/* .fi +/* .IP \fBduplicate_filter_limit\fR +/* Limit the number of envelope recipients that are remembered. +/* .IP \fBheader_size_limit\fR +/* Limit the amount of memory in bytes used to process a message header. +/* SEE ALSO +/* canonical(5) canonical address lookup table format +/* qmgr(8) queue manager daemon +/* syslogd(8) system logging +/* trivial-rewrite(8) address rewriting +/* virtual(5) virtual address lookup table format +/* FILES +/* /etc/postfix/canonical*, canonical mapping table +/* /etc/postfix/virtual*, virtual mapping table +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Single-threaded server skeleton. */ + +#include + +/* Application-specific. */ + +#include "cleanup.h" + + /* + * Global state: any queue files that we have open, so that the error + * handler can clean up in case of trouble. + */ +char *cleanup_path; /* queue file name */ + + /* + * Tunable parameters. + */ +int var_hopcount_limit; /* max mailer hop count */ +int var_header_limit; /* max header length */ +char *var_canonical_maps; /* common canonical maps */ +char *var_send_canon_maps; /* sender canonical maps */ +char *var_rcpt_canon_maps; /* recipient canonical maps */ +char *var_virtual_maps; /* virtual maps */ +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 */ + + /* + * Mappings. + */ +MAPS *cleanup_comm_canon_maps; +MAPS *cleanup_send_canon_maps; +MAPS *cleanup_rcpt_canon_maps; +MAPS *cleanup_virtual_maps; +ARGV *cleanup_masq_domains; + +/* cleanup_service - process one request to inject a message into the queue */ + +static void cleanup_service(VSTREAM *src, char *unused_service, char **argv) +{ + char *junk; + static char *log_queues[] = { + MAIL_QUEUE_DEFER, + MAIL_QUEUE_BOUNCE, + 0, + }; + char **cpp; + + /* + * Sanity check. This service takes no command-line arguments. + */ + if (argv[0]) + msg_fatal("unexpected command-line argument: %s", argv[0]); + + /* + * Initialize. + */ + cleanup_state_alloc(); + cleanup_src = src; + + /* + * Open the queue file. Send the queue ID to the client so they can use + * it for logging purposes. For example, the SMTP server sends the queue + * id to the SMTP client after completion of the DATA command; and when + * the local delivery agent forwards a message, it logs the new queue id + * together with the old one. All this is done to make it easier for mail + * admins to follow a message while it hops from machine to machine. + * + * Save the queue file name, so that the runtime error handler can clean up + * in case of problems. + */ + cleanup_handle = mail_stream_file(MAIL_QUEUE_INCOMING, + MAIL_CLASS_PUBLIC, MAIL_SERVICE_QUEUE); + cleanup_dst = cleanup_handle->stream; + cleanup_path = mystrdup(VSTREAM_PATH(cleanup_dst)); + cleanup_queue_id = mystrdup(cleanup_handle->id); + if (msg_verbose) + msg_info("cleanup_service: open %s", cleanup_path); + + /* + * If there is a time to get rid of spurious bounce/defer log files, this + * is it. The down side is that this costs performance for every message, + * while the probability of spurious bounce/defer log files is quite low. + * Perhaps we should put the queue file ID inside the defer and bounce + * files, so that the bounce and defer daemons can figure out if a file + * is a left-over from a previous message instance. For now, we play safe + * and check each time a new queue file is created. + */ + for (cpp = log_queues; *cpp; cpp++) { + if (mail_queue_remove(*cpp, cleanup_queue_id) == 0) + msg_warn("%s: removed spurious %s log", *cpp, cleanup_queue_id); + else if (errno != ENOENT) + msg_fatal("%s: remove %s log: %m", *cpp, cleanup_queue_id); + } + + /* + * Send the queue id to the client. Read client processing options. If we + * can't read the client processing options we can pretty much forget + * about the whole operation. + */ + mail_print(cleanup_src, "%s", cleanup_queue_id); + if (mail_scan(src, "%d", &cleanup_flags) != 1) { + cleanup_errs |= CLEANUP_STAT_BAD; + cleanup_flags = 0; + } + + /* + * If the client requests us to do the bouncing in case of problems, + * throw away the input only in case of real show-stopper errors, such as + * unrecognizable data (which should never happen) or insufficient space + * for the queue file (which will happen occasionally). Otherwise, throw + * away the input after any error. See the CLEANUP_OUT_OK() definition. + */ + if (cleanup_flags & CLEANUP_FLAG_BOUNCE) { + cleanup_err_mask = + (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE | CLEANUP_STAT_SIZE); + } else { + cleanup_err_mask = ~0; + } + + /* + * First, copy the envelope records to the queue file. Then, copy the + * message content (headers and body). Finally, attach any information + * extracted from message headers. + */ + cleanup_envelope(); + if (CLEANUP_OUT_OK()) + cleanup_message(); + if (CLEANUP_OUT_OK()) + cleanup_extracted(); + + /* + * Now that we have captured the entire message, see if there are any + * other errors. For example, if the message needs to be bounced for lack + * of recipients. We want to turn on the execute bits on a file only when + * we want the queue manager to process it. + */ + if (cleanup_recip == 0) + cleanup_errs |= CLEANUP_STAT_RCPT; + + /* + * If there are no errors, be very picky about queue file write errors + * because we are about to tell the sender that it can throw away its + * copy of the message. + */ + if (cleanup_errs == 0) + cleanup_errs |= mail_stream_finish(cleanup_handle); + else + mail_stream_cleanup(cleanup_handle); + cleanup_handle = 0; + cleanup_dst = 0; + + /* + * If there was an error, remove the queue file, after optionally + * bouncing it. An incomplete message should never be bounced: it was + * canceled by the client, and may not even have an address to bounce to. + * That last test is redundant but we keep it just for robustness. + * + * If we are responsible for bouncing a message, we must must report success + * to the client unless the bounce message file could not be written + * (which is just as bad as not being able to write the message queue + * file in the first place). + * + * Do not log the arrival of a message that will be bounced by the client. + */ +#define CAN_BOUNCE() \ + ((cleanup_errs & (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE)) == 0 \ + && cleanup_sender != 0 \ + && (cleanup_flags & CLEANUP_FLAG_BOUNCE) != 0) + + if (cleanup_errs) { + if (CAN_BOUNCE()) { + if (bounce_append(BOUNCE_FLAG_CLEAN, + cleanup_queue_id, cleanup_recip ? + cleanup_recip : "", "cleanup", cleanup_time, + "%s", cleanup_strerror(cleanup_errs)) == 0 + && bounce_flush(BOUNCE_FLAG_CLEAN, + MAIL_QUEUE_INCOMING, + cleanup_queue_id, cleanup_sender) == 0) { + cleanup_errs = 0; + } else { + msg_warn("%s: bounce message failure", cleanup_queue_id); + cleanup_errs = CLEANUP_STAT_WRITE; + } + } + if (REMOVE(cleanup_path)) + msg_warn("remove %s: %m", cleanup_path); + } + + /* + * Report the completion status back to the client. Order of operations + * matters very much: make sure that our queue file will not be deleted + * by the error handler AFTER we have taken responsibility for delivery. + * Better to deliver twice than to lose mail. + */ + junk = cleanup_path; + cleanup_path = 0; /* don't delete upon error */ + mail_print(cleanup_src, "%d", cleanup_errs);/* we're committed now */ + if (msg_verbose) + msg_info("cleanup_service: status %d", cleanup_errs); + myfree(junk); + + /* + * Cleanup internal state. This is simply complementary to the + * initializations at the beginning of this routine. + */ + cleanup_state_free(); +} + +/* cleanup_all - callback for the runtime error handler */ + +static void cleanup_all(void) +{ + if (cleanup_path && REMOVE(cleanup_path)) + msg_warn("cleanup_all: remove %s: %m", cleanup_path); +} + +/* cleanup_sig - cleanup after signal */ + +static void cleanup_sig(int sig) +{ + cleanup_all(); + exit(sig); +} + +/* pre_jail_init - initialize before entering the chroot jail */ + +static void pre_jail_init(void) +{ + if (*var_canonical_maps) + cleanup_comm_canon_maps = + maps_create(VAR_CANONICAL_MAPS, var_canonical_maps); + if (*var_send_canon_maps) + cleanup_send_canon_maps = + maps_create(VAR_SEND_CANON_MAPS, var_send_canon_maps); + if (*var_rcpt_canon_maps) + cleanup_rcpt_canon_maps = + maps_create(VAR_RCPT_CANON_MAPS, var_rcpt_canon_maps); + if (*var_virtual_maps) + cleanup_virtual_maps = maps_create(VAR_VIRTUAL_MAPS, var_virtual_maps); + if (*var_masq_domains) + cleanup_masq_domains = argv_split(var_masq_domains, " ,\t\r\n"); +} + +/* post_jail_init - initialize after entering the chroot jail */ + +static void post_jail_init(void) +{ + + /* + * Optionally set the file size resource limit. XXX This limits the + * message content to somewhat less than requested, because the total + * queue file size also includes envelope information. Unless people set + * really low limit, the difference is going to matter only when a queue + * file has lots of recipients. + */ + if (var_message_limit > 0) + set_file_limit((off_t) var_message_limit); +} + +/* main - the main program */ + +int main(int argc, char **argv) +{ + static CONFIG_INT_TABLE int_table[] = { + 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, + 0, + }; + static CONFIG_STR_TABLE str_table[] = { + VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps, 0, 0, + VAR_SEND_CANON_MAPS, DEF_SEND_CANON_MAPS, &var_send_canon_maps, 0, 0, + VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps, 0, 0, + VAR_VIRTUAL_MAPS, DEF_VIRTUAL_MAPS, &var_virtual_maps, 0, 0, + VAR_MASQ_DOMAINS, DEF_MASQ_DOMAINS, &var_masq_domains, 0, 0, + VAR_EMPTY_ADDR, DEF_EMPTY_ADDR, &var_empty_addr, 1, 0, + VAR_MASQ_EXCEPTIONS, DEF_MASQ_EXCEPTIONS, &var_masq_exceptions, 0, 0, + 0, + }; + + /* + * Clean up an incomplete queue file in case of a fatal run-time error, + * or after receiving SIGTERM from the master at shutdown time. + */ + signal(SIGTERM, cleanup_sig); + msg_cleanup(cleanup_all); + + /* + * Pass control to the single-threaded service skeleton. + */ + single_server_main(argc, argv, cleanup_service, + MAIL_SERVER_INT_TABLE, int_table, + MAIL_SERVER_STR_TABLE, str_table, + MAIL_SERVER_PRE_INIT, pre_jail_init, + MAIL_SERVER_POST_INIT, post_jail_init, + 0); +} diff --git a/postfix/cleanup/cleanup.h b/postfix/cleanup/cleanup.h new file mode 100644 index 000000000..56e42c28f --- /dev/null +++ b/postfix/cleanup/cleanup.h @@ -0,0 +1,160 @@ +/*++ +/* NAME +/* cleanup 3h +/* SUMMARY +/* canonicalize and enqueue message +/* SYNOPSIS +/* #include "cleanup.h" +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include +#include + + /* + * Global library. + */ +#include +#include +#include +#include + + /* + * These state variables are accessed by many functions, and there is only + * one instance of each. Rather than passing around lots and lots of + * parameters, or passing them around as part of a huge structure, we just + * make the variables global, because that is what they are. + */ +extern VSTRING *cleanup_inbuf; /* read buffer */ +extern VSTRING *cleanup_temp1; /* scratch buffer, local use only */ +extern VSTRING *cleanup_temp2; /* scratch buffer, local use only */ +extern VSTREAM *cleanup_src; /* current input stream */ +extern VSTREAM *cleanup_dst; /* current output stream */ +extern MAIL_STREAM *cleanup_handle; /* mail stream handle */ +extern char *cleanup_queue_id; /* queue file basename */ +extern time_t cleanup_time; /* posting time */ +extern char *cleanup_fullname; /* envelope sender full name */ +extern char *cleanup_sender; /* envelope sender address */ +extern char *cleanup_from; /* From: address */ +extern char *cleanup_resent_from; /* Resent-From: address */ +extern char *cleanup_recip; /* envelope recipient address */ +extern char *cleanup_return_receipt; /* return-receipt address */ +extern char *cleanup_errors_to; /* errors-to address */ +extern int cleanup_flags; /* processing options */ +extern int cleanup_errs; /* any badness experienced */ +extern int cleanup_err_mask; /* allowed badness */ +extern VSTRING *cleanup_header_buf; /* multi-record header */ +extern int cleanup_headers_seen; /* which headers were seen */ +extern int cleanup_hop_count; /* count of received: headers */ +extern ARGV *cleanup_recipients; /* recipients from regular headers */ +extern ARGV *cleanup_resent_recip; /* recipients from resent headers */ +extern char *cleanup_resent; /* any resent- header seen */ +extern BH_TABLE *cleanup_dups; /* recipient dup filter */ + + /* + * Mappings. + */ +extern MAPS *cleanup_comm_canon_maps; +extern MAPS *cleanup_send_canon_maps; +extern MAPS *cleanup_rcpt_canon_maps; +extern MAPS *cleanup_virtual_maps; +extern ARGV *cleanup_masq_domains; + + /* + * Saved queue file name, so the file can be removed in case of a fatal + * run-time error. + */ +extern char *cleanup_path; + + /* + * Tunable parameters. + */ +extern int var_bounce_limit; /* max bounce message size */ +extern int var_message_limit; /* max message size */ +extern int var_hopcount_limit; /* max mailer hop count */ +extern int var_header_limit; /* max header length */ + + /* + * cleanup_state.c + */ +extern void cleanup_state_alloc(void); +extern void cleanup_state_free(void); + + /* + * cleanup_out.c + */ +extern void cleanup_out(int, char *, int); +extern void cleanup_out_string(int, char *); +extern void cleanup_out_format(int, char *,...); + +#define CLEANUP_OUT_BUF(t, b) \ + cleanup_out((t), vstring_str((b)), VSTRING_LEN((b))) + +#define CLEANUP_OUT_OK() \ + ((cleanup_errs & cleanup_err_mask) == 0) + + /* + * cleanup_envelope.c + */ +extern void cleanup_envelope(void); + + /* + * cleanup_message.c + */ +extern void cleanup_message(void); + + /* + * cleanup_extracted.c + */ +extern void cleanup_extracted(void); + + /* + * cleanup_skip.o + */ +extern void cleanup_skip(void); + + /* + * cleanup_rewrite.c + */ +extern void cleanup_rewrite_external(VSTRING *, const char *); +extern void cleanup_rewrite_internal(VSTRING *, const char *); +extern void cleanup_rewrite_tree(TOK822 *); + + /* + * cleanup_map11.c + */ +extern void cleanup_map11_external(VSTRING *, MAPS *); +extern void cleanup_map11_internal(VSTRING *, MAPS *); +extern void cleanup_map11_tree(TOK822 *, MAPS *); + + /* + * cleanup_map1n.c + */ +ARGV *cleanup_map1n_internal(char *, MAPS *); + + /* + * cleanup_masquerade.c + */ +extern void cleanup_masquerade_external(VSTRING *, ARGV *); +extern void cleanup_masquerade_internal(VSTRING *, ARGV *); +extern void cleanup_masquerade_tree(TOK822 *, ARGV *); + + /* + * Cleanup_recipient.c + */ +extern void cleanup_out_recipient(char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ diff --git a/postfix/cleanup/cleanup_envelope.c b/postfix/cleanup/cleanup_envelope.c new file mode 100644 index 000000000..5824139f5 --- /dev/null +++ b/postfix/cleanup/cleanup_envelope.c @@ -0,0 +1,132 @@ +/*++ +/* NAME +/* cleanup_envelope 3 +/* SUMMARY +/* process envelope segment +/* SYNOPSIS +/* #include +/* +/* void cleanup_envelope() +/* DESCRIPTION +/* This module processes the envelope segment of a mail message. +/* While copying records from input to output it validates the +/* message structure, rewrites sender/recipient addresses +/* to canonical form, and expands recipients according to +/* entries in the virtual table. +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "cleanup.h" + +#define STR vstring_str + +/* cleanup_envelope - process envelope segment */ + +void cleanup_envelope(void) +{ + VSTRING *clean_addr = vstring_alloc(100); + int type = 0; + + /* + * The message content size record goes first, so it can easily be + * updated in place. This information takes precedence over any size + * estimate provided by the client. Size goes first so that it it easy to + * produce queue file reports. + */ + cleanup_out_format(REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT, 0L); + + /* + * XXX Rely on the front-end programs to enforce record size limits. + */ + while (CLEANUP_OUT_OK()) { + if ((type = rec_get(cleanup_src, cleanup_inbuf, 0)) < 0) { + cleanup_errs |= CLEANUP_STAT_BAD; + break; + } + if (type == REC_TYPE_MESG) { + if (cleanup_sender == 0 || cleanup_time == 0) { + msg_warn("missing sender or time envelope record"); + cleanup_errs |= CLEANUP_STAT_BAD; + } + break; + } + if (strchr(REC_TYPE_ENVELOPE, type) == 0) { + msg_warn("unexpected record type %d in envelope", type); + cleanup_errs |= CLEANUP_STAT_BAD; + break; + } + if (msg_verbose) + msg_info("envelope %c %s", type, STR(cleanup_inbuf)); + + if (type == REC_TYPE_TIME) { + cleanup_time = atol(STR(cleanup_inbuf)); + CLEANUP_OUT_BUF(type, cleanup_inbuf); + } else if (type == REC_TYPE_FULL) { + cleanup_fullname = mystrdup(STR(cleanup_inbuf)); + } else if (type == REC_TYPE_FROM) { + cleanup_rewrite_internal(clean_addr, STR(cleanup_inbuf)); + if (cleanup_send_canon_maps) + cleanup_map11_internal(clean_addr, cleanup_send_canon_maps); + if (cleanup_comm_canon_maps) + cleanup_map11_internal(clean_addr, cleanup_comm_canon_maps); + if (cleanup_masq_domains) + cleanup_masquerade_internal(clean_addr, cleanup_masq_domains); + CLEANUP_OUT_BUF(type, clean_addr); + if (cleanup_sender == 0) + cleanup_sender = mystrdup(STR(clean_addr)); + } else if (type == REC_TYPE_RCPT) { + cleanup_rewrite_internal(clean_addr, *STR(cleanup_inbuf) ? + STR(cleanup_inbuf) : var_empty_addr); + if (cleanup_rcpt_canon_maps) + cleanup_map11_internal(clean_addr, cleanup_rcpt_canon_maps); + if (cleanup_comm_canon_maps) + cleanup_map11_internal(clean_addr, cleanup_comm_canon_maps); + cleanup_out_recipient(STR(clean_addr)); + if (cleanup_recip == 0) + cleanup_recip = mystrdup(STR(clean_addr)); + } else { + CLEANUP_OUT_BUF(type, cleanup_inbuf); + } + } + + /* + * XXX Keep reading in case of trouble, so that the sender is ready to + * receive our status report. + */ + if (!CLEANUP_OUT_OK()) + if (type >= 0) + cleanup_skip(); + + vstring_free(clean_addr); +} diff --git a/postfix/cleanup/cleanup_extracted.c b/postfix/cleanup/cleanup_extracted.c new file mode 100644 index 000000000..783e93b59 --- /dev/null +++ b/postfix/cleanup/cleanup_extracted.c @@ -0,0 +1,116 @@ +/*++ +/* NAME +/* cleanup_extracted 3 +/* SUMMARY +/* process extracted segment +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* void cleanup_extracted(void) +/* DESCRIPTION +/* This module processes message segments for information +/* extracted from message content. It requires that the input +/* contains no extracted information, and writes extracted +/* information records to the output. +/* 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 +#include +#include + +/* Application-specific. */ + +#include "cleanup.h" + +/* cleanup_extracted - generate segment with header-extracted information */ + +void cleanup_extracted(void) +{ + ARGV *rcpt; + char **cpp; + int type; + + /* + * Do not complain in case of premature EOF - most likely the client + * aborted the operation. + * + * XXX Rely on the front-end programs to enforce record size limits. + */ + if ((type = rec_get(cleanup_src, cleanup_inbuf, 0)) < 0) { + cleanup_errs |= CLEANUP_STAT_BAD; + return; + } + + /* + * Require that the client sends an empty record group. It is OUR job to + * extract information from message headers. Clients can specify options + * via special message header lines. + * + * XXX Keep reading in case of trouble, so that the sender is ready for our + * status report. + */ + if (type != REC_TYPE_END) { + msg_warn("unexpected record type %d in extracted segment", type); + cleanup_errs |= CLEANUP_STAT_BAD; + if (type >= 0) + cleanup_skip(); + return; + } + + /* + * Start the extracted segment. + */ + cleanup_out_string(REC_TYPE_XTRA, ""); + + /* + * Always emit Return-Receipt-To and Errors-To records, and always emit + * them ahead of extracted recipients, so that the queue manager does not + * waste lots of time searching through large numbers of recipient + * addresses. + */ + cleanup_out_string(REC_TYPE_RRTO, cleanup_return_receipt ? + cleanup_return_receipt : ""); + + cleanup_out_string(REC_TYPE_ERTO, cleanup_errors_to ? + cleanup_errors_to : cleanup_sender); + + /* + * Optionally account for missing recipient envelope records. + */ + if (cleanup_recip == 0) { + rcpt = (cleanup_resent[0] ? cleanup_resent_recip : cleanup_recipients); + argv_terminate(rcpt); + for (cpp = rcpt->argv; CLEANUP_OUT_OK() && *cpp; cpp++) + cleanup_out_recipient(*cpp); + if (rcpt->argv[0]) + cleanup_recip = mystrdup(rcpt->argv[0]); + } + + /* + * Terminate the extracted segment. + */ + cleanup_out_string(REC_TYPE_END, ""); +} diff --git a/postfix/cleanup/cleanup_map11.c b/postfix/cleanup/cleanup_map11.c new file mode 100644 index 000000000..cac495f9a --- /dev/null +++ b/postfix/cleanup/cleanup_map11.c @@ -0,0 +1,158 @@ +/*++ +/* NAME +/* cleanup_map11 3 +/* SUMMARY +/* one-to-one mapping +/* SYNOPSIS +/* #include +/* +/* void cleanup_map11_external(addr, maps) +/* VSTRING *addr; +/* MAPS *maps; +/* +/* void cleanup_map11_internal(addr, maps) +/* VSTRING *addr; +/* MAPS *maps; +/* +/* void cleanup_map11_tree(tree, maps) +/* TOK822 *tree; +/* MAPS *maps; +/* DESCRIPTION +/* This module performs one-to-one map lookups. +/* +/* If an address has a mapping, the lookup result is +/* subjected to another iteration of rewriting and mapping. +/* Recursion continues until an address maps onto itself, +/* or until an unreasonable recursion level is reached. +/* +/* cleanup_map11_external() looks up the external (quoted) string +/* form of an address in the maps specified via the \fImaps\fR argument. +/* +/* cleanup_map11_internal() is a wrapper around the +/* cleanup_map11_external() routine that transforms from +/* internal (quoted) string form to external form and back. +/* +/* cleanup_map11_tree() is a wrapper around the +/* cleanup_map11_external() routine that transforms from +/* internal parse tree form to external form and back. +/* DIAGNOSTICS +/* Recoverable errors: the global \fIcleanup_errs\fR flag is updated. +/* SEE ALSO +/* mail_addr_find(3) address lookups +/* mail_addr_map(3) address mappings +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include + +/* Application-specific. */ + +#include "cleanup.h" + +#define STR vstring_str +#define MAX_RECURSION 10 + +/* cleanup_map11_external - one-to-one table lookups */ + +void cleanup_map11_external(VSTRING *addr, MAPS *maps) +{ + int count; + int expand_to_self; + ARGV *new_addr; + char *saved_addr; + + /* + * Produce sensible output even in the face of a recoverable error. This + * simplifies error recovery considerably because we can do delayed error + * checking in one place, instead of having error handling code all over + * the place. + */ + for (count = 0; count < MAX_RECURSION; count++) { + if ((new_addr = mail_addr_map(maps, STR(addr))) != 0) { + if (new_addr->argc > 1) + msg_warn("multi-valued %s entry for %s", + maps->title, STR(addr)); + saved_addr = mystrdup(STR(addr)); + vstring_strcpy(addr, new_addr->argv[0]); + expand_to_self = !strcasecmp(saved_addr, STR(addr)); + myfree(saved_addr); + argv_free(new_addr); + if (expand_to_self) + return; + } else if (dict_errno != 0) { + msg_warn("%s: %s map lookup problem for %s", + cleanup_queue_id, maps->title, STR(addr)); + cleanup_errs |= CLEANUP_STAT_WRITE; + return; + } else { + return; + } + } + msg_warn("%s: unreasonable %s map nesting for %s", + cleanup_queue_id, maps->title, STR(addr)); +} + +/* cleanup_map11_tree - rewrite address node */ + +void cleanup_map11_tree(TOK822 *tree, MAPS *maps) +{ + VSTRING *temp = vstring_alloc(100); + + /* + * Produce sensible output even in the face of a recoverable error. This + * simplifies error recovery considerably because we can do delayed error + * checking in one place, instead of having error handling code all over + * the place. + */ + tok822_externalize(temp, tree->head, TOK822_STR_DEFL); + cleanup_map11_external(temp, maps); + tok822_free_tree(tree->head); + tree->head = tok822_scan(STR(temp), &tree->tail); + vstring_free(temp); +} + +/* cleanup_map11_internal - rewrite address internal form */ + +void cleanup_map11_internal(VSTRING *addr, MAPS *maps) +{ + VSTRING *temp = vstring_alloc(100); + + /* + * Produce sensible output even in the face of a recoverable error. This + * simplifies error recovery considerably because we can do delayed error + * checking in one place, instead of having error handling code all over + * the place. + */ + quote_822_local(temp, STR(addr)); + cleanup_map11_external(temp, maps); + unquote_822_local(addr, STR(temp)); + vstring_free(temp); +} diff --git a/postfix/cleanup/cleanup_map1n.c b/postfix/cleanup/cleanup_map1n.c new file mode 100644 index 000000000..2addef510 --- /dev/null +++ b/postfix/cleanup/cleanup_map1n.c @@ -0,0 +1,133 @@ +/*++ +/* NAME +/* cleanup_map1n 3 +/* SUMMARY +/* one-to-many address mapping +/* SYNOPSIS +/* #include +/* +/* ARGV *cleanup_map1n_internal(addr) +/* char *addr; +/* DESCRIPTION +/* This module implements one-to-many table mapping via table lookup. +/* The process is recursive. The recursion terminates when the +/* left-hand side appears in its own expansion, or when a maximal +/* nesting level is reached. +/* +/* cleanup_map1n_internal() is the interface for addresses in +/* internal (unquoted) form. +/* DIAGNOSTICS +/* Recoverable errors: the global \fIcleanup_errs\fR flag is updated. +/* SEE ALSO +/* mail_addr_map(3) address mappings +/* mail_addr_find(3) address lookups +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include + +/* Application-specific. */ + +#include "cleanup.h" + +/* cleanup_map1n_internal - one-to-many table lookups */ + +ARGV *cleanup_map1n_internal(char *addr, MAPS *maps) +{ + ARGV *argv; + ARGV *lookup; + int count; + int i; + int arg; + int expand_to_self; + char *saved_lhs; + + /* + * Initialize. + */ + argv = argv_alloc(1); + argv_add(argv, addr, ARGV_END); + argv_terminate(argv); + + /* + * Rewrite the address vector in place. With each map lookup result, + * split it into separate addresses, then rewrite and flatten each + * address, and repeat the process. Beware: argv is being changed, so we + * must index the array explicitly, instead of running along it with a + * pointer. + */ +#define UPDATE(ptr,new) { myfree(ptr); ptr = mystrdup(new); } +#define MAX_RECURSION 1000 +#define MAX_EXPANSION 1000 +#define STR vstring_str + + for (expand_to_self = 0, arg = 0; arg < argv->argc; arg++) { + if (argv->argc > MAX_EXPANSION) { + msg_warn("%s: unreasonable %s map expansion size for %s", + cleanup_queue_id, maps->title, addr); + break; + } + for (count = 0; /* void */ ; count++) { + if (count >= MAX_RECURSION) { + msg_warn("%s: unreasonable %s map nesting for %s", + cleanup_queue_id, maps->title, addr); + break; + } + if ((lookup = mail_addr_map(maps, argv->argv[arg])) != 0) { + saved_lhs = mystrdup(argv->argv[arg]); + for (i = 0; i < lookup->argc; i++) { + unquote_822_local(cleanup_temp1, lookup->argv[i]); + if (strcasecmp(saved_lhs, STR(cleanup_temp1)) == 0) + expand_to_self = 1; + if (i == 0) { + UPDATE(argv->argv[arg], STR(cleanup_temp1)); + } else { + argv_add(argv, STR(cleanup_temp1), ARGV_END); + argv_terminate(argv); + } + } + myfree(saved_lhs); + argv_free(lookup); + if (expand_to_self) + return (argv); + } else if (dict_errno != 0) { + msg_warn("%s: %s map lookup problem for %s", + cleanup_queue_id, maps->title, addr); + cleanup_errs |= CLEANUP_STAT_WRITE; + return (argv); + } else { + break; + } + } + } + return (argv); +} diff --git a/postfix/cleanup/cleanup_masquerade.c b/postfix/cleanup/cleanup_masquerade.c new file mode 100644 index 000000000..d00ecb795 --- /dev/null +++ b/postfix/cleanup/cleanup_masquerade.c @@ -0,0 +1,173 @@ +/*++ +/* NAME +/* cleanup_masquerade 3 +/* SUMMARY +/* address masquerading +/* SYNOPSIS +/* #include +/* +/* void cleanup_masquerade_external(addr, masq_domains) +/* VSTRING *addr; +/* ARGV *masq_domains; +/* +/* void cleanup_masquerade_internal(addr, masq_domains) +/* VSTRING *addr; +/* ARGV *masq_domains; +/* +/* void cleanup_masquerade_tree(tree, masq_domains) +/* TOK822 *tree; +/* ARGV *masq_domains; +/* DESCRIPTION +/* This module masquerades addresses, that is, it strips subdomains +/* below domain names that are listed in the masquerade_domains +/* configuration parameter, except for user names listed in the +/* masquerade_exceptions configuration parameter. +/* +/* cleanup_masquerade_external() rewrites the external (quoted) string +/* form of an address. +/* +/* cleanup_masquerade_internal() is a wrapper around the +/* cleanup_masquerade_external() routine that transforms from +/* internal (quoted) string form to external form and back. +/* +/* cleanup_masquerade_tree() is a wrapper around the +/* cleanup_masquerade_external() routine that transforms from +/* internal parse tree form to external form and back. +/* DIAGNOSTICS +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include + +/* Application-specific. */ + +#include "cleanup.h" + +#define STR vstring_str + +/* cleanup_masquerade_external - masquerade address external form */ + +void cleanup_masquerade_external(VSTRING *addr, ARGV *masq_domains) +{ + char *domain; + int domain_len; + char **masqp; + int masq_len; + char *parent; + + /* Stuff for excluded names. */ + static HTABLE *masq_except_table = 0; + char *saved_names; + char *name; + char *ptr; + int excluded; + + /* + * First time, build a lookup table for excluded names. + */ + if (*var_masq_exceptions && masq_except_table == 0) { + masq_except_table = htable_create(5); + ptr = saved_names = mystrdup(var_masq_exceptions); + while ((name = mystrtok(&ptr, ", \t\r\n")) != 0) + htable_enter(masq_except_table, name, (char *) 0); + myfree(saved_names); + } + + /* + * Find the domain part. + */ + if ((domain = strrchr(STR(addr), '@')) == 0) + return; + domain += 1; + domain_len = strlen(domain); + + /* + * Don't masquerade excluded names (regardless of domain). + */ + if (masq_except_table) { + name = mystrndup(STR(addr), domain - 1 - STR(addr)); + excluded = (htable_locate(masq_except_table, name) != 0); + myfree(name); + if (excluded) + return; + } + + /* + * If any parent domain matches the list of masquerade domains, replace + * the domain in the address and terminate. If the domain matches a + * masquerade domain, leave it alone. Order of specification matters. + */ + for (masqp = masq_domains->argv; *masqp; masqp++) { + masq_len = strlen(*masqp); + if (masq_len == domain_len) { + if (strcasecmp(*masqp, domain) == 0) + break; + } else if (masq_len < domain_len) { + parent = domain + domain_len - masq_len; + if (parent[-1] == '.' && strcasecmp(*masqp, parent) == 0) { + if (msg_verbose) + msg_info("masquerade: %s -> %s", domain, *masqp); + vstring_truncate(addr, domain - STR(addr)); + vstring_strcat(addr, *masqp); + break; + } + } + } +} + +/* cleanup_masquerade_tree - masquerade address node */ + +void cleanup_masquerade_tree(TOK822 *tree, ARGV *masq_domains) +{ + VSTRING *temp = vstring_alloc(100); + + tok822_externalize(temp, tree->head, TOK822_STR_DEFL); + cleanup_masquerade_external(temp, masq_domains); + tok822_free_tree(tree->head); + tree->head = tok822_scan(STR(temp), &tree->tail); + + vstring_free(temp); +} + +/* cleanup_masquerade_internal - masquerade address internal form */ + +void cleanup_masquerade_internal(VSTRING *addr, ARGV *masq_domains) +{ + VSTRING *temp = vstring_alloc(100); + + quote_822_local(temp, STR(addr)); + cleanup_masquerade_external(temp, masq_domains); + unquote_822_local(addr, STR(temp)); + + vstring_free(temp); +} diff --git a/postfix/cleanup/cleanup_message.c b/postfix/cleanup/cleanup_message.c new file mode 100644 index 000000000..e357f52a9 --- /dev/null +++ b/postfix/cleanup/cleanup_message.c @@ -0,0 +1,491 @@ +/*++ +/* NAME +/* cleanup_message 3 +/* SUMMARY +/* process message segment +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* void cleanup_message(void) +/* DESCRIPTION +/* This module processes message content segments. +/* While copying records from input to output, it validates +/* the input, rewrites sender/recipient addresses to canonical +/* form, inserts missing message headers, and extracts information +/* from message headers to be used later when generating the extracted +/* output segment. +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "cleanup.h" + +/* cleanup_out_header - output one header as a bunch of records */ + +static void cleanup_out_header(void) +{ + char *start = vstring_str(cleanup_header_buf); + char *line; + char *next_line; + + /* + * Prepend a tab to continued header lines that went through the address + * rewriting machinery. See cleanup_fold_header() below for the form of + * such header lines. NB: This code destroys the header. We could try to + * avoid clobbering it, but we're not going to use the data any further. + */ + for (line = start; line; line = next_line) { + next_line = split_at(line, '\n'); + if (line == start || ISSPACE(*line)) { + cleanup_out_string(REC_TYPE_NORM, line); + } else { + cleanup_out_format(REC_TYPE_NORM, "\t%s", line); + } + } +} + +/* cleanup_fold_header - wrap address list header */ + +static void cleanup_fold_header(void) +{ + char *start_line = vstring_str(cleanup_header_buf); + char *end_line; + char *next_line; + char *line; + + /* + * A rewritten address list contains one address per line. The code below + * replaces newlines by spaces, to fit as many addresses on a line as + * possible (without rearranging the order of addresses). Prepending + * white space to the beginning of lines is delegated to the output + * routine. + */ + for (line = start_line; line != 0; line = next_line) { + end_line = line + strcspn(line, "\n"); + if (line > start_line) { + if (end_line - start_line < 70) { /* TAB counts as one */ + line[-1] = ' '; + } else { + start_line = line; + } + } + next_line = *end_line ? end_line + 1 : 0; + } + cleanup_out_header(); +} + +/* cleanup_extract_internal - save unquoted copy of extracted address */ + +static char *cleanup_extract_internal(VSTRING *buffer, TOK822 *addr) +{ + + /* + * A little routine to stash away a copy of an address that we extracted + * from a message header line. + */ + tok822_internalize(buffer, addr->head, TOK822_STR_DEFL); + return (mystrdup(vstring_str(buffer))); +} + +/* cleanup_rewrite_sender - sender address rewriting */ + +static void cleanup_rewrite_sender(HEADER_OPTS *hdr_opts) +{ + TOK822 *tree; + TOK822 **addr_list; + TOK822 **tpp; + + if (msg_verbose) + msg_info("rewrite_sender: %s", hdr_opts->name); + + /* + * Parse the header line, rewrite each address found, save copies of + * sender addresses, and regenerate the header line. Finally, pipe the + * result through the header line folding routine. + */ + tree = tok822_parse(vstring_str(cleanup_header_buf) + + strlen(hdr_opts->name) + 1); + addr_list = tok822_grep(tree, TOK822_ADDR); + for (tpp = addr_list; *tpp; tpp++) { + cleanup_rewrite_tree(*tpp); + if (cleanup_send_canon_maps) + cleanup_map11_tree(*tpp, cleanup_send_canon_maps); + if (cleanup_comm_canon_maps) + cleanup_map11_tree(*tpp, cleanup_comm_canon_maps); + if (cleanup_masq_domains) + cleanup_masquerade_tree(*tpp, cleanup_masq_domains); + if (hdr_opts->type == HDR_FROM && cleanup_from == 0) + cleanup_from = cleanup_extract_internal(cleanup_header_buf, *tpp); + if (hdr_opts->type == HDR_RESENT_FROM && cleanup_resent_from == 0) + cleanup_resent_from = + cleanup_extract_internal(cleanup_header_buf, *tpp); + } + vstring_sprintf(cleanup_header_buf, "%s: ", hdr_opts->name); + tok822_externalize(cleanup_header_buf, tree, TOK822_STR_HEAD); + myfree((char *) addr_list); + tok822_free_tree(tree); + if ((hdr_opts->flags & HDR_OPT_DROP) == 0) + cleanup_fold_header(); +} + +/* cleanup_rewrite_recip - recipient address rewriting */ + +static void cleanup_rewrite_recip(HEADER_OPTS *hdr_opts) +{ + TOK822 *tree; + TOK822 **addr_list; + TOK822 **tpp; + + if (msg_verbose) + msg_info("rewrite_recip: %s", hdr_opts->name); + + /* + * Parse the header line, rewrite each address found, save copies of + * recipient addresses, and regenerate the header line. Finally, pipe the + * result through the header line folding routine. + */ + tree = tok822_parse(vstring_str(cleanup_header_buf) + + strlen(hdr_opts->name) + 1); + addr_list = tok822_grep(tree, TOK822_ADDR); + for (tpp = addr_list; *tpp; tpp++) { + cleanup_rewrite_tree(*tpp); + if (cleanup_rcpt_canon_maps) + cleanup_map11_tree(*tpp, cleanup_rcpt_canon_maps); + if (cleanup_comm_canon_maps) + cleanup_map11_tree(*tpp, cleanup_comm_canon_maps); + if (cleanup_masq_domains) + cleanup_masquerade_tree(*tpp, cleanup_masq_domains); + if (hdr_opts->type == HDR_RETURN_RECEIPT_TO && !cleanup_return_receipt) + cleanup_return_receipt = + cleanup_extract_internal(cleanup_header_buf, *tpp); + if (hdr_opts->type == HDR_ERRORS_TO && !cleanup_errors_to) + cleanup_errors_to = + cleanup_extract_internal(cleanup_header_buf, *tpp); + tok822_internalize(cleanup_temp1, tpp[0]->head, TOK822_STR_DEFL); + if (cleanup_recip == 0 && (hdr_opts->flags & HDR_OPT_EXTRACT) != 0) + argv_add((hdr_opts->flags & HDR_OPT_RR) ? + cleanup_resent_recip : cleanup_recipients, + vstring_str(cleanup_temp1), (char *) 0); + } + vstring_sprintf(cleanup_header_buf, "%s: ", hdr_opts->name); + tok822_externalize(cleanup_header_buf, tree, TOK822_STR_HEAD); + myfree((char *) addr_list); + tok822_free_tree(tree); + if ((hdr_opts->flags & HDR_OPT_DROP) == 0) + cleanup_fold_header(); +} + +/* cleanup_header - process one complete header line */ + +static void cleanup_header(void) +{ + HEADER_OPTS *hdr_opts; + + + /* + * If this is an "unknown" header, just copy it to the output without + * even bothering to fold long lines. XXX Should split header lines that + * do not fit a REC_TYPE_NORM record. + */ + if ((hdr_opts = header_opts_find(vstring_str(cleanup_header_buf))) == 0) { + cleanup_out_header(); + } + + /* + * Known header. Remember that we have seen at least one. Find out what + * we should do with this header: delete, count, rewrite. Note that we + * should examine headers even when they will be deleted from the output, + * because the addresses in those headers might be needed elsewhere. + */ + else { + cleanup_headers_seen |= (1 << hdr_opts->type); + if (hdr_opts->type == HDR_MESSAGE_ID) + msg_info("%s: message-id=%s", cleanup_queue_id, + vstring_str(cleanup_header_buf) + strlen(hdr_opts->name) + 2); + if (hdr_opts->type == HDR_RESENT_MESSAGE_ID) + msg_info("%s: resent-message-id=%s", cleanup_queue_id, + vstring_str(cleanup_header_buf) + strlen(hdr_opts->name) + 2); + if (hdr_opts->type == HDR_RECEIVED) + if (++cleanup_hop_count >= var_hopcount_limit) + cleanup_errs |= CLEANUP_STAT_HOPS; + if (CLEANUP_OUT_OK()) { + if (hdr_opts->flags & HDR_OPT_RR) + cleanup_resent = "Resent-"; + if (hdr_opts->flags & HDR_OPT_SENDER) { + cleanup_rewrite_sender(hdr_opts); + } else if (hdr_opts->flags & HDR_OPT_RECIP) { + cleanup_rewrite_recip(hdr_opts); + } else if ((hdr_opts->flags & HDR_OPT_DROP) == 0) { + cleanup_out_header(); + } + } + } +} + +/* cleanup_missing_headers - insert missing message headers */ + +static void cleanup_missing_headers(void) +{ + char time_stamp[1024]; /* XXX locale dependent? */ + struct tm *tp; + 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. + */ + if ((cleanup_headers_seen & (1 << (cleanup_resent[0] ? + HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID))) == 0) { + tp = gmtime(&cleanup_time); + strftime(time_stamp, sizeof(time_stamp), "%Y%m%d%H%M%S", tp); + cleanup_out_format(REC_TYPE_NORM, "%sMessage-Id: <%s.%s@%s>", + cleanup_resent, time_stamp, cleanup_queue_id, var_myhostname); + msg_info("%s: %smessage-id=<%s.%s@%s>", + cleanup_queue_id, *cleanup_resent ? "resent-" : "", + time_stamp, cleanup_queue_id, var_myhostname); + } + + /* + * Add a missing (Resent-)Date: header. The date is in local time units, + * with the GMT offset at the end. + */ + if ((cleanup_headers_seen & (1 << (cleanup_resent[0] ? + HDR_RESENT_DATE : HDR_DATE))) == 0) { + cleanup_out_format(REC_TYPE_NORM, "%sDate: %s", + cleanup_resent, mail_date(cleanup_time)); + } + + /* + * Add a missing (Resent-)From: header. If a (Resent-)From: header is + * present, see if we need a (Resent-)Sender: header. + */ +#define NOT_SPECIAL_SENDER(addr) (*(addr) != 0 \ + && strcasecmp(addr, mail_addr_double_bounce()) != 0) + + if ((cleanup_headers_seen & (1 << (cleanup_resent[0] ? + HDR_RESENT_FROM : HDR_FROM))) == 0) { + quote_822_local(cleanup_temp1, cleanup_sender); + vstring_sprintf(cleanup_temp2, "%sFrom: %s", + cleanup_resent, vstring_str(cleanup_temp1)); + if (cleanup_fullname && *cleanup_fullname) { + vstring_strcat(cleanup_temp2, " ("); + token = tok822_alloc(TOK822_COMMENT, cleanup_fullname); + tok822_externalize(cleanup_temp2, token, TOK822_STR_NONE); + tok822_free(token); + vstring_strcat(cleanup_temp2, ")"); + } + CLEANUP_OUT_BUF(REC_TYPE_NORM, cleanup_temp2); + } else if ((cleanup_headers_seen & (1 << (cleanup_resent[0] ? + HDR_RESENT_SENDER : HDR_SENDER))) == 0 + && NOT_SPECIAL_SENDER(cleanup_sender)) { + from = (cleanup_resent[0] ? cleanup_resent_from : cleanup_from); + if (from == 0 || strcasecmp(cleanup_sender, from) != 0) { + quote_822_local(cleanup_temp1, cleanup_sender); + cleanup_out_format(REC_TYPE_NORM, "%sSender: %s", + cleanup_resent, vstring_str(cleanup_temp1)); + } + } +} + +/* cleanup_message - process message content segment */ + +void cleanup_message(void) +{ + char *myname = "cleanup_message"; + long mesg_offset; + long data_offset; + long xtra_offset; + int in_header; + char *start; + int type = 0; + + /* + * Write a dummy start-of-content segment marker. We'll update it with + * real file offset information after reaching the end of the message + * content. + */ + if ((mesg_offset = vstream_ftell(cleanup_dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); + cleanup_out_format(REC_TYPE_MESG, REC_TYPE_MESG_FORMAT, 0); + if ((data_offset = vstream_ftell(cleanup_dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); + + /* + * An unannounced end-of-input condition most likely means that the + * client did not want to send this message after all. Don't complain, + * just stop generating any further output. + * + * XXX Rely on the front-end programs to enforce record size limits. + */ + in_header = 1; + + while (CLEANUP_OUT_OK()) { + + if ((type = rec_get(cleanup_src, cleanup_inbuf, 0)) < 0) { + cleanup_errs |= CLEANUP_STAT_BAD; + break; + } + if (strchr(REC_TYPE_CONTENT, type) == 0) { + msg_warn("%s: unexpected record type %d", myname, type); + cleanup_errs |= CLEANUP_STAT_BAD; + break; + } + start = vstring_str(cleanup_inbuf); + + /* + * First, deal with header information that we have accumulated from + * previous input records. A whole record that starts with whitespace + * is a continuation of previous data. + * + * XXX Silently switch to body processing when some message header + * requires an unreasonable amount of storage, or when a message + * header record does not fit in a REC_TYPE_NORM type record. + */ + if (VSTRING_LEN(cleanup_header_buf) > 0) { + if (VSTRING_LEN(cleanup_header_buf) < var_header_limit + && type == REC_TYPE_NORM && ISSPACE(*start)) { + VSTRING_ADDCH(cleanup_header_buf, '\n'); + vstring_strcat(cleanup_header_buf, start); + continue; + } + + /* + * No more input to append to this saved header. Do output + * processing and reset the saved header buffer. + */ + VSTRING_TERMINATE(cleanup_header_buf); + cleanup_header(); + VSTRING_RESET(cleanup_header_buf); + } + + /* + * Switch to body processing if we didn't read a header or if the + * saved header requires an unreasonable amount of storage. Generate + * missing headers. Add one blank line when the message headers are + * immediately followed by a non-empty message body. + */ + if (in_header + && (VSTRING_LEN(cleanup_header_buf) >= var_header_limit + || type != REC_TYPE_NORM + || !is_header(start))) { + in_header = 0; + cleanup_missing_headers(); + if (type != REC_TYPE_XTRA && *start)/* output blank line */ + cleanup_out_string(REC_TYPE_NORM, ""); + } + + /* + * If this is a header record, save it until we know that the header + * is complete. If this is a body record, copy it to the output + * immediately. + */ + if (type == REC_TYPE_NORM || type == REC_TYPE_CONT) { + if (in_header) { + vstring_strcpy(cleanup_header_buf, start); + } else { + CLEANUP_OUT_BUF(type, cleanup_inbuf); + } + } + + /* + * If we have reached the end of the message content segment, update + * the start-of-content marker, now that we know how large the + * message content segment is, and update the content size indicator + * at the beginning of the message envelope segment. vstream_fseek() + * implicitly flushes the stream, which may fail for various reasons. + */ + else if (type == REC_TYPE_XTRA) { + if ((xtra_offset = vstream_ftell(cleanup_dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); + if (vstream_fseek(cleanup_dst, mesg_offset, SEEK_SET) < 0) { + msg_warn("%s: write queue file: %m", cleanup_queue_id); + if (errno == EFBIG) + cleanup_errs |= CLEANUP_STAT_SIZE; + else + cleanup_errs |= CLEANUP_STAT_WRITE; + break; + } + cleanup_out_format(REC_TYPE_MESG, REC_TYPE_MESG_FORMAT, xtra_offset); + if (vstream_fseek(cleanup_dst, 0L, SEEK_SET) < 0) + msg_fatal("%s: vstream_fseek %s: %m", myname, cleanup_path); + cleanup_out_format(REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT, + xtra_offset - data_offset); + if (vstream_fseek(cleanup_dst, xtra_offset, SEEK_SET) < 0) + msg_fatal("%s: vstream_fseek %s: %m", myname, cleanup_path); + break; + } + + /* + * This should never happen. + */ + else { + msg_panic("%s: unexpected record type: %d", myname, type); + } + } + + /* + * Keep reading in case of problems, so that the sender is ready to + * receive our status report. + */ + if (CLEANUP_OUT_OK() == 0) + if (type >= 0) + cleanup_skip(); +} diff --git a/postfix/cleanup/cleanup_out.c b/postfix/cleanup/cleanup_out.c new file mode 100644 index 000000000..dd51383e6 --- /dev/null +++ b/postfix/cleanup/cleanup_out.c @@ -0,0 +1,117 @@ +/*++ +/* NAME +/* cleanup_out 3 +/* SUMMARY +/* record output support +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* int CLEANUP_OUT_OK() +/* +/* void cleanup_out(type, data, len) +/* int type; +/* char *data; +/* int len; +/* +/* void cleanup_out_string(type, str) +/* int type; +/* char *str; +/* +/* void CLEANUP_OUT_BUF(type, buf) +/* int type; +/* VSTRING *buf; +/* +/* void cleanup_out_format(type, format, ...) +/* int type; +/* char *format; +/* DESCRIPTION +/* This module writes records to the output stream. +/* +/* CLEANUP_OUT_OK() is a macro that evaluates to non-zero +/* as long as it makes sense to produce output. All output +/* routines below check for this condition. +/* +/* cleanup_out() is the main record output routine. It writes +/* one record of the specified type, with the specified data +/* and length to the output stream. +/* +/* cleanup_out_string() outputs one string as a record. +/* +/* CLEANUP_OUT_BUF() is an unsafe macro that outputs +/* one string buffer as a record. +/* +/* cleanup_out_format() formats its arguments and writes +/* the result as a record. +/* 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 /* 44BSD stdarg.h uses abort() */ +#include +#include + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include +#include +#include + +/* Application-specific. */ + +#include "cleanup.h" + +/* cleanup_out - output one single record */ + +void cleanup_out(int type, char *string, int len) +{ + if (CLEANUP_OUT_OK()) { + if (rec_put(cleanup_dst, type, string, len) < 0) { + if (errno == EFBIG) { + msg_warn("queue file size limit exceeded"); + cleanup_errs |= CLEANUP_STAT_SIZE; + } else { + msg_warn("%s: write queue file: %m", cleanup_queue_id); + cleanup_errs |= CLEANUP_STAT_WRITE; + } + } + } +} + +/* cleanup_out_string - output string to one single record */ + +void cleanup_out_string(int type, char *string) +{ + cleanup_out(type, string, strlen(string)); +} + +/* cleanup_out_format - output one formatted record */ + +void cleanup_out_format(int type, char *fmt,...) +{ + static VSTRING *vp; + va_list ap; + + if (vp == 0) + vp = vstring_alloc(100); + va_start(ap, fmt); + vstring_vsprintf(vp, fmt, ap); + va_end(ap); + CLEANUP_OUT_BUF(type, vp); +} diff --git a/postfix/cleanup/cleanup_out_recipient.c b/postfix/cleanup/cleanup_out_recipient.c new file mode 100644 index 000000000..e1899a20b --- /dev/null +++ b/postfix/cleanup/cleanup_out_recipient.c @@ -0,0 +1,72 @@ +/*++ +/* NAME +/* cleanup_out_recipient 3 +/* SUMMARY +/* envelope recipient output filter +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* void cleanup_out_recipient(recipient) +/* char *recipient; +/* DESCRIPTION +/* This module implements an envelope recipient output filter. +/* +/* cleanup_out_recipient() performs virtual table expansion +/* and recipient duplicate filtering, and appends the +/* resulting recipients to the output stream. +/* CONFIGURATION +/* .ad +/* .fi +/* .IP local_duplicate_filter_limit +/* Upper bound to the size of the recipient duplicate filter. +/* Zero means no limit; this may cause the mail system to +/* become stuck. +/* .IP virtual_maps +/* list of virtual address lookup tables. +/* 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 + +/* Global library. */ + +#include +#include +#include + +/* Application-specific. */ + +#include "cleanup.h" + +/* cleanup_out_recipient - envelope recipient output filter */ + +void cleanup_out_recipient(char *recip) +{ + ARGV *argv; + char **cpp; + + if (cleanup_virtual_maps == 0) { + if (been_here_fixed(cleanup_dups, recip) == 0) + cleanup_out_string(REC_TYPE_RCPT, recip); + } else { + argv = cleanup_map1n_internal(recip, cleanup_virtual_maps); + for (cpp = argv->argv; *cpp; cpp++) + if (been_here_fixed(cleanup_dups, *cpp) == 0) + cleanup_out_string(REC_TYPE_RCPT, *cpp); + argv_free(argv); + } +} diff --git a/postfix/cleanup/cleanup_rewrite.c b/postfix/cleanup/cleanup_rewrite.c new file mode 100644 index 000000000..31cc6ef44 --- /dev/null +++ b/postfix/cleanup/cleanup_rewrite.c @@ -0,0 +1,99 @@ +/*++ +/* NAME +/* cleanup_rewrite 3 +/* SUMMARY +/* address canonicalization +/* SYNOPSIS +/* #include +/* +/* void cleanup_rewrite_external(result, addr) +/* VSTRING *result; +/* const char *addr; +/* +/* void cleanup_rewrite_internal(result, addr) +/* VSTRING *result; +/* const char *addr; +/* +/* void cleanup_rewrite_tree(tree) +/* TOK822 *tree; +/* DESCRIPTION +/* This module rewrites addresses to canonical form, adding missing +/* domains and stripping source routes etc., and performs +/* \fIcanonical\fR map lookups to map addresses to official form. +/* +/* cleanup_rewrite_init() performs one-time initialization. +/* +/* cleanup_rewrite_external() rewrites the external (quoted) string +/* form of an address. +/* +/* cleanup_rewrite_internal() is a wrapper around the +/* cleanup_rewrite_external() routine that transforms from +/* internal (quoted) string form to external form and back. +/* +/* cleanup_rewrite_tree() is a wrapper around the +/* cleanup_rewrite_external() routine that transforms from +/* internal parse tree form to external form and back. +/* DIAGNOSTICS +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include +#include +#include + +/* Application-specific. */ + +#include "cleanup.h" + +#define STR vstring_str + +/* cleanup_rewrite_external - rewrite address external form */ + +void cleanup_rewrite_external(VSTRING *result, const char *addr) +{ + rewrite_clnt(REWRITE_CANON, addr, result); +} + +/* cleanup_rewrite_tree - rewrite address node */ + +void cleanup_rewrite_tree(TOK822 *tree) +{ + VSTRING *temp = vstring_alloc(100); + + tok822_externalize(temp, tree->head, TOK822_STR_DEFL); + cleanup_rewrite_external(temp, STR(temp)); + tok822_free_tree(tree->head); + tree->head = tok822_scan(STR(temp), &tree->tail); + vstring_free(temp); +} + +/* 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); +} diff --git a/postfix/cleanup/cleanup_skip.c b/postfix/cleanup/cleanup_skip.c new file mode 100644 index 000000000..00f73aa4b --- /dev/null +++ b/postfix/cleanup/cleanup_skip.c @@ -0,0 +1,57 @@ +/*++ +/* NAME +/* cleanup_skip 3 +/* SUMMARY +/* skip further input +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* void cleanup_skip(void) +/* DESCRIPTION +/* cleanup_skip() skips client input. This function is used after +/* detecting an error. The idea is to drain input from the client +/* so the client is ready to read the cleanup service status report. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include +#include + +/* Application-specific. */ + +#include "cleanup.h" + +/* cleanup_skip - skip further client input */ + +void cleanup_skip(void) +{ + int type; + + msg_warn("skipping further client input"); + + /* + * XXX Rely on the front-end programs to enforce record size limits. + */ + while ((type = rec_get(cleanup_src, cleanup_inbuf, 0)) > 0 + && type != REC_TYPE_END) + /* void */ ; +} diff --git a/postfix/cleanup/cleanup_state.c b/postfix/cleanup/cleanup_state.c new file mode 100644 index 000000000..fe73981e1 --- /dev/null +++ b/postfix/cleanup/cleanup_state.c @@ -0,0 +1,138 @@ +/*++ +/* NAME +/* cleanup_state 3 +/* SUMMARY +/* per-message state variables +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* void cleanup_state_alloc(void) +/* +/* void cleanup_state_free(void) +/* DESCRIPTION +/* This module maintains about two dozen global (ugh) state variables +/* that are used by many routines in the course of processing one +/* message. Using globals seems to make more sense than passing around +/* large parameter lists, or passing around a structure with a large +/* number of components. We can get away with this because we need only +/* one instance at a time. +/* +/* cleanup_state_alloc() initializes the per-message state variables. +/* +/* cleanup_state_free() cleans up. +/* 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 + +/* Application-specific. */ + +#include "cleanup.h" + + /* + * These variables are accessed by many functions, and there is only one + * instance of each. Rather than passing around lots and lots of parameters, + * or passing them around in a structure, we just make the variables global, + * because that is what they are. + */ +VSTRING *cleanup_inbuf; /* read buffer */ +VSTRING *cleanup_temp1; /* scratch buffer, local use only */ +VSTRING *cleanup_temp2; /* scratch buffer, local use only */ +VSTREAM *cleanup_src; /* current input stream */ +VSTREAM *cleanup_dst; /* current output stream */ +MAIL_STREAM *cleanup_handle; /* mail stream handle */ +char *cleanup_queue_id; /* queue file basename */ +time_t cleanup_time; /* posting time */ +char *cleanup_fullname; /* envelope sender full name */ +char *cleanup_sender; /* envelope sender address */ +char *cleanup_from; /* From: address */ +char *cleanup_resent_from; /* Resent-From: address */ +char *cleanup_recip; /* envelope recipient address */ +char *cleanup_return_receipt; /* return-receipt address */ +char *cleanup_errors_to; /* errors-to address */ +int cleanup_flags; /* processing options */ +int cleanup_errs; /* any badness experienced */ +int cleanup_err_mask; /* allowed badness */ +VSTRING *cleanup_header_buf; /* multi-record header */ +int cleanup_headers_seen; /* which headers were seen */ +int cleanup_hop_count; /* count of received: headers */ +ARGV *cleanup_recipients; /* recipients from regular headers */ +ARGV *cleanup_resent_recip; /* recipients from resent headers */ +char *cleanup_resent; /* any resent- header seen */ +BH_TABLE *cleanup_dups; /* recipient dup filter */ + +/* cleanup_state_alloc - initialize global state */ + +void cleanup_state_alloc(void) +{ + cleanup_hop_count = 0; + cleanup_headers_seen = 0; + cleanup_time = 0; + cleanup_errs = 0; + cleanup_from = 0; + cleanup_fullname = 0; + cleanup_header_buf = vstring_alloc(100); + cleanup_queue_id = 0; + cleanup_recip = 0; + cleanup_return_receipt = 0; + cleanup_errors_to = 0; + cleanup_recipients = argv_alloc(2); + cleanup_resent = ""; + cleanup_resent_from = 0; + cleanup_resent_recip = argv_alloc(2); + cleanup_sender = 0; + cleanup_temp1 = vstring_alloc(10); + cleanup_temp2 = vstring_alloc(10); + cleanup_inbuf = vstring_alloc(100); + cleanup_dups = been_here_init(var_dup_filter_limit, BH_FLAG_FOLD); +} + +/* cleanup_state_free - destroy global state */ + +void cleanup_state_free(void) +{ + if (cleanup_fullname) + myfree(cleanup_fullname); + if (cleanup_sender) + myfree(cleanup_sender); + if (cleanup_from) + myfree(cleanup_from); + if (cleanup_resent_from) + myfree(cleanup_resent_from); + if (cleanup_recip) + myfree(cleanup_recip); + if (cleanup_return_receipt) + myfree(cleanup_return_receipt); + if (cleanup_errors_to) + myfree(cleanup_errors_to); + vstring_free(cleanup_header_buf); + argv_free(cleanup_recipients); + argv_free(cleanup_resent_recip); + vstring_free(cleanup_temp1); + vstring_free(cleanup_temp2); + vstring_free(cleanup_inbuf); + if (cleanup_queue_id) + myfree(cleanup_queue_id); + been_here_free(cleanup_dups); +} diff --git a/postfix/conf/LICENSE b/postfix/conf/LICENSE new file mode 100644 index 000000000..5e83ceb25 --- /dev/null +++ b/postfix/conf/LICENSE @@ -0,0 +1,59 @@ +Please read this carefully. You must agree to the following terms and +conditions before installing the Secure Mailer or any related +documentation ("Software"). If you do not agree to these terms +and conditions, you may not install or use the Software. + +Permission to reproduce and create derivative works from the Software +("Software Derivative Works") is hereby granted to you under the +copyrights of International Business Machines Corporation ("IBM"). IBM +also grants you the right to distribute the Software and Software +Derivative Works. + +You grant IBM a world-wide, royalty-free right to use, copy, +distribute, sublicense and prepare derivative works based upon any +feedback, including materials, error corrections, Software Derivatives, +enhancements, suggestions and the like that you provide to IBM relating +to the Software. + +IBM licenses the Software to you on an "AS IS" basis, without warranty +of any kind. IBM HEREBY EXPRESSLY DISCLAIMS ALL WARRANTIES OR +CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, NON +INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. You are solely +responsible for determining the appropriateness of using this Software +and assume all risks associated with the use and distribution of this +Software, including but not limited to the risks of program errors, +damage to or loss of data, programs or equipment, and unavailability or +interruption of operations. IBM WILL NOT BE LIABLE FOR ANY DIRECT +DAMAGES OR FOR ANY SPECIAL, INCIDENTAL, OR INDIRECT DAMAGES OR FOR ANY +ECONOMIC CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS OR SAVINGS), +EVEN IF IBM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IBM +will not be liable for the loss of, or damage to, your records or data, +or any damages claimed by you based on a third party claim. You may +not use the name "IBM" or any other IBM trademark without the prior +written consent of IBM. + +You agree to distribute the Software and any Software Derivatives under +a license agreement that: 1) is sufficient to notify all licensees of +the Software and Software Derivatives that IBM assumes no liability for +any claim that may arise regarding the Software or Software +Derivatives, and 2) that disclaims all warranties, both express and +implied, from IBM regarding the Software and Software Derivatives. (If +you include this Agreement with any distribution of the Software or +Software Derivatives you will have met this requirement.) You agree +that you will not delete any copyright notices in the Software. + +In the event an intellectual property claim is made or appears likely +to be made with respect to the Software, you agree to permit IBM to +enable you to continue to use the Software, or to modify it, or replace +it with software that is at least functionally equivalent. If IBM +determines that none of these alternatives is reasonably available, you +agree, at IBM's request, upon notice to you, to discontinue further +distribution of the Software and to delete or destroy all copies of the +Software you possess. This is IBM's entire obligation to you regarding +any claim of infringement. + +This Agreement is the exclusive statement of your rights in the +Software as provided by IBM. Except for the licenses granted to you in +the second paragraph above, no other licenses are granted hereunder, by +estoppel, implication or otherwise. diff --git a/postfix/conf/access b/postfix/conf/access new file mode 100644 index 000000000..f4ca6ea31 --- /dev/null +++ b/postfix/conf/access @@ -0,0 +1,69 @@ +#++ +# NAME +# access 5 +# SUMMARY +# format of Postfix access table +# SYNOPSIS +# \fBpostmap /etc/postfix/access\fR +# DESCRIPTION +# The optional \fBaccess\fR table directs the Postfix SMTP server +# to selectively reject or accept mail from or to specific hosts, +# domains, networks, host addresses or mail addresses. +# +# The table serves as input to the \fBpostmap\fR(1) command. The +# result, an indexed file in \fBdbm\fR or \fBdb\fR format, +# is used for fast searching by the mail system. After an update +# it may take a minute or so before the change becomes visible. +# Issue a \fBpostfix reload\fR command to eliminate the delay. +# +# The format of the access table is as follows: +# .IP "blanks and comments" +# Blank lines are ignored, as are lines beginning with `#'. +# .IP "\fIpattern action\fR" +# When \fIpattern\fR matches a mail address, domain or host address, +# perform the corresponding \fIaction\fR. +# PATTERNS +# Patterns are tried in the order as listed below: +# .ad +# .fi +# .IP \fIuser\fR@\fIdomain\fR +# Matches the specified mail address. +# .IP \fIdomain.name\fR +# Matches the \fIdomain.name\fR itself and any subdomain thereof, +# either in hostnames or in mail addresses. Top-level domains will +# never be matched. +# .IP \fIuser\fR@ +# Matches all mail addresses with the specified user part. +# .IP \fInet.work.addr.ess\fR +# .IP \fInet.work.addr\fR +# .IP \fInet.work\fR +# .IP \fInet\fR +# Matches any host address in the specified network. A network +# address is a sequence of one or more octets separated by ".". +# ACTIONS +# .ad +# .fi +# .IP "[\fB45\fR]\fIXX text\fR" +# Reject the address etc. that matches the pattern, and respond with +# the numerical code and text. +# .IP \fBREJECT\fR +# Reject the address etc. that matches the pattern. A generic +# error response message is generated. +# .IP \fBOK\fR +# .IP "\fIAny other text\fR" +# Accept the address etc. that matches the pattern. +# BUGS +# The table format does not understand quoting conventions. +# SEE ALSO +# postmap(1) create mapping table +# smtpd(8) smtp server +# 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 +#-- diff --git a/postfix/conf/aliases b/postfix/conf/aliases new file mode 100644 index 000000000..4199c3087 --- /dev/null +++ b/postfix/conf/aliases @@ -0,0 +1,115 @@ +#++ +# NAME +# aliases 5 +# SUMMARY +# format of the Postfix alias database +# SYNOPSIS +# .fi +# \fBpostalias\fR [\fB-c\fR \fIconfig_dir\fR] [\fB-v\fR] +# [\fIfile_type\fR:]\fIinput_file\fR +# DESCRIPTION +# The \fBaliases\fR file provides a system-wide mechanism to +# redirect mail for local recipients. +# +# The file serves as input to the \fBpostalias\fR(1) command. The +# result, an indexed file in \fBdbm\fR or \fBdb\fR format, is +# used for fast lookup by the mail system. After an update +# it may take a minute or so before the change becomes visible. +# Issue a \fBpostfix reload\fR command to eliminate the delay. +# +# The input and output file formats are expected to be compatible +# with Sendmail version 8, and are expected to be suitable for the +# use as NIS maps. +# +# Users can control delivery of their own mail by setting +# up \fB.forward\fR files in their home directory. +# Lines in per-user \fB.forward\fR files have the same syntax +# as the right-hand side of \fBaliases\fR entries. +# +# The format of the alias database input file is as follows: +# .IP \(bu +# An alias definition has the form +# .sp +# .ti +5 +# \fIname\fR: \fIvalue1\fR, \fIvalue2\fR, \fI...\fR +# .IP \(bu +# Lines that begin with whitespace continue the previous line. +# .IP \(bu +# Blank lines are ignored, as are lines beginning with `#'. +# .PP +# The \fIname\fR is a local address (no domain part). +# Use double quotes when the name contains any special characters +# such as whitespace, `#', `:', or `@'. The \fIname\fR is folded to +# lowercase, in order to make database lookups case insensitive. +# .PP +# In addition, when an alias exists for \fBowner-\fIname\fR, delivery +# diagnostics are directed to that address, instead of to the originator. +# This is typically used to direct delivery errors to the owner of +# a mailing list, who is in a better position to deal with mailing +# list delivery problems than the originator of the undelivered mail. +# .PP +# The \fIvalue\fR contains one or more of the following: +# .IP \fIaddress\fR +# Mail is forwarded to \fIaddress\fR, which is compatible +# with the RFC 822 standard. +# .IP \fI/file/name\fR +# Mail is appended to \fI/file/name\fR. See \fBlocal\fR(8) +# for details of delivery to file. +# Delivery is not limited to regular files. For example, to dispose +# of unwanted mail, deflect it to \fB/dev/null\fR. +# .IP "|\fIcommand\fR" +# Mail is piped into \fIcommand\fR. Commands that contain special +# characters, such as whitespace, should be enclosed between double +# quotes. See \fBlocal\fR(8) for details of delivery to command. +# .sp +# When the command fails, a limited amount of command output is +# mailed back to the sender. The file \fB/usr/include/sysexits.h\fR +# defines the expected exit status codes. For example, use +# \fB|"exit 67"\fR to simulate a "user unknown" error, and +# \fB|"exit 0"\fR to implement an expensive black hole. +# .IP \fB:include:\fI/file/name\fR +# Mail is sent to the destinations listed in the named file. +# Lines in \fB:include:\fR files have the same syntax +# as the right-hand side of alias entries. +# .sp +# A destination can be any destination that is described in this +# manual page. However, delivery to "|\fIcommand\fR" and +# \fI/file/name\fR is disallowed by default. To enable, edit the +# \fBallow_mail_to_commands\fR and \fBallow_mail_to_files\fR +# configuration parameters. +# ADDRESS EXTENSION +# .ad +# .fi +# When alias database search fails, and the recipient localpart +# contains the optional recipient delimiter (e.g., \fIuser+foo\fR), +# the search is repeated for the unextended address (e.g., \fIuser\fR). +# CONFIGURATION PARAMETERS +# .ad +# .fi +# The following \fBmain.cf\fR parameters are especially relevant to +# this topic. See the Postfix \fBmain.cf\fR file for syntax details +# and for default values. Use the \fBpostfix reload\fR command after +# a configuration change. +# .IP \fBalias_maps\fR +# List of alias databases. +# .IP \fBallow_mail_to_commands\fR +# Restrict the usage of mail delivery to external command. +# .IP \fBallow_mail_to_files\fR +# Restrict the usage of mail delivery to external file. +# .IP \fBrecipient_delimiter\fR +# Delimiter that separates recipients from address extensions. +# STANDARDS +# RFC 822 (ARPA Internet Text Messages) +# SEE ALSO +# local(8) local delivery agent +# postalias(1) alias database management +# 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 +#-- diff --git a/postfix/conf/canonical b/postfix/conf/canonical new file mode 100644 index 000000000..9cfde8d63 --- /dev/null +++ b/postfix/conf/canonical @@ -0,0 +1,110 @@ +#++ +# NAME +# canonical 5 +# SUMMARY +# format of Postfix canonical table +# SYNOPSIS +# \fBpostmap /etc/postfix/canonical\fR +# DESCRIPTION +# The optional \fBcanonical\fR file specifies an address mapping for +# local and non-local addresses. The mapping is used by the +# \fBcleanup\fR(8) daemon. The address mapping is recursive. +# +# The file serves as input to the \fBpostmap\fR(1) command. The result, +# an indexed file in \fBdbm\fR or \fBdb\fR format, is used for +# fast searching by the mail system. After an update +# it may take a minute or so before the change becomes visible. +# Issue a \fBpostfix reload\fR command to eliminate the delay. +# +# The \fBcanonical\fR mapping affects both message header addresses +# (i.e. addresses that appear inside messages) and message envelope +# addresses (for example, the addresses that are used in SMTP protocol +# commands). Think Sendmail rule set \fBS3\fR, if you like. +# +# Typically, one would use the \fBcanonical\fR table to replace login +# names by \fIFirstname.Lastname\fR, or to clean up addresses produced +# by legacy mail systems. +# +# The \fBcanonical\fR mapping is not to be confused with \fIvirtual +# domain\fR support. Use the \fBvirtual\fR(5) map for that purpose. +# +# The \fBcanonical\fR mapping is not to be confused with local aliasing. +# Use the \fBaliases\fR(5) map for that purpose. +# +# The format of the \fBcanonical\fR table is as follows, mappings +# being tried in the order as listed in this manual page: +# .IP "blanks and comments" +# Blank lines are ignored, as are lines beginning with `#'. +# .IP "\fIuser\fR@\fIdomain address\fR" +# \fIuser\fR@\fIdomain\fR is replaced by \fIaddress\fR. This form +# has the highest precedence. +# .sp +# This form useful to clean up addresses produced by legacy mail systems. +# It can also be used to produce \fIFirstname.Lastname\fR style +# addresses, but see below for a simpler solution. +# .IP "\fIuser address\fR" +# \fIuser\fR@\fIsite\fR is replaced by \fIaddress\fR when \fIsite\fR is +# equal to $\fBmyorigin\fR, when \fIsite\fR is listed in +# $\fBmydestination\fR, or when it is listed in $\fBinet_interfaces\fR. +# .sp +# This form is useful for replacing login names by +# \fIFirstname.Lastname\fR. +# .IP "@\fIdomain address\fR" +# Every address in \fIdomain\fR is replaced by \fIaddress\fR. +# This form has the lowest precedence. +# .PP +# In all the above forms, when \fIaddress\fR has the form +# @\fIotherdomain\fR, the result is the same user in \fIotherdomain\fR. +# ADDRESS EXTENSION +# .fi +# .ad +# When table lookup fails, and the address localpart contains the +# optional recipient delimiter (e.g., \fIuser+foo\fR@\fIdomain\fR), the +# search is repeated for the unextended address (e.g. +# \fIuser\fR@\fIdomain\fR), and the unmatched extension is propagated +# to the result of table lookup. The matching order is: +# \fIuser+foo\fR@\fIdomain\fR, \fIuser\fR@\fIdomain\fR, +# \fIuser+foo\fR, \fIuser\fR, and @\fIdomain\fR. +# BUGS +# The table format does not understand quoting conventions. +# CONFIGURATION PARAMETERS +# .ad +# .fi +# The following \fBmain.cf\fR parameters are especially relevant to +# this topic. See the Postfix \fBmain.cf\fR file for syntax details +# and for default values. Use the \fBpostfix reload\fR command after +# a configuration change. +# .IP \fBcanonical_maps\fR +# List of canonical mapping tables. +# .IP \fBrecipient_canonical_maps\fR +# Address mapping lookup table for envelope and header recipient +# addresses. +# .IP \fBsender_canonical_maps\fR +# Address mapping lookup table for envelope and header sender +# addresses. +# .PP +# Other parameters of interest: +# .IP \fBinet_interfaces\fR +# The network interface addresses that this system receives mail on. +# .IP \fBmasquerade_domains\fR +# List of domains that hide their subdomain structure. +# .IP \fBmasquerade_exceptions\fR +# List of user names that are not subject to address masquerading. +# .IP \fBmydestination\fR +# List of domains that this mail system considers local. +# .IP \fBmyorigin\fR +# The domain that is appended to locally-posted mail. +# SEE ALSO +# cleanup(8) canonicalize and enqueue mail +# postmap(1) create mapping table +# virtual(5) virtual domain mapping +# 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 +#-- diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf new file mode 100644 index 000000000..dcdd3cea3 --- /dev/null +++ b/postfix/conf/main.cf @@ -0,0 +1,322 @@ +# Global Postfix configuration file. This file lists only a subset +# of all 100+ parameters. See the sample-xxx.cf files for a full list. +# +# The general format is lines with parameter = value pairs. Lines +# that begin with whitespace continue the previous line. A value can +# contain references to other $names or ${name}s. + +# LOCAL PATHNAME INFORMATION +# +# The queue_directory specifies the location of the Postfix queue. +# This is also the root directory of Postfix daemons that run chrooted. +# The contributed source code from http://www.postfix.org/ has examples +# for setting up Postfix chroot environments on different UNIX systems. +# +queue_directory = /var/spool/postfix + +# The program_directory parameter specifies the default location of +# Postfix support programs and daemons. This setting can be overruled +# with the command_directory and daemon_directory parameters. +# +program_directory = /some/where/postfix/bin + +# The command_directory parameter specifies the location of all +# postXXX commands. The default value is $program_directory. +# +#command_directory = /usr/sbin + +# The daemon_directory parameter specifies the location of all Postfix +# daemon programs (i.e. programs listed in the master.cf file). The +# default value is $program_directory. This directory must be owned +# by root. +# +#daemon_directory = /usr/libexec/postfix + +# QUEUE AND PROCESS OWNERSHIP +# +# The mail_owner parameter specifies the owner of the Postfix queue +# and of most Postfix daemon processes. Specify the name of a user +# account THAT DOES NOT SHARE A GROUP WITH OTHER ACCOUNTS AND THAT +# OWNS NO OTHER FILES OR PROCESSES ON THE SYSTEM. In particular, +# don't specify nobody or daemon. PLEASE USE A DEDICATED USER. +# +#mail_owner = postfix + +# The default_privs parameter specifies the default rights used by +# the local delivery agent for delivery to external file or command. +# These rights are used in the absence of a recipient user context. +# DO NOT SPECIFY A PRIVILEGED USER OR THE POSTFIX OWNER. +# +#default_privs = nobody + +# INTERNET HOST AND DOMAIN NAMES +# +# The myhostname parameter specifies the internet hostname of this +# mail system. The default is to use the fully-qualified domain name +# from gethostname(). $myhostname is used as a default value for many +# other configuration parameters. +# +#myhostname = host.domain.name +#myhostname = virtual.domain.name + +# The mydomain parameter specifies the local internet domain name. +# The default is to use $myhostname minus the first component. +# $mydomain is used as a default value for many other configuration +# parameters. +# +#mydomain = domain.name + +# SENDING MAIL +# +# The myorigin parameter specifies the domain that locally-posted +# mail appears to come from. The default is to append $myhostname, +# which is fine for small sites. If you run a domain with multiple +# machines, you should (1) change this to $mydomain and (2) set up +# a domain-wide alias database that aliases each user to +# user@that.users.mailhost. +# +#myorigin = $myhostname +#myorigin = $mydomain + +# RECEIVING MAIL + +# The inet_interfaces parameter specifies the network interface +# addresses that this mail system receives mail on. By default, +# the software claims all active interfaces on the machine. The +# parameter also controls delivery of mail to user@[ip.address]. +# +#inet_interfaces = all +#inet_interfaces = $myhostname +#inet_interfaces = $myhostname, localhost + +# The mydestination parameter specifies the list of domains that this +# machine considers itself the final destination for. +# +# The default is $myhostname + localhost.$mydomain. On a mail domain +# gateway, you should also include $mydomain. Do not specify the +# names of domains that this machine is backup MX host for. Specify +# those names via the relay_domains or permit_mx_backup settings for +# the SMTP server (see sample-smtpd.cf. +# +# The local machine is always the final destination for mail addressed +# to user@[the.net.work.address] of an interface that the mail system +# receives mail on (see the inet_interfaces parameter). +# +# Specify a list of host or domain names, /file/name or type:table +# patterns, separated by commas and/or whitespace. A /file/name +# pattern is replaced by its contents; a type:table is matched when +# a name matches a lookup key. Continue long lines by starting the +# next line with whitespace. +# +#mydestination = $myhostname, localhost.$mydomain +#mydestination = $myhostname, localhost.$mydomain $mydomain +#mydestination = $myhostname, localhost.$mydomain, $mydomain, +# mail.$mydomain, www.$mydomain, ftp.$mydomain + +# INTERNET VERSUS INTRANET +# +# The relayhost parameter specifies the default host to send mail to +# when no entry is matched in the optional transport(5) table. When +# no relayhost is given, mail is routed directly to the destination. +# +# On an intranet, specify the organizational domain name. If your +# internal DNS uses no MX records, specify the name of the intranet +# gateway host instead. +# +# Specify a domain, host, host:port, [address] or [address:port]. +# Use the form [destination] to turn off MX lookups. See also the +# default_transport parameter if you're connected via UUCP. +# +#relayhost = $mydomain +#relayhost = gateway.my.domain +#relayhost = uucphost +#relayhost = [mail.$mydomain:9999] + +# DEFAULT TRANSPORT +# +# The default_transport parameter specifies the default message +# delivery transport to use when no transport is explicitly given in +# the optional transport(5) table. +# +#default_transport = smtp +#default_transport = uucp + +# ADDRESS REWRITING +# +# Insert text from sample-rewrite.cf if you need to do address +# masquerading. +# +# Insert text from sample-canonical.cf if you need to do address +# rewriting, or if you need username->Firstname.Lastname mapping. + +# ADDRESS REDIRECTION (VIRTUAL DOMAIN) +# +# Insert text from sample-virtual.cf if you need virtual domain support. + +# "USER HAS MOVED" BOUNCE MESSAGES +# +# Insert text from sample-relocated.cf if you need "user has moved" +# style bounce messages. Alternatively, you can bounce recipients +# with an SMTP server access table. See sample-smtpd.cf. + +# TRANSPORT MAP +# +# Insert text from sample-transport.cf if you need explicit routing. + +# ALIAS DATABASE +# +# The alias_maps parameter specifies the list of alias databases used +# by the local delivery agent. The default list is system dependent. +# On systems with NIS, the default is to search the local alias +# database, then the NIS alias database. See aliases(5) for syntax +# details. +# +# If you change the alias database, run "postalias /etc/aliases" (or +# wherever your system stores the mail alias file), or simply run +# "newaliases" to build the necessary DBM or DB file. +# +# It will take a minute or so before changes become visible. Use +# "postfix reload" to eliminate the delay. +# +#alias_maps = dbm:/etc/aliases +#alias_maps = hash:/etc/aliases +#alias_maps = hash:/etc/aliases, nis:mail.aliases + +# The alias_database parameter specifies the alias database(s) that +# are built with "newaliases" or "sendmail -bi". This is a separate +# configuration parameter, because alias_maps (see above) may specify +# tables that are not necessarily all under control by Postfix. +# +#alias_database = dbm:/etc/aliases +#alias_database = dbm:/etc/mail/aliases +#alias_database = hash:/etc/aliases +#alias_database = hash:/etc/aliases, hash:/opt/majordomo/aliases + +# ADDRESS EXTENSIONS (e.g., user+foo) +# +# The recipient_delimiter parameter specifies the separator between +# user names and address extensions (user+foo). See canonical(5), +# local(8), relocated(5) and virtual(5) for the effects this has on +# aliases, canonical, virtual, relocated and .forward file lookups. +# Basically, the software tries user+foo and .forward+foo before +# trying user and .forward. +# +# recipient_delimiter = + + +# DELIVERY TO MAILBOX +# +# The home_mailbox parameter specifies the optional pathname of a +# mailbox relative to a user's home directory. The default is to +# deliver to the UNIX-style /var/spool/mail/user or /var/mail/user. +# Specify "Maildir/" for qmail-style delivery (the / is required). +# +#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. +# +# 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 + +# JUNK MAIL CONTROLS +# +# The controls listed here are only a very small subset. See the file +# sample-smtpd.cf for an elaborate list of anti-UCE controls. + +# The relay_domains parameter restricts what domains (and subdomains +# thereof) this mail system will relay mail from or to. See the +# smtpd_recipient_restrictions restriction in the file sample-smtpd.cf. +# +# By default, Postfix relays mail only from or to sites in or below +# $mydestination, or in the optional virtual domain list. +# +# Specify a list of hosts or domains, /file/name patterns or type:name +# lookup tables, separated by commas and/or whitespace. Continue +# long lines by starting the next line with whitespace. A file name +# is replaced by its contents; a type:name table is matched when a +# (parent) domain appears as lookup key. +# +# NOTE: Postfix will not automatically forward mail for domains that +# 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 + +# The mynetworks parameter specifies the list of networks that are +# local to this machine. The list is used by the anti-UCE software +# to distinguish local clients from strangers. See permit_mynetworks +# and smtpd_recipient_restrictions in the file sample-smtpd.cf file. +# +# The default is all networks attached to the machine: a complete +# class A network, a complete class B network, and so on. If you want +# stricter control, specify a list of network/mask patterns, where +# the mask specifies the number of bits in the network part of a host +# address. You can also specify the absolute pathname of a pattern +# file instead of listing the patterns here. +# +#mynetworks = 168.100.189.0/28, 127.0.0.0/8 +#mynetworks = $config_directory/mynetworks + +# SHOW SOFTWARE VERSION OR NOT +# +# The smtpd_banner parameter specifies the text that follows the 220 +# status code in the SMTP greeting banner. Some people like to see +# the mail version advertised. By default, Postfix shows no version. +# +# You MUST specify the $myhostname at the start of the text. When +# the SMTP client sees its own hostname at the start of an SMTP +# greeting banner it will report a mailer loop. That's better than +# having a machine meltdown. +# +#smtpd_banner = $myhostname ESMTP $mail_name +#smtpd_banner = $myhostname ESMTP $mail_name ($mail_version) + +# PARALLEL DELIVERY TO THE SAME DESTINATION +# +# How many parallel deliveries to the same user or domain? With local +# delivery, it does not make sense to do massively parallel delivery +# to the same user, because mailbox updates must happen sequentially, +# and expensive pipelines in .forward files can cause disasters when +# too many are run at the same time. With SMTP deliveries, 10 +# simultaneous connections to the same domain could be sufficient to +# raise eyebrows. +# +# Each message delivery transport has its XXX_destination_concurrency_limit +# parameter. The default is $default_destination_concurrency_limit. + +local_destination_concurrency_limit = 2 +default_destination_concurrency_limit = 10 + +# DEBUGGING CONTROL +# +# The debug_peer_level parameter specifies the increment in verbose +# logging level when an SMTP client or server host name or address +# matches a pattern in the debug_peer_list parameter. +# +debug_peer_level = 2 + +# The debug_peer_list parameter specifies an optional list of domain +# or network patterns, /file/name patterns or type:name tables. When +# an SMTP client or server host name or address matches a pattern, +# increase the verbose logging level by the amount specified in the +# debug_peer_level parameter. +# +# debug_peer_list = 127.0.0.1 +# debug_peer_list = some.domain + +# The debugger_command specifies the external command that is executed +# when a Postfix daemon program is run with the -D option. +# +# Use "command .. & sleep 5" so that the debugger can attach before +# the process marches on. If you use an X-based debugger, be sure to +# set up your XAUTHORITY environment variable before starting Postfix. +# +debugger_command = + PATH=/usr/bin:/usr/X11R6/bin + xxgdb $daemon_directory/$process_name $process_id & sleep 5 + +# Other configurable parameters. diff --git a/postfix/conf/master.cf b/postfix/conf/master.cf new file mode 100644 index 000000000..595cbca3f --- /dev/null +++ b/postfix/conf/master.cf @@ -0,0 +1,61 @@ +# +# Postfix master process configuration file. Each line describes how +# a mailer component program should be run. The fields that make up +# each line are described below. A "-" field value requests that a +# default value be used for that field. +# +# Service: any name that is valid for the specified transport type +# (the next field). With INET transports, a service is specified as +# host:port. The host part (and colon) may be omitted. Either host +# or port may be given in symbolic form or in numeric form. +# +# Transport type: "inet" for Internet sockets, "unix" for UNIX-domain +# sockets, "fifo" for named pipes. +# +# Private: whether or not access is restricted to the mail system. +# Default is private service. Internet (inet) sockets can't be private. +# +# Unprivileged: whether the service runs with root privileges or as +# the owner of the Postfix system (the owner name is controlled by the +# mail_owner configuration variable in the main.cf file). +# +# Chroot: whether or not the service runs chrooted to the mail queue +# directory (pathname is controlled by the queue_directory configuration +# variable in the main.cf file). Presently, all Postfix daemons can run +# chrooted, except for the pipe and local daemons. The contributed +# source code from http://www.postfix.org/ describes how to set up +# a Postfix chroot environment for your type of machine. +# +# Wakeup time: automatically wake up the named service after the +# specified number of seconds. Specify 0 for no wakeup. Presently, +# only the local pickup and queue manager daemons need a wakeup timer. +# +# Max procs: the maximum number of processes that may execute this +# service simultaneously. Default is to use a globally configurable +# limit (the default_process_limit configuration parameter in main.cf). +# +# Command + args: the command to be executed. The command name is +# relative to the Postfix program directory (pathname is controlled by +# the program_directory configuration variable). Adding one or more +# -v options turns on verbose logging for that service; adding a -D +# option enables symbolic debugging (see the debugger_command variable +# in the main.cf configuration file). +# +# ========================================================================== +# service type private unpriv chroot wakeup maxproc command + args +# (yes) (yes) (yes) (never) (50) +# ========================================================================== +smtp inet n - n - - smtpd +pickup fifo n n n 60 1 pickup +cleanup unix - - n - 0 cleanup +qmgr fifo n - n 300 1 qmgr +rewrite unix - - n - - trivial-rewrite +bounce unix - - n - 0 bounce +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} +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 new file mode 100755 index 000000000..a4545bf7d --- /dev/null +++ b/postfix/conf/postfix-script-nosgid @@ -0,0 +1,218 @@ +#!/bin/sh + +#++ +# NAME +# postfix-script 1 +# SUMMARY +# execute Postfix administrative commands +# SYNOPSIS +# \fBpostfix-script\fR \fIcommand\fR +# DESCRIPTION +# The \fBfBpostfix-script\fR script executes Postfix administrative +# commands in an environtment that is set up by the \fBpostfix\fR(1) +# command. +# SEE ALSO +# master(8) Postfix master program +# postfix(1) Postfix administrative interface +# LICENSE +# .ad +# .fi +# The Secure Mailer license must be distributed with this software. +# AUTHOR(S) +# Wietse Venema +# IBM T.J. Watson Research +# P.O. Box 704 +# Yorktown Heights, NY 10598, USA +#-- + +# Avoid POSIX death due to SIGHUP when some parent process exits. + +trap '' 1 + +case $daemon_directory in +"") echo This script must be run by the postfix command. 1>&2 + echo Do not run directly. 1>&2 + exit 1 +esac + +LOGGER="$command_directory/postlog -t postfix-script" +INFO="$LOGGER -p info" +WARN="$LOGGER -p warn" +ERROR="$LOGGER -p error" +FATAL="$LOGGER -p fatal" +PANIC="$LOGGER -p panic" + +umask 022 + +# +# LINUX by default does not synchronously update directories - +# that's dangerous for mail. +# +if [ -x /usr/bin/chattr ] +then + CHATTR="/usr/bin/chattr +S" +else + CHATTR=: +fi + +# +# Can't do much without these in place. +# +cd $command_directory || { + $FATAL no Postfix command directory $command_directory! + exit 1 +} +cd $daemon_directory || { + $FATAL no Postfix daemon directory $daemon_directory! + exit 1 +} +cd $config_directory || { + $FATAL no Postfix configuration directory $config_directory! + exit 1 +} +cd $queue_directory || { + $FATAL no Postfix queue directory $queue_directory! + exit 1 +} + +# +# Parse JCL +# +case $1 in + +start_msg) + + echo "Start postfix" + ;; + +stop_msg) + + echo "Stop postfix" + ;; + +start) + + $daemon_directory/master -t 2>/dev/null || { + $FATAL the Postfix mail system is already running + exit 1 + } + $config_directory/postfix-script check || { + $FATAL Postfix integrity check failed! + exit 1 + } + $INFO starting the Postfix mail system + $daemon_directory/master & + ;; + +drain) + + $daemon_directory/master -t 2>/dev/null && { + $FATAL the Postfix mail system is not running + exit 1 + } + $INFO stopping the Postfix mail system + kill -9 `sed 1q pid/master.pid` + ;; + +stop) + + $daemon_directory/master -t 2>/dev/null && { + $FATAL the Postfix mail system is not running + exit 1 + } + $INFO stopping the Postfix mail system + kill `sed 1q pid/master.pid` + ;; + +abort) + + $daemon_directory/master -t 2>/dev/null && { + $FATAL the Postfix mail system is not running + exit 1 + } + $INFO aborting the Postfix mail system + kill `sed 1q pid/master.pid` + ;; + +reload) + + $daemon_directory/master -t 2>/dev/null && { + $FATAL the Postfix mail system is not running + exit 1 + } + $INFO refreshing the Postfix mail system + kill -HUP `cat pid/master.pid` + ;; + +flush) + + cd $queue_directory || { + $FATAL no Postfix queue directory $queue_directory! + exit 1 + } + $command_directory/postkick public qmgr DFA + ;; + +check) + + for dir in $daemon_directory $config_directory $queue_directory + do + ls -lLd $dir | (grep root >/dev/null || + $WARN not owned by root: $dir) + done + + find $daemon_directory/* $config_directory/* ! -user root \ + -exec $WARN not owned by root: {} \; + + find $queue_directory/* $config_directory/* -name '*core' \ + -exec $WARN core file: {} \; 2>/dev/null + + test -d maildrop || { + $WARN creating missing Postfix maildrop directory + mkdir maildrop || exit 1 + chmod 1733 maildrop + chown $mail_owner maildrop + } + test -d pid || { + $WARN creating missing Postfix pid directory + mkdir pid || exit 1 + chmod 755 pid + chown $mail_owner pid + } + for dir in incoming active bounce defer deferred saved corrupt; do + test -d $dir || { + $WARN creating missing Postfix $dir directory + mkdir $dir || exit 1 + chmod 700 $dir; $CHATTR $dir + chown $mail_owner $dir + } + done + test -d public || { + $WARN creating missing Postfix public directory + mkdir public || exit 1 + chmod 755 public + chown $mail_owner public + } + test -d private || { + $WARN creating missing Postfix private directory + mkdir private || exit 1 + chmod 700 private + chown $mail_owner private + } + find `ls -d $queue_directory/* | \ + egrep '/(incoming|active|defer|deferred|bounce|saved|corrupt|public|private)$'` \ + ! \( -type p -o -type s \) ! -user $mail_owner \ + -exec $WARN not owned by $mail_owner: {} \; + + find corrupt -type f -exec $WARN damaged message: {} \; + + # XXX also: look for weird stuff, weird permissions, etc. + ;; + +*) + + $FATAL "usage: postfix start (or stop, reload, abort, flush, or check)" + exit 1 + ;; + +esac diff --git a/postfix/conf/postfix-script-sgid b/postfix/conf/postfix-script-sgid new file mode 100755 index 000000000..7e6b9861c --- /dev/null +++ b/postfix/conf/postfix-script-sgid @@ -0,0 +1,219 @@ +#!/bin/sh + +#++ +# NAME +# postfix-script 1 +# SUMMARY +# execute Postfix administrative commands +# SYNOPSIS +# \fBpostfix-script\fR \fIcommand\fR +# DESCRIPTION +# The \fBfBpostfix-script\fR script executes Postfix administrative +# commands in an environtment that is set up by the \fBpostfix\fR(1) +# command. +# SEE ALSO +# master(8) Postfix master program +# postfix(1) Postfix administrative interface +# LICENSE +# .ad +# .fi +# The Secure Mailer license must be distributed with this software. +# AUTHOR(S) +# Wietse Venema +# IBM T.J. Watson Research +# P.O. Box 704 +# Yorktown Heights, NY 10598, USA +#-- + +# Avoid POSIX death due to SIGHUP when some parent process exits. + +trap '' 1 + +case $daemon_directory in +"") echo This script must be run by the postfix command. 1>&2 + echo Do not run directly. 1>&2 + exit 1 +esac + +LOGGER="$command_directory/postlog -t postfix-script" +INFO="$LOGGER -p info" +WARN="$LOGGER -p warn" +ERROR="$LOGGER -p error" +FATAL="$LOGGER -p fatal" +PANIC="$LOGGER -p panic" + +umask 022 + +# +# LINUX by default does not synchronously update directories - +# that's dangerous for mail. +# +if [ -x /usr/bin/chattr ] +then + CHATTR="/usr/bin/chattr +S" +else + CHATTR=: +fi + +# +# Can't do much without these in place. +# +cd $command_directory || { + $FATAL no Postfix command directory $command_directory! + exit 1 +} +cd $daemon_directory || { + $FATAL no Postfix daemon directory $daemon_directory! + exit 1 +} +cd $config_directory || { + $FATAL no Postfix configuration directory $config_directory! + exit 1 +} +cd $queue_directory || { + $FATAL no Postfix queue directory $queue_directory! + exit 1 +} + +# +# Parse JCL +# +case $1 in + +start_msg) + + echo "Start postfix" + ;; + +stop_msg) + + echo "Stop postfix" + ;; + +start) + + $daemon_directory/master -t 2>/dev/null || { + $FATAL the Postfix mail system is already running + exit 1 + } + $config_directory/postfix-script check || { + $FATAL Postfix integrity check failed! + exit 1 + } + $INFO starting the Postfix mail system + $daemon_directory/master & + ;; + +drain) + + $daemon_directory/master -t 2>/dev/null && { + $FATAL the Postfix mail system is not running + exit 1 + } + $INFO stopping the Postfix mail system + kill -9 `sed 1q pid/master.pid` + ;; + +stop) + + $daemon_directory/master -t 2>/dev/null && { + $FATAL the Postfix mail system is not running + exit 1 + } + $INFO stopping the Postfix mail system + kill `sed 1q pid/master.pid` + ;; + +abort) + + $daemon_directory/master -t 2>/dev/null && { + $FATAL the Postfix mail system is not running + exit 1 + } + $INFO aborting the Postfix mail system + kill `sed 1q pid/master.pid` + ;; + +reload) + + $daemon_directory/master -t 2>/dev/null && { + $FATAL the Postfix mail system is not running + exit 1 + } + $INFO refreshing the Postfix mail system + kill -HUP `cat pid/master.pid` + ;; + +flush) + + cd $queue_directory || { + $FATAL no Postfix queue directory $queue_directory! + exit 1 + } + $command_directory/postkick public qmgr DFA + ;; + +check) + + for dir in $daemon_directory $config_directory $queue_directory + do + ls -lLd $dir | (grep root >/dev/null || + $WARN not owned by root: $dir) + done + + find $daemon_directory/* $config_directory/* ! -user root \ + -exec $WARN not owned by root: {} \; + + find $queue_directory/* $config_directory/* -name '*core' \ + -exec $WARN core file: {} \; 2>/dev/null + + test -d maildrop || { + $WARN creating missing Postfix maildrop directory + mkdir maildrop || exit 1 + chmod 730 maildrop + chown $mail_owner maildrop + chgrp maildrop maildrop + } + test -d pid || { + $WARN creating missing Postfix pid directory + mkdir pid || exit 1 + chmod 755 pid + chown $mail_owner pid + } + for dir in incoming active bounce defer deferred saved corrupt; do + test -d $dir || { + $WARN creating missing Postfix $dir directory + mkdir $dir || exit 1 + chmod 700 $dir; $CHATTR $dir + chown $mail_owner $dir + } + done + test -d public || { + $WARN creating missing Postfix public directory + mkdir public || exit 1 + chmod 755 public + chown $mail_owner public + } + test -d private || { + $WARN creating missing Postfix private directory + mkdir private || exit 1 + chmod 700 private + chown $mail_owner private + } + find `ls -d $queue_directory/* | \ + egrep '/(incoming|active|defer|deferred|bounce|saved|corrupt|public|private)$'` \ + ! \( -type p -o -type s \) ! -user $mail_owner \ + -exec $WARN not owned by $mail_owner: {} \; + + find corrupt -type f -exec $WARN damaged message: {} \; + + # XXX also: look for weird stuff, weird permissions, etc. + ;; + +*) + + $FATAL "usage: postfix start (or stop, reload, abort, flush, or check)" + exit 1 + ;; + +esac diff --git a/postfix/conf/relocated b/postfix/conf/relocated new file mode 100644 index 000000000..ed1a40c66 --- /dev/null +++ b/postfix/conf/relocated @@ -0,0 +1,78 @@ +#++ +# NAME +# relocated 5 +# SUMMARY +# format of Postfix relocated table +# SYNOPSIS +# \fBpostmap /etc/postfix/relocated\fR +# DESCRIPTION +# The optional \fBrelocated\fR file provides the information that is +# used in "user has moved to \fInew_location\fR" bounce messages. +# +# The file serves as input to the \fBpostmap\fR(1) command. The result, +# an indexed file in \fBdbm\fR or \fBdb\fR format, is used for +# fast searching by the mail system. After an update +# issue a \fBpostfix reload\fR command to make the change visible. +# +# Table lookups are case insensitive. +# +# The format of the table is as follows: +# .IP \(bu +# Blank lines are ignored, as are lines beginning with `#'. +# .IP \(bu +# An entry has one of the following form: +# .ti +5 +# \fIkey new_location\fR +# .br +# Where \fInew_location\fR specifies contact information such as +# an email address, or perhaps a street address or telephone number. +# .PP +# The \fIkey\fR field is one of the following: +# .IP \fIuser\fR@\fIdomain\fR +# Matches \fIuser\fR@\fIdomain\fR. This form has precedence over all +# other forms. +# .IP \fIuser\fR +# Matches \fIuser\fR@\fIsite\fR when \fIsite\fR is $\fBmyorigin\fR, +# when \fIsite\fR is listed in $\fBmydestination\fR, or when \fIsite\fR +# is listed in $\fBinet_interfaces\fR. +# .IP @\fIdomain\fR +# Matches every address in \fIdomain\fR. This form has the lowest +# precedence. +# ADDRESS EXTENSION +# .fi +# .ad +# When the search fails, and the address localpart contains the +# optional recipient delimiter (e.g., \fIuser+foo\fR@\fIdomain\fR), +# the search is repeated for the unextended address (e.g. +# \fIuser\fR@\fIdomain\fR). +# BUGS +# The table format does not understand quoting conventions. +# CONFIGURATION PARAMETERS +# .ad +# .fi +# The following \fBmain.cf\fR parameters are especially relevant to +# this topic. See the Postfix \fBmain.cf\fR file for syntax details +# and for default values. Use the \fBpostfix reload\fR command after +# a configuration change. +# .IP \fBrelocated_maps\fR +# List of lookup tables for relocated users or sites. +# .PP +# Other parameters of interest: +# .IP \fBinet_interfaces\fR +# The network interface addresses that this system receives mail on. +# .IP \fBmydestination\fR +# List of domains that this mail system considers local. +# .IP \fBmyorigin\fR +# The domain that is appended to locally-posted mail. +# SEE ALSO +# postmap(1) create lookup table +# LICENSE +# .ad +# .fi +# The Secure Mailer license must be distributed with this software. +# AUTHOR(S) +# Wietse Venema +# IBM T.J. Watson Research +# P.O. Box 704 +# Yorktown Heights, NY 10598, USA +#-- diff --git a/postfix/conf/sample-aliases.cf b/postfix/conf/sample-aliases.cf new file mode 100644 index 000000000..b75c94261 --- /dev/null +++ b/postfix/conf/sample-aliases.cf @@ -0,0 +1,32 @@ +# This file contains example settings of Postfix configuration +# parameters that control alias database lookups. +# +# See the sample-local.cf file for examples of other parameters +# that control local delivery. + +# The alias_database parameter specifies the alias database that is +# built with "newaliases" or "sendmail -bi". This is a separate +# configuration parameter, because the alias_maps parameter may +# specify multiple tables, not necessarily all under control by +# Postfix. +# +# alias_database = dbm:/etc/aliases +# alias_database = dbm:/etc/mail/aliases +alias_database = hash:/etc/aliases + +# The alias_maps parameter specifies the list of alias databases used +# by the local delivery agent. The default list is system dependent. +# On systems with NIS, the default is to search the local alias +# database, then the NIS alias database. See aliases(5) for syntax +# details. +# +# If you change the alias database, run "postalias /etc/aliases" (or +# wherever your system stores the mail alias file), or simply run +# "newaliases" to build the necessary DBM or DB file. +# +# It will take a minute or so before changes become visible. Use +# "postfix reload" to eliminate the delay. +# +# alias_maps = dbm:/etc/aliases, nis:mail.aliases +# alias_maps = hash:/etc/aliases +alias_maps = hash:/etc/aliases diff --git a/postfix/conf/sample-canonical.cf b/postfix/conf/sample-canonical.cf new file mode 100644 index 000000000..65c8fdd84 --- /dev/null +++ b/postfix/conf/sample-canonical.cf @@ -0,0 +1,45 @@ +# This file contains example settings of Postfix configuration +# parameters that control canonical address map lookups. + +# The canonical_maps parameter specifies optional address mapping +# lookup tables. The mapping is applied to both sender and recipient +# addresses, in both envelopes and in headers. This is typically used +# to clean up dirty addresses from legacy mail systems, or to replace +# login names by Firstname.Lastname. See canonical(5) for details. +# +# By default, no canonical address mapping is done. +# +# If you use this feature, run "postmap /etc/postfix/canonical" to +# build the necessary DBM or DB file after every change. The changes +# will become visible after a minute or so. Use "postfix reload" +# to eliminate the delay. +# +# canonical_maps = dbm:/etc/postfix/canonical +# canonical_maps = hash:/etc/postfix/canonical +# canonical_maps = hash:/etc/postfix/canonical, nis:canonical +# canonical_maps = hash:/etc/postfix/canonical, netinfo:/canonical +canonical_maps = + +# The recipient_canonical_maps parameter specifies optional address +# mapping lookup tables for envelope and header RECIPIENT addresses. +# +# By default, no recipient-only address mapping is done. +# +# $recipient_canonical_maps is used before $canonical_maps lookups. +# +# recipient_canonical_maps = hash:/etc/postfix/recipient_canonical +recipient_canonical_maps = + +# The sender_canonical_maps parameter specifies optional address +# mapping lookup tables for envelope and header SENDER addresses. +# +# For example, you want to rewrite the SENDER address user@ugly.domain +# to user@pretty.domain, while still being able to send mail to the +# RECIPIENT address user@ugly.domain. +# +# By default, no sender-only address mapping is done. +# +# $sender_canonical_maps is used before $canonical_maps lookups. +# +# sender_canonical_maps = hash:/etc/postfix/sender_canonical +sender_canonical_maps = diff --git a/postfix/conf/sample-debug.cf b/postfix/conf/sample-debug.cf new file mode 100644 index 000000000..056e22fbf --- /dev/null +++ b/postfix/conf/sample-debug.cf @@ -0,0 +1,29 @@ +# This file contains example settings of Postfix configuration +# parameters that control debugging features. + +# The debug_peer_level parameter specifies the increment in verbose +# logging level when an SMTP client or server host name or address +# matches a pattern in the debug_peer_list parameter. +# +debug_peer_level = 2 + +# The debug_peer_list parameter specifies an optional list of domain +# or network patterns, /file/name patterns or type:name tables. When +# an SMTP client or server host name or address matches a pattern, +# increase the verbose logging level by the amount specified in the +# debug_peer_level parameter. +# +# debug_peer_list = 127.0.0.1 +# debug_peer_list = some.domain +debug_peer_list = + +# The debugger_command specifies the external command that is executed +# when a Postfix daemon program is run with the -D option. +# +# Use "command .. & sleep 5" so that the debugger can attach before +# the process marches on. If you use an X-based debugger, be sure to +# set up your XAUTHORITY environment variable before starting Postfix. +# +debugger_command = + PATH=/usr/bin:/usr/X11R6/bin + xxgdb $program_directory/$process_name $process_id & sleep 5 diff --git a/postfix/conf/sample-ldap.cf b/postfix/conf/sample-ldap.cf new file mode 100644 index 000000000..f68aee6a5 --- /dev/null +++ b/postfix/conf/sample-ldap.cf @@ -0,0 +1,16 @@ +# This file contains example settings of Postfix configuration +# parameters that control LDAP lookups. Source code for LDAP +# lookup is available separately from http://www.postfix.org/ + +# The ldap_lookup_timeout parameter specifies the timeout for LDAP +# database lookups. +# +ldap_lookup_timeout = 10 + +# The ldap_search_base parameter specifies the LDAP database to search. +# +ldap_search_base = + +# The ldap_server_host parameter specifies the LDAP server hostname. +# +ldap_server_host = diff --git a/postfix/conf/sample-local.cf b/postfix/conf/sample-local.cf new file mode 100644 index 000000000..66f978029 --- /dev/null +++ b/postfix/conf/sample-local.cf @@ -0,0 +1,93 @@ +# This file contains example settings of Postfix configuration +# parameters that control local delivery. +# +# See the sample-aliases.cf file for parameters that are specific to +# alias database lookup. + +# +# SECURITY CONTROLS +# + +# The local_command_shell parameter controls what shell will be used +# for delivery to external command. By default, external commands +# are executed directly; commands are given to given to /bin/sh only +# when they contain shell meta characters or shell built-in commands. +# +# Specify a different shell when you need more control over what +# programs can be run from e.g. .forward files. +# +# "sendmail's restricted shell" (smrsh) is what most people will use +# (smrsh is part of recent sendmail distributions) +# +# Note: when a shell is specified, it is invoked even when the +# command contains no shell built-in commands or meta characters. +# +#local_command_shell = /some/where/smrsh -c + +# The allow_mail_to_commands parameter restricts mail delivery to +# external commands. The default is to disallow delivery to "|command" +# in :include: files. +# +# allow_mail_to_commands = alias,forward,include +allow_mail_to_commands = alias,forward + +# The allow_mail_to_files parameter restricts mail delivery to external +# file. The default is to disallow delivery to /file/name in :include: +# files. +# +# allow_mail_to_files = alias,forward,include +allow_mail_to_files = alias,forward + +# The default_privs parameter specifies the default rights used by +# the local delivery agent for delivery to external file or command. +# These rights are used in the absence of a recipient user context. +# DO NOT SPECIFY A PRIVILEGED USER OR THE POSTFIX OWNER. +# +default_privs = nobody + +# +# MAILBOX DELIVERY CONTROLS +# + +# The home_mailbox parameter specifies the optional pathname of a +# mailbox relative to a user's home directory. The default is to +# deliver to the UNIX-style /var/spool/mail/user or /var/mail/user. +# Specify "Maildir/" for qmail-style delivery (the / is required). +# +# home_mailbox = Mailbox +# 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. +# +# 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 +mailbox_command = + +# +# RATE CONTROLS +# + +# The local_destination_concurrency_limit parameter limits the number +# of parallel deliveries to the same local recipient. +# +# The default limit is taken from the default_destination_concurrency_limit +# parameter. I recommend a low limit of 2, just in case someone has +# an expensive shell command in a .forward file or in an alias (e.g., +# a mailing list manager). You don't want to run lots of those. +# +local_destination_concurrency_limit = 2 + +# The local_destination_recipient_limit parameter limits the number +# of recipients per local message delivery. The default limit is +# taken from the default_destination_recipient_limit parameter. +# +# However, the queue manager by design limits the number of recipients +# per local delivery request to exactly 1, so this parameter has no +# effect. +# +local_destination_recipient_limit = 1 diff --git a/postfix/conf/sample-misc.cf b/postfix/conf/sample-misc.cf new file mode 100644 index 000000000..c211f0133 --- /dev/null +++ b/postfix/conf/sample-misc.cf @@ -0,0 +1,232 @@ +# This file contains example settings for miscellaneous Postfix +# configuration parameters. + +# 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 +# determined when the Postfix system is built. +# +# default_database_type = hash +# default_database_type = dbm + +# The default_transport parameter specifies the default message +# delivery transport to use when no transport is explicitly given in +# the optional transport(5) table. +# +# default_transport = uucp +default_transport = smtp + +# The double_bounce_sender parameter specifies the sender address +# for mail that must be discarded when it cannot be delivered. This +# must be a unique name. All mail to this name is silently discarded, +# in order to terminate mail bounce loops. +# +double_bounce_sender = double-bounce + +# The hash_queue_depth parameter specifies the number of subdirectory +# levels below the queue directories listed in the hash_queue_names +# parameter. +# +# Multiple subdirectory levels can speed up directory searches by +# reducing the number of files per directory. +# +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. +# +hash_queue_names = defer + +# The hopcount_limit parameter limits the number of Received: message +# headers. A message that exceeds the limit is bounced. +# +hopcount_limit = 50 + +# The inet_interfaces parameter specifies the network interface +# addresses that this mail system receives mail on. By default, +# the software claims all active interfaces on the machine. The +# parameter also controls delivery of mail to user@[ip.address]. +# +inet_interfaces = all + +# The ipc_idle parameter bounds the idle time in seconds after which +# an internal IPC client disconnects. The purpose is to allow servers +# to terminate voluntarily. Currently this is used by the address +# resolving and rewriting clients. +# +ipc_idle = 100 + +# The ipc_timeout parameter specifies a timeout in seconds for I/O +# on internal communication channels. The purpose is to break out +# of deadlock situations. If the timeout is exceeded the software +# aborts with a fatal error. +# +ipc_timeout = 3600 + +# The mail_name parameter specifies the mail system name that is used +# in Received: headers, in the SMTP greeting banner, and in bounced +# mail. +# +mail_name = Postfix + +# The mail_owner parameter specifies the owner of the Postfix queue +# and of most Postfix daemon processes. Specify the name of a user +# account THAT DOES NOT SHARE A GROUP WITH OTHER ACCOUNTS AND THAT +# OWNS NO OTHER FILES OR PROCESSES ON THE SYSTEM. In particular, +# don't specify nobody or daemon. PLEASE USE A DEDICATED USER. +# +mail_owner = postfix + +# The mail_version parameter specifies the official version of the +# mail system. The version string can be used in, for example, the +# SMTP greeting banner. +# +mail_version = 19981207 + +# The max_idle parameter limits the time in seconds that a Postfix +# daemon process waits for the next service request before exiting. +# This parameter is ignored by the Postfix queue manager. +# +max_idle = 100 + +# The max_use parameter limits the number of service requests handled +# by a Postfix daemon process before exiting. This parameter is +# ignored by the Postfix queue manager. +# +max_use = 100 + +# The mydestination parameter specifies the list of domains that this +# machine considers itself the final destination for. +# +# The default is $myhostname + localhost.$mydomain. On a mail domain +# gateway, you should also include $mydomain. Do not specify the +# names of domains that this machine is backup MX host for. Specify +# those names via the relay_domains or permit_mx_backup settings for +# the SMTP server (see sample-smtpd.cf. +# +# The local machine is always the final destination for mail addressed +# to user@[the.net.work.address] of an interface that the mail system +# receives mail on (see the inet_interfaces parameter). +# +# Specify a list of host or domain names, /file/name or type:table +# patterns, separated by commas and/or whitespace. A /file/name +# pattern is replaced by its contents; a type:table is matched when +# a name matches a lookup key. Continue long lines by starting the +# next line with whitespace. +# +# mydestination = $myhostname, localhost.$mydomain $mydomain +# mydestination = $myhostname, localhost.$mydomain www.$mydomain, ftp.$mydomain +mydestination = $myhostname, localhost.$mydomain + +# The mydomain parameter specifies the local internet domain name. +# The default is to use $myhostname minus the first component. +# $mydomain is used as a default value for many other configuration +# parameters. +# +#mydomain = domain.name + +# The myhostname parameter specifies the internet hostname of this +# mail system. The default is to use the fully-qualified domain name +# from gethostname(). $myhostname is used as a default value for many +# other configuration parameters. +# +#myhostname = host.domain.name + +# The myorigin parameter specifies the domain that locally-posted +# mail appears to come from. The default is to append $myhostname, +# which is fine for small sites. If you run a domain with multiple +# machines, you should (1) change this to $mydomain and (2) set up +# a domain-wide alias database that aliases each user to +# user@that.users.mailhost. +# +# myorigin = $mydomain +myorigin = $myhostname + +# The mynetworks parameter specifies the list of networks that are +# local to this machine. The list is used by the anti-UCE software +# to distinguish local clients from strangers. See permit_mynetworks +# in the sample-smtpd.cf file. +# +# The default is all networks attached to the machine: a complete +# class A network, a complete class B network, and so on. If you want +# stricter control, specify a list of network/mask patterns, where +# the mask specifies the number of bits in the network part of a host +# address. You can also specify the absolute pathname of a pattern +# file instead of listing the patterns here. +# +#mynetworks = 168.100.189.0/28, 127.0.0.0/8 + +# The notify_classes parameter specifies the list of error classes +# that are reported to the postmaster. The default is to report only +# the most serious problems. The paranoid may wish to turn on the +# policy (anti-UCE violations) and protocol error (broken mailers) +# reports. +# +# notify_classes = bounce,policy,protocol,resource,software +notify_classes = resource,software + +# The process_id_directory specifies a lock file directory relative +# to the Postfix queue directory. This facility is used by the master +# daemon to lock out other master daemon instances. +# +process_id_directory = pid + +# The program_directory parameter specifies the location of Postfix +# support programs and daemons. This directory must be owned by root. +# +program_directory = /usr/libexec/postfix + +# The queue_directory specifies the location of the Postfix queue. +# This is also the root directory of Postfix daemons that run chrooted. +# The contributed source code from http://www.postfix.org/ has examples +# for setting up Postfix chroot environments on different UNIX systems. +# +queue_directory = /var/spool/postfix + +# The recipient_delimiter parameter specifies the separator between +# user names and address extensions (user+foo). See canonical(5), +# local(8), relocated(5) and virtual(5) for the effects this has on +# aliases, canonical, virtual, relocated and .forward file lookups. +# Basically, the software tries user+foo and .forward+foo before +# trying user and .forward. +# +# recipient_delimiter = + +recipient_delimiter = + +# The relayhost parameter specifies the default host to send mail to +# when no entry is matched in the optional transport(5) table. When +# no relayhost is given, mail is routed directly to the destination. +# +# On an intranet, specify the organizational domain name. If your +# internal DNS uses no MX records, specify the name of the intranet +# gateway host instead. +# +# Specify a domain, host, host:port, [address] or [address:port]. +# Use the form [destination] to turn off MX lookups. See also the +# default_transport parameter if you're connected via UUCP. +# +# relayhost = $mydomain +# relayhost = gateway.my.domain +# relayhost = uucphost +relayhost = + +# The relocated_maps parameter specifies optional tables with contact +# information for users, hosts or domains that no longer exist. See +# relocated(5) for details. +# +# By default, this feature is disabled. +# +# Specify the types and names of databases to use. After change, +# run "postmap /etc/postfix/relocated", then "postfix reload". +# +# relocated_maps = hash:/etc/postfix/relocated +relocated_maps = + +# The trigger_timeout parameter limits the time to send a trigger to +# a Postfix daemon. This prevents programs from getting stuck when the +# mail system is under heavy load. +# +trigger_timeout = 10 diff --git a/postfix/conf/sample-rate.cf b/postfix/conf/sample-rate.cf new file mode 100644 index 000000000..e95094dcd --- /dev/null +++ b/postfix/conf/sample-rate.cf @@ -0,0 +1,56 @@ +# This file contains example settings of Postfix parameters that +# control delivery rates. + +# The default_destination_concurrency_limit parameter specifies a +# default limit on the number of parallel deliveries to the same +# destination. This is the default limit for delivery via SMTP, via +# the local delivery agent and via the pipe mailer. +# +default_destination_concurrency_limit = 10 + +# The default_destination_recipient_limit parameter specifies a +# default limit on the number of recipients per message delivery. +# This is the default limit for delivery via SMTP, via the local +# delivery agent and via the pipe mailer. +# +default_destination_recipient_limit = 50 + +# The initial_destination_concurrency parameter specifies the initial +# per-destination concurrency level for parallel delivery to the same +# destination. This limit applies to delivery via SMTP, via the local +# delivery agent and via the pipe mailer. +# +# With concurrency of 1, one bad message is enough to block all mail +# to a site. A concurrency of 2 seems a reasonable choice. +# +initial_destination_concurrency = 2 + +# The maximal_backoff_time parameter specifies the maximal time in +# seconds between attempts to deliver a deferred message. +# +maximal_backoff_time = 4000 + +# The maximal_queue_lifetime parameter specifies the maximal time in +# days a message is queued before it is sent back as undeliverable. +# +maximal_queue_lifetime = 5 + +# The minimal_backoff_time parameter specifies the minimal time in +# seconds between attempts to deliver a deferred message. This +# parameter also limits the time an unreachable destination is kept +# in the short-term, in-memory destination status cache. +# +minimal_backoff_time = 1000 + +# The queue_run_delay parameter specifies the time in seconds +# between deferred queue scans by the queue manager. +# +queue_run_delay = 1000 + +# The defer_transports parameter specifies the names of transports +# that should not be delivered to unless someone issues "sendmail +# -q" or equivalent. Specify zero or more names of mail delivery +# transports names that appear in the first field of master.cf). +# +#defer_transports = smtp +#defer_transports = diff --git a/postfix/conf/sample-relocated.cf b/postfix/conf/sample-relocated.cf new file mode 100644 index 000000000..aeea31ad0 --- /dev/null +++ b/postfix/conf/sample-relocated.cf @@ -0,0 +1,18 @@ +# This file contains example settings of Postfix configuration +# parameters that control relocated database lookups. + +# The relocated_maps parameter specifies optional lookup tables with +# new contact information for users or even domains that no longer +# exist. +# +# By default, this feature is disabled. +# +# If you use this feature, run "postmap /etc/postfix/relocated" to +# build the necessary DBM or DB file after change, then "postfix +# reload" to make the changes visible. +# +# relocated_maps = dbm:/etc/postfix/relocated +# relocated_maps = hash:/etc/postfix/relocated +# relocated_maps = hash:/etc/postfix/relocated, nis:virtual +# relocated_maps = hash:/etc/postfix/relocated, netinfo:/relocated +relocated_maps = diff --git a/postfix/conf/sample-resource.cf b/postfix/conf/sample-resource.cf new file mode 100644 index 000000000..846355cdc --- /dev/null +++ b/postfix/conf/sample-resource.cf @@ -0,0 +1,97 @@ +# This file contains example settings of general Postfix resource +# control parameters. +# +# Controls that are specific to message delivery transports are +# described in the respective sample-transportname.cf file. + +# The bounce_size_limit parameter limits the amount of original +# message context in bytes that is sent in a non-delivery notification. +# +bounce_size_limit = 50000 + +# The command_time_limit parameter limits the amount of time for +# delivery to external commands. This limit is used by the local +# delivery agent, and is the default time limit for delivery by the +# pipe mailer. +# +# Note: if you set this time limit to a large value you must update the +# global ipc_timeout parameter as well. See sample-misc.cf for details. +# +command_time_limit = 1000 + +# The default_process_limit parameter specifies the default limit +# on the number of Postfix child processes that provide a given +# service. +# +default_process_limit = 50 + +# The deliver_lock_attempts parameter limits the number of attempts +# to acquire an exclusive lock on a mailbox or other file. +# +deliver_lock_attempts = 5 + +# The deliver_lock_delay parameter limits the time in seconds between +# attempts to acquire an exclusive lock. +# +deliver_lock_delay = 1 + +# The duplicate_filter_limit parameter limits the number of addresses +# remembered by the duplicate filter for alias, virtual, etc. +# expansion. +# +duplicate_filter_limit = 1000 + +# The fork_attempts parameter limits the number of attempts to +# fork() a process. +# +fork_attempts = 5 + +# The fork_delay parameter specifies the delay in seconds between +# fork() attempts. +# +fork_delay = 1 + +# The header_size_limit parameter limits the amount of memory in +# bytes used for processing a message header. If a header is larger, +# the remainder of the entire message is treated as message body. +# +header_size_limit = 102400 + +# The line_length_limit parameter limits the amount of memory in +# bytes used for handling input lines. Longer lines are chopped up +# into pieces and reconstructed upon delivery. +# +line_length_limit = 2048 + +# The message_size_limit parameter limits the total size in bytes of +# a message, including envelope information. +# +message_size_limit = 10240000 + +# The qmgr_message_active_limit parameter limits the number of +# messages in the active queue. +# +qmgr_message_active_limit = 1000 + +# The qmgr_message_recipient_limit parameter limits the number of +# in-memory recipients. This parameter also limits the size of the +# short-term, in-memory destination status cache. +# +qmgr_message_recipient_limit = 1000 + +# The queue_minfree parameter specifies the minimal amount of free +# space in bytes in the queue file system. This is currently used by +# the SMTP server to decide if it will accept any mail at all. +# +queue_minfree = 0 + +# The stale_lock_time parameter limits the time after which a stale +# lock is removed. This is used for delivery to file or mailbox. +# +stale_lock_time = 500 + +# The transport_retry_time parameter specifies the time in seconds +# between attempts by the queue manager to contact a broken message +# delivery transport. +# +transport_retry_time = 60 diff --git a/postfix/conf/sample-rewrite.cf b/postfix/conf/sample-rewrite.cf new file mode 100644 index 000000000..16ab6da8e --- /dev/null +++ b/postfix/conf/sample-rewrite.cf @@ -0,0 +1,48 @@ +# This file contains example settings of Postfix configuration +# parameters that control address rewriting. + +# The allow_percent_hack parameter controls the rewriting of the form +# "user%domain" to "user@domain". This is enabled by default. +# +# allow_percent_hack = no +allow_percent_hack = yes + +# The append_at_myorigin controls the rewriting of the form "user" to +# "user@$mydomain". This should not be turned off. +# +append_at_myorigin = yes + +# The append_dot_mydomain controls the rewriting of the form +# "user@host" to "user@host.$mydomain". This is enabled by default. +# +# append_dot_mydomain = no +append_dot_mydomain = yes + +# The empty_address_recipient specifies the destination for mail from +# <> that is undeliverable (typically, bounce notifications and +# other notifications). By default, such mail is sent to MAILER-DAEMON. +# +empty_address_recipient = MAILER-DAEMON + +# The masquerade_domains parameter gives an optional list of domains +# that must have their subdomain structure stripped off. +# +# By default, address masquerading is disabled. +# +# masquerade_domains = $mydomain +masquerade_domains = + +# The masquerade_exceptions parameter gives an optional list of user +# names that are not subjected to address masquerading. +# +# By default, address masquerading makes no exceptions. +# +#masquerade_exceptions = root +masquerade_exceptions = + +# The swap_bangpath parameter controls the rewriting of the form +# "site!user" to "user@site". This is necessary if your machine is +# connected to UUCP networks. It is enabled by default. +# +# swap_bangpath = no +swap_bangpath = yes diff --git a/postfix/conf/sample-smtp.cf b/postfix/conf/sample-smtp.cf new file mode 100644 index 000000000..e1868b4b4 --- /dev/null +++ b/postfix/conf/sample-smtp.cf @@ -0,0 +1,104 @@ +# This file contains example settings of Postfix configuration +# parameters that control the SMTP client program. + +# +# RATE CONTROLS +# + +# The smtp_destination_concurrency_limit parameter limits the number +# of parallel deliveries to the same destination via the smtp delivery +# agent. +# +# The default limit is the default_destination_concurrency_limit +# parameter. It is probably safer to limit the concurrency to 10. +# +smtp_destination_concurrency_limit = 10 + +# The smtp_destination_recipient_limit parameter limits the number +# of recipients per delivery via the smtp delivery agent. +# +# The default is taken from the default_destination_recipient_limit +# parameter. +# +smtp_destination_recipient_limit = $default_destination_recipient_limit + +# +# TIMEOUT CONTROLS +# +# Note: if you set SMTP timeouts to large values you must update the +# global ipc_timeout parameter as well. See sample-misc.cf for details. +# + +# The smtp_connect_timeout parameter specifies the SMTP client +# 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 exchanger list. Specify 0 to +# disable the timeout. +# +# smtp_connect_timeout = 30 +smtp_connect_timeout = 0 + +# The smtp_helo_timeout parameter specifies the SMTP client timeout +# in seconds for receiving the SMTP greeting banner. +# +# When the server drops the connection without 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 = 300 + +# The smtp_mail_timeout parameter specifies the SMTP client timeout +# in seconds for sending the SMTP MAIL FROM command, and for receiving +# the server response. +# +# In case of problems the client does NOT try the next address on +# the mail exchanger list. +# +smtp_mail_timeout = 300 + +# The smtp_rcpt_timeout parameter specifies the SMTP client timeout +# in seconds for sending the SMTP RCPT TO command, and for receiving +# the server response. +# +# In case of problems the client does NOT try the next address on +# the mail exchanger list. +# +smtp_rcpt_timeout = 300 + +# The smtp_data_init_timeout parameter specifies the SMTP client +# timeout in seconds for sending the SMTP DATA command, and for +# receiving the server response. +# +# In case of problems the client does NOT try the next address on +# the mail exchanger list. +# +smtp_data_init_timeout = 120 + +# The smtp_data_xfer_timeout parameter specifies the SMTP client +# timeout in seconds for sending the SMTP message content. When +# the connection stalls for more than $smtp_data_xfer_timeout the +# SMTP client terminates the transfer. +# +# In case of problems the client does NOT try the next address on +# the mail exchanger list. +# +smtp_data_xfer_timeout = 180 + +# The smtp_data_done_timeout parameter specifies the SMTP client +# timeout in seconds for sending the SMTP ".", and for receiving +# the server response. +# +# When no response is received within the deadline, a warning is +# logged that the mail may be delivered multiple times. +# +# In case of problems the client does NOT try the next address on +# the mail exchanger list. +# +smtp_data_done_timeout = 600 + +# The smtp_quit_timeout parameter specifies the SMTP client timeout +# in seconds for sending the SMTP QUIT command, and for receiving +# the server response. +# +smtp_quit_timeout = 300 diff --git a/postfix/conf/sample-smtpd.cf b/postfix/conf/sample-smtpd.cf new file mode 100644 index 000000000..51c4e87f3 --- /dev/null +++ b/postfix/conf/sample-smtpd.cf @@ -0,0 +1,272 @@ +# This file contains example settings of Postfix configuration parameters +# that control the SMTP server program. + +# +# MISCELLANEOUS +# + +# The smtpd_banner parameter specifies the text that follows the 220 +# status code in the SMTP greeting banner. Some people like to see +# the mail version advertised. By default, Postfix shows no version. +# +# You MUST specify the $myhostname at the start of the text. When +# the SMTP client sees its own hostname at the start of an SMTP +# greeting banner it will report a mailer loop. That's better than +# having a machine meltdown. +# +# smtpd_banner = $myhostname ESMTP $mail_name ($mail_version) +smtpd_banner = $myhostname ESMTP $mail_name + +# The smtpd_recipient_limit parameter restricts the number of recipients +# that the SMTP server accepts per message delivery. +# +smtpd_recipient_limit = 1000 + +# The smtpd_timeout parameter limits the time in seconds to send an +# SMTP server response and to receive an SMTP client request. +# +# Note: if you set SMTP timeouts to large values you must update the +# global ipc_timeout parameter as well. See sample-misc.cf for details. +# +smtpd_timeout = 300 + +# +# TARPIT CONTROLS +# + +# The smtpd_error_sleep_time parameter specifies the time in seconds +# the SMTP server waits before sending a 4xx or 5xx SMTP server error +# response. This prevents naive clients from going into an error - +# disconnect - connect - error loop. +# +smtpd_error_sleep_time = 5 + +# The smtpd_soft_error_limit parameter specifies an error count lower +# limit. When an SMTP client has made this number of errors within +# a session, the server waits error_count seconds before responding +# to any client request. +# +smtpd_soft_error_limit = 10 + +# The smtpd_hard_error_limit parameter specifies an error count upper +# limit. The SMTP server disconnects after an SMTP client makes this +# number of errors within a session. +# +smtpd_hard_error_limit = 100 + +# +# UCE RESTRICTIONS +# + +# The smtpd_client_restrictions parameter specifies optional restrictions +# on SMTP client host names and addresses. +# +# The default is to allow connections from any host. The following +# restrictions are available: +# +# reject_unknown_client:reject the request if the client hostname is unknown. +# permit_mynetworks: permit if the client address matches $mynetworks. +# maptype:mapname: look up client name, parent domains, client address, +# or networks obtained by stripping octets. +# Reject if result is REJECT or "[45]xx text" +# Permit otherwise. +# reject_maps_rbl:reject if the client is listed under $maps_rbl_domains. +# reject: reject the request. Place this at the end of a restriction. +# permit: permit the request. Place this at the end of a restriction. +# +# Restrictions are applied in the order as specified; the first +# restriction that matches wins. +# +# Specify a list of restrictions, separated by commas and/or whitespace. +# Continue long lines by starting the next line with whitespace. +# +smtpd_client_restrictions = + +# The smtpd_helo_required parameter optionally turns on the requirement +# that SMTP clients must introduce themselves at the beginning of an +# SMTP session. +# +# smtpd_helo_required = yes +smtpd_helo_required = no + +# The smtpd_helo_restrictions parameter specifies optional restrictions +# on what SMTP clients can send in SMTP HELO and EHLO commands. +# +# The default is to permit everything. The following restrictions +# are available: +# +# reject_unknown_client:reject the request if the client hostname is unknown. +# 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. +# maptype:mapname: look up HELO hostname or parent domains. +# Reject if result is REJECT or "[45]xx text" +# Permit otherwise. +# check_client_access maptype:mapname: see smtpd_client_restrictions. +# reject: reject the request. Place this at the end of a restriction. +# permit: permit the request. Place this at the end of a restriction. +# +# Restrictions are applied in the order as specified; the first +# restriction that matches wins. +# +# Specify a list of restrictions, separated by commas and/or whitespace. +# Continue long lines by starting the next line with whitespace. +# +# smtpd_helo_restrictions = reject_invalid_hostname +# smtpd_helo_restrictions = permit_mynetworks, reject_unknown_hostname +smtpd_helo_restrictions = + +# The smtpd_sender_restrictions parameter specifies optional restrictions +# on sender addresses that SMTP clients can send in MAIL FROM commands. +# +# The default is to permit any sender address. The following +# restrictions are available: +# +# reject_unknown_client:reject the request if the client hostname is unknown. +# permit_mynetworks: permit if the client address matches $mynetworks. +# reject_unknown_address:reject if the sender domain has no A or MX record. +# maptype:mapname: look up sender address, parent domain, or localpart@. +# Reject if result is REJECT or "[45]xx text" +# Permit otherwise. +# check_client_access maptype:mapname: see smtpd_client_restrictions. +# check_helo_access maptype:mapname: see smtpd_helo_restrictions. +# reject: reject the request. Place this at the end of a restriction. +# permit: permit the request. Place this at the end of a restriction. +# +# Restrictions are applied in the order as specified; the first +# restriction that matches wins. +# +# Specify a list of restrictions, separated by commas and/or whitespace. +# Continue long lines by starting the next line with whitespace. +# +# smtpd_sender_restrictions = reject_unknown_address +# smtpd_sender_restrictions = reject_unknown_address, hash:/etc/postfix/access +smtpd_sender_restrictions = + +# The smtpd_recipient_restrictions parameter specifies restrictions on +# recipient addresses that SMTP clients can send in RCPT TO commands. +# +# The default is to permit any destination from clients that match +# $mynetworks, and to otherwise permit only mail from or to domains +# listed in $relay_domains. +# +# The following restrictions are available: +# +# reject_unknown_client:reject the request if the client hostname is unknown. +# permit_mynetworks: permit if the client address matches $mynetworks. +# check_relay_domains: permit only mail from/to domains in $relay_domains. +# permit_mx_backup: accept mail for sites that list me as MX host. +# maptype:mapname: look up recipient address, parent domain, or localpart@. +# Reject if result is REJECT or "[45]xx text" +# Permit otherwise. +# 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: reject the request. Place this at the end of a restriction. +# permit: permit the request. Place this at the end of a restriction. +# +# Restrictions are applied in the order as specified; the first +# restriction that matches wins. +# +# Specify a list of restrictions, separated by commas and/or whitespace. +# Continue long lines by starting the next line with whitespace. +# +smtpd_recipient_restrictions = permit_mynetworks,check_relay_domains + +# +# ADDITIONAL UCE CONTROLS +# + +# The maps_rbl_domains parameter specifies an optional list of DNS +# domains that publish the network addresses of blacklisted hosts. +# +# By default, RBL blacklist lookups are disabled. See the +# smtpd_client_restrictions parameter. +# +# The real-time blackhole list works as follows: reverse the client +# network address, and reject service if it is listed below any of +# the following domains. +# +maps_rbl_domains = rbl.maps.vix.com + +# The relay_domains parameter restricts what domains (and subdomains +# thereof) this mail system will relay mail from or to. +# +# By default, Postfix relays mail only from or to sites in or below +# $mydestination, or in the optional virtual domain list. +# +# Specify a list of hosts or domains, /file/name patterns or type:name +# lookup tables, separated by commas and/or whitespace. Continue +# long lines by starting the next line with whitespace. A file name +# is replaced by its contents; a type:name table is matched when a +# (parent) domain appears as lookup key. +# +# NOTE: Postfix will not automatically forward mail for domains that +# list this system as their primary or backup MX host. See the +# permit_mx_backup restriction, in the description of the +# smtpd_recipient_restrictions parameter. +# +relay_domains = $mydestination, $virtual_domains + +# +# RESPONSE CODES +# + +# The access_map_reject_code parameter specifies the SMTP server +# response code when a client violates an access map restriction. +# +# Do not change this unless you have a complete understanding of RFC 822. +# +access_map_reject_code = 550 + +# The invalid_hostname_reject_code parameter specifies the SMTP server +# response when a client violates the reject_invalid_hostname anti-UCE +# restriction. +# +# Do not change this unless you have a complete understanding of RFC 822. +# +invalid_hostname_reject_code = 501 + +# The maps_rbl_reject_code parameter specifies the SMTP server response +# when a client violates the maps_rbl_domains restriction. +# +# Do not change this unless you have a complete understanding of RFC 822. +# +maps_rbl_reject_code = 550 + +# The reject_code parameter specifies the SMTP server response code +# when an SMTP client matches a reject restriction. +# +# Do not change this unless you have a complete understanding of RFC 822. +# +reject_code = 550 + +# The relay_domains_reject_code parameter specifies the SMTP server +# response when a client attempts to violate the mail relay policy. +# +# Do not change this unless you have a complete understanding of RFC 822. +# +relay_domains_reject_code = 550 + +# The unknown_address_reject_code parameter specifies the SMTP server +# response when a client violates the reject_unknown_address restriction. +# +# Do not change this unless you have a complete understanding of RFC 822. +# +unknown_address_reject_code = 450 + +# The unknown_client_reject_code parameter specifies the SMTP server +# response when a client without address to name mapping violates +# the reject_unknown_clients restriction. +# +# Do not change this unless you have a complete understanding of RFC 822. +# +unknown_client_reject_code = 450 + +# The unknown_hostname_reject_code parameter specifies the SMTP server +# response when a client violates the reject_unknown_hostname +# restriction. +# +# Do not change this unless you have a complete understanding of RFC 822. +# +unknown_hostname_reject_code = 450 diff --git a/postfix/conf/sample-transport.cf b/postfix/conf/sample-transport.cf new file mode 100644 index 000000000..0600dc30c --- /dev/null +++ b/postfix/conf/sample-transport.cf @@ -0,0 +1,15 @@ +# This file contains example settings of Postfix configuration +# parameters that control the optional transport table lookups. + +# The transport_maps parameter specifies optional tables with domain +# to (transport, nexthop) mappings. See transport(5) for syntax details. +# +# By default, this feature is disabled. Specify the types of databases +# to use. If you use this feature, run "postmap /etc/postfix/transport" +# after change, then "postfix reload". +# +# transport_maps = dbm:/etc/postfix/transport +# transport_maps = hash:/etc/postfix/transport +# transport_maps = hash:/etc/postfix/transport, nis:transport +# transport_maps = hash:/etc/postfix/transport, netinfo:/transport +transport_maps = diff --git a/postfix/conf/sample-virtual.cf b/postfix/conf/sample-virtual.cf new file mode 100644 index 000000000..bfea4df49 --- /dev/null +++ b/postfix/conf/sample-virtual.cf @@ -0,0 +1,20 @@ +# This file contains example settings of Postfix configuration +# parameters that control virtual database lookups. + +# The virtual_maps parameter specifies optional lookup tables to +# redirect specific addresses or even complete domains to another +# address. This is typically used to implement virtual domain support. +# +# By default, no address redirection is done. +# +# If you use this feature, run "postmap /etc/postfix/virtual" to +# build the necessary DBM or DB file after change. +# +# It will take a minute or so before the change becomes visible. +# Use "postfix reload" to eliminate the delay. +# +# virtual_maps = dbm:/etc/postfix/virtual +# virtual_maps = hash:/etc/postfix/virtual +# virtual_maps = hash:/etc/postfix/virtual, nis:virtual +# virtual_maps = hash:/etc/postfix/virtual, netinfo:/virtual +virtual_maps = diff --git a/postfix/conf/transport b/postfix/conf/transport new file mode 100644 index 000000000..524c84d00 --- /dev/null +++ b/postfix/conf/transport @@ -0,0 +1,101 @@ +#++ +# NAME +# transport 5 +# SUMMARY +# format of Postfix transport table +# SYNOPSIS +# \fBpostmap /etc/postfix/transport\fR +# DESCRIPTION +# The optional \fBtransport\fR file specifies a mapping from domain +# hierarchies to message delivery transports and/or relay hosts. The +# mapping is used by the \fBtrivial-rewrite\fR(8) daemon. +# +# The file serves as input to the \fBpostmap\fR(1) command. The result, +# an indexed file in \fBdbm\fR or \fBdb\fR format, is used for +# fast searching by the mail system. After updating this table, +# issue the \fBpostfix reload\fR command to make the change visible. +# +# The format of the transport table is as follows: +# .IP "blanks and comments" +# Blank lines are ignored, as are lines beginning with `#'. +# .IP "\fIdomain transport\fR:\fInexthop\fR" +# Mail for \fIdomain\fR is delivered through \fItransport\fR to +# \fInexthop\fR. +# .IP "\fI.domain transport\fR:\fInexthop\fR" +# Mail for any subdomain of \fIdomain\fR is delivered through +# \fItransport\fR to \fInexthop\fR. +# +# The interpretation of the \fInexthop\fR field is transport +# dependent. In the case of SMTP, specify \fIhost\fR:\fIservice\fR for a +# non-default server port, and use [\fIhost\fR] or [\fIhost\fR:\fIport\fR] +# in order to disable MX (mail exchanger) DNS lookups. The [] form +# can also be used with IP addresses instead of hostnames. +# EXAMPLES +# .ad +# In order to send mail for \fBfoo.org\fR and its subdomains +# via the \fBuucp\fR transport to the UUCP host named \fBfoo\fR: +# +# .ti +5 +# \fBfoo.org uucp:foo\fR +# .ti +5 +# \fB\&.foo.org uucp:foo\fR +# +# When no \fInexthop\fR host name is specified, the destination domain +# name is used instead. For example, the following directs mail for +# \fIuser\fR@\fBfoo.org\fR via the \fBslow\fR transport to a mail +# exchanger for \fBfoo.org\fR. The \fBslow\fR transport could be +# something that runs at most one delivery process at a time: +# +# .ti +5 +# \fBfoo.org slow:\fR +# +# When no \fItransport\fR is specified, the default transport is +# used, as specified via the \fBdefault_transport\fR configuration +# parameter. The following sends all mail for \fBfoo.org\fR and its +# subdomains to host \fBgateway.foo.org\fR: +# +# .ti +5 +# \fBfoo.org :[gateway.foo.org]\fR +# .ti +5 +# \fB\&.foo.org :[gateway.foo.org]\fR +# +# In the above example, the [] are used to suppress MX lookups. +# The result would likely point to your local machine. +# +# In the case of delivery via SMTP, one may specify +# \fIhostname\fR:\fIservice\fR instead of just a host: +# +# .ti +5 +# \fBfoo.org smtp:bar.org:2025\fR +# +# This directs mail for \fIuser\fR@\fBfoo.org\fR to host \fBbar.org\fR +# port \fB2025\fR. Instead of a numerical port a symbolic name may be +# used. Specify [] around the destination in order to disable MX lookups. +# CONFIGURATION PARAMETERS +# .ad +# .fi +# The following \fBmain.cf\fR parameters are especially relevant to +# this topic. See the Postfix \fBmain.cf\fR file for syntax details +# and for default values. Use the \fBpostfix reload\fR command after +# a configuration change. +# .IP \fBtransport_maps\fR +# List of transport lookup tables. +# .PP +# Other parameters of interest: +# .IP \fBdefault_transport\fR +# The transport to use when no transport is explicitly specified. +# .IP \fBrelayhost\fR +# The default host to send to when no transport table entry matches. +# SEE ALSO +# postmap(1) create mapping table +# trivial-rewrite(8) rewrite and resolve addresses +# 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 +#-- diff --git a/postfix/conf/virtual b/postfix/conf/virtual new file mode 100644 index 000000000..a0e6dda49 --- /dev/null +++ b/postfix/conf/virtual @@ -0,0 +1,102 @@ +#++ +# NAME +# virtual 5 +# SUMMARY +# format of Postfix virtual table +# SYNOPSIS +# \fBpostmap /etc/postfix/virtual\fR +# DESCRIPTION +# The optional \fBvirtual\fR table specifies redirections for local +# and non-local recipients or domains. The redirections are used by +# the \fBcleanup\fR(8) daemon. The redirections are recursive. +# +# The \fBvirtual\fR redirection is applied only to the recipient +# envelope address, and does not affect message headers. +# Think Sendmail rule set \fBS0\fR, if you like. Use \fBcanonical\fR(5) +# mapping to rewrite header and envelope addresses in general. +# +# The file serves as input to the \fBpostmap\fR(1) command. The +# result, an indexed file in \fBdbm\fR or \fBdb\fR format, +# is used for fast searching by the mail system. After an update +# it may take a minute or so before the change becomes visible. +# Issue a \fBpostfix reload\fR command to eliminate the delay. +# +# Typical support for a virtual domain looks like the following: +# +# .in +4 +# .nf +# \fIvirtual.domain anything\fR (right-hand content does not matter) +# \fIuser1@virtual.domain address1\fR +# \fIuser2@virtual.domain address2, address3\fR +# .fi +# .in -4 +# +# With this, the SMTP server accepts mail for \fIvirtual.domain\fR +# (provided that the \fBrelay_domains\fR parameter includes +# $\fBvirtual_maps\fR), and mail for \fIunknown\fR@\fIvirtual.domain\fR +# is bounced as undeliverable. +# +# The format of the virtual table is as follows, mappings being +# tried in the order as listed in this manual page: +# .IP "blanks and comments" +# Blank lines are ignored, as are lines beginning with `#'. +# .IP "\fIuser\fR@\fIdomain address, address, ...\fR" +# Mail for \fIuser\fR@\fIdomain\fR is redirected to \fIaddress\fR. +# This form has the highest precedence. +# .IP "\fIuser address, address, ...\fR" +# Mail for \fIuser\fR@\fIsite\fR is redirected to \fIaddress\fR when +# \fIsite\fR is equal to $\fBmyorigin\fR, when \fIsite\fR is listed in +# $\fRmydestination\fR, or when it is listed in $\fIinet_interfaces\fR. +# .sp +# This functionality overlaps with functionality of the local +# \fIalias\fR(5) database. The difference is that \fBvirtual\fR +# mapping can be applied to non-local addresses. +# .IP "@\fIdomain address, address, ...\fR" +# Mail for any user in \fIdomain\fR is redirected to \fIaddress\fR. +# This form has the lowest precedence. +# .PP +# In all the above forms, when \fIaddress\fR has the form +# @\fIotherdomain\fR, the result is the same user in \fIotherdomain\fR. +# This works for the first address in the expansion only. +# ADDRESS EXTENSION +# .fi +# .ad +# When the search fails, and the address localpart contains the +# optional recipient delimiter (e.g., \fIuser+foo\fR@\fIdomain\fR), +# the search is repeated for the unextended address (e.g. +# \fIuser\fR@\fIdomain\fR), and the unmatched address extension is +# propagated to the result of expansion. The matching order is: +# \fIuser+foo\fR@\fIdomain\fR, \fIuser\fR@\fIdomain\fR, +# \fIuser+foo\fR, \fIuser\fR, and @\fIdomain\fR. +# BUGS +# The table format does not understand quoting conventions. +# CONFIGURATION PARAMETERS +# .ad +# .fi +# The following \fBmain.cf\fR parameters are especially relevant to +# this topic. See the Postfix \fBmain.cf\fR file for syntax details +# and for default values. Use the \fBpostfix reload\fR command after +# a configuration change. +# .IP \fBvirtual_maps\fR +# List of virtual mapping tables. +# .PP +# Other parameters of interest: +# .IP \fBinet_interfaces\fR +# The network interface addresses that this system receives mail on. +# .IP \fBmydestination\fR +# List of domains that this mail system considers local. +# .IP \fBmyorigin\fR +# The domain that is appended to locally-posted mail. +# SEE ALSO +# cleanup(8) canonicalize and enqueue mail +# postmap(1) create mapping table +# LICENSE +# .ad +# .fi +# The Secure Mailer license must be distributed with this software. +# AUTHOR(S) +# Wietse Venema +# IBM T.J. Watson Research +# P.O. Box 704 +# Yorktown Heights, NY 10598, USA +#-- diff --git a/postfix/dns/.indent.pro b/postfix/dns/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/dns/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/dns/.printfck b/postfix/dns/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/dns/.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/dns/Makefile.in b/postfix/dns/Makefile.in new file mode 100644 index 000000000..f44595382 --- /dev/null +++ b/postfix/dns/Makefile.in @@ -0,0 +1,103 @@ +SHELL = /bin/sh +SRCS = dns_lookup.c dns_rr.c dns_strerror.c dns_strtype.c +OBJS = dns_lookup.o dns_rr.o dns_strerror.o dns_strtype.o +HDRS = dns.h +TESTSRC = test_dns_lookup.c test_alias_token.c +WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ + -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ + -Wunused +DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) +INCL = +LIB = libdns.a +TESTPROG= test_dns_lookup +LIBS = ../lib/libutil.a +LIB_DIR = ../lib +INC_DIR = ../include + +.c.o:; $(CC) $(CFLAGS) -c $*.c + +all: $(LIB) + +Makefile: Makefile.in + (set -e; echo "# DO NOT EDIT"; $(OPTS) sh ../makedefs; cat $?) >$@ + +test: $(TESTPROG) + +$(LIB): $(OBJS) + $(AR) $(ARFL) $(LIB) $? + $(RANLIB) $(LIB) + +$(LIB_DIR)/$(LIB): $(LIB) + cp $(LIB) $(LIB_DIR) + $(RANLIB) $(LIB_DIR)/$(LIB) + +update: $(LIB_DIR)/$(LIB) $(HDRS) + -for i in $(HDRS); \ + do \ + cmp -s $$i $(INC_DIR)/$$i 2>/dev/null || cp $$i $(INC_DIR); \ + done + cd $(INC_DIR); chmod 644 $(HDRS) + +test_dns_lookup: test_dns_lookup.c $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + +printfck: $(OBJS) $(PROG) + rm -rf printfck + mkdir printfck + cp *.h 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 $(LIB) *core $(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' +dns_lookup.o: dns_lookup.c +dns_lookup.o: ../include/sys_defs.h +dns_lookup.o: ../include/mymalloc.h +dns_lookup.o: ../include/vstring.h +dns_lookup.o: ../include/vbuf.h +dns_lookup.o: ../include/msg.h +dns_lookup.o: ../include/valid_hostname.h +dns_lookup.o: dns.h +dns_rr.o: dns_rr.c +dns_rr.o: ../include/sys_defs.h +dns_rr.o: ../include/msg.h +dns_rr.o: ../include/mymalloc.h +dns_rr.o: dns.h +dns_rr.o: ../include/vstring.h +dns_rr.o: ../include/vbuf.h +dns_strerror.o: dns_strerror.c +dns_strerror.o: ../include/sys_defs.h +dns_strerror.o: ../include/vstring.h +dns_strerror.o: ../include/vbuf.h +dns_strerror.o: dns.h +dns_strtype.o: dns_strtype.c +dns_strtype.o: ../include/sys_defs.h +dns_strtype.o: ../include/vstring.h +dns_strtype.o: ../include/vbuf.h +dns_strtype.o: dns.h +test_dns_lookup.o: test_dns_lookup.c +test_dns_lookup.o: ../include/sys_defs.h +test_dns_lookup.o: ../include/vstring.h +test_dns_lookup.o: ../include/vbuf.h +test_dns_lookup.o: ../include/msg.h +test_dns_lookup.o: ../include/msg_vstream.h +test_dns_lookup.o: ../include/vstream.h +test_dns_lookup.o: dns.h diff --git a/postfix/dns/dns.h b/postfix/dns/dns.h new file mode 100644 index 000000000..f2cf03366 --- /dev/null +++ b/postfix/dns/dns.h @@ -0,0 +1,135 @@ +#ifndef _DNS_H_INCLUDED_ +#define _DNS_H_INCLUDED_ + +/*++ +/* NAME +/* dns 3h +/* SUMMARY +/* domain name service lookup +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include +#include +#ifdef RESOLVE_H_NEEDS_STDIO_H +#include +#endif +#include + + /* + * Name server compatibility. These undocumented macros appear in the file + * , but since they are undocumented we should not count on + * their presence, and so they are included here just in case. + */ +#ifndef GETSHORT + +#define GETSHORT(s, cp) { \ + unsigned char *t_cp = (u_char *)(cp); \ + (s) = ((unsigned)t_cp[0] << 8) \ + | ((unsigned)t_cp[1]) \ + ; \ + (cp) += 2; \ +} + +#define GETLONG(l, cp) { \ + unsigned char *t_cp = (u_char *)(cp); \ + (l) = ((unsigned)t_cp[0] << 24) \ + | ((unsigned)t_cp[1] << 16) \ + | ((unsigned)t_cp[2] << 8) \ + | ((unsigned)t_cp[3]) \ + ; \ + (cp) += 4; \ +} + +#endif + + /* + * Utility library. + */ +#include + + /* + * Structure for fixed resource record data. + */ +typedef struct DNS_FIXED { + unsigned short type; /* T_A, T_CNAME, etc. */ + unsigned short class; /* T_A, T_CNAME, etc. */ + unsigned int ttl; /* always */ + unsigned length; /* record length */ +} DNS_FIXED; + + /* + * Structure of a DNS resource record after expansion. The components are + * named after the things one can expect to find in a DNS resource record. + */ +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 int ttl; /* always */ + unsigned short pref; /* T_MX only */ + struct DNS_RR *next; /* linkage */ + unsigned data_len; /* actual data size */ + char data[1]; /* actually a bunch of data */ +} DNS_RR; + + /* + * dns_strerror.c + */ +extern const char *dns_strerror(unsigned); + + /* + * dns_strtype.c + */ +extern const char *dns_strtype(unsigned); +extern unsigned dns_type(const char *); + + /* + * dns_rr.c + */ +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_append(DNS_RR *, DNS_RR *); +extern DNS_RR *dns_rr_sort(DNS_RR *, int (*) (DNS_RR *, DNS_RR *)); + + /* + * dns_lookup.c + */ +extern int dns_lookup(const char *, unsigned, unsigned, DNS_RR **, + VSTRING *, VSTRING *); +extern int dns_lookup_types(const char *, unsigned, DNS_RR **, + VSTRING *, VSTRING *,...); + + /* + * Status codes. Failures must have negative codes so they will not collide + * with valid counts of answer records etc. + */ +#define DNS_FAIL (-4) /* query failed, don't retry */ +#define DNS_NOTFOUND (-3) /* query ok, data not found */ +#define DNS_RETRY (-2) /* query failed, try again */ +#define DNS_RECURSE (-1) /* recursion needed */ +#define DNS_OK 0 /* query succeeded */ + + /* + * How long can a DNS name be? + */ +#define DNS_NAME_LEN 1024 + +/* 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/dns/dns_lookup.c b/postfix/dns/dns_lookup.c new file mode 100644 index 000000000..865e6c640 --- /dev/null +++ b/postfix/dns/dns_lookup.c @@ -0,0 +1,475 @@ +/*++ +/* NAME +/* dns_lookup 3 +/* SUMMARY +/* domain name service lookup +/* SYNOPSIS +/* #include +/* +/* int dns_lookup(name, type, flags, list, fqdn, why) +/* const char *name; +/* unsigned type; +/* unsigned flags; +/* DNS_RR **list; +/* VSTRING *fqdn; +/* VSTRING *why; +/* +/* int dns_lookup_types(name, flags, list, fqdn, why, type, ...) +/* const char *name; +/* unsigned flags; +/* DNS_RR **list; +/* VSTRING *fqdn; +/* VSTRING *why; +/* unsigned type; +/* DESCRIPTION +/* dns_lookup() looks up DNS resource records. When requested to +/* look up data other than type CNAME, it will follow a limited +/* number of CNAME indirections. All result names (including +/* null terminator) will fit a buffer of size DNS_NAME_LEN. +/* All name results are validated by \fIvalid_hostname\fR(); +/* an invalid name is reported as a transient error. +/* +/* dns_lookup_types() allows the user to specify a null-terminated +/* list of resource types. This function calls dns_lookup() for each +/* listed type in the specified order, until the list is exhausted or +/* until the search result becomes not equal to DNS_NOTFOUND. +/* INPUTS +/* .ad +/* .fi +/* .IP name +/* The name to be looked up in the domain name system. +/* .IP type +/* The resource record type to be looked up (T_A, T_MX etc.). +/* .IP flags +/* A bitwise OR of: +/* .RS +/* .IP RES_DEBUG +/* Print debugging information. +/* .IP RES_DNSRCH +/* Search local domain and parent domains. +/* .IP RES_DEFNAMES +/* Append local domain to unqualified names. +/* .RE +/* OUTPUTS +/* .ad +/* .fi +/* .IP list +/* A null pointer, or a pointer to a variable that receives a +/* list of requested resource records. +/* .IP fqdn +/* A null pointer, or storage for the fully-qualified domain +/* name found for \fIname\fR. +/* .IP why +/* A null pointer, or storage for the reason for failure. +/* DIAGNOSTICS +/* dns_lookup() returns one of the following codes and sets the +/* \fIwhy\fR argument accordingly: +/* .IP DNS_OK +/* The DNS query succeeded. +/* .IP DNS_NOTFOUND +/* The DNS query succeeded; the requested information was not found. +/* .IP DNS_RETRY +/* The query failed; the problem is transient. +/* .IP DNS_FAIL +/* The query failed. +/* BUGS +/* dns_lookup() implements a subset of all possible resource types: +/* CNAME, MX, A, and some records with similar formatting requirements. +/* It is unwise to specify the T_ANY wildcard resource type. +/* +/* It takes a surprising amount of code to accomplish what appears +/* to be a simple task. Later versions of the mail system may implement +/* their own DNS client software. +/* SEE ALSO +/* dns_rr(3) resource record memory and list management +/* 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 /* BSDI stdarg.h uses abort() */ +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include + +/* DNS library. */ + +#include "dns.h" + +/* Local stuff. */ + + /* + * Structure to keep track of things while decoding a name server reply. + */ +#define DNS_REPLY_SIZE 4096 /* in case we're using TCP */ + +typedef struct DNS_REPLY { + unsigned char buf[DNS_REPLY_SIZE]; /* raw reply data */ + int query_count; /* number of queries */ + int answer_count; /* number of answers */ + unsigned char *query_start; /* start of query data */ + unsigned char *answer_start; /* start of answer data */ + unsigned char *end; /* first byte past reply */ +} DNS_REPLY; + +#define INET_ADDR_LEN 4 /* XXX */ + +/* dns_query - query name server and pre-parse the reply */ + +static int dns_query(const char *name, int type, int flags, + DNS_REPLY *reply, VSTRING *why) +{ + HEADER *reply_header; + int len; + + /* + * Initialize the name service. + */ + if ((_res.options & RES_INIT) == 0 && res_init() < 0) { + if (why) + vstring_strcpy(why, "Name service initialization failure"); + return (DNS_FAIL); + } + + /* + * Set search options: debugging, parent domain search, append local + * domain. Do not allow the user to control other features. + */ +#define USER_FLAGS (RES_DEBUG | RES_DNSRCH | RES_DEFNAMES) + + if ((flags & USER_FLAGS) != flags) + msg_panic("dns_query: bad flags: %d", flags); + _res.options &= ~(USER_FLAGS); + _res.options |= flags; + + /* + * Perform the lookup. Claim that the information cannot be found if and + * only if the name server told us so. + */ + len = res_search((char *) name, C_IN, type, reply->buf, sizeof(reply->buf)); + if (len < 0) { + if (why) + vstring_sprintf(why, "Name service error for domain %s: %s", + name, dns_strerror(h_errno)); + if (msg_verbose) + msg_info("dns_query: %s (%s): %s", + name, dns_strtype(type), dns_strerror(h_errno)); + switch (h_errno) { + case NO_RECOVERY: + return (DNS_FAIL); + case HOST_NOT_FOUND: + case NO_DATA: + return (DNS_NOTFOUND); + default: + return (DNS_RETRY); + } + } + if (msg_verbose) + msg_info("dns_query: %s (%s): OK", name, dns_strtype(type)); + + /* + * Initialize the reply structure. Some structure members are filled on + * the fly while the reply is being parsed. + */ + if ((reply->end = reply->buf + len) > reply->buf + sizeof(reply->buf)) + reply->end = reply->buf + sizeof(reply->buf); + reply_header = (HEADER *) reply->buf; + reply->query_start = reply->buf + sizeof(HEADER); + reply->answer_start = 0; + reply->query_count = ntohs(reply_header->qdcount); + reply->answer_count = ntohs(reply_header->ancount); + return (DNS_OK); +} + +/* dns_skip_query - skip query data in name server reply */ + +static int dns_skip_query(DNS_REPLY *reply) +{ + int query_count = reply->query_count; + unsigned char *pos = reply->query_start; + char temp[DNS_NAME_LEN]; + int len; + + /* + * For each query, skip over the domain name and over the fixed query + * data. + */ + while (query_count-- > 0) { + if (pos >= reply->end) + return DNS_RETRY; + len = dn_expand(reply->buf, reply->end, pos, temp, DNS_NAME_LEN); + if (len < 0) + return (DNS_RETRY); + pos += len + QFIXEDSZ; + } + reply->answer_start = pos; + return (DNS_OK); +} + +/* dns_get_fixed - extract fixed data from resource record */ + +static int dns_get_fixed(unsigned char *pos, DNS_FIXED *fixed) +{ + GETSHORT(fixed->type, pos); + GETSHORT(fixed->class, pos); + GETLONG(fixed->ttl, pos); + GETSHORT(fixed->length, pos); + + if (fixed->class != C_IN) { + msg_warn("dns_get_fixed: bad class: %u", fixed->class); + return (DNS_RETRY); + } + return (DNS_OK); +} + +/* dns_get_rr - extract resource record from name server reply */ + +static DNS_RR *dns_get_rr(DNS_REPLY *reply, unsigned char *pos, + char *rr_name, DNS_FIXED *fixed) +{ + char temp[DNS_NAME_LEN]; + int data_len = fixed->length; + unsigned pref = 0; + + if (pos + fixed->length > reply->end) + return (0); + + switch (fixed->type) { + default: + msg_panic("dns_get_rr: don't know how to extract resource type %s", + dns_strtype(fixed->type)); + case T_CNAME: + case T_MB: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0) + return (0); + if (!valid_hostname(temp)) + return (0); + data_len = strlen(temp) + 1; + break; + case T_MX: + GETSHORT(pref, pos); + if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0) + return (0); + if (!valid_hostname(temp)) + return (0); + data_len = strlen(temp) + 1; + break; + case T_A: + if (fixed->length != INET_ADDR_LEN) { + msg_warn("extract_answer: bad address length: %d", fixed->length); + return (0); + } + if (fixed->length > sizeof(temp)) + msg_panic("dns_get_rr: length %d > DNS_NAME_LEN", + fixed->length); + memcpy(temp, pos, fixed->length); + data_len = fixed->length; + break; + } + return (dns_rr_create(rr_name, fixed, pref, temp, data_len)); +} + +/* dns_get_alias - extract CNAME from name server reply */ + +static int dns_get_alias(DNS_REPLY *reply, unsigned char *pos, + DNS_FIXED *fixed, char *cname, int c_len) +{ + if (fixed->type != T_CNAME) + msg_panic("dns_get_alias: bad type %s", dns_strtype(fixed->type)); + if (dn_expand(reply->buf, reply->end, pos, cname, c_len) < 0) + return (DNS_RETRY); + if (!valid_hostname(cname)) + return (DNS_RETRY); + return (DNS_OK); +} + +/* dns_get_answer - extract answers from name server reply */ + +static int dns_get_answer(DNS_REPLY *reply, int type, + DNS_RR **rrlist, VSTRING *fqdn, char *cname, int c_len) +{ + char rr_name[DNS_NAME_LEN]; + unsigned char *pos; + int answer_count = reply->answer_count; + int len; + DNS_FIXED fixed; + DNS_RR *rr; + int resource_found = 0; + int cname_found = 0; + + /* + * Initialize. Skip over the name server query if we haven't yet. + */ + if (reply->answer_start == 0) + if (dns_skip_query(reply) < 0) + return (DNS_RETRY); + pos = reply->answer_start; + if (rrlist) + *rrlist = 0; + + /* + * Either this, or use a GOTO for emergency exits. The purpose is to + * prevent incomplete answers from being passed back to the caller. + */ +#define CORRUPT { \ + if (rrlist && *rrlist) { \ + dns_rr_free(*rrlist); \ + *rrlist = 0; \ + } \ + return (DNS_RETRY); \ + } + + /* + * Iterate over all answers. + */ + while (answer_count-- > 0) { + + /* + * Optionally extract the fully-qualified domain name. + */ + if (pos >= reply->end) + CORRUPT; + len = dn_expand(reply->buf, reply->end, pos, rr_name, DNS_NAME_LEN); + if (len < 0) + CORRUPT; + if (!valid_hostname(rr_name)) + CORRUPT; + if (fqdn) + vstring_strcpy(fqdn, rr_name); + pos += len; + + /* + * Extract the fixed reply data: type, class, ttl, length. + */ + if (pos + RRFIXEDSZ > reply->end) + CORRUPT; + if (dns_get_fixed(pos, &fixed) != DNS_OK) + CORRUPT; + if (msg_verbose) + msg_info("dns_get_answer: type %s for %s", + dns_strtype(fixed.type), rr_name); + pos += RRFIXEDSZ; + + /* + * Optionally extract the requested resource or CNAME data. + */ + if (pos + fixed.length > reply->end) + CORRUPT; + if (type == fixed.type || type == T_ANY) { /* requested type */ + resource_found++; + if (rrlist) { + if ((rr = dns_get_rr(reply, pos, rr_name, &fixed)) == 0) + CORRUPT; + *rrlist = dns_rr_append(*rrlist, rr); + } + } else if (fixed.type == T_CNAME) { /* cname resource */ + cname_found++; + if (cname && c_len > 0) + if (dns_get_alias(reply, pos, &fixed, cname, c_len) != DNS_OK) + CORRUPT; + } + pos += fixed.length; + } + + /* + * See what answer we came up with. Report success when the requested + * information was found. Otherwise, when a CNAME was found, report that + * more recursion is needed. Otherwise report failure. + */ + if (resource_found) + return (DNS_OK); + if (cname_found) + return (DNS_RECURSE); + return (DNS_NOTFOUND); +} + +/* dns_lookup - DNS lookup user interface */ + +int dns_lookup(const char *name, unsigned type, unsigned flags, + DNS_RR **rrlist, VSTRING *fqdn, VSTRING *why) +{ + char cname[DNS_NAME_LEN]; + int c_len = sizeof(cname); + DNS_REPLY reply; + int count; + int status; + + /* + * Perform the lookup. Follow CNAME chains, but only up to a + * pre-determined maximum. + */ + for (count = 0; count < 10; count++) { + + /* + * Perform the DNS lookup, and pre-parse the name server reply. + */ + if ((status = dns_query(name, type, flags, &reply, why)) != DNS_OK) + return (status); + + /* + * Extract resource records of the requested type. Pick up CNAME + * information just in case the requested data is not found. + */ + status = dns_get_answer(&reply, type, rrlist, fqdn, cname, c_len); + switch (status) { + default: + if (why) + vstring_sprintf(why, "%s: Malformed name server reply", name); + case DNS_NOTFOUND: + case DNS_OK: + return (status); + case DNS_RECURSE: + if (msg_verbose) + msg_info("dns_lookup: %s aliased to %s", name, cname); + name = cname; + } + } + if (why) + vstring_sprintf(why, "Name server loop for %s", name); + msg_warn("dns_lookup: Name server loop for %s", name); + return (DNS_NOTFOUND); +} + +/* dns_lookup_types - DNS lookup interface with multiple types */ + +int dns_lookup_types(const char *name, unsigned flags, DNS_RR **rrlist, + VSTRING *fqdn, VSTRING *why,...) +{ + va_list ap; + unsigned type; + int status = DNS_NOTFOUND; + int soft_err = 0; + + va_start(ap, why); + while ((type = va_arg(ap, unsigned)) != 0) { + if (msg_verbose) + msg_info("lookup %s type %d flags %d", name, type, flags); + status = dns_lookup(name, type, flags, rrlist, fqdn, why); + if (status == DNS_OK) + break; + if (status == DNS_RETRY) + soft_err = 1; + } + va_end(ap); + return ((status == DNS_OK || soft_err == 0) ? status : DNS_RETRY); +} diff --git a/postfix/dns/dns_rr.c b/postfix/dns/dns_rr.c new file mode 100644 index 000000000..e6bb5f7a5 --- /dev/null +++ b/postfix/dns/dns_rr.c @@ -0,0 +1,173 @@ +/*++ +/* NAME +/* dns_rr 3 +/* SUMMARY +/* resource record memory and list management +/* SYNOPSIS +/* #include +/* +/* DNS_RR *dns_rr_create(name, fixed, preference, data, data_len) +/* const char *name; +/* DNS_FIXED *fixed; +/* unsigned preference; +/* const char *data; +/* unsigned len; +/* +/* void dns_rr_free(list) +/* DNS_RR *list; +/* +/* DNS_RR *dns_rr_append(list, record) +/* DNS_RR *list; +/* DNS_RR *record; +/* +/* DNS_RR *dns_rr_sort(list, compar) +/* DNS_RR *list +/* int (*compar)(DNS_RR *, DNS_RR *); +/* DESCRIPTION +/* The routines in this module maintain memory for DNS resource record +/* information, and maintain lists of DNS resource records. +/* +/* dns_rr_create() creates and initializes one resource record. +/* The \fIname\fR record specifies the record name. +/* The \fIfixed\fR argument specifies generic resource record +/* information such as resource type and time to live; +/* \fIpreference\fR is used for MX records; \fIdata\fR is a null +/* pointer or specifies optional resource-specific data; +/* \fIdata_len\fR is the amount of resource-specific data. +/* +/* dns_rr_free() releases the resource used by of zero or more +/* resource records. +/* +/* dns_rr_append() appends a resource record to a (list of) resource +/* record(s). +/* +/* dns_rr_sort() sorts a list of resource records into ascending +/* order according to a user-specified criterion. The result is the +/* sorted list. +/* 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 + +/* Utility library. */ + +#include +#include + +/* DNS library. */ + +#include "dns.h" + +/* dns_rr_create - fill in resource record structure */ + +DNS_RR *dns_rr_create(const char *name, DNS_FIXED *fixed, unsigned pref, + const char *data, unsigned data_len) +{ + DNS_RR *rr; + + rr = (DNS_RR *) mymalloc(sizeof(*rr) + data_len - 1); + rr->name = mystrdup(name); + rr->type = fixed->type; + rr->class = fixed->class; + rr->ttl = fixed->ttl; + rr->pref = pref; + if (data && data_len > 0) + memcpy(rr->data, data, data_len); + rr->data_len = data_len; + rr->next = 0; + return (rr); +} + +/* dns_rr_free - destroy resource record structure */ + +void dns_rr_free(DNS_RR *rr) +{ + if (rr) { + if (rr->next) + dns_rr_free(rr->next); + myfree(rr->name); + myfree((char *) rr); + } +} + +/* dns_rr_append - append resource record to list */ + +DNS_RR *dns_rr_append(DNS_RR *list, DNS_RR *rr) +{ + if (list == 0) { + list = rr; + } else { + list->next = dns_rr_append(list->next, rr); + } + return (list); +} + +/* dns_rr_sort_callback - glue function */ + +static int (*dns_rr_sort_user) (DNS_RR *, DNS_RR *); + +static int dns_rr_sort_callback(const void *a, const void *b) +{ + DNS_RR *aa = *(DNS_RR **) a; + DNS_RR *bb = *(DNS_RR **) b; + + return (dns_rr_sort_user(aa, bb)); +} + +/* dns_rr_sort - sort resource record list */ + +DNS_RR *dns_rr_sort(DNS_RR *list, int (*compar) (DNS_RR *, DNS_RR *)) +{ + int (*saved_user) (DNS_RR *, DNS_RR *); + DNS_RR **rr_array; + DNS_RR *rr; + int len; + int i; + + /* + * Save state and initialize. + */ + saved_user = dns_rr_sort_user; + dns_rr_sort_user = compar; + + /* + * Build linear array with pointers to each list element. + */ + for (len = 0, rr = list; rr != 0; len++, rr = rr->next) + /* void */ ; + rr_array = (DNS_RR **) mymalloc(len * sizeof(*rr_array)); + for (len = 0, rr = list; rr != 0; len++, rr = rr->next) + rr_array[len] = rr; + + /* + * Sort by user-specified criterion. + */ + qsort((char *) rr_array, len, sizeof(*rr_array), dns_rr_sort_callback); + + /* + * Fix the links. + */ + for (i = 0; i < len - 1; i++) + rr_array[i]->next = rr_array[i + 1]; + rr_array[i]->next = 0; + list = rr_array[0]; + + /* + * Cleanup. + */ + myfree((char *) rr_array); + dns_rr_sort_user = saved_user; + return (list); +} diff --git a/postfix/dns/dns_strerror.c b/postfix/dns/dns_strerror.c new file mode 100644 index 000000000..9e56d3b86 --- /dev/null +++ b/postfix/dns/dns_strerror.c @@ -0,0 +1,69 @@ +/*++ +/* NAME +/* dns_strerror 3 +/* SUMMARY +/* name service lookup error code to string +/* SYNOPSIS +/* #include +/* +/* const char *dns_strerror(code) +/* int code; +/* DESCRIPTION +/* dns_strerror() maps a name service lookup error to printable string. +/* The result is for read-only purposes, and unknown codes share a +/* common string buffer. +/* 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 + +/* DNS library. */ + +#include "dns.h" + + /* + * Mapping from error code to printable string. The herror() routine does + * something similar, but has output only to the stderr stream. + */ +struct dns_error_map { + unsigned error; + const char *text; +}; + +static struct dns_error_map dns_error_map[] = { + HOST_NOT_FOUND, "Host not found", + TRY_AGAIN, "Host not found, try again", + NO_RECOVERY, "Non-recoverable error", + NO_DATA, "Host found but no data record of requested type", +}; + +/* dns_strerror - map resolver error code to printable string */ + +const char *dns_strerror(unsigned error) +{ + static VSTRING *unknown = 0; + unsigned i; + + for (i = 0; i < sizeof(dns_error_map) / sizeof(dns_error_map[0]); i++) + if (dns_error_map[i].error == error) + return (dns_error_map[i].text); + if (unknown == 0) + unknown = vstring_alloc(sizeof("Unknown error XXXXXX")); + vstring_sprintf(unknown, "Unknown error %u", error); + return (vstring_str(unknown)); +} diff --git a/postfix/dns/dns_strtype.c b/postfix/dns/dns_strtype.c new file mode 100644 index 000000000..8e78de463 --- /dev/null +++ b/postfix/dns/dns_strtype.c @@ -0,0 +1,200 @@ +/*++ +/* NAME +/* dns_strtype 3 +/* SUMMARY +/* name service lookup type codes and printable forms +/* SYNOPSIS +/* #include +/* +/* const char *dns_strtype(code) +/* int code; +/* +/* int dns_type(strval) +/* const char *strval; +/* DESCRIPTION +/* dns_strtype() maps a name service lookup type to printable string. +/* The result is for read-only purposes, and unknown codes share a +/* common string buffer. +/* +/* dns_type() converts a name service lookup string value to a numeric +/* code. A null result means the code was not found. The input can be +/* in lower case, upper case or mixed case. +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include + +/* DNS library. */ + +#include "dns.h" + + /* + * Mapping from type code to printable string. Some names are possibly not + * defined on every platform, so I have #ifdef-ed them all just to be safe. + */ +struct dns_type_map { + unsigned type; + const char *text; +}; + +static struct dns_type_map dns_type_map[] = { +#ifdef T_A + T_A, "A", +#endif +#ifdef T_NS + T_NS, "NS", +#endif +#ifdef T_MD + T_MD, "MD", +#endif +#ifdef T_MF + T_MF, "MF", +#endif +#ifdef T_CNAME + T_CNAME, "CNAME", +#endif +#ifdef T_SOA + T_SOA, "SOA", +#endif +#ifdef T_MB + T_MB, "MB", +#endif +#ifdef T_MG + T_MG, "MG", +#endif +#ifdef T_MR + T_MR, "MR", +#endif +#ifdef T_NULL + T_NULL, "NULL", +#endif +#ifdef T_WKS + T_WKS, "WKS", +#endif +#ifdef T_PTR + T_PTR, "PTR", +#endif +#ifdef T_HINFO + T_HINFO, "HINFO", +#endif +#ifdef T_MINFO + T_MINFO, "MINFO", +#endif +#ifdef T_MX + T_MX, "MX", +#endif +#ifdef T_TXT + T_TXT, "TXT", +#endif +#ifdef T_RP + T_RP, "RP", +#endif +#ifdef T_AFSDB + T_AFSDB, "AFSDB", +#endif +#ifdef T_X25 + T_X25, "X25", +#endif +#ifdef T_ISDN + T_ISDN, "ISDN", +#endif +#ifdef T_RT + T_RT, "RT", +#endif +#ifdef T_NSAP + T_NSAP, "NSAP", +#endif +#ifdef T_NSAP_PTR + T_NSAP_PTR, "NSAP_PTR", +#endif +#ifdef T_SIG + T_SIG, "SIG", +#endif +#ifdef T_KEY + T_KEY, "KEY", +#endif +#ifdef T_PX + T_PX, "PX", +#endif +#ifdef T_GPOS + T_GPOS, "GPOS", +#endif +#ifdef T_AAAA + T_AAAA, "AAAA", +#endif +#ifdef T_LOC + T_LOC, "LOC", +#endif +#ifdef T_UINFO + T_UINFO, "UINFO", +#endif +#ifdef T_UID + T_UID, "UID", +#endif +#ifdef T_GID + T_GID, "GID", +#endif +#ifdef T_UNSPEC + T_UNSPEC, "UNSPEC", +#endif +#ifdef T_AXFR + T_AXFR, "AXFR", +#endif +#ifdef T_MAILB + T_MAILB, "MAILB", +#endif +#ifdef T_MAILA + T_MAILA, "MAILA", +#endif +#ifdef T_ANY + T_ANY, "ANY", +#endif +}; + +/* dns_strtype - translate DNS query type to string */ + +const char *dns_strtype(unsigned type) +{ + static VSTRING *unknown = 0; + unsigned i; + + for (i = 0; i < sizeof(dns_type_map) / sizeof(dns_type_map[0]); i++) + if (dns_type_map[i].type == type) + return (dns_type_map[i].text); + if (unknown == 0) + unknown = vstring_alloc(sizeof("Unknown type XXXXXX")); + vstring_sprintf(unknown, "Unknown type %u", type); + return (vstring_str(unknown)); +} + +/* dns_type - translate string to DNS query type */ + +unsigned dns_type(const char *text) +{ + unsigned i; + + for (i = 0; i < sizeof(dns_type_map) / sizeof(dns_type_map[0]); i++) + if (strcasecmp(dns_type_map[i].text, text) == 0) + return (dns_type_map[i].type); + return (0); +} + diff --git a/postfix/dns/test_dns_lookup.c b/postfix/dns/test_dns_lookup.c new file mode 100644 index 000000000..5f1a9e63e --- /dev/null +++ b/postfix/dns/test_dns_lookup.c @@ -0,0 +1,95 @@ +/*++ +/* NAME +/* test_dns_lookup 1 +/* SUMMARY +/* DNS lookup test program +/* SYNOPSIS +/* test_dns_lookup query-type domain-name +/* DESCRIPTION +/* test_dns_lookup performs a DNS query of the specified resource +/* type for the specified resource name. +/* 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 + +/* Utility library. */ + +#include +#include +#include + +/* Application-specific. */ + +#include "dns.h" + +static void print_rr(DNS_RR *rr) +{ + struct in_addr addr; + + while (rr) { + printf("%s: ttl: %9d ", rr->name, rr->ttl); + switch (rr->type) { + case T_A: + memcpy((char *) &addr.s_addr, rr->data, sizeof(addr.s_addr)); + printf("%s: %s\n", dns_strtype(rr->type), inet_ntoa(addr)); + break; + case T_CNAME: + case T_MB: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + printf("%s: %s\n", dns_strtype(rr->type), rr->data); + break; + case T_MX: + printf("pref: %d %s: %s\n", + rr->pref, dns_strtype(rr->type), rr->data); + break; + default: + msg_fatal("print_rr: don't know how to print type %s", + dns_strtype(rr->type)); + } + rr = rr->next; + } +} + +int main(int argc, char **argv) +{ + int type; + char *name; + VSTRING *fqdn = vstring_alloc(100); + VSTRING *why = vstring_alloc(100); + DNS_RR *rr; + + msg_vstream_init(argv[0], VSTREAM_ERR); + if (argc != 3) + msg_fatal("usage: %s type name", argv[0]); + if ((type = dns_type(argv[1])) == 0) + msg_fatal("invalid query type: %s", argv[1]); + name = argv[2]; + msg_verbose = 1; + switch (dns_lookup_types(name, RES_DEFNAMES | RES_DEBUG, &rr, fqdn, why, type, 0)) { + default: + msg_fatal("%s", vstring_str(why)); + case DNS_OK: + printf("%s: fqdn: %s\n", name, vstring_str(fqdn)); + print_rr(rr); + } + exit(0); +} diff --git a/postfix/fsstone/.indent.pro b/postfix/fsstone/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/fsstone/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/fsstone/.printfck b/postfix/fsstone/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/fsstone/.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/fsstone/Makefile.in b/postfix/fsstone/Makefile.in new file mode 100644 index 000000000..cba53254e --- /dev/null +++ b/postfix/fsstone/Makefile.in @@ -0,0 +1,62 @@ +SHELL = /bin/sh +SRCS = fsstone.c +OBJS = fsstone.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 = fsstone +INC_DIR = ../include +LIBS = ../lib/libglobal.a ../lib/libutil.a + +.c.o:; $(CC) $(CFLAGS) -c $*.c + +all: $(PROG) + +Makefile: Makefile.in + (set -e; echo "# DO NOT EDIT"; $(OPTS) sh ../makedefs; cat $?) >$@ + +fsstone: fsstone.o $(LIBS) + $(CC) $(CFLAGS) -o $@ fsstone.o $(LIBS) $(SYSLIBS) + +test: $(TESTPROG) + +update: ../bin/fsstone + +../bin/fsstone: fsstone + cp $? $@ + +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' +fsstone.o: fsstone.c +fsstone.o: ../include/sys_defs.h +fsstone.o: ../include/msg.h +fsstone.o: ../include/msg_vstream.h +fsstone.o: ../include/vstream.h +fsstone.o: ../include/vbuf.h diff --git a/postfix/fsstone/fsstone.c b/postfix/fsstone/fsstone.c new file mode 100644 index 000000000..1257417dd --- /dev/null +++ b/postfix/fsstone/fsstone.c @@ -0,0 +1,211 @@ +/*++ +/* NAME +/* fsstone 1 +/* SUMMARY +/* measure directory operation overhead +/* SYNOPSIS +/* \fBfsstone\fR [\fB-c\fR] [\fB-r\fR] \fImsg_count files_per_dir\fR +/* DESCRIPTION +/* The \fBfsstone\fR command measures the cost of creating, renaming +/* and deleting queue files versus appending messages to existing +/* files and truncating them after use. +/* +/* The program simulates the arrival of \fImsg_count\fR short messages, +/* and arranges for at most \fIfiles_per_dir\fR simultaneous files +/* in the same directory. +/* +/* Options: +/* .IP \fB-c\fR +/* Create and delete files. +/* .IP \fB-r\fR +/* Rename files twice (requires \fB-c\fR). +/* DIAGNOSTICS +/* Problems are reported to the standard error stream. +/* BUGS +/* The \fB-r\fR option renames files within the same directory. +/* For a more realistic simulation, the program should rename files +/* between directories, and should also have an option to use +/* hashed directories as implemented with, for example, the +/* \fBdir_forest\fR(3) module. +/* 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 + +/* Utility library. */ + +#include +#include + +/* rename_file - rename a file */ + +static void rename_file(int old, int new) +{ + char new_path[BUFSIZ]; + char old_path[BUFSIZ]; + + sprintf(new_path, "%06d", new); + sprintf(old_path, "%06d", old); + if (rename(old_path, new_path)) + msg_fatal("rename %s to %s: %m", old_path, new_path); +} + +/* make_file - create a little file and use it */ + +static void make_file(int seqno) +{ + char path[BUFSIZ]; + FILE *fp; + int i; + + sprintf(path, "%06d", seqno); + if ((fp = fopen(path, "w")) == 0) + msg_fatal("open %s: %m", path); + for (i = 0; i < 400; i++) + fprintf(fp, "hello"); + if (fsync(fileno(fp))) + msg_fatal("fsync: %m"); + if (fclose(fp)) + msg_fatal("fclose: %m"); + if ((fp = fopen(path, "r")) == 0) + msg_fatal("open %s: %m", path); + while (fgets(path, sizeof(path), fp)) + /* void */ ; + if (fclose(fp)) + msg_fatal("fclose: %m"); +} + +/* use_file - use existing file */ + +static void use_file(int seqno) +{ + char path[BUFSIZ]; + FILE *fp; + int i; + + sprintf(path, "%06d", seqno); + if ((fp = fopen(path, "w")) == 0) + msg_fatal("open %s: %m", path); + for (i = 0; i < 400; i++) + fprintf(fp, "hello"); + if (fsync(fileno(fp))) + msg_fatal("fsync: %m"); + if (fclose(fp)) + msg_fatal("fclose: %m"); + if ((fp = fopen(path, "r+")) == 0) + msg_fatal("open %s: %m", path); + while (fgets(path, sizeof(path), fp)) + /* void */ ; + if (ftruncate(fileno(fp), (off_t) 0)) + msg_fatal("ftruncate: %m");; + if (fclose(fp)) + msg_fatal("fclose: %m"); +} + +/* remove_file - delete specified file */ + +static void remove_file(int seq) +{ + char path[BUFSIZ]; + + sprintf(path, "%06d", seq); + if (remove(path)) + msg_fatal("remove %s: %m", path); +} + +/* remove_silent - delete specified file, silently */ + +static void remove_silent(int seq) +{ + char path[BUFSIZ]; + + sprintf(path, "%06d", seq); + (void) remove(path); +} + +/* usage - explain */ + +static void usage(char *myname) +{ + msg_fatal("usage: %s [-c [-r]] messages directory_entries", myname); +} + +int main(int argc, char **argv) +{ + int op_count; + int max_file; + time_t start; + int do_rename = 0; + int do_create = 0; + int seq; + int ch; + + msg_vstream_init(argv[0], VSTREAM_ERR); + while ((ch = GETOPT(argc, argv, "cr")) != EOF) { + switch (ch) { + case 'c': + do_create++; + break; + case 'r': + do_rename++; + break; + default: + usage(argv[0]); + } + } + + if (argc - optind != 2 || (do_rename && !do_create)) + usage(argv[0]); + if ((op_count = atoi(argv[optind])) <= 0) + usage(argv[0]); + if ((max_file = atoi(argv[optind + 1])) <= 0) + usage(argv[0]); + + /* + * Populate the directory with little files. + */ + for (seq = 0; seq < max_file; seq++) + make_file(seq); + + /* + * Simulate arrival and delivery of mail messages. + */ + start = time((time_t *) 0); + while (op_count > 0) { + seq %= max_file; + if (do_create) { + remove_file(seq); + make_file(seq); + if (do_rename) { + rename_file(seq, seq + max_file); + rename_file(seq + max_file, seq); + } + } else { + use_file(seq); + } + seq++; + op_count--; + } + printf("elapsed time: %ld\n", (long) time((time_t *) 0) - start); + + /* + * Clean up directory fillers. + */ + for (seq = 0; seq < max_file; seq++) + remove_silent(seq); + return (0); +} diff --git a/postfix/global/.indent.pro b/postfix/global/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/global/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/global/.printfck b/postfix/global/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/global/.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/global/Makefile.in b/postfix/global/Makefile.in new file mode 100644 index 000000000..3fe66c8fb --- /dev/null +++ b/postfix/global/Makefile.in @@ -0,0 +1,842 @@ +SHELL = /bin/sh +SRCS = been_here.c bounce.c canon_addr.c clean_env.c cleanup_strerror.c \ + config.c config_bool.c config_int.c config_str.c debug_peer.c \ + debug_process.c defer.c deliver_completed.c deliver_flock.c \ + deliver_request.c domain_list.c dot_lockfile.c file_id.c \ + header_opts.c is_header.c mail_addr.c mail_addr_crunch.c \ + mail_addr_find.c mail_addr_map.c mail_command_read.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 +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 \ + deliver_request.o domain_list.o dot_lockfile.o file_id.o \ + header_opts.o is_header.o mail_addr.o mail_addr_crunch.o \ + mail_addr_find.o mail_addr_map.o mail_command_read.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 +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 \ + 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 +TESTSRC = rec2stream.c stream2rec.c recdump.c +WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ + -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ + -Wunused +DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) +INCL = +LIB = libglobal.a +TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \ + mail_addr_map mail_date maps mynetworks mypwd namadr_list \ + off_cvt quote_822_local rec2stream recdump resolve_clnt \ + resolve_local rewrite_clnt stream2rec string_list tok822_parse + +LIBS = ../lib/libutil.a +LIB_DIR = ../lib +INC_DIR = ../include +MAKES = + +.c.o:; $(CC) $(CFLAGS) -c $*.c + +all: $(LIB) + +Makefile: Makefile.in + (set -e; echo "# DO NOT EDIT"; $(OPTS) sh ../makedefs; cat $?) >$@ + +test: $(TESTPROG) + +$(LIB): $(OBJS) + $(AR) $(ARFL) $(LIB) $? + $(RANLIB) $(LIB) + +$(LIB_DIR)/$(LIB): $(LIB) + cp $(LIB) $(LIB_DIR) + $(RANLIB) $(LIB_DIR)/$(LIB) + +update: $(LIB_DIR)/$(LIB) $(HDRS) + -for i in $(HDRS); \ + do \ + cmp -s $$i $(INC_DIR)/$$i 2>/dev/null || cp $$i $(INC_DIR); \ + done + cd $(INC_DIR); chmod 644 $(HDRS) + +dot_lockfile: $(LIB) $(LIBS) + mv $@.o junk + $(CC) -DTEST $(CFLAGS) -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +tok822_parse: $(LIB) $(LIBS) + mv $@.o junk + $(CC) -DTEST $(CFLAGS) -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +rec2stream: rec2stream.c $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + +stream2rec: stream2rec.c $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + +recdump: recdump.c $(LIB) $(LIBS) + $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + +namadr_list: $(LIB) $(LIBS) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +domain_list: $(LIB) $(LIBS) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +mynetworks: $(LIB) $(LIBS) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +resolve_clnt: $(LIB) $(LIBS) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +rewrite_clnt: $(LIB) $(LIBS) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +quote_822_local: $(LIB) $(LIBS) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +off_cvt: $(LIB) $(LIBS) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +mail_addr_map: $(LIB) $(LIBS) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +mail_addr_find: $(LIB) $(LIBS) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +maps: $(LIB) $(LIBS) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +mypwd: $(LIB) $(LIBS) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +mail_date: $(LIB) $(LIBS) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +resolve_local: $(LIB) $(LIBS) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +mail_addr_crunch: $(LIB) $(LIBS) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +string_list: $(LIB) $(LIBS) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +printfck: $(OBJS) $(PROG) + rm -rf printfck + mkdir printfck + cp *.h 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 $(LIB) *core $(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' +been_here.o: been_here.c +been_here.o: ../include/sys_defs.h +been_here.o: ../include/msg.h +been_here.o: ../include/mymalloc.h +been_here.o: ../include/htable.h +been_here.o: ../include/vstring.h +been_here.o: ../include/vbuf.h +been_here.o: ../include/stringops.h +been_here.o: been_here.h +bounce.o: bounce.c +bounce.o: ../include/sys_defs.h +bounce.o: ../include/msg.h +bounce.o: ../include/vstring.h +bounce.o: ../include/vbuf.h +bounce.o: mail_proto.h +bounce.o: ../include/vstream.h +bounce.o: ../include/iostuff.h +bounce.o: defer.h +bounce.o: bounce.h +canon_addr.o: canon_addr.c +canon_addr.o: ../include/sys_defs.h +canon_addr.o: ../include/vstring.h +canon_addr.o: ../include/vbuf.h +canon_addr.o: ../include/mymalloc.h +canon_addr.o: rewrite_clnt.h +canon_addr.o: canon_addr.h +clean_env.o: clean_env.c +clean_env.o: ../include/sys_defs.h +clean_env.o: ../include/msg.h +clean_env.o: clean_env.h +cleanup_strerror.o: cleanup_strerror.c +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 +config.o: config.c +config.o: ../include/sys_defs.h +config.o: ../include/msg.h +config.o: ../include/mymalloc.h +config.o: ../include/vstream.h +config.o: ../include/vbuf.h +config.o: ../include/vstring.h +config.o: ../include/dict.h +config.o: ../include/safe.h +config.o: ../include/stringops.h +config.o: mail_params.h +config.o: config.h +config_bool.o: config_bool.c +config_bool.o: ../include/sys_defs.h +config_bool.o: ../include/msg.h +config_bool.o: ../include/dict.h +config_bool.o: ../include/vstream.h +config_bool.o: ../include/vbuf.h +config_bool.o: config.h +config_int.o: config_int.c +config_int.o: ../include/sys_defs.h +config_int.o: ../include/msg.h +config_int.o: ../include/mymalloc.h +config_int.o: ../include/dict.h +config_int.o: ../include/vstream.h +config_int.o: ../include/vbuf.h +config_int.o: ../include/stringops.h +config_int.o: config.h +config_str.o: config_str.c +config_str.o: ../include/sys_defs.h +config_str.o: ../include/msg.h +config_str.o: ../include/mymalloc.h +config_str.o: config.h +debug_peer.o: debug_peer.c +debug_peer.o: ../include/sys_defs.h +debug_peer.o: ../include/msg.h +debug_peer.o: mail_params.h +debug_peer.o: namadr_list.h +debug_peer.o: debug_peer.h +debug_process.o: debug_process.c +debug_process.o: ../include/sys_defs.h +debug_process.o: ../include/msg.h +debug_process.o: mail_params.h +debug_process.o: config.h +debug_process.o: debug_process.h +defer.o: defer.c +defer.o: ../include/sys_defs.h +defer.o: ../include/msg.h +defer.o: ../include/vstring.h +defer.o: ../include/vbuf.h +defer.o: mail_queue.h +defer.o: ../include/vstream.h +defer.o: mail_proto.h +defer.o: ../include/iostuff.h +defer.o: bounce.h +defer.o: defer.h +deliver_completed.o: deliver_completed.c +deliver_completed.o: ../include/sys_defs.h +deliver_completed.o: ../include/msg.h +deliver_completed.o: ../include/vstream.h +deliver_completed.o: ../include/vbuf.h +deliver_completed.o: record.h +deliver_completed.o: ../include/vstring.h +deliver_completed.o: rec_type.h +deliver_completed.o: deliver_completed.h +deliver_flock.o: deliver_flock.c +deliver_flock.o: ../include/sys_defs.h +deliver_flock.o: ../include/vstring.h +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_request.o: deliver_request.c +deliver_request.o: ../include/sys_defs.h +deliver_request.o: ../include/msg.h +deliver_request.o: ../include/vstream.h +deliver_request.o: ../include/vbuf.h +deliver_request.o: ../include/vstring.h +deliver_request.o: ../include/mymalloc.h +deliver_request.o: ../include/iostuff.h +deliver_request.o: mail_queue.h +deliver_request.o: mail_proto.h +deliver_request.o: mail_open_ok.h +deliver_request.o: recipient_list.h +deliver_request.o: deliver_request.h +domain_list.o: domain_list.c +domain_list.o: ../include/sys_defs.h +domain_list.o: ../include/match_list.h +domain_list.o: ../include/match_ops.h +domain_list.o: domain_list.h +dot_lockfile.o: dot_lockfile.c +dot_lockfile.o: ../include/sys_defs.h +dot_lockfile.o: ../include/vstring.h +dot_lockfile.o: ../include/vbuf.h +dot_lockfile.o: ../include/stringops.h +dot_lockfile.o: ../include/mymalloc.h +dot_lockfile.o: mail_params.h +dot_lockfile.o: dot_lockfile.h +file_id.o: file_id.c +file_id.o: ../include/sys_defs.h +file_id.o: ../include/msg.h +file_id.o: ../include/vstring.h +file_id.o: ../include/vbuf.h +file_id.o: file_id.h +header_opts.o: header_opts.c +header_opts.o: ../include/sys_defs.h +header_opts.o: ../include/msg.h +header_opts.o: ../include/htable.h +header_opts.o: ../include/vstring.h +header_opts.o: ../include/vbuf.h +header_opts.o: header_opts.h +is_header.o: is_header.c +is_header.o: ../include/sys_defs.h +is_header.o: is_header.h +mail_addr.o: mail_addr.c +mail_addr.o: ../include/sys_defs.h +mail_addr.o: ../include/stringops.h +mail_addr.o: mail_params.h +mail_addr.o: mail_addr.h +mail_addr_crunch.o: mail_addr_crunch.c +mail_addr_crunch.o: ../include/sys_defs.h +mail_addr_crunch.o: ../include/mymalloc.h +mail_addr_crunch.o: ../include/argv.h +mail_addr_crunch.o: ../include/vstring.h +mail_addr_crunch.o: ../include/vbuf.h +mail_addr_crunch.o: tok822.h +mail_addr_crunch.o: resolve_clnt.h +mail_addr_crunch.o: canon_addr.h +mail_addr_crunch.o: mail_addr_crunch.h +mail_addr_find.o: mail_addr_find.c +mail_addr_find.o: ../include/sys_defs.h +mail_addr_find.o: ../include/msg.h +mail_addr_find.o: ../include/dict.h +mail_addr_find.o: ../include/vstream.h +mail_addr_find.o: ../include/vbuf.h +mail_addr_find.o: ../include/stringops.h +mail_addr_find.o: ../include/mymalloc.h +mail_addr_find.o: ../include/vstring.h +mail_addr_find.o: mail_params.h +mail_addr_find.o: split_addr.h +mail_addr_find.o: mail_addr_find.h +mail_addr_find.o: maps.h +mail_addr_find.o: resolve_local.h +mail_addr_map.o: mail_addr_map.c +mail_addr_map.o: ../include/sys_defs.h +mail_addr_map.o: ../include/msg.h +mail_addr_map.o: ../include/vstring.h +mail_addr_map.o: ../include/vbuf.h +mail_addr_map.o: ../include/dict.h +mail_addr_map.o: ../include/vstream.h +mail_addr_map.o: ../include/argv.h +mail_addr_map.o: ../include/mymalloc.h +mail_addr_map.o: mail_addr_find.h +mail_addr_map.o: maps.h +mail_addr_map.o: mail_addr_crunch.h +mail_addr_map.o: mail_addr_map.h +mail_command_read.o: mail_command_read.c +mail_command_read.o: ../include/sys_defs.h +mail_command_read.o: ../include/vstring.h +mail_command_read.o: ../include/vbuf.h +mail_command_read.o: ../include/vstream.h +mail_command_read.o: mail_proto.h +mail_command_read.o: ../include/iostuff.h +mail_command_write.o: mail_command_write.c +mail_command_write.o: ../include/sys_defs.h +mail_command_write.o: ../include/vstream.h +mail_command_write.o: ../include/vbuf.h +mail_command_write.o: mail_proto.h +mail_command_write.o: ../include/iostuff.h +mail_connect.o: mail_connect.c +mail_connect.o: ../include/sys_defs.h +mail_connect.o: ../include/msg.h +mail_connect.o: ../include/vstream.h +mail_connect.o: ../include/vbuf.h +mail_connect.o: ../include/connect.h +mail_connect.o: ../include/iostuff.h +mail_connect.o: ../include/mymalloc.h +mail_connect.o: timed_ipc.h +mail_connect.o: mail_proto.h +mail_copy.o: mail_copy.c +mail_copy.o: ../include/sys_defs.h +mail_copy.o: ../include/msg.h +mail_copy.o: ../include/htable.h +mail_copy.o: ../include/vstream.h +mail_copy.o: ../include/vbuf.h +mail_copy.o: ../include/vstring.h +mail_copy.o: ../include/vstring_vstream.h +mail_copy.o: ../include/stringops.h +mail_copy.o: quote_822_local.h +mail_copy.o: record.h +mail_copy.o: rec_type.h +mail_copy.o: mail_queue.h +mail_copy.o: mail_addr.h +mail_copy.o: mail_copy.h +mail_copy.o: mark_corrupt.h +mail_date.o: mail_date.c +mail_date.o: ../include/sys_defs.h +mail_date.o: ../include/msg.h +mail_date.o: ../include/vstring.h +mail_date.o: ../include/vbuf.h +mail_date.o: mail_date.h +mail_error.o: mail_error.c +mail_error.o: ../include/sys_defs.h +mail_error.o: mail_error.h +mail_error.o: ../include/name_mask.h +mail_flush.o: mail_flush.c +mail_flush.o: ../include/sys_defs.h +mail_flush.o: mail_proto.h +mail_flush.o: ../include/vstream.h +mail_flush.o: ../include/vbuf.h +mail_flush.o: ../include/iostuff.h +mail_flush.o: mail_flush.h +mail_open_ok.o: mail_open_ok.c +mail_open_ok.o: ../include/sys_defs.h +mail_open_ok.o: ../include/msg.h +mail_open_ok.o: mail_queue.h +mail_open_ok.o: ../include/vstring.h +mail_open_ok.o: ../include/vbuf.h +mail_open_ok.o: ../include/vstream.h +mail_open_ok.o: mail_open_ok.h +mail_params.o: mail_params.c +mail_params.o: ../include/sys_defs.h +mail_params.o: ../include/msg.h +mail_params.o: ../include/get_hostname.h +mail_params.o: ../include/valid_hostname.h +mail_params.o: mynetworks.h +mail_params.o: config.h +mail_params.o: mail_version.h +mail_params.o: mail_params.h +mail_pathname.o: mail_pathname.c +mail_pathname.o: ../include/sys_defs.h +mail_pathname.o: ../include/stringops.h +mail_pathname.o: mail_proto.h +mail_pathname.o: ../include/vstream.h +mail_pathname.o: ../include/vbuf.h +mail_pathname.o: ../include/iostuff.h +mail_print.o: mail_print.c +mail_print.o: ../include/sys_defs.h +mail_print.o: ../include/msg.h +mail_print.o: ../include/mymalloc.h +mail_print.o: ../include/vstream.h +mail_print.o: ../include/vbuf.h +mail_print.o: mail_proto.h +mail_print.o: ../include/iostuff.h +mail_queue.o: mail_queue.c +mail_queue.o: ../include/sys_defs.h +mail_queue.o: ../include/msg.h +mail_queue.o: ../include/vstring.h +mail_queue.o: ../include/vbuf.h +mail_queue.o: ../include/vstream.h +mail_queue.o: ../include/mymalloc.h +mail_queue.o: ../include/argv.h +mail_queue.o: ../include/dir_forest.h +mail_queue.o: ../include/make_dirs.h +mail_queue.o: ../include/split_at.h +mail_queue.o: file_id.h +mail_queue.o: mail_params.h +mail_queue.o: mail_queue.h +mail_run.o: mail_run.c +mail_run.o: ../include/sys_defs.h +mail_run.o: ../include/msg.h +mail_run.o: ../include/stringops.h +mail_run.o: ../include/mymalloc.h +mail_run.o: mail_params.h +mail_run.o: mail_run.h +mail_scan.o: mail_scan.c +mail_scan.o: ../include/sys_defs.h +mail_scan.o: ../include/msg.h +mail_scan.o: ../include/vstring.h +mail_scan.o: ../include/vbuf.h +mail_scan.o: ../include/vstream.h +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_stream.o: mail_stream.c +mail_stream.o: ../include/sys_defs.h +mail_stream.o: ../include/msg.h +mail_stream.o: ../include/mymalloc.h +mail_stream.o: ../include/vstring.h +mail_stream.o: ../include/vbuf.h +mail_stream.o: ../include/vstream.h +mail_stream.o: ../include/stringops.h +mail_stream.o: cleanup_user.h +mail_stream.o: mail_proto.h +mail_stream.o: ../include/iostuff.h +mail_stream.o: mail_queue.h +mail_stream.o: opened.h +mail_stream.o: mail_stream.h +mail_task.o: mail_task.c +mail_task.o: ../include/sys_defs.h +mail_task.o: ../include/vstring.h +mail_task.o: ../include/vbuf.h +mail_task.o: mail_params.h +mail_task.o: mail_task.h +mail_trigger.o: mail_trigger.c +mail_trigger.o: ../include/sys_defs.h +mail_trigger.o: ../include/msg.h +mail_trigger.o: ../include/mymalloc.h +mail_trigger.o: ../include/iostuff.h +mail_trigger.o: ../include/trigger.h +mail_trigger.o: mail_params.h +mail_trigger.o: mail_proto.h +mail_trigger.o: ../include/vstream.h +mail_trigger.o: ../include/vbuf.h +maps.o: maps.c +maps.o: ../include/sys_defs.h +maps.o: ../include/argv.h +maps.o: ../include/mymalloc.h +maps.o: ../include/msg.h +maps.o: ../include/dict.h +maps.o: ../include/vstream.h +maps.o: ../include/vbuf.h +maps.o: ../include/stringops.h +maps.o: ../include/split_at.h +maps.o: config.h +maps.o: maps.h +mark_corrupt.o: mark_corrupt.c +mark_corrupt.o: ../include/sys_defs.h +mark_corrupt.o: ../include/msg.h +mark_corrupt.o: ../include/vstream.h +mark_corrupt.o: ../include/vbuf.h +mark_corrupt.o: mail_queue.h +mark_corrupt.o: ../include/vstring.h +mark_corrupt.o: mark_corrupt.h +mkmap_db.o: mkmap_db.c +mkmap_db.o: ../include/sys_defs.h +mkmap_db.o: ../include/msg.h +mkmap_db.o: ../include/mymalloc.h +mkmap_db.o: ../include/stringops.h +mkmap_db.o: ../include/dict.h +mkmap_db.o: ../include/vstream.h +mkmap_db.o: ../include/vbuf.h +mkmap_db.o: ../include/dict_db.h +mkmap_db.o: mkmap.h +mkmap_dbm.o: mkmap_dbm.c +mkmap_dbm.o: ../include/sys_defs.h +mkmap_dbm.o: ../include/msg.h +mkmap_dbm.o: ../include/mymalloc.h +mkmap_dbm.o: ../include/stringops.h +mkmap_dbm.o: ../include/dict.h +mkmap_dbm.o: ../include/vstream.h +mkmap_dbm.o: ../include/vbuf.h +mkmap_dbm.o: ../include/dict_dbm.h +mkmap_dbm.o: mkmap.h +mkmap_open.o: mkmap_open.c +mkmap_open.o: ../include/sys_defs.h +mkmap_open.o: ../include/msg.h +mkmap_open.o: ../include/dict.h +mkmap_open.o: ../include/vstream.h +mkmap_open.o: ../include/vbuf.h +mkmap_open.o: ../include/sigdelay.h +mkmap_open.o: ../include/mymalloc.h +mkmap_open.o: ../include/myflock.h +mkmap_open.o: mkmap.h +mynetworks.o: mynetworks.c +mynetworks.o: ../include/sys_defs.h +mynetworks.o: ../include/msg.h +mynetworks.o: ../include/vstring.h +mynetworks.o: ../include/vbuf.h +mynetworks.o: ../include/inet_addr_list.h +mynetworks.o: own_inet_addr.h +mynetworks.o: mynetworks.h +mypwd.o: mypwd.c +mypwd.o: ../include/sys_defs.h +mypwd.o: ../include/mymalloc.h +mypwd.o: ../include/htable.h +mypwd.o: ../include/binhash.h +mypwd.o: ../include/msg.h +mypwd.o: mypwd.h +namadr_list.o: namadr_list.c +namadr_list.o: ../include/sys_defs.h +namadr_list.o: ../include/match_list.h +namadr_list.o: ../include/match_ops.h +namadr_list.o: namadr_list.h +off_cvt.o: off_cvt.c +off_cvt.o: ../include/sys_defs.h +off_cvt.o: ../include/msg.h +off_cvt.o: ../include/vstring.h +off_cvt.o: ../include/vbuf.h +off_cvt.o: off_cvt.h +opened.o: opened.c +opened.o: ../include/sys_defs.h +opened.o: ../include/msg.h +opened.o: ../include/vstring.h +opened.o: ../include/vbuf.h +opened.o: opened.h +own_inet_addr.o: own_inet_addr.c +own_inet_addr.o: ../include/sys_defs.h +own_inet_addr.o: ../include/msg.h +own_inet_addr.o: ../include/mymalloc.h +own_inet_addr.o: ../include/inet_addr_list.h +own_inet_addr.o: ../include/inet_addr_local.h +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 +pipe_command.o: pipe_command.c +pipe_command.o: ../include/sys_defs.h +pipe_command.o: ../include/msg.h +pipe_command.o: ../include/vstream.h +pipe_command.o: ../include/vbuf.h +pipe_command.o: ../include/vstring.h +pipe_command.o: ../include/stringops.h +pipe_command.o: ../include/iostuff.h +pipe_command.o: ../include/timed_wait.h +pipe_command.o: ../include/set_ugid.h +pipe_command.o: ../include/argv.h +pipe_command.o: mail_params.h +pipe_command.o: mail_copy.h +pipe_command.o: clean_env.h +pipe_command.o: pipe_command.h +pipe_command.o: ../include/exec_command.h +pipe_command.o: sys_exits.h +post_mail.o: post_mail.c +post_mail.o: ../include/sys_defs.h +post_mail.o: ../include/msg.h +post_mail.o: ../include/vstream.h +post_mail.o: ../include/vbuf.h +post_mail.o: ../include/vstring.h +post_mail.o: mail_params.h +post_mail.o: record.h +post_mail.o: rec_type.h +post_mail.o: mail_proto.h +post_mail.o: ../include/iostuff.h +post_mail.o: cleanup_user.h +post_mail.o: post_mail.h +post_mail.o: mail_date.h +quote_822_local.o: quote_822_local.c +quote_822_local.o: ../include/sys_defs.h +quote_822_local.o: ../include/vstring.h +quote_822_local.o: ../include/vbuf.h +quote_822_local.o: quote_822_local.h +rec2stream.o: rec2stream.c +rec2stream.o: ../include/sys_defs.h +rec2stream.o: ../include/vstring.h +rec2stream.o: ../include/vbuf.h +rec2stream.o: ../include/vstream.h +rec2stream.o: record.h +rec2stream.o: rec_streamlf.h +rec2stream.o: rec_type.h +rec_streamlf.o: rec_streamlf.c +rec_streamlf.o: ../include/sys_defs.h +rec_streamlf.o: ../include/vstring.h +rec_streamlf.o: ../include/vbuf.h +rec_streamlf.o: ../include/vstream.h +rec_streamlf.o: record.h +rec_streamlf.o: rec_type.h +rec_streamlf.o: rec_streamlf.h +rec_type.o: rec_type.c +rec_type.o: rec_type.h +recdump.o: recdump.c +recdump.o: ../include/sys_defs.h +recdump.o: ../include/msg_vstream.h +recdump.o: ../include/vstream.h +recdump.o: ../include/vbuf.h +recdump.o: record.h +recdump.o: ../include/vstring.h +recdump.o: rec_streamlf.h +recdump.o: rec_type.h +recipient_list.o: recipient_list.c +recipient_list.o: ../include/mymalloc.h +recipient_list.o: recipient_list.h +record.o: record.c +record.o: ../include/sys_defs.h +record.o: ../include/msg.h +record.o: ../include/mymalloc.h +record.o: ../include/vstream.h +record.o: ../include/vbuf.h +record.o: ../include/vstring.h +record.o: record.h +remove.o: remove.c +remove.o: ../include/sys_defs.h +remove.o: ../include/vstring.h +remove.o: ../include/vbuf.h +remove.o: mail_params.h +resolve_clnt.o: resolve_clnt.c +resolve_clnt.o: ../include/sys_defs.h +resolve_clnt.o: ../include/msg.h +resolve_clnt.o: ../include/vstream.h +resolve_clnt.o: ../include/vbuf.h +resolve_clnt.o: ../include/vstring.h +resolve_clnt.o: ../include/vstring_vstream.h +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: resolve_clnt.h +resolve_local.o: resolve_local.c +resolve_local.o: ../include/sys_defs.h +resolve_local.o: ../include/msg.h +resolve_local.o: ../include/mymalloc.h +resolve_local.o: string_list.h +resolve_local.o: mail_params.h +resolve_local.o: own_inet_addr.h +resolve_local.o: resolve_local.h +rewrite_clnt.o: rewrite_clnt.c +rewrite_clnt.o: ../include/sys_defs.h +rewrite_clnt.o: ../include/msg.h +rewrite_clnt.o: ../include/vstring.h +rewrite_clnt.o: ../include/vbuf.h +rewrite_clnt.o: ../include/vstream.h +rewrite_clnt.o: ../include/vstring_vstream.h +rewrite_clnt.o: ../include/events.h +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: rewrite_clnt.h +sent.o: sent.c +sent.o: ../include/sys_defs.h +sent.o: ../include/msg.h +sent.o: ../include/vstring.h +sent.o: ../include/vbuf.h +sent.o: sent.h +smtp_stream.o: smtp_stream.c +smtp_stream.o: ../include/sys_defs.h +smtp_stream.o: ../include/vstring.h +smtp_stream.o: ../include/vbuf.h +smtp_stream.o: ../include/vstream.h +smtp_stream.o: ../include/vstring_vstream.h +smtp_stream.o: ../include/msg.h +smtp_stream.o: ../include/iostuff.h +smtp_stream.o: smtp_stream.h +split_addr.o: split_addr.c +split_addr.o: ../include/sys_defs.h +split_addr.o: ../include/split_at.h +split_addr.o: mail_params.h +split_addr.o: mail_addr.h +split_addr.o: split_addr.h +stream2rec.o: stream2rec.c +stream2rec.o: ../include/sys_defs.h +stream2rec.o: ../include/vstream.h +stream2rec.o: ../include/vbuf.h +stream2rec.o: ../include/vstring.h +stream2rec.o: record.h +stream2rec.o: rec_streamlf.h +stream2rec.o: rec_type.h +string_list.o: string_list.c +string_list.o: ../include/sys_defs.h +string_list.o: ../include/match_list.h +string_list.o: ../include/match_ops.h +string_list.o: string_list.h +sys_exits.o: sys_exits.c +sys_exits.o: ../include/sys_defs.h +sys_exits.o: ../include/msg.h +sys_exits.o: sys_exits.h +timed_ipc.o: timed_ipc.c +timed_ipc.o: ../include/sys_defs.h +timed_ipc.o: ../include/msg.h +timed_ipc.o: ../include/vstream.h +timed_ipc.o: ../include/vbuf.h +timed_ipc.o: ../include/iostuff.h +timed_ipc.o: mail_params.h +timed_ipc.o: timed_ipc.h +tok822_find.o: tok822_find.c +tok822_find.o: ../include/sys_defs.h +tok822_find.o: ../include/vstring.h +tok822_find.o: ../include/vbuf.h +tok822_find.o: tok822.h +tok822_find.o: resolve_clnt.h +tok822_node.o: tok822_node.c +tok822_node.o: ../include/sys_defs.h +tok822_node.o: ../include/mymalloc.h +tok822_node.o: ../include/vstring.h +tok822_node.o: ../include/vbuf.h +tok822_node.o: tok822.h +tok822_node.o: resolve_clnt.h +tok822_parse.o: tok822_parse.c +tok822_parse.o: ../include/sys_defs.h +tok822_parse.o: ../include/vstring.h +tok822_parse.o: ../include/vbuf.h +tok822_parse.o: ../include/msg.h +tok822_parse.o: tok822.h +tok822_parse.o: resolve_clnt.h +tok822_resolve.o: tok822_resolve.c +tok822_resolve.o: ../include/sys_defs.h +tok822_resolve.o: ../include/vstring.h +tok822_resolve.o: ../include/vbuf.h +tok822_resolve.o: ../include/msg.h +tok822_resolve.o: resolve_clnt.h +tok822_resolve.o: tok822.h +tok822_rewrite.o: tok822_rewrite.c +tok822_rewrite.o: ../include/sys_defs.h +tok822_rewrite.o: ../include/vstring.h +tok822_rewrite.o: ../include/vbuf.h +tok822_rewrite.o: ../include/msg.h +tok822_rewrite.o: rewrite_clnt.h +tok822_rewrite.o: tok822.h +tok822_rewrite.o: resolve_clnt.h +tok822_tree.o: tok822_tree.c +tok822_tree.o: ../include/sys_defs.h +tok822_tree.o: ../include/mymalloc.h +tok822_tree.o: ../include/vstring.h +tok822_tree.o: ../include/vbuf.h +tok822_tree.o: tok822.h +tok822_tree.o: resolve_clnt.h diff --git a/postfix/global/been_here.c b/postfix/global/been_here.c new file mode 100644 index 000000000..572c78ebc --- /dev/null +++ b/postfix/global/been_here.c @@ -0,0 +1,175 @@ +/*++ +/* NAME +/* been_here 3 +/* SUMMARY +/* detect repeated occurrence of string +/* SYNOPSIS +/* #include +/* +/* BH_TABLE *been_here_init(size) +/* int size; +/* +/* int been_here_fixed(dup_filter, string) +/* BH_TABLE *dup_filter; +/* char *string; +/* +/* int been_here(dup_filter, format, ...) +/* BH_TABLE *dup_filter; +/* char *format; +/* +/* void been_here_free(dup_filter) +/* BH_TABLE *dup_filter; +/* DESCRIPTION +/* This module implements a simple filter to detect repeated +/* occurrences of character strings. +/* +/* been_here_init() creates an empty duplicate filter. +/* +/* been_here_fixed() looks up a fixed string in the given table, and +/* makes an entry in the table if the string was not found. The result +/* is non-zero (true) if the string was found, zero (false) otherwise. +/* +/* been_here() formats its arguments, looks up the result in the +/* given table, and makes an entry in the table if the string was +/* not found. The result is non-zero (true) if the formatted result was +/* found, zero (false) otherwise. +/* +/* been_here_free() releases storage for a duplicate filter. +/* +/* Arguments: +/* .IP size +/* Upper bound on the table size; at most \fIsize\fR strings will +/* be remembered. Specify a value <= 0 to disable the upper bound. +/* .IP flags +/* Requests for special processing. Specify the bitwise OR of zero +/* or more flags: +/* .RS +/* .IP BH_FLAG_FOLD +/* Enable case-insensitive lookup. +/* .IP BH_FLAG_NONE +/* A manifest constant that requests no special processing. +/* .RE +/* .IP dup_filter +/* The table with remembered names +/* .IP string +/* Fixed search string. +/* .IP format +/* Format for building the search string. +/* 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 /* 44BSD stdarg.h uses abort() */ +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include "been_here.h" + +/* been_here_init - initialize duplicate filter */ + +BH_TABLE *been_here_init(int limit, int flags) +{ + BH_TABLE *dup_filter; + + dup_filter = (BH_TABLE *) mymalloc(sizeof(*dup_filter)); + dup_filter->limit = limit; + dup_filter->flags = flags; + dup_filter->table = htable_create(0); + return (dup_filter); +} + +/* been_here_free - destroy duplicate filter */ + +void been_here_free(BH_TABLE *dup_filter) +{ + htable_free(dup_filter->table, (void (*) (char *)) 0); + myfree((char *) dup_filter); +} + +/* been_here - duplicate detector with finer control */ + +int been_here(BH_TABLE *dup_filter, const char *fmt,...) +{ + VSTRING *buf = vstring_alloc(100); + int status; + va_list ap; + + /* + * Construct the string to be checked. + */ + va_start(ap, fmt); + vstring_vsprintf(buf, fmt, ap); + va_end(ap); + + /* + * Do the duplicate check. + */ + status = been_here_fixed(dup_filter, vstring_str(buf)); + + /* + * Cleanup. + */ + vstring_free(buf); + return (status); +} + +/* been_here_fixed - duplicate detector */ + +int been_here_fixed(BH_TABLE *dup_filter, const char *string) +{ + char *folded_string; + const char *lookup_key; + int status; + + /* + * Special processing: case insensitive lookup. + */ + if (dup_filter->flags & BH_FLAG_FOLD) { + folded_string = mystrdup(string); + lookup_key = lowercase(folded_string); + } else { + folded_string = 0; + lookup_key = string; + } + + /* + * Do the duplicate check. + */ + if (htable_locate(dup_filter->table, lookup_key) != 0) { + status = 1; + } else { + if (dup_filter->limit > 0 + && dup_filter->limit > dup_filter->table->used) + htable_enter(dup_filter->table, lookup_key, (char *) 0); + status = 0; + } + if (msg_verbose) + msg_info("been_here: %s: %d", string, status); + + /* + * Cleanup. + */ + if (folded_string) + myfree(folded_string); + + return (status); +} diff --git a/postfix/global/been_here.h b/postfix/global/been_here.h new file mode 100644 index 000000000..953889784 --- /dev/null +++ b/postfix/global/been_here.h @@ -0,0 +1,47 @@ +#ifndef _BEEN_HERE_H_INCLUDED_ +#define _BEEN_HERE_H_INCLUDED_ + +/*++ +/* NAME +/* been_here 3h +/* SUMMARY +/* detect repeated occurrence of string +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * External interface. + */ +typedef struct { + int limit; /* ceiling, zero for none */ + int flags; /* see below */ + struct HTABLE *table; +} BH_TABLE; + +#define BH_FLAG_NONE 0 /* no special processing */ +#define BH_FLAG_FOLD (1<<0) /* fold case */ + +extern BH_TABLE *been_here_init(int, int); +extern void been_here_free(BH_TABLE *); +extern int been_here_fixed(BH_TABLE *, const char *); +extern int been_here(BH_TABLE *, const char *,...); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/bounce.c b/postfix/global/bounce.c new file mode 100644 index 000000000..c0a741c23 --- /dev/null +++ b/postfix/global/bounce.c @@ -0,0 +1,167 @@ +/*++ +/* NAME +/* bounce 3 +/* SUMMARY +/* bounce service client +/* SYNOPSIS +/* #include +/* +/* int bounce_append(flags, id, recipient, relay, entry, format, ...) +/* int flags; +/* const char *id; +/* const char *recipient; +/* const char *relay; +/* time_t entry; +/* const char *format; +/* +/* int vbounce_append(flags, id, recipient, relay, entry, format, ap) +/* int flags; +/* const char *id; +/* const char *recipient; +/* const char *relay; +/* time_t entry; +/* const char *format; +/* va_list ap; +/* +/* int bounce_flush(flags, queue, id, sender) +/* int flags; +/* const char *queue; +/* const char *id; +/* const char *sender; +/* DESCRIPTION +/* This module implements the client interface to the message +/* bounce service, which maintains a per-message log of status +/* records with recipients that were bounced, and the reason why. +/* +/* bounce_append() appends a reason for non-delivery to the +/* bounce log for the named recipient. +/* +/* vbounce_append() implements an alternative interface. +/* +/* bounce_flush() actually bounces the specified message to +/* the specified sender, including the bounce log that was +/* built with bounce_append(). +/* +/* Arguments: +/* .IP flags +/* The bitwise OR of zero or mor of the following (specify +/* BOUNCE_FLAG_NONE to request no special processing): +/* .RS +/* .IP BOUNCE_FLAG_CLEAN +/* Delete the bounce log in case of an error (as in: pretend +/* that we never even tried to bounce this message). +/* .IP BOUNCE_FLAG_COPY +/* Request that a postmaster copy is sent (bounce_flush() only). +/* .RE +/* .IP queue +/* The message queue name of the original message file. +/* .IP id +/* The message queue id if the original message file. The bounce log +/* file has the same name as the original message file. +/* .IP sender +/* The sender envelope address. +/* .IP relay +/* Name of the host that the message could not be delivered to. +/* This information is used for syslogging only. +/* .IP entry +/* Message arrival time. +/* .IP recipient +/* Recipient address that the message could not be delivered to. +/* This information is used for syslogging only. +/* .IP format +/* The reason for non-delivery. +/* .IP ap +/* Variable-length argument list. +/* DIAGNOSTICS +/* In case of success, these functions log the action, and return a +/* zero value. Otherwise, the functions return a non-zero result, +/* and when BOUNCE_FLAG_CLEAN is disabled, log that message +/* delivery is deferred. +/* BUGS +/* Should be replaced by routines with an attribute-value based +/* interface instead of an interface that uses a rigid argument list. +/* 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 /* 44BSD stdarg.h uses abort() */ +#include +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "mail_proto.h" +#include "defer.h" +#include "bounce.h" + +/* bounce_append - append reason to per-message bounce log */ + +int bounce_append(int flags, const char *id, const char *recipient, + const char *relay, time_t entry, const char *fmt,...) +{ + va_list ap; + int status; + + va_start(ap, fmt); + status = vbounce_append(flags, id, recipient, relay, entry, fmt, ap); + va_end(ap); + return (status); +} + +/* vbounce_append - append bounce reason to per-message log */ + +int vbounce_append(int flags, const char *id, const char *recipient, + const char *relay, time_t entry, const char *fmt, va_list ap) +{ + VSTRING *why = vstring_alloc(100); + int status; + int delay = time((time_t *) 0) - entry; + + vstring_vsprintf(why, fmt, ap); + if (mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_BOUNCE, + "%d %d %s %s %s", BOUNCE_CMD_APPEND, + flags, id, recipient, vstring_str(why)) == 0) { + msg_info("%s: to=<%s>, relay=%s, delay=%d, status=bounced (%s)", + id, recipient, relay, delay, vstring_str(why)); + status = 0; + } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) { + status = defer_append(flags, id, recipient, "bounce", delay, + "bounce failed"); + } else { + status = -1; + } + vstring_free(why); + return (status); +} + +/* bounce_flush - flush the bounce log and deliver to the sender */ + +int bounce_flush(int flags, const char *queue, const char *id, + const char *sender) +{ + if (mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_BOUNCE, + "%d %d %s %s %s", BOUNCE_CMD_FLUSH, + flags, queue, id, sender) == 0) { + return (0); + } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) { + msg_info("%s: status=deferred (bounce failed)", id); + return (-1); + } else { + return (-1); + } +} diff --git a/postfix/global/bounce.h b/postfix/global/bounce.h new file mode 100644 index 000000000..6d1afb1ce --- /dev/null +++ b/postfix/global/bounce.h @@ -0,0 +1,58 @@ +#ifndef _BOUNCE_H_INCLUDED_ +#define _BOUNCE_H_INCLUDED_ + +/*++ +/* NAME +/* bounce 3h +/* SUMMARY +/* bounce service client +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include +#include + + /* + * Client interface. + */ +extern int bounce_append(int, const char *, const char *, const char *, + time_t, const char *,...); +extern int vbounce_append(int, const char *, const char *, const char *, + time_t, const char *, va_list); +extern int bounce_flush(int, const char *, const char *, const char *); + + /* + * Bounce/defer protocol commands. + */ +#define BOUNCE_CMD_APPEND 0 /* append log */ +#define BOUNCE_CMD_FLUSH 1 /* send log */ + + /* + * Flags. + */ +#define BOUNCE_FLAG_NONE 0 /* no flags up */ +#define BOUNCE_FLAG_CLEAN (1<<0) /* remove log on error */ +#define BOUNCE_FLAG_COPY (1<<1) /* postmaster notice */ + + /* + * Backwards compatibility. + */ +#define BOUNCE_FLAG_KEEP BOUNCE_FLAG_NONE + +/* 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/canon_addr.c b/postfix/global/canon_addr.c new file mode 100644 index 000000000..912ae1dc0 --- /dev/null +++ b/postfix/global/canon_addr.c @@ -0,0 +1,67 @@ +/*++ +/* NAME +/* canon_addr 3 +/* SUMMARY +/* simple address canonicalization +/* SYNOPSIS +/* #include +/* +/* VSTRING *canon_addr_external(result, address) +/* VSTRING *result; +/* const char *address; +/* +/* VSTRING *canon_addr_internal(result, address) +/* VSTRING *result; +/* const char *address; +/* DESCRIPTION +/* This module provides a simple interface to the address +/* canonicalization service that is provided by the address +/* rewriting service. +/* +/* canon_addr_external() transforms an address in external (i.e. +/* quoted) RFC822 form to a fully-qualified address (user@domain). +/* +/* canon_addr_internal() transforms an address in internal (i.e. +/* unquoted) RFC822 form to a fully-qualified address (user@domain). +/* STANDARDS +/* RFC 822 (ARPA Internet Text Messages). +/* SEE ALSO +/* rewrite_clnt(3) address rewriting client interface +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "rewrite_clnt.h" +#include "canon_addr.h" + +/* canon_addr_external - make address fully qualified, external form */ + +VSTRING *canon_addr_external(VSTRING *result, const char *addr) +{ + return (rewrite_clnt(REWRITE_CANON, addr, result)); +} + +/* canon_addr_internal - make address fully qualified, internal form */ + +VSTRING *canon_addr_internal(VSTRING *result, const char *addr) +{ + return (rewrite_clnt_internal(REWRITE_CANON, addr, result)); +} diff --git a/postfix/global/canon_addr.h b/postfix/global/canon_addr.h new file mode 100644 index 000000000..63b7e2939 --- /dev/null +++ b/postfix/global/canon_addr.h @@ -0,0 +1,36 @@ +#ifndef _CANON_ADDR_H_INCLUDED_ +#define _CANON_ADDR_H_INCLUDED_ + +/*++ +/* NAME +/* canon_addr 3h +/* SUMMARY +/* simple address canonicalization +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern VSTRING *canon_addr_external(VSTRING *, const char *); +extern VSTRING *canon_addr_internal(VSTRING *, const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/clean_env.c b/postfix/global/clean_env.c new file mode 100644 index 000000000..3086680c8 --- /dev/null +++ b/postfix/global/clean_env.c @@ -0,0 +1,68 @@ +/*++ +/* NAME +/* clean_env 3 +/* SUMMARY +/* clean up the environment +/* SYNOPSIS +/* #include +/* +/* void clean_env() +/* DESCRIPTION +/* clean_env() reduces the process environment to the bare minimum. +/* In the initial version, rules are hard-coded. This will be +/* made configurable. +/* 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 + +/* Utility library. */ + +#include + +/* Global library. */ + +#include "clean_env.h" + +/* clean_env - clean up the environment */ + +void clean_env(void) +{ + char *TZ; + extern char **environ; + + /* + * Preserve selected environment variables. This list will be + * configurable. + */ + TZ = getenv("TZ"); + + /* + * Truncate the process environment, if available. On some systems + * (Ultrix!), environ can be a null pointer. + */ + if (environ) + environ[0] = 0; + + /* + * Restore preserved environment variables. + */ + if (TZ && setenv("TZ", TZ, 1)) + msg_fatal("setenv: %m"); + + /* + * Update the process environment with configurable initial values. + */ +} diff --git a/postfix/global/clean_env.h b/postfix/global/clean_env.h new file mode 100644 index 000000000..a837bf319 --- /dev/null +++ b/postfix/global/clean_env.h @@ -0,0 +1,30 @@ +#ifndef _CLEAN_ENV_H_INCLUDED_ +#define _CLEAN_ENV_H_INCLUDED_ + +/*++ +/* NAME +/* clean_env 3h +/* SUMMARY +/* clean up the environment +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern void clean_env(void); + +/* 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/cleanup_strerror.c b/postfix/global/cleanup_strerror.c new file mode 100644 index 000000000..cd23b4d7a --- /dev/null +++ b/postfix/global/cleanup_strerror.c @@ -0,0 +1,76 @@ +/*++ +/* NAME +/* cleanup_strerror 3 +/* SUMMARY +/* cleanup status code to string +/* SYNOPSIS +/* #include +/* +/* const char *cleanup_strerror(code) +/* int code; +/* DESCRIPTION +/* cleanup_strerror() maps a status code returned by the \fIcleanup\fR +/* service to printable string. +/* The result is for read purposes only. Unknown status codes share +/* a common result buffer. +/* 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 + +/* Global library. */ + +#include "cleanup_user.h" + + /* + * Mapping from status code to printable string. One message may suffer from + * multiple errors, to it is important to list the most severe errors first, + * because the result of lookup can be only one string. + */ +struct cleanup_stat_map { + unsigned status; + const char *text; +}; + +static struct cleanup_stat_map cleanup_stat_map[] = { + CLEANUP_STAT_BAD, "Internal protocol error", + CLEANUP_STAT_RCPT, "No recipients specified", + CLEANUP_STAT_HOPS, "Too many hops", + CLEANUP_STAT_SIZE, "Message file too big", + CLEANUP_STAT_CONT, "Message content rejected", + CLEANUP_STAT_WRITE, "Error writing message file", +}; + +/* cleanup_strerror - map status code to printable string */ + +const char *cleanup_strerror(unsigned status) +{ + static VSTRING *unknown; + unsigned i; + + if (status == 0) + return ("Success"); + + for (i = 0; i < sizeof(cleanup_stat_map) / sizeof(cleanup_stat_map[0]); i++) + if (cleanup_stat_map[i].status & status) + return (cleanup_stat_map[i].text); + + if (unknown == 0) + unknown = vstring_alloc(20); + vstring_sprintf(unknown, "Unknown status %u", status); + return (vstring_str(unknown)); +} diff --git a/postfix/global/cleanup_user.h b/postfix/global/cleanup_user.h new file mode 100644 index 000000000..ed28005e9 --- /dev/null +++ b/postfix/global/cleanup_user.h @@ -0,0 +1,48 @@ +#ifndef _CLEANUP_USER_H_INCLUDED_ +#define _CLEANUP_USER_H_INCLUDED_ + +/*++ +/* NAME +/* cleanup_user 3h +/* SUMMARY +/* cleanup user interface codes +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Options. + */ +#define CLEANUP_FLAG_NONE 0 /* No special features */ +#define CLEANUP_FLAG_BOUNCE (1<<0) /* Bounce bad messages */ +#define CLEANUP_FLAG_FILTER (2<<0) /* Enable content filter */ + + /* + * Diagnostics. + */ + +#define CLEANUP_STAT_OK 0 /* Success. */ + +#define CLEANUP_STAT_BAD (1<<0) /* Internal protocol error */ +#define CLEANUP_STAT_WRITE (1<<1) /* Error writing message file */ +#define CLEANUP_STAT_SIZE (1<<2) /* Message file too big */ +#define CLEANUP_STAT_CONT (1<<3) /* Message content rejected */ +#define CLEANUP_STAT_HOPS (1<<4) /* Too many hops */ +#define CLEANUP_STAT_SYN (1<<5) /* Bad address syntax */ +#define CLEANUP_STAT_RCPT (1<<6) /* No recipients found */ + +extern const char *cleanup_strerror(unsigned); + +/* 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/config.c b/postfix/global/config.c new file mode 100644 index 000000000..10b8aadc9 --- /dev/null +++ b/postfix/global/config.c @@ -0,0 +1,147 @@ +/*++ +/* NAME +/* config 3 +/* SUMMARY +/* global configuration parameter management +/* SYNOPSIS +/* #include +/* +/* void read_config() +/* +/* void config_update(name, value) +/* const char *name; +/* const char *value; +/* +/* const char *config_lookup(name) +/* const char *name; +/* +/* const char *config_eval(string) +/* const char *string; +/* +/* const char *config_lookup_eval(name) +/* const char *name; +/* DESCRIPTION +/* read_config() reads the global Postfix configuration file, and +/* stores its values into a global configuration dictionary. +/* +/* The following routines are wrappers around the generic dictionary +/* access routines. +/* +/* config_update() updates the named global parameter. This has +/* no effect on parameters whose value has already been looked up. +/* The update succeeds or the program terminates with fatal error. +/* +/* config_lookup() looks up the value of the named parameter. +/* A null pointer result means the parameter was not found. +/* The result is volatile and should be copied if it is to be +/* used for any appreciable amount of time. +/* +/* config_eval() recursively expands any $parameters in the +/* string argument. The result is volatile and should be copied +/* if it is to be used for any appreciable amount of time. +/* +/* config_lookup_eval() looks up the named parameter, and expands any +/* $parameters in the result. The result is volatile and should be +/* copied if it is to be used for any appreciable amount of time. +/* DIAGNOSTICS +/* Fatal errors: malformed numerical value. +/* FILES +/* /etc/postfix: default Postfix configuration directory. +/* ENVIRONMENT +/* MAIL_CONFIG, non-default configuration database +/* MAIL_VERBOSE, enable verbose mode +/* SEE ALSO +/* dict(3) generic dictionary manager +/* config_int(3) integer-valued parameters +/* config_str(3) string-valued parameters +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include "mail_params.h" +#include "config.h" + +/* read_config - read global configuration file */ + +void read_config(void) +{ + char *config_dir; + char *path; + + /* + * Permit references to unknown configuration variable names. We rely on + * a separate configuration checking tool to spot misspelled names and + * other kinds of trouble. Enter the configuration directory into the + * default dictionary. + */ + dict_unknown_allowed = 1; + if (var_config_dir) + myfree(var_config_dir); + var_config_dir = mystrdup((config_dir = safe_getenv(CONF_ENV_PATH)) != 0 ? + config_dir : DEF_CONFIG_DIR); /* XXX */ + set_config_str(VAR_CONFIG_DIR, var_config_dir); + path = concatenate(var_config_dir, "/", "main.cf", (char *) 0); + dict_load_file(CONFIG_DICT, path); + myfree(path); + mail_params_init(); +} + +/* config_eval - expand macros in string */ + +const char *config_eval(const char *string) +{ +#define RECURSIVE 1 + + return (dict_eval(CONFIG_DICT, string, RECURSIVE)); +} + +/* config_lookup - lookup named variable */ + +const char *config_lookup(const char *name) +{ + return (dict_lookup(CONFIG_DICT, name)); +} + +/* config_lookup_eval - expand named variable */ + +const char *config_lookup_eval(const char *name) +{ + const char *value; + +#define RECURSIVE 1 + + if ((value = dict_lookup(CONFIG_DICT, name)) != 0) + value = dict_eval(CONFIG_DICT, value, RECURSIVE); + return (value); +} + +/* config_update - update parameter */ + +void config_update(const char *key, const char *value) +{ + dict_update(CONFIG_DICT, key, value); +} diff --git a/postfix/global/config.h b/postfix/global/config.h new file mode 100644 index 000000000..92c124ec7 --- /dev/null +++ b/postfix/global/config.h @@ -0,0 +1,136 @@ +#ifndef _CONFIG_H_INCLUDED_ +#define _CONFIG_H_INCLUDED_ + +/*++ +/* NAME +/* config 3h +/* SUMMARY +/* global configuration parameter management +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Well known names. These are not configurable. One has to start somewhere. + */ +#define CONFIG_DICT "mail_dict" /* global Postfix dictionary */ + + /* + * Environment variables. + */ +#define CONF_ENV_PATH "MAIL_CONFIG" /* config database */ +#define CONF_ENV_VERB "MAIL_VERBOSE" /* verbose mode on */ +#define CONF_ENV_DEBUG "MAIL_DEBUG" /* verbose mode on */ + + /* + * External representation for booleans. + */ +#define CONFIG_BOOL_YES "yes" +#define CONFIG_BOOL_NO "no" + + /* + * Basic configuration management. + */ +extern void read_config(void); + +extern void config_update(const char *, const char *); +extern const char *config_lookup(const char *); +extern const char *config_eval(const char *); +extern const char *config_lookup_eval(const char *); + + /* + * Specific parameter lookup routines. + */ +extern char *get_config_str(const char *, const char *, int, int); +extern int get_config_int(const char *, int, int, int); +extern int get_config_bool(const char *, int); + +extern int get_config_int2(const char *, const char *, int, int, int); + + /* + * Lookup with function-call defaults. + */ +extern char *get_config_str_fn(const char *, const char *(*) (void), int, int); +extern int get_config_int_fn(const char *, int (*) (void), int, int); +extern int get_config_bool_fn(const char *, int (*) (void)); + + /* + * Update dictionary. + */ +extern void set_config_str(const char *, const char *); +extern void set_config_int(const char *, int); +extern void set_config_bool(const char *, int); + + /* + * Tables that allow us to selectively copy values from the global + * configuration file to global variables. + */ +typedef struct { + const char *name; /* config variable name */ + const char *defval; /* default value or null */ + char **target; /* pointer to global variable */ + int min; /* min length or zero */ + int max; /* max length or zero */ +} CONFIG_STR_TABLE; + +typedef struct { + const char *name; /* config variable name */ + int defval; /* default value */ + int *target; /* pointer to global variable */ + int min; /* lower bound or zero */ + int max; /* upper bound or zero */ +} CONFIG_INT_TABLE; + +typedef struct { + const char *name; /* config variable name */ + int defval; /* default value */ + int *target; /* pointer to global variable */ +} CONFIG_BOOL_TABLE; + +extern void get_config_str_table(CONFIG_STR_TABLE *); +extern void get_config_int_table(CONFIG_INT_TABLE *); +extern void get_config_bool_table(CONFIG_BOOL_TABLE *); + + /* + * Tables to initialize parameters from the global configuration file or + * from function calls. + */ +typedef struct { + const char *name; /* config variable name */ + const char *(*defval) (void); /* default value provider */ + char **target; /* pointer to global variable */ + int min; /* lower bound or zero */ + int max; /* upper bound or zero */ +} CONFIG_STR_FN_TABLE; + +typedef struct { + const char *name; /* config variable name */ + int (*defval) (void); /* default value provider */ + int *target; /* pointer to global variable */ + int min; /* lower bound or zero */ + int max; /* upper bound or zero */ +} CONFIG_INT_FN_TABLE; + +typedef struct { + const char *name; /* config variable name */ + int (*defval) (void); /* default value provider */ + int *target; /* pointer to global variable */ +} CONFIG_BOOL_FN_TABLE; + +extern void get_config_str_fn_table(CONFIG_STR_FN_TABLE *); +extern void get_config_int_fn_table(CONFIG_INT_FN_TABLE *); +extern void get_config_bool_fn_table(CONFIG_BOOL_FN_TABLE *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/config_bool.c b/postfix/global/config_bool.c new file mode 100644 index 000000000..0569760c2 --- /dev/null +++ b/postfix/global/config_bool.c @@ -0,0 +1,155 @@ +/*++ +/* NAME +/* config_bool 3 +/* SUMMARY +/* boolean-valued configuration parameter support +/* SYNOPSIS +/* #include +/* +/* int get_config_bool(name, defval) +/* const char *path; +/* const char *name; +/* int defval; +/* +/* int get_config_bool_fn(name, defval) +/* const char *path; +/* const char *name; +/* int (*defval)(); +/* +/* void set_config_bool(name, value) +/* const char *name; +/* int value; +/* +/* void get_config_bool_table(table) +/* CONFIG_BOOL_TABLE *table; +/* +/* void get_config_bool_fn_table(table) +/* CONFIG_BOOL_TABLE *table; +/* DESCRIPTION +/* This module implements configuration parameter support for +/* boolean values. The internal representation is zero (false) +/* and non-zero (true). The external representation is "no" +/* (false) and "yes" (true). The conversion from external +/* representation is case insensitive. +/* +/* get_config_bool() looks up the named entry in the global +/* configuration dictionary. The specified default value is +/* returned when no value was found. +/* +/* get_config_bool_fn() is similar but specifies a function that +/* provides the default value. The function is called only +/* when the default value is needed. +/* +/* set_config_bool() updates the named entry in the global +/* configuration dictionary. This has no effect on values that +/* have been looked up earlier via the get_config_XXX() routines. +/* +/* get_config_bool_table() and get_config_int_fn_table() initialize +/* lists of variables, as directed by their table arguments. A table +/* must be terminated by a null entry. +/* DIAGNOSTICS +/* Fatal errors: malformed boolean value. +/* SEE ALSO +/* config(3) general configuration +/* config_str(3) string-valued configuration parameters +/* config_int(3) integer-valued configuration parameters +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "config.h" + +/* convert_config_bool - look up and convert boolean parameter value */ + +static int convert_config_bool(const char *name, int *intval) +{ + const char *strval; + + if ((strval = config_lookup_eval(name)) == 0) { + return (0); + } else { + if (strcasecmp(strval, CONFIG_BOOL_YES) == 0) { + *intval = 1; + } else if (strcasecmp(strval, CONFIG_BOOL_NO) == 0) { + *intval = 0; + } else { + msg_fatal("bad boolean configuration: %s = %s", name, strval); + } + return (1); + } +} + +/* get_config_bool - evaluate boolean-valued configuration variable */ + +int get_config_bool(const char *name, int defval) +{ + int intval; + + if (convert_config_bool(name, &intval) == 0) + set_config_bool(name, intval = defval); + return (intval); +} + +/* get_config_bool_fn - evaluate boolean-valued configuration variable */ + +typedef int (*stupid_indent_int) (void); + +int get_config_bool_fn(const char *name, stupid_indent_int defval) +{ + int intval; + + if (convert_config_bool(name, &intval) == 0) + set_config_bool(name, intval = defval()); + return (intval); +} + +/* set_config_bool - update boolean-valued configuration dictionary entry */ + +void set_config_bool(const char *name, int value) +{ + config_update(name, value ? CONFIG_BOOL_YES : CONFIG_BOOL_NO); +} + +/* get_config_bool_table - look up table of booleans */ + +void get_config_bool_table(CONFIG_BOOL_TABLE *table) +{ + while (table->name) { + table->target[0] = get_config_bool(table->name, table->defval); + table++; + } +} + +/* get_config_bool_fn_table - look up booleans, defaults are functions */ + +void get_config_bool_fn_table(CONFIG_BOOL_FN_TABLE *table) +{ + while (table->name) { + table->target[0] = get_config_bool_fn(table->name, table->defval); + table++; + } +} diff --git a/postfix/global/config_int.c b/postfix/global/config_int.c new file mode 100644 index 000000000..fba915d70 --- /dev/null +++ b/postfix/global/config_int.c @@ -0,0 +1,193 @@ +/*++ +/* NAME +/* config_int 3 +/* SUMMARY +/* integer-valued configuration parameter support +/* SYNOPSIS +/* #include +/* +/* int get_config_int(name, defval, min, max); +/* const char *name; +/* int defval; +/* int min; +/* int max; +/* +/* int get_config_int_fn(name, defval, min, max); +/* const char *name; +/* int (*defval)(); +/* int min; +/* int max; +/* +/* void set_config_int(name, value) +/* const char *name; +/* int value; +/* +/* void get_config_int_table(table) +/* CONFIG_INT_TABLE *table; +/* +/* void get_config_int_fn_table(table) +/* CONFIG_INT_TABLE *table; +/* AUXILIARY FUNCTIONS +/* int get_config_int2(name1, name2, defval, min, max); +/* const char *name1; +/* const char *name2; +/* int defval; +/* int min; +/* int max; +/* DESCRIPTION +/* This module implements configuration parameter support +/* for integer values. +/* +/* get_config_int() looks up the named entry in the global +/* configuration dictionary. The default value is returned +/* when no value was found. +/* \fImin\fR is zero or specifies a lower limit on the integer +/* value or string length; \fImax\fR is zero or specifies an +/* upper limit on the integer value or string length. +/* +/* get_config_int_fn() is similar but specifies a function that +/* provides the default value. The function is called only +/* when the default value is needed. +/* +/* set_config_int() updates the named entry in the global +/* configuration dictionary. This has no effect on values that +/* have been looked up earlier via the get_config_XXX() routines. +/* +/* get_config_int_table() and get_config_int_fn_table() initialize +/* lists of variables, as directed by their table arguments. A table +/* must be terminated by a null entry. +/* +/* get_config_int2() concatenates the two names and is otherwise +/* identical to get_config_int(). +/* DIAGNOSTICS +/* Fatal errors: malformed numerical value. +/* SEE ALSO +/* config(3) general configuration +/* config_str(3) string-valued configuration parameters +/* 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 /* sscanf() */ + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include "config.h" + +/* convert_config_int - look up and convert integer parameter value */ + +static int convert_config_int(const char *name, int *intval) +{ + const char *strval; + char junk; + + if ((strval = config_lookup_eval(name)) != 0) { + if (sscanf(strval, "%d%c", intval, &junk) != 1) + msg_fatal("bad numerical configuration: %s = %s", name, strval); + return (1); + } + return (0); +} + +/* check_config_int - validate integer value */ + +static void check_config_int(const char *name, int intval, int min, int max) +{ + if (min && intval < min) + msg_fatal("invalid %s: %d (min %d)", name, intval, min); + if (max && intval > max) + msg_fatal("invalid %s: %d (max %d)", name, intval, max); +} + +/* get_config_int - evaluate integer-valued configuration variable */ + +int get_config_int(const char *name, int defval, int min, int max) +{ + int intval; + + if (convert_config_int(name, &intval) == 0) + set_config_int(name, intval = defval); + check_config_int(name, intval, min, max); + return (intval); +} + +/* get_config_int2 - evaluate integer-valued configuration variable */ + +int get_config_int2(const char *name1, const char *name2, int defval, + int min, int max) +{ + int intval; + char *name; + + name = concatenate(name1, name2, (char *) 0); + if (convert_config_int(name, &intval) == 0) + set_config_int(name, intval = defval); + check_config_int(name, intval, min, max); + myfree(name); + return (intval); +} + +/* get_config_int_fn - evaluate integer-valued configuration variable */ + +typedef int (*stupid_indent_int) (void); + +int get_config_int_fn(const char *name, stupid_indent_int defval, + int min, int max) +{ + int intval; + + if (convert_config_int(name, &intval) == 0) + set_config_int(name, intval = defval()); + check_config_int(name, intval, min, max); + return (intval); +} + +/* set_config_int - update integer-valued configuration dictionary entry */ + +void set_config_int(const char *name, int value) +{ + char buf[BUFSIZ]; /* yeah! crappy code! */ + + sprintf(buf, "%d", value); /* yeah! more crappy code! */ + config_update(name, buf); +} + +/* get_config_int_table - look up table of integers */ + +void get_config_int_table(CONFIG_INT_TABLE *table) +{ + while (table->name) { + table->target[0] = get_config_int(table->name, table->defval, + table->min, table->max); + table++; + } +} + +/* get_config_int_fn_table - look up integers, defaults are functions */ + +void get_config_int_fn_table(CONFIG_INT_FN_TABLE *table) +{ + while (table->name) { + table->target[0] = get_config_int_fn(table->name, table->defval, + table->min, table->max); + table++; + } +} diff --git a/postfix/global/config_str.c b/postfix/global/config_str.c new file mode 100644 index 000000000..6d2c6c37b --- /dev/null +++ b/postfix/global/config_str.c @@ -0,0 +1,160 @@ +/*++ +/* NAME +/* config_str 3 +/* SUMMARY +/* string-valued global configuration parameter support +/* SYNOPSIS +/* #include +/* +/* char *get_config_str(name, defval, min, max) +/* const char *name; +/* const char *defval; +/* int min; +/* int max; +/* +/* char *get_config_str_fn(name, defval, min, max) +/* const char *name; +/* const char *(*defval)(void); +/* int min; +/* int max; +/* +/* void set_config_str(name, value) +/* const char *name; +/* const char *value; +/* +/* void get_config_str_table(table) +/* CONFIG_STR_TABLE *table; +/* +/* void get_config_str_fn_table(table) +/* CONFIG_STR_TABLE *table; +/* DESCRIPTION +/* This module implements support for string-valued global +/* configuration parameters. +/* +/* get_config_str() looks up the named entry in the global +/* configuration dictionary. The default value is returned when +/* no value was found. String results should be passed to myfree() +/* when no longer needed. \fImin\fR is zero or specifies a lower +/* bound on the string length; \fImax\fR is zero or specifies an +/* upper limit on the string length. +/* +/* get_config_str_fn() is similar but specifies a function that +/* provides the default value. The function is called only when +/* the default value is used. +/* +/* set_config_str() updates the named entry in the global +/* configuration dictionary. This has no effect on values that +/* have been looked up earlier via the get_config_XXX() routines. +/* +/* get_config_str_table() and get_config_str_fn_table() read +/* lists of variables, as directed by their table arguments. A table +/* must be terminated by a null entry. +/* DIAGNOSTICS +/* Fatal errors: bad string length. +/* SEE ALSO +/* config(3) generic config parameter support +/* 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 + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "config.h" + +/* check_config_str - validate string length */ + +static void check_config_str(const char *name, const char *strval, + int min, int max) +{ + int len = strlen(strval); + + if (min && len < min) + msg_fatal("bad string length (%d < %d): %s = %s", + len, min, name, strval); + if (max && len > max) + msg_fatal("bad string length (%d > %d): %s = %s", + len, max, name, strval); +} + +/* get_config_str - evaluate string-valued configuration variable */ + +char *get_config_str(const char *name, const char *defval, + int min, int max) +{ + const char *strval; + + if ((strval = config_lookup_eval(name)) == 0) { + strval = config_eval(defval); + config_update(name, strval); + } + check_config_str(name, strval, min, max); + return (mystrdup(strval)); +} + +/* get_config_str_fn - evaluate string-valued configuration variable */ + +typedef const char *(*stupid_indent_str) (void); + +char *get_config_str_fn(const char *name, stupid_indent_str defval, + int min, int max) +{ + const char *strval; + + if ((strval = config_lookup_eval(name)) == 0) { + strval = config_eval(defval()); + config_update(name, strval); + } + check_config_str(name, strval, min, max); + return (mystrdup(strval)); +} + +/* set_config_str - update string-valued configuration dictionary entry */ + +void set_config_str(const char *name, const char *value) +{ + config_update(name, value); +} + +/* get_config_str_table - look up table of strings */ + +void get_config_str_table(CONFIG_STR_TABLE *table) +{ + while (table->name) { + if (table->target[0]) + myfree(table->target[0]); + table->target[0] = get_config_str(table->name, table->defval, + table->min, table->max); + table++; + } +} + +/* get_config_str_fn_table - look up strings, defaults are functions */ + +void get_config_str_fn_table(CONFIG_STR_FN_TABLE *table) +{ + while (table->name) { + if (table->target[0]) + myfree(table->target[0]); + table->target[0] = get_config_str_fn(table->name, table->defval, + table->min, table->max); + table++; + } +} diff --git a/postfix/global/debug_peer.c b/postfix/global/debug_peer.c new file mode 100644 index 000000000..29170bad5 --- /dev/null +++ b/postfix/global/debug_peer.c @@ -0,0 +1,129 @@ +/*++ +/* NAME +/* debug_peer 3 +/* SUMMARY +/* increase verbose logging for specific peers +/* SYNOPSIS +/* #include +/* +/* void debug_peer_init(void) +/* +/* int peer_debug_check(peer_name, peer_addr) +/* const char *peer_name; +/* const char *peer_addr; +/* +/* void debug_peer_restore() +/* DESCRIPTION +/* This module implements increased verbose logging for specific +/* network peers. +/* +/* The \fIdebug_peer_list\fR configuration parameter +/* specifies what peers receive this special treatment; see +/* namadr_list(3) for a description of the matching process. +/* +/* The \fIdebug_peer_level\fR configuration parameter specifies +/* by what amount the verbose logging level should increase when +/* a peer is listed in \fIdebug_peer_list\fR. +/* +/* debug_peer_init() performs initializations that must be +/* performed once at the start of the program. +/* +/* debug_peer_check() increases the verbose logging level when the +/* client name or address matches the debug_peer_list pattern. +/* The result is non-zero when the noise leven was increased. +/* +/* debug_peer_restore() restores the verbose logging level. +/* This routine has no effect when debug_peer_check() had no +/* effect; this routine can safely be called multiple times. +/* DIAGNOSTICS +/* Panic: interface violations. +/* Fatal errors: unable to access a peer_list file; invalid +/* peer_list pattern; invalid verbosity level increment. +/* SEE ALSO +/* msg(3) the msg_verbose variable +/* namadr_list(3) match host by name or by address +/* CONFIG PARAMETERS +/* debug_peer_list, patterns as described in namadr_list(3) +/* debug_peer_level, verbose logging level +/* 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 + +/* Global library. */ + +#include +#include +#include + +/* Application-specific. */ + +#define UNUSED_SAVED_LEVEL (-1) + +static NAMADR_LIST *debug_peer_list; +static int saved_level = UNUSED_SAVED_LEVEL; + +/* debug_peer_init - initialize */ + +void debug_peer_init(void) +{ + char *myname = "debug_peer_init"; + + /* + * Sanity check. + */ + if (debug_peer_list) + msg_panic("%s: repeated call", myname); + if (var_debug_peer_list == 0) + msg_panic("%s: uninitialized %s", myname, VAR_DEBUG_PEER_LIST); + if (var_debug_peer_level <= 0) + msg_fatal("%s: %s <= 0", myname, VAR_DEBUG_PEER_LEVEL); + + /* + * Finally. + */ + if (*var_debug_peer_list) + debug_peer_list = namadr_list_init(var_debug_peer_list); +} + +/* debug_peer_check - see if this peer needs verbose logging */ + +int debug_peer_check(const char *name, const char *addr) +{ + + /* + * Crank up the noise when this peer is listed. + */ + if (debug_peer_list != 0 + && saved_level == UNUSED_SAVED_LEVEL + && namadr_list_match(debug_peer_list, name, addr) != 0) { + saved_level = msg_verbose; + msg_verbose += var_debug_peer_level; + return (1); + } + return (0); +} + +/* debug_peer_restore - restore logging level */ + +void debug_peer_restore(void) +{ + if (saved_level != UNUSED_SAVED_LEVEL) { + msg_verbose = saved_level; + saved_level = UNUSED_SAVED_LEVEL; + } +} diff --git a/postfix/global/debug_peer.h b/postfix/global/debug_peer.h new file mode 100644 index 000000000..925e50329 --- /dev/null +++ b/postfix/global/debug_peer.h @@ -0,0 +1,31 @@ +#ifndef _DEBUG_PEER_H_INCLUDED_ +#define _DEBUG_PEER_H_INCLUDED_ +/*++ +/* NAME +/* debug_peer 3h +/* SUMMARY +/* increase verbose logging for specific peers +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern void debug_peer_init(void); +extern int debug_peer_check(const char *, const char *); +extern void debug_peer_restore(void); + +/* 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/debug_process.c b/postfix/global/debug_process.c new file mode 100644 index 000000000..0911afc1d --- /dev/null +++ b/postfix/global/debug_process.c @@ -0,0 +1,62 @@ +/*++ +/* NAME +/* debug_process 3 +/* SUMMARY +/* run an external debugger +/* SYNOPSIS +/* #include +/* +/* char *debug_process() +/* DESCRIPTION +/* debug_process() runs a debugger, as specified in the +/* \fIdebugger_command\fR configuration variable. +/* +/* Examples of non-interactive debuggers are call tracing tools +/* such as: trace, strace or truss. +/* +/* Examples of interactive debuggers are xxgdb, xxdbx, and so on. +/* In order to use an X-based debugger, the process must have a +/* properly set up XAUTHORITY environment variable. +/* 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 + +/* Utility library. */ + +#include + +/* Global library. */ + +#include "mail_params.h" +#include "config.h" +#include "debug_process.h" + +/* debug_process - run a debugger on this process */ + +void debug_process(void) +{ + const char *command; + + /* + * Expand $debugger_command then run it. + */ + command = config_lookup_eval(VAR_DEBUG_COMMAND); + if (*command == 0) + msg_fatal("no %s variable set up", VAR_DEBUG_COMMAND); + msg_info("running: %s", command); + system(command); +} diff --git a/postfix/global/debug_process.h b/postfix/global/debug_process.h new file mode 100644 index 000000000..04272ab5b --- /dev/null +++ b/postfix/global/debug_process.h @@ -0,0 +1,30 @@ +#ifndef _DEBUG_PROCESS_H_INCLUDED_ +#define _DEBUG_PROCESS_H_INCLUDED_ + +/*++ +/* NAME +/* debug_process 3h +/* SUMMARY +/* run an external debugger +/* SYNOPSIS +/* #include +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern void debug_process(void); + +/* 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 new file mode 100644 index 000000000..ebb0b3ae9 --- /dev/null +++ b/postfix/global/defer.c @@ -0,0 +1,156 @@ +/*++ +/* NAME +/* defer 3 +/* SUMMARY +/* defer service client interface +/* SYNOPSIS +/* #include +/* +/* int defer_append(flags, id, recipient, relay, entry, format, ...) +/* int flags; +/* const char *id; +/* const char *recipient; +/* const char *relay; +/* time_t entry; +/* const char *format; +/* +/* int vdefer_append(flags, id, recipient, relay, entry, format, ap) +/* int flags; +/* const char *id; +/* const char *recipient; +/* const char *relay; +/* time_t entry; +/* const char *format; +/* va_list ap; +/* +/* int defer_flush(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 +/* each recipient whose delivery is deferred, and the reason why. +/* +/* defer_append() appends a record to the per-message defer log, +/* with the reason for delayed delivery to the named recipient. +/* The result is a convenient non-zero value. +/* +/* vdefer_append() implements an alternative client interface. +/* +/* defer_flush() bounces the specified message to the specified +/* sender, including the defer log that was built with defer_append(). +/* The result is zero in case of success, non-zero otherwise. +/* +/* Arguments: +/* .IP flags +/* The bit-wise OR of zero or more of the following (specify +/* BOUNCE_FLAG_NONE to explicitly request not special processing): +/* .RS +/* .IP BOUNCE_FLAG_CLEAN +/* Delete the defer log in case of an error (as in: pretend +/* that we never even tried to defer this message). +/* .IP BOUNCE_FLAG_COPY +/* Request that postmaster a copy is sent (defer_flush() only). +/* .RE +/* .IP queue +/* The message queue name of the original message file. +/* .IP id +/* The queue id of the original message file. +/* .IP recipient +/* A recipient address that is being deferred. The domain part +/* of the address is marked dead (for a limited amount of time). +/* .IP sender +/* The sender envelope address. +/* .IP relay +/* Host we could not talk to. +/* .IP entry +/* Message arrival time. +/* .IP format +/* The reason for non-delivery. +/* .IP ap +/* Variable-length argument list. +/* .PP +/* For convenience, these functions always return a non-zero result. +/* DIAGNOSTICS +/* Warnings: problems connecting to the defer service. +/* Fatal: out of memory. +/* BUGS +/* Should be replaced by routines with an attribute-value based +/* interface instead of an interface that uses a rigid argument list. +/* 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 /* 44BSD stdarg.h uses abort() */ +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "mail_queue.h" +#include "mail_proto.h" +#include "bounce.h" +#include "defer.h" + +/* defer_append - defer message delivery */ + +int defer_append(int flags, const char *id, const char *recipient, + const char *relay, time_t entry, const char *fmt,...) +{ + va_list ap; + int status; + + va_start(ap, fmt); + status = vdefer_append(flags, id, recipient, relay, entry, fmt, ap); + va_end(ap); + return (status); +} + +/* vdefer_append - defer delivery of queue file */ + +int vdefer_append(int flags, const char *id, const char *recipient, + const char *relay, time_t entry, const char *fmt, va_list ap) +{ + VSTRING *why = vstring_alloc(100); + int delay = time((time_t *) 0) - entry; + + vstring_vsprintf(why, fmt, ap); + if (mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER, + "%d %d %s %s %s", BOUNCE_CMD_APPEND, + flags, id, recipient, vstring_str(why)) != 0) + msg_warn("%s: defer service failure", id); + msg_info("%s: to=<%s>, relay=%s, delay=%d, status=deferred (%s)", + id, recipient, relay, delay, vstring_str(why)); + vstring_free(why); + return (-1); +} + +/* defer_flush - flush the defer log and deliver to the sender */ + +int defer_flush(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_FLUSH, + flags, queue, id, sender) == 0) { + return (0); + } else { + return (-1); + } +} diff --git a/postfix/global/defer.h b/postfix/global/defer.h new file mode 100644 index 000000000..7458f459a --- /dev/null +++ b/postfix/global/defer.h @@ -0,0 +1,45 @@ +#ifndef _DEFER_H_INCLUDED_ +#define _DEFER_H_INCLUDED_ + +/*++ +/* NAME +/* defer 3h +/* SUMMARY +/* defer service client interface +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include +#include + + /* + * Global library. + */ +#include + + /* + * External interface. + */ +extern int defer_append(int, const char *, const char *, const char *, + time_t, const char *,...); +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 *); + +/* 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/deliver_completed.c b/postfix/global/deliver_completed.c new file mode 100644 index 000000000..485c794f9 --- /dev/null +++ b/postfix/global/deliver_completed.c @@ -0,0 +1,55 @@ +/*++ +/* NAME +/* deliver_completed 3 +/* SUMMARY +/* recipient delivery completion +/* SYNOPSIS +/* #include +/* +/* void deliver_completed(stream, offset) +/* VSTREAM *stream; +/* long offset; +/* DESCRIPTION +/* deliver_completed() crosses off the specified recipient from +/* an open queue file. +/* DIAGNOSTICS +/* Fatal error: unable to update the queue file. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "record.h" +#include "rec_type.h" +#include "deliver_completed.h" + +/* deliver_completed - handle per-recipient delivery completion */ + +void deliver_completed(VSTREAM *stream, long offset) +{ + char *myname = "deliver_completed"; + + if (offset <= 0) + msg_panic("%s: bad offset %ld", myname, offset); + + if (rec_put_type(stream, REC_TYPE_DONE, offset) < 0 + || vstream_fflush(stream)) + msg_fatal("update queue file %s: %m", VSTREAM_PATH(stream)); +} diff --git a/postfix/global/deliver_completed.h b/postfix/global/deliver_completed.h new file mode 100644 index 000000000..cb0b1df5d --- /dev/null +++ b/postfix/global/deliver_completed.h @@ -0,0 +1,35 @@ +#ifndef _DELIVER_COMPLETED_H_INCLUDED_ +#define _DELIVER_COMPLETED_H_INCLUDED_ + +/*++ +/* NAME +/* deliver_completed 3h +/* SUMMARY +/* recipient delivery completion +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern void deliver_completed(VSTREAM *, long); + +/* 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/deliver_flock.c b/postfix/global/deliver_flock.c new file mode 100644 index 000000000..ef5559ba3 --- /dev/null +++ b/postfix/global/deliver_flock.c @@ -0,0 +1,72 @@ +/*++ +/* NAME +/* deliver_flock 3 +/* SUMMARY +/* lock open file for mail delivery +/* SYNOPSIS +/* #include +/* +/* int deliver_flock(fd, why) +/* int fd; +/* VSTRING *why; +/* DESCRIPTION +/* deliver_flock() sets one exclusive lock on an open file +/* for the purpose of mail delivery. It attempts to acquire +/* the exclusive lock several times before giving up. +/* +/* Arguments: +/* .IP fd +/* A file descriptor that is associated with an open file. +/* .IP why +/* A null pointer, or storage for diagnostics. +/* DIAGNOSTICS +/* deliver_flock() returns -1 in case of problems, 0 in case +/* of success. The reason for failure is returned via the \fIwhy\fR +/* parameter. +/* CONFIGURATION PARAMETERS +/* deliver_lock_attempts, number of locking attempts +/* deliver_lock_delay, time in seconds between attempts +/* 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 + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "mail_params.h" +#include "deliver_flock.h" + +/* deliver_flock - lock open file for mail delivery*/ + +int deliver_flock(int fd, VSTRING *why) +{ + int i; + + for (i = 0; /* void */ ; i++) { + if (i >= var_flock_tries) + break; + if (i > 0) + sleep(var_flock_delay); + if (myflock(fd, MYFLOCK_EXCLUSIVE | MYFLOCK_NOWAIT) == 0) + return (0); + } + if (why) + vstring_sprintf(why, "unable to lock exclusively: %m"); + return (-1); +} diff --git a/postfix/global/deliver_flock.h b/postfix/global/deliver_flock.h new file mode 100644 index 000000000..6bb2bdceb --- /dev/null +++ b/postfix/global/deliver_flock.h @@ -0,0 +1,35 @@ +#ifndef _DELIVER_FLOCK_H_INCLUDED_ +#define _DELIVER_FLOCK_H_INCLUDED_ + +/*++ +/* NAME +/* deliver_flock 3h +/* SUMMARY +/* lock open file for mail delivery +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern int deliver_flock(int, VSTRING *); + +/* 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/deliver_request.c b/postfix/global/deliver_request.c new file mode 100644 index 000000000..0bf3319cc --- /dev/null +++ b/postfix/global/deliver_request.c @@ -0,0 +1,284 @@ +/*++ +/* NAME +/* deliver_request 3 +/* SUMMARY +/* mail delivery request protocol, server side +/* SYNOPSIS +/* #include +/* +/* typedef struct DELIVER_REQUEST { +/* .in +5 +/* char *queue_name; +/* char *queue_id; +/* long data_offset; +/* long data_size; +/* char *nexthop; +/* char *sender; +/* char *errors_to; +/* char *return_receipt; +/* long arrival_time; +/* RECIPIENT_LIST rcpt_list; +/* char *hop_status; +/* .in -5 +/* } DELIVER_REQUEST; +/* +/* DELIVER_REQUEST *deliver_request_read(stream) +/* VSTREAM *stream; +/* +/* void deliver_request_done(stream, request, status) +/* VSTREAM *stream; +/* DELIVER_REQUEST *request; +/* int status; +/* DESCRIPTION +/* This module implements the delivery agent side of the `queue manager +/* to delivery agent' protocol. In this game, the queue manager is +/* the client, while the delivery agent is the server. +/* +/* deliver_request_read() reads a client message delivery request. +/* A null result means that the client sent bad information or that +/* it went away unexpectedly. +/* +/* The \fIhop_status\fR structure member must be updated +/* by the caller when all delivery to the destination in +/* \fInexthop\fR should be deferred. The value of the +/* \fIhop_status\fR member is the reason; it is passed +/* to myfree(). +/* +/* deliver_request_done() reports the delivery status back to the +/* client, including the optional \fIhop_status\fR information, +/* and destroys the DELIVER_REQUEST structure. The result is +/* non-zero when the status could not be reported to the client. +/* DIAGNOSTICS +/* Warnings: bad data sent by the client. Fatal errors: out of +/* memory, queue file open errors. +/* SEE ALSO +/* mail_scan(3) low-level intra-mail input routines +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include "mail_queue.h" +#include "mail_proto.h" +#include "mail_open_ok.h" +#include "recipient_list.h" +#include "deliver_request.h" + +/* deliver_request_initial - send initial status code */ + +static int deliver_request_initial(VSTREAM *stream) +{ + int err; + + /* + * The master processes runs a finite number of delivery agent processes + * to handle service requests. Thus, a delivery agent process must send + * something to inform the queue manager that it is ready to receive a + * delivery request; otherwise the queue manager could block in write(). + */ + if (msg_verbose) + msg_info("deliver_request_initial: send initial status"); + mail_print(stream, "%d", 0); + if ((err = vstream_fflush(stream)) != 0) + if (msg_verbose) + msg_warn("send initial status: %m"); + return (err); +} + +/* deliver_request_final - send final delivery request status */ + +static int deliver_request_final(VSTREAM *stream, char *reason, int status) +{ + int err; + + /* + * Send the status and the optional reason. + */ + if (reason == 0) + reason = ""; + if (msg_verbose) + msg_info("deliver_request_final: send: \"%s\" %d", reason, status); + mail_print(stream, "%s %d", reason, status); + if ((err = vstream_fflush(stream)) != 0) + if (msg_verbose) + msg_warn("send final status: %m"); + + /* + * XXX Solaris UNIX-domain streams sockets are brain dead. They lose data + * when you close them immediately after writing to them. That is not how + * sockets are supposed to behave! The workaround is to wait until the + * receiver closes the connection. Calling VSTREAM_GETC() has the benefit + * of using whatever timeout is specified in the ipc_timeout parameter. + */ + (void) VSTREAM_GETC(stream); + return (err); +} + +/* deliver_request_get - receive message delivery request */ + +static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request) +{ + const char *path; + struct stat st; + static VSTRING *queue_name; + static VSTRING *queue_id; + static VSTRING *nexthop; + static VSTRING *address; + static VSTRING *errors_to; + static VSTRING *return_receipt; + long offset; + + /* + * Initialize. For some reason I wanted to allow for multiple instances + * of a deliver_request structure, thus the hoopla with string + * initialization and copying. + */ + if (queue_name == 0) { + queue_name = vstring_alloc(10); + queue_id = vstring_alloc(10); + nexthop = vstring_alloc(10); + address = vstring_alloc(10); + errors_to = vstring_alloc(10); + return_receipt = vstring_alloc(10); + } + + /* + * Extract the queue file name, data offset, and sender address. Abort + * the conversation when they send bad information. + */ + if (mail_scan(stream, "%s %s %ld %ld %s %s %s %s %ld", + queue_name, queue_id, &request->data_offset, + &request->data_size, nexthop, address, + errors_to, return_receipt, &request->arrival_time) != 9) + return (-1); + if (mail_open_ok(vstring_str(queue_name), + vstring_str(queue_id), &st, &path) == 0) + return (-1); + request->queue_name = mystrdup(vstring_str(queue_name)); + request->queue_id = mystrdup(vstring_str(queue_id)); + request->nexthop = mystrdup(vstring_str(nexthop)); + request->sender = mystrdup(vstring_str(address)); + request->errors_to = mystrdup(vstring_str(errors_to)); + request->return_receipt = mystrdup(vstring_str(return_receipt)); + + /* + * Extract the recipient offset and address list. + */ + for (;;) { + if (mail_scan(stream, "%ld", &offset) != 1) + return (-1); + if (offset == 0) + break; + if (mail_scan(stream, "%s", address) != 1) + return (-1); + recipient_list_add(&request->rcpt_list, offset, vstring_str(address)); + } + return (0); +} + +/* deliver_request_alloc - allocate delivery request structure */ + +static DELIVER_REQUEST *deliver_request_alloc(void) +{ + DELIVER_REQUEST *request; + + request = (DELIVER_REQUEST *) mymalloc(sizeof(*request)); + request->queue_name = 0; + request->queue_id = 0; + request->nexthop = 0; + request->sender = 0; + request->errors_to = 0; + request->return_receipt = 0; + request->data_offset = 0; + request->data_size = 0; + recipient_list_init(&request->rcpt_list); + request->hop_status = 0; + return (request); +} + +/* deliver_request_free - clean up delivery request structure */ + +static void deliver_request_free(DELIVER_REQUEST *request) +{ + if (request->queue_name) + myfree(request->queue_name); + if (request->queue_id) + myfree(request->queue_id); + if (request->nexthop) + myfree(request->nexthop); + if (request->sender) + myfree(request->sender); + if (request->errors_to) + myfree(request->errors_to); + if (request->return_receipt) + myfree(request->return_receipt); + recipient_list_free(&request->rcpt_list); + if (request->hop_status) + myfree(request->hop_status); + myfree((char *) request); +} + +/* deliver_request_read - create and read delivery request */ + +DELIVER_REQUEST *deliver_request_read(VSTREAM *stream) +{ + DELIVER_REQUEST *request; + + /* + * Tell the queue manager that we are ready for this request. + */ + if (deliver_request_initial(stream) != 0) + return (0); + + /* + * Be prepared for the queue manager to change its mind after contacting + * us. This can happen when a transport or host goes bad. + */ + (void) read_wait(vstream_fileno(stream), -1); + if (peekfd(vstream_fileno(stream)) <= 0) + return (0); + + /* + * Allocate and read the queue manager's delivery request. + */ + request = deliver_request_alloc(); + if (deliver_request_get(stream, request) < 0) { + deliver_request_free(request); + request = 0; + } + return (request); +} + +/* deliver_request_done - finish delivery request */ + +int deliver_request_done(VSTREAM *stream, DELIVER_REQUEST *request, int status) +{ + int err; + + err = deliver_request_final(stream, request->hop_status, status); + deliver_request_free(request); + return (err); +} diff --git a/postfix/global/deliver_request.h b/postfix/global/deliver_request.h new file mode 100644 index 000000000..d0e3ce6c1 --- /dev/null +++ b/postfix/global/deliver_request.h @@ -0,0 +1,57 @@ +#ifndef _DELIVER_REQUEST_H_INCLUDED_ +#define _DELIVER_REQUEST_H_INCLUDED_ + +/*++ +/* NAME +/* deliver_request 3h +/* SUMMARY +/* mail delivery request protocol, server side +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include + + /* + * Global library. + */ +#include + + /* + * Structure of a server mail delivery request. + */ +typedef struct DELIVER_REQUEST { + char *queue_name; /* message queue name */ + char *queue_id; /* message queue id */ + long data_offset; /* offset to message */ + long data_size; /* message size */ + char *nexthop; /* next hop name */ + char *sender; /* envelope sender */ + char *errors_to; /* error report address */ + char *return_receipt; /* confirm receipt address */ + long arrival_time; /* arrival time */ + RECIPIENT_LIST rcpt_list; /* envelope recipients */ + char *hop_status; /* reason if unavailable */ +} DELIVER_REQUEST; + +typedef struct VSTREAM _deliver_vstream_; +extern DELIVER_REQUEST *deliver_request_read(_deliver_vstream_ *); +extern int deliver_request_done(_deliver_vstream_ *, DELIVER_REQUEST *, 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/global/domain_list.c b/postfix/global/domain_list.c new file mode 100644 index 000000000..1322642d0 --- /dev/null +++ b/postfix/global/domain_list.c @@ -0,0 +1,131 @@ +/*++ +/* NAME +/* domain_list 3 +/* SUMMARY +/* match a host or domain name against a pattern list +/* SYNOPSIS +/* #include +/* +/* DOMAIN_LIST *domain_list_init(pattern_list) +/* const char *pattern_list; +/* +/* int domain_list_match(list, name) +/* DOMAIN_LIST *list; +/* const char *name; +/* +/* void domain_list_free(list) +/* DOMAIN_LIST *list; +/* DESCRIPTION +/* This module implements tests for list membership of a host or +/* domain name. +/* +/* Patterns are separated by whitespace and/or commas. A pattern +/* is either a string, a file name (in which case the contents +/* of the file are substituted for the file name) or a type:name +/* lookup table specification. +/* +/* A host name matches a domain list when its name appears in the +/* list of domain patterns, or when any of its parent domains appears +/* in the list of domain patterns. The matching process is case +/* insensitive. In order to reverse the result, precede a non-file +/* name pattern with an exclamation point (!). +/* +/* domain_list_init() performs initializations. The argument is a +/* list of domain patterns, or the name of a file containing domain +/* patterns. +/* +/* domain_list_match() matches the specified host or domain name +/* against the specified pattern list. +/* +/* domain_list_free() releases storage allocated by domain_list_init(). +/* DIAGNOSTICS +/* Fatal error: unable to open or read a domain_list file; invalid +/* domain_list pattern. +/* SEE ALSO +/* match_list(3) generic list matching +/* match_ops(3) match hosts by name or by address +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "domain_list.h" + +/* domain_list_init - initialize domain list */ + +DOMAIN_LIST *domain_list_init(const char *patterns) +{ + return (match_list_init(patterns, 1, match_hostname)); +} + +/* domain_list_match - match host against domain list */ + +int domain_list_match(DOMAIN_LIST *list, const char *name) +{ + return (match_list_match(list, name)); +} + +/* domain_list_free - release storage */ + +void domain_list_free(DOMAIN_LIST *list) +{ + match_list_free(list); +} + +#ifdef TEST + +#include +#include +#include +#include + +static void usage(char *progname) +{ + msg_fatal("usage: %s [-v] patterns hostname", progname); +} + +main(int argc, char **argv) +{ + DOMAIN_LIST *list; + char *host; + int ch; + + msg_vstream_init(argv[0], VSTREAM_ERR); + + while ((ch = GETOPT(argc, argv, "v")) > 0) { + switch (ch) { + case 'v': + msg_verbose++; + break; + default: + usage(argv[0]); + } + } + if (argc != optind + 2) + usage(argv[0]); + list = domain_list_init(argv[optind]); + host = argv[optind + 1]; + vstream_printf("%s: %s\n", host, domain_list_match(list, host) ? + "YES" : "NO"); + vstream_fflush(VSTREAM_OUT); + domain_list_free(list); +} + +#endif diff --git a/postfix/global/domain_list.h b/postfix/global/domain_list.h new file mode 100644 index 000000000..b36f516b4 --- /dev/null +++ b/postfix/global/domain_list.h @@ -0,0 +1,34 @@ +#ifndef _DOMAIN_LIST_H_INCLUDED_ +#define _DOMAIN_LIST_H_INCLUDED_ + +/*++ +/* NAME +/* domain_list 3h +/* SUMMARY +/* match a host or domain name against a pattern list +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +typedef struct MATCH_LIST DOMAIN_LIST; + +extern DOMAIN_LIST *domain_list_init(const char *); +extern int domain_list_match(DOMAIN_LIST *, const char *); +extern void domain_list_free(DOMAIN_LIST *); + +/* 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/dot_lockfile.c b/postfix/global/dot_lockfile.c new file mode 100644 index 000000000..ccedc2f90 --- /dev/null +++ b/postfix/global/dot_lockfile.c @@ -0,0 +1,158 @@ +/*++ +/* NAME +/* dot_lockfile 3 +/* SUMMARY +/* dotlock file management +/* SYNOPSIS +/* #include +/* +/* int dot_lockfile(path, why) +/* const char *path; +/* VSTRING *why; +/* +/* void dot_unlockfile(path) +/* const char *path; +/* DESCRIPTION +/* dot_lockfile() constructs a lock file name by appending ".lock" to +/* \fIpath\fR and creates the named file exclusively. It tries several +/* times and attempts to break stale locks. A negative result value +/* means no lock file could be created. +/* +/* dot_unlockfile() attempts to remove the lock file created by +/* dot_lockfile(). +/* +/* Arguments: +/* .IP path +/* Name of the file to be locked or unlocked. +/* .IP why +/* A null pointer, or storage for the reason why a lock file could +/* not be created. +/* CONFIGURATION PARAMETERS +/* deliver_lock_attempts, how many times to try to create a lock +/* deliver_lock_delay, how long to wait between attempts +/* stale_lock_time, when to break a stale lock +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include "sys_defs.h" +#include +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include "mail_params.h" +#include "dot_lockfile.h" + +/* dot_lockfile - create user.lock file */ + +int dot_lockfile(const char *path, VSTRING *why) +{ + char *lock_file; + int count; + struct stat st; + int fd; + int status = -1; + + lock_file = concatenate(path, ".lock", (char *) 0); + + for (count = 0; /* void */ ; count++) { + if (count >= var_flock_tries) + break; + if (count > 0) + sleep(var_flock_delay); + + /* + * Attempt to create the lock. This code relies on O_EXCL | O_CREAT + * to not follow symlinks. + */ + if ((fd = open(lock_file, O_WRONLY | O_EXCL | O_CREAT, 0)) >= 0) { + close(fd); + status = 0; + break; + } + + /* + * We can deal only with "file exists" errors. Any other error means + * we better give up trying. + */ + if (errno != EEXIST) + break; + + /* + * Break the lock when it is too old. Give up when we are unable to + * remove a stale lock. + */ + if (stat(lock_file, &st) == 0) + if (time((time_t *) 0) > st.st_ctime + var_flock_stale) + if (unlink(lock_file) < 0) + if (errno != ENOENT) + break; + } + if (status && why) + vstring_sprintf(why, "unable to create lock file %s: %m", lock_file); + + myfree(lock_file); + return (status); +} + +/* dot_unlockfile - remove .lock file */ + +void dot_unlockfile(const char *path) +{ + char *lock_file; + + lock_file = concatenate(path, ".lock", (char *) 0); + (void) unlink(lock_file); + myfree(lock_file); +} + +#ifdef TEST + + /* + * Test program for setting a .lock file. + * + * Usage: dot_lockfile filename + * + * Creates filename.lock and removes it. + */ +#include +#include +#include +#include + +main(int argc, char **argv) +{ + VSTRING *why = vstring_alloc(100); + + msg_vstream_init(argv[0], VSTREAM_ERR); + if (argc != 2) + msg_fatal("usage: %s file-to-be-locked", argv[0]); + read_config(); + if (dot_lockfile(argv[1], why) < 0) + msg_fatal("%s", vstring_str(why)); + dot_unlockfile(argv[1]); + vstring_free(why); + return (0); +} + +#endif diff --git a/postfix/global/dot_lockfile.h b/postfix/global/dot_lockfile.h new file mode 100644 index 000000000..1c0451041 --- /dev/null +++ b/postfix/global/dot_lockfile.h @@ -0,0 +1,36 @@ +#ifndef _DOT_LOCKFILE_H_INCLUDED_ +#define _DOT_LOCKFILE_H_INCLUDED_ + +/*++ +/* NAME +/* dot_lockfile 3h +/* SUMMARY +/* dotlock file management +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern int dot_lockfile(const char *, VSTRING *); +extern void dot_unlockfile(const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/file_id.c b/postfix/global/file_id.c new file mode 100644 index 000000000..ad4ce5798 --- /dev/null +++ b/postfix/global/file_id.c @@ -0,0 +1,77 @@ +/*++ +/* NAME +/* file_id 3 +/* SUMMARY +/* file ID printable representation +/* SYNOPSIS +/* #include +/* +/* const char *get_file_id(fd) +/* int fd; +/* +/* int check_file_id(fd, id) +/* int fd; +/* const char *id; +/* DESCRIPTION +/* get_file_id() queries the operating system for the unique identifier +/* for the specified file and returns a printable representation. +/* The result is volatile. Make a copy if it is to be used for any +/* appreciable amount of time. +/* +/* check_file_id() tests if an open file matches the given +/* printable FILE ID representation. +/* +/* Arguments: +/* .IP fd +/* A valid file descriptor that is associated with an open file. +/* .IP id +/* Printable file ID. +/* DIAGNOSTICS +/* All errors are fatal. +/* 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 + +/* Utility library */ + +#include +#include + +/* Global library. */ + +#include "file_id.h" + +/* get_file_id - lookup file ID, convert to printable form */ + +const char *get_file_id(int fd) +{ + static VSTRING *result; + struct stat st; + + if (result == 0) + result = vstring_alloc(1); + if (fstat(fd, &st) < 0) + msg_fatal("fstat: %m"); + vstring_sprintf(result, "%X", (int) st.st_ino); + return (vstring_str(result)); +} + +/* check_file_id - make sure file name matches ID */ + +int check_file_id(int fd, const char *name) +{ + return (strcmp(get_file_id(fd), name)); +} diff --git a/postfix/global/file_id.h b/postfix/global/file_id.h new file mode 100644 index 000000000..c954e36bc --- /dev/null +++ b/postfix/global/file_id.h @@ -0,0 +1,30 @@ +#ifndef _FILE_ID_H_INCLUDED_ +#define _FILE_ID_H_INCLUDED_ + +/*++ +/* NAME +/* file_id 3h +/* SUMMARY +/* file ID printable representation +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern const char *get_file_id(int); +extern int check_file_id(int, const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/header_opts.c b/postfix/global/header_opts.c new file mode 100644 index 000000000..91b102a80 --- /dev/null +++ b/postfix/global/header_opts.c @@ -0,0 +1,122 @@ +/*++ +/* NAME +/* header_opts 3 +/* SUMMARY +/* message header classification +/* SYNOPSIS +/* #include +/* +/* HEADER_OPTS *header_opts_find(string) +/* const char *string; +/* DESCRIPTION +/* header_opts_find() takes a message header line and looks up control +/* information for the corresponding header type. +/* DIAGNOSTICS +/* Panic: input is not a valid header line. The result is a pointer +/* to HEADER_OPTS in case of success, a null pointer when the header +/* label was not recognized. +/* SEE ALSO +/* header_opts(3h) the gory details +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include "header_opts.h" + + /* + * Header names are given in the preferred capitalization. The lookups are + * case-insensitive. + */ +static HEADER_OPTS header_opts[] = { + "Apparently-To", HDR_APPARENTLY_TO, HDR_OPT_RECIP, + "Bcc", HDR_BCC, HDR_OPT_DROP | HDR_OPT_XRECIP, + "Cc", HDR_CC, HDR_OPT_XRECIP, + "Delivered-To", HDR_DELIVERED_TO, 0, + "Date", HDR_DATE, 0, + "Errors-To", HDR_ERRORS_TO, HDR_OPT_RECIP, + "From", HDR_FROM, HDR_OPT_SENDER, + "Message-Id", HDR_MESSAGE_ID, 0, + "Received", HDR_RECEIVED, 0, + "Reply-To", HDR_REPLY_TO, HDR_OPT_RECIP, + "Resent-Bcc", HDR_RESENT_BCC, HDR_OPT_DROP | HDR_OPT_XRECIP | HDR_OPT_RR, + "Resent-Cc", HDR_RESENT_CC, HDR_OPT_XRECIP | HDR_OPT_RR, + "Resent-Date", HDR_RESENT_DATE, HDR_OPT_RR, + "Resent-From", HDR_RESENT_FROM, HDR_OPT_SENDER | HDR_OPT_RR, + "Resent-Message-Id", HDR_RESENT_MESSAGE_ID, HDR_OPT_RR, + "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-Receipt-To", HDR_RETURN_RECEIPT_TO, HDR_OPT_RECIP, + "Sender", HDR_SENDER, HDR_OPT_SENDER, + "To", HDR_TO, HDR_OPT_XRECIP, +}; + +#define HEADER_OPTS_SIZE (sizeof(header_opts) / sizeof(header_opts[0])) + +static HTABLE *header_hash; /* quick lookup */ +static VSTRING *header_key; + +/* header_opts_init - initialize */ + +static void header_opts_init(void) +{ + HEADER_OPTS *hp; + const char *cp; + + /* + * Build a hash table for quick lookup, and allocate memory for + * lower-casing the lookup key. + */ + header_key = vstring_alloc(10); + header_hash = htable_create(HEADER_OPTS_SIZE); + for (hp = header_opts; hp < header_opts + HEADER_OPTS_SIZE; hp++) { + VSTRING_RESET(header_key); + for (cp = hp->name; *cp; cp++) + VSTRING_ADDCH(header_key, TOLOWER(*cp)); + VSTRING_TERMINATE(header_key); + htable_enter(header_hash, vstring_str(header_key), (char *) hp); + } +} + +/* header_opts_find - look up header options */ + +HEADER_OPTS *header_opts_find(const char *string) +{ + const char *cp; + + if (header_hash == 0) + header_opts_init(); + + /* + * Look up the lower-cased version of the header name. + */ + VSTRING_RESET(header_key); + for (cp = string; *cp != ':'; cp++) { + if (*cp == 0) + msg_panic("header_opts_find: no colon in header: %.30s", string); + VSTRING_ADDCH(header_key, TOLOWER(*cp)); + } + VSTRING_TERMINATE(header_key); + return ((HEADER_OPTS *) htable_find(header_hash, vstring_str(header_key))); +} diff --git a/postfix/global/header_opts.h b/postfix/global/header_opts.h new file mode 100644 index 000000000..27c44ddbe --- /dev/null +++ b/postfix/global/header_opts.h @@ -0,0 +1,76 @@ +#ifndef _HEADER_OPTS_H_INCLUDED_ +#define _HEADER_OPTS_H_INCLUDED_ + +/*++ +/* NAME +/* header_opts 3h +/* SUMMARY +/* message header classification +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +typedef struct { + const char *name; /* name, preferred capitalization */ + int type; /* type, see below */ + int flags; /* flags, see below */ +} HEADER_OPTS; + + /* + * Header types. If we reach 31, we must group the headers we need to + * remember at the beginning, or we should use fd_set bit sets. + */ +#define HDR_APPARENTLY_TO 1 +#define HDR_BCC 2 +#define HDR_CC 3 +#define HDR_CONTENT_LENGTH 4 +#define HDR_CONTENT_TRANSFER_ENCODING 5 +#define HDR_CONTENT_TYPE 6 +#define HDR_DATE 7 +#define HDR_DELIVERED_TO 8 +#define HDR_ERRORS_TO 9 +#define HDR_FROM 10 +#define HDR_MESSAGE_ID 11 +#define HDR_RECEIVED 12 +#define HDR_REPLY_TO 13 +#define HDR_RESENT_BCC 14 +#define HDR_RESENT_CC 15 +#define HDR_RESENT_DATE 16 +#define HDR_RESENT_FROM 17 +#define HDR_RESENT_MESSAGE_ID 18 +#define HDR_RESENT_REPLY_TO 19 +#define HDR_RESENT_SENDER 20 +#define HDR_RESENT_TO 21 +#define HDR_RETURN_PATH 22 +#define HDR_RETURN_RECEIPT_TO 23 +#define HDR_SENDER 24 +#define HDR_TO 25 + + /* + * Header flags. + */ +#define HDR_OPT_DROP (1<<0) /* delete from input */ +#define HDR_OPT_SENDER (1<<1) /* sender address */ +#define HDR_OPT_RECIP (1<<2) /* recipient address */ +#define HDR_OPT_RR (1<<3) /* Resent- header */ +#define HDR_OPT_EXTRACT (1<<4) /* extract flag */ + +#define HDR_OPT_XRECIP (HDR_OPT_RECIP | HDR_OPT_EXTRACT) + +extern HEADER_OPTS *header_opts_find(const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/is_header.c b/postfix/global/is_header.c new file mode 100644 index 000000000..0cd3e93c2 --- /dev/null +++ b/postfix/global/is_header.c @@ -0,0 +1,51 @@ +/*++ +/* NAME +/* is_header 3 +/* SUMMARY +/* message header classification +/* SYNOPSIS +/* #include +/* +/* int is_header(string) +/* const char *string; +/* DESCRIPTION +/* is_header() examines the given string and returns non-zero (true) +/* when it begins with a mail header name + colon. This routine +/* permits 8-bit data in header labels. +/* STANDARDS +/* RFC 822 (ARPA Internet Text Messages) +/* 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 + +/* Global library. */ + +#include "is_header.h" + +/* is_header - determine if this can be a header line */ + +int is_header(const char *str) +{ + const char *cp; + int c; + + for (cp = str; (c = *(unsigned char *) cp) != 0; cp++) { + if (c == ':') + return (cp > str); + if ( /* !ISASCII(c) || */ ISSPACE(c) || ISCNTRL(c)) + break; + } + return (0); +} diff --git a/postfix/global/is_header.h b/postfix/global/is_header.h new file mode 100644 index 000000000..0fc6bd38b --- /dev/null +++ b/postfix/global/is_header.h @@ -0,0 +1,29 @@ +#ifndef _IS_HEADER_H_INCLUDED_ +#define _IS_HEADER_H_INCLUDED_ + +/*++ +/* NAME +/* is_header 3h +/* SUMMARY +/* message header classification +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern int is_header(const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/mail_addr.c b/postfix/global/mail_addr.c new file mode 100644 index 000000000..9ea3cdad3 --- /dev/null +++ b/postfix/global/mail_addr.c @@ -0,0 +1,96 @@ +/*++ +/* NAME +/* mail_addr 3 +/* SUMMARY +/* pre-defined mail addresses +/* SYNOPSIS +/* #include +/* +/* const char *mail_addr_double_bounce() +/* +/* const char *mail_addr_postmaster() +/* +/* const char *mail_addr_mail_daemon() +/* DESCRIPTION +/* This module predefines the following addresses: +/* .IP MAIL_ADDR_POSTMASTER +/* The postmaster handle. Typically used for sending mail to. +/* .IP MAIL_ADDR_MAIL_DAEMON +/* The mailer-daemon handle. Typically used to bring bad news. +/* .IP MAIL_ADDR_EMPTY +/* The empty mail address. This refers to the postmaster on the +/* local machine. +/* .PP +/* mail_addr_double_bounce() produces the fully-qualified version +/* of the local double bounce address. +/* +/* mail_addr_postmaster() produces the fully-qualified version +/* of the local postmaster address. +/* +/* mail_addr_mail_daemon() produces the fully-qualified version +/* of the local mailer-daemon address. +/* CONFIGURATION PARAMETERS +/* double_bounce_sender, the double bounce pseudo account. +/* myhostname, the local machine hostname. +/* BUGS +/* Addresses are constructed by string concatenation, instead of +/* passing them to the rewriting service. +/* 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 + +/* Global library. */ + +#include "mail_params.h" +#include "mail_addr.h" + +/* mail_addr_double_bounce - construct the local double-bounce address */ + +const char *mail_addr_double_bounce(void) +{ + static char *addr; + + if (addr == 0) + addr = concatenate(var_double_bounce_sender, "@", + var_myhostname, (char *) 0); + return (addr); +} + +/* mail_addr_postmaster - construct the local postmaster address */ + +const char *mail_addr_postmaster(void) +{ + static char *addr; + + if (addr == 0) + addr = concatenate(MAIL_ADDR_POSTMASTER, "@", + var_myhostname, (char *) 0); + return (addr); +} + +/* mail_addr_mail_daemon - construct the local mailer-daemon address */ + +const char *mail_addr_mail_daemon(void) +{ + static char *addr; + + if (addr == 0) + addr = concatenate(MAIL_ADDR_MAIL_DAEMON, "@", + var_myhostname, (char *) 0); + return (addr); +} diff --git a/postfix/global/mail_addr.h b/postfix/global/mail_addr.h new file mode 100644 index 000000000..f06a754d4 --- /dev/null +++ b/postfix/global/mail_addr.h @@ -0,0 +1,36 @@ +#ifndef _MAIL_ADDR_H_INCLUDED_ +#define _MAIL_ADDR_H_INCLUDED_ + +/*++ +/* NAME +/* mail_addr 3h +/* SUMMARY +/* pre-defined mail addresses +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Pre-defined addresses. + */ +#define MAIL_ADDR_POSTMASTER "postmaster" +#define MAIL_ADDR_MAIL_DAEMON "MAILER-DAEMON" +#define MAIL_ADDR_EMPTY "" + +extern const char *mail_addr_double_bounce(void); +extern const char *mail_addr_postmaster(void); +extern const char *mail_addr_mail_daemon(void); + +/* 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_addr_crunch.c b/postfix/global/mail_addr_crunch.c new file mode 100644 index 000000000..e7e7118bf --- /dev/null +++ b/postfix/global/mail_addr_crunch.c @@ -0,0 +1,142 @@ +/*++ +/* NAME +/* mail_addr_crunch 3 +/* SUMMARY +/* parse and canonicalize addresses, apply address extension +/* SYNOPSIS +/* #include +/* +/* ARGV *mail_addr_crunch(string, extension) +/* const char *string; +/* const char *extension; +/* DESCRIPTION +/* mail_addr_crunch() parses a string with zero or more addresses, +/* rewrites each address to canonical form, and optionally applies +/* an address extension to each resulting address. Input and result +/* are in external (quoted) format. The caller is expected to pass +/* the result to argv_free(). +/* +/* Arguments: +/* .IP string +/* A string with zero or more addresses in RFC 822 (external) format. +/* .IP extension +/* A null pointer, or an address extension (including the recipient +/* address delimiter) that is propagated to all result addresses. +/* DIAGNOSTICS +/* Fatal error: out of memory. +/* SEE ALSO +/* tok822_parse(3), address parser +/* canon_addr(3), address canonicalization +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include +#include +#include + +/* mail_addr_crunch - break string into addresses, optionally add extension */ + +ARGV *mail_addr_crunch(const char *string, const char *extension) +{ + VSTRING *buf = vstring_alloc(100); + ARGV *argv = argv_alloc(1); + TOK822 *tree; + TOK822 **addr_list; + TOK822 **tpp; + char *ratsign; + int extlen; + + if (extension) + extlen = strlen(extension); + +#define STR(x) vstring_str(x) + + /* + * Parse the string, rewrite each address to canonical form, and convert + * the result to external (quoted) form. Optionally apply the extension + * to each address found. + */ + 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)); + if (extension) { + if ((ratsign = strrchr(STR(buf), '@')) == 0) { + vstring_strcat(buf, extension); + } else { + VSTRING_SPACE(buf, extlen + 1); + memmove(ratsign + extlen, ratsign, strlen(ratsign) + 1); + memcpy(ratsign, extension, extlen); + VSTRING_SKIP(buf); + } + } + argv_add(argv, STR(buf), ARGV_END); + } + argv_terminate(argv); + myfree((char *) addr_list); + tok822_free_tree(tree); + vstring_free(buf); + return (argv); +} + +#ifdef TEST + + /* + * Stand-alone test program, sort of interactive. + */ +#include +#include +#include +#include +#include +#include + +int main(int unused_argc, char **unused_argv) +{ + VSTRING *extension = vstring_alloc(1); + VSTRING *buf = vstring_alloc(1); + ARGV *argv; + char **cpp; + + read_config(); + if (chdir(var_queue_dir) < 0) + msg_fatal("chdir %s: %m", var_queue_dir); + + vstream_printf("extension: (CR for none): "); + vstream_fflush(VSTREAM_OUT); + if (vstring_get_nonl(extension, VSTREAM_IN) == VSTREAM_EOF) + exit(0); + + vstream_printf("print strings to be translated, one per line\n"); + vstream_fflush(VSTREAM_OUT); + while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) { + argv = mail_addr_crunch(STR(buf), VSTRING_LEN(extension) ? STR(extension) : 0); + for (cpp = argv->argv; *cpp; cpp++) + vstream_printf(" %s\n", *cpp); + vstream_fflush(VSTREAM_OUT); + } +} + +#endif diff --git a/postfix/global/mail_addr_crunch.h b/postfix/global/mail_addr_crunch.h new file mode 100644 index 000000000..2c5eb3375 --- /dev/null +++ b/postfix/global/mail_addr_crunch.h @@ -0,0 +1,35 @@ +#ifndef _MAIL_ADDR_CRUNCH_H_INCLUDED_ +#define _MAIL_ADDR_CRUNCH_H_INCLUDED_ + +/*++ +/* NAME +/* mail_addr_crunch 3h +/* SUMMARY +/* parse and canonicalize addresses, apply address extension +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern ARGV *mail_addr_crunch(const char *, const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/mail_addr_find.c b/postfix/global/mail_addr_find.c new file mode 100644 index 000000000..eac168258 --- /dev/null +++ b/postfix/global/mail_addr_find.c @@ -0,0 +1,226 @@ +/*++ +/* NAME +/* mail_addr_find 3 +/* SUMMARY +/* generic address-based lookup +/* SYNOPSIS +/* #include +/* +/* const char *mail_addr_find(maps, address, extension) +/* MAPS *maps; +/* const char *address; +/* char **extension; +/* DESCRIPTION +/* mail_addr_find() searches the specified maps for an entry with as +/* key the specified address, and derivations from that address. +/* The search is case insensitive. +/* The result is overwritten upon each call. +/* +/* An address that is in the form \fIuser\fR matches itself. +/* +/* Given an address of the form \fIuser@domain\fR, the following +/* lookups are done in the given order until one returns a result: +/* .IP user@domain +/* Look up the entire address. +/* .IP user +/* Look up \fIuser\fR when \fIdomain\fR is equal to $myorigin, +/* when \fIdomain\fR matches $mydestination, or when it matches +/* $inet_interfaces. +/* .IP @domain +/* Look for an entry that matches the domain specified in \fIaddress\fR. +/* .PP +/* With address extension enabled, the table lookup order is: +/* \fIuser+extension\fR@\fIdomain\fR, \fIuser\fR@\fIdomain\fR, +/* \fIuser+extension\fR, \fIuser\fR, and @\fIdomain\fR. +/* .PP +/* Arguments: +/* .IP maps +/* Dictionary search path (see maps(3)). +/* .IP address +/* The address to be looked up. +/* .IP extension +/* A null pointer, or the address of a pointer that is set to +/* the address of a dynamic memory copy of the address extension +/* that had to be chopped off in order to match the lookup tables. +/* The copy includes the recipient address delimiter. +/* The caller is expected to pass the copy to myfree(). +/* DIAGNOSTICS +/* The global \fIdict_errno\fR is non-zero when the lookup +/* should be tried again. +/* SEE ALSO +/* maps(3), multi-dictionary search +/* resolve_local(3), recognize local system +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +#define STR vstring_str + +/* mail_addr_find - map a canonical address */ + +const char *mail_addr_find(MAPS *path, const char *address, char **extp) +{ + char *myname = "mail_addr_find"; + const char *result; + char *ratsign = 0; + char *full_key; + char *extent; + char *bare_key; + char *saved_ext; + + /* + * Initialize. + */ + full_key = lowercase(mystrdup(address)); + if (*var_rcpt_delim == 0) { + bare_key = saved_ext = 0; + } else { + bare_key = mystrdup(full_key); + if ((ratsign = strrchr(bare_key, '@')) != 0) + *ratsign = 0; + if ((extent = split_addr(bare_key, *var_rcpt_delim)) != 0) { + extent -= 1; + *extent = *var_rcpt_delim; /* XXX this is unclean */ + saved_ext = mystrdup(extent); /* XXX maybe omit delimiter ? */ + if (ratsign != 0) { + *ratsign = '@'; + memmove(extent, ratsign, strlen(ratsign) + 1); + } + } else { + myfree(bare_key); + bare_key = saved_ext = 0; + } + } + + /* + * Try user+foo@domain and user@domain. + */ + if ((result = maps_find(path, full_key)) == 0 && dict_errno == 0 + && bare_key != 0 && (result = maps_find(path, bare_key)) != 0 + && extp != 0) { + *extp = saved_ext; + saved_ext = 0; + } + + /* + * Try user+foo@$myorigin, user+foo@$mydestination or + * user+foo@[$inet_interfaces]. Then try with +foo stripped off. + */ + if (result == 0 && dict_errno == 0 + && (ratsign = strrchr(full_key, '@')) != 0 + && (strcasecmp(ratsign + 1, var_myorigin) == 0 + || resolve_local(ratsign + 1))) { + *ratsign = 0; + result = maps_find(path, full_key); + if (result == 0 && dict_errno == 0 && bare_key != 0) { + if ((ratsign = strrchr(bare_key, '@')) == 0) + msg_panic("%s: bare key botch", myname); + *ratsign = 0; + if ((result = maps_find(path, bare_key)) != 0 && extp != 0) { + *extp = saved_ext; + saved_ext = 0; + } + } + *ratsign = '@'; + } + + /* + * Try @domain. + */ + if (result == 0 && dict_errno == 0 && ratsign) + result = maps_find(path, ratsign); + + /* + * Clean up. + */ + if (msg_verbose) + msg_info("%s: %s -> %s", myname, address, + result ? result : + dict_errno ? "(try again)" : + "(not found)"); + myfree(full_key); + if (bare_key) + myfree(bare_key); + if (saved_ext) + myfree(saved_ext); + + return (result); +} + +#ifdef TEST + + /* + * Proof-of-concept test program. Read an address from stdin, and spit out + * the lookup result. + */ +#include +#include +#include + +int main(int argc, char **argv) +{ + VSTRING *buffer = vstring_alloc(100); + MAPS *path; + const char *result; + char *extent; + + /* + * Parse JCL. + */ + if (argc != 2) + msg_fatal("usage: %s database", argv[0]); + + /* + * Initialize. + */ + read_config(); + path = maps_create(argv[0], argv[1]); + while (vstring_fgets_nonl(buffer, VSTREAM_IN)) { + extent = 0; + result = mail_addr_find(path, STR(buffer), &extent); + vstream_printf("%s -> %s (%s)\n", STR(buffer), result ? result : + dict_errno ? "(try again)" : + "(not found)", extent ? extent : "null"); + vstream_fflush(VSTREAM_OUT); + if (extent) + myfree(extent); + } + vstring_free(buffer); + + maps_free(path); +} + +#endif diff --git a/postfix/global/mail_addr_find.h b/postfix/global/mail_addr_find.h new file mode 100644 index 000000000..a82246a87 --- /dev/null +++ b/postfix/global/mail_addr_find.h @@ -0,0 +1,35 @@ +#ifndef _MAIL_ADDR_FIND_H_INCLUDED_ +#define _MAIL_ADDR_FIND_H_INCLUDED_ + +/*++ +/* NAME +/* mail_addr_find 3h +/* SUMMARY +/* generic address-based lookup +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Global library. + */ +#include + + /* + * External interface. + */ +extern const char *mail_addr_find(MAPS *, const char *, char **); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/mail_addr_map.c b/postfix/global/mail_addr_map.c new file mode 100644 index 000000000..00fba564e --- /dev/null +++ b/postfix/global/mail_addr_map.c @@ -0,0 +1,171 @@ +/*++ +/* NAME +/* mail_addr_map 3 +/* SUMMARY +/* generic address mapping +/* SYNOPSIS +/* #include +/* +/* ARGV *mail_addr_map(path, address) +/* MAPS *path; +/* const char *address; +/* DESCRIPTION +/* mail_addr_map() returns the translation for the named address, +/* or a null pointer if none is found. The result is in canonical +/* external (quoted) form. The search is case insensitive. +/* +/* Address extensions that aren't explicitly matched in the lookup +/* table are propagated to the result addresses. The caller is +/* expected to pass the result to argv_free(). +/* +/* Lookups are performed by mail_addr_find(). When the result has the +/* form \fI@otherdomain\fR, the result is the original user in +/* \fIotherdomain\fR. +/* +/* Arguments: +/* .IP path +/* Dictionary search path (see maps(3)). +/* .IP address +/* The address to be looked up. +/* DIAGNOSTICS +/* The global \fIdict_errno\fR is non-zero when the lookup +/* should be tried again. +/* SEE ALSO +/* mail_addr_find(3), mail address matching +/* mail_addr_crunch(3), mail address parsing and rewriting +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include + +/* Application-specific. */ + +#define STR vstring_str + +/* mail_addr_map - map a canonical address */ + +ARGV *mail_addr_map(MAPS *path, const char *address) +{ + VSTRING *buffer = 0; + char *myname = "mail_addr_map"; + const char *string; + char *ratsign; + char *extension = 0; + ARGV *argv = 0; + int i; + + /* + * Look up the full address; if no match is found, look up the address + * with the extension stripped off, and remember the unmatched extension. + */ + if ((string = mail_addr_find(path, address, &extension)) != 0) { + + /* + * Prepend the original user to @otherdomain. + */ + if (*string == '@') { + buffer = vstring_alloc(100); + if ((ratsign = strchr(address, '@')) != 0) + vstring_strncpy(buffer, address, ratsign - address); + else + vstring_strcpy(buffer, address); + vstring_strcat(buffer, string); + string = STR(buffer); + } + + /* + * Canonicalize and externalize the result, and propagate the + * unmatched extension to each address found. + */ + argv = mail_addr_crunch(string, extension); + if (buffer) + vstring_free(buffer); + if (msg_verbose) + for (i = 0; i < argv->argc; i++) + msg_info("%s: %s -> %d: %s", myname, address, i, argv->argv[i]); + } + + /* + * No match found. + */ + else { + if (msg_verbose) + msg_info("%s: %s -> %s", myname, address, + dict_errno ? "(try again)" : "(not found)"); + } + + /* + * Cleanup. + */ + if (extension) + myfree(extension); + + return (argv); +} + +#ifdef TEST + + /* + * Proof-of-concept test program. Read an address from stdin, and spit out + * the lookup result. + */ +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + VSTRING *buffer = vstring_alloc(100); + MAPS *path; + ARGV *result; + + /* + * Parse JCL. + */ + if (argc != 2) + msg_fatal("usage: %s database", argv[0]); + + /* + * Initialize. + */ + read_config(); + msg_verbose = 1; + if (chdir(var_queue_dir) < 0) + msg_fatal("chdir %s: %m", var_queue_dir); + path = maps_create(argv[0], argv[1]); + while (vstring_fgets_nonl(buffer, VSTREAM_IN)) { + if ((result = mail_addr_map(path, STR(buffer))) != 0) + argv_free(result); + } + vstring_free(buffer); + maps_free(path); +} + +#endif diff --git a/postfix/global/mail_addr_map.h b/postfix/global/mail_addr_map.h new file mode 100644 index 000000000..bfc07c07c --- /dev/null +++ b/postfix/global/mail_addr_map.h @@ -0,0 +1,40 @@ +#ifndef _MAIL_ADDR_MAP_H_INCLUDED_ +#define _MAIL_ADDR_MAP_H_INCLUDED_ + +/*++ +/* NAME +/* mail_addr_map 3h +/* SUMMARY +/* generic address mapping +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * Global library. + */ +#include + + /* + * External interface. + */ +extern ARGV *mail_addr_map(MAPS *, const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/mail_command_read.c b/postfix/global/mail_command_read.c new file mode 100644 index 000000000..74c7def61 --- /dev/null +++ b/postfix/global/mail_command_read.c @@ -0,0 +1,69 @@ +/*++ +/* NAME +/* mail_command_read 3 +/* SUMMARY +/* single-command server +/* SYNOPSIS +/* #include +/* +/* int mail_command_read(stream, format, ...) +/* VSTREAM *stream; +/* char *format; +/* DESCRIPTION +/* This module implements the server interface for single-command +/* requests: a clients sends a single command and expects a single +/* completion status code. +/* +/* Arguments: +/* .IP stream +/* Server endpoint. +/* .IP format +/* Format string understood by mail_print(3) and mail_scan(3). +/* DIAGNOSTICS +/* Fatal: out of memory. +/* SEE ALSO +/* mail_scan(3) +/* mail_command_write(3) client interface +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include /* 44BSD stdarg.h uses abort() */ +#include +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "mail_proto.h" + +/* mail_command_read - read single-command request */ + +int mail_command_read(VSTREAM *stream, char *fmt,...) +{ + VSTRING *eof = vstring_alloc(10); + va_list ap; + int count; + + va_start(ap, fmt); + count = mail_vscan(stream, fmt, ap); + va_end(ap); + if (mail_scan(stream, "%s", eof) != 1 || strcmp(vstring_str(eof), MAIL_EOF)) + count = -1; + vstring_free(eof); + return (count); +} diff --git a/postfix/global/mail_command_write.c b/postfix/global/mail_command_write.c new file mode 100644 index 000000000..721a96464 --- /dev/null +++ b/postfix/global/mail_command_write.c @@ -0,0 +1,80 @@ +/*++ +/* NAME +/* mail_command_write 3 +/* SUMMARY +/* single-command client +/* SYNOPSIS +/* #include +/* +/* int mail_command_write(class, name, format, ...) +/* const char *class; +/* const char *name; +/* const char *format; +/* DESCRIPTION +/* This module implements a client interface for single-command +/* clients: a client that sends a single command and expects +/* a single completion status code. +/* +/* Arguments: +/* .IP class +/* Service type: MAIL_CLASS_PUBLIC or MAIL_CLASS_PRIVATE +/* .IP name +/* Service name (master.cf). +/* .IP format +/* Format string understood by mail_print(3). +/* DIAGNOSTICS +/* The result is zero in case of success, non-zero otherwise. +/* Warnings: problems connecting to the requested service. +/* Fatal: out of memory. +/* SEE ALSO +/* mail_command_read(3), server interface +/* mail_proto(5h), client-server protocol +/* 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 /* 44BSD stdarg.h uses abort() */ +#include + +/* Utility library. */ + +#include + +/* Global library. */ + +#include "mail_proto.h" + +/* mail_command_write - single-command transaction with completion status */ + +int mail_command_write(const char *class, const char *name, + const char *fmt,...) +{ + va_list ap; + VSTREAM *stream; + int status; + + /* + * Talk a little protocol with the specified service. + */ + if ((stream = mail_connect(class, name, BLOCKING)) == 0) + return (1); + va_start(ap, fmt); + status = mail_vprint(stream, fmt, ap); + va_end(ap); + status |= mail_print(stream, "%s", MAIL_EOF); + status |= vstream_fflush(stream); + if (mail_scan(stream, "%d", &status) != 1) + status = -1; + (void) vstream_fclose(stream); + return (status); +} diff --git a/postfix/global/mail_connect.c b/postfix/global/mail_connect.c new file mode 100644 index 000000000..8b9223a5a --- /dev/null +++ b/postfix/global/mail_connect.c @@ -0,0 +1,118 @@ +/*++ +/* NAME +/* mail_connect 3 +/* SUMMARY +/* intra-mail system connection management +/* SYNOPSIS +/* #include +/* +/* VSTREAM *mail_connect(class, name, block_mode) +/* const char *class; +/* const char *name; +/* int block_mode; +/* +/* VSTREAM *mail_connect_wait(class, name) +/* const char *class; +/* const char *name; +/* DESCRIPTION +/* This module does low-level connection management for intra-mail +/* communication. All reads and writes are subject to a time limit +/* (controlled by the global variable \fIvar_ipc_timeout\fR). This +/* protects against deadlock conditions that should never happen. +/* +/* mail_connect() attempts to connect to the UNIX-domain socket of +/* the named subsystem. The result is a null pointer in case of failure. +/* +/* mail_connect_wait() is like mail_connect(), but keeps trying until +/* the connection succeeds. However, mail_connect_wait() terminates +/* with a fatal error when the service is down. This is to ensure that +/* processes terminate when the mail system shuts down. +/* +/* Arguments: +/* .IP class +/* Name of a class of local transport channel endpoints, +/* either \fIpublic\fR (accessible by any local user) or +/* \fIprivate\fR (administrative access only). +/* .IP service +/* The name of a local transport endpoint within the named class. +/* .IP block_mode +/* NON_BLOCKING for a non-blocking connection, or BLOCKING. +/* SEE ALSO +/* timed_ipc(3), enforce IPC timeouts. +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include "timed_ipc.h" +#include "mail_proto.h" + +/* mail_connect - connect to mail subsystem */ + +VSTREAM *mail_connect(const char *class, const char *name, int block_mode) +{ + char *path; + VSTREAM *stream; + int fd; + + path = mail_pathname(class, name); + if ((fd = unix_connect(path, block_mode, 0)) < 0) { + if (msg_verbose) + msg_info("connect to subsystem %s: %m", path); + stream = 0; + } else { + if (msg_verbose) + msg_info("connect to subsystem %s", path); + stream = vstream_fdopen(fd, O_RDWR); + timed_ipc_setup(stream); + vstream_control(stream, + VSTREAM_CTL_PATH, path, + VSTREAM_CTL_END); + } + myfree(path); + return (stream); +} + +/* mail_connect_wait - connect to mail service until it succeeds */ + +VSTREAM *mail_connect_wait(const char *class, const char *name) +{ + VSTREAM *stream; + int count = 0; + + /* + * XXX Solaris workaround for ECONNREFUSED on a busy socket. + */ + while ((stream = mail_connect(class, name, BLOCKING)) == 0) { + if (errno == ECONNREFUSED || errno == ENOENT) + (count++ >= 10 ? msg_fatal : msg_warn) + ("connect #%d to subsystem %s/%s: %m", count, class, name); + sleep(10); /* XXX make configurable */ + } + return (stream); +} diff --git a/postfix/global/mail_copy.c b/postfix/global/mail_copy.c new file mode 100644 index 000000000..02cace24f --- /dev/null +++ b/postfix/global/mail_copy.c @@ -0,0 +1,202 @@ +/*++ +/* NAME +/* mail_copy 3 +/* SUMMARY +/* copy message with extreme prejudice +/* SYNOPSIS +/* #include +/* +/* int mail_copy(sender, delivered, src, dst, flags, why) +/* const char *sender; +/* const char *delivered; +/* VSTREAM *src; +/* VSTREAM *dst; +/* int flags; +/* VSTRING *why; +/* DESCRIPTION +/* mail_copy() copies a mail message from record stream to stream-lf +/* stream, and attempts to detect all possible I/O errors. +/* +/* Arguments: +/* .IP sender +/* The sender envelope address. +/* .IP delivered +/* Null pointer or delivered-to: header address. +/* .IP src +/* The source record stream, positioned at the beginning of the +/* message contents. +/* .IP dst +/* The destination byte stream (in stream-lf format). If the message +/* ends in an incomplete line, a newline character is appended to +/* the output. +/* .IP flags +/* The binary OR of zero or more of the following: +/* .RS +/* .IP MAIL_COPY_QUOTE +/* prepend a `>' character to lines beginning with `From '. +/* .IP MAIL_COPY_TOFILE +/* On systems that support this, use fsync() to flush the +/* data to stable storage, and truncate the destination +/* file to its original length in case of problems. +/* .IP MAIL_COPY_FROM +/* Prepend a UNIX-style From_ line to the message, and append +/* an empty line to the end of the message. +/* .IP MAIL_COPY_DELIVERED +/* Prepend a Delivered-To: header with the name of the +/* \fIdelivered\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. +/* Use MAIL_COPY_NONE to copy a message without any options enabled. +/* .IP why +/* A null pointer, or storage for the reason of failure. +/* DIAGNOSTICS +/* A non-zero result means the operation failed. Warnings: corrupt +/* message file. A corrupt message is marked as corrupt. +/* SEE ALSO +/* mark_corrupt(3), mark queue file as corrupted. +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include "quote_822_local.h" +#include "record.h" +#include "rec_type.h" +#include "mail_queue.h" +#include "mail_addr.h" +#include "mail_copy.h" +#include "mark_corrupt.h" + +/* mail_copy - copy message with extreme prejudice */ + +int mail_copy(const char *sender, const char *delivered, + VSTREAM *src, VSTREAM *dst, + int flags, VSTRING *why) +{ + char *myname = "mail_copy"; + VSTRING *buf; + char *bp; + long orig_length; + int read_error; + int write_error; + int corrupt_error = 0; + time_t now; + int type; + int prev_type; + + /* + * Initialize. + */ +#ifndef NO_TRUNCATE + if ((flags & MAIL_COPY_TOFILE) != 0) + if ((orig_length = vstream_fseek(dst, 0L, SEEK_END)) < 0) + msg_fatal("seek file %s: %m", VSTREAM_PATH(dst)); +#endif + buf = vstring_alloc(100); + + /* + * Prepend a bunch of headers to the message. + */ + if (flags & MAIL_COPY_FROM) { + 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))); + } + if (flags & MAIL_COPY_DELIVERED) { + if (delivered == 0) + msg_panic("%s: null delivered", myname); + quote_822_local(buf, delivered); + vstream_fprintf(dst, "Delivered-To: %s\n", lowercase(vstring_str(buf))); + } + + /* + * Copy the message. Escape lines that could be confused with the ugly + * From_ line. Make sure that there is a blank line at the end of the + * message so that the next ugly From_ can be found by mail reading + * software. + * + * XXX Rely on the front-end services to enforce record size limits. + */ +#define VSTREAM_FWRITE_BUF(s,b) \ + vstream_fwrite((s),vstring_str(b),VSTRING_LEN(b)) + + prev_type = REC_TYPE_NORM; + while ((type = rec_get(src, buf, 0)) > 0) { + if (type != REC_TYPE_NORM && type != REC_TYPE_CONT) + break; + bp = vstring_str(buf); + if ((flags & MAIL_COPY_QUOTE) && *bp == 'F' && !strncmp(bp, "From ", 5)) + VSTREAM_PUTC('>', dst); + if (VSTRING_LEN(buf) && VSTREAM_FWRITE_BUF(dst, buf) != VSTRING_LEN(buf)) + break; + if (type == REC_TYPE_NORM && VSTREAM_PUTC('\n', dst) == VSTREAM_EOF) + break; + prev_type = type; + } + if (vstream_ferror(dst) == 0) { + if (type != REC_TYPE_XTRA) + corrupt_error = mark_corrupt(src); + if (prev_type != REC_TYPE_NORM) + VSTREAM_PUTC('\n', dst); + if (flags & MAIL_COPY_FROM) + VSTREAM_PUTC('\n', dst); + } + vstring_free(buf); + + /* + * Make sure we read and wrote all. Truncate the file to its original + * length when the delivery failed. POSIX does not require ftruncate(), + * so we may have a portability problem. Note that fclose() may fail even + * while fflush and fsync() succeed. Think of remote file systems such as + * AFS that copy the file back to the server upon close. Oh well, no + * point optimizing the error case. XXX On systems that use flock() + * locking, we must truncate the file file before closing it (and losing + * the exclusive lock). + */ + read_error = vstream_ferror(src); + write_error = vstream_fflush(dst); +#ifdef HAS_FSYNC + if ((flags & MAIL_COPY_TOFILE) != 0) + write_error |= fsync(vstream_fileno(dst)); +#endif +#ifndef NO_TRUNCATE + if ((flags & MAIL_COPY_TOFILE) != 0) + if (read_error || write_error) + ftruncate(vstream_fileno(dst), (off_t) orig_length); +#endif + write_error |= vstream_fclose(dst); + if (why && read_error) + vstring_sprintf(why, "error reading message: %m"); + if (why && write_error) + vstring_sprintf(why, "error writing message: %m"); + return (corrupt_error || read_error || write_error); +} diff --git a/postfix/global/mail_copy.h b/postfix/global/mail_copy.h new file mode 100644 index 000000000..e25436ae7 --- /dev/null +++ b/postfix/global/mail_copy.h @@ -0,0 +1,44 @@ +#ifndef _MAIL_COPY_H_INCLUDED_ +#define _MAIL_COPY_H_INCLUDED_ + +/*++ +/* NAME +/* mail_copy 3h +/* SUMMARY +/* copy message with extreme prejudice +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include + + /* + * External interface. + */ +extern int mail_copy(const char *, const char *, VSTREAM *, VSTREAM *, + int, VSTRING *); + +#define MAIL_COPY_QUOTE (1<<0) /* prepend > to From_ */ +#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_NONE 0 /* all turned off */ + +/* 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_date.c b/postfix/global/mail_date.c new file mode 100644 index 000000000..57708702e --- /dev/null +++ b/postfix/global/mail_date.c @@ -0,0 +1,128 @@ +/*++ +/* NAME +/* mail_date 3 +/* SUMMARY +/* return formatted time +/* SYNOPSIS +/* #include +/* +/* const char *mail_date(when) +/* time_t when; +/* DESCRIPTION +/* mail_date() converts the time specified in \fIwhen\fR to the +/* form: "Mon, 9 Dec 1996 05:38:26 -0500 (EST)" and returns +/* a pointer to the result. The result is overwritten upon +/* each call. +/* DIAGNOSTICS +/* Panic: the offset from UTC is more than a whole day. Fatal +/* error: out of memory. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "mail_date.h" + + /* + * Application-specific. + */ +#define DAY_SEC (24 * HOUR_SEC) /* seconds in a day */ +#define HOUR_SEC (60) /* seconds in an hour */ + +/* mail_date - return formatted time */ + +const char *mail_date(time_t when) +{ + static VSTRING *vp; + struct tm *lt; + struct tm gmt; + int gmtoff; + + /* + * As if strftime() isn't expensive enough, we're dynamically adjusting + * the size for the result, so we won't be surprised by long names etc. + */ + if (vp == 0) + vp = vstring_alloc(100); + else + VSTRING_RESET(vp); + + /* + * POSIX does not require that struct tm has a tm_gmtoff field, so we + * must compute the time offset from UTC by hand. + */ + gmt = *gmtime(&when); + lt = localtime(&when); + gmtoff = (lt->tm_hour - gmt.tm_hour) * HOUR_SEC + lt->tm_min - gmt.tm_min; + if (lt->tm_year < gmt.tm_year) + gmtoff -= DAY_SEC; + else if (lt->tm_year > gmt.tm_year) + gmtoff += DAY_SEC; + else if (lt->tm_yday < gmt.tm_yday) + gmtoff -= DAY_SEC; + else if (lt->tm_yday > gmt.tm_yday) + gmtoff += DAY_SEC; + + /* + * First, format the date and wall-clock time. XXX The %e format (day of + * month, leading zero replaced by blank) isn't in my POSIX book, but + * many vendors seem to support it. + */ +#ifdef MISSING_STRFTIME_E +#define STRFTIME_FMT "%a, %d %b %Y %H:%M:%S " +#else +#define STRFTIME_FMT "%a, %e %b %Y %H:%M:%S " +#endif + + while (strftime(vstring_end(vp), vstring_avail(vp), STRFTIME_FMT, lt) == 0) + VSTRING_SPACE(vp, 100); + VSTRING_SKIP(vp); + + /* + * Then, add the UTC offset. + */ + if (gmtoff < -DAY_SEC || gmtoff > DAY_SEC) + 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)); + + /* + * Finally, add the time zone name. + */ + while (strftime(vstring_end(vp), vstring_avail(vp), " (%Z)", lt) == 0) + VSTRING_SPACE(vp, 100); + VSTRING_SKIP(vp); + + return (vstring_str(vp)); +} + +#ifdef TEST + +#include + +main(void) +{ + vstream_printf("%s\n", mail_date(time((time_t *) 0))); + vstream_fflush(VSTREAM_OUT); + return (0); +} + +#endif diff --git a/postfix/global/mail_date.h b/postfix/global/mail_date.h new file mode 100644 index 000000000..101436c54 --- /dev/null +++ b/postfix/global/mail_date.h @@ -0,0 +1,35 @@ +#ifndef _MAIL_DATE_H_INCLUDED_ +#define _MAIL_DATE_H_INCLUDED_ + +/*++ +/* NAME +/* mail_date 3h +/* SUMMARY +/* return formatted time +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * External interface + */ +extern const char *mail_date(time_t); + +/* 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_error.c b/postfix/global/mail_error.c new file mode 100644 index 000000000..7c1ee7cb1 --- /dev/null +++ b/postfix/global/mail_error.c @@ -0,0 +1,72 @@ +/*++ +/* NAME +/* mail_error 3 +/* SUMMARY +/* mail error classes +/* SYNOPSIS +/* #include +/* +/* NAME_MASK mail_error_masks[]; +/* DESCRIPTION +/* This module implements error class support. +/* +/* mail_error_masks[] is a null-terminated table with mail error +/* class names and their corresponding bit masks. +/* +/* The following is a list of implemented names, with the +/* corresponding bit masks indicated in parentheses: +/* .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 "policy (MAIL_ERROR_POLICY)" +/* Policy violation. This depends on what restrictions have +/* been configured locally. +/* .IP "protocol (MAIL_ERROR_PROTOCOL)" +/* Protocol violation. Something did not follow the appropriate +/* standard, or something requested an unimplemented service. +/* .IP "resource (MAIL_ERROR_RESOURCE)" +/* A message could not be delivered due to lack of system +/* resources, for example, lack of file system space. +/* .IP "software (MAIL_ERROR_SOFTWARE)" +/* Software bug. The author of this program made a mistake. +/* Fixing this requires change to the software. +/* SEE ALSO +/* name_mask(3), name to mask conversion +/* 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. */ + +/* Global library. */ + +#include "mail_error.h" + + /* + * The table that maps names to error bit masks. This will work on most UNIX + * compilation environments. + * + * In a some environments the table will not be linked in unless this module + * also contains a function that is being called explicitly. REF/DEF and all + * that. + */ +NAME_MASK mail_error_masks[] = { + "bounce", MAIL_ERROR_BOUNCE, + "policy", MAIL_ERROR_POLICY, + "protocol", MAIL_ERROR_PROTOCOL, + "resource", MAIL_ERROR_RESOURCE, + "software", MAIL_ERROR_SOFTWARE, + 0, 0, +}; diff --git a/postfix/global/mail_error.h b/postfix/global/mail_error.h new file mode 100644 index 000000000..4cb75412a --- /dev/null +++ b/postfix/global/mail_error.h @@ -0,0 +1,41 @@ +#ifndef _MAIL_ERROR_H_INCLUDED_ +#define _MAIL_ERROR_H_INCLUDED_ + +/*++ +/* NAME +/* mail_error 3h +/* SUMMARY +/* mail error classes +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +#define MAIL_ERROR_POLICY (1<<0) +#define MAIL_ERROR_PROTOCOL (1<<1) +#define MAIL_ERROR_BOUNCE (1<<2) +#define MAIL_ERROR_SOFTWARE (1<<3) +#define MAIL_ERROR_RESOURCE (1<<4) + +extern NAME_MASK mail_error_masks[]; + +/* 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_flush.c b/postfix/global/mail_flush.c new file mode 100644 index 000000000..e5fa32ce7 --- /dev/null +++ b/postfix/global/mail_flush.c @@ -0,0 +1,73 @@ +/*++ +/* NAME +/* mail_flush 3 +/* SUMMARY +/* flush backed up mail +/* SYNOPSIS +/* #include +/* +/* int mail_flush_deferred() +/* +/* int mail_flush_site(site) +/* const char *site; +/* DESCRIPTION +/* This module triggers delivery of backed up mail. +/* +/* mail_flush_deferred() triggers delivery of all mail in the +/* deferred queue. +/* +/* mail_flush_site() triggers delivery of all mail queued for +/* the named site. This routine may degenerate into a +/* mail_flush_deferred() call. +/* DIAGNOSTICS +/* The result is 0 in case of success, -1 in case of failure. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include "sys_defs.h" + +/* Utility library. */ + +/* Global library. */ + +#include +#include + +/* mail_flush_deferred - flush deferred queue */ + +int mail_flush_deferred(void) +{ + static char qmgr_trigger[] = { + QMGR_REQ_FLUSH_DEAD, /* all hosts, all transports */ + QMGR_REQ_SCAN_ALL, /* all time stamps */ + QMGR_REQ_SCAN_DEFERRED, /* scan deferred queue */ + }; + + /* + * Trigger the flush queue service. + */ + return (mail_trigger(MAIL_CLASS_PUBLIC, MAIL_SERVICE_QUEUE, + qmgr_trigger, sizeof(qmgr_trigger))); +} + +/* mail_flush_site - flush deferred mail for site */ + +int mail_flush_site(const char *unused_site) +{ + + /* + * Until we have dedicated per-site queues, this call will degenerate + * into a mail_flush_deferred() call. + */ + return (mail_flush_deferred()); +} diff --git a/postfix/global/mail_flush.h b/postfix/global/mail_flush.h new file mode 100644 index 000000000..defd15c2a --- /dev/null +++ b/postfix/global/mail_flush.h @@ -0,0 +1,30 @@ +#ifndef _MAIL_FLUSH_H_INCLUDED_ +#define _MAIL_FLUSH_H_INCLUDED_ + +/*++ +/* NAME +/* mail_flush 3h +/* SUMMARY +/* flush backed up mail +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern int mail_flush_deferred(void); +extern int mail_flush_site(const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/mail_open_ok.c b/postfix/global/mail_open_ok.c new file mode 100644 index 000000000..9824971a9 --- /dev/null +++ b/postfix/global/mail_open_ok.c @@ -0,0 +1,110 @@ +/*++ +/* NAME +/* mail_open_ok 3 +/* SUMMARY +/* scrutinize mail queue file +/* SYNOPSIS +/* #include +/* +/* int mail_open_ok(queue_name, queue_id, statp, pathp) +/* const char *queue_name; +/* const char *queue_id; +/* struct stat *statp; +/* char **pathp +/* DESCRIPTION +/* mail_open_ok() determines if it is OK to open the specified +/* queue file. +/* +/* The queue name and queue id should conform to the syntax +/* requirements for these names. +/* Unfortunately, on some systems readdir() etc. can return bogus +/* file names. For this reason, the code silently ignores invalid +/* queue file names. +/* +/* The file should have mode 0700. Files with other permissions +/* are silently ignored. +/* +/* The file should be a regular file. +/* Files that do not satisfy this requirement are skipped with +/* a warning. +/* +/* The file link count is not restricted. With a writable maildrop +/* directory, refusal to deliver linked files is prone to denial of +/* service attack; it's better to deliver mail too often than not. +/* +/* Upon return, the \fIstatp\fR argument receives the file +/* attributes and \fIpathp\fR a copy of the file name. The file +/* name is volatile. Make a copy if it is to be used for any +/* appreciable amount of time. +/* DIAGNOSTICS +/* Warnings: bad file attributes (file type), multiple hard links. +/* mail_open_ok() returns MAIL_OPEN_YES for good files, MAIL_OPEN_NO +/* for anything else. It is left up to the system administrator to +/* deal with non-file objects. +/* BUGS +/* mail_open_ok() examines a queue file without actually opening +/* it, and therefore is susceptible to race conditions. +/* 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 libraries. */ + +#include +#include +#include + +/* Utility library. */ + +#include + +/* Global library. */ + +#include "mail_queue.h" +#include "mail_open_ok.h" + +/* mail_open_ok - see if this file is OK to open */ + +int mail_open_ok(const char *queue_name, const char *queue_id, + struct stat * statp, const char **path) +{ + if (mail_queue_name_ok(queue_name) == 0) { + msg_warn("bad mail queue name: %s", queue_name); + return (MAIL_OPEN_NO); + } + if (mail_queue_id_ok(queue_id) == 0) + return (MAIL_OPEN_NO); + + + /* + * I really would like to look up the file attributes *after* opening the + * file so that we could save one directory traversal on systems without + * name-to-inode cache. However, we don't necessarily always want to open + * the file. + */ + *path = mail_queue_path((VSTRING *) 0, queue_name, queue_id); + + if (lstat(*path, statp) < 0) { + if (errno != ENOENT) + msg_warn("%s: %m", *path); + return (MAIL_OPEN_NO); + } + if (!S_ISREG(statp->st_mode)) { + msg_warn("%s: uid %d: not a regular file", *path, statp->st_uid); + return (MAIL_OPEN_NO); + } + if ((statp->st_mode & S_IRWXU) != MAIL_QUEUE_STAT_READY) + return (MAIL_OPEN_NO); + if (statp->st_nlink > 1) { + msg_warn("%s: uid %d: file has %d links", *path, + statp->st_uid, (int) statp->st_nlink); + } + return (MAIL_OPEN_YES); +} diff --git a/postfix/global/mail_open_ok.h b/postfix/global/mail_open_ok.h new file mode 100644 index 000000000..a56521a7f --- /dev/null +++ b/postfix/global/mail_open_ok.h @@ -0,0 +1,33 @@ +#ifndef _MAIL_OPEN_OK_H_INCLUDED_ +#define _MAIL_OPEN_OK_H_INCLUDED_ + +/*++ +/* NAME +/* mail_open_ok 3h +/* SUMMARY +/* scrutinize mail queue file +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern int mail_open_ok(const char *, const char *, struct stat *, + const char **); + +#define MAIL_OPEN_YES 1 +#define MAIL_OPEN_NO 2 + +/* 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_params.c b/postfix/global/mail_params.c new file mode 100644 index 000000000..b27cee4ff --- /dev/null +++ b/postfix/global/mail_params.c @@ -0,0 +1,327 @@ +/*++ +/* NAME +/* mail_params 3 +/* SUMMARY +/* global mail configuration parameters +/* SYNOPSIS +/* #include +/* +/* char *var_myhostname; +/* char *var_mydomain; +/* char *var_myorigin; +/* char *var_mydest; +/* char *var_relayhost; +/* char *var_transit_origin; +/* char *var_transit_dest; +/* char *var_mail_name; +/* char *var_mail_owner; +/* uid_t var_owner_uid; +/* gid_t var_owner_gid; +/* char *var_default_privs; +/* uid_t var_default_uid; +/* gid_t var_default_gid; +/* char *var_config_dir; +/* char *var_program_dir; +/* char *var_daemon_dir; +/* char *var_command_dir; +/* char *var_queue_dir; +/* int var_use_limit; +/* int var_idle_limit; +/* int var_bundle_rcpt; +/* char *var_procname; +/* int var_pid; +/* int var_ipc_timeout; +/* char *var_pid_dir; +/* int var_dont_remove; +/* char *var_inet_interfaces; +/* char *var_mynetworks; +/* char *var_double_bounce_sender; +/* int var_line_limit; +/* char *var_alias_db_map; +/* int var_message_limit; +/* char *var_mail_version; +/* int var_ipc_idle_limit; +/* char *var_db_type; +/* char *var_hash_queue_names; +/* int var_hash_queue_depth; +/* int var_trigger_timeout; +/* char *var_rcpt_delim; +/* int var_fork_tries; +/* int var_fork_delay; +/* int var_flock_tries; +/* int var_flock_delay; +/* int var_flock_stale; +/* int var_disable_dns; +/* +/* char *var_ldap_server_host; +/* char *var_ldap_search_base; +/* int var_ldap_timeout; +/* +/* void mail_params_init() +/* DESCRIPTION +/* This module (actually the associated include file) define the names +/* and defaults of all mail configuration parameters. +/* +/* mail_params_init() initializes the built-in parameters listed above. +/* These parameters are relied upon by library routines, so they are +/* initialized globally so as to avoid hard-to-find errors due to +/* missing initialization. This routine must be called early, at +/* least before entering a chroot jail. +/* DIAGNOSTICS +/* Fatal errors: out of memory; null system or domain name. +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include "mynetworks.h" +#include "config.h" +#include "mail_version.h" +#include "mail_params.h" + + /* + * Special configuration variables. + */ +char *var_myhostname; +char *var_mydomain; +char *var_myorigin; +char *var_mydest; +char *var_relayhost; +char *var_transit_origin; +char *var_transit_dest; +char *var_mail_name; +char *var_mail_owner; +uid_t var_owner_uid; +gid_t var_owner_gid; +char *var_default_privs; +uid_t var_default_uid; +gid_t var_default_gid; +char *var_config_dir; +char *var_program_dir; +char *var_daemon_dir; +char *var_command_dir; +char *var_queue_dir; +int var_use_limit; +int var_idle_limit; +int var_bundle_rcpt; +char *var_procname; +int var_pid; +int var_ipc_timeout; +char *var_pid_dir; +int var_dont_remove; +char *var_inet_interfaces; +char *var_mynetworks; +char *var_double_bounce_sender; +int var_line_limit; +char *var_alias_db_map; +int var_message_limit; +char *var_mail_version; +int var_ipc_idle_limit; +char *var_db_type; +char *var_hash_queue_names; +int var_hash_queue_depth; +int var_trigger_timeout; +char *var_rcpt_delim; +int var_fork_tries; +int var_fork_delay; +int var_flock_tries; +int var_flock_delay; +int var_flock_stale; +int var_disable_dns; + +#ifdef HAS_LDAP + +char *var_ldap_server_host; +char *var_ldap_search_base; +int var_ldap_timeout; + +#endif + +/* check_myhostname - lookup hostname and validate */ + +static const char *check_myhostname(void) +{ + const char *name; + const char *dot; + + name = get_hostname(); + if ((dot = strchr(name, '.')) == 0) + msg_fatal("My hostname %s is not a FQDN. Set %s in %s/main.cf", + name, VAR_MYHOSTNAME, var_config_dir); + return (name); +} + +/* check_mydomainname - lookup domain name and validate */ + +static const char *check_mydomainname(void) +{ + char *dot; + + /* + * Use the hostname when it is not a FQDN ("foo"), or when the hostname + * actually is a domain name ("foo.com"). + */ + if ((dot = strchr(var_myhostname, '.')) == 0 || strchr(dot + 1, '.') == 0) + return (var_myhostname); + return (dot + 1); +} + +/* check_default_privs - lookup default user attributes and validate */ + +static void check_default_privs(void) +{ + struct passwd *pwd; + + if ((pwd = getpwnam(var_default_privs)) == 0) + msg_fatal("unknown %s configuration parameter value: %s", + VAR_DEFAULT_PRIVS, var_default_privs); + if ((var_default_uid = pwd->pw_uid) == 0) + msg_fatal("%s: %s: privileged user is not allowed", + VAR_DEFAULT_PRIVS, var_default_privs); + if ((var_default_gid = pwd->pw_gid) == 0) + msg_fatal("%s: %s: privileged group is not allowed", + VAR_DEFAULT_PRIVS, var_default_privs); +} + +/* check_mail_owner - lookup owner user attributes and validate */ + +static void check_mail_owner(void) +{ + struct passwd *pwd; + + if ((pwd = getpwnam(var_mail_owner)) == 0) + msg_fatal("unknown %s configuration parameter value: %s", + VAR_MAIL_OWNER, var_mail_owner); + if ((var_owner_uid = pwd->pw_uid) == 0) + msg_fatal("%s: %s: privileged user is not allowed", + VAR_MAIL_OWNER, var_mail_owner); + if ((var_owner_gid = pwd->pw_gid) == 0) + msg_fatal("%s: %s: privileged group is not allowed", + VAR_DEFAULT_PRIVS, var_mail_owner); +} + +/* mail_params_init - configure built-in parameters */ + +void mail_params_init() +{ + static CONFIG_STR_FN_TABLE function_str_defaults[] = { + VAR_MYHOSTNAME, check_myhostname, &var_myhostname, 1, 0, + VAR_MYDOMAIN, check_mydomainname, &var_mydomain, 1, 0, + 0, + }; + 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_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, + VAR_DAEMON_DIR, DEF_DAEMON_DIR, &var_daemon_dir, 1, 0, + VAR_COMMAND_DIR, DEF_COMMAND_DIR, &var_command_dir, 1, 0, + VAR_QUEUE_DIR, DEF_QUEUE_DIR, &var_queue_dir, 1, 0, + VAR_PID_DIR, DEF_PID_DIR, &var_pid_dir, 1, 0, + VAR_INET_INTERFACES, DEF_INET_INTERFACES, &var_inet_interfaces, 1, 0, + VAR_DOUBLE_BOUNCE, DEF_DOUBLE_BOUNCE, &var_double_bounce_sender, 1, 0, + VAR_DEFAULT_PRIVS, DEF_DEFAULT_PRIVS, &var_default_privs, 1, 0, + VAR_ALIAS_DB_MAP, DEF_ALIAS_DB_MAP, &var_alias_db_map, 1, 0, + VAR_MAIL_VERSION, DEF_MAIL_VERSION, &var_mail_version, 1, 0, + VAR_DB_TYPE, DEF_DB_TYPE, &var_db_type, 1, 0, + VAR_HASH_QUEUE_NAMES, DEF_HASH_QUEUE_NAMES, &var_hash_queue_names, 1, 0, +#ifdef HAS_LDAP + VAR_LDAP_SERVER, DEF_LDAP_SERVER, &var_ldap_server_host, 0, 0, + VAR_LDAP_SEARCH, DEF_LDAP_SEARCH, &var_ldap_search_base, 0, 0, +#endif + VAR_RCPT_DELIM, DEF_RCPT_DELIM, &var_rcpt_delim, 0, 1, + 0, + }; + static CONFIG_STR_FN_TABLE function_str_defaults_2[] = { + VAR_MYNETWORKS, mynetworks, &var_mynetworks, 1, 0, + 0, + }; + static CONFIG_INT_TABLE other_int_defaults[] = { + VAR_MAX_USE, DEF_MAX_USE, &var_use_limit, 1, 0, + VAR_MAX_IDLE, DEF_MAX_IDLE, &var_idle_limit, 1, 0, + VAR_IPC_TIMEOUT, DEF_IPC_TIMEOUT, &var_ipc_timeout, 1, 0, + VAR_DONT_REMOVE, DEF_DONT_REMOVE, &var_dont_remove, 0, 0, + VAR_LINE_LIMIT, DEF_LINE_LIMIT, &var_line_limit, 1024, 0, + VAR_MESSAGE_LIMIT, DEF_MESSAGE_LIMIT, &var_message_limit, 0, 0, + VAR_IPC_IDLE, DEF_IPC_IDLE, &var_ipc_idle_limit, 1, 0, + VAR_HASH_QUEUE_DEPTH, DEF_HASH_QUEUE_DEPTH, &var_hash_queue_depth, 1, 0, +#ifdef HAS_LDAP + VAR_LDAP_TIMEOUT, DEF_LDAP_TIMEOUT, &var_ldap_timeout, 1, 0, +#endif + VAR_TRIGGER_TIMEOUT, DEF_TRIGGER_TIMEOUT, &var_trigger_timeout, 1, 0, + VAR_FORK_TRIES, DEF_FORK_TRIES, &var_fork_tries, 1, 0, + VAR_FORK_DELAY, DEF_FORK_DELAY, &var_fork_delay, 1, 0, + VAR_FLOCK_TRIES, DEF_FLOCK_TRIES, &var_flock_tries, 1, 0, + VAR_FLOCK_DELAY, DEF_FLOCK_DELAY, &var_flock_delay, 1, 0, + VAR_FLOCK_STALE, DEF_FLOCK_STALE, &var_flock_stale, 1, 0, + 0, + }; + static CONFIG_BOOL_TABLE bool_defaults[] = { + VAR_DISABLE_DNS, DEF_DISABLE_DNS, &var_disable_dns, + 0, + }; + + /* + * Variables whose defaults are determined at runtime. Some sites use + * short hostnames in the host table; some sites name their system after + * the domain. + */ + get_config_str_fn_table(function_str_defaults); + if (!valid_hostname(var_myhostname) || !valid_hostname(var_mydomain)) + msg_fatal("host or domain name configuration error"); + + /* + * Variables that are needed by almost every program. + */ + get_config_str_table(other_str_defaults); + get_config_int_table(other_int_defaults); + get_config_bool_table(bool_defaults); + check_default_privs(); + check_mail_owner(); + + /* + * Variables whose defaults are determined at runtime, after other + * variables have been set. This dependency is admittedly a bit tricky. + * XXX Perhaps we should just register variables, and let the evaluator + * figure out in what order to evaluate things. + */ + get_config_str_fn_table(function_str_defaults_2); + + /* + * The PID variable cannot be set from the configuration file!! + */ + set_config_int(VAR_PID, var_pid = getpid()); + + /* + * If 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 new file mode 100644 index 000000000..af729d97e --- /dev/null +++ b/postfix/global/mail_params.h @@ -0,0 +1,691 @@ +#ifndef _MAIL_PARAMS_H_INCLUDED_ +#define _MAIL_PARAMS_H_INCLUDED_ + +/*++ +/* NAME +/* mail_params 3h +/* SUMMARY +/* globally configurable parameters +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * This is to make it easier to auto-generate tables. + */ +typedef int bool; + + /* + * Name used when this mail system announces itself. + */ +#define VAR_MAIL_NAME "mail_name" +#define DEF_MAIL_NAME "Postfix" +extern char *var_mail_name; + + /* + * What problem classes should be reported to the postmaster via email. + * Default is bad problems only. See mail_error(3). Even when mail notices + * are disabled, problems are still logged to the syslog daemon. + */ +#define VAR_NOTIFY_CLASSES "notify_classes" +#define DEF_NOTIFY_CLASSES "resource,software" +extern char *var_notify_classes; + + /* + * What do I turn <> into? Sendmail defaults to mailer-daemon. + */ +#define VAR_EMPTY_ADDR "empty_address_recipient" +#define DEF_EMPTY_ADDR MAIL_ADDR_MAIL_DAEMON +extern char *var_empty_addr; + + /* + * Privileges used by the mail system: the owner of files and commands, and + * the rights to be used when running external commands. + */ +#define VAR_MAIL_OWNER "mail_owner" +#define DEF_MAIL_OWNER "postfix" +extern char *var_mail_owner; +extern uid_t var_owner_uid; +extern gid_t var_owner_gid; + +#define VAR_DEFAULT_PRIVS "default_privs" +#define DEF_DEFAULT_PRIVS "nobody" +extern char *var_default_privs; +extern uid_t var_default_uid; +extern gid_t var_default_gid; + + /* + * What goes on the right-hand side of addresses of mail sent from this + * machine. + */ +#define VAR_MYORIGIN "myorigin" +#define DEF_MYORIGIN "$myhostname" +extern char *var_myorigin; + + /* + * What domains I will receive mail for. Not to be confused with transit + * mail to other destinations. + */ +#define VAR_MYDEST "mydestination" +#define DEF_MYDEST "$myhostname, localhost.$mydomain" +extern char *var_mydest; + + /* + * These are by default taken from the name service. + */ +#define VAR_MYHOSTNAME "myhostname" /* my hostname (fqdn) */ +extern char *var_myhostname; + +#define VAR_MYDOMAIN "mydomain" /* my domain name */ +extern char *var_mydomain; + + /* + * Virtual host support. Default is to listen on all machine interfaces. + */ +#define VAR_INET_INTERFACES "inet_interfaces" /* listen addresses */ +#define DEF_INET_INTERFACES "all" +extern char *var_inet_interfaces; + + /* + * Masquerading (i.e. subdomain stripping). + */ +#define VAR_MASQ_DOMAINS "masquerade_domains" +#define DEF_MASQ_DOMAINS "" +extern char *var_masq_domains; + +#define VAR_MASQ_EXCEPTIONS "masquerade_exceptions" +#define DEF_MASQ_EXCEPTIONS "" +extern char *var_masq_exceptions; + + /* + * Intranet versus internet. + */ +#define VAR_RELAYHOST "relayhost" +#define DEF_RELAYHOST "" +extern char *var_relayhost; + +#define VAR_DISABLE_DNS "disable_dns_lookups" +#define DEF_DISABLE_DNS 0 +extern bool var_disable_dns; + + /* + * Location of the mail queue directory tree. + */ +#define VAR_QUEUE_DIR "queue_directory" +#ifndef DEF_QUEUE_DIR +#define DEF_QUEUE_DIR "/var/spool/postfix" +#endif +extern char *var_queue_dir; + + /* + * Location of daemon programs. + */ +#define VAR_PROGRAM_DIR "program_directory" +#ifndef DEF_PROGRAM_DIR +#define DEF_PROGRAM_DIR "/usr/libexec/postfix" +#endif + +#define VAR_DAEMON_DIR "daemon_directory" +#ifndef DEF_DAEMON_DIR +#define DEF_DAEMON_DIR "$program_directory" +#endif +extern char *var_daemon_dir; + +#define VAR_COMMAND_DIR "command_directory" +#ifndef DEF_COMMAND_DIR +#define DEF_COMMAND_DIR "$program_directory" +#endif +extern char *var_command_dir; + + /* + * Location of PID files. + */ +#define VAR_PID_DIR "process_id_directory" +#ifndef DEF_PID_DIR +#define DEF_PID_DIR "pid" +#endif +extern char *var_pid_dir; + + /* + * Location of configuration files. + */ +#define VAR_CONFIG_DIR "config_directory" +#ifndef DEF_CONFIG_DIR +#define DEF_CONFIG_DIR "/etc/postfix" +#endif +extern char *var_config_dir; + + /* + * Preferred type of indexed files. The DEF_DB_TYPE macro value is system + * dependent. It is defined in . + */ +#define VAR_DB_TYPE "default_database_type" +extern char *var_db_type; + + /* + * Logging. Changing facility at run-time does not do much good, because + * something may have to be logged before parameters are read from file. + */ +#ifndef LOG_FACILITY +#define LOG_FACILITY LOG_MAIL +#endif + + /* + * trivial rewrite/resolve service: mapping tables. + */ +#define VAR_VIRTUAL_MAPS "virtual_maps" +#define DEF_VIRTUAL_MAPS "" +extern char *var_virtual_maps; + +#define VAR_CANONICAL_MAPS "canonical_maps" +#define DEF_CANONICAL_MAPS "" +extern char *var_canonical_maps; + +#define VAR_SEND_CANON_MAPS "sender_canonical_maps" +#define DEF_SEND_CANON_MAPS "" +extern char *var_send_canon_maps; + +#define VAR_RCPT_CANON_MAPS "recipient_canonical_maps" +#define DEF_RCPT_CANON_MAPS "" +extern char *var_rcpt_canon_maps; + +#define VAR_TRANSPORT_MAPS "transport_maps" +#define DEF_TRANSPORT_MAPS "" +extern char *var_transport_maps; + +#define VAR_DEF_TRANSPORT "default_transport" +#define DEF_DEF_TRANSPORT MAIL_SERVICE_SMTP +extern char *var_def_transport; + + /* + * trivial rewrite/resolve service: rewriting controls. + */ +#define VAR_SWAP_BANGPATH "swap_bangpath" +#define DEF_SWAP_BANGPATH 1 +extern bool var_swap_bangpath; + +#define VAR_APP_AT_MYORIGIN "append_at_myorigin" +#define DEF_APP_AT_MYORIGIN 1 +extern bool var_append_at_myorigin; + +#define VAR_APP_DOT_MYDOMAIN "append_dot_mydomain" +#define DEF_APP_DOT_MYDOMAIN 1 +extern bool var_append_dot_mydomain; + +#define VAR_PERCENT_HACK "allow_percent_hack" +#define DEF_PERCENT_HACK 1 +extern bool var_percent_hack; + + /* + * LDAP lookups. Preliminary code, interface subject to change. + */ +#define VAR_LDAP_SERVER "ldap_server_host" +#define DEF_LDAP_SERVER "" +extern char *var_ldap_server; + +#define VAR_LDAP_SEARCH "ldap_search_base" +#define DEF_LDAP_SEARCH "" +extern char *var_ldap_search; + +#define VAR_LDAP_TIMEOUT "ldap_lookup_timeout" +#define DEF_LDAP_TIMEOUT 10 +extern int var_ldap_timeout; + + /* + * Local delivery: alias databases. + */ +#define VAR_ALIAS_MAPS "alias_maps" +#ifdef HAS_NIS +#define DEF_ALIAS_MAPS ALIAS_DB_MAP ", nis:mail.aliases" +#else +#define DEF_ALIAS_MAPS ALIAS_DB_MAP +#endif +extern char *var_alias_maps; + + /* + * Local delivery: mail to files/commands. + */ +#define VAR_ALLOW_COMMANDS "allow_mail_to_commands" +#define DEF_ALLOW_COMMANDS "alias,forward" +extern char *var_allow_commands; + +#define VAR_COMMAND_MAXTIME "command_time_limit" +#define DEF_COMMAND_MAXTIME 1000 +extern int var_command_maxtime; + +#define VAR_ALLOW_FILES "allow_mail_to_files" +#define DEF_ALLOW_FILES "alias,forward" +extern char *var_allow_files; + +#define VAR_LOCAL_CMD_SHELL "local_command_shell" +#define DEF_LOCAL_CMD_SHELL "" +extern char *var_local_cmd_shell; + +#define VAR_ALIAS_DB_MAP "alias_database" +#define DEF_ALIAS_DB_MAP ALIAS_DB_MAP /* sys_defs.h */ +extern char *var_alias_db_map; + +#define VAR_HOME_MAILBOX "home_mailbox" +#define DEF_HOME_MAILBOX "" +extern char *var_home_mailbox; + +#define VAR_MAILBOX_COMMAND "mailbox_command" +#define DEF_MAILBOX_COMMAND "" +extern char *var_mailbox_command; + +#define VAR_RCPT_DELIM "recipient_delimiter" +#define DEF_RCPT_DELIM "" +extern char *var_rcpt_delim; + +#define VAR_RCPT_FDELIM "recipient_feature_delimiter" +#define DEF_RCPT_FDELIM "" +extern char *var_rcpt_fdelim; + + /* + * Queue manager: maximal size of the duplicate expansion filter. By + * default, we do graceful degradation with huge mailing lists. + */ +#define VAR_DUP_FILTER_LIMIT "duplicate_filter_limit" +#define DEF_DUP_FILTER_LIMIT 1000 +extern int var_dup_filter_limit; + + /* + * Queue manager: relocated databases. + */ +#define VAR_RELOCATED_MAPS "relocated_maps" +#define DEF_RELOCATED_MAPS "" +extern char *var_relocated_maps; + + /* + * Queue manager: after each failed attempt the backoff time (how long we + * won't try this host in seconds) is doubled until it reaches the maximum. + * MAX_QUEUE_TIME limits the amount of time a message may spend in the mail + * queue before it is sent back. + */ +#define VAR_QUEUE_RUN_DELAY "queue_run_delay" +#define DEF_QUEUE_RUN_DELAY 1000 + +#define VAR_MIN_BACKOFF_TIME "minimal_backoff_time" +#define DEF_MIN_BACKOFF_TIME 1000 +extern int var_min_backoff_time; + +#define VAR_MAX_BACKOFF_TIME "maximal_backoff_time" +#define DEF_MAX_BACKOFF_TIME 4000 +extern int var_max_backoff_time; + +#define VAR_MAX_QUEUE_TIME "maximal_queue_lifetime" +#define DEF_MAX_QUEUE_TIME 5 +extern int var_max_queue_time; + +#define VAR_QMGR_ACT_LIMIT "qmgr_message_active_limit" +#define DEF_QMGR_ACT_LIMIT 1000 + +#define VAR_QMGR_RCPT_LIMIT "qmgr_message_recipient_limit" +#define DEF_QMGR_RCPT_LIMIT 10000 +extern int var_qmgr_rcpt_limit; + + /* + * Queue manager: default destination concurrency levels. + */ +#define VAR_INIT_DEST_CON "initial_destination_concurrency" +#define DEF_INIT_DEST_CON 2 +extern int var_init_dest_concurrency; + +#define VAR_DEST_CON_LIMIT "default_destination_concurrency_limit" +#define DEF_DEST_CON_LIMIT 10 +extern int var_dest_con_limit; + + /* + * Queue manager: default number of recipients per transaction. + */ +#define VAR_DEST_RCPT_LIMIT "default_destination_recipient_limit" +#define DEF_DEST_RCPT_LIMIT 50 +extern int var_dest_rcpt_limit; + + /* + * Queue manager: default delay before retrying a dead transport. + */ +#define VAR_XPORT_RETRY_TIME "transport_retry_time" +#define DEF_XPORT_RETRY_TIME 60 +extern int var_transport_retry_time; + + /* + * Queue manager: what transports to defer delivery to. + */ +#define VAR_DEFER_XPORTS "defer_transports" +#define DEF_DEFER_XPORTS "" +extern char *var_defer_xports; + + /* + * Master: default process count limit per mail subsystem. + */ +#define VAR_PROC_LIMIT "default_process_limit" +#define DEF_PROC_LIMIT 50 +extern int var_proc_limit; + + /* + * Any subsystem: default maximum number of clients serviced before a mail + * subsystem terminates (except queue manager). + */ +#define VAR_MAX_USE "max_use" +#define DEF_MAX_USE 100 +extern int var_use_limit; + + /* + * Any subsystem: default amount of time a mail subsystem waits for a client + * connection (except queue manager). + */ +#define VAR_MAX_IDLE "max_idle" +#define DEF_MAX_IDLE 100 +extern int var_idle_limit; + + /* + * Any subsystem: default amount of time a mail subsystem keeps an internal + * IPC connection before closing it. + */ +#define VAR_IPC_IDLE "ipc_idle" +#define DEF_IPC_IDLE 100 +extern int var_ipc_idle_limit; + + /* + * Any front-end subsystem: avoid running out of memory when someone sends + * infinitely-long requests or replies. + */ +#define VAR_LINE_LIMIT "line_length_limit" +#define DEF_LINE_LIMIT 2048 +extern int var_line_limit; + + /* + * Specify what SMTP peers need verbose logging. + */ +#define VAR_DEBUG_PEER_LIST "debug_peer_list" +#define DEF_DEBUG_PEER_LIST "" +extern char *var_debug_peer_list; + +#define VAR_DEBUG_PEER_LEVEL "debug_peer_level" +#define DEF_DEBUG_PEER_LEVEL 2 +extern int var_debug_peer_level; + + /* + * Queue management: what queues are hashed behind a forest of + * subdirectories, and how deep the forest is. + */ +#define VAR_HASH_QUEUE_NAMES "hash_queue_names" +#define DEF_HASH_QUEUE_NAMES "defer" +extern char *var_hash_queue_names; + +#define VAR_HASH_QUEUE_DEPTH "hash_queue_depth" +#define DEF_HASH_QUEUE_DEPTH 2 +extern int var_hash_queue_depth; + + /* + * SMTP client. Timeouts inspired by RFC 1123. The SMTP recipient limit + * determines how many recipient addresses the SMTP client sends along with + * each message. Unfortunately, some mailers misbehave and disconnect (smap) + * when given more recipients than they are willing to handle. + */ +#define VAR_SMTP_CONN_TMOUT "smtp_connect_timeout" +#define DEF_SMTP_CONN_TMOUT 0 +extern int var_smtp_conn_tmout; + +#define VAR_SMTP_HELO_TMOUT "smtp_helo_timeout" +#define DEF_SMTP_HELO_TMOUT 300 +extern int var_smtp_helo_tmout; + +#define VAR_SMTP_MAIL_TMOUT "smtp_mail_timeout" +#define DEF_SMTP_MAIL_TMOUT 300 +extern int var_smtp_mail_tmout; + +#define VAR_SMTP_RCPT_TMOUT "smtp_rcpt_timeout" +#define DEF_SMTP_RCPT_TMOUT 300 +extern int var_smtp_rcpt_tmout; + +#define VAR_SMTP_DATA0_TMOUT "smtp_data_init_timeout" +#define DEF_SMTP_DATA0_TMOUT 120 +extern int var_smtp_data0_tmout; + +#define VAR_SMTP_DATA1_TMOUT "smtp_data_xfer_timeout" +#define DEF_SMTP_DATA1_TMOUT 180 +extern int var_smtp_data1_tmout; + +#define VAR_SMTP_DATA2_TMOUT "smtp_data_done_timeout" +#define DEF_SMTP_DATA2_TMOUT 600 +extern int var_smtp_data2_tmout; + +#define VAR_SMTP_QUIT_TMOUT "smtp_quit_timeout" +#define DEF_SMTP_QUIT_TMOUT 300 +extern int var_smtp_quit_tmout; + + /* + * 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 + * determines after how many client errors we disconnect. + */ +#define VAR_SMTPD_BANNER "smtpd_banner" +#define DEF_SMTPD_BANNER "$myhostname ESMTP $mail_name" +extern char *var_smtpd_banner; + +#define VAR_SMTPD_TMOUT "smtpd_timeout" +#define DEF_SMTPD_TMOUT 300 +extern int var_smtpd_tmout; + +#define VAR_SMTPD_RCPT_LIMIT "smtpd_recipient_limit" +#define DEF_SMTPD_RCPT_LIMIT 1000 +extern int var_smtpd_rcpt_limit; + +#define VAR_SMTPD_SOFT_ERLIM "smtpd_soft_error_limit" +#define DEF_SMTPD_SOFT_ERLIM 10 +extern int var_smtpd_soft_erlim; + +#define VAR_SMTPD_HARD_ERLIM "smtpd_hard_error_limit" +#define DEF_SMTPD_HARD_ERLIM 100 +extern int var_smtpd_hard_erlim; + +#define VAR_SMTPD_ERR_SLEEP "smtpd_error_sleep_time" +#define DEF_SMTPD_ERR_SLEEP 5 +extern int var_smtpd_err_sleep; + + /* + * Cleanup service. Header info that exceeds $header_size_limit bytes forces + * the start of the message body. + */ +#define VAR_HOPCOUNT_LIMIT "hopcount_limit" +#define DEF_HOPCOUNT_LIMIT 50 +extern int var_hopcount_limit; + +#define VAR_HEADER_LIMIT "header_size_limit" +#define DEF_HEADER_LIMIT 102400 +extern int var_header_limit; + + /* + * Message/queue size limits. + */ +#define VAR_MESSAGE_LIMIT "message_size_limit" +#define DEF_MESSAGE_LIMIT 10240000 +extern int var_message_limit; + +#define VAR_QUEUE_MINFREE "queue_minfree" +#define DEF_QUEUE_MINFREE 0 +extern int var_queue_minfree; + + /* + * Bounce service: truncate bounce message that exceed $bounce_size_limit. + */ +#define VAR_BOUNCE_LIMIT "bounce_size_limit" +#define DEF_BOUNCE_LIMIT 50000 +extern int var_bounce_limit; + + /* + * Bounce service: reserved sender address for double bounces. The local + * delivery service discards undeliverable double bounces. + */ +#define VAR_DOUBLE_BOUNCE "double_bounce_sender" +#define DEF_DOUBLE_BOUNCE "double-bounce" +extern char *var_double_bounce_sender; + + /* + * When forking a process, how often to try and how long to wait. + */ +#define VAR_FORK_TRIES "fork_attempts" +#define DEF_FORK_TRIES 5 +extern int var_fork_tries; + +#define VAR_FORK_DELAY "fork_delay" +#define DEF_FORK_DELAY 1 +extern int var_fork_delay; + + /* + * When locking a mailbox, how often to try and how long to wait. + */ +#define VAR_FLOCK_TRIES "deliver_lock_attempts" +#define DEF_FLOCK_TRIES 5 +extern int var_flock_tries; + +#define VAR_FLOCK_DELAY "deliver_lock_delay" +#define DEF_FLOCK_DELAY 1 +extern int var_flock_delay; + +#define VAR_FLOCK_STALE "stale_lock_time" +#define DEF_FLOCK_STALE 500 +extern int var_flock_stale; + + /* + * How long an intra-mail command may take before we assume the mail system + * is in deadlock (should never happen). + */ +#define VAR_IPC_TIMEOUT "ipc_timeout" +#define DEF_IPC_TIMEOUT 3600 +extern int var_ipc_timeout; + + /* + * Time limit on intra-mail triggers. + */ +#define VAR_TRIGGER_TIMEOUT "trigger_timeout" +#define DEF_TRIGGER_TIMEOUT 10 +extern int var_trigger_timeout; + + /* + * SMTP server restrictions. What networks I am willing to relay from, what + * domains I am willing to forward mail from or to, what clients I refuse to + * talk to, and what domains I never want to see in the sender address. + */ +#define VAR_MYNETWORKS "mynetworks" +extern char *var_mynetworks; + +#define VAR_RELAY_DOMAINS "relay_domains" +#define DEF_RELAY_DOMAINS "$mydestination, $virtual_maps" +extern char *var_relay_domains; + +#define VAR_CLIENT_CHECKS "smtpd_client_restrictions" +#define DEF_CLIENT_CHECKS "" +extern char *var_client_checks; + +#define VAR_HELO_REQUIRED "smtpd_helo_required" +#define DEF_HELO_REQUIRED 0 +extern bool var_helo_required; + +#define VAR_HELO_CHECKS "smtpd_helo_restrictions" +#define DEF_HELO_CHECKS "" +extern char *var_helo_checks; + +#define VAR_MAIL_CHECKS "smtpd_sender_restrictions" +#define DEF_MAIL_CHECKS "" +extern char *var_mail_checks; + +#define VAR_RCPT_CHECKS "smtpd_recipient_restrictions" +#define DEF_RCPT_CHECKS PERMIT_MYNETWORKS "," CHECK_RELAY_DOMAINS +extern char *var_rcpt_checks; + + /* + * Names of specific restrictions, and the corresponding configuration + * parameters that control the status codes sent in response to rejected + * requests. + */ +#define PERMIT_ALL "permit" +#define REJECT_ALL "reject" +#define VAR_REJECT_CODE "reject_code" +#define DEF_REJECT_CODE 550 +extern int var_reject_code; + +#define REJECT_UNKNOWN_CLIENT "reject_unknown_client" +#define VAR_UNK_CLIENT_CODE "unknown_client_reject_code" +#define DEF_UNK_CLIENT_CODE 450 +extern int var_unk_client_code; + +#define PERMIT_MYNETWORKS "permit_mynetworks" + +#define PERMIT_NAKED_IP_ADDR "permit_naked_ip_address" + +#define REJECT_INVALID_HOSTNAME "reject_invalid_hostname" +#define VAR_BAD_NAME_CODE "invalid_hostname_reject_code" +#define DEF_BAD_NAME_CODE 501 +extern int var_bad_name_code; + +#define REJECT_UNKNOWN_HOSTNAME "reject_unknown_hostname" +#define VAR_UNK_NAME_CODE "unknown_hostname_reject_code" +#define DEF_UNK_NAME_CODE 450 +extern int var_unk_name_code; + +#define REJECT_UNKNOWN_ADDRESS "reject_unknown_address" +#define VAR_UNK_ADDR_CODE "unknown_address_reject_code" +#define DEF_UNK_ADDR_CODE 450 +extern int var_unk_addr_code; + +#define CHECK_RELAY_DOMAINS "check_relay_domains" +#define VAR_RELAY_CODE "relay_domains_reject_code" +#define DEF_RELAY_CODE 550 +extern int var_relay_code; + +#define PERMIT_MX_BACKUP "permit_mx_backup" + +#define VAR_ACCESS_MAP_CODE "access_map_reject_code" +#define DEF_ACCESS_MAP_CODE 550 +extern int var_access_map_code; + +#define CHECK_CLIENT_ACL "check_client_access" +#define CHECK_HELO_ACL "check_helo_access" +#define CHECK_SENDER_ACL "check_sender_access" +#define CHECK_RECIP_ACL "check_recipient_access" + +#define REJECT_MAPS_RBL "reject_maps_rbl" +#define VAR_MAPS_RBL_CODE "maps_rbl_reject_code" +#define DEF_MAPS_RBL_CODE 550 +extern int var_maps_rbl_code; + +#define VAR_MAPS_RBL_DOMAINS "maps_rbl_domains" +#define DEF_MAPS_RBL_DOMAINS "rbl.maps.vix.com" +extern char *var_maps_rbl_domains; + + /* + * Other. + */ +#define VAR_PROCNAME "process_name" +extern char *var_procname; + +#define VAR_PID "process_id" +extern int var_pid; + +#define VAR_DEBUG_COMMAND "debugger_command" + + /* + * Paranoia: save files instead of deleting them. + */ +#define VAR_DONT_REMOVE "dont_remove" +#define DEF_DONT_REMOVE 0 +extern bool var_dont_remove; + +extern void mail_params_init(void); + +/* 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_pathname.c b/postfix/global/mail_pathname.c new file mode 100644 index 000000000..32fa109fe --- /dev/null +++ b/postfix/global/mail_pathname.c @@ -0,0 +1,44 @@ +/*++ +/* NAME +/* mail_pathname 3 +/* SUMMARY +/* generate pathname from mailer service class and name +/* SYNOPSIS +/* #include +/* +/* char *mail_pathname(service_class, service_name) +/* char *service_class; +/* char *service_name; +/* DESCRIPTION +/* mail_pathname() translates the specified service class and name +/* to a pathname. The result should be passed to myfree() when it +/* no longer needed. +/* 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 + +/* Global library. */ + +#include "mail_proto.h" + +/* mail_pathname - map service class and service name to pathname */ + +char *mail_pathname(const char *service_class, const char *service_name) +{ + return (concatenate(service_class, "/", service_name, (char *) 0)); +} diff --git a/postfix/global/mail_print.c b/postfix/global/mail_print.c new file mode 100644 index 000000000..6500441ff --- /dev/null +++ b/postfix/global/mail_print.c @@ -0,0 +1,206 @@ +/*++ +/* NAME +/* mail_print 3 +/* SUMMARY +/* intra-mail system write routine +/* SYNOPSIS +/* #include +/* +/* int mail_print(stream, format, ...) +/* VSTREAM *stream; +/* const char *format; +/* +/* int mail_vprint(stream, format, ap) +/* VSTREAM *stream; +/* const char *format; +/* va_list ap; +/* +/* void mail_print_register(letter, name, print_fn) +/* int letter; +/* const char *name; +/* void (*print_fn)(VSTREAM *stream, const char *data); +/* DESCRIPTION +/* mail_print() prints one or more null-delimited strings to +/* the named stream, each string being converted according to the +/* contents of the \fIformat\fR argument. +/* +/* mail_vprint() provides an alternative interface. +/* +/* mail_print_register() registers the named print function for +/* the specified letter, for the named type. +/* +/* Arguments: +/* .IP stream +/* The stream to print to. +/* .IP format +/* Format string, which is interpreted as follows: +/* .RS +/* .IP "white space" +/* White space in the format string is ignored. +/* .IP %s +/* The corresponding argument has type (char *). +/* .IP %d +/* The corresponding argument has type (int). +/* .IP %ld +/* The corresponding argument has type (long). +/* .IP %letter +/* Call the print routine that was registered for the specified letter. +/* .PP +/* Anything else in a format string is a fatal error. +/* .RE +/* .IP letter +/* Format letter that is bound to the \fIprint_fn\fR print function. +/* .IP name +/* Descriptive string for verbose logging. +/* .IP print_fn +/* A print function. It takes as arguments: +/* .RS +/* .IP stream +/* The stream to print to. +/* .IP data +/* A generic data pointer. It is up to the function +/* to do any necessary casts to the data-specific type. +/* .RE +/* 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 + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include "mail_proto.h" + + /* + * Provision for the user to register type-specific scanners for + * applications with unusual requirements. + */ +typedef struct { + int letter; + char *name; + MAIL_PRINT_FN print_fn; +} MAIL_PRINT; + +MAIL_PRINT *mail_print_tab = 0; +int mail_print_tablen = 0; + +/* mail_print_register - register printer function */ + +void mail_print_register(int letter, const char *name, MAIL_PRINT_FN print_fn) +{ + MAIL_PRINT *tp; + + for (tp = 0; tp < mail_print_tab + mail_print_tablen; tp++) + if (tp->letter == letter) + msg_panic("mail_print_register: redefined letter: %c", letter); + + /* + * Tight fit allocation. We're not registering lots of characters. + */ + if (mail_print_tab == 0) { + mail_print_tablen = 1; + mail_print_tab = (MAIL_PRINT *) + mymalloc(sizeof(*mail_print_tab) * mail_print_tablen); + } else { + mail_print_tablen++; + mail_print_tab = (MAIL_PRINT *) + myrealloc((char *) mail_print_tab, + sizeof(*mail_print_tab) * mail_print_tablen); + } + tp = mail_print_tab + mail_print_tablen - 1; + tp->letter = letter; + tp->name = mystrdup(name); + tp->print_fn = print_fn; +} + +/* mail_vprint - print null-delimited data to stream */ + +int mail_vprint(VSTREAM *stream, const char *fmt, va_list ap) +{ + const char *cp; + int lflag; + char *sval; + int ival; + long lval; + int error; + MAIL_PRINT *tp; + + for (cp = fmt; (error = vstream_ferror(stream)) == 0 && *cp != 0; cp++) { + if (ISSPACE(*cp)) + continue; + if (*cp != '%') + msg_fatal("mail_vprint: bad format: %.*s>%c<%s", + cp - fmt, fmt, *cp, cp + 1); + if ((lflag = (*++cp == 'l')) != 0) + cp++; + + switch (*cp) { + case 's': + sval = va_arg(ap, char *); + if (msg_verbose) + msg_info("print string: %s", sval); + vstream_fputs(sval, stream); + VSTREAM_PUTC(0, stream); + break; + case 'd': + if (lflag) { + lval = va_arg(ap, long); + if (msg_verbose) + msg_info("print long: %ld", lval); + vstream_fprintf(stream, "%ld", lval); + } else { + ival = va_arg(ap, int); + if (msg_verbose) + msg_info("print int: %d", ival); + vstream_fprintf(stream, "%d", ival); + } + VSTREAM_PUTC(0, stream); + break; + default: + for (tp = mail_print_tab; tp < mail_print_tab + mail_print_tablen; tp++) + if (tp->letter == *cp) { + if (msg_verbose) + msg_info("print %s", tp->name); + tp->print_fn(stream, va_arg(ap, char *)); + break; + } + if (tp >= mail_print_tab + mail_print_tablen) + msg_fatal("mail_vprint: bad format: %.*s>%c<%s", + cp - fmt, fmt, *cp, cp + 1); + } + } + return (error); +} + +/* mail_print - print null-delimited data to stream */ + +int mail_print(VSTREAM *stream, const char *fmt,...) +{ + int status; + va_list ap; + + va_start(ap, fmt); + status = mail_vprint(stream, fmt, ap); + va_end(ap); + return (status); +} diff --git a/postfix/global/mail_proto.h b/postfix/global/mail_proto.h new file mode 100644 index 000000000..d3934dc53 --- /dev/null +++ b/postfix/global/mail_proto.h @@ -0,0 +1,106 @@ +#ifndef _MAIL_PROTO_H_INCLUDED_ +#define _MAIL_PROTO_H_INCLUDED_ + +/*++ +/* NAME +/* mail_proto 3h +/* SUMMARY +/* mail internal IPC support +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include +#include + + /* + * Names of services: these are the names if INET ports, UNIX-domain sockets + * or FIFOs that a service listens on. + */ +#define MAIL_SERVICE_BOUNCE "bounce" +#define MAIL_SERVICE_CLEANUP "cleanup" +#define MAIL_SERVICE_DEFER "defer" +#define MAIL_SERVICE_FORWARD "forward" +#define MAIL_SERVICE_LOCAL "local" +#define MAIL_SERVICE_PICKUP "pickup" +#define MAIL_SERVICE_QUEUE "qmgr" +#define MAIL_SERVICE_RESOLVE "resolve" +#define MAIL_SERVICE_REWRITE "rewrite" +#define MAIL_SERVICE_VIRTUAL "virtual" +#define MAIL_SERVICE_SMTP "smtp" +#define MAIL_SERVICE_SMTPD "smtpd" +#define MAIL_SERVICE_SHOWQ "showq" + + /* + * Well-known socket or FIFO directories. The main difference is in file + * access permissions. + */ +#define MAIL_CLASS_PUBLIC "public" +#define MAIL_CLASS_PRIVATE "private" + + /* + * When sending across a list of objects, this is how we signal the list + * end. + */ +#define MAIL_EOF "@" + + /* + * Generic triggers. + */ +#define TRIGGER_REQ_WAKEUP 'W' /* wakeup */ + + /* + * Queue manager requests. + */ +#define QMGR_REQ_SCAN_DEFERRED 'D' /* scan deferred queue */ +#define QMGR_REQ_SCAN_INCOMING 'I' /* scan incoming queue */ +#define QMGR_REQ_FLUSH_DEAD 'F' /* flush dead xport/site */ +#define QMGR_REQ_SCAN_ALL 'A' /* ignore time stamps */ + + /* + * Functional interface. + */ +#define MAIL_SCAN_MORE 0 +#define MAIL_SCAN_DONE 1 +#define MAIL_SCAN_ERROR -1 + +typedef int (*MAIL_SCAN_FN) (const char *, char *); +typedef void (*MAIL_PRINT_FN) (VSTREAM *, const char *); +extern VSTREAM *mail_connect(const char *, const char *, int); +extern VSTREAM *mail_connect_wait(const char *, const char *); +extern int mail_scan(VSTREAM *, const char *,...); +extern void mail_scan_register(int, const char *, MAIL_SCAN_FN); +extern void mail_print_register(int, const char *, MAIL_PRINT_FN); +extern int mail_print(VSTREAM *, const char *,...); +extern int mail_command_write(const char *, const char *, const char *,...); +extern int mail_command_read(VSTREAM *, char *,...); +extern int mail_trigger(const char *, const char *, const char *, int); +extern char *mail_pathname(const char *, const char *); + + /* + * Stuff that needs + */ +extern int mail_vprint(VSTREAM *, const char *, va_list); +extern int mail_vscan(VSTREAM *, const char *, va_list); + +/* 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_queue.c b/postfix/global/mail_queue.c new file mode 100644 index 000000000..cecdfb31a --- /dev/null +++ b/postfix/global/mail_queue.c @@ -0,0 +1,383 @@ +/*++ +/* NAME +/* mail_queue 3 +/* SUMMARY +/* mail queue file access +/* SYNOPSIS +/* #include +/* +/* VSTREAM *mail_queue_enter(queue_name, mode) +/* const char *queue_name; +/* int mode; +/* +/* VSTREAM *mail_queue_open(queue_name, queue_id, flags, mode) +/* const char *queue_name; +/* const char *queue_id; +/* int flags; +/* int mode; +/* +/* char *mail_queue_dir(buf, queue_name, queue_id) +/* VSTRING *buf; +/* const char *queue_name; +/* const char *queue_id; +/* +/* char *mail_queue_path(buf, queue_name, queue_id) +/* VSTRING *buf; +/* const char *queue_name; +/* const char *queue_id; +/* +/* int mail_queue_rename(queue_id, old_queue, new_queue) +/* const char *queue_id; +/* const char *old_queue; +/* const char *new_queue; +/* +/* int mail_queue_remove(queue_name, queue_id) +/* const char *queue_name; +/* const char *queue_id; +/* +/* int mail_queue_name_ok(queue_name) +/* const char *queue_name; +/* +/* int mail_queue_id_ok(queue_id) +/* const char *queue_id; +/* DESCRIPTION +/* This module encapsulates access to the mail queue hierarchy. +/* Unlike most other modules, this one does not abort the program +/* in case of file access problems. But it does abort when the +/* application attempts to use a malformed queue name or queue id. +/* +/* mail_queue_enter() creates an entry in the named queue. The queue +/* id is the file base name, see VSTREAM_PATH(). Queue ids are +/* relatively short strings and are recycled in the course of time. +/* The only guarantee given is that on a given machine, no two queue +/* entries will have the same queue ID at the same time. +/* +/* mail_queue_open() opens the named queue file. The \fIflags\fR +/* and \fImode\fR arguments are as with open(2). The result is a +/* null pointer in case of problems. +/* +/* mail_queue_dir() returns the directory name of the specified queue +/* file. When a null result buffer pointer is provided, the result is +/* written to a private buffer that may be overwritten upon the next +/* call. +/* +/* mail_queue_path() returns the pathname of the specified queue +/* file. When a null result buffer pointer is provided, the result +/* is written to a private buffer that may be overwritten upon the +/* next call. +/* +/* mail_queue_rename() renames a queue file. A non-zero result +/* means the operation failed. +/* +/* mail_queue_remove() renames the named queue file. A non-zero result +/* means the operation failed. +/* +/* mail_queue_name_ok() validates a mail queue name and returns +/* non-zero (true) if the name contains no nasty characters. +/* +/* mail_queue_id_ok() does the same thing for mail queue ID names. +/* DIAGNOSTICS +/* Panic: invalid queue name or id given to mail_queue_path(), +/* mail_queue_rename(), or mail_queue_remove(). +/* Fatal error: out of memory. +/* 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 /* rename() */ +#include +#include +#include +#include +#include +#include /* gettimeofday, not in POSIX */ +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include "file_id.h" +#include "mail_params.h" +#include "mail_queue.h" + +#define STR vstring_str + +/* mail_queue_dir - construct mail queue directory name */ + +const char *mail_queue_dir(VSTRING *buf, const char *queue_name, + const char *queue_id) +{ + char *myname = "mail_queue_dir"; + static VSTRING *private_buf = 0; + static VSTRING *hash_buf = 0; + static ARGV *hash_queue_names = 0; + char **cpp; + + /* + * Sanity checks. + */ + if (mail_queue_name_ok(queue_name) == 0) + msg_panic("%s: bad queue name: %s", myname, queue_name); + if (mail_queue_id_ok(queue_id) == 0) + msg_panic("%s: bad queue id: %s", myname, queue_id); + + /* + * Initialize. + */ + if (buf == 0) { + if (private_buf == 0) + private_buf = vstring_alloc(100); + buf = private_buf; + } + if (hash_buf == 0) { + hash_buf = vstring_alloc(100); + hash_queue_names = argv_split(var_hash_queue_names, " \t\r\n,"); + } + + /* + * First, put the basic queue directory name into place. + */ + vstring_strcpy(buf, queue_name); + vstring_strcat(buf, "/"); + + /* + * Then, see if we need to append a little directory forest. + */ + for (cpp = hash_queue_names->argv; *cpp; cpp++) { + if (strcasecmp(*cpp, queue_name) == 0) { + vstring_strcat(buf, + dir_forest(hash_buf, queue_id, var_hash_queue_depth)); + break; + } + } + return (STR(buf)); +} + +/* mail_queue_path - map mail queue id to path name */ + +const char *mail_queue_path(VSTRING *buf, const char *queue_name, + const char *queue_id) +{ + static VSTRING *private_buf = 0; + + /* + * Initialize. + */ + if (buf == 0) { + if (private_buf == 0) + private_buf = vstring_alloc(100); + buf = private_buf; + } + + /* + * Append the queue id to the possibly hashed queue directory. + */ + (void) mail_queue_dir(buf, queue_name, queue_id); + vstring_strcat(buf, queue_id); + return (STR(buf)); +} + +/* mail_queue_mkdirs - fill in missing directories */ + +static int mail_queue_mkdirs(const char *path) +{ + char *myname = "mail_queue_mkdirs"; + char *saved_path = mystrdup(path); + int ret; + + /* + * Truncate a copy of the pathname (for safety sake), and create the + * missing directories. + */ + if (split_at_right(saved_path, '/') == 0) + msg_panic("%s: no slash in: %s", myname, saved_path); + ret = make_dirs(saved_path, 0700); + myfree(saved_path); + return (ret); +} + +/* mail_queue_rename - move message to another queue */ + +int mail_queue_rename(const char *queue_id, const char *old_queue, + const char *new_queue) +{ + VSTRING *old_buf = vstring_alloc(100); + VSTRING *new_buf = vstring_alloc(100); + int error; + + /* + * Try the operation. If it fails, see if it is because of missing + * intermediate directories. + */ + error = rename(mail_queue_path(old_buf, old_queue, queue_id), + mail_queue_path(new_buf, new_queue, queue_id)); + if (error != 0 && mail_queue_mkdirs(STR(new_buf)) == 0) + error = rename(STR(old_buf), STR(new_buf)); + + /* + * Cleanup. + */ + vstring_free(old_buf); + vstring_free(new_buf); + + return (error); +} + +/* mail_queue_remove - remove mail queue file */ + +int mail_queue_remove(const char *queue_name, const char *queue_id) +{ + return (REMOVE(mail_queue_path((VSTRING *) 0, queue_name, queue_id))); +} + +/* mail_queue_name_ok - validate mail queue name */ + +int mail_queue_name_ok(const char *queue_name) +{ + const char *cp; + + for (cp = queue_name; *cp; cp++) + if (!ISALNUM(*cp)) + return (0); + return (1); +} + +/* mail_queue_id_ok - validate mail queue id */ + +int mail_queue_id_ok(const char *queue_id) +{ + const char *cp; + + for (cp = queue_id; *cp; cp++) + if (!ISALNUM(*cp)) + return (0); + return (1); +} + +/* mail_queue_enter - make mail queue entry with locally-unique name */ + +VSTREAM *mail_queue_enter(const char *queue_name, int mode) +{ + char *myname = "mail_queue_enter"; + static VSTRING *id_buf; + static int pid; + static VSTRING *path_buf; + static VSTRING *temp_path; + struct timeval tv; + int fd; + const char *file_id; + VSTREAM *stream; + + /* + * Initialize. + */ + if (id_buf == 0) { + pid = getpid(); + id_buf = vstring_alloc(10); + path_buf = vstring_alloc(10); + temp_path = vstring_alloc(100); + } + GETTIMEOFDAY(&tv); + + /* + * Create a file with a temporary name that does not collide. The process + * ID alone is not sufficiently unique: maildrops can be shared via the + * network. Not that I recommend using a network-based queue, or having + * multiple hosts write to the same queue, but we should try to avoid + * losing mail if we can. + * + * If someone is racing against us, try to win. + */ + for (;;) { + vstring_sprintf(temp_path, "%s/%d.%d", queue_name, + (int) tv.tv_usec, pid); + if ((fd = open(STR(temp_path), O_RDWR | O_CREAT | O_EXCL, mode)) >= 0) + break; + if (errno == EEXIST || errno == EISDIR) { + if ((int) ++tv.tv_usec < 0) + tv.tv_usec = 0; + continue; + } + msg_warn("%s: create file %s: %m", myname, STR(temp_path)); + sleep(10); + } + + /* + * Rename the file to something that is derived from the file ID. I saw + * this idea first being used in Zmailer. On any reasonable file system + * the file ID is guaranteed to be unique. Better let the OS resolve + * collisions than doing a worse job in an application. Another + * attractive property of file IDs is that they can appear in messages + * without leaking a significant amount of system information (unlike + * process ids). Not so nice is that files need to be renamed when they + * are moved to another file system. + * + * If someone is racing against us, try to win. + */ + file_id = get_file_id(fd); + GETTIMEOFDAY(&tv); + + for (;;) { + vstring_sprintf(id_buf, "%05X%s", (int) tv.tv_usec, file_id); + mail_queue_path(path_buf, queue_name, STR(id_buf)); + if (rename(STR(temp_path), STR(path_buf)) == 0) /* success */ + break; + if (errno == EPERM || errno == EISDIR) {/* collision. weird. */ + if ((int) ++tv.tv_usec < 0) + tv.tv_usec = 0; + continue; + } + if (errno != ENOENT || mail_queue_mkdirs(STR(path_buf)) < 0) { + msg_warn("%s: rename %s to %s: %m", myname, + STR(temp_path), STR(path_buf)); + sleep(10); + } + } + + stream = vstream_fdopen(fd, O_RDWR); + vstream_control(stream, VSTREAM_CTL_PATH, STR(path_buf), VSTREAM_CTL_END); + return (stream); +} + +/* mail_queue_open - open mail queue file */ + +VSTREAM *mail_queue_open(const char *queue_name, const char *queue_id, + int flags, int mode) +{ + const char *path = mail_queue_path((VSTRING *) 0, queue_name, queue_id); + VSTREAM *fp; + + /* + * Try the operation. If file creation fails, see if it is because of a + * missing subdirectory. + */ + if ((fp = vstream_fopen(path, flags, mode)) == 0) + if ((flags & O_CREAT) == O_CREAT && mail_queue_mkdirs(path) == 0) + fp = vstream_fopen(path, flags, mode); + return (fp); +} diff --git a/postfix/global/mail_queue.h b/postfix/global/mail_queue.h new file mode 100644 index 000000000..f052f4533 --- /dev/null +++ b/postfix/global/mail_queue.h @@ -0,0 +1,57 @@ +#ifndef _MAIL_QUEUE_H_INCLUDED_ +#define _MAIL_QUEUE_H_INCLUDED_ + +/*++ +/* NAME +/* mail_queue 3h +/* SUMMARY +/* mail queue access +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include + + /* + * Mail queue names. + */ +#define MAIL_QUEUE_MAILDROP "maildrop" +#define MAIL_QUEUE_INCOMING "incoming" +#define MAIL_QUEUE_ACTIVE "active" +#define MAIL_QUEUE_DEFERRED "deferred" +#define MAIL_QUEUE_DEFER "defer" +#define MAIL_QUEUE_BOUNCE "bounce" +#define MAIL_QUEUE_CORRUPT "corrupt" + + /* + * Queue file modes. + */ +#define MAIL_QUEUE_STAT_READY (S_IRUSR | S_IWUSR | S_IXUSR) +#define MAIL_QUEUE_STAT_CORRUPT (S_IRUSR) + +extern struct VSTREAM *mail_queue_enter(const char *, int); +extern struct VSTREAM *mail_queue_open(const char *, const char *, int, int); +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_name_ok(const char *); +extern int mail_queue_id_ok(const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/mail_run.c b/postfix/global/mail_run.c new file mode 100644 index 000000000..4b469b6cc --- /dev/null +++ b/postfix/global/mail_run.c @@ -0,0 +1,147 @@ +/*++ +/* NAME +/* mail_run 3 +/* SUMMARY +/* run mail component program +/* SYNOPSIS +/* #include +/* +/* int mail_run_foreground(dir, argv) +/* const char *dir; +/* char **argv; +/* +/* int mail_run_background(dir, argv) +/* const char *dir; +/* char **argv; +/* +/* NORETURN mail_run_replace(dir, argv) +/* const char *dir; +/* char **argv; +/* DESCRIPTION +/* This module runs programs that live in the mail program directory. +/* Each routine takes a directory and a command-line array. The program +/* pathname is built by prepending the directory and a slash to the +/* command name. +/* +/* mail_run_foreground() runs the named command in the foreground and +/* waits until the command terminates. +/* +/* mail_run_background() runs the named command in the background. +/* +/* mail_run_replace() attempts to replace the current process by +/* an instance of the named command. This function never returns. +/* +/* Arguments: +/* .IP argv +/* A null-terminated command-line vector. The first array element +/* is the base name of the program to be executed. +/* DIAGNOSTICS +/* The result is (-1) if the command could not be run. Otherwise, +/* mail_run_foreground() returns the termination status of the +/* command. mail_run_background() returns the process id in case +/* of success. +/* CONFIGURATION PARAMETERS +/* program_directory: directory with Postfix executables; +/* fork_attempts: number of attempts to fork() a process; +/* fork_delay: delay in seconds between fork() attempts. +/* 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 + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include "mail_params.h" +#include "mail_run.h" + +/* mail_run_foreground - run command in foreground */ + +int mail_run_foreground(const char *dir, char **argv) +{ + int count; + char *path; + WAIT_STATUS_T status; + int pid; + int wpid; + +#define RETURN(x) { myfree(path); return(x); } + + path = concatenate(dir, "/", argv[0], (char *) 0); + + for (count = 0; count < var_fork_tries; count++) { + switch (pid = fork()) { + case -1: + msg_warn("fork %s: %m", path); + break; + case 0: + execv(path, argv); + msg_fatal("execv %s: %m", path); + default: + do { + wpid = waitpid(pid, &status, 0); + } while (wpid == -1 && errno == EINTR); + RETURN(wpid == -1 ? -1 : + WIFEXITED(status) ? WEXITSTATUS(status) : 1) + } + sleep(var_fork_delay); + } + RETURN(-1); +} + +/* mail_run_background - run command in background */ + +int mail_run_background(const char *dir, char **argv) +{ + int count; + char *path; + int pid; + +#define RETURN(x) { myfree(path); return(x); } + + path = concatenate(dir, "/", argv[0], (char *) 0); + + for (count = 0; count < var_fork_tries; count++) { + switch (pid = fork()) { + case -1: + msg_warn("fork %s: %m", path); + break; + case 0: + execv(path, argv); + msg_fatal("execv %s: %m", path); + default: + RETURN(pid); + } + sleep(var_fork_delay); + } + RETURN(-1); +} + +/* mail_run_replace - run command, replacing current process */ + +NORETURN mail_run_replace(const char *dir, char **argv) +{ + char *path; + + path = concatenate(dir, "/", argv[0], (char *) 0); + execv(path, argv); + msg_fatal("execv %s: %m", path); +} diff --git a/postfix/global/mail_run.h b/postfix/global/mail_run.h new file mode 100644 index 000000000..b85109f9f --- /dev/null +++ b/postfix/global/mail_run.h @@ -0,0 +1,31 @@ +#ifndef _MAIL_RUN_H_INCLUDED_ +#define _MAIL_RUN_H_INCLUDED_ + +/*++ +/* NAME +/* mail_run 3h +/* SUMMARY +/* run mail component program +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern int mail_run_foreground(const char *, char **); +extern int mail_run_background(const char *, char **); +extern NORETURN mail_run_replace(const char *, char **); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/mail_scan.c b/postfix/global/mail_scan.c new file mode 100644 index 000000000..74a580f23 --- /dev/null +++ b/postfix/global/mail_scan.c @@ -0,0 +1,271 @@ +/*++ +/* NAME +/* mail_scan 3 +/* SUMMARY +/* intra-mail read routine +/* SYNOPSIS +/* #include +/* +/* int mail_scan(stream, format, ...) +/* VSTREAM *stream; +/* const char *format; +/* +/* void mail_scan_register(letter, name, scan_fn) +/* int letter; +/* const char *name; +/* int (*scan_fn)(const char *string, char *result); +/* DESCRIPTION +/* mail_scan() reads one or more null-delimited strings from +/* the named stream, each string being converted according to the +/* contents of the \fIformat\fR argument. +/* The result value is the number of successful conversions. +/* +/* mail_scan_register() registers an input conversion function +/* for the specified letter. +/* +/* Arguments: +/* .IP stream +/* Stream to read from. +/* .IP format +/* Format string, which is interpreted as follows: +/* .RS +/* .IP "white space" +/* White space in the format string is ignored. +/* .IP %s +/* The corresponding argument has type (VSTRING *). +/* .IP %d +/* The corresponding argument has type (int *). +/* .IP %ld +/* The corresponding argument has type (long *). +/* .IP %letter +/* Call the input conversion routine that was registered for +/* the specified letter. +/* .PP +/* Anything else in a format string is a fatal error. +/* .RE +/* .IP letter +/* Format letter that is bound to the \fIscan_fn\fR input +/* conversion function. +/* .IP name +/* Descriptive string for verbose logging. +/* .IP scan_fn +/* An input conversion function. It takes as arguments: +/* .RS +/* .IP string +/* The null-terminated string to be converted. +/* .IP result +/* A character pointer to the result. It is up to the function +/* to do any necessary casts to the data-specific type. +/* .RE +/* .PP +/* The function result values are as follows: +/* .RS +/* .IP MAIL_SCAN_ERROR +/* The expected input could not be read. +/* .IP MAIL_SCAN_DONE +/* The operation completed successfully. +/* .IP MAIL_SCAN_MORE +/* More input is expected. +/* .RE +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include "mail_proto.h" + + /* + * Provision for the user to register type-specific input conversion + * routines for applications with unusual requirements. + */ +typedef struct { + int letter; + char *name; + MAIL_SCAN_FN scanner; +} MAIL_SCAN; + +MAIL_SCAN *mail_scan_tab = 0; +int mail_scan_tablen = 0; + +/* mail_scan_register - register scanner function */ + +void mail_scan_register(int letter, const char *name, MAIL_SCAN_FN scanner) +{ + MAIL_SCAN *tp; + + for (tp = 0; tp < mail_scan_tab + mail_scan_tablen; tp++) + if (tp->letter == letter) + msg_panic("mail_scan_register: redefined letter: %c", letter); + + /* + * Tight fit allocation (as in: Robin Hood and the men that wear tights). + */ + if (mail_scan_tab == 0) { + mail_scan_tablen = 1; + mail_scan_tab = (MAIL_SCAN *) + mymalloc(sizeof(*mail_scan_tab) * mail_scan_tablen); + } else { + mail_scan_tablen++; + mail_scan_tab = (MAIL_SCAN *) + myrealloc((char *) mail_scan_tab, + sizeof(*mail_scan_tab) * mail_scan_tablen); + } + tp = mail_scan_tab + mail_scan_tablen - 1; + tp->letter = letter; + tp->name = mystrdup(name); + tp->scanner = scanner; +} + +/* mail_scan_any - read one null-delimited string from stream */ + +static int mail_scan_any(VSTREAM *stream, VSTRING *vp, char *what) +{ + if (vstring_fgets_null(vp, stream) == 0) { + msg_warn("mail_scan_any: got EOF; expected: %s", what); + return (-1); + } + if (msg_verbose) + msg_info("mail_scan_any: read %s: %s", what, vstring_str(vp)); + return (0); +} + +/* mail_scan_other - read user-defined type from stream */ + +static int mail_scan_other(VSTREAM *stream, VSTRING *vp, + MAIL_SCAN *tp, char *result) +{ + int ret; + + while ((ret = mail_scan_any(stream, vp, tp->name)) == 0) { + switch (tp->scanner(vstring_str(vp), result)) { + case MAIL_SCAN_MORE: + break; + case MAIL_SCAN_DONE: + return (0); + case MAIL_SCAN_ERROR: + return (-1); + } + } + return (ret); +} + +/* mail_scan_long - read long integer from stream */ + +static int mail_scan_long(VSTREAM *stream, VSTRING *vp, long *lp) +{ + int ret; + char junk; + + if ((ret = mail_scan_any(stream, vp, "long integer")) == 0) { + if (sscanf(vstring_str(vp), "%ld%c", lp, &junk) != 1) { + msg_warn("mail_scan_long: bad long long: %s", vstring_str(vp)); + ret = -1; + } + } + return (ret); +} + +/* mail_scan_int - read integer from stream */ + +static int mail_scan_int(VSTREAM *stream, VSTRING *vp, int *lp) +{ + int ret; + char junk; + + if ((ret = mail_scan_any(stream, vp, "integer")) == 0) { + if (sscanf(vstring_str(vp), "%d%c", lp, &junk) != 1) { + msg_warn("mail_scan_int: bad integer: %s", vstring_str(vp)); + ret = -1; + } + } + return (ret); +} + +/* mail_scan - read null-delimited data from stream */ + +int mail_scan(VSTREAM *stream, const char *fmt,...) +{ + va_list ap; + int count; + + va_start(ap, fmt); + count = mail_vscan(stream, fmt, ap); + va_end(ap); + return (count); +} + +/* mail_vscan - read null-delimited data from stream */ + +int mail_vscan(VSTREAM *stream, const char *fmt, va_list ap) +{ + const char *cp; + int lflag; + int count; + int error; + static VSTRING *tmp; + MAIL_SCAN *tp; + + if (tmp == 0) + tmp = vstring_alloc(100); + + for (count = 0, error = 0, cp = fmt; error == 0 && *cp != 0; cp++) { + if (ISSPACE(*cp)) + continue; + if (*cp != '%') + msg_fatal("mail_scan: bad format: %.*s>%c<%s", + cp - fmt, fmt, *cp, cp + 1); + if ((lflag = (*++cp == 'l')) != 0) + cp++; + + switch (*cp) { + case 's': + error = mail_scan_any(stream, va_arg(ap, VSTRING *), "string"); + break; + case 'd': + if (lflag) + error = mail_scan_long(stream, tmp, va_arg(ap, long *)); + else + error = mail_scan_int(stream, tmp, va_arg(ap, int *)); + break; + default: + for (tp = mail_scan_tab; tp < mail_scan_tab + mail_scan_tablen; tp++) + if (tp->letter == *cp) { + if (msg_verbose) + msg_info("mail_scan: %s", tp->name); + error = mail_scan_other(stream, tmp, tp, va_arg(ap, char *)); + break; + } + if (tp >= mail_scan_tab + mail_scan_tablen) + msg_fatal("mail_scan: bad format: %.*s>%c<%s", + cp - fmt, fmt, *cp, cp + 1); + } + if (error == 0) + count++; + } + return (count); +} diff --git a/postfix/global/mail_stream.c b/postfix/global/mail_stream.c new file mode 100644 index 000000000..a51c8f496 --- /dev/null +++ b/postfix/global/mail_stream.c @@ -0,0 +1,266 @@ +/*++ +/* NAME +/* mail_stream 3 +/* SUMMARY +/* mail stream management +/* SYNOPSIS +/* #include +/* +/* typedef struct { +/* .in +4 +/* VSTREAM *stream; +/* char *id; +/* private members... +/* .in -4 +/* } MAIL_STREAM; +/* +/* MAIL_STREAM *mail_stream_file(queue, mode, class, service) +/* const char *queue; +/* int mode; +/* const char *class; +/* const char *service; +/* +/* MAIL_STREAM *mail_stream_service(class, service) +/* const char *class; +/* const char *service; +/* +/* MAIL_STREAM *mail_stream_command(command) +/* const char *command; +/* +/* void mail_stream_cleanup(info) +/* MAIL_STREAM *info; +/* +/* int mail_stream_finish(info) +/* MAIL_STREAM *info; +/* DESCRIPTION +/* This module provides a generic interface to Postfix queue file +/* format messages to file, to Postfix server, or to external command. +/* The routines that open a stream return a handle with an initialized +/* stream and queue id member. The handle is either given to a cleanup +/* routine, to dispose of a failed request, or to a finish routine, to +/* complete the request. +/* +/* mail_stream_file() opens a mail stream to a newly-created file and +/* arranges for trigger delivery at finish time. This call never fails. +/* But it may take forever. +/* +/* mail_stream_command() opens a mail stream to external command, +/* and receives queue ID information from the command. The result +/* is a null pointer when the initial handshake fails. The command +/* is given to the shell only when necessary. At finish time, the +/* command is expected to send a completion status. +/* +/* mail_stream_service() opens a mail stream to Postfix service, +/* and receives queue ID information from the command. The result +/* is a null pointer when the initial handshake fails. At finish +/* time, the daemon is expected to send a completion status. +/* +/* mail_stream_cleanup() cancels the operation that was started with +/* any of the mail_stream_xxx() routines, and destroys the argument. +/* It is up to the caller to remove incomplete file objects. +/* +/* mail_stream_finish() completes the operation that was started with +/* any of the mail_stream_xxx() routines, and destroys the argument. +/* The result is any of the status codes defined in . +/* It is up to the caller to remove incomplete file objects. +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include + +/* Application-specific. */ + +static VSTRING *id_buf; + +#define FREE_AND_WIPE(free, arg) { if (arg) free(arg); arg = 0; } + +/* mail_stream_cleanup - clean up after success or failure */ + +void mail_stream_cleanup(MAIL_STREAM * info) +{ + FREE_AND_WIPE(info->close, info->stream); + FREE_AND_WIPE(myfree, info->id); + FREE_AND_WIPE(myfree, info->class); + FREE_AND_WIPE(myfree, info->service); + myfree((char *) info); +} + +/* mail_stream_finish_file - finish file mail stream */ + +static int mail_stream_finish_file(MAIL_STREAM * info) +{ + int status = 0; + static char wakeup[] = {TRIGGER_REQ_WAKEUP}; + + /* + * Make sure the message makes it to file. Set the execute bit when no + * write error was detected. + */ + if (vstream_fflush(info->stream) + || fchmod(vstream_fileno(info->stream), 0700) +#ifdef HAS_FSYNC + || fsync(vstream_fileno(info->stream)) +#endif + ) + status = CLEANUP_STAT_WRITE; + + /* + * Close the queue file and mark it as closed. Be prepared for + * vstream_fclose() to fail even after vstream_fflush() and fsync() + * reported no error. Reason: after a file is closed, some networked file + * systems copy the file out to another machine. Running the queue on a + * remote file system is not recommended, if only for performance + * reasons. + */ + if (info->close(info->stream)) + status = CLEANUP_STAT_WRITE; + info->stream = 0; + + /* + * When all is well, notify the next service that a new message has been + * queued. + */ + if (status == CLEANUP_STAT_OK) + mail_trigger(info->class, info->service, wakeup, sizeof(wakeup)); + + /* + * Cleanup. + */ + mail_stream_cleanup(info); + return (status); +} + +/* mail_stream_finish_ipc - finish IPC mail stream */ + +static int mail_stream_finish_ipc(MAIL_STREAM * info) +{ + int status = CLEANUP_STAT_WRITE; + + /* + * Receive the peer's completion status. + */ + if (mail_scan(info->stream, "%d", &status) != 1) + status = CLEANUP_STAT_WRITE; + + /* + * Cleanup. + */ + mail_stream_cleanup(info); + return (status); +} + +/* mail_stream_finish - finish action */ + +int mail_stream_finish(MAIL_STREAM * info) +{ + return (info->finish(info)); +} + +/* mail_stream_file - destination is file */ + +MAIL_STREAM *mail_stream_file(const char *queue, const char *class, + const char *service) +{ + MAIL_STREAM *info; + VSTREAM *stream; + + stream = mail_queue_enter(queue, 0600); + if (msg_verbose) + msg_info("open %s", VSTREAM_PATH(stream)); + + info = (MAIL_STREAM *) mymalloc(sizeof(*info)); + info->stream = stream; + info->finish = mail_stream_finish_file; + info->close = vstream_fclose; + info->id = mystrdup(basename(VSTREAM_PATH(stream))); + info->class = mystrdup(class); + info->service = mystrdup(service); + return (info); +} + +/* mail_stream_service - destination is service */ + +MAIL_STREAM *mail_stream_service(const char *class, const char *name) +{ + VSTREAM *stream; + MAIL_STREAM *info; + + if (id_buf == 0) + id_buf = vstring_alloc(10); + + stream = mail_connect_wait(class, name); + if (mail_scan(stream, "%s", id_buf) != 1) { + vstream_fclose(stream); + return (0); + } else { + info = (MAIL_STREAM *) mymalloc(sizeof(*info)); + info->stream = stream; + info->finish = mail_stream_finish_ipc; + info->close = vstream_fclose; + info->id = mystrdup(vstring_str(id_buf)); + info->class = 0; + info->service = 0; + return (info); + } +} + +/* mail_stream_command - destination is command */ + +MAIL_STREAM *mail_stream_command(const char *command) +{ + VSTREAM *stream; + MAIL_STREAM *info; + + if (id_buf == 0) + id_buf = vstring_alloc(10); + + /* + * Treat fork() failure as a transient problem. Treat bad handshake as a + * permanent error. + */ + while ((stream = vstream_popen(command, O_RDWR)) == 0) { + msg_warn("fork: %m"); + sleep(10); + } + if (mail_scan(stream, "%s", id_buf) != 1) { + vstream_pclose(stream); + return (0); + } else { + info = (MAIL_STREAM *) mymalloc(sizeof(*info)); + info->stream = stream; + info->finish = mail_stream_finish_ipc; + info->close = vstream_pclose; + info->id = mystrdup(vstring_str(id_buf)); + info->class = 0; + info->service = 0; + return (info); + } +} diff --git a/postfix/global/mail_stream.h b/postfix/global/mail_stream.h new file mode 100644 index 000000000..3ae6c624a --- /dev/null +++ b/postfix/global/mail_stream.h @@ -0,0 +1,55 @@ +#ifndef _MAIL_STREAM_H_INCLUDED_ +#define _MAIL_STREAM_H_INCLUDED_ + +/*++ +/* NAME +/* mail_stream 3h +/* SUMMARY +/* mail stream management +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include + + /* + * External interface. + */ +typedef struct MAIL_STREAM MAIL_STREAM; + +typedef int (*MAIL_STREAM_FINISH_FN) (MAIL_STREAM *); +typedef int (*MAIL_STREAM_CLOSE_FN) (VSTREAM *); + +struct MAIL_STREAM { + VSTREAM *stream; /* file or pipe or socket */ + char *id; /* queue id */ + MAIL_STREAM_FINISH_FN finish; /* finish code */ + MAIL_STREAM_CLOSE_FN close; /* close stream */ + char *class; /* trigger class */ + char *service; /* trigger service */ +}; + +extern MAIL_STREAM *mail_stream_file(const char *, const char *, const char *); +extern MAIL_STREAM *mail_stream_service(const char *, const char *); +extern MAIL_STREAM *mail_stream_command(const char *); +extern void mail_stream_cleanup(MAIL_STREAM *); +extern int mail_stream_finish(MAIL_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/mail_task.c b/postfix/global/mail_task.c new file mode 100644 index 000000000..050075456 --- /dev/null +++ b/postfix/global/mail_task.c @@ -0,0 +1,59 @@ +/*++ +/* NAME +/* mail_task 3 +/* SUMMARY +/* set task name for logging purposes +/* SYNOPSIS +/* #include +/* +/* const char *mail_task(argv0) +/* const char *argv0; +/* DESCRIPTION +/* mail_task() enforces consistent naming of mailer processes. +/* It strips pathname information from the process name, and +/* prepends the name of the mail system so that logfile entries +/* are easier to recognize. +/* +/* The result is volatile. Make a copy of the result if it is +/* to be used for any appreciable amount of time. +/* 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_params.h" +#include "mail_task.h" + +#define MAIL_TASK_FORMAT "postfix/%s" + +/* mail_task - clean up and decorate the process name */ + +const char *mail_task(const char *argv0) +{ + static VSTRING *canon_name; + const char *slash; + + if (canon_name == 0) + canon_name = vstring_alloc(10); + if ((slash = strrchr(argv0, '/')) != 0) + argv0 = slash + 1; + vstring_sprintf(canon_name, MAIL_TASK_FORMAT, argv0); + return (vstring_str(canon_name)); +} diff --git a/postfix/global/mail_task.h b/postfix/global/mail_task.h new file mode 100644 index 000000000..942753f89 --- /dev/null +++ b/postfix/global/mail_task.h @@ -0,0 +1,29 @@ +#ifndef _MAIL_TASK_H_INCLUDED_ +#define _MAIL_TASK_H_INCLUDED_ + +/*++ +/* NAME +/* mail_task 3h +/* SUMMARY +/* canonicalize process name +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern const char *mail_task(const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/mail_trigger.c b/postfix/global/mail_trigger.c new file mode 100644 index 000000000..66710b145 --- /dev/null +++ b/postfix/global/mail_trigger.c @@ -0,0 +1,95 @@ +/*++ +/* NAME +/* mail_trigger 3 +/* SUMMARY +/* trigger a mail service +/* SYNOPSIS +/* #include +/* +/* int mail_trigger(class, service, request, length) +/* const char *class; +/* const char *service; +/* const char *request; +/* int length; +/* DESCRIPTION +/* mail_trigger() wakes up the specified mail subsystem, by +/* sending it the specified request. +/* +/* Arguments: +/* .IP class +/* Name of a class of local transport channel endpoints, +/* either \fIpublic\fR (accessible by any local user) or +/* \fIprivate\fR (administrative access only). +/* .IP service +/* The name of a local transport endpoint within the named class. +/* .IP request +/* A string. The list of valid requests is service specific. +/* .IP length +/* The length of the request string. +/* DIAGNOSTICS +/* The result is -1 in case of problems, 0 otherwise. +/* Warnings are logged. +/* BUGS +/* Works with FIFO or UNIX-domain services only. +/* +/* Should use master.cf to find out what transport to use. +/* SEE ALSO +/* fifo_trigger(3) trigger a FIFO-based service +/* unix_trigger(3) trigger a UNIX_domain service +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include "mail_params.h" +#include "mail_proto.h" + +/* mail_trigger - trigger a service */ + +int mail_trigger(const char *class, const char *service, + const char *req_buf, int req_len) +{ + struct stat st; + char *path; + int status; + + /* + * XXX Some systems cannot tell the difference between a named pipe + * (fifo) or a UNIX-domain socket. So we may have to try both. + */ + path = mail_pathname(class, service); + if ((status = stat(path, &st)) < 0) { + /* void */ ; + } else if (S_ISFIFO(st.st_mode)) { + status = fifo_trigger(path, req_buf, req_len, var_trigger_timeout); + if (status < 0 && S_ISSOCK(st.st_mode)) + status = unix_trigger(path, req_buf, req_len, var_trigger_timeout); + } else if (S_ISSOCK(st.st_mode)) { + status = unix_trigger(path, req_buf, req_len, var_trigger_timeout); + } else { + msg_warn("%s is not a socket or a fifo", path); + status = -1; + } + myfree(path); + return (status); +} diff --git a/postfix/global/mail_version.h b/postfix/global/mail_version.h new file mode 100644 index 000000000..81ba2ba06 --- /dev/null +++ b/postfix/global/mail_version.h @@ -0,0 +1,32 @@ +#ifndef _MAIL_VERSION_H_INCLUDED_ +#define _MAIL_VERSION_H_INCLUDED_ + +/*++ +/* NAME +/* mail_version 3h +/* SUMMARY +/* globally configurable parameters +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Version of this program. + */ +#define VAR_MAIL_VERSION "mail_version" +#define DEF_MAIL_VERSION "Beta-19990122" +extern char *var_mail_version; + +/* 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/maps.c b/postfix/global/maps.c new file mode 100644 index 000000000..148ce8e56 --- /dev/null +++ b/postfix/global/maps.c @@ -0,0 +1,204 @@ +/*++ +/* NAME +/* maps 3 +/* SUMMARY +/* multi-dictionary search +/* SYNOPSIS +/* #include +/* +/* MAPS *maps_create(title, map_names) +/* const char *title; +/* const char *map_names; +/* +/* const char *maps_find(maps, key) +/* MAPS *maps; +/* const char *key; +/* +/* void maps_free(maps) +/* MAPS *maps; +/* DESCRIPTION +/* This module implements multi-dictionary searches. it goes +/* through the high-level dictionary interface and does file +/* locking. Dictionaries are opened read-only, and in-memory +/* dictionary instances are shared. +/* +/* maps_create() takes list of type:name pairs and opens the +/* named dictionaries. +/* The result is a handle that must be specified along with all +/* other maps_xxx() operations. +/* +/* maps_find() searches the specified list of dictionaries +/* in the specified order for the named key. The result is in +/* memory that is overwritten upon each call. +/* +/* maps_free() releases storage claimed by maps_create() +/* and conveniently returns a null pointer. +/* +/* Arguments: +/* .IP title +/* String used for diagnostics. Typically one specifies the +/* type of information stored in the lookup tables. +/* .IP map_names +/* Null-terminated string with type:name dictionary specifications, +/* separated by whitespace or commas. +/* .IP maps +/* A result from maps_create(). +/* .IP key +/* Null-terminated string with a lookup key. Table lookup is case +/* sensitive. +/* DIAGNOSTICS +/* Panic: inappropriate use; fatal errors: out of memory, unable +/* to open database. +/* +/* maps_find() returns a null pointer when the requested +/* information was not found. The global \fIdict_errno\fR +/* variable indicates if the last lookup failed due to a problem. +/* BUGS +/* The dictionary name space is flat, so dictionary names allocated +/* by maps_create() may collide with dictionary names allocated by +/* other methods. +/* +/* This functionality could be implemented by allowing the user to +/* specify dictionary search paths to dict_lookup() or dict_eval(). +/* However, that would either require that the dict(3) module adopts +/* someone else's list notation syntax, or that the dict(3) module +/* imposes syntax restrictions onto other software, neither of which +/* is desirable. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include "config.h" +#include "maps.h" + +/* maps_create - initialize */ + +MAPS *maps_create(const char *title, const char *map_names) +{ + char *myname = "maps_create"; + char *temp = mystrdup(map_names); + char *bufp = temp; + static char sep[] = " \t,\r\n"; + MAPS *maps; + DICT *dict; + char *map_type_name; + + /* + * Initialize. + */ + maps = (MAPS *) mymalloc(sizeof(*maps)); + maps->title = mystrdup(title); + maps->argv = argv_alloc(2); + + /* + * For each specified type:name pair, either register a new dictionary, + * or increment the reference count of an existing one. + */ + while ((map_type_name = mystrtok(&bufp, sep)) != 0) { + if (msg_verbose) + msg_info("%s: %s", myname, map_type_name); + if ((dict = dict_handle(map_type_name)) == 0) + dict = dict_open(map_type_name, O_RDONLY); + dict_register(map_type_name, dict); + argv_add(maps->argv, map_type_name, ARGV_END); + } + myfree(temp); + argv_terminate(maps->argv); + return (maps); +} + +/* maps_find - search a list of dictionaries */ + +const char *maps_find(MAPS *maps, const char *name) +{ + char *myname = "maps_find"; + char **map_name; + const char *expansion; + + for (map_name = maps->argv->argv; *map_name; map_name++) { + if ((expansion = dict_lookup(*map_name, name)) != 0) { + if (msg_verbose) + msg_info("%s: %s: %s = %s", myname, *map_name, name, expansion); + return (expansion); + } else if (dict_errno != 0) { + break; + } + } + if (msg_verbose) + msg_info("%s: %s: %s", myname, name, dict_errno ? + "search aborted" : "not found"); + return (0); +} + +/* maps_free - release storage */ + +MAPS *maps_free(MAPS *maps) +{ + char **map_name; + + for (map_name = maps->argv->argv; *map_name; map_name++) { + if (msg_verbose) + msg_info("maps_free: %s", *map_name); + dict_unregister(*map_name); + } + myfree(maps->title); + argv_free(maps->argv); + myfree((char *) maps); + return (0); +} + +#ifdef TEST + +#include +#include +#include + +main(int argc, char **argv) +{ + VSTRING *buf = vstring_alloc(100); + MAPS *maps; + const char *result; + + if (argc != 2) + msg_fatal("usage: %s maps", argv[0]); + msg_verbose = 2; + maps = maps_create("whatever", argv[1]); + + while (vstring_fgets_nonl(buf, VSTREAM_IN)) { + if ((result = maps_find(maps, vstring_str(buf))) != 0) { + vstream_printf("%s\n", result); + } else if (dict_errno != 0) { + msg_fatal("lookup error: %m"); + } else { + vstream_printf("not found\n"); + } + vstream_fflush(VSTREAM_OUT); + } + maps_free(maps); + vstring_free(buf); +} + +#endif diff --git a/postfix/global/maps.h b/postfix/global/maps.h new file mode 100644 index 000000000..dd0787b7d --- /dev/null +++ b/postfix/global/maps.h @@ -0,0 +1,37 @@ +#ifndef _MAPS_H_INCLUDED_ +#define _MAPS_H_INCLUDED_ + +/*++ +/* NAME +/* maps 3h +/* SUMMARY +/* multi-dictionary search +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Dictionary name storage. We're borrowing from the argv(3) module. + */ +typedef struct MAPS { + char *title; + struct ARGV *argv; +} MAPS; + +extern MAPS *maps_create(const char *, const char *); +extern const char *maps_find(MAPS *, const char *); +extern MAPS *maps_free(MAPS *); + +/* 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/mark_corrupt.c b/postfix/global/mark_corrupt.c new file mode 100644 index 000000000..994492ef1 --- /dev/null +++ b/postfix/global/mark_corrupt.c @@ -0,0 +1,56 @@ +/*++ +/* NAME +/* mark_corrupt 3 +/* SUMMARY +/* mark queue file as corrupt +/* SYNOPSIS +/* #include +/* +/* char *mark_corrupt(src) +/* VSTREAM *src; +/* DESCRIPTION +/* The \fBmark_corrupt\fR() routine marks the specified open +/* queue file as corrupt, and returns a suitable delivery status +/* so that the queue manager will do the right thing. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include +#include + +/* mark_corrupt - mark queue file as corrupt */ + +int mark_corrupt(VSTREAM *src) +{ + char *myname = "mark_corrupt"; + + /* + * For now, the result value is -1; this may become a bit mask, or + * something even more advanced than that, when the delivery status + * becomes more than just done/deferred. + */ + msg_warn("corrupted queue file: %s", VSTREAM_PATH(src)); + if (fchmod(vstream_fileno(src), MAIL_QUEUE_STAT_CORRUPT)) + msg_fatal("%s: fchmod %s: %m", myname, VSTREAM_PATH(src)); + return (-1); +} diff --git a/postfix/global/mark_corrupt.h b/postfix/global/mark_corrupt.h new file mode 100644 index 000000000..4fad8b74e --- /dev/null +++ b/postfix/global/mark_corrupt.h @@ -0,0 +1,35 @@ +#ifndef _MARK_CORRUPT_H_INCLUDED_ +#define _MARK_CORRUPT_H_INCLUDED_ + +/*++ +/* NAME +/* mark_corrupt 3h +/* SUMMARY +/* mark queue file as corrupt +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern int mark_corrupt(VSTREAM *); + +/* 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/mkmap.h b/postfix/global/mkmap.h new file mode 100644 index 000000000..d004d2faf --- /dev/null +++ b/postfix/global/mkmap.h @@ -0,0 +1,44 @@ +#ifndef _MKMAP_H_INCLUDED_ +#define _MKMAP_H_INCLUDED_ + +/*++ +/* NAME +/* mkmap 3h +/* SUMMARY +/* create or rewrite Postfix database +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * A database handle is an opaque structure. The user is not supposed to + * know its implementation. + */ +typedef struct MKMAP { + struct DICT *(*open) (const char *, int); + struct DICT *dict; + char *lock_file; + int lock_fd; +} MKMAP; + +extern MKMAP *mkmap_open(const char *, const char *, int); +extern void mkmap_append(MKMAP *, const char *, const char *); +extern void mkmap_close(MKMAP *); + +extern MKMAP *mkmap_dbm_open(const char *); +extern MKMAP *mkmap_hash_open(const char *); +extern MKMAP *mkmap_btree_open(const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/mkmap_db.c b/postfix/global/mkmap_db.c new file mode 100644 index 000000000..cc9eff87a --- /dev/null +++ b/postfix/global/mkmap_db.c @@ -0,0 +1,96 @@ +/*++ +/* NAME +/* mkmap_db 3 +/* SUMMARY +/* create or open database, DB style +/* SYNOPSIS +/* #include +/* +/* MKMAP *mkmap_hash_open(path) +/* const char *path; +/* +/* MKMAP *mkmap_btree_open(path) +/* const char *path; +/* DESCRIPTION +/* This module implements support for creating DB databases. +/* +/* mkmap_hash_open() and mkmap_btree_open() take a file name, +/* append the ".db" suffix, and create or open the named DB +/* database. This routine is a DB-specific helper for the more +/* general mkmap_open() interface. +/* +/* All errors are fatal. +/* SEE ALSO +/* dict_db(3), DB dictionary interface. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "mkmap.h" + +#ifdef HAS_DB +#ifdef PATH_DB_H +#include PATH_DB_H +#else +#include +#endif + +/* mkmap_db_open - create or open database */ + +static MKMAP *mkmap_db_open(const char *path, + DICT *(*db_open) (const char *, int)) +{ + MKMAP *mkmap = (MKMAP *) mymalloc(sizeof(*mkmap)); + + /* + * Fill in the generic members. + */ + mkmap->lock_file = concatenate(path, ".db", (char *) 0); + mkmap->open = db_open; + + /* + * Unfortunately, not all systems that might support db databases do + * support locking on open(), so we open the file before updating it. + */ + if ((mkmap->lock_fd = open(mkmap->lock_file, O_CREAT | O_RDWR, 0644)) < 0) + msg_fatal("open %s: %m", mkmap->lock_file); + + return (mkmap); +} + +/* mkmap_hash_open - create or open hashed DB file */ + +MKMAP *mkmap_hash_open(const char *path) +{ + return (mkmap_db_open(path, dict_hash_open)); +} + +/* mkmap_btree_open - create or open btree DB file */ + +MKMAP *mkmap_btree_open(const char *path) +{ + return (mkmap_db_open(path, dict_btree_open)); +} + +#endif diff --git a/postfix/global/mkmap_dbm.c b/postfix/global/mkmap_dbm.c new file mode 100644 index 000000000..618af3b72 --- /dev/null +++ b/postfix/global/mkmap_dbm.c @@ -0,0 +1,84 @@ +/*++ +/* NAME +/* mkmap 3 +/* SUMMARY +/* create or open database, DBM style +/* SYNOPSIS +/* #include +/* +/* MKMAP *mkmap_dbm_open(path) +/* const char *path; +/* DESCRIPTION +/* This module implements support for creating DBM databases. +/* +/* mkmap_dbm_open() takes a file name, appends the ".dir" and ".pag" +/* suffixes, and creates or opens the named DBM database. +/* This routine is a DBM-specific helper for the more general +/* mkmap_open() routine. +/* +/* All errors are fatal. +/* SEE ALSO +/* dict_dbm(3), DBM dictionary interface. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "mkmap.h" + +#ifdef HAS_DBM +#include + +/* mkmap_dbm_open - create or open database */ + +MKMAP *mkmap_dbm_open(const char *path) +{ + MKMAP *mkmap = (MKMAP *) mymalloc(sizeof(*mkmap)); + char *pag_file; + int pag_fd; + + /* + * Fill in the generic members. + */ + mkmap->lock_file = concatenate(path, ".dir", (char *) 0); + mkmap->open = dict_dbm_open; + + /* + * Unfortunately, not all systems support locking on open(), so we open + * the .dir and .pag files before truncating them. Keep one file open for + * locking. + */ + if ((mkmap->lock_fd = open(mkmap->lock_file, O_CREAT | O_RDWR, 0644)) < 0) + msg_fatal("open %s: %m", mkmap->lock_file); + + pag_file = concatenate(path, ".pag", (char *) 0); + if ((pag_fd = open(pag_file, O_CREAT | O_RDWR, 0644)) < 0) + msg_fatal("open %s: %m", pag_file); + if (close(pag_fd)) + msg_warn("close %s: %m", pag_file); + myfree(pag_file); + + return (mkmap); +} + +#endif diff --git a/postfix/global/mkmap_open.c b/postfix/global/mkmap_open.c new file mode 100644 index 000000000..530653d54 --- /dev/null +++ b/postfix/global/mkmap_open.c @@ -0,0 +1,164 @@ +/*++ +/* NAME +/* mkmap_open 3 +/* SUMMARY +/* create or rewrite database, generic interface +/* SYNOPSIS +/* #include +/* +/* MKMAP *mkmap_open(type, path, flags) +/* char *type; +/* char *path; +/* int flags; +/* +/* void mkmap_append(mkmap, key, value, lineno) +/* MKMAP *mkmap; +/* char *key; +/* char *value; +/* int lineno; +/* +/* void mkmap_close(mkmap) +/* MKMAP *mkmap; +/* DESCRIPTION +/* This module implements support for creating Postfix databases. +/* +/* mkmap_open() creates or truncates the named database, after +/* appending the appropriate suffixes to the specified filename. +/* Before the database is updated, it is locked for exclusive +/* access, and signal delivery is suspended. +/* All errors are fatal. +/* +/* mkmap_append() appends the named (key, value) pair to the +/* database. Update errors are fatal; duplicate keys are ignored +/* (but a warning is issued). +/* +/* mkmap_close() closes the database, releases any locks, +/* and resumes signal delivery. All errors are fatal. +/* SEE ALSO +/* sigdelay(3) suspend/resume signal delivery +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include "mkmap.h" + + /* + * Information about available database types. Here, we list only those map + * types that exist as files. Network-based maps are not of interest. + */ +typedef struct { + char *type; + MKMAP *(*create_or_open) (const char *); +} MKMAP_OPEN_INFO; + +MKMAP_OPEN_INFO mkmap_types[] = { +#ifdef HAS_DBM + "dbm", mkmap_dbm_open, +#endif +#ifdef HAS_DB + "hash", mkmap_hash_open, + "btree", mkmap_btree_open, +#endif + 0, +}; + +/* mkmap_append - append entry to map */ + +void mkmap_append(MKMAP *mkmap, const char *key, const char *value) +{ + dict_put(mkmap->dict, key, value); +} + +/* mkmap_close - close database */ + +void mkmap_close(MKMAP *mkmap) +{ + + /* + * Close the database and the locking file descriptor. + */ + dict_close(mkmap->dict); + if (close(mkmap->lock_fd) < 0) + msg_warn("close %s: %m", mkmap->lock_file); + + /* + * Resume signal delivery. + */ + sigresume(); + + /* + * Cleanup. + */ + myfree(mkmap->lock_file); + myfree((char *) mkmap); +} + +/* mkmap_open - create or truncate database */ + +MKMAP *mkmap_open(const char *type, const char *path, int flags) +{ + MKMAP *mkmap; + MKMAP_OPEN_INFO *mp; + + /* + * Find out what map type to use. + */ + for (mp = mkmap_types; /* void */ ; mp++) { + if (mp->type == 0) + msg_fatal("unsupported map type: %s", type); + if (strcmp(type, mp->type) == 0) + break; + } + if (msg_verbose) + msg_info("open %s %s", type, path); + + /* + * Create or open the desired map file(s). + */ + mkmap = mp->create_or_open(path); + + /* + * Get an exclusive lock - we're going to change the database so we can't + * have any spectators. + */ + if (myflock(mkmap->lock_fd, MYFLOCK_EXCLUSIVE) < 0) + msg_fatal("lock %s: %m", mkmap->lock_file); + + /* + * Delay signal delivery, so that we won't leave the database in an + * inconsistent state if we can avoid it. + */ + sigdelay(); + + /* + * Truncate the database upon open, and update it. Read-write mode is + * needed because the underlying routines read as well as write. + */ + mkmap->dict = mkmap->open(path, flags); + mkmap->dict->fd = -1; /* XXX just in case */ + mkmap->dict->flags |= DICT_FLAG_DUP_WARN; + return (mkmap); +} diff --git a/postfix/global/mynetworks.c b/postfix/global/mynetworks.c new file mode 100644 index 000000000..cd1f54f67 --- /dev/null +++ b/postfix/global/mynetworks.c @@ -0,0 +1,111 @@ +/*++ +/* NAME +/* mynetworks 3 +/* SUMMARY +/* generate patterns for my own interface addresses +/* SYNOPSIS +/* #include +/* +/* const char *mynetworks() +/* DESCRIPTION +/* This routine uses the address list built by own_inet_addr() +/* to produce a list of patterns that match the corresponding +/* networks. The patterns are conservative: they match whole +/* class A, B, C or D networks. This is usually sufficient to +/* distinguish between organizations. +/* 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 + +#ifndef IN_CLASSD_NET +#define IN_CLASSD_NET 0xf0000000 +#define IN_CLASSD_NSHIFT 28 +#endif + +#define BITS_PER_ADDR 32 + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* mynetworks - return patterns that match my own networks */ + +const char *mynetworks(void) +{ + static VSTRING *result; + + if (result == 0) { + char *myname = "mynetworks"; + INET_ADDR_LIST *my_addr_list; + unsigned long addr; + unsigned long mask; + struct in_addr net; + int shift; + int i; + + result = vstring_alloc(20); + my_addr_list = own_inet_addr_list(); + + for (i = 0; i < my_addr_list->used; i++) { + addr = ntohl(my_addr_list->addrs[i].s_addr); + if (IN_CLASSA(addr)) { + mask = IN_CLASSA_NET; + shift = IN_CLASSA_NSHIFT; + } else if (IN_CLASSB(addr)) { + mask = IN_CLASSB_NET; + shift = IN_CLASSB_NSHIFT; + } else if (IN_CLASSC(addr)) { + mask = IN_CLASSC_NET; + shift = IN_CLASSC_NSHIFT; + } else if (IN_CLASSD(addr)) { + mask = IN_CLASSD_NET; + shift = IN_CLASSD_NSHIFT; + } else { + msg_fatal("%s: bad address class: %s", + myname, inet_ntoa(my_addr_list->addrs[i])); + } + net.s_addr = htonl(addr & mask); + vstring_sprintf_append(result, "%s/%d ", + inet_ntoa(net), BITS_PER_ADDR - shift); + } + if (msg_verbose) + msg_info("%s: %s", myname, vstring_str(result)); + } + return (vstring_str(result)); +} + +#ifdef TEST + +char *var_inet_interfaces; + +main(int argc, char **argv) +{ + if (argc != 2) + msg_fatal("usage: %s interface_list", argv[0]); + msg_verbose = 10; + var_inet_interfaces = argv[1]; + mynetworks(); +} + +#endif diff --git a/postfix/global/mynetworks.h b/postfix/global/mynetworks.h new file mode 100644 index 000000000..28ba18699 --- /dev/null +++ b/postfix/global/mynetworks.h @@ -0,0 +1,30 @@ +#ifndef _MYNETWORKS_H_INCLUDED_ +#define _MYNETWORKS_H_INCLUDED_ + +/*++ +/* NAME +/* mynetworks 3h +/* SUMMARY +/* lookup patterns for my own interface addresses +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern const char *mynetworks(void); + +/* 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/mypwd.c b/postfix/global/mypwd.c new file mode 100644 index 000000000..23fb29589 --- /dev/null +++ b/postfix/global/mypwd.c @@ -0,0 +1,233 @@ +/*++ +/* NAME +/* mypwd 3 +/* SUMMARY +/* caching getpwnam()/getpwuid() +/* SYNOPSIS +/* #include +/* +/* struct mypasswd *mypwuid(uid) +/* uid_t uid; +/* +/* struct mypasswd *mypwnam(name) +/* const char *name; +/* +/* void mypwfree(pwd) +/* struct mypasswd *pwd; +/* DESCRIPTION +/* This module maintains a reference-counted cache of password +/* database lookup results. The idea is to avoid surprises by +/* getpwnam() or getpwuid() overwriting a previous result, while +/* at the same time avoiding duplicate copies of password +/* information in memory, and to avoid making repeated getpwxxx() +/* calls for the same information. +/* +/* mypwnam() and mypwuid() are wrappers that cache a private copy +/* of results from the getpwnam() and getpwuid() library routines. +/* Results are shared between calls with the same \fIname\fR +/* or \fIuid\fR argument, so changing results is verboten. +/* +/* mypwfree() cleans up the result of mypwnam() and mypwuid(). +/* BUGS +/* This module is security sensitive and complex at the same +/* time, which is bad. +/* 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 +#ifdef USE_PATHS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include "mypwd.h" + + /* + * The private cache. One for lookups by name, one for lookups by uid, and + * one for the last looked up result. + */ +static HTABLE *mypwcache_name = 0; +static BINHASH *mypwcache_uid = 0; +static struct mypasswd *last_pwd; + +/* mypwenter - enter password info into cache */ + +static struct mypasswd *mypwenter(struct passwd * pwd) +{ + struct mypasswd *mypwd; + + /* + * Initialize on the fly. + */ + if (mypwcache_name == 0) { + mypwcache_name = htable_create(0); + mypwcache_uid = binhash_create(0); + } + mypwd = (struct mypasswd *) mymalloc(sizeof(*mypwd)); + mypwd->refcount = 0; + mypwd->pw_name = mystrdup(pwd->pw_name); + mypwd->pw_passwd = mystrdup(pwd->pw_passwd); + mypwd->pw_uid = pwd->pw_uid; + mypwd->pw_gid = pwd->pw_gid; + mypwd->pw_gecos = mystrdup(pwd->pw_gecos); + mypwd->pw_dir = mystrdup(pwd->pw_dir); + mypwd->pw_shell = mystrdup(*pwd->pw_shell ? pwd->pw_shell : _PATH_BSHELL); + htable_enter(mypwcache_name, mypwd->pw_name, (char *) mypwd); + binhash_enter(mypwcache_uid, (char *) &mypwd->pw_uid, + sizeof(mypwd->pw_uid), (char *) mypwd); + return (mypwd); +} + +/* mypwuid - caching getpwuid() */ + +struct mypasswd *mypwuid(uid_t uid) +{ + struct passwd *pwd; + struct mypasswd *mypwd; + + /* + * See if this is the same user as last time. + */ + if (last_pwd != 0) { + if (last_pwd->pw_uid != uid) { + mypwfree(last_pwd); + last_pwd = 0; + } else { + mypwd = last_pwd; + mypwd->refcount++; + return (mypwd); + } + } + + /* + * Find the info in the cache or in the password database. Make a copy, + * so that repeated getpwnam() calls will not clobber our result. + */ + if ((mypwd = (struct mypasswd *) + binhash_find(mypwcache_uid, (char *) &uid, sizeof(uid))) == 0) { + if ((pwd = getpwuid(uid)) == 0) + return (0); + mypwd = mypwenter(pwd); + } + last_pwd = mypwd; + mypwd->refcount += 2; + return (mypwd); +} + +/* mypwnam - caching getpwnam() */ + +struct mypasswd *mypwnam(const char *name) +{ + struct passwd *pwd; + struct mypasswd *mypwd; + + /* + * See if this is the same user as last time. + */ + if (last_pwd != 0) { + if (strcmp(last_pwd->pw_name, name) != 0) { + mypwfree(last_pwd); + last_pwd = 0; + } else { + mypwd = last_pwd; + mypwd->refcount++; + return (mypwd); + } + } + + /* + * Find the info in the cache or in the password database. Make a copy, + * so that repeated getpwnam() calls will not clobber our result. + */ + if ((mypwd = (struct mypasswd *) htable_find(mypwcache_name, name)) == 0) { + if ((pwd = getpwnam(name)) == 0) + return (0); + mypwd = mypwenter(pwd); + } + last_pwd = mypwd; + mypwd->refcount += 2; + return (mypwd); +} + +/* mypwfree - destroy password info */ + +void mypwfree(struct mypasswd * mypwd) +{ + if (mypwd->refcount < 1) + msg_panic("mypwfree: refcount %d", mypwd->refcount); + + if (--mypwd->refcount == 0) { + htable_delete(mypwcache_name, mypwd->pw_name, (void (*) (char *)) 0); + binhash_delete(mypwcache_uid, (char *) &mypwd->pw_uid, + sizeof(mypwd->pw_uid), (void (*) (char *)) 0); + myfree(mypwd->pw_name); + myfree(mypwd->pw_passwd); + myfree(mypwd->pw_gecos); + myfree(mypwd->pw_dir); + myfree(mypwd->pw_shell); + myfree((char *) mypwd); + } +} + +#ifdef TEST + + /* + * Test program. Look up a couple users and/or uid values and see if the + * results will be properly free()d. + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + struct mypasswd **mypwd; + int i; + + msg_vstream_init(argv[0], VSTREAM_ERR); + if (argc == 1) + msg_fatal("usage: %s name or uid ...", argv[0]); + + mypwd = (struct mypasswd **) mymalloc((argc + 1) * sizeof(*mypwd)); + + for (i = 1; i < argc; i++) { + if (ISDIGIT(argv[i][0])) + mypwd[i] = mypwuid(atoi(argv[i])); + else + mypwd[i] = mypwnam(argv[i]); + if (mypwd[i] == 0) + msg_fatal("%s: not found", argv[i]); + msg_info("+ %s link=%d used=%d used=%d", argv[i], mypwd[i]->refcount, + mypwcache_name->used, mypwcache_uid->used); + } + for (i = 1; i < argc; i++) { + msg_info("- %s link=%d used=%d used=%d", argv[i], mypwd[i]->refcount, + mypwcache_name->used, mypwcache_uid->used); + mypwfree(mypwd[i]); + } + + myfree((char *) mypwd); +} + +#endif diff --git a/postfix/global/mypwd.h b/postfix/global/mypwd.h new file mode 100644 index 000000000..d5f8a892b --- /dev/null +++ b/postfix/global/mypwd.h @@ -0,0 +1,43 @@ +#ifndef _MYPWNAM_H_INCLUDED_ +#define _MYPWNAM_H_INCLUDED_ + +/*++ +/* NAME +/* mypwnam 3h +/* SUMMARY +/* caching getpwnam()/getpwuid() +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +struct mypasswd { + int refcount; + char *pw_name; + char *pw_passwd; + uid_t pw_uid; + gid_t pw_gid; + char *pw_gecos; + char *pw_dir; + char *pw_shell; +}; + +extern struct mypasswd *mypwnam(const char *); +extern struct mypasswd *mypwuid(uid_t); +extern void mypwfree(struct mypasswd *); + +/* 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/namadr_list.c b/postfix/global/namadr_list.c new file mode 100644 index 000000000..0e6b1bba7 --- /dev/null +++ b/postfix/global/namadr_list.c @@ -0,0 +1,139 @@ +/*++ +/* NAME +/* namadr_list 3 +/* SUMMARY +/* name/address list membership +/* SYNOPSIS +/* #include +/* +/* NAMADR_LIST *namadr_list_init(pattern_list) +/* const char *pattern_list; +/* +/* int namadr_list_match(list, name, addr) +/* NAMADR_LIST *list; +/* const char *name; +/* const char *addr; +/* +/* void namadr_list_free(list) +/* NAMADR_LIST *list; +/* DESCRIPTION +/* This module implements tests for list membership of a +/* hostname or network address. +/* +/* A list pattern specifies a host name, a domain name, +/* an internet address, or a network/mask pattern, where the +/* mask specifies the number of bits in the network part. +/* When a pattern specifies a file name, its contents are +/* substituted for the file name; when a pattern is a +/* type:name table specification, table lookup is used +/* instead. +/* Patterns are separated by whitespace and/or commas. In +/* order to reverse the result, precede a non-file name +/* pattern with an exclamation point (!). +/* +/* A host matches a list when its name or address matches +/* a pattern, or when any of its parent domains matches a +/* pattern. The matching process is case insensitive. +/* +/* namadr_list_init() performs initializations. The argument +/* is a list of patterns, or the absolute pathname of a file +/* with patterns. +/* +/* namadr_list_match() matches the specified host name and +/* address against the specified list of patterns. +/* +/* namadr_list_free() releases storage allocated by namadr_list_init(). +/* DIAGNOSTICS +/* Fatal errors: unable to open or read a pattern file; invalid +/* pattern. Panic: interface violations. +/* SEE ALSO +/* match_list(3) generic list matching +/* match_ops(3) match host by name or by address +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "namadr_list.h" + +/* namadr_list_init - initialize domain list */ + +NAMADR_LIST *namadr_list_init(const char *patterns) +{ + return (match_list_init(patterns, 2, match_hostaddr, match_hostname)); +} + +/* namadr_list_match - match host against set of namadr_list patterns */ + +int namadr_list_match(NAMADR_LIST *list, const char *name, const char *addr) +{ + return (match_list_match(list, addr, name)); +} + +/* namadr_list_free - release storage */ + +void namadr_list_free(NAMADR_LIST *list) +{ + match_list_free(list); +} + +#ifdef TEST + +#include +#include +#include +#include + +static void usage(char *progname) +{ + msg_fatal("usage: %s [-v] pattern_list hostname address", progname); +} + +main(int argc, char **argv) +{ + NAMADR_LIST *list; + char *host; + char *addr; + int ch; + + msg_vstream_init(argv[0], VSTREAM_ERR); + + while ((ch = GETOPT(argc, argv, "v")) > 0) { + switch (ch) { + case 'v': + msg_verbose++; + break; + default: + usage(argv[0]); + } + } + if (argc != optind + 3) + usage(argv[0]); + list = namadr_list_init(argv[optind]); + host = argv[optind + 1]; + addr = argv[optind + 2]; + vstream_printf("%s/%s: %s\n", host, addr, + namadr_list_match(list, host, addr) ? + "YES" : "NO"); + vstream_fflush(VSTREAM_OUT); + namadr_list_free(list); +} + +#endif diff --git a/postfix/global/namadr_list.h b/postfix/global/namadr_list.h new file mode 100644 index 000000000..186cd66c1 --- /dev/null +++ b/postfix/global/namadr_list.h @@ -0,0 +1,34 @@ +#ifndef _NAMADR_LIST_H_INCLUDED_ +#define _NAMADR_LIST_H_INCLUDED_ + +/*++ +/* NAME +/* namadr 3h +/* SUMMARY +/* name/address membership +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +typedef struct MATCH_LIST NAMADR_LIST; + +extern NAMADR_LIST *namadr_list_init(const char *); +extern int namadr_list_match(NAMADR_LIST *, const char *, const char *); +extern void namadr_list_free(NAMADR_LIST *); + +/* 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/off_cvt.c b/postfix/global/off_cvt.c new file mode 100644 index 000000000..95a689f14 --- /dev/null +++ b/postfix/global/off_cvt.c @@ -0,0 +1,152 @@ +/*++ +/* NAME +/* off_cvt 3 +/* SUMMARY +/* off_t conversions +/* SYNOPSIS +/* #include +/* +/* off_t off_cvt_string(string) +/* const char *string; +/* +/* VSTRING *off_cvt_number(result, offset) +/* VSTRING *result; +/* off_t offset; +/* DESCRIPTION +/* This module provides conversions between \fIoff_t\fR and string. +/* +/* off_cvt_string() converts a string, containing a non-negative +/* offset, to numerical form. The result is -1 in case of problems. +/* +/* off_cvt_number() converts a non-negative offset to string form. +/* +/* Arguments: +/* .IP string +/* String with non-negative number to be converted to off_t. +/* .IP result +/* Buffer for storage of the result of conversion to string. +/* .IP offset +/* Non-negative off_t value to be converted to string. +/* BUGS +/* The string to number conversion routine has no reliable way to +/* detect an overflow error, so the result may be much smaller +/* than the number specified in the input. +/* DIAGNOSTICS +/* Panic: negative offset +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "off_cvt.h" + +/* Application-specific. */ + +#define STR vstring_str +#define END vstring_end +#define SWAP(type, a, b) { type temp; temp = a; a = b; b = temp; } + +/* off_cvt_string - string to number */ + +off_t off_cvt_string(const char *str) +{ + int ch; + off_t result; + + /* + * We're not doing this often, so simplicity has precedence over + * performance. XXX Need a portable way to correctly detect overflow. + * Bear in mind that an off_t is not necessarily a long integer, so using + * raw bit patterns is not going to be a portable solution. + */ + for (result = 0; (ch = *(unsigned char *) str) != 0; str++) { + if (!ISDIGIT(ch)) + return (-1); + result *= 10; + result += ch - '0'; + if (result < 0) + return (-1); + } + return (result); +} + +/* off_cvt_number - number to string */ + +VSTRING *off_cvt_number(VSTRING *buf, off_t offset) +{ + static char digs[] = "0123456789"; + char *start; + char *last; + int i; + + /* + * Sanity checks + */ + if (offset < 0) + msg_panic("off_cvt_number: negative offset %s", + STR(off_cvt_number(buf, -offset))); + + /* + * First accumulate the result, backwards. + */ + VSTRING_RESET(buf); + while (offset != 0) { + VSTRING_ADDCH(buf, digs[offset % 10]); + offset /= 10; + } + VSTRING_TERMINATE(buf); + + /* + * Then, reverse the result. + */ + start = STR(buf); + last = END(buf) - 1; + for (i = 0; i < VSTRING_LEN(buf) / 2; i++) + SWAP(int, start[i], last[-i]); + return (buf); +} + +#ifdef TEST + + /* + * Proof-of-concept test program. Read a number from stdin, convert to + * off_t, back to string, and print the result. + */ +#include +#include + +int main(int unused_argc, char **unused_argv) +{ + VSTRING *buf = vstring_alloc(100); + off_t offset; + + while (vstring_fgets_nonl(buf, VSTREAM_IN)) { + if ((offset = off_cvt_string(STR(buf))) < 0) { + msg_warn("bad input %s", STR(buf)); + } else { + vstream_printf("%s\n", STR(off_cvt_number(buf, offset))); + } + vstream_fflush(VSTREAM_OUT); + } + vstring_free(buf); +} + +#endif diff --git a/postfix/global/off_cvt.h b/postfix/global/off_cvt.h new file mode 100644 index 000000000..7e506f516 --- /dev/null +++ b/postfix/global/off_cvt.h @@ -0,0 +1,37 @@ +#ifndef _OFF_CVT_H_INCLUDED_ +#define _OFF_CVT_H_INCLUDED_ + +/*++ +/* NAME +/* off_cvt 3h +/* SUMMARY +/* off_t conversions +/* SYNOPSIS +/* #include +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern off_t off_cvt_string(const char *); +extern VSTRING *off_cvt_number(VSTRING *, off_t); + +/* 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/opened.c b/postfix/global/opened.c new file mode 100644 index 000000000..a01399cc9 --- /dev/null +++ b/postfix/global/opened.c @@ -0,0 +1,82 @@ +/*++ +/* NAME +/* opened 3 +/* SUMMARY +/* log that a message was opened +/* SYNOPSIS +/* #include +/* +/* void opened(queue_id, sender, size, format, ...) +/* const char *queue_id; +/* const char *sender; +/* long size; +/* const char *format; +/* DESCRIPTION +/* opened() logs that a message was successfully delivered. +/* +/* vopened() implements an alternative interface. +/* +/* Arguments: +/* .IP queue_id +/* Message queue ID. +/* .IP sender +/* Sender address. +/* .IP size +/* Message content size. +/* .IP format +/* Format of optional text. +/* DIAGNOSTICS +/* Fatal: out of memory. +/* BUGS +/* Should be replaced by routines with an attribute-value based +/* interface instead of an interface that uses a rigid argument list. +/* 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 /* 44BSD stdarg.h uses abort() */ +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "opened.h" + +/* opened - log that a message was opened */ + +void opened(const char *queue_id, const char *sender, long size, const char *fmt,...) +{ + va_list ap; + + va_start(ap, fmt); + vopened(queue_id, sender, size, fmt, ap); + va_end(ap); +} + +/* opened - log that a message was opened */ + +void vopened(const char *queue_id, const char *sender, long size, const char *fmt, va_list ap) +{ + VSTRING *text = vstring_alloc(100); + +#define TEXT (vstring_str(text)) + + vstring_vsprintf(text, fmt, ap); + msg_info("%s: from=<%s>, size=%ld%s%s%s", + queue_id, sender, size, *TEXT ? " (" : "", TEXT, *TEXT ? ")" : ""); + vstring_free(text); +} diff --git a/postfix/global/opened.h b/postfix/global/opened.h new file mode 100644 index 000000000..d365b0c00 --- /dev/null +++ b/postfix/global/opened.h @@ -0,0 +1,36 @@ +#ifndef _OPENED_H_INCLUDED_ +#define _OPENED_H_INCLUDED_ + +/*++ +/* NAME +/* opened 3h +/* SUMMARY +/* log that a message was opened +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * External interface. + */ +extern void opened(const char *, const char *, long, const char *,...); +extern void vopened(const char *, const char *, long, const char *, va_list); + +/* 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/own_inet_addr.c b/postfix/global/own_inet_addr.c new file mode 100644 index 000000000..d33a25009 --- /dev/null +++ b/postfix/global/own_inet_addr.c @@ -0,0 +1,123 @@ +/*++ +/* NAME +/* own_inet_addr 3 +/* SUMMARY +/* determine if IP address belongs to this mail system instance +/* SYNOPSIS +/* #include +/* +/* int own_inet_addr(addr) +/* struct in_addr *addr; +/* +/* INET_ADDR_LIST *own_inet_addr_list() +/* DESCRIPTION +/* own_inet_addr() determines if the specified IP address belongs +/* to this mail system instance, i.e. if this mail system instance +/* is supposed to be listening on this specific IP address. +/* +/* own_inet_addr_list() returns the list of all addresses that +/* belong to this mail system instance. +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* Application-specific. */ + +static INET_ADDR_LIST addr_list; + +/* own_inet_addr_init - initialize my own address list */ + +static void own_inet_addr_init(INET_ADDR_LIST *addr_list) +{ + char *hosts; + char *host; + char *sep = " \t,"; + char *bufp; + + inet_addr_list_init(addr_list); + + /* + * If we are listening on all interfaces (default), ask the system what + * the interfaces are. + */ + if (strcasecmp(var_inet_interfaces, DEF_INET_INTERFACES) == 0) { + if (inet_addr_local(addr_list) == 0) + msg_fatal("could not find any active network interfaces"); +#if 0 + if (addr_list->used == 1) + msg_warn("found only one active network interface: %s", + inet_ntoa(addr_list->addrs[0])); +#endif + } + + /* + * If we are supposed to be listening only on specific interface + * addresses (virtual hosting), look up the addresses of those + * interfaces. + */ + else { + bufp = hosts = mystrdup(var_inet_interfaces); + while ((host = mystrtok(&bufp, sep)) != 0) + if (inet_addr_host(addr_list, host) == 0) + msg_fatal("config variable %s: host not found: %s", + VAR_INET_INTERFACES, host); + myfree(hosts); + } +} + +/* own_inet_addr - is this my own internet address */ + +int own_inet_addr(struct in_addr * addr) +{ + int i; + + if (addr_list.used == 0) + own_inet_addr_init(&addr_list); + + for (i = 0; i < addr_list.used; i++) + if (addr->s_addr == addr_list.addrs[i].s_addr) + return (1); + return (0); +} + +/* own_inet_addr_list - return list of addresses */ + +INET_ADDR_LIST *own_inet_addr_list(void) +{ + if (addr_list.used == 0) + own_inet_addr_init(&addr_list); + + return (&addr_list); +} diff --git a/postfix/global/own_inet_addr.h b/postfix/global/own_inet_addr.h new file mode 100644 index 000000000..ad984c818 --- /dev/null +++ b/postfix/global/own_inet_addr.h @@ -0,0 +1,36 @@ +#ifndef _OWN_INET_ADDR_H_INCLUDED_ +#define _OWN_INET_ADDR_H_INCLUDED_ + +/*++ +/* NAME +/* own_inet_addr 3h +/* SUMMARY +/* determine if IP address belongs to this mail system instance +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * External interface. + */ +extern int own_inet_addr(struct in_addr *); +extern struct INET_ADDR_LIST *own_inet_addr_list(void); + +/* 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/pipe_command.c b/postfix/global/pipe_command.c new file mode 100644 index 000000000..9d3bd5abd --- /dev/null +++ b/postfix/global/pipe_command.c @@ -0,0 +1,501 @@ +/*++ +/* NAME +/* pipe_command 3 +/* SUMMARY +/* deliver message to external command +/* SYNOPSIS +/* #include +/* +/* int pipe_command(src, why, key, value, ...) +/* VSTREAM *src; +/* VSTRING *why; +/* int key; +/* DESCRIPTION +/* pipe_command() runs a command with a message as standard +/* input. A limited amount of standard output and standard error +/* output is captured for diagnostics purposes. +/* +/* Arguments: +/* .IP src +/* An open message queue file, positioned at the start of the actual +/* message content. +/* .IP why +/* Storage for diagnostic information. +/* .IP key +/* Specifies what value will follow. pipe_command() takes a list +/* of (key, value) arguments, terminated by PIPE_CMD_END. The +/* following is a listing of key codes together with the expected +/* value type. +/* .RS +/* .IP "PIPE_CMD_COMMAND (char *)" +/* Specifies the command to execute as a string. The string is +/* passed to the shell when it contains shell meta characters +/* or when it appears to be a shell built-in command, otherwise +/* the command is executed without invoking a shell. +/* One of PIPE_CMD_COMMAND or PIPE_CMD_ARGV must be specified. +/* .IP "PIPE_CMD_ARGV (char **)" +/* The command is specified as an argument vector. This vector is +/* passed without further inspection to the \fIexecvp\fR() routine. +/* One of PIPE_CMD_COMMAND or PIPE_CMD_ARGV must be specified. +/* See also the PIPE_CMD_SHELL attribute below. +/* .IP "PIPE_CMD_ENV (char **)" +/* Additional environment information, in the form of a null-terminated +/* list of name, value, name, value, ... elements. By default only the +/* command search path is initialized to _PATH_DEFPATH. +/* .IP "PIPE_CMD_COPY_FLAGS (int)" +/* Flags that are passed on to the \fImail_copy\fR() routine. +/* The default flags value is 0 (zero). +/* .IP "PIPE_CMD_SENDER (char *)" +/* The envelope sender address, which is passed on to the +/* \fImail_copy\fR() routine. +/* .IP "PIPE_CMD_DELIVERED (char *)" +/* The recipient envelope address, which is passed on to the +/* \fImail_copy\fR() routine. +/* .IP "PIPE_CMD_UID (int)" +/* The user ID to execute the command as. The default is +/* the user ID corresponding to the \fIdefault_privs\fR +/* configuration parameter. The user ID must be non-zero. +/* .IP "PIPE_CMD_GID (int)" +/* The group ID to execute the command as. The default is +/* the group ID corresponding to the \fIdefault_privs\fR +/* configuration parameter. The group ID must be non-zero. +/* .IP "PIPE_CMD_TIME_LIMIT (int)" +/* The amount of time the command is allowed to run before it +/* is terminated with SIGKILL. The default is the limit given +/* with the \fIcommand_time_limit\fR configuration parameter. +/* .IP "PIPE_CMD_SHELL (char *)" +/* The shell to use when executing the command specified with +/* PIPE_CMD_COMMAND. This shell is invoked regardless of the +/* command content. +/* .RE +/* DIAGNOSTICS +/* Panic: interface violations (for example, a zero-valued +/* user ID or group ID, or a missing command). +/* +/* pipe_command() returns one of the following status codes: +/* .IP PIPE_STAT_OK +/* The command has taken responsibility for further delivery of +/* the message. +/* .IP PIPE_STAT_DEFER +/* The command failed with a "try again" type error. +/* The reason is given via the \fIwhy\fR argument. +/* .IP PIPE_STAT_BOUNCE +/* The command indicated that the message was not acceptable, +/* or the command did not finish within the time limit. +/* The reason is given via the \fIwhy\fR argument. +/* SEE ALSO +/* mail_copy(3) deliver to any. +/* sys_exits(3) sendmail-compatible exit status codes. +/* 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 +#include +#ifdef USE_PATHS_H +#include +#endif +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +struct pipe_args { + int flags; /* see mail_copy.h */ + char *sender; /* envelope sender */ + char *delivered; /* envelope recipient */ + char **argv; /* either an array */ + char *command; /* or a plain string */ + uid_t uid; /* privileges */ + gid_t gid; /* privileges */ + char **env; /* extra environment */ + char *shell; /* command shell */ +}; + +static int pipe_command_timeout; /* command has timed out */ +static int pipe_command_maxtime; /* available time to complete */ + +/* get_pipe_args - capture the variadic argument list */ + +static void get_pipe_args(struct pipe_args * args, va_list ap) +{ + char *myname = "get_pipe_args"; + int key; + + /* + * First, set the default values. + */ + args->flags = 0; + args->sender = 0; + args->delivered = 0; + args->argv = 0; + args->command = 0; + args->uid = var_default_uid; + args->gid = var_default_gid; + args->env = 0; + args->shell = 0; + + pipe_command_maxtime = var_command_maxtime; + + /* + * Then, override the defaults with user-supplied inputs. + */ + while ((key = va_arg(ap, int)) != PIPE_CMD_END) { + switch (key) { + case PIPE_CMD_COPY_FLAGS: + args->flags |= va_arg(ap, int); + break; + case PIPE_CMD_SENDER: + args->sender = va_arg(ap, char *); + break; + case PIPE_CMD_DELIVERED: + args->delivered = va_arg(ap, char *); + break; + case PIPE_CMD_ARGV: + if (args->command) + msg_panic("%s: got PIPE_CMD_ARGV and PIPE_CMD_COMMAND", myname); + args->argv = va_arg(ap, char **); + break; + case PIPE_CMD_COMMAND: + if (args->argv) + msg_panic("%s: got PIPE_CMD_ARGV and PIPE_CMD_COMMAND", myname); + args->command = va_arg(ap, char *); + break; + case PIPE_CMD_UID: + args->uid = va_arg(ap, int); /* in case uid_t is short */ + break; + case PIPE_CMD_GID: + args->gid = va_arg(ap, int); /* in case gid_t is short */ + break; + case PIPE_CMD_TIME_LIMIT: + pipe_command_maxtime = va_arg(ap, int); + break; + case PIPE_CMD_ENV: + args->env = va_arg(ap, char **); + break; + case PIPE_CMD_SHELL: + args->shell = va_arg(ap, char *); + break; + default: + msg_panic("%s: unknown key: %d", myname, key); + } + } + if (args->command == 0 && args->argv == 0) + msg_panic("%s: missing PIPE_CMD_ARGV or PIPE_CMD_COMMAND", myname); + if (args->uid == 0) + msg_panic("%s: privileged uid", myname); + if (args->gid == 0) + msg_panic("%s: privileged gid", myname); +} + +/* pipe_command_write - write to command with time limit */ + +static int pipe_command_write(int fd, void *buf, unsigned len) +{ + int maxtime = (pipe_command_timeout == 0) ? pipe_command_maxtime : 0; + char *myname = "pipe_command_write"; + + /* + * Don't wait when all available time was already used up. + */ + if (write_wait(fd, maxtime) < 0) { + if (pipe_command_timeout == 0) { + if (msg_verbose) + msg_info("%s: time limit exceeded", myname); + pipe_command_timeout = 1; + } + return (0); + } else { + return (write(fd, buf, len)); + } +} + +/* pipe_command_read - read from command with time limit */ + +static int pipe_command_read(int fd, void *buf, unsigned len) +{ + int maxtime = (pipe_command_timeout == 0) ? pipe_command_maxtime : 0; + char *myname = "pipe_command_read"; + + /* + * Don't wait when all available time was already used up. + */ + if (read_wait(fd, maxtime) < 0) { + if (pipe_command_timeout == 0) { + if (msg_verbose) + msg_info("%s: time limit exceeded", myname); + pipe_command_timeout = 1; + } + return (0); + } else { + return (read(fd, buf, len)); + } +} + +/* pipe_command_wait_or_kill - wait for command with time limit, or kill it */ + +static int pipe_command_wait_or_kill(pid_t pid, WAIT_STATUS_T *statusp, int sig) +{ + int maxtime = (pipe_command_timeout == 0) ? pipe_command_maxtime : 1; + char *myname = "pipe_command_wait_or_kill"; + int n; + + /* + * Don't wait when all available time was already used up. + */ + if ((n = timed_waitpid(pid, statusp, 0, maxtime)) < 0 && errno == ETIMEDOUT) { + if (pipe_command_timeout == 0) { + if (msg_verbose) + msg_info("%s: time limit exceeded", myname); + pipe_command_timeout = 1; + } + kill(-pid, sig); + n = waitpid(pid, statusp, 0); + } + return (n); +} + +/* pipe_command - execute command with extreme prejudice */ + +int pipe_command(VSTREAM *src, VSTRING *why,...) +{ + char *myname = "pipe_comand"; + va_list ap; + VSTREAM *cmd_in_stream; + VSTREAM *cmd_out_stream; + char log_buf[VSTREAM_BUFSIZE + 1]; + int log_len; + pid_t pid; + int write_status; + WAIT_STATUS_T wait_status; + int cmd_in_pipe[2]; + int cmd_out_pipe[2]; + struct pipe_args args; + char **cpp; + ARGV *argv; + + /* + * Process the variadic argument list. This also does sanity checks on + * what data the caller is passing to us. + */ + va_start(ap, why); + get_pipe_args(&args, ap); + va_end(ap); + + /* + * For convenience... + */ + if (args.command == 0) + args.command = args.argv[0]; + + /* + * Set up pipes that connect us to the command input and output streams. + * We're using a rather disgusting hack to capture command output: set + * the output to non-blocking mode, and don't attempt to read the output + * until AFTER the process has terminated. The rationale for this is: 1) + * the command output will be used only when delivery fails; 2) the + * amount of output is expected to be small; 3) the output can be + * truncated without too much loss. I could even argue that truncating + * the amount of diagnostic output is a good thing to do, but I won't go + * that far. + */ + if (pipe(cmd_in_pipe) < 0 || pipe(cmd_out_pipe) < 0) + msg_fatal("%s: pipe: %m", myname); + cmd_in_stream = vstream_fdopen(cmd_in_pipe[1], O_WRONLY); + cmd_out_stream = vstream_fdopen(cmd_out_pipe[0], O_RDONLY); + non_blocking(cmd_out_pipe[1], NON_BLOCKING); + + /* + * Spawn off a child process and irrevocably change privilege to the + * user. This includes revoking all rights on open files (via the close + * on exec flag). If we cannot run the command now, try again some time + * later. + */ + switch (pid = fork()) { + + /* + * Error. Instead of trying again right now, back off, give the + * system a chance to recover, and try again later. + */ + case -1: + vstring_sprintf(why, "Delivery failed: %m"); + return (PIPE_STAT_DEFER); + + /* + * Child. Run the child in a separate process group so that the + * parent can kill not just the child but also its offspring. + */ + case 0: + set_ugid(args.uid, args.gid); + setsid(); + + /* + * Pipe plumbing. + */ + close(cmd_in_pipe[1]); + close(cmd_out_pipe[0]); + if (dup2(cmd_in_pipe[0], STDIN_FILENO) < 0 + || dup2(cmd_out_pipe[1], STDOUT_FILENO) < 0 + || dup2(cmd_out_pipe[1], STDERR_FILENO) < 0) + msg_fatal("%s: dup2: %m", myname); + close(cmd_in_pipe[0]); + close(cmd_out_pipe[1]); + + /* + * Environment plumbing. Always reset the command search path. XXX + * That should probably be done by clean_env(). + */ + clean_env(); + if (setenv("PATH", _PATH_DEFPATH, 1)) + msg_fatal("%s: setenv: %m", myname); + if (args.env) + for (cpp = args.env; *cpp; cpp += 2) + if (setenv(cpp[0], cpp[1], 1)) + msg_fatal("setenv: %m"); + + /* + * Process plumbing. If possible, avoid running a shell. + */ + closelog(); + if (args.argv) { + execvp(args.argv[0], args.argv); + msg_fatal("%s: execvp %s: %m", myname, args.argv[0]); + } else if (args.shell && *args.shell) { + argv = argv_split(args.shell, " \t\r\n"); + argv_add(argv, args.command, (char *) 0); + argv_terminate(argv); + execvp(argv->argv[0], argv->argv); + msg_fatal("%s: execvp %s: %m", myname, argv->argv[0]); + } else { + exec_command(args.command); + } + /* NOTREACHED */ + + /* + * Parent. + */ + default: + close(cmd_in_pipe[0]); + close(cmd_out_pipe[1]); + + /* + * Give the command a limited amount of time to run, by enforcing + * timeouts on all I/O from and to it. + */ + vstream_control(cmd_in_stream, + VSTREAM_CTL_WRITE_FN, pipe_command_write, + VSTREAM_CTL_END); + vstream_control(cmd_out_stream, + VSTREAM_CTL_READ_FN, pipe_command_read, + VSTREAM_CTL_END); + pipe_command_timeout = 0; + + /* + * Pipe the message into the command. XXX We shouldn't be ignoring + * screams for help from mail_copy() like this. But, the command may + * stop reading input early, and that should not be considered an + * error condition. + */ +#define DONT_CARE_WHY ((VSTRING *) 0) + + write_status = mail_copy(args.sender, args.delivered, src, + cmd_in_stream, args.flags, DONT_CARE_WHY); + + /* + * Capture a limited amount of command output, for inclusion in a + * bounce message. Turn tabs and newlines into whitespace, and + * replace other non-printable characters by underscore. + */ + log_len = vstream_fread(cmd_out_stream, log_buf, sizeof(log_buf) - 1); + (void) vstream_fclose(cmd_out_stream); + log_buf[log_len] = 0; + translit(log_buf, "\t\n", " "); + printable(log_buf, '_'); + + /* + * Just because the child closes its output streams, don't assume + * that it will terminate. Instead, be prepared for the situation + * that the child does not terminate, even when the parent + * experiences no read/write timeout. Make sure that the child + * terminates before the parent attempts to retrieve its exit status, + * otherwise the parent could become stuck, and the mail system would + * eventually run out of delivery agents. Do a thorough job, and kill + * not just the child process but also its offspring. + */ + if (pipe_command_timeout) + (void) kill(-pid, SIGKILL); + if (pipe_command_wait_or_kill(pid, &wait_status, SIGKILL) < 0) + msg_fatal("wait: %m"); + if (pipe_command_timeout) { + vstring_sprintf(why, "Command time limit exceeded: \"%s\"%s%s", + args.command, + log_len ? ". Command output: " : "", log_buf); + return (PIPE_STAT_BOUNCE); + } + + /* + * Command exits. Give special treatment to sendmail style exit + * status codes. + */ + if (!NORMAL_EXIT_STATUS(wait_status)) { + if (WIFSIGNALED(wait_status)) { + vstring_sprintf(why, "Command died with signal %d: \"%s\"%s%s", + WTERMSIG(wait_status), + args.command, + log_len ? ". Command output: " : "", log_buf); + return (PIPE_STAT_BOUNCE); + } else if (SYS_EXITS_CODE(WEXITSTATUS(wait_status))) { + vstring_sprintf(why, "%s%s%s", + sys_exits_strerror(WEXITSTATUS(wait_status)), + log_len ? ". Command output: " : "", log_buf); + return (sys_exits_softerror(WEXITSTATUS(wait_status)) ? + PIPE_STAT_DEFER : PIPE_STAT_BOUNCE); + } else { + vstring_sprintf(why, "Command died with status %d: \"%s\"%s%s", + WEXITSTATUS(wait_status), + args.command, + log_len ? ". Command output: " : "", log_buf); + return (PIPE_STAT_BOUNCE); + } + } else if (write_status && errno != EPIPE) { + vstring_sprintf(why, "Command failed: %m: \"%s\"", args.command); + return (PIPE_STAT_DEFER); + } else { + return (PIPE_STAT_OK); + } + } +} diff --git a/postfix/global/pipe_command.h b/postfix/global/pipe_command.h new file mode 100644 index 000000000..cf75aa117 --- /dev/null +++ b/postfix/global/pipe_command.h @@ -0,0 +1,60 @@ +#ifndef _PIPE_COMMAND_H_INCLUDED_ +#define _PIPE_COMMAND_H_INCLUDED_ + +/*++ +/* NAME +/* pipe_command 3h +/* SUMMARY +/* deliver message to external command +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include + + /* + * Global library. + */ +#include + + /* + * Request arguments. + */ +#define PIPE_CMD_END 0 /* terminator */ +#define PIPE_CMD_COMMAND 1 /* command is string */ +#define PIPE_CMD_ARGV 2 /* command is array */ +#define PIPE_CMD_COPY_FLAGS 3 /* mail_copy() flags */ +#define PIPE_CMD_SENDER 4 /* mail_copy() sender */ +#define PIPE_CMD_DELIVERED 5 /* mail_copy() recipient */ +#define PIPE_CMD_UID 6 /* privileges */ +#define PIPE_CMD_GID 7 /* privileges */ +#define PIPE_CMD_TIME_LIMIT 8 /* time limit */ +#define PIPE_CMD_ENV 9 /* extra environment */ +#define PIPE_CMD_SHELL 10 /* alternative shell */ + + /* + * Command completion status. + */ +#define PIPE_STAT_OK 0 /* success */ +#define PIPE_STAT_DEFER 1 /* try again */ +#define PIPE_STAT_BOUNCE 2 /* failed */ + +extern int pipe_command(VSTREAM *, VSTRING *,...); + +/* 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/post_mail.c b/postfix/global/post_mail.c new file mode 100644 index 000000000..c3673243f --- /dev/null +++ b/postfix/global/post_mail.c @@ -0,0 +1,243 @@ +/*++ +/* NAME +/* post_mail 3 +/* SUMMARY +/* convenient mail posting interface +/* SYNOPSIS +/* #include +/* +/* VSTREAM *post_mail_fopen(sender, recipient, flags, via) +/* const char *sender; +/* const char *recipient; +/* int flags; +/* const char *via; +/* +/* VSTREAM *post_mail_fopen_nowait(sender, recipient, flags, via) +/* const char *sender; +/* const char *recipient; +/* int flags; +/* const char *via; +/* +/* int post_mail_fprintf(stream, format, ...) +/* VSTREAM *stream; +/* const char *format; +/* +/* int post_mail_fputs(stream, str) +/* VSTREAM *stream; +/* const char *str; +/* +/* int post_mail_buffer(stream, buf, len) +/* VSTREAM *stream; +/* const char *buffer; +/* +/* int POST_MAIL_BUFFER(stream, buf) +/* VSTREAM *stream; +/* VSTRING *buffer; +/* +/* int post_mail_fclose(stream) +/* VSTREAM *STREAM; +/* DESCRIPTION +/* This module provides a convenient interface for the most +/* common case of sending one message to one recipient. It +/* allows the application to concentrate on message content, +/* without having to worry about queue file structure details. +/* +/* post_mail_fopen() opens a connection to the cleanup service +/* and waits until the service is available, does some option +/* negotiation, generates message envelope records, and generates +/* Received: and Date: message headers. The result is a stream +/* handle that can be used for sending message records. +/* +/* post_mail_fopen_nowait() tries to contact the cleanup service +/* only once, and does not wait until the cleanup service is +/* available. Otherwise it is identical to post_mail_fopen(). +/* +/* post_mail_fprintf() formats message content (header or body) +/* and sends it to the cleanup service. +/* +/* post_mail_fputs() sends pre-formatted content (header or body) +/* to the cleanup service. +/* +/* post_mail_buffer() sends a pre-formatted buffer to the +/* cleanup service. +/* +/* POST_MAIL_BUFFER() is a wrapper for post_mail_buffer() that +/* evaluates its buffer argument more than once. +/* +/* post_mail_fclose() completes the posting of a message. +/* +/* Arguments: +/* .IP sender +/* The sender envelope address. It is up to the application +/* to produce From: headers. +/* .IP recipient +/* The recipient envelope address. It is up to the application +/* to produce To: headers. +/* .IP flags +/* The binary OR of zero or more of the options defined in +/* \fI\fR. +/* .IP via +/* The name of the service responsible for posting this message. +/* .IP stream +/* A stream opened by mail_post_fopen(). +/* DIAGNOSTICS +/* post_mail_fopen_nowait() returns a null pointer when the +/* cleanup service is not available immediately. +/* +/* post_mail_fprintf(), post_mail_fputs() post_mail_fclose(), +/* and post_mail_buffer() return the binary OR of the error +/* status codes defined in \fI\fR. +/* +/* Fatal errors: cleanup initial handshake errors. This means +/* the client and server speak incompatible protocols. +/* SEE ALSO +/* cleanup_user(3h) cleanup options and results +/* cleanup_strerror(3) translate results to text +/* cleanup(8) cleanup service +/* 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 /* 44BSD stdarg.h uses abort() */ +#include +#include + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* post_mail_init - initial negotiations */ + +static void post_mail_init(VSTREAM *stream, const char *sender, + const char *recipient, int flags, const char *via) +{ + VSTRING *id = vstring_alloc(100); + long now = time((time_t *) 0); + const char *date = mail_date(now); + + /* + * Negotiate with the cleanup service. Give up if we can't agree. + */ + if (mail_scan(stream, "%s", id) != 1 + || mail_print(stream, "%d", flags) != 0) + msg_fatal("unable to contact the %s service", MAIL_SERVICE_CLEANUP); + + /* + * Generate a minimal envelope section. The cleanup service will add a + * size record. + */ + rec_fprintf(stream, REC_TYPE_TIME, "%ld", (long) now); + rec_fputs(stream, REC_TYPE_FROM, sender); + rec_fputs(stream, REC_TYPE_RCPT, recipient); + rec_fputs(stream, REC_TYPE_MESG, ""); + + /* + * Do the Received: and Date: header lines. This allows us to shave a few + * cycles by using the expensive date conversion result for both. + */ + post_mail_fprintf(stream, "Received: by %s (%s) via %s", + var_myhostname, var_mail_name, via); + post_mail_fprintf(stream, "\tid %s; %s", vstring_str(id), date); + post_mail_fprintf(stream, "Date: %s", date); + vstring_free(id); +} + +/* post_mail_fopen - prepare for posting a message */ + +VSTREAM *post_mail_fopen(const char *sender, const char *recipient, + int flags, const char *via) +{ + VSTREAM *stream; + + stream = mail_connect_wait(MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP); + post_mail_init(stream, sender, recipient, flags, via); + return (stream); +} + +/* post_mail_fopen_nowait - prepare for posting a message */ + +VSTREAM *post_mail_fopen_nowait(const char *sender, const char *recipient, + int flags, const char *via) +{ + VSTREAM *stream; + + if ((stream = mail_connect(MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP, + BLOCKING)) != 0) + post_mail_init(stream, sender, recipient, flags, via); + return (stream); +} + +/* post_mail_fprintf - format and send message content */ + +int post_mail_fprintf(VSTREAM *cleanup, const char *format,...) +{ + int status; + va_list ap; + + va_start(ap, format); + status = rec_vfprintf(cleanup, REC_TYPE_NORM, format, ap); + va_end(ap); + return (status != REC_TYPE_NORM ? CLEANUP_STAT_WRITE : 0); +} + +/* post_mail_buffer - send pre-formatted buffer */ + +int post_mail_buffer(VSTREAM *cleanup, const char *buf, int len) +{ + return (rec_put(cleanup, REC_TYPE_NORM, buf, len) != REC_TYPE_NORM ? + CLEANUP_STAT_WRITE : 0); +} + +/* post_mail_fputs - send pre-formatted message content */ + +int post_mail_fputs(VSTREAM *cleanup, const char *str) +{ + int len = str ? strlen(str) : 0; + + return (rec_put(cleanup, REC_TYPE_NORM, str, len) != REC_TYPE_NORM ? + CLEANUP_STAT_WRITE : 0); +} + +/* post_mail_fclose - finish posting of message */ + +int post_mail_fclose(VSTREAM *cleanup) +{ + int status = 0; + + /* + * Send the message end marker only when there were no errors. + */ + if (vstream_ferror(cleanup) != 0) { + status = CLEANUP_STAT_WRITE; + } else { + rec_fputs(cleanup, REC_TYPE_XTRA, ""); + rec_fputs(cleanup, REC_TYPE_END, ""); + if (vstream_fflush(cleanup) || mail_scan(cleanup, "%d", &status) != 1) + status = CLEANUP_STAT_WRITE; + } + (void) vstream_fclose(cleanup); + return (status); +} diff --git a/postfix/global/post_mail.h b/postfix/global/post_mail.h new file mode 100644 index 000000000..4786876ba --- /dev/null +++ b/postfix/global/post_mail.h @@ -0,0 +1,49 @@ +#ifndef _POST_MAIL_H_INCLUDED_ +#define _POST_MAIL_H_INCLUDED_ + +/*++ +/* NAME +/* post_mail 3h +/* SUMMARY +/* convenient mail posting interface +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * Global library. + */ +#include + + /* + * External interface. + */ +extern VSTREAM *post_mail_fopen(const char *, const char *, int, const char *); +extern VSTREAM *post_mail_fopen_nowait(const char *, const char *, + int, const char *); +extern int post_mail_fprintf(VSTREAM *, const char *,...); +extern int post_mail_fputs(VSTREAM *, const char *); +extern int post_mail_buffer(VSTREAM *, const char *, int); +extern int post_mail_fclose(VSTREAM *); + +#define POST_MAIL_BUFFER(v, b) \ + post_mail_buffer((v), vstring_str(b), VSTRING_LEN(b)) + +/* 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/quote_822_local.c b/postfix/global/quote_822_local.c new file mode 100644 index 000000000..f32ca696a --- /dev/null +++ b/postfix/global/quote_822_local.c @@ -0,0 +1,222 @@ +/*++ +/* NAME +/* quote_822_local 3 +/* SUMMARY +/* quote local part of mailbox +/* SYNOPSIS +/* #include +/* +/* VSTRING *quote_822_local(dst, src) +/* VSTRING *dst; +/* const char *src; +/* +/* VSTRING *unquote_822_local(dst, src) +/* VSTRING *dst; +/* const char *src; +/* DESCRIPTION +/* quote_822_local() quotes the local part of a mailbox and +/* returns a result that can be used in message headers as +/* specified by RFC 822 (actually, an 8-bit clean version of +/* RFC 822). +/* +/* unquote_822_local() transforms the local part of a mailbox +/* address to unquoted (internal) form. +/* +/* Arguments: +/* .IP dst +/* The result. +/* .IP src +/* The input address. +/* STANDARDS +/* RFC 822 (ARPA Internet Text Messages) +/* BUGS +/* The code assumes that the domain is RFC 822 clean. +/* 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 + +/* Utility library. */ + +#include + +/* Global library. */ + +/* Application-specific. */ + +#include "quote_822_local.h" + +/* Local stuff. */ + +#define YES 1 +#define NO 0 + +/* is_822_dot_string - is this local-part an rfc 822 dot-string? */ + +static int is_822_dot_string(const char *local_part, const char *end) +{ + const char *cp; + int ch; + + /* + * Detect any deviations from a sequence of atoms separated by dots. We + * could use lookup tables to speed up some of the work, but hey, how + * large can a local-part be anyway? + * + * RFC 822 expects 7-bit data. Rather than quoting every 8-bit character + * (and still passing it on as 8-bit data) we leave 8-bit data alone. + */ + if (local_part[0] == 0 || local_part[0] == '.') + return (NO); + for (cp = local_part; cp < end && (ch = *cp) != 0; cp++) { + if (ch == '.' && (cp + 1) < end && cp[1] == '.') + return (NO); +#if 0 + if (ch > 127) + return (NO); +#endif + if (ch == ' ') + return (NO); + if (ISCNTRL(ch)) + return (NO); + if (ch == '(' || ch == ')' + || ch == '<' || ch == '>' + || ch == '@' || ch == ',' + || ch == ';' || ch == ':' + || ch == '\\' || ch == '"' + || ch == '[' || ch == ']') + return (NO); + } + if (cp[-1] == '.') + return (NO); + return (YES); +} + +/* make_822_quoted_string - make quoted-string from local-part */ + +static VSTRING *make_822_quoted_string(VSTRING *dst, const char *local_part, + const char *end) +{ + const char *cp; + int ch; + + /* + * Put quotes around the result, and prepend a backslash to characters + * that need quoting when they occur in a quoted-string. + */ + VSTRING_ADDCH(dst, '"'); + for (cp = local_part; cp < end && (ch = *cp) != 0; cp++) { + if ( /* ch > 127 || */ ch == '"' || ch == '\\' || ch == '\r') + VSTRING_ADDCH(dst, '\\'); + VSTRING_ADDCH(dst, ch); + } + VSTRING_ADDCH(dst, '"'); + return (dst); +} + +/* quote_822_local - quote local part of mailbox according to rfc 822 */ + +VSTRING *quote_822_local(VSTRING *dst, const char *mbox) +{ + const char *start; /* first byte of localpart */ + const char *end; /* first byte after localpart */ + const char *colon; + + /* + * According to RFC 822, a local-part is a dot-string or a quoted-string. + * We first see if the local-part is a dot-string. If it is not, we turn + * it into a quoted-string. Anything else would be too painful. But + * first, skip over any source route that precedes the local-part. + */ + if (mbox[0] == '@' && (colon = strchr(mbox, ':')) != 0) + start = colon + 1; + else + start = mbox; + if ((end = strrchr(start, '@')) == 0) + end = start + strlen(start); + if (is_822_dot_string(start, end)) { + return (vstring_strcpy(dst, mbox)); + } else { + vstring_strncpy(dst, mbox, start - mbox); + make_822_quoted_string(dst, start, end); + return (vstring_strcat(dst, end)); + } +} + +/* unquote_822_local - unquote local part of mailbox according to rfc 822 */ + +VSTRING *unquote_822_local(VSTRING *dst, const char *mbox) +{ + const char *start; /* first byte of localpart */ + const char *end; /* first byte after localpart */ + const char *colon; + const char *cp; + + if (mbox[0] == '@' && (colon = strchr(mbox, ':')) != 0) { + start = colon + 1; + vstring_strncpy(dst, mbox, start - mbox); + } else { + start = mbox; + VSTRING_RESET(dst); + } + if ((end = strrchr(start, '@')) == 0) + end = start + strlen(start); + for (cp = start; cp < end; cp++) { + if (*cp == '"') + continue; + if (*cp == '\\') { + if (cp[1] == 0) + continue; + cp++; + } + VSTRING_ADDCH(dst, *cp); + } + if (*end) + vstring_strcat(dst, end); + else + VSTRING_TERMINATE(dst); + return (dst); +} + +#ifdef TEST + + /* + * Proof-of-concept test program. Read an unquoted address from stdin, and + * show the quoted and unquoted results. + */ +#include +#include + +#define STR vstring_str + +int main(int unused_argc, char **unused_argv) +{ + VSTRING *raw = vstring_alloc(100); + VSTRING *quoted = vstring_alloc(100); + VSTRING *unquoted = vstring_alloc(100); + + while (vstring_fgets_nonl(raw, VSTREAM_IN)) { + quote_822_local(quoted, STR(raw)); + vstream_printf("quoted: %s\n", STR(quoted)); + unquote_822_local(unquoted, STR(quoted)); + vstream_printf("unquoted: %s\n", STR(unquoted)); + vstream_fflush(VSTREAM_OUT); + } + vstring_free(unquoted); + vstring_free(quoted); + vstring_free(raw); +} + +#endif diff --git a/postfix/global/quote_822_local.h b/postfix/global/quote_822_local.h new file mode 100644 index 000000000..714de3627 --- /dev/null +++ b/postfix/global/quote_822_local.h @@ -0,0 +1,36 @@ +#ifndef _QUOTE_822_H_INCLUDED_ +#define _QUOTE_822_H_INCLUDED_ + +/*++ +/* NAME +/* quote_822_local 3h +/* SUMMARY +/* quote local part of mailbox +/* SYNOPSIS +/* #include "quote_822_local.h" +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern VSTRING *quote_822_local(VSTRING *, const char *); +extern VSTRING *unquote_822_local(VSTRING *, const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/global/rec2stream.c b/postfix/global/rec2stream.c new file mode 100644 index 000000000..0e392cf0b --- /dev/null +++ b/postfix/global/rec2stream.c @@ -0,0 +1,46 @@ +/*++ +/* NAME +/* rec2stream 1 +/* SUMMARY +/* convert record stream to stream-lf format +/* SYNOPSIS +/* rec2stream +/* DESCRIPTION +/* rec2stream reads a record stream from standard input and +/* writes the content to standard output in stream-lf format. +/* 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 + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include +#include + +main(void) +{ + VSTRING *buf = vstring_alloc(100); + int type; + + while ((type = rec_get(VSTREAM_IN, buf, 0)) > 0) + REC_STREAMLF_PUT_BUF(VSTREAM_OUT, type, buf); + vstream_fflush(VSTREAM_OUT); +} diff --git a/postfix/global/rec_streamlf.c b/postfix/global/rec_streamlf.c new file mode 100644 index 000000000..69f4b7e3e --- /dev/null +++ b/postfix/global/rec_streamlf.c @@ -0,0 +1,106 @@ +/*++ +/* NAME +/* rec_streamlf 3 +/* SUMMARY +/* record interface to stream-lf files +/* SYNOPSIS +/* #include +/* +/* int rec_streamlf_get(stream, buf, maxlen) +/* VSTREAM *stream; +/* VSTRING *buf; +/* int maxlen; +/* +/* int rec_streamlf_put(stream, type, data, len) +/* VSTREAM *stream; +/* int type; +/* const char *data; +/* int len; +/* AUXILIARY FUNCTIONS +/* int REC_STREAMLF_PUT_BUF(stream, type, buf) +/* VSTREAM *stream; +/* int type; +/* VSTRING *buf; +/* DESCRIPTION +/* This module implements record I/O on top of stream-lf files. +/* +/* rec_streamlf_get() reads one record from the specified stream. +/* The result is not null-terminated and may contain embedded null +/* characters. +/* The \fImaxlen\fR argument specifies an upper bound to the amount +/* of data read. The result is REC_TYPE_NORM when the record was +/* terminated by newline, REC_TYPE_CONT when no terminating newline +/* was found (the record was larger than \fImaxlen\fR characters or +/* EOF was reached), and REC_TYPE_EOF when no data could be read or +/* when an I/O error was detected. +/* +/* rec_streamlf_put() writes one record to the named stream. +/* When the record type is REC_TYPE_NORM, a newline character is +/* appended to the output. The result is the record type, or +/* REC_TYPE_EOF in case of problems. +/* +/* REC_STREAMLF_PUT_BUF() is a wrapper for rec_streamlf_put() that +/* makes it more convenient to output VSTRING buffers. +/* REC_STREAMLF_PUT_BUF() is an unsafe macro that evaluates some +/* arguments more than once. +/* SEE ALSO +/* record(3) typed records +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include +#include +#include + +/* rec_streamlf_get - read record from stream-lf file */ + +int rec_streamlf_get(VSTREAM *stream, VSTRING *buf, int maxlen) +{ + int n = maxlen; + int ch; + + /* + * If this one character ar a time code proves to be a performance + * bottleneck, switch to block search (memchr()) and to block move + * (memcpy()) operations. + */ + VSTRING_RESET(buf); + while (n-- > 0) { + if ((ch = VSTREAM_GETC(stream)) == VSTREAM_EOF) + return (VSTRING_LEN(buf) > 0 ? REC_TYPE_CONT : REC_TYPE_EOF); + if (ch == '\n') + return (REC_TYPE_NORM); + VSTRING_ADDCH(buf, ch); + } + return (REC_TYPE_CONT); +} + +/* rec_streamlf_put - write record to stream-lf file */ + +int rec_streamlf_put(VSTREAM *stream, int type, const char *data, int len) +{ + if (len > 0) + (void) vstream_fwrite(stream, data, len); + if (type == REC_TYPE_NORM) + (void) VSTREAM_PUTC('\n', stream); + return (vstream_ferror(stream) ? REC_TYPE_EOF : type); +} diff --git a/postfix/global/rec_streamlf.h b/postfix/global/rec_streamlf.h new file mode 100644 index 000000000..3706f12a2 --- /dev/null +++ b/postfix/global/rec_streamlf.h @@ -0,0 +1,45 @@ +#ifndef _REC_STREAMLF_H_INCLUDED_ +#define _REC_STREAMLF_H_INCLUDED_ + +/*++ +/* NAME +/* rec_streamlf 3h +/* SUMMARY +/* record interface to stream-lf files +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include + + /* + * Global library. + */ +#include + + /* + * External interface. + */ +extern int rec_streamlf_get(VSTREAM *, VSTRING *, int); +extern int rec_streamlf_put(VSTREAM *, int, const char *, int); + +#define REC_STREAMLF_PUT_BUF(s, t, b) \ + rec_streamlf_put((s), (t), vstring_str(b), VSTRING_LEN(b)) + +/* 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/rec_type.c b/postfix/global/rec_type.c new file mode 100644 index 000000000..6b72e8171 --- /dev/null +++ b/postfix/global/rec_type.c @@ -0,0 +1,70 @@ +/*++ +/* NAME +/* rec_type 3 +/* SUMMARY +/* Postfix record types +/* SYNOPSIS +/* #include +/* +/* const char *rec_type_name(type) +/* int type; +/* DESCRIPTION +/* This module and its associated include file implement the +/* Postfix-specific record types. +/* +/* rec_type_name() returns a printable name for the given record +/* type. +/* 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 +/*--*/ + +/* Global library. */ + +#include "rec_type.h" + + /* + * Lookup table with internal record type codes and printable names. + */ +typedef struct { + int type; + const char *name; +} REC_TYPE_NAME; + +REC_TYPE_NAME rec_type_names[] = { + REC_TYPE_EOF, "end-of-file", /* not Postfix-specific. */ + REC_TYPE_ERROR, "error", /* not Postfix-specific. */ + REC_TYPE_SIZE, "message_size", + REC_TYPE_TIME, "time", + REC_TYPE_FULL, "fullname", + REC_TYPE_FROM, "sender", + REC_TYPE_DONE, "done", + REC_TYPE_RCPT, "recipient", + REC_TYPE_MESG, "message_content", + REC_TYPE_CONT, "unterminated", + REC_TYPE_NORM, "normal_data", + REC_TYPE_XTRA, "extracted_info", + REC_TYPE_RRTO, "return_receipt", + REC_TYPE_ERTO, "errors_to", + REC_TYPE_PRIO, "priority", + REC_TYPE_END, "message_end", + 0, 0, +}; + +/* rec_type_name - map record type ro printable name */ + +const char *rec_type_name(int type) +{ + REC_TYPE_NAME *p; + + for (p = rec_type_names; p->name != 0; p++) + if (p->type == type) + return (p->name); + return ("unknown_record_type"); +} diff --git a/postfix/global/rec_type.h b/postfix/global/rec_type.h new file mode 100644 index 000000000..3a27f80ec --- /dev/null +++ b/postfix/global/rec_type.h @@ -0,0 +1,88 @@ +#ifndef _REC_TYPE_H_INCLUDED_ +#define _REC_TYPE_H_INCLUDED_ + +/*++ +/* NAME +/* rec_type 3h +/* SUMMARY +/* Postfix record types +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Diagnostic codes, not real record lookup results. + */ +#define REC_TYPE_EOF -1 /* no record */ +#define REC_TYPE_ERROR -2 /* bad record */ + + /* + * A queue file or IPC mail message consists of a sequence of typed records. + * The first record group contains time stamp, full name, sender envelope + * information, and optionally contains recipient information. The second + * record group contains data records with the message content. The last + * record group is optional; it contains information extracted from message + * headers, such as recipients, errors-to and return-receipt. + */ +#define REC_TYPE_SIZE 'C' /* first record, created by cleanup */ +#define REC_TYPE_TIME 'T' /* time stamp, required */ +#define REC_TYPE_FULL 'F' /* full name, optional */ +#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_MESG 'M' /* start message records */ + +#define REC_TYPE_CONT 'L' /* long data record */ +#define REC_TYPE_NORM 'N' /* normal data record */ + +#define REC_TYPE_XTRA 'X' /* start extracted records */ + +#define REC_TYPE_RRTO 'r' /* return-receipt, from headers */ +#define REC_TYPE_ERTO 'e' /* errors-to, from headers */ +#define REC_TYPE_PRIO 'P' /* priority */ + +#define REC_TYPE_END 'E' /* terminator, required */ + + /* + * The types of records that I expect to see while processing different + * 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_CONTENT "XLN" +#define REC_TYPE_EXTRACT "EDRPre" +#define REC_TYPE_NOEXTRACT "E" + + /* + * The record at the beginning of the envelope segment specifies the message + * 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 */ + + /* + * 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 */ + + /* + * Programmatic interface. + */ +extern const char *rec_type_name(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/global/recdump.c b/postfix/global/recdump.c new file mode 100644 index 000000000..a02d1755e --- /dev/null +++ b/postfix/global/recdump.c @@ -0,0 +1,55 @@ +/*++ +/* NAME +/* recdump 1 +/* SUMMARY +/* convert record stream to printable form +/* SYNOPSIS +/* recdump +/* DESCRIPTION +/* recdump reads a record stream from standard input and +/* writes the content to standard output in printable form. +/* 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 + +/* Utility library. */ + +#include + +/* Global library. */ + +#include +#include +#include + +int main(int unused_argc, char **argv) +{ + VSTRING *buf = vstring_alloc(100); + long offset; + int type; + + msg_vstream_init(argv[0], VSTREAM_OUT); + + while (offset = vstream_ftell(VSTREAM_IN), + (type = rec_get(VSTREAM_IN, buf, 0)) > 0) { + vstream_fprintf(VSTREAM_OUT, "%15s|%4ld|%3d|%s\n", + rec_type_name(type), offset, + VSTRING_LEN(buf), vstring_str(buf)); + } + vstream_fflush(VSTREAM_OUT); + vstring_free(buf); + exit(0); +} diff --git a/postfix/global/recipient_list.c b/postfix/global/recipient_list.c new file mode 100644 index 000000000..055f2a358 --- /dev/null +++ b/postfix/global/recipient_list.c @@ -0,0 +1,110 @@ +/*++ +/* NAME +/* recipient_list 3 +/* SUMMARY +/* in-core recipient structures +/* SYNOPSIS +/* #include +/* +/* typedef struct { +/* .in +4 +/* long offset; +/* char *address; +/* .in -4 +/* } RECIPIENT; +/* +/* typedef struct { +/* .in +4 +/* RECIPIENT *info; +/* private members... +/* .in -4 +/* } RECIPIENT_LIST; +/* +/* void recipient_list_init(list) +/* RECIPIENT_LIST *list; +/* +/* void recipient_list_add(list, offset, recipient) +/* RECIPIENT_LIST *list; +/* long offset; +/* const char *recipient; +/* +/* void recipient_list_free(list) +/* RECIPIENT_LIST *list; +/* DESCRIPTION +/* This module maintains lists of recipient structures. Each +/* recipient is characterized by a destination address and +/* by the queue file offset of its delivery status record. +/* +/* recipient_list_init() creates an empty recipient structure list. +/* The list argument is initialized such that it can be given to +/* recipient_list_add() and to recipient_list_free(). +/* +/* recipient_list_add() adds a recipient to the specified list. +/* The recipient address is copied with mystrdup(). +/* +/* recipient_list_free() releases memory for the specified list +/* of recipient structures. +/* +/* Arguments: +/* .IP list +/* Recipient list initialized by recipient_list_init(). +/* .IP offset +/* Queue file offset of a recipient delivery status record. +/* .IP recipient +/* Recipient destination address. +/* SEE ALSO +/* recipient_list(3h) data structure +/* DIAGNOSTICS +/* Fatal errors: memory allocation. +/* 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 +/*--*/ + +/* Utility library. */ + +#include + +/* Global library. */ + +#include "recipient_list.h" + +/* recipient_list_init - initialize */ + +void recipient_list_init(RECIPIENT_LIST *list) +{ + list->avail = 1; + list->len = 0; + list->info = (RECIPIENT *) mymalloc(sizeof(RECIPIENT)); +} + +/* recipient_list_add - add rcpt to list */ + +void recipient_list_add(RECIPIENT_LIST *list, long offset, const char *rcpt) +{ + if (list->len >= list->avail) { + list->avail *= 2; + list->info = (RECIPIENT *) + myrealloc((char *) list->info, list->avail * sizeof(RECIPIENT)); + } + list->info[list->len].address = mystrdup(rcpt); + list->info[list->len].offset = offset; + list->len++; +} + +/* recipient_list_free - release memory for in-core recipient structure */ + +void recipient_list_free(RECIPIENT_LIST *list) +{ + RECIPIENT *rcpt; + + for (rcpt = list->info; rcpt < list->info + list->len; rcpt++) + myfree(rcpt->address); + myfree((char *) list->info); +} diff --git a/postfix/global/recipient_list.h b/postfix/global/recipient_list.h new file mode 100644 index 000000000..e7e09d236 --- /dev/null +++ b/postfix/global/recipient_list.h @@ -0,0 +1,46 @@ +#ifndef _RECIPIENT_LIST_H_INCLUDED_ +#define _RECIPIENT_LIST_H_INCLUDED_ + +/*++ +/* NAME +/* recipient_list 3h +/* SUMMARY +/* recipient list structures +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Information about a recipient is kept in this structure. The file offset + * tells us the position of the REC_TYPE_RCPT byte in the message queue + * file, This byte is replaced by REC_TYPE_DONE when the delivery status to + * that recipient is established. + */ +typedef struct RECIPIENT { + long offset; /* REC_TYPE_RCPT byte */ + char *address; /* complete address */ +} RECIPIENT; + +typedef struct RECIPIENT_LIST { + RECIPIENT *info; + int len; + int avail; +} RECIPIENT_LIST; + +extern void recipient_list_init(RECIPIENT_LIST *); +extern void recipient_list_add(RECIPIENT_LIST *, long, const char *); +extern void recipient_list_free(RECIPIENT_LIST *); + +/* 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/record.c b/postfix/global/record.c new file mode 100644 index 000000000..93c41c88b --- /dev/null +++ b/postfix/global/record.c @@ -0,0 +1,269 @@ +/*++ +/* NAME +/* record 3 +/* SUMMARY +/* simple typed record I/O +/* SYNOPSIS +/* #include +/* +/* int rec_get(stream, buf, maxsize) +/* VSTREAM *stream; +/* VSTRING *buf; +/* int maxsize; +/* +/* int rec_put(stream, type, data, len) +/* VSTREAM *stream; +/* int type; +/* const char *data; +/* int len; +/* AUXILIARY FUNCTIONS +/* int rec_put_type(stream, type, offset) +/* VSTREAM *stream; +/* int type; +/* long offset; +/* +/* int rec_fprintf(stream, type, format, ...) +/* VSTREAM *stream; +/* int type; +/* const char *format; +/* +/* int rec_fputs(stream, type, str) +/* VSTREAM *stream; +/* int type; +/* const char *str; +/* +/* int REC_PUT_BUF(stream, type, buf) +/* VSTREAM *stream; +/* int type; +/* VSTRING *buf; +/* +/* int rec_vfprintf(stream, type, format, ap) +/* VSTREAM *stream; +/* int type; +/* const char *format; +/* va_list ap; +/* DESCRIPTION +/* This module reads and writes typed variable-length records. +/* Each record contains a 1-byte type code (0..255), a length +/* (1 or more bytes) and as much data as the length specifies. +/* +/* rec_get() retrieves a record from the named record stream +/* and returns the record type. The \fImaxsize\fR argument is +/* zero, or specifies a maximal acceptable record length. +/* The result is REC_TYPE_EOF when the end of the file was reached, +/* and REC_TYPE_ERROR in case of a bad record. The result buffer is +/* null-terminated for convenience. Records may contain embedded +/* null characters. +/* +/* rec_put() stores the specified record and returns the record +/* type, or REC_TYPE_ERROR in case of problems. +/* +/* rec_put_type() updates the type field of the record at the +/* specified file offset. The result is the new record type, +/* or REC_TYPE_ERROR in case of trouble. +/* +/* rec_fprintf() and rec_vfprintf() format their arguments and +/* write the result to the named stream. The result is the same +/* as with rec_put(). +/* +/* rec_fputs() writes a record with as contents a copy of the +/* specified string. The result is the same as with rec_put(). +/* +/* REC_PUT_BUF() is a wrapper for rec_put() that makes it +/* easier to handle VSTRING buffers. It is an unsafe macro +/* that evaluates some arguments more than once. +/* DIAGNOSTICS +/* Panics: interface violations. Fatal errors: insufficient memory. +/* Warnings: corrupted file. +/* 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 /* 44BSD stdarg.h uses abort() */ +#include +#include +#include + +#ifndef NBBY +#define NBBY 8 /* XXX should be in sys_defs.h */ +#endif + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include + +/* rec_put_type - update record type field */ + +int rec_put_type(VSTREAM *stream, int type, long offset) +{ + if (msg_verbose > 2) + msg_info("rec_put_type: %d at %ld", type, offset); + + if (vstream_fseek(stream, offset, SEEK_SET) < 0 + || VSTREAM_PUTC(type, stream) != type) { + return (REC_TYPE_ERROR); + } else { + return (type); + } +} + +/* rec_put - store typed record */ + +int rec_put(VSTREAM *stream, int type, const char *data, int len) +{ + int len_rest; + int len_byte; + + if (msg_verbose > 2) + msg_info("rec_put: type %c len %d data %.10s", type, len, data); + + /* + * Write the record type, one byte. + */ + if (VSTREAM_PUTC(type, stream) == VSTREAM_EOF) + return (REC_TYPE_ERROR); + + /* + * Write the record data length in 7-bit portions, using the 8th bit to + * indicate that there is more. Use as many length bytes as needed. + */ + len_rest = len; + do { + len_byte = len_rest & 0177; + if (len_rest >>= 7) + len_byte |= 0200; + if (VSTREAM_PUTC(len_byte, stream) == VSTREAM_EOF) { + return (REC_TYPE_ERROR); + } + } while (len_rest != 0); + + /* + * Write the record data portion. Use as many length bytes as needed. + */ + if (len && vstream_fwrite(stream, data, len) != len) + return (REC_TYPE_ERROR); + return (type); +} + +/* rec_get - retrieve typed record */ + +int rec_get(VSTREAM *stream, VSTRING *buf, int maxsize) +{ + char *myname = "rec_get"; + int type; + int len; + int len_byte; + int shift; + + /* + * Sanity check. + */ + if (maxsize < 0) + msg_panic("%s: bad record size limit: %d", myname, maxsize); + + /* + * Extract the record type. + */ + if ((type = VSTREAM_GETC(stream)) == VSTREAM_EOF) + return (REC_TYPE_EOF); + + /* + * Find out the record data length. Return an error result when the + * record data length is malformed or when it exceeds the acceptable + * limit. + */ + for (len = 0, shift = 0; /* void */ ; shift += 7) { + if (shift >= (int) (NBBY * sizeof(int))) { + msg_warn("%s: too many length bits, record type %d", + VSTREAM_PATH(stream), type); + return (REC_TYPE_ERROR); + } + if ((len_byte = VSTREAM_GETC(stream)) == VSTREAM_EOF) { + msg_warn("%s: unexpected EOF reading length, record type %d", + VSTREAM_PATH(stream), type); + return (REC_TYPE_ERROR); + } + len |= (len_byte & 0177) << shift; + if ((len_byte & 0200) == 0) + break; + } + if (len < 0 || (maxsize > 0 && len > maxsize)) { + msg_warn("%s: illegal length %d, record type %d", + VSTREAM_PATH(stream), len, type); + while (len-- > 0 && VSTREAM_GETC(stream) != VSTREAM_EOF) + /* void */ ; + return (REC_TYPE_ERROR); + } + + /* + * Reserve buffer space for the result, and read the record data into the + * buffer. + */ + VSTRING_RESET(buf); + VSTRING_SPACE(buf, len); + if (vstream_fread(stream, vstring_str(buf), len) != len) { + msg_warn("%s: unexpected EOF in data, record type %d length %d", + VSTREAM_PATH(stream), type, len); + return (REC_TYPE_ERROR); + } + VSTRING_AT_OFFSET(buf, len); + VSTRING_TERMINATE(buf); + if (msg_verbose > 2) + msg_info("%s: type %c len %d data %.10s", myname, + type, len, vstring_str(buf)); + return (type); +} + +/* rec_vfprintf - write formatted string to record */ + +int rec_vfprintf(VSTREAM *stream, int type, const char *format, va_list ap) +{ + static VSTRING *vp; + + if (vp == 0) + vp = vstring_alloc(100); + + /* + * Writing a formatted string involves an extra copy, because we must + * know the record length before we can write it. + */ + vstring_vsprintf(vp, format, ap); + return (REC_PUT_BUF(stream, type, vp)); +} + +/* rec_fprintf - write formatted string to record */ + +int rec_fprintf(VSTREAM *stream, int type, const char *format,...) +{ + int result; + va_list ap; + + va_start(ap, format); + result = rec_vfprintf(stream, type, format, ap); + va_end(ap); + return (result); +} + +/* rec_fputs - write string to record */ + +int rec_fputs(VSTREAM *stream, int type, const char *str) +{ + return (rec_put(stream, type, str, str ? strlen(str) : 0)); +} diff --git a/postfix/global/record.h b/postfix/global/record.h new file mode 100644 index 000000000..872868948 --- /dev/null +++ b/postfix/global/record.h @@ -0,0 +1,59 @@ +#ifndef _RECORD_H_INCLUDED_ +#define _RECORD_H_INCLUDED_ + +/*++ +/* NAME +/* record 3h +/* SUMMARY +/* simple typed record I/O +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include +#include + + /* + * Record type values are positive numbers 0..255. Negative record type + * values are reserved for diagnostics. + */ +#define REC_TYPE_EOF -1 /* no record */ +#define REC_TYPE_ERROR -2 /* bad record */ + + /* + * Functional interface. + */ +extern int rec_get(VSTREAM *, VSTRING *, int); +extern int rec_put(VSTREAM *, int, const char *, int); +extern int rec_put_type(VSTREAM *, int, long); +extern int rec_fprintf(VSTREAM *, int, const char *,...); +extern int rec_fputs(VSTREAM *, int, const char *); + +#define REC_PUT_BUF(v, t, b) rec_put((v), (t), vstring_str(b), VSTRING_LEN(b)) + + /* + * Stuff that needs + */ +extern int rec_vfprintf(VSTREAM *, int, const char *, va_list); + +/* 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/remove.c b/postfix/global/remove.c new file mode 100644 index 000000000..bc17cbfbd --- /dev/null +++ b/postfix/global/remove.c @@ -0,0 +1,71 @@ +/*++ +/* NAME +/* REMOVE 3 +/* SUMMARY +/* remove or stash away file +/* SYNOPSIS +/* \fBint REMOVE(path)\fR +/* \fBconst char *path;\fR +/* DESCRIPTION +/* \fBREMOVE()\fR removes a file, or renames it to a unique name, +/* depending on the setting of the boolean \fBvar_dont_remove\fR +/* flag. +/* SEE ALSO +/* remove(3) +/* DIAGNOSTICS +/* The result is 0 in case of success, -1 in case of trouble. +/* The global \fBerrno\fR variable reflects the nature of the +/* problem. +/* FILES +/* saved/*, stashed-away files. +/* 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 + +/* Utility library. */ + +#include + +/* Global library. */ + +#include + +/* REMOVE - squirrel away a file instead of removing it */ + +int REMOVE(const char *path) +{ + static VSTRING *dest; + char *slash; + struct stat st; + + if (var_dont_remove == 0) { + return (remove(path)); + } else { + if (dest == 0) + dest = vstring_alloc(10); + vstring_sprintf(dest, "saved/%s", ((slash = strrchr(path, '/')) != 0) ? + slash + 1 : path); + for (;;) { + if (stat(vstring_str(dest), &st) < 0) + break; + vstring_strcat(dest, "+"); + } + return (rename(path, vstring_str(dest))); + } +} diff --git a/postfix/global/resolve_clnt.c b/postfix/global/resolve_clnt.c new file mode 100644 index 000000000..605de1013 --- /dev/null +++ b/postfix/global/resolve_clnt.c @@ -0,0 +1,247 @@ +/*++ +/* NAME +/* resolve_clnt 3 +/* SUMMARY +/* address resolve service client (internal forms) +/* SYNOPSIS +/* #include +/* +/* typedef struct { +/* .in +4 +/* VSTRING *transport; +/* VSTRING *nexthop +/* VSTRING *recipient; +/* .in -4 +/* } RESOLVE_REPLY; +/* +/* void resolve_clnt_init(reply) +/* RESOLVE_REPLY *reply; +/* +/* void resolve_clnt_query(address, reply) +/* const char *address +/* RESOLVE_REPLY *reply; +/* +/* void resolve_clnt_free(reply) +/* RESOLVE_REPLY *reply; +/* DESCRIPTION +/* This module implements a mail address resolver client. +/* +/* resolve_clnt_init() initializes a reply data structure for use +/* by resolve_clnt_query(). The structure is destroyed by passing +/* it to resolve_clnt_free(). +/* +/* resolve_clnt_query() sends an internal-form recipient address +/* (user@domain) to the resolver daemon and returns the resulting +/* transport name, next_hop host name, and internal-form recipient +/* address. In case of communication failure the program keeps trying +/* until the mail system goes down. +/* DIAGNOSTICS +/* Warnings: communication failure. Fatal error: mail system is down. +/* 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 +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include "mail_proto.h" +#include "mail_params.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); +} + +/* resolve_clnt_init - initialize reply */ + +void resolve_clnt_init(RESOLVE_REPLY *reply) +{ + reply->transport = vstring_alloc(100); + reply->nexthop = vstring_alloc(100); + reply->recipient = vstring_alloc(100); +} + +/* resolve_clnt_query - resolve address to (transport, next hop, recipient) */ + +void resolve_clnt_query(const char *addr, RESOLVE_REPLY *reply) +{ + char *myname = "resolve_clnt_query"; + + /* + * 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 + + 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) + msg_warn("%s: bad write: %m", myname); + } else if (mail_scan(resolve_fp, "%s %s %s", reply->transport, + reply->nexthop, reply->recipient) != 3) { + if (msg_verbose || errno != EPIPE) + msg_warn("%s: bad read: %m", myname); + } else { + if (msg_verbose) + msg_info("%s: `%s' -> t=`%s' h=`%s' r=`%s'", + myname, addr, STR(reply->transport), + STR(reply->nexthop), STR(reply->recipient)); + if (STR(reply->transport)[0] == 0) + msg_warn("%s: null transport result for: <%s>", myname, addr); + else if (STR(reply->recipient)[0] == 0) + msg_warn("%s: null recipient result for: <%s>", myname, addr); + else + break; + } + sleep(10); /* XXX make configurable */ + resolve_clnt_disconnect(); + } +} + +/* resolve_clnt_free - destroy reply */ + +void resolve_clnt_free(RESOLVE_REPLY *reply) +{ + reply->transport = vstring_free(reply->transport); + reply->nexthop = vstring_free(reply->nexthop); + reply->recipient = vstring_free(reply->recipient); +} + +#ifdef TEST + +#include +#include +#include +#include + +static NORETURN usage(char *myname) +{ + msg_fatal("usage: %s [-v] [address...]", myname); +} + +static void resolve(char *addr, RESOLVE_REPLY *reply) +{ + resolve_clnt_query(addr, reply); + vstream_printf("%-10s %s\n", "address", addr); + vstream_printf("%-10s %s\n", "transport", STR(reply->transport)); + vstream_printf("%-10s %s\n", "nexthop", *STR(reply->nexthop) ? + STR(reply->nexthop) : "[none]"); + vstream_printf("%-10s %s\n", "recipient", STR(reply->recipient)); + vstream_fflush(VSTREAM_OUT); +} + +main(int argc, char **argv) +{ + RESOLVE_REPLY reply; + int ch; + + msg_vstream_init(argv[0], VSTREAM_ERR); + + read_config(); + msg_info("using config files in %s", var_config_dir); + if (chdir(var_queue_dir) < 0) + msg_fatal("chdir %s: %m", var_queue_dir); + + while ((ch = GETOPT(argc, argv, "v")) > 0) { + switch (ch) { + case 'v': + msg_verbose++; + break; + default: + usage(argv[0]); + } + } + resolve_clnt_init(&reply); + + if (argc > optind) { + while (argv[optind]) { + resolve(argv[optind], &reply); + optind++; + } + } else { + VSTRING *buffer = vstring_alloc(1); + + while (vstring_fgets_nonl(buffer, VSTREAM_IN)) { + resolve(STR(buffer), &reply); + } + } +} + +#endif diff --git a/postfix/global/resolve_clnt.h b/postfix/global/resolve_clnt.h new file mode 100644 index 000000000..308e5a8c7 --- /dev/null +++ b/postfix/global/resolve_clnt.h @@ -0,0 +1,51 @@ +#ifndef _RESOLVE_CLNT_H_INCLUDED_ +#define _RESOLVE_CLNT_H_INCLUDED_ + +/*++ +/* NAME +/* resolve_clnt 3h +/* SUMMARY +/* address resolver client +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +#define RESOLVE_ADDR "resolve" + +typedef struct RESOLVE_REPLY { + VSTRING *transport; + VSTRING *nexthop; + VSTRING *recipient; +} RESOLVE_REPLY; + +extern void resolve_clnt_init(RESOLVE_REPLY *); +extern void resolve_clnt_query(const char *, RESOLVE_REPLY *); +extern void resolve_clnt_free(RESOLVE_REPLY *); + +#define RESOLVE_CLNT_ASSIGN(reply, transport, nexthop, recipient) { \ + (reply).transport = (transport); \ + (reply).nexthop = (nexthop); \ + (reply).recipient = (recipient); \ + } + +/* 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/resolve_local.c b/postfix/global/resolve_local.c new file mode 100644 index 000000000..6588fddc0 --- /dev/null +++ b/postfix/global/resolve_local.c @@ -0,0 +1,136 @@ +/*++ +/* NAME +/* resolve_local 3 +/* SUMMARY +/* determine if address resolves to local mail system +/* SYNOPSIS +/* #include +/* +/* void resolve_local_init() +/* +/* int resolve_local(host) +/* const char *host; +/* DESCRIPTION +/* resolve_local() determines if the named domain resolves to the +/* local mail system, either by case-insensitive exact match +/* against the domains, files or tables listed in $mydestination, +/* or by any of the network addresses listed in $inet_interfaces. +/* +/* resolve_local_init() performs initialization. If this routine is +/* not called explicitly ahead of time, it will be called on the fly. +/* BUGS +/* Calling resolve_local_init() on the fly is an incomplete solution. +/* It is bound to fail with applications that enter a chroot jail. +/* SEE ALSO +/* own_inet_addr(3), find out my own network interfaces +/* match_list(3), generic pattern matching engine +/* match_ops(3), generic pattern matching operators +/* 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 + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include +#include +#include + +/* Application-specific */ + +static STRING_LIST *resolve_local_list; + +/* resolve_local_init - initialize lookup table */ + +void resolve_local_init(void) +{ + if (resolve_local_list) + msg_panic("resolve_local_init: duplicate initialization"); + resolve_local_list = string_list_init(var_mydest); +} + +/* resolve_local - match address against list of local destinations */ + +int resolve_local(const char *addr) +{ + char *saved_addr = mystrdup(addr); + char *dest; + struct in_addr ipaddr; + int len; + +#define RETURN(x) { myfree(saved_addr); return(x); } + + if (resolve_local_list == 0) + resolve_local_init(); + + /* + * Strip one trailing dot. + */ + len = strlen(saved_addr); + if (saved_addr[len - 1] == '.') + saved_addr[--len] = 0; + + /* + * Compare the destination against the list of destinations that we + * consider local. + */ + if (string_list_match(resolve_local_list, saved_addr)) + RETURN(1); + + /* + * Compare the destination against the list of interface addresses that + * we are supposed to listen on. + */ + dest = saved_addr; + if (*dest == '[' && dest[len - 1] == ']') { + dest++; + dest[len -= 2] = 0; + if ((ipaddr.s_addr = inet_addr(dest)) != INADDR_NONE + && own_inet_addr(&ipaddr)) + RETURN(1); + } + + /* + * Must be remote, or a syntax error. + */ + RETURN(0); +} + +#ifdef TEST + +#include +#include + +int main(int argc, char **argv) +{ + if (argc != 2) + msg_fatal("usage: %s domain", argv[0]); + read_config(); + vstream_printf("%s\n", resolve_local(argv[1]) ? "yes" : "no"); + vstream_fflush(VSTREAM_OUT); +} + +#endif diff --git a/postfix/global/resolve_local.h b/postfix/global/resolve_local.h new file mode 100644 index 000000000..53af376d0 --- /dev/null +++ b/postfix/global/resolve_local.h @@ -0,0 +1,31 @@ +#ifndef _RESOLVE_LOCAL_H_INCLUDED_ +#define _RESOLVE_LOCAL_H_INCLUDED_ + +/*++ +/* NAME +/* resolve_local 3h +/* SUMMARY +/* determine if address resolves to local mail system +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern int resolve_local(const char *); +extern void resolve_local_init(void); + +/* 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/rewrite_clnt.c b/postfix/global/rewrite_clnt.c new file mode 100644 index 000000000..59e225019 --- /dev/null +++ b/postfix/global/rewrite_clnt.c @@ -0,0 +1,243 @@ +/*++ +/* NAME +/* rewrite_clnt 3 +/* SUMMARY +/* address rewrite service client +/* SYNOPSIS +/* #include +/* #include +/* +/* VSTRING *rewrite_clnt(ruleset, address, result) +/* const char *ruleset; +/* const char *address; +/* +/* VSTRING *rewrite_clnt_internal(ruleset, address, result) +/* const char *ruleset; +/* const char *address; +/* VSTRING *result; +/* DESCRIPTION +/* This module implements a mail address rewriting client. +/* +/* rewrite_clnt() sends a rule set name and external-form address to the +/* rewriting service and returns the resulting external-form address. +/* In case of communication failure the program keeps trying until the +/* mail system shuts down. +/* +/* rewrite_clnt_internal() performs the same functionality but takes +/* input in internal (unquoted) form, and produces output in internal +/* (unquoted) form. +/* DIAGNOSTICS +/* Warnings: communication failure. Fatal error: mail system is down. +/* 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 +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include "mail_proto.h" +#include "mail_params.h" +#include "rewrite_clnt.h" + +/* Application-specific. */ + +static VSTREAM *rewrite_fp = 0; +static void rewrite_clnt_disconnect(void); + +/* 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 */ + +static void rewrite_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. + */ + 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) */ + +VSTRING *rewrite_clnt(const char *rule, const char *addr, VSTRING *result) +{ + char *myname = "rewrite_clnt"; + + /* + * 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 + + 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) + msg_warn("%s: bad write: %m", myname); + } else if (mail_scan(rewrite_fp, "%s", result) != 1) { + if (msg_verbose || errno != EPIPE) + msg_warn("%s: bad read: %m", myname); + } else { + if (msg_verbose) + msg_info("rewrite_clnt: %s: %s -> %s", + rule, addr, vstring_str(result)); + if (addr[0] != 0 && STR(result)[0] == 0) + msg_warn("%s: null result for: <%s>", myname, addr); + else + return (result); + } + sleep(10); /* XXX make configurable */ + rewrite_clnt_disconnect(); + } +} + +/* rewrite_clnt_internal - rewrite from/to internal form */ + +VSTRING *rewrite_clnt_internal(const char *ruleset, const char *addr, VSTRING *result) +{ + VSTRING *temp = 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); + return (result); +} + +#ifdef TEST + +#include +#include +#include +#include +#include +#include + +static NORETURN usage(char *myname) +{ + msg_fatal("usage: %s [-v] [rule address...]", myname); +} + +static void rewrite(char *rule, char *addr, VSTRING *reply) +{ + rewrite_clnt(rule, addr, reply); + vstream_printf("%-10s %s\n", "rule", rule); + vstream_printf("%-10s %s\n", "address", addr); + vstream_printf("%-10s %s\n", "result", STR(reply)); + vstream_fflush(VSTREAM_OUT); +} + +main(int argc, char **argv) +{ + VSTRING *reply; + int ch; + char *rule; + char *addr; + + msg_vstream_init(argv[0], VSTREAM_ERR); + + read_config(); + msg_info("using config files in %s", var_config_dir); + if (chdir(var_queue_dir) < 0) + msg_fatal("chdir %s: %m", var_queue_dir); + + while ((ch = GETOPT(argc, argv, "v")) > 0) { + switch (ch) { + case 'v': + msg_verbose++; + break; + default: + usage(argv[0]); + } + } + reply = vstring_alloc(1); + + if (argc > optind) { + for (;;) { + if ((rule = argv[optind++]) == 0) + break; + if ((addr = argv[optind++]) == 0) + usage(argv[0]); + rewrite(rule, addr, reply); + } + } else { + VSTRING *buffer = vstring_alloc(1); + + while (vstring_fgets_nonl(buffer, VSTREAM_IN)) { + if ((rule = strtok(STR(buffer), " \t,")) == 0 + || (addr = strtok((char *) 0, " \t,")) == 0) + usage(argv[0]); + rewrite(rule, addr, reply); + } + vstring_free(buffer); + } + vstring_free(reply); +} + +#endif diff --git a/postfix/global/rewrite_clnt.h b/postfix/global/rewrite_clnt.h new file mode 100644 index 000000000..8e2230180 --- /dev/null +++ b/postfix/global/rewrite_clnt.h @@ -0,0 +1,39 @@ +#ifndef _REWRITE_CLNT_H_INCLUDED_ +#define _REWRITE_CLNT_H_INCLUDED_ + +/*++ +/* NAME +/* rewrite_clnt 3h +/* SUMMARY +/* address rewriter client +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +#define REWRITE_ADDR "rewrite" +#define REWRITE_CANON "canonicalize" + +extern VSTRING *rewrite_clnt(const char *, const char *, VSTRING *); +extern VSTRING *rewrite_clnt_internal(const char *, const char *, VSTRING *); + +/* 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/sent.c b/postfix/global/sent.c new file mode 100644 index 000000000..3b0c11f89 --- /dev/null +++ b/postfix/global/sent.c @@ -0,0 +1,101 @@ +/*++ +/* NAME +/* sent 3 +/* SUMMARY +/* log that a message was sent +/* SYNOPSIS +/* #include +/* +/* int sent(queue_id, recipient, relay, entry, format, ...) +/* const char *queue_id; +/* const char *recipient; +/* const char *relay; +/* time_t entry; +/* const char *format; +/* +/* int vsent(queue_id, recipient, relay, entry, format, ap) +/* const char *queue_id; +/* const char *recipient; +/* const char *relay; +/* time_t entry; +/* const char *format; +/* va_list ap; +/* DESCRIPTION +/* sent() logs that a message was successfully delivered. +/* +/* vsent() implements an alternative interface. +/* +/* Arguments: +/* .IP queue_id +/* The message queue id. +/* .IP recipient +/* The recipient address. +/* .IP relay +/* Name of the host we're talking to. +/* .IP entry +/* Message arrival time. +/* .IP format +/* Optional additional information. +/* .PP +/* For convenience, sent() always returns a zero result. +/* DIAGNOSTICS +/* Fatal: out of memory. +/* BUGS +/* Should be replaced by routines with an attribute-value based +/* interface instead of an interface that uses a rigid argument list. +/* 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 /* 44BSD stdarg.h uses abort() */ +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "sent.h" + +/* sent - log that a message was sent */ + +int sent(const char *queue_id, const char *recipient, const char *relay, + time_t entry, const char *fmt,...) +{ + va_list ap; + + va_start(ap, fmt); + vsent(queue_id, recipient, relay, entry, fmt, ap); + va_end(ap); + return (0); +} + +/* vsent - log that a message was sent */ + +int vsent(const char *queue_id, const char *recipient, const char *relay, + time_t entry, const char *fmt, va_list ap) +{ +#define TEXT (vstring_str(text)) + VSTRING *text = vstring_alloc(100); + int delay = time((time_t *) 0) - entry; + + vstring_vsprintf(text, fmt, ap); + msg_info("%s: to=<%s>, relay=%s, delay=%d, status=sent%s%s%s", + queue_id, recipient, relay, delay, + *TEXT ? " (" : "", TEXT, *TEXT ? ")" : ""); + vstring_free(text); + return (0); +} diff --git a/postfix/global/sent.h b/postfix/global/sent.h new file mode 100644 index 000000000..82eaac188 --- /dev/null +++ b/postfix/global/sent.h @@ -0,0 +1,40 @@ +#ifndef _SENT_H_INCLUDED_ +#define _SENT_H_INCLUDED_ + +/*++ +/* NAME +/* sent 3h +/* SUMMARY +/* log that message was sent +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include +#include + + /* + * External interface. + */ +extern int sent(const char *, const char *, const char *, + time_t, const char *,...); +extern int vsent(const char *, const char *, const char *, + time_t, const char *, va_list); + +/* 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 +/*--*/ +/**INDENT** Error@17: Unmatched #endif */ + +#endif diff --git a/postfix/global/smtp_stream.c b/postfix/global/smtp_stream.c new file mode 100644 index 000000000..bb499a640 --- /dev/null +++ b/postfix/global/smtp_stream.c @@ -0,0 +1,399 @@ +/*++ +/* NAME +/* smtp_stream 3 +/* SUMMARY +/* smtp stream I/O support +/* SYNOPSIS +/* #include +/* +/* jmp_buf smtp_timeout_buf; +/* +/* void smtp_timeout_setup(stream, timeout) +/* VSTREAM *stream; +/* int timeout; +/* +/* void smtp_printf(stream, format, ...) +/* VSTREAM *stream; +/* const char *format; +/* +/* int smtp_get(vp, stream, maxlen) +/* VSTRING *vp; +/* VSTREAM *stream; +/* int maxlen; +/* +/* void smtp_fputs(str, len, stream) +/* const char *str; +/* int len; +/* VSTREAM *stream; +/* +/* void smtp_fwrite(str, len, stream) +/* const char *str; +/* int len; +/* VSTREAM *stream; +/* +/* void smtp_fputc(ch, stream) +/* int ch; +/* VSTREAM *stream; +/* +/* void smtp_vprintf(stream, format, ap) +/* VSTREAM *stream; +/* char *format; +/* va_list ap; +/* DESCRIPTION +/* This module reads and writes text records delimited by CR LF, +/* with error detection: timeouts or unexpected end-of-file. +/* A trailing CR LF is added upon writing and removed upon reading. +/* +/* smtp_timeout_setup() arranges for a time limit on the smtp read +/* and write operations described below. +/* This routine alters the behavior of streams as follows: +/* .IP \(bu +/* The read routine is replaced by one than calls timed_read(). +/* .IP \(bu +/* The write routine is replaced by one that calls timed_write(). +/* .IP \f(bu +/* The stream is configured to use double buffering. +/* .IP \f(bu +/* A timeout error is reported to the vstream module as an I/O error. +/* .PP +/* smtp_printf() formats its arguments and writes the result to +/* the named stream, followed by a CR LF stream. The stream is flushed. +/* Long lines of text are not broken. +/* +/* smtp_get() reads the named stream up to and including +/* the next LF character and strips the trailing CR LF. The +/* \fImaxlen\fR argument limits the length of a line of text, +/* and protects the program against running out of memory. +/* Specify a zero bound to turn off bounds checking. +/* The result is the last character read, or VSTREAM_EOF. +/* +/* smtp_fputs() writes its string argument to the named stream. +/* Long strings are not broken. Each string is followed by a +/* CR LF pair. The stream is not flushed. +/* +/* smtp_fwrite() writes its string argument to the named stream. +/* Long strings are not broken. No CR LF is appended. The stream +/* is not flushed. +/* +/* smtp_fputc() writes one character to the named stream. +/* The stream is not flushed. +/* +/* smtp_vprintf() is the machine underneath smtp_printf(). +/* DIAGNOSTICS +/* .fi +/* .ad +/* In case of error, a longjmp() is performed to the context +/* saved in the global \fIsmtp_timeout_buf\fR. +/* Error codes passed along with longjmp() are: +/* .IP SMTP_ERR_EOF +/* The peer has disconnected unexpectedly. +/* .IP SMTP_ERR_TIME +/* The time limit specified to smtp_timeout_setup() was exceeded. +/* BUGS +/* The timeout etc. context is static, so this module can handle +/* only one SMTP session at a time. +/* +/* The timeout protection, including longjmp(), affects all I/O +/* on the named stream, not just the I/O done by this module. +/* 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 +#include /* FD_ZERO() needs bzero() prototype */ +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "smtp_stream.h" + +jmp_buf smtp_timeout_buf; + + /* + * Timeout handling. When a timeout happens, we shut down the connection in + * the appropriate direction, to force the I/O operation to fail. + */ +static int smtp_timeout_done; +static int smtp_maxtime; + +#define SMTP_DIR_READ 0 /* read direction */ +#define SMTP_DIR_WRITE 1 /* write direction */ + +/* smtp_timeout_event - timeout handler */ + +static void smtp_timeout_event(int fd, int direction) +{ + + /* + * Yes, this is gross, but we cannot longjump() away. Although timeouts + * are restricted to read() and write() operations, we could still leave + * things in an inconsistent state. Instead, we set a flag and force an + * I/O error on the smtp stream. + */ + if (shutdown(fd, direction) < 0) + if (errno != ENOTCONN) + msg_warn("smtp_timeout_event: shutdown: %m"); + smtp_timeout_done = 1; +} + +/* smtp_read - read with timeout */ + +static int smtp_read(int fd, void *buf, unsigned len) +{ + if (read_wait(fd, smtp_maxtime) < 0) { + smtp_timeout_event(fd, SMTP_DIR_READ); + return (-1); + } else { + return (read(fd, buf, len)); + } +} + +/* smtp_write - write with timeout */ + +static int smtp_write(int fd, void *buf, unsigned len) +{ + if (write_wait(fd, smtp_maxtime) < 0) { + smtp_timeout_event(fd, SMTP_DIR_WRITE); + return (-1); + } else { + return (write(fd, buf, len)); + } +} + +/* smtp_timeout_protect - setup timeout trap for specified stream. */ + +static void smtp_timeout_protect(void) +{ + smtp_timeout_done = 0; +} + +/* smtp_timeout_unprotect - finish timeout trap */ + +static void smtp_timeout_unprotect(void) +{ + if (smtp_timeout_done) + longjmp(smtp_timeout_buf, SMTP_ERR_TIME); +} + +/* smtp_timeout_setup - configure timeout trap */ + +void smtp_timeout_setup(VSTREAM *stream, int maxtime) +{ + + /* + * XXX The timeout etc. state is static, so a process can have at most + * one SMTP session at a time. We could use the VSTREAM file descriptor + * number as key into a BINHASH table with per-stream contexts. This + * would allow us to keep talk to multiple SMTP streams at the same time. + * Another possibilty is to use the file descriptor as an index into a + * linear table of structure pointers. In either case we would need to + * provide an smtp_timeout_cleanup() routine to dispose of memory that is + * no longer needed. + */ + vstream_control(stream, + VSTREAM_CTL_READ_FN, smtp_read, + VSTREAM_CTL_WRITE_FN, smtp_write, + VSTREAM_CTL_DOUBLE, + VSTREAM_CTL_END); + smtp_maxtime = maxtime; +} + +/* smtp_vprintf - write one line to SMTP peer */ + +void smtp_vprintf(VSTREAM *stream, const char *fmt, va_list ap) +{ + int err; + + /* + * Do the I/O, protected against timeout. + */ + smtp_timeout_protect(); + vstream_vfprintf(stream, fmt, ap); + vstream_fputs("\r\n", stream); + err = vstream_fflush(stream); + smtp_timeout_unprotect(); + + /* + * See if there was a problem. + */ + if (err != 0) { + if (msg_verbose) + msg_info("smtp_vprintf: EOF"); + longjmp(smtp_timeout_buf, SMTP_ERR_EOF); + } +} + +/* smtp_printf - write one line to SMTP peer */ + +void smtp_printf(VSTREAM *stream, const char *fmt,...) +{ + va_list ap; + + va_start(ap, fmt); + smtp_vprintf(stream, fmt, ap); + va_end(ap); +} + +/* smtp_get - read one line from SMTP peer */ + +int smtp_get(VSTRING *vp, VSTREAM *stream, int bound) +{ + int last_char; + int next_char; + + /* + * It's painful to do I/O with records that may span multiple buffers. + * Allow for partial long lines (we will read the remainder later) and + * allow for lines ending in bare LF. The idea is to be liberal in what + * we accept, strict in what we send. + */ + smtp_timeout_protect(); + last_char = (bound == 0 ? vstring_get(vp, stream) : + vstring_get_bound(vp, stream, bound)); + + switch (last_char) { + + /* + * Do some repair in the rare case that we stopped reading in the + * middle of the CRLF record terminator. + */ + case '\r': + if ((next_char = VSTREAM_GETC(stream)) == '\n') { + VSTRING_ADDCH(vp, '\n'); + last_char = '\n'; + /* FALLTRHOUGH */ + } else { + if (next_char != VSTREAM_EOF) + vstream_ungetc(stream, next_char); + break; + } + + /* + * Strip off the record terminator: either CRLF or just bare LF. + */ + case '\n': + if (VSTRING_LEN(vp) > 1 && vstring_end(vp)[-2] == '\r') + vstring_truncate(vp, VSTRING_LEN(vp) - 2); + else + vstring_truncate(vp, VSTRING_LEN(vp) - 1); + VSTRING_TERMINATE(vp); + + /* + * Partial line: just read the remainder later. If we ran into EOF, + * the next test will deal with it. + */ + default: + break; + } + smtp_timeout_unprotect(); + + /* + * EOF is bad, whether or not it happens in the middle of a record. Don't + * allow data that was truncated because of EOF. + */ + if (vstream_feof(stream) || vstream_ferror(stream)) { + if (msg_verbose) + msg_info("smtp_get: EOF"); + longjmp(smtp_timeout_buf, SMTP_ERR_EOF); + } + return (last_char); +} + +/* smtp_fputs - write one line to SMTP peer */ + +void smtp_fputs(const char *cp, int todo, VSTREAM *stream) +{ + unsigned err; + + if (todo < 0) + msg_panic("smtp_fputs: negative todo %d", todo); + + /* + * Do the I/O, protected against timeout. + */ + smtp_timeout_protect(); + err = (vstream_fwrite(stream, cp, todo) != todo + || vstream_fputs("\r\n", stream) == VSTREAM_EOF); + smtp_timeout_unprotect(); + + /* + * See if there was a problem. + */ + if (err != 0) { + if (msg_verbose) + msg_info("smtp_fputs: EOF"); + longjmp(smtp_timeout_buf, SMTP_ERR_EOF); + } +} + +/* smtp_fwrite - write one string to SMTP peer */ + +void smtp_fwrite(const char *cp, int todo, VSTREAM *stream) +{ + unsigned err; + + if (todo < 0) + msg_panic("smtp_fwrite: negative todo %d", todo); + + /* + * Do the I/O, protected against timeout. + */ + smtp_timeout_protect(); + err = (vstream_fwrite(stream, cp, todo) != todo); + smtp_timeout_unprotect(); + + /* + * See if there was a problem. + */ + if (err != 0) { + if (msg_verbose) + msg_info("smtp_fwrite: EOF"); + longjmp(smtp_timeout_buf, SMTP_ERR_EOF); + } +} + +/* smtp_fputc - write to SMTP peer */ + +void smtp_fputc(int ch, VSTREAM *stream) +{ + int stat; + + /* + * Do the I/O, protected against timeout. + */ + smtp_timeout_protect(); + stat = VSTREAM_PUTC(ch, stream); + smtp_timeout_unprotect(); + + /* + * See if there was a problem. + */ + if (stat == VSTREAM_EOF) { + if (msg_verbose) + msg_info("smtp_fputc: EOF"); + longjmp(smtp_timeout_buf, SMTP_ERR_EOF); + } +} diff --git a/postfix/global/smtp_stream.h b/postfix/global/smtp_stream.h new file mode 100644 index 000000000..f149e54e8 --- /dev/null +++ b/postfix/global/smtp_stream.h @@ -0,0 +1,54 @@ +#ifndef _SMTP_STREAM_H_INCLUDED_ +#define _SMTP_STREAM_H_INCLUDED_ + +/*++ +/* NAME +/* smtp_stream 3h +/* SUMMARY +/* smtp stream I/O support +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include + + /* + * External interface. + */ +#define SMTP_ERR_EOF 1 /* unexpected client disconnect */ +#define SMTP_ERR_TIME 2 /* time out */ + +extern jmp_buf smtp_timeout_buf; + +extern void smtp_timeout_setup(VSTREAM *, int); +extern void smtp_printf(VSTREAM *, const char *,...); +extern int smtp_get(VSTRING *, VSTREAM *, int); +extern void smtp_fputs(const char *, int len, VSTREAM *); +extern void smtp_fwrite(const char *, int len, VSTREAM *); +extern void smtp_fputc(int, VSTREAM *); + +extern void smtp_vprintf(VSTREAM *, const char *, va_list); + +/* 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/split_addr.c b/postfix/global/split_addr.c new file mode 100644 index 000000000..4dbb3580d --- /dev/null +++ b/postfix/global/split_addr.c @@ -0,0 +1,79 @@ +/*++ +/* NAME +/* split_addr 3 +/* SUMMARY +/* recipient localpart address splitter +/* SYNOPSIS +/* #include +/* +/* char *split_addr(localpart, delimiter) +/* char *localpart; +/* int delimiter; +/* DESCRIPTION +/* split_addr() null-terminates \fIlocalpart\fR at the first +/* occurrence of the \fIdelimiter\fR character found, and +/* returns a pointer to the remainder. +/* +/* Reserved addresses are not split: postmaster, mailer-daemon, +/* double-bounce, addresses that begin with owner-, or addresses +/* that end in -request. +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include + +/* Global library. */ + +#include +#include +#include + +/* split_addr - split address with extreme prejudice */ + +char *split_addr(char *localpart, int delimiter) +{ + int len; + + /* + * Don't split these, regardless of what the delimiter is. + */ + if (strcasecmp(localpart, MAIL_ADDR_POSTMASTER) == 0) + return (0); + if (strcasecmp(localpart, MAIL_ADDR_MAIL_DAEMON) == 0) + return (0); + if (strcasecmp(localpart, var_double_bounce_sender) == 0) + return (0); + + /* + * Backwards compatibility: don't split owner-foo or foo-request. + */ + if (strncasecmp(localpart, "owner-", 6) == 0) + return (0); + if ((len = strlen(localpart) - 8) > 0 + && strcasecmp(localpart + len, "-request") == 0) + return (0); + + /* + * Safe to split this address. + */ + return (split_at(localpart, delimiter)); +} diff --git a/postfix/global/split_addr.h b/postfix/global/split_addr.h new file mode 100644 index 000000000..1c87fb99a --- /dev/null +++ b/postfix/global/split_addr.h @@ -0,0 +1,29 @@ +#ifndef _SPLIT_ADDR_H_INCLUDED_ +#define _SPLIT_ADDR_H_INCLUDED_ + +/*++ +/* NAME +/* split_addr 3h +/* SUMMARY +/* recipient localpart address splitter +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern char *split_addr(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/global/stream2rec.c b/postfix/global/stream2rec.c new file mode 100644 index 000000000..99f40eef2 --- /dev/null +++ b/postfix/global/stream2rec.c @@ -0,0 +1,46 @@ +/*++ +/* NAME +/* stream2rec 1 +/* SUMMARY +/* convert stream-lf data to record format +/* SYNOPSIS +/* stream2rec +/* DESCRIPTION +/* stream2rec reads lines from standard input and writes +/* them to standard output in record form. +/* 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 + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include +#include + +main(void) +{ + VSTRING *buf = vstring_alloc(150); + int type; + + while ((type = rec_streamlf_get(VSTREAM_IN, buf, 150)) > 0) + REC_PUT_BUF(VSTREAM_OUT, type, buf); + vstream_fflush(VSTREAM_OUT); +} diff --git a/postfix/global/string_list.c b/postfix/global/string_list.c new file mode 100644 index 000000000..bcb457e77 --- /dev/null +++ b/postfix/global/string_list.c @@ -0,0 +1,128 @@ +/*++ +/* NAME +/* string_list 3 +/* SUMMARY +/* match a string against a pattern list +/* SYNOPSIS +/* #include +/* +/* STRING_LIST *string_list_init(pattern_list) +/* const char *pattern_list; +/* +/* int string_list_match(list, name) +/* STRING_LIST *list; +/* const char *name; +/* +/* void string_list_free(list) +/* STRING_LIST *list; +/* DESCRIPTION +/* This module implements tests for list membership of a string. +/* +/* Patterns are separated by whitespace and/or commas. A pattern +/* is either a string, a file name (in which case the contents +/* of the file are substituted for the file name) or a type:name +/* lookup table specification. +/* +/* A string matches a string list when it appears in the list of +/* string patterns. The matching process is case insensitive. +/* In order to reverse the result, precede a non-file name pattern +/* with an exclamation point (!). +/* +/* string_list_init() performs initializations. The argument is a +/* list of string patterns. +/* +/* string_list_match() matches the specified string against the +/* compiled pattern list. +/* +/* string_list_free() releases storage allocated by string_list_init(). +/* DIAGNOSTICS +/* Fatal error: unable to open or read a pattern file or table. +/* SEE ALSO +/* match_list(3) generic list matching +/* match_ops(3) match strings by name or by address +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "string_list.h" + +/* string_list_init - initialize string list */ + +STRING_LIST *string_list_init(const char *patterns) +{ + return (match_list_init(patterns, 1, match_string)); +} + +/* string_list_match - match string against list */ + +int string_list_match(STRING_LIST * list, const char *string) +{ + return (match_list_match(list, string)); +} + +/* string_list_free - release storage */ + +void string_list_free(STRING_LIST * list) +{ + match_list_free(list); +} + +#ifdef TEST + +#include +#include +#include +#include +#include + +static void usage(char *progname) +{ + msg_fatal("usage: %s [-v] patterns string", progname); +} + +main(int argc, char **argv) +{ + STRING_LIST *list; + char *string; + int ch; + + msg_vstream_init(argv[0], VSTREAM_ERR); + + while ((ch = GETOPT(argc, argv, "v")) > 0) { + switch (ch) { + case 'v': + msg_verbose++; + break; + default: + usage(argv[0]); + } + } + if (argc != optind + 2) + usage(argv[0]); + list = string_list_init(argv[optind]); + string = argv[optind + 1]; + vstream_printf("%s: %s\n", string, string_list_match(list, string) ? + "YES" : "NO"); + vstream_fflush(VSTREAM_OUT); + string_list_free(list); +} + +#endif diff --git a/postfix/global/string_list.h b/postfix/global/string_list.h new file mode 100644 index 000000000..946ecf1d2 --- /dev/null +++ b/postfix/global/string_list.h @@ -0,0 +1,34 @@ +#ifndef _STRING_LIST_H_INCLUDED_ +#define _STRING_LIST_H_INCLUDED_ + +/*++ +/* NAME +/* string_list 3h +/* SUMMARY +/* match a string against a pattern list +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +typedef struct MATCH_LIST STRING_LIST; + +extern STRING_LIST *string_list_init(const char *); +extern int string_list_match(STRING_LIST *, const char *); +extern void string_list_free(STRING_LIST *); + +/* 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/sys_exits.c b/postfix/global/sys_exits.c new file mode 100644 index 000000000..ef7eb2ef5 --- /dev/null +++ b/postfix/global/sys_exits.c @@ -0,0 +1,102 @@ +/*++ +/* NAME +/* sys_exits 3 +/* SUMMARY +/* sendmail-compatible exit status handling +/* SYNOPSIS +/* #include +/* +/* int SYS_EXITS_CODE(code) +/* int code; +/* +/* const char *sys_exits_strerror(code) +/* int code; +/* +/* int sys_exits_softerror(code) +/* int code; +/* DESCRIPTION +/* This module interprets sendmail-compatible process exit status +/* codes. A default result is returned for other exit codes. +/* +/* SYS_EXITS_CODE() returns non-zero when the specified code +/* is a sendmail-compatible process exit status code. +/* +/* sys_exits_strerror() returns a descriptive text for the +/* specified sendmail-compatible status code. +/* +/* sys_exits_softerror() returns non-zero when the specified +/* sendmail-compatible status code corresponds to a recoverable error. +/* DIAGNOSTICS +/* Panic: invalid status code. Fatal error: out of memory. +/* 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 + +/* Global library. */ + +#include + +/* Application-specific. */ + +typedef struct { + int flags; /* non-zero if recoverable */ + int code; /* exit status code */ + const char *text; /* descriptive text */ +} SYS_EXITS_TABLE; + +static SYS_EXITS_TABLE sys_exits_table[] = { + 0, EX_USAGE, "command line usage error", + 0, EX_DATAERR, "data format error", + 0, EX_NOINPUT, "cannot open input", + 0, EX_NOUSER, "user unknown", + 0, EX_NOHOST, "host name unknown", + 0, EX_UNAVAILABLE, "service unavailable", + 0, EX_SOFTWARE, "internal software error", + 1, EX_OSERR, "system resource problem", + 0, EX_OSFILE, "critical OS file missing", + 0, EX_CANTCREAT, "can't create user output file", + 0, EX_IOERR, "input/output error", + 1, EX_TEMPFAIL, "temporary failure", + 0, EX_PROTOCOL, "remote error in protocol", + 0, EX_NOPERM, "permission denied", + 0, EX_CONFIG, "local configuration error", +}; + +/* sys_exits_strerror - map exit status to error string */ + +const char *sys_exits_strerror(int code) +{ + char *myname = "sys_exits_strerror"; + + if (!SYS_EXITS_CODE(code)) + msg_panic("%s: bad code: %d", myname, code); + + return (sys_exits_table[code - EX__BASE].text); +} + +/* sys_exits_softerror - determine if error is transient */ + +int sys_exits_softerror(int code) +{ + char *myname = "sys_exits_softerror"; + + if (!SYS_EXITS_CODE(code)) + msg_panic("%s: bad code: %d", myname, code); + + return (sys_exits_table[code - EX__BASE].flags); +} diff --git a/postfix/global/sys_exits.h b/postfix/global/sys_exits.h new file mode 100644 index 000000000..4b09cb3bb --- /dev/null +++ b/postfix/global/sys_exits.h @@ -0,0 +1,53 @@ +#ifndef _SYS_EXITS_H_INCLUDED_ +#define _SYS_EXITS_H_INCLUDED_ + +/*++ +/* NAME +/* sys_exits 3h +/* SUMMARY +/* sendmail-compatible exit status handling +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern const char *sys_exits_strerror(int); +extern int sys_exits_softerror(int); + +#define SYS_EXITS_CODE(n) ((n) >= EX__BASE && (n) <= EX__MAX) + +#define EX__BASE 64 /* base value for error messages */ + +#define EX_USAGE 64 /* command line usage error */ +#define EX_DATAERR 65 /* data format error */ +#define EX_NOINPUT 66 /* cannot open input */ +#define EX_NOUSER 67 /* addressee unknown */ +#define EX_NOHOST 68 /* host name unknown */ +#define EX_UNAVAILABLE 69 /* service unavailable */ +#define EX_SOFTWARE 70 /* internal software error */ +#define EX_OSERR 71 /* system error (e.g., can't fork) */ +#define EX_OSFILE 72 /* critical OS file missing */ +#define EX_CANTCREAT 73 /* can't create (user) output file */ +#define EX_IOERR 74 /* input/output error */ +#define EX_TEMPFAIL 75 /* temporary failure */ +#define EX_PROTOCOL 76 /* remote error in protocol */ +#define EX_NOPERM 77 /* permission denied */ +#define EX_CONFIG 78 /* configuration error */ + +#define EX__MAX 78 /* maximum listed value */ + +/* 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/timed_ipc.c b/postfix/global/timed_ipc.c new file mode 100644 index 000000000..588f3aa3a --- /dev/null +++ b/postfix/global/timed_ipc.c @@ -0,0 +1,76 @@ +/*++ +/* NAME +/* timed_ipc 3 +/* SUMMARY +/* enforce IPC timeout on stream +/* SYNOPSIS +/* #include +/* +/* void timed_ipc_setup(stream) +/* VSTREAM *stream; +/* DESCRIPTION +/* timed_ipc() enforces on the specified stream the timeout as +/* specified via the \fIipc_timeout\fR configuration parameter: +/* a read or write operation fails if it does not succeed within +/* \fIipc_timeout\fR seconds. This deadline exists as a safety +/* measure for communication between mail subsystem programs, +/* and should never be exceeded. +/* DIAGNOSTICS +/* Panic: sanity check failed. Fatal error: deadline exceeded. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include "mail_params.h" +#include "timed_ipc.h" + +/* timed_ipc_read - read with timeout */ + +static int timed_ipc_read(int fd, void *buf, unsigned len) +{ + if (read_wait(fd, var_ipc_timeout) < 0) + msg_fatal("timed_ipc_read: command read timeout"); + return (read(fd, buf, len)); +} + +/* timed_ipc_write - read with timeout */ + +static int timed_ipc_write(int fd, void *buf, unsigned len) +{ + if (write_wait(fd, var_ipc_timeout) < 0) + msg_fatal("timed_ipc_write: command write timeout"); + return (write(fd, buf, len)); +} + +/* timed_ipc_setup - enable ipc with timeout */ + +void timed_ipc_setup(VSTREAM *stream) +{ + if (var_ipc_timeout <= 0) + msg_panic("timed_ipc_setup: bad ipc_timeout %d", var_ipc_timeout); + + vstream_control(stream, + VSTREAM_CTL_READ_FN, timed_ipc_read, + VSTREAM_CTL_WRITE_FN, timed_ipc_write, + VSTREAM_CTL_END); +} diff --git a/postfix/global/timed_ipc.h b/postfix/global/timed_ipc.h new file mode 100644 index 000000000..5014b3cc0 --- /dev/null +++ b/postfix/global/timed_ipc.h @@ -0,0 +1,35 @@ +#ifndef _TIMED_IPC_H_INCLUDED_ +#define _TIMED_IPC_H_INCLUDED_ + +/*++ +/* NAME +/* timed_ipc 3h +/* SUMMARY +/* enforce IPC timeout on stream +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern void timed_ipc_setup(VSTREAM *); + +/* 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/tok822.h b/postfix/global/tok822.h new file mode 100644 index 000000000..316dfb198 --- /dev/null +++ b/postfix/global/tok822.h @@ -0,0 +1,117 @@ +#ifndef _TOK822_H_INCLUDED_ +#define _TOK822_H_INCLUDED_ + +/*++ +/* NAME +/* tok822 3h +/* SUMMARY +/* RFC822 token structures +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * Global library. + */ +#include + + /* + * Internal address representation: a token tree. + */ +typedef struct TOK822 { + int type; /* token value, see below */ + VSTRING *vstr; /* token contents */ + struct TOK822 *prev; /* peer */ + struct TOK822 *next; /* peer */ + struct TOK822 *head; /* group members */ + struct TOK822 *tail; /* group members */ + struct TOK822 *owner; /* group owner */ +} TOK822; + + /* + * Token values for multi-character objects. Single-character operators are + * represented by their own character value. + */ +#define TOK822_MINTOK 256 +#define TOK822_ATOM 256 /* non-special character sequence */ +#define TOK822_QSTRING 257 /* stuff between "", not nesting */ +#define TOK822_COMMENT 258 /* comment including (), may nest */ +#define TOK822_DOMLIT 259 /* stuff between [] not nesting */ +#define TOK822_ADDR 260 /* actually a token group */ +#define TOK822_STARTGRP 261 /* start of named group */ +#define TOK822_MAXTOK 261 + + /* + * tok822_node.c + */ +extern TOK822 *tok822_alloc(int, const char *); +extern TOK822 *tok822_free(TOK822 *); + + /* + * tok822_tree.c + */ +extern TOK822 *tok822_append(TOK822 *, TOK822 *); +extern TOK822 *tok822_prepend(TOK822 *, TOK822 *); +extern TOK822 *tok822_cut_before(TOK822 *); +extern TOK822 *tok822_cut_after(TOK822 *); +extern TOK822 *tok822_unlink(TOK822 *); +extern TOK822 *tok822_sub_append(TOK822 *, TOK822 *); +extern TOK822 *tok822_sub_prepend(TOK822 *, TOK822 *); +extern TOK822 *tok822_sub_keep_before(TOK822 *, TOK822 *); +extern TOK822 *tok822_sub_keep_after(TOK822 *, TOK822 *); +extern TOK822 *tok822_free_tree(TOK822 *); + +typedef int (*TOK822_ACTION) (TOK822 *); +extern int tok822_apply(TOK822 *, int, TOK822_ACTION); +extern TOK822 **tok822_grep(TOK822 *, int); + + /* + * tok822_parse.c + */ +extern TOK822 *tok822_scan(const char *, TOK822 **); +extern TOK822 *tok822_scan_addr(const char *); +extern TOK822 *tok822_parse(const char *); +extern VSTRING *tok822_externalize(VSTRING *, TOK822 *, int); +extern VSTRING *tok822_internalize(VSTRING *, TOK822 *, int); + +#define TOK822_STR_NONE (0) +#define TOK822_STR_WIPE (1<<0) +#define TOK822_STR_TERM (1<<1) +#define TOK822_STR_LINE (1<<2) +#define TOK822_STR_DEFL (TOK822_STR_WIPE | TOK822_STR_TERM) +#define TOK822_STR_HEAD (TOK822_STR_TERM | TOK822_STR_LINE) + + /* + * tok822_find.c + */ +extern TOK822 *tok822_find_type(TOK822 *, int); +extern TOK822 *tok822_rfind_type(TOK822 *, int); + + /* + * tok822_rewrite.c + */ +extern TOK822 *tok822_rewrite(TOK822 *, const char *); + + /* + * tok822_resolve.c + */ +extern void tok822_resolve(TOK822 *, RESOLVE_REPLY *); + +/* 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/tok822_find.c b/postfix/global/tok822_find.c new file mode 100644 index 000000000..c81564612 --- /dev/null +++ b/postfix/global/tok822_find.c @@ -0,0 +1,69 @@ +/*++ +/* NAME +/* tok822_find 3 +/* SUMMARY +/* token list search operators +/* SYNOPSIS +/* #include +/* +/* TOK822 *tok822_find_type(head, type) +/* TOK822 *head; +/* int type; +/* +/* TOK822 *tok822_rfind_type(tail, type) +/* TOK822 *tail; +/* int type; +/* DESCRIPTION +/* This module implements token list search operations. +/* +/* tok822_find_type() searches a list of tokens for the first +/* instance of the specified token type. The result is the +/* found token or a null pointer when the search failed. +/* +/* tok822_rfind_type() searches a list of tokens in reverse direction +/* for the first instance of the specified token type. The result +/* is the found token or a null pointer when the search failed. +/* 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 + +/* Global library. */ + +#include + +/* tok822_find_type - find specific token type, forward search */ + +TOK822 *tok822_find_type(TOK822 *head, int op) +{ + TOK822 *tp; + + for (tp = head; tp != 0 && tp->type != op; tp = tp->next) + /* void */ ; + return (tp); +} + +/* tok822_rfind_type - find specific token type, backward search */ + +TOK822 *tok822_rfind_type(TOK822 *tail, int op) +{ + TOK822 *tp; + + for (tp = tail; tp != 0 && tp->type != op; tp = tp->prev) + /* void */ ; + return (tp); +} diff --git a/postfix/global/tok822_node.c b/postfix/global/tok822_node.c new file mode 100644 index 000000000..f0456d03f --- /dev/null +++ b/postfix/global/tok822_node.c @@ -0,0 +1,76 @@ +/*++ +/* NAME +/* tok822_node 3 +/* SUMMARY +/* token memory management +/* SYNOPSIS +/* #include +/* +/* TOK822 *tok822_alloc(type, strval) +/* int type; +/* const char *strval; +/* +/* TOK822 *tok822_free(tp) +/* TOK822 *tp; +/* DESCRIPTION +/* This module implements memory management for token +/* structures. A distinction is made between single-character +/* tokens that have no string value, and string-valued tokens. +/* +/* tok822_alloc() allocates memory for a token structure of +/* the named type, and initializes it properly. In case of +/* a single-character token, no string memory is allocated. +/* Otherwise, \fIstrval\fR is a null pointer or provides +/* string data to initialize the token with. +/* +/* tok822_free() releases the memory used for the specified token +/* and conveniently returns a null pointer value. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "tok822.h" + +/* tok822_alloc - allocate and initialize token */ + +TOK822 *tok822_alloc(int type, const char *strval) +{ + TOK822 *tp; + + tp = (TOK822 *) mymalloc(sizeof(*tp)); + tp->type = type; + tp->next = tp->prev = tp->head = tp->tail = tp->owner = 0; + tp->vstr = (type < TOK822_MINTOK ? 0 : + strval == 0 ? vstring_alloc(10) : + vstring_strcpy(vstring_alloc(strlen(strval) + 1), strval)); + return (tp); +} + +/* tok822_free - destroy token */ + +TOK822 *tok822_free(TOK822 *tp) +{ + if (tp->vstr) + vstring_free(tp->vstr); + myfree((char *) tp); + return (0); +} diff --git a/postfix/global/tok822_parse.c b/postfix/global/tok822_parse.c new file mode 100644 index 000000000..798c096a5 --- /dev/null +++ b/postfix/global/tok822_parse.c @@ -0,0 +1,572 @@ +/*++ +/* NAME +/* tok822_parse 3 +/* SUMMARY +/* RFC 822 address parser +/* SYNOPSIS +/* #include +/* +/* TOK822 *tok822_scan(str, tailp) +/* const char *str; +/* TOK822 **tailp; +/* +/* TOK822 *tok822_parse(str) +/* const char *str; +/* +/* TOK822 *tok822_scan_addr(str) +/* const char *str; +/* +/* VSTRING *tok822_externalize(buffer, tree, flags) +/* VSTRING *buffer; +/* TOK822 *tree; +/* int flags; +/* +/* VSTRING *tok822_internalize(buffer, tree, flags) +/* VSTRING *buffer; +/* TOK822 *tree; +/* int flags; +/* DESCRIPTION +/* This module converts address lists between string form and parse +/* tree formats. The string form can appear in two different ways: +/* external (or quoted) form, as used in message headers, and internal +/* (unquoted) form, as used internally by the mail software. +/* Although RFC 822 expects 7-bit data, these routines pay no +/* special attention to 8-bit characters. +/* +/* tok822_scan() converts the external-form string in \fIstr\fR +/* to a linear token list. The \fItailp\fR argument is a null pointer +/* or receives the pointer value of the last result list element. +/* +/* tok822_parse() converts the external-form address list in +/* \fIstr\fR to the corresponding token tree. The parser is permissive +/* and will not throw away information that it does not understand. +/* The parser adds missing commas between addresses. +/* +/* tok822_scan_addr() converts the external-form string in +/* \fIstr\fR to an address token tree. This is just string to +/* token list conversion; no parsing is done. This routine is +/* suitable for data should contain just one address and no +/* other information. +/* +/* tok822_externalize() converts a token list to external form. +/* Where appropriate, characters and strings are quoted and white +/* space is inserted. The \fIflags\fR argument is the binary OR of +/* zero or more of the following: +/* .IP TOK822_STR_WIPE +/* Initially, truncate the result to zero length. +/* .IP TOK822_STR_TERM +/* Append a null terminator to the result when done. +/* .IP TOK822_STR_LINE +/* Append a line break after each comma token, instead of appending +/* whitespace. It is up to the caller to concatenate short lines to +/* produce longer ones. +/* .PP +/* The macro TOK_822_NONE expresses that none of the above features +/* should be activated. +/* +/* The macro TOK822_STR_DEFL combines the TOK822_STR_WIPE and +/* TOK822_STR_TERM flags. This is useful for most token to string +/* conversions. +/* +/* The macro TOK822_STR_HEAD combines the TOK822_STR_TERM and +/* TOK822_STR_LINE flags. This is useful for the special case of +/* token to mail header conversion. +/* +/* tok822_internalize() converts a token list to string form, +/* without quoting. White space is inserted where appropriate. +/* The \fIflags\fR argument is as with tok822_externalize(). +/* STANDARDS +/* .ad +/* .fi +/* RFC 822 (ARPA Internet Text Messages). In addition to this standard +/* this module implements additional operators such as % and !. These +/* are needed because the real world is not all RFC 822. Also, the ':' +/* operator is allowed to appear inside addresses, to accommodate DECnet. +/* In addition, 8-bit data is not given special treatment. +/* 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 + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "tok822.h" + + /* + * I suppose this is my favorite macro. Used heavily for tokenizing. + */ +#define COLLECT(t,s,c,cond) { \ + while ((c = *(unsigned char *) s) != 0) { \ + if (c == '\\') { \ + if ((c = *(unsigned char *)++s) == 0) \ + break; \ + } else if (!(cond)) { \ + break; \ + } \ + VSTRING_ADDCH(t->vstr, ISSPACE(c) ? ' ' : c); \ + s++; \ + } \ + VSTRING_TERMINATE(t->vstr); \ + } + +#define COLLECT_SKIP_LAST(t,s,c,cond) { COLLECT(t,s,c,cond); if (*s) s++; } + + /* + * Not quite as complex. The parser depends heavily on it. + */ +#define SKIP(tp, cond) { \ + while (tp->type && (cond)) \ + tp = tp->prev; \ + } + +#define MOVE_COMMENT_AND_CONTINUE(tp, right) { \ + TOK822 *prev = tok822_unlink(tp); \ + right = tok822_prepend(right, tp); \ + tp = prev; \ + continue; \ + } + +#define SKIP_MOVE_COMMENT(tp, cond, right) { \ + while (tp->type && (cond)) { \ + if (tp->type == TOK822_COMMENT) \ + MOVE_COMMENT_AND_CONTINUE(tp, right); \ + tp = tp->prev; \ + } \ + } + + /* + * Single-character operators. We include the % and ! operators because not + * all the world is RFC822. XXX Make this operator list configurable when we + * have a real rewriting language. + */ +static char tok822_opchar[] = "|\"(),.:;<>@[]%!"; + +static void tok822_quote_atom(TOK822 *); +static const char *tok822_comment(TOK822 *, const char *); +static TOK822 *tok822_group(int, TOK822 *, TOK822 *, int); +static void tok822_copy_quoted(VSTRING *, char *, char *); +static int tok822_append_space(TOK822 *); + +#define DO_WORD (1<<0) /* finding a word is ok here */ +#define DO_GROUP (1<<1) /* doing an address group */ + +#define ADD_COMMA ',' /* resynchronize */ +#define NO_MISSING_COMMA 0 + +/* tok822_internalize - token tree to string, internal form */ + +VSTRING *tok822_internalize(VSTRING *vp, TOK822 *tree, int flags) +{ + TOK822 *tp; + + if (flags & TOK822_STR_WIPE) + VSTRING_RESET(vp); + + for (tp = tree; tp; tp = tp->next) { + switch (tp->type) { + case ',': + VSTRING_ADDCH(vp, tp->type); + if (flags & TOK822_STR_LINE) { + VSTRING_ADDCH(vp, '\n'); + continue; + } + break; + case TOK822_ADDR: + tok822_internalize(vp, tp->head, TOK822_STR_NONE); + break; + case TOK822_ATOM: + case TOK822_COMMENT: + case TOK822_QSTRING: + vstring_strcat(vp, vstring_str(tp->vstr)); + break; + case TOK822_DOMLIT: + VSTRING_ADDCH(vp, '['); + vstring_strcat(vp, vstring_str(tp->vstr)); + VSTRING_ADDCH(vp, ']'); + break; + case TOK822_STARTGRP: + VSTRING_ADDCH(vp, ':'); + break; + default: + if (tp->type >= TOK822_MINTOK) + msg_panic("tok822_internalize: unknown operator %d", tp->type); + VSTRING_ADDCH(vp, tp->type); + } + if (tok822_append_space(tp)) + VSTRING_ADDCH(vp, ' '); + } + if (flags & TOK822_STR_TERM) + VSTRING_TERMINATE(vp); + return (vp); +} + +/* tok822_externalize - token tree to string, external form */ + +VSTRING *tok822_externalize(VSTRING *vp, TOK822 *tree, int flags) +{ + TOK822 *tp; + + if (flags & TOK822_STR_WIPE) + VSTRING_RESET(vp); + + for (tp = tree; tp; tp = tp->next) { + switch (tp->type) { + case ',': + VSTRING_ADDCH(vp, tp->type); + if (flags & TOK822_STR_LINE) { + VSTRING_ADDCH(vp, '\n'); + continue; + } + break; + case TOK822_ADDR: + tok822_externalize(vp, tp->head, TOK822_STR_NONE); + break; + case TOK822_ATOM: + vstring_strcat(vp, vstring_str(tp->vstr)); + break; + case TOK822_COMMENT: + tok822_copy_quoted(vp, vstring_str(tp->vstr), "\\\r\n"); + break; + case TOK822_QSTRING: + VSTRING_ADDCH(vp, '"'); + tok822_copy_quoted(vp, vstring_str(tp->vstr), "\"\\\r\n"); + VSTRING_ADDCH(vp, '"'); + break; + case TOK822_DOMLIT: + VSTRING_ADDCH(vp, '['); + tok822_copy_quoted(vp, vstring_str(tp->vstr), "\"\\\r\n"); + VSTRING_ADDCH(vp, ']'); + break; + case TOK822_STARTGRP: + VSTRING_ADDCH(vp, ':'); + break; + default: + if (tp->type >= TOK822_MINTOK) + msg_panic("tok822_externalize: unknown operator %d", tp->type); + VSTRING_ADDCH(vp, tp->type); + } + if (tok822_append_space(tp)) + VSTRING_ADDCH(vp, ' '); + } + if (flags & TOK822_STR_TERM) + VSTRING_TERMINATE(vp); + return (vp); +} + +/* tok822_copy_quoted - copy a string while quoting */ + +static void tok822_copy_quoted(VSTRING *vp, char *str, char *quote_set) +{ + int ch; + + while ((ch = *(unsigned char *) str++) != 0) { + if (strchr(quote_set, ch)) + VSTRING_ADDCH(vp, '\\'); + VSTRING_ADDCH(vp, ch); + } +} + +/* tok822_append_space - see if space is needed after this token */ + +static int tok822_append_space(TOK822 *tp) +{ + TOK822 *next; + + if (tp == 0 || (next = tp->next) == 0 || tp->owner != 0) + return (0); + if (tp->type == ',' || tp->type == TOK822_STARTGRP || next->type == '<') + return (1); + +#define NON_OPERATOR(x) \ + (x->type == TOK822_ATOM || x->type == TOK822_QSTRING \ + || x->type == TOK822_COMMENT || x->type == TOK822_DOMLIT \ + || x->type == TOK822_ADDR) + + return (NON_OPERATOR(tp) && NON_OPERATOR(next)); +} + +/* tok822_scan - tokenize string */ + +TOK822 *tok822_scan(const char *str, TOK822 **tailp) +{ + TOK822 *head = 0; + TOK822 *tail = 0; + TOK822 *tp; + int ch; + + while ((ch = *(unsigned char *) str++) != 0) { + if (ISSPACE(ch)) + continue; + if (ch == '(') { + tp = tok822_alloc(TOK822_COMMENT, (char *) 0); + VSTRING_ADDCH(tp->vstr, ch); + str = tok822_comment(tp, str); + } else if (ch == '[') { + tp = tok822_alloc(TOK822_DOMLIT, (char *) 0); + COLLECT_SKIP_LAST(tp, str, ch, ch != ']'); + } else if (ch == '"') { + tp = tok822_alloc(TOK822_QSTRING, (char *) 0); + COLLECT_SKIP_LAST(tp, str, ch, ch != '"'); + } else if (strchr(tok822_opchar, ch)) { + tp = tok822_alloc(ch, (char *) 0); + } else { + tp = tok822_alloc(TOK822_ATOM, (char *) 0); + VSTRING_ADDCH(tp->vstr, ch); + COLLECT(tp, str, ch, !ISSPACE(ch) && !strchr(tok822_opchar, ch)); + tok822_quote_atom(tp); + } + tail = (head == 0 ? head = tp : tok822_append(tail, tp)); + } + if (tailp) + *tailp = tail; + return (head); +} + +/* tok822_parse - translate external string to token tree */ + +TOK822 *tok822_parse(const char *str) +{ + TOK822 *head; + TOK822 *tail; + TOK822 *right; + TOK822 *first_token; + TOK822 *last_token; + TOK822 *tp; + int state; + + /* + * First, tokenize the string, from left to right. We are not allowed to + * throw away any information that we do not understand. With a flat + * token list that contains all tokens, we can always convert back to + * string form. + */ + if ((first_token = tok822_scan(str, &last_token)) == 0) + return (0); + + /* + * For convenience, sandwich the token list between two sentinel tokens. + */ +#define GLUE(left,rite) { left->next = rite; rite->prev = left; } + + head = tok822_alloc(0, (char *) 0); + GLUE(head, first_token); + tail = tok822_alloc(0, (char *) 0); + GLUE(last_token, tail); + + /* + * Next step is to transform the token list into a parse tree. This is + * done most conveniently from right to left. If there is something that + * we do not understand, just leave it alone, don't throw it away. The + * address information that we're looking for sits in-between the current + * node (tp) and the one called right. Add missing commas on the fly. + */ + state = DO_WORD; + right = tail; + tp = tail->prev; + while (tp->type) { + if (tp->type == TOK822_COMMENT) { /* move comment to the side */ + MOVE_COMMENT_AND_CONTINUE(tp, right); + } else if (tp->type == ';') { /* rh side of named group */ + right = tok822_group(TOK822_ADDR, tp, right, ADD_COMMA); + state = DO_GROUP | DO_WORD; + } else if (tp->type == ':' && (state & DO_GROUP) != 0) { + tp->type = TOK822_STARTGRP; + (void) tok822_group(TOK822_ADDR, tp, right, NO_MISSING_COMMA); + SKIP(tp, tp->type != ','); + right = tp; + continue; + } else if (tp->type == '>') { /* rh side of */ + right = tok822_group(TOK822_ADDR, tp, right, ADD_COMMA); + SKIP_MOVE_COMMENT(tp, tp->type != '<', right); + (void) tok822_group(TOK822_ADDR, tp, right, NO_MISSING_COMMA); + SKIP(tp, tp->type > 0xff || strchr(">;,:", tp->type) == 0); + right = tp; + state |= DO_WORD; + continue; + } else if (tp->type == TOK822_ATOM || tp->type == TOK822_QSTRING + || tp->type == TOK822_DOMLIT) { + if ((state & DO_WORD) == 0) + right = tok822_group(TOK822_ADDR, tp, right, ADD_COMMA)->next; + state &= ~DO_WORD; + } else if (tp->type == ',') { + right = tok822_group(TOK822_ADDR, tp, right, NO_MISSING_COMMA); + state |= DO_WORD; + } else { + state |= DO_WORD; + } + tp = tp->prev; + } + (void) tok822_group(TOK822_ADDR, tp, right, NO_MISSING_COMMA); + + /* + * Discard the sentinel tokens on the left and right extremes. Properly + * terminate the resulting list. + */ + tp = (head->next != tail ? head->next : 0); + tok822_cut_before(head->next); + tok822_free(head); + tok822_cut_before(tail); + tok822_free(tail); + return (tp); +} + +/* tok822_quote_atom - see if an atom needs quoting when externalized */ + +static void tok822_quote_atom(TOK822 *tp) +{ + char *cp; + int ch; + + /* + * RFC 822 expects 7-bit data. Rather than quoting every 8-bit character + * (and still passing it on as 8-bit data) we leave 8-bit data alone. + */ + for (cp = vstring_str(tp->vstr); (ch = *(unsigned char *) cp) != 0; cp++) { + if ( /* !ISASCII(ch) || */ ISSPACE(ch) + || ISCNTRL(ch) || strchr(tok822_opchar, ch)) { + tp->type = TOK822_QSTRING; + break; + } + } +} + +/* tok822_comment - tokenize comment */ + +const char *tok822_comment(TOK822 *tp, const char *str) +{ + int ch; + + while ((ch = *(unsigned char *) str) != 0) { + VSTRING_ADDCH(tp->vstr, ISSPACE(ch) ? ' ' : ch); + str++; + if (ch == '(') { /* comments can nest! */ + str = tok822_comment(tp, str); + } else if (ch == ')') { + break; + } else if (ch == '\\') { + vstring_truncate(tp->vstr, VSTRING_LEN(tp->vstr) - 1); + if ((ch = *(unsigned char *) str) == 0) + break; + VSTRING_ADDCH(tp->vstr, ch); + str++; + } + } + VSTRING_TERMINATE(tp->vstr); + return (str); +} + +/* tok822_group - cluster a group of tokens */ + +TOK822 *tok822_group(int group_type, TOK822 *left, TOK822 *right, int sync_type) +{ + TOK822 *group; + TOK822 *sync; + TOK822 *first; + + /* + * Cluster the tokens between left and right under their own parse tree + * node. Optionally insert a resync token. + */ + if (left != right && (first = left->next) != right) { + tok822_cut_before(right); + tok822_cut_before(first); + group = tok822_alloc(group_type, (char *) 0); + tok822_sub_append(group, first); + tok822_append(left, group); + tok822_append(group, right); + if (sync_type) { + sync = tok822_alloc(sync_type, (char *) 0); + tok822_append(left, sync); + } + } + return (left); +} + +/* tok822_scan_addr - convert external address string to address token */ + +TOK822 *tok822_scan_addr(const char *addr) +{ + TOK822 *tree = tok822_alloc(TOK822_ADDR, (char *) 0); + + tree->head = tok822_scan(addr, &tree->tail); + return (tree); +} + +#ifdef TEST + +#include +#include + +/* tok822_print - display token */ + +static void tok822_print(TOK822 *tp, int indent) +{ + if (tp->type < TOK822_MINTOK) { + vstream_printf("%*s %s \"%c\"\n", indent, "", "OP", tp->type); + } else if (tp->type == TOK822_ADDR) { + vstream_printf("%*s %s\n", indent, "", "address"); + } else { + vstream_printf("%*s %s \"%s\"\n", indent, "", + tp->type == TOK822_COMMENT ? "comment" : + tp->type == TOK822_ATOM ? "atom" : + tp->type == TOK822_QSTRING ? "quoted string" : + tp->type == TOK822_DOMLIT ? "domain literal" : + tp->type == TOK822_ADDR ? "address" : + "unknown\n", vstring_str(tp->vstr)); + } +} + +int main(int unused_argc, char **unused_argv) +{ + VSTRING *vp = vstring_alloc(100); + TOK822 *list; + TOK822 *tp; + TOK822 *ap; + int indent = 0; + VSTRING *buf = vstring_alloc(100); + + while (vstring_fgets(buf, VSTREAM_IN)) { + list = tok822_parse(vstring_str(buf)); + for (tp = list; tp; tp = tp->next) { + tok822_print(tp, indent); + if (tp->type == TOK822_ADDR) { + indent += 2; + for (ap = tp->head; ap; ap = ap->next) + tok822_print(ap, indent); + indent -= 2; + } + } + vstream_printf("\n"); + + vstream_printf("Internalized:\n%s\n\n", + vstring_str(tok822_internalize(vp, list, TOK822_STR_DEFL))); + vstream_printf("Externalized, no newlines inserted:\n%s\n\n", + vstring_str(tok822_externalize(vp, list, TOK822_STR_DEFL))); + vstream_printf("Externalized, newlines inserted:\n%s\n\n", + vstring_str(tok822_externalize(vp, list, + TOK822_STR_DEFL | TOK822_STR_LINE))); + vstream_fflush(VSTREAM_OUT); + tok822_free_tree(list); + } + vstring_free(vp); + vstring_free(buf); +} + +#endif diff --git a/postfix/global/tok822_resolve.c b/postfix/global/tok822_resolve.c new file mode 100644 index 000000000..ae491ff2d --- /dev/null +++ b/postfix/global/tok822_resolve.c @@ -0,0 +1,64 @@ +/*++ +/* NAME +/* tok822_resolve 3 +/* SUMMARY +/* address resolving, client interface +/* SYNOPSIS +/* #include +/* +/* void tok822_resolve(addr, reply) +/* TOK822 *addr; +/* RESOLVE_REPLY *reply; +/* DESCRIPTION +/* tok822_resolve() takes an address token tree and finds out the +/* transport to deliver via, the next-hop host on that transport, +/* and the recipient relative to that host. +/* SEE ALSO +/* resolve_clnt(3) basic resolver client interface +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "resolve_clnt.h" +#include "tok822.h" + +/* tok822_resolve - address rewriting interface */ + +void tok822_resolve(TOK822 *addr, RESOLVE_REPLY *reply) +{ + VSTRING *intern_form = vstring_alloc(100); + + if (addr->type != TOK822_ADDR) + msg_panic("tok822_resolve: non-address token type: %d", addr->type); + + /* + * Internalize the token tree and ship it to the resolve service. + * Shipping string forms is much simpler than shipping parse trees. + */ + tok822_internalize(intern_form, addr->head, TOK822_STR_DEFL); + resolve_clnt_query(vstring_str(intern_form), reply); + if (msg_verbose) + msg_info("tok822_resolve: addr=%s -> chan=%s, host=%s, rcpt=%s", + vstring_str(intern_form), vstring_str(reply->transport), + vstring_str(reply->nexthop), vstring_str(reply->recipient)); + + vstring_free(intern_form); +} diff --git a/postfix/global/tok822_rewrite.c b/postfix/global/tok822_rewrite.c new file mode 100644 index 000000000..fd52abbf3 --- /dev/null +++ b/postfix/global/tok822_rewrite.c @@ -0,0 +1,68 @@ +/*++ +/* NAME +/* tok822_rewrite 3 +/* SUMMARY +/* address rewriting, client interface +/* SYNOPSIS +/* #include +/* +/* TOK822 *tok822_rewrite(addr, how) +/* TOK822 *addr; +/* char *how; +/* DESCRIPTION +/* tok822_rewrite() takes an address token tree and transforms +/* it according to the rule set specified via \fIhow\fR. The +/* result is the \fIaddr\fR argument. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "rewrite_clnt.h" +#include "tok822.h" + +/* tok822_rewrite - address rewriting interface */ + +TOK822 *tok822_rewrite(TOK822 *addr, const char *how) +{ + VSTRING *input_ext_form = vstring_alloc(100); + VSTRING *canon_ext_form = vstring_alloc(100); + + if (addr->type != TOK822_ADDR) + msg_panic("tok822_rewrite: non-address token type: %d", addr->type); + + /* + * Externalize the token tree, ship it to the rewrite service, and parse + * the result. Shipping external form is much simpler than shipping parse + * trees. + */ + tok822_externalize(input_ext_form, addr->head, TOK822_STR_DEFL); + if (msg_verbose) + msg_info("tok822_rewrite: input: %s", vstring_str(input_ext_form)); + rewrite_clnt(how, vstring_str(input_ext_form), canon_ext_form); + if (msg_verbose) + msg_info("tok822_rewrite: result: %s", vstring_str(canon_ext_form)); + tok822_free_tree(addr->head); + addr->head = tok822_scan(vstring_str(canon_ext_form), &addr->tail); + + vstring_free(input_ext_form); + vstring_free(canon_ext_form); + return (addr); +} diff --git a/postfix/global/tok822_tree.c b/postfix/global/tok822_tree.c new file mode 100644 index 000000000..16cec946a --- /dev/null +++ b/postfix/global/tok822_tree.c @@ -0,0 +1,307 @@ +/*++ +/* NAME +/* tok822_tree 3 +/* SUMMARY +/* assorted token tree operators +/* SYNOPSIS +/* #include +/* +/* TOK822 *tok822_append(t1, t2) +/* TOK822 *t1; +/* TOK822 *t2; +/* +/* TOK822 *tok822_prepend(t1, t2) +/* TOK822 *t1; +/* TOK822 *t2; +/* +/* TOK822 *tok822_cut_before(tp) +/* TOK822 *tp; +/* +/* TOK822 *tok822_cut_after(tp) +/* TOK822 *tp; +/* +/* TOK822 *tok822_unlink(tp) +/* TOK822 *tp; +/* +/* TOK822 *tok822_sub_append(t1, t2) +/* TOK822 *t1; +/* +/* TOK822 *tok822_sub_prepend(t1, t2) +/* TOK822 *t1; +/* TOK822 *t2; +/* +/* TOK822 *tok822_sub_keep_before(t1, t2) +/* TOK822 *tp; +/* +/* TOK822 *tok822_sub_keep_after(t1, t2) +/* TOK822 *tp; +/* +/* int tok822_apply(list, type, action) +/* TOK822 *list; +/* int type; +/* int (*action)(TOK822 *token); +/* +/* int tok822_grep(list, type) +/* TOK822 *list; +/* int type; +/* +/* TOK822 *tok822_free_tree(tp) +/* TOK822 *tp; +/* DESCRIPTION +/* This module manipulates trees of token structures. Trees grow +/* to the right or downwards. Operators are provided to cut and +/* combine trees in various manners. +/* +/* tok822_append() appends the token list \fIt2\fR to the right +/* of token list \fIt1\fR. The result is the last token in \fIt2\fR. +/* The appended list inherits the \fIowner\fR attribute from \fIt1\fR. +/* The parent node, if any, is not updated. +/* +/* tok822_prepend() inserts the token list \fIt2\fR to the left +/* of token \fIt1\fR. The result is the last token in \fIt2\fR. +/* The appended list inherits the \fIowner\fR attribute from \fIt1\fR. +/* The parent node, if any, is not updated. +/* +/* tok822_cut_before() breaks a token list on the left side of \fItp\fR +/* and returns the left neighbor of \tItp\fR. +/* +/* tok822_cut_after() breaks a token list on the right side of \fItp\fR +/* and returns the right neighbor of \tItp\fR. +/* +/* tok822_unlink() disconnects a token from its left and right neighbors +/* and returns the left neighbor of \tItp\fR. +/* +/* tok822_sub_append() appends the token list \fIt2\fR to the right +/* of the token list below \fIt1\fR. The result is the last token +/* in \fIt2\fR. +/* +/* tok822_sub_prepend() prepends the token list \fIt2\fR to the left +/* of the token list below \fIt1\fR. The result is the last token +/* in \fIt2\fR. +/* +/* tok822_sub_keep_before() keeps the token list below \fIt1\fR on the +/* left side of \fIt2\fR and returns the tail of the disconnected list. +/* +/* tok822_sub_keep_after() keeps the token list below \fIt1\fR on the +/* right side of \fIt2\fR and returns the head of the disconnected list. +/* +/* tok822_apply() applies the specified action routine to all tokens +/* matching the given type (to all tokens when a null type is given). +/* Processing terminates when the action routine returns a non-zero +/* value. The result is the last result returned by the action routine. +/* tok822_apply() does not traverse vertical links. +/* +/* tok822_grep() returns a null-terminated array of pointers to tokens +/* matching the specified type (all tokens when a null type is given). +/* tok822_grep() does not traverse vertical links. The result must be +/* given to myfree(). +/* +/* tok822_free_tree() destroys a tree of token structures and +/* conveniently returns a null pointer. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include "tok822.h" + +/* tok822_append - insert token list, return end of inserted list */ + +TOK822 *tok822_append(TOK822 *t1, TOK822 *t2) +{ + TOK822 *next = t1->next; + + t1->next = t2; + t2->prev = t1; + + t2->owner = t1->owner; + while (t2->next) + (t2 = t2->next)->owner = t1->owner; + + t2->next = next; + if (next) + next->prev = t2; + return (t2); +} + +/* tok822_prepend - insert token list, return end of inserted list */ + +TOK822 *tok822_prepend(TOK822 *t1, TOK822 *t2) +{ + TOK822 *prev = t1->prev; + + if (prev) + prev->next = t2; + t2->prev = prev; + + t2->owner = t1->owner; + while (t2->next) + (t2 = t2->next)->owner = t1->owner; + + t2->next = t1; + t1->prev = t2; + return (t2); +} + +/* tok822_cut_before - split list before token, return predecessor token */ + +TOK822 *tok822_cut_before(TOK822 *tp) +{ + TOK822 *prev = tp->prev; + + if (prev) { + prev->next = 0; + tp->prev = 0; + } + return (prev); +} + +/* tok822_cut_after - split list after token, return successor token */ + +TOK822 *tok822_cut_after(TOK822 *tp) +{ + TOK822 *next = tp->next; + + if (next) { + next->prev = 0; + tp->next = 0; + } + return (next); +} + +/* tok822_unlink - take token away from list, return predecessor token */ + +TOK822 *tok822_unlink(TOK822 *tp) +{ + TOK822 *prev = tp->prev; + TOK822 *next = tp->next; + + if (prev) + prev->next = next; + if (next) + next->prev = prev; + tp->prev = tp->next = 0; + return (prev); +} + +/* tok822_sub_append - append sublist, return end of appended list */ + +TOK822 *tok822_sub_append(TOK822 *t1, TOK822 *t2) +{ + if (t1->head) { + return (t1->tail = tok822_append(t1->tail, t2)); + } else { + t1->head = t2; + while (t2->next) + (t2 = t2->next)->owner = t1; + return (t1->tail = t2); + } +} + +/* tok822_sub_prepend - prepend sublist, return end of prepended list */ + +TOK822 *tok822_sub_prepend(TOK822 *t1, TOK822 *t2) +{ + TOK822 *tp; + + if (t1->head) { + tp = tok822_prepend(t1->head, t2); + t1->head = t2; + return (tp); + } else { + t1->head = t2; + while (t2->next) + (t2 = t2->next)->owner = t1; + return (t1->tail = t2); + } +} + +/* tok822_sub_keep_before - cut sublist, return tail of disconnected list */ + +TOK822 *tok822_sub_keep_before(TOK822 *t1, TOK822 *t2) +{ + TOK822 *tail = t1->tail; + + if ((t1->tail = tok822_cut_before(t2)) == 0) + t1->head = 0; + return (tail); +} + +/* tok822_sub_keep_after - cut sublist, return head of disconnected list */ + +TOK822 *tok822_sub_keep_after(TOK822 *t1, TOK822 *t2) +{ + TOK822 *head = t1->head; + + if ((t1->head = tok822_cut_after(t2)) == 0) + t1->tail = 0; + return (head); +} + +/* tok822_free_tree - destroy token tree */ + +TOK822 *tok822_free_tree(TOK822 *tp) +{ + if (tp) { + if (tp->next) + tok822_free_tree(tp->next); + if (tp->head) + tok822_free_tree(tp->head); + tok822_free(tp); + } + return (0); +} + +/* tok822_apply - apply action to specified tokens */ + +int tok822_apply(TOK822 *tree, int type, TOK822_ACTION action) +{ + TOK822 *tp; + int result = 0; + + for (tp = tree; tp; tp = tp->next) { + if (type == 0 || tp->type == type) + if ((result = action(tp)) != 0) + break; + } + return (result); +} + +/* tok822_grep - list matching tokens */ + +TOK822 **tok822_grep(TOK822 *tree, int type) +{ + TOK822 **list; + TOK822 *tp; + int count; + + for (count = 0, tp = tree; tp; tp = tp->next) + if (type == 0 || tp->type == type) + count++; + + list = (TOK822 **) mymalloc(sizeof(*list) * (count + 1)); + + for (count = 0, tp = tree; tp; tp = tp->next) + if (type == 0 || tp->type == type) + list[count++] = tp; + + list[count] = 0; + return (list); +} diff --git a/postfix/html/Makefile.in b/postfix/html/Makefile.in new file mode 100644 index 000000000..2715e7b0c --- /dev/null +++ b/postfix/html/Makefile.in @@ -0,0 +1,116 @@ +# For now, just hard-coded rules for daemons, commands, config files. + +DAEMONS = bounce.8.html cleanup.8.html defer.8.html local.8.html \ + master.8.html pickup.8.html pipe.8.html qmgr.8.html showq.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 +CONFIG = access.5.html aliases.5.html canonical.5.html relocated.5.html \ + transport.5.html virtual.5.html + +update: $(DAEMONS) $(COMMANDS) $(CONFIG) + +Makefile: Makefile.in + (set -e; echo "# DO NOT EDIT"; $(OPTS) sh ../makedefs; cat $?) >$@ + +clean: + echo clean + +tidy: clean + +clobber: + rm -f $(DAEMONS) $(COMMANDS) $(CONFIG) + +bounce.8.html: ../bounce/bounce.c + srctoman $? | nroff -man | man2html | postlink >$@ + +defer.8.html: bounce.8.html + rm -f $@ + ln -s $? $@ + +cleanup.8.html: ../cleanup/cleanup.c + srctoman $? | nroff -man | man2html | postlink >$@ + +local.8.html: ../local/local.c + srctoman $? | nroff -man | man2html | postlink >$@ + +master.8.html: ../master/master.c + srctoman $? | nroff -man | man2html | postlink >$@ + +pickup.8.html: ../pickup/pickup.c + srctoman $? | nroff -man | man2html | postlink >$@ + +pipe.8.html: ../pipe/pipe.c + srctoman $? | nroff -man | man2html | postlink >$@ + +qmgr.8.html: ../qmgr/qmgr.c + srctoman $? | nroff -man | man2html | postlink >$@ + +showq.8.html: ../showq/showq.c + srctoman $? | nroff -man | man2html | postlink >$@ + +smtp.8.html: ../smtp/smtp.c + srctoman $? | nroff -man | man2html | postlink >$@ + +smtpd.8.html: ../smtpd/smtpd.c + srctoman $? | nroff -man | man2html | postlink >$@ + +trivial-rewrite.8.html: ../trivial-rewrite/trivial-rewrite.c + srctoman $? | nroff -man | man2html | postlink >$@ + +postalias.1.html: ../postalias/postalias.c + srctoman $? | nroff -man | man2html | postlink >$@ + +postcat.1.html: ../postcat/postcat.c + srctoman $? | nroff -man | man2html | postlink >$@ + +postconf.1.html: ../postconf/postconf.c + srctoman $? | nroff -man | man2html | postlink >$@ + +postdrop.1.html: ../postdrop/postdrop.c + srctoman $? | nroff -man | man2html | postlink >$@ + +postfix.1.html: ../postfix/postfix.c + srctoman $? | nroff -man | man2html | postlink >$@ + +postkick.1.html: ../postkick/postkick.c + srctoman $? | nroff -man | man2html | postlink >$@ + +postlock.1.html: ../postlock/postlock.c + srctoman $? | nroff -man | man2html | postlink >$@ + +postlog.1.html: ../postlog/postlog.c + srctoman $? | nroff -man | man2html | postlink >$@ + +postmap.1.html: ../postmap/postmap.c + srctoman $? | nroff -man | man2html | postlink >$@ + +sendmail.1.html: ../sendmail/sendmail.c + srctoman $? | nroff -man | man2html | postlink >$@ + +mailq.1.html: sendmail.1.html + rm -f $@ + ln -s $? $@ + +newaliases.1.html: sendmail.1.html + rm -f $@ + ln -s $? $@ + +access.5.html: ../conf/access + srctoman - $? | nroff -man | man2html | postlink >$@ + +aliases.5.html: ../conf/aliases + srctoman - $? | nroff -man | man2html | postlink >$@ + +canonical.5.html: ../conf/canonical + srctoman - $? | nroff -man | man2html | postlink >$@ + +relocated.5.html: ../conf/relocated + srctoman - $? | nroff -man | man2html | postlink >$@ + +transport.5.html: ../conf/transport + srctoman - $? | nroff -man | man2html | postlink >$@ + +virtual.5.html: ../conf/virtual + srctoman - $? | nroff -man | man2html | postlink >$@ diff --git a/postfix/html/access.5.html b/postfix/html/access.5.html new file mode 100644 index 000000000..78284a568 --- /dev/null +++ b/postfix/html/access.5.html @@ -0,0 +1,134 @@ +
+
+
+
+ACCESS(5)                                               ACCESS(5)
+
+
+NAME
+       access - format of Postfix access table
+
+SYNOPSIS
+       postmap /etc/postfix/access
+
+DESCRIPTION
+       The  optional access table directs the Postfix SMTP server
+       to selectively reject or accept mail from or  to  specific
+       hosts,   domains,   networks,   host   addresses  or  mail
+       addresses.
+
+       The table serves as input to the postmap(1)  command.  The
+       result,  an  indexed file in dbm or db format, is used for
+       fast searching by the mail system. After an update it  may
+       take  a  minute  or  so before the change becomes visible.
+       Issue a postfix reload command to eliminate the delay.
+
+       The format of the access table is as follows:
+
+       blanks and comments
+              Blank lines are ignored,  as  are  lines  beginning
+              with `#'.
+
+       pattern action
+              When pattern matches a mail address, domain or host
+              address, perform the corresponding action.
+
+PATTERNS
+       Patterns are tried in the order as listed below:
+
+       user@domain
+              Matches the specified mail address.
+
+       domain.name
+              Matches the domain.name itself  and  any  subdomain
+              thereof,  either in hostnames or in mail addresses.
+              Top-level domains will never be matched.
+
+       user@  Matches all mail addresses with the specified  user
+              part.
+
+       net.work.addr.ess
+
+       net.work.addr
+
+       net.work
+
+       net    Matches  any host address in the specified network.
+              A network address is a  sequence  of  one  or  more
+              octets separated by ".".
+
+ACTIONS
+
+
+
+
+                                                                1
+
+
+
+
+
+ACCESS(5)                                               ACCESS(5)
+
+
+       [45]XX text
+              Reject  the  address etc. that matches the pattern,
+              and respond with the numerical code and text.
+
+       REJECT Reject the address etc. that matches the pattern. A
+              generic error response message is generated.
+
+       OK
+
+       Any other text
+              Accept the address etc. that matches the pattern.
+
+BUGS
+       The  table format does not understand quoting conventions.
+
+SEE ALSO
+       postmap(1) create mapping table
+       smtpd(8) smtp server
+
+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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+                                                                2
+
+
+
diff --git a/postfix/html/aliases.5.html b/postfix/html/aliases.5.html new file mode 100644 index 000000000..9f52d1656 --- /dev/null +++ b/postfix/html/aliases.5.html @@ -0,0 +1,200 @@ +
+
+
+
+ALIASES(5)                                             ALIASES(5)
+
+
+NAME
+       aliases - format of the Postfix alias database
+
+SYNOPSIS
+       postalias [-c config_dir] [-v] [file_type:]input_file
+
+DESCRIPTION
+       The aliases file provides a system-wide mechanism to redi-
+       rect mail for local recipients.
+
+       The file serves as input to the postalias(1) command.  The
+       result,  an  indexed file in dbm or db format, is used for
+       fast lookup by the mail system. After  an  update  it  may
+       take  a  minute  or  so before the change becomes visible.
+       Issue a postfix reload command to eliminate the delay.
+
+       The input and output file formats are expected to be  com-
+       patible  with  Sendmail  version 8, and are expected to be
+       suitable for the use as NIS maps.
+
+       Users can control delivery of their own mail by setting up
+       .forward files in their home directory.  Lines in per-user
+       .forward files have the same syntax as the right-hand side
+       of aliases entries.
+
+       The format of the alias database input file is as follows:
+
+       o      An alias definition has the form
+
+                   name: value1, value2, ...
+
+       o      Lines that begin with whitespace continue the  pre-
+              vious line.
+
+       o      Blank  lines  are  ignored,  as are lines beginning
+              with `#'.
+
+       The name is a local address (no domain part).  Use  double
+       quotes  when the name contains any special characters such
+       as whitespace, `#', `:', or `@'. The  name  is  folded  to
+       lowercase, in order to make database lookups case insensi-
+       tive.
+
+       In addition, when an alias exists for owner-name, delivery
+       diagnostics  are  directed  to that address, instead of to
+       the originator.  This is typically used to direct delivery
+       errors  to the owner of a mailing list, who is in a better
+       position to deal with mailing list delivery problems  than
+       the originator of the undelivered mail.
+
+       The value contains one or more of the following:
+
+       address
+              Mail  is  forwarded to address, which is compatible
+
+
+
+                                                                1
+
+
+
+
+
+ALIASES(5)                                             ALIASES(5)
+
+
+              with the RFC 822 standard.
+
+       /file/name
+              Mail is appended to /file/name.  See  local(8)  for
+              details  of delivery to file.  Delivery is not lim-
+              ited to regular files.  For example, to dispose  of
+              unwanted mail, deflect it to /dev/null.
+
+       |command
+              Mail  is  piped into command. Commands that contain
+              special characters, such as whitespace,  should  be
+              enclosed  between  double  quotes. See local(8) for
+              details of delivery to command.
+
+              When the command fails, a limited amount of command
+              output  is  mailed  back  to  the sender.  The file
+              /usr/include/sysexits.h defines the  expected  exit
+              status  codes. For example, use |"exit 67" to simu-
+              late a "user  unknown"  error,  and  |"exit  0"  to
+              implement an expensive black hole.
+
+       :include:/file/name
+              Mail  is  sent  to  the  destinations listed in the
+              named file.  Lines in :include: files have the same
+              syntax as the right-hand side of alias entries.
+
+              A  destination  can  be  any  destination  that  is
+              described in this manual page. However, delivery to
+              "|command" and /file/name is disallowed by default.
+              To  enable,  edit  the  allow_mail_to_commands  and
+              allow_mail_to_files configuration parameters.
+
+ADDRESS EXTENSION
+       When alias database search fails, and the recipient local-
+       part contains  the  optional  recipient  delimiter  (e.g.,
+       user+foo),  the  search  is  repeated  for  the unextended
+       address (e.g., user).
+
+CONFIGURATION PARAMETERS
+       The following main.cf parameters are  especially  relevant
+       to  this  topic.  See  the Postfix main.cf file for syntax
+       details and for default values.  Use  the  postfix  reload
+       command after a configuration change.
+
+       alias_maps
+              List of alias databases.
+
+       allow_mail_to_commands
+              Restrict  the  usage  of  mail delivery to external
+              command.
+
+       allow_mail_to_files
+              Restrict the usage of  mail  delivery  to  external
+              file.
+
+
+
+                                                                2
+
+
+
+
+
+ALIASES(5)                                             ALIASES(5)
+
+
+       recipient_delimiter
+              Delimiter  that  separates  recipients from address
+              extensions.
+
+STANDARDS
+       RFC 822 (ARPA Internet Text Messages)
+
+SEE ALSO
+       local(8) local delivery agent
+       postalias(1) alias database management
+
+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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+                                                                3
+
+
+
diff --git a/postfix/html/anatomy.html b/postfix/html/anatomy.html new file mode 100644 index 000000000..d3e569d3c --- /dev/null +++ b/postfix/html/anatomy.html @@ -0,0 +1,56 @@ + + + + +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 new file mode 100644 index 000000000..82eea2515 --- /dev/null +++ b/postfix/html/architecture.html @@ -0,0 +1,112 @@ + + + + +Postfix Overview - Global Architecture + + + + + +

Postfix +Overview - Global Architecture

+ +
+ +Up one level | Introduction | Goals +and features | Global architecture | Queue +Management | Security + +

Introduction

+ +Some mail systems such as Sendmail are implemented as one large +monolithic program that does everything. One large program certainly +makes it easy to share data between different parts of the system. +Unfortunately, one large program also makes it easy to make +fatal mistakes. Other mailers such as qmail use a rigid hierarchy +of programs that run other programs in a fixed order and throw them +away after use. This approach gives better insulation, at the cost +of some process creation overhead and inter-process communication. +The additional cost can be kept within acceptable limits by +partitioning the work in a sensible manner. + +

Postfix architecture

+ +Postfix is based on semi-resident, mutually-cooperating, processes +that perform specific tasks for each other, without any particular +parent-child relationship. Again, doing work in separate processes +gives better insulation than using one big program. In addition, +the Postfix approach has the advantage that a service such as +address rewriting is available to every Postfix component program, +without incurring the cost of process creation just to rewrite one +address. By the way: I do not claim that Postfix is the only (mail) +program using this approach. Even in this relatively young discipline +it is hard to come up something new that no-one ever did before. + +

+ +Postfix is implemented as a resident master server that runs Postfix +daemon processes on demand: daemon processes to send or receive +network mail messages, daemon processes to deliver mail locally, +etc. These processes are created up to a configurable number, +are re-used for a configurable number of times, and go away after +a configurable amount of idle time. This approach drastically +reduces process creation overhead while still providing the good +insulation from separate processes. + +

+ +Postfix is intended to be a Sendmail replacement. For this reason +it tries to be compatible with existing infrastructure. However, +many parts of the Postfix system, such as the local delivery program, +are easily replaced by editing an inetd-like configuration +file. For example, the plan is to provide an alternate local +delivery program that runs at a fixed low privilege, for POP/IMAP +users that never log into the shell, and that may not even have a +UNIX account. + +

+ +As a result of this architecture, Postfix is easy to strip down to +the bare minimum. Subsystems that are turned off cannot be exploited. +Firewalls do not need local delivery. On client workstations, one +disables both the smtp listener and local delivery subsystems; or +the client mounts the maildrop directory from a file server, +and runs no resident Postfix processes at all. + +

Communication between Postfix processes

+ +The core of the Postfix system is implemented by a dozen semi-resident +programs. For privacy reasons, these Postfix processes communicate +via UNIX-domain sockets or FIFOs that live in a protected directory. +Despite this privacy, Postfix processes do not really trust the +data that they receive in this manner; just like the contents of +Postfix queue files, they merely treat it as gossip. + +

+ +The amount of information passed on between Postfix processes is +limited. In many cases, the only information exchanged between +Postfix processes is a queue file name and a list of recipients or +some status information. Once an email message is saved to file it +stays there until it is read by a mail delivery program. + +

+ +Postfix takes the usual precautions to avoid loss of information: +flush and fsync() all data before acknowledging receipt, and check +all system call results for error conditions. This style of +programming may be new to some people, but I can assure you that +it has been standard practice for years in many places. + +


+ +Up one level | Introduction | Goals +and features | Global architecture | Queue +Management | Security + + + + diff --git a/postfix/html/backstage.html b/postfix/html/backstage.html new file mode 100644 index 000000000..1cacf5b21 --- /dev/null +++ b/postfix/html/backstage.html @@ -0,0 +1,71 @@ + + + + +Postfix Anatomy - Behind the Scenes + + + + + +

Postfix +Anatomy - Behind the Scenes

+ +
+ +Up one level | Receiving Mail | Delivering Mail | Behind the Scenes | +Command-line Utilities + +

+ +The previous sections gave a simplified overview of how the Postfix +system sends and receives mail. Several other things happen behind +the scenes. Unfortunately, this is hard to visualize on a +two-dimensional display, so this document has no illustration. + +

    + +
  • The master daemon is the supervisor +process that keeps an eye on the well-being of the mail system. It +is typically started at system boot time by the postfix command, and keeps running until +the system goes down. The master daemon +is responsible for starting all other Postfix daemon processes on +demand, and for restarting daemons that terminated prematurely +because of some problem. The master +daemon is also responsible for enforcing the daemon process count +limits as specified in the master.cf configuration file. + +

    + +

  • The bounce or defer daemon is called +upon left and right by other daemon processes, in order to maintain +per-message log files with non-delivery status information. + +

    + +

  • The trivial-rewrite daemon +is called upon left and right by other daemon processes, in order +to rewrite an address to user@fully.qualified.domain form, +or in order to resolve a destination. + +

    + +

  • The showq daemon lists the Postfix +queue status. This is the program behind the mailq command. + +
+ +
+ +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 new file mode 100644 index 000000000..60b195f9f --- /dev/null +++ b/postfix/html/basic.html @@ -0,0 +1,343 @@ + + + + + Postfix Configuration - Basics + + + + + +

Postfix Configuration - Basics

+ +
+ +Up one level | Basic Configuration | UCE Controls | Rate +Controls | Resource Controls | Address Manipulation + +

Introduction

+ +Postfix has about 100 configuration parameters that are controlled +via the main.cf file. Fortunately, they have sensible +default values. In most cases, you need to configure only two or +three parameters before you can use the Postfix mail system: + + + +The default values for many other configuration parameters are +derived from just these two. + +

+ +The third parameter of interest controls the amount of mail sent +to the local postmaster: + +

+ +

+ +By the way, if you change parameters of a running Postfix system, +don't forget to issue a postfix reload command. + +

+ +If you run Postfix on a virtual network interface, or if your +machine runs other mailers on virtual interfaces, you'll have to +look at the other parameters listed here as well: + +

+ +

What domain to use in outbound mail

+ +The myorigin parameter specifies the domain that appears in +mail that is posted on this machine. The default is to use the +local machine name, $myhostname, +which defaults to the name of the machine. Unless you are running +a really small site, you probably want to change that into $mydomain, which defaults to the parent +domain of the machine name. + +

+ +

+ +
Examples: + +

+ +

myorigin = $myhostname (default) + +
myorigin = $mydomain (probably desirable) + +
+ +

What domains to receive mail for +

+ +The mydestination parameter specifies what domains this +machine will deliver locally, instead of forwarding to another +machine. The default is to receive mail for the machine itself. + +

+ +You can specify zero or more domain names, /file/name patterns +and/or type:name lookup tables, separated by whitespace +and/or commas. A /file/name is replaced by its contents; +type:name requests that a table lookup is done, typically +from a virtual database. + +

+ +If your machine is a mail server for its entire domain, you must +list $mydomain as well. + +

+ +

Examples: + +

+ +

+ +
Default setting: + +
mydestination = $myhostname localhost.$mydomain + +

+ +

Domain-wide mail server: + +
mydestination = $myhostname localhost.$mydomain $mydomain + + +

+ +

Host with multiple DNS A records: + +
mydestination = $myhostname localhost.$mydomain www.$mydomain +ftp.$mydomain + +
+ +

+ +Caution: in order to avoid mail delivery loops, you must list all +hostnames of the machine, including $myhostname, and localhost.$mydomain. + +

+ +

What trouble to report to the postmaster +

+ +You should set up a postmaster alias that points to a human person. +This alias is required to exist, so that people can report mail +delivery problems. + +

+ +The Postfix system itself also reports problems to the postmaster +alias. You may not be interested in all types of trouble reports, +so this reporting mechanism is configurable. The default is to +report only serious problems (resource, software) to postmaster: + +

+ +

+ +
Example: + +
notify_classes = resource, software + +

+ +

The meaning of the classes is as follows: + +

+ +

+ +
bounce
Inform the postmaster of undeliverable +mail. For privacy reasons, the postmaster receives the message +headers only. + +

+ +

policy
Inform the postmaster of client requests +that were rejected because of (UCE) policy restrictions. The +postmaster receives a transcript of the entire SMTP session. + +

+ +

protocol
Inform the postmaster of protocol errors +(client or server side) or attempts by a client to execute +unimplemented commands. The postmaster receives a transcript of +the entire SMTP session. + +

+ +

resource
Inform the postmaster of mail not delivered +due to resource problems (for example, queue file write errors). + +

+ +

software
Inform the postmaster of mail not delivered +due to software problems. + +
+ +
+ +

My own hostname

+ +The myhostname parameter describes the fully-qualified domain +name of the machine running the Postfix system. $myhostname +appears as the default value in many other Postfix configuration +parameters. + +

+ +By default, myhostname is set to the local machine name. +If your machine name is not in fully-qualified domain name form, +or if you run Postfix on a virtual interface, you will have to +specify the fully-qualified domain name that the mail system +should use. + +

+ +
Examples: + +

+ +

myhostname = host.local.domain (local hostname is not +FQDN) + +
myhostname = host.virtual.domain (virtual interface) + +
myhostname = virtual.domain (virtual interface) + +
+ +

My own domain name

+ +The mydomain parameter specifies the parent domain of +$myhostname. By default it is derived from $myhostname +by stripping off the first part (unless the result would be a +top-level domain). + +
+ +
Examples: + +

+ +

mydomain = local.domain + +
mydomain = virtual.domain (virtual interface) + +
+ +

My own networks

+ +The mynetworks parameter lists all networks that this machine +is attached to. This information can be used by the +anti-UCE features to distinguish between local systems and +strangers. + +

+ +By default, mynetworks is set to the class A, B or C networks +that the machine is attached to. For example, for my machines at +home, the result is: 168.100.0.0/16 127.0.0.0/8. However, +network 168.100 is owned by my ISP. Of course I do not want +to consider all their customer systems as local, so I use instead: + +

+ +
mynetworks = 168.100.189.0/28, 127.0.0.0/8 + +
+ +

My own network addresses

+ +The inet_interfaces parameter specifies all network interface +addresses that the Postfix system should listen on; mail addressed +to user@[network address] will be delivered locally, +as if it is addressed to a domain listed in $mydestination. + + +

+ +The default is to listen on all active interfaces. If you run +mailers on virtual interfaces, you will have to specify what +interfaces to listen on. This includes the non-virtual mailer that +receives mail for the machine itself as well: it should never listen +on the virtual interfaces or you would have a mailer loop. + +

+ +
Examples: + +

+ +

+ +
Default: + +
inet_interfaces = all + +

+ +

Host running virtual mailers: + +
inet_interfaces = virtual.host.name (virtual domain) + +
inet_interfaces = $myhostname localhost.$mydomain +(non-virtual mailer) + +
+ +
+ +
+ +Up one level | Basic Configuration | UCE Controls | Rate +Controls | Resource Controls | Address Manipulation + + + + diff --git a/postfix/html/big-picture.fig b/postfix/html/big-picture.fig new file mode 100644 index 000000000..44e4d9ac4 --- /dev/null +++ b/postfix/html/big-picture.fig @@ -0,0 +1,159 @@ +#FIG 3.1 +Landscape +Center +Inches +1200 2 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 11850 3300 600 300 11250 3000 12450 3600 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 11850 2250 600 300 11250 1950 12450 2550 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 13500 4350 600 300 12900 4050 14100 4650 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 13500 3300 600 300 12900 3000 14100 3600 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 13500 2250 600 300 12900 1950 14100 2550 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 5700 2775 600 300 5100 2475 6300 3075 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 5700 3825 600 300 5100 3525 6300 4125 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 7350 3300 600 300 6750 3000 7950 3600 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 7350 2250 600 300 6750 1950 7950 2550 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 2700 2775 600 300 2100 2475 3300 3075 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 10800 3300 11250 3300 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 12450 3300 12900 3300 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 12225 3075 13125 2475 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 12138 3542 13038 4142 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 14100 2250 14550 2250 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 14100 3300 14550 3300 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 14100 4350 14550 4350 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 14587 4050 15487 4050 15487 4650 14587 4650 14587 4050 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 14550 3000 15450 3000 15450 3600 14550 3600 14550 3000 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 11850 1500 11850 1950 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 11850 2550 11850 3000 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 13500 1500 13500 1950 +2 1 0 1 -1 7 2 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 14700 1350 13950 2025 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 1 2 + 0 0 1.00 60.00 120.00 + 11850 3600 11850 4050 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 14550 1950 15450 1950 15450 2550 14550 2550 14550 1950 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 11400 900 12300 900 12300 1500 11400 1500 11400 900 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 13050 900 13950 900 13950 1500 13050 1500 13050 900 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 14550 900 15450 900 15450 1500 14550 1500 14550 900 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 11400 4050 12300 4050 12300 4650 11400 4650 11400 4050 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 4 + 0 0 1.00 60.00 120.00 + 10800 3450 11100 3450 11100 4350 10800 4350 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 9900 3000 10800 3000 10800 3600 9900 3600 9900 3000 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 9900 4050 10800 4050 10800 4650 9900 4650 9900 4050 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 9300 3300 9900 3300 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 1 4 + 0 0 1.00 60.00 120.00 + 9900 3450 9600 3450 9600 4350 9900 4350 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 7950 3300 8400 3300 +2 1 0 1 -1 7 2 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 8550 4200 7800 3525 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 8400 3000 9300 3000 9300 3600 8400 3600 8400 3000 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 8400 4050 9300 4050 9300 4650 8400 4650 8400 4050 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 6225 3675 6825 3450 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 6225 2925 6825 3150 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 3300 2775 3750 2775 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 1650 2775 2100 2775 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 4650 2775 5100 2775 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 4650 3825 5100 3825 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 3750 3525 4650 3525 4650 4125 3750 4125 3750 3525 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 1 2 + 0 0 1.00 60.00 120.00 + 5700 4125 5700 4500 +2 1 0 1 -1 7 2 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 4500 4650 5250 4050 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 7350 2550 7350 3000 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 1 2 + 0 0 1.00 60.00 120.00 + 7350 3600 7350 4050 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 750 2475 1650 2475 1650 3075 750 3075 750 2475 +2 2 0 1 -1 6 1 0 20 0.000 0 0 -1 0 0 5 + 3750 2475 4650 2475 4650 3075 3750 3075 3750 2475 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 3750 4500 4650 4500 4650 5100 3750 5100 3750 4500 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 5250 4500 6150 4500 6150 5100 5250 5100 5250 4500 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 6900 4050 7800 4050 7800 4650 6900 4650 6900 4050 +2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 4875 675 14325 675 14325 5325 4875 5325 4875 675 +4 0 -1 0 0 0 15 0.0000 4 150 450 11625 3330 qmgr\001 +4 0 -1 0 0 0 15 0.0000 4 150 630 11515 2325 resolve\001 +4 0 -1 0 0 0 15 0.0000 4 150 810 11415 4425 relocated\001 +4 0 -1 0 0 0 15 0.0000 4 195 360 13320 4402 pipe\001 +4 0 -1 0 0 0 15 0.0000 4 180 420 13290 3345 smtp\001 +4 0 -1 0 0 0 15 0.0000 4 150 405 13297 2325 local\001 +4 0 -1 0 0 0 15 0.0000 4 150 585 13187 1275 aliases\001 +4 0 -1 0 0 0 15 0.0000 4 180 780 11425 1245 transport\001 +4 0 -1 0 0 0 15 0.0000 4 150 735 14602 1275 .forward\001 +4 0 -1 0 0 0 15 0.0000 4 150 690 14635 3375 Internet\001 +4 0 -1 0 0 0 15 0.0000 4 150 930 14607 4425 UUCP etc.\001 +4 0 -1 0 0 0 15 0.0000 4 150 675 14632 2325 mailbox\001 +4 0 -1 0 0 0 15 0.0000 4 150 525 10067 3375 active\001 +4 0 -1 0 0 0 15 0.0000 4 150 735 9952 4425 deferred\001 +4 0 -1 0 0 0 15 0.0000 4 195 780 8430 3352 incoming\001 +4 0 -1 0 0 0 15 0.0000 4 150 540 8560 4425 virtual\001 +4 0 -1 0 0 0 15 0.0000 4 150 690 3840 3892 Internet\001 +4 0 -1 0 0 0 15 0.0000 4 150 405 952 2850 local\001 +4 0 -1 0 0 0 15 0.0000 4 195 750 3795 2827 maildrop\001 +4 0 -1 0 0 0 15 0.0000 4 195 570 5395 2827 pickup\001 +4 0 -1 0 0 0 15 0.0000 4 195 525 5437 3877 smtpd\001 +4 0 -1 0 0 0 15 0.0000 4 195 675 7012 3352 cleanup\001 +4 0 -1 0 0 0 15 0.0000 4 150 630 7035 2325 rewrite\001 +4 0 -1 0 0 0 15 0.0000 4 150 825 6902 4425 canonical\001 +4 0 -1 0 0 0 15 0.0000 4 105 600 5375 4875 access\001 +4 0 -1 0 0 0 15 0.0000 4 150 405 3997 4875 RBL\001 +4 0 -1 0 0 0 15 0.0000 4 150 945 2197 2850 "sendmail"\001 diff --git a/postfix/html/big-picture.gif b/postfix/html/big-picture.gif new file mode 100644 index 000000000..85b38c745 Binary files /dev/null and b/postfix/html/big-picture.gif differ diff --git a/postfix/html/big-picture.html b/postfix/html/big-picture.html new file mode 100644 index 000000000..10d89c3b2 --- /dev/null +++ b/postfix/html/big-picture.html @@ -0,0 +1,76 @@ + + + + +Postfix - the Big Picture + + + + + +

Postfix +- the Big Picture

+ +
+ + + +

+ +The figure shows the main Postfix system components, and the main +information flows between them. Postfix system components are +introduced in the Postfix anatomy +documentation. + +

+ +

    + +
  • Yellow ellipsoids are mail programs. + +
  • Yellow boxes are mail queues or files. + +
  • Blue boxes are lookup tables. + +
  • Programs in the large box run under control by the Postfix +resident master daemon. + +
  • Data in the large box is property of the Postfix mail system. + +
+ +In order to keep the big picture readable the following elements were omitted: + +

+ +

    + +
  • The Postfix command-line utilities. + +
  • The Postfix resident master daemon. + +
  • The DNS lookups by the SMTP server +and client daemons + +
  • The bounce or defer daemon and the +flow of bounced mail. + +
  • The address rewriting and resolving requests by the SMTP server +and by the local delivery agent. + +
  • The flow of mail forwarded by the local delivery agent. + +
  • The flow of postmaster notices +for protocol errors, policy violations, etc. + +
  • Triggers to alert the pickup daemon +and queue manager that new mail has arrived +in the maildrop and incoming queues, respectively. + +
+ +
+ + + + diff --git a/postfix/html/bounce.8.html b/postfix/html/bounce.8.html new file mode 100644 index 000000000..43c762e81 --- /dev/null +++ b/postfix/html/bounce.8.html @@ -0,0 +1,134 @@ +
+
+
+
+BOUNCE(8)                                               BOUNCE(8)
+
+
+NAME
+       bounce - Postfix message bounce or defer daemon
+
+SYNOPSIS
+       bounce [generic Postfix daemon options]
+
+DESCRIPTION
+       The  bounce  daemon  maintains  per-message log files with
+       non-delivery status information. Each log  file  is  named
+       after  the  queue file that it corresponds to, and is kept
+       in a queue subdirectory named after the  service  name  in
+       the master.cf file (either bounce or defer).  This program
+       expects to be run from the master(8) process manager.
+
+       The bounce daemon processes two types of service requests:
+
+       o      Append  a  recipient status record to a per-message
+              log file.
+
+       o      Post a bounce message, with a copy of  a  log  file
+              and  of  the corresponding message. When the bounce
+              is posted successfully, the log file is deleted.
+
+       The software does a best effort to notify the sender  that
+       there  was a problem. A notification is sent even when the
+       log file or original message cannot be read.
+
+       Optionally, a client can request that the per-message  log
+       file  be deleted when the requested operation fails.  This
+       is used by clients that cannot retry transactions by them-
+       selves,  and  that  depend  on  retry  logic  in their own
+       client.
+
+STANDARDS
+       RFC 822 (ARPA Internet Text Messages)
+
+DIAGNOSTICS
+       Problems and transactions are logged to syslogd(8).
+
+BUGS
+       The log files use an  ad-hoc,  unstructured  format.  This
+       will  have  to  change in order to easily support standard
+       delivery status notifications.
+
+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
+       command after a configuration change.
+
+       bounce_size_limit
+              Limit  the  amount of original message context that
+              is sent in a non-delivery notification.
+
+
+
+
+                                                                1
+
+
+
+
+
+BOUNCE(8)                                               BOUNCE(8)
+
+
+       mail_name
+              Use this mail system name in the introductory  text
+              at the start of a bounce message.
+
+       notify_classes
+              Notify  the  postmaster  of  bounced mail when this
+              parameter includes the bounce  class.  For  privacy
+              reasons, the message body is not included.
+
+SEE ALSO
+       master(8) process manager
+       qmgr(8) queue manager
+       syslogd(8) system logging
+
+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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+                                                                2
+
+
+
diff --git a/postfix/html/canonical.5.html b/postfix/html/canonical.5.html new file mode 100644 index 000000000..f36f69808 --- /dev/null +++ b/postfix/html/canonical.5.html @@ -0,0 +1,200 @@ +
+
+
+
+CANONICAL(5)                                         CANONICAL(5)
+
+
+NAME
+       canonical - format of Postfix canonical table
+
+SYNOPSIS
+       postmap /etc/postfix/canonical
+
+DESCRIPTION
+       The  optional  canonical file specifies an address mapping
+       for local and non-local addresses. The mapping is used  by
+       the  cleanup(8) daemon.  The address mapping is recursive.
+
+       The file serves as input to the  postmap(1)  command.  The
+       result,  an  indexed file in dbm or db format, is used for
+       fast searching by the mail system. After an update it  may
+       take  a  minute  or  so before the change becomes visible.
+       Issue a postfix reload command to eliminate the delay.
+
+       The  canonical  mapping  affects   both   message   header
+       addresses (i.e. addresses that appear inside messages) and
+       message envelope addresses  (for  example,  the  addresses
+       that  are  used in SMTP protocol commands). Think Sendmail
+       rule set S3, if you like.
+
+       Typically, one would use the canonical  table  to  replace
+       login   names   by  Firstname.Lastname,  or  to  clean  up
+       addresses produced by legacy mail systems.
+
+       The canonical mapping is not to be confused  with  virtual
+       domain support. Use the virtual(5) map for that purpose.
+
+       The  canonical  mapping  is  not to be confused with local
+       aliasing.  Use the aliases(5) map for that purpose.
+
+       The format of the canonical table is as follows,  mappings
+       being tried in the order as listed in this manual page:
+
+       blanks and comments
+              Blank  lines  are  ignored,  as are lines beginning
+              with `#'.
+
+       user@domain address
+              user@domain is replaced by address. This  form  has
+              the highest precedence.
+
+              This  form useful to clean up addresses produced by
+              legacy mail systems.  It can also be used  to  pro-
+              duce  Firstname.Lastname  style  addresses, but see
+              below for a simpler solution.
+
+       user address
+              user@site is replaced by address when site is equal
+              to  $myorigin,  when  site is listed in $mydestina-
+              tion, or when it is listed in $inet_interfaces.
+
+
+
+
+                                                                1
+
+
+
+
+
+CANONICAL(5)                                         CANONICAL(5)
+
+
+              This form is useful for replacing  login  names  by
+              Firstname.Lastname.
+
+       @domain address
+              Every  address  in  domain  is replaced by address.
+              This form has the lowest precedence.
+
+       In all the above forms, when address has the form  @other-
+       domain, the result is the same user in otherdomain.
+
+ADDRESS EXTENSION
+       When  table  lookup  fails, and the address localpart con-
+       tains   the   optional    recipient    delimiter    (e.g.,
+       user+foo@domain),  the  search  is  repeated for the unex-
+       tended address  (e.g.   user@domain),  and  the  unmatched
+       extension is propagated to the result of table lookup. The
+       matching order is: user+foo@domain, user@domain, user+foo,
+       user, and @domain.
+
+BUGS
+       The  table format does not understand quoting conventions.
+
+CONFIGURATION PARAMETERS
+       The following main.cf parameters are  especially  relevant
+       to  this  topic.  See  the Postfix main.cf file for syntax
+       details and for default values.  Use  the  postfix  reload
+       command after a configuration change.
+
+       canonical_maps
+              List of canonical mapping tables.
+
+       recipient_canonical_maps
+              Address  mapping  lookup  table  for  envelope  and
+              header recipient addresses.
+
+       sender_canonical_maps
+              Address  mapping  lookup  table  for  envelope  and
+              header sender addresses.
+
+       Other parameters of interest:
+
+       inet_interfaces
+              The  network  interface  addresses that this system
+              receives mail on.
+
+       masquerade_domains
+              List of domains that hide  their  subdomain  struc-
+              ture.
+
+       masquerade_exceptions
+              List  of user names that are not subject to address
+              masquerading.
+
+
+
+
+
+                                                                2
+
+
+
+
+
+CANONICAL(5)                                         CANONICAL(5)
+
+
+       mydestination
+              List of domains that  this  mail  system  considers
+              local.
+
+       myorigin
+              The domain that is appended to locally-posted mail.
+
+SEE ALSO
+       cleanup(8) canonicalize and enqueue mail
+       postmap(1) create mapping table
+       virtual(5) virtual domain mapping
+
+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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+                                                                3
+
+
+
diff --git a/postfix/html/cleanup.8.html b/postfix/html/cleanup.8.html new file mode 100644 index 000000000..70c8290a9 --- /dev/null +++ b/postfix/html/cleanup.8.html @@ -0,0 +1,200 @@ +
+
+
+
+CLEANUP(8)                                             CLEANUP(8)
+
+
+NAME
+       cleanup - canonicalize and enqueue Postfix message
+
+SYNOPSIS
+       cleanup [generic Postfix daemon options]
+
+DESCRIPTION
+       The cleanup daemon processes inbound mail, inserts it into
+       the incoming mail queue, and informs the queue manager  of
+       its arrival.
+
+       The cleanup daemon always performs the following transfor-
+       mations:
+
+       o      Insert missing message  headers:  (Resent-)  From:,
+              Message-Id:, and Date:.
+
+       o      Extract envelope recipient addresses from (Resent-)
+              To:, Cc: and Bcc: message headers when  no  recipi-
+              ents are specified in the message envelope.
+
+       o      Transform  envelope  and  header  addresses  to the
+              standard user@fully-qualified-domain form  that  is
+              expected  by  other Postfix programs.  This task is
+              delegated to the trivial-rewrite(8) daemon.
+
+       o      Eliminate duplicate envelope recipient addresses.
+
+       The following address transformations are optional:
+
+       o      Optionally,  rewrite  all   envelope   and   header
+              addresses  according  to  the mappings specified in
+              the canonical(5) lookup tables.
+
+       o      Optionally, masquerade  envelope  sender  addresses
+              and  message  header  addresses (i.e. strip host or
+              domain information below all domains listed in  the
+              masquerade_domains parameter, except for user names
+              listed  in  masquerade_exceptions).   Address  mas-
+              querading does not affect envelope recipients.
+
+       o      Optionally, expand envelope recipients according to
+              information found in the virtual(5) lookup  tables.
+
+       The  cleanup  daemon performs sanity checks on the content
+       of each message. When it finds a problem,  by  default  it
+       returns  a  diagnostic status to the client, and leaves it
+       up to the client to deal with the problem.  Alternatively,
+       the  client  can  request the cleanup daemon to bounce the
+       message back to the sender in case of trouble.
+
+STANDARDS
+       RFC 822 (ARPA Internet Text Messages)
+
+
+
+
+                                                                1
+
+
+
+
+
+CLEANUP(8)                                             CLEANUP(8)
+
+
+DIAGNOSTICS
+       Problems and transactions are logged to syslogd(8).
+
+BUGS
+       Table-driven rewriting rules make it hard  to  express  if
+       then else and other logical relationships.
+
+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
+       command after a configuration change.
+
+Miscellaneous
+       hopcount_limit
+              Limit the number of Received: message headers.
+
+Address transformations
+       empty_address_recipient
+              The destination for  undeliverable  mail  from  <>.
+              This  substitution is done before all other address
+              rewriting.
+
+       canonical_maps
+              Address mapping lookup table for sender and recipi-
+              ent addresses in envelopes and headers.
+
+       recipient_canonical_maps
+              Address  mapping  lookup  table  for  envelope  and
+              header recipient addresses.
+
+       sender_canonical_maps
+              Address  mapping  lookup  table  for  envelope  and
+              header sender addresses.
+
+       masquerade_domains
+              List  of  domains  that hide their subdomain struc-
+              ture.
+
+       masquerade_exceptions
+              List of user names that are not subject to  address
+              masquerading.
+
+       virtual_maps
+              Address mapping lookup table for envelope recipient
+              addresses.
+
+Resource controls
+       duplicate_filter_limit
+              Limit the number of envelope  recipients  that  are
+              remembered.
+
+       header_size_limit
+              Limit the amount of memory in bytes used to process
+
+
+
+                                                                2
+
+
+
+
+
+CLEANUP(8)                                             CLEANUP(8)
+
+
+              a message header.
+
+SEE ALSO
+       canonical(5) canonical address lookup table format
+       qmgr(8) queue manager daemon
+       syslogd(8) system logging
+       trivial-rewrite(8) address rewriting
+       virtual(5) virtual address lookup table format
+
+FILES
+       /etc/postfix/canonical*, canonical mapping table
+       /etc/postfix/virtual*, virtual mapping table
+
+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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+                                                                3
+
+
+
diff --git a/postfix/html/commands.html b/postfix/html/commands.html new file mode 100644 index 000000000..b250e9ccb --- /dev/null +++ b/postfix/html/commands.html @@ -0,0 +1,103 @@ + + + + +Postfix Anatomy - Command-line Utilities + + + + + +

Postfix +Anatomy - Command-line Utilities

+ +
+ +Up one level | Receiving Mail | Delivering Mail | Behind the Scenes | Command-line Utilities + +

+ +Enough daemon talk. The anatomy lesson ends with an introduction +to command-line utilities for day-to-day use of the Postfix mail +system. Besides the sendmail, mailq, and newaliases commands that were already +introduced, the Postfix system comes with it own collection of +utilities. For consistency, these are all named postsomething. + +

    + +
  • The postfix command controls the +operation of the mail system. It is the interface for starting and +stopping the mail system, and for some other administrative +operations. This command is reserved to the super-user. + +

    + +

  • The postalias command maintains +Postfix alias databases. This is the +program behind the newaliases +command. + +

    + +

  • The postcat command displays the +contents of Postfix queue files. This is a limited, preliminary +utility. This program is likely to be superseded by something more +powerful that can also edit Postfix queue files. + +

    + +

  • The postconf command displays +Postfix main.cf parameters: actual values, default values, +or parameters that have non-default settings. This is a limited, +preliminary utility. This program is likely to be superseded by +something more powerful that can not only list but also edit the +main.cf file. + +

    + +

  • The postdrop command is the mail +posting agent that is run by the sendmail +command on systems that have no world-writable maildrop queue +directory. + +

    + +

  • The postkick command makes some +internal communication channels available for use in, for example, +shell scripts. + +

    + +

  • The postlock command provides +Postfix-compatible mailbox locking for use in, for example, shell +scripts. + +

    + +

  • The postlog command provides +Postfix-compatible logging for shell scripts. + +

    + +

  • The postmap command maintains +Postfix lookup tables such as canonical, +virtual and others. It is a cousin of +the UNIX makemap command. + +
+ +
+ +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 new file mode 100644 index 000000000..2566ed1d4 --- /dev/null +++ b/postfix/html/config.html @@ -0,0 +1,67 @@ + + + + + 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/defer.8.html b/postfix/html/defer.8.html new file mode 120000 index 000000000..4de6cc212 --- /dev/null +++ b/postfix/html/defer.8.html @@ -0,0 +1 @@ +bounce.8.html \ No newline at end of file diff --git a/postfix/html/delivering.html b/postfix/html/delivering.html new file mode 100644 index 000000000..9fc2296e2 --- /dev/null +++ b/postfix/html/delivering.html @@ -0,0 +1,139 @@ + + + + +Postfix Anatomy - Delivering Mail + + + + + +

Postfix +Anatomy - Delivering Mail

+ +
+ +Up one level | Receiving Mail | Delivering Mail | Behind the Scenes | Command-line Utilities + +

+ +Once a message has reached the incoming queue the next step +is to deliver it. The figure shows the main components of the +Postfix mail delivery apparatus. For an explanation of the symbols, +click on the icon in the upper left-hand corner of this page. + +

+ +

+ + + +
+ +

+ +

    + +
  • The queue manager is the heart of +the Postfix mail system. It contacts the local, +smtp, or pipe +delivery agents, and sends a delivery request with queue file +pathname information, the message sender address, the host to +deliver to if the destination is remote, and one or more message +recipient addresses. + +

    + +The queue manager maintains a separate deferred queue for +mail that cannot be delivered, so that a large mail backlog will +not slow down normal queue accesses. + +

    + +The queue manager maintains a small active queue with just +the few messages that it has opened for delivery. The active +queue acts as a limited window on the potentially much larger +incoming or deferred queues. The small active +queue prevents the queue manager from running out of memory under +heavy load. + +

    + +Optionally, the queue manager bounces mail for recipients that are +listed in the relocated table. +This table contains contact information for users or even entire +domains that no longer exist. + +

    + +

  • On request by the queue manager, the trivial-rewrite daemon resolves +destinations. By default, it only distinguishes between local +and remote destinations. Additional routing information can +be specified with the optional transport table. + +

    + +

  • On request by the queue manager, the bounce +or defer daemon generates non-delivery reports when mail cannot +be delivered, either due to an unrecoverable error or because the +destination is unreachable for an extended period of time. + +

    + +

  • The local delivery agent understands +UNIX-style mailboxes, sendmail-style system-wide alias databases, and sendmail-style +per-user .forward files. Multiple +local delivery agents can be run in parallel, but parallel delivery +to the same user is usually limited. + +

    + +Together with the sendmail mail +posting agent, the local delivery agent +implements the familiar Sendmail user interface. + +

    + +The local delivery agent has hooks for +alternative forms of local delivery: you can configure it to +deliver to mailbox files in user home directories, and you can even +configure it to delegate mailbox delivery to an external command +such as the popular procmail program. + +

    + +

  • The SMTP client looks up a list of +mail exchangers for the destination host, sorts the list by +preference, and tries each address in turn until it finds a server +that responds. On a busy Postfix system you will see several SMTP +client processes running in parallel. + +

    + +

  • The pipe mailer is the outbound +interface to other mail transports (the sendmail program is the inbound interface). +The Postfix mail system comes with examples +for delivery via the UUCP protocol. At the time of writing, +this venerable protocol is still widely used. By default, Postfix +understands bang path style +addresses. + +
+ +
+ +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 new file mode 100644 index 000000000..5d24b58d0 --- /dev/null +++ b/postfix/html/faq.html @@ -0,0 +1,498 @@ + + + + + + +Postfix Frequently Asked Questions + + + + + +

Postfix Frequently Asked Questions

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

Table of contents

+ + + +
+ +

Running Postfix inside an intranet

+ +The simplest way to set up Postfix on a host inside a firewalled +network is to send all your mail to the intranet mail gateway, and +to let that gateway take care of forwarding. + +

+ +

    + +
  • Edit the main.cf file and specify: + +

    + +

    + +
    relayhost = $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: + +

    + +

    + +
    relayhost = gateway.my.domain + +
    + +

    + +

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

    + +

    + +
    + +
    main.cf: + +
    transport_maps = hash:/etc/postfix/transport + +

    + +

    /etc/postfix/transport: + +
    my.domain   smtp: + +
    + +
    + +

    + +Specify dbm:/etc/postfix/transport if your system +uses dbm files instead of db. + +

    + +

  • Execute the command postfix reload to make the +changes effective. + +
+ +
+ +

Running Postfix on a firewall

+ +Note: this section depends on accidental properties of the +implementation so this information is subject to change. + +

+ +How to set up Postfix on the firewall machine so that it relays +mail for my.domain to a gateway machine on the inside, and +so that it refuses mail for *.my.domain? The problem is that +the standard relay_domains +mail relaying restriction allows mail to *.my.domain when +you specify my.domain. + +

+ +

    + +
  • Specify a null relay_domains +parameter plus a virtual table to +route mail for my.domain to the inside machine: + +

    + +

    + +
    /etc/postfix/main.cf: + +
    mydestination = $myhostname, my.domain, +localhost.my.domain + +
    relay_domains = + +
    virtual_maps = hash:/etc/postfix/virtual + +

    + +

    /etc/postfix/virtual: + +
    @my.domain   @inside-gateway.my.domain + +
    + +

    + +Specify dbm:/etc/postfix/virtual if your system uses dbm +files instead of db. + +

    + +

  • Execute the command postfix reload after a +configuration change. + +
+ +

+ +Unfortunately, the solution cannot use the transport table, because +that table is ignored for destinations that match $mydestination. +That's an implementation error, and it will be removed. + + +


+ +

Support for maildir-style mailboxes

+ +Maildir is a specific one-file-per-message organization that +was introduced with the qmail system by Daniel Bernstein. + +

+ +Postfix supports the maildir mailbox format. Edit main.cf +and specify a line with: home_mailbox = maildir. + +


+ +

Using Procmail for local delivery

+ +
    + +
  • Edit /etc/postfix/main.cf, and specify procmail as the +command for mailbox delivery: + +

    + +

    + +
    mailbox_command = /path/to/procmail + +
    + +

    + +Do not use any shell meta characters or built-ins +such as IFS or &&, because they force Postfix +to run an expensive shell process. + +

    + +

  • Execute the command postfix reload to make the changes +effective. + +
+ +
+ +

Postfix breaks "sendmail -v"

+ +Some people will complain that sendmail -v no longer shows +the actual mail delivery. + +

+ +With a distributed mail system such as Postfix, this is difficult +to implement. Postfix does not run any mail delivery process under +control by a user. Instead, mail delivery is done by daemon processes +that have no parental relationship with user processes. This +eliminates a large variety of potential security exploits with +environment variables, signal handlers, and with other process +attributes that UNIX passes on from parent to child. + +

+ +In addition, Postfix uses multiple processes in order to insulate +subsystems from each other. Making the delivery agents talk directly +to user processes would defeat a lot of the effort that went into +making Postfix more secure than ordinary mailers. + +


+ +

Getting rid of Delivered-To:

+ +Some people will complain about the ugly Delivered-To: +message header that Postfix prepends to their mail. + +

+ +With the Postfix architecture, Delivered-To: is required to +prevent mail forwarding loops. Fortunately, many mail user agents +have per-user or even system-wide configuration files that can be +set up to suppress specific message headers (for example ~/.mailrc +and /usr/lib/Mail.rc). + +

+ +With mailing lists, Delivered-To: can get in the way when +the list exploder uses a "secret" alias that should not be shown +in outbound mail. In order to tackle this, look up the FEATURE +CONTROL section in the documentation of the local delivery agent. + +

+ +See also the FAQ item for problems with the majordomo approve command. + +


+ +

Postfix breaks the majordomo "approve" +command

+ +The Postfix local delivery agent prepends a Delivered-To: +message header to prevent mail forwarding loops. With majordomo +mailing lists, Delivered-To: gets in the way when the +moderator wants to approve postings that were sent to the list. +The Postfix system claims that the mail is looping. + +

+ +Currently, the workaround is to edit the approve script to +strip any header lines that match: + +

+ +

+ +
/delivered-to/i + +
+ +

+ +Yes, this assumes that the moderator knows what she is doing. + +


+ +

Setting up an Internet to UUCP gateway

+ +Here is how to set up a machine that sends some but not all +mail via UUCP. See the UUCP-only FAQ entry +for setting a UUCP-only host. + +

+ +

    + +
  • Make an entry in /etc/postfix/transport: + +

    + +

    + +
    some.domain   uucp:uucp-host + +
    + +

    + +This causes all mail for the some.domain (and subdomains +thereof) to be sent via UUCP to the host uucp-host. + +

    + +

  • Execute the command postmap /etc/postfix/transport whenever +you change the transport file. + +

    + +

  • You need an entry in /etc/postfix/master.cf: + +
    +    uucp      unix  -       n       n       -       -       pipe
    +      flags=F user=uucp argv=uux -n -z -a$sender - $nexthop!rmail ($recipient)
    +
    + +

    + +This runs the uux command, and substitutes the next-hop +hostname (uucp-host) and the recipients before executing +the command. The uux command is executed without assistance +from the shell, so there are no problems with shell meta characters. + +

    + +

  • Edit /etc/postfix/main.cf and enable transport +table lookups: + +

    + +

    + +
    transport_maps = hash:/etc/postfix/transport + +
    + +

    + +Specify dbm instead of hash if your system has no +db support. + +

    + +

  • Edit /etc/postfix/main.cf and add some.domain to +the list of domains that your site is willing to relay mail for. +See the relay_domains +configuration parameter. + +

    + +

  • Execute the command postfix reload to make the +changes effective. + +
+ +
+ +

Using UUCP as the default transport

+ +Here is how to relay all your mail over a UUCP link. See the Internet to UUCP FAQ entry for setting up a machine +that gateways between UUCP and SMTP. + +

+ +

    + +
  • There is no need for a transport table. + +

    + +

  • In /etc/postfix/main.cf, specify the name of your +UUCP gateway host, and specify that all mail must be sent +via the uucp message transport: + +

    + +

    + +
    relayhost = uucp-gateway + +
    default_transport = uucp + +
    + +

    + +

  • You need an entry in /etc/postfix/master.cf: + +
    +    uucp      unix  -       n       n       -       -       pipe
    +      flags=F user=uucp argv=uux -n -z -a$sender - $nexthop!rmail ($recipient)
    +
    + +This runs the uux command, and substitutes the next-hop +hostname (uucp-gateway, or whatever you specified) and the +recipients before executing the command. The uux command +is executed without assistance from the shell, so there are no +problems with shell meta characters. + +

    + +

  • Execute the command postfix reload to make the +changes effective. + +
+ + +
+ +

Using DB libraries on Solaris etc.

+ +The old dbm UNIX database has severe limitations when you +try to store lots of information. It breaks when the number of hash +collisions becomes so large that the entries no longer fit together +in a single disk block. The more modern db database does +not suffer these limitations. It is standard on 4.4BSD and Linux +systems. + +

+ +In order to build Postfix with db support on UNIX systems +that do not have db support out of the box, you need the +db-1.85 release, or the current +version which has a db-1.85 compatible interface. + +

+ +Use the following commands in the Postfix top-level directory: + +

+ +
% make tidy + +
% make makefiles CCARGS="-DHAS_DB +-I/some/where/include" AUXLIBS=/some/where/libdb.a + +
% make + +
+ +

+ +Of course you will have to specify the actual location of the +include directory and of the object library. + +

+ +One problem: older DB versions install a file /usr/include/ndbm.h +that is incompatible with the one in /usr/include. Be sure +to get rid of the bogus file, or the linker will fail to find +dbm_dirfno. + +


+ +Up one level | Postfix +Overview | Postfix Anatomy | Postfix Configuration | Postfix FAQ + + + + diff --git a/postfix/html/flow.fig b/postfix/html/flow.fig new file mode 100644 index 000000000..803727027 --- /dev/null +++ b/postfix/html/flow.fig @@ -0,0 +1,129 @@ +#FIG 3.1 +Landscape +Center +Inches +1200 2 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 3000 1725 600 300 2400 1425 3600 2025 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 6000 1725 600 300 5400 1425 6600 2025 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 6000 2775 600 300 5400 2475 6600 3075 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 7650 2250 600 300 7050 1950 8250 2550 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 7650 3300 600 300 7050 3000 8250 3600 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 4650 6900 600 300 4050 6600 5250 7200 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 6300 6900 600 300 5700 6600 6900 7200 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 6300 5850 600 300 5700 5550 6900 6150 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 6300 7950 600 300 5700 7650 6900 8250 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 2250 6900 2700 6900 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 1 2 + 0 0 1.00 60.00 120.00 + 3150 7200 3150 7650 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 3600 6900 4050 6900 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 5250 6900 5700 6900 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 5025 6675 5925 6075 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 4938 7142 5838 7742 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 6900 5850 7350 5850 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 6900 6900 7350 6900 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 6900 7950 7350 7950 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 3 + 0 0 1.00 60.00 120.00 + 4650 7200 4650 7950 3600 7950 +2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 1125 5325 7125 5325 7125 8475 1125 8475 1125 5325 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 1 2 + 0 0 1.00 60.00 120.00 + 2250 5850 5700 5850 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 6525 2625 7125 2400 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 8250 2250 8700 2250 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 8700 3300 8250 3300 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 6525 1875 7125 2100 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 7650 3000 7650 2550 +2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 5175 675 9825 675 9825 3825 5175 3825 5175 675 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 1 3 + 0 0 1.00 60.00 120.00 + 7650 1950 7650 1200 8700 1200 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 3600 1725 4050 1725 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 1950 1725 2400 1725 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 4950 1725 5400 1725 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 4950 2775 5400 2775 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 1050 1425 1950 1425 1950 2025 1050 2025 1050 1425 +2 2 0 1 -1 6 1 0 20 0.000 0 0 -1 0 0 5 + 4050 1425 4950 1425 4950 2025 4050 2025 4050 1425 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 4050 2475 4950 2475 4950 3075 4050 3075 4050 2475 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 8700 900 9600 900 9600 1500 8700 1500 8700 900 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 8700 1950 9600 1950 9600 2550 8700 2550 8700 1950 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 8700 3000 9600 3000 9600 3600 8700 3600 8700 3000 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 1350 5550 2250 5550 2250 6150 1350 6150 1350 5550 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 1350 6600 2250 6600 2250 7200 1350 7200 1350 6600 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 2700 6600 3600 6600 3600 7200 2700 7200 2700 6600 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 2700 7650 3600 7650 3600 8250 2700 8250 2700 7650 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 7387 7650 8287 7650 8287 8250 7387 8250 7387 7650 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 7350 6600 8250 6600 8250 7200 7350 7200 7350 6600 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 7350 5550 8250 5550 8250 6150 7350 6150 7350 5550 +4 0 -1 0 0 0 15 0.0000 4 150 495 1252 1800 Local\001 +4 0 -1 0 0 0 15 0.0000 4 150 795 2602 1800 Sendmail\001 +4 0 -1 0 0 0 15 0.0000 4 195 780 4110 1777 Maildrop\001 +4 0 -1 0 0 0 15 0.0000 4 150 690 4140 2842 Internet\001 +4 0 -1 0 0 0 15 0.0000 4 195 585 5707 1777 Pickup\001 +4 0 -1 0 0 0 15 0.0000 4 195 555 5722 2827 Smtpd\001 +4 0 -1 0 0 0 15 0.0000 4 195 720 7290 2302 Cleanup\001 +4 0 -1 0 0 0 15 0.0000 4 150 750 8775 1275 Forward\001 +4 0 -1 0 0 0 15 0.0000 4 195 810 8745 2302 Incoming\001 +4 0 -1 0 0 0 15 0.0000 4 150 660 7320 3375 Bounce\001 +4 0 -1 0 0 0 15 0.0000 4 150 675 8812 3375 Internal\001 +4 0 -1 0 0 0 15 0.0000 4 150 750 1425 5925 Forward\001 +4 0 -1 0 0 0 15 0.0000 4 195 810 1395 6952 Incoming\001 +4 0 -1 0 0 0 15 0.0000 4 150 585 2857 6975 Active\001 +4 0 -1 0 0 0 15 0.0000 4 195 495 4402 6952 Qmgr\001 +4 0 -1 0 0 0 15 0.0000 4 150 780 2760 8025 Deferred\001 +4 0 -1 0 0 0 15 0.0000 4 195 450 6075 6952 Smtp\001 +4 0 -1 0 0 0 15 0.0000 4 150 495 6052 5925 Local\001 +4 0 -1 0 0 0 15 0.0000 4 195 375 6120 8002 Pipe\001 +4 0 -1 0 0 0 15 0.0000 4 150 930 7372 8025 UUCP etc.\001 +4 0 -1 0 0 0 15 0.0000 4 150 690 7455 6975 Internet\001 +4 0 -1 0 0 0 15 0.0000 4 150 705 7447 5925 Mailbox\001 diff --git a/postfix/html/goals.html b/postfix/html/goals.html new file mode 100644 index 000000000..ec019edd8 --- /dev/null +++ b/postfix/html/goals.html @@ -0,0 +1,132 @@ + + + + +Postfix Overview - Goals and Features + + + + + +

Postfix +Overview - Goals and Features

+ +
+ +Up one level | Introduction | Goals and features | Global architecture | Queue Management | Security + +

Primary goals

+ +The goal of the Postfix project is to implement a viable alternative +to the UNIX Sendmail program. Specific goals, and the ways that +Postfix attempts to achieve them are: + +
    + +
  • Wide dissemination. Postfix must be adopted by lots of people +in order to make a significant impact on Internet mail performance +and security. Therefore the software is given away for free, with +no strings attached to it. + +

    + +

  • Performance. Postfix is up to three times as fast as its nearest +competitor. A desktop PC running Postfix can receive and deliver +a million different messages per day. Postfix uses web server +tricks to reduce process creation overhead and uses other tricks +to reduce file system overhead, without compromising reliability. + +

    + +

  • Compatibility. Postfix is designed to be sendmail-compatible +to make migration easy. Postfix supports /var[/spool]/mail, +/etc/aliases, NIS, and ~/.forward files. +However, Postfix also attempts to be easy to administer, and +therefore it does not use sendmail.cf. + +

    + +

  • Safety and robustness. Postfix is designed to behave rationally +under stress. When the local system runs out of disk space or +memory, the Postfix software backs off, instead of making the +problem worse. By design, no Postfix program keeps growing as the +number of messages etc. increases. Postfix is designed to stay in +control. + +

    + +

  • Flexibility. Postfix is built from over a dozen little programs +that each perform only one specific task: receive a message via +SMTP, deliver a message via SMTP, deliver a message locally, rewrite +an address, and so on. Sites with specific requirements can replace +one or more little programs by alternative versions. And it is easy +to disable functionality, too: firewalls and client workstations +don't need local delivery at all. + +

    + +

  • Security. Postfix uses multiple layers of defense to protect +the local system against intruders. Almost every Postfix daemon +can run in a chroot jail with fixed low privileges. There +is no direct path from the network to the security-sensitive local +delivery programs - an intruder has to break through several other +programs first. Postfix does not even trust the contents of its +own queue files, or the contents of its own IPC messages. Postfix +avoids placing sender-provided information into shell environment +variables. Last but not least, no Postfix program is set-uid. + +
+ +

Other significant features of interest

+ +
    + +
  • Multiple transports. In +the past the author has configured Sendmail systems that could +relay between Internet, DECnet, X.400 and UUCP. Postfix is designed +to be flexible enough that it can operate in such environments +without requiring virtual domain or alias kludges. However, the +initial release only talks SMTP, and has only limited support for +UUCP. + +

    + +

  • Virtual domains. In the most +common case, adding support for a virtual domain requires change +to only a single Postfix lookup table. Other mailers usually need +multiple levels of aliasing or redirection to achieve the same +result. + +

    + +

  • UCE control. Postfix can restrict what +hosts can relay their mail through a Postfix system, and supports +restrictions on what mail is allowed to come in. Postfix implements +the usual suspects: blacklists, RBL lookups, HELO/sender DNS +lookups. Content filtering hasn't been implemented yet. + +

    + +

  • Table lookups. Postfix does not yet +implement an address rewriting language. Instead it makes extensive +use of table lookups. Tables can be local dbm or db +files, or networked NIS or NetInfo maps. Adding +support for other lookup mechanisms is relatively easy. + +
+ +
+ +Up one level | Introduction | Goals and features | Global architecture | Queue Management | Security + + + + diff --git a/postfix/html/inbound.fig b/postfix/html/inbound.fig new file mode 100644 index 000000000..10e8c66d7 --- /dev/null +++ b/postfix/html/inbound.fig @@ -0,0 +1,85 @@ +#FIG 3.1 +Landscape +Center +Inches +1200 2 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 5850 2775 600 300 5250 2475 6450 3075 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 5850 3825 600 300 5250 3525 6450 4125 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 7500 3300 600 300 6900 3000 8100 3600 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 7500 2250 600 300 6900 1950 8100 2550 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 9000 2250 600 300 8400 1950 9600 2550 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 2850 2775 600 300 2250 2475 3450 3075 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 6375 3675 6975 3450 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 8100 3300 8550 3300 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 6375 2925 6975 3150 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 3450 2775 3900 2775 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 1800 2775 2250 2775 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 4800 2775 5250 2775 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 4800 3825 5250 3825 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 3900 3525 4800 3525 4800 4125 3900 4125 3900 3525 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 1 2 + 0 0 1.00 60.00 120.00 + 5850 4125 5850 4500 +2 1 0 1 -1 7 2 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 4650 4650 5400 4050 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 7500 2550 7500 3000 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 1 2 + 0 0 1.00 60.00 120.00 + 7500 3600 7500 4050 +2 1 0 1 -1 7 2 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 8700 4200 7950 3525 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 900 2475 1800 2475 1800 3075 900 3075 900 2475 +2 2 0 1 -1 6 1 0 20 0.000 0 0 -1 0 0 5 + 3900 2475 4800 2475 4800 3075 3900 3075 3900 2475 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 8550 3000 9450 3000 9450 3600 8550 3600 8550 3000 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 3900 4500 4800 4500 4800 5100 3900 5100 3900 4500 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 5400 4500 6300 4500 6300 5100 5400 5100 5400 4500 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 7050 4050 7950 4050 7950 4650 7050 4650 7050 4050 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 8550 4050 9450 4050 9450 4650 8550 4650 8550 4050 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 8550 2475 7875 3075 +2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 5025 1725 9825 1725 9825 5325 5025 5325 5025 1725 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 1 3 + 0 0 1.00 60.00 120.00 + 8025 3150 8550 2775 9525 2775 +4 0 -1 0 0 0 15 0.0000 4 150 690 3990 3892 Internet\001 +4 0 -1 0 0 0 15 0.0000 4 150 405 1102 2850 local\001 +4 0 -1 0 0 0 15 0.0000 4 195 750 3945 2827 maildrop\001 +4 0 -1 0 0 0 15 0.0000 4 195 570 5545 2827 pickup\001 +4 0 -1 0 0 0 15 0.0000 4 195 675 7122 3352 cleanup\001 +4 0 -1 0 0 0 15 0.0000 4 150 630 7165 2325 rewrite\001 +4 0 -1 0 0 0 15 0.0000 4 195 780 8580 3352 incoming\001 +4 0 -1 0 0 0 15 0.0000 4 150 540 8710 4425 virtual\001 +4 0 -1 0 0 0 15 0.0000 4 150 825 7055 4425 canonical\001 +4 0 -1 0 0 0 15 0.0000 4 105 600 5500 4875 access\001 +4 0 -1 0 0 0 15 0.0000 4 150 405 4147 4875 RBL\001 +4 0 -1 0 0 0 15 0.0000 4 150 630 8685 2325 bounce\001 +4 0 -1 0 0 0 15 0.0000 4 150 945 2347 2850 "sendmail"\001 +4 0 -1 0 0 0 15 0.0000 4 195 525 5557 3877 smtpd\001 diff --git a/postfix/html/inbound.gif b/postfix/html/inbound.gif new file mode 100644 index 000000000..7384a6e5c Binary files /dev/null and b/postfix/html/inbound.gif differ diff --git a/postfix/html/index.html b/postfix/html/index.html new file mode 100644 index 000000000..69ef506e6 --- /dev/null +++ b/postfix/html/index.html @@ -0,0 +1,50 @@ + + + + +Postfix Mail System Documentation + + + + + +

Postfix Mail System Documentation

+ +
+ +

Table of contents

+ +
    + +
  • Postfix Overview. A gentle introduction +to the Postfix mail system. + +

    + +

  • Postfix Anatomy. The programs that +make up the Postfix mail system, and how mail flows through it. + +

    + +

  • Postfix Configuration. How to configure +the system, from the very basic to the very advanced. + +

    + +

  • Postfix FAQ. Look here for quick answers +to common questions. + +
+ +
+ +
    + +
  • About the author. +The author of TCP Wrapper, co-author of SATAN, and other software. + +
  • + + + + diff --git a/postfix/html/local.8.html b/postfix/html/local.8.html new file mode 100644 index 000000000..c2b28458d --- /dev/null +++ b/postfix/html/local.8.html @@ -0,0 +1,332 @@ +
    +
    +
    +
    +LOCAL(8)                                                 LOCAL(8)
    +
    +
    +NAME
    +       local - Postfix local mail delivery
    +
    +SYNOPSIS
    +       local [generic Postfix daemon options]
    +
    +DESCRIPTION
    +       The  local  daemon  processes  delivery  requests from the
    +       Postfix queue manager to deliver mail to local recipients.
    +       Each  delivery  request  specifies  a queue file, a sender
    +       address, a domain or host to deliver to, and one  or  more
    +       recipients.   This program expects to be run from the mas-
    +       ter(8) process manager.
    +
    +       The local daemon updates queue files and marks  recipients
    +       as finished, or it informs the queue manager that delivery
    +       should be tried again at a later  time.  Delivery  problem
    +       reports  are  sent  to the bounce(8) or defer(8) daemon as
    +       appropriate.
    +
    +SYSTEM-WIDE AND USER-LEVEL ALIASING
    +       The system adminstrator can set up one or more system-wide
    +       sendmail-style  alias databases.  Users can have sendmail-
    +       style ~/.forward files.  Mail for name is delivered to the
    +       alias  name,  to  destinations  in  ~name/.forward, to the
    +       mailbox owned by the user name, or  it  is  sent  back  as
    +       undeliverable.
    +
    +       An  alias  or  ~/.forward file may list any combination of
    +       external  commands,  destination  file  names,   :include:
    +       directives,  or mail addresses.  See aliases(5) for a pre-
    +       cise description. Each line in a user's .forward file  has
    +       the same syntax as the right-hand part of an alias.
    +
    +       When  an  address  is  found  in  its own alias expansion,
    +       delivery is made to the  user  instead.  When  a  user  is
    +       listed in the user's own ~/.forward file, delivery is made
    +       to the user's mailbox instead.  An empty  ~/.forward  file
    +       means do not forward mail.
    +
    +       In  order  to prevent the mail system from using up unrea-
    +       sonable  amounts  of  memory,  input  records  read   from
    +       :include:  or  from  ~/.forward  files  are broken up into
    +       chunks of length line_length_limit.
    +
    +       While expanding aliases, ~/.forward files, and so on,  the
    +       program attempts to avoid duplicate deliveries. The dupli-
    +       cate_filter_limit configuration parameter limits the  num-
    +       ber of remembered recipients.
    +
    +MAIL FORWARDING
    +       For  the sake of reliability, forwarded mail is re-submit-
    +       ted as a new message, so that each recipient has  a  sepa-
    +       rate on-file delivery status record.
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +LOCAL(8)                                                 LOCAL(8)
    +
    +
    +       In order to stop mail forwarding loops early, the software
    +       adds a Delivered-To: header with  the  envelope  recipient
    +       address.  If  mail arrives for a recipient that is already
    +       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.
    +       Mailbox  delivery  can be delegated to an external command
    +       specified with the mailbox_command  configuration  parame-
    +       ter.
    +
    +       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  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.
    +
    +EXTERNAL COMMAND DELIVERY
    +       The   allow_mail_to_commands    configuration    parameter
    +       restricts  delivery to external commands. The default set-
    +       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.
    +
    +       A limited amount of command output  (standard  output  and
    +       standard  error) is captured for inclusion with non-deliv-
    +       ery status reports.  A command is forcibly  terminated  if
    +       it  does  not  complete within command_time_limit seconds.
    +       Command exit status codes are expected to follow the  con-
    +       ventions defined in <sysexits.h>.
    +
    +       When mail is delivered on behalf of a user, the HOME, LOG-
    +       NAME, and SHELL environment variables are set accordingly.
    +       The PATH environment variable is always reset to a system-
    +       dependent default path, and the TZ (time zone) environment
    +       variable is always passed on without change.
    +
    +       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.
    +
    +
    +
    +
    +                                                                2
    +
    +
    +
    +
    +
    +LOCAL(8)                                                 LOCAL(8)
    +
    +
    +EXTERNAL FILE DELIVERY
    +       The  allow_mail_to_files configuration parameter restricts
    +       delivery to external files. The  default  setting  (alias,
    +       forward) forbids file destinations in :include: files.
    +
    +       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, prepends a >
    +       character to lines beginning with "From ", and appends  an
    +       empty line.  When the destination is a regular file, it is
    +       locked for exclusive access while delivery is in progress.
    +       In case of problems, an attempt is made to truncate a reg-
    +       ular file to its original length.
    +
    +ADDRESS EXTENSION
    +       The optional recipient_delimiter  configuration  parameter
    +       specifies  how  to  separate address extensions from local
    +       recipient names.
    +
    +       For example, with  "recipient_delimiter  =  +",  mail  for
    +       name+foo  is  delivered  to  the  alias name+foo or to the
    +       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.
    +
    +       In all cases the local daemon  prepends  a  `Delivered-To:
    +       name+foo' header line.
    +
    +DELIVERY RIGHTS
    +       Deliveries  to  external  files  and external commands are
    +       made with the rights of the receiving user on whose behalf
    +       the  delivery  is made.  In the absence of a user context,
    +       the local daemon uses the owner rights  of  the  :include:
    +       file or alias database.  When those files are owned by the
    +       superuser, delivery is made with the rights specified with
    +       the default_privs configuration parameter.
    +
    +STANDARDS
    +       RFC 822 (ARPA Internet Text Messages)
    +
    +DIAGNOSTICS
    +       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 afterwards.
    +
    +       Depending  on the setting of the notify_classes parameter,
    +       the postmaster is notified of bounces and of  other  trou-
    +       ble.
    +
    +BUGS
    +       For  security  reasons,  the  message  delivery  status of
    +       external commands or of external  files  is  never  check-
    +       pointed to file. As a result, the program may occasionally
    +       deliver more than once to  a  command  or  external  file.
    +
    +
    +
    +                                                                3
    +
    +
    +
    +
    +
    +LOCAL(8)                                                 LOCAL(8)
    +
    +
    +       Better safe than sorry.
    +
    +       Mutually-recursive  aliases  or  ~/.forward  files are not
    +       detected early.  The resulting  mail  forwarding  loop  is
    +       broken by the use of the Delivered-To: message header.
    +
    +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
    +       command after a configuration change.
    +
    +Miscellaneous
    +       alias_maps
    +              List of alias databases.
    +
    +       home_mailbox
    +              Pathname of a mailbox relative  to  a  user's  home
    +              directory.    Specify   maildir  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.
    +
    +       mailbox_command
    +              External command to use for mailbox delivery.
    +
    +       recipient_delimiter
    +              Separator between username and address extension.
    +
    +Locking controls
    +       deliver_lock_attempts
    +              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
    +              acquire an exclusive lock.
    +
    +       stale_lock_time
    +              Limit the time after which a stale lock is removed.
    +
    +Resource controls
    +       command_time_limit
    +              Limit  the  amount of time for delivery to external
    +              command.
    +
    +       duplicate_filter_limit
    +              Limit the size of the duplicate filter for  results
    +              from alias etc. expansion.
    +
    +
    +
    +                                                                4
    +
    +
    +
    +
    +
    +LOCAL(8)                                                 LOCAL(8)
    +
    +
    +       line_length_limit
    +              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
    +              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
    +              default_destination_recipient_limit parameter.
    +
    +Security controls
    +       allow_mail_to_commands
    +              Restrict the usage of  mail  delivery  to  external
    +              command.
    +
    +       allow_mail_to_files
    +              Restrict  the  usage  of  mail delivery to external
    +              file.
    +
    +       default_privs
    +              Default rights for delivery  to  external  file  or
    +              command.
    +
    +HISTORY
    +       The  Delivered-To:  header  appears in the qmail system by
    +       Daniel Bernstein.
    +
    +       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.
    +
    +AUTHOR(S)
    +       Wietse Venema
    +       IBM T.J. Watson Research
    +       P.O. Box 704
    +       Yorktown Heights, NY 10598, USA
    +
    +
    +
    +
    +
    +
    +
    +                                                                5
    +
    +
    +
    diff --git a/postfix/html/mailq.1.html b/postfix/html/mailq.1.html new file mode 120000 index 000000000..781db46a9 --- /dev/null +++ b/postfix/html/mailq.1.html @@ -0,0 +1 @@ +sendmail.1.html \ No newline at end of file diff --git a/postfix/html/master.8.html b/postfix/html/master.8.html new file mode 100644 index 000000000..2f7ebabc4 --- /dev/null +++ b/postfix/html/master.8.html @@ -0,0 +1,200 @@ +
    +
    +
    +
    +MASTER(8)                                               MASTER(8)
    +
    +
    +NAME
    +       master - Postfix master process
    +
    +SYNOPSIS
    +       master [-c config_dir] [-D] [-t] [-v]
    +
    +DESCRIPTION
    +       The  master daemon is the resident process that runs Post-
    +       fix daemons on demand: daemons to send or receive messages
    +       via  the  network,  daemons  to deliver mail locally, etc.
    +       These daemons are created on demand up to  a  configurable
    +       maximum number per service.
    +
    +       Postfix  daemons terminate voluntarily, either after being
    +       idle for a configurable amount of time,  or  after  having
    +       serviced  a configurable number of requests. The exception
    +       to this rule is the resident Postfix queue manager.
    +
    +       The behavior of the master daemon  is  controlled  by  the
    +       master.cf  configuration file. The table specifies zero or
    +       more servers in the UNIX or INET domain, or  servers  that
    +       take  requests  from a FIFO. Precise configuration details
    +       are given in the master.cf file, and in the  manual  pages
    +       of the respective daemons.
    +
    +       Options:
    +
    +       -c config_dir
    +              Read  the main.cf and master.cf configuration files
    +              in the named directory.
    +
    +       -D     After initialization, run a debugger on the  master
    +              process.  The  debugging  command is specified with
    +              the debugger_command in the main.cf global configu-
    +              ration file.
    +
    +       -t     Test  mode. Return a zero exit status when the mas-
    +              ter.pid lock file does not exist or when that  file
    +              is  not  locked.   This is evidence that the master
    +              daemon is not running.
    +
    +       -v     Enable verbose logging for debugging purposes. This
    +              option is passed on to child processes. Multiple -v
    +              options make the software increasingly verbose.
    +
    +       Signals:
    +
    +       SIGHUP Upon receipt of a HUP signal (e.g.,  after  postfix
    +              reload), the master process re-reads its configura-
    +              tion files. If a service has been removed from  the
    +              master.cf  file,  its  running processes are termi-
    +              nated immediately.   Otherwise,  running  processes
    +              are  allowed to terminate as soon as is convenient,
    +              so that changes in  configuration  settings  affect
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +MASTER(8)                                               MASTER(8)
    +
    +
    +              only new service requests.
    +
    +       SIGTERM
    +              Upon  receipt of a TERM signal (e.g., after postfix
    +              abort), the master process passes the signal on  to
    +              its child processes and terminates.  This is useful
    +              for an emergency shutdown. Normally one would  ter-
    +              minate  only  the  master  (postfix stop) and allow
    +              running processes to finish what they are doing.
    +
    +DIAGNOSTICS
    +       Problems are reported to syslogd(8).
    +
    +BUGS
    +ENVIRONMENT
    +       MAIL_DEBUG
    +              After initialization, start a debugger as specified
    +              with  the  debugger_command configuration parameter
    +              in the main.cf configuration file.
    +
    +       MAIL_CONFIG
    +              Directory with configuration files.
    +
    +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
    +       command after a configuration change.
    +
    +Miscellaneous
    +       mail_owner
    +              The  owner  of  the  mail queue and of most Postfix
    +              processes.
    +
    +       program_directory
    +              Directory with Postfix support  programs  and  dae-
    +              mons.
    +
    +       queue_directory
    +              Top-level  directory  of the Postfix queue. This is
    +              also the root directory of Postfix daemons that run
    +              chrooted.
    +
    +Resource controls
    +       default_process_limit
    +              Default  limit for the number of simultaneous child
    +              processes that provide a given service.
    +
    +       max_idle
    +              Limit the time in  seconds  that  a  child  process
    +              waits between service requests.
    +
    +       max_use
    +              Limit  the  number of service requests handled by a
    +
    +
    +
    +                                                                2
    +
    +
    +
    +
    +
    +MASTER(8)                                               MASTER(8)
    +
    +
    +              child process.
    +
    +FILES
    +       /etc/postfix/main.cf: global configuration file.
    +       /etc/postfix/master.cf: master process configuration file.
    +       /var/spool/postfix/pid/master.pid: master lock file.
    +
    +SEE ALSO
    +       qmgr(8) queue manager
    +       pickup(8) local mail pickup
    +       syslogd(8) system logging
    +
    +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
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                3
    +
    +
    +
    diff --git a/postfix/html/motivation.html b/postfix/html/motivation.html new file mode 100644 index 000000000..9eccb0923 --- /dev/null +++ b/postfix/html/motivation.html @@ -0,0 +1,60 @@ + + + + +Postfix Overview - Introduction + + + + + +

    Postfix +Overview - Introduction

    + +
    + +Up one level | Introduction | Goals and features | Global architecture | Queue Management | Security + +

    + +Postfix is the freeware project that I started during my sabattical +year in the USA while visiting IBM T.J. Watson Research. I am +grateful to IBM for the opportunity to write this software and for +their permission to give it away. + +

    + +Postfix is my attempt to provide an alternative to the widely-used +Sendmail program. Postfix +attempts to be fast, easy to administer, and hopefully secure, +while at the same time being sendmail compatible enough to not +upset your users. + +

    + +The original plan was to release this software under a different +name, VMailer. With the release in sight, IBM's lawyers discovered +that VMailer was too similar to an existing trade mark. So, the +program will go through its life as Postfix instead. + +

    + +Postfix is a direct competitor to the +qmail by Dan Bernstein. That's competitor, not enemy. I'm sure +that friendly competition will help to improve both programs. + +


    + +Up one level | Introduction | Goals and features | Global architecture | Queue Management | Security + + + + diff --git a/postfix/html/newaliases.1.html b/postfix/html/newaliases.1.html new file mode 120000 index 000000000..781db46a9 --- /dev/null +++ b/postfix/html/newaliases.1.html @@ -0,0 +1 @@ +sendmail.1.html \ No newline at end of file diff --git a/postfix/html/outbound.fig b/postfix/html/outbound.fig new file mode 100644 index 000000000..83eb89444 --- /dev/null +++ b/postfix/html/outbound.fig @@ -0,0 +1,97 @@ +#FIG 3.1 +Landscape +Center +Inches +1200 2 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 4350 3300 600 300 3750 3000 4950 3600 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 4350 2250 600 300 3750 1950 4950 2550 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 6000 4350 600 300 5400 4050 6600 4650 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 6000 3300 600 300 5400 3000 6600 3600 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 6000 2250 600 300 5400 1950 6600 2550 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 2700 2250 600 300 2100 1950 3300 2550 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 3150 3300 3750 3300 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 4950 3300 5400 3300 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 4725 3075 5625 2475 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 4725 3525 5625 4125 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 6600 2250 7050 2250 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 6600 3300 7050 3300 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 6600 4350 7050 4350 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 7087 4050 7987 4050 7987 4650 7087 4650 7087 4050 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 7050 3000 7950 3000 7950 3600 7050 3600 7050 3000 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 4350 1500 4350 1950 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 4350 2550 4350 3000 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 6000 1500 6000 1950 +2 1 0 1 -1 7 2 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 7200 1350 6450 2025 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 1 2 + 0 0 1.00 60.00 120.00 + 4350 3600 4350 4050 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 7050 1950 7950 1950 7950 2550 7050 2550 7050 1950 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 3900 900 4800 900 4800 1500 3900 1500 3900 900 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 5550 900 6450 900 6450 1500 5550 1500 5550 900 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 7050 900 7950 900 7950 1500 7050 1500 7050 900 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 3900 4050 4800 4050 4800 4650 3900 4650 3900 4050 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 1 2 + 0 0 1.00 60.00 120.00 + 3075 2475 3975 3075 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 4 + 0 0 1.00 60.00 120.00 + 3150 3450 3450 3450 3450 4350 3150 4350 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 2250 3000 3150 3000 3150 3600 2250 3600 2250 3000 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 2250 4050 3150 4050 3150 4650 2250 4650 2250 4050 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 1650 3300 2250 3300 +2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 525 675 6825 675 6825 5325 525 5325 525 675 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 1 4 + 0 0 1.00 60.00 120.00 + 2250 3450 1950 3450 1950 4350 2250 4350 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 750 3000 1650 3000 1650 3600 750 3600 750 3000 +4 0 -1 0 0 0 15 0.0000 4 150 450 4125 3330 qmgr\001 +4 0 -1 0 0 0 15 0.0000 4 150 630 4005 2325 resolve\001 +4 0 -1 0 0 0 15 0.0000 4 195 360 5820 4402 pipe\001 +4 0 -1 0 0 0 15 0.0000 4 180 420 5790 3345 smtp\001 +4 0 -1 0 0 0 15 0.0000 4 150 405 5797 2325 local\001 +4 0 -1 0 0 0 15 0.0000 4 150 585 5677 1275 aliases\001 +4 0 -1 0 0 0 15 0.0000 4 150 735 7102 1275 .forward\001 +4 0 -1 0 0 0 15 0.0000 4 150 690 7155 3375 Internet\001 +4 0 -1 0 0 0 15 0.0000 4 150 930 7147 4425 UUCP etc.\001 +4 0 -1 0 0 0 15 0.0000 4 150 675 7132 2325 mailbox\001 +4 0 -1 0 0 0 15 0.0000 4 150 630 2385 2325 bounce\001 +4 0 -1 0 0 0 15 0.0000 4 150 525 2407 3375 active\001 +4 0 -1 0 0 0 15 0.0000 4 150 735 2302 4425 deferred\001 +4 0 -1 0 0 0 15 0.0000 4 150 810 3915 4425 relocated\001 +4 0 -1 0 0 0 15 0.0000 4 195 780 790 3352 incoming\001 +4 0 -1 0 0 0 15 0.0000 4 180 780 3930 1245 transport\001 diff --git a/postfix/html/outbound.gif b/postfix/html/outbound.gif new file mode 100644 index 000000000..84c2dc60c Binary files /dev/null and b/postfix/html/outbound.gif differ diff --git a/postfix/html/overview.html b/postfix/html/overview.html new file mode 100644 index 000000000..9eea539a0 --- /dev/null +++ b/postfix/html/overview.html @@ -0,0 +1,59 @@ + + + + +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 new file mode 100644 index 000000000..1afe00722 --- /dev/null +++ b/postfix/html/pickup.8.html @@ -0,0 +1,134 @@ +
    +
    +
    +
    +PICKUP(8)                                               PICKUP(8)
    +
    +
    +NAME
    +       pickup - Postfix local mail pickup
    +
    +SYNOPSIS
    +       pickup [generic Postfix daemon options]
    +
    +DESCRIPTION
    +       The  pickup  daemon waits for hints that new mail has been
    +       dropped into the world-writable  maildrop  directory,  and
    +       feeds  it into the cleanup(8) daemon.  Ill-formatted files
    +       are deleted without notifying the originator.   This  pro-
    +       gram expects to be run from the master(8) process manager.
    +
    +STANDARDS
    +       None. The pickup daemon does not interact with the outside
    +       world.
    +
    +SECURITY
    +       The  pickup  daemon runs with superuser privileges so that
    +       it 1) can open a queue file with the rights of the submit-
    +       ting  user and 2) can access the Postfix private IPC chan-
    +       nels.  On the positive side, the program can run chrooted,
    +       opens no files for writing, is careful about what files it
    +       opens for reading, and does not actually  touch  any  data
    +       that is sent to its public service endpoint.
    +
    +DIAGNOSTICS
    +       Problems and transactions are logged to syslogd(8).
    +
    +BUGS
    +       The  pickup daemon copies mail from file to the cleanup(8)
    +       daemon.  It could avoid message copying overhead by  send-
    +       ing  a  file descriptor instead of file data, but then the
    +       already complex cleanup(8) daemon would have to deal  with
    +       unfiltered user data.
    +
    +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
    +       command after a configuration change.
    +
    +Miscellaneous
    +       mail_owner
    +              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
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +PICKUP(8)                                               PICKUP(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
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                2
    +
    +
    +
    diff --git a/postfix/html/pipe.8.html b/postfix/html/pipe.8.html new file mode 100644 index 000000000..c5eb2c0ad --- /dev/null +++ b/postfix/html/pipe.8.html @@ -0,0 +1,266 @@ +
    +
    +
    +
    +PIPE(8)                                                   PIPE(8)
    +
    +
    +NAME
    +       pipe - Postfix delivery to external command
    +
    +SYNOPSIS
    +       pipe [generic Postfix daemon options] command_attributes...
    +
    +DESCRIPTION
    +       The  pipe daemon processes requests from the Postfix queue
    +       manager to deliver messages  to  external  commands.  Each
    +       delivery request specifies a queue file, a sender address,
    +       a domain or host to deliver to, and one  or  more  recipi-
    +       ents.   This  program expects to be run from the master(8)
    +       process manager.
    +
    +       The pipe daemon updates queue files and  marks  recipients
    +       as finished, or it informs the queue manager that delivery
    +       should be tried again at a later  time.  Delivery  problem
    +       reports  are  sent  to the bounce(8) or defer(8) daemon as
    +       appropriate.
    +
    +COMMAND ATTRIBUTE SYNTAX
    +       The external command attributes are given in the master.cf
    +       file at the end of a service definition.  The syntax is as
    +       follows:
    +
    +       flags=F> (optional)
    +              Optional message processing flags.  By  default,  a
    +              message is copied unchanged.
    +
    +              F      Prepend  a "From sender time_stamp" envelope
    +                     header to  the  message  content.   This  is
    +                     expected by, for example, UUCP software. The
    +                     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-
    +                     ware.
    +
    +       user=username (required)
    +              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.
    +
    +       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
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +PIPE(8)                                                   PIPE(8)
    +
    +
    +              corresponding information from  the  Postfix  queue
    +              manager delivery request:
    +
    +              ${extension}
    +                     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-
    +                     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-
    +                     ents.
    +
    +              ${nexthop}
    +                     This macro expands to the next-hop hostname.
    +
    +              ${recipient}
    +                     This macro expands to the complete recipient
    +                     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
    +                     address.
    +
    +              ${user}
    +                     This macro expands to the username part of a
    +                     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
    +                     arguments as there are recipients.
    +
    +       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.
    +
    +
    +
    +
    +
    +                                                                2
    +
    +
    +
    +
    +
    +PIPE(8)                                                   PIPE(8)
    +
    +
    +SECURITY
    +       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
    +       command after a configuration change.
    +
    +Miscellaneous
    +       mail_owner
    +              The  process  privileges  used while not running an
    +              external command.
    +
    +Resource controls
    +       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
    +              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
    +              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
    +              manager.
    +
    +SEE ALSO
    +       bounce(8) non-delivery status reports
    +       master(8) process manager
    +       qmgr(8) queue manager
    +       syslogd(8) system logging
    +
    +LICENSE
    +       The  Secure  Mailer  license must be distributed with this
    +       software.
    +
    +AUTHOR(S)
    +       Wietse Venema
    +
    +
    +
    +                                                                3
    +
    +
    +
    +
    +
    +PIPE(8)                                                   PIPE(8)
    +
    +
    +       IBM T.J. Watson Research
    +       P.O. Box 704
    +       Yorktown Heights, NY 10598, USA
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                4
    +
    +
    +
    diff --git a/postfix/html/postalias.1.html b/postfix/html/postalias.1.html new file mode 100644 index 000000000..4af185bff --- /dev/null +++ b/postfix/html/postalias.1.html @@ -0,0 +1,134 @@ +
    +
    +
    +
    +POSTALIAS(1)                                         POSTALIAS(1)
    +
    +
    +NAME
    +       postalias - Postfix alias database maintenance
    +
    +SYNOPSIS
    +       postalias [-c config_dir] [-i] [-v] [file_type:]file_name
    +       ...
    +
    +DESCRIPTION
    +       The  postalias  command  creates  a  new   Postfix   alias
    +       database, or updates an existing one. The input and output
    +       file formats are expected to be compatible  with  Sendmail
    +       version  8, and are expected to be suitable for the use as
    +       NIS alias maps.
    +
    +       While a database update is in progress, signal delivery is
    +       postponed,  and  an exclusive, advisory, lock is placed on
    +       the entire database, in order to avoid surprises in  spec-
    +       tator programs.
    +
    +       Options:
    +
    +       -c config_dir
    +              Read  the  main.cf  configuration file in the named
    +              directory.
    +
    +       -i     Incremental mode. Read entries from standard  input
    +              and  do  not  truncate  an  existing  database.  By
    +              default, postalias creates a new database from  the
    +              entries in file_name.
    +
    +       -v     Enable verbose logging for debugging purposes. Mul-
    +              tiple -v options  make  the  software  increasingly
    +              verbose.
    +
    +       Arguments:
    +
    +       file_type
    +              The type of database to be produced.
    +
    +              btree  The   output   is   a   btree   file,  named
    +                     file_name.db.  This  is  available  only  on
    +                     systems with support for db databases.
    +
    +              dbm    The  output  consists  of  two  files, named
    +                     file_name.pag and  file_name.dir.   This  is
    +                     available  only  on systems with support for
    +                     dbm databases.
    +
    +              hash   The  output  is   a   hashed   file,   named
    +                     file_name.db.   This  is  available  only on
    +                     systems with support for db databases.
    +
    +              When no file_type is specified, the  software  uses
    +              the  database  type specified via the database_type
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +POSTALIAS(1)                                         POSTALIAS(1)
    +
    +
    +              configuration parameter.   The  default  value  for
    +              this parameter depends on the host environment.
    +
    +       file_name
    +              The  name  of  the  alias database source file when
    +              rebuilding a database.
    +
    +DIAGNOSTICS
    +       Problems are logged to the standard error stream. No  out-
    +       put means no problems were detected. Duplicate entries are
    +       skipped and are flagged with a warning.
    +
    +ENVIRONMENT
    +       MAIL_CONFIG
    +              Mail configuration database.
    +
    +       MAIL_VERBOSE
    +              Enable verbose logging for debugging purposes.
    +
    +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.
    +
    +       database_type
    +              Default alias database type. On many UNIX  systems,
    +              the default type is either dbm or hash.
    +
    +STANDARDS
    +       RFC 822 (ARPA Internet Text Messages)
    +
    +SEE ALSO
    +       aliases(5) format of alias database input file.
    +       sendmail(1) mail posting and compatibility interface.
    +
    +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
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                2
    +
    +
    +
    diff --git a/postfix/html/postcat.1.html b/postfix/html/postcat.1.html new file mode 100644 index 000000000..692b9c843 --- /dev/null +++ b/postfix/html/postcat.1.html @@ -0,0 +1,68 @@ +
    +
    +
    +
    +POSTCAT(1)                                             POSTCAT(1)
    +
    +
    +NAME
    +       postcat - show Postfix queue file contents
    +
    +SYNOPSIS
    +       postcat [-v] [files...]
    +
    +DESCRIPTION
    +       The postcat command prints the contents of the named Post-
    +       fix queue files in human-readable form. If  no  files  are
    +       specified  on  the  command  line,  the program reads from
    +       standard input.
    +
    +       Options:
    +
    +       -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.
    +
    +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
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                1
    +
    +
    +
    diff --git a/postfix/html/postconf.1.html b/postfix/html/postconf.1.html new file mode 100644 index 000000000..29cfc8db0 --- /dev/null +++ b/postfix/html/postconf.1.html @@ -0,0 +1,68 @@ +
    +
    +
    +
    +POSTCONF(1)                                           POSTCONF(1)
    +
    +
    +NAME
    +       postconf - Postfix configuration utility
    +
    +SYNOPSIS
    +       postconf [-d] [-n] [-v] [parameter ...]
    +
    +DESCRIPTION
    +       The  postconf command prints the actual value of parameter
    +       (all known parameters by default).
    +
    +       Options:
    +
    +       -d     Print default parameter settings instead of  actual
    +              settings.
    +
    +       -n     Print non-default parameter settings only.
    +
    +       -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.
    +
    +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
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                1
    +
    +
    +
    diff --git a/postfix/html/postdrop.1.html b/postfix/html/postdrop.1.html new file mode 100644 index 000000000..38855b4a9 --- /dev/null +++ b/postfix/html/postdrop.1.html @@ -0,0 +1,134 @@ +
    +
    +
    +
    +POSTDROP(1)                                           POSTDROP(1)
    +
    +
    +NAME
    +       postdrop - Postfix mail posting agent
    +
    +SYNOPSIS
    +       postdrop [option ...]
    +
    +DESCRIPTION
    +       The postdrop command creates a file in the maildrop direc-
    +       tory and copies its standard input to the file.
    +
    +       The command is designed to run  with  set-gid  privileges,
    +       and  with  group  write  permission  to the maildrop queue
    +       directory.
    +
    +       The postdrop command is automatically invoked by the send-
    +       mail(1)  mail posting agent when the maildrop queue direc-
    +       tory is not writable.
    +
    +       Options:
    +
    +       -v     Enable verbose logging for debugging purposes. Mul-
    +              tiple  -v  options  make  the software increasingly
    +              verbose.
    +
    +SECURITY
    +       This program is designed so that it can run with  set-user
    +       (or group) id privileges.
    +
    +DIAGNOSTICS
    +       Fatal  errors:  malformed input, I/O error, out of memory.
    +       Problems are logged to  syslogd(8)  and  to  the  standard
    +       error  stream.   When the input is incomplete, or when the
    +       process receives a HUP, INT,  QUIT  or  TERM  signal,  the
    +       queue file is deleted.
    +
    +ENVIRONMENT
    +       The  program  deletes all environment information, because
    +       the C library can't be trusted.
    +
    +FILES
    +       /var/spool/postfix, mail queue
    +       /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
    +       configuration change.
    +
    +       queue_directory
    +              Top-level directory of the Postfix queue.  This  is
    +              also the root directory of Postfix daemons that run
    +              chrooted.
    +
    +
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +POSTDROP(1)                                           POSTDROP(1)
    +
    +
    +SEE ALSO
    +       sendmail(1) compatibility interface
    +       syslogd(8) system logging
    +
    +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
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                2
    +
    +
    +
    diff --git a/postfix/html/postfix.1.html b/postfix/html/postfix.1.html new file mode 100644 index 000000000..a91f9577b --- /dev/null +++ b/postfix/html/postfix.1.html @@ -0,0 +1,200 @@ +
    +
    +
    +
    +POSTFIX(1)                                             POSTFIX(1)
    +
    +
    +NAME
    +       postfix - Postfix control program
    +
    +SYNOPSIS
    +       postfix [-c config_dir] [-D] [-v] command
    +
    +DESCRIPTION
    +       The  postfix command controls the operation of the Postfix
    +       mail system: start or stop the master daemon, do a  health
    +       check,  and other maintenance. The command sets up a stan-
    +       dardized environment and  runs  the  postfix-script  shell
    +       script to do the actual work.
    +
    +       The following commands are implemented:
    +
    +       check  Validate  the  Postfix  mail  system configuration.
    +              Warn about bad directory/file ownership or  permis-
    +              sions, and create missing directories.
    +
    +       start  Start  the  Postfix mail system. This also runs the
    +              configuration check described above.
    +
    +       stop   Stop the Postfix mail system in an orderly fashion.
    +              Running processes are allowed to terminate at their
    +              earliest convenience.
    +
    +              Note: in order to refresh the Postfix  mail  system
    +              after  a configuration change, do not use the start
    +              and stop commands in  succession.  Use  the  reload
    +              command instead.
    +
    +       abort  Stop the Postfix mail system abruptly. Running pro-
    +              cesses are signaled to stop immediately.
    +
    +       flush  Force delivery: attempt to deliver every message in
    +              the  deferred  mail  queue.  Normally,  attempts to
    +              deliver delayed mail happen at  regular  intervals,
    +              the interval doubling after each failed attempt.
    +
    +       reload Re-read configuration files. Running processes ter-
    +              minate at their earliest convenience.
    +
    +       The following options are implemented:
    +
    +       -c config_dir
    +              The absolute path to a directory with Postfix  con-
    +              figuration  files.  Use this to distinguish between
    +              multiple Postfix instances on the same host.
    +
    +       -D (with postfix start only)
    +              Run each Postfix daemon under control of a debugger
    +              as specified via the debugger_command configuration
    +              parameter.
    +
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +POSTFIX(1)                                             POSTFIX(1)
    +
    +
    +       -v     Enable verbose logging for debugging purposes. Mul-
    +              tiple  -v  options  make  the software increasingly
    +              verbose.
    +
    +ENVIRONMENT
    +       The postfix command sets the following  environment  vari-
    +       ables:
    +
    +       MAIL_CONFIG
    +              The Postfix configuration directory.
    +
    +       MAIL_VERBOSE
    +              This is set when the -v command-line option is pre-
    +              sent.
    +
    +       MAIL_DEBUG
    +              This is set when the -D command-line option is pre-
    +              sent.
    +
    +       The  following configuration parameters are made available
    +       as process environment variables with the same names:
    +
    +       command_directory
    +              The  directory  with   Postfix   support   commands
    +              (default: $program_directory).
    +
    +       daemon_directory
    +              The   directory   with   Postfix   daemon  programs
    +              (default: $program_directory).
    +
    +       config_directory
    +              The directory with  configuration  files  and  with
    +              administrative shell scripts.
    +
    +       queue_directory
    +              The directory with the Postfix queue directory (and
    +              with some files needed for programs  running  in  a
    +              chrooted environment).
    +
    +       mail_owner
    +              The  owner of the Postfix queue and of most Postfix
    +              processes.
    +
    +FILES
    +       $config_directory/postfix-script, administrative commands
    +
    +SEE ALSO
    +       master(8) Postfix master program
    +
    +LICENSE
    +       The Secure Mailer license must be  distributed  with  this
    +       software.
    +
    +
    +
    +
    +
    +                                                                2
    +
    +
    +
    +
    +
    +POSTFIX(1)                                             POSTFIX(1)
    +
    +
    +AUTHOR(S)
    +       Wietse Venema
    +       IBM T.J. Watson Research
    +       P.O. Box 704
    +       Yorktown Heights, NY 10598, USA
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                3
    +
    +
    +
    diff --git a/postfix/html/postkick.1.html b/postfix/html/postkick.1.html new file mode 100644 index 000000000..0a23c3b70 --- /dev/null +++ b/postfix/html/postkick.1.html @@ -0,0 +1,134 @@ +
    +
    +
    +
    +POSTKICK(1)                                           POSTKICK(1)
    +
    +
    +NAME
    +       postkick - kick a Postfix service
    +
    +SYNOPSIS
    +       postkick [-c config_dir] [-v] class service request
    +
    +DESCRIPTION
    +       The  postkick  command sends request to the specified ser-
    +       vice over a local transport channel.  This  command  makes
    +       Postfix  private  IPC  accessible for use in, for example,
    +       shell scripts.
    +
    +       Options:
    +
    +       -c config_dir
    +              Read configuration information from main.cf in  the
    +              named configuration directory.
    +
    +       -v     Enable verbose logging for debugging purposes. Mul-
    +              tiple -v options  make  the  software  increasingly
    +              verbose.
    +
    +       Arguments:
    +
    +       class  Name  of  a  class  of local transport channel end-
    +              points, either  public  (accessible  by  any  local
    +              user) or private (administrative access only).
    +
    +       service
    +              The  name  of a local transport endpoint within the
    +              named class.
    +
    +       request
    +              A string. The list of valid  requests  is  service-
    +              specific.
    +
    +DIAGNOSTICS
    +       Problems and transactions are logged to the standard error
    +       stream.
    +
    +ENVIRONMENT
    +       MAIL_CONFIG
    +              Directory with Postfix configuration files.
    +
    +       MAIL_VERBOSE
    +              Enable verbose logging.
    +
    +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.
    +
    +       queue_directory
    +              Location of the Postfix queue, and of the local IPC
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +POSTKICK(1)                                           POSTKICK(1)
    +
    +
    +              communication endpoints.
    +
    +SEE ALSO
    +       qmgr(8) queue manager trigger protocol
    +       pickup(8) local pickup daemon
    +
    +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
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                2
    +
    +
    +
    diff --git a/postfix/html/postlock.1.html b/postfix/html/postlock.1.html new file mode 100644 index 000000000..0ba4489f2 --- /dev/null +++ b/postfix/html/postlock.1.html @@ -0,0 +1,134 @@ +
    +
    +
    +
    +POSTLOCK(1)                                           POSTLOCK(1)
    +
    +
    +NAME
    +       postlock - lock mail folder and execute command
    +
    +SYNOPSIS
    +       postlock [-c config_dir] [-v] file command...
    +
    +DESCRIPTION
    +       The  postlock command locks file for exclusive access, and
    +       executes command. The locking method  is  compatible  with
    +       the Postfix UNIX-style local delivery agent.
    +
    +       Options:
    +
    +       -c config_dir
    +              Read  configuration information from main.cf in the
    +              named configuration directory.
    +
    +       -v     Enable verbose mode for debugging purposes.  Multi-
    +              ple  -v options make the software increasingly ver-
    +              bose.
    +
    +       Arguments:
    +
    +       file   A mailbox file. The  user  should  have  read/write
    +              permission.
    +
    +       command...
    +              The  command  to  execute  while file is locked for
    +              exclusive  access.    The   command   is   executed
    +              directly,  i.e.  without  interpretation by a shell
    +              command interpreter.
    +
    +DIAGNOSTICS
    +       The result status is 255 (on some systems: -1) when  post-
    +       lock  could  not  perform the requested operation.  Other-
    +       wise, the exit status is the exit status from the command.
    +
    +BUGS
    +       With  remote  file  systems, the ability to acquire a lock
    +       does not necessarily  eliminate  access  conflicts.  Avoid
    +       file access by processes running on different machines.
    +
    +ENVIRONMENT
    +       MAIL_CONFIG
    +              Directory with Postfix configuration files.
    +
    +       MAIL_VERBOSE
    +              Enable verbose logging.
    +
    +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.
    +
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +POSTLOCK(1)                                           POSTLOCK(1)
    +
    +
    +Locking controls
    +       deliver_lock_attempts
    +              Limit  the  number of attempts to acquire an exclu-
    +              sive lock.
    +
    +       deliver_lock_delay
    +              Time in  seconds  between  successive  attempts  to
    +              acquire an exclusive lock.
    +
    +       stale_lock_time
    +              Limit the time after which a stale lock is removed.
    +
    +Resource controls
    +       fork_attempts
    +              Number of attempts to fork() a process before  giv-
    +              ing up.
    +
    +       fork_delay
    +              Delay   in   seconds   between   successive  fork()
    +              attempts.
    +
    +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
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                2
    +
    +
    +
    diff --git a/postfix/html/postlog.1.html b/postfix/html/postlog.1.html new file mode 100644 index 000000000..e6464549a --- /dev/null +++ b/postfix/html/postlog.1.html @@ -0,0 +1,68 @@ +
    +
    +
    +
    +POSTLOG(1)                                             POSTLOG(1)
    +
    +
    +NAME
    +       postlog - Postfix-compatible logging utility
    +
    +SYNOPSIS
    +       postlog [-i] [-p priority] [-t tag] [-v] [text...]
    +
    +DESCRIPTION
    +       The  postlog  command implements a Postfix-compatible log-
    +       ging interface for use in, for example, shell scripts.
    +
    +       By default, postlog logs the text  given  on  the  command
    +       line as one record. If no text is specified on the command
    +       line, postlog reads from  standard  input  and  logs  each
    +       input line as one record.
    +
    +       Logging  is  sent  to  syslogd(8); when the standard error
    +       stream is connected to a terminal, logging is  sent  there
    +       as well.
    +
    +       The following options are implemented:
    +
    +       -i     Include the process ID in the logging tag.
    +
    +       -p priority
    +              Specifies  the  logging  severity:  info (default),
    +              warn, error, fatal, or panic.
    +
    +       -t tag Specifies the logging tag, that is, the identifying
    +              name  that appears at the beginning of each logging
    +              record.
    +
    +       -v     Enable verbose logging for debugging purposes. Mul-
    +              tiple  -v  options  make  the software increasingly
    +              verbose.
    +
    +SEE ALSO
    +       syslogd(8) syslog daemon.
    +
    +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
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                1
    +
    +
    +
    diff --git a/postfix/html/postmap.1.html b/postfix/html/postmap.1.html new file mode 100644 index 000000000..950b96b2b --- /dev/null +++ b/postfix/html/postmap.1.html @@ -0,0 +1,134 @@ +
    +
    +
    +
    +POSTMAP(1)                                             POSTMAP(1)
    +
    +
    +NAME
    +       postmap - Postfix lookup table management
    +
    +SYNOPSIS
    +       postmap [-c config_dir] [-i] [-v] [file_type:]file_name
    +
    +DESCRIPTION
    +       The postmap command creates a new Postfix lookup table, or
    +       updates an existing one. The input and output formats  are
    +       expected to be compatible with:
    +
    +           makemap file_type file_name < file_name
    +
    +       While  the table update is in progress, signal delivery is
    +       postponed, and an exclusive, advisory, lock is  placed  on
    +       the entire table, in order to avoid surprises in spectator
    +       programs.
    +
    +       The format of a lookup table input file is as follows:
    +
    +       o      Blank lines are ignored.  So  are  lines  beginning
    +              with `#'.
    +
    +       o      A table entry has the form
    +
    +                   key whitespace value
    +
    +       o      A  line  that  starts with whitespace continues the
    +              preceding line.
    +
    +       The key and value are processed as is,  except  that  sur-
    +       rounding  white space is stripped off. Unlike with Postfix
    +       alias databases, quotes cannot be used to  protect  lookup
    +       keys  that  contain  special  characters  such  as  `#' or
    +       whitespace. The key is mapped to lowercase to make mapping
    +       lookups case insensitive.
    +
    +       Options:
    +
    +       -c config_dir
    +              Read  the  main.cf  configuration file in the named
    +              directory.
    +
    +       -i     Incremental mode. Read entries from standard  input
    +              and  do  not  truncate  an  existing  database.  By
    +              default, postmap creates a new  database  from  the
    +              entries in file_name.
    +
    +       -v     Enable verbose logging for debugging purposes. Mul-
    +              tiple -v options  make  the  software  increasingly
    +              verbose.
    +
    +       Arguments:
    +
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +POSTMAP(1)                                             POSTMAP(1)
    +
    +
    +       file_type
    +              The type of database to be produced.
    +
    +              btree  The  output  file  is  a  btree  file, named
    +                     file_name.db.  This  is  available  only  on
    +                     systems with support for db databases.
    +
    +              dbm    The  output  consists  of  two  files, named
    +                     file_name.pag and  file_name.dir.   This  is
    +                     available  only  on systems with support for
    +                     dbm databases.
    +
    +              hash   The output file  is  a  hashed  file,  named
    +                     file_name.db.   This  is  available  only on
    +                     systems with support for db databases.
    +
    +              When no file_type is specified, the  software  uses
    +              the  database  type specified via the database_type
    +              configuration parameter.
    +
    +       file_name
    +              The name of  the  lookup  table  source  file  when
    +              rebuilding a database.
    +
    +DIAGNOSTICS
    +       Problems and transactions are logged to the standard error
    +       stream. No output means no problems. Duplicate entries are
    +       skipped and are flagged with a warning.
    +
    +ENVIRONMENT
    +       MAIL_CONFIG
    +              Mail configuration database
    +
    +       MAIL_VERBOSE
    +              Enable verbose logging for debugging purposes.
    +
    +CONFIGURATION PARAMETERS
    +       database_type
    +              Default  output  database  type.  On many UNIX sys-
    +              tems, the default database type is either  hash  or
    +              dbm.
    +
    +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
    +
    +
    +
    +
    +
    +
    +                                                                2
    +
    +
    +
    diff --git a/postfix/html/qmgr.8.html b/postfix/html/qmgr.8.html new file mode 100644 index 000000000..d9f0bac43 --- /dev/null +++ b/postfix/html/qmgr.8.html @@ -0,0 +1,332 @@ +
    +
    +
    +
    +QMGR(8)                                                   QMGR(8)
    +
    +
    +NAME
    +       qmgr - Postfix queue manager
    +
    +SYNOPSIS
    +       qmgr [generic Postfix daemon options]
    +
    +DESCRIPTION
    +       The  qmgr  daemon  awaits the arrival of incoming mail and
    +       arranges for its delivery via Postfix delivery  processes.
    +       The actual mail routing strategy is delegated to the triv-
    +       ial-rewrite(8) daemon.  This program  expects  to  be  run
    +       from the master(8) process manager.
    +
    +       Mail  addressed  to  the  local  double-bounce  address is
    +       silently discarded.  This stops potential loops caused  by
    +       undeliverable bounce notifications.
    +
    +       Mail  addressed to a user listed in the optional relocated
    +       database is bounced with a "user has  moved  to  new_loca-
    +       tion" message. See relocated(5) for a precise description.
    +
    +MAIL QUEUES
    +       The qmgr daemon maintains the following queues:
    +
    +       incoming
    +              Inbound mail from the network, or mail picked up by
    +              the local pickup agent from the maildrop directory.
    +
    +       active Messages that the  queue  manager  has  opened  for
    +              delivery.  Only  a  limited  number  of messages is
    +              allowed to enter the  active  queue  (leaky  bucket
    +              strategy, for a fixed delivery rate).
    +
    +       deferred
    +              Mail  that  could  not  be delivered upon the first
    +              attempt. The queue manager  implements  exponential
    +              backoff  by  doubling  the  time  between  delivery
    +              attempts.
    +
    +       corrupt
    +              Unreadable or damaged queue files  are  moved  here
    +              for inspection.
    +
    +DELIVERY STATUS REPORTS
    +       The  qmgr daemon keeps an eye on per-message delivery sta-
    +       tus reports in  the  following  directories.  Each  status
    +       report file has the same name as the corresponding message
    +       file:
    +
    +       bounce Per-recipient status information about why mail  is
    +              bounced.    These   files  are  maintained  by  the
    +              bounce(8) daemon.
    +
    +       defer  Per-recipient status information about why mail  is
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +QMGR(8)                                                   QMGR(8)
    +
    +
    +              delayed.    These   files  are  maintained  by  the
    +              defer(8) daemon.
    +
    +       The qmgr daemon is responsible for asking the bounce(8) or
    +       defer(8) daemons to send non-delivery reports.
    +
    +STRATEGIES
    +       The  queue  manager implements a variety of strategies for
    +       either opening queue files (input) or for message delivery
    +       (output).
    +
    +       leaky bucket
    +              This  strategy limits the number of messages in the
    +              active queue and prevents the  queue  manager  from
    +              running out of memory under heavy load.
    +
    +       fairness
    +              When  the  active queue has room, the queue manager
    +              takes one message from the incoming queue  and  one
    +              from the deferred queue. This prevents a large mail
    +              backlog from blocking the delivery of new mail.
    +
    +       slow start
    +              This strategy eliminates "thundering herd" problems
    +              by slowly adjusting the number of parallel deliver-
    +              ies to the same destination.
    +
    +       round robin and random walk
    +              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.
    +
    +       exponential backoff
    +              Mail  that  cannot  be  delivered  upon  the  first
    +              attempt is deferred.   The  time  interval  between
    +              delivery attempts is doubled after each attempt.
    +
    +       destination status cache
    +              The   queue  manager  avoids  unnecessary  delivery
    +              attempts by  maintaining  a  short-term,  in-memory
    +              list of unreachable destinations.
    +
    +TRIGGERS
    +       On an idle system, the queue manager waits for the arrival
    +       of trigger events, or it waits for a timer to  go  off.  A
    +       trigger  is  a one-byte message.  Depending on the message
    +       received, the queue manager performs one of the  following
    +       actions  (the message is followed by the symbolic constant
    +       used internally by the software):
    +
    +
    +
    +
    +
    +                                                                2
    +
    +
    +
    +
    +
    +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)
    +              Start an incoming queue scan. If an incoming  queue
    +              scan  is  already  in  progress,  that scan will be
    +              restarted as soon as it finishes.
    +
    +       A (QMGR_REQ_SCAN_ALL)
    +              Ignore deferred queue file time stamps. The request
    +              affects the next deferred queue scan.
    +
    +       F (QMGR_REQ_FLUSH_DEAD)
    +              Purge  all  information  about  dead transports and
    +              destinations.
    +
    +       W (TRIGGER_REQ_WAKEUP)
    +              Wakeup call, This is used by the master  server  to
    +              instantiate  servers  that  should not go away for-
    +              ever. The action is  to  start  an  incoming  queue
    +              scan.
    +
    +       The  qmgr daemon reads an entire buffer worth of triggers.
    +       Multiple identical trigger  requests  are  collapsed  into
    +       one,  and trigger requests are sorted so that A and F pre-
    +       cede D and I. Thus, in order to  force  a  deferred  queue
    +       run, one would request A F D; in order to notify the queue
    +       manager of the arrival of new mail one would request I.
    +
    +STANDARDS
    +       None. The qmgr daemon does not interact with  the  outside
    +       world.
    +
    +SECURITY
    +       The  qmgr  daemon is not security sensitive. It reads sin-
    +       gle-character messages from  untrusted  local  users,  and
    +       thus  may be susceptible to denial of service attacks. The
    +       qmgr daemon does not talk to the outside world, and it can
    +       be run at fixed low privilege in a chrooted environment.
    +
    +DIAGNOSTICS
    +       Problems and transactions are logged to the syslog daemon.
    +       Corrupted message files are saved to the corrupt queue for
    +       further inspection.
    +
    +       Depending  on the setting of the notify_classes parameter,
    +       the postmaster is notified of bounces and of  other  trou-
    +       ble.
    +
    +BUGS
    +       A  single  queue  manager  process has to compete for disk
    +       access with multiple front-end processes such as smtpd.  A
    +
    +
    +
    +                                                                3
    +
    +
    +
    +
    +
    +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
    +       details and for default values.  Use  the  postfix  reload
    +       command after a configuration change.
    +
    +Miscellaneous
    +       relocated_maps
    +              Tables with contact information for users, hosts or
    +              domains that no longer exist. See relocated(5).
    +
    +       queue_directory
    +              Top-level directory of the Postfix queue.
    +
    +Active queue controls
    +       qmgr_message_active_limit
    +              Limit the number of messages in the active queue.
    +
    +       qmgr_message_recipient_limit
    +              Limit the number of in-memory recipients.
    +
    +              This parameter also limits the size of  the  short-
    +              term, in-memory destination cache.
    +
    +Timing controls
    +       min_backoff
    +              Minimal  time  in seconds between delivery attempts
    +              of a deferred message.
    +
    +              This parameter also limits the time an  unreachable
    +              destination  is  kept  in the short-term, in-memory
    +              destination status cache.
    +
    +       max_backoff
    +              Maximal time in seconds between  delivery  attempts
    +              of a deferred message.
    +
    +       maximal_queue_lifetime
    +              Maximal  time in days a message is queued before it
    +              is sent back as undeliverable.
    +
    +       queue_run_delay
    +              Time in seconds between deferred queue scans. Queue
    +              scans do not overlap.
    +
    +       transport_retry_time
    +              Time  in seconds between attempts to contact a bro-
    +              ken delivery transport.
    +
    +Concurrency controls
    +       In the text below, transport  is  the  first  field  in  a
    +
    +
    +
    +                                                                4
    +
    +
    +
    +
    +
    +QMGR(8)                                                   QMGR(8)
    +
    +
    +       master.cf entry.
    +
    +       initial_destination_concurrency
    +              Initial  per-destination concurrency level for par-
    +              allel delivery to the same destination.
    +
    +       default_destination_concurrency_limit
    +              Default limit on the number of parallel  deliveries
    +              to the same destination.
    +
    +       transport_destination_concurrency_limit
    +              Limit  on  the number of parallel deliveries to the
    +              same destination, for delivery via the  named  mes-
    +              sage transport.
    +
    +Recipient controls
    +       default_destination_recipient_limit
    +              Default  limit on the number of recipients per mes-
    +              sage transfer.
    +
    +       transport_destination_recipient_limit
    +              Limit on  the  number  of  recipients  per  message
    +              transfer, for the named message transport.
    +
    +SEE ALSO
    +       master(8), process manager
    +       relocated(5), format of the "user has moved" table
    +       syslogd(8) system logging
    +       trivial-rewrite(8), address routing
    +
    +LICENSE
    +       The  Secure  Mailer  license must be distributed with this
    +       software.
    +
    +AUTHOR(S)
    +       Wietse Venema
    +       IBM T.J. Watson Research
    +       P.O. Box 704
    +       Yorktown Heights, NY 10598, USA
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                5
    +
    +
    +
    diff --git a/postfix/html/queuing.html b/postfix/html/queuing.html new file mode 100644 index 000000000..fe12f6165 --- /dev/null +++ b/postfix/html/queuing.html @@ -0,0 +1,118 @@ + + + + +Postfix Overview - Queue Management + + + + + +

    +Postfix Overview - Queue Management

    + +
    + +Up one level | Introduction | Goals +and features | Global architecture +| Queue Management | Security + +

    Postfix mail queues

    + +Postfix has four different queues: maildrop, incoming, +active and deferred (click the upper left-hand icon +for the big picture). Locally-posted mail is deposited into the +maildrop, and is copied to the incoming queue after +some cleaning up. The incoming queue is for mail that is +still arriving or that the queue manager hasn't looked at yet. +The active queue is a limited-size queue for mail that the +queue manager has opened for delivery. Mail that can't be delivered +goes to the deferred queue, so that it does not get in the +way of other deliveries. + +

    + +The queue manager keeps information in memory about the active +queue only. The active queue size is limited on purpose: the +queue manager should never run out of working memory because of a +peak message workload. Whenever there is space in the +active queue, the queue manager lets in one message from the +incoming queue and one from the deferred queue. This +guarantees that new mail will get through even when there is a +large backlog. + +

    No thundering herd

    + +Implementing a high-performance mail system is one thing. However, +no-one would be pleased when Postfix connects to their site and +overwhelms it with lots of simultaneous deliveries. This is an +issue especially when a site has been down and mail is backed up +elsewhere in the network. + +

    + +Postfix tries to be a good network neighbor. When delivering mail +to a site, Postfix will initially make no more than two simultaneous +connections. As long as deliveries succeed, the concurrency slowly +increases up to some configurable limit (or until the host or +network is unable to handle the load); concurrency is decreased in +the face of trouble. For those familiar with TCP/IP implementation +details, Postfix implements its own analog of the TCP slow +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. + +

    + +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 +not stop the system from delivering to other sites. + +

    + +When mail arrives faster than Postfix can deliver it, Postfix will +favor new mail over delayed mail. The idea is that new mail should +be delivered with as little delay as possible; delayed mail can be +delivered while the system would otherwise be idle. + +

    Exponential backoff

    + +Postfix implements per-message exponential backoff. When a message +cannot be delivered upon the first attempt, the queue manager gives +the queue file a time stamp that is offset into the future by some +configurable amount of time. Queue files with future time stamps +are normally ignored by the queue manager. + +

    + +Whenever a repeat delivery attempt fails, the queue file time stamp +is moved into the future by an amount of time equal to the age of +the message. Thus, the time between delivery attempts doubles each +time. This strategy effectively implements exponential backoff. + +

    Destination status cache

    + +The Postfix queue manager maintains a limited, short-term list of +unreachable destinations. This list helps it to avoid unnecessary +delivery attempts, especially with destinations that have a large +mail backlog. + +
    + +Up one level | Introduction | Goals +and features | Global architecture +| Queue Management | Security + + + + diff --git a/postfix/html/rate.html b/postfix/html/rate.html new file mode 100644 index 000000000..da7d46f0c --- /dev/null +++ b/postfix/html/rate.html @@ -0,0 +1,405 @@ + + + + + + + Postfix Rate Controls + + + + + +

    Postfix Rate Controls

    + +
    + +Up one level | +Basic Configuration | UCE +Controls | Rate Controls | Resource +Controls | Address Manipulation + +

    Introduction

    + +Building a high-performance mail delivery system is one thing; +building one that does not knock over other systems is a different +story. Some mailers suffer from the thundering herd syndrome: +they literally flood other systems with mail. Postfix tries to be +a fast mailer and a good neighbor at the same time. + +

    + +On the inbound side, the Postfix SMTP +server has defenses in place against malicious or confused +clients. They won't protect against an all-out denial of service +attack on your infrastructure, but then nothing will except pulling +the plug. + +

    + +Unless indicated otherwise, all parameters described here are in +the main.cf file. If you change parameters of a running +Postfix system, don't forget to issue a postfix reload +command. + +

    + +

    Process limits

    + +The default_process_limit parameter (default: 50) gives +direct control over inbound and outbound delivery rates. This +parameter controls the number of concurrent processes that implement +a Postfix service (smtp client, smtp server, local delivery, etc.). +On small systems, or on systems connected via dialup networks, a +default_process_limit of 10 is probably more than adequate. +Use a larger value if your machine is a major mail hub. + +

    + +You can override this setting for specific Postfix daemons by +editing the master.cf file. For example, if you do not +wish to receive 50 SMTP messages at the same time, you could specify: + +

    + +
     
    +# ==========================================================================
    +# service type  private unpriv  chroot  wakeup  maxproc command + args
    +#               (yes)   (yes)   (yes)   (never) (50)
    +# ==========================================================================
    +. . .
    +smtp      inet  n       -       -       -       5       smtpd
    +. . .
    +
    +
    + +
    + +

    Destination concurrency

    + +So, you have this huge mailhub with tons of disk and memory, and +have configured Postfix to run up to 1000 SMTP client processes at +the same time. Congratulations. But do you really want to make 1000 +simultaneous connections to the same remote system? Probably not. + +

    + +The Postfix queue manager comes to the rescue. This program implements +the analog of the TCP slow start flow control strategy: +when delivering to a site, send a small number of messages first, +then increase the rate as long as all goes well; back off in the +face of congestion. + +

    + +The initial_destination_concurrency parameter (default: 2) +controls how many messages are initially sent to the same destination +before adapting delivery concurrency. Of course, this setting is +effective only as long as it does not exceed the process limit and the destination concurrency +limit for the specific mail transport channel. + +

    + +The default_destination_concurrency_limit parameter +(default: 10) controls how many messages may be sent to the same +destination simultaneously. You can override this setting for +specific delivery channels (local, smtp, uucp etc.). The +main.cf file recommends the following: + +

    + +
    local_destination_concurrency_limit = 2 + +
    default_destination_concurrency_limit = 10 + +
    + +The local_destination_concurrency_limit parameter controls +how many messages are delivered simultaneously to the same local +recipient. The recommended limit is low because delivery to the +same mailbox must happen sequentially, so massive parallelism is +not useful. Another good reason to limit delivery concurrency to +the same recipient: if the recipient has an expensive shell command +in her .forward file, or if the recipient is a mailing list +manager, you don't want to run too many instances at the same time. + +

    + +A destination concurrency limit of 10 for SMTP delivery seems enough +to noticeably load a system without bringing it to its knees. Be +careful when changing this to a much larger number. + +

    Recipient limits

    + +The default_destination_recipient_limit parameter (default: +50) controls how many recipients a Postfix delivery agent (smtp, +uucp, etc.) will send with each copy of an email message. +If an email message has more than +$default_destination_recipient_limit recipients at the same +destination, the list of recipients will be broken up into smaller lists, +and multiple copies of the message will be sent. + +

    + +You can override this setting for specific Postfix delivery agents +(smtp, uucp, etc.). For example: + +

    + +
    uucp_destination_recipient_limit = 100 + +
    + +would limit the number of recipients per UUCP delivery to 100. + +

    + +You must be careful when increasing the recipient limit; some SMTP +servers abort the connection when they run out of memory or when +a hard recipient limit is reached, so the mail won't get through. + +

    + +The smtpd_recipient_limit parameter (default: 1000) +controls how many recipients the SMTP server will take per delivery. +That's more than any reasonable SMTP client would send. The limit +exists just to protect the local mail system against a malicious +or confused client. + +

    Always postponing delivery

    + +The defer_transports parameter allows you to specify what +mail should always be deferred until Postfix is explicitly asked +to deliver. + +

    + +A small site that is on-line only part of the time, and +that wants to defer all deliveries until the command sendmail +-q is executed (e.g., from a PPP dialout script) would use: + +

    + +

    + +
    defer_transports = smtp + +
    + +

    + +An ISP can use the defer_transports feature for customers +that are off-line most of the time. The customer can trigger delivery +by issuing an ETRN command at the SMTP port. The following +examples show how to configure such a customer: + +

    + +

    + +
    /etc/postfix/main.cf: + +

    + +

    defer_transports = hold + +

    + +You can specify any number of transports here. The example gives +just one. + +

    + +

    /etc/postfix/transport: + +

    + +

    customer.com   hold:[gateway.customer.com] + +
    .customer.com   hold:[gateway.customer.com] + +

    + +The [] are necessary to avoid MX lookups, which might point to your +local machine. The second entry is necessary only if you want to +relay mail for customer subdomains. + +

    + +

    /etc/postfix/master.cf: + +

    + +

    hold   unix   -   -   n   -   -   smtp + +

    + +This is just the master.cf entry for regular SMTP, with the +first field changed to hold. + +

    + +

    Backoff from unreachable hosts

    + +When a Postfix delivery agent (smtp, local, uucp, etc.) +is unable to deliver a message it may blame the message itself or +the receiving party. + +
      + +
    • If the delivery agent blames the message, the queue manager +gives the queue file a time stamp into the future, so it won't be +looked at for a while. By default, the amount of time to cool down +is the amount of time that has passed since the message arrived. +This results in so-called exponential backoff behavior. + +

      + +

    • If the delivery agent blames the receiving party (for example +a local recipient user, or a remote host), the queue manager not +only advances the queue file time stamp, but also puts the receiving +party on a "dead" list so that it will be skipped for some amount +of time. + +
    + +

    + +As you would expect, this whole process is governed by a bunch of +little parameters. + +

    + +
    queue_run_delay (default: 1000 seconds)
    How +often the queue manager scans the queue for deferred mail. + +

    + +

    maximal_queue_lifetime (default: 5 days)
    How +long a message stays in the queue before it is sent back as +undeliverable. + +

    + +

    minimal_backoff_time (default: 1000 seconds)
    The +minimal amount of time a message won't be looked at, and the minimal +amount of time to stay away from a "dead" destination. + +

    + +

    maximal_backoff_time (default: 4000 seconds)
    The +maximal amount of time a message won't be looked at after a delivery +failure. + +

    + +

    qmgr_message_recipient_limit (default: 1000)
    The +size of many in-memory queue manager data structures. Among others, +this parameter limits the size of the short-term, in-memory "dead" +list. Destinations that don't fit the list are not added. + +
    + +

    Slowing down bad clients

    + +First of all, no defense will protect against an all-out denial of +service attack. I just don't want to raise impossible expectations. +But there are a few simple things one can do in order to deal with +confused or malicious client programs. + +

    + +Some defenses are part of a more general strategy: for example, +how long a line of text may be before it is broken up into pieces, +and how much text may be carried in a multi-line message header. +See the resource controls documentation +for details. + +

    + +The Postfix SMTP server increments a +per-session error counter whenever a client request is unrecognized +or unimplemented, or whenever a client request violates UCE restrictions or other reasons. The error +counter is reset when a message is transferred successfully. + +

    + +As the per-session error count increases, the SMTP server changes +behavior. The idea is to limit the damage by slowing down the +client. The behavior is controlled by the following parameters: + +

    + +

    + + + +
    smtpd_error_sleep_time (default: 5 seconds)
    When +the per-session error count is small, the SMTP server pauses only +when reporting a problem to a client. The purpose is to prevent +naive clients from going into a fast connect-error-disconnect +loop. + +

    + + + +

    smtpd_soft_error_limit (default: 10)
    When the +per-session error count exceeds this value, the SMTP server sleeps +error_count seconds before responding to a client request. + +

    + + + +

    smtpd_hard_error_limit (default: 100)
    When +the per-session error count exceeds this value, the SMTP server +disconnects. + +
    + +

    + +Unfortunately, the Postfix SMTP server does not yet know how to +limit the number of connections from the same client, +other than by limiting the total number of SMTP server processes +(see process limit). Things could be worse: +some mailers don't even implement an SMTP server process limit. +That's of course no excuse. I'm still looking for a good solution. + +


    + +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 new file mode 100644 index 000000000..89dafba81 --- /dev/null +++ b/postfix/html/receiving.html @@ -0,0 +1,117 @@ + + + + +Postfix Anatomy - Receiving Mail + + + + + +

    Postfix +Anatomy - Receiving Mail

    + +
    + +Up one level | Receiving Mail | Delivering Mail | Behind the Scenes | Command-line Utilities + +

    + +When a message enters the Postfix mail system, the first stop on +the inside is the incoming queue. The figure below shows +the main components that are involved with new mail. For an +explanation of the symbols used, click on the icon in the upper +left-hand corner of this page. + +

    + +

    + + + +
    + +

    + +

      + +
    • Mail is posted locally. The Postfix sendmail program deposits the message +into the world-writable maildrop directory, where the message +is picked up by the pickup daemon. +This daemon does some sanity checks, in order to protect the rest +of the Postfix system. In order to avoid accidents, the directory +permissions on the maildrop directory must be such that a +user cannot delete someone elses mail. + +

      + +

    • Mail comes in via the network. The Postfix SMTP +server receives the message and does some sanity checks, in +order to protect the rest of the Postfix system. The SMTP server +can be configured to implement UCE controls +on the basis of local or network-based black lists, DNS lookups, +and other client request information. + +

      + +

    • Mail is generated internally by the Postfix system itself, in +order to return undeliverable mail to the sender. The bounce or defer daemon brings the bad +news. + +

      + +

    • Mail is forwarded by the local +delivery agent, either via an entry in the system-wide alias database, or via an entry in a +per-user .forward file. This is +indicated with the unlabeled arrow. + +

      + +

    • Mail is generated internally by the Postfix system itself, in +order to notify the postmaster of a problem (this path is also +indicated with the unlabeled arrow). The Postfix system can be +configured to notify the postmaster +of SMTP protocol problems, UCE policy violations, and so on. + +

      + +

    • The cleanup daemon implements the +final processing stage for new mail. It adds missing From: +and other message headers, arranges for address rewriting to the +standard user@fully.qualified.domain form, and optionally +extracts recipient addresses from message headers. The cleanup +daemon inserts the result as a single queue file into the +incoming queue, and notifies the queue +manager of the arrival of new mail. The cleanup daemon +can be configured to transform addresses on the basis of canonical and virtual table lookups. + +

      + +

    • On request by the cleanup daemon, the trivial-rewrite daemon rewrites +addresses to the standard user@fully.qualified.domain form. +The initial Postfix version does not implement a rewriting language. +Implementing one would take a lot of effort, and most sites do not +need it. Instead, Postfix makes extensive use of table lookup. + +
    + +
    + +Up one level | Receiving Mail | Delivering Mail | Behind the Scenes | Command-line Utilities + + + + diff --git a/postfix/html/relocated.5.html b/postfix/html/relocated.5.html new file mode 100644 index 000000000..99ba43c5a --- /dev/null +++ b/postfix/html/relocated.5.html @@ -0,0 +1,134 @@ +
    +
    +
    +
    +RELOCATED(5)                                         RELOCATED(5)
    +
    +
    +NAME
    +       relocated - format of Postfix relocated table
    +
    +SYNOPSIS
    +       postmap /etc/postfix/relocated
    +
    +DESCRIPTION
    +       The  optional relocated file provides the information that
    +       is used in "user has moved to  new_location"  bounce  mes-
    +       sages.
    +
    +       The  file  serves  as input to the postmap(1) command. The
    +       result, an indexed file in dbm or db format, is  used  for
    +       fast searching by the mail system. After an update issue a
    +       postfix reload command to make the change visible.
    +
    +       Table lookups are case insensitive.
    +
    +       The format of the table is as follows:
    +
    +       o      Blank lines are ignored,  as  are  lines  beginning
    +              with `#'.
    +
    +       o      An entry has one of the following form:
    +                   key  new_location
    +              Where  new_location  specifies  contact information
    +              such as an  email  address,  or  perhaps  a  street
    +              address or telephone number.
    +
    +       The key field is one of the following:
    +
    +       user@domain
    +              Matches  user@domain. This form has precedence over
    +              all other forms.
    +
    +       user   Matches user@site when site is $myorigin, when site
    +              is listed in $mydestination, or when site is listed
    +              in $inet_interfaces.
    +
    +       @domain
    +              Matches every address in domain. This form has  the
    +              lowest precedence.
    +
    +ADDRESS EXTENSION
    +       When  the search fails, and the address localpart contains
    +       the optional recipient delimiter (e.g.,  user+foo@domain),
    +       the  search  is  repeated for the unextended address (e.g.
    +       user@domain).
    +
    +BUGS
    +       The table format does not understand quoting  conventions.
    +
    +CONFIGURATION PARAMETERS
    +       The  following  main.cf parameters are especially relevant
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +RELOCATED(5)                                         RELOCATED(5)
    +
    +
    +       to this topic. See the Postfix  main.cf  file  for  syntax
    +       details  and  for  default  values. Use the postfix reload
    +       command after a configuration change.
    +
    +       relocated_maps
    +              List of lookup tables for relocated users or sites.
    +
    +       Other parameters of interest:
    +
    +       inet_interfaces
    +              The  network  interface  addresses that this system
    +              receives mail on.
    +
    +       mydestination
    +              List of domains that  this  mail  system  considers
    +              local.
    +
    +       myorigin
    +              The domain that is appended to locally-posted mail.
    +
    +SEE ALSO
    +       postmap(1) create lookup table
    +
    +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
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                2
    +
    +
    +
    diff --git a/postfix/html/resource.html b/postfix/html/resource.html new file mode 100644 index 000000000..386432a91 --- /dev/null +++ b/postfix/html/resource.html @@ -0,0 +1,277 @@ + + + + + Postfix Configuration - Resource Controls + + + + + +

    Postfix Configuration - Resource Controls

    + +
    + +Up one level | Basic +Configuration | UCE Controls | Rate Controls | Resource Controls | Address Manipulation + +

    Introduction

    + +The Postfix system is designed to run within a finite memory budget. +To this end, there are configurable limits on the size of +in-memory objects such as text line fragments, on the number of +instances of such objects, and on the time an operation +may take. In addition, strategies are in place for dealing with +resource exhaustion. The idea is to keep running under conditions +of stress, without making the problem worse. + +

    + +

    + +

    Object size limits

    + +The first step towards a fixed memory resource budget is to limit +the size of each in-memory object. Once the size of in-memory +objects is limited, total memory consumption is limited by limiting +the number of object instances. Simple, no? + +

    + +

    + +
    line_length_limit (default: 2048 bytes) + +
    How long a line of text can be before it is broken up into +pieces. All Postfix perimeter programs (SMTP +server, SMTP client, local pickup and local +delivery) enforce this line length limit when reading data from +an untrusted source. Long lines are reconstructed upon delivery. + +

    + +

    header_size_limit (default: 102400 bytes) + +
    How much text may be carried in a multi-line message header. +Header text that does not fit in $header_size_limit bytes +overflows into the message body. This limit is enforced by the cleanup header rewriting code. + +
    + +

    + +The following parameters restrict the use of file system storage: + +

    + +
    message_size_limit (default: 10240000 bytes) + +
    The maximal size of a Postfix queue file for inbound mail, +including envelope information (sender, recipient, etc.). + +

    + +

    queue_minfree (default: no restriction) + +
    How many bytes of free space are needed in the queue file +system. The SMTP server declines inbound +mail delivery requests when there is insufficient space (the mail +will be accepted once enough space becomes available). There is +no default limit; however, it seems like a good idea to require at +least several times $message_size_limit so that the mail +system won't get stuck on a single large message. + +

    + +

    bounce_size_limit (default: 50000 bytes) + +
    How much of an undelivered message is sent back to the sender. + +
    + +

    Object count limits

    + +Once the sizes of memory objects have been limited, the next step +to implement Postfix's finite memory budget is to limit the number +of in-memory object instances. + +
    + +
    qmgr_message_recipient_limit (default: 10000) + +
    An upper bound on the number of queue +manager in-memory recipient address data structures. This +parameter also controls the number of instances of other in-memory +data structures. See, for example, the delivery rate control documentation. + +

    + +

    qmgr_message_active_limit (default: 1000) + +
    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. + +

    + +

    duplicate_filter_limit (default: 1000) + +
    How many recipient addresses the local +delivery agent and address cleanup +daemon remember when delivering a message. A recipient address is +ignored when it is found in the remembered list. + +
    + +

    Time limits

    + +External commands are given a finite time for completion. Such +commands are run by the local delivery +agent when it finds a "|command" destination in an alias database, :include: file or .forward file. The pipe mailer implements an alternative way +to pipe mail into external commands. + +
    + +
    command_time_limit (default: 1000 seconds) + +
    How long the local delivery agent +will wait before aborting an external command. + +

    + +

    service_name_time_limit (default: +$command_time_limit) + +
    The time limit for delivery to external commands via the +pipe mailer. For service_name, substitute the service +name (the first field in the master.cf file). + +
    + +

    Acquiring exclusive file locks

    + +Internally, the Postfix programs cooperate in a very disciplined +manner and rarely need to fight for exclusive file access. However, +access conflicts may happen on the outside, for example, when mail +has to be delivered while a user is accessing her mailbox. Postfix +supports two types of file locks: + +
      + +
    • Internal locks, implemented with the fcntl() or +flock() system primitives. + +

      + +

    • External locks, implemented as files named file.lock. + +
    + +Depending on the host system, Postfix uses one method or both. +The following configuration parameters control how Postfix deals +with file locks: + +
    + +
    deliver_lock_attempts (default: 5) + +
    The number of times to try locking a file before giving up. + +

    + +

    deliver_lock_delay (default: 1 second) + +
    How long to wait between attempts to lock a file. + +

    + +

    stale_lock_time (default: 500) + +
    How old an external lock file may be before it is forcibly +removed. + +
    + +

    Error recovery

    + +Under conditions of severe stress, available system resources may +be insufficient to accommodate Postfix's needs. The world may also +seem to fall apart when a Postfix configuration file is broken, or +when a Postfix program is defective. + +

    + +The general approach taken in the face of disaster is to terminate +with a fatal run-time error (or with a panic in case of software +problems), and to try again after some time (the master daemon will restart processes after +some delay). Each failed attempt is logged; hopefully, someone will +notice the problem and fix it. + +

    + +Some recovery strategies were implemented very early during Postfix +development, and haven't been made configurable yet. What follows +is the beginning of a growing list of recovery control parameters: + +

    + +
    fork_attempts (default: 5 times) + +
    The number of times to attempt to create a new process before +giving up. + +

    + +

    fork_delay (default: 1 second) + +
    The delay between attempts to create a new process. + +

    + +

    transport_retry_time (default: 60 seconds) + +
    The amount of time between queue manager attempts to contact +an apparently defunct Postfix delivery service. + +
    + +
    + +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 new file mode 100644 index 000000000..483807576 --- /dev/null +++ b/postfix/html/rewrite.html @@ -0,0 +1,404 @@ + + + + + Postfix Configuration - Address Manipulation + + + + + +

    Postfix Configuration - Address Manipulation

    + +
    + +Up one level | + Basic Configuration | UCE +Controls | Rate Controls | Resource Controls | Address Manipulation + +

    Introduction

    + +Although the initial Postfix release has no address rewriting +language, it can do quite a bit of address manipulation via table +lookup. While a message flows through the Postfix system, its +addresses are mangled in the order described in this document. + +

    + +Unless indicated otherwise, all parameters described here are in +the main.cf file. If you change parameters of a running +Postfix system, don't forget to issue a postfix reload +command. + +

    + +All mail: + +

    + +

    + +

    + +Local delivery: + +

    + +

    + +

    Rewrite addresses to standard form

    + +Before the
    cleanup daemon runs an +address through any lookup table, it first rewrites the address to +the standard user@fully.qualified.domain form, by sending +the address to the trivial-rewrite +daemon. The purpose of rewriting to standard form is to reduce the +number of entries needed in lookup tables. The Postfix trivial-rewrite program implements +the following hard-coded address manipulations: + +
    + +
    Rewrite @hosta,@hostb:user@site to user@site + +
    The source route feature has been deprecated. Postfix has no +ability to handle such addresses, other than to strip off the source +route. + +

    + + + +

    Rewrite site!user to user@site + +
    This feature is controlled by the boolean swap_bangpath +parameter (default: yes). The purpose is to rewrite +UUCP-style addresses to domain style. This is useful only when you +receive mail via UUCP, but it probably does not hurt otherwise. + +

    + + + +

    Rewrite user%domain to user@domain + +
    This feature is controlled by the boolean allow_percent_hack +parameter (default: yes). Typically, this is used in order +to deal with monstrosities such as user%domain@otherdomain. + +

    + + + +

    Rewrite user to user@$myorigin + +
    This feature is controlled by the boolean append_at_myorigin +parameter (default: yes). The purpose is to get consistent +treatment of user on every machine in $myorigin. + +

    + +You probably should never turn off this feature, because a lot of +Postfix components expect that all addresses have the form +user@domain. + +

    + +If your machine is not the main machine for $myorigin and +you wish to have some users delivered locally without going via +that main machine, make an entry in the virtual +table that redirects user@$myorigin to user@$myhostname. + +

    + + + +

    Rewrite user@host to user@host.$mydomain + +
    This feature is controlled by the boolean append_dot_mydomain +parameter (default: yes). The purpose is to get consistent +treatment of different forms of the same hostname. + +

    + +Some will argue that rewriting host to host.$mydomain +is bad. That is why it can be turned off. Others like the convenience of having +the local domain appended automatically. + +

    + + + +

    Rewrite user@site. to user@site (without the +trailing dot). + +
    + +

    Canonical address mapping

    + +Before the
    cleanup daemon stores +inbound mail into the incoming queue, it uses the canonical table to rewrite all addresses +in message envelopes and in message headers, local or remote. The +mapping is useful to replace login names by Firstname.Lastname +style addresses, or to clean up invalid domains in mail addresses +produced by legacy mail systems. + +

    + +Canonical mapping is disabled by default. To enable, edit the +canonical_maps parameter in the main.cf file and +specify one or more lookup tables, separated by whitespace or commas. +For example: + +

    + +
    canonical_maps = hash:/etc/postfix/canonical + +
    + +

    + +In addition to the canonical maps which are applied to both sender +and recipient addresses, you can specify canonical maps that are +applied only to sender addresses or to recipient addresses. For +example: + +

    + +
    sender_canonical_maps = hash:/etc/postfix/sender_canonical + +

    + +

    recipient_canonical_maps = hash:/etc/postfix/recipient_canonical + +
    + +

    + +The sender and recipient canonical maps are applied before +the common canonical maps. + +

    + +Sender-specific rewriting is useful when you want to rewrite ugly +sender addresses to pretty ones, and still want to be able to +send mail to the those ugly address without creating a mailer loop. + +

    Address masquerading

    + +Address masquerading is a method to hide all hosts below a domain +behind their mail gateway, and to make it appear as if the mail +comes from the gateway itself, instead of from individual machines. + +

    + +Address masquerading is disabled by default. To enable, edit the +masquerade_domains parameter in the main.cf +file and specify one or more domain names separated by whitespace +or commas. For example: + +

    + +
    masquerade_domains = $mydomain + +
    + +

    + +In this example, addresses of the form user@host.$mydomain +would be rewritten to user@$mydomain. + +

    + +The masquerade_exceptions configuration parameter specifies +what user names should not be subjected to address masquerading. +Specify one or more user names separated by whitespace or commas. +For example, + +

    + +
    masquerade_exceptions = root + +
    + +

    + + +By default, Postfix makes no exceptions. +

    + +Subtle point: address masquerading is applied only to message +headers and envelope sender addresses, not to envelope recipients. + +

    Virtual address mapping

    + +After applying the canonical and masquerade mappings, the cleanup daemon uses the virtual table to redirect mail for all +recipients, local or remote. The mapping affects only envelope +recipients; it has no effect on message headers or envelope senders. +Virtual lookups are useful to redirect mail for virtual domains to +real user mailboxes, and to redirect mail for domains that no longer +exist. Virtual lookups can also be used to transform +Firstname.Lastname back into UNIX login names, although it +seems that local aliases are a more appropriate +vehicle. + +

    + +Virtual mapping is disabled by default. To enable, edit the +virtual_maps parameter in the main.cf file and +specify one or more lookup tables, separated by whitespace or +commas. For example: + +

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

    + +Addresses found in virtual maps are subjected to another iteration +of virtual mapping, but are not subjected to canonical mapping, in +order to avoid loops. + +

    Relocated users table

    + +Next, the queue manager runs each recipient name through the +relocated database. This table +provides information on how to reach users that no longer have an +account, or what to do with mail for entire domains that no longer +exist. When mail is sent to an address that is listed in this +table, the message is bounced with an informative message. + +

    + +Lookups of relocated users are disabled by default. To enable, edit +the relocated_maps parameter in the main.cf +file and specify one or more lookup tables, separated by whitespace +or commas. For example: + +

    + +
    relocated_maps = hash:/etc/postfix/relocated + +
    + +

    Mail transport switch

    + +Once the queue manager has established the destination of a message, +the optional
    transport table controls +how the message will be delivered (this table is used by the address +rewriting and resolving daemon). By default, everything is sent +via the smtp transport. The transport +table can be used to send mail to specific sites via UUCP, +or to send mail to a really broken mail system that can handle only +one SMTP connection at a time (yes, such systems exist and people +used to pay real money for them). + +

    + +Transport table lookups are disabled by default. To enable, edit +the transport_maps parameter in the main.cf file and +specify one or more lookup tables, separated by whitespace or +commas. For example: + +

    + +
    transport_maps = hash:/etc/postfix/transport + +
    + +

    Alias database

    + +When mail is to be delivered locally, the
    local +delivery agent runs each local recipient name through the aliases database. The mapping does not +affect addresses in message headers. Local aliases are typically +used to implement distribution lists, or to direct mail for standard +aliases such as postmaster to real people. The table can +also be used to map Firstname.Lastname addresses to login +names. + +

    + +Alias lookups are enabled by default. The default configuration +depends on the system environment, but it is typically one of the +following: + +

    + +

    + +
    alias_maps = hash:/etc/aliases + +
    alias_maps = dbm:/etc/aliases, nis:mail.aliases + +
    + +

    + +The path to the alias database file is controlled via the +alias_database configuration parameter. The value is system +dependent. Usually it is one of the following: + +

    + +
    alias_database = hash:/etc/aliases (4.4BSD, LINUX) + +
    alias_database = dbm:/etc/aliases (4.3BSD, SYSV<4) + +
    alias_database = dbm:/etc/mail/aliases (SYSV4) + +
    + +

    + +For security reasons, deliveries to command and file destinations +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. + +


    + +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 new file mode 100644 index 000000000..63530cecb --- /dev/null +++ b/postfix/html/security.html @@ -0,0 +1,180 @@ + + + + +Postfix Overview - Security + + + + + +

    Postfix +Overview - Security

    + +
    + +Up one level | Introduction | Goals +and features | Global architecture +| Queue Management | Security + +

    Introduction

    + +By definition, mail software processes information from potentially +untrusted sources. Therefore, mail software must be written with +great care, even when it runs with user privileges, and even when +it does not talk directly to a network. + +

    + +Postfix is a complex system. The initial release has about 30,000 +lines of code (after deleting the comments). With a system that +complex, the security of the system should not depend on a single +mechanism. If it did, one single error would be sufficient to +compromise the entire mail system. Therefore, Postfix uses multiple +layers of defense to control the damage from software and other +errors. + +

    Least privilege

    + +Most Postfix daemon programs can be run at fixed low privilege in +a chrooted environment. This is especially true for the programs +that are exposed to the network: the SMTP server and SMTP client. +Although chroot(2), even when combined with low privilege, is no +guarantee against system compromise it does add a considerable +hurdle. And we all know that every little bit helps. + +

    Insulation

    + +Postfix uses separate processes to insulate activities from each +other. In particular, there is no direct path from the network to +the security-sensitive local delivery programs. An intruder first +has to break through multiple programs. Some parts of the Postfix +system are multi-threaded. However, all programs that interact with +the outside world are single-threaded. Separate processes give +better insulation than multiple threads within a shared address +space. + +

    Controlled environment

    + +No Postfix mail delivery program runs under control of a user +process. Instead, most Postfix programs run under control of a +resident master daemon that runs in a controlled environment, +without any parent-child relationship to user processes. This +approach eliminates exploits that involve signals, open files, +environment variables, and other process attributes that the UNIX +system passes on from a possibly-malicious parent to a child. + +

    Set-uid

    + +No Postfix program is set-uid. Introducing the concept was +the biggest mistake made in UNIX history. Set-uid (and its +weaker cousin, set-gid) causes more trouble than it is worth. +Each time a new feature is added to the UNIX system, set-uid +causes a security problem: shared libraries, the /proc file +system, multi-language support, to mention just a few examples. +Set-uid makes it impossible to introduce some of the features +that make UNIX successors such as plan9 so attractive, for example, +per-process file system name spaces. + +

    + +By default, the maildrop queue directory is world-writable, +so that local processes can submit mail without assistance from a +set-uid or set-gid command or from a mail daemon process. The +maildrop directory is not used for mail coming in via the network, +and queue files are not readable for other users. + +

    + +A writable directory opens up opportunities for annoyance: a local +user can make hard links to someone else's maildrop files so they +don't go away and/or are delivered multiple times; a local user +can fill the maildrop directory with garbage and try to make the +mail system crash; and a local user can hard link someone else's +files into the maildrop directory and try to have them delivered +as mail. However, Postfix queue files have a specific format; less +than one in 10^12 non-Postfix files would be recognized as a valid +Postfix queue file. + +

    + +If a world-writable maildrop directory is not acceptable, +sites can revoke world write permission, and enable set-gid +privileges for a small helper program that is provided for this +purpose. + +

    Trust

    + +As mentioned elsewhere in the overview, Postfix programs do not +trust the contents of queue files or of the Postfix internal IPC +messages. Queue files have no on-disk record for deliveries to +sensitive destinations such as files or commands. Instead, programs +such as the local delivery agent attempt to make security-sensitive +decisions on the basis of first-hand information. + +

    + +Of course, Postfix programs do not trust data received from the +network, either. In particular, no Postfix program places +sender-provided data into shell command lines or into environment +variables. If there is one lesson that people have learned from +Web site security disasters it is this one: don't let any data +from the network near a shell. + +

    Large inputs

    + +
      + +
    • Memory for strings and buffers is allocated dynamically, in +order to prevent buffer overrun problems. + +

      + +

    • Long lines in message input are broken up into sequences of +reasonably-sized chunks, and are reconstructed upon delivery. + +

      + +

    • Diagnostics are truncated (in one single place!) before they +are passed to the syslog(3) interface, in order to prevent buffer +overruns on older platforms. However, no general attempt is made +to truncate data before it is passed to system calls or to library +routines. On some platforms, the software may still exhibit buffer +overrun problems, due to vulnerabilities in the underlying software. + +

      + +

    • No specific attempt is made to defend against unreasonably-long +command-line arguments. UNIX kernels impose their own limits, which +should be sufficient to deal with runaway programs or with malicious +users. + +
    + +

    Other defenses

    + +
      + +
    • The number of in-memory instances of any object type is limited, +to prevent the mail system from becoming wedged under heavy load. + +

      + +

    • In case of problems, the software pauses before sending an +error response to a client, before terminating with a fatal error, +or before attempting to restart a failed program. The purpose is +to prevent runaway conditions that only make problems worse. + +
    + +
    + +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 new file mode 100644 index 000000000..143cd03c5 --- /dev/null +++ b/postfix/html/sendmail.1.html @@ -0,0 +1,332 @@ +
    +
    +
    +
    +SENDMAIL(1)                                           SENDMAIL(1)
    +
    +
    +NAME
    +       sendmail - Postfix to Sendmail compatibility interface
    +
    +SYNOPSIS
    +       sendmail [option ...] [recipient ...]
    +
    +       mailq
    +       sendmail -bp
    +
    +       newaliases
    +       sendmail -I
    +
    +DESCRIPTION
    +       The  sendmail  program  implements the Postfix to Sendmail
    +       compatibility interface.  For the  sake  of  compatibility
    +       with  existing  applications,  some  Sendmail command-line
    +       options are recognized but silently ignored.
    +
    +       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.
    +
    +       Specific  command  aliases  are  provided for other common
    +       modes of operation:
    +
    +       mailq  List the mail queue. Each  entry  shows  the  queue
    +              file  ID,  message  size, arrival time, sender, and
    +              the recipients that still need to be delivered.  If
    +              mail  could not be delivered upon the last attempt,
    +              the reason for failure is shown. This mode of oper-
    +              ation  is implemented by connecting to the showq(8)
    +              daemon.
    +
    +       newaliases
    +              Initialize the alias database. If no alias database
    +              type is specified, the program uses the type speci-
    +              fied in the database_type configuration  parameter;
    +              if  no  input  file  is specified, the program pro-
    +              cesses   the    file(s)    specified    with    the
    +              alias_database  configuration  parameter. This mode
    +              of operation is implemented by running the  postal-
    +              ias(1) command.
    +
    +              Note:  it  may  take a minute or so before an alias
    +              database update becomes visible.  Use  the  postfix
    +              reload command to eliminate this delay.
    +
    +       These and other features can be selected by specifying the
    +       appropriate combination of command-line options. Some fea-
    +       tures are controlled by parameters in the main.cf configu-
    +       ration file.
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +SENDMAIL(1)                                           SENDMAIL(1)
    +
    +
    +       The following options are recognized:
    +
    +       -B body_type (ignored)
    +              The message  body  MIME  type.  Currently,  Postfix
    +              implements just-send-eight.
    +
    +       -C config_file (ignored :-)
    +              The path name of the sendmail.cf file. Postfix con-
    +              figuration files are kept in /etc/postfix.
    +
    +       -F full_name
    +              Set the sender full name. This is  used  only  with
    +              messages that have no From: message header.
    +
    +       -I     Initialize  alias database. See the newaliases com-
    +              mand above.
    +
    +       -N dsn (ignored)
    +              Delivery status  notification  control.  Currently,
    +              Postfix does not implement DSN.
    +
    +       -R return_limit (ignored)
    +              Limit   the   size   of   bounced   mail.  Use  the
    +              bounce_size_limit configuration parameter  instead.
    +
    +       -X log_file (ignored)
    +              Log  mailer  traffic.  Use  the debug_peer_list and
    +              debug_peer_level configuration parameters  instead.
    +
    +       -bd    Go  into  daemon  mode.  This  mode of operation is
    +              implemented by executing the postfix start command.
    +
    +       -bi    Initialize  alias database. See the newaliases com-
    +              mand above.
    +
    +       -bm    Read mail  from  standard  input  and  arrange  for
    +              delivery.  This is the default mode of operation.
    +
    +       -bp    List the mail queue. See the mailq command above.
    +
    +       -bs    Stand-alone  SMTP  server  mode. Read SMTP commands
    +              from standard input, and write responses  to  stan-
    +              dard output.  This mode of operation is implemented
    +              by running the smtpd(8) daemon.
    +
    +       -f 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.
    +
    +       -h hop_count (ignored)
    +              Hop  count limit. Use the hopcount_limit configura-
    +              tion parameter instead.
    +
    +
    +
    +
    +                                                                2
    +
    +
    +
    +
    +
    +SENDMAIL(1)                                           SENDMAIL(1)
    +
    +
    +       -i (ignored)
    +              Lines beginning with "." get special treatment only
    +              with -bs.
    +
    +       -m (ignored)
    +              Backwards compatibility.
    +
    +       -n (ignored)
    +              Backwards compatibility.
    +
    +       -oAalias_database
    +              Non-default  alias  database.  Specify  pathname or
    +              type:pathname. See postalias(1) for details.
    +
    +       -o7 (ignored)
    +
    +       -o8 (ignored)
    +              The message body type.  Currently,  Postfix  imple-
    +              ments just-send-eight.
    +
    +       -om (ignored)
    +              The  sender  is  never  eliminated  from alias etc.
    +              expansions.
    +
    +       -o x value (ignored)
    +              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-
    +              ing the qmgr(8) daemon.
    +
    +       -qinterval (ignored)
    +              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
    +              command line.
    +
    +       -v     Enable verbose logging for debugging purposes. Mul-
    +              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
    +       be taken against malicious inputs.
    +
    +DIAGNOSTICS
    +       Problems are logged to  syslogd(8)  and  to  the  standard
    +       error stream.
    +
    +
    +
    +
    +
    +                                                                3
    +
    +
    +
    +
    +
    +SENDMAIL(1)                                           SENDMAIL(1)
    +
    +
    +ENVIRONMENT
    +       MAIL_CONFIG
    +              Directory with Postfix configuration files.
    +
    +       MAIL_VERBOSE
    +              Enable verbose logging
    +
    +       MAIL_DEBUG
    +              Enable debugging with an external command, as spec-
    +              ified  with  the   debugger_command   configuration
    +              parameter.
    +
    +FILES
    +       /var/spool/postfix, mail queue
    +       /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
    +       configuration change.
    +
    +       alias_database
    +              Default   alias  database(s)  for  newaliases.  The
    +              default value for  this  parameter  is  system-spe-
    +              cific.
    +
    +       bounce_size_limit
    +              The amount of original message context that is sent
    +              along with a non-delivery notification.
    +
    +       database_type
    +              Default alias etc. database type. On many UNIX sys-
    +              tems the default type is either dbm or hash.
    +
    +       debugger_command
    +              Command that is executed after a Postfix daemon has
    +              initialized.
    +
    +       debug_peer_level
    +              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
    +              debug_peer_level parameter.
    +
    +       fork_attempts
    +              Number  of attempts to fork() a process before giv-
    +              ing up.
    +
    +
    +
    +
    +
    +                                                                4
    +
    +
    +
    +
    +
    +SENDMAIL(1)                                           SENDMAIL(1)
    +
    +
    +       fork_delay
    +              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
    +              processes.
    +
    +       command_directory
    +              Directory with Postfix support  commands  (default:
    +              $program_directory).
    +
    +       daemon_directory
    +              Directory  with  Postfix  daemon programs (default:
    +              $program_directory).
    +
    +       queue_directory
    +              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
    +              queue.
    +
    +SEE ALSO
    +       pickup(8) mail pickup daemon
    +       postalias(1) maintain alias database
    +       postdrop(1) privileged posting agent
    +       postfix(1) mail system control
    +       postkick(1) kick a Postfix daemon
    +       qmgr(8) queue manager
    +       showq(8) list mail queue
    +       smtpd(8) SMTP server
    +       syslogd(8) system logging
    +
    +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
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                5
    +
    +
    +
    diff --git a/postfix/html/showq.8.html b/postfix/html/showq.8.html new file mode 100644 index 000000000..5b266e7c2 --- /dev/null +++ b/postfix/html/showq.8.html @@ -0,0 +1,68 @@ +
    +
    +
    +
    +SHOWQ(8)                                                 SHOWQ(8)
    +
    +
    +NAME
    +       showq - list the Postfix mail queue
    +
    +SYNOPSIS
    +       showq [generic Postfix daemon options]
    +
    +DESCRIPTION
    +       The  showq  daemon  reports the Postfix mail queue status.
    +       It is the program that emulates the sendmail `mailq'  com-
    +       mand.
    +
    +       The  showq  daemon  can also be run in stand-alone mode by
    +       the super-user. This mode of operation is used to  emulate
    +       the `mailq' command while the Postfix mail system is down.
    +
    +SECURITY
    +       The showq daemon can run in a chroot  jail  at  fixed  low
    +       privilege, and takes no input from the client. Its service
    +       port is accessible to local untrusted users, so  the  ser-
    +       vice can be susceptible to denial of service attacks.
    +
    +STANDARDS
    +       None.  The showq daemon does not interact with the outside
    +       world.
    +
    +DIAGNOSTICS
    +       Problems and transactions are logged to syslogd(8).
    +
    +BUGS
    +       The showq daemon runs at a  fixed  low  privilege;  conse-
    +       quently, it cannot extract information from queue files in
    +       the maildrop directory.
    +
    +SEE ALSO
    +       cleanup(8) canonicalize and enqueue mail
    +       pickup(8) local mail pickup service
    +       qmgr(8) mail being delivered, delayed mail
    +       syslogd(8) system logging
    +
    +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
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                1
    +
    +
    +
    diff --git a/postfix/html/small-picture.fig b/postfix/html/small-picture.fig new file mode 100644 index 000000000..a434b41ee --- /dev/null +++ b/postfix/html/small-picture.fig @@ -0,0 +1,108 @@ +#FIG 3.1 +Landscape +Center +Inches +1200 2 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 11850 2250 600 300 11250 1950 12450 2550 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 13500 4350 600 300 12900 4050 14100 4650 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 13500 3300 600 300 12900 3000 14100 3600 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 11850 3300 600 300 11250 3000 12450 3600 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 13500 2250 600 300 12900 1950 14100 2550 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 2700 2775 600 300 2100 2475 3300 3075 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 5700 2775 600 300 5100 2475 6300 3075 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 5700 3825 600 300 5100 3525 6300 4125 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 7350 3300 600 300 6750 3000 7950 3600 +1 2 0 1 -1 6 1 0 20 0.000 1 0.0000 7350 2250 600 300 6750 1950 7950 2550 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 10800 3300 11250 3300 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 12450 3300 12900 3300 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 12225 3075 13125 2475 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 12138 3542 13038 4142 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 14100 2250 14550 2250 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 14100 3300 14550 3300 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 14100 4350 14550 4350 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 14587 4050 15487 4050 15487 4650 14587 4650 14587 4050 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 14550 3000 15450 3000 15450 3600 14550 3600 14550 3000 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 11850 1500 11850 1950 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 11850 2550 11850 3000 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 13500 1500 13500 1950 +2 1 0 1 -1 7 2 0 -1 0.000 0 0 -1 0 0 2 + 14700 1350 13950 2025 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 4 + 10800 3450 11100 3450 11100 4350 10800 4350 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 11850 3600 11850 4050 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 9900 3000 10800 3000 10800 3600 9900 3600 9900 3000 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 9900 4050 10800 4050 10800 4650 9900 4650 9900 4050 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 14550 1950 15450 1950 15450 2550 14550 2550 14550 1950 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 11400 900 12300 900 12300 1500 11400 1500 11400 900 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 13050 900 13950 900 13950 1500 13050 1500 13050 900 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 14550 900 15450 900 15450 1500 14550 1500 14550 900 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 11400 4050 12300 4050 12300 4650 11400 4650 11400 4050 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 9300 3300 9900 3300 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 4 + 9900 3450 9600 3450 9600 4350 9900 4350 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 6225 3675 6825 3450 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 7950 3300 8400 3300 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 6225 2925 6825 3150 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 3300 2775 3750 2775 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 1650 2775 2100 2775 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 4650 2775 5100 2775 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 4650 3825 5100 3825 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 3750 3525 4650 3525 4650 4125 3750 4125 3750 3525 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 5700 4125 5700 4500 +2 1 0 1 -1 7 2 0 -1 0.000 0 0 -1 0 0 2 + 4500 4650 5250 4050 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 7350 2550 7350 3000 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 7350 3600 7350 4050 +2 1 0 1 -1 7 2 0 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 60.00 120.00 + 8550 4200 7800 3525 +2 2 0 0 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 750 2475 1650 2475 1650 3075 750 3075 750 2475 +2 2 0 1 -1 6 1 0 20 0.000 0 0 -1 0 0 5 + 3750 2475 4650 2475 4650 3075 3750 3075 3750 2475 +2 2 0 1 -1 6 1 0 20 0.000 0 0 7 0 0 5 + 8400 3000 9300 3000 9300 3600 8400 3600 8400 3000 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 3750 4500 4650 4500 4650 5100 3750 5100 3750 4500 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 5250 4500 6150 4500 6150 5100 5250 5100 5250 4500 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 6900 4050 7800 4050 7800 4650 6900 4650 6900 4050 +2 2 0 1 -1 3 1 0 20 0.000 0 0 -1 0 0 5 + 8400 4050 9300 4050 9300 4650 8400 4650 8400 4050 +4 0 -1 0 0 0 15 0.0000 4 150 690 14655 3375 Internet\001 +4 0 -1 0 0 0 15 0.0000 4 150 930 14647 4425 UUCP etc.\001 +4 0 -1 0 0 0 15 0.0000 4 150 690 3840 3892 Internet\001 +4 0 -1 0 0 0 15 0.0000 4 150 405 952 2850 local\001 diff --git a/postfix/html/small-picture.gif b/postfix/html/small-picture.gif new file mode 100644 index 000000000..d8c86fc1b Binary files /dev/null and b/postfix/html/small-picture.gif differ diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html new file mode 100644 index 000000000..d6cef08c7 --- /dev/null +++ b/postfix/html/smtp.8.html @@ -0,0 +1,200 @@ +
    +
    +
    +
    +SMTP(8)                                                   SMTP(8)
    +
    +
    +NAME
    +       smtp - Postfix remote delivery via SMTP
    +
    +SYNOPSIS
    +       smtp [generic Postfix daemon options]
    +
    +DESCRIPTION
    +       The  SMTP  client processes message delivery requests from
    +       the queue manager. Each request specifies a queue file,  a
    +       sender address, a domain or host to deliver to, and recip-
    +       ient information.  This program expects to be run from the
    +       master(8) process manager.
    +
    +       The  SMTP  client updates the queue file and marks recipi-
    +       ents as finished, or it informs  the  queue  manager  that
    +       delivery  should  be tried again at a later time. Delivery
    +       problem reports are sent to the bounce(8) or defer(8) dae-
    +       mon as appropriate.
    +
    +       The  SMTP  client  looks  up  a  list  of  mail  exchanger
    +       addresses for the destination  host,  sorts  the  list  by
    +       preference,  and  connects to each listed address until it
    +       finds a server that responds.
    +
    +       Once the SMTP client has received the server greeting ban-
    +       ner, no error will cause it to proceed to the next address
    +       on the mail exchanger list. Instead, the message is either
    +       bounced, or its delivery is deferred until later.
    +
    +SECURITY
    +       The SMTP client is moderately security-sensitive. It talks
    +       to SMTP servers and to DNS servers  on  the  network.  The
    +       SMTP client can be run chrooted at fixed low privilege.
    +
    +STANDARDS
    +       RFC 821 (SMTP protocol)
    +       RFC 1651 (SMTP service extensions)
    +       RFC 1870 (Message Size Declaration)
    +       RFC 2197 (Pipelining)
    +
    +DIAGNOSTICS
    +       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.
    +
    +       Depending on the setting of the notify_classes  parameter,
    +       the  postmaster is notified of bounces, protocol problems,
    +       and of other trouble.
    +
    +BUGS
    +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
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +SMTP(8)                                                   SMTP(8)
    +
    +
    +       command after a configuration change.
    +
    +Miscellaneous
    +       debug_peer_level
    +              Verbose  logging  level  increment  for  hosts that
    +              match 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
    +              debug_peer_level parameter.
    +
    +       inet_interfaces
    +              The network interface addresses that this mail sys-
    +              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
    +              delivery loops.
    +
    +       notify_classes
    +              When  this  parameter  includes the protocol class,
    +              send mail to the  postmaster  with  transcripts  of
    +              SMTP sessions with protocol errors.
    +
    +Resource controls
    +       smtp_destination_concurrency_limit
    +              Limit the number of parallel deliveries to the same
    +              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
    +              default_destination_recipient_limit parameter.
    +
    +Timeout controls
    +       smtp_connect_timeout
    +              Timeout in seconds for completing a TCP connection.
    +              When no connection can be made within the deadline,
    +              the  SMTP client tries the next address on the mail
    +              exchanger list.
    +
    +       smtp_helo_timeout
    +              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
    +              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,
    +              and for receiving the server response.
    +
    +
    +
    +                                                                2
    +
    +
    +
    +
    +
    +SMTP(8)                                                   SMTP(8)
    +
    +
    +       smtp_mail_timeout
    +              Timeout in seconds for sending the MAIL  FROM  com-
    +              mand, and for receiving the server response.
    +
    +       smtp_rcpt_timeout
    +              Timeout in seconds for sending the RCPT TO command,
    +              and for receiving the server response.
    +
    +       smtp_data_init_timeout
    +              Timeout in seconds for sending  the  DATA  command,
    +              and for receiving the server response.
    +
    +       smtp_data_xfer_timeout
    +              Timeout in seconds for sending the message content.
    +
    +       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
    +              be delivered multiple times.
    +
    +       smtp_quit_timeout
    +              Timeout  in  seconds  for sending the QUIT command,
    +              and for receiving the server response.
    +
    +SEE ALSO
    +       bounce(8) non-delivery status reports
    +       master(8) process manager
    +       qmgr(8) queue manager
    +       syslogd(8) system logging
    +
    +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
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                3
    +
    +
    +
    diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html new file mode 100644 index 000000000..2d990dd18 --- /dev/null +++ b/postfix/html/smtpd.8.html @@ -0,0 +1,266 @@ +
    +
    +
    +
    +SMTPD(8)                                                 SMTPD(8)
    +
    +
    +NAME
    +       smtpd - Postfix SMTP server
    +
    +SYNOPSIS
    +       smtpd [generic Postfix daemon options]
    +
    +DESCRIPTION
    +       The  SMTP  server  accepts network connection requests and
    +       performs zero or more SMTP  transactions  per  connection.
    +       Each received message is piped through the cleanup(8) dae-
    +       mon, and is placed into the incoming queue as  one  single
    +       queue  file.   For  this  mode  of  operation, the program
    +       expects to be run from the master(8) process manager.
    +
    +       Alternatively, the SMTP server takes an  established  con-
    +       nection  on  standard input and deposits messages directly
    +       into the maildrop queue.  In  this  so-called  stand-alone
    +       mode,  the SMTP server can accept mail even while the mail
    +       system is not running.
    +
    +       The SMTP server implements a variety of policies for  con-
    +       nection  requests,  and for parameters given to HELO, MAIL
    +       FROM, VRFY and RCPT TO commands. They are  detailed  below
    +       and in the main.cf configuration file.
    +
    +SECURITY
    +       The SMTP server is moderately security-sensitive. It talks
    +       to SMTP clients and to DNS servers  on  the  network.  The
    +       SMTP server can be run chrooted at fixed low privilege.
    +
    +STANDARDS
    +       RFC 821 (SMTP protocol)
    +       RFC 1123 (Host requirements)
    +       RFC 1651 (SMTP service extensions)
    +       RFC 1652 (8bit-MIME transport)
    +       RFC 1854 (SMTP Pipelining)
    +       RFC 1870 (Message Size Declaration)
    +       RFC 1985 (ETRN command) (partial)
    +
    +DIAGNOSTICS
    +       Problems and transactions are logged to syslogd(8).
    +
    +       Depending  on the setting of the notify_classes parameter,
    +       the postmaster is notified of bounces, protocol  problems,
    +       policy violations, and of other trouble.
    +
    +BUGS
    +       RFC  1985  is  implemented  by  forcing  delivery  of  all
    +       deferred mail.
    +
    +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
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +SMTPD(8)                                                 SMTPD(8)
    +
    +
    +       command after a configuration change.
    +
    +Miscellaneous
    +       command_directory
    +              Location  of  Postfix  support  commands  (default:
    +              $program_directory).
    +
    +       debug_peer_level
    +              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
    +              debug_peer_level parameter.
    +
    +       hopcount_limit
    +              Limit the number of Received: message headers.
    +
    +       notify_classes
    +              List of error classes. Of special interest are:
    +
    +              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
    +                     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
    +              greeting banner.
    +
    +       smtpd_recipient_limit
    +              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
    +              receive a client request.
    +
    +Resource controls
    +       line_length_limit
    +              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
    +
    +
    +
    +
    +
    +SMTPD(8)                                                 SMTPD(8)
    +
    +
    +       queue_minfree
    +              Minimal  amount of free space in bytes in the queue
    +              file system for the SMTP server to accept any  mail
    +              at all.
    +
    +Tarpitting
    +       smtpd_error_sleep_time
    +              Time to wait in seconds before sending a 4xx or 5xx
    +              server error response.
    +
    +       smtpd_soft_error_limit
    +              When an SMTP client has made this number of errors,
    +              wait  error_count  seconds before responding to any
    +              client request.
    +
    +       smtpd_hard_error_limit
    +              Disconnect after a client has made this  number  of
    +              errors.
    +
    +UCE control restrictions
    +       smtpd_client_restrictions
    +              Restrict what clients may connect to this mail sys-
    +              tem.
    +
    +       smtpd_helo_required
    +              Require that clients introduce  themselves  at  the
    +              beginning of an SMTP session.
    +
    +       smtpd_helo_restrictions
    +              Restrict  what client hostnames are allowed in HELO
    +              and EHLO commands.
    +
    +       smtpd_sender_restrictions
    +              Restrict what sender addresses are allowed in  MAIL
    +              FROM commands.
    +
    +       smtpd_recipient_restrictions
    +              Restrict  what  recipient  addresses are allowed in
    +              RCPT TO commands.
    +
    +       maps_rbl_domains
    +              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
    +       access_map_reject_code
    +              Server response when a client  violates  an  access
    +              database restriction.
    +
    +
    +
    +
    +
    +                                                                3
    +
    +
    +
    +
    +
    +SMTPD(8)                                                 SMTPD(8)
    +
    +
    +       invalid_hostname_reject_code
    +              Server   response   when   a  client  violates  the
    +              reject_invalid_hostname restriction.
    +
    +       maps_rbl_reject_code
    +              Server  response  when  a   client   violates   the
    +              maps_rbl_domains restriction.
    +
    +       reject_code
    +              Response  code  when  the  client  matches a reject
    +              restriction.
    +
    +       relay_domains_reject_code
    +              Server response when a client attempts  to  violate
    +              the mail relay policy.
    +
    +       unknown_address_reject_code
    +              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
    +              restriction.
    +
    +       unknown_hostname_reject_code
    +              Server  response  when  a   client   violates   the
    +              reject_unknown_hostname restriction.
    +
    +SEE ALSO
    +       cleanup(8) message canonicalization
    +       master(8) process manager
    +       syslogd(8) system logging
    +
    +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
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                4
    +
    +
    +
    diff --git a/postfix/html/transport.5.html b/postfix/html/transport.5.html new file mode 100644 index 000000000..77fe88a36 --- /dev/null +++ b/postfix/html/transport.5.html @@ -0,0 +1,134 @@ +
    +
    +
    +
    +TRANSPORT(5)                                         TRANSPORT(5)
    +
    +
    +NAME
    +       transport - format of Postfix transport table
    +
    +SYNOPSIS
    +       postmap /etc/postfix/transport
    +
    +DESCRIPTION
    +       The  optional  transport  file  specifies  a  mapping from
    +       domain hierarchies to message delivery  transports  and/or
    +       relay hosts. The mapping is used by the trivial-rewrite(8)
    +       daemon.
    +
    +       The file serves as input to the  postmap(1)  command.  The
    +       result,  an  indexed file in dbm or db format, is used for
    +       fast searching by the mail  system.  After  updating  this
    +       table, issue the postfix reload command to make the change
    +       visible.
    +
    +       The format of the transport table is as follows:
    +
    +       blanks and comments
    +              Blank lines are ignored,  as  are  lines  beginning
    +              with `#'.
    +
    +       domain transport:nexthop
    +              Mail  for  domain is delivered through transport to
    +              nexthop.
    +
    +       .domain transport:nexthop
    +              Mail for  any  subdomain  of  domain  is  delivered
    +              through transport to nexthop.
    +
    +              The  interpretation  of the nexthop field is trans-
    +              port  dependent.  In  the  case  of  SMTP,  specify
    +              host:service for a non-default server port, and use
    +              [host] or [host:port] in order to disable MX  (mail
    +              exchanger)  DNS  lookups.  The  [] form can also be
    +              used with IP addresses instead of hostnames.
    +
    +EXAMPLES
    +       In order to send mail for foo.org and its subdomains
    +       via the uucp transport to the UUCP host named foo:
    +
    +            foo.org      uucp:foo
    +            .foo.org     uucp:foo
    +
    +       When no nexthop host name is specified, the destination domain
    +       name is used instead. For example, the following directs mail for
    +       user@foo.org via the slow transport to a mail
    +       exchanger for foo.org.  The slow transport could be
    +       something that runs at most one delivery process at a time:
    +
    +            foo.org      slow:
    +
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +TRANSPORT(5)                                         TRANSPORT(5)
    +
    +
    +       When no transport is specified, the default transport is
    +       used, as specified via the default_transport configuration
    +       parameter. The following sends all mail for foo.org and its
    +       subdomains to host gateway.foo.org:
    +
    +            foo.org      :[gateway.foo.org]
    +            .foo.org     :[gateway.foo.org]
    +
    +       In the above example, the [] are used to suppress MX lookups.
    +       The result would likely point to your local machine.
    +
    +       In the case of delivery via SMTP, one may specify
    +       hostname:service instead of just a host:
    +
    +            foo.org      smtp:bar.org:2025
    +
    +       This directs mail for user@foo.org to host bar.org
    +       port 2025. Instead of a numerical port a symbolic name may be
    +       used. Specify [] around the destination in order to disable MX lookups.
    +
    +CONFIGURATION PARAMETERS
    +       The following main.cf parameters are  especially  relevant
    +       to  this  topic.  See  the Postfix main.cf file for syntax
    +       details and for default values.  Use  the  postfix  reload
    +       command after a configuration change.
    +
    +       transport_maps
    +              List of transport lookup tables.
    +
    +       Other parameters of interest:
    +
    +       default_transport
    +              The  transport  to use when no transport is explic-
    +              itly specified.
    +
    +       relayhost
    +              The default host to send to when no transport table
    +              entry matches.
    +
    +SEE ALSO
    +       postmap(1) create mapping table
    +       trivial-rewrite(8) rewrite and resolve addresses
    +
    +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
    +
    +
    +
    +
    +
    +                                                                2
    +
    +
    +
    diff --git a/postfix/html/trivial-rewrite.8.html b/postfix/html/trivial-rewrite.8.html new file mode 100644 index 000000000..08ff75d86 --- /dev/null +++ b/postfix/html/trivial-rewrite.8.html @@ -0,0 +1,200 @@ +
    +
    +
    +
    +TRIVIAL-REWRITE(8)                             TRIVIAL-REWRITE(8)
    +
    +
    +NAME
    +       trivial-rewrite  - Postfix address rewriting and resolving
    +       daemon
    +
    +SYNOPSIS
    +       trivial-rewrite [generic Postfix daemon options]
    +
    +DESCRIPTION
    +       The trivial-rewrite daemon processes two types  of  client
    +       service requests:
    +
    +       rewrite
    +              Rewrite  an  address to standard form. The trivial-
    +              rewrite daemon  by  default  appends  local  domain
    +              information  to  unqualified  addresses, swaps bang
    +              paths to domain form,  and  strips  source  routing
    +              information.  This process is under control of sev-
    +              eral configuration parameters (see below).
    +
    +       resolve
    +              Resolve an address to a (transport, nexthop, recip-
    +              ient) triple. The meaning of the results is as fol-
    +              lows:
    +
    +              transport
    +                     The delivery agent to use. This is the first
    +                     field of an entry in the master.cf file.
    +
    +              nexthop
    +                     The host to send to. For local delivery this
    +                     is an empty string.
    +
    +              recipient
    +                     The  envelope  recipient  address  that   is
    +                     passed on to nexthop.
    +
    +              The  trivial-rewrite daemon by default only distin-
    +              guishes between local and non-local mail. For finer
    +              control  over mail routing, use the optional trans-
    +              port(5) lookup table.
    +
    +       This program expects to be run from the master(8)  process
    +       manager.
    +
    +STANDARDS
    +       None.  The  command  does  not  interact  with the outside
    +       world.
    +
    +SECURITY
    +       The trivial-rewrite daemon is not security sensitive.   By
    +       default,  this  daemon  does  not  talk to remote or local
    +       users.  It can run at a fixed low privilege in a  chrooted
    +       environment.
    +
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +TRIVIAL-REWRITE(8)                             TRIVIAL-REWRITE(8)
    +
    +
    +DIAGNOSTICS
    +       Problems and transactions are logged to syslogd(8).
    +
    +BUGS
    +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
    +       command after a configuration change.
    +
    +Miscellaneous
    +       inet_interfaces
    +              The  network  interfaces  that  this  mail   system
    +              receives  mail  on.   This  information  is used to
    +              determine if user@[net.work.addr.ess] is  local  or
    +              remote.
    +
    +       mydestination
    +              List  of domains that this machine considers local.
    +
    +       myorigin
    +              The domain that locally-posted mail appears to come
    +              from.
    +
    +Rewriting
    +       allow_percent_hack
    +              Rewrite user%domain to user@domain.
    +
    +       append_at_myorigin
    +              Rewrite user to user@$myorigin.
    +
    +       append_dot_mydomain
    +              Rewrite user@host to user@host.$mydomain.
    +
    +       swap_bangpath
    +              Rewrite site!user to user@site.
    +
    +Routing
    +       default_transport
    +              The  default  transport to use when no transport is
    +              explicitly given in the transport(5) table.
    +
    +       relayhost
    +              The default host to send mail to when no  entry  is
    +              matched in the transport(5) table.
    +
    +              When  no  relayhost  is  specified,  mail is routed
    +              directly to the destination's mail exchanger.
    +
    +       transport_maps
    +              List of tables with domain to (transport,  nexthop)
    +              mappings.
    +
    +
    +
    +
    +
    +                                                                2
    +
    +
    +
    +
    +
    +TRIVIAL-REWRITE(8)                             TRIVIAL-REWRITE(8)
    +
    +
    +SEE ALSO
    +       master(8) process manager
    +       syslogd(8) system logging
    +       transport(5) transport table format
    +
    +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
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                3
    +
    +
    +
    diff --git a/postfix/html/uce.html b/postfix/html/uce.html new file mode 100644 index 000000000..51865405e --- /dev/null +++ b/postfix/html/uce.html @@ -0,0 +1,645 @@ + + + + + Postfix Configuration - UCE Controls + + + + + +

    +Postfix Configuration - UCE Controls

    + +
    + +Up one level | Basic +Configuration | UCE Controls | Rate +Controls | Resource Controls | Address Manipulation + +

    Introduction

    + +Postfix offers a variety of parameters that limit the delivery of +unsolicited commercial email (UCE). + +

    + +By default, the Postfix SMTP server will +accept mail only from or to the local network or domain, so that +your system can't be used as a mail relay to forward bulk mail from +random strangers. + +

    + +The text in this document describes how you can set up more detailed +anti-UCE policies that prevent delivery of unwanted email altogether, +for example with sendmail-style access lists or with RBL +(real-time blackhole list) name servers. + +

    Unless indicated otherwise, all parameters described here are +in the main.cf file. If you change parameters of a running +Postfix system, don't forget to issue a postfix reload +command. + +

    + + + +

    Client name/address restrictions

    + +The smtpd_client_restrictions parameter restricts what +clients this system accepts SMTP connections from. + +

    + +

    + +
    Default: + +
    Allow SMTP connections from any client. + +

    + +

    Syntax: + +
    Specify a list of zero or more restrictions, separated by +whitespace or commas. Restrictions are applied in the order as +specified; the first restriction that matches wins. + +

    + +

    Examples: + +
    smtpd_client_restrictions = hash:/etc/postfix/access, +reject_maps_rbl + +
    smtpd_client_restrictions = permit_mynetworks, +reject_unknown_client + +

    + +

    Restrictions: + +

    + +

    + + + +
    reject_unknown_client
    Reject the request when the +client address to name lookup failed. The +unknown_client_reject_code parameter specifies the response +code to rejected requests (default: 450). + +

    + + + +

    permit_mynetworks
    Permit the request when the +client address matches any network listed in $mynetworks. + +

    + + + +

    check_client_access maptype:mapname + +
    maptype:mapname
    Search the named access database for the client name, parent +domains, client address, or networks obtained by stripping least +significant octets. Reject the request if the result is REJECT +or "[45]XX text". Permit the request if the result +is anything else. The access_map_reject_code parameter +specifies the response code for REJECT results (default: +550). + +

    + + + +

    reject_maps_rbl
    Reject the request when the client +network address is listed under any of the domains listed in $maps_rbl_domains. The +maps_rbl_reject_code parameter specifies the response code for +rejected requests (default: 550). + +

    + +

    permit + +
    reject + +
    See generic restrictions. + +
    + +
    + + + +

    Require HELO (EHLO) command

    + +The smtpd_helo_required parameter determines if clients must +send a HELO (EHLO) command at the beginning of an +SMTP session. Requiring this will stop some UCE software. + +

    + +

    + +
    Default: + +
    By default, the Postfix SMTP server +does not require the use of HELO (EHLO). + +

    + +

    Syntax: + +
    Specify yes or no. + +

    + +

    Example: + +
    smtpd_helo_required = yes + +
    + + + +

    HELO (EHLO) hostname restrictions

    + +The smtpd_helo_restrictions parameter restricts what hostnames +clients may send with the HELO (EHLO) command. Some +UCE software can be stopped by being strict here. + +
    + +
    Default: + +
    By default, the Postfix SMTP server +accepts any hostname. + +

    + +

    Syntax: + +
    Specify a list of zero or more restrictions, separated by +whitespace or commas. Restrictions are applied in the order as +specified; the first restriction that matches wins. + +

    + +In addition to restrictions that are specific to HELO (EHLO) +command parameters, you can also specify restrictions based +on the client hostname or network address. + +

    + +

    Example: + +
    smtpd_helo_restrictions = reject_invalid_hostname + +

    + +

    Restrictions: + +

    + +

    + + + +
    reject_invalid_hostname
    Reject the request when +the client HELO and EHLO command has a bad hostname syntax. The +invalid_hostname_reject_code specifies the response code to +rejected requests (default: 501). + +

    + + + +

    permit_naked_ip_address
    Permit the request when +the client HELO (EHLO) command contains a naked IP address without +the enclosing [] brackets that the RFC requires. Unfortunately, +some popular PC mail clients send HELO greetings in this +manner. + +

    + + + +

    reject_unknown_hostname
    Reject the request when +the hostname in the client HELO (EHLO) command has no DNS A or MX +record. The unknown_hostname_reject_code specifies the +response code to rejected requests (default: 450). + +

    + + + +

    check_helo_access maptype:mapname + +
    maptype:mapname
    Search the named access database for the HELO hostname +or parent domains in the specified table. Reject the request if +the result is REJECT or "[45]XX text". Permit +the request when the result is anything else. The +access_map_reject_code parameter specifies the response +code for REJECT results (default: 550). + +

    + +

    reject_unknown_client + +
    permit_mynetworks + +
    check_client_access maptype:mapname + +
    See client name/address restrictions. + +

    + +

    permit + +
    reject + +
    See generic restrictions. + +
    + +
    + + + +

    Sender address restrictions

    + +The smtpd_sender_restrictions parameter restricts what sender +addresses this system accepts in MAIL FROM commands. + +

    + +

    + +
    Default: + +
    By default, the Postfix SMTP server +accepts any sender address. + +

    + +

    Syntax: + +
    Specify a list of zero or more restrictions, separated by +whitespace or commas. Restrictions are applied in the order as +specified; the first restriction that matches wins. + +

    + +In addition to restrictions that are specific to sender mail +addresses, you can also specify restrictions based on the information +passed with the HELO/EHLO command, and on the client hostname or +network address. + +

    + +

    Example: + +
    smtpd_sender_restrictions = reject_unknown_address + +

    + +

    Restrictions: + +
    + + + +
    reject_unknown_address
    Reject the request when +the sender mail address has no DNS A or MX record. The +unknown_address_reject_code parameter specifies the response +code for rejected requests (default: 450). + +

    + + + +

    check_sender_access maptype:mapname + +
    maptype:mapname
    Search the named access database for the sender mail address, +parent domain, or localpart@. Reject the request if the +result is REJECT or "[45]XX text". Permit the +request if the result is anything else. The access_map_reject_code + parameter specifies the result code for rejected requests +(default: 550). + +

    + +

    permit_naked_ip_address + +
    reject_invalid_hostname + +
    reject_unknown_hostname + +
    check_helo_access maptype:mapname + +
    See HELO (EHLO) hostname restrictions. + +

    + +

    reject_unknown_client + +
    permit_mynetworks + +
    check_client_access maptype:mapname + +
    See client name/address restrictions. + +

    + +

    permit + +
    reject + +
    See generic restrictions. + +
    + +
    + + + +

    Recipient address restrictions

    + +The smtpd_recipient_restrictions parameter restricts what +recipient addresses this system accepts in RCPT TO commands. + +
    + +
    Default: + +
    By default, the Postfix SMTP server +forwards mail from any client that matches $mynetworks or $relay_domains, or to any destination +that matches $relay_domains. + +

    + +

    Syntax: + +
    Specify a list of zero or more restrictions, separated by +whitespace or commas. Restrictions are applied in the order as +specified; the first restriction that matches wins. + +

    + +In addition to restrictions that are specific to recipient mail +addresses, you can also specify restrictions based on the sender mail +address, on the information passed with the HELO/EHLO command, and +on the client hostname or network address. + +

    + +

    Example: + +
    smtpd_recipient_restrictions = permit_mynetworks, +check_relay_domains + +

    + +

    Restrictions: + +
    + + + +
    check_relay_domains
    Permit the request when the +client hostname matches $relay_domains, +or when the resolved destination address matches +$relay_domains, otherwise reject. The relay_domains_reject_code +parameter specifies the response code for rejected requests (default: +550). + +

    + + + +

    permit_mx_backup
    Permit the request when the local +mail system is MX host for the resolved destination. This includes +the case that the local mail system is the final destination. +Relevant configuration parameters: +$mydestination, +$inet_interfaces. + +

    + +

    check_recipient_access maptype:mapname + +
    maptype:mapname
    Search the named access database for the resolved destination +address, parent domain, or localpart@. Reject the request +if the result is REJECT or "[45]XX text". +Permit the request if the result is anything else. The +access_map_reject_code parameter specifies the result code +for rejected requests (default: 550). + +

    + +

    reject_unknown_address + +
    check_sender_access maptype:mapname + +
    See sender address restrictions. + +

    + +

    permit_naked_ip_address + +
    reject_invalid_hostname + +
    reject_unknown_hostname + +
    check_helo_access maptype:mapname + +
    See HELO (EHLO) hostname restrictions. + +

    + +

    reject_unknown_client + +
    permit_mynetworks + +
    check_client_access maptype:mapname + +
    See client name/address restrictions. + +

    + +

    permit + +
    reject + +
    See generic restrictions. + +
    + +
    + + + +

    Generic restrictions

    + +The following restrictions can use used for client hostnames or +addresses, for HELO (EHLO) hostnames, for sender mail addresses +and for recipient mail addresses. + +
    + +Restrictions: + +

    + +

    + + + +
    permit
    Permit the request. This restriction +is useful at the end of a restriction list, to make the default +policy explicit. + +

    + + + +

    reject
    Reject the request. This restriction +is useful at the end of a restriction list, to make the default +policy explicit. The reject_code configuration parameter +specifies the response code to rejected requests (default: +550). + +
    + +
    + + + +

    Additional UCE control parameters

    + +
    + + + +
    maps_rbl_domains + +
    This parameter controls the behavior of the reject_maps_rbl restriction that can +appear as part of a client name/address restriction list. + +

    + +

    + +
    Default: + +
    maps_rbl_domains = rbl.maps.vix.com + +

    + +Note: RBL lookups are disabled by default. + +

    + +

    Syntax: + +
    Zero or more DNS domains that blacklist client addresses. A +host is blacklisted when its reversed IP address is listed as a +subdomain under any of the domains listed in $maps_rbl_domains. + +
    + +

    + + + +

    relay_domains + +
    This parameter controls the behavior of the check_relay_domains restriction +that can appear as part of a recipient address restriction list. + +

    + +

    + +
    Default: + +
    relay_domains = +$mydestination, $virtual_maps. + +

    + +

    Syntax: + +
    Specify zero or more domain names, /file/name patterns +and/or type:name lookup tables, separated by whitespace +and/or commas. A /file/name is replaced by its contents; +type:name requests that table lookup is done instead +of string comparison. + +
    + +

    + +A host or destination address matches $relay_domains when +its name or parent domain matches any of the names, files or lookup +tables listed in $relay_domains. + +

    + +
    + +Up one level | Basic +Configuration | UCE Controls | Rate +Controls | Resource Controls | Address Manipulation + + + + diff --git a/postfix/html/virtual.5.html b/postfix/html/virtual.5.html new file mode 100644 index 000000000..9b518d62e --- /dev/null +++ b/postfix/html/virtual.5.html @@ -0,0 +1,200 @@ +
    +
    +
    +
    +VIRTUAL(5)                                             VIRTUAL(5)
    +
    +
    +NAME
    +       virtual - format of Postfix virtual table
    +
    +SYNOPSIS
    +       postmap /etc/postfix/virtual
    +
    +DESCRIPTION
    +       The  optional  virtual  table  specifies  redirections for
    +       local and non-local recipients or  domains.  The  redirec-
    +       tions  are used by the cleanup(8) daemon. The redirections
    +       are recursive.
    +
    +       The virtual redirection is applied only to  the  recipient
    +       envelope  address,  and  does  not affect message headers.
    +       Think Sendmail rule set S0, if you like. Use  canonical(5)
    +       mapping  to  rewrite header and envelope addresses in gen-
    +       eral.
    +
    +       The file serves as input to the  postmap(1)  command.  The
    +       result,  an  indexed file in dbm or db format, is used for
    +       fast searching by the mail system. After an update it  may
    +       take  a  minute  or  so before the change becomes visible.
    +       Issue a postfix reload command to eliminate the delay.
    +
    +       Typical support for a virtual domain looks like  the  fol-
    +       lowing:
    +
    +           virtual.domain       anything (right-hand content does not matter)
    +           user1@virtual.domain address1
    +           user2@virtual.domain address2, address3
    +
    +       With this, the SMTP server accepts mail for virtual.domain
    +       (provided that the relay_domains parameter includes  $vir-
    +       tual_maps), and mail for unknown@virtual.domain is bounced
    +       as undeliverable.
    +
    +       The format of the virtual table is  as  follows,  mappings
    +       being tried in the order as listed in this manual page:
    +
    +       blanks and comments
    +              Blank  lines  are  ignored,  as are lines beginning
    +              with `#'.
    +
    +       user@domain address, address, ...
    +              Mail for  user@domain  is  redirected  to  address.
    +              This form has the highest precedence.
    +
    +       user address, address, ...
    +              Mail  for  user@site  is redirected to address when
    +              site is equal to $myorigin, when site is listed  in
    +              $mydestination,   or   when   it   is   listed   in
    +              $inet_interfaces.
    +
    +              This functionality overlaps with  functionality  of
    +
    +
    +
    +                                                                1
    +
    +
    +
    +
    +
    +VIRTUAL(5)                                             VIRTUAL(5)
    +
    +
    +              the local alias(5) database. The difference is that
    +              virtual  mapping  can  be  applied   to   non-local
    +              addresses.
    +
    +       @domain address, address, ...
    +              Mail  for  any  user  in  domain  is  redirected to
    +              address.  This form has the lowest precedence.
    +
    +       In all the above forms, when address has the form  @other-
    +       domain,  the result is the same user in otherdomain.  This
    +       works for the first address in the expansion only.
    +
    +ADDRESS EXTENSION
    +       When the search fails, and the address localpart  contains
    +       the  optional recipient delimiter (e.g., user+foo@domain),
    +       the search is repeated for the  unextended  address  (e.g.
    +       user@domain), and the unmatched address extension is prop-
    +       agated to the result of expansion. The matching order  is:
    +       user+foo@domain, user@domain, user+foo, user, and @domain.
    +
    +BUGS
    +       The table format does not understand quoting  conventions.
    +
    +CONFIGURATION PARAMETERS
    +       The  following  main.cf parameters are especially relevant
    +       to this topic. See the Postfix  main.cf  file  for  syntax
    +       details  and  for  default  values. Use the postfix reload
    +       command after a configuration change.
    +
    +       virtual_maps
    +              List of virtual mapping tables.
    +
    +       Other parameters of interest:
    +
    +       inet_interfaces
    +              The network interface addresses  that  this  system
    +              receives mail on.
    +
    +       mydestination
    +              List  of  domains  that  this mail system considers
    +              local.
    +
    +       myorigin
    +              The domain that is appended to locally-posted mail.
    +
    +SEE ALSO
    +       cleanup(8) canonicalize and enqueue mail
    +       postmap(1) create mapping table
    +
    +LICENSE
    +       The  Secure  Mailer  license must be distributed with this
    +       software.
    +
    +
    +
    +
    +
    +                                                                2
    +
    +
    +
    +
    +
    +VIRTUAL(5)                                             VIRTUAL(5)
    +
    +
    +AUTHOR(S)
    +       Wietse Venema
    +       IBM T.J. Watson Research
    +       P.O. Box 704
    +       Yorktown Heights, NY 10598, USA
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +                                                                3
    +
    +
    +
    diff --git a/postfix/include/.keep b/postfix/include/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/postfix/lib/.keep b/postfix/lib/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/postfix/local/.indent.pro b/postfix/local/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/local/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/local/.printfck b/postfix/local/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/local/.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/local/Makefile.in b/postfix/local/Makefile.in new file mode 100644 index 000000000..568b32f16 --- /dev/null +++ b/postfix/local/Makefile.in @@ -0,0 +1,355 @@ +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 +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 +HDRS = local.h +TESTSRC = +WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ + -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ + -Wunused +DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) +PROG = local +TESTPROG= +INC_DIR = ../include +LIBS = ../lib/libmaster.a ../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 + cp *.h 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' +alias.o: alias.c +alias.o: ../include/sys_defs.h +alias.o: ../include/msg.h +alias.o: ../include/htable.h +alias.o: ../include/dict.h +alias.o: ../include/vstream.h +alias.o: ../include/vbuf.h +alias.o: ../include/argv.h +alias.o: ../include/stringops.h +alias.o: ../include/mymalloc.h +alias.o: ../include/vstring.h +alias.o: ../include/mail_params.h +alias.o: ../include/mail_addr.h +alias.o: ../include/sent.h +alias.o: ../include/defer.h +alias.o: ../include/bounce.h +alias.o: ../include/maps.h +alias.o: ../include/mypwd.h +alias.o: ../include/canon_addr.h +alias.o: local.h +alias.o: ../include/been_here.h +alias.o: ../include/tok822.h +alias.o: ../include/resolve_clnt.h +command.o: command.c +command.o: ../include/sys_defs.h +command.o: ../include/msg.h +command.o: ../include/htable.h +command.o: ../include/vstring.h +command.o: ../include/vbuf.h +command.o: ../include/vstream.h +command.o: ../include/argv.h +command.o: ../include/defer.h +command.o: ../include/bounce.h +command.o: ../include/sent.h +command.o: ../include/been_here.h +command.o: ../include/mail_params.h +command.o: ../include/pipe_command.h +command.o: ../include/mail_copy.h +command.o: local.h +command.o: ../include/tok822.h +command.o: ../include/resolve_clnt.h +deliver_attr.o: deliver_attr.c +deliver_attr.o: ../include/sys_defs.h +deliver_attr.o: ../include/msg.h +deliver_attr.o: ../include/vstream.h +deliver_attr.o: ../include/vbuf.h +deliver_attr.o: local.h +deliver_attr.o: ../include/htable.h +deliver_attr.o: ../include/vstring.h +deliver_attr.o: ../include/been_here.h +deliver_attr.o: ../include/tok822.h +deliver_attr.o: ../include/resolve_clnt.h +delivered.o: delivered.c +delivered.o: ../include/sys_defs.h +delivered.o: ../include/msg.h +delivered.o: ../include/htable.h +delivered.o: ../include/vstring.h +delivered.o: ../include/vbuf.h +delivered.o: ../include/vstream.h +delivered.o: ../include/vstring_vstream.h +delivered.o: ../include/stringops.h +delivered.o: ../include/record.h +delivered.o: ../include/rec_type.h +delivered.o: ../include/is_header.h +delivered.o: ../include/quote_822_local.h +delivered.o: ../include/header_opts.h +delivered.o: local.h +delivered.o: ../include/been_here.h +delivered.o: ../include/tok822.h +delivered.o: ../include/resolve_clnt.h +dotforward.o: dotforward.c +dotforward.o: ../include/sys_defs.h +dotforward.o: ../include/msg.h +dotforward.o: ../include/vstring.h +dotforward.o: ../include/vbuf.h +dotforward.o: ../include/vstream.h +dotforward.o: ../include/htable.h +dotforward.o: ../include/open_as.h +dotforward.o: ../include/lstat_as.h +dotforward.o: ../include/iostuff.h +dotforward.o: ../include/stringops.h +dotforward.o: ../include/mymalloc.h +dotforward.o: ../include/mypwd.h +dotforward.o: ../include/bounce.h +dotforward.o: ../include/been_here.h +dotforward.o: ../include/mail_params.h +dotforward.o: local.h +dotforward.o: ../include/tok822.h +dotforward.o: ../include/resolve_clnt.h +feature.o: feature.c +feature.o: ../include/sys_defs.h +feature.o: ../include/msg.h +feature.o: ../include/stringops.h +feature.o: ../include/mymalloc.h +feature.o: ../include/mail_params.h +feature.o: local.h +feature.o: ../include/htable.h +feature.o: ../include/vstream.h +feature.o: ../include/vbuf.h +feature.o: ../include/vstring.h +feature.o: ../include/been_here.h +feature.o: ../include/tok822.h +feature.o: ../include/resolve_clnt.h +file.o: file.c +file.o: ../include/sys_defs.h +file.o: ../include/msg.h +file.o: ../include/htable.h +file.o: ../include/vstring.h +file.o: ../include/vbuf.h +file.o: ../include/vstream.h +file.o: ../include/deliver_flock.h +file.o: ../include/open_as.h +file.o: ../include/mail_copy.h +file.o: ../include/bounce.h +file.o: ../include/defer.h +file.o: ../include/sent.h +file.o: ../include/been_here.h +file.o: ../include/mail_params.h +file.o: local.h +file.o: ../include/tok822.h +file.o: ../include/resolve_clnt.h +forward.o: forward.c +forward.o: ../include/sys_defs.h +forward.o: ../include/msg.h +forward.o: ../include/mymalloc.h +forward.o: ../include/htable.h +forward.o: ../include/argv.h +forward.o: ../include/vstring.h +forward.o: ../include/vbuf.h +forward.o: ../include/vstream.h +forward.o: ../include/vstring_vstream.h +forward.o: ../include/iostuff.h +forward.o: ../include/stringops.h +forward.o: ../include/mail_proto.h +forward.o: ../include/mail_queue.h +forward.o: ../include/cleanup_user.h +forward.o: ../include/sent.h +forward.o: ../include/record.h +forward.o: ../include/rec_type.h +forward.o: ../include/mark_corrupt.h +forward.o: ../include/mail_date.h +forward.o: ../include/mail_params.h +forward.o: local.h +forward.o: ../include/been_here.h +forward.o: ../include/tok822.h +forward.o: ../include/resolve_clnt.h +include.o: include.c +include.o: ../include/sys_defs.h +include.o: ../include/msg.h +include.o: ../include/htable.h +include.o: ../include/mymalloc.h +include.o: ../include/vstream.h +include.o: ../include/vbuf.h +include.o: ../include/open_as.h +include.o: ../include/stat_as.h +include.o: ../include/iostuff.h +include.o: ../include/mypwd.h +include.o: ../include/bounce.h +include.o: ../include/defer.h +include.o: ../include/been_here.h +include.o: ../include/mail_params.h +include.o: local.h +include.o: ../include/vstring.h +include.o: ../include/tok822.h +include.o: ../include/resolve_clnt.h +indirect.o: indirect.c +indirect.o: ../include/sys_defs.h +indirect.o: ../include/msg.h +indirect.o: ../include/htable.h +indirect.o: ../include/mail_params.h +indirect.o: ../include/bounce.h +indirect.o: ../include/defer.h +indirect.o: ../include/been_here.h +indirect.o: local.h +indirect.o: ../include/vstream.h +indirect.o: ../include/vbuf.h +indirect.o: ../include/vstring.h +indirect.o: ../include/tok822.h +indirect.o: ../include/resolve_clnt.h +local.o: local.c +local.o: ../include/sys_defs.h +local.o: ../include/msg.h +local.o: ../include/mymalloc.h +local.o: ../include/htable.h +local.o: ../include/vstring.h +local.o: ../include/vbuf.h +local.o: ../include/vstream.h +local.o: ../include/iostuff.h +local.o: ../include/name_mask.h +local.o: ../include/set_eugid.h +local.o: ../include/mail_queue.h +local.o: ../include/recipient_list.h +local.o: ../include/deliver_request.h +local.o: ../include/deliver_completed.h +local.o: ../include/mail_params.h +local.o: ../include/mail_addr.h +local.o: ../include/config.h +local.o: ../include/been_here.h +local.o: ../include/mail_server.h +local.o: local.h +local.o: ../include/tok822.h +local.o: ../include/resolve_clnt.h +mailbox.o: mailbox.c +mailbox.o: ../include/sys_defs.h +mailbox.o: ../include/msg.h +mailbox.o: ../include/htable.h +mailbox.o: ../include/vstring.h +mailbox.o: ../include/vbuf.h +mailbox.o: ../include/vstream.h +mailbox.o: ../include/mymalloc.h +mailbox.o: ../include/stringops.h +mailbox.o: ../include/set_eugid.h +mailbox.o: ../include/get_hostname.h +mailbox.o: ../include/make_dirs.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/sent.h +mailbox.o: ../include/mypwd.h +mailbox.o: ../include/been_here.h +mailbox.o: ../include/mail_params.h +mailbox.o: local.h +mailbox.o: ../include/tok822.h +mailbox.o: ../include/resolve_clnt.h +maildir.o: maildir.c +maildir.o: ../include/sys_defs.h +maildir.o: ../include/msg.h +maildir.o: ../include/mymalloc.h +maildir.o: ../include/stringops.h +maildir.o: ../include/vstream.h +maildir.o: ../include/vbuf.h +maildir.o: ../include/vstring.h +maildir.o: ../include/make_dirs.h +maildir.o: ../include/set_eugid.h +maildir.o: ../include/get_hostname.h +maildir.o: ../include/mail_copy.h +maildir.o: ../include/bounce.h +maildir.o: ../include/sent.h +maildir.o: ../include/mail_params.h +maildir.o: local.h +maildir.o: ../include/htable.h +maildir.o: ../include/been_here.h +maildir.o: ../include/tok822.h +maildir.o: ../include/resolve_clnt.h +recipient.o: recipient.c +recipient.o: ../include/sys_defs.h +recipient.o: ../include/msg.h +recipient.o: ../include/mymalloc.h +recipient.o: ../include/htable.h +recipient.o: ../include/split_at.h +recipient.o: ../include/stringops.h +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 +recipient.o: ../include/vstring.h +recipient.o: ../include/been_here.h +recipient.o: ../include/tok822.h +recipient.o: ../include/resolve_clnt.h +resolve.o: resolve.c +resolve.o: ../include/sys_defs.h +resolve.o: ../include/msg.h +resolve.o: ../include/vstring.h +resolve.o: ../include/vbuf.h +resolve.o: ../include/htable.h +resolve.o: ../include/mail_proto.h +resolve.o: ../include/vstream.h +resolve.o: ../include/iostuff.h +resolve.o: ../include/resolve_clnt.h +resolve.o: ../include/rewrite_clnt.h +resolve.o: ../include/tok822.h +resolve.o: ../include/mail_params.h +resolve.o: local.h +resolve.o: ../include/been_here.h +token.o: token.c +token.o: ../include/sys_defs.h +token.o: ../include/msg.h +token.o: ../include/vstring.h +token.o: ../include/vbuf.h +token.o: ../include/vstream.h +token.o: ../include/htable.h +token.o: ../include/readline.h +token.o: ../include/mymalloc.h +token.o: ../include/vstring_vstream.h +token.o: ../include/tok822.h +token.o: ../include/resolve_clnt.h +token.o: ../include/mail_params.h +token.o: local.h +token.o: ../include/been_here.h diff --git a/postfix/local/Musings b/postfix/local/Musings new file mode 100644 index 000000000..6149a2e03 --- /dev/null +++ b/postfix/local/Musings @@ -0,0 +1,39 @@ +Local delivery models + +The "monolithic" model: recursively expand the complete initial +recipient list (via aliases, mailing lists, .forward files) to one +expanded recipient list (mail addresses, shell commands, files, +mailboxes). Sort/uniq the expanded recipient list, and deliver. + +The "forward as if sent by recipient" model: each level of recursion +(aliases, mailing lists, forward files) takes one entire iteration +through the mail system. Non-recursively expand one local recipient +(via alias, mailing list, the recipient's .forward file) to a list +of expanded recipients. Sort/uniq the list and deliver by re-injecting +messages into the mail system. Since recipient expansion uses a +non-recursive algorithm, the mailer might loop indefinitely, +re-injecting messages into itself. These local forwarding loops +must be broken by stamping a message when it reaches the local +delivery stage (e.g., by adding a Delivered-To: message header). + +The Postfix system uses a hybrid approach. It does recursive alias +expansion, but only one initial recipient at a time. It delivers +to expanded recipients by re-submitting the message into the mail +system, so it can keep track of the delivery status for each expanded +recipient. Because alias expansion does not look in .forward files, +it cannot prevent local forwarding loops. The Postfix system adds +Delivered: message headers to break local and external forwarding +loops. + +Delivery status management + +The "exact" model: maintain on file the delivery status of each +expanded recipient: remote recipients, shell commands and files, +including the privileges for delivery to shell commands and files. + +The "safe" model: maintain on file only the delivery status of +non-sensitive destinations (local or remote addresses). Deliver to +sensitive destinations first (commands, files), but do not keep a +record of their status on file (including privileges). This means +that the mail system will occasionally deliver the same message +more than once to a file or command. diff --git a/postfix/local/alias.c b/postfix/local/alias.c new file mode 100644 index 000000000..aa94434c4 --- /dev/null +++ b/postfix/local/alias.c @@ -0,0 +1,301 @@ +/*++ +/* NAME +/* alias 3 +/* SUMMARY +/* alias data base lookups +/* SYNOPSIS +/* #include "local.h" +/* +/* int deliver_alias(state, usr_attr, statusp) +/* LOCAL_STATE state; +/* USER_ATTR usr_attr; +/* int *statusp; +/* DESCRIPTION +/* deliver_alias() looks up the expansion of the recipient in +/* the global alias database and delivers the message to the +/* listed destinations. The result is zero when no alias was found +/* or when the message should be delivered to the user instead. +/* +/* deliver_alias() has wired-in knowledge about a few reserved +/* recipient names. +/* .IP \(bu +/* When no alias is found for the local \fIpostmaster\fR or +/* \fImailer-daemon\fR a warning is issued and the message +/* is discarded. +/* .IP \(bu +/* When an alias exists for recipient \fIname\fR, and an alias +/* exists for \fIowner-name\fR, the sender address is changed +/* to \fIowner-name\fR, and the owner delivery attribute is +/* set accordingly. +/* .PP +/* Arguments: +/* .IP state +/* Attributes that specify the message, recipient and more. +/* Expansion type (alias, include, .forward). +/* A table with the results from expanding aliases or lists. +/* A table with delivered-to: addresses taken from the message. +/* .IP usr_attr +/* User attributes (rights, environment). +/* .IP statusp +/* Delivery status. See below. +/* DIAGNOSTICS +/* Fatal errors: out of memory. The delivery 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 +#include +#include +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "local.h" + +/* Application-specific. */ + +#define NO 0 +#define YES 1 + +/* dict_owner - find out alias database owner */ + +static uid_t dict_owner(char *table) +{ + char *myname = "dict_owner"; + DICT *dict; + struct stat st; + + /* + * This code sits here for now, but we may want to move it to the library + * some time. + */ + if ((dict = dict_handle(table)) == 0) + msg_panic("%s: can't find dictionary: %s", myname, table); + if (dict->fd < 0) + return (0); + if (fstat(dict->fd, &st) < 0) + msg_fatal("%s: fstat dictionary %s: %m", myname, table); + return (st.st_uid); +} + +/* deliver_alias - expand alias file entry */ + +int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) +{ + char *myname = "deliver_alias"; + const char *alias_result; + char *expansion; + char *owner; + static MAPS *maps; + char **cpp; + uid_t alias_uid; + struct mypasswd *alias_pwd; + VSTRING *canon_owner; + + /* + * Make verbose logging easier to understand. + */ + state.level++; + if (msg_verbose) + msg_info("%s[%d]: %s", myname, state.level, state.msg_attr.local); + + /* + * Do this only once. + */ + if (maps == 0) + maps = maps_create("aliases", var_alias_maps); + + /* + * DUPLICATE/LOOP ELIMINATION + * + * We cannot do duplicate elimination here. Sendmail compatibility requires + * that we allow multiple deliveries to the same alias, even recursively! + * For example, we must deliver to mailbox any messags that are addressed + * to the alias of a user that lists that same alias in her own .forward + * file. Yuck! This is just an example of some really perverse semantics + * that people will expect Postfix to implement just like sendmail. + * + * We can recognize one special case: when an alias includes its own name, + * deliver to the user instead, just like sendmail. Otherwise, we just + * bail out when nesting reaches some unreasonable depth, and blame it on + * a possible alias loop. + */ + if (state.msg_attr.exp_from != 0 + && strcasecmp(state.msg_attr.exp_from, state.msg_attr.local) == 0) + return (NO); + if (state.level > 100) { + msg_warn("possible alias database loop for %s", state.msg_attr.local); + *statusp = bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "possible alias database loop for %s", state.msg_attr.local); + return (YES); + } + state.msg_attr.exp_from = state.msg_attr.local; + + /* + * There are a bunch of roles that we're trying to keep track of. + * + * First, there's the issue of whose rights should be used when delivering + * to "|command" or to /file/name. With alias databases, the rights are + * those of who owns the alias, i.e. the database owner. With aliases + * owned by root, a default user is used instead. When an alias with + * default rights references an include file owned by an ordinary user, + * we must use the rights of the include file owner, otherwise the + * include file owner could take control of the default account. + * + * Secondly, there's the question of who to notify of delivery problems. + * With aliases that have an owner- alias, the latter is used to set the + * sender and owner attributes. Otherwise, the owner attribute is reset + * (the alias is globally visible and could be sent to by anyone). + */ + for (cpp = maps->argv->argv; *cpp; cpp++) { + if ((alias_result = dict_lookup(*cpp, state.msg_attr.local)) != 0) { + if (msg_verbose) + msg_info("%s: %s: %s = %s", myname, *cpp, + state.msg_attr.local, alias_result); + + /* + * DELIVERY POLICY + * + * 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 + * command and file code will use default rights when the alias + * database is owned by root, otherwise it will use the rights of + * the alias database owner. + */ + if ((alias_uid = dict_owner(*cpp)) == 0) { + alias_pwd = 0; + RESET_USER_ATTR(usr_attr, state.level); + } else { + if ((alias_pwd = mypwuid(alias_uid)) == 0) { + msg_warn("cannot find alias database owner for %s", *cpp); + *statusp = defer_append(BOUNCE_FLAG_KEEP, + BOUNCE_ATTR(state.msg_attr), + "cannot find alias database owner"); + return (YES); + } + SET_USER_ATTR(usr_attr, alias_pwd, state.level); + } + + /* + * WHERE TO REPORT DELIVERY PROBLEMS. + * + * Use the owner- alias if one is specified, otherwise reset the + * owner attribute and use the include file ownership if we can. + * Save the dict_lookup() result before something clobbers it. + */ +#define STR(x) vstring_str(x) + + expansion = mystrdup(alias_result); + owner = concatenate("owner-", state.msg_attr.local, (char *) 0); + if (maps_find(maps, owner)) { + canon_owner = canon_addr_internal(vstring_alloc(10), owner); + SET_OWNER_ATTR(state.msg_attr, STR(canon_owner), state.level); + } else { + canon_owner = 0; + RESET_OWNER_ATTR(state.msg_attr, state.level); + } + + /* + * 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; + + /* + * Deliver. + */ + *statusp = + (dict_errno ? + defer_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "alias database unavailable") : + deliver_token_string(state, usr_attr, expansion, (int *) 0)); + myfree(expansion); + myfree(owner); + if (canon_owner) + vstring_free(canon_owner); + if (alias_pwd) + mypwfree(alias_pwd); + return (YES); + } + + /* + * If the alias database was inaccessible for some reason, defer + * further delivery for the current top-level recipient. + */ + if (dict_errno != 0) { + *statusp = defer_append(BOUNCE_FLAG_KEEP, + BOUNCE_ATTR(state.msg_attr), + "alias database unavailable"); + return (YES); + } else { + if (msg_verbose) + msg_info("%s: %s: %s not found", myname, *cpp, + state.msg_attr.local); + } + } + + /* + * If no alias was found for a required reserved name, toss the message + * into the bit bucket, and issue a warning instead. + */ +#define STREQ(x,y) (strcasecmp(x,y) == 0) + + if (STREQ(state.msg_attr.local, MAIL_ADDR_MAIL_DAEMON) + || STREQ(state.msg_attr.local, MAIL_ADDR_POSTMASTER)) { + msg_warn("required alias not found: %s", state.msg_attr.local); + *statusp = sent(SENT_ATTR(state.msg_attr), "discarded"); + return (YES); + } + + /* + * Try delivery to a local user instead. + */ + return (NO); +} diff --git a/postfix/local/command.c b/postfix/local/command.c new file mode 100644 index 000000000..2ef58fe53 --- /dev/null +++ b/postfix/local/command.c @@ -0,0 +1,183 @@ +/*++ +/* NAME +/* command 3 +/* SUMMARY +/* message delivery to shell command +/* SYNOPSIS +/* #include "local.h" +/* +/* int deliver_command(state, usr_attr, command) +/* LOCAL_STATE state; +/* USER_ATTR exp_attr; +/* char *command; +/* DESCRIPTION +/* deliver_command() runs a command with a message as standard +/* input. A limited amount of standard output and standard error +/* output is captured for diagnostics purposes. +/* Duplicate commands for the same recipient are suppressed. +/* +/* Arguments: +/* .IP state +/* The attributes that specify the message, recipient and more. +/* Attributes describing the alias, include or forward expansion. +/* A table with the results from expanding aliases or lists. +/* .IP usr_attr +/* Attributes describing user rights and environment. +/* .IP command +/* The shell command to be executed. If possible, the command +/* is executed without actually invoking a shell. +/* DIAGNOSTICS +/* deliver_command() returns non-zero when delivery should be +/* tried again, +/* SEE ALSO +/* mailbox(3) deliver to mailbox +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "local.h" + +/* deliver_command - deliver to shell command */ + +int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, char *command) +{ + char *myname = "deliver_command"; + VSTRING *why; + int cmd_status; + int deliver_status; + ARGV *env; + int copy_flags; + + /* + * DUPLICATE ELIMINATION + * + * Skip this command if it was already delivered to as this user. + */ + if (been_here(state.dup_filter, "command %d %s", usr_attr.uid, command)) + return (0); + + /* + * DELIVERY POLICY + * + * Do we permit mail to shell commands? Allow delivery via mailbox_command. + */ + if (command != var_mailbox_command + && (local_cmd_deliver_mask & state.msg_attr.exp_type) == 0) + return (bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "mail to command is restricted")); + + /* + * DELIVERY RIGHTS + * + * Choose a default uid and gid when none have been selected (i.e. values + * are still zero). + */ + if (usr_attr.uid == 0 && (usr_attr.uid = var_default_uid) == 0) + msg_panic("privileged default user id"); + if (usr_attr.gid == 0 && (usr_attr.gid = var_default_gid) == 0) + msg_panic("privileged default group id"); + + /* + * Deliver. + */ + copy_flags = MAIL_COPY_FROM; + if ((state.msg_attr.features & FEATURE_NODELIVERED) == 0) + copy_flags |= MAIL_COPY_DELIVERED; + + why = vstring_alloc(1); + if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0) + msg_fatal("%s: seek queue file %s: %m", + myname, VSTREAM_PATH(state.msg_attr.fp)); + + /* + * Pass additional environment information. XXX This should be + * configurable. However, passing untrusted information via environment + * parameters opens up a whole can of worms. Lesson from web servers: + * don't let any network data even near a shell. It causes trouble. + */ + env = argv_alloc(1); + if (usr_attr.home) + argv_add(env, "HOME", usr_attr.home, ARGV_END); + if (usr_attr.logname) + argv_add(env, "LOGNAME", usr_attr.logname, ARGV_END); + if (usr_attr.shell) + argv_add(env, "SHELL", usr_attr.shell, ARGV_END); + argv_terminate(env); + + cmd_status = pipe_command(state.msg_attr.fp, why, + PIPE_CMD_UID, usr_attr.uid, + PIPE_CMD_GID, usr_attr.gid, + PIPE_CMD_COMMAND, command, + PIPE_CMD_COPY_FLAGS, copy_flags, + PIPE_CMD_SENDER, state.msg_attr.sender, + PIPE_CMD_DELIVERED, state.msg_attr.delivered, + PIPE_CMD_TIME_LIMIT, var_command_maxtime, + PIPE_CMD_ENV, env->argv, + PIPE_CMD_SHELL, var_local_cmd_shell, + PIPE_CMD_END); + + argv_free(env); + + /* + * Depending on the result, bounce or defer the message. + */ + switch (cmd_status) { + case PIPE_STAT_OK: + deliver_status = sent(SENT_ATTR(state.msg_attr), "\"|%s\"", command); + break; + case PIPE_STAT_BOUNCE: + deliver_status = bounce_append(BOUNCE_FLAG_KEEP, + BOUNCE_ATTR(state.msg_attr), + "%s", vstring_str(why)); + break; + case PIPE_STAT_DEFER: + deliver_status = defer_append(BOUNCE_FLAG_KEEP, + BOUNCE_ATTR(state.msg_attr), + "%s", vstring_str(why)); + break; + default: + msg_panic("%s: bad status %d", myname, cmd_status); + /* NOTREACHED */ + } + + /* + * Cleanup. + */ + vstring_free(why); + + return (deliver_status); +} diff --git a/postfix/local/deliver_attr.c b/postfix/local/deliver_attr.c new file mode 100644 index 000000000..7b62509df --- /dev/null +++ b/postfix/local/deliver_attr.c @@ -0,0 +1,85 @@ +/*++ +/* NAME +/* deliver_attr 3 +/* SUMMARY +/* initialize message delivery attributes +/* SYNOPSIS +/* #include "local.h" +/* +/* void deliver_attr_init(attrp) +/* DELIVER_ATTR *attrp; +/* +/* void deliver_attr_dump(attrp) +/* DELIVER_ATTR *attrp; +/* DESCRIPTION +/* deliver_attr_init() initializes a structure with message delivery +/* attributes to a known initial state (all zeros). +/* +/* deliver_attr_dump() logs the contents of the given attribute list. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include + +/* Application-specific. */ + +#include "local.h" + +/* deliver_attr_init - set message delivery attributes to all-zero state */ + +void deliver_attr_init(DELIVER_ATTR *attrp) +{ + attrp->level = 0; + attrp->fp = 0; + attrp->queue_name = 0; + attrp->queue_id = 0; + attrp->offset = 0; + attrp->sender = 0; + attrp->recipient = 0; + attrp->local = 0; + attrp->extension = 0; + attrp->owner = 0; + attrp->delivered = 0; + attrp->relay = 0; + attrp->exp_type = 0; + attrp->exp_from = 0; + attrp->features = 0; +} + +/* deliver_attr_dump - log message delivery attributes */ + +void deliver_attr_dump(DELIVER_ATTR *attrp) +{ + msg_info("level: %d", attrp->level); + msg_info("path: %s", VSTREAM_PATH(attrp->fp)); + msg_info("fp: 0x%lx", (long) attrp->fp); + msg_info("queue_name: %s", attrp->queue_name ? attrp->queue_name : "null"); + msg_info("queue_id: %s", attrp->queue_id ? attrp->queue_id : "null"); + msg_info("offset: %ld", attrp->offset); + msg_info("sender: %s", attrp->sender ? attrp->sender : "null"); + msg_info("recipient: %s", attrp->recipient ? attrp->recipient : "null"); + msg_info("local: %s", attrp->local ? attrp->local : "null"); + msg_info("extension: %s", attrp->extension ? attrp->extension : "null"); + msg_info("owner: %s", attrp->owner ? attrp->owner : "null"); + msg_info("delivered: %s", attrp->delivered ? attrp->delivered : "null"); + msg_info("relay: %s", attrp->relay ? attrp->relay : "null"); + msg_info("exp_type: %d", attrp->exp_type); + msg_info("exp_from: %s", attrp->exp_from ? attrp->exp_from : "null"); + msg_info("features: %d", attrp->features); +} diff --git a/postfix/local/delivered.c b/postfix/local/delivered.c new file mode 100644 index 000000000..50faeae64 --- /dev/null +++ b/postfix/local/delivered.c @@ -0,0 +1,144 @@ +/*++ +/* NAME +/* delivered 3 +/* SUMMARY +/* process Delivered-To: headers +/* SYNOPSIS +/* #include "local.h" +/* +/* HTABLE *delivered_init(attr) +/* DELIVER_ATTR attr; +/* +/* int delivered_find(table, address) +/* HTABLE *table; +/* char *address; +/* +/* void delivered_free(table) +/* HTABLE *table; +/* DESCRIPTION +/* This module processes addresses in Delivered-To: headers. +/* These headers are added by some mail delivery systems, for the +/* purpose of breaking mail forwarding loops. N.B. This solves +/* a different problem than the Received: hop count limit. Hop +/* counts are used to limit the impact of mail routing problems. +/* +/* delivered_init() extracts Delivered-To: header addresses +/* from the specified message, and returns a table with the +/* result. +/* +/* delivered_find() looks up the address in the lookup table, +/* and returns non-zero when the address was found. The +/* address argument must be in internalized form. +/* +/* delivered_free() releases storage that was allocated by +/* delivered_init(). +/* +/* Arguments: +/* .IP state +/* The attributes that specify the message, recipient and more. +/* .IP table +/* A table with extracted Delivered-To: addresses. +/* .IP address +/* A recipient address, internal form. +/* DIAGNOSTICS +/* Fatal errors: out of memory. +/* SEE ALSO +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "local.h" + +static VSTRING *buf; + +#define STR vstring_str + +/* delivered_init - extract delivered-to information from the message */ + +HTABLE *delivered_init(DELIVER_ATTR attr) +{ + char *cp; + HTABLE *table = htable_create(0); + HEADER_OPTS *hdr; + + if (buf == 0) + buf = vstring_alloc(10); + + if (vstream_fseek(attr.fp, attr.offset, SEEK_SET) < 0) + msg_fatal("seek queue file %s: %m", VSTREAM_PATH(attr.fp)); + + /* + * XXX Assume that normal mail systems produce headers that fit in a + * REC_TYPE_NORM record. Lowercase the delivered-to addresses for + * consistency. + */ + while (rec_get(attr.fp, buf, 0) == REC_TYPE_NORM) { + if (is_header(STR(buf))) { + if ((hdr = header_opts_find(STR(buf))) != 0 + && hdr->type == HDR_DELIVERED_TO) { + cp = STR(buf) + strlen(hdr->name) + 1; + while (ISSPACE(*cp)) + cp++; + lowercase(cp); + if (msg_verbose) + msg_info("delivered_init: %s", cp); + htable_enter(table, cp, (char *) 0); + } + } else if (ISSPACE(STR(buf)[0])) { + continue; + } else { + break; + } + } + return (table); +} + +/* delivered_find - look up recipient in delivered table */ + +int delivered_find(HTABLE *table, char *address) +{ + HTABLE_INFO *ht; + + /* + * mail_copy() uses quote_822_local() when writing the Delivered-To: + * header. We must therefore apply the same transformation when looking + * up the recipient. Lowercase the delivered-to address for consistency. + */ + quote_822_local(buf, address); + lowercase(STR(buf)); + ht = htable_locate(table, STR(buf)); + return (ht != 0); +} diff --git a/postfix/local/dotforward.c b/postfix/local/dotforward.c new file mode 100644 index 000000000..5e4cffaec --- /dev/null +++ b/postfix/local/dotforward.c @@ -0,0 +1,233 @@ +/*++ +/* NAME +/* dotforward 3 +/* SUMMARY +/* $HOME/.forward file expansion +/* SYNOPSIS +/* #include "local.h" +/* +/* int deliver_dotforward(state, usr_attr, statusp) +/* LOCAL_STATE state; +/* USER_ATTR usr_attr; +/* int *statusp; +/* DESCRIPTION +/* deliver_dotforward() delivers a message to the destinations +/* listed in a recipient's $HOME/.forward file. The result is +/* zero when no acceptable $HOME/.forward file was found, or when +/* a recipient is listed in her own .forward file. +/* +/* When mail is sent to an extended address (e.g., user+foo), +/* the address extension is appended to the .forward file name +/* (e.g., .forward+foo). When that file does not exist, .forward +/* is used instead. +/* +/* 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. +/* .IP statusp +/* Message delivery status. See below. +/* DIAGNOSTICS +/* Fatal errors: out of memory. Warnings: bad $HOME/.forward +/* file type, permissions or ownership. The message delivery +/* status is non-zero when delivery should be tried again. +/* SEE ALSO +/* include(3) include file processor. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include +#include +#ifdef USE_PATHS_H +#include +#endif +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +#include "local.h" + +#define NO 0 +#define YES 1 + +/* deliver_dotforward - expand contents of .forward file */ + +int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) +{ + char *myname = "deliver_dotforward"; + struct stat st; + VSTRING *path; + struct mypasswd *mypwd; + int fd; + VSTREAM *fp; + int status; + int forward_found = NO; + int lookup_status; + int addr_count; + + /* + * Make verbose logging easier to understand. + */ + state.level++; + if (msg_verbose) + msg_info("%s[%d]: %s", myname, state.level, state.msg_attr.local); + + /* + * DUPLICATE/LOOP ELIMINATION + * + * If this user includes (an alias of) herself in her own .forward file, + * deliver to the user instead. + */ + if (been_here(state.dup_filter, "forward %s", state.msg_attr.local)) + return (NO); + state.msg_attr.exp_from = state.msg_attr.local; + + /* + * Skip non-existing users. The mailbox delivery routine will catch the + * error. + */ + if ((mypwd = mypwnam(state.msg_attr.local)) == 0) + return (NO); + + /* + * From here on no early returns or we have a memory leak. + */ + + /* + * 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 RIGHTS + * + * Do not inherit rights from the .forward file owner. Instead, use the + * recipient's rights, and insist that the .forward file is owned by the + * recipient. This is a small but significant difference. Use the + * recipient's rights for all /file and |command deliveries, and pass on + * these rights to command/file destinations in included files. When + * these are the rights of root, the /file and |command delivery routines + * will use unprivileged default rights instead. Better safe than sorry. + */ + if (mypwd->pw_uid != 0) + SET_USER_ATTR(usr_attr, mypwd, state.level); + + /* + * DELIVERY POLICY + * + * Update the expansion type attribute so that we can decide if deliveries + * to |command and /file/name are allowed at all. + */ + state.msg_attr.exp_type = EXPAND_TYPE_FWD; + + /* + * WHERE TO REPORT DELIVERY PROBLEMS + * + * Set the owner attribute so that 1) include files won't set the sender to + * be this user and 2) mail forwarded to other local users will be + * resubmitted as a new queue file. + */ + 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, + * 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 + * should always be local to the host running the delivery process. + * Anything else is just asking for trouble when a server goes down + * (either the mailbox server or the home directory server). + * + * With mail to user+foo, try ~/.forward+foo before ~/.forward. Ignore foo + * when it contains '/' or when forward+foo does not exist. + */ +#define STR(x) vstring_str(x) + + status = 0; + path = vstring_alloc(100); + if (state.msg_attr.extension && strchr(state.msg_attr.extension, '/')) { + msg_warn("%s: address with illegal extension: %s", + state.msg_attr.queue_id, state.msg_attr.recipient); + state.msg_attr.extension = 0; + } + if (state.msg_attr.extension != 0) { + vstring_sprintf(path, "%s/.forward%c%s", mypwd->pw_dir, + var_rcpt_delim[0], state.msg_attr.extension); + if ((lookup_status = lstat_as(STR(path), &st, + usr_attr.uid, usr_attr.gid)) < 0) + state.msg_attr.extension = 0; + } + if (state.msg_attr.extension == 0) { + vstring_sprintf(path, "%s/.forward", mypwd->pw_dir); + lookup_status = lstat_as(STR(path), &st, usr_attr.uid, usr_attr.gid); + } + if (lookup_status >= 0) { + if (S_ISREG(st.st_mode) == 0) { + msg_warn("file %s is not a regular file", STR(path)); + } else if (st.st_uid != 0 && st.st_uid != usr_attr.uid) { + msg_warn("file %s has bad owner uid %d", STR(path), st.st_uid); + } else if (st.st_mode & 002) { + msg_warn("file %s is world writable", STR(path)); + } else if ((fd = open_as(STR(path), O_RDONLY, 0, usr_attr.uid, usr_attr.gid)) < 0) { + msg_warn("cannot open file %s: %m", STR(path)); + } else { + close_on_exec(fd, CLOSE_ON_EXEC); + addr_count = 0; + fp = vstream_fdopen(fd, O_RDONLY); + status = deliver_token_stream(state, usr_attr, fp, &addr_count); + if (vstream_fclose(fp)) + msg_warn("close file %s: %m", STR(path)); + if (addr_count > 0) + forward_found = YES; + } + } + + /* + * Clean up. + */ + vstring_free(path); + mypwfree(mypwd); + + *statusp = status; + return (forward_found); +} diff --git a/postfix/local/feature.c b/postfix/local/feature.c new file mode 100644 index 000000000..1a1db60f4 --- /dev/null +++ b/postfix/local/feature.c @@ -0,0 +1,91 @@ +/*++ +/* NAME +/* feature 3 +/* SUMMARY +/* toggle features depending on address +/* SYNOPSIS +/* #include "local.h" +/* +/* int feature_control(state) +/* LOCAL_STATE state; +/* DESCRIPTION +/* feature_control() breaks the localpart of the recipient +/* address up into fields, according to the recipient feature +/* delimiter, and turns on/off the features as encountered. +/* +/* Arguments: +/* .IP state +/* The attributes that specify the message, recipient and more. +/* Attributes describing the alias, include or forward expansion. +/* A table with the results from expanding aliases or lists. +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include + +/* Application-specific. */ + +#include "local.h" + +struct feature_map { + char *name; + int mask; +}; + +static struct feature_map feature_map[] = { + "nodelivered", FEATURE_NODELIVERED, + 0, +}; + +/* feature_control - extract delivery options from recipient localpart */ + +int feature_control(const char *localpart) +{ + struct feature_map *mp; + char *saved_localpart; + char *ptr; + int mask = 0; + char *cp; + + if (*var_rcpt_fdelim) { + ptr = saved_localpart = mystrdup(localpart); + while ((cp = mystrtok(&ptr, var_rcpt_fdelim)) != 0) { + for (mp = feature_map; mp->name; mp++) + if (strcasecmp(mp->name, cp) == 0) { + if (msg_verbose) + msg_info("feature: %s", mp->name); + mask |= mp->mask; + break; + } + } + myfree(saved_localpart); + } + if (msg_verbose) + msg_info("features: 0x%x", mask); + return (mask); +} diff --git a/postfix/local/file.c b/postfix/local/file.c new file mode 100644 index 000000000..d9ff9592b --- /dev/null +++ b/postfix/local/file.c @@ -0,0 +1,175 @@ +/*++ +/* NAME +/* file 3 +/* SUMMARY +/* mail delivery to arbitrary file +/* SYNOPSIS +/* #include "local.h" +/* +/* int deliver_file(state, usr_attr, path) +/* LOCAL_STATE state; +/* USER_ATTR usr_attr; +/* char *path; +/* DESCRIPTION +/* deliver_file() appends a message to a file, UNIX mailbox format, +/* or qmail maildir format, +/* with duplicate suppression. It will deliver only to non-executable +/* regular files. +/* +/* Arguments: +/* .IP state +/* The attributes that specify the message, recipient and more. +/* Attributes describing alias, include or forward expansion. +/* A table with the results from expanding aliases or lists. +/* .IP usr_attr +/* Attributes describing user rights and environment information. +/* .IP path +/* The file to deliver to. If the name ends in '/', delivery is done +/* in qmail maildir format, otherwise delivery is done in UNIX mailbox +/* format. +/* DIAGNOSTICS +/* deliver_file() returns non-zero when delivery should be tried again. +/* SEE ALSO +/* defer(3) +/* bounce(3) +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "local.h" + +#define STR vstring_str + +/* deliver_file - deliver to file */ + +int deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path) +{ + struct stat st; + int fd; + VSTREAM *dst; + VSTRING *why; + int status; + int copy_flags; + + /* + * DUPLICATE ELIMINATION + * + * Skip this file if it was already delivered to as this user. + */ + if (been_here(state.dup_filter, "file %d %s", usr_attr.uid, path)) + return (0); + + /* + * DELIVERY POLICY + * + * Do we allow delivery to files? + */ + if ((local_file_deliver_mask & state.msg_attr.exp_type) == 0) + return (bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "mail to file is restricted")); + + /* + * DELIVERY RIGHTS + * + * Use a default uid/gid when none are given. + */ + if (usr_attr.uid == 0 && (usr_attr.uid = var_default_uid) == 0) + msg_panic("privileged default user id"); + if (usr_attr.gid == 0 && (usr_attr.gid = var_default_gid) == 0) + msg_panic("privileged default group id"); + + /* + * If the name ends in /, use maildir-style delivery instead. + */ + if (path[strlen(path) - 1] == '/') + return (deliver_maildir(state, usr_attr, path)); + + /* + * Deliver. From here on, no early returns or we have a memory leak. + */ + if (msg_verbose) + msg_info("deliver_file (%d,%d): %s", usr_attr.uid, usr_attr.gid, path); + if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0) + msg_fatal("seek queue file %s: %m", state.msg_attr.queue_id); + why = vstring_alloc(100); + + /* + * Open or create the file, lock it, and append the message. Open the + * file as the specified user. XXX Since we cannot set a lockfile before + * creating the destination, there is a small chance that we truncate an + * existing file. + */ + copy_flags = MAIL_COPY_MBOX; + if (state.msg_attr.features & FEATURE_NODELIVERED) + copy_flags &= ~MAIL_COPY_DELIVERED; + +#define FOPEN_AS(p,u,g) ( \ + (fd = open_as(p,O_APPEND|O_CREAT|O_WRONLY,0600,u,g)) >= 0 ? \ + vstream_fdopen(fd, O_WRONLY) : 0) + + if ((dst = FOPEN_AS(path, usr_attr.uid, usr_attr.gid)) == 0) { + status = bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "cannot open destination file %s: %m", path); + } else if (fstat(vstream_fileno(dst), &st) < 0) { + vstream_fclose(dst); + status = defer_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "cannot fstat file %s: %m", path); + } else if (S_ISREG(st.st_mode) && deliver_flock(vstream_fileno(dst), why) < 0) { + vstream_fclose(dst); + status = defer_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "cannot lock destination file %s: %s", + path, STR(why)); + } else if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + vstream_fclose(dst); + status = bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "executable destination file %s", path); + } else if (mail_copy(COPY_ATTR(state.msg_attr), dst, S_ISREG(st.st_mode) ? + copy_flags : (copy_flags & ~MAIL_COPY_TOFILE), why)) { + status = defer_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "cannot append destination file %s: %s", + path, STR(why)); + } else { + status = sent(SENT_ATTR(state.msg_attr), "%s", path); + } + + /* + * Clean up. + */ + vstring_free(why); + return (status); +} diff --git a/postfix/local/forward.c b/postfix/local/forward.c new file mode 100644 index 000000000..2c9060ad0 --- /dev/null +++ b/postfix/local/forward.c @@ -0,0 +1,298 @@ +/*++ +/* NAME +/* forward 3 +/* SUMMARY +/* message forwarding +/* SYNOPSIS +/* #include "local.h" +/* +/* int forward_init() +/* +/* int forward_append(attr) +/* DELIVER_ATTR attr; +/* +/* int forward_finish(attr, cancel) +/* DELIVER_ATTR attr; +/* int cancel; +/* DESCRIPTION +/* This module implements the client interface for message +/* forwarding. +/* +/* forward_init() initializes internal data structures. +/* +/* forward_append() appends a recipient to the list of recipients +/* that will receive a message with the specified message sender +/* and delivered-to addresses. +/* +/* forward_finish() forwards the actual message contents and +/* releases the memory allocated by forward_init() and by +/* forward_append(). When the \fIcancel\fR argument is true, no +/* messages will be forwarded. The \fIattr\fR argument specifies +/* the original message delivery attributes as they were before +/* alias or forward expansions. +/* DIAGNOSTICS +/* A non-zero result means that the requested operation should +/* be tried again. +/* Warnings: problems connecting to the forwarding service, +/* corrupt message file. A corrupt message is saved to the +/* "corrupt" queue for further inspection. +/* Fatal: out of memory. +/* Panic: missing forward_init() or forward_finish() call. +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "local.h" + + /* + * Use one cleanup service connection for each (delivered to, sender) pair. + */ +static HTABLE *forward_dt; + +typedef struct FORWARD_INFO { + VSTREAM *cleanup; /* clean up service handle */ + char *queue_id; /* forwarded message queue id */ + time_t posting_time; /* posting time */ +} FORWARD_INFO; + +/* forward_init - prepare for forwarding */ + +int forward_init(void) +{ + + /* + * Sanity checks. + */ + if (forward_dt != 0) + msg_panic("forward_init: missing forward_finish call"); + + forward_dt = htable_create(0); + return (0); +} + +/* forward_open - open connection to cleanup service */ + +static FORWARD_INFO *forward_open(char *sender) +{ + VSTRING *buffer = vstring_alloc(100); + FORWARD_INFO *info; + VSTREAM *cleanup; + + /* + * Contact the cleanup service and save the new mail queue id. Request + * that the cleanup service bounces bad messages to the sender so that we + * can avoid the trouble of bounce management. + */ + cleanup = mail_connect(MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP, BLOCKING); + if (cleanup == 0) + return (0); + close_on_exec(vstream_fileno(cleanup), CLOSE_ON_EXEC); + if (mail_scan(cleanup, "%s", buffer) != 1) { + vstream_fclose(cleanup); + return (0); + } + info = (FORWARD_INFO *) mymalloc(sizeof(FORWARD_INFO)); + info->cleanup = cleanup; + info->queue_id = mystrdup(vstring_str(buffer)); + info->posting_time = time((time_t *) 0); + mail_print(cleanup, "%d", CLEANUP_FLAG_BOUNCE); + + /* + * Send initial message envelope information. For bounces, set the + * designated sender: mailing list owner, posting user, whatever. + */ + rec_fprintf(cleanup, REC_TYPE_TIME, "%ld", (long) info->posting_time); + rec_fputs(cleanup, REC_TYPE_FROM, sender); + + vstring_free(buffer); + return (info); +} + +/* forward_append - append recipient to message envelope */ + +int forward_append(DELIVER_ATTR attr) +{ + FORWARD_INFO *info; + HTABLE *table_snd; + + /* + * Sanity checks. + */ + if (msg_verbose) + msg_info("forward delivered=%s sender=%s recip=%s", + attr.delivered, attr.sender, attr.recipient); + if (forward_dt == 0) + msg_panic("forward_append: missing forward_init call"); + + /* + * In order to find the recipient list, first index a table by + * delivered-to header address, then by envelope sender address. + */ + if ((table_snd = (HTABLE *) htable_find(forward_dt, attr.delivered)) == 0) { + table_snd = htable_create(0); + htable_enter(forward_dt, attr.delivered, (char *) table_snd); + } + if ((info = (FORWARD_INFO *) htable_find(table_snd, attr.sender)) == 0) { + if ((info = forward_open(attr.sender)) == 0) + return (-1); + htable_enter(table_snd, attr.sender, (char *) info); + } + + /* + * Append the recipient to the message envelope. + */ + rec_fputs(info->cleanup, REC_TYPE_RCPT, attr.recipient); + + return (vstream_ferror(info->cleanup)); +} + +/* forward_send - send forwarded message */ + +static int forward_send(FORWARD_INFO *info, DELIVER_ATTR attr, char *delivered) +{ + char *myname = "forward_send"; + VSTRING *buffer = vstring_alloc(100); + int status; + int rec_type = 0; + + /* + * Start the message content segment. Prepend our Delivered-To: header to + * the message data. Stop at the first error. XXX Rely on the front-end + * services to enforce record size limits. + */ + rec_fputs(info->cleanup, REC_TYPE_MESG, ""); + vstring_strcpy(buffer, delivered); + rec_fprintf(info->cleanup, REC_TYPE_NORM, "Received: by %s (%s)", + var_myhostname, var_mail_name); + rec_fprintf(info->cleanup, REC_TYPE_NORM, "\tid %s; %s", + info->queue_id, mail_date(info->posting_time)); + rec_fprintf(info->cleanup, REC_TYPE_NORM, "Delivered-To: %s", + lowercase(vstring_str(buffer))); + if ((status = vstream_ferror(info->cleanup)) == 0) + if (vstream_fseek(attr.fp, attr.offset, SEEK_SET) < 0) + msg_fatal("%s: seek queue file %s: %m:", + myname, VSTREAM_PATH(attr.fp)); + while (status == 0 && (rec_type = rec_get(attr.fp, buffer, 0)) > 0) { + if (rec_type != REC_TYPE_CONT && rec_type != REC_TYPE_NORM) + break; + status = (REC_PUT_BUF(info->cleanup, rec_type, buffer) != rec_type); + } + if (status == 0 && rec_type != REC_TYPE_XTRA) + status |= mark_corrupt(attr.fp); + + /* + * Send the end-of-data marker only when there were no errors. + */ + if (status == 0) { + rec_fputs(info->cleanup, REC_TYPE_XTRA, ""); + rec_fputs(info->cleanup, REC_TYPE_END, ""); + } + + /* + * Retrieve the cleanup service completion status only if there are no + * problems. + */ + if (status == 0) + if (vstream_fflush(info->cleanup) + || mail_scan(info->cleanup, "%d", &status) != 1) + status = 1; + + /* + * Log successful forwarding. + */ + if (status == 0) + sent(SENT_ATTR(attr), "forwarded as %s", info->queue_id); + + /* + * Cleanup. + */ + vstring_free(buffer); + return (status); +} + +/* forward_finish - complete message forwarding requests and clean up */ + +int forward_finish(DELIVER_ATTR attr, int cancel) +{ + HTABLE_INFO **dt_list; + HTABLE_INFO **dt; + HTABLE_INFO **sn_list; + HTABLE_INFO **sn; + HTABLE *table_snd; + char *delivered; + char *sender; + FORWARD_INFO *info; + int status = cancel; + + /* + * Sanity checks. + */ + if (forward_dt == 0) + msg_panic("forward_finish: missing forward_init call"); + + /* + * Walk over all delivered-to header addresses and over each envelope + * sender address. + */ + for (dt = dt_list = htable_list(forward_dt); *dt; dt++) { + delivered = dt[0]->key; + table_snd = (HTABLE *) dt[0]->value; + for (sn = sn_list = htable_list(table_snd); *sn; sn++) { + sender = sn[0]->key; + info = (FORWARD_INFO *) sn[0]->value; + if (status == 0) + status |= forward_send(info, attr, delivered); + if (msg_verbose) + msg_info("forward_finish: delivered %s sender %s status %d", + delivered, sender, status); + (void) vstream_fclose(info->cleanup); + myfree(info->queue_id); + myfree((char *) info); + } + myfree((char *) sn_list); + htable_free(table_snd, (void (*) (char *)) 0); + } + myfree((char *) dt_list); + htable_free(forward_dt, (void (*) (char *)) 0); + forward_dt = 0; + return (status); +} diff --git a/postfix/local/include.c b/postfix/local/include.c new file mode 100644 index 000000000..d9f0786bc --- /dev/null +++ b/postfix/local/include.c @@ -0,0 +1,193 @@ +/*++ +/* NAME +/* deliver_include 3 +/* SUMMARY +/* deliver to addresses listed in include file +/* SYNOPSIS +/* #include "local.h" +/* +/* int deliver_include(state, usr_attr, path) +/* LOCAL_STATE state; +/* USER_ATTR usr_attr; +/* char *path; +/* DESCRIPTION +/* deliver_include() processes the contents of the named include +/* file and delivers to each address listed. Some sanity checks +/* are done on the include file permissions and type. +/* +/* Arguments: +/* .IP state +/* The attributes that specify the message, recipient and more. +/* 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. +/* .IP path +/* Pathname of the include file. +/* DIAGNOSTICS +/* Fatal errors: out of memory. Warnings: bad include file type +/* or permissions. The result is non-zero when delivery should be +/* tried again. +/* SEE ALSO +/* token(3) tokenize list +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +#include "local.h" + +/* deliver_include - open include file and deliver */ + +int deliver_include(LOCAL_STATE state, USER_ATTR usr_attr, char *path) +{ + char *myname = "deliver_include"; + struct stat st; + struct mypasswd *file_pwd = 0; + int status; + VSTREAM *fp; + int fd; + + /* + * Make verbose logging easier to understand. + */ + state.level++; + if (msg_verbose) + msg_info("%s[%d]: %s", myname, state.level, path); + + /* + * DUPLICATE ELIMINATION + * + * Don't process this include file more than once as this particular user. + */ + if (been_here(state.dup_filter, "include %d %s", usr_attr.uid, path)) + return (0); + state.msg_attr.exp_from = state.msg_attr.local; + + /* + * Can of worms. Allow this include file to be symlinked, but disallow + * inclusion of special files or of files with world write permission + * enabled. + */ + if (*path != '/') + return (bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + ":include:%s uses a relative path", path)); + if (stat_as(path, &st, usr_attr.uid, usr_attr.gid) < 0) + return (bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "unable to lookup file %s: %m", path)); + if (S_ISREG(st.st_mode) == 0) + return (bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "not a regular include file: %s", path)); + if (st.st_mode & S_IWOTH) + return (bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "world writable include file: %s", path)); + + /* + * DELIVERY POLICY + * + * Set the expansion type attribute so that we can decide if destinations + * such as /file/name and |command are allowed at all. + */ + state.msg_attr.exp_type = EXPAND_TYPE_INCL; + + /* + * DELIVERY RIGHTS + * + * When a non-root include file is listed in a root-owned alias, use the + * rights of the include file owner. We do not want to give the include + * file owner control of the default account. + * + * When an include file is listed in a user-owned alias or .forward file, + * leave the delivery rights alone. Users should not be able to make + * things happen with someone else's rights just by including some file + * that is owned by their victim. + */ + if (usr_attr.uid == 0) { + if ((file_pwd = mypwuid(st.st_uid)) == 0) { + msg_warn("cannot find username for uid %d", st.st_uid); + return (defer_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "%s: cannot find :include: file owner", path)); + } + if (file_pwd->pw_uid != 0) + SET_USER_ATTR(usr_attr, file_pwd, state.level); + } + + /* + * MESSAGE FORWARDING + * + * When no owner attribute is set (either via an owner- alias, or as part of + * .forward file processing), set the owner attribute, to disable direct + * delivery of local recipients. By now it is clear that the owner + * attribute should have been called forwarder instead. + */ + if (state.msg_attr.owner == 0) + state.msg_attr.owner = state.msg_attr.recipient; + + /* + * From here on no early returns or we have a memory leak. + * + * FILE OPEN RIGHTS + * + * Use the delivery rights to open the include file. When no delivery rights + * were established sofar, the file containing the :include: is owned by + * root, so it should be OK to open any file that is accessible to root. + * The command and file delivery routines are responsible for setting the + * proper delivery rights. These are the rights of the default user, in + * case the :include: is in a root-owned alias. + */ +#define FOPEN_AS(p,u,g) ((fd = open_as(p,O_RDONLY,0,u,g)) >= 0 ? \ + vstream_fdopen(fd,O_RDONLY) : 0) + + if ((fp = FOPEN_AS(path, usr_attr.uid, usr_attr.gid)) == 0) { + status = bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "cannot open include file %s: %m", path); + } else { + close_on_exec(vstream_fileno(fp), CLOSE_ON_EXEC); + status = deliver_token_stream(state, usr_attr, fp, (int *) 0); + if (vstream_fclose(fp)) + msg_warn("close %s: %m", path); + } + + /* + * Cleanup. + */ + if (file_pwd) + mypwfree(file_pwd); + + return (status); +} diff --git a/postfix/local/indirect.c b/postfix/local/indirect.c new file mode 100644 index 000000000..88e63eb84 --- /dev/null +++ b/postfix/local/indirect.c @@ -0,0 +1,81 @@ +/*++ +/* NAME +/* indirect 3 +/* SUMMARY +/* indirect delivery +/* SYNOPSIS +/* #include "local.h" +/* +/* void deliver_indirect(state) +/* LOCAL_STATE state; +/* char *recipient; +/* DESCRIPTION +/* deliver_indirect() delivers a message via the message +/* forwarding service, with duplicate filtering up to a +/* configurable number of recipients. +/* +/* Arguments: +/* .IP state +/* The attributes that specify the message, sender and more. +/* A table with the results from expanding aliases or lists. +/* CONFIGURATION VARIABLES +/* duplicate_filter_limit, duplicate filter size limit +/* DIAGNOSTICS +/* The result is non-zero when the operation 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 +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +#include "local.h" + +/* deliver_indirect - deliver mail via forwarding service */ + +int deliver_indirect(LOCAL_STATE state) +{ + + /* + * Suppress duplicate expansion results. Add some sugar to the name to + * avoid collisions with other duplicate filters. Allow the user to + * specify an upper bound on the size of the duplicate filter, so that we + * can handle huge mailing lists with millions of recipients. + */ + if (msg_verbose) + msg_info("deliver_indirect: %s", state.msg_attr.recipient); + if (been_here(state.dup_filter, "indirect %s", state.msg_attr.recipient)) + return (0); + + /* + * Send the address to the forwarding service. Inherit the delivered + * attribute from the alias or from the .forward file owner. + */ + if (forward_append(state.msg_attr)) + return (defer_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "unable to forward message")); + return (0); +} diff --git a/postfix/local/local.c b/postfix/local/local.c new file mode 100644 index 000000000..8f0d9f6c1 --- /dev/null +++ b/postfix/local/local.c @@ -0,0 +1,467 @@ +/*++ +/* NAME +/* local 8 +/* SUMMARY +/* Postfix local mail delivery +/* SYNOPSIS +/* \fBlocal\fR [generic Postfix daemon options] +/* DESCRIPTION +/* The \fBlocal\fR daemon processes delivery requests from the +/* Postfix queue manager to deliver mail to local recipients. +/* Each delivery request specifies a queue file, a sender address, +/* a domain or host to deliver to, and one or more recipients. +/* This program expects to be run from the \fBmaster\fR(8) process +/* manager. +/* +/* The \fBlocal\fR daemon updates queue files and marks recipients +/* as finished, or it informs the queue manager that delivery should +/* be tried again at a later time. Delivery problem reports are sent +/* to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate. +/* SYSTEM-WIDE AND USER-LEVEL ALIASING +/* .ad +/* .fi +/* The system adminstrator can set up one or more system-wide +/* \fBsendmail\fR-style alias databases. +/* Users can have \fBsendmail\fR-style ~/.\fBforward\fR files. +/* Mail for \fIname\fR is delivered to the alias \fIname\fR, to +/* destinations in ~\fIname\fR/.\fBforward\fR, to the mailbox owned +/* by the user \fIname\fR, or it is sent back as undeliverable. +/* +/* An alias or ~/.\fBforward\fR file may list any combination of external +/* commands, destination file names, \fB:include:\fR directives, or +/* mail addresses. +/* See \fBaliases\fR(5) for a precise description. Each line in a +/* user's .\fBforward\fR file has the same syntax as the right-hand part +/* of an alias. +/* +/* When an address is found in its own alias expansion, delivery is +/* made to the user instead. When a user is listed in the user's own +/* ~/.\fBforward\fR file, delivery is made to the user's mailbox instead. +/* An empty ~/.\fBforward\fR file means do not forward mail. +/* +/* In order to prevent the mail system from using up unreasonable +/* amounts of memory, input records read from \fB:include:\fR or from +/* ~/.\fBforward\fR files are broken up into chunks of length +/* \fBline_length_limit\fR. +/* +/* While expanding aliases, ~/.\fBforward\fR files, and so on, the +/* program attempts to avoid duplicate deliveries. The +/* \fBduplicate_filter_limit\fR configuration parameter limits the +/* number of remembered recipients. +/* MAIL FORWARDING +/* .ad +/* .fi +/* For the sake of reliability, forwarded mail is re-submitted as +/* a new message, so that each recipient has a separate on-file +/* delivery status record. +/* +/* In order to stop mail forwarding loops early, the software adds a +/* \fBDelivered-To:\fR header with the envelope recipient address. If +/* mail arrives for a recipient that is already listed in a +/* \fBDelivered-To:\fR header, the message is bounced. +/* 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. +/* Mailbox delivery can be delegated to an external command specified +/* with the \fBmailbox_command\fR configuration parameter. +/* +/* 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 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. +/* EXTERNAL COMMAND DELIVERY +/* .ad +/* .fi +/* The \fBallow_mail_to_commands\fR configuration parameter restricts +/* delivery to external commands. The default setting (\fBalias, +/* forward\fR) forbids command destinations in \fB:include:\fR files. +/* +/* The command is executed directly where possible. Assistance by the +/* shell (\fB/bin/sh\fR 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-delivery status reports. +/* A command is forcibly terminated if it does not complete within +/* \fBcommand_time_limit\fR seconds. Command exit status codes are +/* expected to follow the conventions defined in <\fBsysexits.h\fR>. +/* +/* When mail is delivered on behalf of a user, the \fBHOME\fR, +/* \fBLOGNAME\fR, and \fBSHELL\fR environment variables are set +/* accordingly. +/* The \fBPATH\fR environment variable is always reset to a +/* system-dependent default path, and the \fBTZ\fR (time zone) +/* environment variable is always passed on without change. +/* +/* 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. +/* EXTERNAL FILE DELIVERY +/* .ad +/* .fi +/* The \fBallow_mail_to_files\fR configuration parameter restricts +/* delivery to external files. The default setting (\fBalias, +/* forward\fR) forbids file destinations in \fB:include:\fR files. +/* +/* 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, prepends a \fB>\fR +/* character to lines beginning with "\fBFrom \fR", and appends an +/* empty line. +/* When the destination is a regular file, it is locked for exclusive +/* access while delivery is in progress. In case of problems, an attempt +/* is made to truncate a regular file to its original length. +/* ADDRESS EXTENSION +/* .ad +/* .fi +/* The optional \fBrecipient_delimiter\fR configuration parameter +/* specifies how to separate address extensions from local recipient +/* names. +/* +/* For example, with "\fBrecipient_delimiter = +\fR", mail for +/* \fIname\fR+\fIfoo\fR is delivered to the alias \fIname\fR+\fIfoo\fR +/* or to the alias \fIname\fR, to the destinations listed in +/* ~\fIname\fR/.\fBforward\fR+\fIfoo\fR or in ~\fIname\fR/.\fBforward\fR, +/* to the mailbox owned by the user \fIname\fR, or it is sent back as +/* undeliverable. +/* +/* In all cases the \fBlocal\fR daemon prepends a +/* `\fBDelivered-To:\fR \fIname\fR+\fIfoo\fR' header line. +/* DELIVERY RIGHTS +/* .ad +/* .fi +/* Deliveries to external files and external commands are made with +/* the rights of the receiving user on whose behalf the delivery is made. +/* In the absence of a user context, the \fBlocal\fR daemon uses the +/* owner rights of the \fB:include:\fR file or alias database. +/* When those files are owned by the superuser, delivery is made with +/* the rights specified with the \fBdefault_privs\fR configuration +/* parameter. +/* STANDARDS +/* RFC 822 (ARPA Internet Text Messages) +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8). +/* Corrupted message files are marked so that the queue +/* manager can move them to the \fBcorrupt\fR queue afterwards. +/* +/* Depending on the setting of the \fBnotify_classes\fR parameter, +/* the postmaster is notified of bounces and of other trouble. +/* BUGS +/* For security reasons, the message delivery status of external commands +/* or of external files is never checkpointed to file. As a result, +/* the program may occasionally deliver more than once to a command or +/* external file. Better safe than sorry. +/* +/* Mutually-recursive aliases or ~/.\fBforward\fR files are not detected +/* early. The resulting mail forwarding loop is broken by the use of the +/* \fBDelivered-To:\fR message header. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. Use the \fBpostfix reload\fR command after +/* a configuration change. +/* .SH Miscellaneous +/* .ad +/* .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 \fBmaildir\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 \fBrecipient_delimiter\fR +/* Separator between username and address extension. +/* .SH "Locking controls" +/* .ad +/* .fi +/* .IP \fBdeliver_lock_attempts\fR +/* Limit the number of attempts to acquire an exclusive lock +/* on a mailbox or external file. +/* .IP \fBdeliver_lock_delay\fR +/* Time in seconds between successive attempts to acquire +/* an exclusive lock. +/* .IP \fBstale_lock_time\fR +/* Limit the time after which a stale lock is removed. +/* .SH "Resource controls" +/* .ad +/* .fi +/* .IP \fBcommand_time_limit\fR +/* Limit the amount of time for delivery to external command. +/* .IP \fBduplicate_filter_limit\fR +/* Limit the size of the duplicate filter for results from +/* alias etc. expansion. +/* .IP \fBline_length_limit\fR +/* Limit the amount of memory used for processing a partial +/* input line. +/* .IP \fBlocal_destination_concurrency_limit\fR +/* Limit the number of parallel deliveries to the same user. +/* The default limit is taken from the +/* \fBdefault_destination_concurrency_limit\fR parameter. +/* .IP \fBlocal_destination_recipient_limit\fR +/* Limit the number of recipients per message delivery. +/* The default limit is taken from the +/* \fBdefault_destination_recipient_limit\fR parameter. +/* .SH "Security controls" +/* .ad +/* .fi +/* .IP \fBallow_mail_to_commands\fR +/* Restrict the usage of mail delivery to external command. +/* .IP \fBallow_mail_to_files\fR +/* Restrict the usage of mail delivery to external file. +/* .IP \fBdefault_privs\fR +/* Default rights for delivery to external file or command. +/* HISTORY +/* .ad +/* .fi +/* The \fBDelivered-To:\fR header appears in the \fBqmail\fR system +/* by Daniel Bernstein. +/* +/* The \fImaildir\fR structure appears in the \fBqmail\fR 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 +/* .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 + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Single server skeleton. */ + +#include + +/* Application-specific. */ + +#include "local.h" + + /* + * Tunable parameters. + */ +char *var_allow_commands; +char *var_allow_files; +char *var_alias_maps; +int var_dup_filter_limit; +int var_command_maxtime; +char *var_home_mailbox; +char *var_mailbox_command; +char *var_rcpt_fdelim; +char *var_local_cmd_shell; + +int local_cmd_deliver_mask; +int local_file_deliver_mask; + +/* local_deliver - deliver message with extreme prejudice */ + +static int local_deliver(DELIVER_REQUEST *rqst, char *service) +{ + char *myname = "local_deliver"; + RECIPIENT *rcpt_end = rqst->rcpt_list.info + rqst->rcpt_list.len; + RECIPIENT *rcpt; + int rcpt_stat; + int msg_stat; + LOCAL_STATE state; + USER_ATTR usr_attr; + + if (msg_verbose) + msg_info("local_deliver: %s from %s", rqst->queue_id, rqst->sender); + + /* + * Initialize the delivery attributes that are not recipient specific. + * While messages are being delivered and while aliases or forward files + * are being expanded, this attribute list is being changed constantly. + * For this reason, the list is passed on by value (except when it is + * being initialized :-), so that there is no need to undo attribute + * changes made by lower-level routines. The alias/include/forward + * expansion attribute list is part of a tree with self and parent + * references (see the EXPAND_ATTR definitions). The user-specific + * attributes are security sensitive, and are therefore kept separate. + * All this results in a noticeable level of clumsiness, but passing + * things around by value gives good protection against accidental change + * by subroutines. + */ + state.level = 0; + deliver_attr_init(&state.msg_attr); + state.msg_attr.queue_name = rqst->queue_name; + state.msg_attr.queue_id = rqst->queue_id; + state.msg_attr.fp = + mail_queue_open(rqst->queue_name, rqst->queue_id, O_RDWR, 0); + if (state.msg_attr.fp == 0) + msg_fatal("open file %s %s: %m", rqst->queue_name, rqst->queue_id); + close_on_exec(vstream_fileno(state.msg_attr.fp), CLOSE_ON_EXEC); + state.msg_attr.offset = rqst->data_offset; + state.msg_attr.sender = rqst->sender; + state.msg_attr.relay = service; + state.msg_attr.arrival_time = rqst->arrival_time; + 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 */ + + /* + * Iterate over each recipient named in the delivery request. When the + * mail delivery status for a given recipient is definite (i.e. bounced + * or delivered), update the message queue file and cross off the + * recipient. Update the per-message delivery status. + */ + for (msg_stat = 0, rcpt = rqst->rcpt_list.info; rcpt < rcpt_end; rcpt++) { + state.dup_filter = been_here_init(var_dup_filter_limit, BH_FLAG_FOLD); + forward_init(); + state.msg_attr.recipient = rcpt->address; + rcpt_stat = deliver_recipient(state, usr_attr); + rcpt_stat |= forward_finish(state.msg_attr, rcpt_stat); + if (rcpt_stat == 0) + deliver_completed(state.msg_attr.fp, rcpt->offset); + been_here_free(state.dup_filter); + msg_stat |= rcpt_stat; + } + + /* + * Clean up. + */ + delivered_free(state.loop_info); + vstream_fclose(state.msg_attr.fp); + + return (msg_stat); +} + +/* local_service - perform service for client */ + +static void local_service(VSTREAM *stream, char *service, char **argv) +{ + DELIVER_REQUEST *request; + int status; + + /* + * Sanity check. This service takes no command-line arguments. + */ + if (argv[0]) + msg_fatal("unexpected command-line argument: %s", argv[0]); + + /* + * This routine runs whenever a client connects to the UNIX-domain socket + * that is dedicated to local mail delivery service. What we see below is + * a little protocol to (1) tell the client that we are ready, (2) read a + * delivery request from the client, and (3) report the completion status + * of that request. + */ + if ((request = deliver_request_read(stream)) != 0) { + status = local_deliver(request, service); + deliver_request_done(stream, request, status); + } +} + +/* local_mask_init - initialize delivery restrictions */ + +static void local_mask_init(void) +{ + static NAME_MASK file_mask[] = { + "alias", EXPAND_TYPE_ALIAS, + "forward", EXPAND_TYPE_FWD, + "include", EXPAND_TYPE_INCL, + 0, + }; + static NAME_MASK command_mask[] = { + "alias", EXPAND_TYPE_ALIAS, + "forward", EXPAND_TYPE_FWD, + "include", EXPAND_TYPE_INCL, + 0, + }; + + local_file_deliver_mask = name_mask(file_mask, var_allow_files); + local_cmd_deliver_mask = name_mask(command_mask, var_allow_commands); +} + +/* post_init - post-jail initialization */ + +static void post_init(void) +{ + + /* + * Drop privileges most of the time, and set up delivery restrictions. + */ + set_eugid(var_owner_uid, var_owner_gid); + local_mask_init(); +} + +/* main - pass control to the single-threaded skeleton */ + +int main(int argc, char **argv) +{ + static CONFIG_INT_TABLE int_table[] = { + VAR_COMMAND_MAXTIME, DEF_COMMAND_MAXTIME, &var_command_maxtime, 1, 0, + VAR_DUP_FILTER_LIMIT, DEF_DUP_FILTER_LIMIT, &var_dup_filter_limit, 0, 0, + 0, + }; + static CONFIG_STR_TABLE str_table[] = { + VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps, 0, 0, + VAR_HOME_MAILBOX, DEF_HOME_MAILBOX, &var_home_mailbox, 0, 0, + VAR_MAILBOX_COMMAND, DEF_MAILBOX_COMMAND, &var_mailbox_command, 0, 0, + VAR_ALLOW_COMMANDS, DEF_ALLOW_COMMANDS, &var_allow_commands, 0, 0, + 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, + 0, + }; + + single_server_main(argc, argv, local_service, + MAIL_SERVER_INT_TABLE, int_table, + MAIL_SERVER_STR_TABLE, str_table, + MAIL_SERVER_POST_INIT, post_init, + 0); +} diff --git a/postfix/local/local.h b/postfix/local/local.h new file mode 100644 index 000000000..a20b27852 --- /dev/null +++ b/postfix/local/local.h @@ -0,0 +1,182 @@ +/*++ +/* NAME +/* local 3h +/* SUMMARY +/* local mail delivery +/* SYNOPSIS +/* #include "local.h" +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include +#include + + /* + * Global library. + */ +#include +#include + + /* + * User attributes: these control the privileges for delivery to external + * commands, external files, or mailboxes, and the initial environment of + * external commands. + */ +typedef struct USER_ATTR { + uid_t uid; /* file/command access */ + gid_t gid; /* file/command access */ + char *home; /* null or home directory */ + char *logname; /* null or login name */ + char *shell; /* null or login shell */ +} USER_ATTR; + + /* + * Critical macros. Not for obscurity, but to ensure consistency. + */ +#define RESET_USER_ATTR(usr_attr, level) { \ + usr_attr.uid = 0; usr_attr.gid = 0; usr_attr.home = 0; \ + usr_attr.logname = 0; usr_attr.shell = 0; \ + if (msg_verbose) \ + msg_info("%s[%d]: reset user_attr", myname, level); \ + } + +#define SET_USER_ATTR(usr_attr, pwd, level) { \ + usr_attr.uid = pwd->pw_uid; usr_attr.gid = pwd->pw_gid; \ + usr_attr.home = pwd->pw_dir; usr_attr.logname = pwd->pw_name; \ + usr_attr.shell = pwd->pw_shell; \ + if (msg_verbose) \ + msg_info("%s[%d]: set user_attr: %s", \ + myname, level, pwd->pw_name); \ + } + + /* + * The delivery attributes are inherited from files, from aliases, and from + * whatnot. Some of the information is changed on the fly. DELIVER_ATTR + * structres are therefore passed by value, so there is no need to undo + * changes. + */ +typedef struct DELIVER_ATTR { + int level; /* recursion level */ + VSTREAM *fp; /* open queue file */ + char *queue_name; /* mail queue id */ + char *queue_id; /* mail queue id */ + long offset; /* data offset */ + char *sender; /* taken from envelope */ + char *recipient; /* taken from resolver */ + char *local; /* recipient localpart, base name */ + char *extension; /* recipient localpart, extension */ + char *owner; /* null or list owner */ + char *delivered; /* for loop detection */ + char *relay; /* relay host */ + long arrival_time; /* arrival time */ + int exp_type; /* expansion type. see below */ + char *exp_from; /* expanded_from */ + int features; /* see below */ +} DELIVER_ATTR; + +extern void deliver_attr_init(DELIVER_ATTR *); +extern void deliver_attr_dump(DELIVER_ATTR *); + +#define EXPAND_TYPE_ALIAS (1<<0) +#define EXPAND_TYPE_FWD (1<<1) +#define EXPAND_TYPE_INCL (1<<2) + +#define FEATURE_NODELIVERED (1<<0) /* no delivered-to */ + + /* + * Rather than schlepping around dozens of arguments, here is one that has + * all. Well, almost. The user attributes are just a bit too sensitive, so + * they are passed around separately. + */ +typedef struct LOCAL_STATE { + int level; /* nesting level, for logging */ + DELIVER_ATTR msg_attr; /* message attributes */ + BH_TABLE *dup_filter; /* internal duplicate filter */ + HTABLE *loop_info; /* external loop filter */ +} LOCAL_STATE; + +#define RESET_OWNER_ATTR(msg_attr, level) { \ + msg_attr.owner = 0; \ + if (msg_verbose) \ + msg_info("%s[%d]: reset owner attr", myname, level); \ + } + +#define SET_OWNER_ATTR(msg_attr, who, level) { \ + msg_attr.sender = msg_attr.owner = who; \ + if (msg_verbose) \ + msg_info("%s[%d]: set owner attr: %s", \ + myname, level, who); \ + } + + /* + * Bundle up some often-user attributes. + */ +#define BOUNCE_ATTR(attr) attr.queue_id, attr.recipient, attr.relay, \ + attr.arrival_time +#define SENT_ATTR(attr) attr.queue_id, attr.recipient, attr.relay, \ + attr.arrival_time +#define OPENED_ATTR(attr) attr.queue_id, attr.sender +#define COPY_ATTR(attr) attr.sender, attr.delivered, attr.fp + + /* + * "inner" nodes of the delivery graph. + */ +extern int deliver_recipient(LOCAL_STATE, USER_ATTR); +extern int deliver_alias(LOCAL_STATE, USER_ATTR, int *); +extern int deliver_dotforward(LOCAL_STATE, USER_ATTR, int *); +extern int deliver_include(LOCAL_STATE, USER_ATTR, char *); +extern int deliver_token(LOCAL_STATE, USER_ATTR, TOK822 *); +extern int deliver_token_string(LOCAL_STATE, USER_ATTR, char *, int *); +extern int deliver_token_stream(LOCAL_STATE, USER_ATTR, VSTREAM *, int *); +extern int deliver_resolve_tree(LOCAL_STATE, USER_ATTR, TOK822 *); +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_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 *); + + /* + * Restrictions on delivery to sensitive destinations. + */ +extern int local_file_deliver_mask; +extern int local_cmd_deliver_mask; + + /* + * delivered.c + */ +extern HTABLE *delivered_init(DELIVER_ATTR); +extern int delivered_find(HTABLE *, char *); + +#define delivered_free(t) htable_free((t), (void (*) (char *)) 0) + + /* + * forward.c + */ +extern int forward_init(void); +extern int forward_append(DELIVER_ATTR); +extern int forward_finish(DELIVER_ATTR, int); + + /* + * feature.c + */ +extern int feature_control(const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ diff --git a/postfix/local/mailbox.c b/postfix/local/mailbox.c new file mode 100644 index 000000000..6781c51fa --- /dev/null +++ b/postfix/local/mailbox.c @@ -0,0 +1,218 @@ +/*++ +/* NAME +/* mailbox 3 +/* SUMMARY +/* mailbox delivery +/* SYNOPSIS +/* #include "local.h" +/* +/* int deliver_mailbox(state, usr_attr) +/* LOCAL_STATE state; +/* USER_ATTR usr_attr; +/* DESCRIPTION +/* deliver_mailbox() delivers to mailbox, with duplicate +/* suppression. The default is direct mailbox delivery to +/* /var/[spool/]mail/\fIuser\fR; when a \fIhome_mailbox\fR +/* has been configured, mail is delivered to ~/$\fIhome_mailbox\fR; +/* and when a \fImailbox_command\fR has been configured, the message +/* is piped into the command instead. +/* +/* Arguments: +/* .IP state +/* The attributes that specify the message, recipient and more. +/* Attributes describing alias, include or forward expansion. +/* A table with the results from expanding aliases or lists. +/* .IP usr_attr +/* Attributes describing user rights and environment. +/* DIAGNOSTICS +/* deliver_mailbox() returns 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 +#include +#include +#include +#include +#ifdef USE_PATHS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#ifdef USE_DOT_LOCK +#include +#endif +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "local.h" + +/* deliver_mailbox_file - deliver to recipient mailbox */ + +static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr) +{ + char *mailbox; + VSTRING *why; + VSTREAM *dst; + int status; + int copy_flags; + + if (msg_verbose) + msg_info("deliver_mailbox_file: %s", state.msg_attr.recipient); + + /* + * Initialize. Assume the operation will fail. Set the delivered + * attribute to reflect the final recipient. + */ + if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0) + msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp)); + state.msg_attr.delivered = state.msg_attr.recipient; + status = -1; + why = vstring_alloc(100); + if (*var_home_mailbox) + mailbox = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0); + else + mailbox = concatenate(_PATH_MAILDIR, "/", state.msg_attr.local, (char *) 0); + + /* + * 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); +#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); + set_eugid(usr_attr.uid, usr_attr.gid); + if (dst != 0) { + if (deliver_flock(vstream_fileno(dst), why) < 0) + vstream_fclose(dst); + else if (mail_copy(COPY_ATTR(state.msg_attr), dst, + copy_flags, why) == 0) + status = 0; + } +#ifdef USE_DOT_LOCK + set_eugid(0, 0); + dot_unlockfile(mailbox); + } +#endif + set_eugid(var_owner_uid, var_owner_gid); + + if (status) + defer_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "cannot append to file %s: %s", mailbox, vstring_str(why)); + else + sent(SENT_ATTR(state.msg_attr), "mailbox"); + myfree(mailbox); + vstring_free(why); + return (status); +} + +/* deliver_mailbox - deliver to recipient mailbox */ + +int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr) +{ + char *myname = "deliver_mailbox"; + int status; + struct mypasswd *mbox_pwd; + char *path; + + /* + * Make verbose logging easier to understand. + */ + 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++; + + /* + * DUPLICATE ELIMINATION + * + * Don't deliver more than once to this mailbox. + */ + if (been_here(state.dup_filter, "mailbox %s", state.msg_attr.local)) + return (0); + + /* + * Bounce the message when this recipient does not exist. XXX Should + * quote_822_local() the recipient. + */ + 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)); + + /* + * No early returns or we have a memory leak. + */ + + /* + * DELIVERY RIGHTS + * + * Use the rights of the recipient user. + */ + SET_USER_ATTR(usr_attr, mbox_pwd, state.level); + + /* + * Deliver to mailbox or to external delivery agent. + */ +#define LAST_CHAR(s) (s[strlen(s) - 1]) + + if (*var_mailbox_command) + status = deliver_command(state, usr_attr, var_mailbox_command); + else if (*var_home_mailbox && LAST_CHAR(var_home_mailbox) == '/') { + path = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0); + status = deliver_maildir(state, usr_attr, path); + myfree(path); + } else + status = deliver_mailbox_file(state, usr_attr); + + /* + * Cleanup. + */ + mypwfree(mbox_pwd); + return (status); +} diff --git a/postfix/local/maildir.c b/postfix/local/maildir.c new file mode 100644 index 000000000..a67ce0c09 --- /dev/null +++ b/postfix/local/maildir.c @@ -0,0 +1,148 @@ +/*++ +/* NAME +/* maildir 3 +/* SUMMARY +/* delivery to maildir +/* SYNOPSIS +/* #include "local.h" +/* +/* int deliver_maildir(state, usr_attr, path) +/* LOCAL_STATE state; +/* USER_ATTR usr_attr; +/* char *path; +/* DESCRIPTION +/* deliver_maildir() delivers a message to a qmail maildir. +/* +/* Arguments: +/* .IP state +/* The attributes that specify the message, recipient and more. +/* Attributes describing alias, include or forward expansion. +/* A table with the results from expanding aliases or lists. +/* .IP usr_attr +/* Attributes describing user rights and environment information. +/* .IP path +/* The maildir to deliver to, including trailing slash. +/* DIAGNOSTICS +/* deliver_maildir() always succeeds or it bounces the message. +/* SEE ALSO +/* bounce(3) +/* 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 +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +#include "local.h" + +/* deliver_maildir - delivery to maildir-style mailbox */ + +int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path) +{ + char *newdir; + char *tmpdir; + char *tmpfile; + char *newfile; + VSTRING *why; + VSTRING *buf; + VSTREAM *dst; + int status; + int copy_flags; + static int count; + + if (msg_verbose) + msg_info("deliver_maildir: %s %s", state.msg_attr.recipient, path); + + /* + * Initialize. Assume the operation will fail. Set the delivered + * attribute to reflect the final recipient. + */ + if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0) + msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp)); + state.msg_attr.delivered = state.msg_attr.recipient; + status = -1; + buf = vstring_alloc(100); + why = vstring_alloc(100); + + copy_flags = MAIL_COPY_TOFILE; + if ((state.msg_attr.features & FEATURE_NODELIVERED) == 0) + copy_flags |= MAIL_COPY_DELIVERED; + + newdir = concatenate(path, "new/", (char *) 0); + tmpdir = concatenate(path, "tmp/", (char *) 0); + + /* + * Create and write the file as the recipient, so that file quota work. + * Create any missing directories on the fly. + */ +#define STR vstring_str + + set_eugid(usr_attr.uid, usr_attr.gid); + vstring_sprintf(buf, "%ld.%d.%s.%d", (long) time((time_t *) 0), + var_pid, get_hostname(), count++); + tmpfile = concatenate(tmpdir, STR(buf), (char *) 0); + newfile = concatenate(newdir, STR(buf), (char *) 0); + if ((dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0 + && (errno != ENOENT + || make_dirs(tmpdir, 0700) < 0 + || (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) { + vstring_sprintf(why, "create %s: %m", tmpfile); + } else { + if (mail_copy(COPY_ATTR(state.msg_attr), dst, copy_flags, why) == 0) { + if (link(tmpfile, newfile) < 0 + && (errno != ENOENT + || make_dirs(newdir, 0700) < 0 + || link(tmpfile, newfile) < 0)) { + vstring_sprintf(why, "link to %s: %m", newfile); + } else { + if (unlink(tmpfile) < 0) + msg_warn("remove %s: %m", tmpfile); + status = 0; + } + } + } + set_eugid(var_owner_uid, var_owner_gid); + + if (status) + bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "maildir delivery failed: %s", vstring_str(why)); + else + sent(SENT_ATTR(state.msg_attr), "maildir"); + vstring_free(buf); + vstring_free(why); + myfree(newdir); + myfree(tmpdir); + myfree(tmpfile); + myfree(newfile); + return (0); +} diff --git a/postfix/local/recipient.c b/postfix/local/recipient.c new file mode 100644 index 000000000..fd68fc1a3 --- /dev/null +++ b/postfix/local/recipient.c @@ -0,0 +1,190 @@ +/*++ +/* NAME +/* recipient 3 +/* SUMMARY +/* deliver to one local recipient +/* SYNOPSIS +/* #include "local.h" +/* +/* int deliver_recipient(state, usr_attr) +/* LOCAL_STATE state; +/* USER_ATTR *usr_attr; +/* DESCRIPTION +/* deliver_recipient() delivers a message to a local recipient. +/* It is called initially when the queue manager requests +/* delivery to a local recipient, and is called recursively +/* when an alias or forward file expands to a local recipient. +/* +/* When called recursively with, for example, a result from alias +/* or forward file expansion, aliases are expanded immediately, +/* but mail for non-alias destinations is submitted as a new +/* message, so that each recipient has a dedicated queue file +/* message delivery status record (in a shared queue file). +/* +/* When the \fIrecipient_delimiter\fR configuration parameter +/* is set, it is used to separate cookies off recipient names. +/* A common setting is to have "recipient_delimiter = +" +/* so that mail for \fIuser+foo\fR is delivered to \fIuser\fR, +/* with a "Delivered-To: user+foo@domain" header line. +/* +/* Arguments: +/* .IP state +/* The attributes that specify the message, sender, and more. +/* 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 +/* deliver_recipient() returns non-zero when delivery should be +/* tried again. +/* BUGS +/* Mutually-recursive aliases or $HOME/.forward files aren't +/* detected when they could be. The resulting mail forwarding loop +/* is broken by the use of the Delivered-To: message header. +/* SEE ALSO +/* alias(3) delivery to aliases +/* mailbox(3) delivery to mailbox +/* dotforward(3) delivery to destinations in .forward file +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +#include "local.h" + +/* deliver_switch - branch on recipient type */ + +static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr) +{ + int status; + + /* + * \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)); + + /* + * Otherwise, alias expansion has highest precedence. + */ + if (deliver_alias(state, usr_attr, &status)) + return (status); + + /* + * Don't apply the recipient delimiter to reserved addresses. After + * stripping the recipient extension, try aliases again. + */ + if (*var_rcpt_delim) + state.msg_attr.extension = + split_addr(state.msg_attr.local, *var_rcpt_delim); + if (state.msg_attr.extension && deliver_alias(state, usr_attr, &status)) + return (status); + + /* + * Special case for mail locally forwarded or aliased to a different + * local address. Resubmit the message via the cleanup service, so that + * each recipient gets a separate delivery queue file status record in + * the new queue file. The downside of this approach is that mutually + * recursive .forward files cause a mail forwarding loop. Fortunately, + * the loop can be broken by the use of the Delivered-To: message header. + * + * The code below must not trigger on mail sent to an alias that has no + * owner- companion, so that mail for an alias first.last->username is + * delivered directly, instead of going through username->first.last + * canonical mappings in the cleanup service. The downside of this + * approach is that recipients in the expansion of an alias without + * owner- won't have separate delivery queue file status records, because + * for them, the message won't be resubmitted as a new queue file. + */ + if (state.msg_attr.owner != 0 + && strcasecmp(state.msg_attr.owner, state.msg_attr.recipient) != 0) + return (deliver_indirect(state)); + + /* + * Delivery to local user. First try expansion of the recipient's + * $HOME/.forward file. Do mailbox delivery as a last resort. + */ + if (deliver_dotforward(state, usr_attr, &status) == 0) + status = deliver_mailbox(state, usr_attr); + return (status); +} + +/* deliver_recipient - deliver one local recipient */ + +int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr) +{ + int rcpt_stat; + + if (msg_verbose) + msg_info("deliver_recipient: %s", state.msg_attr.recipient); + + /* + * With each level of recursion, detect and break external message + * forwarding loops. + */ + if (delivered_find(state.loop_info, state.msg_attr.recipient)) + return (bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), + "mail forwarding loop for %s", state.msg_attr.recipient)); + + /* + * Set up the recipient-specific attributes. If this is forwarded mail, + * leave the delivered attribute alone, so that the forwarded message + * will show the correct forwarding recipient. + */ + if (state.msg_attr.delivered == 0) + state.msg_attr.delivered = state.msg_attr.recipient; + state.msg_attr.local = mystrdup(state.msg_attr.recipient); + if (split_at_right(state.msg_attr.local, '@') == 0) + 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); + + /* + * Run the recipient through the delivery switch. + */ + if (msg_verbose) + deliver_attr_dump(&state.msg_attr); + rcpt_stat = deliver_switch(state, usr_attr); + + /* + * Clean up. + */ + myfree(state.msg_attr.local); + + return (rcpt_stat); +} diff --git a/postfix/local/resolve.c b/postfix/local/resolve.c new file mode 100644 index 000000000..d76824904 --- /dev/null +++ b/postfix/local/resolve.c @@ -0,0 +1,143 @@ +/*++ +/* NAME +/* resolve 3 +/* SUMMARY +/* resolve recipient and deliver locally or remotely +/* SYNOPSIS +/* #include "local.h" +/* +/* int deliver_resolve_tree(state, usr_attr, addr) +/* LOCAL_STATE state; +/* USER_ATTR usr_attr; +/* TOK822 *addr; +/* +/* int deliver_resolve_addr(state, usr_attr, addr) +/* LOCAL_STATE state; +/* USER_ATTR usr_attr; +/* char *addr; +/* DESCRIPTION +/* deliver_resolve_XXX() resolves a recipient that is the result from +/* e.g., alias expansion, and delivers locally or via forwarding. +/* +/* Arguments: +/* .IP state +/* The attributes that specify the message, sender and more. +/* A table with the results from expanding aliases or lists. +/* A table with delivered-to: addresses taken from the message. +/* .IP addr +/* An address from, e.g., alias expansion. +/* DIAGNOSTICS +/* Fatal errors: out of memory. The result is non-zero when the +/* operation should be tried again. Warnings: malformed address. +/* SEE ALSO +/* recipient(3) local delivery +/* indirect(3) deliver via forwarding +/* 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 + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "local.h" + +#define STR vstring_str + +/* deliver_resolve_addr - resolve and deliver */ + +int deliver_resolve_addr(LOCAL_STATE state, USER_ATTR usr_attr, char *addr) +{ + TOK822 *tree; + int result; + + tree = tok822_scan_addr(addr); + result = deliver_resolve_tree(state, usr_attr, tree); + tok822_free_tree(tree); + return (result); +} + +/* deliver_resolve_tree - resolve and deliver */ + +int deliver_resolve_tree(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr) +{ + RESOLVE_REPLY reply; + int status; + int ext_len; + char *ratsign; + + /* + * Initialize. + */ + resolve_clnt_init(&reply); + + /* + * Rewrite the address to canonical form, just like the cleanup service + * does. Then, resolve the address to (transport, nexhop, recipient), + * just like the queue manager does. The only part missing here is the + * virtual address substitution. Message forwarding fixes most of that. + */ + tok822_rewrite(addr, REWRITE_CANON); + tok822_resolve(addr, &reply); + state.msg_attr.recipient = STR(reply.recipient); + + /* + * Splice in the optional unmatched address extension. + */ + if (state.msg_attr.extension) { + if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0) { + VSTRING_ADDCH(reply.recipient, *var_rcpt_delim); + vstring_strcat(reply.recipient, state.msg_attr.extension); + } else { + ext_len = strlen(state.msg_attr.extension); + VSTRING_SPACE(reply.recipient, ext_len + 2); + memmove(ratsign + ext_len + 1, ratsign, strlen(ratsign) + 1); + *ratsign = *var_rcpt_delim; + memcpy(ratsign + 1, state.msg_attr.extension, ext_len); + VSTRING_SKIP(reply.recipient); + } + } + + /* + * Delivery to a local or non-local address. For a while there was some + * ugly code to force local recursive alias expansions on a host with no + * authority over the local domain, but that code was just too unclean. + */ + if (VSTRING_LEN(reply.nexthop) == 0) { + status = deliver_recipient(state, usr_attr); + } else { + status = deliver_indirect(state); + } + + /* + * Cleanup. + */ + resolve_clnt_free(&reply); + + return (status); +} diff --git a/postfix/local/token.c b/postfix/local/token.c new file mode 100644 index 000000000..b3722f504 --- /dev/null +++ b/postfix/local/token.c @@ -0,0 +1,177 @@ +/*++ +/* NAME +/* token 3 +/* SUMMARY +/* tokenize alias/include/.forward entries and deliver +/* SYNOPSIS +/* #include "local.h" +/* +/* int deliver_token(state, usr_attr, addr) +/* LOCAL_STATE state; +/* USER_ATTR usr_attr; +/* TOK822 *addr; +/* +/* int deliver_token_string(state, usr_attr, string, addr_count) +/* LOCAL_STATE state; +/* USER_ATTR usr_attr; +/* char *string; +/* int *addr_count; +/* +/* int deliver_token_stream(state, usr_attr, fp, addr_count) +/* LOCAL_STATE state; +/* USER_ATTR usr_attr; +/* VSTREAM *fp; +/* int *addr_count; +/* DESCRIPTION +/* This module delivers to addresses listed in an alias database +/* entry, in an include file, or in a .forward file. +/* +/* deliver_token() delivers to the address in the given token. +/* +/* deliver_token_string() delivers to all addresses listed in +/* the specified string. +/* +/* deliver_token_stream() delivers to all addresses listed in +/* the specified stream. Input records > \fIline_length_limit\fR +/* are broken up into multiple records, to prevent the mail +/* system from using unreasonable amounts of memory. +/* +/* Arguments: +/* .IP state +/* The attributes that specify the message, recipient and more. +/* 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. +/* .IP addr +/* A parsed address from an include file, alias file or .forward file. +/* .IP string +/* A null-terminated string. +/* .IP fp +/* A readable stream. +/* .IP addr_count +/* Null pointer, or the address of a counter that is incremented +/* by the number of destinations found by the tokenizer. +/* DIAGNOSTICS +/* Fatal errors: out of memory. The result is non-zero when the +/* operation should be tried again. Warnings: malformed address. +/* SEE ALSO +/* list_token(3) tokenize list +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* Application-specific. */ + +#include "local.h" + +/* deliver_token - deliver to expansion of include file or alias */ + +int deliver_token(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr) +{ + VSTRING *addr_buf = vstring_alloc(100); + static char include[] = ":include:"; + int status; + char *path; + +#define STR vstring_str + + tok822_internalize(addr_buf, addr->head, TOK822_STR_DEFL); + if (msg_verbose) + msg_info("deliver_token: %s", STR(addr_buf)); + + if (*STR(addr_buf) == '/') { + status = deliver_file(state, usr_attr, STR(addr_buf)); + } else if (*STR(addr_buf) == '|') { + status = deliver_command(state, usr_attr, STR(addr_buf) + 1); + } else if (strncasecmp(STR(addr_buf), include, sizeof(include) - 1) == 0) { + path = STR(addr_buf) + sizeof(include) - 1; + status = deliver_include(state, usr_attr, path); + } else { + status = deliver_resolve_tree(state, usr_attr, addr); + } + vstring_free(addr_buf); + + return (status); +} + +/* deliver_token_string - tokenize string and deliver */ + +int deliver_token_string(LOCAL_STATE state, USER_ATTR usr_attr, + char *string, int *addr_count) +{ + TOK822 *tree; + TOK822 *addr; + int status = 0; + + if (msg_verbose) + msg_info("deliver_token_string: %s", string); + + tree = tok822_parse(string); + for (addr = tree; addr != 0; addr = addr->next) { + if (addr->type == TOK822_ADDR) { + if (addr_count) + (*addr_count)++; + status = deliver_token(state, usr_attr, addr); + if (status != 0) + break; + } + } + tok822_free_tree(tree); + return (status); +} + +/* deliver_token_stream - tokenize stream and deliver */ + +int deliver_token_stream(LOCAL_STATE state, USER_ATTR usr_attr, + VSTREAM *fp, int *addr_count) +{ + VSTRING *buf = vstring_alloc(100); + int status = 0; + + if (msg_verbose) + msg_info("deliver_token_stream: %s", VSTREAM_PATH(fp)); + + while (vstring_fgets_bound(buf, fp, var_line_limit)) { + if (*STR(buf) != '#') { + status = deliver_token_string(state, usr_attr, STR(buf), addr_count); + if (status != 0) + break; + } + } + vstring_free(buf); + return (status); +} diff --git a/postfix/makedefs b/postfix/makedefs new file mode 100644 index 000000000..47ce8a5f9 --- /dev/null +++ b/postfix/makedefs @@ -0,0 +1,199 @@ +#!/bin/sh + +#++ +# NAME +# makedefs 1 +# SUMMARY +# makefile configuration utility +# SYNOPSIS +# \fBmakedefs\fR +# DESCRIPTION +# The \fBmakedefs\fR command identifies the program compilation +# environment, and emits macro definitions on the standard output +# stream that can be prepended to template Makefiles. +# +# Default settings can be overruled by specifying them as +# environment variables. Use quotes if variables contain +# whitespace or shell meta characters. +# .IP \fBAUXLIBS=\fIobject_library...\fR +# Specifies one or more non-default object libraries. +# .IP \fBCC=\fIcompiler_command\fR +# Specifies a non-default compiler. On many systems, the default +# is \fBgcc\fR. +# .IP \fBCCARGS=\fIcompiler_arguments\fR +# Specifies non-default compiler arguments, for example, a non-default +# \fIinclude\fR directory. +# .IP \fBDEBUG=\fIdebug_level\fR +# Specifies a non-default debugging level. The default is \fB-g\fR. +# Specify \fBDEBUG=\fR to turn off debugging. +# .IP \fBOPT=\fIoptimization_level\fR +# Specifies a non-default optimization level. The default is \fB-O\fR. +# Specify \fBOPT=\fR to turn off optimization. +# 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 +#-- + +# Emit system-dependent Makefile macro definitions to standard output. + +# Defaults for most sane systems + +RANLIB=ranlib +SYSLIBS= +AR=ar +ARFL=rv + +SYSTEM=`(uname -s) 2>/dev/null` +RELEASE=`(uname -r) 2>/dev/null` + +case "$SYSTEM.$RELEASE" in + UnixWare.5*) SYSTYPE=UW7 + CC=cc + DEBUG= + RANLIB=echo + SYSLIBS="-lresolv -lsocket -lnsl" + ;; + FreeBSD.2*) SYSTYPE=FREEBSD2 + ;; + FreeBSD.3*) SYSTYPE=FREEBSD3 + ;; + OpenBSD.2*) SYSTYPE=OPENBSD2 + ;; + NetBSD.1*) SYSTYPE=NETBSD1 + ;; + BSD/OS.2*) SYSTYPE=BSDI2 + ;; + BSD/OS.3*) SYSTYPE=BSDI3 + ;; + BSD/OS.4*) SYSTYPE=BSDI4 + ;; + OSF1.V3.*) SYSTYPE=OSF1 + # Use the native compiler by default + : ${CC=cc} + : ${DEBUG="-g3"} + ;; + OSF1.V4.*) SYSTYPE=OSF1 + # Use the native compiler by default + : ${CC=cc} + : ${DEBUG="-g3"} + ;; + SunOS.4*) SYSTYPE=SUNOS4 + SYSLIBS=-lresolv + ;; + SunOS.5*) SYSTYPE=SUNOS5 + RANLIB=echo + SYSLIBS="-lresolv -lsocket -lnsl" + case $RELEASE in + 5.[0-4]) CCARGS="$CCARGS -Dusleep=doze" + esac + # Avoid common types of braindamage + case "$LD_LIBRARY_PATH" in + ?*) echo "Don't set LD_LIBRARY_PATH" 1>&2; exit 1;; + esac + case "$CC" in + *ucb*) echo "Don't use /usr/ucb/cc or ucblib" 1>&2; exit 1;; + cc*) case `which cc` in + *ucb*) echo "Don't use /usr/ucb/cc or ucblib" 1>&2; exit 1;; + esac;; + esac + ;; + AIX.*) case "`uname -v`" in + 4) SYSTYPE=AIX4 + # How embarrassing... + case "$CC" in + cc|*/cc|xlc|*/xlc) OPT=;; + esac + ;; + *) echo "Unknown AIX version: `uname -v`." 1>&2; exit 1;; + esac;; + Linux.2*) SYSTYPE=LINUX2 + for name in db nsl resolv + do + test -f /usr/lib/lib$name.a && SYSLIBS="$SYSLIBS -l$name" + done + if [ -f /usr/include/db_185.h ]; then + CCARGS="$CCARGS -DPATH_DB_H=''" + fi + if [ -f /usr/include/db/db.h ]; then + CCARGS="$CCARGS -DPATH_DB_H=''" + fi + ;; + IRIX*.5*) SYSTYPE=IRIX5 + # Use the native compiler by default + : ${CC=cc} + RANLIB=echo + ;; + IRIX*.6*) SYSTYPE=IRIX6 + # Use the native compiler by default, and allow nested comments. + : ${CC="cc -woff 1009,1116,1412"} + RANLIB=echo + ;; +HP-UX.A.09.*) SYSTYPE=HPUX9 + SYSLIBS=-ldbm + CCARGS="$CCARGS -Dusleep=doze" + if [ -f /usr/lib/libdb.a ]; then + CCARGS="$CCARGS -DHAS_DB" + SYSLIBS="$SYSLIBS -ldb" + fi + ;; +HP-UX.B.10.*) SYSTYPE=HPUX10 + if [ -f /usr/lib/libdb.a ]; then + CCARGS="$CCARGS -DHAS_DB" + SYSLIBS=-ldb + fi + ;; +HP-UX.B.11.*) SYSTYPE=HPUX11 + SYSLIBS=-lnsl + if [ -f /usr/lib/libdb.a ]; then + CCARGS="$CCARGS -DHAS_DB" + SYSLIBS="$SYSLIBS -ldb" + fi + ;; + ".") if [ -d /NextApps ]; then + SYSTYPE=`hostinfo | sed -n \ + 's/^.*NeXT Mach 3.*$/NEXTSTEP3/;/NEXTSTEP3/{p;q;}'` + if [ "$SYSTYPE" = "" ]; then + SYSTYPE=`hostinfo | sed -n \ + 's/^.*NeXT Mach 4.*$/OPENSTEP4/;/OPENSTEP4/{p;q;}'` + fi + RANLIB="sleep 5; ranlib" + else + echo "Unable to determine your system type." 1>&2; exit 1 + fi + ;; + *) echo "Unknown system type: $SYSTEM $RELEASE" 1>&2; exit 1;; +esac + +# Defaults that can be overruled (make makefiles CC=cc OPT=-O6 DEBUG=) +# Disable optimizations by default when compiling for Purify. Disable +# optimizations by default with gcc 2.8, until the compiler is known to +# be OK. Those who dare can still overrule this (make makefiles OPT=-O). + +case "$CC" in + *purify*) : ${OPT=};; +*/gcc|gcc) case `$CC -v` in + "gcc version 2.8"*) : ${OPT=};; + esac;; + *) : ${OPT='-O'};; +esac + +: ${CC='gcc $(WARN)'} ${OPT='-O'} ${DEBUG='-g'} + +export SYSTYPE AR ARFL RANLIB SYSLIBS CC OPT DEBUG OPTS + +sed 's/ / /g' <$@ + +clean: + rm -f cat?/* + +tidy: clean + +clobber: + rm -f $(DAEMONS) $(COMMANDS) $(CONFIG) + +man8/bounce.8: ../bounce/bounce.c + srctoman $? >$@ + +man8/defer.8: + echo .so man8/bounce.8 >$@ + +man8/cleanup.8: ../cleanup/cleanup.c + srctoman $? >$@ + +man8/local.8: ../local/local.c + srctoman $? >$@ + +man8/master.8: ../master/master.c + srctoman $? >$@ + +man8/pickup.8: ../pickup/pickup.c + srctoman $? >$@ + +man8/pipe.8: ../pipe/pipe.c + srctoman $? >$@ + +man8/qmgr.8: ../qmgr/qmgr.c + srctoman $? >$@ + +man8/showq.8: ../showq/showq.c + srctoman $? >$@ + +man8/smtp.8: ../smtp/smtp.c + srctoman $? >$@ + +man8/smtpd.8: ../smtpd/smtpd.c + srctoman $? >$@ + +man8/trivial-rewrite.8: ../trivial-rewrite/trivial-rewrite.c + srctoman $? >$@ + +man1/postalias.1: ../postalias/postalias.c + srctoman $? >$@ + +man1/postcat.1: ../postcat/postcat.c + srctoman $? >$@ + +man1/postconf.1: ../postconf/postconf.c + srctoman $? >$@ + +man1/postdrop.1: ../postdrop/postdrop.c + srctoman $? >$@ + +man1/postfix.1: ../postfix/postfix.c + srctoman $? >$@ + +man1/postkick.1: ../postkick/postkick.c + srctoman $? >$@ + +man1/postlock.1: ../postlock/postlock.c + srctoman $? >$@ + +man1/postlog.1: ../postlog/postlog.c + srctoman $? >$@ + +man1/postmap.1: ../postmap/postmap.c + srctoman $? >$@ + +man1/sendmail.1: ../sendmail/sendmail.c + srctoman $? >$@ + +man1/mailq.1: + echo .so man1/sendmail.1 >$@ + +man1/newaliases.1: + echo .so man1/sendmail.1 >$@ + +man5/access.5: ../conf/access + srctoman - $? >$@ + +man5/aliases.5: ../conf/aliases + srctoman - $? >$@ + +man5/canonical.5: ../conf/canonical + srctoman - $? >$@ + +man5/relocated.5: ../conf/relocated + srctoman - $? >$@ + +man5/transport.5: ../conf/transport + srctoman - $? >$@ + +man5/virtual.5: ../conf/virtual + srctoman - $? >$@ diff --git a/postfix/man/cat1/.keep b/postfix/man/cat1/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/postfix/man/cat5/.keep b/postfix/man/cat5/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/postfix/man/cat8/.keep b/postfix/man/cat8/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/postfix/man/man1/mailq.1 b/postfix/man/man1/mailq.1 new file mode 100644 index 000000000..b12bf1859 --- /dev/null +++ b/postfix/man/man1/mailq.1 @@ -0,0 +1 @@ +.so man1/sendmail.1 diff --git a/postfix/man/man1/newaliases.1 b/postfix/man/man1/newaliases.1 new file mode 100644 index 000000000..b12bf1859 --- /dev/null +++ b/postfix/man/man1/newaliases.1 @@ -0,0 +1 @@ +.so man1/sendmail.1 diff --git a/postfix/man/man1/postalias.1 b/postfix/man/man1/postalias.1 new file mode 100644 index 000000000..d01cfe234 --- /dev/null +++ b/postfix/man/man1/postalias.1 @@ -0,0 +1,106 @@ +.TH POSTALIAS 1 +.ad +.fi +.SH NAME +postalias +\- +Postfix alias database maintenance +.SH SYNOPSIS +.na +.nf +.fi +\fBpostalias\fR [\fB-c \fIconfig_dir\fR] [\fB-i\fR] [\fB-v\fR] +[\fIfile_type\fR:]\fIfile_name\fR ... +.SH DESCRIPTION +.ad +.fi +The \fBpostalias\fR command creates a new Postfix alias database, +or updates an existing one. The input and output file formats +are expected to be compatible with Sendmail version 8, and are +expected to be suitable for the use as NIS alias maps. + +While a database update is in progress, signal delivery is +postponed, and an exclusive, advisory, lock is placed on the +entire database, in order to avoid surprises in spectator +programs. + +Options: +.IP "\fB-c \fIconfig_dir\fR" +Read the \fBmain.cf\fR configuration file in the named directory. +.IP \fB-i\fR +Incremental mode. Read entries from standard input and do not +truncate an existing database. By default, \fBpostalias\fR creates +a new database from the entries in \fBfile_name\fR. +.IP \fB-v\fR +Enable verbose logging for debugging purposes. Multiple \fB-v\fR +options make the software increasingly verbose. +.PP +Arguments: +.IP \fIfile_type\fR +The type of database to be produced. +.RS +.IP \fBbtree\fR +The output is a btree file, named \fIfile_name\fB.db\fR. +This is available only on systems with support for \fBdb\fR databases. +.IP \fBdbm\fR +The output consists of two files, named \fIfile_name\fB.pag\fR and +\fIfile_name\fB.dir\fR. +This is available only on systems with support for \fBdbm\fR databases. +.IP \fBhash\fR +The output is a hashed file, named \fIfile_name\fB.db\fR. +This is available only on systems with support for \fBdb\fR databases. +.PP +When no \fIfile_type\fR is specified, the software uses the database +type specified via the \fBdatabase_type\fR configuration parameter. +The default value for this parameter depends on the host environment. +.RE +.IP \fIfile_name\fR +The name of the alias database source file when rebuilding a database. +.SH DIAGNOSTICS +.ad +.fi +Problems are logged to the standard error stream. No output means +no problems were detected. Duplicate entries are skipped and are +flagged with a warning. +.SH ENVIRONMENT +.na +.nf +.ad +.fi +.IP \fBMAIL_CONFIG\fR +Mail configuration database. +.IP \fBMAIL_VERBOSE\fR +Enable verbose logging for debugging purposes. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this program. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. +.IP \fBdatabase_type\fR +Default alias database type. On many UNIX systems, the default type +is either \fBdbm\fR or \fBhash\fR. +.SH STANDARDS +.na +.nf +RFC 822 (ARPA Internet Text Messages) +.SH SEE ALSO +.na +.nf +aliases(5) format of alias database input file. +sendmail(1) mail posting and compatibility interface. +.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/postcat.1 b/postfix/man/man1/postcat.1 new file mode 100644 index 000000000..a343e5a31 --- /dev/null +++ b/postfix/man/man1/postcat.1 @@ -0,0 +1,40 @@ +.TH POSTCAT 1 +.ad +.fi +.SH NAME +postcat +\- +show Postfix queue file contents +.SH SYNOPSIS +.na +.nf +\fBpostcat\fR [\fB-v\fR] [\fIfiles\fR...] +.SH DESCRIPTION +.ad +.fi +The \fBpostcat\fR command prints the contents of the named +Postfix queue \fIfiles\fR in human-readable form. If no +\fIfiles\fR are specified on the command line, the program +reads from standard input. + +Options: +.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. +.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/postconf.1 b/postfix/man/man1/postconf.1 new file mode 100644 index 000000000..bc34bcd0c --- /dev/null +++ b/postfix/man/man1/postconf.1 @@ -0,0 +1,43 @@ +.TH POSTCONF 1 +.ad +.fi +.SH NAME +postconf +\- +Postfix configuration utility +.SH SYNOPSIS +.na +.nf +.fi +\fBpostconf\fR [\fB-d\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). + +Options: +.IP \fB-d\fR +Print default parameter settings instead of actual settings. +.IP \fB-n\fR +Print non-default parameter settings only. +.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. +.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/postdrop.1 b/postfix/man/man1/postdrop.1 new file mode 100644 index 000000000..e76860f1f --- /dev/null +++ b/postfix/man/man1/postdrop.1 @@ -0,0 +1,83 @@ +.TH POSTDROP 1 +.ad +.fi +.SH NAME +postdrop +\- +Postfix mail posting agent +.SH SYNOPSIS +.na +.nf +\fBpostdrop\fR [\fIoption ...\fR] +.SH DESCRIPTION +.ad +.fi +The \fBpostdrop\fR command creates a file in the \fBmaildrop\fR +directory and copies its standard input to the file. + +The command is designed to run with set-gid privileges, and with +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. + +Options: +.IP \fB-v\fR +Enable verbose logging for debugging purposes. Multiple \fB-v\fR +options make the software increasingly verbose. +.SH SECURITY +.na +.nf +.ad +.fi +This program is designed so that it can run with set-user (or +group) id privileges. +.SH DIAGNOSTICS +.ad +.fi +Fatal errors: malformed input, I/O error, out of memory. Problems +are logged to \fBsyslogd\fR(8) and to the standard error stream. +When the input is incomplete, or when the process receives a HUP, +INT, QUIT or TERM signal, the queue file is deleted. +.SH ENVIRONMENT +.na +.nf +.ad +.fi +The program deletes all environment information, because the C +library can't be trusted. +.SH FILES +.na +.nf +/var/spool/postfix, mail queue +/etc/postfix, configuration files +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +See the Postfix \fBmain.cf\fR file for syntax details and for +default values. Use the \fBpostfix reload\fR command after a +configuration change. +.IP \fBqueue_directory\fR +Top-level directory of the Postfix queue. This is also the root +directory of Postfix daemons that run chrooted. +.SH SEE ALSO +.na +.nf +sendmail(1) compatibility interface +syslogd(8) system logging +.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/postfix.1 b/postfix/man/man1/postfix.1 new file mode 100644 index 000000000..da42149eb --- /dev/null +++ b/postfix/man/man1/postfix.1 @@ -0,0 +1,110 @@ +.TH POSTFIX 1 +.ad +.fi +.SH NAME +postfix +\- +Postfix control program +.SH SYNOPSIS +.na +.nf +.fi +\fBpostfix\fR [\fB-c \fIconfig_dir\fR] [\fB-D\fR] [\fB-v\fR] +\fIcommand\fR +.SH DESCRIPTION +.ad +.fi +The \fBpostfix\fR command controls the operation of the Postfix +mail system: start or stop the \fBmaster\fR daemon, do a health +check, and other maintenance. The command sets up a standardized +environment and runs the \fBpostfix-script\fR shell script to +do the actual work. + +The following commands are implemented: +.IP \fBcheck\fR +Validate the Postfix mail system configuration. Warn about bad +directory/file ownership or permissions, and create missing +directories. +.IP \fBstart\fR +Start the Postfix mail system. This also runs the configuration +check described above. +.IP \fBstop\fR +Stop the Postfix mail system in an orderly fashion. Running processes +are allowed to terminate at their earliest convenience. +.sp +Note: in order to refresh the Postfix mail system after a +configuration change, do not use the \fBstart\fR and \fBstop\fR +commands in succession. Use the \fBreload\fR command instead. +.IP \fBabort\fR +Stop the Postfix mail system abruptly. Running processes are +signaled to stop immediately. +.IP \fBflush\fR +Force delivery: attempt to deliver every message in the deferred +mail queue. Normally, attempts to deliver delayed mail happen at +regular intervals, the interval doubling after each failed attempt. +.IP \fBreload\fR +Re-read configuration files. Running processes terminate at their +earliest convenience. +.PP +The following options are implemented: +.IP "\fB-c \fIconfig_dir\fR" +The absolute path to a directory with Postfix configuration files. +Use this to distinguish between multiple Postfix instances on the +same host. +.IP "\fB-D\fR (with \fBpostfix start\fR only)" +Run each Postfix daemon under control of a debugger as specified +via the \fBdebugger_command\fR configuration parameter. +.IP \fB-v\fR +Enable verbose logging for debugging purposes. Multiple \fB-v\fR +options make the software increasingly verbose. +.SH ENVIRONMENT +.na +.nf +.ad +.fi +The \fBpostfix\fR command sets the following environment +variables: +.IP \fBMAIL_CONFIG\fR +The Postfix configuration directory. +.IP \fBMAIL_VERBOSE\fR +This is set when the -v command-line option is present. +.IP \fBMAIL_DEBUG\fR +This is set when the -D command-line option is present. +.PP +The following configuration parameters are made available +as process environment variables with the same names: +.IP \fBcommand_directory\fR +The directory with Postfix support commands (default: +\fB$program_directory\fR). +.IP \fBdaemon_directory\fR +The directory with Postfix daemon programs (default: +\fB$program_directory\fR). +.IP \fBconfig_directory\fR +The directory with configuration files and with administrative +shell scripts. +.IP \fBqueue_directory\fR +The directory with the Postfix queue directory (and with some +files needed for programs running in a chrooted environment). +.IP \fBmail_owner\fR +The owner of the Postfix queue and of most Postfix processes. +.SH FILES +.na +.nf +$\fBconfig_directory/postfix-script\fR, administrative commands +.SH SEE ALSO +.na +.nf +master(8) Postfix master program +.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/postkick.1 b/postfix/man/man1/postkick.1 new file mode 100644 index 000000000..6ff0220cd --- /dev/null +++ b/postfix/man/man1/postkick.1 @@ -0,0 +1,81 @@ +.TH POSTKICK 1 +.ad +.fi +.SH NAME +postkick +\- +kick a Postfix service +.SH SYNOPSIS +.na +.nf +.fi +\fBpostkick\fR [\fB-c \fIconfig_dir\fR] [\fB-v\fR] +\fIclass service request\fR +.SH DESCRIPTION +.ad +.fi +The \fBpostkick\fR command sends \fIrequest\fR to the +specified \fIservice\fR over a local transport channel. +This command makes Postfix private IPC accessible +for use in, for example, shell scripts. + +Options: +.IP "\fB-c\fR \fIconfig_dir\fR" +Read configuration information from \fBmain.cf\fR in the named +configuration directory. +.IP \fB-v\fR +Enable verbose logging for debugging purposes. Multiple \fB-v\fR +options make the software increasingly verbose. +.PP +Arguments: +.IP \fIclass\fR +Name of a class of local transport channel endpoints, +either \fBpublic\fR (accessible by any local user) or +\fBprivate\fR (administrative access only). +.IP \fIservice\fR +The name of a local transport endpoint within the named class. +.IP \fIrequest\fR +A string. The list of valid requests is service-specific. +.SH DIAGNOSTICS +.ad +.fi +Problems and transactions are logged to the standard error +stream. +.SH ENVIRONMENT +.na +.nf +.ad +.fi +.IP \fBMAIL_CONFIG\fR +Directory with Postfix configuration files. +.IP \fBMAIL_VERBOSE\fR +Enable verbose logging. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this program. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. +.IP \fBqueue_directory\fR +Location of the Postfix queue, and of the local IPC communication +endpoints. +.SH SEE ALSO +.na +.nf +qmgr(8) queue manager trigger protocol +pickup(8) local pickup daemon +.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/postlock.1 b/postfix/man/man1/postlock.1 new file mode 100644 index 000000000..36c300e2e --- /dev/null +++ b/postfix/man/man1/postlock.1 @@ -0,0 +1,94 @@ +.TH POSTLOCK 1 +.ad +.fi +.SH NAME +postlock +\- +lock mail folder and execute command +.SH SYNOPSIS +.na +.nf +.fi +\fBpostlock\fR [\fB-c \fIconfig_dir\fB] [\fB-v\fR] +\fIfile command...\fR +.SH DESCRIPTION +.ad +.fi +The \fBpostlock\fR command locks \fIfile\fR for exclusive +access, and executes \fIcommand\fR. The locking method is +compatible with the Postfix UNIX-style local delivery agent. + +Options: +.IP "\fB-c \fIconfig_dir\fR" +Read configuration information from \fBmain.cf\fR in the named +configuration directory. +.IP \fB-v\fR +Enable verbose mode for debugging purposes. Multiple \fB-v\fR +options make the software increasingly verbose. +.PP +Arguments: +.IP \fIfile\fR +A mailbox file. The user should have read/write permission. +.IP \fIcommand...\fR +The command to execute while \fIfile\fR is locked for exclusive +access. The command is executed directly, i.e. without +interpretation by a shell command interpreter. +.SH DIAGNOSTICS +.ad +.fi +The result status is 255 (on some systems: -1) when \fBpostlock\fR +could not perform the requested operation. Otherwise, the +exit status is the exit status from the command. +.SH BUGS +.ad +.fi +With remote file systems, the ability to acquire a lock does not +necessarily eliminate access conflicts. Avoid file access by +processes running on different machines. +.SH ENVIRONMENT +.na +.nf +.ad +.fi +.IP \fBMAIL_CONFIG\fR +Directory with Postfix configuration files. +.IP \fBMAIL_VERBOSE\fR +Enable verbose logging. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this program. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. +.SH "Locking controls" +.ad +.fi +.IP \fBdeliver_lock_attempts\fR +Limit the number of attempts to acquire an exclusive lock. +.IP \fBdeliver_lock_delay\fR +Time in seconds between successive attempts to acquire +an exclusive lock. +.IP \fBstale_lock_time\fR +Limit the time after which a stale lock is removed. +.SH "Resource controls" +.ad +.fi +.IP \fBfork_attempts\fR +Number of attempts to \fBfork\fR() a process before giving up. +.IP \fBfork_delay\fR +Delay in seconds between successive \fBfork\fR() attempts. +.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/postlog.1 b/postfix/man/man1/postlog.1 new file mode 100644 index 000000000..7a7bbb6e3 --- /dev/null +++ b/postfix/man/man1/postlog.1 @@ -0,0 +1,56 @@ +.TH POSTLOG 1 +.ad +.fi +.SH NAME +postlog +\- +Postfix-compatible logging utility +.SH SYNOPSIS +.na +.nf +.fi +\fBpostlog\fR [\fB-i\fR] [\fB-p \fIpriority\fB] [\fB-t \fItag\fR] +[\fB-v\fR] [\fItext...\fR] +.SH DESCRIPTION +.ad +.fi +The \fBpostlog\fR command implements a Postfix-compatible logging +interface for use in, for example, shell scripts. + +By default, \fBpostlog\fR logs the \fItext\fR given on the command +line as one record. If no \fItext\fR is specified on the command +line, \fBpostlog\fR reads from standard input and logs each input +line as one record. + +Logging is sent to \fBsyslogd\fR(8); when the standard error stream +is connected to a terminal, logging is sent there as well. + +The following options are implemented: +.IP \fB-i\fR +Include the process ID in the logging tag. +.IP "\fB-p \fIpriority\fR" +Specifies the logging severity: \fBinfo\fR (default), \fBwarn\fR, +\fBerror\fR, \fBfatal\fR, or \fBpanic\fR. +.IP "\fB-t \fItag\fR" +Specifies the logging tag, that is, the identifying name that +appears at the beginning of each logging record. +.IP \fB-v\fR +Enable verbose logging for debugging purposes. Multiple \fB-v\fR +options make the software increasingly verbose. +.SH SEE ALSO +.na +.nf +syslogd(8) syslog daemon. +.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/postmap.1 b/postfix/man/man1/postmap.1 new file mode 100644 index 000000000..70df09230 --- /dev/null +++ b/postfix/man/man1/postmap.1 @@ -0,0 +1,113 @@ +.TH POSTMAP 1 +.ad +.fi +.SH NAME +postmap +\- +Postfix lookup table management +.SH SYNOPSIS +.na +.nf +.fi +\fBpostmap\fR [\fB-c \fIconfig_dir\fR] [\fB-i\fR] [\fB-v\fR] +[\fIfile_type\fR:]\fIfile_name\fR +.SH DESCRIPTION +.ad +.fi +The \fBpostmap\fR command creates a new Postfix lookup table, +or updates an existing one. The input and output formats are +expected to be compatible with: + +.ti +4 +\fBmakemap \fIfile_type\fR \fIfile_name\fR < \fIfile_name\fR + +While the table update is in progress, signal delivery is +postponed, and an exclusive, advisory, lock is placed on the +entire table, in order to avoid surprises in spectator +programs. + +The format of a lookup table input file is as follows: +.IP \(bu +Blank lines are ignored. So are lines beginning with `#'. +.IP \(bu +A table entry has the form +.sp +.ti +5 +\fIkey\fR whitespace \fIvalue\fR +.IP \(bu +A line that starts with whitespace continues the preceding line. +.PP +The \fIkey\fR and \fIvalue\fR are processed as is, except that +surrounding white space is stripped off. Unlike with Postfix alias +databases, quotes cannot be used to protect lookup keys that contain +special characters such as `#' or whitespace. The \fIkey\fR is mapped +to lowercase to make mapping lookups case insensitive. + +Options: +.IP "\fB-c \fIconfig_dir\fR" +Read the \fBmain.cf\fR configuration file in the named directory. +.IP \fB-i\fR +Incremental mode. Read entries from standard input and do not +truncate an existing database. By default, \fBpostmap\fR creates +a new database from the entries in \fBfile_name\fR. +.IP \fB-v\fR +Enable verbose logging for debugging purposes. Multiple \fB-v\fR +options make the software increasingly verbose. +.PP +Arguments: +.IP \fIfile_type\fR +The type of database to be produced. +.RS +.IP \fBbtree\fR +The output file is a btree file, named \fIfile_name\fB.db\fR. +This is available only on systems with support for \fBdb\fR databases. +.IP \fBdbm\fR +The output consists of two files, named \fIfile_name\fB.pag\fR and +\fIfile_name\fB.dir\fR. +This is available only on systems with support for \fBdbm\fR databases. +.IP \fBhash\fR +The output file is a hashed file, named \fIfile_name\fB.db\fR. +This is available only on systems with support for \fBdb\fR databases. +.PP +When no \fIfile_type\fR is specified, the software uses the database +type specified via the \fBdatabase_type\fR configuration parameter. +.RE +.IP \fIfile_name\fR +The name of the lookup table source file when rebuilding a database. +.SH DIAGNOSTICS +.ad +.fi +Problems and transactions are logged to the standard error +stream. No output means no problems. Duplicate entries are +skipped and are flagged with a warning. +.SH ENVIRONMENT +.na +.nf +.ad +.fi +.IP \fBMAIL_CONFIG\fR +Mail configuration database +.IP \fBMAIL_VERBOSE\fR +Enable verbose logging for debugging purposes. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +.IP \fBdatabase_type\fR +Default output database type. +On many UNIX systems, the default database type is either \fBhash\fR +or \fBdbm\fR. +.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 new file mode 100644 index 000000000..287debf45 --- /dev/null +++ b/postfix/man/man1/sendmail.1 @@ -0,0 +1,233 @@ +.TH SENDMAIL 1 +.ad +.fi +.SH NAME +sendmail +\- +Postfix to Sendmail compatibility interface +.SH SYNOPSIS +.na +.nf +\fBsendmail\fR [\fIoption ...\fR] [\fIrecipient ...\fR] + +\fBmailq\fR +\fBsendmail -bp\fR + +\fBnewaliases\fR +\fBsendmail -I\fR +.SH DESCRIPTION +.ad +.fi +The \fBsendmail\fR program implements the Postfix to Sendmail +compatibility interface. +For the sake of compatibility with existing applications, some +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 +\fBpostdrop\fR(1) command, which is expected to execute with +suitable privileges. + +Specific command aliases are provided for other common modes of +operation: +.IP \fBmailq\fR +List the mail queue. Each entry shows the queue file ID, message +size, arrival time, sender, and the recipients that still need to +be delivered. If mail could not be delivered upon the last attempt, +the reason for failure is shown. This mode of operation is implemented +by connecting to the \fBshowq\fR(8) daemon. +.IP \fBnewaliases\fR +Initialize the alias database. If no alias database type is +specified, the program uses the type specified in the +\fBdatabase_type\fR configuration parameter; if no input file +is specified, the program processes the file(s) specified with the +\fBalias_database\fR configuration parameter. This mode of operation +is implemented by running the \fBpostalias\fR(1) command. +.sp +Note: it may take a minute or so before an alias database update +becomes visible. Use the \fBpostfix reload\fR command to eliminate +this delay. +.PP +These and other features can be selected by specifying the +appropriate combination of command-line options. Some features are +controlled by parameters in the \fBmain.cf\fR configuration file. + +The following options are recognized: +.IP "\fB-B \fIbody_type\fR (ignored)" +The message body MIME type. Currently, Postfix implements +\fBjust-send-eight\fR. +.IP "\fB-C \fIconfig_file\fR (ignored :-)" +The path name of the \fBsendmail.cf\fR file. Postfix configuration +files are kept in \fB/etc/postfix\fR. +.IP "\fB-F \fIfull_name\fR +Set the sender full name. This is used only with messages that +have no \fBFrom:\fR message header. +.IP \fB-I\fR +Initialize alias database. See the \fBnewaliases\fR +command above. +.IP "\fB-N \fIdsn\fR (ignored)" +Delivery status notification control. Currently, Postfix does +not implement \fBDSN\fR. +.IP "\fB-R \fIreturn_limit\fR (ignored)" +Limit the size of bounced mail. Use the \fBbounce_size_limit\fR +configuration parameter instead. +.IP "\fB-X \fIlog_file\fR (ignored)" +Log mailer traffic. Use the \fBdebug_peer_list\fR and +\fBdebug_peer_level\fR configuration parameters instead. +.IP \fB-bd\fR +Go into daemon mode. This mode of operation is implemented by +executing the \fBpostfix start\fR command. +.IP \fB-bi\fR +Initialize alias database. See the \fBnewaliases\fR +command above. +.IP \fB-bm\fR +Read mail from standard input and arrange for delivery. +This is the default mode of operation. +.IP \fB-bp\fR +List the mail queue. See the \fBmailq\fR command above. +.IP \fB-bs\fR +Stand-alone SMTP server mode. Read SMTP commands from +standard input, and write responses to standard output. +This mode of operation is implemented by running the +\fBsmtpd\fR(8) daemon. +.IP "\fB-f \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-h \fIhop_count\fR (ignored)" +Hop count limit. Use the \fBhopcount_limit\fR configuration +parameter instead. +.IP "\fB-i\fR (ignored)" +Lines beginning with "." get special treatment only with \fB-bs\fR. +.IP "\fB-m\fR (ignored)" +Backwards compatibility. +.IP "\fB-n\fR (ignored)" +Backwards compatibility. +.IP "\fB-oA\fIalias_database\fR" +Non-default alias database. Specify \fIpathname\fR or +\fItype\fR:\fIpathname\fR. See \fBpostalias\fR(1) for +details. +.IP "\fB-o7\fR (ignored)" +.IP "\fB-o8\fR (ignored)" +The message body type. Currently, Postfix implements +\fBjust-send-eight\fR. +.IP "\fB-om\fR (ignored)" +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-q\fR +Flush the mail queue. This is implemented by kicking the +\fBqmgr\fR(8) daemon. +.IP "\fB-q\fIinterval\fR (ignored)" +The interval between queue runs. Use the \fBqueue_run_delay\fR +configuration parameter instead. +.IP \fB-t\fR +Extract recipients from message headers. This requires that no +recipients be specified on the command line. +.IP \fB-v\fR +Enable verbose logging for debugging purposes. Multiple \fB-v\fR +options make the software increasingly verbose. +.SH SECURITY +.na +.nf +.ad +.fi +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. +.SH DIAGNOSTICS +.ad +.fi +Problems are logged to \fBsyslogd\fR(8) and to the standard error +stream. +.SH ENVIRONMENT +.na +.nf +.ad +.fi +.IP \fBMAIL_CONFIG\fR +Directory with Postfix configuration files. +.IP \fBMAIL_VERBOSE\fR +Enable verbose logging +.IP \fBMAIL_DEBUG\fR +Enable debugging with an external command, as specified with the +\fBdebugger_command\fR configuration parameter. +.SH FILES +.na +.nf +/var/spool/postfix, mail queue +/etc/postfix, configuration files +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +See the Postfix \fBmain.cf\fR file for syntax details and for +default values. Use the \fBpostfix reload\fR command after a +configuration change. +.IP \fBalias_database\fR +Default alias database(s) for \fBnewaliases\fR. The default value +for this parameter is system-specific. +.IP \fBbounce_size_limit\fR +The amount of original message context that is sent along +with a non-delivery notification. +.IP \fBdatabase_type\fR +Default alias etc. database type. On many UNIX systems the +default type is either \fBdbm\fR or \fBhash\fR. +.IP \fBdebugger_command\fR +Command that is executed after a Postfix daemon has initialized. +.IP \fBdebug_peer_level\fR +Increment in verbose logging level when a remote host matches a +pattern in the \fBdebug_peer_list\fR parameter. +.IP \fBdebug_peer_list\fR +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 \fBfork_attempts\fR +Number of attempts to \fBfork\fR() a process before giving up. +.IP \fBfork_delay\fR +Delay in seconds between successive \fBfork\fR() attempts. +.IP \fBhopcount_limit\fR +Limit the number of \fBReceived:\fR message headers. +.IP \fBmail_owner\fR +The owner of the mail queue and of most Postfix processes. +.IP \fBcommand_directory\fR +Directory with Postfix support commands (default: +\fB$program_directory\fR). +.IP \fBdaemon_directory\fR +Directory with Postfix daemon programs (default: +\fB$program_directory\fR). +.IP \fBqueue_directory\fR +Top-level directory of the Postfix queue. This is also the root +directory of Postfix daemons that run chrooted. +.IP \fBqueue_run_delay\fR +The time between successive scans of the deferred queue. +.SH SEE ALSO +.na +.nf +pickup(8) mail pickup daemon +postalias(1) maintain alias database +postdrop(1) privileged posting agent +postfix(1) mail system control +postkick(1) kick a Postfix daemon +qmgr(8) queue manager +showq(8) list mail queue +smtpd(8) SMTP server +syslogd(8) system logging +.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/man5/access.5 b/postfix/man/man5/access.5 new file mode 100644 index 000000000..f07281ae8 --- /dev/null +++ b/postfix/man/man5/access.5 @@ -0,0 +1,86 @@ +.TH ACCESS 5 +.ad +.fi +.SH NAME +access +\- +format of Postfix access table +.SH SYNOPSIS +.na +.nf +\fBpostmap /etc/postfix/access\fR +.SH DESCRIPTION +.ad +.fi +The optional \fBaccess\fR table directs the Postfix SMTP server +to selectively reject or accept mail from or to specific hosts, +domains, networks, host addresses or mail addresses. + +The table serves as input to the \fBpostmap\fR(1) command. The +result, an indexed file in \fBdbm\fR or \fBdb\fR format, +is used for fast searching by the mail system. After an update +it may take a minute or so before the change becomes visible. +Issue a \fBpostfix reload\fR command to eliminate the delay. + +The format of the access table is as follows: +.IP "blanks and comments" +Blank lines are ignored, as are lines beginning with `#'. +.IP "\fIpattern action\fR" +When \fIpattern\fR matches a mail address, domain or host address, +perform the corresponding \fIaction\fR. +.SH PATTERNS +.na +.nf +Patterns are tried in the order as listed below: +.ad +.fi +.IP \fIuser\fR@\fIdomain\fR +Matches the specified mail address. +.IP \fIdomain.name\fR +Matches the \fIdomain.name\fR itself and any subdomain thereof, +either in hostnames or in mail addresses. Top-level domains will +never be matched. +.IP \fIuser\fR@ +Matches all mail addresses with the specified user part. +.IP \fInet.work.addr.ess\fR +.IP \fInet.work.addr\fR +.IP \fInet.work\fR +.IP \fInet\fR +Matches any host address in the specified network. A network +address is a sequence of one or more octets separated by ".". +.SH ACTIONS +.na +.nf +.ad +.fi +.IP "[\fB45\fR]\fIXX text\fR" +Reject the address etc. that matches the pattern, and respond with +the numerical code and text. +.IP \fBREJECT\fR +Reject the address etc. that matches the pattern. A generic +error response message is generated. +.IP \fBOK\fR +.IP "\fIAny other text\fR" +Accept the address etc. that matches the pattern. +.SH BUGS +.ad +.fi +The table format does not understand quoting conventions. +.SH SEE ALSO +.na +.nf +postmap(1) create mapping table +smtpd(8) smtp server +.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/man5/aliases.5 b/postfix/man/man5/aliases.5 new file mode 100644 index 000000000..8be356658 --- /dev/null +++ b/postfix/man/man5/aliases.5 @@ -0,0 +1,132 @@ +.TH ALIASES 5 +.ad +.fi +.SH NAME +aliases +\- +format of the Postfix alias database +.SH SYNOPSIS +.na +.nf +.fi +\fBpostalias\fR [\fB-c\fR \fIconfig_dir\fR] [\fB-v\fR] +[\fIfile_type\fR:]\fIinput_file\fR +.SH DESCRIPTION +.ad +.fi +The \fBaliases\fR file provides a system-wide mechanism to +redirect mail for local recipients. + +The file serves as input to the \fBpostalias\fR(1) command. The +result, an indexed file in \fBdbm\fR or \fBdb\fR format, is +used for fast lookup by the mail system. After an update +it may take a minute or so before the change becomes visible. +Issue a \fBpostfix reload\fR command to eliminate the delay. + +The input and output file formats are expected to be compatible +with Sendmail version 8, and are expected to be suitable for the +use as NIS maps. + +Users can control delivery of their own mail by setting +up \fB.forward\fR files in their home directory. +Lines in per-user \fB.forward\fR files have the same syntax +as the right-hand side of \fBaliases\fR entries. + +The format of the alias database input file is as follows: +.IP \(bu +An alias definition has the form +.sp +.ti +5 +\fIname\fR: \fIvalue1\fR, \fIvalue2\fR, \fI...\fR +.IP \(bu +Lines that begin with whitespace continue the previous line. +.IP \(bu +Blank lines are ignored, as are lines beginning with `#'. +.PP +The \fIname\fR is a local address (no domain part). +Use double quotes when the name contains any special characters +such as whitespace, `#', `:', or `@'. The \fIname\fR is folded to +lowercase, in order to make database lookups case insensitive. +.PP +In addition, when an alias exists for \fBowner-\fIname\fR, delivery +diagnostics are directed to that address, instead of to the originator. +This is typically used to direct delivery errors to the owner of +a mailing list, who is in a better position to deal with mailing +list delivery problems than the originator of the undelivered mail. +.PP +The \fIvalue\fR contains one or more of the following: +.IP \fIaddress\fR +Mail is forwarded to \fIaddress\fR, which is compatible +with the RFC 822 standard. +.IP \fI/file/name\fR +Mail is appended to \fI/file/name\fR. See \fBlocal\fR(8) +for details of delivery to file. +Delivery is not limited to regular files. For example, to dispose +of unwanted mail, deflect it to \fB/dev/null\fR. +.IP "|\fIcommand\fR" +Mail is piped into \fIcommand\fR. Commands that contain special +characters, such as whitespace, should be enclosed between double +quotes. See \fBlocal\fR(8) for details of delivery to command. +.sp +When the command fails, a limited amount of command output is +mailed back to the sender. The file \fB/usr/include/sysexits.h\fR +defines the expected exit status codes. For example, use +\fB|"exit 67"\fR to simulate a "user unknown" error, and +\fB|"exit 0"\fR to implement an expensive black hole. +.IP \fB:include:\fI/file/name\fR +Mail is sent to the destinations listed in the named file. +Lines in \fB:include:\fR files have the same syntax +as the right-hand side of alias entries. +.sp +A destination can be any destination that is described in this +manual page. However, delivery to "|\fIcommand\fR" and +\fI/file/name\fR is disallowed by default. To enable, edit the +\fBallow_mail_to_commands\fR and \fBallow_mail_to_files\fR +configuration parameters. +.SH ADDRESS EXTENSION +.na +.nf +.ad +.fi +When alias database search fails, and the recipient localpart +contains the optional recipient delimiter (e.g., \fIuser+foo\fR), +the search is repeated for the unextended address (e.g., \fIuser\fR). +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this topic. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.IP \fBalias_maps\fR +List of alias databases. +.IP \fBallow_mail_to_commands\fR +Restrict the usage of mail delivery to external command. +.IP \fBallow_mail_to_files\fR +Restrict the usage of mail delivery to external file. +.IP \fBrecipient_delimiter\fR +Delimiter that separates recipients from address extensions. +.SH STANDARDS +.na +.nf +RFC 822 (ARPA Internet Text Messages) +.SH SEE ALSO +.na +.nf +local(8) local delivery agent +postalias(1) alias database management +.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/man5/canonical.5 b/postfix/man/man5/canonical.5 new file mode 100644 index 000000000..8ddaa6e81 --- /dev/null +++ b/postfix/man/man5/canonical.5 @@ -0,0 +1,127 @@ +.TH CANONICAL 5 +.ad +.fi +.SH NAME +canonical +\- +format of Postfix canonical table +.SH SYNOPSIS +.na +.nf +\fBpostmap /etc/postfix/canonical\fR +.SH DESCRIPTION +.ad +.fi +The optional \fBcanonical\fR file specifies an address mapping for +local and non-local addresses. The mapping is used by the +\fBcleanup\fR(8) daemon. The address mapping is recursive. + +The file serves as input to the \fBpostmap\fR(1) command. The result, +an indexed file in \fBdbm\fR or \fBdb\fR format, is used for +fast searching by the mail system. After an update +it may take a minute or so before the change becomes visible. +Issue a \fBpostfix reload\fR command to eliminate the delay. + +The \fBcanonical\fR mapping affects both message header addresses +(i.e. addresses that appear inside messages) and message envelope +addresses (for example, the addresses that are used in SMTP protocol +commands). Think Sendmail rule set \fBS3\fR, if you like. + +Typically, one would use the \fBcanonical\fR table to replace login +names by \fIFirstname.Lastname\fR, or to clean up addresses produced +by legacy mail systems. + +The \fBcanonical\fR mapping is not to be confused with \fIvirtual +domain\fR support. Use the \fBvirtual\fR(5) map for that purpose. + +The \fBcanonical\fR mapping is not to be confused with local aliasing. +Use the \fBaliases\fR(5) map for that purpose. + +The format of the \fBcanonical\fR table is as follows, mappings +being tried in the order as listed in this manual page: +.IP "blanks and comments" +Blank lines are ignored, as are lines beginning with `#'. +.IP "\fIuser\fR@\fIdomain address\fR" +\fIuser\fR@\fIdomain\fR is replaced by \fIaddress\fR. This form +has the highest precedence. +.sp +This form useful to clean up addresses produced by legacy mail systems. +It can also be used to produce \fIFirstname.Lastname\fR style +addresses, but see below for a simpler solution. +.IP "\fIuser address\fR" +\fIuser\fR@\fIsite\fR is replaced by \fIaddress\fR when \fIsite\fR is +equal to $\fBmyorigin\fR, when \fIsite\fR is listed in +$\fBmydestination\fR, or when it is listed in $\fBinet_interfaces\fR. +.sp +This form is useful for replacing login names by +\fIFirstname.Lastname\fR. +.IP "@\fIdomain address\fR" +Every address in \fIdomain\fR is replaced by \fIaddress\fR. +This form has the lowest precedence. +.PP +In all the above forms, when \fIaddress\fR has the form +@\fIotherdomain\fR, the result is the same user in \fIotherdomain\fR. +.SH ADDRESS EXTENSION +.na +.nf +.fi +.ad +When table lookup fails, and the address localpart contains the +optional recipient delimiter (e.g., \fIuser+foo\fR@\fIdomain\fR), the +search is repeated for the unextended address (e.g. +\fIuser\fR@\fIdomain\fR), and the unmatched extension is propagated +to the result of table lookup. The matching order is: +\fIuser+foo\fR@\fIdomain\fR, \fIuser\fR@\fIdomain\fR, +\fIuser+foo\fR, \fIuser\fR, and @\fIdomain\fR. +.SH BUGS +.ad +.fi +The table format does not understand quoting conventions. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this topic. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.IP \fBcanonical_maps\fR +List of canonical mapping tables. +.IP \fBrecipient_canonical_maps\fR +Address mapping lookup table for envelope and header recipient +addresses. +.IP \fBsender_canonical_maps\fR +Address mapping lookup table for envelope and header sender +addresses. +.PP +Other parameters of interest: +.IP \fBinet_interfaces\fR +The network interface addresses that this system receives mail on. +.IP \fBmasquerade_domains\fR +List of domains that hide their subdomain structure. +.IP \fBmasquerade_exceptions\fR +List of user names that are not subject to address masquerading. +.IP \fBmydestination\fR +List of domains that this mail system considers local. +.IP \fBmyorigin\fR +The domain that is appended to locally-posted mail. +.SH SEE ALSO +.na +.nf +cleanup(8) canonicalize and enqueue mail +postmap(1) create mapping table +virtual(5) virtual domain mapping +.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/man5/relocated.5 b/postfix/man/man5/relocated.5 new file mode 100644 index 000000000..4325a74a4 --- /dev/null +++ b/postfix/man/man5/relocated.5 @@ -0,0 +1,95 @@ +.TH RELOCATED 5 +.ad +.fi +.SH NAME +relocated +\- +format of Postfix relocated table +.SH SYNOPSIS +.na +.nf +\fBpostmap /etc/postfix/relocated\fR +.SH DESCRIPTION +.ad +.fi +The optional \fBrelocated\fR file provides the information that is +used in "user has moved to \fInew_location\fR" bounce messages. + +The file serves as input to the \fBpostmap\fR(1) command. The result, +an indexed file in \fBdbm\fR or \fBdb\fR format, is used for +fast searching by the mail system. After an update +issue a \fBpostfix reload\fR command to make the change visible. + +Table lookups are case insensitive. + +The format of the table is as follows: +.IP \(bu +Blank lines are ignored, as are lines beginning with `#'. +.IP \(bu +An entry has one of the following form: +.ti +5 +\fIkey new_location\fR +.br +Where \fInew_location\fR specifies contact information such as +an email address, or perhaps a street address or telephone number. +.PP +The \fIkey\fR field is one of the following: +.IP \fIuser\fR@\fIdomain\fR +Matches \fIuser\fR@\fIdomain\fR. This form has precedence over all +other forms. +.IP \fIuser\fR +Matches \fIuser\fR@\fIsite\fR when \fIsite\fR is $\fBmyorigin\fR, +when \fIsite\fR is listed in $\fBmydestination\fR, or when \fIsite\fR +is listed in $\fBinet_interfaces\fR. +.IP @\fIdomain\fR +Matches every address in \fIdomain\fR. This form has the lowest +precedence. +.SH ADDRESS EXTENSION +.na +.nf +.fi +.ad +When the search fails, and the address localpart contains the +optional recipient delimiter (e.g., \fIuser+foo\fR@\fIdomain\fR), +the search is repeated for the unextended address (e.g. +\fIuser\fR@\fIdomain\fR). +.SH BUGS +.ad +.fi +The table format does not understand quoting conventions. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this topic. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.IP \fBrelocated_maps\fR +List of lookup tables for relocated users or sites. +.PP +Other parameters of interest: +.IP \fBinet_interfaces\fR +The network interface addresses that this system receives mail on. +.IP \fBmydestination\fR +List of domains that this mail system considers local. +.IP \fBmyorigin\fR +The domain that is appended to locally-posted mail. +.SH SEE ALSO +.na +.nf +postmap(1) create lookup table +.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/man5/transport.5 b/postfix/man/man5/transport.5 new file mode 100644 index 000000000..b209ecc08 --- /dev/null +++ b/postfix/man/man5/transport.5 @@ -0,0 +1,116 @@ +.TH TRANSPORT 5 +.ad +.fi +.SH NAME +transport +\- +format of Postfix transport table +.SH SYNOPSIS +.na +.nf +\fBpostmap /etc/postfix/transport\fR +.SH DESCRIPTION +.ad +.fi +The optional \fBtransport\fR file specifies a mapping from domain +hierarchies to message delivery transports and/or relay hosts. The +mapping is used by the \fBtrivial-rewrite\fR(8) daemon. + +The file serves as input to the \fBpostmap\fR(1) command. The result, +an indexed file in \fBdbm\fR or \fBdb\fR format, is used for +fast searching by the mail system. After updating this table, +issue the \fBpostfix reload\fR command to make the change visible. + +The format of the transport table is as follows: +.IP "blanks and comments" +Blank lines are ignored, as are lines beginning with `#'. +.IP "\fIdomain transport\fR:\fInexthop\fR" +Mail for \fIdomain\fR is delivered through \fItransport\fR to +\fInexthop\fR. +.IP "\fI.domain transport\fR:\fInexthop\fR" +Mail for any subdomain of \fIdomain\fR is delivered through +\fItransport\fR to \fInexthop\fR. + +The interpretation of the \fInexthop\fR field is transport +dependent. In the case of SMTP, specify \fIhost\fR:\fIservice\fR for a +non-default server port, and use [\fIhost\fR] or [\fIhost\fR:\fIport\fR] +in order to disable MX (mail exchanger) DNS lookups. The [] form +can also be used with IP addresses instead of hostnames. +.SH EXAMPLES +.na +.nf +.ad +In order to send mail for \fBfoo.org\fR and its subdomains +via the \fBuucp\fR transport to the UUCP host named \fBfoo\fR: + +.ti +5 +\fBfoo.org uucp:foo\fR +.ti +5 +\fB\&.foo.org uucp:foo\fR + +When no \fInexthop\fR host name is specified, the destination domain +name is used instead. For example, the following directs mail for +\fIuser\fR@\fBfoo.org\fR via the \fBslow\fR transport to a mail +exchanger for \fBfoo.org\fR. The \fBslow\fR transport could be +something that runs at most one delivery process at a time: + +.ti +5 +\fBfoo.org slow:\fR + +When no \fItransport\fR is specified, the default transport is +used, as specified via the \fBdefault_transport\fR configuration +parameter. The following sends all mail for \fBfoo.org\fR and its +subdomains to host \fBgateway.foo.org\fR: + +.ti +5 +\fBfoo.org :[gateway.foo.org]\fR +.ti +5 +\fB\&.foo.org :[gateway.foo.org]\fR + +In the above example, the [] are used to suppress MX lookups. +The result would likely point to your local machine. + +In the case of delivery via SMTP, one may specify +\fIhostname\fR:\fIservice\fR instead of just a host: + +.ti +5 +\fBfoo.org smtp:bar.org:2025\fR + +This directs mail for \fIuser\fR@\fBfoo.org\fR to host \fBbar.org\fR +port \fB2025\fR. Instead of a numerical port a symbolic name may be +used. Specify [] around the destination in order to disable MX lookups. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this topic. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.IP \fBtransport_maps\fR +List of transport lookup tables. +.PP +Other parameters of interest: +.IP \fBdefault_transport\fR +The transport to use when no transport is explicitly specified. +.IP \fBrelayhost\fR +The default host to send to when no transport table entry matches. +.SH SEE ALSO +.na +.nf +postmap(1) create mapping table +trivial-rewrite(8) rewrite and resolve addresses +.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/man5/virtual.5 b/postfix/man/man5/virtual.5 new file mode 100644 index 000000000..c969af7d2 --- /dev/null +++ b/postfix/man/man5/virtual.5 @@ -0,0 +1,119 @@ +.TH VIRTUAL 5 +.ad +.fi +.SH NAME +virtual +\- +format of Postfix virtual table +.SH SYNOPSIS +.na +.nf +\fBpostmap /etc/postfix/virtual\fR +.SH DESCRIPTION +.ad +.fi +The optional \fBvirtual\fR table specifies redirections for local +and non-local recipients or domains. The redirections are used by +the \fBcleanup\fR(8) daemon. The redirections are recursive. + +The \fBvirtual\fR redirection is applied only to the recipient +envelope address, and does not affect message headers. +Think Sendmail rule set \fBS0\fR, if you like. Use \fBcanonical\fR(5) +mapping to rewrite header and envelope addresses in general. + +The file serves as input to the \fBpostmap\fR(1) command. The +result, an indexed file in \fBdbm\fR or \fBdb\fR format, +is used for fast searching by the mail system. After an update +it may take a minute or so before the change becomes visible. +Issue a \fBpostfix reload\fR command to eliminate the delay. + +Typical support for a virtual domain looks like the following: + +.in +4 +.nf +\fIvirtual.domain anything\fR (right-hand content does not matter) +\fIuser1@virtual.domain address1\fR +\fIuser2@virtual.domain address2, address3\fR +.fi +.in -4 + +With this, the SMTP server accepts mail for \fIvirtual.domain\fR +(provided that the \fBrelay_domains\fR parameter includes +$\fBvirtual_maps\fR), and mail for \fIunknown\fR@\fIvirtual.domain\fR +is bounced as undeliverable. + +The format of the virtual table is as follows, mappings being +tried in the order as listed in this manual page: +.IP "blanks and comments" +Blank lines are ignored, as are lines beginning with `#'. +.IP "\fIuser\fR@\fIdomain address, address, ...\fR" +Mail for \fIuser\fR@\fIdomain\fR is redirected to \fIaddress\fR. +This form has the highest precedence. +.IP "\fIuser address, address, ...\fR" +Mail for \fIuser\fR@\fIsite\fR is redirected to \fIaddress\fR when +\fIsite\fR is equal to $\fBmyorigin\fR, when \fIsite\fR is listed in +$\fRmydestination\fR, or when it is listed in $\fIinet_interfaces\fR. +.sp +This functionality overlaps with functionality of the local +\fIalias\fR(5) database. The difference is that \fBvirtual\fR +mapping can be applied to non-local addresses. +.IP "@\fIdomain address, address, ...\fR" +Mail for any user in \fIdomain\fR is redirected to \fIaddress\fR. +This form has the lowest precedence. +.PP +In all the above forms, when \fIaddress\fR has the form +@\fIotherdomain\fR, the result is the same user in \fIotherdomain\fR. +This works for the first address in the expansion only. +.SH ADDRESS EXTENSION +.na +.nf +.fi +.ad +When the search fails, and the address localpart contains the +optional recipient delimiter (e.g., \fIuser+foo\fR@\fIdomain\fR), +the search is repeated for the unextended address (e.g. +\fIuser\fR@\fIdomain\fR), and the unmatched address extension is +propagated to the result of expansion. The matching order is: +\fIuser+foo\fR@\fIdomain\fR, \fIuser\fR@\fIdomain\fR, +\fIuser+foo\fR, \fIuser\fR, and @\fIdomain\fR. +.SH BUGS +.ad +.fi +The table format does not understand quoting conventions. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this topic. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.IP \fBvirtual_maps\fR +List of virtual mapping tables. +.PP +Other parameters of interest: +.IP \fBinet_interfaces\fR +The network interface addresses that this system receives mail on. +.IP \fBmydestination\fR +List of domains that this mail system considers local. +.IP \fBmyorigin\fR +The domain that is appended to locally-posted mail. +.SH SEE ALSO +.na +.nf +cleanup(8) canonicalize and enqueue mail +postmap(1) create mapping table +.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/man8/bounce.8 b/postfix/man/man8/bounce.8 new file mode 100644 index 000000000..e148cc171 --- /dev/null +++ b/postfix/man/man8/bounce.8 @@ -0,0 +1,90 @@ +.TH BOUNCE 8 +.ad +.fi +.SH NAME +bounce +\- +Postfix message bounce or defer daemon +.SH SYNOPSIS +.na +.nf +\fBbounce\fR [generic Postfix daemon options] +.SH DESCRIPTION +.ad +.fi +The \fBbounce\fR daemon maintains per-message log files with +non-delivery status information. Each log file is named after the +queue file that it corresponds to, and is kept in a queue subdirectory +named after the service name in the \fBmaster.cf\fR file (either +\fBbounce\fR or \fBdefer\fR). +This program expects to be run from the \fBmaster\fR(8) process +manager. + +The \fBbounce\fR daemon processes two types of service requests: +.IP \(bu +Append a recipient status record to a per-message log file. +.IP \(bu +Post a bounce message, with a copy of a log file and of the +corresponding message. When the bounce is posted successfully, +the log file is deleted. +.PP +The software does a best effort to notify the sender that there +was a problem. A notification is sent even when the log file +or original message cannot be read. + +Optionally, a client can request that the per-message log file be +deleted when the requested operation fails. +This is used by clients that cannot retry transactions by +themselves, and that depend on retry logic in their own client. +.SH STANDARDS +.na +.nf +RFC 822 (ARPA Internet Text Messages) +.SH DIAGNOSTICS +.ad +.fi +Problems and transactions are logged to \fBsyslogd\fR(8). +.SH BUGS +.ad +.fi +The log files use an ad-hoc, unstructured format. This will have +to change in order to easily support standard delivery status +notifications. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this program. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.IP \fBbounce_size_limit\fR +Limit the amount of original message context that is sent in +a non-delivery notification. +.IP \fBmail_name\fR +Use this mail system name in the introductory text at the +start of a bounce message. +.IP \fBnotify_classes\fR +Notify the postmaster of bounced mail when this parameter +includes the \fBbounce\fR class. For privacy reasons, the message +body is not included. +.SH SEE ALSO +.na +.nf +master(8) process manager +qmgr(8) queue manager +syslogd(8) system logging +.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/man8/cleanup.8 b/postfix/man/man8/cleanup.8 new file mode 100644 index 000000000..666754fba --- /dev/null +++ b/postfix/man/man8/cleanup.8 @@ -0,0 +1,135 @@ +.TH CLEANUP 8 +.ad +.fi +.SH NAME +cleanup +\- +canonicalize and enqueue Postfix message +.SH SYNOPSIS +.na +.nf +\fBcleanup\fR [generic Postfix daemon options] +.SH DESCRIPTION +.ad +.fi +The \fBcleanup\fR daemon processes inbound mail, inserts it +into the \fBincoming\fR mail queue, and informs the queue +manager of its arrival. + +The \fBcleanup\fR daemon always performs the following transformations: +.IP \(bu +Insert missing message headers: (\fBResent-\fR) \fBFrom:\fR, +\fBMessage-Id:\fR, and \fBDate:\fR. +.IP \(bu +Extract envelope recipient addresses from (\fBResent-\fR) \fBTo:\fR, +\fBCc:\fR and \fBBcc:\fR message headers when no recipients are +specified in the message envelope. +.IP \(bu +Transform envelope and header addresses to the standard +\fIuser@fully-qualified-domain\fR form that is expected by other +Postfix programs. +This task is delegated to the \fBtrivial-rewrite\fR(8) daemon. +.IP \(bu +Eliminate duplicate envelope recipient addresses. +.PP +The following address transformations are optional: +.IP \(bu +Optionally, rewrite all envelope and header addresses according +to the mappings specified in the \fBcanonical\fR(5) lookup tables. +.IP \(bu +Optionally, masquerade envelope sender addresses and message +header addresses (i.e. strip host or domain information below +all domains listed in the \fBmasquerade_domains\fR parameter, +except for user names listed in \fBmasquerade_exceptions\fR). +Address masquerading does not affect envelope recipients. +.IP \(bu +Optionally, expand envelope recipients according to information +found in the \fBvirtual\fR(5) lookup tables. +.PP +The \fBcleanup\fR daemon performs sanity checks on the content of +each message. When it finds a problem, by default it returns a +diagnostic status to the client, and leaves it up to the client +to deal with the problem. Alternatively, the client can request +the \fBcleanup\fR daemon to bounce the message back to the sender +in case of trouble. +.SH STANDARDS +.na +.nf +RFC 822 (ARPA Internet Text Messages) +.SH DIAGNOSTICS +.ad +.fi +Problems and transactions are logged to \fBsyslogd\fR(8). +.SH BUGS +.ad +.fi +Table-driven rewriting rules make it hard to express \fBif then +else\fR and other logical relationships. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this program. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.SH Miscellaneous +.ad +.fi +.IP \fBhopcount_limit\fR +Limit the number of \fBReceived:\fR message headers. +.SH "Address transformations" +.ad +.fi +.IP \fBempty_address_recipient\fR +The destination for undeliverable mail from <>. This +substitution is done before all other address rewriting. +.IP \fBcanonical_maps\fR +Address mapping lookup table for sender and recipient addresses +in envelopes and headers. +.IP \fBrecipient_canonical_maps\fR +Address mapping lookup table for envelope and header recipient +addresses. +.IP \fBsender_canonical_maps\fR +Address mapping lookup table for envelope and header sender +addresses. +.IP \fBmasquerade_domains\fR +List of domains that hide their subdomain structure. +.IP \fBmasquerade_exceptions\fR +List of user names that are not subject to address masquerading. +.IP \fBvirtual_maps\fR +Address mapping lookup table for envelope recipient addresses. +.SH "Resource controls" +.ad +.fi +.IP \fBduplicate_filter_limit\fR +Limit the number of envelope recipients that are remembered. +.IP \fBheader_size_limit\fR +Limit the amount of memory in bytes used to process a message header. +.SH SEE ALSO +.na +.nf +canonical(5) canonical address lookup table format +qmgr(8) queue manager daemon +syslogd(8) system logging +trivial-rewrite(8) address rewriting +virtual(5) virtual address lookup table format +.SH FILES +.na +.nf +/etc/postfix/canonical*, canonical mapping table +/etc/postfix/virtual*, virtual mapping table +.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/man8/defer.8 b/postfix/man/man8/defer.8 new file mode 100644 index 000000000..411dfa138 --- /dev/null +++ b/postfix/man/man8/defer.8 @@ -0,0 +1 @@ +.so man8/bounce.8 diff --git a/postfix/man/man8/local.8 b/postfix/man/man8/local.8 new file mode 100644 index 000000000..3ecd49db2 --- /dev/null +++ b/postfix/man/man8/local.8 @@ -0,0 +1,289 @@ +.TH LOCAL 8 +.ad +.fi +.SH NAME +local +\- +Postfix local mail delivery +.SH SYNOPSIS +.na +.nf +\fBlocal\fR [generic Postfix daemon options] +.SH DESCRIPTION +.ad +.fi +The \fBlocal\fR daemon processes delivery requests from the +Postfix queue manager to deliver mail to local recipients. +Each delivery request specifies a queue file, a sender address, +a domain or host to deliver to, and one or more recipients. +This program expects to be run from the \fBmaster\fR(8) process +manager. + +The \fBlocal\fR daemon updates queue files and marks recipients +as finished, or it informs the queue manager that delivery should +be tried again at a later time. Delivery problem reports are sent +to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate. +.SH SYSTEM-WIDE AND USER-LEVEL ALIASING +.na +.nf +.ad +.fi +The system adminstrator can set up one or more system-wide +\fBsendmail\fR-style alias databases. +Users can have \fBsendmail\fR-style ~/.\fBforward\fR files. +Mail for \fIname\fR is delivered to the alias \fIname\fR, to +destinations in ~\fIname\fR/.\fBforward\fR, to the mailbox owned +by the user \fIname\fR, or it is sent back as undeliverable. + +An alias or ~/.\fBforward\fR file may list any combination of external +commands, destination file names, \fB:include:\fR directives, or +mail addresses. +See \fBaliases\fR(5) for a precise description. Each line in a +user's .\fBforward\fR file has the same syntax as the right-hand part +of an alias. + +When an address is found in its own alias expansion, delivery is +made to the user instead. When a user is listed in the user's own +~/.\fBforward\fR file, delivery is made to the user's mailbox instead. +An empty ~/.\fBforward\fR file means do not forward mail. + +In order to prevent the mail system from using up unreasonable +amounts of memory, input records read from \fB:include:\fR or from +~/.\fBforward\fR files are broken up into chunks of length +\fBline_length_limit\fR. + +While expanding aliases, ~/.\fBforward\fR files, and so on, the +program attempts to avoid duplicate deliveries. The +\fBduplicate_filter_limit\fR configuration parameter limits the +number of remembered recipients. +.SH MAIL FORWARDING +.na +.nf +.ad +.fi +For the sake of reliability, forwarded mail is re-submitted as +a new message, so that each recipient has a separate on-file +delivery status record. + +In order to stop mail forwarding loops early, the software adds a +\fBDelivered-To:\fR header with the envelope recipient address. If +mail arrives for a recipient that is already listed in a +\fBDelivered-To:\fR header, the message is bounced. +.SH MAILBOX DELIVERY +.na +.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. +Mailbox delivery can be delegated to an external command specified +with the \fBmailbox_command\fR configuration parameter. + +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 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. +.SH EXTERNAL COMMAND DELIVERY +.na +.nf +.ad +.fi +The \fBallow_mail_to_commands\fR configuration parameter restricts +delivery to external commands. The default setting (\fBalias, +forward\fR) forbids command destinations in \fB:include:\fR files. + +The command is executed directly where possible. Assistance by the +shell (\fB/bin/sh\fR 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-delivery status reports. +A command is forcibly terminated if it does not complete within +\fBcommand_time_limit\fR seconds. Command exit status codes are +expected to follow the conventions defined in <\fBsysexits.h\fR>. + +When mail is delivered on behalf of a user, the \fBHOME\fR, +\fBLOGNAME\fR, and \fBSHELL\fR environment variables are set +accordingly. +The \fBPATH\fR environment variable is always reset to a +system-dependent default path, and the \fBTZ\fR (time zone) +environment variable is always passed on without change. + +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. +.SH EXTERNAL FILE DELIVERY +.na +.nf +.ad +.fi +The \fBallow_mail_to_files\fR configuration parameter restricts +delivery to external files. The default setting (\fBalias, +forward\fR) forbids file destinations in \fB:include:\fR files. + +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, prepends a \fB>\fR +character to lines beginning with "\fBFrom \fR", and appends an +empty line. +When the destination is a regular file, it is locked for exclusive +access while delivery is in progress. In case of problems, an attempt +is made to truncate a regular file to its original length. +.SH ADDRESS EXTENSION +.na +.nf +.ad +.fi +The optional \fBrecipient_delimiter\fR configuration parameter +specifies how to separate address extensions from local recipient +names. + +For example, with "\fBrecipient_delimiter = +\fR", mail for +\fIname\fR+\fIfoo\fR is delivered to the alias \fIname\fR+\fIfoo\fR +or to the alias \fIname\fR, to the destinations listed in +~\fIname\fR/.\fBforward\fR+\fIfoo\fR or in ~\fIname\fR/.\fBforward\fR, +to the mailbox owned by the user \fIname\fR, or it is sent back as +undeliverable. + +In all cases the \fBlocal\fR daemon prepends a +`\fBDelivered-To:\fR \fIname\fR+\fIfoo\fR' header line. +.SH DELIVERY RIGHTS +.na +.nf +.ad +.fi +Deliveries to external files and external commands are made with +the rights of the receiving user on whose behalf the delivery is made. +In the absence of a user context, the \fBlocal\fR daemon uses the +owner rights of the \fB:include:\fR file or alias database. +When those files are owned by the superuser, delivery is made with +the rights specified with the \fBdefault_privs\fR configuration +parameter. +.SH STANDARDS +.na +.nf +RFC 822 (ARPA Internet Text Messages) +.SH DIAGNOSTICS +.ad +.fi +Problems and transactions are logged to \fBsyslogd\fR(8). +Corrupted message files are marked so that the queue +manager can move them to the \fBcorrupt\fR queue afterwards. + +Depending on the setting of the \fBnotify_classes\fR parameter, +the postmaster is notified of bounces and of other trouble. +.SH BUGS +.ad +.fi +For security reasons, the message delivery status of external commands +or of external files is never checkpointed to file. As a result, +the program may occasionally deliver more than once to a command or +external file. Better safe than sorry. + +Mutually-recursive aliases or ~/.\fBforward\fR files are not detected +early. The resulting mail forwarding loop is broken by the use of the +\fBDelivered-To:\fR message header. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this program. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.SH Miscellaneous +.ad +.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 \fBmaildir\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 \fBrecipient_delimiter\fR +Separator between username and address extension. +.SH "Locking controls" +.ad +.fi +.IP \fBdeliver_lock_attempts\fR +Limit the number of attempts to acquire an exclusive lock +on a mailbox or external file. +.IP \fBdeliver_lock_delay\fR +Time in seconds between successive attempts to acquire +an exclusive lock. +.IP \fBstale_lock_time\fR +Limit the time after which a stale lock is removed. +.SH "Resource controls" +.ad +.fi +.IP \fBcommand_time_limit\fR +Limit the amount of time for delivery to external command. +.IP \fBduplicate_filter_limit\fR +Limit the size of the duplicate filter for results from +alias etc. expansion. +.IP \fBline_length_limit\fR +Limit the amount of memory used for processing a partial +input line. +.IP \fBlocal_destination_concurrency_limit\fR +Limit the number of parallel deliveries to the same user. +The default limit is taken from the +\fBdefault_destination_concurrency_limit\fR parameter. +.IP \fBlocal_destination_recipient_limit\fR +Limit the number of recipients per message delivery. +The default limit is taken from the +\fBdefault_destination_recipient_limit\fR parameter. +.SH "Security controls" +.ad +.fi +.IP \fBallow_mail_to_commands\fR +Restrict the usage of mail delivery to external command. +.IP \fBallow_mail_to_files\fR +Restrict the usage of mail delivery to external file. +.IP \fBdefault_privs\fR +Default rights for delivery to external file or command. +.SH HISTORY +.na +.nf +.ad +.fi +The \fBDelivered-To:\fR header appears in the \fBqmail\fR system +by Daniel Bernstein. + +The \fImaildir\fR structure appears in the \fBqmail\fR system +by Daniel Bernstein. +.SH SEE ALSO +.na +.nf +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 +.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/man8/master.8 b/postfix/man/man8/master.8 new file mode 100644 index 000000000..8cf9fd3d7 --- /dev/null +++ b/postfix/man/man8/master.8 @@ -0,0 +1,138 @@ +.TH MASTER 8 +.ad +.fi +.SH NAME +master +\- +Postfix master process +.SH SYNOPSIS +.na +.nf +.fi +\fBmaster\fR [\fB-c \fIconfig_dir\fR] [\fB-D\fR] [\fB-t\fR] [\fB-v\fR] +.SH DESCRIPTION +.ad +.fi +The \fBmaster\fR daemon is the resident process that runs Postfix +daemons on demand: daemons to send or receive messages via the +network, daemons to deliver mail locally, etc. These daemons are +created on demand up to a configurable maximum number per service. + +Postfix daemons terminate voluntarily, either after being idle for +a configurable amount of time, or after having serviced a +configurable number of requests. The exception to this rule is the +resident Postfix queue manager. + +The behavior of the \fBmaster\fR daemon is controlled by the +\fBmaster.cf\fR configuration file. The table specifies zero or +more servers in the \fBUNIX\fR or \fBINET\fR domain, or servers +that take requests from a FIFO. Precise configuration details are +given in the \fBmaster.cf\fR file, and in the manual pages of the +respective daemons. + +Options: +.IP "\fB-c \fIconfig_dir\fR" +Read the \fBmain.cf\fR and \fBmaster.cf\fR configuration files in +the named directory. +.IP \fB-D\fR +After initialization, run a debugger on the master process. The +debugging command is specified with the \fBdebugger_command\fR in +the \fBmain.cf\fR global configuration file. +.IP \fB-t\fR +Test mode. Return a zero exit status when the \fBmaster.pid\fR lock +file does not exist or when that file is not locked. This is evidence +that the \fBmaster\fR daemon is not running. +.IP \fB-v\fR +Enable verbose logging for debugging purposes. This option +is passed on to child processes. Multiple \fB-v\fR options +make the software increasingly verbose. +.PP +Signals: +.IP \fBSIGHUP\fR +Upon receipt of a \fBHUP\fR signal (e.g., after \fBpostfix reload\fR), +the master process re-reads its configuration files. If a service has +been removed from the \fBmaster.cf\fR file, its running processes +are terminated immediately. +Otherwise, running processes are allowed to terminate as soon +as is convenient, so that changes in configuration settings +affect only new service requests. +.IP \fBSIGTERM\fR +Upon receipt of a \fBTERM\fR signal (e.g., after \fBpostfix abort\fR), +the master process passes the signal on to its child processes and +terminates. +This is useful for an emergency shutdown. Normally one would +terminate only the master (\fBpostfix stop\fR) and allow running +processes to finish what they are doing. +.SH DIAGNOSTICS +.ad +.fi +Problems are reported to \fBsyslogd\fR(8). +.SH BUGS +.ad +.fi +.SH ENVIRONMENT +.na +.nf +.IP \fBMAIL_DEBUG\fR +.ad +.fi +After initialization, start a debugger as specified with the +\fBdebugger_command\fR configuration parameter in the \fBmain.cf\fR +configuration file. +.IP \fBMAIL_CONFIG\fR +Directory with configuration files. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this program. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.SH Miscellaneous +.ad +.fi +.IP \fBmail_owner\fR +The owner of the mail queue and of most Postfix processes. +.IP \fBprogram_directory\fR +Directory with Postfix support programs and daemons. +.IP \fBqueue_directory\fR +Top-level directory of the Postfix queue. This is also the root +directory of Postfix daemons that run chrooted. +.SH "Resource controls" +.ad +.fi +.IP \fBdefault_process_limit\fR +Default limit for the number of simultaneous child processes that +provide a given service. +.IP \fBmax_idle\fR +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. +.SH FILES +.na +.nf +/etc/postfix/main.cf: global configuration file. +/etc/postfix/master.cf: master process configuration file. +/var/spool/postfix/pid/master.pid: master lock file. +.SH SEE ALSO +.na +.nf +qmgr(8) queue manager +pickup(8) local mail pickup +syslogd(8) system logging +.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/man8/pickup.8 b/postfix/man/man8/pickup.8 new file mode 100644 index 000000000..5b17e81d0 --- /dev/null +++ b/postfix/man/man8/pickup.8 @@ -0,0 +1,84 @@ +.TH PICKUP 8 +.ad +.fi +.SH NAME +pickup +\- +Postfix local mail pickup +.SH SYNOPSIS +.na +.nf +\fBpickup\fR [generic Postfix daemon options] +.SH DESCRIPTION +.ad +.fi +The \fBpickup\fR daemon waits for hints that new mail has been +dropped into the world-writable \fBmaildrop\fR directory, and +feeds it into the \fBcleanup\fR(8) daemon. +Ill-formatted files are deleted without notifying the originator. +This program expects to be run from the \fBmaster\fR(8) process +manager. +.SH STANDARDS +.na +.nf +.ad +.fi +None. The \fBpickup\fR daemon does not interact with the outside world. +.SH SECURITY +.na +.nf +.ad +.fi +The \fBpickup\fR daemon runs with superuser privileges so that it +1) can open a queue file with the rights of the submitting user +and 2) can access the Postfix private IPC channels. +On the positive side, the program can run chrooted, opens no files +for writing, is careful about what files it opens for reading, and +does not actually touch any data that is sent to its public service +endpoint. +.SH DIAGNOSTICS +.ad +.fi +Problems and transactions are logged to \fBsyslogd\fR(8). +.SH BUGS +.ad +.fi +The \fBpickup\fR daemon copies mail from file to the \fBcleanup\fR(8) +daemon. It could avoid message copying overhead by sending a file +descriptor instead of file data, but then the already complex +\fBcleanup\fR(8) daemon would have to deal with unfiltered user data. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this program. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.SH Miscellaneous +.ad +.fi +.IP \fBmail_owner\fR +The process privileges used while not opening a \fBmaildrop\fR file. +.IP \fBqueue_directory\fR +Top-level directory of the Postfix queue. +.SH SEE ALSO +.na +.nf +cleanup(8) message canonicalization +master(8) process manager +syslogd(8) system logging +.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/man8/pipe.8 b/postfix/man/man8/pipe.8 new file mode 100644 index 000000000..df6a6ef39 --- /dev/null +++ b/postfix/man/man8/pipe.8 @@ -0,0 +1,162 @@ +.TH PIPE 8 +.ad +.fi +.SH NAME +pipe +\- +Postfix delivery to external command +.SH SYNOPSIS +.na +.nf +\fBpipe\fR [generic Postfix daemon options] command_attributes... +.SH DESCRIPTION +.ad +.fi +The \fBpipe\fR daemon processes requests from the Postfix queue +manager to deliver messages to external commands. Each delivery +request specifies a queue file, a sender address, a domain or host +to deliver to, and one or more recipients. +This program expects to be run from the \fBmaster\fR(8) process +manager. + +The \fBpipe\fR daemon updates queue files and marks recipients +as finished, or it informs the queue manager that delivery should +be tried again at a later time. Delivery problem reports are sent +to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate. +.SH COMMAND ATTRIBUTE SYNTAX +.na +.nf +.ad +.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)" +Optional message processing flags. By default, a message is +copied unchanged. +.RS +.IP \fBF\fR +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 \fB>\fR +Prepend \fB>\fR to lines starting with "\fBFrom \fR". This expected +by, for example, \fBUUCP\fR software. +.RE +.IP "\fBuser\fR=\fIusername\fR (required)" +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. +.IP "\fBargv\fR=\fIcommand\fR... (required)" +The command to be executed. This must be specified as the +last command attribute. +The command is executed directly, i.e. without interpretation of +shell meta characters by a shell command interpreter. +.sp +In the command argument vector, the following macros are recognized +and replaced with corresponding information from the Postfix queue +manager delivery request: +.RS +.IP \fB${\fBextension\fR}\fR +This macro expands to the extension part of a recipient address. +For example, with an address \fIuser+foo@domain\fR the extension is +\fIfoo\fR. +A command-line argument that contains \fB${\fBextension\fR}\fR expands +into as many command-line arguments as there are recipients. +.IP \fB${\fBmailbox\fR}\fR +This macro expands to the complete local part of a recipient address. +For example, with an address \fIuser+foo@domain\fR the mailbox is +\fIuser+foo\fR. +A command-line argument that contains \fB${\fBmailbox\fR}\fR +expands into as many command-line arguments as there are recipients. +.IP \fB${\fBnexthop\fR}\fR +This macro expands to the next-hop hostname. +.IP \fB${\fBrecipient\fR}\fR +This macro expands to the complete recipient address. +A command-line argument that contains \fB${\fBrecipient\fR}\fR +expands into as many command-line arguments as there are recipients. +.IP \fB${\fBsender\fR}\fR +This macro expands to the envelope sender address. +.IP \fB${\fBuser\fR}\fR +This macro expands to the username part of a recipient address. +For example, with an address \fIuser+foo@domain\fR the username +part is \fIuser\fR. +A command-line argument that contains \fB${\fBuser\fR}\fR expands +into as many command-line arguments as there are recipients. +.RE +.PP +In addition to the form ${\fIname\fR}, the forms $\fIname\fR and +$(\fIname\fR) are also recognized. Specify \fB$$\fR where a single +\fB$\fR is wanted. +.SH DIAGNOSTICS +.ad +.fi +Command exit status codes are expected to +follow the conventions defined in <\fBsysexits.h\fR>. + +Problems and transactions are logged to \fBsyslogd\fR(8). +Corrupted message files are marked so that the queue manager +can move them to the \fBcorrupt\fR queue for further inspection. +.SH SECURITY +.na +.nf +.fi +.ad +This program needs a dual personality 1) to access the private +Postfix queue and IPC mechanisms, and 2) to execute external +commands as the specified user. It is therefore security sensitive. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this program. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.SH Miscellaneous +.ad +.fi +.IP \fBmail_owner\fR +The process privileges used while not running an external command. +.SH "Resource controls" +.ad +.fi +In the text below, \fItransport\fR is the first field in a +\fBmaster.cf\fR entry. +.IP \fItransport\fB_destination_concurrency_limit\fR +Limit the number of parallel deliveries to the same destination, +for delivery via the named \fItransport\fR. The default limit is +taken from the \fBdefault_destination_concurrency_limit\fR parameter. +The limit is enforced by the Postfix queue manager. +.IP \fItransport\fB_destination_recipient_limit\fR +Limit the number of recipients per message delivery, for delivery +via the named \fItransport\fR. The default limit is taken from +the \fBdefault_destination_recipient_limit\fR parameter. +The limit is enforced by the Postfix queue manager. +.IP \fItransport\fB_time_limit\fR +Limit the time for delivery to external command, for delivery via +the named \fBtransport\fR. The default limit is taken from the +\fBcommand_time_limit\fR parameter. +The limit is enforced by the Postfix queue manager. +.SH SEE ALSO +.na +.nf +bounce(8) non-delivery status reports +master(8) process manager +qmgr(8) queue manager +syslogd(8) system logging +.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/man8/qmgr.8 b/postfix/man/man8/qmgr.8 new file mode 100644 index 000000000..e18309f07 --- /dev/null +++ b/postfix/man/man8/qmgr.8 @@ -0,0 +1,251 @@ +.TH QMGR 8 +.ad +.fi +.SH NAME +qmgr +\- +Postfix queue manager +.SH SYNOPSIS +.na +.nf +\fBqmgr\fR [generic Postfix daemon options] +.SH DESCRIPTION +.ad +.fi +The \fBqmgr\fR daemon awaits the arrival of incoming mail +and arranges for its delivery via Postfix delivery processes. +The actual mail routing strategy is delegated to the +\fBtrivial-rewrite\fR(8) daemon. +This program expects to be run from the \fBmaster\fR(8) process +manager. + +Mail addressed to the local \fBdouble-bounce\fR address is silently +discarded. This stops potential loops caused by undeliverable +bounce notifications. + +Mail addressed to a user listed in the optional \fBrelocated\fR +database is bounced with a "user has moved to \fInew_location\fR" +message. See \fBrelocated\fR(5) for a precise description. +.SH MAIL QUEUES +.na +.nf +.ad +.fi +The \fBqmgr\fR daemon maintains the following queues: +.IP \fBincoming\fR +Inbound mail from the network, or mail picked up by the +local \fBpickup\fR agent from the \fBmaildrop\fR directory. +.IP \fBactive\fR +Messages that the queue manager has opened for delivery. Only +a limited number of messages is allowed to enter the \fBactive\fR +queue (leaky bucket strategy, for a fixed delivery rate). +.IP \fBdeferred\fR +Mail that could not be delivered upon the first attempt. The queue +manager implements exponential backoff by doubling the time between +delivery attempts. +.IP \fBcorrupt\fR +Unreadable or damaged queue files are moved here for inspection. +.SH DELIVERY STATUS REPORTS +.na +.nf +.ad +.fi +The \fBqmgr\fR daemon keeps an eye on per-message delivery status +reports in the following directories. Each status report file has +the same name as the corresponding message file: +.IP \fBbounce\fR +Per-recipient status information about why mail is bounced. +These files are maintained by the \fBbounce\fR(8) daemon. +.IP \fBdefer\fR +Per-recipient status information about why mail is delayed. +These files are maintained by the \fBdefer\fR(8) daemon. +.PP +The \fBqmgr\fR daemon is responsible for asking the +\fBbounce\fR(8) or \fBdefer\fR(8) daemons to send non-delivery +reports. +.SH STRATEGIES +.na +.nf +.ad +.fi +The queue manager implements a variety of strategies for +either opening queue files (input) or for message delivery (output). +.IP "\fBleaky bucket\fR" +This strategy limits the number of messages in the \fBactive\fR queue +and prevents the queue manager from running out of memory under +heavy load. +.IP \fBfairness\fR +When the \fBactive\fR queue has room, the queue manager takes one +message from the \fBincoming\fR queue and one from the \fBdeferred\fR +queue. This prevents a large mail backlog from blocking the delivery +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" +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 +attempt. +.IP "\fBdestination status cache\fR" +The queue manager avoids unnecessary delivery attempts by +maintaining a short-term, in-memory list of unreachable destinations. +.SH TRIGGERS +.na +.nf +.ad +.fi +On an idle system, the queue manager waits for the arrival of +trigger events, or it waits for a timer to go off. A trigger +is a one-byte message. +Depending on the message received, the queue manager performs +one of the following actions (the message is followed by the +symbolic constant used internally by the software): +.IP "\fBD (QMGR_REQ_SCAN_DEFERRED)\fR" +Start a deferred queue scan. If a deferred queue scan is already +in progress, that scan will be restarted as soon as it finishes. +.IP "\fBI (QMGR_REQ_SCAN_INCOMING)\fR" +Start an incoming queue scan. If an incoming queue scan is already +in progress, that scan will be restarted as soon as it finishes. +.IP "\fBA (QMGR_REQ_SCAN_ALL)\fR" +Ignore deferred queue file time stamps. The request affects +the next deferred queue scan. +.IP "\fBF (QMGR_REQ_FLUSH_DEAD)\fR" +Purge all information about dead transports and destinations. +.IP "\fBW (TRIGGER_REQ_WAKEUP)\fR" +Wakeup call, This is used by the master server to instantiate +servers that should not go away forever. The action is to start +an incoming queue scan. +.PP +The \fBqmgr\fR daemon reads an entire buffer worth of triggers. +Multiple identical trigger requests are collapsed into one, and +trigger requests are sorted so that \fBA\fR and \fBF\fR precede +\fBD\fR and \fBI\fR. Thus, in order to force a deferred queue run, +one would request \fBA F D\fR; in order to notify the queue manager +of the arrival of new mail one would request \fBI\fR. +.SH STANDARDS +.na +.nf +.ad +.fi +None. The \fBqmgr\fR daemon does not interact with the outside world. +.SH SECURITY +.na +.nf +.ad +.fi +The \fBqmgr\fR daemon is not security sensitive. It reads +single-character messages from untrusted local users, and thus may +be susceptible to denial of service attacks. The \fBqmgr\fR daemon +does not talk to the outside world, and it can be run at fixed low +privilege in a chrooted environment. +.SH DIAGNOSTICS +.ad +.fi +Problems and transactions are logged to the syslog daemon. +Corrupted message files are saved to the \fBcorrupt\fR queue +for further inspection. + +Depending on the setting of the \fBnotify_classes\fR parameter, +the postmaster is notified of bounces and of other trouble. +.SH BUGS +.ad +.fi +A single queue manager process has to compete for disk access with +multiple front-end processes such as \fBsmtpd\fR. A sudden burst of +inbound mail can negatively impact outbound delivery rates. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this program. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.SH Miscellaneous +.ad +.fi +.IP \fBrelocated_maps\fR +Tables with contact information for users, hosts or domains +that no longer exist. See \fBrelocated\fR(5). +.IP \fBqueue_directory\fR +Top-level directory of the Postfix queue. +.SH "Active queue controls" +.ad +.fi +.IP \fBqmgr_message_active_limit\fR +Limit the number of messages in the active queue. +.IP \fBqmgr_message_recipient_limit\fR +Limit the number of in-memory recipients. +.sp +This parameter also limits the size of the short-term, in-memory +destination cache. +.SH "Timing controls" +.ad +.fi +.IP \fBmin_backoff\fR +Minimal time in seconds between delivery attempts +of a deferred message. +.sp +This parameter also limits the time an unreachable destination +is kept in the short-term, in-memory destination status cache. +.IP \fBmax_backoff\fR +Maximal time in seconds between delivery attempts +of a deferred message. +.IP \fBmaximal_queue_lifetime\fR +Maximal time in days a message is queued +before it is sent back as undeliverable. +.IP \fBqueue_run_delay\fR +Time in seconds between deferred queue scans. Queue scans do +not overlap. +.IP \fBtransport_retry_time\fR +Time in seconds between attempts to contact a broken +delivery transport. +.SH "Concurrency controls" +.ad +.fi +In the text below, \fItransport\fR is the first field in a +\fBmaster.cf\fR entry. +.IP \fBinitial_destination_concurrency\fR +Initial per-destination concurrency level for parallel delivery +to the same destination. +.IP \fBdefault_destination_concurrency_limit\fR +Default limit on the number of parallel deliveries to the same +destination. +.IP \fItransport\fB_destination_concurrency_limit\fR +Limit on the number of parallel deliveries to the same destination, +for delivery via the named message \fItransport\fR. +.SH "Recipient controls" +.ad +.fi +.IP \fBdefault_destination_recipient_limit\fR +Default limit on the number of recipients per message transfer. +.IP \fItransport\fB_destination_recipient_limit\fR +Limit on the number of recipients per message transfer, for the +named message \fItransport\fR. +.SH SEE ALSO +.na +.nf +master(8), process manager +relocated(5), format of the "user has moved" table +syslogd(8) system logging +trivial-rewrite(8), address routing +.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/man8/showq.8 b/postfix/man/man8/showq.8 new file mode 100644 index 000000000..637c1d9fb --- /dev/null +++ b/postfix/man/man8/showq.8 @@ -0,0 +1,65 @@ +.TH SHOWQ 8 +.ad +.fi +.SH NAME +showq +\- +list the Postfix mail queue +.SH SYNOPSIS +.na +.nf +\fBshowq\fR [generic Postfix daemon options] +.SH DESCRIPTION +.ad +.fi +The \fBshowq\fR daemon reports the Postfix mail queue status. +It is the program that emulates the sendmail `mailq' command. + +The \fBshowq\fR daemon can also be run in stand-alone mode +by the super-user. This mode of operation is used to emulate +the `mailq' command while the Postfix mail system is down. +.SH SECURITY +.na +.nf +.ad +.fi +The \fBshowq\fR daemon can run in a chroot jail at fixed low +privilege, and takes no input from the client. Its service port +is accessible to local untrusted users, so the service can be +susceptible to denial of service attacks. +.SH STANDARDS +.na +.nf +.ad +.fi +None. The showq daemon does not interact with the outside world. +.SH DIAGNOSTICS +.ad +.fi +Problems and transactions are logged to \fBsyslogd\fR(8). +.SH BUGS +.ad +.fi +The \fBshowq\fR daemon runs at a fixed low privilege; consequently, +it cannot extract information from queue files in the +\fBmaildrop\fR directory. +.SH SEE ALSO +.na +.nf +cleanup(8) canonicalize and enqueue mail +pickup(8) local mail pickup service +qmgr(8) mail being delivered, delayed mail +syslogd(8) system logging +.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/man8/smtp.8 b/postfix/man/man8/smtp.8 new file mode 100644 index 000000000..d41894ebd --- /dev/null +++ b/postfix/man/man8/smtp.8 @@ -0,0 +1,153 @@ +.TH SMTP 8 +.ad +.fi +.SH NAME +smtp +\- +Postfix remote delivery via SMTP +.SH SYNOPSIS +.na +.nf +\fBsmtp\fR [generic Postfix daemon options] +.SH DESCRIPTION +.ad +.fi +The SMTP client processes message delivery requests from +the queue manager. Each request specifies a queue file, a sender +address, a domain or host to deliver to, and recipient information. +This program expects to be run from the \fBmaster\fR(8) process +manager. + +The SMTP client updates the queue file and marks recipients +as finished, or it informs the queue manager that delivery should +be tried again at a later time. Delivery problem reports are sent +to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate. + +The SMTP client looks up a list of mail exchanger addresses for +the destination host, sorts the list by preference, and connects +to each listed address until it finds a server that responds. + +Once the SMTP client has received the server greeting banner, no +error will cause it to proceed to the next address on the mail +exchanger list. Instead, the message is either bounced, or its +delivery is deferred until later. +.SH SECURITY +.na +.nf +.ad +.fi +The SMTP client is moderately security-sensitive. It talks to SMTP +servers and to DNS servers on the network. The SMTP client can be +run chrooted at fixed low privilege. +.SH STANDARDS +.na +.nf +RFC 821 (SMTP protocol) +RFC 1651 (SMTP service extensions) +RFC 1870 (Message Size Declaration) +RFC 2197 (Pipelining) +.SH DIAGNOSTICS +.ad +.fi +Problems and transactions are logged to \fBsyslogd\fR(8). +Corrupted message files are marked so that the queue manager can +move them to the \fBcorrupt\fR queue for further inspection. + +Depending on the setting of the \fBnotify_classes\fR parameter, +the postmaster is notified of bounces, protocol problems, and of +other trouble. +.SH BUGS +.ad +.fi +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this program. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.SH Miscellaneous +.ad +.fi +.IP \fBdebug_peer_level\fR +Verbose logging level increment for hosts that match a +pattern in the \fBdebug_peer_list\fR parameter. +.IP \fBdebug_peer_list\fR +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 \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 +exchangers for a remote destination, the list is truncated to +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. +.SH "Resource controls" +.ad +.fi +.IP \fBsmtp_destination_concurrency_limit\fR +Limit the number of parallel deliveries to the same destination. +The default limit is taken from the +\fBdefault_destination_concurrency_limit\fR parameter. +.IP \fBsmtp_destination_recipient_limit\fR +Limit the number of recipients per message delivery. +The default limit is taken from the +\fBdefault_destination_recipient_limit\fR parameter. +.SH "Timeout controls" +.ad +.fi +.IP \fBsmtp_connect_timeout\fR +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 exchanger list. +.IP \fBsmtp_helo_timeout\fR +Timeout in seconds for receiving the SMTP greeting banner. +When the server drops the connection without 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. +.IP \fBsmtp_helo_timeout\fR +Timeout in seconds for sending the \fBHELO\fR command, and for +receiving the server response. +.IP \fBsmtp_mail_timeout\fR +Timeout in seconds for sending the \fBMAIL FROM\fR command, and for +receiving the server response. +.IP \fBsmtp_rcpt_timeout\fR +Timeout in seconds for sending the \fBRCPT TO\fR command, and for +receiving the server response. +.IP \fBsmtp_data_init_timeout\fR +Timeout in seconds for sending the \fBDATA\fR command, and for +receiving the server response. +.IP \fBsmtp_data_xfer_timeout\fR +Timeout in seconds for sending the message content. +.IP \fBsmtp_data_done_timeout\fR +Timeout in seconds for sending the "\fB.\fR" command, and for +receiving the server response. When no response is received, a +warning is logged that the mail may be delivered multiple times. +.IP \fBsmtp_quit_timeout\fR +Timeout in seconds for sending the \fBQUIT\fR command, and for +receiving the server response. +.SH SEE ALSO +.na +.nf +bounce(8) non-delivery status reports +master(8) process manager +qmgr(8) queue manager +syslogd(8) system logging +.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/man8/smtpd.8 b/postfix/man/man8/smtpd.8 new file mode 100644 index 000000000..c2923b75e --- /dev/null +++ b/postfix/man/man8/smtpd.8 @@ -0,0 +1,192 @@ +.TH SMTPD 8 +.ad +.fi +.SH NAME +smtpd +\- +Postfix SMTP server +.SH SYNOPSIS +.na +.nf +\fBsmtpd\fR [generic Postfix daemon options] +.SH DESCRIPTION +.ad +.fi +The SMTP server accepts network connection requests +and performs zero or more SMTP transactions per connection. +Each received message is piped through the \fBcleanup\fR(8) +daemon, and is placed into the \fBincoming\fR queue as one +single queue file. For this mode of operation, the program +expects to be run from the \fBmaster\fR(8) process manager. + +Alternatively, the SMTP server takes an established +connection on standard input and deposits messages directly +into the \fBmaildrop\fR queue. In this so-called stand-alone +mode, the SMTP server can accept mail even while the mail +system is not running. + +The SMTP server implements a variety of policies for connection +requests, and for parameters given to \fBHELO, MAIL FROM, VRFY\fR +and \fBRCPT TO\fR commands. They are detailed below and in the +\fBmain.cf\fR configuration file. +.SH SECURITY +.na +.nf +.ad +.fi +The SMTP server is moderately security-sensitive. It talks to SMTP +clients and to DNS servers on the network. The SMTP server can be +run chrooted at fixed low privilege. +.SH STANDARDS +.na +.nf +RFC 821 (SMTP protocol) +RFC 1123 (Host requirements) +RFC 1651 (SMTP service extensions) +RFC 1652 (8bit-MIME transport) +RFC 1854 (SMTP Pipelining) +RFC 1870 (Message Size Declaration) +RFC 1985 (ETRN command) (partial) +.SH DIAGNOSTICS +.ad +.fi +Problems and transactions are logged to \fBsyslogd\fR(8). + +Depending on the setting of the \fBnotify_classes\fR parameter, +the postmaster is notified of bounces, protocol problems, +policy violations, and of other trouble. +.SH BUGS +.ad +.fi +RFC 1985 is implemented by forcing delivery of all deferred mail. +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this program. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.SH Miscellaneous +.ad +.fi +.IP \fBcommand_directory\fR +Location of Postfix support commands (default: +\fB$program_directory\fR). +.IP \fBdebug_peer_level\fR +Increment in verbose logging level when a remote host matches a +pattern in the \fBdebug_peer_list\fR parameter. +.IP \fBdebug_peer_list\fR +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 \fBhopcount_limit\fR +Limit the number of \fBReceived:\fR message headers. +.IP \fBnotify_classes\fR +List of error classes. Of special interest are: +.RS +.IP \fBpolicy\fR +When a client violates any policy, mail a transcript of the +entire SMTP session to the postmaster. +.IP \fBprotocol\fR +When a client violates the SMTP protocol or issues an unimplemented +command, mail a transcript of the entire SMTP session to the +postmaster. +.RE +.IP \fBsmtpd_banner\fR +Text that follows the \fB220\fR status code in the SMTP greeting banner. +.IP \fBsmtpd_recipient_limit\fR +Restrict the number of recipients that the SMTP server accepts +per message delivery. +.IP \fBsmtpd_timeout\fR +Limit the time to send a server response and to receive a client +request. +.SH "Resource controls" +.ad +.fi +.IP \fBline_length_limit\fR +Limit the amount of memory in bytes used for the handling of +partial input lines. +.IP \fBmessage_size_limit\fR +Limit the total size in bytes of a message, including on-disk +storage for envelope information. +.IP \fBqueue_minfree\fR +Minimal amount of free space in bytes in the queue file system +for the SMTP server to accept any mail at all. +.SH Tarpitting +.ad +.fi +.IP \fBsmtpd_error_sleep_time\fR +Time to wait in seconds before sending a 4xx or 5xx server error +response. +.IP \fBsmtpd_soft_error_limit\fR +When an SMTP client has made this number of errors, wait +\fIerror_count\fR seconds before responding to any client request. +.IP \fBsmtpd_hard_error_limit\fR +Disconnect after a client has made this number of errors. +.SH "UCE control restrictions" +.ad +.fi +.IP \fBsmtpd_client_restrictions\fR +Restrict what clients may connect to this mail system. +.IP \fBsmtpd_helo_required\fR +Require that clients introduce themselves at the beginning +of an SMTP session. +.IP \fBsmtpd_helo_restrictions\fR +Restrict what client hostnames are allowed in \fBHELO\fR and +\fBEHLO\fR commands. +.IP \fBsmtpd_sender_restrictions\fR +Restrict what sender addresses are allowed in \fBMAIL FROM\fR commands. +.IP \fBsmtpd_recipient_restrictions\fR +Restrict what recipient addresses are allowed in \fBRCPT TO\fR commands. +.IP \fBmaps_rbl_domains\fR +List of DNS domains that publish the addresses of blacklisted +hosts. +.IP \fBrelay_domains\fR +Restrict what domains or networks this mail system will relay +mail from or to. +.SH "UCE control responses" +.ad +.fi +.IP \fBaccess_map_reject_code\fR +Server response when a client violates an access database restriction. +.IP \fBinvalid_hostname_reject_code\fR +Server response when a client violates the \fBreject_invalid_hostname\fR +restriction. +.IP \fBmaps_rbl_reject_code\fR +Server response when a client violates the \fBmaps_rbl_domains\fR +restriction. +.IP \fBreject_code\fR +Response code when the client matches a \fBreject\fR restriction. +.IP \fBrelay_domains_reject_code\fR +Server response when a client attempts to violate the mail relay +policy. +.IP \fBunknown_address_reject_code\fR +Server response when a client violates the \fBreject_unknown_address\fR +restriction. +.IP \fBunknown_client_reject_code\fR +Server response when a client without address to name mapping +violates the \fBreject_unknown_clients\fR restriction. +.IP \fBunknown_hostname_reject_code\fR +Server response when a client violates the \fBreject_unknown_hostname\fR +restriction. +.SH SEE ALSO +.na +.nf +cleanup(8) message canonicalization +master(8) process manager +syslogd(8) system logging +.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/man8/trivial-rewrite.8 b/postfix/man/man8/trivial-rewrite.8 new file mode 100644 index 000000000..2af0a0bcf --- /dev/null +++ b/postfix/man/man8/trivial-rewrite.8 @@ -0,0 +1,127 @@ +.TH TRIVIAL-REWRITE 8 +.ad +.fi +.SH NAME +trivial-rewrite +\- +Postfix address rewriting and resolving daemon +.SH SYNOPSIS +.na +.nf +\fBtrivial-rewrite\fR [generic Postfix daemon options] +.SH DESCRIPTION +.ad +.fi +The \fBtrivial-rewrite\fR daemon processes two types of client +service requests: +.IP \fBrewrite\fR +Rewrite an address to standard form. The \fBtrivial-rewrite\fR +daemon by default appends local domain information to unqualified +addresses, swaps bang paths to domain form, and strips source +routing information. This process is under control of several +configuration parameters (see below). +.IP \fBresolve\fR +Resolve an address to a (\fItransport\fR, \fInexthop\fR, +\fIrecipient\fR) triple. The meaning of the results is as follows: +.RS +.IP \fItransport\fR +The delivery agent to use. This is the first field of an entry +in the \fBmaster.cf\fR file. +.IP \fInexthop\fR +The host to send to. For local delivery this is an empty string. +.IP \fIrecipient\fR +The envelope recipient address that is passed on to \fInexthop\fR. +.PP +The \fBtrivial-rewrite\fR daemon by default only distinguishes +between local and non-local mail. For finer control over mail +routing, use the optional \fBtransport\fR(5) lookup table. +.RE +.PP +This program expects to be run from the \fBmaster\fR(8) process +manager. +.SH STANDARDS +.na +.nf +.ad +.fi +None. The command does not interact with the outside world. +.SH SECURITY +.na +.nf +.ad +.fi +The \fBtrivial-rewrite\fR daemon is not security sensitive. +By default, this daemon does not talk to remote or local users. +It can run at a fixed low privilege in a chrooted environment. +.SH DIAGNOSTICS +.ad +.fi +Problems and transactions are logged to \fBsyslogd\fR(8). +.SH BUGS +.ad +.fi +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this program. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.SH Miscellaneous +.ad +.fi +.IP \fBinet_interfaces\fR +The network interfaces that this mail system receives mail on. +This information is used to determine if +\fIuser\fR@[\fInet.work.addr.ess\fR] is local or remote. +.IP \fBmydestination\fR +List of domains that this machine considers local. +.IP \fBmyorigin\fR +The domain that locally-posted mail appears to come from. +.SH Rewriting +.ad +.fi +.IP \fBallow_percent_hack\fR +Rewrite \fIuser\fR%\fIdomain\fR to \fIuser\fR@\fIdomain\fR. +.IP \fBappend_at_myorigin\fR +Rewrite \fIuser\fR to \fIuser\fR@$\fBmyorigin\fR. +.IP \fBappend_dot_mydomain\fR +Rewrite \fIuser\fR@\fIhost\fR to \fIuser\fR@\fIhost\fR.$\fBmydomain\fR. +.IP \fBswap_bangpath\fR +Rewrite \fIsite\fR!\fIuser\fR to \fIuser\fR@\fIsite\fR. +.SH Routing +.ad +.fi +.IP \fBdefault_transport\fR +The default transport to use when no transport is explicitly +given in the \fBtransport\fR(5) table. +.IP \fBrelayhost\fR +The default host to send mail to when no entry is matched +in the \fBtransport\fR(5) table. +.sp +When no \fBrelayhost\fR is specified, mail is routed directly +to the destination's mail exchanger. +.IP \fBtransport_maps\fR +List of tables with \fIdomain\fR to (\fItransport, nexthop\fR) +mappings. +.SH SEE ALSO +.na +.nf +master(8) process manager +syslogd(8) system logging +transport(5) transport table format +.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/master/.indent.pro b/postfix/master/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/master/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/master/.printfck b/postfix/master/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/master/.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/master/Makefile.in b/postfix/master/Makefile.in new file mode 100644 index 000000000..56bbaf13c --- /dev/null +++ b/postfix/master/Makefile.in @@ -0,0 +1,252 @@ +SHELL = /bin/sh +SRCS = master.c master_conf.c master_ent.c master_sig.c master_avail.c \ + master_spawn.c master_service.c master_status.o master_listen.c \ + master_proto.c single_server.c multi_server.c master_vars.c \ + master_wakeup.c +OBJS = master.o master_conf.o master_ent.o master_sig.o master_avail.o \ + master_spawn.o master_service.o master_status.o master_listen.o \ + master_vars.o master_wakeup.o +LIB_OBJ = single_server.o multi_server.o trigger_server.o master_proto.o +HDRS = mail_server.h master_proto.h +INT_HDR = master.h +WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ + -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ + -Wunused +DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) +LIB = libmaster.a +PROG = master +TESTPROG= +LIBS = ../lib/libglobal.a ../lib/libutil.a +LIB_DIR = ../lib +INC_DIR = ../include +BIN_DIR = ../bin + +.c.o:; $(CC) $(CFLAGS) -c $*.c + +all: $(PROG) $(LIB) + +Makefile: Makefile.in + (set -e; echo "# DO NOT EDIT"; $(OPTS) sh ../makedefs; cat $?) >$@ + +$(PROG): $(OBJS) $(LIBS) + $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS) + +test: $(TESTPROG) + +$(LIB): $(LIB_OBJ) + $(AR) $(ARFL) $(LIB) $? + $(RANLIB) $(LIB) + +$(LIB_DIR)/$(LIB): $(LIB) + cp $(LIB) $(LIB_DIR)/$(LIB) + $(RANLIB) $(LIB_DIR)/$(LIB) + +$(BIN_DIR)/$(PROG): $(PROG) + cp $(PROG) $(BIN_DIR) + +update: $(LIB_DIR)/$(LIB) $(BIN_DIR)/$(PROG) + -for i in $(HDRS); \ + do \ + cmp -s $$i $(INC_DIR)/$$i 2>/dev/null || cp $$i $(INC_DIR); \ + done + cd $(INC_DIR); chmod 644 $(HDRS) + +printfck: $(OBJS) $(PROG) + rm -rf printfck + mkdir printfck + cp *.h 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) junk $(LIB) + 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' +master.o: master.c +master.o: ../include/sys_defs.h +master.o: ../include/events.h +master.o: ../include/msg.h +master.o: ../include/msg_syslog.h +master.o: ../include/vstring.h +master.o: ../include/vbuf.h +master.o: ../include/mymalloc.h +master.o: ../include/iostuff.h +master.o: ../include/vstream.h +master.o: ../include/stringops.h +master.o: ../include/mail_params.h +master.o: ../include/debug_process.h +master.o: ../include/mail_task.h +master.o: ../include/config.h +master.o: ../include/open_lock.h +master.o: master.h +master_avail.o: master_avail.c +master_avail.o: ../include/sys_defs.h +master_avail.o: ../include/events.h +master_avail.o: ../include/msg.h +master_avail.o: master_proto.h +master_avail.o: master.h +master_conf.o: master_conf.c +master_conf.o: ../include/sys_defs.h +master_conf.o: ../include/msg.h +master_conf.o: ../include/argv.h +master_conf.o: master.h +master_ent.o: master_ent.c +master_ent.o: ../include/sys_defs.h +master_ent.o: ../include/msg.h +master_ent.o: ../include/mymalloc.h +master_ent.o: ../include/vstring.h +master_ent.o: ../include/vbuf.h +master_ent.o: ../include/vstream.h +master_ent.o: ../include/argv.h +master_ent.o: ../include/stringops.h +master_ent.o: ../include/readline.h +master_ent.o: ../include/inet_addr_list.h +master_ent.o: ../include/mail_proto.h +master_ent.o: ../include/iostuff.h +master_ent.o: ../include/mail_params.h +master_ent.o: ../include/own_inet_addr.h +master_ent.o: master_proto.h +master_ent.o: master.h +master_listen.o: master_listen.c +master_listen.o: ../include/sys_defs.h +master_listen.o: ../include/msg.h +master_listen.o: ../include/listen.h +master_listen.o: ../include/iostuff.h +master_listen.o: ../include/mymalloc.h +master_listen.o: ../include/stringops.h +master_listen.o: ../include/inet_addr_list.h +master_listen.o: ../include/set_eugid.h +master_listen.o: ../include/mail_params.h +master_listen.o: master.h +master_proto.o: master_proto.c +master_proto.o: ../include/sys_defs.h +master_proto.o: ../include/msg.h +master_proto.o: master_proto.h +master_service.o: master_service.c +master_service.o: ../include/sys_defs.h +master_service.o: ../include/msg.h +master_service.o: ../include/mymalloc.h +master_service.o: master.h +master_sig.o: master_sig.c +master_sig.o: ../include/sys_defs.h +master_sig.o: ../include/msg.h +master_sig.o: ../include/posix_signals.h +master_sig.o: master.h +master_spawn.o: master_spawn.c +master_spawn.o: ../include/sys_defs.h +master_spawn.o: ../include/msg.h +master_spawn.o: ../include/binhash.h +master_spawn.o: ../include/mymalloc.h +master_spawn.o: ../include/events.h +master_spawn.o: ../include/argv.h +master_spawn.o: master_proto.h +master_spawn.o: master.h +master_status.o: master_status.c +master_status.o: ../include/sys_defs.h +master_status.o: ../include/msg.h +master_status.o: ../include/events.h +master_status.o: ../include/binhash.h +master_status.o: ../include/iostuff.h +master_status.o: master_proto.h +master_status.o: master.h +master_vars.o: master_vars.c +master_vars.o: ../include/sys_defs.h +master_vars.o: ../include/msg.h +master_vars.o: ../include/stringops.h +master_vars.o: ../include/mymalloc.h +master_vars.o: ../include/config.h +master_vars.o: ../include/mail_params.h +master_vars.o: master.h +master_wakeup.o: master_wakeup.c +master_wakeup.o: ../include/sys_defs.h +master_wakeup.o: ../include/msg.h +master_wakeup.o: ../include/trigger.h +master_wakeup.o: ../include/events.h +master_wakeup.o: ../include/mail_proto.h +master_wakeup.o: ../include/vstream.h +master_wakeup.o: ../include/vbuf.h +master_wakeup.o: ../include/iostuff.h +master_wakeup.o: mail_server.h +master_wakeup.o: master.h +multi_server.o: multi_server.c +multi_server.o: ../include/sys_defs.h +multi_server.o: ../include/msg.h +multi_server.o: ../include/msg_syslog.h +multi_server.o: ../include/chroot_uid.h +multi_server.o: ../include/listen.h +multi_server.o: ../include/iostuff.h +multi_server.o: ../include/events.h +multi_server.o: ../include/vstring.h +multi_server.o: ../include/vbuf.h +multi_server.o: ../include/vstream.h +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/mail_task.h +multi_server.o: ../include/debug_process.h +multi_server.o: ../include/mail_params.h +multi_server.o: ../include/config.h +multi_server.o: ../include/timed_ipc.h +multi_server.o: ../include/resolve_local.h +multi_server.o: master_proto.h +multi_server.o: mail_server.h +single_server.o: single_server.c +single_server.o: ../include/sys_defs.h +single_server.o: ../include/msg.h +single_server.o: ../include/msg_syslog.h +single_server.o: ../include/chroot_uid.h +single_server.o: ../include/vstring.h +single_server.o: ../include/vbuf.h +single_server.o: ../include/vstream.h +single_server.o: ../include/msg_vstream.h +single_server.o: ../include/mymalloc.h +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/mail_params.h +single_server.o: ../include/mail_task.h +single_server.o: ../include/debug_process.h +single_server.o: ../include/config.h +single_server.o: ../include/timed_ipc.h +single_server.o: ../include/resolve_local.h +single_server.o: master_proto.h +single_server.o: mail_server.h +trigger_server.o: trigger_server.c +trigger_server.o: ../include/sys_defs.h +trigger_server.o: ../include/msg.h +trigger_server.o: ../include/msg_syslog.h +trigger_server.o: ../include/chroot_uid.h +trigger_server.o: ../include/vstring.h +trigger_server.o: ../include/vbuf.h +trigger_server.o: ../include/vstream.h +trigger_server.o: ../include/msg_vstream.h +trigger_server.o: ../include/mymalloc.h +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/mail_params.h +trigger_server.o: ../include/mail_task.h +trigger_server.o: ../include/debug_process.h +trigger_server.o: ../include/config.h +trigger_server.o: ../include/resolve_local.h +trigger_server.o: master_proto.h +trigger_server.o: mail_server.h diff --git a/postfix/master/mail_server.h b/postfix/master/mail_server.h new file mode 100644 index 000000000..883fd9218 --- /dev/null +++ b/postfix/master/mail_server.h @@ -0,0 +1,60 @@ +/*++ +/* NAME +/* mail_server 3h +/* SUMMARY +/* skeleton servers +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. Tables are defined in config.h. + */ +#define MAIL_SERVER_INT_TABLE 1 +#define MAIL_SERVER_STR_TABLE 2 +#define MAIL_SERVER_BOOL_TABLE 3 + +#define MAIL_SERVER_PRE_INIT 10 +#define MAIL_SERVER_POST_INIT 11 +#define MAIL_SERVER_LOOP 12 + +typedef void (*MAIL_SERVER_INIT_FN) (void); +typedef int (*MAIL_SERVER_LOOP_FN) (void); + + /* + * single_server.c + */ +typedef void (*SINGLE_SERVER_FN) (VSTREAM *, char *, char **); +extern NORETURN single_server_main(int, char **, SINGLE_SERVER_FN, ...); + + /* + * multi_server.c + */ +typedef void (*MULTI_SERVER_FN) (VSTREAM *, char *, char **); +extern NORETURN multi_server_main(int, char **, MULTI_SERVER_FN,...); +extern void multi_server_disconnect(VSTREAM *); + + /* + * trigger_server.c + */ +typedef void (*TRIGGER_SERVER_FN) (char *, int, char *, char **); +extern NORETURN trigger_server_main(int, char **, TRIGGER_SERVER_FN, ...); + +#define TRIGGER_BUF_SIZE 1024 + +/* 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 +/*--*/ diff --git a/postfix/master/master.c b/postfix/master/master.c new file mode 100644 index 000000000..fba9d1fe3 --- /dev/null +++ b/postfix/master/master.c @@ -0,0 +1,332 @@ +/*++ +/* NAME +/* master 8 +/* SUMMARY +/* Postfix master process +/* SYNOPSIS +/* .fi +/* \fBmaster\fR [\fB-c \fIconfig_dir\fR] [\fB-D\fR] [\fB-t\fR] [\fB-v\fR] +/* DESCRIPTION +/* The \fBmaster\fR daemon is the resident process that runs Postfix +/* daemons on demand: daemons to send or receive messages via the +/* network, daemons to deliver mail locally, etc. These daemons are +/* created on demand up to a configurable maximum number per service. +/* +/* Postfix daemons terminate voluntarily, either after being idle for +/* a configurable amount of time, or after having serviced a +/* configurable number of requests. The exception to this rule is the +/* resident Postfix queue manager. +/* +/* The behavior of the \fBmaster\fR daemon is controlled by the +/* \fBmaster.cf\fR configuration file. The table specifies zero or +/* more servers in the \fBUNIX\fR or \fBINET\fR domain, or servers +/* that take requests from a FIFO. Precise configuration details are +/* given in the \fBmaster.cf\fR file, and in the manual pages of the +/* respective daemons. +/* +/* Options: +/* .IP "\fB-c \fIconfig_dir\fR" +/* Read the \fBmain.cf\fR and \fBmaster.cf\fR configuration files in +/* the named directory. +/* .IP \fB-D\fR +/* After initialization, run a debugger on the master process. The +/* debugging command is specified with the \fBdebugger_command\fR in +/* the \fBmain.cf\fR global configuration file. +/* .IP \fB-t\fR +/* Test mode. Return a zero exit status when the \fBmaster.pid\fR lock +/* file does not exist or when that file is not locked. This is evidence +/* that the \fBmaster\fR daemon is not running. +/* .IP \fB-v\fR +/* Enable verbose logging for debugging purposes. This option +/* is passed on to child processes. Multiple \fB-v\fR options +/* make the software increasingly verbose. +/* .PP +/* Signals: +/* .IP \fBSIGHUP\fR +/* Upon receipt of a \fBHUP\fR signal (e.g., after \fBpostfix reload\fR), +/* the master process re-reads its configuration files. If a service has +/* been removed from the \fBmaster.cf\fR file, its running processes +/* are terminated immediately. +/* Otherwise, running processes are allowed to terminate as soon +/* as is convenient, so that changes in configuration settings +/* affect only new service requests. +/* .IP \fBSIGTERM\fR +/* Upon receipt of a \fBTERM\fR signal (e.g., after \fBpostfix abort\fR), +/* the master process passes the signal on to its child processes and +/* terminates. +/* This is useful for an emergency shutdown. Normally one would +/* terminate only the master (\fBpostfix stop\fR) and allow running +/* processes to finish what they are doing. +/* DIAGNOSTICS +/* Problems are reported to \fBsyslogd\fR(8). +/* BUGS +/* ENVIRONMENT +/* .IP \fBMAIL_DEBUG\fR +/* .ad +/* .fi +/* After initialization, start a debugger as specified with the +/* \fBdebugger_command\fR configuration parameter in the \fBmain.cf\fR +/* configuration file. +/* .IP \fBMAIL_CONFIG\fR +/* Directory with configuration files. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. Use the \fBpostfix reload\fR command after +/* a configuration change. +/* .SH Miscellaneous +/* .ad +/* .fi +/* .IP \fBmail_owner\fR +/* The owner of the mail queue and of most Postfix processes. +/* .IP \fBprogram_directory\fR +/* Directory with Postfix support programs and daemons. +/* .IP \fBqueue_directory\fR +/* Top-level directory of the Postfix queue. This is also the root +/* directory of Postfix daemons that run chrooted. +/* .SH "Resource controls" +/* .ad +/* .fi +/* .IP \fBdefault_process_limit\fR +/* Default limit for the number of simultaneous child processes that +/* provide a given service. +/* .IP \fBmax_idle\fR +/* 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. +/* FILES +/* /etc/postfix/main.cf: global configuration file. +/* /etc/postfix/master.cf: master process configuration file. +/* /var/spool/postfix/pid/master.pid: master lock file. +/* SEE ALSO +/* qmgr(8) queue manager +/* pickup(8) local mail pickup +/* syslogd(8) system logging +/* 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 libraries. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "master.h" + +int main(int argc, char **argv) +{ + static VSTREAM *lock_fp; + VSTRING *lock_path; + off_t inherited_limit; + int debug_me = 0; + int ch; + int fd; + int n; + int test_lock = 0; + int fd_limit = open_limit(0); + VSTRING *why; + + /* + * Initialize. + */ + umask(077); /* never fails! */ + + /* + * Process environment options as early as we can. + */ + if (getenv(CONF_ENV_VERB)) + msg_verbose = 1; + if (getenv(CONF_ENV_DEBUG)) + debug_me = 1; + + /* + * Don't die when a process goes away unexpectedly. + */ + signal(SIGPIPE, SIG_IGN); + + /* + * Strip and save the process name for diagnostics etc. + */ + var_procname = mystrdup(basename(argv[0])); + set_config_str(VAR_PROCNAME, var_procname); + + /* + * When running a child process, don't leak any open files that were + * leaked to us by our own (privileged) parent process. Descriptors 0-2 + * are taken care of after we have initialized error logging. + * + * Some systems such as AIX have a huge per-process open file limit. In + * those cases, limit the search for potential file descriptor leaks to + * just the first couple hundred. + */ + if (fd_limit > 500) + fd_limit = 500; + for (fd = 3; fd < fd_limit; fd++) + if ((n = fcntl(fd, F_GETFD, 0)) >= 0 && (n & FD_CLOEXEC) == 0) + fcntl(fd, F_SETFD, n | FD_CLOEXEC); + + /* + * Initialize logging and exit handler. + */ + msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY); + + /* + * If started from a terminal, get rid of any tty association. This also + * means that all errors and warnings must go to the syslog daemon. + */ + for (fd = 0; fd < 3; fd++) { + (void) close(fd); + if (open("/dev/null", O_RDWR, 0) != fd) + msg_fatal("open /dev/null: %m"); + } + setsid(); + + /* + * Make some room for plumbing with file descriptors. XXX This breaks + * when a service listens on many ports. In order to do this right we + * must change the master-child interface so that descriptors do not need + * to have fixed numbers. + */ + for (n = 0; n < 3; n++) { + if (close_on_exec(dup(0), CLOSE_ON_EXEC) < 0) + msg_fatal("dup(0): %m"); + } + + /* + * Process JCL. + */ + while ((ch = GETOPT(argc, argv, "c:Dtv")) > 0) { + switch (ch) { + case 'c': + if (setenv(CONF_ENV_PATH, optarg, 1) < 0) + msg_fatal("out of memory"); + break; + case 'D': + debug_me = 1; + break; + case 't': + test_lock = 1; + break; + case 'v': + msg_verbose++; + break; + default: + msg_fatal("usage: %s [-D] [-t] [-v]", argv[0]); + /* NOTREACHED */ + } + } + + /* + * Final initializations. Unfortunately, we must read the global Postfix + * configuration file after doing command-line processing, so that we get + * consistent results when we SIGHUP the server to reload configuration + * files. + */ + master_vars_init(); + if ((inherited_limit = get_file_limit()) < (off_t) var_message_limit) { + msg_warn("file size limit %lu < message_size_limit %lu -- reset", + (unsigned long) inherited_limit, (unsigned long) var_message_limit); + set_file_limit(var_message_limit); + } + if (chdir(var_queue_dir)) + msg_fatal("chdir %s: %m", var_queue_dir); + + /* + * Lock down the master.pid file. In test mode, no file means that it + * isn't locked. + */ + lock_path = vstring_alloc(10); + why = vstring_alloc(10); + + vstring_sprintf(lock_path, "%s/%s.pid", DEF_PID_DIR, var_procname); + if (test_lock && access(vstring_str(lock_path), F_OK) < 0) + exit(0); + lock_fp = open_lock(vstring_str(lock_path), O_RDWR | O_CREAT, 0644, why); + if (test_lock) + exit(lock_fp ? 0 : 1); + if (lock_fp == 0) + msg_fatal("%s", vstring_str(why)); + vstream_fprintf(lock_fp, "%*lu\n", sizeof(unsigned long) * 4, + (unsigned long) var_pid); + if (vstream_fflush(lock_fp)) + msg_fatal("cannot update lock file %s: %m", vstring_str(lock_path)); + close_on_exec(vstream_fileno(lock_fp), CLOSE_ON_EXEC); + + vstring_free(why); + vstring_free(lock_path); + + /* + * Optionally start the debugger on ourself. + */ + if (debug_me) + debug_process(); + + /* + * Finish initialization, last part. We must process configuration files + * after processing command-line parameters, so that we get consistent + * results when we SIGHUP the server to reload configuration files. + */ + master_config(); + master_sigsetup(); + msg_info("daemon started"); + + /* + * Process events. The event handler will execute the read/write/timer + * action routines. Whenever something has happened, see if we received + * any signal in the mean time. Although the master process appears to do + * multiple things at the same time, it really is all a single thread, so + * that there are no concurrency conflicts within the master process. + */ + for (;;) { + event_loop(-1); + if (master_gotsighup) { + msg_info("reload configuration"); + master_gotsighup = 0; /* this first */ + master_vars_init(); /* then this */ + master_refresh(); /* then this */ + } + if (master_gotsigchld) { + if (msg_verbose) + msg_info("got sigchld"); + master_gotsigchld = 0; /* this first */ + master_reap_child(); /* then this */ + } + } +} diff --git a/postfix/master/master.h b/postfix/master/master.h new file mode 100644 index 000000000..6ca860bd2 --- /dev/null +++ b/postfix/master/master.h @@ -0,0 +1,172 @@ +/*++ +/* NAME +/* master 3h +/* SUMMARY +/* Postfix master - data structures and prototypes +/* SYNOPSIS +/* #include "master.h" +/* DESCRIPTION +/* .nf + + /* + * Server processes that provide the same service share a common "listen" + * socket to accept connection requests, and share a common pipe to the + * master process to send status reports. Server processes die voluntarily + * when idle for a configurable amount of time, or after servicing a + * configurable number of requests; the master process spawns new processes + * on demand up to a configurable concurrency limit and/or periodically. + */ +typedef struct MASTER_SERV { + int flags; /* status, features, etc. */ + char *name; /* service endpoint name */ + int type; /* UNIX-domain, INET, etc. */ + int wakeup_time; /* wakeup interval */ + int *listen_fd; /* incoming requests */ + int listen_fd_count; /* nr of descriptors */ + union { + struct INET_ADDR_LIST *inet; + } addr_list; + int max_proc; /* upper bound on # processes */ + char *path; /* command pathname */ + struct ARGV *args; /* argument vector */ + int avail_proc; /* idle processes */ + int total_proc; /* number of processes */ + int throttle_delay; /* failure recovery parameter */ + int status_fd[2]; /* child status reports */ + struct BINHASH *children; /* linkage */ + struct MASTER_SERV *next; /* linkage */ +} MASTER_SERV; + + /* + * Per-service flag bits. We assume trouble when a child process terminates + * before completing its first request: either the program is defective, + * some configuration is wrong, or the system is out of resources. + */ +#define MASTER_FLAG_THROTTLE (1<<0) /* we're having trouble */ +#define MASTER_FLAG_MARK (1<<1) /* garbage collection support */ + +#define MASTER_THROTTLED(f) ((f)->flags & MASTER_FLAG_THROTTLE) + +#define MASTER_LIMIT_OK(limit, count) ((limit) == 0 || ((count) < (limit))) + + /* + * Service types. + */ +#define MASTER_SERV_TYPE_UNIX 1 /* AF_UNIX domain socket */ +#define MASTER_SERV_TYPE_INET 2 /* AF_INET domain socket */ +#define MASTER_SERV_TYPE_FIFO 3 /* fifo (named pipe) */ + + /* + * Default process management policy values. This is only the bare minimum. + * Most policy management is delegated to child processes. The process + * 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. + */ +typedef int MASTER_PID; /* pid is key into binhash table */ + +typedef struct MASTER_PROC { + MASTER_PID pid; /* child process id */ + int avail; /* availability */ + MASTER_SERV *serv; /* parent linkage */ + int use_count; /* number of service requests */ +} MASTER_PROC; + + /* + * Other manifest constants. + */ +#define MASTER_BUF_LEN 2048 /* logical config line length */ + + /* + * master_ent.c + */ +extern void fset_master_ent(char *); +extern void set_master_ent(void); +extern void end_master_ent(void); +extern void print_master_ent(MASTER_SERV *); +extern MASTER_SERV *get_master_ent(void); +extern void free_master_ent(MASTER_SERV *); + + /* + * master_conf.c + */ +extern void master_config(void); +extern void master_refresh(void); + + /* + * master_vars.c + */ +extern char *var_program_dir; +extern int var_proc_limit; +extern int var_use_limit; +extern int var_idle_limit; +extern void master_vars_init(void); + + /* + * master_tab.c + */ +extern MASTER_SERV *master_head; +extern void master_start_service(MASTER_SERV *); +extern void master_stop_service(MASTER_SERV *); +extern void master_restart_service(MASTER_SERV *); + + /* + * master_events.c + */ +extern int master_gotsighup; +extern int master_gotsigchld; +extern void master_sigsetup(void); + + /* + * master_status.c + */ +extern void master_status_init(MASTER_SERV *); +extern void master_status_cleanup(MASTER_SERV *); + + /* + * master_wakeup.c + */ +extern void master_wakeup_init(MASTER_SERV *); +extern void master_wakeup_cleanup(MASTER_SERV *); + + + /* + * master_listen.c + */ +extern void master_listen_init(MASTER_SERV *); +extern void master_listen_cleanup(MASTER_SERV *); + + /* + * master_avail.c + */ +extern void master_avail_listen(MASTER_SERV *); +extern void master_avail_cleanup(MASTER_SERV *); +extern void master_avail_more(MASTER_SERV *, MASTER_PROC *); +extern void master_avail_less(MASTER_SERV *, MASTER_PROC *); + + /* + * master_spawn.c + */ +extern struct BINHASH *master_child_table; +extern void master_spawn(MASTER_SERV *); +extern void master_reap_child(void); +extern void master_delete_children(MASTER_SERV *); + +/* DIAGNOSTICS +/* BUGS +/* SEE ALSO +/* 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 +/*--*/ diff --git a/postfix/master/master_avail.c b/postfix/master/master_avail.c new file mode 100644 index 000000000..e5507ef6d --- /dev/null +++ b/postfix/master/master_avail.c @@ -0,0 +1,172 @@ +/*++ +/* NAME +/* master_avail 3 +/* SUMMARY +/* Postfix master - process creation policy +/* SYNOPSIS +/* #include "master.h" +/* +/* void master_avail_listen(serv) +/* MASTER_SERV *serv; +/* +/* void master_avail_cleanup(serv) +/* MASTER_SERV *serv; +/* +/* void master_avail_more(serv, proc) +/* MASTER_SERV *serv; +/* MASTER_PROC *proc; +/* +/* void master_avail_less(serv, proc) +/* MASTER_SERV *serv; +/* MASTER_PROC *proc; +/* DESCRIPTION +/* This module implements the process creation policy. As long as +/* the allowed number of processes for the given service is not +/* exceeded, a connection request is either handled by an existing +/* available process, or this module causes a new process to be +/* created to service the request. +/* +/* master_avail_listen() ensures that someone monitors the service's +/* listen socket for connection requests (as long as resources +/* to handle connection requests are available). This function may +/* be called at random. When the maximum number of servers is running, +/* connection requests are left in the system queue. +/* +/* master_avail_cleanup() should be called when the named service +/* is taken out of operation. It terminates child processes by +/* sending SIGTERM. +/* +/* master_avail_more() should be called when the named process +/* has become available for servicing new connection requests. +/* +/* master_avail_less() should be called when the named process +/* has become unavailable for servicing new connection requests. +/* DIAGNOSTICS +/* Panic: internal inconsistencies. +/* BUGS +/* SEE ALSO +/* master_spawn(3), child process birth and death +/* 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 libraries. */ + +#include + +/* Utility library. */ + +#include +#include + +/* Application-specific. */ + +#include "master_proto.h" +#include "master.h" + +/* master_avail_event - create child process to handle connection request */ + +static void master_avail_event(int event, char *context) +{ + MASTER_SERV *serv = (MASTER_SERV *) context; + int n; + + if (event == 0) /* XXX Can this happen? */ + return; + if (MASTER_THROTTLED(serv)) { /* XXX interface botch */ + for (n = 0; n < serv->listen_fd_count; n++) + event_disable_readwrite(serv->listen_fd[n]); + } else { + master_spawn(serv); + } +} + +/* master_avail_listen - make sure that someone monitors the listen socket */ + +void master_avail_listen(MASTER_SERV *serv) +{ + char *myname = "master_avail_listen"; + int n; + + /* + * When no-one else is monitoring the service's listen socket, start + * monitoring the socket for connection requests. All this under the + * restriction that we have sufficient resources to service a connection + * request. + */ + if (msg_verbose) + msg_info("%s: avail %d total %d max %d", myname, + serv->avail_proc, serv->total_proc, serv->max_proc); + if (serv->avail_proc < 1 + && MASTER_LIMIT_OK(serv->max_proc, serv->total_proc) + && !MASTER_THROTTLED(serv)) { + if (msg_verbose) + msg_info("%s: enable events %s", myname, serv->name); + for (n = 0; n < serv->listen_fd_count; n++) + event_enable_read(serv->listen_fd[n], master_avail_event, + (char *) serv); + } +} + +/* master_avail_cleanup - cleanup */ + +void master_avail_cleanup(MASTER_SERV *serv) +{ + int n; + + master_delete_children(serv); /* XXX calls + * master_avail_listen */ + for (n = 0; n < serv->listen_fd_count; n++) + event_disable_readwrite(serv->listen_fd[n]); /* XXX must be last */ +} + +/* master_avail_more - one more available child process */ + +void master_avail_more(MASTER_SERV *serv, MASTER_PROC *proc) +{ + char *myname = "master_avail_more"; + int n; + + /* + * This child process has become available for servicing connection + * requests, so we can stop monitoring the service's listen socket. The + * child will do it for us. + */ + if (msg_verbose) + msg_info("%s: pid %d (%s)", myname, proc->pid, proc->serv->name); + if (proc->avail == MASTER_STAT_AVAIL) + msg_panic("%s: process already available", myname); + serv->avail_proc++; + proc->avail = MASTER_STAT_AVAIL; + if (msg_verbose) + msg_info("%s: disable events %s", myname, serv->name); + for (n = 0; n < serv->listen_fd_count; n++) + event_disable_readwrite(serv->listen_fd[n]); +} + +/* master_avail_less - one less available child process */ + +void master_avail_less(MASTER_SERV *serv, MASTER_PROC *proc) +{ + char *myname = "master_avail_less"; + + /* + * This child is no longer available for servicing connection requests. + * When no child processes are available, start monitoring the service's + * listen socket for new connection requests. + */ + if (msg_verbose) + msg_info("%s: pid %d (%s)", myname, proc->pid, proc->serv->name); + if (proc->avail != MASTER_STAT_AVAIL) + msg_panic("%s: process not available", myname); + serv->avail_proc--; + proc->avail = MASTER_STAT_TAKEN; + master_avail_listen(serv); +} diff --git a/postfix/master/master_conf.c b/postfix/master/master_conf.c new file mode 100644 index 000000000..083295531 --- /dev/null +++ b/postfix/master/master_conf.c @@ -0,0 +1,136 @@ +/*++ +/* NAME +/* master_conf 3 +/* SUMMARY +/* Postfix master - master.cf file processing +/* SYNOPSIS +/* #include "master.h" +/* +/* void master_config(serv) +/* MASTER_SERV *serv; +/* +/* void master_refresh(serv) +/* MASTER_SERV *serv; +/* DESCRIPTION +/* Use master_config() to read the master.cf configuration file +/* during program initialization. +/* +/* Use master_refresh() to re-read the master.cf configuration file +/* when the process is already running. +/* DIAGNOSTICS +/* BUGS +/* SEE ALSO +/* master_ent(3), configuration file programmatic interface. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System libraries. */ + +#include +#include + +/* Utility library. */ + +#include +#include + +/* Application-specific. */ + +#include "master.h" + +/* master_refresh - re-read configuration table */ + +void master_refresh(void) +{ + MASTER_SERV *serv; + MASTER_SERV **servp; + + /* + * Mark all existing services. + */ + for (serv = master_head; serv != 0; serv = serv->next) + serv->flags |= MASTER_FLAG_MARK; + + /* + * Read the master.cf configuration file. The master_conf() routine + * unmarks services upon update. New services are born with the mark bit + * off. After this, anything with the mark bit on should be removed. + */ + master_config(); + + /* + * Delete all services that are still marked - they disappeared from the + * configuration file and are therefore no longer needed. + */ + for (servp = &master_head; (serv = *servp) != 0; /* void */ ) { + if ((serv->flags & MASTER_FLAG_MARK) != 0) { + *servp = serv->next; + master_stop_service(serv); + free_master_ent(serv); + } else { + servp = &serv->next; + } + } +} + +/* master_config - read config file */ + +void master_config(void) +{ + MASTER_SERV *entry; + MASTER_SERV *serv; + +#define STR_DIFF strcmp +#define STR_SAME !strcmp +#define SWAP(type,a,b) { type temp = a; a = b; b = temp; } + + /* + * A service is identified by its endpoint name AND by its transport + * type, not just by its name alone. The name is unique within its + * transport type. XXX Service privacy is encoded in the service name. + */ + set_master_ent(); + while ((entry = get_master_ent()) != 0) { + if (msg_verbose) + print_master_ent(entry); + for (serv = master_head; serv != 0; serv = serv->next) + if (STR_SAME(serv->name, entry->name) && serv->type == entry->type) + break; + + /* + * Add a new service entry. We do not really care in what order the + * service entries are kept in memory. + */ + if (serv == 0) { + entry->next = master_head; + master_head = entry; + master_start_service(entry); + } + + /* + * Update an existing service entry. Make the current generation of + * child processes commit suicide whenever it is convenient. The next + * generation of child processes will run with the new configuration + * settings. + */ + else { + serv->flags &= ~MASTER_FLAG_MARK; + serv->wakeup_time = entry->wakeup_time; + serv->max_proc = entry->max_proc; + serv->throttle_delay = entry->throttle_delay; + SWAP(char *, serv->path, entry->path); + SWAP(ARGV *, serv->args, entry->args); + master_restart_service(serv); + free_master_ent(entry); + } + } + end_master_ent(); +} diff --git a/postfix/master/master_ent.c b/postfix/master/master_ent.c new file mode 100644 index 000000000..e0bde144a --- /dev/null +++ b/postfix/master/master_ent.c @@ -0,0 +1,448 @@ +/*++ +/* NAME +/* master_ent 3 +/* SUMMARY +/* Postfix master - config file access +/* SYNOPSIS +/* #include "master.h" +/* +/* void fset_master_ent(path) +/* char *path; +/* +/* void set_master_ent() +/* +/* MASTER_SERV *get_master_ent() +/* +/* void end_master_ent() +/* +/* void print_master_ent(entry) +/* MASTER_SERV *entry; +/* +/* void free_master_ent(entry) +/* MASTER_SERV *entry; +/* DESCRIPTION +/* This module implements a simple programmatic interface +/* for accessing Postfix master process configuration files. +/* +/* fset_master_ent() specifies the location of the master process +/* configuration file. The pathname is copied. +/* +/* set_master_ent() opens the configuration file. It is an error +/* to call this routine while the configuration file is still open. +/* It is an error to open a configuration file without specifying +/* its name to fset_master_ent(). +/* +/* get_master_ent() reads the next entry from an open configuration +/* file and returns the parsed result. A null result means the end +/* of file was reached. +/* +/* print_master_ent() prints the specified service entry. +/* +/* end_master_ent() closes an open configuration file. It is an error +/* to call this routine when the configuration file is not open. +/* +/* free_master_ent() destroys the memory used for a parsed configuration +/* file entry. +/* DIAGNOSTICS +/* Panics: interface violations. Fatal errors: memory allocation +/* failure. +/* BUGS +/* SEE ALSO +/* 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 libraries. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility libraries. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include + +/* Local stuff. */ + +#include "master_proto.h" +#include "master.h" + +static char *master_path; /* config file name */ +static VSTREAM *master_fp; /* config file pointer */ +static int master_line; /* config file line number */ + +static char master_blanks[] = " \t\r\n";/* field delimiters */ + +static NORETURN fatal_invalid_field(char *, char *); +static NORETURN fatal_with_context(char *,...); + +/* fset_master_ent - specify configuration file pathname */ + +void fset_master_ent(char *path) +{ + if (master_path != 0) + myfree(master_path); + master_path = mystrdup(path); +} + +/* set_master_ent - open configuration file */ + +void set_master_ent() +{ + char *myname = "set_master_ent"; + + if (master_fp != 0) + msg_panic("%s: configuration file still open", myname); + if (master_path == 0) + msg_panic("%s: no configuration file specified", myname); + if ((master_fp = vstream_fopen(master_path, O_RDONLY, 0)) == 0) + msg_fatal("open %s: %m", master_path); + master_line = 0; +} + +/* end_master_ent - close configuration file */ + +void end_master_ent() +{ + char *myname = "end_master_ent"; + + if (master_fp == 0) + msg_panic("%s: configuration file not open", myname); + if (vstream_fclose(master_fp) != 0) + msg_fatal("%s: close configuration file: %m", myname); + master_fp = 0; +} + +/* fatal_with_context - print fatal error with file/line context */ + +static NORETURN fatal_with_context(char *format,...) +{ + char *myname = "fatal_with_context"; + VSTRING *vp = vstring_alloc(100); + va_list ap; + + if (master_path == 0) + msg_panic("%s: no configuration file specified", myname); + + va_start(ap, format); + vstring_vsprintf(vp, format, ap); + va_end(ap); + msg_fatal("%s: line %d: %s", master_path, master_line, vstring_str(vp)); +} + +/* fatal_invalid_field - report invalid field value */ + +static NORETURN fatal_invalid_field(char *name, char *value) +{ + fatal_with_context("field \"%s\": bad value: \"%s\"", name, value); +} + +/* get_str_ent - extract string field */ + +static char *get_str_ent(char **bufp, char *name, char *def_val) +{ + char *value; + + if ((value = mystrtok(bufp, master_blanks)) == 0) + fatal_with_context("missing \"%s\" field", name); + if (strcmp(value, "-") == 0) { + if (def_val == 0) + fatal_with_context("field \"%s\" has no default value", name); + return (def_val); + } else { + return (value); + } +} + +/* get_bool_ent - extract boolean field */ + +static int get_bool_ent(char **bufp, char *name, char *def_val) +{ + char *value; + + value = get_str_ent(bufp, name, def_val); + if (strcmp("y", value) == 0) { + return (1); + } else if (strcmp("n", value) == 0) { + return (0); + } else { + fatal_invalid_field(name, value); + } + /* NOTREACHED */ +} + +/* get_int_ent - extract integer field */ + +static int get_int_ent(char **bufp, char *name, char *def_val, int min_val) +{ + char *value; + int n; + + value = get_str_ent(bufp, name, def_val); + if (!ISDIGIT(*value) || (n = atoi(value)) < min_val) + fatal_invalid_field(name, value); + return (n); +} + +/* get_master_ent - read entry from configuration file */ + +MASTER_SERV *get_master_ent() +{ + VSTRING *buf = vstring_alloc(100); + VSTRING *junk = vstring_alloc(100); + MASTER_SERV *serv; + char *cp; + char *name; + char *transport; + int private; + int unprivileged; /* passed on to child */ + int chroot; /* passed on to child */ + char *command; + int n; + char *bufp; + + if (master_fp == 0) + msg_panic("get_master_ent: config file not open"); + + /* + * Skip blank lines and comment lines. + */ + do { + if (readline(buf, master_fp, &master_line) == 0) { + vstring_free(buf); + vstring_free(junk); + return (0); + } + bufp = vstring_str(buf); + } while ((cp = mystrtok(&bufp, master_blanks)) == 0 || *cp == '#'); + + /* + * Parse one logical line from the configuration file. Initialize service + * structure members in order. + */ + serv = (MASTER_SERV *) mymalloc(sizeof(MASTER_SERV)); + serv->next = 0; + + /* + * Flags member. + */ + serv->flags = 0; + + /* + * Service name. Syntax is transport-specific. + */ + name = cp; + + /* + * Transport type: inet (wild-card listen or virtual) or unix. + */ +#define STR_SAME !strcmp + + transport = get_str_ent(&bufp, "transport type", (char *) 0); + if (STR_SAME(transport, MASTER_XPORT_NAME_INET)) { + serv->type = MASTER_SERV_TYPE_INET; + if (strcasecmp(var_inet_interfaces, DEF_INET_INTERFACES) == 0) { + serv->addr_list.inet = 0; /* wild-card */ + serv->listen_fd_count = 1; + } else { + serv->addr_list.inet = own_inet_addr_list(); /* virtual */ + serv->listen_fd_count = serv->addr_list.inet->used; + } + } else if (STR_SAME(transport, MASTER_XPORT_NAME_UNIX)) { + serv->type = MASTER_SERV_TYPE_UNIX; + serv->listen_fd_count = 1; + } else if (STR_SAME(transport, MASTER_XPORT_NAME_FIFO)) { + serv->type = MASTER_SERV_TYPE_FIFO; + serv->listen_fd_count = 1; + } else { + fatal_with_context("bad transport type: %s", transport); + } + + /* + * Service class: public or private. + */ + private = get_bool_ent(&bufp, "private", "y"); + + /* + * Derive an internal service name. The name may depend on service + * attributes such as privacy. + */ + if (serv->type == MASTER_SERV_TYPE_INET) { + if (private) + fatal_with_context("inet service cannot be private"); + serv->name = mystrdup(name); + } else if (serv->type == MASTER_SERV_TYPE_UNIX) { + serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE : + MAIL_CLASS_PUBLIC, name); + } else if (serv->type == MASTER_SERV_TYPE_FIFO) { + serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE : + MAIL_CLASS_PUBLIC, name); + } else { + msg_panic("bad transport type: %d", serv->type); + } + + /* + * Listen socket(s). XXX We pre-allocate storage because the number of + * sockets is frozen anyway once we build the command-line vector below. + */ + serv->listen_fd = (int *) mymalloc(sizeof(int) * serv->listen_fd_count); + for (n = 0; n < serv->listen_fd_count; n++) + serv->listen_fd[n] = -1; + + /* + * Privilege level. Default is to restrict process privileges to those of + * the mail owner. + */ + unprivileged = get_bool_ent(&bufp, "unprivileged", "y"); + + /* + * Chroot. Default is to restrict file system access to the mail queue. + * XXX Chroot cannot imply unprivileged service (for example, the pickup + * service runs chrooted but needs privileges to open files as the user). + */ + chroot = get_bool_ent(&bufp, "chroot", "y"); + + /* + * Wakeup timer. XXX should we require that var_proc_limit == 1? Right + * now, the only services that have a wakeup timer also happen to be the + * services that have at most one running instance: local pickup and + * local delivery. + */ + serv->wakeup_time = get_int_ent(&bufp, "wakeup_time", "0", 0); + + /* + * Concurrency limit. Zero means no limit. + */ + vstring_sprintf(junk, "%d", var_proc_limit); + serv->max_proc = get_int_ent(&bufp, "max_proc", vstring_str(junk), 0); + + /* + * Path to command, + */ + command = get_str_ent(&bufp, "command", (char *) 0); + serv->path = concatenate(var_daemon_dir, "/", command, (char *) 0); + + /* + * Idle and total process count. + */ + serv->avail_proc = 0; + serv->total_proc = 0; + + /* + * Backoff time in case a service is broken. + */ + serv->throttle_delay = MASTER_DEF_THROTTLE_DELAY; /* XXX config */ + + /* + * Shared channel for child status updates. + */ + serv->status_fd[0] = serv->status_fd[1] = -1; + + /* + * Child process structures. + */ + serv->children = 0; + + /* + * Command-line vector. Add "-n service_name" when the process name + * basename differs from the service name. Always add the transport. + */ + serv->args = argv_alloc(0); + argv_add(serv->args, command, (char *) 0); + if (strcmp(basename(command), name) != 0) + argv_add(serv->args, "-n", name, (char *) 0); + argv_add(serv->args, "-t", transport, (char *) 0); + if (msg_verbose) + argv_add(serv->args, "-v", (char *) 0); + if (unprivileged) + argv_add(serv->args, "-u", (char *) 0); + if (chroot) + argv_add(serv->args, "-c", (char *) 0); + if (serv->listen_fd_count > 1) + argv_add(serv->args, "-s", + vstring_str(vstring_sprintf(junk, "%d", serv->listen_fd_count)), + (char *) 0); + while ((cp = mystrtok(&bufp, master_blanks)) != 0) + argv_add(serv->args, cp, (char *) 0); + argv_terminate(serv->args); + + /* + * Cleanup. + */ + vstring_free(buf); + vstring_free(junk); + return (serv); +} + +/* print_master_ent - show service entry contents */ + +void print_master_ent(MASTER_SERV *serv) +{ + char **cpp; + + msg_info("====start service entry"); + msg_info("flags: %d", serv->flags); + msg_info("name: %s", serv->name); + msg_info("type: %s", + serv->type == MASTER_SERV_TYPE_UNIX ? MASTER_XPORT_NAME_UNIX : + serv->type == MASTER_SERV_TYPE_FIFO ? MASTER_XPORT_NAME_FIFO : + serv->type == MASTER_SERV_TYPE_INET ? MASTER_XPORT_NAME_INET : + "unknown transport type"); + msg_info("listen_fd_count: %d", serv->listen_fd_count); + msg_info("wakeup: %d", serv->wakeup_time); + msg_info("max_proc: %d", serv->max_proc); + msg_info("path: %s", serv->path); + for (cpp = serv->args->argv; *cpp; cpp++) + msg_info("arg[%d]: %s", (int) (cpp - serv->args->argv), *cpp); + msg_info("avail_proc: %d", serv->avail_proc); + msg_info("total_proc: %d", serv->total_proc); + msg_info("throttle_delay: %d", serv->throttle_delay); + msg_info("status_fd %d %d", serv->status_fd[0], serv->status_fd[1]); + msg_info("children: 0x%lx", (long) serv->children); + msg_info("next: 0x%lx", (long) serv->next); + msg_info("====end service entry"); +} + +/* free_master_ent - destroy process entry */ + +void free_master_ent(MASTER_SERV *serv) +{ + + /* + * Undo what get_master_ent() created. + */ + myfree(serv->name); + myfree(serv->path); + argv_free(serv->args); + myfree((char *) serv->listen_fd); + myfree((char *) serv); +} diff --git a/postfix/master/master_listen.c b/postfix/master/master_listen.c new file mode 100644 index 000000000..14b8ed5f4 --- /dev/null +++ b/postfix/master/master_listen.c @@ -0,0 +1,151 @@ +/*++ +/* NAME +/* master_listen 3 +/* SUMMARY +/* Postfix master - start/stop listeners +/* SYNOPSIS +/* #include "master.h" +/* +/* void master_listen_init(serv) +/* MASTER_SERV *serv; +/* +/* void master_listen_cleanup(serv) +/* MASTER_SERV *serv; +/* DESCRIPTION +/* master_listen_init() turns on the listener implemented by the +/* named process. FIFOs and UNIX-domain sockets are created with +/* mode 0622 and with ownership mail_owner. +/* +/* master_listen_cleanup() turns off the listener implemented by the +/* named process. +/* DIAGNOSTICS +/* BUGS +/* SEE ALSO +/* inet_listen(3), internet-domain listener +/* unix_listen(3), unix-domain listener +/* fifo_listen(3), named-pipe listener +/* set_eugid(3), set effective user/group attributes +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include + +/* Application-specific. */ + +#include "master.h" + +/* master_listen_init - enable connection requests */ + +void master_listen_init(MASTER_SERV *serv) +{ + char *myname = "master_listen_init"; + char *end_point; + int n; + + /* + * Find out what transport we should use, then create one or more + * listener sockets. Make the listener sockets non-blocking, so that + * child processes don't block in accept() when multiple processes are + * selecting on the same socket and only one of them gets the connection. + */ + switch (serv->type) { + + /* + * UNIX-domain listener endpoints always come as singlets. + */ + case MASTER_SERV_TYPE_UNIX: + set_eugid(var_owner_uid, var_owner_gid); + serv->listen_fd[0] = + unix_listen(serv->name, serv->max_proc > var_proc_limit ? + serv->max_proc : var_proc_limit, NON_BLOCKING); + close_on_exec(serv->listen_fd[0], CLOSE_ON_EXEC); + set_eugid(getuid(), getgid()); + break; + + /* + * FIFO listener endpoints always come as singlets. + */ + case MASTER_SERV_TYPE_FIFO: + set_eugid(var_owner_uid, var_owner_gid); + serv->listen_fd[0] = fifo_listen(serv->name, 0622, NON_BLOCKING); + close_on_exec(serv->listen_fd[0], CLOSE_ON_EXEC); + set_eugid(getuid(), getgid()); + break; + + /* + * INET-domain listener endpoints can be wildcarded (the default) or + * bound to specific interface addresses. + */ + case MASTER_SERV_TYPE_INET: + if (serv->addr_list.inet == 0) { /* wild-card */ + serv->listen_fd[0] = + inet_listen(serv->name, serv->max_proc > var_proc_limit ? + serv->max_proc : var_proc_limit, NON_BLOCKING); + close_on_exec(serv->listen_fd[0], CLOSE_ON_EXEC); + } else { /* virtual */ + for (n = 0; n < serv->listen_fd_count; n++) { + end_point = concatenate(inet_ntoa(serv->addr_list.inet->addrs[n]), + ":", serv->name, (char *) 0); + serv->listen_fd[n] + = inet_listen(end_point, serv->max_proc > var_proc_limit ? + serv->max_proc : var_proc_limit, NON_BLOCKING); + close_on_exec(serv->listen_fd[n], CLOSE_ON_EXEC); + myfree(end_point); + } + } + break; + default: + msg_panic("%s: unknown service type: %d", myname, serv->type); + } +} + +/* master_listen_cleanup - disable connection requests */ + +void master_listen_cleanup(MASTER_SERV *serv) +{ + char *myname = "master_listen_cleanup"; + int n; + + /* + * XXX The listen socket is shared with child processes. Closing the + * socket in the master process does not really disable listeners in + * child processes. There seems to be no documented way to turn off a + * listener. The 4.4BSD shutdown(2) man page promises an ENOTCONN error + * when shutdown(2) is applied to a socket that is not connected. + */ + for (n = 0; n < serv->listen_fd_count; n++) { + if (close(serv->listen_fd[n]) < 0) + msg_warn("%s: close listener socket %d: %m", + myname, serv->listen_fd[n]); + serv->listen_fd[n] = -1; + } +} diff --git a/postfix/master/master_proto.c b/postfix/master/master_proto.c new file mode 100644 index 000000000..6494b15f2 --- /dev/null +++ b/postfix/master/master_proto.c @@ -0,0 +1,87 @@ +/*++ +/* NAME +/* master_proto 3 +/* SUMMARY +/* Postfix master - status notification protocol +/* SYNOPSIS +/* #include +/* +/* int master_notify(pid, status) +/* int pid; +/* int status; +/* DESCRIPTION +/* The master process provides a standard environment for its +/* child processes. Part of this environment is a pair of file +/* descriptors that the master process shares with all child +/* processes that provide the same service. +/* .IP MASTER_LISTEN_FD +/* The shared file descriptor for accepting client connection +/* requests. The master process listens on this socket or FIFO +/* when all child processes are busy. +/* .IP MASTER_STATUS_FD +/* The shared file descriptor for sending child status updates to +/* the master process. +/* .PP +/* A child process uses master_notify() to send a status notification +/* message to the master process. +/* .IP MASTER_STAT_AVAIL +/* The child process is ready to accept client connections. +/* .IP MASTER_STAT_TAKEN +/* Until further notice, the child process is unavailable for +/* accepting client connections. +/* .PP +/* When a child process terminates without sending a status update, +/* the master process will figure out that the child is no longer +/* available. +/* DIAGNOSTICS +/* The result is -1 in case of problems. This usually means that +/* the parent disconnected after a reload request, in order to +/* force children to commit suicide. +/* 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 "master_proto.h" + +int master_notify(int pid, int status) +{ + char *myname = "master_notify"; + MASTER_STATUS stat; + + /* + * We use a simple binary protocol to minimize security risks. Since this + * is local IPC, there are no byte order or word length issues. The + * server treats this information as gossip, so sending a bad PID or a + * bad status code will only have amusement value. + */ + stat.pid = pid; + stat.avail = status; + + if (write(MASTER_STATUS_FD, (char *) &stat, sizeof(stat)) != sizeof(stat)) { + if (msg_verbose) + msg_info("%s: status %d: %m", myname, status); + return (-1); + } else { + if (msg_verbose) + msg_info("%s: status %d", myname, status); + return (0); + } +} diff --git a/postfix/master/master_proto.h b/postfix/master/master_proto.h new file mode 100644 index 000000000..2929ed463 --- /dev/null +++ b/postfix/master/master_proto.h @@ -0,0 +1,57 @@ +/*++ +/* NAME +/* master_proto 3h +/* SUMMARY +/* master process protocol +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Transport names. The master passes the transport name on the command + * line, and thus the name is part of the master to child protocol. + */ +#define MASTER_XPORT_NAME_UNIX "unix" /* local IPC */ +#define MASTER_XPORT_NAME_FIFO "fifo" /* local IPC */ +#define MASTER_XPORT_NAME_INET "inet" /* non-local IPC */ + + /* + * Format of a status message sent by a child process to the process + * manager. Since this is between processes on the same machine we need not + * worry about byte order and word length. + */ +typedef struct MASTER_STATUS { + int pid; /* process ID */ + int avail; /* availability */ +} MASTER_STATUS; + +#define MASTER_STAT_TAKEN 0 /* this one is occupied */ +#define MASTER_STAT_AVAIL 1 /* this process is idle */ + +extern int master_notify(int, int); /* encapsulate status msg */ + + /* + * File descriptors inherited from the master process. All processes that + * provide a given service share the same status file descriptor, and listen + * on the same service socket(s). The kernel decides what process gets the + * next connection. Usually the number of listening processes is small, so + * one connection will not cause a "thundering herd" effect. When no process + * listens on a given socket, the master process will. MASTER_LISTEN_FD is + * actually the lowest-numbered descriptor of a sequence of descriptors to + * listen on. + */ +#define MASTER_STATUS_FD 3 /* shared channel to parent */ +#define MASTER_LISTEN_FD 4 /* accept connections here */ + +/* 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 +/*--*/ + diff --git a/postfix/master/master_service.c b/postfix/master/master_service.c new file mode 100644 index 000000000..62289b308 --- /dev/null +++ b/postfix/master/master_service.c @@ -0,0 +1,104 @@ +/*++ +/* NAME +/* master_service 3 +/* SUMMARY +/* Postfix master - start/stop services +/* SYNOPSIS +/* #include "master.h" +/* +/* void master_start_service(serv) +/* MASTER_SERV *serv; +/* +/* void master_stop_service(serv) +/* MASTER_SERV *serv; +/* +/* void master_restart_service(serv) +/* MASTER_SERV *serv; +/* DESCRIPTION +/* master_start_service() enables the named service. +/* +/* master_stop_service() disables named service. +/* +/* master_restart_service() requests all running child processes to +/* commit suicide. This is typically used after a configuration reload. +/* DIAGNOSTICS +/* BUGS +/* SEE ALSO +/* master_avail(3), process creation policy +/* master_wakeup(3), service automatic wakeup +/* master_status(3), child status reports +/* master_listen(3), unix/inet listeners +/* 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 libraries. */ + +#include +#include +#include + +/* Utility library. */ + +#include +#include + +/* Application-specific. */ + +#include "master.h" + +MASTER_SERV *master_head; + +/* master_start_service - activate service */ + +void master_start_service(MASTER_SERV *serv) +{ + + /* + * Enable connection requests, wakeup timers, and status updates from + * child processes. + */ + master_listen_init(serv); + master_avail_listen(serv); + master_status_init(serv); + master_wakeup_init(serv); +} + +/* master_stop_service - deactivate service */ + +void master_stop_service(MASTER_SERV *serv) +{ + + /* + * Undo the things that master_start_service() did. + */ + master_wakeup_cleanup(serv); + master_status_cleanup(serv); + master_avail_cleanup(serv); + master_listen_cleanup(serv); +} + +/* master_restart_service - restart service after configuration reload */ + +void master_restart_service(MASTER_SERV *serv) +{ + + /* + * Undo some of the things that master_start_service() did. + */ + master_wakeup_cleanup(serv); + master_status_cleanup(serv); + + /* + * Now undo the undone. + */ + master_status_init(serv); + master_wakeup_init(serv); +} diff --git a/postfix/master/master_sig.c b/postfix/master/master_sig.c new file mode 100644 index 000000000..4931cb9c2 --- /dev/null +++ b/postfix/master/master_sig.c @@ -0,0 +1,187 @@ +/*++ +/* NAME +/* master_sig 3 +/* SUMMARY +/* Postfix master - signal processing +/* SYNOPSIS +/* #include "master.h" +/* +/* int master_gotsighup; +/* int master_gotsigchld; +/* +/* int master_sigsetup() +/* DESCRIPTION +/* This module implements the master process signal handling interface. +/* +/* master_gotsighup (master_gotsigchld) is set to SIGHUP (SIGCHLD) +/* when the process receives a hangup (child death) signal. +/* +/* master_sigsetup() enables processing of hangup and child death signals. +/* Receipt of SIGINT, SIGQUIT, SIGSEGV, SIGILL, or SIGTERM +/* is interpreted as a request for termination. Child processes are +/* notified of the master\'s demise by sending them a SIGTERM signal. +/* DIAGNOSTICS +/* BUGS +/* Need a way to register cleanup actions. +/* SEE ALSO +/* 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 libraries. */ + +#include +#include +#include + +/* Utility library. */ + +#include +#include + +/* Application-specific. */ + +#include "master.h" + +#ifdef USE_SIG_RETURN +#include +#endif + +/* Local stuff. */ + +int master_gotsigchld; +int master_gotsighup; + +/* master_sighup - register arrival of hangup signal */ + +static void master_sighup(int sig) +{ + + /* + * WARNING WARNING WARNING. + * + * This code runs at unpredictable moments, as a signal handler. Don't put + * any code here other than for setting a global flag. + */ + master_gotsighup = sig; +} + +/* master_sigchld - register arrival of child death signal */ + +#ifdef USE_SIG_RETURN + +static void master_sigchld(int sig, int code, struct sigcontext * scp) +{ + + /* + * WARNING WARNING WARNING. + * + * This code runs at unpredictable moments, as a signal handler. Don't put + * any code here other than for setting a global flag, or code that is + * intended to be run within a signal handler. + */ + master_gotsigchld = sig; + if (scp != NULL && scp->sc_syscall == SYS_select) { + scp->sc_syscall_action = SIG_RETURN; + } +} + +#else + +static void master_sigchld(int sig) +{ + + /* + * WARNING WARNING WARNING. + * + * This code runs at unpredictable moments, as a signal handler. Don't put + * any code here other than for setting a global flag. + */ + master_gotsigchld = sig; +} + +#endif + +/* master_sigdeath - die, women and children first */ + +static void master_sigdeath(int sig) +{ + char *myname = "master_sigsetup"; + struct sigaction action; + pid_t pid = getpid(); + + /* + * XXX We're running from a signal handler, and really should not call + * any msg() routines at all, but it would be even worse to silently + * terminate without informing the sysadmin. + */ + msg_info("terminating on signal %d", sig); + + /* + * Terminate all processes in our process group, except ourselves. + */ + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + action.sa_handler = SIG_IGN; + if (sigaction(SIGTERM, &action, (struct sigaction *) 0) < 0) + msg_fatal("%s: sigaction: %m", myname); + if (kill(-pid, SIGTERM) < 0) + msg_fatal("%s: kill process group: %m", myname); + + /* + * Deliver the signal to ourselves and clean up. XXX We're running as a + * signal handler and really should not be doing complicated things... + */ + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + action.sa_handler = SIG_DFL; + if (sigaction(sig, &action, (struct sigaction *) 0) < 0) + msg_fatal("%s: sigaction: %m", myname); + if (kill(pid, sig) < 0) + msg_fatal("%s: kill myself: %m", myname); +} + +/* master_sigsetup - set up signal handlers */ + +void master_sigsetup(void) +{ + char *myname = "master_sigsetup"; + struct sigaction action; + static int sigs[] = { + SIGINT, SIGQUIT, SIGSEGV, SIGILL, SIGTERM, + }; + unsigned i; + + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + + /* + * Prepare to kill our children when we receive any of the above signals. + */ + action.sa_handler = master_sigdeath; + for (i = 0; i < sizeof(sigs) / sizeof(sigs[0]); i++) + if (sigaction(sigs[i], &action, (struct sigaction *) 0) < 0) + msg_fatal("%s: sigaction(%d): %m", myname, sigs[i]); + + /* + * Intercept SIGHUP (re-read config file) and SIGCHLD (child exit). + */ +#ifdef SA_RESTART + action.sa_flags |= SA_RESTART; +#endif + action.sa_handler = master_sighup; + if (sigaction(SIGHUP, &action, (struct sigaction *) 0) < 0) + msg_fatal("%s: sigaction(%d): %m", myname, SIGHUP); + + action.sa_flags |= SA_NOCLDSTOP; + action.sa_handler = master_sigchld; + if (sigaction(SIGCHLD, &action, (struct sigaction *) 0) < 0) + msg_fatal("%s: sigaction(%d): %m", myname, SIGCHLD); +} diff --git a/postfix/master/master_spawn.c b/postfix/master/master_spawn.c new file mode 100644 index 000000000..a7bfe6e75 --- /dev/null +++ b/postfix/master/master_spawn.c @@ -0,0 +1,304 @@ +/*++ +/* NAME +/* master_spawn 3 +/* SUMMARY +/* Postfix master - child process birth and death +/* SYNOPSIS +/* #include "master.h" +/* +/* void master_spawn(serv) +/* MASTER_SERV *serv; +/* +/* void master_reap_child() +/* +/* void master_delete_children(serv) +/* MASTER_SERV *serv; +/* DESCRIPTION +/* This module creates and cleans up child processes, and applies +/* a process creation throttle in case of serious trouble. +/* This module is the working horse for the master_avail(3) process +/* creation policy module. +/* +/* master_spawn() spawns off a child process for the specified service, +/* making the child process available for servicing connection requests. +/* It is an error to call this function then the specified service is +/* throttled. +/* +/* master_reap_child() cleans up all dead child processes. One typically +/* runs this function at a convenient moment after receiving a SIGCHLD +/* signal. When a child process terminates abnormally after being used +/* for the first time, process creation for that service is throttled +/* for a configurable amount of time. +/* +/* master_delete_children() deletes all child processes that provide +/* the named service. Upon exit, the process creation throttle for that +/* service is released. +/* DIAGNOSTICS +/* Panic: interface violations, internal inconsistencies. +/* Fatal errors: out of memory. Warnings: throttle on/off. +/* BUGS +/* SEE ALSO +/* master_avail(3), process creation policy. +/* 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 libraries. */ + +#include +#include +#include +#include +#include /* closelog() */ +#include +#include + +/* Utility libraries. */ + +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "master_proto.h" +#include "master.h" + +BINHASH *master_child_table; +static void master_unthrottle(MASTER_SERV *serv); + +/* master_unthrottle_wrapper - in case (char *) != (struct *) */ + +static void master_unthrottle_wrapper(char *ptr) +{ + MASTER_SERV *serv = (MASTER_SERV *) ptr; + + /* + * This routine runs after expiry of the timer set in master_throttle(), + * which gets called when it appears that the world is falling apart. + */ + master_unthrottle(serv); +} + +/* master_unthrottle - enable process creation */ + +static void master_unthrottle(MASTER_SERV *serv) +{ + + /* + * Enable process creation within this class. Disable the "unthrottle" + * timer just in case we're being called directly from the cleanup + * routine, instead of from the event manager. + */ + if ((serv->flags & MASTER_FLAG_THROTTLE) != 0) { + serv->flags &= ~MASTER_FLAG_THROTTLE; + event_cancel_timer(master_unthrottle_wrapper, (char *) serv); + if (msg_verbose) + msg_info("throttle released for command %s", serv->path); + master_avail_listen(serv); /* XXX interface botch */ + } +} + +/* master_throttle - suspend process creation */ + +static void master_throttle(MASTER_SERV *serv) +{ + + /* + * Perhaps the command to be run is defective, perhaps some configuration + * is wrong, or perhaps the system is out of resources. Disable further + * process creation attempts for a while. + */ + if ((serv->flags & MASTER_FLAG_THROTTLE) == 0) { + serv->flags |= MASTER_FLAG_THROTTLE; + event_request_timer(master_unthrottle_wrapper, (char *) serv, + serv->throttle_delay); + if (msg_verbose) + msg_info("throttling command %s", serv->path); + } +} + +/* master_spawn - spawn off new child process if we can */ + +void master_spawn(MASTER_SERV *serv) +{ + char *myname = "master_spawn"; + MASTER_PROC *proc; + MASTER_PID pid; + int n; + + if (master_child_table == 0) + master_child_table = binhash_create(0); + + /* + * Sanity checks. The master_avail module is supposed to know what it is + * doing. + */ + if (!MASTER_LIMIT_OK(serv->max_proc, serv->total_proc)) + msg_panic("%s: at process limit %d", myname, serv->total_proc); + if (serv->avail_proc > 0) + msg_panic("%s: processes available: %d", myname, serv->avail_proc); + if (serv->flags & MASTER_FLAG_THROTTLE) + msg_panic("%s: throttled service: %s", myname, serv->path); + + /* + * Create a child process and connect parent and child via the status + * pipe. + */ + switch (pid = fork()) { + + /* + * Error. We're out of some essential resource. Best recourse is to + * try again later. + */ + case -1: + msg_warn("%s: fork: %m -- throttling", myname); + master_throttle(serv); + return; + + /* + * Child process. Redirect child stdin/stdout to the parent-child + * connection and run the requested command. Leave child stderr + * alone. Disable exit handlers: they should be executed by the + * parent only. + */ + case 0: + msg_cleanup((void (*) (void)) 0); /* disable exit handler */ + closelog(); /* avoid filedes leak */ + close(serv->status_fd[0]); /* status channel */ + if (serv->status_fd[1] <= MASTER_STATUS_FD) + msg_fatal("%s: status file descriptor collision", myname); + if (dup2(serv->status_fd[1], MASTER_STATUS_FD) < 0) + msg_fatal("%s: dup2 status_fd: %m", myname); + (void) close(serv->status_fd[1]); + + for (n = 0; n < serv->listen_fd_count; n++) { + if (serv->listen_fd[n] <= MASTER_LISTEN_FD + n) + msg_fatal("%s: listen file descriptor collision", myname); + if (dup2(serv->listen_fd[n], MASTER_LISTEN_FD + n) < 0) + msg_fatal("%s: dup2 listen_fd %d: %m", + myname, serv->listen_fd[n]); + (void) close(serv->listen_fd[n]); + } + execvp(serv->path, serv->args->argv); + msg_fatal("%s: exec %s: %m", myname, serv->path); + exit(1); + /* NOTREACHED */ + + /* + * Parent. Fill in a process member data structure and set up links + * between child and process. Say this process has become available. + */ + default: + if (msg_verbose) + msg_info("spawn command %s; pid %d", serv->path, pid); + proc = (MASTER_PROC *) mymalloc(sizeof(MASTER_PROC)); + proc->serv = serv; + proc->pid = pid; + proc->use_count = 0; + proc->avail = 0; + binhash_enter(master_child_table, (char *) &pid, + sizeof(pid), (char *) proc); + serv->total_proc++; + master_avail_more(serv, proc); + return; + } +} + +/* master_delete_child - destroy child process info */ + +static void master_delete_child(MASTER_PROC *proc) +{ + MASTER_SERV *serv; + + /* + * Undo the things that master_spawn did. Stop the process if it still + * exists, and remove it from the lookup tables. Update the number of + * available processes. + */ + serv = proc->serv; + serv->total_proc--; + if (proc->avail == MASTER_STAT_AVAIL) + master_avail_less(serv, proc); + else if (MASTER_LIMIT_OK(serv->max_proc, serv->total_proc) + && serv->avail_proc < 1) + master_avail_listen(serv); + binhash_delete(master_child_table, (char *) &proc->pid, + sizeof(proc->pid), (void (*) (char *)) 0); + myfree((char *) proc); +} + +/* master_reap_child - reap dead children */ + +void master_reap_child(void) +{ + MASTER_SERV *serv; + MASTER_PROC *proc; + MASTER_PID pid; + WAIT_STATUS_T status; + + /* + * Pick up termination status of all dead children. When a process failed + * on its first job, assume we see the symptom of a structural problem + * (configuration problem, system running out of resources) and back off. + */ + while ((pid = waitpid((pid_t) - 1, &status, WNOHANG)) > 0) { + if (msg_verbose) + msg_info("master_reap_child: pid %d", pid); + if ((proc = (MASTER_PROC *) binhash_find(master_child_table, + (char *) &pid, sizeof(pid))) == 0) { + msg_panic("master_reap: unknown pid: %d", pid); + continue; + } + serv = proc->serv; + if (!NORMAL_EXIT_STATUS(status)) { + if (WIFEXITED(status)) + msg_warn("process %s pid %d exit status %d", + serv->path, pid, WEXITSTATUS(status)); + if (WIFSIGNALED(status)) + msg_warn("process %s pid %d killed by signal %d", + serv->path, pid, WTERMSIG(status)); + } + if (!NORMAL_EXIT_STATUS(status) && proc->use_count == 0 + && (serv->flags & MASTER_FLAG_THROTTLE) == 0) { + msg_warn("%s: bad command startup -- throttling", serv->path); + master_throttle(serv); + } + master_delete_child(proc); + } +} + +/* master_delete_children - delete all child processes of service */ + +void master_delete_children(MASTER_SERV *serv) +{ + BINHASH_INFO **list; + BINHASH_INFO **info; + MASTER_PROC *proc; + + /* + * XXX turn on the throttle so that master_reap_child() doesn't. Someone + * has to turn off the throttle in order to stop the associated timer + * request, so we might just as well do it at the end. + */ + master_throttle(serv); + for (info = list = binhash_list(master_child_table); *info; info++) { + proc = (MASTER_PROC *) info[0]->value; + if (proc->serv == serv) + (void) kill(proc->pid, SIGTERM); + } + while (serv->total_proc > 0) + master_reap_child(); + myfree((char *) list); + master_unthrottle(serv); +} diff --git a/postfix/master/master_status.c b/postfix/master/master_status.c new file mode 100644 index 000000000..4cc54db2f --- /dev/null +++ b/postfix/master/master_status.c @@ -0,0 +1,192 @@ +/*++ +/* NAME +/* master_status 3 +/* SUMMARY +/* Postfix master - process child status reports +/* SYNOPSIS +/* #include "master.h" +/* +/* void master_status_init(serv) +/* MASTER_SERV *serv; +/* +/* void master_status_cleanup(serv) +/* MASTER_SERV *serv; +/* DESCRIPTION +/* This module reads and processes status reports from child processes. +/* +/* master_status_init() enables the processing of child status updates +/* for the specified service. Child process status updates (process +/* available, process taken) are passed on to the master_avail_XXX() +/* routines. +/* +/* master_status_cleanup() disables child status update processing +/* for the specified service. +/* DIAGNOSTICS +/* Panic: internal inconsistency. Warnings: a child process sends +/* incomplete or incorrect information. +/* BUGS +/* SEE ALSO +/* master_avail(3) +/* 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 libraries. */ + +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +#include "master_proto.h" +#include "master.h" + +/* master_status_event - status read event handler */ + +static void master_status_event(int event, char *context) +{ + char *myname = "master_status_event"; + MASTER_SERV *serv = (MASTER_SERV *) context; + MASTER_STATUS stat; + MASTER_PROC *proc; + MASTER_PID pid; + int n; + + if (event == 0) /* XXX Can this happen? */ + return; + + /* + * We always keep the child end of the status pipe open, so an EOF read + * condition means that we're seriously confused. We use non-blocking + * reads so that we don't get stuck when someone sends a partial message. + * Messages are short, so a partial read means someone wrote less than a + * whole status message. Hopefully the next read will be in sync again... + * We use a global child process status table because when a child dies + * only its pid is known - we do not know what service it came from. + */ + switch (n = read(serv->status_fd[0], (char *) &stat, sizeof(stat))) { + + case -1: + msg_warn("%s: read: %m", myname); + return; + + case 0: + msg_panic("%s: read EOF status", myname); + /* NOTREACHED */ + + default: + msg_warn("%s: partial status (%d bytes)", myname, n); + return; + + case sizeof(stat): + pid = stat.pid; + if (msg_verbose) + msg_info("%s: pid %d avail %d", myname, stat.pid, stat.avail); + } + + /* + * Sanity checks. Do not freak out when the child sends garbage because + * it is confused or for other reasons. However, be sure to freak out + * when our own data structures are inconsistent. A process not found + * condition can happen when we reap a process before receiving its + * status update, so this is not an error. + */ + if ((proc = (MASTER_PROC *) binhash_find(master_child_table, + (char *) &pid, sizeof(pid))) == 0) { + if (msg_verbose) + msg_info("%s: process id not found: %d", myname, stat.pid); + return; + } + if (proc->serv != serv) + msg_panic("%s: pointer corruption: %p != %p", + myname, (char *) proc->serv, (char *) serv); + + /* + * Update our idea of the child process status. Allow redundant status + * updates, because different types of events may be processed out of + * order. Otherwise, warn about weird status updates but do not take + * action. It's all gossip after all. + */ + if (proc->avail == stat.avail) + return; + switch (stat.avail) { + case MASTER_STAT_AVAIL: + proc->use_count++; + master_avail_more(serv, proc); + break; + case MASTER_STAT_TAKEN: + master_avail_less(serv, proc); + break; + default: + msg_warn("%s: ignoring unknown status: %d allegedly from pid: %d", + myname, stat.pid, stat.avail); + break; + } +} + +/* master_status_init - start status event processing for this service */ + +void master_status_init(MASTER_SERV *serv) +{ + char *myname = "master_status_init"; + + /* + * Sanity checks. + */ + if (serv->status_fd[0] >= 0 || serv->status_fd[1] >= 0) + msg_panic("%s: status events already enabled", myname); + if (msg_verbose) + msg_info("%s: %s", myname, serv->name); + + /* + * Make the read end of this service's status pipe non-blocking so that + * we can detect partial writes on the child side. We use a socket pair, + * so that the child side becomes readable when the master goes away. + */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, serv->status_fd) < 0) + msg_fatal("pipe: %m"); + non_blocking(serv->status_fd[0], BLOCKING); + close_on_exec(serv->status_fd[0], CLOSE_ON_EXEC); + close_on_exec(serv->status_fd[1], CLOSE_ON_EXEC); + event_enable_read(serv->status_fd[0], master_status_event, (char *) serv); +} + +/* master_status_cleanup - stop status event processing for this service */ + +void master_status_cleanup(MASTER_SERV *serv) +{ + char *myname = "master_status_cleanup"; + + /* + * Sanity checks. + */ + if (serv->status_fd[0] < 0 || serv->status_fd[1] < 0) + msg_panic("%s: status events not enabled", myname); + if (msg_verbose) + msg_info("%s: %s", myname, serv->name); + + /* + * Dispose of this service's status pipe after disabling read events. + */ + event_disable_readwrite(serv->status_fd[0]); + if (close(serv->status_fd[0]) != 0) + msg_warn("%s: close status descriptor (read side): %m", myname); + if (close(serv->status_fd[1]) != 0) + msg_warn("%s: close status descriptor (write side): %m", myname); + serv->status_fd[0] = serv->status_fd[1] = -1; +} diff --git a/postfix/master/master_vars.c b/postfix/master/master_vars.c new file mode 100644 index 000000000..6689794b5 --- /dev/null +++ b/postfix/master/master_vars.c @@ -0,0 +1,66 @@ +/*++ +/* NAME +/* master_vars 3 +/* SUMMARY +/* Postfix master - global configuration file access +/* SYNOPSIS +/* #include "master.h" +/* +/* void master_vars_init() +/* DESCRIPTION +/* master_vars_init() reads values from the global Postfix configuration +/* file and assigns them to tunable program parameters. Where no value +/* is specified, a compiled-in default value is used. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* Application-specific. */ + +#include "master.h" + + /* + * Tunable parameters. + */ +int var_proc_limit; + +/* master_vars_init - initialize from global Postfix configuration file */ + +void master_vars_init(void) +{ + char *path; + static CONFIG_INT_TABLE int_table[] = { + VAR_PROC_LIMIT, DEF_PROC_LIMIT, &var_proc_limit, 1, 0, + 0, + }; + + read_config(); + get_config_int_table(int_table); + path = concatenate(var_config_dir, "/master.cf", (char *) 0); + fset_master_ent(path); + myfree(path); +} + diff --git a/postfix/master/master_wakeup.c b/postfix/master/master_wakeup.c new file mode 100644 index 000000000..4e3a1a994 --- /dev/null +++ b/postfix/master/master_wakeup.c @@ -0,0 +1,150 @@ +/*++ +/* NAME +/* master_wakeup 3 +/* SUMMARY +/* Postfix master - start/stop service wakeup timers +/* SYNOPSIS +/* #include "master.h" +/* +/* void master_wakeup_init(serv) +/* MASTER_SERV *serv; +/* +/* void master_wakeup_cleanup(serv) +/* MASTER_SERV *serv; +/* DESCRIPTION +/* This module implements automatic service wakeup. In order to +/* wakeup a service, a wakeup trigger is sent to the corresponding +/* service port or FIFO, and a timer is started to repeat this sequence +/* after a configurable amount of time. +/* +/* master_wakeup_init() wakes up the named service. No wakeup +/* is done or scheduled when a zero wakeup time is given, or when +/* the service has been throttled in the mean time. +/* It is OK to call master_wakeup_init() while a timer is already +/* running for the named service. The effect is to restart the +/* wakeup timer. +/* +/* master_wakeup_cleanup() cancels the wakeup timer for the named +/* service. It is an error to disable a service while it still has +/* an active wakeup timer (doing so would cause a dangling reference +/* to a non-existent service). +/* It is OK to call master_wakeup_cleanup() even when no timer is +/* active for the named service. +/* DIAGNOSTICS +/* BUGS +/* SEE ALSO +/* inet_trigger(3), internet-domain client +/* unix_trigger(3), unix-domain client +/* fifo_trigger(3), fifo client +/* 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 + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include /* triggers */ + +/* Application-specific. */ + +#include "mail_server.h" +#include "master.h" + +/* master_wakeup_timer_event - wakeup event handler */ + +static void master_wakeup_timer_event(char *context) +{ + char *myname = "master_wakeup_timer_event"; + MASTER_SERV *serv = (MASTER_SERV *) context; + int status; + static char wakeup = TRIGGER_REQ_WAKEUP; + + /* + * Don't wakeup services whose automatic wakeup feature was turned off in + * the mean time. + */ + if (serv->wakeup_time == 0) + return; + + /* + * Don't wake up services that are throttled. Find out what transport to + * use. We can't block here so we choose a short timeout. + */ +#define BRIEFLY 1 + + if (MASTER_THROTTLED(serv) == 0) { + if (msg_verbose) + msg_info("%s: service %s", myname, serv->name); + + switch (serv->type) { + case MASTER_SERV_TYPE_INET: + status = inet_trigger(serv->name, &wakeup, sizeof(wakeup), BRIEFLY); + break; + case MASTER_SERV_TYPE_UNIX: + status = unix_trigger(serv->name, &wakeup, sizeof(wakeup), BRIEFLY); + break; + case MASTER_SERV_TYPE_FIFO: + status = fifo_trigger(serv->name, &wakeup, sizeof(wakeup), BRIEFLY); + break; + default: + msg_panic("%s: unknown service type: %d", myname, serv->type); + } + if (status < 0) + msg_warn("%s: service %s: %m", myname, serv->name); + } + + /* + * Schedule another wakeup event. + */ + event_request_timer(master_wakeup_timer_event, (char *) serv, + serv->wakeup_time); +} + +/* master_wakeup_init - start automatic service wakeup */ + +void master_wakeup_init(MASTER_SERV *serv) +{ + char *myname = "master_wakeup_init"; + + if (serv->wakeup_time == 0) + return; + if (msg_verbose) + msg_info("%s: service %s time %d", + myname, serv->name, serv->wakeup_time); + master_wakeup_timer_event((char *) serv); +} + +/* master_wakeup_cleanup - cancel wakeup timer */ + +void master_wakeup_cleanup(MASTER_SERV *serv) +{ + char *myname = "master_wakeup_cleanup"; + + /* + * Cleanup, even when the wakeup feature has been turned off. There might + * still be a pending timer. Don't depend on the code that reloads the + * config file to reset the wakeup timer when things change. + */ + if (msg_verbose) + msg_info("%s: service %s", myname, serv->name); + + event_cancel_timer(master_wakeup_timer_event, (char *) serv); +} diff --git a/postfix/master/multi_server.c b/postfix/master/multi_server.c new file mode 100644 index 000000000..3cf0a013f --- /dev/null +++ b/postfix/master/multi_server.c @@ -0,0 +1,450 @@ +/*++ +/* NAME +/* multi_server 3 +/* SUMMARY +/* skeleton multi-threaded mail subsystem +/* SYNOPSIS +/* #include +/* +/* NORETURN multi_server_main(argc, argv, service, key, value, ...) +/* int argc; +/* char **argv; +/* void (*service)(VSTREAM *stream, char *service_name, char **argv); +/* int key; +/* +/* void multi_server_disconnect(stream, argv) +/* VSTREAM *stream; +/* char **argv; +/* DESCRIPTION +/* This module implements a skeleton for multi-threaded +/* mail subsystems: mail subsystem programs that service multiple +/* clients at the same time. The resulting program expects to be run +/* from the \fBmaster\fR process. +/* +/* multi_server_main() is the skeleton entry point. It should be +/* called from the application main program. The skeleton does all +/* the generic command-line processing, initialization of +/* configurable parameters, and connection management. +/* The skeleton never returns. +/* +/* Arguments: +/* .IP "void (*service)(VSTREAM *stream, char *service_name, char **argv)" +/* A pointer to a function that is called by the skeleton each +/* time a client sends data to the program's service port. The +/* function is run after the program has optionally dropped its +/* privileges. This function should not attempt to preserve state +/* across calls. The stream initial state is non-blocking mode. +/* The service name argument corresponds to the service name in the +/* master.cf file. +/* The argv argument specifies command-line arguments left over +/* after options processing. +/* .PP +/* Optional arguments are specified as a null-terminated (key, value) +/* list. Keys and expected values are: +/* .IP "MAIL_SERVER_INT_TABLE (CONFIG_INT_TABLE *)" +/* A table with configurable parameters, to be loaded from the +/* global Postfix configuration file. Tables are loaded in the +/* order as specified, and multiple instances of the same type +/* are allowed. +/* .IP "MAIL_SERVER_STR_TABLE (CONFIG_STR_TABLE *)" +/* A table with configurable parameters, to be loaded from the +/* global Postfix configuration file. Tables are loaded in the +/* order as specified, and multiple instances of the same type +/* are allowed. +/* .IP "MAIL_SERVER_BOOL_TABLE (CONFIG_BOOL_TABLE *)" +/* A table with configurable parameters, to be loaded from the +/* global Postfix configuration file. Tables are loaded in the +/* order as specified, and multiple instances of the same type +/* are allowed. +/* .IP "MAIL_SERVER_PRE_INIT (void *(void))" +/* A pointer to a function that is called once +/* by the skeleton after it has read the global configuration file +/* and after it has processed command-line arguments, but before +/* the skeleton has optionally relinquished the process privileges. +/* .IP "MAIL_SERVER_POST_INIT (void *(void))" +/* A pointer to a function that is called once +/* by the skeleton after it has optionally relinquished the process +/* privileges, but before servicing client connection requests. +/* .IP "MAIL_SERVER_LOOP (int *(void))" +/* A pointer to function that is executed from +/* within the event loop, whenever an I/O or timer event has happened, +/* 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". +/* .PP +/* multi_server_disconnect() should be called by the application +/* when a client disconnects. +/* +/* 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. +/* +/* The var_idle_limit variable limits the time that a service +/* receives no client connection requests 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 idle limit. +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8). +/* SEE ALSO +/* master(8), master process +/* syslogd(8) system logging +/* 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 +#include +#include +#include +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include + +/* Process manager. */ + +#include "master_proto.h" + +/* Application-specific */ + +#include "mail_server.h" + + /* + * Global state. + */ +static int client_count; +static int use_count; + +static void (*multi_server_service) (VSTREAM *, char *, char **); +static char *multi_server_name; +static char **multi_server_argv; + +/* multi_server_abort - terminate after abnormal master exit */ + +static void multi_server_abort(int unused_event, char *unused_context) +{ + if (msg_verbose) + msg_info("master disconnect -- exiting"); + exit(0); +} + +/* multi_server_timeout - idle time exceeded */ + +static void multi_server_timeout(char *unused_context) +{ + if (msg_verbose) + msg_info("idle timeout -- exiting"); + exit(0); +} + +/* multi_server_disconnect - terminate client session */ + +void multi_server_disconnect(VSTREAM *stream) +{ + if (msg_verbose) + msg_info("connection closed fd %d", vstream_fileno(stream)); + event_disable_readwrite(vstream_fileno(stream)); + (void) vstream_fclose(stream); + client_count--; + use_count++; +} + +/* multi_server_execute - in case (char *) != (struct *) */ + +static void multi_server_execute(int unused_event, char *context) +{ + VSTREAM *stream = (VSTREAM *) context; + + /* + * Do not bother the application when the client disconnected. + */ + if (peekfd(vstream_fileno(stream)) > 0) { + multi_server_service(stream, multi_server_name, multi_server_argv); + } else { + multi_server_disconnect(stream); + } + if (client_count == 0 && var_idle_limit > 0) + event_request_timer(multi_server_timeout, (char *) 0, var_idle_limit); +} + +/* multi_server_accept - accept client connection request */ + +static void multi_server_accept(int unused_event, char *context) +{ + int listen_fd = (int) context; + int time_left = -1; + int fd; + VSTREAM *stream; + + /* + * Be prepared for accept() to fail because some other process already + * got the connection (the number of processes competing for clients is + * kept small, so this is not a "thundering herd" problem). If the + * accept() succeeds, be sure to disable non-blocking I/O, in order to + * minimize confusion. + */ + if (client_count == 0 && var_idle_limit > 0) + time_left = event_cancel_timer(multi_server_timeout, (char *) 0); + if ((fd = sane_accept(listen_fd, (struct sockaddr *) 0, (SOCKADDR_SIZE *) 0)) < 0) { + if (errno != EAGAIN) + msg_fatal("accept connection: %m"); + if (time_left >= 0) + event_request_timer(multi_server_timeout, (char *) 0, time_left); + return; + } + if (msg_verbose) + msg_info("connection established fd %d", fd); + non_blocking(fd, BLOCKING); + close_on_exec(fd, CLOSE_ON_EXEC); + client_count++; + stream = vstream_fdopen(fd, O_RDWR); + timed_ipc_setup(stream); + event_enable_read(fd, multi_server_execute, (char *) stream); +} + +/* multi_server_main - the real main program */ + +NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...) +{ + char *myname = "multi_server_main"; + VSTREAM *stream = 0; + char *root_dir = 0; + char *user_name = 0; + int debug_me = 0; + char *service_name = basename(argv[0]); + int delay; + int c; + int socket_count = 1; + int fd; + va_list ap; + MAIL_SERVER_INIT_FN pre_init = 0; + MAIL_SERVER_INIT_FN post_init = 0; + MAIL_SERVER_LOOP_FN loop = 0; + int key; + char *transport = 0; + + /* + * Process environment options as early as we can. + */ + if (getenv(CONF_ENV_VERB)) + msg_verbose = 1; + if (getenv(CONF_ENV_DEBUG)) + debug_me = 1; + + /* + * Don't die when a process goes away unexpectedly. + */ + signal(SIGPIPE, SIG_IGN); + + /* + * May need this every now and then. + */ + var_procname = mystrdup(basename(argv[0])); + set_config_str(VAR_PROCNAME, var_procname); + + /* + * Initialize logging and exit handler. Do the syslog first, so that its + * initialization completes before we enter the optional chroot jail. + */ + msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY); + if (msg_verbose) + msg_info("daemon started"); + + /* + * Initialize from the configuration file. Allow command-line options to + * override compiled-in defaults or configured parameter values. + */ + read_config(); + va_start(ap, service); + while ((key = va_arg(ap, int)) != 0) { + switch (key) { + case MAIL_SERVER_INT_TABLE: + get_config_int_table(va_arg(ap, CONFIG_INT_TABLE *)); + break; + case MAIL_SERVER_STR_TABLE: + get_config_str_table(va_arg(ap, CONFIG_STR_TABLE *)); + break; + case MAIL_SERVER_BOOL_TABLE: + get_config_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *)); + break; + case MAIL_SERVER_PRE_INIT: + pre_init = va_arg(ap, MAIL_SERVER_INIT_FN); + break; + case MAIL_SERVER_POST_INIT: + post_init = va_arg(ap, MAIL_SERVER_INIT_FN); + break; + case MAIL_SERVER_LOOP: + loop = va_arg(ap, MAIL_SERVER_LOOP_FN); + break; + default: + msg_panic("%s: unknown argument type: %d", myname, key); + } + } + va_end(ap); + + /* + * Pick up policy settings from master process. Shut up error messages to + * stderr, because no-one is going to see them. + */ + opterr = 0; + while ((c = GETOPT(argc, argv, "cDi:m:n:s:St:uv")) > 0) { + switch (c) { + case 'c': + root_dir = var_queue_dir; + break; + case 'D': + debug_me = 1; + break; + case 'i': + if ((var_idle_limit = atoi(optarg)) <= 0) + msg_fatal("invalid max_idle time: %s", optarg); + break; + case 'm': + if ((var_use_limit = atoi(optarg)) <= 0) + msg_fatal("invalid max_use: %s", optarg); + break; + case 'n': + service_name = optarg; + break; + case 's': + if ((socket_count = atoi(optarg)) <= 0) + msg_fatal("invalid socket_count: %s", optarg); + break; + case 'S': + stream = VSTREAM_IN; + break; + case 'u': + user_name = var_mail_owner; + break; + case 't': + transport = optarg; + break; + case 'v': + msg_verbose++; + break; + default: + msg_fatal("invalid option: %c", c); + break; + } + } + + /* + * If not connected to stdin, stdin must not be a terminal. + */ + if (stream == 0 && isatty(STDIN_FILENO)) { + msg_vstream_init(var_procname, VSTREAM_ERR); + msg_fatal("do not run this command by hand"); + } + + /* + * Optionally start the debugger on ourself. + */ + if (debug_me) + debug_process(); + + /* + * Run pre-jail initialization. + */ + if (pre_init) + pre_init(); + + /* + * Optionally, restrict the damage that this process can do. + */ + if (chdir(var_queue_dir) < 0) + msg_fatal("chdir(\"%s\"): %m", var_queue_dir); + resolve_local_init(); + chroot_uid(root_dir, user_name); + + /* + * Run post-jail initialization. + */ + if (post_init) + post_init(); + + /* + * Are we running as a one-shot server with the client connection on + * standard input? If so, make sure the output is written to stdout so as + * to satisfy common expectation. + */ + if (stream != 0) { + vstream_control(stream, + VSTREAM_CTL_DOUBLE, + VSTREAM_CTL_WRITE_FD, STDOUT_FILENO, + VSTREAM_CTL_END); + service(stream, service_name, argv + optind); + vstream_fflush(stream); + exit(0); + } + + /* + * 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 + * no-one has been talking to us for a configurable amount of time, or + * when the master process terminated abnormally. + */ + multi_server_service = service; + multi_server_name = service_name; + multi_server_argv = argv + optind; + if (var_idle_limit > 0) + event_request_timer(multi_server_timeout, (char *) 0, var_idle_limit); + for (fd = MASTER_LISTEN_FD; fd < MASTER_LISTEN_FD + socket_count; fd++) { + event_enable_read(fd, multi_server_accept, (char *) fd); + close_on_exec(fd, CLOSE_ON_EXEC); + } + event_enable_read(MASTER_STATUS_FD, multi_server_abort, (char *) 0); + 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; + event_loop(delay); + } + exit(0); +} diff --git a/postfix/master/single_server.c b/postfix/master/single_server.c new file mode 100644 index 000000000..750176fac --- /dev/null +++ b/postfix/master/single_server.c @@ -0,0 +1,426 @@ +/*++ +/* NAME +/* single_server 3 +/* SUMMARY +/* skeleton single-threaded mail subsystem +/* SYNOPSIS +/* #include +/* +/* NORETURN single_server_main(argc, argv, service, key, value, ...) +/* int argc; +/* char **argv; +/* void (*service)(VSTREAM *stream, char *service_name, char **argv); +/* int key; +/* DESCRIPTION +/* This module implements a skeleton for single-threaded +/* mail subsystems: mail subsystem programs that service one +/* client at a time. The resulting program expects to be run +/* from the \fBmaster\fR process. +/* +/* single_server_main() is the skeleton entry point. It should be +/* called from the application main program. The skeleton does the +/* generic command-line options processing, initialization of +/* configurable parameters, and connection management. +/* The skeleton never returns. +/* +/* Arguments: +/* .IP "void (*service)(VSTREAM *fp, char *service_name, char **argv)" +/* A pointer to a function that is called by the skeleton each time +/* a client connects to the program's service port. The function is +/* run after the program has irrevocably dropped its privileges. +/* The stream initial state is non-blocking mode. +/* The service name argument corresponds to the service name in the +/* master.cf file. +/* The argv argument specifies command-line arguments left over +/* after options processing. +/* .PP +/* Optional arguments are specified as a null-terminated (key, value) +/* list. Keys and expected values are: +/* .IP "MAIL_SERVER_INT_TABLE (CONFIG_INT_TABLE *)" +/* A table with configurable parameters, to be loaded from the +/* global Postfix configuration file. Tables are loaded in the +/* order as specified, and multiple instances of the same type +/* are allowed. +/* .IP "MAIL_SERVER_STR_TABLE (CONFIG_STR_TABLE *)" +/* A table with configurable parameters, to be loaded from the +/* global Postfix configuration file. Tables are loaded in the +/* order as specified, and multiple instances of the same type +/* are allowed. +/* .IP "MAIL_SERVER_BOOL_TABLE (CONFIG_BOOL_TABLE *)" +/* A table with configurable parameters, to be loaded from the +/* global Postfix configuration file. Tables are loaded in the +/* order as specified, and multiple instances of the same type +/* are allowed. +/* .IP "MAIL_SERVER_PRE_INIT (void *(void))" +/* A pointer to a function that is called once +/* by the skeleton after it has read the global configuration file +/* and after it has processed command-line arguments, but before +/* the skeleton has optionally relinquished the process privileges. +/* .IP "MAIL_SERVER_POST_INIT (void *(void))" +/* A pointer to a function that is called once +/* by the skeleton after it has optionally relinquished the process +/* privileges, but before servicing client connection requests. +/* .IP "MAIL_SERVER_LOOP (int *(void))" +/* A pointer to function that is executed from +/* within the event loop, whenever an I/O or timer event has happened, +/* 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". +/* .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. +/* +/* The var_idle_limit variable limits the time that a service +/* receives no client connection requests 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 idle limit. +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8). +/* BUGS +/* SEE ALSO +/* master(8), master process +/* syslogd(8) system logging +/* 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 +#include +#include +#include +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include + +/* Process manager. */ + +#include "master_proto.h" + +/* Application-specific */ + +#include "mail_server.h" + + /* + * Global state. + */ +static int use_count; + +static void (*single_server_service) (VSTREAM *, char *, char **); +static char *single_server_name; +static char **single_server_argv; + +/* single_server_abort - terminate after abnormal master exit */ + +static void single_server_abort(int unused_event, char *unused_context) +{ + if (msg_verbose) + msg_info("master disconnect -- exiting"); + exit(0); +} + +/* single_server_timeout - idle time exceeded */ + +static void single_server_timeout(char *unused_context) +{ + if (msg_verbose) + msg_info("idle timeout -- exiting"); + exit(0); +} + +/* single_server_accept - accept client connection request */ + +static void single_server_accept(int unused_event, char *context) +{ + int listen_fd = (int) context; + VSTREAM *stream; + int time_left = -1; + int fd; + + /* + * Be prepared for accept() to fail because some other process already + * got the connection. We use select() + accept(), instead of simply + * blocking in accept(), because we must be able to detect that the + * master process has gone away unexpectedly. + */ + if (var_idle_limit > 0) + time_left = event_cancel_timer(single_server_timeout, (char *) 0); + + if ((fd = sane_accept(listen_fd, (struct sockaddr *) 0, (SOCKADDR_SIZE *) 0)) < 0) { + if (errno != EAGAIN) + msg_fatal("accept connection: %m"); + if (time_left >= 0) + event_request_timer(single_server_timeout, (char *) 0, time_left); + return; + } + + /* + * If the accept() succeeds, be sure to disable non-blocking I/O, because + * the application is supposed to be single-threaded. Notice the master + * of our (un)availability to service connection requests. Commit suicide + * when the master process disconnected from us. + */ + if (msg_verbose) + msg_info("connection established"); + non_blocking(fd, BLOCKING); + close_on_exec(fd, CLOSE_ON_EXEC); + stream = vstream_fdopen(fd, O_RDWR); + timed_ipc_setup(stream); + if (master_notify(var_pid, MASTER_STAT_TAKEN) < 0) + single_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT); + single_server_service(stream, single_server_name, single_server_argv); + (void) vstream_fclose(stream); + if (master_notify(var_pid, MASTER_STAT_AVAIL) < 0) + single_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT); + if (msg_verbose) + msg_info("connection closed"); + use_count++; + if (var_idle_limit > 0) + event_request_timer(single_server_timeout, (char *) 0, var_idle_limit); +} + +/* single_server_main - the real main program */ + +NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...) +{ + char *myname = "single_server_main"; + VSTREAM *stream = 0; + char *root_dir = 0; + char *user_name = 0; + int debug_me = 0; + char *service_name = basename(argv[0]); + int delay; + int c; + int socket_count = 1; + int fd; + va_list ap; + MAIL_SERVER_INIT_FN pre_init = 0; + MAIL_SERVER_INIT_FN post_init = 0; + MAIL_SERVER_LOOP_FN loop = 0; + int key; + char *transport = 0; + + /* + * Process environment options as early as we can. + */ + if (getenv(CONF_ENV_VERB)) + msg_verbose = 1; + if (getenv(CONF_ENV_DEBUG)) + debug_me = 1; + + /* + * Don't die when a process goes away unexpectedly. + */ + signal(SIGPIPE, SIG_IGN); + + /* + * May need this every now and then. + */ + var_procname = mystrdup(basename(argv[0])); + set_config_str(VAR_PROCNAME, var_procname); + + /* + * Initialize logging and exit handler. Do the syslog first, so that its + * initialization completes before we enter the optional chroot jail. + */ + msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY); + if (msg_verbose) + msg_info("daemon started"); + + /* + * Initialize from the configuration file. Allow command-line options to + * override compiled-in defaults or configured parameter values. + */ + read_config(); + va_start(ap, service); + while ((key = va_arg(ap, int)) != 0) { + switch (key) { + case MAIL_SERVER_INT_TABLE: + get_config_int_table(va_arg(ap, CONFIG_INT_TABLE *)); + break; + case MAIL_SERVER_STR_TABLE: + get_config_str_table(va_arg(ap, CONFIG_STR_TABLE *)); + break; + case MAIL_SERVER_BOOL_TABLE: + get_config_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *)); + break; + case MAIL_SERVER_PRE_INIT: + pre_init = va_arg(ap, MAIL_SERVER_INIT_FN); + break; + case MAIL_SERVER_POST_INIT: + post_init = va_arg(ap, MAIL_SERVER_INIT_FN); + break; + case MAIL_SERVER_LOOP: + loop = va_arg(ap, MAIL_SERVER_LOOP_FN); + break; + default: + msg_panic("%s: unknown argument type: %d", myname, key); + } + } + va_end(ap); + + /* + * Pick up policy settings from master process. Shut up error messages to + * stderr, because no-one is going to see them. + */ + opterr = 0; + while ((c = GETOPT(argc, argv, "cDi:m:n:s:St:uv")) > 0) { + switch (c) { + case 'c': + root_dir = var_queue_dir; + break; + case 'D': + debug_me = 1; + break; + case 'i': + if ((var_idle_limit = atoi(optarg)) <= 0) + msg_fatal("invalid max_idle time: %s", optarg); + break; + case 'm': + if ((var_use_limit = atoi(optarg)) <= 0) + msg_fatal("invalid max_use: %s", optarg); + break; + case 'n': + service_name = optarg; + break; + case 's': + if ((socket_count = atoi(optarg)) <= 0) + msg_fatal("invalid socket_count: %s", optarg); + break; + case 'S': + stream = VSTREAM_IN; + break; + case 'u': + user_name = var_mail_owner; + break; + case 't': + transport = optarg; + break; + case 'v': + msg_verbose++; + break; + default: + msg_fatal("invalid option: %c", c); + break; + } + } + + /* + * If not connected to stdin, stdin must not be a terminal. + */ + if (stream == 0 && isatty(STDIN_FILENO)) { + msg_vstream_init(var_procname, VSTREAM_ERR); + msg_fatal("do not run this command by hand"); + } + + /* + * Optionally start the debugger on ourself. + */ + if (debug_me) + debug_process(); + + /* + * Run pre-jail initialization. + */ + if (pre_init) + pre_init(); + + /* + * Optionally, restrict the damage that this process can do. + */ + if (chdir(var_queue_dir) < 0) + msg_fatal("chdir(\"%s\"): %m", var_queue_dir); + resolve_local_init(); + chroot_uid(root_dir, user_name); + + /* + * Run post-jail initialization. + */ + if (post_init) + post_init(); + + /* + * Are we running as a one-shot server with the client connection on + * standard input? If so, make sure the output is written to stdout so as + * to satisfy common expectation. + */ + if (stream != 0) { + vstream_control(stream, + VSTREAM_CTL_DOUBLE, + VSTREAM_CTL_WRITE_FD, STDOUT_FILENO, + VSTREAM_CTL_END); + service(stream, service_name, argv + optind); + vstream_fflush(stream); + exit(0); + } + + /* + * 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 + * no-one has been talking to us for a configurable amount of time, or + * when the master process terminated abnormally. + */ + single_server_service = service; + single_server_name = service_name; + single_server_argv = argv + optind; + if (var_idle_limit > 0) + event_request_timer(single_server_timeout, (char *) 0, var_idle_limit); + for (fd = MASTER_LISTEN_FD; fd < MASTER_LISTEN_FD + socket_count; fd++) { + event_enable_read(fd, single_server_accept, (char *) fd); + close_on_exec(fd, CLOSE_ON_EXEC); + } + event_enable_read(MASTER_STATUS_FD, single_server_abort, (char *) 0); + close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC); + while (var_use_limit == 0 || use_count < var_use_limit) { + delay = loop ? loop() : -1; + event_loop(delay); + } + exit(0); +} diff --git a/postfix/master/trigger_server.c b/postfix/master/trigger_server.c new file mode 100644 index 000000000..9338e0b4b --- /dev/null +++ b/postfix/master/trigger_server.c @@ -0,0 +1,483 @@ +/*++ +/* NAME +/* trigger_server 3 +/* SUMMARY +/* skeleton triggered mail subsystem +/* SYNOPSIS +/* #include +/* +/* NORETURN trigger_server_main(argc, argv, service, key, value, ...) +/* int argc; +/* char **argv; +/* void (*service)(char *buf, int len, char *service_name, char **argv); +/* int key; +/* DESCRIPTION +/* This module implements a skeleton for triggered +/* mail subsystems: mail subsystem programs that wake up on +/* client request and perform some activity without further +/* client interaction. This module supports local IPC via FIFOs +/* and via UNIX-domain sockets. The resulting program expects to be +/* run from the \fBmaster\fR process. +/* +/* trigger_server_main() is the skeleton entry point. It should be +/* called from the application main program. The skeleton does the +/* generic command-line options processing, initialization of +/* configurable parameters, and connection management. +/* The skeleton never returns. +/* +/* Arguments: +/* .IP "void (*service)(char *buf, int len, char *service_name, char **argv)" +/* A pointer to a function that is called by the skeleton each time +/* a client connects to the program's service port. The function is +/* run after the program has irrevocably dropped its privileges. +/* The buffer argument specifies the data read from the trigger port; +/* this data corresponds to one or more trigger requests. +/* The len argument specifies how much client data is available. +/* The maximal size of the buffer is specified via the +/* TRIGGER_BUF_SIZE manifest constant. +/* The service name argument corresponds to the service name in the +/* master.cf file. +/* The argv argument specifies command-line arguments left over +/* after options processing. +/* The \fBserver\fR argument provides the following information: +/* .PP +/* Optional arguments are specified as a null-terminated (key, value) +/* list. Keys and expected values are: +/* .IP "MAIL_SERVER_INT_TABLE (CONFIG_INT_TABLE *)" +/* A table with configurable parameters, to be loaded from the +/* global Postfix configuration file. Tables are loaded in the +/* order as specified, and multiple instances of the same type +/* are allowed. +/* .IP "MAIL_SERVER_STR_TABLE (CONFIG_STR_TABLE *)" +/* A table with configurable parameters, to be loaded from the +/* global Postfix configuration file. Tables are loaded in the +/* order as specified, and multiple instances of the same type +/* are allowed. +/* .IP "MAIL_SERVER_BOOL_TABLE (CONFIG_BOOL_TABLE *)" +/* A table with configurable parameters, to be loaded from the +/* global Postfix configuration file. Tables are loaded in the +/* order as specified, and multiple instances of the same type +/* are allowed. +/* .IP "MAIL_SERVER_PRE_INIT (void *(void))" +/* A pointer to a function that is called once +/* by the skeleton after it has read the global configuration file +/* and after it has processed command-line arguments, but before +/* the skeleton has optionally relinquished the process privileges. +/* .IP "MAIL_SERVER_POST_INIT (void *(void))" +/* A pointer to a function that is called once +/* by the skeleton after it has optionally relinquished the process +/* privileges, but before servicing client connection requests. +/* .IP "MAIL_SERVER_LOOP (int *(void))" +/* A pointer to function that is executed from +/* within the event loop, whenever an I/O or timer event has happened, +/* 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". +/* .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. +/* +/* The var_idle_limit variable limits the time that a service +/* receives no client connection requests 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 idle limit. +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8). +/* BUGS +/* Works with FIFO-based services only. +/* SEE ALSO +/* master(8), master process +/* syslogd(8) system logging +/* 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 +#include +#include +#include +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include + +/* Process manager. */ + +#include "master_proto.h" + +/* Application-specific */ + +#include "mail_server.h" + + /* + * Global state. + */ +static int use_count; + +static TRIGGER_SERVER_FN trigger_server_service; +static char *trigger_server_name; +static char **trigger_server_argv; +static void (*trigger_server_accept) (int, char *); + +/* trigger_server_abort - terminate after abnormal master exit */ + +static void trigger_server_abort(int unused_event, char *unused_context) +{ + if (msg_verbose) + msg_info("master disconnect -- exiting"); + exit(0); +} + +/* trigger_server_timeout - idle time exceeded */ + +static void trigger_server_timeout(char *unused_context) +{ + if (msg_verbose) + msg_info("idle timeout -- exiting"); + exit(0); +} + +/* trigger_server_wakeup - wake up application */ + +static void trigger_server_wakeup(int fd) +{ + char buf[TRIGGER_BUF_SIZE]; + int len; + + /* + * Commit suicide when the master process disconnected from us. + */ + if (master_notify(var_pid, MASTER_STAT_TAKEN) < 0) + trigger_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT); + if ((len = read(fd, buf, sizeof(buf))) >= 0) + trigger_server_service(buf, len, trigger_server_name, + trigger_server_argv); + if (master_notify(var_pid, MASTER_STAT_AVAIL) < 0) + trigger_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT); + if (var_idle_limit > 0) + event_request_timer(trigger_server_timeout, (char *) 0, var_idle_limit); + use_count++; +} + +/* trigger_server_accept_fifo - accept socket client request */ + +static void trigger_server_accept_fifo(int unused_event, char *context) +{ + char *myname = "trigger_server_accept_fifo"; + int listen_fd = (int) context; + + if (msg_verbose) + msg_info("%s: trigger arrived", myname); + + /* + * Some buggy systems cause Postfix to lock up. + */ + alarm(1000); + + /* + * Read whatever the other side wrote into the FIFO. The FIFO read end is + * non-blocking so we won't get stuck when multiple processes wake up. + */ + trigger_server_wakeup(listen_fd); +} + +/* trigger_server_accept_socket - accept socket client request */ + +static void trigger_server_accept_socket(int unused_event, char *context) +{ + char *myname = "trigger_server_accept_socket"; + int listen_fd = (int) context; + int time_left = 0; + int fd; + + if (msg_verbose) + msg_info("%s: trigger arrived", myname); + + /* + * Some buggy systems cause Postfix to lock up. + */ + alarm(1000); + + /* + * Read a message from a socket. Be prepared for accept() to fail because + * some other process already got the connection. The socket is + * non-blocking so we won't get stuck when multiple processes wake up. + * Don't get stuck when the client connects but sends no data. Restart + * the idle timer if this was a false alarm. + */ + if (var_idle_limit > 0) + time_left = event_cancel_timer(trigger_server_timeout, (char *) 0); + if ((fd = sane_accept(listen_fd, (struct sockaddr *) 0, (SOCKADDR_SIZE *) 0)) < 0) { + if (errno != EAGAIN) + msg_fatal("accept connection: %m"); + if (time_left >= 0) + event_request_timer(trigger_server_timeout, (char *) 0, time_left); + return; + } + close_on_exec(fd, CLOSE_ON_EXEC); + if (read_wait(fd, 10) == 0) + trigger_server_wakeup(fd); + else if (time_left >= 0) + event_request_timer(trigger_server_timeout, (char *) 0, time_left); + close(fd); +} + +/* trigger_server_main - the real main program */ + +NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,...) +{ + char *myname = "trigger_server_main"; + char *root_dir = 0; + char *user_name = 0; + int debug_me = 0; + char *service_name = basename(argv[0]); + VSTREAM *stream = 0; + int delay; + int c; + int socket_count = 1; + int fd; + va_list ap; + MAIL_SERVER_INIT_FN pre_init = 0; + MAIL_SERVER_INIT_FN post_init = 0; + MAIL_SERVER_LOOP_FN loop = 0; + int key; + char buf[TRIGGER_BUF_SIZE]; + int len; + char *transport = 0; + + /* + * Process environment options as early as we can. + */ + if (getenv(CONF_ENV_VERB)) + msg_verbose = 1; + if (getenv(CONF_ENV_DEBUG)) + debug_me = 1; + + /* + * Don't die when a process goes away unexpectedly. + */ + signal(SIGPIPE, SIG_IGN); + + /* + * May need this every now and then. + */ + var_procname = mystrdup(basename(argv[0])); + set_config_str(VAR_PROCNAME, var_procname); + + /* + * Initialize logging and exit handler. Do the syslog first, so that its + * initialization completes before we enter the optional chroot jail. + */ + msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY); + if (msg_verbose) + msg_info("daemon started"); + + /* + * Initialize from the configuration file. Allow command-line options to + * override compiled-in defaults or configured parameter values. + */ + read_config(); + va_start(ap, service); + while ((key = va_arg(ap, int)) != 0) { + switch (key) { + case MAIL_SERVER_INT_TABLE: + get_config_int_table(va_arg(ap, CONFIG_INT_TABLE *)); + break; + case MAIL_SERVER_STR_TABLE: + get_config_str_table(va_arg(ap, CONFIG_STR_TABLE *)); + break; + case MAIL_SERVER_BOOL_TABLE: + get_config_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *)); + break; + case MAIL_SERVER_PRE_INIT: + pre_init = va_arg(ap, MAIL_SERVER_INIT_FN); + break; + case MAIL_SERVER_POST_INIT: + post_init = va_arg(ap, MAIL_SERVER_INIT_FN); + break; + case MAIL_SERVER_LOOP: + loop = va_arg(ap, MAIL_SERVER_LOOP_FN); + break; + default: + msg_panic("%s: unknown argument type: %d", myname, key); + } + } + va_end(ap); + + /* + * Pick up policy settings from master process. Shut up error messages to + * stderr, because no-one is going to see them. + */ + opterr = 0; + while ((c = GETOPT(argc, argv, "cDi:m:n:s:St:uv")) > 0) { + switch (c) { + case 'c': + root_dir = var_queue_dir; + break; + case 'D': + debug_me = 1; + break; + case 'i': + if ((var_idle_limit = atoi(optarg)) <= 0) + msg_fatal("invalid max_idle time: %s", optarg); + break; + case 'm': + if ((var_use_limit = atoi(optarg)) <= 0) + msg_fatal("invalid max_use: %s", optarg); + break; + case 'n': + service_name = optarg; + break; + case 's': + if ((socket_count = atoi(optarg)) <= 0) + msg_fatal("invalid socket_count: %s", optarg); + break; + case 'S': + stream = VSTREAM_IN; + break; + case 't': + transport = optarg; + break; + case 'u': + user_name = var_mail_owner; + break; + case 'v': + msg_verbose++; + break; + default: + msg_fatal("invalid option: %c", c); + break; + } + } + + /* + * If not connected to stdin, stdin must not be a terminal. + */ + if (stream == 0 && isatty(STDIN_FILENO)) { + msg_vstream_init(var_procname, VSTREAM_ERR); + msg_fatal("do not run this command by hand"); + } + + /* + * Optionally start the debugger on ourself. + */ + if (debug_me) + debug_process(); + + /* + * Run pre-jail initialization. + */ + if (pre_init) + pre_init(); + + /* + * Optionally, restrict the damage that this process can do. + */ + if (chdir(var_queue_dir) < 0) + msg_fatal("chdir(\"%s\"): %m", var_queue_dir); + resolve_local_init(); + chroot_uid(root_dir, user_name); + + /* + * Run post-jail initialization. + */ + if (post_init) + post_init(); + + /* + * Are we running as a one-shot server with the client connection on + * standard input? + */ + if (stream != 0) { + if ((len = read(vstream_fileno(stream), buf, sizeof(buf))) <= 0) + msg_fatal("read: %m"); + service(buf, len, service_name, argv + optind); + vstream_fflush(stream); + exit(0); + } + + /* + * 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 + * no-one has been talking to us for a configurable amount of time, or + * when the master process terminated abnormally. + */ + trigger_server_service = service; + trigger_server_name = service_name; + trigger_server_argv = argv + optind; + if (var_idle_limit > 0) + event_request_timer(trigger_server_timeout, (char *) 0, var_idle_limit); + for (fd = MASTER_LISTEN_FD; fd < MASTER_LISTEN_FD + socket_count; fd++) { + event_enable_read(fd, trigger_server_accept, (char *) fd); + close_on_exec(fd, CLOSE_ON_EXEC); + } + event_enable_read(MASTER_STATUS_FD, trigger_server_abort, (char *) 0); + close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC); + while (var_use_limit == 0 || use_count < var_use_limit) { + delay = loop ? loop() : -1; + event_loop(delay); + } + exit(0); +} diff --git a/postfix/pickup/.indent.pro b/postfix/pickup/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/pickup/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/pickup/.printfck b/postfix/pickup/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/pickup/.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/pickup/Makefile.in b/postfix/pickup/Makefile.in new file mode 100644 index 000000000..1edbf8c3b --- /dev/null +++ b/postfix/pickup/Makefile.in @@ -0,0 +1,76 @@ +SHELL = /bin/sh +SRCS = pickup.c +OBJS = pickup.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 = pickup +INC_DIR = ../include +LIBS = ../lib/libmaster.a ../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' +pickup.o: pickup.c +pickup.o: ../include/sys_defs.h +pickup.o: ../include/msg.h +pickup.o: ../include/scan_dir.h +pickup.o: ../include/vstring.h +pickup.o: ../include/vbuf.h +pickup.o: ../include/vstream.h +pickup.o: ../include/open_as.h +pickup.o: ../include/set_eugid.h +pickup.o: ../include/mail_queue.h +pickup.o: ../include/mail_open_ok.h +pickup.o: ../include/mymalloc.h +pickup.o: ../include/mail_proto.h +pickup.o: ../include/iostuff.h +pickup.o: ../include/cleanup_user.h +pickup.o: ../include/mail_date.h +pickup.o: ../include/mail_params.h +pickup.o: ../include/config.h +pickup.o: ../include/record.h +pickup.o: ../include/rec_type.h +pickup.o: ../include/mail_server.h diff --git a/postfix/pickup/pickup.c b/postfix/pickup/pickup.c new file mode 100644 index 000000000..ed3aead02 --- /dev/null +++ b/postfix/pickup/pickup.c @@ -0,0 +1,413 @@ +/*++ +/* NAME +/* pickup 8 +/* SUMMARY +/* Postfix local mail pickup +/* SYNOPSIS +/* \fBpickup\fR [generic Postfix daemon options] +/* DESCRIPTION +/* The \fBpickup\fR daemon waits for hints that new mail has been +/* dropped into the world-writable \fBmaildrop\fR directory, and +/* feeds it into the \fBcleanup\fR(8) daemon. +/* Ill-formatted files are deleted without notifying the originator. +/* This program expects to be run from the \fBmaster\fR(8) process +/* manager. +/* STANDARDS +/* .ad +/* .fi +/* None. The \fBpickup\fR daemon does not interact with the outside world. +/* SECURITY +/* .ad +/* .fi +/* The \fBpickup\fR daemon runs with superuser privileges so that it +/* 1) can open a queue file with the rights of the submitting user +/* and 2) can access the Postfix private IPC channels. +/* On the positive side, the program can run chrooted, opens no files +/* for writing, is careful about what files it opens for reading, and +/* does not actually touch any data that is sent to its public service +/* endpoint. +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8). +/* BUGS +/* The \fBpickup\fR daemon copies mail from file to the \fBcleanup\fR(8) +/* daemon. It could avoid message copying overhead by sending a file +/* descriptor instead of file data, but then the already complex +/* \fBcleanup\fR(8) daemon would have to deal with unfiltered user data. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. Use the \fBpostfix reload\fR command after +/* a configuration change. +/* .SH Miscellaneous +/* .ad +/* .fi +/* .IP \fBmail_owner\fR +/* The process privileges used while not opening a \fBmaildrop\fR file. +/* .IP \fBqueue_directory\fR +/* Top-level directory of the Postfix queue. +/* SEE ALSO +/* cleanup(8) message canonicalization +/* master(8) process manager +/* syslogd(8) system logging +/* 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 +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Single-threaded server skeleton. */ + +#include + +/* Application-specific. */ + + /* + * Structure to bundle a bunch of information about a queue file. + */ +typedef struct { + char *id; /* queue file basename */ + struct stat st; /* queue file status */ + char *path; /* name for open/remove */ + char *sender; /* sender address */ +} PICKUP_INFO; + + /* + * What action should be taken after attempting to deliver a message: remove + * the file from the maildrop, or leave it alone. The latter is also used + * for files that are still being written to. + */ +#define REMOVE_MESSAGE_FILE 1 +#define KEEP_MESSAGE_FILE 2 + +/* file_read_error - handle error while reading queue file */ + +static int file_read_error(PICKUP_INFO *info, int type) +{ + msg_warn("uid=%d: unexpected or malformed record type %d", + info->st.st_uid, type); + return (REMOVE_MESSAGE_FILE); +} + +/* cleanup_service_error - handle error writing to cleanup service. */ + +static int cleanup_service_error(PICKUP_INFO *info, int status) +{ + msg_warn("%s: %s", info->path, cleanup_strerror(status)); + return (status == CLEANUP_STAT_BAD ? + REMOVE_MESSAGE_FILE : KEEP_MESSAGE_FILE); +} + +/* copy_segment - copy a record group */ + +static int copy_segment(VSTREAM *qfile, VSTREAM *cleanup, PICKUP_INFO *info, + VSTRING *buf, char *expected) +{ + int type; + int check_first = (*expected == REC_TYPE_CONTENT[0]); + + /* + * Limit the input record size. All front-end programs should protect the + * mail system against unreasonable inputs. This also requires that we + * limit the size of envelope records written by the local posting agent. + * As time stamp we use the scrutinized queue file modification time, and + * ignore the time stamp embedded in the queue file. + */ + for (;;) { + if ((type = rec_get(qfile, buf, var_line_limit)) < 0 + || strchr(expected, type) == 0) + return (file_read_error(info, type)); + if (type == *expected) + break; + if (type == REC_TYPE_FROM) + if (info->sender == 0) + info->sender = mystrdup(vstring_str(buf)); + if (type == REC_TYPE_TIME) + continue; + else { + + /* + * XXX Force an empty record when the queue file content begins + * with whitespace, so that it won't be considered as being part + * of our own Received: header. What an ugly Kluge. + */ + if (check_first) { + check_first = 0; + if (VSTRING_LEN(buf) > 0 && ISSPACE(vstring_str(buf)[0])) + rec_put(cleanup, REC_TYPE_NORM, "", 0); + } + if ((REC_PUT_BUF(cleanup, type, buf)) < 0) + return (cleanup_service_error(info, CLEANUP_STAT_WRITE)); + } + } + return (0); +} + +/* pickup_copy - copy message to cleanup service */ + +static int pickup_copy(VSTREAM *qfile, VSTREAM *cleanup, + PICKUP_INFO *info, VSTRING *buf) +{ + time_t now = time((time_t *) 0); + int status; + + /* + * Protect against time-warped time stamps. Warn about mail that has been + * queued for an excessive amount of time. Allow for some time drift with + * network clients that mount the maildrop remotely - especially clients + * that can't get their daylight savings offsets right. + */ +#define DAY_SECONDS 864000 +#define HOUR_SECONDS 3600 + + if (info->st.st_mtime > now + 2 * HOUR_SECONDS) { + msg_warn("%s: message dated %ld seconds into the future", + info->id, (long) (info->st.st_mtime - now)); + info->st.st_mtime = now; + } else if (info->st.st_mtime < now - DAY_SECONDS) { + msg_warn("%s: message has been queued for %d days", + info->id, (int) (now - info->st.st_mtime) / DAY_SECONDS); + } + + /* + * Make sure the message has a posting-time record. + */ + rec_fprintf(cleanup, REC_TYPE_TIME, "%ld", (long) info->st.st_mtime); + + /* + * Copy the message envelope segment. Allow only those records that we + * expect to see in the envelope section. The envelope segment must + * contain an envelope sender address. + */ + info->sender = 0; + if ((status = copy_segment(qfile, cleanup, info, buf, REC_TYPE_ENVELOPE)) != 0) + return (status); + if (info->sender == 0) { + msg_warn("%s: uid=%d: no envelope sender", info->id, info->st.st_uid); + return (REMOVE_MESSAGE_FILE); + } + msg_info("%s: uid=%d from=<%s>", info->id, + (int) info->st.st_uid, info->sender); + myfree(info->sender); + + /* + * 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_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, + mail_date(info->st.st_mtime)); + + /* + * Copy the message content segment. Allow only those records that we + * expect to see in the message content section. + */ + if ((status = copy_segment(qfile, cleanup, info, buf, REC_TYPE_CONTENT)) != 0) + return (status); + + /* + * Send the segment with information extracted from message headers. At + * this stage of the mail processing pipeline, that segment should be + * empty, so we require that the it is. This record group ends with a + * type REC_TYPE_END record. + */ + rec_fputs(cleanup, REC_TYPE_XTRA, ""); + if ((status = copy_segment(qfile, cleanup, info, buf, REC_TYPE_NOEXTRACT)) != 0) + return (status); + + /* + * There are no errors. Send the end-of-data marker, and get the cleanup + * service completion status. XXX Since the pickup service is unable to + * bounce, the cleanup service can report only soft errors here. + */ + rec_fputs(cleanup, REC_TYPE_END, ""); + if (mail_scan(cleanup, "%d", &status) != 1) + return (cleanup_service_error(info, CLEANUP_STAT_WRITE)); + + /* + * Depending on the cleanup service completion status, delete the message + * file, or try again later. Bounces are dealt with by the cleanup + * service itself. The master process wakes up the cleanup service every + * now and then. + */ + if (status) { + return (cleanup_service_error(info, status)); + } else { + return (REMOVE_MESSAGE_FILE); + } +} + +/* pickup_file - initialize for file copy and cleanup */ + +static int pickup_file(PICKUP_INFO *info) +{ + struct stat st; + VSTRING *buf; + int status; + VSTREAM *qfile; + VSTREAM *cleanup; + int fd; + + /* + * Open the submitted file. If we cannot open it, and we're not having a + * file descriptor leak problem, delete the submitted file, so that we + * won't keep complaining about the same file again and again. XXX + * Perhaps we should save "bad" files elsewhere for further inspection. + * XXX How can we delete a file when open() fails with ENOENT? + */ + fd = open_as(info->path, O_RDONLY | O_NONBLOCK, 0, + info->st.st_uid, info->st.st_gid); + if (fd < 0) { + if (errno != ENOENT) + msg_fatal("open input file %s: %m", info->path); + msg_warn("open input file %s: %m", info->path); + return (REMOVE_MESSAGE_FILE); + } + + /* + * Like safe_open(pat, O_RDONLY, 0), but without any link count checks. + */ + if (fstat(fd, &st) < 0) + msg_fatal("fstat: %m"); + if (st.st_dev != info->st.st_dev + || st.st_ino != info->st.st_ino + || st.st_mode != info->st.st_mode) { + msg_warn("%s: uid %d: file has changed", info->path, st.st_uid); + return (REMOVE_MESSAGE_FILE); + } + qfile = vstream_fdopen(fd, O_RDONLY); + + /* + * Contact the cleanup service and read the queue ID that it has + * allocated. In case of trouble, request that the cleanup service + * bounces its copy of the message. because the original input file is + * not readable by the bounce service. + * + * The actual message copying code is in a separate routine, so that it is + * easier to implement the many possible error exits without forgetting + * to close files, or to release memory. + */ + buf = vstring_alloc(100); + cleanup = mail_connect_wait(MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP); + if (mail_scan(cleanup, "%s", buf) != 1 + || mail_print(cleanup, "%d", CLEANUP_FLAG_BOUNCE) != 0) { + status = KEEP_MESSAGE_FILE; + } else { + info->id = mystrdup(vstring_str(buf)); + status = pickup_copy(qfile, cleanup, info, buf); + } + vstream_fclose(qfile); + vstream_fclose(cleanup); + vstring_free(buf); + myfree(info->id); + return (status); +} + +/* pickup_service - service client */ + +static void pickup_service(char *unused_buf, int unused_len, + char *unused_service, char **argv) +{ + SCAN_DIR *scan; + char *queue_name; + PICKUP_INFO info; + const char *path; + char *id; + int file_count; + + /* + * Sanity check. This service takes no command-line arguments. + */ + if (argv[0]) + msg_fatal("unexpected command-line argument: %s", argv[0]); + + /* + * Skip over things that we don't want to open, such as files that are + * still being written, or garbage. Leave it up to the sysadmin to remove + * garbage. Keep scanning the queue directory until we stop removing + * files from it. + */ + queue_name = MAIL_QUEUE_MAILDROP; /* XXX should be a list */ + do { + file_count = 0; + scan = scan_dir_open(queue_name); + while ((id = scan_dir_next(scan)) != 0) { + if (mail_open_ok(queue_name, id, &info.st, &path) == MAIL_OPEN_YES) { + info.path = mystrdup(path); + if (pickup_file(&info) == REMOVE_MESSAGE_FILE) { + if (REMOVE(info.path)) + msg_warn("remove %s: %m", info.path); + else + file_count++; + } + myfree(info.path); + } + } + scan_dir_close(scan); + } while (file_count); +} + +/* drop_privileges - drop privileges most of the time */ + +static void drop_privileges(void) +{ + set_eugid(var_owner_uid, var_owner_gid); +} + +/* main - pass control to the multi-threaded server skeleton */ + +int main(int argc, char **argv) +{ + + /* + * 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_POST_INIT, drop_privileges, + 0); +} diff --git a/postfix/pipe/.indent.pro b/postfix/pipe/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/pipe/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/pipe/.printfck b/postfix/pipe/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/pipe/.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/pipe/Makefile.in b/postfix/pipe/Makefile.in new file mode 100644 index 000000000..59eb45dfb --- /dev/null +++ b/postfix/pipe/Makefile.in @@ -0,0 +1,85 @@ +SHELL = /bin/sh +SRCS = pipe.c +OBJS = pipe.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= quote_821_local pipe_unalias +PROG = pipe +INC_DIR = ../include +LIBS = ../lib/libmaster.a ../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' +pipe.o: pipe.c +pipe.o: ../include/sys_defs.h +pipe.o: ../include/msg.h +pipe.o: ../include/vstream.h +pipe.o: ../include/vbuf.h +pipe.o: ../include/vstring.h +pipe.o: ../include/argv.h +pipe.o: ../include/htable.h +pipe.o: ../include/dict.h +pipe.o: ../include/iostuff.h +pipe.o: ../include/mymalloc.h +pipe.o: ../include/mac_parse.h +pipe.o: ../include/set_eugid.h +pipe.o: ../include/split_at.h +pipe.o: ../include/stringops.h +pipe.o: ../include/recipient_list.h +pipe.o: ../include/deliver_request.h +pipe.o: ../include/mail_queue.h +pipe.o: ../include/mail_params.h +pipe.o: ../include/config.h +pipe.o: ../include/bounce.h +pipe.o: ../include/defer.h +pipe.o: ../include/deliver_completed.h +pipe.o: ../include/sent.h +pipe.o: ../include/pipe_command.h +pipe.o: ../include/mail_copy.h +pipe.o: ../include/mail_addr.h +pipe.o: ../include/canon_addr.h +pipe.o: ../include/split_addr.h +pipe.o: ../include/mail_server.h diff --git a/postfix/pipe/pipe.c b/postfix/pipe/pipe.c new file mode 100644 index 000000000..408b43f9a --- /dev/null +++ b/postfix/pipe/pipe.c @@ -0,0 +1,676 @@ +/*++ +/* NAME +/* pipe 8 +/* SUMMARY +/* Postfix delivery to external command +/* SYNOPSIS +/* \fBpipe\fR [generic Postfix daemon options] command_attributes... +/* DESCRIPTION +/* The \fBpipe\fR daemon processes requests from the Postfix queue +/* manager to deliver messages to external commands. Each delivery +/* request specifies a queue file, a sender address, a domain or host +/* to deliver to, and one or more recipients. +/* This program expects to be run from the \fBmaster\fR(8) process +/* manager. +/* +/* The \fBpipe\fR daemon updates queue files and marks recipients +/* as finished, or it informs the queue manager that delivery should +/* be tried again at a later time. Delivery problem reports are sent +/* to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate. +/* COMMAND ATTRIBUTE SYNTAX +/* .ad +/* .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)" +/* Optional message processing flags. By default, a message is +/* copied unchanged. +/* .RS +/* .IP \fBF\fR +/* 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 \fB>\fR +/* Prepend \fB>\fR to lines starting with "\fBFrom \fR". This expected +/* by, for example, \fBUUCP\fR software. +/* .RE +/* .IP "\fBuser\fR=\fIusername\fR (required)" +/* 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. +/* .IP "\fBargv\fR=\fIcommand\fR... (required)" +/* The command to be executed. This must be specified as the +/* last command attribute. +/* The command is executed directly, i.e. without interpretation of +/* shell meta characters by a shell command interpreter. +/* .sp +/* In the command argument vector, the following macros are recognized +/* and replaced with corresponding information from the Postfix queue +/* manager delivery request: +/* .RS +/* .IP \fB${\fBextension\fR}\fR +/* This macro expands to the extension part of a recipient address. +/* For example, with an address \fIuser+foo@domain\fR the extension is +/* \fIfoo\fR. +/* A command-line argument that contains \fB${\fBextension\fR}\fR expands +/* into as many command-line arguments as there are recipients. +/* .IP \fB${\fBmailbox\fR}\fR +/* This macro expands to the complete local part of a recipient address. +/* For example, with an address \fIuser+foo@domain\fR the mailbox is +/* \fIuser+foo\fR. +/* A command-line argument that contains \fB${\fBmailbox\fR}\fR +/* expands into as many command-line arguments as there are recipients. +/* .IP \fB${\fBnexthop\fR}\fR +/* This macro expands to the next-hop hostname. +/* .IP \fB${\fBrecipient\fR}\fR +/* This macro expands to the complete recipient address. +/* A command-line argument that contains \fB${\fBrecipient\fR}\fR +/* expands into as many command-line arguments as there are recipients. +/* .IP \fB${\fBsender\fR}\fR +/* This macro expands to the envelope sender address. +/* .IP \fB${\fBuser\fR}\fR +/* This macro expands to the username part of a recipient address. +/* For example, with an address \fIuser+foo@domain\fR the username +/* part is \fIuser\fR. +/* A command-line argument that contains \fB${\fBuser\fR}\fR expands +/* into as many command-line arguments as there are recipients. +/* .RE +/* .PP +/* In addition to the form ${\fIname\fR}, the forms $\fIname\fR and +/* $(\fIname\fR) are also recognized. Specify \fB$$\fR where a single +/* \fB$\fR is wanted. +/* DIAGNOSTICS +/* Command exit status codes are expected to +/* follow the conventions defined in <\fBsysexits.h\fR>. +/* +/* Problems and transactions are logged to \fBsyslogd\fR(8). +/* Corrupted message files are marked so that the queue manager +/* can move them to the \fBcorrupt\fR queue for further inspection. +/* SECURITY +/* .fi +/* .ad +/* This program needs a dual personality 1) to access the private +/* Postfix queue and IPC mechanisms, and 2) to execute external +/* commands as the specified user. It is therefore security sensitive. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. Use the \fBpostfix reload\fR command after +/* a configuration change. +/* .SH Miscellaneous +/* .ad +/* .fi +/* .IP \fBmail_owner\fR +/* The process privileges used while not running an external command. +/* .SH "Resource controls" +/* .ad +/* .fi +/* In the text below, \fItransport\fR is the first field in a +/* \fBmaster.cf\fR entry. +/* .IP \fItransport\fB_destination_concurrency_limit\fR +/* Limit the number of parallel deliveries to the same destination, +/* for delivery via the named \fItransport\fR. The default limit is +/* taken from the \fBdefault_destination_concurrency_limit\fR parameter. +/* The limit is enforced by the Postfix queue manager. +/* .IP \fItransport\fB_destination_recipient_limit\fR +/* Limit the number of recipients per message delivery, for delivery +/* via the named \fItransport\fR. The default limit is taken from +/* the \fBdefault_destination_recipient_limit\fR parameter. +/* The limit is enforced by the Postfix queue manager. +/* .IP \fItransport\fB_time_limit\fR +/* Limit the time for delivery to external command, for delivery via +/* the named \fBtransport\fR. The default limit is taken from the +/* \fBcommand_time_limit\fR parameter. +/* The limit is enforced by the Postfix queue manager. +/* SEE ALSO +/* bounce(8) non-delivery status reports +/* master(8) process manager +/* qmgr(8) queue manager +/* syslogd(8) system logging +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Single server skeleton. */ + +#include + +/* Application-specific. */ + + /* + * The mini symbol table name and keys used for expanding macros in + * command-line arguments. + */ +#define PIPE_DICT_TABLE "pipe_command" /* table name */ +#define PIPE_DICT_NEXTHOP "nexthop" /* key */ +#define PIPE_DICT_RCPT "recipient" /* key */ +#define PIPE_DICT_SENDER "sender"/* key */ +#define PIPE_DICT_USER "user" /* key */ +#define PIPE_DICT_EXTENSION "extension" /* key */ +#define PIPE_DICT_MAILBOX "mailbox" /* key */ + + /* + * Flags used to pass back the type of special parameter found by + * parse_callback. + */ +#define PIPE_FLAG_RCPT (1<<0) +#define PIPE_FLAG_USER (1<<1) +#define PIPE_FLAG_EXTENSION (1<<2) +#define PIPE_FLAG_MAILBOX (1<<3) + + /* + * Tunable parameters. Values are taken from the config file, after + * prepending the service name to _name, and so on. + */ +int var_command_maxtime; /* system-wide */ + + /* + * For convenience. Instead of passing around lists of parameters, bundle + * them up in convenient structures. + */ + + /* + * Structure for service-specific configuration parameters. + */ +typedef struct { + int time_limit; /* per-service time limit */ +} PIPE_PARAMS; + + /* + * 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 */ + int flags; /* mail_copy() flags */ +} PIPE_ATTR; + +/* parse_callback - callback for mac_parse() */ + +static void parse_callback(int type, VSTRING *buf, char *context) +{ + int *expand_flag = (int *) context; + + /* + * See if this command-line argument references a special macro. + */ + if (type == MAC_PARSE_VARNAME) { + if (strcmp(vstring_str(buf), PIPE_DICT_RCPT) == 0) + *expand_flag |= PIPE_FLAG_RCPT; + else if (strcmp(vstring_str(buf), PIPE_DICT_USER) == 0) + *expand_flag |= PIPE_FLAG_USER; + else if (strcmp(vstring_str(buf), PIPE_DICT_EXTENSION) == 0) + *expand_flag |= PIPE_FLAG_EXTENSION; + else if (strcmp(vstring_str(buf), PIPE_DICT_MAILBOX) == 0) + *expand_flag |= PIPE_FLAG_MAILBOX; + } +} + +/* expand_argv - expand macros in the argument vector */ + +static ARGV *expand_argv(char **argv, RECIPIENT_LIST *rcpt_list) +{ + VSTRING *buf = vstring_alloc(100); + ARGV *result; + char **cpp; + int expand_flag; + int i; + char *ext; + + /* + * This appears to be simple operation (replace $name by its expansion). + * However, it becomes complex because a command-line argument that + * references $recipient must expand to as many command-line arguments as + * there are recipients (that's wat programs called by sendmail expect). + * So we parse each command-line argument, and depending on what we find, + * we either expand the argument just once, or we expand it once for each + * recipient. In either case we end up parsing the command-line argument + * twice. The amount of CPU time wasted will be negligible. + * + * Note: we can't use recursive macro expansion here, because recursion + * would screw up mail addresses that contain $ characters. + */ +#define NO 0 +#define STR vstring_str + + result = argv_alloc(1); + for (cpp = argv; *cpp; cpp++) { + expand_flag = 0; + mac_parse(*cpp, parse_callback, (char *) &expand_flag); + if (expand_flag == 0) { /* no $recipient etc. */ + argv_add(result, dict_eval(PIPE_DICT_TABLE, *cpp, NO), ARGV_END); + } else { /* contains $recipient etc. */ + for (i = 0; i < rcpt_list->len; i++) { + + /* + * This argument contains $recipient. + */ + if (expand_flag & PIPE_FLAG_RCPT) { + dict_update(PIPE_DICT_TABLE, PIPE_DICT_RCPT, + rcpt_list->info[i].address); + } + + /* + * 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 @. + * + * Beware: if the user name is blank (e.g. +user@host), the + * argument is suppressed. This is necessary to allow for + * cyrus bulletin-board (global mailbox) delivery. XXX But, + * skipping empty user parts will also prevent other + * expansions of this specific command-line argument. + */ + if (expand_flag & PIPE_FLAG_USER) { + vstring_strcpy(buf, rcpt_list->info[i].address); + if (split_at_right(STR(buf), '@') == 0) + msg_warn("no @ in recipient address: %s", + rcpt_list->info[i].address); + if (*var_rcpt_delim) + split_addr(STR(buf), *var_rcpt_delim); + if (*STR(buf) == 0) + continue; + lowercase(STR(buf)); + dict_update(PIPE_DICT_TABLE, PIPE_DICT_USER, STR(buf)); + } + + /* + * This argument contains $extension. Extract the recipient + * extension: anything between the leftmost extension + * delimiter and the rightmost @. The extension may be blank. + */ + if (expand_flag & PIPE_FLAG_EXTENSION) { + vstring_strcpy(buf, rcpt_list->info[i].address); + if (split_at_right(STR(buf), '@') == 0) + msg_warn("no @ in recipient address: %s", + rcpt_list->info[i].address); + if (*var_rcpt_delim == 0 + || (ext = split_addr(STR(buf), *var_rcpt_delim)) == 0) + ext = ""; /* insert null arg */ + else + lowercase(ext); + dict_update(PIPE_DICT_TABLE, PIPE_DICT_EXTENSION, ext); + } + + /* + * This argument contains $mailbox. Extract the mailbox name: + * anything to the left of the rightmost @. + */ + if (expand_flag & PIPE_FLAG_MAILBOX) { + vstring_strcpy(buf, rcpt_list->info[i].address); + if (split_at_right(STR(buf), '@') == 0) + msg_warn("no @ in recipient address: %s", + rcpt_list->info[i].address); + lowercase(STR(buf)); + dict_update(PIPE_DICT_TABLE, PIPE_DICT_MAILBOX, STR(buf)); + } + argv_add(result, dict_eval(PIPE_DICT_TABLE, *cpp, NO), ARGV_END); + } + } + } + argv_terminate(result); + vstring_free(buf); + return (result); +} + +/* get_service_params - get service-name dependent config information */ + +static void get_service_params(PIPE_PARAMS *config, char *service) +{ + char *myname = "get_service_params"; + + /* + * Figure out the command time limit for this transport. + */ + config->time_limit = + get_config_int2(service, "_time_limit", var_command_maxtime, 1, 0); + + /* + * Give the poor tester a clue of what is going on. + */ + if (msg_verbose) + msg_info("%s: time_limit %d", myname, config->time_limit); +} + +/* get_service_attr - get command-line attributes */ + +static void get_service_attr(PIPE_ATTR *attr, char **argv) +{ + char *myname = "get_service_attr"; + struct passwd *pwd; + char *cp; + + /* + * Initialize. + */ + attr->user = 0; + attr->command = 0; + attr->flags = 0; + + /* + * Iterate over the command-line attribute list. + */ + for ( /* void */ ; *argv != 0; argv++) { + + /* + * flags=stuff + */ + if (strncasecmp("flags=", *argv, sizeof("flags=") - 1) == 0) { + for (cp = *argv + sizeof("flags=") - 1; *cp; cp++) { + switch (*cp) { + case 'F': + attr->flags |= MAIL_COPY_FROM; + break; + case '>': + attr->flags |= MAIL_COPY_QUOTE; + break; + default: + msg_fatal("unknown flag: %c (ignored)", *cp); + break; + } + } + } + + /* + * user=username + */ + 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); + attr->uid = pwd->pw_uid; + attr->gid = pwd->pw_gid; + } + + /* + * argv=command... + */ + else if (strncasecmp("argv=", *argv, sizeof("argv=") - 1) == 0) { + *argv += sizeof("argv=") - 1; /* XXX clobbers argv */ + attr->command = argv; + break; + } + + /* + * Bad. + */ + else + msg_fatal("unknown attribute name: %s", *argv); + } + + /* + * Sanity checks. Verify that every member has an acceptable value. + */ + if (attr->user == 0) + msg_fatal("missing user= attribute"); + if (attr->command == 0) + msg_fatal("missing argv= attribute"); + if (attr->uid == 0) + msg_fatal("request to deliver as root"); + if (attr->uid == var_owner_uid) + msg_fatal("request to deliver as mail system owner"); + if (attr->gid == 0) + msg_fatal("request to use privileged group id %d", attr->gid); + if (attr->gid == var_owner_gid) + msg_fatal("request to use mail system owner group id %d", attr->gid); + + /* + * Give the poor tester a clue of what is going on. + */ + if (msg_verbose) + msg_info("%s: uid %d, gid %d. flags %d", + myname, attr->uid, attr->gid, attr->flags); +} + +/* eval_command_status - do something with command completion status */ + +static int eval_command_status(int command_status, char *service, + DELIVER_REQUEST *request, VSTREAM *src, + char *why) +{ + RECIPIENT *rcpt; + int status; + int result = 0; + int n; + + /* + * Depending on the result, bounce or defer the message, and mark the + * recipient as done where appropriate. + */ + switch (command_status) { + case PIPE_STAT_OK: + for (n = 0; n < request->rcpt_list.len; n++) { + rcpt = request->rcpt_list.info + n; + sent(request->queue_id, rcpt->address, service, + request->arrival_time, "%s", request->nexthop); + deliver_completed(src, rcpt->offset); + } + break; + case PIPE_STAT_BOUNCE: + for (n = 0; n < request->rcpt_list.len; n++) { + rcpt = request->rcpt_list.info + n; + status = bounce_append(BOUNCE_FLAG_KEEP, + request->queue_id, rcpt->address, + service, request->arrival_time, "%s", why); + if (status == 0) + deliver_completed(src, rcpt->offset); + result |= status; + } + break; + case PIPE_STAT_DEFER: + for (n = 0; n < request->rcpt_list.len; n++) { + rcpt = request->rcpt_list.info + n; + result |= defer_append(BOUNCE_FLAG_KEEP, + request->queue_id, rcpt->address, + service, request->arrival_time, "%s", why); + } + break; + default: + msg_panic("eval_command_status: bad status %d", command_status); + /* NOTREACHED */ + } + return (result); +} + +/* deliver_message - deliver message with extreme prejudice */ + +static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv) +{ + char *myname = "deliver_message"; + static PIPE_PARAMS conf; + static PIPE_ATTR attr; + VSTREAM *src; + RECIPIENT_LIST *rcpt_list = &request->rcpt_list; + VSTRING *why = vstring_alloc(100); + VSTRING *buf; + ARGV *expanded_argv; + int deliver_status; + int command_status; + + if (msg_verbose) + msg_info("%s: from <%s>", myname, request->sender); + + /* + * First of all, replace an empty sender address by the mailer daemon + * address. The resolver already fixes empty recipient addresses. + * + * XXX Should sender and recipient be transformed into external (i.e. + * quoted) form? Problem is that the quoting rules are transport + * specific. Such information must evidently not be hard coded into + * Postfix, but would have to be provided in the form of lookup tables. + */ + if (request->sender[0] == 0) { + buf = vstring_alloc(100); + canon_addr_internal(buf, MAIL_ADDR_MAIL_DAEMON); + myfree(request->sender); + request->sender = vstring_export(buf); + } + + /* + * Sanity checks. The get_service_params() and get_service_attr() + * routines also do some sanity checks. Look up service attributes and + * config information only once. This is safe since the information comes + * from a trusted source, not from the delivery request. + */ + if (request->nexthop[0] == 0) + msg_fatal("empty nexthop hostname"); + if (rcpt_list->len <= 0) + msg_fatal("recipient count: %d", rcpt_list->len); + if (attr.user == 0) { + get_service_params(&conf, service); + get_service_attr(&attr, argv); + } + + /* + * Open the queue file. Opening the file can fail for a variety of + * reasons, such as the system running out of resources. Instead of + * throwing away mail, we're raising a fatal error which forces the mail + * system to back off, and retry later. XXX deliver_request() should + * pre-open the queue file while it does all its sanity checks. + */ + src = mail_queue_open(request->queue_name, request->queue_id, O_RDWR, 0); + if (src == 0) + msg_fatal("%s: open %s %s: %m", myname, + request->queue_name, request->queue_id); + if (msg_verbose) + msg_info("%s: file %s", myname, VSTREAM_PATH(src)); + close_on_exec(vstream_fileno(src), CLOSE_ON_EXEC); + + /* + * Deliver. Set the nexthop and sender variables, and expand the command + * argument vector. Recipients will be expanded on the fly. XXX Rewrite + * envelope and header addresses according to transport-specific + * rewriting rules. + */ + if (vstream_fseek(src, request->data_offset, SEEK_SET) < 0) + msg_fatal("seek queue file %s: %m", VSTREAM_PATH(src)); + + dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, request->sender); + dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, request->nexthop); + expanded_argv = expand_argv(attr.command, rcpt_list); + + command_status = pipe_command(src, why, + PIPE_CMD_UID, attr.uid, + PIPE_CMD_GID, attr.gid, + PIPE_CMD_SENDER, request->sender, + PIPE_CMD_COPY_FLAGS, attr.flags, + PIPE_CMD_ARGV, expanded_argv->argv, + PIPE_CMD_TIME_LIMIT, conf.time_limit, + PIPE_CMD_END); + + deliver_status = eval_command_status(command_status, service, request, + src, vstring_str(why)); + + /* + * Clean up. + */ + if (vstream_fclose(src)) + msg_warn("close %s %s: %m", request->queue_name, request->queue_id); + + vstring_free(why); + argv_free(expanded_argv); + + return (deliver_status); +} + +/* pipe_service - perform service for client */ + +static void pipe_service(VSTREAM *client_stream, char *service, char **argv) +{ + DELIVER_REQUEST *request; + int status; + + /* + * This routine runs whenever a client connects to the UNIX-domain socket + * dedicated to delivery via external command. What we see below is a + * little protocol to (1) tell the queue manager that we are ready, (2) + * read a request from the queue manager, and (3) report the completion + * status of that request. All connection-management stuff is handled by + * the common code in single_server.c. + */ + if ((request = deliver_request_read(client_stream)) != 0) { + status = deliver_message(request, service, argv); + deliver_request_done(client_stream, request, status); + } +} + +/* drop_privileges - drop privileges most of the time */ + +static void drop_privileges(void) +{ + set_eugid(var_owner_uid, var_owner_gid); +} + +/* main - pass control to the single-threaded skeleton */ + +int main(int argc, char **argv) +{ + static CONFIG_INT_TABLE int_table[] = { + VAR_COMMAND_MAXTIME, DEF_COMMAND_MAXTIME, &var_command_maxtime, 1, 0, + 0, + }; + + single_server_main(argc, argv, pipe_service, + MAIL_SERVER_INT_TABLE, int_table, + MAIL_SERVER_POST_INIT, drop_privileges, + 0); +} diff --git a/postfix/postalias/.indent.pro b/postfix/postalias/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/postalias/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/postalias/.printfck b/postfix/postalias/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/postalias/.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/postalias/Makefile.in b/postfix/postalias/Makefile.in new file mode 100644 index 000000000..2c1e1e13f --- /dev/null +++ b/postfix/postalias/Makefile.in @@ -0,0 +1,71 @@ +SHELL = /bin/sh +SRCS = postalias.c +OBJS = postalias.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 = postalias +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' +postalias.o: postalias.c +postalias.o: ../include/sys_defs.h +postalias.o: ../include/msg.h +postalias.o: ../include/mymalloc.h +postalias.o: ../include/vstring.h +postalias.o: ../include/vbuf.h +postalias.o: ../include/vstream.h +postalias.o: ../include/msg_vstream.h +postalias.o: ../include/readline.h +postalias.o: ../include/stringops.h +postalias.o: ../include/split_at.h +postalias.o: ../include/tok822.h +postalias.o: ../include/resolve_clnt.h +postalias.o: ../include/config.h +postalias.o: ../include/mail_params.h +postalias.o: ../include/mkmap.h diff --git a/postfix/postalias/postalias.c b/postfix/postalias/postalias.c new file mode 100644 index 000000000..83c28cfb3 --- /dev/null +++ b/postfix/postalias/postalias.c @@ -0,0 +1,343 @@ +/*++ +/* NAME +/* postalias 1 +/* SUMMARY +/* Postfix alias database maintenance +/* SYNOPSIS +/* .fi +/* \fBpostalias\fR [\fB-c \fIconfig_dir\fR] [\fB-i\fR] [\fB-v\fR] +/* [\fIfile_type\fR:]\fIfile_name\fR ... +/* DESCRIPTION +/* The \fBpostalias\fR command creates a new Postfix alias database, +/* or updates an existing one. The input and output file formats +/* are expected to be compatible with Sendmail version 8, and are +/* expected to be suitable for the use as NIS alias maps. +/* +/* While a database update is in progress, signal delivery is +/* postponed, and an exclusive, advisory, lock is placed on the +/* entire database, in order to avoid surprises in spectator +/* programs. +/* +/* Options: +/* .IP "\fB-c \fIconfig_dir\fR" +/* Read the \fBmain.cf\fR configuration file in the named directory. +/* .IP \fB-i\fR +/* Incremental mode. Read entries from standard input and do not +/* truncate an existing database. By default, \fBpostalias\fR creates +/* a new database from the entries in \fBfile_name\fR. +/* .IP \fB-v\fR +/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR +/* options make the software increasingly verbose. +/* .PP +/* Arguments: +/* .IP \fIfile_type\fR +/* The type of database to be produced. +/* .RS +/* .IP \fBbtree\fR +/* The output is a btree file, named \fIfile_name\fB.db\fR. +/* This is available only on systems with support for \fBdb\fR databases. +/* .IP \fBdbm\fR +/* The output consists of two files, named \fIfile_name\fB.pag\fR and +/* \fIfile_name\fB.dir\fR. +/* This is available only on systems with support for \fBdbm\fR databases. +/* .IP \fBhash\fR +/* The output is a hashed file, named \fIfile_name\fB.db\fR. +/* This is available only on systems with support for \fBdb\fR databases. +/* .PP +/* When no \fIfile_type\fR is specified, the software uses the database +/* type specified via the \fBdatabase_type\fR configuration parameter. +/* The default value for this parameter depends on the host environment. +/* .RE +/* .IP \fIfile_name\fR +/* The name of the alias database source file when rebuilding a database. +/* DIAGNOSTICS +/* Problems are logged to the standard error stream. No output means +/* no problems were detected. Duplicate entries are skipped and are +/* flagged with a warning. +/* ENVIRONMENT +/* .ad +/* .fi +/* .IP \fBMAIL_CONFIG\fR +/* Mail configuration database. +/* .IP \fBMAIL_VERBOSE\fR +/* Enable verbose logging for debugging purposes. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. +/* .IP \fBdatabase_type\fR +/* Default alias database type. On many UNIX systems, the default type +/* is either \fBdbm\fR or \fBhash\fR. +/* STANDARDS +/* RFC 822 (ARPA Internet Text Messages) +/* SEE ALSO +/* aliases(5) format of alias database input file. +/* sendmail(1) mail posting and compatibility interface. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +#define STR vstring_str + +/* postalias - create or update alias database */ + +static void postalias(char *map_type, char *path_name, int incremental) +{ + VSTREAM *source_fp; + VSTRING *line_buffer; + MKMAP *mkmap; + int lineno; + VSTRING *key_buffer; + VSTRING *value_buffer; + TOK822 *tok_list; + TOK822 *key_list; + TOK822 *colon; + TOK822 *value_list; + + /* + * Initialize. + */ + line_buffer = vstring_alloc(100); + key_buffer = vstring_alloc(100); + value_buffer = vstring_alloc(100); + if (incremental) { + source_fp = VSTREAM_IN; + vstream_control(source_fp, VSTREAM_CTL_PATH, "stdin", VSTREAM_CTL_END); + } else if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0) { + msg_fatal("open %s: %m", path_name); + } + + /* + * Open the database, create it when it does not exist, truncate it when + * it does exist, and lock out any spectators. + */ + mkmap = mkmap_open(map_type, path_name, incremental ? + O_RDWR | O_CREAT : O_RDWR | O_CREAT | O_TRUNC); + + /* + * Add records to the database. + */ + lineno = 0; + while (readline(line_buffer, source_fp, &lineno)) { + + /* + * Skip comments. + */ + if (*STR(line_buffer) == '#') + continue; + + /* + * Weird stuff. Normally, a line that begins with whitespace is a + * continuation of the previous line. + */ + if (ISSPACE(*STR(line_buffer))) { + msg_warn("%s, line %d: malformed line", + VSTREAM_PATH(source_fp), lineno); + continue; + } + + /* + * Tokenize the input, so that we do the right thing when a quoted + * localpart contains special characters such as "@", ":" and so on. + */ + if ((tok_list = tok822_scan(STR(line_buffer), (TOK822 **) 0)) == 0) + continue; + + /* + * Enforce the key:value format. Disallow missing keys, multi-address + * keys, or missing values. In order to specify an empty string or + * value, enclose it in double quotes. + */ + if ((colon = tok822_find_type(tok_list, ':')) == 0 + || colon->prev == 0 || colon->next == 0 + || tok822_rfind_type(colon, ',')) { + msg_warn("%s, line %d: need name:value pair", + VSTREAM_PATH(source_fp), lineno); + tok822_free_tree(tok_list); + continue; + } + + /* + * Key must be local. XXX We should use the Postfix rewriting and + * resolving services to handle all address forms correctly. However, + * we can't count on the mail system being up when the alias database + * is being built, so we're guessing a bit. + */ + if (tok822_rfind_type(colon, '@') || tok822_rfind_type(colon, '%')) { + msg_warn("%s, line %d: name must be local", + VSTREAM_PATH(source_fp), lineno); + tok822_free_tree(tok_list); + continue; + } + + /* + * Split the input into key and value parts, and convert from token + * representation back to string representation. Convert the key to + * internal (unquoted) form, because the resolver produces addresses + * in internal form. Convert the value to external (quoted) form, + * because it will have to be re-parsed upon lookup. Discard the + * token representation when done. + */ + key_list = tok_list; + tok_list = 0; + value_list = tok822_cut_after(colon); + tok822_unlink(colon); + tok822_free(colon); + + tok822_internalize(key_buffer, key_list, TOK822_STR_DEFL); + tok822_free_tree(key_list); + + tok822_externalize(value_buffer, value_list, TOK822_STR_DEFL); + tok822_free_tree(value_list); + + /* + * Store the value under a case-insensitive key. + */ + lowercase(STR(key_buffer)); + mkmap_append(mkmap, STR(key_buffer), STR(value_buffer)); + } + + /* + * Sendmail compatibility: add the @:@ signature to indicate that the + * database is complete. This might be needed by NIS clients running + * sendmail. + */ + mkmap_append(mkmap, "@", "@"); + + /* + * Close the alias database, and release the lock. + */ + mkmap_close(mkmap); + + /* + * Cleanup. We're about to terminate, but it is a good sanity check. + */ + vstring_free(value_buffer); + vstring_free(key_buffer); + vstring_free(line_buffer); + if (source_fp != VSTREAM_IN) + vstream_fclose(source_fp); +} + +/* usage - explain */ + +static NORETURN usage(char *myname) +{ + msg_fatal("usage: %s [-c config_directory] [-i] [-v] [output_type:]file...", + myname); +} + +int main(int argc, char **argv) +{ + char *path_name; + int ch; + int fd; + char *slash; + struct stat st; + int incremental = 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 are not set-uid, + * and we are supposed to be running in a controlled environment. + */ + if (getenv(CONF_ENV_VERB)) + msg_verbose = 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); + + /* + * Parse JCL. + */ + while ((ch = GETOPT(argc, argv, "c:iv")) > 0) { + switch (ch) { + default: + usage(argv[0]); + break; + case 'c': + if (setenv(CONF_ENV_PATH, optarg, 1) < 0) + msg_fatal("out of memory"); + break; + case 'i': + incremental = 1; + break; + case 'v': + msg_verbose++; + break; + } + } + read_config(); + + /* + * Use the map type specified by the user, or fall back to a default + * database type. + */ + if (optind + 1 > argc) + usage(argv[0]); + while (optind < argc) { + if ((path_name = split_at(argv[optind], ':')) != 0) { + postalias(argv[optind], path_name, incremental); + } else { + postalias(var_db_type, argv[optind], incremental); + } + optind++; + } + exit(0); +} diff --git a/postfix/postcat/.indent.pro b/postfix/postcat/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/postcat/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/postcat/.printfck b/postfix/postcat/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/postcat/.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/postcat/Makefile.in b/postfix/postcat/Makefile.in new file mode 100644 index 000000000..490136661 --- /dev/null +++ b/postfix/postcat/Makefile.in @@ -0,0 +1,65 @@ +SHELL = /bin/sh +SRCS = postcat.c +OBJS = postcat.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 = postcat +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' +postcat.o: postcat.c +postcat.o: ../include/sys_defs.h +postcat.o: ../include/msg.h +postcat.o: ../include/vstream.h +postcat.o: ../include/vbuf.h +postcat.o: ../include/vstring.h +postcat.o: ../include/msg_vstream.h +postcat.o: ../include/vstring_vstream.h +postcat.o: ../include/record.h +postcat.o: ../include/rec_type.h diff --git a/postfix/postcat/postcat.c b/postfix/postcat/postcat.c new file mode 100644 index 000000000..efa74f5a5 --- /dev/null +++ b/postfix/postcat/postcat.c @@ -0,0 +1,199 @@ +/*++ +/* NAME +/* postcat 1 +/* SUMMARY +/* show Postfix queue file contents +/* SYNOPSIS +/* \fBpostcat\fR [\fB-v\fR] [\fIfiles\fR...] +/* DESCRIPTION +/* The \fBpostcat\fR command prints the contents of the named +/* Postfix queue \fIfiles\fR in human-readable form. If no +/* \fIfiles\fR are specified on the command line, the program +/* reads from standard input. +/* +/* Options: +/* .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. +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* Application-specific. */ + +#define STR vstring_str + +/* postcat - visualize Postfix queue file contents */ + +static void postcat(VSTREAM *fp, VSTRING *buffer) +{ + int prev_type = 0; + int rec_type; + time_t time; + int first = 1; + int ch; + +#define TEXT_RECORD(rec_type) \ + (rec_type == REC_TYPE_CONT || rec_type == REC_TYPE_NORM) + + /* + * See if this is a plausible file. + */ + if ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF) { + if (ch != REC_TYPE_TIME && ch != REC_TYPE_SIZE) { + msg_warn("%s: input is not a valid queue file", VSTREAM_PATH(fp)); + return; + } + vstream_ungetc(fp, ch); + } + + /* + * Now look at the rest. + */ + for (;;) { + rec_type = rec_get(fp, buffer, 0); + if (rec_type == REC_TYPE_ERROR) + msg_fatal("record read error"); + if (rec_type == REC_TYPE_EOF) + return; + if (first == 1) { + vstream_printf("*** ENVELOPE RECORDS %s ***\n", VSTREAM_PATH(fp)); + first = 0; + } + if (prev_type == REC_TYPE_CONT && !TEXT_RECORD(rec_type)) + VSTREAM_PUTCHAR('\n'); + switch (rec_type) { + case REC_TYPE_SIZE: + vstream_printf("message_size: %s\n", STR(buffer)); + break; + case REC_TYPE_TIME: + time = atol(STR(buffer)); + vstream_printf("arrival_time: %s", asctime(localtime(&time))); + break; + case REC_TYPE_CONT: + vstream_printf("%s", STR(buffer)); + break; + case REC_TYPE_NORM: + vstream_printf("%s\n", STR(buffer)); + break; + case REC_TYPE_MESG: + vstream_printf("*** MESSAGE CONTENTS %s ***\n", VSTREAM_PATH(fp)); + break; + case REC_TYPE_XTRA: + vstream_printf("*** HEADER EXTRACTED %s ***\n", VSTREAM_PATH(fp)); + break; + case REC_TYPE_END: + vstream_printf("*** MESSAGE FILE END %s ***\n", VSTREAM_PATH(fp)); + break; + default: + vstream_printf("%s: %s\n", rec_type_name(rec_type), STR(buffer)); + break; + } + prev_type = rec_type; + vstream_fflush(VSTREAM_OUT); + } +} + +/* usage - explain and terminate */ + +static NORETURN usage(char *myname) +{ + msg_fatal("usage: %s [-v] [file(s)...]", myname); +} + +int main(int argc, char **argv) +{ + VSTRING *buffer; + VSTREAM *fp; + int ch; + int fd; + struct stat st; + + /* + * 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"); + + /* + * Set up logging. + */ + msg_vstream_init(argv[0], VSTREAM_ERR); + + /* + * Parse JCL. + */ + while ((ch = GETOPT(argc, argv, "v")) > 0) { + switch (ch) { + case 'v': + msg_verbose++; + break; + default: + usage(argv[0]); + } + } + + /* + * Initialize. + */ + buffer = vstring_alloc(10); + + /* + * If no file names are given, copy stdin. + */ + if (argc == optind) { + vstream_control(VSTREAM_IN, + VSTREAM_CTL_PATH, "stdin", + VSTREAM_CTL_END); + postcat(VSTREAM_IN, buffer); + } + + /* + * Copy the named files in the specified order. + */ + else { + while (optind < argc) { + if ((fp = vstream_fopen(argv[optind], O_RDONLY, 0)) == 0) + msg_fatal("open %s: %m", argv[optind]); + postcat(fp, buffer); + if (vstream_fclose(fp)) + msg_warn("close %s: %m", argv[optind]); + optind++; + } + } + vstring_free(buffer); + exit(0); +} diff --git a/postfix/postconf/.indent.pro b/postfix/postconf/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/postconf/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/postconf/.printfck b/postfix/postconf/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/postconf/.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/postconf/Makefile.in b/postfix/postconf/Makefile.in new file mode 100644 index 000000000..f532f954d --- /dev/null +++ b/postfix/postconf/Makefile.in @@ -0,0 +1,91 @@ +SHELL = /bin/sh +SRCS = postconf.c +OBJS = postconf.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= +MAKES = bool_table.h bool_vars.h int_table.h int_vars.h str_table.h \ + str_vars.h +PROG = postconf +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 + +$(MAKES): + sh extract.sh ../*/*.c + +printfck: $(OBJS) $(PROG) + rm -rf printfck + mkdir printfck + cp *.h 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 $(MAKES) + 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' +postconf.o: postconf.c +postconf.o: ../include/sys_defs.h +postconf.o: ../include/msg.h +postconf.o: ../include/vstream.h +postconf.o: ../include/vbuf.h +postconf.o: ../include/msg_vstream.h +postconf.o: ../include/get_hostname.h +postconf.o: ../include/stringops.h +postconf.o: ../include/htable.h +postconf.o: ../include/dict.h +postconf.o: ../include/safe.h +postconf.o: ../include/mymalloc.h +postconf.o: ../include/line_wrap.h +postconf.o: ../include/mynetworks.h +postconf.o: ../include/config.h +postconf.o: ../include/mail_proto.h +postconf.o: ../include/iostuff.h +postconf.o: ../include/mail_version.h +postconf.o: ../include/mail_params.h +postconf.o: ../include/mail_addr.h +postconf.o: bool_vars.h +postconf.o: int_vars.h +postconf.o: str_vars.h +postconf.o: local_vars.h +postconf.o: smtp_vars.h +postconf.o: bool_table.h +postconf.o: int_table.h +postconf.o: str_table.h +postconf.o: local_table.h +postconf.o: smtp_table.h diff --git a/postfix/postconf/extract.sh b/postfix/postconf/extract.sh new file mode 100644 index 000000000..69323b484 --- /dev/null +++ b/postfix/postconf/extract.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# Extract initialization tables from actual source code. + +awk ' +/static CONFIG_INT_TABLE/,/};/ { + if ($1 ~ /VAR/) { + print "int " substr($3,2,length($3)-2) ";" > "int_vars.h" + print | "sort -u >int_table.h" + } +} +/static CONFIG_STR_TABLE/,/};/ { + if ($1 ~ /VAR/) { + print "char *" substr($3,2,length($3)-2) ";" > "str_vars.h" + print | "sort -u >str_table.h" + } +} +/static CONFIG_BOOL_TABLE/,/};/ { + if ($1 ~ /VAR/) { + print "int " substr($3,2,length($3)-2) ";" > "bool_vars.h" + print | "sort -u >bool_table.h" + } +} +' $* diff --git a/postfix/postconf/local_table.h b/postfix/postconf/local_table.h new file mode 100644 index 000000000..fd9969748 --- /dev/null +++ b/postfix/postconf/local_table.h @@ -0,0 +1,2 @@ + "local_destination_concurrency_limit", "$default_destination_concurrency_limit", &var_local_destination_concurrency_limit, 0, 0, + "local_destination_recipient_limit", "$default_destination_recipient_limit", &var_local_destination_recipient_limit, 0, 0, diff --git a/postfix/postconf/local_vars.h b/postfix/postconf/local_vars.h new file mode 100644 index 000000000..aa4ee57e1 --- /dev/null +++ b/postfix/postconf/local_vars.h @@ -0,0 +1,2 @@ +char *var_local_destination_concurrency_limit; +char *var_local_destination_recipient_limit; diff --git a/postfix/postconf/postconf.c b/postfix/postconf/postconf.c new file mode 100644 index 000000000..a38c895a7 --- /dev/null +++ b/postfix/postconf/postconf.c @@ -0,0 +1,468 @@ +/*++ +/* NAME +/* postconf 1 +/* SUMMARY +/* Postfix configuration utility +/* SYNOPSIS +/* .fi +/* \fBpostconf\fR [\fB-d\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). +/* +/* Options: +/* .IP \fB-d\fR +/* Print default parameter settings instead of actual settings. +/* .IP \fB-n\fR +/* Print non-default parameter settings only. +/* .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. +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include + + /* + * What output we should generate. + */ +#define SHOW_NONDEF (1<<0) /* show non-default settings */ +#define SHOW_DEFS (1<<1) /* show default setting */ + + /* + * Lookup table for in-core parameter info. + */ +HTABLE *param_table; + + /* + * Lookup table for external parameter info. + */ +DICT *text_table; + + /* + * Declarations generated by scanning actual C source files. + */ +#include "bool_vars.h" +#include "int_vars.h" +#include "str_vars.h" + + /* + * Manually extracted. + */ +#include "local_vars.h" +#include "smtp_vars.h" + + /* + * Lookup tables generated by scanning actual C source files. + */ +static CONFIG_BOOL_TABLE bool_table[] = { +#include "bool_table.h" + 0, +}; + +static CONFIG_INT_TABLE int_table[] = { +#include "int_table.h" + 0, +}; + +static CONFIG_STR_TABLE str_table[] = { +#include "str_table.h" +#include "local_table.h" /* XXX */ +#include "smtp_table.h" /* XXX */ + 0, +}; + + /* + * Parameters with default values obtained via function calls. + */ +char *var_myhostname; +char *var_mydomain; +char *var_mynetworks; + +static const char *check_myhostname(void); +static const char *check_mydomainname(void); +static const char *check_mynetworks(void); + +static CONFIG_STR_FN_TABLE str_fn_table[] = { + VAR_MYHOSTNAME, check_myhostname, &var_myhostname, 1, 0, + VAR_MYDOMAIN, check_mydomainname, &var_mydomain, 1, 0, + 0, +}; +static CONFIG_STR_FN_TABLE str_fn_table_2[] = { + VAR_MYNETWORKS, check_mynetworks, &var_mynetworks, 1, 0, + 0, +}; + +/* check_myhostname - lookup hostname and validate */ + +static const char *check_myhostname(void) +{ + return (get_hostname()); +} + +/* get_myhostname - look up and store my hostname */ + +static void get_myhostname(void) +{ + const char *name; + + if ((name = dict_lookup(CONFIG_DICT, VAR_MYHOSTNAME)) == 0) + name = check_myhostname(); + var_myhostname = mystrdup(name); +} + +/* check_mydomainname - lookup domain name and validate */ + +static const char *check_mydomainname(void) +{ + char *dot; + + /* + * Use the hostname when it is not a FQDN ("foo"), or when the hostname + * actually is a domain name ("foo.com"). + */ + if (var_myhostname == 0) + get_myhostname(); + if ((dot = strchr(var_myhostname, '.')) == 0 || strchr(dot + 1, '.') == 0) + return (var_myhostname); + return (dot + 1); +} + +/* check_mynetworks - lookup network address list */ + +static const char *check_mynetworks(void) +{ + if (var_inet_interfaces == 0) + var_inet_interfaces = mystrdup(DEF_INET_INTERFACES); + return (mynetworks()); +} + +/* read_parameters - read parameter info from file */ + +static void read_parameters(void) +{ + char *config_dir; + char *path; + + /* + * A direct rip-off of read_config(). XXX Avoid code duplication by + * better code decomposition. + */ + dict_unknown_allowed = 1; + if (var_config_dir) + myfree(var_config_dir); + var_config_dir = mystrdup((config_dir = safe_getenv(CONF_ENV_PATH)) != 0 ? + config_dir : DEF_CONFIG_DIR); /* XXX */ + set_config_str(VAR_CONFIG_DIR, var_config_dir); + path = concatenate(var_config_dir, "/", "main.cf", (char *) 0); + dict_load_file(CONFIG_DICT, path); + myfree(path); +} + +/* hash_parameters - hash all parameter names so we can find and sort them */ + +static void hash_parameters(void) +{ + CONFIG_BOOL_TABLE *cbt; + CONFIG_INT_TABLE *cit; + CONFIG_STR_TABLE *cst; + CONFIG_STR_FN_TABLE *csft; + + param_table = htable_create(100); + + for (cbt = bool_table; cbt->name; cbt++) + htable_enter(param_table, cbt->name, (char *) cbt); + for (cit = int_table; cit->name; cit++) + htable_enter(param_table, cit->name, (char *) cit); + for (cst = str_table; cst->name; cst++) + htable_enter(param_table, cst->name, (char *) cst); + for (csft = str_fn_table; csft->name; csft++) + htable_enter(param_table, csft->name, (char *) csft); + for (csft = str_fn_table_2; csft->name; csft++) + htable_enter(param_table, csft->name, (char *) csft); +} + +/* print_bool - print boolean parameter */ + +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"); + } 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"); + } else { + vstream_printf("%s = %s\n", cbt->name, value); + } + } else { + if (value != 0) + vstream_printf("%s = %s\n", cbt->name, value); + } + } +} + +/* print_int - print integer parameter */ + +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); + } 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); + } else { + vstream_printf("%s = %s\n", cit->name, value); + } + } else { + if (value != 0) + vstream_printf("%s = %s\n", cit->name, value); + } + } +} + +/* print_str - print string parameter */ + +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); + } 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); + } else { + vstream_printf("%s = %s\n", cst->name, value); + } + } else { + if (value != 0) + vstream_printf("%s = %s\n", cst->name, value); + } + } +} + +/* print_str_fn - print string-function parameter */ + +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()); + } 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()); + } else { + vstream_printf("%s = %s\n", csft->name, value); + } + } else { + if (value != 0) + vstream_printf("%s = %s\n", csft->name, value); + } + } +} + +/* print_str_fn_2 - print string-function parameter */ + +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()); + } 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()); + } else { + vstream_printf("%s = %s\n", csft->name, value); + } + } else { + if (value != 0) + vstream_printf("%s = %s\n", csft->name, value); + } + } +} + +/* print_parameter - show specific parameter */ + +static void print_parameter(int mode, char *ptr) +{ + +#define INSIDE(p,t) (ptr >= (char *) t && ptr < ((char *) t) + sizeof(t)) + + /* + * This is gross, but the best we can do on short notice. + */ + if (INSIDE(ptr, bool_table)) + print_bool(mode, (CONFIG_BOOL_TABLE *) ptr); + if (INSIDE(ptr, int_table)) + print_int(mode, (CONFIG_INT_TABLE *) ptr); + if (INSIDE(ptr, str_table)) + print_str(mode, (CONFIG_STR_TABLE *) ptr); + if (INSIDE(ptr, str_fn_table)) + print_str_fn(mode, (CONFIG_STR_FN_TABLE *) ptr); + if (INSIDE(ptr, str_fn_table_2)) + print_str_fn_2(mode, (CONFIG_STR_FN_TABLE *) ptr); + if (msg_verbose) + vstream_fflush(VSTREAM_OUT); +} + +/* comp_names - qsort helper */ + +static int comp_names(const void *a, const void *b) +{ + HTABLE_INFO **ap = (HTABLE_INFO **) a; + HTABLE_INFO **bp = (HTABLE_INFO **) b; + + return (strcmp(ap[0]->key, bp[0]->key)); +} + +/* show_parameters - show parameter info */ + +static void show_parameters(int mode, char **names) +{ + HTABLE_INFO **list; + HTABLE_INFO **ht; + char **namep; + char *value; + + /* + * Show all parameters. + */ + if (*names == 0) { + list = htable_list(param_table); + qsort((char *) list, param_table->used, sizeof(*list), comp_names); + for (ht = list; *ht; ht++) + print_parameter(mode, ht[0]->value); + myfree((char *) list); + return; + } + + /* + * Show named parameters. + */ + for (namep = names; *namep; namep++) { + if ((value = htable_find(param_table, *namep)) == 0) { + msg_warn("%s: unknown parameter", *namep); + } else { + print_parameter(mode, value); + } + } +} + +/* main */ + +int main(int argc, char **argv) +{ + int ch; + int mode = 0; + int fd; + struct stat st; + + /* + * 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"); + + /* + * Set up logging. + */ + msg_vstream_init(argv[0], VSTREAM_ERR); + + /* + * Parse JCL. + */ + while ((ch = GETOPT(argc, argv, "dnv")) > 0) { + switch (ch) { + case 'd': + if (mode & SHOW_NONDEF) + msg_fatal("specify one of -d and -n"); + mode |= SHOW_DEFS; + break; + case 'n': + if (mode & SHOW_DEFS) + msg_fatal("specify one of -d and -n"); + mode |= SHOW_NONDEF; + break; + case 'v': + msg_verbose++; + break; + default: + msg_fatal("usage: %s [-d (show default)] [-n (show non-default)] [-v] name...", argv[0]); + } + } + + /* + * If showing non-default values, read main.cf. + */ + if ((mode & SHOW_DEFS) == 0) + read_parameters(); + + /* + * Throw together all parameters and show the asked values. + */ + hash_parameters(); + show_parameters(mode, argv + optind); + vstream_fflush(VSTREAM_OUT); + exit(0); +} diff --git a/postfix/postconf/smtp_table.h b/postfix/postconf/smtp_table.h new file mode 100644 index 000000000..17918ee7e --- /dev/null +++ b/postfix/postconf/smtp_table.h @@ -0,0 +1,2 @@ + "smtp_destination_concurrency_limit", "$default_destination_concurrency_limit", &var_smtp_destination_concurrency_limit, 0, 0, + "smtp_destination_recipient_limit", "$default_destination_recipient_limit", &var_smtp_destination_recipient_limit, 0, 0, diff --git a/postfix/postconf/smtp_vars.h b/postfix/postconf/smtp_vars.h new file mode 100644 index 000000000..a24e9ca0c --- /dev/null +++ b/postfix/postconf/smtp_vars.h @@ -0,0 +1,2 @@ +char *var_smtp_destination_concurrency_limit; +char *var_smtp_destination_recipient_limit; diff --git a/postfix/postdrop/.indent.pro b/postfix/postdrop/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/postdrop/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/postdrop/.printfck b/postfix/postdrop/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/postdrop/.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/postdrop/Makefile.in b/postfix/postdrop/Makefile.in new file mode 100644 index 000000000..de5ada7b5 --- /dev/null +++ b/postfix/postdrop/Makefile.in @@ -0,0 +1,75 @@ +SHELL = /bin/sh +SRCS = postdrop.c +OBJS = postdrop.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 = postdrop +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' +postdrop.o: postdrop.c +postdrop.o: ../include/sys_defs.h +postdrop.o: ../include/msg.h +postdrop.o: ../include/mymalloc.h +postdrop.o: ../include/vstream.h +postdrop.o: ../include/vbuf.h +postdrop.o: ../include/vstring.h +postdrop.o: ../include/msg_vstream.h +postdrop.o: ../include/msg_syslog.h +postdrop.o: ../include/mail_proto.h +postdrop.o: ../include/iostuff.h +postdrop.o: ../include/mail_queue.h +postdrop.o: ../include/mail_params.h +postdrop.o: ../include/config.h +postdrop.o: ../include/mail_task.h +postdrop.o: ../include/clean_env.h +postdrop.o: ../include/mail_stream.h +postdrop.o: ../include/cleanup_user.h +postdrop.o: ../include/record.h +postdrop.o: ../include/rec_type.h diff --git a/postfix/postdrop/postdrop.c b/postfix/postdrop/postdrop.c new file mode 100644 index 000000000..4ff41f77b --- /dev/null +++ b/postfix/postdrop/postdrop.c @@ -0,0 +1,294 @@ +/*++ +/* NAME +/* postdrop 1 +/* SUMMARY +/* Postfix mail posting agent +/* SYNOPSIS +/* \fBpostdrop\fR [\fIoption ...\fR] +/* DESCRIPTION +/* The \fBpostdrop\fR command creates a file in the \fBmaildrop\fR +/* directory and copies its standard input to the file. +/* +/* The command is designed to run with set-gid privileges, and with +/* 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. +/* +/* Options: +/* .IP \fB-v\fR +/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR +/* options make the software increasingly verbose. +/* SECURITY +/* .ad +/* .fi +/* This program is designed so that it can run with set-user (or +/* group) id privileges. +/* DIAGNOSTICS +/* Fatal errors: malformed input, I/O error, out of memory. Problems +/* are logged to \fBsyslogd\fR(8) and to the standard error stream. +/* When the input is incomplete, or when the process receives a HUP, +/* INT, QUIT or TERM signal, the queue file is deleted. +/* ENVIRONMENT +/* .ad +/* .fi +/* The program deletes all environment information, because the C +/* library can't be trusted. +/* FILES +/* /var/spool/postfix, mail queue +/* /etc/postfix, configuration files +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* See the Postfix \fBmain.cf\fR file for syntax details and for +/* default values. Use the \fBpostfix reload\fR command after a +/* configuration change. +/* .IP \fBqueue_directory\fR +/* Top-level directory of the Postfix queue. This is also the root +/* directory of Postfix daemons that run chrooted. +/* SEE ALSO +/* sendmail(1) compatibility interface +/* syslogd(8) system logging +/* 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 /* remove() */ +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + + /* + * WARNING WARNING WARNING + * + * This software is designed to run set-gid on systems that cannot afford a + * world-writable spool directory. In order to make this restriction work, + * this software should not run any external commands, nor should it take + * any configuration information from the user. + */ + + /* + * Queue file name. Global, so that the cleanup routine can find it when + * called by the run-time error handler. + */ +static char *postdrop_path; + +/* postdrop_cleanup - callback for the runtime error handler */ + +static void postdrop_cleanup(void) +{ + + /* + * This is the fatal error handler. Don't try to do anything fancy. + */ + if (postdrop_path) { + if (remove(postdrop_path)) + msg_warn("uid=%d: remove %s: %m", getuid(), postdrop_path); + else if (msg_verbose) + msg_info("remove %s", postdrop_path); + postdrop_path = 0; + } +} + +/* postdrop_sig - catch signal and clean up */ + +static void postdrop_sig(int sig) +{ + postdrop_cleanup(); + exit(sig); +} + +/* main - the main program */ + +int main(int argc, char **argv) +{ + struct stat st; + int fd; + int c; + VSTRING *buf; + int status; + MAIL_STREAM *dst; + int rec_type; + static char *segment_info[] = { + REC_TYPE_ENVELOPE, REC_TYPE_CONTENT, REC_TYPE_EXTRACT, + }; + char **expected; + uid_t uid = getuid(); + + /* + * 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"); + + /* + * Strip the environment so we don't have to trust the C library. + */ + clean_env(); + + /* + * Set up logging. Censor the process name: it is provided by the user. + */ + argv[0] = "postdrop"; + 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 the global configuration file and extract configuration + * information. Some claim that the user should supply the working + * directory instead. That might be OK, given that this command needs + * write permission in a subdirectory called "maildrop". However we still + * need to reliably detect incomplete input, and so we must perform + * record-level I/O. With that, we should also take the opportunity to + * perform some sanity checks on the input. + */ + read_config(); + if (chdir(var_queue_dir)) + msg_fatal("chdir %s: %m", var_queue_dir); + if (msg_verbose) + msg_info("chdir %s", var_queue_dir); + + /* + * Set up signal handlers and a runtime error handler so that we can + * clean up incomplete output. + */ + signal(SIGPIPE, SIG_IGN); + + signal(SIGHUP, postdrop_sig); + signal(SIGINT, postdrop_sig); + signal(SIGQUIT, postdrop_sig); + signal(SIGTERM, postdrop_sig); + msg_cleanup(postdrop_cleanup); + + /* + * Parse JCL. + */ + while ((c = GETOPT(argc, argv, "v")) > 0) { + switch (c) { + case 'v': + msg_verbose++; + break; + default: + msg_fatal("usage: %s [-v]", argv[0]); + } + } + + /* + * Create queue file. mail_stream_file() never fails. Send the queue ID + * to the caller. Stash away a copy of the queue file name so we can + * clean up in case of a fatal error or an interrupt. + */ + dst = mail_stream_file(MAIL_QUEUE_MAILDROP, MAIL_CLASS_PUBLIC, + MAIL_SERVICE_PICKUP); + mail_print(VSTREAM_OUT, "%s", dst->id); + vstream_fflush(VSTREAM_OUT); + postdrop_path = mystrdup(VSTREAM_PATH(dst->stream)); + + /* + * Copy stdin to file. The format is checked so that we can recognize + * incomplete input and cancel the operation. With the sanity checks + * applied here, the pickup daemon could skip format checks and pass a + * file descriptor to the cleanup daemon. These are by no means all + * sanity checks - the cleanup service and queue manager services will + * reject messages that lack required information. + */ + vstream_control(VSTREAM_IN, VSTREAM_CTL_PATH, "stdin", VSTREAM_CTL_END); + buf = vstring_alloc(100); + expected = segment_info; + for (;;) { + rec_type = rec_get(VSTREAM_IN, buf, var_line_limit); + if (rec_type == REC_TYPE_EOF) { /* request cancelled */ + mail_stream_cleanup(dst); + if (remove(postdrop_path)) + msg_warn("uid=%d: remove %s: %m", getuid(), postdrop_path); + else if (msg_verbose) + msg_info("remove %s", postdrop_path); + myfree(postdrop_path); + postdrop_path = 0; + exit(0); + } + if (rec_type == REC_TYPE_ERROR) + msg_fatal("uid=%d: malformed input", uid); + if (rec_type == REC_TYPE_TIME) + rec_fprintf(dst->stream, REC_TYPE_TIME, "%ld", + (long) time((time_t *) 0)); + if (strchr(*expected, rec_type) == 0) + msg_fatal("uid=%d: unexpected record type: %d", uid, rec_type); + if (rec_type == **expected) + expected++; + if (REC_PUT_BUF(dst->stream, rec_type, buf) < 0) + msg_fatal("uid=%d: queue file write error", uid); + if (rec_type == REC_TYPE_END) + break; + } + vstring_free(buf); + + /* + * Finish the file. + */ + if ((status = mail_stream_finish(dst)) != 0) + msg_fatal("uid=%d: %s", uid, cleanup_strerror(status)); + + /* + * Disable deletion on fatal error before reporting success, so the file + * will not be deleted after we have taken responsibility for delivery. + */ + if (postdrop_path) { + myfree(postdrop_path); + postdrop_path = 0; + } + + /* + * Send the completion status to the caller and terminate. + */ + mail_print(VSTREAM_OUT, "%d", status); + vstream_fflush(VSTREAM_OUT); + exit(status); +} diff --git a/postfix/postfix/.indent.pro b/postfix/postfix/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/postfix/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/postfix/.printfck b/postfix/postfix/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/postfix/.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/postfix/Makefile.in b/postfix/postfix/Makefile.in new file mode 100644 index 000000000..c195be500 --- /dev/null +++ b/postfix/postfix/Makefile.in @@ -0,0 +1,69 @@ +SHELL = /bin/sh +SRCS = postfix.c +OBJS = postfix.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) +FILES = Makefile $(SRCS) $(HDRS) +INC_DIR = ../include +TESTPROG= +PROG = postfix +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` + +shar: + @shar $(FILES) + +lint: + lint $(SRCS) + +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' +postfix.o: postfix.c +postfix.o: ../include/sys_defs.h +postfix.o: ../include/vstream.h +postfix.o: ../include/vbuf.h +postfix.o: ../include/msg.h +postfix.o: ../include/msg_vstream.h +postfix.o: ../include/msg_syslog.h +postfix.o: ../include/stringops.h +postfix.o: ../include/config.h +postfix.o: ../include/mail_params.h diff --git a/postfix/postfix/postfix.c b/postfix/postfix/postfix.c new file mode 100644 index 000000000..a6e469bf0 --- /dev/null +++ b/postfix/postfix/postfix.c @@ -0,0 +1,231 @@ +/*++ +/* NAME +/* postfix 1 +/* SUMMARY +/* Postfix control program +/* SYNOPSIS +/* .fi +/* \fBpostfix\fR [\fB-c \fIconfig_dir\fR] [\fB-D\fR] [\fB-v\fR] +/* \fIcommand\fR +/* DESCRIPTION +/* The \fBpostfix\fR command controls the operation of the Postfix +/* mail system: start or stop the \fBmaster\fR daemon, do a health +/* check, and other maintenance. The command sets up a standardized +/* environment and runs the \fBpostfix-script\fR shell script to +/* do the actual work. +/* +/* The following commands are implemented: +/* .IP \fBcheck\fR +/* Validate the Postfix mail system configuration. Warn about bad +/* directory/file ownership or permissions, and create missing +/* directories. +/* .IP \fBstart\fR +/* Start the Postfix mail system. This also runs the configuration +/* check described above. +/* .IP \fBstop\fR +/* Stop the Postfix mail system in an orderly fashion. Running processes +/* are allowed to terminate at their earliest convenience. +/* .sp +/* Note: in order to refresh the Postfix mail system after a +/* configuration change, do not use the \fBstart\fR and \fBstop\fR +/* commands in succession. Use the \fBreload\fR command instead. +/* .IP \fBabort\fR +/* Stop the Postfix mail system abruptly. Running processes are +/* signaled to stop immediately. +/* .IP \fBflush\fR +/* Force delivery: attempt to deliver every message in the deferred +/* mail queue. Normally, attempts to deliver delayed mail happen at +/* regular intervals, the interval doubling after each failed attempt. +/* .IP \fBreload\fR +/* Re-read configuration files. Running processes terminate at their +/* earliest convenience. +/* .PP +/* The following options are implemented: +/* .IP "\fB-c \fIconfig_dir\fR" +/* The absolute path to a directory with Postfix configuration files. +/* Use this to distinguish between multiple Postfix instances on the +/* same host. +/* .IP "\fB-D\fR (with \fBpostfix start\fR only)" +/* Run each Postfix daemon under control of a debugger as specified +/* via the \fBdebugger_command\fR configuration parameter. +/* .IP \fB-v\fR +/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR +/* options make the software increasingly verbose. +/* ENVIRONMENT +/* .ad +/* .fi +/* The \fBpostfix\fR command sets the following environment +/* variables: +/* .IP \fBMAIL_CONFIG\fR +/* The Postfix configuration directory. +/* .IP \fBMAIL_VERBOSE\fR +/* This is set when the -v command-line option is present. +/* .IP \fBMAIL_DEBUG\fR +/* This is set when the -D command-line option is present. +/* .PP +/* The following configuration parameters are made available +/* as process environment variables with the same names: +/* .IP \fBcommand_directory\fR +/* The directory with Postfix support commands (default: +/* \fB$program_directory\fR). +/* .IP \fBdaemon_directory\fR +/* The directory with Postfix daemon programs (default: +/* \fB$program_directory\fR). +/* .IP \fBconfig_directory\fR +/* The directory with configuration files and with administrative +/* shell scripts. +/* .IP \fBqueue_directory\fR +/* The directory with the Postfix queue directory (and with some +/* files needed for programs running in a chrooted environment). +/* .IP \fBmail_owner\fR +/* The owner of the Postfix queue and of most Postfix processes. +/* FILES +/* $\fBconfig_directory/postfix-script\fR, administrative commands +/* SEE ALSO +/* master(8) Postfix master program +/* 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 +#include +#ifdef USE_PATHS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* check_setenv - setenv() with extreme prejudice */ + +static void check_setenv(char *name, char *value) +{ +#define CLOBBER 1 + if (setenv(name, value, CLOBBER) < 0) + msg_fatal("setenv: %m"); +} + +/* main - run administrative script from controlled environment */ + +int main(int argc, char **argv) +{ + char *script; + struct stat st; + char *slash; + int uid; + int fd; + int ch; + + /* + * Be consistent with file permissions. + */ + umask(022); + + /* + * To minimize confusion, make sure that the standard file descriptors + * are open. + */ + for (fd = 0; fd < 3; fd++) + if (fstat(fd, &st) == -1 && open("/dev/null", 2) != fd) + msg_fatal("open /dev/null: %m"); + + /* + * Set up diagnostics. XXX What if stdin is the system console during + * boot time? It seems a bad idea to log startup errors to the console. + * This is UNIX, a system that can run without hand holding. + */ + if ((slash = strrchr(argv[0], '/')) != 0) + argv[0] = slash + 1; + if (isatty(STDERR_FILENO)) + msg_vstream_init(argv[0], VSTREAM_ERR); + msg_syslog_init(argv[0], LOG_PID, LOG_FACILITY); + + /* + * The mail system must be run by the superuser so it can revoke + * privileges for selected operations. That's right - it takes privileges + * to toss privileges. + */ + if ((uid = getuid()) != 0) + msg_fatal("must be run by the superuser, not by userid %d", uid); + + /* + * Parse switches. + */ + while ((ch = GETOPT(argc, argv, "c:Dv")) > 0) { + switch (ch) { + default: + msg_fatal("usage: %s [-c config_dir] [-v] command", argv[0]); + case 'c': + if (*optarg != '/') + msg_fatal("-c requires absolute pathname"); + check_setenv(CONF_ENV_PATH, optarg); + break; + case 'D': + check_setenv(CONF_ENV_DEBUG, ""); + break; + case 'v': + msg_verbose++; + check_setenv(CONF_ENV_VERB, ""); + break; + } + } + + /* + * Copy a bunch of configuration parameters into the environment for easy + * access by the maintenance shell script. XXX There should be a postconf + * utility that makes config parameters easily accessible for shell + * scripts. + */ + read_config(); + + check_setenv("PATH", ROOT_PATH); /* sys_defs.h */ + check_setenv(CONF_ENV_PATH, var_config_dir);/* config.h */ + + check_setenv(VAR_COMMAND_DIR, var_command_dir); /* main.cf */ + check_setenv(VAR_DAEMON_DIR, var_daemon_dir); /* main.cf */ + check_setenv(VAR_QUEUE_DIR, var_queue_dir); /* main.cf */ + check_setenv(VAR_CONFIG_DIR, var_config_dir); /* main.cf */ + check_setenv(VAR_MAIL_OWNER, var_mail_owner); /* main.cf */ + + /* + * Make sure these directories exist. Run the maintenance scripts with as + * current directory the mail database. + */ + if (chdir(var_command_dir)) + msg_fatal("chdir(%s): %m", var_command_dir); + if (chdir(var_daemon_dir)) + msg_fatal("chdir(%s): %m", var_daemon_dir); + if (chdir(var_queue_dir)) + msg_fatal("chdir(%s): %m", var_queue_dir); + + /* + * Run the management script with as process name ourself. + */ + script = concatenate(var_config_dir, "/postfix-script", (char *) 0); + execvp(script, argv + optind - 1); + msg_fatal("%s: %m", script); +} diff --git a/postfix/postkick/.indent.pro b/postfix/postkick/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/postkick/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/postkick/.printfck b/postfix/postkick/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/postkick/.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/postkick/Makefile.in b/postfix/postkick/Makefile.in new file mode 100644 index 000000000..5066343e7 --- /dev/null +++ b/postfix/postkick/Makefile.in @@ -0,0 +1,67 @@ +SHELL = /bin/sh +SRCS = postkick.c +OBJS = postkick.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 = postkick +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' +postkick.o: postkick.c +postkick.o: ../include/sys_defs.h +postkick.o: ../include/msg.h +postkick.o: ../include/mymalloc.h +postkick.o: ../include/vstream.h +postkick.o: ../include/vbuf.h +postkick.o: ../include/msg_vstream.h +postkick.o: ../include/safe.h +postkick.o: ../include/mail_proto.h +postkick.o: ../include/iostuff.h +postkick.o: ../include/mail_params.h +postkick.o: ../include/config.h diff --git a/postfix/postkick/postkick.c b/postfix/postkick/postkick.c new file mode 100644 index 000000000..f6ad75a54 --- /dev/null +++ b/postfix/postkick/postkick.c @@ -0,0 +1,167 @@ +/*++ +/* NAME +/* postkick 1 +/* SUMMARY +/* kick a Postfix service +/* SYNOPSIS +/* .fi +/* \fBpostkick\fR [\fB-c \fIconfig_dir\fR] [\fB-v\fR] +/* \fIclass service request\fR +/* DESCRIPTION +/* The \fBpostkick\fR command sends \fIrequest\fR to the +/* specified \fIservice\fR over a local transport channel. +/* This command makes Postfix private IPC accessible +/* for use in, for example, shell scripts. +/* +/* Options: +/* .IP "\fB-c\fR \fIconfig_dir\fR" +/* Read configuration information from \fBmain.cf\fR in the named +/* configuration directory. +/* .IP \fB-v\fR +/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR +/* options make the software increasingly verbose. +/* .PP +/* Arguments: +/* .IP \fIclass\fR +/* Name of a class of local transport channel endpoints, +/* either \fBpublic\fR (accessible by any local user) or +/* \fBprivate\fR (administrative access only). +/* .IP \fIservice\fR +/* The name of a local transport endpoint within the named class. +/* .IP \fIrequest\fR +/* A string. The list of valid requests is service-specific. +/* DIAGNOSTICS +/* Problems and transactions are logged to the standard error +/* stream. +/* ENVIRONMENT +/* .ad +/* .fi +/* .IP \fBMAIL_CONFIG\fR +/* Directory with Postfix configuration files. +/* .IP \fBMAIL_VERBOSE\fR +/* Enable verbose logging. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. +/* .IP \fBqueue_directory\fR +/* Location of the Postfix queue, and of the local IPC communication +/* endpoints. +/* SEE ALSO +/* qmgr(8) queue manager trigger protocol +/* pickup(8) local pickup daemon +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include + +static NORETURN usage(char *myname) +{ + msg_fatal("usage: %s [-c config_dir] [-v] class service request", myname); +} + +int main(int argc, char **argv) +{ + char *class; + char *service; + char *request; + int fd; + struct stat st; + char *slash; + int c; + + /* + * 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. + */ + if (safe_getenv(CONF_ENV_VERB)) + msg_verbose = 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); + set_config_str(VAR_PROCNAME, var_procname = mystrdup(argv[0])); + + /* + * Parse JCL. + */ + while ((c = GETOPT(argc, argv, "c:v")) > 0) { + switch (c) { + default: + usage(argv[0]); + case 'c': + if (setenv(CONF_ENV_PATH, optarg, 1) < 0) + msg_fatal("out of memory"); + break; + case 'v': + msg_verbose++; + break; + } + } + if (argc != optind + 3) + usage(argv[0]); + class = argv[optind]; + service = argv[optind + 1]; + request = argv[optind + 2]; + + /* + * Finish initializations. + */ + read_config(); + if (chdir(var_queue_dir)) + msg_fatal("chdir %s: %m", var_queue_dir); + + /* + * Kick the service. + */ + if (mail_trigger(class, service, request, strlen(request)) < 0) { + msg_warn("Cannot contact class %s service %s - perhaps the mail system is down", + class, service); + exit(1); + } else { + exit(0); + } +} diff --git a/postfix/postlock/.indent.pro b/postfix/postlock/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/postlock/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/postlock/.printfck b/postfix/postlock/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/postlock/.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/postlock/Makefile.in b/postfix/postlock/Makefile.in new file mode 100644 index 000000000..6003b44e8 --- /dev/null +++ b/postfix/postlock/Makefile.in @@ -0,0 +1,67 @@ +SHELL = /bin/sh +SRCS = postlock.c +OBJS = postlock.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 = postlock +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' +postlock.o: postlock.c +postlock.o: ../include/sys_defs.h +postlock.o: ../include/msg.h +postlock.o: ../include/vstring.h +postlock.o: ../include/vbuf.h +postlock.o: ../include/vstream.h +postlock.o: ../include/msg_vstream.h +postlock.o: ../include/iostuff.h +postlock.o: ../include/mail_params.h +postlock.o: ../include/dot_lockfile.h +postlock.o: ../include/deliver_flock.h +postlock.o: ../include/config.h diff --git a/postfix/postlock/postlock.c b/postfix/postlock/postlock.c new file mode 100644 index 000000000..c90879119 --- /dev/null +++ b/postfix/postlock/postlock.c @@ -0,0 +1,229 @@ +/*++ +/* NAME +/* postlock 1 +/* SUMMARY +/* lock mail folder and execute command +/* SYNOPSIS +/* .fi +/* \fBpostlock\fR [\fB-c \fIconfig_dir\fB] [\fB-v\fR] +/* \fIfile command...\fR +/* DESCRIPTION +/* The \fBpostlock\fR command locks \fIfile\fR for exclusive +/* access, and executes \fIcommand\fR. The locking method is +/* compatible with the Postfix UNIX-style local delivery agent. +/* +/* Options: +/* .IP "\fB-c \fIconfig_dir\fR" +/* Read configuration information from \fBmain.cf\fR in the named +/* configuration directory. +/* .IP \fB-v\fR +/* Enable verbose mode for debugging purposes. Multiple \fB-v\fR +/* options make the software increasingly verbose. +/* .PP +/* Arguments: +/* .IP \fIfile\fR +/* A mailbox file. The user should have read/write permission. +/* .IP \fIcommand...\fR +/* The command to execute while \fIfile\fR is locked for exclusive +/* access. The command is executed directly, i.e. without +/* interpretation by a shell command interpreter. +/* DIAGNOSTICS +/* The result status is 255 (on some systems: -1) when \fBpostlock\fR +/* could not perform the requested operation. Otherwise, the +/* exit status is the exit status from the command. +/* BUGS +/* With remote file systems, the ability to acquire a lock does not +/* necessarily eliminate access conflicts. Avoid file access by +/* processes running on different machines. +/* ENVIRONMENT +/* .ad +/* .fi +/* .IP \fBMAIL_CONFIG\fR +/* Directory with Postfix configuration files. +/* .IP \fBMAIL_VERBOSE\fR +/* Enable verbose logging. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. +/* .SH "Locking controls" +/* .ad +/* .fi +/* .IP \fBdeliver_lock_attempts\fR +/* Limit the number of attempts to acquire an exclusive lock. +/* .IP \fBdeliver_lock_delay\fR +/* Time in seconds between successive attempts to acquire +/* an exclusive lock. +/* .IP \fBstale_lock_time\fR +/* Limit the time after which a stale lock is removed. +/* .SH "Resource controls" +/* .ad +/* .fi +/* .IP \fBfork_attempts\fR +/* Number of attempts to \fBfork\fR() a process before giving up. +/* .IP \fBfork_delay\fR +/* Delay in seconds between successive \fBfork\fR() attempts. +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +/* usage - explain */ + +static NORETURN usage(char *myname) +{ + msg_fatal("usage: %s [-c config_dir] [-v] folder command...", myname); +} + +/* fatal_exit - as promised, return 255 in case of locking problems */ + +static void fatal_exit(void) +{ + exit(255); +} + +/* main - go for it */ + +int main(int argc, char **argv) +{ + VSTRING *why; + char *folder; + char **command; + int ch; + int fd; + struct stat st; + int count; + WAIT_STATUS_T status; + pid_t pid; + + /* + * 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 are not set-uid, + * and we are supposed to be running in a controlled environment. + */ + if (getenv(CONF_ENV_VERB)) + msg_verbose = 1; + + /* + * Set up logging and error handling. Intercept fatal exits so we can + * return a distinguished exit status. + */ + msg_vstream_init(argv[0], VSTREAM_ERR); + msg_cleanup(fatal_exit); + + /* + * Parse JCL. + */ + while ((ch = GETOPT(argc, argv, "c:v")) > 0) { + switch (ch) { + default: + usage(argv[0]); + break; + case 'c': + if (setenv(CONF_ENV_PATH, optarg, 1) < 0) + msg_fatal("out of memory"); + break; + case 'v': + msg_verbose++; + break; + } + } + if (optind + 2 > argc) + usage(argv[0]); + folder = argv[optind]; + command = argv + optind + 1; + + /* + * Read the config file. + */ + read_config(); + + /* + * Lock the folder for exclusive access. Lose the lock upon exit. The + * command is not supposed to disappear into the background. + */ + if ((fd = open(folder, O_RDWR, 0)) < 0) + msg_fatal("open %s: %m", folder); + close_on_exec(fd, CLOSE_ON_EXEC); + why = vstring_alloc(1); +#ifdef USE_DOT_LOCK + if (dot_lockfile(folder, why) < 0) + msg_fatal("dotlock file %s: %s", folder, vstring_str(why)); +#endif + if (deliver_flock(fd, why) < 0) + msg_fatal("lock %s: %s", folder, vstring_str(why)); + + /* + * Run the command. Remove the lock after completion. + */ + for (count = 0; count < var_fork_tries; count++) { + switch (pid = fork()) { + case -1: + msg_warn("fork %s: %m", command[0]); + break; + case 0: + execvp(command[0], command); + msg_fatal("execvp %s: %m", command[0]); + /* NOTREACHED */ + default: + if (waitpid(pid, &status, 0) < 0) + msg_fatal("waitpid: %m"); +#ifdef USE_DOT_LOCK + dot_unlockfile(folder); +#endif + exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1); + /* NOTREACHED */ + } + if (count + 1 < var_fork_tries) + sleep(var_fork_delay); + } + msg_fatal("fork %s: %m", command[0]); + /* NOTREACHED */ + +} diff --git a/postfix/postlog/.indent.pro b/postfix/postlog/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/postlog/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/postlog/.printfck b/postfix/postlog/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/postlog/.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/postlog/Makefile.in b/postfix/postlog/Makefile.in new file mode 100644 index 000000000..cd8afeffc --- /dev/null +++ b/postfix/postlog/Makefile.in @@ -0,0 +1,70 @@ +SHELL = /bin/sh +SRCS = postlog.c +OBJS = postlog.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) +FILES = Makefile $(SRCS) $(HDRS) +INC_DIR = ../include +TESTPROG= +PROG = postlog +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` + +shar: + @shar $(FILES) + +lint: + lint $(SRCS) + +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' +postlog.o: postlog.c +postlog.o: ../include/sys_defs.h +postlog.o: ../include/msg.h +postlog.o: ../include/vstring.h +postlog.o: ../include/vbuf.h +postlog.o: ../include/vstream.h +postlog.o: ../include/vstring_vstream.h +postlog.o: ../include/msg_output.h +postlog.o: ../include/msg_vstream.h +postlog.o: ../include/msg_syslog.h +postlog.o: ../include/mail_params.h diff --git a/postfix/postlog/postlog.c b/postfix/postlog/postlog.c new file mode 100644 index 000000000..2ae1551f7 --- /dev/null +++ b/postfix/postlog/postlog.c @@ -0,0 +1,211 @@ +/*++ +/* NAME +/* postlog 1 +/* SUMMARY +/* Postfix-compatible logging utility +/* SYNOPSIS +/* .fi +/* \fBpostlog\fR [\fB-i\fR] [\fB-p \fIpriority\fB] [\fB-t \fItag\fR] +/* [\fB-v\fR] [\fItext...\fR] +/* DESCRIPTION +/* The \fBpostlog\fR command implements a Postfix-compatible logging +/* interface for use in, for example, shell scripts. +/* +/* By default, \fBpostlog\fR logs the \fItext\fR given on the command +/* line as one record. If no \fItext\fR is specified on the command +/* line, \fBpostlog\fR reads from standard input and logs each input +/* line as one record. +/* +/* Logging is sent to \fBsyslogd\fR(8); when the standard error stream +/* is connected to a terminal, logging is sent there as well. +/* +/* The following options are implemented: +/* .IP \fB-i\fR +/* Include the process ID in the logging tag. +/* .IP "\fB-p \fIpriority\fR" +/* Specifies the logging severity: \fBinfo\fR (default), \fBwarn\fR, +/* \fBerror\fR, \fBfatal\fR, or \fBpanic\fR. +/* .IP "\fB-t \fItag\fR" +/* Specifies the logging tag, that is, the identifying name that +/* appears at the beginning of each logging record. +/* .IP \fB-v\fR +/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR +/* options make the software increasingly verbose. +/* SEE ALSO +/* syslogd(8) syslog daemon. +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include /* XXX right place for LOG_FACILITY? */ + +/* Application-specific. */ + + /* + * Support for the severity level mapping. + */ +struct level_table { + char *name; + int level; +}; + +static struct level_table level_table[] = { + "info", MSG_INFO, + "warn", MSG_WARN, + "warning", MSG_WARN, + "error", MSG_ERROR, + "err", MSG_ERROR, + "fatal", MSG_FATAL, + "crit", MSG_FATAL, + "panic", MSG_PANIC, + 0, +}; + +/* level_map - lookup facility or severity value */ + +static int level_map(char *name) +{ + struct level_table *t; + + for (t = level_table; t->name; t++) + if (strcasecmp(t->name, name) == 0) + return (t->level); + msg_fatal("bad severity: \"%s\"", name); +} + +/* log_argv - log the command line */ + +static void log_argv(int level, char **argv) +{ + VSTRING *buf = vstring_alloc(100); + + while (*argv) { + vstring_strcat(buf, *argv++); + if (*argv) + vstring_strcat(buf, " "); + } + msg_text(level, vstring_str(buf)); + vstring_free(buf); +} + +/* log_stream - log lines from a stream */ + +static void log_stream(int level, VSTREAM *fp) +{ + VSTRING *buf = vstring_alloc(100); + + while (vstring_get_nonl(buf, fp) != VSTREAM_EOF) + msg_text(level, vstring_str(buf)); + vstring_free(buf); +} + +/* main - logger */ + +int main(int argc, char **argv) +{ + struct stat st; + char *slash; + int fd; + int ch; + char *tag; + int log_flags = 0; + int level = MSG_INFO; + + /* + * Be consistent with file permissions. + */ + umask(022); + + /* + * To minimize confusion, make sure that the standard file descriptors + * are open. + */ + for (fd = 0; fd < 3; fd++) + if (fstat(fd, &st) == -1 && open("/dev/null", 2) != fd) + msg_fatal("open /dev/null: %m"); + + /* + * Set up diagnostics. + */ + if ((slash = strrchr(argv[0], '/')) != 0) + tag = slash + 1; + else + tag = argv[0]; + if (isatty(STDERR_FILENO)) + msg_vstream_init(tag, VSTREAM_ERR); + msg_syslog_init(tag, LOG_PID, LOG_FACILITY); + + /* + * Parse switches. + */ + while ((ch = GETOPT(argc, argv, "c:ip:t:v")) > 0) { + switch (ch) { + default: + msg_fatal("usage: %s [-i] [-p priority] [-t tag] [-v] text", tag); + break; + case 'i': + log_flags |= LOG_PID; + break; + case 'p': + level = level_map(optarg); + break; + case 't': + tag = optarg; + break; + case 'v': + msg_verbose++; + break; + } + } + + /* + * Re-initialize the logging, this time with the user-specified tag and + * severity level. + */ + if (isatty(STDERR_FILENO)) + msg_vstream_init(tag, VSTREAM_ERR); + msg_syslog_init(tag, log_flags, LOG_FACILITY); + + /* + * Log the command line or log lines from standard input. + */ + if (argc > optind) { + log_argv(level, argv + optind); + } else { + log_stream(level, VSTREAM_IN); + } + exit(0); +} diff --git a/postfix/postmap/.indent.pro b/postfix/postmap/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/postmap/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/postmap/.printfck b/postfix/postmap/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/postmap/.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/postmap/Makefile.in b/postfix/postmap/Makefile.in new file mode 100644 index 000000000..bb6eec9f9 --- /dev/null +++ b/postfix/postmap/Makefile.in @@ -0,0 +1,69 @@ +SHELL = /bin/sh +SRCS = postmap.c +OBJS = postmap.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 = postmap +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' +postmap.o: postmap.c +postmap.o: ../include/sys_defs.h +postmap.o: ../include/msg.h +postmap.o: ../include/mymalloc.h +postmap.o: ../include/vstring.h +postmap.o: ../include/vbuf.h +postmap.o: ../include/vstream.h +postmap.o: ../include/msg_vstream.h +postmap.o: ../include/readline.h +postmap.o: ../include/stringops.h +postmap.o: ../include/split_at.h +postmap.o: ../include/config.h +postmap.o: ../include/mail_params.h +postmap.o: ../include/mkmap.h diff --git a/postfix/postmap/postmap.c b/postfix/postmap/postmap.c new file mode 100644 index 000000000..1ace8e16b --- /dev/null +++ b/postfix/postmap/postmap.c @@ -0,0 +1,308 @@ +/*++ +/* NAME +/* postmap 1 +/* SUMMARY +/* Postfix lookup table management +/* SYNOPSIS +/* .fi +/* \fBpostmap\fR [\fB-c \fIconfig_dir\fR] [\fB-i\fR] [\fB-v\fR] +/* [\fIfile_type\fR:]\fIfile_name\fR +/* DESCRIPTION +/* The \fBpostmap\fR command creates a new Postfix lookup table, +/* or updates an existing one. The input and output formats are +/* expected to be compatible with: +/* +/* .ti +4 +/* \fBmakemap \fIfile_type\fR \fIfile_name\fR < \fIfile_name\fR +/* +/* While the table update is in progress, signal delivery is +/* postponed, and an exclusive, advisory, lock is placed on the +/* entire table, in order to avoid surprises in spectator +/* programs. +/* +/* The format of a lookup table input file is as follows: +/* .IP \(bu +/* Blank lines are ignored. So are lines beginning with `#'. +/* .IP \(bu +/* A table entry has the form +/* .sp +/* .ti +5 +/* \fIkey\fR whitespace \fIvalue\fR +/* .IP \(bu +/* A line that starts with whitespace continues the preceding line. +/* .PP +/* The \fIkey\fR and \fIvalue\fR are processed as is, except that +/* surrounding white space is stripped off. Unlike with Postfix alias +/* databases, quotes cannot be used to protect lookup keys that contain +/* special characters such as `#' or whitespace. The \fIkey\fR is mapped +/* to lowercase to make mapping lookups case insensitive. +/* +/* Options: +/* .IP "\fB-c \fIconfig_dir\fR" +/* Read the \fBmain.cf\fR configuration file in the named directory. +/* .IP \fB-i\fR +/* Incremental mode. Read entries from standard input and do not +/* truncate an existing database. By default, \fBpostmap\fR creates +/* a new database from the entries in \fBfile_name\fR. +/* .IP \fB-v\fR +/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR +/* options make the software increasingly verbose. +/* .PP +/* Arguments: +/* .IP \fIfile_type\fR +/* The type of database to be produced. +/* .RS +/* .IP \fBbtree\fR +/* The output file is a btree file, named \fIfile_name\fB.db\fR. +/* This is available only on systems with support for \fBdb\fR databases. +/* .IP \fBdbm\fR +/* The output consists of two files, named \fIfile_name\fB.pag\fR and +/* \fIfile_name\fB.dir\fR. +/* This is available only on systems with support for \fBdbm\fR databases. +/* .IP \fBhash\fR +/* The output file is a hashed file, named \fIfile_name\fB.db\fR. +/* This is available only on systems with support for \fBdb\fR databases. +/* .PP +/* When no \fIfile_type\fR is specified, the software uses the database +/* type specified via the \fBdatabase_type\fR configuration parameter. +/* .RE +/* .IP \fIfile_name\fR +/* The name of the lookup table source file when rebuilding a database. +/* DIAGNOSTICS +/* Problems and transactions are logged to the standard error +/* stream. No output means no problems. Duplicate entries are +/* skipped and are flagged with a warning. +/* ENVIRONMENT +/* .ad +/* .fi +/* .IP \fBMAIL_CONFIG\fR +/* Mail configuration database +/* .IP \fBMAIL_VERBOSE\fR +/* Enable verbose logging for debugging purposes. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* .IP \fBdatabase_type\fR +/* Default output database type. +/* On many UNIX systems, the default database type is either \fBhash\fR +/* or \fBdbm\fR. +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include + +/* Application-specific. */ + +#define STR vstring_str + +/* postmap - create or update mapping database */ + +static void postmap(char *map_type, char *path_name, int incremental) +{ + VSTREAM *source_fp; + VSTRING *line_buffer; + MKMAP *mkmap; + int lineno; + char *key; + char *value; + + /* + * Initialize. + */ + line_buffer = vstring_alloc(100); + if (incremental) { + source_fp = VSTREAM_IN; + vstream_control(source_fp, VSTREAM_CTL_PATH, "stdin", VSTREAM_CTL_END); + } else if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0) { + msg_fatal("open %s: %m", path_name); + } + + /* + * Open the database, create it when it does not exist, truncate it when + * it does exist, and lock out any spectators. + */ + mkmap = mkmap_open(map_type, path_name, incremental ? + O_RDWR | O_CREAT : O_RDWR | O_CREAT | O_TRUNC); + + /* + * Add records to the database. + */ + lineno = 0; + while (readline(line_buffer, source_fp, &lineno)) { + + /* + * Skip comments. + */ + if (*STR(line_buffer) == '#') + continue; + + /* + * Split on the first whitespace character, then trim leading and + * trailing whitespace from key and value. + */ + key = STR(line_buffer); + value = STR(line_buffer) + strcspn(STR(line_buffer), " \t\r\n"); + if (*value) + *value++ = 0; + while (ISSPACE(*key)) + key++; + while (ISSPACE(*value)) + value++; + trimblanks(key, 0)[0] = 0; + trimblanks(value, 0)[0] = 0; + + /* + * Skip empty lines, or lines with whitespace characters only. + */ + if (*key == 0 && *value == 0) + continue; + + /* + * Enforce the "key whitespace value" format. Disallow missing keys + * or missing values. + */ + if (*key == 0 || *value == 0) { + msg_warn("%s, line %d: expected format: key whitespace value", + VSTREAM_PATH(source_fp), lineno); + continue; + } + if (key[strlen(key) - 1] == ':') + msg_warn("%s, line %d: record is in \"key: value\" format; is this an alias file?", + VSTREAM_PATH(source_fp), lineno); + + /* + * Store the value under a case-insensitive key. + */ + lowercase(key); + mkmap_append(mkmap, key, value); + } + + /* + * Close the mapping database, and release the lock. + */ + mkmap_close(mkmap); + + /* + * Cleanup. We're about to terminate, but it is a good sanity check. + */ + vstring_free(line_buffer); + if (source_fp != VSTREAM_IN) + vstream_fclose(source_fp); +} + +/* usage - explain */ + +static NORETURN usage(char *myname) +{ + msg_fatal("usage: %s [-c config_directory] [-i] [-v] [output_type:]file...", + myname); +} + +int main(int argc, char **argv) +{ + char *path_name; + int ch; + int fd; + char *slash; + struct stat st; + int incremental = 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 are not set-uid, + * and we are supposed to be running in a controlled environment. + */ + if (getenv(CONF_ENV_VERB)) + msg_verbose = 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); + + /* + * Parse JCL. + */ + while ((ch = GETOPT(argc, argv, "c:iv")) > 0) { + switch (ch) { + default: + usage(argv[0]); + break; + case 'c': + if (setenv(CONF_ENV_PATH, optarg, 1) < 0) + msg_fatal("out of memory"); + break; + case 'i': + incremental = 1; + break; + case 'v': + msg_verbose++; + break; + } + } + read_config(); + + /* + * Use the map type specified by the user, or fall back to a default + * database type. + */ + if (optind + 1 > argc) + usage(argv[0]); + while (optind < argc) { + if ((path_name = split_at(argv[optind], ':')) != 0) { + postmap(argv[optind], path_name, incremental); + } else { + postmap(var_db_type, argv[optind], incremental); + } + optind++; + } + exit(0); +} diff --git a/postfix/qmgr/.indent.pro b/postfix/qmgr/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/qmgr/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/qmgr/.printfck b/postfix/qmgr/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/qmgr/.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/qmgr/Makefile.in b/postfix/qmgr/Makefile.in new file mode 100644 index 000000000..645ea3a27 --- /dev/null +++ b/postfix/qmgr/Makefile.in @@ -0,0 +1,218 @@ +SHELL = /bin/sh +SRCS = qmgr.c qmgr_active.c qmgr_transport.c qmgr_queue.c qmgr_entry.c \ + qmgr_message.c qmgr_deliver.c qmgr_move.c qmgr_rcpt_list.c \ + qmgr_defer.c qmgr_enable.c qmgr_scan.c qmgr_bounce.c +OBJS = qmgr.o qmgr_active.o qmgr_transport.o qmgr_queue.o qmgr_entry.o \ + qmgr_message.o qmgr_deliver.o qmgr_move.o qmgr_rcpt_list.o \ + qmgr_defer.o qmgr_enable.o qmgr_scan.o qmgr_bounce.o +HDRS = qmgr.h +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 = qmgr +INC_DIR = ../include +LIBS = ../lib/libmaster.a ../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 + cp *.h 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' +qmgr.o: qmgr.c +qmgr.o: ../include/sys_defs.h +qmgr.o: ../include/msg.h +qmgr.o: ../include/events.h +qmgr.o: ../include/vstream.h +qmgr.o: ../include/vbuf.h +qmgr.o: ../include/mail_queue.h +qmgr.o: ../include/vstring.h +qmgr.o: ../include/recipient_list.h +qmgr.o: ../include/config.h +qmgr.o: ../include/mail_params.h +qmgr.o: ../include/mail_proto.h +qmgr.o: ../include/iostuff.h +qmgr.o: ../include/master_proto.h +qmgr.o: ../include/mail_server.h +qmgr.o: qmgr.h +qmgr.o: ../include/maps.h +qmgr_active.o: qmgr_active.c +qmgr_active.o: ../include/sys_defs.h +qmgr_active.o: ../include/msg.h +qmgr_active.o: ../include/events.h +qmgr_active.o: ../include/mymalloc.h +qmgr_active.o: ../include/vstream.h +qmgr_active.o: ../include/vbuf.h +qmgr_active.o: ../include/mail_params.h +qmgr_active.o: ../include/mail_open_ok.h +qmgr_active.o: ../include/mail_queue.h +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: qmgr.h +qmgr_active.o: ../include/maps.h +qmgr_bounce.o: qmgr_bounce.c +qmgr_bounce.o: ../include/sys_defs.h +qmgr_bounce.o: ../include/bounce.h +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/maps.h +qmgr_defer.o: qmgr_defer.c +qmgr_defer.o: ../include/sys_defs.h +qmgr_defer.o: ../include/msg.h +qmgr_defer.o: ../include/vstream.h +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/maps.h +qmgr_deliver.o: qmgr_deliver.c +qmgr_deliver.o: ../include/sys_defs.h +qmgr_deliver.o: ../include/msg.h +qmgr_deliver.o: ../include/vstring.h +qmgr_deliver.o: ../include/vbuf.h +qmgr_deliver.o: ../include/vstream.h +qmgr_deliver.o: ../include/vstring_vstream.h +qmgr_deliver.o: ../include/events.h +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: qmgr.h +qmgr_deliver.o: ../include/maps.h +qmgr_enable.o: qmgr_enable.c +qmgr_enable.o: ../include/sys_defs.h +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/maps.h +qmgr_entry.o: qmgr_entry.c +qmgr_entry.o: ../include/sys_defs.h +qmgr_entry.o: ../include/msg.h +qmgr_entry.o: ../include/mymalloc.h +qmgr_entry.o: ../include/events.h +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/maps.h +qmgr_message.o: qmgr_message.c +qmgr_message.o: ../include/sys_defs.h +qmgr_message.o: ../include/msg.h +qmgr_message.o: ../include/mymalloc.h +qmgr_message.o: ../include/vstring.h +qmgr_message.o: ../include/vbuf.h +qmgr_message.o: ../include/vstream.h +qmgr_message.o: ../include/split_at.h +qmgr_message.o: ../include/valid_hostname.h +qmgr_message.o: ../include/argv.h +qmgr_message.o: ../include/dict.h +qmgr_message.o: ../include/mail_queue.h +qmgr_message.o: ../include/mail_params.h +qmgr_message.o: ../include/canon_addr.h +qmgr_message.o: ../include/record.h +qmgr_message.o: ../include/rec_type.h +qmgr_message.o: ../include/sent.h +qmgr_message.o: ../include/deliver_completed.h +qmgr_message.o: ../include/mail_addr_find.h +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_move.o: qmgr_move.c +qmgr_move.o: ../include/sys_defs.h +qmgr_move.o: ../include/msg.h +qmgr_move.o: ../include/scan_dir.h +qmgr_move.o: ../include/recipient_list.h +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: qmgr.h +qmgr_move.o: ../include/maps.h +qmgr_queue.o: qmgr_queue.c +qmgr_queue.o: ../include/sys_defs.h +qmgr_queue.o: ../include/msg.h +qmgr_queue.o: ../include/mymalloc.h +qmgr_queue.o: ../include/events.h +qmgr_queue.o: ../include/htable.h +qmgr_queue.o: ../include/mail_params.h +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/maps.h +qmgr_rcpt_list.o: qmgr_rcpt_list.c +qmgr_rcpt_list.o: ../include/sys_defs.h +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/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: qmgr.h +qmgr_scan.o: ../include/vstream.h +qmgr_scan.o: ../include/vbuf.h +qmgr_scan.o: ../include/maps.h +qmgr_transport.o: qmgr_transport.c +qmgr_transport.o: ../include/sys_defs.h +qmgr_transport.o: ../include/msg.h +qmgr_transport.o: ../include/htable.h +qmgr_transport.o: ../include/events.h +qmgr_transport.o: ../include/mymalloc.h +qmgr_transport.o: ../include/vstream.h +qmgr_transport.o: ../include/vbuf.h +qmgr_transport.o: ../include/iostuff.h +qmgr_transport.o: ../include/mail_proto.h +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/maps.h diff --git a/postfix/qmgr/qmgr.c b/postfix/qmgr/qmgr.c new file mode 100644 index 000000000..464ee90aa --- /dev/null +++ b/postfix/qmgr/qmgr.c @@ -0,0 +1,459 @@ +/*++ +/* NAME +/* qmgr 8 +/* SUMMARY +/* Postfix queue manager +/* SYNOPSIS +/* \fBqmgr\fR [generic Postfix daemon options] +/* DESCRIPTION +/* The \fBqmgr\fR daemon awaits the arrival of incoming mail +/* and arranges for its delivery via Postfix delivery processes. +/* The actual mail routing strategy is delegated to the +/* \fBtrivial-rewrite\fR(8) daemon. +/* This program expects to be run from the \fBmaster\fR(8) process +/* manager. +/* +/* Mail addressed to the local \fBdouble-bounce\fR address is silently +/* discarded. This stops potential loops caused by undeliverable +/* bounce notifications. +/* +/* Mail addressed to a user listed in the optional \fBrelocated\fR +/* database is bounced with a "user has moved to \fInew_location\fR" +/* message. See \fBrelocated\fR(5) for a precise description. +/* MAIL QUEUES +/* .ad +/* .fi +/* The \fBqmgr\fR daemon maintains the following queues: +/* .IP \fBincoming\fR +/* Inbound mail from the network, or mail picked up by the +/* local \fBpickup\fR agent from the \fBmaildrop\fR directory. +/* .IP \fBactive\fR +/* Messages that the queue manager has opened for delivery. Only +/* a limited number of messages is allowed to enter the \fBactive\fR +/* queue (leaky bucket strategy, for a fixed delivery rate). +/* .IP \fBdeferred\fR +/* Mail that could not be delivered upon the first attempt. The queue +/* manager implements exponential backoff by doubling the time between +/* delivery attempts. +/* .IP \fBcorrupt\fR +/* Unreadable or damaged queue files are moved here for inspection. +/* DELIVERY STATUS REPORTS +/* .ad +/* .fi +/* The \fBqmgr\fR daemon keeps an eye on per-message delivery status +/* reports in the following directories. Each status report file has +/* the same name as the corresponding message file: +/* .IP \fBbounce\fR +/* Per-recipient status information about why mail is bounced. +/* These files are maintained by the \fBbounce\fR(8) daemon. +/* .IP \fBdefer\fR +/* Per-recipient status information about why mail is delayed. +/* These files are maintained by the \fBdefer\fR(8) daemon. +/* .PP +/* The \fBqmgr\fR daemon is responsible for asking the +/* \fBbounce\fR(8) or \fBdefer\fR(8) daemons to send non-delivery +/* reports. +/* STRATEGIES +/* .ad +/* .fi +/* The queue manager implements a variety of strategies for +/* either opening queue files (input) or for message delivery (output). +/* .IP "\fBleaky bucket\fR" +/* This strategy limits the number of messages in the \fBactive\fR queue +/* and prevents the queue manager from running out of memory under +/* heavy load. +/* .IP \fBfairness\fR +/* When the \fBactive\fR queue has room, the queue manager takes one +/* message from the \fBincoming\fR queue and one from the \fBdeferred\fR +/* queue. This prevents a large mail backlog from blocking the delivery +/* 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" +/* 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 +/* attempt. +/* .IP "\fBdestination status cache\fR" +/* The queue manager avoids unnecessary delivery attempts by +/* maintaining a short-term, in-memory list of unreachable destinations. +/* TRIGGERS +/* .ad +/* .fi +/* On an idle system, the queue manager waits for the arrival of +/* trigger events, or it waits for a timer to go off. A trigger +/* is a one-byte message. +/* Depending on the message received, the queue manager performs +/* one of the following actions (the message is followed by the +/* symbolic constant used internally by the software): +/* .IP "\fBD (QMGR_REQ_SCAN_DEFERRED)\fR" +/* Start a deferred queue scan. If a deferred queue scan is already +/* in progress, that scan will be restarted as soon as it finishes. +/* .IP "\fBI (QMGR_REQ_SCAN_INCOMING)\fR" +/* Start an incoming queue scan. If an incoming queue scan is already +/* in progress, that scan will be restarted as soon as it finishes. +/* .IP "\fBA (QMGR_REQ_SCAN_ALL)\fR" +/* Ignore deferred queue file time stamps. The request affects +/* the next deferred queue scan. +/* .IP "\fBF (QMGR_REQ_FLUSH_DEAD)\fR" +/* Purge all information about dead transports and destinations. +/* .IP "\fBW (TRIGGER_REQ_WAKEUP)\fR" +/* Wakeup call, This is used by the master server to instantiate +/* servers that should not go away forever. The action is to start +/* an incoming queue scan. +/* .PP +/* The \fBqmgr\fR daemon reads an entire buffer worth of triggers. +/* Multiple identical trigger requests are collapsed into one, and +/* trigger requests are sorted so that \fBA\fR and \fBF\fR precede +/* \fBD\fR and \fBI\fR. Thus, in order to force a deferred queue run, +/* one would request \fBA F D\fR; in order to notify the queue manager +/* of the arrival of new mail one would request \fBI\fR. +/* STANDARDS +/* .ad +/* .fi +/* None. The \fBqmgr\fR daemon does not interact with the outside world. +/* SECURITY +/* .ad +/* .fi +/* The \fBqmgr\fR daemon is not security sensitive. It reads +/* single-character messages from untrusted local users, and thus may +/* be susceptible to denial of service attacks. The \fBqmgr\fR daemon +/* does not talk to the outside world, and it can be run at fixed low +/* privilege in a chrooted environment. +/* DIAGNOSTICS +/* Problems and transactions are logged to the syslog daemon. +/* Corrupted message files are saved to the \fBcorrupt\fR queue +/* for further inspection. +/* +/* Depending on the setting of the \fBnotify_classes\fR parameter, +/* the postmaster is notified of bounces and of other trouble. +/* BUGS +/* A single queue manager process has to compete for disk access with +/* multiple front-end processes such as \fBsmtpd\fR. A sudden burst of +/* inbound mail can negatively impact outbound delivery rates. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. Use the \fBpostfix reload\fR command after +/* a configuration change. +/* .SH Miscellaneous +/* .ad +/* .fi +/* .IP \fBrelocated_maps\fR +/* Tables with contact information for users, hosts or domains +/* that no longer exist. See \fBrelocated\fR(5). +/* .IP \fBqueue_directory\fR +/* Top-level directory of the Postfix queue. +/* .SH "Active queue controls" +/* .ad +/* .fi +/* .IP \fBqmgr_message_active_limit\fR +/* Limit the number of messages in the active queue. +/* .IP \fBqmgr_message_recipient_limit\fR +/* Limit the number of in-memory recipients. +/* .sp +/* This parameter also limits the size of the short-term, in-memory +/* destination cache. +/* .SH "Timing controls" +/* .ad +/* .fi +/* .IP \fBmin_backoff\fR +/* Minimal time in seconds between delivery attempts +/* of a deferred message. +/* .sp +/* This parameter also limits the time an unreachable destination +/* is kept in the short-term, in-memory destination status cache. +/* .IP \fBmax_backoff\fR +/* Maximal time in seconds between delivery attempts +/* of a deferred message. +/* .IP \fBmaximal_queue_lifetime\fR +/* Maximal time in days a message is queued +/* before it is sent back as undeliverable. +/* .IP \fBqueue_run_delay\fR +/* Time in seconds between deferred queue scans. Queue scans do +/* not overlap. +/* .IP \fBtransport_retry_time\fR +/* Time in seconds between attempts to contact a broken +/* delivery transport. +/* .SH "Concurrency controls" +/* .ad +/* .fi +/* In the text below, \fItransport\fR is the first field in a +/* \fBmaster.cf\fR entry. +/* .IP \fBinitial_destination_concurrency\fR +/* Initial per-destination concurrency level for parallel delivery +/* to the same destination. +/* .IP \fBdefault_destination_concurrency_limit\fR +/* Default limit on the number of parallel deliveries to the same +/* destination. +/* .IP \fItransport\fB_destination_concurrency_limit\fR +/* Limit on the number of parallel deliveries to the same destination, +/* for delivery via the named message \fItransport\fR. +/* .SH "Recipient controls" +/* .ad +/* .fi +/* .IP \fBdefault_destination_recipient_limit\fR +/* Default limit on the number of recipients per message transfer. +/* .IP \fItransport\fB_destination_recipient_limit\fR +/* Limit on the number of recipients per message transfer, for the +/* named message \fItransport\fR. +/* SEE ALSO +/* master(8), process manager +/* relocated(5), format of the "user has moved" table +/* syslogd(8) system logging +/* trivial-rewrite(8), address routing +/* LICENSE +/* .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 + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include /* QMGR_SCAN constants */ + +/* Master process interface */ + +#include +#include + +/* Application-specific. */ + +#include "qmgr.h" + + /* + * Tunables. + */ +int var_queue_run_delay; +int var_min_backoff_time; +int var_max_backoff_time; +int var_max_queue_time; +int var_qmgr_active_limit; +int var_qmgr_rcpt_limit; +int var_init_dest_concurrency; +int var_transport_retry_time; +int var_dest_con_limit; +int var_dest_rcpt_limit; +char *var_relocated_maps; +char *var_virtual_maps; +char *var_defer_xports; + +static QMGR_SCAN *qmgr_incoming; +static QMGR_SCAN *qmgr_deferred; + +MAPS *qmgr_relocated; +MAPS *qmgr_virtual; + +/* qmgr_deferred_run_event - queue manager heartbeat */ + +static void qmgr_deferred_run_event(char *dummy) +{ + + /* + * This routine runs when it is time for another deferred queue scan. + * Make sure this routine gets called again in the future. + */ + qmgr_scan_request(qmgr_deferred, QMGR_SCAN_START); + event_request_timer(qmgr_deferred_run_event, dummy, var_queue_run_delay); +} + +/* qmgr_trigger_event - respond to external trigger(s) */ + +static void qmgr_trigger_event(char *buf, int len, + char *unused_service, char **argv) +{ + int incoming_flag = 0; + int deferred_flag = 0; + int i; + + /* + * Sanity check. This service takes no command-line arguments. + */ + if (argv[0]) + msg_fatal("unexpected command-line argument: %s", argv[0]); + + /* + * Collapse identical requests that have arrived since we looked last + * time. There is no client feedback so there is no need to process each + * request in order. And as long as we don't have conflicting requests we + * are free to sort them into the most suitable order. + */ + for (i = 0; i < len; i++) { + if (msg_verbose) + msg_info("request: %d (%c)", + buf[i], ISALNUM(buf[i]) ? buf[i] : '?'); + switch (buf[i]) { + case TRIGGER_REQ_WAKEUP: + case QMGR_REQ_SCAN_INCOMING: + incoming_flag |= QMGR_SCAN_START; + break; + case QMGR_REQ_SCAN_DEFERRED: + deferred_flag |= QMGR_SCAN_START; + break; + case QMGR_REQ_FLUSH_DEAD: + deferred_flag |= QMGR_FLUSH_DEAD; + break; + case QMGR_REQ_SCAN_ALL: + deferred_flag |= QMGR_SCAN_ALL; + break; + default: + if (msg_verbose) + msg_info("request ignored"); + break; + } + } + + /* + * Process each request type at most once. Modifiers take effect upon the + * next queue run. If no queue run is in progress, and a queue scan is + * requested, the request takes effect immediately. + */ + if (incoming_flag != 0) + qmgr_scan_request(qmgr_incoming, incoming_flag); + if (deferred_flag != 0) + qmgr_scan_request(qmgr_deferred, deferred_flag); +} + +/* qmgr_loop - queue manager main loop */ + +static int qmgr_loop(void) +{ + char *in_path = 0; + char *df_path = 0; + + /* + * This routine runs as part of the event handling loop, after the event + * manager has delivered a timer or I/O event (including the completion + * of a connection to a delivery process), or after it has waited for a + * specified amount of time. The result value of qmgr_loop() specifies + * how long the event manager should wait for the next event. + */ +#define DONT_WAIT 0 +#define WAIT_FOR_EVENT (-1) + + /* + * Attempt to drain the active queue by allocating a suitable delivery + * process and by delivering mail via it. Delivery process allocation and + * mail delivery are asynchronous. + */ + qmgr_active_drain(); + + /* + * Let some new blood into the active queue when the queue size is + * smaller than some configurable limit, and when the number of in-core + * recipients does not exceed some configurable limit. When the system is + * under heavy load, favor new mail over old mail. + */ + if (qmgr_message_count < var_qmgr_active_limit + && qmgr_recipient_count < var_qmgr_rcpt_limit) + if ((in_path = qmgr_scan_next(qmgr_incoming)) != 0) + qmgr_active_feed(qmgr_incoming, in_path); + if (qmgr_message_count < var_qmgr_active_limit + && qmgr_recipient_count < var_qmgr_rcpt_limit) + if ((df_path = qmgr_scan_next(qmgr_deferred)) != 0) + qmgr_active_feed(qmgr_deferred, df_path); + if (in_path || df_path) + return (DONT_WAIT); + return (WAIT_FOR_EVENT); +} + +/* qmgr_pre_init - pre-jail initialization */ + +static void qmgr_pre_init(void) +{ + if (*var_relocated_maps) + qmgr_relocated = maps_create("relocated", var_relocated_maps); + if (*var_virtual_maps) + qmgr_virtual = maps_create("virtual", var_virtual_maps); +} + +/* qmgr_post_init - post-jail initialization */ + +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 + * time stamp into the future, in order to allow ongoing deliveries to + * finish first. Start scanning the incoming and deferred queues. + */ + var_use_limit = 0; + var_idle_limit = 0; + qmgr_move(MAIL_QUEUE_ACTIVE, MAIL_QUEUE_DEFERRED, + event_time() + var_queue_run_delay); + 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); +} + +/* main - the main program */ + +int main(int argc, char **argv) +{ + static CONFIG_STR_TABLE str_table[] = { + VAR_RELOCATED_MAPS, DEF_RELOCATED_MAPS, &var_relocated_maps, 0, 0, + VAR_VIRTUAL_MAPS, DEF_VIRTUAL_MAPS, &var_virtual_maps, 0, 0, + VAR_DEFER_XPORTS, DEF_DEFER_XPORTS, &var_defer_xports, 0, 0, + 0, + }; + static CONFIG_INT_TABLE int_table[] = { + VAR_QUEUE_RUN_DELAY, DEF_QUEUE_RUN_DELAY, &var_queue_run_delay, 1, 0, + VAR_MIN_BACKOFF_TIME, DEF_MIN_BACKOFF_TIME, &var_min_backoff_time, 1, 0, + VAR_MAX_BACKOFF_TIME, DEF_MAX_BACKOFF_TIME, &var_max_backoff_time, 1, 0, + VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 1, 0, + VAR_QMGR_ACT_LIMIT, DEF_QMGR_ACT_LIMIT, &var_qmgr_active_limit, 1, 0, + VAR_QMGR_RCPT_LIMIT, DEF_QMGR_RCPT_LIMIT, &var_qmgr_rcpt_limit, 1, 0, + VAR_INIT_DEST_CON, DEF_INIT_DEST_CON, &var_init_dest_concurrency, 1, 0, + VAR_XPORT_RETRY_TIME, DEF_XPORT_RETRY_TIME, &var_transport_retry_time, 1, 0, + VAR_DEST_CON_LIMIT, DEF_DEST_CON_LIMIT, &var_dest_con_limit, 0, 0, + VAR_DEST_RCPT_LIMIT, DEF_DEST_RCPT_LIMIT, &var_dest_rcpt_limit, 0, 0, + 0, + }; + + /* + * Use the trigger service skeleton, because no-one else should be + * monitoring our service port while this process runs, and because we do + * not talk back to the client. + */ + trigger_server_main(argc, argv, qmgr_trigger_event, + MAIL_SERVER_INT_TABLE, int_table, + MAIL_SERVER_STR_TABLE, str_table, + MAIL_SERVER_PRE_INIT, qmgr_pre_init, + MAIL_SERVER_POST_INIT, qmgr_post_init, + MAIL_SERVER_LOOP, qmgr_loop, + 0); +} diff --git a/postfix/qmgr/qmgr.h b/postfix/qmgr/qmgr.h new file mode 100644 index 000000000..c8e6c9e2c --- /dev/null +++ b/postfix/qmgr/qmgr.h @@ -0,0 +1,309 @@ +/*++ +/* NAME +/* qmgr 3h +/* SUMMARY +/* queue manager data structures +/* SYNOPSIS +/* #include "qmgr.h" +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * Global library. + */ +#include + + /* + * The queue manager is built around lots of mutually-referring structures. + * These typedefs save some typing. + */ +typedef struct QMGR_TRANSPORT QMGR_TRANSPORT; +typedef struct QMGR_QUEUE QMGR_QUEUE; +typedef struct QMGR_ENTRY QMGR_ENTRY; +typedef struct QMGR_MESSAGE QMGR_MESSAGE; +typedef struct QMGR_TRANSPORT_LIST QMGR_TRANSPORT_LIST; +typedef struct QMGR_QUEUE_LIST QMGR_QUEUE_LIST; +typedef struct QMGR_ENTRY_LIST QMGR_ENTRY_LIST; +typedef struct QMGR_RCPT QMGR_RCPT; +typedef struct QMGR_RCPT_LIST QMGR_RCPT_LIST; +typedef struct QMGR_SCAN QMGR_SCAN; + + /* + * Hairy macros to update doubly-linked lists. + */ +#define QMGR_LIST_ROTATE(head, object) { \ + head.next->peers.prev = head.prev; \ + head.prev->peers.next = head.next; \ + head.next = object->peers.next; \ + if (object->peers.next) \ + head.next->peers.prev = 0; \ + head.prev = object; \ + object->peers.next = 0; \ +} + +#define QMGR_LIST_UNLINK(head, type, object) { \ + type next = object->peers.next; \ + type prev = object->peers.prev; \ + if (prev) prev->peers.next = next; \ + else head.next = next; \ + if (next) next->peers.prev = prev; \ + else head.prev = prev; \ + object->peers.next = object->peers.prev = 0; \ +} + +#define QMGR_LIST_APPEND(head, object) { \ + object->peers.next = head.next; \ + object->peers.prev = 0; \ + if (head.next) { \ + head.next->peers.prev = object; \ + } else { \ + head.prev = object; \ + } \ + head.next = object; \ +} + +#define QMGR_LIST_PREPEND(head, object) { \ + object->peers.prev = head->prev; \ + object->peers.next = 0; \ + head.prev->next = object; \ + head.prev = object; \ +} + +#define QMGR_LIST_INIT(head) { \ + head.prev = 0; \ + head.next = 0; \ +} + + /* + * Transports are looked up by name (when we have resolved a message), or + * round-robin wise (when we want to distribute resources fairly). + */ +struct QMGR_TRANSPORT_LIST { + QMGR_TRANSPORT *next; + QMGR_TRANSPORT *prev; +}; + +extern struct HTABLE *qmgr_transport_byname; /* transport by name */ +extern QMGR_TRANSPORT_LIST qmgr_transport_list; /* transports, round robin */ + + /* + * Each transport (local, smtp-out, bounce) can have one queue per next hop + * name. Queues are looked up by next hop name (when we have resolved a + * message destination), or round-robin wise (when we want to deliver + * messages fairly). + */ +struct QMGR_QUEUE_LIST { + QMGR_QUEUE *next; + QMGR_QUEUE *prev; +}; + +struct QMGR_TRANSPORT { + int flags; /* blocked, etc. */ + char *name; /* transport name */ + int dest_concurrency_limit; /* concurrency per domain */ + int recipient_limit; /* recipients per transaction */ + struct HTABLE *queue_byname; /* queues indexed by domain */ + QMGR_QUEUE_LIST queue_list; /* queues, round robin order */ + QMGR_TRANSPORT_LIST peers; /* linkage */ + char *reason; /* why unavailable */ +}; + +#define QMGR_TRANSPORT_STAT_DEAD (1<<1) +#define QMGR_TRANSPORT_STAT_BUSY (1<<2) + +typedef void (*QMGR_TRANSPORT_ALLOC_NOTIFY) (QMGR_TRANSPORT *, VSTREAM *); +extern QMGR_TRANSPORT *qmgr_transport_select(void); +extern void qmgr_transport_alloc(QMGR_TRANSPORT *, QMGR_TRANSPORT_ALLOC_NOTIFY); +extern void qmgr_transport_throttle(QMGR_TRANSPORT *, const char *); +extern void qmgr_transport_unthrottle(QMGR_TRANSPORT *); +extern QMGR_TRANSPORT *qmgr_transport_create(const char *); +extern QMGR_TRANSPORT *qmgr_transport_find(const char *); + + /* + * Each next hop (e.g., a domain name) has its own queue of pending message + * transactions. The "todo" queue contains messages that are to be delivered + * to this next hop. When a message is elected for transmission, it is moved + * from the "todo" queue to the "busy" queue. Messages are taken from the + * "todo" queue by randomly choosing between the first and the last queue + * entries. This ensures that one problematic message will not block all + * other traffic to that next hop. + */ +struct QMGR_ENTRY_LIST { + QMGR_ENTRY *next; + QMGR_ENTRY *prev; +}; + +struct QMGR_QUEUE { + char *name; /* domain name */ + int todo_refcount; /* queue entries (todo list) */ + int busy_refcount; /* queue entries (busy list) */ + int window; /* slow open algorithm */ + QMGR_TRANSPORT *transport; /* transport linkage */ + QMGR_ENTRY_LIST todo; /* todo queue entries */ + QMGR_ENTRY_LIST busy; /* messages on the wire */ + QMGR_QUEUE_LIST peers; /* neighbor queues */ + char *reason; /* why unavailable */ +}; + +#define QMGR_QUEUE_TODO 1 /* waiting for service */ +#define QMGR_QUEUE_BUSY 2 /* recipients on the wire */ + +extern int qmgr_queue_count; + +extern QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *, const char *); +extern QMGR_QUEUE *qmgr_queue_select(QMGR_TRANSPORT *); +extern void qmgr_queue_done(QMGR_QUEUE *); +extern void qmgr_queue_throttle(QMGR_QUEUE *, const char *); +extern void qmgr_queue_unthrottle(QMGR_QUEUE *); +extern QMGR_QUEUE *qmgr_queue_find(QMGR_TRANSPORT *, const char *); + + /* + * Structure for a recipient list. Initially, it just contains recipient + * addresses and file offsets. After the address resolver has done its work, + * each recipient is accompanied by a reference to a specific queues (which + * implies a specific transport). This is an extended version of similar + * information maintained by the recipient_list(3) module. + */ +struct QMGR_RCPT { + long offset; /* REC_TYPE_RCPT byte */ + char *address; /* complete address */ + QMGR_QUEUE *queue; /* resolved queue */ +}; + +struct QMGR_RCPT_LIST { + QMGR_RCPT *info; + int len; + int avail; +}; + +extern void qmgr_rcpt_list_init(QMGR_RCPT_LIST *); +extern void qmgr_rcpt_list_add(QMGR_RCPT_LIST *, long, const char *); +extern void qmgr_rcpt_list_free(QMGR_RCPT_LIST *); + + /* + * Structure of one next-hop queue entry. In order to save some copying + * effort we allow multiple recipients per transaction. + */ +struct QMGR_ENTRY { + VSTREAM *stream; /* delivery process */ + QMGR_MESSAGE *message; /* message info */ + QMGR_RCPT_LIST rcpt_list; /* as many as it takes */ + QMGR_QUEUE *queue; /* parent linkage */ + QMGR_ENTRY_LIST peers; /* neighbor entries */ +}; + +extern QMGR_ENTRY *qmgr_entry_select(QMGR_QUEUE *); +extern void qmgr_entry_unselect(QMGR_QUEUE *, QMGR_ENTRY *); +extern void qmgr_entry_done(QMGR_ENTRY *, int); +extern QMGR_ENTRY *qmgr_entry_create(QMGR_QUEUE *, QMGR_MESSAGE *); + + /* + * All common in-core information about a message is kept here. When all + * recipients have been tried the message file is linked to the "deferred" + * queue (some hosts not reachable), to the "bounce" queue (some recipients + * were rejected), and is then removed from the "active" queue. + */ +struct QMGR_MESSAGE { + int flags; /* delivery problems */ + int qflags; /* queuing flags */ + VSTREAM *fp; /* open queue file or null */ + int refcount; /* queue entries */ + int single_rcpt; /* send one rcpt at a time */ + long arrival_time; /* time when queued */ + long data_offset; /* data seek offset */ + char *queue_name; /* queue name */ + char *queue_id; /* queue file */ + char *sender; /* complete address */ + char *errors_to; /* error report address */ + char *return_receipt; /* confirm receipt address */ + long data_size; /* message content size */ + long rcpt_offset; /* more recipients here */ + QMGR_RCPT_LIST rcpt_list; /* complete addresses */ +}; + +extern int qmgr_message_count; +extern int qmgr_recipient_count; +extern MAPS *qmgr_relocated; +extern MAPS *qmgr_virtual; + +extern void qmgr_message_free(QMGR_MESSAGE *); +extern QMGR_MESSAGE *qmgr_message_alloc(const char *, const char *, int); +extern QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *); + + /* + * qmgr_defer.c + */ +extern void qmgr_defer_transport(QMGR_TRANSPORT *, const char *); +extern void qmgr_defer_todo(QMGR_QUEUE *, const char *); +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 *, ...); + + /* + * qmgr_deliver.c + */ +extern int qmgr_deliver_concurrency; +extern void qmgr_deliver(QMGR_TRANSPORT *, VSTREAM *); + + /* + * qmgr_active.c + */ +extern void qmgr_active_feed(QMGR_SCAN *, const char *); +extern void qmgr_active_drain(void); +extern void qmgr_active_done(QMGR_MESSAGE *); + + /* + * qmgr_move.c + */ +extern void qmgr_move(const char *, const char *, time_t); + + /* + * qmgr_enable.c + */ +extern void qmgr_enable_all(void); +extern void qmgr_enable_transport(QMGR_TRANSPORT *); +extern void qmgr_enable_queue(QMGR_QUEUE *); + + /* + * Queue scan context. + */ +struct QMGR_SCAN { + char *queue; /* queue name */ + int flags; /* private, this run */ + int nflags; /* private, next run */ + struct SCAN_DIR *handle; /* scan */ +}; + + /* + * Flags that control queue scans or destination selection. These are + * similar to the QMGR_REQ_XXX request codes. + */ +#define QMGR_SCAN_START (1<<0) /* start now/restart when done */ +#define QMGR_SCAN_ALL (1<<1) /* all queue file time stamps */ +#define QMGR_FLUSH_DEAD (1<<2) /* all sites, all transports */ + + /* + * qmgr_scan.c + */ +extern QMGR_SCAN *qmgr_scan_create(const char *); +extern void qmgr_scan_request(QMGR_SCAN *, int); +extern char *qmgr_scan_next(QMGR_SCAN *); + +/* 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 +/*--*/ diff --git a/postfix/qmgr/qmgr_active.c b/postfix/qmgr/qmgr_active.c new file mode 100644 index 000000000..a9d04cea7 --- /dev/null +++ b/postfix/qmgr/qmgr_active.c @@ -0,0 +1,372 @@ +/*++ +/* NAME +/* qmgr_active 3 +/* SUMMARY +/* active queue management +/* SYNOPSIS +/* #include "qmgr.h" +/* +/* void qmgr_active_feed(scan_info, queue_id) +/* QMGR_SCAN *scan_info; +/* const char *queue_id; +/* +/* void qmgr_active_drain() +/* +/* int qmgr_active_done(message) +/* QMGR_MESSAGE *message; +/* DESCRIPTION +/* These functions maintain the active message queue: the set +/* of messages that the queue manager is actually working on. +/* The active queue is limited in size. Messages are drained +/* from the active queue by allocating a delivery process and +/* by delivering mail via that process. Message leak into the +/* active queue only when the active queue is small enough. +/* Damaged message files are saved to the "corrupt" directory. +/* +/* qmgr_active_feed() inserts the named message file into +/* the active queue. Message files with the wrong name or +/* with other wrong properties are skipped but not removed. +/* The following queue flags are recognized, other flags being +/* ignored: +/* .IP QMGR_SCAN_ALL +/* Examine all queue files. Normally, deferred queue files with +/* future time stamps are ignored, and incoming queue files with +/* future time stamps are frowned upon. +/* .PP +/* qmgr_active_drain() allocates one delivery process. +/* Process allocation is asynchronous. Once the delivery +/* process is available, an attempt is made to deliver +/* a message via it. Message delivery is asynchronous, too. +/* +/* qmgr_active_done() deals with a message after delivery +/* has been tried for all in-core recipients. If the message +/* was bounced, a bounce message is sent to the sender, or +/* to the Errors-To: address if one was specified. +/* If there are more on-file recipients, a new batch of +/* in-core recipients is read from the queue file. Otherwise, +/* if a delivery agent marked the queue file as corrupt, +/* the queue file is moved to the "corrupt" queue (surprise); +/* if at least one delivery failed, the message is moved +/* to the deferred queue. The time stamps of a deferred queue +/* file are set to the nearest wakeup time of its recipient +/* sites (if delivery failed due to a problem with a next-hop +/* host), are set into the future by the amount of time the +/* message was queued (per-message exponential backoff), or are set +/* into the future by a minimal backoff time, whichever is more. +/* The minimal_backoff_time parameter specifies the minimal +/* amount of time between delivery attempts; maximal_backoff_time +/* specifies an upper limit. +/* DIAGNOSTICS +/* Fatal: queue file access failures, out of memory. +/* Panic: interface violations, internal consistency errors. +/* Warnings: corrupt message file. A corrupt message is saved +/* to the "corrupt" queue for further inspection. +/* 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 +#include + +#ifndef S_IRWXU /* What? no POSIX system? */ +#define S_IRWXU 0700 +#endif + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "qmgr.h" + +/* qmgr_active_corrupt - move corrupted file out of the way */ + +static void qmgr_active_corrupt(const char *queue_id) +{ + char *myname = "qmgr_active_corrupt"; + + if (mail_queue_rename(queue_id, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_CORRUPT)) { + if (errno != ENOENT) + msg_fatal("%s: save corrupt file queue %s id %s: %m", + myname, MAIL_QUEUE_ACTIVE, queue_id); + msg_warn("%s: save corrupt file queue %s id %s: %m", + myname, MAIL_QUEUE_ACTIVE, queue_id); + } else { + msg_warn("corrupt file queue %s id %s", MAIL_QUEUE_ACTIVE, queue_id); + } +} + +/* qmgr_active_feed - feed one message into active queue */ + +void qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id) +{ + char *myname = "qmgr_active_feed"; + QMGR_MESSAGE *message; + struct stat st; + const char *path; + + if (strcmp(scan_info->queue, MAIL_QUEUE_ACTIVE) == 0) + msg_panic("%s: bad queue %s", myname, scan_info->queue); + if (msg_verbose) + msg_info("%s: queue %s", myname, scan_info->queue); + + /* + * Make sure this is something we are willing to open. + */ + if (mail_open_ok(scan_info->queue, queue_id, &st, &path) == MAIL_OPEN_NO) + return; + + if (msg_verbose) + msg_info("%s: %s", myname, path); + + /* + * 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. + */ + 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())); + return; + } + + /* + * Move the message to the active queue. File access errors are fatal. + */ + if (mail_queue_rename(queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE)) { + if (errno != ENOENT) + msg_fatal("%s: %s: rename from %s to %s: %m", myname, + queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE); + msg_warn("%s: %s: rename from %s to %s: %m", myname, + queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE); + return; + } + + /* + * Reset the defer log. Leave the bounce log alone; if it is still + * around, something did not send it previously. + */ + if (mail_queue_remove(MAIL_QUEUE_DEFER, queue_id) && errno != ENOENT) + msg_fatal("%s: %s: remove %s %s: %m", myname, + queue_id, MAIL_QUEUE_DEFER, queue_id); + + /* + * Extract envelope information: sender and recipients. At this point, + * mail addresses have been processed by the cleanup service so they + * should be in canonical form. Generate requests to deliver this + * message. + * + * Throwing away queue files seems bad, especially when they made it this + * far into the mail system. Therefore we save bad files to a separate + * directory for further inspection. + */ + if ((message = qmgr_message_alloc(MAIL_QUEUE_ACTIVE, queue_id, + scan_info->flags)) == 0) { + qmgr_active_corrupt(queue_id); + } else { + if (message->refcount == 0) + qmgr_active_done(message); + } +} + +/* qmgr_active_done - dispose of message after recipients have been tried */ + +void qmgr_active_done(QMGR_MESSAGE *message) +{ + char *myname = "qmgr_active_done"; + struct stat st; + const char *path; + struct utimbuf tbuf; + time_t delay; + + if (msg_verbose) + msg_info("%s: %s", myname, message->queue_id); + + /* + * During a previous iteration, an attempt to bounce this message may + * have failed, so there may still be a bounce log lying around. XXX By + * groping around in the bounce queue, we're trespassing on the bounce + * service's territory. But doing so is more robust than depending on the + * bounce daemon to do the lookup for us, and for us to do the deleting + * after we have received a successful status from the bounce service. + * The bounce queue directory blocks are most likely in memory anyway. If + * these lookups become a performance problem we will have to build an + * in-core cache into the bounce daemon. + * + * Don't bounce when the bounce log is empty. The bounce process obviously + * failed, and the delivery agent will have requested that the message be + * deferred. + */ + if (stat(mail_queue_path((VSTRING *) 0, MAIL_QUEUE_BOUNCE, message->queue_id), &st) == 0) { + if (st.st_size == 0) { + if (mail_queue_remove(MAIL_QUEUE_BOUNCE, message->queue_id)) + msg_fatal("remove %s %s: %m", + MAIL_QUEUE_BOUNCE, message->queue_id); + } else { + if (msg_verbose) + msg_info("%s: bounce %s", myname, message->queue_id); + message->flags |= bounce_flush(BOUNCE_FLAG_KEEP, + message->queue_name, + message->queue_id, + message->errors_to); + } + } + + /* + * A delivery agent marks a queue file as corrupt by changing its + * attributes, and by pretending that delivery was deferred. + */ + if (message->flags + && !mail_open_ok(MAIL_QUEUE_ACTIVE, message->queue_id, &st, &path)) { + qmgr_active_corrupt(message->queue_id); + qmgr_message_free(message); + return; + } + + /* + * If we did not read all recipients from this file, go read some more, + * but remember whether some recipients have to be tried again. + * + * Throwing away queue files seems bad, especially when they made it this + * far into the mail system. Therefore we save bad files to a separate + * directory for further inspection by a human being. + */ + if (message->rcpt_offset > 0) { + if (qmgr_message_realloc(message) == 0) { + qmgr_active_corrupt(message->queue_id); + qmgr_message_free(message); + } else { + if (message->refcount == 0) + qmgr_active_done(message); /* recurse for consistency */ + } + return; + } + + /* + * 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 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); + } + + /* + * Some recipients need to be tried again. Move the queue file time + * stamps into the future by the amount of time that the message is + * delayed, and move the message to the deferred queue. Impose minimal + * and maximal backoff times. + * + * Since we look at actual time in queue, not time since last delivery + * attempt, backoff times will be distributed. However, we can still see + * spikes in delivery activity because the interval between deferred + * queue scans is finite. + */ + if (message->flags) { + if (message->arrival_time > 0) { + delay = event_time() - message->arrival_time; + if (delay > var_max_backoff_time) + delay = var_max_backoff_time; + if (delay < var_min_backoff_time) + delay = var_min_backoff_time; + } else { + delay = var_min_backoff_time; + } + if (msg_verbose) + msg_info("wakeup %s after %ld secs", message->queue_id, delay); + tbuf.actime = tbuf.modtime = event_time() + delay; + path = mail_queue_path((VSTRING *) 0, message->queue_name, + message->queue_id); + if (utime(path, &tbuf) < 0) + msg_fatal("%s: update %s time stamps: %m", myname, path); + if (mail_queue_rename(message->queue_id, message->queue_name, + MAIL_QUEUE_DEFERRED)) { + if (errno != ENOENT) + msg_fatal("%s: rename %s from %s to %s: %m", myname, + message->queue_id, message->queue_name, MAIL_QUEUE_DEFERRED); + msg_warn("%s: rename %s from %s to %s: %m", myname, + message->queue_id, message->queue_name, MAIL_QUEUE_DEFERRED); + } else if (msg_verbose) { + msg_info("%s: defer %s", myname, message->queue_id); + } + } + + /* + * All recipients done. Remove the queue file. + */ + else { + if (mail_queue_remove(message->queue_name, message->queue_id)) { + if (errno != ENOENT) + msg_fatal("%s: remove %s from %s: %m", myname, + message->queue_id, message->queue_name); + msg_warn("%s: remove %s from %s: %m", myname, + message->queue_id, message->queue_name); + } else if (msg_verbose) { + msg_info("%s: remove %s", myname, message->queue_id); + } + } + + /* + * Finally, delete the in-core message structure. + */ + qmgr_message_free(message); +} + +/* qmgr_active_drain - drain active queue by allocating a delivery process */ + +void qmgr_active_drain(void) +{ + QMGR_TRANSPORT *transport; + + /* + * Use round-robin search to find a transport with pending mail. Allocate + * a delivery process. The process allocation completes asynchronously. + */ + if ((transport = qmgr_transport_select()) != 0) { + if (msg_verbose) + msg_info("qmgr_active_drain: allocate %s", transport->name); + qmgr_transport_alloc(transport, qmgr_deliver); + } +} diff --git a/postfix/qmgr/qmgr_bounce.c b/postfix/qmgr/qmgr_bounce.c new file mode 100644 index 000000000..e96126266 --- /dev/null +++ b/postfix/qmgr/qmgr_bounce.c @@ -0,0 +1,74 @@ +/*++ +/* NAME +/* qmgr_bounce +/* SUMMARY +/* deal with mail that will not be delivered +/* SYNOPSIS +/* #include "qmgr.h" +/* +/* QMGR_QUEUE *qmgr_bounce_recipient(message, recipient, format, ...) +/* QMGR_MESSAGE *message; +/* QMGR_RCPT *recipient; +/* const char *format; +/* DESCRIPTION +/* qmgr_bounce_recipient() produces a bounce log record. +/* Once the bounce record is written successfully, the recipient +/* is marked as done. When the bounce record cannot be written, +/* the message structure is updated to reflect that the mail is +/* deferred. +/* +/* Arguments: +/* .IP message +/* Open queue file with the message being bounced. +/* .IP recipient +/* The recipient that will not be delivered. +/* .IP format +/* Free-format text that describes why delivery will not happen. +/* DIAGNOSTICS +/* Panic: consistency check failure. Fatal: out of memory. +/* 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. */ + +/* Global library. */ + +#include +#include + +/* Application-specific. */ + +#include "qmgr.h" + +/* qmgr_bounce_recipient - bounce one message recipient */ + +void qmgr_bounce_recipient(QMGR_MESSAGE *message, QMGR_RCPT * recipient, + const char *format,...) +{ + va_list ap; + int status; + + va_start(ap, format); + status = vbounce_append(BOUNCE_FLAG_KEEP, message->queue_id, + recipient->address, "none", + message->arrival_time, format, ap); + va_end(ap); + + if (status == 0) + deliver_completed(message->fp, recipient->offset); + else + message->flags |= status; +} diff --git a/postfix/qmgr/qmgr_defer.c b/postfix/qmgr/qmgr_defer.c new file mode 100644 index 000000000..8be8e7247 --- /dev/null +++ b/postfix/qmgr/qmgr_defer.c @@ -0,0 +1,159 @@ +/*++ +/* NAME +/* qmgr_defer +/* SUMMARY +/* deal with mail that must be delivered later +/* SYNOPSIS +/* #include "qmgr.h" +/* +/* void qmgr_defer_recipient(message, address, reason) +/* QMGR_MESSAGE *message; +/* const char *address; +/* const char *reason; +/* +/* void qmgr_defer_todo(queue, reason) +/* QMGR_QUEUE *queue; +/* const char *reason; +/* +/* QMGR_QUEUE *qmgr_defer_transport(transport, reason) +/* QMGR_TRANSPORT *transport; +/* const char *reason; +/* DESCRIPTION +/* qmgr_defer_recipient() defers delivery of the named message to +/* the named recipient. It updates the message structure and writes +/* a log entry. +/* +/* qmgr_defer_todo() iterates over all "todo" deliveries queued for +/* the named site, and calls qmgr_defer_recipient() for each recipient +/* found. Side effects caused by qmgr_entry_done(), qmgr_queue_done(), +/* and by qmgr_active_done(): in-core queue entries will disappear, +/* in-core queues may disappear, in-core and on-disk messages may +/* disappear, bounces may be sent, new in-core queues, queue entries +/* and recipients may appear. +/* +/* qmgr_defer_transport() calls qmgr_defer_todo() for each queue +/* that depends on the named transport. See there for side effects. +/* +/* Arguments: +/* .IP recipient +/* A recipient address; used for logging purposes, and for updating +/* the message-specific \fIdefer\fR log. +/* .IP queue +/* Specifies a queue with delivery requests for a specific next-hop +/* host (or local user). +/* .IP transport +/* Specifies a message delivery transport. +/* .IP reason +/* Free-format text that describes why delivery is deferred; this +/* used for logging purposes, and for updating the message-specific +/* \fIdefer\fR log. +/* BUGS +/* The side effects of calling this routine are quite dramatic. +/* DIAGNOSTICS +/* Panic: consistency check failure. Fatal: out of memory. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include + +/* Application-specific. */ + +#include "qmgr.h" + +/* qmgr_defer_transport - defer todo entries for named transport */ + +void qmgr_defer_transport(QMGR_TRANSPORT *transport, const char *reason) +{ + char *myname = "qmgr_defer_transport"; + QMGR_QUEUE *queue; + QMGR_QUEUE *next; + + /* + * Sanity checks. + */ + if (reason == 0) + msg_panic("%s: null reason", myname); + if (msg_verbose) + msg_info("defer transport %s: %s", transport->name, reason); + + /* + * Proceed carefully. Queues may disappear as a side effect. + */ + for (queue = transport->queue_list.next; queue; queue = next) { + next = queue->peers.next; + qmgr_defer_todo(queue, reason); + } +} + +/* qmgr_defer_todo - defer all todo queue entries for specific site */ + +void qmgr_defer_todo(QMGR_QUEUE *queue, const char *reason) +{ + char *myname = "qmgr_defer_todo"; + QMGR_ENTRY *entry; + QMGR_ENTRY *next; + QMGR_MESSAGE *message; + QMGR_RCPT *recipient; + int nrcpt; + + /* + * Sanity checks. + */ + if (reason == 0) + msg_panic("%s: null reason", myname); + if (msg_verbose) + msg_info("defer site %s: %s", queue->name, reason); + + /* + * Proceed carefully. Queue entries will disappear as a side effect. + */ + for (entry = queue->todo.next; entry != 0; entry = next) { + next = entry->peers.next; + message = entry->message; + for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) { + recipient = entry->rcpt_list.info + nrcpt; + qmgr_defer_recipient(message, recipient->address, reason); + } + qmgr_entry_done(entry, QMGR_QUEUE_TODO); + } +} + +/* qmgr_defer_recipient - defer delivery of specific recipient */ + +void qmgr_defer_recipient(QMGR_MESSAGE *message, const char *address, + const char *reason) +{ + char *myname = "qmgr_defer_recipient"; + + /* + * Sanity checks. + */ + if (reason == 0) + msg_panic("%s: reason 0", myname); + + /* + * Update the message structure and log the message disposition. + */ + message->flags |= defer_append(BOUNCE_FLAG_KEEP, message->queue_id, + address, "none", message->arrival_time, + "%s", reason); +} diff --git a/postfix/qmgr/qmgr_deliver.c b/postfix/qmgr/qmgr_deliver.c new file mode 100644 index 000000000..cde38b091 --- /dev/null +++ b/postfix/qmgr/qmgr_deliver.c @@ -0,0 +1,270 @@ +/*++ +/* NAME +/* qmgr_deliver 3 +/* SUMMARY +/* deliver one pe-site queue entry to that site +/* SYNOPSIS +/* #include "qmgr.h" +/* +/* int qmgr_deliver_concurrency; +/* +/* int qmgr_deliver(transport, fp) +/* QMGR_TRANSPORT *transport; +/* VSTREAM *fp; +/* DESCRIPTION +/* This module implements the client side of the `queue manager +/* to delivery agent' protocol. The queue manager uses +/* asynchronous I/O so that it can drive multiple delivery +/* agents in parallel. Depending on the outcome of a delivery +/* attempt, the status of messages, queues and transports is +/* updated. +/* +/* qmgr_deliver_concurrency is a global counter that says how +/* many delivery processes are in use. This can be used, for +/* example, to control the size of the `active' message queue. +/* +/* qmgr_deliver() executes when a delivery process announces its +/* availability for the named transport. It arranges for delivery +/* of a suitable queue entry. The \fIfp\fR argument specifies a +/* stream that is connected to the delivery process. Upon completion +/* of delivery (successful or not), the stream is closed, so that the +/* delivery process is released. +/* DIAGNOSTICS +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include + +/* Application-specific. */ + +#include "qmgr.h" + +int qmgr_deliver_concurrency; + + /* + * Message delivery status codes. + */ +#define DELIVER_STAT_OK 0 /* all recipients delivered */ +#define DELIVER_STAT_DEFER 1 /* try some recipients later */ +#define DELIVER_STAT_CRASH 2 /* mailer internal problem */ + +/* qmgr_deliver_initial_reply - retrieve initial delivery process response */ + +static int qmgr_deliver_initial_reply(VSTREAM *stream) +{ + int stat; + + if (peekfd(vstream_fileno(stream)) < 0) { + msg_warn("%s: premature disconnect", VSTREAM_PATH(stream)); + return (DELIVER_STAT_CRASH); + } else if (mail_scan(stream, "%d", &stat) != 1) { + msg_warn("%s: malformed response", VSTREAM_PATH(stream)); + return (DELIVER_STAT_CRASH); + } else { + return (stat ? DELIVER_STAT_DEFER : 0); + } +} + +/* qmgr_deliver_final_reply - retrieve final delivery process response */ + +static int qmgr_deliver_final_reply(VSTREAM *stream, VSTRING *reason) +{ + int stat; + + if (peekfd(vstream_fileno(stream)) < 0) { + msg_warn("%s: premature disconnect", VSTREAM_PATH(stream)); + return (DELIVER_STAT_CRASH); + } else if (mail_scan(stream, "%s %d", reason, &stat) != 2) { + msg_warn("%s: malformed response", VSTREAM_PATH(stream)); + return (DELIVER_STAT_CRASH); + } else { + return (stat ? DELIVER_STAT_DEFER : 0); + } +} + +/* qmgr_deliver_send_request - send delivery request to delivery process */ + +static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream) +{ + QMGR_RCPT_LIST list = entry->rcpt_list; + QMGR_RCPT *recipient; + QMGR_MESSAGE *message = entry->message; + + mail_print(stream, "%s %s %ld %ld %s %s %s %s %ld", + message->queue_name, message->queue_id, + message->data_offset, message->data_size, + entry->queue->name, message->sender, + message->errors_to, message->return_receipt, + message->arrival_time); + for (recipient = list.info; recipient < list.info + list.len; recipient++) + mail_print(stream, "%ld %s", recipient->offset, recipient->address); + mail_print(stream, "%s", "0"); + if (vstream_fflush(stream) != 0) { + msg_warn("write to process (%s): %m", entry->queue->transport->name); + return (-1); + } else { + if (msg_verbose) + msg_info("qmgr_deliver: site `%s'", entry->queue->name); + return (0); + } +} + +/* qmgr_deliver_update - process delivery status report */ + +static void qmgr_deliver_update(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; + VSTRING *reason = vstring_alloc(1); + int status; + + /* + * 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 site 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. + */ + status = qmgr_deliver_final_reply(entry->stream, reason); + + /* + * The mail delivery process failed for some reason (although delivery + * may have been successful). Back off with this transport type for a + * while. Dispose of queue entries for this transport that await + * selection (the todo lists). Stay away from queue entries that have + * been selected (the busy lists), or we would have dangling pointers. + * The queue itself won't go away before we dispose of the current queue + * entry. + */ + if (status == DELIVER_STAT_CRASH) { + message->flags |= DELIVER_STAT_DEFER; + qmgr_transport_throttle(transport, "unknown mail transport error"); + qmgr_defer_transport(transport, transport->reason); + } + + /* + * This message must be tried again. + * + * If we have a problem talking to this site, back off with this site for a + * while; dispose of queue entries for this site that await selection + * (the todo list); stay away from queue entries that have been selected + * (the busy list), or we would have dangling pointers. The queue itself + * won't go away before we dispose of the current queue entry. + */ + if (status == DELIVER_STAT_DEFER) { + message->flags |= DELIVER_STAT_DEFER; + if (VSTRING_LEN(reason)) { + qmgr_queue_throttle(queue, vstring_str(reason)); + if (queue->window == 0) + qmgr_defer_todo(queue, queue->reason); + } + } + + /* + * No problems detected. Mark the transport and queue as alive. The queue + * itself won't go away before we dispose of the current queue entry. + */ + if (status == 0) { + qmgr_transport_unthrottle(transport); + qmgr_queue_unthrottle(queue); + } + + /* + * Release the delivery process, and give some other queue entry a chance + * to be delivered. When all recipients for a message have been tried, + * decide what to do next with this message: defer, bounce, delete. + */ + event_disable_readwrite(vstream_fileno(entry->stream)); + if (vstream_fclose(entry->stream) != 0) + msg_warn("qmgr_deliver_update: close delivery stream: %m"); + entry->stream = 0; + qmgr_deliver_concurrency--; + qmgr_entry_done(entry, QMGR_QUEUE_BUSY); + vstring_free(reason); +} + +/* qmgr_deliver - deliver one per-site queue entry */ + +void qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream) +{ + QMGR_QUEUE *queue; + QMGR_ENTRY *entry; + + /* + * Find out if this delivery process is really available. Once elected, + * the delivery process is supposed to express its happiness. If there is + * a problem, wipe the pending deliveries for this transport. This + * routine runs in response to an external event, so it does not run + * while some other queue manipulation is happening. + */ + if (qmgr_deliver_initial_reply(stream) != 0) { + qmgr_transport_throttle(transport, "mail transport unavailable"); + qmgr_defer_transport(transport, transport->reason); + (void) vstream_fclose(stream); + return; + } + + /* + * Find a suitable queue entry. Things may have changed since this + * transport was allocated. If no suitable entry is found, + * unceremoniously disconnect from the delivery process. The delivery + * agent request reading routine is prepared for the queue manager to + * change its mind for no apparent reason. + */ + if ((queue = qmgr_queue_select(transport)) == 0 + || (entry = qmgr_entry_select(queue)) == 0) { + (void) vstream_fclose(stream); + return; + } + + /* + * Send the queue file info and recipient info to the delivery process. + * If there is a problem, wipe the pending deliveries for this transport. + * This routine runs in response to an external event, so it does not run + * while some other queue manipulation is happening. + */ + if (qmgr_deliver_send_request(entry, stream) < 0) { + qmgr_entry_unselect(queue, entry); + qmgr_transport_throttle(transport, "mail transport unavailable"); + qmgr_defer_transport(transport, transport->reason); + /* warning: entry and queue may be dangling pointers here */ + (void) vstream_fclose(stream); + return; + } + + /* + * If we get this far, go wait for the delivery status report. + */ + qmgr_deliver_concurrency++; + entry->stream = stream; + event_enable_read(vstream_fileno(stream), + qmgr_deliver_update, (char *) entry); +} diff --git a/postfix/qmgr/qmgr_enable.c b/postfix/qmgr/qmgr_enable.c new file mode 100644 index 000000000..5f0c3841b --- /dev/null +++ b/postfix/qmgr/qmgr_enable.c @@ -0,0 +1,107 @@ +/*++ +/* NAME +/* qmgr_enable +/* SUMMARY +/* enable dead transports or sites +/* SYNOPSIS +/* #include "qmgr.h" +/* +/* void qmgr_enable_queue(queue) +/* QMGR_QUEUE *queue; +/* +/* QMGR_QUEUE *qmgr_enable_transport(transport) +/* QMGR_TRANSPORT *transport; +/* +/* void qmgr_enable_all(void) +/* DESCRIPTION +/* This module purges dead in-core state information, effectively +/* re-enabling delivery. +/* +/* qmgr_enable_queue() enables deliveries to the named dead site. +/* Empty queues are destroyed. The existed solely to indicate that +/* a site is dead. +/* +/* qmgr_enable_transport() enables deliveries via the specified +/* transport, and calls qmgr_enable_queue() for each destination +/* on that transport. Empty queues are destroyed. +/* +/* qmgr_enable_all() enables all transports and queues. +/* See above for the side effects caused by doing this. +/* BUGS +/* The side effects of calling this module can be quite dramatic. +/* DIAGNOSTICS +/* Panic: consistency check failure. Fatal: out of memory. +/* 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 + +/* Application-specific. */ + +#include "qmgr.h" + +/* qmgr_enable_all - enable transports and queues */ + +void qmgr_enable_all(void) +{ + QMGR_TRANSPORT *xport; + + if (msg_verbose) + msg_info("qmgr_enable_all"); + + /* + * The number of transports does not change as a side effect, so this can + * be a straightforward loop. + */ + for (xport = qmgr_transport_list.next; xport; xport = xport->peers.next) + qmgr_enable_transport(xport); +} + +/* qmgr_enable_transport - defer todo entries for named transport */ + +void qmgr_enable_transport(QMGR_TRANSPORT *transport) +{ + QMGR_QUEUE *queue; + QMGR_QUEUE *next; + + /* + * Proceed carefully. Queues may disappear as a side effect. + */ + if (transport->flags & QMGR_TRANSPORT_STAT_DEAD) { + if (msg_verbose) + msg_info("enable transport %s", transport->name); + qmgr_transport_unthrottle(transport); + } + for (queue = transport->queue_list.next; queue; queue = next) { + next = queue->peers.next; + qmgr_enable_queue(queue); + } +} + +/* qmgr_enable_queue - enable and possibly delete queue */ + +void qmgr_enable_queue(QMGR_QUEUE *queue) +{ + if (queue->window == 0) { + if (msg_verbose) + msg_info("enable site %s/%s", queue->transport->name, queue->name); + qmgr_queue_unthrottle(queue); + } + if (queue->todo.next == 0 && queue->busy.next == 0) + qmgr_queue_done(queue); +} diff --git a/postfix/qmgr/qmgr_entry.c b/postfix/qmgr/qmgr_entry.c new file mode 100644 index 000000000..8277dbe9b --- /dev/null +++ b/postfix/qmgr/qmgr_entry.c @@ -0,0 +1,185 @@ +/*++ +/* NAME +/* qmgr_entry 3 +/* SUMMARY +/* per-site queue entries +/* SYNOPSIS +/* #include "qmgr.h" +/* +/* QMGR_ENTRY *qmgr_entry_create(queue, message) +/* QMGR_QUEUE *queue; +/* QMGR_MESSAGE *message; +/* +/* void qmgr_entry_done(entry, which) +/* QMGR_ENTRY *entry; +/* int which; +/* +/* QMGR_ENTRY *qmgr_entry_select(queue) +/* QMGR_QUEUE *queue; +/* +/* void qmgr_entry_unselect(queue, entry) +/* QMGR_QUEUE *queue; +/* QMGR_ENTRY *entry; +/* DESCRIPTION +/* These routines add/delete/manipulate per-site message +/* delivery requests. +/* +/* qmgr_entry_create() creates an entry for the named queue and +/* message, and appends the entry to the queue's todo list. +/* Filling in and cleaning up the recipients is the responsibility +/* of the caller. +/* +/* qmgr_entry_done() discards a per-site queue entry. The +/* \fIwhich\fR argument is either QMGR_QUEUE_BUSY for an entry +/* of the site's `busy' list (i.e. queue entries that have been +/* selected for actual delivery), or QMGR_QUEUE_TODO for an entry +/* of the site's `todo' list (i.e. queue entries awaiting selection +/* for actual delivery). +/* +/* qmgr_entry_done() triggers cleanup of the per-site queue when +/* the site has no pending deliveries, and the site is either +/* alive, or the site is dead and the number of in-core queues +/* exceeds a configurable limit (see qmgr_queue_done()). +/* +/* qmgr_entry_done() triggers special action when the last in-core +/* queue entry for a message is done with: either read more +/* recipients from the queue file, delete the queue file, or move +/* the queue file to the deferred queue; send bounce reports to the +/* message originator (see qmgr_active_done()). +/* +/* qmgr_entry_select() randomly selects one entry from the named +/* per-site queue's `todo' list for actual delivery. The entry is +/* moved to the queue's `busy' list: the list of messages being +/* delivered. +/* +/* qmgr_entry_unselect() takes the named entry off the named +/* per-site queue's `busy' list and moves it to the queue's +/* `todo' list. +/* DIAGNOSTICS +/* Panic: interface violations, internal inconsistencies. +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include + +/* Application-specific. */ + +#include "qmgr.h" + +/* qmgr_entry_select - select queue entry for delivery */ + +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) { + QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry); + queue->todo_refcount--; + QMGR_LIST_APPEND(queue->busy, entry); + queue->busy_refcount++; + } + return (entry); +} + +/* qmgr_entry_unselect - unselect queue entry for delivery */ + +void qmgr_entry_unselect(QMGR_QUEUE *queue, QMGR_ENTRY *entry) +{ + QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry); + queue->busy_refcount--; + QMGR_LIST_APPEND(queue->todo, entry); + queue->todo_refcount++; +} + +/* qmgr_entry_done - dispose of queue entry */ + +void qmgr_entry_done(QMGR_ENTRY *entry, int which) +{ + QMGR_QUEUE *queue = entry->queue; + QMGR_MESSAGE *message = entry->message; + + /* + * Take this entry off the in-core queue. + */ + if (entry->stream != 0) + msg_panic("qmgr_entry_done: file is open"); + if (which == QMGR_QUEUE_BUSY) { + QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry); + queue->busy_refcount--; + } else if (which == QMGR_QUEUE_TODO) { + QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry); + queue->todo_refcount--; + } else { + msg_panic("qmgr_entry_done: bad queue spec: %d", which); + } + myfree((char *) entry); + + /* + * When the in-core queue for this site is empty and when this site is + * not dead, discard the in-core queue. When this site is dead, but the + * number of in-core queues exceeds some threshold, get rid of this + * in-core queue anyway, in order to avoid running out of memory. + */ + if (queue->todo.next == 0 && queue->busy.next == 0) { + if (queue->window == 0 && qmgr_queue_count > 2 * var_qmgr_rcpt_limit) + qmgr_queue_unthrottle(queue); + if (queue->window > 0) + qmgr_queue_done(queue); + } + + /* + * Update the in-core message reference count. When the in-core message + * structure has no more references, dispose of the message. + */ + message->refcount--; + if (message->refcount == 0) + qmgr_active_done(message); +} + +/* qmgr_entry_create - create queue todo entry */ + +QMGR_ENTRY *qmgr_entry_create(QMGR_QUEUE *queue, QMGR_MESSAGE *message) +{ + QMGR_ENTRY *entry; + + /* + * Sanity check. + */ + if (queue->window == 0) + msg_panic("qmgr_entry_create: dead queue: %s", queue->name); + + entry = (QMGR_ENTRY *) mymalloc(sizeof(QMGR_ENTRY)); + entry->stream = 0; + entry->message = message; + entry->rcpt_list.len = 0; + entry->rcpt_list.info = 0; + message->refcount++; + entry->queue = queue; + QMGR_LIST_APPEND(queue->todo, entry); + queue->todo_refcount++; + return (entry); +} diff --git a/postfix/qmgr/qmgr_message.c b/postfix/qmgr/qmgr_message.c new file mode 100644 index 000000000..3d185959e --- /dev/null +++ b/postfix/qmgr/qmgr_message.c @@ -0,0 +1,679 @@ +/*++ +/* NAME +/* qmgr_message 3 +/* SUMMARY +/* in-core message structures +/* SYNOPSIS +/* #include "qmgr.h" +/* +/* int qmgr_message_count; +/* int qmgr_recipient_count; +/* +/* QMGR_MESSAGE *qmgr_message_alloc(class, name, qflags) +/* const char *class; +/* const char *name; +/* int qflags; +/* +/* QMGR_MESSAGE *qmgr_message_realloc(message) +/* QMGR_MESSAGE *message; +/* +/* void qmgr_message_free(message) +/* QMGR_MESSAGE *message; +/* DESCRIPTION +/* This module performs en-gross operations on queue messages. +/* +/* qmgr_message_count is a global counter for the total number +/* of in-core message structures (i.e. the total size of the +/* `active' message queue). +/* +/* qmgr_recipient_count is a global counter for the total number +/* of in-core recipient structures (i.e. the sum of all recipients +/* in all in-core message structures). +/* +/* qmgr_message_alloc() creates an in-core message structure +/* with sender and recipient information taken from the named queue +/* file. A null result means the queue file could not be read or +/* that the queue file contained incorrect information. The number +/* of recipients read from a queue file is limited by the global +/* var_qmgr_rcpt_limit configuration parameter. When the limit +/* is reached, the \fIrcpt_offset\fR structure member is set to +/* the position where the read was terminated. Recipients are +/* ru through the resolver, and are assigned to destination +/* queues. Recipients that cannot be assigned are deferred or +/* bounced. Mail that has bounced twice is silently absorbed. +/* +/* qmgr_message_realloc() resumes reading recipients from the queue +/* file, and updates the recipient list and \fIrcpt_offset\fR message +/* structure members. A null result means that the file could not be +/* read or that the file contained incorrect information. +/* +/* 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. +/* DIAGNOSTICS +/* Warnings: malformed message file. Fatal errors: out of memory. +/* SEE ALSO +/* envelope(3) message envelope parser +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Client stubs. */ + +#include + +/* Application-specific. */ + +#include "qmgr.h" + +int qmgr_message_count; +int qmgr_recipient_count; + +/* qmgr_message_create - create in-core message structure */ + +static QMGR_MESSAGE *qmgr_message_create(const char *queue_name, + const char *queue_id, int qflags) +{ + QMGR_MESSAGE *message; + + message = (QMGR_MESSAGE *) mymalloc(sizeof(QMGR_MESSAGE)); + qmgr_message_count++; + message->flags = 0; + message->qflags = qflags; + message->fp = 0; + message->refcount = 0; + message->single_rcpt = 0; + message->arrival_time = 0; + message->data_offset = 0; + message->queue_id = mystrdup(queue_id); + message->queue_name = mystrdup(queue_name); + message->sender = 0; + message->errors_to = 0; + message->return_receipt = 0; + message->data_size = 0; + message->rcpt_offset = 0; + qmgr_rcpt_list_init(&message->rcpt_list); + return (message); +} + +/* qmgr_message_close - close queue file */ + +static void qmgr_message_close(QMGR_MESSAGE *message) +{ + vstream_fclose(message->fp); + message->fp = 0; +} + +/* qmgr_message_open - open queue file */ + +static int qmgr_message_open(QMGR_MESSAGE *message) +{ + + /* + * Sanity check. + */ + if (message->fp) + msg_panic("%s: queue file is open", message->queue_id); + + /* + * Open this queue file. Skip files that we cannot open. Back off when + * the system appears to be running out of resources. + */ + if ((message->fp = mail_queue_open(message->queue_name, + message->queue_id, + O_RDWR, 0)) == 0) { + if (errno != ENOENT) + msg_fatal("open %s %s: %m", message->queue_name, message->queue_id); + msg_warn("open %s %s: %m", message->queue_name, message->queue_id); + return (-1); + } + return (0); +} + +/* qmgr_message_read - read envelope records */ + +static int qmgr_message_read(QMGR_MESSAGE *message) +{ + VSTRING *buf; + long extra_offset; + int rec_type; + long curr_offset; + char *start; + struct stat st; + + /* + * Initialize. No early returns or we have a memory leak. + */ + buf = vstring_alloc(100); + + /* + * If we re-open this file, skip over on-file recipient records that we + * already looked at, and reset the in-core recipient address list. + */ + if (message->rcpt_offset) { + if (vstream_fseek(message->fp, message->rcpt_offset, SEEK_SET) < 0) + msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp)); + message->rcpt_offset = 0; + qmgr_recipient_count -= message->rcpt_list.len; + qmgr_rcpt_list_free(&message->rcpt_list); + qmgr_rcpt_list_init(&message->rcpt_list); + } + + /* + * Read envelope records. XXX Rely on the front-end programs to enforce + * record size limits. Read up to var_qmgr_rcpt_limit recipients from the + * queue file, to protect against memory exhaustion. Recipient records + * may appear before or after the message content, so we keep reading + * from the queue file until we have enough recipients (rcpt_offset != 0) + * and until we know where the message content starts (data_offset != 0). + */ + do { + if ((curr_offset = vstream_ftell(message->fp)) < 0) + msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp)); + rec_type = rec_get(message->fp, buf, 0); + start = vstring_str(buf); + if (rec_type == REC_TYPE_SIZE) { + if (message->data_size == 0) + message->data_size = atol(start); + } else if (rec_type == REC_TYPE_TIME) { + if (message->arrival_time == 0) + message->arrival_time = atol(start); + } else if (rec_type == REC_TYPE_FROM) { + if (message->sender == 0) { + message->sender = mystrdup(start); + opened(message->queue_id, message->sender, + message->data_size, "queue %s", message->queue_name); + } + } else if (rec_type == REC_TYPE_RCPT) { +#define TOTAL_RECIPIENT_COUNT (qmgr_recipient_count + message->rcpt_list.len) + if (TOTAL_RECIPIENT_COUNT < var_qmgr_rcpt_limit) { + qmgr_rcpt_list_add(&message->rcpt_list, curr_offset, start); + if (TOTAL_RECIPIENT_COUNT >= var_qmgr_rcpt_limit) { + if ((message->rcpt_offset = vstream_ftell(message->fp)) < 0) + msg_fatal("vstream_ftell %s: %m", + VSTREAM_PATH(message->fp)); + if (message->data_offset != 0 + && message->errors_to != 0 + && message->return_receipt != 0) + break; + } + } + } else if (rec_type == REC_TYPE_MESG) { + if ((message->data_offset = vstream_ftell(message->fp)) < 0) + msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp)); + if (message->rcpt_offset != 0 + && message->errors_to != 0 + && message->return_receipt != 0) + break; + if ((extra_offset = atol(start)) < curr_offset) { + msg_warn("bad extra offset %s file %s", + start, VSTREAM_PATH(message->fp)); + break; + } + if (vstream_fseek(message->fp, extra_offset, SEEK_SET) < 0) + msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp)); + } else if (rec_type == REC_TYPE_ERTO) { + if (message->errors_to == 0) + message->errors_to = mystrdup(start); + } else if (rec_type == REC_TYPE_RRTO) { + if (message->return_receipt == 0) + message->return_receipt = mystrdup(start); + } + } while (rec_type > 0 && rec_type != REC_TYPE_END); + + qmgr_recipient_count += message->rcpt_list.len; + + /* + * If there is no size record, use the queue file size instead. + */ + if (message->data_size == 0) { + if (fstat(vstream_fileno(message->fp), &st) < 0) + msg_fatal("fstat %s: %m", VSTREAM_PATH(message->fp)); + message->data_size = st.st_size; + } + + /* + * Avoid clumsiness elsewhere in the program. When sending data across an + * IPC channel, sending an empty string is more convenient than sending a + * null pointer. + */ + if (message->errors_to == 0) + message->errors_to = mystrdup(""); + if (message->return_receipt == 0) + message->return_receipt = mystrdup(""); + + /* + * Clean up. + */ + vstring_free(buf); + + /* + * Sanity checks. Verify that all required information was found, + * including the queue file end marker. + */ + if (message->arrival_time == 0 + || message->sender == 0 + || message->data_offset == 0 + || (message->rcpt_offset == 0 && rec_type != REC_TYPE_END)) { + msg_warn("%s: envelope records out of order", message->queue_id); + return (-1); + } else { + return (0); + } +} + +/* qmgr_message_sort_compare - compare recipient information */ + +static int qmgr_message_sort_compare(const void *p1, const void *p2) +{ + QMGR_RCPT *rcpt1 = (QMGR_RCPT *) p1; + QMGR_RCPT *rcpt2 = (QMGR_RCPT *) p2; + QMGR_QUEUE *queue1; + QMGR_QUEUE *queue2; + char *at1; + char *at2; + int result; + + /* + * Compare most significant to least significant recipient attributes. + */ + if ((queue1 = rcpt1->queue) != 0 && (queue2 = rcpt2->queue) != 0) { + + /* + * Compare message transport. + */ + if ((result = strcasecmp(queue1->transport->name, + queue2->transport->name)) != 0) + return (result); + + /* + * Compare next-hop hostname. + */ + if ((result = strcasecmp(queue1->name, queue2->name)) != 0) + return (result); + } + + /* + * Compare recipient domain. + */ + if ((at1 = strrchr(rcpt1->address, '@')) != 0 + && (at2 = strrchr(rcpt2->address, '@')) != 0 + && (result = strcasecmp(at1, at2)) != 0) + return (result); + + /* + * Compare recipient address. + */ + return (strcasecmp(rcpt1->address, rcpt2->address)); +} + +/* qmgr_message_sort - sort message recipient addresses by domain */ + +static void qmgr_message_sort(QMGR_MESSAGE *message) +{ + qsort((char *) message->rcpt_list.info, message->rcpt_list.len, + sizeof(message->rcpt_list.info[0]), qmgr_message_sort_compare); + if (msg_verbose) { + QMGR_RCPT_LIST list = message->rcpt_list; + QMGR_RCPT *rcpt; + + msg_info("start sorted recipient list"); + for (rcpt = list.info; rcpt < list.info + list.len; rcpt++) + msg_info("qmgr_message_sort: %s", rcpt->address); + msg_info("end sorted recipient list"); + } +} + +/* qmgr_message_resolve - resolve recipients */ + +static void qmgr_message_resolve(QMGR_MESSAGE *message) +{ + static ARGV *defer_xport_argv; + QMGR_RCPT_LIST list = message->rcpt_list; + QMGR_RCPT *recipient; + QMGR_TRANSPORT *transport = 0; + QMGR_QUEUE *queue = 0; + RESOLVE_REPLY reply; + const char *newloc; + char *at; + char **cpp; + +#define STREQ(x,y) (strcasecmp(x,y) == 0) +#define STR vstring_str +#define UPDATE(ptr,new) { myfree(ptr); ptr = mystrdup(new); } + + resolve_clnt_init(&reply); + for (recipient = list.info; recipient < list.info + list.len; recipient++) { + + /* + * This may be a bit late in the game, but it is the most convenient + * place to scrutinize the destination address syntax. We have a + * complete queue file, so bouncing is easy. That luxury is not + * available to the cleanup service. The main issue is that we want + * to have this test in one place, instead of having to do this in + * every front-ent program. + */ + if ((at = strrchr(recipient->address, '@')) != 0 + && (at + 1)[strspn(at + 1, "[]0123456789.")] != 0 + && valid_hostname(at + 1) == 0) { + qmgr_bounce_recipient(message, recipient, + "bad host/domain syntax: \"%s\"", at + 1); + continue; + } + + /* + * Resolve the destination to (transport, nexthop, address). The + * result address may differ from the one specified by the sender. + */ + resolve_clnt_query(recipient->address, &reply); + if (!STREQ(recipient->address, STR(reply.recipient))) + UPDATE(recipient->address, STR(reply.recipient)); + + + /* + * Bounce recipients that have moved. We do it here instead of in the + * local delivery agent. The benefit is that we can bounce mail for + * virtual addresses, not just local addresses only, and that there + * is no need to run a local delivery agent just for the sake of + * relocation notices. The downside is that this table has no effect + * on local alias expansion results, so that mail will have to make + * almost an entire iteration through the mail system. + */ +#define IGNORE_ADDR_EXTENSION ((char **) 0) + + if (qmgr_relocated != 0) { + if ((newloc = mail_addr_find(qmgr_relocated, recipient->address, + IGNORE_ADDR_EXTENSION)) != 0) { + qmgr_bounce_recipient(message, recipient, + "user has moved to %s", newloc); + continue; + } else if (dict_errno != 0) { + qmgr_defer_recipient(message, recipient->address, + "relocated map lookup failure"); + continue; + } + } + + /* + * Bounce mail to non-existent users in virtual domains. + */ + if (VSTRING_LEN(reply.nexthop) > 0 + && qmgr_virtual != 0 + && (at = strrchr(recipient->address, '@')) != 0 + && maps_find(qmgr_virtual, at + 1)) { + qmgr_bounce_recipient(message, recipient, + "unknown user: \"%s\"", recipient->address); + continue; + } + + /* + * Queues are identified by the transport name and by the next-hop + * hostname. When the destination is local (no next hop), derive the + * queue name from the recipient name. XXX Should split the address + * on the recipient delimiter if one is defined, but doing a proper + * job requires knowledge of local aliases. Yuck! I don't want to + * duplicate delivery-agent specific knowledge in the queue manager. + */ + if (VSTRING_LEN(reply.nexthop) == 0) { + vstring_strcpy(reply.nexthop, STR(reply.recipient)); + (void) split_at_right(STR(reply.nexthop), '@'); +#if 0 + if (*var_rcpt_delim) + (void) split_addr(STR(reply.nexthop), *var_rcpt_delim); +#endif + + /* + * Discard mail to the local double bounce address here, so this + * system can run without a local delivery agent. They'd still + * have to configure something for mail directed to the local + * postmaster, though, but that is an RFC requirement anyway. + */ + if (strcasecmp(STR(reply.nexthop), var_double_bounce_sender) == 0) { + sent(message->queue_id, recipient->address, + "none", message->arrival_time, "discarded"); + deliver_completed(message->fp, recipient->offset); + continue; + } + } + + /* + * Optionally defer deliveries over specific transports, unless the + * restriction is lifted temporarily. + */ + if (*var_defer_xports && (message->qflags & QMGR_SCAN_ALL) == 0) { + if (defer_xport_argv == 0) + defer_xport_argv = argv_split(var_defer_xports, " \t\r\n,"); + for (cpp = defer_xport_argv->argv; *cpp; cpp++) + if (strcasecmp(*cpp, STR(reply.transport)) == 0) + break; + if (*cpp) { + qmgr_defer_recipient(message, recipient->address, + "deferred transport"); + continue; + } + } + + /* + * XXX Gross hack alert. We want to group recipients by transport and + * by next-hop hostname, in order to minimize the number of network + * transactions. However, it would be wasteful to have an in-memory + * resolver reply structure for each in-core recipient. Instead, we + * bind each recipient to an in-core queue instance which is needed + * anyway. That gives all information needed for recipient grouping. + */ + + /* + * Look up or instantiate the proper transport. + */ + if (transport == 0 || !STREQ(transport->name, STR(reply.transport))) { + if ((transport = qmgr_transport_find(STR(reply.transport))) == 0) + transport = qmgr_transport_create(STR(reply.transport)); + queue = 0; + } + + /* + * This transport is dead. Defer delivery to this recipient. + */ + if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) != 0) { + qmgr_defer_recipient(message, recipient->address, transport->reason); + continue; + } + + /* + * This transport is alive. Find or instantiate a queue for this + * recipient. + */ + if (queue == 0 || !STREQ(queue->name, STR(reply.nexthop))) { + if ((queue = qmgr_queue_find(transport, STR(reply.nexthop))) == 0) + queue = qmgr_queue_create(transport, STR(reply.nexthop)); + } + + /* + * This queue is dead. Defer delivery to this recipient. + */ + if (queue->window == 0) { + qmgr_defer_recipient(message, recipient->address, queue->reason); + continue; + } + + /* + * This queue is alive. Bind this recipient to this queue instance. + */ + recipient->queue = queue; + } + resolve_clnt_free(&reply); +} + +/* qmgr_message_assign - assign recipients to specific delivery requests */ + +static void qmgr_message_assign(QMGR_MESSAGE *message) +{ + QMGR_RCPT_LIST list = message->rcpt_list; + QMGR_RCPT *recipient; + QMGR_ENTRY *entry = 0; + QMGR_QUEUE *queue; + + /* + * Try to bundle as many recipients in a delivery request as we can. When + * the recipient resolves to the same site and transport as the previous + * recipient, do not create a new queue entry, just bump the count of + * recipients for the existing queue entry. All this provided that we do + * not exceed the transport-specific limit on the number of recipients + * per transaction. Skip recipients with a dead transport or destination. + */ +#define LIMIT_OK(limit, count) ((limit) == 0 || ((count) < (limit))) + + for (recipient = list.info; recipient < list.info + list.len; recipient++) { + if ((queue = recipient->queue) != 0) { + if (message->single_rcpt || entry == 0 || entry->queue != queue + || !LIMIT_OK(entry->queue->transport->recipient_limit, + entry->rcpt_list.len)) { + entry = qmgr_entry_create(queue, message); + entry->rcpt_list.info = recipient; + entry->rcpt_list.len = 1; + } else { + entry->rcpt_list.len++; + } + } + } +} + +/* qmgr_message_free - release memory for in-core message structure */ + +void qmgr_message_free(QMGR_MESSAGE *message) +{ + if (message->refcount != 0) + msg_panic("qmgr_message_free: reference len: %d", message->refcount); + if (message->fp) + msg_panic("qmgr_message_free: queue file is open"); + myfree(message->queue_id); + myfree(message->queue_name); + if (message->sender) + myfree(message->sender); + if (message->errors_to) + myfree(message->errors_to); + if (message->return_receipt) + myfree(message->return_receipt); + qmgr_recipient_count -= message->rcpt_list.len; + qmgr_rcpt_list_free(&message->rcpt_list); + qmgr_message_count--; + myfree((char *) message); +} + +/* qmgr_message_alloc - create in-core message structure */ + +QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id, + int qflags) +{ + char *myname = "qmgr_message_alloc"; + QMGR_MESSAGE *message; + + if (msg_verbose) + msg_info("%s: %s %s", myname, queue_name, queue_id); + + /* + * Create an in-core message structure. + */ + message = qmgr_message_create(queue_name, queue_id, qflags); + + /* + * Extract message envelope information: time of arrival, sender address, + * recipient addresses. Skip files with malformed envelope information. + */ + if (qmgr_message_open(message) < 0) { + qmgr_message_free(message); + return (0); + } + if (qmgr_message_read(message) < 0) { + qmgr_message_close(message); + qmgr_message_free(message); + return (0); + } else { + qmgr_message_sort(message); + qmgr_message_resolve(message); + qmgr_message_sort(message); + qmgr_message_assign(message); + qmgr_message_close(message); + return (message); + } +} + +/* qmgr_message_realloc - refresh in-core message structure */ + +QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *message) +{ + char *myname = "qmgr_message_realloc"; + + /* + * Sanity checks. + */ + if (message->rcpt_offset <= 0) + msg_panic("%s: invalid offset: %ld", myname, message->rcpt_offset); + if (message->refcount != 0) + msg_panic("%s: bad refcount: %d", myname, message->refcount); + if (msg_verbose) + msg_info("%s: %s %s offset %ld", myname, message->queue_name, + message->queue_id, message->rcpt_offset); + + /* + * Extract recipient addresses. Skip files with malformed envelope + * information. + */ + if (qmgr_message_open(message) < 0) + return (0); + if (qmgr_message_read(message) < 0) { + qmgr_message_close(message); + return (0); + } else { + qmgr_message_sort(message); + qmgr_message_resolve(message); + qmgr_message_sort(message); + qmgr_message_assign(message); + qmgr_message_close(message); + return (message); + } +} diff --git a/postfix/qmgr/qmgr_move.c b/postfix/qmgr/qmgr_move.c new file mode 100644 index 000000000..255145102 --- /dev/null +++ b/postfix/qmgr/qmgr_move.c @@ -0,0 +1,93 @@ +/*++ +/* NAME +/* qmgr_move 3 +/* SUMMARY +/* move queue entries to another queue +/* SYNOPSIS +/* #include "qmgr.h" +/* +/* void qmgr_move(from, to, time_stamp) +/* const char *from; +/* const char *to; +/* time_t time_stamp; +/* DESCRIPTION +/* The \fBqmgr_move\fR routine scans the \fIfrom\fR queue for entries +/* with valid queue names and moves them to the \fIto\fR queue. +/* If \fItime_stamp\fR is non-zero, the queue file time stamps are +/* set to the specified value. +/* Entries with invalid names are left alone. No attempt is made to +/* look for other badness such as multiple links or weird file types. +/* These issues are dealt with when a queue file is actually opened. +/* 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 + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include + +/* Application-specific. */ + +#include "qmgr.h" + +/* qmgr_move - move queue entries to another queue, leave bad files alone */ + +void qmgr_move(const char *src_queue, const char *dst_queue, + time_t time_stamp) +{ + char *myname = "qmgr_move"; + SCAN_DIR *queue_dir; + char *queue_id; + struct utimbuf tbuf; + const char *path; + + if (strcmp(src_queue, dst_queue) == 0) + msg_panic("%s: source queue %s is destination", myname, src_queue); + if (msg_verbose) + 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) { + if (mail_queue_id_ok(queue_id)) { + if (time_stamp > 0) { + tbuf.actime = tbuf.modtime = time_stamp; + path = mail_queue_path((VSTRING *) 0, src_queue, queue_id); + if (utime(path, &tbuf) < 0) + msg_fatal("%s: update %s time stamps: %m", myname, path); + } + if (mail_queue_rename(queue_id, src_queue, dst_queue)) + msg_fatal("%s: rename %s from %s to %s: %m", + myname, queue_id, src_queue, dst_queue); + if (msg_verbose) + msg_info("%s: moved %s from %s to %s", + myname, queue_id, src_queue, dst_queue); + } else { + msg_warn("%s: ignored: queue %s id %s", + myname, src_queue, queue_id); + } + } + scan_dir_close(queue_dir); + + if (msg_verbose) + msg_info("end move queue %s -> %s", src_queue, dst_queue); +} diff --git a/postfix/qmgr/qmgr_queue.c b/postfix/qmgr/qmgr_queue.c new file mode 100644 index 000000000..8a41c71b2 --- /dev/null +++ b/postfix/qmgr/qmgr_queue.c @@ -0,0 +1,278 @@ +/*++ +/* NAME +/* qmgr_queue 3 +/* SUMMARY +/* per-destination queues +/* SYNOPSIS +/* #include "qmgr.h" +/* +/* int qmgr_queue_count; +/* +/* QMGR_QUEUE *qmgr_queue_create(transport, site) +/* QMGR_TRANSPORT *transport; +/* const char *site; +/* +/* void qmgr_queue_done(queue) +/* QMGR_QUEUE *queue; +/* +/* QMGR_QUEUE *qmgr_queue_find(transport, site) +/* QMGR_TRANSPORT *transport; +/* const char *site; +/* +/* QMGR_QUEUE *qmgr_queue_select(transport) +/* QMGR_TRANSPORT *transport; +/* +/* void qmgr_queue_throttle(queue, reason) +/* QMGR_QUEUE *queue; +/* const char *reason; +/* +/* void qmgr_queue_unthrottle(queue) +/* QMGR_QUEUE *queue; +/* DESCRIPTION +/* These routines add/delete/manipulate per-destination queues. +/* Each queue corresponds to a specific transport and destination. +/* Each queue has a `todo' list of delivery requests for that +/* destination, and a `busy' list of delivery requests in progress. +/* +/* qmgr_queue_count is a global counter for the total number +/* of in-core queue structures. +/* +/* qmgr_queue_create() creates an empty queue for the named +/* transport and destination. The queue is given an initial +/* concurrency limit as specified with the +/* \fIinitial_destination_concurrency\fR configuration parameter, +/* provided that it does not exceed the transport-specific +/* concurrency limit. +/* +/* qmgr_queue_done() disposes of a per-destination queue after all +/* its entries have been taken care of. It is an error to dispose +/* of a dead queue. +/* +/* qmgr_queue_find() looks up the queue for the named destination +/* for the named transport. A null result means that the queue +/* was not found. +/* +/* qmgr_queue_select() uses a round-robin strategy to select +/* from the named transport one per-destination queue with a +/* non-empty `todo' list. +/* +/* qmgr_queue_throttle() handles a delivery error, and decrements the +/* concurrency limit for the destination. When the concurrency limit +/* for a destination becomes zero, qmgr_queue_throttle() starts a timer +/* to re-enable delivery to the destination after a configurable delay. +/* +/* qmgr_queue_unthrottle() undoes qmgr_queue_throttle()'s effects. +/* The concurrency limit for the destination is incremented, +/* provided that it does not exceed the destination concurrency +/* limit specified for the transport. This routine implements +/* "slow open" mode, and eliminates the "thundering herd" problem. +/* DIAGNOSTICS +/* None +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* Application-specific. */ + +#include "qmgr.h" + +int qmgr_queue_count; + +/* qmgr_queue_unthrottle_wrapper - in case (char *) != (struct *) */ + +static void qmgr_queue_unthrottle_wrapper(char *context) +{ + QMGR_QUEUE *queue = (QMGR_QUEUE *) context; + + /* + * This routine runs when a wakeup timer goes off; it does not run in the + * context of some queue manipulation. Therefore, it is safe to discard + * this in-core queue when it is empty and when this site is not dead. + */ + qmgr_queue_unthrottle(queue); + if (queue->window > 0 && queue->todo.next == 0 && queue->busy.next == 0) + qmgr_queue_done(queue); +} + +/* qmgr_queue_unthrottle - give this destination another chance */ + +void qmgr_queue_unthrottle(QMGR_QUEUE *queue) +{ + char *myname = "qmgr_queue_unthrottle"; + + if (msg_verbose) + msg_info("%s: queue %s", myname, queue->name); + + /* + * Special case when this site was dead. + */ + if (queue->window == 0) { + event_cancel_timer(qmgr_queue_unthrottle_wrapper, (char *) queue); + if (queue->reason == 0) + msg_panic("%s: queue %s: window 0 reason 0", myname, queue->name); + myfree(queue->reason); + queue->reason = 0; + } + + /* + * Increase the destination's concurrency limit until we reach the + * transport's concurrency limit. Set the destination's concurrency limit + * 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)) + queue->window = queue->busy_refcount + 1; +} + +/* qmgr_queue_throttle - handle destination delivery failure */ + +void qmgr_queue_throttle(QMGR_QUEUE *queue, const char *reason) +{ + char *myname = "qmgr_queue_throttle"; + + /* + * Sanity checks. + */ + if (queue->reason) + msg_panic("%s: queue %s: spurious reason %s", + myname, queue->name, queue->reason); + if (msg_verbose) + msg_info("%s: queue %s: %s", myname, queue->name, 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. + */ + queue->window = queue->busy_refcount - 1; + + /* + * 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. + */ + if (queue->window == 0) { + queue->reason = mystrdup(reason); + event_request_timer(qmgr_queue_unthrottle_wrapper, + (char *) queue, var_min_backoff_time); + } +} + +/* qmgr_queue_select - select in-core queue for delivery */ + +QMGR_QUEUE *qmgr_queue_select(QMGR_TRANSPORT *transport) +{ + QMGR_QUEUE *queue; + + /* + * If we find a suitable site, rotate the list to enforce round-robin + * selection. See similar selection code in qmgr_transport_select(). + */ + for (queue = transport->queue_list.next; queue; queue = queue->peers.next) { + if (queue->window > queue->busy_refcount && queue->todo.next != 0) { + QMGR_LIST_ROTATE(transport->queue_list, queue); + if (msg_verbose) + msg_info("qmgr_queue_select: %s", queue->name); + return (queue); + } + } + return (0); +} + +/* qmgr_queue_done - delete in-core queue for site */ + +void qmgr_queue_done(QMGR_QUEUE *queue) +{ + char *myname = "qmgr_queue_done"; + QMGR_TRANSPORT *transport = queue->transport; + + /* + * Sanity checks. It is an error to delete an in-core queue with pending + * messages or timers. + */ + if (queue->busy_refcount != 0 || queue->todo_refcount != 0) + msg_panic("%s: refcount: %d", myname, + queue->busy_refcount + queue->todo_refcount); + if (queue->todo.next || queue->busy.next) + msg_panic("%s: queue not empty: %s", myname, queue->name); + if (queue->window <= 0) + msg_panic("%s: window %d", myname, queue->window); + if (queue->reason) + msg_panic("%s: queue %s: spurious reason %s", + myname, queue->name, queue->reason); + + /* + * Clean up this in-core queue. + */ + QMGR_LIST_UNLINK(transport->queue_list, QMGR_QUEUE *, queue); + htable_delete(transport->queue_byname, queue->name, (void (*) (char *)) 0); + myfree(queue->name); + qmgr_queue_count--; + myfree((char *) queue); +} + +/* qmgr_queue_create - create in-core queue for site */ + +QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *site) +{ + QMGR_QUEUE *queue; + + /* + * 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++; + queue->name = mystrdup(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; + QMGR_LIST_INIT(queue->todo); + QMGR_LIST_INIT(queue->busy); + queue->reason = 0; + QMGR_LIST_APPEND(transport->queue_list, queue); + htable_enter(transport->queue_byname, site, (char *) queue); + return (queue); +} + +/* qmgr_queue_find - find in-core queue for site */ + +QMGR_QUEUE *qmgr_queue_find(QMGR_TRANSPORT *transport, const char *site) +{ + return ((QMGR_QUEUE *) htable_find(transport->queue_byname, site)); +} diff --git a/postfix/qmgr/qmgr_rcpt_list.c b/postfix/qmgr/qmgr_rcpt_list.c new file mode 100644 index 000000000..2bb292e01 --- /dev/null +++ b/postfix/qmgr/qmgr_rcpt_list.c @@ -0,0 +1,96 @@ +/*++ +/* NAME +/* qmgr_rcpt_list 3 +/* SUMMARY +/* in-core recipient structures +/* SYNOPSIS +/* #include "qmgr.h" +/* +/* void qmgr_rcpt_list_init(list) +/* QMGR_RCPT_LIST *list; +/* +/* void qmgr_rcpt_list_add(list, offset, recipient) +/* QMGR_RCPT_LIST *list; +/* long offset; +/* const char *recipient; +/* +/* void qmgr_rcpt_list_free(list) +/* QMGR_RCPT_LIST *list; +/* DESCRIPTION +/* This module maintains lists of queue manager recipient structures. +/* These structures are extended versions of the structures maintained +/* by the recipient_list(3) module. The extension is that the queue +/* manager version of a recipient can have a reference to a queue +/* structure. +/* +/* qmgr_rcpt_list_init() creates an empty recipient structure list. +/* The list argument is initialized such that it can be given to +/* qmgr_rcpt_list_add() and qmgr_rcpt_list_free(). +/* +/* qmgr_rcpt_list_add() adds a recipient to the specified list. +/* The recipient name is copied. +/* +/* qmgr_rcpt_list_free() releases memory for the specified list +/* of recipient structures. +/* SEE ALSO +/* qmgr_rcpt_list(3h) data structure +/* recipient_list(3) same code, different data structure. +/* DIAGNOSTICS +/* Fatal errors: memory allocation. +/* 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 + +/* Application-specific. */ + +#include "qmgr.h" + +/* qmgr_rcpt_list_init - initialize */ + +void qmgr_rcpt_list_init(QMGR_RCPT_LIST *list) +{ + list->avail = 1; + list->len = 0; + list->info = (QMGR_RCPT *) mymalloc(sizeof(QMGR_RCPT)); +} + +/* qmgr_rcpt_list_add - add rcpt to list */ + +void qmgr_rcpt_list_add(QMGR_RCPT_LIST *list, long offset, const char *rcpt) +{ + if (list->len >= list->avail) { + list->avail *= 2; + list->info = (QMGR_RCPT *) + myrealloc((char *) list->info, list->avail * sizeof(QMGR_RCPT)); + } + list->info[list->len].address = mystrdup(rcpt); + list->info[list->len].offset = offset; + list->info[list->len].queue = 0; + list->len++; +} + +/* qmgr_rcpt_list_free - release memory for in-core recipient structure */ + +void qmgr_rcpt_list_free(QMGR_RCPT_LIST *list) +{ + QMGR_RCPT *rcpt; + + for (rcpt = list->info; rcpt < list->info + list->len; rcpt++) + myfree(rcpt->address); + myfree((char *) list->info); +} diff --git a/postfix/qmgr/qmgr_scan.c b/postfix/qmgr/qmgr_scan.c new file mode 100644 index 000000000..f73e1e1ed --- /dev/null +++ b/postfix/qmgr/qmgr_scan.c @@ -0,0 +1,155 @@ +/*++ +/* NAME +/* qmgr_scan 3 +/* SUMMARY +/* queue scanning +/* SYNOPSIS +/* #include "qmgr.h" +/* +/* QMGR_SCAN *qmgr_scan_create(queue_name) +/* const char *queue_name; +/* +/* char *qmgr_scan_next(scan_info) +/* QMGR_SCAN *scan_info; +/* +/* void qmgr_scan_request(scan_info, flags) +/* QMGR_SCAN *scan_info; +/* int flags; +/* DESCRIPTION +/* This module implements queue scans. A queue scan always runs +/* to completion, so that all files get a fair chance. The caller +/* can request that a queue scan be restarted once it completes. +/* +/* qmgr_scan_create() creates a context for scanning the named queue, +/* but does not start a queue scan. +/* +/* qmgr_scan_next() returns the base name of the next queue file. +/* A null pointer means that no file was found. qmgr_scan_next() +/* automagically restarts a queue scan when a scan request had +/* arrived while the scan was in progress. +/* +/* qmgr_scan_request() records a request for the next queue scan. The +/* flags argument is the bit-wise OR of zero or more of the following, +/* unrecognized flags being ignored: +/* .IP QMGR_FLUSH_DEAD +/* Forget state information about dead hosts or transports. This +/* request takes effect upon the next queue scan. +/* .IP QMGR_SCAN_ALL +/* Ignore queue file time stamps. +/* This flag is passed on to the qmgr_active_feed() routine. +/* .IP QMGR_SCAN_START +/* Start a queue scan when none is in progress, or restart the +/* current scan upon completion. +/* DIAGNOSTICS +/* Fatal: out of memory. +/* Panic: interface violations, internal consistency errors. +/* 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 + +/* Application-specific. */ + +#include "qmgr.h" + +/* qmgr_scan_start - start queue scan */ + +static void qmgr_scan_start(QMGR_SCAN *scan_info) +{ + char *myname = "qmgr_scan_start"; + + /* + * Sanity check. + */ + if (scan_info->handle) + msg_panic("%s: %s queue scan in progress", + myname, scan_info->queue); + + /* + * Give the poor tester a clue. + */ + if (msg_verbose) + msg_info("%s: %sstart %s queue scan", + myname, + scan_info->nflags & QMGR_SCAN_START ? "re" : "", + scan_info->queue); + + /* + * Optionally forget all dead host information. + */ + if (scan_info->nflags & QMGR_FLUSH_DEAD) + qmgr_enable_all(); + + /* + * Start or restart the scan. + */ + scan_info->flags = scan_info->nflags; + scan_info->nflags = 0; + scan_info->handle = scan_dir_open(scan_info->queue); +} + +/* qmgr_scan_request - request for future scan */ + +void qmgr_scan_request(QMGR_SCAN *scan_info, int flags) +{ + + /* + * If a scan is in progress, just record the request. + */ + scan_info->nflags |= flags; + if (scan_info->handle == 0 && (flags & QMGR_SCAN_START) != 0) { + scan_info->nflags &= ~QMGR_SCAN_START; + qmgr_scan_start(scan_info); + } +} + +/* qmgr_scan_next - look for next queue file */ + +char *qmgr_scan_next(QMGR_SCAN *scan_info) +{ + char *path = 0; + + /* + * 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) { + 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); + } + return (path); +} + +/* qmgr_scan_create - create queue scan context */ + +QMGR_SCAN *qmgr_scan_create(const char *queue) +{ + QMGR_SCAN *scan_info; + + scan_info = (QMGR_SCAN *) mymalloc(sizeof(*scan_info)); + scan_info->queue = mystrdup(queue); + scan_info->flags = scan_info->nflags = 0; + scan_info->handle = 0; + return (scan_info); +} diff --git a/postfix/qmgr/qmgr_transport.c b/postfix/qmgr/qmgr_transport.c new file mode 100644 index 000000000..a10714f56 --- /dev/null +++ b/postfix/qmgr/qmgr_transport.c @@ -0,0 +1,327 @@ +/*++ +/* NAME +/* qmgr_transport 3 +/* SUMMARY +/* per-transport data structures +/* SYNOPSIS +/* #include "qmgr.h" +/* +/* QMGR_TRANSPORT *qmgr_transport_create(name) +/* const char *name; +/* +/* QMGR_TRANSPORT *qmgr_transport_find(name) +/* const char *name; +/* +/* QMGR_TRANSPORT *qmgr_transport_select() +/* +/* void qmgr_transport_alloc(transport, notify) +/* QMGR_TRANSPORT *transport; +/* void (*notify)(QMGR_TRANSPORT *transport, VSTREAM *fp); +/* +/* void qmgr_transport_throttle(transport, reason) +/* QMGR_TRANSPORT *transport; +/* const char *reason; +/* +/* void qmgr_transport_unthrottle(transport) +/* QMGR_TRANSPORT *transport; +/* DESCRIPTION +/* This module organizes the world by message transport type. +/* Each transport can have zero or more destination queues +/* associated with it. +/* +/* qmgr_transport_create() instantiates a data structure for the +/* named transport type. +/* +/* qmgr_transport_find() looks up an existing message transport +/* data structure. +/* +/* qmgr_transport_select() attempts to find a transport that +/* has messages pending delivery. This routine implements +/* round-robin search among transports. +/* +/* qmgr_transport_alloc() allocates a delivery process for the +/* specified transport type. Allocation is performed asynchronously. +/* When a process becomes available, the application callback routine +/* is invoked with as arguments the transport and a stream that +/* is connected to a delivery process. It is an error to call +/* qmgr_transport_alloc() while delivery process allocation for +/* the same transport is in progress. +/* +/* qmgr_transport_throttle blocks further allocation of delivery +/* processes for the named transport. Attempts to throttle a +/* throttled transport are ignored. +/* +/* qmgr_transport_unthrottle() undoes qmgr_transport_throttle(). +/* Attempts to unthrottle a non-throttled transport are ignored. +/* DIAGNOSTICS +/* Panic: consistency check failure. Fatal: out of memory. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +#include "qmgr.h" + +HTABLE *qmgr_transport_byname; /* transport by name */ +QMGR_TRANSPORT_LIST qmgr_transport_list;/* transports, round robin */ + + /* + * A local structure to remember a delivery process allocation request. + */ +typedef struct QMGR_TRANSPORT_ALLOC QMGR_TRANSPORT_ALLOC; + +struct QMGR_TRANSPORT_ALLOC { + QMGR_TRANSPORT *transport; /* transport context */ + VSTREAM *stream; /* delivery service stream */ + QMGR_TRANSPORT_ALLOC_NOTIFY notify; /* application call-back routine */ +}; + +/* qmgr_transport_unthrottle_wrapper - in case (char *) != (struct *) */ + +static void qmgr_transport_unthrottle_wrapper(char *context) +{ + qmgr_transport_unthrottle((QMGR_TRANSPORT *) context); +} + +/* qmgr_transport_unthrottle - open the throttle */ + +void qmgr_transport_unthrottle(QMGR_TRANSPORT *transport) +{ + char *myname = "qmgr_transport_unthrottle"; + + /* + * This routine runs after expiration of the timer set by + * qmgr_transport_throttle(), or whenever a delivery transport has been + * used without malfunction. In either case, we enable delivery again if + * the transport was blocked, otherwise the request is ignored. + */ + if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) != 0) { + if (msg_verbose) + msg_info("%s: transport %s", myname, transport->name); + transport->flags &= ~QMGR_TRANSPORT_STAT_DEAD; + if (transport->reason == 0) + msg_panic("%s: transport %s: null reason", myname, transport->name); + myfree(transport->reason); + transport->reason = 0; + event_cancel_timer(qmgr_transport_unthrottle_wrapper, + (char *) transport); + } +} + +/* qmgr_transport_throttle - disable delivery process allocation */ + +void qmgr_transport_throttle(QMGR_TRANSPORT *transport, const char *reason) +{ + char *myname = "qmgr_transport_throttle"; + + /* + * We are unable to connect to a deliver process for this type of message + * transport. Instead of hosing the system by retrying in a tight loop, + * back off and disable this transport type for a while. + */ + if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) == 0) { + if (msg_verbose) + msg_info("%s: transport %s: reason: %s", + myname, transport->name, reason); + transport->flags |= QMGR_TRANSPORT_STAT_DEAD; + if (transport->reason) + msg_panic("%s: transport %s: spurious reason: %s", + myname, transport->name, transport->reason); + transport->reason = mystrdup(reason); + event_request_timer(qmgr_transport_unthrottle_wrapper, + (char *) transport, var_transport_retry_time); + } +} + +/* qmgr_transport_event - delivery process availability notice */ + +static void qmgr_transport_event(int unused_event, char *context) +{ + QMGR_TRANSPORT_ALLOC *alloc = (QMGR_TRANSPORT_ALLOC *) context; + + /* + * This routine notifies the application when the request given to + * qmgr_transport_alloc() completes. + */ + if (msg_verbose) + msg_info("transport_event: %s", alloc->transport->name); + + /* + * Disable further read events that end up calling this function. + */ + event_disable_readwrite(vstream_fileno(alloc->stream)); + alloc->transport->flags &= ~QMGR_TRANSPORT_STAT_BUSY; + + /* + * Notify the requestor. + */ + alloc->notify(alloc->transport, alloc->stream); + myfree((char *) alloc); +} + +#ifdef UNIX_DOMAIN_CONNECT_BLOCKS_FOR_ACCEPT + +/* qmgr_transport_connect - handle connection request completion */ + +static void qmgr_transport_connect(int unused_event, char *context) +{ + QMGR_TRANSPORT_ALLOC *alloc = (QMGR_TRANSPORT_ALLOC *) context; + + /* + * This code is necessary for some versions of LINUX, where connect(2) + * blocks until the application performs an accept(2). Reportedly, the + * same can happen on Solaris 2.5.1. + */ + event_disable_readwrite(vstream_fileno(alloc->stream)); + non_blocking(vstream_fileno(alloc->stream), BLOCKING); + event_enable_read(vstream_fileno(alloc->stream), + qmgr_transport_event, (char *) alloc); +} + +#endif + +/* qmgr_transport_select - select transport for allocation */ + +QMGR_TRANSPORT *qmgr_transport_select(void) +{ + QMGR_TRANSPORT *xport; + QMGR_QUEUE *queue; + + /* + * If we find a suitable transport, rotate the list of transports to + * effectuate round-robin selection. See similar selection code in + * qmgr_queue_select(). + */ +#define STAY_AWAY (QMGR_TRANSPORT_STAT_BUSY | QMGR_TRANSPORT_STAT_DEAD) + + for (xport = qmgr_transport_list.next; xport; xport = xport->peers.next) { + if (xport->flags & STAY_AWAY) + continue; + for (queue = xport->queue_list.next; queue; queue = queue->peers.next) { + if (queue->window > queue->busy_refcount && queue->todo.next != 0) { + QMGR_LIST_ROTATE(qmgr_transport_list, xport); + if (msg_verbose) + msg_info("qmgr_transport_select: %s", xport->name); + return (xport); + } + } + } + return (0); +} + +/* qmgr_transport_alloc - allocate delivery process */ + +void qmgr_transport_alloc(QMGR_TRANSPORT *transport, QMGR_TRANSPORT_ALLOC_NOTIFY notify) +{ + QMGR_TRANSPORT_ALLOC *alloc; + VSTREAM *stream; + + /* + * Sanity checks. + */ + if (transport->flags & QMGR_TRANSPORT_STAT_DEAD) + msg_panic("qmgr_transport: dead transport: %s", transport->name); + if (transport->flags & QMGR_TRANSPORT_STAT_BUSY) + msg_panic("qmgr_transport: nested allocation: %s", transport->name); + + /* + * Connect to the well-known port for this delivery service, and wake up + * when a process announces its availability. In the mean time, block out + * other delivery process allocation attempts for this transport. In case + * of problems, back off. Do not hose the system when it is in trouble + * already. + */ +#ifdef UNIX_DOMAIN_CONNECT_BLOCKS_FOR_ACCEPT +#define BLOCK_MODE NON_BLOCKING +#define ENABLE_EVENTS event_enable_write +#define EVENT_HANDLER qmgr_transport_connect +#else +#define BLOCK_MODE BLOCKING +#define ENABLE_EVENTS event_enable_read +#define EVENT_HANDLER qmgr_transport_event +#endif + + if ((stream = mail_connect(MAIL_CLASS_PRIVATE, transport->name, BLOCK_MODE)) == 0) { + msg_warn("connect to transport %s: %m", transport->name); + qmgr_transport_throttle(transport, "transport is unavailable"); + return; + } + alloc = (QMGR_TRANSPORT_ALLOC *) mymalloc(sizeof(*alloc)); + alloc->stream = stream; + alloc->transport = transport; + alloc->notify = notify; + transport->flags |= QMGR_TRANSPORT_STAT_BUSY; + ENABLE_EVENTS(vstream_fileno(alloc->stream), EVENT_HANDLER, (char *) alloc); +} + +/* qmgr_transport_create - create transport instance */ + +QMGR_TRANSPORT *qmgr_transport_create(const char *name) +{ + QMGR_TRANSPORT *transport; + + if (htable_find(qmgr_transport_byname, name) != 0) + msg_panic("qmgr_transport_create: transport exists: %s", name); + transport = (QMGR_TRANSPORT *) mymalloc(sizeof(QMGR_TRANSPORT)); + transport->flags = 0; + transport->name = mystrdup(name); + + /* + * Use global configuration settings or transport-specific settings. + */ + transport->dest_concurrency_limit = + get_config_int2(name, "_destination_concurrency_limit", + var_dest_con_limit, 0, 0); + transport->recipient_limit = + get_config_int2(name, "_destination_recipient_limit", + var_dest_rcpt_limit, 0, 0); + + transport->queue_byname = htable_create(0); + QMGR_LIST_INIT(transport->queue_list); + transport->reason = 0; + if (qmgr_transport_byname == 0) + qmgr_transport_byname = htable_create(10); + htable_enter(qmgr_transport_byname, name, (char *) transport); + QMGR_LIST_APPEND(qmgr_transport_list, transport); + if (msg_verbose) + msg_info("qmgr_transport_create: %s concurrency %d recipients %d", + transport->name, transport->dest_concurrency_limit, + transport->recipient_limit); + return (transport); +} + +/* qmgr_transport_find - find transport instance */ + +QMGR_TRANSPORT *qmgr_transport_find(const char *name) +{ + return ((QMGR_TRANSPORT *) htable_find(qmgr_transport_byname, name)); +} diff --git a/postfix/sendmail/.indent.pro b/postfix/sendmail/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/sendmail/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/sendmail/.printfck b/postfix/sendmail/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/sendmail/.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/sendmail/Makefile.in b/postfix/sendmail/Makefile.in new file mode 100644 index 000000000..9f401a365 --- /dev/null +++ b/postfix/sendmail/Makefile.in @@ -0,0 +1,86 @@ +SHELL = /bin/sh +SRCS = sendmail.c +OBJS = sendmail.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 = sendmail +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' +sendmail.o: sendmail.c +sendmail.o: ../include/sys_defs.h +sendmail.o: ../include/msg.h +sendmail.o: ../include/mymalloc.h +sendmail.o: ../include/vstream.h +sendmail.o: ../include/vbuf.h +sendmail.o: ../include/vstring.h +sendmail.o: ../include/msg_vstream.h +sendmail.o: ../include/msg_syslog.h +sendmail.o: ../include/vstring_vstream.h +sendmail.o: ../include/username.h +sendmail.o: ../include/fullname.h +sendmail.o: ../include/argv.h +sendmail.o: ../include/safe.h +sendmail.o: ../include/iostuff.h +sendmail.o: ../include/stringops.h +sendmail.o: ../include/mail_queue.h +sendmail.o: ../include/mail_proto.h +sendmail.o: ../include/mail_params.h +sendmail.o: ../include/record.h +sendmail.o: ../include/rec_type.h +sendmail.o: ../include/rec_streamlf.h +sendmail.o: ../include/config.h +sendmail.o: ../include/cleanup_user.h +sendmail.o: ../include/mail_task.h +sendmail.o: ../include/mail_run.h +sendmail.o: ../include/debug_process.h +sendmail.o: ../include/tok822.h +sendmail.o: ../include/resolve_clnt.h +sendmail.o: ../include/mail_flush.h +sendmail.o: ../include/mail_stream.h diff --git a/postfix/sendmail/sendmail.c b/postfix/sendmail/sendmail.c new file mode 100644 index 000000000..2df9d7110 --- /dev/null +++ b/postfix/sendmail/sendmail.c @@ -0,0 +1,787 @@ +/*++ +/* NAME +/* sendmail 1 +/* SUMMARY +/* Postfix to Sendmail compatibility interface +/* SYNOPSIS +/* \fBsendmail\fR [\fIoption ...\fR] [\fIrecipient ...\fR] +/* +/* \fBmailq\fR +/* \fBsendmail -bp\fR +/* +/* \fBnewaliases\fR +/* \fBsendmail -I\fR +/* DESCRIPTION +/* The \fBsendmail\fR program implements the Postfix to Sendmail +/* compatibility interface. +/* For the sake of compatibility with existing applications, some +/* 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 +/* \fBpostdrop\fR(1) command, which is expected to execute with +/* suitable privileges. +/* +/* Specific command aliases are provided for other common modes of +/* operation: +/* .IP \fBmailq\fR +/* List the mail queue. Each entry shows the queue file ID, message +/* size, arrival time, sender, and the recipients that still need to +/* be delivered. If mail could not be delivered upon the last attempt, +/* the reason for failure is shown. This mode of operation is implemented +/* by connecting to the \fBshowq\fR(8) daemon. +/* .IP \fBnewaliases\fR +/* Initialize the alias database. If no alias database type is +/* specified, the program uses the type specified in the +/* \fBdatabase_type\fR configuration parameter; if no input file +/* is specified, the program processes the file(s) specified with the +/* \fBalias_database\fR configuration parameter. This mode of operation +/* is implemented by running the \fBpostalias\fR(1) command. +/* .sp +/* Note: it may take a minute or so before an alias database update +/* becomes visible. Use the \fBpostfix reload\fR command to eliminate +/* this delay. +/* .PP +/* These and other features can be selected by specifying the +/* appropriate combination of command-line options. Some features are +/* controlled by parameters in the \fBmain.cf\fR configuration file. +/* +/* The following options are recognized: +/* .IP "\fB-B \fIbody_type\fR (ignored)" +/* The message body MIME type. Currently, Postfix implements +/* \fBjust-send-eight\fR. +/* .IP "\fB-C \fIconfig_file\fR (ignored :-)" +/* The path name of the \fBsendmail.cf\fR file. Postfix configuration +/* files are kept in \fB/etc/postfix\fR. +/* .IP "\fB-F \fIfull_name\fR +/* Set the sender full name. This is used only with messages that +/* have no \fBFrom:\fR message header. +/* .IP \fB-I\fR +/* Initialize alias database. See the \fBnewaliases\fR +/* command above. +/* .IP "\fB-N \fIdsn\fR (ignored)" +/* Delivery status notification control. Currently, Postfix does +/* not implement \fBDSN\fR. +/* .IP "\fB-R \fIreturn_limit\fR (ignored)" +/* Limit the size of bounced mail. Use the \fBbounce_size_limit\fR +/* configuration parameter instead. +/* .IP "\fB-X \fIlog_file\fR (ignored)" +/* Log mailer traffic. Use the \fBdebug_peer_list\fR and +/* \fBdebug_peer_level\fR configuration parameters instead. +/* .IP \fB-bd\fR +/* Go into daemon mode. This mode of operation is implemented by +/* executing the \fBpostfix start\fR command. +/* .IP \fB-bi\fR +/* Initialize alias database. See the \fBnewaliases\fR +/* command above. +/* .IP \fB-bm\fR +/* Read mail from standard input and arrange for delivery. +/* This is the default mode of operation. +/* .IP \fB-bp\fR +/* List the mail queue. See the \fBmailq\fR command above. +/* .IP \fB-bs\fR +/* Stand-alone SMTP server mode. Read SMTP commands from +/* standard input, and write responses to standard output. +/* This mode of operation is implemented by running the +/* \fBsmtpd\fR(8) daemon. +/* .IP "\fB-f \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-h \fIhop_count\fR (ignored)" +/* Hop count limit. Use the \fBhopcount_limit\fR configuration +/* parameter instead. +/* .IP "\fB-i\fR (ignored)" +/* Lines beginning with "." get special treatment only with \fB-bs\fR. +/* .IP "\fB-m\fR (ignored)" +/* Backwards compatibility. +/* .IP "\fB-n\fR (ignored)" +/* Backwards compatibility. +/* .IP "\fB-oA\fIalias_database\fR" +/* Non-default alias database. Specify \fIpathname\fR or +/* \fItype\fR:\fIpathname\fR. See \fBpostalias\fR(1) for +/* details. +/* .IP "\fB-o7\fR (ignored)" +/* .IP "\fB-o8\fR (ignored)" +/* The message body type. Currently, Postfix implements +/* \fBjust-send-eight\fR. +/* .IP "\fB-om\fR (ignored)" +/* 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-q\fR +/* Flush the mail queue. This is implemented by kicking the +/* \fBqmgr\fR(8) daemon. +/* .IP "\fB-q\fIinterval\fR (ignored)" +/* The interval between queue runs. Use the \fBqueue_run_delay\fR +/* configuration parameter instead. +/* .IP \fB-t\fR +/* Extract recipients from message headers. This requires that no +/* recipients be specified on the command line. +/* .IP \fB-v\fR +/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR +/* options make the software increasingly verbose. +/* SECURITY +/* .ad +/* .fi +/* 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 \fBsyslogd\fR(8) and to the standard error +/* stream. +/* ENVIRONMENT +/* .ad +/* .fi +/* .IP \fBMAIL_CONFIG\fR +/* Directory with Postfix configuration files. +/* .IP \fBMAIL_VERBOSE\fR +/* Enable verbose logging +/* .IP \fBMAIL_DEBUG\fR +/* Enable debugging with an external command, as specified with the +/* \fBdebugger_command\fR configuration parameter. +/* FILES +/* /var/spool/postfix, mail queue +/* /etc/postfix, configuration files +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* See the Postfix \fBmain.cf\fR file for syntax details and for +/* default values. Use the \fBpostfix reload\fR command after a +/* configuration change. +/* .IP \fBalias_database\fR +/* Default alias database(s) for \fBnewaliases\fR. The default value +/* for this parameter is system-specific. +/* .IP \fBbounce_size_limit\fR +/* The amount of original message context that is sent along +/* with a non-delivery notification. +/* .IP \fBdatabase_type\fR +/* Default alias etc. database type. On many UNIX systems the +/* default type is either \fBdbm\fR or \fBhash\fR. +/* .IP \fBdebugger_command\fR +/* Command that is executed after a Postfix daemon has initialized. +/* .IP \fBdebug_peer_level\fR +/* Increment in verbose logging level when a remote host matches a +/* pattern in the \fBdebug_peer_list\fR parameter. +/* .IP \fBdebug_peer_list\fR +/* 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 \fBfork_attempts\fR +/* Number of attempts to \fBfork\fR() a process before giving up. +/* .IP \fBfork_delay\fR +/* Delay in seconds between successive \fBfork\fR() attempts. +/* .IP \fBhopcount_limit\fR +/* Limit the number of \fBReceived:\fR message headers. +/* .IP \fBmail_owner\fR +/* The owner of the mail queue and of most Postfix processes. +/* .IP \fBcommand_directory\fR +/* Directory with Postfix support commands (default: +/* \fB$program_directory\fR). +/* .IP \fBdaemon_directory\fR +/* Directory with Postfix daemon programs (default: +/* \fB$program_directory\fR). +/* .IP \fBqueue_directory\fR +/* Top-level directory of the Postfix queue. This is also the root +/* directory of Postfix daemons that run chrooted. +/* .IP \fBqueue_run_delay\fR +/* The time between successive scans of the deferred queue. +/* SEE ALSO +/* pickup(8) mail pickup daemon +/* postalias(1) maintain alias database +/* postdrop(1) privileged posting agent +/* postfix(1) mail system control +/* postkick(1) kick a Postfix daemon +/* qmgr(8) queue manager +/* showq(8) list mail queue +/* smtpd(8) SMTP server +/* syslogd(8) system logging +/* 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 /* remove() */ +#include +#include +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + + /* + * Modes of operation. + */ +#define SM_MODE_ENQUEUE 1 /* delivery mode */ +#define SM_MODE_NEWALIAS 2 /* initialize alias database */ +#define SM_MODE_MAILQ 3 /* list mail queue */ +#define SM_MODE_DAEMON 4 /* daemon mode */ +#define SM_MODE_USER 5 /* user (stand-alone) mode */ +#define SM_MODE_FLUSHQ 6 /* user (stand-alone) mode */ + + /* + * Queue file name. Global, so that the cleanup routine can find it when + * called by the run-time error handler. + */ +static char *sendmail_path; +static void sendmail_cleanup(void); + + /* + * Silly little macros (SLMs). + */ +#define STR vstring_str + +/* enqueue - post one message */ + +static void enqueue(const char *sender, const char *full_name, char **recipients) +{ + VSTRING *buf; + VSTREAM *dst; + char *saved_sender; + char **cpp; + int type; + char *start; + int skip_from_; + TOK822 *tree; + TOK822 *tp; + enum { + STRIP_CR_DUNNO, STRIP_CR_DO, STRIP_CR_DONT + } strip_cr; + MAIL_STREAM *handle; + char *postdrop_command; + uid_t uid = getuid(); + int status; + + /* + * Initialize. + */ + buf = vstring_alloc(100); + + /* + * The sender name is provided by the user. In principle, the mail pickup + * service could deduce the sender name from queue file ownership, but: + * pickup would not be able to run chrooted, and it may not be desirable + * to use login names at all. + */ + if (sender == 0) + if ((sender = username()) == 0) + msg_fatal("unable to find out your login name"); + saved_sender = mystrdup(sender); + + /* + * 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 + * queue file. + */ + if (access(MAIL_QUEUE_MAILDROP, W_OK) == 0) { + handle = mail_stream_file(MAIL_QUEUE_MAILDROP, + MAIL_CLASS_PUBLIC, MAIL_SERVICE_PICKUP); + sendmail_path = mystrdup(VSTREAM_PATH(handle->stream)); + } else { + postdrop_command = concatenate(var_command_dir, "/postdrop", + msg_verbose ? " -v" : (char *) 0, (char *) 0); + if ((handle = mail_stream_command(postdrop_command)) == 0) + msg_fatal("%s(%d): unable to execute %s", + saved_sender, uid, postdrop_command); + myfree(postdrop_command); + } + dst = handle->stream; + + /* + * First, write envelope information to the output stream. + * + * For sendmail compatibility, parse each command-line recipient as if it + * were an RFC 822 message header; some MUAs specify comma-separated + * recipient lists; and some MUAs even specify "word word
    ". + * + * Sort-uniq-ing the recipient list is done after address canonicalization, + * before recipients are written to queue file. That's cleaner than + * having the queue manager nuke duplicate recipient status records. + * + * XXX Should limit the size of envelope records. + */ + rec_fprintf(dst, REC_TYPE_TIME, "%ld", (long) time((time_t *) 0)); + if (full_name || (full_name = fullname()) != 0) + rec_fputs(dst, REC_TYPE_FULL, full_name); + rec_fputs(dst, REC_TYPE_FROM, saved_sender); + if (recipients) { + for (cpp = recipients; *cpp != 0; cpp++) { + tree = tok822_parse(*cpp); + for (tp = tree; tp != 0; tp = tp->next) { + if (tp->type == TOK822_ADDR) { + tok822_internalize(buf, tp->head, TOK822_STR_DEFL); + if (REC_PUT_BUF(dst, REC_TYPE_RCPT, buf) < 0) + msg_fatal("%s(%d): error writing queue file: %m", + saved_sender, uid); + } + } + tok822_free_tree(tree); + } + } + + /* + * Append the message contents to the queue file. Write chunks of at most + * 1kbyte. Internally, we use different record types for data ending in + * LF and for data that doesn't, so we can actually be binary transparent + * for local mail. Unfortunately, SMTP has no record continuation + * convention, so there is no guarantee that arbitrary data will be + * 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); + skip_from_ = 1; + strip_cr = STRIP_CR_DUNNO; + while ((type = rec_streamlf_get(VSTREAM_IN, buf, var_line_limit)) + != REC_TYPE_EOF) { + if (strip_cr == STRIP_CR_DUNNO && type == REC_TYPE_NORM) { + if (VSTRING_LEN(buf) > 0 && vstring_end(buf)[-1] == '\r') + strip_cr = STRIP_CR_DO; + else + strip_cr = STRIP_CR_DONT; + } + if (skip_from_) { + if (type == REC_TYPE_NORM) { + start = vstring_str(buf); + if (strncmp(start + strspn(start, ">"), "From ", 5) == 0) + continue; + } + skip_from_ = 0; + } + if (strip_cr == STRIP_CR_DO && type == REC_TYPE_NORM) + if (VSTRING_LEN(buf) > 0 && vstring_end(buf)[-1] == '\r') + vstring_truncate(buf, VSTRING_LEN(buf) - 1); + if (REC_PUT_BUF(dst, type, buf) < 0) + msg_fatal("%s(%d): error writing queue file: %m", saved_sender, uid); + } + + /* + * Append an empty section for information extracted from message + * headers. Header parsing is done by the cleanup service. + */ + rec_fputs(dst, REC_TYPE_XTRA, ""); + + /* + * Identify the end of the queue file. + */ + rec_fputs(dst, REC_TYPE_END, ""); + + /* + * Make sure that the message makes it to the file system. Once we have + * terminated with successful exit status we cannot lose the message due + * to "frivolous reasons". If all goes well, prevent the run-time error + * handler from removing the file. + */ + if (vstream_ferror(VSTREAM_IN)) + msg_fatal("%s(%d): error reading input: %m", saved_sender, uid); + if ((status = mail_stream_finish(handle)) != 0) + msg_fatal("%s(%d): %s", saved_sender, uid, cleanup_strerror(status)); + if (sendmail_path) { + myfree(sendmail_path); + sendmail_path = 0; + } + + /* + * Cleanup. Not really necessary as we're about to exit, but good for + * debugging purposes. + */ + vstring_free(buf); + myfree(saved_sender); +} + +/* show_queue - show queue status */ + +static void show_queue(void) +{ + char buf[VSTREAM_BUFSIZE]; + VSTREAM *showq; + int n; + + /* + * Connect to the show queue service. + */ + if ((showq = mail_connect(MAIL_CLASS_PUBLIC, MAIL_SERVICE_SHOWQ, BLOCKING)) != 0) { + while ((n = vstream_fread(showq, buf, sizeof(buf))) > 0) + if (vstream_fwrite(VSTREAM_OUT, buf, n) != n) + msg_fatal("write error: %m"); + + if (vstream_fflush(VSTREAM_OUT)) + msg_fatal("write error: %m"); + + if (vstream_fclose(showq)) + msg_warn("close: %m"); + } + + /* + * When the mail system is down, the superuser can still access the queue + * directly. Just run the showq program in stand-alone mode. + */ + else if (geteuid() == 0) { + ARGV *argv; + int stat; + + msg_warn("Mail system is down -- accessing queue directly"); + argv = argv_alloc(6); + argv_add(argv, MAIL_SERVICE_SHOWQ, "-c", "-u", "-S", (char *) 0); + for (n = 0; n < msg_verbose; n++) + argv_add(argv, "-v", (char *) 0); + argv_terminate(argv); + stat = mail_run_foreground(var_daemon_dir, argv->argv); + argv_free(argv); + } + + /* + * When the mail system is down, unprivileged users are stuck, because by + * design the mail system contains no set_uid programs. The only way for + * an unprivileged user to cross protection boundaries is to talk to the + * showq daemon. + */ + else { + msg_fatal("Queue report unavailable - mail system is down"); + } +} + +/* flush_queue - force delivery */ + +static void flush_queue(void) +{ + + /* + * Trigger the flush queue service. + */ + if (mail_flush_deferred() < 0) + msg_warn("Cannot flush mail queue - mail system is down"); +} + +/* sendmail_cleanup - callback for the runtime error handler */ + +static void sendmail_cleanup(void) +{ + + /* + * We're possibly running from a signal handler, so we should not be + * doing complicated things such as memory of buffer management, but if + * for some reason we can't cleanup it is even worse to just die quietly. + */ + if (sendmail_path) { + if (remove(sendmail_path)) + msg_warn("sendmail_cleanup: remove %s: %m", sendmail_path); + else if (msg_verbose) + msg_info("remove %s", sendmail_path); + sendmail_path = 0; + } +} + +/* sendmail_sig - catch signal and clean up */ + +static void sendmail_sig(int sig) +{ + sendmail_cleanup(); + exit(sig); +} + +/* main - the main program */ + +int main(int argc, char **argv) +{ + static char *full_name = 0; /* sendmail -F */ + struct stat st; + char *slash; + int extract_recipients = 0; /* sendmail -t, almost */ + char *sender = 0; /* sendmail -f */ + int c; + int fd; + int mode; + ARGV *ext_argv; + int debug_me = 0; + int err; + int n; + + /* + * 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. Set up signal handlers so that we + * can clean up incomplete output. + */ + if ((slash = strrchr(argv[0], '/')) != 0) + argv[0] = slash + 1; + msg_vstream_init(argv[0], VSTREAM_ERR); + msg_syslog_init(mail_task("sendmail"), 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); + + signal(SIGPIPE, SIG_IGN); + + signal(SIGHUP, sendmail_sig); + signal(SIGINT, sendmail_sig); + signal(SIGQUIT, sendmail_sig); + signal(SIGTERM, sendmail_sig); + msg_cleanup(sendmail_cleanup); + + /* + * Optionally start the debugger on ourself. This must be done after + * reading the global configuration file, because that file specifies + * what debugger command to execute. + */ + if (debug_me) + debug_process(); + + /* + * The default mode of operation is determined by the process name. It + * can, however, be changed via command-line options (for example, + * "newaliases -bp" will show the mail queue). + */ + if (strcmp(argv[0], "mailq") == 0) { + mode = SM_MODE_MAILQ; + } else if (strcmp(argv[0], "newaliases") == 0) { + mode = SM_MODE_NEWALIAS; + } else if (strcmp(argv[0], "smtpd") == 0) { + mode = SM_MODE_DAEMON; + } else { + mode = SM_MODE_ENQUEUE; + } + + /* + * Parse JCL. Sendmail has been around for a long time, and has acquired + * a large number of options in the course of time. Some options such as + * -q are not parsable with GETOPT() and get special treatment. + */ +#define OPTIND (optind > 0 ? optind : 1) + + while (argv[OPTIND] != 0) { + if (strcmp(argv[OPTIND], "-q") == 0) { + if (mode == SM_MODE_DAEMON) + msg_warn("ignoring -q option in daemon mode"); + else + mode = SM_MODE_FLUSHQ; + optind++; + continue; + } + if ((c = GETOPT(argc, argv, "B:C:F:IN:R:X:b:ce:f:h:imno:p:q:tvx")) <= 0) + break; + switch (c) { + default: + if (msg_verbose) + msg_info("-%c option ignored", c); + break; + case 'n': + msg_fatal("-%c option not supported", c); + case 'B': /* body type */ + break; + case 'F': /* full name */ + full_name = optarg; + break; + case 'I': /* newaliases */ + mode = SM_MODE_NEWALIAS; + break; + case 'N': /* DSN */ + break; + case 'R': /* DSN */ + break; + case 'b': + switch (*optarg) { + default: + msg_fatal("unsupported: -%c%c", c, *optarg); + case 'd': /* daemon mode */ + if (mode == SM_MODE_FLUSHQ) + msg_warn("ignoring -q option in daemon mode"); + mode = SM_MODE_DAEMON; + break; + case 'i': /* newaliases */ + mode = SM_MODE_NEWALIAS; + break; + case 'm': /* deliver mail */ + mode = SM_MODE_ENQUEUE; + break; + case 'p': /* mailq */ + mode = SM_MODE_MAILQ; + break; + case 's': /* stand-alone mode */ + mode = SM_MODE_USER; + break; + } + break; + case 'f': + sender = optarg; + break; + case 'o': + switch (*optarg) { + default: + if (msg_verbose) + msg_info("-%c%c option ignored", c, *optarg); + break; + case 'A': + if (optarg[1] == 0) + msg_fatal("-oA requires pathname"); + myfree(var_alias_db_map); + var_alias_db_map = mystrdup(optarg + 1); + set_config_str(VAR_ALIAS_DB_MAP, var_alias_db_map); + break; + case '7': + case '8': + case 'm': + break; + } + break; + case 'q': + if (optarg[0] && !ISDIGIT(optarg[0])) + msg_fatal("-q%c is not implemented", optarg[0]); + if (mode == SM_MODE_DAEMON) { + if (msg_verbose) + msg_info("-%c%s option ignored", c, optarg); + } + break; + case 't': + extract_recipients = 1; + break; + case 'v': + msg_verbose++; + break; + case '?': + msg_fatal("usage: %s [options]", argv[0]); + } + } + + /* + * Look for conflicting options and arguments. + */ + if (extract_recipients && mode != SM_MODE_ENQUEUE) + msg_fatal("-t can be used only in delivery mode"); + + if (extract_recipients && argv[optind]) + msg_fatal("cannot delete recipients with -t"); + + /* + * Start processing. Some modes are implemented internally (enqueue + * message), or as network clients (show queue, flush queue); everything + * else is delegated to external commands. + */ + switch (mode) { + default: + msg_panic("unknown operation mode: %d", mode); + /* NOTREACHED */ + case SM_MODE_ENQUEUE: + enqueue(sender, full_name, argv + optind); + exit(0); + break; + case SM_MODE_MAILQ: + show_queue(); + exit(0); + break; + case SM_MODE_FLUSHQ: + flush_queue(); + exit(0); + break; + case SM_MODE_DAEMON: + if (argv[optind]) + msg_fatal("daemon mode requires no recipient"); + ext_argv = argv_alloc(2); + argv_add(ext_argv, "postfix", (char *) 0); + for (n = 0; n < msg_verbose; n++) + argv_add(ext_argv, "-v", (char *) 0); + argv_add(ext_argv, "start", (char *) 0); + argv_terminate(ext_argv); + err = mail_run_background(var_command_dir, ext_argv->argv); + argv_free(ext_argv); + exit(err); + break; + case SM_MODE_NEWALIAS: + if (argv[optind]) + msg_fatal("alias initialization mode requires no recipient"); + ext_argv = argv_alloc(2); + argv_add(ext_argv, "postalias", (char *) 0); + for (n = 0; n < msg_verbose; n++) + argv_add(ext_argv, "-v", (char *) 0); + argv_split_append(ext_argv, var_alias_db_map, ", \t\r\n"); + mail_run_replace(var_command_dir, ext_argv->argv); + /* NOTREACHED */ + case SM_MODE_USER: + if (argv[optind]) + msg_fatal("stand-alone mode requires no recipient"); + ext_argv = argv_alloc(2); + argv_add(ext_argv, "smtpd", "-S", (char *) 0); + for (n = 0; n < msg_verbose; n++) + argv_add(ext_argv, "-v", (char *) 0); + argv_terminate(ext_argv); + mail_run_replace(var_daemon_dir, ext_argv->argv); + /* NOTREACHED */ + } +} diff --git a/postfix/showq/.indent.pro b/postfix/showq/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/showq/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/showq/.printfck b/postfix/showq/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/showq/.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/showq/Makefile.in b/postfix/showq/Makefile.in new file mode 100644 index 000000000..efe8d71bd --- /dev/null +++ b/postfix/showq/Makefile.in @@ -0,0 +1,75 @@ +SHELL = /bin/sh +SRCS = showq.c +OBJS = showq.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 = showq +INC_DIR = ../include +LIBS = ../lib/libmaster.a ../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' +showq.o: showq.c +showq.o: ../include/sys_defs.h +showq.o: ../include/msg.h +showq.o: ../include/scan_dir.h +showq.o: ../include/vstring.h +showq.o: ../include/vbuf.h +showq.o: ../include/vstream.h +showq.o: ../include/vstring_vstream.h +showq.o: ../include/stringops.h +showq.o: ../include/mymalloc.h +showq.o: ../include/mail_queue.h +showq.o: ../include/mail_open_ok.h +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/config.h +showq.o: ../include/record.h +showq.o: ../include/rec_type.h +showq.o: ../include/mail_server.h diff --git a/postfix/showq/showq.c b/postfix/showq/showq.c new file mode 100644 index 000000000..f6443c226 --- /dev/null +++ b/postfix/showq/showq.c @@ -0,0 +1,320 @@ +/*++ +/* NAME +/* showq 8 +/* SUMMARY +/* list the Postfix mail queue +/* SYNOPSIS +/* \fBshowq\fR [generic Postfix daemon options] +/* DESCRIPTION +/* The \fBshowq\fR daemon reports the Postfix mail queue status. +/* It is the program that emulates the sendmail `mailq' command. +/* +/* The \fBshowq\fR daemon can also be run in stand-alone mode +/* by the super-user. This mode of operation is used to emulate +/* the `mailq' command while the Postfix mail system is down. +/* SECURITY +/* .ad +/* .fi +/* The \fBshowq\fR daemon can run in a chroot jail at fixed low +/* privilege, and takes no input from the client. Its service port +/* is accessible to local untrusted users, so the service can be +/* susceptible to denial of service attacks. +/* STANDARDS +/* .ad +/* .fi +/* None. The showq daemon does not interact with the outside world. +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8). +/* BUGS +/* The \fBshowq\fR daemon runs at a fixed low privilege; consequently, +/* it cannot extract information from queue files in the +/* \fBmaildrop\fR directory. +/* SEE ALSO +/* cleanup(8) canonicalize and enqueue mail +/* pickup(8) local mail pickup service +/* qmgr(8) mail being delivered, delayed mail +/* syslogd(8) system logging +/* 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 +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Single-threaded server skeleton. */ + +#include + +/* Application-specific. */ + +#define STRING_FORMAT "%-10s %8s %-20s %s\n" +#define DATA_FORMAT "%-10s %8ld %20.20s %s\n" + +static void showq_report(VSTREAM *client, char *id, VSTREAM *qfile, long size, int stop) +{ + VSTRING *buf = vstring_alloc(100); + int rec_type; + time_t arrival_time = 0; + char *start; + long msg_size = 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); + break; + case REC_TYPE_SIZE: + msg_size = atol(start); + break; + 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 ? + asctime(localtime(&arrival_time)) : "??", + printable(start, '?')); + break; + case REC_TYPE_RCPT: + if (*start == 0) + start = "(MAILER-DAEMON)"; + vstream_fprintf(client, STRING_FORMAT, + "", "", "", printable(start, '?')); + break; + case REC_TYPE_MESG: + if (vstream_fseek(qfile, atol(start), SEEK_SET) < 0) + msg_fatal("seek file %s: %m", VSTREAM_PATH(qfile)); + break; + case REC_TYPE_END: + break; + } + } + vstring_free(buf); +} + +/* showq_reasons - show deferral reasons */ + +static void showq_reasons(VSTREAM *client, VSTREAM *logfile) +{ + VSTRING *buf = vstring_alloc(100); + char *recipient; + char *reason; + char *saved_reason = 0; + char *cp; + + /* + * XXX Kluge alert. The defer log is an unstructured file. This has the + * advantage that information is directly suitable for human consumption, + * and that a process may crash while updating the file - the result will + * still be usable. The downside of using an unstructured file is that it + * is hard to process such information mechanically, like we do here. In + * the end this will have to be a structured file anyway so we can do + * DSN. + */ +#define STR vstring_str + + while (vstring_get_nonl(buf, logfile) != VSTREAM_EOF) { + + /* + * Do this now so the string won't be reallocated. + */ + VSTRING_ADDCH(buf, ')'); + VSTRING_TERMINATE(buf); + + cp = printable(STR(buf), '?'); + if (cp[1] == 0) + continue; + + /* + * Find the recipient address. + */ + if (*cp != '<') { + msg_warn("%s: bad defer record: %.30s...", + VSTREAM_PATH(logfile), cp); + continue; + } + recipient = cp + 1; + if ((cp = strstr(recipient, ">:")) == 0) { + msg_warn("%s: bad defer record: %.30s...", + VSTREAM_PATH(logfile), cp); + continue; + } + *cp = 0; + + /* + * Find the reason for deferral. Put parentheses around it. + */ + reason = cp + 2; + while (*reason && ISSPACE(*reason)) + reason++; + reason -= 1; + *reason = '('; + + /* + * Don't print the reason when the previous recipient had the same + * problem. + */ + if (saved_reason == 0 || strcmp(saved_reason, reason) != 0) { + if (saved_reason) + myfree(saved_reason); + saved_reason = mystrdup(reason); + vstream_fprintf(client, "%78s\n", reason); + } + vstream_fprintf(client, STRING_FORMAT, "", "", "", recipient); + } + if (saved_reason) + myfree(saved_reason); + vstring_free(buf); +} + + +/* showq_service - service client */ + +static void showq_service(VSTREAM *client, char *unused_service, char **argv) +{ + char **queue; + VSTREAM *qfile; + VSTREAM *logfile; + const char *path; + int status; + char *id; + int file_count; + unsigned long queue_size = 0; + struct stat st; + char *queue_names[] = { /* XXX configurable */ + MAIL_QUEUE_INCOMING, + MAIL_QUEUE_ACTIVE, + MAIL_QUEUE_DEFERRED, + 0, + }; + + /* + * Sanity check. This service takes no command-line arguments. + */ + if (argv[0]) + msg_fatal("unexpected command-line argument: %s", argv[0]); + + /* + * Skip any files that have the wrong permissions. If we can't open an + * existing file, assume the system is out of resources or that it is + * mis-configured, and force backoff by raising a fatal error. + */ + file_count = 0; + for (queue = queue_names; *queue != 0; queue++) { + SCAN_DIR *scan = scan_dir_open(*queue); + char *saved_id = 0; + + while ((id = scan_dir_next(scan)) != 0) { + + /* + * XXX I have seen showq loop on the same queue id. That would be + * an operating system bug, but who cares whose fault it is. Make + * sure this will never happen again. + */ + if (saved_id) { + if (strcmp(saved_id, id) == 0) { + msg_warn("readdir loop on queue %s id %s", *queue, id); + break; + } + myfree(saved_id); + } + saved_id = mystrdup(id); + status = mail_open_ok(*queue, id, &st, &path); + if (status == MAIL_OPEN_YES) { + if (file_count == 0) + vstream_fprintf(client, STRING_FORMAT, + "-Queue ID-", "--Size--", + "----Arrival Time----", + "-Sender/Recipient-------"); + else + 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); + } + if (vstream_fclose(qfile)) + msg_warn("close file %s %s: %m", *queue, id); + } else if (strcmp(*queue, MAIL_QUEUE_MAILDROP) == 0) { + queue_size += st.st_size; + vstream_fprintf(client, DATA_FORMAT, id, (long) st.st_size, + asctime(localtime(&st.st_mtime)), + "(to be determined)"); + } else if (errno != ENOENT) + msg_fatal("open %s %s: %m", *queue, id); + file_count++; + } + vstream_fflush(client); + } + if (saved_id) + myfree(saved_id); + scan_dir_close(scan); + } + if (file_count == 0) + vstream_fprintf(client, "Mail queue is empty\n"); + else { + vstream_fprintf(client, "\n-- %lu Kbytes in %d Request%s.\n", + queue_size / 1024, file_count, + file_count == 1 ? "" : "s"); + } +} + +/* main - pass control to the single-threaded server skeleton */ + +int main(int argc, char **argv) +{ + single_server_main(argc, argv, showq_service, 0); +} diff --git a/postfix/smtp/.indent.pro b/postfix/smtp/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/smtp/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/smtp/.printfck b/postfix/smtp/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/smtp/.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/smtp/Makefile.in b/postfix/smtp/Makefile.in new file mode 100644 index 000000000..1b63ff9ad --- /dev/null +++ b/postfix/smtp/Makefile.in @@ -0,0 +1,216 @@ +SHELL = /bin/sh +SRCS = smtp.c quote_821_local.c smtp_connect.c smtp_proto.c smtp_chat.c \ + smtp_session.c smtp_addr.c smtp_trouble.c smtp_unalias.c smtp_state.c +OBJS = smtp.o quote_821_local.o smtp_connect.o smtp_proto.o smtp_chat.o \ + smtp_session.o smtp_addr.o smtp_trouble.o smtp_unalias.o smtp_state.o +HDRS = smtp.h +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= quote_821_local smtp_unalias +PROG = smtp +INC_DIR = ../include +LIBS = ../lib/libmaster.a ../lib/libglobal.a ../lib/libdns.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 + cp *.h 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 + +quote_821_local: quote_821_local.c $(LIBS) + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIBS) $(SYSLIBS) + +smtp_unalias: smtp_unalias.c $(LIBS) + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIBS) $(SYSLIBS) + +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' +quote_821_local.o: quote_821_local.c +quote_821_local.o: ../include/sys_defs.h +quote_821_local.o: ../include/vstring.h +quote_821_local.o: ../include/vbuf.h +quote_821_local.o: quote_821_local.h +smtp.o: smtp.c +smtp.o: ../include/sys_defs.h +smtp.o: ../include/msg.h +smtp.o: ../include/mymalloc.h +smtp.o: ../include/name_mask.h +smtp.o: ../include/deliver_request.h +smtp.o: ../include/vstring.h +smtp.o: ../include/vbuf.h +smtp.o: ../include/vstream.h +smtp.o: ../include/recipient_list.h +smtp.o: ../include/mail_queue.h +smtp.o: ../include/mail_params.h +smtp.o: ../include/config.h +smtp.o: ../include/debug_peer.h +smtp.o: ../include/mail_error.h +smtp.o: ../include/mail_server.h +smtp.o: smtp.h +smtp.o: ../include/argv.h +smtp_addr.o: smtp_addr.c +smtp_addr.o: ../include/sys_defs.h +smtp_addr.o: ../include/msg.h +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/mail_params.h +smtp_addr.o: ../include/own_inet_addr.h +smtp_addr.o: ../include/dns.h +smtp_addr.o: smtp.h +smtp_addr.o: ../include/vstream.h +smtp_addr.o: ../include/argv.h +smtp_addr.o: ../include/deliver_request.h +smtp_addr.o: ../include/recipient_list.h +smtp_addr.o: smtp_addr.h +smtp_chat.o: smtp_chat.c +smtp_chat.o: ../include/sys_defs.h +smtp_chat.o: ../include/msg.h +smtp_chat.o: ../include/vstring.h +smtp_chat.o: ../include/vbuf.h +smtp_chat.o: ../include/vstream.h +smtp_chat.o: ../include/argv.h +smtp_chat.o: ../include/stringops.h +smtp_chat.o: ../include/line_wrap.h +smtp_chat.o: ../include/mymalloc.h +smtp_chat.o: ../include/recipient_list.h +smtp_chat.o: ../include/deliver_request.h +smtp_chat.o: ../include/smtp_stream.h +smtp_chat.o: ../include/mail_params.h +smtp_chat.o: ../include/mail_addr.h +smtp_chat.o: ../include/post_mail.h +smtp_chat.o: ../include/cleanup_user.h +smtp_chat.o: smtp.h +smtp_connect.o: smtp_connect.c +smtp_connect.o: ../include/sys_defs.h +smtp_connect.o: ../include/msg.h +smtp_connect.o: ../include/vstream.h +smtp_connect.o: ../include/vbuf.h +smtp_connect.o: ../include/vstring.h +smtp_connect.o: ../include/split_at.h +smtp_connect.o: ../include/mymalloc.h +smtp_connect.o: ../include/inet_addr_list.h +smtp_connect.o: ../include/iostuff.h +smtp_connect.o: ../include/timed_connect.h +smtp_connect.o: ../include/mail_params.h +smtp_connect.o: ../include/own_inet_addr.h +smtp_connect.o: ../include/dns.h +smtp_connect.o: smtp.h +smtp_connect.o: ../include/argv.h +smtp_connect.o: ../include/deliver_request.h +smtp_connect.o: ../include/recipient_list.h +smtp_connect.o: smtp_addr.h +smtp_proto.o: smtp_proto.c +smtp_proto.o: ../include/sys_defs.h +smtp_proto.o: ../include/msg.h +smtp_proto.o: ../include/vstring.h +smtp_proto.o: ../include/vbuf.h +smtp_proto.o: ../include/vstream.h +smtp_proto.o: ../include/vstring_vstream.h +smtp_proto.o: ../include/stringops.h +smtp_proto.o: ../include/mymalloc.h +smtp_proto.o: ../include/mail_params.h +smtp_proto.o: ../include/smtp_stream.h +smtp_proto.o: ../include/mail_queue.h +smtp_proto.o: ../include/recipient_list.h +smtp_proto.o: ../include/deliver_request.h +smtp_proto.o: ../include/deliver_completed.h +smtp_proto.o: ../include/defer.h +smtp_proto.o: ../include/bounce.h +smtp_proto.o: ../include/sent.h +smtp_proto.o: ../include/record.h +smtp_proto.o: ../include/rec_type.h +smtp_proto.o: ../include/off_cvt.h +smtp_proto.o: ../include/mark_corrupt.h +smtp_proto.o: smtp.h +smtp_proto.o: ../include/argv.h +smtp_proto.o: quote_821_local.h +smtp_session.o: smtp_session.c +smtp_session.o: ../include/sys_defs.h +smtp_session.o: ../include/mymalloc.h +smtp_session.o: ../include/vstream.h +smtp_session.o: ../include/vbuf.h +smtp_session.o: smtp.h +smtp_session.o: ../include/vstring.h +smtp_session.o: ../include/argv.h +smtp_session.o: ../include/deliver_request.h +smtp_session.o: ../include/recipient_list.h +smtp_state.o: smtp_state.c +smtp_state.o: ../include/sys_defs.h +smtp_state.o: ../include/mymalloc.h +smtp_state.o: ../include/vstring.h +smtp_state.o: ../include/vbuf.h +smtp_state.o: ../include/vstream.h +smtp_state.o: ../include/config.h +smtp_state.o: smtp.h +smtp_state.o: ../include/argv.h +smtp_state.o: ../include/deliver_request.h +smtp_state.o: ../include/recipient_list.h +smtp_trouble.o: smtp_trouble.c +smtp_trouble.o: ../include/sys_defs.h +smtp_trouble.o: ../include/msg.h +smtp_trouble.o: ../include/vstring.h +smtp_trouble.o: ../include/vbuf.h +smtp_trouble.o: ../include/stringops.h +smtp_trouble.o: ../include/mymalloc.h +smtp_trouble.o: ../include/smtp_stream.h +smtp_trouble.o: ../include/vstream.h +smtp_trouble.o: ../include/deliver_request.h +smtp_trouble.o: ../include/recipient_list.h +smtp_trouble.o: ../include/deliver_completed.h +smtp_trouble.o: ../include/bounce.h +smtp_trouble.o: ../include/defer.h +smtp_trouble.o: ../include/mail_error.h +smtp_trouble.o: ../include/name_mask.h +smtp_trouble.o: smtp.h +smtp_trouble.o: ../include/argv.h +smtp_unalias.o: smtp_unalias.c +smtp_unalias.o: ../include/sys_defs.h +smtp_unalias.o: ../include/htable.h +smtp_unalias.o: ../include/vstring.h +smtp_unalias.o: ../include/vbuf.h +smtp_unalias.o: ../include/msg.h +smtp_unalias.o: ../include/dns.h +smtp_unalias.o: smtp.h +smtp_unalias.o: ../include/vstream.h +smtp_unalias.o: ../include/argv.h +smtp_unalias.o: ../include/deliver_request.h +smtp_unalias.o: ../include/recipient_list.h diff --git a/postfix/smtp/quote_821_local.c b/postfix/smtp/quote_821_local.c new file mode 100644 index 000000000..0ae67e469 --- /dev/null +++ b/postfix/smtp/quote_821_local.c @@ -0,0 +1,159 @@ +/*++ +/* NAME +/* quote_821_local 3 +/* SUMMARY +/* quote local part of address +/* SYNOPSIS +/* #include "quote_821_local.h" +/* +/* VSTRING *quote_821_local(dst, src) +/* VSTRING *dst; +/* char *src; +/* DESCRIPTION +/* quote_821_local() quotes the local part of a mailbox address and +/* returns a result that can be used in SMTP commands as specified +/* by RFC 821. +/* +/* Arguments: +/* .IP dst +/* The result. +/* .IP src +/* The input address. +/* STANDARDS +/* RFC 821 (SMTP protocol) +/* BUGS +/* The code assumes that the domain is RFC 821 clean. +/* 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 + +/* Utility library. */ + +#include + +/* Global library. */ + +#include "quote_821_local.h" + +/* Application-specific. */ + +#define YES 1 +#define NO 0 + +/* is_821_dot_string - is this local-part an rfc 821 dot-string? */ + +static int is_821_dot_string(char *local_part, char *end) +{ + char *cp; + int ch; + + /* + * Detect any deviations from the definition of dot-string. We could use + * lookup tables to speed up some of the work, but hey, how large can a + * local-part be anyway? + */ + if (local_part[0] == 0 || local_part[0] == '.') + return (NO); + for (cp = local_part; cp < end && (ch = *cp) != 0; cp++) { + if (ch == '.' && cp[1] == '.') + return (NO); + if (ch > 127) + return (NO); + if (ch == ' ') + return (NO); + if (ISCNTRL(ch)) + return (NO); + if (ch == '<' || ch == '>' + || ch == '(' || ch == ')' + || ch == '[' || ch == ']' + || ch == '\\' || ch == ',' + || ch == ';' || ch == ':' + || ch == '@' || ch == '"') + return (NO); + } + if (cp[-1] == '.') + return (NO); + return (YES); +} + +/* make_821_quoted_string - make quoted-string from local-part */ + +static VSTRING *make_821_quoted_string(VSTRING *dst, char *local_part, char *end) +{ + char *cp; + int ch; + + /* + * Put quotes around the result, and prepend a backslash to characters + * that need quoting when they occur in a quoted-string. + */ + VSTRING_RESET(dst); + VSTRING_ADDCH(dst, '"'); + for (cp = local_part; cp < end && (ch = *cp) != 0; cp++) { + if (ch > 127 || ch == '\r' || ch == '\n' || ch == '"' || ch == '\\') + VSTRING_ADDCH(dst, '\\'); + VSTRING_ADDCH(dst, ch); + } + VSTRING_ADDCH(dst, '"'); + VSTRING_TERMINATE(dst); + return (dst); +} + +/* quote_821_local - quote local part of address according to rfc 821 */ + +VSTRING *quote_821_local(VSTRING *dst, char *addr) +{ + char *at; + + /* + * According to RFC 821, a local-part is a dot-string or a quoted-string. + * We first see if the local-part is a dot-string. If it is not, we turn + * it into a quoted-string. Anything else would be too painful. + */ + if ((at = strrchr(addr, '@')) == 0) /* just in case */ + at = addr + strlen(addr); /* should not happen */ + if (is_821_dot_string(addr, at)) { + return (vstring_strcpy(dst, addr)); + } else { + make_821_quoted_string(dst, addr, at); + return (vstring_strcat(dst, at)); + } +} + +#ifdef TEST + + /* + * Test program for local-part quoting as per rfc 821 + */ +#include +#include +#include +#include "quote_821_local.h" + +main(void) +{ + VSTRING *src = vstring_alloc(100); + VSTRING *dst = vstring_alloc(100); + + while (vstring_fgets_nonl(src, VSTREAM_IN)) { + vstream_fprintf(VSTREAM_OUT, "%s\n", + vstring_str(quote_821_local(dst, vstring_str(src)))); + vstream_fflush(VSTREAM_OUT); + } + exit(0); +} + +#endif diff --git a/postfix/smtp/quote_821_local.h b/postfix/smtp/quote_821_local.h new file mode 100644 index 000000000..09aca76a0 --- /dev/null +++ b/postfix/smtp/quote_821_local.h @@ -0,0 +1,30 @@ +/*++ +/* NAME +/* quote_821_local 3h +/* SUMMARY +/* quote rfc 821 local part +/* SYNOPSIS +/* #include "quote_821_local.h" +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern VSTRING *quote_821_local(VSTRING *, char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ diff --git a/postfix/smtp/smtp.c b/postfix/smtp/smtp.c new file mode 100644 index 000000000..bededd5f5 --- /dev/null +++ b/postfix/smtp/smtp.c @@ -0,0 +1,322 @@ +/*++ +/* NAME +/* smtp 8 +/* SUMMARY +/* Postfix remote delivery via SMTP +/* SYNOPSIS +/* \fBsmtp\fR [generic Postfix daemon options] +/* DESCRIPTION +/* The SMTP client processes message delivery requests from +/* the queue manager. Each request specifies a queue file, a sender +/* address, a domain or host to deliver to, and recipient information. +/* This program expects to be run from the \fBmaster\fR(8) process +/* manager. +/* +/* The SMTP client updates the queue file and marks recipients +/* as finished, or it informs the queue manager that delivery should +/* be tried again at a later time. Delivery problem reports are sent +/* to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate. +/* +/* The SMTP client looks up a list of mail exchanger addresses for +/* the destination host, sorts the list by preference, and connects +/* to each listed address until it finds a server that responds. +/* +/* Once the SMTP client has received the server greeting banner, no +/* error will cause it to proceed to the next address on the mail +/* exchanger list. Instead, the message is either bounced, or its +/* delivery is deferred until later. +/* SECURITY +/* .ad +/* .fi +/* The SMTP client is moderately security-sensitive. It talks to SMTP +/* servers and to DNS servers on the network. The SMTP client can be +/* run chrooted at fixed low privilege. +/* STANDARDS +/* RFC 821 (SMTP protocol) +/* RFC 1651 (SMTP service extensions) +/* RFC 1870 (Message Size Declaration) +/* RFC 2197 (Pipelining) +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8). +/* Corrupted message files are marked so that the queue manager can +/* move them to the \fBcorrupt\fR queue for further inspection. +/* +/* Depending on the setting of the \fBnotify_classes\fR parameter, +/* the postmaster is notified of bounces, protocol problems, and of +/* other trouble. +/* BUGS +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. Use the \fBpostfix reload\fR command after +/* a configuration change. +/* .SH Miscellaneous +/* .ad +/* .fi +/* .IP \fBdebug_peer_level\fR +/* Verbose logging level increment for hosts that match a +/* pattern in the \fBdebug_peer_list\fR parameter. +/* .IP \fBdebug_peer_list\fR +/* 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 \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 +/* exchangers for a remote destination, the list is truncated to +/* 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. +/* .SH "Resource controls" +/* .ad +/* .fi +/* .IP \fBsmtp_destination_concurrency_limit\fR +/* Limit the number of parallel deliveries to the same destination. +/* The default limit is taken from the +/* \fBdefault_destination_concurrency_limit\fR parameter. +/* .IP \fBsmtp_destination_recipient_limit\fR +/* Limit the number of recipients per message delivery. +/* The default limit is taken from the +/* \fBdefault_destination_recipient_limit\fR parameter. +/* .SH "Timeout controls" +/* .ad +/* .fi +/* .IP \fBsmtp_connect_timeout\fR +/* 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 exchanger list. +/* .IP \fBsmtp_helo_timeout\fR +/* Timeout in seconds for receiving the SMTP greeting banner. +/* When the server drops the connection without 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. +/* .IP \fBsmtp_helo_timeout\fR +/* Timeout in seconds for sending the \fBHELO\fR command, and for +/* receiving the server response. +/* .IP \fBsmtp_mail_timeout\fR +/* Timeout in seconds for sending the \fBMAIL FROM\fR command, and for +/* receiving the server response. +/* .IP \fBsmtp_rcpt_timeout\fR +/* Timeout in seconds for sending the \fBRCPT TO\fR command, and for +/* receiving the server response. +/* .IP \fBsmtp_data_init_timeout\fR +/* Timeout in seconds for sending the \fBDATA\fR command, and for +/* receiving the server response. +/* .IP \fBsmtp_data_xfer_timeout\fR +/* Timeout in seconds for sending the message content. +/* .IP \fBsmtp_data_done_timeout\fR +/* Timeout in seconds for sending the "\fB.\fR" command, and for +/* receiving the server response. When no response is received, a +/* warning is logged that the mail may be delivered multiple times. +/* .IP \fBsmtp_quit_timeout\fR +/* Timeout in seconds for sending the \fBQUIT\fR command, and for +/* receiving the server response. +/* SEE ALSO +/* bounce(8) non-delivery status reports +/* master(8) process manager +/* qmgr(8) queue manager +/* syslogd(8) system logging +/* 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 + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include + +/* Single server skeleton. */ + +#include + +/* Application-specific. */ + +#include "smtp.h" + + /* + * Tunable parameters. These have compiled-in defaults that can be overruled + * by settings in the global Postfix configuration file. + */ +int var_smtp_conn_tmout; +int var_smtp_helo_tmout; +int var_smtp_mail_tmout; +int var_smtp_rcpt_tmout; +int var_smtp_data0_tmout; +int var_smtp_data1_tmout; +int var_smtp_data2_tmout; +int var_smtp_quit_tmout; +char *var_inet_interfaces; +char *var_debug_peer_list; +int var_debug_peer_level; +char *var_notify_classes; + + /* + * Global variables. smtp_errno is set by the address lookup routines and by + * the connection management routines. + */ +int smtp_errno; + +/* deliver_message - deliver message with extreme prejudice */ + +static int deliver_message(DELIVER_REQUEST *request) +{ + char *myname = "deliver_message"; + VSTRING *why; + SMTP_STATE *state; + int result; + + if (msg_verbose) + msg_info("deliver_message: from %s", request->sender); + + /* + * Sanity checks. The smtp server is unprivileged and chrooted, so we can + * afford to distribute the data censoring code, instead of having it all + * in one place. + */ + if (request->nexthop[0] == 0) + msg_fatal("empty nexthop hostname"); + if (request->rcpt_list.len <= 0) + msg_fatal("recipient count: %d", request->rcpt_list.len); + + /* + * Initialize. Bundle all information about the delivery request, so that + * we can produce understandable diagnostics when something goes wrong + * many levels below. The alternative would be to make everything global. + */ + why = vstring_alloc(100); + state = smtp_state_alloc(); + state->request = request; + + /* + * Open the queue file. Opening the file can fail for a variety of + * reasons, such as the system running out of resources. Instead of + * throwing away mail, we're raising a fatal error which forces the mail + * system to back off, and retry later. + */ + state->src = mail_queue_open(request->queue_name, request->queue_id, + O_RDWR, 0); + if (state->src == 0) + msg_fatal("%s: open %s %s: %m", myname, + request->queue_name, request->queue_id); + if (msg_verbose) + msg_info("%s: file %s", myname, VSTREAM_PATH(state->src)); + + /* + * Establish an SMTP session and deliver this message to (limited batches + * of) recipients. XXX By doing the recipient batching in the SMTP agent + * instead of in the queue manager, we're stuck with one connection per + * message per domain. But, the queue manager should not have hard-wired + * logic that is specific to SMTP processing. At the end, notify the + * postmaster of any protocol errors. + */ + if ((state->session = smtp_connect(request->nexthop, why)) == 0) { + smtp_site_fail(state, smtp_errno == SMTP_RETRY ? 450 : 550, + "%s", vstring_str(why)); + } else { + debug_peer_check(state->session->host, state->session->addr); + if (smtp_helo(state) == 0) + smtp_xfer(state); + if (state->history != 0 + && (state->error_mask & name_mask(mail_error_masks, var_notify_classes))) + smtp_chat_notify(state); + smtp_session_free(state->session); + debug_peer_restore(); + } + + /* + * Clean up. + */ + if (vstream_fclose(state->src)) + msg_warn("close %s %s: %m", request->queue_name, request->queue_id); + vstring_free(why); + smtp_chat_reset(state); + result = state->status; + smtp_state_free(state); + + return (result); +} + +/* smtp_service - perform service for client */ + +static void smtp_service(VSTREAM *client_stream, char *unused_service, char **argv) +{ + DELIVER_REQUEST *request; + int status; + + /* + * Sanity check. This service takes no command-line arguments. + */ + if (argv[0]) + msg_fatal("unexpected command-line argument: %s", argv[0]); + + /* + * This routine runs whenever a client connects to the UNIX-domain socket + * dedicated to remote SMTP delivery service. What we see below is a + * little protocol to (1) tell the queue manager that we are ready, (2) + * read a request from the queue manager, and (3) report the completion + * status of that request. All connection-management stuff is handled by + * the common code in single_server.c. + */ + if ((request = deliver_request_read(client_stream)) != 0) { + status = deliver_message(request); + deliver_request_done(client_stream, request, status); + } +} + +/* main - pass control to the single-threaded skeleton */ + +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, + 0, + }; + static CONFIG_INT_TABLE int_table[] = { + VAR_SMTP_CONN_TMOUT, DEF_SMTP_CONN_TMOUT, &var_smtp_conn_tmout, 0, 0, + VAR_SMTP_HELO_TMOUT, DEF_SMTP_HELO_TMOUT, &var_smtp_helo_tmout, 1, 0, + VAR_SMTP_MAIL_TMOUT, DEF_SMTP_MAIL_TMOUT, &var_smtp_mail_tmout, 1, 0, + VAR_SMTP_RCPT_TMOUT, DEF_SMTP_RCPT_TMOUT, &var_smtp_rcpt_tmout, 1, 0, + VAR_SMTP_DATA0_TMOUT, DEF_SMTP_DATA0_TMOUT, &var_smtp_data0_tmout, 1, 0, + VAR_SMTP_DATA1_TMOUT, DEF_SMTP_DATA1_TMOUT, &var_smtp_data1_tmout, 1, 0, + VAR_SMTP_DATA2_TMOUT, DEF_SMTP_DATA2_TMOUT, &var_smtp_data2_tmout, 1, 0, + VAR_SMTP_QUIT_TMOUT, DEF_SMTP_QUIT_TMOUT, &var_smtp_quit_tmout, 1, 0, + VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0, + 0, + }; + + single_server_main(argc, argv, smtp_service, + MAIL_SERVER_INT_TABLE, int_table, + MAIL_SERVER_STR_TABLE, str_table, + MAIL_SERVER_PRE_INIT, debug_peer_init, + 0); +} diff --git a/postfix/smtp/smtp.h b/postfix/smtp/smtp.h new file mode 100644 index 000000000..0d34d8372 --- /dev/null +++ b/postfix/smtp/smtp.h @@ -0,0 +1,129 @@ +/*++ +/* NAME +/* smtp 3h +/* SUMMARY +/* smtp client program +/* SYNOPSIS +/* #include "smtp.h" +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include +#include + + /* + * Global library. + */ +#include + + /* + * State information associated with each SMTP delivery. We're bundling the + * state so that we can give meaningful diagnostics in case of problems. + */ +typedef struct SMTP_STATE { + VSTREAM *src; /* queue file stream */ + DELIVER_REQUEST *request; /* envelope info, offsets */ + struct SMTP_SESSION *session; /* network connection */ + VSTRING *buffer; /* I/O buffer */ + VSTRING *scratch; /* scratch buffer */ + VSTRING *scratch2; /* scratch buffer */ + int status; /* delivery status */ + int features; /* server features */ + ARGV *history; /* transaction log */ + int error_mask; /* error classes */ +} SMTP_STATE; + +#define SMTP_FEATURE_ESMTP (1<<0) +#define SMTP_FEATURE_8BITMIME (1<<1) +#define SMTP_FEATURE_PIPELINING (1<<2) +#define SMTP_FEATURE_SIZE (1<<3) + + /* + * smtp.c + */ +extern int smtp_errno; /* XXX can we get rid of this? */ + + /* + * smtp_session.c + */ +typedef struct SMTP_SESSION { + VSTREAM *stream; /* network connection */ + char *host; /* mail exchanger */ + char *addr; /* mail exchanger */ + int best; /* most preferred host */ +} SMTP_SESSION; + +extern SMTP_SESSION *smtp_session_alloc(VSTREAM *, char *, char *); +extern void smtp_session_free(SMTP_SESSION *); + + /* + * smtp_connect.c + */ +extern SMTP_SESSION *smtp_connect(char *, VSTRING *); +extern SMTP_SESSION *smtp_connect_host(char *, unsigned, VSTRING *); +extern SMTP_SESSION *smtp_connect_domain(char *, unsigned, VSTRING *); + + /* + * smtp_proto.c + */ +extern int smtp_helo(SMTP_STATE *); +extern int smtp_xfer(SMTP_STATE *); +extern void smtp_quit(SMTP_STATE *); + + /* + * smtp_chat.c + */ +typedef struct SMTP_RESP { /* server response */ + int code; /* status */ + char *str; /* text */ + VSTRING *buf; /* origin of text */ +} SMTP_RESP; + +extern void smtp_chat_cmd(SMTP_STATE *, char *,...); +extern SMTP_RESP *smtp_chat_resp(SMTP_STATE *); +extern void smtp_chat_reset(SMTP_STATE *); +extern void smtp_chat_notify(SMTP_STATE *); + + /* + * smtp_trouble.c + */ +extern int smtp_conn_fail(SMTP_STATE *, int, char *,...); +extern int smtp_site_fail(SMTP_STATE *, int, char *,...); +extern int smtp_mesg_fail(SMTP_STATE *, int, char *,...); +extern void smtp_rcpt_fail(SMTP_STATE *, int, RECIPIENT *, char *,...); +extern int smtp_stream_except(SMTP_STATE *, int, char *); + + /* + * smtp_unalias.c + */ +extern const char *smtp_unalias_name(const char *); +extern VSTRING *smtp_unalias_addr(VSTRING *, const char *); + + /* + * smtp_state.c + */ +extern SMTP_STATE *smtp_state_alloc(void); +extern void smtp_state_free(SMTP_STATE *); + + /* + * Status codes. Errors must have negative codes so that they do not + * interfere with useful counts of work done. + */ +#define SMTP_OK 0 /* so far, so good */ +#define SMTP_RETRY (-1) /* transient error */ +#define SMTP_FAIL (-2) /* hard error */ + +/* 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 +/*--*/ diff --git a/postfix/smtp/smtp_addr.c b/postfix/smtp/smtp_addr.c new file mode 100644 index 000000000..73ad0972e --- /dev/null +++ b/postfix/smtp/smtp_addr.c @@ -0,0 +1,295 @@ +/*++ +/* NAME +/* smtp_addr 3 +/* SUMMARY +/* SMTP server address lookup +/* SYNOPSIS +/* #include "smtp_addr.h" +/* +/* DNS_RR *smtp_domain_addr(name, why) +/* char *name; +/* VSTRING *why; +/* +/* DNS_RR *smtp_host_addr(name, why) +/* char *name; +/* VSTRING *why; +/* DESCRIPTION +/* This module implements Internet address lookups. By default, +/* 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 +/* returned in most-preferred first order. The result is truncated +/* 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 +/* request is passed to smtp_host_addr(). +/* +/* smtp_host_addr() looks up all addresses listed for the named +/* host. The host can be specified as a numerical Internet network +/* address, or as a symbolic host name. +/* +/* Results from smtp_domain_addr() or smtp_host_addr() are +/* destroyed by dns_rr_free(), including null lists. +/* DIAGNOSTICS +/* All routines either return a DNS_RR pointer, or return a null +/* pointer and set the \fIsmtp_errno\fR global variable accordingly: +/* .IP SMTP_RETRY +/* The request failed due to a soft error, and should be retried later. +/* .IP SMTP_FAIL +/* The request attempt failed due to a hard error. +/* .PP +/* In addition, a textual description of the problem is made available +/* via the \fIwhy\fR argument. +/* 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 +#include + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* DNS library. */ + +#include + +/* Application-specific. */ + +#include "smtp.h" +#include "smtp_addr.h" + +/* smtp_print_addr - print address list */ + +static void smtp_print_addr(char *what, DNS_RR *addr_list) +{ + DNS_RR *addr; + struct in_addr in_addr; + + msg_info("begin %s address list", what); + for (addr = addr_list; addr; addr = addr->next) { + if (addr->data_len > sizeof(addr)) { + msg_warn("skipping address length %d", addr->data_len); + } else { + memcpy((char *) &in_addr, addr->data, sizeof(in_addr)); + msg_info("pref %4d host %s/%s", + addr->pref, addr->name, + inet_ntoa(in_addr)); + } + } + msg_info("end %s address list", what); +} + +/* smtp_addr_one - address lookup for one host name */ + +static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref, VSTRING *why) +{ + char *myname = "smtp_addr_one"; + DNS_RR *addr = 0; + DNS_RR *rr; + + if (msg_verbose) + msg_info("%s: host %s", myname, host); + + /* + * Append the addresses for this host to the address list. + */ + switch (dns_lookup(host, T_A, 0, &addr, (VSTRING *) 0, why)) { + case DNS_OK: + for (rr = addr; rr; rr = rr->next) + rr->pref = pref; + addr_list = dns_rr_append(addr_list, addr); + break; + default: + smtp_errno = SMTP_RETRY; + break; + case DNS_NOTFOUND: + case DNS_FAIL: + smtp_errno = SMTP_FAIL; + break; + } + return (addr_list); +} + +/* smtp_addr_list - address lookup for a list of mail exchangers */ + +static DNS_RR *smtp_addr_list(DNS_RR *mx_names, VSTRING *why) +{ + DNS_RR *addr_list = 0; + DNS_RR *rr; + + /* + * As long as we are able to look up any host address, we ignore problems + * with DNS lookups. + */ + for (rr = mx_names; rr; rr = rr->next) { + if (rr->type != T_MX) + msg_panic("smtp_addr_list: bad resource type: %d", rr->type); + addr_list = smtp_addr_one(addr_list, (char *) rr->data, rr->pref, why); + } + return (addr_list); +} + +/* smtp_find_self - spot myself in a crowd of mail exchangers */ + +static DNS_RR *smtp_find_self(DNS_RR *addr_list) +{ + char *myname = "smtp_find_self"; + INET_ADDR_LIST *self; + DNS_RR *addr; + int i; + + /* + * Find the first address that lists any address that this mail system is + * supposed to be listening on. + */ +#define INADDRP(x) ((struct in_addr *) (x)) + + self = own_inet_addr_list(); + for (addr = addr_list; addr; addr = addr->next) { + for (i = 0; i < self->used; i++) + if (INADDRP(addr->data)->s_addr == self->addrs[i].s_addr) { + if (msg_verbose) + msg_info("%s: found at pref %d", myname, addr->pref); + return (addr); + } + } + + /* + * Didn't find myself. + */ + if (msg_verbose) + msg_info("%s: not found", myname); + return (0); +} + +/* smtp_truncate_self - truncate address list at self and equivalents */ + +static DNS_RR *smtp_truncate_self(DNS_RR *addr_list, unsigned pref, + char *name, VSTRING *why) +{ + DNS_RR *addr; + DNS_RR *last; + + for (last = 0, addr = addr_list; addr; last = addr, addr = addr->next) { + if (pref == addr->pref) { + if (msg_verbose) + smtp_print_addr("truncated", addr); + dns_rr_free(addr); + if (last == 0) { + vstring_sprintf(why, "mail for %s loops back to myself", name); + smtp_errno = SMTP_FAIL; + addr_list = 0; + } else { + last->next = 0; + } + break; + } + } + return (addr_list); +} + +/* smtp_compare_mx - compare resource records by preference */ + +static int smtp_compare_mx(DNS_RR *a, DNS_RR *b) +{ + return (a->pref - b->pref); +} + +/* smtp_domain_addr - mail exchanger address lookup */ + +DNS_RR *smtp_domain_addr(char *name, VSTRING *why) +{ + DNS_RR *mx_names; + DNS_RR *addr_list = 0; + DNS_RR *self; + + /* + * Look up the mail exchanger hosts listed for this name. Sort the + * results by preference. Look up the corresponding host addresses, and + * 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. + */ + switch (dns_lookup(name, 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_names = dns_rr_sort(mx_names, smtp_compare_mx); + addr_list = smtp_addr_list(mx_names, why); + dns_rr_free(mx_names); + if (msg_verbose) + smtp_print_addr(name, addr_list); + if ((self = smtp_find_self(addr_list)) != 0) + addr_list = smtp_truncate_self(addr_list, self->pref, name, why); + break; + case DNS_NOTFOUND: + addr_list = smtp_host_addr(name, why); + break; + } + + /* + * Clean up. + */ + return (addr_list); +} + +/* smtp_host_addr - direct host lookup */ + +DNS_RR *smtp_host_addr(char *host, VSTRING *why) +{ + DNS_FIXED fixed; + DNS_RR *addr_list; + struct in_addr addr; + + /* + * If the host is specified by numerical address, just convert the + * address to internal form. Otherwise, the host is specified by name. + */ +#define PREF0 0 + if (ISDIGIT(host[0]) && (addr.s_addr = inet_addr(host)) != INADDR_NONE) { + fixed.type = fixed.class = fixed.ttl = fixed.length = 0; + addr_list = dns_rr_create(host, &fixed, PREF0, + (char *) &addr, sizeof(addr)); + } else { + addr_list = smtp_addr_one((DNS_RR *) 0, host, PREF0, why); + } + if (msg_verbose) + smtp_print_addr(host, addr_list); + return (addr_list); +} diff --git a/postfix/smtp/smtp_addr.h b/postfix/smtp/smtp_addr.h new file mode 100644 index 000000000..d0dabba49 --- /dev/null +++ b/postfix/smtp/smtp_addr.h @@ -0,0 +1,31 @@ +/*++ +/* NAME +/* smtp_addr 3h +/* SUMMARY +/* SMTP server address lookup +/* SYNOPSIS +/* #include "smtp_addr.h" +/* DESCRIPTION +/* .nf + + /* + * DNS library. + */ +#include + + /* + * Internal interfaces. + */ +extern DNS_RR *smtp_host_addr(char *, VSTRING *); +extern DNS_RR *smtp_domain_addr(char *, VSTRING *); + +/* 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 +/*--*/ diff --git a/postfix/smtp/smtp_chat.c b/postfix/smtp/smtp_chat.c new file mode 100644 index 000000000..143e7d256 --- /dev/null +++ b/postfix/smtp/smtp_chat.c @@ -0,0 +1,270 @@ +/*++ +/* NAME +/* smtp_chat 3 +/* SUMMARY +/* SMTP client request/response support +/* SYNOPSIS +/* #include "smtp.h" +/* +/* typedef struct { +/* .in +4 +/* int code; +/* char *str; +/* VSTRING *buf; +/* .in -4 +/* } SMTP_RESP; +/* +/* void smtp_chat_cmd(state, format, ...) +/* SMTP_STATE *state; +/* char *format; +/* +/* SMTP_RESP *smtp_chat_resp(state) +/* SMTP_STATE *state; +/* +/* void smtp_chat_notify(state) +/* SMTP_STATE *state; +/* +/* void smtp_chat_reset(state) +/* SMTP_STATE *state; +/* DESCRIPTION +/* This module implements SMTP client support for request/reply +/* conversations, and maintains a limited SMTP transaction log. +/* +/* smtp_chat_cmd() formats a command and sends it to an SMTP server. +/* Optionally, the command is logged. +/* +/* smtp_chat_resp() read one SMTP server response. It separates the +/* numerical status code from the text, and concatenates multi-line +/* responses to one string, using a newline as separator. +/* Optionally, the server response is logged. +/* +/* smtp_chat_notify() sends a copy of the SMTP transaction log +/* to the postmaster for review. The postmaster notice is sent only +/* when delivery is possible immediately. It is an error to call +/* smtp_chat_notify() when no SMTP transaction log exists. +/* +/* smtp_chat_reset() resets the transaction log. This is +/* typically done at the beginning or end of an SMTP session, +/* or within a session to discard non-error information. +/* DIAGNOSTICS +/* Fatal errors: memory allocation problem, server response exceeds +/* configurable limit. +/* All other exceptions are handled by long jumps (see smtp_stream(3)). +/* SEE ALSO +/* smtp_stream(3) SMTP session I/O support +/* msg(3) generic logging interface +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include /* 44BSD stdarg.h uses abort() */ +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "smtp.h" + +#define STR(x) ((char *) vstring_str(x)) +#define LEN VSTRING_LEN + +/* smtp_chat_reset - reset SMTP transaction log */ + +void smtp_chat_reset(SMTP_STATE *state) +{ + if (state->history) { + argv_free(state->history); + state->history = 0; + } +} + +/* smtp_chat_append - append record to SMTP transaction log */ + +static void smtp_chat_append(SMTP_STATE *state, char *direction, char *data) +{ + char *line; + + if (state->history == 0) + state->history = argv_alloc(10); + line = concatenate(direction, data, (char *) 0); + argv_add(state->history, line, (char *) 0); + myfree(line); +} + +/* smtp_chat_cmd - send an SMTP command */ + +void smtp_chat_cmd(SMTP_STATE *state, char *fmt,...) +{ + SMTP_SESSION *session = state->session; + va_list ap; + + /* + * Format the command, and update the transaction log. + */ + va_start(ap, fmt); + vstring_vsprintf(state->buffer, fmt, ap); + va_end(ap); + smtp_chat_append(state, "Out: ", STR(state->buffer)); + + /* + * Optionally log the command first, so we can see in the log what the + * program is trying to do. + */ + if (msg_verbose) + msg_info("> %s: %s", session->host, STR(state->buffer)); + + /* + * Send the command to the SMTP server. + */ + smtp_fputs(STR(state->buffer), LEN(state->buffer), session->stream); +} + +/* smtp_chat_resp - read and process SMTP server response */ + +SMTP_RESP *smtp_chat_resp(SMTP_STATE *state) +{ + SMTP_SESSION *session = state->session; + static SMTP_RESP rdata; + int more; + char *cp; + int last_char; + + /* + * Initialize the response data buffer. + */ + if (rdata.buf == 0) + rdata.buf = vstring_alloc(100); + + /* + * Censor out non-printable characters in server responses. Concatenate + * multi-line server responses. Separate the status code from the text. + * Leave further parsing up to the application. + */ + VSTRING_RESET(rdata.buf); + for (;;) { + last_char = smtp_get(state->buffer, session->stream, var_line_limit); + cp = printable(STR(state->buffer), '?'); + if (last_char != '\n') + msg_warn("%s: response longer than %d: %.30s...", + session->host, var_line_limit, cp); + if (msg_verbose) + msg_info("< %s: %s", session->host, cp); + while (ISDIGIT(*cp)) + cp++; + rdata.code = (cp - STR(state->buffer) == 3 ? + atoi(STR(state->buffer)) : 0); + more = (*cp == '-'); + + /* + * Defend against a denial of service attack by limiting the amount + * of multi-line text that we are willing to store. + */ + if (LEN(rdata.buf) < var_line_limit) { + if (VSTRING_LEN(rdata.buf)) + VSTRING_ADDCH(rdata.buf, '\n'); + vstring_strcat(rdata.buf, STR(state->buffer)); + smtp_chat_append(state, "In: ", STR(state->buffer)); + } + if (VSTRING_LEN(state->buffer) == 0) /* XXX remote brain damage */ + continue; + if (!ISDIGIT(STR(state->buffer)[0])) /* XXX remote brain damage */ + continue; + if (more == 0) + break; + } + VSTRING_TERMINATE(rdata.buf); + rdata.str = STR(rdata.buf); + return (&rdata); +} + +/* print_line - line_wrap callback */ + +static void print_line(const char *str, int len, int indent, char *context) +{ + VSTREAM *notice = (VSTREAM *) context; + + post_mail_fprintf(notice, " %*s%.*s", indent, "", len, str); +} + +/* smtp_chat_notify - notify postmaster */ + +void smtp_chat_notify(SMTP_STATE *state) +{ + char *myname = "smtp_chat_notify"; + SMTP_SESSION *session = state->session; + VSTREAM *notice; + char **cpp; + + /* + * Sanity checks. + */ + if (state->history == 0) + msg_panic("%s: no conversation history", myname); + if (msg_verbose) + msg_info("%s: notify postmaster", myname); + + /* + * Construct a message for the postmaster, explaining what this is all + * about. This is junk mail: don't send it when the mail posting service + * is unavailable, and use the double bounce sender address, to prevent + * mail bounce wars. Always prepend one space to message content that we + * generate from untrusted data. + */ +#define NULL_CLEANUP_FLAGS 0 +#define LENGTH 78 +#define INDENT 4 + + notice = post_mail_fopen_nowait(mail_addr_double_bounce(), + mail_addr_postmaster(), + NULL_CLEANUP_FLAGS, "NOTICE"); + if (notice == 0) { + msg_warn("postmaster notify: %m"); + return; + } + post_mail_fprintf(notice, "From: %s (Mail Delivery System)", + mail_addr_mail_daemon()); + post_mail_fprintf(notice, "To: %s (Postmaster)", mail_addr_postmaster()); + post_mail_fprintf(notice, "Subject: %s SMTP client: errors from %s", + var_mail_name, session->host); + post_mail_fputs(notice, ""); + post_mail_fprintf(notice, "Unexpected response from %s.", session->host); + post_mail_fputs(notice, ""); + post_mail_fputs(notice, "Transcript of session follows."); + post_mail_fputs(notice, ""); + argv_terminate(state->history); + for (cpp = state->history->argv; *cpp; cpp++) + line_wrap(printable(*cpp, '?'), LENGTH, INDENT, print_line, + (char *) notice); + (void) post_mail_fclose(notice); +} diff --git a/postfix/smtp/smtp_connect.c b/postfix/smtp/smtp_connect.c new file mode 100644 index 000000000..132d878da --- /dev/null +++ b/postfix/smtp/smtp_connect.c @@ -0,0 +1,359 @@ +/*++ +/* NAME +/* smtp_connect 3 +/* SUMMARY +/* connect to SMTP server +/* SYNOPSIS +/* #include "smtp.h" +/* +/* SMTP_SESSION *smtp_connect(destination, why) +/* char *destination; +/* VSTRING *why; +/* AUXILIARY FUNCTIONS +/* SMTP_SESSION *smtp_connect_domain(name, port, why) +/* char *name; +/* unsigned port; +/* VSTRING *why; +/* +/* SMTP_SESSION *smtp_connect_host(name, port, why) +/* char *name; +/* unsigned port; +/* VSTRING *why; +/* DESCRIPTION +/* This module implements SMTP connection management. +/* +/* smtp_connect() attempts to establish an SMTP session with a host +/* that represents the named domain. +/* +/* The destination is either a host (or domain) name or a numeric +/* address. Symbolic or numeric service port information may be +/* appended, separated by a colon (":"). +/* +/* By default, the Internet domain name service is queried for mail +/* exchanger hosts. Quote the destination with `[' and `]' to +/* suppress mail exchanger lookups. +/* +/* Numerical address information should always be quoted with `[]'. +/* +/* smtp_connect_domain() attempts to make an SMTP connection to +/* the named host or domain and network port (network byte order). +/* \fIname\fR is used to look up mail exchanger information via +/* the Internet domain name system (DNS). +/* When no mail exchanger is listed for \fIname\fR, the request +/* is passed to smtp_connect_host(). +/* Otherwise, mail exchanger hosts are tried in order of preference, +/* until one is found that responds. In order to avoid mailer loops, +/* the search for mail exchanger hosts stops when a host is found +/* that has the same preference as the sending machine. +/* +/* smtp_connect_host() makes an SMTP connection without looking up +/* mail exchanger information. The host can be specified as an +/* Internet network address or as a symbolic host name. +/* DIAGNOSTICS +/* All routines either return an SMTP_SESSION pointer, or +/* return a null pointer and set the \fIsmtp_errno\fR +/* global variable accordingly: +/* .IP SMTP_RETRY +/* The connection attempt failed, but should be retried later. +/* .IP SMTP_FAIL +/* The connection attempt failed. +/* .PP +/* In addition, a textual description of the error is made available +/* via the \fIwhy\fR argument. +/* SEE ALSO +/* smtp_proto(3) SMTP client protocol +/* 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 +#include +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* DNS library. */ + +#include + +/* Application-specific. */ + +#include "smtp.h" +#include "smtp_addr.h" + +/* smtp_connect_addr - connect to explicit address */ + +static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port, + VSTRING *why) +{ + char *myname = "smtp_connect_addr"; + struct sockaddr_in sin; + int sock; + INET_ADDR_LIST *addr_list; + int conn_stat; + int saved_errno; + VSTREAM *stream; + int ch; + + /* + * Sanity checks. + */ + if (addr->data_len > sizeof(sin.sin_addr)) { + msg_warn("%s: skip address with length %d", myname, addr->data_len); + smtp_errno = SMTP_RETRY; + return (0); + } + + /* + * Initialize. + */ + memset((char *) &sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + + if ((sock = socket(sin.sin_family, SOCK_STREAM, 0)) < 0) + msg_fatal("%s: socket: %m", myname); + + /* + * When running as a virtual host, bind to the virtual interface so that + * 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) { + 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])); + } + + /* + * Connect to the SMTP server. + */ + sin.sin_port = port; + memcpy((char *) &sin.sin_addr, addr->data, sizeof(sin.sin_addr)); + + if (msg_verbose) + msg_info("%s: trying: %s/%s port %d...", + myname, addr->name, inet_ntoa(sin.sin_addr), ntohs(port)); + if (var_smtp_conn_tmout > 0) { + non_blocking(sock, NON_BLOCKING); + conn_stat = timed_connect(sock, (struct sockaddr *) & sin, + sizeof(sin), var_smtp_conn_tmout); + saved_errno = errno; + non_blocking(sock, BLOCKING); + errno = saved_errno; + } else { + conn_stat = connect(sock, (struct sockaddr *) & sin, sizeof(sin)); + } + if (conn_stat < 0) { + vstring_sprintf(why, "connect to %s: %m", addr->name); + smtp_errno = SMTP_RETRY; + close(sock); + return (0); + } + + /* + * Skip this host if it takes no action within some time limit. + */ + if (read_wait(sock, var_smtp_helo_tmout) < 0) { + vstring_sprintf(why, "connect to %s: read timeout", addr->name); + smtp_errno = SMTP_RETRY; + close(sock); + return (0); + } + + /* + * Skip this host if it disconnects without talking to us. + */ + stream = vstream_fdopen(sock, O_RDWR); + if ((ch = VSTREAM_GETC(stream)) == VSTREAM_EOF) { + vstring_sprintf(why, "connect to %s: server dropped connection", + addr->name); + smtp_errno = SMTP_RETRY; + vstream_fclose(stream); + return (0); + } + + /* + * 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. + */ +#if 0 + if (!ISDIGIT(ch)) { + vstring_sprintf(why, "connect to %s: non-numeric server response", + 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))); +} + +/* smtp_connect_host - direct connection to host */ + +SMTP_SESSION *smtp_connect_host(char *host, unsigned port, VSTRING *why) +{ + SMTP_SESSION *session = 0; + DNS_RR *addr_list; + DNS_RR *addr; + + /* + * Try each address in the specified order until we find one that works. + * The addresses belong to the same A record, so we have no information + * on what address is "best". + */ + addr_list = smtp_host_addr(host, why); + for (addr = addr_list; addr; addr = addr->next) { + if ((session = smtp_connect_addr(addr, port, why)) != 0) { + session->best = 1; + break; + } + } + dns_rr_free(addr_list); + return (session); +} + +/* smtp_connect_domain - connect to smtp server for domain */ + +SMTP_SESSION *smtp_connect_domain(char *name, unsigned port, VSTRING *why) +{ + SMTP_SESSION *session = 0; + DNS_RR *addr_list; + DNS_RR *addr; + + /* + * Try each mail exchanger in order of preference until we find one that + * responds. Once we find a server that responds we never try + * alternative mail exchangers. The benefit of this is that we will use + * backup hosts only when we are unable to reach the primary MX host. If + * the primary MX host is reachable but does not want to receive our + * mail, there is no point in trying the backup hosts. + */ + addr_list = smtp_domain_addr(name, why); + for (addr = addr_list; addr; addr = addr->next) { + if ((session = smtp_connect_addr(addr, port, why)) != 0) { + session->best = (addr->pref == addr_list->pref); + break; + } + } + dns_rr_free(addr_list); + return (session); +} + +/* smtp_parse_destination - parse destination */ + +static char *smtp_parse_destination(char *destination, char *def_service, + char **hostp, unsigned *portp) +{ + char *buf = mystrdup(destination); + char *host = buf; + char *service; + struct servent *sp; + char *protocol = "tcp"; /* XXX configurable? */ + unsigned port; + + if (msg_verbose) + msg_info("smtp_parse_destination: %s %s", destination, def_service); + + /* + * Strip quoting. We're working with a copy of the destination argument + * so the stripping can be destructive. + */ + if (*host == '[') { + host++; + host[strcspn(host, "]")] = 0; + } + + /* + * Separate host and service information, or use the default service + * specified by the caller. XXX the ":" character is used in the IPV6 + * address notation, so using split_at_right() is not sufficient. We'd + * have to count the number of ":" instances. + */ + if ((service = split_at_right(host, ':')) == 0) + service = def_service; + if (*service == 0) + msg_fatal("empty service name: %s", destination); + *hostp = host; + + /* + * Convert service to port number, network byte order. + */ + if ((port = atoi(service)) != 0) { + *portp = htons(port); + } else { + if ((sp = getservbyname(service, protocol)) == 0) + msg_fatal("unknown service: %s/%s", service, protocol); + *portp = sp->s_port; + } + return (buf); +} + +/* smtp_connect - establish SMTP connection */ + +SMTP_SESSION *smtp_connect(char *destination, VSTRING *why) +{ + SMTP_SESSION *session; + char *dest_buf; + char *host; + unsigned port; + char *def_service = "smtp"; /* XXX configurable? */ + + /* + * Parse the destination specification. Default is to use the SMTP port. + */ + dest_buf = smtp_parse_destination(destination, def_service, &host, &port); + + /* + * Connect to an SMTP server. Skip mail exchanger lookups when a quoted + * host is specified. + */ + if (msg_verbose) + msg_info("connecting to %s port %d", host, ntohs(port)); + if (*destination == '[') { + session = smtp_connect_host(host, port, why); + } else { + session = smtp_connect_domain(host, port, why); + } + myfree(dest_buf); + return (session); +} diff --git a/postfix/smtp/smtp_proto.c b/postfix/smtp/smtp_proto.c new file mode 100644 index 000000000..87433cf1e --- /dev/null +++ b/postfix/smtp/smtp_proto.c @@ -0,0 +1,613 @@ +/*++ +/* NAME +/* smtp_proto 3 +/* SUMMARY +/* client SMTP protocol +/* SYNOPSIS +/* #include "smtp.h" +/* +/* int smtp_helo(state) +/* SMTP_STATE *state; +/* +/* int smtp_xfer(state) +/* SMTP_STATE *state; +/* DESCRIPTION +/* This module implements the client side of the SMTP protocol. +/* +/* smtp_helo() performs the initial handshake with the SMTP server. +/* +/* smtp_xfer() sends message envelope information followed by the +/* message data, and finishes the SMTP conversation. These operations +/* are combined in one function, in order to implement SMTP pipelining. +/* Recipients are marked as "done" in the mail queue file when +/* bounced or delivered. The message delivery status is updated +/* accordingly. +/* DIAGNOSTICS +/* smtp_helo() and smtp_xfer() return 0 in case of success, -1 in case +/* of failure. For smtp_xfer(), success means the ability to perform +/* an SMTP conversation, not necessarily the ability to deliver mail. +/* +/* Warnings: corrupt message file. A corrupt message is marked +/* as "corrupt" by changing its queue file permissions. +/* BUGS +/* Some SMTP servers will abort when the number of recipients +/* for one message exceeds their capacity. This behavior violates +/* the SMTP protocol. +/* The only way around this is to limit the number of recipients +/* per transaction to an artificially-low value. +/* SEE ALSO +/* smtp(3h) internal data structures +/* smtp_chat(3) query/reply SMTP support +/* smtp_trouble(3) error handlers +/* 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 +/* +/* Pipelining code in cooperation with: +/* Jon Ribbens +/* Oaktree Internet Solutions Ltd., +/* Internet House, +/* Canal Basin, +/* Coventry, +/* CV1 4LY, United Kingdom. +/* +/*--*/ + +/* System library. */ + +#include +#include +#include /* shutdown(2) */ +#include +#include +#include +#include /* 44BSD stdarg.h uses abort() */ +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "smtp.h" +#include "quote_821_local.h" + + /* + * Sender and receiver state. A session does not necessarily go through a + * linear progression, so states should be compared for equality only. + * Normal sessions go from MAIL->RCPT->DATA->DOT->QUIT->LAST. The states + * MAIL, RCPT, and DATA may also be followed by ABORT->QUIT->LAST. + */ +#define SMTP_STATE_MAIL 0 +#define SMTP_STATE_RCPT 1 +#define SMTP_STATE_DATA 2 +#define SMTP_STATE_DOT 3 +#define SMTP_STATE_ABORT 4 +#define SMTP_STATE_QUIT 5 +#define SMTP_STATE_LAST 6 + +int *xfer_timeouts[SMTP_STATE_LAST] = { + &var_smtp_mail_tmout, + &var_smtp_rcpt_tmout, + &var_smtp_data0_tmout, + &var_smtp_data2_tmout, + &var_smtp_quit_tmout, + &var_smtp_quit_tmout, +}; + +char *xfer_states[SMTP_STATE_LAST] = { + "sending MAIL FROM", + "sending RCPT TO", + "sending DATA command", + "sending end of data -- message may be sent more than once", + "sending final RSET", + "sending QUIT", +}; + +/* smtp_helo - perform initial handshake with SMTP server */ + +int smtp_helo(SMTP_STATE *state) +{ + SMTP_SESSION *session = state->session; + DELIVER_REQUEST *request = state->request; + SMTP_RESP *resp; + int except; + char *lines; + char *words; + char *word; + int n; + + /* + * Prepare for disaster. + */ + smtp_timeout_setup(state->session->stream, var_smtp_helo_tmout); + if ((except = setjmp(smtp_timeout_buf)) != 0) + return (smtp_stream_except(state, except, "sending HELO")); + + /* + * Read and parse the server's SMTP greeting banner. + */ + if (((resp = smtp_chat_resp(state))->code / 100) != 2) + return (smtp_site_fail(state, resp->code, + "host %s refused to talk to me: %s", + session->host, translit(resp->str, "\n", " "))); + + /* + * See if we are talking to ourself. This should not be possible with the + * way we implement DNS lookups. However, people are known to sometimes + * screw up the naming service. And, mailer loops are still possible when + * our own mailer routing tables are mis-configured. + */ + words = resp->str; + (void) mystrtok(&words, "- \t\n"); + for (n = 0; (word = mystrtok(&words, " \t\n")) != 0; n++) { + if (n == 0 && strcasecmp(word, var_myhostname) == 0) { + msg_warn("host %s greeted me with my own hostname %s", + session->host, var_myhostname); + return (smtp_site_fail(state, session->best ? 550 : 450, + "mail for %s loops back to myself", + request->nexthop)); + } else if (strcasecmp(word, "ESMTP") == 0) + state->features |= SMTP_FEATURE_ESMTP; + } + + /* + * Return the compliment. Fall back to SMTP if our ESMTP recognition + * heuristic failed. + */ + if (state->features & SMTP_FEATURE_ESMTP) { + smtp_chat_cmd(state, "EHLO %s", var_myhostname); + if ((resp = smtp_chat_resp(state))->code / 100 != 2) + state->features &= ~SMTP_FEATURE_ESMTP; + } + if ((state->features & SMTP_FEATURE_ESMTP) == 0) { + smtp_chat_cmd(state, "HELO %s", var_myhostname); + if ((resp = smtp_chat_resp(state))->code / 100 != 2) + return (smtp_site_fail(state, resp->code, + "host %s refused to talk to me: %s", + session->host, + translit(resp->str, "\n", " "))); + } + + /* + * Pick up some useful features offered by the SMTP server. XXX Until we + * have a portable routine to convert from string to off_t with proper + * overflow detection, ignore the message size limit advertised by the + * SMTP server. Otherwise, we might do the wrong thing when the server + * advertises a really huge message size limit. + */ + lines = resp->str; + (void) mystrtok(&lines, "\n"); + while ((words = mystrtok(&lines, "\n")) != 0) { + if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t")) != 0) { + if (strcasecmp(word, "8BITMIME") == 0) + state->features |= SMTP_FEATURE_8BITMIME; + else if (strcasecmp(word, "PIPELINING") == 0) + state->features |= SMTP_FEATURE_PIPELINING; + else if (strcasecmp(word, "SIZE") == 0) + state->features |= SMTP_FEATURE_SIZE; + } + } + if (msg_verbose) + msg_info("server features: 0x%x", state->features); + return (0); +} + +/* smtp_xfer - send a batch of envelope information and the message data */ + +int smtp_xfer(SMTP_STATE *state) +{ + char *myname = "smtp_xfer"; + DELIVER_REQUEST *request = state->request; + SMTP_SESSION *session = state->session; + SMTP_RESP *resp; + RECIPIENT *rcpt; + VSTRING *next_command = vstring_alloc(100); + int next_state; + int next_rcpt; + int send_state; + int recv_state; + int send_rcpt; + int recv_rcpt; + int nrcpt; + int except; + int rec_type; + int prev_type = 0; + int sndbufsize; + int sndbuffree; + SOCKOPT_SIZE optlen = sizeof(sndbufsize); + int mail_from_rejected; + + /* + * Macros for readability. + */ +#define REWRITE_ADDRESS(addr) do { \ + if (*(addr)) { \ + quote_821_local(state->scratch, addr); \ + smtp_unalias_addr(state->scratch2, vstring_str(state->scratch)); \ + myfree(addr); \ + addr = mystrdup(vstring_str(state->scratch2)); \ + } \ + } while (0) + +#define RETURN(x) do { vstring_free(next_command); return (x); } while (0) + +#define SENDER_IS_AHEAD \ + (recv_state != send_state || recv_rcpt != send_rcpt) + +#define SENDER_IN_WAIT_STATE \ + (send_state == SMTP_STATE_DOT || send_state == SMTP_STATE_LAST) + + /* + * We use SMTP command pipelining if the server said it supported it. + * Since we use blocking I/O, RFC 2197 says that we should inspect the + * TCP window size and not send more than this amount of information. + * Unfortunately this information is not available using the sockets + * interface. However, we *can* get the TCP send buffer size on the local + * TCP/IP stack. We should be able to fill this buffer without being + * blocked, and then the kernel will effectively do non-blocking I/O for + * us by automatically writing out the contents of its send buffer while + * we are reading in the responses. In addition to TCP buffering we have + * to be aware of application-level buffering by the vstream module, + * which is limited to a couple kbytes. + */ + if (state->features & SMTP_FEATURE_PIPELINING) { + if (getsockopt(vstream_fileno(state->session->stream), SOL_SOCKET, + SO_SNDBUF, (char *) &sndbufsize, &optlen) < 0) + msg_fatal("%s: getsockopt: %m", myname); + if (msg_verbose) + msg_info("Using ESMTP PIPELINING, TCP send buffer size is %d", + sndbufsize); + } else { + sndbufsize = 0; + } + sndbuffree = sndbufsize; + + /* + * Pipelining support requires two loops: one loop for sending and one + * for receiving. Each loop has its own independent state. Most of the + * time the sender can run ahead of the receiver by as much as the TCP + * send buffer permits. There are only two places where the sender must + * wait for status information from the receiver: once after sending DATA + * and once after sending QUIT. + * + * The sender state advances until the TCP send buffer would overflow, or + * until the sender needs status information from the receiver. At that + * point the receiver starts processing responses. Once the receiver has + * caught up with the sender, the sender resumes sending commands. If the + * receiver detects a serious problem (MAIL FROM rejected, all RCPT TO + * commands rejected, DATA rejected) it forces the sender to abort the + * SMTP dialog with RSET and QUIT. + */ + nrcpt = 0; + recv_state = send_state = SMTP_STATE_MAIL; + next_rcpt = send_rcpt = recv_rcpt = 0; + mail_from_rejected = 0; + + while (recv_state != SMTP_STATE_LAST) { + + /* + * Build the next command. + */ + switch (send_state) { + + /* + * Sanity check. + */ + default: + msg_panic("%s: bad sender state %d", myname, send_state); + + /* + * Build the MAIL FROM command. + */ + case SMTP_STATE_MAIL: + if (*request->sender) + if (var_disable_dns == 0) + REWRITE_ADDRESS(request->sender); + vstring_sprintf(next_command, "MAIL FROM:<%s>", request->sender); + if (state->features & SMTP_FEATURE_SIZE) + vstring_sprintf_append(next_command, " SIZE=%lu", + request->data_size); + next_state = SMTP_STATE_RCPT; + break; + + /* + * Build one RCPT TO command before we have seen the MAIL FROM + * response. + */ + case SMTP_STATE_RCPT: + rcpt = request->rcpt_list.info + send_rcpt; + if (var_disable_dns == 0) + REWRITE_ADDRESS(rcpt->address); + vstring_sprintf(next_command, "RCPT TO:<%s>", rcpt->address); + if ((next_rcpt = send_rcpt + 1) == request->rcpt_list.len) + next_state = SMTP_STATE_DATA; + break; + + /* + * Build the DATA command before we have seen all the RCPT TO + * responses. + */ + case SMTP_STATE_DATA: + vstring_strcpy(next_command, "DATA"); + next_state = SMTP_STATE_DOT; + break; + + /* + * Build the "." command before we have seen the DATA response. + */ + case SMTP_STATE_DOT: + vstring_strcpy(next_command, "."); + next_state = SMTP_STATE_QUIT; + break; + + /* + * Can't happen. The SMTP_STATE_ABORT sender state is entered by + * the receiver and is left before the bottom of the main loop. + */ + case SMTP_STATE_ABORT: + msg_panic("%s: sender abort state", myname); + + /* + * Build the QUIT command before we have seen the "." or RSET + * response. + */ + case SMTP_STATE_QUIT: + vstring_strcpy(next_command, "QUIT"); + next_state = SMTP_STATE_LAST; + break; + + /* + * The final sender state has no action associated with it. + */ + case SMTP_STATE_LAST: + break; + } + VSTRING_TERMINATE(next_command); + + /* + * Process responses until the receiver has caught up. Vstreams + * automatically flush buffered output when reading new data. + */ + if (SENDER_IN_WAIT_STATE + || (SENDER_IS_AHEAD && VSTRING_LEN(next_command) + 2 > sndbuffree)) { + while (SENDER_IS_AHEAD) { + + /* + * Sanity check. + */ + if (recv_state < SMTP_STATE_MAIL + || recv_state > SMTP_STATE_QUIT) + msg_panic("%s: bad receiver state %d", myname, recv_state); + + /* + * Receive the next server response. Use the proper timeout, + * and log the proper client state in case of trouble. + */ + smtp_timeout_setup(state->session->stream, + *xfer_timeouts[recv_state]); + if ((except = setjmp(smtp_timeout_buf)) != 0) + RETURN(smtp_stream_except(state, except, + xfer_states[recv_state])); + resp = smtp_chat_resp(state); + + /* + * Process the response. + */ + switch (recv_state) { + + /* + * Process the MAIL FROM response. When the server + * rejects the sender, set the mail_from_rejected flag so + * that the receiver may apply a course correction. + */ + case SMTP_STATE_MAIL: + if (resp->code / 100 != 2) { + smtp_mesg_fail(state, resp->code, + "host %s said: %s", session->host, + translit(resp->str, "\n", " ")); + mail_from_rejected = 1; + } + recv_state = SMTP_STATE_RCPT; + break; + + /* + * Process one RCPT TO response. If MAIL FROM was + * rejected, ignore RCPT TO responses: all recipients are + * dead already. When all recipients are rejected the + * receiver may apply a course correction. + */ + case SMTP_STATE_RCPT: + if (!mail_from_rejected) { + if (resp->code / 100 == 2) { + ++nrcpt; + } else { + rcpt = request->rcpt_list.info + recv_rcpt; + smtp_rcpt_fail(state, resp->code, rcpt, + "host %s said: %s", session->host, + translit(resp->str, "\n", " ")); + rcpt->offset = 0; /* in case deferred */ + } + } + if (++recv_rcpt == request->rcpt_list.len) + recv_state = SMTP_STATE_DATA; + break; + + /* + * Process the DATA response. When the server rejects + * DATA, set nrcpt to a negative value so that the + * receiver can apply a course correction. + */ + case SMTP_STATE_DATA: + if (resp->code / 100 != 3) { + if (nrcpt > 0) + smtp_mesg_fail(state, resp->code, + "host %s said: %s", session->host, + translit(resp->str, "\n", " ")); + nrcpt = -1; + } + recv_state = SMTP_STATE_DOT; + break; + + /* + * Process the end of message response. Ignore the + * response when no recipient was accepted: all + * recipients are dead already, and the next receiver + * state is SMTP_STATE_QUIT regardless. Otherwise, if the + * message transfer fails, bounce all remaining + * recipients, else cross off the recipients that were + * delivered. + */ + case SMTP_STATE_DOT: + if (nrcpt > 0) { + if (resp->code / 100 != 2) { + smtp_mesg_fail(state, resp->code, + "host %s said: %s", + session->host, + translit(resp->str, "\n", " ")); + } else { + for (nrcpt = 0; nrcpt < recv_rcpt; nrcpt++) { + rcpt = request->rcpt_list.info + nrcpt; + if (rcpt->offset) { + sent(request->queue_id, rcpt->address, + session->host, request->arrival_time, "%s", + resp->str); + deliver_completed(state->src, rcpt->offset); + rcpt->offset = 0; + } + } + } + } + recv_state = SMTP_STATE_QUIT; + break; + + /* + * Ignore the RSET response. + */ + case SMTP_STATE_ABORT: + recv_state = SMTP_STATE_QUIT; + break; + + /* + * Ignore the QUIT response. + */ + case SMTP_STATE_QUIT: + recv_state = SMTP_STATE_LAST; + break; + } + } + + /* + * At this point, the sender and receiver are fully synchronized, + * so that the entire TCP send buffer becomes available again. + */ + sndbuffree = sndbufsize; + + /* + * We know the server response to every command that was sent. + * Apply a course correction if necessary: the sender wants to + * send RCPT TO but MAIL FROM was rejected; the sender wants to + * send DATA but all recipients were rejected; the sender wants + * to deliver the message but DATA was rejected. + */ + if ((send_state == SMTP_STATE_RCPT && mail_from_rejected) + || (send_state == SMTP_STATE_DATA && nrcpt == 0) + || (send_state == SMTP_STATE_DOT && nrcpt < 0)) { + send_state = recv_state = SMTP_STATE_ABORT; + send_rcpt = recv_rcpt = 0; + vstring_strcpy(next_command, "RSET"); + next_state = SMTP_STATE_QUIT; + next_rcpt = 0; + } + } + + /* + * Make the next sender state the current sender state. + */ + if (send_state == SMTP_STATE_LAST) + continue; + + /* + * Special case if the server accepted the DATA command. If the + * server accepted at least one recipient send the entire message. + * Otherwise, just send "." as per RFC 2197. + */ + if (send_state == SMTP_STATE_DOT && nrcpt > 0) { + smtp_timeout_setup(state->session->stream, + var_smtp_data1_tmout); + if ((except = setjmp(smtp_timeout_buf)) != 0) + RETURN(smtp_stream_except(state, except, + "sending message body")); + + if (vstream_fseek(state->src, request->data_offset, SEEK_SET) < 0) + msg_fatal("seek queue file: %m"); + + while ((rec_type = rec_get(state->src, state->scratch, 0)) > 0) { + if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT) + break; + if (prev_type != REC_TYPE_CONT) + if (vstring_str(state->scratch)[0] == '.') + smtp_fputc('.', session->stream); + if (rec_type == REC_TYPE_CONT) + smtp_fwrite(vstring_str(state->scratch), + VSTRING_LEN(state->scratch), + session->stream); + else + smtp_fputs(vstring_str(state->scratch), + VSTRING_LEN(state->scratch), + session->stream); + prev_type = rec_type; + } + + if (prev_type == REC_TYPE_CONT) /* missing newline at end */ + smtp_fputs("", 0, session->stream); + if (vstream_ferror(state->src)) + msg_fatal("queue file read error"); + if (rec_type != REC_TYPE_XTRA) + RETURN(mark_corrupt(state->src)); + } + + /* + * Copy the next command to the buffer and update the sender state. + */ + if (sndbuffree > 0) + sndbuffree -= VSTRING_LEN(next_command) + 2; + smtp_chat_cmd(state, "%s", vstring_str(next_command)); + send_state = next_state; + send_rcpt = next_rcpt; + } + + RETURN(0); +} diff --git a/postfix/smtp/smtp_session.c b/postfix/smtp/smtp_session.c new file mode 100644 index 000000000..549bf1403 --- /dev/null +++ b/postfix/smtp/smtp_session.c @@ -0,0 +1,71 @@ +/*++ +/* NAME +/* smtp_session 3 +/* SUMMARY +/* SMTP_SESSION structure management +/* SYNOPSIS +/* #include "smtp.h" +/* +/* SMTP_SESSION *smtp_session_alloc(stream, host, addr) +/* VSTREAM *stream; +/* char *host; +/* char *addr; +/* +/* void smtp_session_free(session) +/* SMTP_SESSION *session; +/* DESCRIPTION +/* smtp_session_alloc() allocates memory for an SMTP_SESSION structure +/* and initializes it with the given stream and host name and address +/* information. The host name and address strings are copied. The code +/* assumes that the stream is connected to the "best" alternative. +/* +/* smtp_session_free() destroys an SMTP_SESSION structure and its +/* members, making memory available for reuse. +/* 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 + +/* Application-specific. */ + +#include "smtp.h" + +/* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */ + +SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, char *host, char *addr) +{ + SMTP_SESSION *session; + + session = (SMTP_SESSION *) mymalloc(sizeof(*session)); + session->stream = stream; + session->host = mystrdup(host); + session->addr = mystrdup(addr); + session->best = 1; + return (session); +} + +/* smtp_session_free - destroy SMTP_SESSION structure and contents */ + +void smtp_session_free(SMTP_SESSION *session) +{ + vstream_fclose(session->stream); + myfree(session->host); + myfree(session->addr); + myfree((char *) session); +} + diff --git a/postfix/smtp/smtp_state.c b/postfix/smtp/smtp_state.c new file mode 100644 index 000000000..97c395a84 --- /dev/null +++ b/postfix/smtp/smtp_state.c @@ -0,0 +1,78 @@ +/*++ +/* NAME +/* smtp_state 8 +/* SUMMARY +/* initialize/cleanup shared state +/* SYNOPSIS +/* #include "smtp.h" +/* +/* SMTP_STATE *smtp_state_alloc() +/* +/* void smtp_state_free(state) +/* SMTP_STATE *state; +/* DESCRIPTION +/* smtp_state_init() initializes the shared state, and allocates +/* memory for buffers etc. +/* +/* smtp_cleanup() destroys memory allocated by smtp_state_init(). +/* STANDARDS +/* DIAGNOSTICS +/* BUGS +/* SEE ALSO +/* 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 + +/* Application-specific. */ + +#include "smtp.h" + +/* smtp_state_alloc - initialize */ + +SMTP_STATE *smtp_state_alloc(void) +{ + SMTP_STATE *state = (SMTP_STATE *) mymalloc(sizeof(*state)); + + state->src = 0; + state->request = 0; + state->session = 0; + state->buffer = vstring_alloc(100); + state->scratch = vstring_alloc(100); + state->scratch2 = vstring_alloc(100); + state->status = 0; + state->features = 0; + state->history = 0; + state->error_mask = 0; + return (state); +} + +/* smtp_state_free - destroy state */ + +void smtp_state_free(SMTP_STATE *state) +{ + vstring_free(state->buffer); + vstring_free(state->scratch); + vstring_free(state->scratch2); + myfree((char *) state); +} diff --git a/postfix/smtp/smtp_trouble.c b/postfix/smtp/smtp_trouble.c new file mode 100644 index 000000000..0cf53a289 --- /dev/null +++ b/postfix/smtp/smtp_trouble.c @@ -0,0 +1,307 @@ +/*++ +/* NAME +/* smtp_trouble 3 +/* SUMMARY +/* error handler policies +/* SYNOPSIS +/* #include "smtp.h" +/* +/* int smtp_site_fail(state, code, format, ...) +/* SMTP_STATE *state; +/* int code; +/* char *format; +/* +/* int smtp_mesg_fail(state, code, format, ...) +/* SMTP_STATE *state; +/* int code; +/* char *format; +/* +/* void smtp_rcpt_fail(state, code, recipient, format, ...) +/* SMTP_STATE *state; +/* int code; +/* RECIPIENT *recipient; +/* char *format; +/* +/* int smtp_stream_except(state, exception, description) +/* SMTP_STATE *state; +/* int exception; +/* char *description; +/* DESCRIPTION +/* This module handles all non-fatal errors that can happen while +/* attempting to deliver mail via SMTP, and implements the policy +/* of how to deal with the error. Depending on the nature of +/* the problem, delivery of a single message is deferred, delivery +/* of all messages to the same domain is deferred, or one or more +/* recipients are given up as non-deliverable and a bounce log is +/* updated. +/* +/* In addition, when an unexpected response code is seen such +/* as 3xx where only 4xx or 5xx are expected, or any error code +/* that suggests a syntax error or something similar, the +/* protocol error flag is set so that the postmaster receives +/* a transcript of the session. No notification is generated for +/* what appear to be configuration errors - very likely, they +/* would suffer the same problem and just cause more trouble. +/* +/* smtp_site_fail() handles the case where the program fails to +/* complete the initial SMTP handshake: the server is not reachable, +/* is not running, does not want talk to us, or we talk to ourselves. +/* The \fIcode\fR gives an error status code; the \fIformat\fR +/* argument gives a textual description. The policy is: soft +/* error: defer delivery of all messages to this domain; hard +/* error: bounce all recipients of this message. +/* The result is non-zero. +/* +/* smtp_mesg_fail() handles the case where the smtp server +/* does not accept the sender address or the message data. +/* The policy is: soft errors: defer delivery of this message; +/* hard error: bounce all recipients of this message. +/* The result is non-zero. +/* +/* smtp_rcpt_fail() handles the case where a recipient is not +/* accepted by the server for reasons other than that the server +/* recipient limit is reached. The policy is: soft error: defer +/* delivery to this recipient; hard error: bounce this recipient. +/* +/* smtp_stream_except() handles the exceptions generated by +/* the smtp_stream(3) module (i.e. timeouts and I/O errors). +/* The \fIexception\fR argument specifies the type of problem. +/* The \fIdescription\fR argument describes at what stage of +/* the SMTP dialog the problem happened. The policy is to defer +/* delivery of all messages to the same domain. The result is non-zero. +/* DIAGNOSTICS +/* Panic: unknown exception code. +/* SEE ALSO +/* smtp_proto(3) smtp high-level protocol +/* smtp_stream(3) smtp low-level protocol +/* defer(3) basic message defer interface +/* bounce(3) basic message bounce interface +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include /* 44BSD stdarg.h uses abort() */ +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "smtp.h" + +#define SMTP_SOFT(code) (((code) / 100) == 4) +#define SMTP_HARD(code) (((code) / 100) == 5) +#define KEEP BOUNCE_FLAG_KEEP + +/* smtp_check_code - check response code */ + +static void smtp_check_code(SMTP_STATE *state, int code) +{ + + /* + * The intention of this stuff is to alert the postmaster when the local + * Postfix SMTP client screws up, protocol wise. RFC 821 says that x0z + * replies "refer to syntax errors, syntactically correct commands that + * don't fit any functional category, and unimplemented or superfluous + * commands". Unfortunately, this also triggers postmaster notices when + * remote servers screw up, protocol wise. This is becoming a common + * problem now that response codes are configured manually as part of + * anti-UCE systems, by people who aren't aware of RFC details. + */ + if ((!SMTP_SOFT(code) && !SMTP_HARD(code)) + || (code >= 500 && code < 510)) + state->error_mask |= MAIL_ERROR_PROTOCOL; +} + +/* smtp_site_fail - defer site or bounce recipients */ + +int smtp_site_fail(SMTP_STATE *state, int code, char *format,...) +{ + DELIVER_REQUEST *request = state->request; + SMTP_SESSION *session = state->session; + RECIPIENT *rcpt; + int status; + int nrcpt; + int soft_error = SMTP_SOFT(code); + va_list ap; + VSTRING *why = vstring_alloc(100); + + /* + * Initialize. + */ + va_start(ap, format); + vstring_vsprintf(why, format, ap); + va_end(ap); + + /* + * If this is a soft error, postpone further deliveries to this domain. + * Otherwise, generate a bounce record for each recipient. + */ + for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { + rcpt = request->rcpt_list.info + nrcpt; + if (rcpt->offset == 0) + continue; + status = (soft_error ? defer_append : bounce_append) + (KEEP, request->queue_id, rcpt->address, + session ? session->host : "none", + request->arrival_time, "%s", vstring_str(why)); + if (status == 0) { + deliver_completed(state->src, rcpt->offset); + rcpt->offset = 0; + } + state->status |= status; + } + if (soft_error && request->hop_status == 0) + request->hop_status = mystrdup(vstring_str(why)); + + /* + * Cleanup. + */ + vstring_free(why); + return (-1); +} + +/* smtp_mesg_fail - defer message or bounce all recipients */ + +int smtp_mesg_fail(SMTP_STATE *state, int code, char *format,...) +{ + DELIVER_REQUEST *request = state->request; + SMTP_SESSION *session = state->session; + RECIPIENT *rcpt; + int status; + int nrcpt; + va_list ap; + VSTRING *why = vstring_alloc(100); + + /* + * Initialize. + */ + va_start(ap, format); + vstring_vsprintf(why, format, ap); + va_end(ap); + + /* + * If this is a soft error, postpone delivery of this message. Otherwise, + * generate a bounce record for each recipient. + */ + for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { + rcpt = request->rcpt_list.info + nrcpt; + if (rcpt->offset == 0) + continue; + status = (SMTP_SOFT(code) ? defer_append : bounce_append) + (KEEP, request->queue_id, rcpt->address, + session->host, request->arrival_time, + "%s", vstring_str(why)); + if (status == 0) { + deliver_completed(state->src, rcpt->offset); + rcpt->offset = 0; + } + state->status |= status; + } + smtp_check_code(state, code); + + /* + * Cleanup. + */ + vstring_free(why); + return (-1); +} + +/* smtp_rcpt_fail - defer or bounce recipient */ + +void smtp_rcpt_fail(SMTP_STATE *state, int code, RECIPIENT *rcpt, + char *format,...) +{ + DELIVER_REQUEST *request = state->request; + SMTP_SESSION *session = state->session; + int status; + va_list ap; + + /* + * If this is a soft error, postpone delivery to this recipient. + * Otherwise, generate a bounce record for this recipient. + */ + va_start(ap, format); + status = (SMTP_SOFT(code) ? vdefer_append : vbounce_append) + (KEEP, request->queue_id, rcpt->address, session->host, + request->arrival_time, format, ap); + va_end(ap); + if (status == 0) { + deliver_completed(state->src, rcpt->offset); + rcpt->offset = 0; + } + smtp_check_code(state, code); + state->status |= status; +} + +/* smtp_stream_except - defer domain after I/O problem */ + +int smtp_stream_except(SMTP_STATE *state, int code, char *description) +{ + DELIVER_REQUEST *request = state->request; + SMTP_SESSION *session = state->session; + RECIPIENT *rcpt; + int nrcpt; + VSTRING *why = vstring_alloc(100); + + /* + * Initialize. + */ + switch (code) { + default: + msg_panic("smtp_stream_except: unknown exception %d", code); + case SMTP_ERR_EOF: + vstring_sprintf(why, "lost connection with %s while %s", + session->host, description); + break; + case SMTP_ERR_TIME: + vstring_sprintf(why, "conversation with %s timed out while %s", + session->host, description); + break; + } + + /* + * At this point, the status of individual recipients remains unresolved. + * All we know is that we should stay away from this host for a while. + */ + for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { + rcpt = request->rcpt_list.info + nrcpt; + if (rcpt->offset == 0) + continue; + state->status |= defer_append(KEEP, request->queue_id, + rcpt->address, session->host, + request->arrival_time, + "%s", vstring_str(why)); + } + + /* + * Cleanup. + */ + vstring_free(why); + return (-1); +} diff --git a/postfix/smtp/smtp_unalias.c b/postfix/smtp/smtp_unalias.c new file mode 100644 index 000000000..91cf6f989 --- /dev/null +++ b/postfix/smtp/smtp_unalias.c @@ -0,0 +1,134 @@ +/*++ +/* NAME +/* smtp_unalias 3 +/* SUMMARY +/* replace host/domain alias by official name +/* SYNOPSIS +/* #include "smtp.h" +/* +/* const char *smtp_unalias_name(name) +/* const char *name; +/* +/* VSTRING *smtp_unalias_addr(result, addr) +/* VSTRING *result; +/* const char *addr; +/* DESCRIPTION +/* smtp_unalias_name() looks up A or MX records for the specified +/* name and returns the official name provided in the reply. When +/* no A or MX record is found, the result is a copy of the input. +/* In order to improve performance, smtp_unalias_name() remembers +/* results in a private cache, so a name is looked up only once. +/* +/* smtp_unalias_addr() returns the input address, which is in +/* RFC 821 quoted form, after replacing the domain part by the +/* official name as found by smtp_unalias_name(). When the input +/* contains no domain part, the result is a copy of the input. +/* 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 + +/* Utility library. */ + +#include +#include +#include + +/* DNS library. */ + +#include + +/* Application-specific. */ + +#include "smtp.h" + +static int smtp_unalias_flags; + +/* smtp_unalias_name - look up the official host or domain name. */ + +const char *smtp_unalias_name(const char *name) +{ + static HTABLE *cache; + VSTRING *fqdn; + char *result; + + /* + * Initialize the cache on the fly. The smtp client is designed to exit + * after servicing a limited number of requests, so there is no need to + * prevent the cache from growing too large, or to expire old entries. + */ + if (cache == 0) + cache = htable_create(10); + + /* + * Look up the fqdn. If none is found use the query name instead, so that + * we won't lose time looking up the same bad name again. + */ + if ((result = htable_find(cache, name)) == 0) { + fqdn = vstring_alloc(10); + if (dns_lookup_types(name, smtp_unalias_flags, (DNS_RR **) 0, + fqdn, (VSTRING *) 0, T_MX, T_A, 0) != DNS_OK) + vstring_strcpy(fqdn, name); + htable_enter(cache, name, result = vstring_export(fqdn)); + } + return (result); +} + +/* smtp_unalias_addr - rewrite aliases in domain part of address */ + +VSTRING *smtp_unalias_addr(VSTRING *result, const char *addr) +{ + char *at; + const char *fqdn; + + if ((at = strrchr(addr, '@')) == 0 || at[1] == 0) { + vstring_strcpy(result, addr); + } else { + fqdn = smtp_unalias_name(at + 1); + vstring_strncpy(result, addr, at - addr + 1); + vstring_strcat(result, fqdn); + } + return (result); +} + +#ifdef TEST + + /* + * Test program - read address from stdin, print result on stdout. + */ + +#include + +int main(int unused_argc, char **unused_argv) +{ + VSTRING *addr = vstring_alloc(10); + VSTRING *result = vstring_alloc(10); + + smtp_unalias_flags |= RES_DEBUG; + + while (vstring_fgets_nonl(addr, VSTREAM_IN)) { + smtp_unalias_addr(result, vstring_str(addr)); + vstream_printf("%s -> %s\n", vstring_str(addr), vstring_str(result)); + vstream_fflush(VSTREAM_OUT); + } + vstring_free(addr); + vstring_free(result); +} + +#endif diff --git a/postfix/smtpd/.indent.pro b/postfix/smtpd/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/smtpd/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/smtpd/.printfck b/postfix/smtpd/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/smtpd/.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/smtpd/Makefile.in b/postfix/smtpd/Makefile.in new file mode 100644 index 000000000..a8736d8c8 --- /dev/null +++ b/postfix/smtpd/Makefile.in @@ -0,0 +1,173 @@ +SHELL = /bin/sh +SRCS = smtpd.c smtpd_token.c smtpd_check.c smtpd_chat.c smtpd_state.c +OBJS = smtpd.o smtpd_token.o smtpd_check.o smtpd_chat.o smtpd_state.o +HDRS = smtpd_token.h smtpd_check.h smtpd_chat.h +TESTSRC = smtpd_token_test.c +WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ + -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ + -Wunused +DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) +TESTPROG= smtpd_token smtpd_check +PROG = smtpd +INC_DIR = ../include +LIBS = ../lib/libmaster.a ../lib/libglobal.a ../lib/libdns.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 + +SMTPD_CHECK_OBJ = smtpd_state.o + +smtpd_token: smtpd_token.c $(LIBS) + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIBS) $(SYSLIBS) + +smtpd_check: smtpd_check.c $(SMTPD_CHECK_OBJ) $(LIBS) + $(CC) $(CFLAGS) -DTEST -o $@ smtpd_check.c $(SMTPD_CHECK_OBJ) \ + $(LIBS) $(SYSLIBS) + +printfck: $(OBJS) $(PROG) + rm -rf printfck + mkdir printfck + cp *.h 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 *.db *.out + rm -rf printfck + +tidy: clean + +smtpd_token_test: smtpd_token_test.c smtpd_token.o $(LIBS) + $(CC) $(CFLAGS) -DTEST -o $@ $@.c smtpd_token.o $(LIBS) $(SYSLIBS) + +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' +smtpd.o: smtpd.c +smtpd.o: ../include/sys_defs.h +smtpd.o: ../include/msg.h +smtpd.o: ../include/mymalloc.h +smtpd.o: ../include/vstring.h +smtpd.o: ../include/vbuf.h +smtpd.o: ../include/vstream.h +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/mail_params.h +smtpd.o: ../include/record.h +smtpd.o: ../include/rec_type.h +smtpd.o: ../include/mail_proto.h +smtpd.o: ../include/iostuff.h +smtpd.o: ../include/cleanup_user.h +smtpd.o: ../include/mail_date.h +smtpd.o: ../include/config.h +smtpd.o: ../include/off_cvt.h +smtpd.o: ../include/debug_peer.h +smtpd.o: ../include/mail_error.h +smtpd.o: ../include/name_mask.h +smtpd.o: ../include/mail_flush.h +smtpd.o: ../include/mail_stream.h +smtpd.o: ../include/mail_queue.h +smtpd.o: ../include/mail_server.h +smtpd.o: smtpd_token.h +smtpd.o: smtpd.h +smtpd.o: ../include/argv.h +smtpd.o: smtpd_check.h +smtpd.o: smtpd_chat.h +smtpd_chat.o: smtpd_chat.c +smtpd_chat.o: ../include/sys_defs.h +smtpd_chat.o: ../include/msg.h +smtpd_chat.o: ../include/argv.h +smtpd_chat.o: ../include/vstring.h +smtpd_chat.o: ../include/vbuf.h +smtpd_chat.o: ../include/vstream.h +smtpd_chat.o: ../include/stringops.h +smtpd_chat.o: ../include/line_wrap.h +smtpd_chat.o: ../include/mymalloc.h +smtpd_chat.o: ../include/smtp_stream.h +smtpd_chat.o: ../include/record.h +smtpd_chat.o: ../include/rec_type.h +smtpd_chat.o: ../include/mail_proto.h +smtpd_chat.o: ../include/iostuff.h +smtpd_chat.o: ../include/mail_params.h +smtpd_chat.o: ../include/mail_addr.h +smtpd_chat.o: ../include/post_mail.h +smtpd_chat.o: ../include/cleanup_user.h +smtpd_chat.o: ../include/mail_error.h +smtpd_chat.o: ../include/name_mask.h +smtpd_chat.o: smtpd.h +smtpd_chat.o: ../include/mail_stream.h +smtpd_chat.o: smtpd_chat.h +smtpd_check.o: smtpd_check.c +smtpd_check.o: ../include/sys_defs.h +smtpd_check.o: ../include/msg.h +smtpd_check.o: ../include/vstring.h +smtpd_check.o: ../include/vbuf.h +smtpd_check.o: ../include/split_at.h +smtpd_check.o: ../include/fsspace.h +smtpd_check.o: ../include/stringops.h +smtpd_check.o: ../include/valid_hostname.h +smtpd_check.o: ../include/argv.h +smtpd_check.o: ../include/mymalloc.h +smtpd_check.o: ../include/dict.h +smtpd_check.o: ../include/vstream.h +smtpd_check.o: ../include/dns.h +smtpd_check.o: ../include/namadr_list.h +smtpd_check.o: ../include/domain_list.h +smtpd_check.o: ../include/mail_params.h +smtpd_check.o: ../include/canon_addr.h +smtpd_check.o: ../include/resolve_clnt.h +smtpd_check.o: ../include/mail_error.h +smtpd_check.o: ../include/name_mask.h +smtpd_check.o: ../include/resolve_local.h +smtpd_check.o: ../include/own_inet_addr.h +smtpd_check.o: smtpd.h +smtpd_check.o: ../include/mail_stream.h +smtpd_check.o: smtpd_check.h +smtpd_state.o: smtpd_state.c +smtpd_state.o: ../include/sys_defs.h +smtpd_state.o: ../include/events.h +smtpd_state.o: ../include/mymalloc.h +smtpd_state.o: ../include/vstream.h +smtpd_state.o: ../include/vbuf.h +smtpd_state.o: ../include/name_mask.h +smtpd_state.o: ../include/cleanup_user.h +smtpd_state.o: ../include/mail_params.h +smtpd_state.o: ../include/mail_error.h +smtpd_state.o: smtpd.h +smtpd_state.o: ../include/vstring.h +smtpd_state.o: ../include/argv.h +smtpd_state.o: ../include/mail_stream.h +smtpd_state.o: smtpd_chat.h +smtpd_token.o: smtpd_token.c +smtpd_token.o: ../include/sys_defs.h +smtpd_token.o: ../include/mymalloc.h +smtpd_token.o: ../include/mvect.h +smtpd_token.o: smtpd_token.h +smtpd_token.o: ../include/vstring.h +smtpd_token.o: ../include/vbuf.h diff --git a/postfix/smtpd/smtpd.c b/postfix/smtpd/smtpd.c new file mode 100644 index 000000000..795941e36 --- /dev/null +++ b/postfix/smtpd/smtpd.c @@ -0,0 +1,1120 @@ +/*++ +/* NAME +/* smtpd 8 +/* SUMMARY +/* Postfix SMTP server +/* SYNOPSIS +/* \fBsmtpd\fR [generic Postfix daemon options] +/* DESCRIPTION +/* The SMTP server accepts network connection requests +/* and performs zero or more SMTP transactions per connection. +/* Each received message is piped through the \fBcleanup\fR(8) +/* daemon, and is placed into the \fBincoming\fR queue as one +/* single queue file. For this mode of operation, the program +/* expects to be run from the \fBmaster\fR(8) process manager. +/* +/* Alternatively, the SMTP server takes an established +/* connection on standard input and deposits messages directly +/* into the \fBmaildrop\fR queue. In this so-called stand-alone +/* mode, the SMTP server can accept mail even while the mail +/* system is not running. +/* +/* The SMTP server implements a variety of policies for connection +/* requests, and for parameters given to \fBHELO, MAIL FROM, VRFY\fR +/* and \fBRCPT TO\fR commands. They are detailed below and in the +/* \fBmain.cf\fR configuration file. +/* SECURITY +/* .ad +/* .fi +/* The SMTP server is moderately security-sensitive. It talks to SMTP +/* clients and to DNS servers on the network. The SMTP server can be +/* run chrooted at fixed low privilege. +/* STANDARDS +/* RFC 821 (SMTP protocol) +/* RFC 1123 (Host requirements) +/* RFC 1651 (SMTP service extensions) +/* RFC 1652 (8bit-MIME transport) +/* RFC 1854 (SMTP Pipelining) +/* RFC 1870 (Message Size Declaration) +/* RFC 1985 (ETRN command) (partial) +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8). +/* +/* Depending on the setting of the \fBnotify_classes\fR parameter, +/* the postmaster is notified of bounces, protocol problems, +/* policy violations, and of other trouble. +/* BUGS +/* RFC 1985 is implemented by forcing delivery of all deferred mail. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. Use the \fBpostfix reload\fR command after +/* a configuration change. +/* .SH Miscellaneous +/* .ad +/* .fi +/* .IP \fBcommand_directory\fR +/* Location of Postfix support commands (default: +/* \fB$program_directory\fR). +/* .IP \fBdebug_peer_level\fR +/* Increment in verbose logging level when a remote host matches a +/* pattern in the \fBdebug_peer_list\fR parameter. +/* .IP \fBdebug_peer_list\fR +/* 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 \fBhopcount_limit\fR +/* Limit the number of \fBReceived:\fR message headers. +/* .IP \fBnotify_classes\fR +/* List of error classes. Of special interest are: +/* .RS +/* .IP \fBpolicy\fR +/* When a client violates any policy, mail a transcript of the +/* entire SMTP session to the postmaster. +/* .IP \fBprotocol\fR +/* When a client violates the SMTP protocol or issues an unimplemented +/* command, mail a transcript of the entire SMTP session to the +/* postmaster. +/* .RE +/* .IP \fBsmtpd_banner\fR +/* Text that follows the \fB220\fR status code in the SMTP greeting banner. +/* .IP \fBsmtpd_recipient_limit\fR +/* Restrict the number of recipients that the SMTP server accepts +/* per message delivery. +/* .IP \fBsmtpd_timeout\fR +/* Limit the time to send a server response and to receive a client +/* request. +/* .SH "Resource controls" +/* .ad +/* .fi +/* .IP \fBline_length_limit\fR +/* Limit the amount of memory in bytes used for the handling of +/* partial input lines. +/* .IP \fBmessage_size_limit\fR +/* Limit the total size in bytes of a message, including on-disk +/* storage for envelope information. +/* .IP \fBqueue_minfree\fR +/* Minimal amount of free space in bytes in the queue file system +/* for the SMTP server to accept any mail at all. +/* .SH Tarpitting +/* .ad +/* .fi +/* .IP \fBsmtpd_error_sleep_time\fR +/* Time to wait in seconds before sending a 4xx or 5xx server error +/* response. +/* .IP \fBsmtpd_soft_error_limit\fR +/* When an SMTP client has made this number of errors, wait +/* \fIerror_count\fR seconds before responding to any client request. +/* .IP \fBsmtpd_hard_error_limit\fR +/* Disconnect after a client has made this number of errors. +/* .SH "UCE control restrictions" +/* .ad +/* .fi +/* .IP \fBsmtpd_client_restrictions\fR +/* Restrict what clients may connect to this mail system. +/* .IP \fBsmtpd_helo_required\fR +/* Require that clients introduce themselves at the beginning +/* of an SMTP session. +/* .IP \fBsmtpd_helo_restrictions\fR +/* Restrict what client hostnames are allowed in \fBHELO\fR and +/* \fBEHLO\fR commands. +/* .IP \fBsmtpd_sender_restrictions\fR +/* Restrict what sender addresses are allowed in \fBMAIL FROM\fR commands. +/* .IP \fBsmtpd_recipient_restrictions\fR +/* Restrict what recipient addresses are allowed in \fBRCPT TO\fR commands. +/* .IP \fBmaps_rbl_domains\fR +/* List of DNS domains that publish the addresses of blacklisted +/* hosts. +/* .IP \fBrelay_domains\fR +/* Restrict what domains or networks this mail system will relay +/* mail from or to. +/* .SH "UCE control responses" +/* .ad +/* .fi +/* .IP \fBaccess_map_reject_code\fR +/* Server response when a client violates an access database restriction. +/* .IP \fBinvalid_hostname_reject_code\fR +/* Server response when a client violates the \fBreject_invalid_hostname\fR +/* restriction. +/* .IP \fBmaps_rbl_reject_code\fR +/* Server response when a client violates the \fBmaps_rbl_domains\fR +/* restriction. +/* .IP \fBreject_code\fR +/* Response code when the client matches a \fBreject\fR restriction. +/* .IP \fBrelay_domains_reject_code\fR +/* Server response when a client attempts to violate the mail relay +/* policy. +/* .IP \fBunknown_address_reject_code\fR +/* Server response when a client violates the \fBreject_unknown_address\fR +/* restriction. +/* .IP \fBunknown_client_reject_code\fR +/* Server response when a client without address to name mapping +/* violates the \fBreject_unknown_clients\fR restriction. +/* .IP \fBunknown_hostname_reject_code\fR +/* Server response when a client violates the \fBreject_unknown_hostname\fR +/* restriction. +/* SEE ALSO +/* cleanup(8) message canonicalization +/* master(8) process manager +/* syslogd(8) system logging +/* 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 +#include /* remove() */ +#include +#include +#include +#include +#include +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Single-threaded server skeleton. */ + +#include + +/* Application-specific */ + +#include "smtpd_token.h" +#include "smtpd.h" +#include "smtpd_check.h" +#include "smtpd_chat.h" + + /* + * Tunable parameters. Make sure that there is some bound on the length of + * an SMTP command, so that the mail system stays in control even when a + * malicious client sends commands of unreasonable length (qmail-dos-1). + * Make sure there is some bound on the number of recipients, so that the + * mail system stays in control even when a malicious client sends an + * unreasonable number of recipients (qmail-dos-2). + */ +int var_smtpd_rcpt_limit; +int var_smtpd_tmout; +char *var_relay_domains; +int var_smtpd_soft_erlim; +int var_smtpd_hard_erlim; +int var_queue_minfree; /* XXX use off_t */ +char *var_smtpd_banner; +char *var_debug_peer_list; +int var_debug_peer_level; +char *var_notify_classes; +char *var_client_checks; +char *var_helo_checks; +char *var_mail_checks; +char *var_rcpt_checks; +int var_unk_client_code; +int var_bad_name_code; +int var_unk_name_code; +int var_unk_addr_code; +int var_relay_code; +int var_maps_rbl_code; +int var_access_map_code; +char *var_maps_rbl_domains; +int var_helo_required; +int var_reject_code; +int var_smtpd_err_sleep; + + /* + * Global state, for stand-alone mode queue file cleanup. When this is + * non-null at cleanup time, the named file is removed. + */ +char *smtpd_path; + +/* helo_cmd - process HELO command */ + +static int helo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) +{ + char *err; + + if (argc != 2) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "501 Syntax: HELO hostname"); + return (-1); + } + if (state->helo_name != 0) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "503 Duplicate HELO/EHLO"); + return (-1); + } + if (SMTPD_STAND_ALONE(state) == 0 + && (err = smtpd_check_helo(state, argv[1].strval)) != 0) { + smtpd_chat_reply(state, "%s", err); + return (-1); + } + state->helo_name = mystrdup(printable(argv[1].strval, '?')); + state->protocol = "SMTP"; + smtpd_chat_reply(state, "250 %s", var_myhostname); + return (0); +} + +/* ehlo_cmd - process EHLO command */ + +static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) +{ + char *err; + + if (argc != 2) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "501 Syntax: EHLO hostname"); + return (-1); + } + if (state->helo_name != 0) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "503 Error: duplicate HELO/EHLO"); + return (-1); + } + if (SMTPD_STAND_ALONE(state) == 0 + && (err = smtpd_check_helo(state, argv[1].strval)) != 0) { + smtpd_chat_reply(state, "%s", err); + return (-1); + } + state->helo_name = mystrdup(printable(argv[1].strval, '?')); + state->protocol = "ESMTP"; + smtpd_chat_reply(state, "250-%s", var_myhostname); + smtpd_chat_reply(state, "250-PIPELINING"); + if (var_message_limit) + smtpd_chat_reply(state, "250-SIZE %lu", + (unsigned long) var_message_limit); /* XXX */ + else + smtpd_chat_reply(state, "250-SIZE"); + smtpd_chat_reply(state, "250-ETRN"); + smtpd_chat_reply(state, "250 8BITMIME"); + return (0); +} + +/* helo_reset - reset HELO/EHLO command stuff */ + +static void helo_reset(SMTPD_STATE *state) +{ + if (state->helo_name) + myfree(state->helo_name); + state->helo_name = 0; +} + +/* mail_open_stream - open mail destination */ + +static void mail_open_stream(SMTPD_STATE *state) +{ + char *postdrop_command; + + /* + * If running from the master or from inetd, connect to the cleanup + * service. + */ + if (SMTPD_STAND_ALONE(state) == 0) { + state->dest = mail_stream_service(MAIL_CLASS_PRIVATE, + MAIL_SERVICE_CLEANUP); + if (state->dest == 0 + || mail_print(state->dest->stream, "%d", CLEANUP_FLAG_NONE) != 0) + msg_fatal("unable to connect to the %s %s service", + MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP); + } + + /* + * Otherwise, if the maildrop is writable, create a maildrop file. + * Arrange for pickup service notification. Make a copy of the pathname + * so that the file can be deleted in case of a fatal run-time error. + */ + else if (access(MAIL_QUEUE_MAILDROP, W_OK) == 0) { + state->dest = mail_stream_file(MAIL_QUEUE_MAILDROP, + MAIL_CLASS_PUBLIC, MAIL_SERVICE_PICKUP); + smtpd_path = mystrdup(VSTREAM_PATH(state->dest->stream)); + } + + /* + * Otherwise, pipe the message through the privileged postdrop helper. + * XXX Make postdrop a manifest constant. + */ + else { + postdrop_command = concatenate(var_command_dir, "/postdrop", + msg_verbose ? " -v" : (char *) 0, (char *) 0); + state->dest = mail_stream_command(postdrop_command); + if (state->dest == 0) + msg_fatal("unable to execute %s", postdrop_command); + myfree(postdrop_command); + } + state->cleanup = state->dest->stream; + state->queue_id = mystrdup(state->dest->id); +} + +/* mail_cmd - process MAIL command */ + +static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) +{ + char *err; + int narg; + off_t size = 0; + char *arg; + + /* + * Sanity checks. XXX Ignore bad SIZE= values until we can reliably and + * portably detect overflows while converting from string to off_t. + */ + if (var_helo_required && state->helo_name == 0) { + state->error_mask |= MAIL_ERROR_POLICY; + smtpd_chat_reply(state, "503 Error: send HELO/EHLO first"); + return (-1); + } + if (state->cleanup != 0) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "503 Error: nested MAIL command"); + return (-1); + } + if (argc < 4 + || strcasecmp(argv[1].strval, "from") != 0 + || strcmp(argv[2].strval, ":") != 0) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "501 Syntax: MAIL FROM:
    "); + return (-1); + } + for (narg = 4; narg < argc; narg++) { + arg = argv[narg].strval; + if (strcasecmp(arg, "BODY=8BITMIME") == 0 + || strcasecmp(arg, "BODY=7BIT") == 0) { + /* void */ ; + } else if (strncasecmp(arg, "SIZE=", 5) == 0) { + if ((size = off_cvt_string(arg + 5)) < 0) + size = 0; + } else { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "555 Unsupported option: %s", arg); + return (-1); + } + } + if (SMTPD_STAND_ALONE(state) == 0 + && (err = smtpd_check_mail(state, argv[3].strval)) != 0) { + smtpd_chat_reply(state, "%s", err); + return (-1); + } + if ((err = smtpd_check_size(state, size)) != 0) { + smtpd_chat_reply(state, "%s", err); + return (-1); + } + + /* + * Open queue file or IPC stream. + */ + mail_open_stream(state); + msg_info("%s: client=%s[%s]", state->queue_id, state->name, state->addr); + + /* + * Record the time of arrival and the sender envelope address. + */ + rec_fprintf(state->cleanup, REC_TYPE_TIME, "%ld", + (long) time((time_t *) 0)); + rec_fputs(state->cleanup, REC_TYPE_FROM, argv[3].strval); + state->sender = mystrdup(argv[3].strval); + smtpd_chat_reply(state, "250 Ok"); + return (0); +} + +/* mail_reset - reset MAIL command stuff */ + +static void mail_reset(SMTPD_STATE *state) +{ + + /* + * Unceremoniously close the pipe to the cleanup service. The cleanup + * service will delete the queue file when it detects a premature + * end-of-file condition on input. + */ + if (state->cleanup != 0) { + mail_stream_cleanup(state->dest); + state->dest = 0; + state->cleanup = 0; + } + if (state->queue_id != 0) { + myfree(state->queue_id); + state->queue_id = 0; + } + if (smtpd_path) { + if (remove(smtpd_path)) + msg_warn("remove %s: %m", smtpd_path); + else if (msg_verbose) + msg_info("remove %s", smtpd_path); + myfree(smtpd_path); + smtpd_path = 0; + } + if (state->sender) { + myfree(state->sender); + state->sender = 0; + } +} + +/* rcpt_cmd - process RCPT TO command */ + +static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) +{ + char *err; + + /* + * Sanity checks. + */ + if (state->cleanup == 0) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "503 Error: need MAIL command"); + return (-1); + } + if (argc != 4 + || strcasecmp(argv[1].strval, "to") != 0 + || strcmp(argv[2].strval, ":") != 0) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "501 Syntax: RCPT TO:
    "); + return (-1); + } + if (var_smtpd_rcpt_limit && state->rcpt_count >= var_smtpd_rcpt_limit) { + state->error_mask |= MAIL_ERROR_POLICY; + smtpd_chat_reply(state, "452 Error: too many recipients"); + return (-1); + } + if (SMTPD_STAND_ALONE(state) == 0 + && (err = smtpd_check_rcpt(state, argv[3].strval)) != 0) { + smtpd_chat_reply(state, "%s", err); + return (-1); + } + + /* + * Store the recipient. Remember the first one. + */ + state->rcpt_count++; + if (state->recipient == 0) + state->recipient = mystrdup(argv[3].strval); + rec_fputs(state->cleanup, REC_TYPE_RCPT, argv[3].strval); + smtpd_chat_reply(state, "250 Ok"); + return (0); +} + +/* rcpt_reset - reset RCPT stuff */ + +static void rcpt_reset(SMTPD_STATE *state) +{ + if (state->recipient) { + myfree(state->recipient); + state->recipient = 0; + } + state->rcpt_count = 0; +} + +/* data_cmd - process DATA command */ + +static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) +{ + char *start; + int len; + int curr_rec_type; + int prev_rec_type; + int first = 1; + + /* + * Sanity checks. + */ + if (state->rcpt_count == 0) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "503 Error: need RCPT command"); + return (-1); + } + if (argc != 1) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "501 Syntax: DATA"); + return (-1); + } + + /* + * Terminate the message envelope segment. Start the message content + * segment, and prepend our own Received: header. If there is only one + * recipient, list the recipient address. + */ + rec_fputs(state->cleanup, REC_TYPE_MESG, ""); + rec_fprintf(state->cleanup, REC_TYPE_NORM, + "Received: from %s (%s [%s])", + state->helo_name ? state->helo_name : state->name, + state->name, state->addr); + if (state->rcpt_count == 1 && state->recipient) { + rec_fprintf(state->cleanup, REC_TYPE_NORM, + "\tby %s (%s) with %s id %s", + var_myhostname, var_mail_name, + state->protocol, state->queue_id); + rec_fprintf(state->cleanup, REC_TYPE_NORM, + "\tfor <%s>; %s", state->recipient, mail_date(state->time)); + } else { + rec_fprintf(state->cleanup, REC_TYPE_NORM, + "\tby %s (%s) with %s", + var_myhostname, var_mail_name, state->protocol); + rec_fprintf(state->cleanup, REC_TYPE_NORM, + "\tid %s; %s", state->queue_id, mail_date(state->time)); + } + smtpd_chat_reply(state, "354 End data with ."); + + /* + * Copy the message content. If the cleanup process has a problem, keep + * reading until the remote stops sending, then complain. Read typed + * records from the SMTP stream so we can handle data that spans buffers. + * + * XXX Force an empty record when the queue file content begins with + * whitespace, so that it won't be considered as being part of our own + * Received: header. What an ugly Kluge. + */ + if (vstream_ferror(state->cleanup)) + state->err = CLEANUP_STAT_WRITE; + + for (prev_rec_type = 0; /* void */ ; prev_rec_type = curr_rec_type) { + if (smtp_get(state->buffer, state->client, var_line_limit) == '\n') + curr_rec_type = REC_TYPE_NORM; + else + curr_rec_type = REC_TYPE_CONT; + start = vstring_str(state->buffer); + len = VSTRING_LEN(state->buffer); + if (first) { + first = 0; + if (len > 0 && ISSPACE(start[0])) + rec_put(state->cleanup, REC_TYPE_NORM, "", 0); + } + if (prev_rec_type != REC_TYPE_CONT + && *start == '.' && (++start, --len) == 0) + break; + if (state->err == CLEANUP_STAT_OK + && rec_put(state->cleanup, curr_rec_type, start, len) < 0) + state->err = CLEANUP_STAT_WRITE; + } + + /* + * Send the end-of-segment markers. + */ + if (state->err == CLEANUP_STAT_OK) + if (rec_fputs(state->cleanup, REC_TYPE_XTRA, "") < 0 + || rec_fputs(state->cleanup, REC_TYPE_END, "") < 0 + || vstream_fflush(state->cleanup)) + state->err = CLEANUP_STAT_WRITE; + + /* + * Finish the queue file or finish the cleanup conversation. + */ + if (state->err == 0) + state->err |= mail_stream_finish(state->dest); + else + mail_stream_cleanup(state->dest); + state->dest = 0; + state->cleanup = 0; + + /* + * Delete the queue file or disable delete on fatal error or interrupt. + */ + if (smtpd_path) { + if (state->err != 0) { + if (remove(smtpd_path)) + msg_warn("remove %s: %m", smtpd_path); + else if (msg_verbose) + msg_info("remove %s", smtpd_path); + } + myfree(smtpd_path); + smtpd_path = 0; + } + + /* + * Handle any errors. One message may suffer from multiple errors, so + * complain only about the most severe error. Forgive any previous client + * errors when a message was received successfully. + */ + if (state->err == CLEANUP_STAT_OK) { + state->error_count = 0; + state->error_mask = 0; + smtpd_chat_reply(state, "250 Ok: queued as %s", state->queue_id); + } else if ((state->err & CLEANUP_STAT_BAD) != 0) { + state->error_mask |= MAIL_ERROR_SOFTWARE; + smtpd_chat_reply(state, "451 Error: internal error %d", state->err); + } else if ((state->err & CLEANUP_STAT_RCPT) != 0) { + state->error_mask |= MAIL_ERROR_SOFTWARE; + smtpd_chat_reply(state, "451 Error: internal error %d", state->err); + } else if ((state->err & CLEANUP_STAT_SIZE) != 0) { + state->error_mask |= MAIL_ERROR_BOUNCE; + smtpd_chat_reply(state, "552 Error: message too large"); + } else if ((state->err & CLEANUP_STAT_HOPS) != 0) { + state->error_mask |= MAIL_ERROR_BOUNCE; + smtpd_chat_reply(state, "554 Error: too many hops"); + } else if ((state->err & CLEANUP_STAT_WRITE) != 0) { + state->error_mask |= MAIL_ERROR_RESOURCE; + smtpd_chat_reply(state, "451 Error: queue file write error"); + } else { + msg_panic("data_cmd: unknown status %d", state->err); + } + + /* + * Disconnect after transmission must not be treated as "lost connection + * after DATA". + */ + state->where = SMTPD_AFTER_DOT; + + /* + * Cleanup. The client may send another MAIL command. + */ + mail_reset(state); + rcpt_reset(state); + return (state->err); +} + +/* rset_cmd - process RSET */ + +static int rset_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) +{ + + /* + * Sanity checks. + */ + if (argc != 1) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "501 Syntax: RSET"); + return (-1); + } + + /* + * Restore state to right after HELO/EHLO command. + */ + mail_reset(state); + rcpt_reset(state); + smtpd_chat_reply(state, "250 Ok"); + return (0); +} + +/* noop_cmd - process NOOP */ + +static int noop_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) +{ + + /* + * Sanity checks. + */ + if (argc != 1) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "501 Syntax: NOOP"); + return (-1); + } + smtpd_chat_reply(state, "250 Ok"); + return (0); +} + +/* vrfy_cmd - process VRFY */ + +static int vrfy_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) +{ + VSTRING *buf; + char *err = 0; + int i; + + /* + * The SMTP standard (RFC 821) disallows unquoted special characters in + * the VRFY argument. Common practice violates the standard, however. + * Postfix accomodates common practice where it violates the standard. + */ + if (argc < 2) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "501 Syntax: VRFY address"); + return (-1); + } + + /* + * Yuck. All input is tokenized. Now put it back together again. + */ + buf = vstring_alloc(100); + for (i = 1; i < argc; i++) { + vstring_strcat(buf, " "); + vstring_strcat(buf, argv[i].strval); + } + if (SMTPD_STAND_ALONE(state) == 0) + err = smtpd_check_rcpt(state, vstring_str(buf)); + vstring_free(buf); + + /* + * End untokenize. + */ + if (err != 0) { + smtpd_chat_reply(state, "%s", err); + return (-1); + } + smtpd_chat_reply(state, "252 Send mail to find out"); + return (0); +} + +/* etrn_cmd - process ETRN command */ + +static int etrn_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) +{ + + /* + * Sanity checks. + */ + if (argc != 2) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "501 Syntax: ETRN domain"); + return (-1); + } + + /* + * XXX The preliminary implementation causes a full deferred queue scan. + */ + if (mail_flush_site(argv[1].strval) < 0) + smtpd_chat_reply(state, "458 Unable to queue messages"); + else + smtpd_chat_reply(state, "250 Queuing started"); + return (0); +} + +/* quit_cmd - process QUIT command */ + +static int quit_cmd(SMTPD_STATE *state, int unused_argc, SMTPD_TOKEN *unused_argv) +{ + + /* + * Don't bother checking the syntax. + */ + smtpd_chat_reply(state, "221 Bye"); + return (0); +} + + /* + * The table of all SMTP commands that we know. + */ +typedef struct SMTPD_CMD { + char *name; + int (*action) (SMTPD_STATE *, int, SMTPD_TOKEN *); +} SMTPD_CMD; + +static SMTPD_CMD smtpd_cmd_table[] = { + "HELO", helo_cmd, + "EHLO", ehlo_cmd, + "MAIL", mail_cmd, + "RCPT", rcpt_cmd, + "DATA", data_cmd, + "RSET", rset_cmd, + "NOOP", noop_cmd, + "VRFY", vrfy_cmd, + "ETRN", etrn_cmd, + "QUIT", quit_cmd, + 0, +}; + +/* smtpd_proto - talk the SMTP protocol */ + +static void smtpd_proto(SMTPD_STATE *state) +{ + int argc; + SMTPD_TOKEN *argv; + SMTPD_CMD *cmdp; + + /* + * Print a greeting banner and run the state machine. Read SMTP commands + * one line at a time. According to the standard, a sender or recipient + * address could contain an escaped newline. I think this is perverse, + * and anyone depending on this is really asking for trouble. + * + * In case of mail protocol trouble, the program jumps back to this place, + * so that it can perform the necessary cleanup before talking to the + * next client. The setjmp/longjmp primitives are like a sharp tool: use + * with care. I would certainly recommend against the use of + * setjmp/longjmp in programs that change privilege levels. + * + * In case of file system trouble the program terminates after logging the + * error and after informing the client. In all other cases (out of + * memory, panic) the error is logged, and the msg_cleanup() exit handler + * cleans up, but no attempt is made to inform the client of the nature + * of the problem. + */ + smtp_timeout_setup(state->client, var_smtpd_tmout); + + switch (setjmp(smtp_timeout_buf)) { + + default: + msg_panic("smtpd_proto: unknown error reading from %s[%s]", + state->name, state->addr); + break; + + case SMTP_ERR_TIME: + state->reason = "timeout"; + smtpd_chat_reply(state, "421 Error: timeout exceeded"); + break; + + case SMTP_ERR_EOF: + state->reason = "lost connection"; + break; + + case 0: + for (;;) { + if (state->error_count > var_smtpd_hard_erlim) { + state->reason = "too many errors"; + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "421 Error: too many errors"); + break; + } + smtpd_chat_query(state); + if ((argc = smtpd_token(vstring_str(state->buffer), &argv)) == 0) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "500 Error: bad syntax"); + state->error_count++; + continue; + } + for (cmdp = smtpd_cmd_table; cmdp->name != 0; cmdp++) + if (strcasecmp(argv[0].strval, cmdp->name) == 0) + break; + if (cmdp->name == 0) { + smtpd_chat_reply(state, "502 Error: command not implemented"); + state->error_mask |= MAIL_ERROR_PROTOCOL; + state->error_count++; + continue; + } + if (state->access_denied && cmdp->action != quit_cmd) { + smtpd_chat_reply(state, "%s", state->access_denied); + state->error_count++; + continue; + } + state->where = cmdp->name; + if (cmdp->action(state, argc, argv) != 0) + state->error_count++; + + if (cmdp->action == quit_cmd) + break; + } + break; + } + + /* + * Log abnormal session termination, in case postmaster notification has + * been turned off. In the log, indicate the last recognized state before + * things went wrong. Don't complain about clients that go away without + * sending QUIT. + */ + if (state->reason && state->where && strcmp(state->where, SMTPD_AFTER_DOT)) + msg_info("%s after %s from %s[%s]", + state->reason, state->where, state->name, state->addr); + + /* + * Notify the postmaster if there were errors but no message was + * collected. This usually indicates a client configuration problem, or + * that someone is trying nasty things. Either is significant enough to + * bother the postmaster. XXX Can't report problems when running in + * stand-alone mode: postmaster notices require availability of the + * cleanup service. + */ + if (state->history != 0 && state->client != VSTREAM_IN + && (state->error_mask & state->notify_mask)) + smtpd_chat_notify(state); + + /* + * Cleanup whatever information the client gave us during the SMTP + * dialog. + */ + helo_reset(state); + mail_reset(state); + rcpt_reset(state); + smtpd_chat_reset(state); +} + +/* smtpd_service - service one client */ + +static void smtpd_service(VSTREAM *stream, char *unused_service, char **argv) +{ + SMTPD_STATE state; + PEER_NAME *peer; + + /* + * Sanity check. This service takes no command-line arguments. + */ + if (argv[0]) + msg_fatal("unexpected command-line argument: %s", argv[0]); + + /* + * This routine runs when a client has connected to our network port, or + * when the smtp server is run in stand-alone mode (input from pipe). + * + * Look up and sanitize the peer name, then initialize some connection- + * specific state. When the name service is hosed, hostname lookup will + * take a while. This is why I always run a local name server on critical + * machines. + */ + peer = peer_name(vstream_fileno(stream)); + smtpd_state_init(&state, stream, peer->name, peer->addr); + + /* + * See if we need to turn on verbose logging for this client. + */ + debug_peer_check(state.name, state.addr); + + /* + * See if we want to talk to this client at all. Then, log the connection + * event. + */ + if ((state.access_denied = smtpd_check_client(&state)) != 0) { + smtpd_chat_reply(&state, "%s", state.access_denied); + } else { + smtpd_chat_reply(&state, "220 %s", var_smtpd_banner); + msg_info("connect from %s[%s]", state.name, state.addr); + } + + /* + * Provide the SMTP service. + */ + smtpd_proto(&state); + + /* + * After the client has gone away, clean up whatever we have set up at + * connection time. + */ + msg_info("disconnect from %s[%s]", state.name, state.addr); + smtpd_state_reset(&state); + debug_peer_restore(); +} + +/* smtpd_cleanup - stand-alone mode queue file cleanup */ + +static void smtpd_cleanup(void) +{ + char *myname = "smtpd_cleanup"; + + /* + * This routine is called by the run-time error handler, right before + * program exit. + */ + if (smtpd_path) { + if (remove(smtpd_path)) + msg_warn("%s: remove %s: %m", myname, smtpd_path); + else if (msg_verbose) + msg_info("%s: remove %s", myname, smtpd_path); + smtpd_path = 0; + } +} + +/* smtpd_sig - signal handler */ + +static void smtpd_sig(int sig) +{ + smtpd_cleanup(); + exit(sig); +} + +/* post_jail_init - post-jail initialization */ + +static void post_jail_init(void) +{ + + /* + * Set up signal handlers so that we clean up in stand-alone mode. + */ + signal(SIGHUP, smtpd_sig); + signal(SIGINT, smtpd_sig); + signal(SIGQUIT, smtpd_sig); + signal(SIGTERM, smtpd_sig); +} + +/* pre_jail_init - pre-jail initialization */ + +static void pre_jail_init(void) +{ + + /* + * Initialize blacklist/etc. patterns before entering the chroot jail, in + * case they specify a filename pattern. + */ + smtpd_check_init(); + debug_peer_init(); + msg_cleanup(smtpd_cleanup); +} + +/* main - the main program */ + +int main(int argc, char **argv) +{ + static CONFIG_INT_TABLE int_table[] = { + VAR_SMTPD_RCPT_LIMIT, DEF_SMTPD_RCPT_LIMIT, &var_smtpd_rcpt_limit, 1, 0, + VAR_SMTPD_TMOUT, DEF_SMTPD_TMOUT, &var_smtpd_tmout, 1, 0, + VAR_SMTPD_SOFT_ERLIM, DEF_SMTPD_SOFT_ERLIM, &var_smtpd_soft_erlim, 1, 0, + VAR_SMTPD_HARD_ERLIM, DEF_SMTPD_HARD_ERLIM, &var_smtpd_hard_erlim, 1, 0, + VAR_QUEUE_MINFREE, DEF_QUEUE_MINFREE, &var_queue_minfree, 0, 0, + VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0, + VAR_UNK_CLIENT_CODE, DEF_UNK_CLIENT_CODE, &var_unk_client_code, 0, 0, + VAR_BAD_NAME_CODE, DEF_BAD_NAME_CODE, &var_bad_name_code, 0, 0, + VAR_UNK_NAME_CODE, DEF_UNK_NAME_CODE, &var_unk_name_code, 0, 0, + VAR_UNK_ADDR_CODE, DEF_UNK_ADDR_CODE, &var_unk_addr_code, 0, 0, + VAR_RELAY_CODE, DEF_RELAY_CODE, &var_relay_code, 0, 0, + VAR_MAPS_RBL_CODE, DEF_MAPS_RBL_CODE, &var_maps_rbl_code, 0, 0, + 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, + 0, + }; + static CONFIG_BOOL_TABLE bool_table[] = { + VAR_HELO_REQUIRED, DEF_HELO_REQUIRED, &var_helo_required, + 0, + }; + static CONFIG_STR_TABLE str_table[] = { + VAR_RELAY_DOMAINS, DEF_RELAY_DOMAINS, &var_relay_domains, 0, 0, + VAR_SMTPD_BANNER, DEF_SMTPD_BANNER, &var_smtpd_banner, 1, 0, + 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_CLIENT_CHECKS, DEF_CLIENT_CHECKS, &var_client_checks, 0, 0, + VAR_HELO_CHECKS, DEF_HELO_CHECKS, &var_helo_checks, 0, 0, + VAR_MAIL_CHECKS, DEF_MAIL_CHECKS, &var_mail_checks, 0, 0, + VAR_RCPT_CHECKS, DEF_RCPT_CHECKS, &var_rcpt_checks, 0, 0, + VAR_MAPS_RBL_DOMAINS, DEF_MAPS_RBL_DOMAINS, &var_maps_rbl_domains, 0, 0, + 0, + }; + + /* + * Pass control to the single-threaded service skeleton. + */ + single_server_main(argc, argv, smtpd_service, + MAIL_SERVER_INT_TABLE, int_table, + MAIL_SERVER_STR_TABLE, str_table, + MAIL_SERVER_BOOL_TABLE, bool_table, + MAIL_SERVER_PRE_INIT, pre_jail_init, + MAIL_SERVER_POST_INIT, post_jail_init, + 0); +} diff --git a/postfix/smtpd/smtpd.h b/postfix/smtpd/smtpd.h new file mode 100644 index 000000000..f7c30615b --- /dev/null +++ b/postfix/smtpd/smtpd.h @@ -0,0 +1,80 @@ +/*++ +/* NAME +/* smtpd 3h +/* SUMMARY +/* smtp server +/* SYNOPSIS +/* include "smtpd.h" +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include +#include + + /* + * Global library. + */ +#include + + /* + * Variables that keep track of conversation state. There is only one SMTP + * conversation at a time, so the state variables can be made global. And + * some of this has to be global anyway, so that the run-time error handler + * can clean up in case of a fatal error deep down in some library routine. + */ +typedef struct SMTPD_STATE { + int err; + VSTREAM *client; + VSTRING *buffer; + time_t time; + char *name; + char *addr; + int error_count; + int error_mask; + int notify_mask; + char *helo_name; + char *queue_id; + VSTREAM *cleanup; + MAIL_STREAM *dest; + int rcpt_count; + char *access_denied; + ARGV *history; + char *reason; + char *sender; + char *recipient; + char *protocol; + char *where; +} SMTPD_STATE; + +extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *, + const char *, const char *); +extern void smtpd_state_reset(SMTPD_STATE *); + + /* + * Conversation stages. This is used for "lost connection after XXX" + * diagnostics. + */ +#define SMTPD_AFTER_CONNECT "CONNECT" +#define SMTPD_AFTER_DOT "END-OF-MESSAGE" + + /* + * If running in stand-alone mode, do not try to talk to Postfix daemons but + * write to queue file instead. + */ +#define SMTPD_STAND_ALONE(state) \ + (state->client == VSTREAM_IN && getuid() != var_owner_uid) + +/* 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 +/*--*/ diff --git a/postfix/smtpd/smtpd_chat.c b/postfix/smtpd/smtpd_chat.c new file mode 100644 index 000000000..6e97b4c94 --- /dev/null +++ b/postfix/smtpd/smtpd_chat.c @@ -0,0 +1,220 @@ +/*++ +/* NAME +/* smtpd_chat 3 +/* SUMMARY +/* SMTP server request/response support +/* SYNOPSIS +/* #include +/* #include +/* +/* void smtpd_chat_query(state) +/* SMTPD_STATE *state; +/* +/* void smtpd_chat_reply(state, format, ...) +/* SMTPD_STATE *state; +/* char *format; +/* +/* void smtpd_chat_notify(state) +/* SMTPD_STATE *state; +/* +/* void smtpd_chat_reset(state) +/* SMTPD_STATE *state; +/* DESCRIPTION +/* This module implements SMTP server support for request/reply +/* conversations, and maintains a limited SMTP transaction log. +/* +/* smtpd_chat_query() receives a client request and appends a copy +/* to the SMTP transaction log. +/* +/* smtpd_chat_reply() formats a server reply, sends it to the +/* client, and appends a copy to the SMTP transaction log. +/* +/* smtpd_chat_notify() sends a copy of the SMTP transaction log +/* to the postmaster for review. The postmaster notice is sent only +/* when delivery is possible immediately. It is an error to call +/* smtpd_chat_notify() when no SMTP transaction log exists. +/* +/* smtpd_chat_reset() resets the transaction log. This is +/* typically done at the beginning of an SMTP session, or +/* within a session to discard non-error information. +/* DIAGNOSTICS +/* Panic: interface violations. Fatal errors: out of memory. +/* internal protocol errors. +/* 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 /* 44BSD stdarg.h uses abort() */ +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "smtpd.h" +#include "smtpd_chat.h" + +#define STR vstring_str +#define LEN VSTRING_LEN + +/* smtp_chat_reset - reset SMTP transaction log */ + +void smtpd_chat_reset(SMTPD_STATE *state) +{ + if (state->history) { + argv_free(state->history); + state->history = 0; + } +} + +/* smtp_chat_append - append record to SMTP transaction log */ + +static void smtp_chat_append(SMTPD_STATE *state, char *direction) +{ + char *line; + + if (state->history == 0) + state->history = argv_alloc(10); + line = concatenate(direction, STR(state->buffer), (char *) 0); + argv_add(state->history, line, (char *) 0); + myfree(line); +} + +/* smtpd_chat_query - receive and record an SMTP request */ + +void smtpd_chat_query(SMTPD_STATE *state) +{ + int last_char; + + last_char = smtp_get(state->buffer, state->client, var_line_limit); + smtp_chat_append(state, "In: "); + if (last_char != '\n') + msg_warn("%s[%s]: request longer than %d: %.30s...", + state->name, state->addr, var_line_limit, + printable(STR(state->buffer), '?')); + + if (msg_verbose) + msg_info("< %s[%s]: %s", state->name, state->addr, STR(state->buffer)); +} + +/* smtpd_chat_reply - format, send and record an SMTP response */ + +void smtpd_chat_reply(SMTPD_STATE *state, char *format,...) +{ + va_list ap; + + va_start(ap, format); + vstring_vsprintf(state->buffer, format, ap); + va_end(ap); + smtp_chat_append(state, "Out: "); + + if (msg_verbose) + msg_info("> %s[%s]: %s", state->name, state->addr, STR(state->buffer)); + + /* + * Slow down clients that make errors. Sleep-on-error slows down clients + * that abort the connection and go into a connect-error-disconnect loop; + * sleep-on-anything slows down clients that make an excessive number of + * errors within a session. + */ + if (state->error_count > var_smtpd_soft_erlim) + sleep(state->error_count); + else if (STR(state->buffer)[0] == '4' || STR(state->buffer)[0] == '5') + sleep(var_smtpd_err_sleep); + + smtp_fputs(STR(state->buffer), LEN(state->buffer), state->client); +} + +/* print_line - line_wrap callback */ + +static void print_line(const char *str, int len, int indent, char *context) +{ + VSTREAM *notice = (VSTREAM *) context; + + post_mail_fprintf(notice, " %*s%.*s", indent, "", len, str); +} + +/* smtpd_chat_notify - notify postmaster */ + +void smtpd_chat_notify(SMTPD_STATE *state) +{ + char *myname = "smtpd_chat_notify"; + VSTREAM *notice; + char **cpp; + + /* + * Sanity checks. + */ + if (state->history == 0) + msg_panic("%s: no conversation history", myname); + if (msg_verbose) + msg_info("%s: notify postmaster", myname); + + /* + * Construct a message for the postmaster, explaining what this is all + * about. This is junk mail: don't send it when the mail posting service + * is unavailable, and use the double bounce sender address to prevent + * mail bounce wars. Always prepend one space to message content that we + * generate from untrusted data. + */ +#define NULL_CLEANUP_FLAGS 0 +#define LENGTH 78 +#define INDENT 4 + + notice = post_mail_fopen_nowait(mail_addr_double_bounce(), + mail_addr_postmaster(), + NULL_CLEANUP_FLAGS, "NOTICE"); + if (notice == 0) { + msg_warn("postmaster notify: %m"); + return; + } + post_mail_fprintf(notice, "From: %s (Mail Delivery System)", + mail_addr_mail_daemon()); + post_mail_fprintf(notice, "To: %s (Postmaster)", mail_addr_postmaster()); + post_mail_fprintf(notice, "Subject: %s SMTP server: errors from %s[%s]", + var_mail_name, state->name, state->addr); + post_mail_fputs(notice, ""); + post_mail_fputs(notice, "Transcript of session follows."); + post_mail_fputs(notice, ""); + argv_terminate(state->history); + for (cpp = state->history->argv; *cpp; cpp++) + line_wrap(printable(*cpp, '?'), LENGTH, INDENT, print_line, + (char *) notice); + post_mail_fputs(notice, ""); + if (state->reason) + post_mail_fprintf(notice, "Session aborted, reason: %s", state->reason); + else + post_mail_fputs(notice, "No message was collected successfully."); + (void) post_mail_fclose(notice); +} diff --git a/postfix/smtpd/smtpd_chat.h b/postfix/smtpd/smtpd_chat.h new file mode 100644 index 000000000..a5638b4d7 --- /dev/null +++ b/postfix/smtpd/smtpd_chat.h @@ -0,0 +1,30 @@ +/*++ +/* NAME +/* smtpd_chat 3h +/* SUMMARY +/* SMTP server request/response support +/* SYNOPSIS +/* #include +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern void smtpd_chat_reset(SMTPD_STATE *); +extern void smtpd_chat_query(SMTPD_STATE *); +extern void smtpd_chat_reply(SMTPD_STATE *, char *, ...); +extern void smtpd_chat_notify(SMTPD_STATE *); + +/* 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 +/*--*/ + diff --git a/postfix/smtpd/smtpd_check.c b/postfix/smtpd/smtpd_check.c new file mode 100644 index 000000000..c88a76ab6 --- /dev/null +++ b/postfix/smtpd/smtpd_check.c @@ -0,0 +1,1558 @@ +/*++ +/* NAME +/* smtpd_check 3 +/* SUMMARY +/* SMTP client request filtering +/* SYNOPSIS +/* #include "smtpd.h" +/* #include "smtpd_check.h" +/* +/* void smtpd_check_init() +/* +/* char *smtpd_check_client(state) +/* SMTPD_STATE *state; +/* +/* char *smtpd_check_helo(state, helohost) +/* SMTPD_STATE *state; +/* char *helohost; +/* +/* char *smtpd_check_mail(state, sender) +/* SMTPD_STATE *state; +/* char *sender; +/* +/* char *smtpd_check_rcpt(state, recipient) +/* SMTPD_STATE *state; +/* char *recipient; +/* DESCRIPTION +/* This module implements additional checks on SMTP client requests. +/* A client request is validated in the context of the session state. +/* The result is either an error response (including the numerical +/* code) or the result is a null pointer in case of success. +/* +/* smtpd_check_init() initializes. This function should be called +/* once during the process life time. +/* +/* Each of the following routines scrutinizes the argument passed to +/* an SMTP command such as HELO, MAIL FROM, RCPT TO, or scrutinizes +/* the initial client connection request. The administrator can +/* specify what restrictions apply. +/* +/* Restrictions are specified via configuration parameters named +/* \fIsmtpd_{client,helo,sender,recipient}_restrictions.\fR Each +/* configuration parameter specifies a list of zero or more +/* restrictions that are applied in the order as specified. +/* +/* Restrictions that can appear in some or all restriction +/* lists: +/* .IP reject +/* .IP permit +/* Reject or permit the request unconditionally. This is to be used +/* at the end of a restriction list in order to make the default +/* action explicit. +/* .IP reject_unknown_client +/* Reject the request when the client hostname could not be found. +/* The \fIunknown_client_reject_code\fR configuration parameter +/* specifies the reject status code (default: 450). +/* .IP permit_mynetworks +/* Allow the request when the client address matches the \fImynetworks\fR +/* configuration parameter. +/* .IP maptype:mapname +/* Meaning depends on context: client name/address, helo name, sender +/* or recipient address. +/* Perform a lookup in the specified access table. Reject the request +/* when the lookup result is REJECT or when the result begins with a +/* 4xx or 5xx status code. Other numerical status codes are not +/* permitted. Allow the request otherwise. The +/* \fIaccess_map_reject_code\fR configuration parameter specifies the +/* reject status code (default: 550). +/* .IP "check_client_access maptype:mapname" +/* Look up the client host name or any of its parent domains, or +/* the client address or any network obtained by stripping octets +/* from the address. +/* .IP "check_helo_access maptype:mapname" +/* Look up the HELO/EHLO hostname or any of its parent domains. +/* .IP "check_sender_access maptype:mapname" +/* Look up the resolved sender address, any parent domains of the +/* resolved sender address domain, or the localpart@. +/* .IP "check_recipient_access maptype:mapname" +/* Look up the resolved recipient address in the named access table, +/* any parent domains of the recipient domain, and the localpart@. +/* .IP reject_maps_rbl +/* Look up the client network address in the real-time blackhole +/* DNS zones below the domains listed in the "maps_rbl_domains" +/* configuration parameter. The \fImaps_rbl_reject_code\fR +/* configuration parameter specifies the reject status code +/* (default: 550). +/* .IP permit_naked_ip_address +/* Permit the use of a naked IP address (without enclosing []) +/* in HELO/EHLO commands. +/* This violates the RFC. You must enable this for some popular +/* PC mail clients. +/* .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, +/* and so is a trailing dot. +/* The \fIinvalid_hostname_reject_code\fR configuration parameter +/* specifies the reject status code (default:501). +/* .IP reject_unknown_hostname +/* Reject the request when the HELO/EHLO hostname has no A or MX record. +/* The \fIunknown_hostname_reject_code\fR configuration +/* parameter specifies the reject status code (default: 450). +/* .IP reject_unknown_address +/* Reject the request when the resolved sender address has no +/* DNS A or MX record. +/* The \fIunknown_address_reject_code\fR configuration parameter +/* specifies the reject status code (default: 450). +/* .IP check_relay_domains +/* Allow the request when either the client hostname or the resolved +/* recipient domain matches the \fIrelay_domains\fR configuration +/* parameter. Reject the request otherwise. +/* The \fIrelay_domains_reject_code\fR configuration parameter specifies +/* the reject status code (default: 550). +/* .IP permit_mx_backup +/* Allow the request when the local mail system is mail exchanger +/* for the recipient domain (this includes the case where the local +/* system is the final destination). +/* .PP +/* smtpd_check_client() validates the client host name or address. +/* Relevant configuration parameters: +/* .IP client_restrictions +/* Restrictions on the names or addresses of clients that may connect +/* to this SMTP server. +/* .PP +/* smtpd_check_helo() validates the hostname provided with the +/* HELO/EHLO commands. Relevant configuration parameters: +/* .IP helo_restrictions +/* Restrictions on the hostname that is sent with the HELO/EHLO +/* command. +/* .PP +/* smtpd_check_mail() validates the sender address provided with +/* a MAIL FROM request. Relevant configuration parameters: +/* .IP sender_restrictions +/* Restrictions on the sender address that is sent with the MAIL FROM +/* command. +/* .PP +/* smtpd_check_rcpt() validates the recipient address provided +/* with an RCPT TO request. Relevant configuration parameters: +/* .IP recipient_restrictions +/* Restrictions on the recipient address that is sent with the RCPT +/* TO command. +/* .PP +/* smtpd_check_size() checks if a message with the given size can +/* be received (zero means that the message size is unknown). The +/* message is rejected when: +/* .IP \(bu +/* The message size exceeds the non-zero bound specified with the +/* \fImessage_size_limit\fR configuration parameter. This is a +/* permanent error. +/* .IP \(bu +/* The message would cause the available queue file system space +/* to drop below the bound specified with the \fImin_queue_free\fR +/* configuration parameter. This is a temporary error. +/* .IP \(bu +/* The message would use up more than half the available queue file +/* system space. This is a temporary error. +/* .PP +/* Arguments: +/* .IP name +/* The client hostname, or \fIunknown\fR. +/* .IP addr +/* The client address. +/* .IP helohost +/* The hostname given with the HELO command. +/* .IP sender +/* The sender address given with the MAIL FROM command. +/* .IP recipient +/* The recipient address given with the RCPT TO or VRFY command. +/* .IP size +/* The message size given with the MAIL FROM command (zero if unknown). +/* BUGS +/* Policies like these should not be hard-coded in C, but should +/* be user-programmable instead. +/* SEE ALSO +/* namadr_list(3) host access control +/* domain_list(3) domain access control +/* fsspace(3) free file system space +/* 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 +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* DNS library. */ + +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "smtpd.h" +#include "smtpd_check.h" + + /* + * Eject seat in case of parsing problems. + */ +static jmp_buf smtpd_check_buf; + + /* + * Results of restrictions. + */ +#define SMTPD_CHECK_DUNNO 0 /* indifferent */ +#define SMTPD_CHECK_OK 1 /* explicitly permit */ +#define SMTPD_CHECK_REJECT 2 /* explicitly reject */ + + /* + * Intermediate results. These are static to avoid unnecessary stress on the + * memory manager routines. + */ +static RESOLVE_REPLY reply; +static VSTRING *error_text; + + /* + * Pre-opened access control lists. + */ +static DOMAIN_LIST *relay_domains; +static NAMADR_LIST *mynetworks; + + /* + * Pre-parsed restriction lists. + */ +static ARGV *client_restrctions; +static ARGV *helo_restrctions; +static ARGV *mail_restrctions; +static ARGV *rcpt_restrctions; + +#define STR vstring_str + +/* smtpd_check_parse - pre-parse restrictions */ + +static ARGV *smtpd_check_parse(char *checks) +{ + char *saved_checks = mystrdup(checks); + ARGV *argv = argv_alloc(1); + char *bp = saved_checks; + char *name; + + /* + * Pre-parse the restriction list, and open any dictionaries that we + * encounter. Dictionaries must be opened before entering the chroot + * jail. + */ + while ((name = mystrtok(&bp, " \t\r\n,")) != 0) { + argv_add(argv, name, (char *) 0); + if (strchr(name, ':') && dict_handle(name) == 0) + dict_register(name, dict_open(name, 0)); + } + argv_terminate(argv); + + /* + * Cleanup. + */ + myfree(saved_checks); + return (argv); +} + +/* smtpd_check_init - initialize once during process lifetime */ + +void smtpd_check_init(void) +{ + + /* + * Pre-open access control lists before going to jail. + */ + mynetworks = namadr_list_init(var_mynetworks); + relay_domains = domain_list_init(var_relay_domains); + + /* + * Reply is used as a cache for resolved addresses, and error_text is + * used for returning error responses. + */ + resolve_clnt_init(&reply); + error_text = vstring_alloc(10); + + /* + * Pre-parse the restriction lists. At the same time, pre-open tables + * before going to jail. + */ + client_restrctions = smtpd_check_parse(var_client_checks); + helo_restrctions = smtpd_check_parse(var_helo_checks); + mail_restrctions = smtpd_check_parse(var_mail_checks); + rcpt_restrctions = smtpd_check_parse(var_rcpt_checks); +} + +/* smtpd_check_reject - do the boring things that must be done */ + +static int smtpd_check_reject(SMTPD_STATE *state, int error_class, + char *format,...) +{ + va_list ap; + + /* + * Update the error class mask, and format the response. XXX What about + * multi-line responses? For now we cheat and send whitespace. + */ + state->error_mask |= error_class; + va_start(ap, format); + vstring_vsprintf(error_text, format, ap); + va_end(ap); + printable(STR(error_text), ' '); + + /* + * Validate the response, that is, the response must begin with a + * three-digit status code, and the first digit must be 4 or 5. If the + * response is bad, log a warning and send a generic response instead. + */ + if ((STR(error_text)[0] != '4' && STR(error_text)[0] != '5') + || !ISDIGIT(STR(error_text)[1]) || !ISDIGIT(STR(error_text)[2]) + || ISDIGIT(STR(error_text)[3])) { + msg_warn("response code configuration error: %s", STR(error_text)); + vstring_strcpy(error_text, "450 Service unavailable"); + } + + /* + * Log what is happening. When the sysadmin discards policy violation + * postmaster notices, this may be the only trace left that service was + * rejected. Print the request, client name/address, and response. + */ + msg_info("reject: %s from %s[%s]: %s", state->where, state->name, + state->addr, STR(error_text)); + + return (SMTPD_CHECK_REJECT); +} + +/* reject_unknown_client - fail if client hostname is unknown */ + +static int reject_unknown_client(SMTPD_STATE *state) +{ + char *myname = "reject_unknown_client"; + + if (msg_verbose) + msg_info("%s: %s %s", myname, state->name, state->addr); + + if (strcasecmp(state->name, "unknown") == 0) + return (smtpd_check_reject(state, MAIL_ERROR_POLICY, + "%d Cannot find your hostname, [%s]", + var_unk_client_code, state->addr)); + return (SMTPD_CHECK_DUNNO); +} + +/* permit_mynetworks - succeed if client is in a trusted network */ + +static int permit_mynetworks(SMTPD_STATE *state) +{ + char *myname = "permit_mynetworks"; + + if (msg_verbose) + msg_info("%s: %s %s", myname, state->name, state->addr); + + if (namadr_list_match(mynetworks, state->name, state->addr)) + return (SMTPD_CHECK_OK); + return (SMTPD_CHECK_DUNNO); +} + +/* 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; + + if (msg_verbose) + msg_info("%s: %s", myname, 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; + + /* + * Validate the hostname. + */ + if (!valid_hostname(test_name)) + stat = smtpd_check_reject(state, MAIL_ERROR_POLICY, + "%d <%s>: Invalid name", + var_bad_name_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) +{ + char *myname = "reject_unknown_hostname"; + int dns_status; + + if (msg_verbose) + msg_info("%s: %s", myname, name); + + dns_status = dns_lookup_types(name, 0, (DNS_RR **) 0, (VSTRING *) 0, + (VSTRING *) 0, T_A, T_MX, 0); + if (dns_status != DNS_OK) + return (smtpd_check_reject(state, MAIL_ERROR_POLICY, + "%d <%s>: Host not found", + var_unk_name_code, name)); + return (SMTPD_CHECK_DUNNO); +} + +/* reject_unknown_mailhost - fail if name has no A or MX record */ + +static int reject_unknown_mailhost(SMTPD_STATE *state, char *name) +{ + char *myname = "reject_unknown_mailhost"; + int dns_status; + + if (msg_verbose) + msg_info("%s: %s", myname, name); + + dns_status = dns_lookup_types(name, 0, (DNS_RR **) 0, (VSTRING *) 0, + (VSTRING *) 0, T_A, T_MX, 0); + if (dns_status != DNS_OK) + return (smtpd_check_reject(state, MAIL_ERROR_POLICY, + "%d <%s>: Domain not found", + var_unk_addr_code, name)); + return (SMTPD_CHECK_DUNNO); +} + +/* check_relay_domains - OK/FAIL for message relaying */ + +static int check_relay_domains(SMTPD_STATE *state, char *recipient) +{ + char *myname = "check_relay_domains"; + char *domain; + + if (msg_verbose) + msg_info("%s: %s", myname, recipient); + + /* + * Permit if the client matches the relay_domains list. + */ + if (domain_list_match(relay_domains, state->name)) + return (SMTPD_CHECK_OK); + + /* + * Resolve the address if not yet done. + */ + if (VSTRING_LEN(reply.recipient) == 0) { + canon_addr_internal(reply.recipient, recipient); + resolve_clnt_query(STR(reply.recipient), &reply); + } + + /* + * Permit if destination is local. XXX This must be generalized for + * per-domain user tables and for non-UNIX local delivery agents. + */ + if (STR(reply.nexthop)[0] == 0 + || (domain = strrchr(STR(reply.recipient), '@')) == 0) + return (SMTPD_CHECK_OK); + domain += 1; + + /* + * Permit if the destination matches the relay_domains list. + */ + if (domain_list_match(relay_domains, domain)) + return (SMTPD_CHECK_OK); + + /* + * Deny relaying between sites that both are not in relay_domains. + */ + return (smtpd_check_reject(state, MAIL_ERROR_POLICY, + "%d <%s>: Relay access denied", + var_relay_code, recipient)); +} + +/* has_my_addr - see if this host name lists one of my network addresses */ + +static int has_my_addr(char *host) +{ + char *myname = "has_my_addr"; + struct in_addr addr; + char **cpp; + struct hostent *hp; + + if (msg_verbose) + msg_info("%s: host %s", myname, host); + + /* + * If we can't lookup the host, play safe and assume it is OK. + */ +#define YUP 1 +#define NOPE 0 + + if ((hp = gethostbyname(host)) == 0) { + if (msg_verbose) + msg_info("%s: host %s: not found", myname, host); + return (YUP); + } + if (hp->h_addrtype != AF_INET || hp->h_length != sizeof(addr)) { + msg_warn("address type %d length %d for %s", + hp->h_addrtype, hp->h_length, host); + return (YUP); + } + for (cpp = hp->h_addr_list; *cpp; cpp++) { + memcpy((char *) &addr, *cpp, sizeof(addr)); + if (msg_verbose) + msg_info("%s: addr %s", myname, inet_ntoa(addr)); + if (own_inet_addr(&addr)) + return (YUP); + } + if (msg_verbose) + msg_info("%s: host %s: no match", myname, host); + + return (NOPE); +} + +/* permit_mx_backup - permit use of me as MX backup for recipient domain */ + +static int permit_mx_backup(SMTPD_STATE *unused_state, const char *recipient) +{ + char *myname = "permit_mx_backup"; + char *domain; + DNS_RR *mx_list; + DNS_RR *mx; + int dns_status; + + if (msg_verbose) + msg_info("%s: %s", myname, recipient); + + /* + * Resolve the address if not yet done. + */ + if (VSTRING_LEN(reply.recipient) == 0) { + canon_addr_internal(reply.recipient, recipient); + resolve_clnt_query(STR(reply.recipient), &reply); + } + + /* + * If the destination is local, it is acceptable, because we are + * supposedly MX for our own address. + */ + if (STR(reply.nexthop)[0] == 0 + || (domain = strrchr(STR(reply.recipient), '@')) == 0) + return (SMTPD_CHECK_OK); + domain += 1; + if (resolve_local(domain)) + return (SMTPD_CHECK_OK); + + if (msg_verbose) + msg_info("%s: not local: %s", myname, recipient); + + /* + * Skip numerical forms that didn't match the local system. + */ + if (domain[0] == '#' + || (domain[0] == '[' && domain[strlen(domain) - 1] == ']')) + return (SMTPD_CHECK_DUNNO); + + /* + * Look up the list of MX host names for this domain. If no MX host is + * found, perhaps it is a CNAME for the local machine. Clients aren't + * supposed to send CNAMEs in SMTP commands, but it happens anyway. If we + * can't look up the destination, play safe and assume it is OK. + */ + dns_status = dns_lookup(domain, T_MX, 0, &mx_list, + (VSTRING *) 0, (VSTRING *) 0); + if (dns_status == DNS_NOTFOUND) + return (has_my_addr(domain) ? SMTPD_CHECK_OK : SMTPD_CHECK_DUNNO); + if (dns_status != DNS_OK) + return (SMTPD_CHECK_OK); + + /* + * First, see if we match any of the MX host names listed. Only if no + * name match is found, go through the trouble of host address lookups. + */ + for (mx = mx_list; mx != 0; mx = mx->next) { + if (msg_verbose) + msg_info("%s: resolve hostname: %s", myname, (char *) mx->data); + if (resolve_local((char *) mx->data)) { + dns_rr_free(mx_list); + return (SMTPD_CHECK_OK); + } + } + + /* + * Argh. Do further DNS lookups and match interface addresses. + */ + for (mx = mx_list; mx != 0; mx = mx->next) { + if (msg_verbose) + msg_info("%s: address lookup: %s", myname, (char *) mx->data); + if (has_my_addr((char *) mx->data)) { + dns_rr_free(mx_list); + return (SMTPD_CHECK_OK); + } + } + if (msg_verbose) + msg_info("%s: no match", myname); + + dns_rr_free(mx_list); + return (SMTPD_CHECK_DUNNO); +} + +/* reject_unknown_address - fail if address does not resolve */ + +static int reject_unknown_address(SMTPD_STATE *state, char *addr) +{ + char *myname = "reject_unknown_address"; + char *domain; + + if (msg_verbose) + msg_info("%s: %s", myname, addr); + + /* + * Resolve the address if not yet done. + */ + if (VSTRING_LEN(reply.recipient) == 0) { + canon_addr_internal(reply.recipient, addr); + resolve_clnt_query(STR(reply.recipient), &reply); + } + + /* + * Skip local destinations and non-DNS forms. + */ + if (STR(reply.nexthop)[0] == 0 + || (domain = strrchr(STR(reply.recipient), '@')) == 0) + return (SMTPD_CHECK_DUNNO); + domain += 1; + if (domain[0] == '#') + return (SMTPD_CHECK_DUNNO); + if (domain[0] == '[' && domain[strlen(domain) - 1] == ']') + return (SMTPD_CHECK_DUNNO); + + /* + * Look up the name in the DNS. + */ + return (reject_unknown_mailhost(state, domain)); +} + +/* check_table_result - translate table lookup result into pass/reject */ + +static int check_table_result(SMTPD_STATE *state, char *table, + const char *value, const char *datum) +{ + char *myname = "check_table_result"; + + if (msg_verbose) + msg_info("%s: %s %s %s", myname, table, value, datum); + + /* + * REJECT means NO. Generate a generic error response. + */ + if (strcasecmp(value, "REJECT") == 0) + return (smtpd_check_reject(state, MAIL_ERROR_POLICY, + "%d <%s>: Access denied", + var_access_map_code, datum)); + + /* + * 4xx or 5xx means NO as well. smtpd_check_reject() will validate the + * response status code. + */ + if (ISDIGIT(value[0])) + return (smtpd_check_reject(state, MAIL_ERROR_POLICY, "%s", value)); + + /* + * OK or RELAY or whatever means YES. + */ + return (SMTPD_CHECK_OK); +} + +/* check_access - table lookup without substring magic */ + +static int check_access(SMTPD_STATE *state, char *table, char *name) +{ + char *myname = "check_access"; + char *low_name = lowercase(mystrdup(name)); + const char *value; + +#define CHK_ACCESS_RETURN(x) { myfree(low_name); return(x); } + + if (msg_verbose) + msg_info("%s: %s", myname, name); + + if ((value = dict_lookup(table, low_name)) != 0) + CHK_ACCESS_RETURN(check_table_result(state, table, value, name)); + if (dict_errno != 0) + msg_fatal("%s: table lookup problem", table); + CHK_ACCESS_RETURN(SMTPD_CHECK_DUNNO); +} + +/* check_domain_access - domainname-based table lookup */ + +static int check_domain_access(SMTPD_STATE *state, char *table, char *domain) +{ + char *myname = "check_domain_access"; + char *low_domain = lowercase(mystrdup(domain)); + char *name; + char *next; + const char *value; + + if (msg_verbose) + msg_info("%s: %s", myname, domain); + + /* + * Try the name and its parent domains. Don't try top-level domains. + */ +#define CHK_DOMAIN_RETURN(x) { myfree(low_domain); return(x); } + + for (name = low_domain; (next = strchr(name, '.')) != 0; name = next + 1) { + if ((value = dict_lookup(table, name)) != 0) + CHK_DOMAIN_RETURN(check_table_result(state, table, value, domain)); + if (dict_errno != 0) + msg_fatal("%s: table lookup problem", table); + } + CHK_DOMAIN_RETURN(SMTPD_CHECK_DUNNO); +} + +/* check_addr_access - address-based table lookup */ + +static int check_addr_access(SMTPD_STATE *state, char *table, char *address) +{ + char *myname = "check_addr_access"; + char *addr; + const char *value; + + if (msg_verbose) + msg_info("%s: %s", myname, address); + + /* + * Try the address and its parent networks. + */ + addr = STR(vstring_strcpy(error_text, address)); + + do { + if ((value = dict_lookup(table, addr)) != 0) + return (check_table_result(state, table, value, address)); + if (dict_errno != 0) + msg_fatal("%s: table lookup problem", table); + } while (split_at_right(addr, '.')); + + return (SMTPD_CHECK_DUNNO); +} + +/* check_namadr_access - OK/FAIL based on host name/address lookup */ + +static int check_namadr_access(SMTPD_STATE *state, char *table, + char *name, char *addr) +{ + char *myname = "check_namadr_access"; + int status; + + if (msg_verbose) + msg_info("%s: name %s addr %s", myname, name, addr); + + /* + * Look up the host name, or parent domains thereof. XXX A domain + * wildcard may pre-empt a more specific address table entry. + */ + if ((status = check_domain_access(state, table, name)) != 0) + return (status); + + /* + * Look up the network address, or parent networks thereof. + */ + if ((status = check_addr_access(state, table, addr)) != 0) + return (status); + + /* + * Undecided when the host was not found. + */ + return (SMTPD_CHECK_DUNNO); +} + +/* check_mail_access - OK/FAIL based on mail address lookup */ + +static int check_mail_access(SMTPD_STATE *state, char *table, char *addr) +{ + char *myname = "check_mail_access"; + char *ratsign; + int status; + char *local_at; + + if (msg_verbose) + msg_info("%s: %s", myname, addr); + + /* + * Resolve the address if not yet done. + */ + if (VSTRING_LEN(reply.recipient) == 0) { + canon_addr_internal(reply.recipient, addr); + resolve_clnt_query(STR(reply.recipient), &reply); + } + + /* + * Garbage in, garbage out. Every address from canon_addr_internal() and + * from resolve_clnt_query() must be fully qualified. + */ + if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0) { + msg_warn("%s: no @domain in address: %s", myname, STR(reply.recipient)); + return (0); + } + + /* + * Look up the full address. + */ + if ((status = check_access(state, table, STR(reply.recipient))) != 0) + return (status); + + /* + * Look up the domain name, or parent domains thereof. + */ + if ((status = check_domain_access(state, table, ratsign + 1)) != 0) + return (status); + + /* + * Look up localpart@ + */ + local_at = mystrndup(STR(reply.recipient), + ratsign - STR(reply.recipient) + 1); + status = check_access(state, table, local_at); + myfree(local_at); + if (status != 0) + return (status); + + /* + * Undecided when no match found. + */ + return (SMTPD_CHECK_DUNNO); +} + +/* reject_maps_rbl - reject if client address in real-time blackhole list */ + +static int reject_maps_rbl(SMTPD_STATE *state) +{ + char *myname = "reject_maps_rbl"; + ARGV *octets = argv_split(state->addr, "."); + VSTRING *query = vstring_alloc(100); + char *saved_domains = mystrdup(var_maps_rbl_domains); + char *bp = saved_domains; + char *rbl_domain; + int reverse_len; + int dns_status = DNS_FAIL; + int i; + int result; + + if (msg_verbose) + msg_info("%s: %s", myname, state->addr); + + /* + * Build the constant part of the RBL query: the reverse client address. + */ + for (i = octets->argc - 1; i >= 0; i--) { + vstring_strcat(query, octets->argv[i]); + vstring_strcat(query, "."); + } + reverse_len = VSTRING_LEN(query); + + /* + * Tack on each RBL domain name and query the DNS for an A record. If the + * record exists, the client address is blacklisted. + */ + while ((rbl_domain = mystrtok(&bp, " \t\r\n,")) != 0) { + vstring_truncate(query, reverse_len); + vstring_strcat(query, rbl_domain); + dns_status = dns_lookup(STR(query), T_A, 0, (DNS_RR **) 0, + (VSTRING *) 0, (VSTRING *) 0); + if (dns_status == DNS_OK) + break; + } + + /* + * Report the result. + */ + 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); + else + result = SMTPD_CHECK_DUNNO; + + /* + * Clean up. + */ + argv_free(octets); + vstring_free(query); + myfree(saved_domains); + + return (result); +} + +/* is_map_command - restriction has form: check_xxx_access type:name */ + +static int is_map_command(char *name, char *command, char ***argp) +{ + + /* + * This is a three-valued function: (a) this is not a check_xxx_access + * command, (b) this is a malformed check_xxx_access command, (c) this is + * a well-formed check_xxx_access command. That's too clumsy for function + * result values, so we use regular returns for (a) and (c), and use long + * jumps for the error case (b). + */ + if (strcasecmp(name, command) != 0) { + return (0); + } else if (*argp == 0 || strchr(*(*argp += 1), ':') == 0) { + msg_warn("restriction %s requires maptype:mapname", command); + longjmp(smtpd_check_buf, -1); + } else { + return (1); + } +} + +/* generic_checks - generic restrictions */ + +static int generic_checks(SMTPD_STATE *state, char *name, + char ***cpp, int *status, char *what) +{ + + /* + * Generic restrictions. + */ + if (strcasecmp(name, PERMIT_ALL) == 0) { + *status = SMTPD_CHECK_OK; + return (1); + } + if (strcasecmp(name, REJECT_ALL) == 0) { + *status = smtpd_check_reject(state, MAIL_ERROR_POLICY, *what ? + "%d <%s> Access denied" : "%d Access denied", + var_reject_code, what); + return (1); + } + + /* + * Client name/address restrictions. + */ + if (strcasecmp(name, REJECT_UNKNOWN_CLIENT) == 0) { + *status = reject_unknown_client(state); + return (1); + } + if (strcasecmp(name, PERMIT_MYNETWORKS) == 0) { + *status = permit_mynetworks(state); + return (1); + } + if (is_map_command(name, CHECK_CLIENT_ACL, cpp)) { + *status = check_namadr_access(state, **cpp, state->name, state->addr); + return (1); + } + if (strcasecmp(name, REJECT_MAPS_RBL) == 0) { + *status = reject_maps_rbl(state); + return (1); + } + + /* + * HELO/EHLO parameter restrictions. + */ + if (state->helo_name) { + if (is_map_command(name, CHECK_HELO_ACL, cpp) && state->helo_name) { + *status = check_domain_access(state, **cpp, state->helo_name); + return (1); + } + if (strcasecmp(name, REJECT_INVALID_HOSTNAME) == 0) { + if (*state->helo_name != '[') + *status = reject_invalid_hostname(state, what); + return (1); + } + if (strcasecmp(name, REJECT_UNKNOWN_HOSTNAME) == 0) { + if (*state->helo_name != '[') + *status = reject_unknown_hostname(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) + *status = SMTPD_CHECK_OK; + return (1); + } + } + + /* + * Sender mail address restrictions. + */ + if (state->sender) { + if (is_map_command(name, CHECK_SENDER_ACL, cpp) && state->sender) { + *status = check_mail_access(state, **cpp, state->sender); + return (1); + } + if (strcasecmp(name, REJECT_UNKNOWN_ADDRESS) == 0) { + *status = reject_unknown_address(state, state->sender); + return (1); + } + } + return (0); +} + +/* smtpd_check_client - validate client name or address */ + +char *smtpd_check_client(SMTPD_STATE *state) +{ + char **cpp; + char *name; + int status; + + /* + * Initialize. + */ + VSTRING_RESET(reply.recipient); + status = setjmp(smtpd_check_buf); + if (status != 0) + return (0); + + /* + * Apply restrictions in the order as specified. + */ + for (cpp = client_restrctions->argv; (name = *cpp) != 0; cpp++) { + if (strchr(name, ':') != 0) { + status = check_namadr_access(state, name, state->name, state->addr); + } else if (generic_checks(state, name, &cpp, &status, state->addr) == 0) { + msg_warn("unknown %s check: \"%s\"", VAR_CLIENT_CHECKS, name); + break; + } + if (status != 0) + break; + } + return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0); +} + +/* smtpd_check_helo - validate HELO hostname */ + +char *smtpd_check_helo(SMTPD_STATE *state, char *helohost) +{ + char **cpp; + char *name; + int status; + + /* + * Initialize. + */ + VSTRING_RESET(reply.recipient); + status = setjmp(smtpd_check_buf); + if (status != 0) + return (0); + + /* + * Apply restrictions in the order as specified. Minor kluge so that we + * can delegate work to the generic routine. + */ + state->helo_name = mystrdup(helohost); + for (cpp = helo_restrctions->argv; (name = *cpp) != 0; cpp++) { + if (strchr(name, ':') != 0) { + status = check_domain_access(state, name, helohost); + } else if (generic_checks(state, name, &cpp, &status, helohost) == 0) { + msg_warn("unknown %s check: \"%s\"", VAR_HELO_CHECKS, name); + break; + } + if (status != 0) + break; + } + myfree(state->helo_name); + state->helo_name = 0; + return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0); +} + +/* smtpd_check_mail - validate sender address */ + +char *smtpd_check_mail(SMTPD_STATE *state, char *sender) +{ + char **cpp; + char *name; + int status; + + /* + * Initialize. + */ + VSTRING_RESET(reply.recipient); + status = setjmp(smtpd_check_buf); + if (status != 0) + return (0); + + /* + * Apply restrictions in the order as specified. Minor kluge so that we + * can delegate work to the generic routine. + */ + state->sender = mystrdup(sender); + for (cpp = mail_restrctions->argv; (name = *cpp) != 0; cpp++) { + if (strchr(name, ':') != 0) { + status = check_mail_access(state, name, sender); + } else if (generic_checks(state, name, &cpp, &status, sender) == 0) { + msg_warn("unknown %s check: \"%s\"", VAR_MAIL_CHECKS, name); + return (0); + } + if (status != 0) + break; + } + myfree(state->sender); + state->sender = 0; + return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0); +} + +/* smtpd_check_rcpt - validate recipient address */ + +char *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient) +{ + char **cpp; + char *name; + int status; + + /* + * Initialize. + */ + VSTRING_RESET(reply.recipient); + status = setjmp(smtpd_check_buf); + if (status != 0) + return (0); + + /* + * Apply restrictions in the order as specified. + */ + for (cpp = rcpt_restrctions->argv; (name = *cpp) != 0; cpp++) { + if (strchr(name, ':') != 0) { + status = check_mail_access(state, name, recipient); + } else if (is_map_command(name, CHECK_RECIP_ACL, &cpp)) { + status = check_mail_access(state, *cpp, recipient); + } else if (strcasecmp(name, PERMIT_MX_BACKUP) == 0) { + status = permit_mx_backup(state, recipient); + } else if (strcasecmp(name, CHECK_RELAY_DOMAINS) == 0) { + status = check_relay_domains(state, recipient); + } else if (generic_checks(state, name, &cpp, &status, recipient) == 0) { + msg_warn("unknown %s check: \"%s\"", VAR_RCPT_CHECKS, name); + break; + } + if (status != 0) + break; + } + return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0); +} + +/* smtpd_check_size - check optional SIZE parameter value */ + +char *smtpd_check_size(SMTPD_STATE *state, off_t size) +{ + char *myname = "smtpd_check_size"; + struct fsspace fsbuf; + + /* + * Avoid overflow/underflow when comparing message size against available + * space. + */ +#define BLOCKS(x) ((x) / fsbuf.block_size) + + if (var_message_limit > 0 && size > var_message_limit) { + (void) smtpd_check_reject(state, MAIL_ERROR_POLICY, + "552 Message size exceeds fixed limit"); + return (STR(error_text)); + } + fsspace(".", &fsbuf); + if (msg_verbose) + msg_info("%s: blocks %lu avail %lu min_free %lu size %lu", + myname, + (unsigned long) fsbuf.block_size, + (unsigned long) fsbuf.block_free, + (unsigned long) var_queue_minfree, + (unsigned long) size); + if (BLOCKS(var_queue_minfree) >= fsbuf.block_free + || BLOCKS(size) >= fsbuf.block_free - BLOCKS(var_queue_minfree) + || BLOCKS(size) >= fsbuf.block_free / 2) { + (void) smtpd_check_reject(state, MAIL_ERROR_RESOURCE, + "452 Insufficient system storage"); + return (STR(error_text)); + } + return (0); +} + +#ifdef TEST + + /* + * Test program to try out all these restrictions without having to go live. + * This is not entirely stand-alone, as it requires access to the Postfix + * rewrite/resolve service. This is just for testing code, not for debugging + * configuration files. + */ +#include +#include + +#include +#include + +#include + +#include + + /* + * Dummies. These are never set. + */ +char *var_client_checks = ""; +char *var_helo_checks = ""; +char *var_mail_checks = ""; +char *var_rcpt_checks = ""; +char *var_relay_domains = ""; +char *var_mynetworks = ""; +char *var_notify_classes = ""; + + /* + * String-valued configuration parameters. + */ +char *var_maps_rbl_domains; +char *var_mydest; +char *var_inet_interfaces; + +typedef struct { + char *name; + char *defval; + char **target; +} STRING_TABLE; + +static STRING_TABLE string_table[] = { + VAR_MAPS_RBL_DOMAINS, DEF_MAPS_RBL_DOMAINS, &var_maps_rbl_domains, + VAR_MYDEST, DEF_MYDEST, &var_mydest, + VAR_INET_INTERFACES, DEF_INET_INTERFACES, &var_inet_interfaces, + 0, +}; + +/* string_init - initialize string parameters */ + +static void string_init(void) +{ + STRING_TABLE *sp; + + for (sp = string_table; sp->name; sp++) + sp->target[0] = mystrdup(sp->defval); +} + +/* string_update - update string parameter */ + +static int string_update(char **argv) +{ + STRING_TABLE *sp; + + for (sp = string_table; sp->name; sp++) { + if (strcasecmp(argv[0], sp->name) == 0) { + myfree(sp->target[0]); + sp->target[0] = mystrdup(argv[1]); + return (1); + } + } + return (0); +} + + /* + * Integer parameters. + */ +int var_queue_minfree; /* XXX use off_t */ +typedef struct { + char *name; + int defval; + int *target; +} INT_TABLE; + +int var_unk_client_code; +int var_bad_name_code; +int var_unk_name_code; +int var_unk_addr_code; +int var_relay_code; +int var_maps_rbl_code; +int var_access_map_code; +int var_reject_code; + +static INT_TABLE int_table[] = { + "msg_verbose", 0, &msg_verbose, + VAR_UNK_CLIENT_CODE, DEF_UNK_CLIENT_CODE, &var_unk_client_code, + VAR_BAD_NAME_CODE, DEF_BAD_NAME_CODE, &var_bad_name_code, + VAR_UNK_NAME_CODE, DEF_UNK_NAME_CODE, &var_unk_name_code, + VAR_UNK_ADDR_CODE, DEF_UNK_ADDR_CODE, &var_unk_addr_code, + VAR_RELAY_CODE, DEF_RELAY_CODE, &var_relay_code, + 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, + 0, +}; + +/* int_init - initialize int parameters */ + +static void int_init(void) +{ + INT_TABLE *sp; + + for (sp = int_table; sp->name; sp++) + sp->target[0] = sp->defval; +} + +/* int_update - update int parameter */ + +static int int_update(char **argv) +{ + INT_TABLE *ip; + + for (ip = int_table; ip->name; ip++) { + if (strcasecmp(argv[0], ip->name) == 0) { + if (!ISDIGIT(*argv[1])) + msg_fatal("bad number: %s %s", ip->name, argv[1]); + ip->target[0] = atoi(argv[1]); + return (1); + } + } + return (0); +} + + /* + * Restrictions. + */ +typedef struct { + char *name; + ARGV **target; +} REST_TABLE; + +static REST_TABLE rest_table[] = { + "client_restrictions", &client_restrctions, + "helo_restrictions", &helo_restrctions, + "sender_restrictions", &mail_restrctions, + "recipient_restrictions", &rcpt_restrctions, + 0, +}; + +/* rest_update - update restriction */ + +static int rest_update(char **argv) +{ + REST_TABLE *rp; + + for (rp = rest_table; rp->name; rp++) { + if (strcasecmp(rp->name, argv[0]) == 0) { + argv_free(rp->target[0]); + rp->target[0] = smtpd_check_parse(argv[1]); + return (1); + } + } + return (0); +} + +/* resolve_clnt_init - initialize reply */ + +void resolve_clnt_init(RESOLVE_REPLY *reply) +{ + reply->transport = vstring_alloc(100); + reply->nexthop = vstring_alloc(100); + reply->recipient = vstring_alloc(100); +} + +/* canon_addr_internal - stub */ + +VSTRING *canon_addr_internal(VSTRING *result, const char *addr) +{ + if (strchr(addr, '@') == 0) + msg_fatal("%s: address rewriting is disabled", addr); + vstring_strcpy(result, addr); +} + +/* resolve_clnt_query - stub */ + +void resolve_clnt_query(const char *addr, RESOLVE_REPLY *reply) +{ + vstring_strcpy(reply->transport, "foo"); + vstring_strcpy(reply->nexthop, "foo"); + if (strchr(addr, '%')) + msg_fatal("%s: address rewriting is disabled", addr); + vstring_strcpy(reply->recipient, addr); +} + +/* smtpd_chat_reset - stub */ + +void smtpd_chat_reset(SMTPD_STATE *unused_state) +{ +} + +/* usage - scream and terminate */ + +static NORETURN usage(char *myname) +{ + msg_fatal("usage: %s", myname); +} + +main(int argc, char **argv) +{ + VSTRING *buf = vstring_alloc(100); + SMTPD_STATE state; + ARGV *args; + char *bp; + char *resp; + + /* + * Initialization. Use dummies for client information. + */ + msg_vstream_init(argv[0], VSTREAM_ERR); + if (argc != 1) + usage(argv[0]); + string_init(); + int_init(); + smtpd_check_init(); + smtpd_state_init(&state, VSTREAM_IN, "", ""); + + /* + * Main loop: update config parameters or test the client, helo, sender + * and recipient restrictions. + */ + while (vstring_fgets_nonl(buf, VSTREAM_IN) != 0) { + + /* + * Tokenize the command. Note, the comma is not a separator, so that + * restriction lists can be entered as comma-separated lists. + */ + bp = STR(buf); + if (!isatty(0)) { + vstream_printf(">>> %s\n", bp); + vstream_fflush(VSTREAM_OUT); + } + if (*bp == '#') + continue; + if (*bp == '!') { + vstream_printf("exit %d\n", system(bp + 1)); + continue; + } + args = argv_split(bp, " \t\r\n"); + + /* + * Recognize the command. + */ + resp = "bad command"; + switch (args->argc) { + + /* + * Special case: client identity. + */ + case 3: +#define UPDATE_STRING(ptr,val) { if (ptr) myfree(ptr); ptr = mystrdup(val); } + + if (strcasecmp(args->argv[0], "client") == 0) { + state.where = "CONNECT"; + UPDATE_STRING(state.name, args->argv[1]); + UPDATE_STRING(state.addr, args->argv[2]); + resp = smtpd_check_client(&state); + } + break; + + /* + * Try config settings. + */ + case 2: + if (strcasecmp(args->argv[0], "mynetworks") == 0) { + namadr_list_free(mynetworks); + mynetworks = namadr_list_init(args->argv[1]); + resp = 0; + break; + } + if (strcasecmp(args->argv[0], "relay_domains") == 0) { + domain_list_free(relay_domains); + relay_domains = domain_list_init(args->argv[1]); + resp = 0; + break; + } + if (int_update(args->argv) + || string_update(args->argv) + || rest_update(args->argv)) { + resp = 0; + break; + } + + /* + * Try restrictions. + */ + if (strcasecmp(args->argv[0], "helo") == 0) { + state.where = "HELO"; + resp = smtpd_check_helo(&state, args->argv[1]); + UPDATE_STRING(state.helo_name, args->argv[1]); + } else if (strcasecmp(args->argv[0], "mail") == 0) { + state.where = "MAIL"; + resp = smtpd_check_mail(&state, args->argv[1]); + UPDATE_STRING(state.sender, args->argv[1]); + } else if (strcasecmp(args->argv[0], "rcpt") == 0) { + state.where = "RCPT"; + resp = smtpd_check_rcpt(&state, args->argv[1]); + } + break; + + /* + * Show commands. + */ + default: + resp = "Commands...\n\ + client
    \n\ + helo \n\ + sender
    \n\ + recipient
    \n\ + msg_verbose \n\ + client_restrictions \n\ + helo_restrictions \n\ + sender_restrictions \n\ + recipient_restrictions \n\ + \n\ + Note: no address rewriting \n"; + break; + } + vstream_printf("%s\n", resp ? resp : "OK"); + vstream_fflush(VSTREAM_OUT); + argv_free(args); + } + vstring_free(buf); + smtpd_state_reset(&state); + exit(0); +} + +#endif diff --git a/postfix/smtpd/smtpd_check.h b/postfix/smtpd/smtpd_check.h new file mode 100644 index 000000000..966889325 --- /dev/null +++ b/postfix/smtpd/smtpd_check.h @@ -0,0 +1,31 @@ +/*++ +/* NAME +/* smtpd_check 3h +/* SUMMARY +/* SMTP client request filtering +/* SYNOPSIS +/* #include "smtpd.h" +/* #include "smtpd_check.h" +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern void smtpd_check_init(void); +extern char *smtpd_check_client(SMTPD_STATE *); +extern char *smtpd_check_helo(SMTPD_STATE *, char *); +extern char *smtpd_check_mail(SMTPD_STATE *, char *); +extern char *smtpd_check_size(SMTPD_STATE *, off_t); +extern char *smtpd_check_rcpt(SMTPD_STATE *, char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ diff --git a/postfix/smtpd/smtpd_check.in b/postfix/smtpd/smtpd_check.in new file mode 100644 index 000000000..542f9d9c1 --- /dev/null +++ b/postfix/smtpd/smtpd_check.in @@ -0,0 +1,131 @@ +# +# Initialize. +# +! ../bin/postmap smtpd_check_access +#msg_verbose 1 +mynetworks 127.0.0.0/8,168.100.189.0/28 +relay_domains porcupine.org +# +# Test the client restrictions. +# +client_restrictions permit_mynetworks,reject_unknown_client,hash:./smtpd_check_access +client unknown 131.155.210.17 +client unknown 168.100.189.13 +client random.bad.domain 123.123.123.123 +client friend.bad.domain 123.123.123.123 +client bad.domain 123.123.123.123 +client wzv.win.tue.nl 131.155.210.17 +client aa.win.tue.nl 131.155.210.18 +client_restrictions permit_mynetworks +# +# Test the helo restrictions +# +helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,reject_unknown_hostname,hash:./smtpd_check_access +client unknown 131.155.210.17 +helo foo. +client foo 123.123.123.123 +helo foo. +helo foo +helo spike.porcupine.org +helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,hash:./smtpd_check_access +helo random.bad.domain +helo friend.bad.domain +helo_restrictions reject_invalid_hostname,reject_unknown_hostname +helo 123.123.123.123 +helo_restrictions permit_naked_ip_address,reject_invalid_hostname,reject_unknown_hostname +helo 123.123.123.123 +# +# Test the sender restrictions +# +sender_restrictions permit_mynetworks,reject_unknown_client +client unknown 131.155.210.17 +mail foo@watson.ibm.com +client unknown 168.100.189.13 +mail foo@watson.ibm.com +client foo 123.123.123.123 +mail foo@watson.ibm.com +sender_restrictions reject_unknown_address +mail foo@watson.ibm.com +mail foo@bad.domain +sender_restrictions hash:./smtpd_check_access +mail bad-sender@any.domain +mail bad-sender@good.domain +mail reject@this.address +mail Reject@this.address +mail foo@bad.domain +mail foo@Bad.domain +mail foo@random.bad.domain +mail foo@friend.bad.domain +# +# Test the recipient restrictions +# +recipient_restrictions permit_mynetworks,reject_unknown_client,check_relay_domains +client unknown 131.155.210.17 +rcpt foo@watson.ibm.com +client unknown 168.100.189.13 +rcpt foo@watson.ibm.com +client foo 123.123.123.123 +rcpt foo@watson.ibm.com +rcpt foo@porcupine.org +recipient_restrictions check_relay_domains +client foo.porcupine.org 168.100.189.13 +rcpt foo@watson.ibm.com +rcpt foo@porcupine.org +client foo 123.123.123.123 +rcpt foo@watson.ibm.com +rcpt foo@porcupine.org +recipient_restrictions hash:./smtpd_check_access +mail bad-sender@any.domain +mail bad-sender@good.domain +mail reject@this.address +mail foo@bad.domain +mail foo@random.bad.domain +mail foo@friend.bad.domain +# +# RBL +# +client_restrictions reject_maps_rbl +client spike.porcupine.org 168.100.189.2 +client foo 127.0.0.2 +# +# Hybrids +# +recipient_restrictions check_relay_domains +client foo 131.155.210.17 +rcpt foo@watson.ibm.com +recipient_restrictions check_client_access,hash:./smtpd_check_access,check_relay_domains +client foo 131.155.210.17 +rcpt foo@porcupine.org +helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,hash:./smtpd_check_access +recipient_restrictions check_helo_access,hash:./smtpd_check_access,check_relay_domains +helo bad.domain +rcpt foo@porcupine.org +helo 131.155.210.17 +rcpt foo@porcupine.org +recipient_restrictions check_sender_access,hash:./smtpd_check_access,check_relay_domains +mail foo@bad.domain +rcpt foo@porcupine.org +mail foo@friend.bad.domain +rcpt foo@porcupine.org +# +# MX backup +# +mydestination spike.porcupine.org,localhost.porcupine.org +inet_interfaces 168.100.189.2,127.0.0.1 +recipient_restrictions permit_mx_backup,reject +rcpt wietse@wzv.win.tue.nl +rcpt wietse@trouble.org +rcpt wietse@porcupine.org +# +# Deferred restrictions +# +client_restrictions permit +helo_restrictions permit +sender_restrictions permit +recipient_restrictions check_helo_access,hash:./smtpd_check_access,check_sender_access,hash:./smtpd_check_access +helo bad.domain +mail foo@good.domain +rcpt foo@porcupine.org +helo good.domain +mail foo@bad.domain +rcpt foo@porcupine.org diff --git a/postfix/smtpd/smtpd_check.in2 b/postfix/smtpd/smtpd_check.in2 new file mode 100644 index 000000000..a6463f986 --- /dev/null +++ b/postfix/smtpd/smtpd_check.in2 @@ -0,0 +1,85 @@ +# +# Initialize. +# +! ../bin/postmap smtpd_check_access +#msg_verbose 1 +mynetworks 127.0.0.0/8,168.100.189.0/28 +relay_domains porcupine.org +# +# Test the client restrictions. +# +client_restrictions permit_mynetworks,reject_unknown_client,check_client_access,hash:./smtpd_check_access +client unknown 131.155.210.17 +client unknown 168.100.189.13 +client random.bad.domain 123.123.123.123 +client friend.bad.domain 123.123.123.123 +client bad.domain 123.123.123.123 +client wzv.win.tue.nl 131.155.210.17 +client aa.win.tue.nl 131.155.210.18 +client_restrictions permit_mynetworks +# +# Test the helo restrictions +# +helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,reject_unknown_hostname,check_helo_access,hash:./smtpd_check_access +client unknown 131.155.210.17 +helo foo. +client foo 123.123.123.123 +helo foo. +helo foo +helo spike.porcupine.org +helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,check_helo_access,hash:./smtpd_check_access +helo random.bad.domain +helo friend.bad.domain +# +# Test the sender restrictions +# +sender_restrictions permit_mynetworks,reject_unknown_client +client unknown 131.155.210.17 +mail foo@watson.ibm.com +client unknown 168.100.189.13 +mail foo@watson.ibm.com +client foo 123.123.123.123 +mail foo@watson.ibm.com +sender_restrictions reject_unknown_address +mail foo@watson.ibm.com +mail foo@bad.domain +sender_restrictions check_sender_access,hash:./smtpd_check_access +mail bad-sender@any.domain +mail bad-sender@good.domain +mail reject@this.address +mail Reject@this.address +mail foo@bad.domain +mail foo@Bad.domain +mail foo@random.bad.domain +mail foo@friend.bad.domain +# +# Test the recipient restrictions +# +recipient_restrictions permit_mynetworks,reject_unknown_client,check_relay_domains +client unknown 131.155.210.17 +rcpt foo@watson.ibm.com +client unknown 168.100.189.13 +rcpt foo@watson.ibm.com +client foo 123.123.123.123 +rcpt foo@watson.ibm.com +rcpt foo@porcupine.org +recipient_restrictions check_relay_domains +client foo.porcupine.org 168.100.189.13 +rcpt foo@watson.ibm.com +rcpt foo@porcupine.org +client foo 123.123.123.123 +rcpt foo@watson.ibm.com +rcpt foo@porcupine.org +recipient_restrictions check_recipient_access,hash:./smtpd_check_access +mail bad-sender@any.domain +mail bad-sender@good.domain +mail reject@this.address +mail foo@bad.domain +mail foo@random.bad.domain +mail foo@friend.bad.domain +# +# RBL +# +client_restrictions reject_maps_rbl +client spike.porcupine.org 168.100.189.2 +client foo 127.0.0.2 diff --git a/postfix/smtpd/smtpd_check.ref b/postfix/smtpd/smtpd_check.ref new file mode 100644 index 000000000..73c72c638 --- /dev/null +++ b/postfix/smtpd/smtpd_check.ref @@ -0,0 +1,242 @@ +>>> # +>>> # Initialize. +>>> # +>>> ! ../bin/postmap smtpd_check_access +exit 0 +>>> #msg_verbose 1 +>>> mynetworks 127.0.0.0/8,168.100.189.0/28 +OK +>>> relay_domains porcupine.org +OK +>>> # +>>> # Test the client restrictions. +>>> # +>>> client_restrictions permit_mynetworks,reject_unknown_client,hash:./smtpd_check_access +OK +>>> client unknown 131.155.210.17 +./smtpd_check: reject: CONNECT from unknown[131.155.210.17]: 450 Cannot find your hostname, [131.155.210.17] +450 Cannot find your hostname, [131.155.210.17] +>>> client unknown 168.100.189.13 +OK +>>> client random.bad.domain 123.123.123.123 +./smtpd_check: reject: CONNECT from random.bad.domain[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> client friend.bad.domain 123.123.123.123 +OK +>>> client bad.domain 123.123.123.123 +./smtpd_check: reject: CONNECT from bad.domain[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> client wzv.win.tue.nl 131.155.210.17 +OK +>>> client aa.win.tue.nl 131.155.210.18 +./smtpd_check: reject: CONNECT from aa.win.tue.nl[131.155.210.18]: 550 match 131.155.210 +550 match 131.155.210 +>>> client_restrictions permit_mynetworks +OK +>>> # +>>> # Test the helo restrictions +>>> # +>>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,reject_unknown_hostname,hash:./smtpd_check_access +OK +>>> client unknown 131.155.210.17 +OK +>>> helo foo. +./smtpd_check: reject: HELO from unknown[131.155.210.17]: 450 Cannot find your hostname, [131.155.210.17] +450 Cannot find your hostname, [131.155.210.17] +>>> client foo 123.123.123.123 +OK +>>> helo foo. +./smtpd_check: reject: HELO from foo[123.123.123.123]: 450 : Host not found +450 : Host not found +>>> helo foo +./smtpd_check: reject: HELO from foo[123.123.123.123]: 450 : Host not found +450 : Host not found +>>> helo spike.porcupine.org +OK +>>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,hash:./smtpd_check_access +OK +>>> helo random.bad.domain +./smtpd_check: reject: HELO from foo[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> helo friend.bad.domain +OK +>>> helo_restrictions reject_invalid_hostname,reject_unknown_hostname +OK +>>> helo 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 +OK +>>> helo 123.123.123.123 +OK +>>> # +>>> # Test the sender restrictions +>>> # +>>> sender_restrictions permit_mynetworks,reject_unknown_client +OK +>>> client unknown 131.155.210.17 +OK +>>> mail foo@watson.ibm.com +./smtpd_check: reject: MAIL from unknown[131.155.210.17]: 450 Cannot find your hostname, [131.155.210.17] +450 Cannot find your hostname, [131.155.210.17] +>>> client unknown 168.100.189.13 +OK +>>> mail foo@watson.ibm.com +OK +>>> client foo 123.123.123.123 +OK +>>> mail foo@watson.ibm.com +OK +>>> sender_restrictions reject_unknown_address +OK +>>> mail foo@watson.ibm.com +OK +>>> mail foo@bad.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 450 : Domain not found +450 : Domain not found +>>> sender_restrictions hash:./smtpd_check_access +OK +>>> mail bad-sender@any.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad-sender@ +550 match bad-sender@ +>>> mail bad-sender@good.domain +OK +>>> mail reject@this.address +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match reject@this.address +550 match reject@this.address +>>> mail Reject@this.address +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match reject@this.address +550 match reject@this.address +>>> mail foo@bad.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> mail foo@Bad.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> mail foo@random.bad.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> mail foo@friend.bad.domain +OK +>>> # +>>> # Test the recipient restrictions +>>> # +>>> recipient_restrictions permit_mynetworks,reject_unknown_client,check_relay_domains +OK +>>> client unknown 131.155.210.17 +OK +>>> rcpt foo@watson.ibm.com +./smtpd_check: reject: RCPT from unknown[131.155.210.17]: 450 Cannot find your hostname, [131.155.210.17] +450 Cannot find your hostname, [131.155.210.17] +>>> client unknown 168.100.189.13 +OK +>>> rcpt foo@watson.ibm.com +OK +>>> client foo 123.123.123.123 +OK +>>> rcpt foo@watson.ibm.com +./smtpd_check: reject: RCPT from foo[123.123.123.123]: 550 : Relay access denied +550 : Relay access denied +>>> rcpt foo@porcupine.org +OK +>>> recipient_restrictions check_relay_domains +OK +>>> client foo.porcupine.org 168.100.189.13 +OK +>>> rcpt foo@watson.ibm.com +OK +>>> rcpt foo@porcupine.org +OK +>>> client foo 123.123.123.123 +OK +>>> rcpt foo@watson.ibm.com +./smtpd_check: reject: RCPT from foo[123.123.123.123]: 550 : Relay access denied +550 : Relay access denied +>>> rcpt foo@porcupine.org +OK +>>> recipient_restrictions hash:./smtpd_check_access +OK +>>> mail bad-sender@any.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad-sender@ +550 match bad-sender@ +>>> mail bad-sender@good.domain +OK +>>> mail reject@this.address +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match reject@this.address +550 match reject@this.address +>>> mail foo@bad.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> mail foo@random.bad.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> mail foo@friend.bad.domain +OK +>>> # +>>> # RBL +>>> # +>>> client_restrictions reject_maps_rbl +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 +>>> # +>>> # Hybrids +>>> # +>>> recipient_restrictions check_relay_domains +OK +>>> client foo 131.155.210.17 +OK +>>> rcpt foo@watson.ibm.com +./smtpd_check: reject: RCPT from foo[131.155.210.17]: 550 : Relay access denied +550 : Relay access denied +>>> recipient_restrictions check_client_access,hash:./smtpd_check_access,check_relay_domains +OK +>>> client foo 131.155.210.17 +OK +>>> rcpt foo@porcupine.org +OK +>>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,hash:./smtpd_check_access +OK +>>> recipient_restrictions check_helo_access,hash:./smtpd_check_access,check_relay_domains +OK +>>> helo bad.domain +./smtpd_check: reject: HELO from foo[131.155.210.17]: 550 match bad.domain +550 match bad.domain +>>> rcpt foo@porcupine.org +./smtpd_check: reject: RCPT from foo[131.155.210.17]: 550 match bad.domain +550 match bad.domain +>>> helo 131.155.210.17 +OK +>>> rcpt foo@porcupine.org +OK +>>> recipient_restrictions check_sender_access,hash:./smtpd_check_access,check_relay_domains +OK +>>> mail foo@bad.domain +./smtpd_check: reject: MAIL from foo[131.155.210.17]: 550 match bad.domain +550 match bad.domain +>>> rcpt foo@porcupine.org +./smtpd_check: reject: RCPT from foo[131.155.210.17]: 550 match bad.domain +550 match bad.domain +>>> mail foo@friend.bad.domain +OK +>>> rcpt foo@porcupine.org +OK +>>> # +>>> # MX backup +>>> # +>>> mydestination spike.porcupine.org,localhost.porcupine.org +OK +>>> inet_interfaces 168.100.189.2,127.0.0.1 +OK +>>> recipient_restrictions permit_mx_backup,reject +OK +>>> rcpt wietse@wzv.win.tue.nl +OK +>>> rcpt wietse@trouble.org +./smtpd_check: reject: RCPT from foo[131.155.210.17]: 550 Access denied +550 Access denied +>>> rcpt wietse@porcupine.org +OK diff --git a/postfix/smtpd/smtpd_check.ref2 b/postfix/smtpd/smtpd_check.ref2 new file mode 100644 index 000000000..82f89c184 --- /dev/null +++ b/postfix/smtpd/smtpd_check.ref2 @@ -0,0 +1,175 @@ +>>> # +>>> # Initialize. +>>> # +>>> ! ../bin/postmap smtpd_check_access +exit 0 +>>> #msg_verbose 1 +>>> mynetworks 127.0.0.0/8,168.100.189.0/28 +OK +>>> relay_domains porcupine.org +OK +>>> # +>>> # Test the client restrictions. +>>> # +>>> client_restrictions permit_mynetworks,reject_unknown_client,check_client_access,hash:./smtpd_check_access +OK +>>> client unknown 131.155.210.17 +./smtpd_check: reject: CONNECT from unknown[131.155.210.17]: 450 Cannot find your hostname, [131.155.210.17] +450 Cannot find your hostname, [131.155.210.17] +>>> client unknown 168.100.189.13 +OK +>>> client random.bad.domain 123.123.123.123 +./smtpd_check: reject: CONNECT from random.bad.domain[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> client friend.bad.domain 123.123.123.123 +OK +>>> client bad.domain 123.123.123.123 +./smtpd_check: reject: CONNECT from bad.domain[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> client wzv.win.tue.nl 131.155.210.17 +OK +>>> client aa.win.tue.nl 131.155.210.18 +./smtpd_check: reject: CONNECT from aa.win.tue.nl[131.155.210.18]: 550 match 131.155.210 +550 match 131.155.210 +>>> client_restrictions permit_mynetworks +OK +>>> # +>>> # Test the helo restrictions +>>> # +>>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,reject_unknown_hostname,check_helo_access,hash:./smtpd_check_access +OK +>>> client unknown 131.155.210.17 +OK +>>> helo foo. +./smtpd_check: reject: HELO from unknown[131.155.210.17]: 450 Cannot find your hostname, [131.155.210.17] +450 Cannot find your hostname, [131.155.210.17] +>>> client foo 123.123.123.123 +OK +>>> helo foo. +./smtpd_check: reject: HELO from foo[123.123.123.123]: 450 : Host not found +450 : Host not found +>>> helo foo +./smtpd_check: reject: HELO from foo[123.123.123.123]: 450 : Host not found +450 : Host not found +>>> helo spike.porcupine.org +OK +>>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,check_helo_access,hash:./smtpd_check_access +OK +>>> helo random.bad.domain +./smtpd_check: reject: HELO from foo[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> helo friend.bad.domain +OK +>>> # +>>> # Test the sender restrictions +>>> # +>>> sender_restrictions permit_mynetworks,reject_unknown_client +OK +>>> client unknown 131.155.210.17 +OK +>>> mail foo@watson.ibm.com +./smtpd_check: reject: MAIL from unknown[131.155.210.17]: 450 Cannot find your hostname, [131.155.210.17] +450 Cannot find your hostname, [131.155.210.17] +>>> client unknown 168.100.189.13 +OK +>>> mail foo@watson.ibm.com +OK +>>> client foo 123.123.123.123 +OK +>>> mail foo@watson.ibm.com +OK +>>> sender_restrictions reject_unknown_address +OK +>>> mail foo@watson.ibm.com +OK +>>> mail foo@bad.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 450 : Domain not found +450 : Domain not found +>>> sender_restrictions check_sender_access,hash:./smtpd_check_access +OK +>>> mail bad-sender@any.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad-sender@ +550 match bad-sender@ +>>> mail bad-sender@good.domain +OK +>>> mail reject@this.address +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match reject@this.address +550 match reject@this.address +>>> mail Reject@this.address +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match reject@this.address +550 match reject@this.address +>>> mail foo@bad.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> mail foo@Bad.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> mail foo@random.bad.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> mail foo@friend.bad.domain +OK +>>> # +>>> # Test the recipient restrictions +>>> # +>>> recipient_restrictions permit_mynetworks,reject_unknown_client,check_relay_domains +OK +>>> client unknown 131.155.210.17 +OK +>>> rcpt foo@watson.ibm.com +./smtpd_check: reject: RCPT from unknown[131.155.210.17]: 450 Cannot find your hostname, [131.155.210.17] +450 Cannot find your hostname, [131.155.210.17] +>>> client unknown 168.100.189.13 +OK +>>> rcpt foo@watson.ibm.com +OK +>>> client foo 123.123.123.123 +OK +>>> rcpt foo@watson.ibm.com +./smtpd_check: reject: RCPT from foo[123.123.123.123]: 550 : Relay access denied +550 : Relay access denied +>>> rcpt foo@porcupine.org +OK +>>> recipient_restrictions check_relay_domains +OK +>>> client foo.porcupine.org 168.100.189.13 +OK +>>> rcpt foo@watson.ibm.com +OK +>>> rcpt foo@porcupine.org +OK +>>> client foo 123.123.123.123 +OK +>>> rcpt foo@watson.ibm.com +./smtpd_check: reject: RCPT from foo[123.123.123.123]: 550 : Relay access denied +550 : Relay access denied +>>> rcpt foo@porcupine.org +OK +>>> recipient_restrictions check_recipient_access,hash:./smtpd_check_access +OK +>>> mail bad-sender@any.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad-sender@ +550 match bad-sender@ +>>> mail bad-sender@good.domain +OK +>>> mail reject@this.address +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match reject@this.address +550 match reject@this.address +>>> mail foo@bad.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> mail foo@random.bad.domain +./smtpd_check: reject: MAIL from foo[123.123.123.123]: 550 match bad.domain +550 match bad.domain +>>> mail foo@friend.bad.domain +OK +>>> # +>>> # RBL +>>> # +>>> client_restrictions reject_maps_rbl +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 diff --git a/postfix/smtpd/smtpd_check_access b/postfix/smtpd/smtpd_check_access new file mode 100644 index 000000000..fddcd3ce1 --- /dev/null +++ b/postfix/smtpd/smtpd_check_access @@ -0,0 +1,7 @@ +bad.domain 550 match bad.domain +friend.bad.domain OK +bad-sender@ 550 match bad-sender@ +bad-sender@good.domain OK +131.155.210 550 match 131.155.210 +131.155.210.17 OK +reject@this.address 550 match reject@this.address diff --git a/postfix/smtpd/smtpd_state.c b/postfix/smtpd/smtpd_state.c new file mode 100644 index 000000000..27a08437d --- /dev/null +++ b/postfix/smtpd/smtpd_state.c @@ -0,0 +1,122 @@ +/*++ +/* NAME +/* smtpd_state 3 +/* SUMMARY +/* Postfix SMTP server +/* SYNOPSIS +/* #include "smtpd.h" +/* +/* void smtpd_state_init(state, stream, name, addr) +/* SMTPD_STATE *state; +/* VSTREAM *stream; +/* const char *name; +/* const char *addr; +/* +/* void smtpd_state_reset(state) +/* SMTPD_STATE *state; +/* DESCRIPTION +/* smtpd_state_init() initializes session context. +/* +/* smtpd_state_reset() cleans up session context. +/* +/* Arguments: +/* .IP state +/* Session context. +/* .IP stream +/* Stream connected to peer. The stream is not copied. +/* .IP name +/* Printable representation of the peer host name. The +/* name is copied. +/* .IP addr +/* Printable representation of the peer host address. The +/* address is copied. +/* DIAGNOSTICS +/* All errors are fatal. +/* 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 + +/* Global library. */ + +#include +#include +#include + +/* Application-specific. */ + +#include "smtpd.h" +#include "smtpd_chat.h" + +/* smtpd_state_init - initialize after connection establishment */ + +void smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream, + const char *name, const char *addr) +{ + + /* + * Initialize the state information for this connection, and fill in the + * connection-specific fields. + */ + state->err = CLEANUP_STAT_OK; + state->client = stream; + state->buffer = vstring_alloc(100); + state->time = event_time(); + state->name = mystrdup(name); + state->addr = mystrdup(addr); + state->error_count = 0; + state->error_mask = 0; + state->notify_mask = name_mask(mail_error_masks, var_notify_classes); + state->helo_name = 0; + state->queue_id = 0; + state->cleanup = 0; + state->dest = 0; + state->rcpt_count = 0; + state->access_denied = 0; + state->history = 0; + state->reason = 0; + state->sender = 0; + state->recipient = 0; + state->protocol = "SMTP"; + state->where = SMTPD_AFTER_CONNECT; + + /* + * Initialize the conversation history. + */ + smtpd_chat_reset(state); +} + +/* smtpd_state_reset - cleanup after disconnect */ + +void smtpd_state_reset(SMTPD_STATE *state) +{ + + /* + * When cleaning up, touch only those fields that smtpd_state_init() + * filled in. The other fields are taken care of by their own + * "destructor" functions. + */ + if (state->buffer) + vstring_free(state->buffer); + if (state->name) + myfree(state->name); + if (state->addr) + myfree(state->addr); +} diff --git a/postfix/smtpd/smtpd_token.c b/postfix/smtpd/smtpd_token.c new file mode 100644 index 000000000..846cfc403 --- /dev/null +++ b/postfix/smtpd/smtpd_token.c @@ -0,0 +1,214 @@ +/*++ +/* NAME +/* smtpd_token 3 +/* SUMMARY +/* tokenize SMTPD command +/* SYNOPSIS +/* #include +/* +/* typedef struct { + int tokval; + char *strval; +/* } SMTPD_TOKEN; +/* +/* int smtpd_token(str, argvp) +/* char *str; +/* SMTPD_TOKEN **argvp; +/* DESCRIPTION +/* smtpd_token() routine converts the string in \fIstr\fR to an +/* array of tokens in \fIargvp\fR. The number of tokens is returned +/* via the function return value. +/* +/* Token types: +/* .IP SMTPD_TOK_ADDR +/* The token is of the form , not including the angle brackets. +/* .IP SMTPD_TOK_OTHER +/* The token is something else. +/* .IP SMTPD_TOK_ERROR +/* A malformed token. +/* BUGS +/* This tokenizer understands just enough to tokenize SMTPD commands. +/* It understands backslash escapes, white space, quoted strings, +/* and addresses (including quoted text) enclosed by < and >. Any +/* other sequence of characters is lumped together as one token. +/* 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 + +/* Utility library. */ + +#include +#include + +/* Application-specific. */ + +#include "smtpd_token.h" + + /* + * Macros to make complex code more readable. + */ +#define COLLECT(cp,vp,c,cond) { \ + while ((c = *cp) != 0) { \ + if (c == '\\') { \ + cp++; \ + if ((c = *cp) == 0) \ + break; \ + } else if (!(cond)) { \ + break; \ + } \ + cp++; \ + VSTRING_ADDCH(vp, c); \ + } \ + } + +/* smtp_quoted - read until closing quote */ + +static char *smtp_quoted(char *cp, SMTPD_TOKEN *arg, int last) +{ + int c; + + while ((c = *cp) != 0) { + cp++; + if (c == '\\') { /* parse escape sequence */ + if ((c = *cp) == 0) + break; /* end of input, punt */ + cp++; + VSTRING_ADDCH(arg->vstrval, c); /* store escaped character */ + } else if (c == last) { + return (cp); /* closing quote */ + } else if (c == '"') { + cp = smtp_quoted(cp, arg, c); /* recurse */ + } else { + VSTRING_ADDCH(arg->vstrval, c); /* store character */ + } + } + arg->tokval = SMTPD_TOK_ERROR; /* missing end */ + return (cp); +} + +/* smtp_next_token - extract next token from input, update cp */ + +static char *smtp_next_token(char *cp, SMTPD_TOKEN *arg) +{ + char *special = "<[\">]:"; + int c; + + VSTRING_RESET(arg->vstrval); + arg->tokval = SMTPD_TOK_OTHER; + + for (;;) { + if ((c = *cp++) == 0) { + return (0); + } else if (ISSPACE(c)) { /* whitespace, skip */ + while (ISSPACE(*cp)) + cp++; + continue; + } else if (c == '<') { /* */ + arg->tokval = SMTPD_TOK_ADDR; + cp = smtp_quoted(cp, arg, '>'); + break; + } else if (c == '[') { /* [stuff], keep [] */ + VSTRING_ADDCH(arg->vstrval, c); + cp = smtp_quoted(cp, arg, ']'); + if (cp[-1] == ']') + VSTRING_ADDCH(arg->vstrval, ']'); + break; + } else if (c == '"') { /* string */ + cp = smtp_quoted(cp, arg, c); + break; + } else if (ISCNTRL(c) || strchr(special, c)) { + VSTRING_ADDCH(arg->vstrval, c); + break; + } else { /* other */ + if (c == '\\') + if ((c = *cp) == 0) + break; + VSTRING_ADDCH(arg->vstrval, c); + COLLECT(cp, arg->vstrval, c, + !ISSPACE(c) && !ISCNTRL(c) && !strchr(special, c)); + break; + } + } + VSTRING_TERMINATE(arg->vstrval); + arg->strval = vstring_str(arg->vstrval); + return (cp); +} + +/* smtpd_token_init - initialize token structures */ + +static void smtpd_token_init(char *ptr, int count) +{ + SMTPD_TOKEN *arg; + int n; + + for (arg = (SMTPD_TOKEN *) ptr, n = 0; n < count; arg++, n++) + arg->vstrval = vstring_alloc(10); +} + +/* smtpd_token - tokenize SMTPD command */ + +int smtpd_token(char *cp, SMTPD_TOKEN **argvp) +{ + static SMTPD_TOKEN *smtp_argv; + static MVECT mvect; + int n; + + if (smtp_argv == 0) + smtp_argv = (SMTPD_TOKEN *) mvect_alloc(&mvect, sizeof(*smtp_argv), 1, + smtpd_token_init, (MVECT_FN) 0); + for (n = 0; /* void */ ; n++) { + smtp_argv = (SMTPD_TOKEN *) mvect_realloc(&mvect, n + 1); + if ((cp = smtp_next_token(cp, smtp_argv + n)) == 0) + break; + } + *argvp = smtp_argv; + return (n); +} + +#ifdef TEST + + /* + * Test program for the SMTPD command tokenizer. + */ + +#include +#include +#include + +main(int unused_argc, char **unused_argv) +{ + VSTRING *vp = vstring_alloc(10); + int tok_argc; + SMTPD_TOKEN *tok_argv; + int i; + + for (;;) { + vstream_printf("enter SMTPD command: "); + vstream_fflush(VSTREAM_OUT); + if (vstring_fgets(vp, VSTREAM_IN) == 0) + break; + tok_argc = smtpd_token(vstring_str(vp), &tok_argv); + for (i = 0; i < tok_argc; i++) { + vstream_printf("Token type: %s\n", + tok_argv[i].tokval == SMTPD_TOK_ADDR ? + "address" : "other"); + vstream_printf("Token value: %s\n", tok_argv[i].strval); + } + } + exit(0); +} + +#endif diff --git a/postfix/smtpd/smtpd_token.h b/postfix/smtpd/smtpd_token.h new file mode 100644 index 000000000..88489fa22 --- /dev/null +++ b/postfix/smtpd/smtpd_token.h @@ -0,0 +1,40 @@ +/*++ +/* NAME +/* smtpd_token 3h +/* SUMMARY +/* tokenize SMTPD command +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +typedef struct SMTPD_TOKEN { + int tokval; + char *strval; + VSTRING *vstrval; +} SMTPD_TOKEN; + +#define SMTPD_TOK_OTHER 0 +#define SMTPD_TOK_ADDR 1 +#define SMTPD_TOK_ERROR 2 + +extern int smtpd_token(char *, SMTPD_TOKEN **); + +/* 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 +/*--*/ diff --git a/postfix/smtpstone/.indent.pro b/postfix/smtpstone/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/smtpstone/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/smtpstone/.printfck b/postfix/smtpstone/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/smtpstone/.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/smtpstone/Makefile.in b/postfix/smtpstone/Makefile.in new file mode 100644 index 000000000..c7e8eb6af --- /dev/null +++ b/postfix/smtpstone/Makefile.in @@ -0,0 +1,93 @@ +SHELL = /bin/sh +SRCS = smtp-source.c smtp-sink.c +OBJS = smtp-source.o smtp-sink.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= +INC_DIR = ../include +PROG = smtp-source smtp-sink +LIBS = ../lib/libglobal.a ../lib/libutil.a + +.c.o:; $(CC) $(CFLAGS) -c $*.c + +all: $(PROG) + +Makefile: Makefile.in + (set -e; echo "# DO NOT EDIT"; $(OPTS) sh ../makedefs; cat $?) >$@ + +smtp-sink: smtp-sink.o $(LIBS) + $(CC) $(CFLAGS) -o $@ smtp-sink.o $(LIBS) $(SYSLIBS) + +smtp-source: smtp-source.o $(LIBS) + $(CC) $(CFLAGS) -o $@ smtp-source.o $(LIBS) $(SYSLIBS) + +test: $(TESTPROG) + +update: ../bin/smtp-source ../bin/smtp-sink + +../bin/smtp-source: smtp-source + cp $? $@ + +../bin/smtp-sink: smtp-sink + cp $? $@ + +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' +smtp-sink.o: smtp-sink.c +smtp-sink.o: ../include/sys_defs.h +smtp-sink.o: ../include/msg.h +smtp-sink.o: ../include/vstring.h +smtp-sink.o: ../include/vbuf.h +smtp-sink.o: ../include/vstream.h +smtp-sink.o: ../include/vstring_vstream.h +smtp-sink.o: ../include/get_hostname.h +smtp-sink.o: ../include/listen.h +smtp-sink.o: ../include/iostuff.h +smtp-sink.o: ../include/events.h +smtp-sink.o: ../include/mymalloc.h +smtp-sink.o: ../include/msg_vstream.h +smtp-sink.o: ../include/smtp_stream.h +smtp-source.o: smtp-source.c +smtp-source.o: ../include/sys_defs.h +smtp-source.o: ../include/msg.h +smtp-source.o: ../include/vstring.h +smtp-source.o: ../include/vbuf.h +smtp-source.o: ../include/vstream.h +smtp-source.o: ../include/vstring_vstream.h +smtp-source.o: ../include/get_hostname.h +smtp-source.o: ../include/split_at.h +smtp-source.o: ../include/connect.h +smtp-source.o: ../include/iostuff.h +smtp-source.o: ../include/mymalloc.h +smtp-source.o: ../include/events.h +smtp-source.o: ../include/find_inet.h +smtp-source.o: ../include/smtp_stream.h +smtp-source.o: ../include/mail_date.h diff --git a/postfix/smtpstone/mx-deliver b/postfix/smtpstone/mx-deliver new file mode 100644 index 000000000..a4f44a03e --- /dev/null +++ b/postfix/smtpstone/mx-deliver @@ -0,0 +1,20 @@ +MX needs 24 seconds to deliver 100 SMTP messages to one local user. +smtpd/local-delivery process limit = 100 + +/usr/bin/time ./smtp-source -s 5 -m 100 -t wietse@fist.porcupine.org fist + 10.47 real 0.12 user 0.16 sys +Jun 8 14:45:25 fist mx:smtpd[19432]: connect from spike.porcupine.org(168.100.1 +Jun 8 14:45:49 fist mx:local[19444]: 085788: to=, relay=l +Total time: 24 seconds + +/usr/bin/time ./smtp-source -s 10 -m 100 -t wietse@fist.porcupine.org fist + 9.10 real 0.06 user 0.26 sys +Jun 8 14:46:42 fist mx:smtpd[19443]: connect from spike.porcupine.org(168.100.1 +Jun 8 14:47:06 fist mx:local[19446]: 085792: to=, relay=l +Total time: 24 seconds + +/usr/bin/time ./smtp-source -s 20 -m 100 -t wietse@fist.porcupine.org fist + 9.84 real 0.09 user 0.28 sys +Jun 8 14:48:03 fist mx:smtpd[19458]: connect from spike.porcupine.org(168.100.1 +Jun 8 14:48:28 fist mx:local[19479]: 085795: to=, relay=l +Total time: 25 seconds diff --git a/postfix/smtpstone/mx-explode b/postfix/smtpstone/mx-explode new file mode 100644 index 000000000..e99714738 --- /dev/null +++ b/postfix/smtpstone/mx-explode @@ -0,0 +1,33 @@ +MX needs 12 seconds per 1000 different remote destinations. +smtp process limit = 100, bundle_recipients = 0. + +/usr/bin/time ./smtp-source -r 1000 fist + 1.13 real 0.07 user 0.27 sys +Jun 8 13:32:18 fist mx:smtpd[18174]: connect from spike.porcupine.org(168.100.1 +Jun 8 13:32:31 fist mx:smtp[18209]: 085688: to=<544foo@spike.porcupine.org>, re +Total time: 13 seconds + +/usr/bin/time ./smtp-source -r 2000 fist + 2.55 real 0.21 user 0.48 sys +Jun 8 13:33:23 fist mx:smtpd[18174]: connect from spike.porcupine.org(168.100.1 +Jun 8 13:33:48 fist mx:smtp[18184]: 085693: to=<1041foo@spike.porcupine.org>, r +Total time: 25 seconds + +/usr/bin/time ./smtp-source -r 5000 fist +[test generating machine ran out of resources while receiving mail] + +/usr/bin/time ./smtp-source -r 1000 fist + 1.38 real 0.17 user 0.16 sys +Jun 8 15:20:33 fist mx:smtpd[27695]: connect from spike.porcupine.org(168.100.1 +Jun 8 15:20:46 fist mx:smtp[27724]: 085687: to=<493foo@spike.porcupine.org>, re +Total time: 13 seconds + +/usr/bin/time ./smtp-source -r 2000 fist + 2.64 real 0.23 user 0.46 sys +Jun 8 15:20:52 fist mx:smtpd[27695]: connect from spike.porcupine.org(168.100.1 +Jun 8 15:21:16 fist mx:smtp[27743]: 085687: to=<1086foo@spike.porcupine.org>, r +Total time: 24 seconds + +/usr/bin/time ./smtp-source -r 5000 fist +[test generating machine ran out of resources while receiving mail] + diff --git a/postfix/smtpstone/mx-relay b/postfix/smtpstone/mx-relay new file mode 100644 index 000000000..a5930cb44 --- /dev/null +++ b/postfix/smtpstone/mx-relay @@ -0,0 +1,20 @@ +MX needs 19 seconds to relay 100 messages with one recipient. +smtp/smtpd process limit = 100, bundle_recipients = 0. + +/usr/bin/time ./smtp-source -s 5 -m 100 fist + 9.56 real 0.07 user 0.20 sys +Jun 8 14:33:19 fist mx:smtpd[19366]: connect from spike.porcupine.org(168.100.1 +Jun 8 14:33:36 fist mx:smtp[19382]: 085781: to=, relay +Total time: 17 seconds + +/usr/bin/time ./smtp-source -s 10 -m 100 fist + 8.95 real 0.12 user 0.19 sys +Jun 8 14:34:22 fist mx:smtpd[19377]: connect from spike.porcupine.org(168.100.1 +Jun 8 14:34:41 fist mx:smtp[19378]: 085792: to=, relay +Total time: 19 seconds + +/usr/bin/time ./smtp-source -s 20 -m 100 fist + 9.79 real 0.11 user 0.27 sys +Jun 8 14:35:18 fist mx:smtpd[19377]: connect from spike.porcupine.org(168.100.1 +Jun 8 14:35:38 fist mx:smtp[19382]: 085794: to=, relay +Total time: 20 seconds diff --git a/postfix/smtpstone/performance b/postfix/smtpstone/performance new file mode 100644 index 000000000..805676ff6 --- /dev/null +++ b/postfix/smtpstone/performance @@ -0,0 +1,28 @@ +List performance: time to forward one SMTP message with 1000, 2000 +and 5000 different remote destinations. Outbound SMTP concurrency += 100. + +dests 1000 2000 5000 time per 1000 +============================================= +qmail 15 32 80 16 +mx 13 25 (*) 13 + +(*) message sink host saturated under the load + +Local delivery performance: time to deliver 100 SMTP messages to +one recipient. Outbound SMTP concurrency = 100, inbound SMTP +concurrency = 5, 10, 20. + +concur 5 10 20 average time +============================================ +qmail 62 59 58 60 +mx 24 24 25 24 + +Relay performance: time to forward 100 SMTP messages with one +recipient. Outbound SMTP concurrency = 100, inbound SMTP concurrency += 5, 10, 20. + +concur 5 10 20 average time +============================================ +qmail 56 54 54 55 +mx 17 19 20 19 diff --git a/postfix/smtpstone/qmail-deliver b/postfix/smtpstone/qmail-deliver new file mode 100644 index 000000000..e00ab3938 --- /dev/null +++ b/postfix/smtpstone/qmail-deliver @@ -0,0 +1,20 @@ +Qmail needs 59 seconds to deliver 100 SMTP messages to one local recipient. +Default configuration, concurrencyremote = 100. + +/usr/bin/time ./smtp-source -s 5 -m 100 -t wietse fist + 41.45 real 0.07 user 0.21 sys +Jun 8 12:17:20 fist qmail: 865786640.494072 new msg 39901 +Jun 8 12:18:22 fist qmail: 865786702.301087 end msg 39982 +Total time: 62 sec + +/usr/bin/time ./smtp-source -s 10 -m 100 -t wietse fist + 26.50 real 0.10 user 0.22 sys +Jun 8 12:14:49 fist qmail: 865786489.089492 new msg 39901 +Jun 8 12:15:48 fist qmail: 865786548.316898 end msg 39928 +Total time: 59 sec + +/usr/bin/time ./smtp-source -s 20 -m 100 -t wietse fist + 21.15 real 0.08 user 0.30 sys +Jun 8 12:19:18 fist qmail: 865786758.939755 new msg 39903 +Jun 8 12:20:16 fist qmail: 865786816.739912 end msg 40031 +Total time: 58 sec diff --git a/postfix/smtpstone/qmail-explode b/postfix/smtpstone/qmail-explode new file mode 100644 index 000000000..ed40e2c3b --- /dev/null +++ b/postfix/smtpstone/qmail-explode @@ -0,0 +1,20 @@ +Qmail needs 16 seconds per 1000 destinations. +Default configuration, concurrencyremote = 100. + +/usr/bin/time ./smtp-source -r 1000 fist + 1.16 real 0.09 user 0.24 sys +Jun 8 14:57:17 fist qmail: 865796237.334359 new msg 39906 +Jun 8 14:57:32 fist qmail: 865796252.632756 delivery 2154: success: 168.100.189 +Total time: 15 seconds + +/usr/bin/time ./smtp-source -r 2000 fist + 1.99 real 0.23 user 0.45 sys +Jun 8 14:58:11 fist qmail: 865796291.817523 new msg 39907 +Jun 8 14:58:43 fist qmail: 865796323.174117 delivery 4116: success: 168.100.189 +Total time: 32 seconds + +/usr/bin/time ./smtp-source -r 5000 fist + 4.63 real 0.58 user 1.10 sys +Jun 8 14:59:23 fist qmail: 865796363.346735 new msg 39908 +Jun 8 15:00:43 fist qmail: 865796443.209168 delivery 9153: success: 168.100.189 +Total time: 80 seconds diff --git a/postfix/smtpstone/qmail-relay b/postfix/smtpstone/qmail-relay new file mode 100644 index 000000000..8718e5e45 --- /dev/null +++ b/postfix/smtpstone/qmail-relay @@ -0,0 +1,20 @@ +Qmail needs 54 seconds to relay 100 messages with one recipient. +Default configuration, concurrencyremote = 100. + +spike_4% /usr/bin/time ./smtp-source -s 5 -m 100 fist + 43.77 real 0.05 user 0.23 sys +Jun 8 12:08:36 fist qmail: 865786116.744366 new msg 39901 +Jun 8 12:09:32 fist qmail: 865786172.791473 end msg 39921 +Total time: 56 sec + +/usr/bin/time ./smtp-source -s 10 -m 100 fist + 26.66 real 0.06 user 0.26 sys +Jun 8 12:06:20 fist qmail: 865785980.185885 new msg 39901 +Jun 8 12:07:14 fist qmail: 865786034.306429 end msg 39920 +Total time: 54 sec + +spike_8% /usr/bin/time ./smtp-source -s 20 -m 100 fist + 20.94 real 0.11 user 0.27 sys +Jun 8 12:10:52 fist qmail: 865786252.412648 new msg 39901 +Jun 8 12:11:46 fist qmail: 865786306.080605 end msg 39962 +Total time: 54 sec diff --git a/postfix/smtpstone/smtp-sink.c b/postfix/smtpstone/smtp-sink.c new file mode 100644 index 000000000..025b1321d --- /dev/null +++ b/postfix/smtpstone/smtp-sink.c @@ -0,0 +1,325 @@ +/*++ +/* NAME +/* smtp-sink 8 +/* SUMMARY +/* smtp test server +/* SYNOPSIS +/* smtp-sink [-c] [-v] [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. +/* 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 -v +/* Show the SMTP conversations. +/* SEE ALSO +/* smtp-source, SMTP test message generator +/* 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 +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include + +/* Application-specific. */ + +struct data_state { + VSTREAM *stream; + int state; +}; + +#define ST_ANY 0 +#define ST_CR 1 +#define ST_CR_LF 2 +#define ST_CR_LF_DOT 3 +#define ST_CR_LF_DOT_CR 4 +#define ST_CR_LF_DOT_CR_LF 5 + +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 count; +static int counter; + +/* helo - process HELO/EHLO command */ + +static void helo(VSTREAM *stream) +{ + smtp_printf(stream, "250-%s", var_myhostname); + smtp_printf(stream, "250-8BITMIME"); + smtp_printf(stream, "250 PIPELINING"); +} + +/* ok - send 250 OK */ + +static void ok(VSTREAM *stream) +{ + smtp_printf(stream, "250 Ok"); +} + +/* data_read - read data from socket */ + +static void data_read(int unused_event, char *context) +{ + struct data_state *dstate = (struct data_state *) context; + VSTREAM *stream = dstate->stream; + int avail; + int ch; + struct data_trans { + int state; + int want; + int next_state; + }; + static struct data_trans data_trans[] = { + ST_ANY, '\r', ST_CR, + ST_CR, '\n', ST_CR_LF, + ST_CR_LF, '.', ST_CR_LF_DOT, + ST_CR_LF_DOT, '\r', ST_CR_LF_DOT_CR, + ST_CR_LF_DOT_CR, '\n', ST_CR_LF_DOT_CR_LF, + }; + 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++) + /* void */ ; + + /* + * Try to match the current character desired by the state machine. + * If that fails, try to restart the machine with a match for its + * first state. This covers the case of a CR/LF/CR/LF sequence + * (empty line) right before the end of the message data. + */ + if (ch == dp->want) + dstate->state = dp->next_state; + else if (ch == data_trans[0].want) + dstate->state = data_trans[0].next_state; + else + dstate->state = ST_ANY; + if (dstate->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); + } + } +} + +/* 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); + } +} + + /* + * The table of all SMTP commands that we can handle. + */ +typedef struct 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, + 0, +}; + +/* command_read - talk the SMTP protocol, server side */ + +static void command_read(int unused_event, char *context) +{ + VSTREAM *stream = (VSTREAM *) context; + char *command; + COMMAND *cmdp; + + switch (setjmp(smtp_timeout_buf)) { + + default: + msg_panic("unknown error reading input"); + + case SMTP_ERR_TIME: + smtp_printf(stream, "421 Error: timeout exceeded"); + msg_warn("timeout reading input"); + disconnected(stream); + break; + + case SMTP_ERR_EOF: + msg_warn("lost connection"); + disconnected(stream); + break; + + 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; + } + cmdp->action(stream); + break; + } +} + +/* disconnected - handle disconnection events */ + +static void disconnected(VSTREAM *stream) +{ + if (msg_verbose) + msg_info("disconnect"); + event_disable_readwrite(vstream_fileno(stream)); + vstream_fclose(stream); +} + +/* connected - connection established */ + +static void connected(int unused_event, char *context) +{ + int sock = (int) context; + VSTREAM *stream; + 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); + } +} + +/* usage - explain */ + +static void usage(char *myname) +{ + msg_fatal("usage: %s [-c] [-v] [host]:port backlog", myname); +} + +int main(int argc, char **argv) +{ + int sock; + int backlog; + int ch; + + /* + * Initialize diagnostics. + */ + msg_vstream_init(argv[0], VSTREAM_ERR); + + /* + * Parse JCL. + */ + while ((ch = GETOPT(argc, argv, "cv")) > 0) { + switch (ch) { + case 'c': + count++; + break; + case 'v': + msg_verbose++; + break; + default: + usage(argv[0]); + } + } + if (argc - optind != 2) + usage(argv[0]); + if ((backlog = atoi(argv[optind + 1])) <= 0) + usage(argv[0]); + + /* + * Initialize. + */ + buffer = vstring_alloc(1024); + var_myhostname = "smtp-sink"; + sock = inet_listen(argv[optind], backlog, BLOCKING); + + /* + * Start the event handler. + */ + event_enable_read(sock, connected, (char *) sock); + for (;;) + event_loop(-1); +} diff --git a/postfix/smtpstone/smtp-source.c b/postfix/smtpstone/smtp-source.c new file mode 100644 index 000000000..89780caac --- /dev/null +++ b/postfix/smtpstone/smtp-source.c @@ -0,0 +1,692 @@ +/*++ +/* NAME +/* smtp-source 8 +/* SUMMARY +/* SMTP test generator +/* SYNOPSIS +/* smtp-source [options] host[:port] +/* DESCRIPTION +/* smtp-source connects to the named host and port (default 25) +/* and sends one or more little messages to it, either sequentially +/* or in parallel. +/* +/* Options: +/* .IP -c +/* Display a running counter that is incremented each time +/* an SMTP DATA command completes. +/* .IP "-C count" +/* When a host sends RESET instead of SYN|ACK, try \fIcount\fR times +/* before giving up. The default count is 1. Specify a larger count in +/* order to work around a problem with TCP/IP stacks that send RESET +/* when the listen queue is full. +/* .IP -d +/* Don't disconnect after sending a message; send the next +/* message over the same connection. +/* .IP "-f from" +/* Use the specified sender address (default: ). +/* .IP -o +/* Old mode: don't send HELO, and don't send message headers. +/* .IP "-l length" +/* Send \fIlength\fR bytes as message payload. +/* .IP "-m message_count" +/* 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 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: ). +/* 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 +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* Application-specific. */ + + /* + * Per-session data structure with state. + */ +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; + + /* + * Structure with broken-up SMTP server response. + */ +typedef struct { /* server response */ + int code; /* status */ + char *str; /* text */ + VSTRING *buf; /* origin of text */ +} RESPONSE; + +static VSTRING *buffer; +static int var_line_limit = 10240; +static int var_timeout = 300; +static const char *var_myhostname; +static int session_count; +static int message_count = 1; +static struct sockaddr_in sin; +static int recipients = 1; +static char *defaddr; +static char *recipient; +static char *sender; +static char *message_data; +static int message_length; +static int disconnect = 1; +static int count = 0; +static int counter = 0; +static int send_helo_first = 1; +static int send_headers = 1; +static int connect_count = 1; + +static void connect_done(int, char *); +static void send_helo(SESSION *); +static void helo_done(int, char *); +static void send_mail(SESSION *); +static void mail_done(int, char *); +static void send_rcpt(int, char *); +static void rcpt_done(int, char *); +static void send_data(int, char *); +static void data_done(int, char *); +static void dot_done(int, char *); +static void send_quit(SESSION *); +static void quit_done(int, char *); + +/* command - send an SMTP command */ + +static void command(VSTREAM *stream, char *fmt,...) +{ + VSTRING *buf; + va_list ap; + + /* + * Optionally, log the command before actually sending, so we can see + * what the program is trying to do. + */ + if (msg_verbose) { + buf = vstring_alloc(100); + va_start(ap, fmt); + vstring_vsprintf(buf, fmt, ap); + va_end(ap); + msg_info("%s", vstring_str(buf)); + vstring_free(buf); + } + va_start(ap, fmt); + smtp_vprintf(stream, fmt, ap); + va_end(ap); +} + +/* response - read and process SMTP server response */ + +static RESPONSE *response(VSTREAM *stream, VSTRING *buf) +{ + static RESPONSE rdata; + int more; + char *cp; + + /* + * Initialize the response data buffer. Defend against a denial of + * service attack by limiting the amount of multi-line text that we are + * willing to store. + */ + if (rdata.buf == 0) { + rdata.buf = vstring_alloc(100); + vstring_ctl(rdata.buf, VSTRING_CTL_MAXLEN, var_line_limit, 0); + } + + /* + * Censor out non-printable characters in server responses. Concatenate + * multi-line server responses. Separate the status code from the text. + * Leave further parsing up to the application. + */ +#define BUF ((char *) vstring_str(buf)) + VSTRING_RESET(rdata.buf); + for (;;) { + smtp_get(buf, stream, var_line_limit); + for (cp = BUF; *cp != 0; cp++) + if (!ISPRINT(*cp) && !ISSPACE(*cp)) + *cp = '?'; + cp = BUF; + if (msg_verbose) + msg_info("<<< %s", cp); + while (ISDIGIT(*cp)) + cp++; + rdata.code = (cp - BUF == 3 ? atoi(BUF) : 0); + if ((more = (*cp == '-')) != 0) + cp++; + while (ISSPACE(*cp)) + cp++; + vstring_strcat(rdata.buf, cp); + if (more == 0) + break; + VSTRING_ADDCH(rdata.buf, '\n'); + } + VSTRING_TERMINATE(rdata.buf); + rdata.str = vstring_str(rdata.buf); + return (&rdata); +} + +/* exception_text - translate exceptions from the smtp_stream module */ + +static char *exception_text(int except) +{ + switch (except) { + case SMTP_ERR_EOF: + return ("lost connection"); + case SMTP_ERR_TIME: + return ("timeout"); + default: + msg_panic("exception_text: unknown exception %d", except); + } + /* NOTREACHED */ +} + +/* startup - connect to server but do not wait */ + +static void startup(SESSION *session) +{ + if (message_count-- <= 0) { + myfree((char *) session); + session_count--; + return; + } + if (session->stream == 0) { + if ((session->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))) + break; + if (session->connect_count-- > 1) + usleep(10); + } + session->stream = vstream_fdopen(session->fd, O_RDWR); + smtp_timeout_setup(session->stream, var_timeout); + event_enable_read(session->fd, connect_done, (char *) session); + } else { + send_mail(session); + } +} + +/* connect_done - send message sender info */ + +static void connect_done(int unused_event, char *context) +{ + SESSION *session = (SESSION *) context; + RESPONSE *resp; + int except; + + /* + * Prepare for disaster. + */ + if ((except = setjmp(smtp_timeout_buf)) != 0) + msg_fatal("%s while reading HELO", exception_text(except)); + + /* + * Read and parse the server's SMTP greeting banner. + */ + if (((resp = response(session->stream, buffer))->code / 100) != 2) + msg_fatal("bad startup: %d %s", resp->code, resp->str); + + /* + * Send helo or send the envelope sender address. + */ + if (send_helo_first) + send_helo(session); + else + send_mail(session); +} + +/* send_helo - send hostname */ + +static void send_helo(SESSION *session) +{ + int except; + + /* + * Send the standard greeting with our hostname + */ + if ((except = setjmp(smtp_timeout_buf)) != 0) + msg_fatal("%s while sending HELO", exception_text(except)); + + command(session->stream, "HELO %s", var_myhostname); + + /* + * Prepare for the next event. + */ + event_disable_readwrite(session->fd); + event_enable_read(session->fd, helo_done, (char *) session); +} + +/* helo_done - handle HELO response */ + +static void helo_done(int unused, char *context) +{ + SESSION *session = (SESSION *) context; + RESPONSE *resp; + int except; + + /* + * Get response to HELO command. + */ + if ((except = setjmp(smtp_timeout_buf)) != 0) + msg_fatal("%s while sending HELO", exception_text(except)); + + if ((resp = response(session->stream, buffer))->code / 100 != 2) + msg_fatal("HELO rejected: %d %s", resp->code, resp->str); + + send_mail(session); +} + +/* send_mail - send envelope sender */ + +static void send_mail(SESSION *session) +{ + int except; + + /* + * Send the envelope sender address. + */ + if ((except = setjmp(smtp_timeout_buf)) != 0) + msg_fatal("%s while sending sender", exception_text(except)); + + command(session->stream, "MAIL FROM:<%s>", sender); + + /* + * Prepare for the next event. + */ + event_disable_readwrite(session->fd); + event_enable_read(session->fd, mail_done, (char *) session); +} + +/* mail_done - handle MAIL response */ + +static void mail_done(int unused, char *context) +{ + SESSION *session = (SESSION *) context; + RESPONSE *resp; + int except; + + /* + * Get response to MAIL command. + */ + if ((except = setjmp(smtp_timeout_buf)) != 0) + msg_fatal("%s while sending sender", exception_text(except)); + + if ((resp = response(session->stream, buffer))->code / 100 != 2) + msg_fatal("sender rejected: %d %s", resp->code, resp->str); + + session->rcpt_count = recipients; + send_rcpt(unused, context); +} + +/* send_rcpt - send recipient address */ + +static void send_rcpt(int unused_event, char *context) +{ + SESSION *session = (SESSION *) context; + int except; + + /* + * Send envelope recipient address. + */ + if ((except = setjmp(smtp_timeout_buf)) != 0) + msg_fatal("%s while sending recipient", exception_text(except)); + + if (session->rcpt_count > 1) + command(session->stream, "RCPT TO:<%d%s>", + session->rcpt_count, recipient); + else + command(session->stream, "RCPT TO:<%s>", recipient); + session->rcpt_count--; + + /* + * Prepare for the next event. + */ + event_disable_readwrite(session->fd); + event_enable_read(session->fd, rcpt_done, (char *) session); +} + +/* rcpt_done - handle RCPT completion */ + +static void rcpt_done(int unused, char *context) +{ + SESSION *session = (SESSION *) context; + RESPONSE *resp; + int except; + + /* + * Get response to RCPT command. + */ + if ((except = setjmp(smtp_timeout_buf)) != 0) + msg_fatal("%s while sending recipient", exception_text(except)); + + if ((resp = response(session->stream, buffer))->code / 100 != 2) + msg_fatal("recipient rejected: %d %s", resp->code, resp->str); + + /* + * Send another RCPT command or send DATA. + */ + if (session->rcpt_count > 0) + send_rcpt(unused, context); + else + send_data(unused, context); +} + +/* send_data - send DATA command */ + +static void send_data(int unused_event, char *context) +{ + SESSION *session = (SESSION *) context; + int except; + + /* + * Request data transmission. + */ + if ((except = setjmp(smtp_timeout_buf)) != 0) + msg_fatal("%s while sending DATA command", exception_text(except)); + command(session->stream, "DATA"); + + /* + * Prepare for the next event. + */ + event_disable_readwrite(session->fd); + event_enable_read(session->fd, data_done, (char *) session); +} + +/* data_done - send message content */ + +static void data_done(int unused_event, char *context) +{ + SESSION *session = (SESSION *) context; + RESPONSE *resp; + int except; + static const char *mydate; + static int mypid; + + /* + * Get response to DATA command. + */ + if ((except = setjmp(smtp_timeout_buf)) != 0) + msg_fatal("%s while sending DATA command", exception_text(except)); + if ((resp = response(session->stream, buffer))->code != 354) + msg_fatal("data %d %s", resp->code, resp->str); + + /* + * Send basic header to keep mailers that bother to examine them happy. + */ + if (send_headers) { + if (mydate == 0) { + mydate = mail_date(time((time_t *) 0)); + mypid = getpid(); + } + smtp_printf(session->stream, "From: <%s>", sender); + 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); + smtp_fputs("", 0, session->stream); + } + + /* + * Send some garbage. + */ + if ((except = setjmp(smtp_timeout_buf)) != 0) + msg_fatal("%s while sending message", exception_text(except)); + if (message_length == 0) { + smtp_fputs("La de da de da 1.", 17, session->stream); + smtp_fputs("La de da de da 2.", 17, session->stream); + smtp_fputs("La de da de da 3.", 17, session->stream); + smtp_fputs("La de da de da 4.", 17, session->stream); + } else { + smtp_fputs(message_data, message_length, session->stream); + } + + /* + * Send end of message and process the server response. + */ + command(session->stream, "."); + + /* + * Update the running counter. + */ + if (count) { + counter++; + vstream_printf("%d\r", counter); + vstream_fflush(VSTREAM_OUT); + } + + /* + * Prepare for the next event. + */ + event_disable_readwrite(session->fd); + event_enable_read(session->fd, dot_done, (char *) session); +} + +/* dot_done - send QUIT */ + +static void dot_done(int unused_event, char *context) +{ + SESSION *session = (SESSION *) context; + RESPONSE *resp; + int except; + + /* + * Get response to "." command. + */ + if ((except = setjmp(smtp_timeout_buf)) != 0) + msg_fatal("%s while sending message", exception_text(except)); + if ((resp = response(session->stream, buffer))->code / 100 != 2) + msg_fatal("data %d %s", resp->code, resp->str); + session->xfer_count++; + + /* + * Say goodbye or send the next message. + */ + if (disconnect || message_count < 1) { + send_quit(session); + } else { + event_disable_readwrite(session->fd); + startup(session); + } +} + +/* send_quit - send QUIT command */ + +static void send_quit(SESSION *session) +{ + command(session->stream, "QUIT"); + event_disable_readwrite(session->fd); + event_enable_read(session->fd, quit_done, (char *) session); +} + +/* quit_done - disconnect */ + +static void quit_done(int unused_event, char *context) +{ + SESSION *session = (SESSION *) context; + + (void) response(session->stream, buffer); + event_disable_readwrite(session->fd); + vstream_fclose(session->stream); + session->stream = 0; + startup(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); +} + +/* main - parse JCL and start the machine */ + +int main(int argc, char **argv) +{ + SESSION *session; + char *host; + char *port; + int sessions = 1; + int ch; + int i; + + signal(SIGPIPE, SIG_IGN); + + /* + * Parse JCL. + */ + while ((ch = GETOPT(argc, argv, "cC:df:l:m:or:s:t:v")) > 0) { + switch (ch) { + case 'c': + count++; + break; + case 'C': + if ((connect_count = atoi(optarg)) <= 0) + usage(argv[0]); + break; + case 'd': + disconnect = 0; + break; + case 'f': + sender = optarg; + break; + case 'l': + if ((message_length = atoi(optarg)) <= 0) + usage(argv[0]); + message_data = mymalloc(message_length); + memset(message_data, 'X', message_length); + for (i = 80; i < message_length; i += 80) { + message_data[i - 2] = '\r'; + message_data[i - 1] = '\n'; + } + break; + case 'm': + if ((message_count = atoi(optarg)) <= 0) + usage(argv[0]); + break; + case 'o': + send_helo_first = 0; + send_headers = 0; + break; + case 'r': + if ((recipients = atoi(optarg)) <= 0) + usage(argv[0]); + break; + case 's': + if ((sessions = atoi(optarg)) <= 0) + usage(argv[0]); + break; + case 't': + recipient = optarg; + break; + case 'v': + msg_verbose++; + break; + default: + usage(argv[0]); + } + } + if (argc - optind != 1) + usage(argv[0]); + if ((port = split_at(host = argv[optind], ':')) == 0) + port = "smtp"; + + /* + * Translate endpoint address to internal form. + */ + memset((char *) &sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = find_inet_addr(host); + sin.sin_port = find_inet_port(port, "tcp"); + + /* + * Make sure the SMTP server cannot run us out of memory by sending + * never-ending lines of text. + */ + if (buffer == 0) { + buffer = vstring_alloc(100); + vstring_ctl(buffer, VSTRING_CTL_MAXLEN, var_line_limit, 0); + } + + /* + * Make sure we have sender and recipient addresses. + */ + var_myhostname = get_hostname(); + if (sender == 0 || recipient == 0) { + vstring_sprintf(buffer, "foo@%s", var_myhostname); + defaddr = mystrdup(vstring_str(buffer)); + if (sender == 0) + sender = defaddr; + if (recipient == 0) + recipient = defaddr; + } + + /* + * Start sessions. + */ + while (sessions-- > 0) { + session = (SESSION *) mymalloc(sizeof(*session)); + session->stream = 0; + session->xfer_count = 0; + session->connect_count = connect_count; + session_count++; + startup(session); + } + for (;;) { + event_loop(-1); + if (session_count <= 0 && message_count <= 0) { + if (count) { + VSTREAM_PUTC('\n', VSTREAM_OUT); + vstream_fflush(VSTREAM_OUT); + } + exit(0); + } + } +} diff --git a/postfix/smtpstone/vmail-local b/postfix/smtpstone/vmail-local new file mode 100644 index 000000000..84269b231 --- /dev/null +++ b/postfix/smtpstone/vmail-local @@ -0,0 +1,43 @@ +fist% date; /usr/bin/time ./smtp-source -s 5 -m 100 -t wietse@nipple nipple +Sat May 9 22:08:27 EDT 1998 + 6.29 real 0.08 user 0.17 sys +May 9 22:08:27 nipple wietse[100]: postfix/smtpd[2248]: connect from fist.porcu +May 9 22:08:58 nipple postfix/local[2330]: 5082D53AD0: to=$@ + +test: $(TESTPROG) + +$(BIN_DIR)/$(PROG): $(PROG) + cp $(PROG) $@ + +update: $(BIN_DIR)/$(PROG) + +printfck: $(OBJS) $(PROG) + rm -rf printfck + mkdir printfck + cp *.h 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 trivial-rewrite $(TESTPROG) junk $(LIB) + 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' +resolve.o: resolve.c +resolve.o: ../include/sys_defs.h +resolve.o: ../include/msg.h +resolve.o: ../include/vstring.h +resolve.o: ../include/vbuf.h +resolve.o: ../include/vstream.h +resolve.o: ../include/vstring_vstream.h +resolve.o: ../include/split_at.h +resolve.o: ../include/mail_params.h +resolve.o: ../include/mail_proto.h +resolve.o: ../include/iostuff.h +resolve.o: ../include/mail_addr.h +resolve.o: ../include/rewrite_clnt.h +resolve.o: ../include/resolve_local.h +resolve.o: ../include/config.h +resolve.o: ../include/quote_822_local.h +resolve.o: ../include/tok822.h +resolve.o: ../include/resolve_clnt.h +resolve.o: trivial-rewrite.h +resolve.o: transport.h +rewrite.o: rewrite.c +rewrite.o: ../include/sys_defs.h +rewrite.o: ../include/msg.h +rewrite.o: ../include/vstring.h +rewrite.o: ../include/vbuf.h +rewrite.o: ../include/vstream.h +rewrite.o: ../include/vstring_vstream.h +rewrite.o: ../include/split_at.h +rewrite.o: ../include/mail_params.h +rewrite.o: ../include/mail_proto.h +rewrite.o: ../include/iostuff.h +rewrite.o: ../include/resolve_local.h +rewrite.o: ../include/tok822.h +rewrite.o: ../include/resolve_clnt.h +rewrite.o: ../include/config.h +rewrite.o: trivial-rewrite.h +transport.o: transport.c +transport.o: ../include/sys_defs.h +transport.o: ../include/msg.h +transport.o: ../include/stringops.h +transport.o: ../include/mymalloc.h +transport.o: ../include/vstring.h +transport.o: ../include/vbuf.h +transport.o: ../include/split_at.h +transport.o: ../include/dict.h +transport.o: ../include/vstream.h +transport.o: ../include/mail_params.h +transport.o: ../include/maps.h +transport.o: transport.h +trivial-rewrite.o: trivial-rewrite.c +trivial-rewrite.o: ../include/sys_defs.h +trivial-rewrite.o: ../include/msg.h +trivial-rewrite.o: ../include/vstring.h +trivial-rewrite.o: ../include/vbuf.h +trivial-rewrite.o: ../include/vstream.h +trivial-rewrite.o: ../include/vstring_vstream.h +trivial-rewrite.o: ../include/split_at.h +trivial-rewrite.o: ../include/stringops.h +trivial-rewrite.o: ../include/mail_params.h +trivial-rewrite.o: ../include/mail_proto.h +trivial-rewrite.o: ../include/iostuff.h +trivial-rewrite.o: ../include/resolve_local.h +trivial-rewrite.o: ../include/config.h +trivial-rewrite.o: ../include/resolve_clnt.h +trivial-rewrite.o: ../include/rewrite_clnt.h +trivial-rewrite.o: ../include/tok822.h +trivial-rewrite.o: ../include/mail_server.h +trivial-rewrite.o: trivial-rewrite.h +trivial-rewrite.o: transport.h diff --git a/postfix/trivial-rewrite/resolve.c b/postfix/trivial-rewrite/resolve.c new file mode 100644 index 000000000..160c4dd24 --- /dev/null +++ b/postfix/trivial-rewrite/resolve.c @@ -0,0 +1,234 @@ +/*++ +/* NAME +/* resolve 3 +/* SUMMARY +/* mail address resolver +/* SYNOPSIS +/* #include "trivial-rewrite.h" +/* +/* void resolve_init(void) +/* +/* void resolve_proto(stream) +/* VSTREAM *stream; +/* +/* void resolve_addr(rule, addr, result) +/* char *rule; +/* char *addr; +/* VSTRING *result; +/* DESCRIPTION +/* This module implements the trivial address resolving engine. +/* It distinguishes between local and remote mail, and optionally +/* consults one or more transport tables that map a destination +/* to a transport, nexthop pair. +/* +/* resolve_init() initializes data structures that are private +/* to this module. It should be called once before using the +/* actual resolver routines. +/* +/* resolve_proto() implements the client-server protocol: +/* read one address in FQDN form, reply with a (transport, +/* nexthop, internalized recipient) triple. +/* +/* resolve_addr() gives direct access to the address resolving +/* engine. It resolves an internalized address to a (transport, +/* nexthop, internalized recipient) triple. +/* STANDARDS +/* DIAGNOSTICS +/* Problems and transactions are logged to the syslog daemon. +/* BUGS +/* SEE ALSO +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "trivial-rewrite.h" +#include "transport.h" + +#define STR vstring_str + +/* resolve_addr - resolve address according to rule set */ + +void resolve_addr(char *addr, VSTRING *channel, VSTRING *nexthop, + VSTRING *nextrcpt) +{ + VSTRING *addr_buf = vstring_alloc(100); + TOK822 *tree; + TOK822 *saved_domain = 0; + TOK822 *domain = 0; + + /* + * The address is in internalized (unquoted) form, so we must externalize + * it first before we can parse it. + */ + quote_822_local(addr_buf, addr); + tree = tok822_scan_addr(vstring_str(addr_buf)); + + /* + * Preliminary resolver: strip off all instances of the local domain. + * Terminate when no destination domain is left over, or when the + * destination domain is remote. + */ +#define RESOLVE_LOCAL(domain) \ + resolve_local(STR(tok822_internalize(addr_buf, domain, TOK822_STR_DEFL))) + + while (tree->head) { + + /* + * Strip trailing dot. + */ + if (tree->tail->type == '.') + tok822_free_tree(tok822_sub_keep_before(tree, tree->tail)); + + /* + * A lone empty string becomes the postmaster. + */ + if (tree->head == tree->tail && tree->head->type == TOK822_QSTRING + && VSTRING_LEN(tree->head->vstr) == 0) { + tok822_free(tree->head); + tree->head = tok822_scan(MAIL_ADDR_POSTMASTER, &tree->tail); + } + + /* + * Strip (and save) @domain if local. + */ + if ((domain = tok822_rfind_type(tree->tail, '@')) != 0) { + if (RESOLVE_LOCAL(domain->next) == 0) + break; + tok822_sub_keep_before(tree, domain); + if (saved_domain) + tok822_free_tree(saved_domain); + saved_domain = domain; + } + + /* + * Replace foo%bar by foo@bar, site!user by user@site, rewrite to + * canonical form, and retry. + */ + if (var_swap_bangpath && tok822_rfind_type(tree->tail, '!') != 0) { + rewrite_tree(REWRITE_CANON, tree); + } else if (var_percent_hack + && (domain = tok822_rfind_type(tree->tail, '%')) != 0) { + domain->type = '@'; + rewrite_tree(REWRITE_CANON, tree); + } else { + domain = 0; + break; + } + } + + /* + * Non-local delivery: if no transport is specified, assume the transport + * specified in var_def_transport. If no mail relay is specified in + * var_relayhost, forward to the domain's mail exchanger. + */ + if (domain != 0) { + if (*var_transport_maps == 0 + || (tok822_internalize(addr_buf, domain->next, TOK822_STR_DEFL), + transport_lookup(STR(addr_buf), channel, nexthop) == 0)) { + vstring_strcpy(channel, var_def_transport); + if (*var_relayhost) + vstring_strcpy(nexthop, var_relayhost); + else + tok822_internalize(nexthop, domain->next, TOK822_STR_DEFL); + } + tok822_internalize(nextrcpt, tree, TOK822_STR_DEFL); + } + + /* + * Local delivery: if no domain was specified, assume the local machine. + * See above for what happens with an empty localpart. + */ + else { + vstring_strcpy(channel, MAIL_SERVICE_LOCAL); + vstring_strcpy(nexthop, ""); + if (saved_domain) { + tok822_sub_append(tree, saved_domain); + saved_domain = 0; + } else { + tok822_sub_append(tree, tok822_alloc('@', (char *) 0)); + tok822_sub_append(tree, tok822_scan(var_myhostname, (TOK822 **) 0)); + } + tok822_internalize(nextrcpt, tree, TOK822_STR_DEFL); + } + + /* + * Clean up. + */ + if (saved_domain) + tok822_free_tree(saved_domain); + tok822_free_tree(tree); + vstring_free(addr_buf); +} + +/* Static, so they can be used by the network protocol interface only. */ + +static VSTRING *channel; +static VSTRING *nexthop; +static VSTRING *nextrcpt; +static VSTRING *query; + +/* resolve_proto - read request and send reply */ + +int resolve_proto(VSTREAM *stream) +{ + if (mail_scan(stream, "%s", query) != 1) + return (-1); + + resolve_addr(STR(query), channel, nexthop, nextrcpt); + + if (msg_verbose) + msg_info("%s -> (`%s' `%s' `%s')", STR(query), STR(channel), + STR(nexthop), STR(nextrcpt)); + + mail_print(stream, "%s %s %s", STR(channel), STR(nexthop), STR(nextrcpt)); + + if (vstream_fflush(stream) != 0) { + msg_warn("write resolver reply: %m"); + return (-1); + } + return (0); +} + +/* resolve_init - module initializations */ + +void resolve_init(void) +{ + query = vstring_alloc(100); + channel = vstring_alloc(100); + nexthop = vstring_alloc(100); + nextrcpt = vstring_alloc(100); +} diff --git a/postfix/trivial-rewrite/rewrite.c b/postfix/trivial-rewrite/rewrite.c new file mode 100644 index 000000000..6cae7d8a3 --- /dev/null +++ b/postfix/trivial-rewrite/rewrite.c @@ -0,0 +1,194 @@ +/*++ +/* NAME +/* rewrite 3 +/* SUMMARY +/* mail address rewriter +/* SYNOPSIS +/* #include "trivial-rewrite.h" +/* +/* void rewrite_init(void) +/* +/* void rewrite_proto(stream) +/* VSTREAM *stream; +/* +/* void rewrite_addr(rule, addr, result) +/* char *rule; +/* char *addr; +/* VSTRING *result; +/* +/* void rewrite_tree(rule, tree) +/* char *rule; +/* TOK822 *tree; +/* DESCRIPTION +/* This module implements the trivial address rewriting engine. +/* +/* rewrite_init() initializes data structures that are private +/* to this module. It should be called once before using the +/* actual rewriting routines. +/* +/* rewrite_proto() implements the client-server protocol: read +/* one rule set name and one address in external (quoted) form, +/* reply with the rewritten address in external form. +/* +/* rewrite_addr() rewrites an address string to another string. +/* Both input and output are in external (quoted) form. +/* +/* rewrite_tree() rewrites a parse tree with a single address to +/* another tree. A tree is a dummy node on top of a token list. +/* STANDARDS +/* DIAGNOSTICS +/* Problems and transactions are logged to the syslog daemon. +/* BUGS +/* SEE ALSO +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "trivial-rewrite.h" + +static VSTRING *ruleset; +static VSTRING *address; +static VSTRING *result; + +/* rewrite_tree - rewrite address according to rule set */ + +void rewrite_tree(char *unused_ruleset, TOK822 *tree) +{ + TOK822 *colon; + TOK822 *domain; + TOK822 *bang; + TOK822 *local; + + /* + * An empty address is a special case. + */ + if (tree->head == tree->tail + && tree->tail->type == TOK822_QSTRING + && VSTRING_LEN(tree->tail->vstr) == 0) + return; + + /* + * Strip source route. + */ + if (tree->head->type == '@' + && (colon = tok822_find_type(tree->head, ':')) != 0) + tok822_free_tree(tok822_sub_keep_after(tree, colon)); + + /* + * Swap domain!user to user@domain. + */ + if (var_swap_bangpath != 0 + && (domain = tok822_rfind_type(tree->tail, '@')) == 0 + && (bang = tok822_find_type(tree->head, '!')) != 0) { + tok822_sub_keep_before(tree, bang); + local = tok822_cut_after(bang); + tok822_free(bang); + tok822_sub_prepend(tree, tok822_alloc('@', (char *) 0)); + if (local) + tok822_sub_prepend(tree, local); + } + + /* + * Append missing @origin + */ + if (var_append_at_myorigin != 0 + && (domain = tok822_rfind_type(tree->tail, '@')) == 0) { + domain = tok822_sub_append(tree, tok822_alloc('@', (char *) 0)); + tok822_sub_append(tree, tok822_scan(var_myorigin, (TOK822 **) 0)); + } + + /* + * Append missing .domain + */ + if (var_append_dot_mydomain != 0 + && (domain = tok822_rfind_type(tree->tail, '@')) != 0 + && tok822_find_type(domain, TOK822_DOMLIT) == 0 + && tok822_find_type(domain, '.') == 0) { + tok822_sub_append(tree, tok822_alloc('.', (char *) 0)); + tok822_sub_append(tree, tok822_scan(var_mydomain, (TOK822 **) 0)); + } + + /* + * Strip trailing dot. + */ + if (tree->tail->type == '.') + tok822_free_tree(tok822_sub_keep_before(tree, tree->tail)); +} + +/* rewrite_addr - rewrite address according to rule set */ + +void rewrite_addr(char *ruleset, char *addr, VSTRING *result) +{ + TOK822 *tree; + + /* + * Convert the address from externalized (quoted) form to token list, + * rewrite it, and convert back. + */ + tree = tok822_scan_addr(addr); + rewrite_tree(ruleset, tree); + tok822_externalize(result, tree, TOK822_STR_DEFL); + tok822_free_tree(tree); +} + +/* rewrite_proto - read request and send reply */ + +int rewrite_proto(VSTREAM *stream) +{ + if (mail_scan(stream, "%s %s", ruleset, address) != 2) + return (-1); + + rewrite_addr(vstring_str(ruleset), vstring_str(address), result); + + if (msg_verbose) + msg_info("`%s' `%s' -> `%s'", vstring_str(ruleset), + vstring_str(address), vstring_str(result)); + + mail_print(stream, "%s", vstring_str(result)); + + if (vstream_fflush(stream) != 0) { + msg_warn("write rewrite reply: %m"); + return (-1); + } + return (0); +} + +/* rewrite_init - module initializations */ + +void rewrite_init(void) +{ + ruleset = vstring_alloc(100); + address = vstring_alloc(100); + result = vstring_alloc(100); +} diff --git a/postfix/trivial-rewrite/transport.c b/postfix/trivial-rewrite/transport.c new file mode 100644 index 000000000..407250114 --- /dev/null +++ b/postfix/trivial-rewrite/transport.c @@ -0,0 +1,127 @@ +/*++ +/* NAME +/* transport 3 +/* SUMMARY +/* transport mapping +/* SYNOPSIS +/* #include "transport.h" +/* +/* void transport_init() +/* +/* int transport_lookup(domain, channel, nexthop) +/* const char *domain; +/* VSTRING *channel; +/* VSTRING *nexthop; +/* DESCRIPTION +/* This module implements access to the table that maps transport +/* domains to (channel, nexthop) tuples. +/* +/* transport_init() performs initializations that should be +/* done before the process enters the chroot jail, and +/* before calling transport_lookup(). +/* +/* transport_lookup() finds the channel and nexthop for the given +/* domain, and returns 1 if something was found. Otherwise, 0 +/* is returned. +/* DIAGNOSTICS +/* The global \fIdict_errno\fR is non-zero when the lookup +/* should be tried again. +/* SEE ALSO +/* maps(3), multi-dictionary search +/* transport(5), format of transport map +/* FILES +/* /etc/postfix/transport* +/* CONFIGURATION PARAMETERS +/* transport_maps, names of maps to be searched. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* Application-specific. */ + +#include "transport.h" + +static MAPS *transport_path; + +/* transport_init - pre-jail initialization */ + +void transport_init(void) +{ + if (transport_path) + msg_panic("transport_init: repeated call"); + transport_path = maps_create("transport", var_transport_maps); +} + +/* transport_lookup - map a transport domain */ + +int transport_lookup(const char *domain, VSTRING *channel, VSTRING *nexthop) +{ + char *low_domain = lowercase(mystrdup(domain)); + const char *name; + const char *value; + const char *host; + const char *next; + char *saved_value; + char *transport; + int found = 0; + + if (transport_path == 0) + msg_panic("transport_lookup: missing initialization"); + + /* + * 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. + * + * After checking the full name, check for .upper.domain, to distinguish + * between the upper domain and it's decendants, ala sendmail and tcp + * wrappers. + * + * 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) { + if ((value = maps_find(transport_path, name)) != 0) { + saved_value = mystrdup(value); + if ((host = split_at(saved_value, ':')) == 0 || *host == 0) + host = domain; + if (*(transport = saved_value) == 0) + transport = var_def_transport; + vstring_strcpy(channel, transport); + vstring_strcpy(nexthop, host); + myfree(saved_value); + found = 1; + break; + } else if (dict_errno != 0) { + msg_fatal("transport table lookup problem"); + } + } + myfree(low_domain); + return (found); +} diff --git a/postfix/trivial-rewrite/transport.h b/postfix/trivial-rewrite/transport.h new file mode 100644 index 000000000..2747ec215 --- /dev/null +++ b/postfix/trivial-rewrite/transport.h @@ -0,0 +1,31 @@ +/*++ +/* NAME +/* transport 3h +/* SUMMARY +/* transport mapping +/* SYNOPSIS +/* #include "transport.h" +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern void transport_init(void); +extern int transport_lookup(const char *, VSTRING *, VSTRING *); + +/* 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 +/*--*/ diff --git a/postfix/trivial-rewrite/trivial-rewrite.c b/postfix/trivial-rewrite/trivial-rewrite.c new file mode 100644 index 000000000..e9f59b389 --- /dev/null +++ b/postfix/trivial-rewrite/trivial-rewrite.c @@ -0,0 +1,217 @@ +/*++ +/* NAME +/* trivial-rewrite 8 +/* SUMMARY +/* Postfix address rewriting and resolving daemon +/* SYNOPSIS +/* \fBtrivial-rewrite\fR [generic Postfix daemon options] +/* DESCRIPTION +/* The \fBtrivial-rewrite\fR daemon processes two types of client +/* service requests: +/* .IP \fBrewrite\fR +/* Rewrite an address to standard form. The \fBtrivial-rewrite\fR +/* daemon by default appends local domain information to unqualified +/* addresses, swaps bang paths to domain form, and strips source +/* routing information. This process is under control of several +/* configuration parameters (see below). +/* .IP \fBresolve\fR +/* Resolve an address to a (\fItransport\fR, \fInexthop\fR, +/* \fIrecipient\fR) triple. The meaning of the results is as follows: +/* .RS +/* .IP \fItransport\fR +/* The delivery agent to use. This is the first field of an entry +/* in the \fBmaster.cf\fR file. +/* .IP \fInexthop\fR +/* The host to send to. For local delivery this is an empty string. +/* .IP \fIrecipient\fR +/* The envelope recipient address that is passed on to \fInexthop\fR. +/* .PP +/* The \fBtrivial-rewrite\fR daemon by default only distinguishes +/* between local and non-local mail. For finer control over mail +/* routing, use the optional \fBtransport\fR(5) lookup table. +/* .RE +/* .PP +/* This program expects to be run from the \fBmaster\fR(8) process +/* manager. +/* STANDARDS +/* .ad +/* .fi +/* None. The command does not interact with the outside world. +/* SECURITY +/* .ad +/* .fi +/* The \fBtrivial-rewrite\fR daemon is not security sensitive. +/* By default, this daemon does not talk to remote or local users. +/* It can run at a fixed low privilege in a chrooted environment. +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8). +/* BUGS +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. Use the \fBpostfix reload\fR command after +/* a configuration change. +/* .SH Miscellaneous +/* .ad +/* .fi +/* .IP \fBinet_interfaces\fR +/* The network interfaces that this mail system receives mail on. +/* This information is used to determine if +/* \fIuser\fR@[\fInet.work.addr.ess\fR] is local or remote. +/* .IP \fBmydestination\fR +/* List of domains that this machine considers local. +/* .IP \fBmyorigin\fR +/* The domain that locally-posted mail appears to come from. +/* .SH Rewriting +/* .ad +/* .fi +/* .IP \fBallow_percent_hack\fR +/* Rewrite \fIuser\fR%\fIdomain\fR to \fIuser\fR@\fIdomain\fR. +/* .IP \fBappend_at_myorigin\fR +/* Rewrite \fIuser\fR to \fIuser\fR@$\fBmyorigin\fR. +/* .IP \fBappend_dot_mydomain\fR +/* Rewrite \fIuser\fR@\fIhost\fR to \fIuser\fR@\fIhost\fR.$\fBmydomain\fR. +/* .IP \fBswap_bangpath\fR +/* Rewrite \fIsite\fR!\fIuser\fR to \fIuser\fR@\fIsite\fR. +/* .SH Routing +/* .ad +/* .fi +/* .IP \fBdefault_transport\fR +/* The default transport to use when no transport is explicitly +/* given in the \fBtransport\fR(5) table. +/* .IP \fBrelayhost\fR +/* The default host to send mail to when no entry is matched +/* in the \fBtransport\fR(5) table. +/* .sp +/* When no \fBrelayhost\fR is specified, mail is routed directly +/* to the destination's mail exchanger. +/* .IP \fBtransport_maps\fR +/* List of tables with \fIdomain\fR to (\fItransport, nexthop\fR) +/* mappings. +/* SEE ALSO +/* master(8) process manager +/* syslogd(8) system logging +/* transport(5) transport table format +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Multi server skeleton. */ + +#include + +/* Application-specific. */ + +#include +#include + +static VSTRING *command; + + /* + * Tunable parameters. + */ +char *var_transport_maps; +char *var_def_transport; +bool var_swap_bangpath; +bool var_append_dot_mydomain; +bool var_append_at_myorigin; +bool var_percent_hack; + +/* rewrite_service - read request and send reply */ + +static void rewrite_service(VSTREAM *stream, char *unused_service, char **argv) +{ + int status = -1; + + /* + * Sanity check. This service takes no command-line arguments. + */ + if (argv[0]) + msg_fatal("unexpected command-line argument: %s", argv[0]); + + /* + * This routine runs whenever a client connects to the UNIX-domain socket + * dedicated to address rewriting. All connection-management stuff is + * handled by the common code in multi_server.c. + */ + if (mail_scan(stream, "%s", command) == 1) { + if (strcmp(vstring_str(command), REWRITE_ADDR) == 0) { + status = rewrite_proto(stream); + } else if (strcmp(vstring_str(command), RESOLVE_ADDR) == 0) { + status = resolve_proto(stream); + } else { + msg_warn("bad command %.30s", printable(vstring_str(command), '?')); + } + } + if (status < 0) + multi_server_disconnect(stream); +} + +/* pre_jail_init - initialize before entering chroot jail */ + +static void pre_jail_init(void) +{ + command = vstring_alloc(100); + rewrite_init(); + resolve_init(); + transport_init(); +} + +/* main - pass control to the multi-threaded skeleton code */ + +int main(int argc, char **argv) +{ + static CONFIG_STR_TABLE str_table[] = { + VAR_TRANSPORT_MAPS, DEF_TRANSPORT_MAPS, &var_transport_maps, 0, 0, + VAR_DEF_TRANSPORT, DEF_DEF_TRANSPORT, &var_def_transport, 0, 0, + 0, + }; + static CONFIG_BOOL_TABLE bool_table[] = { + VAR_SWAP_BANGPATH, DEF_SWAP_BANGPATH, &var_swap_bangpath, + VAR_APP_DOT_MYDOMAIN, DEF_APP_DOT_MYDOMAIN, &var_append_dot_mydomain, + VAR_APP_AT_MYORIGIN, DEF_APP_AT_MYORIGIN, &var_append_at_myorigin, + VAR_PERCENT_HACK, DEF_PERCENT_HACK, &var_percent_hack, + 0, + }; + + multi_server_main(argc, argv, rewrite_service, + MAIL_SERVER_STR_TABLE, str_table, + MAIL_SERVER_BOOL_TABLE, bool_table, + MAIL_SERVER_PRE_INIT, pre_jail_init, + 0); +} diff --git a/postfix/trivial-rewrite/trivial-rewrite.h b/postfix/trivial-rewrite/trivial-rewrite.h new file mode 100644 index 000000000..da5ef96ee --- /dev/null +++ b/postfix/trivial-rewrite/trivial-rewrite.h @@ -0,0 +1,47 @@ +/*++ +/* NAME +/* trivial-rewrite 3h +/* SUMMARY +/* mail address rewriter and resolver +/* SYNOPSIS +/* #include "trivial-rewrite.h" +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include + + /* + * Global library. + */ +#include + + /* + * rewrite.c + */ +extern void rewrite_init(void); +extern int rewrite_proto(VSTREAM *); +extern void rewrite_addr(char *, char *, VSTRING *); +extern void rewrite_tree(char *, TOK822 *); + + /* + * resolve.c + */ +extern void resolve_init(void); +extern int resolve_proto(VSTREAM *); +extern void resolve_addr(char *, VSTRING *, VSTRING *, VSTRING *); + +/* 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 +/*--*/ + diff --git a/postfix/util/.indent.pro b/postfix/util/.indent.pro new file mode 100644 index 000000000..e59d396ad --- /dev/null +++ b/postfix/util/.indent.pro @@ -0,0 +1,89 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-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 +-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 +-TSCAN_DIR +-TSINGLE_SERVER +-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/util/.printfck b/postfix/util/.printfck new file mode 100644 index 000000000..9ed900cb0 --- /dev/null +++ b/postfix/util/.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/util/Makefile.in b/postfix/util/Makefile.in new file mode 100644 index 000000000..f75571ff5 --- /dev/null +++ b/postfix/util/Makefile.in @@ -0,0 +1,760 @@ +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 \ + 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 \ + line_wrap.c lowercase.c lstat_as.c mac_parse.c make_dirs.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 +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 \ + 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 \ + line_wrap.o lowercase.o lstat_as.o mac_parse.o make_dirs.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 +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 \ + fsspace.h fullname.h get_domainname.h get_hostname.h htable.h \ + inet_addr_host.h inet_addr_list.h inet_addr_local.h inet_util.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 \ + 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 +WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ + -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ + -Wunused +DEFS = -I. -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) +FILES = Makefile $(SRCS) $(HDRS) +INCL = +LIB = libutil.a +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 + +LIB_DIR = ../lib +INC_DIR = ../include + +.c.o:; $(CC) $(CFLAGS) -c $*.c + +all: $(LIB) + +Makefile: Makefile.in + (set -e; echo "# DO NOT EDIT"; $(OPTS) sh ../makedefs; cat $?) >$@ + +test: $(TESTPROG) + +$(LIB): $(OBJS) + $(AR) $(ARFL) $(LIB) $? + $(RANLIB) $(LIB) + +$(LIB_DIR)/$(LIB): $(LIB) + cp $(LIB) $(LIB_DIR) + $(RANLIB) $(LIB_DIR)/$(LIB) + +update: $(LIB_DIR)/$(LIB) $(HDRS) + -for i in $(HDRS); \ + do \ + cmp -s $$i $(INC_DIR)/$$i 2>/dev/null || cp $$i $(INC_DIR); \ + done + cd $(INC_DIR); chmod 644 $(HDRS) + +printfck: $(OBJS) $(PROG) + rm -rf printfck + mkdir printfck + cp *.h 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` + +shar: + @shar $(FILES) + +lint: + lint $(SRCS) + +clean: + rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAKES) + rm -rf printfck + +tidy: clean + +vstring: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +msg_syslog: msg_syslog.c $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +vstring_vstream: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +valid_hostname: valid_hostname.c $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +events: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +dict_open: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +fullname: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +inet_addr_local: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +inet_addr_host: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +fifo_open: fifo_open.c + $(CC) $(CFLAGS) -o $@ $@.c $(SYSLIBS) + +sigdelay: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +mystrtok: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +fifo_rdwr_bug: fifo_rdwr_bug.c $(LIB) + $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(SYSLIBS) + +fifo_rdonly_bug: fifo_rdonly_bug.c $(LIB) + $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(SYSLIBS) + +translit: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +fsspace: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +exec_command: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +make_dirs: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +mac_parse: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +vstream_popen: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +fifo_trigger: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +peer_name: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +doze: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +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 Makefile + +# do not edit below this line - it is generated by 'make depend' +argv.o: argv.c +argv.o: mymalloc.h +argv.o: argv.h +argv_split.o: argv_split.c +argv_split.o: sys_defs.h +argv_split.o: mymalloc.h +argv_split.o: stringops.h +argv_split.o: argv.h +attr.o: attr.c +attr.o: sys_defs.h +attr.o: mymalloc.h +attr.o: htable.h +attr.o: attr.h +basename.o: basename.c +basename.o: sys_defs.h +basename.o: stringops.h +binhash.o: binhash.c +binhash.o: sys_defs.h +binhash.o: mymalloc.h +binhash.o: msg.h +binhash.o: binhash.h +chroot_uid.o: chroot_uid.c +chroot_uid.o: sys_defs.h +chroot_uid.o: msg.h +chroot_uid.o: chroot_uid.h +close_on_exec.o: close_on_exec.c +close_on_exec.o: sys_defs.h +close_on_exec.o: msg.h +close_on_exec.o: iostuff.h +concatenate.o: concatenate.c +concatenate.o: sys_defs.h +concatenate.o: mymalloc.h +concatenate.o: stringops.h +dict.o: dict.c +dict.o: sys_defs.h +dict.o: msg.h +dict.o: htable.h +dict.o: mymalloc.h +dict.o: vstream.h +dict.o: vbuf.h +dict.o: vstring.h +dict.o: readline.h +dict.o: myflock.h +dict.o: mac_parse.h +dict.o: dict.h +dict.o: dict_ht.h +dict_db.o: dict_db.c +dict_db.o: sys_defs.h +dict_db.o: msg.h +dict_db.o: mymalloc.h +dict_db.o: vstring.h +dict_db.o: vbuf.h +dict_db.o: stringops.h +dict_db.o: iostuff.h +dict_db.o: dict.h +dict_db.o: vstream.h +dict_db.o: dict_db.h +dict_dbm.o: dict_dbm.c +dict_dbm.o: sys_defs.h +dict_env.o: dict_env.c +dict_env.o: sys_defs.h +dict_env.o: mymalloc.h +dict_env.o: msg.h +dict_env.o: safe.h +dict_env.o: dict.h +dict_env.o: vstream.h +dict_env.o: vbuf.h +dict_env.o: dict_env.h +dict_ht.o: dict_ht.c +dict_ht.o: sys_defs.h +dict_ht.o: mymalloc.h +dict_ht.o: htable.h +dict_ht.o: dict.h +dict_ht.o: vstream.h +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 +dict_nis.o: sys_defs.h +dict_nis.o: msg.h +dict_nis.o: mymalloc.h +dict_nis.o: vstring.h +dict_nis.o: vbuf.h +dict_nis.o: dict.h +dict_nis.o: vstream.h +dict_nis.o: dict_nis.h +dict_nisplus.o: dict_nisplus.c +dict_nisplus.o: sys_defs.h +dict_nisplus.o: msg.h +dict_nisplus.o: mymalloc.h +dict_nisplus.o: htable.h +dict_nisplus.o: dict.h +dict_nisplus.o: vstream.h +dict_nisplus.o: vbuf.h +dict_nisplus.o: dict_nisplus.h +dict_open.o: dict_open.c +dict_open.o: sys_defs.h +dict_open.o: argv.h +dict_open.o: mymalloc.h +dict_open.o: msg.h +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_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: stringops.h +dict_open.o: split_at.h +dir_forest.o: dir_forest.c +dir_forest.o: sys_defs.h +dir_forest.o: msg.h +dir_forest.o: dir_forest.h +dir_forest.o: vstring.h +dir_forest.o: vbuf.h +doze.o: doze.c +doze.o: sys_defs.h +doze.o: msg.h +doze.o: iostuff.h +environ.o: environ.c +environ.o: sys_defs.h +events.o: events.c +events.o: sys_defs.h +events.o: mymalloc.h +events.o: msg.h +events.o: iostuff.h +events.o: ring.h +events.o: events.h +exec_command.o: exec_command.c +exec_command.o: sys_defs.h +exec_command.o: msg.h +exec_command.o: argv.h +exec_command.o: exec_command.h +fifo_listen.o: fifo_listen.c +fifo_listen.o: sys_defs.h +fifo_listen.o: msg.h +fifo_listen.o: iostuff.h +fifo_listen.o: listen.h +fifo_open.o: fifo_open.c +fifo_rdonly_bug.o: fifo_rdonly_bug.c +fifo_rdonly_bug.o: sys_defs.h +fifo_rdwr_bug.o: fifo_rdwr_bug.c +fifo_rdwr_bug.o: sys_defs.h +fifo_trigger.o: fifo_trigger.c +fifo_trigger.o: sys_defs.h +fifo_trigger.o: msg.h +fifo_trigger.o: iostuff.h +fifo_trigger.o: trigger.h +file_limit.o: file_limit.c +file_limit.o: sys_defs.h +file_limit.o: msg.h +file_limit.o: iostuff.h +find_inet.o: find_inet.c +find_inet.o: sys_defs.h +find_inet.o: msg.h +find_inet.o: find_inet.h +fsspace.o: fsspace.c +fsspace.o: sys_defs.h +fsspace.o: msg.h +fsspace.o: fsspace.h +fullname.o: fullname.c +fullname.o: sys_defs.h +fullname.o: vstring.h +fullname.o: vbuf.h +fullname.o: safe.h +fullname.o: fullname.h +get_domainname.o: get_domainname.c +get_domainname.o: sys_defs.h +get_domainname.o: mymalloc.h +get_domainname.o: get_hostname.h +get_domainname.o: get_domainname.h +get_hostname.o: get_hostname.c +get_hostname.o: sys_defs.h +get_hostname.o: mymalloc.h +get_hostname.o: msg.h +get_hostname.o: valid_hostname.h +get_hostname.o: get_hostname.h +htable.o: htable.c +htable.o: sys_defs.h +htable.o: mymalloc.h +htable.o: msg.h +htable.o: htable.h +inet_addr_host.o: inet_addr_host.c +inet_addr_host.o: sys_defs.h +inet_addr_host.o: inet_addr_list.h +inet_addr_host.o: inet_addr_host.h +inet_addr_list.o: inet_addr_list.c +inet_addr_list.o: sys_defs.h +inet_addr_list.o: msg.h +inet_addr_list.o: mymalloc.h +inet_addr_list.o: inet_addr_list.h +inet_addr_local.o: inet_addr_local.c +inet_addr_local.o: sys_defs.h +inet_addr_local.o: msg.h +inet_addr_local.o: mymalloc.h +inet_addr_local.o: vstring.h +inet_addr_local.o: vbuf.h +inet_addr_local.o: inet_addr_list.h +inet_addr_local.o: inet_addr_local.h +inet_connect.o: inet_connect.c +inet_connect.o: sys_defs.h +inet_connect.o: mymalloc.h +inet_connect.o: msg.h +inet_connect.o: find_inet.h +inet_connect.o: inet_util.h +inet_connect.o: iostuff.h +inet_connect.o: connect.h +inet_connect.o: timed_connect.h +inet_listen.o: inet_listen.c +inet_listen.o: sys_defs.h +inet_listen.o: mymalloc.h +inet_listen.o: msg.h +inet_listen.o: find_inet.h +inet_listen.o: inet_util.h +inet_listen.o: iostuff.h +inet_listen.o: listen.h +inet_trigger.o: inet_trigger.c +inet_trigger.o: sys_defs.h +inet_trigger.o: msg.h +inet_trigger.o: connect.h +inet_trigger.o: iostuff.h +inet_trigger.o: trigger.h +inet_util.o: inet_util.c +inet_util.o: sys_defs.h +inet_util.o: mymalloc.h +inet_util.o: split_at.h +inet_util.o: inet_util.h +line_wrap.o: line_wrap.c +line_wrap.o: sys_defs.h +line_wrap.o: line_wrap.h +lowercase.o: lowercase.c +lowercase.o: sys_defs.h +lowercase.o: stringops.h +lstat_as.o: lstat_as.c +lstat_as.o: sys_defs.h +lstat_as.o: msg.h +lstat_as.o: set_eugid.h +lstat_as.o: lstat_as.h +mac_parse.o: mac_parse.c +mac_parse.o: sys_defs.h +mac_parse.o: msg.h +mac_parse.o: mac_parse.h +mac_parse.o: vstring.h +mac_parse.o: vbuf.h +make_dirs.o: make_dirs.c +make_dirs.o: sys_defs.h +make_dirs.o: msg.h +make_dirs.o: mymalloc.h +make_dirs.o: stringops.h +make_dirs.o: make_dirs.h +match_list.o: match_list.c +match_list.o: sys_defs.h +match_list.o: msg.h +match_list.o: mymalloc.h +match_list.o: vstring.h +match_list.o: vbuf.h +match_list.o: vstream.h +match_list.o: vstring_vstream.h +match_list.o: stringops.h +match_list.o: argv.h +match_list.o: dict.h +match_list.o: match_list.h +match_ops.o: match_ops.c +match_ops.o: sys_defs.h +match_ops.o: msg.h +match_ops.o: mymalloc.h +match_ops.o: split_at.h +match_ops.o: dict.h +match_ops.o: vstream.h +match_ops.o: vbuf.h +match_ops.o: match_ops.h +match_ops.o: stringops.h +msg.o: msg.c +msg.o: sys_defs.h +msg.o: msg.h +msg.o: msg_output.h +msg_output.o: msg_output.c +msg_output.o: sys_defs.h +msg_output.o: mymalloc.h +msg_output.o: vstring.h +msg_output.o: vbuf.h +msg_output.o: vstream.h +msg_output.o: msg_vstream.h +msg_output.o: stringops.h +msg_output.o: percentm.h +msg_output.o: msg_output.h +msg_syslog.o: msg_syslog.c +msg_syslog.o: sys_defs.h +msg_syslog.o: vstring.h +msg_syslog.o: vbuf.h +msg_syslog.o: stringops.h +msg_syslog.o: msg.h +msg_syslog.o: msg_output.h +msg_syslog.o: msg_syslog.h +msg_vstream.o: msg_vstream.c +msg_vstream.o: sys_defs.h +msg_vstream.o: vstream.h +msg_vstream.o: vbuf.h +msg_vstream.o: msg.h +msg_vstream.o: msg_output.h +msg_vstream.o: msg_vstream.h +mvect.o: mvect.c +mvect.o: sys_defs.h +mvect.o: mymalloc.h +mvect.o: mvect.h +myflock.o: myflock.c +myflock.o: sys_defs.h +myflock.o: msg.h +myflock.o: myflock.h +mymalloc.o: mymalloc.c +mymalloc.o: sys_defs.h +mymalloc.o: msg.h +mymalloc.o: mymalloc.h +mystrtok.o: mystrtok.c +mystrtok.o: sys_defs.h +mystrtok.o: stringops.h +name_mask.o: name_mask.c +name_mask.o: sys_defs.h +name_mask.o: msg.h +name_mask.o: mymalloc.h +name_mask.o: stringops.h +name_mask.o: name_mask.h +non_blocking.o: non_blocking.c +non_blocking.o: sys_defs.h +non_blocking.o: msg.h +non_blocking.o: iostuff.h +open_as.o: open_as.c +open_as.o: sys_defs.h +open_as.o: msg.h +open_as.o: set_eugid.h +open_as.o: open_as.h +open_limit.o: open_limit.c +open_limit.o: sys_defs.h +open_limit.o: iostuff.h +open_lock.o: open_lock.c +open_lock.o: sys_defs.h +open_lock.o: msg.h +open_lock.o: vstream.h +open_lock.o: vbuf.h +open_lock.o: vstring.h +open_lock.o: safe_open.h +open_lock.o: myflock.h +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 +percentm.o: vbuf.h +percentm.o: percentm.h +posix_signals.o: posix_signals.c +posix_signals.o: sys_defs.h +printable.o: printable.c +printable.o: sys_defs.h +printable.o: stringops.h +read_wait.o: read_wait.c +read_wait.o: sys_defs.h +read_wait.o: msg.h +read_wait.o: iostuff.h +readable.o: readable.c +readable.o: sys_defs.h +readable.o: msg.h +readable.o: iostuff.h +readline.o: readline.c +readline.o: sys_defs.h +readline.o: vstream.h +readline.o: vbuf.h +readline.o: vstring.h +readline.o: readline.h +ring.o: ring.c +ring.o: ring.h +safe_getenv.o: safe_getenv.c +safe_getenv.o: sys_defs.h +safe_getenv.o: safe.h +safe_open.o: safe_open.c +safe_open.o: sys_defs.h +safe_open.o: msg.h +safe_open.o: vstream.h +safe_open.o: vbuf.h +safe_open.o: vstring.h +safe_open.o: safe_open.h +sane_accept.o: sane_accept.c +sane_accept.o: sys_defs.h +sane_accept.o: sane_accept.h +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: scan_dir.h +set_eugid.o: set_eugid.c +set_eugid.o: sys_defs.h +set_eugid.o: msg.h +set_eugid.o: set_eugid.h +set_ugid.o: set_ugid.c +set_ugid.o: sys_defs.h +set_ugid.o: msg.h +set_ugid.o: set_ugid.h +sigdelay.o: sigdelay.c +sigdelay.o: sys_defs.h +sigdelay.o: msg.h +sigdelay.o: posix_signals.h +sigdelay.o: sigdelay.h +skipblanks.o: skipblanks.c +skipblanks.o: sys_defs.h +skipblanks.o: stringops.h +split_at.o: split_at.c +split_at.o: sys_defs.h +split_at.o: split_at.h +stat_as.o: stat_as.c +stat_as.o: sys_defs.h +stat_as.o: msg.h +stat_as.o: set_eugid.h +stat_as.o: stat_as.h +sys_compat.o: sys_compat.c +sys_compat.o: sys_defs.h +timed_connect.o: timed_connect.c +timed_connect.o: sys_defs.h +timed_connect.o: msg.h +timed_connect.o: iostuff.h +timed_connect.o: timed_connect.h +timed_wait.o: timed_wait.c +timed_wait.o: sys_defs.h +timed_wait.o: msg.h +timed_wait.o: posix_signals.h +timed_wait.o: timed_wait.h +translit.o: translit.c +translit.o: sys_defs.h +translit.o: stringops.h +trimblanks.o: trimblanks.c +trimblanks.o: sys_defs.h +trimblanks.o: stringops.h +unix_connect.o: unix_connect.c +unix_connect.o: sys_defs.h +unix_connect.o: msg.h +unix_connect.o: iostuff.h +unix_connect.o: connect.h +unix_connect.o: timed_connect.h +unix_listen.o: unix_listen.c +unix_listen.o: sys_defs.h +unix_listen.o: msg.h +unix_listen.o: iostuff.h +unix_listen.o: listen.h +unix_trigger.o: unix_trigger.c +unix_trigger.o: sys_defs.h +unix_trigger.o: msg.h +unix_trigger.o: connect.h +unix_trigger.o: iostuff.h +unix_trigger.o: trigger.h +unsafe.o: unsafe.c +unsafe.o: sys_defs.h +unsafe.o: safe.h +username.o: username.c +username.o: sys_defs.h +username.o: username.h +valid_hostname.o: valid_hostname.c +valid_hostname.o: sys_defs.h +valid_hostname.o: msg.h +valid_hostname.o: mymalloc.h +valid_hostname.o: stringops.h +valid_hostname.o: valid_hostname.h +vbuf.o: vbuf.c +vbuf.o: sys_defs.h +vbuf.o: vbuf.h +vbuf_print.o: vbuf_print.c +vbuf_print.o: sys_defs.h +vbuf_print.o: msg.h +vbuf_print.o: vbuf.h +vbuf_print.o: vstring.h +vbuf_print.o: vbuf_print.h +vstream.o: vstream.c +vstream.o: sys_defs.h +vstream.o: mymalloc.h +vstream.o: msg.h +vstream.o: vbuf_print.h +vstream.o: vbuf.h +vstream.o: vstring.h +vstream.o: vstream.h +vstream_popen.o: vstream_popen.c +vstream_popen.o: sys_defs.h +vstream_popen.o: msg.h +vstream_popen.o: binhash.h +vstream_popen.o: exec_command.h +vstream_popen.o: vstream.h +vstream_popen.o: vbuf.h +vstring.o: vstring.c +vstring.o: sys_defs.h +vstring.o: mymalloc.h +vstring.o: msg.h +vstring.o: vbuf_print.h +vstring.o: vbuf.h +vstring.o: vstring.h +vstring_vstream.o: vstring_vstream.c +vstring_vstream.o: sys_defs.h +vstring_vstream.o: msg.h +vstring_vstream.o: vstring.h +vstring_vstream.o: vbuf.h +vstring_vstream.o: vstream.h +vstring_vstream.o: vstring_vstream.h +writable.o: writable.c +writable.o: sys_defs.h +writable.o: msg.h +writable.o: iostuff.h +write_buf.o: write_buf.c +write_buf.o: sys_defs.h +write_buf.o: msg.h +write_buf.o: iostuff.h +write_wait.o: write_wait.c +write_wait.o: sys_defs.h +write_wait.o: msg.h +write_wait.o: iostuff.h diff --git a/postfix/util/argv.c b/postfix/util/argv.c new file mode 100644 index 000000000..e92f231fc --- /dev/null +++ b/postfix/util/argv.c @@ -0,0 +1,132 @@ +/*++ +/* NAME +/* argv 3 +/* SUMMARY +/* string array utilities +/* SYNOPSIS +/* #include +/* +/* ARGV *argv_alloc(len) +/* int len; +/* +/* ARGV *argv_free(argvp) +/* ARGV *argvp; +/* +/* void argv_add(argvp, arg, ..., ARGV_END) +/* ARGV *argvp; +/* char *arg; +/* +/* void argv_terminate(argvp); +/* ARGV *argvp; +/* DESCRIPTION +/* The functions in this module functions manipulate arrays of string +/* pointers. An ARGV structure contains the following members: +/* .IP len +/* The length of the \fIargv\fR array member. +/* .IP argc +/* The number of \fIargv\fR elements used. +/* .IP argv +/* An array of pointers to null-terminated strings. +/* .PP +/* argv_alloc() returns an empty string array of the requested +/* length. The result is ready for use by argv_add(). The array +/* is not null terminated. +/* +/* argv_add() copies zero or more strings and adds them to the +/* specified string array. The array is not null terminated. +/* Terminate the argument list with a null pointer. The manifest +/* constant ARGV_END provides a convenient notation for this. +/* +/* argv_free() releases storage for a string array, and conveniently +/* returns a null pointer. +/* +/* argv_terminate() null-terminates its string array argument. +/* SEE ALSO +/* msg(3) diagnostics interface +/* DIAGNOSTICS +/* Fatal errors: memory allocation problem. +/* 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 libraries. */ + +#include /* 44BSD stdarg.h uses abort() */ +#include +#include + +/* Application-specific. */ + +#include "mymalloc.h" +#include "argv.h" + +/* argv_free - destroy string array */ + +ARGV *argv_free(ARGV *argvp) +{ + char **cpp; + + for (cpp = argvp->argv; cpp < argvp->argv + argvp->argc; cpp++) + myfree(*cpp); + myfree((char *) argvp->argv); + myfree((char *) argvp); + return (0); +} + +/* argv_alloc - initialize string array */ + +ARGV *argv_alloc(int len) +{ + ARGV *argvp; + + /* + * Make sure that always argvp->argc < argvp->len. + */ + argvp = (ARGV *) mymalloc(sizeof(*argvp)); + argvp->len = (len < 2 ? 2 : len); + argvp->argv = (char **) mymalloc((argvp->len + 1) * sizeof(char *)); + argvp->argc = 0; + argvp->argv[0] = 0; + return (argvp); +} + +/* argv_add - add string to vector */ + +void argv_add(ARGV *argvp,...) +{ + char *arg; + va_list ap; + + /* + * Make sure that always argvp->argc < argvp->len. + */ + va_start(ap, argvp); + while ((arg = va_arg(ap, char *)) != 0) { + if (argvp->argc + 1 >= argvp->len) { + argvp->len *= 2; + argvp->argv = (char **) + myrealloc((char *) argvp->argv, + (argvp->len + 1) * sizeof(char *)); + } + argvp->argv[argvp->argc++] = mystrdup(arg); + } + va_end(ap); +} + +/* argv_terminate - terminate string array */ + +void argv_terminate(ARGV *argvp) +{ + + /* + * Trust that argvp->argc < argvp->len. + */ + argvp->argv[argvp->argc] = 0; +} diff --git a/postfix/util/argv.h b/postfix/util/argv.h new file mode 100644 index 000000000..0d5562310 --- /dev/null +++ b/postfix/util/argv.h @@ -0,0 +1,44 @@ +#ifndef _ARGV_H_INCLUDED_ +#define _ARGV_H_INCLUDED_ + +/*++ +/* NAME +/* argv 3h +/* SUMMARY +/* string array utilities +/* SYNOPSIS +/* #include "argv.h" + DESCRIPTION + .nf + + /* + * External interface. + */ +typedef struct ARGV { + int len; /* number of array elements */ + int argc; /* array elements in use */ + char **argv; /* string array */ +} ARGV; + +extern ARGV *argv_alloc(int); +extern void argv_add(ARGV *,...); +extern void argv_terminate(ARGV *); +extern ARGV *argv_free(ARGV *); + +extern ARGV *argv_split(const char *, const char *); +extern ARGV *argv_split_append(ARGV *, const char *, const char *); + +#define ARGV_END ((char *) 0) + +/* 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/argv_split.c b/postfix/util/argv_split.c new file mode 100644 index 000000000..d7e6bafa2 --- /dev/null +++ b/postfix/util/argv_split.c @@ -0,0 +1,77 @@ +/*++ +/* NAME +/* argv_split 3 +/* SUMMARY +/* string array utilities +/* SYNOPSIS +/* #include +/* +/* ARGV *argv_split(string, delim) +/* const char *string; +/* +/* ARGV *argv_split_append(argv, string, delim) +/* ARGV *argv; +/* const char *string; +/* const char *delim; +/* DESCRIPTION +/* argv_split() breaks up \fIstring\fR into tokens according +/* to the delimiters specified in \fIdelim\fR. The result is +/* a null-terminated string array. +/* +/* argv_split_append() performs the same operation as argv_split(), +/* but appends the result to an existing string array. +/* SEE ALSO +/* mystrtok(), safe string splitter. +/* DIAGNOSTICS +/* Fatal errors: memory allocation problem. +/* 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 libraries. */ + +#include + +/* Application-specific. */ + +#include "mymalloc.h" +#include "stringops.h" +#include "argv.h" + +/* argv_split - split string into token array */ + +ARGV *argv_split(const char *string, const char *delim) +{ + ARGV *argvp = argv_alloc(1); + char *saved_string = mystrdup(string); + char *bp = saved_string; + char *arg; + + while ((arg = mystrtok(&bp, delim)) != 0) + argv_add(argvp, arg, (char *) 0); + argv_terminate(argvp); + myfree(saved_string); + return (argvp); +} + +/* argv_split_append - split string into token array, append to array */ + +ARGV *argv_split_append(ARGV *argvp, const char *string, const char *delim) +{ + char *saved_string = mystrdup(string); + char *bp = saved_string; + char *arg; + + while ((arg = mystrtok(&bp, delim)) != 0) + argv_add(argvp, arg, (char *) 0); + argv_terminate(argvp); + myfree(saved_string); + return (argvp); +} diff --git a/postfix/util/attr.c b/postfix/util/attr.c new file mode 100644 index 000000000..303274543 --- /dev/null +++ b/postfix/util/attr.c @@ -0,0 +1,78 @@ +/*++ +/* NAME +/* attr 3 +/* SUMMARY +/* attribute list manager +/* SYNOPSIS +/* #include +/* #include +/* +/* void attr_enter(attr, name, value) +/* HTABLE *attr; +/* const char *name; +/* const char *value; +/* +/* const char *attr_find(attr, name) +/* HTABLE *attr; +/* const char *name; +/* +/* void attr_free(attr) +/* HTABLE *attr; +/* DESCRIPTION +/* This module maintains open attribute lists of string-valued +/* names and values. The module is built on top of the generic +/* htable(3) hash table manager. +/* +/* attr_enter() adds a new attribute or updates an existing one. +/* Both the name and the value are copied. +/* +/* attr_find() looks up the named attribute. It returns the +/* corresponding value if one is found, a null pointer otherwise. +/* +/* attr_free() destroys the named attribute list and makes its +/* memory available for reuse. +/* BUGS +/* This module cannot store null pointer values. If that is a +/* problem, use the raw hash table management routines instead. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include "sys_defs.h" + +/* Utility library. */ + +#include "mymalloc.h" +#include "htable.h" +#include "attr.h" + +/* attr_enter - add or replace attribute */ + +void attr_enter(HTABLE *attr, const char *name, const char *value) +{ + HTABLE_INFO *ht; + + if ((ht = htable_locate(attr, name)) != 0) {/* replace attribute */ + myfree(ht->value); + ht->value = mystrdup(value); + } else { /* add attribute */ + (void) htable_enter(attr, name, mystrdup(value)); + } +} + +/* attr_free - destroy attribute list */ + +void attr_free(HTABLE *attr) +{ + htable_free(attr, myfree); +} + diff --git a/postfix/util/attr.h b/postfix/util/attr.h new file mode 100644 index 000000000..e81381ed3 --- /dev/null +++ b/postfix/util/attr.h @@ -0,0 +1,33 @@ +#ifndef _ATTR_H_INCLUDED_ +#define _ATTR_H_INCLUDED_ + +/*++ +/* NAME +/* attr 3h +/* SUMMARY +/* attribute list manager +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern void attr_enter(HTABLE *, const char *, const char *); +extern void attr_free(HTABLE *); + +#define attr_find(table, name) htable_find((table), (name)) + +/* 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/basename.c b/postfix/util/basename.c new file mode 100644 index 000000000..429e6cf44 --- /dev/null +++ b/postfix/util/basename.c @@ -0,0 +1,49 @@ +/*++ +/* NAME +/* basename 3 +/* SUMMARY +/* extract file basename +/* SYNOPSIS +/* #include +/* +/* char *basename(path) +/* const char *path; +/* DESCRIPTION +/* The \fBbasename\fR routine skips over the last '/' in +/* \fIpath\fR and returns a pointer to the result. +/* 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 + +#ifndef HAVE_BASENAME + +/* Utility library. */ + +#include "stringops.h" + +/* basename - skip directory prefix */ + +char *basename(const char *path) +{ + char *result; + + if ((result = strrchr(path, '/')) == 0) + result = (char *) path; + else + result += 1; + return (result); +} + +#endif diff --git a/postfix/util/binhash.c b/postfix/util/binhash.c new file mode 100644 index 000000000..0ab34989d --- /dev/null +++ b/postfix/util/binhash.c @@ -0,0 +1,339 @@ +/*++ +/* NAME +/* binhash 3 +/* SUMMARY +/* hash table manager +/* SYNOPSIS +/* #include +/* +/* typedef struct { +/* .in +4 +/* char *key; +/* int key_len; +/* char *value; +/* /* private fields... */ +/* .in -4 +/* } BINHASH_INFO; +/* +/* BINHASH *binhash_create(size) +/* int size; +/* +/* BINHASH_INFO *binhash_enter(table, key, key_len, value) +/* BINHASH *table; +/* const char *key; +/* int key_len; +/* char *value; +/* +/* char *binhash_find(table, key, key_len) +/* BINHASH *table; +/* const char *key; +/* int key_len; +/* +/* BINHASH_INFO *binhash_locate(table, key, key_len) +/* BINHASH *table; +/* const char *key; +/* int key_len; +/* +/* void binhash_delete(table, key, key_len, free_fn) +/* BINHASH *table; +/* const char *key; +/* int key_len; +/* void (*free_fn)(char *); +/* +/* void binhash_free(table, free_fn) +/* BINHASH *table; +/* void (*free_fn)(char *); +/* +/* void binhash_walk(table, action) +/* BINHASH *table; +/* void (*action)(BINHASH_INFO *); +/* +/* BINHASH_INFO **binhash_list(table) +/* BINHASH *table; +/* DESCRIPTION +/* This module maintains one or more hash tables. Each table entry +/* consists of a unique binary-valued lookup key and a generic +/* character-pointer value. +/* The tables are automatically resized when they fill up. When the +/* values to be remembered are not character pointers, proper casts +/* should be used or the code will not be portable. +/* +/* binhash_create() creates a table of the specified size and returns a +/* pointer to the result. The lookup keys are saved with strdup(). +/* +/* binhash_enter() stores a (key, value) pair into the specified table +/* and returns a pointer to the resulting entry. The code does not +/* check if an entry with that key already exists: use binhash_locate() +/* for updating an existing entry. The key is copied; the value is not. +/* +/* binhash_find() returns the value that was stored under the given key, +/* or a null pointer if it was not found. In order to distinguish +/* a null value from a non-existent value, use binhash_locate(). +/* +/* binhash_locate() returns a pointer to the entry that was stored +/* for the given key, or a null pointer if it was not found. +/* +/* binhash_delete() removes one entry that was stored under the given key. +/* If the free_fn argument is not a null pointer, the corresponding +/* function is called with as argument the value that was stored under +/* the key. +/* +/* binhash_free() destroys a hash table, including contents. If the free_fn +/* argument is not a null pointer, the corresponding function is called +/* for each table entry, with as argument the value that was stored +/* with the entry. +/* +/* binhash_walk() invokes the action function for each table entry, with +/* a pointer to the entry as its argument. +/* +/* binhash_list() returns a null-terminated list of pointers to +/* all elements in the named table. The list should be passed to +/* myfree(). +/* RESTRICTIONS +/* A callback function should not modify the hash table that is +/* specified to its caller. +/* DIAGNOSTICS +/* The following conditions are reported and cause the program to +/* terminate immediately: memory allocation failure; an attempt +/* to delete a non-existent entry. +/* SEE ALSO +/* mymalloc(3) memory management wrapper +/* 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 +/*--*/ + +/* C library */ + +#include +#include + +/* Local stuff */ + +#include "mymalloc.h" +#include "msg.h" +#include "binhash.h" + +/* binhash_hash - hash a string */ + +static unsigned binhash_hash(const char *key, int len, unsigned size) +{ + unsigned long h = 0; + unsigned long g; + + /* + * From the "Dragon" book by Aho, Sethi and Ullman. + */ + + while (len-- > 0) { + h = (h << 4) + *key++; + if ((g = (h & 0xf0000000)) != 0) { + h ^= (g >> 24); + h ^= g; + } + } + return (h % size); +} + +/* binhash_link - insert element into table */ + +#define binhash_link(table, elm) { \ + BINHASH_INFO **h = table->data + binhash_hash(elm->key, elm->key_len, table->size);\ + elm->prev = 0; \ + if ((elm->next = *h) != 0) \ + (*h)->prev = elm; \ + *h = elm; \ + table->used++; \ +} + +/* binhash_size - allocate and initialize hash table */ + +static void binhash_size(BINHASH *table, unsigned size) +{ + BINHASH_INFO **h; + + size |= 1; + + table->data = h = (BINHASH_INFO **) mymalloc(size * sizeof(BINHASH_INFO *)); + table->size = size; + table->used = 0; + + while (size-- > 0) + *h++ = 0; +} + +/* binhash_create - create initial hash table */ + +BINHASH *binhash_create(int size) +{ + BINHASH *table; + + table = (BINHASH *) mymalloc(sizeof(BINHASH)); + binhash_size(table, size < 13 ? 13 : size); + return (table); +} + +/* binhash_grow - extend existing table */ + +static void binhash_grow(BINHASH *table) +{ + BINHASH_INFO *ht; + BINHASH_INFO *next; + unsigned old_size = table->size; + BINHASH_INFO **h = table->data; + BINHASH_INFO **old_entries = h; + + binhash_size(table, 2 * old_size); + + while (old_size-- > 0) { + for (ht = *h++; ht; ht = next) { + next = ht->next; + binhash_link(table, ht); + } + } + myfree((char *) old_entries); +} + +/* binhash_enter - enter (key, value) pair */ + +BINHASH_INFO *binhash_enter(BINHASH *table, const char *key, int key_len, char *value) +{ + BINHASH_INFO *ht; + + if (table->used >= table->size) + binhash_grow(table); + ht = (BINHASH_INFO *) mymalloc(sizeof(BINHASH_INFO)); + ht->key = mymemdup(key, key_len); + ht->key_len = key_len; + ht->value = value; + binhash_link(table, ht); + return (ht); +} + +/* binhash_find - lookup value */ + +char *binhash_find(BINHASH *table, const char *key, int key_len) +{ + BINHASH_INFO *ht; + +#define KEY_EQ(x,y,l) (x[0] == y[0] && memcmp(x,y,l) == 0) + + if (table != 0) + for (ht = table->data[binhash_hash(key, key_len, table->size)]; ht; ht = ht->next) + if (key_len == ht->key_len && KEY_EQ(key, ht->key, key_len)) + return (ht->value); + return (0); +} + +/* binhash_locate - lookup entry */ + +BINHASH_INFO *binhash_locate(BINHASH *table, const char *key, int key_len) +{ + BINHASH_INFO *ht; + +#define KEY_EQ(x,y,l) (x[0] == y[0] && memcmp(x,y,l) == 0) + + if (table != 0) + for (ht = table->data[binhash_hash(key, key_len, table->size)]; ht; ht = ht->next) + if (key_len == ht->key_len && KEY_EQ(key, ht->key, key_len)) + return (ht); + return (0); +} + +/* binhash_delete - delete one entry */ + +void binhash_delete(BINHASH *table, const char *key, int key_len, void (*free_fn) (char *)) +{ + if (table != 0) { + BINHASH_INFO *ht; + BINHASH_INFO **h = table->data + binhash_hash(key, key_len, table->size); + +#define KEY_EQ(x,y,l) (x[0] == y[0] && memcmp(x,y,l) == 0) + + for (ht = *h; ht; ht = ht->next) { + if (key_len == ht->key_len && KEY_EQ(key, ht->key, key_len)) { + if (ht->next) + ht->next->prev = ht->prev; + if (ht->prev) + ht->prev->next = ht->next; + else + *h = ht->next; + table->used--; + myfree(ht->key); + if (free_fn) + (*free_fn) (ht->value); + myfree((char *) ht); + return; + } + } + msg_panic("binhash_delete: unknown_key: \"%s\"", key); + } +} + +/* binhash_free - destroy hash table */ + +void binhash_free(BINHASH *table, void (*free_fn) (char *)) +{ + if (table != 0) { + unsigned i = table->size; + BINHASH_INFO *ht; + BINHASH_INFO *next; + BINHASH_INFO **h = table->data; + + while (i-- > 0) { + for (ht = *h++; ht; ht = next) { + next = ht->next; + myfree(ht->key); + if (free_fn) + (*free_fn) (ht->value); + myfree((char *) ht); + } + } + myfree((char *) table->data); + table->data = 0; + myfree((char *) table); + } +} + +/* binhash_walk - iterate over hash table */ + +void binhash_walk(BINHASH *table, void (*action) (BINHASH_INFO *)) +{ + if (table != 0) { + unsigned i = table->size; + BINHASH_INFO **h = table->data; + BINHASH_INFO *ht; + + while (i-- > 0) + for (ht = *h++; ht; ht = ht->next) + (*action) (ht); + } +} + +/* binhash_list - list all table members */ + +BINHASH_INFO **binhash_list(table) +BINHASH *table; +{ + BINHASH_INFO **list; + BINHASH_INFO *member; + int count = 0; + int i; + + if (table != 0) { + list = (BINHASH_INFO **) mymalloc(sizeof(*list) * (table->used + 1)); + for (i = 0; i < table->size; i++) + for (member = table->data[i]; member != 0; member = member->next) + list[count++] = member; + } else { + list = (BINHASH_INFO **) mymalloc(sizeof(*list)); + } + list[count] = 0; + return (list); +} diff --git a/postfix/util/binhash.h b/postfix/util/binhash.h new file mode 100644 index 000000000..814ec8ad3 --- /dev/null +++ b/postfix/util/binhash.h @@ -0,0 +1,58 @@ +#ifndef _BINHASH_H_INCLUDED_ +#define _BINHASH_H_INCLUDED_ + +/*++ +/* NAME +/* binhash 3h +/* SUMMARY +/* hash table manager +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* Structure of one hash table entry. */ + +typedef struct BINHASH_INFO { + char *key; /* lookup key */ + int key_len; /* key length */ + char *value; /* associated value */ + struct BINHASH_INFO *next; /* colliding entry */ + struct BINHASH_INFO *prev; /* colliding entry */ +} BINHASH_INFO; + + /* Structure of one hash table. */ + +typedef struct BINHASH { + int size; /* length of entries array */ + int used; /* number of entries in table */ + BINHASH_INFO **data; /* entries array, auto-resized */ +} BINHASH; + +extern BINHASH *binhash_create(int); +extern BINHASH_INFO *binhash_enter(BINHASH *, const char *, int, char *); +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 BINHASH_INFO **binhash_list(BINHASH *); + +/* 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 +/* CREATION DATE +/* Thu Feb 20 16:54:29 EST 1997 +/* LAST MODIFICATION +/* %E% %U% +/* VERSION/RELEASE +/* %I% +/*--*/ + +#endif diff --git a/postfix/util/chroot_uid.c b/postfix/util/chroot_uid.c new file mode 100644 index 000000000..c64c1b491 --- /dev/null +++ b/postfix/util/chroot_uid.c @@ -0,0 +1,88 @@ +/*++ +/* NAME +/* chroot_uid 3 +/* SUMMARY +/* limit possible damage a process can do +/* SYNOPSIS +/* #include +/* +/* void chroot_uid(root_dir, user_name) +/* const char *root_dir; +/* const char *user_name; +/* DESCRIPTION +/* \fBchroot_uid\fR changes the process root to \fIroot_dir\fR and +/* changes process privileges to those of \fIuser_name\fR. +/* DIAGNOSTICS +/* System call errors are reported via the msg(3) interface. +/* All errors are fatal. +/* 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 + +/* Utility library. */ + +#include "msg.h" +#include "chroot_uid.h" + +/* chroot_uid - restrict the damage that this program can do */ + +void chroot_uid(const char *root_dir, const char *user_name) +{ + struct passwd *pwd; + uid_t uid; + gid_t gid; + + /* + * Look up the uid/gid before entering the jail, and save them so they + * can't be clobbered. Set up the primary and secondary groups. + */ + if (user_name != 0) { + if ((pwd = getpwnam(user_name)) == 0) + msg_fatal("unknown user: %s", user_name); + uid = pwd->pw_uid; + gid = pwd->pw_gid; + if (setgid(gid) < 0) + msg_fatal("setgid(%d): %m", gid); + if (initgroups(user_name, gid) < 0) + msg_fatal("initgroups: %m"); + } + + /* + * Enter the jail. + */ + if (root_dir) { + if (chroot(root_dir)) + msg_fatal("chroot(%s): %m", root_dir); + if (chdir("/")) + msg_fatal("chdir(/): %m"); + } + + /* + * Drop the user privileges. + */ + if (user_name != 0) + if (setuid(uid) < 0) + msg_fatal("setuid(%d): %m", uid); + + /* + * Give the desperate developer a clue of what is happening. + */ + if (msg_verbose > 1) + msg_info("chroot %s user %s", + root_dir ? root_dir : "(none)", + user_name ? user_name : "(none)"); +} diff --git a/postfix/util/chroot_uid.h b/postfix/util/chroot_uid.h new file mode 100644 index 000000000..f2a839920 --- /dev/null +++ b/postfix/util/chroot_uid.h @@ -0,0 +1,29 @@ +#ifndef _CHROOT_UID_H_INCLUDED_ +#define _CHROOT_UID_H_INCLUDED_ + +/*++ +/* NAME +/* chroot_uid 3h +/* SUMMARY +/* limit possible damage a process can do +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern void chroot_uid(const char *, const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/util/close_on_exec.c b/postfix/util/close_on_exec.c new file mode 100644 index 000000000..efa341538 --- /dev/null +++ b/postfix/util/close_on_exec.c @@ -0,0 +1,60 @@ +/*++ +/* NAME +/* close_on_exec 3 +/* SUMMARY +/* set/clear close-on-exec flag +/* SYNOPSIS +/* #include +/* +/* int close_on_exec(int fd, int on) +/* DESCRIPTION +/* the \fIclose_on_exec\fR() function manipulates the close-on-exec +/* flag for the specified open file, and returns the old setting. +/* +/* Arguments: +/* .IP fd +/* A file descriptor. +/* .IP on +/* Use CLOSE_ON_EXEC or PASS_ON_EXEC. +/* DIAGNOSTICS +/* All errors are fatal. +/* 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 interfaces. */ + +#include +#include + +/* Utility library. */ + +#include "msg.h" + +/* Application-specific. */ + +#include "iostuff.h" + +#define PATTERN FD_CLOEXEC + +/* close_on_exec - set/clear close-on-exec flag */ + +int close_on_exec(fd, on) +int fd; +int on; +{ + int flags; + + if ((flags = fcntl(fd, F_GETFD, 0)) < 0) + msg_fatal("fcntl: get flags: %m"); + if (fcntl(fd, F_SETFD, on ? flags | PATTERN : flags & ~PATTERN) < 0) + msg_fatal("fcntl: set close-on-exec flag %s: %m", on ? "on" : "off"); + return ((flags & PATTERN) != 0); +} diff --git a/postfix/util/concatenate.c b/postfix/util/concatenate.c new file mode 100644 index 000000000..cf0d45d1b --- /dev/null +++ b/postfix/util/concatenate.c @@ -0,0 +1,67 @@ +/*++ +/* NAME +/* concatenate 3 +/* SUMMARY +/* concatenate strings +/* SYNOPSIS +/* #include +/* +/* char *concatenate(str, ...) +/* const char *str; +/* DESCRIPTION +/* The \fBconcatenate\fR routine concatenates a null-terminated +/* list of pointers to null-terminated character strings. +/* The result is dynamically allocated and should be passed to myfree() +/* when no longer needed. +/* 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 /* 44BSD stdarg.h uses abort() */ +#include +#include + +/* Utility library. */ + +#include "mymalloc.h" +#include "stringops.h" + +/* concatenate - concatenate null-terminated list of strings */ + +char *concatenate(const char *arg0,...) +{ + char *result; + va_list ap; + int len; + char *arg; + + /* + * Compute the length of the resulting string. + */ + va_start(ap, arg0); + len = strlen(arg0); + while ((arg = va_arg(ap, char *)) != 0) + len += strlen(arg); + va_end(ap); + + /* + * Build the resulting string. Don't care about wasting a CPU cycle. + */ + result = mymalloc(len + 1); + va_start(ap, arg0); + strcpy(result, arg0); + while ((arg = va_arg(ap, char *)) != 0) + strcat(result, arg); + va_end(ap); + return (result); +} diff --git a/postfix/util/connect.h b/postfix/util/connect.h new file mode 100644 index 000000000..5fc5e0413 --- /dev/null +++ b/postfix/util/connect.h @@ -0,0 +1,36 @@ +#ifndef _CONNECT_H_INCLUDED_ +#define _CONNECT_H_INCLUDED_ + +/*++ +/* NAME +/* connect 3h +/* SUMMARY +/* client interface file +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * Client external interface. + */ +extern int unix_connect(const char *, int, int); +extern int inet_connect(const char *, int, 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/dict.c b/postfix/util/dict.c new file mode 100644 index 000000000..82347ddfb --- /dev/null +++ b/postfix/util/dict.c @@ -0,0 +1,437 @@ +/*++ +/* NAME +/* dict 3 +/* SUMMARY +/* dictionary manager +/* SYNOPSIS +/* #include +/* +/* extern int dict_unknown_allowed; +/* extern int dict_errno; +/* +/* void dict_register(dict_name, dict_info) +/* const const char *dict_name; +/* DICT *dict_info; +/* +/* DICT *dict_handle(dict_name) +/* const const char *dict_name; +/* +/* void dict_unregister(dict_name) +/* const const char *dict_name; +/* +/* void dict_update(dict_name, member, value) +/* const const char *dict_name; +/* const const char *member; +/* const char *value; +/* +/* const char *dict_lookup(dict_name, member) +/* const const char *dict_name; +/* const const char *member; +/* +/* const char *dict_eval(dict_name, string, int recursive) +/* const const char *dict_name; +/* const char *string; +/* int recursive; +/* AUXILIARY FUNCTIONS +/* void dict_load_file(dict_name, path) +/* const const char *dict_name; +/* const const char *path; +/* +/* void dict_load_fp(dict_name, fp) +/* const const char *dict_name; +/* FILE *fp; +/* DESCRIPTION +/* This module maintains a collection of name-value dictionaries. +/* Each dictionary has its own name and has its own methods to read +/* or update members. Examples of dictionaries that can be accessed +/* in this manner are the global UNIX-style process environment, +/* hash tables, NIS maps, DBM files, and so on. Dictionary values +/* are not limited to strings but can be arbitrary objects as long +/* as they can be represented by character pointers. +/* FEATURES +/* .fi +/* .ad +/* Notable features of this module are: +/* .IP "macro expansion (string-valued dictionaries only)" +/* Macros of the form $\fIname\fR can be expanded to the current +/* value of \fIname\fR. The forms $(\fIname\fR) and ${\fIname\fR} are +/* also supported. +/* .IP "unknown names" +/* References to unknown dictionary or dictionary member names either +/* default to an empty dictionary or null pointer value, or cause a +/* run-time error. The behavior is controlled by the global +/* \fIdict_unknown_allowed\fR boolean variable. +/* .PP +/* dict_register() adds a new dictionary, including access methods, +/* to the list of known dictionaries, or increments the reference +/* count for an existing (name, dictionary) pair. Otherwise, it is +/* an error to pass an existing name (this would cause a memory leak). +/* +/* dict_handle() returns the generic dictionary handle of the +/* named dictionary, or a null pointer when the named dictionary +/* is not found. +/* +/* dict_unregister() decrements the reference count of the named +/* dictionary. When the reference count reaches zero, dict_unregister() +/* breaks the (name, dictionary) association and executes the +/* dictionary's optional \fIremove\fR method. +/* +/* dict_update() updates the value of the named dictionary member. +/* The dictionary member and the named dictionary are instantiated +/* on the fly. During the update, a file-based dictionary is locked +/* for exclusive access. With file-based dictionaries, duplicate +/* of duplicate entries depends on dictionary flag settings: +/* .IP DICT_FLAG_DUP_WARN +/* Log a warning and ignore the duplicate. +/* .IP DICT_FLAG_DUP_IGNORE +/* Silently ignore the duplicate. +/* .PP +/* The default is to terminate the program with a fatal error. +/* +/* dict_lookup() returns the value of the named member (i.e. without +/* expanding macros in the member value). The \fIdict_name\fR argument +/* specifies the dictionary to search. The dictionary is locked for +/* shared access, when it is file based. The result is a null pointer +/* when no value is found, otherwise the result is owned by the +/* underlying dictionary method. Make a copy if the result is to be +/* modified, or if the result is to survive multiple dict_lookup() calls. +/* +/* dict_eval() expands macro references in the specified string. +/* The result is owned by the dictionary manager. Make a copy if the +/* result is to survive multiple dict_eval() calls. When the +/* \fIrecursive\fR argument is non-zero, macros references are +/* expanded recursively. +/* +/* dict_load_file() reads name-value entries from the named file. +/* Lines that begin with whitespace are concatenated to the preceding +/* line (the newline is deleted). +/* Each entry is stored in the dictionary named by \fIdict_name\fR. +/* +/* dict_load_fp() reads name-value entries from an open stream. +/* It has the same semantics as the dict_load_file() function. +/* SEE ALSO +/* htable(3) +/* BUGS +/* DIAGNOSTICS +/* Fatal errors: out of memory, reference to unknown name, +/* malformed macro name. +/* +/* The lookup routines may set the \fIdict_errno\fR variable when +/* they were unable to find the requested result. The variable +/* is reset before each lookup operation. \fIdict_errno\fR can +/* have the following values: +/* .IP DICT_ERR_RETRY +/* The dictionary was temporarily unavailable. This can happen +/* with network-based services. +/* 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 libraries. */ + +#include "sys_defs.h" +#include +#include +#include + +/* Utility library. */ + +#include "msg.h" +#include "htable.h" +#include "mymalloc.h" +#include "vstream.h" +#include "vstring.h" +#include "readline.h" +#include "myflock.h" +#include "mac_parse.h" +#include "dict.h" +#include "dict_ht.h" + + /* + * By default, use a sane default for an unknown name. + */ +int dict_unknown_allowed = 1; +int dict_errno = 0; + +static HTABLE *dict_table; + + /* + * Each (name, dictionary) instance has a reference count. The count is part + * of the name, not the dictionary. The same dictionary may be registered + * under multiple names. The structure below keeps track of instances and + * reference counts. + */ +typedef struct { + DICT *dict; + int refcount; +} DICT_NODE; + +#define dict_node(dict) \ + (dict_table ? (DICT_NODE *) htable_find(dict_table, dict) : 0) + +#define STR(x) vstring_str(x) + +/* dict_register - make association with dictionary */ + +void dict_register(const char *dict_name, DICT *dict_info) +{ + char *myname = "dict_register"; + DICT_NODE *node; + + if (dict_table == 0) + dict_table = htable_create(0); + if ((node = dict_node(dict_name)) == 0) { + node = (DICT_NODE *) mymalloc(sizeof(*node)); + node->dict = dict_info; + node->refcount = 0; + htable_enter(dict_table, dict_name, (char *) node); + } else if (dict_info != node->dict) + msg_fatal("%s: dictionary name exists: %s", myname, dict_name); + node->refcount++; + if (msg_verbose > 1) + msg_info("%s: %s %d", myname, dict_name, node->refcount); +} + +/* dict_handle - locate generic dictionary handle */ + +DICT *dict_handle(const char *dict_name) +{ + DICT_NODE *node; + + return ((node = dict_node(dict_name)) ? node->dict : 0); +} + +/* dict_node_free - dict_unregister() callback */ + +static void dict_node_free(char *ptr) +{ + DICT_NODE *node = (DICT_NODE *) ptr; + DICT *dict = node->dict; + + if (dict->close) + dict->close(dict); + myfree((char *) node); +} + +/* dict_unregister - break association with named dictionary */ + +void dict_unregister(const char *dict_name) +{ + char *myname = "dict_unregister"; + DICT_NODE *node; + + if ((node = dict_node(dict_name)) == 0) + msg_panic("non-existing dictionary: %s", dict_name); + if (msg_verbose > 1) + msg_info("%s: %s %d", myname, dict_name, node->refcount); + if (--(node->refcount) == 0) + htable_delete(dict_table, dict_name, dict_node_free); +} + +/* dict_update - replace or add dictionary entry */ + +void dict_update(const char *dict_name, const char *member, const char *value) +{ + char *myname = "dict_update"; + DICT_NODE *node; + DICT *dict; + + if ((node = dict_node(dict_name)) == 0) { + if (dict_unknown_allowed == 0) + msg_fatal("%s: unknown dictionary: %s", myname, dict_name); + dict = dict_ht_open(htable_create(0), myfree); + dict_register(dict_name, dict); + } else + dict = node->dict; + if (msg_verbose > 1) + msg_info("%s: %s = %s", myname, member, value); + if (dict->fd >= 0 && myflock(dict->fd, MYFLOCK_EXCLUSIVE) < 0) + msg_fatal("%s: lock dictionary %s: %m", myname, dict_name); + dict->update(dict, member, value); + if (dict->fd >= 0 && myflock(dict->fd, MYFLOCK_NONE) < 0) + msg_fatal("%s: unlock dictionary %s: %m", myname, dict_name); +} + +/* dict_lookup - look up dictionary entry */ + +const char *dict_lookup(const char *dict_name, const char *member) +{ + char *myname = "dict_lookup"; + DICT_NODE *node; + DICT *dict; + const char *ret = 0; + + dict_errno = 0; + + if ((node = dict_node(dict_name)) == 0) { + if (dict_unknown_allowed == 0) + msg_fatal("%s: unknown dictionary: %s", myname, dict_name); + } else { + dict = node->dict; + if (dict->fd >= 0 && myflock(dict->fd, MYFLOCK_SHARED) < 0) + msg_fatal("%s: lock dictionary %s: %m", myname, dict_name); + ret = dict->lookup(dict, member); + if (dict->fd >= 0 && myflock(dict->fd, MYFLOCK_NONE) < 0) + msg_fatal("%s: unlock dictionary %s: %m", myname, dict_name); + if (ret == 0 && dict_unknown_allowed == 0) + msg_fatal("dictionary %s: unknown member: %s", dict_name, member); + } + if (msg_verbose > 1) + msg_info("%s: %s = %s", myname, member, ret ? ret : "(notfound)"); + return (ret); +} + +/* dict_load_file - read entries from text file */ + +void dict_load_file(const char *dict_name, const char *path) +{ + VSTREAM *fp; + + if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0) + msg_fatal("open %s: %m", path); + dict_load_fp(dict_name, fp); + if (vstream_ferror(fp) || vstream_fclose(fp)) + msg_fatal("read %s: %m", path); +} + +/* dict_load_fp - read entries from open stream */ + +void dict_load_fp(const char *dict_name, VSTREAM *fp) +{ + VSTRING *buf; + char *start; + char *member; + char *val; + char *cp; + char *ep; + int lineno; + + /* + * Ugly macros to make complex expressions less unreadable. + */ +#define SKIP(start, var, cond) \ + for (var = start; *var && (cond); var++); + +#define TRIM(s) { \ + char *p; \ + for (p = (s) + strlen(s); p > (s) && ISSPACE(p[-1]); p--); \ + *p = 0; \ + } + + buf = vstring_alloc(100); + lineno = 0; + + while (readline(buf, fp, &lineno)) { + start = STR(buf); + SKIP(start, member, ISSPACE(*member)); /* find member begin */ + if (*member == 0 || *member == '#') + continue; /* comment or blank line */ + SKIP(member, ep, !ISSPACE(*ep) && *ep != '='); /* find member end */ + SKIP(ep, cp, ISSPACE(*cp)); /* skip blanks before '=' */ + if (*cp && *cp != '=') /* need '=' or end of string */ + msg_fatal("%s, line %d: whitespace in attribute name: \"%s\"", + VSTREAM_PATH(fp), lineno, member); + if (*cp) + cp++; /* skip over '=' */ + *ep = 0; /* terminate member name */ + SKIP(cp, val, ISSPACE(*val)); /* skip leading blanks */ + TRIM(val); /* trim trailing blanks */ + dict_update(dict_name, member, val); + } + vstring_free(buf); +} + + /* + * Helper for macro expansion callback. + */ +struct dict_eval_context { + const char *dict_name; /* where to look */ + VSTRING *buf; /* result buffer */ + int recursive; /* recursive or not */ +}; + +/* dict_eval_action - macro parser call-back routine */ + +static void dict_eval_action(int type, VSTRING *buf, char *ptr) +{ + struct dict_eval_context *ctxt = (struct dict_eval_context *) ptr; + char *myname = "dict_eval_action"; + const char *pp; + + if (msg_verbose) + msg_info("%s: type %s buf %s context %s \"%s\" %s", + myname, type == MAC_PARSE_VARNAME ? "variable" : "literal", + STR(buf), ctxt->dict_name, STR(ctxt->buf), + ctxt->recursive ? "recursive" : "non-recursive"); + + /* + * In order to support recursion, we must save the dict_lookup() result. + * We use the input buffer since it will not be needed anymore. + */ + if (type == MAC_PARSE_VARNAME) { + if ((pp = dict_lookup(ctxt->dict_name, STR(buf))) == 0) { + if (dict_errno) /* XXX how would one recover? */ + msg_fatal("dictionary %s: lookup %s: temporary error", + ctxt->dict_name, STR(buf)); + } else if (ctxt->recursive) { + vstring_strcpy(buf, pp); /* XXX clobber input */ + dict_eval(ctxt->dict_name, STR(buf), ctxt->recursive); + } else { + vstring_strcat(ctxt->buf, pp); + } + } else { + vstring_strcat(ctxt->buf, STR(buf)); + } +} + +/* dict_eval - expand embedded dictionary references */ + +const char *dict_eval(const char *dict_name, const char *value, int recursive) +{ + static VSTRING *buf; + static struct dict_eval_context ctxt; + static int loop = 0; + + /* + * Sanity check. + */ + if (loop > 100) + msg_fatal("unreasonable macro nesting: \"%s\"", value); + + /* + * Initialize. + */ + if (buf == 0) + buf = vstring_alloc(10); + if (loop++ == 0) + VSTRING_RESET(buf); + ctxt.buf = buf; + ctxt.recursive = recursive; + ctxt.dict_name = dict_name; + + /* + * Expand macros, possibly recursively. + */ + if (msg_verbose > 1) + msg_info("dict_eval[%d] %s", loop, value); + + mac_parse(value, dict_eval_action, (char *) &ctxt); + + if (msg_verbose > 1) + msg_info("dict_eval[%d] result %s", loop, STR(buf)); + + /* + * Cleanup. + */ + loop--; + VSTRING_TERMINATE(buf); + + return (STR(buf)); +} diff --git a/postfix/util/dict.h b/postfix/util/dict.h new file mode 100644 index 000000000..a66962c33 --- /dev/null +++ b/postfix/util/dict.h @@ -0,0 +1,79 @@ +#ifndef _DICT_H_INCLUDED_ +#define _DICT_H_INCLUDED_ + +/*++ +/* NAME +/* dict 3h +/* SUMMARY +/* dictionary manager +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include + + /* + * Generic dictionary interface - in reality, a dictionary extends this + * structure with private members to maintain internal state. + */ +typedef struct DICT { + int flags; /* see below */ + const char *(*lookup) (struct DICT *, const char *); + void (*update) (struct DICT *, const char *, const char *); + void (*close) (struct DICT *); + int fd; /* for dict_update() lock */ +} DICT; + +#define DICT_FLAG_DUP_WARN (1<<0) /* if file, warn about dups */ +#define DICT_FLAG_DUP_IGNORE (1<<1) /* if file, ignore dups */ + +extern int dict_unknown_allowed; +extern int dict_errno; + +#define DICT_ERR_RETRY 1 /* soft error */ + + /* + * High-level interface, with logical dictionary names and with implied + * locking. + */ +extern void dict_register(const char *, DICT *); +extern DICT *dict_handle(const char *); +extern void dict_unregister(const char *); +extern void dict_update(const char *, const char *, const char *); +extern const char *dict_lookup(const char *, const char *); +extern void dict_load_file(const char *, const char *); +extern void dict_load_fp(const char *, VSTREAM *); +extern const char *dict_eval(const char *, const char *, int); + + /* + * Low-level interface, with physical dictionary handles and no implied + * locking. + */ +extern DICT *dict_open(const char *, int); +extern DICT *dict_open3(const char *, const char *, int); + +#define dict_get(dp, key) (dp)->lookup((dp), (key)) +#define dict_put(dp, key, val) (dp)->update((dp), (key), (val)) +#define dict_close(dp) (dp)->close(dp) + +/* 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/dict_db.c b/postfix/util/dict_db.c new file mode 100644 index 000000000..1d38b09fa --- /dev/null +++ b/postfix/util/dict_db.c @@ -0,0 +1,229 @@ +/*++ +/* NAME +/* dict_db 3 +/* SUMMARY +/* dictionary manager interface to DB files +/* SYNOPSIS +/* #include +/* +/* DICT *dict_hash_open(path, flags) +/* const char *path; +/* int flags; +/* +/* DICT *dict_btree_open(path, flags) +/* const char *path; +/* int flags; +/* DESCRIPTION +/* dict_XXX_open() opens the specified DB database. The result is +/* a pointer to a structure that can be used to access the dictionary +/* using the generic methods documented in dict_open(3). +/* +/* Arguments: +/* .IP path +/* The database pathname, not including the ".db" suffix. +/* .IP flags +/* flags passed to dbopen(). +/* SEE ALSO +/* dict(3) generic dictionary manager +/* DIAGNOSTICS +/* Fatal errors: cannot open file, write error, out of memory. +/* 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 +/*--*/ + +#include "sys_defs.h" + +#ifdef HAS_DB + +/* System library. */ + +#include +#ifdef PATH_DB_H +#include PATH_DB_H +#else +#include +#endif +#include + +/* Utility library. */ + +#include "msg.h" +#include "mymalloc.h" +#include "vstring.h" +#include "stringops.h" +#include "iostuff.h" +#include "dict.h" +#include "dict_db.h" + +/* Application-specific. */ + +typedef struct { + DICT dict; /* generic members */ + int flags; /* see below */ + DB *db; /* open db file */ + char *path; /* pathname */ +} DICT_DB; + +#define DICT_DB_TRY0NULL (1<<0) +#define DICT_DB_TRY1NULL (1<<1) + +/* dict_db_lookup - find database entry */ + +static const char *dict_db_lookup(DICT *dict, const char *name) +{ + DICT_DB *dict_db = (DICT_DB *) dict; + DB *db = dict_db->db; + DBT db_key; + DBT db_value; + int status; + static VSTRING *buf; + + /* + * See if this DB file was written with one null byte appended to key and + * value. + */ + if (dict_db->flags & DICT_DB_TRY1NULL) { + db_key.data = (void *) name; + db_key.size = strlen(name) + 1; + if ((status = db->get(db, &db_key, &db_value, 0)) < 0) + msg_fatal("error reading %s: %m", dict_db->path); + if (status == 0) { + dict_db->flags &= ~DICT_DB_TRY0NULL; + return (db_value.data); + } + } + + /* + * See if this DB file was written with no null byte appended to key and + * value. + */ + if (dict_db->flags & DICT_DB_TRY0NULL) { + db_key.data = (void *) name; + db_key.size = strlen(name); + if ((status = db->get(db, &db_key, &db_value, 0)) < 0) + msg_fatal("error reading %s: %m", dict_db->path); + if (status == 0) { + if (buf == 0) + buf = vstring_alloc(10); + vstring_strncpy(buf, db_value.data, db_value.size); + dict_db->flags &= ~DICT_DB_TRY1NULL; + return (vstring_str(buf)); + } + } + return (0); +} + +/* dict_db_update - add or update database entry */ + +static void dict_db_update(DICT *dict, const char *name, const char *value) +{ + DICT_DB *dict_db = (DICT_DB *) dict; + DB *db = dict_db->db; + DBT db_key; + DBT db_value; + int status; + + db_key.data = (void *) name; + db_value.data = (void *) value; + db_key.size = strlen(name); + db_value.size = strlen(value); + + /* + * If undecided about appending a null byte to key and value, choose a + * default depending on the platform. + */ + if ((dict_db->flags & DICT_DB_TRY1NULL) + && (dict_db->flags & DICT_DB_TRY0NULL)) { +#ifdef DB_NO_TRAILING_NULL + dict_db->flags = DICT_DB_TRY0NULL; +#else + dict_db->flags = DICT_DB_TRY1NULL; +#endif + } + + /* + * Optionally append a null byte to key and value. + */ + if (dict_db->flags & DICT_DB_TRY1NULL) { + db_key.size++; + db_value.size++; + } + + /* + * Do the update. + */ + if ((status = db->put(db, &db_key, &db_value, R_NOOVERWRITE)) < 0) + msg_fatal("error writing %s: %m", dict_db->path); + if (status) { + if (dict->flags & DICT_FLAG_DUP_IGNORE) + /* void */ ; + else if (dict->flags & DICT_FLAG_DUP_WARN) + msg_warn("%s: duplicate entry: \"%s\"", dict_db->path, name); + else + msg_fatal("%s: duplicate entry: \"%s\"", dict_db->path, name); + } +} + +/* dict_db_close - close data base */ + +static void dict_db_close(DICT *dict) +{ + DICT_DB *dict_db = (DICT_DB *) dict; + + if (dict_db->db->close(dict_db->db) < 0) + msg_fatal("close database %s: %m", dict_db->path); + myfree(dict_db->path); + myfree((char *) dict_db); +} + +/* dict_db_open - open data base */ + +static DICT *dict_db_open(const char *path, int flags, int type) +{ + 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) + msg_fatal("open database %s: %m", db_path); + + dict_db = (DICT_DB *) mymalloc(sizeof(*dict_db)); + dict_db->dict.lookup = dict_db_lookup; + dict_db->dict.update = dict_db_update; + dict_db->dict.close = dict_db_close; + dict_db->dict.fd = db->fd(db); + close_on_exec(dict_db->dict.fd, CLOSE_ON_EXEC); + dict_db->flags = DICT_DB_TRY1NULL | DICT_DB_TRY0NULL; + dict_db->db = db; + dict_db->path = db_path; + return (&dict_db->dict); +} + +/* dict_hash_open - create association with data base */ + +DICT *dict_hash_open(const char *path, int flags) +{ + return (dict_db_open(path, flags, DB_HASH)); +} + +/* 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)); +} + +#endif diff --git a/postfix/util/dict_db.h b/postfix/util/dict_db.h new file mode 100644 index 000000000..d03e282f3 --- /dev/null +++ b/postfix/util/dict_db.h @@ -0,0 +1,36 @@ +#ifndef _DICT_DB_H_INCLUDED_ +#define _DICT_DB_H_INCLUDED_ + +/*++ +/* NAME +/* dict_db 3h +/* SUMMARY +/* dictionary manager interface to DB files +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern DICT *dict_hash_open(const char *, int); +extern DICT *dict_btree_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/dict_dbm.c b/postfix/util/dict_dbm.c new file mode 100644 index 000000000..af4f355a3 --- /dev/null +++ b/postfix/util/dict_dbm.c @@ -0,0 +1,192 @@ +/*++ +/* NAME +/* dict_dbm 3 +/* SUMMARY +/* dictionary manager interface to DBM files +/* SYNOPSIS +/* #include +/* +/* DICT *dict_dbm_open(path, flags) +/* const char *name; +/* const char *path; +/* int flags; +/* DESCRIPTION +/* dict_dbm_open() opens the named DBM database and makes it available +/* via the generic interface described in dict_open(3). +/* DIAGNOSTICS +/* Fatal errors: cannot open file, file write error, out of memory. +/* SEE ALSO +/* dict(3) generic dictionary manager +/* ndbm(3) data base subroutines +/* 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 +/*--*/ + +#include "sys_defs.h" + +#ifdef HAS_DBM + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include "msg.h" +#include "mymalloc.h" +#include "htable.h" +#include "iostuff.h" +#include "vstring.h" +#include "dict.h" +#include "dict_dbm.h" + +/* Application-specific. */ + +typedef struct { + DICT dict; /* generic members */ + int flags; /* see below */ + DBM *dbm; /* open database */ + char *path; /* pathname */ +} DICT_DBM; + +#define DICT_DBM_TRY0NULL (1<<0) +#define DICT_DBM_TRY1NULL (1<<1) + +/* dict_dbm_lookup - find database entry */ + +static const char *dict_dbm_lookup(DICT *dict, const char *name) +{ + DICT_DBM *dict_dbm = (DICT_DBM *) dict; + datum dbm_key; + datum dbm_value; + static VSTRING *buf; + + /* + * See if this DBM file was written with one null byte appended to key + * and value. + */ + if (dict_dbm->flags & DICT_DBM_TRY1NULL) { + dbm_key.dptr = (void *) name; + dbm_key.dsize = strlen(name) + 1; + dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key); + if (dbm_value.dptr != 0) { + dict_dbm->flags &= ~DICT_DBM_TRY0NULL; + return (dbm_value.dptr); + } + } + + /* + * See if this DBM file was written with no null byte appended to key and + * value. + */ + if (dict_dbm->flags & DICT_DBM_TRY0NULL) { + dbm_key.dptr = (void *) name; + dbm_key.dsize = strlen(name); + dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key); + if (dbm_value.dptr != 0) { + if (buf == 0) + buf = vstring_alloc(10); + vstring_strncpy(buf, dbm_value.dptr, dbm_value.dsize); + dict_dbm->flags &= ~DICT_DBM_TRY1NULL; + return (vstring_str(buf)); + } + } + return (0); +} + +/* dict_dbm_update - add or update database entry */ + +static void dict_dbm_update(DICT *dict, const char *name, const char *value) +{ + DICT_DBM *dict_dbm = (DICT_DBM *) dict; + datum dbm_key; + datum dbm_value; + int status; + + dbm_key.dptr = (void *) name; + dbm_value.dptr = (void *) value; + dbm_key.dsize = strlen(name); + dbm_value.dsize = strlen(value); + + /* + * If undecided about appending a null byte to key and value, choose a + * default depending on the platform. + */ + if ((dict_dbm->flags & DICT_DBM_TRY1NULL) + && (dict_dbm->flags & DICT_DBM_TRY0NULL)) { +#ifdef DBM_NO_TRAILING_NULL + dict_dbm->flags = DICT_DBM_TRY0NULL; +#else + dict_dbm->flags = DICT_DBM_TRY1NULL; +#endif + } + + /* + * Optionally append a null byte to key and value. + */ + if (dict_dbm->flags & DICT_DBM_TRY1NULL) { + dbm_key.dsize++; + dbm_value.dsize++; + } + + /* + * Do the update. + */ + if ((status = dbm_store(dict_dbm->dbm, dbm_key, dbm_value, DBM_INSERT)) < 0) + msg_fatal("error writing DBM database %s: %m", dict_dbm->path); + if (status) { + if (dict->flags & DICT_FLAG_DUP_IGNORE) + /* void */ ; + else if (dict->flags & DICT_FLAG_DUP_WARN) + msg_warn("%s: duplicate entry: \"%s\"", dict_dbm->path, name); + else + msg_fatal("%s: duplicate entry: \"%s\"", dict_dbm->path, name); + } +} + +/* dict_dbm_close - disassociate from data base */ + +static void dict_dbm_close(DICT *dict) +{ + DICT_DBM *dict_dbm = (DICT_DBM *) dict; + + dbm_close(dict_dbm->dbm); + myfree(dict_dbm->path); + myfree((char *) dict_dbm); +} + +/* dict_dbm_open - open DBM data base */ + +DICT *dict_dbm_open(const char *path, int flags) +{ + DICT_DBM *dict_dbm; + DBM *dbm; + + /* + * XXX SunOS 5.x has no const in dbm_open() prototype. + */ + if ((dbm = dbm_open((char *) path, flags, 0644)) == 0) + msg_fatal("open database %s.{dir,pag}: %m", path); + + dict_dbm = (DICT_DBM *) mymalloc(sizeof(*dict_dbm)); + dict_dbm->dict.lookup = dict_dbm_lookup; + dict_dbm->dict.update = dict_dbm_update; + dict_dbm->dict.close = dict_dbm_close; + dict_dbm->dict.fd = dbm_dirfno(dbm); + close_on_exec(dict_dbm->dict.fd, CLOSE_ON_EXEC); + dict_dbm->flags = DICT_DBM_TRY0NULL | DICT_DBM_TRY1NULL; + dict_dbm->dbm = dbm; + dict_dbm->path = mystrdup(path); + + return (&dict_dbm->dict); +} + +#endif diff --git a/postfix/util/dict_dbm.h b/postfix/util/dict_dbm.h new file mode 100644 index 000000000..da8f5c031 --- /dev/null +++ b/postfix/util/dict_dbm.h @@ -0,0 +1,35 @@ +#ifndef _DICT_DBN_H_INCLUDED_ +#define _DICT_DBN_H_INCLUDED_ + +/*++ +/* NAME +/* dict_dbm 3h +/* SUMMARY +/* dictionary manager interface to DBM files +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern DICT *dict_dbm_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/dict_env.c b/postfix/util/dict_env.c new file mode 100644 index 000000000..cbc8249e7 --- /dev/null +++ b/postfix/util/dict_env.c @@ -0,0 +1,81 @@ +/*++ +/* NAME +/* dict_env 3 +/* SUMMARY +/* dictionary manager interface to environment variables +/* SYNOPSIS +/* #include +/* +/* DICT *dict_env_open(name, flags) +/* const char *name; +/* int flags; +/* DESCRIPTION +/* dict_env_open() opens the environment variable array and +/* makes it accessible via the generic operations documented +/* in dict_open(3). The \fIname\fR and \fIflags\fR arguments +/* are ignored. +/* SEE ALSO +/* dict(3) generic dictionary manager +/* safe_getenv(3) safe getenv() interface +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include "sys_defs.h" +#include /* sprintf() prototype */ +#include +#include +#include + +/* Utility library. */ + +#include "mymalloc.h" +#include "msg.h" +#include "safe.h" +#include "dict.h" +#include "dict_env.h" + +/* dict_env_update - update environment array */ + +static void dict_env_update(DICT *unused_dict, const char *name, const char *value) +{ + if (setenv(name, value, 1)) + msg_fatal("setenv: %m"); +} + +/* dict_env_lookup - access environment array */ + +static const char *dict_env_lookup(DICT *unused_dict, const char *name) +{ + return (safe_getenv(name)); +} + +/* dict_env_close - close environment dictionary */ + +static void dict_env_close(DICT *dict) +{ + myfree((char *) dict); +} + +/* dict_env_open - make association with environment array */ + +DICT *dict_env_open(const char *unused_name, int unused_flags) +{ + DICT *dict; + + dict = (DICT *) mymalloc(sizeof(*dict)); + dict->lookup = dict_env_lookup; + dict->update = dict_env_update; + dict->close = dict_env_close; + dict->fd = -1; + return (dict); +} diff --git a/postfix/util/dict_env.h b/postfix/util/dict_env.h new file mode 100644 index 000000000..672c64a58 --- /dev/null +++ b/postfix/util/dict_env.h @@ -0,0 +1,35 @@ +#ifndef _DICT_ENV_H_INCLUDED_ +#define _DICT_ENV_H_INCLUDED_ + +/*++ +/* NAME +/* dict_env 3h +/* SUMMARY +/* dictionary manager interface to environment variables +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern DICT *dict_env_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/dict_ht.c b/postfix/util/dict_ht.c new file mode 100644 index 000000000..f9baad406 --- /dev/null +++ b/postfix/util/dict_ht.c @@ -0,0 +1,100 @@ +/*++ +/* NAME +/* dict_ht 3 +/* SUMMARY +/* dictionary manager interface to hash tables +/* SYNOPSIS +/* #include +/* +/* DICT *dict_ht_open(table, remove) +/* HTABLE *table; +/* void (*remove)(char *value) +/* DESCRIPTION +/* dict_ht_open() makes specified hash table accessible via the +/* generic dictionary operations documented in dict_open(3). +/* \fIremove\fR specifies an optional callback function +/* that is called by the hash table manager when the hash table is +/* removed from the dictionary manager's care. The hash table is not +/* destroyed when \fIremove\fR is a null pointer. +/* 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 +/*--*/ + +/* System library. */ + +#include "sys_defs.h" + +/* Utility library. */ + +#include "mymalloc.h" +#include "htable.h" +#include "dict.h" +#include "dict_ht.h" + +/* Application-specific. */ + +typedef struct { + DICT dict; /* generic members */ + HTABLE *table; /* hash table */ + void (*remove) (char *); /* callback */ +} DICT_HT; + +/* dict_ht_lookup - find hash-table entry */ + +static const char *dict_ht_lookup(DICT *dict, const char *name) +{ + DICT_HT *dict_ht = (DICT_HT *) dict; + + return (htable_find(dict_ht->table, name)); +} + +/* dict_ht_update - add or update hash-table entry */ + +static void dict_ht_update(DICT *dict, const char *name, const char *value) +{ + DICT_HT *dict_ht = (DICT_HT *) dict; + HTABLE_INFO *ht; + + if ((ht = htable_locate(dict_ht->table, name)) != 0) { + myfree(ht->value); + } else { + ht = htable_enter(dict_ht->table, name, (char *) 0); + } + ht->value = mystrdup(value); +} + +/* dict_ht_close - disassociate from hash table */ + +static void dict_ht_close(DICT *dict) +{ + DICT_HT *dict_ht = (DICT_HT *) dict; + + if (dict_ht->remove) + htable_free(dict_ht->table, dict_ht->remove); + myfree((char *) dict); +} + +/* dict_ht_open - create association with hash table */ + +DICT *dict_ht_open(HTABLE *table, void (*remove) (char *)) +{ + DICT_HT *dict_ht; + + dict_ht = (DICT_HT *) mymalloc(sizeof(*dict_ht)); + dict_ht->dict.lookup = dict_ht_lookup; + dict_ht->dict.update = dict_ht_update; + dict_ht->dict.close = dict_ht_close; + dict_ht->dict.fd = -1; + dict_ht->table = table; + dict_ht->remove = remove; + return (&dict_ht->dict); +} diff --git a/postfix/util/dict_ht.h b/postfix/util/dict_ht.h new file mode 100644 index 000000000..4f408bc97 --- /dev/null +++ b/postfix/util/dict_ht.h @@ -0,0 +1,36 @@ +#ifndef _DICT_HT_H_INCLUDED_ +#define _DICT_HT_H_INCLUDED_ + +/*++ +/* NAME +/* dict_ht 3h +/* SUMMARY +/* dictionary manager interface to hash tables +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include + + /* + * External interface. + */ +extern DICT *dict_ht_open(HTABLE *, void (*) (char *)); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/util/dict_ldap.c b/postfix/util/dict_ldap.c new file mode 100644 index 000000000..5d674f3c1 --- /dev/null +++ b/postfix/util/dict_ldap.c @@ -0,0 +1,11 @@ + /* + * The LDAP software is not bundled with IBM's public release. It will be + * made available as contributed software from http://www.postfix.org/ + */ +#include "sys_defs.h" +#include "dict.h" +#include "dict_ldap.h" + +#ifdef HAS_LDAP +#error "This requires contributed software from http://www.postfix.org/" +#endif diff --git a/postfix/util/dict_ldap.h b/postfix/util/dict_ldap.h new file mode 100644 index 000000000..aaced3e7e --- /dev/null +++ b/postfix/util/dict_ldap.h @@ -0,0 +1,4 @@ + /* + * The LDAP software is not bundled with IBM's public release. It will be + * made available as contributed software from http://www.postfix.org/ + */ diff --git a/postfix/util/dict_ni.c b/postfix/util/dict_ni.c new file mode 100644 index 000000000..06331d491 --- /dev/null +++ b/postfix/util/dict_ni.c @@ -0,0 +1,9 @@ + /* + * The NetInfo software is not bundled with IBM's public release. It will be + * made available as contributed software from http://www.postfix.org/ + */ +#include "sys_defs.h" + +#ifdef HAS_NETINFO +#error "This requires contributed software from http://www.postfix.org/" +#endif diff --git a/postfix/util/dict_ni.h b/postfix/util/dict_ni.h new file mode 100644 index 000000000..2ce9c2db2 --- /dev/null +++ b/postfix/util/dict_ni.h @@ -0,0 +1,4 @@ + /* + * The NetInfo software is not bundled with IBM's public release. It will be + * made available as contributed software from http://www.postfix.org/ + */ diff --git a/postfix/util/dict_nis.c b/postfix/util/dict_nis.c new file mode 100644 index 000000000..0441183d9 --- /dev/null +++ b/postfix/util/dict_nis.c @@ -0,0 +1,237 @@ +/*++ +/* NAME +/* dict_nis 3 +/* SUMMARY +/* dictionary manager interface to NIS maps +/* SYNOPSIS +/* #include +/* +/* DICT *dict_nis_open(map, dummy) +/* const char *map; +/* int dummy; +/* DESCRIPTION +/* dict_nis_open() makes the specified NIS map accessible via +/* the generic dictionary operations described in dict_open(3). +/* The \fIdummy\fR argument is not used. +/* SEE ALSO +/* dict(3) generic dictionary manager +/* DIAGNOSTICS +/* Fatal errors: out of memory, attempt to update NIS 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +#ifdef HAS_NIS + +#include +#ifndef YPERR_BUSY +#define YPERR_BUSY 16 +#endif +#ifndef YPERR_ACCESS +#define YPERR_ACCESS 15 +#endif + +#endif + +/* Utility library. */ + +#include "msg.h" +#include "mymalloc.h" +#include "vstring.h" +#include "dict.h" +#include "dict_nis.h" + +#ifdef HAS_NIS + +/* Application-specific. */ + +typedef struct { + DICT dict; /* generic members */ + char *map; /* NIS map name */ + int flags; /* see below */ +} DICT_NIS; + +#define DICT_NIS_TRY0NULL (1<<0) +#define DICT_NIS_TRY1NULL (1<<1) + + /* + * Class variables, so that multiple maps can share this info. + */ +static char dict_nis_disabled[1]; +static char *dict_nis_domain; + +/* dict_nis_init - NIS binding */ + +static void dict_nis_init(void) +{ + char *myname = "dict_nis_init"; + + if (yp_get_default_domain(&dict_nis_domain) != 0 + || dict_nis_domain == 0 || *dict_nis_domain == 0 + || strcasecmp(dict_nis_domain, "(none)") == 0) { + dict_nis_domain = dict_nis_disabled; + msg_warn("%s: NIS domain name not set - NIS lookups disabled", myname); + } + if (msg_verbose) + msg_info("%s: NIS domain %s", myname, dict_nis_domain); +} + +/* dict_nis_strerror - map error number to string */ + +static char *dict_nis_strerror(int err) +{ + + /* + * Grr. There should be a standard function for this. + */ + switch (err) { + case YPERR_BADARGS: + return ("args to function are bad"); + case YPERR_RPC: + return ("RPC failure - domain has been unbound"); + case YPERR_DOMAIN: + return ("can't bind to server on this domain"); + case YPERR_MAP: + return ("no such map in server's domain"); + case YPERR_KEY: + return ("no such key in map"); + case YPERR_YPERR: + return ("internal yp server or client error"); + case YPERR_RESRC: + return ("resource allocation failure"); + case YPERR_NOMORE: + return ("no more records in map database"); + case YPERR_PMAP: + return ("can't communicate with portmapper"); + case YPERR_YPBIND: + return ("can't communicate with ypbind"); + case YPERR_YPSERV: + return ("can't communicate with ypserv"); + case YPERR_NODOM: + return ("local domain name not set"); + case YPERR_BADDB: + return ("yp database is bad"); + case YPERR_VERS: + return ("yp version mismatch"); + case YPERR_ACCESS: + return ("access violation"); + case YPERR_BUSY: + return ("database busy"); + default: + return ("unknown NIS lookup error"); + } +} + +/* dict_nis_lookup - find table entry */ + +static const char *dict_nis_lookup(DICT *dict, const char *key) +{ + DICT_NIS *dict_nis = (DICT_NIS *) dict; + static char *result; + int result_len; + int err; + static VSTRING *buf; + + dict_errno = 0; + if (dict_nis_domain == dict_nis_disabled) + return (0); + + /* + * See if this NIS map was written with one null byte appended to key and + * value. + */ + if (dict_nis->flags & DICT_NIS_TRY1NULL) { + err = yp_match(dict_nis_domain, dict_nis->map, + (void *) key, strlen(key) + 1, + &result, &result_len); + if (err == 0) { + dict_nis->flags &= ~DICT_NIS_TRY0NULL; + return (result); + } + } + + /* + * See if this NIS map was written with no null byte appended to key and + * value. This should never be the case, but better play safe. + */ + if (dict_nis->flags & DICT_NIS_TRY0NULL) { + err = yp_match(dict_nis_domain, dict_nis->map, + (void *) key, strlen(key), + &result, &result_len); + if (err == 0) { + dict_nis->flags &= ~DICT_NIS_TRY1NULL; + if (buf == 0) + buf = vstring_alloc(10); + vstring_strncpy(buf, result, result_len); + return (vstring_str(buf)); + } + } + + /* + * When the NIS lookup fails for reasons other than "key not found", keep + * logging warnings, and hope that someone will eventually notice the + * problem and fix it. + */ + if (err != YPERR_KEY) { + msg_warn("lookup %s, NIS domain %s, map %s: %s", + key, dict_nis_domain, dict_nis->map, + dict_nis_strerror(err)); + dict_errno = DICT_ERR_RETRY; + } + return (0); +} + +/* dict_nis_update - add or update table entry */ + +static void dict_nis_update(DICT *dict, const char *unused_name, const char *unused_value) +{ + DICT_NIS *dict_nis = (DICT_NIS *) dict; + + msg_fatal("dict_nis_update: attempt to update NIS map %s", dict_nis->map); +} + +/* dict_nis_close - close NIS map */ + +static void dict_nis_close(DICT *dict) +{ + DICT_NIS *dict_nis = (DICT_NIS *) dict; + + myfree(dict_nis->map); + myfree((char *) dict_nis); +} + +/* dict_nis_open - open NIS map */ + +DICT *dict_nis_open(const char *map, int unused_flags) +{ + DICT_NIS *dict_nis; + + dict_nis = (DICT_NIS *) mymalloc(sizeof(*dict_nis)); + dict_nis->dict.lookup = dict_nis_lookup; + dict_nis->dict.update = dict_nis_update; + dict_nis->dict.close = dict_nis_close; + dict_nis->dict.fd = -1; + dict_nis->map = mystrdup(map); + dict_nis->flags = (DICT_NIS_TRY1NULL | DICT_NIS_TRY0NULL); + if (dict_nis_domain == 0) + dict_nis_init(); + return (&dict_nis->dict); +} + +#endif diff --git a/postfix/util/dict_nis.h b/postfix/util/dict_nis.h new file mode 100644 index 000000000..498715f9f --- /dev/null +++ b/postfix/util/dict_nis.h @@ -0,0 +1,35 @@ +#ifndef _DIST_NIS_H_INCLUDED_ +#define _DIST_NIS_H_INCLUDED_ + +/*++ +/* NAME +/* dict_nis 3h +/* SUMMARY +/* dictionary manager interface to NIS maps +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern DICT *dict_nis_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/dict_nisplus.c b/postfix/util/dict_nisplus.c new file mode 100644 index 000000000..0949f540c --- /dev/null +++ b/postfix/util/dict_nisplus.c @@ -0,0 +1,90 @@ +/*++ +/* NAME +/* dict_nisplus 3 +/* SUMMARY +/* dictionary manager interface to NIS+ maps +/* SYNOPSIS +/* #include +/* +/* DICT *dict_nisplus_open(map, dummy) +/* char *map; +/* int dummy; +/* DESCRIPTION +/* dict_nisplus_open() makes the specified NIS+ map accessible via +/* the generic dictionary operations described in dict_open(3). +/* The \fIdummy\fR argument is not used. +/* 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 +/*--*/ + +/* System library. */ + +#include "sys_defs.h" +#include + +/* Utility library. */ + +#include "msg.h" +#include "mymalloc.h" +#include "htable.h" +#include "dict.h" +#include "dict_nisplus.h" + +/* Application-specific. */ + +typedef struct { + DICT dict; /* generic members */ + char *map; /* NISPLUS map name */ +} DICT_NISPLUS; + +/* dict_nisplus_lookup - find table entry */ + +static const char *dict_nisplus_lookup(DICT *unused_dict, const char *unused_name) +{ + msg_warn("dict_nisplus_lookup: NISPLUS lookup not implemented"); + return (0); +} + +/* dict_nisplus_update - add or update table entry */ + +static void dict_nisplus_update(DICT *dict, const char *unused_name, const char *unused_value) +{ + DICT_NISPLUS *dict_nisplus = (DICT_NISPLUS *) dict; + + msg_fatal("dict_nisplus_update: attempt to update NIS+ map %s", + dict_nisplus->map); +} + +/* dict_nisplus_close - close NISPLUS map */ + +static void dict_nisplus_close(DICT *dict) +{ + DICT_NISPLUS *dict_nisplus = (DICT_NISPLUS *) dict; + + myfree(dict_nisplus->map); + myfree((char *) dict_nisplus); +} + +/* dict_nisplus_open - open NISPLUS map */ + +DICT *dict_nisplus_open(const char *map, int unused_flags) +{ + DICT_NISPLUS *dict_nisplus; + + dict_nisplus = (DICT_NISPLUS *) mymalloc(sizeof(*dict_nisplus)); + dict_nisplus->dict.lookup = dict_nisplus_lookup; + dict_nisplus->dict.update = dict_nisplus_update; + dict_nisplus->dict.close = dict_nisplus_close; + dict_nisplus->dict.fd = -1; + dict_nisplus->map = mystrdup(map); + return (&dict_nisplus->dict); +} diff --git a/postfix/util/dict_nisplus.h b/postfix/util/dict_nisplus.h new file mode 100644 index 000000000..5c7a7c10b --- /dev/null +++ b/postfix/util/dict_nisplus.h @@ -0,0 +1,35 @@ +#ifndef _DICT_NISPLUS_H_INCLUDED_ +#define _DICT_NISPLUS_H_INCLUDED_ + +/*++ +/* NAME +/* dict_nisplus 3h +/* SUMMARY +/* dictionary manager interface to NIS+ maps +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern DICT *dict_nisplus_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/dict_open.c b/postfix/util/dict_open.c new file mode 100644 index 000000000..f1dbbbb48 --- /dev/null +++ b/postfix/util/dict_open.c @@ -0,0 +1,238 @@ +/*++ +/* NAME +/* dict_open 3 +/* SUMMARY +/* low-level dictionary interface +/* SYNOPSIS +/* #include +/* +/* DICT *dict_open(dict_spec, flags) +/* const char *dict_spec; +/* int flags; +/* +/* DICT *dict_open3(dict_type, dict_name, flags) +/* const char *dict_type; +/* const char *dict_name; +/* int flags; +/* +/* void dict_put(dict, key, value) +/* DICT *dict; +/* const char *key; +/* const char *value; +/* +/* char *dict_get(dict, key) +/* DICT *dict; +/* const char *key; +/* +/* void dict_close(dict) +/* DICT *dict; +/* DESCRIPTION +/* This module implements a low-level interface to multiple +/* physical dictionary types. +/* +/* dict_open() takes a type:name pair that specifies a dictionary type +/* and dictionary name, opens the dictionary, and returns a dictionary +/* handle. The \fIflags\fR arguments are as in open(2). The dictionary +/* types are as follows: +/* .IP environ +/* The process environment array. The \fIdict_name\fR argument is ignored. +/* .IP dbm +/* DBM file. +/* .IP hash +/* Berkeley DB file in hash format. +/* .IP btree +/* Berkeley DB file in btree format. +/* .IP nis +/* NIS map. Only read access is supported. +/* .IP nisplus +/* NIS+ map. Only read access is supported. +/* .IP netinfo +/* NetInfo table. Only read access is supported. +/* .IP ldap +/* LDAP ("light-weight" directory access protocol) database access. +/* The support is still incomplete. +/* .PP +/* dict_open3() takes separate arguments for dictionary type and +/* name, but otherwise performs the same functions as dict_open(). +/* +/* dict_get() retrieves the value stored in the named dictionary +/* under the given key. A null pointer means the value was not found. +/* This routine does not manipulate any locks. +/* +/* dict_put() stores the specified key and value into the named +/* dictionary. This routine does not manipulate any locks. +/* +/* dict_close() closes the specified dictionary and cleans up the +/* associated data structures. +/* DIAGNOSTICS +/* Fatal error: open error, unsupported dictionary type, attempt to +/* update non-writable dictionary. +/* 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + /* + * lookup table for available map types. + */ +typedef struct { + char *type; + struct DICT *(*open) (const char *, int); +} DICT_OPEN_INFO; + +static DICT_OPEN_INFO dict_open_info[] = { + "environ", dict_env_open, +#ifdef HAS_DBM + "dbm", dict_dbm_open, +#endif +#ifdef HAS_DB + "hash", dict_hash_open, + "btree", dict_btree_open, +#endif +#ifdef HAS_NIS + "nis", dict_nis_open, +#endif +#ifdef HAS_NISPLUS + "nisplus", dict_nisplus_open, +#endif +#ifdef HAS_NETINFO + "netinfo", dict_ni_open, +#endif +#ifdef HAS_LDAP + "ldap", dict_ldap_open, +#endif + 0, +}; + +/* dict_open - open dictionary */ + +DICT *dict_open(const char *dict_spec, int flags) +{ + char *saved_dict_spec = mystrdup(dict_spec); + char *dict_name; + DICT *dict; + + if ((dict_name = split_at(saved_dict_spec, ':')) == 0) + msg_fatal("open dictionary: need \"type:name\" form: %s", dict_spec); + + dict = dict_open3(saved_dict_spec, dict_name, flags); + myfree(saved_dict_spec); + return (dict); +} + + +/* dict_open3 - open dictionary */ + +DICT *dict_open3(const char *dict_type, const char *dict_name, int flags) +{ + char *myname = "dict_open"; + DICT_OPEN_INFO *dp; + DICT *dict = 0; + + for (dp = dict_open_info; dp->type; dp++) { + if (strcasecmp(dp->type, dict_type) == 0) { + if ((dict = dp->open(dict_name, flags)) == 0) + msg_fatal("opening %s:%s %m", dict_type, dict_name); + if (msg_verbose) + msg_info("%s: %s:%s", myname, dict_type, dict_name); + break; + } + } + if (dp->type == 0) + msg_fatal("unsupported dictionary type: %s", dict_type); + return (dict); +} + +#ifdef TEST + + /* + * Proof-of-concept test program. Create, update or read a database. When + * the input is a name=value pair, the database is updated, otherwise the + * program assumes that the input specifies a lookup key and prints the + * corresponding value. + */ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include "vstring.h" +#include "vstream.h" +#include "msg_vstream.h" +#include "vstring_vstream.h" + +main(int argc, char **argv) +{ + VSTRING *keybuf = vstring_alloc(1); + DICT *dict; + char *dict_name; + int dict_flags; + char *key; + const char *value; + + msg_vstream_init(argv[0], VSTREAM_ERR); + if (argc != 3) + msg_fatal("usage: %s type:file read|write|create", argv[0]); + if (strcasecmp(argv[2], "create") == 0) + dict_flags = O_CREAT | O_RDWR | O_TRUNC; + else if (strcasecmp(argv[2], "write") == 0) + dict_flags = O_RDWR; + else if (strcasecmp(argv[2], "read") == 0) + dict_flags = O_RDONLY; + else + msg_fatal("unknown access mode: %s", argv[2]); + dict_name = argv[1]; + dict = dict_open(dict_name, dict_flags); + while (vstring_fgets_nonl(keybuf, VSTREAM_IN)) { + if ((key = strtok(vstring_str(keybuf), " =")) == 0) + continue; + if ((value = strtok((char *) 0, " =")) == 0) { + if ((value = dict_get(dict, key)) == 0) { + vstream_printf("not found\n"); + } else { + vstream_printf("%s\n", value); + } + } else { + dict_put(dict, key, value); + } + vstream_fflush(VSTREAM_OUT); + } + vstring_free(keybuf); + dict_close(dict); +} + +#endif diff --git a/postfix/util/dir_forest.c b/postfix/util/dir_forest.c new file mode 100644 index 000000000..289cd122e --- /dev/null +++ b/postfix/util/dir_forest.c @@ -0,0 +1,110 @@ +/*++ +/* NAME +/* dir_forest 3 +/* SUMMARY +/* file name to directory forest +/* SYNOPSIS +/* #include +/* +/* char *dir_forest(buf, path, depth) +/* VSTRING *buf; +/* const char *path; +/* int depth; +/* DESCRIPTION +/* This module implements support for directory forests: a file +/* organization that introduces one or more levels of intermediate +/* subdirectories in order to reduce the number of files per directory. +/* +/* dir_forest() maps a file basename to a directory forest and +/* returns the resulting string: file name "abcd" becomes "a/b/" +/* and so on. The number of subdirectory levels is adjustable. +/* +/* Arguments: +/* .IP buf +/* A buffer that is overwritten with the result. The result +/* ends in "/" and is null terminated. If a null pointer is +/* specified, the result is written to a private buffer that +/* is overwritten upon each call. +/* .IP path +/* A null-terminated string of printable characters. Characters +/* special to the file system are not permitted. +/* The first subdirectory is named after the first character +/* in \fIpath\fR, and so on. When the path is shorter than the +/* desired number of subdirectory levels, directory names +/* of '_' (underscore) are used as replacement. +/* .IP depth +/* The desired number of subdirectory levels. +/* DIAGNOSTICS +/* Panic: interface violations. Fatal error: out of memory. +/* 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 "msg.h" +#include "dir_forest.h" + +/* dir_forest - translate base name to directory forest */ + +char *dir_forest(VSTRING *buf, const char *path, int depth) +{ + char *myname = "dir_forest"; + static VSTRING *private_buf = 0; + int n; + const char *cp; + int ch; + + /* + * Sanity checks. + */ + if (*path == 0) + msg_panic("%s: empty path", myname); + if (depth < 1) + msg_panic("%s: depth %d", myname, depth); + + /* + * Your buffer or mine? + */ + if (buf == 0) { + if (private_buf == 0) + private_buf = vstring_alloc(1); + buf = private_buf; + } + + /* + * Generate one or more subdirectory levels, depending on the pathname + * contents. When the pathname is short, use underscores instead. + * Disallow non-printable characters or characters that are special to + * the file system. + */ + VSTRING_RESET(buf); + for (cp = path, n = 0; n < depth; n++) { + if ((ch = *cp) == 0) { + ch = '_'; + } else { + if (!ISPRINT(ch) || ch == '.' || ch == '/') + msg_panic("%s: invalid pathname: %s", myname, path); + cp++; + } + VSTRING_ADDCH(buf, ch); + VSTRING_ADDCH(buf, '/'); + } + VSTRING_TERMINATE(buf); + + if (msg_verbose) + msg_info("%s: %s -> %s", myname, path, vstring_str(buf)); + return (vstring_str(buf)); +} diff --git a/postfix/util/dir_forest.h b/postfix/util/dir_forest.h new file mode 100644 index 000000000..2c4c3639e --- /dev/null +++ b/postfix/util/dir_forest.h @@ -0,0 +1,35 @@ +#ifndef _DIR_FOREST_H_INCLUDED_ +#define _DIR_FOREST_H_INCLUDED_ + +/*++ +/* NAME +/* dir_forest 3h +/* SUMMARY +/* file name to directory forest +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern char *dir_forest(VSTRING *, 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/doze.c b/postfix/util/doze.c new file mode 100644 index 000000000..ac34529c2 --- /dev/null +++ b/postfix/util/doze.c @@ -0,0 +1,68 @@ +/*++ +/* NAME +/* doze 3 +/* SUMMARY +/* take a nap +/* SYNOPSIS +/* #include +/* +/* void doze(microseconds) +/* unsigned microseconds; +/* DESCRIPTION +/* doze() sleeps for the specified number of microseconds. It is +/* a simple alternative for systems that lack usleep(). +/* DIAGNOSTICS +/* All errors are fatal. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include "sys_defs.h" +#include +#include +#include + +/* Utility library. */ + +#include +#include + +/* doze - sleep a while */ + +void doze(unsigned delay) +{ + struct timeval tv; + +#define MILLION 1000000 + + tv.tv_sec = (delay > MILLION ? delay / MILLION : 0); + tv.tv_usec = delay % MILLION; + while (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &tv) < 0) + if (errno != EINTR) + msg_fatal("doze: select: %m"); +} + +#ifdef TEST + +#include + +int main(int argc, char **argv) +{ + unsigned delay; + + if (argc != 2 || (delay = atol(argv[1])) == 0) + msg_fatal("usage: %s microseconds", argv[0]); + doze(delay); + exit(0); +} + +#endif diff --git a/postfix/util/environ.c b/postfix/util/environ.c new file mode 100644 index 000000000..f93f6d591 --- /dev/null +++ b/postfix/util/environ.c @@ -0,0 +1,10 @@ + /* + * The environ.c module from TCP Wrappers is not bundled with IBM's public + * release. It will be made available as contributed software from + * http://www.postfix.org/ + */ +#include "sys_defs.h" + +#ifdef MISSING_SETENV_PUTENV +#error "This requires contributed software from http://www.postfix.org/" +#endif diff --git a/postfix/util/events.c b/postfix/util/events.c new file mode 100644 index 000000000..aaf423ba1 --- /dev/null +++ b/postfix/util/events.c @@ -0,0 +1,616 @@ +/*++ +/* NAME +/* events 3 +/* SUMMARY +/* event manager +/* SYNOPSIS +/* #include +/* +/* time_t event_time() +/* +/* void event_loop(delay) +/* int delay; +/* +/* time_t event_request_timer(callback, context, delay) +/* void (*callback)(char *context); +/* char *context; +/* int delay; +/* +/* int event_cancel_timer(callback, context) +/* void (*callback)(char *context); +/* char *context; +/* +/* void event_enable_read(fd, callback, context) +/* int fd; +/* void (*callback)(int event, char *context); +/* char *context; +/* +/* void event_enable_write(fd, callback, context) +/* int fd; +/* void (*callback)(int event, char *context); +/* char *context; +/* +/* void event_disable_readwrite(fd) +/* int fd; +/* DESCRIPTION +/* This module delivers I/O and timer events. +/* Multiple I/O streams and timers can be monitored simultaneously. +/* Events are delivered via callback routines provided by the +/* application. When requesting an event, the application can provide +/* private context that is passed back when the callback routine is +/* executed. +/* +/* event_time() returns a cached value of the current time. +/* +/* event_loop() monitors all I/O channels for which the application has +/* expressed interest, and monitors the timer request queue. +/* It notifies the application whenever events of interest happen. +/* A negative delay value causes the function to pause until something +/* happens; a positive delay value causes event_loop() to return when +/* the next event happens or when the delay time in seconds is over, +/* whatever happens first. A zero delay effectuates a poll. +/* +/* Note: in order to avoid race conditions, event_loop() cannot +/* not be called recursively. +/* +/* event_request_timer() causes the specified callback function to +/* be called with the specified context argument after \fIdelay\fR +/* seconds, or as soon as possible thereafter. The delay should +/* not be negative. +/* 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 +/* delivery. The result is the absolute time at which the timer is +/* scheduled to go off. +/* +/* event_cancel_timer() cancels the specified (callback, context) request. +/* The application is allowed to cancel non-existing requests. The result +/* value is the amount of time left before the timer would have gone off, +/* or -1 in case of no pending timer. +/* +/* event_enable_read() (event_enable_write()) enables read (write) events +/* on the named I/O channel. It is up to the application to assemble +/* partial reads or writes. +/* An I/O channel cannot handle more than one request at the +/* same time. The application is allowed to enable an event that +/* is already enabled (same channel, callback and context). +/* +/* The manifest constants EVENT_NULL_CONTEXT and EVENT_NULL_TYPE +/* provide convenient null values. +/* +/* The callback routine has the following arguments: +/* .IP fd +/* The stream on which the event happened. +/* .IP event +/* An indication of the event type: +/* .RS +/* .IP EVENT_READ +/* read event, +/* .IP EVENT_WRITE +/* write event, +/* .IP EVENT_XCPT +/* exception. +/* .RE +/* .IP context +/* Application context given to event_enable_read() (event_enable_write()). +/* .PP +/* event_disable_readwrite() disables further I/O events on the specified +/* I/O channel. The application is allowed to cancel non-existing +/* I/O event requests. +/* DIAGNOSTICS +/* Panics: interface violations. Fatal errors: out of memory, +/* system call failure. Warnings: the number of available +/* file descriptors is much less than FD_SETSIZE. +/* BUGS +/* This module is based on event selection. It assumes that the +/* event_loop() routine is called frequently. This approach is +/* not suitable for applications with compute-bound loops that +/* take a significant amount of time. +/* 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 libraries. */ + +#include "sys_defs.h" +#include /* XXX: 44BSD uses bzero() */ +#include +#include +#include +#include /* offsetof() */ +#include /* bzero() prototype for 44BSD */ + +#ifdef USE_SYS_SELECT_H +#include +#endif + +/* Application-specific. */ + +#include "mymalloc.h" +#include "msg.h" +#include "iostuff.h" +#include "ring.h" +#include "events.h" + + /* + * I/O events. We pre-allocate one data structure per file descriptor. XXX + * For now use FD_SETSIZE as defined along with the fd-set type. + */ +typedef struct EVENT_FDTABLE EVENT_FDTABLE; + +struct EVENT_FDTABLE { + EVENT_NOTIFY_RDWR callback; + char *context; +}; +static fd_set event_rmask; /* enabled read events */ +static fd_set event_wmask; /* enabled write events */ +static fd_set event_xmask; /* for bad news mostly */ +static int event_fdsize; /* number of file descriptors */ +static EVENT_FDTABLE *event_fdtable; /* one slot per file descriptor */ +static int event_max_fd; /* highest fd number seen */ + + /* + * Timer events. Timer requests are kept sorted, in a circular list. We use + * the RING abstraction, so we get to use a couple ugly macros. + */ +typedef struct EVENT_TIMER EVENT_TIMER; + +struct EVENT_TIMER { + time_t when; /* when event is wanted */ + EVENT_NOTIFY_TIME callback; /* callback function */ + char *context; /* callback context */ + RING ring; /* linkage */ +}; + +static RING event_timer_head; /* timer queue head */ + +#define RING_TO_TIMER(r) \ + ((EVENT_TIMER *) ((char *) (r) - offsetof(EVENT_TIMER, ring))) + +#define FOREACH_QUEUE_ENTRY(entry, head) \ + for (entry = ring_succ(head); entry != (head); entry = ring_succ(entry)) + +#define FIRST_TIMER(head) \ + (ring_succ(head) != (head) ? RING_TO_TIMER(ring_succ(head)) : 0) + + /* + * Other private data structures. + */ +static time_t event_present; /* cached time of day */ + +#define EVENT_INIT_NEEDED() (event_present == 0) + +/* event_init - set up tables and such */ + +static void event_init(void) +{ + EVENT_FDTABLE *fdp; + + if (!EVENT_INIT_NEEDED()) + msg_panic("event_init: repeated call"); + + /* + * Initialize the file descriptor table. XXX It should be possible to + * adjust (or at least extend) the table size on the fly. + */ + if ((event_fdsize = open_limit(FD_SETSIZE)) < 0) + msg_fatal("unable to determine open file limit"); + if (event_fdsize < FD_SETSIZE / 2 && event_fdsize < 256) + msg_warn("could allocate space for only %d open files", event_fdsize); + event_fdtable = (EVENT_FDTABLE *) + mymalloc(sizeof(EVENT_FDTABLE) * event_fdsize); + for (fdp = event_fdtable; fdp < event_fdtable + event_fdsize; fdp++) { + fdp->callback = 0; + fdp->context = 0; + } + + /* + * Initialize the I/O event request masks. + */ + FD_ZERO(&event_rmask); + FD_ZERO(&event_wmask); + FD_ZERO(&event_xmask); + + /* + * Initialize timer stuff. + */ + ring_init(&event_timer_head); + (void) time(&event_present); + + /* + * Avoid an infinite initialization loop. + */ + if (EVENT_INIT_NEEDED()) + msg_panic("event_init: unable to initialize"); +} + +/* event_time - look up cached time of day */ + +time_t event_time(void) +{ + if (EVENT_INIT_NEEDED()) + event_init(); + + return (event_present); +} + +/* event_enable_read - enable read events */ + +void event_enable_read(int fd, EVENT_NOTIFY_RDWR callback, char *context) +{ + char *myname = "event_enable_read"; + EVENT_FDTABLE *fdp; + + if (EVENT_INIT_NEEDED()) + event_init(); + + /* + * Sanity checks. + */ + if (fd < 0 || fd >= event_fdsize) + msg_panic("%s: bad file descriptor: %d", myname, fd); + + if (msg_verbose > 2) + msg_info("%s: fd %d", myname, fd); + + /* + * Disallow multiple requests on the same file descriptor. Allow + * duplicates of the same request. + */ + fdp = event_fdtable + fd; + if (FD_ISSET(fd, &event_xmask)) { + if (FD_ISSET(fd, &event_rmask) + && fdp->callback == callback + && fdp->context == context) + return; + msg_panic("%s: fd %d: multiple I/O request", myname, fd); + } + FD_SET(fd, &event_xmask); + FD_SET(fd, &event_rmask); + fdp->callback = callback; + fdp->context = context; + if (event_max_fd < fd) + event_max_fd = fd; +} + +/* event_enable_write - enable write events */ + +void event_enable_write(int fd, EVENT_NOTIFY_RDWR callback, char *context) +{ + char *myname = "event_enable_write"; + EVENT_FDTABLE *fdp; + + if (EVENT_INIT_NEEDED()) + event_init(); + + /* + * Sanity checks. + */ + if (fd < 0 || fd >= event_fdsize) + msg_panic("%s: bad file descriptor: %d", myname, fd); + + if (msg_verbose > 2) + msg_info("%s: fd %d", myname, fd); + + /* + * Disallow multiple requests on the same file descriptor. Allow + * duplicates of the same request. + */ + fdp = event_fdtable + fd; + if (FD_ISSET(fd, &event_xmask)) { + if (FD_ISSET(fd, &event_wmask) + && fdp->callback == callback + && fdp->context == context) + return; + msg_panic("%s: fd %d: multiple I/O request", myname, fd); + } + FD_SET(fd, &event_xmask); + FD_SET(fd, &event_wmask); + fdp->callback = callback; + fdp->context = context; + if (event_max_fd < fd) + event_max_fd = fd; +} + +/* event_disable_readwrite - disable request for read or write events */ + +void event_disable_readwrite(int fd) +{ + char *myname = "event_disable_readwrite"; + EVENT_FDTABLE *fdp; + + if (EVENT_INIT_NEEDED()) + event_init(); + + /* + * Sanity checks. + */ + if (fd < 0 || fd >= event_fdsize) + msg_panic("%s: bad file descriptor: %d", myname, fd); + + if (msg_verbose > 2) + msg_info("%s: fd %d", myname, fd); + + /* + * Don't complain when there is nothing to cancel. The request may have + * been canceled from another thread. + */ + FD_CLR(fd, &event_xmask); + FD_CLR(fd, &event_rmask); + FD_CLR(fd, &event_wmask); + fdp = event_fdtable + fd; + fdp->callback = 0; + fdp->context = 0; +} + +/* event_request_timer - (re)set timer */ + +time_t event_request_timer(EVENT_NOTIFY_TIME callback, char *context, int delay) +{ + char *myname = "event_request_timer"; + RING *ring; + EVENT_TIMER *timer; + + if (EVENT_INIT_NEEDED()) + event_init(); + + /* + * Sanity checks. + */ + if (delay < 0) + msg_panic("%s: invalid delay: %d", myname, delay); + + /* + * Make sure we schedule this event at the right time. + */ + time(&event_present); + + /* + * See if they are resetting an existing timer request. If so, take the + * request away from the timer queue so that it can be inserted at the + * right place. + */ + FOREACH_QUEUE_ENTRY(ring, &event_timer_head) { + timer = RING_TO_TIMER(ring); + if (timer->callback == callback && timer->context == context) { + timer->when = event_present + delay; + ring_detach(ring); + if (msg_verbose > 2) + msg_info("%s: reset 0x%lx 0x%lx %d", myname, + (long) callback, (long) context, delay); + break; + } + } + + /* + * If not found, schedule a new timer request. + */ + if (ring == &event_timer_head) { + timer = (EVENT_TIMER *) mymalloc(sizeof(EVENT_TIMER)); + timer->when = event_present + delay; + timer->callback = callback; + timer->context = context; + if (msg_verbose > 2) + msg_info("%s: set 0x%lx 0x%lx %d", myname, + (long) callback, (long) context, delay); + } + + /* + * Insert the request at the right place. Timer requests are kept sorted + * to reduce lookup overhead in the event loop. + */ + FOREACH_QUEUE_ENTRY(ring, &event_timer_head) + if (timer->when < RING_TO_TIMER(ring)->when) + break; + ring_prepend(ring, &timer->ring); + + return (timer->when); +} + +/* event_cancel_timer - cancel timer */ + +int event_cancel_timer(EVENT_NOTIFY_TIME callback, char *context) +{ + char *myname = "event_cancel_timer"; + RING *ring; + EVENT_TIMER *timer; + int time_left = -1; + + if (EVENT_INIT_NEEDED()) + event_init(); + + /* + * See if they are canceling an existing timer request. Do not complain + * when the request is not found. It might have been canceled from some + * other thread. + */ + FOREACH_QUEUE_ENTRY(ring, &event_timer_head) { + timer = RING_TO_TIMER(ring); + if (timer->callback == callback && timer->context == context) { + if ((time_left = timer->when - event_present) < 0) + time_left = 0; + ring_detach(ring); + myfree((char *) timer); + break; + } + } + if (msg_verbose > 2) + msg_info("%s: 0x%lx 0x%lx %d", myname, + (long) callback, (long) context, time_left); + return (time_left); +} + +/* event_loop - wait for the next event */ + +void event_loop(int delay) +{ + char *myname = "event_loop"; + static int nested; + fd_set rmask; + fd_set wmask; + fd_set xmask; + struct timeval tv; + struct timeval *tvp; + EVENT_TIMER *timer; + int fd; + EVENT_FDTABLE *fdp; + int select_delay; + + if (EVENT_INIT_NEEDED()) + event_init(); + + /* + * Find out when the next timer would go off. Timer requests are sorted. + * If any timer is scheduled, adjust the delay appropriately. + */ + if ((timer = FIRST_TIMER(&event_timer_head)) != 0) { + event_present = time((time_t *) 0); + if ((select_delay = timer->when - event_present) < 0) { + select_delay = 0; + } else if (delay >= 0 && select_delay > delay) { + select_delay = delay; + } + } else { + select_delay = delay; + } + if (msg_verbose > 2) + msg_info("event_loop: select_delay %d", select_delay); + + /* + * Negative delay means: wait until something happens. Zero delay means: + * poll. Positive delay means: wait at most this long. + */ + if (select_delay < 0) { + tvp = 0; + } else { + tvp = &tv; + tv.tv_usec = 0; + tv.tv_sec = select_delay; + } + + /* + * Pause until the next event happens. When select() has a problem, don't + * go into a tight loop. Allow select() to be interrupted due to the + * arrival of a signal. + */ + rmask = event_rmask; + wmask = event_wmask; + xmask = event_xmask; + + if (select(event_max_fd + 1, &rmask, &wmask, &xmask, tvp) < 0) { + if (errno != EINTR) + msg_fatal("event_loop: select: %m"); + return; + } + + /* + * Before entering the application call-back routines, make sure we + * aren't being called from a call-back routine. Doing so would make us + * vulnerable to all kinds of race conditions. + */ + if (nested++ > 0) + msg_panic("event_loop: recursive call"); + + /* + * Deliver timer events. Requests are sorted: we can stop when we reach + * the future or the list end. Allow the application to update the timer + * queue while it is being called back. To this end, we repeatedly pop + * the first request off the timer queue before delivering the event to + * the application. + */ + event_present = time((time_t *) 0); + + while ((timer = FIRST_TIMER(&event_timer_head)) != 0) { + if (timer->when > event_present) + break; + ring_detach(&timer->ring); /* first this */ + 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 */ + myfree((char *) timer); + } + + /* + * Deliver I/O events. Allow the application to cancel event requests + * while it is being called back. To this end, we keep an eye on the + * contents of event_xmask, so that we deliver only events that are still + * wanted. We do not change the event request masks. It is up to the + * application to determine when a read or write is complete. + */ + for (fd = 0, fdp = event_fdtable; fd <= event_max_fd; fd++, fdp++) { + if (FD_ISSET(fd, &event_xmask)) { + if (FD_ISSET(fd, &xmask)) { + if (msg_verbose > 2) + msg_info("%s: exception %d 0x%lx 0x%lx", myname, + fd, (long) fdp->callback, (long) fdp->context); + fdp->callback(EVENT_XCPT, fdp->context); + } else if (FD_ISSET(fd, &wmask)) { + if (msg_verbose > 2) + msg_info("%s: write %d 0x%lx 0x%lx", myname, + fd, (long) fdp->callback, (long) fdp->context); + fdp->callback(EVENT_WRITE, fdp->context); + } else if (FD_ISSET(fd, &rmask)) { + if (msg_verbose > 2) + msg_info("%s: read %d 0x%lx 0x%lx", myname, + fd, (long) fdp->callback, (long) fdp->context); + fdp->callback(EVENT_READ, fdp->context); + } + } + } + nested--; +} + +#ifdef TEST + + /* + * Proof-of-concept test program for the event manager. Print "dingdong" + * every 5 seconds, while echoing any lines read from stdin. The "dingdong + * changes case each time it is printed. + */ +#include +#include + +#define DELAY 5 + +/* dingdong - print text every DELAY seconds */ + +static void dingdong(char *context) +{ + printf("%c", *context); + fflush(stdout); + *context = (ISUPPER(*context) ? TOLOWER(*context) : TOUPPER(*context)); + event_request_timer(dingdong, context, DELAY); +} + +/* echo - echo text received on stdin */ + +static void echo(int unused_event, char *unused_context) +{ + char buf[BUFSIZ]; + + if (fgets(buf, sizeof(buf), stdin) == 0) + exit(0); + printf("Result: %s", buf); +} + +main(void) +{ + static char text[] = "\rdingdong "; + char *cp; + + for (cp = text; *cp; cp++) + event_request_timer(dingdong, cp, 0); + event_enable_read(fileno(stdin), echo, (char *) 0); + for (;;) + event_loop(-1); +} + +#endif diff --git a/postfix/util/events.h b/postfix/util/events.h new file mode 100644 index 000000000..f3f6208d7 --- /dev/null +++ b/postfix/util/events.h @@ -0,0 +1,61 @@ +#ifndef _EVENTS_H_INCLUDED_ +#define _EVENTS_H_INCLUDED_ + +/*++ +/* NAME +/* events 3h +/* SUMMARY +/* event manager +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * External interface. + */ +typedef void (*EVENT_NOTIFY_RDWR) (int, char *); +typedef void (*EVENT_NOTIFY_TIME) (char *); + +extern time_t event_time(void); +extern void event_enable_read(int, EVENT_NOTIFY_RDWR, char *); +extern void event_enable_write(int, EVENT_NOTIFY_RDWR, char *); +extern void event_disable_readwrite(int); +extern time_t event_request_timer(EVENT_NOTIFY_TIME, char *, int); +extern int event_cancel_timer(EVENT_NOTIFY_TIME, char *); +extern void event_loop(int); + + /* + * Event codes. + */ +#define EVENT_READ (1<<0) /* read event */ +#define EVENT_WRITE (1<<1) /* write event */ +#define EVENT_XCPT (1<<2) /* exception */ + +#define EVENT_ERROR EVENT_XCPT + + /* + * Dummies. + */ +#define EVENT_NULL_TYPE 0 +#define EVENT_NULL_CONTEXT ((char *) 0) + +/* 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 +/* CREATION DATE +/* Wed Jan 29 17:00:03 EST 1997 +/*--*/ + +#endif diff --git a/postfix/util/exec_command.c b/postfix/util/exec_command.c new file mode 100644 index 000000000..ea0342dd8 --- /dev/null +++ b/postfix/util/exec_command.c @@ -0,0 +1,111 @@ +/*++ +/* NAME +/* exec_command 3 +/* SUMMARY +/* execute command +/* SYNOPSIS +/* #include +/* +/* NORETURN exec_command(command) +/* const char *command; +/* DESCRIPTION +/* \fIexec_command\fR() replaces the current process by an instance +/* of \fIcommand\fR. This routine uses a simple heuristic to avoid +/* the overhead of running a command shell interpreter. +/* DIAGNOSTICS +/* This routine never returns. All errors are fatal. +/* 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 +#ifdef USE_PATHS_H +#include +#endif +#include +#include + +/* Utility library. */ + +#include +#include +#include + +/* Application-specific. */ + +#define SPACE_TAB " \t" + +/* exec_command - exec command */ + +NORETURN exec_command(const char *command) +{ + ARGV *argv; + + /* + * Character filter. In this particular case, we allow space and tab in + * addition to the regular character set. + */ + static char ok_chars[] = "1234567890!@%-_=+:,./\ +abcdefghijklmnopqrstuvwxyz\ +ABCDEFGHIJKLMNOPQRSTUVWXYZ" SPACE_TAB; + + /* + * See if this command contains any shell magic characters. + */ + if (command[strspn(command, ok_chars)] == 0) { + + /* + * No shell meta characters found, so we can try to avoid the overhead + * of running a shell. Just split the command on whitespace and exec + * the result directly. + */ + argv = argv_split(command, SPACE_TAB); + (void) execvp(argv->argv[0], argv->argv); + + /* + * Auch. Perhaps they're using some shell built-in command. + */ + if (errno != ENOENT || strchr(argv->argv[0], '/') != 0) + msg_fatal("execvp %s: %m", argv->argv[0]); + + /* + * Not really necessary, but... + */ + argv_free(argv); + } + + /* + * Pass the command to a shell. + */ + (void) execl(_PATH_BSHELL, "sh", "-c", command, (char *) 0); + msg_fatal("execl %s: %m", _PATH_BSHELL); +} + +#ifdef TEST + + /* + * Yet another proof-of-concept test program. + */ +#include +#include + +main(int argc, char **argv) +{ + msg_vstream_init(argv[0], VSTREAM_ERR); + if (argc != 2) + msg_fatal("usage: %s 'command'", argv[0]); + exec_command(argv[1]); +} + +#endif diff --git a/postfix/util/exec_command.h b/postfix/util/exec_command.h new file mode 100644 index 000000000..4e77211de --- /dev/null +++ b/postfix/util/exec_command.h @@ -0,0 +1,30 @@ +#ifndef _EXEC_COMMAND_H_INCLUDED_ +#define _EXEC_COMMAND_H_INCLUDED_ + +/*++ +/* NAME +/* exec_command 3h +/* SUMMARY +/* execute command +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern NORETURN exec_command(const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/util/fifo_listen.c b/postfix/util/fifo_listen.c new file mode 100644 index 000000000..c0a1ba6e6 --- /dev/null +++ b/postfix/util/fifo_listen.c @@ -0,0 +1,110 @@ +/*++ +/* NAME +/* fifo_listen 3 +/* SUMMARY +/* start fifo listener +/* SYNOPSIS +/* #include +/* +/* int fifo_listen(path, permissions, block_mode) +/* const char *path; +/* int permissions; +/* int block_mode; +/* DESCRIPTION +/* The \fBfifo_listen\fR routine creates the specified named pipe with +/* the specified permissions, opens the FIFO read-write or read-only, +/* depending on the host operating system, and returns the resulting +/* file descriptor. +/* The \fIblock_mode\fR argument is either NON_BLOCKING for +/* a non-blocking socket, or BLOCKING for blocking mode. +/* DIAGNOSTICS +/* Fatal errors: all system call failures. +/* 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 interfaces. */ + +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include "msg.h" +#include "iostuff.h" +#include "listen.h" + +#define BUF_LEN 100 + +/* fifo_listen - create fifo listener */ + +int fifo_listen(const char *path, int permissions, int block_mode) +{ + char buf[BUF_LEN]; + static int open_mode = 0; + char *myname = "fifo_listen"; + struct stat st; + int fd; + int count; + + /* + * Create a named pipe (fifo). Do whatever we can so we don't run into + * trouble when this process is restarted after crash. Make sure that we + * open a fifo and not something else, then change permissions to what we + * wanted them to be, because mkfifo() is subject to umask settings. + * Instead we could zero the umask temporarily before creating the FIFO, + * but that would cost even more system calls. Figure out if the fifo + * needs to be opened O_RDWR or O_RDONLY. Some systems need one, some + * need the other. If we choose the wrong mode, the fifo will stay + * readable, causing the program to go into a loop. + */ + if (unlink(path) && errno != ENOENT) + msg_fatal("%s: remove %s: %m", myname, path); + if (mkfifo(path, permissions) < 0) + msg_fatal("%s: create fifo %s: %m", myname, path); + switch (open_mode) { + case 0: + if ((fd = open(path, O_RDWR | O_NONBLOCK, 0)) < 0) + msg_fatal("%s: open %s: %m", myname, path); + if (readable(fd) == 0) { + open_mode = O_RDWR | O_NONBLOCK; + break; + } else { + open_mode = O_RDONLY | O_NONBLOCK; + if (msg_verbose) + msg_info("open O_RDWR makes fifo readable - trying O_RDONLY"); + (void) close(fd); + /* FALLTRHOUGH */ + } + default: + if ((fd = open(path, open_mode, 0)) < 0) + msg_fatal("%s: open %s: %m", myname, path); + break; + } + + /* + * Make sure we opened a FIFO and skip any cruft that might have + * accumulated before we opened it. + */ + if (fstat(fd, &st) < 0) + msg_fatal("%s: fstat %s: %m", myname, path); + if (S_ISFIFO(st.st_mode) == 0) + msg_fatal("%s: not a fifo: %s", myname, path); + if (fchmod(fd, permissions) < 0) + msg_fatal("%s: fchmod %s: %m", myname, path); + non_blocking(fd, block_mode); + while ((count = peekfd(fd)) > 0 + && read(fd, buf, BUF_LEN < count ? BUF_LEN : count) > 0) + /* void */ ; + return (fd); +} diff --git a/postfix/util/fifo_open.c b/postfix/util/fifo_open.c new file mode 100644 index 000000000..6ea988a13 --- /dev/null +++ b/postfix/util/fifo_open.c @@ -0,0 +1,66 @@ +/*++ +/* NAME +/* fifo_open 1 +/* SUMMARY +/* fifo client test program +/* SYNOPSIS +/* fifo_open +/* DESCRIPTION +/* fifo_open creates a FIFO, then attempts to open it for writing +/* with non-blocking mode enabled. According to the POSIX standard +/* the open should succeed. +/* 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 +/*--*/ + +#include +#include +#include +#include +#include + +#define FIFO_PATH "test-fifo" +#define perrorexit(s) { perror(s); exit(1); } + +static void cleanup(void) +{ + printf("Removing fifo %s...\n", FIFO_PATH); + if (unlink(FIFO_PATH)) + perrorexit("unlink"); + printf("Done.\n"); +} + +static void stuck(int unused_sig) +{ + printf("Non-blocking, write-only open of FIFO blocked\n"); + cleanup(); + exit(1); +} + +int main(int unused_argc, char **unused_argv) +{ + (void) unlink(FIFO_PATH); + printf("Creating fifo %s...\n", FIFO_PATH); + if (mkfifo(FIFO_PATH, 0600) < 0) + perrorexit("mkfifo"); + signal(SIGALRM, stuck); + alarm(5); + printf("Opening fifo %s, non-blocking, write-only mode...\n", FIFO_PATH); + if (open(FIFO_PATH, O_WRONLY | O_NONBLOCK, 0) < 0) { + perror("open"); + cleanup(); + exit(1); + } + printf("Non-blocking, write-only open of FIFO succeeded\n"); + cleanup(); + exit(0); +} diff --git a/postfix/util/fifo_rdonly_bug.c b/postfix/util/fifo_rdonly_bug.c new file mode 100644 index 000000000..bdad46348 --- /dev/null +++ b/postfix/util/fifo_rdonly_bug.c @@ -0,0 +1,126 @@ +/*++ +/* NAME +/* fifo_rdonly_bug 1 +/* SUMMARY +/* fifo server test program +/* SYNOPSIS +/* fifo_rdonly_bug +/* DESCRIPTION +/* fifo_rdonly_bug creates a FIFO and opens it read only. It +/* then opens the FIFO for writing, writes one byte, and closes +/* the writing end. On Linux Redhat 4.2 and 5.0, and HP-UX 9.05 +/* and 10.20, select() will report that the FIFO remains readable +/* even after multiple read operations. +/* 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 +/*--*/ + +#include +#include +#include +#include +#include +#include +#include + +#define FIFO_PATH "test-fifo" +#define TRIGGER_DELAY 5 + +#define perrorexit(s) { perror(s); exit(1); } + +static void cleanup(void) +{ + printf("Removing fifo %s...\n", FIFO_PATH); + if (unlink(FIFO_PATH)) + perrorexit("unlink"); + printf("Done.\n"); +} + +static void perrorcleanup(char *str) +{ + perror(str); + cleanup(); + exit(0); +} + +static void readable_event(int fd) +{ + char ch; + static int count = 0; + + if (read(fd, &ch, 1) < 0) { + perror("read"); + sleep(1); + } + if (count++ > 5) { + printf("FIFO remains readable after multiple reads.\n"); + cleanup(); + exit(1); + } +} + +int main(int unused_argc, char **unused_argv) +{ + struct timeval tv; + fd_set read_fds; + fd_set except_fds; + int fd; + int fd2; + + (void) unlink(FIFO_PATH); + + printf("Create fifo %s...\n", FIFO_PATH); + if (mkfifo(FIFO_PATH, 0600) < 0) + perrorexit("mkfifo"); + + printf("Open fifo %s, read-only mode...\n", FIFO_PATH); + if ((fd = open(FIFO_PATH, O_RDONLY | O_NONBLOCK, 0)) < 0) + perrorcleanup("open"); + + printf("Write one byte to the fifo, then close it...\n"); + if ((fd2 = open(FIFO_PATH, O_WRONLY, 0)) < 0) + perrorcleanup("open fifo O_WRONLY"); + if (write(fd2, "", 1) < 1) + perrorcleanup("write one byte to fifo"); + if (close(fd2) < 0) + perrorcleanup("close fifo"); + + printf("Selecting the fifo for readability...\n"); + + for (;;) { + FD_ZERO(&read_fds); + FD_SET(fd, &read_fds); + FD_ZERO(&except_fds); + FD_SET(fd, &except_fds); + tv.tv_sec = 1; + tv.tv_usec = 0; + + switch (select(fd + 1, &read_fds, (fd_set *) 0, &except_fds, &tv)) { + case -1: + perrorexit("select"); + default: + if (FD_ISSET(fd, &except_fds)) { + printf("Exceptional fifo condition! You are not normal!\n"); + readable_event(fd); + } else if (FD_ISSET(fd, &read_fds)) { + printf("Readable fifo condition\n"); + readable_event(fd); + } + break; + case 0: + printf("The fifo is not readable. You're normal.\n"); + cleanup(); + exit(0); + break; + } + } +} diff --git a/postfix/util/fifo_rdwr_bug.c b/postfix/util/fifo_rdwr_bug.c new file mode 100644 index 000000000..c9be211c6 --- /dev/null +++ b/postfix/util/fifo_rdwr_bug.c @@ -0,0 +1,88 @@ +/*++ +/* NAME +/* fifo_rdwr_bug 1 +/* SUMMARY +/* fifo server test program +/* SYNOPSIS +/* fifo_rdwr_bug +/* DESCRIPTION +/* fifo_rdwr_bug creates a FIFO and opens it read-write mode. +/* On BSD/OS 3.1 select() will report that the FIFO is readable +/* even before any data is written to it. Doing an actual read +/* causes the read to block; a non-blocking read fails. +/* 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 +/*--*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FIFO_PATH "test-fifo" +#define perrorexit(s) { perror(s); exit(1); } + +static void cleanup(void) +{ + printf("Removing fifo %s...\n", FIFO_PATH); + if (unlink(FIFO_PATH)) + perrorexit("unlink"); + printf("Done.\n"); +} + +int main(int unused_argc, char **unused_argv) +{ + struct timeval tv; + fd_set read_fds; + fd_set except_fds; + int fd; + + (void) unlink(FIFO_PATH); + + printf("Creating fifo %s...\n", FIFO_PATH); + if (mkfifo(FIFO_PATH, 0600) < 0) + perrorexit("mkfifo"); + + printf("Opening fifo %s, read-write mode...\n", FIFO_PATH); + if ((fd = open(FIFO_PATH, O_RDWR, 0)) < 0) { + perror("open"); + cleanup(); + exit(1); + } + printf("Selecting the fifo for readability...\n"); + FD_ZERO(&read_fds); + FD_SET(fd, &read_fds); + FD_ZERO(&except_fds); + FD_SET(fd, &except_fds); + tv.tv_sec = 1; + tv.tv_usec = 0; + + switch (select(fd + 1, &read_fds, (fd_set *) 0, &except_fds, &tv)) { + case -1: + perrorexit("select"); + default: + if (FD_ISSET(fd, &read_fds)) { + printf("Opening a fifo read-write makes it readable!!\n"); + break; + } + case 0: + printf("The fifo is not readable, as it should be.\n"); + break; + } + cleanup(); + exit(0); +} diff --git a/postfix/util/fifo_trigger.c b/postfix/util/fifo_trigger.c new file mode 100644 index 000000000..b28011436 --- /dev/null +++ b/postfix/util/fifo_trigger.c @@ -0,0 +1,155 @@ +/*++ +/* NAME +/* fifo_trigger 3 +/* SUMMARY +/* wakeup fifo server +/* SYNOPSIS +/* #include +/* +/* int fifo_trigger(service, buf, len, timeout) +/* const char *service; +/* const char *buf; +/* int len; +/* int timeout; +/* DESCRIPTION +/* fifo_trigger() wakes up the named fifo server by writing +/* the contents of the specified buffer to the fifo. +/* +/* Arguments: +/* .IP service +/* Name of the communication endpoint. +/* .IP buf +/* Address of data to be written. +/* .IP len +/* Amount of data to be written. +/* .IP timeout +/* Deadline in seconds. Specify a value <= 0 to disable +/* the time limit. +/* DIAGNOSTICS +/* The result is zero in case of success, -1 in case of problems. +/* BUGS +/* 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 + +/* Utility library. */ + +#include +#include +#include + +/* fifo_trigger - wakeup fifo server */ + +int fifo_trigger(const char *service, const char *buf, int len, int timeout) +{ + char *myname = "fifo_trigger"; + int fd; + + /* + * Write the request to the service fifo. According to POSIX, the open + * shall always return immediately, and shall return an error when no + * process is reading from the FIFO. + */ + if ((fd = open(service, O_WRONLY | O_NONBLOCK, 0)) < 0) { + if (msg_verbose) + msg_info("%s: open %s: %m", myname, service); + return (-1); + } + + /* + * Write the request... + */ + non_blocking(fd, timeout > 0 ? NON_BLOCKING : BLOCKING); + if (write_buf(fd, buf, len, timeout) < 0) + if (msg_verbose) + msg_warn("%s: write %s: %m", myname, service); + + /* + * Disconnect. + */ + if (close(fd)) + if (msg_verbose) + msg_warn("%s: close %s: %m", myname, service); + return (0); +} + +#ifdef TEST + + /* + * Set up a FIFO listener, and keep triggering until the listener becomes + * idle, which should never happen. + */ +#include +#include + +#include "events.h" +#include "listen.h" + +#define TEST_FIFO "test-fifo" + +int trig_count; +int wakeup_count; + +static void cleanup(void) +{ + unlink(TEST_FIFO); + exit(1); +} + +static void handler(int sig) +{ + msg_fatal("got signal %d after %d triggers %d wakeups", + sig, trig_count, wakeup_count); +} + +static void read_event(int unused_event, char *context) +{ + int fd = (int) context; + char ch; + + wakeup_count++; + + if (read(fd, &ch, 1) != 1) + msg_fatal("read %s: %m", TEST_FIFO); +} + +int main(int unused_argc, char **unused_argv) +{ + int listen_fd; + + listen_fd = fifo_listen(TEST_FIFO, 0600, NON_BLOCKING); + msg_cleanup(cleanup); + event_enable_read(listen_fd, read_event, (char *) listen_fd); + signal(SIGINT, handler); + signal(SIGALRM, handler); + for (;;) { + alarm(10); + if (fifo_trigger(TEST_FIFO, "", 1, 0) < 0) + msg_fatal("trigger %s: %m", TEST_FIFO); + trig_count++; + if (fifo_trigger(TEST_FIFO, "", 1, 0) < 0) + msg_fatal("trigger %s: %m", TEST_FIFO); + trig_count++; + if (fifo_trigger(TEST_FIFO, "", 1, 0) < 0) + msg_fatal("trigger %s: %m", TEST_FIFO); + trig_count++; + event_loop(-1); + event_loop(-1); + event_loop(-1); + } +} + +#endif diff --git a/postfix/util/file_limit.c b/postfix/util/file_limit.c new file mode 100644 index 000000000..a45c0a26d --- /dev/null +++ b/postfix/util/file_limit.c @@ -0,0 +1,92 @@ +/*++ +/* NAME +/* file_limit 3 +/* SUMMARY +/* limit the file size +/* SYNOPSIS +/* #include +/* +/* off_t get_file_limit() +/* +/* void set_file_limit(limit) +/* off_t limit; +/* DESCRIPTION +/* This module manipulates the process-wide file size limit. +/* The limit is specified in bytes. +/* +/* get_file_limit() looks up the process-wide file size limit. +/* +/* set_file_limit() sets the process-wide file size limit to +/* \fIlimit\fR. +/* DIAGNOSTICS +/* All errors are fatal. +/* SEE ALSO +/* setrlimit(2) +/* ulimit(2) +/* 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 +#ifdef USE_ULIMIT +#include +#else +#include +#include +#include +#endif + +/* Utility library. */ + +#include +#include + +#define ULIMIT_BLOCK_SIZE 512 + +/* get_file_limit - get process-wide file size limit */ + +off_t get_file_limit(void) +{ +#ifdef USE_ULIMIT + off_t limit; + + if ((limit = ulimit(UL_GETFSIZE, 0)) < 0) + msg_fatal("ulimit: %m"); + return (limit * ULIMIT_BLOCK_SIZE); +#else + struct rlimit rlim; + + if (getrlimit(RLIMIT_FSIZE, &rlim) < 0) + msg_fatal("getrlimit: %m"); + return (rlim.rlim_cur); +#endif /* USE_ULIMIT */ +} + +/* set_file_limit - process-wide file size limit */ + +void set_file_limit(off_t limit) +{ +#ifdef USE_ULIMIT + if (ulimit(UL_SETFSIZE, limit / ULIMIT_BLOCK_SIZE) < 0) + msg_fatal("ulimit: %m"); +#else + struct rlimit rlim; + + rlim.rlim_cur = rlim.rlim_max = limit; + if (setrlimit(RLIMIT_FSIZE, &rlim) < 0) + msg_fatal("setrlimit: %m"); +#ifdef SIGXFSZ + if (signal(SIGXFSZ, SIG_IGN) == SIG_ERR) + msg_fatal("signal(SIGXFSZ,SIG_IGN): %m"); +#endif +#endif /* USE_ULIMIT */ +} diff --git a/postfix/util/find_inet.c b/postfix/util/find_inet.c new file mode 100644 index 000000000..c97fd12b6 --- /dev/null +++ b/postfix/util/find_inet.c @@ -0,0 +1,92 @@ +/*++ +/* NAME +/* find_inet 3 +/* SUMMARY +/* inet-domain name services +/* SYNOPSIS +/* #include +/* +/* unsigned find_inet_addr(host) +/* const char *host; +/* +/* int find_inet_port(port, proto) +/* const char *port; +/* const char *proto; +/* DESCRIPTION +/* These functions translate network address information from +/* between printable form to the internal the form used by the +/* BSD TCP/IP network software. +/* +/* find_inet_addr() translates a symbolic or numerical hostname. +/* +/* find_inet_port() translates a symbolic or numerical port name. +/* BUGS +/* find_inet_addr() ignores all but the first address listed for +/* a symbolic hostname. +/* DIAGNOSTICS +/* Lookup and conversion errors are fatal. +/* 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 libraries. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "msg.h" +#include "find_inet.h" + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +/* find_inet_addr - translate numerical or symbolic host name */ + +unsigned find_inet_addr(const char *host) +{ + struct in_addr addr; + struct hostent *hp; + + addr.s_addr = inet_addr(host); + if ((addr.s_addr == INADDR_NONE) || (addr.s_addr == 0)) { + if ((hp = gethostbyname(host)) == 0) + msg_fatal("host not found: %s", host); + if (hp->h_addrtype != AF_INET) + msg_fatal("unexpected address family: %d", hp->h_addrtype); + if (hp->h_length != sizeof(addr)) + msg_fatal("unexpected address length %d", hp->h_length); + memcpy((char *) &addr, hp->h_addr, hp->h_length); + } + return (addr.s_addr); +} + +/* find_inet_port - translate numerical or symbolic service name */ + +int find_inet_port(const char *service, const char *protocol) +{ + struct servent *sp; + int port; + + if ((port = atoi(service)) != 0) { + return (htons(port)); + } else { + if ((sp = getservbyname(service, protocol)) == 0) + msg_fatal("unknown service: %s/%s", service, protocol); + return (sp->s_port); + } +} diff --git a/postfix/util/find_inet.h b/postfix/util/find_inet.h new file mode 100644 index 000000000..0e5ce7271 --- /dev/null +++ b/postfix/util/find_inet.h @@ -0,0 +1,33 @@ +#ifndef _FIND_INET_H_INCLUDED_ +#define _FIND_INET_H_INCLUDED_ + +/*++ +/* NAME +/* find_inet 3h +/* SUMMARY +/* inet-domain name services +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern unsigned find_inet_addr(const char *); +extern int find_inet_port(const char *, const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* LAST MODIFICATION +/* Thu Feb 6 12:46:36 EST 1997 +/*--*/ + +#endif diff --git a/postfix/util/fsspace.c b/postfix/util/fsspace.c new file mode 100644 index 000000000..491cdb59e --- /dev/null +++ b/postfix/util/fsspace.c @@ -0,0 +1,116 @@ +/*++ +/* NAME +/* fsspace 3 +/* SUMMARY +/* determine available file system space +/* SYNOPSIS +/* #include +/* +/* struct fsspace { +/* .in +4 +/* unsigned long block_size; +/* unsigned long block_free; +/* .in -4 +/* }; +/* +/* void fsspace(path, sp) +/* const char *path; +/* struct fsspace *sp; +/* DESCRIPTION +/* fsspace() returns the amount of available space in the file +/* system specified in \fIpath\fR, in terms of the block size and +/* of the number of available blocks. +/* DIAGNOSTICS +/* All errors are fatal. +/* BUGS +/* Use caution when doing computations with the result from fsspace(). +/* It is easy to cause overflow (by multiplying large numbers) or to +/* cause underflow (by subtracting unsigned numbers). +/* 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 + +#if defined(STATFS_IN_SYS_MOUNT_H) +#include +#include +#elif defined(STATFS_IN_SYS_VFS_H) +#include +#elif defined(STATVFS_IN_SYS_STATVFS_H) +#include +#else +#ifdef USE_STATFS +#error "please specify the include file with `struct statfs'" +#else +#error "please specify the include file with `struct statvfs'" +#endif +#endif + +/* Utility library. */ + +#include +#include + +/* fsspace - find amount of available file system space */ + +void fsspace(const char *path, struct fsspace * sp) +{ + char *myname = "fsspace"; + +#ifdef USE_STATFS + struct statfs fsbuf; + + if (statfs(path, &fsbuf) < 0) + msg_fatal("statfs %s: %m", path); + sp->block_size = fsbuf.f_bsize; + sp->block_free = fsbuf.f_bavail; +#endif +#ifdef USE_STATVFS + struct statvfs fsbuf; + + if (statvfs(path, &fsbuf) < 0) + msg_fatal("statvfs %s: %m", path); + sp->block_size = fsbuf.f_frsize; + sp->block_free = fsbuf.f_bavail; +#endif + if (msg_verbose) + msg_info("%s: %s: block size %lu, blocks free %lu", + myname, path, sp->block_size, sp->block_free); +} + +#ifdef TEST + + /* + * Proof-of-concept test program: print free space unit and count for all + * listed file systems. + */ + +#include + +int main(int argc, char **argv) +{ + struct fsspace sp; + + if (argc == 1) + msg_fatal("usage: %s filesystem...", argv[0]); + + while (--argc && *++argv) { + fsspace(*argv, &sp); + vstream_printf("%10s: block size %lu, blocks free %lu\n", + *argv, sp.block_size, sp.block_free); + vstream_fflush(VSTREAM_OUT); + } + return (0); +} + +#endif diff --git a/postfix/util/fsspace.h b/postfix/util/fsspace.h new file mode 100644 index 000000000..c118e50cf --- /dev/null +++ b/postfix/util/fsspace.h @@ -0,0 +1,33 @@ +#ifndef _FSSPACE_H_INCLUDED_ +#define _FSSPACE_H_INCLUDED_ + +/*++ +/* NAME +/* fsspace 3h +/* SUMMARY +/* determine available file system space +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ +struct fsspace { + unsigned long block_size; /* block size */ + unsigned long block_free; /* free space */ +}; + +extern void fsspace(const char *, struct fsspace *); + +/* 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/fullname.c b/postfix/util/fullname.c new file mode 100644 index 000000000..6a2868360 --- /dev/null +++ b/postfix/util/fullname.c @@ -0,0 +1,110 @@ +/*++ +/* NAME +/* fullname 3 +/* SUMMARY +/* lookup personal name of invoking user +/* SYNOPSIS +/* #include +/* +/* const char *fullname() +/* DESCRIPTION +/* fullname() looks up the personal name of the invoking user. +/* The result is volatile. Make a copy if it is to be used for +/* an appreciable amount of time. +/* +/* On UNIX systems, fullname() first tries to use the NAME environment +/* variable, provided that the environment can be trusted. +/* If that fails, fullname() extracts the username from the GECOS +/* field of the user's password-file entry, replacing any occurrence +/* of "&" by the login name, first letter capitalized. +/* +/* A null result means that no full name information was found. +/* SEE ALSO +/* safe_getenv(3) safe getenv() interface +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include "vstring.h" +#include "safe.h" +#include "fullname.h" + +/* fullname - get name of user */ + +const char *fullname(void) +{ + static VSTRING *result; + char *cp; + int ch; + uid_t uid; + struct passwd *pwd; + + if (result == 0) + result = vstring_alloc(10); + + /* + * Try the environment. + */ + if ((cp = safe_getenv("NAME")) != 0) + return (vstring_str(vstring_strcpy(result, cp))); + + /* + * Try the password file database. + */ + uid = getuid(); + if ((pwd = getpwuid(uid)) == 0) + return (0); + + /* + * Replace all `&' characters by the login name of this user, first + * letter capitalized. Although the full name comes from the protected + * password file, the actual data is specified by the user so we should + * not trust its sanity. + */ + VSTRING_RESET(result); + for (cp = pwd->pw_gecos; (ch = *(unsigned char *) cp) != 0; cp++) { + if (ch == ',' || ch == ';' || ch == '%') + break; + if (ch == '&') { + if (pwd->pw_name[0]) { + VSTRING_ADDCH(result, TOUPPER(pwd->pw_name[0])); + vstring_strcat(result, pwd->pw_name + 1); + } + } else { + VSTRING_ADDCH(result, ch); + } + } + VSTRING_TERMINATE(result); + return (vstring_str(result)); +} + +#ifdef TEST + +#include + +int main(int unused_argc, char **unused_argv) +{ + const char *cp = fullname(); + + printf("%s\n", cp ? cp : "null!"); +} + +#endif diff --git a/postfix/util/fullname.h b/postfix/util/fullname.h new file mode 100644 index 000000000..f24492ae7 --- /dev/null +++ b/postfix/util/fullname.h @@ -0,0 +1,29 @@ +#ifndef _FULLNAME_H_INCLUDED_ +#define _FULLNAME_H_INCLUDED_ + +/*++ +/* NAME +/* fullname 3h +/* SUMMARY +/* lookup personal name of invoking user +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern const char *fullname(void); + +/* 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/get_domainname.c b/postfix/util/get_domainname.c new file mode 100644 index 000000000..0897f529e --- /dev/null +++ b/postfix/util/get_domainname.c @@ -0,0 +1,66 @@ +/*++ +/* NAME +/* get_domainname 3 +/* SUMMARY +/* network domain name lookup +/* SYNOPSIS +/* #include +/* +/* const char *get_domainname() +/* DESCRIPTION +/* get_domainname() returns the local domain name as obtained +/* by stripping the hostname component from the result from +/* get_hostname(). The result is the hostname when get_hostname() +/* does not return a FQDN form ("foo"), or its result has only two +/* components ("foo.com"). +/* DIAGNOSTICS +/* Fatal errors: no hostname, invalid hostname. +/* SEE ALSO +/* get_hostname(3) +/* 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 "mymalloc.h" +#include "get_hostname.h" +#include "get_domainname.h" + +/* Local stuff. */ + +static char *my_domain_name; + +/* get_domainname - look up my domain name */ + +const char *get_domainname(void) +{ + const char *host; + const char *dot; + + /* + * Use the hostname when it is not a FQDN ("foo"), or when the hostname + * actually is a domain name ("foo.com"). + */ + if (my_domain_name == 0) { + host = get_hostname(); + if ((dot = strchr(host, '.')) == 0 || strchr(dot + 1, '.') == 0) { + my_domain_name = mystrdup(host); + } else { + my_domain_name = mystrdup(dot + 1); + } + } + return (my_domain_name); +} diff --git a/postfix/util/get_domainname.h b/postfix/util/get_domainname.h new file mode 100644 index 000000000..177e3fdbd --- /dev/null +++ b/postfix/util/get_domainname.h @@ -0,0 +1,29 @@ +#ifndef _GET_DOMAINNAME_H_INCLUDED_ +#define _GET_DOMAINNAME_H_INCLUDED_ + +/*++ +/* NAME +/* get_domainname 3h +/* SUMMARY +/* network domain name lookup +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface */ + +extern const char *get_domainname(void); + +/* 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/get_hostname.c b/postfix/util/get_hostname.c new file mode 100644 index 000000000..dacb818fe --- /dev/null +++ b/postfix/util/get_hostname.c @@ -0,0 +1,74 @@ +/*++ +/* NAME +/* get_hostname 3 +/* SUMMARY +/* network name lookup +/* SYNOPSIS +/* #include +/* +/* const char *get_hostname() +/* DESCRIPTION +/* get_hostname() returns the local hostname as obtained +/* via gethostname() or its moral equivalent. This routine +/* goes to great length to avoid dependencies on any network +/* services. +/* DIAGNOSTICS +/* Fatal errors: no hostname, invalid hostname. +/* SEE ALSO +/* valid_hostname(3) +/* 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 + +#if (MAXHOSTNAMELEN < 256) +#undef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + +/* Utility library. */ + +#include "mymalloc.h" +#include "msg.h" +#include "valid_hostname.h" +#include "get_hostname.h" + +/* Local stuff. */ + +static char *my_host_name; + +/* get_hostname - look up my host name */ + +const char *get_hostname(void) +{ + char namebuf[MAXHOSTNAMELEN + 1]; + + /* + * The gethostname() call is not (or not yet) in ANSI or POSIX, but it is + * part of the socket interface library. We avoid the more politically- + * correct uname() routine because that has no portable way of dealing + * with long (FQDN) hostnames. + */ + if (my_host_name == 0) { + if (gethostname(namebuf, sizeof(namebuf)) < 0) + msg_fatal("gethostname: %m"); + namebuf[MAXHOSTNAMELEN] = 0; + if (valid_hostname(namebuf) == 0) + msg_fatal("unable to use my own hostname"); + my_host_name = mystrdup(namebuf); + } + return (my_host_name); +} diff --git a/postfix/util/get_hostname.h b/postfix/util/get_hostname.h new file mode 100644 index 000000000..32d2ab2fa --- /dev/null +++ b/postfix/util/get_hostname.h @@ -0,0 +1,29 @@ +#ifndef _GET_HOSTNAME_H_INCLUDED_ +#define _GET_HOSTNAME_H_INCLUDED_ + +/*++ +/* NAME +/* get_hostname 3h +/* SUMMARY +/* network name lookup +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface */ + +extern const char *get_hostname(void); + +/* 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/htable.c b/postfix/util/htable.c new file mode 100644 index 000000000..2e67d9187 --- /dev/null +++ b/postfix/util/htable.c @@ -0,0 +1,331 @@ +/*++ +/* NAME +/* htable 3 +/* SUMMARY +/* hash table manager +/* SYNOPSIS +/* #include +/* +/* typedef struct { +/* .in +4 +/* char *key; +/* char *value; +/* /* private fields... */ +/* .in -4 +/* } HTABLE_INFO; +/* +/* HTABLE *htable_create(size) +/* int size; +/* +/* HTABLE_INFO *htable_enter(table, key, value) +/* HTABLE *table; +/* const char *key; +/* char *value; +/* +/* char *htable_find(table, key) +/* HTABLE *table; +/* const char *key; +/* +/* HTABLE_INFO *htable_locate(table, key) +/* HTABLE *table; +/* const char *key; +/* +/* void htable_delete(table, key, free_fn) +/* HTABLE *table; +/* const char *key; +/* void (*free_fn)(char *); +/* +/* void htable_free(table, free_fn) +/* HTABLE *table; +/* void (*free_fn)(char *); +/* +/* void htable_walk(table, action) +/* HTABLE *table; +/* void (*action)(HTABLE_INFO *); +/* +/* HTABLE_INFO *htable_list(table) +/* HTABLE *table; +/* DESCRIPTION +/* This module maintains one or more hash tables. Each table entry +/* consists of a unique string-valued lookup key and a generic +/* character-pointer value. +/* The tables are automatically resized when they fill up. When the +/* values to be remembered are not character pointers, proper casts +/* should be used or the code will not be portable. +/* +/* htable_create() creates a table of the specified size and returns a +/* pointer to the result. The lookup keys are saved with mystrdup(). +/* htable_enter() stores a (key, value) pair into the specified table +/* and returns a pointer to the resulting entry. The code does not +/* check if an entry with that key already exists: use htable_locate() +/* for updating an existing entry. +/* +/* htable_find() returns the value that was stored under the given key, +/* or a null pointer if it was not found. In order to distinguish +/* a null value from a non-existent value, use htable_locate(). +/* +/* htable_locate() returns a pointer to the entry that was stored +/* for the given key, or a null pointer if it was not found. +/* +/* htable_delete() removes one entry that was stored under the given key. +/* If the free_fn argument is not a null pointer, the corresponding +/* function is called with as argument the value that was stored under +/* the key. +/* +/* htable_free() destroys a hash table, including contents. If the free_fn +/* argument is not a null pointer, the corresponding function is called +/* for each table entry, with as argument the value that was stored +/* with the entry. +/* +/* htable_walk() invokes the action function for each table entry, with +/* a pointer to the entry as its argument. +/* +/* htable_list() returns a null-terminated list of pointers to +/* all elements in the named table. The list should be passed to +/* myfree(). +/* RESTRICTIONS +/* A callback function should not modify the hash table that is +/* specified to its caller. +/* DIAGNOSTICS +/* The following conditions are reported and cause the program to +/* terminate immediately: memory allocation failure; an attempt +/* to delete a non-existent entry. +/* SEE ALSO +/* mymalloc(3) memory management wrapper +/* 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 +/*--*/ + +/* C library */ + +#include +#include + +/* Local stuff */ + +#include "mymalloc.h" +#include "msg.h" +#include "htable.h" + +/* htable_hash - hash a string */ + +static unsigned htable_hash(const char *s, unsigned size) +{ + unsigned long h = 0; + unsigned long g; + + /* + * From the "Dragon" book by Aho, Sethi and Ullman. + */ + + while (*s) { + h = (h << 4) + *s++; + if ((g = (h & 0xf0000000)) != 0) { + h ^= (g >> 24); + h ^= g; + } + } + return (h % size); +} + +/* htable_link - insert element into table */ + +#define htable_link(table, element) { \ + HTABLE_INFO **h = table->data + htable_hash(element->key, table->size);\ + element->prev = 0; \ + if ((element->next = *h) != 0) \ + (*h)->prev = element; \ + *h = element; \ + table->used++; \ +} + +/* htable_size - allocate and initialize hash table */ + +static void htable_size(HTABLE *table, unsigned size) +{ + HTABLE_INFO **h; + + size |= 1; + + table->data = h = (HTABLE_INFO **) mymalloc(size * sizeof(HTABLE_INFO *)); + table->size = size; + table->used = 0; + + while (size-- > 0) + *h++ = 0; +} + +/* htable_create - create initial hash table */ + +HTABLE *htable_create(int size) +{ + HTABLE *table; + + table = (HTABLE *) mymalloc(sizeof(HTABLE)); + htable_size(table, size < 13 ? 13 : size); + return (table); +} + +/* htable_grow - extend existing table */ + +static void htable_grow(HTABLE *table) +{ + HTABLE_INFO *ht; + HTABLE_INFO *next; + unsigned old_size = table->size; + HTABLE_INFO **h = table->data; + HTABLE_INFO **old_entries = h; + + htable_size(table, 2 * old_size); + + while (old_size-- > 0) { + for (ht = *h++; ht; ht = next) { + next = ht->next; + htable_link(table, ht); + } + } + myfree((char *) old_entries); +} + +/* htable_enter - enter (key, value) pair */ + +HTABLE_INFO *htable_enter(HTABLE *table, const char *key, char *value) +{ + HTABLE_INFO *ht; + + if (table->used >= table->size) + htable_grow(table); + ht = (HTABLE_INFO *) mymalloc(sizeof(HTABLE_INFO)); + ht->key = mystrdup(key); + ht->value = value; + htable_link(table, ht); + return (ht); +} + +/* htable_find - lookup value */ + +char *htable_find(HTABLE *table, const char *key) +{ + HTABLE_INFO *ht; + +#define STREQ(x,y) (x == y || (x[0] == y[0] && strcmp(x,y) == 0)) + + if (table) + for (ht = table->data[htable_hash(key, table->size)]; ht; ht = ht->next) + if (STREQ(key, ht->key)) + return (ht->value); + return (0); +} + +/* htable_locate - lookup entry */ + +HTABLE_INFO *htable_locate(HTABLE *table, const char *key) +{ + HTABLE_INFO *ht; + +#define STREQ(x,y) (x == y || (x[0] == y[0] && strcmp(x,y) == 0)) + + if (table) + for (ht = table->data[htable_hash(key, table->size)]; ht; ht = ht->next) + if (STREQ(key, ht->key)) + return (ht); + return (0); +} + +/* htable_delete - delete one entry */ + +void htable_delete(HTABLE *table, const char *key, void (*free_fn) (char *)) +{ + if (table) { + HTABLE_INFO *ht; + HTABLE_INFO **h = table->data + htable_hash(key, table->size); + +#define STREQ(x,y) (x == y || (x[0] == y[0] && strcmp(x,y) == 0)) + + for (ht = *h; ht; ht = ht->next) { + if (STREQ(key, ht->key)) { + if (ht->next) + ht->next->prev = ht->prev; + if (ht->prev) + ht->prev->next = ht->next; + else + *h = ht->next; + table->used--; + myfree(ht->key); + if (free_fn) + (*free_fn) (ht->value); + myfree((char *) ht); + return; + } + } + msg_panic("htable_delete: unknown_key: \"%s\"", key); + } +} + +/* htable_free - destroy hash table */ + +void htable_free(HTABLE *table, void (*free_fn) (char *)) +{ + if (table) { + unsigned i = table->size; + HTABLE_INFO *ht; + HTABLE_INFO *next; + HTABLE_INFO **h = table->data; + + while (i-- > 0) { + for (ht = *h++; ht; ht = next) { + next = ht->next; + myfree(ht->key); + if (free_fn) + (*free_fn) (ht->value); + myfree((char *) ht); + } + } + myfree((char *) table->data); + table->data = 0; + myfree((char *) table); + } +} + +/* htable_walk - iterate over hash table */ + +void htable_walk(HTABLE *table, void (*action) (HTABLE_INFO *)) +{ + if (table) { + unsigned i = table->size; + HTABLE_INFO **h = table->data; + HTABLE_INFO *ht; + + while (i-- > 0) + for (ht = *h++; ht; ht = ht->next) + (*action) (ht); + } +} + +/* htable_list - list all table members */ + +HTABLE_INFO **htable_list(HTABLE *table) +{ + HTABLE_INFO **list; + HTABLE_INFO *member; + int count = 0; + int i; + + if (table != 0) { + list = (HTABLE_INFO **) mymalloc(sizeof(*list) * (table->used + 1)); + for (i = 0; i < table->size; i++) + for (member = table->data[i]; member != 0; member = member->next) + list[count++] = member; + } else { + list = (HTABLE_INFO **) mymalloc(sizeof(*list)); + } + list[count] = 0; + return (list); +} diff --git a/postfix/util/htable.h b/postfix/util/htable.h new file mode 100644 index 000000000..8a9bbfd3b --- /dev/null +++ b/postfix/util/htable.h @@ -0,0 +1,57 @@ +#ifndef _HTABLE_H_INCLUDED_ +#define _HTABLE_H_INCLUDED_ + +/*++ +/* NAME +/* htable 3h +/* SUMMARY +/* hash table manager +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* Structure of one hash table entry. */ + +typedef struct HTABLE_INFO { + char *key; /* lookup key */ + char *value; /* associated value */ + struct HTABLE_INFO *next; /* colliding entry */ + struct HTABLE_INFO *prev; /* colliding entry */ +} HTABLE_INFO; + + /* Structure of one hash table. */ + +typedef struct HTABLE { + int size; /* length of entries array */ + int used; /* number of entries in table */ + HTABLE_INFO **data; /* entries array, auto-resized */ +} HTABLE; + +extern HTABLE *htable_create(int); +extern HTABLE_INFO *htable_enter(HTABLE *, const char *, char *); +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 HTABLE_INFO **htable_list(HTABLE *); + +/* 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 +/* CREATION DATE +/* Fri Feb 14 13:43:19 EST 1997 +/* LAST MODIFICATION +/* %E% %U% +/* VERSION/RELEASE +/* %I% +/*--*/ + +#endif diff --git a/postfix/util/inet_addr_host.c b/postfix/util/inet_addr_host.c new file mode 100644 index 000000000..b06689f1f --- /dev/null +++ b/postfix/util/inet_addr_host.c @@ -0,0 +1,99 @@ +/*++ +/* NAME +/* inet_addr_host 3 +/* SUMMARY +/* determine all host internet interface addresses +/* SYNOPSIS +/* #include +/* +/* int inet_addr_host(addr_list, hostname) +/* INET_ADDR_LIST *addr_list; +/* const char *hostname; +/* DESCRIPTION +/* inet_addr_host() determines all interface addresses of the +/* named host. The host may be specified as a symbolic name, +/* or as a numerical address. Address results are appended to +/* the specified address list. The result value is the number +/* of addresses appended to the list. +/* DIAGNOSTICS +/* Fatal errors: out of memory. +/* BUGS +/* This code uses the name service, so it talks to the network, +/* and that may not be desirable. +/* SEE ALSO +/* inet_addr_list(3) address list management +/* 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 + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +/* Utility library. */ + +#include +#include + +/* inet_addr_host - look up address list for host */ + +int inet_addr_host(INET_ADDR_LIST *addr_list, const char *hostname) +{ + struct hostent *hp; + struct in_addr addr; + int initial_count = addr_list->used; + + if ((addr.s_addr = inet_addr(hostname)) != INADDR_NONE) { + inet_addr_list_append(addr_list, &addr); + } else { + if ((hp = gethostbyname(hostname)) != 0) + while (hp->h_addr_list[0]) + inet_addr_list_append(addr_list, + (struct in_addr *) * hp->h_addr_list++); + } + return (addr_list->used - initial_count); +} + +#ifdef TEST + +#include +#include +#include + +int main(int argc, char **argv) +{ + INET_ADDR_LIST addr_list; + int i; + + msg_vstream_init(argv[0], VSTREAM_ERR); + + if (argc < 2) + msg_fatal("usage: %s hostname...", argv[0]); + + while (--argc && *++argv) { + inet_addr_list_init(&addr_list); + if (inet_addr_host(&addr_list, *argv) == 0) + msg_fatal("not found: %s", *argv); + + for (i = 0; i < addr_list.used; i++) + vstream_printf("%s\n", inet_ntoa(addr_list.addrs[i])); + vstream_fflush(VSTREAM_OUT); + } + inet_addr_list_free(&addr_list); +} + +#endif diff --git a/postfix/util/inet_addr_host.h b/postfix/util/inet_addr_host.h new file mode 100644 index 000000000..39d150a5a --- /dev/null +++ b/postfix/util/inet_addr_host.h @@ -0,0 +1,35 @@ +#ifndef INET_ADDR_HOST_H_INCLUDED_ +#define INET_ADDR_HOST_H_INCLUDED_ + +/*++ +/* NAME +/* inet_addr_host 3h +/* SUMMARY +/* determine all host internet interface addresses +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern int inet_addr_host(INET_ADDR_LIST *, const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/util/inet_addr_list.c b/postfix/util/inet_addr_list.c new file mode 100644 index 000000000..d449e76c7 --- /dev/null +++ b/postfix/util/inet_addr_list.c @@ -0,0 +1,85 @@ +/*++ +/* NAME +/* inet_addr_list 3 +/* SUMMARY +/* internet address list manager +/* SYNOPSIS +/* #include +/* +/* void inet_addr_list_init(list) +/* INET_ADDR_LIST *list; +/* +/* void inet_addr_list_append(list,addr) +/* INET_ADDR_LIST *list; +/* struct in_addr *addr; +/* +/* void inet_addr_list_free(list) +/* INET_ADDR_LIST *list; +/* DESCRIPTION +/* This module maintains simple lists of internet addresses. +/* +/* inet_addr_list_init() initializes a user-provided structure +/* so that it can be used by inet_addr_list_append() and by +/* inet_addr_list_free(). +/* +/* inet_addr_list_append() appends the specified address to +/* the specified list, extending the list on the fly. +/* +/* inet_addr_list_free() reclaims memory used for the +/* specified address list. +/* 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 + +/* Utility library. */ + +#include +#include +#include + +/* inet_addr_list_init - initialize internet address list */ + +void inet_addr_list_init(INET_ADDR_LIST *list) +{ + list->used = 0; + list->size = 2; + list->addrs = (struct in_addr *) + mymalloc(sizeof(*list->addrs) * list->size); +} + +/* inet_addr_list_append - append address to internet address list */ + +void inet_addr_list_append(INET_ADDR_LIST *list, struct in_addr * addr) +{ + char *myname = "inet_addr_list_append"; + + if (msg_verbose > 1) + msg_info("%s: %s", myname, inet_ntoa(*addr)); + + if (list->used >= list->size) + list->size *= 2; + list->addrs = (struct in_addr *) + myrealloc((char *) list->addrs, + sizeof(*list->addrs) * list->size); + list->addrs[list->used++] = *addr; +} + +/* inet_addr_list_free - destroy internet address list */ + +void inet_addr_list_free(INET_ADDR_LIST *list) +{ + myfree((char *) list->addrs); +} diff --git a/postfix/util/inet_addr_list.h b/postfix/util/inet_addr_list.h new file mode 100644 index 000000000..48a4c9712 --- /dev/null +++ b/postfix/util/inet_addr_list.h @@ -0,0 +1,43 @@ +#ifndef _INET_ADDR_LIST_H_INCLUDED_ +#define _INET_ADDR_LIST_H_INCLUDED_ + +/*++ +/* NAME +/* inet_addr_list 3h +/* SUMMARY +/* internet address list manager +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * External interface. + */ +typedef struct INET_ADDR_LIST { + int used; /* nr of elements in use */ + int size; /* actual list size */ + struct in_addr *addrs; /* payload */ +} INET_ADDR_LIST; + +extern void inet_addr_list_init(INET_ADDR_LIST *); +extern void inet_addr_list_free(INET_ADDR_LIST *); +extern void inet_addr_list_append(INET_ADDR_LIST *, struct in_addr *); + +/* 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/inet_addr_local.c b/postfix/util/inet_addr_local.c new file mode 100644 index 000000000..dbe02f9a5 --- /dev/null +++ b/postfix/util/inet_addr_local.c @@ -0,0 +1,155 @@ +/*++ +/* NAME +/* inet_addr_local 3 +/* SUMMARY +/* determine if IP address is local +/* SYNOPSIS +/* #include +/* +/* int inet_addr_local(list) +/* INET_ADDR_LIST *list; +/* DESCRIPTION +/* inet_addr_local() determines all active interface addresses +/* of the local system. Any address found is appended to the +/* specified address list. The result value is the number of +/* active interfaces found. +/* DIAGNOSTICS +/* Fatal errors: out of memory. +/* SEE ALSO +/* inet_addr_list(3) address list management +/* 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 +#include +#ifdef USE_SYS_SOCKIO_H +#include +#endif +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* inet_addr_local - find all IP addresses for this host */ + +int inet_addr_local(INET_ADDR_LIST *addr_list) +{ + char *myname = "inet_addr_local"; + struct ifconf ifc; + struct ifreq ifreq; + struct ifreq *ifr; + struct ifreq *the_end; + int sock; + VSTRING *buf = vstring_alloc(1024); + int initial_count = addr_list->used; + + if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) + msg_fatal("%s: socket: %m", myname); + + /* + * Get the network interface list. XXX The socket API appears to have no + * function that returns the number of network interfaces, so we have to + * guess how much space is needed to store the result. + * + * On BSD-derived systems, ioctl SIOCGIFCONF returns as much information as + * possible, leaving it up to the application to repeat the request with + * a larger buffer if the result caused a tight fit. + * + * Other systems, such as Solaris 2.5, generate an EINVAL error when the + * buffer is too small for the entire result. Workaround: ignore EINVAL + * errors and repeat the request with a larger buffer. The downside is + * that the program can run out of memory due to a non-memory problem, + * making it more difficult than necessary to diagnose the real problem. + */ + for (;;) { + ifc.ifc_len = vstring_avail(buf); + ifc.ifc_buf = vstring_str(buf); + if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) { + if (errno != EINVAL) + msg_fatal("%s: ioctl SIOCGIFCONF: %m", myname); + } else if (ifc.ifc_len < vstring_avail(buf) / 2) + break; + VSTRING_SPACE(buf, vstring_avail(buf) * 2); + } + + /* + * Get the IP address of each active IP network interface. + */ + the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); + for (ifr = ifc.ifc_req; ifr < the_end;) { + if (ifr->ifr_addr.sa_family == AF_INET) { /* IP interface */ + ifreq = *ifr; + if (ioctl(sock, SIOCGIFFLAGS, (char *) &ifreq) < 0) + msg_fatal("%s: ioctl SIOCGIFFLAGS: %m", myname); + if (ifreq.ifr_flags & IFF_UP) { /* active interface */ + if (ioctl(sock, SIOCGIFADDR, (char *) &ifreq) < 0) + msg_fatal("%s: ioctl SIOCGIFADDR: %m", myname); + inet_addr_list_append(addr_list, + &(((struct sockaddr_in *) & ifreq.ifr_addr)->sin_addr)); + } + } + + /* + * Support for variable-length addresses. + */ +#ifdef HAS_SA_LEN + ifr = (struct ifreq *) + ((char *) ifr + sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len); +#else + ifr++; +#endif + } + vstring_free(buf); + (void) close(sock); + return (addr_list->used - initial_count); +} + +#ifdef TEST + +#include +#include + +int main(int unused_argc, char **argv) +{ + INET_ADDR_LIST addr_list; + int i; + + msg_vstream_init(argv[0], VSTREAM_ERR); + + inet_addr_list_init(&addr_list); + inet_addr_local(&addr_list); + + if (addr_list.used == 0) + msg_fatal("cannot find any active network interfaces"); + + if (addr_list.used == 1) + msg_warn("found only one active network interface"); + + for (i = 0; i < addr_list.used; i++) + vstream_printf("%s\n", inet_ntoa(addr_list.addrs[i])); + vstream_fflush(VSTREAM_OUT); + inet_addr_list_free(&addr_list); +} + +#endif diff --git a/postfix/util/inet_addr_local.h b/postfix/util/inet_addr_local.h new file mode 100644 index 000000000..f48ca1eb0 --- /dev/null +++ b/postfix/util/inet_addr_local.h @@ -0,0 +1,35 @@ +#ifndef _INET_ADDR_LOCAL_H_INCLUDED_ +#define _INET_ADDR_LOCAL_H_INCLUDED_ + +/*++ +/* NAME +/* inet_addr_local 3h +/* SUMMARY +/* determine if IP address is local +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern int inet_addr_local(INET_ADDR_LIST *); + +/* 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/inet_connect.c b/postfix/util/inet_connect.c new file mode 100644 index 000000000..152e49846 --- /dev/null +++ b/postfix/util/inet_connect.c @@ -0,0 +1,124 @@ +/*++ +/* NAME +/* inet_connect 3 +/* SUMMARY +/* connect to INET-domain listener +/* SYNOPSIS +/* #include +/* +/* int inet_connect(addr, block_mode, timeout) +/* const char *addr; +/* int block_mode; +/* int timeout; +/* DESCRIPTION +/* inet_connect connects to a listener in the AF_INET domain at +/* the specified address, and returns the resulting file descriptor. +/* +/* Arguments: +/* .IP addr +/* The destination to connect to. The format is host:port. If no +/* host is specified, a port on the local host is assumed. +/* Host and port information may be given in numerical form +/* or as symbolical names. +/* .IP block_mode +/* Either NON_BLOCKING for a non-blocking socket, or BLOCKING for +/* blocking mode. +/* .IP timeout +/* Bounds the number of seconds that the operation may take. Specify +/* a value <= 0 to disable the time limit. +/* DIAGNOSTICS +/* The result is -1 when the connection could not be made. +/* Th nature of the error is available via the global \fIerrno\fR +/* variable. +/* Fatal errors: other system call failures. +/* BUGS +/* This routine uses find_inet_addr() which ignores all but the +/* first address listed for the named host. +/* SEE ALSO +/* find_inet(3), simple inet name service interface +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System interfaces. */ + +#include +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include "mymalloc.h" +#include "msg.h" +#include "find_inet.h" +#include "inet_util.h" +#include "iostuff.h" +#include "connect.h" +#include "timed_connect.h" + +/* inet_connect - connect to AF_INET-domain listener */ + +int inet_connect(const char *addr, int block_mode, int timeout) +{ + char *buf; + char *host; + char *port; + struct sockaddr_in sin; + int sock; + + /* + * Translate address information to internal form. No host defaults to + * the local host. + */ + buf = inet_parse(addr, &host, &port); + if (*host == 0) + host = "localhost"; + memset((char *) &sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = find_inet_addr(host); + sin.sin_port = find_inet_port(port, "tcp"); + myfree(buf); + + /* + * Create a client socket. + */ + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + msg_fatal("socket: %m"); + + /* + * Timed connect. + */ + if (timeout > 0) { + non_blocking(sock, NON_BLOCKING); + if (timed_connect(sock, (struct sockaddr *) & sin, sizeof(sin), timeout) < 0) { + close(sock); + return (-1); + } + if (block_mode != NON_BLOCKING) + non_blocking(sock, block_mode); + return (sock); + } + + /* + * Maybe block until connected. + */ + else { + non_blocking(sock, block_mode); + if (connect(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0 + && errno != EINPROGRESS) { + close(sock); + return (-1); + } + return (sock); + } +} diff --git a/postfix/util/inet_listen.c b/postfix/util/inet_listen.c new file mode 100644 index 000000000..81e3d2a12 --- /dev/null +++ b/postfix/util/inet_listen.c @@ -0,0 +1,104 @@ +/*++ +/* NAME +/* inet_listen 3 +/* SUMMARY +/* start INET-domain listener +/* SYNOPSIS +/* #include +/* +/* int inet_listen(addr, backlog, block_mode) +/* const char *addr; +/* int backlog; +/* int block_mode; +/* DESCRIPTION +/* The \fBinet_listen\fR routine starts a listener in the INET domain +/* on the specified address, with the specified backlog, and returns +/* the resulting file descriptor. +/* +/* Arguments: +/* .IP addr +/* The communication endpoint to listen on. The syntax is "host:port". +/* Host and port may be specified in symbolic form or numerically. +/* A null host field means listen on all network interfaces. +/* .IP backlog +/* This argument is passed on to the \fIlisten(2)\fR routine. +/* .IP block_mode +/* Either NON_BLOCKING for a non-blocking socket, or BLOCKING for +/* blocking mode. +/* DIAGNOSTICS +/* Fatal errors: all errors are fatal. +/* 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 libraries. */ + +#include +#include +#include +#include +#include +#ifndef MAXHOSTNAMELEN +#include +#endif +#include +#include + +/* Utility library. */ + +#include "mymalloc.h" +#include "msg.h" +#include "find_inet.h" +#include "inet_util.h" +#include "iostuff.h" +#include "listen.h" + +/* Application-specific stuff. */ + +#ifndef INADDR_ANY +#define INADDR_ANY 0xffffffff +#endif + +/* inet_listen - create inet-domain listener */ + +int inet_listen(const char *addr, int backlog, int block_mode) +{ + struct sockaddr_in sin; + int sock; + int t = 1; + char *buf; + char *host; + char *port; + + /* + * Translate address information to internal form. + */ + buf = inet_parse(addr, &host, &port); + memset((char *) &sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = find_inet_port(port, "tcp"); + sin.sin_addr.s_addr = (*host ? find_inet_addr(host) : INADDR_ANY); + myfree(buf); + + /* + * Create a listener socket. + */ + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + msg_fatal("socket: %m"); + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &t, sizeof(t)) < 0) + msg_fatal("setsockopt: %m"); + if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) + msg_fatal("bind %s port %d: %m", sin.sin_addr.s_addr == INADDR_ANY ? + "INADDR_ANY" : inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + non_blocking(sock, block_mode); + if (listen(sock, backlog) < 0) + msg_fatal("listen: %m"); + return (sock); +} diff --git a/postfix/util/inet_trigger.c b/postfix/util/inet_trigger.c new file mode 100644 index 000000000..e92c2d8db --- /dev/null +++ b/postfix/util/inet_trigger.c @@ -0,0 +1,91 @@ +/*++ +/* NAME +/* inet_trigger 3 +/* SUMMARY +/* wakeup INET-domain server +/* SYNOPSIS +/* #include +/* +/* int inet_trigger(service, buf, len, timeout) +/* char *service; +/* const char *buf; +/* int len; +/* int timeout; +/* DESCRIPTION +/* inet_trigger() wakes up the named INET-domain server by making +/* a brief connection to it and by writing the contents of the +/* named buffer. +/* +/* Arguments: +/* .IP service +/* Name of the communication endpoint. +/* .IP buf +/* Address of data to be written. +/* .IP len +/* Amount of data to be written. +/* .IP timeout +/* Deadline in seconds. Specify a value <= 0 to disable +/* the time limit. +/* DIAGNOSTICS +/* The result is zero in case of success, -1 in case of problems. +/* BUGS +/* SEE ALSO +/* inet_connect(3), INET-domain client +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include + +/* inet_trigger - wakeup INET-domain server */ + +int inet_trigger(const char *service, const char *buf, int len, int timeout) +{ + char *myname = "inet_trigger"; + int fd; + + if (msg_verbose > 1) + msg_info("%s: service %s", myname, service); + + /* + * Connect... + */ + if ((fd = inet_connect(service, BLOCKING, timeout)) < 0) { + if (msg_verbose) + msg_warn("%s: connect to %s: %m", myname, service); + return (-1); + } + + /* + * Write the request... + */ + if (write_buf(fd, buf, len, timeout) < 0) + if (msg_verbose) + msg_warn("%s: write to %s: %m", myname, service); + + /* + * Disconnect. + */ + if (close(fd) < 0) + if (msg_verbose) + msg_warn("%s: close %s: %m", myname, service); + return (0); +} diff --git a/postfix/util/inet_util.c b/postfix/util/inet_util.c new file mode 100644 index 000000000..47a41a28f --- /dev/null +++ b/postfix/util/inet_util.c @@ -0,0 +1,61 @@ +/*++ +/* NAME +/* inet_util 3 +/* SUMMARY +/* INET-domain utilities +/* SYNOPSIS +/* #include +/* +/* char *inet_parse(addr, hostp, portp) +/* const char *addr; +/* char **hostp; +/* char **portp; +/* DESCRIPTION +/* This module implements various support routines for +/* dealing with AF_INET connections, addresses etc. +/* +/* inet_parse() takes an address of the form host:port and +/* breaks it up into its constituent parts. The resulting +/* host information is an empty string when the address +/* contains no host part or no host: part. inet_parse() +/* returns a pointer to memory that it has allocated for +/* string storage. The caller should pass the host to the +/* myfree() function when the storage is no longer needed. +/* DIAGNOSTICS +/* Fatal errors: invalid address or host forms. +/* 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 libraries. */ + +#include + +/* Utility library. */ + +#include "mymalloc.h" +#include "split_at.h" +#include "inet_util.h" + +/* inet_parse - parse host:port address spec */ + +char *inet_parse(const char *addr, char **hostp, char **portp) +{ + char *buf; + + buf = mystrdup(addr); + if ((*portp = split_at_right(buf, ':')) != 0) { + *hostp = buf; + } else { + *portp = buf; + *hostp = ""; + } + return (buf); +} diff --git a/postfix/util/inet_util.h b/postfix/util/inet_util.h new file mode 100644 index 000000000..7eea60279 --- /dev/null +++ b/postfix/util/inet_util.h @@ -0,0 +1,29 @@ +#ifndef _INET_UTIL_H_INCLUDED_ +#define _INET_UTIL_H_INCLUDED_ + +/*++ +/* NAME +/* inet_util 3h +/* SUMMARY +/* INET-domain utilities +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern char *inet_parse(const char *, char **, char **); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/util/iostuff.h b/postfix/util/iostuff.h new file mode 100644 index 000000000..909bffb5e --- /dev/null +++ b/postfix/util/iostuff.h @@ -0,0 +1,48 @@ +#ifndef _IOSTUFF_H_INCLUDED_ +#define _IOSTUFF_H_INCLUDED_ + +/*++ +/* NAME +/* iostuff 3h +/* SUMMARY +/* miscellaneous I/O primitives +/* SYNOPSIS +/* #include +/* DESCRIPTION + + /* + * External interface. + */ +extern int non_blocking(int, int); +extern int close_on_exec(int, int); +extern int open_limit(int); +extern int readable(int); +extern int writable(int); +extern off_t get_file_limit(void); +extern void set_file_limit(off_t); +extern int peekfd(int); +extern int read_wait(int, int); +extern int write_wait(int, int); +extern int write_buf(int, const char *, int, int); +extern void doze(unsigned); + +#define BLOCKING 0 +#define NON_BLOCKING 1 + +#define CLOSE_ON_EXEC 1 +#define PASS_ON_EXEC 0 + +/* 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 +/* CREATION DATE +/* Sat Jan 25 16:54:13 EST 1997 +/*--*/ + +#endif diff --git a/postfix/util/line_wrap.c b/postfix/util/line_wrap.c new file mode 100644 index 000000000..50d172cab --- /dev/null +++ b/postfix/util/line_wrap.c @@ -0,0 +1,122 @@ +/*++ +/* NAME +/* line_wrap 3 +/* SUMMARY +/* wrap long lines upon output +/* SYNOPSIS +/* #include +/* +/* void line_wrap(string, len, indent, output_fn, context) +/* const char *buf; +/* int len; +/* int indent; +/* void (*output_fn)(const char *str, int len, int indent, char *context); +/* char *context; +/* DESCRIPTION +/* The \fBline_wrap\fR routine outputs the specified string via +/* the specified output function, and attempts to keep output lines +/* shorter than the specified length. The routine does not attempt to +/* break long words that do not fit on a single line. Upon output, +/* trailing whitespace is stripped. +/* +/* Arguments +/* .IP string +/* The input, which cannot contain any newline characters. +/* .IP len +/* The desired maximal output line length. +/* .IP indent +/* The desired amount of indentation of the second etc. output lines +/* with respect to the first output line. A negative indent causes +/* only the first line to be indented; a positive indent causes all +/* but the first line to be indented. A zero count causes no indentation. +/* .IP output_fn +/* The output function that is called with as arguments a string +/* pointer, a string length, a non-negative indentation count, and +/* application context. A typical implementation looks like this: +/* .sp +/* .nf +/* .na +void print(const char *str, int len, int indent, char *context) +{ + VSTREAM *fp = (VSTREAM *) context; + + vstream_fprintf(fp, "%*s%.*s", indent, "", len, str); +} +/* .fi +/* .ad +/* .IP context +/* Application context that is passed on to the output function. +/* For example, a VSTREAM pointer, or a structure that contains +/* a VSTREAM pointer. +/* BUGS +/* No tab expansion and no backspace processing. +/* 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 + +/* Utility library. */ + +#include + +/* line_wrap - wrap long lines upon output */ + +void line_wrap(const char *str, int len, int indent, LINE_WRAP_FN output_fn, + char *context) +{ + const char *start_line; + const char *word; + const char *next_word; + const char *next_space; + int line_len; + int curr_len; + int curr_indent; + + if (indent < 0) { + curr_indent = -indent; + curr_len = len + indent; + } else { + curr_indent = 0; + curr_len = len; + } + + /* + * At strategic positions, output what we have seen, after stripping off + * trailing blanks. + */ + for (start_line = word = str; word != 0; word = next_word) { + next_space = word + strcspn(word, " \t"); + if (word > start_line) { + if (next_space - start_line > curr_len) { + line_len = word - start_line; + while (line_len > 0 && ISSPACE(start_line[line_len - 1])) + line_len--; + output_fn(start_line, line_len, curr_indent, context); + while (*word && ISSPACE(*word)) + word++; + if (start_line == str) { + curr_indent += indent; + curr_len -= indent; + } + start_line = word; + } + } + next_word = *next_space ? next_space + 1 : 0; + } + line_len = strlen(start_line); + while (line_len > 0 && ISSPACE(start_line[line_len - 1])) + line_len--; + output_fn(start_line, line_len, curr_indent, context); +} diff --git a/postfix/util/line_wrap.h b/postfix/util/line_wrap.h new file mode 100644 index 000000000..b901c1f3d --- /dev/null +++ b/postfix/util/line_wrap.h @@ -0,0 +1,31 @@ +#ifndef _LINE_WRAP_H_INCLUDED_ +#define _LINE_WRAP_H_INCLUDED_ + +/*++ +/* NAME +/* line_wrap 3h +/* SUMMARY +/* wrap long lines upon output +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +typedef void (*LINE_WRAP_FN) (const char *, int, int, char *); +extern void line_wrap(const char *, int, int, LINE_WRAP_FN, char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/util/listen.h b/postfix/util/listen.h new file mode 100644 index 000000000..7cd873e0d --- /dev/null +++ b/postfix/util/listen.h @@ -0,0 +1,37 @@ +#ifndef _LISTEN_H_INCLUDED_ +#define _LISTEN_H_INCLUDED_ + +/*++ +/* NAME +/* listen 3h +/* SUMMARY +/* listener interface file +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * Listener external interface. + */ +extern int unix_listen(const char *, int, int); +extern int inet_listen(const char *, int, int); +extern int fifo_listen(const char *, int, 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/lowercase.c b/postfix/util/lowercase.c new file mode 100644 index 000000000..f7388b42e --- /dev/null +++ b/postfix/util/lowercase.c @@ -0,0 +1,43 @@ +/*++ +/* NAME +/* lowercase 3 +/* SUMMARY +/* map uppercase characters to lowercase +/* SYNOPSIS +/* #include +/* +/* char *lowercase(buf) +/* char *buf; +/* DESCRIPTION +/* lowercase() replaces uppercase characters in its null-terminated +/* input by their lowercase equivalent. +/* 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 + +/* Utility library. */ + +#include "stringops.h" + +char *lowercase(char *string) +{ + char *cp; + int ch; + + for (cp = string; (ch = *cp) != 0; cp++) + if (ISUPPER(ch)) + *cp = TOLOWER(ch); + return (string); +} diff --git a/postfix/util/lstat_as.c b/postfix/util/lstat_as.c new file mode 100644 index 000000000..ffb796e8c --- /dev/null +++ b/postfix/util/lstat_as.c @@ -0,0 +1,72 @@ +/*++ +/* NAME +/* lstat_as 3 +/* SUMMARY +/* lstat file as user +/* SYNOPSIS +/* #include +/* #include +/* +/* int lstat_as(path, st, euid, egid) +/* const char *path; +/* struct stat *st; +/* uid_t euid; +/* gid_t egid; +/* DESCRIPTION +/* lstat_as() looks up the file status of the named \fIpath\fR, +/* using the effective rights specified by \fIeuid\fR +/* and \fIegid\fR, and stores the result into the structure pointed +/* to by \fIst\fR. A -1 result means the lookup failed. +/* This call does not follow symbolic links. +/* DIAGNOSTICS +/* Fatal error: no permission to change privilege level. +/* SEE ALSO +/* set_eugid(3) switch effective rights +/* 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 + +/* Utility library. */ + +#include "msg.h" +#include "set_eugid.h" +#include "lstat_as.h" + +/* lstat_as - lstat file as user */ + +int lstat_as(const char *path, struct stat * st, uid_t euid, gid_t egid) +{ + uid_t saved_euid = geteuid(); + gid_t saved_egid = getegid(); + int status; + + /* + * Switch to the target user privileges. + */ + set_eugid(euid, egid); + + /* + * Lstat that file. + */ + status = lstat(path, st); + + /* + * Restore saved privileges. + */ + set_eugid(saved_euid, saved_egid); + + return (status); +} diff --git a/postfix/util/lstat_as.h b/postfix/util/lstat_as.h new file mode 100644 index 000000000..824b32115 --- /dev/null +++ b/postfix/util/lstat_as.h @@ -0,0 +1,30 @@ +#ifndef _LSTAT_AS_H_INCLUDED_ +#define _LSTAT_AS_H_INCLUDED_ + +/*++ +/* NAME +/* lstat_as 3h +/* SUMMARY +/* lstat file as user +/* SYNOPSIS +/* #include +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern int lstat_as(const char *, struct stat *, uid_t, gid_t); + +/* 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/mac_parse.c b/postfix/util/mac_parse.c new file mode 100644 index 000000000..54fc6be5a --- /dev/null +++ b/postfix/util/mac_parse.c @@ -0,0 +1,158 @@ +/*++ +/* NAME +/* mac_parse 3 +/* SUMMARY +/* locate macro references in string +/* SYNOPSIS +/* #include +/* +/* void mac_parse(string, action, context) +/* const char *string; +/* void (*action)(int type, VSTRING *buf, char *context); +/* DESCRIPTION +/* This module recognizes macro references in null-terminated +/* strings. Macro references have the form $name, $(name) or +/* ${name}. A macro name consists of alphanumerics and/or +/* underscore. Other text is treated as literal text. +/* +/* mac_parse() breaks up its string argument into macro references +/* and other text, and invokes the \fIaction\fR routine for each item +/* found. With each action routine call, the \fItype\fR argument +/* indicates what was found, \fIbuf\fR contains a copy of the text +/* found, and \fIcontext\fR is passed on unmodified from the caller. +/* The application is at liberty to clobber \fIbuf\fR. +/* .IP MAC_PARSE_LITERAL +/* The text in \fIbuf\fR is literal text. +/* .IP MAC_PARSE_VARNAME +/* The text in \fIbuf\fR is a macro name. +/* SEE ALSO +/* dict(3) dictionary interface. +/* DIAGNOSTICS +/* Fatal errors: out of memory, malformed macro name. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include + + /* + * Helper macro for consistency. Null-terminate the temporary buffer, + * execute the action, and reset the temporary buffer for re-use. + */ +#define MAC_PARSE_ACTION(type, buf, context) \ + { \ + VSTRING_TERMINATE(buf); \ + action(type, buf, context); \ + VSTRING_RESET(buf); \ + } + +/* mac_parse - split string into literal text and macro references */ + +void mac_parse(const char *value, MAC_PARSE_FN action, char *context) +{ + char *myname = "mac_parse"; + VSTRING *buf = vstring_alloc(1); /* result buffer */ + const char *vp; /* value pointer */ + const char *pp; /* open_paren pointer */ + const char *ep; /* string end pointer */ + static char open_paren[] = "({"; + static char close_paren[] = ")}"; + +#define SKIP(start, var, cond) \ + for (var = start; *var && (cond); var++); + + if (msg_verbose > 1) + msg_info("%s: %s", myname, value); + + for (vp = value; *vp;) { + if (*vp != '$') { /* ordinary character */ + VSTRING_ADDCH(buf, *vp); + vp += 1; + } else if (vp[1] == '$') { /* $$ becomes $ */ + VSTRING_ADDCH(buf, *vp); + vp += 2; + } else { /* found bare $ */ + if (VSTRING_LEN(buf) > 0) + MAC_PARSE_ACTION(MAC_PARSE_LITERAL, buf, context); + vp += 1; + pp = open_paren; + if (*vp == *pp || *vp == *++pp) { /* ${x} or $(x) */ + vp += 1; + SKIP(vp, ep, *ep != close_paren[pp - open_paren]); + if (*ep == 0) + msg_fatal("incomplete macro: %s", value); + vstring_strncat(buf, vp, ep - vp); + vp = ep + 1; + } else { /* plain $x */ + SKIP(vp, ep, ISALNUM(*ep) || *ep == '_'); + vstring_strncat(buf, vp, ep - vp); + vp = ep; + } + if (VSTRING_LEN(buf) == 0) + msg_fatal("empty macro name: %s", value); + MAC_PARSE_ACTION(MAC_PARSE_VARNAME, buf, context); + } + } + if (VSTRING_LEN(buf) > 0) + MAC_PARSE_ACTION(MAC_PARSE_LITERAL, buf, context); + + /* + * Cleanup. + */ + vstring_free(buf); +} + +#ifdef TEST + + /* + * Proof-of-concept test program. Read strings from stdin, print parsed + * result to stdout. + */ +#include + +/* mac_parse_print - print parse tree */ + +static void mac_parse_print(int type, VSTRING *buf, char *unused_context) +{ + char *type_name; + + switch (type) { + case MAC_PARSE_VARNAME: + type_name = "MAC_PARSE_VARNAME"; + break; + case MAC_PARSE_LITERAL: + type_name = "MAC_PARSE_LITERAL"; + break; + default: + msg_panic("unknown token type %d", type); + } + vstream_printf("%s \"%s\"\n", type_name, vstring_str(buf)); +} + +int main(int unused_argc, char **unused_argv) +{ + VSTRING *buf = vstring_alloc(1); + + while (vstring_fgets_nonl(buf, VSTREAM_IN)) { + mac_parse(vstring_str(buf), mac_parse_print, (char *) 0); + vstream_fflush(VSTREAM_OUT); + } + vstring_free(buf); +} + +#endif diff --git a/postfix/util/mac_parse.h b/postfix/util/mac_parse.h new file mode 100644 index 000000000..856fb9830 --- /dev/null +++ b/postfix/util/mac_parse.h @@ -0,0 +1,40 @@ +#ifndef _MAC_PARSE_H_INCLUDED_ +#define _MAC_PARSE_H_INCLUDED_ + +/*++ +/* NAME +/* mac_parse 3h +/* SUMMARY +/* locate macro references in string +/* SYNOPSIS +/* #include + DESCRIPTION + .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +#define MAC_PARSE_LITERAL 1 +#define MAC_PARSE_VARNAME 2 + +typedef void (*MAC_PARSE_FN)(int, VSTRING *, char *); + +extern void mac_parse(const char *, MAC_PARSE_FN, char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/util/make_dirs.c b/postfix/util/make_dirs.c new file mode 100644 index 000000000..ebf8fd019 --- /dev/null +++ b/postfix/util/make_dirs.c @@ -0,0 +1,111 @@ +/*++ +/* NAME +/* make_dirs 3 +/* SUMMARY +/* create directory hierarchy +/* SYNOPSIS +/* #include +/* +/* int make_dirs(path, perms) +/* const char *path; +/* int perms; +/* DESCRIPTION +/* make_dirs() creates the directory specified in \fIpath\fR, and +/* creates any missing intermediate directories as well. Directories +/* are created with the permissions specified in \fIperms\fR, as +/* modified by the process umask. +/* DIAGNOSTICS: +/* Fatal: out of memory. make_dirs() returns 0 in case of success. +/* In case of problems. make_dirs() returns -1 and \fIerrno\fR +/* reflects the nature of the problem. +/* SEE ALSO +/* mkdir(2) +/* 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 + +/* Utility library. */ + +#include "msg.h" +#include "mymalloc.h" +#include "stringops.h" +#include "make_dirs.h" + +/* make_dirs - create directory hierarchy */ + +int make_dirs(const char *path, int perms) +{ + char *saved_path; + unsigned char *cp; + int saved_ch; + struct stat st; + int ret; + + /* + * Initialize. Make a copy of the path that we can safely clobber. + */ + cp = (unsigned char *) (saved_path = mystrdup(path)); + + /* + * I didn't like the 4.4BSD "mkdir -p" implementation, but coming up with + * my own took a day, spread out over several days. + */ +#define SKIP_WHILE(cond, ptr) { while(*ptr && (cond)) ptr++; } + + SKIP_WHILE(*cp == '/', cp); + + for (;;) { + SKIP_WHILE(*cp != '/', cp); + if ((saved_ch = *cp) != 0) + *cp = 0; + if ((ret = stat(saved_path, &st)) < 0) + if (errno != ENOENT || (ret = mkdir(saved_path, perms)) < 0) + break; + if (saved_ch != 0) + *cp = saved_ch; + SKIP_WHILE(*cp == '/', cp); + if (*cp == 0) + break; + } + + /* + * Cleanup. + */ + myfree(saved_path); + return (ret); +} + +#ifdef TEST + + /* + * Test program. Usage: make_dirs path... + */ +#include +#include + +main(int argc, char **argv) +{ + msg_vstream_init(argv[0], VSTREAM_ERR); + if (argc < 2) + msg_fatal("usage: %s path...", argv[0]); + while (--argc > 0 && *++argv != 0) + if (make_dirs(*argv, 0755)) + msg_fatal("%s: %m", *argv); + exit(0); +} + +#endif diff --git a/postfix/util/make_dirs.h b/postfix/util/make_dirs.h new file mode 100644 index 000000000..0df6117d8 --- /dev/null +++ b/postfix/util/make_dirs.h @@ -0,0 +1,30 @@ +#ifndef MAKE_DIRS_H_INCLUDED_ +#define MAKE_DIRS_H_INCLUDED_ + +/*++ +/* NAME +/* make_dirs 3h +/* SUMMARY +/* create directory hierarchy +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern int make_dirs(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/match_list.c b/postfix/util/match_list.c new file mode 100644 index 000000000..1ced1d806 --- /dev/null +++ b/postfix/util/match_list.c @@ -0,0 +1,190 @@ +/*++ +/* NAME +/* match_list 3 +/* SUMMARY +/* generic list-based pattern matching +/* SYNOPSIS +/* #include +/* +/* MATCH_LIST *match_list_init(pattern_list, count, func,...) +/* const char *pattern_list; +/* int count; +/* int (*func)(const char *string, const char *pattern); +/* +/* int match_list_match(list, string,...) +/* MATCH_LIST *list; +/* const char *string; +/* +/* void match_list_free(list) +/* MATCH_LIST *list; +/* DESCRIPTION +/* This module implements a framework for tests for list membership. +/* The actual tests are done by user-supplied functions. +/* +/* Patterns are separated by whitespace and/or commas. A pattern +/* is either a string, a file name (in which case the contents +/* of the file are substituted for the file name) or a type:name +/* lookup table specification. In order to reverse the result of +/* a pattern match, precede a non-file name pattern with an +/* exclamation point (!). +/* +/* match_list_init() performs initializations. The first argument is +/* a list of patterns. The second argument specifies how many match +/* functions follow. +/* +/* match_list_match() matches strings against the specified pattern +/* list, passing the first string to the first function given to +/* match_list_init(), the second string to the second function, and +/* so on. +/* +/* match_list_free() releases storage allocated by match_list_init(). +/* DIAGNOSTICS +/* Fatal error: unable to open or read a match_list file; invalid +/* match_list pattern. +/* SEE ALSO +/* host_match(3) match hosts by name or by address +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific */ + +struct MATCH_LIST { + ARGV *patterns; /* one pattern each */ + int match_count; /* match function/argument count */ + MATCH_LIST_FN *match_func; /* match functions */ + const char **match_args; /* match arguments */ +}; + +/* match_list_parse - parse buffer, destroy buffer */ + +static ARGV *match_list_parse(ARGV *list, char *string) +{ + char *myname = "match_list_parse"; + VSTRING *buf = 0; + VSTREAM *fp; + char *delim = " ,\t\r\n"; + char *bp = string; + char *pattern; + char *cp; + + while ((pattern = mystrtok(&bp, delim)) != 0) { + if (*pattern == '/') { /* /file/name */ + if (buf == 0) + buf = vstring_alloc(10); + if ((fp = vstream_fopen(pattern, O_RDONLY, 0)) == 0) + msg_fatal("%s: open file %s: %m", myname, pattern); + while (vstring_fgets(buf, fp)) + list = match_list_parse(list, vstring_str(buf)); + if (vstream_fclose(fp)) + msg_fatal("%s: read file %s: %m", myname, pattern); + } else if (strchr(pattern, ':') != 0) { /* type:table */ + for (cp = pattern; *cp == '!'; cp++) + /* void */ ; + dict_register(pattern, dict_open(pattern, 0)); + argv_add(list, pattern, (char *) 0); + } else { /* other pattern */ + argv_add(list, pattern, (char *) 0); + } + } + if (buf) + vstring_free(buf); + return (list); +} + +/* match_list_init - initialize pattern list */ + +MATCH_LIST *match_list_init(const char *patterns, int match_count,...) +{ + MATCH_LIST *list; + char *saved_patterns; + va_list ap; + int i; + + list = (MATCH_LIST *) mymalloc(sizeof(*list)); + list->match_count = match_count; + list->match_func = + (MATCH_LIST_FN *) mymalloc(match_count * sizeof(MATCH_LIST_FN)); + list->match_args = + (const char **) mymalloc(match_count * sizeof(const char *)); + va_start(ap, match_count); + for (i = 0; i < match_count; i++) + list->match_func[i] = va_arg(ap, MATCH_LIST_FN); + va_end(ap); + + saved_patterns = mystrdup(patterns); + list->patterns = match_list_parse(argv_alloc(1), saved_patterns); + argv_terminate(list->patterns); + myfree(saved_patterns); + return (list); +} + +/* match_list_match - match strings against pattern list */ + +int match_list_match(MATCH_LIST * list,...) +{ + char *myname = "match_list_match"; + char **cpp; + char *pat; + int match; + int i; + va_list ap; + + /* + * Iterate over all patterns in the list, stop at the first match. + */ + va_start(ap, list); + for (i = 0; i < list->match_count; i++) + list->match_args[i] = va_arg(ap, const char *); + va_end(ap); + + for (cpp = list->patterns->argv; (pat = *cpp) != 0; cpp++) { + for (match = 1; *pat == '!'; pat++) + match = !match; + for (i = 0; i < list->match_count; i++) + if (list->match_func[i] (list->match_args[i], pat) != 0) + return (match); + } + if (msg_verbose) + for (i = 0; i < list->match_count; i++) + msg_info("%s: %s: no match", myname, list->match_args[i]); + return (0); +} + +/* match_list_free - release storage */ + +void match_list_free(MATCH_LIST * list) +{ + argv_free(list->patterns); + myfree((char *) list->match_func); + myfree((char *) list->match_args); + myfree((char *) list); +} diff --git a/postfix/util/match_list.h b/postfix/util/match_list.h new file mode 100644 index 000000000..aba88b8f6 --- /dev/null +++ b/postfix/util/match_list.h @@ -0,0 +1,35 @@ +#ifndef _MATCH_LIST_H_INCLUDED_ +#define _MATCH_LIST_H_INCLUDED_ + +/*++ +/* NAME +/* match_list 3h +/* SUMMARY +/* generic list-based pattern matching +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +typedef struct MATCH_LIST MATCH_LIST; +typedef int (*MATCH_LIST_FN) (const char *, const char *); + +extern MATCH_LIST *match_list_init(const char *, int,...); +extern int match_list_match(MATCH_LIST *,...); +extern void match_list_free(MATCH_LIST *); + +/* 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/match_ops.c b/postfix/util/match_ops.c new file mode 100644 index 000000000..039e90eae --- /dev/null +++ b/postfix/util/match_ops.c @@ -0,0 +1,227 @@ +/*++ +/* NAME +/* match_ops 3 +/* SUMMARY +/* simple string or host pattern matching +/* SYNOPSIS +/* #include +/* +/* int match_string(string, pattern) +/* const char *string; +/* const char *pattern; +/* +/* int match_hostname(name, pattern) +/* const char *name; +/* const char *pattern; +/* +/* int match_hostaddr(addr, pattern) +/* const char *addr; +/* const char *pattern; +/* DESCRIPTION +/* This module implements simple string and host name or address +/* matching. The matching process is case insensitive. If a pattern +/* has the form type:name, table lookup is used instead of string +/* or address comparison. +/* +/* match_string() matches the string against the pattern, requiring +/* an exact (case-insensitive) match. +/* +/* match_hostname() matches the host name when the hostname matches +/* the pattern exactly, or when the pattern matches a parent domain +/* of the named host. +/* +/* match_hostaddr() matches a host address when the pattern is +/* identical to the host address, or when the pattern is a net/mask +/* that contains the address. The mask specifies the number of +/* bits in the network part of the pattern. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffff +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* match_string - match a string literal */ + +int match_string(const char *string, const char *pattern) +{ + char *myname = "match_string"; + int match; + char *key; + + if (msg_verbose) + msg_info("%s: %s ~? %s", myname, string, pattern); + + /* + * Try dictionary lookup: exact match. + */ + if (strchr(pattern, ':') != 0) { + key = lowercase(mystrdup(string)); + match = (dict_lookup(pattern, string) != 0); + myfree(key); + if (match != 0) + return (1); + if (dict_errno != 0) + msg_fatal("%s: table lookup problem", pattern); + return (0); + } + + /* + * Try an exact string match. + */ + if (strcasecmp(string, pattern) == 0) { + return (1); + } + + /* + * No match found. + */ + return (0); +} + +/* match_hostname - match a host by name */ + +int match_hostname(const char *name, const char *pattern) +{ + char *myname = "match_hostname"; + const char *pd; + const char *entry; + char *next; + char *temp; + int match; + + if (msg_verbose) + msg_info("%s: %s ~? %s", myname, name, pattern); + + /* + * Try dictionary lookup: exact match and parent domains. + */ + if (strchr(pattern, ':') != 0) { + temp = lowercase(mystrdup(name)); + match = 0; + for (entry = temp; (next = strchr(entry, '.')) != 0; entry = next + 1) { + if ((match = (dict_lookup(pattern, entry) != 0)) != 0) + break; + if (dict_errno != 0) + msg_fatal("%s: table lookup problem", pattern); + } + myfree(temp); + return (match); + } + + /* + * Try an exact match with the host name. + */ + if (strcasecmp(name, pattern) == 0) { + return (1); + } + + /* + * See if the pattern is a parent domain of the hostname. + */ + else { + pd = name + strlen(name) - strlen(pattern); + if (pd > name && pd[-1] == '.' && strcasecmp(pd, pattern) == 0) + return (1); + } + return (0); +} + +/* match_parse_mask - parse net/mask pattern */ + +static int match_parse_mask(const char *pattern, unsigned long *net_bits, + int *mask_shift) +{ + char *saved_pattern; + char *mask; + +#define BITS_PER_ADDR 32 + + saved_pattern = mystrdup(pattern); + if ((mask = split_at(saved_pattern, '/')) != 0) { + if ((*mask_shift = atoi(mask)) <= 0 || *mask_shift > BITS_PER_ADDR + || (*net_bits = inet_addr(saved_pattern)) == INADDR_NONE) { + msg_fatal("bad net/mask pattern: %s", pattern); + } + } + myfree(saved_pattern); + return (mask != 0); +} + +/* match_hostaddr - match host by address */ + +int match_hostaddr(const char *addr, const char *pattern) +{ + char *myname = "match_hostaddr"; + int mask_shift; + unsigned long mask_bits; + unsigned long net_bits; + unsigned long addr_bits; + + if (msg_verbose) + msg_info("%s: %s ~? %s", myname, addr, pattern); + + if (addr[strspn(addr, "01234567890./:")] != 0) + return (0); + + /* + * Try dictionary lookup. This can be case insensitive. XXX Probably + * should also try again after stripping least significant octets. + */ + if (strchr(pattern, ':') != 0) { + if (dict_lookup(pattern, addr) != 0) + return (1); + if (dict_errno != 0) + msg_fatal("%s: table lookup problem", pattern); + return (0); + } + + /* + * Try an exact match with the host address. + */ + if (strcasecmp(addr, pattern) == 0) { + return (1); + } + + /* + * In a net/mask pattern, the mask is specified as the number of bits of + * the network part. + */ + if (match_parse_mask(pattern, &net_bits, &mask_shift)) { + addr_bits = inet_addr(addr); + if (addr_bits == INADDR_NONE) + msg_fatal("%s: bad address argument: %s", myname, addr); + mask_bits = htonl((0xffffffff) << (BITS_PER_ADDR - mask_shift)); + return ((addr_bits & mask_bits) == (net_bits & mask_bits)); + } + return (0); +} diff --git a/postfix/util/match_ops.h b/postfix/util/match_ops.h new file mode 100644 index 000000000..a938c04fb --- /dev/null +++ b/postfix/util/match_ops.h @@ -0,0 +1,31 @@ +#ifndef _MATCH_OPS_H_INCLUDED_ +#define _MATCH_OPS_H_INCLUDED_ + +/*++ +/* NAME +/* match_ops 3h +/* SUMMARY +/* simple string or host pattern matching +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern int match_string(const char *, const char *); +extern int match_hostname(const char *, const char *); +extern int match_hostaddr(const char *, const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/util/msg.c b/postfix/util/msg.c new file mode 100644 index 000000000..08379a90b --- /dev/null +++ b/postfix/util/msg.c @@ -0,0 +1,207 @@ +/*++ +/* NAME +/* msg 3 +/* SUMMARY +/* diagnostic interface +/* SYNOPSIS +/* #include +/* +/* int msg_verbose; +/* +/* void msg_info(format, ...) +/* const char *format; +/* +/* void msg_warn(format, ...) +/* const char *format; +/* +/* void msg_error(format, ...) +/* const char *format; +/* +/* NORETURN msg_fatal(format, ...) +/* const char *format; +/* +/* NORETURN msg_panic(format, ...) +/* const char *format; +/* +/* MSG_CLEANUP_FN msg_cleanup(cleanup) +/* void (*cleanup)(void); +/* AUXILIARY FUNCTIONS +/* int msg_error_limit(count) +/* int count; +/* +/* void msg_error_clear() +/* DESCRIPTION +/* This module reports diagnostics. By default, diagnostics are sent +/* to the standard error stream, but the disposition can be changed +/* by the user. See the hints below in the SEE ALSO section. +/* +/* msg_info(), msg_warn(), msg_error(), msg_fatal() and msg_panic() +/* produce a one-line record with the program name, a severity code +/* (except for msg_info()), and an informative message. The program +/* name must have been set by calling one of the msg_XXX_init() +/* functions (see the SEE ALSO section). +/* +/* msg_error() reports a recoverable error and increments the error +/* counter. When the error count exceeds a pre-set limit (default: 13) +/* the program terminates by calling msg_fatal(). +/* +/* msg_fatal() reports an unrecoverable error and terminates the program +/* with a non-zero exit status. +/* +/* msg_panic() reports an internal inconsistency, terminates the +/* program immediately (i.e. without calling the optional user-specified +/* cleanup routine), and forces a core dump when possible. +/* +/* msg_cleanup() specifies a function that msg_fatal() should +/* invoke before terminating the program, and returns the +/* current function pointer. Specify a null argument to disable +/* this feature. +/* +/* msg_error_limit() sets the error message count limit, and returns. +/* the old limit. +/* +/* msg_error_clear() sets the error message count to zero. +/* +/* msg_verbose is a global flag that can be set to make software +/* more verbose about what it is doing. By default the flag is zero. +/* By convention, a larger value means more noise. +/* SEE ALSO +/* msg_output(3) specify diagnostics disposition +/* msg_stdio(3) direct diagnostics to standard I/O stream +/* msg_vstream(3) direct diagnostics to VSTREAM. +/* msg_syslog(3) direct diagnostics to syslog daemon +/* BUGS +/* Some output functions may suffer from intentional or accidental +/* record length restrictions that are imposed by library routines +/* and/or by the runtime environment. +/* 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 libraries. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +#include "msg.h" +#include "msg_output.h" + + /* + * Default is verbose logging off. + */ +int msg_verbose = 0; + + /* + * Private state. The msg_exiting flag prevents us from recursively + * reporting an error. + */ +static MSG_CLEANUP_FN msg_cleanup_fn = 0; +static int msg_exiting = 0; +static int msg_error_count = 0; +static int msg_error_bound = 13; + +/* msg_info - report informative message */ + +void msg_info(const char *fmt,...) +{ + va_list ap; + + va_start(ap, fmt); + msg_vprintf(MSG_INFO, fmt, ap); + va_end(ap); +} + +/* msg_warn - report warning message */ + +void msg_warn(const char *fmt,...) +{ + va_list ap; + + va_start(ap, fmt); + msg_vprintf(MSG_WARN, fmt, ap); + va_end(ap); +} + +/* msg_error - report recoverable error */ + +void msg_error(const char *fmt,...) +{ + va_list ap; + + va_start(ap, fmt); + msg_vprintf(MSG_ERROR, fmt, ap); + va_end(ap); + if (++msg_error_count >= msg_error_bound) + msg_fatal("too many errors - program terminated"); +} + +/* msg_fatal - report error and terminate gracefully */ + +NORETURN msg_fatal(const char *fmt,...) +{ + va_list ap; + + if (msg_exiting++ == 0) { + va_start(ap, fmt); + msg_vprintf(MSG_FATAL, fmt, ap); + va_end(ap); + if (msg_cleanup_fn) + msg_cleanup_fn(); + } + sleep(1); + exit(1); +} + +/* msg_panic - report error and dump core */ + +NORETURN msg_panic(const char *fmt,...) +{ + va_list ap; + + if (msg_exiting++ == 0) { + va_start(ap, fmt); + msg_vprintf(MSG_PANIC, fmt, ap); + va_end(ap); + } + sleep(1); + abort(); /* Die! */ + exit(1); /* DIE!! */ +} + +/* msg_cleanup - specify cleanup routine */ + +MSG_CLEANUP_FN msg_cleanup(MSG_CLEANUP_FN cleanup_fn) +{ + MSG_CLEANUP_FN old_fn = msg_cleanup_fn; + + msg_cleanup_fn = cleanup_fn; + return (old_fn); +} + +/* msg_error_limit - set error message counter limit */ + +int msg_error_limit(int limit) +{ + int old = msg_error_bound; + + msg_error_bound = limit; + return (old); +} + +/* msg_error_clear - reset error message counter */ + +void msg_error_clear(void) +{ + msg_error_count = 0; +} diff --git a/postfix/util/msg.h b/postfix/util/msg.h new file mode 100644 index 000000000..2b62dec5e --- /dev/null +++ b/postfix/util/msg.h @@ -0,0 +1,42 @@ +#ifndef _MSG_H_INCLUDED_ +#define _MSG_H_INCLUDED_ + +/*++ +/* NAME +/* msg 3h +/* SUMMARY +/* diagnostics interface +/* SYNOPSIS +/* #include "msg.h" +/* DESCRIPTION +/* .nf + +/* + * External interface. + */ +typedef void (*MSG_CLEANUP_FN) (void); + +extern int msg_verbose; + +extern void msg_info(const char *,...); +extern void msg_warn(const char *,...); +extern void msg_error(const char *,...); +extern NORETURN msg_fatal(const char *,...); +extern NORETURN msg_panic(const char *,...); + +extern int msg_error_limit(int); +extern void msg_error_clear(void); +extern MSG_CLEANUP_FN msg_cleanup(MSG_CLEANUP_FN); + +/* 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/msg_output.c b/postfix/util/msg_output.c new file mode 100644 index 000000000..889206bdb --- /dev/null +++ b/postfix/util/msg_output.c @@ -0,0 +1,133 @@ +/*++ +/* NAME +/* msg_output 3 +/* SUMMARY +/* diagnostics output management +/* SYNOPSIS +/* #include +/* +/* typedef void (*MSG_OUTPUT_FN)(int level, char *text) +/* +/* void msg_output(output_fn) +/* MSG_OUTPUT_FN output_fn; +/* +/* void msg_printf(level, format, ...) +/* int level; +/* const char *format; +/* +/* void msg_vprintf(level, format, ap) +/* int level; +/* const char *format; +/* va_list ap; +/* +/* void msg_text(level, text) +/* int level; +/* const char *text; +/* DESCRIPTION +/* This module implements low-level output management for the +/* msg(3) diagnostics interface. +/* +/* msg_output() registers an output handler for the diagnostics +/* interface. An application can register multiple output handlers. +/* Output handlers are called in the specified order. +/* An output handler takes as arguments a severity level (MSG_INFO, +/* MSG_WARN, MSG_ERROR, MSG_FATAL, MSG_PANIC, monotonically increasing +/* integer values ranging from 0 to MSG_LAST) and pre-formatted, +/* sanitized, text in the form of a null-terminated string. +/* +/* msg_printf() and msg_vprintf() format their arguments, sanitize the +/* result, and call the output handlers registered with msg_output(). +/* +/* msg_text() copies a pre-formatted text, sanitizes the result, and +/* calls the output handlers registered with msg_output(). +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + + /* + * Private state. + */ +static MSG_OUTPUT_FN *msg_output_fn = 0; +static int msg_output_fn_count = 0; +static VSTRING *msg_buffer = 0; + +/* msg_output - specify output handler */ + +void msg_output(MSG_OUTPUT_FN output_fn) +{ + + /* + * We're not doing this often, so avoid complexity and allocate memory + * for an exact fit. + */ + if (msg_output_fn_count == 0) + msg_output_fn = (MSG_OUTPUT_FN *) mymalloc(sizeof(*msg_output_fn)); + else + msg_output_fn = (MSG_OUTPUT_FN *) myrealloc((char *) msg_output_fn, + (msg_output_fn_count + 1) * sizeof(*msg_output_fn)); + msg_output_fn[msg_output_fn_count++] = output_fn; +} + +/* msg_printf - format text and log it */ + +void msg_printf(int level, const char *format,...) +{ + va_list ap; + + va_start(ap, format); + msg_vprintf(level, format, ap); + va_end(ap); +} + +/* msg_vprintf - format text and log it */ + +void msg_vprintf(int level, const char *format, va_list ap) +{ + if (msg_buffer == 0) + msg_buffer = vstring_alloc(100); + vstring_vsprintf(msg_buffer, percentm(format, errno), ap); + msg_text(level, vstring_str(msg_buffer)); +} + +/* msg_text - sanitize and log pre-formatted text */ + +void msg_text(int level, const char *text) +{ + int i; + + /* + * Sanitize the text. Use a private copy if necessary. + */ + if (msg_buffer == 0) + msg_buffer = vstring_alloc(100); + if (text != vstring_str(msg_buffer)) + vstring_strcpy(msg_buffer, text); + printable(vstring_str(msg_buffer), '?'); + if (msg_output_fn_count == 0) + msg_vstream_init("unknown", VSTREAM_ERR); + for (i = 0; i < msg_output_fn_count; i++) + msg_output_fn[i] (level, vstring_str(msg_buffer)); +} diff --git a/postfix/util/msg_output.h b/postfix/util/msg_output.h new file mode 100644 index 000000000..b409e9518 --- /dev/null +++ b/postfix/util/msg_output.h @@ -0,0 +1,47 @@ +#ifndef _MSG_OUTPUT_FN_ +#define _MSG_OUTPUT_FN_ + +/*++ +/* NAME +/* msg_output 3h +/* SUMMARY +/* diagnostics output management +/* SYNOPSIS +/* #include +/* DESCRIPTION + + /* + * System library. + */ +#include + + /* + * External interface. Severity levels are documented to be monotonically + * increasing from 0 up to MSG_LAST. + */ +typedef void (*MSG_OUTPUT_FN) (int, const char *); +extern void msg_output(MSG_OUTPUT_FN); +extern void msg_printf(int, const char *,...); +extern void msg_vprintf(int, const char *, va_list); +extern void msg_text(int, const char *); + +#define MSG_INFO 0 /* informative */ +#define MSG_WARN 1 /* warning (non-fatal) */ +#define MSG_ERROR 2 /* error (fatal) */ +#define MSG_FATAL 3 /* software error (fatal) */ +#define MSG_PANIC 4 /* software error (fatal) */ + +#define MSG_LAST 4 /* highest-numbered severity level */ + +/* 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/msg_syslog.c b/postfix/util/msg_syslog.c new file mode 100644 index 000000000..2ff60ce45 --- /dev/null +++ b/postfix/util/msg_syslog.c @@ -0,0 +1,122 @@ +/*++ +/* NAME +/* msg_syslog 3 +/* SUMMARY +/* direct diagnostics to syslog daemon +/* SYNOPSIS +/* #include +/* +/* void msg_syslog_init(progname, log_opt, facility) +/* const char *progname; +/* int log_opt; +/* int facility; +/* DESCRIPTION +/* This module implements support to report msg(3) diagnostics +/* via the syslog daemon. +/* +/* msg_syslog_init() is a wrapper around the openlog(3) routine +/* that directs subsequent msg(3) output to the syslog daemon. +/* SEE ALSO +/* syslog(3) syslog library +/* msg(3) diagnostics module +/* BUGS +/* Output records are truncated to 2000 characters. This is done in +/* order to defend against a buffer overflow problem in some +/* implementations of the syslog() library routine. +/* 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 libraries. */ + +#include +#include /* 44BSD stdarg.h uses abort() */ +#include +#include +#include + +/* Application-specific. */ + +#include "vstring.h" +#include "stringops.h" +#include "msg.h" +#include "msg_output.h" +#include "msg_syslog.h" + + /* + * Stay a little below the 2048-byte limit of older syslog() implementations. + */ +#define MSG_SYSLOG_RECLEN 2000 + +/* msg_syslog_print - log info to syslog daemon */ + +static void msg_syslog_print(int level, const char *text) +{ + static int log_level[] = { + LOG_INFO, LOG_WARNING, LOG_ERR, LOG_CRIT, LOG_CRIT, + }; + static char *severity_name[] = { + "info", "warning", "error", "fatal", "panic", + }; + + if (level < 0 || level >= (int) (sizeof(log_level) / sizeof(log_level[0]))) + msg_panic("msg_syslog_print: invalid severity level: %d", level); + + if (level == MSG_INFO) { + syslog(log_level[level], "%.*s", MSG_SYSLOG_RECLEN, text); + } else { + syslog(log_level[level], "%s: %.*s", + severity_name[level], MSG_SYSLOG_RECLEN, text); + } +} + +/* msg_syslog_init - initialize */ + +void msg_syslog_init(const char *name, int logopt, int facility) +{ + static int first_call = 1; + + openlog(name, LOG_NDELAY | logopt, facility); + if (first_call) { + first_call = 0; + msg_output(msg_syslog_print); + } +} + +#ifdef TEST + + /* + * Proof-of-concept program to test the syslogging diagnostics interface + * + * Usage: msg_syslog_test text... + */ + +main(int argc, char **argv) +{ + VSTRING *vp = vstring_alloc(256); + + msg_syslog_init(argv[0], LOG_PID, LOG_MAIL); + if (argc < 2) + msg_error("usage: %s text to be logged", argv[0]); + while (--argc && *++argv) { + vstring_strcat(vp, *argv); + if (argv[1]) + vstring_strcat(vp, " "); + } + msg_warn("static text"); + msg_warn("dynamic text: >%s<", vstring_str(vp)); + msg_warn("dynamic numeric: >%d<", 42); + msg_warn("error text: >%m<"); + msg_warn("dynamic: >%s<: error: >%m<", vstring_str(vp)); + vstring_free(vp); + return (0); +} + +#endif diff --git a/postfix/util/msg_syslog.h b/postfix/util/msg_syslog.h new file mode 100644 index 000000000..94e1ea3ff --- /dev/null +++ b/postfix/util/msg_syslog.h @@ -0,0 +1,34 @@ +#ifndef _MSG_SYSLOG_H_INCLUDED_ +#define _MSG_SYSLOG_H_INCLUDED_ + +/*++ +/* NAME +/* msg_syslog 3h +/* SUMMARY +/* direct diagnostics to syslog daemon +/* SYNOPSIS +/* #include +/* DESCRIPTION + + /* + * System library. + */ +#include + + /* + * External interface. + */ +extern void msg_syslog_init(const char *, int, 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/msg_vstream.c b/postfix/util/msg_vstream.c new file mode 100644 index 000000000..154c00438 --- /dev/null +++ b/postfix/util/msg_vstream.c @@ -0,0 +1,87 @@ +/*++ +/* NAME +/* msg_vstream 3 +/* SUMMARY +/* report diagnostics to VSTREAM +/* SYNOPSIS +/* #include +/* +/* void msg_vstream_init(progname, stream) +/* const char *progname; +/* VSTREAM *stream; +/* DESCRIPTION +/* This module implements support to report msg(3) diagnostics +/* to a VSTREAM. +/* +/* msg_vstream_init() sets the program name that appears in each output +/* record, and directs diagnostics (see msg(3)) to the specified +/* VSTREAM. The \fIprogname\fR argument is not copied. +/* SEE ALSO +/* msg(3) +/* BUGS +/* No guarantee that long records are written atomically. +/* Only the last msg_vstream_init() call takes effect. +/* 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 libraries. */ + +#include +#include +#include /* 44BSD stdarg.h uses abort() */ +#include + +/* Utility library. */ + +#include "vstream.h" +#include "msg.h" +#include "msg_output.h" +#include "msg_vstream.h" + + /* + * Private state. + */ +static const char *msg_tag; +static VSTREAM *msg_stream; + +/* msg_vstream_print - log diagnostic to VSTREAM */ + +static void msg_vstream_print(int level, const char *text) +{ + static char *level_text[] = { + "info", "warning", "error", "fatal", "panic", + }; + + if (level < 0 || level >= (int) (sizeof(level_text) / sizeof(level_text[0]))) + msg_panic("invalid severity level: %d", level); + if (level == MSG_INFO) { + vstream_fprintf(msg_stream, "%s: %s\n", + msg_tag, text); + } else { + vstream_fprintf(msg_stream, "%s: %s: %s\n", + msg_tag, level_text[level], text); + } + vstream_fflush(msg_stream); +} + +/* msg_vstream_init - initialize */ + +void msg_vstream_init(const char *name, VSTREAM *vp) +{ + static int first_call = 1; + + msg_tag = name; + msg_stream = vp; + if (first_call) { + first_call = 0; + msg_output(msg_vstream_print); + } +} diff --git a/postfix/util/msg_vstream.h b/postfix/util/msg_vstream.h new file mode 100644 index 000000000..a0dfc7048 --- /dev/null +++ b/postfix/util/msg_vstream.h @@ -0,0 +1,34 @@ +#ifndef _MSG_VSTREAM_H_INCLUDED_ +#define _MSG_VSTREAM_H_INCLUDED_ + +/*++ +/* NAME +/* msg_vstream 3h +/* SUMMARY +/* direct diagnostics to VSTREAM +/* SYNOPSIS +/* #include +/* DESCRIPTION + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern void msg_vstream_init(const char *, VSTREAM *); + +/* 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/mvect.c b/postfix/util/mvect.c new file mode 100644 index 000000000..fa3df03bd --- /dev/null +++ b/postfix/util/mvect.c @@ -0,0 +1,110 @@ +/*++ +/* NAME +/* mvect 3 +/* SUMMARY +/* memory vector management +/* SYNOPSIS +/* #include +/* +/* char *mvect_alloc(vector, elsize, nelm, init_fn, wipe_fn) +/* MVECT *vector; +/* int elsize; +/* int nelm; +/* void (*init_fn)(char *ptr, int count); +/* void (*wipe_fn)(char *ptr, int count); +/* +/* char *mvect_realloc(vector, nelm) +/* MVECT *vector; +/* int nelm; +/* +/* char *mvect_free(vector) +/* MVECT *vector; +/* DESCRIPTION +/* This module supports memory management for arrays of arbitrary +/* objects. It is up to the application to provide specific code +/* that initializes and uses object memory. +/* +/* mvect_alloc() initializes memory for a vector with elements +/* of \fIelsize\fR bytes, and with at least \fInelm\fR elements. +/* \fIinit_fn\fR is a null pointer, or a pointer to a function +/* that initializes \fIcount\fR vector elements. +/* \fIwipe_fn\fR is a null pointer, or a pointer to a function +/* that is complementary to \fIinit_fn\fR. This routine is called +/* by mvect_free(). The result of mvect_alloc() is a pointer to +/* the allocated vector. +/* +/* mvect_realloc() guarantees that the specified vector has space +/* for at least \fInelm\fR elements. The result is a pointer to the +/* allocated vector, which may change across calls. +/* +/* mvect_free() releases storage for the named vector. The result +/* is a convenient null pointer. +/* SEE ALSO +/* mymalloc(3) memory management +/* DIAGNOSTICS +/* Problems are reported via the msg(3) diagnostics routines: +/* the requested amount of memory is not available; improper use +/* is detected; other fatal errors. +/* 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 "mymalloc.h" +#include "mvect.h" + +/* mvect_alloc - allocate memory vector */ + +char *mvect_alloc(MVECT *vect, int elsize, int nelm, + void (*init_fn) (char *, int), void (*wipe_fn) (char *, int)) +{ + vect->nelm = nelm; + vect->elsize = elsize; + vect->init_fn = init_fn; + vect->wipe_fn = wipe_fn; + vect->ptr = mymalloc(vect->elsize * vect->nelm); + if (vect->init_fn) + vect->init_fn(vect->ptr, vect->nelm); + return (vect->ptr); +} + +/* mvect_realloc - adjust memory vector allocation */ + +char *mvect_realloc(MVECT *vect, int nelm) +{ + int old_len = vect->nelm; + int incr = nelm - old_len; + + if (incr > 0) { + if (incr < old_len) + incr = old_len; + vect->nelm += incr; + vect->ptr = myrealloc(vect->ptr, vect->elsize * vect->nelm); + if (vect->init_fn) + vect->init_fn(vect->ptr + old_len * vect->elsize, incr); + } + return (vect->ptr); +} + +/* mvect_free - release memory vector storage */ + +char *mvect_free(MVECT *vect) +{ + if (vect->wipe_fn) + vect->wipe_fn(vect->ptr, vect->nelm); + myfree(vect->ptr); + myfree((char *) vect); + return (0); +} diff --git a/postfix/util/mvect.h b/postfix/util/mvect.h new file mode 100644 index 000000000..23214d547 --- /dev/null +++ b/postfix/util/mvect.h @@ -0,0 +1,42 @@ +#ifndef _MVECT_H_INCLUDED_ +#define _MVECT_H_INCLUDED_ + +/*++ +/* NAME +/* mvect 3h +/* SUMMARY +/* memory vector management +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Generic memory vector interface. + */ +typedef void (*MVECT_FN) (char *, int); + +typedef struct { + char *ptr; + int elsize; + int nelm; + MVECT_FN init_fn; + MVECT_FN wipe_fn; +} MVECT; + +extern char *mvect_alloc(MVECT *, int, int, MVECT_FN, MVECT_FN); +extern char *mvect_realloc(MVECT *, int); +extern char *mvect_free(MVECT *); + +/* 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/myflock.c b/postfix/util/myflock.c new file mode 100644 index 000000000..19a5cabd8 --- /dev/null +++ b/postfix/util/myflock.c @@ -0,0 +1,132 @@ +/*++ +/* NAME +/* myflock 3 +/* SUMMARY +/* lock open file +/* SYNOPSIS +/* #include +/* +/* int myflock(fd, how) +/* int fd; +/* int how; +/* +/* int myflock_locked(err) +/* int err; +/* DESCRIPTION +/* myflock() locks or unlocks an entire open file. Depending +/* on the operating system environment, this function uses the +/* fcntl() or flock() system calls, so the usual caveats apply. +/* +/* In the case of a blocking request, a call that fails due to +/* transient problems is tried again once per second. +/* In the case of a non-blocking request, use the myflock_locked() +/* call to distinguish between expected and unexpected failures. +/* +/* myflock_locked() examines the errno result from a failed +/* non-blocking lock request, and returns non-zero (true) +/* when the lock failed because someone else holds it. +/* +/* Arguments: +/* .IP fd +/* The open file to be locked/unlocked. +/* .IP how +/* One of the following values: +/* .RS +/* .IP MYFLOCK_NONE +/* Releases any locks the process has on the specified open file. +/* .IP MYFLOCK_SHARED +/* Attempts to acquire a shared lock on the specified open file. +/* This is appropriate for read-only access. +/* .IP MYFLOCK_EXCLUSIVE +/* Attempts to acquire an exclusive lock on the specified open +/* file. This is appropriate for write access. +/* .PP +/* In addition, setting the MYFLOCK_NOWAIT bit causes the +/* call to return immediately when the requested lock cannot +/* be acquired. See the myflock_locked() function on how to deal +/* with a negative result. +/* .RE +/* DIAGNOSTICS +/* myflock() returns 0 in case of success, -1 in case of failure. +/* A problem description is returned via the global \fIerrno\fR +/* variable. +/* 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 + +#ifdef USE_FCNTL_LOCK +#include +#include +#endif + +#ifdef USE_FLOCK_LOCK +#include +#endif + +/* Utility library. */ + +#include "msg.h" +#include "myflock.h" + +/* myflock - lock/unlock entire open file */ + +int myflock(int fd, int how) +{ + + /* + * flock() does exactly what we need. Too bad it is not standard. + */ +#ifdef USE_FLOCK_LOCK + static int lock_ops[] = { + LOCK_UN, LOCK_SH, LOCK_EX, -1, + -1, LOCK_SH | LOCK_NB, LOCK_EX | LOCK_NB, -1 + }; + + if ((how & (MYFLOCK_BITS)) != how) + msg_panic("myflock: improper request type: %d", how); + return (flock(fd, lock_ops[how])); +#endif + + /* + * fcntl() is standard and does more than we need, but we can handle it. + */ +#ifdef USE_FCNTL_LOCK + struct flock lock; + int request; + static int lock_ops[] = { + F_UNLCK, F_RDLCK, F_WRLCK + }; + int ret; + + if ((how & (MYFLOCK_BITS)) != how) + msg_panic("myflock: improper request type: %d", how); + memset((char *) &lock, 0, sizeof(lock)); + lock.l_type = lock_ops[how & ~MYFLOCK_NOWAIT]; + request = (how & MYFLOCK_NOWAIT) ? F_SETLK : F_SETLKW; + while ((ret = fcntl(fd, request, &lock)) < 0 + && request == F_SETLKW + && (errno == EINTR || errno == ENOLCK || errno == EDEADLK)) + sleep(1); + return (ret); +#endif +} + +/* myflock_locked - were we locked out or what? */ + +int myflock_locked(int err) +{ + return (err == EAGAIN || err == EWOULDBLOCK || err == EACCES); +} diff --git a/postfix/util/myflock.h b/postfix/util/myflock.h new file mode 100644 index 000000000..7d43efeae --- /dev/null +++ b/postfix/util/myflock.h @@ -0,0 +1,38 @@ +#ifndef _MYFLOCK_H_INCLUDED_ +#define _MYFLOCK_H_INCLUDED_ + +/*++ +/* NAME +/* myflock 3h +/* SUMMARY +/* lock open file +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern int myflock(int, int); +extern int myflock_locked(int); + +#define MYFLOCK_NONE 0 +#define MYFLOCK_SHARED 1 +#define MYFLOCK_EXCLUSIVE 2 +#define MYFLOCK_LOCK_MASK (MYFLOCK_SHARED | MYFLOCK_EXCLUSIVE) +#define MYFLOCK_NOWAIT 4 +#define MYFLOCK_BITS (MYFLOCK_LOCK_MASK | MYFLOCK_NOWAIT) + +/* 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/mymalloc.c b/postfix/util/mymalloc.c new file mode 100644 index 000000000..e20531c06 --- /dev/null +++ b/postfix/util/mymalloc.c @@ -0,0 +1,190 @@ +/*++ +/* NAME +/* mymalloc 3 +/* SUMMARY +/* memory management wrappers +/* SYNOPSIS +/* #include +/* +/* char *mymalloc(len) +/* int len; +/* +/* char *myrealloc(ptr, len) +/* char *ptr; +/* int len; +/* +/* void myfree(ptr) +/* char *ptr; +/* +/* char *mystrdup(str) +/* const char *str; +/* +/* char *mystrndup(str, len) +/* const char *str; +/* int len; +/* +/* char *mymemdup(ptr, len) +/* const char *ptr; +/* int len; +/* DESCRIPTION +/* This module performs low-level memory management with error +/* handling. A call of these functions either succeeds or it does +/* not return at all. +/* +/* mymalloc() allocates the requested amount of memory. The memory +/* is not set to zero. +/* +/* myrealloc() resizes memory obtained from mymalloc() or myrealloc() +/* to the requested size. The result pointer value may differ from +/* that given via the \fIptr\fR argument. +/* +/* myfree() takes memory obtained from mymalloc() or myrealloc() +/* and makes it available for other use. +/* +/* mystrdup() returns a dynamic-memory copy of its null-terminated +/* argument. This routine uses mymalloc(). +/* +/* mystrndup() returns a dynamic-memory copy of at most \fIlen\fR +/* leading characters of its null-terminated +/* argument. The result is null-terminated. This routine uses mymalloc(). +/* +/* mymemdup() makes a copy of the memory pointed to by \fIptr\fR +/* with length \fIlen\fR. The result is null-terminated. +/* This routine uses mymalloc(). +/* SEE ALSO +/* msg(3) diagnostics interface +/* DIAGNOSTICS +/* Problems are reported via the msg(3) diagnostics routines: +/* the requested amount of memory is not available; improper use +/* is detected; other fatal errors. +/* 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 libraries. */ + +#include "sys_defs.h" +#include +#include +#include + +/* Application-specific. */ + +#include "msg.h" +#include "mymalloc.h" + + /* + * Structure of an annotated memory block. In order to detect spurious + * free() calls we prepend a signature to memory given to the application. + * In order to detect access to free()d blocks, overwrite each block as soon + * as it is passed to myfree(). With the code below, the user data has + * integer alignment or better. + */ +typedef struct MBLOCK { + int signature; /* set when block is active */ + int length; /* user requested length */ + char payload[1]; /* actually a bunch of bytes */ +} MBLOCK; + +#define SIGNATURE 0xdead +#define FILLER 0xff + +#define CHECK_IN_PTR(ptr, real_ptr, len, fname) { \ + if (ptr == 0) \ + msg_panic("%s: null pointer input", fname); \ + real_ptr = (MBLOCK *) (ptr - offsetof(MBLOCK, payload[0])); \ + if (real_ptr->signature != SIGNATURE) \ + msg_panic("%s: corrupt or unallocated memory block", fname); \ + real_ptr->signature = 0; \ + if ((len = real_ptr->length) < 1) \ + msg_panic("%s: corrupt memory block length", fname); \ +} + +#define CHECK_OUT_PTR(ptr, real_ptr, len) { \ + real_ptr->signature = SIGNATURE; \ + real_ptr->length = len; \ + ptr = real_ptr->payload; \ +} + +#define SPACE_FOR(len) (offsetof(MBLOCK, payload[0]) + len) + +/* mymalloc - allocate memory or bust */ + +char *mymalloc(int len) +{ + char *ptr; + MBLOCK *real_ptr; + + if (len < 1) + msg_panic("mymalloc: requested length %d", len); + if ((real_ptr = (MBLOCK *) malloc(SPACE_FOR(len))) == 0) + msg_fatal("mymalloc: insufficient memory: %m"); + CHECK_OUT_PTR(ptr, real_ptr, len); + memset(ptr, FILLER, len); + return (ptr); +} + +/* myrealloc - reallocate memory or bust */ + +char *myrealloc(char *ptr, int len) +{ + MBLOCK *real_ptr; + int old_len; + + if (len < 1) + msg_panic("myrealloc: requested length %d", len); + CHECK_IN_PTR(ptr, real_ptr, old_len, "myrealloc"); + if ((real_ptr = (MBLOCK *) realloc((char *) real_ptr, SPACE_FOR(len))) == 0) + msg_fatal("myrealloc: insufficient memory: %m"); + CHECK_OUT_PTR(ptr, real_ptr, len); + if (len > old_len) + memset(ptr + old_len, FILLER, len - old_len); + return (ptr); +} + +/* myfree - release memory */ + +void myfree(char *ptr) +{ + MBLOCK *real_ptr; + int len; + + CHECK_IN_PTR(ptr, real_ptr, len, "myfree"); + memset((char *) real_ptr, FILLER, SPACE_FOR(len)); + free((char *) real_ptr); +} + +/* mystrdup - save string to heap */ + +char *mystrdup(const char *str) +{ + return (strcpy(mymalloc(strlen(str) + 1), str)); +} + +/* mystrndup - save substring to heap */ + +char *mystrndup(const char *str, int len) +{ + char *result; + int slen; + + if ((slen = strlen(str)) < len) + len = slen; + result = memcpy(mymalloc(len + 1), str, len); + result[len] = 0; + return (result); +} + +/* mymemdup - copy memory */ + +char *mymemdup(const char *ptr, int len) +{ + return (memcpy(mymalloc(len), ptr, len)); +} diff --git a/postfix/util/mymalloc.h b/postfix/util/mymalloc.h new file mode 100644 index 000000000..9503bcd17 --- /dev/null +++ b/postfix/util/mymalloc.h @@ -0,0 +1,35 @@ +#ifndef _MALLOC_H_INCLUDED_ +#define _MALLOC_H_INCLUDED_ + +/*++ +/* NAME +/* mymalloc 3h +/* SUMMARY +/* memory management wrappers +/* SYNOPSIS +/* #include "mymalloc.h" + DESCRIPTION + .nf + + /* + * External interface. + */ +extern char *mymalloc(int); +extern char *myrealloc(char *, int); +extern void myfree(char *); +extern char *mystrdup(const char *); +extern char *mystrndup(const char *, int len); +extern char *mymemdup(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/mystrtok.c b/postfix/util/mystrtok.c new file mode 100644 index 000000000..7607a6b35 --- /dev/null +++ b/postfix/util/mystrtok.c @@ -0,0 +1,92 @@ +/*++ +/* NAME +/* mystrtok 3 +/* SUMMARY +/* safe tokenizer +/* SYNOPSIS +/* #include +/* +/* char *mystrtok(bufp, delimiters) +/* char **bufp; +/* const char *delimiters; +/* DESCRIPTION +/* mystrtok() splits a buffer on the specified \fIdelimiters\fR. +/* Tokens are delimited by runs of delimiters, so this routine +/* cannot return zero-length tokens. +/* +/* The \fIbufp\fR argument specifies the start of the search; it +/* is updated with each call. The input is destroyed. +/* +/* The result value is the next token, or a null pointer when the +/* end of the buffer was reached. +/* 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 + +/* Utility library. */ + +#include "stringops.h" + +/* mystrtok - safe tokenizer */ + +char *mystrtok(char **src, const char *sep) +{ + char *start = *src; + char *end; + + /* + * Skip over leading delimiters. + */ + start += strspn(start, sep); + if (*start == 0) { + *src = start; + return (0); + } + + /* + * Separate off one token. + */ + end = start + strcspn(start, sep); + if (*end != 0) + *end++ = 0; + *src = end; + return (start); +} + +#ifdef TEST + + /* + * Test program: read lines from stdin, split on whitespace. + */ +#include "vstring.h" +#include "vstream.h" +#include "vstring_vstream.h" + +int main(void) +{ + VSTRING *vp = vstring_alloc(100); + char *start; + char *str; + + while (vstring_fgets(vp, VSTREAM_IN)) { + start = vstring_str(vp); + while ((str = mystrtok(&start, " \t\r\n")) != 0) + vstream_printf(">%s<\n", str); + vstream_fflush(VSTREAM_OUT); + } + vstring_free(vp); +} + +#endif diff --git a/postfix/util/name_mask.c b/postfix/util/name_mask.c new file mode 100644 index 000000000..1d01439ea --- /dev/null +++ b/postfix/util/name_mask.c @@ -0,0 +1,72 @@ +/*++ +/* NAME +/* name_mask 3 +/* SUMMARY +/* map names to bit mask +/* SYNOPSIS +/* #include +/* +/* int name_mask(table, names) +/* NAME_MASK *table; +/* const char *names; +/* DESCRIPTION +/* name_mask() takes a null-terminated \fItable\fR with (name, mask) +/* values and computes the bit-wise OR of the masks that correspond +/* to the names listed in the \fInames\fR argument, separated by +/* comma and/or whitespace characters. +/* DIAGNOSTICS +/* Fatal: the \fInames\fR argument specifies a name not found in +/* \fItable\fR. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include + +/* name_mask - compute mask corresponding to list of names */ + +int name_mask(NAME_MASK *table, const char *names) +{ + char *myname = "name_mask"; + char *saved_names = mystrdup(names); + char *bp = saved_names; + int result = 0; + NAME_MASK *np; + char *name; + + /* + * Break up the names string, and look up each component in the table. If + * the name is found, merge its mask with the result. + */ + while ((name = mystrtok(&bp, ", \t\r\n")) != 0) { + for (np = table; /* void */ ; np++) { + if (np->name == 0) + msg_fatal("unknown name \"%s\" in \"%s\"", name, names); + if (strcmp(name, np->name) == 0) { + if (msg_verbose) + msg_info("%s: %s", myname, name); + result |= np->mask; + break; + } + } + } + myfree(saved_names); + return (result); +} diff --git a/postfix/util/name_mask.h b/postfix/util/name_mask.h new file mode 100644 index 000000000..2387f3921 --- /dev/null +++ b/postfix/util/name_mask.h @@ -0,0 +1,35 @@ +#ifndef _NAME_MASK_H_INCLUDED_ +#define _NAME_MASK_H_INCLUDED_ + +/*++ +/* NAME +/* name_mask 3h +/* SUMMARY +/* map names to bit mask +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +typedef struct { + const char *name; + int mask; +} NAME_MASK; + +extern int name_mask(NAME_MASK *, const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/util/non_blocking.c b/postfix/util/non_blocking.c new file mode 100644 index 000000000..08f0c8243 --- /dev/null +++ b/postfix/util/non_blocking.c @@ -0,0 +1,66 @@ +/*++ +/* NAME +/* non_blocking 3 +/* SUMMARY +/* set/clear non-blocking flag +/* SYNOPSIS +/* #include +/* +/* int non_blocking(int fd, int on) +/* DESCRIPTION +/* the \fInon_blocking\fR() function manipulates the non-blocking +/* flag for the specified open file, and returns the old setting. +/* +/* Arguments: +/* .IP fd +/* A file descriptor. +/* .IP on +/* For non-blocking I/O, specify a non-zero value (or use the +/* NON_BLOCKING constant); for blocking I/O, specify zero +/* (or use the BLOCKING constant). +/* +/* The result is non-zero when the non-blocking flag was enabled. +/* DIAGNOSTICS +/* All errors are fatal. +/* 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 interfaces. */ + +#include "sys_defs.h" +#include + +/* Utility library. */ + +#include "msg.h" +#include "iostuff.h" + +/* Backwards compatibility */ +#ifdef FNDELAY +#define PATTERN FNDELAY +#else +#define PATTERN O_NONBLOCK +#endif + +/* non_blocking - set/clear non-blocking flag */ + +int non_blocking(fd, on) +int fd; +int on; +{ + int flags; + + if ((flags = fcntl(fd, F_GETFL, 0)) < 0) + msg_fatal("fcntl: get flags: %m"); + if (fcntl(fd, F_SETFL, on ? flags | PATTERN : flags & ~PATTERN) < 0) + msg_fatal("fcntl: set non-blocking flag %s: %m", on ? "on" : "off"); + return ((flags & PATTERN) != 0); +} diff --git a/postfix/util/open_as.c b/postfix/util/open_as.c new file mode 100644 index 000000000..0fa84b797 --- /dev/null +++ b/postfix/util/open_as.c @@ -0,0 +1,70 @@ +/*++ +/* NAME +/* open_as 3 +/* SUMMARY +/* open file as user +/* SYNOPSIS +/* #include +/* #include +/* +/* int open_as(path, flags, mode, euid, egid) +/* const char *path; +/* int mode; +/* uid_t euid; +/* gid_t egid; +/* DESCRIPTION +/* open_as() opens the named \fIpath\fR with the named \fIflags\fR +/* and \fImode\fR, and with the effective rights specified by \fIeuid\fR +/* and \fIegid\fR. A -1 result means the open failed. +/* DIAGNOSTICS +/* Fatal error: no permission to change privilege level. +/* SEE ALSO +/* set_eugid(3) switch effective rights +/* 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 + +/* Utility library. */ + +#include "msg.h" +#include "set_eugid.h" +#include "open_as.h" + +/* open_as - open file as user */ + +int open_as(const char *path, int flags, int mode, uid_t euid, gid_t egid) +{ + uid_t saved_euid = geteuid(); + gid_t saved_egid = getegid(); + int fd; + + /* + * Switch to the target user privileges. + */ + set_eugid(euid, egid); + + /* + * Open that file. + */ + fd = open(path, flags, mode); + + /* + * Restore saved privileges. + */ + set_eugid(saved_euid, saved_egid); + + return (fd); +} diff --git a/postfix/util/open_as.h b/postfix/util/open_as.h new file mode 100644 index 000000000..308e0094f --- /dev/null +++ b/postfix/util/open_as.h @@ -0,0 +1,30 @@ +#ifndef _OPEN_H_INCLUDED_ +#define _OPEN_H_INCLUDED_ + +/*++ +/* NAME +/* open_as 3h +/* SUMMARY +/* open file as user +/* SYNOPSIS +/* #include +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern int open_as(const char *, int, int, uid_t, gid_t); + +/* 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/open_limit.c b/postfix/util/open_limit.c new file mode 100644 index 000000000..fa08b762b --- /dev/null +++ b/postfix/util/open_limit.c @@ -0,0 +1,80 @@ +/*++ +/* NAME +/* open_limit 3 +/* SUMMARY +/* set/get open file limit +/* SYNOPSIS +/* #include +/* +/* int open_limit(int limit) +/* DESCRIPTION +/* The \fIopen_limit\fR() routine attempts to change the maximum +/* number of open files to the specified limit. Specify a null +/* argument to effect no change. The result is the actual open file +/* limit for the current process. The number can be smaller or larger +/* than the requested limit. +/* DIAGNOSTICS +/* open_limit() returns -1 in case of problems. The errno +/* variable gives hints about the nature of the problem. +/* 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 libraries. */ + +#include "sys_defs.h" +#include +#include +#include + +/* Application-specific. */ + +#include "iostuff.h" + + /* + * 44BSD compatibility. + */ +#ifndef RLIMIT_NOFILE +#ifdef RLIMIT_OFILE +#define RLIMIT_NOFILE RLIMIT_OFILE +#endif +#endif + +/* open_limit - set/query file descriptor limit */ + +int open_limit(int limit) +{ +#ifdef RLIMIT_NOFILE + struct rlimit rl; +#endif + + if (limit < 0) { + errno = EINVAL; + return (-1); + } +#ifdef RLIMIT_NOFILE + if (getrlimit(RLIMIT_NOFILE, &rl) < 0) + return (-1); + if (limit > 0) { + if (limit > rl.rlim_max) + rl.rlim_cur = rl.rlim_max; + else + rl.rlim_cur = limit; + if (setrlimit(RLIMIT_NOFILE, &rl) < 0) + return (-1); + } + return (rl.rlim_cur); +#endif + +#ifndef RLIMIT_NOFILE + return (getdtablesize()); +#endif +} + diff --git a/postfix/util/open_lock.c b/postfix/util/open_lock.c new file mode 100644 index 000000000..ec30700ee --- /dev/null +++ b/postfix/util/open_lock.c @@ -0,0 +1,75 @@ +/*++ +/* NAME +/* open_lock 3 +/* SUMMARY +/* open or create file and lock it for exclusive access +/* SYNOPSIS +/* #include +/* +/* VSTREAM *open_lock(path, int flags, int mode, why) +/* const char *path; +/* int flags; +/* int mode; +/* VSTRING *why; +/* DESCRIPTION +/* This module opens or creates the named file and attempts to +/* acquire an exclusive lock. The lock is lost when the last +/* process closes the file. +/* +/* Arguments: +/* .IP "path, flags, mode" +/* These are passed on to safe_open(). +/* .IP why +/* storage for diagnostics. +/* SEE ALSO +/* safe_open(3) carefully open or create file +/* myflock(3) get exclusive lock on file +/* DIAGNOSTICS +/* In case of problems the result is a null pointer and a problem +/* description is returned via the global \fIerrno\fR variable. +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* open_lock - open file and lock it for exclusive access */ + +VSTREAM *open_lock(const char *path, int flags, int mode, VSTRING *why) +{ + VSTREAM *fp; + + /* + * Carefully create or open the file, and lock it down. Some systems + * don't have the O_LOCK open() flag, or the flag does not do what we + * want, so we roll our own lock. + */ + if ((fp = safe_open(path, flags, mode, -1, -1, why)) == 0) + return (0); + if (myflock(vstream_fileno(fp), MYFLOCK_EXCLUSIVE | MYFLOCK_NOWAIT) < 0) { + vstring_sprintf(why, "file %s: unable to lock: %m", path); + vstream_fclose(fp); + return (0); + } + return (fp); +} diff --git a/postfix/util/open_lock.h b/postfix/util/open_lock.h new file mode 100644 index 000000000..869233f95 --- /dev/null +++ b/postfix/util/open_lock.h @@ -0,0 +1,41 @@ +#ifndef _OPEN_LOCK_H_INCLUDED_ +#define _OPEN_LOCK_H_INCLUDED_ + +/*++ +/* NAME +/* open_lock 3h +/* SUMMARY +/* open or create file and lock it for exclusive access +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include +#include + + /* + * External interface. + */ +extern VSTREAM *open_lock(const char *, int, int, VSTRING *); + +/* 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/peekfd.c b/postfix/util/peekfd.c new file mode 100644 index 000000000..90040ebbf --- /dev/null +++ b/postfix/util/peekfd.c @@ -0,0 +1,60 @@ +/*++ +/* NAME +/* peekfd 3 +/* SUMMARY +/* determine amount of data ready to read +/* SYNOPSIS +/* #include +/* +/* int peekfd(fd) +/* int fd; +/* DESCRIPTION +/* peekfd() attempts to find out how many bytes are available to +/* be read from the named file descriptor. The result value is +/* the number of available bytes. +/* DIAGNOSTICS +/* peekfd() returns -1 in case of trouble. The global \fIerrno\fR +/* variable reflects the nature of the problem. +/* 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 +#ifdef FIONREAD_IN_SYS_FILIO_H +#include +#endif +#ifdef FIONREAD_IN_TERMIOS_H +#include +#endif +#include + +/* Utility library. */ + +#include "iostuff.h" + +/* peekfd - return amount of data ready to read */ + +int peekfd(int fd) +{ + int count; + + /* + * Anticipate a series of system-dependent code fragments. + */ +#ifdef FIONREAD + return (ioctl(fd, FIONREAD, (char *) &count) < 0 ? -1 : count); +#else +#error "don't know how to look ahead" +#endif +} + diff --git a/postfix/util/peer_name.c b/postfix/util/peer_name.c new file mode 100644 index 000000000..5dc9327d7 --- /dev/null +++ b/postfix/util/peer_name.c @@ -0,0 +1,113 @@ +/*++ +/* NAME +/* peer_name 3 +/* SUMMARY +/* produce printable peer name and address +/* SYNOPSIS +/* #include +/* +/* typedef struct { +/* .in +4 +/* int type; +/* char name; +/* char addr; +/* .in -4 +/* } PEER_NAME; +/* +/* PEER_NAME *peer_name(sock) +/* int sock; +/* DESCRIPTION +/* The \fIpeer_name\fR() routine attempts to produce a printable +/* version of the peer name and address of the specified socket. +/* The result is in static memory that will be overwritten. +/* Make a copy if the result is to be used for an appreciable +/* amount of time. +/* +/* Where information is unavailable, the name and/or address +/* are set to "unknown". +/* The \fItype\fR result field specifies how the name and address +/* should be interpreted: +/* .IP PEER_TYPE_INET +/* The socket specifies a TCP/IP endpoint. +/* The result is a hostname (from the DNS, a local hosts file or +/* other); the address a dotted quad. +/* .IP PEER_TYPE_LOCAL +/* The socket argument specifies a local transport. +/* The result name is "localhost"; the result address is "127.0.0.1". +/* .IP PEER_TYPE_UNKNOWN +/* The socket argument does not specify a socket. +/* The result name is "localhost"; the result address is "127.0.0.1". +/* 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 + +/* Utility library. */ + +#include +#include +#include + +/* peer_name - produce printable peer name and address */ + +PEER_NAME *peer_name(int sock) +{ + static PEER_NAME peer; + struct sockaddr_in sin; + SOCKADDR_SIZE len = sizeof(sin); + struct hostent *hp; + + if (getpeername(sock, (struct sockaddr *) & sin, &len) == 0) { + switch (sin.sin_family) { + case AF_INET: + peer.type = PEER_TYPE_INET; + hp = gethostbyaddr((char *) &(sin.sin_addr), + sizeof(sin.sin_addr), AF_INET); + peer.name = (hp && valid_hostname(hp->h_name) ? + hp->h_name : "unknown"); + peer.addr = inet_ntoa(sin.sin_addr); + return (&peer); + case AF_UNSPEC: + case AF_UNIX: + peer.type = PEER_TYPE_LOCAL; + peer.name = "localhost"; + peer.addr = "127.0.0.1"; + return (&peer); + } + } + peer.type = PEER_TYPE_UNKNOWN; + peer.name = "localhost"; + peer.addr = "127.0.0.1"; + return (&peer); + +} + +#ifdef TEST + +#include + +int main(int unused_argc, char **unused_argv) +{ + PEER_NAME *peer; + + peer = peer_name(STDIN_FILENO); + msg_info("name %s addr %s", peer->name, peer->addr); +} + +#endif diff --git a/postfix/util/peer_name.h b/postfix/util/peer_name.h new file mode 100644 index 000000000..f1c96374a --- /dev/null +++ b/postfix/util/peer_name.h @@ -0,0 +1,39 @@ +#ifndef _PEER_NAME_H_INCLUDED_ +#define _PEER_NAME_H_INCLUDED_ + +/*++ +/* NAME +/* peer_name 3h +/* SUMMARY +/* produce printable peer name and address +/* SYNOPSIS +/* #include +/* DESCRIPTION + + /* + * External interface. + */ +typedef struct { + int type; /* IPC type, see below */ + char *name; /* peer official name */ + char *addr; /* peer address */ +} PEER_NAME; + +#define PEER_TYPE_UNKNOWN 0 +#define PEER_TYPE_INET 1 +#define PEER_TYPE_LOCAL 2 + +extern PEER_NAME *peer_name(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/percentm.c b/postfix/util/percentm.c new file mode 100644 index 000000000..31eb14ca9 --- /dev/null +++ b/postfix/util/percentm.c @@ -0,0 +1,83 @@ +/*++ +/* NAME +/* percentm 3 +/* SUMMARY +/* expand %m embedded in string to system error text +/* SYNOPSIS +/* #include +/* +/* char *percentm(const char *src, int err) +/* DESCRIPTION +/* The percentm() routine makes a copy of the null-terminated string +/* given via the \fIsrc\fR argument, with %m sequences replaced by +/* the system error text corresponding to the \fIerr\fR argument. +/* The result is overwritten upon each successive call. +/* +/* Arguments: +/* .IP src +/* A null-terminated input string with zero or more %m sequences. +/* .IP err +/* A legal \fIerrno\fR value. The text corresponding to this error +/* value is used when expanding %m sequences. +/* SEE ALSO +/* syslog(3) system logger library +/* HISTORY +/* .ad +/* .fi +/* A percentm() routine appears in the TCP Wrapper software +/* by Wietse Venema. +/* 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 libraries. */ + +#include +#include + +/* Utility library. */ + +#include "vstring.h" +#include "percentm.h" + +/* percentm - replace %m by error message corresponding to value in err */ + +char *percentm(const char *str, int err) +{ + static VSTRING *vp; + const unsigned char *ip = (const unsigned char *) str; + + if (vp == 0) + vp = vstring_alloc(100); /* grows on demand */ + VSTRING_RESET(vp); + + while (*ip) { + switch (*ip) { + default: + VSTRING_ADDCH(vp, *ip++); + break; + case '%': + switch (ip[1]) { + default: /* leave % alone */ + VSTRING_ADDCH(vp, *ip++); + case '\0': /* don't fall off end */ + VSTRING_ADDCH(vp, *ip++); + break; + case 'm': /* replace %m */ + vstring_strcat(vp, strerror(err)); + ip += 2; + break; + } + } + } + VSTRING_TERMINATE(vp); + return (vstring_str(vp)); +} + diff --git a/postfix/util/percentm.h b/postfix/util/percentm.h new file mode 100644 index 000000000..d170e9586 --- /dev/null +++ b/postfix/util/percentm.h @@ -0,0 +1,35 @@ +#ifndef _PERCENT_H_INCLUDED_ +#define _PERCENT_H_INCLUDED_ + +/*++ +/* NAME +/* percentm 3h +/* SUMMARY +/* expand %m embedded in string to system error text +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern char *percentm(const char *, int); + +/* HISTORY +/* .ad +/* .fi +/* A percentm() routine appears in the TCP Wrapper software +/* by Wietse Venema. +/* 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/posix_signals.c b/postfix/util/posix_signals.c new file mode 100644 index 000000000..b7029ddb6 --- /dev/null +++ b/postfix/util/posix_signals.c @@ -0,0 +1,15 @@ + /* + * The NETXSTEP and OPENSTEP software is not bundled with IBM's public + * release. It will be made available as contributed software from + * http://www.postfix.org/ + */ +#include "sys_defs.h" + +#ifdef MISSING_SIGSET_T +#error "This requires contributed software from http://www.postfix.org/" +#endif + +#ifdef MISSING_SIGACTION +#error "This requires contributed software from http://www.postfix.org/" +#endif + diff --git a/postfix/util/posix_signals.h b/postfix/util/posix_signals.h new file mode 100644 index 000000000..a2f6bbf6f --- /dev/null +++ b/postfix/util/posix_signals.h @@ -0,0 +1,5 @@ + /* + * The NETXSTEP and OPENSTEP software is not bundled with IBM's public + * release. It will be made available as contributed software from + * http://www.postfix.org/ + */ diff --git a/postfix/util/printable.c b/postfix/util/printable.c new file mode 100644 index 000000000..85c24897c --- /dev/null +++ b/postfix/util/printable.c @@ -0,0 +1,51 @@ +/*++ +/* NAME +/* printable 3 +/* SUMMARY +/* mask non-printable characters +/* SYNOPSIS +/* #include +/* +/* char *printable(buffer, replacement) +/* char *buffer; +/* int replacement; +/* DESCRIPTION +/* printable() replaces non-printable characters in its input +/* by the given replacement. +/* +/* Arguments: +/* .IP buffer +/* The null-terminated input string. +/* .IP replacement +/* Replacement value for characters in \fIbuffer\fR that do not +/* pass the isprint(3) test. +/* 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 + +/* Utility library. */ + +#include "stringops.h" + +char *printable(char *string, int replacement) +{ + char *cp; + int ch; + + for (cp = string; (ch = *(unsigned char *) cp) != 0; cp++) + if (!ISPRINT(ch)) + *cp = replacement; + return (string); +} diff --git a/postfix/util/read_wait.c b/postfix/util/read_wait.c new file mode 100644 index 000000000..b1ec3528c --- /dev/null +++ b/postfix/util/read_wait.c @@ -0,0 +1,102 @@ +/*++ +/* NAME +/* read_wait 3 +/* SUMMARY +/* wait until descriptor becomes readable +/* SYNOPSIS +/* #include +/* +/* int read_wait(fd, timeout) +/* int fd; +/* int timeout; +/* DESCRIPTION +/* read_wait() blocks the current process until the specified file +/* descriptor becomes readable, or until the deadline is exceeded. +/* +/* Arguments: +/* .IP fd +/* File descriptor in the range 0..FD_SETSIZE. +/* .IP timeout +/* If positive, deadline in seconds. A zero value effects a poll. +/* A negative value means wait until something happens. +/* DIAGNOSTICS +/* Panic: interface violation. All system call errors are fatal. +/* +/* A zero result means success. When the specified deadline is +/* exceeded, read_wait() returns -1 and sets errno to ETIMEDOUT. +/* 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 + +#ifdef USE_SYS_SELECT_H +#include +#endif + +/* Utility library. */ + +#include +#include + +/* read_wait - block with timeout until file descriptor is readable */ + +int read_wait(int fd, int timeout) +{ + fd_set read_fds; + fd_set except_fds; + struct timeval tv; + struct timeval *tp; + + /* + * Sanity checks. + */ + if (FD_SETSIZE <= fd) + msg_panic("descriptor %d does not fit FD_SETSIZE %d", fd, FD_SETSIZE); + + /* + * Use select() so we do not depend on alarm() and on signal() handlers. + * Restart the select when interrupted by some signal. Some select() + * implementations reduce the time to wait when interrupted, which is + * exactly what we want. + */ + FD_ZERO(&read_fds); + FD_SET(fd, &read_fds); + FD_ZERO(&except_fds); + FD_SET(fd, &except_fds); + if (timeout >= 0) { + tv.tv_usec = 0; + tv.tv_sec = timeout; + tp = &tv; + } else { + tp = 0; + } + + for (;;) { + switch (select(fd + 1, &read_fds, (fd_set *) 0, &except_fds, tp)) { + case -1: + if (errno != EINTR) + msg_fatal("select: %m"); + continue; + case 0: + errno = ETIMEDOUT; + return (-1); + default: + return (0); + } + } +} diff --git a/postfix/util/readable.c b/postfix/util/readable.c new file mode 100644 index 000000000..a3af0daf0 --- /dev/null +++ b/postfix/util/readable.c @@ -0,0 +1,88 @@ +/*++ +/* NAME +/* readable 3 +/* SUMMARY +/* test if descriptor is readable +/* SYNOPSIS +/* #include +/* +/* int readable(fd) +/* int fd; +/* DESCRIPTION +/* readable() asks the kernel if the specified file descriptor +/* is readable, i.e. a read operation would not block. +/* +/* Arguments: +/* .IP fd +/* File descriptor in the range 0..FD_SETSIZE. +/* DIAGNOSTICS +/* All system call errors are fatal. +/* 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 + +#ifdef USE_SYS_SELECT_H +#include +#endif + +/* Utility library. */ + +#include +#include + +/* readable - see if file descriptor is readable */ + +int readable(int fd) +{ + struct timeval tv; + fd_set read_fds; + fd_set except_fds; + + /* + * Sanity checks. + */ + if (fd >= FD_SETSIZE) + msg_fatal("fd %d does not fit in FD_SETSIZE", fd); + + /* + * Initialize. + */ + FD_ZERO(&read_fds); + FD_SET(fd, &read_fds); + FD_ZERO(&except_fds); + FD_SET(fd, &except_fds); + tv.tv_sec = 0; + tv.tv_usec = 0; + + /* + * Loop until we have an authoritative answer. + */ + for (;;) { + switch (select(fd + 1, &read_fds, (fd_set *) 0, &except_fds, &tv)) { + case -1: + if (errno != EINTR) + msg_fatal("select: %m"); + continue; + default: + return (FD_ISSET(fd, &read_fds)); + case 0: + return (0); + } + } +} diff --git a/postfix/util/readline.c b/postfix/util/readline.c new file mode 100644 index 000000000..c7a218f7e --- /dev/null +++ b/postfix/util/readline.c @@ -0,0 +1,76 @@ +/*++ +/* NAME +/* readline 3 +/* SUMMARY +/* read logical line +/* SYNOPSIS +/* #include +/* +/* VSTRING *readline(buf, fp, lineno) +/* VSTRING *buf; +/* VSTREAM *fp; +/* int *lineno; +/* DESCRIPTION +/* readline() reads one logical line from the named stream. +/* A line that starts with whitespace is a continuation of +/* the previous line. The newline between continued lines +/* is deleted from the input. The result value is the input +/* buffer argument or a null pointer when no input is found. +/* +/* Arguments: +/* .IP buf +/* A variable-length buffer for input. +/* .IP fp +/* Handle to an open stream. +/* .IP lineno +/* A null pointer, or a pointer to an integer that is incremented +/* after reading a newline. +/* 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 "vstream.h" +#include "vstring.h" +#include "readline.h" + +/* readline - read one logical line */ + +VSTRING *readline(VSTRING *buf, VSTREAM *fp, int *lineno) +{ + int ch; + int next; + + /* + * Lines that start with whitespace continue the preceding line. + */ + VSTRING_RESET(buf); + while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF) { + if (ch == '\n') { + if (lineno) + *lineno += 1; + if ((next = VSTREAM_GETC(fp)) == ' ' || next == '\t') { + ch = next; + } else { + if (next != VSTREAM_EOF) + vstream_ungetc(fp, next); + break; + } + } + VSTRING_ADDCH(buf, ch); + } + VSTRING_TERMINATE(buf); + return (VSTRING_LEN(buf) || ch == '\n' ? buf : 0); +} diff --git a/postfix/util/readline.h b/postfix/util/readline.h new file mode 100644 index 000000000..dd6327002 --- /dev/null +++ b/postfix/util/readline.h @@ -0,0 +1,36 @@ +#ifndef _READLINE_H_INCLUDED_ +#define _READLINE_H_INCLUDED_ + +/*++ +/* NAME +/* readline 3h +/* SUMMARY +/* read logical line +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include + + /* + * External interface. + */ +extern VSTRING *readline(VSTRING *, VSTREAM *, 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/ring.c b/postfix/util/ring.c new file mode 100644 index 000000000..d4c5f82ae --- /dev/null +++ b/postfix/util/ring.c @@ -0,0 +1,121 @@ +/*++ +/* NAME +/* ring 3 +/* SUMMARY +/* circular list management +/* SYNOPSIS +/* #include +/* +/* void ring_init(list) +/* RING *list; +/* +/* void ring_prepend(list, element) +/* RING *list; +/* RING *element; +/* +/* void ring_append(list, element) +/* RING *list; +/* RING *element; +/* +/* RING *ring_pred(element) +/* RING *element; +/* +/* RING *ring_succ(element) +/* RING *element; +/* +/* void ring_detach(element) +/* RING *element; +/* +/* RING_FOREACH(RING *element, RING *head) +/* DESCRIPTION +/* This module manages circular, doubly-linked, lists. It provides +/* operations to initialize a list, to add or remove an element, +/* and to iterate over a list. Although the documentation appears +/* to emphasize the special role of the list head, each operation +/* can be applied to each list member. +/* +/* Examples of applications: any sequence of objects such as queue, +/* unordered list, or stack. Typically, an application embeds a RING +/* structure into its own data structure, and uses the RING primitives +/* to maintain the linkage between application-specific data objects. +/* +/* ring_init() initializes its argument to a list of just one element. +/* +/* ring_append() appends the named element to the named list head. +/* +/* ring_prepend() prepends the named element to the named list head. +/* +/* ring_succ() returns the list element that follows its argument. +/* +/* ring_pred() returns the list element that precedes its argument. +/* +/* ring_detach() disconnects a list element from its neighbors +/* and closes the hole. This routine performs no implicit ring_init() +/* on the removed element. +/* +/* RING_FOREACH() is a macro that expands to a for (... ; ... ; ...) +/* statement that iterates over each list element in forward order. +/* Upon completion, the \fIelement\fR variable is set equal to +/* \fIhead\fR. The list head itself is not treated as a list member. +/* 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 libraries. */ + +/* Application-specific. */ + +#include "ring.h" + +/* ring_init - initialize ring head */ + +void ring_init(ring) +RING *ring; +{ + ring->pred = ring->succ = ring; +} + +/* ring_append - insert entry after ring head */ + +void ring_append(ring, entry) +RING *ring; +RING *entry; +{ + entry->succ = ring->succ; + entry->pred = ring; + ring->succ->pred = entry; + ring->succ = entry; +} + +/* ring_prepend - insert new entry before ring head */ + +void ring_prepend(ring, entry) +RING *ring; +RING *entry; +{ + entry->pred = ring->pred; + entry->succ = ring; + ring->pred->succ = entry; + ring->pred = entry; +} + +/* ring_detach - remove entry from ring */ + +void ring_detach(entry) +RING *entry; +{ + RING *succ = entry->succ; + RING *pred = entry->pred; + + pred->succ = succ; + succ->pred = pred; + + entry->succ = entry->pred = 0; +} diff --git a/postfix/util/ring.h b/postfix/util/ring.h new file mode 100644 index 000000000..a96fe2696 --- /dev/null +++ b/postfix/util/ring.h @@ -0,0 +1,45 @@ +#ifndef _RING_H_INCLUDED_ +#define _RING_H_INCLUDED_ + +/*++ +/* NAME +/* ring 3h +/* SUMMARY +/* circular list management +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +typedef struct RING RING; + +struct RING { + RING *succ; /* successor */ + RING *pred; /* predecessor */ +}; + +extern void ring_init(RING *); +extern void ring_prepend(RING *, RING *); +extern void ring_append(RING *, RING *); +extern void ring_detach(RING *); + +#define ring_succ(c) ((c)->succ) +#define ring_pred(c) ((c)->pred) + +/* 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 +/* LAST MODIFICATION +/* Tue Jan 28 16:50:20 EST 1997 +/*--*/ + +#endif diff --git a/postfix/util/safe.h b/postfix/util/safe.h new file mode 100644 index 000000000..8b75bf43e --- /dev/null +++ b/postfix/util/safe.h @@ -0,0 +1,30 @@ +#ifndef _SAFE_H_INCLUDED_ +#define _SAFE_H_INCLUDED_ + +/*++ +/* NAME +/* safe 3h +/* SUMMARY +/* miscellaneous taint checks +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern int unsafe(void); +extern char *safe_getenv(const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/util/safe_getenv.c b/postfix/util/safe_getenv.c new file mode 100644 index 000000000..04ca65935 --- /dev/null +++ b/postfix/util/safe_getenv.c @@ -0,0 +1,41 @@ +/*++ +/* NAME +/* safe_getenv 3 +/* SUMMARY +/* guarded getenv() +/* SYNOPSIS +/* #include +/* +/* char *safe_getenv(const name) +/* char *name; +/* DESCRIPTION +/* The \fBsafe_getenv\fR() routine reads the named variable from the +/* environment, provided that the unsafe() routine agrees. +/* SEE ALSO +/* unsafe(3), detect non-user privileges +/* 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 "safe.h" + +/* safe_getenv - read environment variable with guard */ + +char *safe_getenv(const char *name) +{ + return (unsafe() == 0 ? getenv(name) : 0); +} diff --git a/postfix/util/safe_open.c b/postfix/util/safe_open.c new file mode 100644 index 000000000..c5a1aa591 --- /dev/null +++ b/postfix/util/safe_open.c @@ -0,0 +1,216 @@ +/*++ +/* NAME +/* safe_open 3 +/* SUMMARY +/* safely open or create regular file +/* SYNOPSIS +/* #include +/* +/* VSTREAM *safe_open(path, flags, mode, user, group, why) +/* const char *path; +/* int flags; +/* int mode; +/* uid_t user; +/* gid_t group; +/* VSTRING *why; +/* DESCRIPTION +/* safe_open() carefully opens or creates a file in a directory +/* that may be writable by untrusted users. If a file is created +/* it is given the specified ownership and permission attributes. +/* If an existing file is opened it must be a regular file with +/* only one hard link. +/* +/* Arguments: +/* .IP "path, flags, mode" +/* These arguments are the same as with open(2). The O_EXCL flag +/* must appear either in combination with O_CREAT, or not at all. +/* .sp +/* No change is made to the permissions of an existing file. +/* .IP "user, group" +/* File ownership for a file created by safe_open(). Specify -1 +/* in order to disable user and/or group ownership change. +/* .sp +/* No change is made to the ownership of an existing file. +/* .IP why +/* A VSTRING pointer for diagnostics. +/* DIAGNOSTICS +/* Panic: interface violations. +/* +/* A null result means there was a problem. The nature of the +/* problem is returned via the \fIwhy\fR buffer; some errors +/* cannot be reported via \fIerrno\fR. +/* HISTORY +/* .fi +/* .ad +/* A safe open routine was discussed by Casper Dik in article +/* <2rdb0s$568@mail.fwi.uva.nl>, posted to comp.security.unix +/* (May 18, 1994). +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include + +/* safe_open_exist - open existing file */ + +static VSTREAM *safe_open_exist(const char *path, int flags, VSTRING *why) +{ + struct stat fstat_st; + struct stat lstat_st; + VSTREAM *fp; + + /* + * Open an existing file. + */ + if ((fp = vstream_fopen(path, flags & ~(O_CREAT | O_EXCL), 0)) == 0) { + vstring_sprintf(why, "error opening file %s: %m", path); + return (0); + } + + /* + * Examine the modes from the open file: it must have exactly one hard + * link (so that someone can't lure us into clobbering a sensitive file + * by making a hard link to it), and it must be a regular file. + */ + if (fstat(vstream_fileno(fp), &fstat_st) < 0) { + vstring_sprintf(why, "file %s: bad status: %m", path); + } else if (S_ISREG(fstat_st.st_mode) == 0) { + vstring_sprintf(why, "file %s: must be a regular file", path); + } else if (fstat_st.st_nlink != 1) { + vstring_sprintf(why, "file %s: must have one hard link", path); + } + + /* + * Look up the file again, this time using lstat(). Compare the fstat() + * (open file) modes with the lstat() modes. If there is any difference, + * either we followed a symlink while opening an existing file, someone + * quickly changed the number of hard links, or someone replaced the file + * after the open() call. The link and mode tests aren't really necessary + * but the additional cost is low. + */ + else if (lstat(path, &lstat_st) < 0 + || fstat_st.st_dev != lstat_st.st_dev + || fstat_st.st_ino != lstat_st.st_ino + || fstat_st.st_nlink != lstat_st.st_nlink + || fstat_st.st_mode != lstat_st.st_mode) { + vstring_sprintf(why, "file %s: status has changed", path); + } + + /* + * We are almost there... + */ + else { + return (fp); + } + + /* + * End up here in case of fstat()/lstat() problems or inconsistencies. + * Reset errno to reduce confusion. + */ + errno = 0; + vstream_fclose(fp); + return (0); +} + +/* safe_open_create - create new file */ + +static VSTREAM *safe_open_create(const char *path, int flags, int mode, + uid_t user, uid_t group, VSTRING *why) +{ + VSTREAM *fp; + + /* + * Create a non-existing file. This relies on O_CREAT | O_EXCL to not + * follow symbolic links. + */ + if ((fp = vstream_fopen(path, flags | (O_CREAT | O_EXCL), mode)) == 0) { + vstring_sprintf(why, "error opening file %s: %m", path); + return (0); + } + + /* + * Optionally change ownership after creating a new file. If there is a + * problem we should not attempt to delete the file. Something else may + * have opened the file in the mean time. + */ +#define CHANGE_OWNER(user, group) (user != (uid_t) -1 || group != (gid_t) -1) + + if (CHANGE_OWNER(user, group) + && fchown(vstream_fileno(fp), user, group) < 0) { + vstring_sprintf(why, "error changing ownership of %s: %m", path); + } + + /* + * We are almost there... + */ + else { + return (fp); + } + + /* + * End up here in case of trouble. + */ + vstream_fclose(fp); + return (0); +} + +/* safe_open - safely open or create file */ + +VSTREAM *safe_open(const char *path, int flags, int mode, + uid_t user, gid_t group, VSTRING *why) +{ + VSTREAM *fp; + + switch (flags & (O_CREAT | O_EXCL)) { + + /* + * Open an existing file, carefully. + */ + case 0: + return (safe_open_exist(path, flags, why)); + + /* + * Create a new file, carefully. + */ + case O_CREAT | O_EXCL: + return (safe_open_create(path, flags, mode, user, group, why)); + + /* + * Open an existing file or create a new one, carefully. When opening + * an existing file, we are prepared to deal with "no file" errors + * only. Any other error means we better give up trying. + */ + case O_CREAT: + if ((fp = safe_open_exist(path, flags, why)) == 0) + if (errno == ENOENT) + fp = safe_open_create(path, flags, mode, user, group, why); + return (fp); + + /* + * Interface violation. Sorry, but we must be strict. + */ + default: + msg_panic("safe_open: O_EXCL flag without O_CREAT flag"); + } +} diff --git a/postfix/util/safe_open.h b/postfix/util/safe_open.h new file mode 100644 index 000000000..02964f8ba --- /dev/null +++ b/postfix/util/safe_open.h @@ -0,0 +1,41 @@ +#ifndef _SAFE_OPEN_H_INCLUDED_ +#define _SAFE_OPEN_H_INCLUDED_ + +/*++ +/* NAME +/* safe_open 3h +/* SUMMARY +/* safely open or create regular file +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include +#include + + /* + * External interface. + */ +extern VSTREAM *safe_open(const char *, int, int, uid_t, gid_t, VSTRING *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/util/sane_accept.c b/postfix/util/sane_accept.c new file mode 100644 index 000000000..b1e2a5309 --- /dev/null +++ b/postfix/util/sane_accept.c @@ -0,0 +1,81 @@ +/*++ +/* NAME +/* sane_accept 3 +/* SUMMARY +/* sanitize accept() error returns +/* SYNOPSIS +/* #include +/* +/* int sane_accept(sock, buf, len) +/* int sock; +/* struct sockaddr *buf; +/* int len; +/* DESCRIPTION +/* sane_accept() implements the accept(2) socket call, and maps +/* known harmless error results to EAGAIN. +/* BUGS +/* Bizarre systems may have other harmless error results. Such +/* systems encourage programers to ignore error results, and +/* penalizes programmers who code defensively. +/* 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 "sane_accept.h" + +/* sane_accept - sanitize accept() error returns */ + +int sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len) +{ + static int accept_ok_errors[] = { + EAGAIN, + ECONNREFUSED, + ECONNRESET, + EHOSTDOWN, + EHOSTUNREACH, + EINTR, + ENETDOWN, + ENETUNREACH, + ENOTCONN, + EWOULDBLOCK, + 0, + }; + int count; + int err; + int fd; + + /* + * XXX Solaris 2.4 accept() returns EPIPE when a UNIX-domain client has + * disconnected in the mean time. From then on, UNIX-domain sockets are + * hosed beyond recovery. There is no point treating this as a beneficial + * error result because the program would go into a tight loop. + * + * XXX LINUX < 2.1 accept() wakes up before the three-way handshake is + * complete, so it can fail with ECONNRESET and other "false alarm" + * indications. + */ + if ((fd = accept(sock, sa, len)) < 0) { + for (count = 0; (err = accept_ok_errors[count]) != 0; count++) { + if (errno == err) { + errno = EAGAIN; + break; + } + } + } + return (fd); +} diff --git a/postfix/util/sane_accept.h b/postfix/util/sane_accept.h new file mode 100644 index 000000000..84cc36034 --- /dev/null +++ b/postfix/util/sane_accept.h @@ -0,0 +1,29 @@ +#ifndef _SANE_ACCEPT_H_ +#define _SANE_ACCEPT_H_ + +/*++ +/* NAME +/* sane_accept 3h +/* SUMMARY +/* sanitize accept() error returns +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern int sane_accept(int, struct sockaddr *, SOCKADDR_SIZE *); + +/* 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/scan_dir.c b/postfix/util/scan_dir.c new file mode 100644 index 000000000..b13b5eba6 --- /dev/null +++ b/postfix/util/scan_dir.c @@ -0,0 +1,136 @@ +/*++ +/* NAME +/* scan_dir 3 +/* SUMMARY +/* directory scanning +/* SYNOPSIS +/* #include +/* +/* SCAN_DIR *scan_dir_open(path) +/* const char *path; +/* +/* char *scan_dir_next(scan) +/* SCAN_DIR *scan; +/* +/* char *scan_dir_path(scan) +/* SCAN_DIR *scan; +/* +/* SCAN_DIR *scan_dir_close(scan) +/* SCAN_DIR *scan; +/* DESCRIPTION +/* These functions scan directories for names. The "." and +/* ".." names are skipped. Essentially, this is +/* extended with error handling and with knowledge of the +/* name of the directory being scanned. +/* +/* scan_dir_open() opens the named directory and +/* returns a handle for subsequent use. +/* +/* scan_dir_close() closes the directory and cleans up +/* and returns a null pointer. +/* +/* scan_dir_next() returns the next filename in the specified +/* directory. It skips the "." and ".." entries. +/* +/* scan_dir_path() returns the name of the directory being scanned. +/* DIAGNOSTICS +/* All errors are fatal. +/* 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 + +#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 "scan_dir.h" + + /* + * Opaque structure, so we don't have to expose the user to the above #ifdef + * spaghetti. + */ +struct SCAN_DIR { + char *path; + DIR *dir; +}; + +/* scan_dir_path - return the path of the directory being read. */ + +char *scan_dir_path(SCAN_DIR *scan) +{ + return scan->path; +} + +/* scan_dir_open - start directory scan */ + +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); + return (scan); +} + +char *scan_dir_next(SCAN_DIR *scan) +{ + 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); + } + } + return (0); +} + +/* scan_dir_close - terminate directory 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); + myfree((char *) scan); + return (0); +} diff --git a/postfix/util/scan_dir.h b/postfix/util/scan_dir.h new file mode 100644 index 000000000..cc7e5538c --- /dev/null +++ b/postfix/util/scan_dir.h @@ -0,0 +1,40 @@ +#ifndef _SCAN_DIR_H_INCLUDED_ +#define _SCAN_DIR_H_INCLUDED_ + +/*++ +/* NAME +/* scan_dir 3h +/* SUMMARY +/* directory scanner +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * The directory scanner interface. + */ +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 SCAN_DIR *scan_dir_close(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/util/set_eugid.c b/postfix/util/set_eugid.c new file mode 100644 index 000000000..b86ca4ce3 --- /dev/null +++ b/postfix/util/set_eugid.c @@ -0,0 +1,57 @@ +/*++ +/* NAME +/* set_eugid 3 +/* SUMMARY +/* set effective user and group attributes +/* SYNOPSIS +/* #include +/* +/* void set_eugid(euid, egid) +/* uid_t euid; +/* gid_t egid; +/* DESCRIPTION +/* set_eugid() sets the effective user and group process attributes +/* and updates the process group access list to be just the specified +/* effective group id. +/* DIAGNOSTICS +/* All system call errors are fatal. +/* SEE ALSO +/* seteuid(2), setegid(2), setgroups(2) +/* 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 + +/* Utility library. */ + +#include "msg.h" +#include "set_eugid.h" + +/* set_eugid - set effective user and group attributes */ + +void set_eugid(uid_t euid, gid_t egid) +{ + if (geteuid() != 0) + if (seteuid(0)) + msg_fatal("set_eugid: seteuid(0): %m"); + if (setegid(egid) < 0) + msg_fatal("set_eugid: setegid(%d): %m", egid); + if (setgroups(1, &egid) < 0) + msg_fatal("set_eugid: setgroups(%d): %m", egid); + if (euid != 0 && seteuid(euid) < 0) + msg_fatal("set_eugid: seteuid(%d): %m", euid); + if (msg_verbose) + msg_info("set_eugid: euid %d egid %d", euid, egid); +} diff --git a/postfix/util/set_eugid.h b/postfix/util/set_eugid.h new file mode 100644 index 000000000..e461cbfed --- /dev/null +++ b/postfix/util/set_eugid.h @@ -0,0 +1,29 @@ +#ifndef _SET_EUGID_H_INCLUDED_ +#define _SET_EUGID_H_INCLUDED_ + +/*++ +/* NAME +/* set_eugid 3h +/* SUMMARY +/* set effective user and group attributes +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern void set_eugid(uid_t, gid_t); + +/* 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/set_ugid.c b/postfix/util/set_ugid.c new file mode 100644 index 000000000..5d70a8627 --- /dev/null +++ b/postfix/util/set_ugid.c @@ -0,0 +1,58 @@ +/*++ +/* NAME +/* set_ugid 3 +/* SUMMARY +/* set real, effective and saved user and group attributes +/* SYNOPSIS +/* #include +/* +/* void set_ugid(uid, gid) +/* uid_t uid; +/* gid_t gid; +/* DESCRIPTION +/* set_ugid() sets the real, effective and saved user and group process +/* attributes and updates the process group access list to be just the +/* user's primary group. This operation is irreversible. +/* DIAGNOSTICS +/* All system call errors are fatal. +/* SEE ALSO +/* setuid(2), setgid(2), setgroups(2) +/* 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 + +/* Utility library. */ + +#include "msg.h" +#include "set_ugid.h" + +/* set_ugid - set real, effective and saved user and group attributes */ + +void set_ugid(uid_t uid, gid_t gid) +{ + if (geteuid() != 0) + if (seteuid(0) < 0) + msg_fatal("seteuid(0): %m"); + if (setgid(gid) < 0) + msg_fatal("setgid(%d): %m", gid); + if (setgroups(1, &gid) < 0) + msg_fatal("setgroups(1, &%d): %m", gid); + if (setuid(uid) < 0) + msg_fatal("setuid(%d): %m", uid); + if (msg_verbose > 1) + msg_info("setugid: uid %d gid %d", uid, gid); +} + diff --git a/postfix/util/set_ugid.h b/postfix/util/set_ugid.h new file mode 100644 index 000000000..e752beb92 --- /dev/null +++ b/postfix/util/set_ugid.h @@ -0,0 +1,29 @@ +#ifndef _SET_UGID_H_INCLUDED_ +#define _SET_UGID_H_INCLUDED_ + +/*++ +/* NAME +/* set_ugid 3h +/* SUMMARY +/* set real, effective and saved user and group attributes +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern void set_ugid(uid_t, gid_t); + +/* 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/sigdelay.c b/postfix/util/sigdelay.c new file mode 100644 index 000000000..c916fb46d --- /dev/null +++ b/postfix/util/sigdelay.c @@ -0,0 +1,117 @@ +/*++ +/* NAME +/* sigdelay 3 +/* SUMMARY +/* delay/resume signal delivery +/* SYNOPSIS +/* #include +/* +/* void sigdelay() +/* +/* void sigresume() +/* DESCRIPTION +/* sigdelay() delays delivery of signals. Signals that +/* arrive in the mean time will be queued. +/* +/* sigresume() resumes delivery of signals. Signals that have +/* arrived in the mean time will be delivered. +/* DIAGNOSTICS +/* All errors are fatal. +/* BUGS +/* The signal queue may be really short (as in: one per signal type). +/* +/* Some signals such as SIGKILL cannot be blocked. +/* 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 "msg.h" +#include "posix_signals.h" +#include "sigdelay.h" + +/* Application-specific. */ + +static sigset_t saved_sigmask; +static sigset_t block_sigmask; +static int suspending; +static int siginit_done; + +/* siginit - compute signal mask only once */ + +static void siginit(void) +{ + int sig; + + siginit_done = 1; + sigemptyset(&block_sigmask); + for (sig = 1; sig < NSIG; sig++) + sigaddset(&block_sigmask, sig); +} + +/* sigresume - deliver delayed signals and disable signal delay */ + +void sigresume(void) +{ + if (suspending != 0) { + suspending = 0; + if (sigprocmask(SIG_SETMASK, &saved_sigmask, (sigset_t *) 0) < 0) + msg_fatal("sigresume: sigprocmask: %m"); + } +} + +/* sigdelay - save signal mask and block all signals */ + +void sigdelay(void) +{ + if (siginit_done == 0) + siginit(); + if (suspending == 0) { + suspending = 1; + if (sigprocmask(SIG_BLOCK, &block_sigmask, &saved_sigmask) < 0) + msg_fatal("sigdelay: sigprocmask: %m"); + } +} + +#ifdef TEST + + /* + * Test program - press Ctrl-C twice while signal delivery is delayed, and + * see how many signals are delivered when signal delivery is resumed. + */ + +#include +#include + +static void gotsig(int sig) +{ + printf("Got signal %d\n", sig); +} + +int main(int unused_argc, int unused_argv) +{ + signal(SIGINT, gotsig); + signal(SIGQUIT, gotsig); + + printf("Delaying signal delivery\n"); + sigdelay(); + sleep(5); + printf("Resuming signal delivery\n"); + sigresume(); + exit(0); +} + +#endif diff --git a/postfix/util/sigdelay.h b/postfix/util/sigdelay.h new file mode 100644 index 000000000..d3b4ea33b --- /dev/null +++ b/postfix/util/sigdelay.h @@ -0,0 +1,31 @@ +#ifndef _SIGDELAY_H_INCLUDED_ +#define _SIGDELAY_H_INCLUDED_ + +/*++ +/* NAME +/* sigdelay 3h +/* SUMMARY +/* delay/resume signal delivery +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern void sigdelay(void); +extern void sigresume(void); + +/* 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/skipblanks.c b/postfix/util/skipblanks.c new file mode 100644 index 000000000..fc1928426 --- /dev/null +++ b/postfix/util/skipblanks.c @@ -0,0 +1,43 @@ +/*++ +/* NAME +/* skipblanks 3 +/* SUMMARY +/* skip leading whitespace +/* SYNOPSIS +/* #include +/* +/* char *skipblanks(string) +/* const char *string; +/* DESCRIPTION +/* skipblanks() returns a pointer to the first non-whitespace +/* character in the specified string, or a pointer to the string +/* terminator when the string contains all white-space characters. +/* 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 + +/* Utility library. */ + +#include "stringops.h" + +char *skipblanks(const char *string) +{ + const char *cp; + + for (cp = string; *cp != 0; cp++) + if (!ISSPACE(*cp)) + break; + return ((char *) cp); +} diff --git a/postfix/util/split_at.c b/postfix/util/split_at.c new file mode 100644 index 000000000..2264f3083 --- /dev/null +++ b/postfix/util/split_at.c @@ -0,0 +1,68 @@ +/*++ +/* NAME +/* split_at 3 +/* SUMMARY +/* trivial token splitter +/* SYNOPSIS +/* #include +/* +/* char *split_at(string, delimiter) +/* char *string; +/* int delimiter +/* +/* char *split_at_right(string, delimiter) +/* char *string; +/* int delimiter +/* DESCRIPTION +/* split_at() null-terminates the \fIstring\fR at the first +/* occurrence of the \fIdelimiter\fR character found, and +/* returns a pointer to the remainder. +/* +/* split_at_right() looks for the rightmost delimiter +/* occurrence, but is otherwise identical to split_at(). +/* HISTORY +/* .ad +/* .fi +/* A split_at() routine appears in the TCP Wrapper software +/* by Wietse Venema. +/* 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 libraries */ + +#include +#include + +/* Utility library. */ + +#include "split_at.h" + +/* split_at - break string at first delimiter, return remainder */ + +char *split_at(char *string, int delimiter) +{ + char *cp; + + if ((cp = strchr(string, delimiter)) != 0) + *cp++ = 0; + return (cp); +} + +/* split_at_right - break string at last delimiter, return remainder */ + +char *split_at_right(char *string, int delimiter) +{ + char *cp; + + if ((cp = strrchr(string, delimiter)) != 0) + *cp++ = 0; + return (cp); +} diff --git a/postfix/util/split_at.h b/postfix/util/split_at.h new file mode 100644 index 000000000..2d03ebb2c --- /dev/null +++ b/postfix/util/split_at.h @@ -0,0 +1,35 @@ +#ifndef _SPLIT_AT_H_INCLUDED_ +#define _SPLIT_AT_H_INCLUDED_ + +/*++ +/* NAME +/* split_at 3h +/* SUMMARY +/* trivial token splitter +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern char *split_at(char *, int); +extern char *split_at_right(char *, int); + +/* HISTORY +/* .ad +/* .fi +/* A split_at() routine appears in the TCP Wrapper software +/* by Wietse Venema. +/* 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/stat_as.c b/postfix/util/stat_as.c new file mode 100644 index 000000000..0f578a541 --- /dev/null +++ b/postfix/util/stat_as.c @@ -0,0 +1,72 @@ +/*++ +/* NAME +/* stat_as 3 +/* SUMMARY +/* stat file as user +/* SYNOPSIS +/* #include +/* #include +/* +/* int stat_as(path, st, euid, egid) +/* const char *path; +/* struct stat *st; +/* uid_t euid; +/* gid_t egid; +/* DESCRIPTION +/* stat_as() looks up the file status of the named \fIpath\fR, +/* using the effective rights specified by \fIeuid\fR +/* and \fIegid\fR, and stores the result into the structure pointed +/* to by \fIst\fR. A -1 result means the lookup failed. +/* This call follows symbolic links. +/* DIAGNOSTICS +/* Fatal error: no permission to change privilege level. +/* SEE ALSO +/* set_eugid(3) switch effective rights +/* 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 + +/* Utility library. */ + +#include "msg.h" +#include "set_eugid.h" +#include "stat_as.h" + +/* stat_as - stat file as user */ + +int stat_as(const char *path, struct stat * st, uid_t euid, gid_t egid) +{ + uid_t saved_euid = geteuid(); + gid_t saved_egid = getegid(); + int status; + + /* + * Switch to the target user privileges. + */ + set_eugid(euid, egid); + + /* + * Stat that file. + */ + status = stat(path, st); + + /* + * Restore saved privileges. + */ + set_eugid(saved_euid, saved_egid); + + return (status); +} diff --git a/postfix/util/stat_as.h b/postfix/util/stat_as.h new file mode 100644 index 000000000..aad37d30a --- /dev/null +++ b/postfix/util/stat_as.h @@ -0,0 +1,30 @@ +#ifndef _STAT_AS_H_INCLUDED_ +#define _STAT_AS_H_INCLUDED_ + +/*++ +/* NAME +/* stat_as 3h +/* SUMMARY +/* stat file as user +/* SYNOPSIS +/* #include +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern int stat_as(const char *, struct stat *, uid_t, gid_t); + +/* 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/stringops.h b/postfix/util/stringops.h new file mode 100644 index 000000000..854c02923 --- /dev/null +++ b/postfix/util/stringops.h @@ -0,0 +1,38 @@ +#ifndef _STRINGOPS_H_INCLUDED_ +#define _STRINGOPS_H_INCLUDED_ + +/*++ +/* NAME +/* stringops 3h +/* SUMMARY +/* string operations +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern char *printable(char *, int); +extern char *lowercase(char *); +extern char *skipblanks(const char *); +extern char *trimblanks(char *, int); +extern char *concatenate(const char *,...); +extern char *mystrtok(char **, const char *); +extern char *translit(char *, const char *, const char *); +#ifndef HAVE_BASENAME +extern char *basename(const char *); +#endif + +/* 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/sys_compat.c b/postfix/util/sys_compat.c new file mode 100644 index 000000000..28e39bc62 --- /dev/null +++ b/postfix/util/sys_compat.c @@ -0,0 +1,215 @@ +/*++ +/* NAME +/* sys_compat 3 +/* SUMMARY +/* compatibility routines +/* SYNOPSIS +/* #include +/* +/* const char *strerror(err) +/* int err; +/* +/* int setenv(name, value, clobber) +/* const char *name; +/* const char *value; +/* int clobber; +/* +/* int seteuid(euid) +/* uid_t euid; +/* +/* int setegid(egid) +/* gid_t euid; +/* +/* int mkfifo(path, mode) +/* char *path; +/* int mode; +/* +/* int waitpid(pid, statusp, options) +/* int pid; +/* WAIT_STATUS_T *statusp; +/* int options; +/* +/* int setsid() +/* DESCRIPTION +/* These routines are compiled for platforms that lack the functionality +/* or that have broken versions that we prefer to stay away from. +/* 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" + + /* + * ANSI strerror() emulation + */ +#ifdef MISSING_STRERROR + +extern int errno; +extern char *sys_errlist[]; +extern int sys_nerr; + +#include + +/* strerror - print text corresponding to error */ + +const char *strerror(int err) +{ + static VSTRING *buf; + + if (err < 0 || err >= sys_nerr) { + if (buf == 0) + buf = vstring_alloc(10); + vstring_sprintf(buf, "Unknown error %d", err); + return (vstring_str(buf)); + } else { + return (sys_errlist[errno]); + } +} + +#endif + + /* + * setenv() emulation on top of putenv(). + */ +#ifdef MISSING_SETENV + +#include +#include +#include + +/* setenv - update or insert environment (name,value) pair */ + +int setenv(const char *name, const char *value, int clobber) +{ + char *cp; + + if (clobber == 0 && getenv(name) != 0) + return (0); + if ((cp = malloc(strlen(name) + strlen(value) + 2)) == 0) + return (1); + sprintf(cp, "%s=%s", name, value); + return (putenv(cp)); +} + +#endif + + /* + * seteuid() and setegid() emulation, the HP-UX way + */ +#ifdef MISSING_SETEUID +#ifdef HAVE_SETRESUID +#include + +int seteuid(uid_t euid) +{ + return setresuid(-1, euid, -1); +} + +#else +#error MISSING_SETEUID +#endif + +#endif + +#ifdef MISSING_SETEGID +#ifdef HAVE_SETRESGID +#include + +int setegid(gid_t egid) +{ + return setresgid(-1, egid, -1); +} + +#else +#error MISSING_SETEGID +#endif + +#endif + + /* + * mkfifo() emulation - requires superuser privileges + */ +#ifdef MISSING_MKFIFO + +#include + +int mkfifo(char *path, int mode) +{ + return mknod(path, (mode & ~_S_IFMT) | _S_IFIFO, 0); +} + +#endif + + /* + * waitpid() emulation on top of Berkeley UNIX wait4() + */ +#ifdef MISSING_WAITPID +#ifdef HAS_WAIT4 + +#include +#include + +int waitpid(int pid, WAIT_STATUS_T *status, int options) +{ + if (pid == -1) + pid = 0; + return wait4(pid, status, options, (struct rusage *) 0); +} + +#else +#error MISSING_WAITPID +#endif + +#endif + + /* + * setsid() emulation, the Berkeley UNIX way + */ +#ifdef MISSING_SETSID + +#include +#include +#include +#include + +#ifdef TIOCNOTTY + +#include + +int setsid(void) +{ + int p = getpid(); + int fd; + + if (setpgrp(p, p)) + return -1; + + fd = open("/dev/tty", O_RDONLY, 0); + if (fd >= 0 || errno != ENXIO) { + if (fd < 0) { + msg_warn("open /dev/tty: %m"); + return -1; + } + if (ioctl(fd, TIOCNOTTY, 0)) { + msg_warn("ioctl TIOCNOTTY: %m"); + return -1; + } + close(fd); + } + return 0; +} + +#else +#error MISSING_SETSID +#endif + +#endif diff --git a/postfix/util/sys_defs.h b/postfix/util/sys_defs.h new file mode 100644 index 000000000..e91483080 --- /dev/null +++ b/postfix/util/sys_defs.h @@ -0,0 +1,638 @@ +#ifndef _SYS_DEFS_H_INCLUDED_ +#define _SYS_DEFS_H_INCLUDED_ + +/*++ +/* NAME +/* sys_defs 3h +/* SUMMARY +/* portability header +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Specific platforms. Major release numbers differ for a good reason. So be + * a good girl, plan for the future, and at least include the major release + * number in the system type (for example, SUNOS5 or FREEBSD2). The system + * type is determined by the makedefs shell script in the top-level + * 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) \ + || defined(BSDI2) || defined(BSDI3) || defined(BSDI4) \ + || defined(OPENBSD2) || defined(NETBSD1) +#define SUPPORTED +#include +#define USE_PATHS_H +#define USE_FLOCK_LOCK +#define HAS_SUN_LEN +#define HAS_FSYNC +#define HAS_DB +#define HAS_SA_LEN +#define DEF_DB_TYPE "hash" +#define ALIAS_DB_MAP "hash:/etc/aliases" +#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0) +#define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin" +#define USE_STATFS +#define STATFS_IN_SYS_MOUNT_H +#endif + +#if defined(OPENBSD2) +#define HAS_ISSETUGID +#endif + +#if defined(NETBSD1) +#define USE_DOT_LOCK +#endif + +#ifdef ULTRIX4 +#define SUPPORTED +#include +#define UNSAFE_CTYPE /* XXX verify */ +#define fpos_t long /* XXX verify */ +#define MISSING_SETENV /* XXX verify */ +#define MISSING_STRERROR /* XXX verify */ +#define _PATH_MAILDIR "/var/spool/mail" +#define _PATH_BSHELL "/bin/sh" +#define _PATH_DEFPATH "/usr/bin:/usr/ucb" +#define _PATH_STDPATH "/usr/bin:/usr/etc:/usr/ucb" +#define USE_FLOCK_LOCK +#define USE_DOT_LOCK +#define HAS_FSYNC +#define HAS_DBM +#define DEF_DB_TYPE "dbm" +#define ALIAS_DB_MAP "dbm:/etc/aliases" +extern int optind; /* XXX verify */ +extern char *optarg; /* XXX verify */ +extern int opterr; /* XXX verify */ + +#define HAS_NIS +#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0) +#define ROOT_PATH "/bin:/usr/bin:/etc:/usr/etc:/usr/ucb" +#define USE_STATFS /* XXX verify */ +#define STATFS_IN_SYS_VFS_H /* XXX verify */ +#define memmove(d,s,l) bcopy(s,d,l) /* XXX verify */ +#endif + +#ifdef OSF1 +#define SUPPORTED +#include +#define MISSING_SETENV +#define USE_PATHS_H +#define _PATH_DEFPATH "/usr/bin:/usr/ucb" +#define USE_FLOCK_LOCK +#define USE_DOT_LOCK +#define HAS_FSYNC +#define HAVE_BASENAME +#define HAS_DBM +#define DEF_DB_TYPE "dbm" +#define ALIAS_DB_MAP "dbm:/var/adm/sendmail/aliases" +extern int optind; /* XXX use */ +extern char *optarg; /* XXX use */ +extern int opterr; /* XXX use */ + +#define HAS_NIS +#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0) +#define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin:/usr/ucb" +#define USE_STATFS +#define STATFS_IN_SYS_MOUNT_H +#endif + +#ifdef SUNOS4 +#define SUPPORTED +#include +#define UNSAFE_CTYPE +#define fpos_t long +#define MISSING_SETENV +#define MISSING_STRERROR +#define _PATH_MAILDIR "/var/spool/mail" +#define _PATH_BSHELL "/bin/sh" +#define _PATH_DEFPATH "/usr/bin:/usr/ucb" +#define _PATH_STDPATH "/usr/bin:/usr/etc:/usr/ucb" +#define USE_FLOCK_LOCK +#define USE_DOT_LOCK +#define HAS_FSYNC +#define HAS_DBM +#define DEF_DB_TYPE "dbm" +#define ALIAS_DB_MAP "dbm:/etc/aliases" +extern int optind; +extern char *optarg; +extern int opterr; + +#define HAS_NIS +#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0) +#define ROOT_PATH "/bin:/usr/bin:/etc:/usr/etc:/usr/ucb" +#define USE_STATFS +#define STATFS_IN_SYS_VFS_H +#define memmove(d,s,l) bcopy(s,d,l) +#endif + +#ifdef SUNOS5 +#define SUPPORTED +#define _SVID_GETTOD /* Solaris 2.5, XSH4.2 versus SVID */ +#include +#define MISSING_SETENV +#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 USE_FCNTL_LOCK +#define USE_DOT_LOCK +#define HAS_FSYNC +#define HAS_DBM +#define DEF_DB_TYPE "dbm" +#define ALIAS_DB_MAP "dbm:/etc/mail/aliases" +#define HAS_NIS +#define USE_SYS_SOCKIO_H /* Solaris 2.5, changed sys/ioctl.h */ +#define GETTIMEOFDAY(t) gettimeofday(t) +#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 /* Solaris 2.5.1, reportedly */ +#endif + +#ifdef UW7 /* UnixWare 7 */ +#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" +#define HAS_NIS +#define USE_SYS_SOCKIO_H +#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0) +#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 +#define MISSING_SETENV +#define _PATH_BSHELL "/bin/sh" +#define _PATH_MAILDIR "/var/spool/mail" /* paths.h lies */ +#define _PATH_DEFPATH "/usr/bin:/usr/ucb" +#define _PATH_STDPATH "/usr/bin:/usr/sbin:/usr/ucb" +#define USE_FCNTL_LOCK +#define USE_DOT_LOCK +#define USE_SYS_SELECT_H +#define HAS_FSYNC +#define HAS_DBM +#define DEF_DB_TYPE "dbm" +#define ALIAS_DB_MAP "dbm:/etc/aliases" +#define HAS_NIS +#define HAS_SA_LEN +#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0) +#define RESOLVE_H_NEEDS_STDIO_H +#define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin:/usr/ucb" +#define SOCKADDR_SIZE size_t +#define SOCKOPT_SIZE size_t +#define USE_STATVFS +#define STATVFS_IN_SYS_STATVFS_H +#define STRCASECMP_IN_STRINGS_H +extern time_t time(time_t *); +extern int seteuid(uid_t); +extern int setegid(gid_t); +extern int initgroups(const char *, int); + +#endif + +#if defined(IRIX5) || defined(IRIX6) +#define SUPPORTED +#include +#define MISSING_SETENV +#define _PATH_MAILDIR "/var/mail" +#define _PATH_BSHELL "/bin/sh" +#define _PATH_DEFPATH "/usr/bin:/usr/bsd" +#define _PATH_STDPATH "/usr/bin:/usr/sbin:/usr/bsd" +#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/aliases" +#define HAS_NIS +#define USE_SYS_SOCKIO_H /* XXX check */ +#define GETTIMEOFDAY(t) gettimeofday(t) +#define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin:/usr/bsd" +#define FIONREAD_IN_SYS_FILIO_H /* XXX check */ +#define DBM_NO_TRAILING_NULL /* XXX check */ +#define USE_STATVFS +#define STATVFS_IN_SYS_STATVFS_H +#endif + +#ifdef LINUX2 +#define SUPPORTED +#include +#define USE_PATHS_H +#define USE_FLOCK_LOCK +#define HAS_FSYNC +#define HAS_DB +#define DEF_DB_TYPE "hash" +#define ALIAS_DB_MAP "hash:/etc/aliases" +#define HAS_NIS +#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0) +#define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin" +#define FIONREAD_IN_TERMIOS_H +#define USE_STATFS +#define STATFS_IN_SYS_VFS_H +#define UNIX_DOMAIN_CONNECT_BLOCKS_FOR_ACCEPT +#define PREPEND_PLUS_TO_OPTSTRING +#endif + + /* + * HPUX11 was copied from HPUX10, but can perhaps be trimmed down a bit. + */ +#ifdef HPUX11 +#define SUPPORTED +#define USE_SIG_RETURN +#include +#define HAS_DBM +#define USE_FCNTL_LOCK +#define HAS_FSYNC +#define DEF_DB_TYPE "dbm" +#define ALIAS_DB_MAP "dbm:/etc/mail/aliases" +#define ROOT_PATH "/usr/bin:/sbin:/usr/sbin" +#define MISSING_SETENV +#define HAS_NIS +#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0) +#define _PATH_BSHELL "/bin/sh" +#define _PATH_MAILDIR "/var/mail" +#define _PATH_DEFPATH "/usr/bin" +#define _PATH_STDPATH "/usr/bin:/sbin:/usr/sbin" +#define MISSING_SETEUID +#define HAVE_SETRESUID +#define MISSING_SETEGID +#define HAVE_SETRESGID +extern int h_errno; /* imports too much stuff */ + +#define USE_STATFS +#define STATFS_IN_SYS_VFS_H +#endif + +#ifdef HPUX10 +#define SUPPORTED +#define USE_SIG_RETURN +#include +#define HAS_DBM +#define USE_FCNTL_LOCK +#define HAS_FSYNC +#define DEF_DB_TYPE "dbm" +#define ALIAS_DB_MAP "dbm:/etc/mail/aliases" +#define ROOT_PATH "/usr/bin:/sbin:/usr/sbin" +#define MISSING_SETENV +#define HAS_NIS +#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0) +#define _PATH_BSHELL "/bin/sh" +#define _PATH_MAILDIR "/var/mail" +#define _PATH_DEFPATH "/usr/bin" +#define _PATH_STDPATH "/usr/bin:/sbin:/usr/sbin" +#define MISSING_SETEUID +#define HAVE_SETRESUID +#define MISSING_SETEGID +#define HAVE_SETRESGID +extern int h_errno; /* imports too much stuff */ + +#define USE_STATFS +#define STATFS_IN_SYS_VFS_H +#endif + +#ifdef HPUX9 +#define SUPPORTED +#define USE_SIG_RETURN +#include +#define HAS_DBM +#define USE_FCNTL_LOCK +#define HAS_FSYNC +#define HAS_NIS +#define MISSING_SETENV +#define MISSING_RLIMIT_FSIZE +#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0) +#define DEF_DB_TYPE "dbm" +#define ALIAS_DB_MAP "dbm:/usr/lib/aliases" +#define ROOT_PATH "/bin:/usr/bin:/etc" +#define _PATH_BSHELL "/bin/sh" +#define _PATH_MAILDIR "/usr/mail" +#define _PATH_DEFPATH "/bin:/usr/bin" +#define _PATH_STDPATH "/bin:/usr/bin:/etc" +#define MISSING_SETEUID +#define HAVE_SETRESUID +#define MISSING_SETEGID +#define HAVE_SETRESGID +extern int h_errno; + +#define USE_ULIMIT /* no setrlimit() */ +#define USE_STATFS +#define STATFS_IN_SYS_VFS_H +#endif + + /* + * NEXTSTEP3, without -lposix, because its naming service is broken. + */ +#ifdef NEXTSTEP3 +#define SUPPORTED +#include +#define HAS_DBM +#define USE_FLOCK_LOCK +#define USE_STATFS +#define HAVE_SYS_DIR_H +#define STATFS_IN_SYS_VFS_H +#define HAS_FSYNC +#define HAS_NIS +#define HAS_NETINFO +#define MISSING_SETENV_PUTENV +#define MISSING_MKFIFO +#define MISSING_SIGSET_T +#define MISSING_SIGACTION +#define MISSING_STD_FILENOS +#define MISSING_SETSID +#define MISSING_WAITPID +#define MISSING_UTIMBUF +#define HAS_WAIT4 +#define WAIT_STATUS_T union wait +#define NORMAL_EXIT_STATUS(x) (WIFEXITED(x) && !WEXITSTATUS (x)) +#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0) +#define _PATH_MAILDIR "/usr/spool/mail" +#define _PATH_BSHELL "/bin/sh" +#define _PATH_DEFPATH "/bin:/usr/bin:/usr/ucb" +#define _PATH_STDPATH "/bin:/usr/bin:/usr/ucb" +#define ROOT_PATH "/bin:/usr/bin:/usr/etc:/usr/ucb" +#define DEF_DB_TYPE "dbm" +#define ALIAS_DB_MAP "dbm:/etc/sendmail/aliases" +#include +#define MISSING_POSIX_S_IS +#define MISSING_POSIX_S_MODES +/* It's amazing what is all missing... */ +#define isascii(c) ((unsigned)(c)<=0177) +extern int opterr; + +#define MISSING_PID_T +#define MISSING_STRFTIME_E +#define FD_CLOEXEC 1 +#define O_NONBLOCK O_NDELAY +#define WEXITSTATUS(x) ((x).w_retcode) +#define WTERMSIG(x) ((x).w_termsig) +#endif + + /* + * OPENSTEP does not have posix (some fix...) + */ +#ifdef OPENSTEP4 +#define SUPPORTED +#include +#define HAS_DBM +#define USE_FLOCK_LOCK +#define USE_STATFS +#define HAVE_SYS_DIR_H +#define STATFS_IN_SYS_VFS_H +#define HAS_FSYNC +#define HAS_NIS +#define HAS_NETINFO +#define MISSING_SETENV_PUTENV +#define MISSING_MKFIFO +#define MISSING_SIGSET_T +#define MISSING_SIGACTION +#define MISSING_STD_FILENOS +#define MISSING_SETSID +#define MISSING_WAITPID +#define MISSING_UTIMBUF +#define HAS_WAIT4 +#define WAIT_STATUS_T union wait +#define NORMAL_EXIT_STATUS(x) (WIFEXITED(x) && !WEXITSTATUS (x)) +#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0) +#define _PATH_MAILDIR "/usr/spool/mail" +#define _PATH_BSHELL "/bin/sh" +#define _PATH_DEFPATH "/bin:/usr/bin:/usr/ucb" +#define _PATH_STDPATH "/bin:/usr/bin:/usr/ucb" +#define ROOT_PATH "/bin:/usr/bin:/usr/etc:/usr/ucb" +#define DEF_DB_TYPE "dbm" +#define ALIAS_DB_MAP "dbm:/etc/sendmail/aliases" +#include +#define MISSING_POSIX_S_IS +#define MISSING_POSIX_S_MODES +/* It's amazing what is all missing... */ +#define isascii(c) ((unsigned)(c)<=0177) +extern int opterr; + +#define MISSING_PID_T +#define MISSING_STRFTIME_E +#define FD_CLOEXEC 1 +#define O_NONBLOCK O_NDELAY +#define WEXITSTATUS(x) ((x).w_retcode) +#define WTERMSIG(x) ((x).w_termsig) +#define NORETURN /* the native compiler */ +#endif + + /* + * We're not going to try to guess like configure does. + */ +#ifndef SUPPORTED +#error "unsupported platform" +#endif + +#ifdef PREPEND_PLUS_TO_OPTSTRING +#define GETOPT(argc, argv, str) getopt((argc), (argv), "+" str) +#else +#define GETOPT(argc, argv, str) getopt((argc), (argv), (str)) +#endif + +#if defined(USE_FCNTL_LOCK) && defined(USE_FLOCK_LOCK) +#error "define USE_FCNTL_LOCK or USE_FLOCK_LOCK, not both" +#endif + +#if !defined(USE_FCNTL_LOCK) && !defined(USE_FLOCK_LOCK) +#error "define USE_FCNTL_LOCK or USE_FLOCK_LOCK" +#endif + +#if defined(USE_STATFS) && defined(USE_STATVFS) +#error "define USE_STATFS or USE_STATVFS, not both" +#endif + +#if !defined(USE_STATFS) && !defined(USE_STATVFS) +#error "define USE_STATFS or USE_STATVFS" +#endif + + /* + * Defaults for normal systems. + */ +#ifndef SOCKADDR_SIZE +#define SOCKADDR_SIZE int +#endif + +#ifndef SOCKOPT_SIZE +#define SOCKOPT_SIZE int +#endif + +#if !defined (HAVE_SYS_NDIR_H) && !defined (HAVE_SYS_DIR_H) \ + && !defined (HAVE_NDIR_H) +#define HAVE_DIRENT_H +#endif + +#ifndef WAIT_STATUS_T +typedef int WAIT_STATUS_T; + +#define NORMAL_EXIT_STATUS(status) ((status) == 0) +#endif + + /* + * Turn on the compatibility stuff. + */ +#ifdef MISSING_UTIMBUF +struct utimbuf { + time_t actime; + time_t modtime; +}; + +#endif + +#ifdef MISSING_STRERROR +extern const char *strerror(int); + +#endif + +#if defined (MISSING_SETENV) || defined (MISSING_SETENV_PUTENV) +extern int setenv(const char *, const char *, int); + +#endif + +#ifdef MISSING_SETEUID +extern int seteuid(uid_t euid); + +#endif + +#ifdef MISSING_SETEGID +extern int setegid(gid_t egid); + +#endif + +#ifdef MISSING_MKFIFO +extern int mkfifo(char *, int); + +#endif + +#ifdef MISSING_WAITPID +extern int waitpid(int, WAIT_STATUS_T *status, int options); + +#endif + +#ifdef MISSING_SETSID +extern int setsid(void); + +#endif + +#ifdef MISSING_STD_FILENOS +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#endif + +#ifdef MISSING_PID_T +typedef int pid_t; + +#endif + +#ifdef MISSING_POSIX_S_IS +#define S_ISBLK(mode) (((mode) & (_S_IFMT)) == (_S_IFBLK)) +#define S_ISCHR(mode) (((mode) & (_S_IFMT)) == (_S_IFCHR)) +#define S_ISDIR(mode) (((mode) & (_S_IFMT)) == (_S_IFDIR)) +#define S_ISSOCK(mode) (((mode) & (_S_IFMT)) == (_S_IFSOCK)) +#define S_ISFIFO(mode) (((mode) & (_S_IFMT)) == (_S_IFIFO)) +#define S_ISREG(mode) (((mode) & (_S_IFMT)) == (_S_IFREG)) +#endif + +#ifdef MISSING_POSIX_S_MODES +#define S_IRUSR _S_IRUSR +#define S_IRGRP 0000040 +#define S_IROTH 0000004 +#define S_IWUSR _S_IWUSR +#define S_IWGRP 0000020 +#define S_IWOTH 0000002 +#define S_IXUSR _S_IXUSR +#define S_IXGRP 0000010 +#define S_IXOTH 0000001 +#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) +#endif + + /* + * Need to specify what functions never return, so that the compiler can + * warn for missing initializations and other trouble. However, OPENSTEP4 + * gcc 2.7.x cannot handle this so we define this only if NORETURN isn't + * already defined above. + */ +#ifndef NORETURN +#if __GNUC__ == 2 && __GNUC_MINOR__ >= 5 || __GNUC__ >= 3 +#define NORETURN void __attribute__((__noreturn__)) +#endif +#endif + +#ifndef NORETURN +#define NORETURN void +#endif + + /* + * Making the ctype.h macros not more expensive than necessary. On some + * systems, ctype.h misbehaves badly with signed characters. + */ +#define _UCHAR_(c) ((unsigned char)(c)) +#ifdef UNSAFE_CTYPE +#define ISASCII(c) isascii(_UCHAR_(c)) +#define ISALNUM(c) (ISASCII(c) && isalnum(c)) +#define ISALPHA(c) (ISASCII(c) && isalpha(c)) +#define ISCNTRL(c) (ISASCII(c) && iscntrl(c)) +#define ISDIGIT(c) (ISASCII(c) && isdigit(c)) +#define ISGRAPH(c) (ISASCII(c) && isgraph(c)) +#define ISLOWER(c) (ISASCII(c) && islower(c)) +#define ISPRINT(c) (ISASCII(c) && isprint(c)) +#define ISPUNCT(c) (ISASCII(c) && ispunct(c)) +#define ISSPACE(c) (ISASCII(c) && isspace(c)) +#define ISUPPER(c) (ISASCII(c) && isupper(c)) +#define TOLOWER(c) (ISUPPER(c) ? tolower(c) : (c)) +#define TOUPPER(c) (ISLOWER(c) ? toupper(c) : (c)) +#else +#define ISASCII(c) isascii(_UCHAR_(c)) +#define ISALNUM(c) isalnum(_UCHAR_(c)) +#define ISALPHA(c) isalpha(_UCHAR_(c)) +#define ISCNTRL(c) iscntrl(_UCHAR_(c)) +#define ISDIGIT(c) isdigit(_UCHAR_(c)) +#define ISGRAPH(c) isgraph(_UCHAR_(c)) +#define ISLOWER(c) islower(_UCHAR_(c)) +#define ISPRINT(c) isprint(_UCHAR_(c)) +#define ISPUNCT(c) ispunct(_UCHAR_(c)) +#define ISSPACE(c) isspace(_UCHAR_(c)) +#define ISUPPER(c) isupper(_UCHAR_(c)) +#define TOLOWER(c) tolower(_UCHAR_(c)) +#define TOUPPER(c) toupper(_UCHAR_(c)) +#endif + + /* + * Scaffolding. I don't want to lose messages while the program is under + * development. + */ +extern int REMOVE(const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/util/timed_connect.c b/postfix/util/timed_connect.c new file mode 100644 index 000000000..c40fe3e63 --- /dev/null +++ b/postfix/util/timed_connect.c @@ -0,0 +1,110 @@ +/*++ +/* NAME +/* timed_connect 3 +/* SUMMARY +/* connect operation with timeout +/* SYNOPSIS +/* #include +/* #include +/* +/* int timed_connect(fd, buf, buf_len, timeout) +/* int fd; +/* struct sockaddr *buf; +/* unsigned buf_len; +/* int timeout; +/* DESCRIPTION +/* timed_connect() implement a BSD socket connect() operation that is +/* bounded in time. +/* +/* Arguments: +/* .IP fd +/* File descriptor in the range 0..FD_SETSIZE. This descriptor +/* must be set to non-blocking mode prior to calling timed_connect(). +/* .IP buf +/* Socket address buffer pointer. +/* .IP buf_len +/* Size of socket address buffer. +/* .IP timeout +/* The deadline in seconds. This must be a number > 0. +/* DIAGNOSTICS +/* Panic: interface violations. +/* When the operation does not complete within the deadline, the +/* result value is -1, and errno is set to ETIMEDOUT. +/* All other returns are identical to those of a blocking connect(2) +/* operation. +/* WARNINGS +/* .ad +/* .fi +/* A common error is to call timed_connect() without enabling +/* non-blocking I/O on the socket. In that case, the \fItimeout\fR +/* parameter takes no effect. +/* 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 + +/* Utility library. */ + +#include "msg.h" +#include "iostuff.h" +#include "timed_connect.h" + +/* timed_connect - connect with deadline */ + +int timed_connect(int sock, struct sockaddr * sa, int len, int timeout) +{ + int error; + SOCKOPT_SIZE error_len; + + /* + * Sanity check. Just like with timed_wait(), the timeout must be a + * positive number. + */ + if (timeout <= 0) + msg_panic("timed_connect: bad timeout: %d", timeout); + + /* + * Start the connection, and handle all possible results. + */ + if (connect(sock, sa, len) == 0) + return (0); + if (errno != EINPROGRESS) + return (-1); + + /* + * A connection is in progress. Wait for a limited amount of time for + * something to happen. If nothing happens, report an error. + */ + if (write_wait(sock, timeout) < 0) + return (-1); + + /* + * Something happened. Some Solaris 2 versions have getsockopt() itself + * return the error, instead of returning it via the parameter list. + */ + error = 0; + error_len = sizeof(error); + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &error, &error_len) < 0) + return (-1); + if (error) { + errno = error; + return (-1); + } + + /* + * No problems. + */ + return (0); +} diff --git a/postfix/util/timed_connect.h b/postfix/util/timed_connect.h new file mode 100644 index 000000000..eb2802f98 --- /dev/null +++ b/postfix/util/timed_connect.h @@ -0,0 +1,35 @@ +#ifndef _TIMED_CONNECT_H_INCLUDED_ +#define _TIMED_CONNECT_H_INCLUDED_ + +/*++ +/* NAME +/* timed_connect 3h +/* SUMMARY +/* connect operation with timeout +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * External interface. + */ +extern int timed_connect(int, struct sockaddr *, int, 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/timed_wait.c b/postfix/util/timed_wait.c new file mode 100644 index 000000000..6e8293fb2 --- /dev/null +++ b/postfix/util/timed_wait.c @@ -0,0 +1,119 @@ +/*++ +/* NAME +/* timed_wait 3 +/* SUMMARY +/* wait operations with timeout +/* SYNOPSIS +/* #include +/* +/* int timed_waitpid(pid, statusp, options, time_limit) +/* pid_t pid; +/* WAIT_STATUS_T *statusp; +/* int options; +/* int time_limit; +/* DESCRIPTION +/* \fItimed_waitpid\fR() waits at most \fItime_limit\fR seconds +/* for process termination. +/* +/* Arguments: +/* .IP "pid, statusp, options" +/* The process ID, status pointer and options passed to waitpid(3). +/* .IP time_limit +/* The time in seconds that timed_waitpid() will wait. +/* This must be a number > 0. +/* DIAGNOSTICS +/* Panic: interface violation. +/* +/* When the time limit is exceeded, the result is -1 and errno +/* is set to ETIMEDOUT. Otherwise, the result value is the result +/* from the underlying waitpid() routine. +/* BUGS +/* If there were a \fIportable\fR way to select() on process status +/* information, these routines would not have to use a steenkeeng +/* alarm() timer and signal() handler. +/* 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 + +/* Utility library. */ + +#include +#include +#include + +/* Application-specific. */ + +static int timed_wait_expired; + +/* timed_wait_alarm - timeout handler */ + +static void timed_wait_alarm(int unused_sig) +{ + + /* + * WARNING WARNING WARNING. + * + * This code runs at unpredictable moments, as a signal handler. This code + * is here only so that we can break out of waitpid(). Don't put any code + * here other than for setting a global flag. + */ + timed_wait_expired = 1; +} + +/* timed_waitpid - waitpid with time limit */ + +int timed_waitpid(pid_t pid, WAIT_STATUS_T *statusp, int options, + int time_limit) +{ + char *myname = "timed_waitpid"; + struct sigaction action; + struct sigaction old_action; + int wpid; + + /* + * Sanity checks. + */ + if (time_limit <= 0) + msg_panic("%s: bad time limit: %d", myname, time_limit); + + /* + * Set up a timer. + */ + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + action.sa_handler = timed_wait_alarm; + if (sigaction(SIGALRM, &action, &old_action) < 0) + msg_fatal("%s: sigaction(SIGALRM): %m", myname); + timed_wait_expired = 0; + alarm(time_limit); + + /* + * Wait for only a limited amount of time. + */ + if ((wpid = waitpid(pid, statusp, options)) < 0 && timed_wait_expired) + errno = ETIMEDOUT; + + /* + * Cleanup. + */ + alarm(0); + if (sigaction(SIGALRM, &old_action, (struct sigaction *) 0) < 0) + msg_fatal("%s: sigaction(SIGALRM): %m", myname); + + return (wpid); +} diff --git a/postfix/util/timed_wait.h b/postfix/util/timed_wait.h new file mode 100644 index 000000000..bdbc270c6 --- /dev/null +++ b/postfix/util/timed_wait.h @@ -0,0 +1,30 @@ +#ifndef _TIMED_WAIT_H_INCLUDED_ +#define _TIMED_WAIT_H_INCLUDED_ + +/*++ +/* NAME +/* timed_wait 3h +/* SUMMARY +/* wait operations with timeout +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern int timed_waitpid(pid_t, WAIT_STATUS_T *, int, 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/translit.c b/postfix/util/translit.c new file mode 100644 index 000000000..ba04bf22c --- /dev/null +++ b/postfix/util/translit.c @@ -0,0 +1,86 @@ +/*++ +/* NAME +/* translit 3 +/* SUMMARY +/* transliterate characters +/* SYNOPSIS +/* #include +/* +/* char *translit(buf, original, replacement) +/* char *buf; +/* char *original; +/* char *replacement; +/* DESCRIPTION +/* translit() takes a null-terminated string, and replaces characters +/* given in its \fIoriginal\fR argument by the corresponding characters +/* in the \fIreplacement\fR string. The result value is the \fIbuf\fR +/* argument. +/* BUGS +/* Cannot replace null characters. +/* 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 + +/* Utility library. */ + +#include "stringops.h" + +char *translit(char *string, const char *original, const char *replacement) +{ + char *cp; + const char *op; + + /* + * For large inputs, should use a lookup table. + */ + for (cp = string; *cp != 0; cp++) { + for (op = original; *op != 0; op++) { + if (*cp == *op) { + *cp = replacement[op - original]; + break; + } + } + } + return (string); +} + +#ifdef TEST + + /* + * Usage: translit string1 string2 + * + * test program to perform the most basic operation of the UNIX tr command. + */ +#include +#include +#include +#include + +#define STR vstring_str + +int main(int argc, char **argv) +{ + VSTRING *buf = vstring_alloc(100); + + if (argc != 3) + msg_fatal("usage: %s string1 string2", argv[0]); + while (vstring_fgets(buf, VSTREAM_IN)) + vstream_fputs(translit(STR(buf), argv[1], argv[2]), VSTREAM_OUT); + vstream_fflush(VSTREAM_OUT); + vstring_free(buf); + return (0); +} + +#endif diff --git a/postfix/util/trigger.h b/postfix/util/trigger.h new file mode 100644 index 000000000..1f266a747 --- /dev/null +++ b/postfix/util/trigger.h @@ -0,0 +1,32 @@ +#ifndef _TRIGGER_H_INCLUDED_ +#define _TRIGGER_H_INCLUDED_ + +/*++ +/* NAME +/* trigger 3h +/* SUMMARY +/* client interface file +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern int unix_trigger(const char *, const char *, int, int); +extern int inet_trigger(const char *, const char *, int, int); +extern int fifo_trigger(const char *, const char *, int, 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/trimblanks.c b/postfix/util/trimblanks.c new file mode 100644 index 000000000..8d0eae8a6 --- /dev/null +++ b/postfix/util/trimblanks.c @@ -0,0 +1,50 @@ +/*++ +/* NAME +/* trimblanks 3 +/* SUMMARY +/* skip leading whitespace +/* SYNOPSIS +/* #include +/* +/* char *trimblanks(string, len) +/* char *string; +/* int len; +/* DESCRIPTION +/* trimblanks() returns a pointer to the beginning of the trailing +/* whitespace in \fIstring\fR, or a pointer to the string terminator +/* when the string contains no trailing whitespace. +/* The \fIlen\fR argument is either zero or the string length. +/* 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 + +/* Utility library. */ + +#include "stringops.h" + +char *trimblanks(char *string, int len) +{ + char *curr; + + if (len) { + curr = string + len; + } else { + for (curr = string; *curr != 0; curr++) + /* void */ ; + } + while (curr > string && ISSPACE(curr[-1])) + curr -= 1; + return (curr); +} diff --git a/postfix/util/unix_connect.c b/postfix/util/unix_connect.c new file mode 100644 index 000000000..0890f7e1c --- /dev/null +++ b/postfix/util/unix_connect.c @@ -0,0 +1,109 @@ +/*++ +/* NAME +/* unix_connect 3 +/* SUMMARY +/* connect to UNIX-domain listener +/* SYNOPSIS +/* #include +/* +/* int unix_connect(addr, block_mode, timeout) +/* const char *addr; +/* int block_mode; +/* int timeout; +/* DESCRIPTION +/* unix_connect() connects to a listener in the UNIX domain at the +/* specified address, and returns the resulting file descriptor. +/* +/* Arguments: +/* .IP addr +/* Null-terminated string with connection destination. +/* .IP block_mode +/* Either NON_BLOCKING for a non-blocking socket, or BLOCKING for +/* blocking mode. +/* .IP timeout +/* Bounds the number of seconds that the operation may take. Specify +/* a value <= 0 to disable the time limit. +/* DIAGNOSTICS +/* The result is -1 in case the connection could not be made. +/* Fatal errors: other system call failures. +/* 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 interfaces. */ + +#include +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include "msg.h" +#include "iostuff.h" +#include "connect.h" +#include "timed_connect.h" + +/* unix_connect - connect to UNIX-domain listener */ + +int unix_connect(const char *addr, int block_mode, int timeout) +{ +#undef sun + struct sockaddr_un sun; + int len = strlen(addr); + int sock; + + /* + * Translate address information to internal form. + */ + if (len >= (int) sizeof(sun.sun_path)) + msg_fatal("unix-domain name too long: %s", addr); + memset((char *) &sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; +#ifdef HAS_SUN_LEN + sun.sun_len = len + 1; +#endif + memcpy(sun.sun_path, addr, len + 1); + + /* + * Create a client socket. + */ + if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + msg_fatal("socket: %m"); + + /* + * Timed connect. + */ + if (timeout > 0) { + non_blocking(sock, NON_BLOCKING); + if (timed_connect(sock, (struct sockaddr *) & sun, sizeof(sun), timeout) < 0) { + close(sock); + return (-1); + } + if (block_mode != NON_BLOCKING) + non_blocking(sock, block_mode); + return (sock); + } + + /* + * Maybe block until connected. + */ + else { + non_blocking(sock, block_mode); + if (connect(sock, (struct sockaddr *) & sun, sizeof(sun)) < 0 + && errno != EINPROGRESS) { + close(sock); + return (-1); + } + return (sock); + } +} diff --git a/postfix/util/unix_listen.c b/postfix/util/unix_listen.c new file mode 100644 index 000000000..9c00710f2 --- /dev/null +++ b/postfix/util/unix_listen.c @@ -0,0 +1,95 @@ +/*++ +/* NAME +/* unix_listen 3 +/* SUMMARY +/* start UNIX-domain listener +/* SYNOPSIS +/* #include +/* +/* int unix_listen(addr, backlog, block_mode) +/* const char *addr; +/* int backlog; +/* int block_mode; +/* DESCRIPTION +/* The \fBunix_listen\fR() routine starts a listener in the UNIX domain +/* on the specified address, with the specified backlog, and returns +/* the resulting file descriptor. +/* +/* Arguments: +/* .IP addr +/* Null-terminated string with connection destination. +/* .IP backlog +/* This argument is passed on to the \fIlisten(2)\fR routine. +/* .IP block_mode +/* Either NON_BLOCKING for a non-blocking socket, or BLOCKING for +/* blocking mode. +/* DIAGNOSTICS +/* Fatal errors: all errors are fatal. +/* 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 interfaces. */ + +#include +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include "msg.h" +#include "iostuff.h" +#include "listen.h" + +/* unix_listen - create UNIX-domain listener */ + +int unix_listen(const char *addr, int backlog, int block_mode) +{ +#undef sun + struct sockaddr_un sun; + int len = strlen(addr); + int sock; + + /* + * Translate address information to internal form. + */ + if (len >= (int) sizeof(sun.sun_path)) + msg_fatal("unix-domain name too long: %s", addr); + memset((char *) &sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; +#ifdef HAS_SUN_LEN + sun.sun_len = len + 1; +#endif + memcpy(sun.sun_path, addr, len + 1); + + /* + * Create a listener socket. Do whatever we can so we don't run into + * trouble when this process is restarted after crash. + */ + if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + msg_fatal("socket: %m"); + (void) unlink(addr); + if (bind(sock, (struct sockaddr *) & sun, sizeof(sun)) < 0) + msg_fatal("bind: %s: %m", addr); +#ifdef FCHMOD_UNIX_SOCKETS + if (fchmod(sock, 0666) < 0) + msg_fatal("fchmod socket %s: %m", addr); +#else + if (chmod(addr, 0666) < 0) + msg_fatal("chmod socket %s: %m", addr); +#endif + non_blocking(sock, block_mode); + if (listen(sock, backlog) < 0) + msg_fatal("listen: %m"); + return (sock); +} diff --git a/postfix/util/unix_trigger.c b/postfix/util/unix_trigger.c new file mode 100644 index 000000000..d0f03d6c7 --- /dev/null +++ b/postfix/util/unix_trigger.c @@ -0,0 +1,89 @@ +/*++ +/* NAME +/* unix_trigger 3 +/* SUMMARY +/* wakeup UNIX-domain server +/* SYNOPSIS +/* #include +/* +/* int unix_trigger(service, buf, len, timeout) +/* const char *service; +/* const char *buf; +/* int len; +/* int timeout; +/* DESCRIPTION +/* unix_trigger() wakes up the named UNIX-domain server by making +/* a brief connection to it and writing the named buffer. +/* +/* Arguments: +/* .IP service +/* Name of the communication endpoint. +/* .IP buf +/* Address of data to be written. +/* .IP len +/* Amount of data to be written. +/* .IP timeout +/* Deadline in seconds. Specify a value <= 0 to disable +/* the time limit. +/* DIAGNOSTICS +/* The result is zero in case of success, -1 in case of problems. +/* SEE ALSO +/* unix_connect(3), UNIX-domain client +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include + +/* unix_trigger - wakeup UNIX-domain server */ + +int unix_trigger(const char *service, const char *buf, int len, int timeout) +{ + char *myname = "unix_trigger"; + int fd; + + if (msg_verbose > 1) + msg_info("%s: service %s", myname, service); + + /* + * Connect... + */ + if ((fd = unix_connect(service, BLOCKING, timeout)) < 0) { + if (msg_verbose) + msg_warn("%s: connect to %s: %m", myname, service); + return (-1); + } + + /* + * Write the request... + */ + if (write_buf(fd, buf, len, timeout) < 0) + if (msg_verbose) + msg_warn("%s: write to %s: %m", myname, service); + + /* + * Disconnect. + */ + if (close(fd) < 0) + if (msg_verbose) + msg_warn("%s: close %s: %m", myname, service); + return (0); +} diff --git a/postfix/util/unsafe.c b/postfix/util/unsafe.c new file mode 100644 index 000000000..8d3f34ad0 --- /dev/null +++ b/postfix/util/unsafe.c @@ -0,0 +1,54 @@ +/*++ +/* NAME +/* unsafe 3 +/* SUMMARY +/* are we running at non-user privileges +/* SYNOPSIS +/* #include +/* +/* int unsafe() +/* DESCRIPTION +/* The \fBunsafe()\fR routine attempts to determine if the process runs +/* with any privileges that do not belong to the user. The purpose is +/* to make it easy to taint any user-provided data such as the current +/* working directory, the process environment, etcetera. +/* +/* On UNIX systems, the result is true when any of the following +/* conditions is true: +/* .IP \(bu +/* The issetuid kernel flag is non-zero (on systems that support +/* this concept). +/* .IP \(bu +/* The real and effective user id differ. +/* .IP \(bu +/* The real and effective group id differ. +/* 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 "safe.h" + +/* unsafe - can we trust user-provided environment, working directory, etc. */ + +int unsafe(void) +{ + return (geteuid() != getuid() +#ifdef HAS_ISSETUGID + || issetugid() +#endif + || getgid() != getegid()); +} diff --git a/postfix/util/username.c b/postfix/util/username.c new file mode 100644 index 000000000..680161c25 --- /dev/null +++ b/postfix/util/username.c @@ -0,0 +1,47 @@ +/*++ +/* NAME +/* username 3 +/* SUMMARY +/* lookup name of real user +/* SYNOPSIS +/* #include +/* +/* const char *username() +/* DESCRIPTION +/* username() jumps whatever system-specific hoops it takes to +/* get the name of the user who started the process. The result +/* is volatile. Make a copy if it is to be used for an appreciable +/* amount of time. +/* 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 + +/* Utility library. */ + +#include "username.h" + +/* username - get name of user */ + +const char *username(void) +{ + uid_t uid; + struct passwd *pwd; + + uid = getuid(); + if ((pwd = getpwuid(uid)) == 0) + return (0); + return (pwd->pw_name); +} diff --git a/postfix/util/username.h b/postfix/util/username.h new file mode 100644 index 000000000..648be45e6 --- /dev/null +++ b/postfix/util/username.h @@ -0,0 +1,29 @@ +#ifndef _USERNAME_H_INCLUDED_ +#define _USERNAME_H_INCLUDED_ + +/*++ +/* NAME +/* username 3h +/* SUMMARY +/* lookup name of real user +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface. */ + +extern const char *username(void); + +/* 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/valid_hostname.c b/postfix/util/valid_hostname.c new file mode 100644 index 000000000..25d1f6c56 --- /dev/null +++ b/postfix/util/valid_hostname.c @@ -0,0 +1,132 @@ +/*++ +/* NAME +/* valid_hostname 3 +/* SUMMARY +/* network name validation +/* SYNOPSIS +/* #include +/* +/* int valid_hostname(name) +/* const char *name; +/* 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. +/* SEE ALSO +/* RFC 952, 1123 +/* 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 + +/* Utility library. */ + +#include "msg.h" +#include "mymalloc.h" +#include "stringops.h" +#include "valid_hostname.h" + +/* valid_hostname - screen out bad hostnames */ + +int valid_hostname(const char *name) +{ + const char *cp; + char *str; + 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"); + return (0); + } + + /* + * Find bad characters. 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; + } + } + + /* + * 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); + return (0); + } + + /* + * Report bad characters. + */ + if (bad_val) { + str = printable(mystrdup(name), '?'); + msg_warn("valid_hostname: invalid character %d(decimal) in %s", + bad_val, str); + myfree(str); + return (0); + } + + /* + * Misplaced delimiters. + */ + if (DELIMITER(name[0]) || adjacent || DELIMITER(name[len - 1])) { + msg_warn("valid_hostname: misplaced delimiter in %s", name); + return (0); + } + return (1); +} + +#ifdef TEST + + /* + * Test program - reads hostnames from stdin, reports invalid hostnames to + * stderr. + */ +#include + +#include "vstring.h" +#include "vstream.h" +#include "vstring_vstream.h" +#include "msg_vstream.h" + +int main(int unused_argc, char **argv) +{ + VSTRING *buffer = vstring_alloc(1); + + msg_vstream_init(argv[0], VSTREAM_ERR); + msg_verbose = 1; + + while (vstring_fgets_nonl(buffer, VSTREAM_IN)) + valid_hostname(vstring_str(buffer)); + exit(0); +} + +#endif diff --git a/postfix/util/valid_hostname.h b/postfix/util/valid_hostname.h new file mode 100644 index 000000000..53f5eb99f --- /dev/null +++ b/postfix/util/valid_hostname.h @@ -0,0 +1,31 @@ +#ifndef _VALID_HOSTNAME_H_INCLUDED_ +#define _VALID_HOSTNAME_H_INCLUDED_ + +/*++ +/* NAME +/* valid_hostname 3h +/* SUMMARY +/* validate hostname +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* External interface */ + +#define VALID_HOSTNAME_LEN 256 + +extern int valid_hostname(const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/util/vbuf.c b/postfix/util/vbuf.c new file mode 100644 index 000000000..00a35c35e --- /dev/null +++ b/postfix/util/vbuf.c @@ -0,0 +1,206 @@ +/*++ +/* NAME +/* vbuf 3 +/* SUMMARY +/* generic buffer package +/* SYNOPSIS +/* #include +/* +/* int VBUF_GET(bp) +/* VBUF *bp; +/* +/* int VBUF_PUT(bp, ch) +/* VBUF *bp; +/* int ch; +/* +/* int VBUF_SPACE(bp, len) +/* VBUF *bp; +/* int len; +/* +/* int vbuf_unget(bp, ch) +/* VBUF *bp; +/* int ch; +/* +/* int vbuf_read(bp, buf, len) +/* VBUF *bp; +/* char *buf; +/* int len; +/* +/* int vbuf_write(bp, buf, len) +/* VBUF *bp; +/* const char *buf; +/* int len; +/* +/* int vbuf_err(bp) +/* VBUF *bp; +/* +/* int vbuf_eof(bp) +/* VBUF *bp; +/* +/* int vbuf_clearerr(bp) +/* VBUF *bp; +/* DESCRIPTION +/* This module implements a buffer with read/write primitives that +/* automatically handle buffer-empty or buffer-full conditions. +/* The application is expected to provide callback routines that run +/* when the read-write primitives detect a buffer-empty/full condition. +/* +/* VBUF buffers provide primitives to store and retrieve characters, +/* and to look up buffer status information. +/* By design, VBUF buffers provide no explicit primitives for buffer +/* memory management. This is left to the application to avoid any bias +/* toward specific management models. The application is free to use +/* whatever strategy suits best: memory-resident buffer, memory mapped +/* file, or stdio-like window to an open file. +/* +/* VBUF_GET() returns the next character from the specified buffer, +/* or VBUF_EOF when none is available. VBUF_GET() is an unsafe macro +/* that evaluates its argument more than once. +/* +/* VBUF_PUT() stores one character into the specified buffer. The result +/* is the stored character, or VBUF_EOF in case of problems. VBUF_PUT() +/* is an unsafe macro that evaluates its arguments more than once. +/* +/* VBUF_SPACE() requests that the requested amount of buffer space be +/* made available, so that it can be accessed without using VBUF_PUT(). +/* The result value is 0 for success, VBUF_EOF for problems. +/* VBUF_SPACE() is an unsafe macro that evaluates its arguments more +/* than once. VBUF_SPACE() does not support read-only streams. +/* +/* vbuf_unget() provides at least one character of pushback, and returns +/* the pushed back character, or VBUF_EOF in case of problems. It is +/* an error to call vbuf_unget() on a buffer before reading any data +/* from it. vbuf_unget() clears the buffer's end-of-file indicator upon +/* success, and sets the buffer's error indicator when an attempt is +/* made to push back a non-character value. +/* +/* vbuf_read() and vbuf_write() do bulk I/O. The result value is the +/* number of bytes transferred. A short count is returned in case of +/* an error. +/* +/* vbuf_err() (vbuf_eof()) is a macro that returns non-zero if an error +/* (end-of-file) condition was detected while reading or writing the +/* buffer. The error status can be reset by calling vbuf_clearerr(). +/* APPLICATION CALLBACK SYNOPSIS +/* int get_ready(bp) +/* VBUF *bp; +/* +/* int put_ready(bp) +/* VBUF *bp; +/* +/* int space(bp, len) +/* VBUF *bp; +/* int len; +/* APPLICATION CALLBACK DESCRIPTION +/* .ad +/* .fi +/* get_ready() is called when VBUF_GET() detects a buffer-empty condition. +/* The result is zero when more data could be read, VBUF_EOF otherwise. +/* +/* put_ready() is called when VBUF_PUT() detects a buffer-full condition. +/* The result is zero when the buffer could be flushed, VBUF_EOF otherwise. +/* +/* space() performs whatever magic necessary to make at least \fIlen\fR +/* bytes available for access without using VBUF_PUT(). The result is 0 +/* in case of success, VBUF_EOF otherwise. +/* SEE ALSO +/* vbuf(3h) layout of the VBUF data structure. +/* 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 + +/* Utility library. */ + +#include "vbuf.h" + +/* vbuf_unget - implement at least one character pushback */ + +int vbuf_unget(VBUF *bp, int ch) +{ + if ((ch & 0xff) != ch || -bp->cnt >= bp->len) { + bp->flags |= VBUF_FLAG_ERR; + return (VBUF_EOF); + } else { + bp->cnt--; + bp->flags &= ~VBUF_FLAG_ERR; + return (*--bp->ptr = ch); + } +} + +/* vbuf_get - handle read buffer empty condition */ + +int vbuf_get(VBUF *bp) +{ + return (bp->get_ready(bp) ? VBUF_EOF : VBUF_GET(bp)); +} + +/* vbuf_put - handle write buffer full condition */ + +int vbuf_put(VBUF *bp, int ch) +{ + return (bp->put_ready(bp) ? VBUF_EOF : VBUF_PUT(bp, ch)); +} + +/* vbuf_read - bulk read from buffer */ + +int vbuf_read(VBUF *bp, char *buf, int len) +{ + int count; + char *cp; + int n; + +#if 0 + for (count = 0; count < len; count++) + if ((buf[count] = VBUF_GET(bp)) < 0) + break; + return (count); +#else + for (cp = buf, count = len; count > 0; cp += n, count -= n) { + if (bp->cnt >= 0 && bp->get_ready(bp)) + break; + n = (count < -bp->cnt ? count : -bp->cnt); + memcpy(cp, bp->ptr, n); + bp->ptr += n; + bp->cnt += n; + } + return (len - count); +#endif +} + +/* vbuf_write - bulk write to buffer */ + +int vbuf_write(VBUF *bp, const char *buf, int len) +{ + int count; + const char *cp; + int n; + +#if 0 + for (count = 0; count < len; count++) + if (VBUF_PUT(bp, buf[count]) < 0) + break; + return (count); +#else + for (cp = buf, count = len; count > 0; cp += n, count -= n) { + if (bp->cnt <= 0 && bp->put_ready(bp) != 0) + break; + n = (count < bp->cnt ? count : bp->cnt); + memcpy(bp->ptr, cp, n); + bp->ptr += n; + bp->cnt -= n; + } + return (len - count); +#endif +} diff --git a/postfix/util/vbuf.h b/postfix/util/vbuf.h new file mode 100644 index 000000000..57aa6f6f9 --- /dev/null +++ b/postfix/util/vbuf.h @@ -0,0 +1,98 @@ +#ifndef _VBUF_H_INCLUDED_ +#define _VBUF_H_INCLUDED_ + +/*++ +/* NAME +/* vbuf 3h +/* SUMMARY +/* generic buffer +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * The VBUF buffer is defined by 1) its structure, by 2) the VBUF_GET() and + * 3) VBUF_PUT() operations that automatically handle buffer empty and + * buffer full conditions, and 4) by the VBUF_SPACE() operation that allows + * the user to reserve buffer space ahead of time, to allow for situations + * where calling VBUF_PUT() is not possible or desirable. + * + * The VBUF buffer does not specify primitives for memory allocation or + * deallocation. The purpose is to allow different applications to have + * different strategies: a memory-resident buffer; a memory-mapped file; or + * a stdio-like window to an open file. Each application provides its own + * get(), put() and space() methods that perform the necessary magic. + * + * This interface is pretty normal. With one exception: the number of bytes + * left to read is negated. This is done so that we can change direction + * between reading and writing on the fly. + */ +typedef struct VBUF VBUF; +typedef int (*VBUF_GET_READY_FN) (VBUF *); +typedef int (*VBUF_PUT_READY_FN) (VBUF *); +typedef int (*VBUF_SPACE_FN) (VBUF *, int); + +struct VBUF { + int flags; /* status, see below */ + unsigned char *data; /* variable-length buffer */ + int len; /* buffer length */ + int cnt; /* bytes left to read/write */ + unsigned char *ptr; /* read/write position */ + VBUF_GET_READY_FN get_ready; /* read buffer empty action */ + VBUF_PUT_READY_FN put_ready; /* write buffer full action */ + VBUF_SPACE_FN space; /* request for buffer space */ +}; + + /* + * Typically, an application will embed a VBUF structure into a larger + * structure that also contains application-specific members. This approach + * gives us the best of both worlds. The application can still use the + * generic VBUF primitives for reading and writing VBUFs. The macro below + * transforms a pointer from VBUF structure to the structure that contains + * it. + */ +#define VBUF_TO_APPL(vbuf_ptr,app_type,vbuf_member) \ + ((app_type *) (((char *) (vbuf_ptr)) - offsetof(app_type,vbuf_member))) + + /* + * Buffer status management. + */ +#define VBUF_FLAG_ERR (1<<0) /* some I/O error */ +#define VBUF_FLAG_EOF (1<<1) /* end of data */ +#define VBUF_FLAG_BAD (VBUF_FLAG_ERR | VBUF_FLAG_EOF) +#define VBUF_FLAG_FIXED (1<<2) /* fixed-size buffer */ + +#define vbuf_error(v) ((v)->flags & VBUF_FLAG_ERR) +#define vbuf_eof(v) ((v)->flags & VBUF_FLAG_EOF) +#define vbuf_clearerr(v) ((v)->flags &= ~VBUF_FLAG_BAD) + + /* + * Buffer I/O-like operations and results. + */ +#define VBUF_GET(v) ((v)->cnt < 0 ? ++(v)->cnt, \ + (int) *(v)->ptr++ : vbuf_get(v)) +#define VBUF_PUT(v,c) ((v)->cnt > 0 ? --(v)->cnt, \ + (int) (*(v)->ptr++ = (c)) : vbuf_put((v),(c))) +#define VBUF_SPACE(v,n) ((v)->space((v),(n))) + +#define VBUF_EOF (-1) /* no more space or data */ + +extern int vbuf_get(VBUF *); +extern int vbuf_put(VBUF *, int); +extern int vbuf_unget(VBUF *, int); +extern int vbuf_read(VBUF *, char *, int); +extern int vbuf_write(VBUF *, 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/vbuf_print.c b/postfix/util/vbuf_print.c new file mode 100644 index 000000000..a820e42b6 --- /dev/null +++ b/postfix/util/vbuf_print.c @@ -0,0 +1,238 @@ +/*++ +/* NAME +/* vbuf_print 3 +/* SUMMARY +/* formatted print to generic buffer +/* SYNOPSIS +/* #include +/* #include +/* +/* VBUF *vbuf_print(bp, format, ap) +/* VBUF *bp; +/* const char *format; +/* va_list ap; +/* DESCRIPTION +/* vbuf_print() appends data to the named buffer according to its +/* \fIformat\fR argument. It understands the s, c, d, u, o, x, X, p, e, +/* f and g format types, the l modifier, field width and precision, +/* sign, and padding with zeros or spaces. +/* +/* In addition, vbuf_print() recognizes the %m format specifier +/* and expands it to the error message corresponding to the current +/* value of the global \fIerrno\fR variable. +/* 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 /* 44BSD stdarg.h uses abort() */ +#include +#include +#include +#include /* 44bsd stdarg.h uses abort() */ +#include /* sprintf() prototype */ +#include /* range of doubles */ +#include + +/* Application-specific. */ + +#include "msg.h" +#include "vbuf.h" +#include "vstring.h" +#include "vbuf_print.h" + + /* + * What we need here is a *sprintf() routine that can ask for more room (as + * in 4.4 BSD). However, that functionality is not widely available, and I + * have no plans to maintain a complete 4.4 BSD *sprintf() alternative. + * + * This means we're stuck with plain old ugly sprintf() for all non-trivial + * conversions. We cannot use snprintf() even if it is available, because + * that routine truncates output, and we want everything. Therefore, it is + * up to us to ensure that sprintf() output always stays within bounds. + * + * Due to the complexity of *printf() format strings we cannot easily predict + * how long results will be without actually doing the conversions. A trick + * used by some people is to print to a temporary file and to read the + * result back. In programs that do a lot of formatting, that might be too + * expensive. + * + * Guessing the output size of a string (%s) conversion is not hard. The + * problem is with numerical results. Instead of making an accurate guess we + * take a wide margin when reserving space. The INT_SPACE margin should be + * large enough to hold the result from any (octal, hex, decimal) integer + * conversion that has no explicit width or precision specifiers. With + * floating-point numbers, use a similar estimate, and add DBL_MAX_10_EXP + * just to be sure. + */ +#define INT_SPACE (4 * sizeof(long)) +#define DBL_SPACE (4 * sizeof(double) + DBL_MAX_10_EXP) +#define PTR_SPACE (4 * sizeof(char *)) + + /* + * Helper macros... Note that there is no need to check the result from + * VSTRING_SPACE() because that always succeeds or never returns. + */ +#define VBUF_SKIP(bp) { \ + while ((bp)->cnt > 0 && *(bp)->ptr) \ + (bp)->ptr++, (bp)->cnt--; \ + } + +#define VSTRING_ADDNUM(vp, n) { \ + VSTRING_SPACE(vp, INT_SPACE); \ + sprintf(vstring_end(vp), "%d", n); \ + VBUF_SKIP(&vp->vbuf); \ + } + +#define VBUF_STRCAT(bp, s) { \ + unsigned char *_cp = (unsigned char *) (s); \ + int ch; \ + while ((ch = *_cp++) != 0) \ + VBUF_PUT((bp), ch); \ + } + +/* vbuf_print - format string, vsprintf-like interface */ + +VBUF *vbuf_print(VBUF *bp, const char *format, va_list ap) +{ + static VSTRING *fmt; /* format specifier */ + unsigned char *cp; + unsigned width; /* field width */ + unsigned prec; /* numerical precision */ + unsigned long_flag; /* long or plain integer */ + int ch; + char *s; + + /* + * Assume that format strings are short. + */ + if (fmt == 0) + fmt = vstring_alloc(INT_SPACE); + + /* + * Iterate over characters in the format string, picking up arguments + * when format specifiers are found. + */ + for (cp = (unsigned char *) format; *cp; cp++) { + if (*cp != '%') { + VBUF_PUT(bp, *cp); /* ordinary character */ + } else if (cp[1] == '%') { + VBUF_PUT(bp, *cp++); /* %% becomes % */ + } else { + + /* + * Handle format specifiers one at a time, since we can only deal + * with arguments one at a time. Try to determine the end of the + * format specifier. We do not attempt to fully parse format + * strings, since we are ging to let sprintf() do the hard work. + * In regular expression notation, we recognize: + * + * %-?0?([0-9]+|\*)?\.?([0-9]+|\*)?l?[a-zA-Z] + * + * which includes some combinations that do not make sense. Garbage + * in, garbage out. + */ + VSTRING_RESET(fmt); /* clear format string */ + VSTRING_ADDCH(fmt, *cp++); + if (*cp == '-') /* left-adjusted field? */ + VSTRING_ADDCH(fmt, *cp++); + if (*cp == '+') /* signed field? */ + VSTRING_ADDCH(fmt, *cp++); + if (*cp == '0') /* zero-padded field? */ + VSTRING_ADDCH(fmt, *cp++); + if (*cp == '*') { /* dynamic field width */ + width = va_arg(ap, int); + VSTRING_ADDNUM(fmt, width); + cp++; + } else { /* hard-coded field width */ + for (width = 0; ISDIGIT(ch = *cp); cp++) { + width = width * 10 + ch - '0'; + VSTRING_ADDCH(fmt, ch); + } + } + if (*cp == '.') /* width/precision separator */ + VSTRING_ADDCH(fmt, *cp++); + if (*cp == '*') { /* dynamic precision */ + prec = va_arg(ap, int); + VSTRING_ADDNUM(fmt, prec); + cp++; + } else { /* hard-coded precision */ + for (prec = 0; ISDIGIT(ch = *cp); cp++) { + prec = prec * 10 + ch - '0'; + VSTRING_ADDCH(fmt, ch); + } + } + if ((long_flag = (*cp == 'l')) != 0)/* long whatever */ + VSTRING_ADDCH(fmt, *cp++); + if (*cp == 0) /* premature end, punt */ + break; + VSTRING_ADDCH(fmt, *cp); /* type (checked below) */ + VSTRING_TERMINATE(fmt); /* null terminate */ + + /* + * Execute the format string - let sprintf() do the hard work for + * non-trivial cases only. For simple string conversions and for + * long string conversions, do a direct copy to the output + * buffer. + */ + switch (*cp) { + case 's': /* string-valued argument */ + s = va_arg(ap, char *); + if (prec > 0 || (width > 0 && width > strlen(s))) { + if (VBUF_SPACE(bp, (width > prec ? width : prec) + INT_SPACE)) + break; + sprintf((char *) bp->ptr, vstring_str(fmt), s); + VBUF_SKIP(bp); + } else { + VBUF_STRCAT(bp, s); + } + break; + case 'c': /* integral-valued argument */ + case 'd': + case 'u': + case 'o': + case 'x': + case 'X': + if (VBUF_SPACE(bp, (width > prec ? width : prec) + INT_SPACE)) + break; + if (long_flag) + sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, long)); + else + sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, int)); + VBUF_SKIP(bp); + break; + case 'e': /* float-valued argument */ + case 'f': + case 'g': + if (VBUF_SPACE(bp, (width > prec ? width : prec) + DBL_SPACE)) + break; + sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, double)); + VBUF_SKIP(bp); + break; + case 'm': + VBUF_STRCAT(bp, strerror(errno)); + break; + case 'p': + if (VBUF_SPACE(bp, (width > prec ? width : prec) + PTR_SPACE)) + break; + sprintf((char *) bp->ptr, vstring_str(fmt), va_arg(ap, char *)); + VBUF_SKIP(bp); + break; + default: /* anything else is bad */ + msg_panic("vbuf_print: unknown format type: %c", *cp); + /* NOTREACHED */ + break; + } + } + } + return (bp); +} diff --git a/postfix/util/vbuf_print.h b/postfix/util/vbuf_print.h new file mode 100644 index 000000000..32549c142 --- /dev/null +++ b/postfix/util/vbuf_print.h @@ -0,0 +1,40 @@ +#ifndef _VBUF_PRINT_H_INCLUDED_ +#define _VBUF_PRINT_H_INCLUDED_ + +/*++ +/* NAME +/* vbuf_print 3h +/* SUMMARY +/* formatted print to generic buffer +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern VBUF *vbuf_print(VBUF *, const char *, va_list); + +/* 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/vstream.c b/postfix/util/vstream.c new file mode 100644 index 000000000..4014e1875 --- /dev/null +++ b/postfix/util/vstream.c @@ -0,0 +1,986 @@ +/*++ +/* NAME +/* vstream 3 +/* SUMMARY +/* light-weight buffered I/O package +/* SYNOPSIS +/* #include +/* +/* VSTREAM *vstream_fopen(path, flags, mode) +/* char *path; +/* int flags; +/* int mode; +/* +/* VSTREAM *vstream_fdopen(fd, flags) +/* int fd; +/* int flags; +/* +/* int vstream_fclose(stream) +/* VSTREAM *stream; +/* +/* VSTREAM *vstream_printf(format, ...) +/* char *format; +/* +/* VSTREAM *vstream_fprintf(stream, format, ...) +/* VSTREAM *stream; +/* char *format; +/* +/* int VSTREAM_GETC(stream) +/* VSTREAM *stream; +/* +/* int VSTREAM_PUTC(ch, stream) +/* int ch; +/* +/* int VSTREAM_GETCHAR(void) +/* +/* int VSTREAM_PUTCHAR(ch) +/* int ch; +/* +/* int vstream_ungetc(stream, ch) +/* VSTREAM *stream; +/* int ch; +/* +/* int vstream_fputs(str, stream) +/* char *str; +/* VSTREAM *stream; +/* +/* long vstream_ftell(stream) +/* VSTREAM *stream; +/* +/* long vstream_fseek(stream, offset, whence) +/* VSTREAM *stream; +/* long offset; +/* int whence; +/* +/* int vstream_fflush(stream) +/* VSTREAM *stream; +/* +/* int vstream_fread(stream, buf, len) +/* VSTREAM *stream; +/* char *buf; +/* int len; +/* +/* int vstream_fwrite(stream, buf, len) +/* VSTREAM *stream; +/* char *buf; +/* int len; +/* +/* void vstream_control(stream, name, ...) +/* VSTREAM *stream; +/* int name; +/* +/* int vstream_fileno(stream) +/* VSTREAM *stream; +/* +/* int vstream_ferror(stream) +/* VSTREAM *stream; +/* +/* int vstream_feof(stream) +/* VSTREAM *stream; +/* +/* int vstream_clearerr(stream) +/* VSTREAM *stream; +/* +/* char *VSTREAM_PATH(stream) +/* VSTREAM *stream; +/* +/* char *vstream_vfprintf(vp, format, ap) +/* char *format; +/* va_list *ap; +/* DESCRIPTION +/* The \fIvstream\fR module implements light-weight buffered I/O +/* similar to the standard I/O routines. +/* +/* The interface is implemented in terms of VSTREAM structure +/* pointers, also called streams. For convenience, three streams +/* are predefined: VSTREAM_IN, VSTREAM_OUT, and VSTREAM_ERR. These +/* streams are connected to the standard input, output and error +/* file descriptors, respectively. +/* +/* Although the interface is patterned after the standard I/O +/* library, there are some major differences: +/* .IP \(bu +/* File descriptors are not limited to the range 0..255. This +/* was reason #1 to write these routines in the first place. +/* .IP \(bu +/* The application can switch between reading and writing on +/* the same stream without having to perform a flush or seek +/* operation, and can change write position without having to +/* flush. This was reason #2. Upon position or direction change, +/* unread input is discarded, and unwritten output is flushed +/* automatically. Exception: with double-buffered streams, unread +/* input is not discarded upon change of I/O direction, and +/* output flushing is delayed until the read buffer must be refilled. +/* .IP \(bu +/* A bidirectional stream can read and write with the same buffer +/* and file descriptor, or it can have separate read/write +/* buffers and/or file descriptors. +/* .IP \(bu +/* No automatic flushing of VSTREAM_OUT upon program exit, or of +/* VSTREAM_ERR at any time. No unbuffered or line buffered modes. +/* This functionality may be added when it is really needed. +/* .PP +/* vstream_fopen() opens the named file and associates a buffered +/* stream with it. The \fIpath\fR, \fIflags\fR and \fImode\fR +/* arguments are passed on to the open(2) routine. The result is +/* a null pointer in case of problems. The \fIpath\fR argument is +/* copied and can be looked up with VSTREAM_PATH(). +/* +/* vstream_fdopen() takes an open file and associates a buffered +/* stream with it. The \fIflags\fR argument specifies how the file +/* was opened. vstream_fdopen() either succeeds or never returns. +/* +/* vstream_fclose() closes the named buffered stream. The result +/* is 0 in case of success, VSTREAM_EOF in case of problems. +/* +/* vstream_fprintf() formats its arguments according to the +/* \fIformat\fR argument and writes the result to the named stream. +/* The result is the stream argument. It understands the s, c, d, u, +/* o, x, X, e, f and g format types, the l modifier, field width and +/* precision, sign, and padding with zeros or spaces. In addition, +/* vstream_fprintf() recognizes the %m format specifier and expands +/* it to the error message corresponding to the current value of the +/* global \fIerrno\fR variable. +/* +/* vstream_printf() performs formatted output to the standard output +/* stream. +/* +/* VSTREAM_GETC() reads the next character from the named stream. +/* The result is VSTREAM_EOF when end-of-file is reached or if a read +/* error was detected. VSTREAM_GETC() is an unsafe macro that +/* evaluates some arguments more than once. +/* +/* VSTREAM_GETCHAR() is an alias for VSTREAM_GETC(VSTREAM_IN). +/* +/* VSTREAM_PUTC() appends the specified character to the specified +/* stream. The result is the stored character, or VSTREAM_EOF in +/* case of problems. VSTREAM_PUTC() is an unsafe macro that +/* evaluates some arguments more than once. +/* +/* VSTREAM_PUTCHAR(c) is an alias for VSTREAM_PUTC(c, VSTREAM_OUT). +/* +/* vstream_unget() pushes back a character onto the specified stream +/* and returns the character, or VSTREAM_EOF in case of problems. +/* It is an error to push back before reading (or immediately after +/* changing the stream offset via vstream_fseek()). Upon successful +/* return, vstream_unget() clears the end-of-file stream flag. +/* +/* vstream_fputs() appends the given null-terminated string to the +/* specified buffered stream. The result is 0 in case of success, +/* VSTREAM_EOF in case of problems. +/* +/* vstream_ftell() returns the file offset for the specified stream, +/* -1 if the stream is connected to a non-seekable file. +/* +/* vstream_fseek() changes the file position for the next read or write +/* operation. Unwritten output is flushed. With unidirectional streams, +/* unread input is discarded. The \fIoffset\fR argument specifies the file +/* position from the beginning of the file (\fIwhence\fR is SEEK_SET), +/* from the current file position (\fIwhence\fR is SEEK_CUR), or from +/* the file end (SEEK_END). The result value is the file offset +/* from the beginning of the file, -1 in case of problems. +/* +/* vstream_fflush() flushes unwritten data to a file that was +/* opened in read-write or write-only mode. +/* vstream_fflush() returns 0 in case of success, VSTREAM_EOF in +/* case of problems. It is an error to flush a read-only stream. +/* +/* vstream_fread() and vstream_fwrite() perform unformatted I/O +/* on the named stream. The result value is the number of bytes +/* transferred. A short count is returned in case of end-of-file +/* or error conditions. +/* +/* vstream_control() allows the user to fine tune the behavior of +/* the specified stream. The arguments are a list of (name, +/* value) pairs, terminated with VSTREAM_CTL_END. +/* The following lists the names and the types of the corresponding +/* value arguments. +/* .IP "VSTREAM_CTL_READ_FN (int (*)(int, void *, unsigned))" +/* The argument specifies an alternative for the read(2) function, +/* for example, a read function that enforces a time limit. +/* .IP "VSTREAM_CTL_WRITE_FN (int (*)(int, void *, unsigned))" +/* The argument specifies an alternative for the write(2) function, +/* for example, a write function that enforces a time limit. +/* .IP "VSTREAM_CTL_PATH (char *)" +/* Updates the stored pathname of the specified stream. The pathname +/* is copied. +/* .IP "VSTREAM_CTL_DOUBLE (no value)" +/* Use separate buffers for reading and for writing. This prevents +/* unread input from being discarded upon change of I/O direction. +/* .IP "VSTREAM_CTL_READ_FD (int) +/* The argument specifies the file descriptor to be used for reading. +/* This feature is limited to double-buffered streams, and makes the +/* stream non-seekable. +/* .IP "VSTREAM_CTL_WRITE_FD (int) +/* 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. +/* .PP +/* vstream_fileno() gives access to the file handle associated with +/* a buffered stream. With streams that have separate read/write +/* file descriptors, the result is the current descriptor. +/* +/* VSTREAM_PATH() is an unsafe macro that returns the name stored +/* with vstream_fopen() or with vstream_control(). The macro is +/* unsafe because it evaluates some arguments more than once. +/* +/* vstream_ferror() (vstream_feof()) returns non-zero when a previous +/* operation on the specified stream caused an error (end-of-file) +/* condition. +/* +/* vstream_clearerr() resets the error and end-of-file indication of +/* specified stream, and returns no useful result. +/* +/* vstream_vfprintf() provides an alternate interface +/* for formatting an argument list according to a format string. +/* DIAGNOSTICS +/* Panics: interface violations. Fatal errors: out of memory. +/* SEE ALSO +/* vbuf_print(3) formatting engine +/* BUGS +/* Should use mmap() on reasonable systems. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include /* 44BSD stdarg.h uses abort() */ +#include +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include "mymalloc.h" +#include "msg.h" +#include "vbuf_print.h" +#include "vstring.h" +#include "vstream.h" + +/* Application-specific. */ + + /* + * Forward declarations. + */ +static int vstream_buf_get_ready(VBUF *); +static int vstream_buf_put_ready(VBUF *); +static int vstream_buf_space(VBUF *, int); + + /* + * Initialization of the three pre-defined streams. Pre-allocate a static + * I/O buffer for the standard error stream, so that the error handler can + * produce a diagnostic even when memory allocation fails. + */ +static unsigned char vstream_fstd_buf[VSTREAM_BUFSIZE]; + +VSTREAM vstream_fstd[] = { + {{ + 0, /* flags */ + 0, 0, 0, 0, /* buffer */ + vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space, + }, STDIN_FILENO, (VSTREAM_FN) read, (VSTREAM_FN) write,}, + {{ + 0, /* flags */ + 0, 0, 0, 0, /* buffer */ + vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space, + }, STDOUT_FILENO, (VSTREAM_FN) read, (VSTREAM_FN) write,}, + {{ + VBUF_FLAG_FIXED | VSTREAM_FLAG_WRITE, + vstream_fstd_buf, VSTREAM_BUFSIZE, VSTREAM_BUFSIZE, vstream_fstd_buf, + vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space, + }, STDERR_FILENO, (VSTREAM_FN) read, (VSTREAM_FN) write,}, +}; + +#define VSTREAM_STATIC(v) ((v) >= VSTREAM_IN && (v) <= VSTREAM_ERR) + + /* + * A bunch of macros to make some expressions more readable. XXX We're + * assuming that O_RDONLY == 0, O_WRONLY == 1, O_RDWR == 2. + */ +#define VSTREAM_ACC_MASK(f) ((f) & (O_APPEND | O_WRONLY | O_RDWR)) + +#define VSTREAM_CAN_READ(f) (VSTREAM_ACC_MASK(f) == O_RDONLY \ + || VSTREAM_ACC_MASK(f) == O_RDWR) +#define VSTREAM_CAN_WRITE(f) (VSTREAM_ACC_MASK(f) & O_WRONLY \ + || VSTREAM_ACC_MASK(f) & O_RDWR \ + || VSTREAM_ACC_MASK(f) & O_APPEND) + +#define VSTREAM_BUF_COUNT(bp, n) \ + ((bp)->flags & VSTREAM_FLAG_READ ? -(n) : (n)) + +#define VSTREAM_BUF_AT_START(bp) { \ + (bp)->cnt = VSTREAM_BUF_COUNT((bp), (bp)->len); \ + (bp)->ptr = (bp)->data; \ + } + +#define VSTREAM_BUF_AT_OFFSET(bp, offset) { \ + (bp)->ptr = (bp)->data + (offset); \ + (bp)->cnt = VSTREAM_BUF_COUNT(bp, (bp)->len - (offset)); \ + } + +#define VSTREAM_BUF_AT_END(bp) { \ + (bp)->cnt = 0; \ + (bp)->ptr = (bp)->data + (bp)->len; \ + } + +#define VSTREAM_BUF_ZERO(bp) { \ + (bp)->flags = 0; \ + (bp)->data = (bp)->ptr = 0; \ + (bp)->len = (bp)->cnt = 0; \ + } + +#define VSTREAM_BUF_ACTIONS(bp, get_action, put_action, space_action) { \ + (bp)->get_ready = (get_action); \ + (bp)->put_ready = (put_action); \ + (bp)->space = (space_action); \ + } + +#define VSTREAM_SAVE_STATE(stream, buffer, filedes) { \ + stream->buffer = stream->buf; \ + stream->filedes = stream->fd; \ + } + +#define VSTREAM_RESTORE_STATE(stream, buffer, filedes) do { \ + stream->buffer.flags = stream->buf.flags; \ + stream->buf = stream->buffer; \ + stream->fd = stream->filedes; \ + } while(0) + +#define VSTREAM_FORK_STATE(stream, buffer, filedes) { \ + stream->buffer = stream->buf; \ + stream->filedes = stream->fd; \ + stream->buffer.data = stream->buffer.ptr = 0; \ + stream->buffer.len = stream->buffer.cnt = 0; \ + stream->buffer.flags &= ~VSTREAM_FLAG_FIXED; \ + }; + +#define VSTREAM_FLAG_READ_DOUBLE (VSTREAM_FLAG_READ | VSTREAM_FLAG_DOUBLE) +#define VSTREAM_FLAG_WRITE_DOUBLE (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_DOUBLE) + +#define VSTREAM_FFLUSH_SOME(stream) \ + vstream_fflush_some((stream), (stream)->buf.len - (stream)->buf.cnt) + +/* vstream_buf_init - initialize buffer */ + +static void vstream_buf_init(VBUF *bp, int flags) +{ + + /* + * Initialize the buffer such that the first data access triggers a + * buffer boundary action. + */ + VSTREAM_BUF_ZERO(bp); + VSTREAM_BUF_ACTIONS(bp, + VSTREAM_CAN_READ(flags) ? vstream_buf_get_ready : 0, + VSTREAM_CAN_WRITE(flags) ? vstream_buf_put_ready : 0, + vstream_buf_space); +} + +/* vstream_buf_alloc - allocate buffer memory */ + +static void vstream_buf_alloc(VBUF *bp, int len) +{ + int used = bp->ptr - bp->data; + char *myname = "vstream_buf_alloc"; + + if (len < bp->len) + msg_panic("%s: attempt to shrink buffer", myname); + if (bp->flags & VSTREAM_FLAG_FIXED) + msg_panic("%s: unable to extend fixed-size buffer", myname); + + /* + * Late buffer allocation allows the user to override the default policy. + * If a buffer already exists, allow for the presence of (output) data. + */ + bp->data = (unsigned char *) + (bp->data ? myrealloc((char *) bp->data, len) : mymalloc(len)); + bp->len = len; + if (bp->flags & VSTREAM_FLAG_READ) + bp->ptr = bp->data + used; + else + VSTREAM_BUF_AT_OFFSET(bp, used); +} + +/* vstream_buf_wipe - reset buffer to initial state */ + +static void vstream_buf_wipe(VBUF *bp) +{ + if ((bp->flags & VBUF_FLAG_FIXED) == 0 && bp->data) + myfree((char *) bp->data); + VSTREAM_BUF_ZERO(bp); + VSTREAM_BUF_ACTIONS(bp, 0, 0, 0); +} + +/* vstream_fflush_some - flush some buffered data */ + +static int vstream_fflush_some(VSTREAM *stream, int to_flush) +{ + char *myname = "vstream_fflush_some"; + VBUF *bp = &stream->buf; + int used; + int left_over; + char *data; + int len; + int n; + + /* + * Sanity checks. It is illegal to flush a read-only stream. Otherwise, + * if there is buffered input, discard the input. If there is buffered + * output, require that the amount to flush is larger than the amount to + * keep, so that we can memcpy() the residue. + */ + if (bp->put_ready == 0) + msg_panic("%s: read-only stream", myname); + switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) { + case VSTREAM_FLAG_READ: /* discard input */ + VSTREAM_BUF_AT_END(bp); + /* FALLTHROUGH */ + case 0: /* flush after seek? */ + return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0); + case VSTREAM_FLAG_WRITE: /* output buffered */ + break; + case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ: + msg_panic("%s: read/write stream", myname); + } + used = bp->len - bp->cnt; + left_over = used - to_flush; + + if (msg_verbose > 2 && stream != VSTREAM_ERR) + msg_info("%s: fd %d flush %d", myname, stream->fd, to_flush); + if (to_flush < 0 || left_over < 0) + msg_panic("%s: bad to_flush %d", myname, to_flush); + if (to_flush < left_over) + msg_panic("%s: to_flush < left_over", myname); + if (to_flush == 0) + return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0); + + /* + * When flushing a buffer, allow for partial writes. These can happen + * while talking to a network. Update the cached file seek position, if + * any. + */ + for (data = (char *) bp->data, len = to_flush; len > 0; len -= n, data += n) { + if ((n = stream->write_fn(stream->fd, data, len)) <= 0) { + bp->flags |= VSTREAM_FLAG_ERR; + return (VSTREAM_EOF); + } + if (msg_verbose > 2 && stream != VSTREAM_ERR && n != to_flush) + msg_info("%s: %d flushed %d/%d", myname, stream->fd, n, to_flush); + } + if (bp->flags & VSTREAM_FLAG_SEEK) + stream->offset += to_flush; + + /* + * Allow for partial buffer flush requests. We use memcpy() for reasons + * of portability to pre-ANSI environments (SunOS 4.x or Ultrix 4.x :-). + * This is OK because we have already verified that the to_flush count is + * larger than the left_over count. + */ + if (left_over > 0) + memcpy(bp->data, bp->data + to_flush, left_over); + bp->cnt += to_flush; + bp->ptr -= to_flush; + return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0); +} + +/* vstream_fflush_delayed - delayed stream flush for double-buffered stream */ + +static int vstream_fflush_delayed(VSTREAM *stream) +{ + int status; + + /* + * Sanity check. + */ + if ((stream->buf.flags & VSTREAM_FLAG_READ_DOUBLE) != VSTREAM_FLAG_READ_DOUBLE) + msg_panic("vstream_fflush_delayed: bad flags"); + + /* + * Temporarily swap buffers and flush unwritten data. This may seem like + * a lot of work, but it's peanuts compared to the write(2) call that we + * already have avoided. For example, delayed flush is never used on a + * non-pipelined SMTP connection. + */ + stream->buf.flags &= ~VSTREAM_FLAG_READ; + VSTREAM_SAVE_STATE(stream, read_buf, read_fd); + stream->buf.flags |= VSTREAM_FLAG_WRITE; + VSTREAM_RESTORE_STATE(stream, write_buf, write_fd); + + status = VSTREAM_FFLUSH_SOME(stream); + + stream->buf.flags &= ~VSTREAM_FLAG_WRITE; + VSTREAM_SAVE_STATE(stream, write_buf, write_fd); + stream->buf.flags |= VSTREAM_FLAG_READ; + VSTREAM_RESTORE_STATE(stream, read_buf, read_fd); + + return (status); +} + +/* vstream_buf_get_ready - vbuf callback to make buffer ready for reading */ + +static int vstream_buf_get_ready(VBUF *bp) +{ + VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf); + char *myname = "vstream_buf_get_ready"; + int n; + + /* + * Detect a change of I/O direction or position. If so, flush any + * unwritten output immediately when the stream is single-buffered, or + * when the stream is double-buffered and the read buffer is empty. + */ + switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) { + case VSTREAM_FLAG_WRITE: /* change direction */ + if (bp->ptr > bp->data) + if ((bp->flags & VSTREAM_FLAG_DOUBLE) == 0 + || stream->read_buf.cnt >= 0) + if (VSTREAM_FFLUSH_SOME(stream)) + return (VSTREAM_EOF); + bp->flags &= ~VSTREAM_FLAG_WRITE; + if (bp->flags & VSTREAM_FLAG_DOUBLE) + VSTREAM_SAVE_STATE(stream, write_buf, write_fd); + /* FALLTHROUGH */ + case 0: /* change position */ + bp->flags |= VSTREAM_FLAG_READ; + if (bp->flags & VSTREAM_FLAG_DOUBLE) { + VSTREAM_RESTORE_STATE(stream, read_buf, read_fd); + if (bp->cnt < 0) + return (0); + } + /* FALLTHROUGH */ + case VSTREAM_FLAG_READ: /* no change */ + break; + case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ: + msg_panic("%s: read/write stream", myname); + } + + /* + * If this is the first GET operation, allocate a buffer. Late buffer + * allocation gives the application a chance to override the default + * buffering policy. + */ + if (bp->data == 0) + vstream_buf_alloc(bp, VSTREAM_BUFSIZE); + + /* + * If the stream is double-buffered and the write buffer is not empty, + * this is the time to flush the write buffer. Delayed flushes reduce + * system call overhead, and on TCP sockets, avoid triggering Nagle's + * algorithm. + */ + if ((bp->flags & VSTREAM_FLAG_DOUBLE) + && stream->write_buf.len > stream->write_buf.cnt) + if (vstream_fflush_delayed(stream)) + return (VSTREAM_EOF); + + /* + * Fill the buffer with as much data as we can handle, or with as much + * data as is available right now, whichever is less. Update the cached + * file seek position, if any. + */ + switch (n = stream->read_fn(stream->fd, bp->data, bp->len)) { + case -1: + bp->flags |= VSTREAM_FLAG_ERR; + return (VSTREAM_EOF); + case 0: + bp->flags |= VSTREAM_FLAG_EOF; + return (VSTREAM_EOF); + default: + if (msg_verbose > 2) + msg_info("%s: fd %d got %d", myname, stream->fd, n); + bp->cnt = -n; + bp->ptr = bp->data; + if (bp->flags & VSTREAM_FLAG_SEEK) + stream->offset += n; + return (0); + } +} + +/* vstream_buf_put_ready - vbuf callback to make buffer ready for writing */ + +static int vstream_buf_put_ready(VBUF *bp) +{ + VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf); + char *myname = "vstream_buf_put_ready"; + + /* + * Sanity checks. Detect a change of I/O direction or position. If so, + * discard unread input, and reset the buffer to the beginning. + */ + switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) { + case VSTREAM_FLAG_READ: /* change direction */ + bp->flags &= ~VSTREAM_FLAG_READ; + if (bp->flags & VSTREAM_FLAG_DOUBLE) + VSTREAM_SAVE_STATE(stream, read_buf, read_fd); + /* FALLTHROUGH */ + case 0: /* change position */ + bp->flags |= VSTREAM_FLAG_WRITE; + if (bp->flags & VSTREAM_FLAG_DOUBLE) + VSTREAM_RESTORE_STATE(stream, write_buf, write_fd); + else + VSTREAM_BUF_AT_START(bp); + /* FALLTHROUGH */ + case VSTREAM_FLAG_WRITE: /* no change */ + break; + case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ: + msg_panic("%s: read/write stream", myname); + } + + /* + * Remember the direction. If this is the first PUT operation for this + * stream, allocate a new buffer; obviously there is no data to be + * flushed yet. Otherwise, flush the buffer if it is full. + */ + if (bp->data == 0) { + vstream_buf_alloc(bp, VSTREAM_BUFSIZE); + } else if (bp->cnt <= 0) { + if (VSTREAM_FFLUSH_SOME(stream)) + return (VSTREAM_EOF); + } + return (0); +} + +/* vstream_buf_space - reserve space ahead of time */ + +static int vstream_buf_space(VBUF *bp, int want) +{ + VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf); + int used; + int incr; + int shortage; + char *myname = "vstream_buf_space"; + + /* + * Sanity checks. Reserving space implies writing. It is illegal to write + * to a read-only stream. Detect a change of I/O direction or position. + * If so, reset the buffer to the beginning. + */ + if (bp->put_ready == 0) + msg_panic("%s: read-only stream", myname); + switch (bp->flags & (VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE)) { + case VSTREAM_FLAG_READ: /* change direction */ + bp->flags &= ~VSTREAM_FLAG_READ; + if (bp->flags & VSTREAM_FLAG_DOUBLE) + VSTREAM_SAVE_STATE(stream, read_buf, read_fd); + /* FALLTHROUGH */ + case 0: /* change position */ + bp->flags |= VSTREAM_FLAG_WRITE; + if (bp->flags & VSTREAM_FLAG_DOUBLE) + VSTREAM_RESTORE_STATE(stream, write_buf, write_fd); + else + VSTREAM_BUF_AT_START(bp); + /* FALLTHROUGH */ + case VSTREAM_FLAG_WRITE: /* no change */ + break; + case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE: + msg_panic("%s: read/write stream", myname); + } + + /* + * See if enough space is available. If not, flush a multiple of + * VSTREAM_BUFSIZE bytes and resize the buffer to a multiple of + * VSTREAM_BUFSIZE. We flush multiples of VSTREAM_BUFSIZE in an attempt + * to keep file updates block-aligned for better performance. + */ +#define VSTREAM_TRUNCATE(count, base) (((count) / (base)) * (base)) +#define VSTREAM_ROUNDUP(count, base) VSTREAM_TRUNCATE(count + base - 1, base) + + if (want > bp->cnt) { + if ((used = bp->len - bp->cnt) > VSTREAM_BUFSIZE) + if (vstream_fflush_some(stream, VSTREAM_TRUNCATE(used, VSTREAM_BUFSIZE))) + return (VSTREAM_EOF); + if ((shortage = (want - bp->cnt)) > 0) { + incr = VSTREAM_ROUNDUP(shortage, VSTREAM_BUFSIZE); + vstream_buf_alloc(bp, bp->len + incr); + } + } + return (vstream_ferror(stream) ? VSTREAM_EOF : 0); /* mmap() may fail */ +} + +/* vstream_fseek - change I/O position */ + +long vstream_fseek(VSTREAM *stream, long offset, int whence) +{ + char *myname = "vstream_fseek"; + VBUF *bp = &stream->buf; + + /* + * Flush any unwritten output. Discard any unread input. Position the + * buffer at the end, so that the next GET or PUT operation triggers a + * buffer boundary action. + */ + switch (bp->flags & (VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE)) { + case VSTREAM_FLAG_WRITE: + if (bp->ptr > bp->data) + if (VSTREAM_FFLUSH_SOME(stream)) + return (-1); + /* FALLTHROUGH */ + case VSTREAM_FLAG_READ: + case 0: + VSTREAM_BUF_AT_END(bp); + break; + case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE: + msg_panic("%s: read/write stream", myname); + } + + /* + * Clear the read/write flags to inform the buffer boundary action + * routines that we may have changed I/O position. + */ + bp->flags &= ~(VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE); + + /* + * Shave an unnecessary system call. + */ + if (bp->flags & VSTREAM_FLAG_NSEEK) { + errno = ESPIPE; + return (-1); + } + + /* + * Update the cached file seek position. + */ + if ((stream->offset = lseek(stream->fd, offset, whence)) < 0) { + bp->flags |= VSTREAM_FLAG_NSEEK; + } else { + bp->flags |= VSTREAM_FLAG_SEEK; + } + return (stream->offset); +} + +/* vstream_ftell - return file offset */ + +long vstream_ftell(VSTREAM *stream) +{ + VBUF *bp = &stream->buf; + + /* + * Shave an unnecessary syscall. + */ + if (bp->flags & VSTREAM_FLAG_NSEEK) { + errno = ESPIPE; + return (-1); + } + + /* + * Use the cached file offset when available. This is the offset after + * the last read, write or seek operation. + */ + if ((bp->flags & VSTREAM_FLAG_SEEK) == 0) { + if ((stream->offset = lseek(stream->fd, 0L, SEEK_CUR)) < 0) { + bp->flags |= VSTREAM_FLAG_NSEEK; + return (-1); + } + bp->flags |= VSTREAM_FLAG_SEEK; + } + + /* + * If this is a read buffer, subtract the number of unread bytes from the + * cached offset. Remember that read counts are negative. + */ + if (bp->flags & VSTREAM_FLAG_READ) + return (stream->offset + bp->cnt); + + /* + * If this is a write buffer, add the number of unwritten bytes to the + * cached offset. + */ + if (bp->flags & VSTREAM_FLAG_WRITE) + return (stream->offset + (bp->ptr - bp->data)); + + /* + * Apparently, this is a new buffer, or a buffer after seek, so there is + * no need to account for unread or unwritten data. + */ + return (stream->offset); +} + +/* vstream_fdopen - add buffering to pre-opened stream */ + +VSTREAM *vstream_fdopen(int fd, int flags) +{ + VSTREAM *stream; + + /* + * Sanity check. + */ + if (fd < 0) + msg_panic("vstream_fdopen: bad file %d", fd); + + /* + * Initialize buffers etc. but do as little as possible. Late buffer + * allocation etc. gives the application a chance to override default + * policies. Either this, or the vstream*open() routines would have to + * have a really ugly interface with lots of mostly-unused arguments (can + * you say VMS?). + */ + stream = (VSTREAM *) mymalloc(sizeof(*stream)); + stream->fd = fd; + stream->read_fn = VSTREAM_CAN_READ(flags) ? (VSTREAM_FN) read : 0; + stream->write_fn = VSTREAM_CAN_WRITE(flags) ? (VSTREAM_FN) write : 0; + vstream_buf_init(&stream->buf, flags); + stream->offset = 0; + stream->path = 0; + return (stream); +} + +/* vstream_fopen - open buffered file stream */ + +VSTREAM *vstream_fopen(const char *path, int flags, int mode) +{ + VSTREAM *stream; + int fd; + + if ((fd = open(path, flags, mode)) < 0) { + return (0); + } else { + stream = vstream_fdopen(fd, flags); + stream->path = mystrdup(path); + return (stream); + } +} + +/* vstream_fflush - flush write buffer */ + +int vstream_fflush(VSTREAM *stream) +{ + if ((stream->buf.flags & VSTREAM_FLAG_READ_DOUBLE) + == VSTREAM_FLAG_READ_DOUBLE + && stream->write_buf.len > stream->write_buf.cnt) + vstream_fflush_delayed(stream); + return (VSTREAM_FFLUSH_SOME(stream)); +} + +/* vstream_fclose - close buffered stream */ + +int vstream_fclose(VSTREAM *stream) +{ + int err; + + if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0) + vstream_fflush(stream); + err = vstream_ferror(stream); + if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) { + err |= close(stream->read_fd); + if (stream->write_fd != stream->read_fd) + err |= close(stream->write_fd); + vstream_buf_wipe(&stream->read_buf); + vstream_buf_wipe(&stream->write_buf); + stream->buf = stream->read_buf; + } else { + err |= close(stream->fd); + vstream_buf_wipe(&stream->buf); + } + if (stream->path) + myfree(stream->path); + if (!VSTREAM_STATIC(stream)) + myfree((char *) stream); + return (err ? VSTREAM_EOF : 0); +} + +/* vstream_printf - formatted print to stdout */ + +VSTREAM *vstream_printf(const char *fmt,...) +{ + VSTREAM *stream = VSTREAM_OUT; + va_list ap; + + va_start(ap, fmt); + vbuf_print(&stream->buf, fmt, ap); + va_end(ap); + return (stream); +} + +/* vstream_fprintf - formatted print to buffered stream */ + +VSTREAM *vstream_fprintf(VSTREAM *stream, const char *fmt,...) +{ + va_list ap; + + va_start(ap, fmt); + vbuf_print(&stream->buf, fmt, ap); + va_end(ap); + return (stream); +} + +/* vstream_fputs - write string to stream */ + +int vstream_fputs(const char *str, VSTREAM *stream) +{ + int ch; + + while ((ch = *str++) != 0) + if (VSTREAM_PUTC(ch, stream) == VSTREAM_EOF) + return (VSTREAM_EOF); + return (0); +} + +/* vstream_control - fine control */ + +void vstream_control(VSTREAM *stream, int name,...) +{ + char *myname = "vstream_control"; + va_list ap; + + for (va_start(ap, name); name != VSTREAM_CTL_END; name = va_arg(ap, int)) { + switch (name) { + case VSTREAM_CTL_READ_FN: + stream->read_fn = va_arg(ap, VSTREAM_FN); + break; + case VSTREAM_CTL_WRITE_FN: + stream->write_fn = va_arg(ap, VSTREAM_FN); + break; + case VSTREAM_CTL_PATH: + if (stream->path) + myfree(stream->path); + stream->path = mystrdup(va_arg(ap, char *)); + break; + case VSTREAM_CTL_DOUBLE: + if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0) { + stream->buf.flags |= VSTREAM_FLAG_DOUBLE; + if (stream->buf.flags & VSTREAM_FLAG_READ) { + VSTREAM_SAVE_STATE(stream, read_buf, read_fd); + VSTREAM_FORK_STATE(stream, write_buf, write_fd); + } else { + VSTREAM_SAVE_STATE(stream, write_buf, write_fd); + VSTREAM_FORK_STATE(stream, read_buf, read_fd); + } + } + break; + case VSTREAM_CTL_READ_FD: + if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0) + msg_panic("VSTREAM_CTL_READ_FD requires double buffering"); + stream->read_fd = va_arg(ap, int); + stream->buf.flags |= VSTREAM_FLAG_NSEEK; + break; + case VSTREAM_CTL_WRITE_FD: + if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0) + msg_panic("VSTREAM_CTL_WRITE_FD requires double buffering"); + stream->write_fd = va_arg(ap, int); + stream->buf.flags |= VSTREAM_FLAG_NSEEK; + break; + default: + msg_panic("%s: bad name %d", myname, name); + } + } +} + +/* vstream_vfprintf - formatted print engine */ + +VSTREAM *vstream_vfprintf(VSTREAM *vp, const char *format, va_list ap) +{ + vbuf_print(&vp->buf, format, ap); + return (vp); +} diff --git a/postfix/util/vstream.h b/postfix/util/vstream.h new file mode 100644 index 000000000..b7540bdba --- /dev/null +++ b/postfix/util/vstream.h @@ -0,0 +1,117 @@ +#ifndef _VSTREAM_H_INCLUDED_ +#define _VSTREAM_H_INCLUDED_ + +/*++ +/* NAME +/* vstream 3h +/* SUMMARY +/* simple buffered I/O package +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include + + /* + * Simple buffered stream. The members of this structure are not part of the + * official interface and can change without prior notice. + */ +typedef int (*VSTREAM_FN) (int, void *, unsigned); + +typedef struct VSTREAM { + VBUF buf; /* generic intelligent buffer */ + int fd; /* file handle, no 256 limit */ + VSTREAM_FN read_fn; /* buffer fill action */ + VSTREAM_FN write_fn; /* buffer fill action */ + long offset; /* cached seek info */ + char *path; /* give it at least try */ + int read_fd; /* read channel (double-buffered) */ + int write_fd; /* write channel (double-buffered) */ + VBUF read_buf; /* read buffer (double-buffered) */ + VBUF write_buf; /* write buffer (double-buffered) */ +} VSTREAM; + +extern VSTREAM vstream_fstd[]; /* pre-defined streams */ + +#define VSTREAM_IN (&vstream_fstd[0]) +#define VSTREAM_OUT (&vstream_fstd[1]) +#define VSTREAM_ERR (&vstream_fstd[2]) + +#define VSTREAM_FLAG_ERR VBUF_FLAG_ERR /* some I/O error */ +#define VSTREAM_FLAG_EOF VBUF_FLAG_EOF /* end of file */ +#define VSTREAM_FLAG_FIXED VBUF_FLAG_FIXED /* fixed-size buffer */ +#define VSTREAM_FLAG_BAD VBUF_FLAG_BAD + +#define VSTREAM_FLAG_READ (1<<8) /* read buffer */ +#define VSTREAM_FLAG_WRITE (1<<9) /* write buffer */ +#define VSTREAM_FLAG_SEEK (1<<10) /* seek info valid */ +#define VSTREAM_FLAG_NSEEK (1<<11) /* can't seek this file */ +#define VSTREAM_FLAG_DOUBLE (1<<12) /* double buffer */ + +#define VSTREAM_BUFSIZE 4096 + +extern VSTREAM *vstream_fopen(const char *, int, int); +extern int vstream_fclose(VSTREAM *); +extern long vstream_fseek(VSTREAM *, long, int); +extern long vstream_ftell(VSTREAM *); +extern int vstream_fflush(VSTREAM *); +extern int vstream_fputs(const char *, VSTREAM *); +extern VSTREAM *vstream_fdopen(int, int); + +#define vstream_fread(v, b, n) vbuf_read(&(v)->buf, (b), (n)) +#define vstream_fwrite(v, b, n) vbuf_write(&(v)->buf, (b), (n)) + +#define VSTREAM_PUTC(ch, vp) VBUF_PUT(&(vp)->buf, (ch)) +#define VSTREAM_GETC(vp) VBUF_GET(&(vp)->buf) +#define vstream_ungetc(vp, ch) vbuf_unget(&(vp)->buf, (ch)) +#define VSTREAM_EOF VBUF_EOF + +#define VSTREAM_PUTCHAR(ch) VSTREAM_PUTC((ch), VSTREAM_OUT) +#define VSTREAM_GETCHAR() VSTREAM_GETC(VSTREAM_IN) + +#define vstream_fileno(vp) ((vp)->fd) +#define vstream_ferror(vp) vbuf_error(&(vp)->buf) +#define vstream_feof(vp) vbuf_eof(&(vp)->buf) +#define vstream_clearerr(vp) vbuf_clearerr(&(vp)->buf) +#define VSTREAM_PATH(vp) ((vp)->path ? (vp)->path : "unknown_stream") + +extern void vstream_control(VSTREAM *, int,...); + +#define VSTREAM_CTL_END 0 +#define VSTREAM_CTL_READ_FN 1 +#define VSTREAM_CTL_WRITE_FN 2 +#define VSTREAM_CTL_PATH 3 +#define VSTREAM_CTL_DOUBLE 4 +#define VSTREAM_CTL_READ_FD 5 +#define VSTREAM_CTL_WRITE_FD 6 + +extern VSTREAM *vstream_printf(const char *,...); +extern VSTREAM *vstream_fprintf(VSTREAM *, const char *,...); + +extern VSTREAM *vstream_popen(const char *, int); +extern int vstream_pclose(VSTREAM *); + +extern VSTREAM *vstream_vfprintf(VSTREAM *, const char *, va_list); + +/* 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/vstream_popen.c b/postfix/util/vstream_popen.c new file mode 100644 index 000000000..739af60b5 --- /dev/null +++ b/postfix/util/vstream_popen.c @@ -0,0 +1,195 @@ +/*++ +/* NAME +/* vstream_popen 3 +/* SUMMARY +/* open stream to child process +/* SYNOPSIS +/* #include +/* +/* VSTREAM *vstream_popen(command, flags) +/* cont char *command; +/* int flags; +/* +/* int vstream_pclose(stream) +/* VSTREAM *stream; +/* DESCRIPTION +/* vstream_popen() opens a one-way or two-way stream to the specified +/* \fIcommand\fR, which is executed by a child process. The \fIflags\fR +/* argument is as with vstream_fopen(). The child's standard input and +/* standard output are redirected to the stream, which is based on a +/* socketpair. +/* +/* vstream_pclose() closes the named stream and returns the child +/* exit status. It is an error to specify a stream that was not +/* returned by vstream_popen() or that is no longer open. +/* DIAGNOSTICS +/* Panics: interface violations. Fatal errors: out of memory. +/* +/* vstream_popen() returns a null pointer in case of trouble. +/* The nature of the problem is specified via the \fIerrno\fR +/* global variable. +/* +/* vstream_pclose() returns -1 in case of trouble. +/* The nature of the problem is specified via the \fIerrno\fR +/* global variable. +/* SEE ALSO +/* vstream(3) light-weight buffered I/O +/* BUGS +/* The interface, stolen from popen()/pclose(), ignores errors +/* returned when the stream is closed, and does not distinguish +/* between exit status codes and kill signals. +/* 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 + +/* Utility library. */ + +#include +#include +#include +#include + +/* Application-specific. */ + +static BINHASH *vstream_popen_table = 0; + +/* vstream_popen - open stream to child process */ + +VSTREAM *vstream_popen(const char *command, int flags) +{ + VSTREAM *stream; + int sockfd[2]; + int pid; + int fd; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0) + return (0); + + switch (pid = fork()) { + case -1: /* error */ + return (0); + case 0: /* child */ + if (close(sockfd[1])) + msg_warn("close: %m"); + for (fd = 0; fd < 2; fd++) + if (sockfd[0] != fd) + if (dup2(sockfd[0], fd) < 0) + msg_fatal("dup2: %m"); + if (sockfd[0] >= 2 && close(sockfd[0])) + msg_warn("close: %m"); + exec_command(command); + /* NOTREACHED */ + default: /* parent */ + if (close(sockfd[0])) + msg_warn("close: %m"); + stream = vstream_fdopen(sockfd[1], flags); + if (vstream_popen_table == 0) + vstream_popen_table = binhash_create(10); + binhash_enter(vstream_popen_table, (char *) &stream, + sizeof(stream), (char *) pid); + return (stream); + } +} + +/* vstream_pclose - close stream to child process */ + +int vstream_pclose(VSTREAM *stream) +{ + char *myname = "vstream_pclose"; + BINHASH_INFO *info; + int pid; + WAIT_STATUS_T wait_status; + + /* + * Sanity check. + */ + if (vstream_popen_table == 0 + || (info = binhash_locate(vstream_popen_table, (char *) &stream, + sizeof(stream))) == 0) + msg_panic("%s: spurious stream %p", myname, (char *) stream); + + /* + * Close the stream and reap the child exit status. Ignore errors while + * flushing the stream. The child might already have terminated. + */ + (void) vstream_fclose(stream); + do { + pid = waitpid((pid_t) info->value, &wait_status, 0); + } while (pid == -1 && errno == EINTR); + binhash_delete(vstream_popen_table, (char *) &stream, sizeof(stream), + (void (*) (char *)) 0); + + return (pid == -1 ? -1 : + WIFSIGNALED(wait_status) ? WTERMSIG(wait_status) : + WEXITSTATUS(wait_status)); +} + +#ifdef TEST + +#include +#include +#include + + /* + * Test program. Run a command and copy lines one by one. + */ +int main(int argc, char **argv) +{ + VSTRING *buf = vstring_alloc(100); + VSTREAM *stream; + int status; + + /* + * Sanity check. + */ + if (argc != 2) + msg_fatal("usage: %s 'command'", argv[0]); + + /* + * Open stream to child process. + */ + if ((stream = vstream_popen(argv[1], O_RDWR)) == 0) + msg_fatal("vstream_popen: %m"); + + /* + * Copy loop, one line at a time. + */ + while (vstring_fgets(buf, stream) != 0) { + if (vstream_fwrite(VSTREAM_OUT, vstring_str(buf), VSTRING_LEN(buf)) + != VSTRING_LEN(buf)) + msg_fatal("vstream_fwrite: %m"); + if (vstream_fflush(VSTREAM_OUT) != 0) + msg_fatal("vstream_fflush: %m"); + if (vstring_fgets(buf, VSTREAM_IN) == 0) + break; + if (vstream_fwrite(stream, vstring_str(buf), VSTRING_LEN(buf)) + != VSTRING_LEN(buf)) + msg_fatal("vstream_fwrite: %m"); + } + + /* + * Cleanup. + */ + vstring_free(buf); + if ((status = vstream_pclose(stream)) != 0) + msg_warn("exit status: %d", status); + + exit(status); +} + +#endif diff --git a/postfix/util/vstring.c b/postfix/util/vstring.c new file mode 100644 index 000000000..6bd7717b0 --- /dev/null +++ b/postfix/util/vstring.c @@ -0,0 +1,484 @@ +/* NAME +/* vstring 3 +/* SUMMARY +/* arbitrary-length string manager +/* SYNOPSIS +/* #include +/* +/* VSTRING *vstring_alloc(len) +/* int len; +/* +/* vstring_ctl(vp, type, value, ..., VSTRING_CTL_END) +/* VSTRING *vp; +/* int type; +/* +/* VSTRING *vstring_free(vp) +/* VSTRING *vp; +/* +/* char *vstring_str(vp) +/* VSTRING *vp; +/* +/* VSTRING *VSTRING_LEN(vp) +/* VSTRING *vp; +/* +/* char *vstring_end(vp) +/* VSTRING *vp; +/* +/* void VSTRING_ADDCH(vp, ch) +/* VSTRING *vp; +/* int ch; +/* +/* int VSTRING_SPACE(vp, len) +/* VSTRING *vp; +/* int len; +/* +/* int vstring_avail(vp) +/* VSTRING *vp; +/* +/* VSTRING *vstring_truncate(vp, len) +/* VSTRING *vp; +/* int len; +/* +/* void VSTRING_RESET(vp) +/* VSTRING *vp; +/* +/* void VSTRING_TERMINATE(vp) +/* VSTRING *vp; +/* +/* void VSTRING_SKIP(vp) +/* VSTRING *vp; +/* +/* VSTRING *vstring_strcpy(vp, src) +/* VSTRING *vp; +/* const char *src; +/* +/* VSTRING *vstring_strncpy(vp, src, len) +/* VSTRING *vp; +/* const char *src; +/* int len; +/* +/* VSTRING *vstring_strcat(vp, src) +/* VSTRING *vp; +/* const char *src; +/* +/* VSTRING *vstring_strncat(vp, src, len) +/* VSTRING *vp; +/* const char *src; +/* int len; +/* +/* VSTRING *vstring_sprintf(vp, format, ...) +/* VSTRING *vp; +/* const char *format; +/* +/* VSTRING *vstring_sprintf_append(vp, format, ...) +/* VSTRING *vp; +/* const char *format; +/* +/* VSTRING *vstring_vsprintf(vp, format, ap) +/* VSTRING *vp; +/* const char *format; +/* va_list ap; +/* +/* VSTRING *vstring_vsprintf_append(vp, format, ap) +/* VSTRING *vp; +/* const char *format; +/* va_list ap; +/* AUXILIARY FUNCTIONS +/* char *vstring_export(vp) +/* VSTRING *vp; +/* +/* VSTRING *vstring_import(str) +/* char *str; +/* DESCRIPTION +/* The functions and macros in this module implement arbitrary-length +/* strings and common operations on those strings. The strings do not +/* need to be null terminated and may contain arbitrary binary data. +/* The strings manage their own memory and grow automatically when full. +/* The optional string null terminator does not add to the string length. +/* +/* vstring_alloc() allocates storage for a variable-length string +/* of at least "len" bytes. The minimal length is 1. The result +/* is a null-terminated string of length zero. +/* +/* vstring_ctl() gives control over memory management policy. +/* The function takes a VSTRING pointer and a list of zero +/* or more (name,value) pairs. The expected valye type of the +/* value depends on the specified name. The name codes are: +/* .IP VSTRING_CTL_MAXLEN (int) +/* Specifies a hard upper limit on a string's length. When the +/* length would be exceeded, the program simulates a memory +/* allocation problem (i.e. it terminates through msg_fatal()). +/* .IP VSTRING_CTL_END (no value) +/* Specifies the end of the argument list. Forgetting to terminate +/* the argument list may cause the program to crash. +/* .PP +/* VSTRING_SPACE() ensures that the named string has room for +/* "len" more characters. VSTRING_SPACE() is an unsafe macro +/* that either returns zero or never returns. +/* +/* vstring_avail() returns the number of bytes that can be placed +/* into the buffer before the buffer would need to grow. +/* +/* vstring_free() reclaims storage for a variable-length string. +/* It conveniently returns a null pointer. +/* +/* vstring_str() is a macro that returns the string value +/* of a variable-length string. It is a safe macro that +/* evaluates its argument only once. +/* +/* VSTRING_LEN() is a macro that returns the current length of +/* its argument (i.e. the distance from the start of the string +/* to the current write position). VSTRING_LEN() is an unsafe macro +/* that evaluates its argument more than once. +/* +/* vstring_end() is a macro that returns the current write position of +/* its argument. It is a safe macro that evaluates its argument only once. +/* +/* VSTRING_ADDCH() adds a character to a variable-length string +/* and extends the string if it fills up. \fIvs\fP is a pointer +/* to a VSTRING structure; \fIch\fP the character value to be written. +/* The result is the written character. +/* Note that VSTRING_ADDCH() is an unsafe macro that evaluates some +/* arguments more than once. The result is NOT null-terminated. +/* +/* vstring_truncate() truncates the named string to the specified +/* length. The operation has no effect when the string is shorter. +/* The string is not null-terminated. +/* +/* VSTRING_RESET() is a macro that resets the write position of its +/* string argument to the very beginning. Note that VSTRING_RESET() +/* is an unsafe macro that evaluates some arguments more than once. +/* The result is NOT null-terminated. +/* +/* VSTRING_TERMINATE() null-terminates its string argument. +/* VSTRING_TERMINATE() is an unsafe macro that evaluates some +/* arguments more than once. +/* VSTRING_TERMINATE() does not return an interesting result. +/* +/* VSTRING_SKIP() is a macro that moves the write position to the first +/* null byte after the current write position. VSTRING_SKIP() is an unsafe +/* macro that evaluates some arguments more than once. +/* +/* vstring_strcpy() copies a null-terminated string to a variable-length +/* string. \fIsrc\fP provides the data to be copied; \fIvp\fP is the +/* target and result value. The result is null-terminated. +/* +/* vstring_strncpy() copies at most \fIlen\fR characters. Otherwise it is +/* identical to vstring_strcpy(). +/* +/* vstring_strcat() appends a null-terminated string to a variable-length +/* string. \fIsrc\fP provides the data to be copied; \fIvp\fP is the +/* target and result value. The result is null-terminated. +/* +/* vstring_strncat() copies at most \fIlen\fR characters. Otherwise it is +/* identical to vstring_strcat(). +/* +/* vstring_sprintf() produces a formatted string according to its +/* \fIformat\fR argument. See vstring_vsprintf() for details. +/* +/* vstring_sprintf_append() is like vstring_sprintf(), but appends +/* to the end of the result buffer. +/* +/* vstring_vsprintf() returns a null-terminated string according to +/* the \fIformat\fR argument. It understands the s, c, d, u, +/* o, x, X, p, e, f and g format types, the l modifier, field width +/* and precision, sign, and null or space padding. This module +/* can format strings as large as available memory permits. +/* +/* vstring_vsprintf_append() is like vstring_vsprintf(), but appends +/* to the end of the result buffer. +/* +/* In addition to stdio-like format specifiers, vstring_vsprintf() +/* recognizes %m and expands it to the corresponding errno text. +/* +/* vstring_export() extracts the string value from a VSTRING. +/* The VSTRING is destroyed. The result should be passed to myfree(). +/* +/* vstring_import() takes a `bare' string and converts it to +/* a VSTRING. The string argument must be obtained from mymalloc(). +/* The string argument is not copied. +/* DIAGNOSTICS +/* Fatal errors: memory allocation failure. +/* BUGS +/* Auto-resizing may change the address of the string data in +/* a vstring structure. Beware of dangling pointers. +/* HISTORY +/* .ad +/* .fi +/* A vstring module appears in the UNPROTO software by Wietse Venema. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System libraries. */ + +#include +#include +#include /* 44BSD stdarg.h uses abort() */ +#include +#include + +/* Utility library. */ + +#include "mymalloc.h" +#include "msg.h" +#include "vbuf_print.h" +#include "vstring.h" + +/* vstring_extend - variable-length string buffer extension policy */ + +static void vstring_extend(VBUF *bp, int incr) +{ + unsigned used = bp->ptr - bp->data; + + /* + * Note: vp->vbuf.len is the current buffer size (both on entry and on + * exit of this routine). We round up the increment size to the buffer + * size to avoid silly little buffer increments. With really large + * strings we might want to abandon the length doubling strategy, and go + * to fixed increments. + */ + bp->len += (bp->len > incr ? bp->len : incr); + bp->data = (unsigned char *) myrealloc((char *) bp->data, bp->len); + bp->ptr = bp->data + used; + bp->cnt = bp->len - used; +} + +/* vstring_buf_get_ready - vbuf callback for read buffer empty condition */ + +static int vstring_buf_get_ready(VBUF *unused_buf) +{ + msg_panic("vstring_buf_get: write-only buffer"); +} + +/* vstring_buf_put_ready - vbuf callback for write buffer full condition */ + +static int vstring_buf_put_ready(VBUF *bp) +{ + vstring_extend(bp, 0); + return (0); +} + +/* vstring_buf_space - vbuf callback to reserve space */ + +static int vstring_buf_space(VBUF *bp, int len) +{ + int need; + + if (len < 0) + msg_panic("vstring_buf_space: bad length %d", len); + if ((need = len - bp->cnt) > 0) + vstring_extend(bp, need); + return (0); +} + +/* vstring_alloc - create variable-length string */ + +VSTRING *vstring_alloc(int len) +{ + VSTRING *vp; + + if (len < 1) + msg_panic("vstring_alloc: bad length %d", len); + vp = (VSTRING *) mymalloc(sizeof(*vp)); + vp->vbuf.flags = 0; + vp->vbuf.data = (unsigned char *) mymalloc(len); + vp->vbuf.len = len; + VSTRING_RESET(vp); + vp->vbuf.data[0] = 0; + vp->vbuf.get_ready = vstring_buf_get_ready; + vp->vbuf.put_ready = vstring_buf_put_ready; + vp->vbuf.space = vstring_buf_space; + vp->maxlen = 0; + return (vp); +} + +/* vstring_free - destroy variable-length string */ + +VSTRING *vstring_free(VSTRING *vp) +{ + if (vp->vbuf.data) + myfree((char *) vp->vbuf.data); + myfree((char *) vp); + return (0); +} + +/* vstring_ctl - modify memory management policy */ + +void vstring_ctl(VSTRING *vp,...) +{ + va_list ap; + int code; + + va_start(ap, vp); + while ((code = va_arg(ap, int)) != VSTRING_CTL_END) { + switch (code) { + default: + msg_panic("vstring_ctl: unknown code: %d", code); + case VSTRING_CTL_MAXLEN: + vp->maxlen = va_arg(ap, int); + if (vp->maxlen < 0) + msg_panic("vstring_ctl: bad max length %d", vp->maxlen); + break; + } + } + va_end(ap); +} + +/* vstring_truncate - truncate string */ + +VSTRING *vstring_truncate(VSTRING *vp, int len) +{ + if (len < 0) + msg_panic("vstring_truncate: bad length %d", len); + if (len < VSTRING_LEN(vp)) + VSTRING_AT_OFFSET(vp, len); + return (vp); +} + +/* vstring_strcpy - copy string */ + +VSTRING *vstring_strcpy(VSTRING *vp, const char *src) +{ + VSTRING_RESET(vp); + + while (*src) { + VSTRING_ADDCH(vp, *src); + src++; + } + VSTRING_TERMINATE(vp); + return (vp); +} + +/* vstring_strncpy - copy string of limited length */ + +VSTRING *vstring_strncpy(VSTRING *vp, const char *src, int len) +{ + VSTRING_RESET(vp); + + while (len-- > 0 && *src) { + VSTRING_ADDCH(vp, *src); + src++; + } + VSTRING_TERMINATE(vp); + return (vp); +} + +/* vstring_strcat - append string */ + +VSTRING *vstring_strcat(VSTRING *vp, const char *src) +{ + while (*src) { + VSTRING_ADDCH(vp, *src); + src++; + } + VSTRING_TERMINATE(vp); + return (vp); +} + +/* vstring_strncat - append string of limited length */ + +VSTRING *vstring_strncat(VSTRING *vp, const char *src, int len) +{ + while (len-- > 0 && *src) { + VSTRING_ADDCH(vp, *src); + src++; + } + VSTRING_TERMINATE(vp); + return (vp); +} + +/* vstring_export - VSTRING to bare string */ + +char *vstring_export(VSTRING *vp) +{ + char *cp; + + cp = (char *) vp->vbuf.data; + vp->vbuf.data = 0; + myfree((char *) vp); + return (cp); +} + +/* vstring_import - bare string to vstring */ + +VSTRING *vstring_import(char *str) +{ + VSTRING *vp; + int len; + + vp = (VSTRING *) mymalloc(sizeof(*vp)); + len = strlen(str); + vp->vbuf.data = (unsigned char *) str; + vp->vbuf.len = len + 1; + VSTRING_AT_OFFSET(vp, len); + vp->maxlen = 0; + return (vp); +} + +/* vstring_sprintf - formatted string */ + +VSTRING *vstring_sprintf(VSTRING *vp, const char *format,...) +{ + va_list ap; + + va_start(ap, format); + vp = vstring_vsprintf(vp, format, ap); + va_end(ap); + return (vp); +} + +/* vstring_vsprintf - format string, vsprintf-like interface */ + +VSTRING *vstring_vsprintf(VSTRING *vp, const char *format, va_list ap) +{ + VSTRING_RESET(vp); + vbuf_print(&vp->vbuf, format, ap); + VSTRING_TERMINATE(vp); + return (vp); +} + +/* vstring_sprintf_append - append formatted string */ + +VSTRING *vstring_sprintf_append(VSTRING *vp, const char *format,...) +{ + va_list ap; + + va_start(ap, format); + vp = vstring_vsprintf_append(vp, format, ap); + va_end(ap); + return (vp); +} + +/* vstring_vsprintf_append - append format string, vsprintf-like interface */ + +VSTRING *vstring_vsprintf_append(VSTRING *vp, const char *format, va_list ap) +{ + vbuf_print(&vp->vbuf, format, ap); + VSTRING_TERMINATE(vp); + return (vp); +} + +#ifdef TEST + + /* + * Test program - concatenate all command-line arguments into one string. + */ +#include + +main(int argc, char **argv) +{ + VSTRING *vp = vstring_alloc(1); + + while (argc-- > 0) { + vstring_strcat(vp, *argv++); + vstring_strcat(vp, "."); + } + printf("argv concatenated: %s\n", vstring_str(vp)); + vstring_free(vp); +} + +#endif diff --git a/postfix/util/vstring.h b/postfix/util/vstring.h new file mode 100644 index 000000000..4d4a972bf --- /dev/null +++ b/postfix/util/vstring.h @@ -0,0 +1,98 @@ +#ifndef _VSTRING_H_INCLUDED_ +#define _VSTRING_H_INCLUDED_ + +/*++ +/* NAME +/* vstring 3h +/* SUMMARY +/* arbitrary-length string manager +/* SYNOPSIS +/* #include "vstring.h" +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include + + /* + * We can't allow bare VBUFs in the interface, because VSTRINGs have a + * specific initialization and destruction sequence. + */ +typedef struct VSTRING { + VBUF vbuf; + int maxlen; +} VSTRING; + +extern void vstring_init(VSTRING *, int); +extern void vstring_wipe(VSTRING *); +extern VSTRING *vstring_alloc(int); +extern void vstring_ctl(VSTRING *,...); +extern VSTRING *vstring_truncate(VSTRING *, int); +extern VSTRING *vstring_free(VSTRING *); +extern VSTRING *vstring_strcpy(VSTRING *, const char *); +extern VSTRING *vstring_strncpy(VSTRING *, const char *, int); +extern VSTRING *vstring_strcat(VSTRING *, const char *); +extern VSTRING *vstring_strncat(VSTRING *, const char *, int); +extern VSTRING *vstring_sprintf(VSTRING *, const char *,...); +extern VSTRING *vstring_sprintf_append(VSTRING *, const char *,...); +extern char *vstring_export(VSTRING *); +extern VSTRING *vstring_import(char *); + +#define VSTRING_CTL_MAXLEN 1 +#define VSTRING_CTL_END 0 + + /* + * Macros. Unsafe macros have UPPERCASE names. + */ +#define VSTRING_SPACE(vp, len) ((vp)->vbuf.space(&(vp)->vbuf, len)) +#define vstring_str(vp) ((char *) (vp)->vbuf.data) +#define VSTRING_LEN(vp) ((vp)->vbuf.ptr - (vp)->vbuf.data) +#define vstring_end(vp) ((char *) (vp)->vbuf.ptr) +#define VSTRING_TERMINATE(vp) { if ((vp)->vbuf.cnt <= 0) \ + VSTRING_SPACE((vp),1); \ + *(vp)->vbuf.ptr = 0; } +#define VSTRING_RESET(vp) { (vp)->vbuf.ptr = (vp)->vbuf.data; \ + (vp)->vbuf.cnt = (vp)->vbuf.len; } +#define VSTRING_ADDCH(vp, ch) VBUF_PUT(&(vp)->vbuf, ch) +#define VSTRING_SKIP(vp) { while ((vp)->vbuf.cnt > 0 && *(vp)->vbuf.ptr) \ + (vp)->vbuf.ptr++, (vp)->vbuf.cnt--; } +#define vstring_avail(vp) ((vp)->vbuf.cnt) + + /* + * The following macro is not part of the public interface, because it can + * really screw up a buffer by positioning past allocated memory. + */ +#define VSTRING_AT_OFFSET(vp, offset) { \ + (vp)->vbuf.ptr = (vp)->vbuf.data + (offset); \ + (vp)->vbuf.cnt = (vp)->vbuf.len - (offset); \ + } + +extern VSTRING *vstring_vsprintf(VSTRING *, const char *, va_list); +extern VSTRING *vstring_vsprintf_append(VSTRING *, const char *, va_list); + +/* BUGS +/* Auto-resizing may change the address of the string data in +/* a vstring structure. Beware of dangling pointers. +/* HISTORY +/* .ad +/* .fi +/* A vstring module appears in the UNPROTO software by Wietse Venema. +/* 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/vstring_vstream.c b/postfix/util/vstring_vstream.c new file mode 100644 index 000000000..078d255e1 --- /dev/null +++ b/postfix/util/vstring_vstream.c @@ -0,0 +1,180 @@ +/*++ +/* NAME +/* vstring_vstream 3 +/* SUMMARY +/* auto-resizing string library, standard I/O interface +/* SYNOPSIS +/* #include +/* +/* int vstring_get(vp, fp) +/* VSTRING *vp; +/* VSTREAM *fp; +/* +/* int vstring_get_nonl(vp, fp) +/* VSTRING *vp; +/* VSTREAM *fp; +/* +/* int vstring_get_null(vp, fp) +/* VSTRING *vp; +/* VSTREAM *fp; +/* +/* int vstring_get_bound(vp, fp, bound) +/* VSTRING *vp; +/* VSTREAM *fp; +/* int bound; +/* +/* int vstring_get_nonl_bound(vp, fp, bound) +/* VSTRING *vp; +/* VSTREAM *fp; +/* int bound; +/* DESCRIPTION +/* The routines in this module each read one newline or null-terminated +/* string from an input stream. In all cases the result is either the +/* last character read, typically the record terminator, or VSTREAM_EOF. +/* +/* vstring_get() reads one line from the named stream, including the +/* terminating newline character if present. +/* +/* vstring_get_nonl() reads a line from the named stream and strips +/* the trailing newline character. +/* +/* vstring_get_null() reads a null-terminated string from the named +/* stream. +/* +/* vstring_get_bound() and vstring_get_nonl_bound() read no more +/* than \fIbound\fR characters. Otherwise they behave like the +/* unbounded versions documented above. +/* DIAGNOSTICS +/* Fatal errors: memory allocation failure. +/* Panic: improper string bound. +/* 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 + +/* Application-specific. */ + +#include "msg.h" +#include "vstring.h" +#include "vstream.h" +#include "vstring_vstream.h" + + /* + * Macro to return the last character added to a VSTRING, for consistency. + */ +#define VSTRING_GET_RESULT(vp) \ + (VSTRING_LEN(vp) > 0 ? vstring_end(vp)[-1] : VSTREAM_EOF) + +/* vstring_get - read line from file, keep newline */ + +int vstring_get(VSTRING *vp, VSTREAM *fp) +{ + int c; + + VSTRING_RESET(vp); + while ((c = VSTREAM_GETC(fp)) != VSTREAM_EOF) { + VSTRING_ADDCH(vp, c); + if (c == '\n') + break; + } + VSTRING_TERMINATE(vp); + return (VSTRING_GET_RESULT(vp)); +} + +/* vstring_get_nonl - read line from file, strip newline */ + +int vstring_get_nonl(VSTRING *vp, VSTREAM *fp) +{ + int c; + + VSTRING_RESET(vp); + while ((c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != '\n') + VSTRING_ADDCH(vp, c); + VSTRING_TERMINATE(vp); + return (c == '\n' ? c : VSTRING_GET_RESULT(vp)); +} + +/* vstring_get_null - read null-terminated string from file */ + +int vstring_get_null(VSTRING *vp, VSTREAM *fp) +{ + int c; + + VSTRING_RESET(vp); + while ((c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != 0) + VSTRING_ADDCH(vp, c); + VSTRING_TERMINATE(vp); + return (c == 0 ? c : VSTRING_GET_RESULT(vp)); +} + +/* vstring_get_bound - read line from file, keep newline, up to bound */ + +int vstring_get_bound(VSTRING *vp, VSTREAM *fp, int bound) +{ + int c; + + if (bound <= 0) + msg_panic("vstring_get_bound: invalid bound %d", bound); + + VSTRING_RESET(vp); + while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF) { + VSTRING_ADDCH(vp, c); + if (c == '\n') + break; + } + VSTRING_TERMINATE(vp); + return (VSTRING_GET_RESULT(vp)); +} + +/* vstring_get_nonl_bound - read line from file, strip newline, up to bound */ + +int vstring_get_nonl_bound(VSTRING *vp, VSTREAM *fp, int bound) +{ + int c; + + if (bound <= 0) + msg_panic("vstring_get_nonl_bound: invalid bound %d", bound); + + VSTRING_RESET(vp); + while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != '\n') + VSTRING_ADDCH(vp, c); + VSTRING_TERMINATE(vp); + return (c == '\n' ? c : VSTRING_GET_RESULT(vp)); +} + +#ifdef TEST + + /* + * Proof-of-concept test program: copy the source to this module to stdout. + */ +#include + +#define TEXT_VSTREAM "vstring_vstream.c" + +main(void) +{ + VSTRING *vp = vstring_alloc(1); + VSTREAM *fp; + + if ((fp = vstream_fopen(TEXT_VSTREAM, O_RDONLY, 0)) == 0) + msg_fatal("open %s: %m", TEXT_VSTREAM); + while (vstring_fgets(vp, fp)) + vstream_fprintf(VSTREAM_OUT, "%s", vstring_str(vp)); + vstream_fclose(fp); + vstream_fflush(VSTREAM_OUT); + vstring_free(vp); +} + +#endif diff --git a/postfix/util/vstring_vstream.h b/postfix/util/vstring_vstream.h new file mode 100644 index 000000000..897167ca6 --- /dev/null +++ b/postfix/util/vstring_vstream.h @@ -0,0 +1,54 @@ +#ifndef _VSTRING_VSTREAM_H_INCLUDED_ +#define _VSTRING_VSTREAM_H_INCLUDED_ + +/*++ +/* NAME +/* vstring_vstream 3h +/* SUMMARY +/* auto-resizing string library +/* SYNOPSIS +/* #include +/* DESCRIPTION + + /* + * Utility library. + */ +#include +#include + + /* + * External interface. + */ +extern int vstring_get(VSTRING *, VSTREAM *); +extern int vstring_get_nonl(VSTRING *, VSTREAM *); +extern int vstring_get_null(VSTRING *, VSTREAM *); +extern int vstring_get_bound(VSTRING *, VSTREAM *, int); +extern int vstring_get_nonl_bound(VSTRING *, VSTREAM *, int); + + /* + * Backwards compatibility for code that still uses the vstring_fgets() + * interface. Unfortunately we can't change the macro name to upper case. + */ +#define vstring_fgets(s, p) \ + (vstring_get((s), (p)) == VSTREAM_EOF ? 0 : (s)) +#define vstring_fgets_nonl(s, p) \ + (vstring_get_nonl((s), (p)) == VSTREAM_EOF ? 0 : (s)) +#define vstring_fgets_null(s, p) \ + (vstring_get_null((s), (p)) == VSTREAM_EOF ? 0 : (s)) +#define vstring_fgets_bound(s, p, l) \ + (vstring_get_bound((s), (p), (l)) == VSTREAM_EOF ? 0 : (s)) +#define vstring_fgets_nonl_bound(s, p, l) \ + (vstring_get_nonl_bound((s), (p), (l)) == VSTREAM_EOF ? 0 : (s)) + +/* 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/writable.c b/postfix/util/writable.c new file mode 100644 index 000000000..3e1da8c86 --- /dev/null +++ b/postfix/util/writable.c @@ -0,0 +1,88 @@ +/*++ +/* NAME +/* writable 3 +/* SUMMARY +/* test if descriptor is writable +/* SYNOPSIS +/* #include +/* +/* int writable(fd) +/* int fd; +/* DESCRIPTION +/* writable() asks the kernel if the specified file descriptor +/* is writable, i.e. a read operation would not block. +/* +/* Arguments: +/* .IP fd +/* File descriptor in the range 0..FD_SETSIZE. +/* DIAGNOSTICS +/* All system call errors are fatal. +/* 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 + +#ifdef USE_SYS_SELECT_H +#include +#endif + +/* Utility library. */ + +#include +#include + +/* writable - see if file descriptor is writable */ + +int writable(int fd) +{ + struct timeval tv; + fd_set write_fds; + fd_set except_fds; + + /* + * Sanity checks. + */ + if (fd >= FD_SETSIZE) + msg_fatal("fd %d does not fit in FD_SETSIZE", fd); + + /* + * Initialize. + */ + FD_ZERO(&write_fds); + FD_SET(fd, &write_fds); + FD_ZERO(&except_fds); + FD_SET(fd, &except_fds); + tv.tv_sec = 0; + tv.tv_usec = 0; + + /* + * Loop until we have an authoritative answer. + */ + for (;;) { + switch (select(fd + 1, (fd_set *) 0, &write_fds, &except_fds, &tv)) { + case -1: + if (errno != EINTR) + msg_fatal("select"); + continue; + default: + return (FD_ISSET(fd, &write_fds)); + case 0: + return (0); + } + } +} diff --git a/postfix/util/write_buf.c b/postfix/util/write_buf.c new file mode 100644 index 000000000..c6b0ad792 --- /dev/null +++ b/postfix/util/write_buf.c @@ -0,0 +1,75 @@ +/*++ +/* NAME +/* write_buf 3 +/* SUMMARY +/* write buffer or bust +/* SYNOPSIS +/* #include +/* +/* int write_buf(fd, buf, len, timeout) +/* int fd; +/* const char *buf; +/* int len; +/* int timeout; +/* DESCRIPTION +/* write_buf() writes a buffer to the named stream in as many +/* fragments as needed, and returns the number of bytes written, +/* which is always the number requested or an error indication. +/* +/* Arguments: +/* .IP fd +/* File descriptor in the range 0..FD_SETSIZE. +/* .IP buf +/* Address of data to be written. +/* .IP len +/* Amount of data to be written. +/* .IP timeout +/* Bounds the time in seconds to wait until \fIfd\fD becomes writable. +/* A value <= 0 means do not wait; this is useful only when \fIfd\fR +/* uses blocking I/O. +/* DIAGNOSTICS +/* write_buf() returns -1 in case of trouble. The global \fIerrno\fR +/* variable reflects the nature of the problem. +/* 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 + +/* Utility library. */ + +#include +#include + +/* write_buf - write buffer or bust */ + +int write_buf(int fd, const char *buf, int len, int timeout) +{ + int count; + + while (len > 0) { + if (timeout > 0 && write_wait(fd, timeout) < 0) + return (-1); + if ((count = write(fd, buf, len)) < 0) { + if (errno == EAGAIN && timeout > 0) + continue; + return (-1); + } + if (count == 0) + msg_fatal("write returned 0"); + buf += count; + len -= count; + } + return (len); +} diff --git a/postfix/util/write_wait.c b/postfix/util/write_wait.c new file mode 100644 index 000000000..a17c0e595 --- /dev/null +++ b/postfix/util/write_wait.c @@ -0,0 +1,102 @@ +/*++ +/* NAME +/* write_wait 3 +/* SUMMARY +/* wait until descriptor becomes writable +/* SYNOPSIS +/* #include +/* +/* int write_wait(fd, timeout) +/* int fd; +/* int timeout; +/* DESCRIPTION +/* write_wait() blocks the current process until the specified file +/* descriptor becomes writable, or until the deadline is exceeded. +/* +/* Arguments: +/* .IP fd +/* File descriptor in the range 0..FD_SETSIZE. +/* .IP timeout +/* If positive, deadline in seconds. A zero value effects a poll. +/* A negative value means wait until something happens. +/* DIAGNOSTICS +/* Panic: interface violation. All system call errors are fatal. +/* +/* A zero result means success. When the specified deadline is +/* exceeded, write_wait() returns -1 and sets errno to ETIMEDOUT. +/* 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 + +#ifdef USE_SYS_SELECT_H +#include +#endif + +/* Utility library. */ + +#include +#include + +/* write_wait - block with timeout until file descriptor is writable */ + +int write_wait(int fd, int timeout) +{ + fd_set write_fds; + fd_set except_fds; + struct timeval tv; + struct timeval *tp; + + /* + * Sanity checks. + */ + if (FD_SETSIZE <= fd) + msg_panic("descriptor %d does not fit FD_SETSIZE %d", fd, FD_SETSIZE); + + /* + * Guard the write() with select() so we do not depend on alarm() and on + * signal() handlers. Restart the select when interrupted by some signal. + * Some select() implementations may reduce the time to wait when + * interrupted, which is exactly what we want. + */ + FD_ZERO(&write_fds); + FD_SET(fd, &write_fds); + FD_ZERO(&except_fds); + FD_SET(fd, &except_fds); + if (timeout >= 0) { + tv.tv_usec = 0; + tv.tv_sec = timeout; + tp = &tv; + } else { + tp = 0; + } + + for (;;) { + switch (select(fd + 1, (fd_set *) 0, &write_fds, &except_fds, tp)) { + case -1: + if (errno != EINTR) + msg_fatal("select: %m"); + continue; + case 0: + errno = ETIMEDOUT; + return (-1); + default: + return (0); + } + } +}