--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+.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
--- /dev/null
+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 <address>" 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 <recipient>"
+ information was in the wrong place. Pointed out by Jon
+ Ribbens, Oaktree Internet Solutions Ltd.
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+# 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
--- /dev/null
+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
--- /dev/null
+# 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
--- /dev/null
+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.
--- /dev/null
+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".
--- /dev/null
+
+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.
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <mail_queue.h>
+#include <mail_params.h>
+#include <config.h>
+#include <bounce.h>
+
+/* Single-threaded server skeleton. */
+
+#include <mail_server.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <quote_822_local.h>
+#include <deliver_flock.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdlib.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+
+/* 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;
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <events.h>
+#include <line_wrap.h>
+#include <name_mask.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <mail_proto.h>
+#include <quote_822_local.h>
+#include <mail_params.h>
+#include <canon_addr.h>
+#include <is_header.h>
+#include <record.h>
+#include <rec_type.h>
+#include <config.h>
+#include <post_mail.h>
+#include <mail_addr.h>
+#include <mark_corrupt.h>
+#include <mail_error.h>
+
+/* Application-specific. */
+
+#include "bounce_service.h"
+
+#define STR vstring_str
+
+/* bounce_header - generate bounce message header */
+
+static int bounce_header(VSTREAM *bounce, VSTRING *buf, 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* bounce_service 3h
+/* SUMMARY
+/* bounce message service
+/* SYNOPSIS
+/* #include <bounce_service.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * 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
+/*--*/
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <mymalloc.h>
+#include <iostuff.h>
+
+/* Global library. */
+
+#include <config.h>
+#include <cleanup_user.h>
+#include <mail_queue.h>
+#include <mail_proto.h>
+#include <opened.h>
+#include <bounce.h>
+#include <mail_params.h>
+#include <mail_stream.h>
+#include <mail_addr.h>
+
+/* Single-threaded server skeleton. */
+
+#include <mail_server.h>
+
+/* Application-specific. */
+
+#include "cleanup.h"
+
+ /*
+ * Global state: any queue files that we have open, so that the error
+ * handler can clean up in case of trouble.
+ */
+char *cleanup_path; /* queue file name */
+
+ /*
+ * 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* cleanup 3h
+/* SUMMARY
+/* canonicalize and enqueue message
+/* SYNOPSIS
+/* #include "cleanup.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+#include <vstream.h>
+#include <argv.h>
+
+ /*
+ * Global library.
+ */
+#include <maps.h>
+#include <tok822.h>
+#include <been_here.h>
+#include <mail_stream.h>
+
+ /*
+ * 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
+/*--*/
--- /dev/null
+/*++
+/* NAME
+/* cleanup_envelope 3
+/* SUMMARY
+/* process envelope segment
+/* SYNOPSIS
+/* #include <cleanup.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <record.h>
+#include <rec_type.h>
+#include <cleanup_user.h>
+#include <tok822.h>
+#include <mail_params.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <argv.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <cleanup_user.h>
+#include <record.h>
+#include <rec_type.h>
+
+/* 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, "");
+}
--- /dev/null
+/*++
+/* NAME
+/* cleanup_map11 3
+/* SUMMARY
+/* one-to-one mapping
+/* SYNOPSIS
+/* #include <cleanup.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <dict.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <cleanup_user.h>
+#include <mail_addr_map.h>
+#include <quote_822_local.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* cleanup_map1n 3
+/* SUMMARY
+/* one-to-many address mapping
+/* SYNOPSIS
+/* #include <cleanup.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <msg.h>
+#include <argv.h>
+#include <vstring.h>
+#include <dict.h>
+
+/* Global library. */
+
+#include <mail_addr_map.h>
+#include <cleanup_user.h>
+#include <quote_822_local.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* cleanup_masquerade 3
+/* SUMMARY
+/* address masquerading
+/* SYNOPSIS
+/* #include <cleanup.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <argv.h>
+#include <htable.h>
+#include <mymalloc.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <tok822.h>
+#include <quote_822_local.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <argv.h>
+#include <split_at.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <record.h>
+#include <rec_type.h>
+#include <cleanup_user.h>
+#include <tok822.h>
+#include <header_opts.h>
+#include <quote_822_local.h>
+#include <mail_params.h>
+#include <mail_date.h>
+#include <mail_addr.h>
+#include <is_header.h>
+
+/* 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();
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <errno.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <record.h>
+#include <rec_type.h>
+#include <cleanup_user.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <argv.h>
+
+/* Global library. */
+
+#include <been_here.h>
+#include <mail_params.h>
+#include <rec_type.h>
+
+/* 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);
+ }
+}
--- /dev/null
+/*++
+/* NAME
+/* cleanup_rewrite 3
+/* SUMMARY
+/* address canonicalization
+/* SYNOPSIS
+/* #include <cleanup.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+
+/* Global library. */
+
+#include <tok822.h>
+#include <rewrite_clnt.h>
+#include <quote_822_local.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* cleanup_skip 3
+/* SUMMARY
+/* skip further input
+/* SYNOPSIS
+/* #include "cleanup.h"
+/*
+/* void cleanup_skip(void)
+/* DESCRIPTION
+/* cleanup_skip() skips client input. This function is used after
+/* detecting an error. The idea is to drain input from the client
+/* so the client is ready to read the cleanup service status report.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <record.h>
+#include <rec_type.h>
+
+/* 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 */ ;
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <been_here.h>
+#include <mail_params.h>
+
+/* 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);
+}
--- /dev/null
+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.
--- /dev/null
+#++
+# 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
+#--
--- /dev/null
+#++
+# 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
+#--
--- /dev/null
+#++
+# 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
+#--
--- /dev/null
+# 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.
--- /dev/null
+#
+# 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)
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#++
+# 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
+#--
--- /dev/null
+# 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
--- /dev/null
+# 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 =
--- /dev/null
+# 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
--- /dev/null
+# 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 =
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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 =
--- /dev/null
+# 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 =
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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 =
--- /dev/null
+# 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 =
--- /dev/null
+#++
+# 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
+#--
--- /dev/null
+#++
+# 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
+#--
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+#ifndef _DNS_H_INCLUDED_
+#define _DNS_H_INCLUDED_
+
+/*++
+/* NAME
+/* dns 3h
+/* SUMMARY
+/* domain name service lookup
+/* SYNOPSIS
+/* #include <dns.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#ifdef RESOLVE_H_NEEDS_STDIO_H
+#include <stdio.h>
+#endif
+#include <resolv.h>
+
+ /*
+ * Name server compatibility. These undocumented macros appear in the file
+ * <arpa/nameser.h>, 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 <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* dns_lookup 3
+/* SUMMARY
+/* domain name service lookup
+/* SYNOPSIS
+/* #include <dns.h>
+/*
+/* 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 <sys_defs.h>
+#include <netdb.h>
+#include <stdlib.h> /* BSDI stdarg.h uses abort() */
+#include <stdarg.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <vstring.h>
+#include <msg.h>
+#include <valid_hostname.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* dns_rr 3
+/* SUMMARY
+/* resource record memory and list management
+/* SYNOPSIS
+/* #include <dns.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* dns_strerror 3
+/* SUMMARY
+/* name service lookup error code to string
+/* SYNOPSIS
+/* #include <dhs.h>
+/*
+/* 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 <sys_defs.h>
+#include <netdb.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+
+/* 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));
+}
--- /dev/null
+/*++
+/* NAME
+/* dns_strtype 3
+/* SUMMARY
+/* name service lookup type codes and printable forms
+/* SYNOPSIS
+/* #include <dns.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <vstring.h>
+
+/* 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);
+}
+
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <msg.h>
+#include <msg_vstream.h>
+
+/* 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);
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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
+/* <i>between</i> directories, and should also have an option to use
+/* <i>hashed</i> 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 <sys_defs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <msg_vstream.h>
+
+/* 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);
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* NAME
+/* been_here 3
+/* SUMMARY
+/* detect repeated occurrence of string
+/* SYNOPSIS
+/* #include <been_here.h>
+/*
+/* 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 <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <vstring.h>
+#include <stringops.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _BEEN_HERE_H_INCLUDED_
+#define _BEEN_HERE_H_INCLUDED_
+
+/*++
+/* NAME
+/* been_here 3h
+/* SUMMARY
+/* detect repeated occurrence of string
+/* SYNOPSIS
+/* #include <been_here.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <stdarg.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* bounce 3
+/* SUMMARY
+/* bounce service client
+/* SYNOPSIS
+/* #include <bounce.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <time.h>
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+
+/* 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);
+ }
+}
--- /dev/null
+#ifndef _BOUNCE_H_INCLUDED_
+#define _BOUNCE_H_INCLUDED_
+
+/*++
+/* NAME
+/* bounce 3h
+/* SUMMARY
+/* bounce service client
+/* SYNOPSIS
+/* #include <bounce.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <time.h>
+#include <stdarg.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* canon_addr 3
+/* SUMMARY
+/* simple address canonicalization
+/* SYNOPSIS
+/* #include <canon_addr.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <mymalloc.h>
+
+/* 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));
+}
--- /dev/null
+#ifndef _CANON_ADDR_H_INCLUDED_
+#define _CANON_ADDR_H_INCLUDED_
+
+/*++
+/* NAME
+/* canon_addr 3h
+/* SUMMARY
+/* simple address canonicalization
+/* SYNOPSIS
+/* #include <canon_addr.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* clean_env 3
+/* SUMMARY
+/* clean up the environment
+/* SYNOPSIS
+/* #include <clean_env.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+
+/* 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.
+ */
+}
--- /dev/null
+#ifndef _CLEAN_ENV_H_INCLUDED_
+#define _CLEAN_ENV_H_INCLUDED_
+
+/*++
+/* NAME
+/* clean_env 3h
+/* SUMMARY
+/* clean up the environment
+/* SYNOPSIS
+/* #include <clean_env.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* cleanup_strerror 3
+/* SUMMARY
+/* cleanup status code to string
+/* SYNOPSIS
+/* #include <cleanup_user.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+
+/* 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));
+}
--- /dev/null
+#ifndef _CLEANUP_USER_H_INCLUDED_
+#define _CLEANUP_USER_H_INCLUDED_
+
+/*++
+/* NAME
+/* cleanup_user 3h
+/* SUMMARY
+/* cleanup user interface codes
+/* SYNOPSIS
+/* #include <cleanup_user.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* config 3
+/* SUMMARY
+/* global configuration parameter management
+/* SYNOPSIS
+/* #include <config.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <dict.h>
+#include <safe.h>
+#include <stringops.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _CONFIG_H_INCLUDED_
+#define _CONFIG_H_INCLUDED_
+
+/*++
+/* NAME
+/* config 3h
+/* SUMMARY
+/* global configuration parameter management
+/* SYNOPSIS
+/* #include <config.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* config_bool 3
+/* SUMMARY
+/* boolean-valued configuration parameter support
+/* SYNOPSIS
+/* #include <config.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <dict.h>
+
+/* 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++;
+ }
+}
--- /dev/null
+/*++
+/* NAME
+/* config_int 3
+/* SUMMARY
+/* integer-valued configuration parameter support
+/* SYNOPSIS
+/* #include <config.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h>
+#include <stdio.h> /* sscanf() */
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <dict.h>
+#include <stringops.h>
+
+/* 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++;
+ }
+}
--- /dev/null
+/*++
+/* NAME
+/* config_str 3
+/* SUMMARY
+/* string-valued global configuration parameter support
+/* SYNOPSIS
+/* #include <config.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+
+/* 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++;
+ }
+}
--- /dev/null
+/*++
+/* NAME
+/* debug_peer 3
+/* SUMMARY
+/* increase verbose logging for specific peers
+/* SYNOPSIS
+/* #include <debug_peer.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <namadr_list.h>
+#include <debug_peer.h>
+
+/* 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;
+ }
+}
--- /dev/null
+#ifndef _DEBUG_PEER_H_INCLUDED_
+#define _DEBUG_PEER_H_INCLUDED_
+/*++
+/* NAME
+/* debug_peer 3h
+/* SUMMARY
+/* increase verbose logging for specific peers
+/* SYNOPSIS
+/* #include <debug_peer.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* debug_process 3
+/* SUMMARY
+/* run an external debugger
+/* SYNOPSIS
+/* #include <debug_process.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _DEBUG_PROCESS_H_INCLUDED_
+#define _DEBUG_PROCESS_H_INCLUDED_
+
+/*++
+/* NAME
+/* debug_process 3h
+/* SUMMARY
+/* run an external debugger
+/* SYNOPSIS
+/* #include <unistd.h>
+/* #include <debug_process.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* defer 3
+/* SUMMARY
+/* defer service client interface
+/* SYNOPSIS
+/* #include <defer.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+
+/* 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);
+ }
+}
--- /dev/null
+#ifndef _DEFER_H_INCLUDED_
+#define _DEFER_H_INCLUDED_
+
+/*++
+/* NAME
+/* defer 3h
+/* SUMMARY
+/* defer service client interface
+/* SYNOPSIS
+/* #include <defer.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <time.h>
+#include <stdarg.h>
+
+ /*
+ * Global library.
+ */
+#include <bounce.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* deliver_completed 3
+/* SUMMARY
+/* recipient delivery completion
+/* SYNOPSIS
+/* #include <deliver_completed.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+
+/* 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));
+}
--- /dev/null
+#ifndef _DELIVER_COMPLETED_H_INCLUDED_
+#define _DELIVER_COMPLETED_H_INCLUDED_
+
+/*++
+/* NAME
+/* deliver_completed 3h
+/* SUMMARY
+/* recipient delivery completion
+/* SYNOPSIS
+/* #include <deliver_completed.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* deliver_flock 3
+/* SUMMARY
+/* lock open file for mail delivery
+/* SYNOPSIS
+/* #include <deliver_flock.h>
+/*
+/* 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 <unistd.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <myflock.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _DELIVER_FLOCK_H_INCLUDED_
+#define _DELIVER_FLOCK_H_INCLUDED_
+
+/*++
+/* NAME
+/* deliver_flock 3h
+/* SUMMARY
+/* lock open file for mail delivery
+/* SYNOPSIS
+/* #include <deliver_flock.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* deliver_request 3
+/* SUMMARY
+/* mail delivery request protocol, server side
+/* SYNOPSIS
+/* #include <deliver_request.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <mymalloc.h>
+#include <iostuff.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _DELIVER_REQUEST_H_INCLUDED_
+#define _DELIVER_REQUEST_H_INCLUDED_
+
+/*++
+/* NAME
+/* deliver_request 3h
+/* SUMMARY
+/* mail delivery request protocol, server side
+/* SYNOPSIS
+/* #include <deliver_request.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+#include <vstream.h>
+
+ /*
+ * Global library.
+ */
+#include <recipient_list.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* domain_list 3
+/* SUMMARY
+/* match a host or domain name against a pattern list
+/* SYNOPSIS
+/* #include <domain_list.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <match_list.h>
+#include <match_ops.h>
+
+/* 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 <msg.h>
+#include <stdlib.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+
+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
--- /dev/null
+#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 <domain_list.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* dot_lockfile 3
+/* SUMMARY
+/* dotlock file management
+/* SYNOPSIS
+/* #include <dot_lockfile.h>
+/*
+/* 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 <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <time.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <stringops.h>
+#include <mymalloc.h>
+
+/* 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 <msg.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+#include <config.h>
+
+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
--- /dev/null
+#ifndef _DOT_LOCKFILE_H_INCLUDED_
+#define _DOT_LOCKFILE_H_INCLUDED_
+
+/*++
+/* NAME
+/* dot_lockfile 3h
+/* SUMMARY
+/* dotlock file management
+/* SYNOPSIS
+/* #include <dot_lockfile.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* file_id 3
+/* SUMMARY
+/* file ID printable representation
+/* SYNOPSIS
+/* #include <file_id.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <string.h>
+
+/* Utility library */
+
+#include <msg.h>
+#include <vstring.h>
+
+/* 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));
+}
--- /dev/null
+#ifndef _FILE_ID_H_INCLUDED_
+#define _FILE_ID_H_INCLUDED_
+
+/*++
+/* NAME
+/* file_id 3h
+/* SUMMARY
+/* file ID printable representation
+/* SYNOPSIS
+/* #include <file_id.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* header_opts 3
+/* SUMMARY
+/* message header classification
+/* SYNOPSIS
+/* #include <header_opts.h>
+/*
+/* 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 <sys_defs.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <vstring.h>
+
+/* 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)));
+}
--- /dev/null
+#ifndef _HEADER_OPTS_H_INCLUDED_
+#define _HEADER_OPTS_H_INCLUDED_
+
+/*++
+/* NAME
+/* header_opts 3h
+/* SUMMARY
+/* message header classification
+/* SYNOPSIS
+/* #include <header_opts.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* is_header 3
+/* SUMMARY
+/* message header classification
+/* SYNOPSIS
+/* #include <is_header.h>
+/*
+/* 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 <ctype.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _IS_HEADER_H_INCLUDED_
+#define _IS_HEADER_H_INCLUDED_
+
+/*++
+/* NAME
+/* is_header 3h
+/* SUMMARY
+/* message header classification
+/* SYNOPSIS
+/* #include <is_header.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* mail_addr 3
+/* SUMMARY
+/* pre-defined mail addresses
+/* SYNOPSIS
+/* #include <mail_addr.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <stringops.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _MAIL_ADDR_H_INCLUDED_
+#define _MAIL_ADDR_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_addr 3h
+/* SUMMARY
+/* pre-defined mail addresses
+/* SYNOPSIS
+/* #include <mail_addr.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* mail_addr_crunch 3
+/* SUMMARY
+/* parse and canonicalize addresses, apply address extension
+/* SYNOPSIS
+/* #include <mail_addr_crunch.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <argv.h>
+#include <vstring.h>
+
+/* Global library. */
+
+#include <tok822.h>
+#include <canon_addr.h>
+#include <mail_addr_crunch.h>
+
+/* 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 <unistd.h>
+#include <msg.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <config.h>
+#include <mail_params.h>
+
+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
--- /dev/null
+#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 <mail_addr_crunch.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <argv.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* mail_addr_find 3
+/* SUMMARY
+/* generic address-based lookup
+/* SYNOPSIS
+/* #include <mail_addr_find.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <dict.h>
+#include <stringops.h>
+#include <mymalloc.h>
+#include <vstring.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <split_addr.h>
+#include <mail_addr_find.h>
+#include <resolve_local.h>
+
+/* 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 <vstream.h>
+#include <vstring_vstream.h>
+#include <config.h>
+
+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
--- /dev/null
+#ifndef _MAIL_ADDR_FIND_H_INCLUDED_
+#define _MAIL_ADDR_FIND_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_addr_find 3h
+/* SUMMARY
+/* generic address-based lookup
+/* SYNOPSIS
+/* #include <mail_addr_find.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Global library.
+ */
+#include <maps.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* mail_addr_map 3
+/* SUMMARY
+/* generic address mapping
+/* SYNOPSIS
+/* #include <mail_addr_map.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <dict.h>
+#include <argv.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <mail_addr_find.h>
+#include <mail_addr_crunch.h>
+#include <mail_addr_map.h>
+
+/* 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 <unistd.h>
+#include <config.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <mail_params.h>
+
+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
--- /dev/null
+#ifndef _MAIL_ADDR_MAP_H_INCLUDED_
+#define _MAIL_ADDR_MAP_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_addr_map 3h
+/* SUMMARY
+/* generic address mapping
+/* SYNOPSIS
+/* #include <mail_addr_map.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <argv.h>
+
+ /*
+ * Global library.
+ */
+#include <maps.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* mail_command_read 3
+/* SUMMARY
+/* single-command server
+/* SYNOPSIS
+/* #include <mail_proto.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <vstream.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* mail_command_write 3
+/* SUMMARY
+/* single-command client
+/* SYNOPSIS
+/* #include <mail_proto.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <vstream.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* mail_connect 3
+/* SUMMARY
+/* intra-mail system connection management
+/* SYNOPSIS
+/* #include <mail_proto.h>
+/*
+/* 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 <sys_defs.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <connect.h>
+#include <mymalloc.h>
+#include <iostuff.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* mail_copy 3
+/* SUMMARY
+/* copy message with extreme prejudice
+/* SYNOPSIS
+/* #include <mail_copy.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <vstring_vstream.h>
+#include <stringops.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _MAIL_COPY_H_INCLUDED_
+#define _MAIL_COPY_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_copy 3h
+/* SUMMARY
+/* copy message with extreme prejudice
+/* SYNOPSIS
+/* #include <mail_copy.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* mail_date 3
+/* SUMMARY
+/* return formatted time
+/* SYNOPSIS
+/* #include <mail_date.h>
+/*
+/* 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 <sys_defs.h>
+#include <time.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+
+/* 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 <vstream.h>
+
+main(void)
+{
+ vstream_printf("%s\n", mail_date(time((time_t *) 0)));
+ vstream_fflush(VSTREAM_OUT);
+ return (0);
+}
+
+#endif
--- /dev/null
+#ifndef _MAIL_DATE_H_INCLUDED_
+#define _MAIL_DATE_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_date 3h
+/* SUMMARY
+/* return formatted time
+/* SYNOPSIS
+/* #include <mail_date.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <time.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* mail_error 3
+/* SUMMARY
+/* mail error classes
+/* SYNOPSIS
+/* #include <mail_error.h>
+/*
+/* 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 <sys_defs.h>
+
+/* 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,
+};
--- /dev/null
+#ifndef _MAIL_ERROR_H_INCLUDED_
+#define _MAIL_ERROR_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_error 3h
+/* SUMMARY
+/* mail error classes
+/* SYNOPSIS
+/* #include <mail_error.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <name_mask.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* mail_flush 3
+/* SUMMARY
+/* flush backed up mail
+/* SYNOPSIS
+/* #include <mail_flush.h>
+/*
+/* 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 <mail_proto.h>
+#include <mail_flush.h>
+
+/* 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());
+}
--- /dev/null
+#ifndef _MAIL_FLUSH_H_INCLUDED_
+#define _MAIL_FLUSH_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_flush 3h
+/* SUMMARY
+/* flush backed up mail
+/* SYNOPSIS
+/* #include <mail_flush.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* mail_open_ok 3
+/* SUMMARY
+/* scrutinize mail queue file
+/* SYNOPSIS
+/* #include <mail_open_ok.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _MAIL_OPEN_OK_H_INCLUDED_
+#define _MAIL_OPEN_OK_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_open_ok 3h
+/* SUMMARY
+/* scrutinize mail queue file
+/* SYNOPSIS
+/* #include <mail_open_ok.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* mail_params 3
+/* SUMMARY
+/* global mail configuration parameters
+/* SYNOPSIS
+/* #include <mail_params.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <string.h>
+#include <pwd.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <get_hostname.h>
+#include <valid_hostname.h>
+
+/* 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");
+}
--- /dev/null
+#ifndef _MAIL_PARAMS_H_INCLUDED_
+#define _MAIL_PARAMS_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_params 3h
+/* SUMMARY
+/* globally configurable parameters
+/* SYNOPSIS
+/* #include <mail_params.h>
+/* 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 <sys_defs.h>.
+ */
+#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
--- /dev/null
+/*++
+/* NAME
+/* mail_pathname 3
+/* SUMMARY
+/* generate pathname from mailer service class and name
+/* SYNOPSIS
+/* #include <mail_proto.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <stringops.h>
+
+/* 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));
+}
--- /dev/null
+/*++
+/* NAME
+/* mail_print 3
+/* SUMMARY
+/* intra-mail system write routine
+/* SYNOPSIS
+/* #include <mail_proto.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _MAIL_PROTO_H_INCLUDED_
+#define _MAIL_PROTO_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_proto 3h
+/* SUMMARY
+/* mail internal IPC support
+/* SYNOPSIS
+/* #include <mail_proto.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <stdarg.h>
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <iostuff.h>
+
+ /*
+ * 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 <stdarg.h>
+ */
+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
--- /dev/null
+/*++
+/* NAME
+/* mail_queue 3
+/* SUMMARY
+/* mail queue file access
+/* SYNOPSIS
+/* #include <mail_queue.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdio.h> /* rename() */
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h> /* gettimeofday, not in POSIX */
+#include <string.h>
+#include <errno.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <mymalloc.h>
+#include <argv.h>
+#include <dir_forest.h>
+#include <make_dirs.h>
+#include <split_at.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _MAIL_QUEUE_H_INCLUDED_
+#define _MAIL_QUEUE_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_queue 3h
+/* SUMMARY
+/* mail queue access
+/* SYNOPSIS
+/* #include <mail_queue.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+#include <vstream.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* mail_run 3
+/* SUMMARY
+/* run mail component program
+/* SYNOPSIS
+/* #include <mail_run.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <stringops.h>
+#include <mymalloc.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _MAIL_RUN_H_INCLUDED_
+#define _MAIL_RUN_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_run 3h
+/* SUMMARY
+/* run mail component program
+/* SYNOPSIS
+/* #include <mail_run.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* mail_scan 3
+/* SUMMARY
+/* intra-mail read routine
+/* SYNOPSIS
+/* #include <mail_proto.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <mymalloc.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* mail_stream 3
+/* SUMMARY
+/* mail stream management
+/* SYNOPSIS
+/* #include <mail_stream.h>
+/*
+/* 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 <cleanup_user.h>.
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <cleanup_user.h>
+#include <mail_proto.h>
+#include <mail_queue.h>
+#include <opened.h>
+#include <mail_stream.h>
+
+/* 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);
+ }
+}
--- /dev/null
+#ifndef _MAIL_STREAM_H_INCLUDED_
+#define _MAIL_STREAM_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_stream 3h
+/* SUMMARY
+/* mail stream management
+/* SYNOPSIS
+/* #include <mail_stream.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* mail_task 3
+/* SUMMARY
+/* set task name for logging purposes
+/* SYNOPSIS
+/* #include <mail_task.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+
+/* 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));
+}
--- /dev/null
+#ifndef _MAIL_TASK_H_INCLUDED_
+#define _MAIL_TASK_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_task 3h
+/* SUMMARY
+/* canonicalize process name
+/* SYNOPSIS
+/* #include <mail_task.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* mail_trigger 3
+/* SUMMARY
+/* trigger a mail service
+/* SYNOPSIS
+/* #include <mail_proto.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <iostuff.h>
+#include <trigger.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _MAIL_VERSION_H_INCLUDED_
+#define _MAIL_VERSION_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_version 3h
+/* SUMMARY
+/* globally configurable parameters
+/* SYNOPSIS
+/* #include <mail_version.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* maps 3
+/* SUMMARY
+/* multi-dictionary search
+/* SYNOPSIS
+/* #include <maps.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <argv.h>
+#include <mymalloc.h>
+#include <msg.h>
+#include <dict.h>
+#include <stringops.h>
+#include <split_at.h>
+
+/* 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 <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+
+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
--- /dev/null
+#ifndef _MAPS_H_INCLUDED_
+#define _MAPS_H_INCLUDED_
+
+/*++
+/* NAME
+/* maps 3h
+/* SUMMARY
+/* multi-dictionary search
+/* SYNOPSIS
+/* #include <maps.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* mark_corrupt 3
+/* SUMMARY
+/* mark queue file as corrupt
+/* SYNOPSIS
+/* #include <mark_corrupt.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <mark_corrupt.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _MARK_CORRUPT_H_INCLUDED_
+#define _MARK_CORRUPT_H_INCLUDED_
+
+/*++
+/* NAME
+/* mark_corrupt 3h
+/* SUMMARY
+/* mark queue file as corrupt
+/* SYNOPSIS
+/* #include <mark_corrupt.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+
+ /*
+ * 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
--- /dev/null
+#ifndef _MKMAP_H_INCLUDED_
+#define _MKMAP_H_INCLUDED_
+
+/*++
+/* NAME
+/* mkmap 3h
+/* SUMMARY
+/* create or rewrite Postfix database
+/* SYNOPSIS
+/* #include <mkmap.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* mkmap_db 3
+/* SUMMARY
+/* create or open database, DB style
+/* SYNOPSIS
+/* #include <mkmap.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <dict.h>
+#include <dict_db.h>
+
+/* Application-specific. */
+
+#include "mkmap.h"
+
+#ifdef HAS_DB
+#ifdef PATH_DB_H
+#include PATH_DB_H
+#else
+#include <db.h>
+#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
--- /dev/null
+/*++
+/* NAME
+/* mkmap 3
+/* SUMMARY
+/* create or open database, DBM style
+/* SYNOPSIS
+/* #include <mkmap.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <dict.h>
+#include <dict_dbm.h>
+
+/* Application-specific. */
+
+#include "mkmap.h"
+
+#ifdef HAS_DBM
+#include <ndbm.h>
+
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* mkmap_open 3
+/* SUMMARY
+/* create or rewrite database, generic interface
+/* SYNOPSIS
+/* #include <mkmap.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <dict.h>
+#include <sigdelay.h>
+#include <mymalloc.h>
+#include <myflock.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* mynetworks 3
+/* SUMMARY
+/* generate patterns for my own interface addresses
+/* SYNOPSIS
+/* #include <mynetworks.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifndef IN_CLASSD_NET
+#define IN_CLASSD_NET 0xf0000000
+#define IN_CLASSD_NSHIFT 28
+#endif
+
+#define BITS_PER_ADDR 32
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <inet_addr_list.h>
+
+/* Global library. */
+
+#include <own_inet_addr.h>
+#include <mynetworks.h>
+
+/* 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
--- /dev/null
+#ifndef _MYNETWORKS_H_INCLUDED_
+#define _MYNETWORKS_H_INCLUDED_
+
+/*++
+/* NAME
+/* mynetworks 3h
+/* SUMMARY
+/* lookup patterns for my own interface addresses
+/* SYNOPSIS
+/* #include <mynetworks.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* mypwd 3
+/* SUMMARY
+/* caching getpwnam()/getpwuid()
+/* SYNOPSIS
+/* #include <mypwd.h>
+/*
+/* 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 <sys_defs.h>
+#include <pwd.h>
+#include <string.h>
+#ifdef USE_PATHS_H
+#include <paths.h>
+#endif
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <htable.h>
+#include <binhash.h>
+#include <msg.h>
+
+/* 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 <stdlib.h>
+#include <ctype.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+
+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
--- /dev/null
+#ifndef _MYPWNAM_H_INCLUDED_
+#define _MYPWNAM_H_INCLUDED_
+
+/*++
+/* NAME
+/* mypwnam 3h
+/* SUMMARY
+/* caching getpwnam()/getpwuid()
+/* SYNOPSIS
+/* #include <mypwd.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* namadr_list 3
+/* SUMMARY
+/* name/address list membership
+/* SYNOPSIS
+/* #include <namadr_list.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <match_list.h>
+#include <match_ops.h>
+
+/* 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 <msg.h>
+#include <stdlib.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+
+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
--- /dev/null
+#ifndef _NAMADR_LIST_H_INCLUDED_
+#define _NAMADR_LIST_H_INCLUDED_
+
+/*++
+/* NAME
+/* namadr 3h
+/* SUMMARY
+/* name/address membership
+/* SYNOPSIS
+/* #include <namadr_list_list.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* off_cvt 3
+/* SUMMARY
+/* off_t conversions
+/* SYNOPSIS
+/* #include <off_cvt.h>
+/*
+/* 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 <sys_defs.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+
+/* 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 <vstream.h>
+#include <vstring_vstream.h>
+
+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
--- /dev/null
+#ifndef _OFF_CVT_H_INCLUDED_
+#define _OFF_CVT_H_INCLUDED_
+
+/*++
+/* NAME
+/* off_cvt 3h
+/* SUMMARY
+/* off_t conversions
+/* SYNOPSIS
+/* #include <vstring.h>
+/* #include <off_cvt.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* opened 3
+/* SUMMARY
+/* log that a message was opened
+/* SYNOPSIS
+/* #include <opened.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _OPENED_H_INCLUDED_
+#define _OPENED_H_INCLUDED_
+
+/*++
+/* NAME
+/* opened 3h
+/* SUMMARY
+/* log that a message was opened
+/* SYNOPSIS
+/* #include <opened.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <stdarg.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* own_inet_addr 3
+/* SUMMARY
+/* determine if IP address belongs to this mail system instance
+/* SYNOPSIS
+/* #include <own_inet_addr.h>
+/*
+/* 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 <sys_defs.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <inet_addr_list.h>
+#include <inet_addr_local.h>
+#include <inet_addr_host.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <own_inet_addr.h>
+
+/* 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);
+}
--- /dev/null
+#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 <own_inet_addr.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <netinet/in.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* pipe_command 3
+/* SUMMARY
+/* deliver message to external command
+/* SYNOPSIS
+/* #include <pipe_command.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#ifdef USE_PATHS_H
+#include <paths.h>
+#endif
+#include <syslog.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <stringops.h>
+#include <iostuff.h>
+#include <timed_wait.h>
+#include <set_ugid.h>
+#include <argv.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_copy.h>
+#include <clean_env.h>
+#include <pipe_command.h>
+#include <exec_command.h>
+#include <sys_exits.h>
+
+/* 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);
+ }
+ }
+}
--- /dev/null
+#ifndef _PIPE_COMMAND_H_INCLUDED_
+#define _PIPE_COMMAND_H_INCLUDED_
+
+/*++
+/* NAME
+/* pipe_command 3h
+/* SUMMARY
+/* deliver message to external command
+/* SYNOPSIS
+/* #include <pipe_command.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+ * Global library.
+ */
+#include <mail_copy.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* post_mail 3
+/* SUMMARY
+/* convenient mail posting interface
+/* SYNOPSIS
+/* #include <post_mail.h>
+/*
+/* 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<cleanup_user.h>\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<cleanup_user.h>\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 <sys_defs.h>
+#include <time.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <record.h>
+#include <rec_type.h>
+#include <mail_proto.h>
+#include <cleanup_user.h>
+#include <post_mail.h>
+#include <mail_date.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _POST_MAIL_H_INCLUDED_
+#define _POST_MAIL_H_INCLUDED_
+
+/*++
+/* NAME
+/* post_mail 3h
+/* SUMMARY
+/* convenient mail posting interface
+/* SYNOPSIS
+/* #include <post_mail.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+
+ /*
+ * Global library.
+ */
+#include <cleanup_user.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* quote_822_local 3
+/* SUMMARY
+/* quote local part of mailbox
+/* SYNOPSIS
+/* #include <quote_822_local.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+
+/* 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 <vstream.h>
+#include <vstring_vstream.h>
+
+#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
--- /dev/null
+#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 <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <record.h>
+#include <rec_streamlf.h>
+
+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);
+}
--- /dev/null
+/*++
+/* NAME
+/* rec_streamlf 3
+/* SUMMARY
+/* record interface to stream-lf files
+/* SYNOPSIS
+/* #include <rec_streamlf.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <record.h>
+#include <rec_type.h>
+#include <rec_streamlf.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _REC_STREAMLF_H_INCLUDED_
+#define _REC_STREAMLF_H_INCLUDED_
+
+/*++
+/* NAME
+/* rec_streamlf 3h
+/* SUMMARY
+/* record interface to stream-lf files
+/* SYNOPSIS
+/* #include <rec_streamlf.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+#include <vstream.h>
+
+ /*
+ * Global library.
+ */
+#include <rec_type.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* rec_type 3
+/* SUMMARY
+/* Postfix record types
+/* SYNOPSIS
+/* #include <rec_type.h>
+/*
+/* 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");
+}
--- /dev/null
+#ifndef _REC_TYPE_H_INCLUDED_
+#define _REC_TYPE_H_INCLUDED_
+
+/*++
+/* NAME
+/* rec_type 3h
+/* SUMMARY
+/* Postfix record types
+/* SYNOPSIS
+/* #include <rec_type.h>
+/* 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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg_vstream.h>
+
+/* Global library. */
+
+#include <record.h>
+#include <rec_streamlf.h>
+#include <rec_type.h>
+
+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);
+}
--- /dev/null
+/*++
+/* NAME
+/* recipient_list 3
+/* SUMMARY
+/* in-core recipient structures
+/* SYNOPSIS
+/* #include <recipient_list.h>
+/*
+/* 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 <mymalloc.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _RECIPIENT_LIST_H_INCLUDED_
+#define _RECIPIENT_LIST_H_INCLUDED_
+
+/*++
+/* NAME
+/* recipient_list 3h
+/* SUMMARY
+/* recipient list structures
+/* SYNOPSIS
+/* #include <recipient_list.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* record 3
+/* SUMMARY
+/* simple typed record I/O
+/* SYNOPSIS
+/* #include <record.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifndef NBBY
+#define NBBY 8 /* XXX should be in sys_defs.h */
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+
+/* Global library. */
+
+#include <record.h>
+
+/* 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));
+}
--- /dev/null
+#ifndef _RECORD_H_INCLUDED_
+#define _RECORD_H_INCLUDED_
+
+/*++
+/* NAME
+/* record 3h
+/* SUMMARY
+/* simple typed record I/O
+/* SYNOPSIS
+/* #include <record.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <stdarg.h>
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+#include <vstream.h>
+
+ /*
+ * 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 <stdarg.h>
+ */
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* 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)));
+ }
+}
--- /dev/null
+/*++
+/* NAME
+/* resolve_clnt 3
+/* SUMMARY
+/* address resolve service client (internal forms)
+/* SYNOPSIS
+/* #include <resolve_clnt.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <vstring_vstream.h>
+#include <events.h>
+#include <iostuff.h>
+
+/* 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 <stdlib.h>
+#include <msg_vstream.h>
+#include <vstring_vstream.h>
+#include <config.h>
+
+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
--- /dev/null
+#ifndef _RESOLVE_CLNT_H_INCLUDED_
+#define _RESOLVE_CLNT_H_INCLUDED_
+
+/*++
+/* NAME
+/* resolve_clnt 3h
+/* SUMMARY
+/* address resolver client
+/* SYNOPSIS
+/* #include <resolve_clnt.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* resolve_local 3
+/* SUMMARY
+/* determine if address resolves to local mail system
+/* SYNOPSIS
+/* #include <resolve_local.h>
+/*
+/* 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 <sys_defs.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <string_list.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <own_inet_addr.h>
+#include <resolve_local.h>
+
+/* 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 <vstream.h>
+#include <config.h>
+
+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
--- /dev/null
+#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 <resolve_local.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* rewrite_clnt 3
+/* SUMMARY
+/* address rewrite service client
+/* SYNOPSIS
+/* #include <vstring.h>
+/* #include <rewrite_clnt.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <events.h>
+#include <iostuff.h>
+#include <quote_822_local.h>
+
+/* 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 <stdlib.h>
+#include <string.h>
+#include <msg_vstream.h>
+#include <vstring_vstream.h>
+#include <config.h>
+#include <mail_params.h>
+
+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
--- /dev/null
+#ifndef _REWRITE_CLNT_H_INCLUDED_
+#define _REWRITE_CLNT_H_INCLUDED_
+
+/*++
+/* NAME
+/* rewrite_clnt 3h
+/* SUMMARY
+/* address rewriter client
+/* SYNOPSIS
+/* #include <rewrite_clnt.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* sent 3
+/* SUMMARY
+/* log that a message was sent
+/* SYNOPSIS
+/* #include <sent.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdio.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _SENT_H_INCLUDED_
+#define _SENT_H_INCLUDED_
+
+/*++
+/* NAME
+/* sent 3h
+/* SUMMARY
+/* log that message was sent
+/* SYNOPSIS
+/* #include <sent.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <time.h>
+#include <stdarg.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* smtp_stream 3
+/* SUMMARY
+/* smtp stream I/O support
+/* SYNOPSIS
+/* #include <smtp_stream.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h> /* FD_ZERO() needs bzero() prototype */
+#include <errno.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <msg.h>
+#include <iostuff.h>
+
+/* 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);
+ }
+}
--- /dev/null
+#ifndef _SMTP_STREAM_H_INCLUDED_
+#define _SMTP_STREAM_H_INCLUDED_
+
+/*++
+/* NAME
+/* smtp_stream 3h
+/* SUMMARY
+/* smtp stream I/O support
+/* SYNOPSIS
+/* #include <smtp_stream.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <stdarg.h>
+#include <setjmp.h>
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+#include <vstream.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* split_addr 3
+/* SUMMARY
+/* recipient localpart address splitter
+/* SYNOPSIS
+/* #include <split_addr.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <split_at.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_addr.h>
+#include <split_addr.h>
+
+/* 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));
+}
--- /dev/null
+#ifndef _SPLIT_ADDR_H_INCLUDED_
+#define _SPLIT_ADDR_H_INCLUDED_
+
+/*++
+/* NAME
+/* split_addr 3h
+/* SUMMARY
+/* recipient localpart address splitter
+/* SYNOPSIS
+/* #include <split_addr.h>
+/* 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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <vstream.h>
+#include <vstring.h>
+
+/* Global library. */
+
+#include <record.h>
+#include <rec_streamlf.h>
+
+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);
+}
--- /dev/null
+/*++
+/* NAME
+/* string_list 3
+/* SUMMARY
+/* match a string against a pattern list
+/* SYNOPSIS
+/* #include <string_list.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <match_list.h>
+#include <match_ops.h>
+
+/* 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 <msg.h>
+#include <stdlib.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <msg_vstream.h>
+
+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
--- /dev/null
+#ifndef _STRING_LIST_H_INCLUDED_
+#define _STRING_LIST_H_INCLUDED_
+
+/*++
+/* NAME
+/* string_list 3h
+/* SUMMARY
+/* match a string against a pattern list
+/* SYNOPSIS
+/* #include <string_list.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* sys_exits 3
+/* SUMMARY
+/* sendmail-compatible exit status handling
+/* SYNOPSIS
+/* #include <sys_exits.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+
+/* Global library. */
+
+#include <sys_exits.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _SYS_EXITS_H_INCLUDED_
+#define _SYS_EXITS_H_INCLUDED_
+
+/*++
+/* NAME
+/* sys_exits 3h
+/* SUMMARY
+/* sendmail-compatible exit status handling
+/* SYNOPSIS
+/* #include <sys_exits.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* timed_ipc 3
+/* SUMMARY
+/* enforce IPC timeout on stream
+/* SYNOPSIS
+/* #include <time_ipc.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <iostuff.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _TIMED_IPC_H_INCLUDED_
+#define _TIMED_IPC_H_INCLUDED_
+
+/*++
+/* NAME
+/* timed_ipc 3h
+/* SUMMARY
+/* enforce IPC timeout on stream
+/* SYNOPSIS
+/* #include <timed_ipc.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+
+ /*
+ * 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
--- /dev/null
+#ifndef _TOK822_H_INCLUDED_
+#define _TOK822_H_INCLUDED_
+
+/*++
+/* NAME
+/* tok822 3h
+/* SUMMARY
+/* RFC822 token structures
+/* SYNOPSIS
+/* #include <tok822.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * Global library.
+ */
+#include <resolve_clnt.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* tok822_find 3
+/* SUMMARY
+/* token list search operators
+/* SYNOPSIS
+/* #include <tok822.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+
+/* Global library. */
+
+#include <tok822.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* tok822_node 3
+/* SUMMARY
+/* token memory management
+/* SYNOPSIS
+/* #include <tok822.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <vstring.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* tok822_parse 3
+/* SUMMARY
+/* RFC 822 address parser
+/* SYNOPSIS
+/* #include <tok822.h>
+/*
+/* 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 <sys_defs.h>
+#include <ctype.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <msg.h>
+
+/* 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 <route> */
+ 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 <vstream.h>
+#include <vstring_vstream.h>
+
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* tok822_resolve 3
+/* SUMMARY
+/* address resolving, client interface
+/* SYNOPSIS
+/* #include <tok822.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <msg.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* tok822_rewrite 3
+/* SUMMARY
+/* address rewriting, client interface
+/* SYNOPSIS
+/* #include <tok822.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <msg.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* tok822_tree 3
+/* SUMMARY
+/* assorted token tree operators
+/* SYNOPSIS
+/* #include <tok822.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <vstring.h>
+
+/* 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);
+}
--- /dev/null
+# 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 >$@
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+ACCESS(5) ACCESS(5)
+
+
+<b>NAME</b>
+ access - format of Postfix access table
+
+<b>SYNOPSIS</b>
+ <b>postmap</b> <b>/etc/postfix/access</b>
+
+<b>DESCRIPTION</b>
+ The optional <b>access</b> 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 <a href="postmap.1.html"><b>postmap</b>(1)</a> command. The
+ result, an indexed file in <b>dbm</b> or <b>db</b> format, is used for
+ fast searching by the mail system. After an update it may
+ take a minute or so before the change becomes visible.
+ Issue a <b>postfix</b> <b>reload</b> command to eliminate the delay.
+
+ The format of the access table is as follows:
+
+ blanks and comments
+ Blank lines are ignored, as are lines beginning
+ with `#'.
+
+ <i>pattern</i> <i>action</i>
+ When <i>pattern</i> matches a mail address, domain or host
+ address, perform the corresponding <i>action</i>.
+
+<b>PATTERNS</b>
+ Patterns are tried in the order as listed below:
+
+ <i>user</i>@<i>domain</i>
+ Matches the specified mail address.
+
+ <i>domain.name</i>
+ Matches the <i>domain.name</i> itself and any subdomain
+ thereof, either in hostnames or in mail addresses.
+ Top-level domains will never be matched.
+
+ <i>user</i>@ Matches all mail addresses with the specified user
+ part.
+
+ <i>net.work.addr.ess</i>
+
+ <i>net.work.addr</i>
+
+ <i>net.work</i>
+
+ <i>net</i> Matches any host address in the specified network.
+ A network address is a sequence of one or more
+ octets separated by ".".
+
+<b>ACTIONS</b>
+
+
+
+
+ 1
+
+
+
+
+
+ACCESS(5) ACCESS(5)
+
+
+ [<b>45</b>]<i>XX</i> <i>text</i>
+ Reject the address etc. that matches the pattern,
+ and respond with the numerical code and text.
+
+ <b>REJECT</b> Reject the address etc. that matches the pattern. A
+ generic error response message is generated.
+
+ <b>OK</b>
+
+ <i>Any</i> <i>other</i> <i>text</i>
+ Accept the address etc. that matches the pattern.
+
+<b>BUGS</b>
+ The table format does not understand quoting conventions.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="postmap.1.html">postmap(1)</a> create mapping table
+ <a href="smtpd.8.html">smtpd(8)</a> smtp server
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with
+ this software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+ALIASES(5) ALIASES(5)
+
+
+<b>NAME</b>
+ aliases - format of the Postfix alias database
+
+<b>SYNOPSIS</b>
+ <b>postalias</b> [<b>-c</b> <i>config_dir</i>] [<b>-v</b>] [<i>file_type</i>:]<i>input_file</i>
+
+<b>DESCRIPTION</b>
+ The <b>aliases</b> file provides a system-wide mechanism to redi-
+ rect mail for local recipients.
+
+ The file serves as input to the <a href="postalias.1.html"><b>postalias</b>(1)</a> command. The
+ result, an indexed file in <b>dbm</b> or <b>db</b> 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 <b>postfix</b> <b>reload</b> 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
+ <b>.forward</b> files in their home directory. Lines in per-user
+ <b>.forward</b> files have the same syntax as the right-hand side
+ of <b>aliases</b> entries.
+
+ The format of the alias database input file is as follows:
+
+ <b>o</b> An alias definition has the form
+
+ <i>name</i>: <i>value1</i>, <i>value2</i>, <i>...</i>
+
+ <b>o</b> Lines that begin with whitespace continue the pre-
+ vious line.
+
+ <b>o</b> Blank lines are ignored, as are lines beginning
+ with `#'.
+
+ The <i>name</i> is a local address (no domain part). Use double
+ quotes when the name contains any special characters such
+ as whitespace, `#', `:', or `@'. The <i>name</i> is folded to
+ lowercase, in order to make database lookups case insensi-
+ tive.
+
+ In addition, when an alias exists for <b>owner-</b><i>name</i>, 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 <i>value</i> contains one or more of the following:
+
+ <i>address</i>
+ Mail is forwarded to <i>address</i>, which is compatible
+
+
+
+ 1
+
+
+
+
+
+ALIASES(5) ALIASES(5)
+
+
+ with the RFC 822 standard.
+
+ <i>/file/name</i>
+ Mail is appended to <i>/file/name</i>. See <a href="local.8.html"><b>local</b>(8)</a> for
+ details of delivery to file. Delivery is not lim-
+ ited to regular files. For example, to dispose of
+ unwanted mail, deflect it to <b>/dev/null</b>.
+
+ |<i>command</i>
+ Mail is piped into <i>command</i>. Commands that contain
+ special characters, such as whitespace, should be
+ enclosed between double quotes. See <a href="local.8.html"><b>local</b>(8)</a> for
+ details of delivery to command.
+
+ When the command fails, a limited amount of command
+ output is mailed back to the sender. The file
+ <b>/usr/include/sysexits.h</b> defines the expected exit
+ status codes. For example, use <b>|"exit</b> <b>67"</b> to simu-
+ late a "user unknown" error, and <b>|"exit</b> <b>0"</b> to
+ implement an expensive black hole.
+
+ <b>:include:</b><i>/file/name</i>
+ Mail is sent to the destinations listed in the
+ named file. Lines in <b>:include:</b> 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
+ "|<i>command</i>" and <i>/file/name</i> is disallowed by default.
+ To enable, edit the <b>allow</b><i>_</i><b>mail</b><i>_</i><b>to</b><i>_</i><b>commands</b> and
+ <b>allow</b><i>_</i><b>mail</b><i>_</i><b>to</b><i>_</i><b>files</b> configuration parameters.
+
+<b>ADDRESS</b> <b>EXTENSION</b>
+ When alias database search fails, and the recipient local-
+ part contains the optional recipient delimiter (e.g.,
+ <i>user+foo</i>), the search is repeated for the unextended
+ address (e.g., <i>user</i>).
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this topic. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
+ command after a configuration change.
+
+ <b>alias</b><i>_</i><b>maps</b>
+ List of alias databases.
+
+ <b>allow</b><i>_</i><b>mail</b><i>_</i><b>to</b><i>_</i><b>commands</b>
+ Restrict the usage of mail delivery to external
+ command.
+
+ <b>allow</b><i>_</i><b>mail</b><i>_</i><b>to</b><i>_</i><b>files</b>
+ Restrict the usage of mail delivery to external
+ file.
+
+
+
+ 2
+
+
+
+
+
+ALIASES(5) ALIASES(5)
+
+
+ <b>recipient</b><i>_</i><b>delimiter</b>
+ Delimiter that separates recipients from address
+ extensions.
+
+<b>STANDARDS</b>
+ RFC 822 (ARPA Internet Text Messages)
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="local.8.html">local(8)</a> local delivery agent
+ <a href="postalias.1.html">postalias(1)</a> alias database management
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+
+
+</pre> </body> </html>
--- /dev/null
+<html>
+
+<head>
+
+<title>Postfix Mail System Anatomy</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix
+Mail System Anatomy</h1>
+
+<hr>
+
+<a href="index.html">Up one level</a> | <a href="overview.html">Postfix
+Overview</a> | Postfix Anatomy | <a href="config.html">Postfix
+Configuration</a> | <a href="faq.html">Postfix FAQ</a>
+
+<h2>Table of contents</h2>
+
+<ul>
+
+<li><a href="receiving.html">Receiving mail</a>. What path a
+message takes on its way into the Postfix mail system, and what
+programs are involved. Lots of daemons, mostly.
+
+<p>
+
+<li><a href="delivering.html">Delivering mail</a>. An introduction
+to the machinery that is responsible for delivering mail: queues,
+a queue manager, and more daemons.
+
+<p>
+
+<li><a href="backstage.html">Behind the scenes</a>. What happens
+behind the scenes while mail is flowing through the Postfix system:
+another manager, and still more daemons.
+
+<p>
+
+<li><a href="commands.html">Command-line utilities</a>. Auxiliary
+programs that people interact with when using the Postfix mail
+system. No daemons here.
+
+</ul>
+
+<hr>
+
+<a href="index.html">Up one level</a> | <a href="overview.html">Postfix
+Overview</a> | Postfix Anatomy | <a href="config.html">Postfix
+Configuration</a> | <a href="faq.html">Postfix FAQ</a>
+
+</body>
+
+</html>
--- /dev/null
+<html>
+
+<head>
+
+<title>Postfix Overview - Global Architecture</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix
+Overview - Global Architecture</h1>
+
+<hr>
+
+<a href="overview.html">Up one level</a> | <a
+href="motivation.html">Introduction</a> | <a href="goals.html">Goals
+and features</a> | Global architecture | <a href="queuing.html">Queue
+Management</a> | <a href="security.html">Security</a>
+
+<h2>Introduction</h2>
+
+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.
+
+<h2>Postfix architecture</h2>
+
+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.
+
+<p>
+
+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.
+
+<p>
+
+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 <i>inetd</i>-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.
+
+<p>
+
+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 <i>maildrop</I> directory from a file server,
+and runs no resident Postfix processes at all.
+
+<h2>Communication between Postfix processes</h2>
+
+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.
+
+<p>
+
+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.
+
+<p>
+
+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.
+
+<hr>
+
+<a href="overview.html">Up one level</a> | <a
+href="motivation.html">Introduction</a> | <a href="goals.html">Goals
+and features</a> | Global architecture | <a href="queuing.html">Queue
+Management</a> | <a href="security.html">Security</a>
+
+</body>
+
+</html>
--- /dev/null
+<html>
+
+<head>
+
+<title>Postfix Anatomy - Behind the Scenes</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix
+Anatomy - Behind the Scenes</h1>
+
+<hr>
+
+<a href="anatomy.html">Up one level</a> | <a
+href="receiving.html">Receiving Mail</a> | <a
+href="delivering.html">Delivering Mail</a> | Behind the Scenes |
+<a href="commands.html">Command-line Utilities</a>
+
+<p>
+
+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.
+
+<ul>
+
+<li>The <a href="master.8.html">master</a> 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 <a
+href="postfix.1.html">postfix</a> command, and keeps running until
+the system goes down. The <a href="master.8.html">master</a> daemon
+is responsible for starting all other Postfix daemon processes on
+demand, and for restarting daemons that terminated prematurely
+because of some problem. The <a href="master.8.html">master</a>
+daemon is also responsible for enforcing the daemon process count
+limits as specified in the <b>master.cf</b> configuration file.
+
+<p>
+
+<li>The <a href="bounce.8.html">bounce or defer</a> daemon is called
+upon left and right by other daemon processes, in order to maintain
+per-message log files with non-delivery status information.
+
+<p>
+
+<li>The <a href="trivial-rewrite.8.html">trivial-rewrite</a> daemon
+is called upon left and right by other daemon processes, in order
+to rewrite an address to <i>user@fully.qualified.domain</i> form,
+or in order to resolve a destination.
+
+<p>
+
+<li>The <a href="showq.8.html">showq</a> daemon lists the Postfix
+queue status. This is the program behind the <a
+href="mailq.1.html">mailq</a> command.
+
+</ul>
+
+<hr>
+
+<a href="anatomy.html">Up one level</a> | <a
+href="receiving.html">Receiving Mail</a> | <a
+href="delivering.html">Delivering Mail</a> | Behind the Scenes |
+<a href="commands.html">Command-line Utilities</a>
+
+</body>
+
+</html>
--- /dev/null
+<html>
+
+<head>
+
+<title> Postfix Configuration - Basics </title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix Configuration - Basics </h1>
+
+<hr>
+
+<a href="config.html">Up one level</a> | Basic Configuration | <a
+href="uce.html">UCE Controls</a> | <a href="rate.html"> Rate
+Controls</a> | <a href="resource.html"> Resource Controls</a> | <a
+href="rewrite.html"> Address Manipulation </a>
+
+<h2> Introduction </h2>
+
+Postfix has about 100 configuration parameters that are controlled
+via the <b>main.cf</b> 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:
+
+<ul>
+
+<li> <a href="#myorigin"> What domain to use in outbound mail </a>
+
+<p>
+
+<li> <a href="#mydestination"> What domains to receive mail for
+</a>
+
+</ul>
+
+The default values for many other configuration parameters are
+derived from just these two.
+
+<p>
+
+The third parameter of interest controls the amount of mail sent
+to the local postmaster:
+
+<ul>
+
+<li> <a href="#notify"> What trouble to report to the postmaster
+</a>
+
+</ul>
+
+<p>
+
+By the way, if you change parameters of a running Postfix system,
+don't forget to issue a <b>postfix reload</b> command.
+
+<p>
+
+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:
+
+<ul>
+
+<li> <a href="#myhostname"> My own hostname </a>
+
+<p>
+
+<li> <a href="#mydomain"> My own domain name </a>
+
+<p>
+
+<li> <a href="#mynetworks"> My own networks </a>
+
+<p>
+
+<li> <a href="#inet_interfaces"> My own network addresses </a>
+
+</ul>
+
+<a name="myorigin"> <h2> What domain to use in outbound mail </h2> </a>
+
+The <b>myorigin</b> parameter specifies the domain that appears in
+mail that is posted on this machine. The default is to use the
+local machine name, <b><a href="#myhostname"> $myhostname</a>, </b>
+which defaults to the name of the machine. Unless you are running
+a really small site, you probably want to change that into <b><a
+href="#mydomain"> $mydomain</a>,</b> which defaults to the parent
+domain of the machine name.
+
+<p>
+
+<dl>
+
+<dt> Examples:
+
+<p>
+
+<dd> <b>myorigin = $myhostname</b> (default)
+
+<dd> <b>myorigin = $mydomain</b> (probably desirable)
+
+</dl>
+
+<a name="mydestination"> <h2> What domains to receive mail for
+</h2> </a>
+
+The <b>mydestination</b> 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.
+
+<p>
+
+You can specify zero or more domain names, <i>/file/name</i> patterns
+and/or <i>type:name</i> lookup tables, separated by whitespace
+and/or commas. A <i>/file/name</i> is replaced by its contents;
+<i>type:name</i> requests that a table lookup is done, typically
+from a <a href="rewrite.html#virtual">virtual</a> database.
+
+<p>
+
+If your machine is a mail server for its entire domain, you must
+list <b>$mydomain</b> as well.
+
+<p>
+
+<dl> Examples:
+
+<p>
+
+<dl>
+
+<dt> Default setting:
+
+<dd> <b>mydestination = $myhostname localhost.$mydomain</b>
+
+<p>
+
+<dt> Domain-wide mail server:
+
+<dd> <b>mydestination = $myhostname localhost.$mydomain $mydomain
+</b>
+
+<p>
+
+<dt> Host with multiple DNS A records:
+
+<dd> <b>mydestination = $myhostname localhost.$mydomain www.$mydomain
+ftp.$mydomain</b>
+
+</dl>
+
+<p>
+
+Caution: in order to avoid mail delivery loops, you must list all
+hostnames of the machine, including $myhostname, and localhost.$mydomain.
+
+</dl>
+
+<a name="notify"> <h2> What trouble to report to the postmaster
+</h2> </a>
+
+You should set up a <b>postmaster</b> <a
+href="rewrite.html#aliases">alias</a> that points to a human person.
+This alias is required to exist, so that people can report mail
+delivery problems.
+
+<p>
+
+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:
+
+<p>
+
+<dl>
+
+<dt> Example:
+
+<dd> <b>notify_classes = resource, software</b>
+
+<p>
+
+<dt>The meaning of the classes is as follows:
+
+<p>
+
+<dl>
+
+<dt> <b>bounce</b> <dd> Inform the postmaster of undeliverable
+mail. For privacy reasons, the postmaster receives the message
+headers only.
+
+<p>
+
+<dt> <b>policy</b> <dd> Inform the postmaster of client requests
+that were rejected because of (UCE) policy restrictions. The
+postmaster receives a transcript of the entire SMTP session.
+
+<p>
+
+<dt> <b>protocol</b> <dd> 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.
+
+<p>
+
+<dt> <b>resource</b> <dd> Inform the postmaster of mail not delivered
+due to resource problems (for example, queue file write errors).
+
+<p>
+
+<dt> <b>software</b> <dd> Inform the postmaster of mail not delivered
+due to software problems.
+
+</dl>
+
+</dl>
+
+<a name="myhostname"> <h2> My own hostname </h2> </a>
+
+The <b>myhostname</b> parameter describes the fully-qualified domain
+name of the machine running the Postfix system. <b> $myhostname</b>
+appears as the default value in many other Postfix configuration
+parameters.
+
+<p>
+
+By default, <b>myhostname</b> 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.
+
+<dl>
+
+<dt> Examples:
+
+<p>
+
+<dd> <b>myhostname = host.local.domain</b> (local hostname is not
+FQDN)
+
+<dd> <b>myhostname = host.virtual.domain</b> (virtual interface)
+
+<dd> <b>myhostname = virtual.domain</b> (virtual interface)
+
+</dl>
+
+<a name="mydomain"> <h2> My own domain name </h2> </a>
+
+The <b>mydomain</b> parameter specifies the parent domain of
+<b>$myhostname.</b> By default it is derived from <b> $myhostname</b>
+by stripping off the first part (unless the result would be a
+top-level domain).
+
+<dl>
+
+<dt> Examples:
+
+<p>
+
+<dd> <b>mydomain = local.domain</b>
+
+<dd> <b>mydomain = virtual.domain</b> (virtual interface)
+
+</dl>
+
+<a name="mynetworks"> <h2> My own networks </h2> </a>
+
+The <b>mynetworks</b> parameter lists all networks that this machine
+is attached to. This information can be used by the <a href="uce.html">
+anti-UCE</a> features to distinguish between local systems and
+strangers.
+
+<p>
+
+By default, <b>mynetworks</b> 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: <b>168.100.0.0/16 127.0.0.0/8. </b> However,
+network <b>168.100</b> is owned by my ISP. Of course I do not want
+to consider all their customer systems as local, so I use instead:
+
+<dl>
+
+<dd> <b>mynetworks = 168.100.189.0/28, 127.0.0.0/8</b>
+
+</dl>
+
+<a name="inet_interfaces"> <h2> My own network addresses </h2> </a>
+
+The <b>inet_interfaces</b> parameter specifies all network interface
+addresses that the Postfix system should listen on; mail addressed
+to <i>user</i>@[<i>network address</i>] will be delivered locally,
+as if it is addressed to a domain listed in <b> $mydestination.
+</b>
+
+<p>
+
+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.
+
+<dl>
+
+<dt> Examples:
+
+<p>
+
+<dl>
+
+<dt> Default:
+
+<dd> <b>inet_interfaces = all</b>
+
+<p>
+
+<dt> Host running virtual mailers:
+
+<dd> <b>inet_interfaces = virtual.host.name</b> (virtual domain)
+
+<dd> <b>inet_interfaces = $myhostname localhost.$mydomain</b>
+(non-virtual mailer)
+
+</dl>
+
+</dl>
+
+<hr>
+
+<a href="config.html">Up one level</a> | Basic Configuration | <a
+href="uce.html">UCE Controls</a> | <a href="rate.html"> Rate
+Controls</a> | <a href="resource.html"> Resource Controls</a> | <a
+href="rewrite.html"> Address Manipulation </a>
+
+</body>
+
+</html>
--- /dev/null
+#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
--- /dev/null
+<html>
+
+<head>
+
+<title>Postfix - the Big Picture</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix
+- the Big Picture</h1>
+
+<hr>
+
+<img src="big-picture.gif" width="810" height="269">
+
+<p>
+
+The figure shows the main Postfix system components, and the main
+information flows between them. Postfix system components are
+introduced in the <a href="anatomy.html">Postfix anatomy</a>
+documentation.
+
+<p>
+
+<ul>
+
+<li>Yellow ellipsoids are mail programs.
+
+<li>Yellow boxes are mail queues or files.
+
+<li>Blue boxes are lookup tables.
+
+<li>Programs in the large box run under control by the Postfix
+resident <a href="master.8.html">master</a> daemon.
+
+<li>Data in the large box is property of the Postfix mail system.
+
+</ul>
+
+In order to keep the big picture readable the following elements were omitted:
+
+<p>
+
+<ul>
+
+<li>The Postfix <a href="commands.html">command-line utilities</a>.
+
+<li>The Postfix resident <a href="master.8.html">master</a> daemon.
+
+<li>The DNS lookups by the SMTP <a href="smtpd.8.html">server</a>
+and <a href="smtp.8.html">client</a> daemons
+
+<li>The <a href="bounce.8.html">bounce or defer</a> daemon and the
+flow of bounced mail.
+
+<li>The address rewriting and resolving requests by the SMTP server
+and by the local delivery agent.
+
+<li>The flow of mail forwarded by the local delivery agent.
+
+<li>The flow of <a href="basic.html#notify">postmaster notices</a>
+for protocol errors, policy violations, etc.
+
+<li>Triggers to alert the <a href="pickup.8.html">pickup</a> daemon
+and <a href="qmgr.8.html">queue manager</a> that new mail has arrived
+in the <b>maildrop</b> and <B>incoming</b> queues, respectively.
+
+</ul>
+
+<hr>
+
+</body>
+
+</html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+BOUNCE(8) BOUNCE(8)
+
+
+<b>NAME</b>
+ bounce - Postfix message bounce or defer daemon
+
+<b>SYNOPSIS</b>
+ <b>bounce</b> [generic Postfix daemon options]
+
+<b>DESCRIPTION</b>
+ The <b>bounce</b> 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 <b>master.cf</b> file (either <b>bounce</b> or <b>defer</b>). This program
+ expects to be run from the <a href="master.8.html"><b>master</b>(8)</a> process manager.
+
+ The <b>bounce</b> daemon processes two types of service requests:
+
+ <b>o</b> Append a recipient status record to a per-message
+ log file.
+
+ <b>o</b> 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.
+
+<b>STANDARDS</b>
+ RFC 822 (ARPA Internet Text Messages)
+
+<b>DIAGNOSTICS</b>
+ Problems and transactions are logged to <b>syslogd</b>(8).
+
+<b>BUGS</b>
+ The log files use an ad-hoc, unstructured format. This
+ will have to change in order to easily support standard
+ delivery status notifications.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
+ command after a configuration change.
+
+ <b>bounce</b><i>_</i><b>size</b><i>_</i><b>limit</b>
+ Limit the amount of original message context that
+ is sent in a non-delivery notification.
+
+
+
+
+ 1
+
+
+
+
+
+BOUNCE(8) BOUNCE(8)
+
+
+ <b>mail</b><i>_</i><b>name</b>
+ Use this mail system name in the introductory text
+ at the start of a bounce message.
+
+ <b>notify</b><i>_</i><b>classes</b>
+ Notify the postmaster of bounced mail when this
+ parameter includes the <b>bounce</b> class. For privacy
+ reasons, the message body is not included.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="master.8.html">master(8)</a> process manager
+ <a href="qmgr.8.html">qmgr(8)</a> queue manager
+ syslogd(8) system logging
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+CANONICAL(5) CANONICAL(5)
+
+
+<b>NAME</b>
+ canonical - format of Postfix canonical table
+
+<b>SYNOPSIS</b>
+ <b>postmap</b> <b>/etc/postfix/canonical</b>
+
+<b>DESCRIPTION</b>
+ The optional <b>canonical</b> file specifies an address mapping
+ for local and non-local addresses. The mapping is used by
+ the <a href="cleanup.8.html"><b>cleanup</b>(8)</a> daemon. The address mapping is recursive.
+
+ The file serves as input to the <a href="postmap.1.html"><b>postmap</b>(1)</a> command. The
+ result, an indexed file in <b>dbm</b> or <b>db</b> format, is used for
+ fast searching by the mail system. After an update it may
+ take a minute or so before the change becomes visible.
+ Issue a <b>postfix</b> <b>reload</b> command to eliminate the delay.
+
+ The <b>canonical</b> 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 <b>S3</b>, if you like.
+
+ Typically, one would use the <b>canonical</b> table to replace
+ login names by <i>Firstname.Lastname</i>, or to clean up
+ addresses produced by legacy mail systems.
+
+ The <b>canonical</b> mapping is not to be confused with <i>virtual</i>
+ <i>domain</i> support. Use the <a href="virtual.5.html"><b>virtual</b>(5)</a> map for that purpose.
+
+ The <b>canonical</b> mapping is not to be confused with local
+ aliasing. Use the <a href="aliases.5.html"><b>aliases</b>(5)</a> map for that purpose.
+
+ The format of the <b>canonical</b> 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 `#'.
+
+ <i>user</i>@<i>domain</i> <i>address</i>
+ <i>user</i>@<i>domain</i> is replaced by <i>address</i>. 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 <i>Firstname.Lastname</i> style addresses, but see
+ below for a simpler solution.
+
+ <i>user</i> <i>address</i>
+ <i>user</i>@<i>site</i> is replaced by <i>address</i> when <i>site</i> is equal
+ to $<b>myorigin</b>, when <i>site</i> is listed in $<b>mydestina-</b>
+ <b>tion</b>, or when it is listed in $<b>inet</b><i>_</i><b>interfaces</b>.
+
+
+
+
+ 1
+
+
+
+
+
+CANONICAL(5) CANONICAL(5)
+
+
+ This form is useful for replacing login names by
+ <i>Firstname.Lastname</i>.
+
+ @<i>domain</i> <i>address</i>
+ Every address in <i>domain</i> is replaced by <i>address</i>.
+ This form has the lowest precedence.
+
+ In all the above forms, when <i>address</i> has the form @<i>other-</i>
+ <i>domain</i>, the result is the same user in <i>otherdomain</i>.
+
+<b>ADDRESS</b> <b>EXTENSION</b>
+ When table lookup fails, and the address localpart con-
+ tains the optional recipient delimiter (e.g.,
+ <i>user+foo</i>@<i>domain</i>), the search is repeated for the unex-
+ tended address (e.g. <i>user</i>@<i>domain</i>), and the unmatched
+ extension is propagated to the result of table lookup. The
+ matching order is: <i>user+foo</i>@<i>domain</i>, <i>user</i>@<i>domain</i>, <i>user+foo</i>,
+ <i>user</i>, and @<i>domain</i>.
+
+<b>BUGS</b>
+ The table format does not understand quoting conventions.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this topic. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
+ command after a configuration change.
+
+ <b>canonical</b><i>_</i><b>maps</b>
+ List of canonical mapping tables.
+
+ <b>recipient</b><i>_</i><b>canonical</b><i>_</i><b>maps</b>
+ Address mapping lookup table for envelope and
+ header recipient addresses.
+
+ <b>sender</b><i>_</i><b>canonical</b><i>_</i><b>maps</b>
+ Address mapping lookup table for envelope and
+ header sender addresses.
+
+ Other parameters of interest:
+
+ <b>inet</b><i>_</i><b>interfaces</b>
+ The network interface addresses that this system
+ receives mail on.
+
+ <b>masquerade</b><i>_</i><b>domains</b>
+ List of domains that hide their subdomain struc-
+ ture.
+
+ <b>masquerade</b><i>_</i><b>exceptions</b>
+ List of user names that are not subject to address
+ masquerading.
+
+
+
+
+
+ 2
+
+
+
+
+
+CANONICAL(5) CANONICAL(5)
+
+
+ <b>mydestination</b>
+ List of domains that this mail system considers
+ local.
+
+ <b>myorigin</b>
+ The domain that is appended to locally-posted mail.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="cleanup.8.html">cleanup(8)</a> canonicalize and enqueue mail
+ <a href="postmap.1.html">postmap(1)</a> create mapping table
+ <a href="virtual.5.html">virtual(5)</a> virtual domain mapping
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+CLEANUP(8) CLEANUP(8)
+
+
+<b>NAME</b>
+ cleanup - canonicalize and enqueue Postfix message
+
+<b>SYNOPSIS</b>
+ <b>cleanup</b> [generic Postfix daemon options]
+
+<b>DESCRIPTION</b>
+ The <b>cleanup</b> daemon processes inbound mail, inserts it into
+ the <b>incoming</b> mail queue, and informs the queue manager of
+ its arrival.
+
+ The <b>cleanup</b> daemon always performs the following transfor-
+ mations:
+
+ <b>o</b> Insert missing message headers: (<b>Resent-</b>) <b>From:</b>,
+ <b>Message-Id:</b>, and <b>Date:</b>.
+
+ <b>o</b> Extract envelope recipient addresses from (<b>Resent-</b>)
+ <b>To:</b>, <b>Cc:</b> and <b>Bcc:</b> message headers when no recipi-
+ ents are specified in the message envelope.
+
+ <b>o</b> Transform envelope and header addresses to the
+ standard <i>user@fully-qualified-domain</i> form that is
+ expected by other Postfix programs. This task is
+ delegated to the <a href="trivial-rewrite.8.html"><b>trivial-rewrite</b>(8)</a> daemon.
+
+ <b>o</b> Eliminate duplicate envelope recipient addresses.
+
+ The following address transformations are optional:
+
+ <b>o</b> Optionally, rewrite all envelope and header
+ addresses according to the mappings specified in
+ the <a href="canonical.5.html"><b>canonical</b>(5)</a> lookup tables.
+
+ <b>o</b> Optionally, masquerade envelope sender addresses
+ and message header addresses (i.e. strip host or
+ domain information below all domains listed in the
+ <b>masquerade</b><i>_</i><b>domains</b> parameter, except for user names
+ listed in <b>masquerade</b><i>_</i><b>exceptions</b>). Address mas-
+ querading does not affect envelope recipients.
+
+ <b>o</b> Optionally, expand envelope recipients according to
+ information found in the <a href="virtual.5.html"><b>virtual</b>(5)</a> lookup tables.
+
+ The <b>cleanup</b> 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 <b>cleanup</b> daemon to bounce the
+ message back to the sender in case of trouble.
+
+<b>STANDARDS</b>
+ RFC 822 (ARPA Internet Text Messages)
+
+
+
+
+ 1
+
+
+
+
+
+CLEANUP(8) CLEANUP(8)
+
+
+<b>DIAGNOSTICS</b>
+ Problems and transactions are logged to <b>syslogd</b>(8).
+
+<b>BUGS</b>
+ Table-driven rewriting rules make it hard to express <b>if</b>
+ <b>then</b> <b>else</b> and other logical relationships.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
+ command after a configuration change.
+
+<b>Miscellaneous</b>
+ <b>hopcount</b><i>_</i><b>limit</b>
+ Limit the number of <b>Received:</b> message headers.
+
+<b>Address</b> <b>transformations</b>
+ <b>empty</b><i>_</i><b>address</b><i>_</i><b>recipient</b>
+ The destination for undeliverable mail from <>.
+ This substitution is done before all other address
+ rewriting.
+
+ <b>canonical</b><i>_</i><b>maps</b>
+ Address mapping lookup table for sender and recipi-
+ ent addresses in envelopes and headers.
+
+ <b>recipient</b><i>_</i><b>canonical</b><i>_</i><b>maps</b>
+ Address mapping lookup table for envelope and
+ header recipient addresses.
+
+ <b>sender</b><i>_</i><b>canonical</b><i>_</i><b>maps</b>
+ Address mapping lookup table for envelope and
+ header sender addresses.
+
+ <b>masquerade</b><i>_</i><b>domains</b>
+ List of domains that hide their subdomain struc-
+ ture.
+
+ <b>masquerade</b><i>_</i><b>exceptions</b>
+ List of user names that are not subject to address
+ masquerading.
+
+ <b>virtual</b><i>_</i><b>maps</b>
+ Address mapping lookup table for envelope recipient
+ addresses.
+
+<b>Resource</b> <b>controls</b>
+ <b>duplicate</b><i>_</i><b>filter</b><i>_</i><b>limit</b>
+ Limit the number of envelope recipients that are
+ remembered.
+
+ <b>header</b><i>_</i><b>size</b><i>_</i><b>limit</b>
+ Limit the amount of memory in bytes used to process
+
+
+
+ 2
+
+
+
+
+
+CLEANUP(8) CLEANUP(8)
+
+
+ a message header.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="canonical.5.html">canonical(5)</a> canonical address lookup table format
+ <a href="qmgr.8.html">qmgr(8)</a> queue manager daemon
+ syslogd(8) system logging
+ <a href="trivial-rewrite.8.html">trivial-rewrite(8)</a> address rewriting
+ <a href="virtual.5.html">virtual(5)</a> virtual address lookup table format
+
+<b>FILES</b>
+ /etc/postfix/canonical*, canonical mapping table
+ /etc/postfix/virtual*, virtual mapping table
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+
+
+</pre> </body> </html>
--- /dev/null
+<html>
+
+<head>
+
+<title>Postfix Anatomy - Command-line Utilities</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix
+Anatomy - Command-line Utilities</h1>
+
+<hr>
+
+<a href="anatomy.html">Up one level</a> | <a
+href="receiving.html">Receiving Mail</a> | <a
+href="delivering.html">Delivering Mail</a> | <a
+href="backstage.html">Behind the Scenes</a> | Command-line Utilities
+
+<p>
+
+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 <a href="sendmail.1.html">sendmail</a>, <a
+href="sendmail.1.html">mailq</a>, and <a
+href="sendmail.1.html">newaliases</a> commands that were already
+introduced, the Postfix system comes with it own collection of
+utilities. For consistency, these are all named post<i>something</i>.
+
+<ul>
+
+<li>The <a href="postfix.1.html">postfix</a> 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.
+
+<p>
+
+<li>The <a href="postalias.1.html">postalias</a> command maintains
+Postfix <a href="aliases.5.html">alias</a> databases. This is the
+program behind the <a href="newaliases.1.html">newaliases</a>
+command.
+
+<p>
+
+<li>The <a href="postcat.1.html">postcat</a> 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.
+
+<p>
+
+<li>The <a href="postconf.1.html">postconf</a> command displays
+Postfix <b>main.cf</b> 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
+<b>main.cf</b> file.
+
+<p>
+
+<li>The <a href="postdrop.1.html">postdrop</a> command is the mail
+posting agent that is run by the <a href="sendmail.1.html">sendmail</a>
+command on systems that have no world-writable <b>maildrop</b> queue
+directory.
+
+<p>
+
+<li>The <a href="postkick.1.html">postkick</a> command makes some
+internal communication channels available for use in, for example,
+shell scripts.
+
+<p>
+
+<li>The <a href="postlock.1.html">postlock</a> command provides
+Postfix-compatible mailbox locking for use in, for example, shell
+scripts.
+
+<p>
+
+<li>The <a href="postlog.1.html">postlog</a> command provides
+Postfix-compatible logging for shell scripts.
+
+<p>
+
+<li>The <a href="postmap.1.html">postmap</a> command maintains
+Postfix lookup tables such as <a href="canonical.5.html">canonical</a>,
+<a href="virtual.5.html">virtual</a> and others. It is a cousin of
+the UNIX <b>makemap</b> command.
+
+</ul>
+
+<hr>
+
+<a href="anatomy.html">Up one level</a> | <a
+href="receiving.html">Receiving Mail</a> | <a
+href="delivering.html">Delivering Mail</a> | <a
+href="backstage.html">Behind the Scenes</a> | Command-line Utilities
+
+</body>
+
+</html>
--- /dev/null
+<html>
+
+<head>
+
+<title> Postfix Mail System Configuration </title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix Mail System Configuration </h1>
+
+<hr>
+
+<a href="index.html">Up one level</a> | <a href="overview.html">Postfix
+Overview</a> | <a href="anatomy.html">Postfix Anatomy</a> | Postfix
+Configuration | <a href="faq.html">Postfix FAQ</a>
+
+<p>
+
+<h2> Table of contents </h2>
+
+<ul>
+
+<li> <a href="basic.html">Basic configuration</a>. 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.
+
+<p>
+
+<li> <a href="uce.html">UCE controls</a>. A description of the
+Postfix mechanisms to reject unwanted mail, a.k.a. UCE, and to
+restrict unauthorized mail relaying.
+
+<p>
+
+<li> <a href="rate.html">Rate controls</a>. How to achieve
+performance without hosing your system or your neighbor's systems,
+and how to deal with broken or malicious client programs.
+
+<p>
+
+<li>
+
+<a href="resource.html">Resource controls</a>. How to set Postfix
+memory and file system resource budgets, and how to deal with
+temporary problems.
+
+<p>
+
+<li> <a href="rewrite.html">Address manipulation</a>. 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.
+
+</ul>
+
+<hr>
+
+<a href="index.html">Up one level</a> | <a href="overview.html">Postfix
+Overview</a> | <a href="anatomy.html">Postfix Anatomy</a> | Postfix
+Configuration | <a href="faq.html">Postfix FAQ</a>
+
+</body>
+
+</html>
--- /dev/null
+bounce.8.html
\ No newline at end of file
--- /dev/null
+<html>
+
+<head>
+
+<title>Postfix Anatomy - Delivering Mail</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix
+Anatomy - Delivering Mail</h1>
+
+<hr>
+
+<a href="anatomy.html">Up one level</a> | <a
+href="receiving.html">Receiving Mail</a> | Delivering Mail | <a
+href="backstage.html">Behind the Scenes</a> | <a
+href="commands.html">Command-line Utilities</a>
+
+<p>
+
+Once a message has reached the <b>incoming</b> 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.
+
+<p>
+
+<center>
+
+<img src="outbound.gif" width="424" height="269">
+
+</center>
+
+<p>
+
+<ul>
+
+<li>The <a href="qmgr.8.html">queue manager</a> is the heart of
+the Postfix mail system. It contacts the <a href="local.8.html">local</a>,
+<a href="smtp.8.html">smtp</a>, or <a href="pipe.8.html">pipe</a>
+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.
+
+<p>
+
+The queue manager maintains a separate <b>deferred</b> queue for
+mail that cannot be delivered, so that a large mail backlog will
+not slow down normal queue accesses.
+
+<p>
+
+The queue manager maintains a small <b>active</b> queue with just
+the few messages that it has opened for delivery. The <b>active</b>
+queue acts as a limited window on the potentially much larger
+<b>incoming</b> or <b>deferred</b> queues. The small <b>active</b>
+queue prevents the queue manager from running out of memory under
+heavy load.
+
+<p>
+
+Optionally, the queue manager bounces mail for recipients that are
+listed in the <a href="rewrite.html#relocated">relocated</a> table.
+This table contains contact information for users or even entire
+domains that no longer exist.
+
+<p>
+
+<li>On request by the queue manager, the <a
+href="trivial-rewrite.8.html">trivial-rewrite</a> daemon resolves
+destinations. By default, it only distinguishes between <i>local</i>
+and <i>remote</i> destinations. Additional routing information can
+be specified with the optional <a
+href="rewrite.html#transport">transport</a> table.
+
+<p>
+
+<li>On request by the queue manager, the <a href="bounce.8.html">bounce
+or defer</a> 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.
+
+<p>
+
+<li>The <a href="local.8.html">local</a> delivery agent understands
+UNIX-style mailboxes, <b>sendmail</b>-style system-wide <a
+href="aliases.5.html">alias</a> databases, and <b>sendmail</b>-style
+per-user <a href="aliases.5.html">.forward</a> files. Multiple
+local delivery agents can be run in parallel, but parallel delivery
+to the same user is usually limited.
+
+<p>
+
+Together with the <a href="sendmail.1.html">sendmail</a> mail
+posting agent, the <a href="local.8.html">local</a> delivery agent
+implements the familiar Sendmail user interface.
+
+<p>
+
+The <a href="local.8.html">local</a> 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 <a href="faq.html#procmail">procmail</a> program.
+
+<p>
+
+<li>The <a href="smtp.8.html">SMTP client</a> 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.
+
+<p>
+
+<li>The <a href="pipe.8.html">pipe mailer</a> is the outbound
+interface to other mail transports (the <a
+href="sendmail.1.html">sendmail</a> program is the inbound interface).
+The Postfix mail system comes with <a href="faq.html">examples</a>
+for delivery via the <b>UUCP</b> protocol. At the time of writing,
+this venerable protocol is still widely used. By default, Postfix
+understands <a href="rewrite.html#standard">bang path</a> style
+addresses.
+
+</ul>
+
+<hr>
+
+<a href="anatomy.html">Up one level</a> | <a
+href="receiving.html">Receiving Mail</a> | Delivering Mail | <a
+href="backstage.html">Behind the Scenes</a> | <a
+href="commands.html">Command-line Utilities</a>
+
+</body>
+
+</html>
--- /dev/null
+<html>
+
+<!--Warning, preformatted content! -->
+
+<head>
+
+<title>Postfix Frequently Asked Questions</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix Frequently Asked Questions</h1>
+
+<hr>
+
+<a href="index.html">Up one level</a> | <a href="overview.html">Postfix
+Overview</a> | <a href="anatomy.html">Postfix Anatomy</a> | <a
+href="config.html">Postfix Configuration</a> | Postfix FAQ
+
+<h2>Table of contents</h2>
+
+<ul>
+
+<li><a href="#intranet">Running Postfix inside an intranet</a>
+
+<li><a href="#firewall">Running Postfix on a firewall</a>
+
+<li><a href="#maildir">Support for maildir-style mailboxes</a>
+
+<li><a href="#procmail">Using Procmail for local delivery</a>
+
+<li><a href="#verbose">Postfix breaks "sendmail -v"</a>
+
+<li><a href="#delivered">Getting rid of Delivered-To:</a>
+
+<li><a href="#approve">Postfix breaks the majordomo "approve" command</a>
+
+<li><a href="#uucp">Setting up an Internet to UUCP gateway</a>
+
+<li><a href="#uucp-only">Using UUCP as the default transport</a>
+
+<li><a href="#db">Using DB libraries on Solaris etc.</a>
+
+</ul>
+
+<hr>
+
+<a name="intranet"> <h2>Running Postfix inside an intranet</h2> </a>
+
+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.
+
+<p>
+
+<ul>
+
+<li>Edit the <b>main.cf</b> file and specify:
+
+<p>
+
+<dl>
+
+<dd><b>relayhost = $mydomain</b>
+
+</dl>
+
+<p>
+
+This assumes that your organization has set up multiple internal
+MX hosts for the local domain.
+
+<p>
+
+If your intranet does not use MX records internally, you have to
+specify the gateway host itself:
+
+<p>
+
+<dl>
+
+<dd><b>relayhost = </b><i>gateway.my.domain</i>
+
+</dl>
+
+<p>
+
+<li>If you want to deliver internal mail directly without going through
+the intranet mail gateway, you have to specify a routing entry in the
+<a href="transport.5.html">transport</a> table, and you have to
+enable <a href="transport.5.html">transport</a> table lookups.
+
+<p>
+
+<dl>
+
+<dl>
+
+<dt>main.cf:
+
+<dd><b>transport_maps = hash:/etc/postfix/transport</b>
+
+<p>
+
+<dt>/etc/postfix/transport:
+
+<dd><i>my.domain</i> <b>smtp</b>:
+
+</dl>
+
+</dl>
+
+<p>
+
+Specify <b>dbm:/etc/postfix/transport</b> if your system
+uses <b>dbm</b> files instead of <b>db</b>.
+
+<p>
+
+<li>Execute the command <b>postfix reload</b> to make the
+changes effective.
+
+</ul>
+
+<hr>
+
+<a name="firewall"><h2>Running Postfix on a firewall</h2> </a>
+
+Note: this section depends on accidental properties of the
+implementation so this information is subject to change.
+
+<p>
+
+How to set up Postfix on the firewall machine so that it relays
+mail for <i>my.domain</i> to a gateway machine on the inside, and
+so that it refuses mail for <i>*.my.domain</i>? The problem is that
+the standard <a href="uce.html#relay_domains">relay_domains</a>
+mail relaying restriction allows mail to <i>*.my.domain</i> when
+you specify <i>my.domain</i>.
+
+<p>
+
+<ul>
+
+<li>Specify a null <a href="uce.html#relay_domains">relay_domains</a>
+parameter plus a <a href="virtual.5.html">virtual</a> table to
+route mail for <i>my.domain</i> to the inside machine:
+
+<p>
+
+<dl>
+
+<dt>/etc/postfix/main.cf:
+
+<dd><b>mydestination = $myhostname</b>, <i>my.domain</i>,
+<b>localhost</b>.<i>my.domain</i>
+
+<dd><b>relay_domains = </b>
+
+<dd><b>virtual_maps = hash:/etc/postfix/virtual</b>
+
+<p>
+
+<dt>/etc/postfix/virtual:
+
+<dd><i>@my.domain</i> <i>@inside-gateway.my.domain</i>
+
+</dl>
+
+<p>
+
+Specify <b>dbm:/etc/postfix/virtual</b> if your system uses <b>dbm</b>
+files instead of <b>db</b>.
+
+<p>
+
+<li>Execute the command <b>postfix reload</b> after a
+configuration change.
+
+</ul>
+
+<p>
+
+Unfortunately, the solution cannot use the transport table, because
+that table is ignored for destinations that match <b>$mydestination</b>.
+That's an implementation error, and it will be removed.
+
+
+<hr>
+
+<a name="maildir"><h2>Support for maildir-style mailboxes</h2> </a>
+
+<b>Maildir</b> is a specific one-file-per-message organization that
+was introduced with the <b>qmail</b> system by Daniel Bernstein.
+
+<p>
+
+Postfix supports the <b>maildir</b> mailbox format. Edit <b>main.cf</b>
+and specify a line with: <b>home_mailbox = maildir</b>.
+
+<hr>
+
+<a name="procmail"><h2>Using Procmail for local delivery</h2> </a>
+
+<ul>
+
+<li>Edit <b>/etc/postfix/main.cf</b>, and specify <b>procmail</b> as the
+command for mailbox delivery:
+
+<p>
+
+<dl>
+
+<dd><b>mailbox_command = </b><i>/path/to/procmail</i>
+
+</dl>
+
+<p>
+
+Do not use any shell meta characters or built-ins
+such as <b>IFS</b> or <b>&&</b>, because they force Postfix
+to run an expensive shell process.
+
+<p>
+
+<li>Execute the command <b>postfix reload</b> to make the changes
+effective.
+
+</ul>
+
+<hr>
+
+<a name="verbose"><h2>Postfix breaks "sendmail -v"</h2> </a>
+
+Some people will complain that <b>sendmail -v</b> no longer shows
+the actual mail delivery.
+
+<p>
+
+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.
+
+<p>
+
+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.
+
+<hr>
+
+<a name="delivered"><h2>Getting rid of Delivered-To:</h2> </a>
+
+Some people will complain about the ugly <b>Delivered-To:</b>
+message header that Postfix prepends to their mail.
+
+<p>
+
+With the Postfix architecture, <b>Delivered-To:</b> 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 <b>~/.mailrc</b>
+and <b>/usr/lib/Mail.rc</b>).
+
+<p>
+
+With mailing lists, <b>Delivered-To:</b> 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 <b>FEATURE
+CONTROL</b> section in the documentation of the <a
+href="local.8.html">local</a> delivery agent.
+
+<p>
+
+See also the FAQ item for problems with the <b>majordomo</b> <a
+href="#approve">approve</a> command.
+
+<hr>
+
+<a name="approve"><h2>Postfix breaks the majordomo "approve"
+command</h2> </a>
+
+The Postfix local delivery agent prepends a <b>Delivered-To:</b>
+message header to prevent mail forwarding loops. With <b>majordomo</b>
+mailing lists, <b>Delivered-To:</b> 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.
+
+<p>
+
+Currently, the workaround is to edit the <b>approve</b> script to
+strip any header lines that match:
+
+<p>
+
+<dl>
+
+<dd><b>/delivered-to/i</b>
+
+</dl>
+
+<p>
+
+Yes, this assumes that the moderator knows what she is doing.
+
+<hr>
+
+<a name="uucp"><h2>Setting up an Internet to UUCP gateway</h2> </a>
+
+Here is how to set up a machine that sends <i>some</i> but not all
+mail via UUCP. See the <a href="#uucp-only">UUCP-only</a> FAQ entry
+for setting a UUCP-only host.
+
+<p>
+
+<ul>
+
+<li>Make an entry in <b>/etc/postfix/transport</b>:
+
+<p>
+
+<dl>
+
+<dd><i>some.domain</i> <b>uucp:</b><i>uucp-host</i>
+
+</dl>
+
+<p>
+
+This causes all mail for the <i>some.domain</i> (and subdomains
+thereof) to be sent via UUCP to the host <i>uucp-host</i>.
+
+<p>
+
+<li>Execute the command <b>postmap /etc/postfix/transport</b> whenever
+you change the <b>transport</b> file.
+
+<p>
+
+<li>You need an entry in <b>/etc/postfix/master.cf</b>:
+
+<pre>
+ uucp unix - n n - - pipe
+ flags=F user=uucp argv=uux -n -z -a$sender - $nexthop!rmail ($recipient)
+</pre>
+
+<p>
+
+This runs the <b>uux</b> command, and substitutes the next-hop
+hostname (<i>uucp-host</i>) and the recipients before executing
+the command. The <b>uux</b> command is executed without assistance
+from the shell, so there are no problems with shell meta characters.
+
+<p>
+
+<li>Edit <b>/etc/postfix/main.cf</b> and enable <b>transport</b>
+table lookups:
+
+<p>
+
+<dl>
+
+<dd><b>transport_maps = hash:/etc/postfix/transport</b>
+
+</dl>
+
+<p>
+
+Specify <B>dbm</b> instead of <b>hash</b> if your system has no
+<b>db</b> support.
+
+<p>
+
+<li>Edit <b>/etc/postfix/main.cf</b> and add <i>some.domain</i> to
+the list of domains that your site is willing to relay mail for.
+See the <a href="uce.html#relay_domains">relay_domains</a>
+configuration parameter.
+
+<p>
+
+<li>Execute the command <b>postfix reload</b> to make the
+changes effective.
+
+</ul>
+
+<hr>
+
+<a name="uucp-only"><h2>Using UUCP as the default transport</h2> </a>
+
+Here is how to relay all your mail over a UUCP link. See the <a
+href="#uucp">Internet to UUCP</a> FAQ entry for setting up a machine
+that gateways between UUCP and SMTP.
+
+<p>
+
+<ul>
+
+<li>There is no need for a <b>transport</b> table.
+
+<p>
+
+<li>In <b>/etc/postfix/main.cf</b>, specify the name of your
+UUCP gateway host, and specify that all mail must be sent
+via the <b>uucp</b> message transport:
+
+<p>
+
+<dl>
+
+<dd><b>relayhost = </b><i>uucp-gateway</i>
+
+<dd><b>default_transport = uucp</b>
+
+</dl>
+
+<p>
+
+<li>You need an entry in <b>/etc/postfix/master.cf</b>:
+
+<pre>
+ uucp unix - n n - - pipe
+ flags=F user=uucp argv=uux -n -z -a$sender - $nexthop!rmail ($recipient)
+</pre>
+
+This runs the <b>uux</b> command, and substitutes the next-hop
+hostname (<i>uucp-gateway</i>, or whatever you specified) and the
+recipients before executing the command. The <b>uux</b> command
+is executed without assistance from the shell, so there are no
+problems with shell meta characters.
+
+<p>
+
+<li>Execute the command <b>postfix reload</b> to make the
+changes effective.
+
+</ul>
+
+
+<hr>
+
+<a name="db"><h2>Using DB libraries on Solaris etc.</h2> </a>
+
+The old <b>dbm</b> 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 <b>db</b> database does
+not suffer these limitations. It is standard on 4.4BSD and Linux
+systems.
+
+<p>
+
+In order to build Postfix with <b>db</b> support on UNIX systems
+that do not have <b>db</b> support out of the box, you need the
+db-1.85 release, or <a href="http://www.sleepycat.com">the current
+version</a> which has a db-1.85 compatible interface.
+
+<p>
+
+Use the following commands in the Postfix top-level directory:
+
+<dl>
+
+<dd>% <b>make tidy</b>
+
+<dd>% <b>make makefiles CCARGS="-DHAS_DB
+-I</b><i>/some/where/include</i><b>" AUXLIBS=</b><i>/some/where/libdb.a</i>
+
+<dd>% <b>make</b>
+
+</dl>
+
+<p>
+
+Of course you will have to specify the actual location of the
+include directory and of the object library.
+
+<p>
+
+One problem: older DB versions install a file <b>/usr/include/ndbm.h</b>
+that is incompatible with the one in <b>/usr/include</b>. Be sure
+to get rid of the bogus file, or the linker will fail to find
+<b>dbm_dirfno</b>.
+
+<hr>
+
+<a href="index.html">Up one level</a> | <a href="overview.html">Postfix
+Overview</a> | <a href="anatomy.html">Postfix Anatomy</a> | <a
+href="config.html">Postfix Configuration</a> | Postfix FAQ
+
+</body>
+
+</html>
--- /dev/null
+#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
--- /dev/null
+<html>
+
+<head>
+
+<title>Postfix Overview - Goals and Features</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix
+Overview - Goals and Features</h1>
+
+<hr>
+
+<a href="overview.html">Up one level</a> | <a
+href="motivation.html">Introduction</a> | Goals and features | <a
+href="architecture.html">Global architecture</a> | <a
+href="queuing.html">Queue Management</a> | <a
+href="security.html">Security</a>
+
+<h2>Primary goals</h2>
+
+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:
+
+<ul>
+
+<li>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.
+
+<p>
+
+<li>Performance. Postfix is up to three times as fast as its nearest
+competitor. A desktop PC running Postfix can receive and deliver
+a million <i>different</i> 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.
+
+<p>
+
+<li>Compatibility. Postfix is designed to be sendmail-compatible
+to make migration easy. Postfix supports <b>/var[/spool]/mail</b>,
+<b>/etc/aliases</b>, <b>NIS</b>, and <b>~/.forward</b> files.
+However, Postfix also attempts to be easy to administer, and
+therefore it does <i>not</i> use <b>sendmail.cf</b>.
+
+<p>
+
+<li>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.
+
+<p>
+
+<li>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.
+
+<p>
+
+<li>Security. Postfix uses multiple layers of defense to protect
+the local system against intruders. Almost every Postfix daemon
+can run in a <i>chroot</i> 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 <i>set-uid</i>.
+
+</ul>
+
+<h2>Other significant features of interest</h2>
+
+<ul>
+
+<li><a href="rewrite.html#transport">Multiple transports</a>. 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.
+
+<p>
+
+<li><a href="rewrite.html#virtual">Virtual domains</a>. 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.
+
+<p>
+
+<li><a href="uce.html">UCE control</a>. 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.
+
+<p>
+
+<li><a href="rewrite.html">Table lookups</a>. Postfix does not yet
+implement an address rewriting language. Instead it makes extensive
+use of table lookups. Tables can be local <b>dbm</b> or <b>db</b>
+files, or networked <b>NIS</b> or <b>NetInfo</b> maps. Adding
+support for other lookup mechanisms is relatively easy.
+
+</ul>
+
+<hr>
+
+<a href="overview.html">Up one level</a> | <a
+href="motivation.html">Introduction</a> | Goals and features | <a
+href="architecture.html">Global architecture</a> | <a
+href="queuing.html">Queue Management</a> | <a
+href="security.html">Security</a>
+
+</body>
+
+</html>
--- /dev/null
+#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
--- /dev/null
+<html>
+
+<head>
+
+<title>Postfix Mail System Documentation</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix Mail System Documentation</h1>
+
+<hr>
+
+<h2>Table of contents</h2>
+
+<ul>
+
+<li><a href="overview.html">Postfix Overview</a>. A gentle introduction
+to the Postfix mail system.
+
+<p>
+
+<li><a href="anatomy.html">Postfix Anatomy</a>. The programs that
+make up the Postfix mail system, and how mail flows through it.
+
+<p>
+
+<li><a href="config.html">Postfix Configuration</a>. How to configure
+the system, from the very basic to the very advanced.
+
+<p>
+
+<li><a href="faq.html">Postfix FAQ</a>. Look here for quick answers
+to common questions.
+
+</ul>
+
+<hr>
+
+<ul>
+
+<li><a href="http://www.porcupine.org/wietse/">About the author</a>.
+The author of TCP Wrapper, co-author of SATAN, and other software.
+
+<li>
+
+</body>
+
+</html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+LOCAL(8) LOCAL(8)
+
+
+<b>NAME</b>
+ local - Postfix local mail delivery
+
+<b>SYNOPSIS</b>
+ <b>local</b> [generic Postfix daemon options]
+
+<b>DESCRIPTION</b>
+ The <b>local</b> 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 <a href="master.8.html"><b>mas-</b>
+ <b>ter</b>(8)</a> process manager.
+
+ The <b>local</b> 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 <a href="bounce.8.html"><b>bounce</b>(8)</a> or <a href="defer.8.html"><b>defer</b>(8)</a> daemon as
+ appropriate.
+
+<b>SYSTEM-WIDE</b> <b>AND</b> <b>USER-LEVEL</b> <b>ALIASING</b>
+ The system adminstrator can set up one or more system-wide
+ <b>sendmail</b>-style alias databases. Users can have <b>sendmail</b>-
+ style ~/.<b>forward</b> files. Mail for <i>name</i> is delivered to the
+ alias <i>name</i>, to destinations in ~<i>name</i>/.<b>forward</b>, to the
+ mailbox owned by the user <i>name</i>, or it is sent back as
+ undeliverable.
+
+ An alias or ~/.<b>forward</b> file may list any combination of
+ external commands, destination file names, <b>:include:</b>
+ directives, or mail addresses. See <a href="aliases.5.html"><b>aliases</b>(5)</a> for a pre-
+ cise description. Each line in a user's .<b>forward</b> 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 ~/.<b>forward</b> file, delivery is made
+ to the user's mailbox instead. An empty ~/.<b>forward</b> 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
+ <b>:include:</b> or from ~/.<b>forward</b> files are broken up into
+ chunks of length <b>line</b><i>_</i><b>length</b><i>_</i><b>limit</b>.
+
+ While expanding aliases, ~/.<b>forward</b> files, and so on, the
+ program attempts to avoid duplicate deliveries. The <b>dupli-</b>
+ <b>cate</b><i>_</i><b>filter</b><i>_</i><b>limit</b> configuration parameter limits the num-
+ ber of remembered recipients.
+
+<b>MAIL</b> <b>FORWARDING</b>
+ 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 <b>Delivered-To:</b> header with the envelope recipient
+ address. If mail arrives for a recipient that is already
+ listed in a <b>Delivered-To:</b> header, the message is bounced.
+
+<b>MAILBOX</b> <b>DELIVERY</b>
+ The per-user mailbox is either a file in the default UNIX
+ mailbox directory (<b>/var/mail/</b><i>user</i> or <b>/var/spool/mail/</b><i>user</i>)
+ or it is a file in the user's home directory with a name
+ specified via the <b>home</b><i>_</i><b>mailbox</b> configuration parameter.
+ Mailbox delivery can be delegated to an external command
+ specified with the <b>mailbox</b><i>_</i><b>command</b> configuration parame-
+ ter.
+
+ The <b>local</b> daemon prepends a "<b>From</b> <i>sender</i> <i>time_stamp</i>" enve-
+ lope header to each message, prepends a <b>Delivered-To:</b>
+ header with the envelope recipient address, prepends a >
+ character to lines beginning with "<b>From</b> ", 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.
+
+<b>EXTERNAL</b> <b>COMMAND</b> <b>DELIVERY</b>
+ The <b>allow</b><i>_</i><b>mail</b><i>_</i><b>to</b><i>_</i><b>commands</b> configuration parameter
+ restricts delivery to external commands. The default set-
+ ting (<b>alias,</b> <b>forward</b>) forbids command destinations in
+ <b>:include:</b> files.
+
+ The command is executed directly where possible. Assis-
+ tance by the shell (<b>/bin/sh</b> 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 <b>command</b><i>_</i><b>time</b><i>_</i><b>limit</b> seconds.
+ Command exit status codes are expected to follow the con-
+ ventions defined in <<b>sysexits.h</b>>.
+
+ When mail is delivered on behalf of a user, the <b>HOME</b>, <b>LOG-</b>
+ <b>NAME</b>, and <b>SHELL</b> environment variables are set accordingly.
+ The <b>PATH</b> environment variable is always reset to a system-
+ dependent default path, and the <b>TZ</b> (time zone) environment
+ variable is always passed on without change.
+
+ The current working directory is the mail queue directory.
+
+ The <b>local</b> daemon prepends a "<b>From</b> <i>sender</i> <i>time_stamp</i>" enve-
+ lope header to each message, prepends a <b>Delivered-To:</b>
+ header with the recipient envelope address, and appends an
+ empty line.
+
+
+
+
+ 2
+
+
+
+
+
+LOCAL(8) LOCAL(8)
+
+
+<b>EXTERNAL</b> <b>FILE</b> <b>DELIVERY</b>
+ The <b>allow</b><i>_</i><b>mail</b><i>_</i><b>to</b><i>_</i><b>files</b> configuration parameter restricts
+ delivery to external files. The default setting (<b>alias,</b>
+ <b>forward</b>) forbids file destinations in <b>:include:</b> files.
+
+ The <b>local</b> daemon prepends a "<b>From</b> <i>sender</i> <i>time_stamp</i>" enve-
+ lope header to each message, prepends a <b>Delivered-To:</b>
+ header with the recipient envelope address, prepends a >
+ character to lines beginning with "<b>From</b> ", 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.
+
+<b>ADDRESS</b> <b>EXTENSION</b>
+ The optional <b>recipient</b><i>_</i><b>delimiter</b> configuration parameter
+ specifies how to separate address extensions from local
+ recipient names.
+
+ For example, with "<b>recipient</b><i>_</i><b>delimiter</b> <b>=</b> <b>+</b>", mail for
+ <i>name</i>+<i>foo</i> is delivered to the alias <i>name</i>+<i>foo</i> or to the
+ alias <i>name</i>, to the destinations listed in ~<i>name</i>/.<b>for-</b>
+ <b>ward</b>+<i>foo</i> or in ~<i>name</i>/.<b>forward</b>, to the mailbox owned by the
+ user <i>name</i>, or it is sent back as undeliverable.
+
+ In all cases the <b>local</b> daemon prepends a `<b>Delivered-To:</b>
+ <i>name</i>+<i>foo</i>' header line.
+
+<b>DELIVERY</b> <b>RIGHTS</b>
+ 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 <b>local</b> daemon uses the owner rights of the <b>:include:</b>
+ file or alias database. When those files are owned by the
+ superuser, delivery is made with the rights specified with
+ the <b>default</b><i>_</i><b>privs</b> configuration parameter.
+
+<b>STANDARDS</b>
+ RFC 822 (ARPA Internet Text Messages)
+
+<b>DIAGNOSTICS</b>
+ Problems and transactions are logged to <b>syslogd</b>(8). Cor-
+ rupted message files are marked so that the queue manager
+ can move them to the <b>corrupt</b> queue afterwards.
+
+ Depending on the setting of the <b>notify</b><i>_</i><b>classes</b> parameter,
+ the postmaster is notified of bounces and of other trou-
+ ble.
+
+<b>BUGS</b>
+ 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 ~/.<b>forward</b> files are not
+ detected early. The resulting mail forwarding loop is
+ broken by the use of the <b>Delivered-To:</b> message header.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
+ command after a configuration change.
+
+<b>Miscellaneous</b>
+ <b>alias</b><i>_</i><b>maps</b>
+ List of alias databases.
+
+ <b>home</b><i>_</i><b>mailbox</b>
+ Pathname of a mailbox relative to a user's home
+ directory. Specify <b>maildir</b> for maildir-style
+ delivery.
+
+ <b>local</b><i>_</i><b>command</b><i>_</i><b>shell</b>
+ 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.
+
+ <b>mailbox</b><i>_</i><b>command</b>
+ External command to use for mailbox delivery.
+
+ <b>recipient</b><i>_</i><b>delimiter</b>
+ Separator between username and address extension.
+
+<b>Locking</b> <b>controls</b>
+ <b>deliver</b><i>_</i><b>lock</b><i>_</i><b>attempts</b>
+ Limit the number of attempts to acquire an exclu-
+ sive lock on a mailbox or external file.
+
+ <b>deliver</b><i>_</i><b>lock</b><i>_</i><b>delay</b>
+ Time in seconds between successive attempts to
+ acquire an exclusive lock.
+
+ <b>stale</b><i>_</i><b>lock</b><i>_</i><b>time</b>
+ Limit the time after which a stale lock is removed.
+
+<b>Resource</b> <b>controls</b>
+ <b>command</b><i>_</i><b>time</b><i>_</i><b>limit</b>
+ Limit the amount of time for delivery to external
+ command.
+
+ <b>duplicate</b><i>_</i><b>filter</b><i>_</i><b>limit</b>
+ Limit the size of the duplicate filter for results
+ from alias etc. expansion.
+
+
+
+ 4
+
+
+
+
+
+LOCAL(8) LOCAL(8)
+
+
+ <b>line</b><i>_</i><b>length</b><i>_</i><b>limit</b>
+ Limit the amount of memory used for processing a
+ partial input line.
+
+ <b>local</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
+ Limit the number of parallel deliveries to the same
+ user. The default limit is taken from the
+ <b>default</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b> parameter.
+
+ <b>local</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
+ Limit the number of recipients per message deliv-
+ ery. The default limit is taken from the
+ <b>default</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b> parameter.
+
+<b>Security</b> <b>controls</b>
+ <b>allow</b><i>_</i><b>mail</b><i>_</i><b>to</b><i>_</i><b>commands</b>
+ Restrict the usage of mail delivery to external
+ command.
+
+ <b>allow</b><i>_</i><b>mail</b><i>_</i><b>to</b><i>_</i><b>files</b>
+ Restrict the usage of mail delivery to external
+ file.
+
+ <b>default</b><i>_</i><b>privs</b>
+ Default rights for delivery to external file or
+ command.
+
+<b>HISTORY</b>
+ The <b>Delivered-To:</b> header appears in the <b>qmail</b> system by
+ Daniel Bernstein.
+
+ The <i>maildir</i> structure appears in the <b>qmail</b> system by
+ Daniel Bernstein.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="aliases.5.html">aliases(5)</a> format of alias database
+ <a href="bounce.8.html">bounce(8)</a> non-delivery status reports
+ <a href="postalias.1.html">postalias(1)</a> create/update alias database
+ syslogd(8) system logging
+ <a href="qmgr.8.html">qmgr(8)</a> queue manager
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+ 5
+
+
+</pre> </body> </html>
--- /dev/null
+sendmail.1.html
\ No newline at end of file
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+MASTER(8) MASTER(8)
+
+
+<b>NAME</b>
+ master - Postfix master process
+
+<b>SYNOPSIS</b>
+ <b>master</b> [<b>-c</b> <i>config_dir</i>] [<b>-D</b>] [<b>-t</b>] [<b>-v</b>]
+
+<b>DESCRIPTION</b>
+ The <b>master</b> 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 <b>master</b> daemon is controlled by the
+ <b>master.cf</b> configuration file. The table specifies zero or
+ more servers in the <b>UNIX</b> or <b>INET</b> domain, or servers that
+ take requests from a FIFO. Precise configuration details
+ are given in the <b>master.cf</b> file, and in the manual pages
+ of the respective daemons.
+
+ Options:
+
+ <b>-c</b> <i>config_dir</i>
+ Read the <b>main.cf</b> and <b>master.cf</b> configuration files
+ in the named directory.
+
+ <b>-D</b> After initialization, run a debugger on the master
+ process. The debugging command is specified with
+ the <b>debugger</b><i>_</i><b>command</b> in the <b>main.cf</b> global configu-
+ ration file.
+
+ <b>-t</b> Test mode. Return a zero exit status when the <b>mas-</b>
+ <b>ter.pid</b> lock file does not exist or when that file
+ is not locked. This is evidence that the <b>master</b>
+ daemon is not running.
+
+ <b>-v</b> Enable verbose logging for debugging purposes. This
+ option is passed on to child processes. Multiple <b>-v</b>
+ options make the software increasingly verbose.
+
+ Signals:
+
+ <b>SIGHUP</b> Upon receipt of a <b>HUP</b> signal (e.g., after <b>postfix</b>
+ <b>reload</b>), the master process re-reads its configura-
+ tion files. If a service has been removed from the
+ <b>master.cf</b> 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.
+
+ <b>SIGTERM</b>
+ Upon receipt of a <b>TERM</b> signal (e.g., after <b>postfix</b>
+ <b>abort</b>), 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 (<b>postfix</b> <b>stop</b>) and allow
+ running processes to finish what they are doing.
+
+<b>DIAGNOSTICS</b>
+ Problems are reported to <b>syslogd</b>(8).
+
+<b>BUGS</b>
+<b>ENVIRONMENT</b>
+ <b>MAIL</b><i>_</i><b>DEBUG</b>
+ After initialization, start a debugger as specified
+ with the <b>debugger</b><i>_</i><b>command</b> configuration parameter
+ in the <b>main.cf</b> configuration file.
+
+ <b>MAIL</b><i>_</i><b>CONFIG</b>
+ Directory with configuration files.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
+ command after a configuration change.
+
+<b>Miscellaneous</b>
+ <b>mail</b><i>_</i><b>owner</b>
+ The owner of the mail queue and of most Postfix
+ processes.
+
+ <b>program</b><i>_</i><b>directory</b>
+ Directory with Postfix support programs and dae-
+ mons.
+
+ <b>queue</b><i>_</i><b>directory</b>
+ Top-level directory of the Postfix queue. This is
+ also the root directory of Postfix daemons that run
+ chrooted.
+
+<b>Resource</b> <b>controls</b>
+ <b>default</b><i>_</i><b>process</b><i>_</i><b>limit</b>
+ Default limit for the number of simultaneous child
+ processes that provide a given service.
+
+ <b>max</b><i>_</i><b>idle</b>
+ Limit the time in seconds that a child process
+ waits between service requests.
+
+ <b>max</b><i>_</i><b>use</b>
+ Limit the number of service requests handled by a
+
+
+
+ 2
+
+
+
+
+
+MASTER(8) MASTER(8)
+
+
+ child process.
+
+<b>FILES</b>
+ /etc/postfix/main.cf: global configuration file.
+ /etc/postfix/master.cf: master process configuration file.
+ /var/spool/postfix/pid/master.pid: master lock file.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="qmgr.8.html">qmgr(8)</a> queue manager
+ <a href="pickup.8.html">pickup(8)</a> local mail pickup
+ syslogd(8) system logging
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+
+
+</pre> </body> </html>
--- /dev/null
+<html>
+
+<head>
+
+<title>Postfix Overview - Introduction</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix
+Overview - Introduction</h1>
+
+<hr>
+
+<a href="overview.html">Up one level</a> | Introduction | <a
+href="goals.html">Goals and features</a> | <a
+href="architecture.html">Global architecture</a> | <a
+href="queuing.html">Queue Management</a> | <a
+href="security.html">Security</a>
+
+<p>
+
+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.
+
+<p>
+
+Postfix is my attempt to provide an alternative to the widely-used
+<a href="http://www.sendmail.org/">Sendmail</a> 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.
+
+<p>
+
+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.
+
+<p>
+
+Postfix is a direct competitor to the <a href="http://www.qmail.org">
+qmail </a> by Dan Bernstein. That's competitor, not enemy. I'm sure
+that friendly competition will help to improve both programs.
+
+<hr>
+
+<a href="overview.html">Up one level</a> | Introduction | <a
+href="goals.html">Goals and features</a> | <a
+href="architecture.html">Global architecture</a> | <a
+href="queuing.html">Queue Management</a> | <a
+href="security.html">Security</a>
+
+</body>
+
+</html>
--- /dev/null
+sendmail.1.html
\ No newline at end of file
--- /dev/null
+#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
--- /dev/null
+<html>
+
+<head>
+
+<title>Postfix Mail System Overview</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix
+Mail System Overview</h1>
+
+<hr>
+
+<a href="index.html">Up one level</a> | Postfix Overview | <a
+href="anatomy.html">Postfix Anatomy</a> | <a
+href="config.html">Postfix Configuration</a> | <a href="faq.html">Postfix
+FAQ</a>
+
+<h2>Table of contents</h2>
+
+<ul>
+
+<li><a href="motivation.html">Introduction</a>. How the Postfix
+mail system came into being, and what it attempts to achieve.
+
+<p>
+
+<li><a href="goals.html">Goals and features</a>. A summary of the
+specific problems that Postfix tries to solve.
+
+<p>
+
+<li><a href="architecture.html">Global architecture</a>. The global
+architecture of the Postfix mail system.
+
+<p>
+
+<li><a href="queuing.html">Queue management</a>. The Postfix queue
+organization, and the strategies that it uses for delivery.
+
+<p>
+
+<li><a href="security.html">Security</a>. Postfix was designed to
+just receive and deliver mail, without opening holes for intruders.
+
+</ul>
+
+<hr>
+
+<a href="index.html">Up one level</a> | Postfix Overview | <a
+href="anatomy.html">Postfix Anatomy</a> | <a
+href="config.html">Postfix Configuration</a> | <a href="faq.html">Postfix
+FAQ</a>
+
+</body>
+
+</html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+PICKUP(8) PICKUP(8)
+
+
+<b>NAME</b>
+ pickup - Postfix local mail pickup
+
+<b>SYNOPSIS</b>
+ <b>pickup</b> [generic Postfix daemon options]
+
+<b>DESCRIPTION</b>
+ The <b>pickup</b> daemon waits for hints that new mail has been
+ dropped into the world-writable <b>maildrop</b> directory, and
+ feeds it into the <a href="cleanup.8.html"><b>cleanup</b>(8)</a> daemon. Ill-formatted files
+ are deleted without notifying the originator. This pro-
+ gram expects to be run from the <a href="master.8.html"><b>master</b>(8)</a> process manager.
+
+<b>STANDARDS</b>
+ None. The <b>pickup</b> daemon does not interact with the outside
+ world.
+
+<b>SECURITY</b>
+ The <b>pickup</b> 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.
+
+<b>DIAGNOSTICS</b>
+ Problems and transactions are logged to <b>syslogd</b>(8).
+
+<b>BUGS</b>
+ The <b>pickup</b> daemon copies mail from file to the <a href="cleanup.8.html"><b>cleanup</b>(8)</a>
+ daemon. It could avoid message copying overhead by send-
+ ing a file descriptor instead of file data, but then the
+ already complex <a href="cleanup.8.html"><b>cleanup</b>(8)</a> daemon would have to deal with
+ unfiltered user data.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
+ command after a configuration change.
+
+<b>Miscellaneous</b>
+ <b>mail</b><i>_</i><b>owner</b>
+ The process privileges used while not opening a
+ <b>maildrop</b> file.
+
+ <b>queue</b><i>_</i><b>directory</b>
+ Top-level directory of the Postfix queue.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="cleanup.8.html">cleanup(8)</a> message canonicalization
+ <a href="master.8.html">master(8)</a> process manager
+ syslogd(8) system logging
+
+
+
+ 1
+
+
+
+
+
+PICKUP(8) PICKUP(8)
+
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+PIPE(8) PIPE(8)
+
+
+<b>NAME</b>
+ pipe - Postfix delivery to external command
+
+<b>SYNOPSIS</b>
+ <b>pipe</b> [generic Postfix daemon options] command_attributes...
+
+<b>DESCRIPTION</b>
+ The <b>pipe</b> 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 <a href="master.8.html"><b>master</b>(8)</a>
+ process manager.
+
+ The <b>pipe</b> 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 <a href="bounce.8.html"><b>bounce</b>(8)</a> or <a href="defer.8.html"><b>defer</b>(8)</a> daemon as
+ appropriate.
+
+<b>COMMAND</b> <b>ATTRIBUTE</b> <b>SYNTAX</b>
+ The external command attributes are given in the <b>master.cf</b>
+ file at the end of a service definition. The syntax is as
+ follows:
+
+ <b>flags=F</b>> (optional)
+ Optional message processing flags. By default, a
+ message is copied unchanged.
+
+ <b>F</b> Prepend a "<b>From</b> <i>sender</i> <i>time_stamp</i>" envelope
+ header to the message content. This is
+ expected by, for example, <b>UUCP</b> software. The
+ <b>F</b> flag also causes an empty line to be
+ appended to the message.
+
+ > Prepend > to lines starting with "<b>From</b> ".
+ This expected by, for example, <b>UUCP</b> soft-
+ ware.
+
+ <b>user</b>=<i>username</i> (required)
+ The external command is executed with the rights of
+ the specified <i>username</i>. The software refuses to
+ execute commands with root privileges, or with the
+ privileges of the mail system owner.
+
+ <b>argv</b>=<i>command</i>... (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:
+
+ <b>${extension</b>}
+ This macro expands to the extension part of
+ a recipient address. For example, with an
+ address <i>user+foo@domain</i> the extension is
+ <i>foo</i>. A command-line argument that contains
+ <b>${extension</b>} expands into as many command-
+ line arguments as there are recipients.
+
+ <b>${mailbox</b>}
+ This macro expands to the complete local
+ part of a recipient address. For example,
+ with an address <i>user+foo@domain</i> the mailbox
+ is <i>user+foo</i>. A command-line argument that
+ contains <b>${mailbox</b>} expands into as many
+ command-line arguments as there are recipi-
+ ents.
+
+ <b>${nexthop</b>}
+ This macro expands to the next-hop hostname.
+
+ <b>${recipient</b>}
+ This macro expands to the complete recipient
+ address. A command-line argument that con-
+ tains <b>${recipient</b>} expands into as many com-
+ mand-line arguments as there are recipients.
+
+ <b>${sender</b>}
+ This macro expands to the envelope sender
+ address.
+
+ <b>${user</b>}
+ This macro expands to the username part of a
+ recipient address. For example, with an
+ address <i>user+foo@domain</i> the username part is
+ <i>user</i>. A command-line argument that contains
+ <b>${user</b>} expands into as many command-line
+ arguments as there are recipients.
+
+ In addition to the form ${<i>name</i>}, the forms $<i>name</i> and
+ $(<i>name</i>) are also recognized. Specify <b>$$</b> where a single <b>$</b>
+ is wanted.
+
+<b>DIAGNOSTICS</b>
+ Command exit status codes are expected to follow the con-
+ ventions defined in <<b>sysexits.h</b>>.
+
+ Problems and transactions are logged to <b>syslogd</b>(8). Cor-
+ rupted message files are marked so that the queue manager
+ can move them to the <b>corrupt</b> queue for further inspection.
+
+
+
+
+
+ 2
+
+
+
+
+
+PIPE(8) PIPE(8)
+
+
+<b>SECURITY</b>
+ 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.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
+ command after a configuration change.
+
+<b>Miscellaneous</b>
+ <b>mail</b><i>_</i><b>owner</b>
+ The process privileges used while not running an
+ external command.
+
+<b>Resource</b> <b>controls</b>
+ In the text below, <i>transport</i> is the first field in a <b>mas-</b>
+ <b>ter.cf</b> entry.
+
+ <i>transport_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
+ Limit the number of parallel deliveries to the same
+ destination, for delivery via the named <i>transport</i>.
+ The default limit is taken from the <b>default</b><i>_</i><b>desti-</b>
+ <b>nation</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b> parameter. The limit is
+ enforced by the Postfix queue manager.
+
+ <i>transport_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
+ Limit the number of recipients per message deliv-
+ ery, for delivery via the named <i>transport</i>. The
+ default limit is taken from the <b>default</b><i>_</i><b>destina-</b>
+ <b>tion</b><i>_</i><b>recipient</b><i>_</i><b>limit</b> parameter. The limit is
+ enforced by the Postfix queue manager.
+
+ <i>transport_</i><b>time</b><i>_</i><b>limit</b>
+ Limit the time for delivery to external command,
+ for delivery via the named <b>transport</b>. The default
+ limit is taken from the <b>command</b><i>_</i><b>time</b><i>_</i><b>limit</b> parame-
+ ter. The limit is enforced by the Postfix queue
+ manager.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="bounce.8.html">bounce(8)</a> non-delivery status reports
+ <a href="master.8.html">master(8)</a> process manager
+ <a href="qmgr.8.html">qmgr(8)</a> queue manager
+ syslogd(8) system logging
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+
+
+
+ 3
+
+
+
+
+
+PIPE(8) PIPE(8)
+
+
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 4
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+POSTALIAS(1) POSTALIAS(1)
+
+
+<b>NAME</b>
+ postalias - Postfix alias database maintenance
+
+<b>SYNOPSIS</b>
+ <b>postalias</b> [<b>-c</b> <i>config_dir</i>] [<b>-i</b>] [<b>-v</b>] [<i>file_type</i>:]<i>file_name</i>
+ ...
+
+<b>DESCRIPTION</b>
+ The <b>postalias</b> 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:
+
+ <b>-c</b> <i>config_dir</i>
+ Read the <b>main.cf</b> configuration file in the named
+ directory.
+
+ <b>-i</b> Incremental mode. Read entries from standard input
+ and do not truncate an existing database. By
+ default, <b>postalias</b> creates a new database from the
+ entries in <b>file</b><i>_</i><b>name</b>.
+
+ <b>-v</b> Enable verbose logging for debugging purposes. Mul-
+ tiple <b>-v</b> options make the software increasingly
+ verbose.
+
+ Arguments:
+
+ <i>file_type</i>
+ The type of database to be produced.
+
+ <b>btree</b> The output is a btree file, named
+ <i>file_name</i><b>.db</b>. This is available only on
+ systems with support for <b>db</b> databases.
+
+ <b>dbm</b> The output consists of two files, named
+ <i>file_name</i><b>.pag</b> and <i>file_name</i><b>.dir</b>. This is
+ available only on systems with support for
+ <b>dbm</b> databases.
+
+ <b>hash</b> The output is a hashed file, named
+ <i>file_name</i><b>.db</b>. This is available only on
+ systems with support for <b>db</b> databases.
+
+ When no <i>file_type</i> is specified, the software uses
+ the database type specified via the <b>database</b><i>_</i><b>type</b>
+
+
+
+ 1
+
+
+
+
+
+POSTALIAS(1) POSTALIAS(1)
+
+
+ configuration parameter. The default value for
+ this parameter depends on the host environment.
+
+ <i>file_name</i>
+ The name of the alias database source file when
+ rebuilding a database.
+
+<b>DIAGNOSTICS</b>
+ 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.
+
+<b>ENVIRONMENT</b>
+ <b>MAIL</b><i>_</i><b>CONFIG</b>
+ Mail configuration database.
+
+ <b>MAIL</b><i>_</i><b>VERBOSE</b>
+ Enable verbose logging for debugging purposes.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values.
+
+ <b>database</b><i>_</i><b>type</b>
+ Default alias database type. On many UNIX systems,
+ the default type is either <b>dbm</b> or <b>hash</b>.
+
+<b>STANDARDS</b>
+ RFC 822 (ARPA Internet Text Messages)
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="aliases.5.html">aliases(5)</a> format of alias database input file.
+ <a href="sendmail.1.html">sendmail(1)</a> mail posting and compatibility interface.
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+POSTCAT(1) POSTCAT(1)
+
+
+<b>NAME</b>
+ postcat - show Postfix queue file contents
+
+<b>SYNOPSIS</b>
+ <b>postcat</b> [<b>-v</b>] [<i>files</i>...]
+
+<b>DESCRIPTION</b>
+ The <b>postcat</b> command prints the contents of the named Post-
+ fix queue <i>files</i> in human-readable form. If no <i>files</i> are
+ specified on the command line, the program reads from
+ standard input.
+
+ Options:
+
+ <b>-v</b> Enable verbose mode for debugging purposes. Multi-
+ ple <b>-v</b> options make the software increasingly ver-
+ bose.
+
+<b>DIAGNOSTICS</b>
+ Problems are reported to the standard error stream.
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+POSTCONF(1) POSTCONF(1)
+
+
+<b>NAME</b>
+ postconf - Postfix configuration utility
+
+<b>SYNOPSIS</b>
+ <b>postconf</b> [<b>-d</b>] [<b>-n</b>] [<b>-v</b>] [<i>parameter</i> <i>...</i>]
+
+<b>DESCRIPTION</b>
+ The <b>postconf</b> command prints the actual value of <i>parameter</i>
+ (all known parameters by default).
+
+ Options:
+
+ <b>-d</b> Print default parameter settings instead of actual
+ settings.
+
+ <b>-n</b> Print non-default parameter settings only.
+
+ <b>-v</b> Enable verbose mode for debugging purposes. Multi-
+ ple <b>-v</b> options make the software increasingly ver-
+ bose.
+
+<b>DIAGNOSTICS</b>
+ Problems are reported to the standard error stream.
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+POSTDROP(1) POSTDROP(1)
+
+
+<b>NAME</b>
+ postdrop - Postfix mail posting agent
+
+<b>SYNOPSIS</b>
+ <b>postdrop</b> [<i>option</i> <i>...</i>]
+
+<b>DESCRIPTION</b>
+ The <b>postdrop</b> command creates a file in the <b>maildrop</b> 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 <b>maildrop</b> queue
+ directory.
+
+ The <b>postdrop</b> command is automatically invoked by the <a href="sendmail.1.html"><b>send-</b>
+ <b>mail</b>(1)</a> mail posting agent when the <b>maildrop</b> queue direc-
+ tory is not writable.
+
+ Options:
+
+ <b>-v</b> Enable verbose logging for debugging purposes. Mul-
+ tiple <b>-v</b> options make the software increasingly
+ verbose.
+
+<b>SECURITY</b>
+ This program is designed so that it can run with set-user
+ (or group) id privileges.
+
+<b>DIAGNOSTICS</b>
+ Fatal errors: malformed input, I/O error, out of memory.
+ Problems are logged to <b>syslogd</b>(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.
+
+<b>ENVIRONMENT</b>
+ The program deletes all environment information, because
+ the C library can't be trusted.
+
+<b>FILES</b>
+ /var/spool/postfix, mail queue
+ /etc/postfix, configuration files
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ See the Postfix <b>main.cf</b> file for syntax details and for
+ default values. Use the <b>postfix</b> <b>reload</b> command after a
+ configuration change.
+
+ <b>queue</b><i>_</i><b>directory</b>
+ Top-level directory of the Postfix queue. This is
+ also the root directory of Postfix daemons that run
+ chrooted.
+
+
+
+
+
+ 1
+
+
+
+
+
+POSTDROP(1) POSTDROP(1)
+
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="sendmail.1.html">sendmail(1)</a> compatibility interface
+ syslogd(8) system logging
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+POSTFIX(1) POSTFIX(1)
+
+
+<b>NAME</b>
+ postfix - Postfix control program
+
+<b>SYNOPSIS</b>
+ <b>postfix</b> [<b>-c</b> <i>config_dir</i>] [<b>-D</b>] [<b>-v</b>] <i>command</i>
+
+<b>DESCRIPTION</b>
+ The <b>postfix</b> command controls the operation of the Postfix
+ mail system: start or stop the <b>master</b> daemon, do a health
+ check, and other maintenance. The command sets up a stan-
+ dardized environment and runs the <b>postfix-script</b> shell
+ script to do the actual work.
+
+ The following commands are implemented:
+
+ <b>check</b> Validate the Postfix mail system configuration.
+ Warn about bad directory/file ownership or permis-
+ sions, and create missing directories.
+
+ <b>start</b> Start the Postfix mail system. This also runs the
+ configuration check described above.
+
+ <b>stop</b> 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 <b>start</b>
+ and <b>stop</b> commands in succession. Use the <b>reload</b>
+ command instead.
+
+ <b>abort</b> Stop the Postfix mail system abruptly. Running pro-
+ cesses are signaled to stop immediately.
+
+ <b>flush</b> 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.
+
+ <b>reload</b> Re-read configuration files. Running processes ter-
+ minate at their earliest convenience.
+
+ The following options are implemented:
+
+ <b>-c</b> <i>config_dir</i>
+ The absolute path to a directory with Postfix con-
+ figuration files. Use this to distinguish between
+ multiple Postfix instances on the same host.
+
+ <b>-D</b> (with <b>postfix</b> <b>start</b> only)
+ Run each Postfix daemon under control of a debugger
+ as specified via the <b>debugger</b><i>_</i><b>command</b> configuration
+ parameter.
+
+
+
+
+ 1
+
+
+
+
+
+POSTFIX(1) POSTFIX(1)
+
+
+ <b>-v</b> Enable verbose logging for debugging purposes. Mul-
+ tiple <b>-v</b> options make the software increasingly
+ verbose.
+
+<b>ENVIRONMENT</b>
+ The <b>postfix</b> command sets the following environment vari-
+ ables:
+
+ <b>MAIL</b><i>_</i><b>CONFIG</b>
+ The Postfix configuration directory.
+
+ <b>MAIL</b><i>_</i><b>VERBOSE</b>
+ This is set when the -v command-line option is pre-
+ sent.
+
+ <b>MAIL</b><i>_</i><b>DEBUG</b>
+ 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:
+
+ <b>command</b><i>_</i><b>directory</b>
+ The directory with Postfix support commands
+ (default: <b>$program</b><i>_</i><b>directory</b>).
+
+ <b>daemon</b><i>_</i><b>directory</b>
+ The directory with Postfix daemon programs
+ (default: <b>$program</b><i>_</i><b>directory</b>).
+
+ <b>config</b><i>_</i><b>directory</b>
+ The directory with configuration files and with
+ administrative shell scripts.
+
+ <b>queue</b><i>_</i><b>directory</b>
+ The directory with the Postfix queue directory (and
+ with some files needed for programs running in a
+ chrooted environment).
+
+ <b>mail</b><i>_</i><b>owner</b>
+ The owner of the Postfix queue and of most Postfix
+ processes.
+
+<b>FILES</b>
+ $<b>config</b><i>_</i><b>directory/postfix-script</b>, administrative commands
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="master.8.html">master(8)</a> Postfix master program
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+
+
+
+
+ 2
+
+
+
+
+
+POSTFIX(1) POSTFIX(1)
+
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+POSTKICK(1) POSTKICK(1)
+
+
+<b>NAME</b>
+ postkick - kick a Postfix service
+
+<b>SYNOPSIS</b>
+ <b>postkick</b> [<b>-c</b> <i>config_dir</i>] [<b>-v</b>] <i>class</i> <i>service</i> <i>request</i>
+
+<b>DESCRIPTION</b>
+ The <b>postkick</b> command sends <i>request</i> to the specified <i>ser-</i>
+ <i>vice</i> over a local transport channel. This command makes
+ Postfix private IPC accessible for use in, for example,
+ shell scripts.
+
+ Options:
+
+ <b>-c</b> <i>config_dir</i>
+ Read configuration information from <b>main.cf</b> in the
+ named configuration directory.
+
+ <b>-v</b> Enable verbose logging for debugging purposes. Mul-
+ tiple <b>-v</b> options make the software increasingly
+ verbose.
+
+ Arguments:
+
+ <i>class</i> Name of a class of local transport channel end-
+ points, either <b>public</b> (accessible by any local
+ user) or <b>private</b> (administrative access only).
+
+ <i>service</i>
+ The name of a local transport endpoint within the
+ named class.
+
+ <i>request</i>
+ A string. The list of valid requests is service-
+ specific.
+
+<b>DIAGNOSTICS</b>
+ Problems and transactions are logged to the standard error
+ stream.
+
+<b>ENVIRONMENT</b>
+ <b>MAIL</b><i>_</i><b>CONFIG</b>
+ Directory with Postfix configuration files.
+
+ <b>MAIL</b><i>_</i><b>VERBOSE</b>
+ Enable verbose logging.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values.
+
+ <b>queue</b><i>_</i><b>directory</b>
+ Location of the Postfix queue, and of the local IPC
+
+
+
+ 1
+
+
+
+
+
+POSTKICK(1) POSTKICK(1)
+
+
+ communication endpoints.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="qmgr.8.html">qmgr(8)</a> queue manager trigger protocol
+ <a href="pickup.8.html">pickup(8)</a> local pickup daemon
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+POSTLOCK(1) POSTLOCK(1)
+
+
+<b>NAME</b>
+ postlock - lock mail folder and execute command
+
+<b>SYNOPSIS</b>
+ <b>postlock</b> [<b>-c</b> <i>config_dir</i><b>]</b> <b>[-v</b>] <i>file</i> <i>command...</i>
+
+<b>DESCRIPTION</b>
+ The <b>postlock</b> command locks <i>file</i> for exclusive access, and
+ executes <i>command</i>. The locking method is compatible with
+ the Postfix UNIX-style local delivery agent.
+
+ Options:
+
+ <b>-c</b> <i>config_dir</i>
+ Read configuration information from <b>main.cf</b> in the
+ named configuration directory.
+
+ <b>-v</b> Enable verbose mode for debugging purposes. Multi-
+ ple <b>-v</b> options make the software increasingly ver-
+ bose.
+
+ Arguments:
+
+ <i>file</i> A mailbox file. The user should have read/write
+ permission.
+
+ <i>command...</i>
+ The command to execute while <i>file</i> is locked for
+ exclusive access. The command is executed
+ directly, i.e. without interpretation by a shell
+ command interpreter.
+
+<b>DIAGNOSTICS</b>
+ The result status is 255 (on some systems: -1) when <b>post-</b>
+ <b>lock</b> could not perform the requested operation. Other-
+ wise, the exit status is the exit status from the command.
+
+<b>BUGS</b>
+ 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.
+
+<b>ENVIRONMENT</b>
+ <b>MAIL</b><i>_</i><b>CONFIG</b>
+ Directory with Postfix configuration files.
+
+ <b>MAIL</b><i>_</i><b>VERBOSE</b>
+ Enable verbose logging.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values.
+
+
+
+
+ 1
+
+
+
+
+
+POSTLOCK(1) POSTLOCK(1)
+
+
+<b>Locking</b> <b>controls</b>
+ <b>deliver</b><i>_</i><b>lock</b><i>_</i><b>attempts</b>
+ Limit the number of attempts to acquire an exclu-
+ sive lock.
+
+ <b>deliver</b><i>_</i><b>lock</b><i>_</i><b>delay</b>
+ Time in seconds between successive attempts to
+ acquire an exclusive lock.
+
+ <b>stale</b><i>_</i><b>lock</b><i>_</i><b>time</b>
+ Limit the time after which a stale lock is removed.
+
+<b>Resource</b> <b>controls</b>
+ <b>fork</b><i>_</i><b>attempts</b>
+ Number of attempts to <b>fork</b>() a process before giv-
+ ing up.
+
+ <b>fork</b><i>_</i><b>delay</b>
+ Delay in seconds between successive <b>fork</b>()
+ attempts.
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+POSTLOG(1) POSTLOG(1)
+
+
+<b>NAME</b>
+ postlog - Postfix-compatible logging utility
+
+<b>SYNOPSIS</b>
+ <b>postlog</b> [<b>-i</b>] [<b>-p</b> <i>priority</i><b>]</b> <b>[-t</b> <i>tag</i>] [<b>-v</b>] [<i>text...</i>]
+
+<b>DESCRIPTION</b>
+ The <b>postlog</b> command implements a Postfix-compatible log-
+ ging interface for use in, for example, shell scripts.
+
+ By default, <b>postlog</b> logs the <i>text</i> given on the command
+ line as one record. If no <i>text</i> is specified on the command
+ line, <b>postlog</b> reads from standard input and logs each
+ input line as one record.
+
+ Logging is sent to <b>syslogd</b>(8); when the standard error
+ stream is connected to a terminal, logging is sent there
+ as well.
+
+ The following options are implemented:
+
+ <b>-i</b> Include the process ID in the logging tag.
+
+ <b>-p</b> <i>priority</i>
+ Specifies the logging severity: <b>info</b> (default),
+ <b>warn</b>, <b>error</b>, <b>fatal</b>, or <b>panic</b>.
+
+ <b>-t</b> <i>tag</i> Specifies the logging tag, that is, the identifying
+ name that appears at the beginning of each logging
+ record.
+
+ <b>-v</b> Enable verbose logging for debugging purposes. Mul-
+ tiple <b>-v</b> options make the software increasingly
+ verbose.
+
+<b>SEE</b> <b>ALSO</b>
+ syslogd(8) syslog daemon.
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+POSTMAP(1) POSTMAP(1)
+
+
+<b>NAME</b>
+ postmap - Postfix lookup table management
+
+<b>SYNOPSIS</b>
+ <b>postmap</b> [<b>-c</b> <i>config_dir</i>] [<b>-i</b>] [<b>-v</b>] [<i>file_type</i>:]<i>file_name</i>
+
+<b>DESCRIPTION</b>
+ The <b>postmap</b> command creates a new Postfix lookup table, or
+ updates an existing one. The input and output formats are
+ expected to be compatible with:
+
+ <b>makemap</b> <i>file_type</i> <i>file_name</i> < <i>file_name</i>
+
+ 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:
+
+ <b>o</b> Blank lines are ignored. So are lines beginning
+ with `#'.
+
+ <b>o</b> A table entry has the form
+
+ <i>key</i> whitespace <i>value</i>
+
+ <b>o</b> A line that starts with whitespace continues the
+ preceding line.
+
+ The <i>key</i> and <i>value</i> 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 <i>key</i> is mapped to lowercase to make mapping
+ lookups case insensitive.
+
+ Options:
+
+ <b>-c</b> <i>config_dir</i>
+ Read the <b>main.cf</b> configuration file in the named
+ directory.
+
+ <b>-i</b> Incremental mode. Read entries from standard input
+ and do not truncate an existing database. By
+ default, <b>postmap</b> creates a new database from the
+ entries in <b>file</b><i>_</i><b>name</b>.
+
+ <b>-v</b> Enable verbose logging for debugging purposes. Mul-
+ tiple <b>-v</b> options make the software increasingly
+ verbose.
+
+ Arguments:
+
+
+
+
+ 1
+
+
+
+
+
+POSTMAP(1) POSTMAP(1)
+
+
+ <i>file_type</i>
+ The type of database to be produced.
+
+ <b>btree</b> The output file is a btree file, named
+ <i>file_name</i><b>.db</b>. This is available only on
+ systems with support for <b>db</b> databases.
+
+ <b>dbm</b> The output consists of two files, named
+ <i>file_name</i><b>.pag</b> and <i>file_name</i><b>.dir</b>. This is
+ available only on systems with support for
+ <b>dbm</b> databases.
+
+ <b>hash</b> The output file is a hashed file, named
+ <i>file_name</i><b>.db</b>. This is available only on
+ systems with support for <b>db</b> databases.
+
+ When no <i>file_type</i> is specified, the software uses
+ the database type specified via the <b>database</b><i>_</i><b>type</b>
+ configuration parameter.
+
+ <i>file_name</i>
+ The name of the lookup table source file when
+ rebuilding a database.
+
+<b>DIAGNOSTICS</b>
+ 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.
+
+<b>ENVIRONMENT</b>
+ <b>MAIL</b><i>_</i><b>CONFIG</b>
+ Mail configuration database
+
+ <b>MAIL</b><i>_</i><b>VERBOSE</b>
+ Enable verbose logging for debugging purposes.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ <b>database</b><i>_</i><b>type</b>
+ Default output database type. On many UNIX sys-
+ tems, the default database type is either <b>hash</b> or
+ <b>dbm</b>.
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+ 2
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+QMGR(8) QMGR(8)
+
+
+<b>NAME</b>
+ qmgr - Postfix queue manager
+
+<b>SYNOPSIS</b>
+ <b>qmgr</b> [generic Postfix daemon options]
+
+<b>DESCRIPTION</b>
+ The <b>qmgr</b> 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 <a href="trivial-rewrite.8.html"><b>triv-</b>
+ <b>ial-rewrite</b>(8)</a> daemon. This program expects to be run
+ from the <a href="master.8.html"><b>master</b>(8)</a> process manager.
+
+ Mail addressed to the local <b>double-bounce</b> address is
+ silently discarded. This stops potential loops caused by
+ undeliverable bounce notifications.
+
+ Mail addressed to a user listed in the optional <b>relocated</b>
+ database is bounced with a "user has moved to <i>new_loca-</i>
+ <i>tion</i>" message. See <a href="relocated.5.html"><b>relocated</b>(5)</a> for a precise description.
+
+<b>MAIL</b> <b>QUEUES</b>
+ The <b>qmgr</b> daemon maintains the following queues:
+
+ <b>incoming</b>
+ Inbound mail from the network, or mail picked up by
+ the local <b>pickup</b> agent from the <b>maildrop</b> directory.
+
+ <b>active</b> Messages that the queue manager has opened for
+ delivery. Only a limited number of messages is
+ allowed to enter the <b>active</b> queue (leaky bucket
+ strategy, for a fixed delivery rate).
+
+ <b>deferred</b>
+ Mail that could not be delivered upon the first
+ attempt. The queue manager implements exponential
+ backoff by doubling the time between delivery
+ attempts.
+
+ <b>corrupt</b>
+ Unreadable or damaged queue files are moved here
+ for inspection.
+
+<b>DELIVERY</b> <b>STATUS</b> <b>REPORTS</b>
+ The <b>qmgr</b> 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:
+
+ <b>bounce</b> Per-recipient status information about why mail is
+ bounced. These files are maintained by the
+ <a href="bounce.8.html"><b>bounce</b>(8)</a> daemon.
+
+ <b>defer</b> Per-recipient status information about why mail is
+
+
+
+ 1
+
+
+
+
+
+QMGR(8) QMGR(8)
+
+
+ delayed. These files are maintained by the
+ <a href="defer.8.html"><b>defer</b>(8)</a> daemon.
+
+ The <b>qmgr</b> daemon is responsible for asking the <a href="bounce.8.html"><b>bounce</b>(8)</a> or
+ <a href="defer.8.html"><b>defer</b>(8)</a> daemons to send non-delivery reports.
+
+<b>STRATEGIES</b>
+ The queue manager implements a variety of strategies for
+ either opening queue files (input) or for message delivery
+ (output).
+
+ <b>leaky</b> <b>bucket</b>
+ This strategy limits the number of messages in the
+ <b>active</b> queue and prevents the queue manager from
+ running out of memory under heavy load.
+
+ <b>fairness</b>
+ When the <b>active</b> queue has room, the queue manager
+ takes one message from the <b>incoming</b> queue and one
+ from the <b>deferred</b> queue. This prevents a large mail
+ backlog from blocking the delivery of new mail.
+
+ <b>slow</b> <b>start</b>
+ This strategy eliminates "thundering herd" problems
+ by slowly adjusting the number of parallel deliver-
+ ies to the same destination.
+
+ <b>round</b> <b>robin</b> and <b>random</b> <b>walk</b>
+ 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.
+
+ <b>exponential</b> <b>backoff</b>
+ Mail that cannot be delivered upon the first
+ attempt is deferred. The time interval between
+ delivery attempts is doubled after each attempt.
+
+ <b>destination</b> <b>status</b> <b>cache</b>
+ The queue manager avoids unnecessary delivery
+ attempts by maintaining a short-term, in-memory
+ list of unreachable destinations.
+
+<b>TRIGGERS</b>
+ 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)
+
+
+ <b>D</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>DEFERRED)</b>
+ Start a deferred queue scan. If a deferred queue
+ scan is already in progress, that scan will be
+ restarted as soon as it finishes.
+
+ <b>I</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>INCOMING)</b>
+ Start an incoming queue scan. If an incoming queue
+ scan is already in progress, that scan will be
+ restarted as soon as it finishes.
+
+ <b>A</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>SCAN</b><i>_</i><b>ALL)</b>
+ Ignore deferred queue file time stamps. The request
+ affects the next deferred queue scan.
+
+ <b>F</b> <b>(QMGR</b><i>_</i><b>REQ</b><i>_</i><b>FLUSH</b><i>_</i><b>DEAD)</b>
+ Purge all information about dead transports and
+ destinations.
+
+ <b>W</b> <b>(TRIGGER</b><i>_</i><b>REQ</b><i>_</i><b>WAKEUP)</b>
+ 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 <b>qmgr</b> daemon reads an entire buffer worth of triggers.
+ Multiple identical trigger requests are collapsed into
+ one, and trigger requests are sorted so that <b>A</b> and <b>F</b> pre-
+ cede <b>D</b> and <b>I</b>. Thus, in order to force a deferred queue
+ run, one would request <b>A</b> <b>F</b> <b>D</b>; in order to notify the queue
+ manager of the arrival of new mail one would request <b>I</b>.
+
+<b>STANDARDS</b>
+ None. The <b>qmgr</b> daemon does not interact with the outside
+ world.
+
+<b>SECURITY</b>
+ The <b>qmgr</b> 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
+ <b>qmgr</b> daemon does not talk to the outside world, and it can
+ be run at fixed low privilege in a chrooted environment.
+
+<b>DIAGNOSTICS</b>
+ Problems and transactions are logged to the syslog daemon.
+ Corrupted message files are saved to the <b>corrupt</b> queue for
+ further inspection.
+
+ Depending on the setting of the <b>notify</b><i>_</i><b>classes</b> parameter,
+ the postmaster is notified of bounces and of other trou-
+ ble.
+
+<b>BUGS</b>
+ A single queue manager process has to compete for disk
+ access with multiple front-end processes such as <b>smtpd</b>. A
+
+
+
+ 3
+
+
+
+
+
+QMGR(8) QMGR(8)
+
+
+ sudden burst of inbound mail can negatively impact out-
+ bound delivery rates.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
+ command after a configuration change.
+
+<b>Miscellaneous</b>
+ <b>relocated</b><i>_</i><b>maps</b>
+ Tables with contact information for users, hosts or
+ domains that no longer exist. See <a href="relocated.5.html"><b>relocated</b>(5)</a>.
+
+ <b>queue</b><i>_</i><b>directory</b>
+ Top-level directory of the Postfix queue.
+
+<b>Active</b> <b>queue</b> <b>controls</b>
+ <b>qmgr</b><i>_</i><b>message</b><i>_</i><b>active</b><i>_</i><b>limit</b>
+ Limit the number of messages in the active queue.
+
+ <b>qmgr</b><i>_</i><b>message</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
+ Limit the number of in-memory recipients.
+
+ This parameter also limits the size of the short-
+ term, in-memory destination cache.
+
+<b>Timing</b> <b>controls</b>
+ <b>min</b><i>_</i><b>backoff</b>
+ 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.
+
+ <b>max</b><i>_</i><b>backoff</b>
+ Maximal time in seconds between delivery attempts
+ of a deferred message.
+
+ <b>maximal</b><i>_</i><b>queue</b><i>_</i><b>lifetime</b>
+ Maximal time in days a message is queued before it
+ is sent back as undeliverable.
+
+ <b>queue</b><i>_</i><b>run</b><i>_</i><b>delay</b>
+ Time in seconds between deferred queue scans. Queue
+ scans do not overlap.
+
+ <b>transport</b><i>_</i><b>retry</b><i>_</i><b>time</b>
+ Time in seconds between attempts to contact a bro-
+ ken delivery transport.
+
+<b>Concurrency</b> <b>controls</b>
+ In the text below, <i>transport</i> is the first field in a
+
+
+
+ 4
+
+
+
+
+
+QMGR(8) QMGR(8)
+
+
+ <b>master.cf</b> entry.
+
+ <b>initial</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b>
+ Initial per-destination concurrency level for par-
+ allel delivery to the same destination.
+
+ <b>default</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
+ Default limit on the number of parallel deliveries
+ to the same destination.
+
+ <i>transport_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
+ Limit on the number of parallel deliveries to the
+ same destination, for delivery via the named mes-
+ sage <i>transport</i>.
+
+<b>Recipient</b> <b>controls</b>
+ <b>default</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
+ Default limit on the number of recipients per mes-
+ sage transfer.
+
+ <i>transport_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
+ Limit on the number of recipients per message
+ transfer, for the named message <i>transport</i>.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="master.8.html">master(8)</a>, process manager
+ <a href="relocated.5.html">relocated(5)</a>, format of the "user has moved" table
+ syslogd(8) system logging
+ <a href="trivial-rewrite.8.html">trivial-rewrite(8)</a>, address routing
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5
+
+
+</pre> </body> </html>
--- /dev/null
+<html>
+
+<head>
+
+<title>Postfix Overview - Queue Management</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a>
+Postfix Overview - Queue Management</h1>
+
+<hr>
+
+<a href="overview.html">Up one level</a> | <a
+href="motivation.html">Introduction</a> | <a href="goals.html">Goals
+and features</a> | <a href="architecture.html">Global architecture</a>
+| Queue Management | <a href="security.html">Security</a>
+
+<h2>Postfix mail queues</h2>
+
+Postfix has four different queues: <b>maildrop</b>, <b>incoming</b>,
+<b>active</b> and <b>deferred</b> (click the upper left-hand icon
+for the big picture). Locally-posted mail is deposited into the
+<b>maildrop</b>, and is copied to the <b>incoming</b> queue after
+some cleaning up. The <b>incoming</b> queue is for mail that is
+still arriving or that the queue manager hasn't looked at yet.
+The <b>active</b> 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 <b>deferred</b> queue, so that it does not get in the
+way of other deliveries.
+
+<p>
+
+The queue manager keeps information in memory about the <b>active</b>
+queue only. The active queue size is limited on purpose: <i>the
+queue manager should never run out of working memory because of a
+peak message workload</i>. Whenever there is space in the
+<b>active</b> queue, the queue manager lets in one message from the
+<b>incoming</b> queue and one from the <b>deferred</b> queue. This
+guarantees that new mail will get through even when there is a
+large backlog.
+
+<h2>No thundering herd</h2>
+
+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.
+
+<p>
+
+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 <i>slow
+start</i> algorithm
+
+<h2>Fairness</h2>
+
+Apart from the <i>thundering herd</i> controls, the Postfix delivery
+strategy is based on <i>round-robin</i> selection and <i>random
+walks</i>. The queue manager sorts message recipients in the active
+queue by destination, makes round-robin walks <i>along</i> all
+destination queues, and makes random walks <i>within</i> each
+destination queue.
+
+<p>
+
+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.
+
+<p>
+
+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.
+
+<h2>Exponential backoff</h2>
+
+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.
+
+<p>
+
+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.
+
+<h2>Destination status cache</h2>
+
+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.
+
+<hr>
+
+<a href="overview.html">Up one level</a> | <a
+href="motivation.html">Introduction</a> | <a href="goals.html">Goals
+and features</a> | <a href="architecture.html">Global architecture</a>
+| Queue Management | <a href="security.html">Security</a>
+
+</body>
+
+</html>
--- /dev/null
+<html>
+
+<!--Warning, preformatted content! -->
+
+<head>
+
+<title> Postfix Rate Controls</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix Rate Controls</h1>
+
+<hr>
+
+<a href="config.html">Up one level</a> |
+<a href="basic.html">Basic Configuration</a> | <a href="uce.html">UCE
+Controls</a> | Rate Controls | <a href="resource.html">Resource
+Controls</a> | <a href="rewrite.html">Address Manipulation</a>
+
+<h2> Introduction</h2>
+
+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 <i>thundering herd</i> syndrome:
+they literally flood other systems with mail. Postfix tries to be
+a fast mailer and a good neighbor at the same time.
+
+<p>
+
+On the inbound side, the Postfix <a href="smtpd.8.html">SMTP
+server</a> 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.
+
+<p>
+
+Unless indicated otherwise, all parameters described here are in
+the <b>main.cf</b> file. If you change parameters of a running
+Postfix system, don't forget to issue a <b>postfix reload</b>
+command.
+
+<ul>
+
+<a href="#process">Process limits</a>
+
+<p>
+
+<a href="#destination">Destination concurrency</a>
+
+<p>
+
+<a href="#recipients">Recipient limits</a>
+
+<p>
+
+<a href="#postponing">Always postponing delivery</a>
+
+<p>
+
+<a href="#backoff">Backoff from dead hosts</a>
+
+<p>
+
+<a href="#slowdown">Slowing down bad clients</a>
+
+</ul>
+
+<a name="process"><h2> Process limits</h2>
+
+The <b>default_process_limit</b> 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
+<b>default_process_limit</b> of 10 is probably more than adequate.
+Use a larger value if your machine is a major mail hub.
+
+<p>
+
+You can override this setting for specific Postfix daemons by
+editing the <b>master.cf</b> file. For example, if you do not
+wish to receive 50 SMTP messages at the same time, you could specify:
+
+<dl>
+
+<dd> <pre>
+# ==========================================================================
+# service type private unpriv chroot wakeup maxproc command + args
+# (yes) (yes) (yes) (never) (50)
+# ==========================================================================
+. . .
+smtp inet n - - - 5 smtpd
+. . .
+
+</pre>
+
+</dl>
+
+<a name="destination"><h2> Destination concurrency</h2>
+
+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.
+
+<p>
+
+The Postfix queue manager comes to the rescue. This program implements
+the analog of the TCP <i> slow start</i> 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.
+
+<p>
+
+The <b>initial_destination_concurrency</b> 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 <b><a
+href="#process">process limit</a></b> and the destination concurrency
+limit for the specific mail transport channel.
+
+<p>
+
+The <b>default_destination_concurrency_limit</b> parameter
+(default: 10) controls how many messages may be sent to the same
+destination simultaneously. You can override this setting for
+specific delivery channels (<b>local, smtp, uucp</b> etc.). The
+<b>main.cf</b> file recommends the following:
+
+<dl>
+
+<dd> <b>local_destination_concurrency_limit = 2</b>
+
+<dd> <b>default_destination_concurrency_limit = 10</b>
+
+</dl>
+
+The <b>local_destination_concurrency_limit</b> 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 <b>.forward </b> file, or if the recipient is a mailing list
+manager, you don't want to run too many instances at the same time.
+
+<p>
+
+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.
+
+<a name="recipients"><h2> Recipient limits</h2>
+
+The <b>default_destination_recipient_limit</b> parameter (default:
+50) controls how many recipients a Postfix delivery agent (<b>smtp</b>,
+<b>uucp</b>, etc.) will send with each copy of an email message.
+If an email message has more than
+<b>$default_destination_recipient_limit</b> 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.
+
+<p>
+
+You can override this setting for specific Postfix delivery agents
+(<b>smtp, uucp,</b> etc.). For example:
+
+<dl>
+
+<dd> <b>uucp_destination_recipient_limit = 100</b>
+
+</dl>
+
+would limit the number of recipients per <b>UUCP</b> delivery to 100.
+
+<p>
+
+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.
+
+<p>
+
+The <b>smtpd_recipient_limit</b> 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.
+
+<a name="postponing"><h2> Always postponing delivery</h2>
+
+The <b>defer_transports</b> parameter allows you to specify what
+mail should always be deferred until Postfix is explicitly asked
+to deliver.
+
+<p>
+
+A small site that is on-line only part of the time, and
+that wants to defer all deliveries until the command <b>sendmail
+-q</b> is executed (e.g., from a PPP dialout script) would use:
+
+<p>
+
+<dl>
+
+<dd><b>defer_transports = smtp</b>
+
+</dl>
+
+<p>
+
+An ISP can use the <b>defer_transports</b> feature for customers
+that are off-line most of the time. The customer can trigger delivery
+by issuing an <b>ETRN</b> command at the SMTP port. The following
+examples show how to configure such a customer:
+
+<p>
+
+<dl>
+
+<dt>/etc/postfix/main.cf:
+
+<p>
+
+<dd><b>defer_transports = hold</b>
+
+<p>
+
+You can specify any number of transports here. The example gives
+just one.
+
+<p>
+
+<dt>/etc/postfix/<a href="transport.5.html">transport</a>:
+
+<p>
+
+<dd><b>customer.com hold:[gateway.customer.com]</b>
+
+<dd><b>.customer.com hold:[gateway.customer.com]</b>
+
+<p>
+
+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.
+
+<p>
+
+<dt>/etc/postfix/master.cf:
+
+<p>
+
+<dd><b>hold unix - - n - - smtp</b>
+
+<p>
+
+This is just the <b>master.cf</b> entry for regular SMTP, with the
+first field changed to <b>hold</b>.
+
+</dl>
+
+<a name="backoff"><h2> Backoff from unreachable hosts</h2>
+
+When a Postfix delivery agent (<b>smtp, local, uucp,</b> etc.)
+is unable to deliver a message it may blame the message itself or
+the receiving party.
+
+<ul>
+
+<li> 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 <i>exponential backoff</i> behavior.
+
+<p>
+
+<li> 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.
+
+</ul>
+
+<p>
+
+As you would expect, this whole process is governed by a bunch of
+little parameters.
+
+<dl>
+
+<dt> <b>queue_run_delay</b> (default: 1000 seconds) <dd> How
+often the queue manager scans the queue for deferred mail.
+
+<p>
+
+<dt> <b>maximal_queue_lifetime</b> (default: 5 days) <dd> How
+long a message stays in the queue before it is sent back as
+undeliverable.
+
+<p>
+
+<dt> <b>minimal_backoff_time</b> (default: 1000 seconds) <dd> 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.
+
+<p>
+
+<dt> <b>maximal_backoff_time</b> (default: 4000 seconds) <dd> The
+maximal amount of time a message won't be looked at after a delivery
+failure.
+
+<p>
+
+<dt> <b>qmgr_message_recipient_limit</b> (default: 1000) <dd> 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.
+
+</dl>
+
+<a name="slowdown"><h2> Slowing down bad clients</h2>
+
+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.
+
+<p>
+
+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 <a href="resource.html">resource controls</a> documentation
+for details.
+
+<p>
+
+The Postfix <a href="smtpd.8.html">SMTP server</a> increments a
+per-session error counter whenever a client request is unrecognized
+or unimplemented, or whenever a client request violates <a
+href="uce.html">UCE restrictions</a> or other reasons. The error
+counter is reset when a message is transferred successfully.
+
+<p>
+
+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:
+
+<p>
+
+<dl>
+
+<a name="#smtpd_error_sleep_time">
+
+<dt> <b>smtpd_error_sleep_time</b> (default: 5 seconds) <dd> 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 <i>connect-error-disconnect</i>
+loop.
+
+<p>
+
+<a name="#smtpd_soft_error_limit">
+
+<dt> <b>smtpd_soft_error_limit</b> (default: 10) <dd> When the
+per-session error count exceeds this value, the SMTP server sleeps
+<b>error_count</b> seconds before responding to a client request.
+
+<p>
+
+<a name="#smtpd_hard_error_limit">
+
+<dt> <b>smtpd_hard_error_limit</b> (default: 100) <dd> When
+the per-session error count exceeds this value, the SMTP server
+disconnects.
+
+</dl>
+
+<p>
+
+Unfortunately, the Postfix SMTP server does not yet know how to
+limit the number of connections <i> from the same client</i>,
+other than by limiting the total number of SMTP server processes
+(see <a href="#process">process limit</a>). 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.
+
+<hr>
+
+<a href="config.html">Up one level</a> |
+<a href="basic.html">Basic Configuration</a> | <a href="uce.html">UCE
+Controls</a> | Rate Controls | <a href="resource.html">Resource
+Controls</a> | <a href="rewrite.html">Address Manipulation</a>
+
+</body>
+
+</html>
--- /dev/null
+<html>
+
+<head>
+
+<title>Postfix Anatomy - Receiving Mail</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix
+Anatomy - Receiving Mail</h1>
+
+<hr>
+
+<a href="anatomy.html">Up one level</a> | Receiving Mail | <a
+href="delivering.html">Delivering Mail</a> | <a
+href="backstage.html">Behind the Scenes</a> | <a
+href="commands.html">Command-line Utilities</a>
+
+<p>
+
+When a message enters the Postfix mail system, the first stop on
+the inside is the <b>incoming</b> 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.
+
+<p>
+
+<center>
+
+<img src="inbound.gif" width="497" height="213">
+
+</center>
+
+<p>
+
+<ul>
+
+<li>Mail is posted locally. The Postfix <a
+href="sendmail.1.html">sendmail</a> program deposits the message
+into the world-writable <b>maildrop</b> directory, where the message
+is picked up by the <a href="pickup.8.html">pickup</a> 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 <b>maildrop</b> directory must be such that a
+user cannot delete someone elses mail.
+
+<p>
+
+<li>Mail comes in via the network. The Postfix <a href="smtpd.8.html">SMTP
+server</a> 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 <a href="uce.html">UCE</a> controls
+on the basis of local or network-based black lists, DNS lookups,
+and other client request information.
+
+<p>
+
+<li>Mail is generated internally by the Postfix system itself, in
+order to return undeliverable mail to the sender. The <a
+href="bounce.8.html">bounce or defer</a> daemon brings the bad
+news.
+
+<p>
+
+<li>Mail is forwarded by the <a href="local.8.html">local</a>
+delivery agent, either via an entry in the system-wide <a
+href="aliases.5.html">alias</a> database, or via an entry in a
+per-user <a href="aliases.5.html">.forward</a> file. This is
+indicated with the unlabeled arrow.
+
+<p>
+
+<li>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 <a href="basic.html#notify">notify</a> the postmaster
+of SMTP protocol problems, UCE policy violations, and so on.
+
+<p>
+
+<li>The <a href="cleanup.8.html">cleanup</a> daemon implements the
+final processing stage for new mail. It adds missing <b>From:</b>
+and other message headers, arranges for address rewriting to the
+standard <i>user@fully.qualified.domain</i> form, and optionally
+extracts recipient addresses from message headers. The <b> cleanup</b>
+daemon inserts the result as a single queue file into the
+<b>incoming</b> queue, and notifies the <a href="qmgr.8.html">queue
+manager</a> of the arrival of new mail. The <b>cleanup</b> daemon
+can be configured to transform addresses on the basis of <a
+href="rewrite.html#canonical">canonical</a> and <a
+href="rewrite.html#virtual">virtual</a> table lookups.
+
+<p>
+
+<li>On request by the <b>cleanup</b> daemon, the <a
+href="trivial-rewrite.8.html">trivial-rewrite</a> daemon rewrites
+addresses to the standard <i>user@fully.qualified.domain</i> 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 <a
+href="rewrite.html">table lookup</a>.
+
+</ul>
+
+<hr>
+
+<a href="anatomy.html">Up one level</a> | Receiving Mail | <a
+href="delivering.html">Delivering Mail</a> | <a
+href="backstage.html">Behind the Scenes</a> | <a
+href="commands.html">Command-line Utilities</a>
+
+</body>
+
+</html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+RELOCATED(5) RELOCATED(5)
+
+
+<b>NAME</b>
+ relocated - format of Postfix relocated table
+
+<b>SYNOPSIS</b>
+ <b>postmap</b> <b>/etc/postfix/relocated</b>
+
+<b>DESCRIPTION</b>
+ The optional <b>relocated</b> file provides the information that
+ is used in "user has moved to <i>new_location</i>" bounce mes-
+ sages.
+
+ The file serves as input to the <a href="postmap.1.html"><b>postmap</b>(1)</a> command. The
+ result, an indexed file in <b>dbm</b> or <b>db</b> format, is used for
+ fast searching by the mail system. After an update issue a
+ <b>postfix</b> <b>reload</b> command to make the change visible.
+
+ Table lookups are case insensitive.
+
+ The format of the table is as follows:
+
+ <b>o</b> Blank lines are ignored, as are lines beginning
+ with `#'.
+
+ <b>o</b> An entry has one of the following form:
+ <i>key</i> <i>new_location</i>
+ Where <i>new_location</i> specifies contact information
+ such as an email address, or perhaps a street
+ address or telephone number.
+
+ The <i>key</i> field is one of the following:
+
+ <i>user</i>@<i>domain</i>
+ Matches <i>user</i>@<i>domain</i>. This form has precedence over
+ all other forms.
+
+ <i>user</i> Matches <i>user</i>@<i>site</i> when <i>site</i> is $<b>myorigin</b>, when <i>site</i>
+ is listed in $<b>mydestination</b>, or when <i>site</i> is listed
+ in $<b>inet</b><i>_</i><b>interfaces</b>.
+
+ @<i>domain</i>
+ Matches every address in <i>domain</i>. This form has the
+ lowest precedence.
+
+<b>ADDRESS</b> <b>EXTENSION</b>
+ When the search fails, and the address localpart contains
+ the optional recipient delimiter (e.g., <i>user+foo</i>@<i>domain</i>),
+ the search is repeated for the unextended address (e.g.
+ <i>user</i>@<i>domain</i>).
+
+<b>BUGS</b>
+ The table format does not understand quoting conventions.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+
+
+
+ 1
+
+
+
+
+
+RELOCATED(5) RELOCATED(5)
+
+
+ to this topic. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
+ command after a configuration change.
+
+ <b>relocated</b><i>_</i><b>maps</b>
+ List of lookup tables for relocated users or sites.
+
+ Other parameters of interest:
+
+ <b>inet</b><i>_</i><b>interfaces</b>
+ The network interface addresses that this system
+ receives mail on.
+
+ <b>mydestination</b>
+ List of domains that this mail system considers
+ local.
+
+ <b>myorigin</b>
+ The domain that is appended to locally-posted mail.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="postmap.1.html">postmap(1)</a> create lookup table
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with
+ this software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+</pre> </body> </html>
--- /dev/null
+<html>
+
+<head>
+
+<title> Postfix Configuration - Resource Controls</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix Configuration - Resource Controls</h1>
+
+<hr>
+
+<a href="config.html">Up one level</a> | <a href="basic.html">Basic
+Configuration</a> | <a href="uce.html">UCE Controls</a> | <a
+href="rate.html">Rate Controls</a> | Resource Controls | <a
+href="rewrite.html">Address Manipulation</a>
+
+<h2> Introduction</h2>
+
+The Postfix system is designed to run within a finite memory budget.
+To this end, there are configurable limits on the <i>size</i> of
+in-memory objects such as text line fragments, on the <i>number of
+instances</i> of such objects, and on the <i>time</i> 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.
+
+<p>
+
+<ul>
+
+<li> <a href="#size">Object size limits</a>
+
+<p>
+
+<li> <a href="#count">Object count limits</a>
+
+<p>
+
+<li> <a href="#time">Time limits</a>
+
+<p>
+
+<li> <a href="#lock">Acquiring exclusive file locks</a>
+
+<p>
+
+<li> <a href="#fan">Error recovery</a>
+
+</ul>
+
+<a name="size"><h2> Object size limits</h2> </a>
+
+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?
+
+<p>
+
+<dl>
+
+<dt> <b>line_length_limit</b> (default: 2048 bytes)
+
+<dd> How long a line of text can be before it is broken up into
+pieces. All Postfix perimeter programs (<a href="smtpd.8.html">SMTP
+server</a>, <a href="smtp.8.html">SMTP client</a>, <a
+href="pickup.8.html">local pickup</a> and <a href="local.8.html">local
+delivery</a>) enforce this line length limit when reading data from
+an untrusted source. Long lines are reconstructed upon delivery.
+
+<p>
+
+<dt> <b>header_size_limit</b> (default: 102400 bytes)
+
+<dd> How much text may be carried in a multi-line message header.
+Header text that does not fit in <b>$header_size_limit</b> bytes
+overflows into the message body. This limit is enforced by the <a
+href="cleanup.8.html"> cleanup</a> header rewriting code.
+
+</dl>
+
+<p>
+
+The following parameters restrict the use of file system storage:
+
+<dl>
+
+<dt> <b>message_size_limit</b> (default: 10240000 bytes)
+
+<dd> The maximal size of a Postfix queue file for inbound mail,
+including envelope information (sender, recipient, etc.).
+
+<p>
+
+<dt> <b>queue_minfree</b> (default: no restriction)
+
+<dd> How many bytes of free space are needed in the queue file
+system. The <a href="smtpd.8.html">SMTP server</a> 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 <b>$message_size_limit</b> so that the mail
+system won't get stuck on a single large message.
+
+<p>
+
+<dt> <b>bounce_size_limit</b> (default: 50000 bytes)
+
+<dd> How much of an undelivered message is sent back to the sender.
+
+</dl>
+
+<a name="count"><h2> Object count limits</h2> </a>
+
+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.
+
+<dl>
+
+<dt> <b>qmgr_message_recipient_limit</b> (default: 10000)
+
+<dd> An upper bound on the number of <a href="qmgr.8.html">queue
+manager</a> in-memory recipient address data structures. This
+parameter also controls the number of instances of other in-memory
+data structures. See, for example, the <a
+href="rate.html#backoff">delivery rate control</a> documentation.
+
+<p>
+
+<dt> <b>qmgr_message_active_limit</b> (default: 1000)
+
+<dd> An upper limit on the number of messages in the <b>active</b>
+queue. For an introduction to the Postfix queue organization see
+the <a href="overview.html">Postfix overview</a> documentation.
+
+<p>
+
+<dt> <b>duplicate_filter_limit</b> (default: 1000)
+
+<dd> How many recipient addresses the <a href="local.8.html">local
+delivery</a> agent and <a href="cleanup.8.html">address cleanup</a>
+daemon remember when delivering a message. A recipient address is
+ignored when it is found in the remembered list.
+
+</dl>
+
+<a name="time"><h2> Time limits</h2> </a>
+
+External commands are given a finite time for completion. Such
+commands are run by the <a href="local.8.html">local</a> delivery
+agent when it finds a "|<i>command</i>" destination in an <a
+href="aliases.5.html">alias</a> database, <a
+href="aliases.5.html">:include:</a> file or <a
+href="aliases.5.html">.forward</a> file. The <a
+href="pipe.8.html">pipe</a> mailer implements an alternative way
+to pipe mail into external commands.
+
+<dl>
+
+<dt> <b>command_time_limit</b> (default: 1000 seconds)
+
+<dd> How long the <a href="local.8.html">local</a> delivery agent
+will wait before aborting an external command.
+
+<p>
+
+<dt> <i>service_name</i><b>_time_limit</b> (default:
+<b>$command_time_limit</b>)
+
+<dd> The time limit for delivery to external commands via the
+<b>pipe</b> mailer. For <i>service_name</i>, substitute the service
+name (the first field in the <b>master.cf </b> file).
+
+</dl>
+
+<a name="lock"><h2> Acquiring exclusive file locks</h2> </a>
+
+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:
+
+<ul>
+
+<li>Internal locks, implemented with the <b>fcntl()</b> or
+<b>flock()</b> system primitives.
+
+<p>
+
+<li>External locks, implemented as files named <i>file</i><b>.lock</b>.
+
+</ul>
+
+Depending on the host system, Postfix uses one method or both.
+The following configuration parameters control how Postfix deals
+with file locks:
+
+<dl>
+
+<dt> <b>deliver_lock_attempts</b> (default: 5)
+
+<dd> The number of times to try locking a file before giving up.
+
+<p>
+
+<dt> <b>deliver_lock_delay</b> (default: 1 second)
+
+<dd> How long to wait between attempts to lock a file.
+
+<p>
+
+<dt> <b>stale_lock_time</b> (default: 500)
+
+<dd> How old an external lock file may be before it is forcibly
+removed.
+
+</dl>
+
+<a name="fan"><h2> Error recovery</h2> </a>
+
+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.
+
+<p>
+
+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 <a
+href="master.8.html">master</a> daemon will restart processes after
+some delay). Each failed attempt is logged; hopefully, someone will
+notice the problem and fix it.
+
+<p>
+
+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:
+
+<dl>
+
+<dt> <b>fork_attempts</b> (default: 5 times)
+
+<dd> The number of times to attempt to create a new process before
+giving up.
+
+<p>
+
+<dt> <b>fork_delay</b> (default: 1 second)
+
+<dd> The delay between attempts to create a new process.
+
+<p>
+
+<dt> <b>transport_retry_time</b> (default: 60 seconds)
+
+<dd> The amount of time between queue manager attempts to contact
+an apparently defunct Postfix delivery service.
+
+</dl>
+
+<hr>
+
+<a href="config.html">Up one level</a> | <a href="basic.html">Basic
+Configuration</a> | <a href="uce.html">UCE Controls</a> | <a
+href="rate.html">Rate Controls</a> | Resource Controls | <a
+href="rewrite.html">Address Manipulation</a>
+
+</body>
+
+</html>
--- /dev/null
+<html>
+
+<head>
+
+<title> Postfix Configuration - Address Manipulation</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix Configuration - Address Manipulation</h1>
+
+<hr>
+
+<a href="config.html">Up one level</a> |
+<a href="basic.html"> Basic Configuration</a> | <a href="uce.html">UCE
+Controls</a> | <a href="rate.html"> Rate Controls</a> | <a
+href="resource.html"> Resource Controls</a> | Address Manipulation
+
+<h2> Introduction</h2>
+
+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.
+
+<p>
+
+Unless indicated otherwise, all parameters described here are in
+the <b>main.cf</b> file. If you change parameters of a running
+Postfix system, don't forget to issue a <b>postfix reload</b>
+command.
+
+<p>
+
+All mail:
+
+<p>
+
+<ul>
+
+<li> <a href="#standard"> Rewrite addresses to standard form</a>
+
+<p>
+
+<li> <a href="#canonical"> Canonical address mapping</a>
+
+<p>
+
+<li> <a href="#masquerade"> Address masquerading</a>
+
+<p>
+
+<li> <a href="#virtual"> Virtual address mapping</a>
+
+<p>
+
+<li> <a href="#relocated"> Relocated users table</a>
+
+<p>
+
+<li> <a href="#transport"> Mail transport switch</a>
+
+</ul>
+
+<p>
+
+Local delivery:
+
+<p>
+
+<ul>
+
+<li> <a href="#aliases"> Alias database</a>
+
+</ul>
+
+<a name="standard"> <h2> Rewrite addresses to standard form</h2>
+
+Before the <a href="cleanup.8.html">cleanup</a> daemon runs an
+address through any lookup table, it first rewrites the address to
+the standard <i>user@fully.qualified.domain</i> form, by sending
+the address to the <a href="trivial-rewrite.8.html">trivial-rewrite</a>
+daemon. The purpose of rewriting to standard form is to reduce the
+number of entries needed in lookup tables. The Postfix <a
+href="trivial-rewrite.8.html">trivial-rewrite </a> program implements
+the following hard-coded address manipulations:
+
+<dl>
+
+<dt>Rewrite <i>@hosta,@hostb:user@site</i> to <i>user@site</i>
+
+<dd>The source route feature has been deprecated. Postfix has no
+ability to handle such addresses, other than to strip off the source
+route.
+
+<p>
+
+<a name="swap_bangpath">
+
+<dt>Rewrite <i>site!user</i> to <i>user@site</i>
+
+<dd> This feature is controlled by the boolean <b>swap_bangpath</b>
+parameter (default: <b>yes</b>). The purpose is to rewrite
+<b>UUCP</b>-style addresses to domain style. This is useful only when you
+receive mail via <b>UUCP</b>, but it probably does not hurt otherwise.
+
+<p>
+
+<a name="percent_hack">
+
+<dt>Rewrite <i>user%domain</i> to <i>user@domain</i>
+
+<dd> This feature is controlled by the boolean <b>allow_percent_hack</b>
+parameter (default: <b>yes</b>). Typically, this is used in order
+to deal with monstrosities such as <i>user%domain@otherdomain</i>.
+
+<p>
+
+<a name="append_at_myorigin">
+
+<dt>Rewrite <i>user</i> to <i>user@<a
+href="basic.html#myorigin">$myorigin</a></i>
+
+<dd> This feature is controlled by the boolean <b>append_at_myorigin</b>
+parameter (default: <b>yes</b>). The purpose is to get consistent
+treatment of <i>user</i> on every machine in <b>$myorigin</b>.
+
+<p>
+
+You probably should never turn off this feature, because a lot of
+Postfix components expect that all addresses have the form
+<i>user@domain</i>.
+
+<p>
+
+If your machine is not the main machine for <b>$myorigin</b> and
+you wish to have some users delivered locally without going via
+that main machine, make an entry in the <a href="#virtual">virtual</a>
+table that redirects <i>user@$myorigin</i> to <i>user@$myhostname</i>.
+
+<p>
+
+<a name="append_dot_mydomain">
+
+<dt>Rewrite <i>user@host</i> to <i>user@host.<a
+href="basic.html#mydomain">$mydomain</a></i>
+
+<dd> This feature is controlled by the boolean <b>append_dot_mydomain</b>
+parameter (default: <b>yes</b>). The purpose is to get consistent
+treatment of different forms of the same hostname.
+
+<p>
+
+Some will argue that rewriting <i>host</i> to <i>host.$mydomain</i>
+is bad. That is why it can be turned off. Others like the convenience of having
+the local domain appended automatically.
+
+<p>
+
+<a name="strip_trailing_dot">
+
+<dt>Rewrite <i>user@site.</i> to <i>user@site</i> (without the
+trailing dot).
+
+</dl>
+
+<a name="canonical"> <h2> Canonical address mapping</h2>
+
+Before the <a href="cleanup.8.html">cleanup</a> daemon stores
+inbound mail into the <b>incoming</b> queue, it uses the <a
+href="canonical.5.html">canonical</a> table to rewrite all addresses
+in message envelopes and in message headers, local or remote. The
+mapping is useful to replace login names by <i>Firstname.Lastname</i>
+style addresses, or to clean up invalid domains in mail addresses
+produced by legacy mail systems.
+
+<p>
+
+Canonical mapping is disabled by default. To enable, edit the <b>
+canonical_maps</b> parameter in the <b>main.cf</b> file and
+specify one or more lookup tables, separated by whitespace or commas.
+For example:
+
+<dl>
+
+<dd><b>canonical_maps = hash:/etc/postfix/canonical</b>
+
+</dl>
+
+<p>
+
+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:
+
+<dl>
+
+<dd><b>sender_canonical_maps = hash:/etc/postfix/sender_canonical</b>
+
+<p>
+
+<dd><b>recipient_canonical_maps = hash:/etc/postfix/recipient_canonical</b>
+
+</dl>
+
+<p>
+
+The sender and recipient canonical maps are applied before
+the common canonical maps.
+
+<p>
+
+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.
+
+<a name="masquerade"> <h2> Address masquerading</h2>
+
+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.
+
+<p>
+
+Address masquerading is disabled by default. To enable, edit the
+<b>masquerade_domains</b> parameter in the <b>main.cf</b>
+file and specify one or more domain names separated by whitespace
+or commas. For example:
+
+<dl>
+
+<dd><b>masquerade_domains = $mydomain</b>
+
+</dl>
+
+<p>
+
+In this example, addresses of the form <i>user@host.$mydomain</i>
+would be rewritten to <i>user@$mydomain</i>.
+
+<p>
+
+The <b>masquerade_exceptions</b> 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,
+
+<dl>
+
+<dd><b>masquerade_exceptions = root</b>
+
+</dl>
+
+<p>
+
+
+By default, Postfix makes no exceptions.
+<p>
+
+Subtle point: address masquerading is applied only to message
+headers and envelope sender addresses, not to envelope recipients.
+
+<a name="virtual"> <h2> Virtual address mapping</h2>
+
+After applying the canonical and masquerade mappings, the <a
+href="cleanup.8.html">cleanup</a> daemon uses the <a
+href="virtual.5.html">virtual</a> 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 <i>
+Firstname.Lastname </i> back into UNIX login names, although it
+seems that local <a href="#aliases">aliases</a> are a more appropriate
+vehicle.
+
+<p>
+
+Virtual mapping is disabled by default. To enable, edit the <b>
+virtual_maps</b> parameter in the <b>main.cf</b> file and
+specify one or more lookup tables, separated by whitespace or
+commas. For example:
+
+<dl>
+
+<dd><b>virtual_maps = hash:/etc/postfix/virtual</b>
+
+</dl>
+
+<p>
+
+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.
+
+<a name="relocated"> <h2> Relocated users table</h2>
+
+Next, the queue manager runs each recipient name through the
+<a href="relocated.5.html">relocated</a> 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.
+
+<p>
+
+Lookups of relocated users are disabled by default. To enable, edit
+the <b>relocated_maps</b> parameter in the <b>main.cf</b>
+file and specify one or more lookup tables, separated by whitespace
+or commas. For example:
+
+<dl>
+
+<dd><b>relocated_maps = hash:/etc/postfix/relocated</b>
+
+</dl>
+
+<a name="transport"> <h2> Mail transport switch</h2>
+
+Once the queue manager has established the destination of a message,
+the optional <a href="transport.5.html">transport</a> 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 <a href="smtp.8.html">smtp</a> transport. The transport
+table can be used to send mail to specific sites via <b>UUCP</b>,
+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).
+
+<p>
+
+Transport table lookups are disabled by default. To enable, edit
+the <b>transport_maps</b> parameter in the <b>main.cf</b> file and
+specify one or more lookup tables, separated by whitespace or
+commas. For example:
+
+<dl>
+
+<dd><b>transport_maps = hash:/etc/postfix/transport</b>
+
+</dl>
+
+<a name="aliases"> <h2> Alias database</h2>
+
+When mail is to be delivered locally, the <a href="local.8.html">local</a>
+delivery agent runs each local recipient name through the <a
+href="aliases.5.html"> aliases</a> 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 <b>postmaster</b> to real people. The table can
+also be used to map <i>Firstname.Lastname</i> addresses to login
+names.
+
+<p>
+
+Alias lookups are enabled by default. The default configuration
+depends on the system environment, but it is typically one of the
+following:
+
+<p>
+
+<dl>
+
+<dd> <b>alias_maps = hash:/etc/aliases</b>
+
+<dd> <b>alias_maps = dbm:/etc/aliases, nis:mail.aliases</b>
+
+</dl>
+
+<p>
+
+The path to the alias database file is controlled via the
+<b>alias_database</b> configuration parameter. The value is system
+dependent. Usually it is one of the following:
+
+<dl>
+
+<dd> <b>alias_database = hash:/etc/aliases</b> (4.4BSD, LINUX)
+
+<dd> <b>alias_database = dbm:/etc/aliases</b> (4.3BSD, SYSV<4)
+
+<dd> <b>alias_database = dbm:/etc/mail/aliases</b> (SYSV4)
+
+</dl>
+
+<p>
+
+For security reasons, deliveries to command and file destinations
+are performed with the rights of the alias database owner. A
+default userid, <b>default_privs</b>, is used for deliveries to
+commands/files in <i>root</i>-owned aliases.
+
+<hr>
+
+<a href="config.html">Up one level</a> |
+<a href="basic.html"> Basic Configuration</a> | <a href="uce.html">UCE
+Controls</a> | <a href="rate.html"> Rate Controls</a> | <a
+href="resource.html"> Resource Controls</a> | Address Manipulation
+
+</body>
+
+</html>
--- /dev/null
+<html>
+
+<head>
+
+<title>Postfix Overview - Security</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a> Postfix
+Overview - Security</h1>
+
+<hr>
+
+<a href="overview.html">Up one level</a> | <a
+href="motivation.html">Introduction</a> | <a href="goals.html">Goals
+and features</a> | <a href="architecture.html">Global architecture</a>
+| <a href="queuing.html">Queue Management</a> | Security
+
+<h2>Introduction</h2>
+
+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.
+
+<p>
+
+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.
+
+<h2>Least privilege</h2>
+
+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.
+
+<h2>Insulation</h2>
+
+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.
+
+<h2>Controlled environment</h2>
+
+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.
+
+<h2>Set-uid</h2>
+
+No Postfix program is <b>set-uid</b>. Introducing the concept was
+the biggest mistake made in UNIX history. <b>Set-uid</b> (and its
+weaker cousin, <b>set-gid</b>) causes more trouble than it is worth.
+Each time a new feature is added to the UNIX system, <b>set-uid</b>
+causes a security problem: shared libraries, the <b>/proc</b> file
+system, multi-language support, to mention just a few examples.
+<b>Set-uid</b> 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.
+
+<p>
+
+By default, the <b>maildrop</b> 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.
+
+<p>
+
+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.
+
+<p>
+
+If a world-writable <b>maildrop</b> directory is not acceptable,
+sites can revoke world write permission, and enable <b>set-gid</b>
+privileges for a small helper program that is provided for this
+purpose.
+
+<h2>Trust</h2>
+
+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.
+
+<p>
+
+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: <i> don't let any data
+from the network near a shell</i>.
+
+<h2>Large inputs</h2>
+
+<ul>
+
+<li>Memory for strings and buffers is allocated dynamically, in
+order to prevent buffer overrun problems.
+
+<p>
+
+<li>Long lines in message input are broken up into sequences of
+reasonably-sized chunks, and are reconstructed upon delivery.
+
+<p>
+
+<li>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.
+
+<p>
+
+<li>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.
+
+</ul>
+
+<h2>Other defenses</h2>
+
+<ul>
+
+<li>The number of in-memory instances of any object type is limited,
+to prevent the mail system from becoming wedged under heavy load.
+
+<p>
+
+<li>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.
+
+</ul>
+
+<hr>
+
+<a href="overview.html">Up one level</a> | <a
+href="motivation.html">Introduction</a> | <a href="goals.html">Goals
+and features</a> | <a href="architecture.html">Global architecture</a>
+| <a href="queuing.html">Queue Management</a> | Security
+
+</body>
+
+</html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+SENDMAIL(1) SENDMAIL(1)
+
+
+<b>NAME</b>
+ sendmail - Postfix to Sendmail compatibility interface
+
+<b>SYNOPSIS</b>
+ <b>sendmail</b> [<i>option</i> <i>...</i>] [<i>recipient</i> <i>...</i>]
+
+ <b>mailq</b>
+ <b>sendmail</b> <b>-bp</b>
+
+ <b>newaliases</b>
+ <b>sendmail</b> <b>-I</b>
+
+<b>DESCRIPTION</b>
+ The <b>sendmail</b> 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, <b>sendmail</b> reads a message from standard input
+ and arranges for delivery. <b>sendmail</b> attempts to create a
+ queue file in the <b>maildrop</b> directory. If the process has
+ no write permission, the message is piped through the
+ <a href="postdrop.1.html"><b>postdrop</b>(1)</a> command, which is expected to execute with
+ suitable privileges.
+
+ Specific command aliases are provided for other common
+ modes of operation:
+
+ <b>mailq</b> 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 <a href="showq.8.html"><b>showq</b>(8)</a>
+ daemon.
+
+ <b>newaliases</b>
+ Initialize the alias database. If no alias database
+ type is specified, the program uses the type speci-
+ fied in the <b>database</b><i>_</i><b>type</b> configuration parameter;
+ if no input file is specified, the program pro-
+ cesses the file(s) specified with the
+ <b>alias</b><i>_</i><b>database</b> configuration parameter. This mode
+ of operation is implemented by running the <b>postal-</b>
+ <b>ias</b>(1) command.
+
+ Note: it may take a minute or so before an alias
+ database update becomes visible. Use the <b>postfix</b>
+ <b>reload</b> 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 <b>main.cf</b> configu-
+ ration file.
+
+
+
+ 1
+
+
+
+
+
+SENDMAIL(1) SENDMAIL(1)
+
+
+ The following options are recognized:
+
+ <b>-B</b> <i>body_type</i> (ignored)
+ The message body MIME type. Currently, Postfix
+ implements <b>just-send-eight</b>.
+
+ <b>-C</b> <i>config_file</i> (ignored :-)
+ The path name of the <b>sendmail.cf</b> file. Postfix con-
+ figuration files are kept in <b>/etc/postfix</b>.
+
+ <b>-F</b> <i>full_name</i>
+ Set the sender full name. This is used only with
+ messages that have no <b>From:</b> message header.
+
+ <b>-I</b> Initialize alias database. See the <b>newaliases</b> com-
+ mand above.
+
+ <b>-N</b> <i>dsn</i> (ignored)
+ Delivery status notification control. Currently,
+ Postfix does not implement <b>DSN</b>.
+
+ <b>-R</b> <i>return_limit</i> (ignored)
+ Limit the size of bounced mail. Use the
+ <b>bounce</b><i>_</i><b>size</b><i>_</i><b>limit</b> configuration parameter instead.
+
+ <b>-X</b> <i>log_file</i> (ignored)
+ Log mailer traffic. Use the <b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b> and
+ <b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b> configuration parameters instead.
+
+ <b>-bd</b> Go into daemon mode. This mode of operation is
+ implemented by executing the <b>postfix</b> <b>start</b> command.
+
+ <b>-bi</b> Initialize alias database. See the <b>newaliases</b> com-
+ mand above.
+
+ <b>-bm</b> Read mail from standard input and arrange for
+ delivery. This is the default mode of operation.
+
+ <b>-bp</b> List the mail queue. See the <b>mailq</b> command above.
+
+ <b>-bs</b> 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 <a href="smtpd.8.html"><b>smtpd</b>(8)</a> daemon.
+
+ <b>-f</b> <i>sender</i>
+ Set the envelope sender address. This is the
+ address where delivery problems are sent to, unless
+ the message contains an <b>Errors-To:</b> message header.
+
+ <b>-h</b> <i>hop_count</i> (ignored)
+ Hop count limit. Use the <b>hopcount</b><i>_</i><b>limit</b> configura-
+ tion parameter instead.
+
+
+
+
+ 2
+
+
+
+
+
+SENDMAIL(1) SENDMAIL(1)
+
+
+ <b>-i</b> (ignored)
+ Lines beginning with "." get special treatment only
+ with <b>-bs</b>.
+
+ <b>-m</b> (ignored)
+ Backwards compatibility.
+
+ <b>-n</b> (ignored)
+ Backwards compatibility.
+
+ <b>-oA</b><i>alias_database</i>
+ Non-default alias database. Specify <i>pathname</i> or
+ <i>type</i>:<i>pathname</i>. See <a href="postalias.1.html"><b>postalias</b>(1)</a> for details.
+
+ <b>-o7</b> (ignored)
+
+ <b>-o8</b> (ignored)
+ The message body type. Currently, Postfix imple-
+ ments <b>just-send-eight</b>.
+
+ <b>-om</b> (ignored)
+ The sender is never eliminated from alias etc.
+ expansions.
+
+ <b>-o</b> <i>x</i> <i>value</i> (ignored)
+ Set option <i>x</i> to <i>value</i>. Use the equivalent configu-
+ ration parameter in <b>main.cf</b> instead.
+
+ <b>-q</b> Flush the mail queue. This is implemented by kick-
+ ing the <a href="qmgr.8.html"><b>qmgr</b>(8)</a> daemon.
+
+ <b>-q</b><i>interval</i> (ignored)
+ The interval between queue runs. Use the
+ <b>queue</b><i>_</i><b>run</b><i>_</i><b>delay</b> configuration parameter instead.
+
+ <b>-t</b> Extract recipients from message headers. This
+ requires that no recipients be specified on the
+ command line.
+
+ <b>-v</b> Enable verbose logging for debugging purposes. Mul-
+ tiple <b>-v</b> options make the software increasingly
+ verbose.
+
+<b>SECURITY</b>
+ 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.
+
+<b>DIAGNOSTICS</b>
+ Problems are logged to <b>syslogd</b>(8) and to the standard
+ error stream.
+
+
+
+
+
+ 3
+
+
+
+
+
+SENDMAIL(1) SENDMAIL(1)
+
+
+<b>ENVIRONMENT</b>
+ <b>MAIL</b><i>_</i><b>CONFIG</b>
+ Directory with Postfix configuration files.
+
+ <b>MAIL</b><i>_</i><b>VERBOSE</b>
+ Enable verbose logging
+
+ <b>MAIL</b><i>_</i><b>DEBUG</b>
+ Enable debugging with an external command, as spec-
+ ified with the <b>debugger</b><i>_</i><b>command</b> configuration
+ parameter.
+
+<b>FILES</b>
+ /var/spool/postfix, mail queue
+ /etc/postfix, configuration files
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ See the Postfix <b>main.cf</b> file for syntax details and for
+ default values. Use the <b>postfix</b> <b>reload</b> command after a
+ configuration change.
+
+ <b>alias</b><i>_</i><b>database</b>
+ Default alias database(s) for <b>newaliases</b>. The
+ default value for this parameter is system-spe-
+ cific.
+
+ <b>bounce</b><i>_</i><b>size</b><i>_</i><b>limit</b>
+ The amount of original message context that is sent
+ along with a non-delivery notification.
+
+ <b>database</b><i>_</i><b>type</b>
+ Default alias etc. database type. On many UNIX sys-
+ tems the default type is either <b>dbm</b> or <b>hash</b>.
+
+ <b>debugger</b><i>_</i><b>command</b>
+ Command that is executed after a Postfix daemon has
+ initialized.
+
+ <b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b>
+ Increment in verbose logging level when a remote
+ host matches a pattern in the <b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b>
+ parameter.
+
+ <b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b>
+ 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
+ <b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b> parameter.
+
+ <b>fork</b><i>_</i><b>attempts</b>
+ Number of attempts to <b>fork</b>() a process before giv-
+ ing up.
+
+
+
+
+
+ 4
+
+
+
+
+
+SENDMAIL(1) SENDMAIL(1)
+
+
+ <b>fork</b><i>_</i><b>delay</b>
+ Delay in seconds between successive <b>fork</b>()
+ attempts.
+
+ <b>hopcount</b><i>_</i><b>limit</b>
+ Limit the number of <b>Received:</b> message headers.
+
+ <b>mail</b><i>_</i><b>owner</b>
+ The owner of the mail queue and of most Postfix
+ processes.
+
+ <b>command</b><i>_</i><b>directory</b>
+ Directory with Postfix support commands (default:
+ <b>$program</b><i>_</i><b>directory</b>).
+
+ <b>daemon</b><i>_</i><b>directory</b>
+ Directory with Postfix daemon programs (default:
+ <b>$program</b><i>_</i><b>directory</b>).
+
+ <b>queue</b><i>_</i><b>directory</b>
+ Top-level directory of the Postfix queue. This is
+ also the root directory of Postfix daemons that run
+ chrooted.
+
+ <b>queue</b><i>_</i><b>run</b><i>_</i><b>delay</b>
+ The time between successive scans of the deferred
+ queue.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="pickup.8.html">pickup(8)</a> mail pickup daemon
+ <a href="postalias.1.html">postalias(1)</a> maintain alias database
+ <a href="postdrop.1.html">postdrop(1)</a> privileged posting agent
+ <a href="postfix.1.html">postfix(1)</a> mail system control
+ <a href="postkick.1.html">postkick(1)</a> kick a Postfix daemon
+ <a href="qmgr.8.html">qmgr(8)</a> queue manager
+ <a href="showq.8.html">showq(8)</a> list mail queue
+ <a href="smtpd.8.html">smtpd(8)</a> SMTP server
+ syslogd(8) system logging
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+ 5
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+SHOWQ(8) SHOWQ(8)
+
+
+<b>NAME</b>
+ showq - list the Postfix mail queue
+
+<b>SYNOPSIS</b>
+ <b>showq</b> [generic Postfix daemon options]
+
+<b>DESCRIPTION</b>
+ The <b>showq</b> daemon reports the Postfix mail queue status.
+ It is the program that emulates the sendmail `mailq' com-
+ mand.
+
+ The <b>showq</b> 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.
+
+<b>SECURITY</b>
+ The <b>showq</b> 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.
+
+<b>STANDARDS</b>
+ None. The showq daemon does not interact with the outside
+ world.
+
+<b>DIAGNOSTICS</b>
+ Problems and transactions are logged to <b>syslogd</b>(8).
+
+<b>BUGS</b>
+ The <b>showq</b> daemon runs at a fixed low privilege; conse-
+ quently, it cannot extract information from queue files in
+ the <b>maildrop</b> directory.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="cleanup.8.html">cleanup(8)</a> canonicalize and enqueue mail
+ <a href="pickup.8.html">pickup(8)</a> local mail pickup service
+ <a href="qmgr.8.html">qmgr(8)</a> mail being delivered, delayed mail
+ syslogd(8) system logging
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+ 1
+
+
+</pre> </body> </html>
--- /dev/null
+#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
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+SMTP(8) SMTP(8)
+
+
+<b>NAME</b>
+ smtp - Postfix remote delivery via SMTP
+
+<b>SYNOPSIS</b>
+ <b>smtp</b> [generic Postfix daemon options]
+
+<b>DESCRIPTION</b>
+ 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
+ <a href="master.8.html"><b>master</b>(8)</a> 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 <a href="bounce.8.html"><b>bounce</b>(8)</a> or <a href="defer.8.html"><b>defer</b>(8)</a> 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.
+
+<b>SECURITY</b>
+ 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.
+
+<b>STANDARDS</b>
+ RFC 821 (SMTP protocol)
+ RFC 1651 (SMTP service extensions)
+ RFC 1870 (Message Size Declaration)
+ RFC 2197 (Pipelining)
+
+<b>DIAGNOSTICS</b>
+ Problems and transactions are logged to <b>syslogd</b>(8). Cor-
+ rupted message files are marked so that the queue manager
+ can move them to the <b>corrupt</b> queue for further inspection.
+
+ Depending on the setting of the <b>notify</b><i>_</i><b>classes</b> parameter,
+ the postmaster is notified of bounces, protocol problems,
+ and of other trouble.
+
+<b>BUGS</b>
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
+
+
+
+ 1
+
+
+
+
+
+SMTP(8) SMTP(8)
+
+
+ command after a configuration change.
+
+<b>Miscellaneous</b>
+ <b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b>
+ Verbose logging level increment for hosts that
+ match a pattern in the <b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b> parameter.
+
+ <b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b>
+ 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
+ <b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b> parameter.
+
+ <b>inet</b><i>_</i><b>interfaces</b>
+ 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.
+
+ <b>notify</b><i>_</i><b>classes</b>
+ When this parameter includes the <b>protocol</b> class,
+ send mail to the postmaster with transcripts of
+ SMTP sessions with protocol errors.
+
+<b>Resource</b> <b>controls</b>
+ <b>smtp</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
+ Limit the number of parallel deliveries to the same
+ destination. The default limit is taken from the
+ <b>default</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b> parameter.
+
+ <b>smtp</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
+ Limit the number of recipients per message deliv-
+ ery. The default limit is taken from the
+ <b>default</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b> parameter.
+
+<b>Timeout</b> <b>controls</b>
+ <b>smtp</b><i>_</i><b>connect</b><i>_</i><b>timeout</b>
+ Timeout in seconds for completing a TCP connection.
+ When no connection can be made within the deadline,
+ the SMTP client tries the next address on the mail
+ exchanger list.
+
+ <b>smtp</b><i>_</i><b>helo</b><i>_</i><b>timeout</b>
+ 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.
+
+ <b>smtp</b><i>_</i><b>helo</b><i>_</i><b>timeout</b>
+ Timeout in seconds for sending the <b>HELO</b> command,
+ and for receiving the server response.
+
+
+
+ 2
+
+
+
+
+
+SMTP(8) SMTP(8)
+
+
+ <b>smtp</b><i>_</i><b>mail</b><i>_</i><b>timeout</b>
+ Timeout in seconds for sending the <b>MAIL</b> <b>FROM</b> com-
+ mand, and for receiving the server response.
+
+ <b>smtp</b><i>_</i><b>rcpt</b><i>_</i><b>timeout</b>
+ Timeout in seconds for sending the <b>RCPT</b> <b>TO</b> command,
+ and for receiving the server response.
+
+ <b>smtp</b><i>_</i><b>data</b><i>_</i><b>init</b><i>_</i><b>timeout</b>
+ Timeout in seconds for sending the <b>DATA</b> command,
+ and for receiving the server response.
+
+ <b>smtp</b><i>_</i><b>data</b><i>_</i><b>xfer</b><i>_</i><b>timeout</b>
+ Timeout in seconds for sending the message content.
+
+ <b>smtp</b><i>_</i><b>data</b><i>_</i><b>done</b><i>_</i><b>timeout</b>
+ Timeout in seconds for sending the "<b>.</b>" command, and
+ for receiving the server response. When no response
+ is received, a warning is logged that the mail may
+ be delivered multiple times.
+
+ <b>smtp</b><i>_</i><b>quit</b><i>_</i><b>timeout</b>
+ Timeout in seconds for sending the <b>QUIT</b> command,
+ and for receiving the server response.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="bounce.8.html">bounce(8)</a> non-delivery status reports
+ <a href="master.8.html">master(8)</a> process manager
+ <a href="qmgr.8.html">qmgr(8)</a> queue manager
+ syslogd(8) system logging
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+SMTPD(8) SMTPD(8)
+
+
+<b>NAME</b>
+ smtpd - Postfix SMTP server
+
+<b>SYNOPSIS</b>
+ <b>smtpd</b> [generic Postfix daemon options]
+
+<b>DESCRIPTION</b>
+ The SMTP server accepts network connection requests and
+ performs zero or more SMTP transactions per connection.
+ Each received message is piped through the <a href="cleanup.8.html"><b>cleanup</b>(8)</a> dae-
+ mon, and is placed into the <b>incoming</b> queue as one single
+ queue file. For this mode of operation, the program
+ expects to be run from the <a href="master.8.html"><b>master</b>(8)</a> process manager.
+
+ Alternatively, the SMTP server takes an established con-
+ nection on standard input and deposits messages directly
+ into the <b>maildrop</b> 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 <b>HELO,</b> <b>MAIL</b>
+ <b>FROM,</b> <b>VRFY</b> and <b>RCPT</b> <b>TO</b> commands. They are detailed below
+ and in the <b>main.cf</b> configuration file.
+
+<b>SECURITY</b>
+ 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.
+
+<b>STANDARDS</b>
+ 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)
+
+<b>DIAGNOSTICS</b>
+ Problems and transactions are logged to <b>syslogd</b>(8).
+
+ Depending on the setting of the <b>notify</b><i>_</i><b>classes</b> parameter,
+ the postmaster is notified of bounces, protocol problems,
+ policy violations, and of other trouble.
+
+<b>BUGS</b>
+ RFC 1985 is implemented by forcing delivery of all
+ deferred mail.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
+
+
+
+ 1
+
+
+
+
+
+SMTPD(8) SMTPD(8)
+
+
+ command after a configuration change.
+
+<b>Miscellaneous</b>
+ <b>command</b><i>_</i><b>directory</b>
+ Location of Postfix support commands (default:
+ <b>$program</b><i>_</i><b>directory</b>).
+
+ <b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b>
+ Increment in verbose logging level when a remote
+ host matches a pattern in the <b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b>
+ parameter.
+
+ <b>debug</b><i>_</i><b>peer</b><i>_</i><b>list</b>
+ 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
+ <b>debug</b><i>_</i><b>peer</b><i>_</i><b>level</b> parameter.
+
+ <b>hopcount</b><i>_</i><b>limit</b>
+ Limit the number of <b>Received:</b> message headers.
+
+ <b>notify</b><i>_</i><b>classes</b>
+ List of error classes. Of special interest are:
+
+ <b>policy</b> When a client violates any policy, mail a
+ transcript of the entire SMTP session to the
+ postmaster.
+
+ <b>protocol</b>
+ When a client violates the SMTP protocol or
+ issues an unimplemented command, mail a
+ transcript of the entire SMTP session to the
+ postmaster.
+
+ <b>smtpd</b><i>_</i><b>banner</b>
+ Text that follows the <b>220</b> status code in the SMTP
+ greeting banner.
+
+ <b>smtpd</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
+ Restrict the number of recipients that the SMTP
+ server accepts per message delivery.
+
+ <b>smtpd</b><i>_</i><b>timeout</b>
+ Limit the time to send a server response and to
+ receive a client request.
+
+<b>Resource</b> <b>controls</b>
+ <b>line</b><i>_</i><b>length</b><i>_</i><b>limit</b>
+ Limit the amount of memory in bytes used for the
+ handling of partial input lines.
+
+ <b>message</b><i>_</i><b>size</b><i>_</i><b>limit</b>
+ Limit the total size in bytes of a message, includ-
+ ing on-disk storage for envelope information.
+
+
+
+ 2
+
+
+
+
+
+SMTPD(8) SMTPD(8)
+
+
+ <b>queue</b><i>_</i><b>minfree</b>
+ Minimal amount of free space in bytes in the queue
+ file system for the SMTP server to accept any mail
+ at all.
+
+<b>Tarpitting</b>
+ <b>smtpd</b><i>_</i><b>error</b><i>_</i><b>sleep</b><i>_</i><b>time</b>
+ Time to wait in seconds before sending a 4xx or 5xx
+ server error response.
+
+ <b>smtpd</b><i>_</i><b>soft</b><i>_</i><b>error</b><i>_</i><b>limit</b>
+ When an SMTP client has made this number of errors,
+ wait <i>error_count</i> seconds before responding to any
+ client request.
+
+ <b>smtpd</b><i>_</i><b>hard</b><i>_</i><b>error</b><i>_</i><b>limit</b>
+ Disconnect after a client has made this number of
+ errors.
+
+<b>UCE</b> <b>control</b> <b>restrictions</b>
+ <b>smtpd</b><i>_</i><b>client</b><i>_</i><b>restrictions</b>
+ Restrict what clients may connect to this mail sys-
+ tem.
+
+ <b>smtpd</b><i>_</i><b>helo</b><i>_</i><b>required</b>
+ Require that clients introduce themselves at the
+ beginning of an SMTP session.
+
+ <b>smtpd</b><i>_</i><b>helo</b><i>_</i><b>restrictions</b>
+ Restrict what client hostnames are allowed in <b>HELO</b>
+ and <b>EHLO</b> commands.
+
+ <b>smtpd</b><i>_</i><b>sender</b><i>_</i><b>restrictions</b>
+ Restrict what sender addresses are allowed in <b>MAIL</b>
+ <b>FROM</b> commands.
+
+ <b>smtpd</b><i>_</i><b>recipient</b><i>_</i><b>restrictions</b>
+ Restrict what recipient addresses are allowed in
+ <b>RCPT</b> <b>TO</b> commands.
+
+ <b>maps</b><i>_</i><b>rbl</b><i>_</i><b>domains</b>
+ List of DNS domains that publish the addresses of
+ blacklisted hosts.
+
+ <b>relay</b><i>_</i><b>domains</b>
+ Restrict what domains or networks this mail system
+ will relay mail from or to.
+
+<b>UCE</b> <b>control</b> <b>responses</b>
+ <b>access</b><i>_</i><b>map</b><i>_</i><b>reject</b><i>_</i><b>code</b>
+ Server response when a client violates an access
+ database restriction.
+
+
+
+
+
+ 3
+
+
+
+
+
+SMTPD(8) SMTPD(8)
+
+
+ <b>invalid</b><i>_</i><b>hostname</b><i>_</i><b>reject</b><i>_</i><b>code</b>
+ Server response when a client violates the
+ <b>reject</b><i>_</i><b>invalid</b><i>_</i><b>hostname</b> restriction.
+
+ <b>maps</b><i>_</i><b>rbl</b><i>_</i><b>reject</b><i>_</i><b>code</b>
+ Server response when a client violates the
+ <b>maps</b><i>_</i><b>rbl</b><i>_</i><b>domains</b> restriction.
+
+ <b>reject</b><i>_</i><b>code</b>
+ Response code when the client matches a <b>reject</b>
+ restriction.
+
+ <b>relay</b><i>_</i><b>domains</b><i>_</i><b>reject</b><i>_</i><b>code</b>
+ Server response when a client attempts to violate
+ the mail relay policy.
+
+ <b>unknown</b><i>_</i><b>address</b><i>_</i><b>reject</b><i>_</i><b>code</b>
+ Server response when a client violates the
+ <b>reject</b><i>_</i><b>unknown</b><i>_</i><b>address</b> restriction.
+
+ <b>unknown</b><i>_</i><b>client</b><i>_</i><b>reject</b><i>_</i><b>code</b>
+ Server response when a client without address to
+ name mapping violates the <b>reject</b><i>_</i><b>unknown</b><i>_</i><b>clients</b>
+ restriction.
+
+ <b>unknown</b><i>_</i><b>hostname</b><i>_</i><b>reject</b><i>_</i><b>code</b>
+ Server response when a client violates the
+ <b>reject</b><i>_</i><b>unknown</b><i>_</i><b>hostname</b> restriction.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="cleanup.8.html">cleanup(8)</a> message canonicalization
+ <a href="master.8.html">master(8)</a> process manager
+ syslogd(8) system logging
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 4
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+TRANSPORT(5) TRANSPORT(5)
+
+
+<b>NAME</b>
+ transport - format of Postfix transport table
+
+<b>SYNOPSIS</b>
+ <b>postmap</b> <b>/etc/postfix/transport</b>
+
+<b>DESCRIPTION</b>
+ The optional <b>transport</b> file specifies a mapping from
+ domain hierarchies to message delivery transports and/or
+ relay hosts. The mapping is used by the <a href="trivial-rewrite.8.html"><b>trivial-rewrite</b>(8)</a>
+ daemon.
+
+ The file serves as input to the <a href="postmap.1.html"><b>postmap</b>(1)</a> command. The
+ result, an indexed file in <b>dbm</b> or <b>db</b> format, is used for
+ fast searching by the mail system. After updating this
+ table, issue the <b>postfix</b> <b>reload</b> 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 `#'.
+
+ <i>domain</i> <i>transport</i>:<i>nexthop</i>
+ Mail for <i>domain</i> is delivered through <i>transport</i> to
+ <i>nexthop</i>.
+
+ <i>.domain</i> <i>transport</i>:<i>nexthop</i>
+ Mail for any subdomain of <i>domain</i> is delivered
+ through <i>transport</i> to <i>nexthop</i>.
+
+ The interpretation of the <i>nexthop</i> field is trans-
+ port dependent. In the case of SMTP, specify
+ <i>host</i>:<i>service</i> for a non-default server port, and use
+ [<i>host</i>] or [<i>host</i>:<i>port</i>] in order to disable MX (mail
+ exchanger) DNS lookups. The [] form can also be
+ used with IP addresses instead of hostnames.
+
+<b>EXAMPLES</b>
+ In order to send mail for <b>foo.org</b> and its subdomains
+ via the <b>uucp</b> transport to the UUCP host named <b>foo</b>:
+
+ <b>foo.org</b> <b>uucp:foo</b>
+ <b>.foo.org</b> <b>uucp:foo</b>
+
+ When no <i>nexthop</i> host name is specified, the destination domain
+ name is used instead. For example, the following directs mail for
+ <i>user</i>@<b>foo.org</b> via the <b>slow</b> transport to a mail
+ exchanger for <b>foo.org</b>. The <b>slow</b> transport could be
+ something that runs at most one delivery process at a time:
+
+ <b>foo.org</b> <b>slow:</b>
+
+
+
+
+ 1
+
+
+
+
+
+TRANSPORT(5) TRANSPORT(5)
+
+
+ When no <i>transport</i> is specified, the default transport is
+ used, as specified via the <b>default</b><i>_</i><b>transport</b> configuration
+ parameter. The following sends all mail for <b>foo.org</b> and its
+ subdomains to host <b>gateway.foo.org</b>:
+
+ <b>foo.org</b> <b>:[gateway.foo.org]</b>
+ <b>.foo.org</b> <b>:[gateway.foo.org]</b>
+
+ 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
+ <i>hostname</i>:<i>service</i> instead of just a host:
+
+ <b>foo.org</b> <b>smtp:bar.org:2025</b>
+
+ This directs mail for <i>user</i>@<b>foo.org</b> to host <b>bar.org</b>
+ port <b>2025</b>. Instead of a numerical port a symbolic name may be
+ used. Specify [] around the destination in order to disable MX lookups.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this topic. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
+ command after a configuration change.
+
+ <b>transport</b><i>_</i><b>maps</b>
+ List of transport lookup tables.
+
+ Other parameters of interest:
+
+ <b>default</b><i>_</i><b>transport</b>
+ The transport to use when no transport is explic-
+ itly specified.
+
+ <b>relayhost</b>
+ The default host to send to when no transport table
+ entry matches.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="postmap.1.html">postmap(1)</a> create mapping table
+ <a href="trivial-rewrite.8.html">trivial-rewrite(8)</a> rewrite and resolve addresses
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+ 2
+
+
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+TRIVIAL-REWRITE(8) TRIVIAL-REWRITE(8)
+
+
+<b>NAME</b>
+ trivial-rewrite - Postfix address rewriting and resolving
+ daemon
+
+<b>SYNOPSIS</b>
+ <b>trivial-rewrite</b> [generic Postfix daemon options]
+
+<b>DESCRIPTION</b>
+ The <b>trivial-rewrite</b> daemon processes two types of client
+ service requests:
+
+ <b>rewrite</b>
+ Rewrite an address to standard form. The <b>trivial-</b>
+ <b>rewrite</b> 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).
+
+ <b>resolve</b>
+ Resolve an address to a (<i>transport</i>, <i>nexthop</i>, <i>recip-</i>
+ <i>ient</i>) triple. The meaning of the results is as fol-
+ lows:
+
+ <i>transport</i>
+ The delivery agent to use. This is the first
+ field of an entry in the <b>master.cf</b> file.
+
+ <i>nexthop</i>
+ The host to send to. For local delivery this
+ is an empty string.
+
+ <i>recipient</i>
+ The envelope recipient address that is
+ passed on to <i>nexthop</i>.
+
+ The <b>trivial-rewrite</b> daemon by default only distin-
+ guishes between local and non-local mail. For finer
+ control over mail routing, use the optional <a href="transport.5.html"><b>trans-</b>
+ <b>port</b>(5)</a> lookup table.
+
+ This program expects to be run from the <a href="master.8.html"><b>master</b>(8)</a> process
+ manager.
+
+<b>STANDARDS</b>
+ None. The command does not interact with the outside
+ world.
+
+<b>SECURITY</b>
+ The <b>trivial-rewrite</b> 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)
+
+
+<b>DIAGNOSTICS</b>
+ Problems and transactions are logged to <b>syslogd</b>(8).
+
+<b>BUGS</b>
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
+ command after a configuration change.
+
+<b>Miscellaneous</b>
+ <b>inet</b><i>_</i><b>interfaces</b>
+ The network interfaces that this mail system
+ receives mail on. This information is used to
+ determine if <i>user</i>@[<i>net.work.addr.ess</i>] is local or
+ remote.
+
+ <b>mydestination</b>
+ List of domains that this machine considers local.
+
+ <b>myorigin</b>
+ The domain that locally-posted mail appears to come
+ from.
+
+<b>Rewriting</b>
+ <b>allow</b><i>_</i><b>percent</b><i>_</i><b>hack</b>
+ Rewrite <i>user</i>%<i>domain</i> to <i>user</i>@<i>domain</i>.
+
+ <b>append</b><i>_</i><b>at</b><i>_</i><b>myorigin</b>
+ Rewrite <i>user</i> to <i>user</i>@$<b>myorigin</b>.
+
+ <b>append</b><i>_</i><b>dot</b><i>_</i><b>mydomain</b>
+ Rewrite <i>user</i>@<i>host</i> to <i>user</i>@<i>host</i>.$<b>mydomain</b>.
+
+ <b>swap</b><i>_</i><b>bangpath</b>
+ Rewrite <i>site</i>!<i>user</i> to <i>user</i>@<i>site</i>.
+
+<b>Routing</b>
+ <b>default</b><i>_</i><b>transport</b>
+ The default transport to use when no transport is
+ explicitly given in the <a href="transport.5.html"><b>transport</b>(5)</a> table.
+
+ <b>relayhost</b>
+ The default host to send mail to when no entry is
+ matched in the <a href="transport.5.html"><b>transport</b>(5)</a> table.
+
+ When no <b>relayhost</b> is specified, mail is routed
+ directly to the destination's mail exchanger.
+
+ <b>transport</b><i>_</i><b>maps</b>
+ List of tables with <i>domain</i> to (<i>transport,</i> <i>nexthop</i>)
+ mappings.
+
+
+
+
+
+ 2
+
+
+
+
+
+TRIVIAL-REWRITE(8) TRIVIAL-REWRITE(8)
+
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="master.8.html">master(8)</a> process manager
+ syslogd(8) system logging
+ <a href="transport.5.html">transport(5)</a> transport table format
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+
+
+</pre> </body> </html>
--- /dev/null
+<html>
+
+<head>
+
+<title> Postfix Configuration - UCE Controls</title>
+
+</head>
+
+<body>
+
+<h1><a href="big-picture.html"><img src="small-picture.gif" width="115" height="45"></a>
+Postfix Configuration - UCE Controls</h1>
+
+<hr>
+
+<a href="config.html">Up one level</a> | <a href="basic.html">Basic
+Configuration</a> | UCE Controls | <a href="rate.html">Rate
+Controls</a> | <a href="resource.html">Resource Controls</a> | <a
+href="rewrite.html">Address Manipulation </a>
+
+<h2> Introduction</h2>
+
+Postfix offers a variety of parameters that limit the delivery of
+unsolicited commercial email (UCE).
+
+<p>
+
+By default, the Postfix <a href="smtpd.8.html">SMTP server</a> 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.
+
+<p>
+
+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 <b>access</b> lists or with <b>RBL</b>
+(real-time blackhole list) name servers.
+
+<p> Unless indicated otherwise, all parameters described here are
+in the <b>main.cf</b> file. If you change parameters of a running
+Postfix system, don't forget to issue a <b>postfix reload</b>
+command.
+
+<ul>
+
+<li> <a href="#smtpd_client_restrictions">Client name/address
+restrictions</a>
+
+<p>
+
+<li> <a href="#smtpd_helo_required">Require HELO (EHLO) command </a>
+
+<p>
+
+<li> <a href="#smtpd_helo_restrictions">HELO (EHLO) hostname
+restrictions</a>
+
+<p>
+
+<li> <a href="#smtpd_sender_restrictions">Sender address restrictions
+</a>
+
+<p>
+
+<li> <a href="#smtpd_recipient_restrictions">Recipient address
+restrictions</a>
+
+<p>
+
+<li> <a href="#generic">Generic restrictions</a>
+
+<p>
+
+<li> <a href="#additional">Additional UCE control parameters</a>
+
+</ul>
+
+<a name="smtpd_client_restrictions">
+
+<h2> Client name/address restrictions</h2>
+
+The <b>smtpd_client_restrictions</b> parameter restricts what
+clients this system accepts SMTP connections from.
+
+<p>
+
+<dl>
+
+<dt>Default:
+
+<dd>Allow SMTP connections from any client.
+
+<p>
+
+<dt>Syntax:
+
+<dd>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.
+
+<p>
+
+<dt>Examples:
+
+<dd> <b>smtpd_client_restrictions = hash:/etc/postfix/access,
+reject_maps_rbl</b>
+
+<dd> <b>smtpd_client_restrictions = permit_mynetworks,
+reject_unknown_client</b>
+
+<p>
+
+<dt> Restrictions:
+
+<p>
+
+<dl>
+
+<a name="reject_unknown_client">
+
+<dt> <b>reject_unknown_client</b> <dd> Reject the request when the
+client address to name lookup failed. The
+<b>unknown_client_reject_code</b> parameter specifies the response
+code to rejected requests (default: <b>450</b>).
+
+<p>
+
+<a name="permit_mynetworks">
+
+<dt> <b>permit_mynetworks</b> <dd> Permit the request when the
+client address matches any network listed in <a
+href="basic.html#mynetworks"> $mynetworks</a>.
+
+<p>
+
+<a name="check_client_access">
+
+<dt> <b>check_client_access</b> <i>maptype</i>:<i>mapname</i>
+
+<dt> <i>maptype</i>:<i>mapname</i> <dd> Search the named <a
+href="access.5.html">access database</a> for the client name, parent
+domains, client address, or networks obtained by stripping least
+significant octets. Reject the request if the result is <b>REJECT</b>
+or "[<b>45</b>]<i>XX text</i>". Permit the request if the result
+is anything else. The <b>access_map_reject_code</b> parameter
+specifies the response code for <b>REJECT</b> results (default:
+<b>550</b>).
+
+<p>
+
+<a name="reject_maps_rbl">
+
+<dt> <b>reject_maps_rbl</b> <dd> Reject the request when the client
+network address is listed under any of the domains listed in <a
+href="#maps_rbl_domains">$maps_rbl_domains</a>. The <b>
+maps_rbl_reject_code</b> parameter specifies the response code for
+rejected requests (default: <b>550</b>).
+
+<p>
+
+<dt> <b><a href="#permit">permit</a></b>
+
+<dt> <b><a href="#reject">reject</a></b>
+
+<dd> See generic restrictions.
+
+</dl>
+
+</dl>
+
+<a name="smtpd_helo_required">
+
+<h2> Require HELO (EHLO) command</h2>
+
+The <b>smtpd_helo_required</b> parameter determines if clients must
+send a <b>HELO</b> (<b>EHLO</b>) command at the beginning of an
+SMTP session. Requiring this will stop some UCE software.
+
+<p>
+
+<dl>
+
+<dt>Default:
+
+<dd>By default, the Postfix <a href="smtpd.8.html">SMTP server</a>
+does not require the use of <b>HELO</b> (<b>EHLO</b>).
+
+<p>
+
+<dt>Syntax:
+
+<dd>Specify <b>yes</b> or <b>no</b>.
+
+<p>
+
+<dt>Example:
+
+<dd> <b>smtpd_helo_required = yes</b>
+
+</dl>
+
+<a name="smtpd_helo_restrictions">
+
+<h2> HELO (EHLO) hostname restrictions</h2>
+
+The <b>smtpd_helo_restrictions</b> parameter restricts what hostnames
+clients may send with the <b>HELO</b> (<b>EHLO</b>) command. Some
+UCE software can be stopped by being strict here.
+
+<dl>
+
+<dt>Default:
+
+<dd>By default, the Postfix <a href="smtpd.8.html">SMTP server</a>
+accepts any hostname.
+
+<p>
+
+<dt>Syntax:
+
+<dd>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.
+
+<p>
+
+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.
+
+<p>
+
+<dt>Example:
+
+<dd> <b>smtpd_helo_restrictions = reject_invalid_hostname</b>
+
+<p>
+
+<dt> Restrictions:
+
+<p>
+
+<dl>
+
+<a name="reject_invalid_hostname">
+
+<dt> <b>reject_invalid_hostname</b> <dd> Reject the request when
+the client HELO and EHLO command has a bad hostname syntax. The
+<b>invalid_hostname_reject_code</b> specifies the response code to
+rejected requests (default: 501).
+
+<p>
+
+<a name="permit_naked_ip_address">
+
+<dt> <b>permit_naked_ip_address</b> <dd> Permit the request when
+the client HELO (EHLO) command contains a naked IP address without
+the enclosing <b>[]</b> brackets that the RFC requires. Unfortunately,
+some popular PC mail clients send <b>HELO</b> greetings in this
+manner.
+
+<p>
+
+<a name="reject_unknown_hostname">
+
+<dt> <b>reject_unknown_hostname</b> <dd> Reject the request when
+the hostname in the client HELO (EHLO) command has no DNS A or MX
+record. The <b>unknown_hostname_reject_code</b> specifies the
+response code to rejected requests (default: <b>450</b>).
+
+<p>
+
+<a name="check_helo_access">
+
+<dt> <b>check_helo_access</b> <i>maptype</i>:<i>mapname</i>
+
+<dt> <i>maptype</i>:<i>mapname</i> <dd> Search the named <a
+href="access.5.html">access database</a> for the <b>HELO</b> hostname
+or parent domains in the specified table. Reject the request if
+the result is <b>REJECT</b> or "[<b>45</b>]<i>XX text</i>". Permit
+the request when the result is anything else. The
+<b>access_map_reject_code </b> parameter specifies the response
+code for <b>REJECT</b> results (default: <b>550</b>).
+
+<p>
+
+<dt> <b><a href="#reject_unknown_client">reject_unknown_client</a></b>
+
+<dt> <b><a href="#permit_mynetworks">permit_mynetworks</a></b>
+
+<dt> <b><a href="#check_client_access">check_client_access</a></b> <i>maptype</i>:<i>mapname</i>
+
+<dd> See client name/address restrictions.
+
+<p>
+
+<dt> <b><a href="#permit">permit</a></b>
+
+<dt> <b><a href="#reject">reject</a></b>
+
+<dd> See generic restrictions.
+
+</dl>
+
+</dl>
+
+<a name="smtpd_sender_restrictions">
+
+<h2> Sender address restrictions</h2>
+
+The <b>smtpd_sender_restrictions</b> parameter restricts what sender
+addresses this system accepts in MAIL FROM commands.
+
+<p>
+
+<dl>
+
+<dt> Default:
+
+<dd>By default, the Postfix <a href="smtpd.8.html">SMTP server</a>
+accepts any sender address.
+
+<p>
+
+<dt>Syntax:
+
+<dd>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.
+
+<p>
+
+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.
+
+<p>
+
+<dt> Example:
+
+<dd> <b>smtpd_sender_restrictions = reject_unknown_address</b>
+
+<p>
+
+<dt> Restrictions:
+
+<dl compact>
+
+<a name="reject_unknown_address">
+
+<dt> <b>reject_unknown_address</b> <dd> Reject the request when
+the sender mail address has no DNS A or MX record. The
+<b>unknown_address_reject_code </b> parameter specifies the response
+code for rejected requests (default: <b>450</b>).
+
+<p>
+
+<a name="check_sender_access">
+
+<dt> <b>check_sender_access</b> <i>maptype</i>:<i>mapname</i>
+
+<dt> <i>maptype</i>:<i>mapname</i> <dd> Search the named <a
+href="access.5.html">access database</a> for the sender mail address,
+parent domain, or <i>localpart</i>@. Reject the request if the
+result is <b>REJECT</b> or "[<b>45</b>]<i>XX text</i>". Permit the
+request if the result is anything else. The <b>access_map_reject_code
+</b> parameter specifies the result code for rejected requests
+(default: <b>550</b>).
+
+<p>
+
+<dt> <b><a href="#permit_naked_ip_address">permit_naked_ip_address</a></b>
+
+<dt> <b><a href="#reject_invalid_hostname">reject_invalid_hostname</a></b>
+
+<dt> <b><a href="#reject_unknown_hostname">reject_unknown_hostname</a></b>
+
+<dt> <b><a href="#check_helo_access">check_helo_access</a></b> <i>maptype</i>:<i>mapname</i>
+
+<dd> See HELO (EHLO) hostname restrictions.
+
+<p>
+
+<dt> <b><a href="#reject_unknown_client">reject_unknown_client</a></b>
+
+<dt> <b><a href="#permit_mynetworks">permit_mynetworks</a></b>
+
+<dt> <b><a href="#check_client_access">check_client_access</a></b> <i>maptype</i>:<i>mapname</i>
+
+<dd> See client name/address restrictions.
+
+<p>
+
+<dt> <b><a href="#permit">permit</a></b>
+
+<dt> <b><a href="#reject">reject</a></b>
+
+<dd> See generic restrictions.
+
+</dl>
+
+</dl>
+
+<a name="smtpd_recipient_restrictions">
+
+<h2> Recipient address restrictions</h2>
+
+The <b>smtpd_recipient_restrictions</b> parameter restricts what
+recipient addresses this system accepts in RCPT TO commands.
+
+<dl>
+
+<dt>Default:
+
+<dd>By default, the Postfix <a href="smtpd.8.html">SMTP server</a>
+forwards mail from any client that matches <a
+href="basic.html#mynetworks">$mynetworks</a> or <a
+href="#relay_domains">$relay_domains</a>, or to any destination
+that matches <a href="#relay_domains"> $relay_domains</a>.
+
+<p>
+
+<dt>Syntax:
+
+<dd>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.
+
+<p>
+
+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.
+
+<p>
+
+<dt> Example:
+
+<dd> <b>smtpd_recipient_restrictions = permit_mynetworks,
+check_relay_domains</b>
+
+<p>
+
+<dt> Restrictions:
+
+<dl>
+
+<a name="check_relay_domains">
+
+<dt> <b>check_relay_domains</b> <dd> Permit the request when the
+client hostname matches <a href="#relay_domains">$relay_domains</a>,
+or when the resolved destination address matches <a href="#relay_domains">
+$relay_domains</a>, otherwise reject. The <b>relay_domains_reject_code</b>
+parameter specifies the response code for rejected requests (default:
+<b>550</b>).
+
+<p>
+
+<a name="permit_mx_backup">
+
+<dt> <b>permit_mx_backup</b> <dd> 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: <a href="basic.html#mydestination">
+$mydestination</a>, <a href="basic.html#inet_interfaces">
+$inet_interfaces</a>.
+
+<p>
+
+<dt> <b>check_recipient_access</b> <i>maptype</i>:<i>mapname</i>
+
+<dt> <i>maptype</i>:<i>mapname</i> <dd> Search the named <a
+href="access.5.html">access database</a> for the resolved destination
+address, parent domain, or <i>localpart</i>@. Reject the request
+if the result is <b>REJECT</b> or "[<b>45</b>]<i>XX text</i>".
+Permit the request if the result is anything else. The
+<b>access_map_reject_code </b> parameter specifies the result code
+for rejected requests (default: <b>550</b>).
+
+<p>
+
+<dt> <b><a href="#reject_unknown_address">reject_unknown_address</a></b>
+
+<dt> <b><a href="#check_sender_access">check_sender_access</a></b> <i>maptype</i>:<i>mapname</i>
+
+<dd> See sender address restrictions.
+
+<p>
+
+<dt> <b><a href="#permit_naked_ip_address">permit_naked_ip_address</a></b>
+
+<dt> <b><a href="#reject_invalid_hostname">reject_invalid_hostname</a></b>
+
+<dt> <b><a href="#reject_unknown_hostname">reject_unknown_hostname</a></b>
+
+<dt> <b><a href="#check_helo_access">check_helo_access</a></b> <i>maptype</i>:<i>mapname</i>
+
+<dd> See HELO (EHLO) hostname restrictions.
+
+<p>
+
+<dt> <b><a href="#reject_unknown_client">reject_unknown_client</a></b>
+
+<dt> <b><a href="#permit_mynetworks">permit_mynetworks</a></b>
+
+<dt> <b><a href="#check_client_access">check_client_access</a></b> <i>maptype</i>:<i>mapname</i>
+
+<dd> See client name/address restrictions.
+
+<p>
+
+<dt> <b><a href="#permit">permit</a></b>
+
+<dt> <b><a href="#reject">reject</a></b>
+
+<dd> See generic restrictions.
+
+</dl>
+
+</dl>
+
+<a name="generic">
+
+<h2> Generic restrictions</h2>
+
+The following restrictions can use used for client hostnames or
+addresses, for HELO (EHLO) hostnames, for sender mail addresses
+and for recipient mail addresses.
+
+<dl>
+
+Restrictions:
+
+<p>
+
+<dl>
+
+<a name="permit">
+
+<dt> <b>permit</b> <dd> Permit the request. This restriction
+is useful at the end of a restriction list, to make the default
+policy explicit.
+
+<p>
+
+<a name="reject">
+
+<dt> <b>reject</b> <dd> Reject the request. This restriction
+is useful at the end of a restriction list, to make the default
+policy explicit. The <b>reject_code</b> configuration parameter
+specifies the response code to rejected requests (default:
+<b>550</b>).
+
+</dl>
+
+</dl>
+
+<a name="additional">
+
+<h2> Additional UCE control parameters</h2>
+
+<dl>
+
+<a name="maps_rbl_domains">
+
+<dt> <b>maps_rbl_domains</b>
+
+<dd>This parameter controls the behavior of the <a
+href="#reject_maps_rbl">reject_maps_rbl</a> restriction that can
+appear as part of a client name/address restriction list.
+
+<p>
+
+<dl>
+
+<dt>Default:
+
+<dd><b>maps_rbl_domains = rbl.maps.vix.com</b>
+
+<p>
+
+Note: RBL lookups are disabled by default.
+
+<p>
+
+<dt>Syntax:
+
+<dd> 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 <b>$maps_rbl_domains.</b>
+
+</dl>
+
+<p>
+
+<a name="relay_domains">
+
+<dt> <b>relay_domains</b>
+
+<dd> This parameter controls the behavior of the <a
+href="#check_relay_domains"> check_relay_domains</a> restriction
+that can appear as part of a recipient address restriction list.
+
+<p>
+
+<dl>
+
+<dt>Default:
+
+<dd><b>relay_domains = <a href="basic.html#mydestination">
+$mydestination</a>, <a href="rewrite.html#virtual">$virtual_maps</a>.</b>
+
+<p>
+
+<dt>Syntax:
+
+<dd> Specify zero or more domain names, <i>/file/name</i> patterns
+and/or <i>type</i>:<i>name</i> lookup tables, separated by whitespace
+and/or commas. A <i>/file/name</i> is replaced by its contents;
+<i>type</i>:<i>name</i> requests that table lookup is done instead
+of string comparison.
+
+</dl>
+
+<p>
+
+A host or destination address matches <b>$relay_domains</b> when
+its name or parent domain matches any of the names, files or lookup
+tables listed in <b>$relay_domains.</b>
+
+</dl>
+
+<hr>
+
+<a href="config.html">Up one level</a> | <a href="basic.html">Basic
+Configuration</a> | UCE Controls | <a href="rate.html">Rate
+Controls</a> | <a href="resource.html">Resource Controls</a> | <a
+href="rewrite.html">Address Manipulation </a>
+
+</body>
+
+</html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+
+
+
+VIRTUAL(5) VIRTUAL(5)
+
+
+<b>NAME</b>
+ virtual - format of Postfix virtual table
+
+<b>SYNOPSIS</b>
+ <b>postmap</b> <b>/etc/postfix/virtual</b>
+
+<b>DESCRIPTION</b>
+ The optional <b>virtual</b> table specifies redirections for
+ local and non-local recipients or domains. The redirec-
+ tions are used by the <a href="cleanup.8.html"><b>cleanup</b>(8)</a> daemon. The redirections
+ are recursive.
+
+ The <b>virtual</b> redirection is applied only to the recipient
+ envelope address, and does not affect message headers.
+ Think Sendmail rule set <b>S0</b>, if you like. Use <a href="canonical.5.html"><b>canonical</b>(5)</a>
+ mapping to rewrite header and envelope addresses in gen-
+ eral.
+
+ The file serves as input to the <a href="postmap.1.html"><b>postmap</b>(1)</a> command. The
+ result, an indexed file in <b>dbm</b> or <b>db</b> format, is used for
+ fast searching by the mail system. After an update it may
+ take a minute or so before the change becomes visible.
+ Issue a <b>postfix</b> <b>reload</b> command to eliminate the delay.
+
+ Typical support for a virtual domain looks like the fol-
+ lowing:
+
+ <i>virtual.domain</i> <i>anything</i> (right-hand content does not matter)
+ <i>user1@virtual.domain</i> <i>address1</i>
+ <i>user2@virtual.domain</i> <i>address2,</i> <i>address3</i>
+
+ With this, the SMTP server accepts mail for <i>virtual.domain</i>
+ (provided that the <b>relay</b><i>_</i><b>domains</b> parameter includes $<b>vir-</b>
+ <b>tual</b><i>_</i><b>maps</b>), and mail for <i>unknown</i>@<i>virtual.domain</i> 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 `#'.
+
+ <i>user</i>@<i>domain</i> <i>address,</i> <i>address,</i> <i>...</i>
+ Mail for <i>user</i>@<i>domain</i> is redirected to <i>address</i>.
+ This form has the highest precedence.
+
+ <i>user</i> <i>address,</i> <i>address,</i> <i>...</i>
+ Mail for <i>user</i>@<i>site</i> is redirected to <i>address</i> when
+ <i>site</i> is equal to $<b>myorigin</b>, when <i>site</i> is listed in
+ $mydestination, or when it is listed in
+ $<i>inet_interfaces</i>.
+
+ This functionality overlaps with functionality of
+
+
+
+ 1
+
+
+
+
+
+VIRTUAL(5) VIRTUAL(5)
+
+
+ the local <i>alias</i>(5) database. The difference is that
+ <b>virtual</b> mapping can be applied to non-local
+ addresses.
+
+ @<i>domain</i> <i>address,</i> <i>address,</i> <i>...</i>
+ Mail for any user in <i>domain</i> is redirected to
+ <i>address</i>. This form has the lowest precedence.
+
+ In all the above forms, when <i>address</i> has the form @<i>other-</i>
+ <i>domain</i>, the result is the same user in <i>otherdomain</i>. This
+ works for the first address in the expansion only.
+
+<b>ADDRESS</b> <b>EXTENSION</b>
+ When the search fails, and the address localpart contains
+ the optional recipient delimiter (e.g., <i>user+foo</i>@<i>domain</i>),
+ the search is repeated for the unextended address (e.g.
+ <i>user</i>@<i>domain</i>), and the unmatched address extension is prop-
+ agated to the result of expansion. The matching order is:
+ <i>user+foo</i>@<i>domain</i>, <i>user</i>@<i>domain</i>, <i>user+foo</i>, <i>user</i>, and @<i>domain</i>.
+
+<b>BUGS</b>
+ The table format does not understand quoting conventions.
+
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this topic. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
+ command after a configuration change.
+
+ <b>virtual</b><i>_</i><b>maps</b>
+ List of virtual mapping tables.
+
+ Other parameters of interest:
+
+ <b>inet</b><i>_</i><b>interfaces</b>
+ The network interface addresses that this system
+ receives mail on.
+
+ <b>mydestination</b>
+ List of domains that this mail system considers
+ local.
+
+ <b>myorigin</b>
+ The domain that is appended to locally-posted mail.
+
+<b>SEE</b> <b>ALSO</b>
+ <a href="cleanup.8.html">cleanup(8)</a> canonicalize and enqueue mail
+ <a href="postmap.1.html">postmap(1)</a> create mapping table
+
+<b>LICENSE</b>
+ The Secure Mailer license must be distributed with this
+ software.
+
+
+
+
+
+ 2
+
+
+
+
+
+VIRTUAL(5) VIRTUAL(5)
+
+
+<b>AUTHOR(S)</b>
+ Wietse Venema
+ IBM T.J. Watson Research
+ P.O. Box 704
+ Yorktown Heights, NY 10598, USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+
+
+</pre> </body> </html>
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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.
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <dict.h>
+#include <argv.h>
+#include <stringops.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_addr.h>
+#include <sent.h>
+#include <defer.h>
+#include <maps.h>
+#include <bounce.h>
+#include <mypwd.h>
+#include <canon_addr.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <argv.h>
+
+/* Global library. */
+
+#include <defer.h>
+#include <bounce.h>
+#include <sent.h>
+#include <been_here.h>
+#include <mail_params.h>
+#include <pipe_command.h>
+#include <mail_copy.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <record.h>
+#include <rec_type.h>
+#include <is_header.h>
+#include <quote_822_local.h>
+#include <header_opts.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef USE_PATHS_H
+#include <paths.h>
+#endif
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <htable.h>
+#include <open_as.h>
+#include <lstat_as.h>
+#include <iostuff.h>
+#include <stringops.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <mypwd.h>
+#include <bounce.h>
+#include <been_here.h>
+#include <mail_params.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <stringops.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <deliver_flock.h>
+#include <open_as.h>
+
+/* Global library. */
+
+#include <mail_copy.h>
+#include <bounce.h>
+#include <defer.h>
+#include <sent.h>
+#include <been_here.h>
+#include <mail_params.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <time.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <argv.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <iostuff.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <mail_queue.h>
+#include <cleanup_user.h>
+#include <sent.h>
+#include <record.h>
+#include <rec_type.h>
+#include <mark_corrupt.h>
+#include <mail_date.h>
+#include <mail_params.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <open_as.h>
+#include <stat_as.h>
+#include <iostuff.h>
+#include <mypwd.h>
+
+/* Global library. */
+
+#include <bounce.h>
+#include <defer.h>
+#include <been_here.h>
+#include <mail_params.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <bounce.h>
+#include <defer.h>
+#include <been_here.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <iostuff.h>
+#include <name_mask.h>
+#include <set_eugid.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <recipient_list.h>
+#include <deliver_request.h>
+#include <deliver_completed.h>
+#include <mail_params.h>
+#include <mail_addr.h>
+#include <config.h>
+#include <been_here.h>
+
+/* Single server skeleton. */
+
+#include <mail_server.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* local 3h
+/* SUMMARY
+/* local mail delivery
+/* SYNOPSIS
+/* #include "local.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <htable.h>
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+ * Global library.
+ */
+#include <been_here.h>
+#include <tok822.h>
+
+ /*
+ * 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
+/*--*/
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef USE_PATHS_H
+#include <paths.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <set_eugid.h>
+
+/* Global library. */
+
+#include <mail_copy.h>
+#include <safe_open.h>
+#include <deliver_flock.h>
+#ifdef USE_DOT_LOCK
+#include <dot_lockfile.h>
+#endif
+#include <bounce.h>
+#include <defer.h>
+#include <sent.h>
+#include <mypwd.h>
+#include <been_here.h>
+#include <mail_params.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <unistd.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <make_dirs.h>
+#include <set_eugid.h>
+#include <get_hostname.h>
+
+/* Global library. */
+
+#include <mail_copy.h>
+#include <bounce.h>
+#include <sent.h>
+#include <mail_params.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <split_at.h>
+#include <stringops.h>
+#include <dict.h>
+
+/* Global library. */
+
+#include <bounce.h>
+#include <defer.h>
+#include <mail_params.h>
+#include <split_addr.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <htable.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <resolve_clnt.h>
+#include <rewrite_clnt.h>
+#include <tok822.h>
+#include <mail_params.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <htable.h>
+#include <readline.h>
+#include <mymalloc.h>
+#include <vstring_vstream.h>
+
+/* Global library. */
+
+#include <tok822.h>
+#include <mail_params.h>
+
+/* 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);
+}
--- /dev/null
+#!/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='<db_185.h>'"
+ fi
+ if [ -f /usr/include/db/db.h ]; then
+ CCARGS="$CCARGS -DPATH_DB_H='<db/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' <<EOF
+SYSTYPE = $SYSTYPE
+AR = $AR
+ARFL = $ARFL
+RANLIB = $RANLIB
+SYSLIBS = $AUXLIBS $SYSLIBS
+CC = $CC $CCARGS
+OPT = $OPT
+DEBUG = $DEBUG
+EOF
--- /dev/null
+# For now, just hard-coded rules for daemons, commands, config files.
+
+DAEMONS = man8/bounce.8 man8/defer.8 man8/cleanup.8 man8/local.8 \
+ man8/master.8 man8/pickup.8 man8/pipe.8 man8/qmgr.8 man8/showq.8 \
+ man8/smtp.8 man8/smtpd.8 man8/trivial-rewrite.8
+COMMANDS= man1/postalias.1 man1/postcat.1 man1/postconf.1 man1/postfix.1 \
+ man1/postkick.1 man1/postlock.1 man1/postlog.1 man1/postdrop.1 \
+ man1/postmap.1 man1/sendmail.1 man1/mailq.1 man1/newaliases.1
+CONFIG = man5/access.5 man5/aliases.5 man5/canonical.5 man5/relocated.5 \
+ man5/transport.5 man5/virtual.5
+
+update: $(DAEMONS) $(COMMANDS) $(CONFIG)
+
+Makefile: Makefile.in
+ (set -e; echo "# DO NOT EDIT"; $(OPTS) sh ../makedefs; cat $?) >$@
+
+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 - $? >$@
--- /dev/null
+.so man1/sendmail.1
--- /dev/null
+.so man1/sendmail.1
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.so man8/bounce.8
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+.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
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* NAME
+/* mail_server 3h
+/* SUMMARY
+/* skeleton servers
+/* SYNOPSIS
+/* #include <mail_server.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+
+ /*
+ * 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
+/*--*/
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+
+/* Utility library. */
+
+#include <events.h>
+#include <msg.h>
+#include <msg_syslog.h>
+#include <vstring.h>
+#include <mymalloc.h>
+#include <iostuff.h>
+#include <vstream.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <debug_process.h>
+#include <mail_task.h>
+#include <config.h>
+#include <open_lock.h>
+
+/* 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 */
+ }
+ }
+}
--- /dev/null
+/*++
+/* 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
+/*--*/
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <events.h>
+#include <msg.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <argv.h>
+
+/* 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();
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility libraries. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <argv.h>
+#include <stringops.h>
+#include <readline.h>
+#include <inet_addr_list.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <mail_params.h>
+#include <own_inet_addr.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <listen.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <inet_addr_list.h>
+#include <set_eugid.h>
+#include <iostuff.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* 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;
+ }
+}
--- /dev/null
+/*++
+/* NAME
+/* master_proto 3
+/* SUMMARY
+/* Postfix master - status notification protocol
+/* SYNOPSIS
+/* #include <master_proto.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+
+/* 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);
+ }
+}
--- /dev/null
+/*++
+/* NAME
+/* master_proto 3h
+/* SUMMARY
+/* master process protocol
+/* SYNOPSIS
+/* #include <master_proto.h>
+/* 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
+/*--*/
+
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <signal.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <posix_signals.h>
+
+/* Application-specific. */
+
+#include "master.h"
+
+#ifdef USE_SIG_RETURN
+#include <sys/syscall.h>
+#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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h> /* closelog() */
+#include <signal.h>
+#include <stdarg.h>
+
+/* Utility libraries. */
+
+#include <msg.h>
+#include <binhash.h>
+#include <mymalloc.h>
+#include <events.h>
+#include <argv.h>
+#include <syslog.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <events.h>
+#include <binhash.h>
+#include <iostuff.h>
+
+/* 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;
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <stringops.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <config.h>
+#include <mail_params.h>
+
+/* 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);
+}
+
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <trigger.h>
+#include <events.h>
+
+/* Global library. */
+
+#include <mail_proto.h> /* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* multi_server 3
+/* SUMMARY
+/* skeleton multi-threaded mail subsystem
+/* SYNOPSIS
+/* #include <mail_server.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <signal.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <msg_syslog.h>
+#include <chroot_uid.h>
+#include <listen.h>
+#include <events.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+#include <mymalloc.h>
+#include <iostuff.h>
+#include <stringops.h>
+#include <sane_accept.h>
+
+/* Global library. */
+
+#include <mail_task.h>
+#include <debug_process.h>
+#include <mail_params.h>
+#include <config.h>
+#include <timed_ipc.h>
+#include <resolve_local.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* single_server 3
+/* SUMMARY
+/* skeleton single-threaded mail subsystem
+/* SYNOPSIS
+/* #include <mail_server.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <signal.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+/* Utility library. */
+
+#include <msg.h>
+#include <msg_syslog.h>
+#include <chroot_uid.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+#include <mymalloc.h>
+#include <events.h>
+#include <iostuff.h>
+#include <stringops.h>
+#include <sane_accept.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_task.h>
+#include <debug_process.h>
+#include <config.h>
+#include <timed_ipc.h>
+#include <resolve_local.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* trigger_server 3
+/* SUMMARY
+/* skeleton triggered mail subsystem
+/* SYNOPSIS
+/* #include <mail_server.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <signal.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+/* Utility library. */
+
+#include <msg.h>
+#include <msg_syslog.h>
+#include <chroot_uid.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+#include <mymalloc.h>
+#include <events.h>
+#include <iostuff.h>
+#include <stringops.h>
+#include <sane_accept.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_task.h>
+#include <debug_process.h>
+#include <config.h>
+#include <resolve_local.h>
+
+/* 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);
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <scan_dir.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <open_as.h>
+#include <set_eugid.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <mail_open_ok.h>
+#include <mymalloc.h>
+#include <mail_proto.h>
+#include <cleanup_user.h>
+#include <mail_date.h>
+#include <mail_params.h>
+#include <config.h>
+#include <record.h>
+#include <rec_type.h>
+
+/* Single-threaded server skeleton. */
+
+#include <mail_server.h>
+
+/* 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);
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <fcntl.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <argv.h>
+#include <htable.h>
+#include <dict.h>
+#include <iostuff.h>
+#include <mymalloc.h>
+#include <mac_parse.h>
+#include <set_eugid.h>
+#include <split_at.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <recipient_list.h>
+#include <deliver_request.h>
+#include <mail_queue.h>
+#include <mail_params.h>
+#include <config.h>
+#include <bounce.h>
+#include <defer.h>
+#include <deliver_completed.h>
+#include <sent.h>
+#include <pipe_command.h>
+#include <mail_copy.h>
+#include <mail_addr.h>
+#include <canon_addr.h>
+#include <split_addr.h>
+
+/* Single server skeleton. */
+
+#include <mail_server.h>
+
+/* 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);
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+#include <readline.h>
+#include <stringops.h>
+#include <split_at.h>
+
+/* Global library. */
+
+#include <tok822.h>
+#include <config.h>
+#include <mail_params.h>
+#include <mkmap.h>
+
+/* 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);
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <msg_vstream.h>
+#include <vstring_vstream.h>
+
+/* Global library. */
+
+#include <record.h>
+#include <rec_type.h>
+
+/* 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);
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+#!/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"
+ }
+}
+' $*
--- /dev/null
+ "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,
--- /dev/null
+char *var_local_destination_concurrency_limit;
+char *var_local_destination_recipient_limit;
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+#include <get_hostname.h>
+#include <stringops.h>
+#include <htable.h>
+#include <dict.h>
+#include <safe.h>
+#include <mymalloc.h>
+#include <line_wrap.h>
+
+/* Global library. */
+
+#include <mynetworks.h>
+#include <config.h>
+#include <mail_proto.h>
+#include <mail_version.h>
+#include <mail_params.h>
+#include <mail_addr.h>
+
+ /*
+ * 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);
+}
--- /dev/null
+ "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,
--- /dev/null
+char *var_smtp_destination_concurrency_limit;
+char *var_smtp_destination_recipient_limit;
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h> /* remove() */
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <syslog.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <msg_vstream.h>
+#include <msg_syslog.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <mail_queue.h>
+#include <mail_params.h>
+#include <config.h>
+#include <mail_task.h>
+#include <clean_env.h>
+#include <mail_stream.h>
+#include <cleanup_user.h>
+#include <record.h>
+#include <rec_type.h>
+
+/* 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);
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <vstream.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <syslog.h>
+#ifdef USE_PATHS_H
+#include <paths.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <msg_vstream.h>
+#include <msg_syslog.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <config.h>
+#include <mail_params.h>
+
+/* 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);
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+#include <safe.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <mail_params.h>
+#include <config.h>
+
+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);
+ }
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+#include <iostuff.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <dot_lockfile.h>
+#include <deliver_flock.h>
+#include <config.h>
+
+/* 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 */
+
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <msg_output.h>
+#include <msg_vstream.h>
+#include <msg_syslog.h>
+
+/* Global library. */
+
+#include <mail_params.h> /* 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);
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+#include <readline.h>
+#include <stringops.h>
+#include <split_at.h>
+
+/* Global library. */
+
+#include <config.h>
+#include <mail_params.h>
+#include <mkmap.h>
+
+/* 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);
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <events.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <recipient_list.h>
+#include <config.h>
+#include <mail_params.h>
+#include <mail_proto.h> /* QMGR_SCAN constants */
+
+/* Master process interface */
+
+#include <master_proto.h>
+#include <mail_server.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* qmgr 3h
+/* SUMMARY
+/* queue manager data structures
+/* SYNOPSIS
+/* #include "qmgr.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+
+ /*
+ * Global library.
+ */
+#include <maps.h>
+
+ /*
+ * 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
+/*--*/
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <utime.h>
+#include <errno.h>
+
+#ifndef S_IRWXU /* What? no POSIX system? */
+#define S_IRWXU 0700
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <events.h>
+#include <mymalloc.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_open_ok.h>
+#include <mail_queue.h>
+#include <recipient_list.h>
+#include <bounce.h>
+#include <defer.h>
+
+/* 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);
+ }
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <stdarg.h>
+
+/* Utility library. */
+
+/* Global library. */
+
+#include <bounce.h>
+#include <deliver_completed.h>
+
+/* 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;
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <defer.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <time.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <events.h>
+#include <iostuff.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <mail_proto.h>
+#include <recipient_list.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <stdlib.h>
+#include <time.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <events.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <split_at.h>
+#include <valid_hostname.h>
+#include <argv.h>
+
+/* Global library. */
+
+#include <dict.h>
+#include <mail_queue.h>
+#include <mail_params.h>
+#include <canon_addr.h>
+#include <record.h>
+#include <rec_type.h>
+#include <sent.h>
+#include <deliver_completed.h>
+#include <mail_addr_find.h>
+#include <opened.h>
+
+/* Client stubs. */
+
+#include <resolve_clnt.h>
+
+/* 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);
+ }
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <utime.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <scan_dir.h>
+#include <recipient_list.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <time.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <events.h>
+#include <htable.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <recipient_list.h>
+
+/* 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));
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <scan_dir.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <events.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <iostuff.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <recipient_list.h>
+#include <config.h>
+#include <mail_params.h>
+
+/* 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));
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h> /* remove() */
+#include <stdlib.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <time.h>
+#include <errno.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <msg_vstream.h>
+#include <msg_syslog.h>
+#include <vstring_vstream.h>
+#include <username.h>
+#include <fullname.h>
+#include <argv.h>
+#include <safe.h>
+#include <iostuff.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <mail_proto.h>
+#include <mail_params.h>
+#include <record.h>
+#include <rec_type.h>
+#include <rec_streamlf.h>
+#include <config.h>
+#include <cleanup_user.h>
+#include <mail_task.h>
+#include <mail_run.h>
+#include <debug_process.h>
+#include <tok822.h>
+#include <mail_flush.h>
+#include <mail_stream.h>
+
+/* 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 <address>".
+ *
+ * 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 */
+ }
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <string.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <scan_dir.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <stringops.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <mail_open_ok.h>
+#include <mail_proto.h>
+#include <mail_date.h>
+#include <mail_params.h>
+#include <config.h>
+#include <record.h>
+#include <rec_type.h>
+
+/* Single-threaded server skeleton. */
+
+#include <mail_server.h>
+
+/* 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);
+}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <string.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+
+/* 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 <stdlib.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#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
--- /dev/null
+/*++
+/* NAME
+/* quote_821_local 3h
+/* SUMMARY
+/* quote rfc 821 local part
+/* SYNOPSIS
+/* #include "quote_821_local.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * 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
+/*--*/
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <name_mask.h>
+
+/* Global library. */
+
+#include <deliver_request.h>
+#include <mail_queue.h>
+#include <mail_params.h>
+#include <config.h>
+#include <debug_peer.h>
+#include <mail_error.h>
+
+/* Single server skeleton. */
+
+#include <mail_server.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* smtp 3h
+/* SUMMARY
+/* smtp client program
+/* SYNOPSIS
+/* #include "smtp.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <vstring.h>
+#include <argv.h>
+
+ /*
+ * Global library.
+ */
+#include <deliver_request.h>
+
+ /*
+ * 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
+/*--*/
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <mymalloc.h>
+#include <inet_addr_list.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <own_inet_addr.h>
+
+/* DNS library. */
+
+#include <dns.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* smtp_addr 3h
+/* SUMMARY
+/* SMTP server address lookup
+/* SYNOPSIS
+/* #include "smtp_addr.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * DNS library.
+ */
+#include <dns.h>
+
+ /*
+ * 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
+/*--*/
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <setjmp.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <argv.h>
+#include <stringops.h>
+#include <line_wrap.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <recipient_list.h>
+#include <deliver_request.h>
+#include <smtp_stream.h>
+#include <mail_params.h>
+#include <mail_addr.h>
+#include <post_mail.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <split_at.h>
+#include <mymalloc.h>
+#include <inet_addr_list.h>
+#include <iostuff.h>
+#include <timed_connect.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <own_inet_addr.h>
+
+/* DNS library. */
+
+#include <dns.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <sys/socket.h> /* shutdown(2) */
+#include <setjmp.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <stringops.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <smtp_stream.h>
+#include <mail_queue.h>
+#include <recipient_list.h>
+#include <deliver_request.h>
+#include <deliver_completed.h>
+#include <defer.h>
+#include <bounce.h>
+#include <sent.h>
+#include <record.h>
+#include <rec_type.h>
+#include <off_cvt.h>
+#include <mark_corrupt.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <vstream.h>
+
+/* 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);
+}
+
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <config.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <setjmp.h>
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <stringops.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <smtp_stream.h>
+#include <deliver_request.h>
+#include <deliver_completed.h>
+#include <bounce.h>
+#include <defer.h>
+#include <mail_error.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <htable.h>
+#include <vstring.h>
+#include <msg.h>
+
+/* DNS library. */
+
+#include <dns.h>
+
+/* 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 <vstring_vstream.h>
+
+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
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdio.h> /* remove() */
+#include <setjmp.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+#include <ctype.h>
+#include <signal.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <stringops.h>
+#include <events.h>
+#include <smtp_stream.h>
+#include <peer_name.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <record.h>
+#include <rec_type.h>
+#include <mail_proto.h>
+#include <cleanup_user.h>
+#include <mail_date.h>
+#include <config.h>
+#include <off_cvt.h>
+#include <debug_peer.h>
+#include <mail_error.h>
+#include <mail_flush.h>
+#include <mail_stream.h>
+#include <mail_queue.h>
+
+/* Single-threaded server skeleton. */
+
+#include <mail_server.h>
+
+/* 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: <address>");
+ 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: <address>");
+ 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 <CR><LF>.<CR><LF>");
+
+ /*
+ * 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* smtpd 3h
+/* SUMMARY
+/* smtp server
+/* SYNOPSIS
+/* include "smtpd.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <vstring.h>
+#include <argv.h>
+
+ /*
+ * Global library.
+ */
+#include <mail_stream.h>
+
+ /*
+ * 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
+/*--*/
--- /dev/null
+/*++
+/* NAME
+/* smtpd_chat 3
+/* SUMMARY
+/* SMTP server request/response support
+/* SYNOPSIS
+/* #include <smtpd.h>
+/* #include <smtpd_chat.h>
+/*
+/* 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 <sys_defs.h>
+#include <setjmp.h>
+#include <unistd.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <argv.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <stringops.h>
+#include <line_wrap.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <smtp_stream.h>
+#include <record.h>
+#include <rec_type.h>
+#include <mail_proto.h>
+#include <mail_params.h>
+#include <mail_addr.h>
+#include <post_mail.h>
+#include <mail_error.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* smtpd_chat 3h
+/* SUMMARY
+/* SMTP server request/response support
+/* SYNOPSIS
+/* #include <smtpd.h>
+/* #include <smtpd_chat.h>
+/* 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
+/*--*/
+
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <netdb.h>
+#include <setjmp.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <split_at.h>
+#include <fsspace.h>
+#include <stringops.h>
+#include <valid_hostname.h>
+#include <argv.h>
+#include <mymalloc.h>
+#include <dict.h>
+
+/* DNS library. */
+
+#include <dns.h>
+
+/* Global library. */
+
+#include <namadr_list.h>
+#include <domain_list.h>
+#include <mail_params.h>
+#include <canon_addr.h>
+#include <resolve_clnt.h>
+#include <mail_error.h>
+#include <resolve_local.h>
+#include <own_inet_addr.h>
+
+/* 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 <unistd.h>
+#include <stdlib.h>
+
+#include <msg_vstream.h>
+#include <vstring_vstream.h>
+
+#include <config.h>
+
+#include <smtpd_chat.h>
+
+ /*
+ * 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 <name> <address>\n\
+ helo <hostname>\n\
+ sender <address>\n\
+ recipient <address>\n\
+ msg_verbose <level>\n\
+ client_restrictions <restrictions>\n\
+ helo_restrictions <restrictions>\n\
+ sender_restrictions <restrictions>\n\
+ recipient_restrictions <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
--- /dev/null
+/*++
+/* 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
+/*--*/
--- /dev/null
+#
+# 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
--- /dev/null
+#
+# 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
--- /dev/null
+>>> #
+>>> # 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 <foo.>: Host not found
+450 <foo.>: Host not found
+>>> helo foo
+./smtpd_check: reject: HELO from foo[123.123.123.123]: 450 <foo>: Host not found
+450 <foo>: 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 <bad.domain>: Domain not found
+450 <bad.domain>: 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 <foo@watson.ibm.com>: Relay access denied
+550 <foo@watson.ibm.com>: 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 <foo@watson.ibm.com>: Relay access denied
+550 <foo@watson.ibm.com>: 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 <foo@watson.ibm.com>: Relay access denied
+550 <foo@watson.ibm.com>: 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 <wietse@trouble.org> Access denied
+550 <wietse@trouble.org> Access denied
+>>> rcpt wietse@porcupine.org
+OK
--- /dev/null
+>>> #
+>>> # 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 <foo.>: Host not found
+450 <foo.>: Host not found
+>>> helo foo
+./smtpd_check: reject: HELO from foo[123.123.123.123]: 450 <foo>: Host not found
+450 <foo>: 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 <bad.domain>: Domain not found
+450 <bad.domain>: 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 <foo@watson.ibm.com>: Relay access denied
+550 <foo@watson.ibm.com>: 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 <foo@watson.ibm.com>: Relay access denied
+550 <foo@watson.ibm.com>: 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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <events.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <name_mask.h>
+
+/* Global library. */
+
+#include <cleanup_user.h>
+#include <mail_params.h>
+#include <mail_error.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* smtpd_token 3
+/* SUMMARY
+/* tokenize SMTPD command
+/* SYNOPSIS
+/* #include <smtpd_token.h>
+/*
+/* 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 <text>, 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 <sys_defs.h>
+#include <ctype.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <mvect.h>
+
+/* 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 == '<') { /* <stuff> */
+ 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 <stdlib.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+
+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
--- /dev/null
+/*++
+/* NAME
+/* smtpd_token 3h
+/* SUMMARY
+/* tokenize SMTPD command
+/* SYNOPSIS
+/* #include <smtpd_token.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * 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
+/*--*/
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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=<wietse@porcupine.org>, 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=<wietse@porcupine.org>, 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=<wietse@porcupine.org>, relay=l
+Total time: 25 seconds
--- /dev/null
+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]
+
--- /dev/null
+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=<foo@spike.porcupine.org>, 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=<foo@spike.porcupine.org>, 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=<foo@spike.porcupine.org>, relay
+Total time: 20 seconds
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <get_hostname.h>
+#include <listen.h>
+#include <events.h>
+#include <mymalloc.h>
+#include <iostuff.h>
+#include <msg_vstream.h>
+
+/* Global library. */
+
+#include <smtp_stream.h>
+
+/* 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 <CR><LF>.<CR><LF>");
+ 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);
+}
--- /dev/null
+/*++
+/* 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: <foo@myhostname>).
+/* .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: <foo@myhostname>).
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <get_hostname.h>
+#include <split_at.h>
+#include <connect.h>
+#include <mymalloc.h>
+#include <events.h>
+#include <find_inet.h>
+
+/* Global library. */
+
+#include <smtp_stream.h>
+#include <mail_date.h>
+
+/* 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);
+ }
+ }
+}
--- /dev/null
+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=<wietse@porcupine.org
+Elapsed 31
+
+fist% date; /usr/bin/time ./smtp-source -s 5 -m 100 -t wietse@nipple nipple
+Sat May 9 22:09:13 EDT 1998
+ 5.94 real 0.09 user 0.16 sys
+May 9 22:09:13 nipple wietse[100]: postfix/smtpd[2248]: connect from fist.porcu
+May 9 22:09:40 nipple postfix/local[2260]: 5082FBE00B: to=<wietse@porcupine.org
+Elapsed 27
+
+fist% date; /usr/bin/time ./smtp-source -s 5 -m 100 -t wietse@nipple nipple
+Sat May 9 22:09:43 EDT 1998
+ 5.97 real 0.06 user 0.17 sys
+May 9 22:09:43 nipple postfix/local[2321]: 5082C7E2F3: to=<wietse@porcupine.org
+May 9 22:10:13 nipple postfix/local[2517]: 5082C8772C: to=<wietse@porcupine.org
+Elapsed 30
+
+fist% date; /usr/bin/time ./smtp-source -s 20 -m 100 -t wietse@nipple nipple
+Sat May 9 22:10:30 EDT 1998
+ 6.26 real 0.08 user 0.20 sys
+May 9 22:10:30 nipple wietse[100]: postfix/smtpd[2248]: connect from fist.porcu
+May 9 22:10:58 nipple postfix/local[2330]: 5082C7A2C9: to=<wietse@porcupine.org
+Elapsed 28
+
+fist% date; /usr/bin/time ./smtp-source -s 20 -m 100 -t wietse@nipple nipple
+Sat May 9 22:11:06 EDT 1998
+ 6.04 real 0.05 user 0.25 sys
+May 9 22:11:06 nipple wietse[100]: postfix/smtpd[2675]: connect from fist.porcu
+May 9 22:11:33 nipple postfix/local[2685]: 5082D23842: to=<wietse@porcupine.org
+Elapsed 27
+
+fist% date; /usr/bin/time ./smtp-source -s 20 -m 100 -t wietse@nipple nipple
+Sat May 9 22:11:38 EDT 1998
+ 6.11 real 0.06 user 0.24 sys
+May 9 22:11:38 nipple postfix/local[2686]: 5082E318AD: to=<wietse@porcupine.org
+May 9 22:12:07 nipple postfix/local[2687]: 5082CBF5EC: to=<wietse@porcupine.org
+Elapsed 31
+
+Elapsed: 29, throughput: 3.4 msg/s (local_delivery_concurrency not set)
--- /dev/null
+fist% date; /usr/bin/time ./smtp-source -s 5 -m 100 nipple
+Sat May 9 21:54:48 EDT 1998
+ 7.09 real 0.03 user 0.18 sys
+May 9 21:54:48 nipple wietse[100]: postfix/smtpd[2206]: connect from fist.porcu
+May 9 21:55:05 nipple wietse[100]: postfix/smtp[2215]: 508672B24D: to=<foo@fist
+Elapsed 17
+
+fist% date; /usr/bin/time ./smtp-source -s 5 -m 100 nipple
+Sat May 9 21:55:09 EDT 1998
+ 6.02 real 0.09 user 0.13 sys
+May 9 21:55:09 nipple wietse[100]: postfix/smtpd[2206]: connect from fist.porcu
+May 9 21:55:23 nipple wietse[100]: postfix/smtp[2217]: 50864BCA59: to=<foo@fist
+Elapsed 14
+
+fist% date; /usr/bin/time ./smtp-source -s 5 -m 100 nipple
+Sat May 9 21:55:26 EDT 1998
+ 6.06 real 0.07 user 0.17 sys
+May 9 21:55:27 nipple wietse[100]: postfix/smtpd[2205]: connect from fist.porcu
+May 9 21:55:41 nipple wietse[100]: postfix/smtp[2218]: 508753CB5D: to=<foo@fist
+Elapsed 14
+
+fist% date; /usr/bin/time ./smtp-source -s 10 -m 100 nipple
+Sat May 9 21:55:53 EDT 1998
+ 6.01 real 0.13 user 0.14 sys
+May 9 21:55:53 nipple wietse[100]: postfix/smtpd[2206]: connect from fist.porcu
+May 9 21:56:08 nipple wietse[100]: postfix/smtp[2216]: 50868D8D44: to=<foo@fist
+Elapsed 15
+
+fist% date; /usr/bin/time ./smtp-source -s 10 -m 100 nipple
+Sat May 9 21:56:10 EDT 1998
+ 6.01 real 0.14 user 0.13 sys
+May 9 21:56:10 nipple wietse[100]: postfix/smtpd[2206]: connect from fist.porcu
+May 9 21:56:24 nipple wietse[100]: postfix/smtp[2229]: 50875825BA: to=<foo@fist
+Elapsed 14
+
+fist% date; /usr/bin/time ./smtp-source -s 10 -m 100 nipple
+Sat May 9 21:56:28 EDT 1998
+ 6.10 real 0.03 user 0.23 sys
+May 9 21:56:28 nipple wietse[100]: postfix/smtpd[2205]: connect from fist.porcu
+May 9 21:56:43 nipple wietse[100]: postfix/smtp[2229]: 508651D724: to=<foo@fist
+Elapsed 15
+
+fist% date; /usr/bin/time ./smtp-source -s 20 -m 100 nipple
+Sat May 9 21:56:58 EDT 1998
+ 6.02 real 0.10 user 0.21 sys
+May 9 21:56:58 nipple wietse[100]: postfix/smtpd[2222]: connect from fist.porcu
+May 9 21:57:13 nipple wietse[100]: postfix/smtp[2231]: 5085D279A7: to=<foo@fist
+Elapsed 15
+
+fist% date; /usr/bin/time ./smtp-source -s 20 -m 100 nipple
+Sat May 9 21:57:18 EDT 1998
+ 6.04 real 0.09 user 0.19 sys
+May 9 21:57:18 nipple wietse[100]: postfix/smtpd[2222]: connect from fist.porcu
+May 9 21:57:32 nipple wietse[100]: postfix/smtp[2230]: 508774A28A: to=<foo@fist
+Elapsed 14
+
+Elapsed: 14.4 s, throughput: 6.9 msg/s
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+SHELL = /bin/sh
+SRCS = trivial-rewrite.c rewrite.c resolve.c transport.c
+OBJS = trivial-rewrite.o rewrite.o resolve.o transport.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)
+LIB =
+TESTPROG=
+PROG = trivial-rewrite
+LIBS = ../lib/libmaster.a ../lib/libglobal.a ../lib/libutil.a
+LIB_DIR = ../lib
+INC_DIR = ../include
+BIN_DIR = ../bin
+
+.c.o:; $(CC) $(CFLAGS) -c $*.c
+
+all: $(PROG) $(LIB)
+
+$(PROG): $(OBJS) $(LIBS)
+ $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS)
+
+Makefile: Makefile.in
+ (set -e; echo "# DO NOT EDIT"; $(OPTS) sh ../makedefs; cat $?) >$@
+
+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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <split_at.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_proto.h>
+#include <mail_addr.h>
+#include <rewrite_clnt.h>
+#include <resolve_local.h>
+#include <config.h>
+#include <quote_822_local.h>
+#include <tok822.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <split_at.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_proto.h>
+#include <resolve_local.h>
+#include <tok822.h>
+#include <config.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <stringops.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <split_at.h>
+#include <dict.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <maps.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* transport 3h
+/* SUMMARY
+/* transport mapping
+/* SYNOPSIS
+/* #include "transport.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * 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
+/*--*/
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <split_at.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_proto.h>
+#include <resolve_local.h>
+#include <config.h>
+#include <resolve_clnt.h>
+#include <rewrite_clnt.h>
+#include <tok822.h>
+
+/* Multi server skeleton. */
+
+#include <mail_server.h>
+
+/* Application-specific. */
+
+#include <trivial-rewrite.h>
+#include <transport.h>
+
+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);
+}
--- /dev/null
+/*++
+/* NAME
+/* trivial-rewrite 3h
+/* SUMMARY
+/* mail address rewriter and resolver
+/* SYNOPSIS
+/* #include "trivial-rewrite.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+#include <vstream.h>
+
+ /*
+ * Global library.
+ */
+#include <tok822.h>
+
+ /*
+ * 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
+/*--*/
+
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/*++
+/* NAME
+/* argv 3
+/* SUMMARY
+/* string array utilities
+/* SYNOPSIS
+/* #include <argv.h>
+/*
+/* 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 <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+#include <string.h>
+
+/* 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;
+}
--- /dev/null
+#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
--- /dev/null
+/*++
+/* NAME
+/* argv_split 3
+/* SUMMARY
+/* string array utilities
+/* SYNOPSIS
+/* #include <argv.h>
+/*
+/* 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 <sys_defs.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* attr 3
+/* SUMMARY
+/* attribute list manager
+/* SYNOPSIS
+/* #include <htable.h>
+/* #include <attr.h>
+/*
+/* 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);
+}
+
--- /dev/null
+#ifndef _ATTR_H_INCLUDED_
+#define _ATTR_H_INCLUDED_
+
+/*++
+/* NAME
+/* attr 3h
+/* SUMMARY
+/* attribute list manager
+/* SYNOPSIS
+/* #include <attr.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* basename 3
+/* SUMMARY
+/* extract file basename
+/* SYNOPSIS
+/* #include <stringops.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+#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
--- /dev/null
+/*++
+/* NAME
+/* binhash 3
+/* SUMMARY
+/* hash table manager
+/* SYNOPSIS
+/* #include <binhash.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _BINHASH_H_INCLUDED_
+#define _BINHASH_H_INCLUDED_
+
+/*++
+/* NAME
+/* binhash 3h
+/* SUMMARY
+/* hash table manager
+/* SYNOPSIS
+/* #include <binhash.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* chroot_uid 3
+/* SUMMARY
+/* limit possible damage a process can do
+/* SYNOPSIS
+/* #include <chroot_uid.h>
+/*
+/* 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 <sys_defs.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <grp.h>
+
+/* 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)");
+}
--- /dev/null
+#ifndef _CHROOT_UID_H_INCLUDED_
+#define _CHROOT_UID_H_INCLUDED_
+
+/*++
+/* NAME
+/* chroot_uid 3h
+/* SUMMARY
+/* limit possible damage a process can do
+/* SYNOPSIS
+/* #include <chroot_uid.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* close_on_exec 3
+/* SUMMARY
+/* set/clear close-on-exec flag
+/* SYNOPSIS
+/* #include <iostuff.h>
+/*
+/* 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 <sys_defs.h>
+#include <fcntl.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* concatenate 3
+/* SUMMARY
+/* concatenate strings
+/* SYNOPSIS
+/* #include <stringops.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+#include <string.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _CONNECT_H_INCLUDED_
+#define _CONNECT_H_INCLUDED_
+
+/*++
+/* NAME
+/* connect 3h
+/* SUMMARY
+/* client interface file
+/* SYNOPSIS
+/* #include <connect.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <iostuff.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* dict 3
+/* SUMMARY
+/* dictionary manager
+/* SYNOPSIS
+/* #include <dict.h>
+/*
+/* 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 <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+
+/* 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));
+}
--- /dev/null
+#ifndef _DICT_H_INCLUDED_
+#define _DICT_H_INCLUDED_
+
+/*++
+/* NAME
+/* dict 3h
+/* SUMMARY
+/* dictionary manager
+/* SYNOPSIS
+/* #include <dict.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <fcntl.h>
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* dict_db 3
+/* SUMMARY
+/* dictionary manager interface to DB files
+/* SYNOPSIS
+/* #include <dict_db.h>
+/*
+/* 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 <limits.h>
+#ifdef PATH_DB_H
+#include PATH_DB_H
+#else
+#include <db.h>
+#endif
+#include <string.h>
+
+/* 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
--- /dev/null
+#ifndef _DICT_DB_H_INCLUDED_
+#define _DICT_DB_H_INCLUDED_
+
+/*++
+/* NAME
+/* dict_db 3h
+/* SUMMARY
+/* dictionary manager interface to DB files
+/* SYNOPSIS
+/* #include <dict_db.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <dict.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* dict_dbm 3
+/* SUMMARY
+/* dictionary manager interface to DBM files
+/* SYNOPSIS
+/* #include <dict_dbm.h>
+/*
+/* 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 <ndbm.h>
+#include <string.h>
+
+/* 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
--- /dev/null
+#ifndef _DICT_DBN_H_INCLUDED_
+#define _DICT_DBN_H_INCLUDED_
+
+/*++
+/* NAME
+/* dict_dbm 3h
+/* SUMMARY
+/* dictionary manager interface to DBM files
+/* SYNOPSIS
+/* #include <dict_dbm.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <dict.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* dict_env 3
+/* SUMMARY
+/* dictionary manager interface to environment variables
+/* SYNOPSIS
+/* #include <dict_env.h>
+/*
+/* 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 <stdio.h> /* sprintf() prototype */
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _DICT_ENV_H_INCLUDED_
+#define _DICT_ENV_H_INCLUDED_
+
+/*++
+/* NAME
+/* dict_env 3h
+/* SUMMARY
+/* dictionary manager interface to environment variables
+/* SYNOPSIS
+/* #include <dict_env.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <dict.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* dict_ht 3
+/* SUMMARY
+/* dictionary manager interface to hash tables
+/* SYNOPSIS
+/* #include <dict_ht.h>
+/*
+/* 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);
+}
--- /dev/null
+#ifndef _DICT_HT_H_INCLUDED_
+#define _DICT_HT_H_INCLUDED_
+
+/*++
+/* NAME
+/* dict_ht 3h
+/* SUMMARY
+/* dictionary manager interface to hash tables
+/* SYNOPSIS
+/* #include <dict_ht.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <htable.h>
+#include <dict.h>
+
+ /*
+ * 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
--- /dev/null
+ /*
+ * 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
--- /dev/null
+ /*
+ * The LDAP software is not bundled with IBM's public release. It will be
+ * made available as contributed software from http://www.postfix.org/
+ */
--- /dev/null
+ /*
+ * 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
--- /dev/null
+ /*
+ * The NetInfo software is not bundled with IBM's public release. It will be
+ * made available as contributed software from http://www.postfix.org/
+ */
--- /dev/null
+/*++
+/* NAME
+/* dict_nis 3
+/* SUMMARY
+/* dictionary manager interface to NIS maps
+/* SYNOPSIS
+/* #include <dict_nis.h>
+/*
+/* 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 <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+#ifdef HAS_NIS
+
+#include <rpcsvc/ypclnt.h>
+#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
--- /dev/null
+#ifndef _DIST_NIS_H_INCLUDED_
+#define _DIST_NIS_H_INCLUDED_
+
+/*++
+/* NAME
+/* dict_nis 3h
+/* SUMMARY
+/* dictionary manager interface to NIS maps
+/* SYNOPSIS
+/* #include <dict_nis.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <dict.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* dict_nisplus 3
+/* SUMMARY
+/* dictionary manager interface to NIS+ maps
+/* SYNOPSIS
+/* #include <dict_nisplus.h>
+/*
+/* 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 <stdio.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _DICT_NISPLUS_H_INCLUDED_
+#define _DICT_NISPLUS_H_INCLUDED_
+
+/*++
+/* NAME
+/* dict_nisplus 3h
+/* SUMMARY
+/* dictionary manager interface to NIS+ maps
+/* SYNOPSIS
+/* #include <dict_nisplus.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <dict.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* dict_open 3
+/* SUMMARY
+/* low-level dictionary interface
+/* SYNOPSIS
+/* #include <dict.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <argv.h>
+#include <mymalloc.h>
+#include <msg.h>
+#include <dict.h>
+#include <dict_env.h>
+#include <dict_dbm.h>
+#include <dict_db.h>
+#include <dict_nis.h>
+#include <dict_nisplus.h>
+#include <dict_ni.h>
+#include <dict_ldap.h>
+#include <stringops.h>
+#include <split_at.h>
+
+ /*
+ * 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 <stdlib.h>
+#include <fcntl.h>
+
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* dir_forest 3
+/* SUMMARY
+/* file name to directory forest
+/* SYNOPSIS
+/* #include <dir_forest.h>
+/*
+/* 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 <sys_defs.h>
+#include <ctype.h>
+
+/* 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));
+}
--- /dev/null
+#ifndef _DIR_FOREST_H_INCLUDED_
+#define _DIR_FOREST_H_INCLUDED_
+
+/*++
+/* NAME
+/* dir_forest 3h
+/* SUMMARY
+/* file name to directory forest
+/* SYNOPSIS
+/* #include <dir_forest.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* doze 3
+/* SUMMARY
+/* take a nap
+/* SYNOPSIS
+/* #include <iostuff.h>
+/*
+/* 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 <sys/time.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* 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 <stdlib.h>
+
+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
--- /dev/null
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* events 3
+/* SUMMARY
+/* event manager
+/* SYNOPSIS
+/* #include <events.h>
+/*
+/* 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 <sys/time.h> /* XXX: 44BSD uses bzero() */
+#include <time.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stddef.h> /* offsetof() */
+#include <string.h> /* bzero() prototype for 44BSD */
+
+#ifdef USE_SYS_SELECT_H
+#include <sys/select.h>
+#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 <stdio.h>
+#include <ctype.h>
+
+#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
--- /dev/null
+#ifndef _EVENTS_H_INCLUDED_
+#define _EVENTS_H_INCLUDED_
+
+/*++
+/* NAME
+/* events 3h
+/* SUMMARY
+/* event manager
+/* SYNOPSIS
+/* #include <events.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <time.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* exec_command 3
+/* SUMMARY
+/* execute command
+/* SYNOPSIS
+/* #include <exec_command.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <string.h>
+#ifdef USE_PATHS_H
+#include <paths.h>
+#endif
+#include <errno.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <argv.h>
+#include <exec_command.h>
+
+/* 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 <vstream.h>
+#include <msg_vstream.h>
+
+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
--- /dev/null
+#ifndef _EXEC_COMMAND_H_INCLUDED_
+#define _EXEC_COMMAND_H_INCLUDED_
+
+/*++
+/* NAME
+/* exec_command 3h
+/* SUMMARY
+/* execute command
+/* SYNOPSIS
+/* #include <exec_command.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* fifo_listen 3
+/* SUMMARY
+/* start fifo listener
+/* SYNOPSIS
+/* #include <listen.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys/stat.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+
+#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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#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;
+ }
+ }
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#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);
+}
--- /dev/null
+/*++
+/* NAME
+/* fifo_trigger 3
+/* SUMMARY
+/* wakeup fifo server
+/* SYNOPSIS
+/* #include <trigger.h>
+/*
+/* 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 <sys_defs.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+#include <trigger.h>
+
+/* 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 <signal.h>
+#include <stdlib.h>
+
+#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
--- /dev/null
+/*++
+/* NAME
+/* file_limit 3
+/* SUMMARY
+/* limit the file size
+/* SYNOPSIS
+/* #include <iostuff.h>
+/*
+/* 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 <sys_defs.h>
+#ifdef USE_ULIMIT
+#include <ulimit.h>
+#else
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <signal.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+#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 */
+}
--- /dev/null
+/*++
+/* NAME
+/* find_inet 3
+/* SUMMARY
+/* inet-domain name services
+/* SYNOPSIS
+/* #include <find_inet.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* 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);
+ }
+}
--- /dev/null
+#ifndef _FIND_INET_H_INCLUDED_
+#define _FIND_INET_H_INCLUDED_
+
+/*++
+/* NAME
+/* find_inet 3h
+/* SUMMARY
+/* inet-domain name services
+/* SYNOPSIS
+/* #include <find_inet.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* fsspace 3
+/* SUMMARY
+/* determine available file system space
+/* SYNOPSIS
+/* #include <fsspace.h>
+/*
+/* 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 <sys_defs.h>
+
+#if defined(STATFS_IN_SYS_MOUNT_H)
+#include <sys/param.h>
+#include <sys/mount.h>
+#elif defined(STATFS_IN_SYS_VFS_H)
+#include <sys/vfs.h>
+#elif defined(STATVFS_IN_SYS_STATVFS_H)
+#include <sys/statvfs.h>
+#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 <msg.h>
+#include <fsspace.h>
+
+/* 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 <vstream.h>
+
+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
--- /dev/null
+#ifndef _FSSPACE_H_INCLUDED_
+#define _FSSPACE_H_INCLUDED_
+
+/*++
+/* NAME
+/* fsspace 3h
+/* SUMMARY
+/* determine available file system space
+/* SYNOPSIS
+/* #include <fsspace.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* fullname 3
+/* SUMMARY
+/* lookup personal name of invoking user
+/* SYNOPSIS
+/* #include <fullname.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* 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 <stdio.h>
+
+int main(int unused_argc, char **unused_argv)
+{
+ const char *cp = fullname();
+
+ printf("%s\n", cp ? cp : "null!");
+}
+
+#endif
--- /dev/null
+#ifndef _FULLNAME_H_INCLUDED_
+#define _FULLNAME_H_INCLUDED_
+
+/*++
+/* NAME
+/* fullname 3h
+/* SUMMARY
+/* lookup personal name of invoking user
+/* SYNOPSIS
+/* #include <fullname.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* get_domainname 3
+/* SUMMARY
+/* network domain name lookup
+/* SYNOPSIS
+/* #include <get_domainname.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _GET_DOMAINNAME_H_INCLUDED_
+#define _GET_DOMAINNAME_H_INCLUDED_
+
+/*++
+/* NAME
+/* get_domainname 3h
+/* SUMMARY
+/* network domain name lookup
+/* SYNOPSIS
+/* #include <get_domainname.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* get_hostname 3
+/* SUMMARY
+/* network name lookup
+/* SYNOPSIS
+/* #include <get_hostname.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/param.h>
+#include <string.h>
+#include <unistd.h>
+
+#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);
+}
--- /dev/null
+#ifndef _GET_HOSTNAME_H_INCLUDED_
+#define _GET_HOSTNAME_H_INCLUDED_
+
+/*++
+/* NAME
+/* get_hostname 3h
+/* SUMMARY
+/* network name lookup
+/* SYNOPSIS
+/* #include <get_hostname.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* htable 3
+/* SUMMARY
+/* hash table manager
+/* SYNOPSIS
+/* #include <htable.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _HTABLE_H_INCLUDED_
+#define _HTABLE_H_INCLUDED_
+
+/*++
+/* NAME
+/* htable 3h
+/* SUMMARY
+/* hash table manager
+/* SYNOPSIS
+/* #include <htable.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* inet_addr_host 3
+/* SUMMARY
+/* determine all host internet interface addresses
+/* SYNOPSIS
+/* #include <inet_addr_host.h>
+/*
+/* 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 <sys_defs.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+/* Utility library. */
+
+#include <inet_addr_list.h>
+#include <inet_addr_host.h>
+
+/* 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 <msg.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+
+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
--- /dev/null
+#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 <inet_addr_host.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <inet_addr_list.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* inet_addr_list 3
+/* SUMMARY
+/* internet address list manager
+/* SYNOPSIS
+/* #include <inet_addr_list.h>
+/*
+/* 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 <sys_defs.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <inet_addr_list.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _INET_ADDR_LIST_H_INCLUDED_
+#define _INET_ADDR_LIST_H_INCLUDED_
+
+/*++
+/* NAME
+/* inet_addr_list 3h
+/* SUMMARY
+/* internet address list manager
+/* SYNOPSIS
+/* #include <inet_addr_list.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <netinet/in.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* inet_addr_local 3
+/* SUMMARY
+/* determine if IP address is local
+/* SYNOPSIS
+/* #include <inet_addr_local.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#ifdef USE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <inet_addr_list.h>
+#include <inet_addr_local.h>
+
+/* 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 <vstream.h>
+#include <msg_vstream.h>
+
+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
--- /dev/null
+#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 <inet_addr_local.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <inet_addr_list.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* inet_connect 3
+/* SUMMARY
+/* connect to INET-domain listener
+/* SYNOPSIS
+/* #include <connect.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* 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);
+ }
+}
--- /dev/null
+/*++
+/* NAME
+/* inet_listen 3
+/* SUMMARY
+/* start INET-domain listener
+/* SYNOPSIS
+/* #include <listen.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#ifndef MAXHOSTNAMELEN
+#include <sys/param.h>
+#endif
+#include <string.h>
+#include <unistd.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* inet_trigger 3
+/* SUMMARY
+/* wakeup INET-domain server
+/* SYNOPSIS
+/* #include <trigger.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <connect.h>
+#include <iostuff.h>
+#include <trigger.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* inet_util 3
+/* SUMMARY
+/* INET-domain utilities
+/* SYNOPSIS
+/* #include <inet_util.h>
+/*
+/* 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 <sys_defs.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _INET_UTIL_H_INCLUDED_
+#define _INET_UTIL_H_INCLUDED_
+
+/*++
+/* NAME
+/* inet_util 3h
+/* SUMMARY
+/* INET-domain utilities
+/* SYNOPSIS
+/* #include <inet_util.h>
+/* 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
--- /dev/null
+#ifndef _IOSTUFF_H_INCLUDED_
+#define _IOSTUFF_H_INCLUDED_
+
+/*++
+/* NAME
+/* iostuff 3h
+/* SUMMARY
+/* miscellaneous I/O primitives
+/* SYNOPSIS
+/* #include <iostuff.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* line_wrap 3
+/* SUMMARY
+/* wrap long lines upon output
+/* SYNOPSIS
+/* #include <line_wrap.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <line_wrap.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _LINE_WRAP_H_INCLUDED_
+#define _LINE_WRAP_H_INCLUDED_
+
+/*++
+/* NAME
+/* line_wrap 3h
+/* SUMMARY
+/* wrap long lines upon output
+/* SYNOPSIS
+/* #include <line_wrap.h>
+/* 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
--- /dev/null
+#ifndef _LISTEN_H_INCLUDED_
+#define _LISTEN_H_INCLUDED_
+
+/*++
+/* NAME
+/* listen 3h
+/* SUMMARY
+/* listener interface file
+/* SYNOPSIS
+/* #include <listen.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <iostuff.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* lowercase 3
+/* SUMMARY
+/* map uppercase characters to lowercase
+/* SYNOPSIS
+/* #include <stringops.h>
+/*
+/* 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 <ctype.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* lstat_as 3
+/* SUMMARY
+/* lstat file as user
+/* SYNOPSIS
+/* #include <sys/stat.h>
+/* #include <lstat_as.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _LSTAT_AS_H_INCLUDED_
+#define _LSTAT_AS_H_INCLUDED_
+
+/*++
+/* NAME
+/* lstat_as 3h
+/* SUMMARY
+/* lstat file as user
+/* SYNOPSIS
+/* #include <sys/stat.h>
+/* #include <lstat_as.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* mac_parse 3
+/* SUMMARY
+/* locate macro references in string
+/* SYNOPSIS
+/* #include <mac_parse.h>
+/*
+/* 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 <sys_defs.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mac_parse.h>
+
+ /*
+ * 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 <vstring_vstream.h>
+
+/* 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
--- /dev/null
+#ifndef _MAC_PARSE_H_INCLUDED_
+#define _MAC_PARSE_H_INCLUDED_
+
+/*++
+/* NAME
+/* mac_parse 3h
+/* SUMMARY
+/* locate macro references in string
+/* SYNOPSIS
+/* #include <mac_parse.h>
+ DESCRIPTION
+ .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* make_dirs 3
+/* SUMMARY
+/* create directory hierarchy
+/* SYNOPSIS
+/* #include <make_dirs.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+
+/* 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 <stdlib.h>
+#include <msg_vstream.h>
+
+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
--- /dev/null
+#ifndef MAKE_DIRS_H_INCLUDED_
+#define MAKE_DIRS_H_INCLUDED_
+
+/*++
+/* NAME
+/* make_dirs 3h
+/* SUMMARY
+/* create directory hierarchy
+/* SYNOPSIS
+/* #include <make_dirs.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* match_list 3
+/* SUMMARY
+/* generic list-based pattern matching
+/* SYNOPSIS
+/* #include <match_list.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <stringops.h>
+#include <argv.h>
+#include <dict.h>
+#include <match_list.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _MATCH_LIST_H_INCLUDED_
+#define _MATCH_LIST_H_INCLUDED_
+
+/*++
+/* NAME
+/* match_list 3h
+/* SUMMARY
+/* generic list-based pattern matching
+/* SYNOPSIS
+/* #include <match_list.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* match_ops 3
+/* SUMMARY
+/* simple string or host pattern matching
+/* SYNOPSIS
+/* #include <match_ops.h>
+/*
+/* 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 <sys_defs.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffff
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <split_at.h>
+#include <dict.h>
+#include <match_ops.h>
+#include <stringops.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _MATCH_OPS_H_INCLUDED_
+#define _MATCH_OPS_H_INCLUDED_
+
+/*++
+/* NAME
+/* match_ops 3h
+/* SUMMARY
+/* simple string or host pattern matching
+/* SYNOPSIS
+/* #include <match_ops.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* msg 3
+/* SUMMARY
+/* diagnostic interface
+/* SYNOPSIS
+/* #include <msg.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+/* 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;
+}
--- /dev/null
+#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
--- /dev/null
+/*++
+/* NAME
+/* msg_output 3
+/* SUMMARY
+/* diagnostics output management
+/* SYNOPSIS
+/* #include <msg_output.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdarg.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+#include <stringops.h>
+#include <percentm.h>
+#include <msg_output.h>
+
+ /*
+ * 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));
+}
--- /dev/null
+#ifndef _MSG_OUTPUT_FN_
+#define _MSG_OUTPUT_FN_
+
+/*++
+/* NAME
+/* msg_output 3h
+/* SUMMARY
+/* diagnostics output management
+/* SYNOPSIS
+/* #include <msg_output.h>
+/* DESCRIPTION
+
+ /*
+ * System library.
+ */
+#include <stdarg.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* msg_syslog 3
+/* SUMMARY
+/* direct diagnostics to syslog daemon
+/* SYNOPSIS
+/* #include <msg_syslog.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+#include <errno.h>
+#include <syslog.h>
+
+/* 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
--- /dev/null
+#ifndef _MSG_SYSLOG_H_INCLUDED_
+#define _MSG_SYSLOG_H_INCLUDED_
+
+/*++
+/* NAME
+/* msg_syslog 3h
+/* SUMMARY
+/* direct diagnostics to syslog daemon
+/* SYNOPSIS
+/* #include <msg_syslog.h>
+/* DESCRIPTION
+
+ /*
+ * System library.
+ */
+#include <syslog.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* msg_vstream 3
+/* SUMMARY
+/* report diagnostics to VSTREAM
+/* SYNOPSIS
+/* #include <msg_vstream.h>
+/*
+/* 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 <sys_defs.h>
+#include <errno.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+/* 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);
+ }
+}
--- /dev/null
+#ifndef _MSG_VSTREAM_H_INCLUDED_
+#define _MSG_VSTREAM_H_INCLUDED_
+
+/*++
+/* NAME
+/* msg_vstream 3h
+/* SUMMARY
+/* direct diagnostics to VSTREAM
+/* SYNOPSIS
+/* #include <msg_vstream.h>
+/* DESCRIPTION
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* mvect 3
+/* SUMMARY
+/* memory vector management
+/* SYNOPSIS
+/* #include <mvect.h>
+/*
+/* 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 <sys_defs.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _MVECT_H_INCLUDED_
+#define _MVECT_H_INCLUDED_
+
+/*++
+/* NAME
+/* mvect 3h
+/* SUMMARY
+/* memory vector management
+/* SYNOPSIS
+/* #include <mvect.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* myflock 3
+/* SUMMARY
+/* lock open file
+/* SYNOPSIS
+/* #include <myflock.h>
+/*
+/* 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 <errno.h>
+#include <unistd.h>
+
+#ifdef USE_FCNTL_LOCK
+#include <fcntl.h>
+#include <string.h>
+#endif
+
+#ifdef USE_FLOCK_LOCK
+#include <sys/file.h>
+#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);
+}
--- /dev/null
+#ifndef _MYFLOCK_H_INCLUDED_
+#define _MYFLOCK_H_INCLUDED_
+
+/*++
+/* NAME
+/* myflock 3h
+/* SUMMARY
+/* lock open file
+/* SYNOPSIS
+/* #include <myflock.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* mymalloc 3
+/* SUMMARY
+/* memory management wrappers
+/* SYNOPSIS
+/* #include <mymalloc.h>
+/*
+/* 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 <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+/* 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));
+}
--- /dev/null
+#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
--- /dev/null
+/*++
+/* NAME
+/* mystrtok 3
+/* SUMMARY
+/* safe tokenizer
+/* SYNOPSIS
+/* #include <stringops.h>
+/*
+/* 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 <string.h>
+
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* name_mask 3
+/* SUMMARY
+/* map names to bit mask
+/* SYNOPSIS
+/* #include <name_mask.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <name_mask.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _NAME_MASK_H_INCLUDED_
+#define _NAME_MASK_H_INCLUDED_
+
+/*++
+/* NAME
+/* name_mask 3h
+/* SUMMARY
+/* map names to bit mask
+/* SYNOPSIS
+/* #include <name_mask.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* non_blocking 3
+/* SUMMARY
+/* set/clear non-blocking flag
+/* SYNOPSIS
+/* #include <iostuff.h>
+/*
+/* 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 <fcntl.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* open_as 3
+/* SUMMARY
+/* open file as user
+/* SYNOPSIS
+/* #include <fcntl.h>
+/* #include <open_as.h>
+/*
+/* 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 <sys_defs.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _OPEN_H_INCLUDED_
+#define _OPEN_H_INCLUDED_
+
+/*++
+/* NAME
+/* open_as 3h
+/* SUMMARY
+/* open file as user
+/* SYNOPSIS
+/* #include <fcntl.h>
+/* #include <open_as.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* open_limit 3
+/* SUMMARY
+/* set/get open file limit
+/* SYNOPSIS
+/* #include <iostuff.h>
+/*
+/* 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 <sys/time.h>
+#include <sys/resource.h>
+#include <errno.h>
+
+/* 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
+}
+
--- /dev/null
+/*++
+/* NAME
+/* open_lock 3
+/* SUMMARY
+/* open or create file and lock it for exclusive access
+/* SYNOPSIS
+/* #include <open_lock.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <safe_open.h>
+#include <myflock.h>
+#include <open_lock.h>
+
+/* 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);
+}
--- /dev/null
+#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 <open_lock.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <fcntl.h>
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* peekfd 3
+/* SUMMARY
+/* determine amount of data ready to read
+/* SYNOPSIS
+/* #include <iostuff.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/ioctl.h>
+#ifdef FIONREAD_IN_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+#ifdef FIONREAD_IN_TERMIOS_H
+#include <termios.h>
+#endif
+#include <unistd.h>
+
+/* 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
+}
+
--- /dev/null
+/*++
+/* NAME
+/* peer_name 3
+/* SUMMARY
+/* produce printable peer name and address
+/* SYNOPSIS
+/* #include <peer_name.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <valid_hostname.h>
+#include <peer_name.h>
+
+/* 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 <unistd.h>
+
+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
--- /dev/null
+#ifndef _PEER_NAME_H_INCLUDED_
+#define _PEER_NAME_H_INCLUDED_
+
+/*++
+/* NAME
+/* peer_name 3h
+/* SUMMARY
+/* produce printable peer name and address
+/* SYNOPSIS
+/* #include <peer_name.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* percentm 3
+/* SUMMARY
+/* expand %m embedded in string to system error text
+/* SYNOPSIS
+/* #include <percentm.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+/* 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 %<any> 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));
+}
+
--- /dev/null
+#ifndef _PERCENT_H_INCLUDED_
+#define _PERCENT_H_INCLUDED_
+
+/*++
+/* NAME
+/* percentm 3h
+/* SUMMARY
+/* expand %m embedded in string to system error text
+/* SYNOPSIS
+/* #include <percentm.h>
+/* 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
--- /dev/null
+ /*
+ * 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
+
--- /dev/null
+ /*
+ * 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/
+ */
--- /dev/null
+/*++
+/* NAME
+/* printable 3
+/* SUMMARY
+/* mask non-printable characters
+/* SYNOPSIS
+/* #include <stringops.h>
+/*
+/* 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 <ctype.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* read_wait 3
+/* SUMMARY
+/* wait until descriptor becomes readable
+/* SYNOPSIS
+/* #include <iostuff.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef USE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* 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);
+ }
+ }
+}
--- /dev/null
+/*++
+/* NAME
+/* readable 3
+/* SUMMARY
+/* test if descriptor is readable
+/* SYNOPSIS
+/* #include <iostuff.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef USE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* 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);
+ }
+ }
+}
--- /dev/null
+/*++
+/* NAME
+/* readline 3
+/* SUMMARY
+/* read logical line
+/* SYNOPSIS
+/* #include <readline.h>
+/*
+/* 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 <sys_defs.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _READLINE_H_INCLUDED_
+#define _READLINE_H_INCLUDED_
+
+/*++
+/* NAME
+/* readline 3h
+/* SUMMARY
+/* read logical line
+/* SYNOPSIS
+/* #include <readline.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* ring 3
+/* SUMMARY
+/* circular list management
+/* SYNOPSIS
+/* #include <ring.h>
+/*
+/* 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;
+}
--- /dev/null
+#ifndef _RING_H_INCLUDED_
+#define _RING_H_INCLUDED_
+
+/*++
+/* NAME
+/* ring 3h
+/* SUMMARY
+/* circular list management
+/* SYNOPSIS
+/* #include <ring.h>
+/* 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
--- /dev/null
+#ifndef _SAFE_H_INCLUDED_
+#define _SAFE_H_INCLUDED_
+
+/*++
+/* NAME
+/* safe 3h
+/* SUMMARY
+/* miscellaneous taint checks
+/* SYNOPSIS
+/* #include <safe.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* safe_getenv 3
+/* SUMMARY
+/* guarded getenv()
+/* SYNOPSIS
+/* #include <safe.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h>
+
+/* Utility library. */
+
+#include "safe.h"
+
+/* safe_getenv - read environment variable with guard */
+
+char *safe_getenv(const char *name)
+{
+ return (unsafe() == 0 ? getenv(name) : 0);
+}
--- /dev/null
+/*++
+/* NAME
+/* safe_open 3
+/* SUMMARY
+/* safely open or create regular file
+/* SYNOPSIS
+/* #include <safe_open.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <safe_open.h>
+
+/* 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");
+ }
+}
--- /dev/null
+#ifndef _SAFE_OPEN_H_INCLUDED_
+#define _SAFE_OPEN_H_INCLUDED_
+
+/*++
+/* NAME
+/* safe_open 3h
+/* SUMMARY
+/* safely open or create regular file
+/* SYNOPSIS
+/* #include <safe_open.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <fcntl.h>
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* sane_accept 3
+/* SUMMARY
+/* sanitize accept() error returns
+/* SYNOPSIS
+/* #include <sane_accept.h>
+/*
+/* 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 <sys/socket.h>
+#include <errno.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _SANE_ACCEPT_H_
+#define _SANE_ACCEPT_H_
+
+/*++
+/* NAME
+/* sane_accept 3h
+/* SUMMARY
+/* sanitize accept() error returns
+/* SYNOPSIS
+/* #include <sane_accept.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* scan_dir 3
+/* SUMMARY
+/* directory scanning
+/* SYNOPSIS
+/* #include <scan_dir.h>
+/*
+/* 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 <dirent>
+/* 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 <sys_defs.h>
+#include <dirent.h>
+#include <string.h>
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+#define dirent direct
+#define NAMLEN(dirent) (dirent)->d_namlen
+#ifdef HAVE_SYS_NDIR_H
+#include <sys/ndir.h>
+#endif
+#ifdef HAVE_SYS_DIR_H
+#include <sys/dir.h>
+#endif
+#ifdef HAVE_NDIR_H
+#include <ndir.h>
+#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);
+}
--- /dev/null
+#ifndef _SCAN_DIR_H_INCLUDED_
+#define _SCAN_DIR_H_INCLUDED_
+
+/*++
+/* NAME
+/* scan_dir 3h
+/* SUMMARY
+/* directory scanner
+/* SYNOPSIS
+/* #include <scan_dir.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <dirent.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* set_eugid 3
+/* SUMMARY
+/* set effective user and group attributes
+/* SYNOPSIS
+/* #include <set_eugid.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <grp.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _SET_EUGID_H_INCLUDED_
+#define _SET_EUGID_H_INCLUDED_
+
+/*++
+/* NAME
+/* set_eugid 3h
+/* SUMMARY
+/* set effective user and group attributes
+/* SYNOPSIS
+/* #include <set_eugid.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* set_ugid 3
+/* SUMMARY
+/* set real, effective and saved user and group attributes
+/* SYNOPSIS
+/* #include <set_ugid.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <grp.h>
+
+/* 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);
+}
+
--- /dev/null
+#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 <set_ugid.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* sigdelay 3
+/* SUMMARY
+/* delay/resume signal delivery
+/* SYNOPSIS
+/* #include <sigdelay.h>
+/*
+/* 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 <sys_defs.h>
+#include <signal.h>
+
+/* 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 <stdio.h>
+#include <unistd.h>
+
+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
--- /dev/null
+#ifndef _SIGDELAY_H_INCLUDED_
+#define _SIGDELAY_H_INCLUDED_
+
+/*++
+/* NAME
+/* sigdelay 3h
+/* SUMMARY
+/* delay/resume signal delivery
+/* SYNOPSIS
+/* #include <sigdelay.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* skipblanks 3
+/* SUMMARY
+/* skip leading whitespace
+/* SYNOPSIS
+/* #include <stringops.h>
+/*
+/* 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 <ctype.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* split_at 3
+/* SUMMARY
+/* trivial token splitter
+/* SYNOPSIS
+/* #include <split_at.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _SPLIT_AT_H_INCLUDED_
+#define _SPLIT_AT_H_INCLUDED_
+
+/*++
+/* NAME
+/* split_at 3h
+/* SUMMARY
+/* trivial token splitter
+/* SYNOPSIS
+/* #include <split_at.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* stat_as 3
+/* SUMMARY
+/* stat file as user
+/* SYNOPSIS
+/* #include <sys/stat.h>
+/* #include <stat_as.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _STAT_AS_H_INCLUDED_
+#define _STAT_AS_H_INCLUDED_
+
+/*++
+/* NAME
+/* stat_as 3h
+/* SUMMARY
+/* stat file as user
+/* SYNOPSIS
+/* #include <sys/stat.h>
+/* #include <stat_as.h>
+/* 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
--- /dev/null
+#ifndef _STRINGOPS_H_INCLUDED_
+#define _STRINGOPS_H_INCLUDED_
+
+/*++
+/* NAME
+/* stringops 3h
+/* SUMMARY
+/* string operations
+/* SYNOPSIS
+/* #include <stringops.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* sys_compat 3
+/* SUMMARY
+/* compatibility routines
+/* SYNOPSIS
+/* #include <sys_defs.h>
+/*
+/* 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 <vstring.h>
+
+/* 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* 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 <unistd.h>
+
+int seteuid(uid_t euid)
+{
+ return setresuid(-1, euid, -1);
+}
+
+#else
+#error MISSING_SETEUID
+#endif
+
+#endif
+
+#ifdef MISSING_SETEGID
+#ifdef HAVE_SETRESGID
+#include <unistd.h>
+
+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 <sys/stat.h>
+
+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 <sys/wait.h>
+#include <errno.h>
+
+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 <sys/ioctl.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef TIOCNOTTY
+
+#include <msg.h>
+
+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
--- /dev/null
+#ifndef _SYS_DEFS_H_INCLUDED_
+#define _SYS_DEFS_H_INCLUDED_
+
+/*++
+/* NAME
+/* sys_defs 3h
+/* SUMMARY
+/* portability header
+/* SYNOPSIS
+/* #include <sys_defs.h>
+/* 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 <sys/types.h>
+#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 <sys/types.h>
+#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 <sys/types.h>
+#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 <getopt.h> */
+extern char *optarg; /* XXX use <getopt.h> */
+extern int opterr; /* XXX use <getopt.h> */
+
+#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 <sys/types.h>
+#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 <sys/types.h>
+#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 <sys/types.h>
+#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 <sys/types.h>
+#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 <sys/types.h>
+#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 <sys/types.h>
+#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 <sys/types.h>
+#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; /* <netdb.h> imports too much stuff */
+
+#define USE_STATFS
+#define STATFS_IN_SYS_VFS_H
+#endif
+
+#ifdef HPUX10
+#define SUPPORTED
+#define USE_SIG_RETURN
+#include <sys/types.h>
+#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; /* <netdb.h> imports too much stuff */
+
+#define USE_STATFS
+#define STATFS_IN_SYS_VFS_H
+#endif
+
+#ifdef HPUX9
+#define SUPPORTED
+#define USE_SIG_RETURN
+#include <sys/types.h>
+#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 <sys/types.h>
+#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 <libc.h>
+#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 <sys/types.h>
+#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 <libc.h>
+#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
--- /dev/null
+/*++
+/* NAME
+/* timed_connect 3
+/* SUMMARY
+/* connect operation with timeout
+/* SYNOPSIS
+/* #include <sys/socket.h>
+/* #include <timed_connect.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _TIMED_CONNECT_H_INCLUDED_
+#define _TIMED_CONNECT_H_INCLUDED_
+
+/*++
+/* NAME
+/* timed_connect 3h
+/* SUMMARY
+/* connect operation with timeout
+/* SYNOPSIS
+/* #include <timed_connect.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <sys/socket.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* timed_wait 3
+/* SUMMARY
+/* wait operations with timeout
+/* SYNOPSIS
+/* #include <timed_wait.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <posix_signals.h>
+#include <timed_wait.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _TIMED_WAIT_H_INCLUDED_
+#define _TIMED_WAIT_H_INCLUDED_
+
+/*++
+/* NAME
+/* timed_wait 3h
+/* SUMMARY
+/* wait operations with timeout
+/* SYNOPSIS
+/* #include <timed_wait.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* translit 3
+/* SUMMARY
+/* transliterate characters
+/* SYNOPSIS
+/* #include <stringops.h>
+/*
+/* 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 <string.h>
+
+/* 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 <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+
+#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
--- /dev/null
+#ifndef _TRIGGER_H_INCLUDED_
+#define _TRIGGER_H_INCLUDED_
+
+/*++
+/* NAME
+/* trigger 3h
+/* SUMMARY
+/* client interface file
+/* SYNOPSIS
+/* #include <trigger.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* trimblanks 3
+/* SUMMARY
+/* skip leading whitespace
+/* SYNOPSIS
+/* #include <stringops.h>
+/*
+/* 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 <ctype.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* unix_connect 3
+/* SUMMARY
+/* connect to UNIX-domain listener
+/* SYNOPSIS
+/* #include <unix_connect.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* 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);
+ }
+}
--- /dev/null
+/*++
+/* NAME
+/* unix_listen 3
+/* SUMMARY
+/* start UNIX-domain listener
+/* SYNOPSIS
+/* #include <listen.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* unix_trigger 3
+/* SUMMARY
+/* wakeup UNIX-domain server
+/* SYNOPSIS
+/* #include <trigger.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <connect.h>
+#include <iostuff.h>
+#include <trigger.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* unsafe 3
+/* SUMMARY
+/* are we running at non-user privileges
+/* SYNOPSIS
+/* #include <safe.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+
+/* 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());
+}
--- /dev/null
+/*++
+/* NAME
+/* username 3
+/* SUMMARY
+/* lookup name of real user
+/* SYNOPSIS
+/* #include <username.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <pwd.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _USERNAME_H_INCLUDED_
+#define _USERNAME_H_INCLUDED_
+
+/*++
+/* NAME
+/* username 3h
+/* SUMMARY
+/* lookup name of real user
+/* SYNOPSIS
+/* #include <username.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* valid_hostname 3
+/* SUMMARY
+/* network name validation
+/* SYNOPSIS
+/* #include <valid_hostname.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+#include <ctype.h>
+
+/* 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 <stdlib.h>
+
+#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
--- /dev/null
+#ifndef _VALID_HOSTNAME_H_INCLUDED_
+#define _VALID_HOSTNAME_H_INCLUDED_
+
+/*++
+/* NAME
+/* valid_hostname 3h
+/* SUMMARY
+/* validate hostname
+/* SYNOPSIS
+/* #include <valid_hostname.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* vbuf 3
+/* SUMMARY
+/* generic buffer package
+/* SYNOPSIS
+/* #include <vbuf.h>
+/*
+/* 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 <string.h>
+
+/* 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
+}
--- /dev/null
+#ifndef _VBUF_H_INCLUDED_
+#define _VBUF_H_INCLUDED_
+
+/*++
+/* NAME
+/* vbuf 3h
+/* SUMMARY
+/* generic buffer
+/* SYNOPSIS
+/* #include <vbuf.h>
+/* 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
--- /dev/null
+/*++
+/* NAME
+/* vbuf_print 3
+/* SUMMARY
+/* formatted print to generic buffer
+/* SYNOPSIS
+/* #include <stdarg.h>
+/* #include <vbuf_print.h>
+/*
+/* 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 <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h> /* 44bsd stdarg.h uses abort() */
+#include <stdio.h> /* sprintf() prototype */
+#include <float.h> /* range of doubles */
+#include <errno.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _VBUF_PRINT_H_INCLUDED_
+#define _VBUF_PRINT_H_INCLUDED_
+
+/*++
+/* NAME
+/* vbuf_print 3h
+/* SUMMARY
+/* formatted print to generic buffer
+/* SYNOPSIS
+/* #include <vbuf_print.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <stdarg.h>
+
+ /*
+ * Utility library.
+ */
+#include <vbuf.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* vstream 3
+/* SUMMARY
+/* light-weight buffered I/O package
+/* SYNOPSIS
+/* #include <vstream.h>
+/*
+/* 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 <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _VSTREAM_H_INCLUDED_
+#define _VSTREAM_H_INCLUDED_
+
+/*++
+/* NAME
+/* vstream 3h
+/* SUMMARY
+/* simple buffered I/O package
+/* SYNOPSIS
+/* #include <vstream.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <fcntl.h>
+#include <stdarg.h>
+
+ /*
+ * Utility library.
+ */
+#include <vbuf.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* vstream_popen 3
+/* SUMMARY
+/* open stream to child process
+/* SYNOPSIS
+/* #include <vstream.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <binhash.h>
+#include <exec_command.h>
+#include <vstream.h>
+
+/* 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 <fcntl.h>
+#include <vstring.h>
+#include <vstring_vstream.h>
+
+ /*
+ * 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
--- /dev/null
+/* NAME
+/* vstring 3
+/* SUMMARY
+/* arbitrary-length string manager
+/* SYNOPSIS
+/* #include <vstring.h>
+/*
+/* 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 <sys_defs.h>
+#include <stddef.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+#include <string.h>
+
+/* 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 <stdio.h>
+
+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
--- /dev/null
+#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 <stdarg.h>
+
+ /*
+ * Utility library.
+ */
+#include <vbuf.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* vstring_vstream 3
+/* SUMMARY
+/* auto-resizing string library, standard I/O interface
+/* SYNOPSIS
+/* #include <vstring_vstream.h>
+/*
+/* 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 <stdio.h>
+#include <string.h>
+
+/* 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 <fcntl.h>
+
+#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
--- /dev/null
+#ifndef _VSTRING_VSTREAM_H_INCLUDED_
+#define _VSTRING_VSTREAM_H_INCLUDED_
+
+/*++
+/* NAME
+/* vstring_vstream 3h
+/* SUMMARY
+/* auto-resizing string library
+/* SYNOPSIS
+/* #include <vstring_vstream.h>
+/* DESCRIPTION
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+ * 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
--- /dev/null
+/*++
+/* NAME
+/* writable 3
+/* SUMMARY
+/* test if descriptor is writable
+/* SYNOPSIS
+/* #include <iostuff.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef USE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* 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);
+ }
+ }
+}
--- /dev/null
+/*++
+/* NAME
+/* write_buf 3
+/* SUMMARY
+/* write buffer or bust
+/* SYNOPSIS
+/* #include <iostuff.h>
+/*
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* write_wait 3
+/* SUMMARY
+/* wait until descriptor becomes writable
+/* SYNOPSIS
+/* #include <iostuff.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef USE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* 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);
+ }
+ }
+}