diff options
Diffstat (limited to 'gnu')
167 files changed, 30280 insertions, 21808 deletions
diff --git a/gnu/usr.sbin/sendmail/FAQ b/gnu/usr.sbin/sendmail/FAQ index 99b1dd83fe1..db6ad9a8f3a 100644 --- a/gnu/usr.sbin/sendmail/FAQ +++ b/gnu/usr.sbin/sendmail/FAQ @@ -5,4 +5,4 @@ A plain-text version of the questions only, with URLs referring to the answers, is posted to comp.mail.sendmail on the 10th and 25th of each month. -$Revision: 1.3 $, Last updated $Date: 2001/01/15 21:08:49 $ +$Revision: 1.4 $, Last updated $Date: 2001/09/11 19:02:47 $ diff --git a/gnu/usr.sbin/sendmail/KNOWNBUGS b/gnu/usr.sbin/sendmail/KNOWNBUGS index a923cf827d9..f8761720226 100644 --- a/gnu/usr.sbin/sendmail/KNOWNBUGS +++ b/gnu/usr.sbin/sendmail/KNOWNBUGS @@ -13,7 +13,6 @@ distribution). This list is not guaranteed to be complete. * Delivery to programs that generate too much output may cause problems - (8.10, 8.11) If e-mail is delivered to a program which generates too much output, then sendmail may issue an error: @@ -150,7 +149,7 @@ This list is not guaranteed to be complete. * MIME encoded full name phrases in the From: header If a full name phrase includes characters from MustQuoteChars, sendmail - will quote the entire full name phrase. If MustQuoteChars includes + will quote the entire full name phrase. If MustQuoteChars includes characters which are not special characters according to STD 11 (RFC 822), this quotation can interfere with MIME encoded full name phrases. By default, sendmail includes the single quote character (') in @@ -200,22 +199,14 @@ This list is not guaranteed to be complete. local mail delivery and NFS hard mounted home directories should be avoided, as attempts to open the forward files could hang. -* Race condition for delivery to set-user-id files +* Race condition for delivery to set-user-ID files Sendmail will deliver to a fail if the file is owned by the DefaultUser - or has the set-user-id bit set. Unfortunately, some systems clear that bit + or has the set-user-ID bit set. Unfortunately, some systems clear that bit when a file is modified. Sendmail compensates by resetting the file mode back to it's original settings. Unfortunately, there's still a permission failure race as sendmail checks the permissions before locking the file. This is unavoidable as sendmail must verify the file is safe to open before opening it. A file can not be locked until it is open. -* Potential denial of service attack with AutoRebuildAliases - - There is a potential for a denial of service attack if the - AutoRebuildAliases option is set as a user can kill the sendmail process - while it is rebuilding the aliases file leaving it in an inconsistent - state. This option and it's use is deprecated and will be removed from a - future version of sendmail. - -$Revision: 1.4 $, Last updated $Date: 2001/08/01 01:01:40 $ +$Revision: 1.5 $, Last updated $Date: 2001/09/11 19:02:47 $ diff --git a/gnu/usr.sbin/sendmail/LICENSE b/gnu/usr.sbin/sendmail/LICENSE index 4a89057fae6..0a7b647530e 100644 --- a/gnu/usr.sbin/sendmail/LICENSE +++ b/gnu/usr.sbin/sendmail/LICENSE @@ -76,4 +76,4 @@ each of the following conditions is met: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -$Revision: 1.4 $, Last updated $Date: 2001/02/28 02:43:48 $ +$Revision: 1.5 $, Last updated $Date: 2001/09/11 19:02:47 $ diff --git a/gnu/usr.sbin/sendmail/Makefile b/gnu/usr.sbin/sendmail/Makefile index d36732b2a6d..d0839ba9555 100644 --- a/gnu/usr.sbin/sendmail/Makefile +++ b/gnu/usr.sbin/sendmail/Makefile @@ -1,14 +1,14 @@ -# $OpenBSD: Makefile,v 1.3 2000/04/04 04:58:35 millert Exp $ +# $OpenBSD: Makefile,v 1.4 2001/09/11 19:02:47 millert Exp $ # We only use these libs internally .if !make(install) -SUBDIR= libsmutil libsmdb +SUBDIR= libsmutil libsmdb libsm .else SUBDIR= .endif # The bits we install -SUBDIR+=sendmail mailstats makemap praliases smrsh cf/cf +SUBDIR+=sendmail mailstats makemap praliases smrsh editmap cf/cf # Extra bits we don't build/install right now #SUBDIR+= libmilter mail.local rmail vacation diff --git a/gnu/usr.sbin/sendmail/Makefile.inc b/gnu/usr.sbin/sendmail/Makefile.inc index a839b1ed210..52ab3d87d93 100644 --- a/gnu/usr.sbin/sendmail/Makefile.inc +++ b/gnu/usr.sbin/sendmail/Makefile.inc @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile.inc,v 1.5 2000/09/03 18:41:12 espie Exp $ +# $OpenBSD: Makefile.inc,v 1.6 2001/09/11 19:02:47 millert Exp $ .include <bsd.own.mk> .include <bsd.obj.mk> @@ -9,14 +9,6 @@ ENVDEF+= -DNETINET6 -DNEEDSGETIPNODE ENVDEF+=-DNIS .endif -.if defined(WANT_LIBWRAP) -.if (${TCP_WRAPPERS:L} == "yes") -ENVDEF+=-DTCPWRAPPERS -DPADD+= ${LIBWRAP} -LDADD+= -lwrap -.endif -.endif - .if defined(WANT_LIBSMDB) LDADD+= -lsmdb .if exists(${.CURDIR}/../libsmdb/${__objdir}) @@ -39,4 +31,34 @@ LDFLAGS+=-L${.CURDIR}/../libsmutil .endif .endif +.if defined(WANT_LIBSM) +LDADD+= -lsm +.if exists(${.CURDIR}/../libsm/${__objdir}) +DPADD+= ${.CURDIR}/../libsm/${__objdir}/libsm.a +LDFLAGS+=-L${.CURDIR}/../libsm/${__objdir} +.else +DPADD+= ${.CURDIR}/../libsm/libsm.a +LDFLAGS+=-L${.CURDIR}/../libsm +.endif +.endif + +.if defined(WANT_LIBMILTER) +LDADD+= -lmilter +.if exists(${.CURDIR}/../libmilter/${__objdir}) +DPADD+= ${.CURDIR}/../libmilter/${__objdir}/libmilter.a +LDFLAGS+=-L${.CURDIR}/../libmilter/${__objdir} +.else +DPADD+= ${.CURDIR}/../libmilter/libmilter.a +LDFLAGS+=-L${.CURDIR}/../libmilter +.endif +.endif + +.if defined(WANT_LIBWRAP) +.if (${TCP_WRAPPERS:L} == "yes") +ENVDEF+=-DTCPWRAPPERS +DPADD+= ${LIBWRAP} +LDADD+= -lwrap +.endif +.endif + CFLAGS+=${ENVDEF} -I${.CURDIR}/../sendmail -I${.CURDIR}/../include diff --git a/gnu/usr.sbin/sendmail/README b/gnu/usr.sbin/sendmail/README index 138eaa9bc20..2bf3438365e 100644 --- a/gnu/usr.sbin/sendmail/README +++ b/gnu/usr.sbin/sendmail/README @@ -27,10 +27,12 @@ the latest updates. You may also find these useful: - d. devtools/README - e. devtools/Site/README - f. mail.local/README - g. smrsh/README + d. sendmail/SECURITY + e. devtools/README + f. devtools/Site/README + g. libmilter/README + h. mail.local/README + i. smrsh/README 4. Read cf/README. @@ -93,7 +95,7 @@ the items in the file to be marked as safe for file and program delivery. Other files affected by this strengthened security include class -files (i.e. Fw /etc/mail/local-host-names), persistent host status files, +files (i.e., Fw /etc/mail/local-host-names), persistent host status files, and the files specified by the ErrorHeader and HelpFile options. Similar DontBlameSendmail flags are available for the class, ErrorHeader, and HelpFile files. @@ -221,8 +223,8 @@ PARTS OF THE WORLD. SO, WHEN YOU IMPORT THIS PACKAGE TO YOUR COUNTRY, RE-DISTRIBUTE IT FROM THERE OR EVEN JUST EMAIL TECHNICAL SUGGESTIONS OR EVEN SOURCE PATCHES TO THE AUTHOR OR OTHER PEOPLE YOU ARE STRONGLY ADVISED TO PAY CLOSE ATTENTION TO ANY EXPORT/IMPORT -AND/OR USE LAWS WHICH APPLY TO YOU. THE AUTHORS ARE NOT LIABLE FOR -ANY VIOLATIONS YOU MAKE HERE. SO BE CAREFUL, IT IS YOUR RESPONSIBILITY. +AND/OR USE LAWS WHICH APPLY TO YOU. THE AUTHORS ARE NOT LIABLE FOR +ANY VIOLATIONS YOU MAKE HERE. SO BE CAREFUL, IT IS YOUR RESPONSIBILITY. If you use OpenSSL then make sure you read their README file which contains information about patents etc. @@ -360,6 +362,8 @@ contrib Some contributed tools to help with sendmail. THESE devtools Build environment. See devtools/README. doc Documentation. If you are getting source, read op.me -- it's long, but worth it. +editmap A program to edit and query maps that have been created + with makemap, e.g., adding and deleting entries. include Include files used by multiple programs in the distribution. libsmdb sendmail database library with support for Berkeley DB 1.X, Berkeley DB 2.X, Berkeley DB 3.X, and NDBM. @@ -392,4 +396,4 @@ sendmail Source for the sendmail program itself. test Some test scripts (currently only for compilation aids). vacation Source for the vacation program. NOT PART OF SENDMAIL! -$Revision: 1.5 $, Last updated $Date: 2001/08/01 01:01:40 $ +$Revision: 1.6 $, Last updated $Date: 2001/09/11 19:02:47 $ diff --git a/gnu/usr.sbin/sendmail/RELEASE_NOTES b/gnu/usr.sbin/sendmail/RELEASE_NOTES index 00d2a7750d4..37534e49fb3 100644 --- a/gnu/usr.sbin/sendmail/RELEASE_NOTES +++ b/gnu/usr.sbin/sendmail/RELEASE_NOTES @@ -1,11 +1,686 @@ SENDMAIL RELEASE NOTES - $Sendmail: RELEASE_NOTES,v 8.561.2.5.2.261 2001/08/20 14:45:32 gshapiro Exp $ + $Sendmail: RELEASE_NOTES,v 8.1127 2001/09/08 01:30:01 ca Exp $ This listing shows the version of the sendmail binary, the version of the sendmail configuration files, the date of release, and a summary of the changes in that release. +8.12.0/8.12.0 2001/09/08 + *NOTICE*: The default installation of sendmail does not use + set-user-ID root anymore. You need to create a new user and + a new group before installing sendmail (both called smmsp by + default). The installation process tries to install + /etc/mail/submit.cf and creates /var/spool/clientmqueue by + default. Please see sendmail/SECURITY for details. + SECURITY: Check for group and world writable forward and :include: + files. These checks can be turned off if absolutely + necessary using the DontBlameSendmail option and the new + flags: + GroupWritableForwardFile + WorldWritableForwardFile + GroupWritableIncludeFile + WorldWritableIncludeFile + Problem noted by Slawek Zak of Politechnika Warszawska, + SECURITY: Drop privileges when using address test mode. Suggested + by Michal Zalewski of the "Internet for Schools" project + (IdS). + Fixed problem of a global variable being used for a timeout jump + point where the variable could become overused for more than + one timeout concurrently. This erroneous behavior resulted in + a corrupted stack causing a core dump. The timeout is now + handled via libsm. Problem noted by Michael Shapiro, + John Beck, and Carl Smith of Sun Microsystems. + If sendmail is set-group-ID then that group ID is used for permission + checks (group ID of RunAsUser). This allows use of a + set-group-ID sendmail binary for initial message submission + and no set-user-ID root sendmail is needed. For details + see sendmail/SECURITY. + Log a warning if a non-trusted user changes the syslog label. + Based on notice from Bryan Costales of SL3D, Inc. + If sendmail is called for initial delivery, try to use submit.cf + with a fallback of sendmail.cf as configuration file. See + sendmail/SECURITY. + New configuration file option UseMSP to allow group writable queue + files if the group is the same as that of a set-group-ID + sendmail binary. See sendmail/SECURITY. + The .cf file is chosen based on the operation mode. For -bm (default), + -bs, and -t it is submit.cf if it exists for all others it + is sendmail.cf (to be backward compatible). This selection + can be changed by the new option -Ac or -Am (alternative .cf + file: client or mta). See sendmail/SECURITY. + The SMTP server no longer forks on each MAIL command. The ONEX + command has been removed. + Implement SMTP PIPELINING per RFC 1854. It can be turned off + at compile time or per host (ruleset). + New option MailboxDatabase specifies the type of mailbox database + used to look up local mail recipients; the default value + is "pw", which means to use getpwnam(). New mailbox database + types can be added by adding custom code to libsm/mbdb.c. + Queue file names are now 15 characters long, rather than 14 characters + long, to accomodate envelope splitting. File systems with + a 14 character file name length limit are no longer + supported. + Recipient list used for delivery now gets internally ordered by + hostsignature (character string version of MX RR). This orders + recipients for the same MX RR's together meaning smaller + portions of the list need to be scanned (instead of the whole + list) each delivery() pass to determine piggybacking. The + significance of the change is better the larger the recipient + list. Hostsignature is now created during recipient list + creation rather than just before delivery. + Enhancements for more opportunistic piggybacking. Previous + piggybacking (called coincidental) extended to coattail + piggybacking. Rather than complete MX RR matching + (coincidental) piggybacking is done if just the lowest value + preference matches (coattail). + If sendmail receives a temporary error on a RCPT TO: command, it will + try other MX hosts if available. + DefaultAuthInfo can contain a list of mechanisms to be used for + outgoing (client-side) SMTP Authentication. + New modifier 'A' for DaemonPortOptions/ClientPortOptions to disable + AUTH (overrides 'a' modifier in DaemonPortOptions). Based + on patch from Lyndon Nerenberg of Messaging Direct. + Enable AUTH mechanism EXTERNAL if STARTTLS is used. + A new ruleset authinfo can be used to return client side + authentication information for AUTH instead of DefaultAuthInfo. + Therefore the DefaultAuthInfo option is deprecated and will be + removed in future versions. + Accept any SMTP continuation code 3xy for AUTH even though RFC 2554 + requires 334. Mercury 1.48 is a known offender. + Add new option AuthMaxBits to limit the overall encryption strength + for the security layer in SMTP AUTH (SASL). See + doc/op/op.me for details. + Introduce new STARTTLS related macros {cn_issuer}, {cn_subject}, + {cert_md5} which hold the CN (common name) of the CA that + signed the presented certificate, the CN and the MD5 hash + of the presented certificate, respectively. + New ruleset try_tls to decide whether to try (as client) STARTTLS. + New ruleset srv_features to enable/disable certain features in the + server per connection. See doc/op/op.me for details. + New ruleset tls_rcpt to decide whether to send e-mail to a particular + recipient; useful to decide whether a conection is secure + enough on a per recipient basis. + New option TLSSrvOptions to modify some aspects of the server + for STARTTLS. + If no certificate has been requested, the macro {verify} has the + value "NOT". + New M=S modifier for ClientPortOptions/DaemonPortOptions to turn off + using/offering STARTTLS when delivering/receiving e-mail. + Macro expand filenames/directories for certs and keys in the .cf file. + Proposed by Neil Rickert of Northern Illinois University. + Generate an ephemeral RSA key for a STARTTLS connection only if + really required. This change results in a noticable + performance gains on most machines. Moreover, if shared + memory is in use, reuse the key several times. + Add queue groups which can be used to group queue directories with + the same behavior together. See doc/op/op.me for details. + If the new option FastSplit (defaults to one) has a value greater + than zero, it suppresses the MX lookups on addresses when they + are initially sorted which may result in faster envelope + splitting. If the mail is submitted directly from the + command line, then the value also limits the number of + processes to deliver the envelopes; if more envelopes are + created they are only queued up and must be taken care of + by a queue run. + The check for 'enough disk space' now pays attention to which file + system each queue directory resides in. + All queue runners can be cleanly terminated via SIGTERM to parent. + New option QueueFileMode for the default permissions of queue files. + Add parallel queue runner code. Allows multiple queue runners per work + group (one or more queues in a multi-queue environment + collected together) to process the same work list at the + same time. + Option MaxQueueChildren added to limit the number of concurrently + active queue runner processes. + New option MaxRunnersPerQueue to specify the maximum number of queue + runners per queue group. + Queue member selection by substring pattern matching now allows + the pattern to be negated. For -qI, -qR and -qS it is + permissible for -q!I, -q!R and -q!S to mean remove members + of the queue that match during processing. + New -qp[time] option is similar to -qtime, except that instead of + periodically forking a child to process the queue, a single + child is forked for each queue that sleeps between queue + runs. A SIGHUP signal can be sent to restart this + persistent queue runner. + The SIGHUP signal now restarts a timed queue run process (i.e., a + sendmail process which only runs the queue at an interval: + sendmail -q15m). + New option NiceQueueRun to set the priority of queue runners. + Proposed by Thom O'Connor. + sendmail will run the queue(s) in the background when invoked with -q + unless the new -qf option or -v is used. + QueueSortOrder=Random sorts the queue randomly, which is useful if + several queue runners are started by hand to avoid contention. + QueueSortOrder=Modification sorts the queue by the modification time + of the qf file (older entries first). + Support Deliver By SMTP Service Extension (RFC 2852) which allows + a client to specify an amount of time within which an e-mail + should be delivered. New option DeliverByMin added to set the + minimum amount of time or disable the extension. + Non-printable characters (ASCII: 0-31, 127) in mailbox addresses are + not allowed unless escaped or quoted. + Add support for a generic DNS map. Based on a patch contributed + by Leif Johansson of Stockholm University, which was based on + work by Assar Westerlund of Swedish Institute of Computer + Science, Kista, and Johan Danielsson of Royal Institute of + Technology, Stockholm, Sweden. + MX records will be looked up for FallBackMXhost. To use the old + behavior (no MX lookups), put the name in square brackets. + Proposed by Thom O'Connor. + Use shared memory to store free space of filesystems that are used + for queues, if shared memory is available and if a key is set + via SharedMemoryKey. This minimizes the number of system + calls to check the available space. See doc/op/op.me for + details. + If shared memory is compiled in the option -bP can be used to print + the number of entries in the queue(s). + Enable generic mail filter API (milter). See libmilter/README + and the usual documentation for details. + Remove AutoRebuildAliases option, deprecated since 8.10. + Remove '-U' (initial user submission) command line option as + announced in 8.10. + Remove support for non-standard SMTP command XUSR. Use an MSA instead. + New macro {addr_type} which contains whether the current address is + an envelope sender or recipient address. Suggested by + Neil Rickert of Northern Illinois University. + Two new options for host maps: -d (retransmission timeout), + -r (number of retries). + New option for LDAP maps: the -V<sep> allows you to specify a + separator such that a lookup can return both an attribute + and value separated by the given separator. + Add new operators '%', '|', '&' (modulo, binary or, binary and) + to map class arith. + If DoubleBounceAddress expands to an empty string, ``double bounces'' + (errors that occur when sending an error message) are dropped. + New DontBlameSendmail options GroupReadableSASLDBFile and + GroupWritableSASLDBFile to relax requirements for sasldb files. + New DontBlameSendmail options GroupReadableKeyFile to relax + requirements for files containing secret keys. This is + necessary for the MSP if client authentification is used. + Properly handle quoted filenames for class files (to allow for + filenames with spaces). + Honor the resolver option RES_NOALIASES when canonifying hostnames. + Add macros to avoid the reuse of {if_addr} etc: + {if_name_out} hostname of interface of outgoing connection. + {if_addr_out} address of interface of outgoing connection. + {if_family_out} family of interface of outgoing connection. + The latter two are only set if the interface does not belong + to the loopback net. + Add macro {nrcpts} which holds the number of (validated) recipients. + DialDelay option applies only to mailers with flag 'Z'. Patch from + Juergen Georgi of RUS University of Stuttgart. + New Timeout.lhlo,auth,starttls options to limit the time waiting for + an answer to the LMTP LHLO, SMTP AUTH or STARTTLS command. + New Timeout.aconnect option to limit the overall waiting time for + all connections for a single delivery attempt to succeed. + Limit the rate recipients in the SMTP envelope are accepted once + a threshold number of recipients has been rejected (option + BadRcptThrottle). From Gregory A Lundberg of the WU-FTPD + Development Group. + New option DelayLA to delay connections if the load averages + exceeds the specified value. The default of 0 does not + change the previous behavior. A value greater than 0 + will cause sendmail to sleep for one second on most + SMTP commands and before accepting connections if that + load average is exceeded. + Use a dynamic (instead of fixed-size) buffer for the list of + recipients that are sent during a connection to a mailer. + This also introduces a new mailer field 'r' which defines + the maximum number of recipients (defaults to 100). + Based on patch by Motonori Nakamura of Kyoto University. + Add new F=1 mailer flag to disable sending of null characters ('\0'). + Add new F=2 mailer flag to disable use of ESMTP, using SMTP instead. + The deprecated [TCP] builtin mailer pathname (P=) is gone. Use [IPC] + instead. + IPC is no longer available as first mailer argument (A=) for [IPC] + builtin mailer pathnames. Use TCP instead. + PH map code updated to use the new libphclient API instead of the + old libqiapi library. Contributed by Mark Roth of the + University of Illinois at Urbana-Champaign. + New option DirectSubmissionModifiers to define {daemon_flags} + for direct (command line) submissions. + New M=O modifier for DaemonPortOptions to ignore the socket in + case of failures. Based on patch by Jun-ichiro itojun + Hagino of the KAME Project. + Add Disposition-Notification-To: (RFC 2298) to the list of headers + whose content is rewritten similar to Reply-To:. + Proposed by Andrzej Filip. + Use STARTTLS/AUTH=server/client for logging incoming/outgoing + STARTTLS/AUTH connections; log incoming connections at level + 9 or higher. Use AUTH/STARTTLS instead of SASL/TLS for SMTP + AUTH/STARTTLS related logfile entries. + Convert unprintable characters (and backslash) into octal or C format + before logging. + Log recipients if no message is transferred but QUIT/RSET is given + (at LogLevel 9/10 or higher). + Log discarded recipients at LogLevel 10 or higher. + Do not log "did not issue MAIL/EXPN/VRFY/ETRN" for connections + in which most commands are rejected due to check_relay or + TCP Wrappers if the host tries one of those commands anyway. + Change logging format for cloned envelopes to be similar to that for + DSNs ("old id: new id: clone"). Suggested by Ulrich Windl + of the Universitat Regensburg. + Added libsm, a C library of general purpose abstractions including + assertions, tracing and debugging with named debug categories, + exception handling, malloc debugging, resource pools, + portability abstractions, and an extensible buffered I/O + package. It will at some point replace libsmutil. + See libsm/index.html for details. + Fixed most memory leaks in sendmail which were previously taken + care of by fork() and exit(). + Use new sm_io*() functions in place of stdio calls. Allows for + more consistent portablity amongst different platforms + new and old (from new libsm). + Common I/O pkg means just one buffering method needed instead of two + ('bf_portable' and 'bf_torek' now just 'bf'). + Sfio no longer needed as SASL/TLS code uses sm_io*() API's. + New possible value 'interactive' for SuperSafe which can be used + together with DeliveryMode=interactive is to avoid some disk + synchronizations calls. + Add per-recipient status information to mailq -v output. + T_ANY queries are no longer used by sendmail. + When compiling with "gcc -O -Wall" specify "-DSM_OMIT_BOGUS_WARNINGS" + too (see include/sm/cdefs.h for more info). + sendmail -d now has general support for named debug categories. + See libsm/debug.html and section 3.4 of doc/op/op.me + for details. + Eliminate the "postmaster warning" DSNs on address parsing errors + such as unbalanced angle brackets or parentheses. The DSNs + generated by this condition were illegal (not RFC conform). + Problem noted by Ulrich Windl of the Universitaet Regensburg. + Do not issue a DSN if the ruleset localaddr resolves to the $#error + mailer and the recipient has hence been rejected during the + SMTP dialogue. Problem reported by Larry Greenfield of CMU. + Deal with a case of multiple deliveries on misconfigured systems + that do not have postmaster defined. If an email was sent + from an address to which a DSN cannot be returned and + in which at least one recipient address is non-deliverable, + then that email had been delivered in each queue run. + Problem reported by Matteo HCE Valsasna of Universita + degli Studi dell'Insubria. + The compilation options SMTP, DAEMON, and QUEUE have been removed, + i.e., the corresponding code is always compiled in now. + Log the command line in daemon/queue-run mode at LogLevel 10 and + higher. Suggested by Robert Harker of Harker Systems. + New ResolverOptions setting: WorkAroundBrokenAAAA. When + attempting to canonify a hostname, some broken nameservers + will return SERVFAIL (a temporary failure) on T_AAAA (IPv6) + lookups. If you want to excuse this behavior, use this new + flag. Suggested by Chris Foote of SE Network Access and + Mark Roth of the University of Illinois at + Urbana-Champaign. + Free the memory allocated by getipnodeby{addr,name}(). Problem + noted by Joy Latten of IBM. + ConnectionRateThrottle limits the number of connections per second + to each daemon individually, not the overall number of + connections. + Specifying only "ldap:" as an AliasFile specification will force + sendmail to use a default alias schema as outlined in the + ``USING LDAP FOR ALIASES, MAPS, and CLASSES'' section of + cf/README. + Add a new syntax for the 'F' (file class) sendmail.cf command. If + the first character after the class name is not a '/' or a + '|' and it contains an '@' (e.g., F{X}key@class:spec), the + rest of the line will be parsed as a map lookup. This + allows classes to be filled via a map lookup. See op.me + for more syntax information. Specifically, this can be + used for commands such as VIRTUSER_DOMAIN_FILE() to read + the list of domains via LDAP (see the ``USING LDAP FOR + ALIASES, MAPS, and CLASSES'' section of cf/README for an + example). + The new macro ${sendmailMTACluster} determines the LDAP cluster for + the default schema used in the above two items. + Unless DontBlameSendmail=RunProgramInUnsafeDirPath is set, log a + warning if a program being run from a mailer or file class + (e.g., F|/path/to/prog) is in an unsafe directory path. + Unless DontBlameSendmail=RunWritableProgram is set, log a warning + if a program being run from a mailer or file class + (e.g., F|/path/to/prog) is group or world writable. + Loopback interfaces (e.g., "lo0") are now probed for class {w} + hostnames. Setting DontProbeInterfaces to "loopback" + (without quotes) will disable this and return to the + pre-8.12 behavior of only probing non-loopback interfaces. + Suggested by Bryan Stansell of GNAC. + In accordance with RFC 2821 section 4.1.4, accept multiple + HELO/EHLO commands. + Multiple ClientPortOptions settings are now allowed, one for each + possible protocol family which may be used for outgoing + connections. Restrictions placed on one family only affect + outgoing connections on that particular family. Because of + this change, the ${client_flags} macro is not set until the + connection is established. Based on patch from Motonori + Nakamura of Kyoto University. + PrivacyOptions=restrictexpand instructs sendmail to drop privileges + when the -bv option is given by users who are neither root + nor the TrustedUser so users can not read private aliases, + forwards, or :include: files. It also will override the -v + (verbose) command line option. + If the M=b modifier is set in DaemonPortOptions and the interface + address can't be used for the outgoing connection, fall + back to the settings in ClientPortOptions (if set). + Problem noted by John Beck of Sun Microsystems. + New named config file rule check_data for DATA command (input: + number of recipients). Based on patch from Mark Roth of + the University of Illinois at Urbana-Champaign. + Add support for ETRN queue selection per RFC 1985. The queue group + can be specified using the '#' option character. For + example, 'ETRN #queuegroup'. + If an LDAP server times out or becomes unavailable, close the + current connection and reopen to get to one of the fallback + servers. Patch from Paul Hilchey of the University of + British Columbia. + Make default error number on $#error messages 550 instead of 501 + because 501 is not allowed on all commands. + The .cf file option UnsafeGroupWrites is deprecated, it should be + replaced with the settings GroupWritableForwardFileSafe + and GroupWritableIncludeFileSafe in DontBlameSendmail + if required. + The deprecated ldapx map class has been removed. Use the ldap map + class instead. + Any IPv6 addresses used in configuration should be prefixed by the + "IPv6:" tag to identify the address properly. For example, + if you want to add the IPv6 address [2002:c0a8:51d2::23f4] to + class {w}, you would need to add [IPv6:2002:c0a8:51d2::23f4]. + Change the $&{opMode} macro if the operation mode changes while the + MTA is running. For example, during a queue run. + Add "use_inet6" as a new ResolverOptions flag to control the + RES_USE_INET6 resolver option. Based on patch from Rick + Nelson of IBM. + The maximum number of commands before the MTA slows down when too + many "light weight" commands have been received are now + configurable during compile time. The current values and + their defaults are: + MAXBADCOMMANDS 25 unknown commands + MAXNOOPCOMMANDS 20 NOOP, VERB, ONEX, XUSR + MAXHELOCOMMANDS 3 HELO, EHLO + MAXVRFYCOMMANDS 6 VRFY, EXPN + MAXETRNCOMMANDS 8 ETRN + Setting a value to 0 disables the check. Patch from Bryan + Costales of SL3D, Inc. + The header syntax H?${MyMacro}?X-My-Header: now not only checks if + ${MyMacro} is defined but also that it is not empty. + Properly quote usernames with special characters if they are used + in headers. Problem noted by Kari Hurtta of the Finnish + Meteorological Institute. + Be sure to include the proper Final-Recipient: DSN header in bounce + messages for messages for mailing list expanded addresses + which are not delivered on the initial attempt. + Do not treat errors as sticky when doing delivery via LMTP after + the final dot has been sent to avoid affecting future + deliveries. Problem reported by Larry Greenfield of CMU. + New compile time flag REQUIRES_DIR_FSYNC which turns on support for + file systems that require to call fsync() for a directory + if the meta-data in it has been changed. This should be + set at least for ReiserFS; it is enabled by default for Linux. + See sendmail/README for further information. + Avoid file locking deadlock when updating the statistics file if + sendmail is signaled to terminate. Problem noted by + Christophe Wolfhugel of France Telecom. + Set the $c macro (hop count) as it is being set instead of when the + envelope is initialized. Problem noted by Kari Hurtta of + the Finnish Meteorological Institute. + Properly count recipients for DeliveryMode defer and queue. Fix + from Peter A. Friend of EarthLink. + Treat invalid hesiod lookups as permanent errors instead of + temporary errors. Problem noted by Russell McOrmond of + flora.ca. + Portability: + Remove support for AIX 2, which supports only 14 character + filenames and is outdated anyway. Suggested by + Valdis Kletnieks of Virginia Tech. + Change several settings for Irix 6: remove confSBINDIR, + i.e., use default /usr/sbin, change owner/group + of man pages and user-executable to root/sys, set + optimization limit to 0 (unlimited). Based on patch + from Ayamura Kikuchi, M.D, and proposal from Kari + Hurtta of the Finnish Meteorological Institute. + Do not assume LDAP support is installed by default under + Solaris 8 and later. + Add support for OpenUNIX. + CONFIG: Increment version number of config file to 10. + CONFIG: Add an install target and a README file in cf/cf. + CONFIG: Don't accept addresses of the form a@b@, a@b@c, a@[b]c, etc. + CONFIG: Reject empty recipient addresses (in check_rcpt). + CONFIG: The access map uses an option of -T<TMPF> to deal with + temporary lookup failures. + CONFIG: New value for access map: SKIP, which causes the default + action to be taken by aborting the search for domain names + or IP nets. + CONFIG: check_rcpt can deal with TEMPFAIL for either recipient or + relay address as long as the other part allows the email + to get through. + CONFIG: Entries for virtusertable can make use of a third parameter + "%3" which contains "+detail" of a wildcard match, i.e., an + entry like user+*@domain. This allows handling of details by + using %1%3 as the RHS. Additionally, a "+" wildcard has been + introduced to match only non-empty details of addresses. + CONFIG: Numbers for rulesets used by MAILERs have been removed + and hence there is no required order within the MAILER + section anymore except for MAILER(`uucp') which must come + after MAILER(`smtp') if uucp-dom and uucp-uudom are used. + CONFIG: Hosts listed in the generics domain class {G} + (GENERICS_DOMAIN() and GENERICS_DOMAIN_FILE()) are treated + as canonical. Suggested by Per Hedeland of Ericsson. + CONFIG: If FEATURE(`delay_checks') is used, make sure that a lookup + in the access map which returns OK or RELAY actually + terminates check_* ruleset checking. + CONFIG: New tag TLS_Rcpt: for access map to be used by ruleset + tls_rcpt, see cf/README for details. + CONFIG: Change format of Received: header line which reveals whether + STARTTLS has been used to "(version=${tls_version} + cipher=${cipher} bits=${cipher_bits} verify=${verify})". + CONFIG: Use "Spam:" as tag for lookups for FEATURE(`delay_checks') + options friends/haters instead of "To:" and enable + specification of whole domains instead of just users. + Notice: this change is not backward compatible. + Suggested by Chris Adams from HiWAAY Informations Services. + CONFIG: Allow for local extensions for most new rulesets, see + cf/README for details. + CONFIG: New FEATURE(`lookupdotdomain') to lookup also .domain in + the access map. Proposed by Randall Winchester of the + University of Maryland. + CONFIG: New FEATURE(`local_no_masquerade') to avoid masquerading for + the local mailer. Proposed by Ingo Brueckl of Wupper Online. + CONFIG: confRELAY_MSG/confREJECT_MSG can override the default + messages for an unauthorized relaying attempt/for access + map entries with RHS REJECT, respectively. + CONFIG: FEATURE(`always_add_domain') takes an optional argument + to specify another domain to be added instead of the local one. + Suggested by Richard H. Gumpertz of Computer Problem + Solving. + CONFIG: confAUTH_OPTIONS allows setting of Cyrus-SASL specific + options, see doc/op/op.me for details. + CONFIG: confAUTH_MAX_BITS sets the maximum encryption strength for + the security layer in SMTP AUTH (SASL). + CONFIG: If Local_localaddr resolves to $#ok, localaddr is terminated + immediately. + CONFIG: FEATURE(`enhdnsbl') is an enhanced version of dnsbl which + allows checking of the return values of the DNS lookups. + See cf/README for details. + CONFIG: FEATURE(`dnsbl') allows now to specify the behavior for + temporary lookup failures. + CONFIG: New option confDELIVER_BY_MIN to specify minimum time for + Deliver By (RFC 2852) or to turn off the extension. + CONFIG: New option confSHARED_MEMORY_KEY to set the key for shared + memory use. + CONFIG: New FEATURE(`compat_check') to look up a key consisting + of the sender and the recipient address delimited by the + string "<@>", e.g., sender@sdomain<@>recipient@rdomain, + in the access map. Based on code contributed by Mathias + Koerber of Singapore Telecommunications Ltd. + CONFIG: Add EXPOSED_USER_FILE() command to allow an exposed user + file. Suggested by John Beck of Sun Microsystems. + CONFIG: Don't use MAILER-DAEMON for error messages delivered + via LMTP. Problem reported by Larry Greenfield of CMU. + CONFIG: New FEATURE(`preserve_luser_host') to preserve the name of + the recipient host if LUSER_RELAY is used. + CONFIG: New FEATURE(`preserve_local_plus_detail') to preserve the + +detail portion of the address when passing address to + local delivery agent. Disables alias and .forward +detail + stripping. Only use if LDA supports this. + CONFIG: Removed deprecated FEATURE(`rbl'). + CONFIG: Add LDAPROUTE_EQUIVALENT() and LDAPROUTE_EQUIVALENT_FILE() + which allow you to specify 'equivalent' hosts for LDAP + Routing lookups. Equivalent hostnames are replaced by the + masquerade domain name for lookups. See cf/README for + additional details. + CONFIG: Add a fourth argument to FEATURE(`ldap_routing') which + instructs the rulesets on what to do if the address being + looked up has +detail information. See cf/README for more + information. + CONFIG: When chosing a new destination via LDAP Routing, also look + up the new routing address/host in the mailertable. Based + on patch from Don Badrak of the United States Census Bureau. + CONFIG: Do not reject the SMTP Mail from: command if LDAP Routing + is in use and the bounce option is enabled. Only reject + recipients as user unknown. + CONFIG: Provide LDAP support for the remaining database map + features. See the ``USING LDAP FOR ALIASES AND MAPS'' + section of cf/README for more information. + CONFIG: Add confLDAP_CLUSTER which defines the ${sendmailMTACluster} + macro used for LDAP searches as described above in ``USING + LDAP FOR ALIASES, MAPS, AND CLASSES''. + CONFIG: confCLIENT_OPTIONS has been replaced by CLIENT_OPTIONS(), + which takes the options as argument and can be used + multiple times; see cf/README for details. + CONFIG: Add configuration macros for new options: + confBAD_RCPT_THROTTLE BadRcptThrottle + confDIRECT_SUBMISSION_MODIFIERS DirectSubmissionModifiers + confMAILBOX_DATABASE MailboxDatabase + confMAIL_SUBMISSION_QUEUE MailSubmissionQueue + confMAX_QUEUE_CHILDREN MaxQueueChildren + confMAX_RUNNERS_PER_QUEUE MaxRunnersPerQueue + confNICE_QUEUE_RUN NiceQueueRun + confQUEUE_FILE_MODE QueueFileMode + confFAST_SPLIT FastSplit + confTLS_SRV_OPTIONS TLSSrvOptions + See above (and related documentation) for further information. + CONFIG: Add configuration variables for new timeout options: + confTO_ACONNECT Timeout.aconnect + confTO_AUTH Timeout.auth + confTO_LHLO Timeout.lhlo + confTO_STARTTLS Timeout.starttls + CONFIG: Add configuration macros for mail filter API: + confINPUT_MAIL_FILTERS InputMailFilters + confMILTER_LOG_LEVEL Milter.LogLevel + confMILTER_MACROS_CONNECT Milter.macros.connect + confMILTER_MACROS_HELO Milter.macros.helo + confMILTER_MACROS_ENVFROM Milter.macros.envfrom + confMILTER_MACROS_ENVRCPT Milter.macros.envrcpt + Mail filters can be defined via INPUT_MAIL_FILTER() and + MAIL_FILTER(). See libmilter/README, cf/README, and + doc/op/op.me for details. + CONFIG: Add support for accepting temporarily unresolvable domains. + See cf/README for details. Based on patch by Motonori + Nakamura of Kyoto University. + CONFIG: confDEQUOTE_OPTS can be used to specify options for the + dequote map. + CONFIG: New macro QUEUE_GROUP() to define queue groups. + CONFIG: New FEATURE(`queuegroup') to select a queue group based + on the full e-mail address or the domain of the recipient. + CONFIG: Any IPv6 addresses used in configuration should be prefixed + by the "IPv6:" tag to identify the address properly. For + example, if you want to use the IPv6 address + 2002:c0a8:51d2::23f4 in the access database, you would need + to use IPv6:2002:c0a8:51d2::23f4 on the left hand side. + This affects the access database as well as the + relay-domains and local-host-names files. + CONFIG: OSTYPE(aux) has been renamed to OSTYPE(a-ux). + CONFIG: Avoid expansion of m4 keywords in SMART_HOST. + CONFIG: Add MASQUERADE_EXCEPTION_FILE() for reading masquerading + exceptions from a file. Suggested by Trey Breckenridge of + Mississippi State University. + CONFIG: Add LOCAL_USER_FILE() for reading local users + (LOCAL_USER() -- $={L}) entries from a file. + CONTRIB: dnsblaccess.m4 is a further enhanced version of enhdnsbl.m4 + which allows to lookup error codes in the access map. + Contributed by Neil Rickert of Northern Illinois University. + DEVTOOLS: Add new options for installation of include and library + files: confINCGRP, confINCMODE, confINCOWN, confLIBGRP, + confLIBMODE, confLIBOWN. + DEVTOOLS: Add new option confDONT_INSTALL_CATMAN to turn off + installation of the the formatted man pages on operating + systems which don't include cat directories. + EDITMAP: New program for editing maps as supplement to makemap. + MAIL.LOCAL: Mail.local now uses the libsm mbdb package to look up + local mail recipients. New option -D mbdb specifies the + mailbox database type. + MAIL.LOCAL: New option "-h filename" which instructs mail.local to + deliver the mail to the named file in the user's home + directory instead of the system mail spool area. Based on + patch from Doug Hardie of the Los Angeles Free-Net. + MAILSTATS: New command line option -P which acts the same as -p but + doesn't truncate the statistics file. + MAKEMAP: Add new option -t to specify a different delimiter + instead of white space. + RMAIL: Invoke sendmail with '-G' to indicate this is a gateway + submission. Problem noted by Kari Hurtta of the Finnish + Meteorological Institute. + SMRSH: Use the vendor supplied directory on FreeBSD 3.3 and later. + VACATION: Change Auto-Submitted: header value from auto-generated to + auto-replied. From Kenneth Murchison of Oceana Matrix Ltd. + VACATION: New option -d to send error/debug messages to stdout + instead of syslog. + VACATION: New option -U which prevents the attempt to lookup login + in the password file. The -f and -m options must be used + to specify the database and message file since there is no + home directory for the default settings for these options. + VACATION: Vacation now uses the libsm mbdb package to look up + local mail recipients; it reads the MailboxDatabase option + from the sendmail.cf file. New option -C cffile which + specifies the path of the sendmail.cf file. + New Directories: + libmilter/docs + New Files: + cf/cf/README + cf/cf/submit.cf + cf/cf/submit.mc + cf/feature/authinfo.m4 + cf/feature/compat_check.m4 + cf/feature/enhdnsbl.m4 + cf/feature/msp.m4 + cf/feature/local_no_masquerade.m4 + cf/feature/lookupdotdomain.m4 + cf/feature/preserve_luser_host.m4 + cf/feature/preserve_local_plus_detail.m4 + cf/feature/queuegroup.m4 + cf/sendmail.schema + contrib/dnsblaccess.m4 + devtools/M4/UNIX/sm-test.m4 + devtools/OS/OpenUNIX.5.i386 + editmap/* + include/sm/* + libsm/* + libsmutil/cf.c + libsmutil/err.c + sendmail/SECURITY + sendmail/TUNING + sendmail/bf.c + sendmail/bf.h + sendmail/sasl.c + sendmail/sm_resolve.c + sendmail/sm_resolve.h + sendmail/tls.c + Deleted Files: + cf/feature/rbl.m4 + cf/ostype/aix2.m4 + devtools/OS/AIX.2 + include/sendmail/cdefs.h + include/sendmail/errstring.h + include/sendmail/useful.h + libsmutil/errstring.c + sendmail/bf_portable.c + sendmail/bf_portable.h + sendmail/bf_torek.c + sendmail/bf_torek.h + sendmail/clock.c + Renamed Files: + cf/cf/generic-solaris2.mc => cf/cf/generic-solaris.mc + cf/cf/generic-solaris2.cf => cf/cf/generic-solaris.cf + cf/ostype/aux.m4 => cf/ostype/a-ux.m4 + 8.11.6/8.11.6 2001/08/20 SECURITY: Fix a possible memory access violation when specifying out-of-bounds debug parameters. Problem detected by @@ -1643,7 +2318,7 @@ summary of the changes in that release. CONFIG: OSTYPE(`bsdi1.0') and OSTYPE(`bsdi2.0') have been deprecated and may be removed from a future release. BSD/OS users should begin using OSTYPE(`bsdi'). - CONFIG: OpenBSD 2.4 installs mail.local non-set-user-id root. This + CONFIG: OpenBSD 2.4 installs mail.local non-set-user-ID root. This requires a new OSTYPE(`openbsd'). From Todd C. Miller of Courtesan Consulting. CONFIG: New OSTYPE(`hpux11') for HP/UX 11.X. @@ -1903,7 +2578,7 @@ summary of the changes in that release. the others (if it exists). DEVTOOLS: Change order of LIBS: first product specific libraries then the default ones. - MAIL.LOCAL: Will not be installed set-user-id root. To use mail.local + MAIL.LOCAL: Will not be installed set-user-ID root. To use mail.local as local delivery agent without LMTP mode, use MODIFY_MAILER_FLAGS(`LOCAL', `+S') to set the S flag. @@ -2458,7 +3133,7 @@ summary of the changes in that release. uid and gid for user bin instead of daemon. If DefaultUser is set in the configuration file, that value overrides this default. - SECURITY: Since 8.8.7, the check for non-set-user-id binaries + SECURITY: Since 8.8.7, the check for non-set-user-ID binaries interfered with setting an alternate group id for the RunAsUser option. Problem noted by Randall Winchester of the University of Maryland. @@ -3126,7 +3801,7 @@ summary of the changes in that release. In some cases, errors during an SMTP session could leave files open or locked. Better handling of missing file descriptors (0, 1, 2) on startup. - Better handling of non-set-user-id binaries -- avoids certain obnoxious + Better handling of non-set-user-ID binaries -- avoids certain obnoxious errors during testing. Errors in file locking of NEWDB maps had the incorrect file name printed in the error message. @@ -3518,7 +4193,7 @@ summary of the changes in that release. change to the sendmail map code was made in 8.8.3. Problem noted by Gregory Neil Shapiro. MAKEMAP: Give warnings on file problems such as map files that are - symbolic links; although makemap is not set-user-id root, it is + symbolic links; although makemap is not set-user-ID root, it is often run as root and hence has the potential for the same sorts of problems as alias rebuilds. MAKEMAP: Change compilation so that it will link properly on @@ -4304,7 +4979,7 @@ summary of the changes in that release. Fix problem finding network interface addresses. Patch from Motonori Nakamura. Don't reject qf entries that are not owned by your effective uid if - you are not running set-user-id; this makes management of + you are not running set-user-ID; this makes management of certain kinds of firewall setups difficult. Patch suggested by Eamonn Coleman of Qualcomm. Add persistent host status. This keeps the information normally @@ -4674,7 +5349,7 @@ summary of the changes in that release. failure in the hosts.files map. This error caused hard bounces when it should have requeued. Aliases to files such as /users/bar/foo/inbox, with /users/bar/foo - owned by bar mode 700 and inbox being set-user-id bar stopped + owned by bar mode 700 and inbox being set-user-ID bar stopped working properly due to excessive paranoia. Pointed out by John Hawkinson of Panix. An SMTP RCPT command referencing a host that gave a nameserver @@ -5348,7 +6023,7 @@ summary of the changes in that release. the aliases file: use the default uid/gid instead of the real uid/gid. This allows you to create a file owned by and writable only by the default uid/gid that will work - all the time (without having the set-user-id bit set). Change + all the time (without having the set-user-ID bit set). Change suggested by Shau-Ping Lo and Andrew Cheng of Sun Microsystems. Add "DialDelay" option (no short name) to provide an "extra" diff --git a/gnu/usr.sbin/sendmail/cf/README b/gnu/usr.sbin/sendmail/cf/README index 0d3964aa208..885a650f6c1 100644 --- a/gnu/usr.sbin/sendmail/cf/README +++ b/gnu/usr.sbin/sendmail/cf/README @@ -1,28 +1,50 @@ SENDMAIL CONFIGURATION FILES -This document describes the sendmail configuration files. This package -requires a post-V7 version of m4; if you are running the 4.2bsd, SysV.2, or -7th Edition version. SunOS's /usr/5bin/m4 or BSD-Net/2's m4 both work. -GNU m4 version 1.1 or later also works. Unfortunately, the M4 on BSDI 1.0 -doesn't work -- you'll have to use a Net/2 or GNU version. GNU m4 is -available from ftp://ftp.gnu.org/pub/gnu/m4/m4-1.4.tar.gz (check for the -latest version). EXCEPTIONS: DEC's m4 on Digital UNIX 4.x is broken (3.x -is fine). Use GNU m4 on this platform. - -To get started, you may want to look at tcpproto.mc (for TCP-only sites), -uucpproto.mc (for UUCP-only sites), and clientproto.mc (for clusters of -clients using a single mail host). Others are versions previously used at -Berkeley. For example, ucbvax has gone away, but ucbvax.mc demonstrates -some interesting techniques. - -******************************************************************* -*** BE SURE YOU CUSTOMIZE THESE FILES! They have some *** -*** Berkeley-specific assumptions built in, such as the name *** -*** of their UUCP-relay. You'll want to create your own *** -*** domain description, and use that in place of *** -*** domain/Berkeley.EDU.m4. *** -******************************************************************* +This document describes the sendmail configuration files. It +explains how to create a sendmail.cf file for use with sendmail. +It also describes how to set options for sendmail which are explained +in the Sendmail Installation and Operation guide (doc/op/op.me). + +To get started, you may want to look at tcpproto.mc (for TCP-only +sites) and clientproto.mc (for clusters of clients using a single +mail host), or the generic-*.mc files as operating system-specific +examples. + +Table of Content: + +INTRODUCTION AND EXAMPLE +A BRIEF INTRODUCTION TO M4 +FILE LOCATIONS +OSTYPE +DOMAINS +MAILERS +FEATURES +HACKS +SITE CONFIGURATION +USING UUCP MAILERS +TWEAKING RULESETS +MASQUERADING AND RELAYING +USING LDAP FOR ALIASES, MAPS, AND CLASSES +LDAP ROUTING +ANTI-SPAM CONFIGURATION CONTROL +STARTTLS +SMTP AUTHENTICATION +ADDING NEW MAILERS OR RULESETS +ADDING NEW MAIL FILTERS +QUEUE GROUP DEFINITIONS +NON-SMTP BASED CONFIGURATIONS +WHO AM I? +ACCEPTING MAIL FOR MULTIPLE NAMES +USING MAILERTABLES +USING USERDB TO MAP FULL NAMES +MISCELLANEOUS SPECIAL FEATURES +SECURITY NOTES +TWEAKING CONFIGURATION OPTIONS +MESSAGE SUBMISSION PROGRAM +FORMAT OF FILES AND MAPS +DIRECTORY LAYOUT +ADMINISTRATIVE DETAILS +--------------------------+ @@ -106,11 +128,10 @@ definition appropriate for your environment. MAILER(`local') MAILER(`smtp') -These describe the mailers used at the default CS site. The -local mailer is always included automatically. Beware: MAILER -declarations should always be at the end of the configuration file, -and MAILER(`smtp') should always precede MAILER(`procmail'), and -MAILER(`uucp'). The general rules are that the order should be: +These describe the mailers used at the default CS site. The local +mailer is always included automatically. Beware: MAILER declarations +should always be at the end of the configuration file. The general +rules are that the order should be: VERSIONID OSTYPE @@ -118,6 +139,7 @@ MAILER(`uucp'). The general rules are that the order should be: FEATURE local macro definitions MAILER + LOCAL_CONFIG LOCAL_RULE_* LOCAL_RULESETS @@ -126,6 +148,14 @@ influence a FEATURE() should be done before that feature. For example, a define(`PROCMAIL_MAILER_PATH', ...) should be done before FEATURE(`local_procmail'). +******************************************************************* +*** BE SURE YOU CUSTOMIZE THESE FILES! They have some *** +*** Berkeley-specific assumptions built in, such as the name *** +*** of their UUCP-relay. You'll want to create your own *** +*** domain description, and use that in place of *** +*** domain/Berkeley.EDU.m4. *** +******************************************************************* + +----------------------------+ | A BRIEF INTRODUCTION TO M4 | @@ -159,6 +189,20 @@ expanded. This also applies to because ``define'' is an M4 keyword. If you want to use them, surround them with directed quotes, `like this'. + +Notice: +------- + +This package requires a post-V7 version of m4; if you are running the +4.2bsd, SysV.2, or 7th Edition version. SunOS's /usr/5bin/m4 or +BSD-Net/2's m4 both work. GNU m4 version 1.1 or later also works. +Unfortunately, the M4 on BSDI 1.0 doesn't work -- you'll have to use a +Net/2 or GNU version. GNU m4 is available from +ftp://ftp.gnu.org/pub/gnu/m4/m4-1.4.tar.gz (check for the latest version). +EXCEPTIONS: DEC's m4 on Digital UNIX 4.x is broken (3.x is fine). Use GNU +m4 on this platform. + + +----------------+ | FILE LOCATIONS | +----------------+ @@ -265,7 +309,10 @@ QUEUE_DIR [/var/spool/mqueue] The directory containing directories. The names 'qf', 'df', and 'xf' are reserved as specific subdirectories for the corresponding queue file types as explained in - doc/op/op.me. + doc/op/op.me. See also QUEUE GROUP DEFINITIONS. +MSP_QUEUE_DIR [/var/spool/clientmqueue] The directory containing + queue files for the MSP (Mail Submission Program, + see sendmail/SECURITY). STATUS_FILE [/etc/mail/statistics] The file containing status information. LOCAL_MAILER_PATH [/bin/mail] The program used to deliver local mail. @@ -294,6 +341,7 @@ LOCAL_SHELL_ARGS [sh -c $u] The arguments passed to deliver "prog" mail. LOCAL_SHELL_DIR [$z:/] The directory search path in which the shell should run. +LOCAL_MAILER_QGRP [undefined] The queue group for the local mailer. USENET_MAILER_PATH [/usr/lib/news/inews] The name of the program used to submit news. USENET_MAILER_FLAGS [rsDFMmn] The mailer flags for the usenet mailer. @@ -301,6 +349,7 @@ USENET_MAILER_ARGS [-m -h -n] The command line arguments for the usenet mailer. USENET_MAILER_MAX [100000] The maximum size of messages that will be accepted by the usenet mailer. +USENET_MAILER_QGRP [undefined] The queue group for the usenet mailer. SMTP_MAILER_FLAGS [undefined] Flags added to SMTP mailer. Default flags are `mDFMuX' for all SMTP-based mailers; the "esmtp" mailer adds `a'; "smtp8" adds `8'; and @@ -322,6 +371,11 @@ ESMTP_MAILER_ARGS [TCP $h] The arguments passed to the esmtp mailer. SMTP8_MAILER_ARGS [TCP $h] The arguments passed to the smtp8 mailer. DSMTP_MAILER_ARGS [TCP $h] The arguments passed to the dsmtp mailer. RELAY_MAILER_ARGS [TCP $h] The arguments passed to the relay mailer. +SMTP_MAILER_QGRP [undefined] The queue group for the smtp mailer. +ESMTP_MAILER_QGRP [undefined] The queue group for the esmtp mailer. +SMTP8_MAILER_QGRP [undefined] The queue group for the smtp8 mailer. +DSMTP_MAILER_QGRP [undefined] The queue group for the dsmtp mailer. +RELAY_MAILER_QGRP [undefined] The queue group for the relay mailer. RELAY_MAILER_MAXMSGS [undefined] If defined, the maximum number of messages to deliver in a single connection for the relay mailer. @@ -341,6 +395,7 @@ UUCP_MAILER_CHARSET [undefined] If defined, messages containing 8-bit data that ARRIVE from an address that resolves to one of the UUCP mailers and which are converted to MIME will be labeled with this character set. +UUCP_MAILER_QGRP [undefined] The queue group for the UUCP mailers. FAX_MAILER_PATH [/usr/local/lib/fax/mailfax] The program used to submit FAX messages. FAX_MAILER_ARGS [mailfax $u $h $f] The arguments passed to the FAX @@ -397,6 +452,7 @@ QPAGE_MAILER_ARGS [qpage -l0 -m -P$u] The arguments passed to deliver qpage mail. QPAGE_MAILER_MAX [4096] If set, the maximum size message that will be accepted by the qpage mailer. +LOCAL_PROG_QGRP [undefined] The queue group for the prog mailer. Note: to tweak Name_MAILER_FLAGS use the macro MODIFY_MAILER_FLAGS: MODIFY_MAILER_FLAGS(`Name', `change') where Name is the first part of @@ -436,7 +492,7 @@ LOCAL_RELAY The site that will handle unqualified names -- that is, names without an @domain extension. Normally MAIL_HUB is preferred for this function. LOCAL_RELAY is mostly useful in conjunction with - FEATURE(stickyhost) -- see the discussion of + FEATURE(`stickyhost') -- see the discussion of stickyhost below. If not set, they are assumed to belong on this machine. This allows you to have a central site to store a company- or department-wide @@ -466,18 +522,14 @@ single machine sitting off somewhere, it is probably more work than it's worth. This is just a mechanism for combining "domain dependent knowledge" into one place. + +---------+ | MAILERS | +---------+ There are fewer mailers supported in this version than the previous version, owing mostly to a simpler world. As a general rule, put the -MAILER definitions last in your .mc file, and always put MAILER(`smtp') -before MAILER(`uucp') and MAILER(`procmail') -- several features and -definitions will modify the definition of mailers, and the smtp mailer -modifies the UUCP mailer. Moreover, MAILER(`cyrus'), MAILER(`pop'), -MAILER(`phquery'), and MAILER(`usenet') must be defined after -MAILER(`local'). +MAILER definitions last in your .mc file. local The local and prog mailers. You will almost always need these; the only exception is if you relay ALL @@ -502,9 +554,9 @@ uucp The UNIX-to-UNIX Copy Program mailer. Actually, this "uucp-new" (a.k.a. "suucp"). The latter is for when you know that the UUCP mailer at the other end can handle multiple recipients in one transfer. If the smtp mailer - is also included in your configuration, two other mailers - ("uucp-dom" and "uucp-uudom") are also defined [warning: - you MUST specify MAILER(smtp) before MAILER(uucp)]. When you + is included in your configuration, two other mailers + ("uucp-dom" and "uucp-uudom") are also defined [warning: you + MUST specify MAILER(`smtp') before MAILER(`uucp')]. When you include the uucp mailer, sendmail looks for all names in class {U} and sends them to the uucp-old mailer; all names in class {Y} are sent to uucp-new; and all @@ -545,6 +597,9 @@ procmail An interface to procmail (does not come with sendmail). If you use this with FEATURE(`local_procmail'), the FEATURE should be listed first. + Of course there are other ways to solve this particular + problem, e.g., a catch-all entry in a virtusertable. + mail11 The DECnet mail11 mailer, useful only if you have the mail11 program from gatekeeper.dec.com:/pub/DEC/gwtools (and DECnet, of course). This is for Phase IV DECnet support; @@ -558,11 +613,12 @@ phquery The phquery program. This is somewhat counterintuitively cyrus The cyrus and cyrusbb mailers. The cyrus mailer delivers to a local cyrus user. this mailer can make use of the - "user+detail@local.host" syntax; it will deliver the mail to - the user's "detail" mailbox if the mailbox's ACL permits. - The cyrusbb mailer delivers to a system-wide cyrus mailbox - if the mailbox's ACL permits. The cyrus mailer must be - defined after the local mailer. + "user+detail@local.host" syntax (see + FEATURE(`preserve_local_plus_detail')); it will deliver the + mail to the user's "detail" mailbox if the mailbox's ACL + permits. The cyrusbb mailer delivers to a system-wide + cyrus mailbox if the mailbox's ACL permits. The cyrus + mailer must be defined after the local mailer. qpage A mailer for QuickPage, a pager interface. See http://www.qpage.org/ for further information. @@ -585,7 +641,7 @@ example, the .mc line: FEATURE(`use_cw_file') tells sendmail that you want to have it read an /etc/mail/local-host-names -file to get values for class {w}. The FEATURE may contain up to 9 +file to get values for class {w}. A FEATURE may contain up to 9 optional parameters -- for example: FEATURE(`mailertable', `dbm /usr/lib/mailertable') @@ -600,6 +656,11 @@ if you specify an argument to a FEATURE. DATABASE_MAP_TYPE is only used if no argument is given for the FEATURE. It must be specified before any feature that uses a map. +Also, features which can take a map definition as an argument can also take +the special keyword `LDAP'. If that keyword is used, the map will use the +LDAP definition described in the ``USING LDAP FOR ALIASES, MAPS, AND +CLASSES'' section below. + Available features are: use_cw_file Read the file /etc/mail/local-host-names file to get @@ -627,7 +688,7 @@ nouucp Don't route UUCP addresses. This feature takes one part unless it originates from a system that is allowed to relay. `nospecial': don't do anything special with "!". - Warnings: 1. See the NOTICE in the ANTI-SPAM section. + Warnings: 1. See the notice in the anti-spam section. 2. don't remove "!" from OperatorChars if `reject' is given as parameter. @@ -752,7 +813,8 @@ always_add_domain mail. Normally it is not added on unqualified names. However, if you use a shared message store but do not use the same user name space everywhere, you may need the host - name on local names. + name on local names. An optional argument specifies + another domain to be added than the local. allmasquerade If masquerading is enabled (using MASQUERADE_AS), this feature will cause recipient addresses to also masquerade @@ -793,18 +855,26 @@ masquerade_entire_domain NOTE: only domains within your jurisdiction and current hierarchy should be masqueraded using this. +local_no_masquerade + This feature prevents the local mailer from masquerading even + if MASQUERADE_AS is used. MASQUERADE_AS will only have effect + on addresses of mail going outside the local domain. + genericstable This feature will cause unqualified addresses (i.e., without a domain) and addresses with a domain listed in class {G} to be looked up in a map and turned into another ("generic") form, which can change both the domain name and the user name. - This is similar to the userdb functionality. The same types of - addresses as for masquerading are looked up, i.e., only header - sender addresses unless the allmasquerade and/or - masquerade_envelope features are given. Qualified addresses - must have the domain part in class {G}; entries can - be added to this class by the macros GENERICS_DOMAIN or - GENERICS_DOMAIN_FILE (analogously to MASQUERADE_DOMAIN and - MASQUERADE_DOMAIN_FILE, see below). + Notice: if you use an MSP (as it is default starting with + 8.12), the MTA will only receive qualified addresses from the + MSP (as required by the RFCs). Hence you need to add your + domain to class {G}. This feature is similar to the userdb + functionality. The same types of addresses as for + masquerading are looked up, i.e., only header sender + addresses unless the allmasquerade and/or masquerade_envelope + features are given. Qualified addresses must have the domain + part in class {G}; entries can be added to this class by the + macros GENERICS_DOMAIN or GENERICS_DOMAIN_FILE (analogously + to MASQUERADE_DOMAIN and MASQUERADE_DOMAIN_FILE, see below). The argument of FEATURE(`genericstable') may be the map definition; the default map definition is: @@ -840,7 +910,7 @@ virtusertable A domain-specific form of aliasing, allowing multiple info@foo.com foo-info info@bar.com bar-info joe@bar.com error:nouser No such user here - jax@bar.com error:D.S.N:unavailable Address invalid + jax@bar.com error:5.7.0:unavailable Address invalid @baz.org jane@example.net then mail addressed to info@foo.com will be sent to the @@ -849,7 +919,7 @@ virtusertable A domain-specific form of aliasing, allowing multiple will be sent to jane@example.net, mail to joe@bar.com will be rejected with the specified error message, and mail to jax@bar.com will also have a RFC 1893 compliant error code - D.S.N. + 5.7.0. The username from the original address is passed as %1 allowing: @@ -858,19 +928,24 @@ virtusertable A domain-specific form of aliasing, allowing multiple meaning someone@foo.org will be sent to someone@example.com. Additionally, if the local part consists of "user+detail" - then "detail" is passed as %2 when a match against user+* - is attempted, so entries like + then "detail" is passed as %2 and "+detail" is passed as %3 + when a match against user+* is attempted, so entries like old+*@foo.org new+%2@example.com gen+*@foo.org %2@example.com - +*@foo.org %1+%2@example.com + +*@foo.org %1%3@example.com + X++@foo.org Z%3@example.com + @bar.org %1%3 and other forms are possible. Note: to preserve "+detail" - for a default case (@domain) +*@domain must be used as - exemplified above. + for a default case (@domain) %1%3 must be used as RHS. + There are two wildcards after "+": "+" matches only a non-empty + detail, "*" matches also empty details, e.g., user+@foo.org + matches +*@foo.org but not ++@foo.org. This can be used + to ensure that the parameters %2 and %3 are not empty. All the host names on the left hand side (foo.com, bar.com, - and baz.org) must be in class {w} or class {VirtHost}, the + and baz.org) must be in class {w} or class {VirtHost}. The latter can be defined by the macros VIRTUSER_DOMAIN or VIRTUSER_DOMAIN_FILE (analogously to MASQUERADE_DOMAIN and MASQUERADE_DOMAIN_FILE, see below). If VIRTUSER_DOMAIN or @@ -1025,13 +1100,13 @@ relay_based_on_MX relay_mail_from Allows relaying if the mail sender is listed as RELAY in the access map. If an optional argument `domain' is given, - the domain portion of the mail sender is checked too. - This should only be used if absolutely necessary as the - sender address can be easily forged. Use of this feature - requires the "From:" tag be prepended to the key in the - access map; see the discussion of tags and - FEATURE(`relay_mail_from') in the section on ANTI-SPAM - CONFIGURATION CONTROL. + relaying can be allowed just based on the domain portion + of the sender address. This feature should only be used if + absolutely necessary as the sender address can be easily + forged. Use of this feature requires the "From:" tag be + prepended to the key in the access map; see the discussion + of tags and FEATURE(`relay_mail_from') in the section on + anti-spam configuration control. relay_local_from Allows relaying if the domain portion of the mail sender @@ -1066,13 +1141,15 @@ accept_unresolvable_domains access_db Turns on the access database feature. The access db gives you the ability to allow or refuse to accept mail from - specified domains for administrative reasons. By default, - the access database specification is: + specified domains for administrative reasons. Moreover, + it can control the behavior of sendmail in various situations. + By default, the access database specification is: - hash /etc/mail/access + hash -T<TMPF> /etc/mail/access - The format of the database is described in the anti-spam - configuration control section later in this document. + See the anti-spam configuration control section for further + important information about this feature. Notice: + "-T<TMPF>" is meant literal, do not replace it by anything. blacklist_recipients Turns on the ability to block incoming mail for certain @@ -1087,25 +1164,27 @@ delay_checks The rulesets check_mail and check_relay will not be called when a client connects or issues a MAIL command, respectively. Instead, those rulesets will be called by the check_rcpt ruleset; they will be skipped under certain circumstances. - See "Delay all checks" in "ANTI-SPAM CONFIGURATION CONTROL". - -rbl This feature is deprecated! Please use dnsbl instead. - Turns on rejection of hosts found in the Realtime Blackhole - List. If an argument is provided it is used as the domain - in which blocked hosts are listed; otherwise, the main RBL - domain rbl.maps.vix.com is used (see NOTE below). For - details, see http://maps.vix.com/rbl/. + See "Delay all checks" in the anti-spam configuration control + section. Note: this feature is incompatible to the versions + in 8.10 and 8.11. dnsbl Turns on rejection of hosts found in an DNS based rejection list. If an argument is provided it is used as the domain in which blocked hosts are listed; otherwise it defaults to blackholes.mail-abuse.org. An explanation for an DNS based - rejection list can be found http://mail-abuse.org/rbl/. A - second argument can be used to change the default error - message of Mail from $&{client_addr} refused by blackhole site - SERVER where SERVER is replaced by the first argument. This - feature can be included several times to query different DNS - based rejection lists. + rejection list can be found at http://mail-abuse.org/rbl/. + A second argument can be used to change the default error + message. Without that second argument, the error message + will be + Mail from IP-ADDRESS refused by blackhole site SERVER + where IP-ADDRESS and SERVER are replaced by the appropriate + information. By default, temporary lookup failures are + ignored. This behavior can be changed by specifying a + third argument, which must be either `t' or a full error + message. See the anti-spam configuration control section for + an example. The dnsbl feature can be included several times + to query different DNS based rejection lists. See also + enhdnsbl for an enhanced version. NOTE: The default DNS blacklist, blackholes.mail-abuse.org, is a service offered by the Mail Abuse Prevention System @@ -1114,6 +1193,30 @@ dnsbl Turns on rejection of hosts found in an DNS based rejection haven't subscribed. Contact MAPS to subscribe (http://mail-abuse.org/). +enhdnsbl Enhanced version of dnsbl (see above). Further arguments + (up to 5) can be used to specify specific return values + from lookups. Temporary lookup failures are ignored unless + a third argument is given, which must be either `t' or a full + error message. By default, any successful lookup will + generate an error. Otherwise the result of the lookup is + compared with the supplied argument(s), and only if a match + occurs an error is generated. For example, + + FEATURE(`enhdnsbl', `dnsbl.example.com', `', `t', `127.0.0.2.') + + will reject the e-mail if the lookup returns the value + ``127.0.0.2.'', or generate a 451 response if the lookup + temporarily failed. The arguments can contain metasymbols + as they are allowed in the LHS of rules. As the example + shows, the default values are also used if an empty argument, + i.e., `', is specified. This feature requires that sendmail + has been compiled with the flag DNSMAP (see sendmail/README). + +lookupdotdomain Look up also .domain in the access map. This allows to + match only subdomains. It does not work well with + FEATURE(`relay_hosts_only'), because most lookups for + subdomains are suppressed by the latter feature. + loose_relay_check Normally, if % addressing is used for a recipient, e.g. user%site@othersite, and othersite is in class {R}, the @@ -1121,11 +1224,65 @@ loose_relay_check user@site for relaying. This feature changes that behavior. It should not be needed for most installations. +authinfo Provide a separate map for client side authentication + information. See SMTP AUTHENTICATION for details. + By default, the authinfo database specification is: + + hash /etc/mail/authinfo + +preserve_luser_host + Preserve the name of the recipient host if LUSER_RELAY + is used. Without this option, the domain part of the + recipient address will be replaced by the host specified + as LUSER_RELAY. + +preserve_local_plus_detail + Preserve the +detail portion of the address when passing + address to local delivery agent. Disables alias and + .forward +detail stripping (e.g., given user+detail, only + that address will be looked up in the alias file; user+* and + user will not be looked up). Only use if the local + delivery agent in use supports +detail addressing. + +compat_check Enable ruleset check_compat to look up pairs of addresses + sender<@>recipient in the access map. Valid values for + the RHS include + DISCARD silently discard message + TEMP: return a temporary error + ERROR: return a permanent error + In the last two cases, a 4xy/5xy SMTP reply code should + follow the colon. + no_default_msa Don't generate the default MSA daemon, i.e., DAEMON_OPTIONS(`Port=587,Name=MSA,M=E') To define a MSA daemon with other parameters, use this FEATURE and introduce new settings via DAEMON_OPTIONS(). +msp Defines config file for Message Submission Program. + See sendmail/SECURITY for details and cf/cf/submit.mc + how to use it. An optional argument can be used to + override the default of `localhost' to use as host to send + all e-mails to. If `MSA' is specified as second argument + then port 587 is used to contact the server. Example: + + FEATURE(`msp, `', `MSA') + + Some more hints about possible changes can be found below + in the section MESSAGE SUBMISSION PROGRAM. + +queuegroup A simple example how to select a queue group based + on the full e-mail address or the domain of the + recipient. Selection is done via entries in the + access map using the tag QGRP:, for example: + + QGRP:example.com main + QGRP:friend@some.org others + QGRP:my.domain local + + where "main", "others", and "local" are names of + queue groups. If an argument is specified, it is used + as default queue group. + +-------+ | HACKS | +-------+ @@ -1146,7 +1303,7 @@ subdomains. ***************************************************** * This section is really obsolete, and is preserved * * only for back compatibility. You should plan on * - * using mailertables for new installations. In * + * using mailertables for new installations. In * * particular, it doesn't work for the newer forms * * of UUCP mailers, such as uucp-uudom. * ***************************************************** @@ -1237,7 +1394,8 @@ The four mailers are: uucp-dom This UUCP mailer keeps everything as domain addresses. Basically, it uses the SMTP mailer rewriting rules. This mailer - is only included if MAILER(`smtp') is also specified. + is only included if MAILER(`smtp') is specified before + MAILER(`uucp'). Unfortunately, a lot of UUCP mailer transport agents require bangified addresses in the envelope, although you can use @@ -1252,7 +1410,7 @@ The four mailers are: at all (e.g., "wolf") or the host component is a UUCP host name instead of a domain name ("somehost!wolf" instead of "some.dom.ain!wolf"). This is also included only if MAILER(`smtp') - is also specified. + is also specified earlier. Examples: @@ -1378,7 +1536,10 @@ To exempt hosts or subdomains from being masqueraded, you can use MASQUERADE_EXCEPTION(`host.domain') This can come handy if you want to masquerade a whole domain -except for one (or a few) host(s). +except for one (or a few) host(s). If these names are in a file, +you can use + + MASQUERADE_EXCEPTION_FILE(`filename') Normally only header addresses are masqueraded. If you want to masquerade the envelope as well, use @@ -1392,9 +1553,9 @@ You can add users to this list using EXPOSED_USER(`usernames') -This adds users to class {E}; you could also use something like +This adds users to class {E}; you could also use - FE/etc/mail/exposed-users + EXPOSED_USER_FILE(`filename') You can also arrange to relay all unqualified names (that is, names without @host) to a relay host. For example, if you have a central @@ -1410,9 +1571,9 @@ locally aliased. You can add entries to this list using LOCAL_USER(`usernames') -This adds users to class {L}; you could also use something like +This adds users to class {L}; you could also use - FL/etc/mail/local-users + LOCAL_USER_FILE(`filename') If you want all incoming mail sent to a centralized hub, as for a shared /var/spool/mail scheme, use @@ -1468,6 +1629,290 @@ specified with a terminal dot: note the trailing dot ---^ ++-------------------------------------------+ +| USING LDAP FOR ALIASES, MAPS, AND CLASSES | ++-------------------------------------------+ + +LDAP can be used for aliases, maps, and classes by either specifying your +own LDAP map specification or using the built-in default LDAP map +specification. The built-in default specifications all provide lookups +which match against either the machine's fully qualified hostname (${j}) or +a "cluster". The cluster allows you to share LDAP entries among a large +number of machines without having to enter each of the machine names into +each LDAP entry. To set the LDAP cluster name to use for a particular +machine or set of machines, set the confLDAP_CLUSTER m4 variable to a +unique name. For example: + + define(`confLDAP_CLUSTER', `Servers') + +Here, the word `Servers' will be the cluster name. As an example, assume +that smtp.sendmail.org, etrn.sendmail.org, and mx.sendmail.org all belong +to the Servers cluster. + +Some of the LDAP LDIF examples below show use of the Servers cluster. +Every entry must have either a sendmailMTAHost or sendmailMTACluster +attribute or it will be ignored. Be careful as mixing clusters and +individual host records can have surprising results (see the CAUTION +sections below). + +See the file cf/sendmail.schema for the actual LDAP schemas. Note that +this schema (and therefore the lookups and examples below) is experimental +at this point as it has had little public review. Therefore, it may change +in future versions. Feedback via sendmail@sendmail.org is encouraged. + +------- +Aliases +------- + +The ALIAS_FILE (O AliasFile) option can be set to use LDAP for alias +lookups. To use the default schema, simply use: + + define(`ALIAS_FILE', `ldap:') + +By doing so, you will use the default schema which expands to a map +declared as follows: + + ldap -k (&(objectClass=sendmailMTAAliasObject) + (sendmailMTAAliasGrouping=aliases) + (|(sendmailMTACluster=${sendmailMTACluster}) + (sendmailMTAHost=$j)) + (sendmailMTAKey=%0)) + -v sendmailMTAAliasValue + +NOTE: The macros shown above ${sendmailMTACluster} and $j are not actually +used when the binary expands the `ldap:' token as the AliasFile option is +not actually macro-expanded when read from the sendmail.cf file. + +Example LDAP LDIF entries might be: + + dn: sendmailMTAKey=sendmail-list, dc=sendmail, dc=org + objectClass: sendmailMTA + objectClass: sendmailMTAAlias + objectClass: sendmailMTAAliasObject + sendmailMTAAliasGrouping: aliases + sendmailMTAHost: etrn.sendmail.org + sendmailMTAKey: sendmail-list + sendmailMTAAliasValue: ca@example.org + sendmailMTAAliasValue: eric + sendmailMTAAliasValue: gshapiro@example.com + + dn: sendmailMTAKey=owner-sendmail-list, dc=sendmail, dc=org + objectClass: sendmailMTA + objectClass: sendmailMTAAlias + objectClass: sendmailMTAAliasObject + sendmailMTAAliasGrouping: aliases + sendmailMTAHost: etrn.sendmail.org + sendmailMTAKey: owner-sendmail-list + sendmailMTAAliasValue: eric + + dn: sendmailMTAKey=postmaster, dc=sendmail, dc=org + objectClass: sendmailMTA + objectClass: sendmailMTAAlias + objectClass: sendmailMTAAliasObject + sendmailMTAAliasGrouping: aliases + sendmailMTACluster: Servers + sendmailMTAKey: postmaster + sendmailMTAAliasValue: eric + +Here, the aliases sendmail-list and owner-sendmail-list will be available +only on etrn.sendmail.org but the postmaster alias will be available on +every machine in the Servers cluster (including etrn.sendmail.org). + +CAUTION: aliases are additive so that entries like these: + + dn: sendmailMTAKey=bob, dc=sendmail, dc=org + objectClass: sendmailMTA + objectClass: sendmailMTAAlias + objectClass: sendmailMTAAliasObject + sendmailMTAAliasGrouping: aliases + sendmailMTACluster: Servers + sendmailMTAKey: bob + sendmailMTAAliasValue: eric + + dn: sendmailMTAKey=bob, dc=sendmail, dc=org + objectClass: sendmailMTA + objectClass: sendmailMTAAlias + objectClass: sendmailMTAAliasObject + sendmailMTAAliasGrouping: aliases + sendmailMTAHost: etrn.sendmail.org + sendmailMTAKey: bob + sendmailMTAAliasValue: gshapiro + +would mean that on all of the hosts in the cluster, mail to bob would go to +eric EXCEPT on etrn.sendmail.org in which case it would go to BOTH eric and +gshapiro. + +If you prefer not to use the default LDAP schema for your aliases, you can +specify the map parameters when setting ALIAS_FILE. For example: + + define(`ALIAS_FILE', `ldap:-k (&(objectClass=mailGroup)(mail=%0)) -v mgrpRFC822MailMember') + +---- +Maps +---- + +FEATURE()'s which take an optional map definition argument (e.g., access, +mailertable, virtusertable, etc.) can instead take the special keyword +`LDAP', e.g.: + + FEATURE(`access_db', `LDAP') + FEATURE(`virtusertable', `LDAP') + +When this keyword is given, that map will use LDAP lookups consisting of +the objectClass sendmailMTAClassObject, the attribute sendmailMTAMapName +with the map name, a search attribute of sendmailMTAKey, and the value +attribute sendmailMTAMapValue. + +The values for sendmailMTAMapName are: + + FEATURE() sendmailMTAMapName + --------- ------------------ + access_db access + authinfo authinfo + bitdomain bitdomain + domaintable domain + genericstable generics + mailertable mailer + uucpdomain uucpdomain + virtusertable virtuser + +For example, FEATURE(`mailertable', `LDAP') would use the map definition: + + Kmailertable ldap -k (&(objectClass=sendmailMTAMapObject) + (sendmailMTAMapName=mailer) + (|(sendmailMTACluster=${sendmailMTACluster}) + (sendmailMTAHost=$j)) + (sendmailMTAKey=%0)) + -1 -v sendmailMTAMapValue + +An example LDAP LDIF entry using this map might be: + + dn: sendmailMTAMapName=mailer, dc=sendmail, dc=org + objectClass: sendmailMTA + objectClass: sendmailMTAMap + sendmailMTACluster: Servers + sendmailMTAMapName: mailer + + dn: sendmailMTAKey=example.com, sendmailMTAMapName=mailer, dc=sendmail, dc=org + objectClass: sendmailMTA + objectClass: sendmailMTAMap + objectClass: sendmailMTAMapObject + sendmailMTAMapName: mailer + sendmailMTACluster: Servers + sendmailMTAKey: example.com + sendmailMTAMapValue: relay:[smtp.example.com] + +CAUTION: If your LDAP database contains the record above and *ALSO* a host +specific record such as: + + dn: sendmailMTAKey=example.com@etrn, sendmailMTAMapName=mailer, dc=sendmail, dc=org + objectClass: sendmailMTA + objectClass: sendmailMTAMap + objectClass: sendmailMTAMapObject + sendmailMTAMapName: mailer + sendmailMTAHost: etrn.sendmail.org + sendmailMTAKey: example.com + sendmailMTAMapValue: relay:[mx.example.com] + +then these entries will give unexpected results. When the lookup is done +on etrn.sendmail.org, the effect is that there is *NO* match at all as maps +require a single match. Since the host etrn.sendmail.org is also in the +Servers cluster, LDAP would return two answers for the example.com map key +in which case sendmail would treat this as no match at all. + +If you prefer not to use the default LDAP schema for your maps, you can +specify the map parameters when using the FEATURE(). For example: + + FEATURE(`access_db', `ldap:-1 -k (&(objectClass=mapDatabase)(key=%0)) -v value') + +------- +Classes +------- + +Normally, classes can be filled via files or programs. As of 8.12, they +can also be filled via map lookups using a new syntax: + + F{ClassName}mapkey@mapclass:mapspec + +mapkey is optional and if not provided the map key will be empty. This can +be used with LDAP to read classes from LDAP. Note that the lookup is only +done when sendmail is initially started. Use the special value `@LDAP' to +use the default LDAP schema. For example: + + RELAY_DOMAIN_FILE(`@LDAP') + +would put all of the attribute sendmailMTAClassValue values of LDAP records +with objectClass sendmailMTAClass and an attribute sendmailMTAClassName of +'R' into class $={R}. In other words, it is equivalent to the LDAP map +specification: + + F{R}@ldap:-k (&(objectClass=sendmailMTAClass) + (sendmailMTAClassName=R) + (|(sendmailMTACluster=${sendmailMTACluster}) + (sendmailMTAHost=$j))) + -v sendmailMTAClassValue + +NOTE: The macros shown above ${sendmailMTACluster} and $j are not actually +used when the binary expands the `@LDAP' token as class declarations are +not actually macro-expanded when read from the sendmail.cf file. + +This can be used with class related commands such as RELAY_DOMAIN_FILE(), +MASQUERADE_DOMAIN_FILE(), etc: + + Command sendmailMTAClassName + ------- -------------------- + CANONIFY_DOMAIN_FILE() Canonify + EXPOSED_USER_FILE() E + GENERICS_DOMAIN_FILE() G + LDAPROUTE_DOMAIN_FILE() LDAPRoute + LDAPROUTE_EQUIVALENT_FILE() LDAPRouteEquiv + LOCAL_USER_FILE() L + MASQUERADE_DOMAIN_FILE() M + MASQUERADE_EXCEPTION_FILE() N + RELAY_DOMAIN_FILE() R + VIRTUSER_DOMAIN_FILE() VirtHost + +You can also add your own as any 'F'ile class of the form: + + F{ClassName}@LDAP + ^^^^^^^^^ +will use "ClassName" for the sendmailMTAClassName. + +An example LDAP LDIF entry would look like: + + dn: sendmailMTAClassName=R, dc=sendmail, dc=org + objectClass: sendmailMTA + objectClass: sendmailMTAClass + sendmailMTACluster: Servers + sendmailMTAClassName: R + sendmailMTAClassValue: sendmail.org + sendmailMTAClassValue: example.com + sendmailMTAClassValue: 10.56.23 + +CAUTION: If your LDAP database contains the record above and *ALSO* a host +specific record such as: + + dn: sendmailMTAClassName=R@etrn.sendmail.org, dc=sendmail, dc=org + objectClass: sendmailMTA + objectClass: sendmailMTAClass + sendmailMTAHost: etrn.sendmail.org + sendmailMTAClassName: R + sendmailMTAClassValue: example.com + +the result will be similar to the aliases caution above. When the lookup +is done on etrn.sendmail.org, $={R} would contain all of the entries (from +both the cluster match and the host match). In other words, the effective +is additive. + +If you prefer not to use the default LDAP schema for your classes, you can +specify the map parameters when using the class command. For example: + + VIRTUSER_DOMAIN_FILE(`@ldap:-k (&(objectClass=virtHosts)(host=*)) -v host') + +Remember, macros can not be used in a class declaration as the binary does +not expand them. + + +--------------+ | LDAP ROUTING | +--------------+ @@ -1483,19 +1928,33 @@ LDAPROUTE_DOMAIN(), e.g.: LDAPROUTE_DOMAIN(`example.com') +Additionally, you can specify equivalent domains for LDAP routing using +LDAPROUTE_EQUIVALENT() and LDAPROUTE_EQUIVALENT_FILE(). 'Equivalent' +hostnames are mapped to $M (the masqueraded hostname for the server) before +the LDAP query. For example, if the mail is addressed to +user@host1.example.com, normally the LDAP lookup would only be done for +'user@host1.example.com' and '@host1.example.com'. However, if +LDAPROUTE_EQUIVALENT(`host1.example.com') is used, the lookups would also be +done on 'user@example.com' and '@example.com' after attempting the +host1.example.com lookups. + By default, the feature will use the schemas as specified in the draft and will not reject addresses not found by the LDAP lookup. However, this behavior can be changed by giving additional arguments to the FEATURE() command: - FEATURE(`ldap_routing', <mailHost>, <mailRoutingAddress>, <bounce>) + FEATURE(`ldap_routing', <mailHost>, <mailRoutingAddress>, <bounce>, <detail>) where <mailHost> is a map definition describing how to lookup an alternative mail host for a particular address; <mailRoutingAddress> is a map definition -describing how to lookup an alternative address for a particular address; and +describing how to lookup an alternative address for a particular address; the <bounce> argument, if present and not the word "passthru", dictates that mail should be bounced if neither a mailHost nor mailRoutingAddress -is found. +is found; and <detail> indicates what actions to take if the address +contains +detail information -- `strip' tries the lookup with the +detail +and if no matches are found, strips the +detail and tries the lookup again; +`preserve', does the same as `strip' but if a mailRoutingAddress match is +found, the +detail information is copied to the new address. The default <mailHost> map definition is: @@ -1537,7 +1996,10 @@ address: original address *OR* bounced as unknown user -The term "local" host above means the host specified is in class {w}. +The term "local" host above means the host specified is in class {w}. If +the result would mean sending the mail to a different host, that host is +looked up in the mailertable before delivery. + Note that the last case depends on whether the third argument is given to the FEATURE() command. The default is to deliver the message to the original address. @@ -1547,7 +2009,7 @@ inetLocalMailRecipient and the address be listed in a mailLocalAddress attribute. If present, there must be only one mailHost attribute and it must contain a fully qualified host name as its value. Similarly, if present, there must be only one mailRoutingAddress attribute and it must -contain an RFC 822 compliant address. Some example LDAP records (in ldif +contain an RFC 822 compliant address. Some example LDAP records (in LDIF format): dn: uid=tom, o=example.com, c=US @@ -1563,7 +2025,8 @@ This would deliver mail for tom@example.com to thomas@mailhost.example.com. mailHost: eng.example.com This would relay mail for dick@example.com to the same address but redirect -the mail to MX records listed for the host eng.example.com. +the mail to MX records listed for the host eng.example.com (unless the +mailertable overrides). dn: uid=harry, o=example.com, c=US objectClass: inetLocalMailRecipient @@ -1604,12 +2067,14 @@ If you really want to revert to the old behaviour, you will need to use FEATURE(`promiscuous_relay'). You can allow certain domains to relay through your server by adding their domain name or IP address to class {R} using RELAY_DOMAIN() and RELAY_DOMAIN_FILE() or via the access database -(described below). The file consists (like any other file based class) -of entries listed on separate lines, e.g., +(described below). Note that IPv6 addresses must be prefaced with "IPv6:". +The file consists (like any other file based class) of entries listed on +separate lines, e.g., sendmail.org 128.32 - 1:2:3:4:5:6:7 + IPv6:2002:c0a8:02c7 + IPv6:2002:c0a8:51d2::23f4 host.mydomain.com If you use @@ -1627,16 +2092,20 @@ portion of an incoming recipient address by using For example, if your server receives a recipient of user@domain.com and domain.com lists your server in its MX records, the mail will be -accepted for relay to domain.com. Note that this will stop spammers -from using your host to relay spam but it will not stop outsiders from -using your server as a relay for their site (that is, they set up an -MX record pointing to your mail server, and you will relay mail addressed -to them without any prior arrangement). Along the same lines, +accepted for relay to domain.com. This feature may cause problems +if MX lookups for the recipient domain are slow or time out. In that +case, mail will be temporarily rejected. It is usually better to +maintain a list of hosts/domains for which the server acts as relay. +Note also that this feature will stop spammers from using your host +to relay spam but it will not stop outsiders from using your server +as a relay for their site (that is, they set up an MX record pointing +to your mail server, and you will relay mail addressed to them +without any prior arrangement). Along the same lines, FEATURE(`relay_local_from') will allow relaying if the sender specifies a return path (i.e. -MAIL FROM: <user@domain>) domain which is a local domain. This a +MAIL FROM: <user@domain>) domain which is a local domain. This is a dangerous feature as it will allow spammers to spam using your mail server by simply specifying a return address of user@your.domain.com. It should not be used unless absolutely necessary. @@ -1648,10 +2117,15 @@ which allows relaying if the mail sender is listed as RELAY in the access map. If an optional argument `domain' is given, the domain portion of the mail sender is also checked to allowing relaying. This option only works together with the tag From: for the LHS of -the access map entries (see below: Finer control...). +the access map entries (see below: Finer control...). This feature +allows spammers to abuse your mail server by specifying a return +address that you enabled in your access file. This may be harder +to figure out for spammers, but it should not be used unless +necessary. Instead use SMTP AUTH or STARTTLS to allow relaying +for roaming users. -If source routing is used in the recipient address (i.e. +If source routing is used in the recipient address (e.g., RCPT TO: <user%site.com@othersite.com>), sendmail will check user@site.com for relaying if othersite.com is an allowed relay host in either class {R}, class {m} if FEATURE(`relay_entire_domain') is used, @@ -1679,14 +2153,30 @@ or reject those addresses. As of 8.9, sendmail will refuse mail if the MAIL FROM: parameter has an unresolvable domain (i.e., one that DNS, your local name service, -or special case rules in ruleset 3 cannot locate). If you want to -continue to accept such domains, e.g., because you are inside a -firewall that has only a limited view of the Internet host name space -(note that you will not be able to return mail to them unless you have -some "smart host" forwarder), use +or special case rules in ruleset 3 cannot locate). This also applies +to addresses that use domain literals, e.g., <user@[1.2.3.4]>, if the +IP address can't be mapped to a host name. If you want to continue +to accept such domains, e.g., because you are inside a firewall that +has only a limited view of the Internet host name space (note that you +will not be able to return mail to them unless you have some "smart +host" forwarder), use FEATURE(`accept_unresolvable_domains') +Alternatively, you can allow specific addresses by adding them to +the access map, e.g., + + From:unresolvable.domain OK + From:[1.2.3.4] OK + From:[1.2.4] OK + +Notice: domains which are temporarily unresolvable are (temporarily) +rejected with a 451 reply code. If those domains should be accepted +(which is discouraged) then you can use + + LOCAL_CONFIG + C{ResOk}TEMP + sendmail will also refuse mail if the MAIL FROM: parameter is not fully qualified (i.e., contains a domain as well as a user). If you want to continue to accept such senders, use @@ -1696,7 +2186,7 @@ want to continue to accept such senders, use Setting the DaemonPortOptions modifier 'u' overrides the default behavior, i.e., unqualified addresses are accepted even without this FEATURE. If this FEATURE is not used, the DaemonPortOptions modifier 'f' can be used -to enforce fully qualified addresses. +to enforce fully qualified domain names. An ``access'' database can be created to accept or reject mail from selected domains. For example, you may choose to reject all mail @@ -1704,10 +2194,19 @@ originating from known spammers. To enable such a database, use FEATURE(`access_db') -The FEATURE macro can accept a second parameter giving the key file +Notice: the access database is applied to the envelope addresses +and the connection information, not to the header. + +The FEATURE macro can accept as second parameter the key file definition for the database; for example - FEATURE(`access_db', `hash /etc/mail/access') + FEATURE(`access_db', `hash -T<TMPF> /etc/mail/access_map') + +Notice: If a second argument is specified it must contain the option +`-T<TMPF>' as shown above. The optional third and fourth parameters +may be `skip' or `lookupdotdomain'. The former enables SKIP as +value part (see below), the latter is another way to enable the +feature of the same name (see above). Remember, since /etc/mail/access is a database, after creating the text file as described below, you must use makemap to create the database @@ -1716,21 +2215,27 @@ map. For example: makemap hash /etc/mail/access < /etc/mail/access The table itself uses e-mail addresses, domain names, and network -numbers as keys. For example, +numbers as keys. Note that IPv6 addresses must be prefaced with "IPv6:". +For example, - spammer@aol.com REJECT - cyberspammer.com REJECT - 192.168.212 REJECT + spammer@aol.com REJECT + cyberspammer.com REJECT + 192.168.212 REJECT + IPv6:2002:c0a8:02c7 RELAY + IPv6:2002:c0a8:51d2::23f4 REJECT would refuse mail from spammer@aol.com, any user from cyberspammer.com -(or any host within the cyberspammer.com domain), and any host on the -192.168.212.* network. +(or any host within the cyberspammer.com domain), any host on the +192.168.212.* network, and the IPv6 address 2002:c0a8:51d2::23f4. It would +allow relay for the IPv6 network 2002:c0a8:02c7::/48. The value part of the map can contain: - OK Accept mail even if other rules in the - running ruleset would reject it, for example, - if the domain name is unresolvable. + OK Accept mail even if other rules in the running + ruleset would reject it, for example, if the domain + name is unresolvable. "Accept" does not mean + "relay", but at most acceptance for local + recipients. That is, OK allows less than RELAY. RELAY Accept mail addressed to the indicated domain or received from the indicated domain for relaying through your SMTP server. RELAY also serves as @@ -1742,10 +2247,16 @@ The value part of the map can contain: it affects only the designated recipient, not the whole message as it does in all other cases. This should only be used if really necessary. + SKIP This can only be used for host/domain names + and IP addresses/nets. It will abort the current + search for this entry without accepting or rejecting + it but causing the default action. ### any text where ### is an RFC 821 compliant error code and "any text" is a message to return for the command. The string should be quoted to avoid surprises, e.g., sendmail may remove spaces otherwise. + This type is deprecated, use one the two + ERROR: entries below instead. ERROR:### any text as above, but useful to mark error messages as such. ERROR:D.S.N:### any text @@ -1758,9 +2269,9 @@ For example: okay.cyberspammer.com OK sendmail.org RELAY 128.32 RELAY - 1:2:3:4:5:6:7 RELAY + IPv6:1:2:3:4:5:6:7 RELAY [127.0.0.3] OK - [1:2:3:4:5:6:7:8] OK + [IPv6:1:2:3:4:5:6:7:8] OK would accept mail from okay.cyberspammer.com, but would reject mail from all other hosts at cyberspammer.com with the indicated message. It would @@ -1768,20 +2279,22 @@ allow relaying mail from and to any hosts in the sendmail.org domain, and allow relaying from the 128.32.*.* network and the IPv6 1:2:3:4:5:6:7:* network. The latter two entries are for checks against ${client_name} if the IP address doesn't resolve to a hostname (or is considered as "may be -forged"). +forged"). That is, using square brackets means these are host names, +not network numbers. Warning: if you change the RFC 821 compliant error code from the default value of 550, then you should probably also change the RFC 1893 compliant error code to match it. For example, if you use - user@example.com 450 mailbox full + user@example.com ERROR:450 mailbox full -the error returned would be "450 4.0.0 mailbox full" which is wrong. -Use "450 4.2.2 mailbox full" or "ERROR:4.2.2:450 mailbox full" -instead. +the error returned would be "450 5.0.0 mailbox full" which is wrong. +Use "ERROR:4.2.2:450 mailbox full" instead. Note, UUCP users may need to add hostname.UUCP to the access database -or class {R}. If you also use: +or class {R}. + +If you also use: FEATURE(`relay_hosts_only') @@ -1824,13 +2337,14 @@ the example from above: Mail can't be sent to spammer@aol.com or anyone at cyberspammer.com. -There is also a ``Realtime Blackhole List'' run by the MAPS project -at http://maps.vix.com/. This is a database maintained in DNS of -spammers. To use this database, use +There are several DNS based blacklists, the first of which was +the RBL (``Realtime Blackhole List'') run by the MAPS project, +see http://mail-abuse.org/. These are databases of spammers +maintained in DNS. To use such a database, specify FEATURE(`dnsbl') -This will cause sendmail to reject mail from any site in the +This will cause sendmail to reject mail from any site in the original Realtime Blackhole List database. This default DNS blacklist, blackholes.mail-abuse.org, is a service offered by the Mail Abuse Prevention System (MAPS). As of July 31, 2001, MAPS is a subscription @@ -1840,22 +2354,46 @@ subscribed. Contact MAPS to subscribe (http://mail-abuse.org/). You can specify an alternative RBL server to check by specifying an argument to the FEATURE. The default error message is -You can specify an alternative RBL domain to check by specifying an -argument to the FEATURE. The default error message is + Mail from IP-ADDRESS refused by blackhole site SERVER - Mail from $&{client_addr} refused by blackhole site DOMAIN +where IP-ADDRESS and SERVER are replaced by the appropriate +information. A second argument can be used to specify a different +text. By default, temporary lookup failures are ignored and hence +cause the connection not to be rejected by the DNS based rejection +list. This behavior can be changed by specifying a third argument, +which must be either `t' or a full error message. For example: + + FEATURE(`dnsbl', `dnsbl.example.com', `', + `"451 Temporary lookup failure for " $&{client_addr} " in dnsbl.example.com"') + +If `t' is used, the error message is: + + 451 Temporary lookup failure of IP-ADDRESS at SERVER + +where IP-ADDRESS and SERVER are replaced by the appropriate +information. + +This FEATURE can be included several times to query different +DNS based rejection lists, e.g., the dial-up user list (see +http://mail-abuse.org/dul/). + +Notice: to avoid checking your own local domains against those +blacklists, use the access_db feature and add: + + Connect:10.1 OK + Connect:127.0.0.1 RELAY + +to the access map, where 10.1 is your local network. You may +want to use "RELAY" instead of "OK" to allow also relaying +instead of just disabling the DNS lookups in the backlists. -where DOMAIN is the first argument of the feature. A second argument -can be used to specify a different text. This FEATURE can be -included several times to query different DNS based rejection lists, -e.g., the dial-up user list (see http://maps.vix.com/dul/). The features described above make use of the check_relay, check_mail, and check_rcpt rulesets. If you wish to include your own checks, you can put your checks in the rulesets Local_check_relay, Local_check_mail, and Local_check_rcpt. For example if you wanted to block senders with all numeric usernames (i.e. 2312343@bigisp.com), -you would use Local_check_mail and the new regex map: +you would use Local_check_mail and the regex map: LOCAL_CONFIG Kallnumbers regex -a@MATCH ^[0-9]+$ @@ -1875,6 +2413,7 @@ appropriate action is taken. Otherwise, the results of the local rewriting are ignored. Finer control by using tags for the LHS of the access map +--------------------------------------------------------- Read this section only if the options listed so far are not sufficient for your purposes. There is now the option to tag entries in the @@ -1886,7 +2425,8 @@ access map according to their type. Three tags are available: If the required item is looked up in a map, it will be tried first with the corresponding tag in front, then (as fallback to enable -backward compatibility) without any tag. For example, +backward compatibility) without any tag, unless the specific feature +requires a tag. For example, From:spammer@some.dom REJECT To:friend.domain RELAY @@ -1909,6 +2449,7 @@ reject mail from all other addresses with another.dom as domain part. Delay all checks +---------------- By using FEATURE(`delay_checks') the rulesets check_mail and check_relay will not be called when a client connects or issues a MAIL command, @@ -1943,24 +2484,33 @@ FEATURE(`delay_checks') can take an optional argument: enables spamhater test If such an argument is given, the recipient will be looked up in the access -map (using the tag To:). If the argument is `friend', then the other +map (using the tag Spam:). If the argument is `friend', then the other rulesets will be skipped if the recipient address is found and has RHS -spamfriend. If the argument is `hater', then the other rulesets will be -applied if the recipient address is found and has RHS spamhater. +friend. If the argument is `hater', then the other rulesets will be +applied if the recipient address is found and has RHS hater. This allows for simple exceptions from the tests, e.g., by activating -the spamfriend option and having +the friend option and having - To:abuse@ SPAMFRIEND + Spam:abuse@ FRIEND in the access map, mail to abuse@localdomain will get through. It is also possible to specify a full address or an address with +detail: - To:abuse@abuse.my.domain SPAMFRIEND - To:me+abuse@ SPAMFRIEND + Spam:abuse@my.domain FRIEND + Spam:me+abuse@ FRIEND + Spam:spam.domain FRIEND +Note: The required tag has been changed in 8.12 from To: to Spam:. +This change is incompatible to previous versions. However, you can +(for now) simply add the new entries to the access map, the old +ones will be ignored. As soon as you removed the old entries from +the access map, specify a third parameter (`n') to this feature and +the backward compatibility rules will not be in the generated .cf +file. Header Checks +------------- You can also reject mail on the basis of the contents of headers. This is done by adding a ruleset call to the 'H' header definition command @@ -1987,10 +2537,14 @@ defined for them can be given by: H*: $>CheckHdr -Notice: All rules act on tokens as explained in doc/op/op.{me,ps,txt}. +Notice: +1. All rules act on tokens as explained in doc/op/op.{me,ps,txt}. That may cause problems with simple header checks due to the -tokenization. It might be simpler to use a regex map and apply it +tokenization. It might be simpler to use a regex map and apply it to $&{currHeader}. +2. There are no default rulesets coming with this distribution of +sendmail. You can either write your own or you can search the +WWW for examples, e.g., http://www.digitalanswers.org/check_local/ After all of the headers are read, the check_eoh ruleset will be called for any final header-related checks. The ruleset is called with the number of @@ -2031,7 +2585,8 @@ probably not be used in production. +----------+ In this text, cert will be used as an abreviation for X.509 certificate, -DN is the distinguished name of a cert, and CA is a certification authority. +DN (CN) is the distinguished (common) name of a cert, and CA is a +certification authority, which signs (issues) certs. For STARTTLS to be offered by sendmail you need to set at least this variables (the file names and paths are just examples): @@ -2044,53 +2599,57 @@ this variables (the file names and paths are just examples): On systems which do not have the compile flag HASURANDOM set (see sendmail/README) you also must set confRAND_FILE. -See doc/op/op.{me,ps} for more information about these options, -esp. the sections ``Certificates for STARTTLS'' and ``PRNG for +See doc/op/op.{me,ps,txt} for more information about these options, +especially the sections ``Certificates for STARTTLS'' and ``PRNG for STARTTLS''. Macros related to STARTTLS are: ${cert_issuer} holds the DN of the CA (the cert issuer). ${cert_subject} holds the DN of the cert (called the cert subject). +${cn_issuer} holds the CN of the CA (the cert issuer). +${cn_subject} holds the CN of the cert (called the cert subject). ${tls_version} the TLS/SSL version used for the connection, e.g., TLSv1, - SSLv3, SSLv2. + TLSv1/SSLv3, SSLv3, SSLv2. ${cipher} the cipher used for the connection, e.g., EDH-DSS-DES-CBC3-SHA, EDH-RSA-DES-CBC-SHA, DES-CBC-MD5, DES-CBC3-SHA. ${cipher_bits} the keylength (in bits) of the symmetric encryption algorithm used for the connection. -${verify} holds the result of the verification of the presented cert. Possible - values are: - OK verification succeeded. - NO no cert presented. - FAIL cert presented but could not be verified, e.g., the signing - CA is missing. - NONE STARTTLS has not been performed. - TEMP temporary error occurred. - PROTOCOL some protocol error occurred. +${verify} holds the result of the verification of the presented cert. + Possible values are: + OK verification succeeded. + NO no cert presented. + NOT no cert requested. + FAIL cert presented but could not be verified, + e.g., the cert of the signing CA is missing. + NONE STARTTLS has not been performed. + TEMP temporary error occurred. + PROTOCOL protocol error occurred (SMTP level). SOFTWARE STARTTLS handshake failed. -${server_name} the name of the server of the current outgoing SMTP +${server_name} the name of the server of the current outgoing SMTP connection. -${server_addr} the address of the server of the current outgoing SMTP +${server_addr} the address of the server of the current outgoing SMTP connection. Relaying +-------- SMTP STARTTLS can allow relaying for senders who have successfully -authenticated themselves. This is done in the ruleset RelayAuth. If the +authenticated themselves. This is done in the ruleset RelayAuth. If the verification of the cert failed (${verify} != OK), relaying is subject to -the usual rules. Otherwise the DN of the issuer is looked up in the access -map using the tag CERTISSUER. If the resulting value is RELAY, relaying is -allowed. If it is SUBJECT, the DN of the cert subject is looked up next in -the access map. using the tag CERTSUBJECT. If the value is RELAY, relaying +the usual rules. Otherwise the DN of the issuer is looked up in the access +map using the tag CERTISSUER. If the resulting value is RELAY, relaying is +allowed. If it is SUBJECT, the DN of the cert subject is looked up next in +the access map using the tag CERTSUBJECT. If the value is RELAY, relaying is allowed. To make things a bit more flexible (or complicated), the values for ${cert_issuer} and ${cert_subject} can be optionally modified by regular expressions defined in the m4 variables _CERT_REGEX_ISSUER_ and -_CERT_REGEX_SUBJECT_, respectively. To avoid problems with those macros in +_CERT_REGEX_SUBJECT_, respectively. To avoid problems with those macros in rulesets and map lookups, they are modified as follows: each non-printable character and the characters '<', '>', '(', ')', '"', '+' are replaced by -their HEX value with a leading '+'. For example: +their HEX value with a leading '+'. For example: /C=US/ST=California/O=endmail.org/OU=private/CN=Darth Mail (Cert)/Email= darth+cert@endmail.org @@ -2111,29 +2670,49 @@ R$* $: $&{verify} ROK $# OK Allowing Connections +-------------------- -The rulesets tls_server and tls_client are used to decide whether an SMTP -connection is accepted (or should continue). +The rulesets tls_server, tls_client, and tls_rcpt are used to decide whether +an SMTP connection is accepted (or should continue). tls_server is called when sendmail acts as client after a STARTTLS command -(should) have been issued. The parameter is the value of ${verify}. +(should) have been issued. The parameter is the value of ${verify}. tls_client is called when sendmail acts as server, after a STARTTLS command -has been issued, and from check_mail. The parameter is the value of +has been issued, and from check_mail. The parameter is the value of ${verify} and STARTTLS or MAIL, respectively. -Both rulesets behave the same. If no access map is in use, the connection +Both rulesets behave the same. If no access map is in use, the connection will be accepted unless ${verify} is SOFTWARE, in which case the connection -is always aborted. Otherwise, ${client_name} (${server_name}) is looked -up in the access map using the tag TLS_Srv (or TLS_Clt), which is done -with the ruleset LookUpDomain. If no entry is found, ${client_addr} +is always aborted. For tls_server/tls_client, ${client_name}/${server_name} +is looked up in the access map using the tag TLS_Srv/TLS_Clt, which is done +with the ruleset LookUpDomain. If no entry is found, ${client_addr} (${server_addr}) is looked up in the access map (same tag, ruleset -LookUpAddr). If this doesn't result in an entry either, just the tag is -looked up in the access map (included the trailing :). The result of the -lookups is then used to call the ruleset tls_connection, which checks the -requirement specified by the RHS in the access map against the actual -parameters of the current TLS connection, esp. ${verify} and -${cipher_bits}. Legal RHSs in the access map are: +LookUpAddr). If this doesn't result in an entry either, just the tag is +looked up in the access map (included the trailing colon). Notice: +requiring that e-mail is sent to a server only encrypted, e.g., via + +TLS_Srv:secure.domain ENCR:112 + +doesn't necessarily mean that e-mail sent to that domain is encrypted. +If the domain has multiple MX servers, e.g., + +secure.domain. IN MX 10 mail.secure.domain. +secure.domain. IN MX 50 mail.other.domain. + +then mail to user@secure.domain may go unencrypted to mail.other.domain. +tls_rcpt can be used to address this problem. + +tls_rcpt is called before a RCPT TO: command is sent. The parameter is the +current recipient. This ruleset is only defined if FEATURE(`access_db') +is selected. A recipient address user@domain is looked up in the access +map in four formats: TLS_Rcpt:user@domain, TLS_Rcpt:user@, TLS_Rcpt:domain, +and TLS_Rcpt:; the first match is taken. + +The result of the lookups is then used to call the ruleset TLS_connection, +which checks the requirement specified by the RHS in the access map against +the actual parameters of the current TLS connection, esp. ${verify} and +${cipher_bits}. Legal RHSs in the access map are: VERIFY verification must have succeeded VERIFY:bits verification must have succeeded and ${cipher_bits} must @@ -2141,39 +2720,64 @@ VERIFY:bits verification must have succeeded and ${cipher_bits} must ENCR:bits ${cipher_bits} must be greater than or equal bits. The RHS can optionally be prefixed by TEMP+ or PERM+ to select a temporary -or permanent error. The default is a temporary error code (403 4.7.0) +or permanent error. The default is a temporary error code (403 4.7.0) unless the macro TLS_PERM_ERR is set during generation of the .cf file. If a certain level of encryption is required, then it might also be possible that this level is provided by the security layer from a SASL algorithm, e.g., DIGEST-MD5. +Furthermore, there can be a list of extensions added. Such a list +starts with '+' and the items are separated by '++'. Allowed +extensions are: + +CN:name name must match ${cn_subject} +CN ${server_name} must match ${cn_subject} +CS:name name must match ${cert_subject} +CI:name name must match ${cert_issuer} + Example: e-mail sent to secure.example.com should only use an encrypted -connection. e-mail received from hosts within the laptop.example.com domain -should only be accepted if they have been authenticated. +connection. E-mail received from hosts within the laptop.example.com domain +should only be accepted if they have been authenticated. The host which +receives e-mail for darth@endmail.org must present a cert that uses the +CN smtp.endmail.org. + TLS_Srv:secure.example.com ENCR:112 TLS_Clt:laptop.example.com PERM+VERIFY:112 +TLS_Rcpt:darth@endmail.org ENCR:112+CN:smtp.endmail.org -Notice: requiring that e-mail is sent to a server only encrypted, -e.g., via -TLS_Srv:secure.domain ENCR:112 +Disabling STARTTLS And Setting SMTP Server Features +--------------------------------------------------- -doesn't necessarily mean that e-mail sent to that domain is encrypted. -If the domain has multiple MX servers, e.g., +By default STARTTLS is used whenever possible. However, there are +some broken MTAs that don't properly implement STARTTLS. To be able +to send to (or receive from) those MTAs, the ruleset try_tls +(srv_features) can be used that work together with the access map. +Entries for the access map must be tagged with Try_TLS (Srv_Features) +and refer to the hostname or IP address of the connecting system. +A default case can be specified by using just the tag. For example, +the following entries in the access map: -secure.domain. IN MX 10 mail.secure.domain. -secure.domain. IN MX 50 mail.other.domain. + Try_TLS:broken.server NO + Srv_Features:my.domain v + Srv_Features: V -then mail to user@secure.domain may go unencrypted to mail.other.domain. +will turn off STARTTLS when sending to broken.server (or any host +in that domain), and request a client certificate during the TLS +handshake only for hosts in my.domain. The valid entries on the RHS +for Srv_Features are listed in the Sendmail Installation and +Operations Guide. Received: Header +---------------- -The Received: header reveals whether STARTTLS has been used. It contains an +The Received: header reveals whether STARTTLS has been used. It contains an extra line: -(using ${tls_version} with cipher ${cipher} (${cipher_bits} bits) verified ${verify}) +(version=${tls_version} cipher=${cipher} bits=${cipher_bits} verify=${verify}) + +---------------------+ | SMTP AUTHENTICATION | @@ -2198,7 +2802,7 @@ RDIGEST-MD5 $| $+@$=w $# OK to allow relaying for users that authenticated using DIGEST-MD5 and have an identity in the local domains. -The ruleset Strust_auth is used to determine whether a given AUTH= +The ruleset trust_auth is used to determine whether a given AUTH= parameter (that is passed to this ruleset) should be trusted. This ruleset may make use of the other ${auth_*} macros. Only if the ruleset resolves to the error mailer, the AUTH= parameter is not @@ -2216,6 +2820,44 @@ If the selected mechanism provides a security layer the number of bits used for the key of the symmetric cipher is stored in the macro ${auth_ssf}. + +If sendmail acts as client, it needs some information how to +authenticate against another MTA. This information can be provided +by the ruleset authinfo or by the option AuthMechanisms. The +authinfo ruleset looks up {server_name} using the tag AuthInfo: in +the access map. If no entry is found, {server_addr} is looked up +in the same way and finally just the tag AuthInfo: to provide +default values. + +The RHS for an Auth: entry in the access map should consists of a +list of tokens, each of which has the form: "TDstring" (including +the quotes). T is a tag which describes the item, D is a delimiter, +either ':' for simple text or '=' for a base64 encoded string. +Valid values for the tag are: + + U user (authorization) id + I authentication id + P password + R realm + M list of mechanisms delimited by spaces + +Example entries are: + +AuthInfo:other.dom "U:user" "I:user" "P:secret" "R:other.dom" "M:DIGEST-MD5" +AuthInfo:more.dom "U:user" "P=c2VjcmV0" + +User or authentication id must exist as well as the password. All +other entries have default values. If one of user or authentication +id is missing, the existing value is used for the missing item. +Realm defaults to $j and the list of mechanisms to those specified +by AuthMechanisms. + +Since this map contains sensitive information, either the access +map must be unreadable by everyone but root (or the trusted user) +or FEATURE(`authinfo') must be used which provides a separate map. +Notice: It is not checked whether the map is actually +group/world-unreadable, this is left to the user. + +--------------------------------+ | ADDING NEW MAILERS OR RULESETS | +--------------------------------+ @@ -2232,8 +2874,19 @@ LOCAL_RULESETS respectively. For example: Smyruleset ... +Local additions for the rulesets srv_features, try_tls, tls_rcpt, +tls_client, and tls_server can be made using LOCAL_SRV_FEATURES, +LOCAL_TRY_TLS, LOCAL_TLS_RCPT, LOCAL_TLS_CLIENT, and LOCAL_TLS_SERVER, +respectively. For example, to add a local ruleset that decides +whether to try STARTTLS in a sendmail client, use: + + LOCAL_TRY_TLS + R... + +Note: you don't need to add a name for the ruleset, it is implicitly +defined by using the appropriate macro. + -#if _FFR_MILTER +-------------------------+ | ADDING NEW MAIL FILTERS | +-------------------------+ @@ -2275,9 +2928,21 @@ more filters than you want to use for `confINPUT_MAIL_FILTERS'. Note that setting `confINPUT_MAIL_FILTERS' after any INPUT_MAIL_FILTER() commands will clear the list created by the prior INPUT_MAIL_FILTER() commands. -#endif /* _FFR_MILTER */ ++-------------------------+ +| QUEUE GROUP DEFINITIONS | ++-------------------------+ + +In addition to the queue directory (which is the default queue group +called "mqueue"), sendmail can deal with multiple queue groups, which +are collections of queue directories with the same behaviour. Queue +groups can be defined using the command: + + QUEUE_GROUP(`name', `equates') + +For details about queue groups, please see doc/op/op.{me,ps,txt}. + +-------------------------------+ | NON-SMTP BASED CONFIGURATIONS | +-------------------------------+ @@ -2537,6 +3202,11 @@ confDOMAIN_NAME $j macro If defined, sets $j. This should domain name. confCF_VERSION $Z macro If defined, this is appended to the configuration version name. +confLDAP_CLUSTER ${sendmailMTACluster} macro + If defined, this is the LDAP + cluster to use for LDAP searches + as described above in ``USING LDAP + FOR ALIASES, MAPS, AND CLASSES''. confFROM_HEADER From: [$?x$x <$g>$|$g$.] The format of an internally generated From: address. confRECEIVED_HEADER Received: @@ -2607,13 +3277,6 @@ confCHECKPOINT_INTERVAL CheckpointInterval [10] Checkpoint queue files every N recipients. confDELIVERY_MODE DeliveryMode [background] Default delivery mode. -confAUTO_REBUILD AutoRebuildAliases - [False] Automatically rebuild alias - file if needed. - There is a potential for a denial - of service attack if this is set. - This option is deprecated and will - be removed from a future version. confERROR_MODE ErrorMode [print] Error message mode. confERROR_MESSAGE ErrorHeader [undefined] Error message header/file. confSAVE_FROM_LINES SaveFromLine Save extra leading From_ lines. @@ -2671,13 +3334,15 @@ confCHECK_ALIASES CheckAliases [False] Check RHS of aliases when considerably on large alias files. confOLD_STYLE_HEADERS* OldStyleHeaders [True] Assume that headers without special chars are old style. -confCLIENT_OPTIONS ClientPortOptions - [none] Options for outgoing SMTP client - connections. confPRIVACY_FLAGS PrivacyOptions [authwarnings] Privacy flags. confCOPY_ERRORS_TO PostmasterCopy [undefined] Address for additional copies of all error messages. confQUEUE_FACTOR QueueFactor [600000] Slope of queue-only function. +confQUEUE_FILE_MODE QueueFileMode [undefined] Default permissions for + queue files (octal). If not set, + sendmail uses 0600 unless its real + and effective uid are different in + which case it uses 0644. confDONT_PRUNE_ROUTES DontPruneRoutes [False] Don't prune down route-addr syntax addresses to the minimum possible. @@ -2697,6 +3362,11 @@ confTO_ICONNECT Timeout.iconnect This allows a single very fast pass followed by more careful delivery attempts in the future. +confTO_ACONNECT Timeout.aconnect + [0] The overall timeout waiting for + all connection for a single delivery + attempt to succeed. If 0, no overall + limit is applied. confTO_HELO Timeout.helo [5m] The timeout waiting for a response to a HELO or EHLO command. confTO_MAIL Timeout.mail [10m] The timeout waiting for a @@ -2726,6 +3396,13 @@ confTO_IDENT Timeout.ident [5s] The timeout waiting for a confTO_FILEOPEN Timeout.fileopen [60s] The timeout waiting for a file (e.g., :include: file) to be opened. +confTO_LHLO Timeout.lhlo [2m] The timeout waiting for a response + to an LMTP LHLO command. +confTO_AUTH Timeout.auth [10m] The timeout waiting for a + response in an AUTH dialogue. +confTO_STARTTLS Timeout.starttls + [1h] The timeout waiting for a + response to an SMTP STARTTLS command. confTO_CONTROL Timeout.control [2m] The timeout for a complete control socket transaction to complete. @@ -2824,6 +3501,10 @@ confREFUSE_LA RefuseLA [varies] Load average at which numproc) where numproc is the number of processors online (if that can be determined). +confDELAY_LA DelayLA [0] Load average at which sendmail + will sleep for one second on most + SMTP commands and before accepting + connections. 0 means no limit. confMAX_ALIAS_RECURSION MaxAliasRecursion [10] Maximum depth of alias recursion. confMAX_DAEMON_CHILDREN MaxDaemonChildren @@ -2840,11 +3521,11 @@ confMAX_MIME_HEADER_LENGTH MaxMimeHeaderLength certain MIME header field values. confCONNECTION_RATE_THROTTLE ConnectionRateThrottle [undefined] The maximum number of - connections permitted per second. - After this many connections are - accepted, further connections will be - delayed. If not set or <= 0, there is - no limit. + connections permitted per second per + daemon. After this many connections + are accepted, further connections + will be delayed. If not set or <= 0, + there is no limit. confWORK_RECIPIENT_FACTOR RecipientFactor [30000] Cost of each recipient. confSEPARATE_PROC ForkEachJob [False] Run all deliveries in a @@ -2852,7 +3533,8 @@ confSEPARATE_PROC ForkEachJob [False] Run all deliveries in a confWORK_CLASS_FACTOR ClassFactor [1800] Priority multiplier for class. confWORK_TIME_FACTOR RetryFactor [90000] Cost of each delivery attempt. confQUEUE_SORT_ORDER QueueSortOrder [Priority] Queue sort algorithm: - Priority, Host, Filename, or Time. + Priority, Host, Filename, Random, + Modification, or Time. confMIN_QUEUE_AGE MinQueueAge [0] The minimum amount of time a job must sit in the queue between queue runs. This allows you to set the @@ -2884,9 +3566,11 @@ confNO_RCPT_ACTION NoRecipientAction known recipients (which may expose blind recipients), "add-apparently-to" to do the same but use Apparently-To: - instead of To:, "add-bcc" to add an - empty Bcc: header, or - "add-to-undisclosed" to add the header + instead of To: (strongly discouraged + in accordance with IETF standards), + "add-bcc" to add an empty Bcc: + header, or "add-to-undisclosed" to + add the header ``To: undisclosed-recipients:;''. confSAFE_FILE_ENV SafeFileEnvironment [undefined] If set, sendmail will do a @@ -2909,6 +3593,18 @@ confMAX_QUEUE_RUN_SIZE MaxQueueRunSize [0] If set, limit the maximum size of so this should be as large as your system can tolerate. If not set, there is no limit. +confMAX_QUEUE_CHILDREN MaxQueueChildren + [undefined] Limits the maximum number + of concurrent queue runners active. + This is to keep system resources used + within a reasonable limit. Relates to + Queue Groups and ForkAllJobs. +confMAX_RUNNERS_PER_QUEUE MaxRunnersPerQueue + [1] Only active when MaxQueueChildren + defined. Controls the maximum number + of queue runners (aka queue children) + active at the same time in a work + group. See also MaxQueueChildren. confDONT_EXPAND_CNAMES DontExpandCnames [False] If set, $[ ... $] lookups that do DNS based lookups do not expand @@ -2969,7 +3665,8 @@ confDOUBLE_BOUNCE_ADDRESS DoubleBounceAddress [postmaster] If an error occurs when sending an error message, send that "double bounce" error message to this - address. + address. If it expands to an empty + string, double bounces are dropped. confDEAD_LETTER_DROP DeadLetterDrop [undefined] Filename to save bounce messages which could not be returned to the user or sent to postmaster. @@ -2993,6 +3690,11 @@ confMAX_RCPTS_PER_MESSAGE MaxRecipientsPerMessage receive a 452 error code (i.e., they are deferred for the next delivery attempt). +confBAD_RCPT_THROTTLE BadRcptThrottle [infinite] If set and more than the + specified number of recipients in an + envelope are rejected, sleep for one + second after each rejected RCPT + command. confDONT_PROBE_INTERFACES DontProbeInterfaces [False] If set, sendmail will _not_ insert the names and addresses of any @@ -3003,6 +3705,9 @@ confDONT_PROBE_INTERFACES DontProbeInterfaces in a mailertable entry) -- otherwise, mail to addresses in this list will bounce with a configuration error. + If set to "loopback" (without + quotes), sendmail will skip + loopback interfaces (e.g., "lo0"). confPID_FILE PidFile [system dependent] Location of pid file. confPROCESS_TITLE_PREFIX ProcessTitlePrefix @@ -3017,6 +3722,9 @@ confDONT_BLAME_SENDMAIL DontBlameSendmail confREJECT_MSG - [550 Access denied] The message given if the access database contains REJECT in the value portion. +confRELAY_MSG - [550 Relaying denied] The message + given if an unauthorized relaying + attempt is rejected. confDF_BUFFER_SIZE DataFileBufferSize [4096] The maximum size of a memory-buffered data (df) file @@ -3036,36 +3744,39 @@ confAUTH_MECHANISMS AuthMechanisms [GSSAPI KERBEROS_V4 DIGEST-MD5 by the CYRUS SASL library. confDEF_AUTH_INFO DefaultAuthInfo [undefined] Name of file that contains authentication information for - outgoing connections. This file - must contain the user id, the - authorization id, the password - (plain text), and the realm to use, - each on a separate line and must be - readable by root (or the trusted - user) only. If no realm is - specified, $j is used. - - NOTE: Currently, AuthMechanisms is - used to determine the list of - mechanisms to use on an outgoing - connection. Sites which require a - different list of mechanisms for - incoming connections and outgoing - connections will have the ability - to do this in 8.11 by specifying a - list of mechanisms as the fifth - line of the DefaultAuthInfo file. - If no mechanisms are given in the - file, AuthMechanisms is used. The - code for doing so is included as - in the sendmail source code but - disabled. It can be enabled by - recompiling sendmail with: - -D_FFR_DEFAUTHINFO_MECHS -confAUTH_OPTIONS AuthOptions [undefined] If this options is 'A' + outgoing connections. This file must + contain the user id, the authorization + id, the password (plain text), the + realm to use, and the list of + mechanisms to try, each on a separate + line and must be readable by root (or + the trusted user) only. If no realm + is specified, $j is used. If no + mechanisms are given in the file, + AuthMechanisms is used. Notice: this + option is deprecated and will be + removed in future versions; it doesn't + work for the MSP since it can't read + the file. Use the authinfo ruleset + instead. +confAUTH_OPTIONS AuthOptions [undefined] If this option is 'A' then the AUTH= parameter for the MAIL FROM command is only issued when authentication succeeded. + Other values (which should be listed + one after the other without any + intervening characters except for + space or comma) are a, c, d, f, p, + and y. See doc/op/op.me for + details. +confAUTH_MAX_BITS AuthMaxBits [INT_MAX] Limit the maximum encryption + strength for the security layer in + SMTP AUTH (SASL). Default is + essentially unlimited. +confTLS_SRV_OPTIONS TLSSrvOptions If this option is 'V' no client + verification is performed, i.e., + the server doesn't ask for a + certificate. confLDAP_DEFAULT_SPEC LDAPDefaultSpec [undefined] Default map specification for LDAP maps. The value should only contain LDAP @@ -3102,15 +3813,64 @@ confRAND_FILE RandFile [undefined] File containing random requires this option if the compile flag HASURANDOM is not set (see sendmail/README). +confNICE_QUEUE_RUN NiceQueueRun [undefined] If set, the priority of + queue runners is set the given value + (nice(3)). +confDIRECT_SUBMISSION_MODIFIERS DirectSubmissionModifiers + [undefined] Defines {daemon_flags} + for direct submissions. +confUSE_MSP UseMSP [false] Use as mail submission + program, see sendmail/SECURITY. +confDELIVER_BY_MIN DeliverByMin [0] Minimum time for Deliver By + SMTP Service Extension (RFC 2852). +confSHARED_MEMORY_KEY SharedMemoryKey [0] Key for shared memory. +confFAST_SPLIT FastSplit [1] If set to a value greater than + zero, the initial MX lookups on + addresses is suppressed when they + are sorted which may result in faster + envelope splitting. +confMAILBOX_DATABASE MailboxDatabase [pw] Type of lookup to find + information about local mailboxes. +confDEQUOTE_OPTS - [empty] Additional options for the + dequote map. +confINPUT_MAIL_FILTERS InputMailFilters + A comma separated list of filters + which determines which filters and + the invocation sequence are + contacted for incoming SMTP + messages. If none are set, no + filters will be contacted. +confMILTER_LOG_LEVEL Milter.LogLevel [9] Log level for input mail filter + actions, defaults to LogLevel. +confMILTER_MACROS_CONNECT Milter.macros.connect + [empty] Macros to transmit to milters + when a session connection starts. +confMILTER_MACROS_HELO Milter.macros.helo + [empty] Macros to transmit to milters + after HELO command. +confMILTER_MACROS_ENVFROM Milter.macros.envfrom + [empty] Macros to transmit to milters + after MAIL FROM command. +confMILTER_MACROS_ENVRCPT Milter.macros.envrcpt + [empty] Macros to transmit to milters + after RCPT TO command. + See also the description of OSTYPE for some parameters that can be tweaked (generally pathnames to mailers). -DaemonPortOptions are a special case since multiple daemons can be -defined. This can be done via +ClientPortOptions and DaemonPortOptions are special cases since multiple +clients/daemons can be defined. This can be done via + CLIENT_OPTIONS(`field1=value1,field2=value2,...') DAEMON_OPTIONS(`field1=value1,field2=value2,...') +Note that multiple CLIENT_OPTIONS() commands (and therefore multiple +ClientPortOptions settings) are allowed in order to give settings for each +protocol family (e.g., one for Family=inet and one for Family=inet6). A +restriction placed on one family only affects outgoing connections on that +particular family. + If DAEMON_OPTIONS is not used, then the default is DAEMON_OPTIONS(`Port=smtp, Name=MTA') @@ -3152,10 +3912,113 @@ Notice: Do NOT use the 'a' modifier on a public accessible MTA! Finally, the M=E modifier shown above disables ETRN as required by RFC 2476. +Mail filters can be defined using the INPUT_MAIL_FILTER() and MAIL_FILTER() +commands: -+-----------+ -| HIERARCHY | -+-----------+ + INPUT_MAIL_FILTER(`sample', `S=local:/var/run/f1.sock') + MAIL_FILTER(`myfilter', `S=inet:3333@localhost') + +The INPUT_MAIL_FILTER() command causes the filter(s) to be called in the +same order they were specified by also setting confINPUT_MAIL_FILTERS. A +filter can be defined without adding it to the input filter list by using +MAIL_FILTER() instead of INPUT_MAIL_FILTER() in your .mc file. +Alternatively, you can reset the list of filters and their order by setting +confINPUT_MAIL_FILTERS option after all INPUT_MAIL_FILTER() commands in +your .mc file. + + ++----------------------------+ +| MESSAGE SUBMISSION PROGRAM | ++----------------------------+ + +The purpose of the message submission program (MSP) is explained +in sendmail/SECURITY. This section contains a list of caveats and +a few hints how for those who want to tweak the default configuration +for it (which is installed as submit.cf). + +Notice: do not add options/features to submit.mc unless you are +absolutely sure you need them. Options you may want to change +include: + +- confTIME_ZONE on OS that don't use the default, e.g., Irix. +- confDELIVERY_MODE is set to interactive in msp.m4 instead + of the default background mode. + +Some things are not intended to work with the MSP. These include +features that influence the delivery process (e.g., mailertable, +aliases), or those that are only important for a SMTP server (e.g., +virtusertable, DaemonPortOptions). Other things don't work well +with the MSP and require tweaking or workarounds. For example, to +allow for client authentication it is not just sufficient to provide +a client certificate and the corresponding key, but it is also +necessary to make the key group (smmsp) readable and tell sendmail +not to complain about that, i.e., + + define(`confDONT_BLAME_SENDMAIL', `GroupReadableKeyFile') + +If the MSP should actually use AUTH then the necessary data +should be placed in a map as explained in SMTP AUTHENTICATION: + +FEATURE(`authinfo', `DATABASE_MAP_TYPE /etc/mail/msp-authinfo') + +/etc/mail/msp-authinfo should contain an entry like: + + AuthInfo:127.0.0.1 "U:smmsp" "P:secret" "M:DIGEST-MD5" + +The file and the map created by makemap should be owned by smmsp, +its group should be smmsp, and it should have mode 640. The database +used by the MTA for AUTH must have a corresponding entry. +Additionally the MTA must trust this authentication data so the AUTH= +part will be relayed on to the next hop. This can be achieved by +adding the following to your sendmail.mc file: + + LOCAL_RULESETS + SLocal_trust_auth + R$* $: $&{auth_authen} + Rsmmsp $# OK + +feature/msp.m4 defines almost all settings for the MSP. Most of +those should not be changed at all. Some of the features and options +can be overridden if really necessary. It is a bit tricky to do +this, because it depends on the actual way the option is defined +in feature/msp.m4. If it is directly defined (i.e., define()) then +the modified value must be defined after + + FEATURE(`msp') + +If it is conditionally defined (i.e., ifdef()) then the desired +value must be defined before the FEATURE line in the .mc file. +To see how the options are defined read feature/msp.m4. + + ++--------------------------+ +| FORMAT OF FILES AND MAPS | ++--------------------------+ + +Files that define classes, i.e., F{classname}, consist of lines +each of which contains a single element of the class. For example, +/etc/mail/local-host-names may have the following content: + +my.domain +another.domain + +Maps must be created using makemap(8) , e.g., + + makemap hash MAP < MAP + +In general, a text file from which a map is created contains lines +of the form + +key value + +where 'key' and 'value' are also called LHS and RHS, respectively. +By default, the delimiter between LHS and RHS is a non-empty sequence +of white space characters. + + ++------------------+ +| DIRECTORY LAYOUT | ++------------------+ Within this directory are several subdirectories, to wit: @@ -3313,4 +4176,4 @@ M4 DIVERSIONS 8 DNS based blacklists 9 special local rulesets (1 and 2) -$Revision: 1.8 $, Last updated $Date: 2001/08/21 16:31:39 $ +$Revision: 1.9 $, Last updated $Date: 2001/09/11 19:02:48 $ diff --git a/gnu/usr.sbin/sendmail/cf/cf/Makefile b/gnu/usr.sbin/sendmail/cf/cf/Makefile index 7e33d49134c..7595f34006b 100644 --- a/gnu/usr.sbin/sendmail/cf/cf/Makefile +++ b/gnu/usr.sbin/sendmail/cf/cf/Makefile @@ -1,8 +1,8 @@ -# $OpenBSD: Makefile,v 1.10 2001/05/29 01:31:11 millert Exp $ +# $OpenBSD: Makefile,v 1.11 2001/09/11 19:02:48 millert Exp $ # # Makefile for configuration files. # -# $Sendmail: Makefile,v 8.40.8.5 2001/04/12 22:39:52 gshapiro Exp $ +# $Sendmail: Makefile,v 8.54 2001/08/20 15:16:48 gshapiro Exp $ # # @@ -25,20 +25,34 @@ RM= rm -f ( cd ${.CURDIR} && $(M4) ${CFDIR}/m4/cf.m4 ${@:R}.mc > ${.OBJDIR}/$@ ) $(CHMOD) $(ROMODE) $@ -ALL= clientproto.cf openbsd-proto.cf courtesan.cf courtesan-nonet.cf \ - courtesan-lists.cf openbsd-lists.cf gandalf.cf alatar.cf \ - nettan.cf waldorf.cf lucifier.cf elbereth.cf corpse.cf knecht.cf +ALL= submit.cf clientproto.cf tcpproto.cf openbsd-localhost.cf \ + openbsd-proto.cf courtesan.cf courtesan-nonet.cf courtesan-lists.cf \ + openbsd-lists.cf gandalf.cf alatar.cf nettan.cf waldorf.cf lucifier.cf \ + elbereth.cf corpse.cf knecht.cf all: $(ALL) clean cleandir: - $(RM) $(ALL) core + $(RM) $(ALL) *.core -depend install: +depend: + +install: + @if test -e ${DESTDIR}/etc/mail/sendmail.cf -a \ + \! -e ${DESTDIR}/etc/mail/submit.cf; then \ + echo "WARNING: installed missing ${DESTDIR}/etc/mail/submit.cf"; \ + echo "You should probably rebuild ${DESTDIR}/etc/mail/sendmail.cf"; \ + ${INSTALL} ${INSTALL_COPY} -o root -g wheel -m 644 submit.cf \ + ${DESTDIR}/etc/mail/submit.cf; \ + fi distribution: openbsd-proto.cf ${INSTALL} ${INSTALL_COPY} -o root -g wheel -m 644 openbsd-proto.cf \ ${DESTDIR}/etc/mail/sendmail.cf + ${INSTALL} ${INSTALL_COPY} -o root -g wheel -m 644 openbsd-localhost.cf \ + ${DESTDIR}/etc/mail/localhost.cf + ${INSTALL} ${INSTALL_COPY} -o root -g wheel -m 644 submit.cf \ + ${DESTDIR}/etc/mail/submit.cf # this is overkill, but.... M4FILES=\ @@ -48,22 +62,30 @@ M4FILES=\ ${CFDIR}/domain/S2K.Berkeley.EDU.m4 \ ${CFDIR}/domain/berkeley-only.m4 \ ${CFDIR}/domain/generic.m4 \ + ${CFDIR}/domain/sigmasoft.m4 \ ${CFDIR}/feature/accept_unqualified_senders.m4 \ ${CFDIR}/feature/accept_unresolvable_domains.m4 \ ${CFDIR}/feature/access_db.m4 \ ${CFDIR}/feature/allmasquerade.m4 \ ${CFDIR}/feature/always_add_domain.m4 \ + ${CFDIR}/feature/authinfo.m4 \ ${CFDIR}/feature/bestmx_is_local.m4 \ ${CFDIR}/feature/bitdomain.m4 \ ${CFDIR}/feature/blacklist_recipients.m4 \ + ${CFDIR}/feature/compat_check.m4 \ + ${CFDIR}/feature/delay_checks.m4 \ ${CFDIR}/feature/dnsbl.m4 \ ${CFDIR}/feature/domaintable.m4 \ + ${CFDIR}/feature/enhdnsbl.m4 \ ${CFDIR}/feature/generics_entire_domain.m4 \ ${CFDIR}/feature/genericstable.m4 \ ${CFDIR}/feature/ldap_routing.m4 \ + ${CFDIR}/feature/msp.m4 \ ${CFDIR}/feature/limited_masquerade.m4 \ ${CFDIR}/feature/local_lmtp.m4 \ + ${CFDIR}/feature/local_no_masquerade.m4 \ ${CFDIR}/feature/local_procmail.m4 \ + ${CFDIR}/feature/lookupdotdomain.m4 \ ${CFDIR}/feature/loose_relay_check.m4 \ ${CFDIR}/feature/mailertable.m4 \ ${CFDIR}/feature/masquerade_entire_domain.m4 \ @@ -74,8 +96,10 @@ M4FILES=\ ${CFDIR}/feature/notsticky.m4 \ ${CFDIR}/feature/nouucp.m4 \ ${CFDIR}/feature/nullclient.m4 \ + ${CFDIR}/feature/preserve_local_plus_detail.m4 \ + ${CFDIR}/feature/preserve_luser_host.m4 \ ${CFDIR}/feature/promiscuous_relay.m4 \ - ${CFDIR}/feature/rbl.m4 \ + ${CFDIR}/feature/queuegroup.m4 \ ${CFDIR}/feature/redirect.m4 \ ${CFDIR}/feature/relay_based_on_MX.m4 \ ${CFDIR}/feature/relay_entire_domain.m4 \ @@ -105,19 +129,22 @@ M4FILES=\ ${CFDIR}/mailer/smtp.m4 \ ${CFDIR}/mailer/usenet.m4 \ ${CFDIR}/mailer/uucp.m4 \ - ${CFDIR}/ostype/aix2.m4 \ ${CFDIR}/ostype/aix3.m4 \ ${CFDIR}/ostype/aix4.m4 \ + ${CFDIR}/ostype/aix5.m4 \ ${CFDIR}/ostype/altos.m4 \ ${CFDIR}/ostype/amdahl-uts.m4 \ + ${CFDIR}/ostype/a-ux.m4 \ ${CFDIR}/ostype/bsd4.3.m4 \ ${CFDIR}/ostype/bsd4.4.m4 \ ${CFDIR}/ostype/bsdi.m4 \ ${CFDIR}/ostype/bsdi1.0.m4 \ ${CFDIR}/ostype/bsdi2.0.m4 \ + ${CFDIR}/ostype/darwin.m4 \ ${CFDIR}/ostype/dgux.m4 \ ${CFDIR}/ostype/domainos.m4 \ ${CFDIR}/ostype/dynix3.2.m4 \ + ${CFDIR}/ostype/freebsd4.m4 \ ${CFDIR}/ostype/gnu.m4 \ ${CFDIR}/ostype/hpux10.m4 \ ${CFDIR}/ostype/hpux11.m4 \ @@ -142,6 +169,7 @@ M4FILES=\ ${CFDIR}/ostype/solaris2.m4 \ ${CFDIR}/ostype/solaris2.ml.m4 \ ${CFDIR}/ostype/solaris2.pre5.m4 \ + ${CFDIR}/ostype/solaris8.m4 \ ${CFDIR}/ostype/sunos3.5.m4 \ ${CFDIR}/ostype/sunos4.1.m4 \ ${CFDIR}/ostype/svr4.m4 \ diff --git a/gnu/usr.sbin/sendmail/cf/cf/courtesan-lists.mc b/gnu/usr.sbin/sendmail/cf/cf/courtesan-lists.mc index 5934c9e3c0a..36a203aee2b 100644 --- a/gnu/usr.sbin/sendmail/cf/cf/courtesan-lists.mc +++ b/gnu/usr.sbin/sendmail/cf/cf/courtesan-lists.mc @@ -6,7 +6,7 @@ divert(-1) # divert(0)dnl -VERSIONID(`$OpenBSD: courtesan-lists.mc,v 1.3 2001/08/01 01:01:40 millert Exp $') +VERSIONID(`$OpenBSD: courtesan-lists.mc,v 1.4 2001/09/11 19:02:48 millert Exp $') OSTYPE(openbsd)dnl dnl dnl Advertise ourselves as ``lists.courtesan.com'' @@ -26,6 +26,10 @@ dnl dnl Always use fully qualified domains FEATURE(always_add_domain) dnl +dnl Some broken nameservers will return SERVFAIL (a temporary failure) +dnl on T_AAAA (IPv6) lookups. +define(`confBIND_OPTS', `WorkAroundBrokenAAAA')dnl +dnl dnl Need to add domo and mailman as "trusted users" to rewrite From lines define(`confTRUSTED_USERS', `domo mailman')dnl dnl diff --git a/gnu/usr.sbin/sendmail/cf/cf/courtesan.mc b/gnu/usr.sbin/sendmail/cf/cf/courtesan.mc index b91f5d3ad56..64b27fef2dd 100644 --- a/gnu/usr.sbin/sendmail/cf/cf/courtesan.mc +++ b/gnu/usr.sbin/sendmail/cf/cf/courtesan.mc @@ -4,7 +4,7 @@ divert(-1) # divert(0)dnl -VERSIONID(`$OpenBSD: courtesan.mc,v 1.6 2001/08/01 01:01:40 millert Exp $') +VERSIONID(`$OpenBSD: courtesan.mc,v 1.7 2001/09/11 19:02:48 millert Exp $') OSTYPE(openbsd) dnl dnl First, we override some default values @@ -13,6 +13,10 @@ define(`confSMTP_LOGIN_MSG', `$m Sendmail $v/$Z/courtesan ready at $b')dnl define(`confMAX_HOP', `20')dnl define(`confMAX_MIME_HEADER_LENGTH', `256/128')dnl dnl +dnl Some broken nameservers will return SERVFAIL (a temporary failure) +dnl on T_AAAA (IPv6) lookups. +define(`confBIND_OPTS', `WorkAroundBrokenAAAA')dnl +dnl dnl Next, we define the features we want FEATURE(nouucp, `reject')dnl FEATURE(always_add_domain)dnl diff --git a/gnu/usr.sbin/sendmail/cf/cf/gandalf.mc b/gnu/usr.sbin/sendmail/cf/cf/gandalf.mc index 360af6a3100..2d05dd8f30b 100644 --- a/gnu/usr.sbin/sendmail/cf/cf/gandalf.mc +++ b/gnu/usr.sbin/sendmail/cf/cf/gandalf.mc @@ -29,14 +29,13 @@ divert(-1) # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -VERSIONID(`$OpenBSD: gandalf.mc,v 1.2 2000/04/02 21:22:35 millert Exp $')dnl +VERSIONID(`$OpenBSD: gandalf.mc,v 1.3 2001/09/11 19:02:48 millert Exp $')dnl OSTYPE(openbsd)dnl DOMAIN(sigmasoft)dnl MASQUERADE_AS(SigmaSoft.COM)dnl FEATURE(allmasquerade)dnl FEATURE(local_procmail)dnl FEATURE(access_db)dnl -define(`confAUTO_REBUILD', True)dnl MAILER(local)dnl MAILER(smtp)dnl MAILER(procmail)dnl diff --git a/gnu/usr.sbin/sendmail/cf/cf/generic-hpux10.mc b/gnu/usr.sbin/sendmail/cf/cf/generic-hpux10.mc index 0fe393e14a6..c7fb58de436 100644 --- a/gnu/usr.sbin/sendmail/cf/cf/generic-hpux10.mc +++ b/gnu/usr.sbin/sendmail/cf/cf/generic-hpux10.mc @@ -20,7 +20,7 @@ divert(-1) # divert(0)dnl -VERSIONID(`$Sendmail: generic-hpux10.mc,v 8.11.22.2 2001/05/29 17:30:18 ca Exp $') +VERSIONID(`$Sendmail: generic-hpux10.mc,v 8.13 2001/05/29 17:29:52 ca Exp $') OSTYPE(hpux10)dnl DOMAIN(generic)dnl MAILER(local)dnl diff --git a/gnu/usr.sbin/sendmail/cf/cf/knecht.mc b/gnu/usr.sbin/sendmail/cf/cf/knecht.mc index 394a53bb7dc..114bf8a455a 100644 --- a/gnu/usr.sbin/sendmail/cf/cf/knecht.mc +++ b/gnu/usr.sbin/sendmail/cf/cf/knecht.mc @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999, 2001 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -15,31 +15,54 @@ divert(-1) # # This is specific to Eric's home machine. # +# Run daemon with -bd -q5m +# + +divert(0) +VERSIONID(`$Sendmail: knecht.mc,v 8.55 2001/08/01 22:20:40 eric Exp $') +OSTYPE(bsd4.4) +DOMAIN(generic) + +define(`ALIAS_FILE', ``/etc/mail/aliases, /var/listmanager/aliases'') +define(`confFORWARD_PATH', `$z/.forward.$w:$z/.forward+$h:$z/.forward') +define(`confDEF_USER_ID', `mailnull') +define(`confHOST_STATUS_DIRECTORY', `.hoststat') +define(`confTO_ICONNECT', `10s') +define(`confCOPY_ERRORS_TO', `Postmaster') +define(`confTO_QUEUEWARN', `8h') +define(`confMIN_QUEUE_AGE', `27m') +define(`confTRUSTED_USERS', ``www listmgr'') +define(`confPRIVACY_FLAGS', ``authwarnings,noexpn,novrfy'') + +define(`CERT_DIR', `MAIL_SETTINGS_DIR`'certs') +define(`confCACERT_PATH', `CERT_DIR') +define(`confCACERT', `CERT_DIR/CAcert.pem') +define(`confSERVER_CERT', `CERT_DIR/MYcert.pem') +define(`confSERVER_KEY', `CERT_DIR/MYkey.pem') +define(`confCLIENT_CERT', `CERT_DIR/MYcert.pem') +define(`confCLIENT_KEY', `CERT_DIR/MYkey.pem') + +FEATURE(access_db) +FEATURE(local_lmtp) +FEATURE(virtusertable) + +FEATURE(`nocanonify', `canonify_hosts') +CANONIFY_DOMAIN(`sendmail.org') +CANONIFY_DOMAIN_FILE(`/etc/mail/canonify-domains') + +dnl # at most 10 queue runners +define(`confMAX_QUEUE_CHILDREN', `20') + +define(`confMAX_RUNNERS_PER_QUEUE', `5') + +dnl # run at most 10 concurrent processes for initial submission +define(`confFAST_SPLIT', `10') -divert(0)dnl -VERSIONID(`$Sendmail: knecht.mc,v 8.37.16.3 2001/02/22 22:38:39 ca Exp $') -OSTYPE(bsd4.4)dnl -DOMAIN(generic)dnl -define(`confFORWARD_PATH', `$z/.forward.$w:$z/.forward+$h:$z/.forward')dnl -define(`confDEF_USER_ID', `mailnull')dnl -define(`confHOST_STATUS_DIRECTORY', `.hoststat')dnl -define(`confTO_ICONNECT', `10s')dnl -define(`confCOPY_ERRORS_TO', `Postmaster')dnl -define(`confTO_QUEUEWARN', `8h')dnl -define(`confTRUSTED_USERS', `www')dnl -define(`confPRIVACY_FLAGS', ``authwarnings,noexpn,novrfy'')dnl -define(`CERT_DIR', `MAIL_SETTINGS_DIR`'certs')dnl -define(`confCACERT_PATH', `CERT_DIR')dnl -define(`confCACERT', `CERT_DIR/CAcert.pem')dnl -define(`confSERVER_CERT', `CERT_DIR/MYcert.pem')dnl -define(`confSERVER_KEY', `CERT_DIR/MYkey.pem')dnl -define(`confCLIENT_CERT', `CERT_DIR/MYcert.pem')dnl -define(`confCLIENT_KEY', `CERT_DIR/MYkey.pem')dnl -FEATURE(virtusertable)dnl -FEATURE(access_db)dnl -FEATURE(local_lmtp)dnl -MAILER(local)dnl -MAILER(smtp)dnl +dnl # 10 runners, split into at most 15 recipients per envelope +QUEUE_GROUP(`mqueue', `P=/var/spool/mqueue, R=5, r=15, F=f') + +MAILER(local) +MAILER(smtp) LOCAL_CONFIG # @@ -69,9 +92,80 @@ SCheckMessageId R< $+ @ $+ > $@ OK R$* $#error $: "554 Header error" +HReceived: $>CheckReceived + +SCheckReceived +R$* ......................................................... $* + $#error $: "554 Header error" + +# +# Reject certain senders +# Regex match to catch things in quotes +# +HFrom: $>+CheckFrom +KCheckFrom regex -a@MATCH + [^a-z]?(Net-Pa)[^a-z] + +SCheckFrom +R$* $: $( CheckFrom $1 $) +R@MATCH $#error $: "553 Header error" + LOCAL_RULESETS SLocal_check_mail # check address against various regex checks R$* $: $>Parse0 $>3 $1 R$+ $: $(checkaddress $1 $) R@MATCH $#error $: "553 Header error" + +# +# Following code from Anthony Howe <achowe@snert.com>. The check +# for the Outlook Express marker may hit some legal messages, but +# the Content-Disposition is clearly illegal. +# + +######################################################################### +# +# w32.sircam.worm@mm +# +# There are serveral patterns that appear common ONLY to SirCam worm and +# not to Outlook Express, which claims to have sent the worm. There are +# four headers that always appear together and in this order: +# +# X-MIMEOLE: Produced By Microsoft MimeOLE V5.50.4133.2400 +# X-Mailer: Microsoft Outlook Express 5.50.4133.2400 +# Content-Type: multipart/mixed; boundary="----27AA9124_Outlook_Express_message_boundary" +# Content-Disposition: Multipart message +# +# Empirical study of the worm message headers vs. true Outlook Express +# (5.50.4133.2400 & 5.50.4522.1200) messages with multipart/mixed attachments +# shows Outlook Express does: +# +# a) NOT supply a Content-Disposition header for multipart/mixed messages. +# b) NOT specify the header X-MimeOLE header name in all-caps +# c) NOT specify boundary tag with the expression "_Outlook_Express_message_boundary" +# +# The solution below catches any one of this three issues. This is not an ideal +# solution, but a temporary measure. A correct solution would be to check for +# the presence of ALL three header attributes. Also the solution is incomplete +# since Outlook Express 5.0 and 4.0 were not compared. +# +# NOTE regex keys are first dequoted and spaces removed before matching. +# This caused me no end of grief. +# +######################################################################### + +LOCAL_RULESETS + +KSirCamWormMarker regex -f -aSUSPECT multipart/mixed;boundary=----.+_Outlook_Express_message_boundary +HContent-Type: $>CheckContentType + +SCheckContentType +R$+ $: $(SirCamWormMarker $1 $) +RSUSPECT $#error $: "553 Possible virus, see http://www.symantec.com/avcenter/venc/data/w32.sircam.worm@mm.html" + +HContent-Disposition: $>CheckContentDisposition + +SCheckContentDisposition +R$- $@ OK +R$- ; $+ $@ OK +R$* $#error $: "553 Illegal Content-Disposition" diff --git a/gnu/usr.sbin/sendmail/cf/cf/lucifier.mc b/gnu/usr.sbin/sendmail/cf/cf/lucifier.mc index a73d331fdc4..2ef1b38b02e 100644 --- a/gnu/usr.sbin/sendmail/cf/cf/lucifier.mc +++ b/gnu/usr.sbin/sendmail/cf/cf/lucifier.mc @@ -30,17 +30,17 @@ divert(-1) # SUCH DAMAGE. # -VERSIONID(`$OpenBSD: lucifier.mc,v 1.1 2000/04/02 19:48:13 millert Exp $')dnl +VERSIONID(`$OpenBSD: lucifier.mc,v 1.2 2001/09/11 19:02:48 millert Exp $')dnl OSTYPE(openbsd)dnl -MAILER(local)dnl -MAILER(smtp)dnl MASQUERADE_AS(lucifier.dial-up.user.akula.net)dnl MASQUERADE_DOMAIN(lucifier.dial-up.user.akula.net)dnl FEATURE(allmasquerade)dnl +MAILER(local)dnl +MAILER(smtp)dnl + define(`BITNET_RELAY', relay.uu.net)dnl -define(`confAUTO_REBUILD', True)dnl define(`confCHECK_ALIASES', True)dnl define(`confMIN_FREE_BLOCKS', 1024)dnl diff --git a/gnu/usr.sbin/sendmail/cf/cf/openbsd-lists.mc b/gnu/usr.sbin/sendmail/cf/cf/openbsd-lists.mc index 27706ad7fe6..a08746733df 100644 --- a/gnu/usr.sbin/sendmail/cf/cf/openbsd-lists.mc +++ b/gnu/usr.sbin/sendmail/cf/cf/openbsd-lists.mc @@ -6,7 +6,7 @@ divert(-1) # divert(0)dnl -VERSIONID(`$OpenBSD: openbsd-lists.mc,v 1.5 2001/08/01 01:01:40 millert Exp $') +VERSIONID(`$OpenBSD: openbsd-lists.mc,v 1.6 2001/09/11 19:02:48 millert Exp $') OSTYPE(openbsd)dnl dnl dnl Advertise ourselves as ``openbsd.org'' @@ -22,6 +22,10 @@ define(`confPRIVACY_FLAGS', `authwarnings, nobodyreturn')dnl define(`confTRY_NULL_MX_LIST', `True')dnl define(`confMAX_HOP', `30')dnl dnl +dnl Some broken nameservers will return SERVFAIL (a temporary failure) +dnl on T_AAAA (IPv6) lookups. +define(`confBIND_OPTS', `WorkAroundBrokenAAAA')dnl +dnl dnl Keep host status on disk between sendmail runs in the .hoststat dir define(`confHOST_STATUS_DIRECTORY', `.hoststat')dnl define(`confTO_HOSTSTATUS', `1h')dnl diff --git a/gnu/usr.sbin/sendmail/cf/cf/openbsd-proto.mc b/gnu/usr.sbin/sendmail/cf/cf/openbsd-proto.mc index 6b0293a46b9..0c58549d405 100644 --- a/gnu/usr.sbin/sendmail/cf/cf/openbsd-proto.mc +++ b/gnu/usr.sbin/sendmail/cf/cf/openbsd-proto.mc @@ -17,7 +17,7 @@ divert(-1) # divert(0)dnl -VERSIONID(`@(#)openbsd-proto.mc $Revision: 1.3 $') +VERSIONID(`@(#)openbsd-proto.mc $Revision: 1.4 $') OSTYPE(openbsd) FEATURE(nouucp, `reject') MAILER(local) @@ -25,6 +25,10 @@ MAILER(smtp) DAEMON_OPTIONS(`Family=inet, address=0.0.0.0, Name=MTA')dnl DAEMON_OPTIONS(`Family=inet6, address=::, Name=MTA6, M=O')dnl dnl +dnl Some broken nameservers will return SERVFAIL (a temporary failure) +dnl on T_AAAA (IPv6) lookups. +define(`confBIND_OPTS', `WorkAroundBrokenAAAA')dnl +dnl dnl Enforce valid Message-Id to help stop spammers dnl LOCAL_RULESETS diff --git a/gnu/usr.sbin/sendmail/cf/cf/tcpproto.mc b/gnu/usr.sbin/sendmail/cf/cf/tcpproto.mc index f7abc26b043..55a1fc85361 100644 --- a/gnu/usr.sbin/sendmail/cf/cf/tcpproto.mc +++ b/gnu/usr.sbin/sendmail/cf/cf/tcpproto.mc @@ -26,7 +26,7 @@ divert(-1) # divert(0)dnl -VERSIONID(`$Sendmail: tcpproto.mc,v 8.13.22.1 2000/08/03 15:25:20 ca Exp $') +VERSIONID(`$Sendmail: tcpproto.mc,v 8.14 2000/08/03 15:26:50 ca Exp $') OSTYPE(`openbsd') FEATURE(`nouucp', `reject') MAILER(`local') diff --git a/gnu/usr.sbin/sendmail/cf/cf/uucpproto.mc b/gnu/usr.sbin/sendmail/cf/cf/uucpproto.mc index e2dd7a3b1db..afd5e4d097a 100644 --- a/gnu/usr.sbin/sendmail/cf/cf/uucpproto.mc +++ b/gnu/usr.sbin/sendmail/cf/cf/uucpproto.mc @@ -27,7 +27,7 @@ divert(-1) divert(0)dnl VERSIONID(`$Sendmail: uucpproto.mc,v 8.15 1999/02/07 07:26:05 gshapiro Exp $') -OSTYPE(unknown) +OSTYPE(openbsd) FEATURE(promiscuous_relay)dnl FEATURE(accept_unresolvable_domains)dnl MAILER(local)dnl diff --git a/gnu/usr.sbin/sendmail/cf/cf/waldorf.mc b/gnu/usr.sbin/sendmail/cf/cf/waldorf.mc index 9bbbc003d31..9fcf3e5ce43 100644 --- a/gnu/usr.sbin/sendmail/cf/cf/waldorf.mc +++ b/gnu/usr.sbin/sendmail/cf/cf/waldorf.mc @@ -1,5 +1,5 @@ divert(-1) -# $OpenBSD: waldorf.mc,v 1.1 2000/04/02 19:48:14 millert Exp $ +# $OpenBSD: waldorf.mc,v 1.2 2001/09/11 19:02:48 millert Exp $ # # Copyright (c) 1996 Niklas Hallqvist # All rights reserved. @@ -32,7 +32,7 @@ divert(-1) # SUCH DAMAGE. # -VERSIONID(`$OpenBSD: waldorf.mc,v 1.1 2000/04/02 19:48:14 millert Exp $') +VERSIONID(`$OpenBSD: waldorf.mc,v 1.2 2001/09/11 19:02:48 millert Exp $') OSTYPE(openbsd)dnl MASQUERADE_AS(appli.se) @@ -40,15 +40,13 @@ MASQUERADE_DOMAIN(appli.se) FEATURE(local_procmail)dnl -MAILER(local)dnl -MAILER(smtp)dnl - FEATURE(limited_masquerade)dnl FEATURE(always_add_domain)dnl FEATURE(virtusertable)dnl FEATURE(use_cw_file)dnl -define(`confAUTO_REBUILD', True)dnl +MAILER(local)dnl +MAILER(smtp)dnl LOCAL_RULE_0 # We take care of all mail directed to either appli.se or *.appli.se diff --git a/gnu/usr.sbin/sendmail/cf/domain/sigmasoft.m4 b/gnu/usr.sbin/sendmail/cf/domain/sigmasoft.m4 index 0df67ecfbb6..458dcd92be6 100644 --- a/gnu/usr.sbin/sendmail/cf/domain/sigmasoft.m4 +++ b/gnu/usr.sbin/sendmail/cf/domain/sigmasoft.m4 @@ -29,10 +29,10 @@ divert(-1) # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # divert(0) -VERSIONID(`$OpenBSD: sigmasoft.m4,v 1.1 2000/04/02 19:48:14 millert Exp $')dnl +VERSIONID(`$OpenBSD: sigmasoft.m4,v 1.2 2001/09/11 19:02:48 millert Exp $')dnl define(`UUCP_RELAY', relay1.uu.net)dnl define(`BITNET_RELAY', relay2.uu.net)dnl define(`confME_TOO', True)dnl -FEATURE(rbl)dnl +FEATURE(dnsbl, `rbl.maps.vix.com', `Rejected - see http://www.mail-abuse.org/rbl/')dnl FEATURE(redirect)dnl FEATURE(relay_based_on_MX)dnl diff --git a/gnu/usr.sbin/sendmail/cf/feature/access_db.m4 b/gnu/usr.sbin/sendmail/cf/feature/access_db.m4 index 9e13f9c59cb..de078d94707 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/access_db.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/access_db.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. # All rights reserved. # # By using this file, you agree to the terms and conditions set @@ -10,14 +10,28 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: access_db.m4,v 8.15 1999/07/22 17:55:34 gshapiro Exp $') +VERSIONID(`$Sendmail: access_db.m4,v 8.23 2001/03/16 00:51:25 gshapiro Exp $') divert(-1) define(`_ACCESS_TABLE_', `') define(`_TAG_DELIM_', `:')dnl should be in OperatorChars +ifelse(lower(_ARG2_),`skip',`define(`_ACCESS_SKIP_', `1')') +ifelse(lower(_ARG2_),`lookupdotdomain',`define(`_LOOKUPDOTDOMAIN_', `1')') +ifelse(lower(_ARG3_),`skip',`define(`_ACCESS_SKIP_', `1')') +ifelse(lower(_ARG3_),`lookupdotdomain',`define(`_LOOKUPDOTDOMAIN_', `1')') +define(`_ATMPF_', `<TMPF>')dnl +dnl check whether arg contains -T`'_ATMPF_ +ifelse(defn(`_ARG_'), `', `', + defn(`_ARG_'), `LDAP', `', + `ifelse(index(_ARG_, _ATMPF_), `-1', + `errprint(`*** WARNING: missing -T'_ATMPF_` in argument of FEATURE(`access_db',' defn(`_ARG_')`) +') + define(`_ABP_', index(_ARG_, ` ')) + define(`_NARG_', `substr(_ARG_, 0, _ABP_) -T'_ATMPF_` substr(_ARG_, _ABP_)') +')') LOCAL_CONFIG # Access list database (for spam stomping) -Kaccess ifelse(defn(`_ARG_'), `', - DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`access', - `_ARG_') +Kaccess ifelse(defn(`_ARG_'), `', DATABASE_MAP_TYPE -T`'_ATMPF_ MAIL_SETTINGS_DIR`access', + defn(`_ARG_'), `LDAP', `ldap -T`'_ATMPF_ -1 -v sendmailMTAMapValue -k (&(objectClass=sendmailMTAMapObject)(|(sendmailMTACluster=${sendmailMTACluster})(sendmailMTAHost=$j))(sendmailMTAMapName=access)(sendmailMTAKey=%0))', + defn(`_NARG_'), `', `_ARG_', `_NARG_') diff --git a/gnu/usr.sbin/sendmail/cf/feature/allmasquerade.m4 b/gnu/usr.sbin/sendmail/cf/feature/allmasquerade.m4 index 731d7b5fc41..1a6819331d5 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/allmasquerade.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/allmasquerade.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -13,7 +13,13 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: allmasquerade.m4,v 8.11 1999/08/06 01:28:26 gshapiro Exp $') +VERSIONID(`$Sendmail: allmasquerade.m4,v 8.13 2000/09/12 22:00:53 ca Exp $') divert(-1) +ifdef(`_MAILER_local_', + `errprint(`*** MAILER(`local') must appear after FEATURE(`allmasquerade')') +')dnl +ifdef(`_MAILER_uucp_', + `errprint(`*** MAILER(`uucp') must appear after FEATURE(`allmasquerade')') +')dnl define(`_ALL_MASQUERADE_', 1) diff --git a/gnu/usr.sbin/sendmail/cf/feature/always_add_domain.m4 b/gnu/usr.sbin/sendmail/cf/feature/always_add_domain.m4 index c7c6ebfcc42..f899347bd67 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/always_add_domain.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/always_add_domain.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -13,7 +13,10 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: always_add_domain.m4,v 8.9 1999/02/07 07:26:08 gshapiro Exp $') +VERSIONID(`$Sendmail: always_add_domain.m4,v 8.11 2000/09/12 22:00:53 ca Exp $') divert(-1) -define(`_ALWAYS_ADD_DOMAIN_', 1) +ifdef(`_MAILER_local_', + `errprint(`*** MAILER(`local') must appear after FEATURE(`always_add_domain')') +')dnl +define(`_ALWAYS_ADD_DOMAIN_', ifelse(len(X`'_ARG_),`1',`',_ARG_)) diff --git a/gnu/usr.sbin/sendmail/cf/feature/bestmx_is_local.m4 b/gnu/usr.sbin/sendmail/cf/feature/bestmx_is_local.m4 index b8065ddccfb..7938d9d8b41 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/bestmx_is_local.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/bestmx_is_local.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -13,10 +13,10 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: bestmx_is_local.m4,v 8.24 1999/10/18 21:50:24 ca Exp $') +VERSIONID(`$Sendmail: bestmx_is_local.m4,v 8.26 2000/09/17 17:30:00 gshapiro Exp $') divert(-1) -define(_BESTMX_IS_LOCAL_, _ARG_) +define(`_BESTMX_IS_LOCAL_', _ARG_) LOCAL_CONFIG # turn on bestMX lookup table diff --git a/gnu/usr.sbin/sendmail/cf/feature/bitdomain.m4 b/gnu/usr.sbin/sendmail/cf/feature/bitdomain.m4 index d6423dd5470..57bada72479 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/bitdomain.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/bitdomain.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998, 1999, 2001 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -13,13 +13,13 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: bitdomain.m4,v 8.23 1999/07/22 17:55:34 gshapiro Exp $') +VERSIONID(`$Sendmail: bitdomain.m4,v 8.28 2001/03/16 00:51:25 gshapiro Exp $') divert(-1) define(`_BITDOMAIN_TABLE_', `') LOCAL_CONFIG # BITNET mapping table -Kbitdomain ifelse(defn(`_ARG_'), `', - DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`bitdomain', +Kbitdomain ifelse(defn(`_ARG_'), `', DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`bitdomain', + defn(`_ARG_'), `LDAP', `ldap -1 -v sendmailMTAMapValue -k (&(objectClass=sendmailMTAMapObject)(|(sendmailMTACluster=${sendmailMTACluster})(sendmailMTAHost=$j))(sendmailMTAMapName=bitdomain)(sendmailMTAKey=%0))', `_ARG_') diff --git a/gnu/usr.sbin/sendmail/cf/feature/delay_checks.m4 b/gnu/usr.sbin/sendmail/cf/feature/delay_checks.m4 index be92bcd6665..0791f488137 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/delay_checks.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/delay_checks.m4 @@ -10,7 +10,7 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: delay_checks.m4,v 8.7 2000/02/26 01:32:02 gshapiro Exp $') +VERSIONID(`$Sendmail: delay_checks.m4,v 8.8 2000/12/05 18:50:45 ca Exp $') divert(-1) define(`_DELAY_CHECKS_', 1) @@ -20,3 +20,6 @@ ifelse(defn(`_ARG_'), `', `', `errprint(`*** ERROR: illegal argument _ARG_ for FEATURE(delay_checks) ') ') + +dnl be backward compatible by default +ifelse(len(X`'_ARG2_), `1', `define(`_DELAY_COMPAT_8_10_', 1)', `') diff --git a/gnu/usr.sbin/sendmail/cf/feature/dnsbl.m4 b/gnu/usr.sbin/sendmail/cf/feature/dnsbl.m4 index 4c595e98f5c..a66f47ed8cb 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/dnsbl.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/dnsbl.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. # All rights reserved. # # By using this file, you agree to the terms and conditions set @@ -11,15 +11,22 @@ divert(-1) divert(0) ifdef(`_DNSBL_R_',`dnl',`dnl -VERSIONID(`$Sendmail: dnsbl.m4,v 8.18.16.1 2000/11/22 01:13:21 ca Exp $')') +VERSIONID(`$Sendmail: dnsbl.m4,v 8.24 2001/03/29 20:48:45 gshapiro Exp $') +define(`_DNSBL_R_',`') +LOCAL_CONFIG +# map for DNS based blacklist lookups +Kdnsbl host -T<TMP>') divert(-1) define(`_DNSBL_SRV_', `ifelse(len(X`'_ARG_),`1',`blackholes.mail-abuse.org',_ARG_)')dnl define(`_DNSBL_MSG_', `ifelse(len(X`'_ARG2_),`1',`"550 Mail from " $`'&{client_addr} " refused by blackhole site '_DNSBL_SRV_`"',`_ARG2_')')dnl +define(`_DNSBL_MSG_TMP_', `ifelse(_ARG3_,`t',`"451 Temporary lookup failure of " $`'&{client_addr} " at '_DNSBL_SRV_`"',`_ARG2_')')dnl divert(8) # DNS based IP address spam list _DNSBL_SRV_ R$* $: $&{client_addr} -R::ffff:$-.$-.$-.$- $: <?> $(host $4.$3.$2.$1._DNSBL_SRV_. $: OK $) -R$-.$-.$-.$- $: <?> $(host $4.$3.$2.$1._DNSBL_SRV_. $: OK $) +R$-.$-.$-.$- $: <?> $(dnsbl $4.$3.$2.$1._DNSBL_SRV_. $: OK $) R<?>OK $: OKSOFAR +ifelse(len(X`'_ARG3_),`1', +`R<?>$+<TMP> $: TMPOK', +`R<?>$+<TMP> $#error $@ 4.7.1 $: _DNSBL_MSG_TMP_') R<?>$+ $#error $@ 5.7.1 $: _DNSBL_MSG_ divert(-1) diff --git a/gnu/usr.sbin/sendmail/cf/feature/domaintable.m4 b/gnu/usr.sbin/sendmail/cf/feature/domaintable.m4 index 5542e315f29..c2a27a493ca 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/domaintable.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/domaintable.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998, 1999, 2001 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -13,13 +13,13 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: domaintable.m4,v 8.17 1999/07/22 17:55:35 gshapiro Exp $') +VERSIONID(`$Sendmail: domaintable.m4,v 8.22 2001/03/16 00:51:25 gshapiro Exp $') divert(-1) define(`_DOMAIN_TABLE_', `') LOCAL_CONFIG # Domain table (adding domains) -Kdomaintable ifelse(defn(`_ARG_'), `', - DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`domaintable', +Kdomaintable ifelse(defn(`_ARG_'), `', DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`domaintable', + defn(`_ARG_'), `LDAP', `ldap -1 -v sendmailMTAMapValue -k (&(objectClass=sendmailMTAMapObject)(|(sendmailMTACluster=${sendmailMTACluster})(sendmailMTAHost=$j))(sendmailMTAMapName=domain)(sendmailMTAKey=%0))', `_ARG_') diff --git a/gnu/usr.sbin/sendmail/cf/feature/genericstable.m4 b/gnu/usr.sbin/sendmail/cf/feature/genericstable.m4 index f03a7af8530..72b6790c1a8 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/genericstable.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/genericstable.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998, 1999, 2001 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -13,13 +13,13 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: genericstable.m4,v 8.16 1999/07/22 17:55:35 gshapiro Exp $') +VERSIONID(`$Sendmail: genericstable.m4,v 8.21 2001/03/16 00:51:26 gshapiro Exp $') divert(-1) define(`_GENERICS_TABLE_', `') LOCAL_CONFIG # Generics table (mapping outgoing addresses) -Kgenerics ifelse(defn(`_ARG_'), `', - DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`genericstable', +Kgenerics ifelse(defn(`_ARG_'), `', DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`genericstable', + defn(`_ARG_'), `LDAP', `ldap -1 -v sendmailMTAMapValue -k (&(objectClass=sendmailMTAMapObject)(|(sendmailMTACluster=${sendmailMTACluster})(sendmailMTAHost=$j))(sendmailMTAMapName=generics)(sendmailMTAKey=%0))', `_ARG_') diff --git a/gnu/usr.sbin/sendmail/cf/feature/ldap_routing.m4 b/gnu/usr.sbin/sendmail/cf/feature/ldap_routing.m4 index 4f2e7799fa4..677ce67d8c5 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/ldap_routing.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/ldap_routing.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers. +# Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers. # All rights reserved. # # By using this file, you agree to the terms and conditions set @@ -10,7 +10,7 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: ldap_routing.m4,v 8.5.4.1 2000/07/15 18:05:05 gshapiro Exp $') +VERSIONID(`$Sendmail: ldap_routing.m4,v 8.8 2001/06/27 21:46:31 gshapiro Exp $') divert(-1) # Check first two arguments. If they aren't set, may need to warn in proto.m4 @@ -23,6 +23,11 @@ ifelse(len(X`'_ARG3_), `1', `define(`_LDAP_ROUTING_', `_PASS_THROUGH_')', _ARG3_, `passthru', `define(`_LDAP_ROUTING_', `_PASS_THROUGH_')', `define(`_LDAP_ROUTING_', `_MUST_EXIST_')') +# Check for fouth argument to indicate how to deal with +detail info +ifelse(len(X`'_ARG4_), `1', `', + _ARG4_, `strip', `define(`_LDAP_ROUTE_DETAIL_', `_STRIP_')', + _ARG4_, `preserve', `define(`_LDAP_ROUTE_DETAIL_', `_PRESERVE_')') + LOCAL_CONFIG # LDAP routing maps Kldapmh ifelse(len(X`'_ARG1_), `1', diff --git a/gnu/usr.sbin/sendmail/cf/feature/local_lmtp.m4 b/gnu/usr.sbin/sendmail/cf/feature/local_lmtp.m4 index f680970fd3b..b7d74fa843a 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/local_lmtp.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/local_lmtp.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. # All rights reserved. # # By using this file, you agree to the terms and conditions set @@ -10,7 +10,7 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: local_lmtp.m4,v 8.15 1999/11/18 05:06:22 ca Exp $') +VERSIONID(`$Sendmail: local_lmtp.m4,v 8.16 2000/08/18 18:58:45 ca Exp $') divert(-1) ifdef(`_MAILER_local_', @@ -24,3 +24,4 @@ define(`LOCAL_MAILER_PATH', define(`LOCAL_MAILER_FLAGS', `PSXfmnz9') define(`LOCAL_MAILER_ARGS', `mail.local -l') define(`LOCAL_MAILER_DSN_DIAGNOSTIC_CODE', `SMTP') +define(`_LOCAL_LMTP_', `1') diff --git a/gnu/usr.sbin/sendmail/cf/feature/mailertable.m4 b/gnu/usr.sbin/sendmail/cf/feature/mailertable.m4 index ad6088e0d66..b425860cc2e 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/mailertable.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/mailertable.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998, 1999, 2001 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -13,13 +13,13 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: mailertable.m4,v 8.18 1999/07/22 17:55:35 gshapiro Exp $') +VERSIONID(`$Sendmail: mailertable.m4,v 8.23 2001/03/16 00:51:26 gshapiro Exp $') divert(-1) define(`_MAILER_TABLE_', `') LOCAL_CONFIG # Mailer table (overriding domains) -Kmailertable ifelse(defn(`_ARG_'), `', - DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`mailertable', +Kmailertable ifelse(defn(`_ARG_'), `', DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`mailertable', + defn(`_ARG_'), `LDAP', `ldap -1 -v sendmailMTAMapValue -k (&(objectClass=sendmailMTAMapObject)(|(sendmailMTACluster=${sendmailMTACluster})(sendmailMTAHost=$j))(sendmailMTAMapName=mailer)(sendmailMTAKey=%0))', `_ARG_') diff --git a/gnu/usr.sbin/sendmail/cf/feature/no_default_msa.m4 b/gnu/usr.sbin/sendmail/cf/feature/no_default_msa.m4 index 52a18653f21..11aa676aa92 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/no_default_msa.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/no_default_msa.m4 @@ -10,7 +10,7 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: no_default_msa.m4,v 8.1.10.1 2000/09/17 17:04:22 gshapiro Exp $') +VERSIONID(`$Sendmail: no_default_msa.m4,v 8.2 2001/02/14 05:03:22 gshapiro Exp $') divert(-1) define(`_NO_MSA_', `1') diff --git a/gnu/usr.sbin/sendmail/cf/feature/nullclient.m4 b/gnu/usr.sbin/sendmail/cf/feature/nullclient.m4 index 568774bfb18..656c6b7f193 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/nullclient.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/nullclient.m4 @@ -22,7 +22,7 @@ ifelse(defn(`_ARG_'), `', `errprint(`Feature "nullclient" requires argument')', # divert(0) -VERSIONID(`$Sendmail: nullclient.m4,v 8.21.16.3 2000/09/17 17:04:22 gshapiro Exp $') +VERSIONID(`$Sendmail: nullclient.m4,v 8.24 2000/09/17 17:30:00 gshapiro Exp $') divert(-1) undefine(`ALIAS_FILE') diff --git a/gnu/usr.sbin/sendmail/cf/feature/promiscuous_relay.m4 b/gnu/usr.sbin/sendmail/cf/feature/promiscuous_relay.m4 index 8aac82e1965..4d9d711b9cf 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/promiscuous_relay.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/promiscuous_relay.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-1999, 2001 Sendmail, Inc. and its suppliers. # All rights reserved. # # By using this file, you agree to the terms and conditions set @@ -10,7 +10,10 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: promiscuous_relay.m4,v 8.10 1999/02/07 07:26:11 gshapiro Exp $') +VERSIONID(`$Sendmail: promiscuous_relay.m4,v 8.12 2001/02/06 17:14:35 ca Exp $') divert(-1) define(`_PROMISCUOUS_RELAY_', 1) +errprint(`*** WARNING: FEATURE(`promiscuous_relay') configures your system as open + relay. Do NOT use it on a server that is connected to the Internet! +') diff --git a/gnu/usr.sbin/sendmail/cf/feature/relay_local_from.m4 b/gnu/usr.sbin/sendmail/cf/feature/relay_local_from.m4 index 8be79252014..d793976c9eb 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/relay_local_from.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/relay_local_from.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-1999, 2001 Sendmail, Inc. and its suppliers. # All rights reserved. # # By using this file, you agree to the terms and conditions set @@ -10,7 +10,11 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: relay_local_from.m4,v 8.5 1999/02/07 07:26:12 gshapiro Exp $') +VERSIONID(`$Sendmail: relay_local_from.m4,v 8.6 2001/02/06 15:55:21 ca Exp $') divert(-1) define(`_RELAY_LOCAL_FROM_', 1) +errprint(`*** WARNING: FEATURE(`relay_local_from') may cause your system to act as open + relay. Use SMTP AUTH or STARTTLS instead. If you cannot use those, + try FEATURE(`relay_mail_from'). +') diff --git a/gnu/usr.sbin/sendmail/cf/feature/relay_mail_from.m4 b/gnu/usr.sbin/sendmail/cf/feature/relay_mail_from.m4 index e65890c1c44..fc134746a1f 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/relay_mail_from.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/relay_mail_from.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1999, 2001 Sendmail, Inc. and its suppliers. # All rights reserved. # # By using this file, you agree to the terms and conditions set @@ -10,11 +10,14 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: relay_mail_from.m4,v 8.2 1999/04/02 02:25:13 gshapiro Exp $') +VERSIONID(`$Sendmail: relay_mail_from.m4,v 8.3 2001/02/06 16:07:12 ca Exp $') divert(-1) ifdef(`_ACCESS_TABLE_', `define(`_RELAY_DB_FROM_', 1) ifelse(_ARG_,`domain',`define(`_RELAY_DB_FROM_DOMAIN_', 1)')', - `errprint(`*** ERROR: FEATURE(relay_mail_from) requires FEATURE(access_db) + `errprint(`*** ERROR: FEATURE(`relay_mail_from') requires FEATURE(`access_db') ')') +errprint(`*** WARNING: FEATURE(`relay_mail_from') may cause your system to act as open + relay. Use SMTP AUTH or STARTTLS instead. +') diff --git a/gnu/usr.sbin/sendmail/cf/feature/use_ct_file.m4 b/gnu/usr.sbin/sendmail/cf/feature/use_ct_file.m4 index db6c308e6ab..c010fdf655b 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/use_ct_file.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/use_ct_file.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998, 1999, 2001 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -13,12 +13,11 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: use_ct_file.m4,v 8.9 1999/02/07 07:26:13 gshapiro Exp $') +VERSIONID(`$Sendmail: use_ct_file.m4,v 8.11 2001/08/26 20:58:57 gshapiro Exp $') divert(-1) -# if defined, the sendmail.cf will read the /etc/sendmail.ct file -# to find the names of trusted users. There should only be a few -# of these, and normally this is done directly in the .cf file. +# if defined, the sendmail.cf will read the /etc/mail/trusted-users file to +# find the names of trusted users. There should only be a few of these. define(`_USE_CT_FILE_', `') diff --git a/gnu/usr.sbin/sendmail/cf/feature/use_cw_file.m4 b/gnu/usr.sbin/sendmail/cf/feature/use_cw_file.m4 index bad558f352e..15e77707cef 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/use_cw_file.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/use_cw_file.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998, 1999, 2001 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -13,12 +13,12 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: use_cw_file.m4,v 8.9 1999/02/07 07:26:13 gshapiro Exp $') +VERSIONID(`$Sendmail: use_cw_file.m4,v 8.11 2001/08/26 20:58:57 gshapiro Exp $') divert(-1) -# if defined, the sendmail.cf will read the /etc/sendmail.cw file -# to find alternate names for this host. Typically only used when -# several hosts have been squashed into one another at high speed. +# if defined, the sendmail.cf will read the /etc/mail/local-host-names file +# to find alternate names for this host. Typically only used when several +# hosts have been squashed into one another at high speed. define(`USE_CW_FILE', `') diff --git a/gnu/usr.sbin/sendmail/cf/feature/uucpdomain.m4 b/gnu/usr.sbin/sendmail/cf/feature/uucpdomain.m4 index 0f7f78b99f2..88e975d58ae 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/uucpdomain.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/uucpdomain.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998, 1999, 2001 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -13,13 +13,13 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: uucpdomain.m4,v 8.22 1999/07/22 17:55:35 gshapiro Exp $') +VERSIONID(`$Sendmail: uucpdomain.m4,v 8.27 2001/03/16 00:51:26 gshapiro Exp $') divert(-1) define(`_UUDOMAIN_TABLE_', `') LOCAL_CONFIG # UUCP domain table -Kuudomain ifelse(defn(`_ARG_'), `', - DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`uudomain', +Kuudomain ifelse(defn(`_ARG_'), `', DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`uudomain', + defn(`_ARG_'), `LDAP', `ldap -1 -v sendmailMTAMapValue -k (&(objectClass=sendmailMTAMapObject)(|(sendmailMTACluster=${sendmailMTACluster})(sendmailMTAHost=$j))(sendmailMTAMapName=uucpdomain)(sendmailMTAKey=%0))', `_ARG_') diff --git a/gnu/usr.sbin/sendmail/cf/feature/virtusertable.m4 b/gnu/usr.sbin/sendmail/cf/feature/virtusertable.m4 index 1210ee98285..608cb33f789 100644 --- a/gnu/usr.sbin/sendmail/cf/feature/virtusertable.m4 +++ b/gnu/usr.sbin/sendmail/cf/feature/virtusertable.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998, 1999, 2001 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -13,13 +13,13 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: virtusertable.m4,v 8.16 1999/07/22 17:55:36 gshapiro Exp $') +VERSIONID(`$Sendmail: virtusertable.m4,v 8.21 2001/03/16 00:51:26 gshapiro Exp $') divert(-1) define(`_VIRTUSER_TABLE_', `') LOCAL_CONFIG # Virtual user table (maps incoming users) -Kvirtuser ifelse(defn(`_ARG_'), `', - DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`virtusertable', +Kvirtuser ifelse(defn(`_ARG_'), `', DATABASE_MAP_TYPE MAIL_SETTINGS_DIR`virtusertable', + defn(`_ARG_'), `LDAP', `ldap -1 -v sendmailMTAMapValue -k (&(objectClass=sendmailMTAMapObject)(|(sendmailMTACluster=${sendmailMTACluster})(sendmailMTAHost=$j))(sendmailMTAMapName=virtuser)(sendmailMTAKey=%0))', `_ARG_') diff --git a/gnu/usr.sbin/sendmail/cf/m4/cfhead.m4 b/gnu/usr.sbin/sendmail/cf/m4/cfhead.m4 index 603b4fafe5e..da0cfbfd72b 100644 --- a/gnu/usr.sbin/sendmail/cf/m4/cfhead.m4 +++ b/gnu/usr.sbin/sendmail/cf/m4/cfhead.m4 @@ -23,6 +23,10 @@ include(TEMPFILE)dnl syscmd(rm -f TEMPFILE)dnl')', `dnl') ##### ###################################################################### +##### +##### DO NOT EDIT THIS FILE! Only edit the source .mc file. +##### +###################################################################### ###################################################################### divert(-1) @@ -46,8 +50,6 @@ define(`OSTYPE', ## helpful functions define(`lower', `translit(`$1', `ABCDEFGHIJKLMNOPQRSTUVWXYZ', `abcdefghijklmnopqrstuvwx')') define(`strcasecmp', `ifelse(lower($1), lower($2), `1', `0')') -## new FEATUREs -define(`_DNSBL_R_',`') ## access to further arguments in FEATURE/HACK define(`_ACC_ARG_1_',`$1') define(`_ACC_ARG_2_',`$2') @@ -101,14 +103,21 @@ dnl in MAILER.m4: _MODMF_(LMF,`LOCAL') dnl ---------------------------------------- define(`MAILER', `define(`_M_N_', `ifelse(`$2', `', `$1', `$2')')dnl -ifdef(_MAILER_`'_M_N_`'_, `dnl`'', +ifdef(`_MAILER_DEFINED_', `', `define(`_MAILER_DEFINED_', `1')')dnl +ifdef(_MAILER_`'_M_N_`'_, +`errprint(`*** ERROR: MAILER('_M_N_`) already included +')', `define(_MAILER_`'_M_N_`'_, `')define(`_ARG_', `$2')define(`_ARGS_', `shift($@)')PUSHDIVERT(7)include(_CF_DIR_`'mailer/$1.m4)POPDIVERT`'')') define(`DOMAIN', `PUSHDIVERT(-1)define(`_ARG_', `$2')include(_CF_DIR_`'domain/$1.m4)POPDIVERT`'') -define(`FEATURE', `PUSHDIVERT(-1)define(`_ARG_', `$2')define(`_ARGS_', `shift($@)')include(_CF_DIR_`'feature/$1.m4)POPDIVERT`'') +define(`FEATURE', `PUSHDIVERT(-1)ifdef(`_MAILER_DEFINED_',`errprint(`*** ERROR: FEATURE() should be before MAILER() +')')define(`_ARG_', `$2')define(`_ARGS_', `shift($@)')include(_CF_DIR_`'feature/$1.m4)POPDIVERT`'') define(`HACK', `PUSHDIVERT(-1)define(`_ARG_', `$2')define(`_ARGS_', `shift($@)')include(_CF_DIR_`'hack/$1.m4)POPDIVERT`'') define(`_DPO_',`') define(`DAEMON_OPTIONS', `define(`_DPO_', defn(`_DPO_') O DaemonPortOptions=`$1')') +define(`_CPO_',`') +define(`CLIENT_OPTIONS', `define(`_CPO_', defn(`_CPO_') +O ClientPortOptions=`$1')') define(`_MAIL_FILTERS_', `') define(`MAIL_FILTER', `define(`_MAIL_FILTERS_', defn(`_MAIL_FILTERS_') X`'$1`, '`$2')') @@ -116,7 +125,10 @@ define(`INPUT_MAIL_FILTER', `MAIL_FILTER(`$1', `$2') ifelse(defn(`confINPUT_MAIL_FILTERS')X, `X', `define(`confINPUT_MAIL_FILTERS', $1)', `define(`confINPUT_MAIL_FILTERS', defn(`confINPUT_MAIL_FILTERS')`, '`$1')')') -define(`CF_LEVEL', `9')dnl +define(`_QUEUE_GROUP_', `') +define(`QUEUE_GROUP', `define(`_QUEUE_GROUP_', defn(`_QUEUE_GROUP_') +Q`'$1`, '`$2')') +define(`CF_LEVEL', `10')dnl define(`VERSIONID', ``##### $1 #####'') define(`LOCAL_RULE_0', `divert(3)') define(`LOCAL_RULE_1', @@ -139,6 +151,36 @@ define(`LOCAL_RULESETS', `divert(9) ') +define(`LOCAL_SRV_FEATURES', +`define(`_LOCAL_SRV_FEATURES_') +ifdef(`_MAILER_DEFINED_',,`errprint(`*** WARNING: MAILER() should be before LOCAL_SRV_FEATURES +')') +divert(9) +SLocal_srv_features') +define(`LOCAL_TRY_TLS', +`define(`_LOCAL_TRY_TLS_') +ifdef(`_MAILER_DEFINED_',,`errprint(`*** WARNING: MAILER() should be before LOCAL_TRY_TLS +')') +divert(9) +SLocal_try_tls') +define(`LOCAL_TLS_RCPT', +`define(`_LOCAL_TLS_RCPT_') +ifdef(`_MAILER_DEFINED_',,`errprint(`*** WARNING: MAILER() should be before LOCAL_TLS_RCPT +')') +divert(9) +SLocal_tls_rcpt') +define(`LOCAL_TLS_CLIENT', +`define(`_LOCAL_TLS_CLIENT_') +ifdef(`_MAILER_DEFINED_',,`errprint(`*** WARNING: MAILER() should be before LOCAL_TLS_CLIENT +')') +divert(9) +SLocal_tls_client') +define(`LOCAL_TLS_SERVER', +`define(`_LOCAL_TLS_SERVER_') +ifdef(`_MAILER_DEFINED_',,`errprint(`*** WARNING: MAILER() should be before LOCAL_TLS_SERVER +')') +divert(9) +SLocal_tls_server') define(`LOCAL_RULE_3', `divert(2)') define(`LOCAL_CONFIG', `divert(6)') define(`MAILER_DEFINITIONS', `divert(7)') @@ -149,17 +191,19 @@ define(`DOL', ``$'$1') define(`SITECONFIG', `CONCAT(D, $3, $2) define(`_CLASS_$3_', `')dnl -ifelse($3, U, Cw$2 $2.UUCP, `dnl') +ifelse($3, U, C{w}$2 $2.UUCP, `dnl') define(`SITE', `ifelse(CONCAT($'2`, $3), SU, CONCAT(CY, $'1`), CONCAT(C, $3, $'1`))') sinclude(_CF_DIR_`'siteconfig/$1.m4)') define(`EXPOSED_USER', `PUSHDIVERT(5)C{E}$1 POPDIVERT`'dnl`'') -ifdef(`_FFR_EXPOSED_USER_FILE', `define(`EXPOSED_USER_FILE', `PUSHDIVERT(5)F{E}$1 -POPDIVERT`'dnl`'')', `dnl') +define(`EXPOSED_USER_FILE', `PUSHDIVERT(5)F{E}$1 +POPDIVERT`'dnl`'') define(`LOCAL_USER', `PUSHDIVERT(5)C{L}$1 POPDIVERT`'dnl`'') +define(`LOCAL_USER_FILE', `PUSHDIVERT(5)F{L}$1 +POPDIVERT`'dnl`'') define(`MASQUERADE_AS', `define(`MASQUERADE_NAME', $1)') define(`MASQUERADE_DOMAIN', `PUSHDIVERT(5)C{M}$1 POPDIVERT`'dnl`'') @@ -167,6 +211,8 @@ define(`MASQUERADE_EXCEPTION', `PUSHDIVERT(5)C{N}$1 POPDIVERT`'dnl`'') define(`MASQUERADE_DOMAIN_FILE', `PUSHDIVERT(5)F{M}$1 POPDIVERT`'dnl`'') +define(`MASQUERADE_EXCEPTION_FILE', `PUSHDIVERT(5)F{N}$1 +POPDIVERT`'dnl`'') define(`LOCAL_DOMAIN', `PUSHDIVERT(5)C{w}$1 POPDIVERT`'dnl`'') define(`CANONIFY_DOMAIN', `PUSHDIVERT(5)C{Canonify}$1 @@ -181,6 +227,10 @@ define(`LDAPROUTE_DOMAIN', `PUSHDIVERT(5)C{LDAPRoute}$1 POPDIVERT`'dnl`'') define(`LDAPROUTE_DOMAIN_FILE', `PUSHDIVERT(5)F{LDAPRoute}$1 POPDIVERT`'dnl`'') +define(`LDAPROUTE_EQUIVALENT', `PUSHDIVERT(5)C{LDAPRouteEquiv}$1 +POPDIVERT`'dnl`'') +define(`LDAPROUTE_EQUIVALENT_FILE', `PUSHDIVERT(5)F{LDAPRouteEquiv}$1 +POPDIVERT`'dnl`'') define(`VIRTUSER_DOMAIN', `PUSHDIVERT(5)C{VirtHost}$1 define(`_VIRTHOSTS_') POPDIVERT`'dnl`'') @@ -191,7 +241,7 @@ define(`RELAY_DOMAIN', `PUSHDIVERT(5)C{R}$1 POPDIVERT`'dnl`'') define(`RELAY_DOMAIN_FILE', `PUSHDIVERT(5)F{R}$1 POPDIVERT`'dnl`'') -define(`TRUST_AUTH_MECH', `PUSHDIVERT(5)C{TrustAuthMech}$1 +define(`TRUST_AUTH_MECH', `_DEFIFNOT(`_USE_AUTH_',`1')PUSHDIVERT(5)C{TrustAuthMech}$1 POPDIVERT`'dnl`'') define(`_OPTINS', `ifdef(`$1', `$2$1$3')') @@ -211,14 +261,14 @@ define(`confFROM_LINE', `From $g $d') define(`confOPERATORS', `.:%@!^/[]+') define(`confSMTP_LOGIN_MSG', `$j Sendmail $v/$Z; $b') define(`_REC_AUTH_', `$.$?{auth_type}(authenticated') -define(`_REC_FULL_AUTH_', `$.$?{auth_type}(authenticated as ${auth_authen} $?{auth_author}for ${auth_author} $.with ${auth_type}') +define(`_REC_FULL_AUTH_', `$.$?{auth_type}(user=${auth_authen} $?{auth_author}author=${auth_author} $.mech=${auth_type}') define(`_REC_HDR_', `$?sfrom $s $.$?_($?s$|from $.$_)') define(`_REC_END_', `for $u; $|; $.$b') -define(`_REC_TLS_', `(using ${tls_version} with cipher ${cipher} (${cipher_bits} bits) verified ${verify})$.$?u') +define(`_REC_TLS_', `(version=${tls_version} cipher=${cipher} bits=${cipher_bits} verify=${verify})$.$?u') define(`_REC_BY_', `$.by $j ($v/$Z)$?r with $r$. id $i$?{tls_version}') define(`confRECEIVED_HEADER', `_REC_HDR_ - _REC_AUTH_$?{auth_ssf} (${auth_ssf} bits)$.) + _REC_AUTH_$?{auth_ssf} bits=${auth_ssf}$.) _REC_BY_ _REC_TLS_ _REC_END_') @@ -251,4 +301,4 @@ define(`confMILTER_MACROS_ENVRCPT', ``{rcpt_mailer}, {rcpt_host}, {rcpt_addr}'') divert(0)dnl -VERSIONID(`$Sendmail: cfhead.m4,v 8.76.4.16 2001/03/06 22:56:36 ca Exp $') +VERSIONID(`$Sendmail: cfhead.m4,v 8.107 2001/07/22 03:25:37 ca Exp $') diff --git a/gnu/usr.sbin/sendmail/cf/m4/proto.m4 b/gnu/usr.sbin/sendmail/cf/m4/proto.m4 index cf721de8d37..dc2ae85c02c 100644 --- a/gnu/usr.sbin/sendmail/cf/m4/proto.m4 +++ b/gnu/usr.sbin/sendmail/cf/m4/proto.m4 @@ -1,6 +1,6 @@ divert(-1) # -# Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983, 1995 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -13,14 +13,16 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: proto.m4,v 8.446.2.5.2.44 2001/07/31 22:25:49 gshapiro Exp $') - -MAILER(local)dnl +VERSIONID(`$Sendmail: proto.m4,v 8.620 2001/08/20 00:55:08 ca Exp $') # level CF_LEVEL config file format V`'CF_LEVEL/ifdef(`VENDOR_NAME', `VENDOR_NAME', `Berkeley') divert(-1) +dnl if MAILER(`local') not defined: do it ourself; be nice +dnl maybe we should issue a warning? +ifdef(`_MAILER_local_',`', `MAILER(local)') + # do some sanity checking ifdef(`__OSTYPE__',, `errprint(`*** ERROR: No system type defined (use OSTYPE macro) @@ -76,8 +78,10 @@ define(`_OPTION', `ifdef(`$2', `O $1`'ifelse(defn(`$2'), `',, `=$2')', `#O $1`'i dnl required to "rename" the check_* rulesets... define(`_U_',ifdef(`_DELAY_CHECKS_',`',`_')) dnl default relaying denied message -ifdef(`confRELAY_MSG', `', `define(`confRELAY_MSG', `"550 Relaying denied"')') -define(`CODE553', `553') +ifdef(`confRELAY_MSG', `', `define(`confRELAY_MSG', +ifdef(`_USE_AUTH_', `"550 Relaying denied. Proper authentication required."', `"550 Relaying denied"'))') +ifdef(`confRCPTREJ_MSG', `', `define(`confRCPTREJ_MSG', `"550 Mailbox disabled for this recipient"')') +define(`_CODE553', `553') divert(0)dnl # override file safeties - setting this option compromises system security, @@ -93,6 +97,10 @@ _OPTION(LDAPDefaultSpec, `confLDAP_DEFAULT_SPEC', `-h localhost') # local info # ################## +# my LDAP cluster +# need to set this before any LDAP lookups are done (including classes) +ifdef(`confLDAP_CLUSTER', `D{sendmailMTACluster}`'confLDAP_CLUSTER', `#D{sendmailMTACluster}$m') + Cwlocalhost ifdef(`USE_CW_FILE', `# file containing names of hosts for which we receive email @@ -131,7 +139,7 @@ CPFAX ')dnl # "Smart" relay host (may be null) -DS`'ifdef(`SMART_HOST', SMART_HOST) +DS`'ifdef(`SMART_HOST', `SMART_HOST') ifdef(`LUSER_RELAY', `dnl # place to which unknown users should be forwarded @@ -151,15 +159,18 @@ C[[ ifdef(`_ACCESS_TABLE_', `dnl # access_db acceptance class C{Accept}OK RELAY -ifdef(`_DELAY_CHECKS_',`dnl +ifdef(`_DELAY_COMPAT_8_10_',`dnl ifdef(`_BLACKLIST_RCPT_',`dnl # possible access_db RHS for spam friends/haters C{SpamTag}SPAMFRIEND SPAMHATER')')', `dnl') +dnl mark for "domain is ok" (resolved or accepted anyway) +define(`_RES_OK_', `OKR')dnl ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',`dnl',`dnl # Resolve map (to check if a host exists in check_mail) -Kresolve host -a<OK> -T<TEMP>') +Kresolve host -a<_RES_OK_> -T<TEMP>') +C{ResOk}_RES_OK_ ifdef(`_NEED_MACRO_MAP_', `dnl ifdef(`_MACRO_MAP_', `', `# macro storage map @@ -171,16 +182,20 @@ ifdef(`confCR_FILE', `dnl FR`'confCR_FILE', `dnl') -define(`TLS_SRV_TAG', `TLS_Srv')dnl -define(`TLS_CLT_TAG', `TLS_Clt')dnl -define(`TLS_TRY_TAG', `Try_TLS')dnl -define(`TLS_OFF_TAG', `Offer_TLS')dnl +define(`TLS_SRV_TAG', `"TLS_Srv"')dnl +define(`TLS_CLT_TAG', `"TLS_Clt"')dnl +define(`TLS_RCPT_TAG', `"TLS_Rcpt"')dnl +define(`TLS_TRY_TAG', `"Try_TLS"')dnl +define(`SRV_FEAT_TAG', `"Srv_Features"')dnl dnl this may be useful in other contexts too ifdef(`_ARITH_MAP_', `', `# arithmetic map define(`_ARITH_MAP_', `1')dnl Karith arith') ifdef(`_ACCESS_TABLE_', `dnl -# possible values for tls_connect in access map +ifdef(`_MACRO_MAP_', `', `# macro storage map +define(`_MACRO_MAP_', `1')dnl +Kmacro macro') +# possible values for TLS_connection in access map C{tls}VERIFY ENCR', `dnl') ifdef(`_CERT_REGEX_ISSUER_', `dnl # extract relevant part from cert issuer @@ -189,14 +204,16 @@ ifdef(`_CERT_REGEX_SUBJECT_', `dnl # extract relevant part from cert subject KCERTSubject regex _CERT_REGEX_SUBJECT_', `dnl') +ifdef(`LOCAL_RELAY', `dnl # who I send unqualified names to (null means deliver locally) -DR`'ifdef(`LOCAL_RELAY', LOCAL_RELAY) +DR`'LOCAL_RELAY') +ifdef(`MAIL_HUB', `dnl # who gets all local email traffic ($R has precedence for unqualified names) -DH`'ifdef(`MAIL_HUB', MAIL_HUB) +DH`'MAIL_HUB') # dequoting map -Kdequote dequote +Kdequote dequote`'ifdef(`confDEQUOTE_OPTS', ` confDEQUOTE_OPTS', `') divert(0)dnl # end of nullclient diversion # class E: names that should be exposed as from this host, even if we masquerade @@ -207,8 +224,9 @@ divert(0)dnl # end of nullclient diversion undivert(5)dnl ifdef(`_VIRTHOSTS_', `CR$={VirtHost}', `dnl') +ifdef(`MASQUERADE_NAME', `dnl # who I masquerade as (null for no masquerading) (see also $=M) -DM`'ifdef(`MASQUERADE_NAME', MASQUERADE_NAME) +DM`'MASQUERADE_NAME') # my name for error messages ifdef(`confMAILER_NAME', `Dn`'confMAILER_NAME', `#DnMAILER-DAEMON') @@ -219,6 +237,10 @@ include(_CF_DIR_`m4/version.m4') ############### # Options # ############### +ifdef(`confAUTO_REBUILD', +`errprint(WARNING: `confAUTO_REBUILD' is no longer valid. + There was a potential for a denial of service attack if this is set. +)')dnl # strip message body to 7 bits on input? _OPTION(SevenBitInput, `confSEVEN_BIT_INPUT', `False') @@ -250,11 +272,6 @@ _OPTION(CheckpointInterval, `confCHECKPOINT_INTERVAL', `10') # default delivery mode _OPTION(DeliveryMode, `confDELIVERY_MODE', `background') -# automatically rebuild the alias database? -# NOTE: There is a potential for a denial of service attack if this is set. -# This option is deprecated and will be removed from a future version. -_OPTION(AutoRebuildAliases, `confAUTO_REBUILD', `False') - # error message header/file _OPTION(ErrorHeader, `confERROR_MESSAGE', `MAIL_SETTINGS_DIR`'error-header') @@ -264,6 +281,9 @@ _OPTION(ErrorMode, `confERROR_MODE', `print') # save Unix-style "From_" lines at top of header? _OPTION(SaveFromLine, `confSAVE_FROM_LINES', `False') +# queue file mode (qf files) +_OPTION(QueueFileMode, `confQUEUE_FILE_MODE', `0600') + # temporary file mode _OPTION(TempFileMode, `confTEMP_FILE_MODE', `0600') @@ -321,12 +341,23 @@ ifelse(defn(`confDAEMON_OPTIONS'), `', `dnl', )'dnl `DAEMON_OPTIONS(`confDAEMON_OPTIONS')') ifelse(defn(`_DPO_'), `', -`ifdef(`_NETINET6_', `O DaemonPortOptions=Name=MTA-IPv4, Family=inet -O DaemonPortOptions=Name=MTA-IPv6, Family=inet6',`O DaemonPortOptions=Name=MTA')', `_DPO_') +`ifdef(`_NETINET6_', `O DaemonPortOptions=Name=MTA-v4, Family=inet +O DaemonPortOptions=Name=MTA-v6, Family=inet6',`O DaemonPortOptions=Name=MTA')', `_DPO_') ifdef(`_NO_MSA_', `dnl', `O DaemonPortOptions=Port=587, Name=MSA, M=E') # SMTP client options -_OPTION(ClientPortOptions, `confCLIENT_OPTIONS', `Address=0.0.0.0') +ifelse(defn(`confCLIENT_OPTIONS'), `', `dnl', +`errprint(WARNING: `confCLIENT_OPTIONS' is no longer valid. See cf/README for more information. +)'dnl +`CLIENT_OPTIONS(`confCLIENT_OPTIONS')') +ifelse(defn(`_CPO_'), `', +`#O ClientPortOptions=Family=inet, Address=0.0.0.0', `_CPO_') + +# Modifiers to `define' {daemon_flags} for direct submissions +_OPTION(DirectSubmissionModifiers, `confDIRECT_SUBMISSION_MODIFIERS', `') + +# Use as mail submission program? See sendmail/SECURITY +_OPTION(UseMSP, `confUSE_MSP', `') # privacy flags _OPTION(PrivacyOptions, `confPRIVACY_FLAGS', `authwarnings') @@ -337,12 +368,37 @@ _OPTION(PostmasterCopy, `confCOPY_ERRORS_TO', `Postmaster') # slope of queue-only function _OPTION(QueueFactor, `confQUEUE_FACTOR', `600000') +# limit on number of concurrent queue runners +_OPTION(MaxQueueChildren, `confMAX_QUEUE_CHILDREN', `') + +# maximum number of queue-runners per queue-grouping with multiple queues +_OPTION(MaxRunnersPerQueue, `confMAX_RUNNERS_PER_QUEUE', `1') + +# priority of queue runners (nice(3)) +_OPTION(NiceQueueRun, `confNICE_QUEUE_RUN', `') + +# shall we sort the queue by hostname first? +_OPTION(QueueSortOrder, `confQUEUE_SORT_ORDER', `priority') + +# minimum time in queue before retry +_OPTION(MinQueueAge, `confMIN_QUEUE_AGE', `30m') + +# how many jobs can you process in the queue? +_OPTION(MaxQueueRunSize, `confMAX_QUEUE_RUN_SIZE', `10000') + +# perform initial split of envelope without checking MX records +_OPTION(FastSplit, `confFAST_SPLIT', `1') + # queue directory O QueueDirectory=ifdef(`QUEUE_DIR', QUEUE_DIR, `/var/spool/mqueue') +# key for shared memory; 0 to turn off +_OPTION(SharedMemoryKey, `confSHARED_MEMORY_KEY', `0') + # timeouts (many of these) _OPTION(Timeout.initial, `confTO_INITIAL', `5m') _OPTION(Timeout.connect, `confTO_CONNECT', `5m') +_OPTION(Timeout.aconnect, `confTO_ACONNECT', `0s') _OPTION(Timeout.iconnect, `confTO_ICONNECT', `5m') _OPTION(Timeout.helo, `confTO_HELO', `5m') _OPTION(Timeout.mail, `confTO_MAIL', `10m') @@ -372,6 +428,12 @@ _OPTION(Timeout.resolver.retrans.normal, `confTO_RESOLVER_RETRANS_NORMAL', `5s') _OPTION(Timeout.resolver.retry, `confTO_RESOLVER_RETRY', `4') _OPTION(Timeout.resolver.retry.first, `confTO_RESOLVER_RETRY_FIRST', `4') _OPTION(Timeout.resolver.retry.normal, `confTO_RESOLVER_RETRY_NORMAL', `4') +_OPTION(Timeout.lhlo, `confTO_LHLO', `2m') +_OPTION(Timeout.auth, `confTO_AUTH', `10m') +_OPTION(Timeout.starttls, `confTO_STARTTLS', `1h') + +# time for DeliverBy; extension disabled if less than 0 +_OPTION(DeliverByMin, `confDELIVER_BY_MIN', `0') # should we not prune routes in route-addr syntax addresses? _OPTION(DontPruneRoutes, `confDONT_PRUNE_ROUTES', `False') @@ -408,6 +470,9 @@ _OPTION(QueueLA, `confQUEUE_LA', `8') # load average at which we refuse connections _OPTION(RefuseLA, `confREFUSE_LA', `12') +# load average at which we delay connections; 0 means no limit +_OPTION(DelayLA, `confDELAY_LA', `0') + # maximum number of children we allow at one time _OPTION(MaxDaemonChildren, `confMAX_DAEMON_CHILDREN', `12') @@ -426,16 +491,10 @@ _OPTION(ClassFactor, `confWORK_CLASS_FACTOR', `1800') # work time factor _OPTION(RetryFactor, `confWORK_TIME_FACTOR', `90000') -# shall we sort the queue by hostname first? -_OPTION(QueueSortOrder, `confQUEUE_SORT_ORDER', `priority') - -# minimum time in queue before retry -_OPTION(MinQueueAge, `confMIN_QUEUE_AGE', `30m') - # default character set _OPTION(DefaultCharSet, `confDEF_CHAR_SET', `iso-8859-1') -# service switch file (ignored on Solaris, Ultrix, OSF/1, others) +# service switch file (name hardwired on Solaris, Ultrix, OSF/1, others) _OPTION(ServiceSwitchFile, `confSERVICE_SWITCH_FILE', `MAIL_SETTINGS_DIR`'service.switch') # hosts file (normally /etc/hosts) @@ -453,9 +512,6 @@ _OPTION(SafeFileEnvironment, `confSAFE_FILE_ENV', `/arch') # are colons OK in addresses? _OPTION(ColonOkInAddr, `confCOLON_OK_IN_ADDR', `True') -# how many jobs can you process in the queue? -_OPTION(MaxQueueRunSize, `confMAX_QUEUE_RUN_SIZE', `10000') - # shall I avoid expanding CNAMEs (violates protocols)? _OPTION(DontExpandCnames, `confDONT_EXPAND_CNAMES', `False') @@ -481,7 +537,11 @@ _OPTION(OperatorChars, `confOPERATORS', `.:@[]') _OPTION(DontInitGroups, `confDONT_INIT_GROUPS', `False') # are group-writable `:include:' and .forward files (un)trustworthy? +# True (the default) means they are not trustworthy. _OPTION(UnsafeGroupWrites, `confUNSAFE_GROUP_WRITES', `True') +ifdef(`confUNSAFE_GROUP_WRITES', +`errprint(`WARNING: confUNSAFE_GROUP_WRITES is deprecated; use confDONT_BLAME_SENDMAIL. +')') # where do errors that occur when sending errors get sent? _OPTION(DoubleBounceAddress, `confDOUBLE_BOUNCE_ADDRESS', `postmaster') @@ -495,6 +555,10 @@ _OPTION(RunAsUser, `confRUN_AS_USER', `sendmail') # maximum number of recipients per SMTP envelope _OPTION(MaxRecipientsPerMessage, `confMAX_RCPTS_PER_MESSAGE', `100') +# limit the rate recipients per SMTP envelope are accepted +# once the threshold number of recipients have been rejected +_OPTION(BadRcptThrottle, `confBAD_RCPT_THROTTLE', `20') + # shall we get local names from our installed interfaces? _OPTION(DontProbeInterfaces, `confDONT_PROBE_INTERFACES', `False') @@ -531,8 +595,11 @@ _OPTION(DataFileBufferSize, `confDF_BUFFER_SIZE', `4096') # Transcript file (xf) memory-buffer file maximum size _OPTION(XscriptFileBufferSize, `confXF_BUFFER_SIZE', `4096') +# lookup type to find information about local mailboxes +_OPTION(MailboxDatabase, `confMAILBOX_DATABASE', `pw') + # list of authentication mechanisms -_OPTION(AuthMechanisms, `confAUTH_MECHANISMS', `GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5') +_OPTION(AuthMechanisms, `confAUTH_MECHANISMS', `EXTERNAL GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5') # default authentication information for outgoing connections _OPTION(DefaultAuthInfo, `confDEF_AUTH_INFO', `MAIL_SETTINGS_DIR`'default-auth-info') @@ -540,11 +607,18 @@ _OPTION(DefaultAuthInfo, `confDEF_AUTH_INFO', `MAIL_SETTINGS_DIR`'default-auth-i # SMTP AUTH flags _OPTION(AuthOptions, `confAUTH_OPTIONS', `') -ifdef(`_FFR_MILTER', ` +# SMTP AUTH maximum encryption strength +_OPTION(AuthMaxBits, `confAUTH_MAX_BITS', `') + +# SMTP STARTTLS server options +_OPTION(TLSSrvOptions, `confTLS_SRV_OPTIONS', `') + # Input mail filters _OPTION(InputMailFilters, `confINPUT_MAIL_FILTERS', `') +ifdef(`confINPUT_MAIL_FILTERS', `dnl # Milter options +_OPTION(Milter.LogLevel, `confMILTER_LOG_LEVEL', `') _OPTION(Milter.macros.connect, `confMILTER_MACROS_CONNECT', `') _OPTION(Milter.macros.helo, `confMILTER_MACROS_HELO', `') _OPTION(Milter.macros.envfrom, `confMILTER_MACROS_ENVFROM', `') @@ -567,10 +641,10 @@ _OPTION(DHParameters, `confDH_PARAMETERS', `') # Random data source (required for systems without /dev/urandom under OpenSSL) _OPTION(RandFile, `confRAND_FILE', `') -ifdef(`confQUEUE_FILE_MODE', -`# queue file mode (qf files) -O QueueFileMode=confQUEUE_FILE_MODE -') +############################ +`# QUEUE GROUP DEFINITIONS #' +############################ +_QUEUE_GROUP_ ########################### # Message precedences # @@ -631,9 +705,9 @@ R$@ $@ <@> R$* $: $1 <@> mark addresses R$* < $* > $* <@> $: $1 < $2 > $3 unmark <addr> R@ $* <@> $: @ $1 unmark @host:... +R$* [ IPv6 : $+ ] <@> $: $1 [ IPv6 : $2 ] unmark IPv6 addr R$* :: $* <@> $: $1 :: $2 unmark node::addr R:`include': $* <@> $: :`include': $1 unmark :`include':... -R$* [ IPv6 : $+ ] <@> $: $1 [ IPv6 : $2 ] unmark IPv6 addr R$* : $* [ $* ] $: $1 : $2 [ $3 ] <@> remark if leading colon R$* : $* <@> $: $2 strip colon if marked R$* <@> $: $1 unmark @@ -656,10 +730,15 @@ ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl R@ $+ , $+ @ $1 : $2 change all "," to ":" # localize and dispose of route-based addresses +dnl XXX: IPv6 colon conflict +ifdef(`NO_NETINET6', `dnl', +`R@ [$+] : $+ $@ $>Canonify2 < @ [$1] > : $2 handle <route-addr>') R@ $+ : $+ $@ $>Canonify2 < @$1 > : $2 handle <route-addr> dnl',`dnl # strip route address <@a,@b,@c:user@d> -> <user@d> R@ $+ , $+ $2 +ifdef(`NO_NETINET6', `dnl', +`R@ [ $* ] : $+ $2') R@ $+ : $+ $2 dnl') @@ -672,8 +751,9 @@ R$+ @ $+ $: $1 < @ $2 > focus on domain R$+ < $+ @ $+ > $1 $2 < @ $3 > move gaze right R$+ < @ $+ > $@ $>Canonify2 $1 < @ $2 > already canonical -# do some sanity checking -R$* < @ $* : $* > $* $1 < @ $2 $3 > $4 nix colons in addrs +dnl This is flagged as an error in S0; no need to silently fix it here. +dnl # do some sanity checking +dnl R$* < @ $~[ $* : $* > $* $1 < @ $2 $3 > $4 nix colons in addrs ifdef(`_NO_UUCP_', `dnl', `# convert old-style addresses to a domain-based address @@ -708,13 +788,8 @@ R$* < @ localhost . $m > $* $: $1 < @ $j . > $2 local domain ifdef(`_NO_UUCP_', `dnl', `R$* < @ localhost . UUCP > $* $: $1 < @ $j . > $2 .UUCP domain') -# check for IPv6 domain literal (save quoted form) -R$* < @ [ IPv6 : $+ ] > $* $: $2 $| $1 < @@ [ $(dequote $2 $) ] > $3 mark IPv6 addr -R$+ $| $* < @@ $=w > $* $: $2 < @ $j . > $4 self-literal -R$+ $| $* < @@ [ $+ ] > $* $@ $2 < @ [ IPv6 : $1 ] > $4 canon IP addr - -# check for IPv4 domain literal -R$* < @ [ $+ ] > $* $: $1 < @@ [ $2 ] > $3 mark [a.b.c.d] +# check for IPv4/IPv6 domain literal +R$* < @ [ $+ ] > $* $: $1 < @@ [ $2 ] > $3 mark [addr] R$* < @@ $=w > $* $: $1 < @ $j . > $3 self-literal R$* < @@ $+ > $* $@ $1 < @ $2 > $3 canon IP addr @@ -780,7 +855,7 @@ dnl this should only apply to unqualified hostnames dnl but if a valid character inside an unqualified hostname is an OperatorChar dnl then $- does not work. # lookup unqualified hostnames -R$* $| $* < @ $* > $* $: $2 < @ $[ $3 $] > $4', `dnl')', `dnl +R$* $| $* < @ $* > $* $: $2 < @ $[ $3 $] > $4', `dnl')', `dnl dnl _NO_CANONIFY_ is not set: canonify unless: dnl {daemon_flags} contains CC (do not canonify) dnl but add a trailing dot to qualified hostnames so other rules will work @@ -803,6 +878,12 @@ ifdef(`_VIRTUSER_ENTIRE_DOMAIN_', `R$* < @ $* $={VirtHost} > $* $: $1 < @ $2 $3 . > $4', `R$* < @ $={VirtHost} > $* $: $1 < @ $2 . > $3')', `dnl') +ifdef(`_GENERICS_TABLE_', `dnl +dnl hosts for genericstable are also canonical +ifdef(`_GENERICS_ENTIRE_DOMAIN_', +`R$* < @ $* $=G > $* $: $1 < @ $2 $3 . > $4', +`R$* < @ $=G > $* $: $1 < @ $2 . > $3')', +`dnl') dnl remove superfluous dots (maybe repeatedly) which may have been added dnl by one of the rules before R$* < @ $* . . > $* $1 < @ $2 . > $3 @@ -870,26 +951,41 @@ R$* $: $>Parse1 $1 final parsing SParse0 R<@> $@ <@> special case error msgs -R$* : $* ; <@> $#error $@ 5.1.3 $: "CODE553 List:; syntax illegal for recipient addresses" +R$* : $* ; <@> $#error $@ 5.1.3 $: "_CODE553 List:; syntax illegal for recipient addresses" R@ <@ $* > < @ $1 > catch "@@host" bogosity -R<@ $+> $#error $@ 5.1.3 $: "CODE553 User address required" +R<@ $+> $#error $@ 5.1.3 $: "_CODE553 User address required" +R$+ <@> $#error $@ 5.1.3 $: "_CODE553 Hostname required" R$* $: <> $1 -R<> $* < @ [ $+ ] > $* $1 < @ [ $2 ] > $3 -R<> $* <$* : $* > $* $#error $@ 5.1.3 $: "CODE553 Colon illegal in host name part" +dnl allow tricks like [host1]:[host2] +R<> $* < @ [ $* ] : $+ > $* $1 < @ [ $2 ] : $3 > $4 +R<> $* < @ [ $* ] , $+ > $* $1 < @ [ $2 ] , $3 > $4 +dnl but no a@[b]c +R<> $* < @ [ $* ] $+ > $* $#error $@ 5.1.2 $: "_CODE553 Invalid address" +R<> $* < @ [ $+ ] > $* $1 < @ [ $2 ] > $3 +R<> $* <$* : $* > $* $#error $@ 5.1.3 $: "_CODE553 Colon illegal in host name part" R<> $* $1 -R$* < @ . $* > $* $#error $@ 5.1.2 $: "CODE553 Invalid host name" -R$* < @ $* .. $* > $* $#error $@ 5.1.2 $: "CODE553 Invalid host name" +R$* < @ . $* > $* $#error $@ 5.1.2 $: "_CODE553 Invalid host name" +R$* < @ $* .. $* > $* $#error $@ 5.1.2 $: "_CODE553 Invalid host name" +dnl no a@b@ +R$* < @ $* @ > $* $#error $@ 5.1.2 $: "_CODE553 Invalid route address" +dnl no a@b@c +R$* @ $* < @ $* > $* $#error $@ 5.1.3 $: "_CODE553 Invalid route address" dnl comma only allowed before @; this check is not complete -R$* , $~O $* $#error $@ 5.1.2 $: "CODE553 Invalid route address" +R$* , $~O $* $#error $@ 5.1.3 $: "_CODE553 Invalid route address" + +ifdef(`_STRICT_RFC821_', `# more RFC 821 checks +R$* . < @ $* > $* $#error $@ 5.1.2 $: "_CODE553 Local part must not end with a dot" +R. $* < @ $* > $* $#error $@ 5.1.2 $: "_CODE553 Local part must not begin with a dot" +dnl', `dnl') # now delete the local info -- note $=O to find characters that cause forwarding R$* < @ > $* $@ $>Parse0 $>canonify $1 user@ => user R< @ $=w . > : $* $@ $>Parse0 $>canonify $2 @here:... -> ... R$- < @ $=w . > $: $(dequote $1 $) < @ $2 . > dequote "foo"@here -R< @ $+ > $#error $@ 5.1.3 $: "CODE553 User address required" +R< @ $+ > $#error $@ 5.1.3 $: "_CODE553 User address required" R$* $=O $* < @ $=w . > $@ $>Parse0 $>canonify $1 $2 $3 ...@here -> ... R$- $: $(dequote $1 $) < @ *LOCAL* > dequote "foo" -R< @ *LOCAL* > $#error $@ 5.1.3 $: "CODE553 User address required" +R< @ *LOCAL* > $#error $@ 5.1.3 $: "_CODE553 User address required" R$* $=O $* < @ *LOCAL* > $@ $>Parse0 $>canonify $1 $2 $3 ...@*LOCAL* -> ... R$* < @ *LOCAL* > $: $1 @@ -901,7 +997,8 @@ R$* < @ *LOCAL* > $: $1 SParse1 ifdef(`_LDAP_ROUTING_', `dnl # handle LDAP routing for hosts in $={LDAPRoute} -R$+ < @ $={LDAPRoute} . > $: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $2>', +R$+ < @ $={LDAPRoute} . > $: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $2> <> +R$+ < @ $={LDAPRouteEquiv} . > $: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $M> <>', `dnl') ifdef(`_MAILER_smtp_', @@ -909,35 +1006,63 @@ ifdef(`_MAILER_smtp_', dnl there is no check whether this is really an IP number R$* < @ [ $+ ] > $* $: $>ParseLocal $1 < @ [ $2 ] > $3 numeric internet spec R$* < @ [ $+ ] > $* $1 < @ [ $2 ] : $S > $3 Add smart host to path -R$* < @ [ IPv6 : $+ ] : > $* - $#_SMTP_ $@ [ $(dequote $2 $) ] $: $1 < @ [IPv6 : $2 ] > $3 no smarthost: send -R$* < @ [ $+ ] : > $* $#_SMTP_ $@ [$2] $: $1 < @ [$2] > $3 no smarthost: send +R$* < @ [ $+ ] : > $* $#_SMTP_ $@ [$2] $: $1 < @ [$2] > $3 no smarthost: send R$* < @ [ $+ ] : $- : $*> $* $#$3 $@ $4 $: $1 < @ [$2] > $5 smarthost with mailer R$* < @ [ $+ ] : $+ > $* $#_SMTP_ $@ $3 $: $1 < @ [$2] > $4 smarthost without mailer', `dnl') ifdef(`_VIRTUSER_TABLE_', `dnl # handle virtual users +ifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl +dnl this is not a documented option +dnl it stops looping in virtusertable mapping if input and output +dnl are identical, i.e., if address A is mapped to A. +dnl it does not deal with multi-level recursion +# handle full domains in RHS of virtusertable +R$+ < @ $+ > $: $(macro {RecipientAddress} $) $1 < @ $2 > +R$+ < @ $+ > $: <?> $1 < @ $2 > $| $>final $1 < @ $2 > +R<?> $+ $| $+ $: $1 $(macro {RecipientAddress} $@ $2 $) +R<?> $+ $| $* $: $1', +`dnl') R$+ $: <!> $1 Mark for lookup +dnl input: <!> local<@domain> ifdef(`_VIRTUSER_ENTIRE_DOMAIN_', `R<!> $+ < @ $* $={VirtHost} . > $: < $(virtuser $1 @ $2 $3 $@ $1 $: @ $) > $1 < @ $2 $3 . >', `R<!> $+ < @ $={VirtHost} . > $: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >') +dnl input: <result-of-lookup | @> local<@domain> | <!> local<@domain> R<!> $+ < @ $=w . > $: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . > +dnl if <@> local<@domain>: no match but try lookup +dnl user+detail: try user++@domain if detail not empty +R<@> $+ + $+ < @ $* . > + $: < $(virtuser $1 + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . > +dnl user+detail: try user+*@domain R<@> $+ + $* < @ $* . > - $: < $(virtuser $1 + * @ $3 $@ $1 $@ $2 $: @ $) > $1 + $2 < @ $3 . > + $: < $(virtuser $1 + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . > +dnl user+detail: try user@domain R<@> $+ + $* < @ $* . > - $: < $(virtuser $1 @ $3 $@ $1 $: @ $) > $1 + $2 < @ $3 . > + $: < $(virtuser $1 @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . > dnl try default entry: @domain +dnl ++@domain +R<@> $+ + $+ < @ $+ . > $: < $(virtuser + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . > dnl +*@domain -R<@> $+ + $+ < @ $+ . > $: < $(virtuser + * @ $3 $@ $1 $@ $2 $: @ $) > $1 + $2 < @ $3 . > +R<@> $+ + $* < @ $+ . > $: < $(virtuser + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . > dnl @domain if +detail exists -R<@> $+ + $* < @ $+ . > $: < $(virtuser @ $3 $@ $1 $@ $2 $: @ $) > $1 + $2 < @ $3 . > +R<@> $+ + $* < @ $+ . > $: < $(virtuser @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . > dnl without +detail (or no match) R<@> $+ < @ $+ . > $: < $(virtuser @ $2 $@ $1 $: @ $) > $1 < @ $2 . > +dnl no match R<@> $+ $: $1 +dnl remove mark R<!> $+ $: $1 R< error : $-.$-.$- : $+ > $* $#error $@ $1.$2.$3 $: $4 R< error : $- $+ > $* $#error $@ $(dequote $1 $) $: $2 +ifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl +# check virtuser input address against output address, if same, skip recursion +R< $+ > $+ < @ $+ > $: < $1 > $2 < @ $3 > $| $1 +# it is the same: stop now +R< $+ > $+ < @ $+ > $| $&{RecipientAddress} $: $>ParseLocal $>Parse0 $>canonify $1 +R< $+ > $+ < @ $+ > $| $* $: < $1 > $2 < @ $3 > +dnl', `dnl') dnl this is not a documented option dnl it performs no looping at all for virtusertable ifdef(`_NO_VIRTUSER_RECURSION_', @@ -1020,7 +1145,7 @@ R$* < @ $* > $* $: $>MailerToTriple < $S > $1 < @ $2 > $3 glue on smarthost nam # deal with other remote names ifdef(`_MAILER_smtp_', `R$* < @$* > $* $#_SMTP_ $@ $2 $: $1 < @ $2 > $3 user@host.domain', -`R$* < @$* > $* $#error $@ 5.1.2 $: "CODE553 Unrecognized host name " $2') +`R$* < @$* > $* $#error $@ 5.1.2 $: "_CODE553 Unrecognized host name " $2') # handle locally delivered names R$=L $#_LOCAL_ $: @ $1 special local names @@ -1033,15 +1158,25 @@ R$+ $#_LOCAL_ $: $1 regular local names SLocal_localaddr Slocaladdr=5 R$+ $: $1 $| $>"Local_localaddr" $1 +R$+ $| $#ok $@ $1 no change R$+ $| $#$* $#$2 R$+ $| $* $: $1 -ifdef(`_FFR_5_', ` +ifdef(`_PRESERVE_LUSER_HOST_', `dnl +# Preserve rcpt_host in {Host} +R$+ $: $1 $| $&h $| $&{Host} check h and {Host} +R$+ $| $| $: $(macro {Host} $@ $) $1 no h or {Host} +R$+ $| $| $+ $: $1 h not set, {Host} set +R$+ $| +$* $| $* $: $1 h is +detail, {Host} set +R$+ $| $+ $| $* $: $(macro {Host} $@ @$2 $) $1 set {Host} to h +')dnl + +ifdef(`_FFR_5_', `dnl # Preserve host in a macro R$+ $: $(macro {LocalAddrHost} $) $1 R$+ @ $+ $: $(macro {LocalAddrHost} $@ @ $2 $) $1') -ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `', ` +ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `', `dnl # deal with plussed users so aliases work nicely R$+ + * $#_LOCAL_ $@ $&h $: $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}') R$+ + $* $#_LOCAL_ $@ + $2 $: $1 + *`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}') @@ -1051,35 +1186,61 @@ R$+ $: <> $1 ifdef(`LUSER_RELAY', `dnl # send unrecognized local users to a relay host -ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', ` +ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl R< > $+ + $* $: < ? $L > <+ $2> $(user $1 $) look up user+ R< > $+ $: < ? $L > < > $(user $1 $) look up user R< ? $* > < $* > $+ <> $: < > $3 $2 found; strip $L R< ? $* > < $* > $+ $: < $1 > $3 $2 not found', ` R< > $+ $: < $L > $(user $1 $) look up user -R< $* > $+ <> $: < > $2 found; strip $L')', -`dnl') +R< $* > $+ <> $: < > $2 found; strip $L') +ifdef(`_PRESERVE_LUSER_HOST_', `dnl +R< $+ > $+ $: < $1 > $2 $&{Host}') +dnl') -# see if we have a relay or a hub -R< > $+ $: < $H > $1 try hub -R< > $+ $: < $R > $1 try relay -ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', ` -R< > $+ $@ $1', ` +ifdef(`MAIL_HUB', `dnl +R< > $+ $: < $H > $1 try hub', `dnl') +ifdef(`LOCAL_RELAY', `dnl +R< > $+ $: < $R > $1 try relay', `dnl') +ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl +R< > $+ $@ $1', `dnl R< > $+ $: < > < $1 <> $&h > nope, restore +detail +ifdef(`_PRESERVE_LUSER_HOST_', `dnl +R< > < $+ @ $+ <> + $* > $: < > < $1 + $3 @ $2 > check whether +detail') R< > < $+ <> + $* > $: < > < $1 + $2 > check whether +detail R< > < $+ <> $* > $: < > < $1 > else discard R< > < $+ + $* > $* < > < $1 > + $2 $3 find the user part R< > < $+ > + $* $#_LOCAL_ $@ $2 $: @ $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}') strip the extra + R< > < $+ > $@ $1 no +detail R$+ $: $1 <> $&h add +detail back in +ifdef(`_PRESERVE_LUSER_HOST_', `dnl +R$+ @ $+ <> + $* $: $1 + $3 @ $2 check whether +detail') R$+ <> + $* $: $1 + $2 check whether +detail R$+ <> $* $: $1 else discard') R< local : $* > $* $: $>MailerToTriple < local : $1 > $2 no host extension R< error : $* > $* $: $>MailerToTriple < error : $1 > $2 no host extension -R< $- : $+ > $+ $: $>MailerToTriple < $1 : $2 > $3 < @ $2 > +ifdef(`_PRESERVE_LUSER_HOST_', `dnl +dnl it is $~[ instead of $- to avoid matches on IPv6 addresses +R< $~[ : $+ > $+ @ $+ $: $>MailerToTriple < $1 : $2 > $3 < @ $4 >') +R< $~[ : $+ > $+ $: $>MailerToTriple < $1 : $2 > $3 < @ $2 > +ifdef(`_PRESERVE_LUSER_HOST_', `dnl +R< $+ > $+ @ $+ $@ $>MailerToTriple < $1 > $2 < @ $3 >') R< $+ > $+ $@ $>MailerToTriple < $1 > $2 < @ $1 > ifdef(`_MAILER_TABLE_', `dnl +ifdef(`_LDAP_ROUTING_', `dnl +################################################################### +### Ruleset LDAPMailertable -- mailertable lookup for LDAP ### +dnl input: <Domain> FullAddress +################################################################### + +SLDAPMailertable +R< $+ > $* $: < $(mailertable $1 $) > $2 lookup +R< $~[ : $* > $* $>MailerToTriple < $1 : $2 > $3 check resolved? +R< $+ > $* $: < $1 > $>Mailertable <$1> $2 try domain +R< $+ > $#$* $#$2 found +R< $+ > $* $#_RELAY_ $@ $1 $: $2 not found, direct relay', +`dnl') + ################################################################### ### Ruleset 90 -- try domain part of mailertable entry ### dnl input: LeftPartOfDomain <RightPartOfDomain> FullAddress @@ -1108,7 +1269,6 @@ dnl <error:text> -> error dnl <mailer:user@host> lp<@domain>rest -> mailer host user dnl <mailer:host> address -> mailer host address dnl <localdomain> address -> address -dnl <[IPv6:number]> address -> relay number address dnl <host> address -> relay host address ################################################################### @@ -1117,10 +1277,10 @@ R< > $* $@ $1 strip off null relay R< error : $-.$-.$- : $+ > $* $#error $@ $1.$2.$3 $: $4 R< error : $- $+ > $* $#error $@ $(dequote $1 $) $: $2 R< local : $* > $* $>CanonLocal < $1 > $2 -R< $- : $+ @ $+ > $*<$*>$* $# $1 $@ $3 $: $2<@$3> use literal user -R< $- : $+ > $* $# $1 $@ $2 $: $3 try qualified mailer +dnl it is $~[ instead of $- to avoid matches on IPv6 addresses +R< $~[ : $+ @ $+ > $*<$*>$* $# $1 $@ $3 $: $2<@$3> use literal user +R< $~[ : $+ > $* $# $1 $@ $2 $: $3 try qualified mailer R< $=w > $* $@ $2 delete local host -R< [ IPv6 : $+ ] > $* $#_RELAY_ $@ $(dequote $1 $) $: $2 use unqualified mailer R< $+ > $* $#_RELAY_ $@ $1 $: $2 use unqualified mailer ################################################################### @@ -1170,6 +1330,7 @@ R$+ < @ *LOCAL* > $: < $1@$j > $1 < @ *LOCAL* > @ mark dnl workspace: either user<@domain> or <user@domain> user <@domain> @ dnl ignore the first case for now dnl if it has the mark lookup full address +dnl broken: %1 is full address not just detail R< $+ > $+ < $* > @ $: < $(generics $1 $: @ $1 $) > $2 < $3 > dnl workspace: ... or <match|@user@domain> user <@domain> dnl no match, try user+detail@domain @@ -1194,6 +1355,7 @@ R< > $* $: $1 not found', # do not masquerade anything in class N R$* < @ $* $=N . > $@ $1 < @ $2 $3 . > +ifdef(`MASQUERADE_NAME', `dnl # special case the users that should be exposed R$=E < @ *LOCAL* > $@ $1 < @ $j . > leave exposed ifdef(`_MASQUERADE_ENTIRE_DOMAIN_', @@ -1211,6 +1373,9 @@ ifdef(`_LIMITED_MASQUERADE_', `dnl', R$* < @ *LOCAL* > $* $: $1 < @ $j . @ $M > $2 R$* < @ $+ @ > $* $: $1 < @ $2 > $3 $M is null R$* < @ $+ @ $+ > $* $: $1 < @ $3 . > $4 $M is not null +dnl', `dnl no masquerading +dnl just fix *LOCAL* leftovers +R$* < @ *LOCAL* > $@ $1 < @ $j . >') ################################################################### ### Ruleset 94 -- convert envelope names to masqueraded form ### @@ -1229,115 +1394,183 @@ SParseLocal=98 undivert(3)dnl LOCAL_RULE_0 ifdef(`_LDAP_ROUTING_', `dnl +###################################################################### +### LDAPExpand: Expand address using LDAP routing +### +### Parameters: +### <$1> -- parsed address (user < @ domain . >) (pass through) +### <$2> -- RFC822 address (user @ domain) (used for lookup) +### <$3> -- +detail information +### +### Returns: +### Mailer triplet ($#mailer $@ host $: address) +### Parsed address (user < @ domain . >) +###################################################################### + SLDAPExpand # do the LDAP lookups -R<$+><$+> $: <$(ldapmra $2 $: $)> <$(ldapmh $2 $: $)> <$1> <$2> +R<$+><$+><$*> $: <$(ldapmra $2 $: $)> <$(ldapmh $2 $: $)> <$1> <$2> <$3> # if mailRoutingAddress and local or non-existant mailHost, # return the new mailRoutingAddress -R< $+ > < $=w > < $+ > < $+ > $@ $>Parse0 $>canonify $1 -R< $+ > < > < $+ > < $+ > $@ $>Parse0 $>canonify $1 +ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl +R<$+@$+> <$=w> <$+> <$+> <$*> $@ $>Parse0 $>canonify $1 $6 @ $2 +R<$+@$+> <> <$+> <$+> <$*> $@ $>Parse0 $>canonify $1 $5 @ $2') +R<$+> <$=w> <$+> <$+> <$*> $@ $>Parse0 $>canonify $1 +R<$+> <> <$+> <$+> <$*> $@ $>Parse0 $>canonify $1 # if mailRoutingAddress and non-local mailHost, # relay to mailHost with new mailRoutingAddress -R< $+ > < $+ > < $+ > < $+ > $#_RELAY_ $@ $2 $: $>canonify $1 +ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl +ifdef(`_MAILER_TABLE_', `dnl +# check mailertable for host, relay from there +R<$+@$+> <$+> <$+> <$+> <$*> $>LDAPMailertable <$3> $>canonify $1 $6 @ $2', +`R<$+@$+> <$+> <$+> <$+> <$*> $#_RELAY_ $@ $3 $: $>canonify $1 $6 @ $2')') +ifdef(`_MAILER_TABLE_', `dnl +# check mailertable for host, relay from there +R<$+> <$+> <$+> <$+> <$*> $>LDAPMailertable <$2> $>canonify $1', +`R<$+> <$+> <$+> <$+> <$*> $#_RELAY_ $@ $2 $: $>canonify $1') # if no mailRoutingAddress and local mailHost, # return original address -R< > < $=w > <$+> <$+> $@ $2 +R<> <$=w> <$+> <$+> <$*> $@ $2 # if no mailRoutingAddress and non-local mailHost, # relay to mailHost with original address -R< > < $+ > <$+> <$+> $#_RELAY_ $@ $1 $: $2 +ifdef(`_MAILER_TABLE_', `dnl +# check mailertable for host, relay from there +R<> <$+> <$+> <$+> <$*> $>LDAPMailertable <$1> $2', +`R<> <$+> <$+> <$+> <$*> $#_RELAY_ $@ $1 $: $2') -# if no mailRoutingAddress and no mailHost, +ifdef(`_LDAP_ROUTE_DETAIL_', +`# if no mailRoutingAddress and no mailHost, +# try without +detail +R<> <> <$+> <$+ + $* @ $+> <> $@ $>LDAPExpand <$1> <$2 @ $4> <+$3>')dnl + +# if still no mailRoutingAddress and no mailHost, # try @domain -R< > < > <$+> <$+ @ $+> $@ $>LDAPExpand <$1> <@ $3> +ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl +R<> <> <$+> <$+ + $* @ $+> <> $@ $>LDAPExpand <$1> <@ $4> <+$3>') +R<> <> <$+> <$+ @ $+> <$*> $@ $>LDAPExpand <$1> <@ $3> <$4> # if no mailRoutingAddress and no mailHost and this was a domain attempt, ifelse(_LDAP_ROUTING_, `_MUST_EXIST_', `dnl # user does not exist -R< > < > <$+> <@ $+> $#error $@ nouser $: "550 User unknown"', +R<> <> <$+> <@ $+> <$*> $: <?> < $&{addr_type} > < $1 > +# only give error for envelope recipient +R<?> <e r> <$+> $#error $@ nouser $: "550 User unknown" +R<?> <$*> <$+> $@ $2', `dnl # return the original address -R< > < > <$+> <@ $+> $@ $1')', +R<> <> <$+> <@ $+> <$*> $@ $1')', `dnl') ifelse(substr(confDELIVERY_MODE,0,1), `d', `errprint(`WARNING: Antispam rules not available in deferred delivery mode. ')') -ifdef(`_ACCESS_TABLE_', `dnl +ifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)') ###################################################################### -### LookUpDomain -- search for domain in access database +### D: LookUpDomain -- search for domain in access database ### ### Parameters: ### <$1> -- key (domain name) ### <$2> -- default (what to return if not found in db) dnl must not be empty -### <$3> -- passthru (additional data passed unchanged through) -### <$4> -- mark (must be <(!|+) single-token>) +### <$3> -- mark (must be <(!|+) single-token>) ### ! does lookup only with tag ### + does lookup with and without tag +### <$4> -- passthru (additional data passed unchanged through) dnl returns: <default> <passthru> dnl <result> <passthru> ###################################################################### -SLookUpDomain -dnl remove IPv6 mark and dequote address -dnl it is a bit ugly because it is checked on each "iteration" -R<[IPv6 : $+]> <$+> <$*> <$*> $: <[$(dequote $1 $)]> <$2> <$3> <$4> +SD dnl workspace <key> <default> <passthru> <mark> dnl lookup with tag (in front, no delimiter here) -R<$*> <$+> <$*> <$- $-> $: < $(access $5`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3> <$4 $5> +dnl 2 3 4 5 +R<$*> <$+> <$- $-> <$*> $: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5> dnl workspace <result-of-lookup|?> <key> <default> <passthru> <mark> -ifdef(`_FFR_LOOKUPDOTDOMAIN', `dnl omit first component: lookup .rest -R<?> <$+.$+> <$+> <$*> <$- $-> $: < $(access $5`'_TAG_DELIM_`'.$2 $: ? $) > <$1.$2> <$3> <$4> <$5 $6>', `dnl') dnl lookup without tag? -R<?> <$+> <$+> <$*> <+ $*> $: < $(access $1 $: ? $) > <$1> <$2> <$3> <+ $4> -ifdef(`_FFR_LOOKUPDOTDOMAIN', `dnl omit first component: lookup .rest -R<?> <$+.$+> <$+> <$*> <+ $*> $: < $(access .$2 $: ? $) > <$1.$2> <$3> <$4> <+ $5>', `dnl') -dnl lookup IP address (no check is done whether it is an IP number!) -R<?> <[$+.$-]> <$+> <$*> <$*> $@ $>LookUpDomain <[$1]> <$3> <$4> <$5> -dnl lookup IPv6 address -R<?> <[$+::$-]> <$+> <$*> <$*> $: $>LookUpDomain <[$1]> <$3> <$4> <$5> -R<?> <[$+:$-]> <$+> <$*> <$*> $: $>LookUpDomain <[$1]> <$3> <$4> <$5> +dnl 1 2 3 4 +R<?> <$+> <$+> <+ $-> <$*> $: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4> +ifdef(`_LOOKUPDOTDOMAIN_', `dnl omit first component: lookup .rest +dnl XXX apply this also to IP addresses? +dnl currently it works the wrong way round for [1.2.3.4] +dnl 1 2 3 4 5 6 +R<?> <$+.$+> <$+> <$- $-> <$*> $: < $(access $5`'_TAG_DELIM_`'.$2 $: ? $) > <$1.$2> <$3> <$4 $5> <$6> +dnl 1 2 3 4 5 +R<?> <$+.$+> <$+> <+ $-> <$*> $: < $(access .$2 $: ? $) > <$1.$2> <$3> <+ $4> <$5>', `dnl') +ifdef(`_ACCESS_SKIP_', `dnl +dnl found SKIP: return <default> and <passthru> +dnl 1 2 3 4 5 +R<SKIP> <$+> <$+> <$- $-> <$*> $@ <$2> <$5>', `dnl') +dnl not found: IPv4 net (no check is done whether it is an IP number!) +dnl 1 2 3 4 5 6 +R<?> <[$+.$-]> <$+> <$- $-> <$*> $@ $>D <[$1]> <$3> <$4 $5> <$6> +ifdef(`NO_NETINET6', `dnl', +`dnl not found: IPv6 net +dnl (could be merged with previous rule if we have a class containing .:) +dnl 1 2 3 4 5 6 +R<?> <[$+::$-]> <$+> <$- $-> <$*> $: $>D <[$1]> <$3> <$4 $5> <$6> +R<?> <[$+:$-]> <$+> <$- $-> <$*> $: $>D <[$1]> <$3> <$4 $5> <$6>') dnl not found, but subdomain: try again -R<?> <$+.$+> <$+> <$*> <$*> $@ $>LookUpDomain <$2> <$3> <$4> <$5> -dnl not found, no subdomain: return default -R<?> <$+> <$+> <$*> <$*> $@ <$2> <$3> -dnl return result of lookup -R<$*> <$+> <$+> <$*> <$*> $@ <$1> <$4> +dnl 1 2 3 4 5 6 +R<?> <$+.$+> <$+> <$- $-> <$*> $@ $>D <$2> <$3> <$4 $5> <$6> +dnl not found, no subdomain: return <default> and <passthru> +dnl 1 2 3 4 5 +R<?> <$+> <$+> <$- $-> <$*> $@ <$2> <$5> +ifdef(`_ATMPF_', `dnl tempfail? +dnl 2 3 4 5 6 +R<$* _ATMPF_> <$+> <$+> <$- $-> <$*> $@ <_ATMPF_> <$6>', `dnl') +dnl return <result of lookup> and <passthru> +dnl 2 3 4 5 6 +R<$*> <$+> <$+> <$- $-> <$*> $@ <$1> <$6> ###################################################################### -### LookUpAddress -- search for host address in access database +### A: LookUpAddress -- search for host address in access database ### ### Parameters: ### <$1> -- key (dot quadded host address) ### <$2> -- default (what to return if not found in db) dnl must not be empty -### <$3> -- passthru (additional data passed through) -### <$4> -- mark (must be <(!|+) single-token>) +### <$3> -- mark (must be <(!|+) single-token>) ### ! does lookup only with tag ### + does lookup with and without tag +### <$4> -- passthru (additional data passed through) dnl returns: <default> <passthru> dnl <result> <passthru> ###################################################################### -SLookUpAddress +SA dnl lookup with tag -R<$+> <$+> <$*> <$- $+> $: < $(access $5`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3> <$4 $5> +dnl 2 3 4 5 +R<$+> <$+> <$- $-> <$*> $: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5> dnl lookup without tag -R<?> <$+> <$+> <$*> <+ $+> $: < $(access $1 $: ? $) > <$1> <$2> <$3> <+ $4> -dnl no match; IPv6: remove last part -R<?> <$+::$-> <$+> <$*> <$*> $@ $>LookUpAddress <$1> <$3> <$4> <$5> -R<?> <$+:$-> <$+> <$*> <$*> $@ $>LookUpAddress <$1> <$3> <$4> <$5> +dnl 1 2 3 4 +R<?> <$+> <$+> <+ $-> <$*> $: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4> +dnl workspace <result-of-lookup|?> <key> <default> <mark> <passthru> +ifdef(`_ACCESS_SKIP_', `dnl +dnl found SKIP: return <default> and <passthru> +dnl 1 2 3 4 5 +R<SKIP> <$+> <$+> <$- $-> <$*> $@ <$2> <$5>', `dnl') +ifdef(`NO_NETINET6', `dnl', +`dnl no match; IPv6: remove last part +dnl 1 2 3 4 5 6 +R<?> <$+::$-> <$+> <$- $-> <$*> $@ $>A <$1> <$3> <$4 $5> <$6> +R<?> <$+:$-> <$+> <$- $-> <$*> $@ $>A <$1> <$3> <$4 $5> <$6>') dnl no match; IPv4: remove last part -R<?> <$+.$-> <$+> <$*> <$*> $@ $>LookUpAddress <$1> <$3> <$4> <$5> +dnl 1 2 3 4 5 6 +R<?> <$+.$-> <$+> <$- $-> <$*> $@ $>A <$1> <$3> <$4 $5> <$6> dnl no match: return default -R<?> <$+> <$+> <$*> <$*> $@ <$2> <$3> +dnl 1 2 3 4 5 +R<?> <$+> <$+> <$- $-> <$*> $@ <$2> <$5> +ifdef(`_ATMPF_', `dnl tempfail? +dnl 2 3 4 5 6 +R<$* _ATMPF_> <$+> <$+> <$- $-> <$*> $@ <_ATMPF_> <$6>', `dnl') dnl match: return result -R<$*> <$+> <$+> <$*> <$*> $@ <$1> <$4>', -`dnl') - +dnl 2 3 4 5 6 +R<$*> <$+> <$+> <$- $-> <$*> $@ <$1> <$6> +dnl endif _ACCESS_TABLE_ +divert(0) ###################################################################### ### CanonAddr -- Convert an address into a standard form for ### relay checking. Route address syntax is @@ -1385,23 +1618,18 @@ R<?> $* $=O $* < @ $* > $: <NO> $1 $2 $3 < @ $4> dnl no $=O in localpart: return R<?> $* $@ $1 -dnl workspace: <?> localpart<@domain>, where localpart contains $=O +dnl workspace: <NO> localpart<@domain>, where localpart contains $=O dnl mark everything which has an "authorized" domain with <RELAY> ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl # if we relay, check username portion for user%host so host can be checked also R<NO> $* < @ $* $=m > $: <RELAY> $1 < @ $2 $3 >', `dnl') - -ifdef(`_RELAY_MX_SERVED_', `dnl -dnl do "we" ($=w) act as backup MX server for the destination domain? -R<NO> $* < @ $+ > $: <MX> < : $(mxserved $2 $) : > < $1 < @$2 > > -R<MX> < : $* <TEMP> : > $* $#error $@ 4.7.1 $: "450 Can not check MX records for recipient host " $1 -dnl yes: mark it as <RELAY> -R<MX> < $* : $=w. : $* > < $+ > $: <RELAY> $4 -dnl no: put old <NO> mark back -R<MX> < : $* : > < $+ > $: <NO> $2', `dnl') - dnl workspace: <(NO|RELAY)> localpart<@domain>, where localpart contains $=O dnl if mark is <NO> then change it to <RELAY> if domain is "authorized" + +dnl what if access map returns something else than RELAY? +dnl we are only interested in RELAY entries... +dnl other To: entries: blacklist recipient; generic entries? +dnl if it is an error we probably do not want to relay anyway ifdef(`_RELAY_HOSTS_ONLY_', `R<NO> $* < @ $=R > $: <RELAY> $1 < @ $2 > ifdef(`_ACCESS_TABLE_', `dnl @@ -1409,12 +1637,23 @@ R<NO> $* < @ $+ > $: <$(access To:$2 $: NO $)> $1 < @ $2 > R<NO> $* < @ $+ > $: <$(access $2 $: NO $)> $1 < @ $2 >',`dnl')', `R<NO> $* < @ $* $=R > $: <RELAY> $1 < @ $2 $3 > ifdef(`_ACCESS_TABLE_', `dnl -R<NO> $* < @ $+ > $: $>LookUpDomain <$2> <NO> <$1 < @ $2 >> <+To> +R<NO> $* < @ $+ > $: $>D <$2> <NO> <+ To> <$1 < @ $2 >> R<$+> <$+> $: <$1> $2',`dnl')') +ifdef(`_RELAY_MX_SERVED_', `dnl +dnl do "we" ($=w) act as backup MX server for the destination domain? +R<NO> $* < @ $+ > $: <MX> < : $(mxserved $2 $) : > < $1 < @$2 > > +R<MX> < : $* <TEMP> : > $* $#TEMP $@ 4.7.1 $: "450 Can not check MX records for recipient host " $1 +dnl yes: mark it as <RELAY> +R<MX> < $* : $=w. : $* > < $+ > $: <RELAY> $4 +dnl no: put old <NO> mark back +R<MX> < : $* : > < $+ > $: <NO> $2', `dnl') + +dnl do we relay to this recipient domain? R<RELAY> $* < @ $* > $@ $>ParseRecipient $1 -R<$-> $* $@ $2 +dnl something else +R<$+> $* $@ $2 ###################################################################### @@ -1435,26 +1674,26 @@ R< $* > $* $: $2 ifdef(`_ACCESS_TABLE_', `dnl dnl workspace: {client_name} $| {client_addr} -R$+ $| $+ $: $>LookUpDomain < $1 > <?> < $2 > <+Connect> -dnl workspace: <result-of-lookup> <{client_addr}> -R<?> <$+> $: $>LookUpAddress < $1 > <?> < $1 > <+Connect> no: another lookup -dnl workspace: <result-of-lookup> <{client_addr}> -R<?> < $+ > $: $1 found nothing +R$+ $| $+ $: $>D < $1 > <?> <+ Connect> < $2 > dnl workspace: <result-of-lookup> <{client_addr}> -dnl or {client_addr} -R<$={Accept}> < $* > $@ $1 return value of lookup -R<REJECT> $* $#error ifdef(`confREJECT_MSG', `$: "confREJECT_MSG"', `$@ 5.7.1 $: "550 Access denied"') -R<DISCARD> $* $#discard $: discard +R<?> <$+> $: $>A < $1 > <?> <+ Connect> <> no: another lookup +dnl workspace: <result-of-lookup> (<>|<{client_addr}>) +R<?> <$*> $: OK found nothing +dnl workspace: <result-of-lookup> (<>|<{client_addr}>) | OK +R<$={Accept}> <$*> $@ $1 return value of lookup +R<REJECT> <$*> $#error ifdef(`confREJECT_MSG', `$: "confREJECT_MSG"', `$@ 5.7.1 $: "550 Access denied"') +R<DISCARD> <$*> $#discard $: discard dnl error tag R<ERROR:$-.$-.$-:$+> <$*> $#error $@ $1.$2.$3 $: $4 R<ERROR:$+> <$*> $#error $: $1 +ifdef(`_ATMPF_', `R<$* _ATMPF_> <$*> $#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl') dnl generic error from access map R<$+> <$*> $#error $: $1', `dnl') ifdef(`_RBL_',`dnl # DNS based IP address spam list +dnl workspace: ignored... R$* $: $&{client_addr} -R::ffff:$-.$-.$-.$- $: <?> $(host $4.$3.$2.$1._RBL_. $: OK $) R$-.$-.$-.$- $: <?> $(host $4.$3.$2.$1._RBL_. $: OK $) R<?>OK $: OKSOFAR R<?>$+ $#error $@ 5.7.1 $: "550 Mail from " $&{client_addr} " refused by blackhole site _RBL_"', @@ -1529,7 +1768,7 @@ dnl workspace: < ? $&{client_name} > <user@localhost|host> dnl or: <address> dnl or: <?> <address> (thanks to u in ${daemon_flags}) R<? $=w> $* $: $2 local client: ok -R<? $+> <$+> $#error $@ 5.5.4 $: "CODE553 Real domain name required for sender address" +R<? $+> <$+> $#error $@ 5.5.4 $: "_CODE553 Real domain name required for sender address" dnl remove <?> (happens only if ${client_name} == "" or u in ${daemon_flags}) R<?> $* $: $1') dnl workspace: address (or <address>) @@ -1541,23 +1780,23 @@ R<?> $* < @ $+ . > <?> $1 < @ $2 > strip trailing dots R<?> $* < @ $* $=P > $: <OK> $1 < @ $2 $3 > dnl workspace <mark> CanonicalAddress where mark is ? or OK ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_', -`R<?> $* < @ $+ > $: <OK> $1 < @ $2 > ... unresolvable OK', +`R<?> $* < @ $+ > $: <_RES_OK_> $1 < @ $2 > ... unresolvable OK', `R<?> $* < @ $+ > $: <? $(resolve $2 $: $2 <PERM> $) > $1 < @ $2 > R<? $* <$->> $* < @ $+ > $: <$2> $3 < @ $4 >') -dnl workspace <mark> CanonicalAddress where mark is ?, OK, PERM, TEMP +dnl workspace <mark> CanonicalAddress where mark is ?, _RES_OK_, PERM, TEMP dnl mark is ? iff the address is user (wo @domain) ifdef(`_ACCESS_TABLE_', `dnl # check sender address: user@address, user@, address dnl should we remove +ext from user? -dnl workspace: <mark> CanonicalAddress where mark is: ?, OK, PERM, TEMP -R<$+> $+ < @ $* > $: @<$1> <$2 < @ $3 >> $| <F:$2@$3> <U:$2@> <H:$3> +dnl workspace: <mark> CanonicalAddress where mark is: ?, _RES_OK_, PERM, TEMP +R<$+> $+ < @ $* > $: @<$1> <$2 < @ $3 >> $| <F:$2@$3> <U:$2@> <D:$3> R<$+> $+ $: @<$1> <$2> $| <U:$2@> dnl workspace: @<mark> <CanonicalAddress> $| <@type:address> .... dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>> dnl will only return user<@domain when "reversing" the args -R@ <$+> <$*> $| <$+> $: <@> <$1> <$2> $| $>SearchList <+From> $| <$3> <> +R@ <$+> <$*> $| <$+> $: <@> <$1> <$2> $| $>SearchList <+ From> $| <$3> <> dnl workspace: <@><mark> <CanonicalAddress> $| <result> R<@> <$+> <$*> $| <$*> $: <$3> <$1> <$2> reverse result dnl workspace: <result> <mark> <CanonicalAddress> @@ -1574,25 +1813,26 @@ ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl dnl prepend daemon_flags R<?> $* $: $&{daemon_flags} $| <?> $1 dnl accept unqualified sender: change mark to avoid test -R$* u $* $| <?> $* $: <OK> $3 +R$* u $* $| <?> $* $: <_RES_OK_> $3 dnl remove daemon_flags R$* $| $* $: $2 R<?> $* $: < ? $&{client_name} > $1 R<?> $* $@ <OK> ...local unqualed ok -R<? $+> $* $#error $@ 5.5.4 $: "CODE553 Domain name required for sender address " $&f +R<? $+> $* $#error $@ 5.5.4 $: "_CODE553 Domain name required for sender address " $&f ...remote is not') # check results R<?> $* $: @ $1 mark address: nothing known about it -R<OK> $* $@ <OK> +R<$={ResOk}> $* $@ <_RES_OK_> domain ok: stop R<TEMP> $* $#error $@ 4.1.8 $: "451 Domain of sender address " $&f " does not resolve" -R<PERM> $* $#error $@ 5.1.8 $: "CODE553 Domain of sender address " $&f " does not exist" +R<PERM> $* $#error $@ 5.1.8 $: "_CODE553 Domain of sender address " $&f " does not exist" ifdef(`_ACCESS_TABLE_', `dnl -R<$={Accept}> $* $# $1 +R<$={Accept}> $* $# $1 accept from access map R<DISCARD> $* $#discard $: discard R<REJECT> $* $#error ifdef(`confREJECT_MSG', `$: "confREJECT_MSG"', `$@ 5.7.1 $: "550 Access denied"') dnl error tag R<ERROR:$-.$-.$-:$+> $* $#error $@ $1.$2.$3 $: $4 R<ERROR:$+> $* $#error $: $1 +ifdef(`_ATMPF_', `R<_ATMPF_> $* $#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl') dnl generic error from access map R<$+> $* $#error $: $1 error from access db', `dnl') @@ -1608,27 +1848,76 @@ R$* $| $#$* $#$2 R$* $| $* $@ $>"Basic_check_rcpt" $1 SBasic_check_rcpt +# empty address? +R<> $#error $@ nouser $: "553 User address required" +R$@ $#error $@ nouser $: "553 User address required" # check for deferred delivery mode R$* $: < ${deliveryMode} > $1 R< d > $* $@ deferred R< $* > $* $: $2 ifdef(`_REQUIRE_QUAL_RCPT_', `dnl -# require qualified recipient? +dnl this code checks for user@host where host is not a FQHN. +dnl it is not activated. +dnl notice: code to check for a recipient without a domain name is +dnl available down below; look for the same macro. +dnl this check is done here because the name might be qualified by the +dnl canonicalization. +# require fully qualified domain part? +dnl very simple canonification: make sure the address is in < > R$+ $: <?> $1 -R<?><$+> $: <@> <$1> -R<?>$+ $: <@> <$1> +R<?> <$+> $: <@> <$1> +R<?> $+ $: <@> <$1> +R<@> < postmaster > $: postmaster +R<@> < $* @ $+ . $+ > $: < $3 @ $4 . $5 > dnl prepend daemon_flags -R$* $: $&{daemon_flags} $| $1 +R<@> $* $: $&{daemon_flags} $| <@> $1 dnl workspace: ${daemon_flags} $| <@> <address> dnl do not allow these at all or only from local systems? -R$* r $* $| <@> < $* @ $- > $: < ? $&{client_name} > < $3 @ $4 > +R$* r $* $| <@> < $* @ $* > $: < ? $&{client_name} > < $3 @ $4 > R<?> < $* > $: <$1> R<? $=w> < $* > $: <$1> -R<? $+> <$+> $#error $@ 5.5.4 $: "553 Domain name required" +R<? $+> <$+> $#error $@ 5.5.4 $: "553 Fully qualified domain name required" dnl remove daemon_flags for other cases R$* $| <@> $* $: $2', `dnl') +dnl ################################################################## +dnl call subroutines for recipient and relay +dnl possible returns from subroutines: +dnl $#TEMP temporary failure +dnl $#error permanent failure (or temporary if from access map) +dnl $#other stop processing +dnl RELAY RELAYing allowed +dnl other otherwise +###################################################################### +R$* $: $1 $| @ $>"Rcpt_ok" $1 +dnl temporary failure? remove mark @ and remember +R$* $| @ $#TEMP $+ $: $1 $| T $2 +dnl error or ok (stop) +R$* $| @ $#$* $#$2 +ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl') +R$* $| @ RELAY $@ RELAY +dnl something else: call check sender (relay) +R$* $| @ $* $: O $| $>"Relay_ok" $1 +dnl temporary failure: call check sender (relay) +R$* $| T $+ $: T $2 $| $>"Relay_ok" $1 +dnl temporary failure? return that +R$* $| $#TEMP $+ $#error $2 +dnl error or ok (stop) +R$* $| $#$* $#$2 +R$* $| RELAY $@ RELAY +dnl something else: return previous temp failure +R T $+ $| $* $#error $1 +# anything else is bogus +R$* $#error $@ 5.7.1 $: confRELAY_MSG +divert(0) + +###################################################################### +### Rcpt_ok: is the recipient ok? +dnl input: recipient address (RCPT TO) +dnl output: see explanation at call +###################################################################### +SRcpt_ok ifdef(`_LOOSE_RELAY_CHECK_',`dnl R$* $: $>CanonAddr $1 R$* < @ $* . > $1 < @ $2 > strip trailing dots', @@ -1641,7 +1930,7 @@ R$* < @ $* > $* $: $1 < @ $2 @@ $(bestmx $2 $) > $3', `dnl # limit bestmx to $=B R$* < @ $* $=B > $* $: $1 < @ $2 $3 @@ $(bestmx $2 $3 $) > $4') -R$* $=O $* < @ $* @@ $=w . > $* $@ $>"Basic_check_rcpt" $1 $2 $3 +R$* $=O $* < @ $* @@ $=w . > $* $@ $>"Rcpt_ok" $1 $2 $3 R$* < @ $* @@ $=w . > $* $: $1 < @ $3 > $4 R$* < @ $* @@ $* > $* $: $1 < @ $2 > $4') @@ -1651,50 +1940,58 @@ ifdef(`_ACCESS_TABLE_', `dnl R$* $: <?> $1 dnl user is now tagged with @ to be consistent with check_mail dnl and to distinguish users from hosts (com would be host, com@ would be user) -R<?> $+ < @ $=w > $: <> <$1 < @ $2 >> $| <F:$1@$2> <U:$1@> <H:$2> -R<?> $+ < @ $* > $: <> <$1 < @ $2 >> $| <F:$1@$2> <H:$2> +R<?> $+ < @ $=w > $: <> <$1 < @ $2 >> $| <F:$1@$2> <U:$1@> <D:$2> +R<?> $+ < @ $* > $: <> <$1 < @ $2 >> $| <F:$1@$2> <D:$2> R<?> $+ $: <> <$1> $| <U:$1@> dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>> dnl will only return user<@domain when "reversing" the args -R<> <$*> $| <$+> $: <@> <$1> $| $>SearchList <+To> $| <$2> <> +R<> <$*> $| <$+> $: <@> <$1> $| $>SearchList <+ To> $| <$2> <> R<@> <$*> $| <$*> $: <$2> <$1> reverse result R<?> <$*> $: @ $1 mark address as no match +dnl we may have to filter here because otherwise some RHSs +dnl would be interpreted as generic error messages... +dnl error messages should be "tagged" by prefixing them with error: ! +dnl that would make a lot of things easier. R<$={Accept}> <$*> $: @ $2 mark address as no match -ifdef(`_DELAY_CHECKS_',`dnl +ifdef(`_ACCESS_SKIP_', `dnl +R<SKIP> <$*> $: @ $1 mark address as no match', `dnl') +ifdef(`_DELAY_COMPAT_8_10_',`dnl +dnl compatility with 8.11/8.10: dnl we have to filter these because otherwise they would be interpreted dnl as generic error message... dnl error messages should be "tagged" by prefixing them with error: ! dnl that would make a lot of things easier. dnl maybe we should stop checks already here (if SPAM_xyx)? R<$={SpamTag}> <$*> $: @ $2 mark address as no match') -R<REJECT> $* $#error $@ 5.2.1 $: "550 Mailbox disabled for this recipient" +R<REJECT> $* $#error $@ 5.2.1 $: confRCPTREJ_MSG R<DISCARD> $* $#discard $: discard dnl error tag R<ERROR:$-.$-.$-:$+> $* $#error $@ $1.$2.$3 $: $4 R<ERROR:$+> $* $#error $: $1 +ifdef(`_ATMPF_', `R<_ATMPF_> $* $#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl') dnl generic error from access map R<$+> $* $#error $: $1 error from access db R@ $* $1 remove mark', `dnl')', `dnl') -ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)') -# authenticated? -dnl do this unconditionally? this requires to manage CAs carefully -dnl just because someone has a CERT signed by a "trusted" CA -dnl does not mean we want to allow relaying for her, -dnl either use a subroutine or provide something more sophisticated -dnl this could for example check the DN (maybe an access map lookup) -R$* $: $1 $| $>RelayAuth $1 $| $&{verify} client authenticated? -R$* $| $# $+ $# $2 error/ok? -R$* $| $* $: $1 no - -# authenticated by a trusted mechanism? -R$* $: $1 $| $&{auth_type} +ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl') +# authenticated via TLS? +R$* $: $1 $| $>RelayTLS client authenticated? +R$* $| $# $+ $# $2 error/ok? +R$* $| $* $: $1 no + +R$* $: $1 $| $>"Local_Relay_Auth" $&{auth_type} +dnl workspace: localpart<@domain> $| result of Local_Relay_Auth +R$* $| $# $* $# $2 +dnl if Local_Relay_Auth returns NO then do not check $={TrustAuthMech} +R$* $| NO $: $1 +R$* $| $* $: $1 $| $&{auth_type} +dnl workspace: localpart<@domain> [ $| ${auth_type} ] dnl empty ${auth_type}? R$* $| $: $1 dnl mechanism ${auth_type} accepted? dnl use $# to override further tests (delay_checks): see check_rcpt below -R$* $| $={TrustAuthMech} $# RELAYAUTH -dnl undo addition of ${auth_type} +R$* $| $={TrustAuthMech} $# RELAY +dnl remove ${auth_type} R$* $| $* $: $1 dnl workspace: localpart<@domain> | localpart ifelse(defn(`_NO_UUCP_'), `r', @@ -1702,20 +1999,21 @@ ifelse(defn(`_NO_UUCP_'), `r', R$* ! $* $: <REMOTE> $2 < @ BANG_PATH >', `dnl') # anything terminating locally is ok ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl -R$+ < @ $* $=m > $@ RELAYTO', `dnl') -R$+ < @ $=w > $@ RELAYTO +R$+ < @ $* $=m > $@ RELAY', `dnl') +R$+ < @ $=w > $@ RELAY ifdef(`_RELAY_HOSTS_ONLY_', -`R$+ < @ $=R > $@ RELAYTO +`R$+ < @ $=R > $@ RELAY ifdef(`_ACCESS_TABLE_', `dnl R$+ < @ $+ > $: <$(access To:$2 $: ? $)> <$1 < @ $2 >> dnl workspace: <Result-of-lookup | ?> <localpart<@domain>> R<?> <$+ < @ $+ >> $: <$(access $2 $: ? $)> <$1 < @ $2 >>',`dnl')', -`R$+ < @ $* $=R > $@ RELAYTO +`R$+ < @ $* $=R > $@ RELAY ifdef(`_ACCESS_TABLE_', `dnl -R$+ < @ $+ > $: $>LookUpDomain <$2> <?> <$1 < @ $2 >> <+To>',`dnl')') +R$+ < @ $+ > $: $>D <$2> <?> <+ To> <$1 < @ $2 >>',`dnl')') ifdef(`_ACCESS_TABLE_', `dnl dnl workspace: <Result-of-lookup | ?> <localpart<@domain>> -R<RELAY> $* $@ RELAYTO +R<RELAY> $* $@ RELAY +ifdef(`_ATMPF_', `R<$* _ATMPF_> $* $#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl') R<$*> <$*> $: $2',`dnl') @@ -1723,8 +2021,8 @@ ifdef(`_RELAY_MX_SERVED_', `dnl # allow relaying for hosts which we MX serve R$+ < @ $+ > $: < : $(mxserved $2 $) : > $1 < @ $2 > dnl this must not necessarily happen if the client is checked first... -R< : $* <TEMP> : > $* $#error $@ 4.7.1 $: "450 Can not check MX records for recipient host " $1 -R<$* : $=w . : $*> $* $@ RELAYTO +R< : $* <TEMP> : > $* $#TEMP $@ 4.7.1 $: "450 Can not check MX records for recipient host " $1 +R<$* : $=w . : $*> $* $@ RELAY R< : $* : > $* $: $2', `dnl') @@ -1737,7 +2035,7 @@ dnl but we should accept it anyway (maybe making it an option: dnl RequireFQDN ?) dnl postmaster must be accepted without domain (DRUMS) ifdef(`_REQUIRE_QUAL_RCPT_', `dnl -R<?> postmaster $@ TOPOSTMASTER +R<?> postmaster $@ OK # require qualified recipient? dnl prepend daemon_flags R<?> $+ $: $&{daemon_flags} $| <?> $1 @@ -1747,31 +2045,38 @@ dnl r flag? add client_name R$* r $* $| <?> $+ $: < ? $&{client_name} > <?> $3 dnl no r flag: relay to local user (only local part) # no qualified recipient required -R$* $| <?> $+ $@ RELAYTOLOCAL +R$* $| <?> $+ $@ RELAY dnl client_name is empty -R<?> <?> $+ $@ RELAYTOLOCAL +R<?> <?> $+ $@ RELAY dnl client_name is local -R<? $=w> <?> $+ $@ RELAYTOLOCAL +R<? $=w> <?> $+ $@ RELAY dnl client_name is not local R<? $+> $+ $#error $@ 5.5.4 $: "553 Domain name required"', `dnl dnl no qualified recipient required -R<?> $+ $@ RELAYTOLOCAL') +R<?> $+ $@ RELAY') dnl it is a remote user: remove mark and then check client R<$+> $* $: $2 dnl currently the recipient address is not used below +###################################################################### +### Relay_ok: is the relay/sender ok? +dnl input: ignored +dnl output: see explanation at call +###################################################################### +SRelay_ok # anything originating locally is ok # check IP address R$* $: $&{client_addr} -R$@ $@ RELAYFROM originated locally -R0 $@ RELAYFROM originated locally -R$=R $* $@ RELAYFROM relayable IP address +R$@ $@ RELAY originated locally +R0 $@ RELAY originated locally +R$=R $* $@ RELAY relayable IP address ifdef(`_ACCESS_TABLE_', `dnl -R$* $: $>LookUpAddress <$1> <?> <$1> <+Connect> -R<RELAY> $* $@ RELAYFROM relayable IP address +R$* $: $>A <$1> <?> <+ Connect> <$1> +R<RELAY> $* $@ RELAY relayable IP address +ifdef(`_ATMPF_', `R<_ATMPF_> $* $#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl') R<$*> <$*> $: $2', `dnl') R$* $: [ $1 ] put brackets around it... -R$=w $@ RELAYFROM ... and see if it is local +R$=w $@ RELAY ... and see if it is local ifdef(`_RELAY_DB_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl ifdef(`_RELAY_LOCAL_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl @@ -1780,48 +2085,56 @@ dnl input: {client_addr} or something "broken" dnl just throw the input away; we do not need it. # check whether FROM is allowed to use system as relay R$* $: <?> $>CanonAddr $&f +R<?> $+ < @ $+ . > <?> $1 < @ $2 > remove trailing dot ifdef(`_RELAY_LOCAL_FROM_', `dnl # check whether local FROM is ok -R<?> $+ < @ $=w . > $@ RELAYFROMMAIL FROM local', `dnl') +R<?> $+ < @ $=w > $@ RELAY FROM local', `dnl') ifdef(`_RELAY_DB_FROM_', `dnl -R<?> $+ < @ $+ . > <?> $1 < @ $2 > remove trailing dot -R<?> $+ < @ $+ > $: $1 < @ $2 > $| $>SearchList <! From> $| <F:$1@$2> ifdef(`_RELAY_DB_FROM_DOMAIN_', `<H:$2>') <> -R$* <RELAY> $@ RELAYFROMMAIL RELAY FROM sender ok', `dnl -ifdef(`_RELAY_DB_FROM_DOMAIN_', `errprint(`*** ERROR: _RELAY_DB_FROM_DOMAIN_ requires _RELAY_DB_FROM_ +R<?> $+ < @ $+ > $: <@> $>SearchList <! From> $| <F:$1@$2> ifdef(`_RELAY_DB_FROM_DOMAIN_', `<D:$2>') <> +R<@> <RELAY> $@ RELAY RELAY FROM sender ok +ifdef(`_ATMPF_', `R<@> <_ATMPF_> $#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl') +', `dnl +ifdef(`_RELAY_DB_FROM_DOMAIN_', +`errprint(`*** ERROR: _RELAY_DB_FROM_DOMAIN_ requires _RELAY_DB_FROM_ ')', `dnl') dnl')', `dnl') +dnl notice: the rulesets above do not leave a unique workspace behind. +dnl it does not matter in this case because the following rule ignores +dnl the input. otherwise these rules must "clean up" the workspace. # check client name: first: did it resolve? dnl input: ignored R$* $: < $&{client_resolve} > -R<TEMP> $#error $@ 4.7.1 $: "450 Relaying temporarily denied. Cannot resolve PTR record for " $&{client_addr} +R<TEMP> $#TEMP $@ 4.7.1 $: "450 Relaying temporarily denied. Cannot resolve PTR record for " $&{client_addr} R<FORGED> $#error $@ 5.7.1 $: "550 Relaying denied. IP name possibly forged " $&{client_name} R<FAIL> $#error $@ 5.7.1 $: "550 Relaying denied. IP name lookup failed " $&{client_name} dnl ${client_resolve} should be OK, so go ahead -R$* $: <?> $&{client_name} +R$* $: <@> $&{client_name} +dnl should not be necessary since it has been done for client_addr already +R<@> $@ RELAY +dnl workspace: <@> ${client_name} (not empty) # pass to name server to make hostname canonical -R<?> $* $~P $:<?> $[ $1 $2 $] +R<@> $* $=P $:<?> $1 $2 +R<@> $+ $:<?> $[ $1 $] +dnl workspace: <?> ${client_name} (canonified) R$* . $1 strip trailing dots -dnl should not be necessary since it has been done for client_addr already -R<?> $@ RELAYFROM ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl -R<?> $* $=m $@ RELAYFROM', `dnl') -R<?> $=w $@ RELAYFROM +R<?> $* $=m $@ RELAY', `dnl') +R<?> $=w $@ RELAY ifdef(`_RELAY_HOSTS_ONLY_', -`R<?> $=R $@ RELAYFROM +`R<?> $=R $@ RELAY ifdef(`_ACCESS_TABLE_', `dnl R<?> $* $: <$(access Connect:$1 $: ? $)> <$1> R<?> <$*> $: <$(access $1 $: ? $)> <$1>',`dnl')', -`R<?> $* $=R $@ RELAYFROM +`R<?> $* $=R $@ RELAY ifdef(`_ACCESS_TABLE_', `dnl -R<?> $* $: $>LookUpDomain <$1> <?> <$1> <+Connect>',`dnl')') +R<?> $* $: $>D <$1> <?> <+ Connect> <$1>',`dnl')') ifdef(`_ACCESS_TABLE_', `dnl -R<RELAY> $* $@ RELAYFROM +R<RELAY> $* $@ RELAY +ifdef(`_ATMPF_', `R<$* _ATMPF_> $* $#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl') R<$*> <$*> $: $2',`dnl') - -# anything else is bogus -R$* $#error $@ 5.7.1 $: confRELAY_MSG +dnl end of _PROMISCUOUS_RELAY_ divert(0) ifdef(`_DELAY_CHECKS_',`dnl # turn a canonical address in the form user<@domain> @@ -1849,11 +2162,11 @@ ifdef(`_ACCESS_TABLE_', `', dnl one of the next two rules is supposed to match dnl this code has been copied from BLACKLIST... etc dnl and simplified by omitting some < >. -R<?> $+ < @ $=w > $: <> $1 < @ $2 > $| <F: $1@$2 > <U: $1@> -R<?> $+ < @ $* > $: <> $1 < @ $2 > $| <F: $1@$2 > +R<?> $+ < @ $=w > $: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 > <U: $1@> +R<?> $+ < @ $* > $: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 > dnl R<?> $@ something_is_very_wrong_here -# lookup the addresses only with To tag -R<> $* $| <$+> $: <@> $1 $| $>SearchList <!To> $| <$2> <> +# lookup the addresses only with Spam tag +R<> $* $| <$+> $: <@> $1 $| $>SearchList <! Spam> $| <$2> <> R<@> $* $| $* $: $2 $1 reverse result dnl', `dnl') ifdef(`_SPAM_FRIEND_', @@ -1861,12 +2174,12 @@ ifdef(`_SPAM_FRIEND_', ifdef(`_SPAM_HATER_', `errprint(`*** ERROR: define either SpamHater or SpamFriend ')', `dnl') -R<SPAMFRIEND> $+ $@ SPAMFRIEND +R<FRIEND> $+ $@ SPAMFRIEND R<$*> $+ $: $2', `dnl') ifdef(`_SPAM_HATER_', `# is the recipient no spam hater? -R<SPAMHATER> $+ $: $1 spam hater: continue checks +R<HATER> $+ $: $1 spam hater: continue checks R<$*> $+ $@ NOSPAMHATER everyone else: stop dnl',`dnl') dnl run further checks: check_mail @@ -1878,7 +2191,144 @@ R$* $: $1 $| $>checkrelay $&{client_name} $| $&{client_addr} R$* $| $#$* $#$2 R$* $| $* $: $1 ', `dnl') -ifdef(`_ACCESS_TABLE_', `dnl + +ifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)') +###################################################################### +### F: LookUpFull -- search for an entry in access database +### +### lookup of full key (which should be an address) and +### variations if +detail exists: +* and without +detail +### +### Parameters: +### <$1> -- key +### <$2> -- default (what to return if not found in db) +dnl must not be empty +### <$3> -- mark (must be <(!|+) single-token>) +### ! does lookup only with tag +### + does lookup with and without tag +### <$4> -- passthru (additional data passed unchanged through) +dnl returns: <default> <passthru> +dnl <result> <passthru> +###################################################################### + +SF +dnl workspace: <key> <def> <o tag> <thru> +dnl full lookup +dnl 2 3 4 5 +R<$+> <$*> <$- $-> <$*> $: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5> +dnl no match, try without tag +dnl 1 2 3 4 +R<?> <$+> <$*> <+ $-> <$*> $: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4> +dnl no match, +detail: try +* +dnl 1 2 3 4 5 6 7 +R<?> <$+ + $* @ $+> <$*> <$- $-> <$*> + $: <$(access $6`'_TAG_DELIM_`'$1+*@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7> +dnl no match, +detail: try +* without tag +dnl 1 2 3 4 5 6 +R<?> <$+ + $* @ $+> <$*> <+ $-> <$*> + $: <$(access $1+*@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6> +dnl no match, +detail: try without +detail +dnl 1 2 3 4 5 6 7 +R<?> <$+ + $* @ $+> <$*> <$- $-> <$*> + $: <$(access $6`'_TAG_DELIM_`'$1@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7> +dnl no match, +detail: try without +detail and without tag +dnl 1 2 3 4 5 6 +R<?> <$+ + $* @ $+> <$*> <+ $-> <$*> + $: <$(access $1@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6> +dnl no match, return <default> <passthru> +dnl 1 2 3 4 5 +R<?> <$+> <$*> <$- $-> <$*> $@ <$2> <$5> +ifdef(`_ATMPF_', `dnl tempfail? +dnl 2 3 4 5 +R<$+ _ATMPF_> <$*> <$- $-> <$*> $@ <_ATMPF_> <$5>', `dnl') +dnl match, return <match> <passthru> +dnl 2 3 4 5 +R<$+> <$*> <$- $-> <$*> $@ <$1> <$5> + +###################################################################### +### E: LookUpExact -- search for an entry in access database +### +### Parameters: +### <$1> -- key +### <$2> -- default (what to return if not found in db) +dnl must not be empty +### <$3> -- mark (must be <(!|+) single-token>) +### ! does lookup only with tag +### + does lookup with and without tag +### <$4> -- passthru (additional data passed unchanged through) +dnl returns: <default> <passthru> +dnl <result> <passthru> +###################################################################### + +SE +dnl 2 3 4 5 +R<$*> <$*> <$- $-> <$*> $: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5> +dnl no match, try without tag +dnl 1 2 3 4 +R<?> <$+> <$*> <+ $-> <$*> $: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4> +dnl no match, return default passthru +dnl 1 2 3 4 5 +R<?> <$+> <$*> <$- $-> <$*> $@ <$2> <$5> +ifdef(`_ATMPF_', `dnl tempfail? +dnl 2 3 4 5 +R<$+ _ATMPF_> <$*> <$- $-> <$*> $@ <_ATMPF_> <$5>', `dnl') +dnl match, return <match> <passthru> +dnl 2 3 4 5 +R<$+> <$*> <$- $-> <$*> $@ <$1> <$5> + +###################################################################### +### U: LookUpUser -- search for an entry in access database +### +### lookup of key (which should be a local part) and +### variations if +detail exists: +* and without +detail +### +### Parameters: +### <$1> -- key (user@) +### <$2> -- default (what to return if not found in db) +dnl must not be empty +### <$3> -- mark (must be <(!|+) single-token>) +### ! does lookup only with tag +### + does lookup with and without tag +### <$4> -- passthru (additional data passed unchanged through) +dnl returns: <default> <passthru> +dnl <result> <passthru> +###################################################################### + +SU +dnl user lookups are always with trailing @ +dnl 2 3 4 5 +R<$+> <$*> <$- $-> <$*> $: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5> +dnl no match, try without tag +dnl 1 2 3 4 +R<?> <$+> <$*> <+ $-> <$*> $: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4> +dnl do not remove the @ from the lookup: +dnl it is part of the +detail@ which is omitted for the lookup +dnl no match, +detail: try +* +dnl 1 2 3 4 5 6 +R<?> <$+ + $* @> <$*> <$- $-> <$*> + $: <$(access $5`'_TAG_DELIM_`'$1+*@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6> +dnl no match, +detail: try +* without tag +dnl 1 2 3 4 5 +R<?> <$+ + $* @> <$*> <+ $-> <$*> + $: <$(access $1+*@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5> +dnl no match, +detail: try without +detail +dnl 1 2 3 4 5 6 +R<?> <$+ + $* @> <$*> <$- $-> <$*> + $: <$(access $5`'_TAG_DELIM_`'$1@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6> +dnl no match, +detail: try without +detail and without tag +dnl 1 2 3 4 5 +R<?> <$+ + $* @> <$*> <+ $-> <$*> + $: <$(access $1@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5> +dnl no match, return <default> <passthru> +dnl 1 2 3 4 5 +R<?> <$+> <$*> <$- $-> <$*> $@ <$2> <$5> +ifdef(`_ATMPF_', `dnl tempfail? +dnl 2 3 4 5 +R<$+ _ATMPF_> <$*> <$- $-> <$*> $@ <_ATMPF_> <$5>', `dnl') +dnl match, return <match> <passthru> +dnl 2 3 4 5 +R<$+> <$*> <$- $-> <$*> $@ <$1> <$5> + ###################################################################### ### SearchList: search a list of items in the access map ### Parameters: @@ -1887,7 +2337,7 @@ dnl maybe we should have a @ (again) in front of the mark to dnl avoid errorneous matches (with error messages?) dnl if we can make sure that tag is always a single token dnl then we can omit the delimiter $|, otherwise we need it -dnl to avoid errorneous matchs (first rule: H: if there +dnl to avoid errorneous matchs (first rule: D: if there dnl is that mark somewhere in the list, it will be taken). dnl moreover, we can do some tricks to enforce lookup with dnl the tag only, e.g.: @@ -1897,7 +2347,7 @@ dnl the tag only, e.g.: dnl Warning: + and ! should be in OperatorChars (otherwise there must be dnl a blank between them and the tag. ### possible values for "mark" are: -### H: recursive host lookup (LookUpDomain) +### D: recursive host lookup (LookUpDomain) dnl A: recursive address lookup (LookUpAddress) [not yet required] ### E: exact lookup, no modifications ### F: full lookup, try user+ext@domain and user@domain @@ -1907,42 +2357,32 @@ dnl A: recursive address lookup (LookUpAddress) [not yet required] # class with valid marks for SearchList dnl if A is activated: add it -C{src}E F H U +C{src}E F D U ifdef(`_FFR_SRCHLIST_A', `A') SSearchList -# mark H: lookup domain -R<$+> $| <H:$+> <$*> $: <$1> $| <@> $>LookUpDomain <$2> <?> <$3> <$1> -R<$+> $| <@> <$+> <$*> $: <$1> $| <$2> <$3> -dnl A: NOT YET REQUIRED -dnl R<$+> $| <A:$+> <$*> $: <$1> $| <@> $>LookUpAddress <$2> <?> <$3> <$1> -dnl R<$+> $| <@> <$+> <$*> $: <$1> $| <$2> <$3> -dnl lookup of the item with tag -dnl this applies to F: U: E: -R<$- $-> $| <$={src}:$+> <$*> $: <$1 $2> $| <$(access $2`'_TAG_DELIM_`'$4 $: $3:$4 $)> <$5> -dnl no match, try without tag -R<+ $-> $| <$={src}:$+> <$*> $: <+ $1> $| <$(access $3 $: $2:$3 $)> <$4> -dnl do we really have to distinguish these cases? -dnl probably yes, there might be a + in the domain part (is that allowed?) -dnl user+detail lookups: should it be: -dnl user+detail, user+*, user; just like aliases? -R<$- $-> $| <F:$* + $*@$+> <$*> $: <$1 $2> $| <$(access $2`'_TAG_DELIM_`'$3@$5 $: F:$3 + $4@$5$)> <$6> -R<+ $-> $| <F:$* + $*@$+> <$*> $: <+ $1> $| <$(access $2@$4 $: F:$2 + $3@$4$)> <$5> -dnl user lookups are always with trailing @ -dnl do not remove the @ from the lookup: -dnl it is part of the +detail@ which is omitted for the lookup -R<$- $-> $| <U:$* + $*> <$*> $: <$1 $2> $| <$(access $2`'_TAG_DELIM_`'$3@ $: U:$3 + $4$)> <$5> -dnl no match, try without tag -R<+ $-> $| <U:$* + $*> <$*> $: <+ $1> $| <$(access $2@ $: U:$2 + $3$)> <$4> -dnl no match, try rest of list -R<$+> $| <$={src}:$+> <$+> $@ $>SearchList <$1> $| <$4> -dnl no match, list empty: return failure -R<$+> $| <$={src}:$+> <> $@ <?> -dnl got result, return it -R<$+> $| <$+> <$*> $@ <$2> +# just call the ruleset with the name of the tag... nice trick... +dnl 2 3 4 +R<$+> $| <$={src}:$*> <$*> $: <$1> $| <$4> $| $>$2 <$3> <?> <$1> <> +dnl workspace: <o tag> $| <rest> $| <result of lookup> <> +dnl no match and nothing left: return +R<$+> $| <> $| <?> <> $@ <?> +dnl no match but something left: continue +R<$+> $| <$+> $| <?> <> $@ $>SearchList <$1> $| <$2> +dnl match: return +R<$+> $| <$*> $| <$+> <> $@ <$3> dnl return result from recursive invocation -R<$+> $| <$+> $@ <$2>', `dnl') +R<$+> $| <$+> $@ <$2> +dnl endif _ACCESS_TABLE_ +divert(0) + +###################################################################### +### trust_auth: is user trusted to authenticate as someone else? +### +### Parameters: +### $1: AUTH= parameter from MAIL command +###################################################################### -# is user trusted to authenticate as someone else? -dnl AUTH= parameter from MAIL command +dnl empty ruleset definition so it can be called +SLocal_trust_auth Strust_auth R$* $: $&{auth_type} $| $1 # required by RFC 2554 section 4. @@ -1956,111 +2396,288 @@ R$* $| $#$* $#$2 dnl default: error R$* $#error $@ 5.7.1 $: "550 " $&{auth_authen} " not allowed to act as " $&{auth_author} -dnl empty ruleset definition so it can be called -SLocal_trust_auth +###################################################################### +### Relay_Auth: allow relaying based on authentication? +### +### Parameters: +### $1: ${auth_type} +###################################################################### +SLocal_Relay_Auth -ifdef(`_FFR_TLS_O_T', `dnl -Soffer_tls -R$* $: $>LookUpDomain <$&{client_name}> <?> <> <! TLS_OFF_TAG> -R<?>$* $: $>LookUpAddress <$&{client_addr}> <?> <> <! TLS_OFF_TAG> -R<?>$* $: <$(access TLS_OFF_TAG: $: ? $)> +ifdef(`_ACCESS_TABLE_', `dnl +###################################################################### +### srv_features: which features to offer to a client? +### (done in server) +###################################################################### +Ssrv_features +ifdef(`_LOCAL_SRV_FEATURES_', `dnl +R$* $: $1 $| $>"Local_srv_features" $1 +R$* $| $#$* $#$2 +R$* $| $* $: $1', `dnl') +R$* $: $>D <$&{client_name}> <?> <! SRV_FEAT_TAG> <> +R<?>$* $: $>A <$&{client_addr}> <?> <! SRV_FEAT_TAG> <> +R<?>$* $: <$(access SRV_FEAT_TAG: $: ? $)> R<?>$* $@ OK -R<NO> <> $#error $@ 5.7.1 $: "550 do not offer TLS for " $&{client_name} " ["$&{client_addr}"]" +ifdef(`_ATMPF_', `dnl tempfail? +R<$* _ATMPF_>$* $#temp', `dnl') +R<$+>$* $# $1 +###################################################################### +### try_tls: try to use STARTTLS? +### (done in client) +###################################################################### Stry_tls -R$* $: $>LookUpDomain <$&{server_name}> <?> <> <! TLS_TRY_TAG> -R<?>$* $: $>LookUpAddress <$&{server_addr}> <?> <> <! TLS_TRY_TAG> +ifdef(`_LOCAL_TRY_TLS_', `dnl +R$* $: $1 $| $>"Local_try_tls" $1 +R$* $| $#$* $#$2 +R$* $| $* $: $1', `dnl') +R$* $: $>D <$&{server_name}> <?> <! TLS_TRY_TAG> <> +R<?>$* $: $>A <$&{server_addr}> <?> <! TLS_TRY_TAG> <> R<?>$* $: <$(access TLS_TRY_TAG: $: ? $)> R<?>$* $@ OK +ifdef(`_ATMPF_', `dnl tempfail? +R<$* _ATMPF_>$* $#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl') R<NO>$* $#error $@ 5.7.1 $: "550 do not try TLS with " $&{server_name} " ["$&{server_addr}"]" -')dnl + +###################################################################### +### tls_rcpt: is connection with server "good" enough? +### (done in client, per recipient) +dnl called from deliver() before RCPT command +### +### Parameters: +### $1: recipient +###################################################################### +Stls_rcpt +ifdef(`_LOCAL_TLS_RCPT_', `dnl +R$* $: $1 $| $>"Local_tls_rcpt" $1 +R$* $| $#$* $#$2 +R$* $| $* $: $1', `dnl') +dnl store name of other side +R$* $: $(macro {TLS_Name} $@ $&{server_name} $) $1 +dnl canonify recipient address +R$+ $: <?> $>CanonAddr $1 +dnl strip trailing dots +R<?> $+ < @ $+ . > <?> $1 <@ $2 > +dnl full address? +R<?> $+ < @ $+ > $: $1 <@ $2 > $| <F:$1@$2> <U:$1@> <D:$2> <E:> +dnl only localpart? +R<?> $+ $: $1 $| <U:$1@> <E:> +dnl look it up +dnl also look up a default value via E: +R$* $| $+ $: $1 $| $>SearchList <! TLS_RCPT_TAG> $| $2 <> +dnl found nothing: stop here +R$* $| <?> $@ OK +ifdef(`_ATMPF_', `dnl tempfail? +R$* $| <$* _ATMPF_> $#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl') +dnl use the generic routine (for now) +R$* $| <$+> $@ $>"TLS_connection" $&{verify} $| <$2>') -# is connection with client "good" enough? (done in server) -# input: ${verify} $| (MAIL|STARTTLS) +###################################################################### +### tls_client: is connection with client "good" enough? +### (done in server) +### +### Parameters: +### ${verify} $| (MAIL|STARTTLS) +###################################################################### dnl MAIL: called from check_mail dnl STARTTLS: called from smtp() after STARTTLS has been accepted Stls_client +ifdef(`_LOCAL_TLS_CLIENT_', `dnl +R$* $: $1 $| $>"Local_tls_client" $1 +R$* $| $#$* $#$2 +R$* $| $* $: $1', `dnl') ifdef(`_ACCESS_TABLE_', `dnl +dnl store name of other side +R$* $: $(macro {TLS_Name} $@ $&{server_name} $) $1 dnl ignore second arg for now dnl maybe use it to distinguish permanent/temporary error? dnl if MAIL: permanent (STARTTLS has not been offered) dnl if STARTTLS: temporary (offered but maybe failed) -R$* $| $* $: $1 $| $>LookUpDomain <$&{client_name}> <?> <> <! TLS_CLT_TAG> -R$* $| <?>$* $: $1 $| $>LookUpAddress <$&{client_addr}> <?> <> <! TLS_CLT_TAG> +R$* $| $* $: $1 $| $>D <$&{client_name}> <?> <! TLS_CLT_TAG> <> +R$* $| <?>$* $: $1 $| $>A <$&{client_addr}> <?> <! TLS_CLT_TAG> <> dnl do a default lookup: just TLS_CLT_TAG R$* $| <?>$* $: $1 $| <$(access TLS_CLT_TAG`'_TAG_DELIM_ $: ? $)> -R$* $@ $>"tls_connection" $1', `dnl -R$* $| $* $@ $>"tls_connection" $1') +ifdef(`_ATMPF_', `dnl tempfail? +R$* $| <$* _ATMPF_> $#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl') +R$* $@ $>"TLS_connection" $1', `dnl +R$* $| $* $@ $>"TLS_connection" $1') -# is connection with server "good" enough? (done in client) +###################################################################### +### tls_server: is connection with server "good" enough? +### (done in client) +### +### Parameter: +### ${verify} +###################################################################### dnl i.e. has the server been authenticated and is encryption active? dnl called from deliver() after STARTTLS command -# input: ${verify} Stls_server +ifdef(`_LOCAL_TLS_SERVER_', `dnl +R$* $: $1 $| $>"Local_tls_server" $1 +R$* $| $#$* $#$2 +R$* $| $* $: $1', `dnl') ifdef(`_ACCESS_TABLE_', `dnl -R$* $: $1 $| $>LookUpDomain <$&{server_name}> <?> <> <! TLS_SRV_TAG> -R$* $| <?>$* $: $1 $| $>LookUpAddress <$&{server_addr}> <?> <> <! TLS_SRV_TAG> +dnl store name of other side +R$* $: $(macro {TLS_Name} $@ $&{server_name} $) $1 +R$* $: $1 $| $>D <$&{server_name}> <?> <! TLS_SRV_TAG> <> +R$* $| <?>$* $: $1 $| $>A <$&{server_addr}> <?> <! TLS_SRV_TAG> <> dnl do a default lookup: just TLS_SRV_TAG R$* $| <?>$* $: $1 $| <$(access TLS_SRV_TAG`'_TAG_DELIM_ $: ? $)> -R$* $@ $>"tls_connection" $1', `dnl -R$* $@ $>"tls_connection" $1') +ifdef(`_ATMPF_', `dnl tempfail? +R$* $| <$* _ATMPF_> $#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl') +R$* $@ $>"TLS_connection" $1', `dnl +R$* $@ $>"TLS_connection" $1') -Stls_connection +###################################################################### +### TLS_connection: is TLS connection "good" enough? +### +### Parameters: ifdef(`_ACCESS_TABLE_', `dnl +### ${verify} $| <Requirement> [<>]', `dnl +### ${verify}') +### Requirement: RHS from access map, may be ? for none. +dnl syntax for Requirement: +dnl [(PERM|TEMP)+] (VERIFY[:bits]|ENCR:bits) [+extensions] +dnl extensions: could be a list of further requirements +dnl for now: CN:string {cn_subject} == string +###################################################################### +STLS_connection +ifdef(`_ACCESS_TABLE_', `dnl', `dnl use default error +dnl deal with TLS handshake failures: abort +RSOFTWARE $#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake." +divert(-1)') dnl common ruleset for tls_{client|server} -dnl input: $&{verify} $| <ResultOfLookup> [<>] +dnl input: ${verify} $| <ResultOfLookup> [<>] dnl remove optional <> R$* $| <$*>$* $: $1 $| <$2> +dnl workspace: ${verify} $| <ResultOfLookup> +# create the appropriate error codes dnl permanent or temporary error? R$* $| <PERM + $={tls} $*> $: $1 $| <503:5.7.0> <$2 $3> R$* $| <TEMP + $={tls} $*> $: $1 $| <403:4.7.0> <$2 $3> dnl default case depends on TLS_PERM_ERR R$* $| <$={tls} $*> $: $1 $| <ifdef(`TLS_PERM_ERR', `503:5.7.0', `403:4.7.0')> <$2 $3> -dnl deal with TLS handshake failures: abort +dnl workspace: ${verify} $| [<SMTP:ESC>] <ResultOfLookup> +# deal with TLS handshake failures: abort RSOFTWARE $| <$-:$+> $* $#error $@ $2 $: $1 " TLS handshake failed." dnl no <reply:dns> i.e. not requirements in the access map dnl use default error RSOFTWARE $| $* $#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake failed." -R$* $| <$*> <VERIFY> $: <$2> <VERIFY> $1 -R$* $| <$*> <$={tls}:$->$* $: <$2> <$3:$4> $1 +R$* $| <$*> <VERIFY> $: <$2> <VERIFY> <> $1 +dnl separate optional requirements +R$* $| <$*> <VERIFY + $+> $: <$2> <VERIFY> <$3> $1 +R$* $| <$*> <$={tls}:$->$* $: <$2> <$3:$4> <> $1 +dnl separate optional requirements +R$* $| <$*> <$={tls}:$- + $+>$* $: <$2> <$3:$4> <$5> $1 dnl some other value in access map: accept dnl this also allows to override the default case (if used) R$* $| $* $@ OK # authentication required: give appropriate error # other side did authenticate (via STARTTLS) -dnl workspace: <SMTP:ESC> <{VERIFY,ENCR}[:BITS]> ${verify} +dnl workspace: <SMTP:ESC> <{VERIFY,ENCR}[:BITS]> <[extensions]> ${verify} dnl only verification required and it succeeded -R<$*><VERIFY> OK $@ OK +R<$*><VERIFY> <> OK $@ OK +dnl verification required and it succeeded but extensions are given +dnl change it to <SMTP:ESC> <REQ:0> <extensions> +R<$*><VERIFY> <$+> OK $: <$1> <REQ:0> <$2> dnl verification required + some level of encryption -R<$*><VERIFY:$-> OK $: <$1> <REQ:$2> +R<$*><VERIFY:$-> <$*> OK $: <$1> <REQ:$2> <$3> dnl just some level of encryption required -R<$*><ENCR:$-> $* $: <$1> <REQ:$2> -dnl verification required but ${verify} is not set -R<$-:$+><VERIFY $*> $#error $@ $2 $: $1 " authentication required" -R<$-:$+><VERIFY $*> FAIL $#error $@ $2 $: $1 " authentication failed" -R<$-:$+><VERIFY $*> NO $#error $@ $2 $: $1 " not authenticated" -R<$-:$+><VERIFY $*> NONE $#error $@ $2 $: $1 " other side does not support STARTTLS" +R<$*><ENCR:$-> <$*> $* $: <$1> <REQ:$2> <$3> +dnl workspace: +dnl 1. <SMTP:ESC> <VERIFY [:bits]> <[extensions]> {verify} (!= OK) +dnl 2. <SMTP:ESC> <REQ:bits> <[extensions]> +dnl verification required but ${verify} is not set (case 1.) +R<$-:$+><VERIFY $*> <$*> $#error $@ $2 $: $1 " authentication required" +R<$-:$+><VERIFY $*> <$*> FAIL $#error $@ $2 $: $1 " authentication failed" +R<$-:$+><VERIFY $*> <$*> NO $#error $@ $2 $: $1 " not authenticated" +R<$-:$+><VERIFY $*> <$*> NOT $#error $@ $2 $: $1 " no authentication requested" +R<$-:$+><VERIFY $*> <$*> NONE $#error $@ $2 $: $1 " other side does not support STARTTLS" dnl some other value for ${verify} -R<$-:$+><VERIFY $*> $+ $#error $@ $2 $: $1 " authentication failure " $4 -dnl some level of encryption required: get the maximum level -R<$*><REQ:$-> $: <$1> <REQ:$2> $>max $&{cipher_bits} : $&{auth_ssf} +R<$-:$+><VERIFY $*> <$*> $+ $#error $@ $2 $: $1 " authentication failure " $4 +dnl some level of encryption required: get the maximum level (case 2.) +R<$*><REQ:$-> <$*> $: <$1> <REQ:$2> <$3> $>max $&{cipher_bits} : $&{auth_ssf} dnl compare required bits with actual bits -R<$*><REQ:$-> $- $: <$1> <$2:$3> $(arith l $@ $3 $@ $2 $) -R<$-:$+><$-:$-> TRUE $#error $@ $2 $: $1 " encryption too weak " $4 " less than " $3 +R<$*><REQ:$-> <$*> $- $: <$1> <$2:$4> <$3> $(arith l $@ $4 $@ $2 $) +R<$-:$+><$-:$-> <$*> TRUE $#error $@ $2 $: $1 " encryption too weak " $4 " less than " $3 +dnl strength requirements fulfilled +dnl TLS Additional Requirements Separator +dnl this should be something which does not appear in the extensions itself +dnl @ could be part of a CN, DN, etc... +dnl use < > ? those are encoded in CN, DN, ... +define(`_TLS_ARS_', `++')dnl +dnl workspace: +dnl <SMTP:ESC> <REQ:bits> <extensions> result-of-compare +R<$-:$+><$-:$-> <$*> $* $: <$1:$2 _TLS_ARS_ $5> +dnl workspace: <SMTP:ESC _TLS_ARS_ extensions> +dnl continue: check extensions +R<$-:$+ _TLS_ARS_ > $@ OK +dnl split extensions into own list +R<$-:$+ _TLS_ARS_ $+ > $: <$1:$2> <$3> +R<$-:$+> < $+ _TLS_ARS_ $+ > <$1:$2> <$3> <$4> +R<$-:$+> $+ $@ $>"TLS_req" $3 $| <$1:$2> +###################################################################### +### TLS_req: check additional TLS requirements +### +### Parameters: [<list> <of> <req>] $| <$-:$+> +### $-: SMTP reply code +### $+: Enhanced Status Code +dnl further requirements for this ruleset: +dnl name of "other side" is stored is {TLS_name} (client/server_name) +dnl +dnl currently only CN[:common_name] is implemented +dnl right now this is only a logical AND +dnl i.e. all requirements must be true +dnl how about an OR? CN must be X or CN must be Y or .. +dnl use a macro to compute this as a trivial sequential +dnl operations (no precedences etc)? +###################################################################### +STLS_req +dnl no additional requirements: ok +R $| $+ $@ OK +dnl require CN: but no CN specified: use name of other side +R<CN> $* $| <$+> $: <CN:$&{TLS_Name}> $1 $| <$2> +dnl match, check rest +R<CN:$&{cn_subject}> $* $| <$+> $@ $>"TLS_req" $1 $| <$2> +dnl CN does not match +dnl 1 2 3 4 +R<CN:$+> $* $| <$-:$+> $#error $@ $4 $: $3 " CN " $&{cn_subject} " does not match " $1 +dnl cert subject +R<CS:$&{cert_subject}> $* $| <$+> $@ $>"TLS_req" $1 $| <$2> +dnl CS does not match +dnl 1 2 3 4 +R<CS:$+> $* $| <$-:$+> $#error $@ $4 $: $3 " CERT Subject " $&{cert_subject} " does not match " $1 +dnl match, check rest +R<CI:$&{cert_issuer}> $* $| <$+> $@ $>"TLS_req" $1 $| <$2> +dnl CI does not match +dnl 1 2 3 4 +R<CI:$+> $* $| <$-:$+> $#error $@ $4 $: $3 " CERT Issuer " $&{cert_issuer} " does not match " $1 +dnl return from recursive call +ROK $@ OK + +###################################################################### +### max: return the maximum of two values separated by : +### +### Parameters: [$-]:[$-] +###################################################################### Smax -dnl compute the max of two values separated by : R: $: 0 R:$- $: $1 R$-: $: $1 R$-:$- $: $(arith l $@ $1 $@ $2 $) : $1 : $2 RTRUE:$-:$- $: $2 -R$-:$-:$- $: $2', -`dnl use default error -dnl deal with TLS handshake failures: abort -RSOFTWARE $#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake."') +R$-:$-:$- $: $2 +dnl endif _ACCESS_TABLE_ +divert(0) -SRelayAuth +###################################################################### +### RelayTLS: allow relaying based on TLS authentication +### +### Parameters: +### none +###################################################################### +SRelayTLS # authenticated? dnl we do not allow relaying for anyone who can present a cert dnl signed by a "trusted" CA. For example, even if we put verisigns @@ -2073,24 +2690,54 @@ dnl (maybe after extracting a part with a regular expression) dnl if this returns RELAY we relay without further questions dnl if it returns SUBJECT we perform a similar check on the dnl cert subject. -R$* $| OK $: $1 -R$* $| $* $@ NO not authenticated ifdef(`_ACCESS_TABLE_', `dnl +R$* $: <?> $&{verify} +R<?> OK $: OK authenticated: continue +R<?> $* $@ NO not authenticated ifdef(`_CERT_REGEX_ISSUER_', `dnl -R$* $: $1 $| $(CERTIssuer $&{cert_issuer} $)', -`R$* $: $1 $| $&{cert_issuer}') -R$* $| $+ $: $1 $| $(access CERTISSUER:$2 $) +R$* $: $(CERTIssuer $&{cert_issuer} $)', +`R$* $: $&{cert_issuer}') +R$+ $: $(access CERTISSUER:$1 $) dnl use $# to stop further checks (delay_check) -R$* $| RELAY $# RELAYCERTISSUER +RRELAY $# RELAY ifdef(`_CERT_REGEX_SUBJECT_', `dnl -R$* $| SUBJECT $: $1 $| <@> $(CERTSubject $&{cert_subject} $)', -`R$* $| SUBJECT $: $1 $| <@> $&{cert_subject}') -R$* $| <@> $+ $: $1 $| <@> $(access CERTSUBJECT:$2 $) -R$* $| <@> RELAY $# RELAYCERTSUBJECT -R$* $| $* $: $1', `dnl') +RSUBJECT $: <@> $(CERTSubject $&{cert_subject} $)', +`RSUBJECT $: <@> $&{cert_subject}') +R<@> $+ $: <@> $(access CERTSUBJECT:$1 $) +R<@> RELAY $# RELAY +R$* $: NO', `dnl') + +###################################################################### +### authinfo: lookup authinfo in the access map +### +### Parameters: +### $1: {server_name} +### $2: {server_addr} +dnl both are currently ignored +dnl if it should be done via another map, we either need to restrict +dnl functionality (it calls D and A) or copy those rulesets (or add another +dnl parameter which I want to avoid, it's quite complex already) +###################################################################### +dnl omit this ruleset if neither is defined? +dnl it causes DefaultAuthInfo to be ignored +dnl (which may be considered a good thing). +Sauthinfo +ifdef(`_AUTHINFO_TABLE_', `dnl +R$* $: <$(authinfo AuthInfo:$&{server_name} $: ? $)> +R<?> $: <$(authinfo AuthInfo:$&{server_addr} $: ? $)> +R<?> $: <$(authinfo AuthInfo: $: ? $)> +R<?> $@ no no authinfo available +R<$*> $# $1 +dnl', `dnl +ifdef(`_ACCESS_TABLE_', `dnl +R$* $: $1 $| $>D <$&{server_name}> <?> <! AuthInfo> <> +R$* $| <?>$* $: $1 $| $>A <$&{server_addr}> <?> <! AuthInfo> <> +R$* $| <?>$* $: $1 $| <$(access AuthInfo: $: ? $)> <> +R$* $| <?>$* $@ no no authinfo available +R$* $| <$*> <> $# $2 +dnl', `dnl')') undivert(9)dnl LOCAL_RULESETS -ifdef(`_FFR_MILTER', ` # ###################################################################### ###################################################################### @@ -2099,7 +2746,7 @@ ifdef(`_FFR_MILTER', ` ##### ###################################################################### ###################################################################### -_MAIL_FILTERS_') +_MAIL_FILTERS_ # ###################################################################### ###################################################################### diff --git a/gnu/usr.sbin/sendmail/cf/m4/version.m4 b/gnu/usr.sbin/sendmail/cf/m4/version.m4 index 366d1d8c144..d4b63a5fc5f 100644 --- a/gnu/usr.sbin/sendmail/cf/m4/version.m4 +++ b/gnu/usr.sbin/sendmail/cf/m4/version.m4 @@ -11,8 +11,8 @@ divert(-1) # the sendmail distribution. # # -VERSIONID(`$Sendmail: version.m4,v 8.39.4.35 2001/08/20 14:45:34 gshapiro Exp $') +VERSIONID(`$Sendmail: version.m4,v 8.71 2001/09/07 20:59:45 ca Exp $') # divert(0) # Configuration version number -DZ8.11.6`'ifdef(`confCF_VERSION', `/confCF_VERSION') +DZ8.12.0`'ifdef(`confCF_VERSION', `/confCF_VERSION') diff --git a/gnu/usr.sbin/sendmail/cf/mailer/cyrus.m4 b/gnu/usr.sbin/sendmail/cf/mailer/cyrus.m4 index 384d26a798a..16eed11d5e8 100644 --- a/gnu/usr.sbin/sendmail/cf/mailer/cyrus.m4 +++ b/gnu/usr.sbin/sendmail/cf/mailer/cyrus.m4 @@ -1,6 +1,6 @@ PUSHDIVERT(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. # All rights reserved. # # By using this file, you agree to the terms and conditions set @@ -35,9 +35,6 @@ PUSHDIVERT(-1) # Contributed to Berkeley by John Gardiner Myers <jgm+@CMU.EDU>. # -ifdef(`_MAILER_local_', `', - `errprint(`*** MAILER(`local') must appear before MAILER(`cyrus')')')dnl - _DEFIFNOT(`CYRUS_MAILER_FLAGS', `Ah5@/:|') ifdef(`CYRUS_MAILER_PATH',, `define(`CYRUS_MAILER_PATH', /usr/cyrus/bin/deliver)') ifdef(`CYRUS_MAILER_ARGS',, `define(`CYRUS_MAILER_ARGS', `deliver -e -m $h -- $u')') @@ -51,7 +48,7 @@ POPDIVERT ### Cyrus Mailer specification ### ################################################## -VERSIONID(`$Sendmail: cyrus.m4,v 8.21 1999/10/18 04:57:52 gshapiro Exp $ (Carnegie Mellon)') +VERSIONID(`$Sendmail: cyrus.m4,v 8.22 2000/09/02 17:46:43 ca Exp $ (Carnegie Mellon)') Mcyrus, P=CYRUS_MAILER_PATH, F=_MODMF_(CONCAT(`lsDFMnPq', CYRUS_MAILER_FLAGS), `CYRUS'), S=EnvFromL, R=EnvToL/HdrToL, ifdef(`CYRUS_MAILER_MAX', `M=CYRUS_MAILER_MAX, ')U=CYRUS_MAILER_USER, T=DNS/RFC822/X-Unix, diff --git a/gnu/usr.sbin/sendmail/cf/mailer/local.m4 b/gnu/usr.sbin/sendmail/cf/mailer/local.m4 index 81ce246ef11..8ce1652788f 100644 --- a/gnu/usr.sbin/sendmail/cf/mailer/local.m4 +++ b/gnu/usr.sbin/sendmail/cf/mailer/local.m4 @@ -21,65 +21,73 @@ _DEFIFNOT(`LOCAL_SHELL_FLAGS', `eu9') ifdef(`LOCAL_SHELL_PATH',, `define(`LOCAL_SHELL_PATH', /bin/sh)') ifdef(`LOCAL_SHELL_ARGS',, `define(`LOCAL_SHELL_ARGS', `sh -c $u')') ifdef(`LOCAL_SHELL_DIR',, `define(`LOCAL_SHELL_DIR', `$z:/')') +define(`LOCAL_RWR', `ifdef(`_LOCAL_LMTP_', +`S=EnvFromSMTP/HdrFromL, R=EnvToL/HdrToL', +`S=EnvFromL/HdrFromL, R=EnvToL/HdrToL')') +define(`_LOCAL_QGRP', `ifelse(defn(`LOCAL_MAILER_QGRP'),`',`', ` Q=LOCAL_MAILER_QGRP,')')dnl +define(`_PROG_QGRP', `ifelse(defn(`LOCAL_PROG_QGRP'),`',`', ` Q=LOCAL_PROG_QGRP,')')dnl POPDIVERT ################################################## ### Local and Program Mailer specification ### ################################################## -VERSIONID(`$Sendmail: local.m4,v 8.50.16.2 2000/09/17 17:04:22 gshapiro Exp $') +VERSIONID(`$Sendmail: local.m4,v 8.58 2000/10/26 01:58:29 ca Exp $') # # Envelope sender rewriting # -SEnvFromL=10 +SEnvFromL R<@> $n errors to mailer-daemon R@ <@ $*> $n temporarily bypass Sun bogosity R$+ $: $>AddDomain $1 add local domain if needed -R$* $: $>MasqEnv $1 do masquerading +ifdef(`_LOCAL_NO_MASQUERADE_', `dnl', `dnl +R$* $: $>MasqEnv $1 do masquerading') # # Envelope recipient rewriting # -SEnvToL=20 +SEnvToL R$+ < @ $* > $: $1 strip host part -ifdef(`_FFR_ADDR_TYPE', `dnl -ifdef(`confUSERDB_SPEC', `dnl', -`dnl Do not forget to bump V9 to V10 before removing _FFR_ADDR_TYPE check +ifdef(`confUSERDB_SPEC', `dnl', `dnl R$+ + $* $: < $&{addr_type} > $1 + $2 mark with addr type R<e s> $+ + $* $: $1 remove +detail for sender -R< $* > $+ $: $2 else remove mark')', `dnl') +R< $* > $+ $: $2 else remove mark') # # Header sender rewriting # -SHdrFromL=30 +SHdrFromL R<@> $n errors to mailer-daemon R@ <@ $*> $n temporarily bypass Sun bogosity R$+ $: $>AddDomain $1 add local domain if needed -R$* $: $>MasqHdr $1 do masquerading +ifdef(`_LOCAL_NO_MASQUERADE_', `dnl', `dnl +R$* $: $>MasqHdr $1 do masquerading') # # Header recipient rewriting # -SHdrToL=40 +SHdrToL R$+ $: $>AddDomain $1 add local domain if needed -ifdef(`_ALL_MASQUERADE_', -`R$* $: $>MasqHdr $1 do all-masquerading', +ifdef(`_ALL_MASQUERADE_', `dnl +ifdef(`_LOCAL_NO_MASQUERADE_', `dnl', `dnl +R$* $: $>MasqHdr $1 do all-masquerading')', `R$* < @ *LOCAL* > $* $: $1 < @ $j . > $2') # # Common code to add local domain name (only if always-add-domain) # -SAddDomain=50 +SAddDomain ifdef(`_ALWAYS_ADD_DOMAIN_', `dnl R$* < @ $* > $* $@ $1 < @ $2 > $3 already fully qualified +ifelse(len(X`'_ALWAYS_ADD_DOMAIN_),`1',` R$+ $@ $1 < @ *LOCAL* > add local qualification', +`R$+ $@ $1 < @ _ALWAYS_ADD_DOMAIN_ > add qualification')', `dnl') -Mlocal, P=LOCAL_MAILER_PATH, F=_MODMF_(CONCAT(_DEF_LOCAL_MAILER_FLAGS, LOCAL_MAILER_FLAGS), `LOCAL'), S=EnvFromL/HdrFromL, R=EnvToL/HdrToL,_OPTINS(`LOCAL_MAILER_EOL', ` E=', `, ') - _OPTINS(`LOCAL_MAILER_MAX', `M=', `, ')_OPTINS(`LOCAL_MAILER_MAXMSGS', `m=', `, ')_OPTINS(`LOCAL_MAILER_MAXRCPTS', `r=', `, ')_OPTINS(`LOCAL_MAILER_CHARSET', `C=', `, ')T=DNS/RFC822/LOCAL_MAILER_DSN_DIAGNOSTIC_CODE, +Mlocal, P=LOCAL_MAILER_PATH, F=_MODMF_(CONCAT(_DEF_LOCAL_MAILER_FLAGS, LOCAL_MAILER_FLAGS), `LOCAL'), LOCAL_RWR,_OPTINS(`LOCAL_MAILER_EOL', ` E=', `, ') + _OPTINS(`LOCAL_MAILER_MAX', `M=', `, ')_OPTINS(`LOCAL_MAILER_MAXMSGS', `m=', `, ')_OPTINS(`LOCAL_MAILER_MAXRCPTS', `r=', `, ')_OPTINS(`LOCAL_MAILER_CHARSET', `C=', `, ')T=DNS/RFC822/LOCAL_MAILER_DSN_DIAGNOSTIC_CODE,_LOCAL_QGRP A=LOCAL_MAILER_ARGS Mprog, P=LOCAL_SHELL_PATH, F=CONCAT(_DEF_LOCAL_SHELL_FLAGS, LOCAL_SHELL_FLAGS), S=EnvFromL/HdrFromL, R=EnvToL/HdrToL, D=LOCAL_SHELL_DIR, - _OPTINS(`LOCAL_MAILER_MAX', `M=', `, ')T=X-Unix/X-Unix/X-Unix, + _OPTINS(`LOCAL_MAILER_MAX', `M=', `, ')T=X-Unix/X-Unix/X-Unix,_PROG_QGRP A=LOCAL_SHELL_ARGS diff --git a/gnu/usr.sbin/sendmail/cf/mailer/mail11.m4 b/gnu/usr.sbin/sendmail/cf/mailer/mail11.m4 index 54261dfd470..a7346f78edd 100644 --- a/gnu/usr.sbin/sendmail/cf/mailer/mail11.m4 +++ b/gnu/usr.sbin/sendmail/cf/mailer/mail11.m4 @@ -1,6 +1,6 @@ PUSHDIVERT(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. # All rights reserved. # # By using this file, you agree to the terms and conditions set @@ -41,13 +41,9 @@ POPDIVERT ### UTK-MAIL11 Mailer specification ### ########################################### -VERSIONID(`$Sendmail: mail11.m4,v 8.19 1999/10/18 04:57:54 gshapiro Exp $') +VERSIONID(`$Sendmail: mail11.m4,v 8.21 2001/07/19 00:16:16 ca Exp $') -SMail11From=15 -R$+ $: $>25 $1 preprocess -R$w :: $+ $@ $w :: $1 ready to go - -SMail11To=25 +SMail11To R$+ < @ $- .UUCP > $: $2 ! $1 back to old style R$+ < @ $- .DECNET > $: $2 :: $1 convert to DECnet style R$+ < @ $- .LOCAL > $: $2 :: $1 convert to DECnet style @@ -55,6 +51,10 @@ R$+ < @ $=w. > $: $2 :: $1 convert to DECnet style R$=w :: $+ $2 strip local names R$+ :: $+ $@ $1 :: $2 already qualified +SMail11From +R$+ $: $>Mail11To $1 preprocess +R$w :: $+ $@ $w :: $1 ready to go + Mmail11, P=MAIL11_MAILER_PATH, F=_MODMF_(MAIL11_MAILER_FLAGS, `MAIL11'), S=Mail11From, R=Mail11To, T=DNS/X-DECnet/X-Unix, A=MAIL11_MAILER_ARGS diff --git a/gnu/usr.sbin/sendmail/cf/mailer/phquery.m4 b/gnu/usr.sbin/sendmail/cf/mailer/phquery.m4 index 3d1263aedab..2b709d7c33a 100644 --- a/gnu/usr.sbin/sendmail/cf/mailer/phquery.m4 +++ b/gnu/usr.sbin/sendmail/cf/mailer/phquery.m4 @@ -1,6 +1,6 @@ PUSHDIVERT(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -14,9 +14,6 @@ PUSHDIVERT(-1) # Contributed by Kimmo Suominen <kim@tac.nyc.ny.us>. # -ifdef(`_MAILER_local_', `', - `errprint(`*** MAILER(`local') must appear before MAILER(`phquery')')')dnl - ifdef(`PH_MAILER_PATH',, `define(`PH_MAILER_PATH', /usr/local/etc/phquery)') _DEFIFNOT(`PH_MAILER_FLAGS', `ehmu') ifdef(`PH_MAILER_ARGS',, `define(`PH_MAILER_ARGS', `phquery -- $u')') @@ -27,7 +24,7 @@ POPDIVERT ### PH Mailer specification ### #################################### -VERSIONID(`$Sendmail: phquery.m4,v 8.15 1999/10/18 04:57:54 gshapiro Exp $') +VERSIONID(`$Sendmail: phquery.m4,v 8.16 2000/09/02 17:46:43 ca Exp $') Mph, P=PH_MAILER_PATH, F=_MODMF_(CONCAT(`nrDFM', PH_MAILER_FLAGS), `PH'), S=EnvFromL, R=EnvToL/HdrToL, T=DNS/RFC822/X-Unix, diff --git a/gnu/usr.sbin/sendmail/cf/mailer/pop.m4 b/gnu/usr.sbin/sendmail/cf/mailer/pop.m4 index 2c286406b81..16bd707eac3 100644 --- a/gnu/usr.sbin/sendmail/cf/mailer/pop.m4 +++ b/gnu/usr.sbin/sendmail/cf/mailer/pop.m4 @@ -1,6 +1,6 @@ PUSHDIVERT(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -12,9 +12,6 @@ PUSHDIVERT(-1) # # -ifdef(`_MAILER_local_', `', - `errprint(`*** MAILER(`local') must appear before MAILER(`pop')')')dnl - ifdef(`POP_MAILER_PATH',, `define(`POP_MAILER_PATH', /usr/lib/mh/spop)') _DEFIFNOT(`POP_MAILER_FLAGS', `Penu') ifdef(`POP_MAILER_ARGS',, `define(`POP_MAILER_ARGS', `pop $u')') @@ -25,7 +22,7 @@ POPDIVERT ### POP Mailer specification ### #################################### -VERSIONID(`$Sendmail: pop.m4,v 8.20 1999/10/18 04:57:54 gshapiro Exp $') +VERSIONID(`$Sendmail: pop.m4,v 8.21 2000/09/02 17:46:43 ca Exp $') Mpop, P=POP_MAILER_PATH, F=_MODMF_(CONCAT(`lsDFMq', POP_MAILER_FLAGS), `POP'), S=EnvFromL, R=EnvToL/HdrToL, T=DNS/RFC822/X-Unix, diff --git a/gnu/usr.sbin/sendmail/cf/mailer/procmail.m4 b/gnu/usr.sbin/sendmail/cf/mailer/procmail.m4 index b523be61765..2fd3534cc21 100644 --- a/gnu/usr.sbin/sendmail/cf/mailer/procmail.m4 +++ b/gnu/usr.sbin/sendmail/cf/mailer/procmail.m4 @@ -1,6 +1,6 @@ PUSHDIVERT(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -12,9 +12,6 @@ PUSHDIVERT(-1) # # -ifdef(`_MAILER_smtp_', `', - `errprint(`*** MAILER(`smtp') must appear before MAILER(`procmail')')')dnl - ifdef(`PROCMAIL_MAILER_PATH',, `ifdef(`PROCMAIL_PATH', `define(`PROCMAIL_MAILER_PATH', PROCMAIL_PATH)', @@ -29,7 +26,7 @@ POPDIVERT ### PROCMAIL Mailer specification ### ##################*****################## -VERSIONID(`$Sendmail: procmail.m4,v 8.20 1999/10/18 04:57:54 gshapiro Exp $') +VERSIONID(`$Sendmail: procmail.m4,v 8.21 2000/09/02 17:46:43 ca Exp $') Mprocmail, P=PROCMAIL_MAILER_PATH, F=_MODMF_(CONCAT(`DFM', PROCMAIL_MAILER_FLAGS), `PROCMAIL'), S=EnvFromSMTP/HdrFromSMTP, R=EnvToSMTP/HdrFromSMTP, ifdef(`PROCMAIL_MAILER_MAX', `M=PROCMAIL_MAILER_MAX, ')T=DNS/RFC822/X-Unix, diff --git a/gnu/usr.sbin/sendmail/cf/mailer/smtp.m4 b/gnu/usr.sbin/sendmail/cf/mailer/smtp.m4 index 26656295034..0259481541e 100644 --- a/gnu/usr.sbin/sendmail/cf/mailer/smtp.m4 +++ b/gnu/usr.sbin/sendmail/cf/mailer/smtp.m4 @@ -1,6 +1,6 @@ PUSHDIVERT(-1) # -# Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -19,24 +19,29 @@ ifdef(`ESMTP_MAILER_ARGS',, `define(`ESMTP_MAILER_ARGS', `TCP $h')') ifdef(`SMTP8_MAILER_ARGS',, `define(`SMTP8_MAILER_ARGS', `TCP $h')') ifdef(`DSMTP_MAILER_ARGS',, `define(`DSMTP_MAILER_ARGS', `TCP $h')') ifdef(`RELAY_MAILER_ARGS',, `define(`RELAY_MAILER_ARGS', `TCP $h')') +define(`_SMTP_QGRP', `ifelse(defn(`SMTP_MAILER_QGRP'),`',`', ` Q=SMTP_MAILER_QGRP,')')dnl +define(`_ESMTP_QGRP', `ifelse(defn(`ESMTP_MAILER_QGRP'),`',`', ` Q=ESMTP_MAILER_QGRP,')')dnl +define(`_SMTP8_QGRP', `ifelse(defn(`SMTP8_MAILER_QGRP'),`',`', ` Q=SMTP8_MAILER_QGRP,')')dnl +define(`_DSMTP_QGRP', `ifelse(defn(`DSMTP_MAILER_QGRP'),`',`', ` Q=DSMTP_MAILER_QGRP,')')dnl +define(`_RELAY_QGRP', `ifelse(defn(`RELAY_MAILER_QGRP'),`',`', ` Q=RELAY_MAILER_QGRP,')')dnl POPDIVERT ##################################### ### SMTP Mailer specification ### ##################################### -VERSIONID(`$Sendmail: smtp.m4,v 8.56.2.1.2.3 2000/09/25 13:53:27 ca Exp $') +VERSIONID(`$Sendmail: smtp.m4,v 8.64 2001/04/03 01:52:54 gshapiro Exp $') # # common sender and masquerading recipient rewriting # -SMasqSMTP=61 +SMasqSMTP R$* < @ $* > $* $@ $1 < @ $2 > $3 already fully qualified R$+ $@ $1 < @ *LOCAL* > add local qualification # # convert pseudo-domain addresses to real domain addresses # -SPseudoToReal=51 +SPseudoToReal # pass <route-addr>s through R< @ $+ > $* $@ < @ $1 > $2 resolve <route-addr> @@ -44,7 +49,7 @@ R< @ $+ > $* $@ < @ $1 > $2 resolve <route-addr> # output fake domains as user%fake@relay ifdef(`BITNET_RELAY', `R$+ <@ $+ .BITNET. > $: $1 % $2 .BITNET < @ $B > user@host.BITNET -R$+.BITNET <@ $+:$+ > $: $1 .BITNET < @ $3 > strip mailer: part', +R$+.BITNET <@ $~[ $*:$+ > $: $1 .BITNET < @ $4 > strip mailer: part', `dnl') ifdef(`_NO_UUCP_', `dnl', ` # do UUCP heuristics; note that these are shared with UUCP mailers @@ -56,14 +61,14 @@ R< $&h ! > $- ! $+ $@ $2 < @ $1 .UUCP. > R< $&h ! > $-.$+ ! $+ $@ $3 < @ $1.$2 > R< $&h ! > $+ $@ $1 < @ $&h .UUCP. > R< $+ ! > $+ $: $1 ! $2 < @ $Y > use UUCP_RELAY -R$+ < @ $+ : $+ > $@ $1 < @ $3 > strip mailer: part +R$+ < @ $~[ $* : $+ > $@ $1 < @ $4 > strip mailer: part R$+ < @ > $: $1 < @ *LOCAL* > if no UUCP_RELAY') # # envelope sender rewriting # -SEnvFromSMTP=11 +SEnvFromSMTP R$+ $: $>PseudoToReal $1 sender/recipient common R$* :; <@> $@ list:; special case R$* $: $>MasqSMTP $1 qualify unqual'ed names @@ -74,7 +79,7 @@ R$+ $: $>MasqEnv $1 do masquerading # envelope recipient rewriting -- # also header recipient if not masquerading recipients # -SEnvToSMTP=21 +SEnvToSMTP R$+ $: $>PseudoToReal $1 sender/recipient common R$+ $: $>MasqSMTP $1 qualify unqual'ed names R$* < @ *LOCAL* > $* $: $1 < @ $j . > $2 @@ -82,7 +87,7 @@ R$* < @ *LOCAL* > $* $: $1 < @ $j . > $2 # # header sender and masquerading header recipient rewriting # -SHdrFromSMTP=31 +SHdrFromSMTP R$+ $: $>PseudoToReal $1 sender/recipient common R:; <@> $@ list:; special case @@ -96,22 +101,22 @@ R$+ $: $>MasqHdr $1 do masquerading # # relay mailer header masquerading recipient rewriting # -SMasqRelay=71 +SMasqRelay R$+ $: $>MasqSMTP $1 R$+ $: $>MasqHdr $1 Msmtp, P=[IPC], F=_MODMF_(CONCAT(_DEF_SMTP_MAILER_FLAGS, SMTP_MAILER_FLAGS), `SMTP'), S=EnvFromSMTP/HdrFromSMTP, R=ifdef(`_ALL_MASQUERADE_', `EnvToSMTP/HdrFromSMTP', `EnvToSMTP'), E=\r\n, L=990, - _OPTINS(`SMTP_MAILER_MAX', `M=', `, ')_OPTINS(`SMTP_MAILER_MAXMSGS', `m=', `, ')_OPTINS(`SMTP_MAILER_MAXRCPTS', `r=', `, ')_OPTINS(`SMTP_MAILER_CHARSET', `C=', `, ')T=DNS/RFC822/SMTP, + _OPTINS(`SMTP_MAILER_MAX', `M=', `, ')_OPTINS(`SMTP_MAILER_MAXMSGS', `m=', `, ')_OPTINS(`SMTP_MAILER_MAXRCPTS', `r=', `, ')_OPTINS(`SMTP_MAILER_CHARSET', `C=', `, ')T=DNS/RFC822/SMTP,_SMTP_QGRP A=SMTP_MAILER_ARGS -Mesmtp, P=[IPC], F=_MODMF_(CONCAT(_DEF_SMTP_MAILER_FLAGS, `a', SMTP_MAILER_FLAGS), `SMTP'), S=EnvFromSMTP/HdrFromSMTP, R=ifdef(`_ALL_MASQUERADE_', `EnvToSMTP/HdrFromSMTP', `EnvToSMTP'), E=\r\n, L=990, - _OPTINS(`SMTP_MAILER_MAX', `M=', `, ')_OPTINS(`SMTP_MAILER_MAXMSGS', `m=', `, ')_OPTINS(`SMTP_MAILER_MAXRCPTS', `r=', `, ')_OPTINS(`SMTP_MAILER_CHARSET', `C=', `, ')T=DNS/RFC822/SMTP, +Mesmtp, P=[IPC], F=_MODMF_(CONCAT(_DEF_SMTP_MAILER_FLAGS, `a', SMTP_MAILER_FLAGS), `ESMTP'), S=EnvFromSMTP/HdrFromSMTP, R=ifdef(`_ALL_MASQUERADE_', `EnvToSMTP/HdrFromSMTP', `EnvToSMTP'), E=\r\n, L=990, + _OPTINS(`SMTP_MAILER_MAX', `M=', `, ')_OPTINS(`SMTP_MAILER_MAXMSGS', `m=', `, ')_OPTINS(`SMTP_MAILER_MAXRCPTS', `r=', `, ')_OPTINS(`SMTP_MAILER_CHARSET', `C=', `, ')T=DNS/RFC822/SMTP,_ESMTP_QGRP A=ESMTP_MAILER_ARGS -Msmtp8, P=[IPC], F=_MODMF_(CONCAT(_DEF_SMTP_MAILER_FLAGS, `8', SMTP_MAILER_FLAGS), `SMTP'), S=EnvFromSMTP/HdrFromSMTP, R=ifdef(`_ALL_MASQUERADE_', `EnvToSMTP/HdrFromSMTP', `EnvToSMTP'), E=\r\n, L=990, - _OPTINS(`SMTP_MAILER_MAX', `M=', `, ')_OPTINS(`SMTP_MAILER_MAXMSGS', `m=', `, ')_OPTINS(`SMTP_MAILER_MAXRCPTS', `r=', `, ')_OPTINS(`SMTP_MAILER_CHARSET', `C=', `, ')T=DNS/RFC822/SMTP, +Msmtp8, P=[IPC], F=_MODMF_(CONCAT(_DEF_SMTP_MAILER_FLAGS, `8', SMTP_MAILER_FLAGS), `SMTP8'), S=EnvFromSMTP/HdrFromSMTP, R=ifdef(`_ALL_MASQUERADE_', `EnvToSMTP/HdrFromSMTP', `EnvToSMTP'), E=\r\n, L=990, + _OPTINS(`SMTP_MAILER_MAX', `M=', `, ')_OPTINS(`SMTP_MAILER_MAXMSGS', `m=', `, ')_OPTINS(`SMTP_MAILER_MAXRCPTS', `r=', `, ')_OPTINS(`SMTP_MAILER_CHARSET', `C=', `, ')T=DNS/RFC822/SMTP,_SMTP8_QGRP A=SMTP8_MAILER_ARGS -Mdsmtp, P=[IPC], F=_MODMF_(CONCAT(_DEF_SMTP_MAILER_FLAGS, `a%', SMTP_MAILER_FLAGS), `SMTP'), S=EnvFromSMTP/HdrFromSMTP, R=ifdef(`_ALL_MASQUERADE_', `EnvToSMTP/HdrFromSMTP', `EnvToSMTP'), E=\r\n, L=990, - _OPTINS(`SMTP_MAILER_MAX', `M=', `, ')_OPTINS(`SMTP_MAILER_MAXMSGS', `m=', `, ')_OPTINS(`SMTP_MAILER_MAXRCPTS', `r=', `, ')_OPTINS(`SMTP_MAILER_CHARSET', `C=', `, ')T=DNS/RFC822/SMTP, +Mdsmtp, P=[IPC], F=_MODMF_(CONCAT(_DEF_SMTP_MAILER_FLAGS, `a%', SMTP_MAILER_FLAGS), `DSMTP'), S=EnvFromSMTP/HdrFromSMTP, R=ifdef(`_ALL_MASQUERADE_', `EnvToSMTP/HdrFromSMTP', `EnvToSMTP'), E=\r\n, L=990, + _OPTINS(`SMTP_MAILER_MAX', `M=', `, ')_OPTINS(`SMTP_MAILER_MAXMSGS', `m=', `, ')_OPTINS(`SMTP_MAILER_MAXRCPTS', `r=', `, ')_OPTINS(`SMTP_MAILER_CHARSET', `C=', `, ')T=DNS/RFC822/SMTP,_DSMTP_QGRP A=DSMTP_MAILER_ARGS Mrelay, P=[IPC], F=_MODMF_(CONCAT(_DEF_SMTP_MAILER_FLAGS, `a8', RELAY_MAILER_FLAGS), `RELAY'), S=EnvFromSMTP/HdrFromSMTP, R=ifdef(`_ALL_MASQUERADE_', `MasqSMTP/MasqRelay', `MasqSMTP'), E=\r\n, L=2040, - _OPTINS(`RELAY_MAILER_CHARSET', `C=', `, ')_OPTINS(`RELAY_MAILER_MAXMSGS', `m=', `, ')_OPTINS(`SMTP_MAILER_MAXRCPTS', `r=', `, ')T=DNS/RFC822/SMTP, + _OPTINS(`RELAY_MAILER_CHARSET', `C=', `, ')_OPTINS(`RELAY_MAILER_MAXMSGS', `m=', `, ')_OPTINS(`SMTP_MAILER_MAXRCPTS', `r=', `, ')T=DNS/RFC822/SMTP,_RELAY_QGRP A=RELAY_MAILER_ARGS diff --git a/gnu/usr.sbin/sendmail/cf/mailer/usenet.m4 b/gnu/usr.sbin/sendmail/cf/mailer/usenet.m4 index 6c0c13833a0..3a981dff5ee 100644 --- a/gnu/usr.sbin/sendmail/cf/mailer/usenet.m4 +++ b/gnu/usr.sbin/sendmail/cf/mailer/usenet.m4 @@ -1,6 +1,6 @@ PUSHDIVERT(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -12,19 +12,17 @@ PUSHDIVERT(-1) # # -ifdef(`_MAILER_local_', `', - `errprint(`*** MAILER(`local') must appear before MAILER(`usenet')')')dnl - ifdef(`USENET_MAILER_PATH',, `define(`USENET_MAILER_PATH', /usr/lib/news/inews)') _DEFIFNOT(`USENET_MAILER_FLAGS', `rsDFMmn') ifdef(`USENET_MAILER_ARGS',, `define(`USENET_MAILER_ARGS', `inews -m -h -n')') +define(`_USENET_QGRP', `ifelse(defn(`USENET_MAILER_QGRP'),`',`', ` Q=USENET_MAILER_QGRP,')')dnl POPDIVERT #################################### ### USENET Mailer specification ### #################################### -VERSIONID(`$Sendmail: usenet.m4,v 8.19 1999/11/16 03:33:04 gshapiro Exp $') +VERSIONID(`$Sendmail: usenet.m4,v 8.21 2000/10/26 02:08:19 ca Exp $') Musenet, P=USENET_MAILER_PATH, F=_MODMF_(USENET_MAILER_FLAGS, `USENET'), S=EnvFromL, R=EnvToL, - _OPTINS(`USENET_MAILER_MAX', `M=', `, ')T=X-Usenet/X-Usenet/X-Unix, + _OPTINS(`USENET_MAILER_MAX', `M=', `, ')T=X-Usenet/X-Usenet/X-Unix,USENET_MAILER_QGRP A=USENET_MAILER_ARGS $u diff --git a/gnu/usr.sbin/sendmail/cf/mailer/uucp.m4 b/gnu/usr.sbin/sendmail/cf/mailer/uucp.m4 index 9ea08b3a3d4..d772c702c54 100644 --- a/gnu/usr.sbin/sendmail/cf/mailer/uucp.m4 +++ b/gnu/usr.sbin/sendmail/cf/mailer/uucp.m4 @@ -1,6 +1,6 @@ PUSHDIVERT(-1) # -# Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -11,8 +11,6 @@ PUSHDIVERT(-1) # the sendmail distribution. # # -ifdef(`_MAILER_smtp_', `', - `errprint(`*** MAILER(`smtp') must appear before MAILER(`uucp')')')dnl ifdef(`UUCP_MAILER_PATH',, `define(`UUCP_MAILER_PATH', /usr/bin/uux)') ifdef(`UUCP_MAILER_ARGS',, `define(`UUCP_MAILER_ARGS', `uux - -r -a$g -gC $h!rmail ($u)')') @@ -20,17 +18,18 @@ _DEFIFNOT(`UUCP_MAILER_FLAGS', `') ifdef(`UUCP_MAILER_MAX',, `define(`UUCP_MAILER_MAX', `ifdef(`UUCP_MAX_SIZE', `UUCP_MAX_SIZE', 100000)')') +define(`_UUCP_QGRP', `ifelse(defn(`UUCP_MAILER_QGRP'),`',`', ` Q=UUCP_MAILER_QGRP,')')dnl POPDIVERT ##################################### ### UUCP Mailer specification ### ##################################### -VERSIONID(`$Sendmail: uucp.m4,v 8.38 1999/10/18 04:57:55 gshapiro Exp $') +VERSIONID(`$Sendmail: uucp.m4,v 8.44 2001/08/24 19:49:08 ca Exp $') # # envelope and header sender rewriting # -SFromU=12 +SFromU # handle error address as a special case R<@> $n errors to mailer-daemon @@ -52,7 +51,7 @@ R! $+ $: $k ! $1 in case $U undefined # # envelope recipient rewriting # -SEnvToU=22 +SEnvToU # list:; should disappear R:; <@> $@ @@ -67,7 +66,7 @@ R$* < @ $+ > $2 ! $1 convert to UUCP format # # header recipient rewriting # -SHdrToU=42 +SHdrToU # list:; syntax should disappear R:; <@> $@ @@ -88,7 +87,7 @@ ifdef(`_MAILER_smtp_', `# # envelope sender rewriting for uucp-dom mailer # -SEnvFromUD=52 +SEnvFromUD # handle error address as a special case R<@> $n errors to mailer-daemon @@ -99,7 +98,7 @@ R$* $@ $>EnvFromSMTP $1 # # envelope sender rewriting for uucp-uudom mailer # -SEnvFromUUD=72 +SEnvFromUUD # handle error address as a special case R<@> $n errors to mailer-daemon @@ -111,8 +110,10 @@ R$* < @ $* . > $* $1 < @ $2 > $3 strip trailing dots R<@ $- . UUCP > : $+ $@ $1 ! $2 convert to UUCP format R<@ $+ > : $+ $@ $1 ! $2 convert to UUCP format R$* < @ $- . UUCP > $@ $2 ! $1 convert to UUCP format -R$* < @ $+ > $@ $2 ! $1 convert to UUCP format') - +R$* < @ $+ > $@ $2 ! $1 convert to UUCP format', +`errprint(`*** MAILER(`smtp') must appear before MAILER(`uucp') + if uucp-dom should be included.') +') PUSHDIVERT(4) # resolve locally connected UUCP links @@ -128,29 +129,29 @@ POPDIVERT # old UUCP mailer (two names) Muucp, P=UUCP_MAILER_PATH, F=_MODMF_(CONCAT(`DFMhuUd', UUCP_MAILER_FLAGS), `UUCP'), S=FromU, R=EnvToU/HdrToU, - M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix, + M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix,_UUCP_QGRP A=UUCP_MAILER_ARGS Muucp-old, P=UUCP_MAILER_PATH, F=_MODMF_(CONCAT(`DFMhuUd', UUCP_MAILER_FLAGS), `UUCP'), S=FromU, R=EnvToU/HdrToU, - M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix, + M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix,_UUCP_QGRP A=UUCP_MAILER_ARGS # smart UUCP mailer (handles multiple addresses) (two names) Msuucp, P=UUCP_MAILER_PATH, F=_MODMF_(CONCAT(`mDFMhuUd', UUCP_MAILER_FLAGS), `UUCP'), S=FromU, R=EnvToU/HdrToU, - M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix, + M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix,_UUCP_QGRP A=UUCP_MAILER_ARGS Muucp-new, P=UUCP_MAILER_PATH, F=_MODMF_(CONCAT(`mDFMhuUd', UUCP_MAILER_FLAGS), `UUCP'), S=FromU, R=EnvToU/HdrToU, - M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix, + M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix,_UUCP_QGRP A=UUCP_MAILER_ARGS ifdef(`_MAILER_smtp_', `# domain-ized UUCP mailer Muucp-dom, P=UUCP_MAILER_PATH, F=_MODMF_(CONCAT(`mDFMhud', UUCP_MAILER_FLAGS), `UUCP'), S=EnvFromUD/HdrFromSMTP, R=ifdef(`_ALL_MASQUERADE_', `EnvToSMTP/HdrFromSMTP', `EnvToSMTP'), - M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix, + M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix,_UUCP_QGRP A=UUCP_MAILER_ARGS # domain-ized UUCP mailer with UUCP-style sender envelope Muucp-uudom, P=UUCP_MAILER_PATH, F=_MODMF_(CONCAT(`mDFMhud', UUCP_MAILER_FLAGS), `UUCP'), S=EnvFromUUD/HdrFromSMTP, R=ifdef(`_ALL_MASQUERADE_', `EnvToSMTP/HdrFromSMTP', `EnvToSMTP'), - M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix, + M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix,_UUCP_QGRP A=UUCP_MAILER_ARGS') diff --git a/gnu/usr.sbin/sendmail/cf/ostype/aix5.m4 b/gnu/usr.sbin/sendmail/cf/ostype/aix5.m4 index fae7533435f..f982f432955 100644 --- a/gnu/usr.sbin/sendmail/cf/ostype/aix5.m4 +++ b/gnu/usr.sbin/sendmail/cf/ostype/aix5.m4 @@ -10,7 +10,7 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: aix5.m4,v 1.1.2.1 2000/12/09 03:32:08 ca Exp $') +VERSIONID(`$Sendmail: aix5.m4,v 1.1 2000/12/08 21:53:36 ca Exp $') ifdef(`LOCAL_MAILER_PATH',, `define(`LOCAL_MAILER_PATH', /bin/bellmail)')dnl ifdef(`LOCAL_MAILER_ARGS',, `define(`LOCAL_MAILER_ARGS', mail -F $g $u)')dnl _DEFIFNOT(`LOCAL_MAILER_FLAGS', `mn9')dnl diff --git a/gnu/usr.sbin/sendmail/cf/ostype/darwin.m4 b/gnu/usr.sbin/sendmail/cf/ostype/darwin.m4 index 9dcf28aeab1..094a4646940 100644 --- a/gnu/usr.sbin/sendmail/cf/ostype/darwin.m4 +++ b/gnu/usr.sbin/sendmail/cf/ostype/darwin.m4 @@ -11,7 +11,7 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: darwin.m4,v 8.1.2.1 2000/06/15 06:37:04 gshapiro Exp $') +VERSIONID(`$Sendmail: darwin.m4,v 8.1 2000/06/15 06:36:30 gshapiro Exp $') ifdef(`STATUS_FILE',, `define(`STATUS_FILE', `/var/log/sendmail.st')')dnl ifdef(`LOCAL_MAILER_PATH',, `define(`LOCAL_MAILER_PATH', /usr/libexec/mail.local)')dnl ifdef(`UUCP_MAILER_ARGS',, `define(`UUCP_MAILER_ARGS', `uux - -r -z -a$g $h!rmail ($u)')')dnl diff --git a/gnu/usr.sbin/sendmail/cf/ostype/linux.m4 b/gnu/usr.sbin/sendmail/cf/ostype/linux.m4 index 0e3abf17be1..839eff4dcc4 100644 --- a/gnu/usr.sbin/sendmail/cf/ostype/linux.m4 +++ b/gnu/usr.sbin/sendmail/cf/ostype/linux.m4 @@ -13,7 +13,7 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: linux.m4,v 8.11.16.2 2000/09/17 17:04:22 gshapiro Exp $') +VERSIONID(`$Sendmail: linux.m4,v 8.13 2000/09/17 17:30:00 gshapiro Exp $') define(`confEBINDIR', `/usr/sbin') ifdef(`PROCMAIL_MAILER_PATH',, define(`PROCMAIL_MAILER_PATH', `/usr/bin/procmail')) diff --git a/gnu/usr.sbin/sendmail/cf/ostype/mklinux.m4 b/gnu/usr.sbin/sendmail/cf/ostype/mklinux.m4 index d4e2ed8e315..cbd3e62e649 100644 --- a/gnu/usr.sbin/sendmail/cf/ostype/mklinux.m4 +++ b/gnu/usr.sbin/sendmail/cf/ostype/mklinux.m4 @@ -15,7 +15,7 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: mklinux.m4,v 8.14.4.1 2000/05/09 18:48:58 gshapiro Exp $') +VERSIONID(`$Sendmail: mklinux.m4,v 8.15 2000/05/09 18:48:56 gshapiro Exp $') define(`confEBINDIR', `/usr/sbin') ifdef(`STATUS_FILE',, `define(`STATUS_FILE', `/var/log/sendmail.st')') diff --git a/gnu/usr.sbin/sendmail/cf/ostype/solaris8.m4 b/gnu/usr.sbin/sendmail/cf/ostype/solaris8.m4 index 2897167bd18..8add7856337 100644 --- a/gnu/usr.sbin/sendmail/cf/ostype/solaris8.m4 +++ b/gnu/usr.sbin/sendmail/cf/ostype/solaris8.m4 @@ -15,7 +15,7 @@ divert(-1) # divert(0) -VERSIONID(`$Sendmail: solaris8.m4,v 8.1.2.2 2000/08/23 16:10:01 gshapiro Exp $') +VERSIONID(`$Sendmail: solaris8.m4,v 8.2 2000/08/23 16:10:49 gshapiro Exp $') divert(-1) ifdef(`UUCP_MAILER_ARGS',, `define(`UUCP_MAILER_ARGS', `uux - -r -a$g $h!rmail ($u)')') diff --git a/gnu/usr.sbin/sendmail/contrib/README b/gnu/usr.sbin/sendmail/contrib/README index c211ca1e9ff..67ddc63f803 100644 --- a/gnu/usr.sbin/sendmail/contrib/README +++ b/gnu/usr.sbin/sendmail/contrib/README @@ -7,4 +7,4 @@ care -- some of the patches may be out of date with the latest release of sendmail. Also, the previous comment applies -- patches belong to the original author, not to us. -$Revision: 1.3 $, Last updated $Date: 2001/01/15 21:09:00 $ +$Revision: 1.4 $, Last updated $Date: 2001/09/11 19:02:48 $ diff --git a/gnu/usr.sbin/sendmail/contrib/buildvirtuser b/gnu/usr.sbin/sendmail/contrib/buildvirtuser index 5b3c872e789..da606c69bdf 100644 --- a/gnu/usr.sbin/sendmail/contrib/buildvirtuser +++ b/gnu/usr.sbin/sendmail/contrib/buildvirtuser @@ -27,7 +27,7 @@ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. -# $Sendmail: buildvirtuser,v 1.1.2.3 2001/02/12 02:57:13 gshapiro Exp $ +# $Sendmail: buildvirtuser,v 1.3 2001/02/12 02:58:20 gshapiro Exp $ =head1 NAME diff --git a/gnu/usr.sbin/sendmail/contrib/link_hash.sh b/gnu/usr.sbin/sendmail/contrib/link_hash.sh index d55799fa63d..44924c4f489 100644 --- a/gnu/usr.sbin/sendmail/contrib/link_hash.sh +++ b/gnu/usr.sbin/sendmail/contrib/link_hash.sh @@ -3,7 +3,7 @@ ## Copyright (c) 2000 Sendmail, Inc. and its suppliers. ## All rights reserved. ## -## $Sendmail: link_hash.sh,v 1.1.2.1 2000/04/25 00:10:47 ca Exp $ +## $Sendmail: link_hash.sh,v 1.2 2000/04/25 00:12:28 ca Exp $ ## # # ln a certificate to its hash diff --git a/gnu/usr.sbin/sendmail/contrib/qtool.8 b/gnu/usr.sbin/sendmail/contrib/qtool.8 index bc37cf7a25d..17affe5c08f 100644 --- a/gnu/usr.sbin/sendmail/contrib/qtool.8 +++ b/gnu/usr.sbin/sendmail/contrib/qtool.8 @@ -6,9 +6,9 @@ .\" the sendmail distribution. .\" .\" -.\" $Sendmail: qtool.8,v 8.9.16.2 2000/12/15 19:50:41 gshapiro Exp $ +.\" $Sendmail: qtool.8,v 8.12 2000/12/15 19:53:35 gshapiro Exp $ .\" -.TH QTOOL 8 "$Date: 2001/01/15 21:09:00 $" +.TH QTOOL 8 "$Date: 2001/09/11 19:02:48 $" .SH NAME qtool \- manipulate sendmail queues @@ -17,7 +17,7 @@ qtool .RB [options] target_directory source [source ...] .PP -.B qtool.pl [-d/-b] +.B qtool.pl [-d|-b] .RB [options] source [source ...] .SH DESCRIPTION @@ -42,6 +42,10 @@ running sendmail with the -OTimeout.queuereturn=now option. \fB\-b\fP Bounce all of the messages specified by source. The messages will be bounced immediately. No attempt will be made to deliver the messages. +.TP +\fB\-C\fP configfile +Specify the sendmail config file. +Defaults to /etc/mail/sendmail.cf. .TP \fB\-d\fP Delete all of the messages specified by source. @@ -191,12 +195,15 @@ Moves the message with id d6CLQh100847 in queue q1 to queue q2. \fBqtool.pl q2 q1/qfd6CLQh100847\fP Moves the message with id d6CLQh100847 in queue q1 to queue q2. .TP -\fBqtool.pl q2 q1/dfd6CLQh100847\fP -Moves the message with id d6CLQh100847 in queue q1 to queue q2. -.TP \fBqtool.pl -e '$msg{num_delivery_attempts} == 3' /q2 /q1\fP Moves all of the queue files that have had three attempted deliveries from queue q1 to queue q2. +.SH BUGS +In sendmail 8.12, it is possible for a message's qf and df files +to be stored in different queues. +In this situation, you must give qtool the pathname of the qf file, +not of the df file. +To be safe, never feed qtool the pathname of a df file. .SH SEE ALSO sendmail(8) .SH HISTORY diff --git a/gnu/usr.sbin/sendmail/contrib/qtool.pl b/gnu/usr.sbin/sendmail/contrib/qtool.pl index 3f819bc9ed8..0f81449a946 100644 --- a/gnu/usr.sbin/sendmail/contrib/qtool.pl +++ b/gnu/usr.sbin/sendmail/contrib/qtool.pl @@ -3,7 +3,7 @@ ## Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. ## All rights reserved. ## -## $Sendmail: qtool.pl,v 8.15.16.4 2000/11/30 07:14:01 gshapiro Exp $ +## $Sendmail: qtool.pl,v 8.20 2000/12/05 16:10:07 dmoen Exp $ ## use strict; use File::Basename; @@ -63,7 +63,7 @@ my $action; my $new_condition; my $conditions = new Compound(); -Getopt::Std::getopts('bde:s:', \%opts); +Getopt::Std::getopts('bC:de:s:', \%opts); sub move_action { @@ -126,6 +126,37 @@ if ($action == \&move_action) $destination = new Queue($dst_name); } +# determine queue_root by reading config file +my $queue_root; +{ + my $config_file = "/etc/mail/sendmail.cf"; + if (defined $opts{C}) + { + $config_file = $opts{C}; + } + + my $line; + open(CONFIG_FILE, $config_file) or die "$config_file: $!"; + while ($line = <CONFIG_FILE>) + { + chomp $line; + if ($line =~ m/^O QueueDirectory=(.*)/) + { + $queue_root = $1; + if ($queue_root =~ m/(.*)\/[^\/]+\*$/) + { + $queue_root = $1; + } + last; + } + } + close(CONFIG_FILE); + if (!defined $queue_root) + { + die "QueueDirectory option not defined in $config_file"; + } +} + while (@ARGV) { $source_name = shift(@ARGV); @@ -161,6 +192,7 @@ sub usage print(" $0 [-d|-b] source ...\n"); print("options:\n"); print(" -b Bounce the messages specified by source.\n"); + print(" -C configfile Specify sendmail config file.\n"); print(" -d Delete the messages specified by source.\n"); print(" -e [perl expression] Move only messages for which perl expression returns true.\n"); print(" -s [seconds] Move only messages whose qf file is older than seconds.\n"); @@ -222,6 +254,10 @@ sub add_source return "'$source_name' does not exist"; } $data_dir_name = File::Spec->catfile("$source_dir_name", "df"); + if (!-d $data_dir_name) + { + $data_dir_name = $source_dir_name; + } $source_dir_name = File::Spec->catfile("$source_dir_name", "qf"); } @@ -402,6 +438,7 @@ sub parse 'B' => 'body_type', 'C' => 'controlling_user', 'D' => 'data_file_name', + 'd' => 'data_file_directory', 'E' => 'error_recipient', 'F' => 'flags', 'H' => 'parse_header', @@ -621,10 +658,21 @@ sub new sub initialize { my $self = shift; - my $queue_dir = shift; + my $data_dir = shift; $self->{id} = shift; - - $self->{file_name} = $queue_dir . '/df' . $self->{id}; + my $control_file = shift; + + $self->{file_name} = $data_dir . '/df' . $self->{id}; + return if -e $self->{file_name}; + $control_file->parse(); + return if !defined $control_file->{data_file_directory}; + $data_dir = $queue_root . '/' . $control_file->{data_file_directory}; + chomp $data_dir; + if (-d ($data_dir . '/df')) + { + $data_dir .= '/df'; + } + $self->{file_name} = $data_dir . '/df' . $self->{id}; } sub do_stat @@ -694,14 +742,11 @@ sub initialize $self->{id} = $id; $self->{control_file} = new ControlFile($queue_dir, $id); - if ($data_dir) - { - $self->{data_file} = new DataFile($data_dir, $id); - } - else + if (!$data_dir) { - $self->{data_file} = new DataFile($queue_dir, $id); + $data_dir = $queue_dir; } + $self->{data_file} = new DataFile($data_dir, $id, $self->{control_file}); } sub last_modified_time diff --git a/gnu/usr.sbin/sendmail/doc/op/op.me b/gnu/usr.sbin/sendmail/doc/op/op.me index 60b7b92df61..96bd17402ec 100644 --- a/gnu/usr.sbin/sendmail/doc/op/op.me +++ b/gnu/usr.sbin/sendmail/doc/op/op.me @@ -9,7 +9,7 @@ .\" the sendmail distribution. .\" .\" -.\" $Sendmail: op.me,v 8.317.4.71 2001/08/14 15:26:00 ca Exp $ +.\" $Sendmail: op.me,v 8.569 2001/09/08 01:20:50 gshapiro Exp $ .\" .\" eqn op.me | pic | troff -me .\" @@ -81,16 +81,17 @@ This documentation is under modification. .sp .r Eric Allman +Gregory Neil Shapiro +Claus Assmann Sendmail, Inc. -eric@Sendmail.COM .sp .de Ve Version \\$2 .. -.Ve $Revision: 1.8 $ +.Ve $Revision: 1.9 $ .rm Ve .sp -For Sendmail Version 8.11 +For Sendmail Version 8.12 .)l .(f Sendmail is a trademark of Sendmail, Inc. @@ -130,6 +131,7 @@ RFC1123 (Internet Host Requirements), RFC2045 (MIME), RFC1869 (SMTP Service Extensions), RFC1652 (SMTP 8BITMIME Extension), +RFC1854 (SMTP Service Extension for Command Pipelining), RFC1870 (SMTP SIZE Extension), RFC1891 (SMTP Delivery Status Notifications), RFC1892 (Multipart/Report), @@ -140,8 +142,12 @@ RFC2033 (Local Message Transmission Protocol), RFC2034 (SMTP Service Extension for Returning Enhanced Error Codes), RFC2476 (Message Submission), RFC2487 (SMTP Service Extension for Secure SMTP over TLS), +RFC2554 (SMTP Service Extension for Authentication), +RFC2821 (Simple Mail Transfer Protocol), +RFC2822 (Internet Message Format), +RFC2852 (Deliver By SMTP Service Extension), and -RFC2554 (SMTP Service Extension for Authentication). +RFC2920 (SMTP Service Extension for Command Pipelining), However, since .i sendmail is designed to work in a wider world, @@ -169,9 +175,9 @@ for you to install .i sendmail and keep it happy. Section three -describes some parameters that may be safely tweaked. -Section four has information regarding the command line arguments. +Section four +describes some parameters that may be safely tweaked. Section five contains the nitty-gritty information about the configuration file. @@ -203,6 +209,11 @@ and the settings of various options. Although the configuration file can be quite complex, a configuration can usually be built using an M4-based configuration language. +Assuming you have the standard +.i sendmail +distribution, see +.i cf/README +for further information. .pp The remainder of this section will describe the installation of .i sendmail @@ -214,10 +225,11 @@ are given from the root of the subtree, normally .i /usr/src/usr.\*(SD/sendmail -on 4.4BSD. +on 4.4BSD-based systems. .pp -If you are loading this off the tape, -continue with the next section. +Continue with the next section if you need/want to compile +.i sendmail +yourself. If you have a running binary already on your system, you should probably skip to section 1.2. .sh 2 "Compiling Sendmail" @@ -248,6 +260,8 @@ command. In most cases these are only used when the .i obj.* directory is first created. +To restart from scratch, use +.i -c . These commands include: .nr ii 0.5i .ip "\-L \fIlibdirs\fP" @@ -285,15 +299,53 @@ will avoid auto-detecting libraries if this is set. All libraries and map definitions must be specified in the site configuration file. .lp -Any other parameters are passed to the +Most other parameters are passed to the .i make -program. +program; for details see +.i $BUILDTOOLS/README . .sh 3 "Creating a Site Configuration File" .\"XXX .pp (This section is not yet complete. For now, see the file devtools/README for details.) See sendmail/README for various compilation flags that can be set. +.sh 3 "Notes About Some Configuration Settings" +.\"XXX +.pp +Not all configuration setting are critical to getting sendmail to run +correctly. Note that "correctly" does not directly imply highest +performance immediately. +.pp +.i Sendmail +uses the functions +.i strlcat \|(3) +and +.i strlcpy \|(3) +which, at the time of writing, are not widely available. +Further, for those systems where +these functions are available they are new enough that performance +tuning has not yet occurred. +.i Sendmail +includes in its sm library (aka +.i libsm ) +these functions with an sm_ prefix. By default sendmail uses the +.i libsm +versions of these functions as performance tuning has occurred. +A performance testing program +.i libsm/b-strl.c +can be used to evaluate which versions of the functions are faster: +.i libsm +or the system's +.i libc . +If you decide to use the +.i libc +versions then add +.(b +-DSM_CONF_STRL=0 +.)b +as compile time option, see +.i $BUILDTOOLS/README +for details. .sh 3 "Tweaking the Makefile" .pp .\" .b "XXX This should all be in the Site Configuration File section." @@ -343,6 +395,8 @@ If neither of these are defined, reads the alias file into memory on every invocation. This can be slow and should be avoided. There are also several methods for remote database access: +.ip LDAP +Lightweight Directory Access Protocol. .ip NIS Sun's Network Information Services (formerly YP). .ip NISPLUS @@ -352,9 +406,12 @@ NeXT's NetInfo service. .ip HESIOD Hesiod service (from Athena). .lp -Other compilation flags are set in conf.h +Other compilation flags are set in +.i conf.h and should be predefined for you unless you are porting to a new environment. +For more options see +.i sendmail/README . .sh 3 "Compilation and installation" .pp After making the local system configuration described above, @@ -382,7 +439,14 @@ and /usr/\*(SB/mailq to /usr/\*(SD/sendmail. -On 4.4BSD systems it will also format and install man pages. +On most systems it will also format and install man pages. +Notice: as of version 8.12 +.i sendmail +will no longer be installed set-user-ID root by default. +If you really want to use the old method, you can specify it as target: +.(b +\&./Build install-set-user-id +.)b .sh 2 "Configuration Files" .pp .i Sendmail @@ -401,24 +465,9 @@ The world is complex, and the mail configuration reflects that. The distribution includes an m4-based configuration package that hides a lot of the complexity. -.pp -These configuration files are simpler than old versions -largely because the world has become simpler; -in particular, -text-based host files are officially eliminated, -obviating the need to -.q hide -hosts behind a registered internet gateway. -.pp -These files also assume that most of your neighbors -use domain-based UUCP addressing; -that is, -instead of naming hosts as -.q host!user -they will use -.q host.domain!user . -The configuration files can be customized to work around this, -but it is more complex. +See +.i cf/README +for details. .pp Our configuration files are processed by .i m4 @@ -444,7 +493,7 @@ as a general description of an SMTP-connected host running Solaris 2.x. Files ending .b \&.mc -(``Master Configuration'') +(``M4 Configuration'') are the input descriptions; the output is in the corresponding .b \&.cf @@ -521,6 +570,7 @@ Local UUCP connectivity information. This directory has been supplanted by the mailertable feature; any new configurations should use that feature to do UUCP (and other) routing. +The use of this directory is deprecated. .pp If you are in a new domain (e.g., a company), @@ -580,7 +630,8 @@ many systems install it in I understand it is in /usr/ucblib on System V Release 4. .)f -It should be setuid root. +It should be set-group-ID smmsp as described in +sendmail/SECURITY. For security reasons, /, /usr, and /usr/\*(SD should be owned by root, mode 755\**. @@ -594,7 +645,7 @@ and permissions are .)f .sh 3 "/etc/mail/sendmail.cf" .pp -This is the configuration file for +This is the main configuration file for .i sendmail \**. .(f \**Actually, the pathname varies depending on the operating system; @@ -609,8 +660,9 @@ to the flags passed to the C compiler. Moving this file is not recommended: other programs and scripts know of this location. .)f -This is the only non-library file name compiled into -.i sendmail \**. +This is one of the two non-library file names compiled into +.i sendmail \**, +the other is /etc/mail/submit.cf. .(f \**The system libraries can reference other files; in particular, system library subroutines that @@ -627,6 +679,32 @@ If you have a particularly unusual system configuration you may need to create a special version. The format of this file is detailed in later sections of this document. +.sh 3 "/etc/mail/submit.cf" +.pp +This is the configuration file for +.i sendmail +when it is used for initial mail submission, in which case +it is also called ``Mail Submission Program'' (MSP) +in contrast to ``Mail Transfer Agent'' (MTA). +Starting with version 8.12, +.i sendmail +uses one of two different configuration files based on its operation mode +(or the new +.b \-A +option). +For initial mail submission, i.e., if one of the options +.b \-bm +(default), +.b \-bs , +or +.b \-t +is specified, submit.cf is used (if available), +for other operations sendmail.cf is used. +Details can be found in +.i sendmail/SECURITY . +submit.cf is shipped with sendmail (in cf/cf/) and is installed by default. +If changes to the configuration need to be made, start with +cf/cf/submit.mc and follow the instruction in cf/README. .sh 3 "/usr/\*(SB/newaliases" .pp The @@ -672,7 +750,7 @@ and owned by root. The actual path of this directory is defined in the .b Q -option of the +keyword of the .i sendmail.cf file. To use multiple queues, @@ -692,6 +770,26 @@ queue file types. That is, the data files are stored in the `df' subdirectory, the transcript files are stored in the `xf' subdirectory, and all others are stored in the `qf' subdirectory. +.pp +If shared memory support is compiled in, +.i sendmail +stores the available diskspace in a shared memory segment +to make the values readily available to all children without +incurring system overhead. +In this case, only the daemon updates the data; +i.e., the sendmail daemon creates the shared memory segment +and deletes it if it is terminated. +To use this, +.i sendmail +must have been compiled with support for shared memory +(-DSM_CONF_SHM) +and the option +.b SharedMemoryKey +must be set. +Notice: do not use the same key for +.i sendmail +invocations with different queue directories +or different queue group declarations. .sh 3 "/var/spool/mqueue/.hoststat" .pp This is a typical value for the @@ -711,7 +809,7 @@ which includes some aliases which .i must be defined: .(b -cp lib/aliases /etc/mail/aliases +cp sendmail/aliases /etc/mail/aliases .i "edit /etc/mail/aliases" .)b You should extend this file with any aliases that are apropos to your system. @@ -743,7 +841,7 @@ it listens on the SMTP socket for connections and it processes the queue periodically to insure that mail gets delivered when hosts come up. .pp -Add the following lines to +If necessary, add the following lines to .q /etc/rc (or .q /etc/rc.local @@ -755,7 +853,7 @@ in one of the startup files, typically .q /etc/init.d/sendmail : .(b if [ \-f /usr/\*(SD/sendmail \-a \-f /etc/mail/sendmail.cf ]; then - (cd /var/spool/mqueue; rm \-f [lnx]f*) + (cd /var/spool/mqueue; rm \-f xf*) /usr/\*(SD/sendmail \-bd \-q30m & echo \-n ' sendmail' >/dev/console fi @@ -764,8 +862,8 @@ The .q cd and .q rm -commands insure that all lock files have been removed; -extraneous lock files may be left around +commands insure that all transcript files have been removed; +extraneous transcript files may be left around if the system goes down in the middle of processing a message. The line that actually invokes .i sendmail @@ -834,12 +932,6 @@ done Figure 1 \(em A complex startup script .hl .)z -.pp -If you are not running a version of UNIX -that supports Berkeley TCP/IP, -do not include the -.b \-bd -flag. .sh 3 "/etc/mail/helpfile" .pp This is the help file used by the SMTP @@ -944,10 +1036,10 @@ The number of envelope recipients for this message The message id of the message (from the header). .ip proto The protocol used to receive this message (e.g., ESMTP or UUCP) -.ip daemon -The daemon name from the -.b DaemonPortOptions -setting. +.ip daemon +The daemon name from the +.b DaemonPortOptions +setting. .ip relay The machine from which it was received. .lp @@ -976,7 +1068,7 @@ The enhanced error code (RFC2034) if available. The delivery status. .lp Not all fields are present in all messages; -for example, the relay is not listed for local deliveries. +for example, the relay is usually not listed for local deliveries. .sh 3 "Levels" .pp If you have @@ -1015,13 +1107,103 @@ signal. The results are logged at .sm LOG_DEBUG priority. -.sh 2 "The Mail Queue" +.sh 2 "The Mail Queues" .pp -Sometimes a host cannot handle a message immediately. -For example, it may be down or overloaded, causing it to refuse connections. -The sending host is then expected to save this message in -its mail queue -and attempt to deliver it later. +Mail messages may either be delivered immediately or be held for later +delivery. +Held messages are placed into a holding directory called a mail queue. +.pp +A mail message may be queued for these reasons: +.bu +If a mail message is temporarily undeliverable, it is queued +and delivery is attempted later. +If the message is addressed to multiple recipients, it is queued +only for those recipients to whom delivery is not immediately possible. +.bu +If the SuperSafe option is set to true, +all mail messages are queued while delivery is attempted. +.bu +If the DeliveryMode option is set to queue-only or defer, +all mail is queued, and no immediate delivery is attempted. +.bu +If the load average becomes higher than the value of the QueueLA option +and the +.b QueueFactor +(\c +.b q ) +option divided by the difference in the current load average and the +.b QueueLA +option plus one +is less than the priority of the message, +messages are queued rather than immediately delivered. +.sh 3 "Queue Groups and Queue Directories" +.pp +There are one or more mail queues. +Each mail queue belongs to a queue group. +There is always a default queue group that is called ``mqueue'' +(which is where messages go by default unless otherwise specified). +The directory or directories which comprise the default queue group +are specified by the QueueDirectory option. +There are zero or more +additional named queue groups declared using the +.b Q +command in the configuration file. +.pp +By default, a queued message is placed in the queue group +associated with the first recipient in the recipient list. +A recipient address is mapped to a queue group as follows. +First, if there is a ruleset called ``queuegroup'', +and if this ruleset maps the address to a queue group name, +then that queue group is chosen. +That is, the argument for the ruleset is the recipient address +and the result should be +.b $# +followed by the name of a queue group. +Otherwise, if the mailer associated with the address specifies +a queue group, then that queue group is chosen. +Otherwise, the default queue group is chosen. +.pp +A message with multiple recipients will be split +if different queue groups are chosen +by the mapping of recipients to queue groups. +.pp +When a message is placed in a queue group, and the queue group has +more than one queue, a queue is selected randomly. +.pp +If a message with multiple recipients is placed into a queue group +with the 'r' option (maximum number of recipients per message) +set to a positive value +.i N , +and if there are more than +.i N +recipients +in the message, then the message will be split into multiple messages, +each of which have at most +.i N +recipients. +.sh 3 "Queue Runs" +.pp +.i sendmail +has two different ways to process the queue(s). +The first one is to start queue runners after certain intervals +(``normal'' queue runners), +the second one is to keep queue runner processes around +(``persistent'' queue runners). +How to select either of these types is discussed in the appendix +``COMMAND LINE FLAGS''. +Persistent queue runners have the advantage that no new processes +need to be spawned at certain intervals; they just sleep for +a specified time after they finished a queue run. +Their disadvantage is that a new queue run is only started +after all queue runners belonging to a group finished their tasks. +In case one of the queue runners tries delivery to a slow recipient site +at the end of a queue run, the next queue run may be substantially delayed. +In general this should be smoothed out due to the distribution of +those slow jobs, however, for sites with small number of +queue entries this might introduce noticable delays. +In general, persistent queue runners are only useful for +sites with big queues. +.sh 3 "Manual Intervention" .pp Under normal conditions the mail queue will be processed transparently. However, you may find that manual intervention is sometimes necessary. @@ -1032,9 +1214,11 @@ Although .i sendmail ought to recover gracefully when the host comes up, you may find performance unacceptably bad in the meantime. +In that case you want to check the content of the queue +and manipulate it as explained in the next two sections. .sh 3 "Printing the queue" .pp -The contents of the queue can be printed +The contents of the queue(s) can be printed using the .i mailq command @@ -1049,13 +1233,17 @@ This will produce a listing of the queue id's, the size of the message, the date the message entered the queue, and the sender and recipients. +If shared memory support is compiled in, +the flag +.b \-bP +can be used to print the number of entries in the queue(s), +provided a process updates the data. .sh 3 "Forcing the queue" .pp .i Sendmail -should run the queue automatically -at intervals. +should run the queue automatically at intervals. When using multiple queues, -a separate process will be created to +a separate process will by default be created to run each of the queues unless the queue run is initiated by a user with the verbose flag. @@ -1107,17 +1295,34 @@ You should then kill the existing daemon (since it will still be processing in the old queue directory) and create a new daemon. .pp -To run the old mail queue, -run the following command: +To run the old mail queue, issue the following command: .(b -/usr/\*(SD/sendmail \-oQ/var/spool/omqueue \-q +/usr/\*(SD/sendmail \-C /etc/mail/queue.cf \-q .)b The -.b \-oQ -flag specifies an alternate queue directory +.b \-C +flag specifies an alternate configuration file +.b queue.cf +which should refer to the moved queue directory +.(b +O QueueDirectory=/var/spool/omqueue +.)b and the .b \-q flag says to just run every job in the queue. +You can also specify the moved queue directory on the command line +.(b +/usr/\*(SD/sendmail \-oQ/var/spool/omqueue \-q +.)b +but this requires that you do not have +queue groups in the configuration file, +because those are not subdirectories of the moved directory. +See the section about "Queue Group Declaration" for details; +you most likely need a different configuration file to correctly deal +with this problem. +However, a proper configuration of queue groups should avoid +filling up queue directories, so you shouldn't run into +this problem. If you have a tendency toward voyeurism, you can use the .b \-v @@ -1132,7 +1337,7 @@ rmdir /var/spool/omqueue .pp .i Sendmail stores a large amount of information about each remote system it -has connected to in memory. It is now possible to preserve some +has connected to in memory. It is possible to preserve some of this information on disk as well, by using the .b HostStatusDirectory option, so that it may be shared between several invocations of @@ -1241,13 +1446,23 @@ aliases files nis will ask .i sendmail to look for hosts in the Domain Name System first. -If the requested host name is not found, -it tries local files, +If the requested host name is not found, it tries local files, and if that fails it tries NIS. -Similarly, -when looking for aliases -it will try the local files first -followed by NIS. +Similarly, when looking for aliases +it will try the local files first followed by NIS. +.pp +Notice: since +.i sendmail +must access MX records for correct operation, it will use +DNS if it is configured in the +.b ServiceSwitchFile +file. +Hence an entry like +.(b +hosts files dns +.)b +will not avoid DNS lookups even if a host can be found +in /etc/hosts. .pp Service switches are not completely integrated. For example, despite the fact that the host entry listed in the above example @@ -1255,14 +1470,6 @@ specifies to look in NIS, on SunOS this won't happen because the system implementation of .i gethostbyname \|(3) doesn't understand this. -If there is enough demand -.i sendmail -may reimplement -.i gethostbyname \|(3), -.i gethostbyaddr \|(3), -.i getpwent \|(3), -and the other system routines that would be necessary -to make this work seamlessly. .sh 2 "The Alias Database" .pp After recipient addresses are read from the SMTP connection @@ -1422,28 +1629,6 @@ flag: /usr/\*(SD/sendmail \-bi .)b .pp -If the -.b RebuildAliases -(old -.b D ) -option is specified in the configuration, -.i sendmail -will rebuild the alias database automatically -if possible -when it is out of date. -Auto-rebuild can be dangerous -on heavily loaded machines -with large alias files; -if it might take more than the rebuild timeout -(option -.b AliasWait , -old -.b a , -which is normally five minutes) -to rebuild the database, -there is a chance that several processes will start the rebuild process -simultaneously. -.pp If you have multiple aliases databases specified, the .b \-bi @@ -1534,6 +1719,9 @@ of using ``\c as the return address. .sh 2 "User Information Database" .pp +This option is deprecated, use virtusertable and genericstable instead +as explained in +.i cf/README . If you have a version of .i sendmail with the user information database @@ -1582,7 +1770,7 @@ defined by the configuration file. Others have interpretations built into .i sendmail that cannot be changed without changing the code. -These builtins are described here. +These built-ins are described here. .sh 3 "Errors-To:" .pp If errors occur anywhere during processing, @@ -1617,7 +1805,7 @@ One of the possible actions is to add an header line for any recipients it is aware of. .pp The Apparently-To: header is non-standard -and is deprecated. +and is both deprecated and strongly discouraged. .sh 3 "Precedence" .pp The Precedence: header can be used as a crude control of message priority. @@ -1693,27 +1881,32 @@ Some important arguments are described here. .sh 2 "Queue Interval" .pp The amount of time between forking a process -to run through the queue -is defined by the +to run through the queue is defined by the .b \-q flag. If you run with delivery mode set to .b i or .b b -this can be relatively large, -since it will only be relevant +this can be relatively large, since it will only be relevant when a host that was down comes back up. If you run in .b q -mode -it should be relatively short, +mode it should be relatively short, since it defines the maximum amount of time that a message may sit in the queue. (See also the MinQueueAge option.) .pp RFC 1123 section 5.3.1.1 says that this value should be at least 30 minutes (although that probably doesn't make sense if you use ``queue-only'' mode). +.pp +Notice: the meaning of the interval time depends on whether normal +queue runners or persistent queue runners are used. +For the former, it is the time between subsequent starts of a queue run. +For the latter, it is the time sendmail waits after a persistent queue +runner has finished its work to start the next one. +Hence for persistent queue runners this interval should be very low, +typically no more than two minutes. .sh 2 "Daemon Mode" .pp If you allow incoming mail over an IPC connection, @@ -1735,8 +1928,9 @@ flag may be combined in one call: An alternative approach is to invoke sendmail from .i inetd (8) (use the -.b \-bs -flag to ask sendmail to speak SMTP on its standard input and output). +.b \-bs \ \-Am +flags to ask sendmail to speak SMTP on its standard input and output +and to run as MTA). This works and allows you to wrap .i sendmail in a TCP wrapper program, @@ -1764,7 +1958,7 @@ when this is done to watch what happens: .)b .pp You can also limit the jobs to those with a particular queue identifier, -sender, or recipient +recipient, sender, or queue group using one of the queue modifiers. For example, .q \-qRberkeley @@ -1773,40 +1967,77 @@ restricts the queue run to jobs that have the string somewhere in one of the recipient addresses. Similarly, .q \-qSstring -limits the run to particular senders and +limits the run to particular senders, .q \-qIstring -limits it to particular queue identifiers. +limits it to particular queue identifiers, and +.q \-qGstring +limits it to a particular queue group. +You may also place an +.b ! +before the +.b I +or +.b R +or +.b S +to indicate that jobs are limited to not including a particular queue +identifier, recipient or sender. +For example, +.q \-q!Rseattle +limits the queue run to jobs that do not have the string +.q seattle +somewhere in one of the recipient addresses. +Should you need to terminate the queue jobs currently active then a SIGTERM +to the parent of the process (or processes) will cleanly stop the jobs. .sh 2 "Debugging" .pp There are a fairly large number of debug flags built into .i sendmail . -Each debug flag has a number and a level, -where higher levels means to print out more information. +Each debug flag has a category and a level. +Higher levels increase the level of debugging activity; +in most cases, this means to print out more information. The convention is that levels greater than nine are .q absurd, i.e., they print out so much information that you wouldn't normally want to see them except for debugging that particular piece of code. +.pp +A debug category is either an integer, like 42, +or a name, like ANSI. +You can specify a range of numeric debug categories +using the syntax 17-42. +You can specify a set of named debug categories using +a glob pattern like +.q sm_trace_* . +At present, only +.q * +and +.q ? +are supported in these glob patterns. +.pp Debug flags are set using the .b \-d option; the syntax is: .(b -.ta \w'debug-option 'u +.ta \w'debug-categories:M 'u debug-flag: \fB\-d\fP debug-list debug-list: debug-option [ , debug-option ]* -debug-option: debug-range [ . debug-level ] -debug-range: integer | integer \- integer +debug-option: debug-categories [ . debug-level ] +debug-categories: integer | integer \- integer | category-pattern +category-pattern: [a-zA-Z_*?][a-zA-Z0-9_*?]* debug-level: integer .)b where spaces are for reading ease only. For example, .(b -\-d12 Set flag 12 to level 1 -\-d12.3 Set flag 12 to level 3 -\-d3\-17 Set flags 3 through 17 to level 1 -\-d3\-17.4 Set flags 3 through 17 to level 4 +\-d12 Set category 12 to level 1 +\-d12.3 Set category 12 to level 3 +\-d3\-17 Set categories 3 through 17 to level 1 +\-d3\-17.4 Set categories 3 through 17 to level 4 +\-dANSI Set category ANSI to level 1 +\-dsm_trace_*.3 Set all named categories matching sm_trace_* to level 3 .)b For a complete list of the available debug flags you will have to look at the code @@ -1814,6 +2045,10 @@ and the .i TRACEFLAGS file in the sendmail distribution (they are too dynamic to keep this document up to date). +For a list of named debug categories in the sendmail binary, use +.(b +ident /usr/sbin/sendmail | grep Debug +.)b .sh 2 "Changing the Values of Options" .pp Options can be overridden using the @@ -1836,7 +2071,7 @@ the equivalent line using the long option name is .pp Some options have security implications. Sendmail allows you to set these, -but relinquishes its setuid root permissions thereafter\**. +but relinquishes its set-user-ID or set-group-ID permissions thereafter\**. .(f \**That is, it sets its effective uid to the real uid; thus, if you are executing as root, @@ -1864,7 +2099,8 @@ it defaults to in the current directory. .pp .i Sendmail -gives up its setuid root permissions +gives up set-user-ID root permissions +(if it has been installed set-user-ID root) when you use this flag, so it is common to use a publicly writable directory (such as /tmp) as the queue directory (QueueDirectory or Q option) while testing. @@ -1965,8 +2201,8 @@ This version requires that you use: .pp As of version 8.7, some other syntaxes are available in test mode: -.bu -\&.D\|x\|value +.nr ii 1i +.ip \&.D\|x\|value defines macro .i x to have the indicated @@ -1975,18 +2211,47 @@ This is useful when debugging rules that use the .b $& \c .i x syntax. -.bu -\&.C\|c\|value +.ip \&.C\|c\|value adds the indicated .i value to class .i c . -.bu -\&.S\|ruleset +.ip \&=S\|ruleset dumps the contents of the indicated ruleset. -.bu -\-d\|debug-spec +.ip \-d\|debug-spec is equivalent to the command-line flag. +.lp +Version 8.9 introduced more features: +.nr ii 1i +.ip ? +shows a help message. +.ip =M +display the known mailers. +.ip $m +print the value of macro m. +.ip $=c +print the contents of class c. +.ip /mx\ host +returns the MX records for `host'. +.ip /parse\ address +parse address, returning the value of +.i crackaddr , +and the parsed address. +.ip /try\ mailer\ addr +rewrite address into the form it will have when +presented to the indicated mailer. +.ip /tryflags\ flags +set flags used by parsing. The flags can be `H' for +Header or `E' for Envelope, and `S' for Sender or `R' +for Recipient. These can be combined, `HR' sets +flags for header recipients. +.ip /canon\ hostname +try to canonify hostname. +.ip /map\ mapname\ key +look up `key' in the indicated `mapname'. +.ip /quit +quit address test mode. +.lp .sh 2 "Persistent Host Status Information" .pp When @@ -2072,22 +2337,22 @@ w weeks .pp The argument to the .b \-q -flag -specifies how often a sub-daemon will run the queue. -This is typically set to between fifteen minutes -and one hour. -If not set, -or set to zero, +flag specifies how often a sub-daemon will run the queue. +This is typically set to between fifteen minutes and one hour. +If not set, or set to zero, the queue will not be run automatically. RFC 1123 section 5.3.1.1 recommends that this be at least 30 minutes. +Should you need to terminate the queue jobs currently active then a SIGTERM +to the parent of the process (or processes) will cleanly stop the jobs. .sh 3 "Read timeouts" .pp Timeouts all have option names .q Timeout.\fIsuboption\fP . +Most of these control SMTP operations. The recognized .i suboption s, their default values, and the minimum values -allowed by RFC 1123 section 5.3.2 are: +allowed by RFC 2821 section 4.5.3.2 (or RFC 1123 section 5.3.2) are: .nr ii 1i .ip connect The time to wait for an SMTP connection to open @@ -2110,6 +2375,18 @@ The concept is that this should be very short (a few seconds); hosts that are well connected and responsive will thus be serviced immediately. Hosts that are slow will not hold up other deliveries in the initial delivery attempt. +.ip aconnect +[0, unspecified] +The overall timeout waiting for all connection for a single delivery +attempt to succeed. +If 0, no overall limit is applied. +This can be used to restrict the total amount of time trying to connect to +a long list of host that could accept an e-mail for the recipient. +This timeout does not apply to +.b FallbackMXhost , +i.e., if the time is exhausted, the +.b FallbackMXhost +is tried next. .ip initial The wait for the initial 220 greeting message [5m, 5m]. @@ -2162,10 +2439,19 @@ the time to wait for another command. [1h, 5m]. .ip ident\(dd The timeout waiting for a reply to an IDENT query -[30s\**, unspecified]. +[5s\**, unspecified]. .(f \**On some systems the default is zero to turn the protocol off entirely. .)f +.ip lhlo +The wait for a reply to an LMTP LHLO command +[2m, unspecified]. +.ip auth +The timeout for a reply in an SMTP AUTH dialogue +[10m, unspecified]. +.ip starttls +The timeout for a reply to an SMTP STARTTLS command and the TLS handshake +[1h, unspecified]. .ip fileopen\(dd The timeout for opening .forward and :include: files [60s, none]. .ip control\(dd @@ -2175,7 +2461,7 @@ How long status information about a host (e.g., host down) will be cached before it is considered stale [30m, unspecified]. -.ip resolver.retrans +.ip resolver.retrans\(dd The resolver's retransmission time interval (in seconds) @@ -2184,21 +2470,21 @@ Sets both .i Timeout.resolver.retrans.first and .i Timeout.resolver.retrans.normal . -.ip resolver.retrans.first +.ip resolver.retrans.first\(dd The resolver's retransmission time interval (in seconds) for the first attempt to deliver a message [varies]. -.ip resolver.retrans.normal +.ip resolver.retrans.normal\(dd The resolver's retransmission time interval (in seconds) for all resolver lookups except the first delivery attempt [varies]. -.ip resolver.retry +.ip resolver.retry\(dd The number of times to retransmit a resolver query. Sets both @@ -2206,13 +2492,13 @@ Sets both and .i Timeout.resolver.retry.normal [varies]. -.ip resolver.retry.first +.ip resolver.retry.first\(dd The number of times to retransmit a resolver query for the first attempt to deliver a message [varies]. -.ip resolver.retry.normal +.ip resolver.retry.normal\(dd The number of times to retransmit a resolver query for all resolver lookups @@ -2230,32 +2516,6 @@ All but those marked with .DD (\(dd) apply to client SMTP. .pp -Many of the RFC 1123 minimum values -may well be too short. -.i Sendmail -was designed to the RFC 822 protocols, -which did not specify read timeouts; -hence, versions of -.i sendmail -prior to version 8.1 did not guarantee to reply to messages promptly. -In particular, a -.q RCPT -command specifying a mailing list -will expand and verify the entire list; -a large list on a slow system -may easily take more than five minutes\**. -.(f -\**This verification includes looking up every address -with the name server; -this involves network delays, -and can in some cases can be considerable. -.)f -I recommend a one hour timeout \*- -since a communications failure during the RCPT phase is rare, -a long timeout is not onerous -and may ultimately help reduce network load -and duplicated messages. -.pp For example, the lines: .(b O Timeout.command=25m @@ -2266,7 +2526,7 @@ and the input data block timeout to three hours. .sh 3 "Message timeouts" .pp After sitting in the queue for a few days, -a message will time out. +an undeliverable message will time out. This is to insure that at least the sender is aware of the inability to send a message. The timeout is typically set to five days. @@ -2313,7 +2573,7 @@ to return entries immediately during a queue run, e.g., to bounce messages independent of their time in the queue. .pp Since these options are global, -and since you can not know +and since you cannot know .i "a priori" how long another host outside your domain will be down, a five day timeout is recommended. @@ -2347,11 +2607,13 @@ option, .i sendmail will fork before each individual message while running the queue. -This will prevent +This option was used with earlier releases to prevent .i sendmail -from consuming large amounts of memory, -so it may be useful in memory-poor environments. -However, if the +from consuming large amounts of memory. +It should no longer be necessary with +.i sendmail +8.12. +If the .b ForkEachJob option is not set, .i sendmail @@ -2362,7 +2624,7 @@ If the .b ForkEachJob option is set, .i sendmail -can not use connection caching. +cannot use connection caching. .sh 2 "Queue Priorities" .pp Every message is assigned a priority when it is first instantiated, @@ -2430,27 +2692,23 @@ option defaults to 90000. .sh 2 "Load Limiting" .pp .i Sendmail -can be asked to queue (but not deliver) -mail if the system load average gets too high -using the +can be asked to queue (but not deliver) mail +if the system load average gets too high using the .b QueueLA (\c .b x ) option. When the load average exceeds the value of the .b QueueLA -option, -the delivery mode is set to +option, the delivery mode is set to .b q -(queue only) -if the +(queue only) if the .b QueueFactor (\c .b q ) option divided by the difference in the current load average and the .b QueueLA -option -plus one +option plus one is less than the priority of the message \(em that is, the message is queued iff: .EQ @@ -2459,22 +2717,22 @@ pri > { bold QueueFactor } over { LA - { bold QueueLA } + 1 } The .b QueueFactor option defaults to 600000, -so each point of load average is worth 600000 -priority points +so each point of load average is worth 600000 priority points (as described above). .pp -For drastic cases, -the +For drastic cases, the .b RefuseLA (\c .b X ) option defines a load average at which .i sendmail -will refuse -to accept network connections. -Locally generated mail -(including incoming UUCP mail) +will refuse to accept network connections. +Locally generated mail, i.e., mail which is not submitted via SMTP +(including incoming UUCP mail), is still accepted. +Notice that the MSP submits mail to the MTA via SMTP, and hence +mail will be queued in the client queue in such a case. +Therefore it is necessary to run the client mail queue periodically. .sh 2 "Delivery Mode" .pp There are a number of delivery modes that @@ -2514,7 +2772,9 @@ Mode .q d is identical to mode .q q -except that it also prevents all the early map lookups from working; +except that it also prevents lookups in maps including the +.b -D +flag from working during the initial queue phase; it is intended for ``dial on demand'' sites where DNS lookups might cost real money. Some simple error messages @@ -2538,7 +2798,7 @@ upon initial receipt of the mail. This speeds up the response to RCPT commands. Mode .q i -cannot be used by the SMTP server. +should not be used by the SMTP server. .sh 2 "Log Level" .pp The level of logging can be set for @@ -2610,9 +2870,18 @@ option to turn off some of these checks. .sh 3 "To suid or not to suid?" .pp .i Sendmail -is normally installed -setuid to root. -At the point where it is about to +is no longer installed +set-user-ID to root. +sendmail/SECURITY +explains how to configure and install +.i sendmail +without set-user-ID to root but set-group-ID +which is the default configuration starting with 8.12. +.pp +The daemon usually runs as root, unless other measures are taken. +At the point where +.i sendmail +is about to .i exec \|(2) a mailer, it checks to see if the userid is zero (root); @@ -2638,36 +2907,7 @@ to be accounted to root rather than to the user sending the mail. .pp -If you don't make -.i sendmail -setuid to root, it will still run but you lose a lot of functionality -and a lot of privacy, since you'll have to make the queue directory -world readable. -You could also make -.i sendmail -setuid to some pseudo-user -(e.g., create a user called -.q sendmail -and make -.i sendmail -setuid to that) -which will fix the privacy problems -but not the functionality issues. -It also introduces problems on some operating systems -if sendmail needs to give up the setuid special privileges. -Also, this isn't a guarantee of security: -for example, -root occasionally sends mail, -and the daemon often runs as root. -Note however that -.i sendmail -must run as root or the trusted user in order to create the SMTP listener -socket. -.pp -A middle ground is to make -.i sendmail -setuid to root, -but set the +A middle ground is to set the .b RunAsUser option. This causes @@ -2772,6 +3012,10 @@ Allow a .i \&.forward file that is in an unsafe directory to include references to program and files. +.ip GroupReadableKeyFile +Accept a group-readable key file for STARTTLS. +.ip GroupReadableSASLDBFile +Accept a group-readable Cyrus SASL password file. .ip GroupWritableAliasFile Allow group-writable alias files. .ip GroupWritableDirPathSafe @@ -2779,14 +3023,24 @@ Change the definition of .q "unsafe directory" to consider group-writable directories to be safe. World-writable directories are always unsafe. +.ip GroupWritableForwardFile +Allow group writable +.i \&.forward +files. .ip GroupWritableForwardFileSafe Accept group-writable .i \&.forward files as safe for program and file delivery. +.ip GroupWritableIncludeFile +Allow group wriable +.i :include: +files. .ip GroupWritableIncludeFileSafe Accept group-writable .i :include: files as safe for program and file delivery. +.ip GroupWritableSASLDBFile +Accept a group-writable Cyrus SASL password file. .ip HelpFileInUnsafeDirPath Allow the file named in the .b HelpFile @@ -2836,9 +3090,9 @@ in unsafe directories. Do not mark file and program deliveries as unsafe if sendmail is not running with root privileges. .ip RunProgramInUnsafeDirPath -Go ahead and run programs that are in writable directories. +Run programs that are in writable directories without logging a warning. .ip RunWritableProgram -Go ahead and run programs that are group- or world-writable. +Run programs that are group- or world-writable without logging a warning. .ip TrustStickyBit Allow group or world writable directories if the sticky bit is set on the directory. @@ -2846,6 +3100,14 @@ Do not set this on systems which do not honor the sticky bit on directories. .ip WorldWritableAliasFile Accept world-writable alias files. +.ip WorldWritableForwardfile +Allow world writable +.i \&.forward +files. +.ip WorldWritableIncludefile +Allow world wriable +.i :include: +files. .ip WriteMapToHardLink Allow writes to maps that are hard links. .ip WriteMapToSymLink @@ -2980,13 +3242,21 @@ turns on the AAONLY (accept authoritative answers only) and turns off the DNSRCH (search the domain path) options. Most resolver libraries default DNSRCH, DEFNAMES, and RECURSE flags on and all others off. +If NETINET6 is enabled, most libraries default to USE_INET6 as well. You can also include .q HasWildcardMX to specify that there is a wildcard MX record matching your domain; this turns off MX matching when canonifying names, which can lead to inappropriate canonifications. -.pp -Version level 1 configurations +Use +.q WorkAroundBrokenAAAA +when faced with a a broken nameservers that returns SERVFAIL +(a temporary failure) +on T_AAAA (IPv6) lookups +during hostname canonification. +.pp +Version level 1 configurations (see the section about +Configuration Version Level) turn DNSRCH and DEFNAMES off when doing delivery lookups, but leave them on everywhere else. Version 8 of @@ -3026,7 +3296,8 @@ when linking. Some sites mount each user's home directory from a local disk on their workstation, so that local access is fast. -However, the result is that .forward file lookups are slow. +However, the result is that .forward file lookups +from a central mail server are slow. In some cases, mail can even be delivered on machines inappropriately because of a file server being down. @@ -3056,14 +3327,13 @@ it should be mode 1777 (that is, the sticky bit should be set). Users should create the files mode 644. Note that you must use the -forwardfileinunsafedirpath and -forwardfileinunsafedirpathsafe -flags with the DontBlameSendmail option -to allow forward files in a world -writable directory. -This might also be used as a -denial of service -attack (users could create forward files for other users); +ForwardFileInUnsafeDirPath and +ForwardFileInUnsafeDirPathSafe +flags with the +.b DontBlameSendmail +option to allow forward files in a world writable directory. +This might also be used as a denial of service attack +(users could create forward files for other users); a better approach might be to create /var/forward mode 755 @@ -3162,10 +3432,11 @@ since this is done every time .i sendmail starts up, rather than easy for a human to read or write. -On the -.q "future project" -list is a -configuration-file compiler. +The configuration file should be generated via the method described in +.b cf/README , +it should not be edited directly unless someone is familiar +with the internals of the syntax described here and it is +not possible to achieve the desired result via the default method. .pp The configuration file is organized as a series of lines, each of which begins with a single character @@ -3432,7 +3703,7 @@ and may be multi-part. If the .i mailer -is the builtin IPC mailer, +is the built-in IPC mailer, the .i host may be a colon-separated list of hosts @@ -3653,7 +3924,12 @@ Many of these can also resolve to the special mailer name this accepts the message as though it were successful but then discards it without delivery. Note, -this mailer can not be chosen as a mailer in ruleset 0. +this mailer cannot be chosen as a mailer in ruleset 0. +Note also that all +.q check_* +rulesets have to deal with temporary failures, especially for map lookups, +themselves, i.e., they should return a temporary error code +or at least they should make a proper decision in those cases. .sh 4 "check_relay" .pp The @@ -3686,6 +3962,14 @@ ruleset is passed the user name parameter of the .sm "SMTP RCPT" command. It can accept or reject the address. +.sh 4 "check_data" +.pp +The +.i check_data +ruleset is called after the +.sm "SMTP DATA" +command, its parameter is the number of recipients. +It can accept or reject the command. .sh 4 "check_compat" .pp The @@ -3817,6 +4101,103 @@ If the ruleset does resolve to the .q error mailer, the connection is aborted (treated as non-deliverable with a permanent or temporary error). +.sh 4 "tls_rcpt" +.pp +The +.i tls_rcpt +ruleset is called each time before a RCPT TO command is sent. +The parameter is the current recipient. +If the ruleset does resolve to the +.q error +mailer, the RCPT TO command is suppressed +(treated as non-deliverable with a permanent or temporary error). +This ruleset allows to require encryption or verification of +the recipient's MTA even if the mail is somehow redirected +to another host. +For example, sending mail to +.i luke@endmail.org +may get redirected to a host named +.i death.star +and hence the tls_server ruleset won't apply. +By introducing per recipient restrictions such attacks +(e.g., via DNS spoofing) can be made impossible. +See +.i cf/README +how this ruleset can be used. +.sh 4 "srv_features" +.pp +The +.i srv_features +ruleset is called when a client connects to sendmail. +This ruleset should return +.b $# +followed by a list of options (single characters +delimited by white space). +If the return value starts with anything else it is silently ignored. +Generally upper case characters turn off a feature +while lower case characters turn it on. +The option `S' causes the server not to offer STARTTLS. +This is useful to interact with MTAs/MUAs that have broken +STARTTLS implementations by simply not offering it. +`V' turns off the request for a client certificate +during the TLS handshake. +Option `A' and `P' suppress SMTP AUTH and PIPELINING, respectively. +The ruleset may return `$#temp' to indicate that there is a temporary +problem determining the correct features, e.g., if a map is unavailable. +In that case, the SMTP server issues a temporary failure and does not +accept email. +.sh 4 "try_tls" +.pp +The +.i try_tls +ruleset is called when sendmail connects to another MTA. +If the ruleset does resolve to the +.q error +mailer, sendmail does not try STARTTLS even if it is offered. +This is useful to interact with MTAs that have broken +STARTTLS implementations by simply not using it. +.sh 4 "authinfo" +.pp +The +.i authinfo +ruleset is called when sendmail tries to authenticate to another MTA. +It should return +.b $# +followed by a list of tokens that are used for SMTP AUTH. +If the return value starts with anything else it is silently ignored. +Each token is a tagged string of the form: +"TDstring" +(including the quotes), where +.(b +.ta 9n +T Tag which describes the item +D Delimiter: ':' simple text follows + '=' string is base64 encoded +string Value of the item +.)b +Valid values for the tag are: +.(b +.ta 9n +U user (authorization) id +I authentication id +P password +R realm +M list of mechanisms delimited by spaces +.)b +If this ruleset is defined, the option +.b DefaultAuthInfo +is ignored (even if the ruleset does not return a ``useful'' result). +.sh 4 "queuegroup" +.pp +The +.i queuegroup +ruleset is used to map an address to a queue group name. +It should return +.b $# +followed by the name of a queue group. +If the return value starts with anything else it is silently ignored. +See the section about Queue Groups and Queue Directories +for further information. .sh 3 "IPC mailers" .pp Some special processing occurs @@ -3831,11 +4212,16 @@ The host name passed after has MX expansion performed if not delivering via a named socket; this looks the name up in DNS to find alternate delivery sites. .pp -The host name can also be provided as a dotted quad in square brackets; +The host name can also be provided as a dotted quad +or an IPv6 address in square brackets; for example: .(b [128.32.149.78] .)b +or +.(b +[IPv6:2002:c0a8:51d2::23f4] +.)b This causes direct conversion of the numeric value to an IP host address. .pp @@ -3934,19 +4320,6 @@ The .b $| ) clause may be omitted. .pp -Lower case macro names are reserved to have -special semantics, -used to pass information in or out of -.i sendmail , -and special characters are reserved to -provide conditionals, etc. -Upper case names -(that is, -.b $A -through -.b $Z ) -are specifically reserved for configuration file authors. -.pp The following macros are defined and/or used internally by .i sendmail for interpolation into argv's for mailers @@ -4005,7 +4378,7 @@ This is set in ruleset 0 from the $@ field of a parsed address. .ip $i The queue id, e.g., -.q HAA12345 . +.q f344MXxp018717 . .ip $j\(dd The \*(lqofficial\*(rq domain name for this site. This is fully qualified if the full qualification can be found. @@ -4106,15 +4479,26 @@ The full name of the sender. The home directory of the recipient. .ip $_ The validated sender address. +.ip ${addr_type} +The type of the address which is currently being rewritten. +This macro contains up to three characters, the first +is either `e' or `h' for envelope/header address, +the second is a space, +and the third is either `s' or `r' for sender/recipient address. +Notice: for header addresses no distinction is currently made +between sender and recipient addresses, i.e., the macro contains +only `h'. .ip ${auth_authen} The client's authentication credentials as determined by authentication (only set if successful). +The format depends on the mechanism used, it might be just `user', +or `user@realm', or something similar (SMTP AUTH only). .ip ${auth_author} The authorization identity, i.e. the AUTH= parameter of the .sm "SMTP MAIL" command if supplied. .ip ${auth_type} -The mechanism used for authentication +The mechanism used for SMTP authentication (only set if successful). .ip ${auth_ssf} The keylength (in bits) of the symmetric encryption algorithm @@ -4125,44 +4509,64 @@ The message body type as determined from the envelope. .ip ${cert_issuer} The DN (distinguished name) of the CA (certificate authority) -that signed the presented certificate (the cert issuer). +that signed the presented certificate (the cert issuer) +(STARTTLS only). +.ip ${cert_md5} +The MD5 hash of the presented certificate (STARTTLS only). .ip ${cert_subject} -The DN of the presented certificate (called the cert subject). +The DN of the presented certificate (called the cert subject) +(STARTTLS only). .ip ${cipher} The cipher suite used for the connection, e.g., EDH-DSS-DES-CBC3-SHA, -EDH-RSA-DES-CBC-SHA, DES-CBC-MD5, DES-CBC3-SHA. +EDH-RSA-DES-CBC-SHA, DES-CBC-MD5, DES-CBC3-SHA +(STARTTLS only). .ip ${cipher_bits} The keylength (in bits) of the symmetric encryption algorithm used for a TLS connection. .ip ${client_addr} The IP address of the SMTP client. +IPv6 addresses are tagged with "IPv6:" before the address. Defined in the SMTP server only. .ip ${client_name} The host name of the SMTP client. This may be the client's bracketed IP address -in the form [ nnn.nnn.nnn.nnn ] if the client's +in the form [ nnn.nnn.nnn.nnn ] for IPv4 +and [ IPv6:nnnn:...:nnnn ] for IPv6 +if the client's IP address is not resolvable, or if it is resolvable but the IP address of the resolved hostname doesn't match the original IP address. Defined in the SMTP server only. +See also +.b ${client_resolve} . .ip ${client_port} The port number of the SMTP client. Defined in the SMTP server only. .ip ${client_resolve} Holds the result of the resolve call for -.b ${client_name} -: OK, FAIL, FORGED, TEMP. +.b ${client_name} . +Possible values are: +.(b +.ta 10n +OK resolved successfully +FAIL permanent lookup failure +FORGED forward lookup doesn't match reverse lookup +TEMP temporary lookup failure +.)b Defined in the SMTP server only. +.ip ${cn_issuer} +The CN (common name) of the CA that signed the presented certificate +(STARTTLS only). +.ip ${cn_subject} +The CN (common name) of the presented certificate +(STARTTLS only). .ip ${currHeader} Header value as quoted string (possibly truncated to .b MAXNAME ). +This macro is only available in header check rulesets. .ip ${daemon_addr} The IP address the daemon is listening on for connections. -Unless -.b DaemonPortOptions -is set, this will be -.q 0.0.0.0 . .ip ${daemon_family} The network family if the daemon is accepting network connections. @@ -4208,32 +4612,43 @@ It is initially set to the value of the .b DeliveryMode option. .ip ${envid} -The envelope id passed to sendmail as part of the envelope. +The envelope id parameter (ENVID=) passed to sendmail as part of the envelope. .ip ${hdrlen} The length of the header value which is stored in ${currHeader} (before possible truncation). -If this value is greater than or equal +If this value is greater than or equal to .b MAXNAME the header has been truncated. .ip ${hdr_name} The name of the header field for which the current header check ruleset has been called. This is useful for a default header check ruleset to get -the name of the header. +the name of the header; +the macro is only available in header check rulesets. .ip ${if_addr} The IP address of the interface of an incoming connection unless it is in the loopback net. +IPv6 addresses are tagged with "IPv6:" before the address. +.ip ${if_addr_out} +The IP address of the interface of an outgoing connection +unless it is in the loopback net. +IPv6 addresses are tagged with "IPv6:" before the address. .ip ${if_family} The IP family of the interface of an incoming connection unless it is in the loopback net. +.ip ${if_family_out} +The IP family of the interface of an outgoing connection +unless it is in the loopback net. .ip ${if_name} -The name of the interface of an incoming connection. +The hostname associated with the interface of an incoming connection. This macro can be used for SmtpGreetingMessage and HReceived for virtual hosting. For example: .(b O SmtpGreetingMessage=$?{if_name}${if_name}$|$j$. MTA .)b +.ip ${if_name_out} +The name of the interface of an outgoing connection. .ip ${mail_addr} The address part of the resolved triple of the address given for the .sm "SMTP MAIL" @@ -4256,6 +4671,12 @@ before the message has been collected, thereafter the message size as computed by .i sendmail (and can be used in check_compat). +.ip ${nrcpts} +The number of validated recipients for a single message. +Note: since recipient validation happens after +.i check_rcpt +has been called, the value in this ruleset +is one less than what might be expected. .ip ${ntries} The number of delivery attempts. .ip ${opMode} @@ -4276,36 +4697,39 @@ to The address part of the resolved triple of the address given for the .sm "SMTP RCPT" command. -Defined in the SMTP server only. +Defined in the SMTP server only after a RCPT command. .ip ${rcpt_host} The host from the resolved triple of the address given for the .sm "SMTP RCPT" command. -Defined in the SMTP server only. +Defined in the SMTP server only after a RCPT command. .ip ${rcpt_mailer} The mailer from the resolved triple of the address given for the .sm "SMTP RCPT" command. -Defined in the SMTP server only. +Defined in the SMTP server only after a RCPT command. .ip ${server_addr} The address of the server of the current outgoing SMTP connection. .ip ${server_name} The name of the server of the current outgoing SMTP connection. .ip ${tls_version} -The TLS/SSL version used for the connection, e.g., TLSv1, SSLv3, SSLv2. +The TLS/SSL version used for the connection, e.g., TLSv1, SSLv3, SSLv2; +defined after STARTTLS has been used. .ip ${verify} -The result of the verification of the presented cert. +The result of the verification of the presented cert; +only defined after STARTTLS has been used. Possible values are: .(b -.ta 9n +.ta 13n OK verification succeeded. NO no cert presented. +NOT no cert requested. FAIL cert presented but could not be verified, e.g., the signing CA is missing. NONE STARTTLS has not been performed. TEMP temporary error occurred. -PROTOCOL some protocol error occurred. -SOFTWARE STARTTLS handshake failed, +PROTOCOL some protocol error occurred. +SOFTWARE STARTTLS handshake failed, which is a fatal error for this session, the e-mail will be queued. .)b @@ -4552,6 +4976,9 @@ The syntax is: .br .b F \c .i c\||program +.br +.b F \c +.i c\|[mapkey]@mapclass:mapspec .)b The first form defines the class .i c @@ -4582,18 +5009,47 @@ The ``F'' forms read the elements of the class .i c from the named -.i file +.i file , +.i program , or -.i program . +.i "map specification" . Each element should be listed on a separate line. -To specify an optional file, use ``-o'' between the class +To specify an optional file, use ``\-o'' between the class name and the file name, e.g., .(b -Fc -o /path/to/file +Fc \-o /path/to/file .)b If the file can't be used, .i sendmail will not complain but silently ignore it. +The map form should be an optional map key, an at sign, +and a map class followed by the specification for that map. +Examples include: +.(b +F{VirtHosts}@ldap:\-k (&(objectClass=virtHosts)(host=*)) \-v host +F{MyClass}foo@hash:/etc/mail/classes +.)b +will fill the class +.b $={VirtHosts} +from an LDAP map lookup and +.b $={MyClass} +from a hash database map lookup of the +.b foo . +There is also a built-in schema that can be accessed by only specifying: +.(b +F{\c +.i ClassName }@LDAP +.)b +This will tell sendmail to use the default schema: +.(b +\-k (&(objectClass=sendmailMTAClass) + (sendmailMTAClassName=\c +.i ClassName ) + (|(sendmailMTACluster=${sendmailMTACluster}) + (sendmailMTAHost=$j))) +\-v sendmailMTAClassValue +.)b +Note that the lookup is only done when sendmail is initially started. .pp Elements of classes can be accessed in rules using .b $= @@ -4729,6 +5185,7 @@ Path The pathname of the mailer Flags Special flags for this mailer Sender Rewriting set(s) for sender addresses Recipient Rewriting set(s) for recipient addresses +recipients Maximum number of recipients per connection Argv An argument vector to pass to this mailer Eol The end-of-line string for this mailer Maxsize The maximum message length to this mailer @@ -4740,9 +5197,11 @@ Nice The nice(2) increment for the mailer Charset The default character set for 8-bit characters Type Type information for DSN diagnostics Wait The maximum time to wait for the mailer +Queuegroup The default queue group for the mailer / The root directory for the mailer .)b -Only the first character of the field name is checked. +Only the first character of the field name is checked +(it's case-sensitive). .pp The following flags may be set in the mailer description. Any other flags may be used freely @@ -4870,10 +5329,10 @@ to another .i sendmail \*- as such it can use special protocol features. -This option is not required -(i.e., -if this option is omitted the transmission will still operate successfully, -although perhaps not as efficiently as possible). +This flag should not be used except for debugging purposes +because it uses +.b VERB +as SMTP command. .ip j Do User Database rewriting on recipients as well as senders. .ip k @@ -4972,6 +5431,8 @@ Secure ports aren't (secure, that is) except on UNIX machines, so it is unclear that this adds anything. +.i sendmail +must be running as root to be able to use this flag. .ip s Strip quote characters (" and \e) off of the address before calling the mailer. @@ -5001,10 +5462,12 @@ on the end. .ip w The user must have a valid account on this machine, i.e., -getpwnam +.i getpwnam must succeed. -If not, -the mail is bounced. +If not, the mail is bounced. +See also the +.b MailBoxDatabase +option. This is required to get .q \&.forward capability. @@ -5029,8 +5492,16 @@ and the local mailer. This is a variant on SMTP defined in RFC 2033 that is specifically designed for delivery to a local mailbox. +.ip Z +Apply DialDelay (if set) to this mailer. .ip 0 Don't look up MX records for hosts sent via SMTP. +.ip 1 +Don't send null characters ('\\0') to this mailer. +.ip 2 +Don't use ESMTP even if offered; this is useful for broken +systems that offer ESMTP but fail on EHLO (without recovering +when HELO is tried next). .ip 3 Extend the list of characters converted to =XX notation when converting to Quoted-Printable @@ -5124,7 +5595,7 @@ The mailer with the special name .q discard causes any mail sent to it to be discarded but otherwise treated as though it were successfully delivered. -This mailer can not be used in ruleset 0, +This mailer cannot be used in ruleset 0, only in the various address checking rulesets. .pp The mailer named @@ -5148,13 +5619,25 @@ M*file*, P=[FILE], F=lsDFMPEouq9, T=DNS/RFC822/X-Unix, A=FILE $u M*include*, P=/dev/null, F=su, A=INCLUDE $u .)b .pp +Builtin pathnames are [FILE] and [IPC], the former is used for +delivery to files, the latter for delivery via interprocess communication. +For mailers that use [IPC] as pathname the argument vector +must start with TCP or FILE for delivery via a TCP or a Unix domain socket. +.pp +If the argument vector does not contain $u then +.i sendmail +will speak SMTP (or LMTP if the mailer flag z is specified) to the mailer. +.pp +If no Eol field is defined, then the default is "\\r\\n" for +SMTP mailers and "\\n" of others. +.pp The Sender and Recipient rewriting sets may either be a simple ruleset id or may be two ids separated by a slash; if so, the first rewriting set is applied to envelope addresses and the second is applied to headers. -Setting any value zero disables corresponding mailer-specific rewriting. +Setting any value to zero disables corresponding mailer-specific rewriting. .pp The Directory is actually a colon-separated path of directories to try. @@ -5168,7 +5651,7 @@ This is intended to be used only on the mailer, since some shells (such as .i csh ) -refuse to execute if they cannot read the home directory. +refuse to execute if they cannot read the current directory. Since the queue directory is not normally readable by unprivileged users .i csh scripts as recipients can fail. @@ -5232,8 +5715,13 @@ or begin with The default is .q dns/rfc822/smtp . .pp -The m= field specifies the maximum number of messages to attempt to deliver -on a single SMTP or LMTP connection. +The m= field specifies the maximum number of messages +to attempt to deliver on a single SMTP or LMTP connection. +The default is infinite. +.pp +The r= field specifies the maximum number of recipients +to attempt to deliver in a single envelope. +It defaults to 100. .pp The /= field specifies a new root directory for the mailer. The path is macro expanded and then passed to the @@ -5245,6 +5733,11 @@ The Wait= field specifies the maximum time to wait for the mailer to return after sending all data to it. This applies to mailers that have been forked by .i sendmail . +.pp +The Queuegroup= field specifies the default queue group in which +received mail should be queued. +This can be overridden by other means as explained in section +``Queue Groups and Queue Directories''. .sh 2 "H \*- Define Header" .pp The format of the header lines that @@ -5272,8 +5765,8 @@ The syntax of this line is one of the following: .)b .(b F .b H [\c -.b ? \c -.i ${macro} \c +.b ?$ \c +.i {macro} \c .b ? \c .b ]\c .i hname \c @@ -5305,6 +5798,12 @@ storage map in a ruleset. If one of these headers is in the input it is reflected to the output regardless of these flags or macros. +Notice: +If a +.i ${macro} +is used to set a header, then it is useful to add that macro to class +.i $={persistentMacros} +which consists of the macros that should be saved across queue runs. .pp Some headers have special semantics that will be described later. @@ -5378,13 +5877,10 @@ or .)b .sh 2 "O \*- Set Option" .pp -There are a number of -global -options that +There are a number of global options that can be set from a configuration file. Options are represented by full words; -some are also representable as single characters -for back compatibility. +some are also representable as single characters for back compatibility. The syntax of this line is: .(b F .b O \0 @@ -5421,6 +5917,11 @@ the default is TRUE), or a time interval. .pp +All filenames used in options should be absolute paths, +i.e., starting with '/'. +Relative filenames most likely cause surprises during operation +(unless otherwise noted). +.pp The options supported (with the old, one character names in brackets) are: .nr ii 1i .ip "AliasFile=\fIspec, spec, ...\fP" @@ -5437,6 +5938,29 @@ where .i class \c .b : is optional and defaults to ``implicit''. +Note that +.i info +is required for all +.i class es +except +.q ldap . +For the +.q ldap +class, +if +.i info +is not specified, +a default +.i info +value is used as follows: +.(b +\-k (&(objectClass=sendmailMTAAliasObject) + (sendmailMTAAliasName=aliases) + (|(sendmailMTACluster=${sendmailMTACluster}) + (sendmailMTAHost=$j)) + (sendmailMTAKey=%0)) +\-v sendmailMTAAliasValue +.)b Depending on how .i sendmail is compiled, valid classes are @@ -5487,47 +6011,70 @@ entry to exist in the alias database before starting up. If it does not appear in the .i timeout -interval -rebuild the database -(if the -.b AutoRebuildAliases -option is also set) -or issue a warning. +interval issue a warning. .ip AllowBogusHELO [no short name] If set, allow HELO SMTP commands that don't include a host name. Setting this violates RFC 1123 section 5.2.5, but is necessary to interoperate with several SMTP clients. If there is a value, it is still checked for legitimacy. +.ip AuthMaxBits=\fIN\fP +[no short name] +Limit the maximum encryption strength for the security layer in +SMTP AUTH (SASL). Default is essentially unlimited. +This allows to turn off additional encryption in SASL if +STARTTLS is already encrypting the communication, because the +existing encryption strength is taken into account when choosing +an algorithm for the security layer. +For example, if STARTTLS is used and the symmetric cipher is 3DES, +then the the keylength (in bits) is 168. +Hence setting +.b AuthMaxBits +to 168 will disable any encryption in SASL. .ip AuthMechanisms [no short name] List of authentication mechanisms for AUTH (separated by spaces). The advertised list of authentication mechanisms will be the intersection of this list and the list of available mechanisms as determined by the Cyrus SASL library. +If STARTTLS is active, EXTERNAL will be added to this list. +In that case, the value of {cert_subject} is used as authentication id. .ip AuthOptions [no short name] -When to use the AUTH= parameter for the MAIL FROM command; +List of options for SMTP AUTH consisting of single characters +with intervening white space or commas. .(b -.ta 1i -A Only when authentication succeeded. -.)b -The default is to try whenever SMTP AUTH is available. -.ip AutoRebuildAliases -[D] -If set, -rebuild the alias database if necessary and possible. -The rebuild will happen the next time an alias is looked up. -If this option is not set, -.i sendmail -will never rebuild the alias database -unless explicitly requested -using -.b \-bi . -.b NOTE : -There is a potential for a denial of service attack if this is set. -This option is deprecated and -will be removed from a future version. +.ta 4n +A Use the AUTH= parameter for the MAIL FROM + command only when authentication succeeded. +a protection from active (non-dictionary) attacks + during authentication exchange. +c require mechanisms which pass client credentials, + and allow mechanisms which can pass credentials + to do so. +d don't permit mechanisms susceptible to passive + dictionary attack. +f require forward secrecy between sessions + (breaking one won't help break next). +p don't permit mechanisms susceptible to simple + passive attack (e.g., PLAIN, LOGIN). +y don't permit mechanisms that allow anonymous login. +.)b +The first option applies to sendmail as a client, the others to a server. +Example: +.(b +O AuthOptions=p,y +.)b +would disallow ANONYMOUS as AUTH mechanism and would +allow PLAIN only if a security layer (e.g., +provided by STARTTLS) is already active. +The options 'a', 'c', 'd', 'f', 'p', and 'y' refer to properties of the +selected SASL mechanisms. +Explanations of these properties can be found in the Cyrus SASL documentation. +.ip BadRcptThrottle=\fIN\fP +[no short name] +If set and more than the specified number of recipients in a single SMTP +envelope are rejected, sleep for one second after each rejected RCPT command. .ip BlankSub=\fIc\fP [B] Set the blank substitution character to @@ -5541,7 +6088,8 @@ This directory directory must contain the hashes of each CA certificate as filenames (or as links to them). .ip CACERTFile [no short name] -File containing one CA certificate. +File containing one or more CA certificates; +see section about STARTTLS for more information. .ip CheckAliases [n] Validate the RHS of aliases when rebuilding the alias database. @@ -5570,7 +6118,15 @@ Defaults to 1800. .ip ClientCertFile [no short name] File containing the certificate of the client, i.e., this certificate -is used when sendmail acts as client. +is used when +.i sendmail +acts as client (for STARTTLS). +.ip ClientKeyFile +[no short name] +File containing the private key belonging to the client certificate +(for STARTTLS if +.i sendmail +runs as client). .ip ClientPortOptions=\fIoptions\fP [O] Set client SMTP options. @@ -5596,13 +6152,21 @@ can be the following character: .(b .ta 1i h use name of interface for HELO command +A don't use AUTH when sending e-mail +S don't use STARTTLS when sending e-mail .)b If ``h'' is set, the name corresponding to the outgoing interface address (whether chosen via the Connection parameter or the default) is used for the HELO/EHLO command. -.ip ClientKeyFile -[no short name] -File containing the private key belonging to the client certificate. +However, the name must not start with a square bracket +and it must contain at least one dot. +This is a simple test whether the name is not +an IP address (in square brackets) but a qualified hostname. +Note that multiple ClientPortOptions settings are allowed +in order to give settings for each protocol family +(e.g., one for Family=inet and one for Family=inet6). +A restriction placed on one family only affects +outgoing connections on that particular family. .ip ColonOkInAddr [no short name] If set, colons are acceptable in e-mail addresses @@ -5661,7 +6225,7 @@ override the connection address (for testing purposes). If set to a positive value, allow no more than .i N -incoming daemon connections in a one second period. +incoming connections in a one second period per daemon. This is intended to flatten out peaks and allow the load average checking to cut in. Defaults to zero (no limits). @@ -5687,7 +6251,9 @@ If not set, no control socket will be available. Solaris and pre-4.4BSD kernel users should see the note in sendmail/README . .ip DHParameters File with DH parameters for STARTTLS. -This is only required if DSA/DH is used. +This is only required if a ciphersuite containing DSA/DH is used. +This is only for people with a good knowledge of TLS, all others +can ignore this option. .ip DaemonPortOptions=\fIoptions\fP [O] Set server SMTP options. @@ -5729,8 +6295,11 @@ b bind to interface through which mail has been received c perform hostname canonification (.cf) f require fully qualified hostname (.cf) u allow unqualified addresses (.cf) +A disable AUTH (overrides 'a' modifier) C don't perform hostname canonification E disallow ETRN (see RFC 2476) +O optional; if opening the socket fails ignore it +S don't offer STARTTLS .)b That is, one way to specify a message submission agent (MSA) that always requires authentication is: @@ -5756,7 +6325,8 @@ See the relevant documentation for The modifier ``f'' disallows addresses of the form .b user@host unless they are submitted directly. -The flag ``u'' allows unqualified sender addresses. +The flag ``u'' allows unqualified sender addresses, +i.e., those without @host. ``b'' forces sendmail to bind to the interface through which the e-mail has been received for the outgoing connection. @@ -5773,16 +6343,29 @@ Note, will listen on a new socket for each occurence of the DaemonPortOptions option in a configuration file. +The modifier ``O'' causes sendmail to ignore a socket +if it can't be opened. +This applies to failures from the socket(2) and bind(2) calls. .ip DefaultAuthInfo [no short name] Filename that contains default authentication information for outgoing connections. This file must contain the user id, the authorization id, -the password (plain text), and the realm to use +the password (plain text), the realm and the list of mechanisms to use on separate lines and must be readable by root (or the trusted user) only. If no realm is specified, .b $j is used. +If no mechanisms are specified, the list given by +.b AuthMechanisms +is used. +Notice: this option is deprecated and will be removed in future versions. +Moreover, it doesn't work for the MSP since it can't read the file +(the file must not be group/world-readable otherwise +.i sendmail +will complain). +Use the authinfo ruleset instead which provides more control over +the usage of the data anyway. .ip DefaultCharSet=\fIcharset\fP [no short name] When a message that has 8-bit characters but is not in MIME format @@ -5811,7 +6394,7 @@ formerly hardcoded to /usr/tmp/dead.letter. If this option is not set (the default), sendmail will not attempt to save to a system-wide dead.letter file in the event -it can not bounce the mail to the user or postmaster. +it cannot bounce the mail to the user or postmaster. Instead, it will rename the qf file as it has in the past when the dead.letter file could not be opened. @@ -5845,6 +6428,19 @@ option has been combined into the .b DefaultUser option. .)f +.ip DelayLA=\fILA\fP +[no short name] +When the system load average exceeds +.i LA , +.i sendmail +will sleep for one second on most SMTP commands and +before accepting connections. +.ip DeliverByMin=\fItime\fP +[0] +Set minimum time for Deliver By SMTP Service Extension (RFC 2852). +If 0, no time is listed, if less than 0, the extension is not offered, +if greater than 0, it is listed as minimum time +for the EHLO keyword DELIVERBY. .ip DeliveryMode=\fIx\fP [d] Deliver in mode @@ -5879,6 +6475,17 @@ Units default to seconds, so uses a five second delay. Defaults to zero (no retry). +This delay only applies to mailers which have the +Z flag set. +.ip DirectSubmissionModifiers=\fImodifiers\fP +Defines +.b ${daemon_flags} +for direct (command line) submissions. +If not set, +.b ${daemon_flags} +is either "CC f" if the option +.b \-G +is used or "c u" otherwise. .ip DontBlameSendmail=\fIoption,option,...\fP [no short name] In order to avoid possible cracking attempts @@ -5892,46 +6499,7 @@ a group-writable directory, then you will have to turn off this checking (at the cost of making your system more vulnerable to attack). -The arguments are individual options that turn off checking: -.(b -Safe -AssumeSafeChown -ClassFileInUnsafeDirPath -DontWarnForwardFileInUnsafeDirPath -ErrorHeaderInUnsafeDirPath -FileDeliveryToHardLink -FileDeliveryToSymLink -ForwardFileInUnsafeDirPath -ForwardFileInUnsafeDirPathSafe -ForwardFileIngroupWritableDirPath -GroupWritableAliasFile -GroupWritableDirPathSafe -GroupWritableForwardFileSafe -GroupWritableIncludeFileSafe -HelpFileinUnsafeDirPath -IncludeFileInUnsafeDirPath -IncludeFileInUnsafeDirPathSafe -IncludeFileIngroupWritableDirPath -InsufficientEntropy -LinkedAliasFileInWritableDir -LinkedClassFileInWritableDir -LinkedForwardFileInWritableDir -LinkedIncludeFileInWritableDir -LinkedMapInWritableDir -LinkedServiceSwitchFileInWritableDir -MapInUnsafeDirPath -NonRootSafeAddr -RunProgramInUnsafeDirPath -RunWritableProgram -TrustStickyBit -WorldWritableAliasFile -WriteMapToHardLink -WriteMapToSymLink -WriteStatsToHardLink -WriteStatsToSymLink -.)b -.b Safe -is the default. +The possible arguments have been described earlier. The details of these flags are described above. .\"XXX should have more here!!! XXX .b "Use of this option is not recommended." @@ -5981,6 +6549,9 @@ However, you will need to be certain to include all variant names in the .b $=w class by some other mechanism. +If set to +.b loopback , +loopback interfaces (e.g., lo0) will not be probed. .ip DontPruneRoutes [R] Normally, @@ -6018,6 +6589,7 @@ The address is macro expanded at the time of delivery. If not set, defaults to .q postmaster . +If set to an empty string, double bounces are dropped. .ip EightBitMode=\fIaction\fP [8] Set handling of eight-bit data. @@ -6092,17 +6664,34 @@ If specified, the .i fallbackhost acts like a very low priority MX on every host. +MX records will be looked up for this host, +unless the name is surrounded by square brackets. This is intended to be used by sites with poor network connectivity. Messages which are undeliverable due to temporary address failures (e.g., DNS failure) -also go to the FallBackMX host. +also go to the FallbackMX host. +.ip FastSplit +[no short name] +If set to a value greater than zero (the default is one), +it suppresses the MX lookups on addresses +when they are initially sorted, i.e., for the first delivery attempt. +This usually results in faster envelope splitting unless the MX records +are readily available in a local DNS cache. +To enforce initial sorting based on MX records set +.b FastSplit +to zero. +If the mail is submitted directly from the command line, then +the value also limits the number of processes to deliver the envelopes; +if more envelopes are created they are only queued up +and must be taken care of by a queue run. +Since the default submission method is via SMTP (either from a MUA +or via the MSP), the value of +.b FastSplit +is seldom used to limit the number of processes to deliver the envelopes. .ip ForkEachJob [Y] If set, deliver each job that is run from the queue in a separate process. -Use this option if you are short of memory, -since the default tends to consume considerable amounts of memory -while the queue is being processed. .ip ForwardPath=\fIpath\fP [J] Set the path for searching for users' .forward files. @@ -6174,6 +6763,11 @@ A suggested value for sites desiring persistent host status is Ignore dots in incoming messages. This is always disabled (that is, dots are always accepted) when reading SMTP mail. +.ip InputMailFilters=\fIname,name,...\fP +A comma separated list of filters which determines which filters +(see the "X \*- Mail Filter (Milter) Definitions" section) +and the invocation sequence are contacted for incoming SMTP messages. +If none are set, no filters will be contacted. .ip LDAPDefaultSpec=\fIspec\fP [no short name] Sets a default map specification for LDAP maps. @@ -6198,6 +6792,21 @@ This is intended only for use from the command line. The .b \-M flag is preferred. +.ip MailboxDatabase +[no short name] +Type of lookup to find information about local mailboxes, +defaults to ``pw'' which uses +.i getpwnam . +Other types can be introduced by adding them to the source code, +see libsm/mbdb.c for details. +.ip UseMSP +[no short name] +Use as mail submission program, i.e., +allow group writable queue files +if the group is the same as that of a set-group-ID sendmail binary. +See the file +.b sendmail/SECURITY +in the distribution tarball. .ip MatchGECOS [G] Allow fuzzy matching on the GECOS field. @@ -6243,10 +6852,12 @@ to be advertised in the ESMTP EHLO response. Messages larger than this will be rejected. .ip MaxMimeHeaderLength=\fIN[/M]\fP [no short name] -Sets the maximum length of certain MIME header field values -to +Sets the maximum length of certain MIME header field values to .i N characters. +These MIME header fields are determined by being a member of +class {checkMIMETextHeaders}, which currently contains only +the header Content-Description. For some of these headers which take parameters, the maximum length of each parameter is set to .i M @@ -6257,6 +6868,21 @@ is not specified, one half of will be used. By default, these values are 0, meaning no checks are done. +.ip MaxQueueChildren=\fIN\fP +[no short name] +When set, this limits the number of concurrent queue runner processes to +.i N. +This helps to control the amount of system resources used when processing +the queue. When there are multiple queue groups defined and the total number +of queue runners for these queue groups would exceed +.i MaxQueueChildren +then the queue groups will not all run concurrently. That is, some portion +of the queue groups will run concurrently such that +.i MaxQueueChildren +will not be exceeded, while the remaining queue groups will be run later (in +round robin order). See also +.i MaxRunnersPerQueue +and the section \fBQueue Group Declaration\fP. .ip MaxQueueRunSize=\fIN\fP [no short name] The maximum number of jobs that will be processed @@ -6279,12 +6905,52 @@ in an SMTP transaction. Note: setting this too low can interfere with sending mail from MUAs that use SMTP for initial submission. If not set, there is no limit on the number of recipients per envelope. +.ip MaxRunnersPerQueue=\fIN\fP +[no short name] +This sets the default maximum number of queue runners for queue groups. +Up to +.i N +queue runners will work in parallel on a queue group's messages. +This is useful where the processing of a message in the queue might +delay the processing of subsequent messages. Such a delay may be the result +of non-erroneous situations such as a low bandwidth connection. +May be overridden on a per queue group basis by setting the +.i Runners +option; see the section \fBQueue Group Declaration\fP. +The default is 1 when not set. .ip MeToo [m] Send to me too, even if I am in an alias expansion. This option is deprecated and will be removed from a future version. +.ip Milter +[no short name] +This option has several sub(sub)options. +The names of the suboptions are separated by dots. +At the first level the following options are available: +.(b +.ta \w'LogLevel'u+3n +LogLevel Log level for input mail filter actions, defaults to LogLevel. +macros Specifies list of macro to transmit to filters. + See list below. +.)b +The ``macros'' option has the following suboptions +which specify the list of macro to transmit to milters +after a certain event occurred. +.(b +.ta \w'envfrom'u+3n +connect After session connection start +helo After HELO command +envfrom After MAIL FROM command +envrcpt After RCPT TO command +.)b +By default the lists of macros are empty. +Example: +.(b +O Milter.LogLevel=12 +O Milter.macros.connect=j, _, {daemon_name} +.)b .ip MinFreeBlocks=\fIN\fP [b] Insist on at least @@ -6310,6 +6976,9 @@ Sets the list of characters that must be quoted if used in a full name that is in the phrase part of a ``phrase <address>'' syntax. The default is ``\'.''. The characters ``@,;:\e()[]'' are always added to this list. +.ip NiceQueueRun +[no short name] +The priority of queue runners (nice(3)). .ip NoRecipientAction [no short name] The action to take when you receive a message that has no valid @@ -6411,6 +7080,7 @@ noetrn Disallow ETRN entirely noverb Disallow VERB entirely restrictmailq Restrict mailq command restrictqrun Restrict \-q command line flag +restrictexpand Restrict \-bv and \-v command line flags noreceipts Don't return success DSNs\** nobodyreturn Don't return the body of a message with DSNs goaway Disallow essentially all SMTP status queries @@ -6430,6 +7100,7 @@ pseudo-flag sets all flags except .q noreceipts , .q restrictmailq , .q restrictqrun , +.q restrictexpand , .q noetrn , and .q nobodyreturn . @@ -6439,6 +7110,22 @@ can print the queue. If queue runs are restricted, only root and the owner of the queue directory can run the queue. +The +.q restrictexpand +pseudo-flag instructs +.i sendmail +to drop privileges when the +.b \-bv +option is given by users who are neither root nor the TrustedUser +so users cannot read private aliases, forwards, or :include: files. +It will add the +.q NonRootSafeAddr +to the +.q DontBlameSendmail +option to prevent misleading unsafe address warnings. +It also overrides the +.b \-v +(verbose) command line option to prevent information leakage. Authentication Warnings add warnings about various conditions that may indicate attempts to spoof the mail system, such as using a non-standard queue directory. @@ -6451,18 +7138,27 @@ The will be macro processed. .ip QueueDirectory=\fIdir\fP [Q] -Use the named -.i dir -as the queue directory. -To use multiple queues, supply a value ending with an asterisk. -For example, -.i /var/spool/mqueue/q* -will use all of the directories or symbolic links to directories -beginning with -.i q -in +The QueueDirectory option serves two purposes. +First, it specifies the directory or set of directories that comprise +the default queue group. +Second, it specifies the directory D which is the ancestor of all queue +directories, and which sendmail uses as its current working directory. +When sendmail dumps core, it leaves its core files in D. +There are two cases. +If \fIdir\fR ends with an asterisk (eg, \fI/var/spool/mqueue/q*\fR), +then all of the directories or symbolic links to directories +beginning with `q' in .i /var/spool/mqueue -as queue directories. +will be used as queue directories of the default queue group, +and +.i /var/spool/mqueue +will be used as the working directory D. +Otherwise, +\fIdir\fR must name a directory (usually \fI/var/spool/mqueue\fR): +the default queue group consists of the single queue directory \fIdir\fR, +and the working directory D is set to \fIdir\fR. +To define additional groups of queue directories, +use the configuration file `Q' command. Do not change the queue directory structure while sendmail is running. .ip QueueFactor=\fIfactor\fP @@ -6496,6 +7192,11 @@ just queue messages Defaults to 8 multiplied by the number of processors online on the system (if that can be determined). +.ip QueueFileMode=\fImode\fP +[no short name] +Default permissions for queue files (octal). +If not set, sendmail uses 0600 unless its real +and effective uid are different in which case it uses 0644. .ip QueueSortOrder=\fIalgorithm\fP [no short name] Sets the @@ -6508,7 +7209,11 @@ Legal values are .q filename (to order by the name of the queue file name), .q time -(to order by the submission time), +(to order by the submission/creation time), +.q random +(to order randomly), +.q modification +(to order by the modification time of the qf file (older entries first)), and .q priority (to order by message priority). @@ -6517,13 +7222,16 @@ but may tend to process low priority messages that go to a single host over high priority messages that go to several hosts; it probably shouldn't be used on slow network links. -Filename ordering saves the overhead of +Filename and modification time ordering saves the overhead of reading all of the queued items before starting the queue run. -Time ordering is almost always a bad idea, +Creation (submission) time ordering is almost always a bad idea, since it allows large, bulk mail to go out before smaller, personal mail, but may have applicability on some hosts with very fast connections. +Random is useful if several queue runners are started by hand +which try to drain the same queue since odds are they will be working +on different parts of the queue at the same time. Priority ordering is the default. .ip QueueTimeout=\fItimeout\fP [T] @@ -6559,6 +7267,7 @@ can be .q recurse , .q defnames , .q stayopen , +.q use_inet6 , or .q dnsrch . The string @@ -6569,6 +7278,14 @@ or .b \- ) can be specified to turn off matching against MX records when doing name canonifications. +The string +.q WorkAroundBrokenAAAA +(without a +.b + +or +.b \- ) +can be specified to work around some broken nameservers +which return SERVFAIL (a temporary failure) on T_AAAA (IPv6) lookups. .b N.B. Prior to 8.7, this option indicated that the name server be responding @@ -6693,6 +7410,20 @@ UNIX-style lines at the front of headers. Normally they are assumed redundant and discarded. +.ip SharedMemoryKey +[no short name] +Key to use for shared memory segment; +if not set (or 0), shared memory will not be used. +Requires support for shared memory to be compiled into +.i sendmail . +If this option is set, +.i sendmail +can share some data between different instances. +For example, the number of entries in a queue directory +or the available space in a file system. +This allows for more efficient program execution, since only +one process needs to update the data instead of each individual +process gathering the data each time it is required. .ip SendMimeErrors [j] If set, send error messages in MIME format @@ -6705,10 +7436,12 @@ RFC1891. .ip ServerCertFile [no short name] File containing the certificate of the server, i.e., this certificate -is used when sendmail acts as server. +is used when sendmail acts as server +(used for STARTTLS). .ip ServerKeyFile [no short name] -File containing the private key belonging to the server certificate. +File containing the private key belonging to the server certificate +(used for STARTTLS). .ip ServiceSwitchFile=\fIfilename\fP [no short name] If your host operating system has a service switch abstraction @@ -6799,9 +7532,11 @@ It can be printed using the program. .ip SuperSafe [s] -Be super-safe when running things, -i.e., -always instantiate the queue file, +This option can be set to True, False, or Interactive. +If set to True, +.i sendmail +will be super-safe when running things, +i.e., always instantiate the queue file, even if you are going to attempt immediate delivery. .i Sendmail always instantiates the queue file @@ -6809,10 +7544,23 @@ before returning control to the client under any circumstances. This should really .i always -be set. +be set to True. +The Interactive value has been introduced in 8.12 and can +be used together with +.b DeliveryMode=i . +It skips some synchronization calls which are effectively +doubled in the code execution path for this mode. +.ip TLSSrvOptions +[no short name] +List of options for SMTP STARTTLS for the server +consisting of single characters +with intervening white space or commas. +The flag ``v'' disables client verification, and hence +it is not possible to use a client certificate for relaying. +Currently there are no other flags available. .ip TempFileMode=\fImode\fP [F] -The file mode for queue files, files to which +The file mode for transcript files, files to which .i sendmail delivers directly, and files in the .b HostStatusDirectory . @@ -6890,6 +7638,9 @@ that is, they cannot reference programs or write directly to files. World writable :include: and .forward files are always unsafe. +Note: use +.b DontBlameSendmail +instead; this option is deprecated. .ip UseErrorsTo [l] If there is an @@ -6939,7 +7690,7 @@ All options can be specified on the command line using the \-O or \-o flag, but most will cause .i sendmail -to relinquish its setuid permissions. +to relinquish its set-user-ID permissions. The options that will not cause this are SevenBitInput [7], EightBitMode [8], @@ -7038,7 +7789,7 @@ to do with the version on the files. For example, as of this writing -version 8 config files +version 10 config files (specifically, 8.10) used version level 9 configurations. .pp @@ -7143,6 +7894,9 @@ Version level nine configuration files allow parentheses in rulesets, i.e. they are not treated as comments and hence removed. .pp +Version level ten configuration files allow +queue group definitions. +.pp The .b V line may have an optional @@ -7241,7 +7995,7 @@ Note that .i default clauses never do this mapping. .pp -The built in map with both name and class +The built-in map with both name and class .q host is the host name canonicalization lookup. Thus, @@ -7381,6 +8135,13 @@ If the .b \-z flag is given, then all MX names are returned, separated by the given delimiter. +.ip dns +This map requires the option -R to specify the DNS resource record +type to lookup. The following types are supported: +A, AAAA, AFSDB, CNAME, MX, NS, PTR, SRV, and TXT. +A map lookup will return only one record. +Hence for some types, e.g., MX records, the return value might be a random +element of the list due to randomizing in the DNS resolver. .ip sequence The arguments on the `K' line are a list of maps; the resulting map searches the argument maps in order @@ -7470,15 +8231,14 @@ or the string specified with the the .b \-d flag. The flags available for the map are .(b +.ta 4n -n not -f case sensitive --b basic regular expressions - (default is extended) +-b basic regular expressions (default is extended) -s substring match -d set the delimiter used for -s -a append string to key --m match only, do not - replace/discard value +-m match only, do not replace/discard value -D perform no lookup in deferred delivery mode. .)b The @@ -7530,7 +8290,8 @@ R$\- $: $(storage {MyMacro} $) $1 .)b .ip arith Perform simple arithmetic operations. -The operation is given as key, currently +, -, *, /, +The operation is given as key, currently +, -, *, /, %, +|, & (bitwise OR, AND), l (for less than), and = are supported. The two operands are given as arguments. The lookup returns the result of the computation, @@ -7714,6 +8475,12 @@ in the presence of the .b \-A flag. .pp +Some additional flags are available for the host and dns maps: +.ip "\-d" +delay: specify the resolver's retransmission time interval (in seconds). +.ip "\-r" +retry: specify the number of times to retransmit a resolver query. +.pp The following additional flags are present in the ldap map only: .ip "\-R" Do not auto chase referrals. sendmail must be compiled with @@ -7721,6 +8488,10 @@ Do not auto chase referrals. sendmail must be compiled with to use this flag. .ip "\-n" Retrieve attribute names only. +.ip "\-V\fIsep\fP" +Retrieve both attributes name and value(s), +separated by +.i sep . .ip "\-r\fIderef\fP" Set the alias dereference option to one of never, always, search, or find. .ip "\-s\fIscope\fP" @@ -7811,8 +8582,245 @@ New classes can be added in the routine .b setupmaps in file .b conf.c . +.sh 2 "Q \*- Queue Group Declaration" +.pp +In addition to the option +.i QueueDirectory, +queue groups can be declared that define a (group of) queue directories +under a common name. +The syntax is as follows: +.(b F +.b Q \c +.i name +{, \c +.i field =\c +.i value \|}+ +.)b +where +.i name +is the symbolic name of the queue group under which +it can be referenced in various places +and the +.q field=name +pairs define attributes of the queue group. +Fields are: +.ip Flags +Flags for this queue group. +.ip Nice +The nice(2) increment for the queue group. +.ip Interval +The time between two queue runs. +.ip Path +The queue directory of the group (required). +.ip Runners +The number of parallel runners processing the queue. +.ip Jobs +The maximum number of jobs (messages delivered) per queue run. +.ip recipients +The maximum number of recipients per envelope. +Envelopes with more than this number of recipients will be split +into multiple envelopes in the same queue directory. +The default value 0 means no limit. +.lp +Only the first character of the field name is checked. +.pp +By default, a queue group named +.i mqueue +is defined that uses the value of the +.i QueueDirectory +option as path. +Notice: all paths that are used for queue groups must +be subdirectories of +.i QueueDirectory . +Since they can be symbolic links, this isn't a real restriction, +If +.i QueueDirectory +uses a wildcard, then the directory one level up is considered +the ``base'' directory which all other queue directories must share. +Please make sure that the queue directories do not overlap, +e.g., do not specify +.(b +O QueueDirectory=/var/spool/mqueue/* +Qone, P=/var/spool/mqueue/dir1 +Qtwo, P=/var/spool/mqueue/dir2 +.)b +because this also includes +.q dir1 +and +.q dir2 +in the default queue group. +However, +.(b +O QueueDirectory=/var/spool/mqueue/main* +Qone, P=/var/spool/mqueue/dir +Qtwo, P=/var/spool/mqueue/other* +.)b +is a valid queue group specification. +.pp +Options listed in the ``Flags'' field can be used to modify +the behavior of a queue group. +The ``f'' flag must be set if multiple queue runners are +supposed to work on the entries in a queue group. +Otherwise +.i sendmail +will work on the entries strictly sequentially. +.pp +The ``Interval'' field sets the time between queue runs. +If no queue group specific interval is set, then the parameter of the +.b -q +option from the command line is used. +.pp +To control the overall number of concurrently active queue runners +the option +.b MaxQueueChildren +can be set. +This limits the number of processes used for running the queues to +.b MaxQueueChildren , +though at any one time fewer processes may be active +as a result of queue options, completed queue runs, system load, etc. +.pp +The maximum number of queue runners for an individual queue group can be +controlled via the +.b Runners +option. +If set to 0, entries in the queue will not be processed, which +is useful to ``quarantine'' queue files. +The number of runners per queue group may also be set with the option +.b MaxRunnersPerQueue , +which applies to queue groups that have no individual limit. +That is, the default value for +.b Runners +is +.b MaxRunnersPerQueue +if set, otherwise 1. +.pp +The field Jobs describes the maximum number of jobs +(messages delivered) per queue run, which is the queue group specific +value of +.b MaxQueueRunSize . +.pp +Notice: queue groups should be declared after all queue related options +have been set because queue groups take their defaults from those options. +If an option is set after a queue group declaration, the values of +options in the queue group are set to the defaults of +.i sendmail +unless explicitly set in the declaration. +.pp +Each envelope is assigned to a queue group based on the algorithm +described in section +``Queue Groups and Queue Directories''. +.sh 2 "X \*- Mail Filter (Milter) Definitions" +.pp +The +.i sendmail +Mail Filter API (Milter) is designed to allow third-party programs access +to mail messages as they are being processed in order to filter +meta-information and content. +They are declared in the configuration file as: +.(b F +.b X \c +.i name +{, \c +.i field =\c +.i value \|}* +.)b +where +.i name +is the name of the filter +(used internally only) +and the +.q field=name +pairs define attributes of the filter. +Also see the documentation for the +.b InputMailFilters +option for more information. +.pp +Fields are: +.(b +.ta 1i +Socket The socket specification +Flags Special flags for this filter +Timeouts Timeouts for this filter +.)b +Only the first character of the field name is checked +(it's case-sensitive). +.pp +The socket specification is one of the following forms: +.(b F +.b S= \c +.b inet \c +.b : +.i port +.b @ +.i host +.)b +.(b F +.b S= \c +.b inet6 \c +.b : +.i port +.b @ +.i host +.)b +.(b F +.b S= \c +.b local \c +.b : +.i path +.)b +The first two describe an IPv4 or IPv6 socket listening on a certain +.i port +at a given +.i host +or IP address. +The final form describes a named socket on the filesystem at the given +.i path . +.pp +The following flags may be set in the filter description. +.nr ii 4n +.ip R +Reject connection if filter unavailable. +.ip T +Temporary fail connection if filter unavailable. +.pp +The timeouts can be set using the four fields inside of the +.b T= +equate: +.nr ii 4n +.ip C +Timeout for connecting to a filter. +If set to 0, the system's +.i connect() +timeout will be used. +.ip S +Timeout for sending information from the MTA to a filter. +.ip R +Timeout for reading reply from the filter. +.ip E +Overall timeout between sending end-of-message to filter and waiting for +the final acknowledgment. +.pp +Note the separator between each timeout field is a +.b ';' . +The default values (if not set) are: +.b T=C:5m;S:10s;R:10s;E:5m +where +.b s +is seconds and +.b m +is minutes. +.pp +Examples: +.(b +Xfilter1, S=local:/var/run/f1.sock, F=R +Xfilter2, S=inet6:999@localhost, F=T, T=S:1s;R:1s;E:5m +Xfilter3, S=inet:3333@localhost, T=C:2m +.)b .sh 2 "The User Database" .pp +The user database is deprecated in favor of ``virtusertable'' +and ``genericstable'' as explained in the file +.b cf/README . If you have a version of .i sendmail with the user database package @@ -8028,6 +9036,10 @@ Compile in support for Hesiod. Compile in support for IRIX NSD lookups. .ip MAP_REGEX Compile in support for regular expression matching. +.ip DNSMAP +Compile in support for DNS map lookups in the +.i sendmail.cf +file. .ip PH_MAP Compile in support for ph lookups. .ip SASL @@ -8038,15 +9050,18 @@ Compile in support for STARTTLS. .ip EGD Compile in support for the "Entropy Gathering Daemon" to provide better random data for TLS. -.ip SFIO -Compile in support for sfio, which is required to enable encryption, -e.g., STARTTLS. .ip TCPWRAPPERS Compile in support for TCP Wrappers. .ip _PATH_SENDMAILCF The pathname of the sendmail.cf file. .ip _PATH_SENDMAILPID The pathname of the sendmail.pid file. +.ip SM_CONF_SHM +Compile in support for shared memory, see section about +"/var/spool/mqueue". +.ip MILTER +Compile in support for contacting external mail filters built with the +Milter API. .pp There are also several compilation flags to indicate the environment such as @@ -8090,6 +9105,8 @@ since .i sendmail will break up a delivery into smaller batches as needed. A higher number may reduce load on your system, however. +.ip "MAXQUEUEGROUPS [50]" +The maximum number of queue groups. .ip "MAXATOM [1000]" The maximum number of atoms (tokens) @@ -8198,17 +9215,6 @@ you can set this flag to turn off special processing of UNIX-style .q "From " lines. -.ip QUEUE\(dg -This flag should be set to compile in the queueing code. -If this is not set, -mailers must accept the mail immediately -or it will be returned to the sender. -.ip SMTP\(dg -If set, -the code to handle user and server SMTP will be compiled in. -This is only necessary if your machine has some mailer -that speaks SMTP -(this means most machines everywhere). .ip USERDB\(dg Include the .b experimental @@ -8600,9 +9606,9 @@ is a pointer to the portion of the configuration file line following the map class name; flags and filenames can be extracted from this line. The initialization function must return -.sm TRUE +.sm true if it successfully opened the map, -.sm FALSE +.sm false otherwise. .pp The lookup function is called as @@ -8641,7 +9647,7 @@ shouldqueue(pri, ctime) time_t ctime; { if (CurrentLA < QueueLA) - return (FALSE); + return false; return (pri > (QueueFactor / (CurrentLA \- QueueLA + 1))); } .)b @@ -8656,7 +9662,7 @@ variable .i QueueLA ), .i shouldqueue returns -.sm FALSE +.sm false immediately (that is, it should .i not @@ -8668,7 +9674,7 @@ variable .i RefuseLA ), .i shouldqueue returns -.sm TRUE +.sm true immediately. Otherwise, it computes the function based on the message priority, the queue factor @@ -8699,7 +9705,7 @@ to ensure that messages are eventually processed. The function .i refuseconnections returns -.sm TRUE +.sm true if incoming SMTP connections should be refused. The current implementation is based exclusively on the current load average and the refuse load average option @@ -8745,11 +9751,20 @@ if you wanted to generalize .b $] lookups. We now recommend that you create a new keyed map instead. -.sh 2 "Certificates for STARTTLS" +.sh 2 "STARTTLS" .pp In this section we assume that .i sendmail has been compiled with support for STARTTLS. +To properly understand the use of STARTTLS in +.i sendmail , +it is necessary to understand at least some basics about X.509 certificates +and public key cryptography. +This information can be found in books about SSL/TLS +or on WWW sites, e.g., +.q http://www.OpenSSL.org/ . +.sh 3 "Certificates for STARTTLS" +.pp When acting as a server, .i sendmail requires X.509 certificates to support STARTTLS: @@ -8785,7 +9800,7 @@ To allow for automatic startup of sendmail, private keys must be stored unencrypted. The keys are only protected by the permissions of the file system. Never make a private key available to a third party. -.sh 2 "PRNG for STARTTLS" +.sh 3 "PRNG for STARTTLS" .pp STARTTLS requires a strong pseudo random number generator (PRNG) to operate properly. @@ -8917,6 +9932,26 @@ have freed me up to do other work. .pp Arguments must be presented with flags before addresses. The flags are: +.ip \-A\fIx\fP +Select an alternative .cf file which is either +.i sendmail.cf +for +.b \-Am +or +.i submit.cf +for +.b \-Ac . +By default the .cf file is chosen based on the operation mode. +For +.b -bm +(default), +.b -bs , +and +.b -t +it is +.i submit.cf +if it exists, for all others it is +.i sendmail.cf . .ip \-b\fIx\fP Set operation mode to .i x . @@ -8932,6 +9967,7 @@ t Run in test mode v Just verify addresses, don't collect or deliver i Initialize the alias database p Print the mail queue +P Print overview over the mail queue (requires shared memory) h Print the persistent host status database H Purge expired entries from the persistent host status database .)b @@ -9048,10 +10084,40 @@ Try to process the queued up mail. If the time is given, a .i sendmail -will run through the queue at the specified interval -to deliver queued mail; -otherwise, it only runs once. -.ip \-q\fIXstring\fP +will start one or more processes to run through the queue(s) at the specified +time interval to deliver queued mail; otherwise, it only runs once. +Each of these processes acts on a workgroup. +These processes are also known as workgroup processes or WGP's for short. +Each workgroup is responsible for controlling the processing of one or +more queues; workgroups help manage the use of system resources by sendmail. +Each workgroup may have one or more children concurrently processing +queues depending on the setting of \fIMaxQueueChildren\fP. +.ip \-qp\fItime\fP +Similar to \-q with a time argument, +except that instead of periodically starting WGP's +sendmail starts persistent WGP's +that alternate between processing queues and sleeping. +The sleep time is specified by the time argument; it defaults to 1 second, +except that a WGP always sleeps at least 5 seconds if their queues were +empty in the previous run. +Persistent processes are managed by a queue control process (QCP). +The QCP is the parent process of the WGP's. +Typically the QCP will be the sendmail daemon (when started with \-bd or \-bD) +or a special process (named Queue control) (when started without \-bd or \-bD). +If a persistent WGP ceases to be active for some reason +another WGP will be started by the QCP for the same workgroup +in most cases. When a persistent WGP has core dumped, the debug flag +\fIno_persistent_restart\fP is set or the specific persistent WGP has been +restarted too many times already then the WGP will not be started again +and a message will be logged to this effect. +To stop (SIGTERM) or restart (SIGHUP) persistent WGP's the appropriate +signal should be sent to the QCP. The QCP will propagate the signal to all of +the WGP's and if appropriate restart the persistent WGP's. +.ip \-q\fIGname\fP +Run the jobs in the queue group +.i name +once. +.ip \-q[!]\fIXstring\fP Run the queue once, limiting the jobs to those matching .i Xstring . @@ -9068,6 +10134,7 @@ to limit based on sender. A particular queued job is accepted if one of the corresponding addresses contains the indicated .i string . +The optional ! character negates the condition tested. Multiple .i \-q\fIX\fP flags are permitted, @@ -9101,12 +10168,6 @@ The line will be deleted before sending. Any addresses in the argument vector will be deleted from the send list. -.ip "\-U" -Indicate that this is an initial User Agent submission. -This flag is deprecated. -Future releases will ignore this flag and -assume all submissions from the command line are -initial submissions. .ip "\-V envid" The indicated .i envid @@ -9139,14 +10200,7 @@ running as daemon. .+c "QUEUE FILE FORMATS" .pp This appendix describes the format of the queue files. -These files live in the directory defined by the -.b Q -option in the -.i sendmail.cf -file, usually -.i /var/spool/mqueue -or -.i /usr/spool/mqueue . +These files live in a queue directory. The individual qf, df, and xf files may be stored in separate .i qf/ , @@ -9156,28 +10210,15 @@ and subdirectories if they are present in the queue directory. .pp -To use multiple queues, -supply a value ending with an asterisk. -For example, -.i /var/spool/mqueue/q* -will use all of the directories or symbolic links to directories -beginning with `q' in -.i /var/spool/mqueue -as queue directories. -New messages will be randomly placed -into one of the queues. -Do not change the queue directory structure -while sendmail is running. -.pp All queue files have the name -\fIx\fP\|\fBf\fP\fIYMDhmsNPPPPP\fP +.i ttYMDhmsNNppppp where -.i YMDhmsNPPPPP +.i YMDhmsNNppppp is the .i id for this message and the -.i x +.i tt is a type. The individual letters in the .i id @@ -9195,37 +10236,43 @@ Encoded hour Encoded minute .ip s Encoded second -.ip N -Envelope number -.ip PPPPP -At least five digits of the process ID +.ip NN +Encoded envelope number +.ip ppppp +At least five decimal digits of the process ID .pp All files with the same id collectively define one message. -If memory-buffered files are available, -some of these files may never appear -on disk. +Due to the use of memory-buffered files, +some of these files may never appear on disk. .pp The types are: .nr ii 0.5i -.ip d -The data file. -The message body (excluding the header) is kept in this file. -.ip q +.ip qf The queue control file. This file contains the information necessary to process the job. -.ip t +.ip df +The data file. +The message body (excluding the header) is kept in this file. +Sometimes the df file is not stored in the same directory as the qf file; +in this case, +the qf file contains a `d' record which names the queue directory +that contains the df file. +.ip tf A temporary file. -These are an image of the +This is an image of the .b qf file when it is being rebuilt. It should be renamed to a .b qf file very quickly. -.ip x +.ip xf A transcript file, existing during the life of a session showing everything that happens during that session. +Sometimes the xf file must be generated before a queue group has been selected; +in this case, +the xf file will be stored in a directory of the default queue group. .pp The .b qf @@ -9239,7 +10286,7 @@ used to allow new binaries to read queue files created by older versions. Defaults to version zero. Must be the first line of the file if present. -For 8.10 the version number is 4. +For 8.12 the version number is 6. .ip A The information given by the AUTH= parameter of the .q "MAIL FROM:" @@ -9267,13 +10314,16 @@ is the name of the alias that expanded to this address The ``original recipient'', specified by the ORCPT= field in an ESMTP transaction. Used exclusively for Delivery Status Notifications. -It applies only to the immediately following `R' line. +It applies only to the following `R' line. +.ip r +The ``final recipient'' +used for Delivery Status Notifications. +It applies only to the following `R' line. .ip R A recipient address. This will normally be completely aliased, but is actually realiased when the job is processed. -There will be one line -for each recipient. +There will be one line for each recipient. Version 1 qf files also include a leading colon-terminated list of flags, which can be @@ -9319,6 +10369,10 @@ The total number of delivery attempts. .ip K The time (as seconds since January 1, 1970) of the last delivery attempt. +.ip d +If the df file is in a different directory than the qf file, +then a `d' record is present, +specifying the directory in which the df file resides. .ip I The i-number of the data file; this can be used to recover your mail queue @@ -9475,7 +10529,7 @@ replace it with a blank sheet for double-sided output. .\".sz 10 .\"Eric Allman .\".sp -.\"Version $Revision: 1.8 $ +.\"Version $Revision: 1.9 $ .\".ce 0 .bp 3 .ce diff --git a/gnu/usr.sbin/sendmail/editmap/Makefile b/gnu/usr.sbin/sendmail/editmap/Makefile index 98353cbb881..e5a553ad39d 100644 --- a/gnu/usr.sbin/sendmail/editmap/Makefile +++ b/gnu/usr.sbin/sendmail/editmap/Makefile @@ -1,17 +1,12 @@ -# $Sendmail: Makefile,v 1.1 2000/08/31 16:19:25 ca Exp $ +# $OpenBSD: Makefile,v 1.2 2001/09/11 19:02:48 millert Exp $ -SHELL= /bin/sh -BUILD= ./Build -OPTIONS= $(CONFIG) $(FLAGS) +PROG= editmap +MAN= editmap.8 +ENVDEF= -DNOT_SENDMAIL -all: FRC - $(SHELL) $(BUILD) $(OPTIONS) $@ -clean: FRC - $(SHELL) $(BUILD) $(OPTIONS) $@ -install: FRC - $(SHELL) $(BUILD) $(OPTIONS) $@ +WANT_LIBSM=1 +WANT_LIBSMDB=1 +WANT_LIBSMUTIL=1 -fresh: FRC - $(SHELL) $(BUILD) $(OPTIONS) -c - -FRC: +.include "../../Makefile.inc" +.include <bsd.prog.mk> diff --git a/gnu/usr.sbin/sendmail/include/libmilter/mfapi.h b/gnu/usr.sbin/sendmail/include/libmilter/mfapi.h index 25833e5b0c1..97a07b41974 100644 --- a/gnu/usr.sbin/sendmail/include/libmilter/mfapi.h +++ b/gnu/usr.sbin/sendmail/include/libmilter/mfapi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers. + * Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set @@ -7,7 +7,7 @@ * the sendmail distribution. * * - * $Sendmail: mfapi.h,v 8.13.4.12 2000/09/09 02:11:48 ca Exp $ + * $Sendmail: mfapi.h,v 8.28 2001/07/19 21:20:29 gshapiro Exp $ */ /* @@ -17,13 +17,14 @@ #ifndef _LIBMILTER_MFAPI_H # define _LIBMILTER_MFAPI_H 1 -# define LIBMILTER_API extern +# include <sys/socket.h> +# include "libmilter/mfdef.h" +# define LIBMILTER_API extern # include <sys/types.h> #ifndef _SOCK_ADDR -# include <sys/socket.h> # define _SOCK_ADDR struct sockaddr #endif /* ! _SOCK_ADDR */ @@ -42,24 +43,6 @@ typedef struct smfi_str *SMFICTX_PTR; typedef struct smfiDesc smfiDesc_str; typedef struct smfiDesc *smfiDesc_ptr; -#define SMFI_VERSION 2 /* version number */ - -/* -** What the filter might do -- values to be ORed together for -** smfiDesc.xxfi_flags. -*/ - -#define SMFIF_ADDHDRS 0x00000001L /* filter may add headers */ -#define SMFIF_CHGBODY 0x00000002L /* filter may replace body */ -#define SMFIF_MODBODY SMFIF_CHGBODY /* backwards compatible */ -#define SMFIF_ADDRCPT 0x00000004L /* filter may add recipients */ -#define SMFIF_DELRCPT 0x00000008L /* filter may delete recipients */ -#define SMFIF_CHGHDRS 0x00000010L /* filter may change/delete headers */ - -#define SMFI_V1_ACTS 0x0000000FL /* The actions of V1 filter */ -#define SMFI_V2_ACTS 0x0000001FL /* The actions of V2 filter */ -#define SMFI_CURR_ACTS SMFI_V2_ACTS /* The current version */ - /* ** Type which callbacks should return to indicate message status. ** This may take on one of the SMFIS_* values listed below. @@ -77,20 +60,11 @@ typedef int sfsistat; # define SM__P(X) __P(X) #endif /* __linux__ && __GNUC__ && __cplusplus && _GNUC_MINOR__ >= 8 */ -/* Some platforms don't define __P -- do it for them here: */ -#ifndef __P -# ifdef __STDC__ -# define __P(X) X -# else /* __STDC__ */ -# define __P(X) () -# endif /* __STDC__ */ -#endif /* __P */ - struct smfiDesc { char *xxfi_name; /* filter name */ int xxfi_version; /* version code -- do not change */ - u_long xxfi_flags; /* flags */ + unsigned long xxfi_flags; /* flags */ /* connection info filter */ sfsistat (*xxfi_connect) SM__P((SMFICTX *, char *, _SOCK_ADDR *)); @@ -111,7 +85,7 @@ struct smfiDesc sfsistat (*xxfi_eoh) SM__P((SMFICTX *)); /* body block */ - sfsistat (*xxfi_body) SM__P((SMFICTX *, u_char *, size_t)); + sfsistat (*xxfi_body) SM__P((SMFICTX *, unsigned char *, size_t)); /* end of message */ sfsistat (*xxfi_eom) SM__P((SMFICTX *)); @@ -130,6 +104,21 @@ LIBMILTER_API int smfi_settimeout __P((int)); LIBMILTER_API int smfi_setconn __P((char *)); LIBMILTER_API int smfi_stop __P((void)); +#define SMFI_VERSION 2 /* version number */ + +/* +** What the filter might do -- values to be ORed together for +** smfiDesc.xxfi_flags. +*/ + +#define SMFIF_NONE 0x00000000L /* no flags */ +#define SMFIF_ADDHDRS 0x00000001L /* filter may add headers */ +#define SMFIF_CHGBODY 0x00000002L /* filter may replace body */ +#define SMFIF_MODBODY SMFIF_CHGBODY /* backwards compatible */ +#define SMFIF_ADDRCPT 0x00000004L /* filter may add recipients */ +#define SMFIF_DELRCPT 0x00000008L /* filter may delete recipients */ +#define SMFIF_CHGHDRS 0x00000010L /* filter may change/delete headers */ + /* ** Continue processing message/connection. */ @@ -244,14 +233,14 @@ extern sfsistat xxfi_eoh __P((SMFICTX *)); */ /* body block */ -extern sfsistat xxfi_body __P((SMFICTX *, u_char *, size_t)); +extern sfsistat xxfi_body __P((SMFICTX *, unsigned char *, size_t)); /* ** xxfi_body(ctx, bodyp, bodylen) Invoked for each body chunk. There may ** be multiple body chunks passed to the filter. End-of-lines are ** represented as received from SMTP (normally Carriage-Return/Line-Feed). ** -** u_char *bodyp; Pointer to body data +** unsigned char *bodyp; Pointer to body data ** size_t bodylen; Length of body data */ @@ -370,7 +359,7 @@ LIBMILTER_API int smfi_delrcpt __P((SMFICTX *, char *)); ** not be deleted. */ -LIBMILTER_API int smfi_replacebody __P((SMFICTX *, u_char *, int)); +LIBMILTER_API int smfi_replacebody __P((SMFICTX *, unsigned char *, int)); /* ** Replace the body of the message. This routine may be called multiple diff --git a/gnu/usr.sbin/sendmail/include/libmilter/milter.h b/gnu/usr.sbin/sendmail/include/libmilter/milter.h index 6da5b9364ed..2dcbf97bb1c 100644 --- a/gnu/usr.sbin/sendmail/include/libmilter/milter.h +++ b/gnu/usr.sbin/sendmail/include/libmilter/milter.h @@ -7,70 +7,18 @@ * the sendmail distribution. * * - * $Sendmail: milter.h,v 8.24.16.10 2001/07/20 04:19:35 gshapiro Exp $ + * $Sendmail: milter.h,v 8.35 2001/06/27 21:46:44 gshapiro Exp $ */ /* -** MILTER.H -- Global definitions for mail filter and MTA. +** MILTER.H -- Global definitions for mail filter. */ #ifndef _LIBMILTER_MILTER_H # define _LIBMILTER_MILTER_H 1 -#include "libmilter/mfapi.h" #include "sendmail.h" - -/* Shared protocol constants */ -# define MILTER_LEN_BYTES 4 /* length of 32 bit integer in bytes */ -# define MILTER_OPTLEN (MILTER_LEN_BYTES * 3) /* length of options */ -# define MILTER_CHUNK_SIZE 65535 /* body chunk size */ - -/* address families */ -# define SMFIA_UNKNOWN 'U' /* unknown */ -# define SMFIA_UNIX 'L' /* unix/local */ -# define SMFIA_INET '4' /* inet */ -# define SMFIA_INET6 '6' /* inet6 */ - -/* commands: don't use anything smaller than ' ' */ -# define SMFIC_ABORT 'A' /* Abort */ -# define SMFIC_BODY 'B' /* Body chunk */ -# define SMFIC_CONNECT 'C' /* Connection information */ -# define SMFIC_MACRO 'D' /* Define macro */ -# define SMFIC_BODYEOB 'E' /* final body chunk (End) */ -# define SMFIC_HELO 'H' /* HELO/EHLO */ -# define SMFIC_HEADER 'L' /* Header */ -# define SMFIC_MAIL 'M' /* MAIL from */ -# define SMFIC_EOH 'N' /* EOH */ -# define SMFIC_OPTNEG 'O' /* Option negotiation */ -# define SMFIC_QUIT 'Q' /* QUIT */ -# define SMFIC_RCPT 'R' /* RCPT to */ - -/* actions (replies) */ -# define SMFIR_ADDRCPT '+' /* add recipient */ -# define SMFIR_DELRCPT '-' /* remove recipient */ -# define SMFIR_ACCEPT 'a' /* accept */ -# define SMFIR_REPLBODY 'b' /* replace body (chunk) */ -# define SMFIR_CONTINUE 'c' /* continue */ -# define SMFIR_DISCARD 'd' /* discard */ -# define SMFIR_CHGHEADER 'm' /* change header */ -# define SMFIR_PROGRESS 'p' /* progress */ -# define SMFIR_REJECT 'r' /* reject */ -# define SMFIR_TEMPFAIL 't' /* tempfail */ -# define SMFIR_ADDHEADER 'h' /* add header */ -# define SMFIR_REPLYCODE 'y' /* reply code etc */ - -/* What the MTA can send/filter wants in protocol */ -# define SMFIP_NOCONNECT 0x00000001L /* MTA should not send connect info */ -# define SMFIP_NOHELO 0x00000002L /* MTA should not send HELO info */ -# define SMFIP_NOMAIL 0x00000004L /* MTA should not send MAIL info */ -# define SMFIP_NORCPT 0x00000008L /* MTA should not send RCPT info */ -# define SMFIP_NOBODY 0x00000010L /* MTA should not send body */ -# define SMFIP_NOHDRS 0x00000020L /* MTA should not send headers */ -# define SMFIP_NOEOH 0x00000040L /* MTA should not send EOH */ - -# define SMFI_V1_PROT 0x0000003FL /* The protocol of V1 filter */ -# define SMFI_V2_PROT 0x0000007FL /* The protocol of V2 filter */ -# define SMFI_CURR_PROT SMFI_V2_PROT /* The current version */ +#include "libmilter/mfapi.h" /* socket and thread portability */ # include <pthread.h> @@ -97,7 +45,7 @@ struct smfi_str time_t ctx_timeout; /* timeout */ int ctx_state; /* state */ smfiDesc_ptr ctx_smfi; /* filter description */ - u_long ctx_pflags; /* protocol flags */ + unsigned long ctx_pflags; /* protocol flags */ char **ctx_mac_ptr[MAX_MACROS_ENTRIES]; char *ctx_mac_buf[MAX_MACROS_ENTRIES]; char *ctx_reply; /* reply code */ diff --git a/gnu/usr.sbin/sendmail/include/libsmdb/smdb.h b/gnu/usr.sbin/sendmail/include/libsmdb/smdb.h index 55fe20d7d19..5606381b223 100644 --- a/gnu/usr.sbin/sendmail/include/libsmdb/smdb.h +++ b/gnu/usr.sbin/sendmail/include/libsmdb/smdb.h @@ -1,22 +1,22 @@ /* -** Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers. -** All rights reserved. -** -** By using this file, you agree to the terms and conditions set -** forth in the LICENSE file which can be found at the top level of -** the sendmail distribution. -** -** $Sendmail: smdb.h,v 8.29.2.1.2.2 2000/10/05 22:23:55 gshapiro Exp $ -*/ + * Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + * $Sendmail: smdb.h,v 8.36 2001/05/18 14:55:56 ca Exp $ + * + */ #ifndef _SMDB_H_ # define _SMDB_H_ # include <sys/types.h> # include <sys/stat.h> -# ifndef __P -# include "sendmail/cdefs.h" -# endif /* __P */ +# include <sm/gen.h> +# include <sm/errstring.h> # ifndef NDBM # ifndef NEWDB @@ -36,28 +36,28 @@ ERROR NDBM or NEWDB must be defined. # endif /* NEWDB */ /* -** Some size constants +** Some size constants */ + #define SMDB_MAX_USER_NAME_LEN 1024 #define SMDB_MAX_NAME_LEN 1024 /* -** This file defines the abstraction for database lookups. It is pretty -** much a copy of the db2 interface with the exception that every function -** returns 0 on success and non-zero on failure. The non-zero return code -** is meaningful. +** This file defines the abstraction for database lookups. It is pretty +** much a copy of the db2 interface with the exception that every function +** returns 0 on success and non-zero on failure. The non-zero return code +** is meaningful. ** -** I'm going to put the function comments in this file since the interface -** MUST be the same for all inheritors of this interface. +** I'm going to put the function comments in this file since the interface +** MUST be the same for all inheritors of this interface. */ typedef struct database_struct SMDB_DATABASE; typedef struct cursor_struct SMDB_CURSOR; typedef struct entry_struct SMDB_DBENT; - -/* -** DB_CLOSE_FUNC -- close the database +/* +** DB_CLOSE_FUNC -- close the database ** ** Parameters: ** db -- The database to close. @@ -66,12 +66,11 @@ typedef struct entry_struct SMDB_DBENT; ** 0 - Success, otherwise errno. ** */ -typedef int (*db_close_func) __P((SMDB_DATABASE *db)); +typedef int (*db_close_func) __P((SMDB_DATABASE *db)); - -/* -** DB_DEL_FUNC -- removes a key and data pair from the database +/* +** DB_DEL_FUNC -- removes a key and data pair from the database ** ** Parameters: ** db -- The database to close. @@ -83,13 +82,12 @@ typedef int (*db_close_func) __P((SMDB_DATABASE *db)); ** 0 - Success, otherwise errno. ** */ -typedef int (*db_del_func) __P((SMDB_DATABASE *db, - SMDB_DBENT *key, u_int flags)); +typedef int (*db_del_func) __P((SMDB_DATABASE *db, + SMDB_DBENT *key, unsigned int flags)); - -/* -** DB_FD_FUNC -- Returns a pointer to a file used for the database. +/* +** DB_FD_FUNC -- Returns a pointer to a file used for the database. ** ** Parameters: ** db -- The database to close. @@ -99,12 +97,11 @@ typedef int (*db_del_func) __P((SMDB_DATABASE *db, ** 0 - Success, otherwise errno. ** */ -typedef int (*db_fd_func) __P((SMDB_DATABASE *db, int* fd)); +typedef int (*db_fd_func) __P((SMDB_DATABASE *db, int* fd)); - -/* -** DB_GET_FUNC -- Gets the data associated with a key. +/* +** DB_GET_FUNC -- Gets the data associated with a key. ** ** Parameters: ** db -- The database to close. @@ -117,14 +114,13 @@ typedef int (*db_fd_func) __P((SMDB_DATABASE *db, int* fd)); ** 0 - Success, otherwise errno. ** */ + typedef int (*db_get_func) __P((SMDB_DATABASE *db, SMDB_DBENT *key, - SMDB_DBENT *data, u_int flags)); - + SMDB_DBENT *data, unsigned int flags)); - -/* -** DB_PUT_FUNC -- Sets some data according to the key. +/* +** DB_PUT_FUNC -- Sets some data according to the key. ** ** Parameters: ** db -- The database to close. @@ -139,13 +135,13 @@ typedef int (*db_get_func) __P((SMDB_DATABASE *db, ** 0 - Success, otherwise errno. ** */ + typedef int (*db_put_func) __P((SMDB_DATABASE *db, SMDB_DBENT *key, - SMDB_DBENT *data, u_int flags)); + SMDB_DBENT *data, unsigned int flags)); - -/* -** DB_SYNC_FUNC -- Flush any cached information to disk. +/* +** DB_SYNC_FUNC -- Flush any cached information to disk. ** ** Parameters: ** db -- The database to sync. @@ -155,11 +151,11 @@ typedef int (*db_put_func) __P((SMDB_DATABASE *db, ** 0 - Success, otherwise errno. ** */ -typedef int (*db_sync_func) __P((SMDB_DATABASE *db, u_int flags)); - -/* -** DB_SET_OWNER_FUNC -- Set the owner and group of the database files. +typedef int (*db_sync_func) __P((SMDB_DATABASE *db, unsigned int flags)); + +/* +** DB_SET_OWNER_FUNC -- Set the owner and group of the database files. ** ** Parameters: ** db -- The database to set. @@ -170,12 +166,11 @@ typedef int (*db_sync_func) __P((SMDB_DATABASE *db, u_int flags)); ** 0 - Success, otherwise errno. ** */ -typedef int (*db_set_owner_func) __P((SMDB_DATABASE *db, uid_t uid, - gid_t gid)); - -/* -** DB_CURSOR -- Obtain a cursor for sequential access +typedef int (*db_set_owner_func) __P((SMDB_DATABASE *db, uid_t uid, gid_t gid)); + +/* +** DB_CURSOR -- Obtain a cursor for sequential access ** ** Parameters: ** db -- The database to use. @@ -186,8 +181,9 @@ typedef int (*db_set_owner_func) __P((SMDB_DATABASE *db, uid_t uid, ** 0 - Success, otherwise errno. ** */ + typedef int (*db_cursor_func) __P((SMDB_DATABASE *db, - SMDB_CURSOR **cursor, u_int flags)); + SMDB_CURSOR **cursor, unsigned int flags)); typedef int (*db_lockfd_func) __P((SMDB_DATABASE *db)); @@ -204,11 +200,8 @@ struct database_struct db_lockfd_func smdb_lockfd; void *smdb_impl; }; - - - -/* -** DB_CURSOR_CLOSE -- Close a cursor +/* +** DB_CURSOR_CLOSE -- Close a cursor ** ** Parameters: ** cursor -- The cursor to close. @@ -217,11 +210,11 @@ struct database_struct ** 0 - Success, otherwise errno. ** */ + typedef int (*db_cursor_close_func) __P((SMDB_CURSOR *cursor)); - -/* -** DB_CURSOR_DEL -- Delete the key/value pair of this cursor +/* +** DB_CURSOR_DEL -- Delete the key/value pair of this cursor ** ** Parameters: ** cursor -- The cursor. @@ -231,11 +224,12 @@ typedef int (*db_cursor_close_func) __P((SMDB_CURSOR *cursor)); ** 0 - Success, otherwise errno. ** */ -typedef int (*db_cursor_del_func) __P((SMDB_CURSOR *cursor, u_int flags)); - -/* -** DB_CURSOR_GET -- Get the key/value of this cursor. +typedef int (*db_cursor_del_func) __P((SMDB_CURSOR *cursor, + unsigned int flags)); + +/* +** DB_CURSOR_GET -- Get the key/value of this cursor. ** ** Parameters: ** cursor -- The cursor. @@ -250,22 +244,23 @@ typedef int (*db_cursor_del_func) __P((SMDB_CURSOR *cursor, u_int flags)); ** database is hit. ** */ + typedef int (*db_cursor_get_func) __P((SMDB_CURSOR *cursor, SMDB_DBENT *key, SMDB_DBENT *data, - u_int flags)); + unsigned int flags)); /* -** Flags for DB_CURSOR_GET +** Flags for DB_CURSOR_GET */ + #define SMDB_CURSOR_GET_FIRST 0 #define SMDB_CURSOR_GET_LAST 1 #define SMDB_CURSOR_GET_NEXT 2 #define SMDB_CURSOR_GET_RANGE 3 - -/* -** DB_CURSOR_PUT -- Put the key/value at this cursor. +/* +** DB_CURSOR_PUT -- Put the key/value at this cursor. ** ** Parameters: ** cursor -- The cursor. @@ -277,10 +272,11 @@ typedef int (*db_cursor_get_func) __P((SMDB_CURSOR *cursor, ** 0 - Success, otherwise errno. ** */ + typedef int (*db_cursor_put_func) __P((SMDB_CURSOR *cursor, SMDB_DBENT *key, SMDB_DBENT *data, - u_int flags)); + unsigned int flags)); @@ -296,9 +292,9 @@ struct cursor_struct struct database_params_struct { - u_int smdbp_num_elements; - u_int smdbp_cache_size; - bool smdbp_allow_dup; + unsigned int smdbp_num_elements; + unsigned int smdbp_cache_size; + bool smdbp_allow_dup; }; typedef struct database_params_struct SMDB_DBPARAMS; @@ -319,10 +315,10 @@ struct entry_struct }; typedef char *SMDB_DBTYPE; -typedef u_int SMDB_FLAG; +typedef unsigned int SMDB_FLAG; /* -** These are types of databases. +** These are types of databases. */ # define SMDB_TYPE_DEFAULT NULL @@ -335,8 +331,9 @@ typedef u_int SMDB_FLAG; # define SMDB_TYPE_NDBM_LEN 4 /* -** These are flags +** These are flags */ + /* Flags for put */ # define SMDBF_NO_OVERWRITE 0x00000001 # define SMDBF_ALLOW_DUP 0x00000002 diff --git a/gnu/usr.sbin/sendmail/include/sendmail/mailstats.h b/gnu/usr.sbin/sendmail/include/sendmail/mailstats.h index 5dc69b563a9..600581ec278 100644 --- a/gnu/usr.sbin/sendmail/include/sendmail/mailstats.h +++ b/gnu/usr.sbin/sendmail/include/sendmail/mailstats.h @@ -10,7 +10,7 @@ * the sendmail distribution. * * - * $Sendmail: mailstats.h,v 8.13 1999/05/22 02:29:10 ca Exp $ + * $Sendmail: mailstats.h,v 8.17 2001/09/04 22:42:40 ca Exp $ */ #define STAT_VERSION 3 diff --git a/gnu/usr.sbin/sendmail/include/sendmail/pathnames.h b/gnu/usr.sbin/sendmail/include/sendmail/pathnames.h index 8e55178ff45..361b4b7e4d2 100644 --- a/gnu/usr.sbin/sendmail/include/sendmail/pathnames.h +++ b/gnu/usr.sbin/sendmail/include/sendmail/pathnames.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. + * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. @@ -9,28 +9,54 @@ * the sendmail distribution. * * - * $Sendmail: pathnames.h,v 8.16.8.8 2000/09/28 21:26:39 gshapiro Exp $ + * $Sendmail: pathnames.h,v 8.35 2001/03/23 22:09:44 ca Exp $ */ +#ifndef SM_PATHNAMES_H +# define SM_PATHNAMES_H -# ifndef _PATH_SENDMAILCF -# if defined(USE_VENDOR_CF_PATH) && defined(_PATH_VENDOR_CF) -# define _PATH_SENDMAILCF _PATH_VENDOR_CF -# else /* defined(USE_VENDOR_CF_PATH) && defined(_PATH_VENDOR_CF) */ -# define _PATH_SENDMAILCF "/etc/mail/sendmail.cf" -# endif /* defined(USE_VENDOR_CF_PATH) && defined(_PATH_VENDOR_CF) */ -# endif /* ! _PATH_SENDMAILCF */ -# ifndef _PATH_SENDMAILPID -# ifdef BSD4_4 -# define _PATH_SENDMAILPID "/var/run/sendmail.pid" -# else /* BSD4_4 */ -# define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" -# endif /* BSD4_4 */ -# endif /* ! _PATH_SENDMAILPID */ +# ifndef _PATH_SENDMAILCF +# if defined(USE_VENDOR_CF_PATH) && defined(_PATH_VENDOR_CF) +# define _PATH_SENDMAILCF _PATH_VENDOR_CF +# else /* defined(USE_VENDOR_CF_PATH) && defined(_PATH_VENDOR_CF) */ +# define _PATH_SENDMAILCF "/etc/mail/sendmail.cf" +# endif /* defined(USE_VENDOR_CF_PATH) && defined(_PATH_VENDOR_CF) */ +# endif /* ! _PATH_SENDMAILCF */ -# ifndef _PATH_HOSTS -# define _PATH_HOSTS "/etc/hosts" -# endif /* ! _PATH_HOSTS */ +# ifndef _PATH_SENDMAILPID +# ifdef BSD4_4 +# define _PATH_SENDMAILPID "/var/run/sendmail.pid" +# else /* BSD4_4 */ +# define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" +# endif /* BSD4_4 */ +# endif /* ! _PATH_SENDMAILPID */ +# ifndef _PATH_SENDMAIL +# define _PATH_SENDMAIL "/usr/lib/sendmail" +# endif /* ! _PATH_SENDMAIL */ +# ifndef _PATH_MAILDIR +# define _PATH_MAILDIR "/var/spool/mail" +# endif /* ! _PATH_MAILDIR */ + +# ifndef _PATH_LOCTMP +# define _PATH_LOCTMP "/tmp/local.XXXXXX" +# endif /* ! _PATH_LOCTMP */ + +# ifndef _PATH_HOSTS +# define _PATH_HOSTS "/etc/hosts" +# endif /* ! _PATH_HOSTS */ + + + +# ifndef _DIR_SENDMAILCF +# define _DIR_SENDMAILCF "/etc/mail/" +# endif /* ! _DIR_SENDMAILCF */ + +# define SM_GET_RIGHT_CF 0 /* get "right" .cf */ +# define SM_GET_SENDMAIL_CF 1 /* always use sendmail.cf */ +# define SM_GET_SUBMIT_CF 2 /* always use submit.cf */ + +extern char *getcfname __P((int, int, int, char *)); +#endif /* ! SM_PATHNAMES_H */ diff --git a/gnu/usr.sbin/sendmail/include/sendmail/sendmail.h b/gnu/usr.sbin/sendmail/include/sendmail/sendmail.h index adea3597b1c..9f8435d102b 100644 --- a/gnu/usr.sbin/sendmail/include/sendmail/sendmail.h +++ b/gnu/usr.sbin/sendmail/include/sendmail/sendmail.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. + * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 @@ -10,23 +10,18 @@ * the sendmail distribution. * * - * $Sendmail: sendmail.h,v 8.34.4.8 2001/06/01 05:06:51 gshapiro Exp $ + * $Sendmail: sendmail.h,v 8.67 2001/09/08 01:20:57 gshapiro Exp $ */ /* ** SENDMAIL.H -- Global definitions for sendmail. */ -#if SFIO -# include <sfio/stdio.h> -#else /* SFIO */ -# include <stdio.h> -#endif /* SFIO */ -#include <string.h> +#include <stdio.h> +#include <sm/bitops.h> +#include <sm/io.h> +#include <sm/string.h> #include "conf.h" -#include "sendmail/errstring.h" -#include "sendmail/useful.h" - /********************************************************************** ** Table sizes, etc.... @@ -37,45 +32,6 @@ #endif /* ! MAXMAILERS */ /* -** Data structure for bit maps. -** -** Each bit in this map can be referenced by an ascii character. -** This is 256 possible bits, or 32 8-bit bytes. -*/ - -#define BITMAPBITS 256 /* number of bits in a bit map */ -#define BYTEBITS 8 /* number of bits in a byte */ -#define BITMAPBYTES (BITMAPBITS / BYTEBITS) /* number of bytes in bit map */ - -/* internal macros */ -#define _BITWORD(bit) ((bit) / (BYTEBITS * sizeof (int))) -#define _BITBIT(bit) ((unsigned int)1 << ((bit) % (BYTEBITS * sizeof (int)))) - -typedef unsigned int BITMAP256[BITMAPBYTES / sizeof (int)]; - -/* properly case and truncate bit */ -#define bitidx(bit) ((unsigned int) (bit) & 0xff) - -/* test bit number N */ -#define bitnset(bit, map) ((map)[_BITWORD(bit)] & _BITBIT(bit)) - -/* set bit number N */ -#define setbitn(bit, map) (map)[_BITWORD(bit)] |= _BITBIT(bit) - -/* clear bit number N */ -#define clrbitn(bit, map) (map)[_BITWORD(bit)] &= ~_BITBIT(bit) - -/* clear an entire bit map */ -#define clrbitmap(map) memset((char *) map, '\0', BITMAPBYTES) - - -/* -** Utility macros -*/ - -/* return number of bytes left in a buffer */ -#define SPACELEFT(buf, ptr) (sizeof buf - ((ptr) - buf)) -/* ** Flags passed to safefile/safedirpath. */ @@ -85,7 +41,7 @@ typedef unsigned int BITMAP256[BITMAPBYTES / sizeof (int)]; #define SFF_ROOTOK 0x00000004L /* ok for root to own this file */ #define SFF_RUNASREALUID 0x00000008L /* if no ctladdr, run as real uid */ #define SFF_NOPATHCHECK 0x00000010L /* don't bother checking dir path */ -#define SFF_SETUIDOK 0x00000020L /* setuid files are ok */ +#define SFF_SETUIDOK 0x00000020L /* set-user-ID files are ok */ #define SFF_CREAT 0x00000040L /* ok to create file if necessary */ #define SFF_REGONLY 0x00000080L /* regular files only */ #define SFF_SAFEDIRPATH 0x00000100L /* no writable directories allowed */ @@ -108,6 +64,7 @@ typedef unsigned int BITMAP256[BITMAPBYTES / sizeof (int)]; extern int safefile __P((char *, UID_T, GID_T, char *, long, int, struct stat *)); extern int safedirpath __P((char *, UID_T, GID_T, char *, long, int, int)); extern int safeopen __P((char *, int, int, long)); +extern SM_FILE_T*safefopen __P((char *, int, int, long)); extern int dfopen __P((char *, int, int, long)); extern bool filechanged __P((char *, int, struct stat *)); @@ -116,6 +73,7 @@ extern bool filechanged __P((char *, int, struct stat *)); ** ** Hopefully nobody uses these. */ + #define DBS_SAFE 0 #define DBS_ASSUMESAFECHOWN 1 #define DBS_GROUPWRITABLEDIRPATHSAFE 2 @@ -144,42 +102,33 @@ extern bool filechanged __P((char *, int, struct stat *)); #define DBS_HELPFILEINUNSAFEDIRPATH 25 #define DBS_FORWARDFILEINUNSAFEDIRPATHSAFE 26 #define DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE 27 -#define DBS_RUNPROGRAMINUNSAFEDIRPATH 28 /* Not used yet */ +#define DBS_RUNPROGRAMINUNSAFEDIRPATH 28 #define DBS_RUNWRITABLEPROGRAM 29 #define DBS_INCLUDEFILEINUNSAFEDIRPATH 30 #define DBS_NONROOTSAFEADDR 31 #define DBS_TRUSTSTICKYBIT 32 #define DBS_DONTWARNFORWARDFILEINUNSAFEDIRPATH 33 #define DBS_INSUFFICIENTENTROPY 34 -#if _FFR_UNSAFE_SASL -# define DBS_GROUPREADABLESASLFILE 35 -#endif /* _FFR_UNSAFE_SASL */ -#if _FFR_UNSAFE_WRITABLE_INCLUDE -# define DBS_GROUPWRITABLEFORWARDFILE 36 -# define DBS_GROUPWRITABLEINCLUDEFILE 37 -# define DBS_WORLDWRITABLEFORWARDFILE 38 -# define DBS_WORLDWRITABLEINCLUDEFILE 39 -#endif /* _FFR_UNSAFE_WRITABLE_INCLUDE */ +#define DBS_GROUPREADABLESASLDBFILE 35 +#define DBS_GROUPWRITABLESASLDBFILE 36 +#define DBS_GROUPWRITABLEFORWARDFILE 37 +#define DBS_GROUPWRITABLEINCLUDEFILE 38 +#define DBS_WORLDWRITABLEFORWARDFILE 39 +#define DBS_WORLDWRITABLEINCLUDEFILE 40 +#define DBS_GROUPREADABLEKEYFILE 41 +#if _FFR_GROUPREADABLEAUTHINFOFILE +# define DBS_GROUPREADABLEAUTHINFOFILE 42 +#endif /* _FFR_GROUPREADABLEAUTHINFOFILE */ /* struct defining such things */ struct dbsval { - char *dbs_name; /* name of DontBlameSendmail flag */ - u_char dbs_flag; /* numeric level */ + char *dbs_name; /* name of DontBlameSendmail flag */ + unsigned char dbs_flag; /* numeric level */ }; -#if _FFR_DPRINTF -extern void dprintf __P((const char *, ...)); -extern int dflush __P((void)); -#else /* _FFR_DPRINTF */ -#define dprintf printf -#define dflush() fflush(stdout) -#endif /* _FFR_DPRINTF */ - -extern int sm_snprintf __P((char *, size_t, const char *, ...)); -extern int sm_vsnprintf __P((char *, size_t, const char *, va_list)); -extern char *quad_to_string __P((QUAD_T)); - -extern size_t strlcpy __P((char *, const char *, size_t)); -extern size_t strlcat __P((char *, const char *, size_t)); +/* Flags for submitmode */ +#define SUBMIT_UNKNOWN 0x0000 /* unknown agent type */ +#define SUBMIT_MTA 0x0001 /* act like a message transfer agent */ +#define SUBMIT_MSA 0x0002 /* act like a message submission agent */ diff --git a/gnu/usr.sbin/sendmail/libmilter/Makefile b/gnu/usr.sbin/sendmail/libmilter/Makefile index 01400cccf1c..3e5a650668c 100644 --- a/gnu/usr.sbin/sendmail/libmilter/Makefile +++ b/gnu/usr.sbin/sendmail/libmilter/Makefile @@ -1,7 +1,9 @@ -# $OpenBSD: Makefile,v 1.2 2000/04/02 19:48:31 millert Exp $ +# $OpenBSD: Makefile,v 1.3 2001/09/11 19:02:49 millert Exp $ -LIB= libmilter -SRCS= main.c engine.c listener.c handler.c comm.c smfi.c signal.c sm_gethost.c +LIB= milter +SRCS= main.c engine.c listener.c handler.c comm.c smfi.c signal.c \ + sm_gethost.c +CPPFLAGS+= -pthread # This is not a library that gets installed so only build the .a version # In the future we may wish to install it to ease the use of external filters. diff --git a/gnu/usr.sbin/sendmail/libmilter/README b/gnu/usr.sbin/sendmail/libmilter/README index aacaadfa5bf..fed06305d78 100644 --- a/gnu/usr.sbin/sendmail/libmilter/README +++ b/gnu/usr.sbin/sendmail/libmilter/README @@ -9,17 +9,11 @@ through reference to a sample filter which is attached at the end of this file. It is necessary to first build libmilter.a, which can be done by issuing the './Build' command in SRCDIR/libmilter . -NOTE: Both libmilter and the callouts in sendmail are marked as an FFR (For -Future Release). If you intend to use them in 8.11.X, you must compiled -both libmilter and sendmail with -D_FFR_MILTER defined. You can do this by -adding the following to your devtools/Site/site.config.m4 file: +NOTE: If you intend to use filters in sendmail, you must compile sendmail +with -DMILTER defined. You can do this by adding the following to +your devtools/Site/site.config.m4 file: - dnl Milter - APPENDDEF(`conf_sendmail_ENVDEF', `-D_FFR_MILTER=1') - APPENDDEF(`conf_libmilter_ENVDEF', `-D_FFR_MILTER=1') - -You will also need to define _FFR_MILTER when building your .cf file using -m4. + APPENDDEF(`conf_sendmail_ENVDEF', `-DMILTER') +-------------------+ | BUILDING A FILTER | @@ -29,14 +23,14 @@ The following command presumes that the sample code from the end of this README is saved to a file named 'sample.c' and built in the local platform- specific build subdirectory (SRCDIR/obj.*/libmilter). - cc -I../../sendmail -I../../include -o sample sample.c libmilter.a ../libsmutil/libsmutil.a -pthread + cc -I../../sendmail -I../../include -o sample sample.c libmilter.a ../libsm/libsm.a -pthread It is recommended that you build your filters in a location outside of the sendmail source tree. Modify the compiler include references (-I) and the library locations accordingly. Also, some operating systems may require additional libraries. For example, SunOS 5.X requires '-lresolv --lsocket -lnsl'. Depending on your OS you may need a library instead -of the option -pthread, e.g., -lpthread. +-lsocket -lnsl'. Depending on your operating system you may need a library +instead of the option -pthread, e.g., -lpthread. Filters must be thread-safe! Many operating systems now provide support for POSIX threads in the standard C libraries. The compiler flag to link with @@ -83,7 +77,7 @@ Finally, you can override the default timeouts used by sendmail when talking to the filters using the T= equate. There are four fields inside of the T= equate: -Letter Meaning +Letter Meaning C Timeout for connecting to a filter (if 0, use system timeout) S Timeout for sending information from the MTA to a filter R Timeout for reading reply from the filter @@ -94,7 +88,7 @@ Note the separator between each is a ';' as a ',' already separates equates and therefore can't separate timeouts. The default values (if not set in the config) are: -T=C:0m;S:10s;R:10s;E:5m +T=C:5m;S:10s;R:10s;E:5m where 's' is seconds and 'm' is minutes. @@ -182,6 +176,30 @@ the logging level of sendmail can be raised with the LogLevel option. See the sendmail(8) manual page for more information. ++--------------+ +| REQUIREMENTS | ++--------------+ + +libmilter requires pthread support in the operating system. Moreover, it +requires that the library functions it uses are thread safe; which is true +for the operating systems libmilter has been developed and tested on. On +some operating systems this requires special compile time options (e.g., +not just -pthread). libmilter is currently known to work on (modulo problems +in the pthread support of some specific versions): + +FreeBSD 3.x, 4.x +SunOS 5.x (x >= 5) +AIX 4.3.x +HP UX 11.x +Linux (recent versions/distributions) + +libmilter is currently not supported on: + +IRIX 6.x +Ultrix + +Feedback about problems (and possible fixes) is welcome. + +--------------------------+ | SOURCE FOR SAMPLE FILTER | +--------------------------+ @@ -201,14 +219,11 @@ below to verify the functions are thread safe. #include "libmilter/mfapi.h" +#ifndef true typedef int bool; - -#ifndef FALSE -# define FALSE 0 -#endif /* ! FALSE*/ -#ifndef TRUE -# define TRUE 1 -#endif /* ! TRUE*/ +# define false 0 +# define true 1 +#endif /* ! true */ struct mlfiPriv { @@ -295,7 +310,7 @@ mlfi_body(ctx, bodyp, bodylen) if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0) { /* write failed */ - (void) mlfi_cleanup(ctx, FALSE); + (void) mlfi_cleanup(ctx, false); return SMFIS_TEMPFAIL; } @@ -307,7 +322,7 @@ sfsistat mlfi_eom(ctx) SMFICTX *ctx; { - return mlfi_cleanup(ctx, TRUE); + return mlfi_cleanup(ctx, true); } sfsistat @@ -321,7 +336,7 @@ sfsistat mlfi_abort(ctx) SMFICTX *ctx; { - return mlfi_cleanup(ctx, FALSE); + return mlfi_cleanup(ctx, false); } sfsistat @@ -349,7 +364,7 @@ mlfi_cleanup(ctx, ok) { /* add a header to the message announcing our presence */ if (gethostname(host, sizeof host) < 0) - strlcpy(host, "localhost", sizeof host); + snprintf(host, sizeof host, "localhost"); p = strrchr(priv->mlfi_fname, '/'); if (p == NULL) p = priv->mlfi_fname; @@ -426,4 +441,4 @@ main(argc, argv) /* eof */ -$Revision: 1.7 $, Last updated $Date: 2001/08/01 01:01:40 $ +$Revision: 1.8 $, Last updated $Date: 2001/09/11 19:02:49 $ diff --git a/gnu/usr.sbin/sendmail/libmilter/comm.c b/gnu/usr.sbin/sendmail/libmilter/comm.c index 692ec2e84a4..4d8baaa9dd3 100644 --- a/gnu/usr.sbin/sendmail/libmilter/comm.c +++ b/gnu/usr.sbin/sendmail/libmilter/comm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers. + * Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set @@ -8,17 +8,16 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: comm.c,v 8.30.4.6 2000/10/05 22:44:01 gshapiro Exp $"; -#endif /* ! lint */ +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: comm.c,v 8.43 2001/07/20 20:33:07 ca Exp $") -#if _FFR_MILTER #include "libmilter.h" +#include <sm/errstring.h> -#define FD_Z FD_ZERO(&readset); \ - FD_SET((u_int) sd, &readset); \ - FD_ZERO(&excset); \ - FD_SET((u_int) sd, &excset) +#define FD_Z FD_ZERO(&readset); \ + FD_SET((unsigned int) sd, &readset); \ + FD_ZERO(&excset); \ + FD_SET((unsigned int) sd, &excset) /* ** MI_RD_CMD -- read a command @@ -77,7 +76,7 @@ mi_rd_cmd(sd, timeout, cmd, rlen, name) { smi_log(SMI_LOG_ERR, "%s, mi_rd_cmd: read returned %d: %s", - name, len, strerror(errno)); + name, len, sm_errstring(errno)); *cmd = SMFIC_RECVERR; return NULL; } @@ -100,7 +99,7 @@ mi_rd_cmd(sd, timeout, cmd, rlen, name) { smi_log(SMI_LOG_ERR, "%s: mi_rd_cmd: select returned %d: %s", - name, ret, strerror(errno)); + name, ret, sm_errstring(errno)); *cmd = SMFIC_RECVERR; return NULL; } @@ -116,7 +115,11 @@ mi_rd_cmd(sd, timeout, cmd, rlen, name) *cmd = SMFIC_TOOBIG; return NULL; } +#if _FFR_ADD_NULL + buf = malloc(expl + 1); +#else /* _FFR_ADD_NULL */ buf = malloc(expl); +#endif /* _FFR_ADD_NULL */ if (buf == NULL) { *cmd = SMFIC_MALLOC; @@ -137,7 +140,7 @@ mi_rd_cmd(sd, timeout, cmd, rlen, name) { smi_log(SMI_LOG_ERR, "%s: mi_rd_cmd: read returned %d: %s", - name, len, strerror(errno)); + name, len, sm_errstring(errno)); ret = -1; break; } @@ -156,6 +159,10 @@ mi_rd_cmd(sd, timeout, cmd, rlen, name) if (len >= expl - i) { *rlen = expl; +#if _FFR_ADD_NULL + /* makes life simpler for common string routines */ + buf[expl] = '\0'; +#endif /* _FFR_ADD_NULL */ return buf; } i += len; @@ -175,7 +182,7 @@ mi_rd_cmd(sd, timeout, cmd, rlen, name) { smi_log(SMI_LOG_ERR, "%s: mi_rd_cmd: select returned %d: %s", - name, ret, strerror(save_errno)); + name, ret, sm_errstring(save_errno)); *cmd = SMFIC_RECVERR; return NULL; } @@ -222,7 +229,7 @@ mi_wr_cmd(sd, timeout, cmd, buf, len) do { FD_ZERO(&wrtset); - FD_SET((u_int) sd, &wrtset); + FD_SET((unsigned int) sd, &wrtset); if ((ret = select(sd + 1, NULL, &wrtset, NULL, timeout)) == 0) return MI_FAILURE; } while (ret < 0 && errno == EINTR); @@ -248,7 +255,7 @@ mi_wr_cmd(sd, timeout, cmd, buf, len) do { FD_ZERO(&wrtset); - FD_SET((u_int) sd, &wrtset); + FD_SET((unsigned int) sd, &wrtset); if ((ret = select(sd + 1, NULL, &wrtset, NULL, timeout)) == 0) return MI_FAILURE; } while (ret < 0 && errno == EINTR); @@ -264,4 +271,3 @@ mi_wr_cmd(sd, timeout, cmd, buf, len) } return MI_SUCCESS; } -#endif /* _FFR_MILTER */ diff --git a/gnu/usr.sbin/sendmail/libmilter/engine.c b/gnu/usr.sbin/sendmail/libmilter/engine.c index 3ecbe6944f0..4b8cb008644 100644 --- a/gnu/usr.sbin/sendmail/libmilter/engine.c +++ b/gnu/usr.sbin/sendmail/libmilter/engine.c @@ -8,13 +8,10 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: engine.c,v 8.67.4.17 2001/01/22 19:00:16 gshapiro Exp $"; -#endif /* ! lint */ +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: engine.c,v 8.94 2001/08/14 12:49:45 ca Exp $") -#if _FFR_MILTER #include "libmilter.h" -#include "sendmail/useful.h" #if NETINET || NETINET6 # include <arpa/inet.h> @@ -118,13 +115,16 @@ static int dec_arg2 __P((char *, size_t, char **, char **)); ** is set in the NX_* value ** this function is coded in trans_ok(), see below. */ + #define MASK(x) (0x0001 << (x)) /* generate a bit "mask" for a state */ #define NX_INIT (MASK(ST_OPTS)) #define NX_OPTS (MASK(ST_CONN)) #define NX_CONN (MASK(ST_HELO) | MASK(ST_MAIL)) #define NX_HELO (MASK(ST_HELO) | MASK(ST_MAIL)) #define NX_MAIL (MASK(ST_RCPT) | MASK(ST_ABRT)) -#define NX_RCPT (MASK(ST_HDRS) | MASK(ST_EOHS) | MASK(ST_RCPT) | MASK(ST_ABRT)) +#define NX_RCPT (MASK(ST_HDRS) | MASK(ST_EOHS) | \ + MASK(ST_BODY) | MASK(ST_ENDM) | \ + MASK(ST_RCPT) | MASK(ST_ABRT)) #define NX_HDRS (MASK(ST_EOHS) | MASK(ST_HDRS) | MASK(ST_ABRT)) #define NX_EOHS (MASK(ST_BODY) | MASK(ST_ENDM) | MASK(ST_ABRT)) #define NX_BODY (MASK(ST_ENDM) | MASK(ST_BODY) | MASK(ST_ABRT)) @@ -217,7 +217,7 @@ mi_engine(ctx) if (mi_stop() == MILTER_ABRT) { if (ctx->ctx_dbg > 3) - dprintf("[%d] milter_abort\n", + sm_dprintf("[%d] milter_abort\n", (int) ctx->ctx_id); ret = MI_FAILURE; break; @@ -227,7 +227,7 @@ mi_engine(ctx) cmd < SMFIC_VALIDCMD) { if (ctx->ctx_dbg > 5) - dprintf("[%d] mi_engine: mi_rd_cmd error (%x)\n", + sm_dprintf("[%d] mi_engine: mi_rd_cmd error (%x)\n", (int) ctx->ctx_id, (int) cmd); /* @@ -240,7 +240,7 @@ mi_engine(ctx) break; } if (ctx->ctx_dbg > 4) - dprintf("[%d] got cmd '%c' len %d\n", + sm_dprintf("[%d] got cmd '%c' len %d\n", (int) ctx->ctx_id, cmd, len); for (i = 0; i < ncmds; i++) { @@ -251,7 +251,7 @@ mi_engine(ctx) { /* unknown command */ if (ctx->ctx_dbg > 1) - dprintf("[%d] cmd '%c' unknown\n", + sm_dprintf("[%d] cmd '%c' unknown\n", (int) ctx->ctx_id, cmd); ret = MI_FAILURE; break; @@ -260,7 +260,7 @@ mi_engine(ctx) { /* stop for now */ if (ctx->ctx_dbg > 1) - dprintf("[%d] cmd '%c' not impl\n", + sm_dprintf("[%d] cmd '%c' not impl\n", (int) ctx->ctx_id, cmd); ret = MI_FAILURE; break; @@ -269,14 +269,14 @@ mi_engine(ctx) /* is new state ok? */ newstate = cmds[i].cm_next; if (ctx->ctx_dbg > 5) - dprintf("[%d] cur %x new %x nextmask %x\n", + sm_dprintf("[%d] cur %x new %x nextmask %x\n", (int) ctx->ctx_id, curstate, newstate, next_states[curstate]); if (newstate != ST_NONE && !trans_ok(curstate, newstate)) { if (ctx->ctx_dbg > 1) - dprintf("[%d] abort: cur %d (%x) new %d (%x) next %x\n", + sm_dprintf("[%d] abort: cur %d (%x) new %d (%x) next %x\n", (int) ctx->ctx_id, curstate, MASK(curstate), newstate, MASK(newstate), @@ -337,7 +337,7 @@ mi_engine(ctx) else if (r == _SMFIS_ABORT) { if (ctx->ctx_dbg > 5) - dprintf("[%d] function returned abort\n", + sm_dprintf("[%d] function returned abort\n", (int) ctx->ctx_id); ret = MI_FAILURE; break; @@ -560,7 +560,7 @@ st_connectinfo(g) size_t l; size_t i; char *s, family; - u_short port = 0; + unsigned short port = 0; _SOCK_ADDR sockaddr; sfsistat (*fi_connect) __P((SMFICTX *, char *, _SOCK_ADDR *)); @@ -617,11 +617,11 @@ st_connectinfo(g) # if NETINET6 if (family == SMFIA_INET6) { - if (inet_pton(AF_INET6, s + i, - &sockaddr.sin6.sin6_addr) != 1) + if (mi_inet_pton(AF_INET6, s + i, + &sockaddr.sin6.sin6_addr) != 1) { smi_log(SMI_LOG_ERR, - "%s: connect[%d]: inet_pton failed", + "%s: connect[%d]: mi_inet_pton failed", g->a_ctx->ctx_smfi->xxfi_name, (int) g->a_ctx->ctx_id); return _SMFIS_ABORT; @@ -635,7 +635,7 @@ st_connectinfo(g) # if NETUNIX if (family == SMFIA_UNIX) { - if (strlcpy(sockaddr.sunix.sun_path, s + i, + if (sm_strlcpy(sockaddr.sunix.sun_path, s + i, sizeof sockaddr.sunix.sun_path) >= sizeof sockaddr.sunix.sun_path) { @@ -734,21 +734,21 @@ st_header(g) return _SMFIS_ABORT; } -#define ARGV_FCT(lf, rf, idx) \ - char **argv; \ - sfsistat (*lf) __P((SMFICTX *, char **)); \ - int r; \ - \ - if (g == NULL) \ - return _SMFIS_ABORT; \ - mi_clr_macros(g->a_ctx, g->a_idx + 1); \ - if (g->a_ctx->ctx_smfi == NULL || \ - (lf = g->a_ctx->ctx_smfi->rf) == NULL) \ - return SMFIS_CONTINUE; \ +#define ARGV_FCT(lf, rf, idx) \ + char **argv; \ + sfsistat (*lf) __P((SMFICTX *, char **)); \ + int r; \ + \ + if (g == NULL) \ + return _SMFIS_ABORT; \ + mi_clr_macros(g->a_ctx, g->a_idx + 1); \ + if (g->a_ctx->ctx_smfi == NULL || \ + (lf = g->a_ctx->ctx_smfi->rf) == NULL) \ + return SMFIS_CONTINUE; \ if ((argv = dec_argv(g->a_buf, g->a_len)) == NULL) \ - return _SMFIS_ABORT; \ - r = (*lf)(g->a_ctx, argv); \ - free(argv); \ + return _SMFIS_ABORT; \ + r = (*lf)(g->a_ctx, argv); \ + free(argv); \ return r; /* @@ -863,13 +863,14 @@ static int st_bodychunk(g) genarg *g; { - sfsistat (*fi_body) __P((SMFICTX *, u_char *, size_t)); + sfsistat (*fi_body) __P((SMFICTX *, unsigned char *, size_t)); if (g == NULL) return _SMFIS_ABORT; if (g->a_ctx->ctx_smfi != NULL && (fi_body = g->a_ctx->ctx_smfi->xxfi_body) != NULL) - return (*fi_body)(g->a_ctx, (u_char *)g->a_buf, g->a_len); + return (*fi_body)(g->a_ctx, (unsigned char *)g->a_buf, + g->a_len); return SMFIS_CONTINUE; } /* @@ -890,7 +891,7 @@ st_bodyend(g) genarg *g; { sfsistat r; - sfsistat (*fi_body) __P((SMFICTX *, u_char *, size_t)); + sfsistat (*fi_body) __P((SMFICTX *, unsigned char *, size_t)); sfsistat (*fi_eom) __P((SMFICTX *)); if (g == NULL) @@ -907,7 +908,8 @@ st_bodyend(g) timeout.tv_sec = g->a_ctx->ctx_timeout; timeout.tv_usec = 0; sd = g->a_ctx->ctx_sd; - r = (*fi_body)(g->a_ctx, (u_char *)g->a_buf, g->a_len); + r = (*fi_body)(g->a_ctx, (unsigned char *)g->a_buf, + g->a_len); if (r != SMFIS_CONTINUE && sendreply(r, sd, &timeout, g->a_ctx) != MI_SUCCESS) return _SMFIS_ABORT; @@ -963,13 +965,14 @@ trans_ok(old, new) { /* is this state transition allowed? */ if ((MASK(new) & next_states[s]) != 0) - return TRUE; + return true; /* ** no: try next state; ** this works since the relevant states are ordered ** strict sequentially */ + n = s + 1; /* @@ -977,12 +980,13 @@ trans_ok(old, new) ** see fix_stm() which sets this bit for those ** states which the filter program is not interested in */ + if (bitset(NX_SKIP, next_states[n])) s = n; else - return FALSE; + return false; } while (s <= ST_LAST); - return FALSE; + return false; } /* ** FIX_STM -- add "skip" bits to the state transition table @@ -1001,7 +1005,7 @@ static void fix_stm(ctx) SMFICTX_PTR ctx; { - u_long fl; + unsigned long fl; if (ctx == NULL || ctx->ctx_smfi == NULL) return; @@ -1111,9 +1115,8 @@ mi_sendok(ctx, flag) int flag; { if (ctx == NULL || ctx->ctx_smfi == NULL) - return FALSE; + return false; if (flag != 0 && !bitset(flag, ctx->ctx_smfi->xxfi_flags)) - return FALSE; + return false; return ctx->ctx_state == ST_ENDM; } -#endif /* _FFR_MILTER */ diff --git a/gnu/usr.sbin/sendmail/libmilter/handler.c b/gnu/usr.sbin/sendmail/libmilter/handler.c index 3b5102fe031..f165befc2a2 100644 --- a/gnu/usr.sbin/sendmail/libmilter/handler.c +++ b/gnu/usr.sbin/sendmail/libmilter/handler.c @@ -8,11 +8,9 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: handler.c,v 8.19.4.3 2000/12/29 19:45:39 gshapiro Exp $"; -#endif /* ! lint */ +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: handler.c,v 8.24 2000/12/29 19:45:55 gshapiro Exp $") -#if _FFR_MILTER #include "libmilter.h" @@ -64,4 +62,3 @@ mi_handle_session(ctx) ctx = NULL; return ret; } -#endif /* _FFR_MILTER */ diff --git a/gnu/usr.sbin/sendmail/libmilter/libmilter.h b/gnu/usr.sbin/sendmail/libmilter/libmilter.h index d826f972368..fe90c563e06 100644 --- a/gnu/usr.sbin/sendmail/libmilter/libmilter.h +++ b/gnu/usr.sbin/sendmail/libmilter/libmilter.h @@ -13,12 +13,13 @@ #ifndef _LIBMILTER_H # define _LIBMILTER_H 1 + +#include <sm/gen.h> + #ifdef _DEFINE # define EXTERN # define INIT(x) = x -# ifndef lint -static char MilterlId[] = "@(#)$Sendmail: libmilter.h,v 8.3.6.16 2001/06/07 23:21:35 geir Exp $"; -# endif /* ! lint */ +SM_IDSTR(MilterlId, "@(#)$Sendmail: libmilter.h,v 8.26 2001/07/20 02:48:37 gshapiro Exp $") #else /* _DEFINE */ # define EXTERN extern # define INIT(x) @@ -31,11 +32,6 @@ static char MilterlId[] = "@(#)$Sendmail: libmilter.h,v 8.3.6.16 2001/06/07 23:2 #include "libmilter/milter.h" -#ifndef __P -# include "sendmail/cdefs.h" -#endif /* ! __P */ -#include "sendmail/useful.h" - # define ValidSocket(sd) ((sd) >= 0) # define INVALID_SOCKET -1 # define MI_SOCK_READ(s, b, l) (read(s, b, l)) @@ -61,16 +57,19 @@ typedef pthread_mutex_t smutex_t; #define MI_TIMEOUT 7210 /* default timeout for read/write */ #define MI_CHK_TIME 5 /* checking whether to terminate */ -#if SOMAXCONN > 20 -# define MI_SOMAXCONN SOMAXCONN -#else /* SOMAXCONN */ -# define MI_SOMAXCONN 20 -#endif /* SOMAXCONN */ +#ifndef MI_SOMAXCONN +# if SOMAXCONN > 20 +# define MI_SOMAXCONN SOMAXCONN +# else /* SOMAXCONN */ +# define MI_SOMAXCONN 20 +# endif /* SOMAXCONN */ +#endif /* ! MI_SOMAXCONN */ /* maximum number of repeated failures in mi_listener() */ #define MAX_FAILS_M 16 /* malloc() */ #define MAX_FAILS_T 16 /* thread creation */ #define MAX_FAILS_A 16 /* accept() */ +#define MAX_FAILS_S 16 /* select() */ /* internal "commands", i.e., error codes */ #define SMFIC_TIMEOUT ((char) 1) /* timeout */ @@ -84,6 +83,7 @@ typedef pthread_mutex_t smutex_t; /* hack */ #define smi_log syslog +#define sm_dprintf printf #define milter_ret int #define SMI_LOG_ERR LOG_ERR #define SMI_LOG_FATAL LOG_ERR @@ -106,6 +106,7 @@ extern int mi_control_startup __P((char *)); extern void mi_stop_milters __P((int)); extern void mi_clean_signals __P((void)); extern struct hostent *mi_gethostbyname __P((char *, int)); +extern int mi_inet_pton __P((int, const char *, void *)); extern void mi_closener __P((void)); /* communication functions */ diff --git a/gnu/usr.sbin/sendmail/libmilter/listener.c b/gnu/usr.sbin/sendmail/libmilter/listener.c index f197ff01cbc..656c82ba6b6 100644 --- a/gnu/usr.sbin/sendmail/libmilter/listener.c +++ b/gnu/usr.sbin/sendmail/libmilter/listener.c @@ -8,21 +8,23 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: listener.c,v 8.38.2.1.2.22 2001/05/16 17:15:58 ca Exp $"; -#endif /* ! lint */ +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: listener.c,v 8.72 2001/06/28 20:59:06 ca Exp $") -#if _FFR_MILTER /* ** listener.c -- threaded network listener */ #include "libmilter.h" +#include <sm/errstring.h> # if NETINET || NETINET6 # include <arpa/inet.h> # endif /* NETINET || NETINET6 */ + +static smutex_t L_Mutex; + /* ** MI_MILTEROPEN -- setup socket to listen on ** @@ -35,8 +37,15 @@ static char id[] = "@(#)$Sendmail: listener.c,v 8.38.2.1.2.22 2001/05/16 17:15:5 ** ** Returns: ** socket upon success, error code otherwise. +** +** Side effect: +** sets sockpath if UNIX socket. */ +#if NETUNIX +static char *sockpath = NULL; +#endif /* NETUNIX */ + static socket_t mi_milteropen(conn, backlog, socksize, family, name) char *conn; @@ -47,6 +56,7 @@ mi_milteropen(conn, backlog, socksize, family, name) { socket_t sock; int sockopt = 1; + size_t len = -1; char *p; char *colon; char *at; @@ -157,15 +167,16 @@ mi_milteropen(conn, backlog, socksize, family, name) # endif /* 0 */ at = colon; - if (strlcpy(addr.sunix.sun_path, colon, - sizeof addr.sunix.sun_path) >= - sizeof addr.sunix.sun_path) + len = strlen(colon) + 1; + if (len >= sizeof addr.sunix.sun_path) { errno = EINVAL; smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long", name, colon); return INVALID_SOCKET; } + (void) sm_strlcpy(addr.sunix.sun_path, colon, + sizeof addr.sunix.sun_path); # if 0 errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, S_IRUSR|S_IWUSR, NULL); @@ -179,7 +190,6 @@ mi_milteropen(conn, backlog, socksize, family, name) return INVALID_SOCKET; } # endif /* 0 */ - } #endif /* NETUNIX */ @@ -196,7 +206,7 @@ mi_milteropen(conn, backlog, socksize, family, name) # endif /* NETINET6 */ ) { - u_short port; + unsigned short port; /* Parse port@host */ at = strchr(colon, '@'); @@ -221,7 +231,7 @@ mi_milteropen(conn, backlog, socksize, family, name) *at = '\0'; if (isascii(*colon) && isdigit(*colon)) - port = htons((u_short) atoi(colon)); + port = htons((unsigned short) atoi(colon)); else { # ifdef NO_GETSERVBYNAME @@ -252,7 +262,7 @@ mi_milteropen(conn, backlog, socksize, family, name) end = strchr(at, ']'); if (end != NULL) { - bool found = FALSE; + bool found = false; # if NETINET unsigned long hid = INADDR_NONE; # endif /* NETINET */ @@ -263,23 +273,22 @@ mi_milteropen(conn, backlog, socksize, family, name) *end = '\0'; # if NETINET if (addr.sa.sa_family == AF_INET && - (hid = inet_addr(&at[1])) != - INADDR_NONE) + (hid = inet_addr(&at[1])) != INADDR_NONE) { addr.sin.sin_addr.s_addr = hid; addr.sin.sin_port = port; - found = TRUE; + found = true; } # endif /* NETINET */ # if NETINET6 (void) memset(&hid6, '\0', sizeof hid6); if (addr.sa.sa_family == AF_INET6 && - inet_pton(AF_INET6, &at[1], - &hid6.sin6_addr) == 1) + mi_inet_pton(AF_INET6, &at[1], + &hid6.sin6_addr) == 1) { addr.sin6.sin6_addr = hid6.sin6_addr; addr.sin6.sin6_port = port; - found = TRUE; + found = true; } # endif /* NETINET6 */ *end = ']'; @@ -338,9 +347,9 @@ mi_milteropen(conn, backlog, socksize, family, name) name, at, hp->h_addrtype); return INVALID_SOCKET; } -# if _FFR_FREEHOSTENT && NETINET6 +# if NETINET6 freehostent(hp); -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +# endif /* NETINET6 */ } } else @@ -367,7 +376,7 @@ mi_milteropen(conn, backlog, socksize, family, name) { smi_log(SMI_LOG_ERR, "%s: Unable to create new socket: %s", - name, strerror(errno)); + name, sm_errstring(errno)); return INVALID_SOCKET; } @@ -375,7 +384,8 @@ mi_milteropen(conn, backlog, socksize, family, name) sizeof(sockopt)) == -1) { smi_log(SMI_LOG_ERR, - "%s: Unable to setsockopt: %s", name, strerror(errno)); + "%s: Unable to setsockopt: %s", name, + sm_errstring(errno)); (void) close(sock); return INVALID_SOCKET; } @@ -384,7 +394,7 @@ mi_milteropen(conn, backlog, socksize, family, name) { smi_log(SMI_LOG_ERR, "%s: Unable to bind to port %s: %s", - name, conn, strerror(errno)); + name, conn, sm_errstring(errno)); (void) close(sock); return INVALID_SOCKET; } @@ -392,10 +402,32 @@ mi_milteropen(conn, backlog, socksize, family, name) if (listen(sock, backlog) < 0) { smi_log(SMI_LOG_ERR, - "%s: listen call failed: %s", name, strerror(errno)); + "%s: listen call failed: %s", name, + sm_errstring(errno)); (void) close(sock); return INVALID_SOCKET; } + +#if NETUNIX + if (addr.sa.sa_family == AF_UNIX && len > 0) + { + /* + ** Set global variable sockpath so the UNIX socket can be + ** unlink()ed at exit. + */ + sockpath = (char *) malloc(len); + if (sockpath != NULL) + (void) sm_strlcpy(sockpath, colon, len); + else + { + smi_log(SMI_LOG_ERR, + "%s: can't malloc(%d) for sockpath: %s", + name, len, sm_errstring(errno)); + (void) close(sock); + return INVALID_SOCKET; + } + } +#endif /* NETUNIX */ *family = addr.sa.sa_family; return sock; } @@ -418,11 +450,12 @@ mi_thread_handle_wrapper(arg) static socket_t listenfd = INVALID_SOCKET; -static smutex_t L_Mutex; - /* ** MI_CLOSENER -- close listen socket ** +** NOTE: It is assumed that this function is called from a +** function that has a mutex lock (currently mi_stop_milters()). +** ** Parameters: ** none. ** @@ -436,8 +469,42 @@ mi_closener() (void) smutex_lock(&L_Mutex); if (ValidSocket(listenfd)) { +#if NETUNIX + bool removable = false; + struct stat sockinfo; + struct stat fileinfo; + + if (sockpath != NULL && + fstat(listenfd, &sockinfo) == 0 && + (S_ISFIFO(sockinfo.st_mode) +# ifdef S_ISSOCK + || !S_ISSOCK(sockinfo.st_mode) +# endif /* S_ISSOCK */ + )) + removable = true; +#endif /* NETUNIX */ + (void) close(listenfd); listenfd = INVALID_SOCKET; + +#if NETUNIX + /* XXX sleep() some time before doing this? */ + if (sockpath != NULL) + { + if (removable && + stat(sockpath, &fileinfo) == 0 && + fileinfo.st_dev == sockinfo.st_dev && + fileinfo.st_ino == sockinfo.st_ino && + (S_ISFIFO(fileinfo.st_mode) +# ifdef S_ISSOCK + || S_ISSOCK(fileinfo.st_mode) +# endif /* S_ISSOCK */ + )) + (void) unlink(sockpath); + free(sockpath); + sockpath = NULL; + } +#endif /* NETUNIX */ } (void) smutex_unlock(&L_Mutex); } @@ -460,7 +527,7 @@ mi_closener() ** MI_FAILURE -- Network initialization failed. */ -# if BROKEN_PTHREAD_SLEEP +#if BROKEN_PTHREAD_SLEEP /* ** Solaris 2.6, perhaps others, gets an internal threads library panic @@ -480,7 +547,7 @@ mi_closener() ** 0 */ -# define MI_SLEEP(s) \ +# define MI_SLEEP(s) \ { \ int rs = 0; \ struct timeval st; \ @@ -496,9 +563,9 @@ mi_closener() rs, errno); \ } \ } -# else /* BROKEN_PTHREAD_SLEEP */ -# define MI_SLEEP(s) sleep((s)) -# endif /* BROKEN_PTHREAD_SLEEP */ +#else /* BROKEN_PTHREAD_SLEEP */ +# define MI_SLEEP(s) sleep((s)) +#endif /* BROKEN_PTHREAD_SLEEP */ int mi_listener(conn, dbg, smfi, timeout, backlog) @@ -513,9 +580,10 @@ mi_listener(conn, dbg, smfi, timeout, backlog) int sockopt = 1; int r; int ret = MI_SUCCESS; - int mcnt = 0; - int tcnt = 0; - int acnt = 0; + int mcnt = 0; /* error count for malloc() failures */ + int tcnt = 0; /* error count for thread_create() failures */ + int acnt = 0; /* error count for accept() failures */ + int scnt = 0; /* error count for select() failures */ int save_errno = 0; sthread_t thread_id; _SOCK_ADDR cliaddr; @@ -564,8 +632,8 @@ mi_listener(conn, dbg, smfi, timeout, backlog) /* select on interface ports */ FD_ZERO(&readset); FD_ZERO(&excset); - FD_SET((u_int) listenfd, &readset); - FD_SET((u_int) listenfd, &excset); + FD_SET((unsigned int) listenfd, &readset); + FD_SET((unsigned int) listenfd, &excset); chktime.tv_sec = MI_CHK_TIME; chktime.tv_usec = 0; r = select(listenfd + 1, &readset, NULL, &excset, &chktime); @@ -580,16 +648,30 @@ mi_listener(conn, dbg, smfi, timeout, backlog) (void) smutex_unlock(&L_Mutex); if (save_errno == EINTR) continue; - ret = MI_FAILURE; - break; + scnt++; + smi_log(SMI_LOG_ERR, + "%s: select() failed (%s), %s", + smfi->xxfi_name, sm_errstring(save_errno), + scnt >= MAX_FAILS_S ? "abort" : "try again"); + MI_SLEEP(scnt); + if (scnt >= MAX_FAILS_S) + { + ret = MI_FAILURE; + break; + } + continue; } if (!FD_ISSET(listenfd, &readset)) { /* some error: just stop for now... */ ret = MI_FAILURE; (void) smutex_unlock(&L_Mutex); + smi_log(SMI_LOG_ERR, + "%s: select() returned exception for socket, abort", + smfi->xxfi_name); break; } + scnt = 0; /* reset error counter for select() */ memset(&cliaddr, '\0', sizeof cliaddr); connfd = accept(listenfd, (struct sockaddr *) &cliaddr, @@ -617,12 +699,13 @@ mi_listener(conn, dbg, smfi, timeout, backlog) if (!ValidSocket(connfd)) { - smi_log(SMI_LOG_ERR, - "%s: accept() returned invalid socket (%s)", - smfi->xxfi_name, strerror(save_errno)); if (save_errno == EINTR) continue; acnt++; + smi_log(SMI_LOG_ERR, + "%s: accept() returned invalid socket (%s), %s", + smfi->xxfi_name, sm_errstring(save_errno), + acnt >= MAX_FAILS_A ? "abort" : "try again"); MI_SLEEP(acnt); if (acnt >= MAX_FAILS_A) { @@ -631,20 +714,22 @@ mi_listener(conn, dbg, smfi, timeout, backlog) } continue; } + acnt = 0; /* reset error counter for accept() */ if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE, (void *) &sockopt, sizeof sockopt) < 0) { - smi_log(SMI_LOG_WARN, "%s: setsockopt() failed", - smfi->xxfi_name); + smi_log(SMI_LOG_WARN, "%s: setsockopt() failed (%s)", + smfi->xxfi_name, sm_errstring(errno)); /* XXX: continue? */ } if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL) { (void) close(connfd); - smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed", - smfi->xxfi_name); mcnt++; + smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed (%s), %s", + smfi->xxfi_name, sm_errstring(save_errno), + mcnt >= MAX_FAILS_M ? "abort" : "try again"); MI_SLEEP(mcnt); if (mcnt >= MAX_FAILS_M) { @@ -653,8 +738,7 @@ mi_listener(conn, dbg, smfi, timeout, backlog) } continue; } - mcnt = 0; - acnt = 0; + mcnt = 0; /* reset error counter for malloc() */ memset(ctx, '\0', sizeof *ctx); ctx->ctx_sd = connfd; ctx->ctx_dbg = dbg; @@ -685,10 +769,11 @@ mi_listener(conn, dbg, smfi, timeout, backlog) mi_thread_handle_wrapper, (void *) ctx)) != 0) { - smi_log(SMI_LOG_ERR, - "%s: thread_create() failed: %d", - smfi->xxfi_name, r); tcnt++; + smi_log(SMI_LOG_ERR, + "%s: thread_create() failed: %d, %s", + smfi->xxfi_name, r, + tcnt >= MAX_FAILS_T ? "abort" : "try again"); MI_SLEEP(tcnt); (void) close(connfd); free(ctx); @@ -708,4 +793,3 @@ mi_listener(conn, dbg, smfi, timeout, backlog) (void) smutex_destroy(&L_Mutex); return ret; } -#endif /* _FFR_MILTER */ diff --git a/gnu/usr.sbin/sendmail/libmilter/main.c b/gnu/usr.sbin/sendmail/libmilter/main.c index 521f149fca1..2fbe35334e3 100644 --- a/gnu/usr.sbin/sendmail/libmilter/main.c +++ b/gnu/usr.sbin/sendmail/libmilter/main.c @@ -8,11 +8,9 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: main.c,v 8.34.4.11 2001/05/07 22:06:37 gshapiro Exp $"; -#endif /* ! lint */ +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: main.c,v 8.49 2001/04/21 01:18:16 ca Exp $") -#if _FFR_MILTER #define _DEFINE 1 #include "libmilter.h" #include <fcntl.h> @@ -51,7 +49,7 @@ smfi_register(smfilter) smfi->xxfi_name = (char *) malloc(len); if (smfi->xxfi_name == NULL) return MI_FAILURE; - (void) strlcpy(smfi->xxfi_name, smfilter.xxfi_name, len); + (void) sm_strlcpy(smfi->xxfi_name, smfilter.xxfi_name, len); /* compare milter version with hard coded version */ if (smfi->xxfi_version != SMFI_VERSION) @@ -61,6 +59,9 @@ smfi_register(smfilter) "%s: smfi_register: version mismatch application: %d != milter: %d", smfi->xxfi_name, smfi->xxfi_version, (int) SMFI_VERSION); + + /* XXX how about smfi? */ + free(smfi->xxfi_name); return MI_FAILURE; } @@ -84,11 +85,26 @@ smfi_stop() return MI_SUCCESS; } +/* +** default values for some variables. +** Most of these can be changed with the functions below. +*/ + static int dbg = 0; static char *conn = NULL; static int timeout = MI_TIMEOUT; static int backlog= MI_SOMAXCONN; +/* +** SMFI_SETDBG -- set debug level. +** +** Parameters: +** odbg -- new debug level. +** +** Returns: +** MI_SUCCESS +*/ + int smfi_setdbg(odbg) int odbg; @@ -97,6 +113,16 @@ smfi_setdbg(odbg) return MI_SUCCESS; } +/* +** SMFI_SETTIMEOUT -- set timeout (for read/write). +** +** Parameters: +** otimeout -- new timeout. +** +** Returns: +** MI_SUCCESS +*/ + int smfi_settimeout(otimeout) int otimeout; @@ -105,6 +131,16 @@ smfi_settimeout(otimeout) return MI_SUCCESS; } +/* +** SMFI_SETCONN -- set connection information (socket description) +** +** Parameters: +** oconn -- new connection information. +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + int smfi_setconn(oconn) char *oconn; @@ -116,11 +152,21 @@ smfi_setconn(oconn) l = strlen(oconn) + 1; if ((conn = (char *) malloc(l)) == NULL) return MI_FAILURE; - if (strlcpy(conn, oconn, l) >= l) + if (sm_strlcpy(conn, oconn, l) >= l) return MI_FAILURE; return MI_SUCCESS; } +/* +** SMFI_SETBACKLOG -- set backlog +** +** Parameters: +** odbg -- new backlog. +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + int smfi_setbacklog(obacklog) int obacklog; @@ -131,12 +177,20 @@ smfi_setbacklog(obacklog) return MI_SUCCESS; } +/* +** SMFI_MAIN -- setup milter connnection and start listener. +** +** Parameters: +** none. +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ int smfi_main() { - - signal(SIGPIPE, SIG_IGN); + (void) signal(SIGPIPE, SIG_IGN); if (conn == NULL) { smi_log(SMI_LOG_FATAL, "%s: missing connection information", @@ -153,11 +207,9 @@ smfi_main() return MI_FAILURE; } - /* Startup the listener */ if (mi_listener(conn, dbg, smfi, timeout, backlog) != MI_SUCCESS) return MI_FAILURE; return MI_SUCCESS; } -#endif /* _FFR_MILTER */ diff --git a/gnu/usr.sbin/sendmail/libmilter/signal.c b/gnu/usr.sbin/sendmail/libmilter/signal.c index 19a79be2560..b41f8f8fcb5 100644 --- a/gnu/usr.sbin/sendmail/libmilter/signal.c +++ b/gnu/usr.sbin/sendmail/libmilter/signal.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers. + * Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set @@ -8,11 +8,9 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: signal.c,v 8.10.4.8 2000/11/20 21:15:37 ca Exp $"; -#endif /* ! lint */ +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: signal.c,v 8.24 2001/01/24 00:27:35 ca Exp $") -#if _FFR_MILTER #include "libmilter.h" /* @@ -100,7 +98,7 @@ mi_signal_thread(name) sigaddset(&set, SIGINT); errs = 0; - while (TRUE) + while (true) { sig = 0; #ifdef SOLARIS @@ -212,4 +210,3 @@ mi_control_startup(name) } return MI_SUCCESS; } -#endif /* _FFR_MILTER */ diff --git a/gnu/usr.sbin/sendmail/libmilter/sm_gethost.c b/gnu/usr.sbin/sendmail/libmilter/sm_gethost.c index 57b102f236d..a0337b1f73b 100644 --- a/gnu/usr.sbin/sendmail/libmilter/sm_gethost.c +++ b/gnu/usr.sbin/sendmail/libmilter/sm_gethost.c @@ -8,11 +8,9 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: sm_gethost.c,v 8.7.8.11 2001/07/21 00:10:23 gshapiro Exp $"; -#endif /* ! lint */ +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: sm_gethost.c,v 8.23 2001/07/21 00:05:06 gshapiro Exp $") -#if _FFR_MILTER #include <sendmail.h> #if NETINET || NETINET6 # include <arpa/inet.h> @@ -48,7 +46,7 @@ getipnodebyname(name, family, flags, err) int flags; int *err; { - bool resv6 = TRUE; + bool resv6 = true; struct hostent *h; if (family == AF_INET6) @@ -59,13 +57,12 @@ getipnodebyname(name, family, flags, err) } SM_SET_H_ERRNO(0); h = gethostbyname(name); - *err = h_errno; if (family == AF_INET6 && !resv6) _res.options &= ~RES_USE_INET6; + *err = h_errno; return h; } -# if _FFR_FREEHOSTENT void freehostent(h) struct hostent *h; @@ -77,7 +74,6 @@ freehostent(h) return; } -# endif /* _FFR_FREEHOSTENT */ #endif /* NEEDSGETIPNODE && NETINET6 */ struct hostent * @@ -117,4 +113,33 @@ mi_gethostbyname(name, family) #endif /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4)) */ return h; } -#endif /* _FFR_MILTER */ + +#if NETINET6 +/* +** MI_INET_PTON -- convert printed form to network address. +** +** Wrapper for inet_pton() which handles IPv6: labels. +** +** Parameters: +** family -- address family +** src -- string +** dst -- destination address structure +** +** Returns: +** 1 if the address was valid +** 0 if the address wasn't parseable +** -1 if error +*/ + +int +mi_inet_pton(family, src, dst) + int family; + const char *src; + void *dst; +{ + if (family == AF_INET6 && + strncasecmp(src, "IPv6:", 5) == 0) + src += 5; + return inet_pton(family, src, dst); +} +#endif /* NETINET6 */ diff --git a/gnu/usr.sbin/sendmail/libmilter/smfi.c b/gnu/usr.sbin/sendmail/libmilter/smfi.c index 5297524e5fd..82b3d4cf638 100644 --- a/gnu/usr.sbin/sendmail/libmilter/smfi.c +++ b/gnu/usr.sbin/sendmail/libmilter/smfi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers. + * Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set @@ -8,13 +8,10 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: smfi.c,v 8.28.4.6 2000/06/28 23:48:56 gshapiro Exp $"; -#endif /* ! lint */ +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: smfi.c,v 8.42 2001/08/27 18:09:16 gshapiro Exp $") -#if _FFR_MILTER #include "libmilter.h" -#include "sendmail/useful.h" /* ** SMFI_ADDHEADER -- send a new header to the MTA @@ -181,13 +178,14 @@ smfi_delrcpt(ctx, rcpt) int smfi_replacebody(ctx, bodyp, bodylen) SMFICTX *ctx; - u_char *bodyp; + unsigned char *bodyp; int bodylen; { int len, off, r; struct timeval timeout; - if (bodyp == NULL && bodylen > 0) + if (bodylen < 0 || + (bodyp == NULL && bodylen > 0)) return MI_FAILURE; if (!mi_sendok(ctx, SMFIF_CHGBODY)) return MI_FAILURE; @@ -267,29 +265,41 @@ smfi_setreply(ctx, rcode, xcode, message) char *xcode; char *message; { - size_t len, l1, l2, l3; + size_t len; char *buf; if (rcode == NULL || ctx == NULL) return MI_FAILURE; - l1 = strlen(rcode) + 1; - if (l1 != 4) + + /* ### <sp> \0 */ + len = strlen(rcode) + 2; + if (len != 5) return MI_FAILURE; if ((rcode[0] != '4' && rcode[0] != '5') || !isascii(rcode[1]) || !isdigit(rcode[1]) || !isascii(rcode[2]) || !isdigit(rcode[2])) return MI_FAILURE; - l2 = xcode == NULL ? 1 : strlen(xcode) + 1; - if (xcode != NULL && !myisenhsc(xcode, '\0')) - return MI_FAILURE; - l3 = message == NULL ? 1 : strlen(message) + 1; - len = l1 + l2 + l3; + if (xcode != NULL) + len += strlen(xcode) + 1; + if (message != NULL) + len += strlen(message) + 1; buf = malloc(len); if (buf == NULL) return MI_FAILURE; /* oops */ - (void) snprintf(buf, len, "%s %s %s", rcode, - xcode == NULL ? "" : xcode, - message == NULL ? "" : message); + (void) sm_strlcpy(buf, rcode, len); + (void) sm_strlcat(buf, " ", len); + if (xcode != NULL) + { + if (!myisenhsc(xcode, '\0')) + return MI_FAILURE; + (void) sm_strlcat(buf, xcode, len); + } + if (message != NULL) + { + if (xcode != NULL) + (void) sm_strlcat(buf, " ", len); + (void) sm_strlcat(buf, message, len); + } if (ctx->ctx_reply != NULL) free(ctx->ctx_reply); ctx->ctx_reply = buf; @@ -397,4 +407,3 @@ smfi_getsymval(ctx, symname) } return NULL; } -#endif /* _FFR_MILTER */ diff --git a/gnu/usr.sbin/sendmail/libsm/Makefile b/gnu/usr.sbin/sendmail/libsm/Makefile index e2685542067..c9f0bd07416 100644 --- a/gnu/usr.sbin/sendmail/libsm/Makefile +++ b/gnu/usr.sbin/sendmail/libsm/Makefile @@ -1,17 +1,19 @@ -# $Sendmail: Makefile,v 1.1 2000/03/27 19:26:48 dmoen Exp $ +# $OpenBSD: Makefile,v 1.2 2001/09/11 19:02:49 millert Exp $ -SHELL= /bin/sh -BUILD= ./Build -OPTIONS= $(CONFIG) $(FLAGS) +LIB= sm +SRCS= assert.c debug.c errstring.c exc.c heap.c match.c rpool.c strdup.c \ + strerror.c strl.c clrerr.c fclose.c feof.c ferror.c fflush.c fget.c \ + fpos.c findfp.c flags.c fopen.c fprintf.c fpurge.c fput.c fread.c \ + fscanf.c fseek.c fvwrite.c fwalk.c fwrite.c get.c makebuf.c put.c \ + refill.c rewind.c setvbuf.c smstdio.c snprintf.c sscanf.c stdio.c \ + strio.c ungetc.c vasprintf.c vfprintf.c vfscanf.c vprintf.c \ + vsnprintf.c vsprintf.c vsscanf.c wbuf.c wsetup.c string.c stringf.c \ + xtrap.c strto.c test.c path.c strcasecmp.c strrevcmp.c signal.c \ + clock.c config.c shm.c mbdb.c strexit.c cf.c ldap.c niprop.c +ENVDEF= -DNOT_SENDMAIL -all: FRC - $(SHELL) $(BUILD) $(OPTIONS) $@ -clean: FRC - $(SHELL) $(BUILD) $(OPTIONS) $@ -install: FRC - $(SHELL) $(BUILD) $(OPTIONS) $@ +# This is not a library that gets installed so only build the .a version +NOPROFILE=1 +NOPIC=1 -fresh: FRC - $(SHELL) $(BUILD) $(OPTIONS) -c - -FRC: +.include <bsd.lib.mk> diff --git a/gnu/usr.sbin/sendmail/libsmdb/smdb.c b/gnu/usr.sbin/sendmail/libsmdb/smdb.c index 03756c87079..da694d07fe1 100644 --- a/gnu/usr.sbin/sendmail/libsmdb/smdb.c +++ b/gnu/usr.sbin/sendmail/libsmdb/smdb.c @@ -1,5 +1,5 @@ /* -** Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers. +** Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers. ** All rights reserved. ** ** By using this file, you agree to the terms and conditions set @@ -7,9 +7,8 @@ ** the sendmail distribution. */ -#ifndef lint -static char id[] = "@(#)$Sendmail: smdb.c,v 8.37.4.2 2000/08/24 17:08:00 gshapiro Exp $"; -#endif /* ! lint */ +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: smdb.c,v 8.49 2001/09/04 22:43:45 ca Exp $") #include <fcntl.h> #include <stdlib.h> @@ -38,7 +37,7 @@ smdb_malloc_database() db = (SMDB_DATABASE *) malloc(sizeof(SMDB_DATABASE)); if (db != NULL) - memset(db, '\0', sizeof(SMDB_DATABASE)); + (void) memset(db, '\0', sizeof(SMDB_DATABASE)); return db; } @@ -61,7 +60,6 @@ smdb_free_database(database) if (database != NULL) free(database); } - /* ** SMDB_LOCKFILE -- lock a file using flock or (shudder) fcntl locking ** @@ -72,8 +70,8 @@ smdb_free_database(database) ** LOCK_NB -- non-blocking. ** ** Returns: -** TRUE if the lock was acquired. -** FALSE otherwise. +** true if the lock was acquired. +** false otherwise. */ static bool @@ -87,7 +85,7 @@ smdb_lockfile(fd, type) int action; struct flock lfd; - memset(&lfd, '\0', sizeof lfd); + (void) memset(&lfd, '\0', sizeof lfd); if (bitset(LOCK_UN, type)) lfd.l_type = F_UNLCK; else if (bitset(LOCK_EX, type)) @@ -103,9 +101,7 @@ smdb_lockfile(fd, type) while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR) continue; if (i >= 0) - { - return TRUE; - } + return true; save_errno = errno; /* @@ -118,9 +114,7 @@ smdb_lockfile(fd, type) */ if (save_errno == EINVAL) - { - return TRUE; - } + return true; if (!bitset(LOCK_NB, type) || (save_errno != EACCES && save_errno != EAGAIN)) @@ -132,18 +126,16 @@ smdb_lockfile(fd, type) # endif /* F_GETFL */ # if 0 syslog(LOG_ERR, "cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", - filename, ext, fd, type, omode, geteuid()); + filename, ext, fd, type, omode, (int) geteuid()); # endif /* 0 */ - return FALSE; + return false; } #else /* !HASFLOCK */ while ((i = flock(fd, type)) < 0 && errno == EINTR) continue; if (i >= 0) - { - return TRUE; - } + return true; save_errno = errno; if (!bitset(LOCK_NB, type) || save_errno != EWOULDBLOCK) @@ -155,15 +147,14 @@ smdb_lockfile(fd, type) # endif /* F_GETFL */ # if 0 syslog(LOG_ERR, "cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", - filename, ext, fd, type, omode, geteuid()); + filename, ext, fd, type, omode, (int) geteuid()); # endif /* 0 */ - return FALSE; + return false; } #endif /* !HASFLOCK */ errno = save_errno; - return FALSE; + return false; } - /* ** SMDB_OPEN_DATABASE -- Opens a database. ** @@ -208,11 +199,11 @@ smdb_open_database(database, db_name, mode, mode_mask, sff, type, user_info, SMDB_DBPARAMS *params; { int result; - bool type_was_default = FALSE; + bool type_was_default = false; if (type == SMDB_TYPE_DEFAULT) { - type_was_default = TRUE; + type_was_default = true; #ifdef NEWDB type = SMDB_TYPE_HASH; #else /* NEWDB */ @@ -255,7 +246,6 @@ smdb_open_database(database, db_name, mode, mode_mask, sff, type, user_info, return SMDBE_UNKNOWN_DB_TYPE; } - /* ** SMDB_ADD_EXTENSION -- Adds an extension to a file name. ** @@ -296,14 +286,13 @@ smdb_add_extension(full_name, max_full_name_len, db_name, extension) if (db_name_len < extension_len + 1 || db_name[db_name_len - extension_len - 1] != '.' || strcmp(&db_name[db_name_len - extension_len], extension) != 0) - snprintf(full_name, max_full_name_len, "%s.%s", db_name, - extension); + (void) sm_snprintf(full_name, max_full_name_len, "%s.%s", + db_name, extension); else - (void) strlcpy(full_name, db_name, max_full_name_len); + (void) sm_strlcpy(full_name, db_name, max_full_name_len); return SMDBE_OK; } - /* ** SMDB_LOCK_FILE -- Locks the database file. ** @@ -342,7 +331,6 @@ smdb_lock_file(lock_fd, db_name, mode, sff, extension) return SMDBE_OK; } - /* ** SMDB_UNLOCK_FILE -- Unlocks a file ** @@ -367,7 +355,6 @@ smdb_unlock_file(lock_fd) return SMDBE_OK; } - /* ** SMDB_LOCK_MAP -- Locks a database. ** @@ -395,7 +382,6 @@ smdb_lock_map(database, type) return SMDBE_LOCK_NOT_GRANTED; return SMDBE_OK; } - /* ** SMDB_UNLOCK_MAP -- Unlocks a database ** @@ -419,8 +405,6 @@ smdb_unlock_map(database) return SMDBE_LOCK_NOT_HELD; return SMDBE_OK; } - - /* ** SMDB_SETUP_FILE -- Gets db file ready for use. ** @@ -465,7 +449,6 @@ smdb_setup_file(db_name, extension, mode_mask, sff, user_info, stat_info) return SMDBE_OK; } - /* ** SMDB_FILECHANGED -- Checks to see if a file changed. ** @@ -496,10 +479,7 @@ smdb_filechanged(db_name, extension, db_fd, stat_info) extension); if (result != SMDBE_OK) return result; - - result = filechanged(db_file_name, db_fd, stat_info); - - return result; + return filechanged(db_file_name, db_fd, stat_info); } /* ** SMDB_PRINT_AVAILABLE_TYPES -- Prints the names of the available types. diff --git a/gnu/usr.sbin/sendmail/libsmdb/smdb1.c b/gnu/usr.sbin/sendmail/libsmdb/smdb1.c index 6052d3a7dc3..7349c00414f 100644 --- a/gnu/usr.sbin/sendmail/libsmdb/smdb1.c +++ b/gnu/usr.sbin/sendmail/libsmdb/smdb1.c @@ -1,5 +1,5 @@ /* -** Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers. +** Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers. ** All rights reserved. ** ** By using this file, you agree to the terms and conditions set @@ -7,9 +7,8 @@ ** the sendmail distribution. */ -#ifndef lint -static char id[] = "@(#)$Sendmail: smdb1.c,v 8.43.4.3 2000/10/05 23:06:30 gshapiro Exp $"; -#endif /* ! lint */ +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: smdb1.c,v 8.51 2001/05/10 01:23:58 ca Exp $") #include <unistd.h> #include <stdlib.h> @@ -64,8 +63,6 @@ smdb_type_to_db1_type(type) /* Should never get here thanks to test in smdb_db_open() */ return DB_HASH; } - - /* ** SMDB_PUT_FLAGS_TO_DB1_FLAGS -- Translates smdb put flags to db1 put flags. ** @@ -80,7 +77,7 @@ smdb_type_to_db1_type(type) ** */ -u_int +unsigned int smdb_put_flags_to_db1_flags(flags) SMDB_FLAG flags; { @@ -93,7 +90,6 @@ smdb_put_flags_to_db1_flags(flags) return return_flags; } - /* ** SMDB_CURSOR_GET_FLAGS_TO_SMDB1 ** @@ -131,6 +127,10 @@ smdb_cursor_get_flags_to_smdb1(flags) } } +/* +** The rest of these functions correspond to the interface laid out in smdb.h. +*/ + SMDB_DB1_DATABASE * smdb1_malloc_database() { @@ -141,17 +141,12 @@ smdb1_malloc_database() if (db1 != NULL) { db1->smdb1_lock_fd = -1; - db1->smdb1_cursor_in_use = FALSE; + db1->smdb1_cursor_in_use = false; } return db1; } -/* -** The rest of these function correspond to the interface laid out -** in smdb.h. -*/ - int smdb1_close(database) SMDB_DATABASE *database; @@ -172,12 +167,12 @@ int smdb1_del(database, key, flags) SMDB_DATABASE *database; SMDB_DBENT *key; - u_int flags; + unsigned int flags; { DB *db = ((SMDB_DB1_DATABASE *) database->smdb_impl)->smdb1_db; DBT dbkey; - memset(&dbkey, '\0', sizeof dbkey); + (void) memset(&dbkey, '\0', sizeof dbkey); dbkey.data = key->data; dbkey.size = key->size; return db->del(db, &dbkey, flags); @@ -212,14 +207,14 @@ smdb1_get(database, key, data, flags) SMDB_DATABASE *database; SMDB_DBENT *key; SMDB_DBENT *data; - u_int flags; + unsigned int flags; { int result; DB *db = ((SMDB_DB1_DATABASE *) database->smdb_impl)->smdb1_db; DBT dbkey, dbdata; - memset(&dbdata, '\0', sizeof dbdata); - memset(&dbkey, '\0', sizeof dbkey); + (void) memset(&dbdata, '\0', sizeof dbdata); + (void) memset(&dbkey, '\0', sizeof dbkey); dbkey.data = key->data; dbkey.size = key->size; @@ -240,13 +235,13 @@ smdb1_put(database, key, data, flags) SMDB_DATABASE *database; SMDB_DBENT *key; SMDB_DBENT *data; - u_int flags; + unsigned int flags; { DB *db = ((SMDB_DB1_DATABASE *) database->smdb_impl)->smdb1_db; DBT dbkey, dbdata; - memset(&dbdata, '\0', sizeof dbdata); - memset(&dbkey, '\0', sizeof dbkey); + (void) memset(&dbdata, '\0', sizeof dbdata); + (void) memset(&dbkey, '\0', sizeof dbkey); dbkey.data = key->data; dbkey.size = key->size; dbdata.data = data->data; @@ -282,7 +277,7 @@ smdb1_set_owner(database, uid, gid) int smdb1_sync(database, flags) SMDB_DATABASE *database; - u_int flags; + unsigned int flags; { DB *db = ((SMDB_DB1_DATABASE *) database->smdb_impl)->smdb1_db; @@ -299,7 +294,7 @@ smdb1_cursor_close(cursor) if (!db1->smdb1_cursor_in_use) return SMDBE_NOT_A_VALID_CURSOR; - db1->smdb1_cursor_in_use = FALSE; + db1->smdb1_cursor_in_use = false; free(cursor); return SMDBE_OK; @@ -308,7 +303,7 @@ smdb1_cursor_close(cursor) int smdb1_cursor_del(cursor, flags) SMDB_CURSOR *cursor; - u_int flags; + unsigned int flags; { SMDB_DB1_CURSOR *db1_cursor = (SMDB_DB1_CURSOR *) cursor->smdbc_impl; SMDB_DB1_DATABASE *db1 = db1_cursor->db; @@ -331,8 +326,8 @@ smdb1_cursor_get(cursor, key, value, flags) DB *db = db1->smdb1_db; DBT dbkey, dbdata; - memset(&dbdata, '\0', sizeof dbdata); - memset(&dbkey, '\0', sizeof dbkey); + (void) memset(&dbdata, '\0', sizeof dbdata); + (void) memset(&dbkey, '\0', sizeof dbkey); db1_flags = smdb_cursor_get_flags_to_smdb1(flags); result = db->seq(db, &dbkey, &dbdata, db1_flags); @@ -359,8 +354,8 @@ smdb1_cursor_put(cursor, key, value, flags) DB *db = db1->smdb1_db; DBT dbkey, dbdata; - memset(&dbdata, '\0', sizeof dbdata); - memset(&dbkey, '\0', sizeof dbkey); + (void) memset(&dbdata, '\0', sizeof dbdata); + (void) memset(&dbkey, '\0', sizeof dbkey); dbkey.data = key->data; dbkey.size = key->size; dbdata.data = value->data; @@ -373,7 +368,7 @@ int smdb1_cursor(database, cursor, flags) SMDB_DATABASE *database; SMDB_CURSOR **cursor; - u_int flags; + unsigned int flags; { SMDB_DB1_DATABASE *db1 = (SMDB_DB1_DATABASE *) database->smdb_impl; SMDB_CURSOR *cur; @@ -382,7 +377,7 @@ smdb1_cursor(database, cursor, flags) if (db1->smdb1_cursor_in_use) return SMDBE_ONLY_SUPPORTS_ONE_CURSOR; - db1->smdb1_cursor_in_use = TRUE; + db1->smdb1_cursor_in_use = true; db1_cursor = (SMDB_DB1_CURSOR *) malloc(sizeof(SMDB_DB1_CURSOR)); db1_cursor->db = db1; @@ -400,7 +395,6 @@ smdb1_cursor(database, cursor, flags) return SMDBE_OK; } - /* ** SMDB_DB_OPEN -- Opens a db1 database. ** @@ -488,7 +482,7 @@ smdb_db_open(database, db_name, mode, mode_mask, sff, type, user_info, if (db_params != NULL && (strncmp(SMDB_TYPE_HASH, type, SMDB_TYPE_HASH_LEN) == 0)) { - memset(&hash_info, '\0', sizeof hash_info); + (void) memset(&hash_info, '\0', sizeof hash_info); hash_info.nelem = db_params->smdbp_num_elements; hash_info.cachesize = db_params->smdbp_cache_size; params = &hash_info; @@ -497,7 +491,7 @@ smdb_db_open(database, db_name, mode, mode_mask, sff, type, user_info, if (db_params != NULL && (strncmp(SMDB_TYPE_BTREE, type, SMDB_TYPE_BTREE_LEN) == 0)) { - memset(&btree_info, '\0', sizeof btree_info); + (void) memset(&btree_info, '\0', sizeof btree_info); btree_info.cachesize = db_params->smdbp_cache_size; if (db_params->smdbp_allow_dup) btree_info.flags |= R_DUP; diff --git a/gnu/usr.sbin/sendmail/libsmdb/smdb2.c b/gnu/usr.sbin/sendmail/libsmdb/smdb2.c index a1f9f20fdbb..f4c3b17db1f 100644 --- a/gnu/usr.sbin/sendmail/libsmdb/smdb2.c +++ b/gnu/usr.sbin/sendmail/libsmdb/smdb2.c @@ -7,9 +7,8 @@ ** the sendmail distribution. */ -#ifndef lint -static char id[] = "@(#)$Sendmail: smdb2.c,v 8.53.2.1.2.7 2001/02/14 04:07:24 gshapiro Exp $"; -#endif /* ! lint */ +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: smdb2.c,v 8.65 2001/05/10 01:23:58 ca Exp $") #include <fcntl.h> #include <stdlib.h> @@ -57,8 +56,6 @@ smdb_type_to_db2_type(type) return DB_UNKNOWN; } - - /* ** DB2_ERROR_TO_SMDB -- Translates db2 errors to smdbe errors ** @@ -142,7 +139,6 @@ db2_error_to_smdb(error) } return result; } - /* ** SMDB_PUT_FLAGS_TO_DB2_FLAGS -- Translates smdb put flags to db2 put flags. ** @@ -157,7 +153,7 @@ db2_error_to_smdb(error) ** */ -u_int +unsigned int smdb_put_flags_to_db2_flags(flags) SMDB_FLAG flags; { @@ -170,7 +166,6 @@ smdb_put_flags_to_db2_flags(flags) return return_flags; } - /* ** SMDB_CURSOR_GET_FLAGS_TO_DB2 -- Translates smdb cursor get flags to db2 ** getflags. @@ -209,6 +204,11 @@ smdb_cursor_get_flags_to_db2(flags) } } +/* +** Except for smdb_db_open, the rest of these functions correspond to the +** interface laid out in smdb.h. +*/ + SMDB_DB2_DATABASE * smdb2_malloc_database() { @@ -221,12 +221,6 @@ smdb2_malloc_database() return db2; } - -/* -** Except for smdb_db_open, the rest of these function correspond to the -** interface laid out in smdb.h. -*/ - int smdb2_close(database) SMDB_DATABASE *database; @@ -247,12 +241,12 @@ int smdb2_del(database, key, flags) SMDB_DATABASE *database; SMDB_DBENT *key; - u_int flags; + unsigned int flags; { DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db; DBT dbkey; - memset(&dbkey, '\0', sizeof dbkey); + (void) memset(&dbkey, '\0', sizeof dbkey); dbkey.data = key->data; dbkey.size = key->size; return db2_error_to_smdb(db->del(db, NULL, &dbkey, flags)); @@ -277,20 +271,19 @@ smdb2_lockfd(database) return db2->smdb2_lock_fd; } - int smdb2_get(database, key, data, flags) SMDB_DATABASE *database; SMDB_DBENT *key; SMDB_DBENT *data; - u_int flags; + unsigned int flags; { int result; DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db; DBT dbkey, dbdata; - memset(&dbdata, '\0', sizeof dbdata); - memset(&dbkey, '\0', sizeof dbkey); + (void) memset(&dbdata, '\0', sizeof dbdata); + (void) memset(&dbkey, '\0', sizeof dbkey); dbkey.data = key->data; dbkey.size = key->size; @@ -305,13 +298,13 @@ smdb2_put(database, key, data, flags) SMDB_DATABASE *database; SMDB_DBENT *key; SMDB_DBENT *data; - u_int flags; + unsigned int flags; { DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db; DBT dbkey, dbdata; - memset(&dbdata, '\0', sizeof dbdata); - memset(&dbkey, '\0', sizeof dbkey); + (void) memset(&dbdata, '\0', sizeof dbdata); + (void) memset(&dbkey, '\0', sizeof dbkey); dbkey.data = key->data; dbkey.size = key->size; dbdata.data = data->data; @@ -348,7 +341,7 @@ smdb2_set_owner(database, uid, gid) int smdb2_sync(database, flags) SMDB_DATABASE *database; - u_int flags; + unsigned int flags; { DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db; @@ -389,8 +382,8 @@ smdb2_cursor_get(cursor, key, value, flags) DBC *dbc = (DBC *) cursor->smdbc_impl; DBT dbkey, dbdata; - memset(&dbdata, '\0', sizeof dbdata); - memset(&dbkey, '\0', sizeof dbkey); + (void) memset(&dbdata, '\0', sizeof dbdata); + (void) memset(&dbkey, '\0', sizeof dbkey); db2_flags = smdb_cursor_get_flags_to_db2(flags); result = dbc->c_get(dbc, &dbkey, &dbdata, db2_flags); @@ -413,8 +406,8 @@ smdb2_cursor_put(cursor, key, value, flags) DBC *dbc = (DBC *) cursor->smdbc_impl; DBT dbkey, dbdata; - memset(&dbdata, '\0', sizeof dbdata); - memset(&dbkey, '\0', sizeof dbkey); + (void) memset(&dbdata, '\0', sizeof dbdata); + (void) memset(&dbkey, '\0', sizeof dbkey); dbkey.data = key->data; dbkey.size = key->size; dbdata.data = value->data; @@ -467,7 +460,7 @@ smdb_db_open_internal(db_name, db_type, db_flags, db_params, db) DB_INFO db_info; params = NULL; - memset(&db_info, '\0', sizeof db_info); + (void) memset(&db_info, '\0', sizeof db_info); if (db_params != NULL) { db_info.db_cachesize = db_params->smdbp_cache_size; @@ -576,7 +569,7 @@ smdb_db_open(database, db_name, mode, mode_mask, sff, type, user_info, db_params SMDB_USER_INFO *user_info; SMDB_DBPARAMS *db_params; { - bool lockcreated = FALSE; + bool lockcreated = false; int result; int db_flags; int lock_fd; @@ -604,7 +597,7 @@ smdb_db_open(database, db_name, mode, mode_mask, sff, type, user_info, db_params if (stat_info.st_mode == ST_MODE_NOFILE && bitset(mode, O_CREAT)) - lockcreated = TRUE; + lockcreated = true; result = smdb_lock_file(&lock_fd, db_name, mode, sff, SMDB2_FILE_EXTENSION); diff --git a/gnu/usr.sbin/sendmail/libsmdb/smndbm.c b/gnu/usr.sbin/sendmail/libsmdb/smndbm.c index 22cef581663..388936e93ea 100644 --- a/gnu/usr.sbin/sendmail/libsmdb/smndbm.c +++ b/gnu/usr.sbin/sendmail/libsmdb/smndbm.c @@ -1,5 +1,5 @@ /* -** Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers. +** Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers. ** All rights reserved. ** ** By using this file, you agree to the terms and conditions set @@ -7,9 +7,8 @@ ** the sendmail distribution. */ -#ifndef lint -static char id[] = "@(#)$Sendmail: smndbm.c,v 8.40.4.3 2000/10/05 22:27:50 gshapiro Exp $"; -#endif /* ! lint */ +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: smndbm.c,v 8.47 2001/05/10 01:23:58 ca Exp $") #include <fcntl.h> #include <stdlib.h> @@ -67,15 +66,9 @@ smdb_put_flags_to_ndbm_flags(flags) return return_flags; } -/* -** smdbm_malloc_database -- Create and initialize SMDB_DBM_DATABASE -** -** Parameters: -** None -** -** Returns: -** A pointer to an allocated SMDB_DBM_DATABASE or NULL -** +/* +** Except for smdb_ndbm_open, the rest of these function correspond to the +** interface laid out in smdb.h. */ SMDB_DBM_DATABASE * @@ -88,17 +81,12 @@ smdbm_malloc_database() { db->smndbm_dbm = NULL; db->smndbm_lock_fd = -1; - db->smndbm_cursor_in_use = FALSE; + db->smndbm_cursor_in_use = false; } return db; } -/* -** Except for smdb_ndbm_open, the rest of these function correspond to the -** interface laid out in smdb.h. -*/ - int smdbm_close(database) SMDB_DATABASE *database; @@ -120,13 +108,13 @@ int smdbm_del(database, key, flags) SMDB_DATABASE *database; SMDB_DBENT *key; - u_int flags; + unsigned int flags; { int result; DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm; datum dbkey; - memset(&dbkey, '\0', sizeof dbkey); + (void) memset(&dbkey, '\0', sizeof dbkey); dbkey.dptr = key->data; dbkey.dsize = key->size; @@ -175,13 +163,13 @@ smdbm_get(database, key, data, flags) SMDB_DATABASE *database; SMDB_DBENT *key; SMDB_DBENT *data; - u_int flags; + unsigned int flags; { DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm; datum dbkey, dbdata; - memset(&dbkey, '\0', sizeof dbkey); - memset(&dbdata, '\0', sizeof dbdata); + (void) memset(&dbkey, '\0', sizeof dbkey); + (void) memset(&dbdata, '\0', sizeof dbdata); dbkey.dptr = key->data; dbkey.dsize = key->size; @@ -209,15 +197,15 @@ smdbm_put(database, key, data, flags) SMDB_DATABASE *database; SMDB_DBENT *key; SMDB_DBENT *data; - u_int flags; + unsigned int flags; { int result; int save_errno; DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm; datum dbkey, dbdata; - memset(&dbkey, '\0', sizeof dbkey); - memset(&dbdata, '\0', sizeof dbdata); + (void) memset(&dbkey, '\0', sizeof dbkey); + (void) memset(&dbdata, '\0', sizeof dbdata); dbkey.dptr = key->data; dbkey.dsize = key->size; dbdata.dptr = data->data; @@ -282,7 +270,7 @@ smndbm_set_owner(database, uid, gid) int smdbm_sync(database, flags) SMDB_DATABASE *database; - u_int flags; + unsigned int flags; { return SMDBE_UNSUPPORTED; } @@ -297,7 +285,7 @@ smdbm_cursor_close(cursor) if (!db->smndbm_cursor_in_use) return SMDBE_NOT_A_VALID_CURSOR; - db->smndbm_cursor_in_use = FALSE; + db->smndbm_cursor_in_use = false; free(dbm_cursor); free(cursor); @@ -307,7 +295,7 @@ smdbm_cursor_close(cursor) int smdbm_cursor_del(cursor, flags) SMDB_CURSOR *cursor; - u_int flags; + unsigned int flags; { int result; SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl; @@ -343,8 +331,8 @@ smdbm_cursor_get(cursor, key, value, flags) DBM *dbm = db->smndbm_dbm; datum dbkey, dbdata; - memset(&dbkey, '\0', sizeof dbkey); - memset(&dbdata, '\0', sizeof dbdata); + (void) memset(&dbkey, '\0', sizeof dbkey); + (void) memset(&dbdata, '\0', sizeof dbdata); if (flags == SMDB_CURSOR_GET_RANGE) return SMDBE_UNSUPPORTED; @@ -406,7 +394,7 @@ smdbm_cursor_put(cursor, key, value, flags) DBM *dbm = db->smndbm_dbm; datum dbdata; - memset(&dbdata, '\0', sizeof dbdata); + (void) memset(&dbdata, '\0', sizeof dbdata); dbdata.dptr = value->data; dbdata.dsize = value->size; @@ -448,7 +436,7 @@ smdbm_cursor(database, cursor, flags) if (db->smndbm_cursor_in_use) return SMDBE_ONLY_SUPPORTS_ONE_CURSOR; - db->smndbm_cursor_in_use = TRUE; + db->smndbm_cursor_in_use = true; dbm_cursor = (SMDB_DBM_CURSOR *) malloc(sizeof(SMDB_DBM_CURSOR)); dbm_cursor->smndbmc_db = db; dbm_cursor->smndbmc_current_key.dptr = NULL; @@ -467,7 +455,6 @@ smdbm_cursor(database, cursor, flags) return SMDBE_OK; } - /* ** SMDB_NDBM_OPEN -- Opens a ndbm database. ** @@ -481,8 +468,7 @@ smdbm_cursor(database, cursor, flags) ** Only SMDB_NDBM is supported. ** user_info -- Information on the user to use for file ** permissions. -** db_params -- -** No params are supported. +** db_params -- No params are supported. ** ** Returns: ** SMDBE_OK -- Success, otherwise errno: diff --git a/gnu/usr.sbin/sendmail/libsmutil/Makefile b/gnu/usr.sbin/sendmail/libsmutil/Makefile index 6e1ce3cc1ff..a928c762124 100644 --- a/gnu/usr.sbin/sendmail/libsmutil/Makefile +++ b/gnu/usr.sbin/sendmail/libsmutil/Makefile @@ -1,7 +1,7 @@ -# $OpenBSD: Makefile,v 1.3 2000/05/15 03:37:32 millert Exp $ +# $OpenBSD: Makefile,v 1.4 2001/09/11 19:02:49 millert Exp $ LIB= smutil -SRCS= debug.c errstring.c lockfile.c safefile.c snprintf.c +SRCS= debug.c err.c lockfile.c safefile.c snprintf.c cf.c ENVDEF= -DNOT_SENDMAIL # This is not a library that gets installed so only build the .a version diff --git a/gnu/usr.sbin/sendmail/libsmutil/debug.c b/gnu/usr.sbin/sendmail/libsmutil/debug.c index ced594e0299..b043c0e1715 100644 --- a/gnu/usr.sbin/sendmail/libsmutil/debug.c +++ b/gnu/usr.sbin/sendmail/libsmutil/debug.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999 Sendmail, Inc. and its suppliers. + * Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set @@ -8,33 +8,8 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: debug.c,v 8.2 1999/07/26 04:04:09 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> -u_char tTdvect[100]; /* trace vector */ - -#if _FFR_DPRINTF_ -void -/*VARARGS1*/ -#ifdef __STDC__ -dprintf(const char *fmt, ...) -#else /* __STDC__ */ -dprintf(fmt, va_alist) - const char *fmt; - va_dcl -#endif /* __STDC__ */ -{ - VA_LOCAL_DECL; - - (void) vfprintf(stdout, fmt, ap); -} +SM_RCSID("@(#)$Sendmail: debug.c,v 8.7 2001/06/27 21:46:54 gshapiro Exp $") -int -dflush() -{ - return fflush(stdout); -} -#endif /* _FFR_DPRINTF_ */ +unsigned char tTdvect[100]; /* trace vector */ diff --git a/gnu/usr.sbin/sendmail/libsmutil/lockfile.c b/gnu/usr.sbin/sendmail/libsmutil/lockfile.c index d9d87ad0c69..f72d0fa284b 100644 --- a/gnu/usr.sbin/sendmail/libsmutil/lockfile.c +++ b/gnu/usr.sbin/sendmail/libsmutil/lockfile.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. + * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 @@ -11,12 +11,11 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: lockfile.c,v 8.3.16.11 2000/11/16 02:54:28 geir Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +SM_RCSID("@(#)$Sendmail: lockfile.c,v 8.16 2001/01/24 00:34:54 ca Exp $") + + /* ** LOCKFILE -- lock a file using flock or (shudder) fcntl locking ** @@ -30,8 +29,8 @@ static char id[] = "@(#)$Sendmail: lockfile.c,v 8.3.16.11 2000/11/16 02:54:28 ge ** LOCK_UN -- unlock. ** ** Returns: -** TRUE if the lock was acquired. -** FALSE otherwise. +** true if the lock was acquired. +** false otherwise. */ bool @@ -59,7 +58,7 @@ lockfile(fd, filename, ext, type) action = F_SETLKW; if (fcntl(fd, action, &lfd) >= 0) - return TRUE; + return true; /* ** On SunOS, if you are testing using -oQ/tmp/mqueue or @@ -71,14 +70,14 @@ lockfile(fd, filename, ext, type) */ if (errno == EINVAL) - return TRUE; + return true; #else /* !HASFLOCK */ if (flock(fd, type) >= 0) - return TRUE; + return true; #endif /* !HASFLOCK */ - return FALSE; + return false; } diff --git a/gnu/usr.sbin/sendmail/libsmutil/safefile.c b/gnu/usr.sbin/sendmail/libsmutil/safefile.c index de2b155653c..9a046038250 100644 --- a/gnu/usr.sbin/sendmail/libsmutil/safefile.c +++ b/gnu/usr.sbin/sendmail/libsmutil/safefile.c @@ -11,11 +11,11 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: safefile.c,v 8.81.4.10 2001/07/20 04:19:36 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +#include <sm/io.h> +#include <sm/errstring.h> + +SM_RCSID("@(#)$Sendmail: safefile.c,v 8.114 2001/09/08 01:21:03 gshapiro Exp $") /* @@ -61,18 +61,18 @@ safefile(fn, uid, gid, user, flags, mode, st) char fbuf[MAXPATHLEN + 1]; if (tTd(44, 4)) - dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n", + sm_dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n", fn, (int) uid, (int) gid, flags, mode); errno = 0; - if (st == NULL) - st = &fstbuf; - if (strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf) + if (sm_strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf) { if (tTd(44, 4)) - dprintf("\tpathname too long\n"); + sm_dprintf("\tpathname too long\n"); return ENAMETOOLONG; } fn = fbuf; + if (st == NULL) + st = &fstbuf; /* ignore SFF_SAFEDIRPATH if we are debugging */ if (RealUid != 0 && RunAsUid == RealUid) @@ -93,7 +93,7 @@ safefile(fn, uid, gid, user, flags, mode, st) S_ISREG(st->st_mode)) { /* - ** If final file is setuid, run as the owner of that + ** If final file is set-user-ID, run as the owner of that ** file. Gotta be careful not to reveal anything too ** soon here! */ @@ -139,7 +139,7 @@ safefile(fn, uid, gid, user, flags, mode, st) if (ret == 0) { /* directory is safe */ - checkpath = FALSE; + checkpath = false; } else { @@ -149,7 +149,7 @@ safefile(fn, uid, gid, user, flags, mode, st) { ret = errno; if (tTd(44, 4)) - dprintf("\t%s\n", errstring(ret)); + sm_dprintf("\t%s\n", sm_errstring(ret)); return ret; } # endif /* HASLSTAT */ @@ -188,7 +188,7 @@ safefile(fn, uid, gid, user, flags, mode, st) char *dir = fn; if (tTd(44, 4)) - dprintf("\t%s\n", errstring(ret)); + sm_dprintf("\t%s\n", sm_errstring(ret)); errno = 0; if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT) @@ -241,9 +241,10 @@ safefile(fn, uid, gid, user, flags, mode, st) } ret = errno; if (tTd(44, 4)) - dprintf("\t[final dir %s uid %d mode %lo] %s\n", - dir, (int) stbuf.st_uid, (u_long) stbuf.st_mode, - errstring(ret)); + sm_dprintf("\t[final dir %s uid %d mode %lo] %s\n", + dir, (int) stbuf.st_uid, + (unsigned long) stbuf.st_mode, + sm_errstring(ret)); if (p != NULL) *p = '/'; st->st_mode = ST_MODE_NOFILE; @@ -254,46 +255,46 @@ safefile(fn, uid, gid, user, flags, mode, st) if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode)) { if (tTd(44, 4)) - dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n", - (u_long) st->st_mode); + sm_dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n", + (unsigned long) st->st_mode); return E_SM_NOSLINK; } # endif /* S_ISLNK */ if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode)) { if (tTd(44, 4)) - dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n", - (u_long) st->st_mode); + sm_dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n", + (unsigned long) st->st_mode); return E_SM_REGONLY; } if (bitset(SFF_NOGWFILES, flags) && bitset(S_IWGRP, st->st_mode)) { if (tTd(44, 4)) - dprintf("\t[write bits %lo]\tE_SM_GWFILE\n", - (u_long) st->st_mode); + sm_dprintf("\t[write bits %lo]\tE_SM_GWFILE\n", + (unsigned long) st->st_mode); return E_SM_GWFILE; } if (bitset(SFF_NOWWFILES, flags) && bitset(S_IWOTH, st->st_mode)) { if (tTd(44, 4)) - dprintf("\t[write bits %lo]\tE_SM_WWFILE\n", - (u_long) st->st_mode); + sm_dprintf("\t[write bits %lo]\tE_SM_WWFILE\n", + (unsigned long) st->st_mode); return E_SM_WWFILE; } if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode)) { if (tTd(44, 4)) - dprintf("\t[read bits %lo]\tE_SM_GRFILE\n", - (u_long) st->st_mode); + sm_dprintf("\t[read bits %lo]\tE_SM_GRFILE\n", + (unsigned long) st->st_mode); return E_SM_GRFILE; } if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode)) { if (tTd(44, 4)) - dprintf("\t[read bits %lo]\tE_SM_WRFILE\n", - (u_long) st->st_mode); + sm_dprintf("\t[read bits %lo]\tE_SM_WRFILE\n", + (unsigned long) st->st_mode); return E_SM_WRFILE; } if (!bitset(SFF_EXECOK, flags) && @@ -301,14 +302,14 @@ safefile(fn, uid, gid, user, flags, mode, st) bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode)) { if (tTd(44, 4)) - dprintf("\t[exec bits %lo]\tE_SM_ISEXEC]\n", - (u_long) st->st_mode); + sm_dprintf("\t[exec bits %lo]\tE_SM_ISEXEC]\n", + (unsigned long) st->st_mode); return E_SM_ISEXEC; } if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1) { if (tTd(44, 4)) - dprintf("\t[link count %d]\tE_SM_NOHLINK\n", + sm_dprintf("\t[link count %d]\tE_SM_NOHLINK\n", (int) st->st_nlink); return E_SM_NOHLINK; } @@ -348,20 +349,20 @@ safefile(fn, uid, gid, user, flags, mode, st) mode >>= 3; } if (tTd(44, 4)) - dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ", + sm_dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ", (int) st->st_uid, (int) st->st_nlink, - (u_long) st->st_mode, (u_long) mode); + (unsigned long) st->st_mode, (unsigned long) mode); if ((st->st_uid == uid || st->st_uid == 0 || st->st_uid == TrustedUid || !bitset(SFF_MUSTOWN, flags)) && (st->st_mode & mode) == mode) { if (tTd(44, 4)) - dprintf("\tOK\n"); + sm_dprintf("\tOK\n"); return 0; } if (tTd(44, 4)) - dprintf("\tEACCES\n"); + sm_dprintf("\tEACCES\n"); return EACCES; } /* @@ -410,19 +411,22 @@ safedirpath(fn, uid, gid, user, flags, level, offset) if (level > MAXSYMLINKS) return ELOOP; + if (level < 0 || offset < 0 || offset > strlen(fn)) + return EINVAL; + /* special case root directory */ if (*fn == '\0') fn = "/"; if (tTd(44, 4)) - dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n", + sm_dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n", fn, (long) uid, (long) gid, flags, level, offset); if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail)) mode |= S_IWGRP; /* Make a modifiable copy of the filename */ - if (strlcpy(s, fn, sizeof s) >= sizeof s) + if (sm_strlcpy(s, fn, sizeof s) >= sizeof s) return EINVAL; p = s + offset; @@ -463,7 +467,7 @@ safedirpath(fn, uid, gid, user, flags, level, offset) continue; if (tTd(44, 20)) - dprintf("\t[dir %s]\n", s); + sm_dprintf("\t[dir %s]\n", s); # if HASLSTAT ret = lstat(s, &stbuf); @@ -537,25 +541,28 @@ safedirpath(fn, uid, gid, user, flags, level, offset) { *sptr = '\0'; offset = sptr + 1 - s; - if ((strlen(s) + 1 + - strlen(buf) + 1) > sizeof fullbuf) + if (sm_strlcpyn(fullbuf, + sizeof fullbuf, 2, + s, "/") >= + sizeof fullbuf || + sm_strlcat(fullbuf, buf, + sizeof fullbuf) >= + sizeof fullbuf) { ret = EINVAL; break; } - snprintf(fullbuf, sizeof fullbuf, - "%s/%s", s, buf); *sptr = '/'; } else { - if (strlen(buf) + 1 > sizeof fullbuf) + if (sm_strlcpy(fullbuf, buf, + sizeof fullbuf) >= + sizeof fullbuf) { ret = EINVAL; break; } - (void) strlcpy(fullbuf, buf, - sizeof fullbuf); } target = fullbuf; } @@ -577,8 +584,8 @@ safedirpath(fn, uid, gid, user, flags, level, offset) bitset(mode, stbuf.st_mode)) { if (tTd(44, 4)) - dprintf("\t[dir %s] mode %lo ", - s, (u_long) stbuf.st_mode); + sm_dprintf("\t[dir %s] mode %lo ", + s, (unsigned long) stbuf.st_mode); if (bitset(SFF_SAFEDIRPATH, flags)) { if (bitset(S_IWOTH, stbuf.st_mode)) @@ -586,11 +593,11 @@ safedirpath(fn, uid, gid, user, flags, level, offset) else ret = E_SM_GWDIR; if (tTd(44, 4)) - dprintf("FATAL\n"); + sm_dprintf("FATAL\n"); break; } if (tTd(44, 4)) - dprintf("WARNING\n"); + sm_dprintf("WARNING\n"); if (Verbose > 1) message("051 WARNING: %s writable directory %s", bitset(S_IWOTH, stbuf.st_mode) @@ -643,8 +650,8 @@ safedirpath(fn, uid, gid, user, flags, level, offset) } } if (tTd(44, 4)) - dprintf("\t[dir %s] %s\n", fn, - ret == 0 ? "OK" : errstring(ret)); + sm_dprintf("\t[dir %s] %s\n", fn, + ret == 0 ? "OK" : sm_errstring(ret)); return ret; } /* @@ -673,8 +680,8 @@ safeopen(fn, omode, cmode, sff) struct stat stb; if (tTd(44, 10)) - printf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n", - fn, omode, cmode, sff); + sm_dprintf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n", + fn, omode, cmode, sff); if (bitset(O_CREAT, omode)) sff |= SFF_CREAT; @@ -731,6 +738,81 @@ safeopen(fn, omode, cmode, sff) return fd; } /* +** SAFEFOPEN -- do a file open with extra checking +** +** Parameters: +** fn -- the file name to open. +** omode -- the open-style mode flags. +** cmode -- the create-style mode flags. +** sff -- safefile flags. +** +** Returns: +** Same as fopen. +*/ + +SM_FILE_T * +safefopen(fn, omode, cmode, sff) + char *fn; + int omode; + int cmode; + long sff; +{ + int fd; + int save_errno; + SM_FILE_T *fp; + int fmode; + + switch (omode & O_ACCMODE) + { + case O_RDONLY: + fmode = SM_IO_RDONLY; + break; + + case O_WRONLY: + if (bitset(O_APPEND, omode)) + fmode = SM_IO_APPEND; + else + fmode = SM_IO_WRONLY; + break; + + case O_RDWR: + if (bitset(O_TRUNC, omode)) + fmode = SM_IO_RDWRTR; + else if (bitset(O_APPEND, omode)) + fmode = SM_IO_APPENDRW; + else + fmode = SM_IO_RDWR; + break; + + default: + syserr("554 5.3.5 safefopen: unknown omode %o", omode); + fmode = 0; + } + fd = safeopen(fn, omode, cmode, sff); + if (fd < 0) + { + save_errno = errno; + if (tTd(44, 10)) + sm_dprintf("safefopen: safeopen failed: %s\n", + sm_errstring(errno)); + errno = save_errno; + return NULL; + } + fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) fd, fmode, NULL); + if (fp != NULL) + return fp; + + save_errno = errno; + if (tTd(44, 10)) + { + sm_dprintf("safefopen: fdopen(%s, %d) failed: omode=%x, sff=%lx, err=%s\n", + fn, fmode, omode, sff, sm_errstring(errno)); + } + (void) close(fd); + errno = save_errno; + return NULL; +} +/* ** FILECHANGED -- check to see if file changed after being opened ** ** Parameters: @@ -739,8 +821,8 @@ safeopen(fn, omode, cmode, sff) ** stb -- stat structure from before open. ** ** Returns: -** TRUE -- if a problem was detected. -** FALSE -- if this file is still the same. +** true -- if a problem was detected. +** false -- if this file is still the same. */ bool @@ -756,13 +838,13 @@ filechanged(fn, fd, stb) # if HASLSTAT && BOGUS_O_EXCL /* only necessary if exclusive open follows symbolic links */ if (lstat(fn, stb) < 0 || stb->st_nlink != 1) - return TRUE; + return true; # else /* HASLSTAT && BOGUS_O_EXCL */ - return FALSE; + return false; # endif /* HASLSTAT && BOGUS_O_EXCL */ } if (fstat(fd, &sta) < 0) - return TRUE; + return true; if (sta.st_nlink != stb->st_nlink || sta.st_dev != stb->st_dev || @@ -775,35 +857,27 @@ filechanged(fn, fd, stb) { if (tTd(44, 8)) { - dprintf("File changed after opening:\n"); - dprintf(" nlink = %ld/%ld\n", + sm_dprintf("File changed after opening:\n"); + sm_dprintf(" nlink = %ld/%ld\n", (long) stb->st_nlink, (long) sta.st_nlink); - dprintf(" dev = %ld/%ld\n", + sm_dprintf(" dev = %ld/%ld\n", (long) stb->st_dev, (long) sta.st_dev); - if (sizeof sta.st_ino > sizeof (long)) - { - dprintf(" ino = %s/", - quad_to_string(stb->st_ino)); - dprintf("%s\n", - quad_to_string(sta.st_ino)); - } - else - dprintf(" ino = %lu/%lu\n", - (unsigned long) stb->st_ino, - (unsigned long) sta.st_ino); + sm_dprintf(" ino = %llu/%llu\n", + (ULONGLONG_T) stb->st_ino, + (ULONGLONG_T) sta.st_ino); # if HAS_ST_GEN - dprintf(" gen = %ld/%ld\n", + sm_dprintf(" gen = %ld/%ld\n", (long) stb->st_gen, (long) sta.st_gen); # endif /* HAS_ST_GEN */ - dprintf(" uid = %ld/%ld\n", + sm_dprintf(" uid = %ld/%ld\n", (long) stb->st_uid, (long) sta.st_uid); - dprintf(" gid = %ld/%ld\n", + sm_dprintf(" gid = %ld/%ld\n", (long) stb->st_gid, (long) sta.st_gid); } - return TRUE; + return true; } - return FALSE; + return false; } /* ** DFOPEN -- determined file open diff --git a/gnu/usr.sbin/sendmail/libsmutil/snprintf.c b/gnu/usr.sbin/sendmail/libsmutil/snprintf.c index 0aaeb668ff2..5c6c3d44f34 100644 --- a/gnu/usr.sbin/sendmail/libsmutil/snprintf.c +++ b/gnu/usr.sbin/sendmail/libsmutil/snprintf.c @@ -11,379 +11,10 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: snprintf.c,v 8.27.16.4 2001/07/20 04:19:37 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> -/* -** SNPRINTF, VSNPRINT -- counted versions of printf -** -** These versions have been grabbed off the net. They have been -** cleaned up to compile properly and support for .precision and -** %lx has been added. -*/ - -/************************************************************** - * Original: - * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 - * A bombproof version of doprnt (sm_dopr) included. - * Sigh. This sort of thing is always nasty do deal with. Note that - * the version here does not include floating point... - * - * snprintf() is used instead of sprintf() as it does limit checks - * for string length. This covers a nasty loophole. - * - * The other functions are there to prevent NULL pointers from - * causing nast effects. - **************************************************************/ - -/*static char _id[] = "$OrigId: snprintf.c,v 1.2 1995/10/09 11:19:47 roberto Exp $";*/ -void sm_dopr(); -char *DoprEnd; -int SnprfOverflow; - -#if !HASSNPRINTF && !SNPRINTF_IS_BROKEN -# define sm_snprintf snprintf -# ifndef luna2 -# define sm_vsnprintf vsnprintf -extern int vsnprintf __P((char *, size_t, const char *, va_list)); -# endif /* ! luna2 */ -#endif /* !HASSNPRINTF && !SNPRINTF_IS_BROKEN */ - -/* VARARGS3 */ -int -# ifdef __STDC__ -sm_snprintf(char *str, size_t count, const char *fmt, ...) -# else /* __STDC__ */ -sm_snprintf(str, count, fmt, va_alist) - char *str; - size_t count; - const char *fmt; - va_dcl -# endif /* __STDC__ */ -{ - int len; - VA_LOCAL_DECL - - VA_START(fmt); - len = sm_vsnprintf(str, count, fmt, ap); - VA_END; - return len; -} - -int -sm_vsnprintf(str, count, fmt, args) - char *str; - size_t count; - const char *fmt; - va_list args; -{ - str[0] = 0; - DoprEnd = str + count - 1; - SnprfOverflow = 0; - sm_dopr( str, fmt, args ); - if (count > 0) - DoprEnd[0] = 0; - if (SnprfOverflow > 0 && tTd(57, 2)) - dprintf("\nvsnprintf overflow, len = %ld, str = %s", - (long) count, shortenstring(str, MAXSHORTSTR)); - return strlen(str) + SnprfOverflow; -} - -/* - * sm_dopr(): poor man's version of doprintf - */ - -void fmtstr __P((char *value, int ljust, int len, int zpad, int maxwidth)); -void fmtnum __P((long value, int base, int dosign, int ljust, int len, int zpad)); -void dostr __P(( char * , int )); -char *output; -void dopr_outch __P(( int c )); -int SyslogErrno; - -void -sm_dopr( buffer, format, args ) - char *buffer; - const char *format; - va_list args; -{ - int ch; - long value; - int longflag = 0; - int pointflag = 0; - int maxwidth = 0; - char *strvalue; - int ljust; - int len; - int zpad; -#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) - extern char *sys_errlist[]; - extern int sys_nerr; -#endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */ - - - output = buffer; - while( (ch = *format++) != '\0' ){ - switch( ch ){ - case '%': - ljust = len = zpad = maxwidth = 0; - longflag = pointflag = 0; - nextch: - ch = *format++; - switch( ch ){ - case 0: - dostr( "**end of format**" , 0); - return; - case '-': ljust = 1; goto nextch; - case '0': /* set zero padding if len not set */ - if(len==0 && !pointflag) zpad = '0'; - /* FALLTHROUGH */ - case '1': case '2': case '3': - case '4': case '5': case '6': - case '7': case '8': case '9': - if (pointflag) - maxwidth = maxwidth*10 + ch - '0'; - else - len = len*10 + ch - '0'; - goto nextch; - case '*': - if (pointflag) - maxwidth = va_arg( args, int ); - else - len = va_arg( args, int ); - goto nextch; - case '.': pointflag = 1; goto nextch; - case 'l': longflag = 1; goto nextch; - case 'u': case 'U': - /*fmtnum(value,base,dosign,ljust,len,zpad) */ - if( longflag ){ - value = va_arg( args, long ); - } else { - value = va_arg( args, int ); - } - fmtnum( value, 10,0, ljust, len, zpad ); break; - case 'o': case 'O': - /*fmtnum(value,base,dosign,ljust,len,zpad) */ - if( longflag ){ - value = va_arg( args, long ); - } else { - value = va_arg( args, int ); - } - fmtnum( value, 8,0, ljust, len, zpad ); break; - case 'd': case 'D': - if( longflag ){ - value = va_arg( args, long ); - } else { - value = va_arg( args, int ); - } - fmtnum( value, 10,1, ljust, len, zpad ); break; - case 'x': - if( longflag ){ - value = va_arg( args, long ); - } else { - value = va_arg( args, int ); - } - fmtnum( value, 16,0, ljust, len, zpad ); break; - case 'X': - if( longflag ){ - value = va_arg( args, long ); - } else { - value = va_arg( args, int ); - } - fmtnum( value,-16,0, ljust, len, zpad ); break; - case 's': - strvalue = va_arg( args, char *); - if (maxwidth > 0 || !pointflag) { - if (pointflag && len > maxwidth) - len = maxwidth; /* Adjust padding */ - fmtstr( strvalue,ljust,len,zpad, maxwidth); - } - break; - case 'c': - ch = va_arg( args, int ); - dopr_outch( ch ); break; - case 'm': -#if HASSTRERROR - dostr(strerror(SyslogErrno), 0); -#else /* HASSTRERROR */ - if (SyslogErrno < 0 || SyslogErrno >= sys_nerr) - { - dostr("Error ", 0); - fmtnum(SyslogErrno, 10, 0, 0, 0, 0); - } - else - dostr((char *)sys_errlist[SyslogErrno], 0); -#endif /* HASSTRERROR */ - break; - - case '%': dopr_outch( ch ); continue; - default: - dostr( "???????" , 0); - } - break; - default: - dopr_outch( ch ); - break; - } - } - *output = 0; -} - -void -fmtstr( value, ljust, len, zpad, maxwidth ) - char *value; - int ljust, len, zpad, maxwidth; -{ - int padlen, strleng; /* amount to pad */ - - if( value == 0 ){ - value = "<NULL>"; - } - for( strleng = 0; value[strleng]; ++ strleng ); /* strlen */ - if (strleng > maxwidth && maxwidth) - strleng = maxwidth; - padlen = len - strleng; - if( padlen < 0 ) padlen = 0; - if( ljust ) padlen = -padlen; - while( padlen > 0 ) { - dopr_outch( ' ' ); - --padlen; - } - dostr( value, maxwidth ); - while( padlen < 0 ) { - dopr_outch( ' ' ); - ++padlen; - } -} - -void -fmtnum( value, base, dosign, ljust, len, zpad ) - long value; - int base, dosign, ljust, len, zpad; -{ - int signvalue = 0; - unsigned long uvalue; - char convert[20]; - int place = 0; - int padlen = 0; /* amount to pad */ - int caps = 0; - - /* DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n", - value, base, dosign, ljust, len, zpad )); */ - uvalue = value; - if( dosign ){ - if( value < 0 ) { - signvalue = '-'; - uvalue = -value; - } - } - if( base < 0 ){ - caps = 1; - base = -base; - } - do{ - convert[place++] = - (caps? "0123456789ABCDEF":"0123456789abcdef") - [uvalue % (unsigned)base ]; - uvalue = (uvalue / (unsigned)base ); - }while(uvalue); - convert[place] = 0; - padlen = len - place; - if( padlen < 0 ) padlen = 0; - if( ljust ) padlen = -padlen; - /* DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n", - convert,place,signvalue,padlen)); */ - if( zpad && padlen > 0 ){ - if( signvalue ){ - dopr_outch( signvalue ); - --padlen; - signvalue = 0; - } - while( padlen > 0 ){ - dopr_outch( zpad ); - --padlen; - } - } - while( padlen > 0 ) { - dopr_outch( ' ' ); - --padlen; - } - if( signvalue ) dopr_outch( signvalue ); - while( place > 0 ) dopr_outch( convert[--place] ); - while( padlen < 0 ){ - dopr_outch( ' ' ); - ++padlen; - } -} +SM_RCSID("@(#)$Sendmail: snprintf.c,v 8.41 2001/08/28 23:07:01 gshapiro Exp $") -void -dostr( str , cut) - char *str; - int cut; -{ - if (cut) { - while(*str && cut-- > 0) dopr_outch(*str++); - } else { - while(*str) dopr_outch(*str++); - } -} - -void -dopr_outch( c ) - int c; -{ -#if 0 - if( iscntrl(c) && c != '\n' && c != '\t' ){ - c = '@' + (c & 0x1F); - if( DoprEnd == 0 || output < DoprEnd ) - *output++ = '^'; - } -#endif /* 0 */ - if( DoprEnd == 0 || output < DoprEnd ) - *output++ = c; - else - SnprfOverflow++; -} - -/* -** QUAD_TO_STRING -- Convert a quad type to a string. -** -** Convert a quad type to a string. This must be done -** separately as %lld/%qd are not supported by snprint() -** and adding support would slow down systems which only -** emulate the data type. -** -** Parameters: -** value -- number to convert to a string. -** -** Returns: -** pointer to a string. -*/ - -char * -quad_to_string(value) - QUAD_T value; -{ - char *formatstr; - static char buf[64]; - - /* - ** Use sprintf() instead of snprintf() since snprintf() - ** does not support %qu or %llu. The buffer is large enough - ** to hold the string so there is no danger of buffer - ** overflow. - */ - -#if NEED_PERCENTQ - formatstr = "%qu"; -#else /* NEED_PERCENTQ */ - formatstr = "%llu"; -#endif /* NEED_PERCENTQ */ - sprintf(buf, formatstr, value); - return buf; -} /* ** SHORTENSTRING -- return short version of a string ** @@ -401,9 +32,9 @@ quad_to_string(value) char * shortenstring(s, m) register const char *s; - int m; + size_t m; { - int l; + size_t l; static char buf[MAXSHORTSTR + 1]; l = strlen(s); @@ -415,16 +46,15 @@ shortenstring(s, m) { if (m < 5) { - (void) strlcpy(buf, s, m + 1); + (void) sm_strlcpy(buf, s, m + 1); return buf; } - (void) strlcpy(buf, s, m - 2); - (void) strlcat(buf, "...", sizeof buf); + (void) sm_strlcpy(buf, s, m - 2); + (void) sm_strlcat(buf, "...", sizeof buf); return buf; } m = (m - 3) / 2; - (void) strlcpy(buf, s, m + 1); - (void) strlcat(buf, "...", sizeof buf); - (void) strlcat(buf, s + l - m, sizeof buf); + (void) sm_strlcpy(buf, s, m + 1); + (void) sm_strlcat2(buf, "...", s + l - m, sizeof buf); return buf; } diff --git a/gnu/usr.sbin/sendmail/mail.local/Makefile b/gnu/usr.sbin/sendmail/mail.local/Makefile index 09ee3f6efa9..df4dc409d32 100644 --- a/gnu/usr.sbin/sendmail/mail.local/Makefile +++ b/gnu/usr.sbin/sendmail/mail.local/Makefile @@ -1,9 +1,9 @@ -# $OpenBSD: Makefile,v 1.4 2000/06/15 12:31:59 millert Exp $ +# $OpenBSD: Makefile,v 1.5 2001/09/11 19:02:49 millert Exp $ PROG= mail.local MAN= mail.local.8 -WANT_LIBSMUTIL=1 +WANT_LIBSM=1 .include "../../../libexec/Makefile.inc" .include <bsd.prog.mk> diff --git a/gnu/usr.sbin/sendmail/mail.local/README b/gnu/usr.sbin/sendmail/mail.local/README index d9cf67388f5..520eca1b199 100644 --- a/gnu/usr.sbin/sendmail/mail.local/README +++ b/gnu/usr.sbin/sendmail/mail.local/README @@ -2,8 +2,8 @@ This directory contains the source files for mail.local. This is not intended to be used on *stock* System V derived systems such as Solaris or HP-UX, since they use a totally different approach to mailboxes -(essentially, they have a setgid program rather than setuid, and they rely -on the ability to "give away" files to do their work). +(essentially, they have a set-group-ID program rather than set-user-ID, and +they rely on the ability to "give away" files to do their work). If you choose to run *this* mail.local on these systems then you may also need to replace the existing MUAs, as well as IMAP and POP servers, with @@ -29,11 +29,11 @@ your site.config.m4 file with: APPENDDEF(`conf_mail_local_ENVDEF', `-DMAILGID=6') -mail.local will not be installed setuid root. To use it as local +mail.local will not be installed set-user-ID root. To use it as local delivery agent without LMTP mode, use: MODIFY_MAILER_FLAGS(`LOCAL', `+S') in the .mc file. -$Revision: 1.3 $, Last updated $Date: 2001/01/15 21:09:04 $ +$Revision: 1.4 $, Last updated $Date: 2001/09/11 19:02:49 $ diff --git a/gnu/usr.sbin/sendmail/mail.local/mail.local.8 b/gnu/usr.sbin/sendmail/mail.local/mail.local.8 index badb7b1d992..c56a627a1c5 100644 --- a/gnu/usr.sbin/sendmail/mail.local/mail.local.8 +++ b/gnu/usr.sbin/sendmail/mail.local/mail.local.8 @@ -1,4 +1,4 @@ -.\" Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. +.\" Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. .\" All rights reserved. .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. @@ -8,18 +8,22 @@ .\" the sendmail distribution. .\" .\" -.\" $Sendmail: mail.local.8,v 8.14.14.5 2000/12/29 18:12:16 gshapiro Exp $ +.\" $Sendmail: mail.local.8,v 8.23 2001/04/05 23:27:35 gshapiro Exp $ .\" -.TH MAIL.LOCAL 8 "$Date: 2001/01/15 21:09:04 $" +.TH MAIL.LOCAL 8 "$Date: 2001/09/11 19:02:49 $" .SH NAME mail.local \- store mail in a mailbox .SH SYNOPSIS .B mail.local -.RB [ \-7 "] [" \-b "] [" \-d "] [" \-l "] [" \-f -.IR from "] " -.RB [ \-r -.IR from "] " "user ..." +.RB [ \-7 "] [" \-b "] [" \-d "] [" \-D +.IR mbdb ] +.RB [ \-l "] [" \-f +\fIfrom\fR|\fB\-r\fR +.IR from ] +.RB [ \-h +\fIfilename\fR ] +.I "user ..." .SH DESCRIPTION .B Mail.local reads the standard input up to an end-of-file and appends it to each @@ -40,6 +44,12 @@ if a mailbox exceeds quota. .TP .B \-d Specify this is a delivery (for backward compatibility). +This option has no effect. +.TP +.BI \-D " mbdb" +Specify the name of the mailbox database +which is used to look up local recipient names. +This option defaults to "pw", which means use getpwnam(). .TP .BI \-f " from" Specify the sender's name. @@ -49,6 +59,11 @@ Turn on LMTP mode. .TP .BI \-r " from" Specify the sender's name (for backward compatibility). +Same as \-f. +.TP +.BI \-h " filename" +Store incoming mail in \fIfilename\fR in the user's home directory instead +of a system mail spool directory. .PP Individual mail messages in the mailbox are delimited by an empty line followed by a line beginning with the string ``From ''. @@ -86,10 +101,10 @@ Used to set the appropriate time zone on the timestamp. temporary files .TP /var/mail/user -user's mailbox directory +user's default mailbox directory .TP /var/mail/user.lock -lock file for a user's mailbox +lock file for a user's default mailbox .PD .SH SEE ALSO mail(1), diff --git a/gnu/usr.sbin/sendmail/mail.local/mail.local.c b/gnu/usr.sbin/sendmail/mail.local/mail.local.c index 971273a12f0..4b5b9ce350f 100644 --- a/gnu/usr.sbin/sendmail/mail.local/mail.local.c +++ b/gnu/usr.sbin/sendmail/mail.local/mail.local.c @@ -10,232 +10,110 @@ * */ -#ifndef lint -static char copyright[] = +#include <sm/gen.h> + +SM_IDSTR(copyright, "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ All rights reserved.\n\ Copyright (c) 1990, 1993, 1994\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* ! lint */ + The Regents of the University of California. All rights reserved.\n") + +SM_IDSTR(id, "@(#)$Sendmail: mail.local.c,v 8.232 2001/09/08 01:21:04 gshapiro Exp $") -#ifndef lint -static char id[] = "@(#)$Sendmail: mail.local.c,v 8.143.4.58 2001/06/01 05:33:31 gshapiro Exp $"; -#endif /* ! lint */ +#include <stdlib.h> +#include <sm/errstring.h> +#include <sm/io.h> +# include <unistd.h> +# ifdef EX_OK +# undef EX_OK /* unistd.h may have another use for this */ +# endif /* EX_OK */ +#include <sm/mbdb.h> +#include <sm/sysexits.h> /* ** This is not intended to work on System V derived systems ** such as Solaris or HP-UX, since they use a totally different -** approach to mailboxes (essentially, they have a setgid program -** rather than setuid, and they rely on the ability to "give away" +** approach to mailboxes (essentially, they have a set-group-ID program +** rather than set-user-ID, and they rely on the ability to "give away" ** files to do their work). IT IS NOT A BUG that this doesn't ** work on such architectures. */ -/* additional mode for open() */ -# define EXTRA_MODE 0 - -# include <sys/types.h> -# include <sys/param.h> -# include <sys/stat.h> +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <stdlib.h> # include <sys/socket.h> # include <sys/file.h> - # include <netinet/in.h> # include <arpa/nameser.h> - -# include <fcntl.h> # include <netdb.h> -# include <pwd.h> -# include <stdio.h> -# include <stdlib.h> -# include <string.h> -# include <syslog.h> -# include <time.h> -# include <unistd.h> -# ifdef EX_OK -# undef EX_OK /* unistd.h may have another use for this */ -# endif /* EX_OK */ -# include <sysexits.h> -# include <ctype.h> +# include <pwd.h> -# ifndef __P -# include "sendmail/cdefs.h" -# endif /* ! __P */ -# include "sendmail/useful.h" - -extern size_t strlcpy __P((char *, const char *, size_t)); -extern size_t strlcat __P((char *, const char *, size_t)); - -# if defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__) || defined(IRIX64) || defined(IRIX5) || defined(IRIX6) -# ifndef HASSTRERROR -# define HASSTRERROR 1 -# endif /* ! HASSTRERROR */ -# endif /* defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__) || defined(IRIX64) || defined(IRIX5) || defined(IRIX6) */ - -# include "sendmail/errstring.h" - -# ifndef LOCKTO_RM -# define LOCKTO_RM 300 /* timeout for stale lockfile removal */ -# endif /* ! LOCKTO_RM */ -# ifndef LOCKTO_GLOB -# define LOCKTO_GLOB 400 /* global timeout for lockfile creation */ -# endif /* ! LOCKTO_GLOB */ - -# ifdef __STDC__ -# include <stdarg.h> -# define REALLOC(ptr, size) realloc(ptr, size) -# else /* __STDC__ */ -# include <varargs.h> -/* define a realloc() which works for NULL pointers */ -# define REALLOC(ptr, size) (((ptr) == NULL) ? malloc(size) : realloc(ptr, size)) -# endif /* __STDC__ */ - -# if (defined(sun) && defined(__svr4__)) || defined(__SVR4) -# define USE_LOCKF 1 -# define USE_SETEUID 1 -# define _PATH_MAILDIR "/var/mail" -# endif /* (defined(sun) && defined(__svr4__)) || defined(__SVR4) */ - -# ifdef NCR_MP_RAS3 -# define USE_LOCKF 1 -# define HASSNPRINTF 1 -# define _PATH_MAILDIR "/var/mail" -# endif /* NCR_MP_RAS3 */ - -# if defined(_AIX) -# define USE_LOCKF 1 -# define USE_SETEUID 1 -# endif /* defined(_AIX) */ - -# if defined(__hpux) -# define USE_LOCKF 1 -# define USE_SETRESUID 1 -# endif /* defined(__hpux) */ - -# ifdef DGUX -# define HASSNPRINTF 1 -# define USE_LOCKF 1 -# endif /* DGUX */ - -# if defined(_CRAY) -# if !defined(MAXPATHLEN) -# define MAXPATHLEN PATHSIZE -# endif /* !defined(MAXPATHLEN) */ -# define _PATH_MAILDIR "/usr/spool/mail" -# endif /* defined(_CRAY) */ - -# if defined(NeXT) && !defined(__APPLE__) -# include <libc.h> -# define _PATH_MAILDIR "/usr/spool/mail" -# define S_IRUSR S_IREAD -# define S_IWUSR S_IWRITE -# endif /* defined(NeXT) && !defined(__APPLE__) */ - -# if defined(IRIX64) || defined(IRIX5) || defined(IRIX6) -# include <paths.h> -# endif /* defined(IRIX64) || defined(IRIX5) || defined(IRIX6) */ +#include <sm/string.h> +#include <syslog.h> +#include <ctype.h> -/* - * If you don't have flock, you could try using lockf instead. - */ +#include <sm/conf.h> +#include <sendmail/pathnames.h> -# ifdef USE_LOCKF -# define flock(a, b) lockf(a, b, 0) -# ifdef LOCK_EX -# undef LOCK_EX -# endif /* LOCK_EX */ -# define LOCK_EX F_LOCK -# endif /* USE_LOCKF */ - -# ifndef LOCK_EX -# include <sys/file.h> -# endif /* ! LOCK_EX */ - -# if defined(BSD4_4) || defined(__GLIBC__) -# include <paths.h> -# define _PATH_LOCTMP "/tmp/local.XXXXXX" -# endif /* defined(BSD4_4) || defined(__GLIBC__) */ - -# ifdef BSD4_4 -# define HAS_ST_GEN 1 -# else /* BSD4_4 */ -# ifndef _BSD_VA_LIST_ -# define _BSD_VA_LIST_ va_list -# endif /* ! _BSD_VA_LIST_ */ -# endif /* BSD4_4 */ - -# if defined(BSD4_4) || defined(linux) -# define HASSNPRINTF 1 -# else /* defined(BSD4_4) || defined(linux) */ -# ifndef ultrix -extern FILE *fdopen __P((int, const char *)); -# endif /* ! ultrix */ -# endif /* defined(BSD4_4) || defined(linux) */ - -# if SOLARIS >= 20300 || (SOLARIS < 10000 && SOLARIS >= 203) -# define CONTENTLENGTH 1 /* Needs the Content-Length header */ -# endif /* SOLARIS >= 20300 || (SOLARIS < 10000 && SOLARIS >= 203) */ - -# if SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) -# define HASSNPRINTF 1 /* has snprintf starting in 2.6 */ -# endif /* SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) */ - -# ifdef HPUX11 -# define HASSNPRINTF 1 /* has snprintf starting in 11.X */ -# endif /* HPUX11 */ - -# if _AIX4 >= 40300 -# define HASSNPRINTF 1 /* has snprintf starting in 4.3 */ -# endif /* _AIX4 >= 40300 */ - -# if !HASSNPRINTF && !SFIO -extern int snprintf __P((char *, size_t, const char *, ...)); -# ifndef _CRAY -extern int vsnprintf __P((char *, size_t, const char *, ...)); -# endif /* ! _CRAY */ -# endif /* !HASSNPRINTF && !SFIO */ -/* -** If you don't have setreuid, and you have saved uids, and you have -** a seteuid() call that doesn't try to emulate using setuid(), then -** you can try defining USE_SETEUID. -*/ +/* additional mode for open() */ +# define EXTRA_MODE 0 + -# ifdef USE_SETEUID -# define setreuid(r, e) seteuid(e) -# endif /* USE_SETEUID */ +#ifndef LOCKTO_RM +# define LOCKTO_RM 300 /* timeout for stale lockfile removal */ +#endif /* ! LOCKTO_RM */ +#ifndef LOCKTO_GLOB +# define LOCKTO_GLOB 400 /* global timeout for lockfile creation */ +#endif /* ! LOCKTO_GLOB */ + +/* define a realloc() which works for NULL pointers */ +#define REALLOC(ptr, size) (((ptr) == NULL) ? malloc(size) : realloc(ptr, size)) /* -** And of course on hpux you have setresuid() +** If you don't have flock, you could try using lockf instead. */ -# ifdef USE_SETRESUID -# define setreuid(r, e) setresuid(-1, e, -1) -# endif /* USE_SETRESUID */ +#ifdef LDA_USE_LOCKF +# define flock(a, b) lockf(a, b, 0) +# ifdef LOCK_EX +# undef LOCK_EX +# endif /* LOCK_EX */ +# define LOCK_EX F_LOCK +#endif /* LDA_USE_LOCKF */ -# ifndef _PATH_LOCTMP -# define _PATH_LOCTMP "/tmp/local.XXXXXX" -# endif /* ! _PATH_LOCTMP */ -# ifndef _PATH_MAILDIR -# define _PATH_MAILDIR "/var/spool/mail" -# endif /* ! _PATH_MAILDIR */ +#ifndef LOCK_EX +# include <sys/file.h> +#endif /* ! LOCK_EX */ -# ifndef S_ISREG -# define S_ISREG(mode) (((mode) & _S_IFMT) == S_IFREG) -# endif /* ! S_ISREG */ +/* +** If you don't have setreuid, and you have saved uids, and you have +** a seteuid() call that doesn't try to emulate using setuid(), then +** you can try defining LDA_USE_SETEUID. +*/ -# ifdef MAILLOCK -# include <maillock.h> -# endif /* MAILLOCK */ +#ifdef LDA_USE_SETEUID +# define setreuid(r, e) seteuid(e) +#endif /* LDA_USE_SETEUID */ -# define U_UID pw->pw_uid -# define U_GID pw->pw_gid +#ifdef LDA_CONTENTLENGTH +# define CONTENTLENGTH 1 +#endif /* LDA_CONTENTLENGTH */ #ifndef INADDRSZ # define INADDRSZ 4 /* size of an IPv4 address in bytes */ #endif /* ! INADDRSZ */ +#ifdef MAILLOCK +# include <maillock.h> +#endif /* MAILLOCK */ + #ifndef MAILER_DAEMON # define MAILER_DAEMON "MAILER-DAEMON" #endif /* ! MAILER_DAEMON */ @@ -246,17 +124,18 @@ off_t HeaderLength; off_t BodyLength; #endif /* CONTENTLENGTH */ -bool EightBitMime = TRUE; /* advertise 8BITMIME in LMTP */ +bool EightBitMime = true; /* advertise 8BITMIME in LMTP */ char ErrBuf[10240]; /* error buffer */ int ExitVal = EX_OK; /* sysexits.h error value. */ -bool HoldErrs = FALSE; /* Hold errors in ErrBuf */ -bool LMTPMode = FALSE; -bool BounceQuota = FALSE; /* permanent error when over quota */ +bool HoldErrs = false; /* Hold errors in ErrBuf */ +bool LMTPMode = false; +bool BounceQuota = false; /* permanent error when over quota */ +char *HomeMailFile = NULL; /* store mail in homedir */ void deliver __P((int, char *)); int e_to_sys __P((int)); void notifybiff __P((char *)); -int store __P((char *, int, bool *)); +int store __P((char *, bool *)); void usage __P((void)); int lockmbox __P((char *)); void unlockmbox __P((void)); @@ -273,6 +152,8 @@ main(argc, argv) int ch, fd; uid_t uid; char *from; + char *mbdbname = "pw"; + int err; extern char *optarg; extern int optind; @@ -291,21 +172,25 @@ main(argc, argv) # endif /* LOG_MAIL */ from = NULL; - while ((ch = getopt(argc, argv, "7bdf:r:l")) != -1) + while ((ch = getopt(argc, argv, "7bdD:f:h:r:l")) != -1) { switch(ch) { case '7': /* Do not advertise 8BITMIME */ - EightBitMime = FALSE; + EightBitMime = false; break; case 'b': /* bounce mail when over quota. */ - BounceQuota = TRUE; + BounceQuota = true; break; case 'd': /* Backward compatible. */ break; + case 'D': /* mailbox database type */ + mbdbname = optarg; + break; + case 'f': case 'r': /* Backward compatible. */ if (from != NULL) @@ -316,8 +201,18 @@ main(argc, argv) from = optarg; break; + case 'h': + if (optarg != NULL || *optarg != '\0') + HomeMailFile = optarg; + else + { + mailerr(NULL, "-h: missing filename"); + usage(); + } + break; + case 'l': - LMTPMode = TRUE; + LMTPMode = true; break; case '?': @@ -331,6 +226,19 @@ main(argc, argv) /* initialize biff structures */ notifybiff(NULL); + err = sm_mbdb_initialize(mbdbname); + if (err != EX_OK) + { + char *errcode = "521"; + + if (err == EX_TEMPFAIL) + errcode = "421"; + + mailerr(errcode, "Can not open mailbox database %s: %s", + mbdbname, sm_strexit(err)); + exit(err); + } + if (LMTPMode) { extern void dolmtp __P((void)); @@ -357,7 +265,6 @@ main(argc, argv) */ uid = getuid(); - if (from == NULL && ((from = getlogin()) == NULL || (pw = getpwnam(from)) == NULL || pw->pw_uid != uid)) @@ -373,9 +280,9 @@ main(argc, argv) ** at the expense of repeated failures and multiple deliveries. */ - HoldErrs = TRUE; - fd = store(from, 0, NULL); - HoldErrs = FALSE; + HoldErrs = true; + fd = store(from, NULL); + HoldErrs = false; if (fd < 0) { flush_error(); @@ -467,6 +374,8 @@ parseaddr(s, rcpt) s = MAILER_DAEMON; l = strlen(s) + 1; + if (l < 0) + return NULL; p = malloc(l); if (p == NULL) { @@ -474,7 +383,7 @@ parseaddr(s, rcpt) exit(EX_TEMPFAIL); } - (void) strlcpy(p, s, l); + (void) sm_strlcpy(p, s, l); return p; } @@ -482,9 +391,22 @@ char * process_recipient(addr) char *addr; { - if (getpwnam(addr) == NULL) + SM_MBDB_T user; + + switch (sm_mbdb_lookup(addr, &user)) + { + case EX_OK: + return NULL; + + case EX_NOUSER: return "550 5.1.1 User unknown"; - return NULL; + + case EX_TEMPFAIL: + return "451 4.3.0 User database failure; retry later"; + + default: + return "550 5.3.0 User database failure"; + } } #define RCPT_GROW 30 @@ -496,7 +418,7 @@ dolmtp() char **rcpt_addr = NULL; int rcpt_num = 0; int rcpt_alloc = 0; - bool gotlhlo = FALSE; + bool gotlhlo = false; char *err; int msgfd; char *p; @@ -507,7 +429,7 @@ dolmtp() memset(myhostname, '\0', sizeof myhostname); (void) gethostname(myhostname, sizeof myhostname - 1); if (myhostname[0] == '\0') - strlcpy(myhostname, "localhost", sizeof myhostname); + sm_strlcpy(myhostname, "localhost", sizeof myhostname); printf("220 %s LMTP ready\r\n", myhostname); for (;;) @@ -525,18 +447,18 @@ dolmtp() { case 'd': case 'D': - if (strcasecmp(buf, "data") == 0) + if (sm_strcasecmp(buf, "data") == 0) { - bool inbody = FALSE; + bool inbody = false; if (rcpt_num == 0) { mailerr("503 5.5.1", "No recipients"); continue; } - HoldErrs = TRUE; - msgfd = store(return_path, rcpt_num, &inbody); - HoldErrs = FALSE; + HoldErrs = true; + msgfd = store(return_path, &inbody); + HoldErrs = false; if (msgfd < 0 && !inbody) { flush_error(); @@ -566,7 +488,7 @@ dolmtp() case 'l': case 'L': - if (strncasecmp(buf, "lhlo ", 5) == 0) + if (sm_strncasecmp(buf, "lhlo ", 5) == 0) { /* check for duplicate per RFC 1651 4.2 */ if (gotlhlo) @@ -575,7 +497,7 @@ dolmtp() myhostname); continue; } - gotlhlo = TRUE; + gotlhlo = true; printf("250-%s\r\n", myhostname); if (EightBitMime) printf("250-8BITMIME\r\n"); @@ -589,7 +511,7 @@ dolmtp() case 'm': case 'M': - if (strncasecmp(buf, "mail ", 5) == 0) + if (sm_strncasecmp(buf, "mail ", 5) == 0) { if (return_path != NULL) { @@ -597,9 +519,9 @@ dolmtp() "Nested MAIL command"); continue; } - if (strncasecmp(buf+5, "from:", 5) != 0 || + if (sm_strncasecmp(buf+5, "from:", 5) != 0 || ((return_path = parseaddr(buf + 10, - FALSE)) == NULL)) + false)) == NULL)) { mailerr("501 5.5.4", "Syntax error in parameters"); @@ -614,7 +536,7 @@ dolmtp() case 'n': case 'N': - if (strcasecmp(buf, "noop") == 0) + if (sm_strcasecmp(buf, "noop") == 0) { printf("250 2.0.0 Ok\r\n"); continue; @@ -625,7 +547,7 @@ dolmtp() case 'q': case 'Q': - if (strcasecmp(buf, "quit") == 0) + if (sm_strcasecmp(buf, "quit") == 0) { printf("221 2.0.0 Bye\r\n"); exit(EX_OK); @@ -636,7 +558,7 @@ dolmtp() case 'r': case 'R': - if (strncasecmp(buf, "rcpt ", 5) == 0) + if (sm_strncasecmp(buf, "rcpt ", 5) == 0) { if (return_path == NULL) { @@ -658,9 +580,9 @@ dolmtp() exit(EX_TEMPFAIL); } } - if (strncasecmp(buf + 5, "to:", 3) != 0 || + if (sm_strncasecmp(buf + 5, "to:", 3) != 0 || ((rcpt_addr[rcpt_num] = parseaddr(buf + 8, - TRUE)) == NULL)) + true)) == NULL)) { mailerr("501 5.5.4", "Syntax error in parameters"); @@ -676,7 +598,7 @@ dolmtp() printf("250 2.1.5 Ok\r\n"); continue; } - else if (strcasecmp(buf, "rset") == 0) + else if (sm_strcasecmp(buf, "rset") == 0) { printf("250 2.0.0 Ok\r\n"); @@ -694,7 +616,7 @@ rset: case 'v': case 'V': - if (strncasecmp(buf, "vrfy ", 5) == 0) + if (sm_strncasecmp(buf, "vrfy ", 5) == 0) { printf("252 2.3.3 Try RCPT to attempt delivery\r\n"); continue; @@ -714,25 +636,24 @@ rset: } int -store(from, lmtprcpts, inbody) +store(from, inbody) char *from; - int lmtprcpts; bool *inbody; { FILE *fp = NULL; time_t tval; - bool eline; - bool fullline = TRUE; /* current line is terminated */ + bool eline; /* previous line was empty */ + bool fullline = true; /* current line is terminated */ bool prevfl; /* previous line was terminated */ char line[2048]; int fd; char tmpbuf[sizeof _PATH_LOCTMP + 1]; if (inbody != NULL) - *inbody = FALSE; + *inbody = false; (void) umask(0077); - (void) strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf); + (void) sm_strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf); if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL) { mailerr("451 4.3.0", "Unable to open temporary file"); @@ -746,7 +667,7 @@ store(from, lmtprcpts, inbody) (void) fflush(stdout); } if (inbody != NULL) - *inbody = TRUE; + *inbody = true; (void) time(&tval); (void) fprintf(fp, "From %s %s", from, ctime(&tval)); @@ -757,7 +678,7 @@ store(from, lmtprcpts, inbody) #endif /* CONTENTLENGTH */ line[0] = '\0'; - eline = TRUE; + eline = true; while (fgets(line, sizeof(line), stdin) != (char *) NULL) { size_t line_len = 0; @@ -779,7 +700,7 @@ store(from, lmtprcpts, inbody) } /* Check to see if we have the full line from fgets() */ - fullline = FALSE; + fullline = false; if (line_len > 0) { if (line[line_len - 1] == '\n') @@ -791,7 +712,7 @@ store(from, lmtprcpts, inbody) line[line_len - 1] = '\0'; line_len--; } - fullline = TRUE; + fullline = true; } else if (line[line_len - 1] == '\r') { @@ -800,19 +721,19 @@ store(from, lmtprcpts, inbody) if (peek == '\n') { line[line_len - 1] = '\n'; - fullline = TRUE; + fullline = true; } else (void) ungetc(peek, stdin); } } else - fullline = TRUE; + fullline = true; #ifdef CONTENTLENGTH if (prevfl && line[0] == '\n' && HeaderLength == 0) { - eline = FALSE; + eline = false; if (fp != NULL) HeaderLength = ftell(fp); if (HeaderLength <= 0) @@ -827,7 +748,7 @@ store(from, lmtprcpts, inbody) } #else /* CONTENTLENGTH */ if (prevfl && line[0] == '\n') - eline = TRUE; + eline = true; #endif /* CONTENTLENGTH */ else { @@ -835,12 +756,12 @@ store(from, lmtprcpts, inbody) fp != NULL && !memcmp(line, "From ", 5)) (void) putc('>', fp); - eline = FALSE; + eline = false; #ifdef CONTENTLENGTH /* discard existing "Content-Length:" headers */ if (prevfl && HeaderLength == 0 && (line[0] == 'C' || line[0] == 'c') && - strncasecmp(line, ContentHdr, 15) == 0) + sm_strncasecmp(line, ContentHdr, 15) == 0) { /* ** be paranoid: clear the line @@ -895,15 +816,10 @@ store(from, lmtprcpts, inbody) if (HeaderLength > 0 && BodyLength >= 0) { - extern char *quad_to_string(); - - if (sizeof BodyLength > sizeof(long)) - snprintf(line, sizeof line, "%s\n", - quad_to_string(BodyLength)); - else - snprintf(line, sizeof line, "%ld\n", - (long) BodyLength); - strlcpy(&ContentHdr[16], line, sizeof(ContentHdr) - 16); + (void) sm_snprintf(line, sizeof line, "%lld\n", + (LONGLONG_T) BodyLength); + (void) sm_strlcpy(&ContentHdr[16], line, + sizeof(ContentHdr) - 16); } else BodyLength = -1; /* Something is wrong here */ @@ -930,9 +846,9 @@ deliver(fd, name) { struct stat fsb; struct stat sb; - struct passwd *pw; char path[MAXPATHLEN]; int mbfd = -1, nr = 0, nw, off; + int exitval; char *p; char *errcode; off_t curoff; @@ -941,26 +857,42 @@ deliver(fd, name) int readamount; #endif /* CONTENTLENGTH */ char biffmsg[100], buf[8*1024]; - extern char *quad_to_string(); - + SM_MBDB_T user; /* ** Disallow delivery to unknown names -- special mailboxes can be ** handled in the sendmail aliases file. */ - if ((pw = getpwnam(name)) == NULL) + exitval = sm_mbdb_lookup(name, &user); + switch (exitval) { - if (ExitVal == EX_TEMPFAIL) - errcode = "451 4.3.0"; - else - { - ExitVal = EX_UNAVAILABLE; - errcode = "550 5.1.1"; - } - mailerr(errcode, "Unknown name: %s", name); + case EX_OK: + break; + + case EX_NOUSER: + exitval = EX_UNAVAILABLE; + mailerr("550 5.1.1", "%s: User unknown", name); + break; + + case EX_TEMPFAIL: + mailerr("451 4.3.0", "%s: User database failure; retry later", + name); + break; + + default: + exitval = EX_UNAVAILABLE; + mailerr("550 5.3.0", "%s: User database failure", name); + break; + } + + if (exitval != EX_OK) + { + if (ExitVal != EX_TEMPFAIL) + ExitVal = exitval; return; } + endpwent(); /* @@ -982,7 +914,29 @@ deliver(fd, name) } - (void) snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name); + if (HomeMailFile == NULL) + { + if (sm_snprintf(path, sizeof(path), "%s/%s", + _PATH_MAILDIR, name) >= sizeof(path)) + { + exitval = EX_UNAVAILABLE; + mailerr("550 5.1.1", "%s: Invalid mailbox path", name); + return; + } + } + else if (*user.mbdb_homedir == '\0') + { + exitval = EX_UNAVAILABLE; + mailerr("550 5.1.1", "%s: User missing home directory", name); + return; + } + else if (sm_snprintf(path, sizeof(path), "%s/%s", + user.mbdb_homedir, HomeMailFile) >= sizeof(path)) + { + exitval = EX_UNAVAILABLE; + mailerr("550 5.1.1", "%s: Invalid mailbox path", name); + return; + } /* @@ -1024,7 +978,7 @@ tryagain: errcode = "551 5.3.0"; mailerr(errcode, "lockmailbox %s failed; error code %d %s", - p, off, errno > 0 ? errstring(errno) : ""); + p, off, errno > 0 ? sm_errstring(errno) : ""); return; } @@ -1032,7 +986,7 @@ tryagain: { int save_errno; int mode = S_IRUSR|S_IWUSR; - gid_t gid = U_GID; + gid_t gid = user.mbdb_gid; #ifdef MAILGID (void) umask(0007); @@ -1058,13 +1012,13 @@ tryagain: /* open failed, don't try again */ mailerr("450 4.2.0", "%s: %s", path, - errstring(save_errno)); + sm_errstring(save_errno)); goto err0; } - else if (fchown(mbfd, U_UID, gid) < 0) + else if (fchown(mbfd, user.mbdb_uid, gid) < 0) { mailerr("451 4.3.0", "chown %u.%u: %s", - U_UID, gid, name); + user.mbdb_uid, gid, name); goto err1; } else @@ -1076,7 +1030,7 @@ tryagain: ** is no longer valid; better safe than sorry. */ - sb.st_uid = U_UID; + sb.st_uid = user.mbdb_uid; (void) close(mbfd); mbfd = -1; } @@ -1086,28 +1040,29 @@ tryagain: mailerr("550 5.2.0", "%s: irregular file", path); goto err0; } - else if (sb.st_uid != U_UID) + else if (sb.st_uid != user.mbdb_uid) { ExitVal = EX_CANTCREAT; mailerr("550 5.2.0", "%s: wrong ownership (%d)", - path, sb.st_uid); + path, (int) sb.st_uid); goto err0; } /* change UID for quota checks */ - if (setreuid(0, U_UID) < 0) + if (setreuid(0, user.mbdb_uid) < 0) { mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)", - U_UID, errstring(errno), getuid(), geteuid()); + (int) user.mbdb_uid, sm_errstring(errno), + (int) getuid(), (int) geteuid()); goto err1; } #ifdef DEBUG - fprintf(stderr, "new euid = %d\n", geteuid()); + fprintf(stderr, "new euid = %d\n", (int) geteuid()); #endif /* DEBUG */ mbfd = open(path, O_APPEND|O_WRONLY|EXTRA_MODE, 0); if (mbfd < 0) { - mailerr("450 4.2.0", "%s: %s", path, errstring(errno)); + mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); goto err0; } else if (fstat(mbfd, &fsb) < 0 || @@ -1127,32 +1082,65 @@ tryagain: goto err1; } +#if 0 + /* + ** This code could be reused if we decide to add a + ** per-user quota field to the sm_mbdb interface. + */ + + /* + ** Fail if the user has a quota specified, and delivery of this + ** message would exceed that quota. We bounce such failures using + ** EX_UNAVAILABLE, unless there were internal problems, since + ** storing immense messages for later retries can cause queueing + ** issues. + */ + + if (ui.quota > 0) + { + struct stat dsb; + + if (fstat(fd, &dsb) < 0) + { + ExitVal = EX_TEMPFAIL; + mailerr("451 4.3.0", + "%s: fstat: can't stat temporary storage: %s", + ui.mailspool, sm_errstring(errno)); + goto err1; + } + + if (dsb.st_size + sb.st_size + 1 > ui.quota) + { + ExitVal = EX_UNAVAILABLE; + mailerr("551 5.2.2", + "%s: Mailbox full or quota exceeded", + ui.mailspool); + goto err1; + } + } +#endif /* 0 */ /* Wait until we can get a lock on the file. */ if (flock(mbfd, LOCK_EX) < 0) { - mailerr("450 4.2.0", "%s: %s", path, errstring(errno)); + mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); goto err1; } /* Get the starting offset of the new message for biff. */ curoff = lseek(mbfd, (off_t) 0, SEEK_END); - if (sizeof curoff > sizeof(long)) - (void) snprintf(biffmsg, sizeof(biffmsg), "%s@%s\n", - name, quad_to_string(curoff)); - else - (void) snprintf(biffmsg, sizeof(biffmsg), "%s@%ld\n", - name, (long) curoff); + (void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n", + name, (LONGLONG_T) curoff); /* Copy the message into the file. */ if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1) { mailerr("450 4.2.0", "Temporary file: %s", - errstring(errno)); + sm_errstring(errno)); goto err1; } #ifdef DEBUG - fprintf(stderr, "before writing: euid = %d\n", geteuid()); + fprintf(stderr, "before writing: euid = %d\n", (int) geteuid()); #endif /* DEBUG */ #ifdef CONTENTLENGTH headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ; @@ -1160,7 +1148,7 @@ tryagain: { if (headerbytes == 0) { - snprintf(buf, sizeof buf, "%s", ContentHdr); + (void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr); nr = strlen(buf); headerbytes = -1; readamount = 0; @@ -1190,7 +1178,7 @@ tryagain: errcode = "552 5.2.2"; #endif /* EDQUOT */ mailerr(errcode, "%s: %s", - path, errstring(errno)); + path, sm_errstring(errno)); goto err3; } } @@ -1198,18 +1186,18 @@ tryagain: if (nr < 0) { mailerr("450 4.2.0", "Temporary file: %s", - errstring(errno)); + sm_errstring(errno)); goto err3; } /* Flush to disk, don't wait for update. */ if (fsync(mbfd) < 0) { - mailerr("450 4.2.0", "%s: %s", path, errstring(errno)); + mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); err3: (void) setreuid(0, 0); #ifdef DEBUG - fprintf(stderr, "reset euid = %d\n", geteuid()); + fprintf(stderr, "reset euid = %d\n", (int) geteuid()); #endif /* DEBUG */ (void) ftruncate(mbfd, curoff); err1: if (mbfd >= 0) @@ -1226,7 +1214,7 @@ err0: unlockmbox(); if (errno == EDQUOT && BounceQuota) errcode = "552 5.2.2"; #endif /* EDQUOT */ - mailerr(errcode, "%s: %s", path, errstring(errno)); + mailerr(errcode, "%s: %s", path, sm_errstring(errno)); (void) truncate(path, curoff); } else @@ -1235,11 +1223,11 @@ err0: unlockmbox(); if (setreuid(0, 0) < 0) { mailerr("450 4.2.0", "setreuid(0, 0): %s", - errstring(errno)); + sm_errstring(errno)); goto err0; } #ifdef DEBUG - fprintf(stderr, "reset euid = %d\n", geteuid()); + fprintf(stderr, "reset euid = %d\n", (int) geteuid()); #endif /* DEBUG */ unlockmbox(); if (LMTPMode) @@ -1253,7 +1241,7 @@ err0: unlockmbox(); ** EPA 11/94. */ -bool Locked = FALSE; +bool Locked = false; #ifdef MAILLOCK int @@ -1266,7 +1254,7 @@ lockmbox(name) return 0; if ((r = maillock(name, 15)) == L_SUCCESS) { - Locked = TRUE; + Locked = true; return 0; } switch (r) @@ -1293,7 +1281,7 @@ unlockmbox() { if (Locked) mailunlock(); - Locked = FALSE; + Locked = false; } #else /* MAILLOCK */ @@ -1310,7 +1298,7 @@ lockmbox(path) return 0; if (strlen(path) + 6 > sizeof LockName) return EX_SOFTWARE; - (void) snprintf(LockName, sizeof LockName, "%s.lock", path); + (void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path); (void) time(&start); for (; ; sleep(5)) { @@ -1330,7 +1318,7 @@ lockmbox(path) { /* defeat lock checking programs which test pid */ (void) write(fd, "0", 2); - Locked = TRUE; + Locked = true; (void) close(fd); return 0; } @@ -1360,7 +1348,7 @@ unlockmbox() if (!Locked) return; (void) unlink(LockName); - Locked = FALSE; + Locked = false; } #endif /* MAILLOCK */ @@ -1368,7 +1356,7 @@ void notifybiff(msg) char *msg; { - static bool initialized = FALSE; + static bool initialized = false; static int f = -1; struct hostent *hp; struct servent *sp; @@ -1377,7 +1365,7 @@ notifybiff(msg) if (!initialized) { - initialized = TRUE; + initialized = true; /* Be silent if biff service not available. */ if ((sp = getservbyname("biff", "udp")) == NULL || @@ -1408,11 +1396,12 @@ void usage() { ExitVal = EX_USAGE; - mailerr(NULL, "usage: mail.local [-7] [-b] [-l] [-f from] user ..."); + mailerr(NULL, "usage: mail.local [-7] [-b] [-d] [-l] [-f from|-r from] [-h filename] user ..."); exit(ExitVal); } void +/*VARARGS2*/ #ifdef __STDC__ mailerr(const char *hdr, const char *fmt, ...) #else /* __STDC__ */ @@ -1423,26 +1412,19 @@ mailerr(hdr, fmt, va_alist) #endif /* __STDC__ */ { size_t len = 0; - va_list ap; + SM_VA_LOCAL_DECL (void) e_to_sys(errno); -#ifdef __STDC__ - va_start(ap, fmt); -#else /* __STDC__ */ - va_start(ap); -#endif /* __STDC__ */ + SM_VA_START(ap, fmt); - if (LMTPMode) + if (LMTPMode && hdr != NULL) { - if (hdr != NULL) - { - snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr); - len = strlen(ErrBuf); - } + sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr); + len = strlen(ErrBuf); } - (void) vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap); - va_end(ap); + (void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap); + SM_VA_END(ap); if (!HoldErrs) flush_error(); @@ -1631,7 +1613,7 @@ _gettemp(path, doopen) extern int errno; register char *start, *trv; struct stat sbuf; - u_int pid; + unsigned int pid; pid = getpid(); for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ diff --git a/gnu/usr.sbin/sendmail/mailstats/Makefile b/gnu/usr.sbin/sendmail/mailstats/Makefile index e4de6a2d3b4..92a2344d9e2 100644 --- a/gnu/usr.sbin/sendmail/mailstats/Makefile +++ b/gnu/usr.sbin/sendmail/mailstats/Makefile @@ -1,9 +1,10 @@ -# $OpenBSD: Makefile,v 1.3 2000/05/15 03:37:32 millert Exp $ +# $OpenBSD: Makefile,v 1.4 2001/09/11 19:02:49 millert Exp $ PROG= mailstats MAN= mailstats.8 ENVDEF= -DNOT_SENDMAIL +WANT_LIBSM=1 WANT_LIBSMUTIL=1 .include "../../Makefile.inc" diff --git a/gnu/usr.sbin/sendmail/mailstats/mailstats.8 b/gnu/usr.sbin/sendmail/mailstats/mailstats.8 index 17778c01186..313f97cfa5f 100644 --- a/gnu/usr.sbin/sendmail/mailstats/mailstats.8 +++ b/gnu/usr.sbin/sendmail/mailstats/mailstats.8 @@ -6,9 +6,9 @@ .\" the sendmail distribution. .\" .\" -.\" $Sendmail: mailstats.8,v 8.17.4.6 2001/05/07 22:06:38 gshapiro Exp $ +.\" $Sendmail: mailstats.8,v 8.25 2001/04/18 04:53:31 gshapiro Exp $ .\" -.Dd May 7, 2001 +.Dd April 18, 2001 .Dt MAILSTATS 1 .Os .Sh NAME @@ -18,6 +18,7 @@ .Nm mailstats .Op Fl o .Op Fl p +.Op Fl P .Op Fl C Ar cffile .Op Fl f Ar stfile .Sh DESCRIPTION @@ -74,6 +75,8 @@ specified in the configuration file. .It Fl p Output information in program-readable mode and clear statistics. +.It Fl P +Output information in program-readable mode without clearing statistics. .It Fl o Don't display the name of the mailer in the output. .El diff --git a/gnu/usr.sbin/sendmail/mailstats/mailstats.c b/gnu/usr.sbin/sendmail/mailstats/mailstats.c index 69e885b6fc1..ba5749edea2 100644 --- a/gnu/usr.sbin/sendmail/mailstats/mailstats.c +++ b/gnu/usr.sbin/sendmail/mailstats/mailstats.c @@ -12,17 +12,15 @@ * */ -#ifndef lint -static char copyright[] = +#include <sm/gen.h> + +SM_IDSTR(copyright, "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ All rights reserved.\n\ Copyright (c) 1988, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* ! lint */ + The Regents of the University of California. All rights reserved.\n") -#ifndef lint -static char id[] = "@(#)$Sendmail: mailstats.c,v 8.53.16.13 2001/05/07 22:06:38 gshapiro Exp $"; -#endif /* ! lint */ +SM_IDSTR(id, "@(#)$Sendmail: mailstats.c,v 8.89 2001/09/04 22:44:22 ca Exp $") #include <unistd.h> #include <stddef.h> @@ -35,6 +33,7 @@ static char id[] = "@(#)$Sendmail: mailstats.c,v 8.53.16.13 2001/05/07 22:06:38 #endif /* EX_OK */ #include <sysexits.h> +#include <sm/errstring.h> #include <sendmail/sendmail.h> #include <sendmail/mailstats.h> #include <sendmail/pathnames.h> @@ -42,7 +41,6 @@ static char id[] = "@(#)$Sendmail: mailstats.c,v 8.53.16.13 2001/05/07 22:06:38 #define MNAMELEN 20 /* max length of mailer name */ - int main(argc, argv) int argc; @@ -54,9 +52,10 @@ main(argc, argv) int ch, fd; char *sfile; char *cfile; - FILE *cfp; + SM_FILE_T *cfp; bool mnames; bool progmode; + bool trunc; long frmsgs = 0, frbytes = 0, tomsgs = 0, tobytes = 0, rejmsgs = 0; long dismsgs = 0; time_t now; @@ -68,15 +67,19 @@ main(argc, argv) extern char *optarg; extern int optind; - - cfile = _PATH_SENDMAILCF; + cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL); sfile = NULL; - mnames = TRUE; - progmode = FALSE; - while ((ch = getopt(argc, argv, "C:f:op")) != -1) + mnames = true; + progmode = false; + trunc = false; + while ((ch = getopt(argc, argv, "cC:f:opP")) != -1) { switch (ch) { + case 'c': + cfile = getcfname(0, 0, SM_GET_SUBMIT_CF, NULL); + break; + case 'C': cfile = optarg; break; @@ -86,18 +89,22 @@ main(argc, argv) break; case 'o': - mnames = FALSE; + mnames = false; break; case 'p': - progmode = TRUE; + trunc = true; + /* FALLTHROUGH */ + + case 'P': + progmode = true; break; case '?': default: usage: - (void) fputs("usage: mailstats [-C cffile] [-f stfile] [-o] [-p]\n", - stderr); + (void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, + "usage: mailstats [-C cffile] [-P] [-f stfile] [-o] [-p]\n"); exit(EX_USAGE); } } @@ -107,21 +114,22 @@ main(argc, argv) if (argc != 0) goto usage; - if ((cfp = fopen(cfile, "r")) == NULL) + if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile, SM_IO_RDONLY, + NULL)) == NULL) { save_errno = errno; - fprintf(stderr, "mailstats: "); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "mailstats: "); errno = save_errno; - perror(cfile); + sm_perror(cfile); exit(EX_NOINPUT); } mno = 0; - (void) strlcpy(mtable[mno++], "prog", MNAMELEN + 1); - (void) strlcpy(mtable[mno++], "*file*", MNAMELEN + 1); - (void) strlcpy(mtable[mno++], "*include*", MNAMELEN + 1); + (void) sm_strlcpy(mtable[mno++], "prog", MNAMELEN + 1); + (void) sm_strlcpy(mtable[mno++], "*file*", MNAMELEN + 1); + (void) sm_strlcpy(mtable[mno++], "*include*", MNAMELEN + 1); - while (fgets(buf, sizeof(buf), cfp) != NULL) + while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL) { register char *b; char *s; @@ -134,7 +142,7 @@ main(argc, argv) break; case 'O': /* option -- see if .st file */ - if (strncasecmp(b, " StatusFile", 11) == 0 && + if (sm_strncasecmp(b, " StatusFile", 11) == 0 && !(isascii(b[11]) && isalnum(b[11]))) { /* new form -- find value */ @@ -151,12 +159,12 @@ main(argc, argv) } /* this is the S or StatusFile option -- save it */ - if (strlcpy(sfilebuf, b, sizeof sfilebuf) >= + if (sm_strlcpy(sfilebuf, b, sizeof sfilebuf) >= sizeof sfilebuf) { - fprintf(stderr, - "StatusFile filename too long: %.30s...\n", - b); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "StatusFile filename too long: %.30s...\n", + b); exit(EX_CONFIG); } b = strchr(sfilebuf, '#'); @@ -176,9 +184,9 @@ main(argc, argv) if (mno >= MAXMAILERS) { - fprintf(stderr, - "Too many mailers defined, %d max.\n", - MAXMAILERS); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "Too many mailers defined, %d max.\n", + MAXMAILERS); exit(EX_SOFTWARE); } m = mtable[mno]; @@ -195,13 +203,14 @@ main(argc, argv) if (i == mno) mno++; } - (void) fclose(cfp); + (void) sm_io_close(cfp, SM_TIME_DEFAULT); for (; mno < MAXMAILERS; mno++) - mtable[mno][0]='\0'; + mtable[mno][0] = '\0'; if (sfile == NULL) { - fprintf(stderr, "mailstats: no statistics file located\n"); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "mailstats: no statistics file located\n"); exit (EX_OSFILE); } @@ -209,9 +218,9 @@ main(argc, argv) if ((fd < 0) || (i = read(fd, &stats, sizeof stats)) < 0) { save_errno = errno; - (void) fputs("mailstats: ", stderr); + (void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, "mailstats: "); errno = save_errno; - perror(sfile); + sm_perror(sfile); exit(EX_NOINPUT); } if (i == 0) @@ -220,9 +229,10 @@ main(argc, argv) if ((i = read(fd, &stats, sizeof stats)) < 0) { save_errno = errno; - (void) fputs("mailstats: ", stderr); + (void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, + "mailstats: "); errno = save_errno; - perror(sfile); + sm_perror(sfile); exit(EX_NOINPUT); } else if (i == 0) @@ -235,21 +245,24 @@ main(argc, argv) { if (stats.stat_magic != STAT_MAGIC) { - fprintf(stderr, - "mailstats: incorrect magic number in %s\n", - sfile); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "mailstats: incorrect magic number in %s\n", + sfile); exit(EX_OSERR); } else if (stats.stat_version != STAT_VERSION) { - fprintf(stderr, - "mailstats version (%d) incompatible with %s version (%d)\n", - STAT_VERSION, sfile, stats.stat_version); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "mailstats version (%d) incompatible with %s version (%d)\n", + STAT_VERSION, sfile, + stats.stat_version); + exit(EX_OSERR); } else if (i != sizeof stats || stats.stat_size != sizeof(stats)) { - (void) fputs("mailstats: file size changed.\n", stderr); + (void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, + "mailstats: file size changed.\n"); exit(EX_OSERR); } } @@ -257,13 +270,17 @@ main(argc, argv) if (progmode) { (void) time(&now); - printf("%ld %ld\n", (long) stats.stat_itime, (long) now); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%ld %ld\n", + (long) stats.stat_itime, (long) now); } else { - printf("Statistics from %s", ctime(&stats.stat_itime)); - printf(" M msgsfr bytes_from msgsto bytes_to msgsrej msgsdis%s\n", - mnames ? " Mailer" : ""); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Statistics from %s", + ctime(&stats.stat_itime)); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + " M msgsfr bytes_from msgsto bytes_to msgsrej msgsdis%s\n", + mnames ? " Mailer" : ""); } for (i = 0; i < MAXMAILERS; i++) { @@ -276,13 +293,19 @@ main(argc, argv) format = "%2d %8ld %10ld %8ld %10ld %6ld %6ld"; else format = "%2d %8ld %10ldK %8ld %10ldK %6ld %6ld"; - printf(format, i, - stats.stat_nf[i], stats.stat_bf[i], - stats.stat_nt[i], stats.stat_bt[i], - stats.stat_nr[i], stats.stat_nd[i]); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + format, i, + stats.stat_nf[i], + stats.stat_bf[i], + stats.stat_nt[i], + stats.stat_bt[i], + stats.stat_nr[i], + stats.stat_nd[i]); if (mnames) - printf(" %s", mtable[i]); - printf("\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + " %s", + mtable[i]); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); frmsgs += stats.stat_nf[i]; frbytes += stats.stat_bf[i]; tomsgs += stats.stat_nt[i]; @@ -293,22 +316,34 @@ main(argc, argv) } if (progmode) { - printf(" T %8ld %10ld %8ld %10ld %6ld %6ld\n", - frmsgs, frbytes, tomsgs, tobytes, rejmsgs, dismsgs); - printf(" C %8ld %8ld %6ld\n", - stats.stat_cf, stats.stat_ct, stats.stat_cr); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + " T %8ld %10ld %8ld %10ld %6ld %6ld\n", + frmsgs, frbytes, tomsgs, tobytes, rejmsgs, + dismsgs); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + " C %8ld %8ld %6ld\n", + stats.stat_cf, stats.stat_ct, + stats.stat_cr); (void) close(fd); - fd = open(sfile, O_RDWR | O_TRUNC); - if (fd >= 0) - (void) close(fd); + if (trunc) + { + fd = open(sfile, O_RDWR | O_TRUNC); + if (fd >= 0) + (void) close(fd); + } } else { - printf("=============================================================\n"); - printf(" T %8ld %10ldK %8ld %10ldK %6ld %6ld\n", - frmsgs, frbytes, tomsgs, tobytes, rejmsgs, dismsgs); - printf(" C %8ld %10s %8ld %10s %6ld\n", - stats.stat_cf, "", stats.stat_ct, "", stats.stat_cr); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "=============================================================\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + " T %8ld %10ldK %8ld %10ldK %6ld %6ld\n", + frmsgs, frbytes, tomsgs, tobytes, rejmsgs, + dismsgs); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + " C %8ld %10s %8ld %10s %6ld\n", + stats.stat_cf, "", stats.stat_ct, "", + stats.stat_cr); } exit(EX_OK); /* NOTREACHED */ diff --git a/gnu/usr.sbin/sendmail/makemap/Makefile b/gnu/usr.sbin/sendmail/makemap/Makefile index cf2aa0999ec..54323ca86fb 100644 --- a/gnu/usr.sbin/sendmail/makemap/Makefile +++ b/gnu/usr.sbin/sendmail/makemap/Makefile @@ -1,9 +1,10 @@ -# $OpenBSD: Makefile,v 1.3 2000/05/15 03:37:32 millert Exp $ +# $OpenBSD: Makefile,v 1.4 2001/09/11 19:02:49 millert Exp $ PROG= makemap MAN= makemap.8 ENVDEF= -DNOT_SENDMAIL +WANT_LIBSM=1 WANT_LIBSMDB=1 WANT_LIBSMUTIL=1 diff --git a/gnu/usr.sbin/sendmail/makemap/makemap.8 b/gnu/usr.sbin/sendmail/makemap/makemap.8 index ef95c574a58..3e49bc4ca09 100644 --- a/gnu/usr.sbin/sendmail/makemap/makemap.8 +++ b/gnu/usr.sbin/sendmail/makemap/makemap.8 @@ -8,7 +8,7 @@ .\" the sendmail distribution. .\" .\" -.\" $Sendmail: makemap.8,v 8.21.16.5 2000/12/29 18:12:20 gshapiro Exp $ +.\" $Sendmail: makemap.8,v 8.27 2000/12/29 18:16:55 gshapiro Exp $ .\" .Dd December 29, 2000 .Dt MAKEMAP 8 @@ -28,6 +28,7 @@ .Op Fl o .Op Fl r .Op Fl s +.Op Fl t Ar delim .Op Fl u .Op Fl v .Ar maptype @@ -129,6 +130,8 @@ and does not do the insert. Ignore safety checks on maps being created. This includes checking for hard or symbolic links in world writable directories. +.It Fl t +Use the specified delimiter instead of white space. .It Fl u dump (unmap) the content of the database to standard output. .It Fl v diff --git a/gnu/usr.sbin/sendmail/makemap/makemap.c b/gnu/usr.sbin/sendmail/makemap/makemap.c index 9996e328887..f8c7bc28404 100644 --- a/gnu/usr.sbin/sendmail/makemap/makemap.c +++ b/gnu/usr.sbin/sendmail/makemap/makemap.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. + * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1992 Eric P. Allman. All rights reserved. * Copyright (c) 1992, 1993 @@ -11,18 +11,16 @@ * */ -#ifndef lint -static char copyright[] = -"@(#) Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.\n\ +#include <sm/gen.h> + +SM_IDSTR(copyright, +"@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ All rights reserved.\n\ Copyright (c) 1992 Eric P. Allman. All rights reserved.\n\ Copyright (c) 1992, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* ! lint */ + The Regents of the University of California. All rights reserved.\n") -#ifndef lint -static char id[] = "@(#)$Sendmail: makemap.c,v 8.135.4.13 2000/10/05 23:00:50 gshapiro Exp $"; -#endif /* ! lint */ +SM_IDSTR(id, "@(#)$Sendmail: makemap.c,v 8.170 2001/08/28 23:07:04 gshapiro Exp $") #include <sys/types.h> @@ -47,31 +45,20 @@ uid_t RunAsUid; uid_t RunAsGid; char *RunAsUserName; int Verbose = 2; -bool DontInitGroups = FALSE; +bool DontInitGroups = false; uid_t TrustedUid = 0; BITMAP256 DontBlameSendmail; #define BUFSIZE 1024 -#if _FFR_DELIM -# define ISSEP(c) ((sep == '\0' && isascii(c) && isspace(c)) || (c) == sep) -#else /* _FFR_DELIM */ -# define ISSEP(c) (isascii(c) && isspace(c)) -#endif /* _FFR_DELIM */ - +#define ISSEP(c) ((sep == '\0' && isascii(c) && isspace(c)) || (c) == sep) static void usage(progname) char *progname; { - fprintf(stderr, - "Usage: %s [-C cffile] [-N] [-c cachesize] [-d] [-e] [-f] [-l] [-o] [-r] [-s] %s[-u] [-v] type mapname\n", - progname, -#if _FFR_DELIM - "[-t delimiter] " -#else /* _FFR_DELIM */ - "" -#endif /* _FFR_DELIM */ - ); + sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "Usage: %s [-C cffile] [-N] [-c cachesize] [-d] [-e] [-f] [-l] [-o] [-r] [-s] [-t delimiter] [-u] [-v] type mapname\n", + progname); exit(EX_USAGE); } @@ -82,21 +69,19 @@ main(argc, argv) { char *progname; char *cfile; - bool inclnull = FALSE; - bool notrunc = FALSE; - bool allowreplace = FALSE; - bool allowempty = FALSE; - bool verbose = FALSE; - bool foldcase = TRUE; - bool unmake = FALSE; -#if _FFR_DELIM + bool inclnull = false; + bool notrunc = false; + bool allowreplace = false; + bool allowempty = false; + bool verbose = false; + bool foldcase = true; + bool unmake = false; char sep = '\0'; -#endif /* _FFR_DELIM */ int exitstat; int opt; char *typename = NULL; char *mapname = NULL; - int lineno; + unsigned int lineno; int st; int mode; int smode; @@ -110,7 +95,7 @@ main(argc, argv) SMDB_USER_INFO user_info; char ibuf[BUFSIZE]; #if HASFCHOWN - FILE *cfp; + SM_FILE_T *cfp; char buf[MAXLINE]; #endif /* HASFCHOWN */ static char rnamebuf[MAXNAME]; /* holds RealUserName */ @@ -125,21 +110,21 @@ main(argc, argv) progname++; else progname = argv[0]; - cfile = _PATH_SENDMAILCF; + cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL); clrbitmap(DontBlameSendmail); RunAsUid = RealUid = getuid(); RunAsGid = RealGid = getgid(); pw = getpwuid(RealUid); if (pw != NULL) - (void) strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf); + (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf); else - (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", - (int) RealUid); + (void) sm_snprintf(rnamebuf, sizeof rnamebuf, + "Unknown UID %d", (int) RealUid); RunAsUserName = RealUserName = rnamebuf; user_info.smdbu_id = RunAsUid; user_info.smdbu_group_id = RunAsGid; - (void) strlcpy(user_info.smdbu_name, RunAsUserName, + (void) sm_strlcpy(user_info.smdbu_name, RunAsUserName, SMDB_MAX_USER_NAME_LEN); @@ -153,7 +138,7 @@ main(argc, argv) break; case 'N': - inclnull = TRUE; + inclnull = true; break; case 'c': @@ -161,15 +146,15 @@ main(argc, argv) break; case 'd': - params.smdbp_allow_dup = TRUE; + params.smdbp_allow_dup = true; break; case 'e': - allowempty = TRUE; + allowempty = true; break; case 'f': - foldcase = FALSE; + foldcase = false; break; case 'l': @@ -178,11 +163,11 @@ main(argc, argv) break; case 'o': - notrunc = TRUE; + notrunc = true; break; case 'r': - allowreplace = TRUE; + allowreplace = true; break; case 's': @@ -192,23 +177,22 @@ main(argc, argv) setbitn(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail); break; -#if _FFR_DELIM case 't': if (optarg == NULL || *optarg == '\0') { - fprintf(stderr, "Invalid separator\n"); + sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "Invalid separator\n"); break; } sep = *optarg; break; -#endif /* _FFR_DELIM */ case 'u': - unmake = TRUE; + unmake = true; break; case 'v': - verbose = TRUE; + verbose = true; break; default: @@ -239,12 +223,14 @@ main(argc, argv) #if HASFCHOWN /* Find TrustedUser value in sendmail.cf */ - if ((cfp = fopen(cfile, "r")) == NULL) + if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile, SM_IO_RDONLY, + NULL)) == NULL) { - fprintf(stderr, "makemap: %s: %s", cfile, errstring(errno)); + sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "makemap: %s: %s", + cfile, sm_errstring(errno)); exit(EX_NOINPUT); } - while (fgets(buf, sizeof(buf), cfp) != NULL) + while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL) { register char *b; @@ -270,8 +256,9 @@ main(argc, argv) TrustedUid = 0; pw = getpwnam(b); if (pw == NULL) - fprintf(stderr, - "TrustedUser: unknown user %s\n", b); + (void) sm_io_fprintf(smioerr, + SM_TIME_DEFAULT, + "TrustedUser: unknown user %s\n", b); else TrustedUid = pw->pw_uid; } @@ -279,8 +266,9 @@ main(argc, argv) # ifdef UID_MAX if (TrustedUid > UID_MAX) { - fprintf(stderr, - "TrustedUser: uid value (%ld) > UID_MAX (%ld)", + (void) sm_io_fprintf(smioerr, + SM_TIME_DEFAULT, + "TrustedUser: uid value (%ld) > UID_MAX (%ld)", (long) TrustedUid, (long) UID_MAX); TrustedUid = 0; @@ -294,7 +282,7 @@ main(argc, argv) continue; } } - (void) fclose(cfp); + (void) sm_io_close(cfp, SM_TIME_DEFAULT); #endif /* HASFCHOWN */ if (!params.smdbp_allow_dup && !allowreplace) @@ -326,13 +314,14 @@ main(argc, argv) if (errno == SMDBE_UNSUPPORTED_DB_TYPE && (hint = smdb_db_definition(typename)) != NULL) - fprintf(stderr, - "%s: Need to recompile with -D%s for %s support\n", - progname, hint, typename); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: Need to recompile with -D%s for %s support\n", + progname, hint, typename); else - fprintf(stderr, - "%s: error opening type %s map %s: %s\n", - progname, typename, mapname, errstring(errno)); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: error opening type %s map %s: %s\n", + progname, typename, mapname, + sm_errstring(errno)); exit(EX_CANTCREAT); } @@ -343,9 +332,9 @@ main(argc, argv) errno = database->smdb_set_owner(database, TrustedUid, -1); if (errno != SMDBE_OK) { - fprintf(stderr, - "WARNING: ownership change on %s failed %s", - mapname, errstring(errno)); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "WARNING: ownership change on %s failed %s", + mapname, sm_errstring(errno)); } } @@ -360,9 +349,9 @@ main(argc, argv) if (errno != SMDBE_OK) { - fprintf(stderr, - "%s: cannot make cursor for type %s map %s\n", - progname, typename, mapname); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: cannot make cursor for type %s map %s\n", + progname, typename, mapname); exit(EX_SOFTWARE); } @@ -376,11 +365,12 @@ main(argc, argv) if (errno != SMDBE_OK) break; - printf("%.*s\t%.*s\n", - (int) db_key.size, - (char *) db_key.data, - (int) db_val.size, - (char *)db_val.data); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "%.*s\t%.*s\n", + (int) db_key.size, + (char *) db_key.data, + (int) db_val.size, + (char *)db_val.data); } (void) cursor->smdbc_close(cursor); @@ -388,7 +378,8 @@ main(argc, argv) else { lineno = 0; - while (fgets(ibuf, sizeof ibuf, stdin) != NULL) + while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf) + != NULL) { register char *p; @@ -401,26 +392,23 @@ main(argc, argv) p = strchr(ibuf, '\n'); if (p != NULL) *p = '\0'; - else if (!feof(stdin)) + else if (!sm_io_eof(smioin)) { - fprintf(stderr, - "%s: %s: line %d: line too long (%ld bytes max)\n", - progname, mapname, lineno, (long) sizeof ibuf); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: %s: line %u: line too long (%ld bytes max)\n", + progname, mapname, lineno, + (long) sizeof ibuf); exitstat = EX_DATAERR; continue; } if (ibuf[0] == '\0' || ibuf[0] == '#') continue; - if ( -#if _FFR_DELIM - sep == '\0' && -#endif /* _FFR_DELIM */ - isascii(ibuf[0]) && isspace(ibuf[0])) + if (sep == '\0' && isascii(ibuf[0]) && isspace(ibuf[0])) { - fprintf(stderr, - "%s: %s: line %d: syntax error (leading space)\n", - progname, mapname, lineno); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: %s: line %u: syntax error (leading space)\n", + progname, mapname, lineno); exitstat = EX_DATAERR; continue; } @@ -444,10 +432,10 @@ main(argc, argv) p++; if (!allowempty && *p == '\0') { - fprintf(stderr, - "%s: %s: line %d: no RHS for LHS %s\n", - progname, mapname, lineno, - (char *) db_key.data); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: %s: line %u: no RHS for LHS %s\n", + progname, mapname, lineno, + (char *) db_key.data); exitstat = EX_DATAERR; continue; } @@ -463,9 +451,10 @@ main(argc, argv) if (verbose) { - printf("key=`%s', val=`%s'\n", - (char *) db_key.data, - (char *) db_val.data); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "key=`%s', val=`%s'\n", + (char *) db_key.data, + (char *) db_val.data); } errno = database->smdb_put(database, &db_key, &db_val, @@ -487,19 +476,20 @@ main(argc, argv) if (st < 0) { - fprintf(stderr, - "%s: %s: line %d: key %s: put error: %s\n", - progname, mapname, lineno, - (char *) db_key.data, - errstring(errno)); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: %s: line %u: key %s: put error: %s\n", + progname, mapname, lineno, + (char *) db_key.data, + sm_errstring(errno)); exitstat = EX_IOERR; } else if (st > 0) { - fprintf(stderr, - "%s: %s: line %d: key %s: duplicate key\n", - progname, mapname, - lineno, (char *) db_key.data); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: %s: line %u: key %s: duplicate key\n", + progname, mapname, + lineno, + (char *) db_key.data); exitstat = EX_DATAERR; } } @@ -512,8 +502,9 @@ main(argc, argv) errno = database->smdb_close(database); if (errno != SMDBE_OK) { - fprintf(stderr, "%s: close(%s): %s\n", - progname, mapname, errstring(errno)); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: close(%s): %s\n", + progname, mapname, sm_errstring(errno)); exitstat = EX_IOERR; } smdb_free_database(database); @@ -522,51 +513,3 @@ main(argc, argv) /* NOTREACHED */ return exitstat; } - -/*VARARGS1*/ -void -#ifdef __STDC__ -message(const char *msg, ...) -#else /* __STDC__ */ -message(msg, va_alist) - const char *msg; - va_dcl -#endif /* __STDC__ */ -{ - const char *m; - VA_LOCAL_DECL - - m = msg; - if (isascii(m[0]) && isdigit(m[0]) && - isascii(m[1]) && isdigit(m[1]) && - isascii(m[2]) && isdigit(m[2]) && m[3] == ' ') - m += 4; - VA_START(msg); - (void) vfprintf(stderr, m, ap); - VA_END; - (void) fprintf(stderr, "\n"); -} - -/*VARARGS1*/ -void -#ifdef __STDC__ -syserr(const char *msg, ...) -#else /* __STDC__ */ -syserr(msg, va_alist) - const char *msg; - va_dcl -#endif /* __STDC__ */ -{ - const char *m; - VA_LOCAL_DECL - - m = msg; - if (isascii(m[0]) && isdigit(m[0]) && - isascii(m[1]) && isdigit(m[1]) && - isascii(m[2]) && isdigit(m[2]) && m[3] == ' ') - m += 4; - VA_START(msg); - (void) vfprintf(stderr, m, ap); - VA_END; - (void) fprintf(stderr, "\n"); -} diff --git a/gnu/usr.sbin/sendmail/praliases/Makefile b/gnu/usr.sbin/sendmail/praliases/Makefile index dbb66732423..8f412fc944e 100644 --- a/gnu/usr.sbin/sendmail/praliases/Makefile +++ b/gnu/usr.sbin/sendmail/praliases/Makefile @@ -1,9 +1,10 @@ -# $OpenBSD: Makefile,v 1.3 2000/05/15 03:37:32 millert Exp $ +# $OpenBSD: Makefile,v 1.4 2001/09/11 19:02:49 millert Exp $ PROG= praliases MAN= praliases.1 ENVDEF= -DNOT_SENDMAIL +WANT_LIBSM=1 WANT_LIBSMDB=1 WANT_LIBSMUTIL=1 diff --git a/gnu/usr.sbin/sendmail/praliases/praliases.1 b/gnu/usr.sbin/sendmail/praliases/praliases.1 index ca7b92ff244..1a10b25d228 100644 --- a/gnu/usr.sbin/sendmail/praliases/praliases.1 +++ b/gnu/usr.sbin/sendmail/praliases/praliases.1 @@ -6,7 +6,7 @@ .\" the sendmail distribution. .\" .\" -.\" $Sendmail: praliases.8,v 8.15.4.2 2000/12/15 19:50:45 gshapiro Exp $ +.\" $Sendmail: praliases.8,v 8.17 2000/12/15 19:53:45 gshapiro Exp $ .\" .Dd December 15, 2000 .Dt PRALIASES 1 diff --git a/gnu/usr.sbin/sendmail/praliases/praliases.c b/gnu/usr.sbin/sendmail/praliases/praliases.c index 5c17f7f9175..7e473a23ae8 100644 --- a/gnu/usr.sbin/sendmail/praliases/praliases.c +++ b/gnu/usr.sbin/sendmail/praliases/praliases.c @@ -11,18 +11,16 @@ * */ -#ifndef lint -static char copyright[] = +#include <sm/gen.h> + +SM_IDSTR(copyright, "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ All rights reserved.\n\ Copyright (c) 1983 Eric P. Allman. All rights reserved.\n\ Copyright (c) 1988, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* ! lint */ + The Regents of the University of California. All rights reserved.\n") -#ifndef lint -static char id[] = "@(#)$Sendmail: praliases.c,v 8.59.4.19 2001/02/28 02:37:57 ca Exp $"; -#endif /* ! lint */ +SM_IDSTR(id, "@(#)$Sendmail: praliases.c,v 8.91 2001/03/29 21:15:53 rodney Exp $") #include <sys/types.h> #include <ctype.h> @@ -50,12 +48,10 @@ uid_t RunAsUid; uid_t RunAsGid; char *RunAsUserName; int Verbose = 2; -bool DontInitGroups = FALSE; +bool DontInitGroups = false; uid_t TrustedUid = 0; BITMAP256 DontBlameSendmail; -extern void syserr __P((const char *, ...)); - # define DELIMITERS " ,/" # define PATH_SEPARATOR ':' @@ -66,7 +62,7 @@ main(argc, argv) { char *cfile; char *filename = NULL; - FILE *cfp; + SM_FILE_T *cfp; int ch; char afilebuf[MAXLINE]; char buf[MAXLINE]; @@ -75,7 +71,6 @@ main(argc, argv) extern char *optarg; extern int optind; - clrbitmap(DontBlameSendmail); RunAsUid = RealUid = getuid(); RunAsGid = RealGid = getgid(); @@ -84,14 +79,14 @@ main(argc, argv) { if (strlen(pw->pw_name) > MAXNAME - 1) pw->pw_name[MAXNAME] = 0; - snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name); + sm_snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name); } else - (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", - (int) RealUid); + (void) sm_snprintf(rnamebuf, sizeof rnamebuf, + "Unknown UID %d", (int) RealUid); RunAsUserName = RealUserName = rnamebuf; - cfile = _PATH_SENDMAILCF; + cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL); while ((ch = getopt(argc, argv, "C:f:")) != -1) { switch ((char)ch) { @@ -103,8 +98,8 @@ main(argc, argv) break; case '?': default: - (void)fprintf(stderr, - "usage: praliases [-C cffile] [-f aliasfile]\n"); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "usage: praliases [-C cffile] [-f aliasfile]\n"); exit(EX_USAGE); } } @@ -117,14 +112,16 @@ main(argc, argv) exit(EX_OK); } - if ((cfp = fopen(cfile, "r")) == NULL) + if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile, SM_IO_RDONLY, + NULL)) == NULL) { - fprintf(stderr, "praliases: %s: %s\n", - cfile, errstring(errno)); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "praliases: %s: %s\n", cfile, + sm_errstring(errno)); exit(EX_NOINPUT); } - while (fgets(buf, sizeof(buf), cfp) != NULL) + while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL) { register char *b, *p; @@ -136,7 +133,7 @@ main(argc, argv) switch (*b++) { case 'O': /* option -- see if alias file */ - if (strncasecmp(b, " AliasFile", 10) == 0 && + if (sm_strncasecmp(b, " AliasFile", 10) == 0 && !(isascii(b[10]) && isalnum(b[10]))) { /* new form -- find value */ @@ -153,13 +150,13 @@ main(argc, argv) } /* this is the A or AliasFile option -- save it */ - if (strlcpy(afilebuf, b, sizeof afilebuf) >= + if (sm_strlcpy(afilebuf, b, sizeof afilebuf) >= sizeof afilebuf) { - fprintf(stderr, - "praliases: AliasFile filename too long: %.30s\n", + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "praliases: AliasFile filename too long: %.30s\n", b); - (void) fclose(cfp); + (void) sm_io_close(cfp, SM_TIME_DEFAULT); exit(EX_CONFIG); } b = afilebuf; @@ -177,7 +174,7 @@ main(argc, argv) /* find end of spec */ if (p != NULL) { - bool quoted = FALSE; + bool quoted = false; for (; *p != '\0'; p++) { @@ -224,7 +221,7 @@ main(argc, argv) continue; } } - (void) fclose(cfp); + (void) sm_io_close(cfp, SM_TIME_DEFAULT); exit(EX_OK); /* NOTREACHED */ return EX_OK; @@ -280,8 +277,8 @@ praliases(filename, argc, argv) strcmp(db_type, "btree") != 0 && strcmp(db_type, "dbm") != 0) { - fprintf(stderr, - "praliases: Skipping non-file based alias type %s\n", + sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "praliases: Skipping non-file based alias type %s\n", db_type); return; } @@ -291,8 +288,8 @@ praliases(filename, argc, argv) { if (colon != NULL) *colon = ':'; - fprintf(stderr, "praliases: illegal alias specification: %s\n", - filename); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "praliases: illegal alias specification: %s\n", filename); goto fatal; } @@ -301,14 +298,16 @@ praliases(filename, argc, argv) user_info.smdbu_id = RunAsUid; user_info.smdbu_group_id = RunAsGid; - strlcpy(user_info.smdbu_name, RunAsUserName, SMDB_MAX_USER_NAME_LEN); + (void) sm_strlcpy(user_info.smdbu_name, RunAsUserName, + SMDB_MAX_USER_NAME_LEN); result = smdb_open_database(&database, db_name, O_RDONLY, 0, SFF_ROOTOK, db_type, &user_info, ¶ms); if (result != SMDBE_OK) { - fprintf(stderr, "praliases: %s: open: %s\n", - db_name, errstring(result)); + sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "praliases: %s: open: %s\n", + db_name, sm_errstring(result)); goto fatal; } @@ -320,8 +319,9 @@ praliases(filename, argc, argv) result = database->smdb_cursor(database, &cursor, 0); if (result != SMDBE_OK) { - fprintf(stderr, "praliases: %s: set cursor: %s\n", - db_name, errstring(result)); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "praliases: %s: set cursor: %s\n", db_name, + sm_errstring(result)); goto fatal; } @@ -340,18 +340,19 @@ praliases(filename, argc, argv) continue; #endif /* 0 */ - printf("%.*s:%.*s\n", - (int) db_key.size, - (char *) db_key.data, - (int) db_value.size, - (char *) db_value.data); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "%.*s:%.*s\n", + (int) db_key.size, + (char *) db_key.data, + (int) db_value.size, + (char *) db_value.data); } if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY) { - fprintf(stderr, + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "praliases: %s: get value at cursor: %s\n", - db_name, errstring(result)); + db_name, sm_errstring(result)); goto fatal; } } @@ -372,14 +373,17 @@ praliases(filename, argc, argv) } if (get_res == SMDBE_OK) { - printf("%.*s:%.*s\n", - (int) db_key.size, - (char *) db_key.data, - (int) db_value.size, - (char *) db_value.data); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "%.*s:%.*s\n", + (int) db_key.size, + (char *) db_key.data, + (int) db_value.size, + (char *) db_value.data); } else - printf("%s: No such key\n", (char *) db_key.data); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "%s: No such key\n", + (char *)db_key.data); } fatal: @@ -391,51 +395,3 @@ praliases(filename, argc, argv) *colon = ':'; return; } - -/*VARARGS1*/ -void -#ifdef __STDC__ -message(const char *msg, ...) -#else /* __STDC__ */ -message(msg, va_alist) - const char *msg; - va_dcl -#endif /* __STDC__ */ -{ - const char *m; - VA_LOCAL_DECL - - m = msg; - if (isascii(m[0]) && isdigit(m[0]) && - isascii(m[1]) && isdigit(m[1]) && - isascii(m[2]) && isdigit(m[2]) && m[3] == ' ') - m += 4; - VA_START(msg); - (void) vfprintf(stderr, m, ap); - VA_END; - (void) fprintf(stderr, "\n"); -} - -/*VARARGS1*/ -void -#ifdef __STDC__ -syserr(const char *msg, ...) -#else /* __STDC__ */ -syserr(msg, va_alist) - const char *msg; - va_dcl -#endif /* __STDC__ */ -{ - const char *m; - VA_LOCAL_DECL - - m = msg; - if (isascii(m[0]) && isdigit(m[0]) && - isascii(m[1]) && isdigit(m[1]) && - isascii(m[2]) && isdigit(m[2]) && m[3] == ' ') - m += 4; - VA_START(msg); - (void) vfprintf(stderr, m, ap); - VA_END; - (void) fprintf(stderr, "\n"); -} diff --git a/gnu/usr.sbin/sendmail/rmail/Makefile b/gnu/usr.sbin/sendmail/rmail/Makefile index 5be7009ab9e..004749d42e1 100644 --- a/gnu/usr.sbin/sendmail/rmail/Makefile +++ b/gnu/usr.sbin/sendmail/rmail/Makefile @@ -1,9 +1,9 @@ -# $OpenBSD: Makefile,v 1.3 2000/05/15 03:37:33 millert Exp $ +# $OpenBSD: Makefile,v 1.4 2001/09/11 19:02:49 millert Exp $ PROG= rmail MAN= rmail.8 -WANT_LIBSMUTIL=1 +WANT_LIBSM=1 .include "../../../../bin/Makefile.inc" .include <bsd.prog.mk> diff --git a/gnu/usr.sbin/sendmail/rmail/rmail.8 b/gnu/usr.sbin/sendmail/rmail/rmail.8 index 04ae669fd6a..3683309e141 100644 --- a/gnu/usr.sbin/sendmail/rmail/rmail.8 +++ b/gnu/usr.sbin/sendmail/rmail/rmail.8 @@ -1,4 +1,4 @@ -.\" Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +.\" Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. .\" All rights reserved. .\" Copyright (c) 1983, 1990 .\" The Regents of the University of California. All rights reserved. @@ -8,9 +8,9 @@ .\" the sendmail distribution. .\" .\" -.\" $Sendmail: rmail.8,v 8.1.16.2 2000/12/29 18:12:22 gshapiro Exp $ +.\" $Sendmail: rmail.8,v 8.4 2001/04/03 01:53:16 gshapiro Exp $ .\" -.TH RMAIL 8 "$Date: 2001/01/15 21:09:06 $" +.TH RMAIL 8 "$Date: 2001/09/11 19:02:49 $" .SH NAME rmail \- handle remote mail received via uucp diff --git a/gnu/usr.sbin/sendmail/rmail/rmail.c b/gnu/usr.sbin/sendmail/rmail/rmail.c index cfc80a5c424..0148124cf26 100644 --- a/gnu/usr.sbin/sendmail/rmail/rmail.c +++ b/gnu/usr.sbin/sendmail/rmail/rmail.c @@ -10,17 +10,15 @@ * */ -#ifndef lint -static char copyright[] = +#include <sm/gen.h> + +SM_IDSTR(copyright, "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ All rights reserved.\n\ Copyright (c) 1988, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* ! lint */ + The Regents of the University of California. All rights reserved.\n") -#ifndef lint -static char id[] = "@(#)$Sendmail: rmail.c,v 8.39.4.12 2001/05/07 22:06:39 gshapiro Exp $"; -#endif /* ! lint */ +SM_IDSTR(id, "@(#)$Sendmail: rmail.c,v 8.58 2001/09/04 22:44:31 ca Exp $") /* * RMAIL -- UUCP mail server. @@ -54,69 +52,18 @@ static char id[] = "@(#)$Sendmail: rmail.c,v 8.39.4.12 2001/05/07 22:06:39 gshap #include <ctype.h> #include <fcntl.h> -#ifdef BSD4_4 -# define FORK vfork -# include <paths.h> -#else /* BSD4_4 */ -# define FORK fork -# ifndef _PATH_SENDMAIL -# define _PATH_SENDMAIL "/usr/lib/sendmail" -# endif /* ! _PATH_SENDMAIL */ -#endif /* BSD4_4 */ -#include <stdio.h> +#include <sm/io.h> #include <stdlib.h> -#include <string.h> +#include <sm/string.h> #include <unistd.h> #ifdef EX_OK # undef EX_OK /* unistd.h may have another use for this */ #endif /* EX_OK */ #include <sysexits.h> -#ifndef MAX -# define MAX(a, b) ((a) < (b) ? (b) : (a)) -#endif /* ! MAX */ - -#ifndef __P -# ifdef __STDC__ -# define __P(protos) protos -# else /* __STDC__ */ -# define __P(protos) () -# define const -# endif /* __STDC__ */ -#endif /* ! __P */ - -#ifndef STDIN_FILENO -# define STDIN_FILENO 0 -#endif /* ! STDIN_FILENO */ - -#if defined(BSD4_4) || defined(linux) || SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) || _AIX4 >= 40300 || defined(HPUX11) -# define HASSNPRINTF 1 -#endif /* defined(BSD4_4) || defined(linux) || SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) || _AIX4 >= 40300 || defined(HPUX11) */ - -#if defined(sun) && !defined(BSD) && !defined(SOLARIS) && !defined(__svr4__) && !defined(__SVR4) -# define memmove(d, s, l) (bcopy((s), (d), (l))) -#endif /* defined(sun) && !defined(BSD) && !defined(SOLARIS) && !defined(__svr4__) && !defined(__SVR4) */ - -#if !HASSNPRINTF && !SFIO -extern int snprintf __P((char *, size_t, const char *, ...)); -#endif /* !HASSNPRINTF && !SFIO */ - -#if defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__) || defined(IRIX64) || defined(IRIX5) || defined(IRIX6) -# ifndef HASSTRERROR -# define HASSTRERROR 1 -# endif /* ! HASSTRERROR */ -#endif /* defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__) || - defined(IRIX64) || defined(IRIX5) || defined(IRIX6) */ - -#if defined(SUNOS403) || defined(NeXT) || (defined(MACH) && defined(i386) && !defined(__GNU__)) || defined(oldBSD43) || defined(MORE_BSD) || defined(umipsbsd) || defined(ALTOS_SYSTEM_V) || defined(RISCOS) || defined(_AUX_SOURCE) || defined(UMAXV) || defined(titan) || defined(UNIXWARE) || defined(sony_news) || defined(luna) || defined(nec_ews_svr4) || defined(_nec_ews_svr4) || defined(__MAXION__) -# undef WIFEXITED -# undef WEXITSTATUS -# define WIFEXITED(st) (((st) & 0377) == 0) -# define WEXITSTATUS(st) (((st) >> 8) & 0377) -#endif /* defined(SUNOS403) || defined(NeXT) || (defined(MACH) && defined(i386) && !defined(__GNU__)) || defined(oldBSD43) || defined(MORE_BSD) || defined(umipsbsd) || defined(ALTOS_SYSTEM_V) || defined(RISCOS) || defined(_AUX_SOURCE) || defined(UMAXV) || defined(titan) || defined(UNIXWARE) || defined(sony_news) || defined(luna) || defined(nec_ews_svr4) || defined(_nec_ews_svr4) || defined(__MAXION__) */ - - -#include "sendmail/errstring.h" +#include <sm/conf.h> +#include <sm/errstring.h> +#include <sendmail/pathnames.h> static void err __P((int, const char *, ...)); static void usage __P((void)); @@ -134,7 +81,7 @@ xalloc(sz) if (sz <= 0) sz = 1; - p = malloc((unsigned) sz); + p = malloc(sz); if (p == NULL) err(EX_TEMPFAIL, "out of memory"); return (p); @@ -148,7 +95,7 @@ main(argc, argv) int ch, debug, i, pdes[2], pid, status; size_t fplen = 0, fptlen = 0, len; off_t offset; - FILE *fp; + SM_FILE_T *fp; char *addrp = NULL, *domain, *p, *t; char *from_path, *from_sys, *from_user; char **args, buf[2048], lbuf[2048]; @@ -186,8 +133,9 @@ main(argc, argv) for (offset = 0; ; ) { /* Get and nul-terminate the line. */ - if (fgets(lbuf, sizeof(lbuf), stdin) == NULL) - exit(EX_DATAERR); + if (sm_io_fgets(smioin, SM_TIME_DEFAULT, lbuf, + sizeof(lbuf)) == NULL) + err(EX_DATAERR, "no data"); if ((p = strchr(lbuf, '\n')) == NULL) err(EX_DATAERR, "line too long"); *p = '\0'; @@ -221,7 +169,10 @@ main(argc, argv) } *t = '\0'; if (debug) - fprintf(stderr, "remote from: %s\n", p); + (void) sm_io_fprintf(smioerr, + SM_TIME_DEFAULT, + "remote from: %s\n", + p); break; } } @@ -241,7 +192,9 @@ main(argc, argv) err(EX_DATAERR, "corrupted From line: %s", lbuf); if (debug) - fprintf(stderr, "bang: %s\n", p); + (void) sm_io_fprintf(smioerr, + SM_TIME_DEFAULT, + "bang: %s\n", p); } } @@ -261,8 +214,10 @@ main(argc, argv) { from_sys = newstr(p); if (debug) - fprintf(stderr, "from_sys: %s\n", - from_sys); + (void) sm_io_fprintf(smioerr, + SM_TIME_DEFAULT, + "from_sys: %s\n", + from_sys); } /* Concatenate to the path string. */ @@ -271,14 +226,14 @@ main(argc, argv) { fplen = 0; if ((from_path = malloc(fptlen = 256)) == NULL) - err(EX_TEMPFAIL, NULL); + err(EX_TEMPFAIL, "out of memory"); } if (fplen + len + 2 > fptlen) { - fptlen += MAX(fplen + len + 2, 256); + fptlen += SM_MAX(fplen + len + 2, 256); if ((from_path = realloc(from_path, fptlen)) == NULL) - err(EX_TEMPFAIL, NULL); + err(EX_TEMPFAIL, "out of memory"); } memmove(from_path + fplen, p, len); fplen += len; @@ -302,36 +257,40 @@ main(argc, argv) if (debug) { if (from_path != NULL) - fprintf(stderr, "from_path: %s\n", from_path); - fprintf(stderr, "from_user: %s\n", from_user); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "from_path: %s\n", + from_path); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "from_user: %s\n", from_user); } if (offset != -1) - offset = (off_t)ftell(stdin); + offset = (off_t)sm_io_tell(smioin, SM_TIME_DEFAULT); } - /* Allocate args (with room for sendmail args as well as recipients) */ + /* Allocate args (with room for sendmail args as well as recipients */ args = (char **)xalloc(sizeof(*args) * (10 + argc)); i = 0; args[i++] = _PATH_SENDMAIL; /* Build sendmail's argument list. */ + args[i++] = "-G"; /* relay submission */ args[i++] = "-oee"; /* No errors, just status. */ args[i++] = "-odq"; /* Queue it, don't try to deliver. */ args[i++] = "-oi"; /* Ignore '.' on a line by itself. */ /* set from system and protocol used */ if (from_sys == NULL) - snprintf(buf, sizeof(buf), "-p%s", domain); + sm_snprintf(buf, sizeof(buf), "-p%s", domain); else if (strchr(from_sys, '.') == NULL) - snprintf(buf, sizeof(buf), "-p%s:%s.%s", + sm_snprintf(buf, sizeof(buf), "-p%s:%s.%s", domain, from_sys, domain); else - snprintf(buf, sizeof(buf), "-p%s:%s", domain, from_sys); + sm_snprintf(buf, sizeof(buf), "-p%s:%s", domain, from_sys); args[i++] = newstr(buf); /* Set name of ``from'' person. */ - snprintf(buf, sizeof(buf), "-f%s%s", + sm_snprintf(buf, sizeof(buf), "-f%s%s", from_path ? from_path : "", from_user); args[i++] = newstr(buf); @@ -354,7 +313,7 @@ main(argc, argv) len = strlen(*argv) + 3; if ((args[i] = malloc(len)) == NULL) err(EX_TEMPFAIL, "Cannot malloc"); - snprintf(args[i++], len, "<%s>", *argv); + sm_snprintf(args[i++], len, "<%s>", *argv); } argv++; argc--; @@ -367,9 +326,11 @@ main(argc, argv) if (debug) { - fprintf(stderr, "Sendmail arguments:\n"); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "Sendmail arguments:\n"); for (i = 0; args[i] != NULL; i++) - fprintf(stderr, "\t%s\n", args[i]); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "\t%s\n", args[i]); } /* @@ -388,12 +349,12 @@ main(argc, argv) } if (pipe(pdes) < 0) - err(EX_OSERR, NULL); + err(EX_OSERR, "pipe failed"); - switch (pid = FORK()) + switch (pid = fork()) { case -1: /* Err. */ - err(EX_OSERR, NULL); + err(EX_OSERR, "fork failed"); /* NOTREACHED */ case 0: /* Child. */ @@ -404,25 +365,27 @@ main(argc, argv) } (void) close(pdes[1]); (void) execv(_PATH_SENDMAIL, args); - _exit(127); + err(EX_UNAVAILABLE, "%s", _PATH_SENDMAIL); /* NOTREACHED */ } - if ((fp = fdopen(pdes[1], "w")) == NULL) - err(EX_OSERR, NULL); + if ((fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *)pdes[1], + SM_IO_WRONLY, NULL)) == NULL) + err(EX_OSERR, "sm_io_open failed"); (void) close(pdes[0]); /* Copy the file down the pipe. */ do { - (void) fprintf(fp, "%s", lbuf); - } while (fgets(lbuf, sizeof(lbuf), stdin) != NULL); + (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", lbuf); + } while (sm_io_fgets(smioin, SM_TIME_DEFAULT, lbuf, + sizeof(lbuf)) != NULL); - if (ferror(stdin)) - err(EX_TEMPFAIL, "stdin: %s", errstring(errno)); + if (sm_io_error(smioin)) + err(EX_TEMPFAIL, "stdin: %s", sm_errstring(errno)); - if (fclose(fp)) - err(EX_OSERR, NULL); + if (sm_io_close(fp, SM_TIME_DEFAULT)) + err(EX_OSERR, "sm_io_close failed"); if ((waitpid(pid, &status, 0)) == -1) err(EX_OSERR, "%s", _PATH_SENDMAIL); @@ -441,16 +404,11 @@ main(argc, argv) static void usage() { - (void) fprintf(stderr, "usage: rmail [-T] [-D domain] user ...\n"); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "usage: rmail [-T] [-D domain] user ...\n"); exit(EX_USAGE); } -#ifdef __STDC__ -# include <stdarg.h> -#else /* __STDC__ */ -# include <varargs.h> -#endif /* __STDC__ */ - static void #ifdef __STDC__ err(int eval, const char *fmt, ...) @@ -461,15 +419,15 @@ err(eval, fmt, va_alist) va_dcl #endif /* __STDC__ */ { - va_list ap; -#ifdef __STDC__ - va_start(ap, fmt); -#else /* __STDC__ */ - va_start(ap); -#endif /* __STDC__ */ - (void) fprintf(stderr, "rmail: "); - (void) vfprintf(stderr, fmt, ap); - va_end(ap); - (void) fprintf(stderr, "\n"); + SM_VA_LOCAL_DECL + + if (fmt != NULL) + { + SM_VA_START(ap, fmt); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "rmail: "); + (void) sm_io_vfprintf(smioerr, SM_TIME_DEFAULT, fmt, ap); + SM_VA_END(ap); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "\n"); + } exit(eval); } diff --git a/gnu/usr.sbin/sendmail/sendmail/Makefile b/gnu/usr.sbin/sendmail/sendmail/Makefile index 4bf21c2ebe1..1fd52c631f5 100644 --- a/gnu/usr.sbin/sendmail/sendmail/Makefile +++ b/gnu/usr.sbin/sendmail/sendmail/Makefile @@ -1,36 +1,37 @@ -# $OpenBSD: Makefile,v 1.12 2001/05/05 22:00:17 millert Exp $ +# $OpenBSD: Makefile,v 1.13 2001/09/11 19:02:49 millert Exp $ PROG= sendmail WANT_LIBWRAP=1 +WANT_LIBSM=1 WANT_LIBSMUTIL=1 # For TLS/SSL support -ENVDEF+= -DSTARTTLS -D_FFR_TLS_TOREK +ENVDEF+= -DSTARTTLS LDADD+= -lssl -lcrypto DPADD= ${LIBSSL} ${LIBCRYPTO} -# Work around broken name servers that return SERV_FAIL for AAAA records -ENVDEF+= -D_FFR_WORKAROUND_BROKEN_NAMESERVERS +# To build with mail filter support (untested) +#WANT_LIBMILTER=1 +#ENVDEF+= -DMILTER +#CPPFLAGS+= -pthread +#LDFLAGS+= -pthread # Since we have random PIDs we need to be careful to avoid filename collisions ENVDEF+= -DFAST_PID_RECYCLE -# To cause sendmail to drop privs in test mode (-bt) uncomment the following -#ENVDEF+= -D_FFR_TESTMODE_DROP_PRIVS - -SRCS= main.c alias.c arpadate.c bf_torek.c clock.c collect.c \ - conf.c control.c convtime.c daemon.c deliver.c domain.c \ - envelope.c err.c headers.c macro.c map.c mci.c milter.c \ - mime.c parseaddr.c queue.c readcf.c recipient.c savemail.c \ - sfsasl.c shmticklib.c srvrsmtp.c stab.c stats.c sysexits.c \ - timers.c trace.c udb.c usersmtp.c util.c version.c +SRCS= main.c alias.c arpadate.c bf.c collect.c conf.c control.c convtime.c \ + daemon.c deliver.c domain.c envelope.c err.c headers.c macro.c map.c \ + mci.c milter.c mime.c parseaddr.c queue.c readcf.c recipient.c \ + savemail.c sasl.c sfsasl.c shmticklib.c sm_resolve.c srvrsmtp.c stab.c \ + stats.c sysexits.c timers.c tls.c trace.c udb.c usersmtp.c util.c \ + version.c MAN= aliases.5 mailq.1 newaliases.8 sendmail.8 MLINKS= sendmail.8 hoststat.1 sendmail.8 purgestat.1 BINDIR= /usr/libexec/sendmail BINOWN= root -BINGRP= bin -BINMODE=4555 +BINGRP= smmsp +BINMODE=2555 beforeinstall: # Force user to make the world sane for us @@ -46,9 +47,9 @@ beforeinstall: then echo "Error: /etc/rc must check for /etc/mail/sendmail.cf, not /etc/sendmail.cf"; \ false; \ fi - ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m 444 \ + ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g wheel -m 444 \ ${.CURDIR}/helpfile ${DESTDIR}/etc/mail/helpfile - ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m 644 \ + ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g wheel -m 644 \ /dev/null ${DESTDIR}/var/log/sendmail.st .include "../../Makefile.inc" diff --git a/gnu/usr.sbin/sendmail/sendmail/README b/gnu/usr.sbin/sendmail/sendmail/README index 9d3709bbae6..ef74b7720e6 100644 --- a/gnu/usr.sbin/sendmail/sendmail/README +++ b/gnu/usr.sbin/sendmail/sendmail/README @@ -9,17 +9,14 @@ # the sendmail distribution. # # -# $Sendmail: README,v 8.263.2.1.2.38 2001/08/15 22:07:11 gshapiro Exp $ +# $Sendmail: README,v 8.333 2001/09/08 01:21:08 gshapiro Exp $ # This directory contains the source files for sendmail(TM). -********************* -!! DO NOT USE MAKE !! in this directory to compile sendmail -- -********************* instead, use the "Build" script located in -the sendmail directory. It will build an appropriate Makefile, and -create an appropriate obj.* subdirectory so that multiplatform -support works easily. + ******************************************************************* + !! Read sendmail/SECURITY for important installation information !! + ******************************************************************* ********************************************************** ** Read below for more details on building sendmail. ** @@ -32,7 +29,7 @@ support works easily. For detailed instructions, please read the document ../doc/op/op.me: - eqn ../doc/op/op.me | pic | ditroff -me + cd ../doc/op ; make op.ps op.txt Sendmail is a trademark of Sendmail, Inc. @@ -123,7 +120,9 @@ MAP_REGEX Regular Expression support. You will need to use an operating system which comes with the POSIX regex() routines or install a regexp library such as libregex from the Free Software Foundation. -PH_MAP PH map support. You will need the qi PH package. +DNSMAP DNS map support. Requires NAMED_BIND. +PH_MAP PH map support. You will need the libphclient library from + the nph package (http://www-dev.cso.uiuc.edu/ph/nph/). MAP_NSD nsd map support (IRIX 6.5 and later). >>> NOTE WELL for NEWDB support: If you want to get ndbm support, for @@ -205,7 +204,8 @@ SYS5SETPGRP Use System V setpgrp() semantics. Implied by SYSTEM5. HASFCHMOD Define this to one if you have the fchmod(2) system call. This improves security. HASFCHOWN Define this to one if you have the fchown(2) system call. - This is required for the TrustedUser option. + This is required for the TrustedUser option if sendmail + must rebuild an (alias) map. HASFLOCK Set this if you prefer to use the flock(2) system call rather than using fcntl-based locking. Fcntl locking has some semantic gotchas, but many vendor systems @@ -273,17 +273,13 @@ HASURANDOMDEV Define this if your system has /dev/urandom(4). HASSTRERROR Define this if you have the libc strerror(3) function (which should be declared in <errno.h>), and it should be used instead of sys_errlist. -NEEDGETOPT Define this if you need a reimplementation of getopt(3). +SM_CONF_GETOPT Define this as 0 if you need a reimplementation of getopt(3). On some systems, getopt does very odd things if called to scan the arguments twice. This flag will ask sendmail to compile in a local version of getopt that works properly. NEEDSTRTOL Define this if your standard C library does not define strtol(3). This will compile in a local version. -NEEDVPRINTF Define this if your standard C library does not define - vprintf(3). Note that the resulting fake implementation - is not very elegant and may not even work on some - architectures. NEEDFSYNC Define this if your standard C library does not define fsync(2). This will try to simulate the operation using fcntl(2); if that is not available it does nothing, which @@ -446,9 +442,6 @@ SIOCGIFNUM_IS_BROKEN Set this if your system has an SIOCGIFNUM ioctl defined, but it doesn't behave the same way as "most" systems (Solaris, HP-UX). -NEED_PERCENTQ Set this if your system doesn't support the printf - format strings %lld or %llu. If this is set, %qd and - %qu are used instead. FAST_PID_RECYCLE Set this if your system can reuse the same PID in the same second. @@ -456,13 +449,16 @@ SO_REUSEADDR_IS_BROKEN Set this if your system has a setsockopt() SO_REUSEADDR flag but doesn't pay attention to it when trying to bind a socket to a recently closed port. -SNPRINTF_IS_BROKEN - Set this if your system has an snprintf() implementation - which does not NUL terminate the string being filled in. - Use test/t_snprintf.c to test your system. NEEDSGETIPNODE Set this if your system supports IPv6 but doesn't include the getipnodeby{name,addr}() functions. Set automatically for Linux's glibc. +SM_CONF_STRL + By default this is on (1). This uses the sendmail versions + of strlcpy and strlcat (called sm_strlcpy and sm_strlcat + respectively). The program ../libsm/b-strl.c can be used + to determine performance of these functions if your OS + has these functions natively. +PIPELINING Support SMTP PIPELINING (set by default). +-----------------------+ @@ -529,8 +525,6 @@ NETUNIX Define this to get Unix domain networking support. Defined support this networking domain. NETNS Define this to get NS networking support. NETX25 Define this to get X.25 networking support. -SMTP Define this to get the SMTP code. Implied by NETINET - or NETISO. NAMED_BIND If non-zero, include DNS (name daemon) support, including MX support. The specs say you must use this if you run SMTP. You don't have to be running a name server daemon @@ -538,12 +532,6 @@ NAMED_BIND If non-zero, include DNS (name daemon) support, including including remote access to another machine, requires this option. Defined by default in conf.h. Define it to zero ONLY on machines that do not use DNS in any way. -QUEUE Define this to get queueing code. Implied by NETINET - or NETISO; required by SMTP. This gives you other good - stuff -- it should be on. -DAEMON Define this to get general network support. Implied by - NETINET or NETISO. Defined by default in conf.h. You - almost certainly want it on. MATCHGECOS Permit fuzzy matching of user names against the full name (GECOS) field in the /etc/passwd file. This should probably be on, since you can disable it from the config @@ -568,7 +556,7 @@ SHARE_V1 Support for the fair share scheduler, version 1. Setting to 1 causes final delivery to be done using the recipients resource limitations. So far as I know, this is only supported on ConvexOS. -SASL Enables SMTP AUTH (RFC 2554). This requires the Cyrus SASL +SASL Enables SMTP AUTH (RFC 2554). This requires the Cyrus SASL library (ftp://ftp.andrew.cmu.edu/pub/cyrus-mail/). Please install at least version 1.5.13. See below for further information: SASL COMPILATION AND CONFIGURATION. If your @@ -576,31 +564,27 @@ SASL Enables SMTP AUTH (RFC 2554). This requires the Cyrus SASL to its version number using a simple conversion: a.b.c -> c + b*100 + a*10000, e.g. for 1.5.9 define SASL=10509. Note: Using an older version than 1.5.5 of Cyrus SASL is - not supported. Starting with version 1.5.10, setting SASL=1 - is sufficient. Any value other than 1 (or 0) will be + not supported. Starting with version 1.5.10, setting SASL=1 + is sufficient. Any value other than 1 (or 0) will be compared with the actual version found and if there is a mismatch, compilation will fail. EGD Define this if your system has EGD installed, see - http://www.lothar.com/tech/crypto/ . It should be used to + http://www.lothar.com/tech/crypto/ . It should be used to seed the PRNG for STARTTLS if HASURANDOMDEV is not defined. -STARTTLS Enables SMTP STARTTLS (RFC 2487). This requires OpenSSL - (http://www.OpenSSL.org/) and sfio (see below). - Use OpenSSL 0.9.5a or later (if compatible with this - version), do not use 0.9.3. +STARTTLS Enables SMTP STARTTLS (RFC 2487). This requires OpenSSL + (http://www.OpenSSL.org/); use OpenSSL 0.9.5a or later + (if compatible with this version), do not use 0.9.3. See STARTTLS COMPILATION AND CONFIGURATION for further information. TLS_NO_RSA Turn off support for RSA algorithms in STARTTLS. -SFIO Uses sfio instead of stdio. sfio is available from AT&T - (http://www.research.att.com/sw/tools/sfio/). If this - compile flag is set, confSTDIO_TYPE must be set to portable. - This compile flag is necessary for STARTTLS; it also - enables the security layer of SASL. The sfio include file - stdio.h must be installed in a subdirectory called sfio, - i.e., if you install sfio in /usr/local, stdio.h should - be in /usr/local/include/sfio, and libsfio.a should be in - /usr/local/lib. Notice: read the sfio section in - OPERATING SYSTEM AND COMPILE QUIRKS. - +MILTER Turn on support for external filters using the Milter API. + See libmilter/README for more information. +REQUIRES_DIR_FSYNC Turn on support for file systems that require to + call fsync() for a directory if the meta-data in it has + been changed. This should be turned on at least for + ReiserFS; it is enabled by default for Linux. An alternative + to this compile time flag is to mount the queue directory + without the -async option, or using chattr +S on Linux. Generic notice: If you enable a compile time option that needs libraries or include files that don't come with sendmail or are @@ -642,25 +626,21 @@ YOU HEADACHES! When attempting to canonify a hostname, some broken name servers will return SERVFAIL (a temporary failure) on T_AAAA (IPv6) lookups. If you -want to excuse this behavior, compile sendmail with --D_FFR_WORKAROUND_BROKEN_NAMESERVERS and add WorkAroundBrokenAAAA to your -ResolverOptions setting. However, instead, we recommend catching the -problem and reporting it to the name server administrator so we can rid the -world of broken name servers. +want to excuse this behavior, include WorkAroundBrokenAAAA in +ResolverOptions. However, instead, we recommend catching the problem and +reporting it to the name server administrator so we can rid the world of +broken name servers. +----------------------------------------+ | STARTTLS COMPILATION AND CONFIGURATION | +----------------------------------------+ -Please read the docs accompanying the OpenSSL library and sfio. -You have to compile and install both libraries before you can compile +Please read the documentation accompanying the OpenSSL library. You +have to compile and install the OpenSSL libraries before you can compile sendmail. See devtools/README how to set the correct compile time parameters; you should at least set the following variables: -define(`confSTDIO_TYPE', `portable') -APPENDDEF(`conf_sendmail_ENVDEF', `-DSFIO') -APPENDDEF(`conf_sendmail_LIBS', `-lsfio') APPENDDEF(`conf_sendmail_ENVDEF', `-DSTARTTLS') APPENDDEF(`conf_sendmail_LIBS', `-lssl -lcrypto') @@ -681,10 +661,6 @@ and try again. Then take a look at the logfile and see whether there are any problems listed about permissions (unsafe files) or the validity of X.509 certificates. -Note: sfio must be used in all libraries with which sendmail exchanges -file pointers. An example is PH map support. This does not apply to the -usual libraries, e.g., OpenSSL, Berkeley DB, Cyrus SASL. - Further information can be found via: http://www.sendmail.org/tips/ @@ -693,11 +669,11 @@ http://www.sendmail.org/tips/ | SASL COMPILATION AND CONFIGURATION | +------------------------------------+ -Please read the docs accompanying the library (INSTALL and README). -If you use Berkeley DB for Cyrus SASL then you must compile sendmail -with the same version of Berkeley DB. See devtools/README how to -set the correct compile time parameters; you should at least set -the following variables: +Please read the documentation accompanying the Cyrus SASL library +(INSTALL and README). If you use Berkeley DB for Cyrus SASL then +you must compile sendmail with the same version of Berkeley DB. +See devtools/README how to set the correct compile time parameters; +you should at least set the following variables: APPENDDEF(`conf_sendmail_ENVDEF', `-DSASL') APPENDDEF(`conf_sendmail_LIBS', `-lsasl') @@ -709,10 +685,10 @@ BUILDING SENDMAIL. You have to select and install authentication mechanisms and tell sendmail where to find the sasl library and the include files (see -devtools/README for the parameters to set). Setup the required +devtools/README for the parameters to set). Setup the required users and passwords as explained in the SASL documentation. See -also cf/README for authentication related options (esp. DefaultAuthInfo -if you want authentication between MTAs). +also cf/README for authentication related options (especially +DefaultAuthInfo if you want authentication between MTAs). To perform an initial test, connect to your sendmail daemon (telnet localhost 25) and issue a EHLO localhost and see whether @@ -731,6 +707,9 @@ http://www.sendmail.org/tips/ +-------------------------------------+ GCC problems + When compiling with "gcc -O -Wall" specify "-DSM_OMIT_BOGUS_WARNINGS" + too (see include/sm/cdefs.h for more info). + ***************************************************************** ** IMPORTANT: DO NOT USE OPTIMIZATION (``-O'') IF YOU ARE ** ** RUNNING GCC 2.4.x or 2.5.x. THERE IS A BUG IN THE GCC ** @@ -774,9 +753,9 @@ Configuration file location binary. NETINFO systems use NETINFO to determine the location of - sendmail.cf. The full path to sendmail.cf is stored as the value of + sendmail.cf. The full path to sendmail.cf is stored as the value of the "sendmail.cf" property in the "/locations/sendmail" - subdirectory of NETINFO. Set the value of this property to + subdirectory of NETINFO. Set the value of this property to "/etc/mail/sendmail.cf" (without the quotes) to use this new default location for Sendmail 8.10.0 and higher. @@ -901,7 +880,7 @@ Solaris 2.4 (SunOS 5.4) >> testing and debugging mechanisms. It was decided that the only >> secure way to do this was to allow a `trusted' path to be used in >> LD_LIBRARY_PATH. The only trusted directory we presently define - >> is /usr/lib. Thus a setuid root developer could play with some + >> is /usr/lib. Thus a set-user-ID root developer could play with some >> alternative shared object implementations and place them in >> /usr/lib (being root we assume they'ed have access to write in this >> directory). This change was made as part of 1155380 - after a @@ -920,7 +899,7 @@ Solaris 2.5.1 (SunOS 5.5.1) and 2.6 (SunOS 5.6) Apparently Solaris 2.5.1 patch 103663-01 installs a new /usr/include/resolv.h file that defines the __P macro without checking to see if it is already defined. This new resolv.h is also - included in the Solaris 2.6 distribution. This causes compile + included in the Solaris 2.6 distribution. This causes compile warnings such as: In file included from daemon.c:51: @@ -933,8 +912,7 @@ Solaris 2.5.1 (SunOS 5.5.1) and 2.6 (SunOS 5.6) #undef __P #include "/usr/include/resolv.h" - Sun is aware of the problem (Sun bug ID 4081053) and it will be fixed - in Solaris 2.7. + This problem was fixed in Solaris 7 (Sun bug ID 4081053). Solaris 7 (SunOS 5.7) Solaris 7 includes LDAP libraries but the implementation was @@ -950,6 +928,15 @@ Solaris 7 (SunOS 5.7) to ldap_set_option for LDAP_OPT_REFERRALS in ldapmap_setopts if LDAP support is compiled in sendmail. +Solaris 8 and later (SunOS 5.8 and later) + Solaris 8 and later can optionally install LDAP support. If you + have installed the Entire Distribution meta-cluster cluster, you + can use the following in devtools/Site/site.SunOS.5.8.m4 (or other + appropriately versioned file) to enable LDAP: + + APPENDDEF(`confMAPDEF', `-DLDAPMAP') + APPENDDEF(`confLIBS', `-lldap') + Solaris If you are using dns for hostname resolution on Solaris, make sure that the 'dns' entry is last on the hosts line in @@ -1070,6 +1057,9 @@ IRIX 6.x less than 16 bits long unless they are 8 bits; IRIX 6.2 has some other sized structs. See http://www.bitmechanic.com/mail-archives/mysql/current/0418.html + This problem seems to be fixed by gcc v2.95.2, gcc v2.8.1 + is reported as broken. Check your gcc version for this bug + before installing sendmail. IRIX 6.4 The IRIX 6.5.4 version of /bin/m4 does not work properly with @@ -1102,13 +1092,13 @@ BSDI (BSD/386) 1.0, NetBSD 0.9, FreeBSD 1.0 I haven't had a chance to test this myself. The M4 shipped in FreeBSD and NetBSD 0.9 don't handle the config - files properly. One must use either GNU m4 1.1 or the PD-M4 + files properly. One must use either GNU m4 1.1 or the PD-M4 recently posted in comp.os.386bsd.bugs (and maybe others). NetBSD-current includes the PD-M4 (as stated in the NetBSD file CHANGES). - FreeBSD 1.0 RELEASE has uname(2) now. Use -DUSEUNAME in order to - use it (look into devtools/OS/FreeBSD). NetBSD-current may have + FreeBSD 1.0 RELEASE has uname(2) now. Use -DUSEUNAME in order to + use it (look into devtools/OS/FreeBSD). NetBSD-current may have it too but it has not been verified. The latest version of Berkeley DB uses a different naming @@ -1134,6 +1124,12 @@ BSDI (BSD/386) 1.0, NetBSD 0.9, FreeBSD 1.0 APPENDDEF(`confOBJADD', `oldbind.compat.o') +OpenBSD (up to 2.9 Release), NetBSD, FreeBSD (up to 4.3-RELEASE) + m4 from *BSD won't handle libsm/Makefile.m4 properly, since the + maximum length for strings is too short. You need to use GNU m4 + or patch m4, see for example: + http://FreeBSD.org/cgi/cvsweb.cgi/src/usr.bin/m4/eval.c.diff?r1=1.11&r2=1.12 + A/UX Date: Tue, 12 Oct 1993 18:28:28 -0400 (EDT) From: "Eric C. Hagberg" <hagberg@med.cornell.edu> @@ -1151,7 +1147,7 @@ A/UX What I did was to get the gnu-dbm-1.6 package, compile it, and then re-compile sendmail with "-lgdbm", "-DNDBM", and using the - ndbm.h header file that comes with the gnu-package. This makes + ndbm.h header file that comes with the gnu-package. This makes things behave properly. [NOTE: see comment above about GDBM] @@ -1171,14 +1167,18 @@ SCO Unix to know that if they are on SCO, they had better set OI-dnsrch or they will core dump as soon as they try to use the resolver. - ie. although SCO has _res.dnsrch defined, and is kinda BIND 4.8.3, it - does not inititialise it, nor does it understand 'search' in + i.e., although SCO has _res.dnsrch defined, and is kinda BIND 4.8.3, + it does not inititialise it, nor does it understand 'search' in /etc/named.boot. - sigh - According to SCO, the m4 which ships with UnixWare 2.1.2 is broken. We recommend installing GNU m4 before attempting to build sendmail. + On some versions a bogus error value is listed if connections + time out (large negative number). To avoid this explicitly set + Timeout.connect to a reasonable value (several minutes). + DG/UX Doug Anderson <dlander@afterlife.ncsc.mil> has successfully run V8 on the DG/UX 5.4.2 and 5.4R3.x platforms under heavy usage. @@ -1205,8 +1205,8 @@ HP-UX 8.00 From: Kimmo Suominen <Kimmo.Suominen@lut.fi> Subject: 8.6.5 w/ HP-UX 8.00 on s300 - Just compiled and fought with sendmail 8.6.5 on a HP9000/360 (ie. a - series 300 machine) running HP-UX 8.00. + Just compiled and fought with sendmail 8.6.5 on a HP9000/360 (i.e., + a series 300 machine) running HP-UX 8.00. I was getting segmentation fault when delivering to a local user. With debugging I saw it was faulting when doing _free@libc... *sigh* @@ -1237,13 +1237,6 @@ Linux with sendmail's version of cdefs.h. Deleting sendmail's version on those systems should be non-harmful, and new versions don't care. - Sendmail assumes that libc has snprintf, which has been true since - libc 4.7.0. If you are running an older version, you will need to - use -DHASSNPRINTF=0 in the Makefile. If may be able to use -lbsd - (which includes snprintf) instead of turning this off on versions - of libc between 4.4.4 and 4.7.0 (snprintf improves security, so - you want to use this if at all possible). - NOTE ON LINUX & BIND: By default, the Makefile generated for Linux includes header files in /usr/local/include and libraries in /usr/local/lib. If you've installed BIND on your system, the header @@ -1266,11 +1259,6 @@ Linux implementation in the Linux 2.2.0 kernel and poll()-aware versions of glib (at least up to 2.0.111). - Some pre-glibc distributions of Linux include a syslog.h that does - not work properly with SFIO. You can fix this by adding - "#include <syslog.h>" to the SFIO version of stdio.h as the very - first line. - glibc glibc 2.2.1 (and possibly other versions) changed the value of __RES in resolv.h but failed to actually provide the IPv6 API @@ -1342,44 +1330,6 @@ AIX 3.1.x If you don't care about load average throttling, just turn off load average checking using -DLA_TYPE=LA_ZERO. -AIX 2.2.1 - Date: Mon Dec 4 14:14:56 CST 1995 - From: Mark Whetzel <markw@antimatr.houston.tx.us> - Subject: Porting sendmail 8.7.2 to AIX V2 on the RT. - - This version of sendmail does not support MB, MG, and MR resource - records, which are supported by AIX sendmail. - - AIX V2 on the RT does not have 'paths.h'. Create a null - file in the 'obj' directory to remove this compile error. - - A patch file is needed to get the BSD 'db' library to compile - for AIX/RT. I have sent the necessary updates to the author, - but they may not be immediately available. - [NOTE: Berkeley DB version 2.X runs on AIX/RT.] - - The original AIX/RT resolver libraries are very old, and you - should get the latest BIND to replace it. The 4.8.3 version - has been tested, but 4.9.x is out and should work. - - To make the load average code work correctly requires an - external routine, as the kernel does not maintain system - load averages, similar to AIX V3.1.x. A reverse port of the - older 1.05 'monitor' load average daemon code written by - Jussi Maki that will work on AIX V2 for the RT is available - by E-mail to Mark Whetzel <markw@antimatr.houston.tx.us>. - That code depends on an external daemon to collect system - load information, and the external routine 'getloadavg', - that will return that information. The 'LA_SUBR' define - will handle this for AIX V2 on the RT. - - Note: You will have to change devtools/OS/AIX.2 to correctly - point to the locatons of the updated BIND source tree and - the location of the 'newdb' tree and library location. - You will also have to change devtools/OS/AIX.2 to know - about the location of the 'getloadavg' routine if you use - the LA_SUBR define. - RISC/os RISC/os from MIPS is a merged AT&T/Berkeley system. When you compile on that platform you will get duplicate definitions @@ -1555,30 +1505,14 @@ OpenSSL Do not use 0.9.3, but OpenSSL 0.9.5a or later if compatible with 0.9.5a. -sfio - You may run into problems if you use sfio2000 (the body of a - message is lost). Use sfio1999 instead; however, it also has - a bug that can cause sendmail to fail. A patch has been provided - by Petr Lampa of Brno University of Technology, which is given here: - -diff -rc ../../../../sfio/src/lib/sfio/sfputr.c ./sfputr.c -*** ../../../../sfio/src/lib/sfio/sfputr.c Tue May 16 18:25:49 2000 ---- ./sfputr.c Wed Sep 20 09:06:01 2000 -*************** -*** 24,29 **** ---- 24,30 ---- - for(w = 0; (*s || rc >= 0); ) - { SFWPEEK(f,ps,p); - -+ if(p == -1) return -1; /* PL */ - if(p == 0 || (f->flags&SF_WHOLE) ) - { n = strlen(s); - if(p >= (n + (rc < 0 ? 0 : 1)) ) - - PH PH support is provided by Mark Roth <roth@uiuc.edu>. The map is described at http://www-dev.cso.uiuc.edu/sendmail/ . + + NOTE: The "spacedname" pseudo-field which was used by earlier + versions of the PH map code is no longer supported! See the URL + listed above for more information. + Please contact Mark Roth for support and questions regarding the map. @@ -1605,7 +1539,7 @@ Regular Expressions (MAP_REGEX) pattern-compile-error: : Operation not applicable - Your libc does not include a running version of POSIX-regex. Use + Your libc does not include a running version of POSIX-regex. Use librx or regex.o from the GNU Free Software Foundation, ftp://ftp.gnu.org/pub/gnu/rx-?.?.tar.gz or ftp://ftp.gnu.org/pub/gnu/regex-?.?.tar.gz. @@ -1665,14 +1599,10 @@ TRACEFLAGS My own personal list of the trace flags -- not guaranteed alias.c Does name aliasing in all forms. aliases.5 Man page describing the format of the aliases file. arpadate.c A subroutine which creates ARPANET standard dates. -bf.h Buffered file I/O function declarations. -bf_portable.c Stub routines for systems lacking the Torek stdio library. -bf_portable.h Data structure and function declarations for bf_portable.c. -bf_torek.c Routines to implement memory-buffered file system using - hooks provided by Torek stdio library. -bf_torek.h Data structure and function declarations for bf_torek.c. -clock.c Routines to implement real-time oriented functions - in sendmail -- e.g., timeouts. +bf.c Routines to implement memory-buffered file system using + hooks provided by libsm now (formerly Torek stdio library). +bf.h Buffered file I/O function declarations and + data structure and function declarations for bf.c. collect.c The routine that actually reads the mail into a temp file. It also does a certain amount of parsing of the header, etc. @@ -1681,9 +1611,9 @@ conf.c The configuration file. This contains information controversial, or code compiled in for efficiency reasons. Most of the configuration is in sendmail.cf. conf.h Configuration that must be known everywhere. +control.c Routines to implement control socket. convtime.c A routine to sanely process times. -daemon.c Routines to implement daemon mode. This version is - specifically for Berkeley 4.1 IPC. +daemon.c Routines to implement daemon mode. deliver.c Routines to deliver mail. domain.c Routines that interface with DNS (the Domain Name System). @@ -1707,12 +1637,15 @@ queue.c Routines to implement message queueing. readcf.c The routine that reads the configuration file and translates it to internal form. recipient.c Routines that manipulate the recipient list. +sasl.c Routines to interact with Cyrys-SASL. savemail.c Routines which save the letter on processing errors. sendmail.8 Man page for the sendmail command. sendmail.h Main header file for sendmail. -sfsasl.c I/O interface between SASL/TLS and the MTA using SFIO. +sfsasl.c I/O interface between SASL/TLS and the MTA. sfsasl.h Header file for sfsasl.c. shmticklib.c Routines for shared memory counters. +sm_resolve.c Routines for DNS lookups (for DNS map type). +sm_resolve.h Header file for sm_resolve.c. srvrsmtp.c Routines to implement server SMTP. stab.c Routines to manage the symbol table. stats.c Routines to collect and post the statistics. @@ -1722,6 +1655,7 @@ sysexits.c List of error messages associated with error codes sysexits.h List of error codes for systems that lack their own. timers.c Routines to provide microtimers. timers.h Data structure and function declarations for timers.h. +tls.c Routines for TLS. trace.c The trace package. These routines allow setting and testing of trace flags with a high granularity. udb.c The user database interface module. @@ -1730,4 +1664,4 @@ util.c Some general purpose routines used by sendmail. version.c The version number and information about this version of sendmail. -(Version $Revision: 1.8 $, last update $Date: 2001/08/21 16:31:45 $ ) +(Version $Revision: 1.9 $, last update $Date: 2001/09/11 19:02:49 $ ) diff --git a/gnu/usr.sbin/sendmail/sendmail/TRACEFLAGS b/gnu/usr.sbin/sendmail/sendmail/TRACEFLAGS index 949592a5833..d21f0d1c1e3 100644 --- a/gnu/usr.sbin/sendmail/sendmail/TRACEFLAGS +++ b/gnu/usr.sbin/sendmail/sendmail/TRACEFLAGS @@ -1,4 +1,4 @@ -# $Sendmail: TRACEFLAGS,v 8.29.16.1 2001/05/03 17:24:00 gshapiro Exp $ +# $Sendmail: TRACEFLAGS,v 8.34 2001/04/25 00:16:04 ca Exp $ 0, 1 main.c main skip background fork 0, 4 main.c main canonical name, UUCP node name, a.k.a.s 0, 15 main.c main print configuration @@ -75,11 +75,14 @@ 62 multiple file descriptor checking 63 queue.c runqueue process watching 64 multiple Milter +65 main.c permission checks +66 srvrsmtp.c conformance checks 67 conf.c signals +69 queue.c scheduling 80 content length 81 sun remote mode 91 mci.c syslogging of MCI cache information -94 srvrsmtp.c cause commands to fail (for protocol testing) +94,>99 srvrsmtp.c cause commands to fail (for protocol testing) 95 srvrsmtp.c AUTH 95 usersmtp.c AUTH 98 * timers diff --git a/gnu/usr.sbin/sendmail/sendmail/alias.c b/gnu/usr.sbin/sendmail/sendmail/alias.c index 50e9624232c..609ae197d89 100644 --- a/gnu/usr.sbin/sendmail/sendmail/alias.c +++ b/gnu/usr.sbin/sendmail/sendmail/alias.c @@ -8,13 +8,12 @@ * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. + * */ #include <sendmail.h> -#ifndef lint -static char id[] = "@(#)$Sendmail: alias.c,v 8.142.4.11 2001/05/03 17:24:01 gshapiro Exp $"; -#endif /* ! lint */ +SM_RCSID("@(#)$Sendmail: alias.c,v 8.203 2001/09/04 22:43:02 ca Exp $") # define SEPARATOR ':' # define ALIAS_SPEC_SEPARATORS " ,/:" @@ -62,7 +61,7 @@ alias(a, sendq, aliaslevel, e) char obuf[MAXNAME + 7]; if (tTd(27, 1)) - dprintf("alias(%s)\n", a->q_user); + sm_dprintf("alias(%s)\n", a->q_user); /* don't realias already aliased names */ if (!QS_IS_OK(a->q_state)) @@ -81,7 +80,6 @@ alias(a, sendq, aliaslevel, e) ** bounce messages inappropriately. */ - #if _FFR_REDIRECTEMPTY /* ** envelope <> can't be sent to mailing lists, only owner- @@ -91,15 +89,14 @@ alias(a, sendq, aliaslevel, e) if (e->e_sender != NULL && *e->e_sender == '\0') { /* Look for owner of alias */ - (void) strlcpy(obuf, "owner-", sizeof obuf); - (void) strlcat(obuf, a->q_user, sizeof obuf); + (void) sm_strlcpyn(obuf, sizeof obuf, 2, "owner-", a->q_user); if (aliaslookup(obuf, &status) != NULL) { if (LogLevel > 8) syslog(LOG_WARNING, "possible spam from <> to list: %s, redirected to %s\n", a->q_user, obuf); - a->q_user = newstr(obuf); + a->q_user = sm_rpool_strdup_x(e->e_rpool, obuf); } } #endif /* _FFR_REDIRECTEMPTY */ @@ -109,7 +106,11 @@ alias(a, sendq, aliaslevel, e) { a->q_state = QS_QUEUEUP; if (e->e_message == NULL) - e->e_message = newstr("alias database unavailable"); + e->e_message = "alias database unavailable"; + + /* XXX msg only per recipient? */ + if (a->q_message == NULL) + a->q_message = "alias database unavailable"; return; } if (p == NULL) @@ -121,8 +122,8 @@ alias(a, sendq, aliaslevel, e) */ if (tTd(27, 1)) - dprintf("%s (%s, %s) aliased to %s\n", - a->q_paddr, a->q_host, a->q_user, p); + sm_dprintf("%s (%s, %s) aliased to %s\n", + a->q_paddr, a->q_host, a->q_user, p); if (bitset(EF_VRFYONLY, e->e_flags)) { a->q_state = QS_VERIFIED; @@ -131,13 +132,13 @@ alias(a, sendq, aliaslevel, e) message("aliased to %s", shortenstring(p, MAXSHORTSTR)); if (LogLevel > 10) sm_syslog(LOG_INFO, e->e_id, - "alias %.100s => %s", - a->q_paddr, shortenstring(p, MAXSHORTSTR)); + "alias %.100s => %s", + a->q_paddr, shortenstring(p, MAXSHORTSTR)); a->q_flags &= ~QSELFREF; if (tTd(27, 5)) { - dprintf("alias: QS_EXPANDED "); - printaddr(a, FALSE); + sm_dprintf("alias: QS_EXPANDED "); + printaddr(a, false); } a->q_state = QS_EXPANDED; @@ -160,12 +161,11 @@ alias(a, sendq, aliaslevel, e) ** Look for owner of alias */ - (void) strlcpy(obuf, "owner-", sizeof obuf); if (strncmp(a->q_user, "owner-", 6) == 0 || - strlen(a->q_user) > (SIZE_T) sizeof obuf - 7) - (void) strlcat(obuf, "owner", sizeof obuf); + strlen(a->q_user) > sizeof obuf - 7) + (void) sm_strlcpy(obuf, "owner-owner", sizeof obuf); else - (void) strlcat(obuf, a->q_user, sizeof obuf); + (void) sm_strlcpyn(obuf, sizeof obuf, 2, "owner-", a->q_user); owner = aliaslookup(obuf, &status); if (owner == NULL) return; @@ -173,12 +173,13 @@ alias(a, sendq, aliaslevel, e) /* reflect owner into envelope sender */ if (strpbrk(owner, ",:/|\"") != NULL) owner = obuf; - a->q_owner = newstr(owner); + a->q_owner = sm_rpool_strdup_x(e->e_rpool, owner); /* announce delivery to this alias; NORECEIPT bit set later */ if (e->e_xfp != NULL) - fprintf(e->e_xfp, "Message delivered to mailing list %s\n", - a->q_paddr); + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + "Message delivered to mailing list %s\n", + a->q_paddr); e->e_flags |= EF_SENDRECEIPT; a->q_flags |= QDELIVERED|QEXPANDED; } @@ -218,7 +219,7 @@ aliaslookup(name, pstat) DYNOPENMAP(map); /* special case POstMastER -- always use lower case */ - if (strcasecmp(name, "postmaster") == 0) + if (sm_strcasecmp(name, "postmaster") == 0) name = "postmaster"; return (*map->map_class->map_lookup)(map, name, NULL, pstat); @@ -245,7 +246,7 @@ setalias(spec) STAB *s; if (tTd(27, 8)) - dprintf("setalias(%s)\n", spec); + sm_dprintf("setalias(%s)\n", spec); for (p = spec; p != NULL; ) { @@ -265,8 +266,8 @@ setalias(spec) } if (AliasFileMap == NULL) { - (void) strlcpy(buf, "aliases.files sequence", - sizeof buf); + (void) sm_strlcpy(buf, "aliases.files sequence", + sizeof buf); AliasFileMap = makemapentry(buf); if (AliasFileMap == NULL) { @@ -274,7 +275,7 @@ setalias(spec) return; } } - (void) snprintf(buf, sizeof buf, "Alias%d", NAliasFileMaps); + (void) sm_snprintf(buf, sizeof buf, "Alias%d", NAliasFileMaps); s = stab(buf, ST_MAP, ST_ENTER); map = &s->s_map; memset(map, '\0', sizeof *map); @@ -296,7 +297,7 @@ setalias(spec) /* find end of spec */ if (p != NULL) { - bool quoted = FALSE; + bool quoted = false; for (; *p != '\0'; p++) { @@ -320,7 +321,7 @@ setalias(spec) *p++ = '\0'; if (tTd(27, 20)) - dprintf(" map %s:%s %s\n", class, s->s_name, spec); + sm_dprintf(" map %s:%s %s\n", class, s->s_name, spec); /* look up class */ s = stab(class, ST_MAPCLASS, ST_FIND); @@ -336,9 +337,10 @@ setalias(spec) else { map->map_class = &s->s_mapclass; + map->map_mflags |= MF_ALIAS; if (map->map_class->map_parse(map, spec)) { - map->map_mflags |= MF_VALID|MF_ALIAS; + map->map_mflags |= MF_VALID; AliasFileMap->map_stack[NAliasFileMaps++] = map; } } @@ -358,8 +360,8 @@ setalias(spec) ** just checking to see if it should be created. ** ** Returns: -** TRUE -- if the database is open when we return. -** FALSE -- if the database is closed when we return. +** true -- if the database is open when we return. +** false -- if the database is closed when we return. */ bool @@ -368,14 +370,14 @@ aliaswait(map, ext, isopen) char *ext; bool isopen; { - bool attimeout = FALSE; + bool attimeout = false; time_t mtime; struct stat stb; char buf[MAXNAME + 1]; if (tTd(27, 3)) - dprintf("aliaswait(%s:%s)\n", - map->map_class->map_cname, map->map_file); + sm_dprintf("aliaswait(%s:%s)\n", + map->map_class->map_cname, map->map_file); if (bitset(MF_ALIASWAIT, map->map_mflags)) return isopen; map->map_mflags |= MF_ALIASWAIT; @@ -383,16 +385,18 @@ aliaswait(map, ext, isopen) if (SafeAlias > 0) { auto int st; - time_t toolong = curtime() + SafeAlias; unsigned int sleeptime = 2; + int loopcount = 0; + time_t toolong = curtime() + SafeAlias; while (isopen && map->map_class->map_lookup(map, "@", NULL, &st) == NULL) { + loopcount++; if (curtime() > toolong) { /* we timed out */ - attimeout = TRUE; + attimeout = true; break; } @@ -402,8 +406,8 @@ aliaswait(map, ext, isopen) */ if (tTd(27, 2)) - dprintf("aliaswait: sleeping for %u seconds\n", - sleeptime); + sm_dprintf("aliaswait: sleeping for %u seconds (loopcount = %d)\n", + sleeptime, loopcount); map->map_mflags |= MF_CLOSING; map->map_class->map_close(map); @@ -420,59 +424,26 @@ aliaswait(map, ext, isopen) if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) { if (tTd(27, 3)) - dprintf("aliaswait: not rebuildable\n"); + sm_dprintf("aliaswait: not rebuildable\n"); map->map_mflags &= ~MF_ALIASWAIT; return isopen; } if (stat(map->map_file, &stb) < 0) { if (tTd(27, 3)) - dprintf("aliaswait: no source file\n"); + sm_dprintf("aliaswait: no source file\n"); map->map_mflags &= ~MF_ALIASWAIT; return isopen; } mtime = stb.st_mtime; - snprintf(buf, sizeof buf, "%s%s", - map->map_file, ext == NULL ? "" : ext); + (void) sm_strlcpyn(buf, sizeof buf, 2, + map->map_file, ext == NULL ? "" : ext); if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout) { -#if !_FFR_REMOVE_AUTOREBUILD - /* database is out of date */ - if (AutoRebuild && - stb.st_ino != 0 && - (stb.st_uid == geteuid() || - (geteuid() == 0 && stb.st_uid == TrustedUid))) - { - bool oldSuprErrs; - - message("auto-rebuilding alias database %s", buf); - oldSuprErrs = SuprErrs; - SuprErrs = TRUE; - if (isopen) - { - map->map_mflags |= MF_CLOSING; - map->map_class->map_close(map); - map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); - } - (void) rebuildaliases(map, TRUE); - isopen = map->map_class->map_open(map, O_RDONLY); - SuprErrs = oldSuprErrs; - } - else - { - if (LogLevel > 3) - sm_syslog(LOG_INFO, NOQID, - "alias database %s out of date", - buf); - message("Warning: alias database %s out of date", buf); - } -#else /* !_FFR_REMOVE_AUTOREBUILD */ if (LogLevel > 3) sm_syslog(LOG_INFO, NOQID, - "alias database %s out of date", - buf); + "alias database %s out of date", buf); message("Warning: alias database %s out of date", buf); -#endif /* !_FFR_REMOVE_AUTOREBUILD */ } map->map_mflags &= ~MF_ALIASWAIT; return isopen; @@ -485,7 +456,7 @@ aliaswait(map, ext, isopen) ** automatic -- set if this was automatically generated. ** ** Returns: -** TRUE if successful; FALSE otherwise. +** true if successful; false otherwise. ** ** Side Effects: ** Reads the text version of the database, builds the @@ -497,9 +468,9 @@ rebuildaliases(map, automatic) register MAP *map; bool automatic; { - FILE *af; - bool nolock = FALSE; - bool success = FALSE; + SM_FILE_T *af; + bool nolock = false; + bool success = false; long sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK; sigfunc_t oldsigint, oldsigquit; #ifdef SIGTSTP @@ -507,7 +478,7 @@ rebuildaliases(map, automatic) #endif /* SIGTSTP */ if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) - return FALSE; + return false; if (!bitnset(DBS_LINKEDALIASFILEINWRITABLEDIR, DontBlameSendmail)) sff |= SFF_NOWLINK; @@ -527,25 +498,26 @@ rebuildaliases(map, automatic) int saveerr = errno; if (tTd(27, 1)) - dprintf("Can't open %s: %s\n", - map->map_file, errstring(saveerr)); + sm_dprintf("Can't open %s: %s\n", + map->map_file, sm_errstring(saveerr)); if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags)) message("newaliases: cannot open %s: %s", - map->map_file, errstring(saveerr)); + map->map_file, sm_errstring(saveerr)); errno = 0; - return FALSE; + return false; } - nolock = TRUE; + nolock = true; if (tTd(27, 1) || - fstat(fileno(af), &stb) < 0 || + fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &stb) < 0 || bitset(S_IWUSR|S_IWGRP|S_IWOTH, stb.st_mode)) message("warning: cannot lock %s: %s", - map->map_file, errstring(errno)); + map->map_file, sm_errstring(errno)); } /* see if someone else is rebuilding the alias file */ if (!nolock && - !lockfile(fileno(af), map->map_file, NULL, LOCK_EX|LOCK_NB)) + !lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), map->map_file, + NULL, LOCK_EX|LOCK_NB)) { /* yes, they are -- wait until done */ message("Alias file %s is locked (maybe being rebuilt)", @@ -553,18 +525,18 @@ rebuildaliases(map, automatic) if (OpMode != MD_INITALIAS) { /* wait for other rebuild to complete */ - (void) lockfile(fileno(af), map->map_file, NULL, - LOCK_EX); + (void) lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), + map->map_file, NULL, LOCK_EX); } - (void) fclose(af); + (void) sm_io_close(af, SM_TIME_DEFAULT); errno = 0; - return FALSE; + return false; } - oldsigint = setsignal(SIGINT, SIG_IGN); - oldsigquit = setsignal(SIGQUIT, SIG_IGN); + oldsigint = sm_signal(SIGINT, SIG_IGN); + oldsigquit = sm_signal(SIGQUIT, SIG_IGN); #ifdef SIGTSTP - oldsigtstp = setsignal(SIGTSTP, SIG_IGN); + oldsigtstp = sm_signal(SIGTSTP, SIG_IGN); #endif /* SIGTSTP */ if (map->map_class->map_open(map, O_RDWR)) @@ -577,22 +549,22 @@ rebuildaliases(map, automatic) username()); } map->map_mflags |= MF_OPEN|MF_WRITABLE; - map->map_pid = getpid(); - readaliases(map, af, !automatic, TRUE); - success = TRUE; + map->map_pid = CurrentPid; + readaliases(map, af, !automatic, true); + success = true; } else { if (tTd(27, 1)) - dprintf("Can't create database for %s: %s\n", - map->map_file, errstring(errno)); + sm_dprintf("Can't create database for %s: %s\n", + map->map_file, sm_errstring(errno)); if (!automatic) syserr("Cannot create database for alias file %s", map->map_file); } /* close the file, thus releasing locks */ - (void) fclose(af); + (void) sm_io_close(af, SM_TIME_DEFAULT); /* add distinguished entries and close the database */ if (bitset(MF_OPEN, map->map_mflags)) @@ -603,10 +575,10 @@ rebuildaliases(map, automatic) } /* restore the old signals */ - (void) setsignal(SIGINT, oldsigint); - (void) setsignal(SIGQUIT, oldsigquit); + (void) sm_signal(SIGINT, oldsigint); + (void) sm_signal(SIGQUIT, oldsigquit); # ifdef SIGTSTP - (void) setsignal(SIGTSTP, oldsigtstp); + (void) sm_signal(SIGTSTP, oldsigtstp); # endif /* SIGTSTP */ return success; } @@ -634,7 +606,7 @@ rebuildaliases(map, automatic) void readaliases(map, af, announcestats, logstats) register MAP *map; - FILE *af; + SM_FILE_T *af; bool announcestats; bool logstats; { @@ -652,52 +624,56 @@ readaliases(map, af, announcestats, logstats) FileName = map->map_file; LineNumber = 0; naliases = bytes = longest = 0; - skipping = FALSE; - while (fgets(line, sizeof line, af) != NULL) + skipping = false; + while (sm_io_fgets(af, SM_TIME_DEFAULT, line, sizeof line) != NULL) { int lhssize, rhssize; int c; LineNumber++; p = strchr(line, '\n'); + + /* XXX what if line="a\\" ? */ while (p != NULL && p > line && p[-1] == '\\') { p--; - if (fgets(p, SPACELEFT(line, p), af) == NULL) + if (sm_io_fgets(af, SM_TIME_DEFAULT, p, + SPACELEFT(line, p)) == NULL) break; LineNumber++; p = strchr(p, '\n'); } if (p != NULL) *p = '\0'; - else if (!feof(af)) + else if (!sm_io_eof(af)) { errno = 0; syserr("554 5.3.0 alias line too long"); /* flush to end of line */ - while ((c = getc(af)) != EOF && c != '\n') + while ((c = sm_io_getc(af, SM_TIME_DEFAULT)) != + SM_IO_EOF && c != '\n') continue; /* skip any continuation lines */ - skipping = TRUE; + skipping = true; continue; } switch (line[0]) { case '#': case '\0': - skipping = FALSE; + skipping = false; continue; case ' ': case '\t': if (!skipping) syserr("554 5.3.5 Non-continuation line starts with space"); - skipping = TRUE; + skipping = true; continue; } - skipping = FALSE; + skipping = false; /* ** Process the LHS @@ -715,7 +691,8 @@ readaliases(map, af, announcestats, logstats) syserr("554 5.3.5 missing colon"); continue; } - if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv) == NULL) + if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv, true) + == NULL) { syserr("554 5.3.5 %.40s... illegal alias name", line); continue; @@ -751,7 +728,8 @@ readaliases(map, af, announcestats, logstats) if (*p == '\0') break; if (parseaddr(p, &bl, RF_COPYNONE, ',', - &delimptr, CurEnv) == NULL) + &delimptr, CurEnv, true) + == NULL) usrerr("553 5.3.5 %s... bad address", p); p = delimptr; } @@ -762,24 +740,26 @@ readaliases(map, af, announcestats, logstats) } /* see if there should be a continuation line */ - c = getc(af); - if (!feof(af)) - (void) ungetc(c, af); + c = sm_io_getc(af, SM_TIME_DEFAULT); + if (!sm_io_eof(af)) + (void) sm_io_ungetc(af, SM_TIME_DEFAULT, c); if (c != ' ' && c != '\t') break; /* read continuation line */ - if (fgets(p, sizeof line - (p - line), af) == NULL) + if (sm_io_fgets(af, SM_TIME_DEFAULT, p, + sizeof line - (p-line)) == NULL) break; LineNumber++; /* check for line overflow */ - if (strchr(p, '\n') == NULL && !feof(af)) + if (strchr(p, '\n') == NULL && !sm_io_eof(af)) { usrerr("554 5.3.5 alias too long"); - while ((c = fgetc(af)) != EOF && c != '\n') + while ((c = sm_io_getc(af, SM_TIME_DEFAULT)) + != SM_IO_EOF && c != '\n') continue; - skipping = TRUE; + skipping = true; break; } } @@ -800,7 +780,7 @@ readaliases(map, af, announcestats, logstats) ** Special case pOStmaStER -- always make it lower case. */ - if (strcasecmp(al.q_user, "postmaster") == 0) + if (sm_strcasecmp(al.q_user, "postmaster") == 0) makelower(al.q_user); lhssize = strlen(al.q_user); @@ -829,12 +809,18 @@ readaliases(map, af, announcestats, logstats) longest = rhssize; } +#if 0 + /* + ** address strings are now stored in the envelope rpool, + ** and therefore cannot be freed. + */ if (al.q_paddr != NULL) - sm_free(al.q_paddr); + sm_free(al.q_paddr); /* disabled */ if (al.q_host != NULL) - sm_free(al.q_host); + sm_free(al.q_host); /* disabled */ if (al.q_user != NULL) - sm_free(al.q_user); + sm_free(al.q_user); /* disabled */ +#endif /* 0 */ } CurEnv->e_to = NULL; @@ -881,11 +867,13 @@ forward(user, sendq, aliaslevel, e) bool got_transient; if (tTd(27, 1)) - dprintf("forward(%s)\n", user->q_paddr); + sm_dprintf("forward(%s)\n", user->q_paddr); if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) || !QS_IS_OK(user->q_state)) return; + if (ForwardPath != NULL && *ForwardPath == '\0') + return; if (user->q_home == NULL) { syserr("554 5.3.0 forward: no home"); @@ -893,13 +881,13 @@ forward(user, sendq, aliaslevel, e) } /* good address -- look for .forward file in home */ - define('z', user->q_home, e); - define('u', user->q_user, e); - define('h', user->q_host, e); + macdefine(&e->e_macro, A_PERM, 'z', user->q_home); + macdefine(&e->e_macro, A_PERM, 'u', user->q_user); + macdefine(&e->e_macro, A_PERM, 'h', user->q_host); if (ForwardPath == NULL) ForwardPath = newstr("\201z/.forward"); - got_transient = FALSE; + got_transient = false; for (pp = ForwardPath; pp != NULL; pp = ep) { int err; @@ -915,18 +903,18 @@ forward(user, sendq, aliaslevel, e) if (buf[0] == '\0') continue; if (tTd(27, 3)) - dprintf("forward: trying %s\n", buf); + sm_dprintf("forward: trying %s\n", buf); - err = include(buf, TRUE, user, sendq, aliaslevel, e); + err = include(buf, true, user, sendq, aliaslevel, e); if (err == 0) break; else if (transienterror(err)) { /* we may have to suspend this message */ - got_transient = TRUE; + got_transient = true; if (tTd(27, 2)) - dprintf("forward: transient error on %s\n", - buf); + sm_dprintf("forward: transient error on %s\n", + buf); if (LogLevel > 2) { char *curhost = CurHostName; @@ -934,7 +922,7 @@ forward(user, sendq, aliaslevel, e) CurHostName = NULL; sm_syslog(LOG_ERR, e->e_id, "forward %s: transient error: %s", - buf, errstring(err)); + buf, sm_errstring(err)); CurHostName = curhost; } @@ -964,19 +952,18 @@ forward(user, sendq, aliaslevel, e) case E_SM_ISEXEC: case E_SM_WWFILE: case E_SM_GWFILE: - syserr("forward: %s: %s", buf, errstring(err)); + syserr("forward: %s: %s", buf, sm_errstring(err)); break; #endif /* _FFR_FORWARD_SYSERR */ default: if (LogLevel > (RunAsUid == 0 ? 2 : 10)) sm_syslog(LOG_WARNING, e->e_id, - "forward %s: %s", buf, - errstring(err)); + "forward %s: %s", buf, + sm_errstring(err)); if (Verbose) message("forward: %s: %s", - buf, - errstring(err)); + buf, sm_errstring(err)); break; } } diff --git a/gnu/usr.sbin/sendmail/sendmail/aliases b/gnu/usr.sbin/sendmail/sendmail/aliases index be9e97d7943..34db4c64452 100644 --- a/gnu/usr.sbin/sendmail/sendmail/aliases +++ b/gnu/usr.sbin/sendmail/sendmail/aliases @@ -1,5 +1,5 @@ # -# $Sendmail: aliases,v 8.1.36.1 2000/10/16 20:18:39 gshapiro Exp $ +# $Sendmail: aliases,v 8.2 2000/10/16 20:20:36 gshapiro Exp $ # @(#)aliases 8.2 (Berkeley) 3/5/94 # # Aliases in this file will NOT be expanded in the header from diff --git a/gnu/usr.sbin/sendmail/sendmail/aliases.5 b/gnu/usr.sbin/sendmail/sendmail/aliases.5 index 03ea14b2b95..90637c6a1db 100644 --- a/gnu/usr.sbin/sendmail/sendmail/aliases.5 +++ b/gnu/usr.sbin/sendmail/sendmail/aliases.5 @@ -9,7 +9,7 @@ .\" the sendmail distribution. .\" .\" -.\" $Sendmail: aliases.5,v 8.15.4.2 2000/12/14 23:08:15 gshapiro Exp $ +.\" $Sendmail: aliases.5,v 8.17 2000/12/14 23:09:46 gshapiro Exp $ .\" .Dd December 14, 2000 .Dt ALIASES 5 diff --git a/gnu/usr.sbin/sendmail/sendmail/arpadate.c b/gnu/usr.sbin/sendmail/sendmail/arpadate.c index ec0eafdb355..c984f3ad125 100644 --- a/gnu/usr.sbin/sendmail/sendmail/arpadate.c +++ b/gnu/usr.sbin/sendmail/sendmail/arpadate.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 1999, 2001 Sendmail, Inc. and its suppliers. + * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 @@ -11,12 +11,10 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: arpadate.c,v 8.23.20.2 2001/05/07 22:07:26 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +SM_RCSID("@(#)$Sendmail: arpadate.c,v 8.28 2001/02/14 14:45:47 ca Exp $") + /* ** ARPADATE -- Create date in ARPANET format ** @@ -74,6 +72,7 @@ arpadate(ud) ** to resolve the timezone. */ + /* SM_REQUIRE(ud == NULL || strlen(ud) >= 23); */ t = curtime(); if (ud == NULL) ud = ctime(&t); diff --git a/gnu/usr.sbin/sendmail/sendmail/bf.h b/gnu/usr.sbin/sendmail/sendmail/bf.h index dc63fd2082f..b95fef6628b 100644 --- a/gnu/usr.sbin/sendmail/sendmail/bf.h +++ b/gnu/usr.sbin/sendmail/sendmail/bf.h @@ -1,27 +1,33 @@ /* - * Copyright (c) 1999, 2001 Sendmail, Inc. and its suppliers. + * Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * - * $Sendmail: bf.h,v 8.5.16.2 2001/02/14 04:07:27 gshapiro Exp $ + * $Sendmail: bf.h,v 8.15 2001/05/31 21:02:53 ca Exp $ * * Contributed by Exactis.com, Inc. * */ #ifndef BF_H -#define BF_H 1 +# define BF_H 1 -extern FILE *bfopen __P((char *, int, size_t, long)); -extern FILE *bfdup __P((FILE *)); -extern int bfcommit __P((FILE *)); -extern int bfrewind __P((FILE *)); -extern int bftruncate __P((FILE *)); -extern int bffsync __P((FILE *)); -extern int bfclose __P((FILE *)); -extern bool bftest __P((FILE *)); +extern SM_FILE_T *bfopen __P((char *, MODE_T, size_t, long)); +extern SM_FILE_T *bfdup __P((SM_FILE_T *)); +extern int bfcommit __P((SM_FILE_T *)); +extern int bfrewind __P((SM_FILE_T *)); +extern int bftruncate __P((SM_FILE_T *)); +extern int bfclose __P((SM_FILE_T *)); +extern bool bftest __P((SM_FILE_T *)); -#endif /* BF_H */ +/* "what" flags for sm_io_setinfo() for the SM_FILE_TYPE file type */ +# define SM_BF_SETBUFSIZE 1000 /* set buffer size */ +# define SM_BF_COMMIT 1001 /* commit file to disk */ +# define SM_BF_TRUNCATE 1002 /* truncate the file */ +# define SM_BF_TEST 1003 /* historical support; temp */ + +# define BF_FILE_TYPE "SendmailBufferedFile" +#endif /* ! BF_H */ diff --git a/gnu/usr.sbin/sendmail/sendmail/collect.c b/gnu/usr.sbin/sendmail/sendmail/collect.c index 64bfc1ddd9f..8471ef42b8c 100644 --- a/gnu/usr.sbin/sendmail/sendmail/collect.c +++ b/gnu/usr.sbin/sendmail/sendmail/collect.c @@ -11,16 +11,231 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: collect.c,v 8.136.4.22 2001/06/07 21:01:02 ca Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +SM_RCSID("@(#)$Sendmail: collect.c,v 8.228 2001/09/04 22:43:02 ca Exp $") static void collecttimeout __P((time_t)); -static void dferror __P((FILE *volatile, char *, ENVELOPE *)); +static void dferror __P((SM_FILE_T *volatile, char *, ENVELOPE *)); static void eatfrom __P((char *volatile, ENVELOPE *)); +static void collect_doheader __P((ENVELOPE *)); +static SM_FILE_T *collect_dfopen __P((ENVELOPE *)); +static SM_FILE_T *collect_eoh __P((ENVELOPE *, int, int)); + +/* +** COLLECT_EOH -- end-of-header processing in collect() +** +** Called by collect() when it encounters the blank line +** separating the header from the message body, or when it +** encounters EOF in a message that contains only a header. +** +** Parameters: +** e -- envelope +** numhdrs -- number of headers +** hdrslen -- length of headers +** +** Results: +** NULL, or handle to open data file +** +** Side Effects: +** end-of-header check ruleset is invoked. +** envelope state is updated. +** headers may be added and deleted. +** selects the queue. +** opens the data file. +*/ + +static SM_FILE_T * +collect_eoh(e, numhdrs, hdrslen) + ENVELOPE *e; + int numhdrs; + int hdrslen; +{ + char hnum[16]; + char hsize[16]; + + /* call the end-of-header check ruleset */ + (void) sm_snprintf(hnum, sizeof hnum, "%d", numhdrs); + (void) sm_snprintf(hsize, sizeof hsize, "%d", hdrslen); + if (tTd(30, 10)) + sm_dprintf("collect: rscheck(\"check_eoh\", \"%s $| %s\")\n", + hnum, hsize); + (void) rscheck("check_eoh", hnum, hsize, e, false, true, 3, NULL, + e->e_id); + + /* + ** Process the header, + ** select the queue, open the data file. + */ + + collect_doheader(e); + return collect_dfopen(e); +} + +/* +** COLLECT_DOHEADER -- process header in collect() +** +** Called by collect() after it has finished parsing the header, +** but before it selects the queue and creates the data file. +** The results of processing the header will affect queue selection. +** +** Parameters: +** e -- envelope +** +** Results: +** none. +** +** Side Effects: +** envelope state is updated. +** headers may be added and deleted. +*/ + +static void +collect_doheader(e) + ENVELOPE *e; +{ + /* + ** Find out some information from the headers. + ** Examples are who is the from person & the date. + */ + + eatheader(e, true, false); + + if (GrabTo && e->e_sendqueue == NULL) + usrerr("No recipient addresses found in header"); + + /* collect statistics */ + if (OpMode != MD_VERIFY) + markstats(e, (ADDRESS *) NULL, false); + + /* + ** If we have a Return-Receipt-To:, turn it into a DSN. + */ + + if (RrtImpliesDsn && hvalue("return-receipt-to", e->e_header) != NULL) + { + ADDRESS *q; + + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + if (!bitset(QHASNOTIFY, q->q_flags)) + q->q_flags |= QHASNOTIFY|QPINGONSUCCESS; + } + + /* + ** Add an appropriate recipient line if we have none. + */ + + if (hvalue("to", e->e_header) != NULL || + hvalue("cc", e->e_header) != NULL || + hvalue("apparently-to", e->e_header) != NULL) + { + /* have a valid recipient header -- delete Bcc: headers */ + e->e_flags |= EF_DELETE_BCC; + } + else if (hvalue("bcc", e->e_header) == NULL) + { + /* no valid recipient headers */ + register ADDRESS *q; + char *hdr = NULL; + + /* create a recipient field */ + switch (NoRecipientAction) + { + case NRA_ADD_APPARENTLY_TO: + hdr = "Apparently-To"; + break; + + case NRA_ADD_TO: + hdr = "To"; + break; + + case NRA_ADD_BCC: + addheader("Bcc", " ", 0, e); + break; + + case NRA_ADD_TO_UNDISCLOSED: + addheader("To", "undisclosed-recipients:;", 0, e); + break; + } + + if (hdr != NULL) + { + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (q->q_alias != NULL) + continue; + if (tTd(30, 3)) + sm_dprintf("Adding %s: %s\n", + hdr, q->q_paddr); + addheader(hdr, q->q_paddr, 0, e); + } + } + } +} + +/* +** COLLECT_DFOPEN -- open the message data file +** +** Called by collect() after it has finished processing the header. +** Queue selection occurs at this point, possibly based on the +** envelope's recipient list and on header information. +** +** Parameters: +** e -- envelope +** +** Results: +** NULL, or a pointer to an open data file, +** into which the message body will be written by collect(). +** +** Side Effects: +** Calls syserr, sets EF_FATALERRS and returns NULL +** if there is insufficient disk space. +** Aborts process if data file could not be opened. +** Otherwise, the queue is selected, +** e->e_{dfino,dfdev,msgsize,flags} are updated, +** and a pointer to an open data file is returned. +*/ + +static SM_FILE_T * +collect_dfopen(e) + ENVELOPE *e; +{ + MODE_T oldumask = 0; + int dfd; + struct stat stbuf; + SM_FILE_T *df; + char *dfname; + + if (!setnewqueue(e)) + return NULL; + + dfname = queuename(e, 'd'); + if (bitset(S_IWGRP, QueueFileMode)) + oldumask = umask(002); + df = bfopen(dfname, QueueFileMode, DataFileBufferSize, + SFF_OPENASROOT); + if (bitset(S_IWGRP, QueueFileMode)) + (void) umask(oldumask); + if (df == NULL) + { + syserr("@Cannot create %s", dfname); + e->e_flags |= EF_NO_BODY_RETN; + flush_errors(true); + finis(true, ExitStat); + /* NOTREACHED */ + } + dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL); + if (dfd < 0 || fstat(dfd, &stbuf) < 0) + e->e_dfino = -1; + else + { + e->e_dfdev = stbuf.st_dev; + e->e_dfino = stbuf.st_ino; + } + e->e_msgsize = 0; + e->e_flags |= EF_HAS_DF; + return df; +} /* ** COLLECT -- read & parse message header & make temp file. @@ -42,13 +257,20 @@ static void eatfrom __P((char *volatile, ENVELOPE *)); ** none. ** ** Side Effects: -** Temp file is created and filled. -** The from person may be set. +** If successful, +** - Data file is created and filled, and e->e_dfp is set. +** - The from person may be set. +** If the "enough disk space" check fails, +** - syserr is called. +** - e->e_dfp is NULL. +** - e->e_flags & EF_FATALERRS is set. +** - collect() returns. +** If data file cannot be created, the process is terminated. */ static jmp_buf CtxCollectTimeout; static bool volatile CollectProgress; -static EVENT *volatile CollectTimeout = NULL; +static SM_EVENT *volatile CollectTimeout = NULL; /* values for input state machine */ #define IS_NORM 0 /* middle of line */ @@ -65,83 +287,44 @@ static EVENT *volatile CollectTimeout = NULL; void collect(fp, smtpmode, hdrp, e) - FILE *fp; + SM_FILE_T *fp; bool smtpmode; HDR **hdrp; register ENVELOPE *e; { - register FILE *volatile df; - volatile bool ignrdot = smtpmode ? FALSE : IgnrDot; - volatile time_t dbto = smtpmode ? TimeOuts.to_datablock : 0; + register SM_FILE_T *volatile df; + volatile bool ignrdot; + volatile time_t dbto; register char *volatile bp; - volatile int c = EOF; - volatile bool inputerr = FALSE; + volatile int c; + volatile bool inputerr; bool headeronly; char *volatile buf; volatile int buflen; volatile int istate; volatile int mstate; - volatile int hdrslen = 0; - volatile int numhdrs = 0; - volatile int dfd; - volatile int rstat = EX_OK; - u_char *volatile pbp; - u_char peekbuf[8]; - char hsize[16]; - char hnum[16]; - char dfname[MAXPATHLEN]; + volatile int hdrslen; + volatile int numhdrs; + volatile int afd; + unsigned char *volatile pbp; + unsigned char peekbuf[8]; char bufbuf[MAXLINE]; + df = NULL; + ignrdot = smtpmode ? false : IgnrDot; + dbto = smtpmode ? TimeOuts.to_datablock : 0; + c = SM_IO_EOF; + inputerr = false; headeronly = hdrp != NULL; - - /* - ** Create the temp file name and create the file. - */ - - if (!headeronly) - { - struct stat stbuf; - long sff = SFF_OPENASROOT; - - - (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname); -#if _FFR_QUEUE_FILE_MODE - { - MODE_T oldumask; - - if (bitset(S_IWGRP, QueueFileMode)) - oldumask = umask(002); - df = bfopen(dfname, QueueFileMode, - DataFileBufferSize, sff); - if (bitset(S_IWGRP, QueueFileMode)) - (void) umask(oldumask); - } -#else /* _FFR_QUEUE_FILE_MODE */ - df = bfopen(dfname, FileMode, DataFileBufferSize, sff); -#endif /* _FFR_QUEUE_FILE_MODE */ - if (df == NULL) - { - HoldErrs = FALSE; - if (smtpmode) - syserr("421 4.3.5 Unable to create data file"); - else - syserr("Cannot create %s", dfname); - e->e_flags |= EF_NO_BODY_RETN; - finis(TRUE, ExitStat); - /* NOTREACHED */ - } - dfd = fileno(df); - if (dfd < 0 || fstat(dfd, &stbuf) < 0) - e->e_dfino = -1; - else - { - e->e_dfdev = stbuf.st_dev; - e->e_dfino = stbuf.st_ino; - } - HasEightBits = FALSE; - e->e_msgsize = 0; - e->e_flags |= EF_HAS_DF; - } + hdrslen = 0; + numhdrs = 0; + HasEightBits = false; + buf = bp = bufbuf; + buflen = sizeof bufbuf; + pbp = peekbuf; + istate = IS_BOL; + mstate = SaveFrom ? MS_HEADER : MS_UFROM; + CollectProgress = false; /* ** Tell ARPANET to go ahead. @@ -151,7 +334,7 @@ collect(fp, smtpmode, hdrp, e) message("354 Enter mail, end with \".\" on a line by itself"); if (tTd(30, 2)) - dprintf("collect\n"); + sm_dprintf("collect\n"); /* ** Read the message. @@ -162,13 +345,6 @@ collect(fp, smtpmode, hdrp, e) ** the larger picture (e.g., header versus body). */ - buf = bp = bufbuf; - buflen = sizeof bufbuf; - pbp = peekbuf; - istate = IS_BOL; - mstate = SaveFrom ? MS_HEADER : MS_UFROM; - CollectProgress = FALSE; - if (dbto != 0) { /* handle possible input timeout */ @@ -176,52 +352,56 @@ collect(fp, smtpmode, hdrp, e) { if (LogLevel > 2) sm_syslog(LOG_NOTICE, e->e_id, - "timeout waiting for input from %s during message collect", - CurHostName ? CurHostName : "<local machine>"); + "timeout waiting for input from %s during message collect", + CURHOSTNAME); errno = 0; usrerr("451 4.4.1 timeout waiting for input during message collect"); goto readerr; } - CollectTimeout = setevent(dbto, collecttimeout, dbto); + CollectTimeout = sm_setevent(dbto, collecttimeout, dbto); } for (;;) { if (tTd(30, 35)) - dprintf("top, istate=%d, mstate=%d\n", istate, mstate); + sm_dprintf("top, istate=%d, mstate=%d\n", istate, + mstate); for (;;) { if (pbp > peekbuf) c = *--pbp; else { - while (!feof(fp) && !ferror(fp)) + while (!sm_io_eof(fp) && !sm_io_error(fp)) { errno = 0; - c = getc(fp); - - if (c == EOF && errno == EINTR) + c = sm_io_getc(fp, SM_TIME_DEFAULT); + if (c == SM_IO_EOF && errno == EINTR) { /* Interrupted, retry */ - clearerr(fp); + sm_io_clearerr(fp); continue; } break; } - CollectProgress = TRUE; + CollectProgress = true; if (TrafficLogFile != NULL && !headeronly) { if (istate == IS_BOL) - (void) fprintf(TrafficLogFile, - "%05d <<< ", - (int) getpid()); - if (c == EOF) - (void) fprintf(TrafficLogFile, - "[EOF]\n"); + (void) sm_io_fprintf(TrafficLogFile, + SM_TIME_DEFAULT, + "%05d <<< ", + (int) CurrentPid); + if (c == SM_IO_EOF) + (void) sm_io_fprintf(TrafficLogFile, + SM_TIME_DEFAULT, + "[EOF]\n"); else - (void) putc(c, TrafficLogFile); + (void) sm_io_putc(TrafficLogFile, + SM_TIME_DEFAULT, + c); } - if (c == EOF) + if (c == SM_IO_EOF) goto readerr; if (SevenBitInput) c &= 0x7f; @@ -229,7 +409,7 @@ collect(fp, smtpmode, hdrp, e) HasEightBits |= bitset(0x80, c); } if (tTd(30, 94)) - dprintf("istate=%d, c=%c (0x%x)\n", + sm_dprintf("istate=%d, c=%c (0x%x)\n", istate, (char) c, c); switch (istate) { @@ -278,7 +458,8 @@ collect(fp, smtpmode, hdrp, e) istate = IS_BOL; else { - (void) ungetc(c, fp); + (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, + c); c = '\r'; istate = IS_NORM; } @@ -290,7 +471,8 @@ collect(fp, smtpmode, hdrp, e) istate = IS_CR; continue; } - else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags)) + else if (c == '\n' && !bitset(EF_NL_NOT_EOL, + e->e_flags)) istate = IS_BOL; else istate = IS_NORM; @@ -313,7 +495,9 @@ bufferchar: case MS_BODY: /* just put the character out */ if (!bitset(EF_TOOBIG, e->e_flags)) - (void) putc(c, df); + (void) sm_io_putc(df, SM_TIME_DEFAULT, + c); + /* FALLTHROUGH */ case MS_DISCARD: @@ -338,11 +522,11 @@ bufferchar: memmove(buf, obuf, bp - obuf); bp = &buf[bp - obuf]; if (obuf != bufbuf) - sm_free(obuf); + sm_free(obuf); /* XXX */ } if (c >= 0200 && c <= 0237) { -#if 0 /* causes complaints -- figure out something for 8.11 */ +#if 0 /* causes complaints -- figure out something for 8.n+1 */ usrerr("Illegal character 0x%x in header", c); #else /* 0 */ /* EMPTY */ @@ -351,7 +535,7 @@ bufferchar: else if (c != '\0') { *bp++ = c; - hdrslen++; + ++hdrslen; if (!headeronly && MaxHeadersLength > 0 && hdrslen > MaxHeadersLength) @@ -359,7 +543,7 @@ bufferchar: sm_syslog(LOG_NOTICE, e->e_id, "headers too large (%d max) from %s during message collect", MaxHeadersLength, - CurHostName != NULL ? CurHostName : "localhost"); + CURHOSTNAME); errno = 0; e->e_flags |= EF_CLRQUEUE; e->e_status = "5.6.0"; @@ -376,7 +560,7 @@ bufferchar: nextstate: if (tTd(30, 35)) - dprintf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n", + sm_dprintf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n", istate, mstate, buf); switch (mstate) { @@ -402,12 +586,12 @@ nextstate: /* check for possible continuation line */ do { - clearerr(fp); + sm_io_clearerr(fp); errno = 0; - c = getc(fp); - } while (c == EOF && errno == EINTR); - if (c != EOF) - (void) ungetc(c, fp); + c = sm_io_getc(fp, SM_TIME_DEFAULT); + } while (c == SM_IO_EOF && errno == EINTR); + if (c != SM_IO_EOF) + (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c); if (c == ' ' || c == '\t') { /* yep -- defer this */ @@ -431,19 +615,14 @@ nextstate: case MS_BODY: if (tTd(30, 1)) - dprintf("EOH\n"); + sm_dprintf("EOH\n"); if (headeronly) goto readerr; - /* call the end-of-header check ruleset */ - snprintf(hnum, sizeof hnum, "%d", numhdrs); - snprintf(hsize, sizeof hsize, "%d", hdrslen); - if (tTd(30, 10)) - dprintf("collect: rscheck(\"check_eoh\", \"%s $| %s\")\n", - hnum, hsize); - rstat = rscheck("check_eoh", hnum, hsize, e, FALSE, - TRUE, 4, NULL); + df = collect_eoh(e, numhdrs, hdrslen); + if (df == NULL) + e->e_flags |= EF_TOOBIG; bp = buf; @@ -460,7 +639,8 @@ nextstate: if (!bitset(EF_TOOBIG, e->e_flags)) { while (*bp != '\0') - (void) putc(*bp++, df); + (void) sm_io_putc(df, SM_TIME_DEFAULT, + *bp++); } break; } @@ -468,48 +648,54 @@ nextstate: } readerr: - if ((feof(fp) && smtpmode) || ferror(fp)) + if ((sm_io_eof(fp) && smtpmode) || sm_io_error(fp)) { const char *errmsg; - if (feof(fp)) + if (sm_io_eof(fp)) errmsg = "unexpected close"; else - errmsg = errstring(errno); - + errmsg = sm_errstring(errno); if (tTd(30, 1)) - dprintf("collect: premature EOM: %s\n", errmsg); - if (LogLevel >= 2) + sm_dprintf("collect: premature EOM: %s\n", errmsg); + if (LogLevel > 1) sm_syslog(LOG_WARNING, e->e_id, "collect: premature EOM: %s", errmsg); - inputerr = TRUE; + inputerr = true; } /* reset global timer */ if (CollectTimeout != NULL) - clrevent(CollectTimeout); + sm_clrevent(CollectTimeout); if (headeronly) return; + if (mstate != MS_BODY) + { + /* no body or discard, so we never opened the data file */ + SM_ASSERT(df == NULL); + df = collect_eoh(e, numhdrs, hdrslen); + } + if (df == NULL) { /* skip next few clauses */ /* EMPTY */ } - else if (fflush(df) != 0 || ferror(df)) + else if (sm_io_flush(df, SM_TIME_DEFAULT) != 0 || sm_io_error(df)) { - dferror(df, "fflush||ferror", e); - flush_errors(TRUE); - finis(TRUE, ExitStat); + dferror(df, "sm_io_flush||sm_io_error", e); + flush_errors(true); + finis(true, ExitStat); /* NOTREACHED */ } - else if (!SuperSafe) + else if (SuperSafe != SAFE_REALLY) { /* skip next few clauses */ /* EMPTY */ } - else if (bfcommit(df) < 0) + else if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0 && errno != EINVAL) { int save_errno = errno; @@ -517,40 +703,45 @@ readerr: { char *dfile; struct stat st; + int dfd; dfile = queuename(e, 'd'); if (stat(dfile, &st) < 0) st.st_size = -1; errno = EEXIST; - syserr("collect: bfcommit(%s): already on disk, size = %ld", + syserr("@collect: bfcommit(%s): already on disk, size = %ld", dfile, (long) st.st_size); - dfd = fileno(df); + dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL); if (dfd >= 0) - dumpfd(dfd, TRUE, TRUE); + dumpfd(dfd, true, true); } errno = save_errno; dferror(df, "bfcommit", e); - flush_errors(TRUE); + flush_errors(true); finis(save_errno != EEXIST, ExitStat); } - else if (bffsync(df) < 0) + else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) >= 0 && + fsync(afd) < 0) { - dferror(df, "bffsync", e); - flush_errors(TRUE); - finis(TRUE, ExitStat); + dferror(df, "fsync", e); + flush_errors(true); + finis(true, ExitStat); /* NOTREACHED */ } - else if (bfclose(df) < 0) + else if (sm_io_close(df, SM_TIME_DEFAULT) < 0) { - dferror(df, "bfclose", e); - flush_errors(TRUE); - finis(TRUE, ExitStat); + dferror(df, "sm_io_close", e); + flush_errors(true); + finis(true, ExitStat); /* NOTREACHED */ } else { /* everything is happily flushed to disk */ df = NULL; + + /* remove from available space in filesystem */ + updfs(e, false, true); } /* An EOF when running SMTP is an error */ @@ -563,18 +754,18 @@ readerr: if (host == NULL) host = "localhost"; - if (feof(fp)) + if (sm_io_eof(fp)) problem = "unexpected close"; - else if (ferror(fp)) + else if (sm_io_error(fp)) problem = "I/O error"; else problem = "read timeout"; - if (LogLevel > 0 && feof(fp)) + if (LogLevel > 0 && sm_io_eof(fp)) sm_syslog(LOG_NOTICE, e->e_id, - "collect: %s on connection from %.100s, sender=%s", - problem, host, - shortenstring(e->e_from.q_paddr, MAXSHORTSTR)); - if (feof(fp)) + "collect: %s on connection from %.100s, sender=%s", + problem, host, + shortenstring(e->e_from.q_paddr, MAXSHORTSTR)); + if (sm_io_eof(fp)) usrerr("451 4.4.1 collect: %s on connection from %s, from=%s", problem, host, shortenstring(e->e_from.q_paddr, MAXSHORTSTR)); @@ -588,104 +779,32 @@ readerr: e->e_flags &= ~EF_FATALERRS; e->e_flags |= EF_CLRQUEUE; - /* and don't try to deliver the partial message either */ - if (InChild) - ExitStat = EX_QUIT; - finis(TRUE, ExitStat); + finis(true, ExitStat); /* NOTREACHED */ } - /* - ** Find out some information from the headers. - ** Examples are who is the from person & the date. - */ - - eatheader(e, TRUE); - - if (GrabTo && e->e_sendqueue == NULL) - usrerr("No recipient addresses found in header"); - - /* collect statistics */ - if (OpMode != MD_VERIFY) - markstats(e, (ADDRESS *) NULL, FALSE); - - /* - ** If we have a Return-Receipt-To:, turn it into a DSN. - */ - - if (RrtImpliesDsn && hvalue("return-receipt-to", e->e_header) != NULL) - { - ADDRESS *q; - - for (q = e->e_sendqueue; q != NULL; q = q->q_next) - if (!bitset(QHASNOTIFY, q->q_flags)) - q->q_flags |= QHASNOTIFY|QPINGONSUCCESS; - } - - /* - ** Add an Apparently-To: line if we have no recipient lines. - */ - - if (hvalue("to", e->e_header) != NULL || - hvalue("cc", e->e_header) != NULL || - hvalue("apparently-to", e->e_header) != NULL) + /* Log collection information. */ + if (bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4) { - /* have a valid recipient header -- delete Bcc: headers */ - e->e_flags |= EF_DELETE_BCC; - } - else if (hvalue("bcc", e->e_header) == NULL) - { - /* no valid recipient headers */ - register ADDRESS *q; - char *hdr = NULL; - - /* create an Apparently-To: field */ - /* that or reject the message.... */ - switch (NoRecipientAction) - { - case NRA_ADD_APPARENTLY_TO: - hdr = "Apparently-To"; - break; - - case NRA_ADD_TO: - hdr = "To"; - break; - - case NRA_ADD_BCC: - addheader("Bcc", " ", 0, &e->e_header); - break; - - case NRA_ADD_TO_UNDISCLOSED: - addheader("To", "undisclosed-recipients:;", 0, &e->e_header); - break; - } - - if (hdr != NULL) - { - for (q = e->e_sendqueue; q != NULL; q = q->q_next) - { - if (q->q_alias != NULL) - continue; - if (tTd(30, 3)) - dprintf("Adding %s: %s\n", - hdr, q->q_paddr); - addheader(hdr, q->q_paddr, 0, &e->e_header); - } - } + logsender(e, e->e_msgid); + e->e_flags &= ~EF_LOGSENDER; } /* check for message too large */ if (bitset(EF_TOOBIG, e->e_flags)) { e->e_flags |= EF_NO_BODY_RETN|EF_CLRQUEUE; - e->e_status = "5.2.3"; - usrerrenh(e->e_status, - "552 Message exceeds maximum fixed size (%ld)", - MaxMessageSize); - if (LogLevel > 6) - sm_syslog(LOG_NOTICE, e->e_id, - "message size (%ld) exceeds maximum (%ld)", - e->e_msgsize, MaxMessageSize); + if (!bitset(EF_FATALERRS, e->e_flags)) + { + e->e_status = "5.2.3"; + usrerrenh(e->e_status, + "552 Message exceeds maximum fixed size (%ld)", + MaxMessageSize); + if (LogLevel > 6) + sm_syslog(LOG_NOTICE, e->e_id, + "message size (%ld) exceeds maximum (%ld)", + e->e_msgsize, MaxMessageSize); + } } /* check for illegal 8-bit data */ @@ -703,27 +822,26 @@ readerr: { /* if it claimed to be 8 bits, well, it lied.... */ if (e->e_bodytype != NULL && - strcasecmp(e->e_bodytype, "8BITMIME") == 0) + sm_strcasecmp(e->e_bodytype, "8BITMIME") == 0) e->e_bodytype = "7BIT"; } - if (SuperSafe) + if (SuperSafe == SAFE_REALLY && !bitset(EF_FATALERRS, e->e_flags)) { - if ((e->e_dfp = fopen(dfname, "r")) == NULL) + char *dfname = queuename(e, 'd'); + if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, + SM_IO_RDONLY, NULL)) == NULL) { /* we haven't acked receipt yet, so just chuck this */ - syserr("Cannot reopen %s", dfname); - finis(TRUE, ExitStat); + syserr("@Cannot reopen %s", dfname); + finis(true, ExitStat); /* NOTREACHED */ } } else e->e_dfp = df; - if (e->e_dfp == NULL) - syserr("!collect: no e_dfp"); } - static void collecttimeout(timeout) time_t timeout; @@ -739,9 +857,9 @@ collecttimeout(timeout) if (CollectProgress) { /* reset the timeout */ - CollectTimeout = sigsafe_setevent(timeout, collecttimeout, - timeout); - CollectProgress = FALSE; + CollectTimeout = sm_sigsafe_setevent(timeout, collecttimeout, + timeout); + CollectProgress = false; } else { @@ -755,12 +873,16 @@ collecttimeout(timeout) errno = ETIMEDOUT; longjmp(CtxCollectTimeout, 1); } - errno = save_errno; } /* ** DFERROR -- signal error on writing the data file. ** +** Called by collect(). Collect() always terminates the process +** immediately after calling dferror(), which means that the SMTP +** session will be terminated, which means that any error message +** issued by dferror must be a 421 error, as per RFC 821. +** ** Parameters: ** df -- the file pointer for the data file. ** msg -- detailed message. @@ -776,7 +898,7 @@ collecttimeout(timeout) static void dferror(df, msg, e) - FILE *volatile df; + SM_FILE_T *volatile df; char *msg; register ENVELOPE *e; { @@ -798,44 +920,52 @@ dferror(df, msg, e) if ( #if STAT64 > 0 - fstat64(fileno(df), &st) + fstat64(sm_io_getinfo(df, SM_IO_WHAT_FD, NULL), &st) #else /* STAT64 > 0 */ - fstat(fileno(df), &st) + fstat(sm_io_getinfo(df, SM_IO_WHAT_FD, NULL), &st) #endif /* STAT64 > 0 */ < 0) st.st_size = 0; - (void) freopen(dfname, "w", df); + (void) sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, dfname, + SM_IO_WRONLY, NULL, df); if (st.st_size <= 0) - fprintf(df, "\n*** Mail could not be accepted"); - /*CONSTCOND*/ - else if (sizeof st.st_size > sizeof (long)) - fprintf(df, "\n*** Mail of at least %s bytes could not be accepted\n", - quad_to_string(st.st_size)); + (void) sm_io_fprintf(df, SM_TIME_DEFAULT, + "\n*** Mail could not be accepted"); else - fprintf(df, "\n*** Mail of at least %lu bytes could not be accepted\n", - (unsigned long) st.st_size); - fprintf(df, "*** at %s due to lack of disk space for temp file.\n", + (void) sm_io_fprintf(df, SM_TIME_DEFAULT, + "\n*** Mail of at least %llu bytes could not be accepted\n", + (ULONGLONG_T) st.st_size); + (void) sm_io_fprintf(df, SM_TIME_DEFAULT, + "*** at %s due to lack of disk space for temp file.\n", MyHostName); - avail = freediskspace(qid_printqueue(e->e_queuedir), &bsize); + avail = freediskspace(qid_printqueue(e->e_qgrp, e->e_qdir), + &bsize); if (avail > 0) { if (bsize > 1024) avail *= bsize / 1024; else if (bsize < 1024) avail /= 1024 / bsize; - fprintf(df, "*** Currently, %ld kilobytes are available for mail temp files.\n", + (void) sm_io_fprintf(df, SM_TIME_DEFAULT, + "*** Currently, %ld kilobytes are available for mail temp files.\n", avail); } +#if 0 + /* Wrong response code; should be 421. */ e->e_status = "4.3.1"; usrerrenh(e->e_status, "452 Out of disk space for temp file"); +#else /* 0 */ + syserr("421 4.3.1 Out of disk space for temp file"); +#endif /* 0 */ } else - syserr("collect: Cannot write %s (%s, uid=%d)", - dfname, msg, geteuid()); - if (freopen("/dev/null", "w", df) == NULL) + syserr("421 4.3.0 collect: Cannot write %s (%s, uid=%d, gid=%d)", + dfname, msg, geteuid(), getegid()); + if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL, + SM_IO_WRONLY, NULL, df) == NULL) sm_syslog(LOG_ERR, e->e_id, - "dferror: freopen(\"/dev/null\") failed: %s", - errstring(errno)); + "dferror: sm_io_reopen(\"/dev/null\") failed: %s", + sm_errstring(errno)); } /* ** EATFROM -- chew up a UNIX style from line and process @@ -877,7 +1007,7 @@ eatfrom(fm, e) register char **dt; if (tTd(30, 2)) - dprintf("eatfrom(%s)\n", fm); + sm_dprintf("eatfrom(%s)\n", fm); /* find the date part */ p = fm; @@ -915,13 +1045,12 @@ eatfrom(fm, e) if (*p != '\0') { - char *q; + char *q, buf[25]; /* we have found a date */ - q = xalloc(25); - (void) strlcpy(q, p, 25); - q = arpadate(q); - define('a', newstr(q), e); + (void) sm_strlcpy(buf, p, sizeof(buf)); + q = arpadate(buf); + macdefine(&e->e_macro, A_TEMP, 'a', q); } } #endif /* ! NOTUNIX */ diff --git a/gnu/usr.sbin/sendmail/sendmail/conf.c b/gnu/usr.sbin/sendmail/sendmail/conf.c index 9369500d4f3..4f4b7b1fcd8 100644 --- a/gnu/usr.sbin/sendmail/sendmail/conf.c +++ b/gnu/usr.sbin/sendmail/sendmail/conf.c @@ -11,11 +11,10 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: conf.c,v 8.646.2.2.2.87 2001/07/20 23:56:52 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> + +SM_RCSID("@(#)$Sendmail: conf.c,v 8.907 2001/09/04 22:43:02 ca Exp $") + #include <sendmail/pathnames.h> # include <sys/ioctl.h> @@ -32,6 +31,7 @@ static char id[] = "@(#)$Sendmail: conf.c,v 8.646.2.2.2.87 2001/07/20 23:56:52 g static void setupmaps __P((void)); static void setupmailers __P((void)); +static void setupqueues __P((void)); static int get_num_procs_online __P((void)); @@ -79,6 +79,7 @@ struct hdrinfo HdrInfo[] = { "errors-to", H_FROM|H_ERRORSTO, NULL }, { "full-name", H_ACHECK, NULL }, { "return-receipt-to", H_RECEIPTTO, NULL }, + { "disposition-notification-to", H_FROM, NULL }, /* destination fields */ { "to", H_RCPT, NULL }, @@ -131,6 +132,7 @@ struct prival PrivacyValues[] = { "needvrfyhelo", PRIV_NEEDVRFYHELO }, { "noexpn", PRIV_NOEXPN }, { "novrfy", PRIV_NOVRFY }, + { "restrictexpand", PRIV_RESTRICTEXPAND }, { "restrictmailq", PRIV_RESTRICTMAILQ }, { "restrictqrun", PRIV_RESTRICTQRUN }, { "noetrn", PRIV_NOETRN }, @@ -145,6 +147,7 @@ struct prival PrivacyValues[] = /* ** DontBlameSendmail values */ + struct dbsval DontBlameSendmailValues[] = { { "safe", DBS_SAFE }, @@ -194,19 +197,20 @@ struct dbsval DontBlameSendmailValues[] = { "dontwarnforwardfileinunsafedirpath", DBS_DONTWARNFORWARDFILEINUNSAFEDIRPATH }, { "insufficiententropy", DBS_INSUFFICIENTENTROPY }, -#if _FFR_UNSAFE_SASL - { "groupreadablesaslfile", DBS_GROUPREADABLESASLFILE }, -#endif /* _FFR_UNSAFE_SASL */ -#if _FFR_UNSAFE_WRITABLE_INCLUDE + { "groupreadablesasldbfile", DBS_GROUPREADABLESASLDBFILE }, + { "groupwritablesasldbfile", DBS_GROUPWRITABLESASLDBFILE }, { "groupwritableforwardfile", DBS_GROUPWRITABLEFORWARDFILE }, { "groupwritableincludefile", DBS_GROUPWRITABLEINCLUDEFILE }, { "worldwritableforwardfile", DBS_WORLDWRITABLEFORWARDFILE }, { "worldwritableincludefile", DBS_WORLDWRITABLEINCLUDEFILE }, -#endif /* _FFR_UNSAFE_WRITABLE_INCLUDE */ + { "groupreadablekeyfile", DBS_GROUPREADABLEKEYFILE }, +#if _FFR_GROUPREADABLEAUTHINFOFILE + { "groupreadableadefaultauthinfofile", + DBS_GROUPREADABLEAUTHINFOFILE }, +#endif /* _FFR_GROUPREADABLEAUTHINFOFILE */ { NULL, 0 } }; - /* ** Miscellaneous stuff. */ @@ -215,8 +219,8 @@ int DtableSize = 50; /* max open files; reset in 4.2bsd */ /* ** SETDEFAULTS -- set default values ** -** Because of the way freezing is done, these must be initialized -** using direct code. +** Some of these must be initialized using direct code since they +** depend on run-time values. So let's do all of them this way. ** ** Parameters: ** e -- the default envelope. @@ -255,10 +259,8 @@ setdefaults(e) QueueFactor = WkRecipFact * 20; /* option q */ FileMode = (RealUid != geteuid()) ? 0644 : 0600; /* option F */ -#if _FFR_QUEUE_FILE_MODE QueueFileMode = (RealUid != geteuid()) ? 0644 : 0600; /* option QueueFileMode */ -#endif /* _FFR_QUEUE_FILE_MODE */ if (((pw = sm_getpwnam("mailnull")) != NULL && pw->pw_uid != 0) || ((pw = sm_getpwnam("sendmail")) != NULL && pw->pw_uid != 0) || @@ -276,24 +278,30 @@ setdefaults(e) } TrustedUid = 0; if (tTd(37, 4)) - dprintf("setdefaults: DefUser=%s, DefUid=%d, DefGid=%d\n", + sm_dprintf("setdefaults: DefUser=%s, DefUid=%d, DefGid=%d\n", DefUser != NULL ? DefUser : "<1:1>", (int) DefUid, (int) DefGid); CheckpointInterval = 10; /* option C */ MaxHopCount = 25; /* option h */ set_delivery_mode(SM_FORK, e); /* option d */ e->e_errormode = EM_PRINT; /* option e */ - e->e_queuedir = NOQDIR; + e->e_qgrp = NOQGRP; + e->e_qdir = NOQDIR; + e->e_xfqgrp = NOQGRP; + e->e_xfqdir = NOQDIR; e->e_ctime = curtime(); - SevenBitInput = FALSE; /* option 7 */ + SevenBitInput = false; /* option 7 */ MaxMciCache = 1; /* option k */ MciCacheTimeout = 5 MINUTES; /* option K */ LogLevel = 9; /* option L */ - inittimeouts(NULL, FALSE); /* option r */ +#if MILTER + MilterLogLevel = -1; +#endif /* MILTER */ + inittimeouts(NULL, false); /* option r */ PrivacyFlags = PRIV_PUBLIC; /* option p */ - MeToo = TRUE; /* option m */ - SendMIMEErrors = TRUE; /* option f */ - SuperSafe = TRUE; /* option s */ + MeToo = true; /* option m */ + SendMIMEErrors = true; /* option f */ + SuperSafe = SAFE_REALLY; /* option s */ clrbitmap(DontBlameSendmail); /* DontBlameSendmail option */ #if MIME8TO7 MimeMode = MM_CVTMIME|MM_PASS8BIT; /* option 8 */ @@ -314,14 +322,20 @@ setdefaults(e) MaxRuleRecursion = MAXRULERECURSION; MaxAliasRecursion = 10; MaxMacroRecursion = 10; - ColonOkInAddr = TRUE; - DontLockReadFiles = TRUE; + ColonOkInAddr = true; + DontLockReadFiles = true; + DontProbeInterfaces = DPI_PROBEALL; DoubleBounceAddr = "postmaster"; MaxHeadersLength = MAXHDRSLEN; MaxForwardEntries = 0; + FastSplit = 1; #if SASL AuthMechanisms = newstr(AUTH_MECHANISMS); + MaxSLBits = INT_MAX; #endif /* SASL */ +#if STARTTLS + TLS_Srv_Opts = TLS_I_SRV; +#endif /* STARTTLS */ #ifdef HESIOD_INIT HesiodContext = NULL; #endif /* HESIOD_INIT */ @@ -344,10 +358,11 @@ setdefaults(e) XscriptFileBufferSize = 4096; for (i = 0; i < MAXRWSETS; i++) RuleSetNames[i] = NULL; -#if _FFR_MILTER +#if MILTER InputFilters[0] = NULL; -#endif /* _FFR_MILTER */ +#endif /* MILTER */ setupmaps(); + setupqueues(); setupmailers(); setupheaders(); } @@ -365,11 +380,30 @@ setdefuser() DefUser = defuserbuf; defpwent = sm_getpwuid(DefUid); - snprintf(defuserbuf, sizeof defuserbuf, "%s", - defpwent == NULL ? "nobody" : defpwent->pw_name); + (void) sm_strlcpy(defuserbuf, + (defpwent == NULL || defpwent->pw_name == NULL) + ? "nobody" : defpwent->pw_name, + sizeof defuserbuf); if (tTd(37, 4)) - dprintf("setdefuser: DefUid=%d, DefUser=%s\n", - (int) DefUid, DefUser); + sm_dprintf("setdefuser: DefUid=%d, DefUser=%s\n", + (int) DefUid, DefUser); +} +/* +** SETUPQUEUES -- initialize default queues +** +** The mqueue QUEUE structure gets filled in after readcf() but +** we need something to point to now for the mailer setup, +** which use "mqueue" as default queue. +*/ + +static void +setupqueues() +{ + char buf[100]; + + MaxRunnersPerQueue = 1; + (void) sm_strlcpy(buf, "mqueue, P=/var/spool/mqueue", sizeof buf); + makequeue(buf, false); } /* ** SETUPMAILERS -- initialize default mailers @@ -380,16 +414,16 @@ setupmailers() { char buf[100]; - (void) strlcpy(buf, "prog, P=/bin/sh, F=lsouDq9, T=X-Unix/X-Unix/X-Unix, A=sh -c \201u", - sizeof buf); + (void) sm_strlcpy(buf, "prog, P=/bin/sh, F=lsouDq9, T=X-Unix/X-Unix/X-Unix, A=sh -c \201u", + sizeof buf); makemailer(buf); - (void) strlcpy(buf, "*file*, P=[FILE], F=lsDFMPEouq9, T=X-Unix/X-Unix/X-Unix, A=FILE \201u", - sizeof buf); + (void) sm_strlcpy(buf, "*file*, P=[FILE], F=lsDFMPEouq9, T=X-Unix/X-Unix/X-Unix, A=FILE \201u", + sizeof buf); makemailer(buf); - (void) strlcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE \201u", - sizeof buf); + (void) sm_strlcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE \201u", + sizeof buf); makemailer(buf); initerrmailers(); } @@ -420,7 +454,7 @@ setupmaps() { register STAB *s; -#ifdef NEWDB +#if NEWDB MAPDEF("hash", ".db", MCF_ALIASOK|MCF_REBUILDABLE, map_parseargs, hash_map_open, db_map_close, db_map_lookup, db_map_store); @@ -430,37 +464,32 @@ setupmaps() db_map_lookup, db_map_store); #endif /* NEWDB */ -#ifdef NDBM +#if NDBM MAPDEF("dbm", ".dir", MCF_ALIASOK|MCF_REBUILDABLE, map_parseargs, ndbm_map_open, ndbm_map_close, ndbm_map_lookup, ndbm_map_store); #endif /* NDBM */ -#ifdef NIS +#if NIS MAPDEF("nis", NULL, MCF_ALIASOK, map_parseargs, nis_map_open, null_map_close, nis_map_lookup, null_map_store); #endif /* NIS */ -#ifdef NISPLUS +#if NISPLUS MAPDEF("nisplus", NULL, MCF_ALIASOK, map_parseargs, nisplus_map_open, null_map_close, nisplus_map_lookup, null_map_store); #endif /* NISPLUS */ -#ifdef LDAPMAP - MAPDEF("ldap", NULL, MCF_ALIASOK, +#if LDAPMAP + MAPDEF("ldap", NULL, MCF_ALIASOK|MCF_NOTPERSIST, ldapmap_parseargs, ldapmap_open, ldapmap_close, ldapmap_lookup, null_map_store); - - /* Deprecated */ - MAPDEF("ldapx", NULL, MCF_ALIASOK, - ldapx_map_parseargs, ldapmap_open, ldapmap_close, - ldapmap_lookup, null_map_store); #endif /* LDAPMAP */ -#ifdef PH_MAP - MAPDEF("ph", NULL, 0, +#if PH_MAP + MAPDEF("ph", NULL, MCF_NOTPERSIST, ph_map_parseargs, ph_map_open, ph_map_close, ph_map_lookup, null_map_store); #endif /* PH_MAP */ @@ -472,9 +501,9 @@ setupmaps() nsd_map_lookup, null_map_store); #endif /* MAP_NSD */ -#ifdef HESIOD +#if HESIOD MAPDEF("hesiod", NULL, MCF_ALIASOK|MCF_ALIASONLY, - map_parseargs, hes_map_open, null_map_close, + map_parseargs, hes_map_open, hes_map_close, hes_map_lookup, null_map_store); #endif /* HESIOD */ @@ -491,6 +520,14 @@ setupmaps() #endif /* 0 */ #if NAMED_BIND +# if DNSMAP + MAPDEF("dns", NULL, 0, + dns_map_parseargs, dns_map_open, null_map_close, + dns_map_lookup, null_map_store); +# endif /* DNSMAP */ +#endif /* NAMED_BIND */ + +#if NAMED_BIND /* best MX DNS lookup */ MAPDEF("bestmx", NULL, MCF_OPTFILE, map_parseargs, null_map_open, null_map_close, @@ -523,7 +560,7 @@ setupmaps() dequote_init, null_map_open, null_map_close, dequote_map, null_map_store); -#ifdef MAP_REGEX +#if MAP_REGEX MAPDEF("regex", NULL, 0, regex_map_init, null_map_open, null_map_close, regex_map_lookup, null_map_store); @@ -621,7 +658,7 @@ inithostmaps() if (strcmp(maptype[i], "files") == 0 && stab("hosts.files", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "hosts.files text -k 0 -v 1 /etc/hosts", + (void) sm_strlcpy(buf, "hosts.files text -k 0 -v 1 /etc/hosts", sizeof buf); (void) makemapentry(buf); } @@ -629,24 +666,24 @@ inithostmaps() else if (strcmp(maptype[i], "dns") == 0 && stab("hosts.dns", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "hosts.dns dns A", sizeof buf); + (void) sm_strlcpy(buf, "hosts.dns dns A", sizeof buf); (void) makemapentry(buf); } # endif /* NAMED_BIND */ -# ifdef NISPLUS +# if NISPLUS else if (strcmp(maptype[i], "nisplus") == 0 && stab("hosts.nisplus", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "hosts.nisplus nisplus -k name -v address hosts.org_dir", + (void) sm_strlcpy(buf, "hosts.nisplus nisplus -k name -v address hosts.org_dir", sizeof buf); (void) makemapentry(buf); } # endif /* NISPLUS */ -# ifdef NIS +# if NIS else if (strcmp(maptype[i], "nis") == 0 && stab("hosts.nis", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "hosts.nis nis -k 0 -v 1 hosts.byname", + (void) sm_strlcpy(buf, "hosts.nis nis -k 0 -v 1 hosts.byname", sizeof buf); (void) makemapentry(buf); } @@ -655,7 +692,7 @@ inithostmaps() else if (strcmp(maptype[i], "netinfo") == 0) && stab("hosts.netinfo", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "hosts.netinfo netinfo -v name /machines", + (void) sm_strlcpy(buf, "hosts.netinfo netinfo -v name /machines", sizeof buf); (void) makemapentry(buf); } @@ -670,10 +707,10 @@ inithostmaps() if (stab("host", ST_MAP, ST_FIND) == NULL) { /* user didn't initialize: set up host map */ - (void) strlcpy(buf, "host host", sizeof buf); + (void) sm_strlcpy(buf, "host host", sizeof buf); #if NAMED_BIND if (ConfigLevel >= 2) - (void) strlcat(buf, " -a. -D", sizeof buf); + (void) sm_strlcat(buf, " -a. -D", sizeof buf); #endif /* NAMED_BIND */ (void) makemapentry(buf); } @@ -688,23 +725,24 @@ inithostmaps() if (strcmp(maptype[i], "files") == 0 && stab("aliases.files", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "aliases.files null", sizeof buf); + (void) sm_strlcpy(buf, "aliases.files null", + sizeof buf); (void) makemapentry(buf); } -#ifdef NISPLUS +#if NISPLUS else if (strcmp(maptype[i], "nisplus") == 0 && stab("aliases.nisplus", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "aliases.nisplus nisplus -kalias -vexpansion mail_aliases.org_dir", + (void) sm_strlcpy(buf, "aliases.nisplus nisplus -kalias -vexpansion mail_aliases.org_dir", sizeof buf); (void) makemapentry(buf); } #endif /* NISPLUS */ -#ifdef NIS +#if NIS else if (strcmp(maptype[i], "nis") == 0 && stab("aliases.nis", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "aliases.nis nis mail.aliases", + (void) sm_strlcpy(buf, "aliases.nis nis mail.aliases", sizeof buf); (void) makemapentry(buf); } @@ -713,16 +751,16 @@ inithostmaps() else if (strcmp(maptype[i], "netinfo") == 0 && stab("aliases.netinfo", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "aliases.netinfo netinfo -z, /aliases", + (void) sm_strlcpy(buf, "aliases.netinfo netinfo -z, /aliases", sizeof buf); (void) makemapentry(buf); } #endif /* NETINFO */ -#ifdef HESIOD +#if HESIOD else if (strcmp(maptype[i], "hesiod") == 0 && stab("aliases.hesiod", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "aliases.hesiod hesiod aliases", + (void) sm_strlcpy(buf, "aliases.hesiod hesiod aliases", sizeof buf); (void) makemapentry(buf); } @@ -730,7 +768,7 @@ inithostmaps() } if (stab("aliases", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "aliases switch aliases", sizeof buf); + (void) sm_strlcpy(buf, "aliases switch aliases", sizeof buf); (void) makemapentry(buf); } @@ -745,40 +783,40 @@ inithostmaps() if (strcmp(maptype[i], "files") == 0 && stab("users.files", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "users.files text -m -z: -k0 -v6 /etc/passwd", + (void) sm_strlcpy(buf, "users.files text -m -z: -k0 -v6 /etc/passwd", sizeof buf); (void) makemapentry(buf); } -# ifdef NISPLUS +# if NISPLUS else if (strcmp(maptype[i], "nisplus") == 0 && stab("users.nisplus", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "users.nisplus nisplus -m -kname -vhome passwd.org_dir", + (void) sm_strlcpy(buf, "users.nisplus nisplus -m -kname -vhome passwd.org_dir", sizeof buf); (void) makemapentry(buf); } # endif /* NISPLUS */ -# ifdef NIS +# if NIS else if (strcmp(maptype[i], "nis") == 0 && stab("users.nis", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "users.nis nis -m passwd.byname", + (void) sm_strlcpy(buf, "users.nis nis -m passwd.byname", sizeof buf); (void) makemapentry(buf); } # endif /* NIS */ -# ifdef HESIOD +# if HESIOD else if (strcmp(maptype[i], "hesiod") == 0) && stab("users.hesiod", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "users.hesiod hesiod", sizeof buf); + (void) sm_strlcpy(buf, "users.hesiod hesiod", sizeof buf); (void) makemapentry(buf); } # endif /* HESIOD */ } if (stab("users", ST_MAP, ST_FIND) == NULL) { - (void) strlcpy(buf, "users switch -m passwd", sizeof buf); + (void) sm_strlcpy(buf, "users switch -m passwd", sizeof buf); (void) makemapentry(buf); } #endif /* 0 */ @@ -919,15 +957,16 @@ switch_map_find(service, maptype, mapreturn) */ STAB *st; + static time_t servicecachetime; /* time service switch was cached */ time_t now = curtime(); for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) mapreturn[svcno] = 0; - if ((now - ServiceCacheTime) > (time_t) ServiceCacheMaxAge) + if ((now - servicecachetime) > (time_t) ServiceCacheMaxAge) { /* (re)read service switch */ - register FILE *fp; + register SM_FILE_T *fp; long sff = SFF_REGONLY|SFF_OPENASROOT|SFF_NOLOCK; if (!bitnset(DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR, @@ -935,13 +974,14 @@ switch_map_find(service, maptype, mapreturn) sff |= SFF_NOWLINK; if (ConfigFileRead) - ServiceCacheTime = now; + servicecachetime = now; fp = safefopen(ServiceSwitchFile, O_RDONLY, 0, sff); if (fp != NULL) { char buf[MAXLINE]; - while (fgets(buf, sizeof buf, fp) != NULL) + while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, + sizeof buf) != NULL) { register char *p; @@ -976,7 +1016,7 @@ switch_map_find(service, maptype, mapreturn) st = stab(buf, ST_SERVICE, ST_ENTER); if (st->s_service[0] != NULL) - sm_free((void *) st->s_service[0]); + sm_free((void *) st->s_service[0]); /* XXX */ p = newstr(p); for (svcno = 0; svcno < MAXMAPSTACK; ) { @@ -993,7 +1033,7 @@ switch_map_find(service, maptype, mapreturn) if (svcno < MAXMAPSTACK) st->s_service[svcno] = NULL; } - (void) fclose(fp); + (void) sm_io_close(fp, SM_TIME_DEFAULT); } } @@ -1029,10 +1069,10 @@ switch_map_find(service, maptype, mapreturn) maptype[svcno++] = "netinfo"; # endif /* defined(AUTO_NETINFO_ALIASES) && defined (NETINFO) */ # ifdef AUTO_NIS_ALIASES -# ifdef NISPLUS +# if NISPLUS maptype[svcno++] = "nisplus"; # endif /* NISPLUS */ -# ifdef NIS +# if NIS maptype[svcno++] = "nis"; # endif /* NIS */ # endif /* AUTO_NIS_ALIASES */ @@ -1091,19 +1131,18 @@ username() { pw = sm_getpwuid(RealUid); if (pw != NULL) - myname = newstr(pw->pw_name); + myname = pw->pw_name; } else { uid_t uid = RealUid; - myname = newstr(myname); if ((pw = sm_getpwnam(myname)) == NULL || (uid != 0 && uid != pw->pw_uid)) { pw = sm_getpwuid(uid); if (pw != NULL) - myname = newstr(pw->pw_name); + myname = pw->pw_name; } } if (myname == NULL || myname[0] == '\0') @@ -1111,8 +1150,11 @@ username() syserr("554 5.3.0 Who are you?"); myname = "postmaster"; } + else if (strpbrk(myname, ",;:/|\"\\") != NULL) + myname = addquotes(myname, NULL); + else + myname = sm_pstrdup_x(myname); } - return myname; } /* @@ -1203,7 +1245,7 @@ checkcompat(to, e) register ENVELOPE *e; { if (tTd(49, 1)) - dprintf("checkcompat(to=%s, from=%s)\n", + sm_dprintf("checkcompat(to=%s, from=%s)\n", to->q_paddr, e->e_from.q_paddr); #ifdef EXAMPLE_CODE @@ -1223,254 +1265,6 @@ checkcompat(to, e) return EX_OK; } /* -** SETSIGNAL -- set a signal handler -** -** This is essentially old BSD "signal(3)". -** -** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD -** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE -** DOING. -*/ - -sigfunc_t -setsignal(sig, handler) - int sig; - sigfunc_t handler; -{ -# if defined(SA_RESTART) || (!defined(SYS5SIGNALS) && !defined(BSD4_3)) - struct sigaction n, o; -# endif /* defined(SA_RESTART) || (!defined(SYS5SIGNALS) && !defined(BSD4_3)) */ - - /* - ** First, try for modern signal calls - ** and restartable syscalls - */ - -# ifdef SA_RESTART - memset(&n, '\0', sizeof n); -# if USE_SA_SIGACTION - n.sa_sigaction = (void(*)(int, siginfo_t *, void *)) handler; - n.sa_flags = SA_RESTART|SA_SIGINFO; -# else /* USE_SA_SIGACTION */ - n.sa_handler = handler; - n.sa_flags = SA_RESTART; -# endif /* USE_SA_SIGACTION */ - if (sigaction(sig, &n, &o) < 0) - return SIG_ERR; - return o.sa_handler; -# else /* SA_RESTART */ - - /* - ** Else check for SYS5SIGNALS or - ** BSD4_3 signals - */ - -# if defined(SYS5SIGNALS) || defined(BSD4_3) -# ifdef BSD4_3 - return signal(sig, handler); -# else /* BSD4_3 */ - return sigset(sig, handler); -# endif /* BSD4_3 */ -# else /* defined(SYS5SIGNALS) || defined(BSD4_3) */ - - /* - ** Finally, if nothing else is available, - ** go for a default - */ - - memset(&n, '\0', sizeof n); - n.sa_handler = handler; - if (sigaction(sig, &n, &o) < 0) - return SIG_ERR; - return o.sa_handler; -# endif /* defined(SYS5SIGNALS) || defined(BSD4_3) */ -# endif /* SA_RESTART */ -} -/* -** ALLSIGNALS -- act on all signals -** -** Parameters: -** block -- whether to block or release all signals. -** -** Returns: -** none. -*/ - -void -allsignals(block) - bool block; -{ -# ifdef BSD4_3 -# ifndef sigmask -# define sigmask(s) (1 << ((s) - 1)) -# endif /* ! sigmask */ - if (block) - { - int mask = 0; - - mask |= sigmask(SIGALRM); - mask |= sigmask(SIGCHLD); - mask |= sigmask(SIGHUP); - mask |= sigmask(SIGINT); - mask |= sigmask(SIGTERM); - mask |= sigmask(SIGUSR1); - - (void) sigblock(mask); - } - else - sigsetmask(0); -# else /* BSD4_3 */ -# ifdef ALTOS_SYSTEM_V - if (block) - { - (void) sigset(SIGALRM, SIG_HOLD); - (void) sigset(SIGCHLD, SIG_HOLD); - (void) sigset(SIGHUP, SIG_HOLD); - (void) sigset(SIGINT, SIG_HOLD); - (void) sigset(SIGTERM, SIG_HOLD); - (void) sigset(SIGUSR1, SIG_HOLD); - } - else - { - (void) sigset(SIGALRM, SIG_DFL); - (void) sigset(SIGCHLD, SIG_DFL); - (void) sigset(SIGHUP, SIG_DFL); - (void) sigset(SIGINT, SIG_DFL); - (void) sigset(SIGTERM, SIG_DFL); - (void) sigset(SIGUSR1, SIG_DFL); - } -# else /* ALTOS_SYSTEM_V */ - sigset_t sset; - - (void) sigemptyset(&sset); - (void) sigaddset(&sset, SIGALRM); - (void) sigaddset(&sset, SIGCHLD); - (void) sigaddset(&sset, SIGHUP); - (void) sigaddset(&sset, SIGINT); - (void) sigaddset(&sset, SIGTERM); - (void) sigaddset(&sset, SIGUSR1); - (void) sigprocmask(block ? SIG_BLOCK : SIG_UNBLOCK, &sset, NULL); -# endif /* ALTOS_SYSTEM_V */ -# endif /* BSD4_3 */ -} -/* -** BLOCKSIGNAL -- hold a signal to prevent delivery -** -** Parameters: -** sig -- the signal to block. -** -** Returns: -** 1 signal was previously blocked -** 0 signal was not previously blocked -** -1 on failure. -*/ - -int -blocksignal(sig) - int sig; -{ -# ifdef BSD4_3 -# ifndef sigmask -# define sigmask(s) (1 << ((s) - 1)) -# endif /* ! sigmask */ - return (sigblock(sigmask(sig)) & sigmask(sig)) != 0; -# else /* BSD4_3 */ -# ifdef ALTOS_SYSTEM_V - sigfunc_t handler; - - handler = sigset(sig, SIG_HOLD); - if (handler == SIG_ERR) - return -1; - else - return handler == SIG_HOLD; -# else /* ALTOS_SYSTEM_V */ - sigset_t sset, oset; - - (void) sigemptyset(&sset); - (void) sigaddset(&sset, sig); - if (sigprocmask(SIG_BLOCK, &sset, &oset) < 0) - return -1; - else - return sigismember(&oset, sig); -# endif /* ALTOS_SYSTEM_V */ -# endif /* BSD4_3 */ -} -/* -** RELEASESIGNAL -- release a held signal -** -** Parameters: -** sig -- the signal to release. -** -** Returns: -** 1 signal was previously blocked -** 0 signal was not previously blocked -** -1 on failure. -*/ - -int -releasesignal(sig) - int sig; -{ -# ifdef BSD4_3 - return (sigsetmask(sigblock(0) & ~sigmask(sig)) & sigmask(sig)) != 0; -# else /* BSD4_3 */ -# ifdef ALTOS_SYSTEM_V - sigfunc_t handler; - - handler = sigset(sig, SIG_HOLD); - if (sigrelse(sig) < 0) - return -1; - else - return handler == SIG_HOLD; -# else /* ALTOS_SYSTEM_V */ - sigset_t sset, oset; - - (void) sigemptyset(&sset); - (void) sigaddset(&sset, sig); - if (sigprocmask(SIG_UNBLOCK, &sset, &oset) < 0) - return -1; - else - return sigismember(&oset, sig); -# endif /* ALTOS_SYSTEM_V */ -# endif /* BSD4_3 */ -} -/* -** HOLDSIGS -- arrange to hold all signals -** -** Parameters: -** none. -** -** Returns: -** none. -** -** Side Effects: -** Arranges that signals are held. -*/ - -void -holdsigs() -{ -} -/* -** RLSESIGS -- arrange to release all signals -** -** This undoes the effect of holdsigs. -** -** Parameters: -** none. -** -** Returns: -** none. -** -** Side Effects: -** Arranges that signals are released. -*/ - -void -rlsesigs() -{ -} -/* ** INIT_MD -- do machine dependent initializations ** ** Systems that have global modes that should be set should do @@ -1646,9 +1440,10 @@ struct nlist Nl[] = # endif /* _AUX_SOURCE */ # define X_AVENRUN 0 -static int +int getla() { + int j; static int kmem = -1; # if LA_TYPE == LA_INT long avenrun[3]; @@ -1665,7 +1460,7 @@ getla() if (kmem < 0) { # ifdef _AUX_SOURCE - (void) strlcpy(Nl[X_AVENRUN].n_name, LA_AVENRUN, + (void) sm_strlcpy(Nl[X_AVENRUN].n_name, LA_AVENRUN, sizeof Nl[X_AVENRUN].n_name); Nl[1].n_name[0] = '\0'; # endif /* _AUX_SOURCE */ @@ -1677,14 +1472,14 @@ getla() # endif /* defined(_AIX3) || defined(_AIX4) */ { if (tTd(3, 1)) - dprintf("getla: nlist(%s): %s\n", _PATH_UNIX, - errstring(errno)); + sm_dprintf("getla: nlist(%s): %s\n", _PATH_UNIX, + sm_errstring(errno)); return -1; } if (Nl[X_AVENRUN].n_value == 0) { if (tTd(3, 1)) - dprintf("getla: nlist(%s, %s) ==> 0\n", + sm_dprintf("getla: nlist(%s, %s) ==> 0\n", _PATH_UNIX, LA_AVENRUN); return -1; } @@ -1696,52 +1491,61 @@ getla() if (kmem < 0) { if (tTd(3, 1)) - dprintf("getla: open(/dev/kmem): %s\n", - errstring(errno)); + sm_dprintf("getla: open(/dev/kmem): %s\n", + sm_errstring(errno)); + return -1; + } + if ((j = fcntl(kmem, F_GETFD, 0)) < 0 || + fcntl(kmem, F_SETFD, j | FD_CLOEXEC) < 0) + { + if (tTd(3, 1)) + sm_dprintf("getla: fcntl(/dev/kmem, FD_CLOEXEC): %s\n", + sm_errstring(errno)); + (void) close(kmem); + kmem = -1; return -1; } - (void) fcntl(kmem, F_SETFD, FD_CLOEXEC); } if (tTd(3, 20)) - dprintf("getla: symbol address = %#lx\n", - (u_long) Nl[X_AVENRUN].n_value); + sm_dprintf("getla: symbol address = %#lx\n", + (unsigned long) Nl[X_AVENRUN].n_value); if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, SEEK_SET) == -1 || read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun)) { /* thank you Ian */ if (tTd(3, 1)) - dprintf("getla: lseek or read: %s\n", - errstring(errno)); + sm_dprintf("getla: lseek or read: %s\n", + sm_errstring(errno)); return -1; } # if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT) if (tTd(3, 5)) { # if LA_TYPE == LA_SHORT - dprintf("getla: avenrun = %d", avenrun[0]); + sm_dprintf("getla: avenrun = %d", avenrun[0]); if (tTd(3, 15)) - dprintf(", %d, %d", avenrun[1], avenrun[2]); + sm_dprintf(", %d, %d", avenrun[1], avenrun[2]); # else /* LA_TYPE == LA_SHORT */ - dprintf("getla: avenrun = %ld", avenrun[0]); + sm_dprintf("getla: avenrun = %ld", avenrun[0]); if (tTd(3, 15)) - dprintf(", %ld, %ld", avenrun[1], avenrun[2]); + sm_dprintf(", %ld, %ld", avenrun[1], avenrun[2]); # endif /* LA_TYPE == LA_SHORT */ - dprintf("\n"); + sm_dprintf("\n"); } if (tTd(3, 1)) - dprintf("getla: %d\n", + sm_dprintf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT); return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); # else /* (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT) */ if (tTd(3, 5)) { - dprintf("getla: avenrun = %g", avenrun[0]); + sm_dprintf("getla: avenrun = %g", avenrun[0]); if (tTd(3, 15)) - dprintf(", %g, %g", avenrun[1], avenrun[2]); - dprintf("\n"); + sm_dprintf(", %g, %g", avenrun[1], avenrun[2]); + sm_dprintf("\n"); } if (tTd(3, 1)) - dprintf("getla: %d\n", (int) (avenrun[0] +0.5)); + sm_dprintf("getla: %d\n", (int) (avenrun[0] +0.5)); return ((int) (avenrun[0] + 0.5)); # endif /* (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT) */ } @@ -1752,9 +1556,10 @@ getla() # include <sys/ksym.h> -static int +int getla() { + int j; static int kmem = -1; long avenrun[3]; extern int errno; @@ -1766,11 +1571,20 @@ getla() if (kmem < 0) { if (tTd(3, 1)) - dprintf("getla: open(/dev/kmem): %s\n", - errstring(errno)); + sm_dprintf("getla: open(/dev/kmem): %s\n", + sm_errstring(errno)); + return -1; + } + if ((j = fcntl(kmem, F_GETFD, 0)) < 0 || + fcntl(kmem, F_SETFD, j | FD_CLOEXEC) < 0) + { + if (tTd(3, 1)) + sm_dprintf("getla: fcntl(/dev/kmem, FD_CLOEXEC): %s\n", + sm_errstring(errno)); + (void) close(kmem); + kmem = -1; return -1; } - (void) fcntl(kmem, F_SETFD, FD_CLOEXEC); } mirk.mirk_symname = LA_AVENRUN; mirk.mirk_buf = avenrun; @@ -1778,19 +1592,19 @@ getla() if (ioctl(kmem, MIOC_READKSYM, &mirk) < 0) { if (tTd(3, 1)) - dprintf("getla: ioctl(MIOC_READKSYM) failed: %s\n", - errstring(errno)); + sm_dprintf("getla: ioctl(MIOC_READKSYM) failed: %s\n", + sm_errstring(errno)); return -1; } if (tTd(3, 5)) { - dprintf("getla: avenrun = %d", avenrun[0]); + sm_dprintf("getla: avenrun = %d", avenrun[0]); if (tTd(3, 15)) - dprintf(", %d, %d", avenrun[1], avenrun[2]); - dprintf("\n"); + sm_dprintf(", %d, %d", avenrun[1], avenrun[2]); + sm_dprintf("\n"); } if (tTd(3, 1)) - dprintf("getla: %d\n", + sm_dprintf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT); return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); } @@ -1801,7 +1615,7 @@ getla() # include <sys/dg_sys_info.h> -static int +int getla() { struct dg_sys_info_load_info load_info; @@ -1810,7 +1624,7 @@ getla() DG_SYS_INFO_LOAD_INFO_TYPE, DG_SYS_INFO_LOAD_VERSION_0); if (tTd(3, 1)) - dprintf("getla: %d\n", (int) (load_info.one_minute + 0.5)); + sm_dprintf("getla: %d\n", (int) (load_info.one_minute + 0.5)); return ((int) (load_info.one_minute + 0.5)); } @@ -1832,7 +1646,7 @@ struct pst_swapinfo; # include <sys/param.h> # include <sys/pstat.h> -static int +int getla() { struct pst_dynamic pstd; @@ -1842,7 +1656,7 @@ getla() return 0; if (tTd(3, 1)) - dprintf("getla: %d\n", (int) (pstd.psd_avg_1_min + 0.5)); + sm_dprintf("getla: %d\n", (int) (pstd.psd_avg_1_min + 0.5)); return (int) (pstd.psd_avg_1_min + 0.5); } @@ -1851,7 +1665,7 @@ getla() #if LA_TYPE == LA_SUBR -static int +int getla() { double avenrun[3]; @@ -1859,12 +1673,12 @@ getla() if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0) { if (tTd(3, 1)) - dprintf("getla: getloadavg failed: %s", - errstring(errno)); + sm_dprintf("getla: getloadavg failed: %s", + sm_errstring(errno)); return -1; } if (tTd(3, 1)) - dprintf("getla: %d\n", (int) (avenrun[0] +0.5)); + sm_dprintf("getla: %d\n", (int) (avenrun[0] +0.5)); return ((int) (avenrun[0] + 0.5)); } @@ -1882,7 +1696,7 @@ getla() # include <mach.h> # endif /* defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0 */ -static int +int getla() { processor_set_t default_set; @@ -1895,8 +1709,8 @@ getla() if (error != KERN_SUCCESS) { if (tTd(3, 1)) - dprintf("getla: processor_set_default failed: %s", - errstring(errno)); + sm_dprintf("getla: processor_set_default failed: %s", + sm_errstring(errno)); return -1; } info_count = PROCESSOR_SET_BASIC_INFO_COUNT; @@ -1905,12 +1719,12 @@ getla() &info_count) != KERN_SUCCESS) { if (tTd(3, 1)) - dprintf("getla: processor_set_info failed: %s", - errstring(errno)); + sm_dprintf("getla: processor_set_info failed: %s", + sm_errstring(errno)); return -1; } if (tTd(3, 1)) - dprintf("getla: %d\n", + sm_dprintf("getla: %d\n", (int) ((info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE)); return (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE; @@ -1919,6 +1733,12 @@ getla() #endif /* LA_TYPE == LA_MACH */ #if LA_TYPE == LA_PROCSTR +# if SM_CONF_BROKEN_STRTOD + ERROR: This OS has most likely a broken strtod() implemenentation. + ERROR: The function is required for getla(). + ERROR: Check the compilation options _LA_PROCSTR and + ERROR: _SM_CONF_BROKEN_STRTOD (without the leading _). +# endif /* SM_CONF_BROKEN_STRTOD */ /* ** Read /proc/loadavg for the load average. This is assumed to be @@ -1932,33 +1752,34 @@ getla() # define _PATH_LOADAVG "/proc/loadavg" # endif /* ! _PATH_LOADAVG */ -static int +int getla() { double avenrun; register int result; - FILE *fp; + SM_FILE_T *fp; - fp = fopen(_PATH_LOADAVG, "r"); + fp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, _PATH_LOADAVG, SM_IO_RDONLY, + NULL); if (fp == NULL) { if (tTd(3, 1)) - dprintf("getla: fopen(%s): %s\n", - _PATH_LOADAVG, errstring(errno)); + sm_dprintf("getla: sm_io_open(%s): %s\n", + _PATH_LOADAVG, sm_errstring(errno)); return -1; } - result = fscanf(fp, "%lf", &avenrun); - (void) fclose(fp); + result = sm_io_fscanf(fp, SM_TIME_DEFAULT, "%lf", &avenrun); + (void) sm_io_close(fp, SM_TIME_DEFAULT); if (result != 1) { if (tTd(3, 1)) - dprintf("getla: fscanf() = %d: %s\n", - result, errstring(errno)); + sm_dprintf("getla: sm_io_fscanf() = %d: %s\n", + result, sm_errstring(errno)); return -1; } if (tTd(3, 1)) - dprintf("getla(): %.2f\n", avenrun); + sm_dprintf("getla(): %.2f\n", avenrun); return ((int) (avenrun + 0.5)); } @@ -1969,8 +1790,10 @@ getla() # include <sys/sysmp.h> -int getla(void) +int +getla(void) { + int j; static int kmem = -1; int avenrun[3]; @@ -1980,32 +1803,41 @@ int getla(void) if (kmem < 0) { if (tTd(3, 1)) - dprintf("getla: open(%s): %s\n", _PATH_KMEM, - errstring(errno)); + sm_dprintf("getla: open(%s): %s\n", _PATH_KMEM, + sm_errstring(errno)); + return -1; + } + if ((j = fcntl(kmem, F_GETFD, 0)) < 0 || + fcntl(kmem, F_SETFD, j | FD_CLOEXEC) < 0) + { + if (tTd(3, 1)) + sm_dprintf("getla: fcntl(/dev/kmem, FD_CLOEXEC): %s\n", + sm_errstring(errno)); + (void) close(kmem); + kmem = -1; return -1; } - (void) fcntl(kmem, F_SETFD, FD_CLOEXEC); } if (lseek(kmem, (sysmp(MP_KERNADDR, MPKA_AVENRUN) & 0x7fffffff), SEEK_SET) == -1 || - read(kmem, (char *)avenrun, sizeof(avenrun)) < sizeof(avenrun)) + read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun)) { if (tTd(3, 1)) - dprintf("getla: lseek or read: %s\n", - errstring(errno)); + sm_dprintf("getla: lseek or read: %s\n", + sm_errstring(errno)); return -1; } if (tTd(3, 5)) { - dprintf("getla: avenrun = %ld", (long int) avenrun[0]); + sm_dprintf("getla: avenrun = %ld", (long int) avenrun[0]); if (tTd(3, 15)) - dprintf(", %ld, %ld", + sm_dprintf(", %ld, %ld", (long int) avenrun[1], (long int) avenrun[2]); - dprintf("\n"); + sm_dprintf("\n"); } if (tTd(3, 1)) - dprintf("getla: %d\n", + sm_dprintf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT); return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); @@ -2016,7 +1848,7 @@ int getla(void) # include <kstat.h> -static int +int getla() { static kstat_ctl_t *kc = NULL; @@ -2029,8 +1861,8 @@ getla() if (kc == NULL) { if (tTd(3, 1)) - dprintf("getla: kstat_open(): %s\n", - errstring(errno)); + sm_dprintf("getla: kstat_open(): %s\n", + sm_errstring(errno)); return -1; } if (ksp == NULL) @@ -2038,19 +1870,19 @@ getla() if (ksp == NULL) { if (tTd(3, 1)) - dprintf("getla: kstat_lookup(): %s\n", - errstring(errno)); + sm_dprintf("getla: kstat_lookup(): %s\n", + sm_errstring(errno)); return -1; } if (kstat_read(kc, ksp, NULL) < 0) { if (tTd(3, 1)) - dprintf("getla: kstat_read(): %s\n", - errstring(errno)); + sm_dprintf("getla: kstat_read(): %s\n", + sm_errstring(errno)); return -1; } ksn = (kstat_named_t *) kstat_data_lookup(ksp, "avenrun_1min"); - la = ((double)ksn->value.ul + FSCALE/2) / FSCALE; + la = ((double) ksn->value.ul + FSCALE/2) / FSCALE; /* kstat_close(kc); /o do not close for fast access */ return la; } @@ -2071,7 +1903,7 @@ getla() # define _PATH_AVENRUN "/dev/table/avenrun" # endif /* ! _PATH_AVENRUN */ -static int +int getla() { static int afd = -1; @@ -2089,8 +1921,8 @@ getla() if (afd < 0) { sm_syslog(LOG_ERR, NOQID, - "can't open %s: %m", - _PATH_AVENRUN); + "can't open %s: %s", + _PATH_AVENRUN, strerror(errno)); return -1; } } @@ -2098,10 +1930,10 @@ getla() r = read(afd, &avenrun, sizeof avenrun); if (tTd(3, 5)) - dprintf("getla: avenrun = %d\n", avenrun); + sm_dprintf("getla: avenrun = %d\n", avenrun); loadav = (int) (avenrun + FSCALE/2) >> FSHIFT; if (tTd(3, 1)) - dprintf("getla: %d\n", loadav); + sm_dprintf("getla: %d\n", loadav); return loadav; } @@ -2112,7 +1944,8 @@ struct rtentry; struct mbuf; # include <sys/table.h> -int getla() +int +getla() { int ave = 0; struct tbl_loadavg tab; @@ -2120,12 +1953,12 @@ int getla() if (table(TBL_LOADAVG, 0, &tab, 1, sizeof(tab)) == -1) { if (tTd(3, 1)) - dprintf("getla: table %s\n", errstring(errno)); + sm_dprintf("getla: table %s\n", sm_errstring(errno)); return -1; } if (tTd(3, 1)) - dprintf("getla: scale = %d\n", tab.tl_lscale); + sm_dprintf("getla: scale = %d\n", tab.tl_lscale); if (tab.tl_lscale) ave = ((tab.tl_avenrun.l[2] + (tab.tl_lscale/2)) / @@ -2134,7 +1967,7 @@ int getla() ave = (int) (tab.tl_avenrun.d[2] + 0.5); if (tTd(3, 1)) - dprintf("getla: %d\n", ave); + sm_dprintf("getla: %d\n", ave); return ave; } @@ -2143,7 +1976,7 @@ int getla() #if LA_TYPE == LA_PSET -static int +int getla() { double avenrun[3]; @@ -2152,12 +1985,12 @@ getla() sizeof(avenrun) / sizeof(avenrun[0])) < 0) { if (tTd(3, 1)) - dprintf("getla: pset_getloadavg failed: %s", - errstring(errno)); + sm_dprintf("getla: pset_getloadavg failed: %s", + sm_errstring(errno)); return -1; } if (tTd(3, 1)) - dprintf("getla: %d\n", (int) (avenrun[0] +0.5)); + sm_dprintf("getla: %d\n", (int) (avenrun[0] +0.5)); return ((int) (avenrun[0] + 0.5)); } @@ -2165,11 +1998,11 @@ getla() #if LA_TYPE == LA_ZERO -static int +int getla() { if (tTd(3, 1)) - dprintf("getla: ZERO\n"); + sm_dprintf("getla: ZERO\n"); return 0; } @@ -2200,7 +2033,7 @@ getla() /* Non Apollo stuff removed by Don Lewis 11/15/93 */ #ifndef lint -static char rcsid[] = "@(#)$OrigId: getloadavg.c,v 1.16 1991/06/21 12:51:15 paul Exp $"; +SM_UNUSED(static char rcsid[]) = "@(#)$OrigId: getloadavg.c,v 1.16 1991/06/21 12:51:15 paul Exp $"; #endif /* ! lint */ #ifdef apollo @@ -2209,61 +2042,57 @@ static char rcsid[] = "@(#)$OrigId: getloadavg.c,v 1.16 1991/06/21 12:51:15 pau /* ARGSUSED */ int getloadavg( call_data ) - caddr_t call_data; /* pointer to (double) return value */ + caddr_t call_data; /* pointer to (double) return value */ { - double *avenrun = (double *) call_data; - int i; - status_$t st; - long loadav[3]; - proc1_$get_loadav(loadav, &st); - *avenrun = loadav[0] / (double) (1 << 16); - return 0; + double *avenrun = (double *) call_data; + int i; + status_$t st; + long loadav[3]; + + proc1_$get_loadav(loadav, &st); + *avenrun = loadav[0] / (double) (1 << 16); + return 0; } #endif /* apollo */ /* -** SM_GETLA -- get the current load average and set macro +** SM_GETLA -- get the current load average ** ** Parameters: -** e -- the envelope for the load average macro. +** none ** ** Returns: -** The current load average as an integer. +** none ** ** Side Effects: -** Sets the load average macro ({load_avg}) if -** envelope e is not NULL. +** Set CurrentLA to the current load average. +** Set {load_avg} in GlobalMacros to the current load average. */ -int -sm_getla(e) - ENVELOPE *e; +void +sm_getla() { - register int la; - - la = getla(); - if (e != NULL) - { - char labuf[8]; + char labuf[8]; - snprintf(labuf, sizeof labuf, "%d", la); - define(macid("{load_avg}", NULL), newstr(labuf), e); - } - return la; + CurrentLA = getla(); + (void) sm_snprintf(labuf, sizeof labuf, "%d", CurrentLA); + macdefine(&GlobalMacros, A_TEMP, macid("{load_avg}"), labuf); } - /* ** SHOULDQUEUE -- should this message be queued or sent? ** ** Compares the message cost to the load average to decide. ** +** Note: Do NOT change this API! It is documented in op.me +** and theoretically the user can change this function... +** ** Parameters: ** pri -- the priority of the message in question. -** ct -- the message creation time. +** ct -- the message creation time (unused, but see above). ** ** Returns: -** TRUE -- if this message should be queued up for the +** true -- if this message should be queued up for the ** time being. -** FALSE -- if the load is low enough to send this message. +** false -- if the load is low enough to send this message. ** ** Side Effects: ** none. @@ -2278,25 +2107,25 @@ shouldqueue(pri, ct) bool rval; if (tTd(3, 30)) - dprintf("shouldqueue: CurrentLA=%d, pri=%ld: ", + sm_dprintf("shouldqueue: CurrentLA=%d, pri=%ld: ", CurrentLA, pri); if (CurrentLA < QueueLA) { if (tTd(3, 30)) - dprintf("FALSE (CurrentLA < QueueLA)\n"); - return FALSE; + sm_dprintf("false (CurrentLA < QueueLA)\n"); + return false; } -#if 0 /* this code is reported to cause oscillation around RefuseLA */ +# if 0 /* this code is reported to cause oscillation around RefuseLA */ if (CurrentLA >= RefuseLA && QueueLA < RefuseLA) { if (tTd(3, 30)) - dprintf("TRUE (CurrentLA >= RefuseLA)\n"); - return TRUE; + sm_dprintf("TRUE (CurrentLA >= RefuseLA)\n"); + return true; } -#endif /* 0 */ +# endif /* 0 */ rval = pri > (QueueFactor / (CurrentLA - QueueLA + 1)); if (tTd(3, 30)) - dprintf("%s (by calculation)\n", rval ? "TRUE" : "FALSE"); + sm_dprintf("%s (by calculation)\n", rval ? "true" : "false"); return rval; } /* @@ -2306,37 +2135,86 @@ shouldqueue(pri, ct) ** name -- daemon name (for error messages only) ** e -- the current envelope. ** d -- number of daemon +** active -- was this daemon actually active? ** ** Returns: -** TRUE if incoming SMTP connections should be refused +** true if incoming SMTP connections should be refused ** (for now). -** FALSE if we should accept new work. +** false if we should accept new work. ** ** Side Effects: ** Sets process title when it is rejecting connections. */ bool -refuseconnections(name, e, d) +refuseconnections(name, e, d, active) char *name; ENVELOPE *e; int d; + bool active; { -#ifdef XLA + static time_t lastconn[MAXDAEMONS]; + static int conncnt[MAXDAEMONS]; + +#if XLA if (!xla_smtp_ok()) - return TRUE; + return true; #endif /* XLA */ - CurrentLA = sm_getla(NULL); + if (ConnRateThrottle > 0) + { + time_t now; + + now = curtime(); + if (active) + { + if (now != lastconn[d]) + { + lastconn[d] = now; + conncnt[d] = 1; + } + else if (conncnt[d]++ > ConnRateThrottle) + { +#define D_MSG_CRT "deferring connections on daemon %s: %d per second" + /* sleep to flatten out connection load */ + sm_setproctitle(true, e, D_MSG_CRT, + name, ConnRateThrottle); + if (LogLevel > 8) + sm_syslog(LOG_INFO, NOQID, D_MSG_CRT, + name, ConnRateThrottle); + (void) sleep(1); + } + } + else if (now != lastconn[d]) + conncnt[d] = 0; + } + + sm_getla(); if (RefuseLA > 0 && CurrentLA >= RefuseLA) { - sm_setproctitle(TRUE, e, "rejecting connections on daemon %s: load average: %d", - name, CurrentLA); - if (LogLevel >= 9) - sm_syslog(LOG_INFO, NOQID, - "rejecting connections on daemon %s: load average: %d", - name, CurrentLA); - return TRUE; +# define R_MSG_LA "rejecting connections on daemon %s: load average: %d" + sm_setproctitle(true, e, R_MSG_LA, name, CurrentLA); + if (LogLevel > 8) + sm_syslog(LOG_INFO, NOQID, R_MSG_LA, name, CurrentLA); + return true; + } + + if (DelayLA > 0 && CurrentLA >= DelayLA) + { + time_t now; + static time_t log_delay = (time_t) 0; + +# define MIN_DELAY_LOG 90 /* wait before logging this again */ +# define D_MSG_LA "delaying connections on daemon %s: load average=%d >= %d" + /* sleep to flatten out connection load */ + sm_setproctitle(true, e, D_MSG_LA, name, DelayLA); + if (LogLevel > 8 && (now = curtime()) > log_delay) + { + sm_syslog(LOG_INFO, NOQID, D_MSG_LA, + name, CurrentLA, DelayLA); + log_delay = now + MIN_DELAY_LOG; + } + (void) sleep(1); } if (MaxChildren > 0 && CurChildren >= MaxChildren) @@ -2344,17 +2222,16 @@ refuseconnections(name, e, d) proc_list_probe(); if (CurChildren >= MaxChildren) { - sm_setproctitle(TRUE, e, "rejecting connections on daemon %s: %d children, max %d", +#define R_MSG_CHILD "rejecting connections on daemon %s: %d children, max %d" + sm_setproctitle(true, e, R_MSG_CHILD, name, CurChildren, MaxChildren); - if (LogLevel >= 9) - sm_syslog(LOG_INFO, NOQID, - "rejecting connections on daemon %s: %d children, max %d", + if (LogLevel > 8) + sm_syslog(LOG_INFO, NOQID, R_MSG_CHILD, name, CurChildren, MaxChildren); - return TRUE; + return true; } } - - return FALSE; + return false; } /* ** SETPROCTITLE -- set process title for ps @@ -2452,7 +2329,7 @@ initsetproctitle(argc, argv, envp) char **argv; char **envp; { - register int i, envpsize = 0; + register int i; extern char **environ; /* @@ -2461,7 +2338,7 @@ initsetproctitle(argc, argv, envp) */ for (i = 0; envp[i] != NULL; i++) - envpsize += strlen(envp[i]) + 1; + continue; environ = (char **) xalloc(sizeof (char *) * (i + 1)); for (i = 0; envp[i] != NULL; i++) environ[i] = newstr(envp[i]); @@ -2505,11 +2382,12 @@ setproctitle(fmt, va_alist) register int i; register char *p; SETPROC_STATIC char buf[SPT_BUFSIZE]; - VA_LOCAL_DECL + SM_VA_LOCAL_DECL # if SPT_TYPE == SPT_PSTAT union pstun pst; # endif /* SPT_TYPE == SPT_PSTAT */ # if SPT_TYPE == SPT_SCO + int j; off_t seek_off; static int kmem = -1; static pid_t kmempid = -1; @@ -2519,15 +2397,17 @@ setproctitle(fmt, va_alist) p = buf; /* print sendmail: heading for grep */ - (void) strlcpy(p, "sendmail: ", SPACELEFT(buf, p)); + (void) sm_strlcpy(p, "sendmail: ", SPACELEFT(buf, p)); p += strlen(p); /* print the argument string */ - VA_START(fmt); - (void) vsnprintf(p, SPACELEFT(buf, p), fmt, ap); - VA_END; + SM_VA_START(ap, fmt); + (void) sm_vsnprintf(p, SPACELEFT(buf, p), fmt, ap); + SM_VA_END(ap); - i = strlen(buf); + i = (int) strlen(buf); + if (i < 0) + return; # if SPT_TYPE == SPT_PSTAT pst.pst_command = buf; @@ -2541,15 +2421,21 @@ setproctitle(fmt, va_alist) sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf); # endif /* SPT_TYPE == SPT_SYSMIPS */ # if SPT_TYPE == SPT_SCO - if (kmem < 0 || kmempid != getpid()) + if (kmem < 0 || kmempid != CurrentPid) { if (kmem >= 0) (void) close(kmem); kmem = open(_PATH_KMEM, O_RDWR, 0); if (kmem < 0) return; - (void) fcntl(kmem, F_SETFD, FD_CLOEXEC); - kmempid = getpid(); + if ((j = fcntl(kmem, F_GETFD, 0)) < 0 || + fcntl(kmem, F_SETFD, j | FD_CLOEXEC) < 0) + { + (void) close(kmem); + kmem = -1; + return; + } + kmempid = CurrentPid; } buf[PSARGSZ - 1] = '\0'; seek_off = UVUBLK + (off_t) u.u_psargs - (off_t) &u; @@ -2565,7 +2451,7 @@ setproctitle(fmt, va_alist) i = LastArgv - Argv[0] - 2; buf[i] = '\0'; } - (void) strlcpy(Argv[0], buf, i + 1); + (void) sm_strlcpy(Argv[0], buf, i + 1); p = &Argv[0][i]; while (p < LastArgv) *p++ = SPT_PADCHAR; @@ -2608,15 +2494,15 @@ sm_setproctitle(status, e, fmt, va_alist) #endif /* __STDC__ */ { char buf[SPT_BUFSIZE]; - VA_LOCAL_DECL + SM_VA_LOCAL_DECL /* print the argument string */ - VA_START(fmt); - (void) vsnprintf(buf, sizeof buf, fmt, ap); - VA_END; + SM_VA_START(ap, fmt); + (void) sm_vsnprintf(buf, sizeof buf, fmt, ap); + SM_VA_END(ap); if (status) - proc_list_set(getpid(), buf); + proc_list_set(CurrentPid, buf); if (ProcTitlePrefix != NULL) { @@ -2660,15 +2546,15 @@ waitfor(pid) { errno = 0; # if defined(ISC_UNIX) || defined(_SCO_unix_) - savesig = releasesignal(SIGCHLD); + savesig = sm_releasesignal(SIGCHLD); # endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */ i = wait(&st); # if defined(ISC_UNIX) || defined(_SCO_unix_) if (savesig > 0) - blocksignal(SIGCHLD); + sm_blocksignal(SIGCHLD); # endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */ if (i > 0) - (void) proc_list_drop(i); + (void) proc_list_drop(i, NULL, NULL); } while ((i >= 0 || errno == EINTR) && i != pid); if (i < 0) return -1; @@ -2679,6 +2565,45 @@ waitfor(pid) # endif /* WAITUNION */ } /* +** SM_WAIT -- wait +** +** Parameters: +** status -- pointer to status (return value) +** +** Returns: +** pid +*/ + +pid_t +sm_wait(status) + int *status; +{ +# ifdef WAITUNION + union wait st; +# else /* WAITUNION */ + auto int st; +# endif /* WAITUNION */ + pid_t i; +# if defined(ISC_UNIX) || defined(_SCO_unix_) + int savesig; +# endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */ + +# if defined(ISC_UNIX) || defined(_SCO_unix_) + savesig = sm_releasesignal(SIGCHLD); +# endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */ + i = wait(&st); +# if defined(ISC_UNIX) || defined(_SCO_unix_) + if (savesig > 0) + sm_blocksignal(SIGCHLD); +# endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */ +# ifdef WAITUNION + *status = st.w_status; +# else /* WAITUNION */ + *status = st; +# endif /* WAITUNION */ + return i; +} +/* ** REAPCHILD -- pick up the body of my child, lest it become a zombie ** ** Parameters: @@ -2701,21 +2626,14 @@ SIGFUNC_DECL reapchild(sig) int sig; { + int m = 0, pld, wgrp; int save_errno = errno; int st; pid_t pid; # if HASWAITPID auto int status; int count; -# else /* HASWAITPID */ -# ifdef WNOHANG - union wait status; -# else /* WNOHANG */ - auto int status; -# endif /* WNOHANG */ -# endif /* HASWAITPID */ -# if HASWAITPID count = 0; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { @@ -2724,10 +2642,14 @@ reapchild(sig) break; # else /* HASWAITPID */ # ifdef WNOHANG + union wait status; + while ((pid = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0) { st = status.w_status; # else /* WNOHANG */ + auto int status; + /* ** Catch one zombie -- we will be re-invoked (we hope) if there ** are more. Unreliable signals probably break this, but this @@ -2741,170 +2663,29 @@ reapchild(sig) # endif /* WNOHANG */ # endif /* HASWAITPID */ /* Drop PID and check if it was a control socket child */ - if (proc_list_drop(pid) == PROC_CONTROL && - WIFEXITED(st)) + pld = proc_list_drop(pid, &m, &wgrp); + if (pld == PROC_CONTROL && WIFEXITED(st)) { /* if so, see if we need to restart or shutdown */ if (WEXITSTATUS(st) == EX_RESTART) - { RestartRequest = "control socket"; - } else if (WEXITSTATUS(st) == EX_SHUTDOWN) - { - /* emulate a SIGTERM shutdown */ ShutdownRequest = "control socket"; - /* NOTREACHED */ - } } + if (pld == PROC_QUEUE_CHILD && !WIFSTOPPED(st) && wgrp > -1) + { + /* restart this persistent runner */ + mark_work_group_restart(wgrp, st); + } + else if (pld == PROC_NONE) + m = 0; + CurRunners -= m; /* Update */ } FIX_SYSV_SIGNAL(sig, reapchild); errno = save_errno; return SIGFUNC_RETURN; } /* -** PUTENV -- emulation of putenv() in terms of setenv() -** -** Not needed on Posix-compliant systems. -** This doesn't have full Posix semantics, but it's good enough -** for sendmail. -** -** Parameter: -** env -- the environment to put. -** -** Returns: -** none. -*/ - -#if NEEDPUTENV - -# if NEEDPUTENV == 2 /* no setenv(3) call available */ - -int -putenv(str) - char *str; -{ - char **current; - int matchlen, envlen = 0; - char *tmp; - char **newenv; - static bool first = TRUE; - extern char **environ; - - /* - * find out how much of str to match when searching - * for a string to replace. - */ - if ((tmp = strchr(str, '=')) == NULL || tmp == str) - matchlen = strlen(str); - else - matchlen = (int) (tmp - str); - ++matchlen; - - /* - * Search for an existing string in the environment and find the - * length of environ. If found, replace and exit. - */ - for (current = environ; *current; current++) - { - ++envlen; - - if (strncmp(str, *current, matchlen) == 0) - { - /* found it, now insert the new version */ - *current = (char *)str; - return 0; - } - } - - /* - * There wasn't already a slot so add space for a new slot. - * If this is our first time through, use malloc(), else realloc(). - */ - if (first) - { - newenv = (char **) xalloc(sizeof(char *) * (envlen + 2)); - first = FALSE; - (void) memcpy(newenv, environ, sizeof(char *) * envlen); - } - else - { - newenv = (char **) xrealloc((char *)environ, - sizeof(char *) * (envlen + 2)); - } - - /* actually add in the new entry */ - environ = newenv; - environ[envlen] = (char *)str; - environ[envlen + 1] = NULL; - - return 0; -} - -# else /* NEEDPUTENV == 2 */ - -int -putenv(env) - char *env; -{ - char *p; - int l; - char nbuf[100]; - - p = strchr(env, '='); - if (p == NULL) - return 0; - l = p - env; - if (l > sizeof nbuf - 1) - l = sizeof nbuf - 1; - memmove(nbuf, env, l); - nbuf[l] = '\0'; - return setenv(nbuf, ++p, 1); -} - -# endif /* NEEDPUTENV == 2 */ -#endif /* NEEDPUTENV */ -/* -** UNSETENV -- remove a variable from the environment -** -** Not needed on newer systems. -** -** Parameters: -** name -- the string name of the environment variable to be -** deleted from the current environment. -** -** Returns: -** none. -** -** Globals: -** environ -- a pointer to the current environment. -** -** Side Effects: -** Modifies environ. -*/ - -#if !HASUNSETENV - -void -unsetenv(name) - char *name; -{ - extern char **environ; - register char **pp; - int len = strlen(name); - - for (pp = environ; *pp != NULL; pp++) - { - if (strncmp(name, *pp, len) == 0 && - ((*pp)[len] == '=' || (*pp)[len] == '\0')) - break; - } - - for (; *pp != NULL; pp++) - *pp = pp[1]; -} - -#endif /* !HASUNSETENV */ -/* ** GETDTABLESIZE -- return number of file descriptors ** ** Only on non-BSD systems @@ -2953,16 +2734,18 @@ int uname(name) struct utsname *name; { - FILE *file; + SM_FILE_T *file; char *n; name->nodename[0] = '\0'; /* try /etc/whoami -- one line with the node name */ - if ((file = fopen("/etc/whoami", "r")) != NULL) + if ((file = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, "/etc/whoami", + SM_IO_RDONLY, NULL)) != NULL) { - (void) fgets(name->nodename, NODE_LENGTH + 1, file); - (void) fclose(file); + (void) sm_io_fgets(file, SM_TIME_DEFAULT, name->nodename, + NODE_LENGTH + 1); + (void) sm_io_close(file, SM_TIME_DEFAULT); n = strchr(name->nodename, '\n'); if (n != NULL) *n = '\0'; @@ -2971,17 +2754,19 @@ uname(name) } /* try /usr/include/whoami.h -- has a #define somewhere */ - if ((file = fopen("/usr/include/whoami.h", "r")) != NULL) + if ((file = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, + "/usr/include/whoami.h", SM_IO_RDONLY, NULL)) + != NULL) { char buf[MAXLINE]; - while (fgets(buf, MAXLINE, file) != NULL) + while (sm_io_fgets(file, SM_TIME_DEFAULT, buf, MAXLINE) != NULL) { - if (sscanf(buf, "#define sysname \"%*[^\"]\"", + if (sm_io_sscanf(buf, "#define sysname \"%*[^\"]\"", NODE_LENGTH, name->nodename) > 0) break; } - (void) fclose(file); + (void) sm_io_close(file, SM_TIME_DEFAULT); if (name->nodename[0] != '\0') return 0; } @@ -2994,7 +2779,8 @@ uname(name) /* try uuname -l to return local name */ if ((file = popen("uuname -l", "r")) != NULL) { - (void) fgets(name, NODE_LENGTH + 1, file); + (void) sm_io_fgets(file, SM_TIME_DEFAULT, name, + NODE_LENGTH + 1); (void) pclose(file); n = strchr(name, '\n'); if (n != NULL) @@ -3062,7 +2848,7 @@ setsid __P ((void)) # ifdef SYS5SETPGRP return setpgrp(); # else /* SYS5SETPGRP */ - return setpgid(0, getpid()); + return setpgid(0, CurrentPid); # endif /* SYS5SETPGRP */ } @@ -3112,7 +2898,7 @@ dgux_inet_addr(host) ** GETOPT -- for old systems or systems with bogus implementations */ -#if NEEDGETOPT +#if !SM_CONF_GETOPT /* * Copyright (c) 1985 Regents of the University of California. @@ -3131,8 +2917,8 @@ static char sccsid[] = "@(#)getopt.c 4.3 (Berkeley) 3/9/86"; # endif /* defined(LIBC_SCCS) && !defined(lint) */ /* - * get option letter from argument vector - */ +** get option letter from argument vector +*/ # ifdef _CONVEX_SOURCE extern int optind, opterr, optopt; extern char *optarg; @@ -3145,8 +2931,12 @@ char *optarg = NULL; /* argument associated with option */ # define BADCH (int)'?' # define EMSG "" -# define tell(s) if (opterr) {fputs(*nargv,stderr);fputs(s,stderr); \ - fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);} +# define tell(s) if (opterr) \ + {sm_io_fputs(smioerr, SM_TIME_DEFAULT, *nargv); \ + (void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, s); \ + (void) sm_io_putc(smioerr, SM_TIME_DEFAULT, optopt); \ + (void) sm_io_putc(smioerr, SM_TIME_DEFAULT, '\n'); \ + return BADCH;} int getopt(nargc,nargv,ostr) @@ -3191,51 +2981,10 @@ getopt(nargc,nargv,ostr) place = EMSG; ++optind; } - return(optopt); /* dump back option letter */ + return optopt; /* dump back option letter */ } -#endif /* NEEDGETOPT */ -/* -** VFPRINTF, VSPRINTF -- for old 4.3 BSD systems missing a real version -*/ - -#if NEEDVPRINTF - -# define MAXARG 16 - -vfprintf(fp, fmt, ap) - FILE *fp; - char *fmt; - char **ap; -{ - char *bp[MAXARG]; - int i = 0; - - while (*ap && i < MAXARG) - bp[i++] = *ap++; - fprintf(fp, fmt, bp[0], bp[1], bp[2], bp[3], - bp[4], bp[5], bp[6], bp[7], - bp[8], bp[9], bp[10], bp[11], - bp[12], bp[13], bp[14], bp[15]); -} - -vsprintf(s, fmt, ap) - char *s; - char *fmt; - char **ap; -{ - char *bp[MAXARG]; - int i = 0; - - while (*ap && i < MAXARG) - bp[i++] = *ap++; - sprintf(s, fmt, bp[0], bp[1], bp[2], bp[3], - bp[4], bp[5], bp[6], bp[7], - bp[8], bp[9], bp[10], bp[11], - bp[12], bp[13], bp[14], bp[15]); -} - -#endif /* NEEDVPRINTF */ +#endif /* !SM_CONF_GETOPT */ /* ** USERSHELLOK -- tell if a user's shell is ok for unrestricted use ** @@ -3244,8 +2993,8 @@ vsprintf(s, fmt, ap) ** shell -- the user's shell from /etc/passwd ** ** Returns: -** TRUE -- if it is ok to use this for unrestricted access. -** FALSE -- if the shell is restricted. +** true -- if it is ok to use this for unrestricted access. +** false -- if the shell is restricted. */ #if !HASGETUSERSHELL @@ -3324,7 +3073,7 @@ usershellok(user, shell) if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') || ConfigLevel <= 1) - return TRUE; + return true; setusershell(); while ((p = getusershell()) != NULL) @@ -3336,12 +3085,12 @@ usershellok(user, shell) # if USEGETCONFATTR auto char *v; # endif /* USEGETCONFATTR */ - register FILE *shellf; + register SM_FILE_T *shellf; char buf[MAXLINE]; if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') || ConfigLevel <= 1) - return TRUE; + return true; # if USEGETCONFATTR /* @@ -3362,14 +3111,15 @@ usershellok(user, shell) while (*v != '\0') { if (strcmp(v, shell) == 0 || strcmp(v, WILDCARD_SHELL) == 0) - return TRUE; + return true; v += strlen(v) + 1; } - return FALSE; + return false; } # endif /* USEGETCONFATTR */ - shellf = fopen(_PATH_SHELLS, "r"); + shellf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, _PATH_SHELLS, + SM_IO_RDONLY, NULL); if (shellf == NULL) { /* no /etc/shells; see if it is one of the std shells */ @@ -3378,17 +3128,17 @@ usershellok(user, shell) if (errno != ENOENT && LogLevel > 3) sm_syslog(LOG_ERR, NOQID, "usershellok: cannot open %s: %s", - _PATH_SHELLS, errstring(errno)); + _PATH_SHELLS, sm_errstring(errno)); for (d = DefaultUserShells; *d != NULL; d++) { if (strcmp(shell, *d) == 0) - return TRUE; + return true; } - return FALSE; + return false; } - while (fgets(buf, sizeof buf, shellf) != NULL) + while (sm_io_fgets(shellf, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) { register char *p, *q; @@ -3403,12 +3153,12 @@ usershellok(user, shell) *p = '\0'; if (strcmp(shell, q) == 0 || strcmp(WILDCARD_SHELL, q) == 0) { - (void) fclose(shellf); - return TRUE; + (void) sm_io_close(shellf, SM_TIME_DEFAULT); + return true; } } - (void) fclose(shellf); - return FALSE; + (void) sm_io_close(shellf, SM_TIME_DEFAULT); + return false; # endif /* HASGETUSERSHELL */ } /* @@ -3430,33 +3180,33 @@ usershellok(user, shell) */ /* statfs types */ -#define SFS_NONE 0 /* no statfs implementation */ -#define SFS_USTAT 1 /* use ustat */ -#define SFS_4ARGS 2 /* use four-argument statfs call */ -#define SFS_VFS 3 /* use <sys/vfs.h> implementation */ -#define SFS_MOUNT 4 /* use <sys/mount.h> implementation */ -#define SFS_STATFS 5 /* use <sys/statfs.h> implementation */ -#define SFS_STATVFS 6 /* use <sys/statvfs.h> implementation */ - -#ifndef SFS_TYPE -# define SFS_TYPE SFS_NONE -#endif /* ! SFS_TYPE */ - -#if SFS_TYPE == SFS_USTAT -# include <ustat.h> -#endif /* SFS_TYPE == SFS_USTAT */ -#if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS -# include <sys/statfs.h> -#endif /* SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS */ -#if SFS_TYPE == SFS_VFS -# include <sys/vfs.h> -#endif /* SFS_TYPE == SFS_VFS */ -#if SFS_TYPE == SFS_MOUNT -# include <sys/mount.h> -#endif /* SFS_TYPE == SFS_MOUNT */ -#if SFS_TYPE == SFS_STATVFS -# include <sys/statvfs.h> -#endif /* SFS_TYPE == SFS_STATVFS */ +# define SFS_NONE 0 /* no statfs implementation */ +# define SFS_USTAT 1 /* use ustat */ +# define SFS_4ARGS 2 /* use four-argument statfs call */ +# define SFS_VFS 3 /* use <sys/vfs.h> implementation */ +# define SFS_MOUNT 4 /* use <sys/mount.h> implementation */ +# define SFS_STATFS 5 /* use <sys/statfs.h> implementation */ +# define SFS_STATVFS 6 /* use <sys/statvfs.h> implementation */ + +# ifndef SFS_TYPE +# define SFS_TYPE SFS_NONE +# endif /* ! SFS_TYPE */ + +# if SFS_TYPE == SFS_USTAT +# include <ustat.h> +# endif /* SFS_TYPE == SFS_USTAT */ +# if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS +# include <sys/statfs.h> +# endif /* SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS */ +# if SFS_TYPE == SFS_VFS +# include <sys/vfs.h> +# endif /* SFS_TYPE == SFS_VFS */ +# if SFS_TYPE == SFS_MOUNT +# include <sys/mount.h> +# endif /* SFS_TYPE == SFS_MOUNT */ +# if SFS_TYPE == SFS_STATVFS +# include <sys/statvfs.h> +# endif /* SFS_TYPE == SFS_STATVFS */ long freediskspace(dir, bsize) @@ -3519,63 +3269,45 @@ freediskspace(dir, bsize) return -1; } /* -** ENOUGHDISKSPACE -- is there enough free space on the queue fs? -** -** Only implemented if you have statfs. +** ENOUGHDISKSPACE -- is there enough free space on the queue file systems? ** ** Parameters: ** msize -- the size to check against. If zero, we don't yet ** know how big the message will be, so just check for ** a "reasonable" amount. -** log -- log message? +** e -- envelope, or NULL -- controls logging ** ** Returns: -** TRUE if there is enough space. -** FALSE otherwise. +** true if in every queue group there is at least one +** queue directory whose file system contains enough free space. +** false otherwise. +** +** Side Effects: +** If there is not enough disk space and e != NULL +** then sm_syslog is called. */ bool -enoughdiskspace(msize, log) +enoughdiskspace(msize, e) long msize; - bool log; + ENVELOPE *e; { - long bfree; - long bsize; + int i; if (MinBlocksFree <= 0 && msize <= 0) { if (tTd(4, 80)) - dprintf("enoughdiskspace: no threshold\n"); - return TRUE; + sm_dprintf("enoughdiskspace: no threshold\n"); + return true; } - bfree = freediskspace(QueueDir, &bsize); - if (bfree >= 0) + filesys_update(); + for (i = 0; i < NumQueue; ++i) { - if (tTd(4, 80)) - dprintf("enoughdiskspace: bavail=%ld, need=%ld\n", - bfree, msize); - - /* convert msize to block count */ - msize = msize / bsize + 1; - if (MinBlocksFree >= 0) - msize += MinBlocksFree; - - if (bfree < msize) - { - if (log && LogLevel > 0) - sm_syslog(LOG_ALERT, CurEnv->e_id, - "low on space (have %ld, %s needs %ld in %s)", - bfree, - CurHostName == NULL ? "SMTP-DAEMON" : CurHostName, - msize, QueueDir); - return FALSE; - } + if (pickqdir(Queue[i], msize, e) < 0) + return false; } - else if (tTd(4, 80)) - dprintf("enoughdiskspace failure: min=%ld, need=%ld: %s\n", - MinBlocksFree, msize, errstring(errno)); - return TRUE; + return true; } /* ** TRANSIENTERROR -- tell if an error code indicates a transient failure @@ -3587,8 +3319,8 @@ enoughdiskspace(msize, log) ** err -- the errno code to classify. ** ** Returns: -** TRUE if this is probably transient. -** FALSE otherwise. +** true if this is probably transient. +** false otherwise. */ bool @@ -3605,9 +3337,7 @@ transienterror(err) case ENFILE: /* Too many open files in system */ case EMFILE: /* Too many open files */ case ENOSPC: /* No space left on device */ -#ifdef ETIMEDOUT case ETIMEDOUT: /* Connection timed out */ -#endif /* ETIMEDOUT */ #ifdef ESTALE case ESTALE: /* Stale NFS file handle */ #endif /* ESTALE */ @@ -3678,11 +3408,11 @@ transienterror(err) case ENOLCK: /* No locks available */ #endif /* ENOLCK */ case E_SM_OPENTIMEOUT: /* PSEUDO: open timed out */ - return TRUE; + return true; } /* nope, must be permanent */ - return FALSE; + return false; } /* ** LOCKFILE -- lock a file using flock or (shudder) fcntl locking @@ -3697,8 +3427,8 @@ transienterror(err) ** LOCK_UN -- unlock. ** ** Returns: -** TRUE if the lock was acquired. -** FALSE otherwise. +** true if the lock was acquired. +** false otherwise. */ bool @@ -3731,7 +3461,7 @@ lockfile(fd, filename, ext, type) action = F_SETLKW; if (tTd(55, 60)) - dprintf("lockfile(%s%s, action=%d, type=%d): ", + sm_dprintf("lockfile(%s%s, action=%d, type=%d): ", filename, ext, action, lfd.l_type); while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR) @@ -3739,13 +3469,13 @@ lockfile(fd, filename, ext, type) if (i >= 0) { if (tTd(55, 60)) - dprintf("SUCCESS\n"); - return TRUE; + sm_dprintf("SUCCESS\n"); + return true; } save_errno = errno; if (tTd(55, 60)) - dprintf("(%s) ", errstring(save_errno)); + sm_dprintf("(%s) ", sm_errstring(save_errno)); /* ** On SunOS, if you are testing using -oQ/tmp/mqueue or @@ -3759,8 +3489,8 @@ lockfile(fd, filename, ext, type) if (save_errno == EINVAL) { if (tTd(55, 60)) - dprintf("SUCCESS\n"); - return TRUE; + sm_dprintf("SUCCESS\n"); + return true; } if (!bitset(LOCK_NB, type) || @@ -3773,27 +3503,27 @@ lockfile(fd, filename, ext, type) # endif /* F_GETFL */ syserr("cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", filename, ext, fd, type, omode, geteuid()); - dumpfd(fd, TRUE, TRUE); + dumpfd(fd, true, true); } # else /* !HASFLOCK */ if (ext == NULL) ext = ""; if (tTd(55, 60)) - dprintf("lockfile(%s%s, type=%o): ", filename, ext, type); + sm_dprintf("lockfile(%s%s, type=%o): ", filename, ext, type); while ((i = flock(fd, type)) < 0 && errno == EINTR) continue; if (i >= 0) { if (tTd(55, 60)) - dprintf("SUCCESS\n"); - return TRUE; + sm_dprintf("SUCCESS\n"); + return true; } save_errno = errno; if (tTd(55, 60)) - dprintf("(%s) ", errstring(save_errno)); + sm_dprintf("(%s) ", sm_errstring(save_errno)); if (!bitset(LOCK_NB, type) || save_errno != EWOULDBLOCK) { @@ -3804,13 +3534,13 @@ lockfile(fd, filename, ext, type) # endif /* F_GETFL */ syserr("cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", filename, ext, fd, type, omode, geteuid()); - dumpfd(fd, TRUE, TRUE); + dumpfd(fd, true, true); } # endif /* !HASFLOCK */ if (tTd(55, 60)) - dprintf("FAIL\n"); + sm_dprintf("FAIL\n"); errno = save_errno; - return FALSE; + return false; } /* ** CHOWNSAFE -- tell if chown is "safe" (executable only by root) @@ -3860,9 +3590,9 @@ lockfile(fd, filename, ext, type) ** safedir -- set if the parent directory is safe. ** ** Returns: -** TRUE -- if the chown(2) operation is "safe" -- that is, +** true -- if the chown(2) operation is "safe" -- that is, ** only root can chown the file to an arbitrary user. -** FALSE -- if an arbitrary user can give away a file. +** false -- if an arbitrary user can give away a file. */ #ifndef IS_SAFE_CHOWN @@ -3880,7 +3610,7 @@ chownsafe(fd, safedir) /* give the system administrator a chance to override */ if (bitnset(DBS_ASSUMESAFECHOWN, DontBlameSendmail)) - return TRUE; + return true; /* ** Some systems (e.g., SunOS) seem to have the call and the @@ -3895,9 +3625,9 @@ chownsafe(fd, safedir) # else /* SAFENFSPATHCONF */ return safedir && errno == 0 && rval IS_SAFE_CHOWN; # endif /* SAFENFSPATHCONF */ -# else /* (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && \ */ +# else /* (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && ... */ return bitnset(DBS_ASSUMESAFECHOWN, DontBlameSendmail); -# endif /* (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && \ */ +# endif /* (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && ... */ } /* ** RESETLIMITS -- reset system controlled resource limits @@ -3943,39 +3673,14 @@ resetlimits() errno = 0; } /* -** GETCFNAME -- return the name of the .cf file. -** -** Some systems (e.g., NeXT) determine this dynamically. -*/ - -char * -getcfname() -{ - - if (ConfFile != NULL) - return ConfFile; -#if NETINFO - { - char *cflocation; - - cflocation = ni_propval("/locations", NULL, "sendmail", - "sendmail.cf", '\0'); - if (cflocation != NULL) - return cflocation; - } -#endif /* NETINFO */ - - return _PATH_SENDMAILCF; -} -/* ** SETVENDOR -- process vendor code from V configuration line ** ** Parameters: ** vendor -- string representation of vendor. ** ** Returns: -** TRUE -- if ok. -** FALSE -- if vendor code could not be processed. +** true -- if ok. +** false -- if vendor code could not be processed. ** ** Side Effects: ** It is reasonable to set mode flags here to tweak @@ -3988,31 +3693,31 @@ bool setvendor(vendor) char *vendor; { - if (strcasecmp(vendor, "Berkeley") == 0) + if (sm_strcasecmp(vendor, "Berkeley") == 0) { VendorCode = VENDOR_BERKELEY; - return TRUE; + return true; } /* add vendor extensions here */ #ifdef SUN_EXTENSIONS - if (strcasecmp(vendor, "Sun") == 0) + if (sm_strcasecmp(vendor, "Sun") == 0) { VendorCode = VENDOR_SUN; - return TRUE; + return true; } #endif /* SUN_EXTENSIONS */ #if defined(VENDOR_NAME) && defined(VENDOR_CODE) - if (strcasecmp(vendor, VENDOR_NAME) == 0) + if (sm_strcasecmp(vendor, VENDOR_NAME) == 0) { VendorCode = VENDOR_CODE; - return TRUE; + return true; } #endif /* defined(VENDOR_NAME) && defined(VENDOR_CODE) */ - return FALSE; + return false; } /* ** GETVENDOR -- return vendor name based on vendor code @@ -4041,23 +3746,23 @@ getvendor(vendorcode) switch (vendorcode) { - case VENDOR_BERKELEY: - return "Berkeley"; + case VENDOR_BERKELEY: + return "Berkeley"; - case VENDOR_SUN: - return "Sun"; + case VENDOR_SUN: + return "Sun"; - case VENDOR_HP: - return "HP"; + case VENDOR_HP: + return "HP"; - case VENDOR_IBM: - return "IBM"; + case VENDOR_IBM: + return "IBM"; - case VENDOR_SENDMAIL: - return "Sendmail"; + case VENDOR_SENDMAIL: + return "Sendmail"; - default: - return "Unknown"; + default: + return "Unknown"; } } /* @@ -4130,7 +3835,7 @@ vendor_daemon_setup(e) if (getluid() != -1) { usrerr("Daemon cannot have LUID"); - finis(FALSE, EX_USAGE); + finis(false, EX_USAGE); } #endif /* SECUREWARE */ } @@ -4187,60 +3892,68 @@ int allow_severity = LOG_INFO; int deny_severity = LOG_NOTICE; #endif /* TCPWRAPPERS */ -#if DAEMON char * validate_connection(sap, hostname, e) SOCKADDR *sap; char *hostname; ENVELOPE *e; { -# if TCPWRAPPERS +#if TCPWRAPPERS char *host; -# endif /* TCPWRAPPERS */ + char *addr; + extern int hosts_ctl(); +#endif /* TCPWRAPPERS */ if (tTd(48, 3)) - dprintf("validate_connection(%s, %s)\n", + sm_dprintf("validate_connection(%s, %s)\n", hostname, anynet_ntoa(sap)); if (rscheck("check_relay", hostname, anynet_ntoa(sap), - e, TRUE, TRUE, 4, NULL) != EX_OK) + e, true, true, 3, NULL, NOQID) != EX_OK) { static char reject[BUFSIZ*2]; extern char MsgBuf[]; if (tTd(48, 4)) - dprintf(" ... validate_connection: BAD (rscheck)\n"); + sm_dprintf(" ... validate_connection: BAD (rscheck)\n"); if (strlen(MsgBuf) >= 3) - (void) strlcpy(reject, MsgBuf, sizeof reject); + (void) sm_strlcpy(reject, MsgBuf, sizeof reject); else - (void) strlcpy(reject, "Access denied", sizeof reject); + (void) sm_strlcpy(reject, "Access denied", sizeof reject); return reject; } -# if TCPWRAPPERS +#if TCPWRAPPERS if (hostname[0] == '[' && hostname[strlen(hostname) - 1] == ']') host = "unknown"; else host = hostname; - if (!hosts_ctl("sendmail", host, anynet_ntoa(sap), STRING_UNKNOWN)) + addr = anynet_ntoa(sap); + +# if NETINET6 + /* TCP/Wrappers don't want the IPv6: protocol label */ + if (addr != NULL && sm_strncasecmp(addr, "IPv6:", 5) == 0) + addr += 5; +# endif /* NETINET6 */ + + if (!hosts_ctl("sendmail", host, addr, STRING_UNKNOWN)) { if (tTd(48, 4)) - dprintf(" ... validate_connection: BAD (tcpwrappers)\n"); - if (LogLevel >= 4) + sm_dprintf(" ... validate_connection: BAD (tcpwrappers)\n"); + if (LogLevel > 3) sm_syslog(LOG_NOTICE, e->e_id, - "tcpwrappers (%s, %s) rejection", - host, anynet_ntoa(sap)); + "tcpwrappers (%s, %s) rejection", + host, addr); return "Access denied"; } -# endif /* TCPWRAPPERS */ +#endif /* TCPWRAPPERS */ if (tTd(48, 4)) - dprintf(" ... validate_connection: OK\n"); + sm_dprintf(" ... validate_connection: OK\n"); return NULL; } -#endif /* DAEMON */ /* ** STRTOL -- convert string to long integer ** @@ -4256,11 +3969,11 @@ static char sccsid[] = "@(#)strtol.c 8.1 (Berkeley) 6/4/93"; # endif /* defined(LIBC_SCCS) && !defined(lint) */ /* - * Convert a string to a long integer. - * - * Ignores `locale' stuff. Assumes that the upper and lower case - * alphabets and digits are each contiguous. - */ +** Convert a string to a long integer. +** +** Ignores `locale' stuff. Assumes that the upper and lower case +** alphabets and digits are each contiguous. +*/ long strtol(nptr, endptr, base) @@ -4275,10 +3988,10 @@ strtol(nptr, endptr, base) register int neg = 0, any, cutlim; /* - * Skip white space and pick up leading +/- sign if any. - * If base is 0, allow 0x for hex and 0 for octal, else - * assume decimal; if base is already 16, allow 0x. - */ + ** Skip white space and pick up leading +/- sign if any. + ** If base is 0, allow 0x for hex and 0 for octal, else + ** assume decimal; if base is already 16, allow 0x. + */ do { c = *s++; } while (isspace(c)); @@ -4297,25 +4010,25 @@ strtol(nptr, endptr, base) base = c == '0' ? 8 : 10; /* - * Compute the cutoff value between legal numbers and illegal - * numbers. That is the largest legal value, divided by the - * base. An input number that is greater than this value, if - * followed by a legal input character, is too big. One that - * is equal to this value may be valid or not; the limit - * between valid and invalid numbers is then based on the last - * digit. For instance, if the range for longs is - * [-2147483648..2147483647] and the input base is 10, - * cutoff will be set to 214748364 and cutlim to either - * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated - * a value > 214748364, or equal but the next digit is > 7 (or 8), - * the number is too big, and we will return a range error. - * - * Set any if any `digits' consumed; make it negative to indicate - * overflow. - */ - cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX; - cutlim = cutoff % (unsigned long)base; - cutoff /= (unsigned long)base; + ** Compute the cutoff value between legal numbers and illegal + ** numbers. That is the largest legal value, divided by the + ** base. An input number that is greater than this value, if + ** followed by a legal input character, is too big. One that + ** is equal to this value may be valid or not; the limit + ** between valid and invalid numbers is then based on the last + ** digit. For instance, if the range for longs is + ** [-2147483648..2147483647] and the input base is 10, + ** cutoff will be set to 214748364 and cutlim to either + ** 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated + ** a value > 214748364, or equal but the next digit is > 7 (or 8), + ** the number is too big, and we will return a range error. + ** + ** Set any if any `digits' consumed; make it negative to indicate + ** overflow. + */ + cutoff = neg ? -(unsigned long) LONG_MIN : LONG_MAX; + cutlim = cutoff % (unsigned long) base; + cutoff /= (unsigned long) base; for (acc = 0, any = 0;; c = *s++) { if (isdigit(c)) c -= '0'; @@ -4414,7 +4127,7 @@ getipnodebyname(name, family, flags, err) int flags; int *err; { - bool resv6 = TRUE; + bool resv6 = true; struct hostent *h; if (family == AF_INET6) @@ -4425,9 +4138,9 @@ getipnodebyname(name, family, flags, err) } SM_SET_H_ERRNO(0); h = gethostbyname(name); - *err = h_errno; - if (family == AF_INET6 && !resv6) + if (!resv6) _res.options &= ~RES_USE_INET6; + *err = h_errno; return h; } @@ -4446,7 +4159,6 @@ getipnodebyaddr(addr, len, family, err) return h; } -# if _FFR_FREEHOSTENT void freehostent(h) struct hostent *h; @@ -4458,8 +4170,7 @@ freehostent(h) return; } -# endif /* _FFR_FREEHOSTENT */ -#endif /* NEEDSGETIPNODE && NETINET6 */ +#endif /* NETINET6 && NEEDSGETIPNODE */ struct hostent * sm_gethostbyname(name, family) @@ -4475,14 +4186,14 @@ sm_gethostbyname(name, family) extern struct hostent *_switch_gethostbyname_r(); if (tTd(61, 10)) - dprintf("_switch_gethostbyname_r(%s)... ", name); + sm_dprintf("_switch_gethostbyname_r(%s)... ", name); h = _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno); save_errno = errno; # else /* SOLARIS == 20300 || SOLARIS == 203 */ extern struct hostent *__switch_gethostbyname(); if (tTd(61, 10)) - dprintf("__switch_gethostbyname(%s)... ", name); + sm_dprintf("__switch_gethostbyname(%s)... ", name); h = __switch_gethostbyname(name); save_errno = errno; # endif /* SOLARIS == 20300 || SOLARIS == 203 */ @@ -4497,7 +4208,7 @@ sm_gethostbyname(name, family) char hbuf[MAXNAME]; if (tTd(61, 10)) - dprintf("sm_gethostbyname(%s, %d)... ", name, family); + sm_dprintf("sm_gethostbyname(%s, %d)... ", name, family); # if NETINET6 # if ADDRCONFIG_IS_BROKEN @@ -4513,7 +4224,7 @@ sm_gethostbyname(name, family) if (h == NULL) { if (tTd(61, 10)) - dprintf("failure\n"); + sm_dprintf("failure\n"); nmaps = switch_map_find("hosts", maptype, mapreturn); while (--nmaps >= 0) @@ -4526,19 +4237,19 @@ sm_gethostbyname(name, family) if (nmaps >= 0) { /* try short name */ - if (strlen(name) > (SIZE_T) sizeof hbuf - 1) + if (strlen(name) > sizeof hbuf - 1) { errno = save_errno; return NULL; } - (void) strlcpy(hbuf, name, sizeof hbuf); + (void) sm_strlcpy(hbuf, name, sizeof hbuf); (void) shorten_hostname(hbuf); /* if it hasn't been shortened, there's no point */ if (strcmp(hbuf, name) != 0) { if (tTd(61, 10)) - dprintf("sm_gethostbyname(%s, %d)... ", + sm_dprintf("sm_gethostbyname(%s, %d)... ", hbuf, family); # if NETINET6 @@ -4556,10 +4267,10 @@ sm_gethostbyname(name, family) if (tTd(61, 10)) { if (h == NULL) - dprintf("failure\n"); + sm_dprintf("failure\n"); else { - dprintf("%s\n", h->h_name); + sm_dprintf("%s\n", h->h_name); if (tTd(61, 11)) { #if NETINET6 @@ -4568,12 +4279,12 @@ sm_gethostbyname(name, family) #else /* NETINET6 */ struct in_addr ia; #endif /* NETINET6 */ - int i; + size_t i; if (h->h_aliases != NULL) for (i = 0; h->h_aliases[i] != NULL; i++) - dprintf("\talias: %s\n", + sm_dprintf("\talias: %s\n", h->h_aliases[i]); for (i = 0; h->h_addr_list[i] != NULL; i++) { @@ -4590,7 +4301,7 @@ sm_gethostbyname(name, family) addr = (char *) inet_ntoa(ia); #endif /* NETINET6 */ if (addr != NULL) - dprintf("\taddr: %s\n", addr); + sm_dprintf("\taddr: %s\n", addr); } } } @@ -4652,31 +4363,30 @@ sm_gethostbyaddr(addr, len, type) ** SM_GETPW{NAM,UID} -- wrapper for getpwnam and getpwuid */ - struct passwd * sm_getpwnam(user) char *user; { -# ifdef _AIX4 +#ifdef _AIX4 extern struct passwd *_getpwnam_shadow(const char *, const int); return _getpwnam_shadow(user, 0); -# else /* _AIX4 */ +#else /* _AIX4 */ return getpwnam(user); -# endif /* _AIX4 */ +#endif /* _AIX4 */ } struct passwd * sm_getpwuid(uid) UID_T uid; { -# if defined(_AIX4) && 0 +#if defined(_AIX4) && 0 extern struct passwd *_getpwuid_shadow(const int, const int); return _getpwuid_shadow(uid,0); -# else /* defined(_AIX4) && 0 */ +#else /* defined(_AIX4) && 0 */ return getpwuid(uid); -# endif /* defined(_AIX4) && 0 */ +#endif /* defined(_AIX4) && 0 */ } /* ** SECUREWARE_SETUP_SECURE -- Convex SecureWare setup @@ -4713,30 +4423,34 @@ secureware_setup_secure(uid) switch (rc) { case SSI_NO_PRPW_ENTRY: - syserr("No protected passwd entry, uid = %d", uid); + syserr("No protected passwd entry, uid = %d", + (int) uid); break; case SSI_LOCKED: - syserr("Account has been disabled, uid = %d", uid); + syserr("Account has been disabled, uid = %d", + (int) uid); break; case SSI_RETIRED: - syserr("Account has been retired, uid = %d", uid); + syserr("Account has been retired, uid = %d", + (int) uid); break; case SSI_BAD_SET_LUID: - syserr("Could not set LUID, uid = %d", uid); + syserr("Could not set LUID, uid = %d", (int) uid); break; case SSI_BAD_SET_PRIVS: - syserr("Could not set kernel privs, uid = %d", uid); + syserr("Could not set kernel privs, uid = %d", + (int) uid); default: syserr("Unknown return code (%d) from set_secure_info(%d)", - rc, uid); + rc, (int) uid); break; } - finis(FALSE, EX_NOPERM); + finis(false, EX_NOPERM); } } #endif /* SECUREWARE */ @@ -4765,28 +4479,28 @@ add_hostnames(sa) switch (sa->sa.sa_family) { #if NETINET - case AF_INET: - hp = sm_gethostbyaddr((char *) &sa->sin.sin_addr, - sizeof(sa->sin.sin_addr), - sa->sa.sa_family); - break; + case AF_INET: + hp = sm_gethostbyaddr((char *) &sa->sin.sin_addr, + sizeof(sa->sin.sin_addr), + sa->sa.sa_family); + break; #endif /* NETINET */ #if NETINET6 - case AF_INET6: - hp = sm_gethostbyaddr((char *) &sa->sin6.sin6_addr, - sizeof(sa->sin6.sin6_addr), - sa->sa.sa_family); - break; + case AF_INET6: + hp = sm_gethostbyaddr((char *) &sa->sin6.sin6_addr, + sizeof(sa->sin6.sin6_addr), + sa->sa.sa_family); + break; #endif /* NETINET6 */ - default: - /* Give warning about unsupported family */ - if (LogLevel > 3) - sm_syslog(LOG_WARNING, NOQID, - "Unsupported address family %d: %.100s", - sa->sa.sa_family, anynet_ntoa(sa)); - return -1; + default: + /* Give warning about unsupported family */ + if (LogLevel > 3) + sm_syslog(LOG_WARNING, NOQID, + "Unsupported address family %d: %.100s", + sa->sa.sa_family, anynet_ntoa(sa)); + return -1; } if (hp == NULL) @@ -4798,16 +4512,16 @@ add_hostnames(sa) !(sa->sa.sa_family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&sa->sin6.sin6_addr)) && #endif /* NETINET6 */ - TRUE) + true) sm_syslog(LOG_WARNING, NOQID, - "gethostbyaddr(%.100s) failed: %d\n", - anynet_ntoa(sa), + "gethostbyaddr(%.100s) failed: %d", + anynet_ntoa(sa), #if NAMED_BIND - h_errno + h_errno #else /* NAMED_BIND */ - -1 + -1 #endif /* NAMED_BIND */ - ); + ); errno = save_errno; return -1; } @@ -4817,16 +4531,16 @@ add_hostnames(sa) { setclass('w', (char *) hp->h_name); if (tTd(0, 4)) - dprintf("\ta.k.a.: %s\n", hp->h_name); + sm_dprintf("\ta.k.a.: %s\n", hp->h_name); - if (snprintf(hnb, sizeof hnb, "[%s]", hp->h_name) < sizeof hnb + if (sm_snprintf(hnb, sizeof hnb, "[%s]", hp->h_name) < sizeof hnb && !wordinclass((char *) hnb, 'w')) setclass('w', hnb); } else { if (tTd(0, 43)) - dprintf("\ta.k.a.: %s (already in $=w)\n", hp->h_name); + sm_dprintf("\ta.k.a.: %s (already in $=w)\n", hp->h_name); } /* save all it aliases name */ @@ -4836,8 +4550,8 @@ add_hostnames(sa) { setclass('w', *ha); if (tTd(0, 4)) - dprintf("\ta.k.a.: %s\n", *ha); - if (snprintf(hnb, sizeof hnb, + sm_dprintf("\ta.k.a.: %s\n", *ha); + if (sm_snprintf(hnb, sizeof hnb, "[%s]", *ha) < sizeof hnb && !wordinclass((char *) hnb, 'w')) setclass('w', hnb); @@ -4845,13 +4559,13 @@ add_hostnames(sa) else { if (tTd(0, 43)) - dprintf("\ta.k.a.: %s (already in $=w)\n", + sm_dprintf("\ta.k.a.: %s (already in $=w)\n", *ha); } } -#if _FFR_FREEHOSTENT && NETINET6 +#if NETINET6 freehostent(hp); -#endif /* _FFR_FREEHOSTENT && NETINET6 */ +#endif /* NETINET6 */ return 0; } /* @@ -4886,7 +4600,7 @@ struct mbuf; void load_if_names() { -#if NETINET6 && defined(SIOCGLIFCONF) +# if NETINET6 && defined(SIOCGLIFCONF) int s; int i; struct lifconf lifc; @@ -4898,24 +4612,25 @@ load_if_names() return; /* get the list of known IP address from the kernel */ -# ifdef SIOCGLIFNUM +# ifdef SIOCGLIFNUM lifn.lifn_family = AF_UNSPEC; lifn.lifn_flags = 0; if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) { /* can't get number of interfaces -- fall back */ if (tTd(0, 4)) - dprintf("SIOCGLIFNUM failed: %s\n", errstring(errno)); + sm_dprintf("SIOCGLIFNUM failed: %s\n", + sm_errstring(errno)); numifs = -1; } else { numifs = lifn.lifn_count; if (tTd(0, 42)) - dprintf("system has %d interfaces\n", numifs); + sm_dprintf("system has %d interfaces\n", numifs); } if (numifs < 0) -# endif /* SIOCGLIFNUM */ +# endif /* SIOCGLIFNUM */ numifs = MAXINTERFACES; if (numifs <= 0) @@ -4930,7 +4645,8 @@ load_if_names() if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) { if (tTd(0, 4)) - dprintf("SIOCGLIFCONF failed: %s\n", errstring(errno)); + sm_dprintf("SIOCGLIFCONF failed: %s\n", + sm_errstring(errno)); (void) close(s); sm_free(lifc.lifc_buf); return; @@ -4938,19 +4654,19 @@ load_if_names() /* scan the list of IP address */ if (tTd(0, 40)) - dprintf("scanning for interface specific names, lifc_len=%d\n", + sm_dprintf("scanning for interface specific names, lifc_len=%d\n", lifc.lifc_len); - for (i = 0; i < lifc.lifc_len; ) + for (i = 0; i < lifc.lifc_len && i >= 0; ) { struct lifreq *ifr = (struct lifreq *)&lifc.lifc_buf[i]; SOCKADDR *sa = (SOCKADDR *) &ifr->lifr_addr; char *addr; struct in6_addr ia6; struct in_addr ia; -# ifdef SIOCGLIFFLAGS +# ifdef SIOCGLIFFLAGS struct lifreq ifrf; -# endif /* SIOCGLIFFLAGS */ +# endif /* SIOCGLIFFLAGS */ char ip_addr[256]; char buf6[INET6_ADDRSTRLEN]; int af = ifr->lifr_addr.ss_family; @@ -4966,7 +4682,7 @@ load_if_names() s = socket(af, SOCK_DGRAM, 0); if (s == -1) { - sm_free(lifc.lifc_buf); + sm_free(lifc.lifc_buf); /* XXX */ return; } @@ -4978,37 +4694,37 @@ load_if_names() if ((lifc.lifc_len - i) < sizeof *ifr) break; -# ifdef BSD4_4_SOCKADDR +# ifdef BSD4_4_SOCKADDR if (sa->sa.sa_len > sizeof ifr->lifr_addr) i += sizeof ifr->lifr_name + sa->sa.sa_len; else -# endif /* BSD4_4_SOCKADDR */ +# endif /* BSD4_4_SOCKADDR */ i += sizeof *ifr; if (tTd(0, 20)) - dprintf("%s\n", anynet_ntoa(sa)); + sm_dprintf("%s\n", anynet_ntoa(sa)); if (af != AF_INET && af != AF_INET6) continue; -# ifdef SIOCGLIFFLAGS +# ifdef SIOCGLIFFLAGS memset(&ifrf, '\0', sizeof(struct lifreq)); - (void) strlcpy(ifrf.lifr_name, ifr->lifr_name, + (void) sm_strlcpy(ifrf.lifr_name, ifr->lifr_name, sizeof(ifrf.lifr_name)); if (ioctl(s, SIOCGLIFFLAGS, (char *) &ifrf) < 0) { if (tTd(0, 4)) - dprintf("SIOCGLIFFLAGS failed: %s\n", - errstring(errno)); + sm_dprintf("SIOCGLIFFLAGS failed: %s\n", + sm_errstring(errno)); continue; } else if (tTd(0, 41)) - dprintf("\tflags: %lx\n", - (unsigned long)ifrf.lifr_flags); + sm_dprintf("\tflags: %lx\n", + (unsigned long) ifrf.lifr_flags); if (!bitset(IFF_UP, ifrf.lifr_flags)) continue; -# endif /* SIOCGLIFFLAGS */ +# endif /* SIOCGLIFFLAGS */ ip_addr[0] = '\0'; @@ -5043,9 +4759,10 @@ load_if_names() /* save IP address in text from */ addr = anynet_ntop(&ia6, buf6, sizeof buf6); if (addr != NULL) - (void) snprintf(ip_addr, sizeof ip_addr, - "[%.*s]", - (int) sizeof ip_addr - 3, addr); + (void) sm_snprintf(ip_addr, sizeof ip_addr, + "[%.*s]", + (int) sizeof ip_addr - 3, + addr); break; case AF_INET: @@ -5059,7 +4776,7 @@ load_if_names() } /* save IP address in text from */ - (void) snprintf(ip_addr, sizeof ip_addr, "[%.*s]", + (void) sm_snprintf(ip_addr, sizeof ip_addr, "[%.*s]", (int) sizeof ip_addr - 3, inet_ntoa(ia)); break; } @@ -5071,20 +4788,21 @@ load_if_names() { setclass('w', ip_addr); if (tTd(0, 4)) - dprintf("\ta.k.a.: %s\n", ip_addr); + sm_dprintf("\ta.k.a.: %s\n", ip_addr); } -# ifdef SIOCGLIFFLAGS +# ifdef SIOCGLIFFLAGS /* skip "loopback" interface "lo" */ - if (bitset(IFF_LOOPBACK, ifrf.lifr_flags)) + if (DontProbeInterfaces == DPI_SKIPLOOPBACK && + bitset(IFF_LOOPBACK, ifrf.lifr_flags)) continue; -# endif /* SIOCGLIFFLAGS */ +# endif /* SIOCGLIFFLAGS */ (void) add_hostnames(sa); } - sm_free(lifc.lifc_buf); + sm_free(lifc.lifc_buf); /* XXX */ (void) close(s); -#else /* NETINET6 && defined(SIOCGLIFCONF) */ -# if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN +# else /* NETINET6 && defined(SIOCGLIFCONF) */ +# if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN int s; int i; struct ifconf ifc; @@ -5095,18 +4813,19 @@ load_if_names() return; /* get the list of known IP address from the kernel */ -# if defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN +# if defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN if (ioctl(s, SIOCGIFNUM, (char *) &numifs) < 0) { /* can't get number of interfaces -- fall back */ if (tTd(0, 4)) - dprintf("SIOCGIFNUM failed: %s\n", errstring(errno)); + sm_dprintf("SIOCGIFNUM failed: %s\n", + sm_errstring(errno)); numifs = -1; } else if (tTd(0, 42)) - dprintf("system has %d interfaces\n", numifs); + sm_dprintf("system has %d interfaces\n", numifs); if (numifs < 0) -# endif /* defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN */ +# endif /* defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN */ numifs = MAXINTERFACES; if (numifs <= 0) @@ -5119,18 +4838,18 @@ load_if_names() if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) { if (tTd(0, 4)) - dprintf("SIOCGIFCONF failed: %s\n", errstring(errno)); + sm_dprintf("SIOCGIFCONF failed: %s\n", + sm_errstring(errno)); (void) close(s); - sm_free(ifc.ifc_buf); return; } /* scan the list of IP address */ if (tTd(0, 40)) - dprintf("scanning for interface specific names, ifc_len=%d\n", + sm_dprintf("scanning for interface specific names, ifc_len=%d\n", ifc.ifc_len); - for (i = 0; i < ifc.ifc_len; ) + for (i = 0; i < ifc.ifc_len && i >= 0; ) { int af; struct ifreq *ifr = (struct ifreq *) &ifc.ifc_buf[i]; @@ -5164,7 +4883,7 @@ load_if_names() i += sizeof *ifr; if (tTd(0, 20)) - dprintf("%s\n", anynet_ntoa(sa)); + sm_dprintf("%s\n", anynet_ntoa(sa)); af = ifr->ifr_addr.sa_family; if (af != AF_INET @@ -5176,11 +4895,11 @@ load_if_names() # ifdef SIOCGIFFLAGS memset(&ifrf, '\0', sizeof(struct ifreq)); - (void) strlcpy(ifrf.ifr_name, ifr->ifr_name, + (void) sm_strlcpy(ifrf.ifr_name, ifr->ifr_name, sizeof(ifrf.ifr_name)); (void) ioctl(s, SIOCGIFFLAGS, (char *) &ifrf); if (tTd(0, 41)) - dprintf("\tflags: %lx\n", + sm_dprintf("\tflags: %lx\n", (unsigned long) ifrf.ifr_flags); # define IFRFREF ifrf # else /* SIOCGIFFLAGS */ @@ -5206,7 +4925,7 @@ load_if_names() } /* save IP address in text from */ - (void) snprintf(ip_addr, sizeof ip_addr, "[%.*s]", + (void) sm_snprintf(ip_addr, sizeof ip_addr, "[%.*s]", (int) sizeof ip_addr - 3, inet_ntoa(ia)); break; @@ -5240,9 +4959,10 @@ load_if_names() /* save IP address in text from */ addr = anynet_ntop(&ia6, buf6, sizeof buf6); if (addr != NULL) - (void) snprintf(ip_addr, sizeof ip_addr, - "[%.*s]", - (int) sizeof ip_addr - 3, addr); + (void) sm_snprintf(ip_addr, sizeof ip_addr, + "[%.*s]", + (int) sizeof ip_addr - 3, + addr); break; # endif /* NETINET6 */ @@ -5255,20 +4975,21 @@ load_if_names() { setclass('w', ip_addr); if (tTd(0, 4)) - dprintf("\ta.k.a.: %s\n", ip_addr); + sm_dprintf("\ta.k.a.: %s\n", ip_addr); } /* skip "loopback" interface "lo" */ - if (bitset(IFF_LOOPBACK, IFRFREF.ifr_flags)) + if (DontProbeInterfaces == DPI_SKIPLOOPBACK && + bitset(IFF_LOOPBACK, IFRFREF.ifr_flags)) continue; (void) add_hostnames(sa); } - sm_free(ifc.ifc_buf); + sm_free(ifc.ifc_buf); /* XXX */ (void) close(s); -# undef IFRFREF -# endif /* defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN */ -#endif /* NETINET6 && defined(SIOCGLIFCONF) */ +# undef IFRFREF +# endif /* defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN */ +# endif /* NETINET6 && defined(SIOCGLIFCONF) */ } /* ** ISLOOPBACK -- is socket address in the loopback net? @@ -5277,8 +4998,8 @@ load_if_names() ** sa -- socket address. ** ** Returns: -** TRUE -- is socket address in the loopback net? -** FALSE -- otherwise +** true -- is socket address in the loopback net? +** false -- otherwise ** */ @@ -5288,14 +5009,14 @@ isloopback(sa) { #if NETINET6 if (IN6_IS_ADDR_LOOPBACK(&sa.sin6.sin6_addr)) - return TRUE; + return true; #else /* NETINET6 */ /* XXX how to correctly extract IN_LOOPBACKNET part? */ if (((ntohl(sa.sin.sin_addr.s_addr) & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) - return TRUE; + return true; #endif /* NETINET6 */ - return FALSE; + return false; } /* ** GET_NUM_PROCS_ONLINE -- return the number of processors currently online @@ -5321,7 +5042,7 @@ get_num_procs_online() mib[1] = HW_NCPU; sz = (size_t) sizeof nproc; (void) sysctl(mib, 2, &nproc, &sz, NULL, 0); -# endif /* defined(CTL_HW) && defined(HW_NCPUS) */ +# endif /* defined(CTL_HW) && defined(HW_NCPU) */ #else /* USESYSCTL */ # ifdef _SC_NPROCESSORS_ONLN nproc = (int) sysconf(_SC_NPROCESSORS_ONLN); @@ -5359,7 +5080,7 @@ seed_random() long seed; struct timeval t; - seed = (long) getpid(); + seed = (long) CurrentPid; if (gettimeofday(&t, NULL) >= 0) seed += t.tv_sec + t.tv_usec; @@ -5402,17 +5123,23 @@ sm_syslog(level, id, fmt, va_alist) int seq = 1; int idlen; char buf0[MAXLINE]; - extern int SnprfOverflow; - extern int SyslogErrno; - extern char *DoprEnd; - VA_LOCAL_DECL + char *newstring; + extern int SyslogPrefixLen; + SM_VA_LOCAL_DECL - save_errno = SyslogErrno = errno; + save_errno = errno; if (id == NULL) + { id = "NOQUEUE"; + idlen = strlen(id) + SyslogPrefixLen; + } else if (strcmp(id, NOQID) == 0) + { id = ""; - idlen = strlen(id); + idlen = SyslogPrefixLen; + } + else + idlen = strlen(id) + SyslogPrefixLen; if (buf == NULL) { @@ -5422,38 +5149,43 @@ sm_syslog(level, id, fmt, va_alist) for (;;) { - /* do a virtual vsnprintf into buf */ - VA_START(fmt); - buf[0] = 0; - DoprEnd = buf + bufsize - 1; - SnprfOverflow = 0; - sm_dopr(buf, fmt, ap); - *DoprEnd = '\0'; - VA_END; - /* end of virtual vsnprintf */ - - if (SnprfOverflow == 0) + int n; + + /* print log message into buf */ + SM_VA_START(ap, fmt); + n = sm_vsnprintf(buf, bufsize, fmt, ap); + SM_VA_END(ap); + SM_ASSERT(n > 0); + if (n < bufsize) break; /* String too small, redo with correct size */ - bufsize += SnprfOverflow + 1; + bufsize = n + 1; if (buf != buf0) + { sm_free(buf); - buf = xalloc(bufsize * sizeof (char)); + buf = NULL; + } + buf = sm_malloc_x(bufsize); } - if ((strlen(buf) + idlen + 1) < SYSLOG_BUFSIZE) + + /* clean up buf after it has been expanded with args */ + newstring = str2prt(buf); + if ((strlen(newstring) + idlen + 1) < SYSLOG_BUFSIZE) { #if LOG if (*id == '\0') - syslog(level, "%s", buf); + syslog(level, "%s", newstring); else - syslog(level, "%s: %s", id, buf); + syslog(level, "%s: %s", id, newstring); #else /* LOG */ /*XXX should do something more sensible */ if (*id == '\0') - fprintf(stderr, "%s\n", buf); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s\n", + newstring); else - fprintf(stderr, "%s: %s\n", id, buf); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: %s\n", id, newstring); #endif /* LOG */ if (buf == buf0) buf = NULL; @@ -5461,18 +5193,26 @@ sm_syslog(level, id, fmt, va_alist) return; } - begin = buf; +/* +** additional length for splitting: " ..." + 3, where 3 is magic to +** have some data for the next entry. +*/ + +#define SL_SPLIT 7 + + begin = newstring; + idlen += 5; /* strlen("[999]"), see below */ while (*begin != '\0' && - (strlen(begin) + idlen + 5) > SYSLOG_BUFSIZE) + (strlen(begin) + idlen) > SYSLOG_BUFSIZE) { char save; - if (seq == 999) + if (seq >= 999) { /* Too many messages */ break; } - end = begin + SYSLOG_BUFSIZE - idlen - 12; + end = begin + SYSLOG_BUFSIZE - idlen - SL_SPLIT; while (end > begin) { /* Break on comma or space */ @@ -5485,30 +5225,32 @@ sm_syslog(level, id, fmt, va_alist) } /* No separator, break midstring... */ if (end == begin) - end = begin + SYSLOG_BUFSIZE - idlen - 12; + end = begin + SYSLOG_BUFSIZE - idlen - SL_SPLIT; save = *end; *end = 0; #if LOG syslog(level, "%s[%d]: %s ...", id, seq++, begin); #else /* LOG */ - fprintf(stderr, "%s[%d]: %s ...\n", id, seq++, begin); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s[%d]: %s ...\n", id, seq++, begin); #endif /* LOG */ *end = save; begin = end; } - if (seq == 999) + if (seq >= 999) #if LOG syslog(level, "%s[%d]: log terminated, too many parts", id, seq); #else /* LOG */ - fprintf(stderr, "%s[%d]: log terminated, too many parts\n", - id, seq); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s[%d]: log terminated, too many parts\n", id, seq); #endif /* LOG */ else if (*begin != '\0') #if LOG syslog(level, "%s[%d]: %s", id, seq, begin); #else /* LOG */ - fprintf(stderr, "%s[%d]: %s\n", id, seq, begin); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s[%d]: %s\n", id, seq, begin); #endif /* LOG */ if (buf == buf0) buf = NULL; @@ -5545,11 +5287,11 @@ hard_syslog(pri, msg, va_alist) { int i; char buf[SYSLOG_BUFSIZE]; - VA_LOCAL_DECL; + SM_VA_LOCAL_DECL - VA_START(msg); - vsnprintf(buf, sizeof buf, msg, ap); - VA_END; + SM_VA_START(ap, msg); + (void) sm_vsnprintf(buf, sizeof buf, msg, ap); + SM_VA_END(ap); for (i = MAXSYSLOGTRIES; --i >= 0 && syslog(pri, CAST "%s", buf) < 0; ) continue; @@ -5573,14 +5315,14 @@ int local_hostname_length(hostname) char *hostname; { - int len_host, len_domain; + size_t len_host, len_domain; if (!*_res.defdname) res_init(); len_host = strlen(hostname); len_domain = strlen(_res.defdname); if (len_host > len_domain && - (strcasecmp(hostname + len_host - len_domain, + (sm_strcasecmp(hostname + len_host - len_domain, _res.defdname) == 0) && hostname[len_host - len_domain - 1] == '.') return len_host - len_domain - 1; @@ -5595,30 +5337,38 @@ local_hostname_length(hostname) char *CompileOptions[] = { +#if NAMED_BIND +# if DNSMAP + "DNSMAP", +# endif /* DNSMAP */ +#endif /* NAMED_BIND */ #if EGD "EGD", #endif /* EGD */ -#ifdef HESIOD +#if HESIOD "HESIOD", #endif /* HESIOD */ #if HES_GETMAILHOST "HES_GETMAILHOST", #endif /* HES_GETMAILHOST */ -#ifdef LDAPMAP +#if LDAPMAP "LDAPMAP", #endif /* LDAPMAP */ -#ifdef MAP_NSD +#if LOG + "LOG", +#endif /* LOG */ +#if MAP_NSD "MAP_NSD", #endif /* MAP_NSD */ -#ifdef MAP_REGEX +#if MAP_REGEX "MAP_REGEX", #endif /* MAP_REGEX */ -#if LOG - "LOG", -#endif /* LOG */ #if MATCHGECOS "MATCHGECOS", #endif /* MATCHGECOS */ +#if MILTER + "MILTER", +#endif /* MILTER */ #if MIME7TO8 "MIME7TO8", #endif /* MIME7TO8 */ @@ -5628,7 +5378,7 @@ char *CompileOptions[] = #if NAMED_BIND "NAMED_BIND", #endif /* NAMED_BIND */ -#ifdef NDBM +#if NDBM "NDBM", #endif /* NDBM */ #if NETINET @@ -5652,52 +5402,58 @@ char *CompileOptions[] = #if NETX25 "NETX25", #endif /* NETX25 */ -#ifdef NEWDB +#if NEWDB "NEWDB", #endif /* NEWDB */ -#ifdef NIS +#if NIS "NIS", #endif /* NIS */ -#ifdef NISPLUS +#if NISPLUS "NISPLUS", #endif /* NISPLUS */ -#ifdef PH_MAP +#if NO_DH + "NO_DH", +#endif /* NO_DH */ +#if PH_MAP "PH_MAP", #endif /* PH_MAP */ -#if QUEUE - "QUEUE", -#endif /* QUEUE */ +#ifdef PICKY_HELO_CHECK + "PICKY_HELO_CHECK", +#endif /* PICKY_HELO_CHECK */ +#if PIPELINING + "PIPELINING", +#endif /* PIPELINING */ #if SASL "SASL", #endif /* SASL */ #if SCANF "SCANF", #endif /* SCANF */ -#if SFIO - "SFIO", -#endif /* SFIO */ -#if SMTP - "SMTP", -#endif /* SMTP */ #if SMTPDEBUG "SMTPDEBUG", #endif /* SMTPDEBUG */ #if STARTTLS "STARTTLS", #endif /* STARTTLS */ -#ifdef SUID_ROOT_FILES_OK +#if SUID_ROOT_FILES_OK "SUID_ROOT_FILES_OK", #endif /* SUID_ROOT_FILES_OK */ #if TCPWRAPPERS "TCPWRAPPERS", #endif /* TCPWRAPPERS */ +#if TLS_NO_RSA + "TLS_NO_RSA", +#endif /* TLS_NO_RSA */ +#if TLS_VRFY_PER_CTX + "TLS_VRFY_PER_CTX", +#endif /* TLS_VRFY_PER_CTX */ #if USERDB "USERDB", #endif /* USERDB */ #if XDEBUG "XDEBUG", #endif /* XDEBUG */ -#ifdef XLA +#if XLA "XLA", #endif /* XLA */ NULL @@ -5710,9 +5466,27 @@ char *CompileOptions[] = char *OsCompileOptions[] = { +#if ADDRCONFIG_IS_BROKEN + "ADDRCONFIG_IS_BROKEN", +#endif /* ADDRCONFIG_IS_BROKEN */ +#ifdef AUTO_NETINFO_HOSTS + "AUTO_NETINFO_HOSTS", +#endif /* AUTO_NETINFO_HOSTS */ +#ifdef AUTO_NIS_ALIASES + "AUTO_NIS_ALIASES", +#endif /* AUTO_NIS_ALIASES */ +#if BROKEN_RES_SEARCH + "BROKEN_RES_SEARCH", +#endif /* BROKEN_RES_SEARCH */ +#ifdef BSD4_4_SOCKADDR + "BSD4_4_SOCKADDR", +#endif /* BSD4_4_SOCKADDR */ #if BOGUS_O_EXCL "BOGUS_O_EXCL", #endif /* BOGUS_O_EXCL */ +#if DEC_OSF_BROKEN_GETPWENT + "DEC_OSF_BROKEN_GETPWENT", +#endif /* DEC_OSF_BROKEN_GETPWENT */ #if FAST_PID_RECYCLE "FAST_PID_RECYCLE", #endif /* FAST_PID_RECYCLE */ @@ -5758,9 +5532,6 @@ char *OsCompileOptions[] = #if HASSETVBUF "HASSETVBUF", #endif /* HASSETVBUF */ -#if HASSNPRINTF - "HASSNPRINTF", -#endif /* HASSNPRINTF */ #if HAS_ST_GEN "HAS_ST_GEN", #endif /* HAS_ST_GEN */ @@ -5797,9 +5568,27 @@ char *OsCompileOptions[] = #if NEEDFSYNC "NEEDFSYNC", #endif /* NEEDFSYNC */ +#if NEEDLOCAL_HOSTNAME_LENGTH + "NEEDLOCAL_HOSTNAME_LENGTH", +#endif /* NEEDLOCAL_HOSTNAME_LENGTH */ +#if NEEDSGETIPNODE + "NEEDSGETIPNODE", +#endif /* NEEDSGETIPNODE */ +#if NEEDSTRSTR + "NEEDSTRSTR", +#endif /* NEEDSTRSTR */ +#if NEEDSTRTOL + "NEEDSTRTOL", +#endif /* NEEDSTRTOL */ +#ifdef NO_GETSERVBYNAME + "NO_GETSERVBYNAME", +#endif /* NO_GETSERVBYNAME */ #if NOFTRUNCATE "NOFTRUNCATE", #endif /* NOFTRUNCATE */ +#if REQUIRES_DIR_FSYNC + "REQUIRES_DIR_FSYNC", +#endif /* REQUIRES_DIR_FSYNC */ #if RLIMIT_NEEDS_SYS_TIME_H "RLIMIT_NEEDS_SYS_TIME_H", #endif /* RLIMIT_NEEDS_SYS_TIME_H */ @@ -5836,9 +5625,137 @@ char *OsCompileOptions[] = #if USE_SIGLONGJMP "USE_SIGLONGJMP", #endif /* USE_SIGLONGJMP */ +#if USEGETCONFATTR + "USEGETCONFATTR", +#endif /* USEGETCONFATTR */ #if USESETEUID "USESETEUID", #endif /* USESETEUID */ +#ifdef USESYSCTL + "USESYSCTL", +#endif /* USESYSCTL */ +#if USING_NETSCAPE_LDAP + "USING_NETSCAPE_LDAP", +#endif /* USING_NETSCAPE_LDAP */ +#ifdef WAITUNION + "WAITUNION", +#endif /* WAITUNION */ + NULL +}; + +/* +** FFR compile options. +*/ + +char *FFRCompileOptions[] = +{ +#if _FFR_ADAPTIVE_EOL + "_FFR_ADAPTIVE_EOL", +#endif /* _FFR_ADAPTIVE_EOL */ +#if _FFR_ALLOW_SASLINFO + "_FFR_ALLOW_SASLINFO", +#endif /* _FFR_ALLOW_SASLINFO */ +#if _FFR_BESTMX_BETTER_TRUNCATION + "_FFR_BESTMX_BETTER_TRUNCATION", +#endif /* _FFR_BESTMX_BETTER_TRUNCATION */ +#if _FFR_CACHE_LPC + "_FFR_CACHE_LPC", +#endif /* _FFR_CACHE_LPC */ +#if _FFR_CATCH_BROKEN_MTAS + "_FFR_CATCH_BROKEN_MTAS", +#endif /* _FFR_CATCH_BROKEN_MTAS */ +#if _FFR_CHECK_EOM + "_FFR_CHECK_EOM", +#endif /* _FFR_CHECK_EOM */ +#if _FFR_CONTROL_MSTAT + "_FFR_CONTROL_MSTAT", +#endif /* _FFR_CONTROL_MSTAT */ +#if _FFR_DAEMON_NETUNIX + "_FFR_DAEMON_NETUNIX", +#endif /* _FFR_DAEMON_NETUNIX */ +#if _FFR_DEPRECATE_MAILER_FLAG_I + "_FFR_DEPRECATE_MAILER_FLAG_I", +#endif /* _FFR_DEPRECATE_MAILER_FLAG_I */ +#if _FFR_DNSMAP_BASE + "_FFR_DNSMAP_BASE", +#endif /* _FFR_DNSMAP_BASE */ +#if _FFR_DNSMAP_MULTI + "_FFR_DNSMAP_MULTI", +# if _FFR_DNSMAP_MULTILIMIT + "_FFR_DNSMAP_MULTILIMIT", +# endif /* _FFR_DNSMAP_MULTILIMIT */ +#endif /* _FFR_DNSMAP_MULTI */ +#if _FFR_DONTLOCKFILESFORREAD_OPTION + "_FFR_DONTLOCKFILESFORREAD_OPTION", +#endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */ +#if _FFR_FIX_DASHT + "_FFR_FIX_DASHT", +#endif /* _FFR_FIX_DASHT */ +#if _FFR_FORWARD_SYSERR + "_FFR_FORWARD_SYSERR", +#endif /* _FFR_FORWARD_SYSERR */ +#if _FFR_GEN_ORCPT + "_FFR_GEN_ORCPT", +#endif /* _FFR_GEN_ORCPT */ +#if _FFR_GROUPREADABLEAUTHINFOFILE + "_FFR_GROUPREADABLEAUTHINFOFILE", +#endif /* _FFR_GROUPREADABLEAUTHINFOFILE */ +#if _FFR_HDR_TYPE + "_FFR_HDR_TYPE", +#endif /* _FFR_HDR_TYPE */ +#if _FFR_IGNORE_EXT_ON_HELO + "_FFR_IGNORE_EXT_ON_HELO", +#endif /* _FFR_IGNORE_EXT_ON_HELO */ +#if _FFR_MAX_FORWARD_ENTRIES + "_FFR_MAX_FORWARD_ENTRIES", +#endif /* _FFR_MAX_FORWARD_ENTRIES */ +#if MILTER +# if _FFR_MILTER_PERDAEMON + "_FFR_MILTER_PERDAEMON", +# endif /* _FFR_MILTER_PERDAEMON */ +#endif /* MILTER */ +#if _FFR_NODELAYDSN_ON_HOLD + "_FFR_NODELAYDSN_ON_HOLD", +#endif /* _FFR_NODELAYDSN_ON_HOLD */ +#if _FFR_NO_PIPE + "_FFR_NO_PIPE", +#endif /* _FFR_NO_PIPE */ +#if _FFR_QUEUEDELAY + "_FFR_QUEUEDELAY", +#endif /* _FFR_QUEUEDELAY */ +#if _FFR_QUEUE_MACRO + "_FFR_QUEUE_MACRO", +#endif /* _FFR_QUEUE_MACRO */ +#if _FFR_QUEUE_SCHED_DBG + "_FFR_QUEUE_SCHED_DBG", +#endif /* _FFR_QUEUE_SCHED_DBG */ +#if _FFR_REDIRECTEMPTY + "_FFR_REDIRECTEMPTY", +#endif /* _FFR_REDIRECTEMPTY */ +#if _FFR_RESET_MACRO_GLOBALS + "_FFR_RESET_MACRO_GLOBALS", +#endif /* _FFR_RESET_MACRO_GLOBALS */ +#if _FFR_RHS + "_FFR_RHS", +#endif /* _FFR_RHS */ +#if _FFR_SAVE_CHARSET + "_FFR_SAVE_CHARSET", +#endif /* _FFR_SAVE_CHARSET */ +#if _FFR_SHM_STATUS + "_FFR_SHM_STATUS", +#endif /* _FFR_SHM_STATUS */ +#if _FFR_SMTP_SSL + "_FFR_SMTP_SSL", +#endif /* _FFR_SMTP_SSL */ +#if _FFR_SOFT_BOUNCE + "_FFR_SOFT_BOUNCE", +#endif /* _FFR_SOFT_BOUNCE */ +#if _FFR_TIMERS + "_FFR_TIMERS", +#endif /* _FFR_TIMERS */ +#if _FFR_TLS_1 + "_FFR_TLS_1", +#endif /* _FFR_TLS_1 */ NULL }; diff --git a/gnu/usr.sbin/sendmail/sendmail/conf.h b/gnu/usr.sbin/sendmail/sendmail/conf.h index f3432060bd8..fab43849ca3 100644 --- a/gnu/usr.sbin/sendmail/sendmail/conf.h +++ b/gnu/usr.sbin/sendmail/sendmail/conf.h @@ -10,7 +10,7 @@ * the sendmail distribution. * * - * $Sendmail: conf.h,v 8.496.4.54 2001/07/31 22:30:24 gshapiro Exp $ + * $Sendmail: conf.h,v 8.555 2001/09/03 17:21:30 gshapiro Exp $ */ /* @@ -29,9 +29,6 @@ struct rusage; /* forward declaration to get gcc to shut up in wait.h */ # include <sys/param.h> # include <sys/types.h> -# if SFIO && defined(SF_APPEND) -# undef SF_APPEND /* Both sfio/stdio.h and sys/stat.h define it */ -# endif /* SFIO && defined(SF_APPEND) */ # include <sys/stat.h> # ifndef __QNX__ /* in QNX this grabs bogus LOCK_* manifests */ @@ -59,6 +56,7 @@ struct rusage; /* forward declaration to get gcc to shut up in wait.h */ /********************************************************************** ** Table sizes, etc.... ** There shouldn't be much need to change these.... +** If you do, be careful, none should be set anywhere near INT_MAX **********************************************************************/ #define MAXLINE 2048 /* max line length */ @@ -74,17 +72,25 @@ struct rusage; /* forward declaration to get gcc to shut up in wait.h */ #define MAXUSERENVIRON 100 /* max envars saved, must be >= 3 */ #define MAXALIASDB 12 /* max # of alias databases */ #define MAXMAPSTACK 12 /* max # of stacked or sequenced maps */ -#if _FFR_MILTER +#if MILTER # define MAXFILTERS 25 /* max # of milter filters */ -# define MAXFILTERMACROS 50 /* max # of macros per milter cmd */ -#endif /* _FFR_MILTER */ +# define MAXFILTERMACROS 50 /* max # of macros per milter cmd */ +#endif /* MILTER */ #define MAXSMTPARGS 20 /* max # of ESMTP args for MAIL/RCPT */ #define MAXTOCLASS 8 /* max # of message timeout classes */ #define MAXRESTOTYPES 3 /* max # of resolver timeout types */ #define MAXMIMEARGS 20 /* max args in Content-Type: */ #define MAXMIMENESTING 20 /* max MIME multipart nesting */ #define QUEUESEGSIZE 1000 /* increment for queue size */ -#define MAXQFNAME 21 /* max qf file name length */ + +/* +** MAXQFNAME == 2 (size of "qf", "df" prefix) +** + 8 (base 60 encoded date, time & sequence number) +** + 10 (base 10 encoded 32 bit process id) +** + 1 (terminating NUL character). +*/ + +#define MAXQFNAME 21 /* max qf file name length + 1 */ #define MACBUFSIZE 4096 /* max expanded macro buffer size */ #define TOBUFSIZE SM_ARG_MAX /* max buffer to hold address list */ #define MAXSHORTSTR 203 /* max short string length */ @@ -102,28 +108,32 @@ struct rusage; /* forward declaration to get gcc to shut up in wait.h */ # define MAXSYMLINKS 32 /* max number of symlinks in a path */ #endif /* ! MAXSYMLINKS */ #define MAXLINKPATHLEN (MAXPATHLEN * MAXSYMLINKS) /* max link-expanded file */ -#define DATA_PROGRESS_TIMEOUT 300 /* how ofter to check DATA progress */ +#define DATA_PROGRESS_TIMEOUT 300 /* how often to check DATA progress */ #define ENHSCLEN 10 /* max len of enhanced status code */ -#if _FFR_DYNAMIC_TOBUF -# define DEFAULT_MAX_RCPT 100 /* max number of RCPTs per envelope */ -#endif /* _FFR_DYNAMIC_TOBUF */ +#define DEFAULT_MAX_RCPT 100 /* max number of RCPTs per envelope */ +#define MAXQUEUEGROUPS 50 /* max # of queue groups */ + /* must be less than BITMAPBITS for DoQueueRun */ +#define MAXWORKGROUPS 50 /* max # of work groups */ +#define MAXFILESYS BITMAPBITS /* max # of queue file systems + * must be <= BITMAPBITS */ +#ifndef FILESYS_UPDATE_INTERVAL +# define FILESYS_UPDATE_INTERVAL 300 /* how often to update FileSys table */ +#endif /* FILESYS_UPDATE_INTERVAL */ + +#ifndef SM_DEFAULT_TTL +# define SM_DEFAULT_TTL 3600 /* default TTL for services that don't have one */ +#endif /* SM_DEFAULT_TTL */ #if SASL # ifndef AUTH_MECHANISMS -# if STARTTLS && _FFR_EXT_MECH +# if STARTTLS # define AUTH_MECHANISMS "EXTERNAL GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5" -# else /* STARTTLS && _FFR_EXT_MECH */ +# else /* STARTTLS */ # define AUTH_MECHANISMS "GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5" -# endif /* STARTTLS && _FFR_EXT_MECH */ +# endif /* STARTTLS */ # endif /* ! AUTH_MECHANISMS */ #endif /* SASL */ -#ifdef LDAPMAP -# define LDAPMAP_MAX_ATTR 64 -# define LDAPMAP_MAX_FILTER 1024 -# define LDAPMAP_MAX_PASSWD 256 -#endif /* LDAPMAP */ - /********************************************************************** ** Compilation options. ** #define these to 1 if they are available; @@ -171,2680 +181,20 @@ struct rusage; /* forward declaration to get gcc to shut up in wait.h */ # define MIME7TO8 1 /* 7->8 bit MIME conversions */ #endif /* ! MIME7TO8 */ -/********************************************************************** -** "Hard" compilation options. -** #define these if they are available; comment them out otherwise. -** These cannot be overridden from the Makefile, and should really not -** be turned off unless absolutely necessary. -**********************************************************************/ +#if NAMED_BIND +# ifndef DNSMAP +# define DNSMAP 1 /* DNS map type */ +# endif /* ! DNSMAP */ +#endif /* NAMED_BIND */ -#define LOG 1 /* enable logging -- don't turn off */ +#ifndef PIPELINING +# define PIPELINING 1 /* SMTP PIPELINING */ +#endif /* PIPELINING */ /********************************************************************** ** End of site-specific configuration. **********************************************************************/ -/* -** General "standard C" defines. -** -** These may be undone later, to cope with systems that claim to -** be Standard C but aren't. Gcc is the biggest offender -- it -** doesn't realize that the library is part of the language. -** -** Life would be much easier if we could get rid of this sort -** of bozo problems. -*/ - -#ifdef __STDC__ -# define HASSETVBUF 1 /* we have setvbuf(3) in libc */ -#endif /* __STDC__ */ - -/* -** Assume you have standard calls; can be #undefed below if necessary. -*/ - -#ifndef HASLSTAT -# define HASLSTAT 1 /* has lstat(2) call */ -#endif /* ! HASLSTAT */ -/********************************************************************** -** Operating system configuration. -** -** Unless you are porting to a new OS, you shouldn't have to -** change these. -**********************************************************************/ - -/* -** HP-UX -- tested for 8.07, 9.00, and 9.01. -** -** If V4FS is defined, compile for HP-UX 10.0. -** 11.x support from Richard Allen <ra@hp.is>. -*/ - -#ifdef __hpux - /* common definitions for HP-UX 9.x and 10.x */ -# undef m_flags /* conflict between Berkeley DB 1.85 db.h & sys/sysmacros.h on HP 300 */ -# define SYSTEM5 1 /* include all the System V defines */ -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# define HASFCHMOD 1 /* has fchmod(2) syscall */ -# define USESETEUID 1 /* has usable seteuid(2) call */ -# define BOGUS_O_EXCL 1 /* exclusive open follows symlinks */ -# define seteuid(e) setresuid(-1, e, -1) -# define IP_SRCROUTE 1 /* can check IP source routing */ -# define LA_TYPE LA_HPUX -# define SPT_TYPE SPT_PSTAT -# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */ -# define GIDSET_T gid_t -# ifndef HASGETUSERSHELL -# define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps */ -# endif /* ! HASGETUSERSHELL */ -# ifdef HPUX11 -# define HASFCHOWN 1 /* has fchown(2) */ -# define HASSNPRINTF 1 /* has snprintf(3) */ -# ifndef BROKEN_RES_SEARCH -# define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_errno=0 */ -# endif /* ! BROKEN_RES_SEARCH */ -# else /* HPUX11 */ -# ifndef NOT_SENDMAIL -# define syslog hard_syslog -# endif /* ! NOT_SENDMAIL */ -# endif /* HPUX11 */ -# define SAFENFSPATHCONF 1 /* pathconf(2) pessimizes on NFS filesystems */ - -# ifdef V4FS - /* HP-UX 10.x */ -# define _PATH_UNIX "/stand/vmunix" -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ -# ifndef IDENTPROTO -# define IDENTPROTO 1 /* TCP/IP implementation fixed in 10.0 */ -# endif /* ! IDENTPROTO */ -# include <sys/mpctl.h> /* for mpctl() in get_num_procs_online() */ -# else /* V4FS */ - /* HP-UX 9.x */ -# define _PATH_UNIX "/hp-ux" -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef IDENTPROTO -# define IDENTPROTO 0 /* TCP/IP implementation is broken */ -# endif /* ! IDENTPROTO */ -# ifdef __STDC__ -extern void hard_syslog(int, char *, ...); -# else /* __STDC__ */ -extern void hard_syslog(); -# endif /* __STDC__ */ -# define FDSET_CAST (int *) /* cast for fd_set parameters to select */ -# endif /* V4FS */ - -#endif /* __hpux */ - - -/* -** IBM AIX 4.x -*/ - -#ifdef _AIX4 -# define _AIX3 1 /* pull in AIX3 stuff */ -# define BSD4_4_SOCKADDR /* has sa_len */ -# define USESETEUID 1 /* seteuid(2) works */ -# define TZ_TYPE TZ_NAME /* use tzname[] vector */ -# define SOCKOPT_LEN_T size_t /* arg#5 to getsockopt */ -# if _AIX4 >= 40200 -# define HASSETREUID 1 /* setreuid(2) works as of AIX 4.2 */ -# define SOCKADDR_LEN_T size_t /* e.g., arg#3 to accept, getsockname */ -# endif /* _AIX4 >= 40200 */ -# if _AIX4 >= 40300 -# define HASSNPRINTF 1 /* has snprintf starting in 4.3 */ -# endif /* _AIX4 >= 40300 */ -# if defined(_ILS_MACROS) /* IBM versions aren't side-effect clean */ -# undef isascii -# define isascii(c) !(c & ~0177) -# undef isdigit -# define isdigit(__a) (_IS(__a,_ISDIGIT)) -# undef isspace -# define isspace(__a) (_IS(__a,_ISSPACE)) -# endif /* defined(_ILS_MACROS) */ -#endif /* _AIX4 */ - - -/* -** IBM AIX 3.x -- actually tested for 3.2.3 -*/ - -#ifdef _AIX3 -# include <paths.h> -# include <sys/machine.h> /* to get byte order */ -# include <sys/select.h> -# define HASFCHOWN 1 /* has fchown(2) */ -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# define HASUNAME 1 /* use System V uname(2) system call */ -# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ -# define HASFCHMOD 1 /* has fchmod(2) syscall */ -# define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ -# define GIDSET_T gid_t -# define SFS_TYPE SFS_STATFS /* use <sys/statfs.h> statfs() impl */ -# define SPT_PADCHAR '\0' /* pad process title with nulls */ -# define LA_TYPE LA_INT -# define FSHIFT 16 -# define LA_AVENRUN "avenrun" -#endif /* _AIX3 */ - - -/* -** IBM AIX 2.2.1 -- actually tested for osupdate level 2706+1773 -** -** From Mark Whetzel <markw@wg.waii.com>. -*/ - -#ifdef AIX /* AIX/RT compiler pre-defines this */ -# include <paths.h> -# include <sys/time.h> /* AIX/RT resource.h does NOT include this */ -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# define HASUNAME 1 /* use System V uname(2) system call */ -# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ -# define HASFCHMOD 0 /* does not have fchmod(2) syscall */ -# define HASSETREUID 1 /* use setreuid(2) -lbsd system call */ -# define HASSETVBUF 1 /* use setvbuf(2) system call */ -# define HASSETRLIMIT 0 /* does not have setrlimit call */ -# define HASFLOCK 0 /* does not have flock call - use fcntl */ -# define HASULIMIT 1 /* use ulimit instead of setrlimit call */ -# define NEEDGETOPT 1 /* Do we need theirs or ours */ -# define SYS5SETPGRP 1 /* don't have setpgid on AIX/RT */ -# define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ -# define BSD4_3 1 /* NOT bsd 4.4 or posix signals */ -# define GIDSET_T int -# define SFS_TYPE SFS_STATFS /* use <sys/statfs.h> statfs() impl */ -# define SPT_PADCHAR '\0' /* pad process title with nulls */ -# define LA_TYPE LA_SUBR /* use our ported loadavgd daemon */ -# define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ -# define ARBPTR_T int * -# define void int -typedef int pid_t; -/* RTisms for BSD compatibility, specified in the Makefile - define BSD 1 - define BSD_INCLUDES 1 - define BSD_REMAP_SIGNAL_TO_SIGVEC - RTisms needed above */ -/* make this sendmail in a completely different place */ -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/local/newmail/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/usr/local/newmail/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ -#endif /* AIX */ - - -/* -** Silicon Graphics IRIX -** -** Compiles on 4.0.1. -** -** Use IRIX64 instead of IRIX for 64-bit IRIX (6.0). -** Use IRIX5 instead of IRIX for IRIX 5.x. -** -** This version tries to be adaptive using _MIPS_SIM: -** _MIPS_SIM == _ABIO32 (= 1) Abi: -32 on IRIX 6.2 -** _MIPS_SIM == _ABIN32 (= 2) Abi: -n32 on IRIX 6.2 -** _MIPS_SIM == _ABI64 (= 3) Abi: -64 on IRIX 6.2 -** -** _MIPS_SIM is 1 also on IRIX 5.3 -** -** IRIX64 changes from Mark R. Levinson <ml@cvdev.rochester.edu>. -** IRIX5 changes from Kari E. Hurtta <Kari.Hurtta@fmi.fi>. -** Adaptive changes from Kari E. Hurtta <Kari.Hurtta@fmi.fi>. -*/ - -#if defined(__sgi) -# ifndef IRIX -# define IRIX -# endif /* ! IRIX */ -# if _MIPS_SIM > 0 && !defined(IRIX5) -# define IRIX5 /* IRIX5 or IRIX6 */ -# endif /* _MIPS_SIM > 0 && !defined(IRIX5) */ -# if _MIPS_SIM > 1 && !defined(IRIX6) && !defined(IRIX64) -# define IRIX6 /* IRIX6 */ -# endif /* _MIPS_SIM > 1 && !defined(IRIX6) && !defined(IRIX64) */ -#endif /* defined(__sgi) */ - -#ifdef IRIX -# define SYSTEM5 1 /* this is a System-V derived system */ -# define HASSETREUID 1 /* has setreuid(2) call */ -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# define HASFCHMOD 1 /* has fchmod(2) syscall */ -# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ -# define IP_SRCROUTE 1 /* can check IP source routing */ -# define setpgid BSDsetpgrp -# define GIDSET_T gid_t -# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ -# define SFS_BAVAIL f_bfree /* alternate field name */ -# define SYSLOG_BUFSIZE 512 -# ifdef IRIX6 -# define STAT64 1 -# define QUAD_T unsigned long long -# define LA_TYPE LA_IRIX6 /* figure out at run time */ -# define SAFENFSPATHCONF 0 /* pathconf(2) lies on NFS filesystems */ -# else /* IRIX6 */ -# define LA_TYPE LA_INT - -# ifdef IRIX64 -# define STAT64 1 -# define QUAD_T unsigned long long -# define NAMELISTMASK 0x7fffffffffffffff /* mask for nlist() values */ -# else /* IRIX64 */ -# define STAT64 0 -# define NAMELISTMASK 0x7fffffff /* mask for nlist() values */ -# endif /* IRIX64 */ -# endif /* IRIX6 */ -# if defined(IRIX64) || defined(IRIX5) || defined(IRIX6) -# include <sys/cdefs.h> -# include <paths.h> -# define ARGV_T char *const * -# define HASFCHOWN 1 /* has fchown(2) */ -# define HASSETRLIMIT 1 /* has setrlimit(2) syscall */ -# define HASGETDTABLESIZE 1 /* has getdtablesize(2) syscall */ -# define HASSTRERROR 1 /* has strerror(3) */ -# else /* defined(IRIX64) || defined(IRIX5) || defined(IRIX6) */ -# define ARGV_T const char ** -# define WAITUNION 1 /* use "union wait" as wait argument type */ -# endif /* defined(IRIX64) || defined(IRIX5) || defined(IRIX6) */ -#endif /* IRIX */ - - -/* -** SunOS and Solaris -** -** Tested on SunOS 4.1.x (a.k.a. Solaris 1.1.x) and -** Solaris 2.4 (a.k.a. SunOS 5.4). -*/ - -#if defined(sun) && !defined(BSD) - -# include <sys/time.h> -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# define HASUNAME 1 /* use System V uname(2) system call */ -# define HASFCHMOD 1 /* has fchmod(2) syscall */ -# define IP_SRCROUTE 1 /* can check IP source routing */ -# define SAFENFSPATHCONF 1 /* pathconf(2) pessimizes on NFS filesystems */ -# ifndef HASFCHOWN -# define HASFCHOWN 1 /* fchown(2) */ -# endif /* ! HASFCHOWN */ - -# ifdef SOLARIS_2_3 -# define SOLARIS 20300 /* for back compat only -- use -DSOLARIS=20300 */ -# endif /* SOLARIS_2_3 */ - -# if defined(NOT_SENDMAIL) && !defined(SOLARIS) && defined(sun) && (defined(__svr4__) || defined(__SVR4)) -# define SOLARIS 1 /* unknown Solaris version */ -# endif /* defined(NOT_SENDMAIL) && !defined(SOLARIS) && defined(sun) && (defined(__svr4__) || defined(__SVR4)) */ - -# ifdef SOLARIS - /* Solaris 2.x (a.k.a. SunOS 5.x) */ -# ifndef __svr4__ -# define __svr4__ /* use all System V Release 4 defines below */ -# endif /* ! __svr4__ */ -# define GIDSET_T gid_t -# define USE_SA_SIGACTION 1 /* use sa_sigaction field */ -# define HASSTRERROR 1 /* has strerror(3) */ -# if _FFR_MILTER -# define BROKEN_PTHREAD_SLEEP 1 /* sleep after pthread_create() fails */ -# endif /* _FFR_MILTER */ -# ifndef _PATH_UNIX -# define _PATH_UNIX "/dev/ksyms" -# endif /* ! _PATH_UNIX */ -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ -# ifndef _PATH_HOSTS -# define _PATH_HOSTS "/etc/inet/hosts" -# endif /* ! _PATH_HOSTS */ -# ifndef SYSLOG_BUFSIZE -# define SYSLOG_BUFSIZE 1024 /* allow full size syslog buffer */ -# endif /* ! SYSLOG_BUFSIZE */ -# ifndef TZ_TYPE -# define TZ_TYPE TZ_TZNAME -# endif /* ! TZ_TYPE */ -# if SOLARIS >= 20300 || (SOLARIS < 10000 && SOLARIS >= 203) -# define USESETEUID 1 /* seteuid works as of 2.3 */ -# endif /* SOLARIS >= 20300 || (SOLARIS < 10000 && SOLARIS >= 203) */ -# if SOLARIS >= 20500 || (SOLARIS < 10000 && SOLARIS >= 205) -# define HASSETREUID 1 /* setreuid works as of 2.5 */ -# if SOLARIS < 207 || (SOLARIS > 10000 && SOLARIS < 20700) -# ifndef LA_TYPE -# define LA_TYPE LA_KSTAT /* use kstat(3k) -- may work in < 2.5 */ -# endif /* ! LA_TYPE */ -# ifndef RANDOMSHIFT /* random() doesn't work well (sometimes) */ -# define RANDOMSHIFT 8 -# endif /* RANDOMSHIFT */ -# endif /* SOLARIS < 207 || (SOLARIS > 10000 && SOLARIS < 20700) */ -# else /* SOLARIS >= 20500 || (SOLARIS < 10000 && SOLARIS >= 205) */ -# ifndef HASRANDOM -# define HASRANDOM 0 /* doesn't have random(3) */ -# endif /* ! HASRANDOM */ -# endif /* SOLARIS >= 20500 || (SOLARIS < 10000 && SOLARIS >= 205) */ -# if SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) -# define HASSNPRINTF 1 /* has snprintf starting in 2.6 */ -# else /* SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) */ -# if _FFR_MILTER -# define SM_INT32 int /* 32bit integer */ -# endif /* _FFR_MILTER */ -# endif /* SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) */ -# if SOLARIS >= 20700 || (SOLARIS < 10000 && SOLARIS >= 207) -# ifndef LA_TYPE -# include <sys/loadavg.h> -# if SOLARIS >= 20900 || (SOLARIS < 10000 && SOLARIS >= 209) -# include <sys/pset.h> -# define LA_TYPE LA_PSET /* pset_getloadavg(3c) appears in 2.9 */ -# else -# define LA_TYPE LA_SUBR /* getloadavg(3c) appears in 2.7 */ -# endif /* SOLARIS >= 20900 || (SOLARIS < 10000 && SOLARIS >= 209) */ -# endif /* ! LA_TYPE */ -# define HASGETUSERSHELL 1 /* getusershell(3c) bug fixed in 2.7 */ -# endif /* SOLARIS >= 20700 || (SOLARIS < 10000 && SOLARIS >= 207) */ -# if SOLARIS >= 20800 || (SOLARIS < 10000 && SOLARIS >= 208) -# define HASSTRL 1 /* str*(3) added in 2.8 */ -# undef _PATH_SENDMAILPID /* tmpfs /var/run added in 2.8 */ -# define _PATH_SENDMAILPID "/var/run/sendmail.pid" -# endif /* SOLARIS >= 20800 || (SOLARIS < 10000 && SOLARIS >= 208) */ -# if SOLARIS >= 20900 || (SOLARIS < 10000 && SOLARIS >= 209) -# define HASURANDOMDEV 1 /* /dev/[u]random added in S9 */ -# endif /* SOLARIS >= 20900 || (SOLARIS < 10000 && SOLARIS >= 209) */ -# ifndef HASGETUSERSHELL -# define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps pre-2.7 */ -# endif /* ! HASGETUSERSHELL */ - -# else /* SOLARIS */ - /* SunOS 4.0.3 or 4.1.x */ -# define HASGETUSERSHELL 1 /* DOES have getusershell(3) call in libc */ -# define HASSETREUID 1 /* has setreuid(2) call */ -# ifndef HASFLOCK -# define HASFLOCK 1 /* has flock(2) call */ -# endif /* ! HASFLOCK */ -# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */ -# define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone */ -# include <memory.h> -# include <vfork.h> -# ifdef __GNUC__ -# define strtoul strtol /* gcc library bogosity */ -# endif /* __GNUC__ */ -# define memmove(d, s, l) (bcopy((s), (d), (l))) - -# ifdef SUNOS403 - /* special tweaking for SunOS 4.0.3 */ -# include <malloc.h> -# define BSD4_3 1 /* 4.3 BSD-based */ -# define NEEDSTRSTR 1 /* need emulation of strstr(3) routine */ -# define WAITUNION 1 /* use "union wait" as wait argument type */ -# undef WIFEXITED -# undef WEXITSTATUS -# undef HASUNAME -# define setpgid setpgrp -# define MODE_T int -typedef int pid_t; -extern char *getenv(); - -# else /* SUNOS403 */ - /* 4.1.x specifics */ -# define HASSETSID 1 /* has Posix setsid(2) call */ -# define HASSETVBUF 1 /* we have setvbuf(3) in libc */ - -# endif /* SUNOS403 */ -# endif /* SOLARIS */ - -# ifndef LA_TYPE -# define LA_TYPE LA_INT -# endif /* ! LA_TYPE */ - -#endif /* defined(sun) && !defined(BSD) */ - -/* -** DG/UX -** -** Tested on 5.4.2 and 5.4.3. Use DGUX_5_4_2 to get the -** older support. -** 5.4.3 changes from Mark T. Robinson <mtr@ornl.gov>. -*/ - -#ifdef DGUX_5_4_2 -# define DGUX 1 -#endif /* DGUX_5_4_2 */ - -#ifdef DGUX -# define SYSTEM5 1 -# define LA_TYPE LA_DGUX -# define HASSETREUID 1 /* has setreuid(2) call */ -# define HASUNAME 1 /* use System V uname(2) system call */ -# define HASSETSID 1 /* has Posix setsid(2) call */ -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# define IP_SRCROUTE 0 /* does not have <netinet/ip_var.h> */ -# define HASGETUSERSHELL 0 /* does not have getusershell(3) */ -# define HASSNPRINTF 1 /* has snprintf(3) */ -# ifndef IDENTPROTO -# define IDENTPROTO 0 /* TCP/IP implementation is broken */ -# endif /* ! IDENTPROTO */ -# define SPT_TYPE SPT_NONE /* don't use setproctitle */ -# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ - -/* these include files must be included early on DG/UX */ -# include <netinet/in.h> -# include <arpa/inet.h> - -/* compiler doesn't understand const? */ -# define const - -# ifdef DGUX_5_4_2 -# define inet_addr dgux_inet_addr -extern long dgux_inet_addr(); -# endif /* DGUX_5_4_2 */ -#endif /* DGUX */ - - -/* -** Digital Ultrix 4.2A or 4.3 -** -** Apparently, fcntl locking is broken on 4.2A, in that locks are -** not dropped when the process exits. This causes major problems, -** so flock is the only alternative. -*/ - -#ifdef ultrix -# define HASSETREUID 1 /* has setreuid(2) call */ -# define HASUNSETENV 1 /* has unsetenv(3) call */ -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# define HASUNAME 1 /* use System V uname(2) system call */ -# define HASFCHMOD 1 /* has fchmod(2) syscall */ -# define HASFCHOWN 1 /* has fchown(2) syscall */ -# ifndef HASFLOCK -# define HASFLOCK 1 /* has flock(2) call */ -# endif /* ! HASFLOCK */ -# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ -# ifndef BROKEN_RES_SEARCH -# define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_errno=0 */ -# endif /* ! BROKEN_RES_SEARCH */ -# if !defined(NEEDLOCAL_HOSTNAME_LENGTH) && NAMED_BIND && __RES >= 19931104 && __RES < 19950621 -# define NEEDLOCAL_HOSTNAME_LENGTH 1 /* see sendmail/README */ -# endif /* !defined(NEEDLOCAL_HOSTNAME_LENGTH) && NAMED_BIND && __RES >= 19931104 && __RES < 19950621 */ -# ifdef vax -# define LA_TYPE LA_FLOAT -# else /* vax */ -# define LA_TYPE LA_INT -# define LA_AVENRUN "avenrun" -# endif /* vax */ -# define SFS_TYPE SFS_MOUNT /* use <sys/mount.h> statfs() impl */ -# ifndef IDENTPROTO -# define IDENTPROTO 0 /* pre-4.4 TCP/IP implementation is broken */ -# endif /* ! IDENTPROTO */ -# define SYSLOG_BUFSIZE 256 -#endif /* ultrix */ - - -/* -** OSF/1 for KSR. -** -** Contributed by Todd C. Miller <Todd.Miller@cs.colorado.edu> -*/ - -#ifdef __ksr__ -# define __osf__ 1 /* get OSF/1 defines below */ -# ifndef TZ_TYPE -# define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ -# endif /* ! TZ_TYPE */ -#endif /* __ksr__ */ - - -/* -** OSF/1 for Intel Paragon. -** -** Contributed by Jeff A. Earickson <jeff@ssd.intel.com> -** of Intel Scalable Systems Divison. -*/ - -#ifdef __PARAGON__ -# define __osf__ 1 /* get OSF/1 defines below */ -# ifndef TZ_TYPE -# define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ -# endif /* ! TZ_TYPE */ -# define GIDSET_T gid_t -# define MAXNAMLEN NAME_MAX -#endif /* __PARAGON__ */ - - -/* -** Tru64 UNIX, formerly known as Digital UNIX, formerly known as DEC OSF/1 -** -** Tested for 3.2 and 4.0. -*/ - -#ifdef __osf__ -# define HASUNAME 1 /* has uname(2) call */ -# define HASUNSETENV 1 /* has unsetenv(3) call */ -# define USESETEUID 1 /* has usable seteuid(2) call */ -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# define HASFCHMOD 1 /* has fchmod(2) syscall */ -# define HASFCHOWN 1 /* has fchown(2) syscall */ -# define HASSETLOGIN 1 /* has setlogin(2) */ -# define IP_SRCROUTE 1 /* can check IP source routing */ -# define HAS_ST_GEN 1 /* has st_gen field in stat struct */ -# define GIDSET_T gid_t -# if _FFR_MILTER -# define SM_INT32 int /* 32bit integer */ -# endif /* _FFR_MILTER */ -# ifndef HASFLOCK -# define HASFLOCK 1 /* has flock(2) call */ -# endif /* ! HASFLOCK */ -# define LA_TYPE LA_ALPHAOSF -# define SFS_TYPE SFS_STATVFS /* use <sys/statvfs.h> statfs() impl */ -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/var/adm/sendmail/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/var/run/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ -#endif /* __osf__ */ - - -/* -** NeXTstep -*/ - -#ifdef NeXT -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# define NEEDPUTENV 2 /* need putenv(3) call; no setenv(3) call */ -# ifndef HASFLOCK -# define HASFLOCK 1 /* has flock(2) call */ -# endif /* ! HASFLOCK */ -# define NEEDGETOPT 1 /* need a replacement for getopt(3) */ -# define WAITUNION 1 /* use "union wait" as wait argument type */ -# define UID_T int /* compiler gripes on uid_t */ -# define GID_T int /* ditto for gid_t */ -# define MODE_T int /* and mode_t */ -# define setpgid setpgrp -# ifndef NOT_SENDMAIL -# define sleep sleepX -# endif /* ! NOT_SENDMAIL */ -# ifndef LA_TYPE -# define LA_TYPE LA_MACH -# endif /* ! LA_TYPE */ -# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */ -# ifndef _POSIX_SOURCE -typedef int pid_t; -# undef WEXITSTATUS -# undef WIFEXITED -# undef WIFSTOPPED -# undef WTERMSIG -# endif /* ! _POSIX_SOURCE */ -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/etc/sendmail/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/etc/sendmail/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ - -# ifdef TCPWRAPPERS -# ifndef HASUNSETENV -# define HASUNSETENV 1 -# endif /* ! HASUNSETENV */ -# undef NEEDPUTENV -# endif /* TCPWRAPPERS */ - -#endif /* NeXT */ - -/* -** Apple Rhapsody -** Contributed by Wilfredo Sanchez <wsanchez@apple.com> -** -** Also used for Apple Darwin support. -*/ - -#if defined(DARWIN) -# define HASFCHMOD 1 /* has fchmod(2) syscall */ -# define HASFLOCK 1 /* has flock(2) syscall */ -# define HASUNAME 1 /* has uname(2) syscall */ -# define HASUNSETENV 1 -# define HASSETSID 1 /* has the setsid(2) POSIX syscall */ -# define HASINITGROUPS 1 -# define HASSETVBUF 1 -# define HASSETREUID 1 -# define USESETEUID 1 /* has usable seteuid(2) call */ -# define HASLSTAT 1 -# define HASSETRLIMIT 1 -# define HASWAITPID 1 -# define HASSTRERROR 1 /* has strerror(3) */ -# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ -# define HASSTRERROR 1 /* has strerror(3) */ -# define HASGETDTABLESIZE 1 -# define HASGETUSERSHELL 1 -# define NEEDGETOPT 1 /* need a replacement for getopt(3) */ -# define BSD4_4_SOCKADDR /* has sa_len */ -# define NETLINK 1 /* supports AF_LINK */ -# define HAS_ST_GEN 1 /* has st_gen field in stat struct */ -# define GIDSET_T gid_t -# define LA_TYPE LA_SUBR /* use getloadavg(3) */ -# define SFS_TYPE SFS_MOUNT /* use <sys/mount.h> statfs() impl */ -# define SPT_TYPE SPT_PSSTRINGS -# define SPT_PADCHAR '\0' /* pad process title with nulls */ -# define ERRLIST_PREDEFINED /* don't declare sys_errlist */ -#endif /* DARWIN */ - - -/* -** 4.4 BSD -** -** See also BSD defines. -*/ - -#if defined(BSD4_4) && !defined(__bsdi__) && !defined(__GNU__) -# include <paths.h> -# define HASUNSETENV 1 /* has unsetenv(3) call */ -# define USESETEUID 1 /* has usable seteuid(2) call */ -# define HASFCHMOD 1 /* has fchmod(2) syscall */ -# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ -# define HASSTRERROR 1 /* has strerror(3) */ -# define HAS_ST_GEN 1 /* has st_gen field in stat struct */ -# include <sys/cdefs.h> -# define ERRLIST_PREDEFINED /* don't declare sys_errlist */ -# define BSD4_4_SOCKADDR /* has sa_len */ -# define NEED_PRINTF_PERCENTQ 1 /* doesn't have %lld */ -# define NETLINK 1 /* supports AF_LINK */ -# ifndef LA_TYPE -# define LA_TYPE LA_SUBR -# endif /* ! LA_TYPE */ -# define SFS_TYPE SFS_MOUNT /* use <sys/mount.h> statfs() impl */ -# define SPT_TYPE SPT_PSSTRINGS /* use PS_STRINGS pointer */ -#endif /* defined(BSD4_4) && !defined(__bsdi__) && !defined(__GNU__) */ - - -/* -** BSD/OS (was BSD/386) (all versions) -** From Tony Sanders, BSDI -*/ - -#ifdef __bsdi__ -# include <paths.h> -# define HASUNSETENV 1 /* has the unsetenv(3) call */ -# define HASSETREUID 0 /* BSD-OS has broken setreuid(2) emulation */ -# define HASSETSID 1 /* has the setsid(2) POSIX syscall */ -# define USESETEUID 1 /* has usable seteuid(2) call */ -# define HASFCHMOD 1 /* has fchmod(2) syscall */ -# define HASFCHOWN 1 /* has fchown(2) syscall */ -# define HASSETLOGIN 1 /* has setlogin(2) */ -# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ -# define HASUNAME 1 /* has uname(2) syscall */ -# define HASSTRERROR 1 /* has strerror(3) */ -# define HAS_ST_GEN 1 /* has st_gen field in stat struct */ -# include <sys/cdefs.h> -# define ERRLIST_PREDEFINED /* don't declare sys_errlist */ -# define BSD4_4_SOCKADDR /* has sa_len */ -# define NETLINK 1 /* supports AF_LINK */ -# define SFS_TYPE SFS_MOUNT /* use <sys/mount.h> statfs() impl */ -# ifndef LA_TYPE -# define LA_TYPE LA_SUBR -# endif /* ! LA_TYPE */ -# define GIDSET_T gid_t -# define QUAD_T quad_t -# if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199312 - /* version 1.1 or later */ -# undef SPT_TYPE -# define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */ -# else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199312 */ - /* version 1.0 or earlier */ -# define SPT_PADCHAR '\0' /* pad process title with nulls */ -# endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199312 */ -# if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199701 /* on 3.x */ -# define HASSETUSERCONTEXT 1 /* has setusercontext */ -# endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199701 */ -# if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199910 /* on 4.x */ -# define HASURANDOMDEV 1 /* has /dev/urandom(4) */ -# endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199910 */ -#endif /* __bsdi__ */ - - -/* -** QNX 4.2x -** Contributed by Glen McCready <glen@qnx.com>. -** -** Should work with all versions of QNX. -*/ - -#if defined(__QNX__) -# include <unix.h> -# include <sys/select.h> -# undef NGROUPS_MAX -# define HASSETSID 1 /* has the setsid(2) POSIX syscall */ -# define USESETEUID 1 /* has usable seteuid(2) call */ -# define HASFCHMOD 1 /* has fchmod(2) syscall */ -# define HASGETDTABLESIZE 1 /* has getdtablesize(2) call */ -# define HASSETREUID 1 /* has setreuid(2) call */ -# define HASSTRERROR 1 /* has strerror(3) */ -# define HASFLOCK 0 -# undef HASINITGROUPS /* has initgroups(3) call */ -# define NEEDGETOPT 1 /* use sendmail's getopt */ -# define IP_SRCROUTE 1 /* can check IP source routing */ -# define TZ_TYPE TZ_TMNAME /* use tmname variable */ -# define GIDSET_T gid_t -# define LA_TYPE LA_ZERO -# define SFS_TYPE SFS_NONE -# define SPT_TYPE SPT_REUSEARGV -# define SPT_PADCHAR '\0' /* pad process title with nulls */ -# define HASGETUSERSHELL 0 -# define E_PSEUDOBASE 512 -# define _FILE_H_INCLUDED -#endif /* defined(__QNX__) */ - - -/* -** FreeBSD / NetBSD / OpenBSD (all architectures, all versions) -** -** 4.3BSD clone, closer to 4.4BSD for FreeBSD 1.x and NetBSD 0.9x -** 4.4BSD-Lite based for FreeBSD 2.x and NetBSD 1.x -** -** See also BSD defines. -*/ - -#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) -# include <paths.h> -# define HASUNSETENV 1 /* has unsetenv(3) call */ -# define HASSETSID 1 /* has the setsid(2) POSIX syscall */ -# define USESETEUID 1 /* has usable seteuid(2) call */ -# define HASFCHMOD 1 /* has fchmod(2) syscall */ -# define HASFCHOWN 1 /* fchown(2) */ -# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ -# define HASUNAME 1 /* has uname(2) syscall */ -# define HASSTRERROR 1 /* has strerror(3) */ -# define HAS_ST_GEN 1 /* has st_gen field in stat struct */ -# define NEED_PRINTF_PERCENTQ 1 /* doesn't have %lld */ -# include <sys/cdefs.h> -# define ERRLIST_PREDEFINED /* don't declare sys_errlist */ -# define BSD4_4_SOCKADDR /* has sa_len */ -# define NETLINK 1 /* supports AF_LINK */ -# define SAFENFSPATHCONF 1 /* pathconf(2) pessimizes on NFS filesystems */ -# define GIDSET_T gid_t -# define QUAD_T unsigned long long -# define SFIO_STDIO_COMPAT 1 /* can use RES_DEBUG */ -# ifndef LA_TYPE -# define LA_TYPE LA_SUBR -# endif /* ! LA_TYPE */ -# define SFS_TYPE SFS_MOUNT /* use <sys/mount.h> statfs() impl */ -# if defined(__NetBSD__) && (NetBSD > 199307 || NetBSD0_9 > 1) -# undef SPT_TYPE -# define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */ -# endif /* defined(__NetBSD__) && (NetBSD > 199307 || NetBSD0_9 > 1) */ -# if defined(__NetBSD__) && ((__NetBSD_Version__ > 102070000) || (NetBSD1_2 > 8) || defined(NetBSD1_4) || defined(NetBSD1_3)) -# define HASURANDOMDEV 1 /* has /dev/urandom(4) */ -# endif /* defined(__NetBSD__) && ((__NetBSD_Version__ > 102070000) || (NetBSD1_2 > 8) || defined(NetBSD1_4) || defined(NetBSD1_3)) */ -# if defined(__FreeBSD__) -# define HASSETLOGIN 1 /* has setlogin(2) */ -# if __FreeBSD_version >= 227001 -# define HASSRANDOMDEV 1 /* has srandomdev(3) */ -# define HASURANDOMDEV 1 /* has /dev/urandom(4) */ -# endif /* __FreeBSD_version >= 227001 */ -# undef SPT_TYPE -# if __FreeBSD__ >= 2 -# include <osreldate.h> -# if __FreeBSD_version >= 199512 /* 2.2-current when it appeared */ -# include <libutil.h> -# define SPT_TYPE SPT_BUILTIN -# endif /* __FreeBSD_version >= 199512 */ -# if __FreeBSD_version >= 222000 /* 2.2.2-release and later */ -# define HASSETUSERCONTEXT 1 /* BSDI-style login classes */ -# endif /* __FreeBSD_version >= 222000 */ -# if __FreeBSD_version >= 330000 /* 3.3.0-release and later */ -# ifndef HASSTRL -# define HASSTRL 1 /* has strlc{py,at}(3) functions */ -# endif /* HASSTRL */ -# endif /* __FreeBSD_version >= 330000 */ -# define USESYSCTL 1 /* use sysctl(3) for getting ncpus */ -# include <sys/sysctl.h> -# endif /* __FreeBSD__ >= 2 */ -# ifndef SPT_TYPE -# define SPT_TYPE SPT_REUSEARGV -# define SPT_PADCHAR '\0' /* pad process title with nulls */ -# endif /* ! SPT_TYPE */ -# endif /* defined(__FreeBSD__) */ -# if defined(__OpenBSD__) -# undef SPT_TYPE -# define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */ -# define HASSETLOGIN 1 /* has setlogin(2) */ -# define HASSETREUID 0 /* OpenBSD has broken setreuid(2) emulation */ -# define HASURANDOMDEV 1 /* has /dev/urandom(4) */ -# if OpenBSD < 199912 -# define HASSTRL 0 /* strlcat(3) is broken in 2.5 and earlier */ -# else /* OpenBSD < 199912 */ -# define HASSTRL 1 /* has strlc{py,at}(3) functions */ -# if OpenBSD >= 200006 -# define HASSRANDOMDEV 1 /* has srandomdev(3) */ -# endif -# if OpenBSD >= 200012 -# define HASSETUSERCONTEXT 1 /* BSDI-style login classes */ -# endif -# endif /* OpenBSD < 199912 */ -# endif /* defined(__OpenBSD__) */ -#endif /* defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) */ - - -/* -** Mach386 -** -** For mt Xinu's Mach386 system. -*/ - -#if defined(MACH) && defined(i386) && !defined(__GNU__) -# define MACH386 1 -# define HASUNSETENV 1 /* has unsetenv(3) call */ -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# ifndef HASFLOCK -# define HASFLOCK 1 /* has flock(2) call */ -# endif /* ! HASFLOCK */ -# define NEEDGETOPT 1 /* need a replacement for getopt(3) */ -# define NEEDSTRTOL 1 /* need the strtol() function */ -# define setpgid setpgrp -# ifndef LA_TYPE -# define LA_TYPE LA_FLOAT -# endif /* ! LA_TYPE */ -# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */ -# undef HASSETVBUF /* don't actually have setvbuf(3) */ -# undef WEXITSTATUS -# undef WIFEXITED -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/etc/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ -#endif /* defined(MACH) && defined(i386) && !defined(__GNU__) */ - - - -/* -** GNU OS (hurd) -** Largely BSD & posix compatible. -** Port contributed by Miles Bader <miles@gnu.ai.mit.edu>. -** Updated by Mark Kettenis <kettenis@wins.uva.nl>. -*/ - -#if defined(__GNU__) && !defined(NeXT) -# include <paths.h> -# define HASFCHMOD 1 /* has fchmod(2) call */ -# define HASFCHOWN 1 /* has fchown(2) call */ -# define HASUNAME 1 /* has uname(2) call */ -# define HASUNSETENV 1 /* has unsetenv(3) call */ -# define HAS_ST_GEN 1 /* has st_gen field in stat struct */ -# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ -# define HASSTRERROR 1 /* has strerror(3) */ -# define GIDSET_T gid_t -# define SOCKADDR_LEN_T socklen_t -# define SOCKOPT_LEN_T socklen_t -# if (__GLIBC__ == 2 && __GLIBC_MINOR__ > 1) || __GLIBC__ > 2 -# define LA_TYPE LA_SUBR -# else -# define LA_TYPE LA_MACH - /* GNU uses mach[34], which renames some rpcs from mach2.x. */ -# define host_self mach_host_self -# endif -# define SFS_TYPE SFS_STATFS -# define SPT_TYPE SPT_CHANGEARGV -# define ERRLIST_PREDEFINED 1 /* don't declare sys_errlist */ -# define BSD4_4_SOCKADDR 1 /* has sa_len */ -# define SIOCGIFCONF_IS_BROKEN 1 /* SIOCGFCONF doesn't work */ -# define HAS_IN_H 1 /* GNU has netinet/in.h. */ -/* GNU has no MAXPATHLEN; ideally the code should be changed to not use it. */ -# define MAXPATHLEN 2048 -#endif /* defined(__GNU__) && !defined(NeXT) */ - -/* -** 4.3 BSD -- this is for very old systems -** -** Should work for mt Xinu MORE/BSD and Mips UMIPS-BSD 2.1. -** -** You'll also have to install a new resolver library. -** I don't guarantee that support for this environment is complete. -*/ - -#if defined(oldBSD43) || defined(MORE_BSD) || defined(umipsbsd) -# define NEEDVPRINTF 1 /* need a replacement for vprintf(3) */ -# define NEEDGETOPT 1 /* need a replacement for getopt(3) */ -# define ARBPTR_T char * -# define setpgid setpgrp -# ifndef LA_TYPE -# define LA_TYPE LA_FLOAT -# endif /* ! LA_TYPE */ -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef IDENTPROTO -# define IDENTPROTO 0 /* TCP/IP implementation is broken */ -# endif /* ! IDENTPROTO */ -# undef WEXITSTATUS -# undef WIFEXITED -typedef short pid_t; -#endif /* defined(oldBSD43) || defined(MORE_BSD) || defined(umipsbsd) */ - - -/* -** SCO Unix -** -** This includes three parts: -** -** The first is for SCO OpenServer 5. -** (Contributed by Keith Reynolds <keithr@sco.COM>). -** -** SCO OpenServer 5 has a compiler version number macro, -** which we can use to figure out what version we're on. -** This may have to change in future releases. -** -** The second is for SCO UNIX 3.2v4.2/Open Desktop 3.0. -** (Contributed by Philippe Brand <phb@colombo.telesys-innov.fr>). -** -** The third is for SCO UNIX 3.2v4.0/Open Desktop 2.0 and earlier. -*/ - -/* SCO OpenServer 5 */ -#if _SCO_DS >= 1 -# include <paths.h> -# define SIOCGIFNUM_IS_BROKEN 1 /* SIOCGIFNUM returns bogus value */ -# define HASSNPRINTF 1 /* has snprintf(3) call */ -# define HASFCHMOD 1 /* has fchmod(2) call */ -# define HASFCHOWN 1 /* has fchown(2) call */ -# define HASSETRLIMIT 1 /* has setrlimit(2) call */ -# define USESETEUID 1 /* has seteuid(2) call */ -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# define HASGETDTABLESIZE 1 /* has getdtablesize(2) call */ -# define RLIMIT_NEEDS_SYS_TIME_H 1 -# ifndef LA_TYPE -# define LA_TYPE LA_DEVSHORT -# endif /* ! LA_TYPE */ -# define _PATH_AVENRUN "/dev/table/avenrun" -# ifndef _SCO_unix_4_2 -# define _SCO_unix_4_2 -# else /* ! _SCO_unix_4_2 */ -# define SOCKADDR_LEN_T size_t /* e.g., arg#3 to accept, getsockname */ -# define SOCKOPT_LEN_T size_t /* arg#5 to getsockopt */ -# endif /* ! _SCO_unix_4_2 */ -#endif /* _SCO_DS >= 1 */ - -/* SCO UNIX 3.2v4.2/Open Desktop 3.0 */ -#ifdef _SCO_unix_4_2 -# define _SCO_unix_ -# define HASSETREUID 1 /* has setreuid(2) call */ -#endif /* _SCO_unix_4_2 */ - -/* SCO UNIX 3.2v4.0 Open Desktop 2.0 and earlier */ -#ifdef _SCO_unix_ -# include <sys/stream.h> /* needed for IP_SRCROUTE */ -# define SYSTEM5 1 /* include all the System V defines */ -# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ -# define NOFTRUNCATE 0 /* has (simulated) ftruncate call */ -# define USE_SIGLONGJMP 1 /* sigsetjmp needed for signal handling */ -# define MAXPATHLEN PATHSIZE -# define SFS_TYPE SFS_4ARGS /* use <sys/statfs.h> 4-arg impl */ -# define SFS_BAVAIL f_bfree /* alternate field name */ -# define SPT_TYPE SPT_SCO /* write kernel u. area */ -# define TZ_TYPE TZ_TM_NAME /* use tm->tm_name */ -# define UID_T uid_t -# define GID_T gid_t -# define GIDSET_T gid_t -# define _PATH_UNIX "/unix" -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/etc/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ - -/* stuff fixed in later releases */ -# ifndef _SCO_unix_4_2 -# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ -# endif /* ! _SCO_unix_4_2 */ - -# ifndef _SCO_DS -# define ftruncate chsize /* use chsize(2) to emulate ftruncate */ -# define NEEDFSYNC 1 /* needs the fsync(2) call stub */ -# define NETUNIX 0 /* no unix domain socket support */ -# define LA_TYPE LA_SHORT -# endif /* ! _SCO_DS */ - -#endif /* _SCO_unix_ */ - - -/* -** ISC (SunSoft) Unix. -** -** Contributed by J.J. Bailey <jjb@jagware.bcc.com> -*/ - -#ifdef ISC_UNIX -# include <net/errno.h> -# include <sys/stream.h> /* needed for IP_SRCROUTE */ -# include <sys/bsdtypes.h> -# define SYSTEM5 1 /* include all the System V defines */ -# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ -# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ -# define HASSETREUID 1 /* has setreuid(2) call */ -# define NEEDFSYNC 1 /* needs the fsync(2) call stub */ -# define NETUNIX 0 /* no unix domain socket support */ -# define MAXPATHLEN 1024 -# define LA_TYPE LA_SHORT -# define SFS_TYPE SFS_STATFS /* use <sys/statfs.h> statfs() impl */ -# define SFS_BAVAIL f_bfree /* alternate field name */ -# define _PATH_UNIX "/unix" -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/etc/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ -#endif /* ISC_UNIX */ - - -/* -** Altos System V (5.3.1) -** Contributed by Tim Rice <tim@trr.metro.net>. -*/ - -#ifdef ALTOS_SYSTEM_V -# include <sys/stream.h> -# include <limits.h> -# define SYSTEM5 1 /* include all the System V defines */ -# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ -# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ -# define WAITUNION 1 /* use "union wait" as wait argument type */ -# define NEEDFSYNC 1 /* no fsync(2) in system library */ -# define NEEDSTRSTR 1 /* need emulation of the strstr(3) call */ -# define NOFTRUNCATE 1 /* do not have ftruncate(2) */ -# define MAXPATHLEN PATH_MAX -# define LA_TYPE LA_SHORT -# define SFS_TYPE SFS_STATFS /* use <sys/statfs.h> statfs() impl */ -# define SFS_BAVAIL f_bfree /* alternate field name */ -# define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ -# define NETUNIX 0 /* no unix domain socket support */ -# undef WIFEXITED -# undef WEXITSTATUS -# define strtoul strtol /* gcc library bogosity */ - -typedef unsigned short uid_t; -typedef unsigned short gid_t; -typedef short pid_t; -typedef unsigned long mode_t; - -/* some stuff that should have been in the include files */ -extern char *malloc(); -extern struct passwd *getpwent(); -extern struct passwd *getpwnam(); -extern struct passwd *getpwuid(); -extern char *getenv(); -extern struct group *getgrgid(); -extern struct group *getgrnam(); - -#endif /* ALTOS_SYSTEM_V */ - - -/* -** ConvexOS 11.0 and later -** -** "Todd C. Miller" <millert@mroe.cs.colorado.edu> claims this -** works on 9.1 as well. -** -** ConvexOS 11.5 and later, should work on 11.0 as defined. -** For pre-ConvexOOS 11.0, define NEEDGETOPT, undef IDENTPROTO -** -** Eric Schnoebelen (eric@cirr.com) For CONVEX Computer Corp. -** (now the CONVEX Technologies Center of Hewlett Packard) -*/ - -#ifdef _CONVEX_SOURCE -# define HASGETDTABLESIZE 1 /* has getdtablesize(2) */ -# define HASINITGROUPS 1 /* has initgroups(3) */ -# define HASUNAME 1 /* use System V uname(2) system call */ -# define HASSETSID 1 /* has POSIX setsid(2) call */ -# define HASUNSETENV 1 /* has unsetenv(3) */ -# define HASFLOCK 1 /* has flock(2) */ -# define HASSETRLIMIT 1 /* has setrlimit(2) */ -# define HASSETREUID 1 /* has setreuid(2) */ -# define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_error=0 */ -# define NEEDPUTENV 1 /* needs putenv (written in terms of setenv) */ -# define NEEDGETOPT 0 /* need replacement for getopt(3) */ -# define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ -# define LA_TYPE LA_FLOAT -# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */ -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef S_IREAD -# define S_IREAD _S_IREAD -# define S_IWRITE _S_IWRITE -# define S_IEXEC _S_IEXEC -# define S_IFMT _S_IFMT -# define S_IFCHR _S_IFCHR -# define S_IFBLK _S_IFBLK -# endif /* ! S_IREAD */ -# ifndef TZ_TYPE -# define TZ_TYPE TZ_TIMEZONE -# endif /* ! TZ_TYPE */ -# ifndef IDENTPROTO -# define IDENTPROTO 1 -# endif /* ! IDENTPROTO */ -# ifndef SHARE_V1 -# define SHARE_V1 1 /* version 1 of the fair share scheduler */ -# endif /* ! SHARE_V1 */ -# if !defined(__GNUC__ ) -# define UID_T int /* GNUC gets it right, ConvexC botches */ -# define GID_T int /* GNUC gets it right, ConvexC botches */ -# endif /* !defined(__GNUC__ ) */ -# if SECUREWARE -# define FORK fork /* SecureWare wants the real fork! */ -# else /* SECUREWARE */ -# define FORK vfork /* the rest of the OS versions don't care */ -# endif /* SECUREWARE */ -#endif /* _CONVEX_SOURCE */ - - -/* -** RISC/os 4.52 -** -** Gives a ton of warning messages, but otherwise compiles. -*/ - -#ifdef RISCOS - -# define HASUNSETENV 1 /* has unsetenv(3) call */ -# ifndef HASFLOCK -# define HASFLOCK 1 /* has flock(2) call */ -# endif /* ! HASFLOCK */ -# define WAITUNION 1 /* use "union wait" as wait argument type */ -# define NEEDGETOPT 1 /* need a replacement for getopt(3) */ -# define NEEDPUTENV 1 /* need putenv(3) call */ -# define NEEDSTRSTR 1 /* need emulation of the strstr(3) call */ -# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */ -# define LA_TYPE LA_INT -# define LA_AVENRUN "avenrun" -# define _PATH_UNIX "/unix" -# undef WIFEXITED - -# define setpgid setpgrp - -typedef int pid_t; -# define SIGFUNC_DEFINED -# define SIGFUNC_RETURN (0) -# define SIGFUNC_DECL int -typedef int (*sigfunc_t)(); -extern char *getenv(); -extern void *malloc(); - -/* added for RISC/os 4.01...which is dumber than 4.50 */ -# ifdef RISCOS_4_0 -# ifndef ARBPTR_T -# define ARBPTR_T char * -# endif /* ! ARBPTR_T */ -# undef HASFLOCK -# define HASFLOCK 0 -# endif /* RISCOS_4_0 */ - -# include <sys/time.h> - -#endif /* RISCOS */ - - -/* -** Linux 0.99pl10 and above... -** -** Thanks to, in reverse order of contact: -** -** John Kennedy <warlock@csuchico.edu> -** Andrew Pam <avatar@aus.xanadu.com> -** Florian La Roche <rzsfl@rz.uni-sb.de> -** Karl London <karl@borg.demon.co.uk> -** -** Last compiled against: [07/21/98 @ 11:47:34 AM (Tuesday)] -** sendmail 8.9.1 bind-8.1.2 db-2.4.14 -** gcc-2.8.1 glibc-2.0.94 linux-2.1.109 -** -** NOTE: Override HASFLOCK as you will but, as of 1.99.6, mixed-style -** file locking is no longer allowed. In particular, make sure -** your DBM library and sendmail are both using either flock(2) -** *or* fcntl(2) file locking, but not both. -*/ - -#ifdef __linux__ -# include <linux/version.h> -# if !defined(KERNEL_VERSION) /* not defined in 2.0.x kernel series */ -# define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) -# endif /* KERNEL_VERSION */ -# define BSD 1 /* include BSD defines */ -# define USESETEUID 0 /* Have it due to POSIX, but doesn't work */ -# define NEEDGETOPT 1 /* need a replacement for getopt(3) */ -# define HASUNAME 1 /* use System V uname(2) system call */ -# define HASUNSETENV 1 /* has unsetenv(3) call */ -# ifndef HASSNPRINTF -# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ -# endif /* ! HASSNPRINTF */ -# define ERRLIST_PREDEFINED /* don't declare sys_errlist */ -# define GIDSET_T gid_t /* from <linux/types.h> */ -# define HASGETUSERSHELL 0 /* getusershell(3) broken in Slackware 2.0 */ -# ifndef IP_SRCROUTE -# define IP_SRCROUTE 0 /* linux <= 1.2.8 doesn't support IP_OPTIONS */ -# endif /* ! IP_SRCROUTE */ -# ifndef HAS_IN_H -# define HAS_IN_H 1 /* use netinet/in.h */ -# endif /* ! HAS_IN_H */ -# define USE_SIGLONGJMP 1 /* sigsetjmp needed for signal handling */ -# ifndef HASFLOCK -# if LINUX_VERSION_CODE < 66399 -# define HASFLOCK 0 /* flock(2) is broken after 0.99.13 */ -# else /* LINUX_VERSION_CODE < 66399 */ -# define HASFLOCK 1 /* flock(2) fixed after 1.3.95 */ -# endif /* LINUX_VERSION_CODE < 66399 */ -# endif /* ! HASFLOCK */ -# ifndef LA_TYPE -# define LA_TYPE LA_PROCSTR -# endif /* ! LA_TYPE */ -# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() impl */ -# define SPT_PADCHAR '\0' /* pad process title with nulls */ -# if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,0,0)) -# ifndef HASURANDOMDEV -# define HASURANDOMDEV 1 /* 2.0 (at least) has linux/drivers/char/random.c */ -# endif /* ! HASURANDOMDEV */ -# endif /* LINUX_VERSION_CODE */ -# ifndef TZ_TYPE -# define TZ_TYPE TZ_NONE /* no standard for Linux */ -# endif /* ! TZ_TYPE */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/var/run/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ -# include <sys/sysmacros.h> -# undef atol /* wounded in <stdlib.h> */ -# if NETINET6 - /* - ** Linux doesn't have a good way to tell userland what interfaces are - ** IPv6-capable. Therefore, the BIND resolver can not determine if there - ** are IPv6 interfaces to honor AI_ADDRCONFIG. Unfortunately, it assumes - ** that none are present. (Excuse the macro name ADDRCONFIG_IS_BROKEN.) - */ -# define ADDRCONFIG_IS_BROKEN 1 - - /* - ** Indirectly included from glibc's <feature.h>. IPv6 support is native - ** in 2.1 and later, but the APIs appear before the functions. - */ -# if defined(__GLIBC__) && defined(__GLIBC_MINOR__) -# define GLIBC_VERSION ((__GLIBC__ << 8) + __GLIBC_MINOR__) -# if (GLIBC_VERSION >= 0x201) -# undef IPPROTO_ICMPV6 /* linux #defines, glibc enums */ -# else /* (GLIBC_VERSION >= 0x201) */ -# include <linux/in6.h> /* IPv6 support */ -# endif /* (GLIBC_VERSION >= 0x201) */ -# if (GLIBC_VERSION >= 0x201 && !defined(NEEDSGETIPNODE)) - /* Have APIs in <netdb.h>, but no support in glibc */ -# define NEEDSGETIPNODE 1 -# endif /* (GLIBC_VERSION >= 0x201 && !defined(NEEDSGETIPNODE)) */ -# undef GLIBC_VERSION -# endif /* defined(__GLIBC__) && defined(__GLIBC_MINOR__) */ -# endif /* NETINET6 */ -# ifndef HASFCHOWN -# define HASFCHOWN 1 /* fchown(2) */ -# endif /* ! HASFCHOWN */ -#endif /* __linux__ */ - - -/* -** DELL SVR4 Issue 2.2, and others -** From Kimmo Suominen <kim@grendel.lut.fi> -** -** It's on #ifdef DELL_SVR4 because Solaris also gets __svr4__ -** defined, and the definitions conflict. -** -** Peter Wemm <peter@perth.DIALix.oz.au> claims that the setreuid -** trick works on DELL 2.2 (SVR4.0/386 version 4.0) and ESIX 4.0.3A -** (SVR4.0/386 version 3.0). -*/ - -#ifdef DELL_SVR4 - /* no changes necessary */ - /* see general __svr4__ defines below */ -#endif /* DELL_SVR4 */ - - -/* -** Apple A/UX 3.0 -*/ - -#ifdef _AUX_SOURCE -# include <sys/sysmacros.h> -# define BSD /* has BSD routines */ -# define HASSETRLIMIT 0 /* ... but not setrlimit(2) */ -# define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_errno=0 */ -# define BOGUS_O_EXCL 1 /* exclusive open follows symlinks */ -# define HASUNAME 1 /* use System V uname(2) system call */ -# define HASFCHMOD 1 /* has fchmod(2) syscall */ -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# define HASSETVBUF 1 /* has setvbuf(3) in libc */ -# define HASSTRERROR 1 /* has strerror(3) */ -# define SIGFUNC_DEFINED /* sigfunc_t already defined */ -# define SIGFUNC_RETURN /* POSIX-mode */ -# define SIGFUNC_DECL void /* POSIX-mode */ -# define ERRLIST_PREDEFINED 1 -# ifndef IDENTPROTO -# define IDENTPROTO 0 /* TCP/IP implementation is broken */ -# endif /* ! IDENTPROTO */ -# ifndef LA_TYPE -# define LA_TYPE LA_INT -# define FSHIFT 16 -# endif /* ! LA_TYPE */ -# define LA_AVENRUN "avenrun" -# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */ -# define TZ_TYPE TZ_TZNAME -# ifndef _PATH_UNIX -# define _PATH_UNIX "/unix" /* should be in <paths.h> */ -# endif /* ! _PATH_UNIX */ -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# undef WIFEXITED -# undef WEXITSTATUS -#endif /* _AUX_SOURCE */ - - -/* -** Encore UMAX V -** -** Not extensively tested. -*/ - -#ifdef UMAXV -# define HASUNAME 1 /* use System V uname(2) system call */ -# define HASSETVBUF 1 /* we have setvbuf(3) in libc */ -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ -# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ -# define SYS5SETPGRP 1 /* use System V setpgrp(2) syscall */ -# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ -# define MAXPATHLEN PATH_MAX -extern struct passwd *getpwent(), *getpwnam(), *getpwuid(); -extern struct group *getgrent(), *getgrnam(), *getgrgid(); -# undef WIFEXITED -# undef WEXITSTATUS -#endif /* UMAXV */ - - -/* -** Stardent Titan 3000 running TitanOS 4.2. -** -** Must be compiled in "cc -43" mode. -** -** From Kate Hedstrom <kate@ahab.rutgers.edu>. -** -** Note the tweaking below after the BSD defines are set. -*/ - -#ifdef titan -# define setpgid setpgrp -typedef int pid_t; -# undef WIFEXITED -# undef WEXITSTATUS -#endif /* titan */ - - -/* -** Sequent DYNIX 3.2.0 -** -** From Jim Davis <jdavis@cs.arizona.edu>. -*/ - -#ifdef sequent - -# define BSD 1 -# define HASUNSETENV 1 -# define BSD4_3 1 /* to get signal() in conf.c */ -# define WAITUNION 1 -# define LA_TYPE LA_FLOAT -# ifdef _POSIX_VERSION -# undef _POSIX_VERSION /* set in <unistd.h> */ -# endif /* _POSIX_VERSION */ -# undef HASSETVBUF /* don't actually have setvbuf(3) */ -# define setpgid setpgrp - -/* Have to redefine WIFEXITED to take an int, to work with waitfor() */ -# undef WIFEXITED -# define WIFEXITED(s) (((union wait*)&(s))->w_stopval != WSTOPPED && \ - ((union wait*)&(s))->w_termsig == 0) -# define WEXITSTATUS(s) (((union wait*)&(s))->w_retcode) -typedef int pid_t; -# define isgraph(c) (isprint(c) && (c != ' ')) - -# ifndef IDENTPROTO -# define IDENTPROTO 0 /* TCP/IP implementation is broken */ -# endif /* ! IDENTPROTO */ - -# ifndef _PATH_UNIX -# define _PATH_UNIX "/dynix" -# endif /* ! _PATH_UNIX */ -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -#endif /* sequent */ - - -/* -** Sequent DYNIX/ptx v2.0 (and higher) -** -** For DYNIX/ptx v1.x, undefine HASSETREUID. -** -** From Tim Wright <timw@sequent.com>. -** Update from Jack Woolley <jwoolley@sctcorp.com>, 26 Dec 1995, -** for DYNIX/ptx 4.0.2. -*/ - -#ifdef _SEQUENT_ -# include <sys/stream.h> -# define SYSTEM5 1 /* include all the System V defines */ -# define HASSETSID 1 /* has POSIX setsid(2) call */ -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# define HASSETREUID 1 /* has setreuid(2) call */ -# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ -# define GIDSET_T gid_t -# define LA_TYPE LA_INT -# define SFS_TYPE SFS_STATFS /* use <sys/statfs.h> statfs() impl */ -# define SPT_TYPE SPT_NONE /* don't use setproctitle */ -# ifndef IDENTPROTO -# define IDENTPROTO 0 /* TCP/IP implementation is broken */ -# endif /* ! IDENTPROTO */ -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/etc/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ -#endif /* _SEQUENT_ */ - - -/* -** Cray Unicos -** -** Ported by David L. Kensiski, Sterling Sofware <kensiski@nas.nasa.gov> -*/ - -#ifdef UNICOS -# define SYSTEM5 1 /* include all the System V defines */ -# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ -# define MAXPATHLEN PATHSIZE -# define LA_TYPE LA_ZERO -# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ -# define SFS_BAVAIL f_bfree /* alternate field name */ -#endif /* UNICOS */ - - -/* -** Apollo DomainOS -** -** From Todd Martin <tmartint@tus.ssi1.com> & Don Lewis <gdonl@gv.ssi1.com> -** -** 15 Jan 1994; updated 2 Aug 1995 -** -*/ - -#ifdef apollo -# define HASSETREUID 1 /* has setreuid(2) call */ -# define HASINITGROUPS 1 /* has initgroups(2) call */ -# define IP_SRCROUTE 0 /* does not have <netinet/ip_var.h> */ -# define SPT_TYPE SPT_NONE /* don't use setproctitle */ -# define LA_TYPE LA_SUBR /* use getloadavg.c */ -# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ -# define SFS_BAVAIL f_bfree /* alternate field name */ -# define TZ_TYPE TZ_TZNAME -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/etc/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ -# undef S_IFSOCK /* S_IFSOCK and S_IFIFO are the same */ -# undef S_IFIFO -# define S_IFIFO 0010000 -# ifndef IDENTPROTO -# define IDENTPROTO 0 /* TCP/IP implementation is broken */ -# endif /* ! IDENTPROTO */ -# define RLIMIT_NEEDS_SYS_TIME_H 1 -# if defined(NGROUPS_MAX) && !NGROUPS_MAX -# undef NGROUPS_MAX -# endif /* defined(NGROUPS_MAX) && !NGROUPS_MAX */ -#endif /* apollo */ - -/* -** System V Rel 5.x (a.k.a Unixware7 w/o BSD-Compatibility Libs ie. native) -** -** Contributed by Paul Gampe <paulg@apnic.net> -*/ - -#ifdef __svr5__ -# include <sys/mkdev.h> -# define __svr4__ -# define SYS5SIGNALS 1 -# define HASFCHOWN 1 /* has fchown(2) call */ -# define HASSETSID 1 -# define HASSNPRINTF 1 -# define HASSETREUID 1 -# define HASWAITPID 1 -# define HASGETDTABLESIZE 1 -# define GIDSET_T gid_t -# define SOCKADDR_LEN_T size_t -# define SOCKOPT_LEN_T size_t -# ifndef _PATH_UNIX -# define _PATH_UNIX "/stand/unix" -# endif /* ! _PATH_UNIX */ -# define SPT_PADCHAR '\0' /* pad process title with nulls */ -# ifndef SYSLOG_BUFSIZE -# define SYSLOG_BUFSIZE 1024 /* unsure */ -# endif /* SYSLOG_BUFSIZE */ -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/etc/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/etc/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ -# undef offsetof /* avoid stddefs.h, sys/sysmacros.h conflict */ -#if !defined(SM_SET_H_ERRNO) && defined(_REENTRANT) -# define SM_SET_H_ERRNO(err) set_h_errno((err)) -#endif /* ! SM_SET_H_ERRNO && _REENTRANT */ -#endif /* __svr5__ */ - -/* ###################################################################### */ - -/* -** UnixWare 2.x -*/ - -#ifdef UNIXWARE2 -# define UNIXWARE 1 -# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ -# undef offsetof /* avoid stddefs.h, sys/sysmacros.h conflict */ -#endif /* UNIXWARE2 */ - - -/* -** UnixWare 1.1.2. -** -** Updated by Petr Lampa <lampa@fee.vutbr.cz>. -** From Evan Champion <evanc@spatial.synapse.org>. -*/ - -#ifdef UNIXWARE -# include <sys/mkdev.h> -# define SYSTEM5 1 -# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ -# define HASSETREUID 1 -# define HASSETSID 1 -# define HASINITGROUPS 1 -# define GIDSET_T gid_t -# define SLEEP_T unsigned -# define SFS_TYPE SFS_STATVFS -# define LA_TYPE LA_ZERO -# undef WIFEXITED -# undef WEXITSTATUS -# ifndef _PATH_UNIX -# define _PATH_UNIX "/unix" -# endif /* ! _PATH_UNIX */ -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ -# define SYSLOG_BUFSIZE 128 -#endif /* UNIXWARE */ - - -/* -** Intergraph CLIX 3.1 -** -** From Paul Southworth <pauls@locust.cic.net> -*/ - -#ifdef CLIX -# define SYSTEM5 1 /* looks like System V */ -# ifndef HASGETUSERSHELL -# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ -# endif /* ! HASGETUSERSHELL */ -# define DEV_BSIZE 512 /* device block size not defined */ -# define GIDSET_T gid_t -# undef LOG /* syslog not available */ -# define NEEDFSYNC 1 /* no fsync in system library */ -# define GETSHORT _getshort -#endif /* CLIX */ - - -/* -** NCR MP-RAS 2.x (SysVr4) with Wollongong TCP/IP -** -** From Kevin Darcy <kevin@tech.mis.cfc.com>. -*/ - -#ifdef NCR_MP_RAS2 -# include <sys/sockio.h> -# define __svr4__ -# define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ -# define SYSLOG_BUFSIZE 1024 -# define SPT_TYPE SPT_NONE -#endif /* NCR_MP_RAS2 */ - - -/* -** NCR MP-RAS 3.x (SysVr4) with STREAMware TCP/IP -** -** From Tom Moore <Tom.Moore@DaytonOH.NCR.COM> -*/ - -#ifdef NCR_MP_RAS3 -# define __svr4__ -# define HASFCHOWN 1 /* has fchown(2) call */ -# define SIOCGIFNUM_IS_BROKEN 1 /* SIOCGIFNUM has non-std interface */ -# define SO_REUSEADDR_IS_BROKEN 1 /* doesn't work if accept() fails */ -# define SYSLOG_BUFSIZE 1024 -# define SPT_TYPE SPT_NONE -# ifndef _XOPEN_SOURCE -# define _XOPEN_SOURCE -# define _XOPEN_SOURCE_EXTENDED 1 -# include <sys/resource.h> -# undef _XOPEN_SOURCE -# undef _XOPEN_SOURCE_EXTENDED -# endif /* ! _XOPEN_SOURCE */ -#endif /* NCR_MP_RAS3 */ - - -/* -** Tandem NonStop-UX SVR4 -** -** From Rick McCarty <mccarty@mpd.tandem.com>. -*/ - -#ifdef NonStop_UX_BXX -# define __svr4__ -#endif /* NonStop_UX_BXX */ - - -/* -** Hitachi 3050R/3050RX and 3500 Workstations running HI-UX/WE2. -** -** Tested for 1.04, 1.03 -** From Akihiro Hashimoto ("Hash") <hash@dominic.ipc.chiba-u.ac.jp>. -** -** Tested for 4.02, 6.10 and 7.10 -** From Motonori NAKAMURA <motonori@media.kyoto-u.ac.jp>. -*/ - -#if !defined(__hpux) && (defined(_H3050R) || defined(_HIUX_SOURCE)) -# define SYSTEM5 1 /* include all the System V defines */ -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# define HASFCHMOD 1 /* has fchmod(2) syscall */ -# define setreuid(r, e) setresuid(r, e, -1) -# define LA_TYPE LA_FLOAT -# define SPT_TYPE SPT_PSTAT -# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */ -# ifndef HASSETVBUF -# define HASSETVBUF /* HI-UX has no setlinebuf */ -# endif /* ! HASSETVBUF */ -# ifndef GIDSET_T -# define GIDSET_T gid_t -# endif /* ! GIDSET_T */ -# ifndef _PATH_UNIX -# define _PATH_UNIX "/HI-UX" -# endif /* ! _PATH_UNIX */ -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef IDENTPROTO -# define IDENTPROTO 0 /* TCP/IP implementation is broken */ -# endif /* ! IDENTPROTO */ -# ifndef HASGETUSERSHELL -# define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps */ -# endif /* ! HASGETUSERSHELL */ -# define FDSET_CAST (int *) /* cast for fd_set parameters to select */ - -/* -** avoid m_flags conflict between Berkeley DB 1.85 db.h & sys/sysmacros.h -** on HIUX 3050 -*/ -# undef m_flags - -# ifdef __STDC__ -extern int syslog(int, char *, ...); -# else /* __STDC__ */ -extern int syslog(); -# endif /* __STDC__ */ - -#endif /* !defined(__hpux) && (defined(_H3050R) || defined(_HIUX_SOURCE)) */ - - -/* -** Amdahl UTS System V 2.1.5 (SVr3-based) -** -** From: Janet Jackson <janet@dialix.oz.au>. -*/ - -#ifdef _UTS -# include <sys/sysmacros.h> -# undef HASLSTAT /* has symlinks, but they cause problems */ -# define NEEDFSYNC 1 /* system fsync(2) fails on non-EFS filesys */ -# define SYS5SIGNALS 1 /* System V signal semantics */ -# define SYS5SETPGRP 1 /* use System V setpgrp(2) syscall */ -# define HASUNAME 1 /* use System V uname(2) system call */ -# define HASINITGROUPS 1 /* has initgroups(3) function */ -# define HASSETVBUF 1 /* has setvbuf(3) function */ -# ifndef HASGETUSERSHELL -# define HASGETUSERSHELL 0 /* does not have getusershell(3) function */ -# endif /* ! HASGETUSERSHELL */ -# define GIDSET_T gid_t /* type of 2nd arg to getgroups(2) isn't int */ -# define LA_TYPE LA_ZERO /* doesn't have load average */ -# define SFS_TYPE SFS_4ARGS /* use 4-arg statfs() */ -# define SFS_BAVAIL f_bfree /* alternate field name */ -# define _PATH_UNIX "/unix" -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -#endif /* _UTS */ - -/* -** Cray Computer Corporation's CSOS -** -** From Scott Bolte <scott@craycos.com>. -*/ - -#ifdef _CRAYCOM -# define SYSTEM5 1 /* include all the System V defines */ -# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ -# define NEEDFSYNC 1 /* no fsync in system library */ -# define MAXPATHLEN PATHSIZE -# define LA_TYPE LA_ZERO -# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ -# define SFS_BAVAIL f_bfree /* alternate field name */ -# define _POSIX_CHOWN_RESTRICTED -1 -extern struct group *getgrent(), *getgrnam(), *getgrgid(); -#endif /* _CRAYCOM */ - - -/* -** Sony NEWS-OS 4.2.1R and 6.0.3 -** -** From Motonori NAKAMURA <motonori@cs.ritsumei.ac.jp>. -*/ - -#ifdef sony_news -# ifndef __svr4 - /* NEWS-OS 4.2.1R */ -# ifndef BSD -# define BSD /* has BSD routines */ -# endif /* ! BSD */ -# define HASUNSETENV 1 /* has unsetenv(2) call */ -# undef HASSETVBUF /* don't actually have setvbuf(3) */ -# define WAITUNION 1 /* use "union wait" as wait argument type */ -# define LA_TYPE LA_INT -# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */ -# ifndef HASFLOCK -# define HASFLOCK 1 /* has flock(2) call */ -# endif /* ! HASFLOCK */ -# define setpgid setpgrp -# undef WIFEXITED -# undef WEXITSTATUS -# define MODE_T int /* system include files have no mode_t */ -typedef int pid_t; -typedef int (*sigfunc_t)(); -# define SIGFUNC_DEFINED -# define SIGFUNC_RETURN (0) -# define SIGFUNC_DECL int - -# else /* ! __svr4 */ - /* NEWS-OS 6.0.3 with /bin/cc */ -# ifndef __svr4__ -# define __svr4__ /* use all System V Release 4 defines below */ -# endif /* ! __svr4__ */ -# define HASSETSID 1 /* has Posix setsid(2) call */ -# define HASGETUSERSHELL 1 /* DOES have getusershell(3) call in libc */ -# define LA_TYPE LA_READKSYM /* use MIOC_READKSYM ioctl */ -# ifndef SPT_TYPE -# define SPT_TYPE SPT_SYSMIPS /* use sysmips() (OS 6.0.2 or later) */ -# endif /* ! SPT_TYPE */ -# define GIDSET_T gid_t -# undef WIFEXITED -# undef WEXITSTATUS -# ifndef SYSLOG_BUFSIZE -# define SYSLOG_BUFSIZE 256 -# endif /* ! SYSLOG_BUFSIZE */ -# define _PATH_UNIX "/stand/unix" -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ - -# endif /* ! __svr4 */ -#endif /* sony_news */ - - -/* -** Omron LUNA/UNIOS-B 3.0, LUNA2/Mach and LUNA88K Mach -** -** From Motonori NAKAMURA <motonori@cs.ritsumei.ac.jp>. -*/ - -#ifdef luna -# ifndef IDENTPROTO -# define IDENTPROTO 0 /* TCP/IP implementation is broken */ -# endif /* ! IDENTPROTO */ -# define HASUNSETENV 1 /* has unsetenv(2) call */ -# define NEEDPUTENV 1 /* need putenv(3) call */ -# define NEEDGETOPT 1 /* need a replacement for getopt(3) */ -# define NEEDSTRSTR 1 /* need emulation of the strstr(3) call */ -# define WAITUNION 1 /* use "union wait" as wait argument type */ -# ifdef uniosb -# include <sys/time.h> -# define NEEDVPRINTF 1 /* need a replacement for vprintf(3) */ -# define LA_TYPE LA_INT -# define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone */ -# endif /* uniosb */ -# ifdef luna2 -# define LA_TYPE LA_SUBR -# define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone */ -# endif /* luna2 */ -# ifdef luna88k -# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ -# define LA_TYPE LA_INT -# endif /* luna88k */ -# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */ -# define setpgid setpgrp -# undef WIFEXITED -# undef WEXITSTATUS -typedef int pid_t; -typedef int (*sigfunc_t)(); -# define SIGFUNC_DEFINED -# define SIGFUNC_RETURN (0) -# define SIGFUNC_DECL int -extern char *getenv(); -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -#endif /* luna */ - - -/* -** NEC EWS-UX/V 4.2 (with /usr/ucb/cc) -** -** From Motonori NAKAMURA <motonori@cs.ritsumei.ac.jp>. -*/ - -#if defined(nec_ews_svr4) || defined(_nec_ews_svr4) -# ifndef __svr4__ -# define __svr4__ /* use all System V Release 4 defines below */ -# endif /* ! __svr4__ */ -# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ -# define HASSETSID 1 /* has Posix setsid(2) call */ -# define LA_TYPE LA_READKSYM /* use MIOC_READSYM ioctl */ -# define SFS_TYPE SFS_USTAT /* use System V ustat(2) syscall */ -# define GIDSET_T gid_t -# undef WIFEXITED -# undef WEXITSTATUS -# define NAMELISTMASK 0x7fffffff /* mask for nlist() values */ -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ -# ifndef SYSLOG_BUFSIZE -# define SYSLOG_BUFSIZE 1024 /* allow full size syslog buffer */ -# endif /* ! SYSLOG_BUFSIZE */ -#endif /* defined(nec_ews_svr4) || defined(_nec_ews_svr4) */ - - -/* -** Fujitsu/ICL UXP/DS (For the DS/90 Series) -** -** From Diego R. Lopez <drlopez@cica.es>. -** Additional changes from Fumio Moriya and Toshiaki Nomura of the -** Fujitsu Fresoftware group <dsfrsoft@oai6.yk.fujitsu.co.jp>. -*/ - -#ifdef __uxp__ -# include <arpa/nameser.h> -# include <sys/sysmacros.h> -# include <sys/mkdev.h> -# define __svr4__ -# define HASGETUSERSHELL 0 -# define HASFLOCK 0 -# if UXPDS == 10 -# define HASSNPRINTF 0 /* no snprintf(3) or vsnprintf(3) */ -# else /* UXPDS == 10 */ -# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ -# endif /* UXPDS == 10 */ -# define _PATH_UNIX "/stand/unix" -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ -#endif /* __uxp__ */ - -/* -** Pyramid DC/OSx -** -** From Earle Ake <akee@wpdiss1.wpafb.af.mil>. -*/ - -#ifdef DCOSx -# define GIDSET_T gid_t -# ifndef IDENTPROTO -# define IDENTPROTO 0 /* TCP/IP implementation is broken */ -# endif /* ! IDENTPROTO */ -#endif /* DCOSx */ - -/* -** Concurrent Computer Corporation Maxion -** -** From Donald R. Laster Jr. <laster@access.digex.net>. -*/ - -#ifdef __MAXION__ - -# include <sys/stream.h> -# define __svr4__ 1 /* SVR4.2MP */ -# define HASSETREUID 1 /* have setreuid(2) */ -# define HASLSTAT 1 /* have lstat(2) */ -# define HASSETRLIMIT 1 /* have setrlimit(2) */ -# define HASGETDTABLESIZE 1 /* have getdtablesize(2) */ -# define HASSNPRINTF 1 /* have snprintf(3) */ -# define HASGETUSERSHELL 1 /* have getusershell(3) */ -# define NOFTRUNCATE 1 /* do not have ftruncate(2) */ -# define SLEEP_T unsigned -# define SFS_TYPE SFS_STATVFS -# define SFS_BAVAIL f_bavail -# ifndef SYSLOG_BUFSIZE -# define SYSLOG_BUFSIZE 256 /* Use 256 bytes */ -# endif /* ! SYSLOG_BUFSIZE */ - -# undef WUNTRACED -# undef WIFEXITED -# undef WIFSIGNALED -# undef WIFSTOPPED -# undef WEXITSTATUS -# undef WTERMSIG -# undef WSTOPSIG - -#endif /* __MAXION__ */ - -/* -** Harris Nighthawk PowerUX (nh6000 box) -** -** Contributed by Bob Miorelli, Pratt & Whitney <miorelli@pweh.com> -*/ - -#ifdef _PowerUX -# ifndef __svr4__ -# define __svr4__ -# endif /* ! __svr4__ */ -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ -# define SYSLOG_BUFSIZE 1024 -# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ -# define LA_TYPE LA_ZERO -typedef struct msgb mblk_t; -# undef offsetof /* avoid stddefs.h and sys/sysmacros.h conflict */ -#endif /* _PowerUX */ - -/* -** Siemens Nixdorf Informationssysteme AG SINIX -** -** Contributed by Gerald Rinske <Gerald.Rinske@mch.sni.de> -** of Siemens Business Services VAS. -*/ -#ifdef sinix -# define HASRANDOM 0 /* has random(3) */ -# define SYSLOG_BUFSIZE 1024 -#endif /* sinix */ - -/* -** CRAY T3E -** -** Contributed by Manu Mahonen <mailadm@csc.fi> -** of Center for Scientific Computing. -*/ -#ifdef _CRAY -# define GET_IPOPT_DST(dst) *(struct in_addr *)&(dst) -#endif /* _CRAY */ - -/* -** Motorola 922, MC88110, UNIX SYSTEM V/88 Release 4.0 Version 4.3 -** -** Contributed by Sergey Rusanov <rsm@utfoms.udmnet.ru> -*/ - -#ifdef MOTO -# define HASFCHMOD 1 -# define HASSETRLIMIT 0 -# define HASSETSID 1 -# define HASSETREUID 1 -# define HASULIMIT 1 -# define HASWAITPID 1 -# define HASGETDTABLESIZE 1 -# define HASGETUSERSHELL 1 -# define IP_SRCROUTE 0 -# define IDENTPROTO 0 -# define RES_DNSRCH_VARIABLE _res_dnsrch -# define _PATH_UNIX "/unix" -# define _PATH_VENDOR_CF "/etc/sendmail.cf" -# define _PATH_SENDMAILPID "/var/run/sendmail.pid" -#endif /* MOTO */ - - -/********************************************************************** -** End of Per-Operating System defines -**********************************************************************/ -/********************************************************************** -** More general defines -**********************************************************************/ - -/* general BSD defines */ -#ifdef BSD -# define HASGETDTABLESIZE 1 /* has getdtablesize(2) call */ -# ifndef HASSETREUID -# define HASSETREUID 1 /* has setreuid(2) call */ -# endif /* ! HASSETREUID */ -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# ifndef IP_SRCROUTE -# define IP_SRCROUTE 1 /* can check IP source routing */ -# endif /* ! IP_SRCROUTE */ -# ifndef HASSETRLIMIT -# define HASSETRLIMIT 1 /* has setrlimit(2) call */ -# endif /* ! HASSETRLIMIT */ -# ifndef HASFLOCK -# define HASFLOCK 1 /* has flock(2) call */ -# endif /* ! HASFLOCK */ -# ifndef TZ_TYPE -# define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone variable */ -# endif /* ! TZ_TYPE */ -#endif /* BSD */ - -/* general System V Release 4 defines */ -#ifdef __svr4__ -# define SYSTEM5 1 -# define USESETEUID 1 /* has usable seteuid(2) call */ -# define HASINITGROUPS 1 /* has initgroups(3) call */ -# define BSD_COMP 1 /* get BSD ioctl calls */ -# ifndef HASSETRLIMIT -# define HASSETRLIMIT 1 /* has setrlimit(2) call */ -# endif /* ! HASSETRLIMIT */ -# ifndef HASGETUSERSHELL -# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ -# endif /* ! HASGETUSERSHELL */ -# ifndef HASFCHMOD -# define HASFCHMOD 1 /* most (all?) SVr4s seem to have fchmod(2) */ -# endif /* ! HASFCHMOD */ - -# ifndef _PATH_UNIX -# define _PATH_UNIX "/unix" -# endif /* ! _PATH_UNIX */ -# ifndef _PATH_VENDOR_CF -# define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf" -# endif /* ! _PATH_VENDOR_CF */ -# ifndef _PATH_SENDMAILPID -# define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid" -# endif /* ! _PATH_SENDMAILPID */ -# ifndef SYSLOG_BUFSIZE -# define SYSLOG_BUFSIZE 128 -# endif /* ! SYSLOG_BUFSIZE */ -# ifndef SFS_TYPE -# define SFS_TYPE SFS_STATVFS -# endif /* ! SFS_TYPE */ - -# define USE_SIGLONGJMP 1 /* sigsetjmp needed for signal handling */ -#endif /* __svr4__ */ - -/* general System V defines */ -#ifdef SYSTEM5 -# include <sys/sysmacros.h> -# define HASUNAME 1 /* use System V uname(2) system call */ -# define SYS5SETPGRP 1 /* use System V setpgrp(2) syscall */ -# define HASSETVBUF 1 /* we have setvbuf(3) in libc */ -# ifndef HASULIMIT -# define HASULIMIT 1 /* has the ulimit(2) syscall */ -# endif /* ! HASULIMIT */ -# ifndef LA_TYPE -# ifdef MIOC_READKSYM -# define LA_TYPE LA_READKSYM /* use MIOC_READKSYM ioctl */ -# else /* MIOC_READKSYM */ -# define LA_TYPE LA_INT /* assume integer load average */ -# endif /* MIOC_READKSYM */ -# endif /* ! LA_TYPE */ -# ifndef SFS_TYPE -# define SFS_TYPE SFS_USTAT /* use System V ustat(2) syscall */ -# endif /* ! SFS_TYPE */ -# ifndef TZ_TYPE -# define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ -# endif /* ! TZ_TYPE */ -#endif /* SYSTEM5 */ - -/* general POSIX defines */ -#ifdef _POSIX_VERSION -# define HASSETSID 1 /* has Posix setsid(2) call */ -# define HASWAITPID 1 /* has Posix waitpid(2) call */ -# if _POSIX_VERSION >= 199500 && !defined(USESETEUID) -# define USESETEUID 1 /* has usable seteuid(2) call */ -# endif /* _POSIX_VERSION >= 199500 && !defined(USESETEUID) */ -#endif /* _POSIX_VERSION */ -/* -** Tweaking for systems that (for example) claim to be BSD or POSIX -** but don't have all the standard BSD or POSIX routines (boo hiss). -*/ - -#ifdef titan -# undef HASINITGROUPS /* doesn't have initgroups(3) call */ -#endif /* titan */ - -#ifdef _CRAYCOM -# undef HASSETSID /* despite POSIX claim, doesn't have setsid */ -#endif /* _CRAYCOM */ - -#ifdef MOTO -# undef USESETEUID -#endif /* MOTO */ - -/* -** Due to a "feature" in some operating systems such as Ultrix 4.3 and -** HPUX 8.0, if you receive a "No route to host" message (ICMP message -** ICMP_UNREACH_HOST) on _any_ connection, all connections to that host -** are closed. Some firewalls return this error if you try to connect -** to the IDENT port (113), so you can't receive email from these hosts -** on these systems. The firewall really should use a more specific -** message such as ICMP_UNREACH_PROTOCOL or _PORT or _FILTER_PROHIB. If -** not explicitly set to zero above, default it on. -*/ - -#ifndef IDENTPROTO -# define IDENTPROTO 1 /* use IDENT proto (RFC 1413) */ -#endif /* ! IDENTPROTO */ - -#ifndef IP_SRCROUTE -# define IP_SRCROUTE 1 /* Detect IP source routing */ -#endif /* ! IP_SRCROUTE */ - -#ifndef HASGETUSERSHELL -# define HASGETUSERSHELL 1 /* libc has getusershell(3) call */ -#endif /* ! HASGETUSERSHELL */ - -#ifndef NETUNIX -# define NETUNIX 1 /* include unix domain support */ -#endif /* ! NETUNIX */ - -#ifndef HASRANDOM -# define HASRANDOM 1 /* has random(3) support */ -#endif /* ! HASRANDOM */ - -#ifndef HASFLOCK -# define HASFLOCK 0 /* assume no flock(2) support */ -#endif /* ! HASFLOCK */ - -#ifndef HASSETREUID -# define HASSETREUID 0 /* assume no setreuid(2) call */ -#endif /* ! HASSETREUID */ - -#ifndef HASFCHMOD -# define HASFCHMOD 0 /* assume no fchmod(2) syscall */ -#endif /* ! HASFCHMOD */ - -#ifndef USESETEUID -# define USESETEUID 0 /* assume no seteuid(2) call or no saved ids */ -#endif /* ! USESETEUID */ - -#ifndef HASSETRLIMIT -# define HASSETRLIMIT 0 /* assume no setrlimit(2) support */ -#endif /* ! HASSETRLIMIT */ - -#ifndef HASULIMIT -# define HASULIMIT 0 /* assume no ulimit(2) support */ -#endif /* ! HASULIMIT */ - -#ifndef SECUREWARE -# define SECUREWARE 0 /* assume no SecureWare C2 auditing hooks */ -#endif /* ! SECUREWARE */ - -#ifndef USE_SIGLONGJMP -# define USE_SIGLONGJMP 0 /* assume setjmp handles signals properly */ -#endif /* ! USE_SIGLONGJMP */ - -#ifndef FDSET_CAST -# define FDSET_CAST /* (empty) cast for fd_set arg to select */ -#endif /* ! FDSET_CAST */ - -/* -** Pick a mailer setuid method for changing the current uid -*/ - -#define USE_SETEUID 0 -#define USE_SETREUID 1 -#define USE_SETUID 2 - -#if USESETEUID -# define MAILER_SETUID_METHOD USE_SETEUID -#else /* USESETEUID */ -# if HASSETREUID -# define MAILER_SETUID_METHOD USE_SETREUID -# else /* HASSETREUID */ -# define MAILER_SETUID_METHOD USE_SETUID -# endif /* HASSETREUID */ -#endif /* USESETEUID */ - -/* -** If no type for argument two of getgroups call is defined, assume -** it's an integer -- unfortunately, there seem to be several choices -** here. -*/ - -#ifndef GIDSET_T -# define GIDSET_T int -#endif /* ! GIDSET_T */ - -#ifndef UID_T -# define UID_T uid_t -#endif /* ! UID_T */ - -#ifndef GID_T -# define GID_T gid_t -#endif /* ! GID_T */ - -#ifndef SIZE_T -# define SIZE_T size_t -#endif /* ! SIZE_T */ - -#ifndef MODE_T -# define MODE_T mode_t -#endif /* ! MODE_T */ - -#ifndef ARGV_T -# define ARGV_T char ** -#endif /* ! ARGV_T */ - -#ifndef SOCKADDR_LEN_T -# define SOCKADDR_LEN_T int -#endif /* ! SOCKADDR_LEN_T */ - -#ifndef SOCKOPT_LEN_T -# define SOCKOPT_LEN_T int -#endif /* ! SOCKOPT_LEN_T */ - -#ifndef QUAD_T -# define QUAD_T unsigned long -#endif /* ! QUAD_T */ -/********************************************************************** -** Remaining definitions should never have to be changed. They are -** primarily to provide back compatibility for older systems -- for -** example, it includes some POSIX compatibility definitions -**********************************************************************/ - -/* System 5 compatibility */ -#ifndef S_ISREG -# define S_ISREG(foo) ((foo & S_IFMT) == S_IFREG) -#endif /* ! S_ISREG */ -#ifndef S_ISDIR -# define S_ISDIR(foo) ((foo & S_IFMT) == S_IFDIR) -#endif /* ! S_ISDIR */ -#if !defined(S_ISLNK) && defined(S_IFLNK) -# define S_ISLNK(foo) ((foo & S_IFMT) == S_IFLNK) -#endif /* !defined(S_ISLNK) && defined(S_IFLNK) */ -#if !defined(S_ISFIFO) -# if defined(S_IFIFO) -# define S_ISFIFO(foo) ((foo & S_IFMT) == S_IFIFO) -# else /* defined(S_IFIFO) */ -# define S_ISFIFO(foo) FALSE -# endif /* defined(S_IFIFO) */ -#endif /* !defined(S_ISFIFO) */ -#ifndef S_IRUSR -# define S_IRUSR 0400 -#endif /* ! S_IRUSR */ -#ifndef S_IWUSR -# define S_IWUSR 0200 -#endif /* ! S_IWUSR */ -#ifndef S_IRGRP -# define S_IRGRP 0040 -#endif /* ! S_IRGRP */ -#ifndef S_IWGRP -# define S_IWGRP 0020 -#endif /* ! S_IWGRP */ -#ifndef S_IROTH -# define S_IROTH 0004 -#endif /* ! S_IROTH */ -#ifndef S_IWOTH -# define S_IWOTH 0002 -#endif /* ! S_IWOTH */ - -#ifndef O_ACCMODE -# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) -#endif /* ! O_ACCMODE */ - -/* close-on-exec flag */ -#ifndef FD_CLOEXEC -# define FD_CLOEXEC 1 -#endif /* ! FD_CLOEXEC */ - -/* -** Older systems don't have this error code -- it should be in -** /usr/include/sysexits.h. -*/ - -#ifndef EX_CONFIG -# define EX_CONFIG 78 /* configuration error */ -#endif /* ! EX_CONFIG */ - -/* pseudo-codes */ -#define EX_QUIT 22 /* drop out of server immediately */ -#define EX_RESTART 23 /* restart sendmail daemon */ -#define EX_SHUTDOWN 24 /* shutdown sendmail daemon */ - -/* pseudo-code used for mci_setstat */ -#define EX_NOTSTICKY -5 /* don't save persistent status */ - - -/* -** An "impossible" file mode to indicate that the file does not exist. -*/ - -#define ST_MODE_NOFILE 0171147 /* unlikely to occur */ - - -/* type of arbitrary pointer */ -#ifndef ARBPTR_T -# define ARBPTR_T void * -#endif /* ! ARBPTR_T */ - -#ifndef __P -# include "sendmail/cdefs.h" -#endif /* ! __P */ - -#if HESIOD && !defined(NAMED_BIND) -# define NAMED_BIND 1 /* not one without the other */ -#endif /* HESIOD && !defined(NAMED_BIND) */ - -# if NAMED_BIND && !defined( __ksr__ ) && !defined( h_errno ) -extern int h_errno; -# endif /* NAMED_BIND && !defined( __ksr__ ) && !defined( h_errno ) */ - -#ifdef LDAPMAP -# include <sys/time.h> -# include <lber.h> -# include <ldap.h> - -/* Some LDAP constants */ -# define LDAPMAP_FALSE 0 -# define LDAPMAP_TRUE 1 - -/* -** ldap_init(3) is broken in Umich 3.x and OpenLDAP 1.0/1.1. -** Use the lack of LDAP_OPT_SIZELIMIT to detect old API implementations -** and assume (falsely) that all old API implementations are broken. -** (OpenLDAP 1.2 and later have a working ldap_init(), add -DUSE_LDAP_INIT) -*/ - -# if defined(LDAP_OPT_SIZELIMIT) && !defined(USE_LDAP_INIT) -# define USE_LDAP_INIT 1 -# endif /* defined(LDAP_OPT_SIZELIMIT) && !defined(USE_LDAP_INIT) */ - -/* -** LDAP_OPT_SIZELIMIT is not defined under Umich 3.x nor OpenLDAP 1.x, -** hence ldap_set_option() must not exist. -*/ - -# if defined(LDAP_OPT_SIZELIMIT) && !defined(USE_LDAP_SET_OPTION) -# define USE_LDAP_SET_OPTION 1 -# endif /* defined(LDAP_OPT_SIZELIMIT) && !defined(USE_LDAP_SET_OPTION) */ - -#endif /* LDAPMAP */ - -/* -** Do some required dependencies -*/ - -#if NETINET || NETINET6 || NETISO -# ifndef SMTP -# define SMTP 1 /* enable user and server SMTP */ -# endif /* ! SMTP */ -# ifndef QUEUE -# define QUEUE 1 /* enable queueing */ -# endif /* ! QUEUE */ -# ifndef DAEMON -# define DAEMON 1 /* include the daemon (requires IPC & SMTP) */ -# endif /* ! DAEMON */ -#endif /* NETINET || NETINET6 || NETISO */ - - -/* -** Arrange to use either varargs or stdargs -*/ - -#ifdef __STDC__ - -# include <stdarg.h> - -# define VA_LOCAL_DECL va_list ap; -# define VA_START(f) va_start(ap, f) -# define VA_END va_end(ap) - -#else /* __STDC__ */ - -# include <varargs.h> - -# define VA_LOCAL_DECL va_list ap; -# define VA_START(f) va_start(ap) -# define VA_END va_end(ap) - -#endif /* __STDC__ */ - -#if HASUNAME -# include <sys/utsname.h> -# ifdef newstr -# undef newstr -# endif /* newstr */ -#else /* HASUNAME */ -# define NODE_LENGTH 32 -struct utsname -{ - char nodename[NODE_LENGTH + 1]; -}; -#endif /* HASUNAME */ - -#if !defined(MAXHOSTNAMELEN) && !defined(_SCO_unix_) && !defined(NonStop_UX_BXX) && !defined(ALTOS_SYSTEM_V) -# define MAXHOSTNAMELEN 256 -#endif /* !defined(MAXHOSTNAMELEN) && !defined(_SCO_unix_) && !defined(NonStop_UX_BXX) && !defined(ALTOS_SYSTEM_V) */ - -#if !defined(SIGCHLD) && defined(SIGCLD) -# define SIGCHLD SIGCLD -#endif /* !defined(SIGCHLD) && defined(SIGCLD) */ - -#ifndef STDIN_FILENO -# define STDIN_FILENO 0 -#endif /* ! STDIN_FILENO */ - -#ifndef STDOUT_FILENO -# define STDOUT_FILENO 1 -#endif /* ! STDOUT_FILENO */ - -#ifndef STDERR_FILENO -# define STDERR_FILENO 2 -#endif /* ! STDERR_FILENO */ - -#ifndef LOCK_SH -# define LOCK_SH 0x01 /* shared lock */ -# define LOCK_EX 0x02 /* exclusive lock */ -# define LOCK_NB 0x04 /* non-blocking lock */ -# define LOCK_UN 0x08 /* unlock */ -#endif /* ! LOCK_SH */ - -#ifndef S_IXOTH -# define S_IXOTH (S_IEXEC >> 6) -#endif /* ! S_IXOTH */ - -#ifndef S_IXGRP -# define S_IXGRP (S_IEXEC >> 3) -#endif /* ! S_IXGRP */ - -#ifndef S_IXUSR -# define S_IXUSR (S_IEXEC) -#endif /* ! S_IXUSR */ - -#ifndef SEEK_SET -# define SEEK_SET 0 -# define SEEK_CUR 1 -# define SEEK_END 2 -#endif /* ! SEEK_SET */ - -#ifndef SIG_ERR -# define SIG_ERR ((void (*)()) -1) -#endif /* ! SIG_ERR */ - -#ifndef WEXITSTATUS -# define WEXITSTATUS(st) (((st) >> 8) & 0377) -#endif /* ! WEXITSTATUS */ -#ifndef WIFEXITED -# define WIFEXITED(st) (((st) & 0377) == 0) -#endif /* ! WIFEXITED */ -#ifndef WIFSTOPPED -# define WIFSTOPPED(st) (((st) & 0100) == 0) -#endif /* ! WIFSTOPPED */ -#ifndef WCOREDUMP -# define WCOREDUMP(st) (((st) & 0200) != 0) -#endif /* ! WCOREDUMP */ -#ifndef WTERMSIG -# define WTERMSIG(st) (((st) & 0177)) -#endif /* ! WTERMSIG */ - -#ifndef SIGFUNC_DEFINED -typedef void (*sigfunc_t) __P((int)); -#endif /* ! SIGFUNC_DEFINED */ -#ifndef SIGFUNC_RETURN -# define SIGFUNC_RETURN -#endif /* ! SIGFUNC_RETURN */ -#ifndef SIGFUNC_DECL -# define SIGFUNC_DECL void -#endif /* ! SIGFUNC_DECL */ - -/* size of syslog buffer */ -#ifndef SYSLOG_BUFSIZE -# define SYSLOG_BUFSIZE 1024 -#endif /* ! SYSLOG_BUFSIZE */ - -/* -** Size of prescan buffer. -** Despite comments in the _sendmail_ book, this probably should -** not be changed; there are some hard-to-define dependencies. -*/ - -#define PSBUFSIZE (MAXNAME + MAXATOM) /* size of prescan buffer */ - -/* fork routine -- set above using #ifdef _osname_ or in Makefile */ -#ifndef FORK -# define FORK fork /* function to call to fork mailer */ -#endif /* ! FORK */ - -/* setting h_errno */ -#ifndef SM_SET_H_ERRNO -# define SM_SET_H_ERRNO(err) h_errno = (err) -#endif /* SM_SET_H_ERRNO */ - -/* random routine -- set above using #ifdef _osname_ or in Makefile */ -#if HASRANDOM -# define get_random() random() -#else /* HASRANDOM */ -# define get_random() ((long) rand()) -# ifndef RANDOMSHIFT -# define RANDOMSHIFT 8 -# endif /* RANDOMSHIFT */ -#endif /* HASRANDOM */ - -/* -** Default to using scanf in readcf. -*/ - -#ifndef SCANF -# define SCANF 1 -#endif /* ! SCANF */ - -#if _FFR_MILTER -/* 32 bit type */ -# ifndef SM_INT32 -# define SM_INT32 int32_t -# endif /* SM_INT32 */ -#endif /* _FFR_MILTER */ - -/* -** SVr4 and similar systems use different routines for setjmp/longjmp -** with signal support -*/ - -#if USE_SIGLONGJMP -# ifdef jmp_buf -# undef jmp_buf -# endif /* jmp_buf */ -# define jmp_buf sigjmp_buf -# ifdef setjmp -# undef setjmp -# endif /* setjmp */ -# define setjmp(env) sigsetjmp(env, 1) -# ifdef longjmp -# undef longjmp -# endif /* longjmp */ -# define longjmp(env, val) siglongjmp(env, val) -#endif /* USE_SIGLONGJMP */ - -#if !defined(NGROUPS_MAX) && defined(NGROUPS) -# define NGROUPS_MAX NGROUPS /* POSIX naming convention */ -#endif /* !defined(NGROUPS_MAX) && defined(NGROUPS) */ - -/* -** Some snprintf() implementations are rumored not to NUL terminate. -*/ -#if SNPRINTF_IS_BROKEN -# ifdef snprintf -# undef snprintf -# endif /* snprintf */ -# define snprintf sm_snprintf -# ifdef vsnprintf -# undef vsnprintf -# endif /* vsnprintf */ -# define vsnprintf sm_vsnprintf -#endif /* SNPRINTF_IS_BROKEN */ - -/* -** If we don't have a system syslog, simulate it. -*/ - -#if !LOG -# define LOG_EMERG 0 /* system is unusable */ -# define LOG_ALERT 1 /* action must be taken immediately */ -# define LOG_CRIT 2 /* critical conditions */ -# define LOG_ERR 3 /* error conditions */ -# define LOG_WARNING 4 /* warning conditions */ -# define LOG_NOTICE 5 /* normal but significant condition */ -# define LOG_INFO 6 /* informational */ -# define LOG_DEBUG 7 /* debug-level messages */ -#endif /* !LOG */ - -#if SFIO -# ifdef ERRLIST_PREDEFINED -# undef ERRLIST_PREDEFINED -# endif /* ERRLIST_PREDEFINED */ -# if !HASSNPRINTF -# define HASSNPRINTF 1 /* sfio includes snprintf() */ -# endif /* !HASSNPRINTF */ -#endif /* SFIO */ -#ifndef SFIO_STDIO_COMPAT -# define SFIO_STDIO_COMPAT 0 -#endif /* ! SFIO_STDIO_COMPAT */ +#include <sm/conf.h> -#endif /* CONF_H */ +#endif /* ! CONF_H */ diff --git a/gnu/usr.sbin/sendmail/sendmail/control.c b/gnu/usr.sbin/sendmail/sendmail/control.c index c369832c551..31409f7f9c7 100644 --- a/gnu/usr.sbin/sendmail/sendmail/control.c +++ b/gnu/usr.sbin/sendmail/sendmail/control.c @@ -8,18 +8,20 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: control.c,v 8.44.14.20 2001/05/03 17:24:03 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +SM_RCSID("@(#)$Sendmail: control.c,v 8.110 2001/08/27 16:59:13 ca Exp $") + /* values for cmd_code */ -# define CMDERROR 0 /* bad command */ -# define CMDRESTART 1 /* restart daemon */ -# define CMDSHUTDOWN 2 /* end daemon */ -# define CMDHELP 3 /* help */ -# define CMDSTATUS 4 /* daemon status */ +#define CMDERROR 0 /* bad command */ +#define CMDRESTART 1 /* restart daemon */ +#define CMDSHUTDOWN 2 /* end daemon */ +#define CMDHELP 3 /* help */ +#define CMDSTATUS 4 /* daemon status */ +#define CMDMEMDUMP 5 /* dump memory, to find memory leaks */ +#if _FFR_CONTROL_MSTAT +# define CMDMSTAT 6 /* daemon status, more info, tagged data */ +#endif /* _FFR_CONTROL_MSTAT */ struct cmd { @@ -33,10 +35,15 @@ static struct cmd CmdTab[] = { "restart", CMDRESTART }, { "shutdown", CMDSHUTDOWN }, { "status", CMDSTATUS }, + { "memdump", CMDMEMDUMP }, +#if _FFR_CONTROL_MSTAT + { "mstat", CMDMSTAT }, +#endif /* _FFR_CONTROL_MSTAT */ { NULL, CMDERROR } }; + int ControlSocket = -1; /* @@ -55,13 +62,13 @@ int ControlSocket = -1; int opencontrolsocket() { -#if NETUNIX +# if NETUNIX int save_errno; int rval; long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN; struct sockaddr_un controladdr; - if (ControlSocketName == NULL) + if (ControlSocketName == NULL || *ControlSocketName == '\0') return 0; if (strlen(ControlSocketName) >= sizeof controladdr.sun_path) @@ -87,8 +94,8 @@ opencontrolsocket() (void) unlink(ControlSocketName); memset(&controladdr, '\0', sizeof controladdr); controladdr.sun_family = AF_UNIX; - (void) strlcpy(controladdr.sun_path, ControlSocketName, - sizeof controladdr.sun_path); + (void) sm_strlcpy(controladdr.sun_path, ControlSocketName, + sizeof controladdr.sun_path); if (bind(ControlSocket, (struct sockaddr *) &controladdr, sizeof controladdr) < 0) @@ -115,11 +122,11 @@ opencontrolsocket() sm_syslog(LOG_ALERT, NOQID, "ownership change on %s to uid %d failed: %s", ControlSocketName, (int) u, - errstring(save_errno)); + sm_errstring(save_errno)); message("050 ownership change on %s to uid %d failed: %s", ControlSocketName, (int) u, - errstring(save_errno)); - closecontrolsocket(TRUE); + sm_errstring(save_errno)); + closecontrolsocket(true); errno = save_errno; return -1; } @@ -128,7 +135,7 @@ opencontrolsocket() if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0) { save_errno = errno; - closecontrolsocket(TRUE); + closecontrolsocket(true); errno = save_errno; return -1; } @@ -136,11 +143,11 @@ opencontrolsocket() if (listen(ControlSocket, 8) < 0) { save_errno = errno; - closecontrolsocket(TRUE); + closecontrolsocket(true); errno = save_errno; return -1; } -#endif /* NETUNIX */ +# endif /* NETUNIX */ return 0; } /* @@ -160,7 +167,7 @@ void closecontrolsocket(fullclose) bool fullclose; { -#if NETUNIX +# if NETUNIX long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN; if (ControlSocket >= 0) @@ -184,11 +191,11 @@ closecontrolsocket(fullclose) { sm_syslog(LOG_WARNING, NOQID, "Could not remove control socket: %s", - errstring(errno)); + sm_errstring(errno)); return; } } -#endif /* NETUNIX */ +# endif /* NETUNIX */ return; } /* @@ -207,15 +214,12 @@ closecontrolsocket(fullclose) void clrcontrol() { -#if NETUNIX +# if NETUNIX if (ControlSocket >= 0) (void) close(ControlSocket); ControlSocket = -1; -#endif /* NETUNIX */ +# endif /* NETUNIX */ } - -#ifndef NOT_SENDMAIL - /* ** CONTROL_COMMAND -- read and process command from named socket ** @@ -232,6 +236,7 @@ clrcontrol() static jmp_buf CtxControlTimeout; +/* ARGSUSED0 */ static void controltimeout(timeout) time_t timeout; @@ -252,17 +257,17 @@ control_command(sock, e) ENVELOPE *e; { volatile int exitstat = EX_OK; - FILE *s = NULL; - EVENT *ev = NULL; - FILE *traffic; - FILE *oldout; + SM_FILE_T *s = NULL; + SM_EVENT *ev = NULL; + SM_FILE_T *traffic; + SM_FILE_T *oldout; char *cmd; char *p; struct cmd *c; char cmdbuf[MAXLINE]; char inp[MAXLINE]; - sm_setproctitle(FALSE, e, "control cmd read"); + sm_setproctitle(false, e, "control cmd read"); if (TimeOuts.to_control > 0) { @@ -274,11 +279,12 @@ control_command(sock, e) "timeout waiting for input during control command"); exit(EX_IOERR); } - ev = setevent(TimeOuts.to_control, controltimeout, - TimeOuts.to_control); + ev = sm_setevent(TimeOuts.to_control, controltimeout, + TimeOuts.to_control); } - s = fdopen(sock, "r+"); + s = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) sock, SM_IO_RDWR, + NULL); if (s == NULL) { int save_errno = errno; @@ -287,19 +293,20 @@ control_command(sock, e) errno = save_errno; exit(EX_IOERR); } - setbuf(s, NULL); + (void) sm_io_setvbuf(s, SM_TIME_DEFAULT, NULL, + SM_IO_NBF, SM_IO_BUFSIZ); - if (fgets(inp, sizeof inp, s) == NULL) + if (sm_io_fgets(s, SM_TIME_DEFAULT, inp, sizeof inp) == NULL) { - (void) fclose(s); + (void) sm_io_close(s, SM_TIME_DEFAULT); exit(EX_IOERR); } - (void) fflush(s); + (void) sm_io_flush(s, SM_TIME_DEFAULT); /* clean up end of line */ - fixcrlf(inp, TRUE); + fixcrlf(inp, true); - sm_setproctitle(FALSE, e, "control: %s", inp); + sm_setproctitle(false, e, "control: %s", inp); /* break off command */ for (p = inp; isascii(*p) && isspace(*p); p++) @@ -318,7 +325,7 @@ control_command(sock, e) /* decode command */ for (c = CmdTab; c->cmd_name != NULL; c++) { - if (strcasecmp(c->cmd_name, cmdbuf) == 0) + if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0) break; } @@ -335,45 +342,85 @@ control_command(sock, e) break; case CMDRESTART: /* restart the daemon */ - fprintf(s, "OK\r\n"); + (void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n"); exitstat = EX_RESTART; break; case CMDSHUTDOWN: /* kill the daemon */ - fprintf(s, "OK\r\n"); + (void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n"); exitstat = EX_SHUTDOWN; break; case CMDSTATUS: /* daemon status */ proc_list_probe(); { + int qgrp; long bsize; long free; - free = freediskspace(QueueDir, &bsize); + /* XXX need to deal with different partitions */ + qgrp = e->e_qgrp; + if (!ISVALIDQGRP(qgrp)) + qgrp = 0; + free = freediskspace(Queue[qgrp]->qg_qdir, &bsize); /* ** Prevent overflow and don't lose ** precision (if bsize == 512) */ - free = (long)((double)free * ((double)bsize / 1024)); + free = (long)((double) free * ((double) bsize / 1024)); - fprintf(s, "%d/%d/%ld/%d\r\n", - CurChildren, MaxChildren, - free, sm_getla(NULL)); + (void) sm_io_fprintf(s, SM_TIME_DEFAULT, + "%d/%d/%ld/%d\r\n", + CurChildren, MaxChildren, + free, getla()); } - proc_list_display(s); + proc_list_display(s, ""); + break; + +# if _FFR_CONTROL_MSTAT + case CMDMSTAT: /* daemon status, extended, tagged format */ + proc_list_probe(); + (void) sm_io_fprintf(s, SM_TIME_DEFAULT, + "C:%d\r\nM:%d\r\nL:%d\r\n", + CurChildren, MaxChildren, + getla()); + printnqe(s, "Q:"); + disk_status(s, "D:"); + proc_list_display(s, "P:"); + break; +# endif /* _FFR_CONTROL_MSTAT */ + + case CMDMEMDUMP: /* daemon memory dump, to find memory leaks */ +# if SM_HEAP_CHECK + /* dump the heap, if we are checking for memory leaks */ + if (sm_debug_active(&SmHeapCheck, 2)) + { + sm_heap_report(s, sm_debug_level(&SmHeapCheck) - 1); + } + else + { + (void) sm_io_fprintf(s, SM_TIME_DEFAULT, + "Memory dump unavailable.\r\n"); + (void) sm_io_fprintf(s, SM_TIME_DEFAULT, + "To fix, run sendmail with -dsm_check_heap.4\r\n"); + } +# else /* SM_HEAP_CHECK */ + (void) sm_io_fprintf(s, SM_TIME_DEFAULT, + "Memory dump unavailable.\r\n"); + (void) sm_io_fprintf(s, SM_TIME_DEFAULT, + "To fix, rebuild with -DSM_HEAP_CHECK\r\n"); +# endif /* SM_HEAP_CHECK */ break; case CMDERROR: /* unknown command */ - fprintf(s, "Bad command (%s)\r\n", cmdbuf); + (void) sm_io_fprintf(s, SM_TIME_DEFAULT, + "Bad command (%s)\r\n", cmdbuf); break; } - (void) fclose(s); + (void) sm_io_close(s, SM_TIME_DEFAULT); if (ev != NULL) - clrevent(ev); + sm_clrevent(ev); exit(exitstat); } -#endif /* ! NOT_SENDMAIL */ - diff --git a/gnu/usr.sbin/sendmail/sendmail/convtime.c b/gnu/usr.sbin/sendmail/sendmail/convtime.c index 5e2c885fa68..ae2064f243e 100644 --- a/gnu/usr.sbin/sendmail/sendmail/convtime.c +++ b/gnu/usr.sbin/sendmail/sendmail/convtime.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. + * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 @@ -11,12 +11,10 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: convtime.c,v 8.25 1999/06/16 21:11:26 ca Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +SM_RCSID("@(#)$Sendmail: convtime.c,v 8.36 2001/02/13 22:32:08 ca Exp $") + /* ** CONVTIME -- convert time ** @@ -47,10 +45,16 @@ convtime(p, units) { register time_t t, r; register char c; + bool pos = true; r = 0; - if (strcasecmp(p, "now") == 0) + if (sm_strcasecmp(p, "now") == 0) return NOW; + if (*p == '-') + { + pos = false; + ++p; + } while (*p != '\0') { t = 0; @@ -92,14 +96,14 @@ convtime(p, units) r += t; } - return r; + return pos ? r : -r; } /* ** PINTVL -- produce printable version of a time interval ** ** Parameters: ** intvl -- the interval to be converted -** brief -- if TRUE, print this in an extremely compact form +** brief -- if true, print this in an extremely compact form ** (basically used for logging). ** ** Returns: @@ -154,38 +158,43 @@ pintvl(intvl, brief) { if (dy > 0) { - (void) snprintf(p, SPACELEFT(buf, p), "%d+", dy); + (void) sm_snprintf(p, SPACELEFT(buf, p), "%d+", dy); p += strlen(p); } - (void) snprintf(p, SPACELEFT(buf, p), "%02d:%02d:%02d", - hr, mi, se); + (void) sm_snprintf(p, SPACELEFT(buf, p), "%02d:%02d:%02d", + hr, mi, se); return buf; } /* use the verbose form */ if (wk > 0) { - (void) snprintf(p, SPACELEFT(buf, p), ", %d week%s", wk, PLURAL(wk)); + (void) sm_snprintf(p, SPACELEFT(buf, p), ", %d week%s", wk, + PLURAL(wk)); p += strlen(p); } if (dy > 0) { - (void) snprintf(p, SPACELEFT(buf, p), ", %d day%s", dy, PLURAL(dy)); + (void) sm_snprintf(p, SPACELEFT(buf, p), ", %d day%s", dy, + PLURAL(dy)); p += strlen(p); } if (hr > 0) { - (void) snprintf(p, SPACELEFT(buf, p), ", %d hour%s", hr, PLURAL(hr)); + (void) sm_snprintf(p, SPACELEFT(buf, p), ", %d hour%s", hr, + PLURAL(hr)); p += strlen(p); } if (mi > 0) { - (void) snprintf(p, SPACELEFT(buf, p), ", %d minute%s", mi, PLURAL(mi)); + (void) sm_snprintf(p, SPACELEFT(buf, p), ", %d minute%s", mi, + PLURAL(mi)); p += strlen(p); } if (se > 0) { - (void) snprintf(p, SPACELEFT(buf, p), ", %d second%s", se, PLURAL(se)); + (void) sm_snprintf(p, SPACELEFT(buf, p), ", %d second%s", se, + PLURAL(se)); p += strlen(p); } diff --git a/gnu/usr.sbin/sendmail/sendmail/daemon.c b/gnu/usr.sbin/sendmail/sendmail/daemon.c index db27326e0c2..9697f7a1563 100644 --- a/gnu/usr.sbin/sendmail/sendmail/daemon.c +++ b/gnu/usr.sbin/sendmail/sendmail/daemon.c @@ -13,20 +13,13 @@ #include <sendmail.h> - -#ifndef lint -# ifdef DAEMON -static char id[] = "@(#)$Sendmail: daemon.c,v 8.401.4.68 2001/07/20 18:45:58 gshapiro Exp $ (with daemon mode)"; -# else /* DAEMON */ -static char id[] = "@(#)$Sendmail: daemon.c,v 8.401.4.68 2001/07/20 18:45:58 gshapiro Exp $ (without daemon mode)"; -# endif /* DAEMON */ -#endif /* ! lint */ +SM_RCSID("@(#)$Sendmail: daemon.c,v 8.588 2001/09/05 15:08:00 ca Exp $") #if defined(SOCK_STREAM) || defined(__GNU_LIBRARY__) # define USE_SOCK_STREAM 1 #endif /* defined(SOCK_STREAM) || defined(__GNU_LIBRARY__) */ -#if DAEMON || defined(USE_SOCK_STREAM) +#if defined(USE_SOCK_STREAM) # if NETINET || NETINET6 # include <arpa/inet.h> # endif /* NETINET || NETINET6 */ @@ -35,42 +28,42 @@ static char id[] = "@(#)$Sendmail: daemon.c,v 8.401.4.68 2001/07/20 18:45:58 gsh # define NO_DATA NO_ADDRESS # endif /* ! NO_DATA */ # endif /* NAMED_BIND */ -#endif /* DAEMON || defined(USE_SOCK_STREAM) */ - -#if DAEMON - -# if STARTTLS -# include <openssl/rand.h> -# endif /* STARTTLS */ - -# include <sys/time.h> - -# if IP_SRCROUTE && NETINET -# include <netinet/in_systm.h> -# include <netinet/ip.h> -# if HAS_IN_H -# include <netinet/in.h> -# ifndef IPOPTION -# define IPOPTION ip_opts -# define IP_LIST ip_opts -# define IP_DST ip_dst -# endif /* ! IPOPTION */ -# else /* HAS_IN_H */ -# include <netinet/ip_var.h> -# ifndef IPOPTION -# define IPOPTION ipoption -# define IP_LIST ipopt_list -# define IP_DST ipopt_dst -# endif /* ! IPOPTION */ -# endif /* HAS_IN_H */ -# endif /* IP_SRCROUTE && NETINET */ - -/* structure to describe a daemon */ +#endif /* defined(USE_SOCK_STREAM) */ + +#if STARTTLS +# include <openssl/rand.h> +#endif /* STARTTLS */ + +#include <sys/time.h> + +#if IP_SRCROUTE && NETINET +# include <netinet/in_systm.h> +# include <netinet/ip.h> +# if HAS_IN_H +# include <netinet/in.h> +# ifndef IPOPTION +# define IPOPTION ip_opts +# define IP_LIST ip_opts +# define IP_DST ip_dst +# endif /* ! IPOPTION */ +# else /* HAS_IN_H */ +# include <netinet/ip_var.h> +# ifndef IPOPTION +# define IPOPTION ipoption +# define IP_LIST ipopt_list +# define IP_DST ipopt_dst +# endif /* ! IPOPTION */ +# endif /* HAS_IN_H */ +#endif /* IP_SRCROUTE && NETINET */ + +#include <sm/fdset.h> + +/* structure to describe a daemon or a client */ struct daemon { int d_socket; /* fd for socket */ SOCKADDR d_addr; /* socket for incoming */ - u_short d_port; /* port number */ + unsigned short d_port; /* port number */ int d_listenqueue; /* size of listen queue */ int d_tcprcvbufsize; /* size of TCP receive buffer */ int d_tcpsndbufsize; /* size of TCP send buffer */ @@ -80,15 +73,20 @@ struct daemon BITMAP256 d_flags; /* flags; see sendmail.h */ char *d_mflags; /* flags for use in macro */ char *d_name; /* user-supplied name */ +#if MILTER +# if _FFR_MILTER_PERDAEMON + char *d_inputfilterlist; + struct milter *d_inputfilters[MAXFILTERS]; +# endif /* _FFR_MILTER_PERDAEMON */ +#endif /* MILTER */ }; typedef struct daemon DAEMON_T; -static void connecttimeout __P((void)); -static int opendaemonsocket __P((struct daemon *, bool)); -static u_short setupdaemon __P((SOCKADDR *)); -static SIGFUNC_DECL sighup __P((int)); -static void restart_daemon __P((void)); +static void connecttimeout __P((void)); +static int opendaemonsocket __P((DAEMON_T *, bool)); +static unsigned short setupdaemon __P((SOCKADDR *)); +static void getrequests_checkdiskspace __P((ENVELOPE *e)); /* ** DAEMON.C -- routines to use when running as a daemon. @@ -110,22 +108,18 @@ static void restart_daemon __P((void)); ** etc., to avoid having extra file descriptors during ** the queue run and to avoid confusing the network ** code (if it cares). -** makeconnection(host, port, outfile, infile, e) +** makeconnection(host, port, mci, e, enough) ** Make a connection to the named host on the given -** port. Set *outfile and *infile to the files -** appropriate for communication. Returns zero on -** success, else an exit status describing the -** error. +** port. Returns zero on success, else an exit status +** describing the error. ** host_map_lookup(map, hbuf, avp, pstat) ** Convert the entry in hbuf into a canonical form. */ static DAEMON_T Daemons[MAXDAEMONS]; -static int ndaemons = 0; /* actual number of daemons */ +static int NDaemons = 0; /* actual number of daemons */ -/* options for client */ -static int TcpRcvBufferSize = 0; /* size of TCP receive buffer */ -static int TcpSndBufferSize = 0; /* size of TCP send buffer */ +static time_t NextDiskSpaceCheck = 0; /* ** GETREQUESTS -- open mail IPC port and get requests. @@ -143,6 +137,8 @@ static int TcpSndBufferSize = 0; /* size of TCP send buffer */ ** routine is always in the child. The file pointers ** "InChannel" and "OutChannel" should be set to point ** to the communication channel. +** May restart persistent queue runners if they have ended +** for some reason. */ BITMAP256 * @@ -150,12 +146,11 @@ getrequests(e) ENVELOPE *e; { int t; - time_t last_disk_space_check = 0; int idx, curdaemon = -1; int i, olddaemon = 0; -# if XDEBUG +#if XDEBUG bool j_has_dot; -# endif /* XDEBUG */ +#endif /* XDEBUG */ char status[MAXLINE]; SOCKADDR sa; SOCKADDR_LEN_T len = sizeof sa; @@ -163,12 +158,13 @@ getrequests(e) extern int ControlSocket; # endif /* NETUNIX */ extern ENVELOPE BlankEnvelope; + extern bool refuseconnections __P((char *, ENVELOPE *, int, bool)); - for (idx = 0; idx < ndaemons; idx++) + for (idx = 0; idx < NDaemons; idx++) { Daemons[idx].d_port = setupdaemon(&(Daemons[idx].d_addr)); - Daemons[idx].d_firsttime = TRUE; + Daemons[idx].d_firsttime = true; Daemons[idx].d_refuse_connections_until = (time_t) 0; } @@ -178,48 +174,45 @@ getrequests(e) if (tTd(15, 1)) { - for (idx = 0; idx < ndaemons; idx++) + for (idx = 0; idx < NDaemons; idx++) { - dprintf("getrequests: daemon %s: port %d\n", - Daemons[idx].d_name, - ntohs(Daemons[idx].d_port)); + sm_dprintf("getrequests: daemon %s: port %d\n", + Daemons[idx].d_name, + ntohs(Daemons[idx].d_port)); } } /* get a socket for the SMTP connection */ - for (idx = 0; idx < ndaemons; idx++) - Daemons[idx].d_socksize = opendaemonsocket(&Daemons[idx], TRUE); + for (idx = 0; idx < NDaemons; idx++) + Daemons[idx].d_socksize = opendaemonsocket(&Daemons[idx], true); if (opencontrolsocket() < 0) sm_syslog(LOG_WARNING, NOQID, "daemon could not open control socket %s: %s", - ControlSocketName, errstring(errno)); - - (void) setsignal(SIGCHLD, reapchild); - (void) setsignal(SIGHUP, sighup); + ControlSocketName, sm_errstring(errno)); - /* workaround: can't seem to release the signal in the parent */ - (void) releasesignal(SIGHUP); + /* If there are any queue runners releated reapchild() co-ord's */ + (void) sm_signal(SIGCHLD, reapchild); - /* write the pid to file */ + /* write the pid to file, command line args to syslog */ log_sendmail_pid(e); -# if XDEBUG +#if XDEBUG { char jbuf[MAXHOSTNAMELEN]; expand("\201j", jbuf, sizeof jbuf, e); j_has_dot = strchr(jbuf, '.') != NULL; } -# endif /* XDEBUG */ +#endif /* XDEBUG */ /* Add parent process as first item */ - proc_list_add(getpid(), "Sendmail daemon", PROC_DAEMON); + proc_list_add(CurrentPid, "Sendmail daemon", PROC_DAEMON, 0, -1); if (tTd(15, 1)) { - for (idx = 0; idx < ndaemons; idx++) - dprintf("getrequests: daemon %s: %d\n", + for (idx = 0; idx < NDaemons; idx++) + sm_dprintf("getrequests: daemon %s: %d\n", Daemons[idx].d_name, Daemons[idx].d_socket); } @@ -228,62 +221,39 @@ getrequests(e) { register pid_t pid; auto SOCKADDR_LEN_T lotherend; - bool timedout = FALSE; - bool control = FALSE; + bool timedout = false; + bool control = false; int save_errno; int pipefd[2]; - time_t timenow; -# if STARTTLS + time_t now; +#if STARTTLS long seed; -# endif /* STARTTLS */ - extern bool refuseconnections __P((char *, ENVELOPE *, int)); +#endif /* STARTTLS */ /* see if we are rejecting connections */ - (void) blocksignal(SIGALRM); + (void) sm_blocksignal(SIGALRM); if (ShutdownRequest != NULL) shutdown_daemon(); else if (RestartRequest != NULL) restart_daemon(); + else if (RestartWorkGroup) + restart_marked_work_groups(); - timenow = curtime(); - - /* - ** Use ConnRateThrottle only if the - ** last pass was for a connection - */ - - if (ConnRateThrottle > 0 && curdaemon >= 0) + for (idx = 0; idx < NDaemons; idx++) { - static int conncnt = 0; - static time_t lastconn = 0; - - if (timenow != lastconn) - { - lastconn = timenow; - conncnt = 1; - } - else if (++conncnt > ConnRateThrottle) - { - /* sleep to flatten out connection load */ - sm_setproctitle(TRUE, e, - "deferring connections: %d per second", - ConnRateThrottle); - if (LogLevel >= 9) - sm_syslog(LOG_INFO, NOQID, - "deferring connections: %d per second", - ConnRateThrottle); - (void) sleep(1); - } - } + /* + ** XXX do this call outside the loop? + ** no: refuse_connections may sleep(). + */ - for (idx = 0; idx < ndaemons; idx++) - { - if (timenow < Daemons[idx].d_refuse_connections_until) + now = curtime(); + if (now < Daemons[idx].d_refuse_connections_until) continue; if (bitnset(D_DISABLE, Daemons[idx].d_flags)) continue; - if (refuseconnections(Daemons[idx].d_name, e, idx)) + if (refuseconnections(Daemons[idx].d_name, e, idx, + curdaemon == idx)) { if (Daemons[idx].d_socket >= 0) { @@ -293,19 +263,19 @@ getrequests(e) } /* refuse connections for next 15 seconds */ - Daemons[idx].d_refuse_connections_until = timenow + 15; + Daemons[idx].d_refuse_connections_until = now + 15; } else if (Daemons[idx].d_socket < 0 || Daemons[idx].d_firsttime) { - if (!Daemons[idx].d_firsttime && LogLevel >= 9) + if (!Daemons[idx].d_firsttime && LogLevel > 8) sm_syslog(LOG_INFO, NOQID, "accepting connections again for daemon %s", Daemons[idx].d_name); /* arrange to (re)open the socket if needed */ - (void) opendaemonsocket(&Daemons[idx], FALSE); - Daemons[idx].d_firsttime = FALSE; + (void) opendaemonsocket(&Daemons[idx], false); + Daemons[idx].d_firsttime = false; } } @@ -314,58 +284,12 @@ getrequests(e) shutdown_daemon(); else if (RestartRequest != NULL) restart_daemon(); + else if (RestartWorkGroup) + restart_marked_work_groups(); - if (timenow >= last_disk_space_check) - { - bool logged = FALSE; + getrequests_checkdiskspace(e); - if (!enoughdiskspace(MinBlocksFree + 1, FALSE)) - { - for (idx = 0; idx < ndaemons; idx++) - { - if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags)) - { - /* log only if not logged before */ - if (!logged) - { - if (LogLevel >= 9) - sm_syslog(LOG_INFO, NOQID, - "rejecting new messages: min free: %ld", - MinBlocksFree); - logged = TRUE; - sm_setproctitle(TRUE, e, - "rejecting new messages: min free: %ld", - MinBlocksFree); - } - setbitn(D_ETRNONLY, Daemons[idx].d_flags); - } - } - } - else - { - for (idx = 0; idx < ndaemons; idx++) - { - if (bitnset(D_ETRNONLY, Daemons[idx].d_flags)) - { - /* log only if not logged before */ - if (!logged) - { - if (LogLevel >= 9) - sm_syslog(LOG_INFO, NOQID, - "accepting new messages (again)"); - logged = TRUE; - } - - /* title will be set below */ - clrbitn(D_ETRNONLY, Daemons[idx].d_flags); - } - } - } - /* only check disk space once a minute */ - last_disk_space_check = timenow + 60; - } - -# if XDEBUG +#if XDEBUG /* check for disaster */ { char jbuf[MAXHOSTNAMELEN]; @@ -386,9 +310,9 @@ getrequests(e) abort(); } } -# endif /* XDEBUG */ +#endif /* XDEBUG */ -# if 0 +#if 0 /* ** Andrew Sun <asun@ieps-sun.ml.com> claims that this will ** fix the SVr4 problem. But it seems to have gone away, @@ -396,14 +320,14 @@ getrequests(e) */ if (DaemonSocket >= 0 && - SetNonBlocking(DaemonSocket, FALSE) < 0) + SetNonBlocking(DaemonSocket, false) < 0) log an error here; -# endif /* 0 */ - (void) releasesignal(SIGALRM); +#endif /* 0 */ + (void) sm_releasesignal(SIGALRM); for (;;) { - bool setproc = FALSE; + bool setproc = false; int highest = -1; fd_set readfds; struct timeval timeout; @@ -412,10 +336,12 @@ getrequests(e) shutdown_daemon(); else if (RestartRequest != NULL) restart_daemon(); + else if (RestartWorkGroup) + restart_marked_work_groups(); FD_ZERO(&readfds); - for (idx = 0; idx < ndaemons; idx++) + for (idx = 0; idx < NDaemons; idx++) { /* wait for a connection */ if (Daemons[idx].d_socket >= 0) @@ -424,24 +350,25 @@ getrequests(e) !bitnset(D_ETRNONLY, Daemons[idx].d_flags)) { - sm_setproctitle(TRUE, e, + sm_setproctitle(true, e, "accepting connections"); - setproc = TRUE; + setproc = true; } if (Daemons[idx].d_socket > highest) highest = Daemons[idx].d_socket; - FD_SET((u_int)Daemons[idx].d_socket, &readfds); + SM_FD_SET(Daemons[idx].d_socket, + &readfds); } } -# if NETUNIX +#if NETUNIX if (ControlSocket >= 0) { if (ControlSocket > highest) highest = ControlSocket; - FD_SET(ControlSocket, &readfds); + SM_FD_SET(ControlSocket, &readfds); } -# endif /* NETUNIX */ +#endif /* NETUNIX */ timeout.tv_sec = 5; timeout.tv_usec = 0; @@ -454,29 +381,32 @@ getrequests(e) shutdown_daemon(); else if (RestartRequest != NULL) restart_daemon(); + else if (RestartWorkGroup) + restart_marked_work_groups(); - if (DoQueueRun) - (void) runqueue(TRUE, FALSE); - curdaemon = -1; + if (doqueuerun()) + (void) runqueue(true, false, false, false); + if (t <= 0) { - timedout = TRUE; + timedout = true; break; } - control = FALSE; + control = false; errno = 0; /* look "round-robin" for an active socket */ - if ((idx = olddaemon + 1) >= ndaemons) + if ((idx = olddaemon + 1) >= NDaemons) idx = 0; - for (i = 0; i < ndaemons; i++) + for (i = 0; i < NDaemons; i++) { if (Daemons[idx].d_socket >= 0 && - FD_ISSET(Daemons[idx].d_socket, &readfds)) + SM_FD_ISSET(Daemons[idx].d_socket, + &readfds)) { lotherend = Daemons[idx].d_socksize; memset(&RealHostAddr, '\0', @@ -505,12 +435,12 @@ getrequests(e) olddaemon = curdaemon = idx; break; } - if (++idx >= ndaemons) + if (++idx >= NDaemons) idx = 0; } -# if NETUNIX +#if NETUNIX if (curdaemon == -1 && ControlSocket >= 0 && - FD_ISSET(ControlSocket, &readfds)) + SM_FD_ISSET(ControlSocket, &readfds)) { struct sockaddr_un sa_un; @@ -538,26 +468,25 @@ getrequests(e) errno = EINVAL; } if (t >= 0) - control = TRUE; + control = true; } -# else /* NETUNIX */ +#else /* NETUNIX */ if (curdaemon == -1) { /* No daemon to service */ continue; } -# endif /* NETUNIX */ +#endif /* NETUNIX */ if (t >= 0 || errno != EINTR) break; } if (timedout) { - timedout = FALSE; + timedout = false; continue; } save_errno = errno; - timenow = curtime(); - (void) blocksignal(SIGALRM); + (void) sm_blocksignal(SIGALRM); if (t < 0) { errno = save_errno; @@ -566,15 +495,15 @@ getrequests(e) /* arrange to re-open the socket next time around */ (void) close(Daemons[curdaemon].d_socket); Daemons[curdaemon].d_socket = -1; -# if SO_REUSEADDR_IS_BROKEN +#if SO_REUSEADDR_IS_BROKEN /* ** Give time for bound socket to be released. ** This creates a denial-of-service if you can ** force accept() to fail on affected systems. */ - Daemons[curdaemon].d_refuse_connections_until = timenow + 15; -# endif /* SO_REUSEADDR_IS_BROKEN */ + Daemons[curdaemon].d_refuse_connections_until = curtime() + 15; +#endif /* SO_REUSEADDR_IS_BROKEN */ continue; } @@ -584,49 +513,58 @@ getrequests(e) switch (Daemons[curdaemon].d_addr.sa.sa_family) { case AF_UNSPEC: - define(macid("{daemon_family}", NULL), - "unspec", &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{daemon_family}"), "unspec"); break; -# if NETINET +#if _FFR_DAEMON_NETUNIX +# if NETUNIX + case AF_UNIX: + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{daemon_family}"), "local"); + break; +# endif /* NETUNIX */ +#endif /* _FFR_DAEMON_NETUNIX */ +#if NETINET case AF_INET: - define(macid("{daemon_family}", NULL), - "inet", &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{daemon_family}"), "inet"); break; -# endif /* NETINET */ -# if NETINET6 +#endif /* NETINET */ +#if NETINET6 case AF_INET6: - define(macid("{daemon_family}", NULL), - "inet6", &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{daemon_family}"), "inet6"); break; -# endif /* NETINET6 */ -# if NETISO +#endif /* NETINET6 */ +#if NETISO case AF_ISO: - define(macid("{daemon_family}", NULL), - "iso", &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{daemon_family}"), "iso"); break; -# endif /* NETISO */ -# if NETNS +#endif /* NETISO */ +#if NETNS case AF_NS: - define(macid("{daemon_family}", NULL), - "ns", &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{daemon_family}"), "ns"); break; -# endif /* NETNS */ -# if NETX25 +#endif /* NETNS */ +#if NETX25 case AF_CCITT: - define(macid("{daemon_family}", NULL), - "x.25", &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{daemon_family}"), "x.25"); break; -# endif /* NETX25 */ +#endif /* NETX25 */ } - define(macid("{daemon_name}", NULL), - Daemons[curdaemon].d_name, &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{daemon_name}"), + Daemons[curdaemon].d_name); if (Daemons[curdaemon].d_mflags != NULL) - define(macid("{daemon_flags}", NULL), - Daemons[curdaemon].d_mflags, - &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{daemon_flags}"), + Daemons[curdaemon].d_mflags); else - define(macid("{daemon_flags}", NULL), - "", &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{daemon_flags}"), ""); } /* @@ -634,26 +572,38 @@ getrequests(e) */ if (tTd(15, 2)) - dprintf("getrequests: forking (fd = %d)\n", t); + sm_dprintf("getrequests: forking (fd = %d)\n", t); /* - ** advance state of PRNG - ** this is necessary because otherwise all child processes + ** Advance state of PRNG. + ** This is necessary because otherwise all child processes ** will produce the same PRN sequence and hence the selection ** of a queue directory (and other things, e.g., MX selection) ** are not "really" random. */ -# if STARTTLS +#if STARTTLS + /* XXX get some better "random" data? */ seed = get_random(); - RAND_seed((void *) &last_disk_space_check, - sizeof last_disk_space_check); - RAND_seed((void *) &timenow, sizeof timenow); + RAND_seed((void *) &NextDiskSpaceCheck, + sizeof NextDiskSpaceCheck); + RAND_seed((void *) &now, sizeof now); RAND_seed((void *) &seed, sizeof seed); -# else /* STARTTLS */ +#else /* STARTTLS */ (void) get_random(); -# endif /* STARTTLS */ +#endif /* STARTTLS */ -#ifndef DEBUG_NO_FORK +#if NAMED_BIND + /* + ** Update MX records for FallBackMX. + ** Let's hope this is fast otherwise we screw up the + ** response time. + */ + + if (FallBackMX != NULL) + (void) getfallbackmxrr(FallBackMX); +#endif /* NAMED_BIND */ + +#if !PROFILING /* ** Create a pipe to keep the child from writing to the ** socket until after the parent has closed it. Otherwise @@ -663,7 +613,7 @@ getrequests(e) if (pipe(pipefd) < 0) pipefd[0] = pipefd[1] = -1; - (void) blocksignal(SIGCHLD); + (void) sm_blocksignal(SIGCHLD); pid = fork(); if (pid < 0) { @@ -673,19 +623,20 @@ getrequests(e) (void) close(pipefd[0]); (void) close(pipefd[1]); } - (void) releasesignal(SIGCHLD); + (void) sm_releasesignal(SIGCHLD); (void) sleep(10); (void) close(t); continue; } -#else /* ! DEBUG_NO_FORK */ + +#else /* !PROFILING */ pid = 0; -#endif /* ! DEBUG_NO_FORK */ +#endif /* !PROFILING */ if (pid == 0) { char *p; - FILE *inchannel, *outchannel = NULL; + SM_FILE_T *inchannel, *outchannel = NULL; /* ** CHILD -- return to caller. @@ -695,27 +646,39 @@ getrequests(e) /* Reset global flags */ RestartRequest = NULL; + RestartWorkGroup = false; ShutdownRequest = NULL; PendingSignal = 0; + CurrentPid = getpid(); + + (void) sm_releasesignal(SIGALRM); + (void) sm_releasesignal(SIGCHLD); + (void) sm_signal(SIGCHLD, SIG_DFL); + (void) sm_signal(SIGHUP, SIG_DFL); + (void) sm_signal(SIGTERM, intsig); + + /* turn on profiling */ + /* SM_PROF(0); */ + + /* + ** Initialize exception stack and default exception + ** handler for child process. + */ - (void) releasesignal(SIGALRM); - (void) releasesignal(SIGCHLD); - (void) setsignal(SIGCHLD, SIG_DFL); - (void) setsignal(SIGHUP, SIG_DFL); - (void) setsignal(SIGTERM, intsig); + sm_exc_newthread(fatal_error); if (!control) { - define(macid("{daemon_addr}", NULL), - newstr(anynet_ntoa(&Daemons[curdaemon].d_addr)), - &BlankEnvelope); - (void) snprintf(status, sizeof status, "%d", + macdefine(&BlankEnvelope.e_macro, A_TEMP, + macid("{daemon_addr}"), + anynet_ntoa(&Daemons[curdaemon].d_addr)); + (void) sm_snprintf(status, sizeof status, "%d", ntohs(Daemons[curdaemon].d_port)); - define(macid("{daemon_port}", NULL), - newstr(status), &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_TEMP, + macid("{daemon_port}"), status); } - for (idx = 0; idx < ndaemons; idx++) + for (idx = 0; idx < NDaemons; idx++) { if (Daemons[idx].d_socket >= 0) (void) close(Daemons[idx].d_socket); @@ -727,25 +690,26 @@ getrequests(e) if (control) { /* Add control socket process */ - proc_list_add(getpid(), "console socket child", - PROC_CONTROL_CHILD); + proc_list_add(CurrentPid, + "console socket child", + PROC_CONTROL_CHILD, 0, -1); } else { proc_list_clear(); /* Add parent process as first child item */ - proc_list_add(getpid(), "daemon child", - PROC_DAEMON_CHILD); + proc_list_add(CurrentPid, "daemon child", + PROC_DAEMON_CHILD, 0, -1); /* don't schedule queue runs if ETRN */ QueueIntvl = 0; - sm_setproctitle(TRUE, e, "startup with %s", + sm_setproctitle(true, e, "startup with %s", anynet_ntoa(&RealHostAddr)); } -#ifndef DEBUG_NO_FORK +#if !PROFILING if (pipefd[0] != -1) { auto char c; @@ -767,99 +731,114 @@ getrequests(e) continue; (void) close(pipefd[0]); } -#endif /* ! DEBUG_NO_FORK */ +#endif /* !PROFILING */ /* control socket processing */ if (control) { control_command(t, e); - /* NOTREACHED */ exit(EX_SOFTWARE); } /* determine host name */ p = hostnamebyanyaddr(&RealHostAddr); - if (strlen(p) > (SIZE_T) MAXNAME) + if (strlen(p) > MAXNAME) /* XXX - 1 ? */ p[MAXNAME] = '\0'; RealHostName = newstr(p); if (RealHostName[0] == '[') { - /* TEMP, FAIL: which one? */ - define(macid("{client_resolve}", NULL), - (h_errno == TRY_AGAIN) ? "TEMP" : "FAIL", - &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{client_resolve}"), + h_errno == TRY_AGAIN ? "TEMP" : "FAIL"); } else - define(macid("{client_resolve}", NULL), "OK", - &BlankEnvelope); - sm_setproctitle(TRUE, e, "startup with %s", p); - - if ((inchannel = fdopen(t, "r")) == NULL || - (t = dup(t)) < 0 || - (outchannel = fdopen(t, "w")) == NULL) + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{client_resolve}"), "OK"); + sm_setproctitle(true, e, "startup with %s", p); + + if ((inchannel = sm_io_open(SmFtStdiofd, + SM_TIME_DEFAULT, + (void *) t, + SM_IO_RDONLY, NULL)) == NULL + || (t = dup(t)) < 0 || + (outchannel = sm_io_open(SmFtStdiofd, + SM_TIME_DEFAULT, + (void *) t, + SM_IO_WRONLY, NULL)) + == NULL) { - syserr("cannot open SMTP server channel, fd=%d", t); - finis(FALSE, EX_OK); + syserr("cannot open SMTP server channel, fd=%d", + t); + finis(false, EX_OK); } + sm_io_automode(inchannel, outchannel); InChannel = inchannel; OutChannel = outchannel; - DisConnected = FALSE; + DisConnected = false; -# ifdef XLA +#if XLA if (!xla_host_ok(RealHostName)) { message("421 4.4.5 Too many SMTP sessions for this host"); - finis(FALSE, EX_OK); + finis(false, EX_OK); } -# endif /* XLA */ +#endif /* XLA */ /* find out name for interface of connection */ - if (getsockname(fileno(InChannel), &sa.sa, - &len) == 0) + if (getsockname(sm_io_getinfo(InChannel, SM_IO_WHAT_FD, + NULL), &sa.sa, &len) == 0) { p = hostnamebyanyaddr(&sa); if (tTd(15, 9)) - dprintf("getreq: got name %s\n", p); - define(macid("{if_name}", NULL), - newstr(p), &BlankEnvelope); + sm_dprintf("getreq: got name %s\n", p); + macdefine(&BlankEnvelope.e_macro, A_TEMP, + macid("{if_name}"), p); + + /* + ** Do this only if it is not the loopback + ** interface. + */ - /* do this only if it is not the loopback */ - /* interface: how to figure out? XXX */ if (!isloopback(sa)) { - define(macid("{if_addr}", NULL), - newstr(anynet_ntoa(&sa)), - &BlankEnvelope); - p = xalloc(5); - snprintf(p, 4, "%d", sa.sa.sa_family); - define(macid("{if_family}", NULL), p, - &BlankEnvelope); + char *addr; + char family[5]; + + addr = anynet_ntoa(&sa); + (void) sm_snprintf(family, + sizeof(family), + "%d", sa.sa.sa_family); + macdefine(&BlankEnvelope.e_macro, + A_TEMP, + macid("{if_addr}"), addr); + macdefine(&BlankEnvelope.e_macro, + A_TEMP, + macid("{if_family}"), family); if (tTd(15, 7)) - dprintf("getreq: got addr %s and family %s\n", - macvalue(macid("{if_addr}", NULL), - &BlankEnvelope), - macvalue(macid("{if_addr}", NULL), - &BlankEnvelope)); + sm_dprintf("getreq: got addr %s and family %s\n", + addr, family); } else { - define(macid("{if_addr}", NULL), NULL, - &BlankEnvelope); - define(macid("{if_family}", NULL), NULL, - &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, + A_PERM, + macid("{if_addr}"), NULL); + macdefine(&BlankEnvelope.e_macro, + A_PERM, + macid("{if_family}"), NULL); } } else { if (tTd(15, 7)) - dprintf("getreq: getsockname failed\n"); - define(macid("{if_name}", NULL), NULL, - &BlankEnvelope); - define(macid("{if_addr}", NULL), NULL, - &BlankEnvelope); - define(macid("{if_family}", NULL), NULL, - &BlankEnvelope); + sm_dprintf("getreq: getsockname failed\n"); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{if_name}"), NULL); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{if_addr}"), NULL); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{if_family}"), NULL); } break; } @@ -867,17 +846,18 @@ getrequests(e) /* parent -- keep track of children */ if (control) { - snprintf(status, sizeof status, "control socket server child"); - proc_list_add(pid, status, PROC_CONTROL); + (void) sm_snprintf(status, sizeof status, + "control socket server child"); + proc_list_add(pid, status, PROC_CONTROL, 0, -1); } else { - snprintf(status, sizeof status, - "SMTP server child for %s", - anynet_ntoa(&RealHostAddr)); - proc_list_add(pid, status, PROC_DAEMON); + (void) sm_snprintf(status, sizeof status, + "SMTP server child for %s", + anynet_ntoa(&RealHostAddr)); + proc_list_add(pid, status, PROC_DAEMON, 0, -1); } - (void) releasesignal(SIGCHLD); + (void) sm_releasesignal(SIGCHLD); /* close the read end of the synchronization pipe */ if (pipefd[0] != -1) @@ -896,11 +876,102 @@ getrequests(e) pipefd[1] = -1; } } - if (tTd(15, 2)) - dprintf("getreq: returning\n"); + sm_dprintf("getreq: returning\n"); + +#if MILTER +# if _FFR_MILTER_PERDAEMON + /* set the filters for this daemon */ + if (Daemons[curdaemon].d_inputfilterlist != NULL) + { + for (i = 0; + (Daemons[curdaemon].d_inputfilters[i] != NULL && + i < MAXFILTERS); + i++) + { + InputFilters[i] = Daemons[curdaemon].d_inputfilters[i]; + } + if (i < MAXFILTERS) + InputFilters[i] = NULL; + } +# endif /* _FFR_MILTER_PERDAEMON */ +#endif /* MILTER */ return &Daemons[curdaemon].d_flags; } + +/* +** GETREQUESTS_CHECKDISKSPACE -- check available diskspace. +** +** Parameters: +** e -- envelope. +** +** Returns: +** none. +** +** Side Effects: +** Modifies Daemon flags (D_ETRNONLY) if not enough disk space. +*/ + +static void +getrequests_checkdiskspace(e) + ENVELOPE *e; +{ + bool logged = false; + int idx; + time_t now; + + now = curtime(); + if (now < NextDiskSpaceCheck) + return; + + /* Check if there is available disk space in all queue groups. */ + if (!enoughdiskspace(0, NULL)) + { + for (idx = 0; idx < NDaemons; ++idx) + { + if (bitnset(D_ETRNONLY, Daemons[idx].d_flags)) + continue; + + /* log only if not logged before */ + if (!logged) + { + if (LogLevel > 8) + sm_syslog(LOG_INFO, NOQID, + "rejecting new messages: min free: %ld", + MinBlocksFree); + sm_setproctitle(true, e, + "rejecting new messages: min free: %ld", + MinBlocksFree); + logged = true; + } + setbitn(D_ETRNONLY, Daemons[idx].d_flags); + } + } + else + { + for (idx = 0; idx < NDaemons; ++idx) + { + if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags)) + continue; + + /* log only if not logged before */ + if (!logged) + { + if (LogLevel > 8) + sm_syslog(LOG_INFO, NOQID, + "accepting new messages (again)"); + logged = true; + } + + /* title will be set later */ + clrbitn(D_ETRNONLY, Daemons[idx].d_flags); + } + } + + /* only check disk space once a minute */ + NextDiskSpaceCheck = now + 60; +} + /* ** OPENDAEMONSOCKET -- open SMTP socket ** @@ -918,11 +989,11 @@ getrequests(e) ** Exits if the socket cannot be created. */ -# define MAXOPENTRIES 10 /* maximum number of tries to open connection */ +#define MAXOPENTRIES 10 /* maximum number of tries to open connection */ static int opendaemonsocket(d, firsttime) - struct daemon *d; + DAEMON_T *d; bool firsttime; { int on = 1; @@ -932,7 +1003,7 @@ opendaemonsocket(d, firsttime) int save_errno; if (tTd(15, 2)) - dprintf("opendaemonsocket(%s)\n", d->d_name); + sm_dprintf("opendaemonsocket(%s)\n", d->d_name); do { @@ -940,24 +1011,54 @@ opendaemonsocket(d, firsttime) (void) sleep(5); if (firsttime || d->d_socket < 0) { +#if _FFR_DAEMON_NETUNIX +# if NETUNIX + if (d->d_addr.sa.sa_family == AF_UNIX) + { + int rval; + long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_ROOTOK|SFF_EXECOK; + /* if not safe, don't use it */ + rval = safefile(d->d_addr.sunix.sun_path, + RunAsUid, RunAsGid, + RunAsUserName, sff, + S_IRUSR|S_IWUSR, NULL); + if (rval != 0) + { + save_errno = errno; + syserr("opendaemonsocket: daemon %s: unsafe domain socket %s", + d->d_name, + d->d_addr.sunix.sun_path); + goto fail; + } + + /* Don't try to overtake an existing socket */ + (void) unlink(d->d_addr.sunix.sun_path); + } +# endif /* NETUNIX */ +#endif /* _FFR_DOMAIN_NETUNIX */ d->d_socket = socket(d->d_addr.sa.sa_family, SOCK_STREAM, 0); if (d->d_socket < 0) { save_errno = errno; - syserr("opendaemonsocket: daemon %s: can't create server SMTP socket", d->d_name); + syserr("opendaemonsocket: daemon %s: can't create server SMTP socket", + d->d_name); + fail: if (bitnset(D_OPTIONAL, d->d_flags) && - (save_errno == EAFNOSUPPORT || - save_errno == EPROTONOSUPPORT)) + (!transienterror(save_errno) || + ntries >= MAXOPENTRIES - 1)) { - syserr("opendaemonsocket: daemon %s: optional socket disabled", d->d_name); + syserr("opendaemonsocket: daemon %s: optional socket disabled", + d->d_name); setbitn(D_DISABLE, d->d_flags); + d->d_socket = -1; return -1; } severe: if (LogLevel > 0) sm_syslog(LOG_ALERT, NOQID, - "daemon %s: problem creating SMTP socket", d->d_name); + "daemon %s: problem creating SMTP socket", + d->d_name); d->d_socket = -1; continue; } @@ -973,7 +1074,7 @@ opendaemonsocket(d, firsttime) (void) setsockopt(d->d_socket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on); -# ifdef SO_RCVBUF +#ifdef SO_RCVBUF if (d->d_tcprcvbufsize > 0) { if (setsockopt(d->d_socket, SOL_SOCKET, @@ -982,8 +1083,8 @@ opendaemonsocket(d, firsttime) sizeof(d->d_tcprcvbufsize)) < 0) syserr("opendaemonsocket: daemon %s: setsockopt(SO_RCVBUF)", d->d_name); } -# endif /* SO_RCVBUF */ -# ifdef SO_SNDBUF +#endif /* SO_RCVBUF */ +#ifdef SO_SNDBUF if (d->d_tcpsndbufsize > 0) { if (setsockopt(d->d_socket, SOL_SOCKET, @@ -992,7 +1093,7 @@ opendaemonsocket(d, firsttime) sizeof(d->d_tcpsndbufsize)) < 0) syserr("opendaemonsocket: daemon %s: setsockopt(SO_SNDBUF)", d->d_name); } -# endif /* SO_SNDBUF */ +#endif /* SO_SNDBUF */ if ((fdflags = fcntl(d->d_socket, F_GETFD, 0)) == -1 || fcntl(d->d_socket, F_SETFD, @@ -1002,30 +1103,37 @@ opendaemonsocket(d, firsttime) syserr("opendaemonsocket: daemon %s: failed to %s close-on-exec flag: %s", d->d_name, fdflags == -1 ? "get" : "set", - errstring(save_errno)); + sm_errstring(save_errno)); (void) close(d->d_socket); goto severe; } switch (d->d_addr.sa.sa_family) { -# if NETINET +#if _FFR_DAEMON_NETUNIX +# ifdef NETUNIX + case AF_UNIX: + socksize = sizeof d->d_addr.sunix; + break; +# endif /* NETUNIX */ +#endif /* _FFR_DAEMON_NETUNIX */ +#if NETINET case AF_INET: socksize = sizeof d->d_addr.sin; break; -# endif /* NETINET */ +#endif /* NETINET */ -# if NETINET6 +#if NETINET6 case AF_INET6: socksize = sizeof d->d_addr.sin6; break; -# endif /* NETINET6 */ +#endif /* NETINET6 */ -# if NETISO +#if NETISO case AF_ISO: socksize = sizeof d->d_addr.siso; break; -# endif /* NETISO */ +#endif /* NETISO */ default: socksize = sizeof d->d_addr; @@ -1039,7 +1147,7 @@ opendaemonsocket(d, firsttime) syserr("opendaemonsocket: daemon %s: cannot bind", d->d_name); (void) close(d->d_socket); - goto severe; + goto fail; } } if (!firsttime && @@ -1063,17 +1171,17 @@ opendaemonsocket(d, firsttime) ** ** Parameters: ** daemonaddr -- socket for daemon -** daemon -- number of daemon ** ** Returns: ** port number on which daemon should run ** */ -static u_short + +static unsigned short setupdaemon(daemonaddr) SOCKADDR *daemonaddr; { - u_short port; + unsigned short port; /* ** Set up the address for the mailer. @@ -1082,28 +1190,28 @@ setupdaemon(daemonaddr) if (daemonaddr->sa.sa_family == AF_UNSPEC) { memset(daemonaddr, '\0', sizeof *daemonaddr); -# if NETINET +#if NETINET daemonaddr->sa.sa_family = AF_INET; -# endif /* NETINET */ +#endif /* NETINET */ } switch (daemonaddr->sa.sa_family) { -# if NETINET +#if NETINET case AF_INET: if (daemonaddr->sin.sin_addr.s_addr == 0) daemonaddr->sin.sin_addr.s_addr = INADDR_ANY; port = daemonaddr->sin.sin_port; break; -# endif /* NETINET */ +#endif /* NETINET */ -# if NETINET6 +#if NETINET6 case AF_INET6: if (IN6_IS_ADDR_UNSPECIFIED(&daemonaddr->sin6.sin6_addr)) daemonaddr->sin6.sin6_addr = in6addr_any; port = daemonaddr->sin6.sin6_port; break; -# endif /* NETINET6 */ +#endif /* NETINET6 */ default: /* unknown protocol */ @@ -1112,9 +1220,9 @@ setupdaemon(daemonaddr) } if (port == 0) { -# ifdef NO_GETSERVBYNAME +#ifdef NO_GETSERVBYNAME port = htons(25); -# else /* NO_GETSERVBYNAME */ +#else /* NO_GETSERVBYNAME */ { register struct servent *sp; @@ -1127,28 +1235,28 @@ setupdaemon(daemonaddr) else port = sp->s_port; } -# endif /* NO_GETSERVBYNAME */ +#endif /* NO_GETSERVBYNAME */ } switch (daemonaddr->sa.sa_family) { -# if NETINET +#if NETINET case AF_INET: daemonaddr->sin.sin_port = port; break; -# endif /* NETINET */ +#endif /* NETINET */ -# if NETINET6 +#if NETINET6 case AF_INET6: daemonaddr->sin6.sin6_port = port; break; -# endif /* NETINET6 */ +#endif /* NETINET6 */ default: /* unknown protocol */ break; } - return(port); + return port; } /* ** CLRDAEMON -- reset the daemon connection @@ -1168,13 +1276,79 @@ clrdaemon() { int i; - for (i = 0; i < ndaemons; i++) + for (i = 0; i < NDaemons; i++) { if (Daemons[i].d_socket >= 0) (void) close(Daemons[i].d_socket); Daemons[i].d_socket = -1; } } + +/* +** GETMODIFIERS -- get modifier flags +** +** Parameters: +** v -- the modifiers (input text line). +** modifiers -- pointer to flag field to represent modifiers. +** +** Returns: +** (xallocat()ed) string representation of modifiers. +** +** Side Effects: +** fills in modifiers. +*/ + +char * +getmodifiers(v, modifiers) + char *v; + BITMAP256 modifiers; +{ + int l; + char *h, *f, *flags; + + /* maximum length of flags: upper case Option -> "OO " */ + l = 3 * strlen(v) + 3; + flags = xalloc(l); + f = flags; + clrbitmap(modifiers); + for (h = v; *h != '\0'; h++) + { + if (!(isascii(*h) && isspace(*h))) + { + setbitn(*h, modifiers); + if (flags != f) + *flags++ = ' '; + *flags++ = *h; + if (isupper(*h)) + *flags++ = *h; + } + } + *flags++ = '\0'; + return f; +} + +/* +** CHKDAEMONMODIFIERS -- check whether all daemons have set a flag. +** +** Parameters: +** flag -- the flag to test. +** +** Returns: +** true iff all daemons have set flag. +*/ + +bool +chkdaemonmodifiers(flag) + int flag; +{ + int i; + + for (i = 0; i < NDaemons; i++) + if (!bitnset((char) flag, Daemons[i].d_flags)) + return false; + return true; +} + /* ** SETSOCKADDROPTIONS -- set options for SOCKADDR (daemon or client) ** @@ -1189,20 +1363,18 @@ clrdaemon() static void setsockaddroptions(p, d) register char *p; - struct daemon *d; + DAEMON_T *d; { -# if NETISO +#if NETISO short portno; -# endif /* NETISO */ - int l; - char *h, *flags; +#endif /* NETISO */ char *port = NULL; char *addr = NULL; -# if NETINET +#if NETINET if (d->d_addr.sa.sa_family == AF_UNSPEC) d->d_addr.sa.sa_family = AF_INET; -# endif /* NETINET */ +#endif /* NETINET */ while (p != NULL) { @@ -1230,26 +1402,33 @@ setsockaddroptions(p, d) case 'F': /* address family */ if (isascii(*v) && isdigit(*v)) d->d_addr.sa.sa_family = atoi(v); -# if NETINET - else if (strcasecmp(v, "inet") == 0) +#if _FFR_DAEMON_NETUNIX +# ifdef NETUNIX + else if (sm_strcasecmp(v, "unix") == 0 || + sm_strcasecmp(v, "local") == 0) + d->d_addr.sa.sa_family = AF_UNIX; +# endif /* NETUNIX */ +#endif /* _FFR_DAEMON_NETUNIX */ +#if NETINET + else if (sm_strcasecmp(v, "inet") == 0) d->d_addr.sa.sa_family = AF_INET; -# endif /* NETINET */ -# if NETINET6 - else if (strcasecmp(v, "inet6") == 0) +#endif /* NETINET */ +#if NETINET6 + else if (sm_strcasecmp(v, "inet6") == 0) d->d_addr.sa.sa_family = AF_INET6; -# endif /* NETINET6 */ -# if NETISO - else if (strcasecmp(v, "iso") == 0) +#endif /* NETINET6 */ +#if NETISO + else if (sm_strcasecmp(v, "iso") == 0) d->d_addr.sa.sa_family = AF_ISO; -# endif /* NETISO */ -# if NETNS - else if (strcasecmp(v, "ns") == 0) +#endif /* NETISO */ +#if NETNS + else if (sm_strcasecmp(v, "ns") == 0) d->d_addr.sa.sa_family = AF_NS; -# endif /* NETNS */ -# if NETX25 - else if (strcasecmp(v, "x.25") == 0) +#endif /* NETNS */ +#if NETX25 + else if (sm_strcasecmp(v, "x.25") == 0) d->d_addr.sa.sa_family = AF_CCITT; -# endif /* NETX25 */ +#endif /* NETX25 */ else syserr("554 5.3.5 Unknown address family %s in Family=option", v); @@ -1259,6 +1438,14 @@ setsockaddroptions(p, d) addr = v; break; +#if MILTER +# if _FFR_MILTER_PERDAEMON + case 'I': + d->d_inputfilterlist = v; + break; +# endif /* _FFR_MILTER_PERDAEMON */ +#endif /* MILTER */ + case 'P': /* port */ port = v; break; @@ -1268,25 +1455,7 @@ setsockaddroptions(p, d) break; case 'M': /* modifiers (flags) */ - l = 3 * strlen(v) + 3; - h = v; - flags = xalloc(l); - d->d_mflags = flags; - for (; *h != '\0'; h++) - { - if (!(isascii(*h) && isspace(*h))) - { - if (flags != d->d_mflags) - *flags++ = ' '; - *flags++ = *h; - if (isupper(*h)) - *flags++ = *h; - } - } - *flags++ = '\0'; - for (; *v != '\0'; v++) - if (!(isascii(*v) && isspace(*v))) - setbitn(bitidx(*v), d->d_flags); + d->d_mflags = getmodifiers(v, d->d_flags); break; case 'S': /* send buffer size */ @@ -1312,10 +1481,39 @@ setsockaddroptions(p, d) { switch (d->d_addr.sa.sa_family) { -# if NETINET +#if _FFR_DAEMON_NETUNIX +# if NETUNIX + case AF_UNIX: + if (strlen(addr) >= sizeof(d->d_addr.sunix.sun_path)) + { + errno = ENAMETOOLONG; + syserr("setsockaddroptions: domain socket name too long: %s > %d", + addr, sizeof(d->d_addr.sunix.sun_path)); + break; + } + + /* if not safe, don't use it */ + if (safefile(addr, RunAsUid, RunAsGid, + RunAsUserName, + SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_ROOTOK|SFF_EXECOK, + S_IRUSR|S_IWUSR, NULL) != 0) + { + syserr("setsockaddroptions: unsafe domain socket"); + break; + } + (void) memset(&d->d_addr.sunix.sun_path, '\0', + sizeof(d->d_addr.sunix.sun_path)); + (void) sm_strlcpy((char *)&d->d_addr.sunix.sun_path, + addr, + sizeof(d->d_addr.sunix.sun_path)); + break; +# endif /* NETUNIX */ +#endif /* _FFR_DAEMON_NETUNIX */ +#if NETINET case AF_INET: if (!isascii(*addr) || !isdigit(*addr) || - ((d->d_addr.sin.sin_addr.s_addr = inet_addr(addr)) == INADDR_NONE)) + ((d->d_addr.sin.sin_addr.s_addr = inet_addr(addr)) + == INADDR_NONE)) { register struct hostent *hp; @@ -1335,21 +1533,20 @@ setsockaddroptions(p, d) memmove(&d->d_addr.sin.sin_addr, *(hp->h_addr_list), INADDRSZ); -# if _FFR_FREEHOSTENT && NETINET6 +# if NETINET6 freehostent(hp); hp = NULL; -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +# endif /* NETINET6 */ } } break; -# endif /* NETINET */ +#endif /* NETINET */ -# if NETINET6 +#if NETINET6 case AF_INET6: - if (!isascii(*addr) || - (!isxdigit(*addr) && *addr != ':') || - inet_pton(AF_INET6, addr, - &d->d_addr.sin6.sin6_addr) != 1) + if (!isascii(*addr) || !isxdigit(*addr) || + anynet_pton(AF_INET6, addr, + &d->d_addr.sin6.sin6_addr) != 1) { register struct hostent *hp; @@ -1369,14 +1566,12 @@ setsockaddroptions(p, d) memmove(&d->d_addr.sin6.sin6_addr, *(hp->h_addr_list), IN6ADDRSZ); -# if _FFR_FREEHOSTENT freehostent(hp); hp = NULL; -# endif /* _FFR_FREEHOSTENT */ } } break; -# endif /* NETINET6 */ +#endif /* NETINET6 */ default: syserr("554 5.3.5 address= option unsupported for family %d", @@ -1389,16 +1584,17 @@ setsockaddroptions(p, d) { switch (d->d_addr.sa.sa_family) { -# if NETINET +#if NETINET case AF_INET: if (isascii(*port) && isdigit(*port)) - d->d_addr.sin.sin_port = htons((u_short)atoi((const char *)port)); + d->d_addr.sin.sin_port = htons((unsigned short) + atoi((const char *) port)); else { -# ifdef NO_GETSERVBYNAME +# ifdef NO_GETSERVBYNAME syserr("554 5.3.5 invalid port number: %s", port); -# else /* NO_GETSERVBYNAME */ +# else /* NO_GETSERVBYNAME */ register struct servent *sp; sp = getservbyname(port, "tcp"); @@ -1407,21 +1603,22 @@ setsockaddroptions(p, d) port); else d->d_addr.sin.sin_port = sp->s_port; -# endif /* NO_GETSERVBYNAME */ +# endif /* NO_GETSERVBYNAME */ } break; -# endif /* NETINET */ +#endif /* NETINET */ -# if NETINET6 +#if NETINET6 case AF_INET6: if (isascii(*port) && isdigit(*port)) - d->d_addr.sin6.sin6_port = htons((u_short)atoi(port)); + d->d_addr.sin6.sin6_port = htons((unsigned short) + atoi(port)); else { -# ifdef NO_GETSERVBYNAME +# ifdef NO_GETSERVBYNAME syserr("554 5.3.5 invalid port number: %s", port); -# else /* NO_GETSERVBYNAME */ +# else /* NO_GETSERVBYNAME */ register struct servent *sp; sp = getservbyname(port, "tcp"); @@ -1430,22 +1627,22 @@ setsockaddroptions(p, d) port); else d->d_addr.sin6.sin6_port = sp->s_port; -# endif /* NO_GETSERVBYNAME */ +# endif /* NO_GETSERVBYNAME */ } break; -# endif /* NETINET6 */ +#endif /* NETINET6 */ -# if NETISO +#if NETISO case AF_ISO: /* assume two byte transport selector */ if (isascii(*port) && isdigit(*port)) - portno = htons((u_short)atoi(port)); + portno = htons((unsigned short) atoi(port)); else { -# ifdef NO_GETSERVBYNAME +# ifdef NO_GETSERVBYNAME syserr("554 5.3.5 invalid port number: %s", port); -# else /* NO_GETSERVBYNAME */ +# else /* NO_GETSERVBYNAME */ register struct servent *sp; sp = getservbyname(port, "tcp"); @@ -1454,12 +1651,12 @@ setsockaddroptions(p, d) port); else portno = sp->s_port; -# endif /* NO_GETSERVBYNAME */ +# endif /* NO_GETSERVBYNAME */ } memmove(TSEL(&d->d_addr.siso), (char *) &portno, 2); break; -# endif /* NETISO */ +#endif /* NETISO */ default: syserr("554 5.3.5 Port= option unsupported for family %d", @@ -1475,41 +1672,53 @@ setsockaddroptions(p, d) ** p -- the options line. ** ** Returns: -** TRUE if successful, FALSE otherwise. +** true if successful, false otherwise. +** +** Side Effects: +** increments number of daemons. */ +#define DEF_LISTENQUEUE 10 + bool setdaemonoptions(p) register char *p; { - if (ndaemons >= MAXDAEMONS) - return FALSE; - Daemons[ndaemons].d_socket = -1; - Daemons[ndaemons].d_listenqueue = 10; - clrbitmap(Daemons[ndaemons].d_flags); - setsockaddroptions(p, &Daemons[ndaemons]); - - if (Daemons[ndaemons].d_name != NULL) - Daemons[ndaemons].d_name = newstr(Daemons[ndaemons].d_name); + if (NDaemons >= MAXDAEMONS) + return false; + Daemons[NDaemons].d_socket = -1; + Daemons[NDaemons].d_listenqueue = DEF_LISTENQUEUE; + clrbitmap(Daemons[NDaemons].d_flags); + setsockaddroptions(p, &Daemons[NDaemons]); + +#if MILTER +# if _FFR_MILTER_PERDAEMON + if (Daemons[NDaemons].d_inputfilterlist != NULL) + Daemons[NDaemons].d_inputfilterlist = newstr(Daemons[NDaemons].d_inputfilterlist); +# endif /* _FFR_MILTER_PERDAEMON */ +#endif /* MILTER */ + + if (Daemons[NDaemons].d_name != NULL) + Daemons[NDaemons].d_name = newstr(Daemons[NDaemons].d_name); else { char num[30]; - snprintf(num, sizeof num, "Daemon%d", ndaemons); - Daemons[ndaemons].d_name = newstr(num); + (void) sm_snprintf(num, sizeof num, "Daemon%d", NDaemons); + Daemons[NDaemons].d_name = newstr(num); } if (tTd(37, 1)) { - dprintf("Daemon %s flags: ", Daemons[ndaemons].d_name); - if (bitnset(D_ETRNONLY, Daemons[ndaemons].d_flags)) - dprintf("ETRNONLY "); - if (bitnset(D_NOETRN, Daemons[ndaemons].d_flags)) - dprintf("NOETRN "); - dprintf("\n"); + sm_dprintf("Daemon %s flags: ", Daemons[NDaemons].d_name); + if (bitnset(D_ETRNONLY, Daemons[NDaemons].d_flags)) + sm_dprintf("ETRNONLY "); + if (bitnset(D_NOETRN, Daemons[NDaemons].d_flags)) + sm_dprintf("NOETRN "); + sm_dprintf("\n"); } - ++ndaemons; - return TRUE; + ++NDaemons; + return true; } /* ** INITDAEMON -- initialize daemon if not yet done. @@ -1523,15 +1732,16 @@ setdaemonoptions(p) ** Side Effects: ** initializes structure for one daemon. */ + void initdaemon() { - if (ndaemons == 0) + if (NDaemons == 0) { - Daemons[ndaemons].d_socket = -1; - Daemons[ndaemons].d_listenqueue = 10; - Daemons[ndaemons].d_name = "Daemon0"; - ndaemons = 1; + Daemons[NDaemons].d_socket = -1; + Daemons[NDaemons].d_listenqueue = DEF_LISTENQUEUE; + Daemons[NDaemons].d_name = "Daemon0"; + NDaemons = 1; } } /* @@ -1544,27 +1754,31 @@ initdaemon() ** none. */ -static SOCKADDR ClientAddr; /* address for client */ +static DAEMON_T ClientSettings[AF_MAX + 1]; void setclientoptions(p) register char *p; { - struct daemon d; - extern ENVELOPE BlankEnvelope; + int family; + DAEMON_T d; memset(&d, '\0', sizeof d); setsockaddroptions(p, &d); /* grab what we need */ - memcpy(&ClientAddr, &d.d_addr, sizeof ClientAddr); - TcpSndBufferSize = d.d_tcpsndbufsize; - TcpRcvBufferSize = d.d_tcprcvbufsize; - if (d.d_mflags != NULL) - define(macid("{client_flags}", NULL), d.d_mflags, - &BlankEnvelope); + family = d.d_addr.sa.sa_family; + STRUCTCOPY(d, ClientSettings[family]); + setbitn(D_ISSET, ClientSettings[family].d_flags); /* mark as set */ + if (d.d_name != NULL) + ClientSettings[family].d_name = newstr(d.d_name); else - define(macid("{client_flags}", NULL), "", &BlankEnvelope); + { + char num[30]; + + (void) sm_snprintf(num, sizeof num, "Client%d", family); + ClientSettings[family].d_name = newstr(num); + } } /* ** ADDR_FAMILY -- determine address family from address @@ -1583,30 +1797,106 @@ static int addr_family(addr) char *addr; { -# if NETINET6 +#if NETINET6 SOCKADDR clt_addr; -# endif /* NETINET6 */ +#endif /* NETINET6 */ -# if NETINET +#if NETINET if (inet_addr(addr) != INADDR_NONE) { if (tTd(16, 9)) - printf("addr_family(%s): INET\n", addr); + sm_dprintf("addr_family(%s): INET\n", addr); return AF_INET; } -# endif /* NETINET */ -# if NETINET6 - if (inet_pton(AF_INET6, addr, &clt_addr.sin6.sin6_addr) == 1) +#endif /* NETINET */ +#if NETINET6 + if (anynet_pton(AF_INET6, addr, &clt_addr.sin6.sin6_addr) == 1) { if (tTd(16, 9)) - printf("addr_family(%s): INET6\n", addr); + sm_dprintf("addr_family(%s): INET6\n", addr); return AF_INET6; } -# endif /* NETINET6 */ +#endif /* NETINET6 */ +#if _FFR_DAEMON_NETUNIX +# if NETUNIX + if (*addr == '/') + { + if (tTd(16, 9)) + sm_dprintf("addr_family(%s): LOCAL\n", addr); + return AF_UNIX; + } +# endif /* NETUNIX */ +#endif /* _FFR_DAEMON_NETUNIX */ if (tTd(16, 9)) - printf("addr_family(%s): UNSPEC\n", addr); + sm_dprintf("addr_family(%s): UNSPEC\n", addr); return AF_UNSPEC; } + +/* +** CHKCLIENTMODIFIERS -- check whether all clients have set a flag. +** +** Parameters: +** flag -- the flag to test. +** +** Returns: +** true iff all configured clients have set the flag. +*/ + +bool +chkclientmodifiers(flag) + int flag; +{ + int i; + bool flagisset; + + flagisset = false; + for (i = 0; i < AF_MAX; i++) + { + if (bitnset(D_ISSET, ClientSettings[i].d_flags)) + { + if (!bitnset((char) flag, ClientSettings[i].d_flags)) + return false; + flagisset = true; + } + } + return flagisset; +} + +#if MILTER +# if _FFR_MILTER_PERDAEMON +/* +** SETUP_DAEMON_FILTERS -- Parse per-socket filters +** +** Parameters: +** none +** +** Returns: +** none +*/ + +void +setup_daemon_milters() +{ + int idx; + + if (OpMode == MD_SMTP) + { + /* no need to configure the daemons */ + return; + } + + for (idx = 0; idx < NDaemons; idx++) + { + if (Daemons[idx].d_inputfilterlist != NULL) + { + milter_config(Daemons[idx].d_inputfilterlist, + Daemons[idx].d_inputfilters, + MAXFILTERS); + } + } +} +# endif /* _FFR_MILTER_PERDAEMON */ +#endif /* MILTER */ /* ** MAKECONNECTION -- make a connection to an SMTP socket on a machine. ** @@ -1616,6 +1906,8 @@ addr_family(addr) ** mci -- a pointer to the mail connection information ** structure to be filled in. ** e -- the current envelope. +** enough -- time at which to stop further connection attempts. +** (0 means no limit) ** ** Returns: ** An exit code telling whether the connection could be @@ -1630,24 +1922,25 @@ static jmp_buf CtxConnectTimeout; SOCKADDR CurHostAddr; /* address of current host */ int -makeconnection(host, port, mci, e) +makeconnection(host, port, mci, e, enough) char *host; - volatile u_int port; + volatile unsigned int port; register MCI *mci; ENVELOPE *e; + time_t enough; { register volatile int addrno = 0; register volatile int s; - register struct hostent *volatile hp = (struct hostent *)NULL; + register struct hostent *volatile hp = (struct hostent *) NULL; SOCKADDR addr; SOCKADDR clt_addr; int save_errno = 0; volatile SOCKADDR_LEN_T addrlen; volatile bool firstconnect; - EVENT *volatile ev = NULL; -# if NETINET6 - volatile bool v6found = FALSE; -# endif /* NETINET6 */ + SM_EVENT *volatile ev = NULL; +#if NETINET6 + volatile bool v6found = false; +#endif /* NETINET6 */ volatile int family = InetMode; SOCKADDR_LEN_T len; volatile SOCKADDR_LEN_T socksize = 0; @@ -1656,9 +1949,9 @@ makeconnection(host, port, mci, e) char *p; extern ENVELOPE BlankEnvelope; - /* retranslate ${daemon_flags} into bitmap */ + /* retranslate {daemon_flags} into bitmap */ clrbitmap(d_flags); - if ((p = macvalue(macid("{daemon_flags}", NULL), e)) != NULL) + if ((p = macvalue(macid("{daemon_flags}"), e)) != NULL) { for (; *p != '\0'; p++) { @@ -1667,33 +1960,19 @@ makeconnection(host, port, mci, e) } } - /* "add" ${client_flags} to bitmap */ - if ((p = macvalue(macid("{client_flags}", NULL), e)) != NULL) - { - for (; *p != '\0'; p++) - { - /* look for just this one flag */ - if (*p == D_IFNHELO) - { - setbitn(bitidx(*p), d_flags); - break; - } - } - } - -# if NETINET6 +#if NETINET6 v4retry: -# endif /* NETINET6 */ - clt_bind = FALSE; +#endif /* NETINET6 */ + clt_bind = false; /* Set up the address for outgoing connection. */ if (bitnset(D_BINDIF, d_flags) && - (p = macvalue(macid("{if_addr}", NULL), e)) != NULL && + (p = macvalue(macid("{if_addr}"), e)) != NULL && *p != '\0') { -# if NETINET6 +#if NETINET6 char p6[INET6_ADDRSTRLEN]; -# endif /* NETINET6 */ +#endif /* NETINET6 */ memset(&clt_addr, '\0', sizeof clt_addr); @@ -1701,79 +1980,80 @@ makeconnection(host, port, mci, e) clt_addr.sa.sa_family = addr_family(p); switch (clt_addr.sa.sa_family) { -# if NETINET +#if NETINET case AF_INET: clt_addr.sin.sin_addr.s_addr = inet_addr(p); if (clt_addr.sin.sin_addr.s_addr != INADDR_NONE && clt_addr.sin.sin_addr.s_addr != INADDR_LOOPBACK) { - clt_bind = TRUE; + clt_bind = true; socksize = sizeof (struct sockaddr_in); } break; -# endif /* NETINET */ +#endif /* NETINET */ -# if NETINET6 +#if NETINET6 case AF_INET6: if (inet_addr(p) != INADDR_NONE) - snprintf(p6, sizeof p6, "::ffff:%s", p); + (void) sm_snprintf(p6, sizeof p6, + "IPv6:::ffff:%s", p); else - strlcpy(p6, p, sizeof p6); - if (inet_pton(AF_INET6, p6, - &clt_addr.sin6.sin6_addr) == 1 && + (void) sm_strlcpy(p6, p, sizeof p6); + if (anynet_pton(AF_INET6, p6, + &clt_addr.sin6.sin6_addr) == 1 && !IN6_IS_ADDR_LOOPBACK(&clt_addr.sin6.sin6_addr)) { - clt_bind = TRUE; + clt_bind = true; socksize = sizeof (struct sockaddr_in6); } break; -# endif /* NETINET6 */ +#endif /* NETINET6 */ -# if 0 +#if 0 default: syserr("554 5.3.5 Address= option unsupported for family %d", clt_addr.sa.sa_family); break; -# endif /* 0 */ +#endif /* 0 */ } if (clt_bind) family = clt_addr.sa.sa_family; } - else + + /* D_BINDIF not set or not available, fallback to ClientPortOptions */ + if (!clt_bind) { - STRUCTCOPY(ClientAddr, clt_addr); - if (clt_addr.sa.sa_family == AF_UNSPEC) - clt_addr.sa.sa_family = family; + STRUCTCOPY(ClientSettings[family].d_addr, clt_addr); switch (clt_addr.sa.sa_family) { -# if NETINET +#if NETINET case AF_INET: if (clt_addr.sin.sin_addr.s_addr == 0) clt_addr.sin.sin_addr.s_addr = INADDR_ANY; else - clt_bind = TRUE; + clt_bind = true; if (clt_addr.sin.sin_port != 0) - clt_bind = TRUE; + clt_bind = true; socksize = sizeof (struct sockaddr_in); break; -# endif /* NETINET */ -# if NETINET6 +#endif /* NETINET */ +#if NETINET6 case AF_INET6: if (IN6_IS_ADDR_UNSPECIFIED(&clt_addr.sin6.sin6_addr)) clt_addr.sin6.sin6_addr = in6addr_any; else - clt_bind = TRUE; + clt_bind = true; socksize = sizeof (struct sockaddr_in6); if (clt_addr.sin6.sin6_port != 0) - clt_bind = TRUE; + clt_bind = true; break; -# endif /* NETINET6 */ -# if NETISO +#endif /* NETINET6 */ +#if NETISO case AF_ISO: socksize = sizeof clt_addr.siso; - clt_bind = TRUE; + clt_bind = true; break; -# endif /* NETISO */ +#endif /* NETISO */ default: break; } @@ -1784,9 +2064,7 @@ makeconnection(host, port, mci, e) ** Accept "[a.b.c.d]" syntax for host name. */ -# if NAMED_BIND SM_SET_H_ERRNO(0); -# endif /* NAMED_BIND */ errno = 0; memset(&CurHostAddr, '\0', sizeof CurHostAddr); memset(&addr, '\0', sizeof addr); @@ -1798,18 +2076,18 @@ makeconnection(host, port, mci, e) p = strchr(host, ']'); if (p != NULL) { -# if NETINET +#if NETINET unsigned long hid = INADDR_NONE; -# endif /* NETINET */ -# if NETINET6 +#endif /* NETINET */ +#if NETINET6 struct sockaddr_in6 hid6; -# endif /* NETINET6 */ +#endif /* NETINET6 */ *p = '\0'; -# if NETINET6 +#if NETINET6 memset(&hid6, '\0', sizeof hid6); -# endif /* NETINET6 */ -# if NETINET +#endif /* NETINET6 */ +#if NETINET if (family == AF_INET && (hid = inet_addr(&host[1])) != INADDR_NONE) { @@ -1817,34 +2095,34 @@ makeconnection(host, port, mci, e) addr.sin.sin_addr.s_addr = hid; } else -# endif /* NETINET */ -# if NETINET6 +#endif /* NETINET */ +#if NETINET6 if (family == AF_INET6 && - inet_pton(AF_INET6, &host[1], - &hid6.sin6_addr) == 1) + anynet_pton(AF_INET6, &host[1], + &hid6.sin6_addr) == 1) { addr.sin6.sin6_family = AF_INET6; addr.sin6.sin6_addr = hid6.sin6_addr; } else -# endif /* NETINET6 */ +#endif /* NETINET6 */ { /* try it as a host name (avoid MX lookup) */ hp = sm_gethostbyname(&host[1], family); if (hp == NULL && p[-1] == '.') { -# if NAMED_BIND +#if NAMED_BIND int oldopts = _res.options; _res.options &= ~(RES_DEFNAMES|RES_DNSRCH); -# endif /* NAMED_BIND */ +#endif /* NAMED_BIND */ p[-1] = '\0'; hp = sm_gethostbyname(&host[1], family); p[-1] = '.'; -# if NAMED_BIND +#if NAMED_BIND _res.options = oldopts; -# endif /* NAMED_BIND */ +#endif /* NAMED_BIND */ } *p = ']'; goto gothostent; @@ -1871,23 +2149,23 @@ makeconnection(host, port, mci, e) hp = sm_gethostbyname(host, family); if (hp == NULL && *p == '.') { -# if NAMED_BIND +#if NAMED_BIND int oldopts = _res.options; _res.options &= ~(RES_DEFNAMES|RES_DNSRCH); -# endif /* NAMED_BIND */ +#endif /* NAMED_BIND */ *p = '\0'; hp = sm_gethostbyname(host, family); *p = '.'; -# if NAMED_BIND +#if NAMED_BIND _res.options = oldopts; -# endif /* NAMED_BIND */ +#endif /* NAMED_BIND */ } } gothostent: if (hp == NULL) { -# if NAMED_BIND +#if NAMED_BIND /* check for name server timeouts */ if (errno == ETIMEDOUT || h_errno == TRY_AGAIN || (errno == ECONNREFUSED && UseNameServer)) @@ -1897,8 +2175,8 @@ gothostent: errno = save_errno; return EX_TEMPFAIL; } -# endif /* NAMED_BIND */ -# if NETINET6 +#endif /* NAMED_BIND */ +#if NETINET6 /* ** Try v6 first, then fall back to v4. ** If we found a v6 address, but no v4 @@ -1912,7 +2190,7 @@ gothostent: } if (v6found) goto v6tempfail; -# endif /* NETINET6 */ +#endif /* NETINET6 */ save_errno = errno; mci_setstat(mci, EX_NOHOST, "5.1.2", NULL); errno = save_errno; @@ -1921,21 +2199,21 @@ gothostent: addr.sa.sa_family = hp->h_addrtype; switch (hp->h_addrtype) { -# if NETINET +#if NETINET case AF_INET: memmove(&addr.sin.sin_addr, hp->h_addr, INADDRSZ); break; -# endif /* NETINET */ +#endif /* NETINET */ -# if NETINET6 +#if NETINET6 case AF_INET6: memmove(&addr.sin6.sin6_addr, hp->h_addr, IN6ADDRSZ); break; -# endif /* NETINET6 */ +#endif /* NETINET6 */ default: if (hp->h_length > sizeof addr.sa.sa_data) @@ -1946,9 +2224,7 @@ gothostent: errno = EINVAL; return EX_NOHOST; } - memmove(addr.sa.sa_data, - hp->h_addr, - hp->h_length); + memmove(addr.sa.sa_data, hp->h_addr, hp->h_length); break; } addrno = 1; @@ -1960,9 +2236,9 @@ gothostent: if (port == 0) { -# ifdef NO_GETSERVBYNAME +#ifdef NO_GETSERVBYNAME port = htons(25); -# else /* NO_GETSERVBYNAME */ +#else /* NO_GETSERVBYNAME */ register struct servent *sp = getservbyname("smtp", "tcp"); if (sp == NULL) @@ -1974,41 +2250,56 @@ gothostent: } else port = sp->s_port; -# endif /* NO_GETSERVBYNAME */ +#endif /* NO_GETSERVBYNAME */ } +#if NETINET6 + if (addr.sa.sa_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr) && + ClientSettings[AF_INET].d_addr.sa.sa_family != 0) + { + /* + ** Ignore mapped IPv4 address since + ** there is a ClientPortOptions setting + ** for IPv4. + */ + + goto nextaddr; + } +#endif /* NETINET6 */ + switch (addr.sa.sa_family) { -# if NETINET +#if NETINET case AF_INET: addr.sin.sin_port = port; addrlen = sizeof (struct sockaddr_in); break; -# endif /* NETINET */ +#endif /* NETINET */ -# if NETINET6 +#if NETINET6 case AF_INET6: addr.sin6.sin6_port = port; addrlen = sizeof (struct sockaddr_in6); break; -# endif /* NETINET6 */ +#endif /* NETINET6 */ -# if NETISO +#if NETISO case AF_ISO: /* assume two byte transport selector */ memmove(TSEL((struct sockaddr_iso *) &addr), (char *) &port, 2); addrlen = sizeof (struct sockaddr_iso); break; -# endif /* NETISO */ +#endif /* NETISO */ default: syserr("Can't connect to address family %d", addr.sa.sa_family); mci_setstat(mci, EX_NOHOST, "5.1.2", NULL); errno = EINVAL; -# if _FFR_FREEHOSTENT && NETINET6 +#if NETINET6 if (hp != NULL) freehostent(hp); -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +#endif /* NETINET6 */ return EX_NOHOST; } @@ -2016,25 +2307,25 @@ gothostent: ** Try to actually open the connection. */ -# ifdef XLA +#if XLA /* if too many connections, don't bother trying */ if (!xla_noqueue_ok(host)) { -# if _FFR_FREEHOSTENT && NETINET6 +# if NETINET6 if (hp != NULL) freehostent(hp); -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +# endif /* NETINET6 */ return EX_TEMPFAIL; } -# endif /* XLA */ +#endif /* XLA */ - firstconnect = TRUE; + firstconnect = true; for (;;) { if (tTd(16, 1)) - dprintf("makeconnection (%s [%s].%d (%d))\n", - host, anynet_ntoa(&addr), ntohs(port), - addr.sa.sa_family); + sm_dprintf("makeconnection (%s [%s].%d (%d))\n", + host, anynet_ntoa(&addr), ntohs(port), + addr.sa.sa_family); /* save for logging */ CurHostAddr = addr; @@ -2047,46 +2338,45 @@ gothostent: } else { - s = socket(clt_addr.sa.sa_family, SOCK_STREAM, 0); + s = socket(addr.sa.sa_family, SOCK_STREAM, 0); } if (s < 0) { save_errno = errno; syserr("makeconnection: cannot create socket"); -# ifdef XLA +#if XLA xla_host_end(host); -# endif /* XLA */ +#endif /* XLA */ mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); -# if _FFR_FREEHOSTENT && NETINET6 +#if NETINET6 if (hp != NULL) freehostent(hp); -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +#endif /* NETINET6 */ errno = save_errno; return EX_TEMPFAIL; } -# ifdef SO_SNDBUF - if (TcpSndBufferSize > 0) +#ifdef SO_SNDBUF + if (ClientSettings[family].d_tcpsndbufsize > 0) { if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, - (char *) &TcpSndBufferSize, - sizeof(TcpSndBufferSize)) < 0) + (char *) &ClientSettings[family].d_tcpsndbufsize, + sizeof(ClientSettings[family].d_tcpsndbufsize)) < 0) syserr("makeconnection: setsockopt(SO_SNDBUF)"); } -# endif /* SO_SNDBUF */ -# ifdef SO_RCVBUF - if (TcpRcvBufferSize > 0) +#endif /* SO_SNDBUF */ +#ifdef SO_RCVBUF + if (ClientSettings[family].d_tcprcvbufsize > 0) { if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, - (char *) &TcpRcvBufferSize, - sizeof(TcpRcvBufferSize)) < 0) + (char *) &ClientSettings[family].d_tcprcvbufsize, + sizeof(ClientSettings[family].d_tcprcvbufsize)) < 0) syserr("makeconnection: setsockopt(SO_RCVBUF)"); } -# endif /* SO_RCVBUF */ - +#endif /* SO_RCVBUF */ if (tTd(16, 1)) - dprintf("makeconnection: fd=%d\n", s); + sm_dprintf("makeconnection: fd=%d\n", s); /* turn on network debugging? */ if (tTd(16, 101)) @@ -2096,9 +2386,9 @@ gothostent: (void) setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on); } - if (e->e_xfp != NULL) - (void) fflush(e->e_xfp); /* for debugging */ - errno = 0; /* for debugging */ + if (e->e_xfp != NULL) /* for debugging */ + (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); + errno = 0; /* for debugging */ if (clt_bind) { @@ -2106,7 +2396,7 @@ gothostent: switch (clt_addr.sa.sa_family) { -# if NETINET +#if NETINET case AF_INET: if (clt_addr.sin.sin_port != 0) (void) setsockopt(s, SOL_SOCKET, @@ -2114,9 +2404,9 @@ gothostent: (char *) &on, sizeof on); break; -# endif /* NETINET */ +#endif /* NETINET */ -# if NETINET6 +#if NETINET6 case AF_INET6: if (clt_addr.sin6.sin6_port != 0) (void) setsockopt(s, SOL_SOCKET, @@ -2124,7 +2414,7 @@ gothostent: (char *) &on, sizeof on); break; -# endif /* NETINET6 */ +#endif /* NETINET6 */ } if (bind(s, &clt_addr.sa, socksize) < 0) @@ -2134,10 +2424,10 @@ gothostent: errno = save_errno; syserr("makeconnection: cannot bind socket [%s]", anynet_ntoa(&clt_addr)); -# if _FFR_FREEHOSTENT && NETINET6 +#if NETINET6 if (hp != NULL) freehostent(hp); -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +#endif /* NETINET6 */ errno = save_errno; return EX_TEMPFAIL; } @@ -2153,34 +2443,34 @@ gothostent: int i; if (e->e_ntries <= 0 && TimeOuts.to_iconnect != 0) - ev = setevent(TimeOuts.to_iconnect, - connecttimeout, 0); + ev = sm_setevent(TimeOuts.to_iconnect, + connecttimeout, 0); else if (TimeOuts.to_connect != 0) - ev = setevent(TimeOuts.to_connect, - connecttimeout, 0); + ev = sm_setevent(TimeOuts.to_connect, + connecttimeout, 0); else ev = NULL; switch (ConnectOnlyTo.sa.sa_family) { -# if NETINET +#if NETINET case AF_INET: addr.sin.sin_addr.s_addr = ConnectOnlyTo.sin.sin_addr.s_addr; break; -# endif /* NETINET */ +#endif /* NETINET */ -# if NETINET6 +#if NETINET6 case AF_INET6: memmove(&addr.sin6.sin6_addr, &ConnectOnlyTo.sin6.sin6_addr, IN6ADDRSZ); break; -# endif /* NETINET6 */ +#endif /* NETINET6 */ } i = connect(s, (struct sockaddr *) &addr, addrlen); save_errno = errno; if (ev != NULL) - clrevent(ev); + sm_clrevent(ev); if (i >= 0) break; } @@ -2188,12 +2478,13 @@ gothostent: save_errno = errno; /* if running demand-dialed connection, try again */ - if (DialDelay > 0 && firstconnect) + if (DialDelay > 0 && firstconnect && + bitnset(M_DIALDELAY, mci->mci_mailer->m_flags)) { if (tTd(16, 1)) - dprintf("Connect failed (%s); trying again...\n", - errstring(save_errno)); - firstconnect = FALSE; + sm_dprintf("Connect failed (%s); trying again...\n", + sm_errstring(save_errno)); + firstconnect = false; (void) sleep(DialDelay); continue; } @@ -2201,34 +2492,38 @@ gothostent: /* couldn't connect.... figure out why */ (void) close(s); - if (LogLevel >= 14) + if (LogLevel > 13) sm_syslog(LOG_INFO, e->e_id, "makeconnection (%s [%s]) failed: %s", host, anynet_ntoa(&addr), - errstring(save_errno)); + sm_errstring(save_errno)); - if (hp != NULL && hp->h_addr_list[addrno] != NULL) +#if NETINET6 +nextaddr: +#endif /* NETINET6 */ + if (hp != NULL && hp->h_addr_list[addrno] != NULL && + (enough == 0 || curtime() < enough)) { if (tTd(16, 1)) - dprintf("Connect failed (%s); trying new address....\n", - errstring(save_errno)); + sm_dprintf("Connect failed (%s); trying new address....\n", + sm_errstring(save_errno)); switch (addr.sa.sa_family) { -# if NETINET +#if NETINET case AF_INET: memmove(&addr.sin.sin_addr, hp->h_addr_list[addrno++], INADDRSZ); break; -# endif /* NETINET */ +#endif /* NETINET */ -# if NETINET6 +#if NETINET6 case AF_INET6: memmove(&addr.sin6.sin6_addr, hp->h_addr_list[addrno++], IN6ADDRSZ); break; -# endif /* NETINET6 */ +#endif /* NETINET6 */ default: memmove(addr.sa.sa_data, @@ -2240,84 +2535,106 @@ gothostent: } errno = save_errno; -# if NETINET6 +#if NETINET6 if (family == AF_INET6) { if (tTd(16, 1)) - dprintf("Connect failed (%s); retrying with AF_INET....\n", - errstring(save_errno)); - v6found = TRUE; + sm_dprintf("Connect failed (%s); retrying with AF_INET....\n", + sm_errstring(save_errno)); + v6found = true; family = AF_INET; -# if _FFR_FREEHOSTENT if (hp != NULL) { freehostent(hp); hp = NULL; } -# endif /* _FFR_FREEHOSTENT */ goto v4retry; } v6tempfail: -# endif /* NETINET6 */ +#endif /* NETINET6 */ /* couldn't open connection */ -# if NETINET6 +#if NETINET6 /* Don't clobber an already saved errno from v4retry */ if (errno > 0) -# endif /* NETINET6 */ +#endif /* NETINET6 */ save_errno = errno; if (tTd(16, 1)) - dprintf("Connect failed (%s)\n", errstring(save_errno)); -# ifdef XLA + sm_dprintf("Connect failed (%s)\n", + sm_errstring(save_errno)); +#if XLA xla_host_end(host); -# endif /* XLA */ +#endif /* XLA */ mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL); -# if _FFR_FREEHOSTENT && NETINET6 +#if NETINET6 if (hp != NULL) freehostent(hp); -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +#endif /* NETINET6 */ errno = save_errno; return EX_TEMPFAIL; } -# if _FFR_FREEHOSTENT && NETINET6 +#if NETINET6 if (hp != NULL) { freehostent(hp); hp = NULL; } -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +#endif /* NETINET6 */ /* connection ok, put it into canonical form */ mci->mci_out = NULL; - if ((mci->mci_out = fdopen(s, "w")) == NULL || + if ((mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) s, + SM_IO_WRONLY, NULL)) == NULL || (s = dup(s)) < 0 || - (mci->mci_in = fdopen(s, "r")) == NULL) + (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) s, + SM_IO_RDONLY, NULL)) == NULL) { save_errno = errno; syserr("cannot open SMTP client channel, fd=%d", s); mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); if (mci->mci_out != NULL) - (void) fclose(mci->mci_out); + (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); (void) close(s); errno = save_errno; return EX_TEMPFAIL; } + sm_io_automode(mci->mci_out, mci->mci_in); + + /* set {client_flags} */ + if (ClientSettings[addr.sa.sa_family].d_mflags != NULL) + { + macdefine(&mci->mci_macro, A_PERM, + macid("{client_flags}"), + ClientSettings[addr.sa.sa_family].d_mflags); + } + else + macdefine(&mci->mci_macro, A_PERM, + macid("{client_flags}"), ""); + + /* "add" {client_flags} to bitmap */ + if (bitnset(D_IFNHELO, ClientSettings[addr.sa.sa_family].d_flags)) + { + /* look for just this one flag */ + setbitn(D_IFNHELO, d_flags); + } /* find out name for Interface through which we connect */ len = sizeof addr; if (getsockname(s, &addr.sa, &len) == 0) { char *name; - char *p; + char family[5]; - define(macid("{if_addr}", NULL), newstr(anynet_ntoa(&addr)), - &BlankEnvelope); - p = xalloc(5); - snprintf(p, 4, "%d", addr.sa.sa_family); - define(macid("{if_family}", NULL), p, &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_TEMP, + macid("{if_addr_out}"), anynet_ntoa(&addr)); + (void) sm_snprintf(family, sizeof(family), "%d", + addr.sa.sa_family); + macdefine(&BlankEnvelope.e_macro, A_TEMP, + macid("{if_family_out}"), family); name = hostnamebyanyaddr(&addr); - define(macid("{if_name}", NULL), newstr(name), &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_TEMP, + macid("{if_name_out}"), name); if (LogLevel > 11) { /* log connection information */ @@ -2332,9 +2649,12 @@ gothostent: } else { - define(macid("{if_name}", NULL), NULL, &BlankEnvelope); - define(macid("{if_addr}", NULL), NULL, &BlankEnvelope); - define(macid("{if_family}", NULL), NULL, &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{if_name_out}"), NULL); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{if_addr_out}"), NULL); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{if_family_out}"), NULL); } mci_setstat(mci, EX_OK, NULL, NULL); return EX_OK; @@ -2368,8 +2688,9 @@ connecttimeout() ** none. */ -# if NETUNIX -int makeconnection_ds(mux_path, mci) +#if NETUNIX +int +makeconnection_ds(mux_path, mci) char *mux_path; register MCI *mci; { @@ -2397,12 +2718,14 @@ int makeconnection_ds(mux_path, mci) if (strlen(mux_path) >= sizeof unix_addr.sun_path) { syserr("makeconnection_ds: domain socket name too long"); - /* XXX why TEMPFAIL ? */ + + /* XXX why TEMPFAIL but 5.x.y ? */ mci_setstat(mci, EX_TEMPFAIL, "5.3.5", NULL); errno = ENAMETOOLONG; return EX_UNAVAILABLE; } - (void) strlcpy(unix_addr.sun_path, mux_path, sizeof unix_addr.sun_path); + (void) sm_strlcpy(unix_addr.sun_path, mux_path, + sizeof unix_addr.sun_path); /* initialize domain socket */ sock = socket(AF_UNIX, SOCK_STREAM, 0); @@ -2429,54 +2752,65 @@ int makeconnection_ds(mux_path, mci) /* connection ok, put it into canonical form */ mci->mci_out = NULL; - if ((mci->mci_out = fdopen(sock, "w")) == NULL || - (sock = dup(sock)) < 0 || - (mci->mci_in = fdopen(sock, "r")) == NULL) + if ((mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, + (void *) sock, SM_IO_WRONLY, NULL)) + == NULL + || (sock = dup(sock)) < 0 || + (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, + (void *) sock, SM_IO_RDONLY, NULL)) + == NULL) { save_errno = errno; syserr("cannot open SMTP client channel, fd=%d", sock); mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); if (mci->mci_out != NULL) - (void) fclose(mci->mci_out); + (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); (void) close(sock); errno = save_errno; return EX_TEMPFAIL; } + sm_io_automode(mci->mci_out, mci->mci_in); mci_setstat(mci, EX_OK, NULL, NULL); errno = 0; return EX_OK; } -# endif /* NETUNIX */ +#endif /* NETUNIX */ /* -** SIGHUP -- handle a SIGHUP signal +** SHUTDOWN_DAEMON -- Performs a clean shutdown of the daemon ** ** Parameters: -** sig -- incoming signal. +** none. ** ** Returns: ** none. ** ** Side Effects: -** Sets RestartRequest which should cause the daemon -** to restart. -** -** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD -** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE -** DOING. +** closes control socket, exits. */ -/* ARGSUSED */ -static SIGFUNC_DECL -sighup(sig) - int sig; +void +shutdown_daemon() { - int save_errno = errno; + char *reason; - FIX_SYSV_SIGNAL(sig, sighup); - RestartRequest = "signal"; - errno = save_errno; - return SIGFUNC_RETURN; + sm_allsignals(true); + + reason = ShutdownRequest; + ShutdownRequest = NULL; + PendingSignal = 0; + + if (LogLevel > 79) + sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt (%s)", + reason == NULL ? "implicit call" : reason); + + FileName = NULL; + closecontrolsocket(true); +#if XLA + xla_all_end(); +#endif /* XLA */ + + finis(false, EX_OK); } /* ** RESTART_DAEMON -- Performs a clean restart of the daemon @@ -2495,14 +2829,15 @@ sighup(sig) #define SM_NOOP_SIGNAL(sig, old) \ do \ { \ - (old) = setsignal((sig), sm_signal_noop); \ + (old) = sm_signal((sig), sm_signal_noop); \ if ((old) == SIG_IGN || (old) == SIG_DFL) \ - (void) setsignal((sig), (old)); \ + (void) sm_signal((sig), (old)); \ } while (0) -static void +void restart_daemon() { + bool drop; int i; int save_errno; char *reason; @@ -2510,8 +2845,8 @@ restart_daemon() extern int DtableSize; /* clear the events to turn off SIGALRMs */ - clear_events(); - allsignals(TRUE); + sm_clear_events(); + sm_allsignals(true); reason = RestartRequest; RestartRequest = NULL; @@ -2522,21 +2857,34 @@ restart_daemon() if (LogLevel > 3) sm_syslog(LOG_INFO, NOQID, "could not restart: need full path"); - finis(FALSE, EX_OSFILE); + finis(false, EX_OSFILE); + /* NOTREACHED */ } if (LogLevel > 3) sm_syslog(LOG_INFO, NOQID, "restarting %s due to %s", SaveArgv[0], reason == NULL ? "implicit call" : reason); - closecontrolsocket(TRUE); - if (drop_privileges(TRUE) != EX_OK) + closecontrolsocket(true); + + /* + ** Want to drop to the user who started the process in all cases + ** *but* when running as "smmsp" for the clientmqueue queue run + ** daemon. In that case, UseMSP will be true, RunAsUid should not + ** be root, and RealUid should be either 0 or RunAsUid. + */ + + drop = !(UseMSP && RunAsUid != 0 && + (RealUid == 0 || RealUid == RunAsUid)); + + if (drop_privileges(drop) != EX_OK) { if (LogLevel > 0) sm_syslog(LOG_ALERT, NOQID, - "could not set[ug]id(%d, %d): %m", - RunAsUid, RunAsGid); - finis(FALSE, EX_OSERR); + "could not drop privileges: %s", + sm_errstring(errno)); + finis(false, EX_OSERR); + /* NOTREACHED */ } /* arrange for all the files to be closed */ @@ -2547,6 +2895,9 @@ restart_daemon() if ((j = fcntl(i, F_GETFD, 0)) != -1) (void) fcntl(i, F_SETFD, j | FD_CLOEXEC); } +#if SM_CONF_SHM + cleanup_shm(DaemonPid == getpid()); +#endif /* SM_CONF_SHM */ /* ** Need to allow signals before execve() to make them "harmless". @@ -2564,27 +2915,28 @@ restart_daemon() #ifdef SIGUSR1 SM_NOOP_SIGNAL(SIGUSR1, ousr1); #endif /* SIGUSR1 */ - allsignals(FALSE); + sm_allsignals(false); (void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron); save_errno = errno; /* block signals again and restore needed signals */ - allsignals(TRUE); + sm_allsignals(true); /* For finis() events */ - (void) setsignal(SIGALRM, oalrm); + (void) sm_signal(SIGALRM, oalrm); #ifdef SIGUSR1 /* For debugging finis() */ - (void) setsignal(SIGUSR1, ousr1); + (void) sm_signal(SIGUSR1, ousr1); #endif /* SIGUSR1 */ errno = save_errno; if (LogLevel > 0) - sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", - SaveArgv[0]); - finis(FALSE, EX_OSFILE); + sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %s", + SaveArgv[0], sm_errstring(errno)); + finis(false, EX_OSFILE); + /* NOTREACHED */ } /* ** MYHOSTNAME -- return the name of this host. @@ -2608,9 +2960,9 @@ myhostname(hostbuf, size) register struct hostent *hp; if (gethostname(hostbuf, size) < 0 || hostbuf[0] == '\0') - (void) strlcpy(hostbuf, "localhost", size); + (void) sm_strlcpy(hostbuf, "localhost", size); hp = sm_gethostbyname(hostbuf, InetMode); -# if NETINET && NETINET6 +#if NETINET && NETINET6 if (hp == NULL && InetMode == AF_INET6) { /* @@ -2621,14 +2973,13 @@ myhostname(hostbuf, size) hp = sm_gethostbyname(hostbuf, AF_INET); } -# endif /* NETINET && NETINET6 */ - +#endif /* NETINET && NETINET6 */ if (hp == NULL) return NULL; if (strchr(hp->h_name, '.') != NULL || strchr(hostbuf, '.') == NULL) (void) cleanstrcpy(hostbuf, hp->h_name, size); -# if NETINFO +#if NETINFO if (strchr(hostbuf, '.') == NULL) { char *domainname; @@ -2637,12 +2988,9 @@ myhostname(hostbuf, size) "domain", '\0'); if (domainname != NULL && strlen(domainname) + strlen(hostbuf) + 1 < size) - { - (void) strlcat(hostbuf, ".", size); - (void) strlcat(hostbuf, domainname, size); - } + (void) sm_strlcat2(hostbuf, ".", domainname, size); } -# endif /* NETINFO */ +#endif /* NETINFO */ /* ** If there is still no dot in the name, try looking for a @@ -2674,7 +3022,7 @@ myhostname(hostbuf, size) */ if (strchr(hostbuf, '.') == NULL && - !getcanonname(hostbuf, size, TRUE)) + !getcanonname(hostbuf, size, true, NULL)) { sm_syslog(LOG_CRIT, NOQID, "My unqualified host name (%s) unknown; sleeping for retry", @@ -2682,7 +3030,7 @@ myhostname(hostbuf, size) message("My unqualified host name (%s) unknown; sleeping for retry", hostbuf); (void) sleep(60); - if (!getcanonname(hostbuf, size, TRUE)) + if (!getcanonname(hostbuf, size, true, NULL)) { sm_syslog(LOG_ALERT, NOQID, "unable to qualify my own domain name (%s) -- using short name", @@ -2712,22 +3060,22 @@ addrcmp(hp, ha, sa) char *ha; SOCKADDR *sa; { -# if NETINET6 - u_char *a; -# endif /* NETINET6 */ +#if NETINET6 + unsigned char *a; +#endif /* NETINET6 */ switch (sa->sa.sa_family) { -# if NETINET +#if NETINET case AF_INET: if (hp->h_addrtype == AF_INET) return memcmp(ha, (char *) &sa->sin.sin_addr, INADDRSZ); break; -# endif /* NETINET */ +#endif /* NETINET */ -# if NETINET6 +#if NETINET6 case AF_INET6: - a = (u_char *) &sa->sin6.sin6_addr; + a = (unsigned char *) &sa->sin6.sin6_addr; /* Straight binary comparison */ if (hp->h_addrtype == AF_INET6) @@ -2738,7 +3086,7 @@ addrcmp(hp, ha, sa) IN6_IS_ADDR_V4MAPPED(&sa->sin6.sin6_addr)) return memcmp(a + IN6ADDRSZ - INADDRSZ, ha, INADDRSZ); break; -# endif /* NETINET6 */ +#endif /* NETINET6 */ } return -1; } @@ -2749,9 +3097,9 @@ addrcmp(hp, ha, sa) ** ** Parameters: ** fd -- the descriptor -** may_be_forged -- an outage that is set to TRUE if the +** may_be_forged -- an outage that is set to true if the ** forward lookup of RealHostName does not match -** RealHostAddr; set to FALSE if they do match. +** RealHostAddr; set to false if they do match. ** ** Returns: ** The user@host information associated with this descriptor. @@ -2777,15 +3125,24 @@ getauthinfo(fd, may_be_forged) int fd; bool *may_be_forged; { - volatile u_short port = 0; + unsigned short SM_NONVOLATILE port = 0; SOCKADDR_LEN_T falen; register char *volatile p = NULL; SOCKADDR la; SOCKADDR_LEN_T lalen; +#ifndef NO_GETSERVBYNAME register struct servent *sp; +# if NETINET + static unsigned short port4 = 0; +# endif /* NETINET */ +# if NETINET6 + static unsigned short port6 = 0; +# endif /* NETINET6 */ +#endif /* ! NO_GETSERVBYNAME */ volatile int s; int i = 0; - EVENT *ev; + size_t len; + SM_EVENT *ev; int nleft; struct hostent *hp; char *ostype = NULL; @@ -2793,7 +3150,7 @@ getauthinfo(fd, may_be_forged) char ibuf[MAXNAME + 1]; static char hbuf[MAXNAME * 2 + 11]; - *may_be_forged = FALSE; + *may_be_forged = false; falen = sizeof RealHostAddr; if (isatty(fd) || (i = getpeername(fd, &RealHostAddr.sa, &falen)) < 0 || falen <= 0 || RealHostAddr.sa.sa_family == 0) @@ -2805,14 +3162,15 @@ getauthinfo(fd, may_be_forged) ** errno in this case, so a mis-report doesn't ** happen later. */ + if (errno != ENOTSOCK) return NULL; errno = 0; } - (void) snprintf(hbuf, sizeof hbuf, "%s@localhost", - RealUserName); + (void) sm_strlcpyn(hbuf, sizeof hbuf, 2, RealUserName, + "@localhost"); if (tTd(9, 1)) - dprintf("getauthinfo: %s\n", hbuf); + sm_dprintf("getauthinfo: %s\n", hbuf); return hbuf; } @@ -2821,25 +3179,17 @@ getauthinfo(fd, may_be_forged) /* translate that to a host name */ RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr)); if (strlen(RealHostName) > MAXNAME) - RealHostName[MAXNAME] = '\0'; + RealHostName[MAXNAME] = '\0'; /* XXX - 1 ? */ } /* cross check RealHostName with forward DNS lookup */ - if (anynet_ntoa(&RealHostAddr)[0] == '[' || - RealHostName[0] == '[') - { - /* - ** address is not a socket or have an - ** IP address with no forward lookup - */ - *may_be_forged = FALSE; - } - else + if (anynet_ntoa(&RealHostAddr)[0] != '[' && + RealHostName[0] != '[') { int family; family = RealHostAddr.sa.sa_family; -# if NETINET6 && NEEDSGETIPNODE +#if NETINET6 && NEEDSGETIPNODE /* ** If RealHostAddr is an IPv6 connection with an ** IPv4-mapped address, we need RealHostName's IPv4 @@ -2856,22 +3206,24 @@ getauthinfo(fd, may_be_forged) if (family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&RealHostAddr.sin6.sin6_addr)) family = AF_INET; -# endif /* NETINET6 && NEEDSGETIPNODE */ +#endif /* NETINET6 && NEEDSGETIPNODE */ /* try to match the reverse against the forward lookup */ hp = sm_gethostbyname(RealHostName, family); if (hp == NULL) - *may_be_forged = TRUE; + *may_be_forged = true; else { for (ha = hp->h_addr_list; *ha != NULL; ha++) + { if (addrcmp(hp, *ha, &RealHostAddr) == 0) break; + } *may_be_forged = *ha == NULL; -# if _FFR_FREEHOSTENT && NETINET6 +#if NETINET6 freehostent(hp); hp = NULL; -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +#endif /* NETINET6 */ } } @@ -2881,7 +3233,7 @@ getauthinfo(fd, may_be_forged) lalen = sizeof la; switch (RealHostAddr.sa.sa_family) { -# if NETINET +#if NETINET case AF_INET: if (getsockname(fd, &la.sa, &lalen) < 0 || lalen <= 0 || @@ -2893,7 +3245,7 @@ getauthinfo(fd, may_be_forged) port = RealHostAddr.sin.sin_port; /* create ident query */ - (void) snprintf(ibuf, sizeof ibuf, "%d,%d\r\n", + (void) sm_snprintf(ibuf, sizeof ibuf, "%d,%d\r\n", ntohs(RealHostAddr.sin.sin_port), ntohs(la.sin.sin_port)); @@ -2901,19 +3253,35 @@ getauthinfo(fd, may_be_forged) la.sin.sin_port = 0; /* create foreign address */ -# ifdef NO_GETSERVBYNAME +# ifdef NO_GETSERVBYNAME RealHostAddr.sin.sin_port = htons(113); -# else /* NO_GETSERVBYNAME */ - sp = getservbyname("auth", "tcp"); - if (sp != NULL) - RealHostAddr.sin.sin_port = sp->s_port; - else - RealHostAddr.sin.sin_port = htons(113); +# else /* NO_GETSERVBYNAME */ + + /* + ** getservbyname() consumes about 5% of the time + ** when receiving a small message (almost all of the time + ** spent in this routine). + ** Hence we store the port in a static variable + ** to save this time. + ** The portnumber shouldn't change very often... + ** This code makes the assumption that the port number + ** is not 0. + */ + + if (port4 == 0) + { + sp = getservbyname("auth", "tcp"); + if (sp != NULL) + port4 = sp->s_port; + else + port4 = htons(113); + } + RealHostAddr.sin.sin_port = port4; break; -# endif /* NO_GETSERVBYNAME */ -# endif /* NETINET */ +# endif /* NO_GETSERVBYNAME */ +#endif /* NETINET */ -# if NETINET6 +#if NETINET6 case AF_INET6: if (getsockname(fd, &la.sa, &lalen) < 0 || lalen <= 0 || @@ -2925,7 +3293,7 @@ getauthinfo(fd, may_be_forged) port = RealHostAddr.sin6.sin6_port; /* create ident query */ - (void) snprintf(ibuf, sizeof ibuf, "%d,%d\r\n", + (void) sm_snprintf(ibuf, sizeof ibuf, "%d,%d\r\n", ntohs(RealHostAddr.sin6.sin6_port), ntohs(la.sin6.sin6_port)); @@ -2933,17 +3301,21 @@ getauthinfo(fd, may_be_forged) la.sin6.sin6_port = 0; /* create foreign address */ -# ifdef NO_GETSERVBYNAME +# ifdef NO_GETSERVBYNAME RealHostAddr.sin6.sin6_port = htons(113); -# else /* NO_GETSERVBYNAME */ - sp = getservbyname("auth", "tcp"); - if (sp != NULL) - RealHostAddr.sin6.sin6_port = sp->s_port; - else - RealHostAddr.sin6.sin6_port = htons(113); +# else /* NO_GETSERVBYNAME */ + if (port6 == 0) + { + sp = getservbyname("auth", "tcp"); + if (sp != NULL) + port6 = sp->s_port; + else + port6 = htons(113); + } + RealHostAddr.sin6.sin6_port = port6; break; -# endif /* NO_GETSERVBYNAME */ -# endif /* NETINET6 */ +# endif /* NO_GETSERVBYNAME */ +#endif /* NETINET6 */ default: /* no ident info */ goto noident; @@ -2958,24 +3330,22 @@ getauthinfo(fd, may_be_forged) } /* put a timeout around the whole thing */ - ev = setevent(TimeOuts.to_ident, authtimeout, 0); + ev = sm_setevent(TimeOuts.to_ident, authtimeout, 0); /* connect to foreign IDENT server using same address as SMTP socket */ s = socket(la.sa.sa_family, SOCK_STREAM, 0); if (s < 0) { - clrevent(ev); + sm_clrevent(ev); goto noident; } if (bind(s, &la.sa, lalen) < 0 || connect(s, &RealHostAddr.sa, lalen) < 0) - { goto closeident; - } if (tTd(9, 10)) - dprintf("getauthinfo: sent %s", ibuf); + sm_dprintf("getauthinfo: sent %s", ibuf); /* send query */ if (write(s, ibuf, strlen(ibuf)) < 0) @@ -2989,11 +3359,11 @@ getauthinfo(fd, may_be_forged) p += i; nleft -= i; *p = '\0'; - if (strchr(ibuf, '\n') != NULL) + if (strchr(ibuf, '\n') != NULL || nleft <= 0) break; } (void) close(s); - clrevent(ev); + sm_clrevent(ev); if (i < 0 || p == &ibuf[0]) goto noident; @@ -3002,7 +3372,7 @@ getauthinfo(fd, may_be_forged) *++p = '\0'; if (tTd(9, 3)) - dprintf("getauthinfo: got %s\n", ibuf); + sm_dprintf("getauthinfo: got %s\n", ibuf); /* parse result */ p = strchr(ibuf, ':'); @@ -3013,7 +3383,7 @@ getauthinfo(fd, may_be_forged) } while (isascii(*++p) && isspace(*p)) continue; - if (strncasecmp(p, "userid", 6) != 0) + if (sm_strncasecmp(p, "userid", 6) != 0) { /* presumably an error string */ goto noident; @@ -3052,55 +3422,55 @@ getauthinfo(fd, may_be_forged) continue; /* p now points to the authenticated name -- copy carefully */ - if (strncasecmp(ostype, "other", 5) == 0 && + if (sm_strncasecmp(ostype, "other", 5) == 0 && (ostype[5] == ' ' || ostype[5] == '\0')) { - snprintf(hbuf, sizeof hbuf, "IDENT:"); + (void) sm_strlcpy(hbuf, "IDENT:", sizeof hbuf); cleanstrcpy(&hbuf[6], p, MAXNAME); } else cleanstrcpy(hbuf, p, MAXNAME); - i = strlen(hbuf); - snprintf(&hbuf[i], sizeof hbuf - i, "@%s", - RealHostName == NULL ? "localhost" : RealHostName); + len = strlen(hbuf); + (void) sm_strlcpyn(&hbuf[len], sizeof hbuf - len, 2, "@", + RealHostName == NULL ? "localhost" : RealHostName); goto postident; closeident: (void) close(s); - clrevent(ev); + sm_clrevent(ev); noident: /* put back the original incoming port */ switch (RealHostAddr.sa.sa_family) { -# if NETINET +#if NETINET case AF_INET: if (port > 0) RealHostAddr.sin.sin_port = port; break; -# endif /* NETINET */ +#endif /* NETINET */ -# if NETINET6 +#if NETINET6 case AF_INET6: if (port > 0) RealHostAddr.sin6.sin6_port = port; break; -# endif /* NETINET6 */ +#endif /* NETINET6 */ } if (RealHostName == NULL) { if (tTd(9, 1)) - dprintf("getauthinfo: NULL\n"); + sm_dprintf("getauthinfo: NULL\n"); return NULL; } - snprintf(hbuf, sizeof hbuf, "%s", RealHostName); + (void) sm_strlcpy(hbuf, RealHostName, sizeof hbuf); postident: -# if IP_SRCROUTE -# ifndef GET_IPOPT_DST -# define GET_IPOPT_DST(dst) (dst) -# endif /* ! GET_IPOPT_DST */ +#if IP_SRCROUTE +# ifndef GET_IPOPT_DST +# define GET_IPOPT_DST(dst) (dst) +# endif /* ! GET_IPOPT_DST */ /* ** Extract IP source routing information. ** @@ -3118,8 +3488,8 @@ postident: { SOCKOPT_LEN_T ipoptlen; int j; - u_char *q; - u_char *o; + unsigned char *q; + unsigned char *o; int l; struct IPOPTION ipopt; @@ -3129,8 +3499,8 @@ postident: goto noipsr; if (ipoptlen == 0) goto noipsr; - o = (u_char *) ipopt.IP_LIST; - while (o != NULL && o < (u_char *) &ipopt + ipoptlen) + o = (unsigned char *) ipopt.IP_LIST; + while (o != NULL && o < (unsigned char *) &ipopt + ipoptlen) { switch (*o) { @@ -3157,10 +3527,11 @@ postident: p = &hbuf[strlen(hbuf)]; l = sizeof hbuf - (hbuf - p) - 6; - snprintf(p, SPACELEFT(hbuf, p), " [%s@%.*s", - *o == IPOPT_SSRR ? "!" : "", - l > 240 ? 120 : l / 2, - inet_ntoa(GET_IPOPT_DST(ipopt.IP_DST))); + (void) sm_snprintf(p, SPACELEFT(hbuf, p), + " [%s@%.*s", + *o == IPOPT_SSRR ? "!" : "", + l > 240 ? 120 : l / 2, + inet_ntoa(GET_IPOPT_DST(ipopt.IP_DST))); i = strlen(p); p += i; l -= strlen(p); @@ -3174,12 +3545,13 @@ postident: struct in_addr addr; memcpy(&addr, q, sizeof(addr)); - snprintf(p, SPACELEFT(hbuf, p), - "%c%.*s", - j != 0 ? '@' : ':', - l > 240 ? 120 : - j == 0 ? l : l / 2, - inet_ntoa(addr)); + (void) sm_snprintf(p, + SPACELEFT(hbuf, p), + "%c%.*s", + j != 0 ? '@' : ':', + l > 240 ? 120 : + j == 0 ? l : l / 2, + inet_ntoa(addr)); i = strlen(p); p += i; l -= i + 1; @@ -3194,48 +3566,50 @@ postident: break; } } - snprintf(p, SPACELEFT(hbuf, p), "]"); + (void) sm_snprintf(p, SPACELEFT(hbuf, p), "]"); goto postipsr; } noipsr: -# endif /* IP_SRCROUTE */ +#endif /* IP_SRCROUTE */ if (RealHostName != NULL && RealHostName[0] != '[') { p = &hbuf[strlen(hbuf)]; - (void) snprintf(p, SPACELEFT(hbuf, p), " [%.100s]", - anynet_ntoa(&RealHostAddr)); + (void) sm_snprintf(p, SPACELEFT(hbuf, p), " [%.100s]", + anynet_ntoa(&RealHostAddr)); } if (*may_be_forged) { p = &hbuf[strlen(hbuf)]; - (void) snprintf(p, SPACELEFT(hbuf, p), " (may be forged)"); + (void) sm_strlcpy(p, " (may be forged)", SPACELEFT(hbuf, p)); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{client_resolve}"), "FORGED"); } -# if IP_SRCROUTE +#if IP_SRCROUTE postipsr: -# endif /* IP_SRCROUTE */ - if (tTd(9, 1)) - dprintf("getauthinfo: %s\n", hbuf); +#endif /* IP_SRCROUTE */ /* put back the original incoming port */ switch (RealHostAddr.sa.sa_family) { -# if NETINET +#if NETINET case AF_INET: if (port > 0) RealHostAddr.sin.sin_port = port; break; -# endif /* NETINET */ +#endif /* NETINET */ -# if NETINET6 +#if NETINET6 case AF_INET6: if (port > 0) RealHostAddr.sin6.sin6_port = port; break; -# endif /* NETINET6 */ +#endif /* NETINET6 */ } + if (tTd(9, 1)) + sm_dprintf("getauthinfo: %s\n", hbuf); return hbuf; } /* @@ -3268,34 +3642,39 @@ host_map_lookup(map, name, av, statp) int *statp; { register struct hostent *hp; -# if NETINET +#if NETINET struct in_addr in_addr; -# endif /* NETINET */ -# if NETINET6 +#endif /* NETINET */ +#if NETINET6 struct in6_addr in6_addr; -# endif /* NETINET6 */ +#endif /* NETINET6 */ char *cp, *ans = NULL; register STAB *s; + time_t now; +#if NAMED_BIND + time_t SM_NONVOLATILE retrans = 0; + int SM_NONVOLATILE retry = 0; +#endif /* NAMED_BIND */ char hbuf[MAXNAME + 1]; /* ** See if we have already looked up this name. If so, just - ** return it. + ** return it (unless expired). */ + now = curtime(); s = stab(name, ST_NAMECANON, ST_ENTER); - if (bitset(NCF_VALID, s->s_namecanon.nc_flags)) + if (bitset(NCF_VALID, s->s_namecanon.nc_flags) && + s->s_namecanon.nc_exp >= now) { if (tTd(9, 1)) - dprintf("host_map_lookup(%s) => CACHE %s\n", - name, - s->s_namecanon.nc_cname == NULL + sm_dprintf("host_map_lookup(%s) => CACHE %s\n", + name, + s->s_namecanon.nc_cname == NULL ? "NULL" : s->s_namecanon.nc_cname); errno = s->s_namecanon.nc_errno; -# if NAMED_BIND SM_SET_H_ERRNO(s->s_namecanon.nc_herrno); -# endif /* NAMED_BIND */ *statp = s->s_namecanon.nc_stat; if (*statp == EX_TEMPFAIL) { @@ -3333,7 +3712,7 @@ host_map_lookup(map, name, av, statp) bitset(MF_DEFER, map->map_mflags)) { if (tTd(9, 1)) - dprintf("host_map_lookup(%s) => DEFERRED\n", name); + sm_dprintf("host_map_lookup(%s) => DEFERRED\n", name); *statp = EX_TEMPFAIL; return NULL; } @@ -3346,47 +3725,83 @@ host_map_lookup(map, name, av, statp) */ if (tTd(9, 1)) - dprintf("host_map_lookup(%s) => ", name); + sm_dprintf("host_map_lookup(%s) => ", name); +#if NAMED_BIND + if (map->map_timeout > 0) + { + retrans = _res.retrans; + _res.retrans = map->map_timeout; + } + if (map->map_retry > 0) + { + retry = _res.retry; + _res.retry = map->map_retry; + } +#endif /* NAMED_BIND */ + + /* set default TTL */ + s->s_namecanon.nc_exp = now + SM_DEFAULT_TTL; if (*name != '[') { - snprintf(hbuf, sizeof hbuf, "%s", name); - if (getcanonname(hbuf, sizeof hbuf - 1, !HasWildcardMX)) + int ttl; + + (void) sm_strlcpy(hbuf, name, sizeof hbuf); + if (getcanonname(hbuf, sizeof hbuf - 1, !HasWildcardMX, &ttl)) + { ans = hbuf; + if (ttl > 0) + s->s_namecanon.nc_exp = now + SM_MIN(ttl, + SM_DEFAULT_TTL); + } } else { if ((cp = strchr(name, ']')) == NULL) { if (tTd(9, 1)) - dprintf("FAILED\n"); + sm_dprintf("FAILED\n"); return NULL; } *cp = '\0'; hp = NULL; -# if NETINET +#if NETINET if ((in_addr.s_addr = inet_addr(&name[1])) != INADDR_NONE) hp = sm_gethostbyaddr((char *)&in_addr, INADDRSZ, AF_INET); -# endif /* NETINET */ -# if NETINET6 +#endif /* NETINET */ +#if NETINET6 if (hp == NULL && - inet_pton(AF_INET6, &name[1], &in6_addr) == 1) + anynet_pton(AF_INET6, &name[1], &in6_addr) == 1) hp = sm_gethostbyaddr((char *)&in6_addr, IN6ADDRSZ, AF_INET6); -# endif /* NETINET6 */ +#endif /* NETINET6 */ *cp = ']'; if (hp != NULL) { /* found a match -- copy out */ - ans = denlstring((char *) hp->h_name, TRUE, TRUE); -# if _FFR_FREEHOSTENT && NETINET6 + ans = denlstring((char *) hp->h_name, true, true); +#if NETINET6 + if (ans == hp->h_name) + { + static char n[MAXNAME + 1]; + + /* hp->h_name is about to disappear */ + (void) sm_strlcpy(n, ans, sizeof n); + ans = n; + } freehostent(hp); hp = NULL; -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +#endif /* NETINET6 */ } } +#if NAMED_BIND + if (map->map_timeout > 0) + _res.retrans = retrans; + if (map->map_retry > 0) + _res.retry = retry; +#endif /* NAMED_BIND */ s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */ @@ -3394,23 +3809,25 @@ host_map_lookup(map, name, av, statp) if (ans != NULL) { s->s_namecanon.nc_stat = *statp = EX_OK; - s->s_namecanon.nc_cname = newstr(ans); + if (s->s_namecanon.nc_cname != NULL) + sm_free(s->s_namecanon.nc_cname); + s->s_namecanon.nc_cname = sm_strdup_x(ans); if (bitset(MF_MATCHONLY, map->map_mflags)) cp = map_rewrite(map, name, strlen(name), NULL); else cp = map_rewrite(map, ans, strlen(ans), av); if (tTd(9, 1)) - dprintf("FOUND %s\n", ans); + sm_dprintf("FOUND %s\n", ans); return cp; } /* No match found */ s->s_namecanon.nc_errno = errno; -# if NAMED_BIND +#if NAMED_BIND s->s_namecanon.nc_herrno = h_errno; if (tTd(9, 1)) - dprintf("FAIL (%d)\n", h_errno); + sm_dprintf("FAIL (%d)\n", h_errno); switch (h_errno) { case TRY_AGAIN: @@ -3436,152 +3853,23 @@ host_map_lookup(map, name, av, statp) *statp = EX_UNAVAILABLE; break; } -# else /* NAMED_BIND */ +#else /* NAMED_BIND */ if (tTd(9, 1)) - dprintf("FAIL\n"); + sm_dprintf("FAIL\n"); *statp = EX_NOHOST; -# endif /* NAMED_BIND */ +#endif /* NAMED_BIND */ s->s_namecanon.nc_stat = *statp; return NULL; } -#else /* DAEMON */ -/* code for systems without sophisticated networking */ - -/* -** MYHOSTNAME -- stub version for case of no daemon code. -** -** Can't convert to upper case here because might be a UUCP name. -** -** Mark, you can change this to be anything you want...... -*/ - -char ** -myhostname(hostbuf, size) - char hostbuf[]; - int size; -{ - register FILE *f; - - hostbuf[0] = '\0'; - f = fopen("/usr/include/whoami", "r"); - if (f != NULL) - { - (void) fgets(hostbuf, size, f); - fixcrlf(hostbuf, TRUE); - (void) fclose(f); - } - if (hostbuf[0] == '\0') - (void) strlcpy(hostbuf, "localhost", size); - return NULL; -} /* -** GETAUTHINFO -- get the real host name associated with a file descriptor -** -** Parameters: -** fd -- the descriptor -** may_be_forged -- an outage that is set to TRUE if the -** forward lookup of RealHostName does not match -** RealHostAddr; set to FALSE if they do match. -** -** Returns: -** The host name associated with this descriptor, if it can -** be determined. -** NULL otherwise. -** -** Side Effects: -** none -*/ - -char * -getauthinfo(fd, may_be_forged) - int fd; - bool *may_be_forged; -{ - *may_be_forged = FALSE; - return NULL; -} -/* -** HOST_MAP_LOOKUP -- turn a hostname into canonical form +** HOST_MAP_INIT -- initialize host class structures ** ** Parameters: -** map -- a pointer to the database map. -** name -- a buffer containing a hostname. -** avp -- a pointer to a (cf file defined) argument vector. -** statp -- an exit status (out parameter). +** map -- a pointer to this map. +** args -- argument string. ** ** Returns: -** mapped host name -** FALSE otherwise. -** -** Side Effects: -** Looks up the host specified in name. If it is not -** the canonical name for that host, replace it with -** the canonical name. If the name is unknown, or it -** is already the canonical name, leave it unchanged. -*/ - -/*ARGSUSED*/ -char * -host_map_lookup(map, name, avp, statp) - MAP *map; - char *name; - char **avp; - char *statp; -{ - register struct hostent *hp = NULL; - char *cp; - - hp = sm_gethostbyname(name, InetMode); - if (hp == NULL && InetMode != AF_INET) - hp = sm_gethostbyname(name, AF_INET); - if (hp == NULL) - { -# if NAMED_BIND - if (tTd(9, 1)) - dprintf("FAIL (%d)\n", h_errno); - switch (h_errno) - { - case TRY_AGAIN: - if (UseNameServer) - { - CurEnv->e_status = "4.4.3"; - message("851 %s: Name server timeout", - shortenstring(name, 33)); - } - *statp = EX_TEMPFAIL; - break; - - case HOST_NOT_FOUND: - case NO_DATA: - *statp = EX_NOHOST; - break; - - case NO_RECOVERY: - *statp = EX_SOFTWARE; - break; - - default: - *statp = EX_UNAVAILABLE; - break; - } -#else /* NAMED_BIND */ - *statp = EX_NOHOST; -#endif /* NAMED_BIND */ - return NULL; - } - if (bitset(MF_MATCHONLY, map->map_mflags)) - cp = map_rewrite(map, name, strlen(name), NULL); - else - cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), avp); -# if _FFR_FREEHOSTENT && NETINET6 - freehostent(hp); -# endif /* _FFR_FREEHOSTENT && NETINET6 */ - return cp; -} - -#endif /* DAEMON */ -/* -** HOST_MAP_INIT -- initialize host class structures +** true. */ bool @@ -3622,6 +3910,27 @@ host_map_init(map, args) case 'D': map->map_mflags |= MF_DEFER; break; + + case 'd': + { + char *h; + + while (isascii(*++p) && isspace(*p)) + continue; + h = strchr(p, ' '); + if (h != NULL) + *h = '\0'; + map->map_timeout = convtime(p, 's'); + if (h != NULL) + *h = ' '; + } + break; + + case 'r': + while (isascii(*++p) && isspace(*p)) + continue; + map->map_retry = atoi(p); + break; } while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; @@ -3632,7 +3941,7 @@ host_map_init(map, args) map->map_app = newstr(map->map_app); if (map->map_tapp != NULL) map->map_tapp = newstr(map->map_tapp); - return TRUE; + return true; } #if NETINET6 @@ -3647,6 +3956,7 @@ host_map_init(map, args) ** Returns: ** A printable version of that structure. */ + char * anynet_ntop(s6a, dst, dst_len) struct in6_addr *s6a; @@ -3660,9 +3970,54 @@ anynet_ntop(s6a, dst, dst_len) &s6a->s6_addr[IN6ADDRSZ - INADDRSZ], dst, dst_len); else + { + char *d; + size_t sz; + + /* Save pointer to beginning of string */ + d = dst; + + /* Add IPv6: protocol tag */ + sz = sm_strlcpy(dst, "IPv6:", dst_len); + if (sz >= dst_len) + return NULL; + dst += sz; + dst_len -= sz; ap = (char *) inet_ntop(AF_INET6, s6a, dst, dst_len); + + /* Restore pointer to beginning of string */ + if (ap != NULL) + ap = d; + } return ap; } + +/* +** ANYNET_PTON -- convert printed form to network address. +** +** Wrapper for inet_pton() which handles IPv6: labels. +** +** Parameters: +** family -- address family +** src -- string +** dst -- destination address structure +** +** Returns: +** 1 if the address was valid +** 0 if the address wasn't parseable +** -1 if error +*/ + +int +anynet_pton(family, src, dst) + int family; + const char *src; + void *dst; +{ + if (family == AF_INET6 && sm_strncasecmp(src, "IPv6:", 5) == 0) + src += 5; + return inet_pton(family, src, dst); +} #endif /* NETINET6 */ /* ** ANYNET_NTOA -- convert a network address to printable form. @@ -3700,10 +4055,10 @@ anynet_ntoa(sap) # if NETUNIX case AF_UNIX: if (sap->sunix.sun_path[0] != '\0') - snprintf(buf, sizeof buf, "[UNIX: %.64s]", - sap->sunix.sun_path); + (void) sm_snprintf(buf, sizeof buf, "[UNIX: %.64s]", + sap->sunix.sun_path); else - snprintf(buf, sizeof buf, "[UNIX: localhost]"); + (void) sm_strlcpy(buf, "[UNIX: localhost]", sizeof buf); return buf; # endif /* NETUNIX */ @@ -3722,8 +4077,8 @@ anynet_ntoa(sap) # if NETLINK case AF_LINK: - snprintf(buf, sizeof buf, "[LINK: %s]", - link_ntoa((struct sockaddr_dl *) &sap->sa)); + (void) sm_snprintf(buf, sizeof buf, "[LINK: %s]", + link_ntoa((struct sockaddr_dl *) &sap->sa)); return buf; # endif /* NETLINK */ default: @@ -3733,12 +4088,13 @@ anynet_ntoa(sap) } /* unknown family -- just dump bytes */ - (void) snprintf(buf, sizeof buf, "Family %d: ", sap->sa.sa_family); + (void) sm_snprintf(buf, sizeof buf, "Family %d: ", sap->sa.sa_family); bp = &buf[strlen(buf)]; ap = sap->sa.sa_data; for (l = sizeof sap->sa.sa_data; --l >= 0; ) { - (void) snprintf(bp, SPACELEFT(buf, bp), "%02x:", *ap++ & 0377); + (void) sm_snprintf(bp, SPACELEFT(buf, bp), "%02x:", + *ap++ & 0377); bp += 3; } *--bp = '\0'; @@ -3781,24 +4137,21 @@ hostnamebyanyaddr(sap) # if NETINET case AF_INET: hp = sm_gethostbyaddr((char *) &sap->sin.sin_addr, - INADDRSZ, - AF_INET); + INADDRSZ, AF_INET); break; # endif /* NETINET */ # if NETINET6 case AF_INET6: hp = sm_gethostbyaddr((char *) &sap->sin6.sin6_addr, - IN6ADDRSZ, - AF_INET6); + IN6ADDRSZ, AF_INET6); break; # endif /* NETINET6 */ # if NETISO case AF_ISO: hp = sm_gethostbyaddr((char *) &sap->siso.siso_addr, - sizeof sap->siso.siso_addr, - AF_ISO); + sizeof sap->siso.siso_addr, AF_ISO); break; # endif /* NETISO */ @@ -3809,9 +4162,8 @@ hostnamebyanyaddr(sap) # endif /* NETUNIX */ default: - hp = sm_gethostbyaddr(sap->sa.sa_data, - sizeof sap->sa.sa_data, - sap->sa.sa_family); + hp = sm_gethostbyaddr(sap->sa.sa_data, sizeof sap->sa.sa_data, + sap->sa.sa_family); break; } @@ -3831,31 +4183,29 @@ hostnamebyanyaddr(sap) { char *name; - name = denlstring((char *) hp->h_name, TRUE, TRUE); - -# if _FFR_FREEHOSTENT && NETINET6 + name = denlstring((char *) hp->h_name, true, true); +# if NETINET6 if (name == hp->h_name) { static char n[MAXNAME + 1]; /* Copy the string, hp->h_name is about to disappear */ - strlcpy(n, name, sizeof n); + (void) sm_strlcpy(n, name, sizeof n); name = n; } - freehostent(hp); -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +# endif /* NETINET6 */ return name; } # endif /* NETINET || NETINET6 */ -# if _FFR_FREEHOSTENT && NETINET6 +# if NETINET6 if (hp != NULL) { freehostent(hp); hp = NULL; } -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +# endif /* NETINET6 */ # if NETUNIX if (sap->sa.sa_family == AF_UNIX && sap->sunix.sun_path[0] == '\0') @@ -3864,7 +4214,8 @@ hostnamebyanyaddr(sap) { static char buf[203]; - (void) snprintf(buf, sizeof buf, "[%.200s]", anynet_ntoa(sap)); + (void) sm_snprintf(buf, sizeof buf, "[%.200s]", + anynet_ntoa(sap)); return buf; } } diff --git a/gnu/usr.sbin/sendmail/sendmail/deliver.c b/gnu/usr.sbin/sendmail/sendmail/deliver.c index 02f6ad747d5..6bd1e8143fd 100644 --- a/gnu/usr.sbin/sendmail/sendmail/deliver.c +++ b/gnu/usr.sbin/sendmail/sendmail/deliver.c @@ -11,34 +11,35 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: deliver.c,v 8.600.2.1.2.86 2001/07/20 21:52:55 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +#include <sys/time.h> +SM_RCSID("@(#)$Sendmail: deliver.c,v 8.899 2001/09/08 01:21:09 gshapiro Exp $") #if HASSETUSERCONTEXT # include <login_cap.h> #endif /* HASSETUSERCONTEXT */ -#if STARTTLS || (SASL && SFIO) +#if STARTTLS || SASL # include "sfsasl.h" -#endif /* STARTTLS || (SASL && SFIO) */ +#endif /* STARTTLS || SASL */ +void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int, bool)); static int deliver __P((ENVELOPE *, ADDRESS *)); static void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int)); static void mailfiletimeout __P((void)); -static void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int, bool)); static int parse_hostsignature __P((char *, char **, MAILER *)); static void sendenvelope __P((ENVELOPE *, int)); -static char *hostsignature __P((MAILER *, char *)); +extern MCI *mci_new __P((SM_RPOOL_T *)); +static int coloncmp __P((const char *, const char *)); -#if SMTP -# if STARTTLS +#if STARTTLS static int starttls __P((MAILER *, MCI *, ENVELOPE *)); -# endif /* STARTTLS */ -#endif /* SMTP */ +static int endtlsclt __P((MCI *)); +#endif /* STARTTLS */ +# if STARTTLS || SASL +static bool iscltflgset __P((ENVELOPE *, int)); +# endif /* STARTTLS || SASL */ /* ** SENDALL -- actually send all the messages. @@ -70,7 +71,7 @@ sendall(e, mode) register ENVELOPE *ee; ENVELOPE *splitenv = NULL; int oldverbose = Verbose; - bool somedeliveries = FALSE, expensive = FALSE; + bool somedeliveries = false, expensive = false; pid_t pid; /* @@ -81,11 +82,13 @@ sendall(e, mode) if (bitset(EF_DISCARD, e->e_flags)) { if (tTd(13, 1)) - dprintf("sendall: discarding id %s\n", e->e_id); + sm_dprintf("sendall: discarding id %s\n", e->e_id); e->e_flags |= EF_CLRQUEUE; - if (LogLevel > 4) + if (LogLevel > 9) + logundelrcpts(e, "discarded", 9, true); + else if (LogLevel > 4) sm_syslog(LOG_INFO, e->e_id, "discarded"); - markstats(e, NULL, TRUE); + markstats(e, NULL, true); return; } @@ -114,13 +117,13 @@ sendall(e, mode) if (tTd(13, 1)) { - dprintf("\n===== SENDALL: mode %c, id %s, e_from ", + sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ", mode, e->e_id); - printaddr(&e->e_from, FALSE); - dprintf("\te_flags = "); + printaddr(&e->e_from, false); + sm_dprintf("\te_flags = "); printenvflags(e); - dprintf("sendqueue:\n"); - printaddr(e->e_sendqueue, TRUE); + sm_dprintf("sendqueue:\n"); + printaddr(e->e_sendqueue, true); } /* @@ -144,9 +147,7 @@ sendall(e, mode) recip = "(nobody)"; errno = 0; -#if QUEUE - queueup(e, mode == SM_QUEUE || mode == SM_DEFER); -#endif /* QUEUE */ + queueup(e, WILL_BE_QUEUED(mode), false); e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE; ExitStat = EX_UNAVAILABLE; syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s", @@ -178,8 +179,8 @@ sendall(e, mode) { if (tTd(13, 5)) { - dprintf("sendall: QS_SENDER "); - printaddr(&e->e_from, FALSE); + sm_dprintf("sendall: QS_SENDER "); + printaddr(&e->e_from, false); } e->e_from.q_state = QS_SENDER; (void) recipient(&e->e_from, &e->e_sendqueue, 0, e); @@ -209,8 +210,8 @@ sendall(e, mode) if (tTd(13, 25)) { - dprintf("\nAfter first owner pass, sendq =\n"); - printaddr(e->e_sendqueue, TRUE); + sm_dprintf("\nAfter first owner pass, sendq =\n"); + printaddr(e->e_sendqueue, true); } owner = ""; @@ -218,8 +219,8 @@ sendall(e, mode) while (owner != NULL && otherowners > 0) { if (tTd(13, 28)) - dprintf("owner = \"%s\", otherowners = %d\n", - owner, otherowners); + sm_dprintf("owner = \"%s\", otherowners = %d\n", + owner, otherowners); owner = NULL; otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0; @@ -227,19 +228,19 @@ sendall(e, mode) { if (tTd(13, 30)) { - dprintf("Checking "); - printaddr(q, FALSE); + sm_dprintf("Checking "); + printaddr(q, false); } if (QS_IS_DEAD(q->q_state)) { if (tTd(13, 30)) - dprintf(" ... QS_IS_DEAD\n"); + sm_dprintf(" ... QS_IS_DEAD\n"); continue; } if (tTd(13, 29) && !tTd(13, 30)) { - dprintf("Checking "); - printaddr(q, FALSE); + sm_dprintf("Checking "); + printaddr(q, false); } if (q->q_owner != NULL) @@ -247,8 +248,8 @@ sendall(e, mode) if (owner == NULL) { if (tTd(13, 40)) - dprintf(" ... First owner = \"%s\"\n", - q->q_owner); + sm_dprintf(" ... First owner = \"%s\"\n", + q->q_owner); owner = q->q_owner; } else if (owner != q->q_owner) @@ -256,8 +257,8 @@ sendall(e, mode) if (strcmp(owner, q->q_owner) == 0) { if (tTd(13, 40)) - dprintf(" ... Same owner = \"%s\"\n", - owner); + sm_dprintf(" ... Same owner = \"%s\"\n", + owner); /* make future comparisons cheap */ q->q_owner = owner; @@ -265,27 +266,27 @@ sendall(e, mode) else { if (tTd(13, 40)) - dprintf(" ... Another owner \"%s\"\n", - q->q_owner); + sm_dprintf(" ... Another owner \"%s\"\n", + q->q_owner); otherowners++; } owner = q->q_owner; } else if (tTd(13, 40)) - dprintf(" ... Same owner = \"%s\"\n", - owner); + sm_dprintf(" ... Same owner = \"%s\"\n", + owner); } else { if (tTd(13, 40)) - dprintf(" ... Null owner\n"); + sm_dprintf(" ... Null owner\n"); otherowners++; } if (QS_IS_BADADDR(q->q_state)) { if (tTd(13, 30)) - dprintf(" ... QS_IS_BADADDR\n"); + sm_dprintf(" ... QS_IS_BADADDR\n"); continue; } @@ -302,28 +303,26 @@ sendall(e, mode) if (FallBackMX != NULL && !wordinclass(FallBackMX, 'w') && mode != SM_VERIFY && - (strcmp(m->m_mailer, "[IPC]") == 0 || - strcmp(m->m_mailer, "[TCP]") == 0) && + strcmp(m->m_mailer, "[IPC]") == 0 && m->m_argv[0] != NULL && - (strcmp(m->m_argv[0], "TCP") == 0 || - strcmp(m->m_argv[0], "IPC") == 0)) + strcmp(m->m_argv[0], "TCP")) { int len; char *p; if (tTd(13, 30)) - dprintf(" ... FallBackMX\n"); + sm_dprintf(" ... FallBackMX\n"); - len = strlen(FallBackMX) + 3; - p = xalloc(len); - snprintf(p, len, "[%s]", FallBackMX); + len = strlen(FallBackMX) + 1; + p = sm_rpool_malloc_x(e->e_rpool, len); + (void) sm_strlcpy(p, FallBackMX, len); q->q_state = QS_OK; q->q_host = p; } else { if (tTd(13, 30)) - dprintf(" ... QS_IS_QUEUEUP\n"); + sm_dprintf(" ... QS_IS_QUEUEUP\n"); continue; } } @@ -341,9 +340,9 @@ sendall(e, mode) bitnset(M_EXPENSIVE, q->q_mailer->m_flags)) { if (tTd(13, 30)) - dprintf(" ... expensive\n"); + sm_dprintf(" ... expensive\n"); q->q_state = QS_QUEUEUP; - expensive = TRUE; + expensive = true; } else if (bitnset(M_HOLD, q->q_mailer->m_flags) && QueueLimitId == NULL && @@ -351,15 +350,15 @@ sendall(e, mode) QueueLimitRecipient == NULL) { if (tTd(13, 30)) - dprintf(" ... hold\n"); + sm_dprintf(" ... hold\n"); q->q_state = QS_QUEUEUP; - expensive = TRUE; + expensive = true; } else { if (tTd(13, 30)) - dprintf(" ... deliverable\n"); - somedeliveries = TRUE; + sm_dprintf(" ... deliverable\n"); + somedeliveries = true; } } @@ -369,32 +368,37 @@ sendall(e, mode) ** Split this envelope into two. */ - ee = (ENVELOPE *) xalloc(sizeof *ee); - *ee = *e; + ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, + sizeof *ee); + STRUCTCOPY(*e, *ee); ee->e_message = NULL; ee->e_id = NULL; assign_queueid(ee); if (tTd(13, 1)) - dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n", - e->e_id, ee->e_id, owner, otherowners); - - ee->e_header = copyheader(e->e_header); - ee->e_sendqueue = copyqueue(e->e_sendqueue); - ee->e_errorqueue = copyqueue(e->e_errorqueue); + sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n", + e->e_id, ee->e_id, owner, + otherowners); + + ee->e_header = copyheader(e->e_header, ee->e_rpool); + ee->e_sendqueue = copyqueue(e->e_sendqueue, + ee->e_rpool); + ee->e_errorqueue = copyqueue(e->e_errorqueue, + ee->e_rpool); ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM); ee->e_flags |= EF_NORECEIPT; - setsender(owner, ee, NULL, '\0', TRUE); + setsender(owner, ee, NULL, '\0', true); if (tTd(13, 5)) { - dprintf("sendall(split): QS_SENDER "); - printaddr(&ee->e_from, FALSE); + sm_dprintf("sendall(split): QS_SENDER "); + printaddr(&ee->e_from, false); } ee->e_from.q_state = QS_SENDER; ee->e_dfp = NULL; ee->e_lockfp = NULL; ee->e_xfp = NULL; - ee->e_queuedir = e->e_queuedir; + ee->e_qgrp = e->e_qgrp; + ee->e_qdir = e->e_qdir; ee->e_errormode = EM_MAIL; ee->e_sibling = splitenv; ee->e_statmsg = NULL; @@ -406,8 +410,8 @@ sendall(e, mode) { q->q_state = QS_CLONED; if (tTd(13, 6)) - dprintf("\t... stripping %s from original envelope\n", - q->q_paddr); + sm_dprintf("\t... stripping %s from original envelope\n", + q->q_paddr); } } for (q = ee->e_sendqueue; q != NULL; q = q->q_next) @@ -416,8 +420,8 @@ sendall(e, mode) { q->q_state = QS_CLONED; if (tTd(13, 6)) - dprintf("\t... dropping %s from cloned envelope\n", - q->q_paddr); + sm_dprintf("\t... dropping %s from cloned envelope\n", + q->q_paddr); } else { @@ -425,8 +429,8 @@ sendall(e, mode) q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); q->q_flags |= DefaultNotify & ~QPINGONSUCCESS; if (tTd(13, 6)) - dprintf("\t... moving %s to cloned envelope\n", - q->q_paddr); + sm_dprintf("\t... moving %s to cloned envelope\n", + q->q_paddr); } } @@ -441,26 +445,26 @@ sendall(e, mode) */ if (e->e_xfp != NULL) - ee->e_xfp = bfdup(e->e_xfp); + ee->e_xfp = sm_io_dup(e->e_xfp); /* failed to dup e->e_xfp, start a new transcript */ if (ee->e_xfp == NULL) openxscript(ee); if (mode != SM_VERIFY && LogLevel > 4) - sm_syslog(LOG_INFO, ee->e_id, - "clone %s, owner=%s", - e->e_id, owner); + sm_syslog(LOG_INFO, e->e_id, + "%s: clone: owner=%s", + ee->e_id, owner); } } if (owner != NULL) { - setsender(owner, e, NULL, '\0', TRUE); + setsender(owner, e, NULL, '\0', true); if (tTd(13, 5)) { - dprintf("sendall(owner): QS_SENDER "); - printaddr(&e->e_from, FALSE); + sm_dprintf("sendall(owner): QS_SENDER "); + printaddr(&e->e_from, false); } e->e_from.q_state = QS_SENDER; e->e_errormode = EM_MAIL; @@ -469,14 +473,15 @@ sendall(e, mode) } /* if nothing to be delivered, just queue up everything */ - if (!somedeliveries && mode != SM_QUEUE && mode != SM_DEFER && + if (!somedeliveries && !WILL_BE_QUEUED(mode) && mode != SM_VERIFY) { - time_t now = curtime(); + time_t now; if (tTd(13, 29)) - dprintf("No deliveries: auto-queuing\n"); + sm_dprintf("No deliveries: auto-queuing\n"); mode = SM_QUEUE; + now = curtime(); /* treat this as a delivery in terms of counting tries */ e->e_dtime = now; @@ -490,11 +495,12 @@ sendall(e, mode) } } -#if QUEUE - if ((mode == SM_QUEUE || mode == SM_DEFER || mode == SM_FORK || - (mode != SM_VERIFY && SuperSafe)) && + if ((WILL_BE_QUEUED(mode) || mode == SM_FORK || + (mode != SM_VERIFY && SuperSafe == SAFE_REALLY)) && (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL)) { + bool msync; + /* ** Be sure everything is instantiated in the queue. ** Split envelopes first in case the machine crashes. @@ -502,11 +508,16 @@ sendall(e, mode) ** recipients. */ +#if !HASFLOCK + msync = false; +#else /* !HASFLOCK */ + msync = mode == SM_FORK; +#endif /* !HASFLOCK */ + for (ee = splitenv; ee != NULL; ee = ee->e_sibling) - queueup(ee, mode == SM_QUEUE || mode == SM_DEFER); - queueup(e, mode == SM_QUEUE || mode == SM_DEFER); + queueup(ee, WILL_BE_QUEUED(mode), msync); + queueup(e, WILL_BE_QUEUED(mode), msync); } -#endif /* QUEUE */ if (tTd(62, 10)) checkfds("after envelope splitting"); @@ -517,20 +528,20 @@ sendall(e, mode) if (tTd(13, 20)) { - dprintf("sendall: final mode = %c\n", mode); + sm_dprintf("sendall: final mode = %c\n", mode); if (tTd(13, 21)) { - dprintf("\n================ Final Send Queue(s) =====================\n"); - dprintf("\n *** Envelope %s, e_from=%s ***\n", - e->e_id, e->e_from.q_paddr); - printaddr(e->e_sendqueue, TRUE); + sm_dprintf("\n================ Final Send Queue(s) =====================\n"); + sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", + e->e_id, e->e_from.q_paddr); + printaddr(e->e_sendqueue, true); for (ee = splitenv; ee != NULL; ee = ee->e_sibling) { - dprintf("\n *** Envelope %s, e_from=%s ***\n", - ee->e_id, ee->e_from.q_paddr); - printaddr(ee->e_sendqueue, TRUE); + sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", + ee->e_id, ee->e_from.q_paddr); + printaddr(ee->e_sendqueue, true); } - dprintf("==========================================================\n\n"); + sm_dprintf("==========================================================\n\n"); } } switch (mode) @@ -546,18 +557,18 @@ sendall(e, mode) #endif /* HASFLOCK */ if (e->e_nrcpts > 0) e->e_flags |= EF_INQUEUE; - dropenvelope(e, splitenv != NULL); + dropenvelope(e, splitenv != NULL, true); for (ee = splitenv; ee != NULL; ee = ee->e_sibling) { if (ee->e_nrcpts > 0) ee->e_flags |= EF_INQUEUE; - dropenvelope(ee, FALSE); + dropenvelope(ee, false, true); } return; case SM_FORK: if (e->e_xfp != NULL) - (void) fflush(e->e_xfp); + (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); #if !HASFLOCK /* @@ -573,7 +584,7 @@ sendall(e, mode) /* now drop the envelope in the parent */ e->e_flags |= EF_INQUEUE; - dropenvelope(e, splitenv != NULL); + dropenvelope(e, splitenv != NULL, false); /* arrange to reacquire lock after fork */ e->e_id = qid; @@ -586,7 +597,7 @@ sendall(e, mode) /* drop envelope in parent */ ee->e_flags |= EF_INQUEUE; - dropenvelope(ee, FALSE); + dropenvelope(ee, false, false); /* and save qid for reacquisition */ ee->e_id = qid; @@ -602,7 +613,7 @@ sendall(e, mode) ** them if necessary. */ - closemaps(); + closemaps(false); pid = fork(); if (pid < 0) @@ -624,13 +635,13 @@ sendall(e, mode) /* close any random open files in the envelope */ closexscript(e); if (e->e_dfp != NULL) - (void) bfclose(e->e_dfp); + (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); e->e_dfp = NULL; e->e_flags &= ~EF_HAS_DF; /* can't call unlockqueue to avoid unlink of xfp */ if (e->e_lockfp != NULL) - (void) fclose(e->e_lockfp); + (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); else syserr("%s: sendall: null lockfp", e->e_id); e->e_lockfp = NULL; @@ -646,17 +657,25 @@ sendall(e, mode) /* Reset global flags */ RestartRequest = NULL; + RestartWorkGroup = false; ShutdownRequest = NULL; PendingSignal = 0; /* + ** Initialize exception stack and default exception + ** handler for child process. + */ + + sm_exc_newthread(fatal_error); + + /* ** Since we have accepted responsbility for the message, ** change the SIGTERM handler. intsig() (the old handler) ** would remove the envelope if this was a command line ** message submission. */ - (void) setsignal(SIGTERM, SIG_DFL); + (void) sm_signal(SIGTERM, SIG_DFL); /* double fork to avoid zombies */ pid = fork(); @@ -664,6 +683,8 @@ sendall(e, mode) exit(EX_OK); save_errno = errno; + CurrentPid = getpid(); + /* be sure we are immune from the terminal */ disconnect(2, e); clearstats(); @@ -678,11 +699,11 @@ sendall(e, mode) #else /* HASFLOCK */ e->e_id = NULL; #endif /* HASFLOCK */ - finis(TRUE, ExitStat); + finis(true, ExitStat); } /* be sure to give error messages in child */ - QuickAbort = FALSE; + QuickAbort = false; /* ** Close any cached connections. @@ -694,7 +715,7 @@ sendall(e, mode) ** message. */ - mci_flush(FALSE, NULL); + mci_flush(false, NULL); #if HASFLOCK break; @@ -708,31 +729,31 @@ sendall(e, mode) { ENVELOPE *sibling = ee->e_sibling; - (void) dowork(ee->e_queuedir, ee->e_id, - FALSE, FALSE, ee); + (void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id, + false, false, ee); ee->e_sibling = sibling; } - (void) dowork(e->e_queuedir, e->e_id, - FALSE, FALSE, e); - finis(TRUE, ExitStat); + (void) dowork(e->e_qgrp, e->e_qdir, e->e_id, + false, false, e); + finis(true, ExitStat); #endif /* HASFLOCK */ } sendenvelope(e, mode); - dropenvelope(e, TRUE); + dropenvelope(e, true, true); for (ee = splitenv; ee != NULL; ee = ee->e_sibling) { CurEnv = ee; if (mode != SM_VERIFY) openxscript(ee); sendenvelope(ee, mode); - dropenvelope(ee, TRUE); + dropenvelope(ee, true, true); } CurEnv = e; Verbose = oldverbose; if (mode == SM_FORK) - finis(TRUE, ExitStat); + finis(true, ExitStat); } static void @@ -744,9 +765,9 @@ sendenvelope(e, mode) bool didany; if (tTd(13, 10)) - dprintf("sendenvelope(%s) e_flags=0x%lx\n", - e->e_id == NULL ? "[NOQUEUE]" : e->e_id, - e->e_flags); + sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n", + e->e_id == NULL ? "[NOQUEUE]" : e->e_id, + e->e_flags); if (LogLevel > 80) sm_syslog(LOG_DEBUG, e->e_id, "sendenvelope, flags=0x%lx", @@ -766,9 +787,15 @@ sendenvelope(e, mode) return; } - /* Don't attempt deliveries if we want to bounce now */ + /* + ** Don't attempt deliveries if we want to bounce now + ** or if deliver-by time is exceeded. + */ + if (!bitset(EF_RESPONSE, e->e_flags) && - TimeOuts.to_q_return[e->e_timeoutclass] == NOW) + (TimeOuts.to_q_return[e->e_timeoutclass] == NOW || + (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 && + curtime() > e->e_ctime + e->e_deliver_by))) return; /* @@ -781,9 +808,46 @@ sendenvelope(e, mode) e->e_nsent = 0; e->e_flags |= EF_GLOBALERRS; - define(macid("{envid}", NULL), e->e_envid, e); - define(macid("{bodytype}", NULL), e->e_bodytype, e); - didany = FALSE; + macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid); + macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype); + didany = false; + + if (!bitset(EF_SPLIT, e->e_flags)) + { + ENVELOPE *oldsib; + ENVELOPE *ee; + + /* + ** Save old sibling and set it to NULL to avoid + ** queueing up the same envelopes again. + ** This requires that envelopes in that list have + ** been take care of before (or at some other place). + */ + + oldsib = e->e_sibling; + e->e_sibling = NULL; + (void) split_by_recipient(e); + for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) + queueup(ee, false, true); + + /* clean up */ + for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) + { + /* now unlock the job */ + closexscript(ee); + unlockqueue(ee); + + /* this envelope is marked unused */ + if (ee->e_dfp != NULL) + { + (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT); + ee->e_dfp = NULL; + } + ee->e_id = NULL; + ee->e_flags &= ~EF_HAS_DF; + } + e->e_sibling = oldsib; + } /* now run through the queue */ for (q = e->e_sendqueue; q != NULL; q = q->q_next) @@ -791,8 +855,8 @@ sendenvelope(e, mode) #if XDEBUG char wbuf[MAXNAME + 20]; - (void) snprintf(wbuf, sizeof wbuf, "sendall(%.*s)", - MAXNAME, q->q_paddr); + (void) sm_snprintf(wbuf, sizeof wbuf, "sendall(%.*s)", + MAXNAME, q->q_paddr); checkfd012(wbuf); #endif /* XDEBUG */ if (mode == SM_VERIFY) @@ -813,7 +877,6 @@ sendenvelope(e, mode) } else if (QS_IS_OK(q->q_state)) { -#if QUEUE /* ** Checkpoint the send list every few addresses */ @@ -821,12 +884,11 @@ sendenvelope(e, mode) if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval) { - queueup(e, FALSE); + queueup(e, false, false); e->e_nsent = 0; } -#endif /* QUEUE */ (void) deliver(e, q); - didany = TRUE; + didany = true; } } if (didany) @@ -839,6 +901,59 @@ sendenvelope(e, mode) checkfd012("end of sendenvelope"); #endif /* XDEBUG */ } + +#if REQUIRES_DIR_FSYNC +/* +** SYNC_DIR -- fsync a directory based on a filename +** +** Parameters: +** filename -- path of file +** panic -- panic? +** +** Returns: +** none +*/ + +void +sync_dir(filename, panic) + char *filename; + bool panic; +{ + int dirfd; + char *dirp; + char dir[MAXPATHLEN]; + + /* filesystems which require the directory be synced */ + dirp = strrchr(filename, '/'); + if (dirp != NULL) + { + if (sm_strlcpy(dir, filename, sizeof dir) >= sizeof dir) + return; + dir[dirp - filename] = '\0'; + dirp = dir; + } + else + dirp = "."; + dirfd = open(dirp, O_RDONLY, 0700); + if (tTd(40,32)) + sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)", + dirp, dirfd); + if (dirfd >= 0) + { + if (fsync(dirfd) < 0) + { + if (panic) + syserr("!sync_dir: cannot fsync directory %s", + dirp); + else if (LogLevel > 1) + sm_syslog(LOG_ERR, NOQID, + "sync_dir: cannot fsync directory %s: %s", + dirp, sm_errstring(errno)); + } + (void) close(dirfd); + } +} +#endif /* REQUIRES_DIR_FSYNC */ /* ** DUP_QUEUE_FILE -- duplicate a queue file into a split queue ** @@ -853,7 +968,7 @@ sendenvelope(e, mode) static void dup_queue_file(e, ee, type) - struct envelope *e, *ee; + ENVELOPE *e, *ee; int type; { char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN]; @@ -865,8 +980,8 @@ dup_queue_file(e, ee, type) ** Make sure both are in the same directory. */ - snprintf(f1buf, sizeof f1buf, "%s", queuename(e, type)); - snprintf(f2buf, sizeof f2buf, "%s", queuename(ee, type)); + (void) sm_strlcpy(f1buf, queuename(e, type), sizeof f1buf); + (void) sm_strlcpy(f2buf, queuename(ee, type), sizeof f2buf); if (link(f1buf, f2buf) < 0) { int save_errno = errno; @@ -877,17 +992,18 @@ dup_queue_file(e, ee, type) if (unlink(f2buf) < 0) { syserr("!sendall: unlink(%s): permanent", - f2buf); + f2buf); /* NOTREACHED */ } if (link(f1buf, f2buf) < 0) { syserr("!sendall: link(%s, %s): permanent", - f1buf, f2buf); + f1buf, f2buf); /* NOTREACHED */ } } } + SYNC_DIR(f2buf, true); } /* ** DOFORK -- do a fork, retrying a couple of times on failure. @@ -953,6 +1069,72 @@ dofork() DOFORK(fork); return pid; } + +/* +** COLONCMP -- compare host-signatures up to first ':' or EOS +** +** This takes two strings which happen to be host-signatures and +** compares them. If the lowest preference portions of the MX-RR's +** match (up to ':' or EOS, whichever is first), then we have +** match. This is used for coattail-piggybacking messages during +** message delivery. +** If the signatures are the same up to the first ':' the remainder of +** the signatures are then compared with a normal strcmp(). This saves +** re-examining the first part of the signatures. +** +** Parameters: +** a - first host-signature +** b - second host-signature +** +** Returns: +** HS_MATCH_NO -- no "match". +** HS_MATCH_FIRST -- "match" for the first MX preference +** (up to the first colon (':')). +** HS_MATCH_FULL -- match for the entire MX record. +** +** Side Effects: +** none. +*/ + +#define HS_MATCH_NO 0 +#define HS_MATCH_FIRST 1 +#define HS_MATCH_FULL 2 + +static int +coloncmp(a, b) + register const char *a; + register const char *b; +{ + int ret = HS_MATCH_NO; + int braclev = 0; + + while (*a == *b++) + { + /* Need to account for IPv6 bracketed addresses */ + if (*a == '[') + braclev++; + else if (*a == '[' && braclev > 0) + braclev--; + else if (*a == ':' && braclev <= 0) + { + ret = HS_MATCH_FIRST; + a++; + break; + } + else if (*a == '\0') + return HS_MATCH_FULL; /* a full match */ + a++; + } + if (ret == HS_MATCH_NO && + braclev <= 0 && + ((*a == '\0' && *(b - 1) == ':') || + (*a == ':' && *(b - 1) == '\0'))) + return HS_MATCH_FIRST; + if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0) + return HS_MATCH_FULL; + + return ret; +} /* ** DELIVER -- Deliver a message to a list of addresses. ** @@ -962,6 +1144,45 @@ dofork() ** that it will deliver to all these addresses however -- so ** deliver should be called once for each address on the ** list. +** Deliver tries to be as opportunistic as possible about piggybacking +** messages. Some definitions to make understanding easier follow below. +** Piggybacking occurs when an existing connection to a mail host can +** be used to send the same message to more than one recipient at the +** same time. So "no piggybacking" means one message for one recipient +** per connection. "Intentional piggybacking" happens when the +** recipients' host address (not the mail host address) is used to +** attempt piggybacking. Recipients with the same host address +** have the same mail host. "Coincidental piggybacking" relies on +** piggybacking based on all the mail host addresses in the MX-RR. This +** is "coincidental" in the fact it could not be predicted until the +** MX Resource Records for the hosts were obtained and examined. For +** example (preference order and equivalence is important, not values): +** domain1 IN MX 10 mxhost-A +** IN MX 20 mxhost-B +** domain2 IN MX 4 mxhost-A +** IN MX 8 mxhost-B +** Domain1 and domain2 can piggyback the same message to mxhost-A or +** mxhost-B (if mxhost-A cannot be reached). +** "Coattail piggybacking" relaxes the strictness of "coincidental +** piggybacking" in the hope that most significant (lowest value) +** MX preference host(s) can create more piggybacking. For example +** (again, preference order and equivalence is important, not values): +** domain3 IN MX 100 mxhost-C +** IN MX 100 mxhost-D +** IN MX 200 mxhost-E +** domain4 IN MX 50 mxhost-C +** IN MX 50 mxhost-D +** IN MX 80 mxhost-F +** A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C +** is available. Same with mxhost-D because in both RR's the preference +** value is the same as mxhost-C, respectively. +** So deliver attempts coattail piggybacking when possible. If the +** first MX preference level hosts cannot be used then the piggybacking +** reverts to coincidental piggybacking. Using the above example you +** cannot deliver to mxhost-F for domain3 regardless of preference value. +** ("Coattail" from "riding on the coattails of your predecessor" meaning +** gaining benefit from a predecessor effort with no or little addition +** effort. The predecessor here being the preceding MX RR). ** ** Parameters: ** e -- the envelope to deliver. @@ -994,41 +1215,41 @@ deliver(e, firstto) register char *p; register MAILER *m; /* mailer for this recipient */ ADDRESS *volatile ctladdr; +# if HASSETUSERCONTEXT ADDRESS *volatile contextaddr = NULL; +# endif /* HASSETUSERCONTEXT */ register MCI *volatile mci; - register ADDRESS *to = firstto; - volatile bool clever = FALSE; /* running user smtp to this mailer */ + register ADDRESS *SM_NONVOLATILE to = firstto; + volatile bool clever = false; /* running user smtp to this mailer */ ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */ int rcode; /* response code */ - int lmtp_rcode = EX_OK; - int nummxhosts = 0; /* number of MX hosts available */ - int hostnum = 0; /* current MX host index */ + SM_NONVOLATILE int lmtp_rcode = EX_OK; + SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */ + SM_NONVOLATILE int hostnum = 0; /* current MX host index */ char *firstsig; /* signature of firstto */ - pid_t pid = -1; + volatile pid_t pid = -1; char *volatile curhost; - register u_short port = 0; + SM_NONVOLATILE unsigned short port = 0; + SM_NONVOLATILE time_t enough = 0; #if NETUNIX - char *mux_path = NULL; /* path to UNIX domain socket */ + char *SM_NONVOLATILE mux_path = NULL; /* path to UNIX domain socket */ #endif /* NETUNIX */ time_t xstart; bool suidwarn; bool anyok; /* at least one address was OK */ - bool goodmxfound = FALSE; /* at least one MX was OK */ + SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */ bool ovr; -#if _FFR_DYNAMIC_TOBUF int strsize; int rcptcount; + int ret; static int tobufsize = 0; static char *tobuf = NULL; -#else /* _FFR_DYNAMIC_TOBUF */ - char tobuf[TOBUFSIZE]; /* text line of to people */ -#endif /* _FFR_DYNAMIC_TOBUF */ + char *rpath; /* translated return path */ int mpvect[2]; int rpvect[2]; char *mxhosts[MAXMXHOSTS + 1]; char *pv[MAXPV + 1]; char buf[MAXNAME + 1]; - char rpathbuf[MAXNAME + 1]; /* translated return path */ errno = 0; if (!QS_IS_OK(to->q_state)) @@ -1040,29 +1261,32 @@ deliver(e, firstto) host = to->q_host; CurEnv = e; /* just in case */ e->e_statmsg = NULL; -#if SMTP SmtpError[0] = '\0'; -#endif /* SMTP */ xstart = curtime(); if (tTd(10, 1)) - dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n", + sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n", e->e_id, m->m_name, host, to->q_user); if (tTd(10, 100)) - printopenfds(FALSE); + printopenfds(false); /* - ** Clear $&{client_*} macros if this is a bounce message to + ** Clear {client_*} macros if this is a bounce message to ** prevent rejection by check_compat ruleset. */ if (bitset(EF_RESPONSE, e->e_flags)) { - define(macid("{client_name}", NULL), "", e); - define(macid("{client_addr}", NULL), "", e); - define(macid("{client_port}", NULL), "", e); + macdefine(&e->e_macro, A_PERM, macid("{client_name}"), ""); + macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), ""); + macdefine(&e->e_macro, A_PERM, macid("{client_port}"), ""); + macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), ""); } + SM_TRY + { + ADDRESS *skip_back = NULL; + /* ** Do initial argv setup. ** Insert the mailer name. Notice that $x expansion is @@ -1080,15 +1304,15 @@ deliver(e, firstto) p = e->e_sender; else p = e->e_from.q_paddr; - p = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e); - if (strlen(p) >= (SIZE_T) sizeof rpathbuf) + rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e); + if (strlen(rpath) > MAXSHORTSTR) { - p = shortenstring(p, MAXSHORTSTR); - syserr("remotename: huge return %s", p); + rpath = shortenstring(rpath, MAXSHORTSTR); + syserr("remotename: huge return %s", rpath); } - snprintf(rpathbuf, sizeof rpathbuf, "%s", p); - define('g', rpathbuf, e); /* translated return path */ - define('h', host, e); /* to host */ + rpath = sm_rpool_strdup_x(e->e_rpool, rpath); + macdefine(&e->e_macro, A_PERM, 'g', rpath); + macdefine(&e->e_macro, A_PERM, 'h', host); Errors = 0; pvp = pv; *pvp++ = m->m_argv[0]; @@ -1102,7 +1326,7 @@ deliver(e, firstto) *pvp++ = "-f"; else *pvp++ = "-r"; - *pvp++ = newstr(rpathbuf); + *pvp++ = rpath; } /* @@ -1129,12 +1353,13 @@ deliver(e, firstto) /* this entry is safe -- go ahead and process it */ expand(*mvp, buf, sizeof buf, e); - *pvp++ = newstr(buf); + *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); if (pvp >= &pv[MAXPV - 3]) { syserr("554 5.3.5 Too many parameters to %s before $u", pv[0]); - return -1; + rcode = -1; + goto cleanup; } } @@ -1147,14 +1372,8 @@ deliver(e, firstto) if (*mvp == NULL) { /* running LMTP or SMTP */ -#if SMTP - clever = TRUE; + clever = true; *pvp = NULL; -#else /* SMTP */ - /* oops! we don't implement SMTP */ - syserr("554 5.3.5 SMTP style mailer not implemented"); - return EX_SOFTWARE; -#endif /* SMTP */ } else if (bitnset(M_LMTP, m->m_flags)) { @@ -1172,62 +1391,85 @@ deliver(e, firstto) ** always send another copy later. */ -#if _FFR_DYNAMIC_TOBUF e->e_to = NULL; strsize = 2; rcptcount = 0; -#else /* _FFR_DYNAMIC_TOBUF */ - tobuf[0] = '\0'; - e->e_to = tobuf; -#endif /* _FFR_DYNAMIC_TOBUF */ - ctladdr = NULL; - firstsig = hostsignature(firstto->q_mailer, firstto->q_host); + if (firstto->q_signature == NULL) + firstto->q_signature = hostsignature(firstto->q_mailer, + firstto->q_host); + firstsig = firstto->q_signature; + for (; to != NULL; to = to->q_next) { /* avoid sending multiple recipients to dumb mailers */ -#if _FFR_DYNAMIC_TOBUF if (tochain != NULL && !bitnset(M_MUSER, m->m_flags)) break; -#else /* _FFR_DYNAMIC_TOBUF */ - if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags)) - break; -#endif /* _FFR_DYNAMIC_TOBUF */ /* if already sent or not for this host, don't send */ - if (!QS_IS_OK(to->q_state) || - to->q_mailer != firstto->q_mailer || - strcmp(hostsignature(to->q_mailer, to->q_host), - firstsig) != 0) + if (!QS_IS_OK(to->q_state)) /* already sent; look at next */ continue; - /* avoid overflowing tobuf */ -#if _FFR_DYNAMIC_TOBUF - strsize += strlen(to->q_paddr) + 1; - if (!clever && strsize > TOBUFSIZE) + /* + ** Must be same mailer to keep grouping rcpts. + ** If mailers don't match: continue; sendqueue is not + ** sorted by mailers, so don't break; + */ + + if (to->q_mailer != firstto->q_mailer) + continue; + + if (to->q_signature == NULL) /* for safety */ + to->q_signature = hostsignature(to->q_mailer, + to->q_host); + + /* + ** This is for coincidental and tailcoat piggybacking messages + ** to the same mail host. While the signatures are identical + ** (that's the MX-RR's are identical) we can do coincidental + ** piggybacking. We try hard for coattail piggybacking + ** with the same mail host when the next recipient has the + ** same host at lowest preference. It may be that this + ** won't work out, so 'skip_back' is maintained if a backup + ** to coincidental piggybacking or full signature must happen. + */ + + ret = firstto == to ? HS_MATCH_FULL : + coloncmp(to->q_signature, firstsig); + if (ret == HS_MATCH_FULL) + skip_back = to; + else if (ret == HS_MATCH_NO) break; + if (!clever) + { + /* avoid overflowing tobuf */ + strsize += strlen(to->q_paddr) + 1; + if (strsize > TOBUFSIZE) + break; + } + if (++rcptcount > to->q_mailer->m_maxrcpt) break; -#else /* _FFR_DYNAMIC_TOBUF */ - if (sizeof tobuf < (strlen(to->q_paddr) + strlen(tobuf) + 2)) - break; -#endif /* _FFR_DYNAMIC_TOBUF */ if (tTd(10, 1)) { - dprintf("\nsend to "); - printaddr(to, FALSE); + sm_dprintf("\nsend to "); + printaddr(to, false); } /* compute effective uid/gid when sending */ if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags)) +# if HASSETUSERCONTEXT contextaddr = ctladdr = getctladdr(to); +# else /* HASSETUSERCONTEXT */ + ctladdr = getctladdr(to); +# endif /* HASSETUSERCONTEXT */ if (tTd(10, 2)) { - dprintf("ctladdr="); - printaddr(ctladdr, FALSE); + sm_dprintf("ctladdr="); + printaddr(ctladdr, false); } user = to->q_user; @@ -1247,45 +1489,44 @@ deliver(e, firstto) to->q_status = "5.2.3"; else to->q_status = "5.3.4"; + /* set to->q_rstatus = NULL; or to the following? */ usrerrenh(to->q_status, "552 Message is too large; %ld bytes max", m->m_maxsize); - markfailure(e, to, NULL, EX_UNAVAILABLE, FALSE); + markfailure(e, to, NULL, EX_UNAVAILABLE, false); giveresponse(EX_UNAVAILABLE, to->q_status, m, - NULL, ctladdr, xstart, e); + NULL, ctladdr, xstart, e, to); continue; } -#if NAMED_BIND SM_SET_H_ERRNO(0); -#endif /* NAMED_BIND */ + ovr = true; - ovr = TRUE; /* do config file checking of compatibility */ rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr, - e, TRUE, TRUE, 4, NULL); + e, true, true, 3, NULL, e->e_id); if (rcode == EX_OK) { /* do in-code checking if not discarding */ if (!bitset(EF_DISCARD, e->e_flags)) { rcode = checkcompat(to, e); - ovr = FALSE; + ovr = false; } } if (rcode != EX_OK) { markfailure(e, to, NULL, rcode, ovr); giveresponse(rcode, to->q_status, m, - NULL, ctladdr, xstart, e); + NULL, ctladdr, xstart, e, to); continue; } if (bitset(EF_DISCARD, e->e_flags)) { if (tTd(10, 5)) { - dprintf("deliver: discarding recipient "); - printaddr(to, FALSE); + sm_dprintf("deliver: discarding recipient "); + printaddr(to, false); } /* pretend the message was sent */ @@ -1340,11 +1581,11 @@ deliver(e, firstto) if (strcmp(m->m_mailer, "[FILE]") == 0) { - define('u', user, e); /* to user */ + macdefine(&e->e_macro, A_PERM, 'u', user); p = to->q_home; if (p == NULL && ctladdr != NULL) p = ctladdr->q_home; - define('z', p, e); /* user's home */ + macdefine(&e->e_macro, A_PERM, 'z', p); expand(m->m_argv[1], buf, sizeof buf, e); if (strlen(buf) > 0) rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e); @@ -1355,8 +1596,8 @@ deliver(e, firstto) rcode = EX_CONFIG; } giveresponse(rcode, to->q_status, m, NULL, - ctladdr, xstart, e); - markfailure(e, to, NULL, rcode, TRUE); + ctladdr, xstart, e, to); + markfailure(e, to, NULL, rcode, true); e->e_nsent++; if (rcode == EX_OK) { @@ -1366,12 +1607,14 @@ deliver(e, firstto) { to->q_flags |= QDELIVERED; to->q_status = "2.1.5"; - fprintf(e->e_xfp, "%s... Successfully delivered\n", - to->q_paddr); + (void) sm_io_fprintf(e->e_xfp, + SM_TIME_DEFAULT, + "%s... Successfully delivered\n", + to->q_paddr); } } to->q_statdate = curtime(); - markstats(e, to, FALSE); + markstats(e, to, false); continue; } @@ -1383,20 +1626,13 @@ deliver(e, firstto) /* link together the chain of recipients */ to->q_tchain = tochain; tochain = to; - -#if _FFR_DYNAMIC_TOBUF e->e_to = "[CHAIN]"; -#else /* _FFR_DYNAMIC_TOBUF */ - /* create list of users for error messages */ - (void) strlcat(tobuf, ",", sizeof tobuf); - (void) strlcat(tobuf, to->q_paddr, sizeof tobuf); -#endif /* _FFR_DYNAMIC_TOBUF */ - define('u', user, e); /* to user */ + macdefine(&e->e_macro, A_PERM, 'u', user); /* to user */ p = to->q_home; if (p == NULL && ctladdr != NULL) p = ctladdr->q_home; - define('z', p, e); /* user's home */ + macdefine(&e->e_macro, A_PERM, 'z', p); /* user's home */ /* set the ${dsn_notify} macro if applicable */ if (bitset(QHASNOTIFY, to->q_flags)) @@ -1405,24 +1641,28 @@ deliver(e, firstto) notify[0] = '\0'; if (bitset(QPINGONSUCCESS, to->q_flags)) - (void) strlcat(notify, "SUCCESS,", - sizeof notify); + (void) sm_strlcat(notify, "SUCCESS,", + sizeof notify); if (bitset(QPINGONFAILURE, to->q_flags)) - (void) strlcat(notify, "FAILURE,", - sizeof notify); + (void) sm_strlcat(notify, "FAILURE,", + sizeof notify); if (bitset(QPINGONDELAY, to->q_flags)) - (void) strlcat(notify, "DELAY,", sizeof notify); + (void) sm_strlcat(notify, "DELAY,", + sizeof notify); /* Set to NEVER or drop trailing comma */ if (notify[0] == '\0') - (void) strlcat(notify, "NEVER", sizeof notify); + (void) sm_strlcat(notify, "NEVER", + sizeof notify); else notify[strlen(notify) - 1] = '\0'; - define(macid("{dsn_notify}", NULL), newstr(notify), e); + macdefine(&e->e_macro, A_TEMP, + macid("{dsn_notify}"), notify); } else - define(macid("{dsn_notify}", NULL), NULL, e); + macdefine(&e->e_macro, A_PERM, + macid("{dsn_notify}"), NULL); /* ** Expand out this user into argument list. @@ -1431,7 +1671,7 @@ deliver(e, firstto) if (!clever) { expand(*mvp, buf, sizeof buf, e); - *pvp++ = newstr(buf); + *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); if (pvp >= &pv[MAXPV - 2]) { /* allow some space for trailing parms */ @@ -1441,57 +1681,48 @@ deliver(e, firstto) } /* see if any addresses still exist */ -#if _FFR_DYNAMIC_TOBUF if (tochain == NULL) -#else /* _FFR_DYNAMIC_TOBUF */ - if (tobuf[0] == '\0') -#endif /* _FFR_DYNAMIC_TOBUF */ { - define('g', (char *) NULL, e); - e->e_to = NULL; - return 0; + rcode = 0; + goto cleanup; } /* print out messages as full list */ -#if _FFR_DYNAMIC_TOBUF + strsize = 1; + for (to = tochain; to != NULL; to = to->q_tchain) + strsize += strlen(to->q_paddr) + 1; + if (strsize < TOBUFSIZE) + strsize = TOBUFSIZE; + if (strsize > tobufsize) { - int l = 1; - char *tobufptr; - - for (to = tochain; to != NULL; to = to->q_tchain) - l += strlen(to->q_paddr) + 1; - if (l < TOBUFSIZE) - l = TOBUFSIZE; - if (l > tobufsize) - { - if (tobuf != NULL) - sm_free(tobuf); - tobufsize = l; - tobuf = xalloc(tobufsize); - } - tobufptr = tobuf; - *tobufptr = '\0'; - for (to = tochain; to != NULL; to = to->q_tchain) - { - snprintf(tobufptr, tobufsize - (tobufptr - tobuf), - ",%s", to->q_paddr); - tobufptr += strlen(tobufptr); - } + SM_FREE_CLR(tobuf); + tobuf = sm_pmalloc_x(strsize); + tobufsize = strsize; + } + p = tobuf; + *p = '\0'; + for (to = tochain; to != NULL; to = to->q_tchain) + { + (void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2, + ",", to->q_paddr); + p += strlen(p); } -#endif /* _FFR_DYNAMIC_TOBUF */ e->e_to = tobuf + 1; /* ** Fill out any parameters after the $u parameter. */ - while (!clever && *++mvp != NULL) + if (!clever) { - expand(*mvp, buf, sizeof buf, e); - *pvp++ = newstr(buf); - if (pvp >= &pv[MAXPV]) - syserr("554 5.3.0 deliver: pv overflow after $u for %s", - pv[0]); + while (*++mvp != NULL) + { + expand(*mvp, buf, sizeof buf, e); + *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); + if (pvp >= &pv[MAXPV]) + syserr("554 5.3.0 deliver: pv overflow after $u for %s", + pv[0]); + } } *pvp++ = NULL; @@ -1515,14 +1746,11 @@ deliver(e, firstto) if (tTd(11, 1)) { - dprintf("openmailer:"); + sm_dprintf("openmailer:"); printav(pv); } errno = 0; -#if NAMED_BIND SM_SET_H_ERRNO(0); -#endif /* NAMED_BIND */ - CurHostName = NULL; /* @@ -1543,8 +1771,9 @@ deliver(e, firstto) char wbuf[MAXLINE]; /* make absolutely certain 0, 1, and 2 are in use */ - snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)", - shortenstring(e->e_to, MAXSHORTSTR), m->m_name); + (void) sm_snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)", + shortenstring(e->e_to, MAXSHORTSTR), + m->m_name); checkfd012(wbuf); } #endif /* XDEBUG */ @@ -1559,7 +1788,7 @@ deliver(e, firstto) { e->e_status = "5.6.3"; usrerrenh(e->e_status, - "554 Cannot send 8-bit data to 7-bit destination"); + "554 Cannot send 8-bit data to 7-bit destination"); rcode = EX_DATAERR; goto give_up; } @@ -1570,17 +1799,50 @@ deliver(e, firstto) /* check for Local Person Communication -- not for mortals!!! */ if (strcmp(m->m_mailer, "[LPC]") == 0) { - mci = (MCI *) xalloc(sizeof *mci); - memset((char *) mci, '\0', sizeof *mci); - mci->mci_in = stdin; - mci->mci_out = stdout; +#if _FFR_CACHE_LPC + if (clever) + { + /* flush any expired connections */ + (void) mci_scan(NULL); + + /* try to get a cached connection or just a slot */ + mci = mci_get(m->m_name, m); + if (mci->mci_host == NULL) + mci->mci_host = m->m_name; + CurHostName = mci->mci_host; + if (mci->mci_state != MCIS_CLOSED) + { + message("Using cached SMTP/LPC connection for %s...", + m->m_name); + mci->mci_deliveries++; + goto do_transfer; + } + } + else + { + mci = mci_new(e->e_rpool); + } + mci->mci_in = smioin; + mci->mci_out = smioout; + mci->mci_mailer = m; + mci->mci_host = m->m_name; + if (clever) + { + mci->mci_state = MCIS_OPENING; + mci_cache(mci); + } + else + mci->mci_state = MCIS_OPEN; +#else /* _FFR_CACHE_LPC */ + mci = mci_new(e->e_rpool); + mci->mci_in = smioin; + mci->mci_out = smioout; mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN; mci->mci_mailer = m; +#endif /* _FFR_CACHE_LPC */ } - else if (strcmp(m->m_mailer, "[IPC]") == 0 || - strcmp(m->m_mailer, "[TCP]") == 0) + else if (strcmp(m->m_mailer, "[IPC]") == 0) { -#if DAEMON register int i; if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0') @@ -1622,7 +1884,7 @@ deliver(e, firstto) # endif /* NETUNIX */ ) { - port = htons((u_short)atoi(pv[2])); + port = htons((unsigned short) atoi(pv[2])); if (port == 0) { # ifdef NO_GETSERVBYNAME @@ -1639,6 +1901,8 @@ deliver(e, firstto) } nummxhosts = parse_hostsignature(curhost, mxhosts, m); + if (TimeOuts.to_aconnect > 0) + enough = curtime() + TimeOuts.to_aconnect; tryhost: while (hostnum < nummxhosts) { @@ -1664,6 +1928,24 @@ tryhost: *endp = '\0'; } + if (hostnum == 1 && skip_back != NULL) + { + /* + ** Coattail piggybacking is no longer an + ** option with the mail host next to be tried + ** no longer the lowest MX preference + ** (hostnum == 1 meaning we're on the second + ** preference). We do not try to coattail + ** piggyback more than the first MX preference. + ** Revert 'tochain' to last location for + ** coincidental piggybacking. This works this + ** easily because the q_tchain kept getting + ** added to the top of the linked list. + */ + + tochain = skip_back; + } + if (*mxhosts[hostnum] == '\0') { syserr("deliver: null host name in signature"); @@ -1672,8 +1954,8 @@ tryhost: *endp = sep; continue; } - (void) strlcpy(hostbuf, mxhosts[hostnum], - sizeof hostbuf); + (void) sm_strlcpy(hostbuf, mxhosts[hostnum], + sizeof hostbuf); hostnum++; if (endp != NULL) *endp = sep; @@ -1683,15 +1965,22 @@ tryhost: mci = mci_get(hostbuf, m); if (mci->mci_state != MCIS_CLOSED) { + char *type; + if (tTd(11, 1)) { - dprintf("openmailer: "); - mci_dump(mci, FALSE); + sm_dprintf("openmailer: "); + mci_dump(mci, false); } CurHostName = mci->mci_host; - message("Using cached %sSMTP connection to %s via %s...", - bitset(MCIF_ESMTP, mci->mci_flags) ? "E" : "", - hostbuf, m->m_name); + if (bitnset(M_LMTP, m->m_flags)) + type = "L"; + else if (bitset(MCIF_ESMTP, mci->mci_flags)) + type = "ES"; + else + type = "S"; + message("Using cached %sMTP connection to %s via %s...", + type, hostbuf, m->m_name); mci->mci_deliveries++; break; } @@ -1699,19 +1988,19 @@ tryhost: if (mci->mci_exitstat != EX_OK) { if (mci->mci_exitstat == EX_TEMPFAIL) - goodmxfound = TRUE; + goodmxfound = true; continue; } if (mci_lock_host(mci) != EX_OK) { mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); - goodmxfound = TRUE; + goodmxfound = true; continue; } /* try the connection */ - sm_setproctitle(TRUE, e, "%s %s: %s", + sm_setproctitle(true, e, "%s %s: %s", qid_printname(e), hostbuf, "user open"); # if NETUNIX @@ -1719,7 +2008,7 @@ tryhost: { message("Connecting to %s via %s...", mux_path, m->m_name); - i = makeconnection_ds(mux_path, mci); + i = makeconnection_ds((char *) mux_path, mci); } else # endif /* NETUNIX */ @@ -1731,7 +2020,8 @@ tryhost: message("Connecting to %s port %d via %s...", hostbuf, ntohs(port), m->m_name); - i = makeconnection(hostbuf, port, mci, e); + i = makeconnection(hostbuf, port, mci, e, + enough); } mci->mci_errno = errno; mci->mci_lastuse = curtime(); @@ -1740,23 +2030,55 @@ tryhost: # if NAMED_BIND mci->mci_herrno = h_errno; # endif /* NAMED_BIND */ + + /* + ** Have we tried long enough to get a connection? + ** If yes, skip to the fallback MX hosts + ** (if existent). + */ + + if (enough > 0 && mci->mci_lastuse >= enough) + { + int h; +# if NAMED_BIND + extern int NumFallBackMXHosts; +# else /* NAMED_BIND */ + const int NumFallBackMXHosts = 0; +# endif /* NAMED_BIND */ + + if (hostnum < nummxhosts && LogLevel > 9) + sm_syslog(LOG_INFO, e->e_id, + "Timeout.to_aconnect occurred before exhausting all addresses"); + + /* turn off timeout if fallback available */ + if (NumFallBackMXHosts > 0) + enough = 0; + + /* skip to a fallback MX host */ + h = nummxhosts - NumFallBackMXHosts; + if (hostnum < h) + hostnum = h; + } if (i == EX_OK) { - goodmxfound = TRUE; + goodmxfound = true; mci->mci_state = MCIS_OPENING; mci_cache(mci); if (TrafficLogFile != NULL) - fprintf(TrafficLogFile, "%05d === CONNECT %s\n", - (int) getpid(), hostbuf); + (void) sm_io_fprintf(TrafficLogFile, + SM_TIME_DEFAULT, + "%05d === CONNECT %s\n", + (int) CurrentPid, + hostbuf); break; } else { if (tTd(11, 1)) - dprintf("openmailer: makeconnection => stat=%d, errno=%d\n", - i, errno); + sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n", + i, errno); if (i == EX_TEMPFAIL) - goodmxfound = TRUE; + goodmxfound = true; mci_unlock_host(mci); } @@ -1772,13 +2094,6 @@ tryhost: goto give_up; } mci->mci_pid = 0; -#else /* DAEMON */ - syserr("554 5.3.5 openmailer: no IPC"); - if (tTd(11, 1)) - dprintf("openmailer: NULL\n"); - rcode = EX_UNAVAILABLE; - goto give_up; -#endif /* DAEMON */ } else { @@ -1786,7 +2101,6 @@ tryhost: (void) mci_scan(NULL); mci = NULL; -#if SMTP if (bitnset(M_LMTP, m->m_flags)) { /* try to get a cached connection */ @@ -1802,7 +2116,6 @@ tryhost: goto do_transfer; } } -#endif /* SMTP */ /* announce the connection to verbose listeners */ if (host == NULL || host[0] == '\0') @@ -1813,10 +2126,14 @@ tryhost: { char **av; - fprintf(TrafficLogFile, "%05d === EXEC", (int) getpid()); + (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, + "%05d === EXEC", (int) CurrentPid); for (av = pv; *av != NULL; av++) - fprintf(TrafficLogFile, " %s", *av); - fprintf(TrafficLogFile, "\n"); + (void) sm_io_fprintf(TrafficLogFile, + SM_TIME_DEFAULT, " %s", + *av); + (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, + "\n"); } #if XDEBUG @@ -1827,9 +2144,9 @@ tryhost: if (pipe(mpvect) < 0) { syserr("%s... openmailer(%s): pipe (to mailer)", - shortenstring(e->e_to, MAXSHORTSTR), m->m_name); + shortenstring(e->e_to, MAXSHORTSTR), m->m_name); if (tTd(11, 1)) - dprintf("openmailer: NULL\n"); + sm_dprintf("openmailer: NULL\n"); rcode = EX_OSERR; goto give_up; } @@ -1839,11 +2156,11 @@ tryhost: if (mpvect[0] < 3 || mpvect[1] < 3) { syserr("%s... openmailer(%s): bogus mpvect %d %d", - shortenstring(e->e_to, MAXSHORTSTR), m->m_name, - mpvect[0], mpvect[1]); - printopenfds(TRUE); + shortenstring(e->e_to, MAXSHORTSTR), m->m_name, + mpvect[0], mpvect[1]); + printopenfds(true); if (tTd(11, 1)) - dprintf("openmailer: NULL\n"); + sm_dprintf("openmailer: NULL\n"); rcode = EX_OSERR; goto give_up; } @@ -1853,18 +2170,21 @@ tryhost: checkfdopen(mpvect[1], "mpvect[1]"); if (mpvect[0] == mpvect[1] || (e->e_lockfp != NULL && - (mpvect[0] == fileno(e->e_lockfp) || - mpvect[1] == fileno(e->e_lockfp)))) + (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, + NULL) || + mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, + NULL)))) { if (e->e_lockfp == NULL) syserr("%s... openmailer(%s): overlapping mpvect %d %d", - shortenstring(e->e_to, MAXSHORTSTR), - m->m_name, mpvect[0], mpvect[1]); + shortenstring(e->e_to, MAXSHORTSTR), + m->m_name, mpvect[0], mpvect[1]); else syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d", - shortenstring(e->e_to, MAXSHORTSTR), - m->m_name, mpvect[0], mpvect[1], - fileno(e->e_lockfp)); + shortenstring(e->e_to, MAXSHORTSTR), + m->m_name, mpvect[0], mpvect[1], + sm_io_getinfo(e->e_lockfp, + SM_IO_WHAT_FD, NULL)); } #endif /* XDEBUG */ @@ -1872,12 +2192,12 @@ tryhost: if (pipe(rpvect) < 0) { syserr("%s... openmailer(%s): pipe (from mailer)", - shortenstring(e->e_to, MAXSHORTSTR), - m->m_name); + shortenstring(e->e_to, MAXSHORTSTR), + m->m_name); (void) close(mpvect[0]); (void) close(mpvect[1]); if (tTd(11, 1)) - dprintf("openmailer: NULL\n"); + sm_dprintf("openmailer: NULL\n"); rcode = EX_OSERR; goto give_up; } @@ -1894,10 +2214,10 @@ tryhost: ** around so that endmailer will get it. */ - if (e->e_xfp != NULL) - (void) fflush(e->e_xfp); /* for debugging */ - (void) fflush(stdout); - (void) setsignal(SIGCHLD, SIG_DFL); + if (e->e_xfp != NULL) /* for debugging */ + (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); + (void) sm_io_flush(smioout, SM_TIME_DEFAULT); + (void) sm_signal(SIGCHLD, SIG_DFL); DOFORK(FORK); @@ -1907,13 +2227,13 @@ tryhost: { /* failure */ syserr("%s... openmailer(%s): cannot fork", - shortenstring(e->e_to, MAXSHORTSTR), m->m_name); + shortenstring(e->e_to, MAXSHORTSTR), m->m_name); (void) close(mpvect[0]); (void) close(mpvect[1]); (void) close(rpvect[0]); (void) close(rpvect[1]); if (tTd(11, 1)) - dprintf("openmailer: NULL\n"); + sm_dprintf("openmailer: NULL\n"); rcode = EX_OSERR; goto give_up; } @@ -1921,31 +2241,37 @@ tryhost: { int i; int save_errno; + int sff; int new_euid = NO_UID; int new_ruid = NO_UID; int new_gid = NO_GID; + char *user = NULL; struct stat stb; extern int DtableSize; /* clear the events to turn off SIGALRMs */ - clear_events(); + sm_clear_events(); /* Reset global flags */ RestartRequest = NULL; + RestartWorkGroup = false; ShutdownRequest = NULL; PendingSignal = 0; + CurrentPid = getpid(); if (e->e_lockfp != NULL) - (void) close(fileno(e->e_lockfp)); + (void) close(sm_io_getinfo(e->e_lockfp, + SM_IO_WHAT_FD, + NULL)); /* child -- set up input & exec mailer */ - (void) setsignal(SIGALRM, sm_signal_noop); - (void) setsignal(SIGCHLD, SIG_DFL); - (void) setsignal(SIGHUP, SIG_IGN); - (void) setsignal(SIGINT, SIG_IGN); - (void) setsignal(SIGTERM, SIG_DFL); + (void) sm_signal(SIGALRM, sm_signal_noop); + (void) sm_signal(SIGCHLD, SIG_DFL); + (void) sm_signal(SIGHUP, SIG_IGN); + (void) sm_signal(SIGINT, SIG_IGN); + (void) sm_signal(SIGTERM, SIG_DFL); # ifdef SIGUSR1 - (void) setsignal(SIGUSR1, sm_signal_noop); + (void) sm_signal(SIGUSR1, sm_signal_noop); # endif /* SIGUSR1 */ if (m != FileMailer || stat(tochain->q_user, &stb) < 0) @@ -1966,8 +2292,8 @@ tryhost: pwd = sm_getpwnam(contextaddr->q_user); if (pwd != NULL) (void) setusercontext(NULL, - pwd, pwd->pw_uid, - LOGIN_SETRESOURCES|LOGIN_SETPRIORITY); + pwd, pwd->pw_uid, + LOGIN_SETRESOURCES|LOGIN_SETPRIORITY); } # endif /* HASSETUSERCONTEXT */ @@ -1984,15 +2310,16 @@ tryhost: { if (!DontInitGroups) { - char *u = ctladdr->q_ruser; - - if (u == NULL) - u = ctladdr->q_user; + user = ctladdr->q_ruser; + if (user == NULL) + user = ctladdr->q_user; - if (initgroups(u, ctladdr->q_gid) == -1 && suidwarn) + if (initgroups(user, + ctladdr->q_gid) == -1 + && suidwarn) { syserr("openmailer: initgroups(%s, %d) failed", - u, ctladdr->q_gid); + user, ctladdr->q_gid); exit(EX_TEMPFAIL); } } @@ -2001,7 +2328,8 @@ tryhost: GIDSET_T gidset[1]; gidset[0] = ctladdr->q_gid; - if (setgroups(1, gidset) == -1 && suidwarn) + if (setgroups(1, gidset) == -1 + && suidwarn) { syserr("openmailer: setgroups() failed"); exit(EX_TEMPFAIL); @@ -2013,10 +2341,12 @@ tryhost: { if (!DontInitGroups) { - if (initgroups(DefUser, DefGid) == -1 && suidwarn) + user = DefUser; + if (initgroups(DefUser, DefGid) == -1 && + suidwarn) { syserr("openmailer: initgroups(%s, %d) failed", - DefUser, DefGid); + DefUser, DefGid); exit(EX_TEMPFAIL); } } @@ -2025,7 +2355,8 @@ tryhost: GIDSET_T gidset[1]; gidset[0] = DefGid; - if (setgroups(1, gidset) == -1 && suidwarn) + if (setgroups(1, gidset) == -1 + && suidwarn) { syserr("openmailer: setgroups() failed"); exit(EX_TEMPFAIL); @@ -2061,12 +2392,12 @@ tryhost: { expand(m->m_rootdir, buf, sizeof buf, e); if (tTd(11, 20)) - dprintf("openmailer: chroot %s\n", - buf); + sm_dprintf("openmailer: chroot %s\n", + buf); if (chroot(buf) < 0) { syserr("openmailer: Cannot chroot(%s)", - buf); + buf); exit(EX_TEMPFAIL); } if (chdir("/") < 0) @@ -2078,6 +2409,7 @@ tryhost: /* reset user id */ endpwent(); + sm_mbdb_terminate(); if (bitnset(M_SPECIFIC_UID, m->m_flags)) { new_euid = m->m_uid; @@ -2090,7 +2422,21 @@ tryhost: */ if (RealUid != 0 && RealUid != getuid()) + { +# if MAILER_SETUID_METHOD == USE_SETEUID +# if HASSETREUID + if (setreuid(RealUid, geteuid()) < 0) + { + syserr("openmailer: setreuid(%d, %d) failed", + (int) RealUid, (int) geteuid()); + exit(EX_OSERR); + } +# endif /* HASSETREUID */ +# endif /* MAILER_SETUID_METHOD == USE_SETEUID */ +# if MAILER_SETUID_METHOD == USE_SETREUID new_ruid = RealUid; +# endif /* MAILER_SETUID_METHOD == USE_SETREUID */ + } } else if (bitset(S_ISUID, stb.st_mode)) new_ruid = stb.st_uid; @@ -2111,26 +2457,10 @@ tryhost: vendor_set_uid(new_euid); # if MAILER_SETUID_METHOD == USE_SETEUID -# if HASSETREUID - /* - ** Undo the effects of the uid change in main - ** for signal handling. The real uid may - ** be used by mailer in adding a "From " - ** line. - */ - - if (new_ruid != NO_UID && - setreuid(RealUid, geteuid()) < 0) - { - syserr("openmailer: setreuid(%d, %d) failed", - (int) new_ruid, (int) geteuid()); - exit(EX_OSERR); - } -# endif /* HASSETREUID */ if (seteuid(new_euid) < 0 && suidwarn) { syserr("openmailer: seteuid(%ld) failed", - (long) new_euid); + (long) new_euid); exit(EX_TEMPFAIL); } # endif /* MAILER_SETUID_METHOD == USE_SETEUID */ @@ -2138,7 +2468,7 @@ tryhost: if (setreuid(new_ruid, new_euid) < 0 && suidwarn) { syserr("openmailer: setreuid(%ld, %ld) failed", - (long) new_ruid, (long) new_euid); + (long) new_ruid, (long) new_euid); exit(EX_TEMPFAIL); } # endif /* MAILER_SETUID_METHOD == USE_SETREUID */ @@ -2146,7 +2476,7 @@ tryhost: if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn) { syserr("openmailer: setuid(%ld) failed", - (long) new_euid); + (long) new_euid); exit(EX_TEMPFAIL); } # endif /* MAILER_SETUID_METHOD == USE_SETUID */ @@ -2157,15 +2487,15 @@ tryhost: if (setuid(new_ruid) < 0 && suidwarn) { syserr("openmailer: setuid(%ld) failed", - (long) new_ruid); + (long) new_ruid); exit(EX_TEMPFAIL); } } if (tTd(11, 2)) - dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n", - (int) getuid(), (int) geteuid(), - (int) getgid(), (int) getegid()); + sm_dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n", + (int) getuid(), (int) geteuid(), + (int) getgid(), (int) getegid()); /* move into some "safe" directory */ if (m->m_execdir != NULL) @@ -2181,13 +2511,30 @@ tryhost: if (q != NULL) *q++ = ':'; if (tTd(11, 20)) - dprintf("openmailer: trydir %s\n", - buf); + sm_dprintf("openmailer: trydir %s\n", + buf); if (buf[0] != '\0' && chdir(buf) >= 0) break; } } + /* Check safety of program to be run */ + sff = SFF_ROOTOK|SFF_EXECOK; + if (!bitnset(DBS_RUNWRITABLEPROGRAM, + DontBlameSendmail)) + sff |= SFF_NOGWFILES|SFF_NOWWFILES; + if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, + DontBlameSendmail)) + sff |= SFF_NOPATHCHECK; + else + sff |= SFF_SAFEDIRPATH; + ret = safefile(m->m_mailer, getuid(), getgid(), + user, sff, 0, NULL); + if (ret != 0) + sm_syslog(LOG_INFO, e->e_id, + "Warning: program %s unsafe: %s", + m->m_mailer, sm_errstring(ret)); + /* arrange to filter std & diag output of command */ (void) close(rpvect[0]); if (dup2(rpvect[1], STDOUT_FILENO) < 0) @@ -2202,8 +2549,8 @@ tryhost: if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) { syserr("%s... openmailer(%s): cannot dup stdout for stderr", - shortenstring(e->e_to, MAXSHORTSTR), - m->m_name); + shortenstring(e->e_to, MAXSHORTSTR), + m->m_name); _exit(EX_OSERR); } @@ -2212,8 +2559,8 @@ tryhost: if (dup2(mpvect[0], STDIN_FILENO) < 0) { syserr("%s... openmailer(%s): cannot dup pipe %d for stdin", - shortenstring(e->e_to, MAXSHORTSTR), - m->m_name, mpvect[0]); + shortenstring(e->e_to, MAXSHORTSTR), + m->m_name, mpvect[0]); _exit(EX_OSERR); } (void) close(mpvect[0]); @@ -2248,8 +2595,25 @@ tryhost: if (mci == NULL) { - mci = (MCI *) xalloc(sizeof *mci); - memset((char *) mci, '\0', sizeof *mci); + if (clever) + { + /* + ** Allocate from general heap, not + ** envelope rpool, because this mci + ** is going to be cached. + */ + + mci = mci_new(NULL); + } + else + { + /* + ** Prevent a storage leak by allocating + ** this from the envelope rpool. + */ + + mci = mci_new(e->e_rpool); + } } mci->mci_mailer = m; if (clever) @@ -2263,11 +2627,13 @@ tryhost: } mci->mci_pid = pid; (void) close(mpvect[0]); - mci->mci_out = fdopen(mpvect[1], "w"); + mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, + (void *) mpvect[1], SM_IO_WRONLY, + NULL); if (mci->mci_out == NULL) { syserr("deliver: cannot create mailer output channel, fd=%d", - mpvect[1]); + mpvect[1]); (void) close(mpvect[1]); (void) close(rpvect[0]); (void) close(rpvect[1]); @@ -2276,21 +2642,19 @@ tryhost: } (void) close(rpvect[1]); - mci->mci_in = fdopen(rpvect[0], "r"); + mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, + (void *) rpvect[0], SM_IO_RDONLY, + NULL); if (mci->mci_in == NULL) { syserr("deliver: cannot create mailer input channel, fd=%d", mpvect[1]); (void) close(rpvect[0]); - (void) fclose(mci->mci_out); + (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); mci->mci_out = NULL; rcode = EX_OSERR; goto give_up; } - - /* Don't cache non-clever connections */ - if (!clever) - mci->mci_flags |= MCIF_TEMP; } /* @@ -2300,12 +2664,17 @@ tryhost: if (bitnset(M_7BITS, m->m_flags) && (!clever || mci->mci_state == MCIS_OPENING)) mci->mci_flags |= MCIF_7BIT; -#if SMTP if (clever && mci->mci_state != MCIS_CLOSED) { -# if SASL && SFIO +# if STARTTLS || SASL + int dotpos; + char *srvname; + extern SOCKADDR CurHostAddr; +# endif /* STARTTLS || SASL */ + +# if SASL # define DONE_AUTH(f) bitset(MCIF_AUTHACT, f) -# endif /* SASL && SFIO */ +# endif /* SASL */ # if STARTTLS # define DONE_STARTTLS(f) bitset(MCIF_TLSACT, f) # endif /* STARTTLS */ @@ -2313,97 +2682,111 @@ tryhost: # define SET_HELO(f) f |= MCIF_ONLY_EHLO # define CLR_HELO(f) f &= ~MCIF_ONLY_EHLO +# if STARTTLS || SASL + /* don't use CurHostName, it is changed in many places */ + if (mci->mci_host != NULL) + { + srvname = mci->mci_host; + dotpos = strlen(srvname) - 1; + if (dotpos >= 0) + { + if (srvname[dotpos] == '.') + srvname[dotpos] = '\0'; + else + dotpos = -1; + } + } + else + { + srvname = ""; + dotpos = -1; + } + macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"), + srvname); + + /* CurHostAddr is set by makeconnection() and mci_get() */ + if (CurHostAddr.sa.sa_family != 0) + macdefine(&mci->mci_macro, A_TEMP, + macid("{server_addr}"), + anynet_ntoa(&CurHostAddr)); + else + macdefine(&mci->mci_macro, A_PERM, + macid("{server_addr}"), NULL); + + /* undo change of srvname (mci->mci_host) */ + if (dotpos >= 0) + srvname[dotpos] = '.'; -# if STARTTLS || (SASL && SFIO) -reconnect: /* after switching to an authenticated connection */ -# endif /* STARTTLS || (SASL && SFIO) */ +reconnect: /* after switching to an encrypted connection */ +# endif /* STARTTLS || SASL */ + /* set the current connection information */ + e->e_mci = mci; # if SASL mci->mci_saslcap = NULL; # endif /* SASL */ smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags)); CLR_HELO(mci->mci_flags); + if (IS_DLVR_RETURN(e)) + { + /* + ** Check whether other side can deliver e-mail + ** fast enough + */ + + if (!bitset(MCIF_DLVR_BY, mci->mci_flags)) + { + e->e_status = "5.4.7"; + usrerrenh(e->e_status, + "554 Server does not support Deliver By"); + rcode = EX_UNAVAILABLE; + goto give_up; + } + if (e->e_deliver_by > 0 && + e->e_deliver_by - (curtime() - e->e_ctime) < + mci->mci_min_by) + { + e->e_status = "5.4.7"; + usrerrenh(e->e_status, + "554 Message can't be delivered in time; %ld < %ld", + e->e_deliver_by - (curtime() - e->e_ctime), + mci->mci_min_by); + rcode = EX_UNAVAILABLE; + goto give_up; + } + } + # if STARTTLS /* first TLS then AUTH to provide a security layer */ if (mci->mci_state != MCIS_CLOSED && !DONE_STARTTLS(mci->mci_flags)) { int olderrors; - int dotpos; bool usetls; bool saveQuickAbort = QuickAbort; bool saveSuprErrs = SuprErrs; char *host = NULL; -# if _FFR_TLS_CLT1 - char *p; -# endif /* _FFR_TLS_CLT1 */ - char *srvname; - extern SOCKADDR CurHostAddr; rcode = EX_OK; usetls = bitset(MCIF_TLS, mci->mci_flags); -# if _FFR_TLS_CLT1 - if (usetls && - (p = macvalue(macid("{client_flags}", NULL), e)) - != NULL) - { - for (; *p != '\0'; p++) - { - /* look for just this one flag */ - if (*p == D_CLTNOTLS) - { - usetls = FALSE; - break; - } - } - } -# endif /* _FFR_TLS_CLT1 */ + if (usetls) + usetls = !iscltflgset(e, D_NOTLS); - if (mci->mci_host != NULL) - { - srvname = mci->mci_host; - dotpos = strlen(srvname) - 1; - if (dotpos >= 0) - { - if (srvname[dotpos] == '.') - srvname[dotpos] = '\0'; - else - dotpos = -1; - } - } - else - { - srvname = ""; - dotpos = -1; - } - define(macid("{server_name}", NULL), - newstr(srvname), e); - if (CurHostAddr.sa.sa_family != 0) - define(macid("{server_addr}", NULL), - newstr(anynet_ntoa(&CurHostAddr)), e); - else - define(macid("{server_addr}", NULL), NULL, e); if (usetls) { - host = macvalue(macid("{server_name}", NULL), - e); -# if _FFR_TLS_O_T + host = macvalue(macid("{server_name}"), e); olderrors = Errors; - QuickAbort = FALSE; - SuprErrs = TRUE; - if (rscheck("try_tls", srvname, NULL, - e, TRUE, FALSE, 8, host) != EX_OK + QuickAbort = false; + SuprErrs = true; + if (rscheck("try_tls", host, NULL, e, true, + false, 7, host, NOQID) != EX_OK || Errors > olderrors) - usetls = FALSE; + usetls = false; SuprErrs = saveSuprErrs; QuickAbort = saveQuickAbort; -# endif /* _FFR_TLS_O_T */ } - /* undo change of srvname */ - if (dotpos >= 0) - srvname[dotpos] = '.'; if (usetls) { if ((rcode = starttls(m, mci, e)) == EX_OK) @@ -2421,6 +2804,7 @@ reconnect: /* after switching to an authenticated connection */ ** or abort? How to decide? ** set a macro and call a ruleset. */ + mci->mci_flags &= ~MCIF_TLS; switch (rcode) { @@ -2442,21 +2826,16 @@ reconnect: /* after switching to an authenticated connection */ s = "FAILURE"; rcode = EX_TEMPFAIL; } - define(macid("{verify}", NULL), - newstr(s), e); + macdefine(&e->e_macro, A_PERM, + macid("{verify}"), s); } } - else if (mci->mci_ssl != NULL) - { - /* active TLS connection, use that data */ - (void) tls_get_info(mci->mci_ssl, e, FALSE, - mci->mci_host, FALSE); - } else - define(macid("{verify}", NULL), "NONE", e); + macdefine(&e->e_macro, A_PERM, + macid("{verify}"), "NONE"); olderrors = Errors; - QuickAbort = FALSE; - SuprErrs = TRUE; + QuickAbort = false; + SuprErrs = true; /* ** rcode == EX_SOFTWARE is special: @@ -2466,9 +2845,11 @@ reconnect: /* after switching to an authenticated connection */ ** to log the problem and return an appropriate ** error code. */ + if (rscheck("tls_server", - macvalue(macid("{verify}", NULL), e), - NULL, e, TRUE, TRUE, 6, host) != EX_OK || + macvalue(macid("{verify}"), e), + NULL, e, true, true, 5, host, + NOQID) != EX_OK || Errors > olderrors || rcode == EX_SOFTWARE) { @@ -2478,13 +2859,14 @@ reconnect: /* after switching to an authenticated connection */ if (ISSMTPCODE(MsgBuf) && extenhsc(MsgBuf + 4, ' ', enhsc) > 0) { - p = newstr(MsgBuf); + p = sm_rpool_strdup_x(e->e_rpool, + MsgBuf); } else { p = "403 4.7.0 server not authenticated."; - (void) strlcpy(enhsc, "4.7.0", - sizeof enhsc); + (void) sm_strlcpy(enhsc, "4.7.0", + sizeof enhsc); } SuprErrs = saveSuprErrs; QuickAbort = saveQuickAbort; @@ -2495,7 +2877,8 @@ reconnect: /* after switching to an authenticated connection */ mci->mci_state = MCIS_QUITING; if (mci->mci_in != NULL) { - (void) fclose(mci->mci_in); + (void) sm_io_close(mci->mci_in, + SM_TIME_DEFAULT); mci->mci_in = NULL; } mci->mci_flags &= ~MCIF_TLSACT; @@ -2513,13 +2896,15 @@ reconnect: /* after switching to an authenticated connection */ /* temp or permanent failure? */ rcode = (*p == '4') ? EX_TEMPFAIL : EX_UNAVAILABLE; - mci_setstat(mci, rcode, newstr(enhsc), p); + mci_setstat(mci, rcode, enhsc, p); /* ** hack to get the error message into ** the envelope (done in giveresponse()) */ - (void) strlcpy(SmtpError, p, sizeof SmtpError); + + (void) sm_strlcpy(SmtpError, p, + sizeof SmtpError); } QuickAbort = saveQuickAbort; SuprErrs = saveSuprErrs; @@ -2531,79 +2916,85 @@ reconnect: /* after switching to an authenticated connection */ goto reconnect; } } - else if (mci->mci_ssl != NULL) - { - /* active TLS connection, use that data */ - (void) tls_get_info(mci->mci_ssl, e, FALSE, - mci->mci_host, FALSE); - } # endif /* STARTTLS */ # if SASL /* if other server supports authentication let's authenticate */ if (mci->mci_state != MCIS_CLOSED && mci->mci_saslcap != NULL && -# if SFIO - !DONE_AUTH(mci->mci_flags) && -# endif /* SFIO */ - SASLInfo != NULL) + !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH)) { - /* - ** should we require some minimum authentication? - ** XXX ignore result? - */ - if (smtpauth(m, mci, e) == EX_OK) + /* Should we require some minimum authentication? */ + if ((ret = smtpauth(m, mci, e)) == EX_OK) { -# if SFIO int result; - sasl_ssf_t *ssf; + sasl_ssf_t *ssf = NULL; - /* get security strength (features) */ + /* Get security strength (features) */ result = sasl_getprop(mci->mci_conn, SASL_SSF, (void **) &ssf); + + /* XXX authid? */ if (LogLevel > 9) sm_syslog(LOG_INFO, NOQID, - "SASL: outgoing connection to %.64s: mech=%.16s, bits=%d", + "AUTH=client, relay=%.100s, mech=%.16s, bits=%d", mci->mci_host, - macvalue(macid("{auth_type}", - NULL), e), - result == SASL_OK ? *ssf - : 0); + macvalue(macid("{auth_type}"), e), + result == SASL_OK ? *ssf : 0); /* - ** only switch to encrypted connection + ** Only switch to encrypted connection ** if a security layer has been negotiated */ + if (result == SASL_OK && *ssf > 0) { /* - ** convert sfio stuff to use SASL - ** check return values - ** if the call fails, - ** fall back to unencrypted version - ** unless some cf option requires - ** encryption then the connection must - ** be aborted + ** Convert I/O layer to use SASL. + ** If the call fails, the connection + ** is aborted. */ - if (sfdcsasl(mci->mci_in, mci->mci_out, + + if (sfdcsasl(&mci->mci_in, + &mci->mci_out, mci->mci_conn) == 0) { - SET_HELO(mci->mci_flags); mci->mci_flags &= ~MCIF_EXTENS; - mci->mci_flags |= MCIF_AUTHACT; + mci->mci_flags |= MCIF_AUTHACT| + MCIF_ONLY_EHLO; goto reconnect; } - syserr("SASL TLS switch failed in client"); + syserr("AUTH TLS switch failed in client"); } /* else? XXX */ -# endif /* SFIO */ mci->mci_flags |= MCIF_AUTHACT; } + else if (ret == EX_TEMPFAIL) + { + if (LogLevel > 8) + sm_syslog(LOG_ERR, NOQID, + "AUTH=client, relay=%.100s, temporary failure, connection abort", + mci->mci_host); + smtpquit(m, mci, e); + + /* avoid bogus error msg */ + mci->mci_errno = 0; + rcode = EX_TEMPFAIL; + mci_setstat(mci, rcode, "4.7.1", p); + + /* + ** hack to get the error message into + ** the envelope (done in giveresponse()) + */ + + (void) sm_strlcpy(SmtpError, + "Temporary AUTH failure", + sizeof SmtpError); + } } # endif /* SASL */ } -#endif /* SMTP */ do_transfer: /* clear out per-message flags from connection structure */ @@ -2618,13 +3009,13 @@ do_transfer: if (bitnset(M_MAKE8BIT, m->m_flags) && !bitset(MCIF_7BIT, mci->mci_flags) && (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && - (strcasecmp(p, "quoted-printable") == 0 || - strcasecmp(p, "base64") == 0) && + (sm_strcasecmp(p, "quoted-printable") == 0 || + sm_strcasecmp(p, "base64") == 0) && (p = hvalue("Content-Type", e->e_header)) != NULL) { /* may want to convert 7 -> 8 */ /* XXX should really parse it here -- and use a class XXX */ - if (strncasecmp(p, "text/plain", 10) == 0 && + if (sm_strncasecmp(p, "text/plain", 10) == 0 && (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) mci->mci_flags |= MCIF_CVT7TO8; } @@ -2632,8 +3023,8 @@ do_transfer: if (tTd(11, 1)) { - dprintf("openmailer: "); - mci_dump(mci, FALSE); + sm_dprintf("openmailer: "); + mci_dump(mci, false); } if (mci->mci_state != MCIS_OPEN) @@ -2641,25 +3032,21 @@ do_transfer: /* couldn't open the mailer */ rcode = mci->mci_exitstat; errno = mci->mci_errno; -#if NAMED_BIND SM_SET_H_ERRNO(mci->mci_herrno); -#endif /* NAMED_BIND */ if (rcode == EX_OK) { /* shouldn't happen */ syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s", - (u_long) mci, rcode, errno, mci->mci_state, - firstsig); - mci_dump_all(TRUE); + (unsigned long) mci, rcode, errno, + mci->mci_state, firstsig); + mci_dump_all(true); rcode = EX_SOFTWARE; } -#if DAEMON else if (nummxhosts > hostnum) { /* try next MX site */ goto tryhost; } -#endif /* DAEMON */ } else if (!clever) { @@ -2673,80 +3060,107 @@ do_transfer: /* get the exit status */ rcode = endmailer(mci, e, pv); - if (rcode == EX_TEMPFAIL && - SmtpError[0] == '\0') + if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0') { /* ** Need an e_message for mailq display. ** We set SmtpError as */ - snprintf(SmtpError, sizeof SmtpError, - "%s mailer (%s) exited with EX_TEMPFAIL", - m->m_name, m->m_mailer); + (void) sm_snprintf(SmtpError, sizeof SmtpError, + "%s mailer (%s) exited with EX_TEMPFAIL", + m->m_name, m->m_mailer); } } else -#if SMTP { /* ** Send the MAIL FROM: protocol */ + /* XXX this isn't pipelined... */ rcode = smtpmailfrom(m, mci, e); if (rcode == EX_OK) { - register char *t = tobuf; register int i; +# if PIPELINING + ADDRESS *volatile pchain; +# endif /* PIPELINING */ /* send the recipient list */ tobuf[0] = '\0'; + mci->mci_retryrcpt = false; + mci->mci_tolist = tobuf; +# if PIPELINING + pchain = NULL; + mci->mci_nextaddr = NULL; +# endif /* PIPELINING */ for (to = tochain; to != NULL; to = to->q_tchain) { - e->e_to = to->q_paddr; -#if !_FFR_DYNAMIC_TOBUF - if (strlen(to->q_paddr) + - (t - tobuf) + 2 > sizeof tobuf) - { - /* not enough room */ + if (!QS_IS_UNMARKED(to->q_state)) continue; - } -#endif /* !_FFR_DYNAMIC_TOBUF */ + /* mark recipient state as "ok so far" */ + to->q_state = QS_OK; + e->e_to = to->q_paddr; # if STARTTLS -# if _FFR_TLS_RCPT i = rscheck("tls_rcpt", to->q_user, NULL, e, - TRUE, TRUE, 4, mci->mci_host); + true, true, 3, mci->mci_host, + e->e_id); if (i != EX_OK) { /* avoid bogus error msg */ errno = 0; - markfailure(e, to, mci, i, FALSE); - giveresponse(i, to->q_status, m, - mci, ctladdr, xstart, e); + markfailure(e, to, mci, i, false); + giveresponse(i, to->q_status, m, mci, + ctladdr, xstart, e, to); + if (i == EX_TEMPFAIL) + { + mci->mci_retryrcpt = true; + to->q_state = QS_RETRY; + } continue; } -# endif /* _FFR_TLS_RCPT */ # endif /* STARTTLS */ - if ((i = smtprcpt(to, m, mci, e)) != EX_OK) + i = smtprcpt(to, m, mci, e, ctladdr, xstart); +# if PIPELINING + if (bitset(MCIF_PIPELINED, mci->mci_flags)) { - markfailure(e, to, mci, i, FALSE); - giveresponse(i, to->q_status, m, - mci, ctladdr, xstart, e); + /* + ** Add new element to list of + ** recipients for pipelining. + */ + + to->q_pchain = NULL; + if (mci->mci_nextaddr == NULL) + mci->mci_nextaddr = to; + if (pchain == NULL) + pchain = to; + else + { + pchain->q_pchain = to; + pchain = pchain->q_pchain; + } } - else +# endif /* PIPELINING */ + if (i != EX_OK) { - *t++ = ','; - for (p = to->q_paddr; *p; *t++ = *p++) - continue; - *t = '\0'; + markfailure(e, to, mci, i, false); + giveresponse(i, to->q_status, m, mci, + ctladdr, xstart, e, to); + if (i == EX_TEMPFAIL) + to->q_state = QS_RETRY; } } - /* now send the data */ - if (tobuf[0] == '\0') + /* No recipients in list and no missing responses? */ + if (tobuf[0] == '\0' +# if PIPELINING + && mci->mci_nextaddr == NULL +# endif /* PIPELINING */ + ) { rcode = EX_OK; e->e_to = NULL; @@ -2756,24 +3170,15 @@ do_transfer: else { e->e_to = tobuf + 1; - rcode = smtpdata(m, mci, e); + rcode = smtpdata(m, mci, e, ctladdr, xstart); } } -# if DAEMON if (rcode == EX_TEMPFAIL && nummxhosts > hostnum) { /* try next MX site */ goto tryhost; } -# endif /* DAEMON */ - } -#else /* SMTP */ - { - syserr("554 5.3.5 deliver: need SMTP compiled to use clever mailer"); - rcode = EX_CONFIG; - goto give_up; } -#endif /* SMTP */ #if NAMED_BIND if (ConfigLevel < 2) _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ @@ -2790,15 +3195,14 @@ do_transfer: */ give_up: -#if SMTP if (bitnset(M_LMTP, m->m_flags)) { lmtp_rcode = rcode; tobuf[0] = '\0'; - anyok = FALSE; + anyok = false; + strsize = 0; } else -#endif /* SMTP */ anyok = rcode == EX_OK; for (to = tochain; to != NULL; to = to->q_tchain) @@ -2807,7 +3211,6 @@ do_transfer: if (!QS_IS_OK(to->q_state)) continue; -#if SMTP /* if running LMTP, get the status for each address */ if (bitnset(M_LMTP, m->m_flags)) { @@ -2815,44 +3218,30 @@ do_transfer: rcode = smtpgetstat(m, mci, e); if (rcode == EX_OK) { -#if _FFR_DYNAMIC_TOBUF - (void) strlcat(tobuf, ",", tobufsize); - (void) strlcat(tobuf, to->q_paddr, tobufsize); -#else /* _FFR_DYNAMIC_TOBUF */ - if (strlen(to->q_paddr) + - strlen(tobuf) + 2 > sizeof tobuf) - { - syserr("LMTP tobuf overflow"); - } - else - { - (void) strlcat(tobuf, ",", - sizeof tobuf); - (void) strlcat(tobuf, to->q_paddr, - sizeof tobuf); - } -#endif /* _FFR_DYNAMIC_TOBUF */ - anyok = TRUE; + strsize += sm_strlcat2(tobuf + strsize, ",", + to->q_paddr, + tobufsize - strsize); + SM_ASSERT(strsize < tobufsize); + anyok = true; } else { e->e_to = to->q_paddr; - markfailure(e, to, mci, rcode, TRUE); + markfailure(e, to, mci, rcode, true); giveresponse(rcode, to->q_status, m, mci, - ctladdr, xstart, e); + ctladdr, xstart, e, to); e->e_to = tobuf + 1; continue; } } else -#endif /* SMTP */ { /* mark bad addresses */ if (rcode != EX_OK) { if (goodmxfound && rcode == EX_NOHOST) rcode = EX_TEMPFAIL; - markfailure(e, to, mci, rcode, TRUE); + markfailure(e, to, mci, rcode, true); continue; } } @@ -2862,37 +3251,63 @@ do_transfer: to->q_statdate = curtime(); e->e_nsent++; -#if QUEUE /* ** Checkpoint the send list every few addresses */ if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval) { - queueup(e, FALSE); + queueup(e, false, false); e->e_nsent = 0; } -#endif /* QUEUE */ if (bitnset(M_LOCALMAILER, m->m_flags) && bitset(QPINGONSUCCESS, to->q_flags)) { to->q_flags |= QDELIVERED; to->q_status = "2.1.5"; - fprintf(e->e_xfp, "%s... Successfully delivered\n", - to->q_paddr); + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + "%s... Successfully delivered\n", + to->q_paddr); } else if (bitset(QPINGONSUCCESS, to->q_flags) && bitset(QPRIMARY, to->q_flags) && !bitset(MCIF_DSN, mci->mci_flags)) { to->q_flags |= QRELAYED; - fprintf(e->e_xfp, "%s... relayed; expect no further notifications\n", - to->q_paddr); + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + "%s... relayed; expect no further notifications\n", + to->q_paddr); + } + else if (IS_DLVR_NOTIFY(e) && + !bitset(MCIF_DLVR_BY, mci->mci_flags) && + bitset(QPRIMARY, to->q_flags) && + (!bitset(QHASNOTIFY, to->q_flags) || + bitset(QPINGONSUCCESS, to->q_flags) || + bitset(QPINGONFAILURE, to->q_flags) || + bitset(QPINGONDELAY, to->q_flags))) + { + /* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */ + to->q_flags |= QBYNRELAY; + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + "%s... Deliver-by notify: relayed\n", + to->q_paddr); + } + else if (IS_DLVR_TRACE(e) && + (!bitset(QHASNOTIFY, to->q_flags) || + bitset(QPINGONSUCCESS, to->q_flags) || + bitset(QPINGONFAILURE, to->q_flags) || + bitset(QPINGONDELAY, to->q_flags)) && + bitset(QPRIMARY, to->q_flags)) + { + /* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */ + to->q_flags |= QBYTRACE; + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + "%s... Deliver-By trace: relayed\n", + to->q_paddr); } } -#if SMTP if (bitnset(M_LMTP, m->m_flags)) { /* @@ -2904,44 +3319,78 @@ do_transfer: e->e_statmsg = NULL; /* reset the mci state for the next transaction */ - if (mci != NULL && mci->mci_state == MCIS_ACTIVE) + if (mci != NULL && + (mci->mci_state == MCIS_MAIL || + mci->mci_state == MCIS_RCPT || + mci->mci_state == MCIS_DATA)) mci->mci_state = MCIS_OPEN; } -#endif /* SMTP */ if (tobuf[0] != '\0') - giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e); + { + giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain); + if (tochain->q_message != NULL && + !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK) + { + for (to = tochain->q_tchain; to != NULL; + to = to->q_tchain) + { + /* see if address already marked */ + if (QS_IS_QUEUEUP(to->q_state) && + to->q_message == NULL) + to->q_message = sm_rpool_strdup_x(e->e_rpool, + tochain->q_message); + } + } + } if (anyok) - markstats(e, tochain, FALSE); + markstats(e, tochain, false); mci_store_persistent(mci); -#if SMTP + /* Some recipients were tempfailed, try them on the next host */ + if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum) + { + /* try next MX site */ + goto tryhost; + } + /* now close the connection */ if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED && !bitset(MCIF_CACHED, mci->mci_flags)) smtpquit(m, mci, e); -#endif /* SMTP */ - /* - ** Restore state and return. - */ - -#if XDEBUG +cleanup: ; + } + SM_FINALLY { + /* + ** Restore state and return. + */ +#if XDEBUG char wbuf[MAXLINE]; /* make absolutely certain 0, 1, and 2 are in use */ - snprintf(wbuf, sizeof wbuf, "%s... end of deliver(%s)", - e->e_to == NULL ? "NO-TO-LIST" - : shortenstring(e->e_to, MAXSHORTSTR), - m->m_name); + (void) sm_snprintf(wbuf, sizeof wbuf, + "%s... end of deliver(%s)", + e->e_to == NULL ? "NO-TO-LIST" + : shortenstring(e->e_to, + MAXSHORTSTR), + m->m_name); checkfd012(wbuf); - } #endif /* XDEBUG */ - errno = 0; - define('g', (char *) NULL, e); - e->e_to = NULL; + errno = 0; + + /* + ** It was originally necessary to set macro 'g' to NULL + ** because it previously pointed to an auto buffer. + ** We don't do this any more, so this may be unnecessary. + */ + + macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL); + e->e_to = NULL; + } + SM_END_TRY return rcode; } @@ -2964,7 +3413,7 @@ do_transfer: ** the message will be queued, as appropriate. */ -static void +void markfailure(e, q, mci, rcode, ovr) register ENVELOPE *e; register ADDRESS *q; @@ -2994,9 +3443,10 @@ markfailure(e, q, mci, rcode, ovr) /* find most specific error code possible */ if (mci != NULL && mci->mci_status != NULL) { - status = mci->mci_status; + status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status); if (mci->mci_rstatus != NULL) - rstatus = newstr(mci->mci_rstatus); + rstatus = sm_rpool_strdup_x(e->e_rpool, + mci->mci_rstatus); else rstatus = NULL; } @@ -3059,18 +3509,18 @@ markfailure(e, q, mci, rcode, ovr) } if (rcode != EX_OK && q->q_rstatus == NULL && q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL && - strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0) + sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0) { char buf[16]; - (void) snprintf(buf, sizeof buf, "%d", rcode); - q->q_rstatus = newstr(buf); + (void) sm_snprintf(buf, sizeof buf, "%d", rcode); + q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf); } q->q_statdate = curtime(); if (CurHostName != NULL && CurHostName[0] != '\0' && mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags)) - q->q_statmta = newstr(CurHostName); + q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName); } /* ** ENDMAILER -- Wait for mailer to terminate. @@ -3117,26 +3567,26 @@ endmailer(mci, e, pv) int st; int save_errno = errno; char buf[MAXLINE]; - EVENT *ev = NULL; + SM_EVENT *ev = NULL; mci_unlock_host(mci); /* close output to mailer */ if (mci->mci_out != NULL) - (void) fclose(mci->mci_out); + (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); /* copy any remaining input to transcript */ if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR && e->e_xfp != NULL) { while (sfgets(buf, sizeof buf, mci->mci_in, - TimeOuts.to_quit, "Draining Input") != NULL) - (void) fputs(buf, e->e_xfp); + TimeOuts.to_quit, "Draining Input") != NULL) + (void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf); } #if SASL - /* shutdown SASL */ + /* close SASL connection */ if (bitset(MCIF_AUTHACT, mci->mci_flags)) { sasl_dispose(&mci->mci_conn); @@ -3151,7 +3601,7 @@ endmailer(mci, e, pv) /* now close the input */ if (mci->mci_in != NULL) - (void) fclose(mci->mci_in); + (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); mci->mci_in = mci->mci_out = NULL; mci->mci_state = MCIS_CLOSED; @@ -3165,8 +3615,8 @@ endmailer(mci, e, pv) if (mci->mci_mailer->m_wait > 0) { if (setjmp(EndWaitTimeout) == 0) - ev = setevent(mci->mci_mailer->m_wait, - endwaittimeout, 0); + ev = sm_setevent(mci->mci_mailer->m_wait, + endwaittimeout, 0); else { syserr("endmailer %s: wait timeout (%ld)", @@ -3180,7 +3630,7 @@ endmailer(mci, e, pv) st = waitfor(mci->mci_pid); save_errno = errno; if (ev != NULL) - clrevent(ev); + sm_clrevent(ev); errno = save_errno; if (st == -1) @@ -3206,10 +3656,11 @@ endmailer(mci, e, pv) { register char **av; - fprintf(e->e_xfp, "Arguments:"); + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:"); for (av = pv; *av != NULL; av++) - fprintf(e->e_xfp, " %s", *av); - fprintf(e->e_xfp, "\n"); + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s", + *av); + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n"); } ExitStat = EX_TEMPFAIL; @@ -3231,6 +3682,7 @@ endmailer(mci, e, pv) ** xstart -- the transaction start time, for computing ** transaction delays. ** e -- the current envelope. +** to -- the current recipient (NULL if none). ** ** Returns: ** none. @@ -3241,7 +3693,7 @@ endmailer(mci, e, pv) */ void -giveresponse(status, dsn, m, mci, ctladdr, xstart, e) +giveresponse(status, dsn, m, mci, ctladdr, xstart, e, to) int status; char *dsn; register MAILER *m; @@ -3249,15 +3701,15 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e) ADDRESS *ctladdr; time_t xstart; ENVELOPE *e; + ADDRESS *to; { register const char *statmsg; - extern char *SysExMsg[]; - register int i; int errnum = errno; int off = 4; - extern int N_SysEx; + bool usestat = false; char dsnbuf[ENHSCLEN]; char buf[MAXLINE]; + char *exmsg; if (e == NULL) syserr("giveresponse: null envelope"); @@ -3266,48 +3718,43 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e) ** Compute status message from code. */ - i = status - EX__BASE; + exmsg = sm_sysexmsg(status); if (status == 0) { statmsg = "250 2.0.0 Sent"; if (e->e_statmsg != NULL) { - (void) snprintf(buf, sizeof buf, "%s (%s)", - statmsg, - shortenstring(e->e_statmsg, 403)); + (void) sm_snprintf(buf, sizeof buf, "%s (%s)", + statmsg, + shortenstring(e->e_statmsg, 403)); statmsg = buf; } } - else if (i < 0 || i >= N_SysEx) + else if (exmsg == NULL) { - (void) snprintf(buf, sizeof buf, - "554 5.3.0 unknown mailer error %d", - status); + (void) sm_snprintf(buf, sizeof buf, + "554 5.3.0 unknown mailer error %d", + status); status = EX_UNAVAILABLE; statmsg = buf; + usestat = true; } else if (status == EX_TEMPFAIL) { char *bp = buf; - snprintf(bp, SPACELEFT(buf, bp), "%s", SysExMsg[i] + 1); + (void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp)); bp += strlen(bp); #if NAMED_BIND if (h_errno == TRY_AGAIN) - statmsg = errstring(h_errno+E_DNSBASE); + statmsg = sm_errstring(h_errno + E_DNSBASE); else #endif /* NAMED_BIND */ { if (errnum != 0) - statmsg = errstring(errnum); + statmsg = sm_errstring(errnum); else - { -#if SMTP statmsg = SmtpError; -#else /* SMTP */ - statmsg = NULL; -#endif /* SMTP */ - } } if (statmsg != NULL && statmsg[0] != '\0') { @@ -3333,33 +3780,39 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e) #endif /* EHOSTUNREACH */ if (mci->mci_host != NULL) { - snprintf(bp, SPACELEFT(buf, bp), - ": %s", mci->mci_host); + (void) sm_strlcpyn(bp, + SPACELEFT(buf, bp), + 2, ": ", + mci->mci_host); bp += strlen(bp); } break; } - snprintf(bp, SPACELEFT(buf, bp), ": %s", statmsg); + (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ", + statmsg); + usestat = true; } statmsg = buf; } #if NAMED_BIND else if (status == EX_NOHOST && h_errno != 0) { - statmsg = errstring(h_errno + E_DNSBASE); - (void) snprintf(buf, sizeof buf, "%s (%s)", - SysExMsg[i] + 1, statmsg); + statmsg = sm_errstring(h_errno + E_DNSBASE); + (void) sm_snprintf(buf, sizeof buf, "%s (%s)", exmsg + 1, + statmsg); statmsg = buf; + usestat = true; } #endif /* NAMED_BIND */ else { - statmsg = SysExMsg[i]; + statmsg = exmsg; if (*statmsg++ == ':' && errnum != 0) { - (void) snprintf(buf, sizeof buf, "%s: %s", - statmsg, errstring(errnum)); + (void) sm_snprintf(buf, sizeof buf, "%s: %s", statmsg, + sm_errstring(errnum)); statmsg = buf; + usestat = true; } } @@ -3375,8 +3828,8 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e) { if (dsn == NULL) { - snprintf(dsnbuf, sizeof dsnbuf, - "%.*s", off, statmsg + 4); + (void) sm_snprintf(dsnbuf, sizeof dsnbuf, + "%.*s", off, statmsg + 4); dsn = dsnbuf; } off += 5; @@ -3387,7 +3840,8 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e) } message("%s", statmsg + off); if (status == EX_TEMPFAIL && e->e_xfp != NULL) - fprintf(e->e_xfp, "%s\n", &MsgBuf[4]); + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n", + &MsgBuf[4]); } else { @@ -3399,18 +3853,21 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e) { if (dsn == NULL) { - snprintf(dsnbuf, sizeof dsnbuf, - "%.*s", off, statmsg + 4); + (void) sm_snprintf(dsnbuf, sizeof dsnbuf, + "%.*s", off, statmsg + 4); dsn = dsnbuf; } off += 5; - (void) strlcpy(mbuf, statmsg, off); - (void) strlcat(mbuf, " %s", sizeof mbuf); + + /* copy only part of statmsg to mbuf */ + (void) sm_strlcpy(mbuf, statmsg, off); + (void) sm_strlcat(mbuf, " %s", sizeof mbuf); } else { dsnbuf[0] = '\0'; - (void) snprintf(mbuf, sizeof mbuf, "%.3s %%s", statmsg); + (void) sm_snprintf(mbuf, sizeof mbuf, "%.3s %%s", + statmsg); off = 4; } usrerr(mbuf, &statmsg[off]); @@ -3428,23 +3885,27 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e) logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e); if (tTd(11, 2)) - dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s\n", - status, - dsn == NULL ? "<NULL>" : dsn, - e->e_message == NULL ? "<NULL>" : e->e_message); + sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n", + status, + dsn == NULL ? "<NULL>" : dsn, + e->e_message == NULL ? "<NULL>" : e->e_message, + errnum); if (status != EX_TEMPFAIL) setstat(status); if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL)) + e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off); + if (status != EX_OK && to != NULL && to->q_message == NULL) { - if (e->e_message != NULL) - sm_free(e->e_message); - e->e_message = newstr(statmsg + off); + if (!usestat && e->e_message != NULL) + to->q_message = sm_rpool_strdup_x(e->e_rpool, + e->e_message); + else + to->q_message = sm_rpool_strdup_x(e->e_rpool, + statmsg + off); } errno = 0; -#if NAMED_BIND SM_SET_H_ERRNO(0); -#endif /* NAMED_BIND */ } /* ** LOGDELIVERY -- log the delivery in the system log @@ -3484,7 +3945,7 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e) register char *bp; register char *p; int l; - time_t now; + time_t now = curtime(); char buf[1024]; #if (SYSLOG_BUFSIZE) >= 256 @@ -3492,68 +3953,65 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e) bp = buf; if (ctladdr != NULL) { - snprintf(bp, SPACELEFT(buf, bp), ", ctladdr=%s", - shortenstring(ctladdr->q_paddr, 83)); + (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=", + shortenstring(ctladdr->q_paddr, 83)); bp += strlen(bp); if (bitset(QGOODUID, ctladdr->q_flags)) { - (void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", - (int) ctladdr->q_uid, - (int) ctladdr->q_gid); + (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", + (int) ctladdr->q_uid, + (int) ctladdr->q_gid); bp += strlen(bp); } } /* delay & xdelay: max 41 bytes */ - now = curtime(); - snprintf(bp, SPACELEFT(buf, bp), ", delay=%s", - pintvl(now - e->e_ctime, TRUE)); + (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=", + pintvl(now - e->e_ctime, true)); bp += strlen(bp); if (xstart != (time_t) 0) { - snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s", - pintvl(now - xstart, TRUE)); + (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", + pintvl(now - xstart, true)); bp += strlen(bp); } /* mailer: assume about 19 bytes (max 10 byte mailer name) */ if (m != NULL) { - snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", m->m_name); + (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", + m->m_name); bp += strlen(bp); } /* pri: changes with each delivery attempt */ - snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld", e->e_msgpriority); + (void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld", + e->e_msgpriority); bp += strlen(bp); /* relay: max 66 bytes for IPv4 addresses */ if (mci != NULL && mci->mci_host != NULL) { -# if DAEMON extern SOCKADDR CurHostAddr; -# endif /* DAEMON */ - snprintf(bp, SPACELEFT(buf, bp), ", relay=%s", - shortenstring(mci->mci_host, 40)); + (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=", + shortenstring(mci->mci_host, 40)); bp += strlen(bp); -# if DAEMON if (CurHostAddr.sa.sa_family != 0) { - snprintf(bp, SPACELEFT(buf, bp), " [%s]", - anynet_ntoa(&CurHostAddr)); + (void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]", + anynet_ntoa(&CurHostAddr)); } -# endif /* DAEMON */ } else if (strcmp(status, "queued") != 0) { p = macvalue('h', e); if (p != NULL && p[0] != '\0') { - snprintf(bp, SPACELEFT(buf, bp), ", relay=%s", - shortenstring(p, 40)); + (void) sm_snprintf(bp, SPACELEFT(buf, bp), + ", relay=%s", shortenstring(p, 40)); } } bp += strlen(bp); @@ -3561,8 +4019,8 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e) /* dsn */ if (dsn != NULL && *dsn != '\0') { - snprintf(bp, SPACELEFT(buf, bp), ", dsn=%s", - shortenstring(dsn, ENHSCLEN)); + (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=", + shortenstring(dsn, ENHSCLEN)); bp += strlen(bp); } @@ -3581,23 +4039,25 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e) { /* desperation move -- truncate data */ bp = buf + sizeof buf - ((STATLEN) + 17); - (void) strlcpy(bp, "...", SPACELEFT(buf, bp)); + (void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp)); bp += 3; } - (void) strlcpy(bp, ", stat=", SPACELEFT(buf, bp)); + (void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp)); bp += strlen(bp); - (void) strlcpy(bp, shortenstring(status, STATLEN), SPACELEFT(buf, bp)); + (void) sm_strlcpy(bp, shortenstring(status, STATLEN), + SPACELEFT(buf, bp)); /* id, to: max 13 + TOBUFSIZE bytes */ l = SYSLOG_BUFSIZE - 100 - strlen(buf); + if (l < 0) + l = 0; p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; - while (strlen(p) >= (SIZE_T) l) + while (strlen(p) >= l) { register char *q; -#if _FFR_DYNAMIC_TOBUF for (q = p + l; q > p; q--) { if (*q == ',') @@ -3605,32 +4065,22 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e) } if (p == q) break; -#else /* _FFR_DYNAMIC_TOBUF */ - q = strchr(p + l, ','); - if (q == NULL) - break; -#endif /* _FFR_DYNAMIC_TOBUF */ - - sm_syslog(LOG_INFO, e->e_id, - "to=%.*s [more]%s", + sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s", (int) (++q - p), p, buf); p = q; } -#if _FFR_DYNAMIC_TOBUF sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf); -#else /* _FFR_DYNAMIC_TOBUF */ - sm_syslog(LOG_INFO, e->e_id, "to=%s%s", p, buf); -#endif /* _FFR_DYNAMIC_TOBUF */ #else /* (SYSLOG_BUFSIZE) >= 256 */ l = SYSLOG_BUFSIZE - 85; + if (l < 0) + l = 0; p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; - while (strlen(p) >= (SIZE_T) l) + while (strlen(p) >= l) { register char *q; -#if _FFR_DYNAMIC_TOBUF for (q = p + l; q > p; q--) { if (*q == ',') @@ -3638,51 +4088,42 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e) } if (p == q) break; -#else /* _FFR_DYNAMIC_TOBUF */ - q = strchr(p + l, ','); - if (q == NULL) - break; -#endif /* _FFR_DYNAMIC_TOBUF */ - sm_syslog(LOG_INFO, e->e_id, - "to=%.*s [more]", + sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]", (int) (++q - p), p); p = q; } -#if _FFR_DYNAMIC_TOBUF sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p); -#else /* _FFR_DYNAMIC_TOBUF */ - sm_syslog(LOG_INFO, e->e_id, "to=%s", p); -#endif /* _FFR_DYNAMIC_TOBUF */ if (ctladdr != NULL) { bp = buf; - snprintf(bp, SPACELEFT(buf, bp), "ctladdr=%s", - shortenstring(ctladdr->q_paddr, 83)); + (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=", + shortenstring(ctladdr->q_paddr, 83)); bp += strlen(bp); if (bitset(QGOODUID, ctladdr->q_flags)) { - (void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", - ctladdr->q_uid, ctladdr->q_gid); + (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", + ctladdr->q_uid, ctladdr->q_gid); bp += strlen(bp); } sm_syslog(LOG_INFO, e->e_id, "%s", buf); } bp = buf; - snprintf(bp, SPACELEFT(buf, bp), "delay=%s", - pintvl(now - e->e_ctime, TRUE)); + (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=", + pintvl(now - e->e_ctime, true)); bp += strlen(bp); if (xstart != (time_t) 0) { - snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s", - pintvl(now - xstart, TRUE)); + (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", + pintvl(now - xstart, true)); bp += strlen(bp); } if (m != NULL) { - snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", m->m_name); + (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", + m->m_name); bp += strlen(bp); } sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); @@ -3691,24 +4132,22 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e) bp = buf; if (mci != NULL && mci->mci_host != NULL) { -# if DAEMON extern SOCKADDR CurHostAddr; -# endif /* DAEMON */ - snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", mci->mci_host); + (void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", + mci->mci_host); bp += strlen(bp); -# if DAEMON if (CurHostAddr.sa.sa_family != 0) - snprintf(bp, SPACELEFT(buf, bp), " [%.100s]", - anynet_ntoa(&CurHostAddr)); -# endif /* DAEMON */ + (void) sm_snprintf(bp, SPACELEFT(buf, bp), + " [%.100s]", + anynet_ntoa(&CurHostAddr)); } else if (strcmp(status, "queued") != 0) { p = macvalue('h', e); if (p != NULL && p[0] != '\0') - snprintf(buf, sizeof buf, "relay=%.100s", p); + (void) sm_snprintf(buf, sizeof buf, "relay=%.100s", p); } if (buf[0] != '\0') sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); @@ -3774,16 +4213,16 @@ putfromline(mci, e) } else *at++ = '\0'; - (void) snprintf(xbuf, sizeof xbuf, - "From %.800s \201d remote from %.100s\n", - buf, at); + (void) sm_snprintf(xbuf, sizeof xbuf, + "From %.800s \201d remote from %.100s\n", + buf, at); } else { *bang++ = '\0'; - (void) snprintf(xbuf, sizeof xbuf, - "From %.800s \201d remote from %.100s\n", - bang, buf); + (void) sm_snprintf(xbuf, sizeof xbuf, + "From %.800s \201d remote from %.100s\n", + bang, buf); template = xbuf; } } @@ -3817,9 +4256,11 @@ putbody(mci, e, separator) register ENVELOPE *e; char *separator; { - bool dead = FALSE; + bool dead = false; char buf[MAXLINE]; +#if MIME8TO7 char *boundaries[MAXMIMENESTING + 1]; +#endif /* MIME8TO7 */ /* ** Output the body of the message @@ -3829,7 +4270,8 @@ putbody(mci, e, separator) { char *df = queuename(e, 'd'); - e->e_dfp = fopen(df, "r"); + e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, + SM_IO_RDONLY, NULL); if (e->e_dfp == NULL) { char *msg = "!putbody: Cannot open %s for %s from %s"; @@ -3838,6 +4280,7 @@ putbody(mci, e, separator) msg++; syserr(msg, df, e->e_to, e->e_from.q_paddr); } + } if (e->e_dfp == NULL) { @@ -3854,7 +4297,8 @@ putbody(mci, e, separator) { struct stat stbuf; - if (fstat(fileno(e->e_dfp), &stbuf) < 0) + if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf) + < 0) e->e_dfino = -1; else { @@ -3879,9 +4323,9 @@ putbody(mci, e, separator) if (hvalue("Content-Type", e->e_header) == NULL) { - snprintf(buf, sizeof buf, - "Content-Type: text/plain; charset=%s", - defcharset(e)); + (void) sm_snprintf(buf, sizeof buf, + "Content-Type: text/plain; charset=%s", + defcharset(e)); putline(buf, mci); } @@ -3913,7 +4357,7 @@ putbody(mci, e, separator) */ if (bitset(EF_DONT_MIME, e->e_flags)) - SuprErrs = TRUE; + SuprErrs = true; (void) mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER|M87F_NO8TO7); @@ -3950,22 +4394,22 @@ putbody(mci, e, separator) ostate = OS_HEAD; bp = buf; pbp = peekbuf; - while (!ferror(mci->mci_out) && !dead) + while (!sm_io_error(mci->mci_out) && !dead) { if (pbp > peekbuf) c = *--pbp; - else if ((c = getc(e->e_dfp)) == EOF) + else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) + == SM_IO_EOF) break; if (bitset(MCIF_7BIT, mci->mci_flags)) c &= 0x7f; switch (ostate) { case OS_HEAD: -#if _FFR_NONULLS if (c == '\0' && - bitnset(M_NONULLS, mci->mci_mailer->m_flags)) + bitnset(M_NONULLS, + mci->mci_mailer->m_flags)) break; -#endif /* _FFR_NONULLS */ if (c != '\r' && c != '\n' && bp < buflim) { *bp++ = c; @@ -3975,10 +4419,10 @@ putbody(mci, e, separator) /* check beginning of line for special cases */ *bp = '\0'; pos = 0; - padc = EOF; + padc = SM_IO_EOF; if (buf[0] == 'F' && - bitnset(M_ESCFROM, mci->mci_mailer->m_flags) && - strncmp(buf, "From ", 5) == 0) + bitnset(M_ESCFROM, mci->mci_mailer->m_flags) + && strncmp(buf, "From ", 5) == 0) { padc = '>'; } @@ -3988,7 +4432,8 @@ putbody(mci, e, separator) /* possible separator */ int sl = strlen(separator); - if (strncmp(&buf[2], separator, sl) == 0) + if (strncmp(&buf[2], separator, sl) + == 0) padc = ' '; } if (buf[0] == '.' && @@ -4000,57 +4445,68 @@ putbody(mci, e, separator) /* now copy out saved line */ if (TrafficLogFile != NULL) { - fprintf(TrafficLogFile, "%05d >>> ", - (int) getpid()); - if (padc != EOF) - (void) putc(padc, - TrafficLogFile); + (void) sm_io_fprintf(TrafficLogFile, + SM_TIME_DEFAULT, + "%05d >>> ", + (int) CurrentPid); + if (padc != SM_IO_EOF) + (void) sm_io_putc(TrafficLogFile, + SM_TIME_DEFAULT, + padc); for (xp = buf; xp < bp; xp++) - (void) putc((unsigned char) *xp, - TrafficLogFile); + (void) sm_io_putc(TrafficLogFile, + SM_TIME_DEFAULT, + (unsigned char) *xp); if (c == '\n') - (void) fputs(mci->mci_mailer->m_eol, - TrafficLogFile); + (void) sm_io_fputs(TrafficLogFile, + SM_TIME_DEFAULT, + mci->mci_mailer->m_eol); } - if (padc != EOF) + if (padc != SM_IO_EOF) { - if (putc(padc, mci->mci_out) == EOF) + if (sm_io_putc(mci->mci_out, + SM_TIME_DEFAULT, padc) + == SM_IO_EOF) { - dead = TRUE; + dead = true; continue; } else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } pos++; } for (xp = buf; xp < bp; xp++) { - if (putc((unsigned char) *xp, - mci->mci_out) == EOF) + if (sm_io_putc(mci->mci_out, + SM_TIME_DEFAULT, + (unsigned char) *xp) + == SM_IO_EOF) { - dead = TRUE; + dead = true; break; } else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } } if (dead) continue; if (c == '\n') { - if (fputs(mci->mci_mailer->m_eol, - mci->mci_out) == EOF) + if (sm_io_fputs(mci->mci_out, + SM_TIME_DEFAULT, + mci->mci_mailer->m_eol) + == SM_IO_EOF) break; else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } pos = 0; } @@ -4076,19 +4532,22 @@ putbody(mci, e, separator) if (c == '\n') { /* got CRLF */ - if (fputs(mci->mci_mailer->m_eol, - mci->mci_out) == EOF) + if (sm_io_fputs(mci->mci_out, + SM_TIME_DEFAULT, + mci->mci_mailer->m_eol) + == SM_IO_EOF) continue; else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } if (TrafficLogFile != NULL) { - (void) fputs(mci->mci_mailer->m_eol, - TrafficLogFile); + (void) sm_io_fputs(TrafficLogFile, + SM_TIME_DEFAULT, + mci->mci_mailer->m_eol); } ostate = OS_HEAD; continue; @@ -4106,11 +4565,10 @@ putbody(mci, e, separator) ostate = OS_CR; continue; } -#if _FFR_NONULLS if (c == '\0' && - bitnset(M_NONULLS, mci->mci_mailer->m_flags)) + bitnset(M_NONULLS, + mci->mci_mailer->m_flags)) break; -#endif /* _FFR_NONULLS */ putch: if (mci->mci_mailer->m_linelimit > 0 && pos >= mci->mci_mailer->m_linelimit - 1 && @@ -4121,46 +4579,57 @@ putch: /* check next character for EOL */ if (pbp > peekbuf) d = *(pbp - 1); - else if ((d = getc(e->e_dfp)) != EOF) + else if ((d = sm_io_getc(e->e_dfp, + SM_TIME_DEFAULT)) + != SM_IO_EOF) *pbp++ = d; - if (d == '\n' || d == EOF) + if (d == '\n' || d == SM_IO_EOF) { if (TrafficLogFile != NULL) - (void) putc((unsigned char) c, - TrafficLogFile); - if (putc((unsigned char) c, - mci->mci_out) == EOF) + (void) sm_io_putc(TrafficLogFile, + SM_TIME_DEFAULT, + (unsigned char) c); + if (sm_io_putc(mci->mci_out, + SM_TIME_DEFAULT, + (unsigned char) c) + == SM_IO_EOF) { - dead = TRUE; + dead = true; continue; } else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } pos++; continue; } - if (putc('!', mci->mci_out) == EOF || - fputs(mci->mci_mailer->m_eol, - mci->mci_out) == EOF) + if (sm_io_putc(mci->mci_out, + SM_TIME_DEFAULT, '!') + == SM_IO_EOF || + sm_io_fputs(mci->mci_out, + SM_TIME_DEFAULT, + mci->mci_mailer->m_eol) + == SM_IO_EOF) { - dead = TRUE; + dead = true; continue; } else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } if (TrafficLogFile != NULL) { - fprintf(TrafficLogFile, "!%s", - mci->mci_mailer->m_eol); + (void) sm_io_fprintf(TrafficLogFile, + SM_TIME_DEFAULT, + "!%s", + mci->mci_mailer->m_eol); } ostate = OS_HEAD; *pbp++ = c; @@ -4169,15 +4638,18 @@ putch: if (c == '\n') { if (TrafficLogFile != NULL) - (void) fputs(mci->mci_mailer->m_eol, - TrafficLogFile); - if (fputs(mci->mci_mailer->m_eol, - mci->mci_out) == EOF) + (void) sm_io_fputs(TrafficLogFile, + SM_TIME_DEFAULT, + mci->mci_mailer->m_eol); + if (sm_io_fputs(mci->mci_out, + SM_TIME_DEFAULT, + mci->mci_mailer->m_eol) + == SM_IO_EOF) continue; else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } pos = 0; ostate = OS_HEAD; @@ -4185,18 +4657,21 @@ putch: else { if (TrafficLogFile != NULL) - (void) putc((unsigned char) c, - TrafficLogFile); - if (putc((unsigned char) c, - mci->mci_out) == EOF) + (void) sm_io_putc(TrafficLogFile, + SM_TIME_DEFAULT, + (unsigned char) c); + if (sm_io_putc(mci->mci_out, + SM_TIME_DEFAULT, + (unsigned char) c) + == SM_IO_EOF) { - dead = TRUE; + dead = true; continue; } else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } pos++; ostate = OS_INLINE; @@ -4211,21 +4686,23 @@ putch: if (TrafficLogFile != NULL) { for (xp = buf; xp < bp; xp++) - (void) putc((unsigned char) *xp, - TrafficLogFile); + (void) sm_io_putc(TrafficLogFile, + SM_TIME_DEFAULT, + (unsigned char) *xp); } for (xp = buf; xp < bp; xp++) { - if (putc((unsigned char) *xp, mci->mci_out) == - EOF) + if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, + (unsigned char) *xp) + == SM_IO_EOF) { - dead = TRUE; + dead = true; break; } else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } } pos += bp - buf; @@ -4233,19 +4710,21 @@ putch: if (!dead && pos > 0) { if (TrafficLogFile != NULL) - (void) fputs(mci->mci_mailer->m_eol, - TrafficLogFile); - (void) fputs(mci->mci_mailer->m_eol, mci->mci_out); + (void) sm_io_fputs(TrafficLogFile, + SM_TIME_DEFAULT, + mci->mci_mailer->m_eol); + (void) sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, + mci->mci_mailer->m_eol); /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } } - if (ferror(e->e_dfp)) + if (sm_io_error(e->e_dfp)) { syserr("putbody: %s/df%s: read error", - qid_printqueue(e->e_queuedir), e->e_id); + qid_printqueue(e->e_dfqgrp, e->e_dfqdir), e->e_id); ExitStat = EX_IOERR; } @@ -4269,8 +4748,8 @@ endofmessage: buf[0] != '\0' && buf[0] != '\n') putline("", mci); - (void) fflush(mci->mci_out); - if (ferror(mci->mci_out) && errno != EPIPE) + (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT); + if (sm_io_error(mci->mci_out) && errno != EPIPE) { syserr("putbody: write error"); ExitStat = EX_IOERR; @@ -4281,8 +4760,8 @@ endofmessage: /* ** MAILFILE -- Send a message to a file. ** -** If the file has the setuid/setgid bits set, but NO execute -** bits, sendmail will try to become the owner of that file +** If the file has the set-user-ID/set-group-ID bits set, but NO +** execute bits, sendmail will try to become the owner of that file ** rather than the real user. Obviously, this only works if ** sendmail runs as root. ** @@ -4308,6 +4787,8 @@ endofmessage: ** none. */ +# define RETURN(st) exit(st); + static jmp_buf CtxMailfileTimeout; int @@ -4318,7 +4799,7 @@ mailfile(filename, mailer, ctladdr, sfflags, e) volatile long sfflags; register ENVELOPE *e; { - register FILE *f; + register SM_FILE_T *f; register pid_t pid = -1; volatile int mode; int len; @@ -4326,28 +4807,28 @@ mailfile(filename, mailer, ctladdr, sfflags, e) bool suidwarn = geteuid() == 0; char *p; char *volatile realfile; - EVENT *ev; + SM_EVENT *ev; char buf[MAXLINE + 1]; char targetfile[MAXPATHLEN + 1]; if (tTd(11, 1)) { - dprintf("mailfile %s\n ctladdr=", filename); - printaddr(ctladdr, FALSE); + sm_dprintf("mailfile %s\n ctladdr=", filename); + printaddr(ctladdr, false); } if (mailer == NULL) mailer = FileMailer; if (e->e_xfp != NULL) - (void) fflush(e->e_xfp); + (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); /* ** Special case /dev/null. This allows us to restrict file ** delivery to regular files only. */ - if (strcmp(filename, "/dev/null") == 0) + if (sm_path_isdevnull(filename)) return EX_OK; /* check for 8-bit available */ @@ -4360,7 +4841,7 @@ mailfile(filename, mailer, ctladdr, sfflags, e) { e->e_status = "5.6.3"; usrerrenh(e->e_status, - "554 Cannot send 8-bit data to 7-bit destination"); + "554 Cannot send 8-bit data to 7-bit destination"); return EX_DATAERR; } @@ -4372,19 +4853,19 @@ mailfile(filename, mailer, ctladdr, sfflags, e) if (strncmp(SafeFileEnv, filename, len) == 0) filename += len; - if (len + strlen(filename) + 1 > MAXPATHLEN) + if (len + strlen(filename) + 1 >= sizeof targetfile) { syserr("mailfile: filename too long (%s/%s)", SafeFileEnv, filename); return EX_CANTCREAT; } - (void) strlcpy(targetfile, SafeFileEnv, sizeof targetfile); + (void) sm_strlcpy(targetfile, SafeFileEnv, sizeof targetfile); realfile = targetfile + len; if (targetfile[len - 1] != '/') - (void) strlcat(targetfile, "/", sizeof targetfile); + (void) sm_strlcat(targetfile, "/", sizeof targetfile); if (*filename == '/') filename++; - (void) strlcat(targetfile, filename, sizeof targetfile); + (void) sm_strlcat(targetfile, filename, sizeof targetfile); } else if (mailer->m_rootdir != NULL) { @@ -4394,7 +4875,7 @@ mailfile(filename, mailer, ctladdr, sfflags, e) if (strncmp(targetfile, filename, len) == 0) filename += len; - if (len + strlen(filename) + 1 > MAXPATHLEN) + if (len + strlen(filename) + 1 >= sizeof targetfile) { syserr("mailfile: filename too long (%s/%s)", targetfile, filename); @@ -4402,21 +4883,22 @@ mailfile(filename, mailer, ctladdr, sfflags, e) } realfile = targetfile + len; if (targetfile[len - 1] != '/') - (void) strlcat(targetfile, "/", sizeof targetfile); + (void) sm_strlcat(targetfile, "/", sizeof targetfile); if (*filename == '/') - (void) strlcat(targetfile, filename + 1, - sizeof targetfile); + (void) sm_strlcat(targetfile, filename + 1, + sizeof targetfile); else - (void) strlcat(targetfile, filename, sizeof targetfile); + (void) sm_strlcat(targetfile, filename, + sizeof targetfile); } else { - if (strlen(filename) > MAXPATHLEN) + if (sm_strlcpy(targetfile, filename, sizeof targetfile) >= + sizeof targetfile) { syserr("mailfile: filename too long (%s)", filename); return EX_CANTCREAT; } - (void) strlcpy(targetfile, filename, sizeof targetfile); realfile = targetfile; } @@ -4440,31 +4922,34 @@ mailfile(filename, mailer, ctladdr, sfflags, e) /* Reset global flags */ RestartRequest = NULL; + RestartWorkGroup = false; ShutdownRequest = NULL; PendingSignal = 0; + CurrentPid = getpid(); if (e->e_lockfp != NULL) - (void) close(fileno(e->e_lockfp)); + (void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, + NULL)); - (void) setsignal(SIGINT, SIG_DFL); - (void) setsignal(SIGHUP, SIG_DFL); - (void) setsignal(SIGTERM, SIG_DFL); + (void) sm_signal(SIGINT, SIG_DFL); + (void) sm_signal(SIGHUP, SIG_DFL); + (void) sm_signal(SIGTERM, SIG_DFL); (void) umask(OldUmask); e->e_to = filename; ExitStat = EX_OK; if (setjmp(CtxMailfileTimeout) != 0) { - exit(EX_TEMPFAIL); + RETURN(EX_TEMPFAIL); } if (TimeOuts.to_fileopen > 0) - ev = setevent(TimeOuts.to_fileopen, - mailfiletimeout, 0); + ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout, + 0); else ev = NULL; - /* check file mode to see if setuid */ + /* check file mode to see if set-user-ID */ if (stat(targetfile, &stb) < 0) mode = FileMode; else @@ -4478,18 +4963,19 @@ mailfile(filename, mailer, ctladdr, sfflags, e) if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) || bitset(SFF_RUNASREALUID, sfflags)) { - /* ignore setuid and setgid bits */ + /* ignore set-user-ID and set-group-ID bits */ mode &= ~(S_ISGID|S_ISUID); if (tTd(11, 20)) - dprintf("mailfile: ignoring setuid/setgid bits\n"); + sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n"); } - /* we have to open the dfile BEFORE setuid */ + /* we have to open the dfile BEFORE setuid() */ if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) { char *df = queuename(e, 'd'); - e->e_dfp = fopen(df, "r"); + e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, + SM_IO_RDONLY, NULL); if (e->e_dfp == NULL) { syserr("mailfile: Cannot open %s for %s from %s", @@ -4508,7 +4994,7 @@ mailfile(filename, mailer, ctladdr, sfflags, e) { /* Only root can change the uid */ syserr("mailfile: insufficient privileges to change uid"); - exit(EX_TEMPFAIL); + RETURN(EX_TEMPFAIL); } } else if (bitset(S_ISUID, mode)) @@ -4545,7 +5031,7 @@ mailfile(filename, mailer, ctladdr, sfflags, e) { /* Only root can change the gid */ syserr("mailfile: insufficient privileges to change gid"); - exit(EX_TEMPFAIL); + RETURN(EX_TEMPFAIL); } } else if (bitset(S_ISGID, mode)) @@ -4587,7 +5073,7 @@ mailfile(filename, mailer, ctladdr, sfflags, e) { syserr("mailfile: initgroups(%s, %d) failed", RealUserName, RealGid); - exit(EX_TEMPFAIL); + RETURN(EX_TEMPFAIL); } } else @@ -4598,7 +5084,7 @@ mailfile(filename, mailer, ctladdr, sfflags, e) if (setgroups(1, gidset) == -1 && suidwarn) { syserr("mailfile: setgroups() failed"); - exit(EX_TEMPFAIL); + RETURN(EX_TEMPFAIL); } } @@ -4610,41 +5096,42 @@ mailfile(filename, mailer, ctladdr, sfflags, e) { *realfile = '\0'; if (tTd(11, 20)) - dprintf("mailfile: chroot %s\n", targetfile); + sm_dprintf("mailfile: chroot %s\n", targetfile); if (chroot(targetfile) < 0) { syserr("mailfile: Cannot chroot(%s)", targetfile); - exit(EX_CANTCREAT); + RETURN(EX_CANTCREAT); } *realfile = '/'; } if (tTd(11, 40)) - dprintf("mailfile: deliver to %s\n", realfile); + sm_dprintf("mailfile: deliver to %s\n", realfile); if (chdir("/") < 0) { syserr("mailfile: cannot chdir(/)"); - exit(EX_CANTCREAT); + RETURN(EX_CANTCREAT); } /* now reset the group and user ids */ endpwent(); + sm_mbdb_terminate(); if (setgid(RealGid) < 0 && suidwarn) { syserr("mailfile: setgid(%ld) failed", (long) RealGid); - exit(EX_TEMPFAIL); + RETURN(EX_TEMPFAIL); } vendor_set_uid(RealUid); if (setuid(RealUid) < 0 && suidwarn) { syserr("mailfile: setuid(%ld) failed", (long) RealUid); - exit(EX_TEMPFAIL); + RETURN(EX_TEMPFAIL); } if (tTd(11, 2)) - dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n", + sm_dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n", (int) getuid(), (int) geteuid(), (int) getgid(), (int) getegid()); @@ -4663,7 +5150,8 @@ mailfile(filename, mailer, ctladdr, sfflags, e) if (q != NULL) *q++ = ':'; if (tTd(11, 20)) - dprintf("mailfile: trydir %s\n", buf); + sm_dprintf("mailfile: trydir %s\n", + buf); if (buf[0] != '\0' && chdir(buf) >= 0) break; } @@ -4713,32 +5201,34 @@ mailfile(filename, mailer, ctladdr, sfflags, e) { usrerr("454 4.3.0 cannot open %s: %s", shortenstring(realfile, MAXSHORTSTR), - errstring(errno)); - exit(EX_TEMPFAIL); + sm_errstring(errno)); + RETURN(EX_TEMPFAIL); } else { usrerr("554 5.3.0 cannot open %s: %s", shortenstring(realfile, MAXSHORTSTR), - errstring(errno)); - exit(EX_CANTCREAT); + sm_errstring(errno)); + RETURN(EX_CANTCREAT); } } - if (filechanged(realfile, fileno(f), &stb)) + if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), + &stb)) { syserr("554 5.3.0 file changed after open"); - exit(EX_CANTCREAT); + RETURN(EX_CANTCREAT); } - if (fstat(fileno(f), &stb) < 0) + if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0) { - syserr("554 5.3.0 cannot fstat %s", errstring(errno)); - exit(EX_CANTCREAT); + syserr("554 5.3.0 cannot fstat %s", + sm_errstring(errno)); + RETURN(EX_CANTCREAT); } curoff = stb.st_size; if (ev != NULL) - clrevent(ev); + sm_clrevent(ev); memset(&mcibuf, '\0', sizeof mcibuf); mcibuf.mci_mailer = mailer; @@ -4758,13 +5248,13 @@ mailfile(filename, mailer, ctladdr, sfflags, e) if (bitnset(M_MAKE8BIT, mailer->m_flags) && !bitset(MCIF_7BIT, mcibuf.mci_flags) && (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && - (strcasecmp(p, "quoted-printable") == 0 || - strcasecmp(p, "base64") == 0) && + (sm_strcasecmp(p, "quoted-printable") == 0 || + sm_strcasecmp(p, "base64") == 0) && (p = hvalue("Content-Type", e->e_header)) != NULL) { /* may want to convert 7 -> 8 */ /* XXX should really parse it here -- and use a class XXX */ - if (strncasecmp(p, "text/plain", 10) == 0 && + if (sm_strncasecmp(p, "text/plain", 10) == 0 && (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) mcibuf.mci_flags |= MCIF_CVT7TO8; } @@ -4774,25 +5264,28 @@ mailfile(filename, mailer, ctladdr, sfflags, e) (*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER); (*e->e_putbody)(&mcibuf, e, NULL); putline("\n", &mcibuf); - if (fflush(f) != 0 || - (SuperSafe && fsync(fileno(f)) < 0) || - ferror(f)) + if (sm_io_flush(f, SM_TIME_DEFAULT) != 0 || + (SuperSafe != SAFE_NO && + fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) || + sm_io_error(f)) { setstat(EX_IOERR); #if !NOFTRUNCATE - (void) ftruncate(fileno(f), curoff); + (void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), + curoff); #endif /* !NOFTRUNCATE */ } /* reset ISUID & ISGID bits for paranoid systems */ #if HASFCHMOD - (void) fchmod(fileno(f), (MODE_T) mode); + (void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), + (MODE_T) mode); #else /* HASFCHMOD */ (void) chmod(filename, (MODE_T) mode); #endif /* HASFCHMOD */ - if (fclose(f) < 0) + if (sm_io_close(f, SM_TIME_DEFAULT) < 0) setstat(EX_IOERR); - (void) fflush(stdout); + (void) sm_io_flush(smioout, SM_TIME_DEFAULT); (void) setuid(RealUid); exit(ExitStat); /* NOTREACHED */ @@ -4850,15 +5343,17 @@ mailfiletimeout() ** Side Effects: ** Can tweak the symbol table. */ + #define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */ -static char * +char * hostsignature(m, host) register MAILER *m; char *host; { register char *p; register STAB *s; + time_t now; #if NAMED_BIND char sep = ':'; char prevsep = ':'; @@ -4866,35 +5361,32 @@ hostsignature(m, host) int len; int nmx; int hl; - time_t now; char *hp; char *endp; int oldoptions = _res.options; char *mxhosts[MAXMXHOSTS + 1]; - u_short mxprefs[MAXMXHOSTS + 1]; + unsigned short mxprefs[MAXMXHOSTS + 1]; #endif /* NAMED_BIND */ if (tTd(17, 3)) - dprintf("hostsignature(%s)\n", host); + sm_dprintf("hostsignature(%s)\n", host); /* ** If local delivery (and not remote), just return a constant. */ - p = m->m_mailer; if (bitnset(M_LOCALMAILER, m->m_flags) && - strcmp(p, "[IPC]") != 0 && - strcmp(p, "[TCP]") != 0) + strcmp(m->m_mailer, "[IPC]") != 0) return "localhost"; /* ** Check to see if this uses IPC -- if not, it can't have MX records. */ - if (strcmp(p, "[IPC]") != 0 && - strcmp(p, "[TCP]") != 0) + if (strcmp(m->m_mailer, "[IPC]") != 0 || + CurEnv->e_sendmode == SM_DEFER) { - /* just an ordinary mailer */ + /* just an ordinary mailer or deferred mode */ return host; } #if NETUNIX @@ -4910,24 +5402,34 @@ hostsignature(m, host) ** Look it up in the symbol table. */ + now = curtime(); s = stab(host, ST_HOSTSIG, ST_ENTER); - if (s->s_hostsig != NULL) + if (s->s_hostsig.hs_sig != NULL) { - if (tTd(17, 3)) - dprintf("hostsignature(): stab(%s) found %s\n", host, - s->s_hostsig); - return s->s_hostsig; + if (s->s_hostsig.hs_exp >= now) + { + if (tTd(17, 3)) + sm_dprintf("hostsignature(): stab(%s) found %s\n", host, + s->s_hostsig.hs_sig); + return s->s_hostsig.hs_sig; + } + + /* signature is expired: clear it */ + sm_free(s->s_hostsig.hs_sig); + s->s_hostsig.hs_sig = NULL; } + /* set default TTL */ + s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL; + /* - ** Not already there -- create a signature. + ** Not already there or expired -- create a signature. */ #if NAMED_BIND if (ConfigLevel < 2) _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ - now = curtime(); for (hp = host; hp != NULL; hp = endp) { #if NETINET6 @@ -4957,8 +5459,10 @@ hostsignature(m, host) else { auto int rcode; + int ttl; - nmx = getmxrr(hp, mxhosts, mxprefs, TRUE, &rcode); + nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true, + &ttl); if (nmx <= 0) { int save_errno; @@ -4972,7 +5476,7 @@ hostsignature(m, host) mci->mci_lastuse = now; if (rcode == EX_NOHOST) mci_setstat(mci, rcode, "5.1.2", - "550 Host unknown"); + "550 Host unknown"); else mci_setstat(mci, rcode, NULL, NULL); @@ -4981,34 +5485,41 @@ hostsignature(m, host) mxhosts[0] = hp; } if (tTd(17, 3)) - dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n", - nmx, mxhosts[0]); + sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n", + nmx, mxhosts[0]); + + /* + ** Set new TTL: we use only one! + ** We could try to use the minimum instead. + */ + + s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL); } len = 0; for (i = 0; i < nmx; i++) len += strlen(mxhosts[i]) + 1; - if (s->s_hostsig != NULL) - len += strlen(s->s_hostsig) + 1; - if (len >= MAXHOSTSIGNATURE) + if (s->s_hostsig.hs_sig != NULL) + len += strlen(s->s_hostsig.hs_sig) + 1; + if (len < 0 || len >= MAXHOSTSIGNATURE) { sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d", host, MAXHOSTSIGNATURE, len); len = MAXHOSTSIGNATURE; } - p = xalloc(len); - if (s->s_hostsig != NULL) + p = sm_pmalloc_x(len); + if (s->s_hostsig.hs_sig != NULL) { - (void) strlcpy(p, s->s_hostsig, len); - sm_free(s->s_hostsig); - s->s_hostsig = p; + (void) sm_strlcpy(p, s->s_hostsig.hs_sig, len); + sm_free(s->s_hostsig.hs_sig); /* XXX */ + s->s_hostsig.hs_sig = p; hl = strlen(p); p += hl; *p++ = prevsep; len -= hl + 1; } else - s->s_hostsig = p; + s->s_hostsig.hs_sig = p; for (i = 0; i < nmx; i++) { hl = strlen(mxhosts[i]); @@ -5026,7 +5537,7 @@ hostsignature(m, host) *p++ = ':'; len--; } - (void) strlcpy(p, mxhosts[i], len); + (void) sm_strlcpy(p, mxhosts[i], len); p += hl; len -= hl; } @@ -5043,16 +5554,20 @@ hostsignature(m, host) *endp++ = sep; prevsep = sep; } - makelower(s->s_hostsig); + makelower(s->s_hostsig.hs_sig); if (ConfigLevel < 2) _res.options = oldoptions; #else /* NAMED_BIND */ /* not using BIND -- the signature is just the host name */ - s->s_hostsig = host; + /* + ** 'host' points to storage that will be freed after we are + ** done processing the current envelope, so we copy it. + */ + s->s_hostsig.hs_sig = sm_pstrdup_x(host); #endif /* NAMED_BIND */ if (tTd(17, 1)) - dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig); - return s->s_hostsig; + sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig); + return s->s_hostsig.hs_sig; } /* ** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array. @@ -5065,6 +5580,7 @@ hostsignature(m, host) ** Parameters: ** sig -- the host signature. ** mxhosts -- array to populate. +** mailer -- mailer. ** ** Returns: ** The number of hosts inserted into mxhosts array. @@ -5079,11 +5595,10 @@ parse_hostsignature(sig, mxhosts, mailer) char **mxhosts; MAILER *mailer; { - int nmx = 0; - int curpref = 0; - int i, j; + unsigned short curpref = 0; + int nmx = 0, i, j; /* NOTE: i, j, and nmx must have same type */ char *hp, *endp; - u_short prefer[MAXMXHOSTS]; + unsigned short prefer[MAXMXHOSTS]; long rndm[MAXMXHOSTS]; for (hp = sig; hp != NULL; hp = endp) @@ -5149,7 +5664,7 @@ parse_hostsignature(sig, mxhosts, mailer) if (prefer[i] > prefer[j] || (prefer[i] == prefer[j] && rndm[i] > rndm[j])) { - register u_short tempp; + register unsigned short tempp; register long tempr; register char *temp1; @@ -5168,27 +5683,57 @@ parse_hostsignature(sig, mxhosts, mailer) return nmx; } -#if SMTP # if STARTTLS static SSL_CTX *clt_ctx = NULL; +static bool tls_ok_clt = true; /* -** INITCLTTLS -- initialize client side TLS +** SETCLTTLS -- client side TLS: allow/disallow. ** ** Parameters: +** tls_ok -- should tls be done? +** +** Returns: ** none. ** +** Side Effects: +** sets tls_ok_clt (static variable in this module) +*/ + +void +setclttls(tls_ok) + bool tls_ok; +{ + tls_ok_clt = tls_ok; + return; +} +/* +** INITCLTTLS -- initialize client side TLS +** +** Parameters: +** tls_ok -- should tls initialization be done? +** ** Returns: ** succeeded? +** +** Side Effects: +** sets tls_ok_clt (static variable in this module) */ bool -initclttls() +initclttls(tls_ok) + bool tls_ok; { + if (!tls_ok_clt) + return false; + tls_ok_clt = tls_ok; + if (!tls_ok_clt) + return false; if (clt_ctx != NULL) - return TRUE; /* already done */ - return inittls(&clt_ctx, TLS_I_CLT, FALSE, CltCERTfile, Cltkeyfile, - CACERTpath, CACERTfile, DHParams); + return true; /* already done */ + tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, false, CltCERTfile, + Cltkeyfile, CACERTpath, CACERTfile, DHParams); + return tls_ok_clt; } /* @@ -5215,14 +5760,14 @@ starttls(m, mci, e) int result = 0; int rfd, wfd; SSL *clt_ssl = NULL; + time_t tlsstart; - if (clt_ctx == NULL && !initclttls()) + if (clt_ctx == NULL && !initclttls(true)) return EX_TEMPFAIL; smtpmessage("STARTTLS", m, mci); /* get the reply */ - smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, NULL, NULL); - /* which timeout? XXX */ + smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL); /* check return code from server */ if (smtpresult == 454) @@ -5235,77 +5780,126 @@ starttls(m, mci, e) return EX_PROTOCOL; if (LogLevel > 13) - sm_syslog(LOG_INFO, e->e_id, "TLS: start client"); + sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok"); /* start connection */ if ((clt_ssl = SSL_new(clt_ctx)) == NULL) { if (LogLevel > 5) { - sm_syslog(LOG_ERR, e->e_id, - "TLS: error: client: SSL_new failed"); + sm_syslog(LOG_ERR, NOQID, + "STARTTLS=client, error: SSL_new failed"); if (LogLevel > 9) - tlslogerr(); + tlslogerr("client"); } return EX_SOFTWARE; } - rfd = fileno(mci->mci_in); - wfd = fileno(mci->mci_out); + rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL); + wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL); /* SSL_clear(clt_ssl); ? */ if (rfd < 0 || wfd < 0 || - (result = SSL_set_rfd(clt_ssl, rfd)) <= 0 || - (result = SSL_set_wfd(clt_ssl, wfd)) <= 0) + (result = SSL_set_rfd(clt_ssl, rfd)) != 1 || + (result = SSL_set_wfd(clt_ssl, wfd)) != 1) { if (LogLevel > 5) { - sm_syslog(LOG_ERR, e->e_id, - "TLS: error: SSL_set_xfd failed=%d", result); + sm_syslog(LOG_ERR, NOQID, + "STARTTLS=client, error: SSL_set_xfd failed=%d", + result); if (LogLevel > 9) - tlslogerr(); + tlslogerr("client"); } return EX_SOFTWARE; } SSL_set_connect_state(clt_ssl); + tlsstart = curtime(); + +ssl_retry: if ((result = SSL_connect(clt_ssl)) <= 0) { int i; + bool timedout; + time_t left; + time_t now = curtime(); + struct timeval tv; /* what to do in this case? */ i = SSL_get_error(clt_ssl, result); + + /* + ** For SSL_ERROR_WANT_{READ,WRITE}: + ** There is not a complete SSL record available yet + ** or there is only a partial SSL record removed from + ** the network (socket) buffer into the SSL buffer. + ** The SSL_connect will only succeed when a full + ** SSL record is available (assuming a "real" error + ** doesn't happen). To handle when a "real" error + ** does happen the select is set for exceptions too. + ** The connection may be re-negotiated during this time + ** so both read and write "want errors" need to be handled. + ** A select() exception loops back so that a proper SSL + ** error message can be gotten. + */ + + left = TimeOuts.to_starttls - (now - tlsstart); + timedout = left <= 0; + if (!timedout) + { + tv.tv_sec = left; + tv.tv_usec = 0; + } + + if (!timedout && i == SSL_ERROR_WANT_READ) + { + fd_set ssl_maskr, ssl_maskx; + + FD_ZERO(&ssl_maskr); + FD_SET(rfd, &ssl_maskr); + FD_ZERO(&ssl_maskx); + FD_SET(rfd, &ssl_maskx); + if (select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx, &tv) + > 0) + goto ssl_retry; + } + if (!timedout && i == SSL_ERROR_WANT_WRITE) + { + fd_set ssl_maskw, ssl_maskx; + + FD_ZERO(&ssl_maskw); + FD_SET(wfd, &ssl_maskw); + FD_ZERO(&ssl_maskx); + FD_SET(rfd, &ssl_maskx); + if (select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx, &tv) + > 0) + goto ssl_retry; + } if (LogLevel > 5) { sm_syslog(LOG_ERR, e->e_id, - "TLS: error: SSL_connect failed=%d (%d)", - result, i); - if (LogLevel > 9) - tlslogerr(); + "STARTTLS=client, error: connect failed=%d, SSL_error=%d, timedout=%d", + result, i, (int) timedout); + if (LogLevel > 8) + tlslogerr("client"); } SSL_free(clt_ssl); clt_ssl = NULL; return EX_SOFTWARE; } mci->mci_ssl = clt_ssl; - result = tls_get_info(clt_ssl, e, FALSE, mci->mci_host, TRUE); + result = tls_get_info(mci->mci_ssl, false, mci->mci_host, + &mci->mci_macro, true); - /* switch to use SSL... */ -#if SFIO - if (sfdctls(mci->mci_in, mci->mci_out, mci->mci_ssl) == 0) - return EX_OK; -#else /* SFIO */ -# if _FFR_TLS_TOREK + /* switch to use TLS... */ if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0) return EX_OK; -# endif /* _FFR_TLS_TOREK */ -#endif /* SFIO */ /* failure */ SSL_free(clt_ssl); clt_ssl = NULL; return EX_SOFTWARE; } - /* ** ENDTLSCLT -- shutdown secure connection (client side) ** @@ -5315,7 +5909,8 @@ starttls(m, mci, e) ** Returns: ** success? */ -int + +static int endtlsclt(mci) MCI *mci; { @@ -5327,48 +5922,35 @@ endtlsclt(mci) mci->mci_flags &= ~MCIF_TLSACT; return r; } +# endif /* STARTTLS */ +# if STARTTLS || SASL /* -** ENDTLS -- shutdown secure connection +** ISCLTFLGSET -- check whether client flag is set. ** ** Parameters: -** ssl -- SSL connection information. -** side -- srv/clt (for logging). +** e -- envelope. +** flag -- flag to check in {client_flags} ** ** Returns: -** success? +** true iff flag is set. */ -int -endtls(ssl, side) - SSL *ssl; - char *side; +static bool +iscltflgset(e, flag) + ENVELOPE *e; + int flag; { - int ret = EX_OK; + char *p; - if (ssl != NULL) + p = macvalue(macid("{client_flags}"), e); + if (p == NULL) + return false; + for (; *p != '\0'; p++) { - int r; - - if ((r = SSL_shutdown(ssl)) < 0) - { - if (LogLevel > 11) - sm_syslog(LOG_WARNING, NOQID, - "SSL_shutdown %s failed: %d", - side, r); - ret = EX_SOFTWARE; - } - else if (r == 0) - { - if (LogLevel > 13) - sm_syslog(LOG_WARNING, NOQID, - "SSL_shutdown %s not done", - side); - ret = EX_SOFTWARE; - } - SSL_free(ssl); - ssl = NULL; + /* look for just this one flag */ + if (*p == (char) flag) + return true; } - return ret; + return false; } -# endif /* STARTTLS */ -#endif /* SMTP */ +# endif /* STARTTLS || SASL */ diff --git a/gnu/usr.sbin/sendmail/sendmail/domain.c b/gnu/usr.sbin/sendmail/sendmail/domain.c index ef9fea5ee00..45a059ba319 100644 --- a/gnu/usr.sbin/sendmail/sendmail/domain.c +++ b/gnu/usr.sbin/sendmail/sendmail/domain.c @@ -13,19 +13,17 @@ #include <sendmail.h> -#ifndef lint -# if NAMED_BIND -static char id[] = "@(#)$Sendmail: domain.c,v 8.114.6.1.2.8 2001/02/12 21:40:19 gshapiro Exp $ (with name server)"; -# else /* NAMED_BIND */ -static char id[] = "@(#)$Sendmail: domain.c,v 8.114.6.1.2.8 2001/02/12 21:40:19 gshapiro Exp $ (without name server)"; -# endif /* NAMED_BIND */ -#endif /* ! lint */ - +#if NAMED_BIND +SM_RCSID("@(#)$Sendmail: domain.c,v 8.172 2001/09/04 22:43:03 ca Exp $ (with name server)") +#else /* NAMED_BIND */ +SM_RCSID("@(#)$Sendmail: domain.c,v 8.172 2001/09/04 22:43:03 ca Exp $ (without name server)") +#endif /* NAMED_BIND */ #if NAMED_BIND # include <arpa/inet.h> + /* ** The standard udp packet size PACKETSZ (512) is not sufficient for some ** nameserver answers containing very many resource records. The resolver @@ -41,8 +39,8 @@ static char id[] = "@(#)$Sendmail: domain.c,v 8.114.6.1.2.8 2001/02/12 21:40:19 typedef union { - HEADER qb1; - u_char qb2[MAXPACKET]; + HEADER qb1; + unsigned char qb2[MAXPACKET]; } querybuf; # ifndef MXHOSTBUFSIZE @@ -50,6 +48,9 @@ typedef union # endif /* ! MXHOSTBUFSIZE */ static char MXHostBuf[MXHOSTBUFSIZE]; +#if (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) + ERROR: _MXHOSTBUFSIZE is out of range +#endif /* (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) */ # ifndef MAXDNSRCH # define MAXDNSRCH 6 /* number of possible domains to search */ @@ -59,10 +60,6 @@ static char MXHostBuf[MXHOSTBUFSIZE]; # define RES_DNSRCH_VARIABLE _res.dnsrch # endif /* ! RES_DNSRCH_VARIABLE */ -# ifndef MAX -# define MAX(a, b) ((a) > (b) ? (a) : (b)) -# endif /* ! MAX */ - # ifndef NO_DATA # define NO_DATA NO_ADDRESS # endif /* ! NO_DATA */ @@ -76,11 +73,106 @@ static char MXHostBuf[MXHOSTBUFSIZE]; # if defined(__RES) && (__RES >= 19940415) # define RES_UNC_T char * # else /* defined(__RES) && (__RES >= 19940415) */ -# define RES_UNC_T u_char * +# define RES_UNC_T unsigned char * # endif /* defined(__RES) && (__RES >= 19940415) */ static char *gethostalias __P((char *)); static int mxrand __P((char *)); +static int fallbackmxrr __P((int, unsigned short *, char **)); + +/* +** GETFALLBACKMXRR -- get MX resource records for fallback MX host. +** +** We have to initialize this once before doing anything else. +** Moreover, we have to repeat this from time to time to avoid +** stale data, e.g., in persistent queue runners. +** This should be done in a parent process so the child +** processes have the right data. +** +** Parameters: +** host -- the name of the fallback MX host. +** +** Returns: +** number of MX records. +** +** Side Effects: +** Populates NumFallBackMXHosts and fbhosts. +** Sets renewal time (based on TTL). +*/ + +int NumFallBackMXHosts = 0; /* Number of fallback MX hosts (after MX expansion) */ +static char *fbhosts[MAXMXHOSTS + 1]; + +int +getfallbackmxrr(host) + char *host; +{ + int i, rcode; + int ttl; + static time_t renew = 0; + +#if 0 + /* This is currently done before this function is called. */ + if (host == NULL || *host == '\0') + return 0; +#endif /* 0 */ + if (NumFallBackMXHosts > 0 && renew > curtime()) + return NumFallBackMXHosts; + if (host[0] == '[') + { + fbhosts[0] = host; + NumFallBackMXHosts = 1; + } + else + { + /* free old data */ + for (i = 0; i < NumFallBackMXHosts; i++) + sm_free(fbhosts[i]); + + /* get new data */ + NumFallBackMXHosts = getmxrr(host, fbhosts, NULL, false, + &rcode, false, &ttl); + renew = curtime() + ttl; + for (i = 0; i < NumFallBackMXHosts; i++) + fbhosts[i] = newstr(fbhosts[i]); + } + return NumFallBackMXHosts; +} + +/* +** FALLBACKMXRR -- add MX resource records for fallback MX host to list. +** +** Parameters: +** nmx -- current number of MX records. +** prefs -- array of preferences. +** mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS) +** +** Returns: +** new number of MX records. +** +** Side Effects: +** If FallBackMX was set, it appends the MX records for +** that host to mxhosts (and modifies prefs accordingly). +*/ + +static int +fallbackmxrr(nmx, prefs, mxhosts) + int nmx; + unsigned short *prefs; + char **mxhosts; +{ + int i; + + for (i = 0; i < NumFallBackMXHosts && nmx < MAXMXHOSTS; i++) + { + if (nmx > 0) + prefs[nmx] = prefs[nmx - 1] + 1; + else + prefs[nmx] = 0; + mxhosts[nmx++] = fbhosts[i]; + } + return nmx; +} /* ** GETMXRR -- get MX resource records for a domain @@ -90,50 +182,60 @@ static int mxrand __P((char *)); ** mxhosts -- a pointer to a return buffer of MX records. ** mxprefs -- a pointer to a return buffer of MX preferences. ** If NULL, don't try to populate. -** droplocalhost -- If TRUE, all MX records less preferred +** droplocalhost -- If true, all MX records less preferred ** than the local host (as determined by $=w) will ** be discarded. ** rcode -- a pointer to an EX_ status code. +** tryfallback -- add also fallback MX host? +** pttl -- pointer to return TTL (can be NULL). ** ** Returns: ** The number of MX records found. ** -1 if there is an internal failure. ** If no MX records are found, mxhosts[0] is set to host ** and 1 is returned. +** +** Side Effects: +** The entries made for mxhosts point to a static array +** MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied, +** if it must be preserved across calls to this function. */ int -getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode) +getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode, tryfallback, pttl) char *host; char **mxhosts; - u_short *mxprefs; + unsigned short *mxprefs; bool droplocalhost; int *rcode; + bool tryfallback; + int *pttl; { - register u_char *eom, *cp; + register unsigned char *eom, *cp; register int i, j, n; int nmx = 0; register char *bp; HEADER *hp; querybuf answer; int ancount, qdcount, buflen; - bool seenlocal = FALSE; - u_short pref, type; - u_short localpref = 256; + bool seenlocal = false; + unsigned short pref, type; + unsigned short localpref = 256; char *fallbackMX = FallBackMX; - bool trycanon = FALSE; - u_short *prefs; + bool trycanon = false; + unsigned short *prefs; int (*resfunc)(); - u_short prefer[MAXMXHOSTS]; + unsigned short prefer[MAXMXHOSTS]; int weight[MAXMXHOSTS]; + int ttl = 0; extern int res_query(), res_search(); if (tTd(8, 2)) - dprintf("getmxrr(%s, droplocalhost=%d)\n", - host, droplocalhost); + sm_dprintf("getmxrr(%s, droplocalhost=%d)\n", + host, droplocalhost); - if (fallbackMX != NULL && droplocalhost && - wordinclass(fallbackMX, 'w')) + if ((fallbackMX != NULL && droplocalhost && + wordinclass(fallbackMX, 'w')) || !tryfallback) { /* don't use fallback for this pass */ fallbackMX = NULL; @@ -146,7 +248,6 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode) else prefs = prefer; - /* efficiency hack -- numeric or non-MX lookups */ if (host[0] == '[') goto punt; @@ -167,16 +268,17 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode) resfunc = res_search; errno = 0; - n = (*resfunc)(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer)); + n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer, + sizeof(answer)); if (n < 0) { if (tTd(8, 1)) - dprintf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", - (host == NULL) ? "<NULL>" : host, errno, h_errno); + sm_dprintf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", + host == NULL ? "<NULL>" : host, errno, h_errno); switch (h_errno) { case NO_DATA: - trycanon = TRUE; + trycanon = true; /* FALLTHROUGH */ case NO_RECOVERY: @@ -188,7 +290,7 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode) case 0: /* Ultrix resolver retns failure w/ h_errno=0 */ # endif /* BROKEN_RES_SEARCH */ /* host doesn't exist in DNS; might be in /etc/hosts */ - trycanon = TRUE; + trycanon = true; *rcode = EX_NOHOST; goto punt; @@ -198,12 +300,7 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode) if (fallbackMX != NULL) { /* name server is hosed -- push to fallback */ - if (nmx > 0) - prefs[nmx] = prefs[nmx - 1] + 1; - else - prefs[nmx] = 0; - mxhosts[nmx++] = fallbackMX; - return nmx; + return fallbackmxrr(nmx, prefs, mxhosts); } /* it might come up later; better queue it up */ *rcode = EX_TEMPFAIL; @@ -226,50 +323,69 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode) /* find first satisfactory answer */ hp = (HEADER *)&answer; - cp = (u_char *)&answer + HFIXEDSZ; - eom = (u_char *)&answer + n; - for (qdcount = ntohs((u_short)hp->qdcount); + cp = (unsigned char *)&answer + HFIXEDSZ; + eom = (unsigned char *)&answer + n; + for (qdcount = ntohs((unsigned short) hp->qdcount); qdcount--; cp += n + QFIXEDSZ) { if ((n = dn_skipname(cp, eom)) < 0) goto punt; } + + /* NOTE: see definition of MXHostBuf! */ buflen = sizeof(MXHostBuf) - 1; + SM_ASSERT(buflen > 0); bp = MXHostBuf; - ancount = ntohs((u_short)hp->ancount); + ancount = ntohs((unsigned short) hp->ancount); + + /* See RFC 1035 for layout of RRs. */ + /* XXX leave room for FallBackMX ? */ while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) { - if ((n = dn_expand((u_char *)&answer, - eom, cp, (RES_UNC_T) bp, buflen)) < 0) + if ((n = dn_expand((unsigned char *)&answer, eom, cp, + (RES_UNC_T) bp, buflen)) < 0) break; cp += n; GETSHORT(type, cp); - cp += INT16SZ + INT32SZ; - GETSHORT(n, cp); + cp += INT16SZ; /* skip over class */ + GETLONG(ttl, cp); + GETSHORT(n, cp); /* rdlength */ if (type != T_MX) { if (tTd(8, 8) || _res.options & RES_DEBUG) - dprintf("unexpected answer type %d, size %d\n", + sm_dprintf("unexpected answer type %d, size %d\n", type, n); cp += n; continue; } GETSHORT(pref, cp); - if ((n = dn_expand((u_char *)&answer, eom, cp, + if ((n = dn_expand((unsigned char *)&answer, eom, cp, (RES_UNC_T) bp, buflen)) < 0) break; cp += n; + n = strlen(bp); +# if 0 + /* Can this happen? */ + if (n == 0) + { + if (LogLevel > 4) + sm_syslog(LOG_ERR, NOQID, + "MX records for %s contain empty string", + host); + continue; + } +# endif /* 0 */ if (wordinclass(bp, 'w')) { if (tTd(8, 3)) - dprintf("found localhost (%s) in MX list, pref=%d\n", + sm_dprintf("found localhost (%s) in MX list, pref=%d\n", bp, pref); if (droplocalhost) { if (!seenlocal || pref < localpref) localpref = pref; - seenlocal = TRUE; + seenlocal = true; continue; } weight[nmx] = 0; @@ -278,7 +394,6 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode) weight[nmx] = mxrand(bp); prefs[nmx] = pref; mxhosts[nmx++] = bp; - n = strlen(bp); bp += n; if (bp[-1] != '.') { @@ -286,9 +401,18 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode) n++; } *bp++ = '\0'; + if (buflen < n + 1) + { + /* don't want to wrap buflen */ + break; + } buflen -= n + 1; } + /* return only one TTL entry, that should be sufficient */ + if (ttl > 0 && pttl != NULL) + *pttl = ttl; + /* sort the records */ for (i = 0; i < nmx; i++) { @@ -321,7 +445,7 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode) /* delete duplicates from list (yes, some bozos have duplicates) */ for (i = 0; i < nmx - 1; ) { - if (strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) + if (sm_strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) i++; else { @@ -393,19 +517,19 @@ punt: host, MyHostName); return -1; } -# if _FFR_FREEHOSTENT && NETINET6 +# if NETINET6 freehostent(h); hp = NULL; -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +# endif /* NETINET6 */ } - if (strlen(host) >= (SIZE_T) sizeof MXHostBuf) + if (strlen(host) >= sizeof MXHostBuf) { *rcode = EX_CONFIG; syserr("Host name %s too long", shortenstring(host, MAXSHORTSTR)); return -1; } - snprintf(MXHostBuf, sizeof MXHostBuf, "%s", host); + (void) sm_strlcpy(MXHostBuf, host, sizeof MXHostBuf); mxhosts[0] = MXHostBuf; prefs[0] = 0; if (host[0] == '[') @@ -427,8 +551,8 @@ punt: *p = ']'; } # if NETINET6 - else if (inet_pton(AF_INET6, &MXHostBuf[1], - &tmp6.sin6_addr) == 1) + else if (anynet_pton(AF_INET6, &MXHostBuf[1], + &tmp6.sin6_addr) == 1) { nmx++; *p = ']'; @@ -436,14 +560,15 @@ punt: # endif /* NETINET6 */ else { - trycanon = TRUE; + trycanon = true; mxhosts[0]++; } } } if (trycanon && - getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE)) + getcanonname(mxhosts[0], sizeof MXHostBuf - 2, false, pttl)) { + /* XXX MXHostBuf == "" ? is that possible? */ bp = &MXHostBuf[strlen(MXHostBuf)]; if (bp[-1] != '.') { @@ -457,13 +582,8 @@ punt: /* if we have a default lowest preference, include that */ if (fallbackMX != NULL && !seenlocal) { - if (nmx > 0) - prefs[nmx] = prefs[nmx - 1] + 1; - else - prefs[nmx] = 0; - mxhosts[nmx++] = fallbackMX; + nmx = fallbackmxrr(nmx, prefs, mxhosts); } - return nmx; } /* @@ -479,9 +599,6 @@ punt: ** ** Returns: ** A random but repeatable value based on the host name. -** -** Side Effects: -** none. */ static int @@ -499,7 +616,7 @@ mxrand(host) } if (tTd(17, 9)) - dprintf("mxrand(%s)", host); + sm_dprintf("mxrand(%s)", host); hfunc = seed; while (*host != '\0') @@ -515,7 +632,7 @@ mxrand(host) hfunc++; if (tTd(17, 9)) - dprintf(" = %d\n", hfunc); + sm_dprintf(" = %d\n", hfunc); return hfunc; } /* @@ -535,13 +652,19 @@ bestmx_map_lookup(map, name, av, statp) { int nmx; int saveopts = _res.options; - int i, len = 0; - char *p; + int i; + ssize_t len = 0; + char *result; char *mxhosts[MAXMXHOSTS + 1]; +#if _FFR_BESTMX_BETTER_TRUNCATION + char *buf; +#else /* _FFR_BESTMX_BETTER_TRUNCATION */ + char *p; char buf[PSBUFSIZE / 2]; +#endif /* _FFR_BESTMX_BETTER_TRUNCATION */ _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); - nmx = getmxrr(name, mxhosts, NULL, FALSE, statp); + nmx = getmxrr(name, mxhosts, NULL, false, statp, true, NULL); _res.options = saveopts; if (nmx <= 0) return NULL; @@ -554,10 +677,49 @@ bestmx_map_lookup(map, name, av, statp) ** We were given a -z flag (return all MXs) and there are multiple ** ones. We need to build them all into a list. */ + +#if _FFR_BESTMX_BETTER_TRUNCATION + for (i = 0; i < nmx; i++) + { + if (strchr(mxhosts[i], map->map_coldelim) != NULL) + { + syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", + mxhosts[i], map->map_coldelim); + return NULL; + } + len += strlen(mxhosts[i]) + 1; + if (len < 0) + { + len -= strlen(mxhosts[i]) + 1; + break; + } + } + buf = (char *) sm_malloc(len); + if (buf == NULL) + { + *statp = EX_UNAVAILABLE; + return NULL; + } + *buf = '\0'; + for (i = 0; i < nmx; i++) + { + int end; + + end = sm_strlcat(buf, mxhosts[i], len); + if (i != nmx && end + 1 < len) + { + buf[end] = map->map_coldelim; + buf[end + 1] = '\0'; + } + } + + /* Cleanly truncate for rulesets */ + truncate_at_delim(buf, PSBUFSIZE / 2, map->map_coldelim); +#else /* _FFR_BESTMX_BETTER_TRUNCATION */ p = buf; for (i = 0; i < nmx; i++) { - int slen; + size_t slen; if (strchr(mxhosts[i], map->map_coldelim) != NULL) { @@ -573,11 +735,17 @@ bestmx_map_lookup(map, name, av, statp) *p++ = map->map_coldelim; len++; } - (void) strlcpy(p, mxhosts[i], sizeof buf - len); + (void) sm_strlcpy(p, mxhosts[i], sizeof buf - len); p += slen; len += slen; } - return map_rewrite(map, buf, len, av); +#endif /* _FFR_BESTMX_BETTER_TRUNCATION */ + + result = map_rewrite(map, buf, len, av); +#if _FFR_BESTMX_BETTER_TRUNCATION + sm_free(buf); +#endif /* _FFR_BESTMX_BETTER_TRUNCATION */ + return result; } /* ** DNS_GETCANONNAME -- get the canonical name for named host using DNS @@ -603,20 +771,28 @@ bestmx_map_lookup(map, name, av, statp) ** hbsize -- the size of the host buffer. ** trymx -- if set, try MX records as well as A and CNAME. ** statp -- pointer to place to store status. +** pttl -- pointer to return TTL (can be NULL). ** ** Returns: -** TRUE -- if the host matched. -** FALSE -- otherwise. +** true -- if the host matched. +** false -- otherwise. */ +# if NETINET6 +# define SM_T_INITIAL T_AAAA +# else /* NETINET6 */ +# define SM_T_INITIAL T_A +# endif /* NETINET6 */ + bool -dns_getcanonname(host, hbsize, trymx, statp) +dns_getcanonname(host, hbsize, trymx, statp, pttl) char *host; int hbsize; bool trymx; int *statp; + int *pttl; { - register u_char *eom, *ap; + register unsigned char *eom, *ap; register char *cp; register int n; HEADER *hp; @@ -625,23 +801,24 @@ dns_getcanonname(host, hbsize, trymx, statp) int ret; char **domain; int type; + int ttl = 0; char **dp; char *mxmatch; bool amatch; - bool gotmx = FALSE; + bool gotmx = false; int qtype; int loopcnt; char *xp; - char nbuf[MAX(MAXPACKET, MAXDNAME*2+2)]; + char nbuf[SM_MAX(MAXPACKET, MAXDNAME*2+2)]; char *searchlist[MAXDNSRCH+2]; if (tTd(8, 2)) - dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); + sm_dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); if ((_res.options & RES_INIT) == 0 && res_init() == -1) { *statp = EX_UNAVAILABLE; - return FALSE; + return false; } *statp = EX_OK; @@ -669,6 +846,7 @@ cnameloop: ** If this is a simple name, determine whether it matches an ** alias in the file defined by the environment variable HOSTALIASES. */ + if (n == 0 && (xp = gethostalias(host)) != NULL) { if (loopcnt++ > MAXCNAMEDEPTH) @@ -677,7 +855,7 @@ cnameloop: } else { - (void) strlcpy(host, xp, hbsize); + (void) sm_strlcpy(host, xp, hbsize); goto cnameloop; } } @@ -720,16 +898,15 @@ cnameloop: */ mxmatch = NULL; - qtype = T_ANY; + qtype = SM_T_INITIAL; for (dp = searchlist; *dp != NULL; ) { - if (qtype == T_ANY) - gotmx = FALSE; + if (qtype == SM_T_INITIAL) + gotmx = false; if (tTd(8, 5)) - dprintf("dns_getcanonname: trying %s.%s (%s)\n", + sm_dprintf("dns_getcanonname: trying %s.%s (%s)\n", host, *dp, - qtype == T_ANY ? "ANY" : # if NETINET6 qtype == T_AAAA ? "AAAA" : # endif /* NETINET6 */ @@ -742,33 +919,18 @@ cnameloop: if (ret <= 0) { if (tTd(8, 7)) - dprintf("\tNO: errno=%d, h_errno=%d\n", + sm_dprintf("\tNO: errno=%d, h_errno=%d\n", errno, h_errno); if (errno == ECONNREFUSED || h_errno == TRY_AGAIN) { /* - ** the name server seems to be down or - ** broken. + ** the name server seems to be down or broken. */ SM_SET_H_ERRNO(TRY_AGAIN); *statp = EX_TEMPFAIL; - /* - ** If the ANY query is larger than the - ** UDP packet size, the resolver will - ** fall back to TCP. However, some - ** misconfigured firewalls block 53/TCP - ** so the ANY lookup fails whereas an MX - ** or A record might work. Therefore, - ** don't fail on ANY queries. - ** - ** The ANY query is really meant to prime - ** the cache so this isn't dangerous. - */ - -#if _FFR_WORKAROUND_BROKEN_NAMESERVERS if (WorkAroundBrokenAAAA) { /* @@ -781,37 +943,26 @@ cnameloop: ** didn't give an answer). */ - if (qtype != T_ANY && - errno != ETIMEDOUT) - return FALSE; + if (errno != ETIMEDOUT) + return false; } -#else /* _FFR_WORKAROUND_BROKEN_NAMESERVERS */ - if (qtype != T_ANY) - return FALSE; -#endif /* _FFR_WORKAROUND_BROKEN_NAMESERVERS */ + else + return false; } if (h_errno != HOST_NOT_FOUND) { /* might have another type of interest */ - if (qtype == T_ANY) - { -# if NETINET6 - qtype = T_AAAA; -# else /* NETINET6 */ - qtype = T_A; -# endif /* NETINET6 */ - continue; - } # if NETINET6 - else if (qtype == T_AAAA) + if (qtype == T_AAAA) { qtype = T_A; continue; } + else # endif /* NETINET6 */ - else if (qtype == T_A && !gotmx && - (trymx || **dp == '\0')) + if (qtype == T_A && !gotmx && + (trymx || **dp == '\0')) { qtype = T_MX; continue; @@ -820,15 +971,20 @@ cnameloop: /* definite no -- try the next domain */ dp++; - qtype = T_ANY; + qtype = SM_T_INITIAL; continue; } else if (tTd(8, 7)) - dprintf("\tYES\n"); + sm_dprintf("\tYES\n"); /* avoid problems after truncation in tcp packets */ if (ret > sizeof(answer)) ret = sizeof(answer); + if (ret < 0) + { + *statp = EX_SOFTWARE; + return false; + } /* ** Appear to have a match. Confirm it by searching for A or @@ -837,41 +993,42 @@ cnameloop: */ hp = (HEADER *) &answer; - ap = (u_char *) &answer + HFIXEDSZ; - eom = (u_char *) &answer + ret; + ap = (unsigned char *) &answer + HFIXEDSZ; + eom = (unsigned char *) &answer + ret; /* skip question part of response -- we know what we asked */ - for (qdcount = ntohs((u_short)hp->qdcount); + for (qdcount = ntohs((unsigned short) hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) { if ((ret = dn_skipname(ap, eom)) < 0) { if (tTd(8, 20)) - dprintf("qdcount failure (%d)\n", - ntohs((u_short)hp->qdcount)); + sm_dprintf("qdcount failure (%d)\n", + ntohs((unsigned short) hp->qdcount)); *statp = EX_SOFTWARE; - return FALSE; /* ???XXX??? */ + return false; /* ???XXX??? */ } } - amatch = FALSE; - for (ancount = ntohs((u_short)hp->ancount); + amatch = false; + for (ancount = ntohs((unsigned short) hp->ancount); --ancount >= 0 && ap < eom; ap += n) { - n = dn_expand((u_char *) &answer, eom, ap, + n = dn_expand((unsigned char *) &answer, eom, ap, (RES_UNC_T) nbuf, sizeof nbuf); if (n < 0) break; ap += n; GETSHORT(type, ap); - ap += INT16SZ + INT32SZ; - GETSHORT(n, ap); + ap += INT16SZ; /* skip over class */ + GETLONG(ttl, ap); + GETSHORT(n, ap); /* rdlength */ switch (type) { case T_MX: - gotmx = TRUE; + gotmx = true; if (**dp != '\0' && HasWildcardMX) { /* @@ -900,7 +1057,7 @@ cnameloop: # if NETINET6 case T_AAAA: /* Flag that a good match was found */ - amatch = TRUE; + amatch = true; /* continue in case a CNAME also exists */ continue; @@ -908,7 +1065,7 @@ cnameloop: case T_A: /* Flag that a good match was found */ - amatch = TRUE; + amatch = true; /* continue in case a CNAME also exists */ continue; @@ -917,7 +1074,7 @@ cnameloop: if (DontExpandCnames) { /* got CNAME -- guaranteed canonical */ - amatch = TRUE; + amatch = true; break; } @@ -930,21 +1087,25 @@ cnameloop: { char ebuf[MAXLINE]; - snprintf(ebuf, sizeof ebuf, + (void) sm_snprintf(ebuf, + sizeof ebuf, "Deferred: DNS failure: CNAME loop for %.100s", host); - CurEnv->e_message = newstr(ebuf); + CurEnv->e_message = + sm_rpool_strdup_x( + CurEnv->e_rpool, ebuf); } SM_SET_H_ERRNO(NO_RECOVERY); *statp = EX_CONFIG; - return FALSE; + return false; } /* value points at name */ - if ((ret = dn_expand((u_char *)&answer, - eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0) + if ((ret = dn_expand((unsigned char *)&answer, + eom, ap, (RES_UNC_T) nbuf, + sizeof(nbuf))) < 0) break; - (void)strlcpy(host, nbuf, hbsize); + (void) sm_strlcpy(host, nbuf, hbsize); /* ** RFC 1034 section 3.6 specifies that CNAME @@ -973,32 +1134,21 @@ cnameloop: /* ** Nothing definitive yet. - ** If this was a T_ANY query, we don't really know what - ** was returned -- it might have been a T_NS, - ** for example. Try T_A to be more specific - ** during the next pass. ** If this was a T_A query and we haven't yet found a MX ** match, try T_MX if allowed to do so. ** Otherwise, try the next domain. */ - if (qtype == T_ANY) - { # if NETINET6 - qtype = T_AAAA; -# else /* NETINET6 */ - qtype = T_A; -# endif /* NETINET6 */ - } -# if NETINET6 - else if (qtype == T_AAAA) + if (qtype == T_AAAA) qtype = T_A; + else # endif /* NETINET6 */ - else if (qtype == T_A && !gotmx && (trymx || **dp == '\0')) + if (qtype == T_A && !gotmx && (trymx || **dp == '\0')) qtype = T_MX; else { - qtype = T_ANY; + qtype = SM_T_INITIAL; dp++; } } @@ -1008,7 +1158,7 @@ cnameloop: { if (*statp == EX_OK) *statp = EX_NOHOST; - return FALSE; + return false; } /* @@ -1017,14 +1167,18 @@ cnameloop: ** Otherwise append the saved domain name. */ - (void) snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host, - *mxmatch == '\0' ? "" : ".", - MAXDNAME, mxmatch); - (void) strlcpy(host, nbuf, hbsize); + (void) sm_snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host, + *mxmatch == '\0' ? "" : ".", + MAXDNAME, mxmatch); + (void) sm_strlcpy(host, nbuf, hbsize); if (tTd(8, 5)) - dprintf("dns_getcanonname: %s\n", host); + sm_dprintf("dns_getcanonname: %s\n", host); *statp = EX_OK; - return TRUE; + + /* return only one TTL entry, that should be sufficient */ + if (ttl > 0 && pttl != NULL) + *pttl = ttl; + return true; } static char * @@ -1032,19 +1186,21 @@ gethostalias(host) char *host; { char *fname; - FILE *fp; + SM_FILE_T *fp; register char *p = NULL; long sff = SFF_REGONLY; char buf[MAXLINE]; static char hbuf[MAXDNAME]; + if (ResNoAliases) + return NULL; if (DontLockReadFiles) sff |= SFF_NOLOCK; fname = getenv("HOSTALIASES"); if (fname == NULL || (fp = safefopen(fname, O_RDONLY, 0, sff)) == NULL) return NULL; - while (fgets(buf, sizeof buf, fp) != NULL) + while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) { for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++) continue; @@ -1054,17 +1210,17 @@ gethostalias(host) continue; } *p++ = '\0'; - if (strcasecmp(buf, host) == 0) + if (sm_strcasecmp(buf, host) == 0) break; } - if (feof(fp)) + if (sm_io_eof(fp)) { /* no match */ - (void) fclose(fp); + (void) sm_io_close(fp, SM_TIME_DEFAULT); return NULL; } - (void) fclose(fp); + (void) sm_io_close(fp, SM_TIME_DEFAULT); /* got a match; extract the equivalent name */ while (*p != '\0' && isascii(*p) && isspace(*p)) @@ -1073,7 +1229,7 @@ gethostalias(host) while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; *p = '\0'; - (void) strlcpy(hbuf, host, sizeof hbuf); + (void) sm_strlcpy(hbuf, host, sizeof hbuf); return hbuf; } #endif /* NAMED_BIND */ diff --git a/gnu/usr.sbin/sendmail/sendmail/envelope.c b/gnu/usr.sbin/sendmail/sendmail/envelope.c index 0f50853615f..fcb2e5a56bb 100644 --- a/gnu/usr.sbin/sendmail/sendmail/envelope.c +++ b/gnu/usr.sbin/sendmail/sendmail/envelope.c @@ -11,21 +11,21 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: envelope.c,v 8.180.14.10 2001/05/03 17:24:06 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +SM_RCSID("@(#)$Sendmail: envelope.c,v 8.264 2001/08/31 23:03:13 gshapiro Exp $") /* -** NEWENVELOPE -- allocate a new envelope +** NEWENVELOPE -- fill in a new envelope ** ** Supports inheritance. ** ** Parameters: ** e -- the new envelope to fill in. ** parent -- the envelope to be the parent of e. +** rpool -- either NULL, or a pointer to a resource pool +** from which envelope memory is allocated, and +** to which envelope resources are attached. ** ** Returns: ** e. @@ -35,13 +35,23 @@ static char id[] = "@(#)$Sendmail: envelope.c,v 8.180.14.10 2001/05/03 17:24:06 */ ENVELOPE * -newenvelope(e, parent) +newenvelope(e, parent, rpool) register ENVELOPE *e; register ENVELOPE *parent; + SM_RPOOL_T *rpool; { - if (e == parent && e->e_parent != NULL) + /* + ** This code used to read: + ** if (e == parent && e->e_parent != NULL) + ** parent = e->e_parent; + ** So if e == parent && e->e_parent == NULL then we would + ** set e->e_parent = e, which creates a loop in the e_parent chain. + ** This meant macvalue() could go into an infinite loop. + */ + + if (e == parent) parent = e->e_parent; - clearenvelope(e, TRUE); + clearenvelope(e, true, rpool); if (e == CurEnv) memmove((char *) &e->e_from, (char *) &NullAddress, @@ -58,16 +68,31 @@ newenvelope(e, parent) e->e_puthdr = putheader; e->e_putbody = putbody; if (CurEnv->e_xfp != NULL) - (void) fflush(CurEnv->e_xfp); + (void) sm_io_flush(CurEnv->e_xfp, SM_TIME_DEFAULT); return e; } + +/* values for msg_timeout, see also IS_* below for usage (bit layout) */ +#define MSG_T_O 0x01 /* normal timeout */ +#define MSG_T_O_NOW 0x02 /* NOW timeout */ +#define MSG_NOT_BY 0x04 /* Deliver-By time exceeded, mode R */ +#define MSG_WARN 0x10 /* normal queue warning */ +#define MSG_WARN_BY 0x20 /* Deliver-By time exceeded, mode N */ + +#define IS_MSG_ERR(x) (((x) & 0x0f) != 0) /* return an error */ + +/* immediate return */ +#define IS_IMM_RET(x) (((x) & (MSG_T_O_NOW|MSG_NOT_BY)) != 0) +#define IS_MSG_WARN(x) (((x) & 0xf0) != 0) /* return a warning */ + /* ** DROPENVELOPE -- deallocate an envelope. ** ** Parameters: ** e -- the envelope to deallocate. ** fulldrop -- if set, do return receipts. +** split -- if true, split by recipient if message is queued up ** ** Returns: ** none. @@ -78,17 +103,19 @@ newenvelope(e, parent) */ void -dropenvelope(e, fulldrop) +dropenvelope(e, fulldrop, split) register ENVELOPE *e; bool fulldrop; + bool split; { - bool queueit = FALSE; - bool message_timeout = FALSE; - bool failure_return = FALSE; - bool delay_return = FALSE; - bool success_return = FALSE; + bool savedf = false; + bool queueit = false; + int msg_timeout = 0; + bool failure_return = false; + bool delay_return = false; + bool success_return = false; bool pmnotify = bitset(EF_PM_NOTIFY, e->e_flags); - bool done = FALSE; + bool done = false; register ADDRESS *q; char *id = e->e_id; time_t now; @@ -96,21 +123,21 @@ dropenvelope(e, fulldrop) if (tTd(50, 1)) { - dprintf("dropenvelope %lx: id=", (u_long) e); + sm_dprintf("dropenvelope %p: id=", e); xputs(e->e_id); - dprintf(", flags="); + sm_dprintf(", flags="); printenvflags(e); if (tTd(50, 10)) { - dprintf("sendq="); - printaddr(e->e_sendqueue, TRUE); + sm_dprintf("sendq="); + printaddr(e->e_sendqueue, true); } } if (LogLevel > 84) sm_syslog(LOG_DEBUG, id, "dropenvelope, e_flags=0x%lx, OpMode=%c, pid=%d", - e->e_flags, OpMode, getpid()); + e->e_flags, OpMode, (int) CurrentPid); /* we must have an id to remove disk files */ if (id == NULL) @@ -133,12 +160,18 @@ dropenvelope(e, fulldrop) now = curtime(); if (now >= e->e_ctime + TimeOuts.to_q_return[e->e_timeoutclass]) - message_timeout = TRUE; - - if (TimeOuts.to_q_return[e->e_timeoutclass] == NOW && + msg_timeout = MSG_T_O; + if (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 && + now >= e->e_ctime + e->e_deliver_by && !bitset(EF_RESPONSE, e->e_flags)) { - message_timeout = TRUE; + msg_timeout = MSG_NOT_BY; + e->e_flags |= EF_FATALERRS|EF_CLRQUEUE; + } + else if (TimeOuts.to_q_return[e->e_timeoutclass] == NOW && + !bitset(EF_RESPONSE, e->e_flags)) + { + msg_timeout = MSG_T_O_NOW; e->e_flags |= EF_FATALERRS|EF_CLRQUEUE; } @@ -146,31 +179,32 @@ dropenvelope(e, fulldrop) for (q = e->e_sendqueue; q != NULL; q = q->q_next) { if (QS_IS_UNDELIVERED(q->q_state)) - queueit = TRUE; + queueit = true; /* see if a notification is needed */ if (bitset(QPINGONFAILURE, q->q_flags) && - ((message_timeout && QS_IS_UNDELIVERED(q->q_state)) || + ((IS_MSG_ERR(msg_timeout) && + QS_IS_UNDELIVERED(q->q_state)) || QS_IS_BADADDR(q->q_state) || - (TimeOuts.to_q_return[e->e_timeoutclass] == NOW && - !bitset(EF_RESPONSE, e->e_flags)))) - + IS_IMM_RET(msg_timeout))) { - failure_return = TRUE; + failure_return = true; if (!done && q->q_owner == NULL && !emptyaddr(&e->e_from)) { (void) sendtolist(e->e_from.q_paddr, NULLADDR, &e->e_errorqueue, 0, e); - done = TRUE; + done = true; } } - else if (bitset(QPINGONSUCCESS, q->q_flags) && - ((QS_IS_SENT(q->q_state) && - bitnset(M_LOCALMAILER, q->q_mailer->m_flags)) || - bitset(QRELAYED|QEXPANDED|QDELIVERED, q->q_flags))) + else if ((bitset(QPINGONSUCCESS, q->q_flags) && + ((QS_IS_SENT(q->q_state) && + bitnset(M_LOCALMAILER, q->q_mailer->m_flags)) || + bitset(QRELAYED|QEXPANDED|QDELIVERED, q->q_flags))) || + bitset(QBYTRACE, q->q_flags) || + bitset(QBYNRELAY, q->q_flags)) { - success_return = TRUE; + success_return = true; } } @@ -184,75 +218,146 @@ dropenvelope(e, fulldrop) if (!queueit) /* EMPTY */ /* nothing to do */ ; - else if (message_timeout) + else if (IS_MSG_ERR(msg_timeout)) { if (failure_return) { - (void) snprintf(buf, sizeof buf, + if (msg_timeout == MSG_NOT_BY) + { + (void) sm_snprintf(buf, sizeof buf, + "delivery time expired %lds", + e->e_deliver_by); + } + else + { + (void) sm_snprintf(buf, sizeof buf, "Cannot send message for %s", - pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE)); - if (e->e_message != NULL) - sm_free(e->e_message); - e->e_message = newstr(buf); + pintvl(TimeOuts.to_q_return[e->e_timeoutclass], + false)); + } + + /* don't free, allocated from e_rpool */ + e->e_message = sm_rpool_strdup_x(e->e_rpool, buf); message(buf); e->e_flags |= EF_CLRQUEUE; } - fprintf(e->e_xfp, "Message could not be delivered for %s\n", - pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE)); - fprintf(e->e_xfp, "Message will be deleted from queue\n"); + if (msg_timeout == MSG_NOT_BY) + { + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + "Delivery time (%lds) expired\n", + e->e_deliver_by); + } + else + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + "Message could not be delivered for %s\n", + pintvl(TimeOuts.to_q_return[e->e_timeoutclass], + false)); + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + "Message will be deleted from queue\n"); for (q = e->e_sendqueue; q != NULL; q = q->q_next) { if (QS_IS_UNDELIVERED(q->q_state)) { q->q_state = QS_BADADDR; - q->q_status = "4.4.7"; + if (msg_timeout == MSG_NOT_BY) + q->q_status = "5.4.7"; + else + q->q_status = "4.4.7"; } } } - else if (TimeOuts.to_q_warning[e->e_timeoutclass] > 0 && - now >= e->e_ctime + TimeOuts.to_q_warning[e->e_timeoutclass]) + else { - if (!bitset(EF_WARNING|EF_RESPONSE, e->e_flags) && - e->e_class >= 0 && - e->e_from.q_paddr != NULL && - strcmp(e->e_from.q_paddr, "<>") != 0 && - strncasecmp(e->e_from.q_paddr, "owner-", 6) != 0 && - (strlen(e->e_from.q_paddr) <= (SIZE_T) 8 || - strcasecmp(&e->e_from.q_paddr[strlen(e->e_from.q_paddr) - 8], "-request") != 0)) + if (TimeOuts.to_q_warning[e->e_timeoutclass] > 0 && + now >= e->e_ctime + + TimeOuts.to_q_warning[e->e_timeoutclass]) + msg_timeout = MSG_WARN; + else if (IS_DLVR_NOTIFY(e) && + e->e_deliver_by > 0 && + now >= e->e_ctime + e->e_deliver_by) + msg_timeout = MSG_WARN_BY; + + if (IS_MSG_WARN(msg_timeout)) { - for (q = e->e_sendqueue; q != NULL; q = q->q_next) + if (!bitset(EF_WARNING|EF_RESPONSE, e->e_flags) && + e->e_class >= 0 && + e->e_from.q_paddr != NULL && + strcmp(e->e_from.q_paddr, "<>") != 0 && + sm_strncasecmp(e->e_from.q_paddr, "owner-", 6) != 0 && + (strlen(e->e_from.q_paddr) <= 8 || + sm_strcasecmp(&e->e_from.q_paddr[strlen(e->e_from.q_paddr) - 8], + "-request") != 0)) { - if (QS_IS_UNDELIVERED(q->q_state) && + for (q = e->e_sendqueue; q != NULL; + q = q->q_next) + { + if (QS_IS_UNDELIVERED(q->q_state) #if _FFR_NODELAYDSN_ON_HOLD - !bitnset(M_HOLD, q->q_mailer->m_flags) && + && !bitnset(M_HOLD, + q->q_mailer->m_flags) #endif /* _FFR_NODELAYDSN_ON_HOLD */ - bitset(QPINGONDELAY, q->q_flags)) + ) + { + if (msg_timeout == + MSG_WARN_BY && + (bitset(QPINGONDELAY, + q->q_flags) || + !bitset(QHASNOTIFY, + q->q_flags)) + ) + { + q->q_flags |= QBYNDELAY; + delay_return = true; + } + if (bitset(QPINGONDELAY, + q->q_flags)) + { + q->q_flags |= QDELAYED; + delay_return = true; + } + } + } + } + if (delay_return) + { + if (msg_timeout == MSG_WARN_BY) { - q->q_flags |= QDELAYED; - delay_return = TRUE; + (void) sm_snprintf(buf, sizeof buf, + "Warning: Delivery time (%lds) exceeded", + e->e_deliver_by); } + else + (void) sm_snprintf(buf, sizeof buf, + "Warning: could not send message for past %s", + pintvl(TimeOuts.to_q_warning[e->e_timeoutclass], + false)); + + /* don't free, allocated from e_rpool */ + e->e_message = sm_rpool_strdup_x(e->e_rpool, + buf); + message(buf); + e->e_flags |= EF_WARNING; } + if (msg_timeout == MSG_WARN_BY) + { + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + "Warning: Delivery time (%lds) exceeded\n", + e->e_deliver_by); + } + else + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + "Warning: message still undelivered after %s\n", + pintvl(TimeOuts.to_q_warning[e->e_timeoutclass], + false)); + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + "Will keep trying until message is %s old\n", + pintvl(TimeOuts.to_q_return[e->e_timeoutclass], + false)); } - if (delay_return) - { - (void) snprintf(buf, sizeof buf, - "Warning: could not send message for past %s", - pintvl(TimeOuts.to_q_warning[e->e_timeoutclass], FALSE)); - if (e->e_message != NULL) - sm_free(e->e_message); - e->e_message = newstr(buf); - message(buf); - e->e_flags |= EF_WARNING; - } - fprintf(e->e_xfp, - "Warning: message still undelivered after %s\n", - pintvl(TimeOuts.to_q_warning[e->e_timeoutclass], FALSE)); - fprintf(e->e_xfp, "Will keep trying until message is %s old\n", - pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE)); } if (tTd(50, 2)) - dprintf("failure_return=%d delay_return=%d success_return=%d queueit=%d\n", + sm_dprintf("failure_return=%d delay_return=%d success_return=%d queueit=%d\n", failure_return, delay_return, success_return, queueit); /* @@ -268,7 +373,7 @@ dropenvelope(e, fulldrop) QS_IS_VERIFIED(q->q_state)) && bitset(QPINGONFAILURE, q->q_flags)) { - failure_return = TRUE; + failure_return = true; q->q_state = QS_BADADDR; } } @@ -285,7 +390,7 @@ dropenvelope(e, fulldrop) auto ADDRESS *rlist = NULL; if (tTd(50, 8)) - dprintf("dropenvelope(%s): sending return receipt\n", + sm_dprintf("dropenvelope(%s): sending return receipt\n", id); e->e_flags |= EF_SENDRECEIPT; (void) sendtolist(e->e_from.q_paddr, NULLADDR, &rlist, 0, e); @@ -300,8 +405,8 @@ dropenvelope(e, fulldrop) if ((failure_return || delay_return) && e->e_errormode != EM_QUIET) { if (tTd(50, 8)) - dprintf("dropenvelope(%s): saving mail\n", id); - savemail(e, !bitset(EF_NO_BODY_RETN, e->e_flags)); + sm_dprintf("dropenvelope(%s): saving mail\n", id); + savedf = savemail(e, !bitset(EF_NO_BODY_RETN, e->e_flags)); } /* @@ -321,7 +426,7 @@ dropenvelope(e, fulldrop) expand(PostMasterCopy, pcopy, sizeof pcopy, e); if (tTd(50, 8)) - dprintf("dropenvelope(%s): sending postmaster copy to %s\n", + sm_dprintf("dropenvelope(%s): sending postmaster copy to %s\n", id, pcopy); (void) sendtolist(pcopy, NULLADDR, &rlist, 0, e); } @@ -338,43 +443,88 @@ dropenvelope(e, fulldrop) simpledrop: if (tTd(50, 8)) - dprintf("dropenvelope(%s): at simpledrop, queueit=%d\n", + sm_dprintf("dropenvelope(%s): at simpledrop, queueit=%d\n", id, queueit); if (!queueit || bitset(EF_CLRQUEUE, e->e_flags)) { if (tTd(50, 1)) { - dprintf("\n===== Dropping [dq]f%s... queueit=%d, e_flags=", + sm_dprintf("\n===== Dropping [dq]f%s... queueit=%d, e_flags=", e->e_id, queueit); printenvflags(e); } - xunlink(queuename(e, 'd')); - xunlink(queuename(e, 'q')); + if (!savedf) + (void) xunlink(queuename(e, 'd')); + if (xunlink(queuename(e, 'q')) == 0) + { + /* add to available space in filesystem */ + updfs(e, true, !savedf); + } if (e->e_ntries > 0 && LogLevel > 9) sm_syslog(LOG_INFO, id, "done; delay=%s, ntries=%d", - pintvl(curtime() - e->e_ctime, TRUE), + pintvl(curtime() - e->e_ctime, true), e->e_ntries); } else if (queueit || !bitset(EF_INQUEUE, e->e_flags)) { -#if QUEUE - queueup(e, FALSE); -#else /* QUEUE */ - syserr("554 5.3.0 dropenvelope: queueup"); -#endif /* QUEUE */ + if (!split) + queueup(e, false, true); + else + { + ENVELOPE *oldsib; + ENVELOPE *ee; + + /* + ** Save old sibling and set it to NULL to avoid + ** queueing up the same envelopes again. + ** This requires that envelopes in that list have + ** been take care of before (or at some other place). + */ + + oldsib = e->e_sibling; + e->e_sibling = NULL; + (void) split_by_recipient(e); + for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) + queueup(ee, false, true); + queueup(e, false, true); + + /* clean up */ + for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) + { + /* now unlock the job */ + if (tTd(50, 8)) + sm_dprintf("dropenvelope(%s): unlocking job\n", + ee->e_id); + closexscript(ee); + unlockqueue(ee); + + /* this envelope is marked unused */ + if (ee->e_dfp != NULL) + { + (void) sm_io_close(ee->e_dfp, + SM_TIME_DEFAULT); + ee->e_dfp = NULL; + } + ee->e_id = NULL; + ee->e_flags &= ~EF_HAS_DF; + } + e->e_sibling = oldsib; + } } /* now unlock the job */ if (tTd(50, 8)) - dprintf("dropenvelope(%s): unlocking job\n", id); + sm_dprintf("dropenvelope(%s): unlocking job\n", id); closexscript(e); unlockqueue(e); /* make sure that this envelope is marked unused */ if (e->e_dfp != NULL) - (void) bfclose(e->e_dfp); - e->e_dfp = NULL; + { + (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); + e->e_dfp = NULL; + } e->e_id = NULL; e->e_flags &= ~EF_HAS_DF; } @@ -389,6 +539,9 @@ simpledrop: ** fullclear - if set, the current envelope is total ** garbage and should be ignored; otherwise, ** release any resources it may indicate. +** rpool -- either NULL, or a pointer to a resource pool +** from which envelope memory is allocated, and +** to which envelope resources are attached. ** ** Returns: ** none. @@ -399,34 +552,70 @@ simpledrop: */ void -clearenvelope(e, fullclear) +clearenvelope(e, fullclear, rpool) register ENVELOPE *e; bool fullclear; + SM_RPOOL_T *rpool; { register HDR *bh; register HDR **nhp; extern ENVELOPE BlankEnvelope; + char **p; if (!fullclear) { /* clear out any file information */ if (e->e_xfp != NULL) - (void) bfclose(e->e_xfp); + (void) sm_io_close(e->e_xfp, SM_TIME_DEFAULT); if (e->e_dfp != NULL) - (void) bfclose(e->e_dfp); + (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); e->e_xfp = e->e_dfp = NULL; } - /* now clear out the data */ - STRUCTCOPY(BlankEnvelope, *e); + /* + ** Copy BlankEnvelope into *e. + ** It is not safe to simply copy pointers to strings; + ** the strings themselves must be copied (or set to NULL). + ** The problem is that when we assign a new string value to + ** a member of BlankEnvelope, we free the old string. + ** We did not need to do this copying in sendmail 8.11 :-( + ** and it is a potential performance hit. Reference counted + ** strings are one way out. + */ + + *e = BlankEnvelope; e->e_message = NULL; + + /* + ** Copy the macro table. + ** We might be able to avoid this by zeroing the macro table + ** and always searching BlankEnvelope.e_macro after e->e_macro + ** in macvalue(). + */ + + for (p = &e->e_macro.mac_table[0]; + p <= &e->e_macro.mac_table[MAXMACROID]; + ++p) + { + if (*p != NULL) + *p = sm_rpool_strdup_x(rpool, *p); + } + + /* + ** XXX There are many strings in the envelope structure + ** XXX that we are not attempting to copy here. + ** XXX Investigate this further. + */ + + e->e_rpool = rpool; + e->e_macro.mac_rpool = rpool; if (Verbose) set_delivery_mode(SM_DELIVER, e); bh = BlankEnvelope.e_header; nhp = &e->e_header; while (bh != NULL) { - *nhp = (HDR *) xalloc(sizeof *bh); + *nhp = (HDR *) sm_rpool_malloc_x(rpool, sizeof *bh); memmove((char *) *nhp, (char *) bh, sizeof *bh); bh = bh->h_link; nhp = &(*nhp)->h_link; @@ -453,8 +642,7 @@ void initsys(e) register ENVELOPE *e; { - char cbuf[5]; /* holds hop count */ - char pbuf[10]; /* holds pid */ + char buf[10]; #ifdef TTYNAME static char ybuf[60]; /* holds tty id */ register char *p; @@ -464,9 +652,9 @@ initsys(e) /* ** Give this envelope a reality. ** I.e., an id, a transcript, and a creation time. + ** We don't select the queue until all of the recipients are known. */ - setnewqueue(e); openxscript(e); e->e_ctime = curtime(); #if _FFR_QUEUEDELAY @@ -490,18 +678,18 @@ initsys(e) */ /* process id */ - (void) snprintf(pbuf, sizeof pbuf, "%d", (int) getpid()); - define('p', newstr(pbuf), e); + (void) sm_snprintf(buf, sizeof buf, "%d", (int) CurrentPid); + macdefine(&e->e_macro, A_TEMP, 'p', buf); /* hop count */ - (void) snprintf(cbuf, sizeof cbuf, "%d", e->e_hopcount); - define('c', newstr(cbuf), e); + (void) sm_snprintf(buf, sizeof buf, "%d", e->e_hopcount); + macdefine(&e->e_macro, A_TEMP, 'c', buf); /* time as integer, unix time, arpa time */ settime(e); /* Load average */ - (void)sm_getla(e); + sm_getla(); #ifdef TTYNAME /* tty name */ @@ -512,8 +700,8 @@ initsys(e) { if (strrchr(p, '/') != NULL) p = strrchr(p, '/') + 1; - snprintf(ybuf, sizeof ybuf, "%s", p); - define('y', ybuf, e); + (void) sm_strlcpy(ybuf, sizeof ybuf, p); + macdefine(&e->e_macro, A_PERM, 'y', ybuf); } } #endif /* TTYNAME */ @@ -537,25 +725,23 @@ settime(e) { register char *p; auto time_t now; - char tbuf[20]; /* holds "current" time */ - char dbuf[30]; /* holds ctime(tbuf) */ + char buf[30]; register struct tm *tm; now = curtime(); tm = gmtime(&now); - (void) snprintf(tbuf, sizeof tbuf, "%04d%02d%02d%02d%02d", tm->tm_year + 1900, - tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); - define('t', newstr(tbuf), e); - (void) strlcpy(dbuf, ctime(&now), sizeof dbuf); - p = strchr(dbuf, '\n'); + (void) sm_snprintf(buf, sizeof buf, "%04d%02d%02d%02d%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min); + macdefine(&e->e_macro, A_TEMP, 't', buf); + (void) sm_strlcpy(buf, ctime(&now), sizeof buf); + p = strchr(buf, '\n'); if (p != NULL) *p = '\0'; - define('d', newstr(dbuf), e); - p = arpadate(dbuf); - p = newstr(p); + macdefine(&e->e_macro, A_TEMP, 'd', buf); + macdefine(&e->e_macro, A_TEMP, 'b', arpadate(buf)); if (macvalue('a', e) == NULL) - define('a', p, e); - define('b', p, e); + macdefine(&e->e_macro, A_PERM, 'a', macvalue('b', e)); } /* ** OPENXSCRIPT -- Open transcript file @@ -598,19 +784,17 @@ openxscript(e) if (e->e_xfp == NULL) { syserr("Can't create transcript file %s", p); - e->e_xfp = fopen("/dev/null", "r+"); + e->e_xfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, + SM_PATH_DEVNULL, SM_IO_RDWR, NULL); if (e->e_xfp == NULL) - syserr("!Can't open /dev/null"); + syserr("!Can't open %s", SM_PATH_DEVNULL); } -#if HASSETVBUF - (void) setvbuf(e->e_xfp, NULL, _IOLBF, 0); -#else /* HASSETVBUF */ - (void) setlinebuf(e->e_xfp); -#endif /* HASSETVBUF */ + (void) sm_io_setvbuf(e->e_xfp, SM_TIME_DEFAULT, NULL, SM_IO_LBF, 0); if (tTd(46, 9)) { - dprintf("openxscript(%s):\n ", p); - dumpfd(fileno(e->e_xfp), TRUE, FALSE); + sm_dprintf("openxscript(%s):\n ", p); + dumpfd(sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL), true, + false); } } /* @@ -636,7 +820,7 @@ closexscript(e) if (e->e_lockfp == NULL) syserr("closexscript: job not locked"); #endif /* 0 */ - (void) bfclose(e->e_xfp); + (void) sm_io_close(e->e_xfp, SM_TIME_DEFAULT); e->e_xfp = NULL; } /* @@ -688,14 +872,13 @@ setsender(from, e, delimptr, delimchar, internal) { register char **pvp; char *realname = NULL; - register struct passwd *pw; char *bp; char buf[MAXNAME + 2]; char pvpbuf[PSBUFSIZE]; extern char *FullName; if (tTd(45, 1)) - dprintf("setsender(%s)\n", from == NULL ? "" : from); + sm_dprintf("setsender(%s)\n", from == NULL ? "" : from); /* ** Figure out the real user executing us. @@ -709,17 +892,16 @@ setsender(from, e, delimptr, delimchar, internal) realname = username(); if (ConfigLevel < 2) - SuprErrs = TRUE; + SuprErrs = true; + + macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e s"); -#if _FFR_ADDR_TYPE - define(macid("{addr_type}", NULL), "e s", e); -#endif /* _FFR_ADDR_TYPE */ /* preset state for then clause in case from == NULL */ e->e_from.q_state = QS_BADADDR; e->e_from.q_flags = 0; if (from == NULL || parseaddr(from, &e->e_from, RF_COPYALL|RF_SENDERADDR, - delimchar, delimptr, e) == NULL || + delimchar, delimptr, e, false) == NULL || QS_IS_BADADDR(e->e_from.q_state) || e->e_from.q_mailer == ProgMailer || e->e_from.q_mailer == FileMailer || @@ -738,9 +920,9 @@ setsender(from, e, delimptr, delimchar, internal) if (host == NULL) host = MyHostName; - (void) snprintf(ebuf, sizeof ebuf, "%.*s@%.*s", - MAXNAME, realname, - MAXNAME, host); + (void) sm_snprintf(ebuf, sizeof ebuf, + "%.*s@%.*s", MAXNAME, + realname, MAXNAME, host); p = ebuf; } sm_syslog(LOG_NOTICE, e->e_id, @@ -756,39 +938,41 @@ setsender(from, e, delimptr, delimchar, internal) usrerrenh(e->e_status, "553 Invalid sender address"); } - SuprErrs = TRUE; + SuprErrs = true; } if (from == realname || - parseaddr(from = newstr(realname), &e->e_from, - RF_COPYALL|RF_SENDERADDR, ' ', NULL, e) == NULL) + parseaddr(from = realname, + &e->e_from, RF_COPYALL|RF_SENDERADDR, ' ', + NULL, e, false) == NULL) { char nbuf[100]; - SuprErrs = TRUE; + SuprErrs = true; expand("\201n", nbuf, sizeof nbuf, e); - if (parseaddr(from = newstr(nbuf), &e->e_from, - RF_COPYALL, ' ', NULL, e) == NULL && + from = sm_rpool_strdup_x(e->e_rpool, nbuf); + if (parseaddr(from, &e->e_from, RF_COPYALL, ' ', + NULL, e, false) == NULL && parseaddr(from = "postmaster", &e->e_from, - RF_COPYALL, ' ', NULL, e) == NULL) + RF_COPYALL, ' ', NULL, e, false) == NULL) syserr("553 5.3.0 setsender: can't even parse postmaster!"); } } else - FromFlag = TRUE; + FromFlag = true; e->e_from.q_state = QS_SENDER; if (tTd(45, 5)) { - dprintf("setsender: QS_SENDER "); - printaddr(&e->e_from, FALSE); + sm_dprintf("setsender: QS_SENDER "); + printaddr(&e->e_from, false); } - SuprErrs = FALSE; + SuprErrs = false; #if USERDB if (bitnset(M_CHECKUDB, e->e_from.q_mailer->m_flags)) { register char *p; - p = udbsender(e->e_from.q_user); + p = udbsender(e->e_from.q_user, e->e_rpool); if (p != NULL) from = p; } @@ -796,6 +980,8 @@ setsender(from, e, delimptr, delimchar, internal) if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags)) { + SM_MBDB_T user; + if (!internal) { /* if the user already given fullname don't redefine */ @@ -806,34 +992,36 @@ setsender(from, e, delimptr, delimchar, internal) } if (e->e_from.q_user[0] != '\0' && - (pw = sm_getpwnam(e->e_from.q_user)) != NULL) + sm_mbdb_lookup(e->e_from.q_user, &user) == EX_OK) { /* ** Process passwd file entry. */ /* extract home directory */ - if (*pw->pw_dir == '\0') + if (*user.mbdb_homedir == '\0') e->e_from.q_home = NULL; - else if (strcmp(pw->pw_dir, "/") == 0) - e->e_from.q_home = newstr(""); + else if (strcmp(user.mbdb_homedir, "/") == 0) + e->e_from.q_home = ""; else - e->e_from.q_home = newstr(pw->pw_dir); - define('z', e->e_from.q_home, e); + e->e_from.q_home = sm_rpool_strdup_x(e->e_rpool, + user.mbdb_homedir); + macdefine(&e->e_macro, A_PERM, 'z', e->e_from.q_home); /* extract user and group id */ - e->e_from.q_uid = pw->pw_uid; - e->e_from.q_gid = pw->pw_gid; - e->e_from.q_flags |= QGOODUID; + if (user.mbdb_uid != SM_NO_UID) + { + e->e_from.q_uid = user.mbdb_uid; + e->e_from.q_gid = user.mbdb_gid; + e->e_from.q_flags |= QGOODUID; + } /* extract full name from passwd file */ - if (FullName == NULL && pw->pw_gecos != NULL && - strcmp(pw->pw_name, e->e_from.q_user) == 0 && - !internal) + if (FullName == NULL && !internal && + user.mbdb_fullname[0] != '\0' && + strcmp(user.mbdb_name, e->e_from.q_user) == 0) { - buildfname(pw->pw_gecos, e->e_from.q_user, buf, sizeof buf); - if (buf[0] != '\0') - FullName = newstr(buf); + FullName = newstr(user.mbdb_fullname); } } else @@ -841,7 +1029,7 @@ setsender(from, e, delimptr, delimchar, internal) e->e_from.q_home = NULL; } if (FullName != NULL && !internal) - define('x', FullName, e); + macdefine(&e->e_macro, A_PERM, 'x', FullName); } else if (!internal && OpMode != MD_DAEMON && OpMode != MD_SMTP) { @@ -874,24 +1062,22 @@ setsender(from, e, delimptr, delimchar, internal) sm_syslog(LOG_NOTICE, e->e_id, "cannot prescan from (%s)", shortenstring(from, MAXSHORTSTR)); - finis(TRUE, ExitStat); + finis(true, ExitStat); } - (void) rewrite(pvp, 3, 0, e); - (void) rewrite(pvp, 1, 0, e); - (void) rewrite(pvp, 4, 0, e); -#if _FFR_ADDR_TYPE - define(macid("{addr_type}", NULL), NULL, e); -#endif /* _FFR_ADDR_TYPE */ + (void) REWRITE(pvp, 3, e); + (void) REWRITE(pvp, 1, e); + (void) REWRITE(pvp, 4, e); + macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), NULL); bp = buf + 1; cataddr(pvp, NULL, bp, sizeof buf - 2, '\0'); if (*bp == '@' && !bitnset(M_NOBRACKET, e->e_from.q_mailer->m_flags)) { /* heuristic: route-addr: add angle brackets */ - (void) strlcat(bp, ">", sizeof buf - 1); + (void) sm_strlcat(bp, ">", sizeof buf - 1); *--bp = '<'; } - e->e_sender = newstr(bp); - define('f', e->e_sender, e); + e->e_sender = sm_rpool_strdup_x(e->e_rpool, bp); + macdefine(&e->e_macro, A_PERM, 'f', e->e_sender); /* save the domain spec if this mailer wants it */ if (e->e_from.q_mailer != NULL && @@ -900,15 +1086,11 @@ setsender(from, e, delimptr, delimchar, internal) char **lastat; /* get rid of any pesky angle brackets */ -#if _FFR_ADDR_TYPE - define(macid("{addr_type}", NULL), "e s", e); -#endif /* _FFR_ADDR_TYPE */ - (void) rewrite(pvp, 3, 0, e); - (void) rewrite(pvp, 1, 0, e); - (void) rewrite(pvp, 4, 0, e); -#if _FFR_ADDR_TYPE - define(macid("{addr_type}", NULL), NULL, e); -#endif /* _FFR_ADDR_TYPE */ + macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e s"); + (void) REWRITE(pvp, 3, e); + (void) REWRITE(pvp, 1, e); + (void) REWRITE(pvp, 4, e); + macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), NULL); /* strip off to the last "@" sign */ for (lastat = NULL; *pvp != NULL; pvp++) @@ -916,10 +1098,10 @@ setsender(from, e, delimptr, delimchar, internal) lastat = pvp; if (lastat != NULL) { - e->e_fromdomain = copyplist(lastat, TRUE); + e->e_fromdomain = copyplist(lastat, true, e->e_rpool); if (tTd(45, 3)) { - dprintf("Saving from domain: "); + sm_dprintf("Saving from domain: "); printav(e->e_fromdomain); } } @@ -937,8 +1119,8 @@ setsender(from, e, delimptr, delimchar, internal) struct eflags { - char *ef_name; - u_long ef_bit; + char *ef_name; + unsigned long ef_bit; }; static struct eflags EnvelopeFlags[] = @@ -967,6 +1149,8 @@ static struct eflags EnvelopeFlags[] = { "HAS_DF", EF_HAS_DF }, { "IS_MIME", EF_IS_MIME }, { "DONT_MIME", EF_DONT_MIME }, + { "DISCARD", EF_DISCARD }, + { "TOOBIG", EF_TOOBIG }, { NULL, 0 } }; @@ -975,19 +1159,21 @@ printenvflags(e) register ENVELOPE *e; { register struct eflags *ef; - bool first = TRUE; + bool first = true; - printf("%lx", e->e_flags); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%lx", e->e_flags); for (ef = EnvelopeFlags; ef->ef_name != NULL; ef++) { if (!bitset(ef->ef_bit, e->e_flags)) continue; if (first) - printf("<%s", ef->ef_name); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "<%s", + ef->ef_name); else - printf(",%s", ef->ef_name); - first = FALSE; + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, ",%s", + ef->ef_name); + first = false; } if (!first) - printf(">\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, ">\n"); } diff --git a/gnu/usr.sbin/sendmail/sendmail/err.c b/gnu/usr.sbin/sendmail/sendmail/err.c index 8551c1e85d8..978a8c37f00 100644 --- a/gnu/usr.sbin/sendmail/sendmail/err.c +++ b/gnu/usr.sbin/sendmail/sendmail/err.c @@ -11,42 +11,97 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: err.c,v 8.120.4.5 2001/08/17 22:09:40 ca Exp $"; -#endif /* ! lint */ - #include <sendmail.h> -#ifdef LDAPMAP + +SM_RCSID("@(#)$Sendmail: err.c,v 8.184 2001/09/04 22:43:03 ca Exp $") + +#if LDAPMAP # include <lber.h> # include <ldap.h> /* for LDAP error codes */ #endif /* LDAPMAP */ - static void putoutmsg __P((char *, bool, bool)); static void puterrmsg __P((char *)); static char *fmtmsg __P((char *, const char *, const char *, const char *, int, const char *, va_list)); /* +** FATAL_ERROR -- handle a fatal exception +** +** This function is installed as the default exception handler +** in the main sendmail process, and in all child processes +** that we create. Its job is to handle exceptions that are not +** handled at a lower level. +** +** The theory is that unhandled exceptions will be 'fatal' class +** exceptions (with an "F:" prefix), such as the out-of-memory +** exception "F:sm.heap". As such, they are handled by exiting +** the process in exactly the same way that xalloc() in Sendmail 8.10 +** exits the process when it fails due to lack of memory: +** we call syserr with a message beginning with "!". +** +** Parameters: +** exc -- exception which is terminating this process +** +** Returns: +** none +*/ + +void +fatal_error(exc) + SM_EXC_T *exc; +{ + static char buf[256]; + SM_FILE_T f; + + /* + ** This function may be called when the heap is exhausted. + ** The following code writes the message for 'exc' into our + ** static buffer without allocating memory or raising exceptions. + */ + + sm_strio_init(&f, buf, sizeof(buf)); + sm_exc_write(exc, &f); + (void) sm_io_flush(&f, SM_TIME_DEFAULT); + + /* + ** Terminate the process after logging an error and cleaning up. + ** Problems: + ** - syserr decides what class of error this is by looking at errno. + ** That's no good; we should look at the exc structure. + ** - The cleanup code should be moved out of syserr + ** and into individual exception handlers + ** that are part of the module they clean up after. + */ + + errno = ENOMEM; + syserr("!%s", buf); +} + +/* ** SYSERR -- Print error message. ** -** Prints an error message via printf to the diagnostic output. +** Prints an error message via sm_io_printf to the diagnostic output. ** ** If the first character of the syserr message is `!' it will ** log this as an ALERT message and exit immediately. This can ** leave queue files in an indeterminate state, so it should not ** be used lightly. ** +** If the first character of the syserr message is '!' or '@' +** then syserr knows that the process is about to be terminated, +** so the SMTP reply code defaults to 421. Otherwise, the +** reply code defaults to 451 or 554, depending on errno. +** ** Parameters: -** fmt -- the format string. If it does not begin with -** a three-digit SMTP reply code, either 554 or -** 451 is assumed depending on whether errno -** is set. +** fmt -- the format string. An optional '!' or '@', +** followed by an optional three-digit SMTP +** reply code, followed by message text. ** (others) -- parameters ** ** Returns: ** none -** Through TopFrame if QuickAbort is set. +** Raises E:mta.quickabort if QuickAbort is set. ** ** Side Effects: ** increments Errors. @@ -73,22 +128,45 @@ syserr(fmt, va_alist) register char *p; int save_errno = errno; bool panic; + bool exiting; char *user; char *enhsc; char *errtxt; struct passwd *pw; char ubuf[80]; - VA_LOCAL_DECL + SM_VA_LOCAL_DECL - panic = *fmt == '!'; - if (panic) + switch (*fmt) { - fmt++; - HoldErrs = FALSE; + case '!': + ++fmt; + panic = true; + exiting = true; + break; + case '@': + ++fmt; + panic = false; + exiting = true; + break; + default: + panic = false; + exiting = false; + break; } /* format and output the error message */ - if (save_errno == 0) + if (exiting) + { + /* + ** Since we are terminating the process, + ** we are aborting the entire SMTP session, + ** rather than just the current transaction. + */ + + p = "421"; + enhsc = "4.0.0"; + } + else if (save_errno == 0) { p = "554"; enhsc = "5.0.0"; @@ -98,17 +176,19 @@ syserr(fmt, va_alist) p = "451"; enhsc = "4.0.0"; } - VA_START(fmt); + SM_VA_START(ap, fmt); errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap); - VA_END; + SM_VA_END(ap); puterrmsg(MsgBuf); /* save this message for mailq printing */ if (!panic && CurEnv != NULL) { - if (CurEnv->e_message != NULL) + char *nmsg = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); + + if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) sm_free(CurEnv->e_message); - CurEnv->e_message = newstr(errtxt); + CurEnv->e_message = nmsg; } /* determine exit status if not already set */ @@ -119,7 +199,7 @@ syserr(fmt, va_alist) else ExitStat = EX_OSERR; if (tTd(54, 1)) - dprintf("syserr: ExitStat = %d\n", ExitStat); + sm_dprintf("syserr: ExitStat = %d\n", ExitStat); } pw = sm_getpwuid(RealUid); @@ -128,7 +208,7 @@ syserr(fmt, va_alist) else { user = ubuf; - snprintf(ubuf, sizeof ubuf, "UID%d", (int) RealUid); + (void) sm_snprintf(ubuf, sizeof ubuf, "UID%d", (int) RealUid); } if (LogLevel > 0) @@ -157,13 +237,13 @@ syserr(fmt, va_alist) #ifdef ESTALE case ESTALE: #endif /* ESTALE */ - printopenfds(TRUE); - mci_dump_all(TRUE); + printopenfds(true); + mci_dump_all(true); break; } if (panic) { -#ifdef XLA +#if XLA xla_all_end(); #endif /* XLA */ sync_queue_time(); @@ -173,7 +253,7 @@ syserr(fmt, va_alist) } errno = 0; if (QuickAbort) - longjmp(TopFrame, 2); + sm_exc_raisenew_x(&EtypeQuickAbort, 2); } /* ** USRERR -- Signal user error. @@ -182,12 +262,12 @@ syserr(fmt, va_alist) ** ** Parameters: ** fmt -- the format string. If it does not begin with -** a three-digit SMTP reply code, 501 is assumed. -** (others) -- printf strings +** a three-digit SMTP reply code, 550 is assumed. +** (others) -- sm_io_printf strings ** ** Returns: ** none -** Through TopFrame if QuickAbort is set. +** Raises E:mta.quickabort if QuickAbort is set. ** ** Side Effects: ** increments Errors. @@ -205,7 +285,7 @@ usrerr(fmt, va_alist) { char *enhsc; char *errtxt; - VA_LOCAL_DECL + SM_VA_LOCAL_DECL if (fmt[0] == '5' || fmt[0] == '6') enhsc = "5.0.0"; @@ -215,9 +295,9 @@ usrerr(fmt, va_alist) enhsc = "2.0.0"; else enhsc = NULL; - VA_START(fmt); - errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "501", enhsc, 0, fmt, ap); - VA_END; + SM_VA_START(ap, fmt); + errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap); + SM_VA_END(ap); if (SuprErrs) return; @@ -234,30 +314,31 @@ usrerr(fmt, va_alist) case '5': case '6': - if (CurEnv->e_message != NULL) + if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) sm_free(CurEnv->e_message); if (MsgBuf[0] == '6') { char buf[MAXLINE]; - snprintf(buf, sizeof buf, "Postmaster warning: %.*s", - (int) sizeof buf - 22, errtxt); - CurEnv->e_message = newstr(buf); + (void) sm_snprintf(buf, sizeof buf, + "Postmaster warning: %.*s", + (int) sizeof buf - 22, errtxt); + CurEnv->e_message = + sm_rpool_strdup_x(CurEnv->e_rpool, buf); } else { - CurEnv->e_message = newstr(errtxt); + CurEnv->e_message = + sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); } break; } puterrmsg(MsgBuf); - if (LogLevel > 3 && LogUsrErrs) sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt); - if (QuickAbort) - longjmp(TopFrame, 1); + sm_exc_raisenew_x(&EtypeQuickAbort, 1); } /* ** USRERRENH -- Signal user error. @@ -267,12 +348,12 @@ usrerr(fmt, va_alist) ** Parameters: ** enhsc -- the enhanced status code. ** fmt -- the format string. If it does not begin with -** a three-digit SMTP reply code, 501 is assumed. -** (others) -- printf strings +** a three-digit SMTP reply code, 550 is assumed. +** (others) -- sm_io_printf strings ** ** Returns: ** none -** Through TopFrame if QuickAbort is set. +** Raises E:mta.quickabort if QuickAbort is set. ** ** Side Effects: ** increments Errors. @@ -290,7 +371,7 @@ usrerrenh(enhsc, fmt, va_alist) #endif /* __STDC__ */ { char *errtxt; - VA_LOCAL_DECL + SM_VA_LOCAL_DECL if (enhsc == NULL || *enhsc == '\0') { @@ -301,9 +382,9 @@ usrerrenh(enhsc, fmt, va_alist) else if (fmt[0] == '2') enhsc = "2.0.0"; } - VA_START(fmt); - errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "501", enhsc, 0, fmt, ap); - VA_END; + SM_VA_START(ap, fmt); + errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap); + SM_VA_END(ap); if (SuprErrs) return; @@ -320,38 +401,39 @@ usrerrenh(enhsc, fmt, va_alist) case '5': case '6': - if (CurEnv->e_message != NULL) + if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) sm_free(CurEnv->e_message); if (MsgBuf[0] == '6') { char buf[MAXLINE]; - snprintf(buf, sizeof buf, "Postmaster warning: %.*s", - (int) sizeof buf - 22, errtxt); - CurEnv->e_message = newstr(buf); + (void) sm_snprintf(buf, sizeof buf, + "Postmaster warning: %.*s", + (int) sizeof buf - 22, errtxt); + CurEnv->e_message = + sm_rpool_strdup_x(CurEnv->e_rpool, buf); } else { - CurEnv->e_message = newstr(errtxt); + CurEnv->e_message = + sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); } break; } puterrmsg(MsgBuf); - if (LogLevel > 3 && LogUsrErrs) sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt); - if (QuickAbort) - longjmp(TopFrame, 1); + sm_exc_raisenew_x(&EtypeQuickAbort, 1); } /* ** MESSAGE -- print message (not necessarily an error) ** ** Parameters: -** msg -- the message (printf fmt) -- it can begin with +** msg -- the message (sm_io_printf fmt) -- it can begin with ** an SMTP reply code. If not, 050 is assumed. -** (others) -- printf arguments +** (others) -- sm_io_printf arguments ** ** Returns: ** none @@ -371,13 +453,13 @@ message(msg, va_alist) #endif /* __STDC__ */ { char *errtxt; - VA_LOCAL_DECL + SM_VA_LOCAL_DECL errno = 0; - VA_START(msg); + SM_VA_START(ap, msg); errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap); - VA_END; - putoutmsg(MsgBuf, FALSE, FALSE); + SM_VA_END(ap); + putoutmsg(MsgBuf, false, false); /* save this message for mailq printing */ switch (MsgBuf[0]) @@ -389,9 +471,10 @@ message(msg, va_alist) /* FALLTHROUGH */ case '5': - if (CurEnv->e_message != NULL) + if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) sm_free(CurEnv->e_message); - CurEnv->e_message = newstr(errtxt); + CurEnv->e_message = + sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); break; } } @@ -401,10 +484,10 @@ message(msg, va_alist) ** Just like "message" except it never puts the to... tag on. ** ** Parameters: -** msg -- the message (printf fmt) -- if it begins +** msg -- the message (sm_io_printf fmt) -- if it begins ** with a three digit SMTP reply code, that is used, ** otherwise 050 is assumed. -** (others) -- printf arguments +** (others) -- sm_io_printf arguments ** ** Returns: ** none @@ -424,14 +507,14 @@ nmessage(msg, va_alist) #endif /* __STDC__ */ { char *errtxt; - VA_LOCAL_DECL + SM_VA_LOCAL_DECL errno = 0; - VA_START(msg); + SM_VA_START(ap, msg); errtxt = fmtmsg(MsgBuf, (char *) NULL, "050", (char *) NULL, 0, msg, ap); - VA_END; - putoutmsg(MsgBuf, FALSE, FALSE); + SM_VA_END(ap); + putoutmsg(MsgBuf, false, false); /* save this message for mailq printing */ switch (MsgBuf[0]) @@ -443,9 +526,10 @@ nmessage(msg, va_alist) /* FALLTHROUGH */ case '5': - if (CurEnv->e_message != NULL) + if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL) sm_free(CurEnv->e_message); - CurEnv->e_message = newstr(errtxt); + CurEnv->e_message = + sm_rpool_strdup_x(CurEnv->e_rpool, errtxt); break; } } @@ -454,9 +538,9 @@ nmessage(msg, va_alist) ** ** Parameters: ** msg -- message to output (in SMTP format). -** holdmsg -- if TRUE, don't output a copy of the message to +** holdmsg -- if true, don't output a copy of the message to ** our output channel. -** heldmsg -- if TRUE, this is a previously held message; +** heldmsg -- if true, this is a previously held message; ** don't log it to the transcript file. ** ** Returns: @@ -479,7 +563,7 @@ putoutmsg(msg, holdmsg, heldmsg) /* display for debugging */ if (tTd(54, 8)) - dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "", + sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "", heldmsg ? " (held)" : ""); /* map warnings to something SMTP can handle */ @@ -491,12 +575,13 @@ putoutmsg(msg, holdmsg, heldmsg) /* output to transcript if serious */ if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL && strchr("45", msg[0]) != NULL) - fprintf(CurEnv->e_xfp, "%s\n", msg); + (void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n", + msg); - if (LogLevel >= 15 && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) + if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) sm_syslog(LOG_INFO, CurEnv->e_id, - "--> %s%s", - msg, holdmsg ? " (held)" : ""); + "--- %s%s%s", msg, holdmsg ? " (hold)" : "", + heldmsg ? " (held)" : ""); if (msgcode == '8') msg[0] = '0'; @@ -510,11 +595,11 @@ putoutmsg(msg, holdmsg, heldmsg) msg[0] = msgcode; if (HeldMessageBuf[0] == '5' && msgcode == '4') return; - snprintf(HeldMessageBuf, sizeof HeldMessageBuf, "%s", msg); + (void) sm_strlcpy(HeldMessageBuf, msg, sizeof HeldMessageBuf); return; } - (void) fflush(stdout); + (void) sm_io_flush(smioout, SM_TIME_DEFAULT); if (OutChannel == NULL) return; @@ -535,15 +620,21 @@ putoutmsg(msg, holdmsg, heldmsg) /* if DisConnected, OutChannel now points to the transcript */ if (!DisConnected && (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) - fprintf(OutChannel, "%s\r\n", msg); + (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n", + msg); else - fprintf(OutChannel, "%s\n", errtxt); + (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n", + errtxt); if (TrafficLogFile != NULL) - fprintf(TrafficLogFile, "%05d >>> %s\n", (int) getpid(), - (OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : errtxt); + (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, + "%05d >>> %s\n", (int) CurrentPid, + (OpMode == MD_SMTP || OpMode == MD_DAEMON) + ? msg : errtxt); +#if !PIPELINING + /* XXX can't flush here for SMTP pipelining */ if (msg[3] == ' ') - (void) fflush(OutChannel); - if (!ferror(OutChannel) || DisConnected) + (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); + if (!sm_io_error(OutChannel) || DisConnected) return; /* @@ -552,17 +643,18 @@ putoutmsg(msg, holdmsg, heldmsg) ** rude servers don't read result. */ - if (InChannel == NULL || feof(InChannel) || ferror(InChannel) || - strncmp(msg, "221", 3) == 0) + if (InChannel == NULL || sm_io_eof(InChannel) || + sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0) return; /* can't call syserr, 'cause we are using MsgBuf */ - HoldErrs = TRUE; + HoldErrs = true; if (LogLevel > 0) sm_syslog(LOG_CRIT, CurEnv->e_id, "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", - CurHostName == NULL ? "NO-HOST" : CurHostName, - shortenstring(msg, MAXSHORTSTR), errstring(errno)); + CURHOSTNAME, + shortenstring(msg, MAXSHORTSTR), sm_errstring(errno)); +#endif /* !PIPELINING */ } /* ** PUTERRMSG -- like putoutmsg, but does special processing for error messages @@ -584,11 +676,11 @@ puterrmsg(msg) char msgcode = msg[0]; /* output the message as usual */ - putoutmsg(msg, HoldErrs, FALSE); + putoutmsg(msg, HoldErrs, false); /* be careful about multiple error messages */ if (OnlyOneError) - HoldErrs = TRUE; + HoldErrs = true; /* signal the error */ Errors++; @@ -663,6 +755,7 @@ isenhsc(s, delim) ** Side Effects: ** fills e with enhanced status code. */ + int extenhsc(s, delim, e) const char *s; @@ -726,7 +819,7 @@ fmtmsg(eb, to, num, enhsc, eno, fmt, ap) const char *enhsc; int eno; const char *fmt; - va_list ap; + SM_VA_LOCAL_DECL { char del; int l; @@ -743,7 +836,15 @@ fmtmsg(eb, to, num, enhsc, eno, fmt, ap) del = '-'; else del = ' '; - (void) snprintf(eb, spaceleft, "%3.3s%c", num, del); +#if _FFR_SOFT_BOUNCE + if (SoftBounce && num[0] == '5') + { + /* replace 5 by 4 */ + (void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del); + } + else +#endif /* _FFR_SOFT_BOUNCE */ + (void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del); eb += 4; spaceleft -= 4; @@ -751,7 +852,7 @@ fmtmsg(eb, to, num, enhsc, eno, fmt, ap) { /* copy enh.status code including trailing blank */ l++; - (void) strlcpy(eb, fmt, l + 1); + (void) sm_strlcpy(eb, fmt, l + 1); eb += l; spaceleft -= l; fmt += l; @@ -759,19 +860,26 @@ fmtmsg(eb, to, num, enhsc, eno, fmt, ap) else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4) { /* copy enh.status code */ - (void) strlcpy(eb, enhsc, l + 1); + (void) sm_strlcpy(eb, enhsc, l + 1); eb[l] = ' '; eb[++l] = '\0'; eb += l; spaceleft -= l; } +#if _FFR_SOFT_BOUNCE + if (SoftBounce && eb[-l] == '5') + { + /* replace 5 by 4 */ + eb[-l] = '4'; + } +#endif /* _FFR_SOFT_BOUNCE */ errtxt = eb; /* output the file name and line number */ if (FileName != NULL) { - (void) snprintf(eb, spaceleft, "%s: line %d: ", - shortenstring(FileName, 83), LineNumber); + (void) sm_snprintf(eb, spaceleft, "%s: line %d: ", + shortenstring(FileName, 83), LineNumber); eb += (l = strlen(eb)); spaceleft -= l; } @@ -798,22 +906,22 @@ fmtmsg(eb, to, num, enhsc, eno, fmt, ap) strncmp(num, "550", 3) == 0 || strncmp(num, "553", 3) == 0)) { - (void) snprintf(eb, spaceleft, "%s... ", - shortenstring(to, MAXSHORTSTR)); + (void) sm_strlcpyn(eb, spaceleft, 2, + shortenstring(to, MAXSHORTSTR), "... "); spaceleft -= strlen(eb); while (*eb != '\0') *eb++ &= 0177; } /* output the message */ - (void) vsnprintf(eb, spaceleft, fmt, ap); + (void) sm_vsnprintf(eb, spaceleft, fmt, ap); spaceleft -= strlen(eb); while (*eb != '\0') *eb++ &= 0177; /* output the error code, if any */ if (eno != 0) - (void) snprintf(eb, spaceleft, ": %s", errstring(eno)); + (void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno)); return errtxt; } @@ -831,7 +939,7 @@ void buffer_errors() { HeldMessageBuf[0] = '\0'; - HoldErrs = TRUE; + HoldErrs = true; } /* ** FLUSH_ERRORS -- flush the held error message buffer @@ -849,12 +957,12 @@ flush_errors(print) bool print; { if (print && HeldMessageBuf[0] != '\0') - putoutmsg(HeldMessageBuf, FALSE, TRUE); + putoutmsg(HeldMessageBuf, false, true); HeldMessageBuf[0] = '\0'; - HoldErrs = FALSE; + HoldErrs = false; } /* -** ERRSTRING -- return string description of error code +** SM_ERRSTRING -- return string description of error code ** ** Parameters: ** errnum -- the error number to translate @@ -867,7 +975,7 @@ flush_errors(print) */ const char * -errstring(errnum) +sm_errstring(errnum) int errnum; { char *dnsmsg; @@ -887,58 +995,60 @@ errstring(errnum) dnsmsg = NULL; switch (errnum) { -#if defined(DAEMON) && defined(ETIMEDOUT) case ETIMEDOUT: case ECONNRESET: bp = buf; -# if HASSTRERROR - snprintf(bp, SPACELEFT(buf, bp), "%s", strerror(errnum)); -# else /* HASSTRERROR */ +#if HASSTRERROR + (void) sm_strlcpy(bp, strerror(errnum), SPACELEFT(buf, bp)); +#else /* HASSTRERROR */ if (errnum >= 0 && errnum < sys_nerr) - snprintf(bp, SPACELEFT(buf, bp), "%s", sys_errlist[errnum]); + (void) sm_strlcpy(bp, sys_errlist[errnum], + SPACELEFT(buf, bp)); else - snprintf(bp, SPACELEFT(buf, bp), "Error %d", errnum); -# endif /* HASSTRERROR */ + (void) sm_snprintf(bp, SPACELEFT(buf, bp), + "Error %d", errnum); +#endif /* HASSTRERROR */ bp += strlen(bp); if (CurHostName != NULL) { if (errnum == ETIMEDOUT) { - snprintf(bp, SPACELEFT(buf, bp), " with "); + (void) sm_snprintf(bp, SPACELEFT(buf, bp), + " with "); bp += strlen(bp); } else { bp = buf; - snprintf(bp, SPACELEFT(buf, bp), + (void) sm_snprintf(bp, SPACELEFT(buf, bp), "Connection reset by "); bp += strlen(bp); } - snprintf(bp, SPACELEFT(buf, bp), "%s", - shortenstring(CurHostName, MAXSHORTSTR)); + (void) sm_strlcpy(bp, + shortenstring(CurHostName, MAXSHORTSTR), + SPACELEFT(buf, bp)); bp += strlen(buf); } if (SmtpPhase != NULL) { - snprintf(bp, SPACELEFT(buf, bp), " during %s", - SmtpPhase); + (void) sm_snprintf(bp, SPACELEFT(buf, bp), + " during %s", SmtpPhase); } return buf; case EHOSTDOWN: if (CurHostName == NULL) break; - (void) snprintf(buf, sizeof buf, "Host %s is down", + (void) sm_snprintf(buf, sizeof buf, "Host %s is down", shortenstring(CurHostName, MAXSHORTSTR)); return buf; case ECONNREFUSED: if (CurHostName == NULL) break; - (void) snprintf(buf, sizeof buf, "Connection refused by %s", + (void) sm_strlcpyn(buf, sizeof buf, 2, "Connection refused by ", shortenstring(CurHostName, MAXSHORTSTR)); return buf; -#endif /* defined(DAEMON) && defined(ETIMEDOUT) */ #if NAMED_BIND case HOST_NOT_FOUND + E_DNSBASE: @@ -1006,18 +1116,18 @@ errstring(errnum) if (dnsmsg != NULL) { bp = buf; - bp += strlcpy(bp, "Name server: ", sizeof buf); + bp += sm_strlcpy(bp, "Name server: ", sizeof buf); if (CurHostName != NULL) { - snprintf(bp, SPACELEFT(buf, bp), "%s: ", - shortenstring(CurHostName, MAXSHORTSTR)); + (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, + shortenstring(CurHostName, MAXSHORTSTR), ": "); bp += strlen(bp); } - snprintf(bp, SPACELEFT(buf, bp), "%s", dnsmsg); + (void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp)); return buf; } -#ifdef LDAPMAP +#if LDAPMAP if (errnum >= E_LDAPBASE) return ldap_err2string(errnum - E_LDAPBASE); #endif /* LDAPMAP */ @@ -1028,7 +1138,7 @@ errstring(errnum) if (errnum > 0 && errnum < sys_nerr) return sys_errlist[errnum]; - (void) snprintf(buf, sizeof buf, "Error %d", errnum); + (void) sm_snprintf(buf, sizeof buf, "Error %d", errnum); return buf; #endif /* HASSTRERROR */ } diff --git a/gnu/usr.sbin/sendmail/sendmail/headers.c b/gnu/usr.sbin/sendmail/sendmail/headers.c index 51500a403a3..2ed6c1c4deb 100644 --- a/gnu/usr.sbin/sendmail/sendmail/headers.c +++ b/gnu/usr.sbin/sendmail/sendmail/headers.c @@ -11,12 +11,10 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: headers.c,v 8.203.4.13 2001/05/03 17:24:06 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +SM_RCSID("@(#)$Sendmail: headers.c,v 8.262 2001/09/04 22:43:03 ca Exp $") + static size_t fix_mime_header __P((char *)); static int priencode __P((char *)); static void put_vanilla_header __P((HDR *, char *, MCI *)); @@ -65,32 +63,32 @@ setupheaders() static struct hdrinfo NormalHeader = { NULL, 0, NULL }; -u_long +unsigned long chompheader(line, pflag, hdrp, e) char *line; int pflag; HDR **hdrp; register ENVELOPE *e; { - u_char mid = '\0'; + unsigned char mid = '\0'; register char *p; register HDR *h; HDR **hp; char *fname; char *fvalue; - bool cond = FALSE; + bool cond = false; bool dropfrom; bool headeronly; STAB *s; struct hdrinfo *hi; - bool nullheader = FALSE; + bool nullheader = false; BITMAP256 mopts; if (tTd(31, 6)) { - dprintf("chompheader: "); + sm_dprintf("chompheader: "); xputs(line); - dprintf("\n"); + sm_dprintf("\n"); } headeronly = hdrp != NULL; @@ -122,7 +120,7 @@ chompheader(line, pflag, hdrp, e) goto hse; } - mid = (u_char) *p++; + mid = (unsigned char) *p++; /* catch ?$abc? */ if (*p != '\0') @@ -140,7 +138,7 @@ chompheader(line, pflag, hdrp, e) goto hse; } - mid = (u_char)macid(p, NULL); + mid = (unsigned char) macid(p); if (bitset(0200, mid)) p += strlen(macname(mid)) + 2; else @@ -152,7 +150,6 @@ chompheader(line, pflag, hdrp, e) *q = '?'; goto hse; } - } else { @@ -165,7 +162,7 @@ chompheader(line, pflag, hdrp, e) } setbitn(bitidx(*p), mopts); - cond = TRUE; + cond = true; p++; } } @@ -196,7 +193,7 @@ hse: while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') - nullheader = TRUE; + nullheader = true; /* security scan: long field names are end-of-header */ if (strlen(fname) > 100) @@ -222,6 +219,12 @@ hse: { *endp = '\0'; s = stab(fname, ST_HEADER, ST_ENTER); + if (LogLevel > 9 && + s->s_header.hi_ruleset != NULL) + sm_syslog(LOG_WARNING, NOQID, + "Warning: redefined ruleset for header=%s, old=%s, new=%s", + fname, + s->s_header.hi_ruleset, p); s->s_header.hi_ruleset = newstr(p); if (!strc) s->s_header.hi_flags |= H_STRIPCOMM; @@ -240,11 +243,12 @@ hse: if (tTd(31, 9)) { if (s == NULL) - dprintf("no header flags match\n"); + sm_dprintf("no header flags match\n"); else - dprintf("header match, flags=%lx, ruleset=%s\n", - hi->hi_flags, - hi->hi_ruleset == NULL ? "<NULL>" : hi->hi_ruleset); + sm_dprintf("header match, flags=%lx, ruleset=%s\n", + hi->hi_flags, + hi->hi_ruleset == NULL ? "<NULL>" + : hi->hi_ruleset); } /* see if this is a resent message */ @@ -281,7 +285,7 @@ hse: if (bitset(pflag, CHHDR_CHECK)) { - bool stripcom = FALSE; + bool stripcom = false; char *rs; /* no ruleset? look for default */ @@ -300,52 +304,70 @@ hse: stripcom = bitset(hi->hi_flags, H_STRIPCOMM); if (rs != NULL) { - int l; + int l, k; char qval[MAXNAME]; - char hlen[16]; - char *sp, *dp; - dp = qval; l = 0; - dp[l++] = '"'; - for (sp = fvalue; *sp != '\0' && l < MAXNAME - 3; sp++) + qval[l++] = '"'; + + /* - 3 to avoid problems with " at the end */ + for (k = 0; fvalue[k] != '\0' && l < MAXNAME - 3; k++) { - switch(*sp) + switch (fvalue[k]) { + /* XXX other control chars? */ case '\011': /* ht */ case '\012': /* nl */ case '\013': /* vt */ case '\014': /* np */ case '\015': /* cr */ - dp[l++] = ' '; + qval[l++] = ' '; break; case '"': - dp[l++] = '\\'; + qval[l++] = '\\'; /* FALLTHROUGH */ default: - dp[l++] = *sp; + qval[l++] = fvalue[k]; break; } } - dp[l++] = '"'; - dp[l++] = '\0'; - l = strlen(fvalue); - snprintf(hlen, sizeof hlen, "%d", l); - define(macid("{hdrlen}", NULL), newstr(hlen), e); - if (l >= MAXNAME) + qval[l++] = '"'; + qval[l] = '\0'; + k += strlen(fvalue + k); + if (k >= MAXNAME) { if (LogLevel > 9) sm_syslog(LOG_WARNING, e->e_id, "Warning: truncated header '%s' before check with '%s' len=%d max=%d", - fname, rs, l, MAXNAME - 1); + fname, rs, k, MAXNAME - 1); } - if ((sp = macvalue(macid("{currHeader}", NULL), e)) != - NULL) - sm_free(sp); - define(macid("{currHeader}", NULL), newstr(qval), e); - define(macid("{hdr_name}", NULL), newstr(fname), e); - (void) rscheck(rs, fvalue, NULL, e, stripcom, TRUE, 4, - NULL); + macdefine(&e->e_macro, A_TEMP, + macid("{currHeader}"), qval); + macdefine(&e->e_macro, A_TEMP, + macid("{hdr_name}"), fname); + + (void) sm_snprintf(qval, sizeof qval, "%d", k); + macdefine(&e->e_macro, A_TEMP, macid("{hdrlen}"), qval); +#if _FFR_HDR_TYPE + /* + ** XXX: h isn't set yet + ** If we really want to be precise then we have + ** to lookup the header (see below). + ** It's probably not worth the effort. + */ + + if (bitset(H_FROM, h->h_flags)) + macdefine(&e->e_macro, A_PERM, + macid("{addr_type}"), "h s"); + else if (bitset(H_RCPT, h->h_flags)) + macdefine(&e->e_macro, A_PERM, + macid("{addr_type}"), "h r"); + else +#endif /* _FFR_HDR_TYPE */ + macdefine(&e->e_macro, A_PERM, + macid("{addr_type}"), "h"); + (void) rscheck(rs, fvalue, NULL, e, stripcom, true, 3, + NULL, e->e_id); } } @@ -355,16 +377,16 @@ hse: ** insert the full name information in all circumstances. */ - dropfrom = FALSE; + dropfrom = false; p = "resent-from"; if (!bitset(EF_RESENT, e->e_flags)) p += 7; if (!bitset(pflag, CHHDR_DEF) && !headeronly && - !bitset(EF_QUEUERUN, e->e_flags) && strcasecmp(fname, p) == 0) + !bitset(EF_QUEUERUN, e->e_flags) && sm_strcasecmp(fname, p) == 0) { if (tTd(31, 2)) { - dprintf("comparing header from (%s) against default (%s or %s)\n", + sm_dprintf("comparing header from (%s) against default (%s or %s)\n", fvalue, e->e_from.q_paddr, e->e_from.q_user); } if (e->e_from.q_paddr != NULL && @@ -372,13 +394,13 @@ hse: bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && (strcmp(fvalue, e->e_from.q_paddr) == 0 || strcmp(fvalue, e->e_from.q_user) == 0)) - dropfrom = TRUE; + dropfrom = true; } /* delete default value for this header */ for (hp = hdrp; (h = *hp) != NULL; hp = &h->h_link) { - if (strcasecmp(fname, h->h_field) == 0 && + if (sm_strcasecmp(fname, h->h_field) == 0 && !bitset(H_USER, h->h_flags) && !bitset(H_FORCE, h->h_flags)) { @@ -397,7 +419,7 @@ hse: if (!cond) { /* copy conditions from default case */ - memmove((char *)mopts, (char *)h->h_mflags, + memmove((char *) mopts, (char *) h->h_mflags, sizeof mopts); } h->h_macro = mid; @@ -405,9 +427,9 @@ hse: } /* create a new node */ - h = (HDR *) xalloc(sizeof *h); - h->h_field = newstr(fname); - h->h_value = newstr(fvalue); + h = (HDR *) sm_rpool_malloc_x(e->e_rpool, sizeof *h); + h->h_field = sm_rpool_strdup_x(e->e_rpool, fname); + h->h_value = sm_rpool_strdup_x(e->e_rpool, fvalue); h->h_link = NULL; memmove((char *) h->h_mflags, (char *) mopts, sizeof mopts); h->h_macro = mid; @@ -444,7 +466,7 @@ hse: ** field -- the name of the header field. ** value -- the value of the field. ** flags -- flags to add to h_flags. -** hdrlist -- an indirect pointer to the header structure list. +** e -- envelope. ** ** Returns: ** none. @@ -454,15 +476,16 @@ hse: */ void -addheader(field, value, flags, hdrlist) +addheader(field, value, flags, e) char *field; char *value; int flags; - HDR **hdrlist; + ENVELOPE *e; { register HDR *h; STAB *s; HDR **hp; + HDR **hdrlist = &e->e_header; /* find info struct */ s = stab(field, ST_HEADER, ST_FIND); @@ -470,14 +493,14 @@ addheader(field, value, flags, hdrlist) /* find current place in list -- keep back pointer? */ for (hp = hdrlist; (h = *hp) != NULL; hp = &h->h_link) { - if (strcasecmp(field, h->h_field) == 0) + if (sm_strcasecmp(field, h->h_field) == 0) break; } /* allocate space for new header */ - h = (HDR *) xalloc(sizeof *h); + h = (HDR *) sm_rpool_malloc_x(e->e_rpool, sizeof *h); h->h_field = field; - h->h_value = newstr(value); + h->h_value = sm_rpool_strdup_x(e->e_rpool, value); h->h_link = *hp; h->h_flags = flags; if (s != NULL) @@ -514,7 +537,7 @@ hvalue(field, header) for (h = header; h != NULL; h = h->h_link) { if (!bitset(H_DEFAULT, h->h_flags) && - strcasecmp(h->h_field, field) == 0) + sm_strcasecmp(h->h_field, field) == 0) return h->h_value; } return NULL; @@ -535,8 +558,8 @@ hvalue(field, header) ** h -- string to check for possible headerness. ** ** Returns: -** TRUE if h is a header. -** FALSE otherwise. +** true if h is a header. +** false otherwise. ** ** Side Effects: ** none. @@ -549,13 +572,13 @@ isheader(h) register char *s = h; if (s[0] == '-' && s[1] == '-') - return FALSE; + return false; while (*s > ' ' && *s != ':' && *s != '\0') s++; if (h == s) - return FALSE; + return false; /* following technically violates RFC822 */ while (isascii(*s) && isspace(*s)) @@ -572,6 +595,7 @@ isheader(h) ** message priority). This should not be set ** when reading a queue file because some info ** needed to compute the priority is wrong. +** log -- call logsender()? ** ** Returns: ** none. @@ -579,30 +603,29 @@ isheader(h) ** Side Effects: ** Sets a bunch of global variables from information ** in the collected header. -** Aborts the message if the hop count is exceeded. */ void -eatheader(e, full) +eatheader(e, full, log) register ENVELOPE *e; bool full; + bool log; { register HDR *h; register char *p; int hopcnt = 0; - char *msgid; char buf[MAXLINE]; /* ** Set up macros for possible expansion in headers. */ - define('f', e->e_sender, e); - define('g', e->e_sender, e); + macdefine(&e->e_macro, A_PERM, 'f', e->e_sender); + macdefine(&e->e_macro, A_PERM, 'g', e->e_sender); if (e->e_origrcpt != NULL && *e->e_origrcpt != '\0') - define('u', e->e_origrcpt, e); + macdefine(&e->e_macro, A_PERM, 'u', e->e_origrcpt); else - define('u', NULL, e); + macdefine(&e->e_macro, A_PERM, 'u', NULL); /* full name of from person */ p = hvalue("full-name", e->e_header); @@ -615,22 +638,23 @@ eatheader(e, full) ** as a comment so crackaddr() doesn't destroy ** the name portion of the address. */ - p = addquotes(p); + + p = addquotes(p, e->e_rpool); } - define('x', p, e); + macdefine(&e->e_macro, A_PERM, 'x', p); } if (tTd(32, 1)) - dprintf("----- collected header -----\n"); - msgid = NULL; + sm_dprintf("----- collected header -----\n"); + e->e_msgid = NULL; for (h = e->e_header; h != NULL; h = h->h_link) { if (tTd(32, 1)) - dprintf("%s: ", h->h_field); + sm_dprintf("%s: ", h->h_field); if (h->h_value == NULL) { if (tTd(32, 1)) - dprintf("<NULL>\n"); + sm_dprintf("<NULL>\n"); continue; } @@ -640,24 +664,24 @@ eatheader(e, full) { if (tTd(32, 1)) { - dprintf("("); + sm_dprintf("("); xputs(h->h_value); - dprintf(") "); + sm_dprintf(") "); } expand(h->h_value, buf, sizeof buf, e); if (buf[0] != '\0') { if (bitset(H_FROM, h->h_flags)) - expand(crackaddr(buf), buf, sizeof buf, e); - h->h_value = newstr(buf); + expand(crackaddr(buf), buf, sizeof buf, + e); + h->h_value = sm_rpool_strdup_x(e->e_rpool, buf); h->h_flags &= ~H_DEFAULT; } } - if (tTd(32, 1)) { xputs(h->h_value); - dprintf("\n"); + sm_dprintf("\n"); } /* count the number of times it has been processed */ @@ -667,7 +691,8 @@ eatheader(e, full) /* send to this person if we so desire */ if (GrabTo && bitset(H_RCPT, h->h_flags) && !bitset(H_DEFAULT, h->h_flags) && - (!bitset(EF_RESENT, e->e_flags) || bitset(H_RESENT, h->h_flags))) + (!bitset(EF_RESENT, e->e_flags) || + bitset(H_RESENT, h->h_flags))) { #if 0 int saveflags = e->e_flags; @@ -692,15 +717,15 @@ eatheader(e, full) p = "resent-message-id"; if (!bitset(EF_RESENT, e->e_flags)) p += 7; - if (strcasecmp(h->h_field, p) == 0) + if (sm_strcasecmp(h->h_field, p) == 0) { - msgid = h->h_value; - while (isascii(*msgid) && isspace(*msgid)) - msgid++; + e->e_msgid = h->h_value; + while (isascii(*e->e_msgid) && isspace(*e->e_msgid)) + e->e_msgid++; } } if (tTd(32, 1)) - dprintf("----------------------------\n"); + sm_dprintf("----------------------------\n"); /* if we are just verifying (that is, sendmail -t -bv), drop out now */ if (OpMode == MD_VERIFY) @@ -708,7 +733,11 @@ eatheader(e, full) /* store hop count */ if (hopcnt > e->e_hopcount) + { e->e_hopcount = hopcnt; + (void) sm_snprintf(buf, sizeof buf, "%d", e->e_hopcount); + macdefine(&e->e_macro, A_TEMP, 'c', buf); + } /* message priority */ p = hvalue("precedence", e->e_header); @@ -730,11 +759,11 @@ eatheader(e, full) if (p != NULL) { /* (this should be in the configuration file) */ - if (strcasecmp(p, "urgent") == 0) + if (sm_strcasecmp(p, "urgent") == 0) e->e_timeoutclass = TOC_URGENT; - else if (strcasecmp(p, "normal") == 0) + else if (sm_strcasecmp(p, "normal") == 0) e->e_timeoutclass = TOC_NORMAL; - else if (strcasecmp(p, "non-urgent") == 0) + else if (sm_strcasecmp(p, "non-urgent") == 0) e->e_timeoutclass = TOC_NONURGENT; } @@ -743,11 +772,11 @@ eatheader(e, full) if (p == NULL) p = hvalue("date", e->e_header); if (p != NULL) - define('a', p, e); + macdefine(&e->e_macro, A_PERM, 'a', p); /* check to see if this is a MIME message */ if ((e->e_bodytype != NULL && - strcasecmp(e->e_bodytype, "8BITMIME") == 0) || + sm_strcasecmp(e->e_bodytype, "8BITMIME") == 0) || hvalue("MIME-Version", e->e_header) != NULL) { e->e_flags |= EF_IS_MIME; @@ -785,9 +814,9 @@ eatheader(e, full) if (hi->hi_field != NULL) { if (tTd(32, 2)) - dprintf("eatheader: setsender(*%s == %s)\n", + sm_dprintf("eatheader: setsender(*%s == %s)\n", hi->hi_field, p); - setsender(p, e, NULL, '\0', TRUE); + setsender(p, e, NULL, '\0', true); } } @@ -795,9 +824,11 @@ eatheader(e, full) ** Log collection information. */ - if (bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4) - logsender(e, msgid); - e->e_flags &= ~EF_LOGSENDER; + if (log && bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4) + { + logsender(e, e->e_msgid); + e->e_flags &= ~EF_LOGSENDER; + } } /* ** LOGSENDER -- log sender information @@ -824,6 +855,7 @@ logsender(e, msgid) char mbuf[MAXNAME + 1]; /* don't allow newlines in the message-id */ + /* XXX do we still need this? sm_syslog() replaces control chars */ if (msgid != NULL) { l = strlen(msgid); @@ -848,70 +880,59 @@ logsender(e, msgid) else { name = hbuf; - (void) snprintf(hbuf, sizeof hbuf, "%.80s", RealHostName); + (void) sm_snprintf(hbuf, sizeof hbuf, "%.80s", RealHostName); if (RealHostAddr.sa.sa_family != 0) { p = &hbuf[strlen(hbuf)]; - (void) snprintf(p, SPACELEFT(hbuf, p), " (%.100s)", - anynet_ntoa(&RealHostAddr)); + (void) sm_snprintf(p, SPACELEFT(hbuf, p), + " (%.100s)", + anynet_ntoa(&RealHostAddr)); } } /* some versions of syslog only take 5 printf args */ #if (SYSLOG_BUFSIZE) >= 256 sbp = sbuf; - snprintf(sbp, SPACELEFT(sbuf, sbp), - "from=%.200s, size=%ld, class=%d, nrcpts=%d", - e->e_from.q_paddr == NULL ? "<NONE>" : e->e_from.q_paddr, - e->e_msgsize, e->e_class, e->e_nrcpts); + (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), + "from=%.200s, size=%ld, class=%d, nrcpts=%d", + e->e_from.q_paddr == NULL ? "<NONE>" : e->e_from.q_paddr, + e->e_msgsize, e->e_class, e->e_nrcpts); sbp += strlen(sbp); if (msgid != NULL) { - snprintf(sbp, SPACELEFT(sbuf, sbp), ", msgid=%.100s", mbuf); + (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), + ", msgid=%.100s", mbuf); sbp += strlen(sbp); } if (e->e_bodytype != NULL) { - (void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", bodytype=%.20s", - e->e_bodytype); + (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), + ", bodytype=%.20s", e->e_bodytype); sbp += strlen(sbp); } p = macvalue('r', e); if (p != NULL) { - (void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", proto=%.20s", p); - sbp += strlen(sbp); - } - p = macvalue(macid("{daemon_name}", NULL), e); - if (p != NULL) - { - (void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", daemon=%.20s", p); - sbp += strlen(sbp); - } -# if SASL - p = macvalue(macid("{auth_type}", NULL), e); - if (p != NULL) - { - (void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", mech=%.12s", p); + (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), + ", proto=%.20s", p); sbp += strlen(sbp); } - p = macvalue(macid("{auth_author}", NULL), e); + p = macvalue(macid("{daemon_name}"), e); if (p != NULL) { - (void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", auth=%.30s", p); + (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), + ", daemon=%.20s", p); sbp += strlen(sbp); } -# endif /* SASL */ - sm_syslog(LOG_INFO, e->e_id, - "%.850s, relay=%.100s", - sbuf, name); + sm_syslog(LOG_INFO, e->e_id, "%.850s, relay=%.100s", sbuf, name); #else /* (SYSLOG_BUFSIZE) >= 256 */ sm_syslog(LOG_INFO, e->e_id, "from=%s", e->e_from.q_paddr == NULL ? "<NONE>" - : shortenstring(e->e_from.q_paddr, 83)); + : shortenstring(e->e_from.q_paddr, + 83)); sm_syslog(LOG_INFO, e->e_id, "size=%ld, class=%ld, nrcpts=%d", e->e_msgsize, e->e_class, e->e_nrcpts); @@ -923,13 +944,15 @@ logsender(e, msgid) *sbp = '\0'; if (e->e_bodytype != NULL) { - snprintf(sbp, SPACELEFT(sbuf, sbp), "bodytype=%.20s, ", e->e_bodytype); + (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), + "bodytype=%.20s, ", e->e_bodytype); sbp += strlen(sbp); } p = macvalue('r', e); if (p != NULL) { - snprintf(sbp, SPACELEFT(sbuf, sbp), "proto=%.20s, ", p); + (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), + "proto=%.20s, ", p); sbp += strlen(sbp); } sm_syslog(LOG_INFO, e->e_id, @@ -957,7 +980,7 @@ priencode(p) for (i = 0; i < NumPriorities; i++) { - if (strcasecmp(p, Priorities[i].pri_name) == 0) + if (sm_strcasecmp(p, Priorities[i].pri_name) == 0) return Priorities[i].pri_val; } @@ -1006,10 +1029,10 @@ crackaddr(addr) bool qmode; bool realqmode; bool skipping; - bool putgmac = FALSE; - bool quoteit = FALSE; - bool gotangle = FALSE; - bool gotcolon = FALSE; + bool putgmac = false; + bool quoteit = false; + bool gotangle = false; + bool gotcolon = false; register char *bp; char *buflim; char *bufhead; @@ -1017,7 +1040,7 @@ crackaddr(addr) static char buf[MAXNAME + 1]; if (tTd(33, 1)) - dprintf("crackaddr(%s)\n", addr); + sm_dprintf("crackaddr(%s)\n", addr); /* strip leading spaces */ while (*addr != '\0' && isascii(*addr) && isspace(*addr)) @@ -1033,7 +1056,7 @@ crackaddr(addr) p = addrhead = addr; copylev = anglelev = realanglelev = cmtlev = realcmtlev = 0; bracklev = 0; - qmode = realqmode = FALSE; + qmode = realqmode = false; while ((c = *p++) != '\0') { @@ -1053,7 +1076,7 @@ crackaddr(addr) { /* arrange to quote the address */ if (cmtlev <= 0 && !qmode) - quoteit = TRUE; + quoteit = true; if ((c = *p++) == '\0') { @@ -1138,7 +1161,7 @@ crackaddr(addr) if (*p == ':' || *p == '.') { if (cmtlev <= 0 && !qmode) - quoteit = TRUE; + quoteit = true; if (copylev > 0 && !skipping) { *bp++ = c; @@ -1148,7 +1171,7 @@ crackaddr(addr) goto putg; } - gotcolon = TRUE; + gotcolon = true; bp = bufhead; if (quoteit) @@ -1189,7 +1212,7 @@ crackaddr(addr) while (isascii(*p) && isspace(*p) && bp < buflim) *bp++ = *p++; copylev = 0; - putgmac = quoteit = FALSE; + putgmac = quoteit = false; bufhead = bp; addrhead = p; continue; @@ -1212,7 +1235,7 @@ crackaddr(addr) */ if (cmtlev <= 0 && !qmode) - quoteit = TRUE; + quoteit = true; } /* check for angle brackets */ @@ -1222,8 +1245,8 @@ crackaddr(addr) /* assume first of two angles is bogus */ if (gotangle) - quoteit = TRUE; - gotangle = TRUE; + quoteit = true; + gotangle = true; /* oops -- have to change our mind */ anglelev = 1; @@ -1265,7 +1288,7 @@ crackaddr(addr) *bp++ = c; } copylev = 0; - putgmac = quoteit = FALSE; + putgmac = quoteit = false; continue; } @@ -1285,7 +1308,7 @@ crackaddr(addr) /* syntax error: unmatched > */ if (copylev > 0) bp--; - quoteit = TRUE; + quoteit = true; continue; } if (copylev++ <= 0) @@ -1301,7 +1324,7 @@ crackaddr(addr) *bp++ = ' '; *bp++ = MACROEXPAND; *bp++ = 'g'; - putgmac = TRUE; + putgmac = true; } } @@ -1316,9 +1339,9 @@ crackaddr(addr) if (tTd(33, 1)) { - dprintf("crackaddr=>`"); + sm_dprintf("crackaddr=>`"); xputs(buf); - dprintf("'\n"); + sm_dprintf("'\n"); } return buf; @@ -1339,13 +1362,6 @@ crackaddr(addr) ** none. */ -/* - * Macro for fast max (not available in e.g. DG/UX, 386/ix). - */ -#ifndef MAX -# define MAX(a,b) (((a)>(b))?(a):(b)) -#endif /* ! MAX */ - void putheader(mci, hdr, e, flags) register MCI *mci; @@ -1354,11 +1370,11 @@ putheader(mci, hdr, e, flags) int flags; { register HDR *h; - char buf[MAX(MAXLINE,BUFSIZ)]; + char buf[SM_MAX(MAXLINE,BUFSIZ)]; char obuf[MAXLINE]; if (tTd(34, 1)) - dprintf("--- putheader, mailer = %s ---\n", + sm_dprintf("--- putheader, mailer = %s ---\n", mci->mci_mailer->m_name); /* @@ -1373,10 +1389,11 @@ putheader(mci, hdr, e, flags) for (h = hdr; h != NULL; h = h->h_link) { register char *p = h->h_value; + char *q; if (tTd(34, 11)) { - dprintf(" %s: ", h->h_field); + sm_dprintf(" %s: ", h->h_field); xputs(p); } @@ -1387,7 +1404,7 @@ putheader(mci, hdr, e, flags) /* heuristic shortening of MIME fields to avoid MUA overflows */ if (MaxMimeFieldLength > 0 && wordinclass(h->h_field, - macid("{checkMIMEFieldHeaders}", NULL))) + macid("{checkMIMEFieldHeaders}"))) { size_t len; @@ -1398,15 +1415,15 @@ putheader(mci, hdr, e, flags) "Truncated MIME %s header due to field size (length = %ld) (possible attack)", h->h_field, (unsigned long) len); if (tTd(34, 11)) - dprintf(" truncated MIME %s header due to field size (length = %ld) (possible attack)\n", - h->h_field, - (unsigned long) len); + sm_dprintf(" truncated MIME %s header due to field size (length = %ld) (possible attack)\n", + h->h_field, + (unsigned long) len); } } if (MaxMimeHeaderLength > 0 && wordinclass(h->h_field, - macid("{checkMIMETextHeaders}", NULL))) + macid("{checkMIMETextHeaders}"))) { size_t len; @@ -1418,15 +1435,15 @@ putheader(mci, hdr, e, flags) "Truncated long MIME %s header (length = %ld) (possible attack)", h->h_field, (unsigned long) len); if (tTd(34, 11)) - dprintf(" truncated long MIME %s header (length = %ld) (possible attack)\n", - h->h_field, - (unsigned long) len); + sm_dprintf(" truncated long MIME %s header (length = %ld) (possible attack)\n", + h->h_field, + (unsigned long) len); } } if (MaxMimeHeaderLength > 0 && wordinclass(h->h_field, - macid("{checkMIMEHeaders}", NULL))) + macid("{checkMIMEHeaders}"))) { size_t len; @@ -1438,9 +1455,9 @@ putheader(mci, hdr, e, flags) "Truncated long MIME %s header (length = %ld) (possible attack)", h->h_field, (unsigned long) len); if (tTd(34, 11)) - dprintf(" truncated long MIME %s header (length = %ld) (possible attack)\n", - h->h_field, - (unsigned long) len); + sm_dprintf(" truncated long MIME %s header (length = %ld) (possible attack)\n", + h->h_field, + (unsigned long) len); } } @@ -1450,20 +1467,21 @@ putheader(mci, hdr, e, flags) ** MIME. If converting, add a new CTE header in ** mime8to7(). */ + if (bitset(H_CTE, h->h_flags) && bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, mci->mci_flags) && !bitset(M87F_NO8TO7, flags)) { if (tTd(34, 11)) - dprintf(" (skipped (content-transfer-encoding))\n"); + sm_dprintf(" (skipped (content-transfer-encoding))\n"); continue; } if (bitset(MCIF_INMIME, mci->mci_flags)) { if (tTd(34, 11)) - dprintf("\n"); + sm_dprintf("\n"); put_vanilla_header(h, p, mci); continue; } @@ -1471,10 +1489,11 @@ putheader(mci, hdr, e, flags) if (bitset(H_CHECK|H_ACHECK, h->h_flags) && !bitintersect(h->h_mflags, mci->mci_mailer->m_flags) && (h->h_macro == '\0' || - macvalue(bitidx(h->h_macro), e) == NULL)) + (q = macvalue(bitidx(h->h_macro), e)) == NULL || + *q == '\0')) { if (tTd(34, 11)) - dprintf(" (skipped)\n"); + sm_dprintf(" (skipped)\n"); continue; } @@ -1482,7 +1501,7 @@ putheader(mci, hdr, e, flags) if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) { if (tTd(34, 11)) - dprintf(" (skipped (resent))\n"); + sm_dprintf(" (skipped (resent))\n"); continue; } @@ -1491,7 +1510,7 @@ putheader(mci, hdr, e, flags) (RrtImpliesDsn || bitset(EF_NORECEIPT, e->e_flags))) { if (tTd(34, 11)) - dprintf(" (skipped (receipt))\n"); + sm_dprintf(" (skipped (receipt))\n"); continue; } @@ -1504,7 +1523,7 @@ putheader(mci, hdr, e, flags) if (*p == '\0') { if (tTd(34, 11)) - dprintf(" (skipped -- null value)\n"); + sm_dprintf(" (skipped -- null value)\n"); continue; } } @@ -1515,20 +1534,20 @@ putheader(mci, hdr, e, flags) if (bitset(EF_DELETE_BCC, e->e_flags)) { if (tTd(34, 11)) - dprintf(" (skipped -- bcc)\n"); + sm_dprintf(" (skipped -- bcc)\n"); } else { /* no other recipient headers: truncate value */ - (void) snprintf(obuf, sizeof obuf, "%s:", - h->h_field); + (void) sm_strlcpyn(obuf, sizeof obuf, 2, + h->h_field, ":"); putline(obuf, mci); } continue; } if (tTd(34, 11)) - dprintf("\n"); + sm_dprintf("\n"); if (bitset(H_FROM|H_RCPT, h->h_flags)) { @@ -1536,7 +1555,7 @@ putheader(mci, hdr, e, flags) bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); if (bitset(H_FROM, h->h_flags)) - oldstyle = FALSE; + oldstyle = false; commaize(h, p, oldstyle, mci, e); } else @@ -1561,9 +1580,9 @@ putheader(mci, hdr, e, flags) putline("MIME-Version: 1.0", mci); if (hvalue("Content-Type", e->e_header) == NULL) { - snprintf(obuf, sizeof obuf, - "Content-Type: text/plain; charset=%s", - defcharset(e)); + (void) sm_snprintf(obuf, sizeof obuf, + "Content-Type: text/plain; charset=%s", + defcharset(e)); putline(obuf, mci); } if (hvalue("Content-Transfer-Encoding", e->e_header) == NULL) @@ -1597,25 +1616,25 @@ put_vanilla_header(h, v, mci) putflags = PXLF_HEADER; if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags)) putflags |= PXLF_STRIP8BIT; - (void) snprintf(obuf, sizeof obuf, "%.200s: ", h->h_field); + (void) sm_snprintf(obuf, sizeof obuf, "%.200s: ", h->h_field); obp = obuf + strlen(obuf); while ((nlp = strchr(v, '\n')) != NULL) { int l; l = nlp - v; - if (SPACELEFT(obuf, obp) - 1 < (size_t)l) + if (SPACELEFT(obuf, obp) - 1 < (size_t) l) l = SPACELEFT(obuf, obp) - 1; - snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v); + (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v); putxline(obuf, strlen(obuf), mci, putflags); v += l + 1; obp = obuf; if (*v != ' ' && *v != '\t') *obp++ = ' '; } - snprintf(obp, SPACELEFT(obuf, obp), "%.*s", - (int) sizeof obuf - (obp - obuf) - 1, v); + (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s", + (int) (SPACELEFT(obuf, obp) - 1), v); putxline(obuf, strlen(obuf), mci, putflags); } /* @@ -1624,7 +1643,7 @@ put_vanilla_header(h, v, mci) ** Parameters: ** h -- the header field to output. ** p -- the value to put in it. -** oldstyle -- TRUE if this is an old style header. +** oldstyle -- true if this is an old style header. ** mci -- the connection information. ** e -- the envelope containing the message. ** @@ -1646,7 +1665,7 @@ commaize(h, p, oldstyle, mci, e) register char *obp; int opos; int omax; - bool firstone = TRUE; + bool firstone = true; int putflags = PXLF_HEADER; char obuf[MAXLINE + 3]; @@ -1656,13 +1675,14 @@ commaize(h, p, oldstyle, mci, e) */ if (tTd(14, 2)) - dprintf("commaize(%s: %s)\n", h->h_field, p); + sm_dprintf("commaize(%s: %s)\n", h->h_field, p); if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags)) putflags |= PXLF_STRIP8BIT; obp = obuf; - (void) snprintf(obp, SPACELEFT(obuf, obp), "%.200s: ", h->h_field); + (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.200s: ", + h->h_field); opos = strlen(h->h_field) + 2; if (opos > 202) opos = 202; @@ -1713,7 +1733,7 @@ commaize(h, p, oldstyle, mci, e) p = oldp; break; } - p += *p == '@' ? 1 : 2; + ++p; while (*p != '\0' && isascii(*p) && isspace(*p)) p++; } @@ -1738,7 +1758,7 @@ commaize(h, p, oldstyle, mci, e) { char *q; - q = udbsender(name); + q = udbsender(name, e->e_rpool); if (q != NULL) name = q; } @@ -1750,14 +1770,14 @@ commaize(h, p, oldstyle, mci, e) *p = savechar; continue; } - name = denlstring(name, FALSE, TRUE); + name = denlstring(name, false, true); /* ** record data progress so DNS timeouts ** don't cause DATA timeouts */ - DataProgress = TRUE; + DataProgress = true; /* output the name with nice formatting */ opos += strlen(name); @@ -1765,23 +1785,23 @@ commaize(h, p, oldstyle, mci, e) opos += 2; if (opos > omax && !firstone) { - snprintf(obp, SPACELEFT(obuf, obp), ",\n"); + (void) sm_strlcpy(obp, ",\n", SPACELEFT(obuf, obp)); putxline(obuf, strlen(obuf), mci, putflags); obp = obuf; - (void) strlcpy(obp, " ", sizeof obp); + (void) sm_strlcpy(obp, " ", sizeof obp); opos = strlen(obp); obp += opos; opos += strlen(name); } else if (!firstone) { - snprintf(obp, SPACELEFT(obuf, obp), ", "); + (void) sm_strlcpy(obp, ", ", SPACELEFT(obuf, obp)); obp += 2; } while ((c = *name++) != '\0' && obp < &obuf[MAXLINE]) *obp++ = c; - firstone = FALSE; + firstone = false; *p = savechar; } *obp = '\0'; @@ -1794,6 +1814,7 @@ commaize(h, p, oldstyle, mci, e) ** ** Parameters: ** header -- list of header structures to copy. +** rpool -- resource pool, or NULL ** ** Returns: ** a copy of 'header'. @@ -1803,8 +1824,9 @@ commaize(h, p, oldstyle, mci, e) */ HDR * -copyheader(header) +copyheader(header, rpool) register HDR *header; + SM_RPOOL_T *rpool; { register HDR *newhdr; HDR *ret; @@ -1812,7 +1834,7 @@ copyheader(header) while (header != NULL) { - newhdr = (HDR *) xalloc(sizeof *newhdr); + newhdr = (HDR *) sm_rpool_malloc_x(rpool, sizeof *newhdr); STRUCTCOPY(*header, *newhdr); *tail = newhdr; tail = &newhdr->h_link; diff --git a/gnu/usr.sbin/sendmail/sendmail/helpfile b/gnu/usr.sbin/sendmail/sendmail/helpfile index eb9dc992ab6..1de9a8d53b5 100644 --- a/gnu/usr.sbin/sendmail/sendmail/helpfile +++ b/gnu/usr.sbin/sendmail/sendmail/helpfile @@ -11,7 +11,7 @@ cpyr By using this file, you agree to the terms and conditions set cpyr forth in the LICENSE file which can be found at the top level of cpyr the sendmail distribution. cpyr -cpyr $$Sendmail: helpfile,v 8.31.16.4 2000/09/17 14:21:00 ca Exp $$ +cpyr $$Sendmail: helpfile,v 8.38 2000/10/15 17:18:44 ca Exp $$ cpyr smtp This is sendmail version $v smtp Topics: @@ -39,7 +39,6 @@ ehlo TURN Turn the operation around [RFC821] ehlo 8BITMIME Use 8-bit data [RFC1652] ehlo SIZE Message size declaration [RFC1870] ehlo VERB Verbose [Allman] -ehlo ONEX One message transaction only [Allman] ehlo CHUNKING Chunking [RFC1830] ehlo BINARYMIME Binary MIME [RFC1830] ehlo PIPELINING Command Pipelining [RFC1854] @@ -47,8 +46,8 @@ ehlo DSN Delivery Status Notification [RFC1891] ehlo ETRN Remote Message Queue Starting [RFC1985] ehlo STARTTLS Secure SMTP [RFC2487] ehlo AUTH Authentication [RFC2554] -ehlo XUSR Initial (user) submission [Allman] ehlo ENHANCEDSTATUSCODES Enhanced status codes [RFC2034] +ehlo DELIVERBY Deliver By [RFC2852] mail MAIL FROM: <sender> [ <parameters> ] mail Specifies the sender. Parameters are ESMTP extensions. mail See "HELP DSN" for details. @@ -134,3 +133,4 @@ control help This message. control restart Restart sendmail. control shutdown Shutdown sendmail. control status Show sendmail status. +control memdump Dump allocated memory list. diff --git a/gnu/usr.sbin/sendmail/sendmail/macro.c b/gnu/usr.sbin/sendmail/sendmail/macro.c index 16709ccd2a1..ce96274b4a3 100644 --- a/gnu/usr.sbin/sendmail/sendmail/macro.c +++ b/gnu/usr.sbin/sendmail/sendmail/macro.c @@ -11,20 +11,90 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: macro.c,v 8.40.16.9 2001/02/22 01:16:55 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +SM_RCSID("@(#)$Sendmail: macro.c,v 8.83 2001/09/04 22:43:03 ca Exp $") + #if MAXMACROID != (BITMAPBITS - 1) ERROR Read the comment in conf.h #endif /* MAXMACROID != (BITMAPBITS - 1) */ -char *MacroName[MAXMACROID + 1]; /* macro id to name table */ -int NextMacroId = 0240; /* codes for long named macros */ +static char *MacroName[MAXMACROID + 1]; /* macro id to name table */ +int NextMacroId = 0240; /* codes for long named macros */ /* +** INITMACROS -- initialize the macro system +** +** This just involves defining some macros that are actually +** used internally as metasymbols to be themselves. +** +** Parameters: +** none. +** +** Returns: +** none. +** +** Side Effects: +** initializes several macros to be themselves. +*/ + +struct metamac MetaMacros[] = +{ + /* LHS pattern matching characters */ + { '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE }, + { '=', MATCHCLASS }, { '~', MATCHNCLASS }, + + /* these are RHS metasymbols */ + { '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER }, + { '>', CALLSUBR }, + + /* the conditional operations */ + { '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI }, + + /* the hostname lookup characters */ + { '[', HOSTBEGIN }, { ']', HOSTEND }, + { '(', LOOKUPBEGIN }, { ')', LOOKUPEND }, + + /* miscellaneous control characters */ + { '&', MACRODEXPAND }, + + { '\0', '\0' } +}; + +#define MACBINDING(name, mid) \ + stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \ + MacroName[mid] = name; + +void +initmacros(e) + register ENVELOPE *e; +{ + register struct metamac *m; + register int c; + char buf[5]; + + for (m = MetaMacros; m->metaname != '\0'; m++) + { + buf[0] = m->metaval; + buf[1] = '\0'; + macdefine(&e->e_macro, A_TEMP, m->metaname, buf); + } + buf[0] = MATCHREPL; + buf[2] = '\0'; + for (c = '0'; c <= '9'; c++) + { + buf[1] = c; + macdefine(&e->e_macro, A_TEMP, c, buf); + } + + /* set defaults for some macros sendmail will use later */ + macdefine(&e->e_macro, A_PERM, 'n', "MAILER-DAEMON"); + + /* set up external names for some internal macros */ + MACBINDING("opMode", MID_OPMODE); + /*XXX should probably add equivalents for all short macros here XXX*/ +} +/* ** EXPAND -- macro expand a string using $x escapes. ** ** Parameters: @@ -50,8 +120,8 @@ expand(s, buf, bufsize, e) register char *xp; register char *q; bool skipping; /* set if conditionally skipping output */ - bool recurse = FALSE; /* set if recursion required */ - int i; + bool recurse; /* set if recursion required */ + size_t i; int skiplev; /* skipping nesting level */ int iflev; /* if nesting level */ char xbuf[MACBUFSIZE]; @@ -59,12 +129,13 @@ expand(s, buf, bufsize, e) if (tTd(35, 24)) { - dprintf("expand("); + sm_dprintf("expand("); xputs(s); - dprintf(")\n"); + sm_dprintf(")\n"); } - skipping = FALSE; + recurse = false; + skipping = false; skiplev = 0; iflev = 0; if (s == NULL) @@ -98,17 +169,17 @@ expand(s, buf, bufsize, e) case CONDELSE: /* change state of skipping */ if (iflev == 0) - break; + break; /* XXX: error */ if (skiplev == 0) skipping = !skipping; continue; case CONDFI: /* stop skipping */ if (iflev == 0) - break; + break; /* XXX: error */ iflev--; if (skiplev == 0) - skipping = FALSE; + skipping = false; if (skipping) skiplev--; continue; @@ -142,7 +213,7 @@ expand(s, buf, bufsize, e) { /* check for any sendmail metacharacters */ if ((c & 0340) == 0200) - recurse = TRUE; + recurse = true; *xp++ = c; } } @@ -151,9 +222,9 @@ expand(s, buf, bufsize, e) if (tTd(35, 24)) { - dprintf("expand ==> "); + sm_dprintf("expand ==> "); xputs(xbuf); - dprintf("\n"); + sm_dprintf("\n"); } /* recurse as appropriate */ @@ -172,109 +243,155 @@ expand(s, buf, bufsize, e) /* copy results out */ i = xp - xbuf; - if ((size_t)i >= bufsize) + if (i >= bufsize) i = bufsize - 1; memmove(buf, xbuf, i); buf[i] = '\0'; } -/* -** DEFINE -- define a macro. + +/* +** MACDEFINE -- bind a macro name to a value ** -** this would be better done using a #define macro. +** Set a macro to a value, with fancy storage management. +** macdefine will make a copy of the value, if required, +** and will ensure that the storage for the previous value +** is not leaked. ** ** Parameters: -** n -- the macro name. -** v -- the macro value. -** e -- the envelope to store the definition in. -** -** Returns: -** none. -** -** Side Effects: -** e->e_macro[n] is defined. -** -** Notes: -** There is one macro for each ASCII character, -** although they are not all used. The currently -** defined macros are: -** -** $a date in ARPANET format (preferring the Date: line -** of the message) -** $b the current date (as opposed to the date as found -** the message) in ARPANET format -** $c hop count -** $d (current) date in UNIX (ctime) format -** $e the SMTP entry message+ -** $f raw from address -** $g translated from address -** $h to host -** $i queue id -** $j official SMTP hostname, used in messages+ -** $k UUCP node name -** $l UNIX-style from line+ -** $m The domain part of our full name. -** $n name of sendmail ("MAILER-DAEMON" on local -** net typically)+ -** $o delimiters ("operators") for address tokens+ -** (set via OperatorChars option in V6 or later -** sendmail.cf files) -** $p my process id in decimal -** $q the string that becomes an address -- this is -** normally used to combine $g & $x. -** $r protocol used to talk to sender -** $s sender's host name -** $t the current time in seconds since 1/1/1970 -** $u to user -** $v version number of sendmail -** $w our host name (if it can be determined) -** $x signature (full name) of from person -** $y the tty id of our terminal -** $z home directory of to person -** $_ RFC1413 authenticated sender address -** -** Macros marked with + must be defined in the -** configuration file and are used internally, but -** are not set. -** -** There are also some macros that can be used -** arbitrarily to make the configuration file -** cleaner. In general all upper-case letters -** are available. +** mac -- Macro table. +** vclass -- storage class of 'value', ignored if value==NULL. +** A_HEAP means that the value was allocated by +** malloc, and that macdefine owns the storage. +** A_TEMP means that value points to temporary storage, +** and thus macdefine needs to make a copy. +** A_PERM means that value points to storage that +** will remain allocated and unchanged for +** at least the lifetime of mac. Use A_PERM if: +** -- value == NULL, +** -- value points to a string literal, +** -- value was allocated from mac->mac_rpool +** or (in the case of an envelope macro) +** from e->e_rpool, +** -- in the case of an envelope macro, +** value is a string member of the envelope +** such as e->e_sender. +** id -- Macro id. This is a single character macro name +** such as 'g', or a value returned by macid(). +** value -- Macro value: either NULL, or a string. */ void -define(n, v, e) - int n; - char *v; - register ENVELOPE *e; +#if SM_HEAP_CHECK +macdefine_tagged(mac, vclass, id, value, file, line, grp) +#else /* SM_HEAP_CHECK */ +macdefine(mac, vclass, id, value) +#endif /* SM_HEAP_CHECK */ + MACROS_T *mac; + ARGCLASS_T vclass; + int id; + char *value; +#if SM_HEAP_CHECK + char *file; + int line; + int grp; +#endif /* SM_HEAP_CHECK */ { - int m; + char *newvalue; + + if (id < 0 || id > MAXMACROID) + return; - m = bitidx(n); if (tTd(35, 9)) { - dprintf("%sdefine(%s as ", - (e->e_macro[m] == NULL) ? "" - : "re", macname(n)); - xputs(v); - dprintf(")\n"); + sm_dprintf("%sdefine(%s as ", + mac->mac_table[id] == NULL ? "" : "re", macname(id)); + xputs(value); + sm_dprintf(")\n"); + } + + if (mac->mac_rpool == NULL) + { + char *freeit = NULL; + + if (mac->mac_table[id] != NULL && + bitnset(id, mac->mac_allocated)) + freeit = mac->mac_table[id]; + + if (value == NULL || vclass == A_HEAP) + { + sm_heap_checkptr_tagged(value, file, line); + newvalue = value; + clrbitn(id, mac->mac_allocated); + } + else + { + newvalue = sm_strdup_tagged_x(value, file, line, 0); + setbitn(id, mac->mac_allocated); + } + mac->mac_table[id] = newvalue; + if (freeit != NULL) + sm_free(freeit); + } + else + { + if (value == NULL || vclass == A_PERM) + newvalue = value; + else + newvalue = sm_rpool_strdup_x(mac->mac_rpool, value); + mac->mac_table[id] = newvalue; + if (vclass == A_HEAP) + sm_free(value); } - e->e_macro[m] = v; #if _FFR_RESET_MACRO_GLOBALS - switch (m) + switch (id) { case 'j': - MyHostName = v; + PSTRSET(MyHostName, value); break; } #endif /* _FFR_RESET_MACRO_GLOBALS */ } + +/* +** MACSET -- set a named macro to a value (low level) +** +** No fancy storage management; the caller takes full responsibility. +** Often used with macget; see also macdefine. +** +** Parameters: +** mac -- Macro table. +** i -- Macro name, specified as an integer offset. +** value -- Macro value: either NULL, or a string. +*/ + +void +macset(mac, i, value) + MACROS_T *mac; + int i; + char *value; +{ + if (i < 0 || i > MAXMACROID) + return; + + if (tTd(35, 9)) + { + sm_dprintf("macset(%s as ", macname(i)); + xputs(value); + sm_dprintf(")\n"); + } + mac->mac_table[i] = value; +} + /* ** MACVALUE -- return uninterpreted value of a macro. ** +** Does fancy path searching. +** The low level counterpart is macget. +** ** Parameters: ** n -- the name of the macro. +** e -- envelope in which to start looking for the macro. ** ** Returns: ** The value of n. @@ -289,9 +406,16 @@ macvalue(n, e) register ENVELOPE *e; { n = bitidx(n); + if (e != NULL && e->e_mci != NULL) + { + register char *p = e->e_mci->mci_macro.mac_table[n]; + + if (p != NULL) + return p; + } while (e != NULL) { - register char *p = e->e_macro[n]; + register char *p = e->e_macro.mac_table[n]; if (p != NULL) return p; @@ -299,7 +423,7 @@ macvalue(n, e) break; e = e->e_parent; } - return NULL; + return GlobalMacros.mac_table[n]; } /* ** MACNAME -- return the name of a macro given its internal id @@ -334,7 +458,7 @@ macname(n) return mbuf; } /* -** MACID -- return id of macro identified by its name +** MACID_PARSE -- return id of macro identified by its name ** ** Parameters: ** p -- pointer to name string -- either a single @@ -343,15 +467,16 @@ macname(n) ** after the name. ** ** Returns: -** The internal id code for this macro. This will -** fit into a single byte. +** 0 -- An error was detected. +** 1..255 -- The internal id code for this macro. ** ** Side Effects: ** If this is a new macro name, a new id is allocated. +** On error, syserr is called. */ int -macid(p, ep) +macid_parse(p, ep) register char *p; char **ep; { @@ -361,9 +486,9 @@ macid(p, ep) if (tTd(35, 14)) { - dprintf("macid("); + sm_dprintf("macid("); xputs(p); - dprintf(") => "); + sm_dprintf(") => "); } if (*p == '\0' || (p[0] == '{' && p[1] == '}')) @@ -372,7 +497,7 @@ macid(p, ep) if (ep != NULL) *ep = p; if (tTd(35, 14)) - dprintf("NULL\n"); + sm_dprintf("NULL\n"); return 0; } if (*p != '{') @@ -381,7 +506,7 @@ macid(p, ep) if (ep != NULL) *ep = p + 1; if (tTd(35, 14)) - dprintf("%c\n", bitidx(*p)); + sm_dprintf("%c\n", bitidx(*p)); return bitidx(*p); } bp = mbuf; @@ -401,7 +526,7 @@ macid(p, ep) else if (*p != '}') { syserr("Macro/class name ({%s}) too long (%d chars max)", - mbuf, sizeof mbuf - 1); + mbuf, (int) (sizeof mbuf - 1)); } else if (mbuf[1] == '\0') { @@ -420,7 +545,8 @@ macid(p, ep) { if (NextMacroId > MAXMACROID) { - syserr("Macro/class {%s}: too many long names", mbuf); + syserr("Macro/class {%s}: too many long names", + mbuf); s->s_macro = -1; } else @@ -437,11 +563,11 @@ macid(p, ep) { syserr("Unable to assign macro/class ID (mid = 0x%x)", mid); if (tTd(35, 14)) - dprintf("NULL\n"); + sm_dprintf("NULL\n"); return 0; } if (tTd(35, 14)) - dprintf("0x%x\n", mid); + sm_dprintf("0x%x\n", mid); return mid; } /* @@ -452,8 +578,8 @@ macid(p, ep) ** cl -- the class name. ** ** Returns: -** TRUE if str can be found in cl. -** FALSE otherwise. +** true if str can be found in cl. +** false otherwise. */ bool diff --git a/gnu/usr.sbin/sendmail/sendmail/mailq.1 b/gnu/usr.sbin/sendmail/sendmail/mailq.1 index f8ae4a2b7fd..ecb1b88cbe7 100644 --- a/gnu/usr.sbin/sendmail/sendmail/mailq.1 +++ b/gnu/usr.sbin/sendmail/sendmail/mailq.1 @@ -9,9 +9,9 @@ .\" the sendmail distribution. .\" .\" -.\" $Sendmail: mailq.1,v 8.14.28.3 2000/12/14 23:08:15 gshapiro Exp $ +.\" $Sendmail: mailq.1,v 8.18 2000/12/23 19:37:48 ca Exp $ .\" -.Dd December 14, 2000 +.Dd December 23, 2000 .Dt MAILQ 1 .Os .Sh NAME @@ -61,6 +61,8 @@ indicating the ``controlling user'' information; this shows who will own any programs that are executed on behalf of this message and the name of the alias this command expanded from, if any. +Moreover, status messages for each recipient are printed +if available. .El .Pp The diff --git a/gnu/usr.sbin/sendmail/sendmail/main.c b/gnu/usr.sbin/sendmail/sendmail/main.c index 49d2ae7420f..cad28848a98 100644 --- a/gnu/usr.sbin/sendmail/sendmail/main.c +++ b/gnu/usr.sbin/sendmail/sendmail/main.c @@ -11,8 +11,13 @@ * */ +#define _DEFINE +#include <sendmail.h> +#include <sm/xtrap.h> +#include <sm/signal.h> + #ifndef lint -static char copyright[] = +SM_UNUSED(static char copyright[]) = "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ All rights reserved.\n\ Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.\n\ @@ -20,28 +25,32 @@ static char copyright[] = The Regents of the University of California. All rights reserved.\n"; #endif /* ! lint */ -#ifndef lint -static char id[] = "@(#)$Sendmail: main.c,v 8.485.4.65 2001/07/20 00:53:00 gshapiro Exp $"; -#endif /* ! lint */ - -#define _DEFINE - -#include <sendmail.h> +SM_RCSID("@(#)$Sendmail: main.c,v 8.804 2001/09/08 01:21:09 gshapiro Exp $") #if NETINET || NETINET6 # include <arpa/inet.h> #endif /* NETINET || NETINET6 */ -static SIGFUNC_DECL intindebug __P((int)); -static SIGFUNC_DECL quiesce __P((int)); -#ifdef SIGUSR1 -static SIGFUNC_DECL sigusr1 __P((int)); -# endif /* SIGUSR1 */ -static SIGFUNC_DECL term_daemon __P((int)); +/* for getcfname() */ +#include <sendmail/pathnames.h> + +static SM_DEBUG_T +DebugNoPRestart = SM_DEBUG_INITIALIZER("no_persistent_restart", + "@(#)$Debug: no_persistent_restart - don't restart, log only $"); + static void dump_class __P((STAB *, int)); static void obsolete __P((char **)); static void testmodeline __P((char *, ENVELOPE *)); +static char *getextenv __P((const char *)); +static void sm_printoptions __P((char **)); +static SIGFUNC_DECL intindebug __P((int)); +static SIGFUNC_DECL sighup __P((int)); +static SIGFUNC_DECL sigpipe __P((int)); +static SIGFUNC_DECL sigterm __P((int)); +#ifdef SIGUSR1 +static SIGFUNC_DECL sigusr1 __P((int)); +#endif /* SIGUSR1 */ /* ** SENDMAIL -- Post mail to a set of destinations. @@ -59,7 +68,7 @@ static void testmodeline __P((char *, ENVELOPE *)); ** ** See the associated documentation for details. ** -** Author: +** Authors: ** Eric Allman, UCB/INGRES (until 10/81). ** Britton-Lee, Inc., purveyors of fine ** database computers (11/81 - 10/88). @@ -71,31 +80,30 @@ static void testmodeline __P((char *, ENVELOPE *)); ** The support of the my employers is gratefully acknowledged. ** Few of them (Britton-Lee in particular) have had ** anything to gain from my involvement in this project. +** +** Gregory Neil Shapiro, +** Worcester Polytechnic Institute (until 3/98). +** Sendmail, Inc. (3/98 - present). +** +** Claus Assmann, +** Sendmail, Inc. (12/98 - present). */ - -int NextMailer; /* "free" index into Mailer struct */ char *FullName; /* sender's full name */ ENVELOPE BlankEnvelope; /* a "blank" envelope */ static ENVELOPE MainEnvelope; /* the envelope around the basic letter */ ADDRESS NullAddress = /* a null address */ { "", "", NULL, "" }; char *CommandLineArgs; /* command line args for pid file */ -bool Warn_Q_option = FALSE; /* warn about Q option use */ +bool Warn_Q_option = false; /* warn about Q option use */ static int MissingFds = 0; /* bit map of fds missing on startup */ +char *Mbdb = "pw"; /* mailbox database defaults to /etc/passwd */ #ifdef NGROUPS_MAX GIDSET_T InitialGidSet[NGROUPS_MAX]; #endif /* NGROUPS_MAX */ -#if DAEMON && !SMTP -ERROR %%%% Cannot have DAEMON mode without SMTP %%%% ERROR -#endif /* DAEMON && !SMTP */ -#if SMTP && !QUEUE -ERROR %%%% Cannot have SMTP mode without QUEUE %%%% ERROR -#endif /* SMTP && !QUEUE */ - -#define MAXCONFIGLEVEL 9 /* highest config version level known */ +#define MAXCONFIGLEVEL 10 /* highest config version level known */ #if SASL static sasl_callback_t srvcallbacks[] = @@ -104,10 +112,16 @@ static sasl_callback_t srvcallbacks[] = { SASL_CB_PROXY_POLICY, &proxy_policy, NULL }, { SASL_CB_LIST_END, NULL, NULL } }; - #endif /* SASL */ -int SubmitMode; +unsigned int SubmitMode; +int SyslogPrefixLen; /* estimated length of syslog prefix */ +#define PIDLEN 6 /* pid length for computing SyslogPrefixLen */ +#ifndef SL_FUDGE +# define SL_FUDGE 10 /* fudge offset for SyslogPrefixLen */ +#endif /* ! SL_FUDGE */ +#define SLDLL 8 /* est. length of default syslog label */ + int main(argc, argv, envp) @@ -123,32 +137,48 @@ main(argc, argv, envp) register int i; int j; int dp; - bool safecf = TRUE; + bool safecf = true; BITMAP256 *p_flags = NULL; /* daemon flags */ - bool warn_C_flag = FALSE; - bool auth = TRUE; /* whether to set e_auth_param */ + bool warn_C_flag = false; + bool auth = true; /* whether to set e_auth_param */ char warn_f_flag = '\0'; - bool run_in_foreground = FALSE; /* -bD mode */ - static bool reenter = FALSE; + bool run_in_foreground = false; /* -bD mode */ struct passwd *pw; struct hostent *hp; char *nullserver = NULL; char *authinfo = NULL; char *sysloglabel = NULL; /* label for syslog */ - bool forged; + char *conffile = NULL; /* name of .cf file */ + char *runqueuegroup = NULL; /* queue group to process */ + bool forged, negate; + bool queuepersistent = false; /* queue runner process runs forever */ + bool foregroundqueue = false; /* queue run in foreground */ + int cftype; /* which cf file to use? */ + static time_t starttime = 0; /* when was process started */ struct stat traf_st; /* for TrafficLog FIFO check */ + char buf[MAXLINE]; char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */ static char rnamebuf[MAXNAME]; /* holds RealUserName */ char *emptyenviron[1]; -# if STARTTLS +#if STARTTLS bool tls_ok; -# endif /* STARTTLS */ +#endif /* STARTTLS */ QUEUE_CHAR *new; + ENVELOPE *e; extern int DtableSize; extern int optind; extern int opterr; extern char *optarg; extern char **environ; +#if SASL + extern void sm_sasl_init __P((void)); +#endif /* SASL */ + + /* turn off profiling */ + SM_PROF(0); + + /* install default exception handler */ + sm_exc_newthread(fatal_error); /* ** Check to see if we reentered. @@ -156,12 +186,12 @@ main(argc, argv, envp) ** were NULL when invoked. */ - if (reenter) + if (starttime != 0) { syserr("main: reentered!"); abort(); } - reenter = TRUE; + starttime = curtime(); /* avoid null pointer dereferences */ TermEscape.te_rv_on = TermEscape.te_rv_off = ""; @@ -176,6 +206,10 @@ main(argc, argv, envp) /* do machine-dependent initializations */ init_md(argc, argv); + CurrentPid = getpid(); + + /* get whatever .cf file is right for the opmode */ + cftype = SM_GET_RIGHT_CF; /* in 4.4BSD, the table can be huge; impose a reasonable limit */ @@ -195,30 +229,35 @@ main(argc, argv, envp) i = DtableSize; while (--i > 0) { - if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) + if (i != STDIN_FILENO && i != STDOUT_FILENO && + i != STDERR_FILENO) (void) close(i); } errno = 0; #if LOG +# ifndef SM_LOG_STR +# define SM_LOG_STR "sendmail" +# endif /* ! SM_LOG_STR */ # ifdef LOG_MAIL - openlog("sendmail", LOG_PID, LOG_MAIL); + openlog(SM_LOG_STR, LOG_PID, LOG_MAIL); # else /* LOG_MAIL */ - openlog("sendmail", LOG_PID); + openlog(SM_LOG_STR, LOG_PID); # endif /* LOG_MAIL */ #endif /* LOG */ + SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + SL_FUDGE + SLDLL; if (MissingFds != 0) { char mbuf[MAXLINE]; mbuf[0] = '\0'; if (bitset(1 << STDIN_FILENO, MissingFds)) - (void) strlcat(mbuf, ", stdin", sizeof mbuf); + (void) sm_strlcat(mbuf, ", stdin", sizeof mbuf); if (bitset(1 << STDOUT_FILENO, MissingFds)) - (void) strlcat(mbuf, ", stdout", sizeof mbuf); + (void) sm_strlcat(mbuf, ", stdout", sizeof mbuf); if (bitset(1 << STDERR_FILENO, MissingFds)) - (void) strlcat(mbuf, ", stderr", sizeof mbuf); + (void) sm_strlcat(mbuf, ", stderr", sizeof mbuf); syserr("File descriptors missing on startup: %s", &mbuf[2]); } @@ -231,30 +270,30 @@ main(argc, argv, envp) checkfd012("after openlog"); #endif /* XDEBUG */ - tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); + tTsetup(tTdvect, sizeof tTdvect, "0-99.1,*_trace_*.1"); #ifdef NGROUPS_MAX /* save initial group set for future checks */ i = getgroups(NGROUPS_MAX, InitialGidSet); - if (i == 0) + if (i <= 0) InitialGidSet[0] = (GID_T) -1; while (i < NGROUPS_MAX) InitialGidSet[i++] = InitialGidSet[0]; #endif /* NGROUPS_MAX */ /* drop group id privileges (RunAsUser not yet set) */ - dp = drop_privileges(FALSE); + dp = drop_privileges(false); setstat(dp); -# ifdef SIGUSR1 +#ifdef SIGUSR1 /* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */ if (getuid() == 0 || (getuid() == geteuid() && getgid() == getegid())) { /* arrange to dump state on user-1 signal */ - (void) setsignal(SIGUSR1, sigusr1); + (void) sm_signal(SIGUSR1, sigusr1); } -# endif /* SIGUSR1 */ +#endif /* SIGUSR1 */ /* initialize for setproctitle */ initsetproctitle(argc, argv, envp); @@ -268,13 +307,13 @@ main(argc, argv, envp) #if defined(__osf__) || defined(_AIX3) -# define OPTIONS "B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:x" +# define OPTIONS "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:x" #endif /* defined(__osf__) || defined(_AIX3) */ #if defined(sony_news) -# define OPTIONS "B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtUV:vX:" +# define OPTIONS "A:B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtV:vX:" #endif /* defined(sony_news) */ #ifndef OPTIONS -# define OPTIONS "B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:" +# define OPTIONS "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:" #endif /* ! OPTIONS */ opterr = 0; while ((j = getopt(argc, argv, OPTIONS)) != -1) @@ -282,30 +321,24 @@ main(argc, argv, envp) switch (j) { case 'd': - /* hack attack -- see if should use ANSI mode */ - if (strcmp(optarg, "ANSI") == 0) - { - TermEscape.te_rv_on = "\033[7m"; - TermEscape.te_rv_off = "\033[0m"; - break; - } tTflag(optarg); - setbuf(stdout, (char *) NULL); + (void) sm_io_setvbuf(smioout, SM_TIME_DEFAULT, + (char *) NULL, SM_IO_NBF, + SM_IO_BUFSIZ); break; case 'G': /* relay (gateway) submission */ - SubmitMode |= SUBMIT_MTA; + SubmitMode = SUBMIT_MTA; break; case 'L': - j = min(strlen(optarg), 24) + 1; + j = SM_MIN(strlen(optarg), 24) + 1; sysloglabel = xalloc(j); - (void) strlcpy(sysloglabel, optarg, j); + (void) sm_strlcpy(sysloglabel, optarg, j); + SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + + SL_FUDGE + j; break; - case 'U': /* initial (user) submission */ - SubmitMode |= SUBMIT_MSA; - break; } } opterr = 1; @@ -342,56 +375,67 @@ main(argc, argv, envp) */ setdefaults(&BlankEnvelope); + initmacros(&BlankEnvelope); + set_op_mode(MD_DELIVER); RealUid = getuid(); RealGid = getgid(); pw = sm_getpwuid(RealUid); if (pw != NULL) - (void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name); + (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf); else - (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", - (int) RealUid); + (void) sm_snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", + (int) RealUid); RealUserName = rnamebuf; if (tTd(0, 101)) { - dprintf("Version %s\n", Version); - finis(FALSE, EX_OK); + sm_dprintf("Version %s\n", Version); + finis(false, EX_OK); + /* NOTREACHED */ } /* - ** if running non-setuid binary as non-root, pretend + ** if running non-set-user-ID binary as non-root, pretend ** we are the RunAsUid */ if (RealUid != 0 && geteuid() == RealUid) { if (tTd(47, 1)) - dprintf("Non-setuid binary: RunAsUid = RealUid = %d\n", - (int)RealUid); + sm_dprintf("Non-set-user-ID binary: RunAsUid = RealUid = %d\n", + (int) RealUid); RunAsUid = RealUid; } else if (geteuid() != 0) RunAsUid = geteuid(); - if (RealUid != 0 && getegid() == RealGid) + EffGid = getegid(); + if (RealUid != 0 && EffGid == RealGid) RunAsGid = RealGid; if (tTd(47, 5)) { - dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n", - (int)geteuid(), (int)getuid(), - (int)getegid(), (int)getgid()); - dprintf("main: RunAsUser = %d:%d\n", - (int)RunAsUid, (int)RunAsGid); + sm_dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n", + (int) geteuid(), (int) getuid(), + (int) getegid(), (int) getgid()); + sm_dprintf("main: RunAsUser = %d:%d\n", + (int) RunAsUid, (int) RunAsGid); } /* save command line arguments */ j = 0; for (av = argv; *av != NULL; ) j += strlen(*av++) + 1; + if (j < 0 || j > SM_ARG_MAX) + { + syserr("!Arguments too long"); + + /* NOTREACHED */ + return EX_USAGE; + } SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1)); CommandLineArgs = xalloc(j); p = CommandLineArgs; @@ -402,7 +446,7 @@ main(argc, argv, envp) SaveArgv[i++] = newstr(*av); if (av != argv) *p++ = ' '; - (void) strlcpy(p, *av++, j); + (void) sm_strlcpy(p, *av++, j); h = strlen(p); p += h; j -= h + 1; @@ -411,60 +455,47 @@ main(argc, argv, envp) if (tTd(0, 1)) { - int ll; extern char *CompileOptions[]; - dprintf("Version %s\n Compiled with:", Version); - av = CompileOptions; - ll = 7; - while (*av != NULL) - { - if (ll + strlen(*av) > 63) - { - dprintf("\n"); - ll = 0; - } - if (ll == 0) - dprintf("\t\t"); - else - dprintf(" "); - dprintf("%s", *av); - ll += strlen(*av++) + 1; - } - dprintf("\n"); + sm_dprintf("Version %s\n Compiled with:", Version); + sm_printoptions(CompileOptions); } if (tTd(0, 10)) { - int ll; extern char *OsCompileOptions[]; - dprintf(" OS Defines:"); - av = OsCompileOptions; - ll = 7; - while (*av != NULL) - { - if (ll + strlen(*av) > 63) - { - dprintf("\n"); - ll = 0; - } - if (ll == 0) - dprintf("\t\t"); - else - dprintf(" "); - dprintf("%s", *av); - ll += strlen(*av++) + 1; - } - dprintf("\n"); + sm_dprintf(" OS Defines:"); + sm_printoptions(OsCompileOptions); #ifdef _PATH_UNIX - dprintf("Kernel symbols:\t%s\n", _PATH_UNIX); + sm_dprintf("Kernel symbols:\t%s\n", _PATH_UNIX); #endif /* _PATH_UNIX */ - dprintf(" Def Conf file:\t%s\n", getcfname()); - dprintf(" Def Pid file:\t%s\n", PidFile); + + /* XXX This doesn't work because OpMode isn't set correctly */ + sm_dprintf(" Def Conf file:\t%s\n", getcfname(OpMode, + SubmitMode, + SM_GET_RIGHT_CF, + conffile)); + sm_dprintf(" Def Pid file:\t%s\n", PidFile); } - InChannel = stdin; - OutChannel = stdout; + if (tTd(0, 12)) + { + extern char *SmCompileOptions[]; + + sm_dprintf(" libsm Defines:"); + sm_printoptions(SmCompileOptions); + } + + if (tTd(0, 13)) + { + extern char *FFRCompileOptions[]; + + sm_dprintf(" FFR Defines:"); + sm_printoptions(FFRCompileOptions); + } + + InChannel = smioin; + OutChannel = smioout; /* clear sendmail's environment */ ExternalEnviron = environ; @@ -475,6 +506,7 @@ main(argc, argv, envp) ** restore any original TZ setting until TimeZoneSpec has been ** determined - or early log messages may get bogus time stamps */ + if ((p = getextenv("TZ")) != NULL) { char *tz; @@ -482,16 +514,15 @@ main(argc, argv, envp) tzlen = strlen(p) + 4; tz = xalloc(tzlen); - (void) snprintf(tz, tzlen, "TZ=%s", p); + (void) sm_strlcpyn(tz, tzlen, 2, "TZ=", p); (void) putenv(tz); } /* prime the child environment */ setuserenv("AGENT", "sendmail"); - (void) setsignal(SIGPIPE, SIG_IGN); + (void) sm_signal(SIGPIPE, SIG_IGN); OldUmask = umask(022); - OpMode = MD_DELIVER; FullName = getextenv("NAME"); /* @@ -501,20 +532,13 @@ main(argc, argv, envp) #if NAMED_BIND if (!bitset(RES_INIT, _res.options)) (void) res_init(); - - /* - ** hack to avoid crashes when debugging for the resolver is - ** turned on and sfio is used - */ if (tTd(8, 8)) -# if !SFIO || SFIO_STDIO_COMPAT _res.options |= RES_DEBUG; -# else /* !SFIO || SFIO_STDIO_COMPAT */ - dprintf("RES_DEBUG not available due to SFIO\n"); -# endif /* !SFIO || SFIO_STDIO_COMPAT */ else _res.options &= ~RES_DEBUG; # ifdef RES_NOALIASES + if (bitset(RES_NOALIASES, _res.options)) + ResNoAliases = true; _res.options |= RES_NOALIASES; # endif /* RES_NOALIASES */ TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry; @@ -529,22 +553,21 @@ main(argc, argv, envp) from = NULL; /* initialize some macros, etc. */ - initmacros(CurEnv); - init_vendor_macros(CurEnv); + init_vendor_macros(&BlankEnvelope); /* version */ - define('v', Version, CurEnv); + macdefine(&BlankEnvelope.e_macro, A_PERM, 'v', Version); /* hostname */ hp = myhostname(jbuf, sizeof jbuf); if (jbuf[0] != '\0') { - struct utsname utsname; + struct utsname utsname; if (tTd(0, 4)) - dprintf("canonical name: %s\n", jbuf); - define('w', newstr(jbuf), CurEnv); /* must be new string */ - define('j', newstr(jbuf), CurEnv); + sm_dprintf("canonical name: %s\n", jbuf); + macdefine(&BlankEnvelope.e_macro, A_TEMP, 'w', jbuf); + macdefine(&BlankEnvelope.e_macro, A_TEMP, 'j', jbuf); setclass('w', jbuf); p = strchr(jbuf, '.'); @@ -552,13 +575,14 @@ main(argc, argv, envp) { if (p[1] != '\0') { - define('m', newstr(&p[1]), CurEnv); + macdefine(&BlankEnvelope.e_macro, A_TEMP, 'm', + &p[1]); } while (p != NULL && strchr(&p[1], '.') != NULL) { *p = '\0'; if (tTd(0, 4)) - dprintf("\ta.k.a.: %s\n", jbuf); + sm_dprintf("\ta.k.a.: %s\n", jbuf); setclass('w', jbuf); *p++ = '.'; p = strchr(p, '.'); @@ -570,15 +594,14 @@ main(argc, argv, envp) else { if (tTd(0, 22)) - dprintf("uname failed (%s)\n", - errstring(errno)); + sm_dprintf("uname failed (%s)\n", + sm_errstring(errno)); makelower(jbuf); p = jbuf; } if (tTd(0, 4)) - dprintf(" UUCP nodename: %s\n", p); - p = newstr(p); - define('k', p, CurEnv); + sm_dprintf(" UUCP nodename: %s\n", p); + macdefine(&BlankEnvelope.e_macro, A_TEMP, 'k', p); setclass('k', p); setclass('w', p); } @@ -587,11 +610,11 @@ main(argc, argv, envp) for (av = hp->h_aliases; av != NULL && *av != NULL; av++) { if (tTd(0, 4)) - dprintf("\ta.k.a.: %s\n", *av); + sm_dprintf("\ta.k.a.: %s\n", *av); setclass('w', *av); } #if NETINET || NETINET6 - for (i = 0; hp->h_addr_list[i] != NULL; i++) + for (i = 0; i >= 0 && hp->h_addr_list[i] != NULL; i++) { # if NETINET6 char *addr; @@ -612,8 +635,8 @@ main(argc, argv, envp) break; memmove(&ia, hp->h_addr_list[i], INADDRSZ); - (void) snprintf(ipbuf, sizeof ipbuf, - "[%.100s]", inet_ntoa(ia)); + (void) sm_snprintf(ipbuf, sizeof ipbuf, + "[%.100s]", inet_ntoa(ia)); break; # endif /* NETINET */ @@ -625,8 +648,8 @@ main(argc, argv, envp) memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ); addr = anynet_ntop(&ia6, buf6, sizeof buf6); if (addr != NULL) - (void) snprintf(ipbuf, sizeof ipbuf, - "[%.100s]", addr); + (void) sm_snprintf(ipbuf, sizeof ipbuf, + "[%.100s]", addr); break; # endif /* NETINET6 */ } @@ -634,20 +657,21 @@ main(argc, argv, envp) break; if (tTd(0, 4)) - dprintf("\ta.k.a.: %s\n", ipbuf); + sm_dprintf("\ta.k.a.: %s\n", ipbuf); setclass('w', ipbuf); } #endif /* NETINET || NETINET6 */ -#if _FFR_FREEHOSTENT && NETINET6 +#if NETINET6 freehostent(hp); hp = NULL; -#endif /* _FFR_FREEHOSTENT && NETINET6 */ +#endif /* NETINET6 */ } /* current time */ - define('b', arpadate((char *) NULL), CurEnv); + macdefine(&BlankEnvelope.e_macro, A_TEMP, 'b', arpadate((char *) NULL)); + /* current load average */ - CurrentLA = sm_getla(CurEnv); + sm_getla(); QueueLimitRecipient = (QUEUE_CHAR *) NULL; QueueLimitSender = (QUEUE_CHAR *) NULL; @@ -662,15 +686,15 @@ main(argc, argv, envp) if (p++ == NULL) p = *av; if (strcmp(p, "newaliases") == 0) - OpMode = MD_INITALIAS; + set_op_mode(MD_INITALIAS); else if (strcmp(p, "mailq") == 0) - OpMode = MD_PRINT; + set_op_mode(MD_PRINT); else if (strcmp(p, "smtpd") == 0) - OpMode = MD_DAEMON; + set_op_mode(MD_DAEMON); else if (strcmp(p, "hoststat") == 0) - OpMode = MD_HOSTSTAT; + set_op_mode(MD_HOSTSTAT); else if (strcmp(p, "purgestat") == 0) - OpMode = MD_PURGESTAT; + set_op_mode(MD_PURGESTAT); optind = 1; while ((j = getopt(argc, argv, OPTIONS)) != -1) @@ -682,27 +706,17 @@ main(argc, argv, envp) { case MD_DAEMON: case MD_FGDAEMON: -#if !DAEMON - usrerr("Daemon mode not implemented"); - ExitStat = EX_USAGE; - break; -#endif /* !DAEMON */ case MD_SMTP: -#if !SMTP - usrerr("I don't speak SMTP"); - ExitStat = EX_USAGE; - break; -#endif /* !SMTP */ - case MD_INITALIAS: case MD_DELIVER: case MD_VERIFY: case MD_TEST: case MD_PRINT: + case MD_PRINTNQE: case MD_HOSTSTAT: case MD_PURGESTAT: case MD_ARPAFTP: - OpMode = j; + set_op_mode(j); break; case MD_FREEZE: @@ -717,17 +731,22 @@ main(argc, argv, envp) } break; + case 'A': /* use Alternate sendmail/submit.cf */ + cftype = optarg[0] == 'c' ? SM_GET_SUBMIT_CF + : SM_GET_SENDMAIL_CF; + break; + case 'B': /* body type */ - CurEnv->e_bodytype = newstr(optarg); + BlankEnvelope.e_bodytype = newstr(optarg); break; case 'C': /* select configuration file (already done) */ if (RealUid != 0) - warn_C_flag = TRUE; - ConfFile = newstr(optarg); - dp = drop_privileges(TRUE); + warn_C_flag = true; + conffile = newstr(optarg); + dp = drop_privileges(true); setstat(dp); - safecf = FALSE; + safecf = false; break; case 'd': /* debugging -- already done */ @@ -741,7 +760,7 @@ main(argc, argv, envp) ExitStat = EX_USAGE; break; } - from = newstr(denlstring(optarg, TRUE, TRUE)); + from = newstr(denlstring(optarg, true, true)); if (strcmp(RealUserName, from) != 0) warn_f_flag = j; break; @@ -755,7 +774,12 @@ main(argc, argv, envp) break; case 'h': /* hop count */ - CurEnv->e_hopcount = (short) strtol(optarg, &ep, 10); + BlankEnvelope.e_hopcount = (short) strtol(optarg, &ep, + 10); + (void) sm_snprintf(buf, sizeof buf, "%d", + BlankEnvelope.e_hopcount); + macdefine(&BlankEnvelope.e_macro, A_TEMP, 'c', buf); + if (*ep) { usrerr("Bad hop count (%s)", optarg); @@ -768,25 +792,25 @@ main(argc, argv, envp) break; case 'n': /* don't alias */ - NoAlias = TRUE; + NoAlias = true; break; case 'N': /* delivery status notifications */ DefaultNotify |= QHASNOTIFY; - define(macid("{dsn_notify}", NULL), - newstr(optarg), CurEnv); - if (strcasecmp(optarg, "never") == 0) + macdefine(&BlankEnvelope.e_macro, A_TEMP, + macid("{dsn_notify}"), optarg); + if (sm_strcasecmp(optarg, "never") == 0) break; for (p = optarg; p != NULL; optarg = p) { p = strchr(p, ','); if (p != NULL) *p++ = '\0'; - if (strcasecmp(optarg, "success") == 0) + if (sm_strcasecmp(optarg, "success") == 0) DefaultNotify |= QPINGONSUCCESS; - else if (strcasecmp(optarg, "failure") == 0) + else if (sm_strcasecmp(optarg, "failure") == 0) DefaultNotify |= QPINGONFAILURE; - else if (strcasecmp(optarg, "delay") == 0) + else if (sm_strcasecmp(optarg, "delay") == 0) DefaultNotify |= QPINGONDELAY; else { @@ -797,11 +821,12 @@ main(argc, argv, envp) break; case 'o': /* set option */ - setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv); + setoption(*optarg, optarg + 1, false, true, + &BlankEnvelope); break; case 'O': /* set option (long form) */ - setoption(' ', optarg, FALSE, TRUE, CurEnv); + setoption(' ', optarg, false, true, &BlankEnvelope); break; case 'p': /* set protocol */ @@ -811,26 +836,28 @@ main(argc, argv, envp) *p++ = '\0'; if (*p != '\0') { - ep = xalloc(strlen(p) + 1); + ep = sm_malloc_x(strlen(p) + 1); cleanstrcpy(ep, p, MAXNAME); - define('s', ep, CurEnv); + macdefine(&BlankEnvelope.e_macro, + A_HEAP, 's', ep); } } if (*optarg != '\0') { - ep = xalloc(strlen(optarg) + 1); + ep = sm_malloc_x(strlen(optarg) + 1); cleanstrcpy(ep, optarg, MAXNAME); - define('r', ep, CurEnv); + macdefine(&BlankEnvelope.e_macro, A_HEAP, + 'r', ep); } break; case 'q': /* run queue files at intervals */ -#if QUEUE /* sanity check */ if (OpMode != MD_DELIVER && OpMode != MD_DAEMON && OpMode != MD_FGDAEMON && OpMode != MD_PRINT && + OpMode != MD_PRINTNQE && OpMode != MD_QUEUERUN) { usrerr("Can not use -q with -b%c", OpMode); @@ -840,33 +867,65 @@ main(argc, argv, envp) /* don't override -bd, -bD or -bp */ if (OpMode == MD_DELIVER) - OpMode = MD_QUEUERUN; + set_op_mode(MD_QUEUERUN); FullName = NULL; + negate = optarg[0] == '!'; + if (negate) + { + /* negate meaning of pattern match */ + optarg++; /* skip '!' for next switch */ + } + switch (optarg[0]) { - case 'I': + case 'G': /* Limit by queue group name */ + if (negate) + { + usrerr("Can not use -q!G"); + ExitStat = EX_USAGE; + break; + } + runqueuegroup = newstr(&optarg[1]); + break; + + case 'I': /* Limit by ID */ new = (QUEUE_CHAR *) xalloc(sizeof *new); new->queue_match = newstr(&optarg[1]); + new->queue_negate = negate; new->queue_next = QueueLimitId; QueueLimitId = new; break; - case 'R': + case 'R': /* Limit by recipient */ new = (QUEUE_CHAR *) xalloc(sizeof *new); new->queue_match = newstr(&optarg[1]); + new->queue_negate = negate; new->queue_next = QueueLimitRecipient; QueueLimitRecipient = new; break; - case 'S': + case 'S': /* Limit by sender */ new = (QUEUE_CHAR *) xalloc(sizeof *new); new->queue_match = newstr(&optarg[1]); + new->queue_negate = negate; new->queue_next = QueueLimitSender; QueueLimitSender = new; break; + case 'f': /* foreground queue run */ + foregroundqueue = true; + break; + + case 'p': /* Persistent queue */ + queuepersistent = true; + if (QueueIntvl == 0) + QueueIntvl = 1; + if (optarg[1] == '\0') + break; + ++optarg; + /* FALL THRU */ default: i = Errors; QueueIntvl = convtime(optarg, 'm'); @@ -876,37 +935,29 @@ main(argc, argv, envp) ExitStat = EX_USAGE; break; } -#else /* QUEUE */ - usrerr("I don't know about queues"); - ExitStat = EX_USAGE; -#endif /* QUEUE */ break; case 'R': /* DSN RET: what to return */ - if (bitset(EF_RET_PARAM, CurEnv->e_flags)) + if (bitset(EF_RET_PARAM, BlankEnvelope.e_flags)) { usrerr("Duplicate -R flag"); ExitStat = EX_USAGE; break; } - CurEnv->e_flags |= EF_RET_PARAM; - if (strcasecmp(optarg, "hdrs") == 0) - CurEnv->e_flags |= EF_NO_BODY_RETN; - else if (strcasecmp(optarg, "full") != 0) + BlankEnvelope.e_flags |= EF_RET_PARAM; + if (sm_strcasecmp(optarg, "hdrs") == 0) + BlankEnvelope.e_flags |= EF_NO_BODY_RETN; + else if (sm_strcasecmp(optarg, "full") != 0) { usrerr("Invalid -R value"); ExitStat = EX_USAGE; } - define(macid("{dsn_ret}", NULL), - newstr(optarg), CurEnv); + macdefine(&BlankEnvelope.e_macro, A_TEMP, + macid("{dsn_ret}"), optarg); break; case 't': /* read recipients from message */ - GrabTo = TRUE; - break; - - case 'U': /* initial (user) submission */ - /* already set */ + GrabTo = true; break; case 'V': /* DSN ENVID: set "original" envelope id */ @@ -917,31 +968,34 @@ main(argc, argv, envp) } else { - CurEnv->e_envid = newstr(optarg); - define(macid("{dsn_envid}", NULL), - newstr(optarg), CurEnv); + BlankEnvelope.e_envid = newstr(optarg); + macdefine(&BlankEnvelope.e_macro, A_TEMP, + macid("{dsn_envid}"), optarg); } break; case 'X': /* traffic log file */ - dp = drop_privileges(TRUE); + dp = drop_privileges(true); setstat(dp); if (stat(optarg, &traf_st) == 0 && S_ISFIFO(traf_st.st_mode)) - TrafficLogFile = fopen(optarg, "w"); + TrafficLogFile = sm_io_open(SmFtStdio, + SM_TIME_DEFAULT, + optarg, + SM_IO_WRONLY, NULL); else - TrafficLogFile = fopen(optarg, "a"); + TrafficLogFile = sm_io_open(SmFtStdio, + SM_TIME_DEFAULT, + optarg, + SM_IO_APPEND, NULL); if (TrafficLogFile == NULL) { syserr("cannot open %s", optarg); ExitStat = EX_CANTCREAT; break; } -#if HASSETVBUF - (void) setvbuf(TrafficLogFile, NULL, _IOLBF, 0); -#else /* HASSETVBUF */ - (void) setlinebuf(TrafficLogFile); -#endif /* HASSETVBUF */ + (void) sm_io_setvbuf(TrafficLogFile, SM_TIME_DEFAULT, + NULL, SM_IO_LBF, 0); break; /* compatibility flags */ @@ -950,21 +1004,21 @@ main(argc, argv, envp) case 'm': /* send to me too */ case 'T': /* set timeout interval */ case 'v': /* give blow-by-blow description */ - setoption(j, "T", FALSE, TRUE, CurEnv); + setoption(j, "T", false, true, &BlankEnvelope); break; case 'e': /* error message disposition */ case 'M': /* define macro */ - setoption(j, optarg, FALSE, TRUE, CurEnv); + setoption(j, optarg, false, true, &BlankEnvelope); break; case 's': /* save From lines in headers */ - setoption('f', "T", FALSE, TRUE, CurEnv); + setoption('f', "T", false, true, &BlankEnvelope); break; #ifdef DBM case 'I': /* initialize alias DBM file */ - OpMode = MD_INITALIAS; + set_op_mode(MD_INITALIAS); break; #endif /* DBM */ @@ -980,39 +1034,23 @@ main(argc, argv, envp) #endif /* defined(sony_news) */ default: - finis(TRUE, EX_USAGE); + finis(true, EX_USAGE); + /* NOTREACHED */ break; } } av += optind; - if (bitset(SUBMIT_MTA, SubmitMode) && - bitset(SUBMIT_MSA, SubmitMode)) - { - /* sanity check */ - errno = 0; /* reset to avoid bogus error messages */ - syserr("Cannot use both -G and -U together"); - } - else if (bitset(SUBMIT_MTA, SubmitMode)) - define(macid("{daemon_flags}", NULL), "CC f", CurEnv); - else if (bitset(SUBMIT_MSA, SubmitMode)) + if (bitset(SUBMIT_MTA, SubmitMode)) { - define(macid("{daemon_flags}", NULL), "c u", CurEnv); - - /* check for wrong OpMode */ - if (OpMode != MD_DELIVER && OpMode != MD_SMTP) - { - errno = 0; /* reset to avoid bogus error msgs */ - syserr("Cannot use -U and -b%c", OpMode); - } + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{daemon_flags}"), "CC f"); } - else + else if (OpMode == MD_DELIVER || OpMode == MD_SMTP) { -#if _FFR_DEFAULT_SUBMIT_TO_MSA - define(macid("{daemon_flags}", NULL), "c u", CurEnv); -#else /* _FFR_DEFAULT_SUBMIT_TO_MSA */ - /* EMPTY */ -#endif /* _FFR_DEFAULT_SUBMIT_TO_MSA */ + SubmitMode = SUBMIT_MSA; + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{daemon_flags}"), "c u"); } /* @@ -1021,27 +1059,20 @@ main(argc, argv, envp) ** Extract special fields for local use. */ - /* set up ${opMode} for use in config file */ - { - char mbuf[2]; - - mbuf[0] = OpMode; - mbuf[1] = '\0'; - define(MID_OPMODE, newstr(mbuf), CurEnv); - } - #if XDEBUG checkfd012("before readcf"); #endif /* XDEBUG */ - vendor_pre_defaults(CurEnv); + vendor_pre_defaults(&BlankEnvelope); - readcf(getcfname(), safecf, CurEnv); - ConfigFileRead = TRUE; - vendor_post_defaults(CurEnv); + readcf(getcfname(OpMode, SubmitMode, cftype, conffile), + safecf, &BlankEnvelope); +#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) + ConfigFileRead = true; +#endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */ + vendor_post_defaults(&BlankEnvelope); /* Remove the ability for a normal user to send signals */ - if (RealUid != 0 && - RealUid != geteuid()) + if (RealUid != 0 && RealUid != geteuid()) { uid_t new_uid = geteuid(); @@ -1056,17 +1087,17 @@ main(argc, argv, envp) if (new_uid == 0) new_uid = DefUid; if (tTd(47, 5)) - dprintf("Changing real uid to %d\n", (int) new_uid); + sm_dprintf("Changing real uid to %d\n", (int) new_uid); if (setreuid(new_uid, geteuid()) < 0) { syserr("main: setreuid(%d, %d) failed", (int) new_uid, (int) geteuid()); - finis(FALSE, EX_OSERR); + finis(false, EX_OSERR); /* NOTREACHED */ } if (tTd(47, 10)) - dprintf("Now running as e/ruid %d:%d\n", - (int) geteuid(), (int) getuid()); + sm_dprintf("Now running as e/ruid %d:%d\n", + (int) geteuid(), (int) getuid()); #else /* HASSETREUID */ /* ** Have to change both effective and real so need to @@ -1074,23 +1105,44 @@ main(argc, argv, envp) */ if (tTd(47, 5)) - dprintf("Changing uid to %d\n", (int) new_uid); + sm_dprintf("Changing uid to %d\n", (int) new_uid); if (setuid(new_uid) < 0) { syserr("main: setuid(%d) failed", (int) new_uid); - finis(FALSE, EX_OSERR); + finis(false, EX_OSERR); /* NOTREACHED */ } if (tTd(47, 10)) - dprintf("Now running as e/ruid %d:%d\n", - (int) geteuid(), (int) getuid()); + sm_dprintf("Now running as e/ruid %d:%d\n", + (int) geteuid(), (int) getuid()); #endif /* HASSETREUID */ } +#if NAMED_BIND + if (FallBackMX != NULL) + (void) getfallbackmxrr(FallBackMX); +#endif /* NAMED_BIND */ + + if (SuperSafe == SAFE_INTERACTIVE && CurEnv->e_sendmode != SM_DELIVER) + { + printf("WARNING: SuperSafe=interactive should only be used with\n DeliveryMode=interactive\n"); + } + + if (UseMSP && (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON)) + { + usrerr("Mail submission program cannot be used as daemon"); + finis(false, EX_USAGE); + } + + if (OpMode == MD_DELIVER || OpMode == MD_SMTP || OpMode == MD_ARPAFTP || + OpMode == MD_DAEMON || OpMode == MD_FGDAEMON || + OpMode == MD_QUEUERUN) + makeworkgroups(); + /* set up the basic signal handlers */ - if (setsignal(SIGINT, SIG_IGN) != SIG_IGN) - (void) setsignal(SIGINT, intsig); - (void) setsignal(SIGTERM, intsig); + if (sm_signal(SIGINT, SIG_IGN) != SIG_IGN) + (void) sm_signal(SIGINT, intsig); + (void) sm_signal(SIGTERM, intsig); /* Enforce use of local time (null string overrides this) */ if (TimeZoneSpec == NULL) @@ -1101,14 +1153,39 @@ main(argc, argv, envp) setuserenv("TZ", NULL); tzset(); + /* initialize mailbox database */ + i = sm_mbdb_initialize(Mbdb); + if (i != EX_OK) + { + usrerr("Can't initialize mailbox database \"%s\": %s", + Mbdb, sm_strexit(i)); + ExitStat = i; + } + /* avoid denial-of-service attacks */ resetlimits(); - if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON) + if (OpMode == MD_TEST) + { + /* can't be done after readcf if RunAs* is used */ + dp = drop_privileges(true); + if (dp != EX_OK) + { + finis(false, dp); + /* NOTREACHED */ + } + } + else if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON) { /* drop privileges -- daemon mode done after socket/bind */ - dp = drop_privileges(FALSE); + dp = drop_privileges(false); setstat(dp); + if (dp == EX_OK && UseMSP && (geteuid() == 0 || getuid() == 0)) + { + usrerr("Mail submission program must have RunAsUser set to non root user"); + finis(false, EX_CONFIG); + /* NOTREACHED */ + } } #if NAMED_BIND @@ -1121,33 +1198,33 @@ main(argc, argv, envp) */ authinfo = getauthinfo(STDIN_FILENO, &forged); - define('_', authinfo, CurEnv); + macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo); /* suppress error printing if errors mailed back or whatever */ - if (CurEnv->e_errormode != EM_PRINT) - HoldErrs = TRUE; + if (BlankEnvelope.e_errormode != EM_PRINT) + HoldErrs = true; /* set up the $=m class now, after .cf has a chance to redefine $m */ - expand("\201m", jbuf, sizeof jbuf, CurEnv); + expand("\201m", jbuf, sizeof jbuf, &BlankEnvelope); if (jbuf[0] != '\0') setclass('m', jbuf); /* probe interfaces and locate any additional names */ - if (!DontProbeInterfaces) + if (DontProbeInterfaces != DPI_PROBENONE) load_if_names(); if (tTd(0, 1)) { - dprintf("\n============ SYSTEM IDENTITY (after readcf) ============"); - dprintf("\n (short domain name) $w = "); - xputs(macvalue('w', CurEnv)); - dprintf("\n (canonical domain name) $j = "); - xputs(macvalue('j', CurEnv)); - dprintf("\n (subdomain name) $m = "); - xputs(macvalue('m', CurEnv)); - dprintf("\n (node name) $k = "); - xputs(macvalue('k', CurEnv)); - dprintf("\n========================================================\n\n"); + sm_dprintf("\n============ SYSTEM IDENTITY (after readcf) ============"); + sm_dprintf("\n (short domain name) $w = "); + xputs(macvalue('w', &BlankEnvelope)); + sm_dprintf("\n (canonical domain name) $j = "); + xputs(macvalue('j', &BlankEnvelope)); + sm_dprintf("\n (subdomain name) $m = "); + xputs(macvalue('m', &BlankEnvelope)); + sm_dprintf("\n (node name) $k = "); + xputs(macvalue('k', &BlankEnvelope)); + sm_dprintf("\n========================================================\n\n"); } /* @@ -1157,23 +1234,28 @@ main(argc, argv, envp) /* process authorization warnings from command line */ if (warn_C_flag) - auth_warning(CurEnv, "Processed by %s with -C %s", - RealUserName, ConfFile); + auth_warning(&BlankEnvelope, "Processed by %s with -C %s", + RealUserName, conffile); if (Warn_Q_option && !wordinclass(RealUserName, 't')) - auth_warning(CurEnv, "Processed from queue %s", QueueDir); + auth_warning(&BlankEnvelope, "Processed from queue %s", + QueueDir); + if (sysloglabel != NULL && !wordinclass(RealUserName, 't') && + RealUid != 0 && RealUid != TrustedUid && LogLevel > 1) + sm_syslog(LOG_WARNING, NOQID, "user %d changed syslog label", + (int) RealUid); /* check body type for legality */ - if (CurEnv->e_bodytype == NULL) + if (BlankEnvelope.e_bodytype == NULL) /* EMPTY */ /* nothing */ ; - else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0) - SevenBitInput = TRUE; - else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0) - SevenBitInput = FALSE; + else if (sm_strcasecmp(BlankEnvelope.e_bodytype, "7BIT") == 0) + SevenBitInput = true; + else if (sm_strcasecmp(BlankEnvelope.e_bodytype, "8BITMIME") == 0) + SevenBitInput = false; else { - usrerr("Illegal body type %s", CurEnv->e_bodytype); - CurEnv->e_bodytype = NULL; + usrerr("Illegal body type %s", BlankEnvelope.e_bodytype); + BlankEnvelope.e_bodytype = NULL; } /* tweak default DSN notifications */ @@ -1188,50 +1270,148 @@ main(argc, argv, envp) if (ConfigLevel > MAXCONFIGLEVEL) { syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)", - ConfigLevel, Version, MAXCONFIGLEVEL); + ConfigLevel, Version, MAXCONFIGLEVEL); } /* need MCI cache to have persistence */ if (HostStatDir != NULL && MaxMciCache == 0) { HostStatDir = NULL; - printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n"); } /* need HostStatusDir in order to have SingleThreadDelivery */ if (SingleThreadDelivery && HostStatDir == NULL) { - SingleThreadDelivery = FALSE; - printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n"); + SingleThreadDelivery = false; + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: HostStatusDirectory required for SingleThreadDelivery\n"); } /* check for permissions */ - if ((OpMode == MD_DAEMON || - OpMode == MD_FGDAEMON || - OpMode == MD_PURGESTAT) && - RealUid != 0 && + if (RealUid != 0 && RealUid != TrustedUid) { - if (LogLevel > 1) - sm_syslog(LOG_ALERT, NOQID, - "user %d attempted to %s", - RealUid, - OpMode != MD_PURGESTAT ? "run daemon" - : "purge host status"); - usrerr("Permission denied"); - finis(FALSE, EX_USAGE); - } - if (OpMode == MD_INITALIAS && - RealUid != 0 && - RealUid != TrustedUid && - !wordinclass(RealUserName, 't')) - { - if (LogLevel > 1) - sm_syslog(LOG_ALERT, NOQID, - "user %d attempted to rebuild the alias map", - RealUid); - usrerr("Permission denied"); - finis(FALSE, EX_USAGE); + char *action = NULL; + + switch (OpMode) + { + case MD_QUEUERUN: + /* Normal users can do a single queue run */ + if (QueueIntvl == 0) + break; + + /* but not persistent queue runners */ + if (action == NULL) + action = "start a queue runner daemon"; + /* FALLTHROUGH */ + + case MD_PURGESTAT: + if (action == NULL) + action = "purge host status"; + /* FALLTHROUGH */ + + case MD_DAEMON: + case MD_FGDAEMON: + if (action == NULL) + action = "run daemon"; + + if (tTd(65, 1)) + sm_dprintf("Deny user %d attempt to %s\n", + (int) RealUid, action); + + if (LogLevel > 1) + sm_syslog(LOG_ALERT, NOQID, + "user %d attempted to %s", + (int) RealUid, action); + usrerr("Permission denied (real uid not trusted)"); + finis(false, EX_USAGE); + /* NOTREACHED */ + break; + + case MD_VERIFY: + if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags)) + { + /* + ** If -bv and RestrictExpand, + ** drop privs to prevent normal + ** users from reading private + ** aliases/forwards/:include:s + */ + + if (tTd(65, 1)) + sm_dprintf("Drop privs for user %d attempt to expand (RestrictExpand)\n", + (int) RealUid); + + dp = drop_privileges(true); + + /* Fake address safety */ + if (tTd(65, 1)) + sm_dprintf("Faking DontBlameSendmail=NonRootSafeAddr\n"); + setbitn(DBS_NONROOTSAFEADDR, DontBlameSendmail); + + if (dp != EX_OK) + { + if (tTd(65, 1)) + sm_dprintf("Failed to drop privs for user %d attempt to expand, exiting\n", + (int) RealUid); + CurEnv->e_id = NULL; + finis(true, dp); + /* NOTREACHED */ + } + } + break; + + case MD_TEST: + case MD_PRINT: + case MD_PRINTNQE: + case MD_FREEZE: + case MD_HOSTSTAT: + /* Nothing special to check */ + break; + + case MD_INITALIAS: + if (!wordinclass(RealUserName, 't')) + { + if (tTd(65, 1)) + sm_dprintf("Deny user %d attempt to rebuild the alias map\n", + (int) RealUid); + if (LogLevel > 1) + sm_syslog(LOG_ALERT, NOQID, + "user %d attempted to rebuild the alias map", + (int) RealUid); + usrerr("Permission denied (real uid not trusted)"); + finis(false, EX_USAGE); + /* NOTREACHED */ + } + if (UseMSP) + { + usrerr("User %d cannot rebuild aliases in mail submission program", + (int) RealUid); + finis(false, EX_USAGE); + /* NOTREACHED */ + } + /* FALLTHROUGH */ + + default: + if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags) && + Verbose != 0) + { + /* + ** If -v and RestrictExpand, reset + ** Verbose to prevent normal users + ** from seeing the expansion of + ** aliases/forwards/:include:s + */ + + if (tTd(65, 1)) + sm_dprintf("Dropping verbosity for user %d (RestrictExpand)\n", + (int) RealUid); + Verbose = 0; + } + break; + } } if (MeToo) @@ -1244,48 +1424,49 @@ main(argc, argv, envp) HostStatDir = NULL; if (Verbose == 0) Verbose = 2; - CurEnv->e_errormode = EM_PRINT; - HoldErrs = FALSE; + BlankEnvelope.e_errormode = EM_PRINT; + HoldErrs = false; break; case MD_VERIFY: - CurEnv->e_errormode = EM_PRINT; - HoldErrs = FALSE; - + BlankEnvelope.e_errormode = EM_PRINT; + HoldErrs = false; /* arrange to exit cleanly on hangup signal */ - if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) - (void) setsignal(SIGHUP, intsig); + if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) + (void) sm_signal(SIGHUP, intsig); + if (geteuid() != 0) + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Notice: -bv may give misleading output for non-privileged user\n"); break; case MD_FGDAEMON: - run_in_foreground = TRUE; - OpMode = MD_DAEMON; + run_in_foreground = true; + set_op_mode(MD_DAEMON); /* FALLTHROUGH */ case MD_DAEMON: - vendor_daemon_setup(CurEnv); + vendor_daemon_setup(&BlankEnvelope); /* remove things that don't make sense in daemon mode */ FullName = NULL; - GrabTo = FALSE; + GrabTo = false; /* arrange to restart on hangup signal */ if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/') sm_syslog(LOG_WARNING, NOQID, "daemon invoked without full pathname; kill -1 won't work"); - (void) setsignal(SIGTERM, term_daemon); break; case MD_INITALIAS: Verbose = 2; - CurEnv->e_errormode = EM_PRINT; - HoldErrs = FALSE; + BlankEnvelope.e_errormode = EM_PRINT; + HoldErrs = false; /* FALLTHROUGH */ default: /* arrange to exit cleanly on hangup signal */ - if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) - (void) setsignal(SIGHUP, intsig); + if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) + (void) sm_signal(SIGHUP, intsig); break; } @@ -1297,7 +1478,7 @@ main(argc, argv, envp) /* full names can't have newlines */ if (strchr(FullName, '\n') != NULL) { - full = newstr(denlstring(FullName, TRUE, TRUE)); + full = newstr(denlstring(FullName, true, true)); FullName = full; } @@ -1310,9 +1491,9 @@ main(argc, argv, envp) ** the name portion of the address. */ - FullName = addquotes(FullName); + FullName = addquotes(FullName, NULL); if (full != NULL) - sm_free(full); + sm_free(full); /* XXX */ } } @@ -1320,10 +1501,10 @@ main(argc, argv, envp) if (Verbose) { /* turn off noconnect option */ - setoption('c', "F", TRUE, FALSE, CurEnv); + setoption('c', "F", true, false, &BlankEnvelope); /* turn on interactive delivery */ - setoption('d', "", TRUE, FALSE, CurEnv); + setoption('d', "", true, false, &BlankEnvelope); } #ifdef VENDOR_CODE @@ -1343,19 +1524,21 @@ main(argc, argv, envp) } if (ConfigLevel < 3) - UseErrorsTo = TRUE; + UseErrorsTo = true; /* set options that were previous macros */ if (SmtpGreeting == NULL) { - if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL) + if (ConfigLevel < 7 && + (p = macvalue('e', &BlankEnvelope)) != NULL) SmtpGreeting = newstr(p); else SmtpGreeting = "\201j Sendmail \201v ready at \201b"; } if (UnixFromLine == NULL) { - if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL) + if (ConfigLevel < 7 && + (p = macvalue('l', &BlankEnvelope)) != NULL) UnixFromLine = newstr(p); else UnixFromLine = "From \201g \201d"; @@ -1363,11 +1546,11 @@ main(argc, argv, envp) SmtpError[0] = '\0'; /* our name for SMTP codes */ - expand("\201j", jbuf, sizeof jbuf, CurEnv); + expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope); if (jbuf[0] == '\0') - MyHostName = newstr("localhost"); + PSTRSET(MyHostName, "localhost"); else - MyHostName = jbuf; + PSTRSET(MyHostName, jbuf); if (strchr(MyHostName, '.') == NULL) message("WARNING: local host name (%s) is not qualified; fix $j in config file", MyHostName); @@ -1375,6 +1558,13 @@ main(argc, argv, envp) /* make certain that this name is part of the $=w class */ setclass('w', MyHostName); + /* fill in the structure of the *default* queue */ + st = stab("mqueue", ST_QUEUE, ST_FIND); + if (st == NULL) + syserr("No default queue (mqueue) defined"); + else + set_def_queueval(st->s_quegrp, true); + /* the indices of built-in mailers */ st = stab("local", ST_MAILER, ST_FIND); if (st != NULL) @@ -1454,29 +1644,28 @@ main(argc, argv, envp) #endif /* USE_B_CLASS */ /* MIME headers which have fields to check for overflow */ - setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-disposition"); - setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-type"); + setclass(macid("{checkMIMEFieldHeaders}"), "content-disposition"); + setclass(macid("{checkMIMEFieldHeaders}"), "content-type"); /* MIME headers to check for length overflow */ - setclass(macid("{checkMIMETextHeaders}", NULL), "content-description"); + setclass(macid("{checkMIMETextHeaders}"), "content-description"); /* MIME headers to check for overflow and rebalance */ - setclass(macid("{checkMIMEHeaders}", NULL), "content-disposition"); - setclass(macid("{checkMIMEHeaders}", NULL), "content-id"); - setclass(macid("{checkMIMEHeaders}", NULL), "content-transfer-encoding"); - setclass(macid("{checkMIMEHeaders}", NULL), "content-type"); - setclass(macid("{checkMIMEHeaders}", NULL), "mime-version"); + setclass(macid("{checkMIMEHeaders}"), "content-disposition"); + setclass(macid("{checkMIMEHeaders}"), "content-id"); + setclass(macid("{checkMIMEHeaders}"), "content-transfer-encoding"); + setclass(macid("{checkMIMEHeaders}"), "content-type"); + setclass(macid("{checkMIMEHeaders}"), "mime-version"); /* Macros to save in the qf file -- don't remove any */ - setclass(macid("{persistentMacros}", NULL), "r"); - setclass(macid("{persistentMacros}", NULL), "s"); - setclass(macid("{persistentMacros}", NULL), "_"); - setclass(macid("{persistentMacros}", NULL), "{if_addr}"); - setclass(macid("{persistentMacros}", NULL), "{daemon_flags}"); - setclass(macid("{persistentMacros}", NULL), "{client_flags}"); + setclass(macid("{persistentMacros}"), "r"); + setclass(macid("{persistentMacros}"), "s"); + setclass(macid("{persistentMacros}"), "_"); + setclass(macid("{persistentMacros}"), "{if_addr}"); + setclass(macid("{persistentMacros}"), "{daemon_flags}"); /* operate in queue directory */ - if (QueueDir == NULL) + if (QueueDir == NULL || *QueueDir == '\0') { if (OpMode != MD_TEST) { @@ -1486,29 +1675,22 @@ main(argc, argv, envp) } else { - /* - ** If multiple queues wildcarded, use one for - ** the daemon's home. Note that this preconditions - ** a wildcarded QueueDir to a real pathname. - */ - if (OpMode != MD_TEST) - multiqueue_cache(); + setup_queues(OpMode == MD_DAEMON); } /* check host status directory for validity */ - if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE)) + if (HostStatDir != NULL && !path_is_dir(HostStatDir, false)) { /* cannot use this value */ if (tTd(0, 2)) - dprintf("Cannot use HostStatusDirectory = %s: %s\n", - HostStatDir, errstring(errno)); + sm_dprintf("Cannot use HostStatusDirectory = %s: %s\n", + HostStatDir, sm_errstring(errno)); HostStatDir = NULL; } -#if QUEUE - if (OpMode == MD_QUEUERUN && RealUid != 0 && - bitset(PRIV_RESTRICTQRUN, PrivacyFlags)) + if (OpMode == MD_QUEUERUN && + RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags)) { struct stat stbuf; @@ -1519,21 +1701,33 @@ main(argc, argv, envp) { /* nope, really a botch */ usrerr("You do not have permission to process the queue"); - finis(FALSE, EX_NOPERM); + finis(false, EX_NOPERM); + /* NOTREACHED */ } } -#endif /* QUEUE */ -#if _FFR_MILTER +#if MILTER /* sanity checks on milter filters */ if (OpMode == MD_DAEMON || OpMode == MD_SMTP) - milter_parse_list(InputFilterList, InputFilters, MAXFILTERS); -#endif /* _FFR_MILTER */ - + { + milter_config(InputFilterList, InputFilters, MAXFILTERS); +# if _FFR_MILTER_PERDAEMON + setup_daemon_milters(); +# endif /* _FFR_MILTER_PERDAEMON */ + } +#endif /* MILTER */ /* if we've had errors so far, exit now */ if (ExitStat != EX_OK && OpMode != MD_TEST) - finis(FALSE, ExitStat); + { + finis(false, ExitStat); + /* NOTREACHED */ + } + +#if SASL + /* sendmail specific SASL initialization */ + sm_sasl_init(); +#endif /* SASL */ #if XDEBUG checkfd012("before main() initmaps"); @@ -1547,43 +1741,54 @@ main(argc, argv, envp) { case MD_PRINT: /* print the queue */ -#if QUEUE - dropenvelope(CurEnv, TRUE); - (void) setsignal(SIGPIPE, quiesce); + dropenvelope(&BlankEnvelope, true, false); + (void) sm_signal(SIGPIPE, sigpipe); printqueue(); - finis(FALSE, EX_OK); -#else /* QUEUE */ - usrerr("No queue to print"); - finis(FALSE, EX_UNAVAILABLE); -#endif /* QUEUE */ + finis(false, EX_OK); + /* NOTREACHED */ + break; + + case MD_PRINTNQE: + /* print number of entries in queue */ + dropenvelope(&BlankEnvelope, true, false); + (void) sm_signal(SIGPIPE, sigpipe); + printnqe(smioout, NULL); + finis(false, EX_OK); + /* NOTREACHED */ break; case MD_HOSTSTAT: - (void) setsignal(SIGPIPE, quiesce); + (void) sm_signal(SIGPIPE, sigpipe); (void) mci_traverse_persistent(mci_print_persistent, NULL); - finis(FALSE, EX_OK); + finis(false, EX_OK); + /* NOTREACHED */ break; case MD_PURGESTAT: (void) mci_traverse_persistent(mci_purge_persistent, NULL); - finis(FALSE, EX_OK); + finis(false, EX_OK); + /* NOTREACHED */ break; case MD_INITALIAS: /* initialize maps */ initmaps(); - finis(FALSE, ExitStat); + finis(false, ExitStat); + /* NOTREACHED */ break; case MD_SMTP: case MD_DAEMON: /* reset DSN parameters */ DefaultNotify = QPINGONFAILURE|QPINGONDELAY; - define(macid("{dsn_notify}", NULL), NULL, CurEnv); - CurEnv->e_envid = NULL; - define(macid("{dsn_envid}", NULL), NULL, CurEnv); - CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN); - define(macid("{dsn_ret}", NULL), NULL, CurEnv); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{dsn_notify}"), NULL); + BlankEnvelope.e_envid = NULL; + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{dsn_envid}"), NULL); + BlankEnvelope.e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{dsn_ret}"), NULL); /* don't open maps for daemon -- done below in child */ break; @@ -1605,7 +1810,8 @@ main(argc, argv, envp) ** Switch to the main envelope. */ - CurEnv = newenvelope(&MainEnvelope, CurEnv); + CurEnv = newenvelope(&MainEnvelope, &BlankEnvelope, + sm_rpool_new_x(NULL)); MainEnvelope.e_flags = BlankEnvelope.e_flags; /* @@ -1614,79 +1820,190 @@ main(argc, argv, envp) if (OpMode == MD_TEST) { - char buf[MAXLINE]; - -#if _FFR_TESTMODE_DROP_PRIVS - dp = drop_privileges(TRUE); - if (dp != EX_OK) - { - CurEnv->e_id = NULL; - finis(TRUE, dp); - } -#endif /* _FFR_TESTMODE_DROP_PRIVS */ - - if (isatty(fileno(stdin))) + if (isatty(sm_io_getinfo(smioin, SM_IO_WHAT_FD, NULL))) Verbose = 2; if (Verbose) { - printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n"); - printf("Enter <ruleset> <address>\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Enter <ruleset> <address>\n"); } - if (setjmp(TopFrame) > 0) - printf("\n"); - (void) setsignal(SIGINT, intindebug); for (;;) { - if (Verbose == 2) - printf("> "); - (void) fflush(stdout); - if (fgets(buf, sizeof buf, stdin) == NULL) - testmodeline("/quit", CurEnv); - p = strchr(buf, '\n'); - if (p != NULL) - *p = '\0'; - if (Verbose < 2) - printf("> %s\n", buf); - testmodeline(buf, CurEnv); + SM_TRY + { + (void) sm_signal(SIGINT, intindebug); + if (Verbose == 2) + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "> "); + (void) sm_io_flush(smioout, SM_TIME_DEFAULT); + if (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, + sizeof buf) == NULL) + testmodeline("/quit", &MainEnvelope); + p = strchr(buf, '\n'); + if (p != NULL) + *p = '\0'; + if (Verbose < 2) + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "> %s\n", buf); + testmodeline(buf, &MainEnvelope); + } + SM_EXCEPT(exc, "[!F]*") + { + /* + ** 8.10 just prints \n on interrupt. + ** I'm printing the exception here in case + ** sendmail is extended to raise additional + ** exceptions in this context. + */ + + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "\n"); + sm_exc_print(exc, smioout); + } + SM_END_TRY } } -#if SMTP -# if STARTTLS - tls_ok = init_tls_library(); -# endif /* STARTTLS */ -#endif /* SMTP */ +#if STARTTLS + tls_ok = true; + if (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER) + { + /* check whether STARTTLS is turned off for the client */ + if (chkclientmodifiers(D_NOTLS)) + tls_ok = false; + } + else if (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON || + OpMode == MD_SMTP) + { + /* check whether STARTTLS is turned off for the server */ + if (chkdaemonmodifiers(D_NOTLS)) + tls_ok = false; + } + else /* other modes don't need STARTTLS */ + tls_ok = false; + + if (tls_ok) + { + /* basic TLS initialization */ + tls_ok = init_tls_library(); + } + + if (!tls_ok && (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER)) + { + /* disable TLS for client */ + setclttls(false); + } +#endif /* STARTTLS */ -#if QUEUE /* ** If collecting stuff from the queue, go start doing that. */ if (OpMode == MD_QUEUERUN && QueueIntvl == 0) { -# if SMTP -# if STARTTLS - if (tls_ok - ) + int qgrp = NOQGRP; + pid_t pid = -1; + +#if STARTTLS + /* init TLS for client, ignore result for now */ + (void) initclttls(tls_ok); +#endif /* STARTTLS */ + + if (runqueuegroup != NULL) + { + /* Selecting a particular queue group to run */ + qgrp = name2qid(runqueuegroup); + if (qgrp == NOQGRP) + { + usrerr("Queue group %s unknown", + runqueuegroup); + finis(true, ExitStat); + /* NOTREACHED */ + } + } + + /* + ** The parent process of the caller of runqueue() needs + ** to stay around for a possible SIGTERM. The SIGTERM will + ** tell this process that all of the queue runners children + ** need to be sent SIGTERM as well. At the same time, we + ** want to return control to the command line. So we do an + ** extra fork(). + */ + + if (Verbose || foregroundqueue || (pid = fork()) <= 0) { - /* init TLS for client, ignore result for now */ - (void) initclttls(); + /* + ** If the fork() failed we should still try to do + ** the queue run. If it succeeded then the child + ** is going to start the run and wait for all + ** of the children to finish. + */ + + if (pid == 0) + { + /* Reset global flags */ + RestartRequest = NULL; + ShutdownRequest = NULL; + PendingSignal = 0; + + /* disconnect from terminal */ + disconnect(2, CurEnv); + } + + CurrentPid = getpid(); + if (qgrp != NOQGRP) + { + /* + ** To run a specific queue group mark it to + ** be run, select the work group it's in and + ** increment the work counter. + */ + + runqueueevent(qgrp); + (void) run_work_group(Queue[qgrp]->qg_wgrp, + false, Verbose, + queuepersistent, false); + } + else + (void) runqueue(false, Verbose, + queuepersistent, true); + + /* set the title to make it easier to find */ + sm_setproctitle(true, CurEnv, "Queue control"); + (void) sm_signal(SIGCHLD, SIG_DFL); + while (CurChildren > 0) + { + int status; + pid_t ret; + + while ((ret = sm_wait(&status)) <= 0) + continue; + + /* Only drop when a child gives status */ + if (WIFSTOPPED(status)) + continue; + + (void) proc_list_drop(ret, NULL, NULL); + } } -# endif /* STARTTLS */ -# endif /* SMTP */ - (void) runqueue(FALSE, Verbose); - finis(TRUE, ExitStat); + finis(true, ExitStat); + /* NOTREACHED */ } -#endif /* QUEUE */ # if SASL if (OpMode == MD_SMTP || OpMode == MD_DAEMON) { - /* give a syserr or just disable AUTH ? */ - if ((i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK) + /* check whether AUTH is turned off for the server */ + if (!chkdaemonmodifiers(D_NOAUTH) && + (i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK) syserr("!sasl_server_init failed! [%s]", - sasl_errstring(i, NULL, NULL)); + sasl_errstring(i, NULL, NULL)); } # endif /* SASL */ @@ -1710,80 +2027,206 @@ main(argc, argv, envp) if (i < 0) syserr("daemon: cannot fork"); if (i != 0) - finis(FALSE, EX_OK); + { + finis(false, EX_OK); + /* NOTREACHED */ + } + + /* + ** Initialize exception stack and default exception + ** handler for child process. + */ + + /* Reset global flags */ + RestartRequest = NULL; + RestartWorkGroup = false; + ShutdownRequest = NULL; + PendingSignal = 0; + CurrentPid = getpid(); + + sm_exc_newthread(fatal_error); /* disconnect from our controlling tty */ - disconnect(2, CurEnv); + disconnect(2, &MainEnvelope); } dtype[0] = '\0'; if (OpMode == MD_DAEMON) - (void) strlcat(dtype, "+SMTP", sizeof dtype); + { + (void) sm_strlcat(dtype, "+SMTP", sizeof dtype); + DaemonPid = CurrentPid; + } if (QueueIntvl != 0) { - (void) strlcat(dtype, "+queueing@", sizeof dtype); - (void) strlcat(dtype, pintvl(QueueIntvl, TRUE), - sizeof dtype); + (void) sm_strlcat2(dtype, + queuepersistent + ? "+persistent-queueing@" + : "+queueing@", + pintvl(QueueIntvl, true), + sizeof dtype); } if (tTd(0, 1)) - (void) strlcat(dtype, "+debugging", sizeof dtype); + (void) sm_strlcat(dtype, "+debugging", sizeof dtype); sm_syslog(LOG_INFO, NOQID, "starting daemon (%s): %s", Version, dtype + 1); -#ifdef XLA +#if XLA xla_create_file(); #endif /* XLA */ /* save daemon type in a macro for possible PidFile use */ - define(macid("{daemon_info}", NULL), - newstr(dtype + 1), &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_TEMP, + macid("{daemon_info}"), dtype + 1); /* save queue interval in a macro for possible PidFile use */ - define(macid("{queue_interval}", NULL), - newstr(pintvl(QueueIntvl, TRUE)), CurEnv); + macdefine(&MainEnvelope.e_macro, A_TEMP, + macid("{queue_interval}"), pintvl(QueueIntvl, true)); + + /* workaround: can't seem to release the signal in the parent */ + (void) sm_signal(SIGHUP, sighup); + (void) sm_releasesignal(SIGHUP); + (void) sm_signal(SIGTERM, sigterm); -#if QUEUE if (QueueIntvl != 0) { - (void) runqueue(TRUE, FALSE); + (void) runqueue(true, false, queuepersistent, true); + + /* + ** If queuepersistent but not in daemon mode then + ** we're going to do the queue runner monitoring here. + ** If in daemon mode then the monitoring will happen + ** elsewhere. + */ + + if (OpMode != MD_DAEMON && queuepersistent) + { + /* set the title to make it easier to find */ + sm_setproctitle(true, CurEnv, "Queue control"); + (void) sm_signal(SIGCHLD, SIG_DFL); + while (CurChildren > 0) + { + int status; + pid_t ret; + int group; + + if (ShutdownRequest != NULL) + shutdown_daemon(); + else if (RestartRequest != NULL) + restart_daemon(); + else if (RestartWorkGroup) + restart_marked_work_groups(); + + while ((ret = sm_wait(&status)) <= 0) + continue; + + if (WIFSTOPPED(status)) + continue; + + /* Probe only on a child status */ + (void) proc_list_drop(ret, NULL, + &group); + + if (WIFSIGNALED(status)) + { + if (WCOREDUMP(status)) + { + sm_syslog(LOG_ERR, NOQID, + "persistent queue runner=%d core dumped, signal=%d", + group, WTERMSIG(status)); + + /* don't restart this one */ + continue; + } + + sm_syslog(LOG_ERR, NOQID, + "persistent queue runner=%d died, signal=%d", + group, WTERMSIG(status)); + } + + /* + ** When debugging active, don't + ** restart the persistent queues. + ** But do log this as info. + */ + + if (sm_debug_active(&DebugNoPRestart, + 1)) + { + sm_syslog(LOG_DEBUG, NOQID, + "persistent queue runner=%d, exited", + group); + continue; + } + + /* restart this persistent runner */ + mark_work_group_restart(group, status); + } + finis(true, ExitStat); + /* NOTREACHED */ + } + if (OpMode != MD_DAEMON) { - /* write the pid to file */ - log_sendmail_pid(CurEnv); - (void) setsignal(SIGTERM, term_daemon); + char qtype[200]; + + /* + ** Write the pid to file + ** XXX Overwrites sendmail.pid + */ + + log_sendmail_pid(&MainEnvelope); + + /* set the title to make it easier to find */ + qtype[0] = '\0'; + (void) sm_strlcpyn(qtype, sizeof qtype, 4, + "Queue runner@", + pintvl(QueueIntvl, true), + " for ", + QueueDir); + sm_setproctitle(true, CurEnv, qtype); for (;;) { (void) pause(); if (ShutdownRequest != NULL) shutdown_daemon(); - else if (DoQueueRun) - (void) runqueue(TRUE, FALSE); + else if (RestartRequest != NULL) + restart_daemon(); + else if (RestartWorkGroup) + restart_marked_work_groups(); + + if (doqueuerun()) + (void) runqueue(true, false, + false, false); } } } -#endif /* QUEUE */ - dropenvelope(CurEnv, TRUE); + dropenvelope(&MainEnvelope, true, false); -#if DAEMON -# if STARTTLS +#if STARTTLS /* init TLS for server, ignore result for now */ - (void) initsrvtls(); -# endif /* STARTTLS */ - p_flags = getrequests(CurEnv); + (void) initsrvtls(tls_ok); +#endif /* STARTTLS */ +#if PROFILING + nextreq: +#endif /* PROFILING */ + p_flags = getrequests(&MainEnvelope); /* drop privileges */ - (void) drop_privileges(FALSE); - - /* at this point we are in a child: reset state */ - (void) newenvelope(CurEnv, CurEnv); + (void) drop_privileges(false); /* ** Get authentication data + ** Set _ macro in BlankEnvelope before calling newenvelope(). */ - authinfo = getauthinfo(fileno(InChannel), &forged); - define('_', authinfo, &BlankEnvelope); -#endif /* DAEMON */ + authinfo = getauthinfo(sm_io_getinfo(InChannel, SM_IO_WHAT_FD, + NULL), &forged); + macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo); + + /* at this point we are in a child: reset state */ + sm_rpool_free(MainEnvelope.e_rpool); + (void) newenvelope(&MainEnvelope, &MainEnvelope, + sm_rpool_new_x(NULL)); } if (LogLevel > 9) @@ -1792,7 +2235,6 @@ main(argc, argv, envp) sm_syslog(LOG_INFO, NULL, "connect from %.100s", authinfo); } -#if SMTP /* ** If running SMTP protocol, start collecting and executing ** commands. This will never return. @@ -1810,91 +2252,98 @@ main(argc, argv, envp) { char ipbuf[103]; - (void) snprintf(ipbuf, sizeof ipbuf, "[%.100s]", - anynet_ntoa(&RealHostAddr)); - define(macid("{client_name}", NULL), - newstr(ipbuf), &BlankEnvelope); - define(macid("{client_resolve}", NULL), - "FORGED", &BlankEnvelope); + (void) sm_snprintf(ipbuf, sizeof ipbuf, "[%.100s]", + anynet_ntoa(&RealHostAddr)); + macdefine(&BlankEnvelope.e_macro, A_TEMP, + macid("{client_name}"), ipbuf); } else - define(macid("{client_name}", NULL), RealHostName, - &BlankEnvelope); - define(macid("{client_addr}", NULL), - newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope); - (void)sm_getla(&BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{client_name}"), RealHostName); + macdefine(&BlankEnvelope.e_macro, A_TEMP, + macid("{client_addr}"), anynet_ntoa(&RealHostAddr)); + sm_getla(); - switch(RealHostAddr.sa.sa_family) + switch (RealHostAddr.sa.sa_family) { -# if NETINET +#if NETINET case AF_INET: - (void) snprintf(pbuf, sizeof pbuf, "%d", - RealHostAddr.sin.sin_port); + (void) sm_snprintf(pbuf, sizeof pbuf, "%d", + RealHostAddr.sin.sin_port); break; -# endif /* NETINET */ -# if NETINET6 +#endif /* NETINET */ +#if NETINET6 case AF_INET6: - (void) snprintf(pbuf, sizeof pbuf, "%d", - RealHostAddr.sin6.sin6_port); + (void) sm_snprintf(pbuf, sizeof pbuf, "%d", + RealHostAddr.sin6.sin6_port); break; -# endif /* NETINET6 */ +#endif /* NETINET6 */ default: - (void) snprintf(pbuf, sizeof pbuf, "0"); + (void) sm_snprintf(pbuf, sizeof pbuf, "0"); break; } - define(macid("{client_port}", NULL), - newstr(pbuf), &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_TEMP, + macid("{client_port}"), pbuf); if (OpMode == MD_DAEMON) { /* validate the connection */ - HoldErrs = TRUE; + HoldErrs = true; nullserver = validate_connection(&RealHostAddr, - RealHostName, CurEnv); - HoldErrs = FALSE; + RealHostName, + &MainEnvelope); + HoldErrs = false; } else if (p_flags == NULL) { p_flags = (BITMAP256 *) xalloc(sizeof *p_flags); clrbitmap(p_flags); } -# if STARTTLS +#if STARTTLS if (OpMode == MD_SMTP) - (void) initsrvtls(); -# endif /* STARTTLS */ - - - smtp(nullserver, *p_flags, CurEnv); + (void) initsrvtls(tls_ok); +#endif /* STARTTLS */ + + /* turn off profiling */ + SM_PROF(1); + smtp(nullserver, *p_flags, &MainEnvelope); +#if PROFILING + /* turn off profiling */ + SM_PROF(0); + if (OpMode == MD_DAEMON) + goto nextreq; +#endif /* PROFILING */ } -#endif /* SMTP */ - clearenvelope(CurEnv, FALSE); + sm_rpool_free(MainEnvelope.e_rpool); + clearenvelope(&MainEnvelope, false, sm_rpool_new_x(NULL)); if (OpMode == MD_VERIFY) { - set_delivery_mode(SM_VERIFY, CurEnv); + set_delivery_mode(SM_VERIFY, &MainEnvelope); PostMasterCopy = NULL; } else { /* interactive -- all errors are global */ - CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER; + MainEnvelope.e_flags |= EF_GLOBALERRS|EF_LOGSENDER; } /* ** Do basic system initialization and set the sender */ - initsys(CurEnv); - define(macid("{ntries}", NULL), "0", CurEnv); - setsender(from, CurEnv, NULL, '\0', FALSE); + initsys(&MainEnvelope); + macdefine(&MainEnvelope.e_macro, A_PERM, macid("{ntries}"), "0"); + macdefine(&MainEnvelope.e_macro, A_PERM, macid("{nrcpts}"), "0"); + setsender(from, &MainEnvelope, NULL, '\0', false); if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') && - (!bitnset(M_LOCALMAILER, CurEnv->e_from.q_mailer->m_flags) || - strcmp(CurEnv->e_from.q_user, RealUserName) != 0)) + (!bitnset(M_LOCALMAILER, MainEnvelope.e_from.q_mailer->m_flags) || + strcmp(MainEnvelope.e_from.q_user, RealUserName) != 0)) { - auth_warning(CurEnv, "%s set sender to %s using -%c", - RealUserName, from, warn_f_flag); + auth_warning(&MainEnvelope, "%s set sender to %s using -%c", + RealUserName, from, warn_f_flag); #if SASL - auth = FALSE; + auth = false; #endif /* SASL */ } if (auth) @@ -1902,46 +2351,50 @@ main(argc, argv, envp) char *fv; /* set the initial sender for AUTH= to $f@$j */ - fv = macvalue('f', CurEnv); + fv = macvalue('f', &MainEnvelope); if (fv == NULL || *fv == '\0') - CurEnv->e_auth_param = NULL; + MainEnvelope.e_auth_param = NULL; else { if (strchr(fv, '@') == NULL) { - i = strlen(fv) + strlen(macvalue('j', CurEnv)) - + 2; - p = xalloc(i); - (void) snprintf(p, i, "%s@%s", fv, - macvalue('j', CurEnv)); + i = strlen(fv) + strlen(macvalue('j', + &MainEnvelope)) + 2; + p = sm_malloc_x(i); + (void) sm_strlcpyn(p, i, 3, fv, "@", + macvalue('j', + &MainEnvelope)); } else - p = newstr(fv); - CurEnv->e_auth_param = newstr(xtextify(p, "=")); + p = sm_strdup_x(fv); + MainEnvelope.e_auth_param = sm_rpool_strdup_x(MainEnvelope.e_rpool, + xtextify(p, "=")); + sm_free(p); /* XXX */ } } - if (macvalue('s', CurEnv) == NULL) - define('s', RealHostName, CurEnv); + if (macvalue('s', &MainEnvelope) == NULL) + macdefine(&MainEnvelope.e_macro, A_PERM, 's', RealHostName); if (*av == NULL && !GrabTo) { - CurEnv->e_to = NULL; - CurEnv->e_flags |= EF_GLOBALERRS; - HoldErrs = FALSE; - SuperSafe = FALSE; + MainEnvelope.e_to = NULL; + MainEnvelope.e_flags |= EF_GLOBALERRS; + HoldErrs = false; + SuperSafe = SAFE_NO; usrerr("Recipient names must be specified"); /* collect body for UUCP return */ if (OpMode != MD_VERIFY) - collect(InChannel, FALSE, NULL, CurEnv); - finis(TRUE, EX_USAGE); + collect(InChannel, false, NULL, &MainEnvelope); + finis(true, EX_USAGE); + /* NOTREACHED */ } /* ** Scan argv and deliver the message to everyone. */ - sendtoargv(av, CurEnv); + sendtoargv(av, &MainEnvelope); /* if we have had errors sofar, arrange a meaningful exit stat */ if (Errors > 0 && ExitStat == EX_OK) @@ -1957,7 +2410,7 @@ main(argc, argv, envp) { ADDRESS *q; - for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next) + for (q = MainEnvelope.e_sendqueue; q != NULL; q = q->q_next) q->q_state = QS_REMOVED; } #endif /* _FFR_FIX_DASHT */ @@ -1966,122 +2419,95 @@ main(argc, argv, envp) ** Read the input mail. */ - CurEnv->e_to = NULL; + MainEnvelope.e_to = NULL; if (OpMode != MD_VERIFY || GrabTo) { int savederrors = Errors; - long savedflags = CurEnv->e_flags & EF_FATALERRS; + long savedflags = MainEnvelope.e_flags & EF_FATALERRS; - CurEnv->e_flags |= EF_GLOBALERRS; - CurEnv->e_flags &= ~EF_FATALERRS; + MainEnvelope.e_flags |= EF_GLOBALERRS; + MainEnvelope.e_flags &= ~EF_FATALERRS; Errors = 0; buffer_errors(); - collect(InChannel, FALSE, NULL, CurEnv); + collect(InChannel, false, NULL, &MainEnvelope); /* header checks failed */ if (Errors > 0) { - /* Log who the mail would have gone to */ - if (LogLevel > 8 && CurEnv->e_message != NULL && - !GrabTo) + if (!GrabTo) { - ADDRESS *a; - - for (a = CurEnv->e_sendqueue; - a != NULL; - a = a->q_next) - { - if (!QS_IS_UNDELIVERED(a->q_state)) - continue; - - CurEnv->e_to = a->q_paddr; - logdelivery(NULL, NULL, NULL, - CurEnv->e_message, - NULL, (time_t) 0, CurEnv); - } - CurEnv->e_to = NULL; + /* Log who the mail would have gone to */ + logundelrcpts(&MainEnvelope, + MainEnvelope.e_message, + 8, false); } - flush_errors(TRUE); - finis(TRUE, ExitStat); + flush_errors(true); + finis(true, ExitStat); /* NOTREACHED */ return -1; } /* bail out if message too large */ - if (bitset(EF_CLRQUEUE, CurEnv->e_flags)) + if (bitset(EF_CLRQUEUE, MainEnvelope.e_flags)) { - finis(TRUE, ExitStat != EX_OK ? ExitStat : EX_DATAERR); + finis(true, ExitStat != EX_OK ? ExitStat : EX_DATAERR); /* NOTREACHED */ return -1; } Errors = savederrors; - CurEnv->e_flags |= savedflags; + MainEnvelope.e_flags |= savedflags; } errno = 0; if (tTd(1, 1)) - dprintf("From person = \"%s\"\n", CurEnv->e_from.q_paddr); + sm_dprintf("From person = \"%s\"\n", + MainEnvelope.e_from.q_paddr); /* ** Actually send everything. ** If verifying, just ack. */ - CurEnv->e_from.q_state = QS_SENDER; - if (tTd(1, 5)) + if (Errors == 0) + split_by_recipient(&MainEnvelope); + + /* make sure we deliver at least the first envelope */ + i = FastSplit > 0 ? 0 : -1; + for (e = &MainEnvelope; e != NULL; e = e->e_sibling, i++) { - dprintf("main: QS_SENDER "); - printaddr(&CurEnv->e_from, FALSE); - } - CurEnv->e_to = NULL; - CurrentLA = sm_getla(CurEnv); - GrabTo = FALSE; + ENVELOPE *next; + + e->e_from.q_state = QS_SENDER; + if (tTd(1, 5)) + { + sm_dprintf("main[%d]: QS_SENDER ", i); + printaddr(&e->e_from, false); + } + e->e_to = NULL; + sm_getla(); + GrabTo = false; #if NAMED_BIND - _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; - _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; + _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; + _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; #endif /* NAMED_BIND */ - sendall(CurEnv, SM_DEFAULT); + next = e->e_sibling; + e->e_sibling = NULL; + + /* after FastSplit envelopes: queue up */ + sendall(e, i >= FastSplit ? SM_QUEUE : SM_DEFAULT); + e->e_sibling = next; + } /* ** All done. ** Don't send return error message if in VERIFY mode. */ - finis(TRUE, ExitStat); + finis(true, ExitStat); /* NOTREACHED */ return ExitStat; } /* -** QUIESCE -- signal handler for SIGPIPE -** -** Parameters: -** sig -- incoming signal. -** -** Returns: -** none. -** -** Side Effects: -** Sets StopRequest which should cause the mailq/hoststatus -** display to stop. -** -** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD -** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE -** DOING. -*/ - -/* ARGSUSED */ -static SIGFUNC_DECL -quiesce(sig) - int sig; -{ - int save_errno = errno; - - FIX_SYSV_SIGNAL(sig, quiesce); - StopRequest = TRUE; - errno = save_errno; - return SIGFUNC_RETURN; -} -/* ** STOP_SENDMAIL -- Stop the running program ** ** Parameters: @@ -2102,41 +2528,6 @@ stop_sendmail() (void) setuid(RealUid); exit(EX_OK); } - -/* -** INTINDEBUG -- signal handler for SIGINT in -bt mode -** -** Parameters: -** sig -- incoming signal. -** -** Returns: -** none. -** -** Side Effects: -** longjmps back to test mode loop. -** -** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD -** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE -** DOING. -** -** XXX: More work is needed for this signal handler. -*/ - -/* ARGSUSED */ -static SIGFUNC_DECL -intindebug(sig) - int sig; -{ - int save_errno = errno; - - FIX_SYSV_SIGNAL(sig, intindebug); - errno = save_errno; - CHECK_CRITICAL(sig); - - errno = save_errno; - longjmp(TopFrame, 1); - return SIGFUNC_RETURN; -} /* ** FINIS -- Clean up and exit. ** @@ -2157,70 +2548,135 @@ finis(drop, exitstat) volatile int exitstat; { /* Still want to process new timeouts added below */ - clear_events(); - (void) releasesignal(SIGALRM); + sm_clear_events(); + (void) sm_releasesignal(SIGALRM); if (tTd(2, 1)) { - dprintf("\n====finis: stat %d e_id=%s e_flags=", - exitstat, - CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id); + sm_dprintf("\n====finis: stat %d e_id=%s e_flags=", + exitstat, + CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id); printenvflags(CurEnv); } if (tTd(2, 9)) - printopenfds(FALSE); + printopenfds(false); - /* if we fail in finis(), just exit */ - if (setjmp(TopFrame) != 0) - { - /* failed -- just give it up */ - goto forceexit; - } + SM_TRY + /* + ** Clean up. This might raise E:mta.quickabort + */ - /* clean up temp files */ - CurEnv->e_to = NULL; - if (drop) - { - if (CurEnv->e_id != NULL) - dropenvelope(CurEnv, TRUE); - else - poststats(StatFile); - } + /* clean up temp files */ + CurEnv->e_to = NULL; + if (drop) + { + if (CurEnv->e_id != NULL) + { + dropenvelope(CurEnv, true, false); + sm_rpool_free(CurEnv->e_rpool); + CurEnv->e_rpool = NULL; + } + else + poststats(StatFile); + } - /* flush any cached connections */ - mci_flush(TRUE, NULL); + /* flush any cached connections */ + mci_flush(true, NULL); - /* close maps belonging to this pid */ - closemaps(); + /* close maps belonging to this pid */ + closemaps(false); #if USERDB - /* close UserDatabase */ - _udbx_close(); + /* close UserDatabase */ + _udbx_close(); #endif /* USERDB */ -#ifdef XLA - /* clean up extended load average stuff */ - xla_all_end(); +#if SASL + stop_sasl_client(); +#endif /* SASL */ + +#if XLA + /* clean up extended load average stuff */ + xla_all_end(); #endif /* XLA */ - /* and exit */ - forceexit: - if (LogLevel > 78) - sm_syslog(LOG_DEBUG, CurEnv->e_id, - "finis, pid=%d", - (int) getpid()); - if (exitstat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET) - exitstat = EX_OK; + SM_FINALLY + /* + ** And exit. + */ - sync_queue_time(); + if (LogLevel > 78) + sm_syslog(LOG_DEBUG, CurEnv->e_id, "finis, pid=%d", + (int) CurrentPid); + if (exitstat == EX_TEMPFAIL || + CurEnv->e_errormode == EM_BERKNET) + exitstat = EX_OK; + + /* XXX clean up queues and related data structures */ + cleanup_queues(); +#if SM_CONF_SHM + cleanup_shm(DaemonPid == getpid()); +#endif /* SM_CONF_SHM */ + + /* reset uid for process accounting */ + endpwent(); + sm_mbdb_terminate(); + (void) setuid(RealUid); +#if SM_HEAP_CHECK + /* dump the heap, if we are checking for memory leaks */ + if (sm_debug_active(&SmHeapCheck, 2)) + sm_heap_report(smioout, + sm_debug_level(&SmHeapCheck) - 1); +#endif /* SM_HEAP_CHECK */ + if (sm_debug_active(&SmXtrapReport, 1)) + sm_dprintf("xtrap count = %d\n", SmXtrapCount); + exit(exitstat); + SM_END_TRY +} +/* +** INTINDEBUG -- signal handler for SIGINT in -bt mode +** +** Parameters: +** sig -- incoming signal. +** +** Returns: +** none. +** +** Side Effects: +** longjmps back to test mode loop. +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. +*/ - /* reset uid for process accounting */ - endpwent(); - (void) setuid(RealUid); - exit(exitstat); +/* Type of an exception generated on SIGINT during address test mode. */ +static const SM_EXC_TYPE_T EtypeInterrupt = +{ + SmExcTypeMagic, + "S:mta.interrupt", + "", + sm_etype_printf, + "interrupt", +}; + +/* ARGSUSED */ +static SIGFUNC_DECL +intindebug(sig) + int sig; +{ + int save_errno = errno; + + FIX_SYSV_SIGNAL(sig, intindebug); + errno = save_errno; + CHECK_CRITICAL(sig); + errno = save_errno; + sm_exc_raisenew_x(&EtypeInterrupt); + errno = save_errno; + return SIGFUNC_RETURN; } /* -** TERM_DEAMON -- SIGTERM handler for the daemon +** SIGTERM -- SIGTERM handler for the daemon ** ** Parameters: ** sig -- signal number. @@ -2239,51 +2695,75 @@ finis(drop, exitstat) /* ARGSUSED */ static SIGFUNC_DECL -term_daemon(sig) +sigterm(sig) int sig; { int save_errno = errno; - FIX_SYSV_SIGNAL(sig, term_daemon); + FIX_SYSV_SIGNAL(sig, sigterm); ShutdownRequest = "signal"; errno = save_errno; return SIGFUNC_RETURN; } /* -** SHUTDOWN_DAEMON -- Performs a clean shutdown of the daemon +** SIGHUP -- handle a SIGHUP signal ** ** Parameters: -** none. +** sig -- incoming signal. ** ** Returns: ** none. ** ** Side Effects: -** closes control socket, exits. +** Sets RestartRequest which should cause the daemon +** to restart. +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. */ -void -shutdown_daemon() +/* ARGSUSED */ +static SIGFUNC_DECL +sighup(sig) + int sig; { - char *reason; - - allsignals(TRUE); - - reason = ShutdownRequest; - ShutdownRequest = NULL; - PendingSignal = 0; + int save_errno = errno; - if (LogLevel > 79) - sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt (%s)", - reason == NULL ? "implicit call" : reason); + FIX_SYSV_SIGNAL(sig, sighup); + RestartRequest = "signal"; + errno = save_errno; + return SIGFUNC_RETURN; +} +/* +** SIGPIPE -- signal handler for SIGPIPE +** +** Parameters: +** sig -- incoming signal. +** +** Returns: +** none. +** +** Side Effects: +** Sets StopRequest which should cause the mailq/hoststatus +** display to stop. +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. +*/ - FileName = NULL; - closecontrolsocket(TRUE); -#ifdef XLA - xla_all_end(); -#endif /* XLA */ +/* ARGSUSED */ +static SIGFUNC_DECL +sigpipe(sig) + int sig; +{ + int save_errno = errno; - finis(FALSE, EX_OK); + FIX_SYSV_SIGNAL(sig, sigpipe); + StopRequest = true; + errno = save_errno; + return SIGFUNC_RETURN; } /* ** INTSIG -- clean up on interrupt @@ -2312,13 +2792,14 @@ SIGFUNC_DECL intsig(sig) int sig; { - bool drop = FALSE; + bool drop = false; int save_errno = errno; FIX_SYSV_SIGNAL(sig, intsig); errno = save_errno; CHECK_CRITICAL(sig); - allsignals(TRUE); + sm_allsignals(true); + if (sig != 0 && LogLevel > 79) sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt"); FileName = NULL; @@ -2344,89 +2825,15 @@ intsig(sig) for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next) q->q_state = QS_DONTSEND; - /* and don't try to deliver the partial message either */ - if (InChild) - ExitStat = EX_QUIT; - - drop = TRUE; + drop = true; } else if (OpMode != MD_TEST) - unlockqueue(CurEnv); - - finis(drop, EX_OK); -} -/* -** INITMACROS -- initialize the macro system -** -** This just involves defining some macros that are actually -** used internally as metasymbols to be themselves. -** -** Parameters: -** none. -** -** Returns: -** none. -** -** Side Effects: -** initializes several macros to be themselves. -*/ - -struct metamac MetaMacros[] = -{ - /* LHS pattern matching characters */ - { '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE }, - { '=', MATCHCLASS }, { '~', MATCHNCLASS }, - - /* these are RHS metasymbols */ - { '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER }, - { '>', CALLSUBR }, - - /* the conditional operations */ - { '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI }, - - /* the hostname lookup characters */ - { '[', HOSTBEGIN }, { ']', HOSTEND }, - { '(', LOOKUPBEGIN }, { ')', LOOKUPEND }, - - /* miscellaneous control characters */ - { '&', MACRODEXPAND }, - - { '\0', '\0' } -}; - -#define MACBINDING(name, mid) \ - stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \ - MacroName[mid] = name; - -void -initmacros(e) - register ENVELOPE *e; -{ - register struct metamac *m; - register int c; - char buf[5]; - extern char *MacroName[MAXMACROID + 1]; - - for (m = MetaMacros; m->metaname != '\0'; m++) - { - buf[0] = m->metaval; - buf[1] = '\0'; - define(m->metaname, newstr(buf), e); - } - buf[0] = MATCHREPL; - buf[2] = '\0'; - for (c = '0'; c <= '9'; c++) { - buf[1] = c; - define(c, newstr(buf), e); + unlockqueue(CurEnv); } - /* set defaults for some macros sendmail will use later */ - define('n', "MAILER-DAEMON", e); - - /* set up external names for some internal macros */ - MACBINDING("opMode", MID_OPMODE); - /*XXX should probably add equivalents for all short macros here XXX*/ + finis(drop, EX_OK); + /* NOTREACHED */ } /* ** DISCONNECT -- remove our connection with any foreground process @@ -2456,11 +2863,12 @@ disconnect(droplev, e) int fd; if (tTd(52, 1)) - dprintf("disconnect: In %d Out %d, e=%lx\n", - fileno(InChannel), fileno(OutChannel), (u_long) e); + sm_dprintf("disconnect: In %d Out %d, e=%p\n", + sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL), + sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL), e); if (tTd(52, 100)) { - dprintf("don't\n"); + sm_dprintf("don't\n"); return; } if (LogLevel > 93) @@ -2469,40 +2877,64 @@ disconnect(droplev, e) droplev); /* be sure we don't get nasty signals */ - (void) setsignal(SIGINT, SIG_IGN); - (void) setsignal(SIGQUIT, SIG_IGN); + (void) sm_signal(SIGINT, SIG_IGN); + (void) sm_signal(SIGQUIT, SIG_IGN); /* we can't communicate with our caller, so.... */ - HoldErrs = TRUE; + HoldErrs = true; CurEnv->e_errormode = EM_MAIL; Verbose = 0; - DisConnected = TRUE; + DisConnected = true; /* all input from /dev/null */ - if (InChannel != stdin) + if (InChannel != smioin) { - (void) fclose(InChannel); - InChannel = stdin; + (void) sm_io_close(InChannel, SM_TIME_DEFAULT); + InChannel = smioin; } - if (freopen("/dev/null", "r", stdin) == NULL) + if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL, + SM_IO_RDONLY, NULL, smioin) == NULL) sm_syslog(LOG_ERR, e->e_id, - "disconnect: freopen(\"/dev/null\") failed: %s", - errstring(errno)); + "disconnect: sm_io_reopen(\"%s\") failed: %s", + SM_PATH_DEVNULL, sm_errstring(errno)); - /* output to the transcript */ - if (OutChannel != stdout) + /* + ** output to the transcript + ** We also compare the fd numbers here since OutChannel + ** might be a layer on top of smioout due to encryption + ** (see sfsasl.c). + */ + + if (OutChannel != smioout && + sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL) != + sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL)) { - (void) fclose(OutChannel); - OutChannel = stdout; + (void) sm_io_close(OutChannel, SM_TIME_DEFAULT); + OutChannel = smioout; + +#if 0 + /* + ** Has smioout been closed? Reopen it. + ** This shouldn't happen anymore, the code is here + ** just as a reminder. + */ + + if (smioout->sm_magic == NULL && + sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL, + SM_IO_WRONLY, NULL, smioout) == NULL) + sm_syslog(LOG_ERR, e->e_id, + "disconnect: sm_io_reopen(\"%s\") failed: %s", + SM_PATH_DEVNULL, sm_errstring(errno)); +#endif /* 0 */ } if (droplev > 0) { - fd = open("/dev/null", O_WRONLY, 0666); + fd = open(SM_PATH_DEVNULL, O_WRONLY, 0666); if (fd == -1) sm_syslog(LOG_ERR, e->e_id, - "disconnect: open(\"/dev/null\") failed: %s", - errstring(errno)); - (void) fflush(stdout); + "disconnect: open(\"%s\") failed: %s", + SM_PATH_DEVNULL, sm_errstring(errno)); + (void) sm_io_flush(smioout, SM_TIME_DEFAULT); (void) dup2(fd, STDOUT_FILENO); (void) dup2(fd, STDERR_FILENO); (void) close(fd); @@ -2520,9 +2952,8 @@ disconnect(droplev, e) #endif /* XDEBUG */ if (LogLevel > 71) - sm_syslog(LOG_DEBUG, e->e_id, - "in background, pid=%d", - (int) getpid()); + sm_syslog(LOG_DEBUG, e->e_id, "in background, pid=%d", + (int) CurrentPid); errno = 0; } @@ -2554,12 +2985,12 @@ obsolete(argv) } /* If -C doesn't have an argument, use sendmail.cf. */ -#define __DEFPATH "sendmail.cf" +#define __DEFPATH "sendmail.cf" if (ap[1] == 'C' && ap[2] == '\0') { *argv = xalloc(sizeof(__DEFPATH) + 2); - (void) snprintf(argv[0], sizeof(__DEFPATH) + 2, "-C%s", - __DEFPATH); + (void) sm_strlcpyn(argv[0], sizeof(__DEFPATH) + 2, 2, + "-C", __DEFPATH); } /* If -q doesn't have an argument, run it once. */ @@ -2604,7 +3035,7 @@ auth_warning(e, msg, va_alist) #endif /* __STDC__ */ { char buf[MAXLINE]; - VA_LOCAL_DECL + SM_VA_LOCAL_DECL if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags)) { @@ -2616,21 +3047,21 @@ auth_warning(e, msg, va_alist) struct hostent *hp; hp = myhostname(hostbuf, sizeof hostbuf); -#if _FFR_FREEHOSTENT && NETINET6 +#if NETINET6 if (hp != NULL) { freehostent(hp); hp = NULL; } -#endif /* _FFR_FREEHOSTENT && NETINET6 */ +#endif /* NETINET6 */ } - (void) snprintf(buf, sizeof buf, "%s: ", hostbuf); + (void) sm_strlcpyn(buf, sizeof buf, 2, hostbuf, ": "); p = &buf[strlen(buf)]; - VA_START(msg); - vsnprintf(p, SPACELEFT(buf, p), msg, ap); - VA_END; - addheader("X-Authentication-Warning", buf, 0, &e->e_header); + SM_VA_START(ap, msg); + (void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap); + SM_VA_END(ap); + addheader("X-Authentication-Warning", buf, 0, e); if (LogLevel > 3) sm_syslog(LOG_INFO, e->e_id, "Authentication-Warning: %.400s", @@ -2647,7 +3078,7 @@ auth_warning(e, msg, va_alist) ** The value, if any. */ -char * +static char * getextenv(envar) const char *envar; { @@ -2663,7 +3094,7 @@ getextenv(envar) return NULL; } /* -** SETUSERENV -- set an environment in the propogated environment +** SETUSERENV -- set an environment in the propagated environment ** ** Parameters: ** envar -- the name of the environment variable. @@ -2695,7 +3126,7 @@ setuserenv(envar, value) i = strlen(envar) + 1; l = strlen(value) + i + 1; p = (char *) xalloc(l); - (void) snprintf(p, l, "%s=%s", envar, value); + (void) sm_strlcpyn(p, l, 3, envar, "=", value); while (*evp != NULL && strncmp(*evp, p, i) != 0) evp++; @@ -2738,12 +3169,12 @@ dumpstate(when) "*** $j not in $=w ***"); } sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren); - sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)\n", + sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)", NextMacroId, MAXMACROID); sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---"); - printopenfds(TRUE); + printopenfds(true); sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---"); - mci_dump_all(TRUE); + mci_dump_all(true); rs = strtorwset("debug_dumpstate", NULL, ST_FIND); if (rs > 0) { @@ -2752,7 +3183,7 @@ dumpstate(when) char *pv[MAXATOM + 1]; pv[0] = NULL; - status = rewrite(pv, rs, 0, CurEnv); + status = REWRITE(pv, rs, CurEnv); sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- ruleset debug_dumpstate returns stat %d, pv: ---", status); @@ -2761,6 +3192,7 @@ dumpstate(when) } sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---"); } + #ifdef SIGUSR1 /* ** SIGUSR1 -- Signal a request to dump state. @@ -2784,15 +3216,21 @@ sigusr1(sig) int sig; { int save_errno = errno; +# if SM_HEAP_CHECK + extern void dumpstab __P((void)); +# endif /* SM_HEAP_CHECK */ FIX_SYSV_SIGNAL(sig, sigusr1); errno = save_errno; CHECK_CRITICAL(sig); dumpstate("user signal"); +# if SM_HEAP_CHECK + dumpstab(); +# endif /* SM_HEAP_CHECK */ errno = save_errno; return SIGFUNC_RETURN; } -# endif /* SIGUSR1 */ +#endif /* SIGUSR1 */ /* ** DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option ** @@ -2813,9 +3251,12 @@ drop_privileges(to_real_uid) GIDSET_T emptygidset[1]; if (tTd(47, 1)) - dprintf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n", - (int)to_real_uid, (int)RealUid, - (int)RealGid, (int)RunAsUid, (int)RunAsGid); + sm_dprintf("drop_privileges(%d): Real[UG]id=%d:%d, get[ug]id=%d:%d, gete[ug]id=%d:%d, RunAs[UG]id=%d:%d\n", + (int) to_real_uid, + (int) RealUid, (int) RealGid, + (int) getuid(), (int) getgid(), + (int) geteuid(), (int) getegid(), + (int) RunAsUid, (int) RunAsGid); if (to_real_uid) { @@ -2826,33 +3267,72 @@ drop_privileges(to_real_uid) /* make sure no one can grab open descriptors for secret files */ endpwent(); + sm_mbdb_terminate(); /* reset group permissions; these can be set later */ emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid(); + + /* + ** Notice: on some OS (Linux...) the setgroups() call causes + ** a logfile entry if sendmail is not run by root. + ** However, it is unclear (no POSIX standard) whether + ** setgroups() can only succeed if executed by root. + ** So for now we keep it as it is; if you want to change it, use + ** if (geteuid() == 0 && setgroups(1, emptygidset) == -1) + */ + if (setgroups(1, emptygidset) == -1 && geteuid() == 0) { syserr("drop_privileges: setgroups(1, %d) failed", - (int)emptygidset[0]); + (int) emptygidset[0]); rval = EX_OSERR; } /* reset primary group and user id */ - if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0) + if ((to_real_uid || RunAsGid != 0) && EffGid != RunAsGid && + setgid(RunAsGid) < 0) { - syserr("drop_privileges: setgid(%d) failed", (int)RunAsGid); + syserr("drop_privileges: setgid(%d) failed", (int) RunAsGid); rval = EX_OSERR; } if (to_real_uid || RunAsUid != 0) { uid_t euid = geteuid(); - if (setuid(RunAsUid) < 0) + if (setuid(RunAsUid) < 0 || + getuid() != RunAsUid || geteuid() != RunAsUid) { - syserr("drop_privileges: setuid(%d) failed", - (int)RunAsUid); - rval = EX_OSERR; +#if HASSETREUID + /* + ** if ruid != RunAsUid, euid == RunAsUid, then + ** try resetting just the real uid, then using + ** setuid() to drop the saved-uid as well. + */ + + if (euid == RunAsUid) + { + if (setreuid(RunAsUid, -1) < 0) + { + syserr("drop_privileges: setreuid(%d, -1) failed", + (int) RunAsUid); + rval = EX_OSERR; + } + if (setuid(RunAsUid) < 0) + { + syserr("drop_privileges: second setuid(%d) attempt failed", + (int) RunAsUid); + rval = EX_OSERR; + } + } + else +#endif /* HASSETREUID */ + { + syserr("drop_privileges: setuid(%d) failed", + (int) RunAsUid); + rval = EX_OSERR; + } } - else if (RunAsUid != 0 && setuid(0) == 0) + if (RunAsUid != 0 && setuid(0) == 0) { /* ** Believe it or not, the Linux capability model @@ -2872,19 +3352,19 @@ drop_privileges(to_real_uid) ** making it possible to set it back again later. */ - syserr("drop_privileges: Unable to drop non-root set-user-id privileges"); + syserr("drop_privileges: Unable to drop non-root set-user-ID privileges"); rval = EX_OSERR; } } if (tTd(47, 5)) { - dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n", - (int)geteuid(), (int)getuid(), - (int)getegid(), (int)getgid()); - dprintf("drop_privileges: RunAsUser = %d:%d\n", - (int)RunAsUid, (int)RunAsGid); + sm_dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n", + (int) geteuid(), (int) getuid(), + (int) getegid(), (int) getgid()); + sm_dprintf("drop_privileges: RunAsUser = %d:%d\n", + (int) RunAsUid, (int) RunAsGid); if (tTd(47, 10)) - dprintf("drop_privileges: rval = %d\n", rval); + sm_dprintf("drop_privileges: rval = %d\n", rval); } return rval; } @@ -2901,6 +3381,9 @@ drop_privileges(to_real_uid) ** ** Returns: ** none +** +** Side Effects: +** possibly changes MissingFds */ void @@ -2918,11 +3401,11 @@ fill_fd(fd, where) syserr("fill_fd: %s: fd %d not open", where, fd); else MissingFds |= 1 << fd; - i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666); + i = open(SM_PATH_DEVNULL, fd == 0 ? O_RDONLY : O_WRONLY, 0666); if (i < 0) { - syserr("!fill_fd: %s: cannot open /dev/null", - where == NULL ? "startup" : where); + syserr("!fill_fd: %s: cannot open %s", + where == NULL ? "startup" : where, SM_PATH_DEVNULL); } if (fd != i) { @@ -2931,6 +3414,41 @@ fill_fd(fd, where) } } /* +** SM_PRINTOPTIONS -- print options +** +** Parameters: +** options -- array of options. +** +** Returns: +** none. +*/ + +static void +sm_printoptions(options) + char **options; +{ + int ll; + char **av; + + av = options; + ll = 7; + while (*av != NULL) + { + if (ll + strlen(*av) > 63) + { + sm_dprintf("\n"); + ll = 0; + } + if (ll == 0) + sm_dprintf("\t\t"); + else + sm_dprintf(" "); + sm_dprintf("%s", *av); + ll += strlen(*av++) + 1; + } + sm_dprintf("\n"); +} +/* ** TESTMODELINE -- process a test mode input line ** ** Parameters: @@ -2961,11 +3479,9 @@ testmodeline(line, e) ADDRESS a; static int tryflags = RF_COPYNONE; char exbuf[MAXLINE]; - extern u_char TokTypeNoC[]; + extern unsigned char TokTypeNoC[]; -#if _FFR_ADDR_TYPE - define(macid("{addr_type}", NULL), "e r", e); -#endif /* _FFR_ADDR_TYPE */ + macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r"); /* skip leading spaces */ while (*line == ' ') @@ -2985,18 +3501,18 @@ testmodeline(line, e) switch (line[1]) { case 'D': - mid = macid(&line[2], &delimptr); + mid = macid_parse(&line[2], &delimptr); if (mid == 0) return; translate_dollars(delimptr); - define(mid, newstr(delimptr), e); + macdefine(&e->e_macro, A_TEMP, mid, delimptr); break; case 'C': if (line[2] == '\0') /* not to call syserr() */ return; - mid = macid(&line[2], &delimptr); + mid = macid_parse(&line[2], &delimptr); if (mid == 0) return; translate_dollars(delimptr); @@ -3021,11 +3537,13 @@ testmodeline(line, e) break; case '\0': - printf("Usage: .[DC]macro value(s)\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Usage: .[DC]macro value(s)\n"); break; default: - printf("Unknown \".\" command %s\n", line); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Unknown \".\" command %s\n", line); break; } return; @@ -3037,7 +3555,8 @@ testmodeline(line, e) rs = strtorwset(&line[2], NULL, ST_FIND); if (rs < 0) { - printf("Undefined ruleset %s\n", &line[2]); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Undefined ruleset %s\n", &line[2]); return; } rw = RewriteRules[rs]; @@ -3045,22 +3564,28 @@ testmodeline(line, e) return; do { - (void) putchar('R'); + (void) sm_io_putc(smioout, SM_TIME_DEFAULT, + 'R'); s = rw->r_lhs; while (*s != NULL) { xputs(*s++); - (void) putchar(' '); + (void) sm_io_putc(smioout, + SM_TIME_DEFAULT, ' '); } - (void) putchar('\t'); - (void) putchar('\t'); + (void) sm_io_putc(smioout, SM_TIME_DEFAULT, + '\t'); + (void) sm_io_putc(smioout, SM_TIME_DEFAULT, + '\t'); s = rw->r_rhs; while (*s != NULL) { xputs(*s++); - (void) putchar(' '); + (void) sm_io_putc(smioout, + SM_TIME_DEFAULT, ' '); } - (void) putchar('\n'); + (void) sm_io_putc(smioout, SM_TIME_DEFAULT, + '\n'); } while ((rw = rw->r_next) != NULL); break; @@ -3073,11 +3598,13 @@ testmodeline(line, e) break; case '\0': - printf("Usage: =Sruleset or =M\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Usage: =Sruleset or =M\n"); break; default: - printf("Unknown \"=\" command %s\n", line); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Unknown \"=\" command %s\n", line); break; } return; @@ -3090,11 +3617,13 @@ testmodeline(line, e) break; case '\0': - printf("Usage: -d{debug arguments}\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Usage: -d{debug arguments}\n"); break; default: - printf("Unknown \"-\" command %s\n", line); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Unknown \"-\" command %s\n", line); break; } return; @@ -3102,21 +3631,23 @@ testmodeline(line, e) case '$': if (line[1] == '=') { - mid = macid(&line[2], NULL); + mid = macid(&line[2]); if (mid != 0) stabapply(dump_class, mid); return; } - mid = macid(&line[1], NULL); + mid = macid(&line[1]); if (mid == 0) return; p = macvalue(mid, e); if (p == NULL) - printf("Undefined\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Undefined\n"); else { xputs(p); - printf("\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "\n"); } return; @@ -3134,15 +3665,17 @@ testmodeline(line, e) p = ""; if (line[1] == '\0') { - printf("Usage: /[canon|map|mx|parse|try|tryflags]\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Usage: /[canon|map|mx|parse|try|tryflags]\n"); return; } - if (strcasecmp(&line[1], "quit") == 0) + if (sm_strcasecmp(&line[1], "quit") == 0) { CurEnv->e_id = NULL; - finis(TRUE, ExitStat); + finis(true, ExitStat); + /* NOTREACHED */ } - if (strcasecmp(&line[1], "mx") == 0) + if (sm_strcasecmp(&line[1], "mx") == 0) { #if NAMED_BIND /* look up MX records */ @@ -3152,75 +3685,95 @@ testmodeline(line, e) if (*p == '\0') { - printf("Usage: /mx address\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Usage: /mx address\n"); return; } - nmx = getmxrr(p, mxhosts, NULL, FALSE, &rcode); - printf("getmxrr(%s) returns %d value(s):\n", p, nmx); + nmx = getmxrr(p, mxhosts, NULL, false, &rcode, true, + NULL); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "getmxrr(%s) returns %d value(s):\n", + p, nmx); for (i = 0; i < nmx; i++) - printf("\t%s\n", mxhosts[i]); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "\t%s\n", mxhosts[i]); #else /* NAMED_BIND */ - printf("No MX code compiled in\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "No MX code compiled in\n"); #endif /* NAMED_BIND */ } - else if (strcasecmp(&line[1], "canon") == 0) + else if (sm_strcasecmp(&line[1], "canon") == 0) { char host[MAXHOSTNAMELEN]; if (*p == '\0') { - printf("Usage: /canon address\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Usage: /canon address\n"); return; } - else if (strlcpy(host, p, sizeof host) >= sizeof host) + else if (sm_strlcpy(host, p, sizeof host) >= sizeof host) { - printf("Name too long\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Name too long\n"); return; } - (void) getcanonname(host, sizeof host, HasWildcardMX); - printf("getcanonname(%s) returns %s\n", p, host); + (void) getcanonname(host, sizeof host, HasWildcardMX, + NULL); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "getcanonname(%s) returns %s\n", + p, host); } - else if (strcasecmp(&line[1], "map") == 0) + else if (sm_strcasecmp(&line[1], "map") == 0) { auto int rcode = EX_OK; char *av[2]; if (*p == '\0') { - printf("Usage: /map mapname key\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Usage: /map mapname key\n"); return; } - for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++) + for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++) continue; if (*q == '\0') { - printf("No key specified\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "No key specified\n"); return; } *q++ = '\0'; map = stab(p, ST_MAP, ST_FIND); if (map == NULL) { - printf("Map named \"%s\" not found\n", p); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Map named \"%s\" not found\n", p); return; } if (!bitset(MF_OPEN, map->s_map.map_mflags) && !openmap(&(map->s_map))) { - printf("Map named \"%s\" not open\n", p); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Map named \"%s\" not open\n", p); return; } - printf("map_lookup: %s (%s) ", p, q); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "map_lookup: %s (%s) ", p, q); av[0] = q; av[1] = NULL; p = (*map->s_map.map_class->map_lookup) (&map->s_map, q, av, &rcode); if (p == NULL) - printf("no match (%d)\n", rcode); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "no match (%d)\n", + rcode); else - printf("returns %s (%d)\n", p, rcode); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "returns %s (%d)\n", p, + rcode); } - else if (strcasecmp(&line[1], "try") == 0) + else if (sm_strcasecmp(&line[1], "try") == 0) { MAILER *m; STAB *st; @@ -3234,30 +3787,36 @@ testmodeline(line, e) } if (q == NULL || *q == '\0') { - printf("Usage: /try mailer address\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Usage: /try mailer address\n"); return; } st = stab(p, ST_MAILER, ST_FIND); if (st == NULL) { - printf("Unknown mailer %s\n", p); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Unknown mailer %s\n", p); return; } m = st->s_mailer; - printf("Trying %s %s address %s for mailer %s\n", - bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope", - bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient", - q, p); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Trying %s %s address %s for mailer %s\n", + bitset(RF_HEADERADDR, tryflags) ? "header" + : "envelope", + bitset(RF_SENDERADDR, tryflags) ? "sender" + : "recipient", q, p); p = remotename(q, m, tryflags, &rcode, CurEnv); - printf("Rcode = %d, addr = %s\n", - rcode, p == NULL ? "<NULL>" : p); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Rcode = %d, addr = %s\n", + rcode, p == NULL ? "<NULL>" : p); e->e_to = NULL; } - else if (strcasecmp(&line[1], "tryflags") == 0) + else if (sm_strcasecmp(&line[1], "tryflags") == 0) { if (*p == '\0') { - printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Usage: /tryflags [Hh|Ee][Ss|Rr]\n"); return; } for (; *p != '\0'; p++) @@ -3285,40 +3844,53 @@ testmodeline(line, e) break; } } -#if _FFR_ADDR_TYPE exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e'; exbuf[1] = ' '; exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r'; exbuf[3] = '\0'; - define(macid("{addr_type}", NULL), newstr(exbuf), e); -#endif /* _FFR_ADDR_TYPE */ + macdefine(&e->e_macro, A_TEMP, + macid("{addr_type}"), exbuf); } - else if (strcasecmp(&line[1], "parse") == 0) + else if (sm_strcasecmp(&line[1], "parse") == 0) { if (*p == '\0') { - printf("Usage: /parse address\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Usage: /parse address\n"); return; } q = crackaddr(p); - printf("Cracked address = "); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Cracked address = "); xputs(q); - printf("\nParsing %s %s address\n", - bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope", - bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient"); - if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL) - printf("Cannot parse\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "\nParsing %s %s address\n", + bitset(RF_HEADERADDR, tryflags) ? + "header" : "envelope", + bitset(RF_SENDERADDR, tryflags) ? + "sender" : "recipient"); + if (parseaddr(p, &a, tryflags, '\0', NULL, e, true) + == NULL) + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Cannot parse\n"); else if (a.q_host != NULL && a.q_host[0] != '\0') - printf("mailer %s, host %s, user %s\n", - a.q_mailer->m_name, a.q_host, a.q_user); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "mailer %s, host %s, user %s\n", + a.q_mailer->m_name, + a.q_host, + a.q_user); else - printf("mailer %s, user %s\n", - a.q_mailer->m_name, a.q_user); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "mailer %s, user %s\n", + a.q_mailer->m_name, + a.q_user); e->e_to = NULL; } else { - printf("Unknown \"/\" command %s\n", line); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Unknown \"/\" command %s\n", + line); } return; } @@ -3330,11 +3902,12 @@ testmodeline(line, e) p++; if (*p == '\0') { - printf("No address!\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "No address!\n"); return; } *p = '\0'; - if (invalidaddr(p + 1, NULL)) + if (invalidaddr(p + 1, NULL, true)) return; do { @@ -3353,13 +3926,16 @@ testmodeline(line, e) rs = strtorwset(p, NULL, ST_FIND); if (rs < 0) { - printf("Undefined ruleset %s\n", p); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Undefined ruleset %s\n", + p); break; } - status = rewrite(pvp, rs, 0, e); + status = REWRITE(pvp, rs, e); if (status != EX_OK) - printf("== Ruleset %s (%d) status %d\n", - p, rs, status); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "== Ruleset %s (%d) status %d\n", + p, rs, status); while (*p != '\0' && *p++ != ',') continue; } @@ -3374,5 +3950,23 @@ dump_class(s, id) if (s->s_type != ST_CLASS) return; if (bitnset(bitidx(id), s->s_class)) - printf("%s\n", s->s_name); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "%s\n", s->s_name); } + +/* +** An exception type used to create QuickAbort exceptions. +** This is my first cut at converting QuickAbort from longjmp to exceptions. +** These exceptions have a single integer argument, which is the argument +** to longjmp in the original code (either 1 or 2). I don't know the +** significance of 1 vs 2: the calls to setjmp don't care. +*/ + +const SM_EXC_TYPE_T EtypeQuickAbort = +{ + SmExcTypeMagic, + "E:mta.quickabort", + "i", + sm_etype_printf, + "quick abort %0", +}; diff --git a/gnu/usr.sbin/sendmail/sendmail/map.c b/gnu/usr.sbin/sendmail/sendmail/map.c index 709f9a6be37..8f6c303141c 100644 --- a/gnu/usr.sbin/sendmail/sendmail/map.c +++ b/gnu/usr.sbin/sendmail/sendmail/map.c @@ -11,14 +11,15 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: map.c,v 8.414.4.55 2001/08/15 22:08:58 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +SM_RCSID("@(#)$Sendmail: map.c,v 8.601 2001/09/04 22:43:03 ca Exp $") + +#if LDAPMAP +# include <sm/ldap.h> +#endif /* LDAPMAP */ -#ifdef NDBM +#if NDBM # include <ndbm.h> # ifdef R_FIRST ERROR README: You are running the Berkeley DB version of ndbm.h. See @@ -27,21 +28,21 @@ static char id[] = "@(#)$Sendmail: map.c,v 8.414.4.55 2001/08/15 22:08:58 gshapi ERROR README: and use -DNEWDB instead. # endif /* R_FIRST */ #endif /* NDBM */ -#ifdef NEWDB +#if NEWDB # include <db.h> # ifndef DB_VERSION_MAJOR # define DB_VERSION_MAJOR 1 # endif /* ! DB_VERSION_MAJOR */ #endif /* NEWDB */ -#ifdef NIS +#if NIS struct dom_binding; /* forward reference needed on IRIX */ # include <rpcsvc/ypclnt.h> -# ifdef NDBM +# if NDBM # define NDBM_YP_COMPAT /* create YP-compatible NDBM files */ # endif /* NDBM */ #endif /* NIS */ -#ifdef NEWDB +#if NEWDB # if DB_VERSION_MAJOR < 2 static bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *)); # endif /* DB_VERSION_MAJOR < 2 */ @@ -53,20 +54,15 @@ static bool db_map_open __P((MAP *, int, char *, DBTYPE, void **)); # endif /* DB_VERSION_MAJOR > 2 */ #endif /* NEWDB */ static bool extract_canonname __P((char *, char *, char *, char[], int)); -#ifdef LDAPMAP -static void ldapmap_clear __P((LDAPMAP_STRUCT *)); -static STAB *ldapmap_findconn __P((LDAPMAP_STRUCT *)); -static int ldapmap_geterrno __P((LDAP *)); -static void ldapmap_setopts __P((LDAP *, LDAPMAP_STRUCT *)); -static bool ldapmap_start __P((MAP *)); -static void ldaptimeout __P((int)); -#endif /* LDAPMAP */ static void map_close __P((STAB *, int)); static void map_init __P((STAB *, int)); -#ifdef NISPLUS +#ifdef LDAPMAP +static STAB * ldapmap_findconn __P((SM_LDAP_STRUCT *)); +#endif /* LDAPMAP */ +#if NISPLUS static bool nisplus_getcanonname __P((char *, int, int *)); #endif /* NISPLUS */ -#ifdef NIS +#if NIS static bool nis_getcanonname __P((char *, int, int *)); #endif /* NIS */ #if NETINFO @@ -74,14 +70,25 @@ static bool ni_getcanonname __P((char *, int, int *)); #endif /* NETINFO */ static bool text_getcanonname __P((char *, int, int *)); +/* default error message for trying to open a map in write mode */ +#ifdef ENOSYS +# define SM_EMAPCANTWRITE ENOSYS +#else /* ENOSYS */ +# ifdef EFTYPE +# define SM_EMAPCANTWRITE EFTYPE +# else /* EFTYPE */ +# define SM_EMAPCANTWRITE ENXIO +# endif /* EFTYPE */ +#endif /* ENOSYS */ + /* ** MAP.C -- implementations for various map classes. ** ** Each map class implements a series of functions: ** ** bool map_parse(MAP *map, char *args) -** Parse the arguments from the config file. Return TRUE -** if they were ok, FALSE otherwise. Fill in map with the +** Parse the arguments from the config file. Return true +** if they were ok, false otherwise. Fill in map with the ** values. ** ** char *map_lookup(MAP *map, char *key, char **args, int *pstat) @@ -99,9 +106,9 @@ static bool text_getcanonname __P((char *, int, int *)); ** ** bool map_open(MAP *map, int mode) ** Open the map for the indicated mode. Mode should -** be either O_RDONLY or O_RDWR. Return TRUE if it -** was opened successfully, FALSE otherwise. If the open -** failed an the MF_OPTIONAL flag is not set, it should +** be either O_RDONLY or O_RDWR. Return true if it +** was opened successfully, false otherwise. If the open +** failed and the MF_OPTIONAL flag is not set, it should ** also print an error. If the MF_ALIAS bit is set ** and this map class understands the @:@ convention, it ** should call aliaswait() before returning. @@ -116,10 +123,6 @@ static bool text_getcanonname __P((char *, int, int *)); #define DBMMODE 0644 -#ifndef EX_NOTFOUND -# define EX_NOTFOUND EX_NOHOST -#endif /* ! EX_NOTFOUND */ - #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL # define LOCK_ON_OPEN 1 /* we can open/create a locked file */ #else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */ @@ -136,8 +139,8 @@ static bool text_getcanonname __P((char *, int, int *)); ** ap -- a pointer to the args on the config line. ** ** Returns: -** TRUE -- if everything parsed OK. -** FALSE -- otherwise. +** true -- if everything parsed OK. +** false -- otherwise. ** ** Side Effects: ** null terminates the filename; stores it in map @@ -151,10 +154,11 @@ map_parseargs(map, ap) register char *p = ap; /* - ** there is no check whether there is really an argument, - ** but that's not important enough to warrant extra code + ** There is no check whether there is really an argument, + ** but that's not important enough to warrant extra code. */ - map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL; + + map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; map->map_spacesub = SpaceSub; /* default value */ for (;;) { @@ -285,9 +289,9 @@ map_parseargs(map, ap) { syserr("No file name for %s map %s", map->map_class->map_cname, map->map_mname); - return FALSE; + return false; } - return TRUE; + return true; } /* ** MAP_REWRITE -- rewrite a database key, interpolating %n indications. @@ -304,9 +308,6 @@ map_parseargs(map, ap) ** Returns: ** Pointer to rewritten result. This is static data that ** should be copied if it is to be saved! -** -** Side Effects: -** none. */ char * @@ -327,15 +328,15 @@ map_rewrite(map, s, slen, av) if (tTd(39, 1)) { - dprintf("map_rewrite(%.*s), av =", (int)slen, s); + sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s); if (av == NULL) - dprintf(" (nullv)"); + sm_dprintf(" (nullv)"); else { for (avp = av; *avp != NULL; avp++) - dprintf("\n\t%s", *avp); + sm_dprintf("\n\t%s", *avp); } - dprintf("\n"); + sm_dprintf("\n"); } /* count expected size of output (can safely overestimate) */ @@ -368,7 +369,7 @@ map_rewrite(map, s, slen, av) buflen = len; if (buf != NULL) sm_free(buf); - buf = xalloc(buflen); + buf = sm_pmalloc_x(buflen); } bp = buf; @@ -388,7 +389,7 @@ map_rewrite(map, s, slen, av) { pushc: if (--len <= 0) - break; + break; *bp++ = c; continue; } @@ -413,11 +414,11 @@ map_rewrite(map, s, slen, av) } } if (map->map_app != NULL && len > 0) - (void) strlcpy(bp, map->map_app, len); + (void) sm_strlcpy(bp, map->map_app, len); else *bp = '\0'; if (tTd(39, 1)) - dprintf("map_rewrite => %s\n", buf); + sm_dprintf("map_rewrite => %s\n", buf); return buf; } /* @@ -472,7 +473,7 @@ map_init(s, unused) return; if (tTd(38, 2)) - dprintf("map_init(%s:%s, %s)\n", + sm_dprintf("map_init(%s:%s, %s)\n", map->map_class->map_cname == NULL ? "NULL" : map->map_class->map_cname, map->map_mname == NULL ? "NULL" : map->map_mname, @@ -482,7 +483,7 @@ map_init(s, unused) !bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) { if (tTd(38, 3)) - dprintf("\tnot rebuildable\n"); + sm_dprintf("\tnot rebuildable\n"); return; } @@ -494,7 +495,7 @@ map_init(s, unused) map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); } - (void) rebuildaliases(map, FALSE); + (void) rebuildaliases(map, false); return; } /* @@ -505,39 +506,38 @@ map_init(s, unused) ** ** Returns: ** whether open succeeded. -** */ bool openmap(map) MAP *map; { - bool restore = FALSE; + bool restore = false; bool savehold = HoldErrs; bool savequick = QuickAbort; int saveerrors = Errors; if (!bitset(MF_VALID, map->map_mflags)) - return FALSE; + return false; /* better safe than sorry... */ if (bitset(MF_OPEN, map->map_mflags)) - return TRUE; + return true; /* Don't send a map open error out via SMTP */ if ((OnlyOneError || QuickAbort) && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) { - restore = TRUE; - HoldErrs = TRUE; - QuickAbort = FALSE; + restore = true; + HoldErrs = true; + QuickAbort = false; } errno = 0; if (map->map_class->map_open(map, O_RDONLY)) { if (tTd(38, 4)) - dprintf("openmap()\t%s:%s %s: valid\n", + sm_dprintf("openmap()\t%s:%s %s: valid\n", map->map_class->map_cname == NULL ? "NULL" : map->map_class->map_cname, map->map_mname == NULL ? "NULL" : @@ -545,12 +545,12 @@ openmap(map) map->map_file == NULL ? "NULL" : map->map_file); map->map_mflags |= MF_OPEN; - map->map_pid = getpid(); + map->map_pid = CurrentPid; } else { if (tTd(38, 4)) - dprintf("openmap()\t%s:%s %s: invalid%s%s\n", + sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n", map->map_class->map_cname == NULL ? "NULL" : map->map_class->map_cname, map->map_mname == NULL ? "NULL" : @@ -558,15 +558,15 @@ openmap(map) map->map_file == NULL ? "NULL" : map->map_file, errno == 0 ? "" : ": ", - errno == 0 ? "" : errstring(errno)); + errno == 0 ? "" : sm_errstring(errno)); if (!bitset(MF_OPTIONAL, map->map_mflags)) { extern MAPCLASS BogusMapClass; + map->map_orgclass = map->map_class; map->map_class = &BogusMapClass; - map->map_mflags |= MF_OPEN; - map->map_pid = getpid(); - MapOpenErr = TRUE; + map->map_mflags |= MF_OPEN|MF_OPENBOGUS; + map->map_pid = CurrentPid; } else { @@ -588,23 +588,24 @@ openmap(map) ** CLOSEMAPS -- close all open maps opened by the current pid. ** ** Parameters: -** none +** bogus -- only close bogus maps. ** ** Returns: ** none. */ void -closemaps() +closemaps(bogus) + bool bogus; { - stabapply(map_close, 0); + stabapply(map_close, bogus); } /* ** MAP_CLOSE -- close a map opened by the current pid. ** ** Parameters: -** s -- STAB entry: if map: try to open -** second parameter is unused (required by stabapply()) +** s -- STAB entry: if map: try to close +** bogus -- only close bogus maps or MCF_NOTPERSIST maps. ** ** Returns: ** none. @@ -612,31 +613,49 @@ closemaps() /* ARGSUSED1 */ static void -map_close(s, unused) +map_close(s, bogus) register STAB *s; - int unused; + int bogus; /* int because of stabapply(), used as bool */ { MAP *map; + extern MAPCLASS BogusMapClass; if (s->s_type != ST_MAP) return; map = &s->s_map; + /* + ** close the map iff: + ** it is valid and open and opened by this process + ** and (!bogus or it's a bogus map or it is not persistent) + ** negate this: return iff + ** it is not valid or it is not open or not opened by this process + ** or (bogus and it's not a bogus map and it's not not-persistent) + */ + if (!bitset(MF_VALID, map->map_mflags) || !bitset(MF_OPEN, map->map_mflags) || bitset(MF_CLOSING, map->map_mflags) || - map->map_pid != getpid()) + map->map_pid != CurrentPid || + (bogus && map->map_class != &BogusMapClass && + !bitset(MCF_NOTPERSIST, map->map_class->map_cflags))) return; + if (map->map_class == &BogusMapClass && map->map_orgclass != NULL && + map->map_orgclass != &BogusMapClass) + map->map_class = map->map_orgclass; if (tTd(38, 5)) - dprintf("closemaps: closing %s (%s)\n", + sm_dprintf("closemaps: closing %s (%s)\n", map->map_mname == NULL ? "NULL" : map->map_mname, map->map_file == NULL ? "NULL" : map->map_file); - map->map_mflags |= MF_CLOSING; - map->map_class->map_close(map); - map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); + if (!bitset(MF_OPENBOGUS, map->map_mflags)) + { + map->map_mflags |= MF_CLOSING; + map->map_class->map_close(map); + } + map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING); } /* ** GETCANONNAME -- look up name using service switch @@ -645,45 +664,49 @@ map_close(s, unused) ** host -- the host name to look up. ** hbsize -- the size of the host buffer. ** trymx -- if set, try MX records. +** pttl -- pointer to return TTL (can be NULL). ** ** Returns: -** TRUE -- if the host was found. -** FALSE -- otherwise. +** true -- if the host was found. +** false -- otherwise. */ bool -getcanonname(host, hbsize, trymx) +getcanonname(host, hbsize, trymx, pttl) char *host; int hbsize; bool trymx; + int *pttl; { int nmaps; int mapno; - bool found = FALSE; - bool got_tempfail = FALSE; + bool found = false; + bool got_tempfail = false; auto int status; char *maptype[MAXMAPSTACK]; short mapreturn[MAXMAPACTIONS]; nmaps = switch_map_find("hosts", maptype, mapreturn); + if (pttl != 0) + *pttl = SM_DEFAULT_TTL; for (mapno = 0; mapno < nmaps; mapno++) { int i; if (tTd(38, 20)) - dprintf("getcanonname(%s), trying %s\n", + sm_dprintf("getcanonname(%s), trying %s\n", host, maptype[mapno]); if (strcmp("files", maptype[mapno]) == 0) { found = text_getcanonname(host, hbsize, &status); } -#ifdef NIS +#if NIS else if (strcmp("nis", maptype[mapno]) == 0) { found = nis_getcanonname(host, hbsize, &status); } #endif /* NIS */ -#ifdef NISPLUS +#if NISPLUS else if (strcmp("nisplus", maptype[mapno]) == 0) { found = nisplus_getcanonname(host, hbsize, &status); @@ -692,7 +715,7 @@ getcanonname(host, hbsize, trymx) #if NAMED_BIND else if (strcmp("dns", maptype[mapno]) == 0) { - found = dns_getcanonname(host, hbsize, trymx, &status); + found = dns_getcanonname(host, hbsize, trymx, &status, pttl); } #endif /* NAMED_BIND */ #if NETINFO @@ -703,7 +726,7 @@ getcanonname(host, hbsize, trymx) #endif /* NETINFO */ else { - found = FALSE; + found = false; status = EX_UNAVAILABLE; } @@ -723,7 +746,7 @@ getcanonname(host, hbsize, trymx) if (status == EX_TEMPFAIL) { i = MA_TRYAGAIN; - got_tempfail = TRUE; + got_tempfail = true; } else if (status == EX_NOTFOUND) i = MA_NOTFOUND; @@ -738,7 +761,7 @@ getcanonname(host, hbsize, trymx) char *d; if (tTd(38, 20)) - dprintf("getcanonname(%s), found\n", host); + sm_dprintf("getcanonname(%s), found\n", host); /* ** If returned name is still single token, compensate @@ -753,25 +776,27 @@ getcanonname(host, hbsize, trymx) hbsize > (int) (strlen(host) + strlen(d) + 1)) { if (host[strlen(host) - 1] != '.') - (void) strlcat(host, ".", hbsize); - (void) strlcat(host, d, hbsize); + (void) sm_strlcat2(host, ".", d, + hbsize); + else + (void) sm_strlcat(host, d, hbsize); } else - return FALSE; + return false; } - return TRUE; + return true; } if (tTd(38, 20)) - dprintf("getcanonname(%s), failed, status=%d\n", host, status); + sm_dprintf("getcanonname(%s), failed, status=%d\n", host, + status); -#if NAMED_BIND if (got_tempfail) SM_SET_H_ERRNO(TRY_AGAIN); else SM_SET_H_ERRNO(HOST_NOT_FOUND); -#endif /* NAMED_BIND */ - return FALSE; + + return false; } /* ** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry @@ -784,8 +809,8 @@ getcanonname(host, hbsize, trymx) ** cbuflen -- the size of cbuf. ** ** Returns: -** TRUE -- if the line matched the desired name. -** FALSE -- otherwise. +** true -- if the line matched the desired name. +** false -- otherwise. */ static bool @@ -798,11 +823,11 @@ extract_canonname(name, dot, line, cbuf, cbuflen) { int i; char *p; - bool found = FALSE; + bool found = false; cbuf[0] = '\0'; if (line[0] == '#') - return FALSE; + return false; for (i = 1; ; i++) { @@ -816,16 +841,16 @@ extract_canonname(name, dot, line, cbuf, cbuflen) if (cbuf[0] == '\0' || (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL)) { - snprintf(cbuf, cbuflen, "%s", p); + (void) sm_strlcpy(cbuf, p, cbuflen); } - if (strcasecmp(name, p) == 0) - found = TRUE; + if (sm_strcasecmp(name, p) == 0) + found = true; else if (dot != NULL) { /* try looking for the FQDN as well */ *dot = '.'; - if (strcasecmp(name, p) == 0) - found = TRUE; + if (sm_strcasecmp(name, p) == 0) + found = true; *dot = '\0'; } } @@ -839,16 +864,477 @@ extract_canonname(name, dot, line, cbuf, cbuflen) { p = &cbuf[i]; *p++ = '.'; - (void) strlcpy(p, domain, cbuflen - i - 1); + (void) sm_strlcpy(p, domain, cbuflen - i - 1); } } return found; } + +/* +** DNS modules +*/ + +#if NAMED_BIND +# if DNSMAP + +# include "sm_resolve.h" +# if NETINET || NETINET6 +# include <arpa/inet.h> +# endif /* NETINET || NETINET6 */ + +/* +** DNS_MAP_OPEN -- stub to check proper value for dns map type +*/ + +bool +dns_map_open(map, mode) + MAP *map; + int mode; +{ + if (tTd(38,2)) + sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode); + + mode &= O_ACCMODE; + if (mode != O_RDONLY) + { + /* issue a pseudo-error message */ + errno = SM_EMAPCANTWRITE; + return false; + } + return true; +} + +/* +** DNS_MAP_PARSEARGS -- parse dns map definition args. +** +** Parameters: +** map -- pointer to MAP +** args -- pointer to the args on the config line. +** +** Returns: +** true -- if everything parsed OK. +** false -- otherwise. +*/ + +# if _FFR_DNSMAP_MULTILIMIT +# if !_FFR_DNSMAP_MULTI + ERROR README: You must define _FFR_DNSMAP_MULTI to use _FFR_DNSMAP_MULTILIMIT +# endif /* ! _FFR_DNSMAP_MULTI */ +# endif /* _FFR_DNSMAP_MULTILIMIT */ + +# if _FFR_DNSMAP_MULTI +# if _FFR_DNSMAP_MULTILIMIT +# define map_sizelimit map_lockfd /* overload field */ +# endif /* _FFR_DNSMAP_MULTILIMIT */ +# endif /* _FFR_DNSMAP_MULTI */ + +bool +dns_map_parseargs(map,args) + MAP *map; + char *args; +{ + register char *p = args; + int dns_m_type; + + dns_m_type = -1; + map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; + + for (;;) + { + while (isascii(*p) && isspace(*p)) + p++; + if (*p != '-') + break; + switch (*++p) + { + case 'N': + map->map_mflags |= MF_INCLNULL; + map->map_mflags &= ~MF_TRY0NULL; + break; + + case 'O': + map->map_mflags &= ~MF_TRY1NULL; + break; + + case 'o': + map->map_mflags |= MF_OPTIONAL; + break; + + case 'f': + map->map_mflags |= MF_NOFOLDCASE; + break; + + case 'm': + map->map_mflags |= MF_MATCHONLY; + break; + + case 'A': + map->map_mflags |= MF_APPEND; + break; + + case 'q': + map->map_mflags |= MF_KEEPQUOTES; + break; + + case 't': + map->map_mflags |= MF_NODEFER; + break; + + case 'a': + map->map_app = ++p; + break; + + case 'T': + map->map_tapp = ++p; + break; + + case 'd': + { + char *h; + + ++p; + h = strchr(p, ' '); + if (h != NULL) + *h = '\0'; + map->map_timeout = convtime(p, 's'); + if (h != NULL) + *h = ' '; + } + break; + + case 'r': + while (isascii(*++p) && isspace(*p)) + continue; + map->map_retry = atoi(p); + break; + +# if _FFR_DNSMAP_MULTI + case 'z': + if (*++p != '\\') + map->map_coldelim = *p; + else + { + switch (*++p) + { + case 'n': + map->map_coldelim = '\n'; + break; + + case 't': + map->map_coldelim = '\t'; + break; + + default: + map->map_coldelim = '\\'; + } + } + break; + +# if _FFR_DNSMAP_MULTILIMIT + case 'Z': + while (isascii(*++p) && isspace(*p)) + continue; + map->map_sizelimit = atoi(p); + break; +# endif /* _FFR_DNSMAP_MULTILIMIT */ +# endif /* _FFR_DNSMAP_MULTI */ + + /* Start of dns_map specific args */ + case 'R': /* search field */ + { + char *h; + + while (isascii(*++p) && isspace(*p)) + continue; + h = strchr(p, ' '); + if (h != NULL) + *h = '\0'; + dns_m_type = dns_string_to_type(p); + if (h != NULL) + *h = ' '; + if (dns_m_type < 0) + syserr("dns map %s: wrong type %s", + map->map_mname, p); + } + break; + +# if _FFR_DNSMAP_BASE + case 'B': /* base domain */ + { + char *h; + + while (isascii(*++p) && isspace(*p)) + continue; + h = strchr(p, ' '); + if (h != NULL) + *h = '\0'; + + /* + ** slight abuse of map->map_file; it isn't + ** used otherwise in this map type. + */ + + map->map_file = newstr(p); + if (h != NULL) + *h = ' '; + } + break; +# endif /* _FFR_DNSMAP_BASE */ + + } + while (*p != '\0' && !(isascii(*p) && isspace(*p))) + p++; + if (*p != '\0') + *p++ = '\0'; + } + if (dns_m_type < 0) + syserr("dns map %s: missing -R type", map->map_mname); + if (map->map_app != NULL) + map->map_app = newstr(map->map_app); + if (map->map_tapp != NULL) + map->map_tapp = newstr(map->map_tapp); + + /* + ** Assumption: assert(sizeof int <= sizeof(ARBPTR_T)); + ** Even if this assumption is wrong, we use only one byte, + ** so it doesn't really matter. + */ + + map->map_db1 = (ARBPTR_T) dns_m_type; + return true; +} + +/* +** DNS_MAP_LOOKUP -- perform dns map lookup. +** +** Parameters: +** map -- pointer to MAP +** name -- name to lookup +** av -- arguments to interpolate into buf. +** statp -- pointer to status (EX_) +** +** Returns: +** result of lookup if succeeded. +** NULL -- otherwise. +*/ + +char * +dns_map_lookup(map, name, av, statp) + MAP *map; + char *name; + char **av; + int *statp; +{ +# if _FFR_DNSMAP_MULTI +# if _FFR_DNSMAP_MULTILIMIT + int resnum = 0; +# endif /* _FFR_DNSMAP_MULTILIMIT */ +# endif /* _FFR_DNSMAP_MULTI */ + int dns_m_type = (int) map->map_db1; + char *vp = NULL, *result = NULL; + size_t vsize; + RESOURCE_RECORD_T *rr = NULL; + DNS_REPLY_T *r = NULL; +# if NETINET6 + static char buf6[INET6_ADDRSTRLEN]; +# endif /* NETINET6 */ + + if (tTd(38, 20)) + sm_dprintf("dns_map_lookup(%s, %s)\n", + map->map_mname, name); + +# if _FFR_DNSMAP_BASE + if (map->map_file != NULL && *map->map_file != '\0') + { + size_t len; + char *appdomain; + + len = strlen(map->map_file) + strlen(name) + 2; + appdomain = (char *) sm_malloc(len); + if (appdomain == NULL) + { + *statp = EX_UNAVAILABLE; + return NULL; + } + (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file); + r = dns_lookup_int(appdomain, C_IN, dns_m_type, + map->map_timeout, map->map_retry); + sm_free(appdomain); + } + else +# endif /* _FFR_DNSMAP_BASE */ + { + r = dns_lookup_int(name, C_IN, dns_m_type, map->map_timeout, + map->map_retry); + } + + if (r == NULL) + { + result = NULL; + if (errno == ETIMEDOUT || h_errno == TRY_AGAIN || + errno == ECONNREFUSED) + *statp = EX_TEMPFAIL; + else + *statp = EX_NOTFOUND; + goto cleanup; + } + *statp = EX_OK; + for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next) + { + char *type = NULL; + char *value = NULL; + + switch (rr->rr_type) + { + case T_NS: + type = "T_NS"; + value = rr->rr_u.rr_txt; + break; + case T_CNAME: + type = "T_CNAME"; + value = rr->rr_u.rr_txt; + break; + case T_AFSDB: + type = "T_AFSDB"; + value = rr->rr_u.rr_mx->mx_r_domain; + break; + case T_SRV: + type = "T_SRV"; + value = rr->rr_u.rr_srv->srv_r_target; + break; + case T_PTR: + type = "T_PTR"; + value = rr->rr_u.rr_txt; + break; + case T_TXT: + type = "T_TXT"; + value = rr->rr_u.rr_txt; + break; + case T_MX: + type = "T_MX"; + value = rr->rr_u.rr_mx->mx_r_domain; + break; +# if NETINET + case T_A: + type = "T_A"; + value = inet_ntoa(*(rr->rr_u.rr_a)); + break; +# endif /* NETINET */ +# if NETINET6 + case T_AAAA: + type = "T_AAAA"; + value = anynet_ntop(rr->rr_u.rr_aaaa, buf6, + sizeof buf6); + break; +# endif /* NETINET6 */ + } + + if (dns_m_type != rr->rr_type) + { + if (tTd(38, 40)) + sm_dprintf("\tskipping type %s (%d) value %s\n", + type != NULL ? type : "<UNKNOWN>", + rr->rr_type, + value != NULL ? value : "<NO VALUE>"); + continue; + } + +# if NETINET6 + if (rr->rr_type == T_AAAA && value == NULL) + { + result = NULL; + *statp = EX_DATAERR; + if (tTd(38, 40)) + sm_dprintf("\tbad T_AAAA conversion\n"); + goto cleanup; + } +# endif /* NETINET6 */ + if (tTd(38, 40)) + sm_dprintf("\tfound type %s (%d) value %s\n", + type != NULL ? type : "<UNKNOWN>", + rr->rr_type, + value != NULL ? value : "<NO VALUE>"); +# if _FFR_DNSMAP_MULTI + if (value != NULL && + (map->map_coldelim == '\0' || +# if _FFR_DNSMAP_MULTILIMIT + map->map_sizelimit == 1 || +# endif /* _FFR_DNSMAP_MULTILIMIT */ + bitset(MF_MATCHONLY, map->map_mflags))) + { + /* Only care about the first match */ + vp = newstr(value); + break; + } + else if (vp == NULL) + { + /* First result */ + vp = newstr(value); + } + else + { + /* concatenate the results */ + int sz; + char *new; + + sz = strlen(vp) + strlen(value) + 2; + new = xalloc(sz); + (void) sm_snprintf(new, sz, "%s%c%s", + vp, map->map_coldelim, value); + sm_free(vp); + vp = new; +# if _FFR_DNSMAP_MULTILIMIT + if (map->map_sizelimit > 0 && + ++resnum >= map->map_sizelimit) + break; +# endif /* _FFR_DNSMAP_MULTILIMIT */ + } +# else /* _FFR_DNSMAP_MULTI */ + vp = value; + break; +# endif /* _FFR_DNSMAP_MULTI */ + } + if (vp == NULL) + { + result = NULL; + *statp = EX_NOTFOUND; + if (tTd(38, 40)) + sm_dprintf("\tno match found\n"); + goto cleanup; + } + +# if _FFR_DNSMAP_MULTI + /* Cleanly truncate for rulesets */ + truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim); +# endif /* _FFR_DNSMAP_MULTI */ + + vsize = strlen(vp); + + if (LogLevel > 9) + sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s", + name, vp); + if (bitset(MF_MATCHONLY, map->map_mflags)) + result = map_rewrite(map, name, strlen(name), NULL); + else + result = map_rewrite(map, vp, vsize, av); + + cleanup: +# if _FFR_DNSMAP_MULTI + if (vp != NULL) + sm_free(vp); +# endif /* _FFR_DNSMAP_MULTI */ + if (r != NULL) + dns_free_data(r); + return result; +} +# endif /* DNSMAP */ +#endif /* NAMED_BIND */ + /* ** NDBM modules */ -#ifdef NDBM +#if NDBM /* ** NDBM_MAP_OPEN -- DBM-style map open @@ -872,14 +1358,14 @@ ndbm_map_open(map, mode) struct stat std, stp; if (tTd(38, 2)) - dprintf("ndbm_map_open(%s, %s, %d)\n", + sm_dprintf("ndbm_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); map->map_lockfd = -1; mode &= O_ACCMODE; /* do initial file and directory checks */ - snprintf(dirfile, sizeof dirfile, "%s.dir", map->map_file); - snprintf(pagfile, sizeof pagfile, "%s.pag", map->map_file); + (void) sm_strlcpyn(dirfile, sizeof dirfile, 2, map->map_file, ".dir"); + (void) sm_strlcpyn(pagfile, sizeof pagfile, 2, map->map_file, ".pag"); sff = SFF_ROOTOK|SFF_REGONLY; if (mode == O_RDWR) { @@ -898,31 +1384,11 @@ ndbm_map_open(map, mode) if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) sff |= SFF_SAFEDIRPATH; ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName, - sff, smode, &std); + sff, smode, &std); if (ret == 0) ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &stp); -# if !_FFR_REMOVE_AUTOREBUILD - if (ret == ENOENT && AutoRebuild && - bitset(MCF_REBUILDABLE, map->map_class->map_cflags) && - (bitset(MF_IMPL_NDBM, map->map_mflags) || - bitset(MF_ALIAS, map->map_mflags)) && - mode == O_RDONLY) - { - bool impl = bitset(MF_IMPL_NDBM, map->map_mflags); - - /* may be able to rebuild */ - map->map_mflags &= ~MF_IMPL_NDBM; - if (!rebuildaliases(map, TRUE)) - return FALSE; - if (impl) - return impl_map_open(map, O_RDONLY); - else - return ndbm_map_open(map, O_RDONLY); - } -# endif /* !_FFR_REMOVE_AUTOREBUILD */ - if (ret != 0) { char *prob = "unsafe"; @@ -931,11 +1397,11 @@ ndbm_map_open(map, mode) if (ret == ENOENT) prob = "missing"; if (tTd(38, 2)) - dprintf("\t%s map file: %d\n", prob, ret); + sm_dprintf("\t%s map file: %d\n", prob, ret); if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("dbm map \"%s\": %s map file %s", map->map_mname, prob, map->map_file); - return FALSE; + return false; } if (std.st_mode == ST_MODE_NOFILE) mode |= O_CREAT|O_EXCL; @@ -986,7 +1452,7 @@ ndbm_map_open(map, mode) errno = save_errno; syserr("ndbm_map_open: cannot create database %s", map->map_file); - return FALSE; + return false; } if (ftruncate(dirfd, (off_t) 0) < 0 || ftruncate(pagfd, (off_t) 0) < 0) @@ -997,7 +1463,7 @@ ndbm_map_open(map, mode) errno = save_errno; syserr("ndbm_map_open: cannot truncate %s.{dir,pag}", map->map_file); - return FALSE; + return false; } /* if new file, get "before" bits for later filechanged check */ @@ -1010,7 +1476,7 @@ ndbm_map_open(map, mode) errno = save_errno; syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file", map->map_file); - return FALSE; + return false; } /* have to save the lock for the duration (bletch) */ @@ -1029,8 +1495,8 @@ ndbm_map_open(map, mode) { save_errno = errno; if (bitset(MF_ALIAS, map->map_mflags) && - aliaswait(map, ".pag", FALSE)) - return TRUE; + aliaswait(map, ".pag", false)) + return true; # if !LOCK_ON_OPEN && !NOFTRUNCATE if (map->map_lockfd >= 0) (void) close(map->map_lockfd); @@ -1038,7 +1504,7 @@ ndbm_map_open(map, mode) errno = save_errno; if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("Cannot open DBM database %s", map->map_file); - return FALSE; + return false; } dfd = dbm_dirfno(dbm); pfd = dbm_pagfno(dbm); @@ -1053,7 +1519,7 @@ ndbm_map_open(map, mode) errno = 0; syserr("dbm map \"%s\": cannot support GDBM", map->map_mname); - return FALSE; + return false; } if (filechanged(dirfile, dfd, &std) || @@ -1068,7 +1534,7 @@ ndbm_map_open(map, mode) errno = save_errno; syserr("ndbm_map_open(%s): file changed after open", map->map_file); - return FALSE; + return false; } map->map_db1 = (ARBPTR_T) dbm; @@ -1091,8 +1557,8 @@ ndbm_map_open(map, mode) (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN); # endif /* LOCK_ON_OPEN */ if (bitset(MF_ALIAS, map->map_mflags) && - !aliaswait(map, ".pag", TRUE)) - return FALSE; + !aliaswait(map, ".pag", true)) + return false; } else { @@ -1107,14 +1573,20 @@ ndbm_map_open(map, mode) sm_syslog(LOG_ALERT, NOQID, "ownership change on %s failed: %s", - map->map_file, errstring(err)); + map->map_file, sm_errstring(err)); message("050 ownership change on %s failed: %s", - map->map_file, errstring(err)); + map->map_file, sm_errstring(err)); } +# else /* HASFCHOWN */ + sm_syslog(LOG_ALERT, NOQID, + "no fchown(): cannot change ownership on %s", + map->map_file); + message("050 no fchown(): cannot change ownership on %s", + map->map_file); # endif /* HASFCHOWN */ } } - return TRUE; + return true; } @@ -1135,7 +1607,7 @@ ndbm_map_lookup(map, name, av, statp) struct stat stbuf; if (tTd(38, 20)) - dprintf("ndbm_map_lookup(%s, %s)\n", + sm_dprintf("ndbm_map_lookup(%s, %s)\n", map->map_mname, name); key.dptr = name; @@ -1169,7 +1641,7 @@ lockdbm: if (map->map_class->map_open(map, omode)) { map->map_mflags |= MF_OPEN; - map->map_pid = getpid(); + map->map_pid = CurrentPid; if ((omode && O_ACCMODE) == O_RDWR) map->map_mflags |= MF_WRITABLE; goto lockdbm; @@ -1181,9 +1653,10 @@ lockdbm: extern MAPCLASS BogusMapClass; *statp = EX_TEMPFAIL; + map->map_orgclass = map->map_class; map->map_class = &BogusMapClass; map->map_mflags |= MF_OPEN; - map->map_pid = getpid(); + map->map_pid = CurrentPid; syserr("Cannot reopen NDBM database %s", map->map_file); } @@ -1231,7 +1704,7 @@ ndbm_map_store(map, lhs, rhs) char keybuf[MAXNAME + 1]; if (tTd(38, 12)) - dprintf("ndbm_map_store(%s, %s, %s)\n", + sm_dprintf("ndbm_map_store(%s, %s, %s)\n", map->map_mname, lhs, rhs); key.dsize = strlen(lhs); @@ -1268,23 +1741,23 @@ ndbm_map_store(map, lhs, rhs) datum old; old.dptr = ndbm_map_lookup(map, key.dptr, - (char **)NULL, &xstat); + (char **) NULL, &xstat); if (old.dptr != NULL && *(char *) old.dptr != '\0') { old.dsize = strlen(old.dptr); if (data.dsize + old.dsize + 2 > bufsiz) { if (buf != NULL) - sm_free(buf); + (void) sm_free(buf); bufsiz = data.dsize + old.dsize + 2; - buf = xalloc(bufsiz); + buf = sm_pmalloc_x(bufsiz); } - snprintf(buf, bufsiz, "%s,%s", - data.dptr, old.dptr); + (void) sm_strlcpyn(buf, bufsiz, 3, + data.dptr, ",", old.dptr); data.dsize = data.dsize + old.dsize + 1; data.dptr = buf; if (tTd(38, 9)) - dprintf("ndbm_map_store append=%s\n", + sm_dprintf("ndbm_map_store append=%s\n", data.dptr); } } @@ -1305,7 +1778,7 @@ ndbm_map_close(map) register MAP *map; { if (tTd(38, 9)) - dprintf("ndbm_map_close(%s, %s, %lx)\n", + sm_dprintf("ndbm_map_close(%s, %s, %lx)\n", map->map_mname, map->map_file, map->map_mflags); if (bitset(MF_WRITABLE, map->map_mflags)) @@ -1323,7 +1796,7 @@ ndbm_map_close(map) map->map_mflags |= MF_NOFOLDCASE; - (void) snprintf(buf, sizeof buf, "%010ld", curtime()); + (void) sm_snprintf(buf, sizeof buf, "%010ld", curtime()); ndbm_map_store(map, "YP_LAST_MODIFIED", buf); (void) gethostname(buf, sizeof buf); @@ -1353,7 +1826,7 @@ ndbm_map_close(map) ** NEWDB (Hash and BTree) Modules */ -#ifdef NEWDB +#if NEWDB /* ** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives. @@ -1395,7 +1868,7 @@ bt_map_open(map, mode) # endif /* DB_VERSION_MAJOR > 2 */ if (tTd(38, 2)) - dprintf("bt_map_open(%s, %s, %d)\n", + sm_dprintf("bt_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); # if DB_VERSION_MAJOR < 3 @@ -1424,7 +1897,7 @@ hash_map_open(map, mode) # endif /* DB_VERSION_MAJOR > 2 */ if (tTd(38, 2)) - dprintf("hash_map_open(%s, %s, %d)\n", + sm_dprintf("hash_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); # if DB_VERSION_MAJOR < 3 @@ -1467,10 +1940,10 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo) char buf[MAXNAME + 1]; /* do initial file and directory checks */ - (void) strlcpy(buf, map->map_file, sizeof buf - 3); + (void) sm_strlcpy(buf, map->map_file, sizeof buf - 3); i = strlen(buf); if (i < 3 || strcmp(&buf[i - 3], ".db") != 0) - (void) strlcat(buf, ".db", sizeof buf); + (void) sm_strlcat(buf, ".db", sizeof buf); mode &= O_ACCMODE; omode = mode; @@ -1494,27 +1967,6 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo) sff |= SFF_SAFEDIRPATH; i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st); -# if !_FFR_REMOVE_AUTOREBUILD - if (i == ENOENT && AutoRebuild && - bitset(MCF_REBUILDABLE, map->map_class->map_cflags) && - (bitset(MF_IMPL_HASH, map->map_mflags) || - bitset(MF_ALIAS, map->map_mflags)) && - mode == O_RDONLY) - { - bool impl = bitset(MF_IMPL_HASH, map->map_mflags); - - /* may be able to rebuild */ - map->map_mflags &= ~MF_IMPL_HASH; - if (!rebuildaliases(map, TRUE)) - return FALSE; - if (impl) - return impl_map_open(map, O_RDONLY); - else - return db_map_open(map, O_RDONLY, mapclassname, - dbtype, openinfo); - } -# endif /* !_FFR_REMOVE_AUTOREBUILD */ - if (i != 0) { char *prob = "unsafe"; @@ -1523,12 +1975,12 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo) if (i == ENOENT) prob = "missing"; if (tTd(38, 2)) - dprintf("\t%s map file: %s\n", prob, errstring(i)); + sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i)); errno = i; if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("%s map \"%s\": %s map file %s", mapclassname, map->map_mname, prob, buf); - return FALSE; + return false; } if (st.st_mode == ST_MODE_NOFILE) omode |= O_CREAT|O_EXCL; @@ -1552,7 +2004,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo) { if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("db_map_open: cannot pre-open database %s", buf); - return FALSE; + return false; } /* make sure no baddies slipped in just before the open... */ @@ -1562,7 +2014,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo) (void) close(fd); errno = save_errno; syserr("db_map_open(%s): file changed after pre-open", buf); - return FALSE; + return false; } /* if new file, get the "before" bits for later filechanged check */ @@ -1573,7 +2025,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo) errno = save_errno; syserr("db_map_open(%s): cannot fstat pre-opened file", buf); - return FALSE; + return false; } /* actually lock the pre-opened file */ @@ -1662,8 +2114,8 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo) if (db == NULL) { if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) && - aliaswait(map, ".db", FALSE)) - return TRUE; + aliaswait(map, ".db", false)) + return true; # if !LOCK_ON_OPEN if (map->map_lockfd >= 0) (void) close(map->map_lockfd); @@ -1672,7 +2124,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo) if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("Cannot open %s database %s", mapclassname, buf); - return FALSE; + return false; } # if DB_VERSION_MAJOR < 2 @@ -1695,7 +2147,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo) # endif /* !LOCK_ON_OPEN */ errno = save_errno; syserr("db_map_open(%s): file changed after open", buf); - return FALSE; + return false; } if (mode == O_RDWR) @@ -1720,10 +2172,16 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo) sm_syslog(LOG_ALERT, NOQID, "ownership change on %s failed: %s", - buf, errstring(err)); + buf, sm_errstring(err)); message("050 ownership change on %s failed: %s", - buf, errstring(err)); + buf, sm_errstring(err)); } +# else /* HASFCHOWN */ + sm_syslog(LOG_ALERT, NOQID, + "no fchown(): cannot change ownership on %s", + map->map_file); + message("050 no fchown(): cannot change ownership on %s", + map->map_file); # endif /* HASFCHOWN */ } } @@ -1740,9 +2198,9 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo) map->map_mtime = st.st_mtime; if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) && - !aliaswait(map, ".db", TRUE)) - return FALSE; - return TRUE; + !aliaswait(map, ".db", true)) + return false; + return true; } @@ -1771,13 +2229,13 @@ db_map_lookup(map, name, av, statp) memset(&val, '\0', sizeof val); if (tTd(38, 20)) - dprintf("db_map_lookup(%s, %s)\n", + sm_dprintf("db_map_lookup(%s, %s)\n", map->map_mname, name); i = strlen(map->map_file); if (i > MAXNAME) i = MAXNAME; - (void) strlcpy(buf, map->map_file, i + 1); + (void) sm_strlcpy(buf, map->map_file, i + 1); if (i > 3 && strcmp(&buf[i - 3], ".db") == 0) buf[i - 3] = '\0'; @@ -1812,7 +2270,7 @@ db_map_lookup(map, name, av, statp) if (map->map_class->map_open(map, omode)) { map->map_mflags |= MF_OPEN; - map->map_pid = getpid(); + map->map_pid = CurrentPid; if ((omode && O_ACCMODE) == O_RDWR) map->map_mflags |= MF_WRITABLE; db = (DB *) map->map_db2; @@ -1825,9 +2283,10 @@ db_map_lookup(map, name, av, statp) extern MAPCLASS BogusMapClass; *statp = EX_TEMPFAIL; + map->map_orgclass = map->map_class; map->map_class = &BogusMapClass; map->map_mflags |= MF_OPEN; - map->map_pid = getpid(); + map->map_pid = CurrentPid; syserr("Cannot reopen DB database %s", map->map_file); } @@ -1924,7 +2383,7 @@ db_map_store(map, lhs, rhs) memset(&data, '\0', sizeof data); if (tTd(38, 12)) - dprintf("db_map_store(%s, %s, %s)\n", + sm_dprintf("db_map_store(%s, %s, %s)\n", map->map_mname, lhs, rhs); key.size = strlen(lhs); @@ -1980,23 +2439,24 @@ db_map_store(map, lhs, rhs) memset(&old, '\0', sizeof old); old.data = db_map_lookup(map, key.data, - (char **)NULL, &status); + (char **) NULL, &status); if (old.data != NULL) { old.size = strlen(old.data); - if (data.size + old.size + 2 > (size_t)bufsiz) + if (data.size + old.size + 2 > (size_t) bufsiz) { if (buf != NULL) sm_free(buf); bufsiz = data.size + old.size + 2; - buf = xalloc(bufsiz); + buf = sm_pmalloc_x(bufsiz); } - snprintf(buf, bufsiz, "%s,%s", - (char *) data.data, (char *) old.data); + (void) sm_strlcpyn(buf, bufsiz, 3, + (char *) data.data, ",", + (char *) old.data); data.size = data.size + old.size + 1; data.data = buf; if (tTd(38, 9)) - dprintf("db_map_store append=%s\n", + sm_dprintf("db_map_store append=%s\n", (char *) data.data); } } @@ -2022,7 +2482,7 @@ db_map_close(map) register DB *db = map->map_db2; if (tTd(38, 9)) - dprintf("db_map_close(%s, %s, %lx)\n", + sm_dprintf("db_map_close(%s, %s, %lx)\n", map->map_mname, map->map_file, map->map_mflags); if (bitset(MF_WRITABLE, map->map_mflags)) @@ -2054,7 +2514,8 @@ db_map_close(map) ** process, do not close the map but recover ** the file descriptor. */ - if (map->map_pid != getpid()) + + if (map->map_pid != CurrentPid) { int fd = -1; @@ -2074,7 +2535,7 @@ db_map_close(map) ** NIS Modules */ -#ifdef NIS +#if NIS # ifndef YPERR_BUSY # define YPERR_BUSY 16 @@ -2095,23 +2556,15 @@ nis_map_open(map, mode) auto int vsize; if (tTd(38, 2)) - dprintf("nis_map_open(%s, %s, %d)\n", + sm_dprintf("nis_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); mode &= O_ACCMODE; if (mode != O_RDONLY) { /* issue a pseudo-error message */ -# ifdef ENOSYS - errno = ENOSYS; -# else /* ENOSYS */ -# ifdef EFTYPE - errno = EFTYPE; -# else /* EFTYPE */ - errno = ENXIO; -# endif /* EFTYPE */ -# endif /* ENOSYS */ - return FALSE; + errno = SM_EMAPCANTWRITE; + return false; } p = strchr(map->map_file, '@'); @@ -2133,7 +2586,7 @@ nis_map_open(map, mode) if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("421 4.3.5 NIS map %s specified, but NIS not running", map->map_file); - return FALSE; + return false; } } @@ -2142,7 +2595,7 @@ nis_map_open(map, mode) yperr = yp_match(map->map_domain, map->map_file, "@", 1, &vp, &vsize); if (tTd(38, 10)) - dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n", + sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n", map->map_domain, map->map_file, yperr_string(yperr)); if (vp != NULL) sm_free(vp); @@ -2158,9 +2611,9 @@ nis_map_open(map, mode) # if 0 if (!bitset(MF_ALIAS, map->map_mflags) || - aliaswait(map, NULL, TRUE)) + aliaswait(map, NULL, true)) # endif /* 0 */ - return TRUE; + return true; } if (!bitset(MF_OPTIONAL, map->map_mflags)) @@ -2169,7 +2622,7 @@ nis_map_open(map, mode) map->map_file, map->map_domain, yperr_string(yperr)); } - return FALSE; + return false; } @@ -2190,9 +2643,10 @@ nis_map_lookup(map, name, av, statp) int buflen; int yperr; char keybuf[MAXNAME + 1]; + char *SM_NONVOLATILE result = NULL; if (tTd(38, 20)) - dprintf("nis_map_lookup(%s, %s)\n", + sm_dprintf("nis_map_lookup(%s, %s)\n", map->map_mname, name); buflen = strlen(name); @@ -2213,11 +2667,7 @@ nis_map_lookup(map, name, av, statp) } if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags)) { - if (vp != NULL) - { - sm_free(vp); - vp = NULL; - } + SM_FREE_CLR(vp); buflen++; yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen, &vp, &vsize); @@ -2232,17 +2682,16 @@ nis_map_lookup(map, name, av, statp) sm_free(vp); return NULL; } - if (bitset(MF_MATCHONLY, map->map_mflags)) - return map_rewrite(map, name, strlen(name), NULL); - else - { - char *ret; - - ret = map_rewrite(map, vp, vsize, av); + SM_TRY + if (bitset(MF_MATCHONLY, map->map_mflags)) + result = map_rewrite(map, name, strlen(name), NULL); + else + result = map_rewrite(map, vp, vsize, av); + SM_FINALLY if (vp != NULL) sm_free(vp); - return ret; - } + SM_END_TRY + return result; } @@ -2260,20 +2709,20 @@ nis_getcanonname(name, hbsize, statp) auto int vsize; int keylen; int yperr; - static bool try0null = TRUE; - static bool try1null = TRUE; + static bool try0null = true; + static bool try1null = true; static char *yp_domain = NULL; char host_record[MAXLINE]; char cbuf[MAXNAME]; char nbuf[MAXNAME + 1]; if (tTd(38, 20)) - dprintf("nis_getcanonname(%s)\n", name); + sm_dprintf("nis_getcanonname(%s)\n", name); - if (strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) + if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) { *statp = EX_UNAVAILABLE; - return FALSE; + return false; } (void) shorten_hostname(nbuf); keylen = strlen(nbuf); @@ -2288,20 +2737,16 @@ nis_getcanonname(name, hbsize, statp) yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen, &vp, &vsize); if (yperr == 0) - try1null = FALSE; + try1null = false; } if (yperr == YPERR_KEY && try1null) { - if (vp != NULL) - { - sm_free(vp); - vp = NULL; - } + SM_FREE_CLR(vp); keylen++; yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen, &vp, &vsize); if (yperr == 0) - try0null = FALSE; + try0null = false; } if (yperr != 0) { @@ -2313,26 +2758,25 @@ nis_getcanonname(name, hbsize, statp) *statp = EX_UNAVAILABLE; if (vp != NULL) sm_free(vp); - return FALSE; + return false; } - (void) strlcpy(host_record, vp, sizeof host_record); + (void) sm_strlcpy(host_record, vp, sizeof host_record); sm_free(vp); if (tTd(38, 44)) - dprintf("got record `%s'\n", host_record); + sm_dprintf("got record `%s'\n", host_record); if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof cbuf)) { /* this should not happen, but.... */ *statp = EX_NOHOST; - return FALSE; + return false; } - if (hbsize <= strlen(cbuf)) + if (sm_strlcpy(name, cbuf, hbsize) >= hbsize) { *statp = EX_UNAVAILABLE; - return FALSE; + return false; } - (void) strlcpy(name, cbuf, hbsize); *statp = EX_OK; - return TRUE; + return true; } #endif /* NIS */ @@ -2342,7 +2786,7 @@ nis_getcanonname(name, hbsize, statp) ** This code donated by Sun Microsystems. */ -#ifdef NISPLUS +#if NISPLUS # undef NIS /* symbol conflict in nis.h */ # undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */ @@ -2368,14 +2812,14 @@ nisplus_map_open(map, mode) char qbuf[MAXLINE + NIS_MAXNAMELEN]; if (tTd(38, 2)) - dprintf("nisplus_map_open(%s, %s, %d)\n", + sm_dprintf("nisplus_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); mode &= O_ACCMODE; if (mode != O_RDONLY) { errno = EPERM; - return FALSE; + return false; } if (*map->map_file == '\0') @@ -2386,19 +2830,19 @@ nisplus_map_open(map, mode) /* set default NISPLUS Domain to $m */ map->map_domain = newstr(nisplus_default_domain()); if (tTd(38, 2)) - dprintf("nisplus_map_open(%s): using domain %s\n", + sm_dprintf("nisplus_map_open(%s): using domain %s\n", map->map_file, map->map_domain); } if (!PARTIAL_NAME(map->map_file)) { map->map_domain = newstr(""); - snprintf(qbuf, sizeof qbuf, "%s", map->map_file); + (void) sm_strlcpy(qbuf, map->map_file, sizeof qbuf); } else { /* check to see if this map actually exists */ - snprintf(qbuf, sizeof qbuf, "%s.%s", - map->map_file, map->map_domain); + (void) sm_strlcpyn(qbuf, sizeof qbuf, 3, + map->map_file, ".", map->map_domain); } retry_cnt = 0; @@ -2416,7 +2860,7 @@ nisplus_map_open(map, mode) if (retry_cnt++ > 4) { errno = EAGAIN; - return FALSE; + return false; } /* try not to overwhelm hosed server */ sleep(2); @@ -2430,7 +2874,7 @@ nisplus_map_open(map, mode) nis_sperrno(res->status)); # endif /* 0 */ errno = EAGAIN; - return FALSE; + return false; } } @@ -2438,7 +2882,7 @@ nisplus_map_open(map, mode) (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ)) { if (tTd(38, 10)) - dprintf("nisplus_map_open: %s is not a table\n", qbuf); + sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf); # if 0 if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("421 4.0.0 %s.%s: %s is not a table", @@ -2446,7 +2890,7 @@ nisplus_map_open(map, mode) nis_sperrno(res->status)); # endif /* 0 */ errno = EBADF; - return FALSE; + return false; } /* default key column is column 0 */ if (map->map_keycolnm == NULL) @@ -2455,7 +2899,7 @@ nisplus_map_open(map, mode) max_col = COL_MAX(res); /* verify the key column exist */ - for (i = 0; i< max_col; i++) + for (i = 0; i < max_col; i++) { if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0) break; @@ -2463,17 +2907,17 @@ nisplus_map_open(map, mode) if (i == max_col) { if (tTd(38, 2)) - dprintf("nisplus_map_open(%s): can not find key column %s\n", + sm_dprintf("nisplus_map_open(%s): can not find key column %s\n", map->map_file, map->map_keycolnm); errno = ENOENT; - return FALSE; + return false; } /* default value column is the last column */ if (map->map_valcolnm == NULL) { map->map_valcolno = max_col - 1; - return TRUE; + return true; } for (i = 0; i< max_col; i++) @@ -2481,15 +2925,15 @@ nisplus_map_open(map, mode) if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0) { map->map_valcolno = i; - return TRUE; + return true; } } if (tTd(38, 2)) - dprintf("nisplus_map_open(%s): can not find column %s\n", + sm_dprintf("nisplus_map_open(%s): can not find column %s\n", map->map_file, map->map_keycolnm); errno = ENOENT; - return FALSE; + return false; } @@ -2513,7 +2957,7 @@ nisplus_map_lookup(map, name, av, statp) nis_result *result; if (tTd(38, 20)) - dprintf("nisplus_map_lookup(%s, %s)\n", + sm_dprintf("nisplus_map_lookup(%s, %s)\n", map->map_mname, name); if (!bitset(MF_OPEN, map->map_mflags)) @@ -2521,7 +2965,7 @@ nisplus_map_lookup(map, name, av, statp) if (nisplus_map_open(map, O_RDONLY)) { map->map_mflags |= MF_OPEN; - map->map_pid = getpid(); + map->map_pid = CurrentPid; } else { @@ -2569,15 +3013,15 @@ nisplus_map_lookup(map, name, av, statp) /* construct the query */ if (PARTIAL_NAME(map->map_file)) - snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s", + (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s", map->map_keycolnm, search_key, map->map_file, map->map_domain); else - snprintf(qbuf, sizeof qbuf, "[%s=%s],%s", + (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s", map->map_keycolnm, search_key, map->map_file); if (tTd(38, 20)) - dprintf("qbuf=%s\n", qbuf); + sm_dprintf("qbuf=%s\n", qbuf); result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL); if (result->status == NIS_SUCCESS) { @@ -2593,7 +3037,7 @@ nisplus_map_lookup(map, name, av, statp) /* ignore second entry */ if (tTd(38, 20)) - dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n", + sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n", name, count); } @@ -2603,7 +3047,7 @@ nisplus_map_lookup(map, name, av, statp) p = ""; vsize = strlen(p); if (tTd(38, 20)) - dprintf("nisplus_map_lookup(%s), found %s\n", + sm_dprintf("nisplus_map_lookup(%s), found %s\n", name, p); if (bitset(MF_MATCHONLY, map->map_mflags)) str = map_rewrite(map, name, strlen(name), NULL); @@ -2626,7 +3070,7 @@ nisplus_map_lookup(map, name, av, statp) } } if (tTd(38, 20)) - dprintf("nisplus_map_lookup(%s), failed\n", name); + sm_dprintf("nisplus_map_lookup(%s), failed\n", name); nis_freeresult(result); return NULL; } @@ -2650,39 +3094,39 @@ nisplus_getcanonname(name, hbsize, statp) char nbuf[MAXNAME + 1]; char qbuf[MAXLINE + NIS_MAXNAMELEN]; - if (strlen(name) >= sizeof nbuf) + if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) { *statp = EX_UNAVAILABLE; - return FALSE; + return false; } - (void) strlcpy(nbuf, name, sizeof nbuf); (void) shorten_hostname(nbuf); p = strchr(nbuf, '.'); if (p == NULL) { /* single token */ - snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir", nbuf); + (void) sm_snprintf(qbuf, sizeof qbuf, + "[name=%s],hosts.org_dir", nbuf); } else if (p[1] != '\0') { /* multi token -- take only first token in nbuf */ *p = '\0'; - snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir.%s", - nbuf, &p[1]); + (void) sm_snprintf(qbuf, sizeof qbuf, + "[name=%s],hosts.org_dir.%s", nbuf, &p[1]); } else { *statp = EX_NOHOST; - return FALSE; + return false; } if (tTd(38, 20)) - dprintf("\nnisplus_getcanoname(%s), qbuf=%s\n", - name, qbuf); + sm_dprintf("\nnisplus_getcanoname(%s), qbuf=%s\n", + name, qbuf); result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH, - NULL, NULL); + NULL, NULL); if (result->status == NIS_SUCCESS) { @@ -2698,20 +3142,20 @@ nisplus_getcanonname(name, hbsize, statp) /* ignore second entry */ if (tTd(38, 20)) - dprintf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n", - name, count); + sm_dprintf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n", + name, count); } if (tTd(38, 20)) - dprintf("nisplus_getcanoname(%s), found in directory \"%s\"\n", - name, (NIS_RES_OBJECT(result))->zo_domain); + sm_dprintf("nisplus_getcanoname(%s), found in directory \"%s\"\n", + name, (NIS_RES_OBJECT(result))->zo_domain); vp = ((NIS_RES_OBJECT(result))->EN_col(0)); vsize = strlen(vp); if (tTd(38, 20)) - dprintf("nisplus_getcanonname(%s), found %s\n", - name, vp); + sm_dprintf("nisplus_getcanonname(%s), found %s\n", + name, vp); if (strchr(vp, '.') != NULL) { domain = ""; @@ -2725,15 +3169,16 @@ nisplus_getcanonname(name, hbsize, statp) if (hbsize > vsize + (int) strlen(domain) + 1) { if (domain[0] == '\0') - (void) strlcpy(name, vp, hbsize); + (void) sm_strlcpy(name, vp, hbsize); else - snprintf(name, hbsize, "%s.%s", vp, domain); + (void) sm_snprintf(name, hbsize, + "%s.%s", vp, domain); *statp = EX_OK; } else *statp = EX_NOHOST; nis_freeresult(result); - return TRUE; + return true; } else { @@ -2745,10 +3190,10 @@ nisplus_getcanonname(name, hbsize, statp) *statp = EX_UNAVAILABLE; } if (tTd(38, 20)) - dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n", - name, result->status, *statp); + sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n", + name, result->status, *statp); nis_freeresult(result); - return FALSE; + return false; } char * @@ -2761,7 +3206,7 @@ nisplus_default_domain() return default_domain; p = nis_local_directory(); - snprintf(default_domain, sizeof default_domain, "%s", p); + (void) sm_strlcpy(default_domain, p, sizeof default_domain); return default_domain; } @@ -2776,11 +3221,13 @@ nisplus_default_domain() #if defined(LDAPMAP) || defined(PH_MAP) -# ifdef PH_MAP +# if PH_MAP # define ph_map_dequote ldapmap_dequote # endif /* PH_MAP */ -char * +static char *ldapmap_dequote __P((char *)); + +static char * ldapmap_dequote(str) char *str; { @@ -2806,9 +3253,9 @@ ldapmap_dequote(str) } #endif /* defined(LDAPMAP) || defined(PH_MAP) */ -#ifdef LDAPMAP +#if LDAPMAP -LDAPMAP_STRUCT *LDAPDefaults = NULL; +static SM_LDAP_STRUCT *LDAPDefaults = NULL; /* ** LDAPMAP_OPEN -- open LDAP map @@ -2823,11 +3270,11 @@ ldapmap_open(map, mode) MAP *map; int mode; { - LDAPMAP_STRUCT *lmap; + SM_LDAP_STRUCT *lmap; STAB *s; if (tTd(38, 2)) - dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode); + sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode); mode &= O_ACCMODE; @@ -2835,127 +3282,47 @@ ldapmap_open(map, mode) if (mode != O_RDONLY) { /* issue a pseudo-error message */ -# ifdef ENOSYS - errno = ENOSYS; -# else /* ENOSYS */ -# ifdef EFTYPE - errno = EFTYPE; -# else /* EFTYPE */ - errno = ENXIO; -# endif /* EFTYPE */ -# endif /* ENOSYS */ - return FALSE; - } - - /* Comma separate if used as an alias file */ - if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags)) - map->map_coldelim = ','; + errno = SM_EMAPCANTWRITE; + return false; + } - lmap = (LDAPMAP_STRUCT *) map->map_db1; + lmap = (SM_LDAP_STRUCT *) map->map_db1; s = ldapmap_findconn(lmap); if (s->s_lmap != NULL) { /* Already have a connection open to this LDAP server */ - lmap->ldap_ld = ((LDAPMAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld; + lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld; /* Add this map as head of linked list */ lmap->ldap_next = s->s_lmap; s->s_lmap = map; if (tTd(38, 2)) - dprintf("using cached connection\n"); - return TRUE; + sm_dprintf("using cached connection\n"); + return true; } if (tTd(38, 2)) - dprintf("opening new connection\n"); + sm_dprintf("opening new connection\n"); /* No connection yet, connect */ - if (!ldapmap_start(map)) - return FALSE; - - /* Save connection for reuse */ - s->s_lmap = map; - return TRUE; -} - -/* -** LDAPMAP_START -- actually connect to an LDAP server -** -** Parameters: -** map -- the map being opened. -** -** Returns: -** TRUE if connection is successful, FALSE otherwise. -** -** Side Effects: -** Populates lmap->ldap_ld. -*/ - -static jmp_buf LDAPTimeout; - -static bool -ldapmap_start(map) - MAP *map; -{ - register int bind_result; - int save_errno; - register EVENT *ev = NULL; - LDAPMAP_STRUCT *lmap; - LDAP *ld; - - if (tTd(38, 2)) - dprintf("ldapmap_start(%s)\n", map->map_mname); - - lmap = (LDAPMAP_STRUCT *) map->map_db1; - - if (tTd(38,9)) - dprintf("ldapmap_start(%s, %d)\n", - lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host, - lmap->ldap_port); - -# if USE_LDAP_INIT - ld = ldap_init(lmap->ldap_host, lmap->ldap_port); - save_errno = errno; -# else /* USE_LDAP_INIT */ - /* - ** If using ldap_open(), the actual connection to the server - ** happens now so we need the timeout here. For ldap_init(), - ** the connection happens at bind time. - */ - - /* set the timeout */ - if (lmap->ldap_timeout.tv_sec != 0) + if (!sm_ldap_start(map->map_mname, lmap)) { - if (setjmp(LDAPTimeout) != 0) + if (errno == ETIMEDOUT) { if (LogLevel > 1) sm_syslog(LOG_NOTICE, CurEnv->e_id, "timeout conning to LDAP server %.100s", lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host); - return FALSE; } - ev = setevent(lmap->ldap_timeout.tv_sec, ldaptimeout, 0); - } - - ld = ldap_open(lmap->ldap_host, lmap->ldap_port); - save_errno = errno; - - /* clear the event if it has not sprung */ - if (ev != NULL) - clrevent(ev); -# endif /* USE_LDAP_INIT */ - errno = save_errno; - if (ld == NULL) - { if (!bitset(MF_OPTIONAL, map->map_mflags)) { if (bitset(MF_NODEFER, map->map_mflags)) syserr("%s failed to %s in map %s", # if USE_LDAP_INIT - "ldap_init", + "ldap_init/ldap_bind", # else /* USE_LDAP_INIT */ "ldap_open", # endif /* USE_LDAP_INIT */ @@ -2965,7 +3332,7 @@ ldapmap_start(map) else syserr("421 4.0.0 %s failed to %s in map %s", # if USE_LDAP_INIT - "ldap_init", + "ldap_init/ldap_bind", # else /* USE_LDAP_INIT */ "ldap_open", # endif /* USE_LDAP_INIT */ @@ -2973,86 +3340,12 @@ ldapmap_start(map) : lmap->ldap_host, map->map_mname); } - return FALSE; - } - - ldapmap_setopts(ld, lmap); - -# if USE_LDAP_INIT - /* - ** If using ldap_init(), the actual connection to the server - ** happens at ldap_bind_s() so we need the timeout here. - */ - - /* set the timeout */ - if (lmap->ldap_timeout.tv_sec != 0) - { - if (setjmp(LDAPTimeout) != 0) - { - if (LogLevel > 1) - sm_syslog(LOG_NOTICE, CurEnv->e_id, - "timeout conning to LDAP server %.100s", - lmap->ldap_host == NULL ? "localhost" - : lmap->ldap_host); - return FALSE; - } - ev = setevent(lmap->ldap_timeout.tv_sec, ldaptimeout, 0); + return false; } -# endif /* USE_LDAP_INIT */ -# ifdef LDAP_AUTH_KRBV4 - if (lmap->ldap_method == LDAP_AUTH_KRBV4 && - lmap->ldap_secret != NULL) - { - /* - ** Need to put ticket in environment here instead of - ** during parseargs as there may be different tickets - ** for different LDAP connections. - */ - - (void) putenv(lmap->ldap_secret); - } -# endif /* LDAP_AUTH_KRBV4 */ - - bind_result = ldap_bind_s(ld, lmap->ldap_binddn, - lmap->ldap_secret, lmap->ldap_method); - -# if USE_LDAP_INIT - /* clear the event if it has not sprung */ - if (ev != NULL) - clrevent(ev); -# endif /* USE_LDAP_INIT */ - - if (bind_result != LDAP_SUCCESS) - { - errno = bind_result + E_LDAPBASE; - if (!bitset(MF_OPTIONAL, map->map_mflags)) - { - syserr("421 4.0.0 Cannot bind to map %s in ldap server %s", - map->map_mname, - lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host); - } - return FALSE; - } - - /* We need to cast ld into the map structure */ - lmap->ldap_ld = ld; - return TRUE; -} - -/* ARGSUSED */ -static void -ldaptimeout(sig_no) - int sig_no; -{ - /* - ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD - ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE - ** DOING. - */ - - errno = ETIMEDOUT; - longjmp(LDAPTimeout, 1); + /* Save connection for reuse */ + s->s_lmap = map; + return true; } /* @@ -3063,20 +3356,20 @@ void ldapmap_close(map) MAP *map; { - LDAPMAP_STRUCT *lmap; + SM_LDAP_STRUCT *lmap; STAB *s; if (tTd(38, 2)) - dprintf("ldapmap_close(%s)\n", map->map_mname); + sm_dprintf("ldapmap_close(%s)\n", map->map_mname); - lmap = (LDAPMAP_STRUCT *) map->map_db1; + lmap = (SM_LDAP_STRUCT *) map->map_db1; /* Check if already closed */ if (lmap->ldap_ld == NULL) return; /* Close the LDAP connection */ - ldap_unbind(lmap->ldap_ld); + sm_ldap_close(lmap); /* Mark all the maps that share the connection as closed */ s = ldapmap_findconn(lmap); @@ -3086,11 +3379,10 @@ ldapmap_close(map) MAP *smap = s->s_lmap; if (tTd(38, 2) && smap != map) - dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n", - map->map_mname, smap->map_mname); - + sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n", + map->map_mname, smap->map_mname); smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE); - lmap = (LDAPMAP_STRUCT *) smap->map_db1; + lmap = (SM_LDAP_STRUCT *) smap->map_db1; lmap->ldap_ld = NULL; s->s_lmap = lmap->ldap_next; lmap->ldap_next = NULL; @@ -3099,11 +3391,11 @@ ldapmap_close(map) # ifdef SUNET_ID /* -** SUNET_ID_HASH -- Convert a string to it's Sunet_id canonical form +** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form ** This only makes sense at Stanford University. */ -char * +static char * sunet_id_hash(str) char *str; { @@ -3147,21 +3439,19 @@ ldapmap_lookup(map, name, av, statp) int msgid; int ret; int vsize; - char *fp, *vp; - char *p, *q; + char *vp, *p; char *result = NULL; - LDAPMAP_STRUCT *lmap = NULL; + SM_LDAP_STRUCT *lmap = NULL; char keybuf[MAXNAME + 1]; - char filter[LDAPMAP_MAX_FILTER + 1]; if (tTd(38, 20)) - dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name); + sm_dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name); /* Get ldap struct pointer from map */ - lmap = (LDAPMAP_STRUCT *) map->map_db1; - ldapmap_setopts(lmap->ldap_ld, lmap); + lmap = (SM_LDAP_STRUCT *) map->map_db1; + sm_ldap_setopts(lmap->ldap_ld, lmap); - (void) strlcpy(keybuf, name, sizeof keybuf); + (void) sm_strlcpy(keybuf, name, sizeof keybuf); if (!bitset(MF_NOFOLDCASE, map->map_mflags)) { @@ -3172,91 +3462,35 @@ ldapmap_lookup(map, name, av, statp) # endif /* SUNET_ID */ } - /* substitute keybuf into filter, perhaps multiple times */ - memset(filter, '\0', sizeof filter); - fp = filter; - p = lmap->ldap_filter; - while ((q = strchr(p, '%')) != NULL) - { - if (q[1] == 's') - { - snprintf(fp, SPACELEFT(filter, fp), "%.*s%s", - (int) (q - p), p, keybuf); - fp += strlen(fp); - p = q + 2; - } - else if (q[1] == '0') - { - char *k = keybuf; - - snprintf(fp, SPACELEFT(filter, fp), "%.*s", - (int) (q - p), p); - fp += strlen(fp); - p = q + 2; - - /* Properly escape LDAP special characters */ - while (SPACELEFT(filter, fp) > 0 && - *k != '\0') - { - if (*k == '*' || *k == '(' || - *k == ')' || *k == '\\') - { - (void) strlcat(fp, - (*k == '*' ? "\\2A" : - (*k == '(' ? "\\28" : - (*k == ')' ? "\\29" : - (*k == '\\' ? "\\5C" : - "\00")))), - SPACELEFT(filter, fp)); - fp += strlen(fp); - k++; - } - else - *fp++ = *k++; - } - } - else - { - snprintf(fp, SPACELEFT(filter, fp), "%.*s", - (int) (q - p + 1), p); - p = q + (q[1] == '%' ? 2 : 1); - fp += strlen(fp); - } - } - snprintf(fp, SPACELEFT(filter, fp), "%s", p); - if (tTd(38, 20)) - dprintf("ldap search filter=%s\n", filter); - - lmap->ldap_res = NULL; - msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base, lmap->ldap_scope, - filter, - (lmap->ldap_attr[0] == NULL ? NULL : - lmap->ldap_attr), - lmap->ldap_attrsonly); + msgid = sm_ldap_search(lmap, keybuf); if (msgid == -1) { int save_errno; - errno = ldapmap_geterrno(lmap->ldap_ld) + E_LDAPBASE; + errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE; save_errno = errno; if (!bitset(MF_OPTIONAL, map->map_mflags)) { if (bitset(MF_NODEFER, map->map_mflags)) syserr("Error in ldap_search using %s in map %s", - filter, map->map_mname); + keybuf, map->map_mname); else syserr("421 4.0.0 Error in ldap_search using %s in map %s", - filter, map->map_mname); + keybuf, map->map_mname); } *statp = EX_TEMPFAIL; -#ifdef LDAP_SERVER_DOWN - errno = save_errno; - if (errno == LDAP_SERVER_DOWN + E_LDAPBASE) + errno = save_errno - E_LDAPBASE; + switch (errno) { +#ifdef LDAP_SERVER_DOWN + case LDAP_SERVER_DOWN: +#endif /* LDAP_SERVER_DOWN */ + case LDAP_TIMEOUT: + case LDAP_UNAVAILABLE: /* server disappeared, try reopen on next search */ ldapmap_close(map); + break; } -#endif /* LDAP_SERVER_DOWN */ errno = save_errno; return NULL; } @@ -3287,9 +3521,9 @@ ldapmap_lookup(map, name, av, statp) } (void) ldap_abandon(lmap->ldap_ld, msgid); if (vp != NULL) - sm_free(vp); + sm_free(vp); /* XXX */ if (tTd(38, 25)) - dprintf("ldap search found multiple on a single match query\n"); + sm_dprintf("ldap search found multiple on a single match query\n"); return NULL; } } @@ -3341,7 +3575,7 @@ ldapmap_lookup(map, name, av, statp) attr); if (vals == NULL) { - errno = ldapmap_geterrno(lmap->ldap_ld); + errno = sm_ldap_geterrno(lmap->ldap_ld); if (errno == LDAP_SUCCESS) continue; @@ -3370,7 +3604,7 @@ ldapmap_lookup(map, name, av, statp) (void) ldap_abandon(lmap->ldap_ld, msgid); if (vp != NULL) - sm_free(vp); + sm_free(vp); /* XXX */ return NULL; } } @@ -3420,7 +3654,18 @@ ldapmap_lookup(map, name, av, statp) continue; } - vp = newstr(vals[0]); + vsize = strlen(vals[0]) + 1; + if (lmap->ldap_attrsep != '\0') + vsize += strlen(attr) + 1; + vp = xalloc(vsize); + if (lmap->ldap_attrsep != '\0') + sm_snprintf(vp, vsize, + "%s%c%s", + attr, + lmap->ldap_attrsep, + vals[0]); + else + sm_strlcpy(vp, vals[0], vsize); ldap_value_free(vals); # if USING_NETSCAPE_LDAP ldap_memfree(attr); @@ -3438,10 +3683,11 @@ ldapmap_lookup(map, name, av, statp) vsize = strlen(vp) + strlen(attr) + 2; tmp = xalloc(vsize); - snprintf(tmp, vsize, "%s%c%s", - vp, map->map_coldelim, - attr); - sm_free(vp); + (void) sm_snprintf(tmp, + vsize, "%s%c%s", + vp, map->map_coldelim, + attr); + sm_free(vp); /* XXX */ vp = tmp; } # if USING_NETSCAPE_LDAP @@ -3458,15 +3704,25 @@ ldapmap_lookup(map, name, av, statp) vsize = 0; for (i = 0; vals[i] != NULL; i++) + { vsize += strlen(vals[i]) + 1; + if (lmap->ldap_attrsep != '\0') + vsize += strlen(attr) + 1; + } vp_tmp = xalloc(vsize); *vp_tmp = '\0'; p = vp_tmp; for (i = 0; vals[i] != NULL; i++) { - p += strlcpy(p, vals[i], - vsize - (p - vp_tmp)); + if (lmap->ldap_attrsep != '\0') + { + p += sm_strlcpy(p, attr, + vsize - (p - vp_tmp)); + *p++ = lmap->ldap_attrsep; + } + p += sm_strlcpy(p, vals[i], + vsize - (p - vp_tmp)); if (p >= vp_tmp + vsize) syserr("ldapmap_lookup: Internal error: buffer too small for LDAP values"); if (vals[i + 1] != NULL) @@ -3484,14 +3740,14 @@ ldapmap_lookup(map, name, av, statp) } vsize = strlen(vp) + strlen(vp_tmp) + 2; tmp = xalloc(vsize); - snprintf(tmp, vsize, "%s%c%s", + (void) sm_snprintf(tmp, vsize, "%s%c%s", vp, map->map_coldelim, vp_tmp); - sm_free(vp); - sm_free(vp_tmp); + sm_free(vp); /* XXX */ + sm_free(vp_tmp); /* XXX */ vp = tmp; } - errno = ldapmap_geterrno(lmap->ldap_ld); + errno = sm_ldap_geterrno(lmap->ldap_ld); /* ** We check errno != LDAP_DECODING_ERROR since @@ -3524,7 +3780,7 @@ ldapmap_lookup(map, name, av, statp) } (void) ldap_abandon(lmap->ldap_ld, msgid); if (vp != NULL) - sm_free(vp); + sm_free(vp); /* XXX */ return NULL; } @@ -3532,7 +3788,7 @@ ldapmap_lookup(map, name, av, statp) if (map->map_coldelim == '\0' && vp != NULL) break; } - errno = ldapmap_geterrno(lmap->ldap_ld); + errno = sm_ldap_geterrno(lmap->ldap_ld); if (errno != LDAP_SUCCESS && errno != LDAP_DECODING_ERROR) { /* Must be an error */ @@ -3554,7 +3810,7 @@ ldapmap_lookup(map, name, av, statp) } (void) ldap_abandon(lmap->ldap_ld, msgid); if (vp != NULL) - sm_free(vp); + sm_free(vp); /* XXX */ return NULL; } ldap_msgfree(lmap->ldap_res); @@ -3579,7 +3835,7 @@ ldapmap_lookup(map, name, av, statp) lmap->ldap_res = NULL; } if (vp != NULL) - sm_free(vp); + sm_free(vp); /* XXX */ return NULL; } *statp = EX_OK; @@ -3588,12 +3844,11 @@ ldapmap_lookup(map, name, av, statp) if (ret == 0) errno = ETIMEDOUT; else - errno = ldapmap_geterrno(lmap->ldap_ld); + errno = sm_ldap_geterrno(lmap->ldap_ld); if (errno != LDAP_SUCCESS) { int save_errno; - /* Must be an error */ if (ret != 0) errno += E_LDAPBASE; save_errno = errno; @@ -3609,23 +3864,24 @@ ldapmap_lookup(map, name, av, statp) } *statp = EX_TEMPFAIL; if (vp != NULL) - sm_free(vp); -#ifdef LDAP_SERVER_DOWN - errno = save_errno; - if (errno == LDAP_SERVER_DOWN + E_LDAPBASE) + sm_free(vp); /* XXX */ + + errno = save_errno - E_LDAPBASE; + switch (errno) { +#ifdef LDAP_SERVER_DOWN + case LDAP_SERVER_DOWN: +#endif /* LDAP_SERVER_DOWN */ + case LDAP_TIMEOUT: + case LDAP_UNAVAILABLE: /* server disappeared, try reopen on next search */ ldapmap_close(map); + break; } -#endif /* LDAP_SERVER_DOWN */ errno = save_errno; return NULL; } - /* Did we match anything? */ - if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags)) - return NULL; - /* ** If MF_NOREWRITE, we are special map which doesn't ** actually return a map value. Instead, we don't free @@ -3636,10 +3892,15 @@ ldapmap_lookup(map, name, av, statp) if (bitset(MF_NOREWRITE, map->map_mflags)) { if (vp != NULL) - sm_free(vp); + sm_free(vp); /* XXX */ + *statp = EX_OK; return ""; } + /* Did we match anything? */ + if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags)) + return NULL; + if (*statp == EX_OK) { if (LogLevel > 9) @@ -3654,7 +3915,7 @@ ldapmap_lookup(map, name, av, statp) result = map_rewrite(map, vp, strlen(vp), av); } if (vp != NULL) - sm_free(vp); + sm_free(vp); /* XXX */ } return result; } @@ -3673,126 +3934,39 @@ ldapmap_lookup(map, name, av, statp) ** ** Returns: ** Symbol table entry for the LDAP connection. -** */ static STAB * ldapmap_findconn(lmap) - LDAPMAP_STRUCT *lmap; + SM_LDAP_STRUCT *lmap; { - int len; char *nbuf; - STAB *s; - - len = (lmap->ldap_host == NULL ? strlen("localhost") : - strlen(lmap->ldap_host)) + 1 + 8 + 1 + - (lmap->ldap_binddn == NULL ? 0 : strlen(lmap->ldap_binddn)) + - 1 + - (lmap->ldap_secret == NULL ? 0 : strlen(lmap->ldap_secret)) + - 8 + 1; - nbuf = xalloc(len); - snprintf(nbuf, len, "%s%c%d%c%s%c%s%d", - (lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host), - CONDELSE, - lmap->ldap_port, - CONDELSE, - (lmap->ldap_binddn == NULL ? "" : lmap->ldap_binddn), - CONDELSE, - (lmap->ldap_secret == NULL ? "" : lmap->ldap_secret), - (int) getpid()); - s = stab(nbuf, ST_LMAP, ST_ENTER); - sm_free(nbuf); + STAB *SM_NONVOLATILE s = NULL; + + nbuf = sm_stringf_x("%s%c%d%c%s%c%s%d", + (lmap->ldap_host == NULL ? "localhost" + : lmap->ldap_host), + CONDELSE, + lmap->ldap_port, + CONDELSE, + (lmap->ldap_binddn == NULL ? "" + : lmap->ldap_binddn), + CONDELSE, + (lmap->ldap_secret == NULL ? "" + : lmap->ldap_secret), + (int) CurrentPid); + SM_TRY + s = stab(nbuf, ST_LMAP, ST_ENTER); + SM_FINALLY + sm_free(nbuf); + SM_END_TRY return s; } /* -** LDAPMAP_SETOPTS -- set LDAP options -** -** Parameters: -** ld -- LDAP session handle -** lmap -- LDAP map information -** -** Returns: -** None. -** -*/ - -static void -ldapmap_setopts(ld, lmap) - LDAP *ld; - LDAPMAP_STRUCT *lmap; -{ -# if USE_LDAP_SET_OPTION - ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref); - if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options)) - ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON); - else - ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); - ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit); - ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit); -# else /* USE_LDAP_SET_OPTION */ - /* From here on in we can use ldap internal timelimits */ - ld->ld_deref = lmap->ldap_deref; - ld->ld_options = lmap->ldap_options; - ld->ld_sizelimit = lmap->ldap_sizelimit; - ld->ld_timelimit = lmap->ldap_timelimit; -# endif /* USE_LDAP_SET_OPTION */ -} -/* -** LDAPMAP_GETERRNO -- get ldap errno value -** -** Parameters: -** ld -- LDAP session handle -** -** Returns: -** LDAP errno. -** -*/ - -static int -ldapmap_geterrno(ld) - LDAP *ld; -{ - int err = LDAP_SUCCESS; - -# if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 - (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err); -# else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ -# ifdef LDAP_OPT_SIZELIMIT - err = ldap_get_lderrno(ld, NULL, NULL); -# else /* LDAP_OPT_SIZELIMIT */ - err = ld->ld_errno; - - /* - ** Reset value to prevent lingering LDAP_DECODING_ERROR due to - ** OpenLDAP 1.X's hack (see above) - */ - - ld->ld_errno = LDAP_SUCCESS; -# endif /* LDAP_OPT_SIZELIMIT */ -# endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ - return err; -} - -/* -** LDAPX_MAP_PARSEARGS -- print warning about use of ldapx map. -*/ - -bool -ldapx_map_parseargs(map, args) - MAP *map; - char *args; -{ - printf("Warning: The \"ldapx\" map class is deprecated and will be removed in a future\n"); - printf(" version. Use the \"ldap\" map class instead for map \"%s\".\n", - map->map_mname); - return ldapmap_parseargs(map, args); -} - -/* ** LDAPMAP_PARSEARGS -- parse ldap map definition args. */ -struct lamvalues LDAPAuthMethods[] = +static struct lamvalues LDAPAuthMethods[] = { { "none", LDAP_AUTH_NONE }, { "simple", LDAP_AUTH_SIMPLE }, @@ -3802,7 +3976,7 @@ struct lamvalues LDAPAuthMethods[] = { NULL, 0 } }; -struct ladvalues LDAPAliasDereference[] = +static struct ladvalues LDAPAliasDereference[] = { { "never", LDAP_DEREF_NEVER }, { "always", LDAP_DEREF_ALWAYS }, @@ -3811,7 +3985,7 @@ struct ladvalues LDAPAliasDereference[] = { NULL, 0 } }; -struct lssvalues LDAPSearchScope[] = +static struct lssvalues LDAPSearchScope[] = { { "base", LDAP_SCOPE_BASE }, { "one", LDAP_SCOPE_ONELEVEL }, @@ -3824,25 +3998,26 @@ ldapmap_parseargs(map, args) MAP *map; char *args; { - bool secretread = TRUE; + bool secretread = true; int i; register char *p = args; - LDAPMAP_STRUCT *lmap; + SM_LDAP_STRUCT *lmap; struct lamvalues *lam; struct ladvalues *lad; struct lssvalues *lss; + char ldapfilt[MAXLINE]; char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD]; /* Get ldap struct pointer from map */ - lmap = (LDAPMAP_STRUCT *) map->map_db1; + lmap = (SM_LDAP_STRUCT *) map->map_db1; /* Check if setting the initial LDAP defaults */ if (lmap == NULL || lmap != LDAPDefaults) { - /* We need to alloc an LDAPMAP_STRUCT struct */ - lmap = (LDAPMAP_STRUCT *) xalloc(sizeof *lmap); + /* We need to alloc an SM_LDAP_STRUCT struct */ + lmap = (SM_LDAP_STRUCT *) xalloc(sizeof *lmap); if (LDAPDefaults == NULL) - ldapmap_clear(lmap); + sm_ldap_clear(lmap); else STRUCTCOPY(*LDAPDefaults, *lmap); } @@ -3850,6 +4025,58 @@ ldapmap_parseargs(map, args) /* there is no check whether there is really an argument */ map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; map->map_spacesub = SpaceSub; /* default value */ + + /* Check if setting up an alias or file class LDAP map */ + if (bitset(MF_ALIAS, map->map_mflags)) + { + /* Comma separate if used as an alias file */ + map->map_coldelim = ','; + if (*args == '\0') + { + int n; + char *lc; + char jbuf[MAXHOSTNAMELEN]; + char lcbuf[MAXLINE]; + + /* Get $j */ + expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope); + if (jbuf[0] == '\0') + { + (void) sm_strlcpy(jbuf, "localhost", + sizeof jbuf); + } + + lc = macvalue(macid("{sendmailMTACluster}"), CurEnv); + if (lc == NULL) + lc = ""; + else + { + expand(lc, lcbuf, sizeof lcbuf, CurEnv); + lc = lcbuf; + } + + n = sm_snprintf(ldapfilt, sizeof ldapfilt, + "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))", + lc, jbuf); + if (n >= sizeof ldapfilt) + { + syserr("%s: Default LDAP string too long", + map->map_mname); + return false; + } + + /* default args for an alias LDAP entry */ + lmap->ldap_filter = ldapfilt; + lmap->ldap_attr[0] = "sendmailMTAAliasValue"; + lmap->ldap_attr[1] = NULL; + } + } + else if (bitset(MF_FILECLASS, map->map_mflags)) + { + /* Space separate if used as a file class file */ + map->map_coldelim = ' '; + } + for (;;) { while (isascii(*p) && isspace(*p)) @@ -3929,6 +4156,27 @@ ldapmap_parseargs(map, args) break; /* Start of ldapmap specific args */ + case 'V': + if (*++p != '\\') + lmap->ldap_attrsep = *p; + else + { + switch (*++p) + { + case 'n': + lmap->ldap_attrsep = '\n'; + break; + + case 't': + lmap->ldap_attrsep = '\t'; + break; + + default: + lmap->ldap_attrsep = '\\'; + } + } + break; + case 'k': /* search field */ while (isascii(*++p) && isspace(*p)) continue; @@ -3963,14 +4211,14 @@ ldapmap_parseargs(map, args) while (isascii(*++p) && isspace(*p)) continue; - if (strncasecmp(p, "LDAP_DEREF_", 11) == 0) + if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0) p += 11; for (lad = LDAPAliasDereference; lad != NULL && lad->lad_name != NULL; lad++) { - if (strncasecmp(p, lad->lad_name, - strlen(lad->lad_name)) == 0) + if (sm_strncasecmp(p, lad->lad_name, + strlen(lad->lad_name)) == 0) break; } if (lad->lad_name != NULL) @@ -3989,7 +4237,7 @@ ldapmap_parseargs(map, args) p, map->map_mname); if (ptr != NULL) *ptr = ' '; - return FALSE; + return false; } } break; @@ -3998,14 +4246,14 @@ ldapmap_parseargs(map, args) while (isascii(*++p) && isspace(*p)) continue; - if (strncasecmp(p, "LDAP_SCOPE_", 11) == 0) + if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0) p += 11; for (lss = LDAPSearchScope; lss != NULL && lss->lss_name != NULL; lss++) { - if (strncasecmp(p, lss->lss_name, - strlen(lss->lss_name)) == 0) + if (sm_strncasecmp(p, lss->lss_name, + strlen(lss->lss_name)) == 0) break; } if (lss->lss_name != NULL) @@ -4024,7 +4272,7 @@ ldapmap_parseargs(map, args) p, map->map_mname); if (ptr != NULL) *ptr = ' '; - return FALSE; + return false; } } break; @@ -4070,14 +4318,14 @@ ldapmap_parseargs(map, args) while (isascii(*++p) && isspace(*p)) continue; - if (strncasecmp(p, "LDAP_AUTH_", 10) == 0) + if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0) p += 10; for (lam = LDAPAuthMethods; lam != NULL && lam->lam_name != NULL; lam++) { - if (strncasecmp(p, lam->lam_name, - strlen(lam->lam_name)) == 0) + if (sm_strncasecmp(p, lam->lam_name, + strlen(lam->lam_name)) == 0) break; } if (lam->lam_name != NULL) @@ -4096,7 +4344,7 @@ ldapmap_parseargs(map, args) p, map->map_mname); if (ptr != NULL) *ptr = ' '; - return FALSE; + return false; } } @@ -4111,7 +4359,7 @@ ldapmap_parseargs(map, args) while (isascii(*++p) && isspace(*p)) continue; lmap->ldap_secret = p; - secretread = FALSE; + secretread = false; break; default: @@ -4165,7 +4413,7 @@ ldapmap_parseargs(map, args) LDAPDefaults == lmap || LDAPDefaults->ldap_secret != lmap->ldap_secret)) { - FILE *sfd; + SM_FILE_T *sfd; long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES; if (DontLockReadFiles) @@ -4195,12 +4443,12 @@ ldapmap_parseargs(map, args) { syserr("LDAP map: cannot open secret %s", ldapmap_dequote(lmap->ldap_secret)); - return FALSE; + return false; } lmap->ldap_secret = sfgets(m_tmp, LDAPMAP_MAX_PASSWD, sfd, TimeOuts.to_fileopen, "ldapmap_parseargs"); - (void) fclose(sfd); + (void) sm_io_close(sfd, SM_TIME_DEFAULT); if (lmap->ldap_secret != NULL && strlen(m_tmp) > 0) { @@ -4220,16 +4468,18 @@ ldapmap_parseargs(map, args) ** stashed */ - snprintf(m_tmp, MAXPATHLEN + LDAPMAP_MAX_PASSWD, - "KRBTKFILE=%s", - ldapmap_dequote(lmap->ldap_secret)); + (void) sm_snprintf(m_tmp, + MAXPATHLEN + LDAPMAP_MAX_PASSWD, + "KRBTKFILE=%s", + ldapmap_dequote(lmap->ldap_secret)); lmap->ldap_secret = m_tmp; break; # endif /* LDAP_AUTH_KRBV4 */ default: /* Should NEVER get here */ syserr("LDAP map: Illegal value in lmap method"); - return FALSE; + return false; + /* NOTREACHED */ break; } } @@ -4259,7 +4509,7 @@ ldapmap_parseargs(map, args) /* If setting defaults, don't process ldap_filter and ldap_attr */ if (lmap == LDAPDefaults) - return TRUE; + return true; if (lmap->ldap_filter != NULL) lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter)); @@ -4268,7 +4518,7 @@ ldapmap_parseargs(map, args) if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) { syserr("No filter given in map %s", map->map_mname); - return FALSE; + return false; } } @@ -4295,7 +4545,7 @@ ldapmap_parseargs(map, args) { syserr("Too many return attributes in %s (max %d)", map->map_mname, LDAPMAP_MAX_ATTR); - return FALSE; + return false; } if (*v != '\0') lmap->ldap_attr[i++] = newstr(v); @@ -4304,48 +4554,9 @@ ldapmap_parseargs(map, args) } map->map_db1 = (ARBPTR_T) lmap; - return TRUE; + return true; } -/* -** LDAPMAP_CLEAR -- set default values for LDAPMAP_STRUCT -** -** Parameters: -** lmap -- pointer to LDAPMAP_STRUCT to clear -** -** Returns: -** None. -** -*/ - -static void -ldapmap_clear(lmap) - LDAPMAP_STRUCT *lmap; -{ - lmap->ldap_host = NULL; - lmap->ldap_port = LDAP_PORT; - lmap->ldap_deref = LDAP_DEREF_NEVER; - lmap->ldap_timelimit = LDAP_NO_LIMIT; - lmap->ldap_sizelimit = LDAP_NO_LIMIT; -# ifdef LDAP_REFERRALS - lmap->ldap_options = LDAP_OPT_REFERRALS; -# else /* LDAP_REFERRALS */ - lmap->ldap_options = 0; -# endif /* LDAP_REFERRALS */ - lmap->ldap_binddn = NULL; - lmap->ldap_secret = NULL; - lmap->ldap_method = LDAP_AUTH_SIMPLE; - lmap->ldap_base = NULL; - lmap->ldap_scope = LDAP_SCOPE_SUBTREE; - lmap->ldap_attrsonly = LDAPMAP_FALSE; - lmap->ldap_timeout.tv_sec = 0; - lmap->ldap_timeout.tv_usec = 0; - lmap->ldap_ld = NULL; - lmap->ldap_filter = NULL; - lmap->ldap_attr[0] = NULL; - lmap->ldap_res = NULL; - lmap->ldap_next = NULL; -} /* ** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf ** @@ -4354,7 +4565,6 @@ ldapmap_clear(lmap) ** ** Returns: ** None. -** */ void @@ -4366,8 +4576,8 @@ ldapmap_set_defaults(spec) /* Allocate and set the default values */ if (LDAPDefaults == NULL) - LDAPDefaults = (LDAPMAP_STRUCT *) xalloc(sizeof *LDAPDefaults); - ldapmap_clear(LDAPDefaults); + LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof *LDAPDefaults); + sm_ldap_clear(LDAPDefaults); memset(&map, '\0', sizeof map); @@ -4391,16 +4601,8 @@ ldapmap_set_defaults(spec) map.map_tapp != NULL) { syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags"); - if (map.map_app != NULL) - { - sm_free(map.map_app); - map.map_app = NULL; - } - if (map.map_tapp != NULL) - { - sm_free(map.map_tapp); - map.map_tapp = NULL; - } + SM_FREE_CLR(map.map_app); + SM_FREE_CLR(map.map_tapp); } if (LDAPDefaults->ldap_filter != NULL) @@ -4422,7 +4624,7 @@ ldapmap_set_defaults(spec) ** PH map */ -#ifdef PH_MAP +#if PH_MAP /* ** Support for the CCSO Nameserver (ph/qi). @@ -4430,8 +4632,11 @@ ldapmap_set_defaults(spec) ** Contributed by Mark D. Roth <roth@uiuc.edu>. Contact him for support. */ -# include <qiapi.h> -# include <qicode.h> +/* what version of the ph map code we're running */ +static char phmap_id[PH_BUF_SIZE]; + +/* sendmail version for phmap id string */ +extern const char Version[]; /* ** PH_MAP_PARSEARGS -- parse ph map definition args. @@ -4442,20 +4647,23 @@ ph_map_parseargs(map, args) MAP *map; char *args; { - int i; - register int done; - PH_MAP_STRUCT *pmap = NULL; + register bool done; register char *p = args; + PH_MAP_STRUCT *pmap = NULL; + + /* initialize version string */ + (void) sm_snprintf(phmap_id, sizeof phmap_id, + "sendmail-%s phmap-20010529 libphclient-%s", + Version, libphclient_version); pmap = (PH_MAP_STRUCT *) xalloc(sizeof *pmap); /* defaults */ pmap->ph_servers = NULL; pmap->ph_field_list = NULL; - pmap->ph_to_server = NULL; - pmap->ph_from_server = NULL; - pmap->ph_sockfd = -1; + pmap->ph = NULL; pmap->ph_timeout = 0; + pmap->ph_fastclose = 0; map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; for (;;) @@ -4507,13 +4715,11 @@ ph_map_parseargs(map, args) map->map_tapp = ++p; break; -#if _FFR_PHMAP_TIMEOUT case 'l': while (isascii(*++p) && isspace(*p)) continue; pmap->ph_timeout = atoi(p); break; -#endif /* _FFR_PHMAP_TIMEOUT */ case 'S': map->map_spacesub = *++p; @@ -4529,7 +4735,13 @@ ph_map_parseargs(map, args) pmap->ph_servers = p; break; - case 'v': /* fields to search for */ + case 'v': + sm_syslog(LOG_WARNING, NULL, + "ph_map_parseargs: WARNING: -v option will be removed in a future release - please use -k instead"); + /* intentional fallthrough for backward compatibility */ + /* FALLTHROUGH */ + + case 'k': /* fields to search for */ while (isascii(*++p) && isspace(*p)) continue; pmap->ph_field_list = p; @@ -4566,72 +4778,51 @@ ph_map_parseargs(map, args) if (pmap->ph_field_list != NULL) pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list)); - else - pmap->ph_field_list = DEFAULT_PH_MAP_FIELDS; if (pmap->ph_servers != NULL) pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers)); else { syserr("ph_map_parseargs: -h flag is required"); - return FALSE; + return false; } map->map_db1 = (ARBPTR_T) pmap; - return TRUE; + return true; } -#if _FFR_PHMAP_TIMEOUT /* ** PH_MAP_CLOSE -- close the connection to the ph server */ -static void -ph_map_safeclose(map) +void +ph_map_close(map) MAP *map; { - int save_errno = errno; PH_MAP_STRUCT *pmap; pmap = (PH_MAP_STRUCT *)map->map_db1; + if (tTd(38, 9)) + sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d", + map->map_mname, pmap->ph_fastclose); - if (pmap->ph_sockfd != -1) - { - (void) close(pmap->ph_sockfd); - pmap->ph_sockfd = -1; - } - if (pmap->ph_from_server != NULL) - { - (void) fclose(pmap->ph_from_server); - pmap->ph_from_server = NULL; - } - if (pmap->ph_to_server != NULL) + + if (pmap->ph != NULL) { - (void) fclose(pmap->ph_to_server); - pmap->ph_to_server = NULL; + ph_set_sendhook(pmap->ph, NULL); + ph_set_recvhook(pmap->ph, NULL); + ph_close(pmap->ph, pmap->ph_fastclose); } - map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); - errno = save_errno; -} -void -ph_map_close(map) - MAP *map; -{ - PH_MAP_STRUCT *pmap; - - pmap = (PH_MAP_STRUCT *)map->map_db1; - (void) fprintf(pmap->ph_to_server, "quit\n"); - (void) fflush(pmap->ph_to_server); - ph_map_safeclose(map); + map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); } static jmp_buf PHTimeout; /* ARGSUSED */ static void -ph_timeout(sig) - int sig; +ph_timeout(unused) + int unused; { /* ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD @@ -4642,23 +4833,28 @@ ph_timeout(sig) errno = ETIMEDOUT; longjmp(PHTimeout, 1); } -#else /* _FFR_PHMAP_TIMEOUT */ -/* -** PH_MAP_CLOSE -- close the connection to the ph server -*/ -void -ph_map_close(map) - MAP *map; +static void +ph_map_send_debug(text) + char *text; { - PH_MAP_STRUCT *pmap; + if (LogLevel > 9) + sm_syslog(LOG_NOTICE, CurEnv->e_id, + "ph_map_send_debug: ==> %s", text); + if (tTd(38, 20)) + sm_dprintf("ph_map_send_debug: ==> %s\n", text); +} - pmap = (PH_MAP_STRUCT *)map->map_db1; - CloseQi(pmap->ph_to_server, pmap->ph_from_server); - pmap->ph_to_server = NULL; - pmap->ph_from_server = NULL; +static void +ph_map_recv_debug(text) + char *text; +{ + if (LogLevel > 10) + sm_syslog(LOG_NOTICE, CurEnv->e_id, + "ph_map_recv_debug: <== %s", text); + if (tTd(38, 21)) + sm_dprintf("ph_map_recv_debug: <== %s\n", text); } -#endif /* _FFR_PHMAP_TIMEOUT */ /* ** PH_MAP_OPEN -- sub for opening PH map @@ -4668,60 +4864,49 @@ ph_map_open(map, mode) MAP *map; int mode; { -#if !_FFR_PHMAP_TIMEOUT - int save_errno = 0; -#endif /* !_FFR_PHMAP_TIMEOUT */ - int j; - char *hostlist, *tmp; - QIR *server_data = NULL; PH_MAP_STRUCT *pmap; -#if _FFR_PHMAP_TIMEOUT - register EVENT *ev = NULL; -#endif /* _FFR_PHMAP_TIMEOUT */ + register SM_EVENT *ev = NULL; + int save_errno = 0; + char *hostlist, *host; if (tTd(38, 2)) - dprintf("ph_map_open(%s)\n", map->map_mname); + sm_dprintf("ph_map_open(%s)\n", map->map_mname); mode &= O_ACCMODE; if (mode != O_RDONLY) { /* issue a pseudo-error message */ -# ifdef ENOSYS - errno = ENOSYS; -# else /* ENOSYS */ -# ifdef EFTYPE - errno = EFTYPE; -# else /* EFTYPE */ - errno = ENXIO; -# endif /* EFTYPE */ -# endif /* ENOSYS */ - return FALSE; + errno = SM_EMAPCANTWRITE; + return false; } if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER && bitset(MF_DEFER, map->map_mflags)) { if (tTd(9, 1)) - dprintf("ph_map_open(%s) => DEFERRED\n", - map->map_mname); + sm_dprintf("ph_map_open(%s) => DEFERRED\n", + map->map_mname); /* - ** Unset MF_DEFER here so that map_lookup() returns - ** a temporary failure using the bogus map and - ** map->map_tapp instead of the default permanent error. + ** Unset MF_DEFER here so that map_lookup() returns + ** a temporary failure using the bogus map and + ** map->map_tapp instead of the default permanent error. */ map->map_mflags &= ~MF_DEFER; - return FALSE; + return false; } pmap = (PH_MAP_STRUCT *)map->map_db1; + pmap->ph_fastclose = 0; /* refresh field for reopen */ + /* try each host in the list */ hostlist = newstr(pmap->ph_servers); - tmp = strtok(hostlist, " "); - do + for (host = strtok(hostlist, " "); + host != NULL; + host = strtok(NULL, " ")) { -#if _FFR_PHMAP_TIMEOUT + /* set timeout */ if (pmap->ph_timeout != 0) { if (setjmp(PHTimeout) != 0) @@ -4730,67 +4915,33 @@ ph_map_open(map, mode) if (LogLevel > 1) sm_syslog(LOG_NOTICE, CurEnv->e_id, "timeout connecting to PH server %.100s", - tmp); -# ifdef ETIMEDOUT + host); errno = ETIMEDOUT; -# else /* ETIMEDOUT */ - errno = EAGAIN; -# endif /* ETIMEDOUT */ goto ph_map_open_abort; } - ev = setevent(pmap->ph_timeout, ph_timeout, 0); + ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0); } - if (!OpenQiSock(tmp, &(pmap->ph_sockfd)) && - !Sock2FILEs(pmap->ph_sockfd, &(pmap->ph_to_server), - &(pmap->ph_from_server)) && - fprintf(pmap->ph_to_server, "id sendmail+phmap\n") >= 0 && - fflush(pmap->ph_to_server) == 0 && - (server_data = ReadQi(pmap->ph_from_server, &j)) != NULL && - server_data->code == 200) + + /* open connection to server */ + if (!ph_open(&(pmap->ph), host, PH_ROUNDROBIN|PH_DONTID, + ph_map_send_debug, ph_map_recv_debug) && + !ph_id(pmap->ph, phmap_id)) { if (ev != NULL) - clrevent(ev); - FreeQIR(server_data); -#else /* _FFR_PHMAP_TIMEOUT */ - if (OpenQi(tmp, &(pmap->ph_to_server), - &(pmap->ph_from_server)) >= 0) - { - if (fprintf(pmap->ph_to_server, - "id sendmail+phmap\n") < 0 || - fflush(pmap->ph_to_server) != 0 || - (server_data = ReadQi(pmap->ph_from_server, - &j)) == NULL || - server_data->code != 200) - { - save_errno = errno; - CloseQi(pmap->ph_to_server, - pmap->ph_from_server); - continue; - } - if (server_data != NULL) - FreeQIR(server_data); -#endif /* _FFR_PHMAP_TIMEOUT */ - sm_free(hostlist); - return TRUE; + sm_clrevent(ev); + sm_free(hostlist); /* XXX */ + return true; } -#if _FFR_PHMAP_TIMEOUT + ph_map_open_abort: - if (ev != NULL) - clrevent(ev); - ph_map_safeclose(map); - if (server_data != NULL) - { - FreeQIR(server_data); - server_data = NULL; - } -#else /* _FFR_PHMAP_TIMEOUT */ save_errno = errno; -#endif /* _FFR_PHMAP_TIMEOUT */ - } while (tmp = strtok(NULL, " ")); + if (ev != NULL) + sm_clrevent(ev); + pmap->ph_fastclose = PH_FASTCLOSE; + ph_map_close(map); + errno = save_errno; + } -#if !_FFR_PHMAP_TIMEOUT - errno = save_errno; -#endif /* !_FFR_PHMAP_TIMEOUT */ if (bitset(MF_NODEFER, map->map_mflags)) { if (errno == 0) @@ -4802,18 +4953,14 @@ ph_map_open(map, mode) sm_syslog(LOG_NOTICE, CurEnv->e_id, "ph_map_open: %s: cannot connect to PH server", map->map_mname); - sm_free(hostlist); - return FALSE; + sm_free(hostlist); /* XXX */ + return false; } /* ** PH_MAP_LOOKUP -- look up key from ph server */ -#if _FFR_PHMAP_TIMEOUT -# define MAX_PH_FIELDS 20 -#endif /* _FFR_PHMAP_TIMEOUT */ - char * ph_map_lookup(map, key, args, pstat) MAP *map; @@ -4821,25 +4968,16 @@ ph_map_lookup(map, key, args, pstat) char **args; int *pstat; { - int j; - size_t sz; - char *tmp, *tmp2; - char *message = NULL, *field = NULL, *fmtkey; - QIR *server_data = NULL; - QIR *qirp; - char keybuf[MAXKEY + 1], fieldbuf[101]; -#if _FFR_PHMAP_TIMEOUT - QIR *hold_data[MAX_PH_FIELDS]; - int hold_data_idx = 0; - register EVENT *ev = NULL; -#endif /* _FFR_PHMAP_TIMEOUT */ + int i, save_errno = 0; + register SM_EVENT *ev = NULL; PH_MAP_STRUCT *pmap; + char *value = NULL; pmap = (PH_MAP_STRUCT *)map->map_db1; *pstat = EX_OK; -#if _FFR_PHMAP_TIMEOUT + /* set timeout */ if (pmap->ph_timeout != 0) { if (setjmp(PHTimeout) != 0) @@ -4849,260 +4987,49 @@ ph_map_lookup(map, key, args, pstat) sm_syslog(LOG_NOTICE, CurEnv->e_id, "timeout during PH lookup of %.100s", key); -# ifdef ETIMEDOUT errno = ETIMEDOUT; -# else /* ETIMEDOUT */ - errno = 0; -# endif /* ETIMEDOUT */ *pstat = EX_TEMPFAIL; goto ph_map_lookup_abort; } - ev = setevent(pmap->ph_timeout, ph_timeout, 0); + ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0); } -#endif /* _FFR_PHMAP_TIMEOUT */ - /* check all relevant fields */ - tmp = pmap->ph_field_list; - do - { -#if _FFR_PHMAP_TIMEOUT - server_data = NULL; -#endif /* _FFR_PHMAP_TIMEOUT */ - while (isascii(*tmp) && isspace(*tmp)) - tmp++; - if (*tmp == '\0') - break; - sz = strcspn(tmp, " ") + 1; - if (sz > sizeof fieldbuf) - sz = sizeof fieldbuf; - (void) strlcpy(fieldbuf, tmp, sz); - field = fieldbuf; - tmp += sz; - - (void) strlcpy(keybuf, key, sizeof keybuf); - fmtkey = keybuf; - if (strcmp(field, "alias") == 0) - { - /* - ** for alias lookups, replace any punctuation - ** characters with '-' - */ - - for (tmp2 = fmtkey; *tmp2 != '\0'; tmp2++) - { - if (isascii(*tmp2) && ispunct(*tmp2)) - *tmp2 = '-'; - } - tmp2 = field; - } - else if (strcmp(field,"spacedname") == 0) - { - /* - ** for "spaced" name lookups, replace any - ** punctuation characters with a space - */ - - for (tmp2 = fmtkey; *tmp2 != '\0'; tmp2++) - { - if (isascii(*tmp2) && ispunct(*tmp2) && - *tmp2 != '*') - *tmp2 = ' '; - } - tmp2 = &(field[6]); - } - else - tmp2 = field; - - if (LogLevel > 9) - sm_syslog(LOG_NOTICE, CurEnv->e_id, - "ph_map_lookup: query %s=\"%s\" return email", - tmp2, fmtkey); - if (tTd(38, 20)) - dprintf("ph_map_lookup: query %s=\"%s\" return email\n", - tmp2, fmtkey); - - j = 0; - - if (fprintf(pmap->ph_to_server, "query %s=%s return email\n", - tmp2, fmtkey) < 0) - message = "qi query command failed"; - else if (fflush(pmap->ph_to_server) != 0) - message = "qi fflush failed"; - else if ((server_data = ReadQi(pmap->ph_from_server, - &j)) == NULL) - message = "ReadQi() returned NULL"; - -#if _FFR_PHMAP_TIMEOUT - if ((hold_data[hold_data_idx] = server_data) != NULL) - { - /* save pointer for later free() */ - hold_data_idx++; - } -#endif /* _FFR_PHMAP_TIMEOUT */ - - if (server_data == NULL || - (server_data->code >= 400 && - server_data->code < 500)) - { - /* temporary failure */ - *pstat = EX_TEMPFAIL; -#if _FFR_PHMAP_TIMEOUT - break; -#else /* _FFR_PHMAP_TIMEOUT */ - if (server_data != NULL) - { - FreeQIR(server_data); - server_data = NULL; - } - return NULL; -#endif /* _FFR_PHMAP_TIMEOUT */ - } - - /* - ** if we found a single match, break out. - ** otherwise, try the next field. - */ - - if (j == 1) - break; - - /* - ** check for a single response which is an error: - ** ReadQi() doesn't set j on error responses, - ** but we should stop here instead of moving on if - ** it happens (e.g., alias found but email field empty) - */ - - for (qirp = server_data; - qirp != NULL && qirp->code < 0; - qirp++) - { - if (tTd(38, 20)) - dprintf("ph_map_lookup: QIR: %d:%d:%d:%s\n", - qirp->code, qirp->subcode, qirp->field, - (qirp->message ? qirp->message - : "[NULL]")); - if (qirp->code <= -500) - { - j = 0; - goto ph_map_lookup_abort; - } - } - -#if _FFR_PHMAP_TIMEOUT - } while (*tmp != '\0' && hold_data_idx < MAX_PH_FIELDS); -#else /* _FFR_PHMAP_TIMEOUT */ - } while (*tmp != '\0'); -#endif /* _FFR_PHMAP_TIMEOUT */ + /* perform lookup */ + i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value); + if (i == -1) + *pstat = EX_TEMPFAIL; + else if (i == PH_NOMATCH || i == PH_DATAERR) + *pstat = EX_UNAVAILABLE; ph_map_lookup_abort: -#if _FFR_PHMAP_TIMEOUT if (ev != NULL) - clrevent(ev); + sm_clrevent(ev); /* - ** Return EX_TEMPFAIL if the timer popped + ** Close the connection if the timer popped ** or we got a temporary PH error */ if (*pstat == EX_TEMPFAIL) - ph_map_safeclose(map); - - /* if we didn't find a single match, bail out */ - if (*pstat == EX_OK && j != 1) - *pstat = EX_UNAVAILABLE; + { + save_errno = errno; + pmap->ph_fastclose = PH_FASTCLOSE; + ph_map_close(map); + errno = save_errno; + } if (*pstat == EX_OK) { - /* - ** skip leading whitespace and chop at first address - */ - - for (tmp = server_data->message; - isascii(*tmp) && isspace(*tmp); - tmp++) - continue; - - for (tmp2 = tmp; *tmp2 != '\0'; tmp2++) - { - if (isascii(*tmp2) && isspace(*tmp2)) - { - *tmp2 = '\0'; - break; - } - } - if (tTd(38,20)) - dprintf("ph_map_lookup: %s => %s\n", key, tmp); + sm_dprintf("ph_map_lookup: %s => %s\n", key, value); if (bitset(MF_MATCHONLY, map->map_mflags)) - message = map_rewrite(map, key, strlen(key), NULL); + return map_rewrite(map, key, strlen(key), NULL); else - message = map_rewrite(map, tmp, strlen(tmp), args); + return map_rewrite(map, value, strlen(value), args); } - /* - ** Deferred free() of returned server_data values - ** the deferral is to avoid the risk of a free() being - ** interrupted by the event timer. By now the timeout event - ** has been cleared and none of the data is still in use. - */ - - while (--hold_data_idx >= 0) - { - if (hold_data[hold_data_idx] != NULL) - FreeQIR(hold_data[hold_data_idx]); - } - - if (*pstat == EX_OK) - return message; - return NULL; -#else /* _FFR_PHMAP_TIMEOUT */ - /* if we didn't find a single match, bail out */ - if (j != 1) - { - *pstat = EX_UNAVAILABLE; - if (server_data != NULL) - { - FreeQIR(server_data); - server_data = NULL; - } - return NULL; - } - - /* - ** skip leading whitespace and chop at first address - */ - - for (tmp = server_data->message; - isascii(*tmp) && isspace(*tmp); - tmp++) - continue; - - for (tmp2 = tmp; *tmp2 != '\0'; tmp2++) - { - if (isascii(*tmp2) && isspace(*tmp2)) - { - *tmp2 = '\0'; - break; - } - } - - if (tTd(38,20)) - dprintf("ph_map_lookup: %s => %s\n", key, tmp); - - if (bitset(MF_MATCHONLY, map->map_mflags)) - message = map_rewrite(map, key, strlen(key), NULL); - else - message = map_rewrite(map, tmp, strlen(tmp), args); - if (server_data != NULL) - { - FreeQIR(server_data); - server_data = NULL; - } - return message; -#endif /* _FFR_PHMAP_TIMEOUT */ } #endif /* PH_MAP */ /* @@ -5165,56 +5092,56 @@ syslog_map_parseargs(map, args) map->map_prio = LOG_INFO; else { - if (strncasecmp("LOG_", priority, 4) == 0) + if (sm_strncasecmp("LOG_", priority, 4) == 0) priority += 4; #ifdef LOG_EMERG - if (strcasecmp("EMERG", priority) == 0) + if (sm_strcasecmp("EMERG", priority) == 0) map->map_prio = LOG_EMERG; else #endif /* LOG_EMERG */ #ifdef LOG_ALERT - if (strcasecmp("ALERT", priority) == 0) + if (sm_strcasecmp("ALERT", priority) == 0) map->map_prio = LOG_ALERT; else #endif /* LOG_ALERT */ #ifdef LOG_CRIT - if (strcasecmp("CRIT", priority) == 0) + if (sm_strcasecmp("CRIT", priority) == 0) map->map_prio = LOG_CRIT; else #endif /* LOG_CRIT */ #ifdef LOG_ERR - if (strcasecmp("ERR", priority) == 0) + if (sm_strcasecmp("ERR", priority) == 0) map->map_prio = LOG_ERR; else #endif /* LOG_ERR */ #ifdef LOG_WARNING - if (strcasecmp("WARNING", priority) == 0) + if (sm_strcasecmp("WARNING", priority) == 0) map->map_prio = LOG_WARNING; else #endif /* LOG_WARNING */ #ifdef LOG_NOTICE - if (strcasecmp("NOTICE", priority) == 0) + if (sm_strcasecmp("NOTICE", priority) == 0) map->map_prio = LOG_NOTICE; else #endif /* LOG_NOTICE */ #ifdef LOG_INFO - if (strcasecmp("INFO", priority) == 0) + if (sm_strcasecmp("INFO", priority) == 0) map->map_prio = LOG_INFO; else #endif /* LOG_INFO */ #ifdef LOG_DEBUG - if (strcasecmp("DEBUG", priority) == 0) + if (sm_strcasecmp("DEBUG", priority) == 0) map->map_prio = LOG_DEBUG; else #endif /* LOG_DEBUG */ { syserr("syslog_map_parseargs: Unknown priority %s\n", priority); - return FALSE; + return false; } } - return TRUE; + return true; } /* @@ -5233,7 +5160,7 @@ syslog_map_lookup(map, string, args, statp) if (ptr != NULL) { if (tTd(38, 20)) - dprintf("syslog_map_lookup(%s (priority %d): %s\n", + sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n", map->map_mname, map->map_prio, ptr); sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr); @@ -5247,7 +5174,7 @@ syslog_map_lookup(map, string, args, statp) ** HESIOD Modules */ -#ifdef HESIOD +#if HESIOD bool hes_map_open(map, mode) @@ -5255,32 +5182,24 @@ hes_map_open(map, mode) int mode; { if (tTd(38, 2)) - dprintf("hes_map_open(%s, %s, %d)\n", + sm_dprintf("hes_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); if (mode != O_RDONLY) { /* issue a pseudo-error message */ -# ifdef ENOSYS - errno = ENOSYS; -# else /* ENOSYS */ -# ifdef EFTYPE - errno = EFTYPE; -# else /* EFTYPE */ - errno = ENXIO; -# endif /* EFTYPE */ -# endif /* ENOSYS */ - return FALSE; + errno = SM_EMAPCANTWRITE; + return false; } # ifdef HESIOD_INIT if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0) - return TRUE; + return true; if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("421 4.0.0 cannot initialize Hesiod map (%s)", - errstring(errno)); - return FALSE; + sm_errstring(errno)); + return false; # else /* HESIOD_INIT */ if (hes_error() == HES_ER_UNINIT) hes_init(); @@ -5288,13 +5207,13 @@ hes_map_open(map, mode) { case HES_ER_OK: case HES_ER_NOTFOUND: - return TRUE; + return true; } if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("421 4.0.0 cannot initialize Hesiod map (%d)", hes_error()); - return FALSE; + return false; # endif /* HESIOD_INIT */ } @@ -5308,7 +5227,7 @@ hes_map_lookup(map, name, av, statp) char **hp; if (tTd(38, 20)) - dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name); + sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name); if (name[0] == '\\') { @@ -5323,7 +5242,7 @@ hes_map_lookup(map, name, av, statp) else np = xalloc(strlen(name) + 2); np[0] = '\\'; - (void) strlcpy(&np[1], name, (sizeof nbuf) - 1); + (void) sm_strlcpy(&np[1], name, (sizeof nbuf) - 1); # ifdef HESIOD_INIT hp = hesiod_resolve(HesiodContext, np, map->map_file); # else /* HESIOD_INIT */ @@ -5331,7 +5250,7 @@ hes_map_lookup(map, name, av, statp) # endif /* HESIOD_INIT */ save_errno = errno; if (np != nbuf) - sm_free(np); + sm_free(np); /* XXX */ errno = save_errno; } else @@ -5351,9 +5270,9 @@ hes_map_lookup(map, name, av, statp) *statp = EX_NOTFOUND; break; case ECONNREFUSED: - case EMSGSIZE: *statp = EX_TEMPFAIL; break; + case EMSGSIZE: case ENOMEM: default: *statp = EX_UNAVAILABLE; @@ -5394,6 +5313,27 @@ hes_map_lookup(map, name, av, statp) return map_rewrite(map, hp[0], strlen(hp[0]), av); } +/* +** HES_MAP_CLOSE -- free the Hesiod context +*/ + +void +hes_map_close(map) + MAP *map; +{ + if (tTd(38, 20)) + sm_dprintf("hes_map_close(%s)\n", map->map_file); + +# ifdef HESIOD_INIT + /* Free the hesiod context */ + if (HesiodContext != NULL) + { + hesiod_end(HesiodContext); + HesiodContext = NULL; + } +# endif /* HESIOD_INIT */ +} + #endif /* HESIOD */ /* ** NeXT NETINFO Modules @@ -5414,7 +5354,7 @@ ni_map_open(map, mode) int mode; { if (tTd(38, 2)) - dprintf("ni_map_open(%s, %s, %d)\n", + sm_dprintf("ni_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); mode &= O_ACCMODE; @@ -5424,10 +5364,14 @@ ni_map_open(map, mode) if (map->map_valcolnm == NULL) map->map_valcolnm = NETINFO_DEFAULT_PROPERTY; - if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags)) - map->map_coldelim = ','; - - return TRUE; + if (map->map_coldelim == '\0') + { + if (bitset(MF_ALIAS, map->map_mflags)) + map->map_coldelim = ','; + else if (bitset(MF_FILECLASS, map->map_mflags)) + map->map_coldelim = ' '; + } + return true; } @@ -5446,7 +5390,7 @@ ni_map_lookup(map, name, av, statp) char *propval; if (tTd(38, 20)) - dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name); + sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name); propval = ni_propval(map->map_file, map->map_keycolnm, name, map->map_valcolnm, map->map_coldelim); @@ -5454,11 +5398,14 @@ ni_map_lookup(map, name, av, statp) if (propval == NULL) return NULL; - if (bitset(MF_MATCHONLY, map->map_mflags)) - res = map_rewrite(map, name, strlen(name), NULL); - else - res = map_rewrite(map, propval, strlen(propval), av); - sm_free(propval); + SM_TRY + if (bitset(MF_MATCHONLY, map->map_mflags)) + res = map_rewrite(map, name, strlen(name), NULL); + else + res = map_rewrite(map, propval, strlen(propval), av); + SM_FINALLY + sm_free(propval); + SM_END_TRY return res; } @@ -5474,12 +5421,12 @@ ni_getcanonname(name, hbsize, statp) char nbuf[MAXNAME + 1]; if (tTd(38, 20)) - dprintf("ni_getcanonname(%s)\n", name); + sm_dprintf("ni_getcanonname(%s)\n", name); - if (strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) + if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) { *statp = EX_UNAVAILABLE; - return FALSE; + return false; } (void) shorten_hostname(nbuf); @@ -5487,7 +5434,7 @@ ni_getcanonname(name, hbsize, statp) if (strchr(nbuf, '.')) { *statp = EX_NOHOST; - return FALSE; + return false; } /* Do the search */ @@ -5496,209 +5443,23 @@ ni_getcanonname(name, hbsize, statp) if (vptr == NULL) { *statp = EX_NOHOST; - return FALSE; + return false; } /* Only want the first machine name */ if ((ptr = strchr(vptr, '\n')) != NULL) *ptr = '\0'; - if (hbsize >= strlen(vptr)) + if (sm_strlcpy(name, vptr, hbsize) >= hbsize) { - (void) strlcpy(name, vptr, hbsize); sm_free(vptr); - *statp = EX_OK; - return TRUE; + *statp = EX_UNAVAILABLE; + return true; } - *statp = EX_UNAVAILABLE; sm_free(vptr); - return FALSE; -} - - -/* -** NI_PROPVAL -- NetInfo property value lookup routine -** -** Parameters: -** keydir -- the NetInfo directory name in which to search -** for the key. -** keyprop -- the name of the property in which to find the -** property we are interested. Defaults to "name". -** keyval -- the value for which we are really searching. -** valprop -- the property name for the value in which we -** are interested. -** sepchar -- if non-nil, this can be multiple-valued, and -** we should return a string separated by this -** character. -** -** Returns: -** NULL -- if: -** 1. the directory is not found -** 2. the property name is not found -** 3. the property contains multiple values -** 4. some error occurred -** else -- the value of the lookup. -** -** Example: -** To search for an alias value, use: -** ni_propval("/aliases", "name", aliasname, "members", ',') -** -** Notes: -** Caller should free the return value of ni_proval -*/ - -# include <netinfo/ni.h> - -# define LOCAL_NETINFO_DOMAIN "." -# define PARENT_NETINFO_DOMAIN ".." -# define MAX_NI_LEVELS 256 - -char * -ni_propval(keydir, keyprop, keyval, valprop, sepchar) - char *keydir; - char *keyprop; - char *keyval; - char *valprop; - int sepchar; -{ - char *propval = NULL; - int i; - int j, alen, l; - void *ni = NULL; - void *lastni = NULL; - ni_status nis; - ni_id nid; - ni_namelist ninl; - register char *p; - char keybuf[1024]; - - /* - ** Create the full key from the two parts. - ** - ** Note that directory can end with, e.g., "name=" to specify - ** an alternate search property. - */ - - i = strlen(keydir) + strlen(keyval) + 2; - if (keyprop != NULL) - i += strlen(keyprop) + 1; - if (i >= sizeof keybuf) - return NULL; - (void) strlcpy(keybuf, keydir, sizeof keybuf); - (void) strlcat(keybuf, "/", sizeof keybuf); - if (keyprop != NULL) - { - (void) strlcat(keybuf, keyprop, sizeof keybuf); - (void) strlcat(keybuf, "=", sizeof keybuf); - } - (void) strlcat(keybuf, keyval, sizeof keybuf); - - if (tTd(38, 21)) - dprintf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n", - keydir, keyprop, keyval, valprop, sepchar, keybuf); - /* - ** If the passed directory and property name are found - ** in one of netinfo domains we need to search (starting - ** from the local domain moving all the way back to the - ** root domain) set propval to the property's value - ** and return it. - */ - - for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++) - { - if (i == 0) - { - nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni); - if (tTd(38, 20)) - dprintf("ni_open(LOCAL) = %d\n", nis); - } - else - { - if (lastni != NULL) - ni_free(lastni); - lastni = ni; - nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni); - if (tTd(38, 20)) - dprintf("ni_open(PARENT) = %d\n", nis); - } - - /* - ** Don't bother if we didn't get a handle on a - ** proper domain. This is not necessarily an error. - ** We would get a positive ni_status if, for instance - ** we never found the directory or property and tried - ** to open the parent of the root domain! - */ - - if (nis != 0) - break; - - /* - ** Find the path to the server information. - */ - - if (ni_pathsearch(ni, &nid, keybuf) != 0) - continue; - - /* - ** Find associated value information. - */ - - if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0) - continue; - - if (tTd(38, 20)) - dprintf("ni_lookupprop: len=%d\n", - ninl.ni_namelist_len); - - /* - ** See if we have an acceptable number of values. - */ - - if (ninl.ni_namelist_len <= 0) - continue; - - if (sepchar == '\0' && ninl.ni_namelist_len > 1) - { - ni_namelist_free(&ninl); - continue; - } - - /* - ** Calculate number of bytes needed and build result - */ - - alen = 1; - for (j = 0; j < ninl.ni_namelist_len; j++) - alen += strlen(ninl.ni_namelist_val[j]) + 1; - propval = p = xalloc(alen); - for (j = 0; j < ninl.ni_namelist_len; j++) - { - (void) strlcpy(p, ninl.ni_namelist_val[j], alen); - l = strlen(p); - p += l; - *p++ = sepchar; - alen -= l + 1; - } - *--p = '\0'; - - ni_namelist_free(&ninl); - } - - /* - ** Clean up. - */ - - if (ni != NULL) - ni_free(ni); - if (lastni != NULL && ni != lastni) - ni_free(lastni); - if (tTd(38, 20)) - dprintf("ni_propval returns: '%s'\n", propval); - - return propval; + *statp = EX_OK; + return false; } - #endif /* NETINFO */ /* ** TEXT (unindexed text file) Modules @@ -5722,28 +5483,28 @@ text_map_open(map, mode) int i; if (tTd(38, 2)) - dprintf("text_map_open(%s, %s, %d)\n", + sm_dprintf("text_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); mode &= O_ACCMODE; if (mode != O_RDONLY) { errno = EPERM; - return FALSE; + return false; } if (*map->map_file == '\0') { syserr("text map \"%s\": file name required", map->map_mname); - return FALSE; + return false; } if (map->map_file[0] != '/') { syserr("text map \"%s\": file name must be fully qualified", map->map_mname); - return FALSE; + return false; } sff = SFF_ROOTOK|SFF_REGONLY; @@ -5758,12 +5519,12 @@ text_map_open(map, mode) /* cannot open this map */ if (tTd(38, 2)) - dprintf("\tunsafe map file: %d\n", i); + sm_dprintf("\tunsafe map file: %d\n", i); errno = save_errno; if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("text map \"%s\": unsafe map file %s", map->map_mname, map->map_file); - return FALSE; + return false; } if (map->map_keycolnm == NULL) @@ -5775,7 +5536,7 @@ text_map_open(map, mode) syserr("text map \"%s\", file %s: -k should specify a number, not %s", map->map_mname, map->map_file, map->map_keycolnm); - return FALSE; + return false; } map->map_keycolno = atoi(map->map_keycolnm); } @@ -5789,23 +5550,23 @@ text_map_open(map, mode) syserr("text map \"%s\", file %s: -v should specify a number, not %s", map->map_mname, map->map_file, map->map_valcolnm); - return FALSE; + return false; } map->map_valcolno = atoi(map->map_valcolnm); } if (tTd(38, 2)) { - dprintf("text_map_open(%s, %s): delimiter = ", + sm_dprintf("text_map_open(%s, %s): delimiter = ", map->map_mname, map->map_file); if (map->map_coldelim == '\0') - dprintf("(white space)\n"); + sm_dprintf("(white space)\n"); else - dprintf("%c\n", map->map_coldelim); + sm_dprintf("%c\n", map->map_coldelim); } map->map_sff = sff; - return TRUE; + return true; } @@ -5823,7 +5584,7 @@ text_map_lookup(map, name, av, statp) char *vp; auto int vsize; int buflen; - FILE *f; + SM_FILE_T *f; char delim; int key_idx; bool found_it; @@ -5832,13 +5593,13 @@ text_map_lookup(map, name, av, statp) char linebuf[MAXLINE]; char buf[MAXNAME + 1]; - found_it = FALSE; + found_it = false; if (tTd(38, 20)) - dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name); + sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name); buflen = strlen(name); if (buflen > sizeof search_key - 1) - buflen = sizeof search_key - 1; + buflen = sizeof search_key - 1; /* XXX just cut if off? */ memmove(search_key, name, buflen); search_key[buflen] = '\0'; if (!bitset(MF_NOFOLDCASE, map->map_mflags)) @@ -5853,7 +5614,7 @@ text_map_lookup(map, name, av, statp) } key_idx = map->map_keycolno; delim = map->map_coldelim; - while (fgets(linebuf, MAXLINE, f) != NULL) + while (sm_io_fgets(f, SM_TIME_DEFAULT, linebuf, MAXLINE) != NULL) { char *p; @@ -5864,13 +5625,13 @@ text_map_lookup(map, name, av, statp) if (p != NULL) *p = '\0'; p = get_column(linebuf, key_idx, delim, buf, sizeof buf); - if (p != NULL && strcasecmp(search_key, p) == 0) + if (p != NULL && sm_strcasecmp(search_key, p) == 0) { - found_it = TRUE; + found_it = true; break; } } - (void) fclose(f); + (void) sm_io_close(f, SM_TIME_DEFAULT); if (!found_it) { *statp = EX_NOTFOUND; @@ -5902,30 +5663,31 @@ text_getcanonname(name, hbsize, statp) { bool found; char *dot; - FILE *f; + SM_FILE_T *f; char linebuf[MAXLINE]; char cbuf[MAXNAME + 1]; char nbuf[MAXNAME + 1]; if (tTd(38, 20)) - dprintf("text_getcanonname(%s)\n", name); + sm_dprintf("text_getcanonname(%s)\n", name); - if (strlen(name) >= (SIZE_T) sizeof nbuf) + if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) { *statp = EX_UNAVAILABLE; - return FALSE; + return false; } - (void) strlcpy(nbuf, name, sizeof nbuf); dot = shorten_hostname(nbuf); - f = fopen(HostsFile, "r"); + f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY, + NULL); if (f == NULL) { *statp = EX_UNAVAILABLE; - return FALSE; + return false; } - found = FALSE; - while (!found && fgets(linebuf, MAXLINE, f) != NULL) + found = false; + while (!found && + sm_io_fgets(f, SM_TIME_DEFAULT, linebuf, MAXLINE) != NULL) { char *p = strpbrk(linebuf, "#\n"); @@ -5935,21 +5697,20 @@ text_getcanonname(name, hbsize, statp) found = extract_canonname(nbuf, dot, linebuf, cbuf, sizeof cbuf); } - (void) fclose(f); + (void) sm_io_close(f, SM_TIME_DEFAULT); if (!found) { *statp = EX_NOHOST; - return FALSE; + return false; } - if ((SIZE_T) hbsize >= strlen(cbuf)) + if (sm_strlcpy(name, cbuf, hbsize) >= hbsize) { - (void) strlcpy(name, cbuf, hbsize); - *statp = EX_OK; - return TRUE; + *statp = EX_UNAVAILABLE; + return false; } - *statp = EX_UNAVAILABLE; - return FALSE; + *statp = EX_OK; + return true; } /* ** STAB (Symbol Table) Modules @@ -5971,7 +5732,7 @@ stab_map_lookup(map, name, av, pstat) register STAB *s; if (tTd(38, 20)) - dprintf("stab_lookup(%s, %s)\n", + sm_dprintf("stab_lookup(%s, %s)\n", map->map_mname, name); s = stab(name, ST_ALIAS, ST_FIND); @@ -6012,19 +5773,19 @@ stab_map_open(map, mode) register MAP *map; int mode; { - FILE *af; + SM_FILE_T *af; long sff; struct stat st; if (tTd(38, 2)) - dprintf("stab_map_open(%s, %s, %d)\n", + sm_dprintf("stab_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); mode &= O_ACCMODE; if (mode != O_RDONLY) { errno = EPERM; - return FALSE; + return false; } sff = SFF_ROOTOK|SFF_REGONLY; @@ -6034,14 +5795,14 @@ stab_map_open(map, mode) sff |= SFF_SAFEDIRPATH; af = safefopen(map->map_file, O_RDONLY, 0444, sff); if (af == NULL) - return FALSE; - readaliases(map, af, FALSE, FALSE); + return false; + readaliases(map, af, false, false); - if (fstat(fileno(af), &st) >= 0) + if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0) map->map_mtime = st.st_mtime; - (void) fclose(af); + (void) sm_io_close(af, SM_TIME_DEFAULT); - return TRUE; + return true; } /* ** Implicit Modules @@ -6062,14 +5823,14 @@ impl_map_lookup(map, name, av, pstat) int *pstat; { if (tTd(38, 20)) - dprintf("impl_map_lookup(%s, %s)\n", + sm_dprintf("impl_map_lookup(%s, %s)\n", map->map_mname, name); -#ifdef NEWDB +#if NEWDB if (bitset(MF_IMPL_HASH, map->map_mflags)) return db_map_lookup(map, name, av, pstat); #endif /* NEWDB */ -#ifdef NDBM +#if NDBM if (bitset(MF_IMPL_NDBM, map->map_mflags)) return ndbm_map_lookup(map, name, av, pstat); #endif /* NDBM */ @@ -6087,13 +5848,13 @@ impl_map_store(map, lhs, rhs) char *rhs; { if (tTd(38, 12)) - dprintf("impl_map_store(%s, %s, %s)\n", + sm_dprintf("impl_map_store(%s, %s, %s)\n", map->map_mname, lhs, rhs); -#ifdef NEWDB +#if NEWDB if (bitset(MF_IMPL_HASH, map->map_mflags)) db_map_store(map, lhs, rhs); #endif /* NEWDB */ -#ifdef NDBM +#if NDBM if (bitset(MF_IMPL_NDBM, map->map_mflags)) ndbm_map_store(map, lhs, rhs); #endif /* NDBM */ @@ -6110,27 +5871,27 @@ impl_map_open(map, mode) int mode; { if (tTd(38, 2)) - dprintf("impl_map_open(%s, %s, %d)\n", + sm_dprintf("impl_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); mode &= O_ACCMODE; -#ifdef NEWDB +#if NEWDB map->map_mflags |= MF_IMPL_HASH; if (hash_map_open(map, mode)) { # ifdef NDBM_YP_COMPAT if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL) # endif /* NDBM_YP_COMPAT */ - return TRUE; + return true; } else map->map_mflags &= ~MF_IMPL_HASH; #endif /* NEWDB */ -#ifdef NDBM +#if NDBM map->map_mflags |= MF_IMPL_NDBM; if (ndbm_map_open(map, mode)) { - return TRUE; + return true; } else map->map_mflags &= ~MF_IMPL_NDBM; @@ -6149,7 +5910,7 @@ impl_map_open(map, mode) if (mode == O_RDONLY) return stab_map_open(map, mode); else - return FALSE; + return false; } @@ -6162,9 +5923,9 @@ impl_map_close(map) MAP *map; { if (tTd(38, 9)) - dprintf("impl_map_close(%s, %s, %lx)\n", + sm_dprintf("impl_map_close(%s, %s, %lx)\n", map->map_mname, map->map_file, map->map_mflags); -#ifdef NEWDB +#if NEWDB if (bitset(MF_IMPL_HASH, map->map_mflags)) { db_map_close(map); @@ -6172,7 +5933,7 @@ impl_map_close(map) } #endif /* NEWDB */ -#ifdef NDBM +#if NDBM if (bitset(MF_IMPL_NDBM, map->map_mflags)) { ndbm_map_close(map); @@ -6198,48 +5959,40 @@ user_map_open(map, mode) int mode; { if (tTd(38, 2)) - dprintf("user_map_open(%s, %d)\n", + sm_dprintf("user_map_open(%s, %d)\n", map->map_mname, mode); mode &= O_ACCMODE; if (mode != O_RDONLY) { /* issue a pseudo-error message */ -#ifdef ENOSYS - errno = ENOSYS; -#else /* ENOSYS */ -# ifdef EFTYPE - errno = EFTYPE; -# else /* EFTYPE */ - errno = ENXIO; -# endif /* EFTYPE */ -#endif /* ENOSYS */ - return FALSE; + errno = SM_EMAPCANTWRITE; + return false; } if (map->map_valcolnm == NULL) /* EMPTY */ /* nothing */ ; - else if (strcasecmp(map->map_valcolnm, "name") == 0) + else if (sm_strcasecmp(map->map_valcolnm, "name") == 0) map->map_valcolno = 1; - else if (strcasecmp(map->map_valcolnm, "passwd") == 0) + else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0) map->map_valcolno = 2; - else if (strcasecmp(map->map_valcolnm, "uid") == 0) + else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0) map->map_valcolno = 3; - else if (strcasecmp(map->map_valcolnm, "gid") == 0) + else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0) map->map_valcolno = 4; - else if (strcasecmp(map->map_valcolnm, "gecos") == 0) + else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0) map->map_valcolno = 5; - else if (strcasecmp(map->map_valcolnm, "dir") == 0) + else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0) map->map_valcolno = 6; - else if (strcasecmp(map->map_valcolnm, "shell") == 0) + else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0) map->map_valcolno = 7; else { syserr("User map %s: unknown column name %s", map->map_mname, map->map_valcolnm); - return FALSE; + return false; } - return TRUE; + return true; } @@ -6255,15 +6008,15 @@ user_map_lookup(map, key, av, statp) char **av; int *statp; { - struct passwd *pw; auto bool fuzzy; + SM_MBDB_T user; if (tTd(38, 20)) - dprintf("user_map_lookup(%s, %s)\n", + sm_dprintf("user_map_lookup(%s, %s)\n", map->map_mname, key); - pw = finduser(key, &fuzzy); - if (pw == NULL) + *statp = finduser(key, &fuzzy, &user); + if (*statp != EX_OK) return NULL; if (bitset(MF_MATCHONLY, map->map_mflags)) return map_rewrite(map, key, strlen(key), NULL); @@ -6276,33 +6029,35 @@ user_map_lookup(map, key, av, statp) { case 0: case 1: - rwval = pw->pw_name; + rwval = user.mbdb_name; break; case 2: - rwval = pw->pw_passwd; + rwval = "x"; /* passwd no longer supported */ break; case 3: - snprintf(buf, sizeof buf, "%d", (int) pw->pw_uid); + (void) sm_snprintf(buf, sizeof buf, "%d", + (int) user.mbdb_uid); rwval = buf; break; case 4: - snprintf(buf, sizeof buf, "%d", (int) pw->pw_gid); + (void) sm_snprintf(buf, sizeof buf, "%d", + (int) user.mbdb_gid); rwval = buf; break; case 5: - rwval = pw->pw_gecos; + rwval = user.mbdb_fullname; break; case 6: - rwval = pw->pw_dir; + rwval = user.mbdb_homedir; break; case 7: - rwval = pw->pw_shell; + rwval = user.mbdb_shell; break; } return map_rewrite(map, rwval, strlen(rwval), av); @@ -6334,14 +6089,14 @@ prog_map_lookup(map, name, av, statp) char buf[MAXLINE]; if (tTd(38, 20)) - dprintf("prog_map_lookup(%s, %s) %s\n", + sm_dprintf("prog_map_lookup(%s, %s) %s\n", map->map_mname, name, map->map_file); i = 0; argv[i++] = map->map_file; if (map->map_rebuild != NULL) { - snprintf(buf, sizeof buf, "%s", map->map_rebuild); + (void) sm_strlcpy(buf, map->map_rebuild, sizeof buf); for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t")) { if (i >= MAXPV - 1) @@ -6353,21 +6108,21 @@ prog_map_lookup(map, name, av, statp) argv[i] = NULL; if (tTd(38, 21)) { - dprintf("prog_open:"); + sm_dprintf("prog_open:"); for (i = 0; argv[i] != NULL; i++) - dprintf(" %s", argv[i]); - dprintf("\n"); + sm_dprintf(" %s", argv[i]); + sm_dprintf("\n"); } - (void) blocksignal(SIGCHLD); + (void) sm_blocksignal(SIGCHLD); pid = prog_open(argv, &fd, CurEnv); if (pid < 0) { if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("prog_map_lookup(%s) failed (%s) -- closing", - map->map_mname, errstring(errno)); + map->map_mname, sm_errstring(errno)); else if (tTd(38, 9)) - dprintf("prog_map_lookup(%s) failed (%s) -- closing", - map->map_mname, errstring(errno)); + sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing", + map->map_mname, sm_errstring(errno)); map->map_mflags &= ~(MF_VALID|MF_OPEN); *statp = EX_OSFILE; return NULL; @@ -6376,14 +6131,14 @@ prog_map_lookup(map, name, av, statp) if (i < 0) { syserr("prog_map_lookup(%s): read error %s\n", - map->map_mname, errstring(errno)); + map->map_mname, sm_errstring(errno)); rval = NULL; } else if (i == 0) { if (tTd(38, 20)) - dprintf("prog_map_lookup(%s): empty answer\n", - map->map_mname); + sm_dprintf("prog_map_lookup(%s): empty answer\n", + map->map_mname); rval = NULL; } else @@ -6408,13 +6163,13 @@ prog_map_lookup(map, name, av, statp) (void) close(fd); status = waitfor(pid); save_errno = errno; - (void) releasesignal(SIGCHLD); + (void) sm_releasesignal(SIGCHLD); errno = save_errno; if (status == -1) { syserr("prog_map_lookup(%s): wait error %s\n", - map->map_mname, errstring(errno)); + map->map_mname, sm_errstring(errno)); *statp = EX_SOFTWARE; rval = NULL; } @@ -6426,7 +6181,7 @@ prog_map_lookup(map, name, av, statp) else { syserr("prog_map_lookup(%s): child died on signal %d", - map->map_mname, status); + map->map_mname, status); *statp = EX_UNAVAILABLE; rval = NULL; } @@ -6446,7 +6201,7 @@ prog_map_lookup(map, name, av, statp) ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix. ** ** We don't need an explicit open, since all maps are -** opened during startup, including underlying maps. +** opened on demand. */ /* @@ -6461,7 +6216,7 @@ seq_map_parse(map, ap) int maxmap; if (tTd(38, 2)) - dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap); + sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap); maxmap = 0; while (*ap != '\0') { @@ -6490,7 +6245,7 @@ seq_map_parse(map, ap) syserr("Sequence map %s: unknown member map %s", map->map_mname, ap); } - else if (maxmap == MAXMAPSTACK) + else if (maxmap >= MAXMAPSTACK) { syserr("Sequence map %s: too many member maps (%d max)", map->map_mname, MAXMAPSTACK); @@ -6502,10 +6257,9 @@ seq_map_parse(map, ap) } ap = p; } - return TRUE; + return true; } - /* ** SWITCH_MAP_OPEN -- open a switched map ** @@ -6527,19 +6281,19 @@ switch_map_open(map, mode) char *maptype[MAXMAPSTACK]; if (tTd(38, 2)) - dprintf("switch_map_open(%s, %s, %d)\n", + sm_dprintf("switch_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); mode &= O_ACCMODE; nmaps = switch_map_find(map->map_file, maptype, map->map_return); if (tTd(38, 19)) { - dprintf("\tswitch_map_find => %d\n", nmaps); + sm_dprintf("\tswitch_map_find => %d\n", nmaps); for (mapno = 0; mapno < nmaps; mapno++) - dprintf("\t\t%s\n", maptype[mapno]); + sm_dprintf("\t\t%s\n", maptype[mapno]); } if (nmaps <= 0 || nmaps > MAXMAPSTACK) - return FALSE; + return false; for (mapno = 0; mapno < nmaps; mapno++) { @@ -6548,8 +6302,8 @@ switch_map_open(map, mode) if (maptype[mapno] == NULL) continue; - (void) snprintf(nbuf, sizeof nbuf, "%s.%s", - map->map_mname, maptype[mapno]); + (void) sm_strlcpyn(nbuf, sizeof nbuf, 3, + map->map_mname, ".", maptype[mapno]); s = stab(nbuf, ST_MAP, ST_FIND); if (s == NULL) { @@ -6560,15 +6314,16 @@ switch_map_open(map, mode) { map->map_stack[mapno] = &s->s_map; if (tTd(38, 4)) - dprintf("\tmap_stack[%d] = %s:%s\n", - mapno, s->s_map.map_class->map_cname, - nbuf); + sm_dprintf("\tmap_stack[%d] = %s:%s\n", + mapno, + s->s_map.map_class->map_cname, + nbuf); } } - return TRUE; + return true; } - +#if 0 /* ** SEQ_MAP_CLOSE -- close all underlying maps */ @@ -6580,7 +6335,7 @@ seq_map_close(map) int mapno; if (tTd(38, 9)) - dprintf("seq_map_close(%s)\n", map->map_mname); + sm_dprintf("seq_map_close(%s)\n", map->map_mname); for (mapno = 0; mapno < MAXMAPSTACK; mapno++) { @@ -6593,7 +6348,7 @@ seq_map_close(map) mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); } } - +#endif /* 0 */ /* ** SEQ_MAP_LOOKUP -- sequenced map lookup @@ -6608,10 +6363,10 @@ seq_map_lookup(map, key, args, pstat) { int mapno; int mapbit = 0x01; - bool tempfail = FALSE; + bool tempfail = false; if (tTd(38, 20)) - dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key); + sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key); for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++) { @@ -6638,7 +6393,7 @@ seq_map_lookup(map, key, args, pstat) { if (bitset(mapbit, map->map_return[MA_TRYAGAIN])) return NULL; - tempfail = TRUE; + tempfail = true; } else if (bitset(mapbit, map->map_return[MA_NOTFOUND])) break; @@ -6650,7 +6405,6 @@ seq_map_lookup(map, key, args, pstat) return NULL; } - /* ** SEQ_MAP_STORE -- sequenced map store */ @@ -6664,7 +6418,7 @@ seq_map_store(map, key, val) int mapno; if (tTd(38, 12)) - dprintf("seq_map_store(%s, %s, %s)\n", + sm_dprintf("seq_map_store(%s, %s, %s)\n", map->map_mname, key, val); for (mapno = 0; mapno < MAXMAPSTACK; mapno++) @@ -6690,7 +6444,7 @@ null_map_open(map, mode) MAP *map; int mode; { - return TRUE; + return true; } /* ARGSUSED */ @@ -6722,7 +6476,6 @@ null_map_store(map, key, val) return; } - /* ** BOGUS stubs */ @@ -6740,9 +6493,9 @@ bogus_map_lookup(map, key, args, pstat) MAPCLASS BogusMapClass = { - "bogus-map", NULL, 0, - NULL, bogus_map_lookup, null_map_store, - null_map_open, null_map_close, + "bogus-map", NULL, 0, + NULL, bogus_map_lookup, null_map_store, + null_map_open, null_map_close, }; /* ** MACRO modules @@ -6758,21 +6511,21 @@ macro_map_lookup(map, name, av, statp) int mid; if (tTd(38, 20)) - dprintf("macro_map_lookup(%s, %s)\n", map->map_mname, + sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname, name == NULL ? "NULL" : name); if (name == NULL || *name == '\0' || - (mid = macid(name, NULL)) == '\0') + (mid = macid(name)) == 0) { *statp = EX_CONFIG; return NULL; } if (av[1] == NULL) - define(mid, NULL, CurEnv); + macdefine(&CurEnv->e_macro, A_PERM, mid, NULL); else - define(mid, newstr(av[1]), CurEnv); + macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]); *statp = EX_OK; return ""; @@ -6781,14 +6534,12 @@ macro_map_lookup(map, name, av, statp) ** REGEX modules */ -#ifdef MAP_REGEX +#if MAP_REGEX # include <regex.h> # define DEFAULT_DELIM CONDELSE - # define END_OF_FIELDS -1 - # define ERRBUF_SIZE 80 # define MAX_MATCH 32 @@ -6810,7 +6561,7 @@ parse_fields(s, ibuf, blen, nr_substrings) { register char *cp; int i = 0; - bool lastone = FALSE; + bool lastone = false; blen--; /* for terminating END_OF_FIELDS */ cp = s; @@ -6825,7 +6576,7 @@ parse_fields(s, ibuf, blen, nr_substrings) } if (*cp == '\0') { - lastone = TRUE; + lastone = true; break; } } @@ -6862,16 +6613,14 @@ regex_map_init(map, ap) register char *p; char *sub_param = NULL; int pflags; - static char defdstr[] = { (char)DEFAULT_DELIM, '\0' }; + static char defdstr[] = { (char) DEFAULT_DELIM, '\0' }; if (tTd(38, 2)) - dprintf("regex_map_init: mapname '%s', args '%s'\n", + sm_dprintf("regex_map_init: mapname '%s', args '%s'\n", map->map_mname, ap); pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB; - p = ap; - map_p = (struct regex_map *) xnalloc(sizeof *map_p); map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t)); @@ -6928,7 +6677,7 @@ regex_map_init(map, ap) *p++ = '\0'; } if (tTd(38, 3)) - dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags); + sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags); if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0) { @@ -6936,11 +6685,11 @@ regex_map_init(map, ap) char errbuf[ERRBUF_SIZE]; (void) regerror(regerr, map_p->regex_pattern_buf, - errbuf, ERRBUF_SIZE); - syserr("pattern-compile-error: %s\n", errbuf); - sm_free(map_p->regex_pattern_buf); - sm_free(map_p); - return FALSE; + errbuf, sizeof errbuf); + syserr("pattern-compile-error: %s", errbuf); + sm_free(map_p->regex_pattern_buf); /* XXX */ + sm_free(map_p); /* XXX */ + return false; } if (map->map_app != NULL) @@ -6959,28 +6708,28 @@ regex_map_init(map, ap) substrings = map_p->regex_pattern_buf->re_nsub + 1; if (tTd(38, 3)) - dprintf("regex_map_init: nr of substrings %d\n", + sm_dprintf("regex_map_init: nr of substrings %d\n", substrings); if (substrings >= MAX_MATCH) { - syserr("too many substrings, %d max\n", MAX_MATCH); - sm_free(map_p->regex_pattern_buf); - sm_free(map_p); - return FALSE; + syserr("too many substrings, %d max", MAX_MATCH); + sm_free(map_p->regex_pattern_buf); /* XXX */ + sm_free(map_p); /* XXX */ + return false; } if (sub_param != NULL && sub_param[0] != '\0') { /* optional parameter -sfields */ if (parse_fields(sub_param, fields, MAX_MATCH + 1, substrings) == -1) - return FALSE; + return false; } else { - /* set default fields */ int i; + /* set default fields */ for (i = 0; i < substrings; i++) fields[i] = i; fields[i] = END_OF_FIELDS; @@ -6990,15 +6739,14 @@ regex_map_init(map, ap) { int *ip; - dprintf("regex_map_init: subfields"); + sm_dprintf("regex_map_init: subfields"); for (ip = fields; *ip != END_OF_FIELDS; ip++) - dprintf(" %d", *ip); - dprintf("\n"); + sm_dprintf(" %d", *ip); + sm_dprintf("\n"); } } - map->map_db1 = (ARBPTR_T)map_p; /* dirty hack */ - - return TRUE; + map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */ + return true; } static char * @@ -7029,9 +6777,9 @@ regex_map_lookup(map, name, av, statp) { char **cpp; - dprintf("regex_map_lookup: key '%s'\n", name); + sm_dprintf("regex_map_lookup: key '%s'\n", name); for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) - dprintf("regex_map_lookup: arg '%s'\n", *cpp); + sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp); } map_p = (struct regex_map *)(map->map_db1); @@ -7042,7 +6790,7 @@ regex_map_lookup(map, name, av, statp) { /* option -n */ if (reg_res == REG_NOMATCH) - return regex_map_rewrite(map, "", (size_t)0, av); + return regex_map_rewrite(map, "", (size_t) 0, av); else return NULL; } @@ -7054,9 +6802,9 @@ regex_map_lookup(map, name, av, statp) /* option -s */ static char retbuf[MAXNAME]; int fields[MAX_MATCH + 1]; - bool first = TRUE; + bool first = true; int anglecnt = 0, cmntcnt = 0, spacecnt = 0; - bool quotemode = FALSE, bslashmode = FALSE; + bool quotemode = false, bslashmode = false; register char *dp, *sp; char *endp, *ldp; int *ip; @@ -7088,8 +6836,7 @@ regex_map_lookup(map, name, av, statp) } } else - first = FALSE; - + first = false; if (*ip >= MAX_MATCH || pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0) @@ -7104,40 +6851,40 @@ regex_map_lookup(map, name, av, statp) if (bslashmode) { *dp++ = *sp; - bslashmode = FALSE; + bslashmode = false; } else if (quotemode && *sp != '"' && *sp != '\\') { *dp++ = *sp; } - else switch(*dp++ = *sp) + else switch (*dp++ = *sp) { - case '\\': - bslashmode = TRUE; + case '\\': + bslashmode = true; break; - case '(': + case '(': cmntcnt++; break; - case ')': + case ')': cmntcnt--; break; - case '<': + case '<': anglecnt++; break; - case '>': + case '>': anglecnt--; break; - case ' ': + case ' ': spacecnt++; break; - case '"': + case '"': quotemode = !quotemode; break; } @@ -7163,7 +6910,7 @@ regex_map_lookup(map, name, av, statp) /* ** NSD modules */ -#ifdef MAP_NSD +#if MAP_NSD # include <ndbm.h> # define _DATUM_DEFINED @@ -7171,9 +6918,9 @@ regex_map_lookup(map, name, av, statp) typedef struct ns_map_list { - ns_map_t *map; - char *mapname; - struct ns_map_list *next; + ns_map_t *map; /* XXX ns_ ? */ + char *mapname; + struct ns_map_list *next; } ns_map_list_t; static ns_map_t * @@ -7216,11 +6963,11 @@ nsd_map_lookup(map, name, av, statp) char buf[MAXLINE]; if (tTd(38, 20)) - dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name); + sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name); buflen = strlen(name); if (buflen > sizeof keybuf - 1) - buflen = sizeof keybuf - 1; + buflen = sizeof keybuf - 1; /* XXX simply cut off? */ memmove(keybuf, name, buflen); keybuf[buflen] = '\0'; if (!bitset(MF_NOFOLDCASE, map->map_mflags)) @@ -7230,7 +6977,7 @@ nsd_map_lookup(map, name, av, statp) if (ns_map == NULL) { if (tTd(38, 20)) - dprintf("nsd_map_t_find failed\n"); + sm_dprintf("nsd_map_t_find failed\n"); *statp = EX_UNAVAILABLE; return NULL; } @@ -7274,19 +7021,19 @@ arith_map_lookup(map, name, av, statp) { long r; long v[2]; - bool res = FALSE; + bool res = false; bool boolres; static char result[16]; char **cpp; if (tTd(38, 2)) { - dprintf("arith_map_lookup: key '%s'\n", name); + sm_dprintf("arith_map_lookup: key '%s'\n", name); for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) - dprintf("arith_map_lookup: arg '%s'\n", *cpp); + sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp); } r = 0; - boolres = FALSE; + boolres = false; cpp = av; *statp = EX_OK; @@ -7295,15 +7042,15 @@ arith_map_lookup(map, name, av, statp) ** - no check is made whether they are really numbers ** - just ignores args after the second */ + for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++) v[r++] = strtol(*cpp, NULL, 0); /* operator and (at least) two operands given? */ if (name != NULL && r == 2) { - switch(*name) + switch (*name) { -#if _FFR_ARITH case '|': r = v[0] | v[1]; break; @@ -7317,8 +7064,6 @@ arith_map_lookup(map, name, av, statp) return NULL; r = v[0] % v[1]; break; -#endif /* _FFR_ARITH */ - case '+': r = v[0] + v[1]; break; @@ -7339,12 +7084,12 @@ arith_map_lookup(map, name, av, statp) case 'l': res = v[0] < v[1]; - boolres = TRUE; + boolres = true; break; case '=': res = v[0] == v[1]; - boolres = TRUE; + boolres = true; break; default: @@ -7357,9 +7102,10 @@ arith_map_lookup(map, name, av, statp) return NULL; } if (boolres) - snprintf(result, sizeof result, res ? "TRUE" : "FALSE"); + (void) sm_snprintf(result, sizeof result, + res ? "TRUE" : "FALSE"); else - snprintf(result, sizeof result, "%ld", r); + (void) sm_snprintf(result, sizeof result, "%ld", r); return result; } *statp = EX_CONFIG; diff --git a/gnu/usr.sbin/sendmail/sendmail/mci.c b/gnu/usr.sbin/sendmail/sendmail/mci.c index eed835628cc..4ae5f6db929 100644 --- a/gnu/usr.sbin/sendmail/sendmail/mci.c +++ b/gnu/usr.sbin/sendmail/sendmail/mci.c @@ -11,12 +11,9 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: mci.c,v 8.133.10.8 2001/05/03 17:24:10 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +SM_RCSID("@(#)$Sendmail: mci.c,v 8.196 2001/09/04 22:43:04 ca Exp $") #if NETINET || NETINET6 # include <arpa/inet.h> @@ -29,7 +26,7 @@ static int mci_generate_persistent_path __P((const char *, char *, static bool mci_load_persistent __P((MCI *)); static void mci_uncache __P((MCI **, bool)); static int mci_lock_host_statfile __P((MCI *)); -static int mci_read_persistent __P((FILE *, MCI *)); +static int mci_read_persistent __P((SM_FILE_T *, MCI *)); /* ** Mail Connection Information (MCI) Caching Module. @@ -103,16 +100,16 @@ mci_cache(mci) /* otherwise we may have to clear the slot */ if (*mcislot != NULL) - mci_uncache(mcislot, TRUE); + mci_uncache(mcislot, true); if (tTd(42, 5)) - dprintf("mci_cache: caching %lx (%s) in slot %d\n", - (u_long) mci, mci->mci_host, - (int)(mcislot - MciCache)); + sm_dprintf("mci_cache: caching %p (%s) in slot %d\n", + mci, mci->mci_host, (int) (mcislot - MciCache)); if (tTd(91, 100)) sm_syslog(LOG_DEBUG, CurEnv->e_id, "mci_cache: caching %lx (%.100s) in slot %d", - (u_long) mci, mci->mci_host, mcislot - MciCache); + (unsigned long) mci, mci->mci_host, + (int) (mcislot - MciCache)); *mcislot = mci; mci->mci_flags |= MCIF_CACHED; @@ -145,7 +142,7 @@ mci_scan(savemci) if (MciCache == NULL) { /* first call */ - MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache); + MciCache = (MCI **) sm_pmalloc_x(MaxMciCache * sizeof *MciCache); memset((char *) MciCache, '\0', MaxMciCache * sizeof *MciCache); return &MciCache[0]; } @@ -170,7 +167,7 @@ mci_scan(savemci) bestmci = &MciCache[i]; /* close it */ - mci_uncache(bestmci, TRUE); + mci_uncache(bestmci, true); continue; } if (*bestmci == NULL) @@ -187,8 +184,8 @@ mci_scan(savemci) ** ** Parameters: ** mcislot -- the slot to empty. -** doquit -- if TRUE, send QUIT protocol on this connection. -** if FALSE, we are assumed to be in a forked child; +** doquit -- if true, send QUIT protocol on this connection. +** if false, we are assumed to be in a forked child; ** all we want to do is close the file(s). ** ** Returns: @@ -213,17 +210,16 @@ mci_uncache(mcislot, doquit) mci_unlock_host(mci); if (tTd(42, 5)) - dprintf("mci_uncache: uncaching %lx (%s) from slot %d (%d)\n", - (u_long) mci, mci->mci_host, - (int)(mcislot - MciCache), doquit); + sm_dprintf("mci_uncache: uncaching %p (%s) from slot %d (%d)\n", + mci, mci->mci_host, (int) (mcislot - MciCache), + doquit); if (tTd(91, 100)) sm_syslog(LOG_DEBUG, CurEnv->e_id, "mci_uncache: uncaching %lx (%.100s) from slot %d (%d)", - (u_long) mci, mci->mci_host, - mcislot - MciCache, doquit); + (unsigned long) mci, mci->mci_host, + (int) (mcislot - MciCache), doquit); mci->mci_deliveries = 0; -#if SMTP if (doquit) { message("Closing connection to %s", mci->mci_host); @@ -233,30 +229,45 @@ mci_uncache(mcislot, doquit) /* only uses the envelope to flush the transcript file */ if (mci->mci_state != MCIS_CLOSED) smtpquit(mci->mci_mailer, mci, &BlankEnvelope); -# ifdef XLA +#if XLA xla_host_end(mci->mci_host); -# endif /* XLA */ +#endif /* XLA */ } else -#endif /* SMTP */ { if (mci->mci_in != NULL) - (void) fclose(mci->mci_in); + (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); if (mci->mci_out != NULL) - (void) fclose(mci->mci_out); + (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); mci->mci_in = mci->mci_out = NULL; mci->mci_state = MCIS_CLOSED; mci->mci_exitstat = EX_OK; mci->mci_errno = 0; mci->mci_flags = 0; + + mci->mci_retryrcpt = false; + mci->mci_tolist = NULL; +#if PIPELINING + mci->mci_okrcpts = 0; +#endif /* PIPELINING */ + } + + SM_FREE_CLR(mci->mci_status); + SM_FREE_CLR(mci->mci_rstatus); + SM_FREE_CLR(mci->mci_heloname); + if (mci->mci_rpool != NULL) + { + sm_rpool_free(mci->mci_rpool); + mci->mci_macro.mac_rpool = NULL; + mci->mci_rpool = NULL; } } /* ** MCI_FLUSH -- flush the entire cache ** ** Parameters: -** doquit -- if TRUE, send QUIT protocol. -** if FALSE, just close the connection. +** doquit -- if true, send QUIT protocol. +** if false, just close the connection. ** allbut -- but leave this one open. ** ** Returns: @@ -281,6 +292,13 @@ mci_flush(doquit, allbut) } /* ** MCI_GET -- get information about a particular host +** +** Parameters: +** host -- host to look for. +** m -- mailer. +** +** Returns: +** mci for this host (might be new). */ MCI * @@ -290,13 +308,10 @@ mci_get(host, m) { register MCI *mci; register STAB *s; - -#if DAEMON extern SOCKADDR CurHostAddr; /* clear CurHostAddr so we don't get a bogus address with this name */ memset(&CurHostAddr, '\0', sizeof CurHostAddr); -#endif /* DAEMON */ /* clear out any expired connections */ (void) mci_scan(NULL); @@ -307,8 +322,21 @@ mci_get(host, m) s = stab(host, ST_MCI + m->m_mno, ST_ENTER); mci = &s->s_mci; + /* initialize per-message data */ + mci->mci_retryrcpt = false; + mci->mci_tolist = NULL; +#if PIPELINING + mci->mci_okrcpts = 0; +#endif /* PIPELINING */ + + if (mci->mci_rpool == NULL) + mci->mci_rpool = sm_rpool_new_x(NULL); + + if (mci->mci_macro.mac_rpool == NULL) + mci->mci_macro.mac_rpool = mci->mci_rpool; + /* - ** We don't need to load the peristent data if we have data + ** We don't need to load the persistent data if we have data ** already loaded in the cache. */ @@ -317,7 +345,7 @@ mci_get(host, m) !mci_load_persistent(mci)) { if (tTd(42, 2)) - dprintf("mci_get(%s %s): lock failed\n", + sm_dprintf("mci_get(%s %s): lock failed\n", host, m->m_name); mci->mci_exitstat = EX_TEMPFAIL; mci->mci_state = MCIS_CLOSED; @@ -327,12 +355,11 @@ mci_get(host, m) if (tTd(42, 2)) { - dprintf("mci_get(%s %s): mci_state=%d, _flags=%lx, _exitstat=%d, _errno=%d\n", + sm_dprintf("mci_get(%s %s): mci_state=%d, _flags=%lx, _exitstat=%d, _errno=%d\n", host, m->m_name, mci->mci_state, mci->mci_flags, mci->mci_exitstat, mci->mci_errno); } -#if SMTP if (mci->mci_state == MCIS_OPEN) { /* poke the connection to see if it's still alive */ @@ -345,19 +372,17 @@ mci_get(host, m) mci->mci_exitstat = EX_OK; mci->mci_state = MCIS_CLOSED; } -# if DAEMON else { - /* get peer host address for logging reasons only */ + /* get peer host address */ /* (this should really be in the mci struct) */ SOCKADDR_LEN_T socklen = sizeof CurHostAddr; - (void) getpeername(fileno(mci->mci_in), + (void) getpeername(sm_io_getinfo(mci->mci_in, + SM_IO_WHAT_FD, NULL), (struct sockaddr *) &CurHostAddr, &socklen); } -# endif /* DAEMON */ } -#endif /* SMTP */ if (mci->mci_state == MCIS_CLOSED) { time_t now = curtime(); @@ -374,7 +399,39 @@ mci_get(host, m) return mci; } /* +** MCI_NEW -- allocate new MCI structure +** +** Parameters: +** rpool -- if non-NULL: allocate from that rpool. +** +** Returns: +** mci (new). +*/ + +MCI * +mci_new(rpool) + SM_RPOOL_T *rpool; +{ + register MCI *mci; + + if (rpool == NULL) + mci = (MCI *) sm_malloc_x(sizeof *mci); + else + mci = (MCI *) sm_rpool_malloc_x(rpool, sizeof *mci); + memset((char *) mci, '\0', sizeof *mci); + mci->mci_rpool = sm_rpool_new_x(NULL); + mci->mci_macro.mac_rpool = mci->mci_rpool; + return mci; +} +/* ** MCI_MATCH -- check connection cache for a particular host +** +** Parameters: +** host -- host to look for. +** m -- mailer. +** +** Returns: +** true iff open connection exists. */ bool @@ -386,15 +443,13 @@ mci_match(host, m) register STAB *s; if (m->m_mno < 0 || m->m_mno > MAXMAILERS) - return FALSE; + return false; s = stab(host, ST_MCI + m->m_mno, ST_FIND); if (s == NULL) - return FALSE; + return false; mci = &s->s_mci; - if (mci->mci_state == MCIS_OPEN) - return TRUE; - return FALSE; + return mci->mci_state == MCIS_OPEN; } /* ** MCI_SETSTAT -- set status codes in MCI structure. @@ -420,12 +475,13 @@ mci_setstat(mci, xstat, dstat, rstat) if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL) mci->mci_exitstat = xstat; - mci->mci_status = dstat; - if (mci->mci_rstatus != NULL) - sm_free(mci->mci_rstatus); + SM_FREE_CLR(mci->mci_status); + if (dstat != NULL) + mci->mci_status = sm_strdup_x(dstat); + + SM_FREE_CLR(mci->mci_rstatus); if (rstat != NULL) - rstat = newstr(rstat); - mci->mci_rstatus = rstat; + mci->mci_rstatus = sm_strdup_x(rstat); } /* ** MCI_DUMP -- dump the contents of an MCI structure. @@ -448,7 +504,6 @@ struct mcifbits static struct mcifbits MciFlags[] = { { MCIF_VALID, "VALID" }, - { MCIF_TEMP, "TEMP" }, { MCIF_CACHED, "CACHED" }, { MCIF_ESMTP, "ESMTP" }, { MCIF_EXPN, "EXPN" }, @@ -462,10 +517,18 @@ static struct mcifbits MciFlags[] = { MCIF_8BITOK, "8BITOK" }, { MCIF_CVT7TO8, "CVT7TO8" }, { MCIF_INMIME, "INMIME" }, + { MCIF_AUTH, "AUTH" }, + { MCIF_AUTHACT, "AUTHACT" }, + { MCIF_ENHSTAT, "ENHSTAT" }, + { MCIF_PIPELINED, "PIPELINED" }, +#if STARTTLS + { MCIF_TLS, "TLS" }, + { MCIF_TLSACT, "TLSACT" }, +#endif /* STARTTLS */ + { MCIF_DLVR_BY, "DLVR_BY" }, { 0, NULL } }; - void mci_dump(mci, logit) register MCI *mci; @@ -477,14 +540,14 @@ mci_dump(mci, logit) sep = logit ? " " : "\n\t"; p = buf; - snprintf(p, SPACELEFT(buf, p), "MCI@%lx: ", (u_long) mci); + (void) sm_snprintf(p, SPACELEFT(buf, p), "MCI@%p: ", mci); p += strlen(p); if (mci == NULL) { - snprintf(p, SPACELEFT(buf, p), "NULL"); + (void) sm_snprintf(p, SPACELEFT(buf, p), "NULL"); goto printit; } - snprintf(p, SPACELEFT(buf, p), "flags=%lx", mci->mci_flags); + (void) sm_snprintf(p, SPACELEFT(buf, p), "flags=%lx", mci->mci_flags); p += strlen(p); if (mci->mci_flags != 0) { @@ -495,38 +558,37 @@ mci_dump(mci, logit) { if (!bitset(f->mcif_bit, mci->mci_flags)) continue; - snprintf(p, SPACELEFT(buf, p), "%s,", f->mcif_name); + (void) sm_strlcpyn(p, SPACELEFT(buf, p), 2, + f->mcif_name, ","); p += strlen(p); } p[-1] = '>'; } - snprintf(p, SPACELEFT(buf, p), + + /* Note: sm_snprintf() takes care of NULL arguments for %s */ + (void) sm_snprintf(p, SPACELEFT(buf, p), ",%serrno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s", sep, mci->mci_errno, mci->mci_herrno, mci->mci_exitstat, mci->mci_state, (int) mci->mci_pid, sep); p += strlen(p); - snprintf(p, SPACELEFT(buf, p), + (void) sm_snprintf(p, SPACELEFT(buf, p), "maxsize=%ld, phase=%s, mailer=%s,%s", - mci->mci_maxsize, - mci->mci_phase == NULL ? "NULL" : mci->mci_phase, + mci->mci_maxsize, mci->mci_phase, mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name, sep); p += strlen(p); - snprintf(p, SPACELEFT(buf, p), + (void) sm_snprintf(p, SPACELEFT(buf, p), "status=%s, rstatus=%s,%s", - mci->mci_status == NULL ? "NULL" : mci->mci_status, - mci->mci_rstatus == NULL ? "NULL" : mci->mci_rstatus, - sep); + mci->mci_status, mci->mci_rstatus, sep); p += strlen(p); - snprintf(p, SPACELEFT(buf, p), + (void) sm_snprintf(p, SPACELEFT(buf, p), "host=%s, lastuse=%s", - mci->mci_host == NULL ? "NULL" : mci->mci_host, - ctime(&mci->mci_lastuse)); + mci->mci_host, ctime(&mci->mci_lastuse)); printit: if (logit) sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf); else - printf("%s\n", buf); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", buf); } /* ** MCI_DUMP_ALL -- print the entire MCI cache @@ -559,7 +621,7 @@ mci_dump_all(logit) ** file, and if we want to do that, we ought to have ** locked it. This has the (according to some) ** desirable effect of serializing connectivity with -** remote hosts -- i.e.: one connection to a give +** remote hosts -- i.e.: one connection to a given ** host at a time. ** ** Parameters: @@ -577,7 +639,7 @@ mci_lock_host(mci) if (mci == NULL) { if (tTd(56, 1)) - dprintf("mci_lock_host: NULL mci\n"); + sm_dprintf("mci_lock_host: NULL mci\n"); return EX_OK; } @@ -599,15 +661,16 @@ mci_lock_host_statfile(mci) return EX_OK; if (tTd(56, 2)) - dprintf("mci_lock_host: attempting to lock %s\n", - mci->mci_host); + sm_dprintf("mci_lock_host: attempting to lock %s\n", + mci->mci_host); - if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, TRUE) < 0) + if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, + true) < 0) { /* of course this should never happen */ if (tTd(56, 2)) - dprintf("mci_lock_host: Failed to generate host path for %s\n", - mci->mci_host); + sm_dprintf("mci_lock_host: Failed to generate host path for %s\n", + mci->mci_host); retVal = EX_TEMPFAIL; goto cleanup; @@ -618,24 +681,24 @@ mci_lock_host_statfile(mci) if (mci->mci_statfile == NULL) { - syserr("mci_lock_host: cannot create host lock file %s", - fname); + syserr("mci_lock_host: cannot create host lock file %s", fname); goto cleanup; } - if (!lockfile(fileno(mci->mci_statfile), fname, "", LOCK_EX|LOCK_NB)) + if (!lockfile(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), + fname, "", LOCK_EX|LOCK_NB)) { if (tTd(56, 2)) - dprintf("mci_lock_host: couldn't get lock on %s\n", + sm_dprintf("mci_lock_host: couldn't get lock on %s\n", fname); - (void) fclose(mci->mci_statfile); + (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); mci->mci_statfile = NULL; retVal = EX_TEMPFAIL; goto cleanup; } if (tTd(56, 12) && mci->mci_statfile != NULL) - dprintf("mci_lock_host: Sanity check -- lock is good\n"); + sm_dprintf("mci_lock_host: Sanity check -- lock is good\n"); cleanup: errno = save_errno; @@ -663,7 +726,7 @@ mci_unlock_host(mci) if (mci == NULL) { if (tTd(56, 1)) - dprintf("mci_unlock_host: NULL mci\n"); + sm_dprintf("mci_unlock_host: NULL mci\n"); return; } @@ -673,19 +736,18 @@ mci_unlock_host(mci) if (!SingleThreadDelivery && mci_lock_host_statfile(mci) == EX_TEMPFAIL) { if (tTd(56, 1)) - dprintf("mci_unlock_host: stat file already locked\n"); + sm_dprintf("mci_unlock_host: stat file already locked\n"); } else { if (tTd(56, 2)) - dprintf("mci_unlock_host: store prior to unlock\n"); - + sm_dprintf("mci_unlock_host: store prior to unlock\n"); mci_store_persistent(mci); } if (mci->mci_statfile != NULL) { - (void) fclose(mci->mci_statfile); + (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); mci->mci_statfile = NULL; } @@ -698,12 +760,11 @@ mci_unlock_host(mci) ** in common for all running sendmails. ** ** Parameters: -** mci -- the host/connection to load persistent info -** for. +** mci -- the host/connection to load persistent info for. ** ** Returns: -** TRUE -- lock was successful -** FALSE -- lock failed +** true -- lock was successful +** false -- lock failed */ static bool @@ -711,33 +772,34 @@ mci_load_persistent(mci) MCI *mci; { int save_errno = errno; - bool locked = TRUE; - FILE *fp; + bool locked = true; + SM_FILE_T *fp; char fname[MAXPATHLEN + 1]; if (mci == NULL) { if (tTd(56, 1)) - dprintf("mci_load_persistent: NULL mci\n"); - return TRUE; + sm_dprintf("mci_load_persistent: NULL mci\n"); + return true; } if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL) - return TRUE; + return true; /* Already have the persistent information in memory */ if (SingleThreadDelivery && mci->mci_statfile != NULL) - return TRUE; + return true; if (tTd(56, 1)) - dprintf("mci_load_persistent: Attempting to load persistent information for %s\n", - mci->mci_host); + sm_dprintf("mci_load_persistent: Attempting to load persistent information for %s\n", + mci->mci_host); - if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, FALSE) < 0) + if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, + false) < 0) { /* Not much we can do if the file isn't there... */ if (tTd(56, 1)) - dprintf("mci_load_persistent: Couldn't generate host path\n"); + sm_dprintf("mci_load_persistent: Couldn't generate host path\n"); goto cleanup; } @@ -747,20 +809,22 @@ mci_load_persistent(mci) { /* I can't think of any reason this should ever happen */ if (tTd(56, 1)) - dprintf("mci_load_persistent: open(%s): %s\n", - fname, errstring(errno)); + sm_dprintf("mci_load_persistent: open(%s): %s\n", + fname, sm_errstring(errno)); goto cleanup; } FileName = fname; - locked = lockfile(fileno(fp), fname, "", LOCK_SH|LOCK_NB); + locked = lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, "", + LOCK_SH|LOCK_NB); if (locked) { (void) mci_read_persistent(fp, mci); - (void) lockfile(fileno(fp), fname, "", LOCK_UN); + (void) lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, + "", LOCK_UN); } FileName = NULL; - (void) fclose(fp); + (void) sm_io_close(fp, SM_TIME_DEFAULT); cleanup: errno = save_errno; @@ -787,7 +851,7 @@ cleanup: static int mci_read_persistent(fp, mci) - FILE *fp; + SM_FILE_T *fp; register MCI *mci; { int ver; @@ -801,19 +865,18 @@ mci_read_persistent(fp, mci) syserr("mci_read_persistent: NULL mci"); if (tTd(56, 93)) { - dprintf("mci_read_persistent: fp=%lx, mci=", (u_long) fp); - mci_dump(mci, FALSE); + sm_dprintf("mci_read_persistent: fp=%lx, mci=", + (unsigned long) fp); + mci_dump(mci, false); } - mci->mci_status = NULL; - if (mci->mci_rstatus != NULL) - sm_free(mci->mci_rstatus); - mci->mci_rstatus = NULL; + SM_FREE_CLR(mci->mci_status); + SM_FREE_CLR(mci->mci_rstatus); - rewind(fp); + sm_io_rewind(fp, SM_TIME_DEFAULT); ver = -1; LineNumber = 0; - while (fgets(buf, sizeof buf, fp) != NULL) + while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) { LineNumber++; p = strchr(buf, '\n'); @@ -891,7 +954,7 @@ mci_store_persistent(mci) if (mci == NULL) { if (tTd(56, 1)) - dprintf("mci_store_persistent: NULL mci\n"); + sm_dprintf("mci_store_persistent: NULL mci\n"); return; } @@ -899,35 +962,42 @@ mci_store_persistent(mci) return; if (tTd(56, 1)) - dprintf("mci_store_persistent: Storing information for %s\n", - mci->mci_host); + sm_dprintf("mci_store_persistent: Storing information for %s\n", + mci->mci_host); if (mci->mci_statfile == NULL) { if (tTd(56, 1)) - dprintf("mci_store_persistent: no statfile\n"); + sm_dprintf("mci_store_persistent: no statfile\n"); return; } - rewind(mci->mci_statfile); + sm_io_rewind(mci->mci_statfile, SM_TIME_DEFAULT); #if !NOFTRUNCATE - (void) ftruncate(fileno(mci->mci_statfile), (off_t) 0); + (void) ftruncate(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), + (off_t) 0); #endif /* !NOFTRUNCATE */ - fprintf(mci->mci_statfile, "V0\n"); - fprintf(mci->mci_statfile, "E%d\n", mci->mci_errno); - fprintf(mci->mci_statfile, "H%d\n", mci->mci_herrno); - fprintf(mci->mci_statfile, "S%d\n", mci->mci_exitstat); + (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "V0\n"); + (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "E%d\n", + mci->mci_errno); + (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "H%d\n", + mci->mci_herrno); + (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "S%d\n", + mci->mci_exitstat); if (mci->mci_status != NULL) - fprintf(mci->mci_statfile, "D%.80s\n", - denlstring(mci->mci_status, TRUE, FALSE)); + (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, + "D%.80s\n", + denlstring(mci->mci_status, true, false)); if (mci->mci_rstatus != NULL) - fprintf(mci->mci_statfile, "R%.80s\n", - denlstring(mci->mci_rstatus, TRUE, FALSE)); - fprintf(mci->mci_statfile, "U%ld\n", (long)(mci->mci_lastuse)); - fprintf(mci->mci_statfile, ".\n"); + (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, + "R%.80s\n", + denlstring(mci->mci_rstatus, true, false)); + (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "U%ld\n", + (long)(mci->mci_lastuse)); + (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, ".\n"); - (void) fflush(mci->mci_statfile); + (void) sm_io_flush(mci->mci_statfile, SM_TIME_DEFAULT); errno = save_errno; return; @@ -972,39 +1042,39 @@ mci_traverse_persistent(action, pathname) return -1; if (tTd(56, 1)) - dprintf("mci_traverse: pathname is %s\n", pathname); + sm_dprintf("mci_traverse: pathname is %s\n", pathname); ret = stat(pathname, &statbuf); if (ret < 0) { if (tTd(56, 2)) - dprintf("mci_traverse: Failed to stat %s: %s\n", - pathname, errstring(errno)); + sm_dprintf("mci_traverse: Failed to stat %s: %s\n", + pathname, sm_errstring(errno)); return ret; } if (S_ISDIR(statbuf.st_mode)) { - struct dirent *e; + bool leftone, removedone; + size_t len; char *newptr; + struct dirent *e; char newpath[MAXPATHLEN + 1]; - bool leftone, removedone; if ((d = opendir(pathname)) == NULL) { if (tTd(56, 2)) - dprintf("mci_traverse: opendir %s: %s\n", - pathname, errstring(errno)); + sm_dprintf("mci_traverse: opendir %s: %s\n", + pathname, sm_errstring(errno)); return -1; } - - if (strlen(pathname) >= sizeof newpath - MAXNAMLEN - 3) + len = sizeof(newpath) - MAXNAMLEN - 3; + if (sm_strlcpy(newpath, pathname, len) >= len) { if (tTd(56, 2)) - dprintf("mci_traverse: path \"%s\" too long", + sm_dprintf("mci_traverse: path \"%s\" too long", pathname); return -1; } - (void) strlcpy(newpath, pathname, sizeof newpath); newptr = newpath + strlen(newpath); *newptr++ = '/'; @@ -1014,15 +1084,16 @@ mci_traverse_persistent(action, pathname) ** during these loops, but it's better than doing ** a rewinddir() inside the inner loop */ + do { - leftone = removedone = FALSE; + leftone = removedone = false; while ((e = readdir(d)) != NULL) { if (e->d_name[0] == '.') continue; - (void) strlcpy(newptr, e->d_name, + (void) sm_strlcpy(newptr, e->d_name, sizeof newpath - (newptr - newpath)); @@ -1032,22 +1103,24 @@ mci_traverse_persistent(action, pathname) if (ret < 0) break; if (ret == 1) - leftone = TRUE; + leftone = true; if (!removedone && ret == 0 && action == mci_purge_persistent) - removedone = TRUE; + removedone = true; } if (ret < 0) break; + /* ** The following appears to be ** necessary during purges, since ** we modify the directory structure */ + if (removedone) rewinddir(d); if (tTd(56, 40)) - dprintf("mci_traverse: path %s: ret %d removed %d left %d\n", + sm_dprintf("mci_traverse: path %s: ret %d removed %d left %d\n", pathname, ret, removedone, leftone); } while (removedone); @@ -1095,6 +1168,7 @@ mci_traverse_persistent(action, pathname) ** Do something with the file containing the persistent ** information. */ + ret = (*action)(pathname, host); } @@ -1118,8 +1192,8 @@ mci_print_persistent(pathname, hostname) char *pathname; char *hostname; { - static int initflag = FALSE; - FILE *fp; + static bool initflag = false; + SM_FILE_T *fp; int width = Verbose ? 78 : 25; bool locked; MCI mcib; @@ -1133,18 +1207,19 @@ mci_print_persistent(pathname, hostname) if (!initflag) { - initflag = TRUE; - printf(" -------------- Hostname --------------- How long ago ---------Results---------\n"); + initflag = true; + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + " -------------- Hostname --------------- How long ago ---------Results---------\n"); } - fp = safefopen(pathname, O_RDWR, FileMode, + fp = safefopen(pathname, O_RDONLY, FileMode, SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); if (fp == NULL) { if (tTd(56, 1)) - dprintf("mci_print_persistent: cannot open %s: %s\n", - pathname, errstring(errno)); + sm_dprintf("mci_print_persistent: cannot open %s: %s\n", + pathname, sm_errstring(errno)); return 0; } @@ -1153,43 +1228,49 @@ mci_print_persistent(pathname, hostname) if (mci_read_persistent(fp, &mcib) < 0) { syserr("%s: could not read status file", pathname); - (void) fclose(fp); + (void) sm_io_close(fp, SM_TIME_DEFAULT); FileName = NULL; return 0; } - locked = !lockfile(fileno(fp), pathname, "", LOCK_EX|LOCK_NB); - (void) fclose(fp); + locked = !lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), pathname, + "", LOCK_EX|LOCK_NB); + (void) sm_io_close(fp, SM_TIME_DEFAULT); FileName = NULL; - printf("%c%-39s %12s ", - locked ? '*' : ' ', hostname, - pintvl(curtime() - mcib.mci_lastuse, TRUE)); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%c%-39s %12s ", + locked ? '*' : ' ', hostname, + pintvl(curtime() - mcib.mci_lastuse, true)); if (mcib.mci_rstatus != NULL) - printf("%.*s\n", width, mcib.mci_rstatus); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", width, + mcib.mci_rstatus); else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0) - printf("Deferred: %.*s\n", width - 10, errstring(mcib.mci_errno)); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Deferred: %.*s\n", width - 10, + sm_errstring(mcib.mci_errno)); else if (mcib.mci_exitstat != 0) { - int i = mcib.mci_exitstat - EX__BASE; - extern int N_SysEx; - extern char *SysExMsg[]; + char *exmsg = sm_sysexmsg(mcib.mci_exitstat); - if (i < 0 || i >= N_SysEx) + if (exmsg == NULL) { char buf[80]; - snprintf(buf, sizeof buf, "Unknown mailer error %d", + (void) sm_snprintf(buf, sizeof buf, + "Unknown mailer error %d", mcib.mci_exitstat); - printf("%.*s\n", width, buf); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", + width, buf); } else - printf("%.*s\n", width, &(SysExMsg[i])[5]); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", + width, &exmsg[5]); } else if (mcib.mci_errno == 0) - printf("OK\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK\n"); else - printf("OK: %.*s\n", width - 4, errstring(mcib.mci_errno)); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK: %.*s\n", + width - 4, sm_errstring(mcib.mci_errno)); return 0; } @@ -1217,14 +1298,14 @@ mci_purge_persistent(pathname, hostname) int ret; if (tTd(56, 1)) - dprintf("mci_purge_persistent: purging %s\n", pathname); + sm_dprintf("mci_purge_persistent: purging %s\n", pathname); ret = stat(pathname, &statbuf); if (ret < 0) { if (tTd(56, 2)) - dprintf("mci_purge_persistent: Failed to stat %s: %s\n", - pathname, errstring(errno)); + sm_dprintf("mci_purge_persistent: Failed to stat %s: %s\n", + pathname, sm_errstring(errno)); return ret; } if (curtime() - statbuf.st_mtime < MciInfoTimeout) @@ -1235,8 +1316,8 @@ mci_purge_persistent(pathname, hostname) if (unlink(pathname) < 0) { if (tTd(56, 2)) - dprintf("mci_purge_persistent: failed to unlink %s: %s\n", - pathname, errstring(errno)); + sm_dprintf("mci_purge_persistent: failed to unlink %s: %s\n", + pathname, sm_errstring(errno)); } } else @@ -1246,13 +1327,13 @@ mci_purge_persistent(pathname, hostname) return 1; if (tTd(56, 1)) - dprintf("mci_purge_persistent: dpurge %s\n", pathname); + sm_dprintf("mci_purge_persistent: dpurge %s\n", pathname); if (rmdir(pathname) < 0) { if (tTd(56, 2)) - dprintf("mci_purge_persistent: rmdir %s: %s\n", - pathname, errstring(errno)); + sm_dprintf("mci_purge_persistent: rmdir %s: %s\n", + pathname, sm_errstring(errno)); } } @@ -1309,7 +1390,7 @@ mci_generate_persistent_path(host, path, pathlen, createflag) } if (tTd(56, 80)) - dprintf("mci_generate_persistent_path(%s): ", host); + sm_dprintf("mci_generate_persistent_path(%s): ", host); if (*host == '\0' || *host == '.') return -1; @@ -1318,9 +1399,9 @@ mci_generate_persistent_path(host, path, pathlen, createflag) if (strlen(host) > sizeof t_host - 1) return -1; if (host[0] == '[') - (void) strlcpy(t_host, host + 1, sizeof t_host); + (void) sm_strlcpy(t_host, host + 1, sizeof t_host); else - (void) strlcpy(t_host, host, sizeof t_host); + (void) sm_strlcpy(t_host, host, sizeof t_host); /* ** Delete any trailing dots from the hostname. @@ -1332,18 +1413,21 @@ mci_generate_persistent_path(host, path, pathlen, createflag) (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']'))) *--elem = '\0'; -#if NETINET || NETINET6 /* check for bogus bracketed address */ - if (host[0] == '[' + if (host[0] == '[') + { + bool good = false; # if NETINET6 - && inet_pton(AF_INET6, t_host, &in6_addr) != 1 + if (anynet_pton(AF_INET6, t_host, &in6_addr) == 1) + good = true; # endif /* NETINET6 */ # if NETINET - && inet_addr(t_host) == INADDR_NONE + if (inet_addr(t_host) != INADDR_NONE) + good = true; # endif /* NETINET */ - ) - return -1; -#endif /* NETINET || NETINET6 */ + if (!good) + return -1; + } /* check for what will be the final length of the path */ len = strlen(HostStatDir) + 2; @@ -1357,10 +1441,8 @@ mci_generate_persistent_path(host, path, pathlen, createflag) } if (len > pathlen || len < 1) return -1; - - (void) strlcpy(path, HostStatDir, pathlen); + (void) sm_strlcpy(path, HostStatDir, pathlen); p = path + strlen(path); - while (elem > t_host) { if (!path_is_dir(path, createflag)) @@ -1385,14 +1467,12 @@ mci_generate_persistent_path(host, path, pathlen, createflag) *p++ = '.'; *p = '\0'; } - if (tTd(56, 80)) { if (ret < 0) - dprintf("FAILURE %d\n", ret); + sm_dprintf("FAILURE %d\n", ret); else - dprintf("SUCCESS %s\n", path); + sm_dprintf("SUCCESS %s\n", path); } - return ret; } diff --git a/gnu/usr.sbin/sendmail/sendmail/milter.c b/gnu/usr.sbin/sendmail/sendmail/milter.c index 2a8e95e151b..4ece61ff1e7 100644 --- a/gnu/usr.sbin/sendmail/sendmail/milter.c +++ b/gnu/usr.sbin/sendmail/sendmail/milter.c @@ -8,13 +8,14 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: milter.c,v 8.50.4.53 2001/08/15 02:01:03 ca Exp $"; -#endif /* ! lint */ +#include <sendmail.h> -#if _FFR_MILTER +SM_RCSID("@(#)$Sendmail: milter.c,v 8.173 2001/09/04 22:43:04 ca Exp $") + +#if MILTER +# include <libmilter/mfapi.h> +# include <libmilter/mfdef.h> -# include <sendmail.h> # include <errno.h> # include <sys/time.h> @@ -22,12 +23,10 @@ static char id[] = "@(#)$Sendmail: milter.c,v 8.50.4.53 2001/08/15 02:01:03 ca E # include <arpa/inet.h> # endif /* NETINET || NETINET6 */ -# define SM_FD_SET FD_SET -# define SM_FD_ISSET FD_ISSET -# define SM_FD_SETSIZE FD_SETSIZE +# include <sm/fdset.h> static void milter_connect_timeout __P((void)); -static void milter_error __P((struct milter *)); +static void milter_error __P((struct milter *, ENVELOPE *)); static int milter_open __P((struct milter *, bool, ENVELOPE *)); static void milter_parse_timeouts __P((char *, struct milter *)); @@ -63,7 +62,7 @@ static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1]; !isascii(response[2]) || !isdigit(response[2])) \ { \ if (response != NULL) \ - sm_free(response); \ + sm_free(response); /* XXX */ \ response = newstr(default); \ } \ else \ @@ -75,7 +74,7 @@ static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1]; { \ if (*ptr == '%' && *++ptr != '%') \ { \ - sm_free(response); \ + sm_free(response); /* XXX */ \ response = newstr(default); \ break; \ } \ @@ -89,16 +88,16 @@ static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1]; \ if (tTd(64, 5)) \ { \ - dprintf(msg, dfname, errstring(save_errno)); \ - dprintf("\n"); \ + sm_dprintf(msg, dfname, sm_errstring(save_errno)); \ + sm_dprintf("\n"); \ } \ - if (LogLevel > 0) \ - sm_syslog(LOG_ERR, e->e_id, msg, dfname, errstring(save_errno)); \ - if (SuperSafe) \ + if (MilterLogLevel > 0) \ + sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \ + if (SuperSafe == SAFE_REALLY) \ { \ if (e->e_dfp != NULL) \ { \ - (void) fclose(e->e_dfp); \ + (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \ e->e_dfp = NULL; \ } \ e->e_flags &= ~EF_HAS_DF; \ @@ -113,73 +112,85 @@ static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1]; ** routine -- routine name for debug/logging ** secs -- number of seconds in timeout ** write -- waiting to read or write? +** started -- whether this is part of a previous sequence ** ** Assumes 'm' is a milter structure for the current socket. */ -# define MILTER_TIMEOUT(routine, secs, write) \ +# define MILTER_TIMEOUT(routine, secs, write, started) \ { \ int ret; \ int save_errno; \ fd_set fds; \ struct timeval tv; \ \ - if (SM_FD_SETSIZE != 0 && m->mf_sock >= SM_FD_SETSIZE) \ + if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \ { \ if (tTd(64, 5)) \ - dprintf("%s(%s): socket %d is larger than FD_SETSIZE %d\n", \ - routine, m->mf_name, m->mf_sock, SM_FD_SETSIZE); \ - if (LogLevel > 0) \ + sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \ + (routine), m->mf_name, m->mf_sock, \ + SM_FD_SETSIZE); \ + if (MilterLogLevel > 0) \ sm_syslog(LOG_ERR, e->e_id, \ - "%s(%s): socket %d is larger than FD_SETSIZE %d\n", \ - routine, m->mf_name, m->mf_sock, SM_FD_SETSIZE); \ - milter_error(m); \ + "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \ + m->mf_name, (routine), m->mf_sock, \ + SM_FD_SETSIZE); \ + milter_error(m, e); \ return NULL; \ } \ \ FD_ZERO(&fds); \ SM_FD_SET(m->mf_sock, &fds); \ - tv.tv_sec = secs; \ + tv.tv_sec = (secs); \ tv.tv_usec = 0; \ ret = select(m->mf_sock + 1, \ - write ? NULL : &fds, \ - write ? &fds : NULL, \ + (write) ? NULL : &fds, \ + (write) ? &fds : NULL, \ NULL, &tv); \ \ switch (ret) \ { \ case 0: \ if (tTd(64, 5)) \ - dprintf("%s(%s): timeout\n", routine, m->mf_name); \ - if (LogLevel > 0) \ - sm_syslog(LOG_ERR, e->e_id, "%s(%s): timeout\n", \ - routine, m->mf_name); \ - milter_error(m); \ + sm_dprintf("milter_%s(%s): timeout\n", (routine), \ + m->mf_name); \ + if (MilterLogLevel > 0) \ + sm_syslog(LOG_ERR, e->e_id, \ + "Milter (%s): %s %s %s %s", \ + m->mf_name, "timeout", \ + started ? "during" : "before", \ + "data", (routine)); \ + milter_error(m, e); \ return NULL; \ \ case -1: \ save_errno = errno; \ if (tTd(64, 5)) \ - dprintf("%s(%s): select: %s\n", \ - routine, m->mf_name, errstring(save_errno)); \ - if (LogLevel > 0) \ + sm_dprintf("milter_%s(%s): select: %s\n", (routine), \ + m->mf_name, sm_errstring(save_errno)); \ + if (MilterLogLevel > 0) \ + { \ sm_syslog(LOG_ERR, e->e_id, \ - "%s(%s): select: %s\n", \ - routine, m->mf_name, errstring(save_errno)); \ - milter_error(m); \ + "Milter (%s): select(%s): %s", \ + m->mf_name, (routine), \ + sm_errstring(save_errno)); \ + } \ + milter_error(m, e); \ return NULL; \ \ default: \ if (SM_FD_ISSET(m->mf_sock, &fds)) \ break; \ if (tTd(64, 5)) \ - dprintf("%s(%s): socket not ready\n", \ - routine, m->mf_name); \ - if (LogLevel > 0) \ + sm_dprintf("milter_%s(%s): socket not ready\n", \ + (routine), m->mf_name); \ + if (MilterLogLevel > 0) \ + { \ sm_syslog(LOG_ERR, e->e_id, \ - "%s(%s): socket not ready\n", \ - m->mf_name, routine); \ - milter_error(m); \ + "Milter (%s): socket(%s) not ready", \ + m->mf_name, (routine)); \ + } \ + milter_error(m, e); \ return NULL; \ } \ } @@ -212,6 +223,7 @@ milter_sysread(m, buf, sz, to, e) { time_t readstart = 0; ssize_t len, curl; + bool started = false; curl = 0; @@ -228,18 +240,22 @@ milter_sysread(m, buf, sz, to, e) if (now - readstart >= to) { if (tTd(64, 5)) - dprintf("milter_read(%s): timeout before data read\n", - m->mf_name); - if (LogLevel > 0) + sm_dprintf("milter_read (%s): %s %s %s", + m->mf_name, "timeout", + started ? "during" : "before", + "data read"); + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "milter_read(%s): timeout before data read\n", - m->mf_name); - milter_error(m); + "Milter (%s): %s %s %s", + m->mf_name, "timeout", + started ? "during" : "before", + "data read"); + milter_error(m, e); return NULL; } to -= now - readstart; readstart = now; - MILTER_TIMEOUT("milter_read", to, FALSE); + MILTER_TIMEOUT("read", to, false, started); } len = read(m->mf_sock, buf + curl, sz - curl); @@ -249,18 +265,19 @@ milter_sysread(m, buf, sz, to, e) int save_errno = errno; if (tTd(64, 5)) - dprintf("milter_read(%s): read returned %ld: %s\n", + sm_dprintf("milter_read(%s): read returned %ld: %s\n", m->mf_name, (long) len, - errstring(save_errno)); - if (LogLevel > 0) + sm_errstring(save_errno)); + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "milter_read(%s): read returned %ld: %s", + "Milter (%s): read returned %ld: %s", m->mf_name, (long) len, - errstring(save_errno)); - milter_error(m); + sm_errstring(save_errno)); + milter_error(m, e); return NULL; } + started = true; curl += len; if (len == 0 || curl >= sz) break; @@ -270,13 +287,13 @@ milter_sysread(m, buf, sz, to, e) if (curl != sz) { if (tTd(64, 5)) - dprintf("milter_read(%s): read returned %ld, expecting %ld\n", + sm_dprintf("milter_read(%s): cmd read returned %ld, expecting %ld\n", m->mf_name, (long) curl, (long) sz); - if (LogLevel > 0) + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "milter_read(%s): read returned %ld, expecting %ld", + "milter_read(%s): cmd read returned %ld, expecting %ld", m->mf_name, (long) curl, (long) sz); - milter_error(m); + milter_error(m, e); return NULL; } return buf; @@ -314,13 +331,13 @@ milter_read(m, cmd, rlen, to, e) if (now - readstart >= to) { if (tTd(64, 5)) - dprintf("milter_read(%s): timeout before data read\n", + sm_dprintf("milter_read(%s): timeout before data read\n", m->mf_name); - if (LogLevel > 0) + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "milter_read(%s): timeout before data read\n", + "Milter read(%s): timeout before data read", m->mf_name); - milter_error(m); + milter_error(m, e); return NULL; } to -= now - readstart; @@ -332,35 +349,35 @@ milter_read(m, cmd, rlen, to, e) expl = ntohl(i) - 1; if (tTd(64, 25)) - dprintf("milter_read(%s): expecting %ld bytes\n", + sm_dprintf("milter_read(%s): expecting %ld bytes\n", m->mf_name, (long) expl); if (expl < 0) { if (tTd(64, 5)) - dprintf("milter_read(%s): read size %ld out of range\n", + sm_dprintf("milter_read(%s): read size %ld out of range\n", m->mf_name, (long) expl); - if (LogLevel > 0) + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, "milter_read(%s): read size %ld out of range", m->mf_name, (long) expl); - milter_error(m); + milter_error(m, e); return NULL; } if (expl == 0) return NULL; - buf = (char *)xalloc(expl); + buf = (char *) xalloc(expl); if (milter_sysread(m, buf, expl, to, e) == NULL) { - sm_free(buf); + sm_free(buf); /* XXX */ return NULL; } if (tTd(64, 50)) - dprintf("milter_read(%s): Returning %*s\n", + sm_dprintf("milter_read(%s): Returning %*s\n", m->mf_name, (int) expl, buf); *rlen = expl; return buf; @@ -395,23 +412,24 @@ milter_write(m, cmd, buf, len, to, e) ssize_t sl, i; mi_int32 nl; char data[MILTER_LEN_BYTES + 1]; + bool started = false; if (len < 0 || len > MILTER_CHUNK_SIZE) { if (tTd(64, 5)) - dprintf("milter_write(%s): length %ld out of range\n", + sm_dprintf("milter_write(%s): length %ld out of range\n", m->mf_name, (long) len); - if (LogLevel > 0) + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, "milter_write(%s): length %ld out of range", m->mf_name, (long) len); - milter_error(m); + milter_error(m, e); return NULL; } if (tTd(64, 20)) - dprintf("milter_write(%s): cmd %c, len %ld\n", - m->mf_name, cmd, (long) len); + sm_dprintf("milter_write(%s): cmd %c, len %ld\n", + m->mf_name, cmd, (long) len); nl = htonl(len + 1); /* add 1 for the cmd char */ (void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES); @@ -421,7 +439,7 @@ milter_write(m, cmd, buf, len, to, e) if (to > 0) { writestart = curtime(); - MILTER_TIMEOUT("milter_write", to, TRUE); + MILTER_TIMEOUT("write", to, true, started); } /* use writev() instead to send the whole stuff at once? */ @@ -431,15 +449,15 @@ milter_write(m, cmd, buf, len, to, e) int save_errno = errno; if (tTd(64, 5)) - dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n", - m->mf_name, cmd, (long) i, (long) sl, - errstring(save_errno)); - if (LogLevel > 0) + sm_dprintf("milter_write (%s): write(%c) returned %ld, expected %ld: %s\n", + m->mf_name, cmd, (long) i, (long) sl, + sm_errstring(save_errno)); + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "milter_write(%s): write(%c) returned %ld, expected %ld: %s", + "Milter (%s): write(%c) returned %ld, expected %ld: %s", m->mf_name, cmd, (long) i, (long) sl, - errstring(save_errno)); - milter_error(m); + sm_errstring(save_errno)); + milter_error(m, e); return buf; } @@ -447,8 +465,9 @@ milter_write(m, cmd, buf, len, to, e) return buf; if (tTd(64, 50)) - dprintf("milter_write(%s): Sending %*s\n", - m->mf_name, (int) len, buf); + sm_dprintf("milter_write(%s): Sending %*s\n", + m->mf_name, (int) len, buf); + started = true; if (to > 0) { @@ -458,19 +477,19 @@ milter_write(m, cmd, buf, len, to, e) if (now - writestart >= to) { if (tTd(64, 5)) - dprintf("milter_write(%s): timeout before data send\n", - m->mf_name); - if (LogLevel > 0) + sm_dprintf("milter_write(%s): timeout before data write\n", + m->mf_name); + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "milter_write(%s): timeout before data send\n", + "Milter (%s): timeout before data write", m->mf_name); - milter_error(m); + milter_error(m, e); return NULL; } else { to -= now - writestart; - MILTER_TIMEOUT("milter_write", to, TRUE); + MILTER_TIMEOUT("write", to, true, started); } } @@ -480,15 +499,15 @@ milter_write(m, cmd, buf, len, to, e) int save_errno = errno; if (tTd(64, 5)) - dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n", - m->mf_name, cmd, (long) i, (long) sl, - errstring(save_errno)); - if (LogLevel > 0) + sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n", + m->mf_name, cmd, (long) i, (long) sl, + sm_errstring(save_errno)); + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "milter_write(%s): write(%c) returned %ld, expected %ld: %s", + "Milter (%s): write(%c) returned %ld, expected %ld: %s", m->mf_name, cmd, (long) i, (long) len, - errstring(save_errno)); - milter_error(m); + sm_errstring(save_errno)); + milter_error(m, e); return NULL; } return buf; @@ -533,16 +552,16 @@ milter_open(m, parseonly, e) if (m->mf_conn == NULL || m->mf_conn[0] == '\0') { if (tTd(64, 5)) - dprintf("X%s: empty or missing socket information\n", - m->mf_name); + sm_dprintf("X%s: empty or missing socket information\n", + m->mf_name); if (parseonly) syserr("X%s: empty or missing socket information", m->mf_name); - else if (LogLevel > 10) + else if (MilterLogLevel > 10) sm_syslog(LOG_ERR, e->e_id, - "X%s: empty or missing socket information", + "Milter (%s): empty or missing socket information", m->mf_name); - milter_error(m); + milter_error(m, e); return -1; } @@ -569,25 +588,25 @@ milter_open(m, parseonly, e) # else /* NETINET6 */ /* no protocols available */ sm_syslog(LOG_ERR, e->e_id, - "X%s: no valid socket protocols available", + "Milter (%s): no valid socket protocols available", m->mf_name); - milter_error(m); + milter_error(m, e); return -1; # endif /* NETINET6 */ # endif /* NETINET */ # endif /* NETUNIX */ } # if NETUNIX - else if (strcasecmp(p, "unix") == 0 || - strcasecmp(p, "local") == 0) + else if (sm_strcasecmp(p, "unix") == 0 || + sm_strcasecmp(p, "local") == 0) addr.sa.sa_family = AF_UNIX; # endif /* NETUNIX */ # if NETINET - else if (strcasecmp(p, "inet") == 0) + else if (sm_strcasecmp(p, "inet") == 0) addr.sa.sa_family = AF_INET; # endif /* NETINET */ # if NETINET6 - else if (strcasecmp(p, "inet6") == 0) + else if (sm_strcasecmp(p, "inet6") == 0) addr.sa.sa_family = AF_INET6; # endif /* NETINET6 */ else @@ -598,16 +617,16 @@ milter_open(m, parseonly, e) errno = EINVAL; # endif /* EPROTONOSUPPORT */ if (tTd(64, 5)) - dprintf("X%s: unknown socket type %s\n", + sm_dprintf("X%s: unknown socket type %s\n", m->mf_name, p); if (parseonly) syserr("X%s: unknown socket type %s", m->mf_name, p); - else if (LogLevel > 10) + else if (MilterLogLevel > 10) sm_syslog(LOG_ERR, e->e_id, - "X%s: unknown socket type %s", + "Milter (%s): unknown socket type %s", m->mf_name, p); - milter_error(m); + milter_error(m, e); return -1; } *colon++ = ':'; @@ -628,17 +647,17 @@ milter_open(m, parseonly, e) if (strlen(colon) >= sizeof addr.sunix.sun_path) { if (tTd(64, 5)) - dprintf("X%s: local socket name %s too long\n", + sm_dprintf("X%s: local socket name %s too long\n", m->mf_name, colon); errno = EINVAL; if (parseonly) syserr("X%s: local socket name %s too long", m->mf_name, colon); - else if (LogLevel > 10) + else if (MilterLogLevel > 10) sm_syslog(LOG_ERR, e->e_id, - "X%s: local socket name %s too long", + "Milter (%s): local socket name %s too long", m->mf_name, colon); - milter_error(m); + milter_error(m, e); return -1; } errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, @@ -649,16 +668,16 @@ milter_open(m, parseonly, e) { if (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON) - fprintf(stderr, - "WARNING: X%s: local socket name %s missing\n", - m->mf_name, colon); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "WARNING: X%s: local socket name %s missing\n", + m->mf_name, colon); } else if (errno != 0) { /* if not safe, don't create */ save_errno = errno; if (tTd(64, 5)) - dprintf("X%s: local socket name %s unsafe\n", + sm_dprintf("X%s: local socket name %s unsafe\n", m->mf_name, colon); errno = save_errno; if (parseonly) @@ -669,22 +688,22 @@ milter_open(m, parseonly, e) syserr("X%s: local socket name %s unsafe", m->mf_name, colon); } - else if (LogLevel > 10) + else if (MilterLogLevel > 10) sm_syslog(LOG_ERR, e->e_id, - "X%s: local socket name %s unsafe", + "Milter (%s): local socket name %s unsafe", m->mf_name, colon); - milter_error(m); + milter_error(m, e); return -1; } - (void) strlcpy(addr.sunix.sun_path, colon, + (void) sm_strlcpy(addr.sunix.sun_path, colon, sizeof addr.sunix.sun_path); addrlen = sizeof (struct sockaddr_un); } else # endif /* NETUNIX */ # if NETINET || NETINET6 - if (FALSE + if (false # if NETINET || addr.sa.sa_family == AF_INET # endif /* NETINET */ @@ -693,42 +712,42 @@ milter_open(m, parseonly, e) # endif /* NETINET6 */ ) { - u_short port; + unsigned short port; /* Parse port@host */ at = strchr(colon, '@'); if (at == NULL) { if (tTd(64, 5)) - dprintf("X%s: bad address %s (expected port@host)\n", + sm_dprintf("X%s: bad address %s (expected port@host)\n", m->mf_name, colon); if (parseonly) syserr("X%s: bad address %s (expected port@host)", m->mf_name, colon); - else if (LogLevel > 10) + else if (MilterLogLevel > 10) sm_syslog(LOG_ERR, e->e_id, - "X%s: bad address %s (expected port@host)", + "Milter (%s): bad address %s (expected port@host)", m->mf_name, colon); - milter_error(m); + milter_error(m, e); return -1; } *at = '\0'; if (isascii(*colon) && isdigit(*colon)) - port = htons((u_short) atoi(colon)); + port = htons((unsigned short) atoi(colon)); else { # ifdef NO_GETSERVBYNAME if (tTd(64, 5)) - dprintf("X%s: invalid port number %s\n", + sm_dprintf("X%s: invalid port number %s\n", m->mf_name, colon); if (parseonly) syserr("X%s: invalid port number %s", m->mf_name, colon); - else if (LogLevel > 10) + else if (MilterLogLevel > 10) sm_syslog(LOG_ERR, e->e_id, - "X%s: invalid port number %s", + "Milter (%s): invalid port number %s", m->mf_name, colon); - milter_error(m); + milter_error(m, e); return -1; # else /* NO_GETSERVBYNAME */ register struct servent *sp; @@ -738,17 +757,17 @@ milter_open(m, parseonly, e) { save_errno = errno; if (tTd(64, 5)) - dprintf("X%s: unknown port name %s\n", + sm_dprintf("X%s: unknown port name %s\n", m->mf_name, colon); errno = save_errno; if (parseonly) syserr("X%s: unknown port name %s", m->mf_name, colon); - else if (LogLevel > 10) + else if (MilterLogLevel > 10) sm_syslog(LOG_ERR, e->e_id, - "X%s: unknown port name %s", + "Milter (%s): unknown port name %s", m->mf_name, colon); - milter_error(m); + milter_error(m, e); return -1; } port = sp->s_port; @@ -762,7 +781,7 @@ milter_open(m, parseonly, e) end = strchr(at, ']'); if (end != NULL) { - bool found = FALSE; + bool found = false; # if NETINET unsigned long hid = INADDR_NONE; # endif /* NETINET */ @@ -777,50 +796,50 @@ milter_open(m, parseonly, e) { addr.sin.sin_addr.s_addr = hid; addr.sin.sin_port = port; - found = TRUE; + found = true; } # endif /* NETINET */ # if NETINET6 (void) memset(&hid6, '\0', sizeof hid6); if (addr.sa.sa_family == AF_INET6 && - inet_pton(AF_INET6, &at[1], - &hid6.sin6_addr) == 1) + anynet_pton(AF_INET6, &at[1], + &hid6.sin6_addr) == 1) { addr.sin6.sin6_addr = hid6.sin6_addr; addr.sin6.sin6_port = port; - found = TRUE; + found = true; } # endif /* NETINET6 */ *end = ']'; if (!found) { if (tTd(64, 5)) - dprintf("X%s: Invalid numeric domain spec \"%s\"\n", + sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n", m->mf_name, at); if (parseonly) syserr("X%s: Invalid numeric domain spec \"%s\"", m->mf_name, at); - else if (LogLevel > 10) + else if (MilterLogLevel > 10) sm_syslog(LOG_ERR, e->e_id, - "X%s: Invalid numeric domain spec \"%s\"", + "Milter (%s): Invalid numeric domain spec \"%s\"", m->mf_name, at); - milter_error(m); + milter_error(m, e); return -1; } } else { if (tTd(64, 5)) - dprintf("X%s: Invalid numeric domain spec \"%s\"\n", + sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n", m->mf_name, at); if (parseonly) syserr("X%s: Invalid numeric domain spec \"%s\"", m->mf_name, at); - else if (LogLevel > 10) + else if (MilterLogLevel > 10) sm_syslog(LOG_ERR, e->e_id, - "X%s: Invalid numeric domain spec \"%s\"", + "Milter (%s): Invalid numeric domain spec \"%s\"", m->mf_name, at); - milter_error(m); + milter_error(m, e); return -1; } } @@ -831,17 +850,17 @@ milter_open(m, parseonly, e) { save_errno = errno; if (tTd(64, 5)) - dprintf("X%s: Unknown host name %s\n", - m->mf_name, at); + sm_dprintf("X%s: Unknown host name %s\n", + m->mf_name, at); errno = save_errno; if (parseonly) syserr("X%s: Unknown host name %s", m->mf_name, at); - else if (LogLevel > 10) + else if (MilterLogLevel > 10) sm_syslog(LOG_ERR, e->e_id, - "X%s: Unknown host name %s", + "Milter (%s): Unknown host name %s", m->mf_name, at); - milter_error(m); + milter_error(m, e); return -1; } addr.sa.sa_family = hp->h_addrtype; @@ -850,8 +869,7 @@ milter_open(m, parseonly, e) # if NETINET case AF_INET: memmove(&addr.sin.sin_addr, - hp->h_addr, - INADDRSZ); + hp->h_addr, INADDRSZ); addr.sin.sin_port = port; addrlen = sizeof (struct sockaddr_in); addrno = 1; @@ -861,8 +879,7 @@ milter_open(m, parseonly, e) # if NETINET6 case AF_INET6: memmove(&addr.sin6.sin6_addr, - hp->h_addr, - IN6ADDRSZ); + hp->h_addr, IN6ADDRSZ); addr.sin6.sin6_port = port; addrlen = sizeof (struct sockaddr_in6); addrno = 1; @@ -871,21 +888,21 @@ milter_open(m, parseonly, e) default: if (tTd(64, 5)) - dprintf("X%s: Unknown protocol for %s (%d)\n", - m->mf_name, at, - hp->h_addrtype); + sm_dprintf("X%s: Unknown protocol for %s (%d)\n", + m->mf_name, at, + hp->h_addrtype); if (parseonly) syserr("X%s: Unknown protocol for %s (%d)", m->mf_name, at, hp->h_addrtype); - else if (LogLevel > 10) + else if (MilterLogLevel > 10) sm_syslog(LOG_ERR, e->e_id, - "X%s: Unknown protocol for %s (%d)", + "Milter (%s): Unknown protocol for %s (%d)", m->mf_name, at, hp->h_addrtype); - milter_error(m); -# if _FFR_FREEHOSTENT && NETINET6 + milter_error(m, e); +# if NETINET6 freehostent(hp); -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +# endif /* NETINET6 */ return -1; } } @@ -894,13 +911,15 @@ milter_open(m, parseonly, e) # endif /* NETINET || NETINET6 */ { if (tTd(64, 5)) - dprintf("X%s: unknown socket protocol\n", m->mf_name); + sm_dprintf("X%s: unknown socket protocol\n", + m->mf_name); if (parseonly) syserr("X%s: unknown socket protocol", m->mf_name); - else if (LogLevel > 10) + else if (MilterLogLevel > 10) sm_syslog(LOG_ERR, e->e_id, - "X%s: unknown socket protocol", m->mf_name); - milter_error(m); + "Milter (%s): unknown socket protocol", + m->mf_name); + milter_error(m, e); return -1; } @@ -908,10 +927,10 @@ milter_open(m, parseonly, e) if (parseonly) { m->mf_state = SMFS_READY; -# if _FFR_FREEHOSTENT && NETINET6 +# if NETINET6 if (hp != NULL) freehostent(hp); -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +# endif /* NETINET6 */ return 0; } @@ -921,13 +940,13 @@ milter_open(m, parseonly, e) { /* shouldn't happen */ if (tTd(64, 1)) - dprintf("milter_open(%s): Trying to open filter in state %c\n", - m->mf_name, (char) m->mf_state); - milter_error(m); -# if _FFR_FREEHOSTENT && NETINET6 + sm_dprintf("Milter (%s): Trying to open filter in state %c\n", + m->mf_name, (char) m->mf_state); + milter_error(m, e); +# if NETINET6 if (hp != NULL) freehostent(hp); -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +# endif /* NETINET6 */ return -1; } @@ -939,33 +958,34 @@ milter_open(m, parseonly, e) { save_errno = errno; if (tTd(64, 5)) - dprintf("X%s: error creating socket: %s\n", - m->mf_name, errstring(save_errno)); - if (LogLevel > 0) + sm_dprintf("Milter (%s): error creating socket: %s\n", + m->mf_name, + sm_errstring(save_errno)); + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "X%s: error creating socket: %s", - m->mf_name, errstring(save_errno)); - milter_error(m); -# if _FFR_FREEHOSTENT && NETINET6 + "Milter (%s): error creating socket: %s", + m->mf_name, sm_errstring(save_errno)); + milter_error(m, e); +# if NETINET6 if (hp != NULL) freehostent(hp); -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +# endif /* NETINET6 */ return -1; } if (setjmp(MilterConnectTimeout) == 0) { - EVENT *ev = NULL; + SM_EVENT *ev = NULL; int i; if (m->mf_timeout[SMFTO_CONNECT] > 0) - ev = setevent(m->mf_timeout[SMFTO_CONNECT], - milter_connect_timeout, 0); + ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT], + milter_connect_timeout, 0); i = connect(sock, (struct sockaddr *) &addr, addrlen); save_errno = errno; if (ev != NULL) - clrevent(ev); + sm_clrevent(ev); errno = save_errno; if (i >= 0) break; @@ -976,12 +996,12 @@ milter_open(m, parseonly, e) p = CurHostName; CurHostName = at; if (tTd(64, 5)) - dprintf("milter_open(%s): %s failed: %s\n", - m->mf_name, at, errstring(save_errno)); - if (LogLevel >= 14) + sm_dprintf("milter_open (%s): open %s failed: %s\n", + m->mf_name, at, sm_errstring(save_errno)); + if (MilterLogLevel > 13) sm_syslog(LOG_INFO, e->e_id, - "milter_open(%s): %s failed: %s", - m->mf_name, at, errstring(save_errno)); + "Milter (%s): open %s failed: %s", + m->mf_name, at, sm_errstring(save_errno)); CurHostName = p; (void) close(sock); @@ -1008,18 +1028,18 @@ milter_open(m, parseonly, e) default: if (tTd(64, 5)) - dprintf("X%s: Unknown protocol for %s (%d)\n", - m->mf_name, at, - hp->h_addrtype); - if (LogLevel > 0) + sm_dprintf("X%s: Unknown protocol for %s (%d)\n", + m->mf_name, at, + hp->h_addrtype); + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "X%s: Unknown protocol for %s (%d)", + "Milter (%s): Unknown protocol for %s (%d)", m->mf_name, at, hp->h_addrtype); - milter_error(m); -# if _FFR_FREEHOSTENT && NETINET6 + milter_error(m, e); +# if NETINET6 freehostent(hp); -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +# endif /* NETINET6 */ return -1; } continue; @@ -1027,28 +1047,28 @@ milter_open(m, parseonly, e) p = CurHostName; CurHostName = at; if (tTd(64, 5)) - dprintf("X%s: error connecting to filter: %s\n", - m->mf_name, errstring(save_errno)); - if (LogLevel > 0) + sm_dprintf("X%s: error connecting to filter: %s\n", + m->mf_name, sm_errstring(save_errno)); + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "X%s: error connecting to filter: %s", - m->mf_name, errstring(save_errno)); + "Milter (%s): error connecting to filter: %s", + m->mf_name, sm_errstring(save_errno)); CurHostName = p; - milter_error(m); -# if _FFR_FREEHOSTENT && NETINET6 + milter_error(m, e); +# if NETINET6 if (hp != NULL) freehostent(hp); -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +# endif /* NETINET6 */ return -1; } m->mf_state = SMFS_OPEN; -# if _FFR_FREEHOSTENT && NETINET6 +# if NETINET6 if (hp != NULL) { freehostent(hp); hp = NULL; } -# endif /* _FFR_FREEHOSTENT && NETINET6 */ +# endif /* NETINET6 */ return sock; } @@ -1095,12 +1115,12 @@ milter_setup(line) syserr("name required for mail filter"); return; } - m = (struct milter *)xalloc(sizeof *m); + m = (struct milter *) xalloc(sizeof *m); memset((char *) m, '\0', sizeof *m); m->mf_name = newstr(line); m->mf_state = SMFS_READY; m->mf_sock = -1; - m->mf_timeout[SMFTO_CONNECT] = (time_t) 0; + m->mf_timeout[SMFTO_CONNECT] = (time_t) 300; m->mf_timeout[SMFTO_WRITE] = (time_t) 10; m->mf_timeout[SMFTO_READ] = (time_t) 10; m->mf_timeout[SMFTO_EOM] = (time_t) 300; @@ -1160,7 +1180,7 @@ milter_setup(line) } /* early check for errors */ - (void) milter_open(m, TRUE, CurEnv); + (void) milter_open(m, true, CurEnv); /* enter the filter into the symbol table */ s = stab(m->mf_name, ST_MILTER, ST_ENTER); @@ -1170,7 +1190,7 @@ milter_setup(line) s->s_milter = m; } /* -** MILTER_PARSE_LIST -- parse option list into an array +** MILTER_CONFIG -- parse option list into an array and check config ** ** Called when reading configuration file. ** @@ -1184,7 +1204,7 @@ milter_setup(line) */ void -milter_parse_list(spec, list, max) +milter_config(spec, list, max) char *spec; struct milter **list; int max; @@ -1212,7 +1232,11 @@ milter_parse_list(spec, list, max) list[0] = NULL; return; } +#if _FFR_MILTER_PERDAEMON + p = strpbrk(p, ";,"); +#else /* _FFR_MILTER_PERDAEMON */ p = strpbrk(p, ","); +#endif /* _FFR_MILTER_PERDAEMON */ if (p != NULL) *p++ = '\0'; @@ -1226,6 +1250,10 @@ milter_parse_list(spec, list, max) list[numitems++] = s->s_milter; } list[numitems] = NULL; + + /* if not set, set to LogLevel */ + if (MilterLogLevel == -1) + MilterLogLevel = LogLevel; } /* ** MILTER_PARSE_TIMEOUTS -- parse timeout list @@ -1280,39 +1308,39 @@ milter_parse_timeouts(spec, m) case 'C': m->mf_timeout[SMFTO_CONNECT] = convtime(p, 's'); if (tTd(64, 5)) - printf("X%s: %c=%ld\n", - m->mf_name, fcode, - (u_long) m->mf_timeout[SMFTO_CONNECT]); + sm_dprintf("X%s: %c=%lu\n", + m->mf_name, fcode, + (unsigned long) m->mf_timeout[SMFTO_CONNECT]); break; case 'S': m->mf_timeout[SMFTO_WRITE] = convtime(p, 's'); if (tTd(64, 5)) - printf("X%s: %c=%ld\n", - m->mf_name, fcode, - (u_long) m->mf_timeout[SMFTO_WRITE]); + sm_dprintf("X%s: %c=%lu\n", + m->mf_name, fcode, + (unsigned long) m->mf_timeout[SMFTO_WRITE]); break; case 'R': m->mf_timeout[SMFTO_READ] = convtime(p, 's'); if (tTd(64, 5)) - printf("X%s: %c=%ld\n", - m->mf_name, fcode, - (u_long) m->mf_timeout[SMFTO_READ]); + sm_dprintf("X%s: %c=%lu\n", + m->mf_name, fcode, + (unsigned long) m->mf_timeout[SMFTO_READ]); break; case 'E': m->mf_timeout[SMFTO_EOM] = convtime(p, 's'); if (tTd(64, 5)) - printf("X%s: %c=%ld\n", - m->mf_name, fcode, - (u_long) m->mf_timeout[SMFTO_EOM]); + sm_dprintf("X%s: %c=%lu\n", + m->mf_name, fcode, + (unsigned long) m->mf_timeout[SMFTO_EOM]); break; default: if (tTd(64, 5)) - printf("X%s: %c unknown\n", - m->mf_name, fcode); + sm_dprintf("X%s: %c unknown\n", + m->mf_name, fcode); syserr("X%s: unknown filter timeout %c", m->mf_name, fcode); break; @@ -1338,8 +1366,8 @@ static BITMAP256 StickyMilterOpt; static struct milteropt { - char *mo_name; /* long name of milter option */ - u_char mo_code; /* code for option */ + char *mo_name; /* long name of milter option */ + unsigned char mo_code; /* code for option */ } MilterOptTab[] = { # define MO_MACROS_CONNECT 0x01 @@ -1350,6 +1378,8 @@ static struct milteropt { "macros.envfrom", MO_MACROS_ENVFROM }, # define MO_MACROS_ENVRCPT 0x04 { "macros.envrcpt", MO_MACROS_ENVRCPT }, +# define MO_LOGLEVEL 0x05 + { "loglevel", MO_LOGLEVEL }, { NULL, 0 }, }; @@ -1365,16 +1395,19 @@ milter_set_option(name, val, sticky) char **macros = NULL; if (tTd(37, 2) || tTd(64, 5)) - dprintf("milter_set_option(%s = %s)", name, val); + sm_dprintf("milter_set_option(%s = %s)", name, val); for (mo = MilterOptTab; mo->mo_name != NULL; mo++) { - if (strcasecmp(mo->mo_name, name) == 0) + if (sm_strcasecmp(mo->mo_name, name) == 0) break; } if (mo->mo_name == NULL) + { syserr("milter_set_option: invalid Milter option %s", name); + return; + } /* ** See if this option is preset for us. @@ -1383,15 +1416,19 @@ milter_set_option(name, val, sticky) if (!sticky && bitnset(mo->mo_code, StickyMilterOpt)) { if (tTd(37, 2) || tTd(64,5)) - dprintf(" (ignored)\n"); + sm_dprintf(" (ignored)\n"); return; } if (tTd(37, 2) || tTd(64,5)) - dprintf("\n"); + sm_dprintf("\n"); switch (mo->mo_code) { + case MO_LOGLEVEL: + MilterLogLevel = atoi(val); + break; + case MO_MACROS_CONNECT: if (macros == NULL) macros = MilterConnectMacros; @@ -1448,7 +1485,6 @@ milter_set_option(name, val, sticky) syserr("milter_set_option: invalid Milter option %s", name); break; } - if (sticky) setbitn(mo->mo_code, StickyMilterOpt); } @@ -1468,32 +1504,33 @@ milter_reopen_df(e) { char dfname[MAXPATHLEN]; - (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname); + (void) sm_strlcpy(dfname, queuename(e, 'd'), sizeof dfname); /* - ** In SuperSafe mode, e->e_dfp is a read-only FP so + ** In SuperSafe == SAFE_REALLY mode, e->e_dfp is a read-only FP so ** close and reopen writable (later close and reopen ** read only again). ** - ** In !SuperSafe mode, e->e_dfp still points at the + ** In SuperSafe != SAFE_REALLY mode, e->e_dfp still points at the ** buffered file I/O descriptor, still open for writing ** so there isn't as much work to do, just truncate it ** and go. */ - if (SuperSafe) + if (SuperSafe == SAFE_REALLY) { /* close read-only df */ if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL) { - (void) fclose(e->e_dfp); + (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); e->e_flags &= ~EF_HAS_DF; } /* open writable */ - if ((e->e_dfp = fopen(dfname, "w+")) == NULL) + if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, + SM_IO_RDWR, NULL)) == NULL) { - MILTER_DF_ERROR("milter_reopen_df: fopen %s: %s"); + MILTER_DF_ERROR("milter_reopen_df: sm_io_open %s: %s"); return -1; } } @@ -1523,29 +1560,32 @@ milter_reset_df(e) int afd; char dfname[MAXPATHLEN]; - (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname); + (void) sm_strlcpy(dfname, queuename(e, 'd'), sizeof dfname); - if (fflush(e->e_dfp) != 0 || ferror(e->e_dfp)) + if (sm_io_flush(e->e_dfp, SM_TIME_DEFAULT) != 0 || + sm_io_error(e->e_dfp)) { MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s"); return -1; } - else if (!SuperSafe) + else if (SuperSafe != SAFE_REALLY) { /* skip next few clauses */ /* EMPTY */ } - else if ((afd = fileno(e->e_dfp)) >= 0 && fsync(afd) < 0) + else if ((afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL)) >= 0 + && fsync(afd) < 0) { MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s"); return -1; } - else if (fclose(e->e_dfp) < 0) + else if (sm_io_close(e->e_dfp, SM_TIME_DEFAULT) < 0) { MILTER_DF_ERROR("milter_reset_df: error closing %s: %s"); return -1; } - else if ((e->e_dfp = fopen(dfname, "r")) == NULL) + else if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, + SM_IO_RDONLY, NULL)) == NULL) { MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s"); return -1; @@ -1561,17 +1601,17 @@ milter_reset_df(e) ** none ** ** Returns: -** TRUE if any filter deletes recipients, FALSE otherwise +** true if any filter deletes recipients, false otherwise */ bool milter_can_delrcpts() { - bool can = FALSE; + bool can = false; int i; if (tTd(64, 10)) - dprintf("milter_can_delrcpts:"); + sm_dprintf("milter_can_delrcpts:"); for (i = 0; InputFilters[i] != NULL; i++) { @@ -1579,12 +1619,12 @@ milter_can_delrcpts() if (bitset(SMFIF_DELRCPT, m->mf_fflags)) { - can = TRUE; + can = true; break; } } if (tTd(64, 10)) - dprintf("%s\n", can ? "TRUE" : "FALSE"); + sm_dprintf("%s\n", can ? "true" : "false"); return can; } @@ -1605,7 +1645,10 @@ milter_quit_filter(m, e) ENVELOPE *e; { if (tTd(64, 10)) - dprintf("milter_quit_filter(%s)\n", m->mf_name); + sm_dprintf("milter_quit_filter(%s)\n", m->mf_name); + if (MilterLogLevel > 18) + sm_syslog(LOG_INFO, e->e_id, "Milter (%s): quit filter", + m->mf_name); /* Never replace error state */ if (m->mf_state == SMFS_ERROR) @@ -1647,7 +1690,10 @@ milter_abort_filter(m, e) ENVELOPE *e; { if (tTd(64, 10)) - dprintf("milter_abort_filter(%s)\n", m->mf_name); + sm_dprintf("milter_abort_filter(%s)\n", m->mf_name); + if (MilterLogLevel > 10) + sm_syslog(LOG_INFO, e->e_id, "Milter (%s): abort filter", + m->mf_name); if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG) @@ -1692,7 +1738,7 @@ milter_send_macros(m, macros, cmd, e) s = 1; /* for the command character */ for (i = 0; macros[i] != NULL; i++) { - mid = macid(macros[i], NULL); + mid = macid(macros[i]); if (mid == 0) continue; v = macvalue(mid, e); @@ -1701,12 +1747,15 @@ milter_send_macros(m, macros, cmd, e) s += strlen(macros[i]) + 1 + strlen(v) + 1; } - buf = (char *)xalloc(s); + if (s < 0) + return; + + buf = (char *) xalloc(s); bp = buf; *bp++ = cmd; for (i = 0; macros[i] != NULL; i++) { - mid = macid(macros[i], NULL); + mid = macid(macros[i]); if (mid == 0) continue; v = macvalue(mid, e); @@ -1714,17 +1763,17 @@ milter_send_macros(m, macros, cmd, e) continue; if (tTd(64, 10)) - dprintf("milter_send_macros(%s, %c): %s=%s\n", + sm_dprintf("milter_send_macros(%s, %c): %s=%s\n", m->mf_name, cmd, macros[i], v); - (void) strlcpy(bp, macros[i], s - (bp - buf)); + (void) sm_strlcpy(bp, macros[i], s - (bp - buf)); bp += strlen(bp) + 1; - (void) strlcpy(bp, v, s - (bp - buf)); + (void) sm_strlcpy(bp, v, s - (bp - buf)); bp += strlen(bp) + 1; } (void) milter_write(m, SMFIC_MACRO, buf, s, m->mf_timeout[SMFTO_WRITE], e); - sm_free(buf); + sm_free(buf); /* XXX */ } /* @@ -1753,12 +1802,12 @@ milter_send_command(m, command, data, sz, e, state) { char rcmd; ssize_t rlen; - u_long skipflag; + unsigned long skipflag; char *defresponse; char *response; if (tTd(64, 10)) - dprintf("milter_send_command(%s): cmd %c len %ld\n", + sm_dprintf("milter_send_command(%s): cmd %c len %ld\n", m->mf_name, (char) command, (long) sz); /* find skip flag and default failure */ @@ -1818,7 +1867,7 @@ milter_send_command(m, command, data, sz, e, state) bitset(skipflag, m->mf_pflags)) return NULL; - + /* send the command to the filter */ (void) milter_write(m, command, data, sz, m->mf_timeout[SMFTO_WRITE], e); if (m->mf_state == SMFS_ERROR) @@ -1827,6 +1876,7 @@ milter_send_command(m, command, data, sz, e, state) return NULL; } + /* get the response from the filter */ response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e); if (m->mf_state == SMFS_ERROR) @@ -1836,13 +1886,16 @@ milter_send_command(m, command, data, sz, e, state) } if (tTd(64, 10)) - dprintf("milter_send_command(%s): returned %c\n", - m->mf_name, (char) rcmd); + sm_dprintf("milter_send_command(%s): returned %c\n", + m->mf_name, (char) rcmd); switch (rcmd) { case SMFIR_REPLYCODE: MILTER_CHECK_REPLYCODE(defresponse); + if (MilterLogLevel > 8) + sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s", + m->mf_name, response); /* FALLTHROUGH */ case SMFIR_REJECT: @@ -1858,6 +1911,9 @@ milter_send_command(m, command, data, sz, e, state) m->mf_state = SMFS_CLOSABLE; else m->mf_state = SMFS_DONE; + if (MilterLogLevel > 8) + sm_syslog(LOG_INFO, e->e_id, "Milter (%s): accepted", + m->mf_name); break; case SMFIR_CONTINUE: @@ -1868,18 +1924,18 @@ milter_send_command(m, command, data, sz, e, state) default: /* Invalid response to command */ - if (LogLevel > 0) + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, "milter_send_command(%s): returned bogus response %c", m->mf_name, rcmd); - milter_error(m); + milter_error(m, e); break; } if (*state != SMFIR_REPLYCODE && response != NULL) { - sm_free(response); + sm_free(response); /* XXX */ response = NULL; } return response; @@ -1911,9 +1967,10 @@ milter_command(command, data, sz, macros, e, state) { int i; char *response = NULL; + time_t tn = 0; if (tTd(64, 10)) - dprintf("milter_command: cmd %c len %ld\n", + sm_dprintf("milter_command: cmd %c len %ld\n", (char) command, (long) sz); *state = SMFIR_CONTINUE; @@ -1944,7 +2001,19 @@ milter_command(command, data, sz, macros, e, state) } } + if (MilterLogLevel > 21) + tn = curtime(); + response = milter_send_command(m, command, data, sz, e, state); + + if (MilterLogLevel > 21) + { + /* log the time it took for the command per filter */ + sm_syslog(LOG_INFO, e->e_id, + "Milter (%s): time command (%c), %d", + m->mf_name, command, (int) (tn - curtime())); + } + if (*state != SMFIR_CONTINUE) break; } @@ -1977,11 +2046,11 @@ milter_negotiate(m, e) /* sanity check */ if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN) { - if (LogLevel > 0) + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "milter_negotiate(%s): impossible state", + "Milter (%s): negotiate, impossible state", m->mf_name); - milter_error(m); + milter_error(m, e); return -1; } @@ -2006,15 +2075,15 @@ milter_negotiate(m, e) if (rcmd != SMFIC_OPTNEG) { if (tTd(64, 5)) - dprintf("milter_negotiate(%s): returned %c instead of %c\n", + sm_dprintf("milter_negotiate(%s): returned %c instead of %c\n", m->mf_name, rcmd, SMFIC_OPTNEG); - if (LogLevel > 0) + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "milter_negotiate(%s): returned %c instead of %c", + "Milter (%s): negotiate: returned %c instead of %c", m->mf_name, rcmd, SMFIC_OPTNEG); if (response != NULL) - sm_free(response); - milter_error(m); + sm_free(response); /* XXX */ + milter_error(m, e); return -1; } @@ -2022,15 +2091,15 @@ milter_negotiate(m, e) if (response == NULL || rlen < MILTER_LEN_BYTES) { if (tTd(64, 5)) - dprintf("milter_negotiate(%s): did not return valid info\n", + sm_dprintf("milter_negotiate(%s): did not return valid info\n", m->mf_name); - if (LogLevel > 0) + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "milter_negotiate(%s): did not return valid info", + "Milter (%s): negotiate: did not return valid info", m->mf_name); if (response != NULL) - sm_free(response); - milter_error(m); + sm_free(response); /* XXX */ + milter_error(m, e); return -1; } @@ -2041,15 +2110,15 @@ milter_negotiate(m, e) if (rlen != MILTER_OPTLEN) { if (tTd(64, 5)) - dprintf("milter_negotiate(%s): did not return enough info\n", + sm_dprintf("milter_negotiate(%s): did not return enough info\n", m->mf_name); - if (LogLevel > 0) + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "milter_negotiate(%s): did not return enough info", + "Milter (%s): negotiate: did not return enough info", m->mf_name); if (response != NULL) - sm_free(response); - milter_error(m); + sm_free(response); /* XXX */ + milter_error(m, e); return -1; } @@ -2057,7 +2126,7 @@ milter_negotiate(m, e) MILTER_LEN_BYTES); (void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2), MILTER_LEN_BYTES); - sm_free(response); + sm_free(response); /* XXX */ response = NULL; m->mf_fvers = ntohl(fvers); @@ -2069,13 +2138,13 @@ milter_negotiate(m, e) m->mf_fvers > SMFI_VERSION) { if (tTd(64, 5)) - dprintf("milter_negotiate(%s): version %lu != MTA milter version %d\n", + sm_dprintf("milter_negotiate(%s): version %lu != MTA milter version %d\n", m->mf_name, m->mf_fvers, SMFI_VERSION); - if (LogLevel > 0) + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "milter_negotiate(%s): version %ld != MTA milter version %d", + "Milter (%s): negotiate: version %ld != MTA milter version %d", m->mf_name, m->mf_fvers, SMFI_VERSION); - milter_error(m); + milter_error(m, e); return -1; } @@ -2083,15 +2152,15 @@ milter_negotiate(m, e) if ((m->mf_fflags & SMFI_CURR_ACTS) != m->mf_fflags) { if (tTd(64, 5)) - dprintf("milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n", + sm_dprintf("milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n", m->mf_name, m->mf_fflags, - (u_long) SMFI_CURR_ACTS); - if (LogLevel > 0) + (unsigned long) SMFI_CURR_ACTS); + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n", + "Milter (%s): negotiate: filter abilities 0x%lx != MTA milter abilities 0x%lx", m->mf_name, m->mf_fflags, - (u_long) SMFI_CURR_ACTS); - milter_error(m); + (unsigned long) SMFI_CURR_ACTS); + milter_error(m, e); return -1; } @@ -2099,20 +2168,20 @@ milter_negotiate(m, e) if ((m->mf_pflags & SMFI_CURR_PROT) != m->mf_pflags) { if (tTd(64, 5)) - dprintf("milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n", + sm_dprintf("milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n", m->mf_name, m->mf_pflags, - (u_long) SMFI_CURR_PROT); - if (LogLevel > 0) + (unsigned long) SMFI_CURR_PROT); + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n", + "Milter (%s): negotiate: protocol abilities 0x%lx != MTA milter abilities 0x%lx", m->mf_name, m->mf_pflags, - (u_long) SMFI_CURR_PROT); - milter_error(m); + (unsigned long) SMFI_CURR_PROT); + milter_error(m, e); return -1; } if (tTd(64, 5)) - dprintf("milter_negotiate(%s): version %lu, fflags 0x%lx, pflags 0x%lx\n", + sm_dprintf("milter_negotiate(%s): version %lu, fflags 0x%lx, pflags 0x%lx\n", m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags); return 0; } @@ -2154,8 +2223,9 @@ milter_per_connection_check(e) */ static void -milter_error(m) +milter_error(m, e) struct milter *m; + ENVELOPE *e; { /* ** We could send a quit here but @@ -2170,6 +2240,10 @@ milter_error(m) m->mf_sock = -1; } m->mf_state = SMFS_ERROR; + + if (MilterLogLevel > 0) + sm_syslog(LOG_INFO, e->e_id, "Milter (%s): to error state", + m->mf_name); } /* ** MILTER_HEADERS -- send headers to a single milter filter @@ -2192,6 +2266,10 @@ milter_headers(m, e, state) char *response = NULL; HDR *h; + if (MilterLogLevel > 17) + sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, send", + m->mf_name); + for (h = e->e_header; h != NULL; h = h->h_link) { char *buf; @@ -2210,23 +2288,31 @@ milter_headers(m, e, state) continue; if (tTd(64, 10)) - dprintf("milter_headers: %s: %s\n", + sm_dprintf("milter_headers: %s: %s\n", h->h_field, h->h_value); + if (MilterLogLevel > 21) + sm_syslog(LOG_INFO, e->e_id, "Milter (%s): header, %s", + m->mf_name, h->h_field); - s = strlen(h->h_field) + 1 + - strlen(h->h_value) + 1; + s = strlen(h->h_field) + 1 + strlen(h->h_value) + 1; + if (s < 0) + continue; buf = (char *) xalloc(s); - snprintf(buf, s, "%s%c%s", h->h_field, '\0', h->h_value); + (void) sm_snprintf(buf, s, "%s%c%s", + h->h_field, '\0', h->h_value); /* send it over */ response = milter_send_command(m, SMFIC_HEADER, buf, s, e, state); - sm_free(buf); + sm_free(buf); /* XXX */ if (m->mf_state == SMFS_ERROR || m->mf_state == SMFS_DONE || *state != SMFIR_CONTINUE) break; } + if (MilterLogLevel > 17) + sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, sent", + m->mf_name); return response; } /* @@ -2255,19 +2341,22 @@ milter_body(m, e, state) char buf[MILTER_CHUNK_SIZE]; if (tTd(64, 10)) - dprintf("milter_body\n"); + sm_dprintf("milter_body\n"); if (bfrewind(e->e_dfp) < 0) { ExitStat = EX_IOERR; *state = SMFIR_TEMPFAIL; syserr("milter_body: %s/df%s: rewind error", - qid_printqueue(e->e_queuedir), e->e_id); + qid_printqueue(e->e_qgrp, e->e_qdir), e->e_id); return NULL; } + if (MilterLogLevel > 17) + sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, send", + m->mf_name); bp = buf; - while ((c = getc(e->e_dfp)) != EOF) + while ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != SM_IO_EOF) { /* Change LF to CRLF */ if (c == '\n') @@ -2314,7 +2403,7 @@ milter_body(m, e, state) } /* check for read errors */ - if (ferror(e->e_dfp)) + if (sm_io_error(e->e_dfp)) { ExitStat = EX_IOERR; if (*state == SMFIR_CONTINUE || @@ -2323,12 +2412,12 @@ milter_body(m, e, state) *state = SMFIR_TEMPFAIL; if (response != NULL) { - sm_free(response); + sm_free(response); /* XXX */ response = NULL; } } syserr("milter_body: %s/df%s: read error", - qid_printqueue(e->e_queuedir), e->e_id); + qid_printqueue(e->e_qgrp, e->e_qdir), e->e_id); return response; } @@ -2343,6 +2432,9 @@ milter_body(m, e, state) e, state); bp = buf; } + if (MilterLogLevel > 17) + sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, sent", + m->mf_name); return response; } @@ -2372,20 +2464,20 @@ milter_addheader(response, rlen, e) HDR *h; if (tTd(64, 10)) - dprintf("milter_addheader: "); + sm_dprintf("milter_addheader: "); /* sanity checks */ if (response == NULL) { if (tTd(64, 10)) - dprintf("NULL response\n"); + sm_dprintf("NULL response\n"); return; } if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) { if (tTd(64, 10)) - dprintf("didn't follow protocol (total len)\n"); + sm_dprintf("didn't follow protocol (total len)\n"); return; } @@ -2396,20 +2488,20 @@ milter_addheader(response, rlen, e) if (strlen(response) + strlen(val) + 2 != (size_t) rlen) { if (tTd(64, 10)) - dprintf("didn't follow protocol (part len)\n"); + sm_dprintf("didn't follow protocol (part len)\n"); return; } if (*response == '\0') { if (tTd(64, 10)) - dprintf("empty field name\n"); + sm_dprintf("empty field name\n"); return; } for (h = e->e_header; h != NULL; h = h->h_link) { - if (strcasecmp(h->h_field, response) == 0 && + if (sm_strcasecmp(h->h_field, response) == 0 && !bitset(H_USER, h->h_flags) && !bitset(H_TRACE, h->h_flags)) break; @@ -2421,16 +2513,23 @@ milter_addheader(response, rlen, e) if (h != NULL) { if (tTd(64, 10)) - dprintf("Replace default header %s value with %s\n", - h->h_field, val); + sm_dprintf("Replace default header %s value with %s\n", + h->h_field, val); + if (MilterLogLevel > 8) + sm_syslog(LOG_INFO, e->e_id, + "Milter change: default header %s value with %s", + h->h_field, val); h->h_value = newstr(val); h->h_flags |= H_USER; } else { if (tTd(64, 10)) - dprintf("Add %s: %s\n", response, val); - addheader(newstr(response), val, H_USER, &e->e_header); + sm_dprintf("Add %s: %s\n", response, val); + if (MilterLogLevel > 8) + sm_syslog(LOG_INFO, e->e_id, "Milter add: header: %s: %s", + response, val); + addheader(newstr(response), val, H_USER, e); } } /* @@ -2456,20 +2555,20 @@ milter_changeheader(response, rlen, e) HDR *h, *sysheader; if (tTd(64, 10)) - dprintf("milter_changeheader: "); + sm_dprintf("milter_changeheader: "); /* sanity checks */ if (response == NULL) { if (tTd(64, 10)) - dprintf("NULL response\n"); + sm_dprintf("NULL response\n"); return; } if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen) { if (tTd(64, 10)) - dprintf("didn't follow protocol (total len)\n"); + sm_dprintf("didn't follow protocol (total len)\n"); return; } @@ -2484,21 +2583,21 @@ milter_changeheader(response, rlen, e) strlen(val) + 1 != (size_t) rlen) { if (tTd(64, 10)) - dprintf("didn't follow protocol (part len)\n"); + sm_dprintf("didn't follow protocol (part len)\n"); return; } if (*field == '\0') { if (tTd(64, 10)) - dprintf("empty field name\n"); + sm_dprintf("empty field name\n"); return; } sysheader = NULL; for (h = e->e_header; h != NULL; h = h->h_link) { - if (strcasecmp(h->h_field, field) == 0) + if (sm_strcasecmp(h->h_field, field) == 0) { if (bitset(H_USER, h->h_flags) && --index <= 0) @@ -2530,15 +2629,14 @@ milter_changeheader(response, rlen, e) if (*val == '\0') { if (tTd(64, 10)) - dprintf("Delete (noop) %s:\n", field); + sm_dprintf("Delete (noop) %s:\n", field); } else { /* treat modify value with no existing header as add */ if (tTd(64, 10)) - dprintf("Add %s: %s\n", field, val); - - addheader(newstr(field), val, H_USER, &e->e_header); + sm_dprintf("Add %s: %s\n", field, val); + addheader(newstr(field), val, H_USER, e); } return; } @@ -2547,25 +2645,46 @@ milter_changeheader(response, rlen, e) { if (*val == '\0') { - dprintf("Delete%s %s: %s\n", - h == sysheader ? " (default header)" : "", - field, - h->h_value == NULL ? "<NULL>" : h->h_value); + sm_dprintf("Delete%s %s: %s\n", + h == sysheader ? " (default header)" : "", + field, + h->h_value == NULL ? "<NULL>" : h->h_value); + } + else + { + sm_dprintf("Change%s %s: from %s to %s\n", + h == sysheader ? " (default header)" : "", + field, + h->h_value == NULL ? "<NULL>" : h->h_value, + val); + } + } + + if (MilterLogLevel > 8) + { + if (*val == '\0') + { + sm_syslog(LOG_INFO, e->e_id, + "Milter delete: header %s %s: %s", + h == sysheader ? " (default header)" : "", + field, + h->h_value == NULL ? "<NULL>" : h->h_value); } else { - dprintf("Change%s %s: from %s to %s\n", - h == sysheader ? " (default header)" : "", - field, - h->h_value == NULL ? "<NULL>" : h->h_value, - val); + sm_syslog(LOG_INFO, e->e_id, + "Milter change: header %s %s: from %s to %s", + h == sysheader ? " (default header)" : "", + field, + h->h_value == NULL ? "<NULL>" : h->h_value, + val); } } if (h != sysheader && h->h_value != NULL) { e->e_msgsize -= strlen(h->h_value); - sm_free(h->h_value); + /* rpool, don't free: sm_free(h->h_value); XXX */ } if (*val == '\0') @@ -2601,13 +2720,13 @@ milter_addrcpt(response, rlen, e) ENVELOPE *e; { if (tTd(64, 10)) - dprintf("milter_addrcpt: "); + sm_dprintf("milter_addrcpt: "); /* sanity checks */ if (response == NULL) { if (tTd(64, 10)) - dprintf("NULL response\n"); + sm_dprintf("NULL response\n"); return; } @@ -2615,13 +2734,15 @@ milter_addrcpt(response, rlen, e) strlen(response) + 1 != (size_t) rlen) { if (tTd(64, 10)) - dprintf("didn't follow protocol (total len %d != rlen %d)\n", - strlen(response), rlen -1); + sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n", + (int) strlen(response), (int) (rlen - 1)); return; } if (tTd(64, 10)) - dprintf("%s\n", response); + sm_dprintf("%s\n", response); + if (MilterLogLevel > 8) + sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response); (void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e); return; } @@ -2644,13 +2765,13 @@ milter_delrcpt(response, rlen, e) ENVELOPE *e; { if (tTd(64, 10)) - dprintf("milter_delrcpt: "); + sm_dprintf("milter_delrcpt: "); /* sanity checks */ if (response == NULL) { if (tTd(64, 10)) - dprintf("NULL response\n"); + sm_dprintf("NULL response\n"); return; } @@ -2658,12 +2779,15 @@ milter_delrcpt(response, rlen, e) strlen(response) + 1 != (size_t) rlen) { if (tTd(64, 10)) - dprintf("didn't follow protocol (total len)\n"); + sm_dprintf("didn't follow protocol (total len)\n"); return; } if (tTd(64, 10)) - dprintf("%s\n", response); + sm_dprintf("%s\n", response); + if (MilterLogLevel > 8) + sm_syslog(LOG_INFO, e->e_id, "Milter delete: rcpt %s", + response); (void) removefromlist(response, &e->e_sendqueue, e); return; } @@ -2691,7 +2815,7 @@ milter_replbody(response, rlen, newfilter, e) int i; if (tTd(64, 10)) - dprintf("milter_replbody\n"); + sm_dprintf("milter_replbody\n"); /* If a new filter, reset previous character and truncate df */ if (newfilter) @@ -2699,7 +2823,7 @@ milter_replbody(response, rlen, newfilter, e) off_t prevsize = 0; char dfname[MAXPATHLEN]; - (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname); + (void) sm_strlcpy(dfname, queuename(e, 'd'), sizeof dfname); /* Reset prevchar */ prevchar = '\0'; @@ -2710,32 +2834,68 @@ milter_replbody(response, rlen, newfilter, e) int afd; struct stat st; - afd = fileno(e->e_dfp); + afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL); if (afd > 0 && fstat(afd, &st) == 0) prevsize = st.st_size; } /* truncate current df file */ - if (bftruncate(e->e_dfp) < 0) + if (sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE)) { - MILTER_DF_ERROR("milter_reopen_df: bftruncate %s: %s"); - return -1; + if (sm_io_setinfo(e->e_dfp, SM_BF_TRUNCATE, NULL) < 0) + { + MILTER_DF_ERROR("milter_replbody: sm_io truncate %s: %s"); + return -1; + } } else { - if (prevsize > e->e_msgsize) - e->e_msgsize = 0; - else - e->e_msgsize -= prevsize; + int err; + +# if NOFTRUNCATE + /* XXX: Not much we can do except rewind it */ + err = sm_io_error(e->e_dfp); + (void) sm_io_flush(e->e_dfp, SM_TIME_DEFAULT); + + /* + ** Clear error if tried to fflush() + ** a read-only file pointer and + ** there wasn't a previous error. + */ + + if (err == 0) + sm_io_clearerr(e->e_dfp); + + /* errno is set implicitly by fseek() before return */ + err = sm_io_seek(e->e_dfp, SM_TIME_DEFAULT, + 0, SEEK_SET); +# else /* NOFTRUNCATE */ + err = ftruncate(sm_io_getinfo(e->e_dfp, + SM_IO_WHAT_FD, NULL), + 0); +# endif /* NOFTRUNCATE */ + if (err < 0) + { + MILTER_DF_ERROR("milter_replbody: sm_io ftruncate %s: %s"); + return -1; + } } + + if (prevsize > e->e_msgsize) + e->e_msgsize = 0; + else + e->e_msgsize -= prevsize; } + if (MilterLogLevel > 8) + sm_syslog(LOG_INFO, e->e_id, "Milter message: body replaced"); + if (response == NULL) { /* Flush the buffered '\r' */ if (prevchar == '\r') { - (void) putc(prevchar, e->e_dfp); + (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, prevchar); e->e_msgsize++; } return 0; @@ -2749,7 +2909,8 @@ milter_replbody(response, rlen, newfilter, e) /* Not CRLF, output prevchar */ if (response[i] != '\n') { - (void) putc(prevchar, e->e_dfp); + (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, + prevchar); e->e_msgsize++; } prevchar = '\0'; @@ -2772,7 +2933,7 @@ milter_replbody(response, rlen, newfilter, e) continue; } } - (void) putc(response[i], e->e_dfp); + (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, response[i]); e->e_msgsize++; } return 0; @@ -2790,11 +2951,11 @@ milter_replbody(response, rlen, newfilter, e) ** state -- return state from response. ** ** Returns: -** none +** true iff at least one filter is active */ /* ARGSUSED */ -void +bool milter_init(e, state) ENVELOPE *e; char *state; @@ -2802,14 +2963,22 @@ milter_init(e, state) int i; if (tTd(64, 10)) - dprintf("milter_init\n"); + sm_dprintf("milter_init\n"); *state = SMFIR_CONTINUE; + if (InputFilters[0] == NULL) + { + if (MilterLogLevel > 10) + sm_syslog(LOG_INFO, e->e_id, + "Milter: no active filter"); + return false; + } + for (i = 0; InputFilters[i] != NULL; i++) { struct milter *m = InputFilters[i]; - m->mf_sock = milter_open(m, FALSE, e); + m->mf_sock = milter_open(m, false, e); if (m->mf_state == SMFS_ERROR) { MILTER_CHECK_ERROR(continue); @@ -2821,14 +2990,26 @@ milter_init(e, state) m->mf_state == SMFS_ERROR) { if (tTd(64, 5)) - dprintf("milter_init(%s): failed to %s\n", - m->mf_name, - m->mf_sock < 0 ? "open" : "negotiate"); + sm_dprintf("milter_init(%s): failed to %s\n", + m->mf_name, + m->mf_sock < 0 ? "open" : + "negotiate"); + if (MilterLogLevel > 0) + sm_syslog(LOG_INFO, e->e_id, + "Milter (%s): init failed to %s", + m->mf_name, + m->mf_sock < 0 ? "open" : + "negotiate"); /* if negotation failure, close socket */ - milter_error(m); + milter_error(m, e); MILTER_CHECK_ERROR(continue); } + if (MilterLogLevel > 9) + sm_syslog(LOG_INFO, e->e_id, + "Milter (%s): init success to %s", + m->mf_name, + m->mf_sock < 0 ? "open" : "negotiate"); } /* @@ -2839,6 +3020,8 @@ milter_init(e, state) if (*state != SMFIR_CONTINUE) milter_quit(e); + + return true; } /* ** MILTER_CONNECT -- send connection info to milter filters @@ -2861,7 +3044,7 @@ milter_connect(hostname, addr, e, state) char *state; { char family; - u_short port; + unsigned short port; char *buf, *bp; char *response; char *sockinfo = NULL; @@ -2871,7 +3054,9 @@ milter_connect(hostname, addr, e, state) # endif /* NETINET6 */ if (tTd(64, 10)) - dprintf("milter_connect(%s)\n", hostname); + sm_dprintf("milter_connect(%s)\n", hostname); + if (MilterLogLevel > 9) + sm_syslog(LOG_INFO, e->e_id, "Milter: connect to filters"); /* gather data */ switch (addr.sa.sa_family) @@ -2915,7 +3100,7 @@ milter_connect(hostname, addr, e, state) if (family != SMFIA_UNKNOWN) s += sizeof(port) + strlen(sockinfo) + 1; - buf = (char *)xalloc(s); + buf = (char *) xalloc(s); bp = buf; /* put together data */ @@ -2935,7 +3120,7 @@ milter_connect(hostname, addr, e, state) response = milter_command(SMFIC_CONNECT, buf, s, MilterConnectMacros, e, state); - sm_free(buf); + sm_free(buf); /* XXX */ /* ** If this message connection is done for, @@ -2943,7 +3128,11 @@ milter_connect(hostname, addr, e, state) */ if (*state != SMFIR_CONTINUE) + { + if (MilterLogLevel > 7) + sm_syslog(LOG_INFO, e->e_id, "Milter: connect, ending"); milter_quit(e); + } else milter_per_connection_check(e); @@ -2962,7 +3151,7 @@ milter_connect(hostname, addr, e, state) *state = SMFIR_REJECT; if (response != NULL) { - sm_free(response); + sm_free(response); /* XXX */ response = NULL; } } @@ -2990,9 +3179,9 @@ milter_helo(helo, e, state) char *response; if (tTd(64, 10)) - dprintf("milter_helo(%s)\n", helo); + sm_dprintf("milter_helo(%s)\n", helo); - /* HELO/EHLO can come after encryption is negotiated */ + /* HELO/EHLO can come at any point */ for (i = 0; InputFilters[i] != NULL; i++) { struct milter *m = InputFilters[i]; @@ -3041,16 +3230,19 @@ milter_envfrom(args, e, state) if (tTd(64, 10)) { - dprintf("milter_envfrom:"); + sm_dprintf("milter_envfrom:"); for (i = 0; args[i] != NULL; i++) - dprintf(" %s", args[i]); - dprintf("\n"); + sm_dprintf(" %s", args[i]); + sm_dprintf("\n"); } /* sanity check */ if (args[0] == NULL) { *state = SMFIR_REJECT; + if (MilterLogLevel > 10) + sm_syslog(LOG_INFO, e->e_id, + "Milter: reject, no sender"); return NULL; } @@ -3077,18 +3269,28 @@ milter_envfrom(args, e, state) s = 0; for (i = 0; args[i] != NULL; i++) s += strlen(args[i]) + 1; - buf = (char *)xalloc(s); + + if (s < 0) + { + *state = SMFIR_TEMPFAIL; + return NULL; + } + + buf = (char *) xalloc(s); bp = buf; for (i = 0; args[i] != NULL; i++) { - (void) strlcpy(bp, args[i], s - (bp - buf)); + (void) sm_strlcpy(bp, args[i], s - (bp - buf)); bp += strlen(bp) + 1; } + if (MilterLogLevel > 14) + sm_syslog(LOG_INFO, e->e_id, "Milter: senders: %s", buf); + /* send it over */ response = milter_command(SMFIC_MAIL, buf, s, MilterEnvFromMacros, e, state); - sm_free(buf); + sm_free(buf); /* XXX */ /* ** If filter rejects/discards a per message command, @@ -3097,6 +3299,8 @@ milter_envfrom(args, e, state) */ MILTER_CHECK_DONE_MSG(); + if (MilterLogLevel > 10 && *state == SMFIR_REJECT) + sm_syslog(LOG_INFO, e->e_id, "Milter: reject, senders"); return response; } /* @@ -3124,16 +3328,18 @@ milter_envrcpt(args, e, state) if (tTd(64, 10)) { - dprintf("milter_envrcpt:"); + sm_dprintf("milter_envrcpt:"); for (i = 0; args[i] != NULL; i++) - dprintf(" %s", args[i]); - dprintf("\n"); + sm_dprintf(" %s", args[i]); + sm_dprintf("\n"); } /* sanity check */ if (args[0] == NULL) { *state = SMFIR_REJECT; + if (MilterLogLevel > 10) + sm_syslog(LOG_INFO, e->e_id, "Milter: reject, no rcpt"); return NULL; } @@ -3141,18 +3347,28 @@ milter_envrcpt(args, e, state) s = 0; for (i = 0; args[i] != NULL; i++) s += strlen(args[i]) + 1; - buf = (char *)xalloc(s); + + if (s < 0) + { + *state = SMFIR_TEMPFAIL; + return NULL; + } + + buf = (char *) xalloc(s); bp = buf; for (i = 0; args[i] != NULL; i++) { - (void) strlcpy(bp, args[i], s - (bp - buf)); + (void) sm_strlcpy(bp, args[i], s - (bp - buf)); bp += strlen(bp) + 1; } + if (MilterLogLevel > 14) + sm_syslog(LOG_INFO, e->e_id, "Milter: rcpts: %s", buf); + /* send it over */ response = milter_command(SMFIC_RCPT, buf, s, MilterEnvRcptMacros, e, state); - sm_free(buf); + sm_free(buf); /* XXX */ return response; } /* @@ -3191,10 +3407,10 @@ milter_data(e, state) ENVELOPE *e; char *state; { - bool replbody = FALSE; /* milter_replbody() called? */ - bool replfailed = FALSE; /* milter_replbody() failed? */ - bool rewind = FALSE; /* rewind df file? */ - bool dfopen = FALSE; /* df open for writing? */ + bool replbody = false; /* milter_replbody() called? */ + bool replfailed = false; /* milter_replbody() failed? */ + bool rewind = false; /* rewind df file? */ + bool dfopen = false; /* df open for writing? */ bool newfilter; /* reset on each new filter */ char rcmd; int i; @@ -3204,7 +3420,7 @@ milter_data(e, state) ssize_t rlen; if (tTd(64, 10)) - dprintf("milter_data\n"); + sm_dprintf("milter_data\n"); *state = SMFIR_CONTINUE; @@ -3232,7 +3448,7 @@ milter_data(e, state) /* Now reset state for later evaluation */ *state = SMFIR_CONTINUE; - newfilter = TRUE; + newfilter = true; /* previous problem? */ if (m->mf_state == SMFS_ERROR) @@ -3259,7 +3475,7 @@ milter_data(e, state) if (!bitset(SMFIP_NOEOH, m->mf_pflags)) { if (tTd(64, 10)) - dprintf("milter_data: eoh\n"); + sm_dprintf("milter_data: eoh\n"); /* send it over */ response = milter_send_command(m, SMFIC_EOH, NULL, 0, @@ -3271,7 +3487,7 @@ milter_data(e, state) if (!bitset(SMFIP_NOBODY, m->mf_pflags) && e->e_dfp != NULL) { - rewind = TRUE; + rewind = true; response = milter_body(m, e, state); MILTER_CHECK_RESULTS(); } @@ -3291,13 +3507,13 @@ milter_data(e, state) curtime() - eomsent >= m->mf_timeout[SMFTO_EOM]) { if (tTd(64, 5)) - dprintf("milter_data(%s): EOM ACK/NAK timeout\n", + sm_dprintf("milter_data(%s): EOM ACK/NAK timeout\n", m->mf_name); - if (LogLevel > 0) + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, - "milter_data(%s): EOM ACK/NAK timeout\n", + "milter_data(%s): EOM ACK/NAK timeout", m->mf_name); - milter_error(m); + milter_error(m, e); MILTER_CHECK_ERROR(continue); break; } @@ -3308,18 +3524,21 @@ milter_data(e, state) break; if (tTd(64, 10)) - dprintf("milter_data(%s): state %c\n", - m->mf_name, (char) rcmd); + sm_dprintf("milter_data(%s): state %c\n", + m->mf_name, (char) rcmd); switch (rcmd) { case SMFIR_REPLYCODE: MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected"); + if (MilterLogLevel > 8) + sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s", + m->mf_name, response); *state = rcmd; m->mf_state = SMFS_DONE; break; - case SMFIR_REJECT: + case SMFIR_REJECT: /* log msg at end of function */ case SMFIR_DISCARD: case SMFIR_TEMPFAIL: *state = rcmd; @@ -3342,7 +3561,7 @@ milter_data(e, state) case SMFIR_ADDHEADER: if (!bitset(SMFIF_ADDHDRS, m->mf_fflags)) { - if (LogLevel > 9) + if (MilterLogLevel > 9) sm_syslog(LOG_WARNING, e->e_id, "milter_data(%s): lied about adding headers, honoring request anyway", m->mf_name); @@ -3353,7 +3572,7 @@ milter_data(e, state) case SMFIR_CHGHEADER: if (!bitset(SMFIF_CHGHDRS, m->mf_fflags)) { - if (LogLevel > 9) + if (MilterLogLevel > 9) sm_syslog(LOG_WARNING, e->e_id, "milter_data(%s): lied about changing headers, honoring request anyway", m->mf_name); @@ -3364,7 +3583,7 @@ milter_data(e, state) case SMFIR_ADDRCPT: if (!bitset(SMFIF_ADDRCPT, m->mf_fflags)) { - if (LogLevel > 9) + if (MilterLogLevel > 9) sm_syslog(LOG_WARNING, e->e_id, "milter_data(%s) lied about adding recipients, honoring request anyway", m->mf_name); @@ -3375,7 +3594,7 @@ milter_data(e, state) case SMFIR_DELRCPT: if (!bitset(SMFIF_DELRCPT, m->mf_fflags)) { - if (LogLevel > 9) + if (MilterLogLevel > 9) sm_syslog(LOG_WARNING, e->e_id, "milter_data(%s): lied about removing recipients, honoring request anyway", m->mf_name); @@ -3386,11 +3605,11 @@ milter_data(e, state) case SMFIR_REPLBODY: if (!bitset(SMFIF_MODBODY, m->mf_fflags)) { - if (LogLevel > 0) + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, "milter_data(%s): lied about replacing body, rejecting request and tempfailing message", m->mf_name); - replfailed = TRUE; + replfailed = true; break; } @@ -3402,33 +3621,32 @@ milter_data(e, state) { if (milter_reopen_df(e) < 0) { - replfailed = TRUE; + replfailed = true; break; } - dfopen = TRUE; - rewind = TRUE; + dfopen = true; + rewind = true; } if (milter_replbody(response, rlen, newfilter, e) < 0) - replfailed = TRUE; - newfilter = FALSE; - replbody = TRUE; + replfailed = true; + newfilter = false; + replbody = true; break; default: /* Invalid response to command */ - if (LogLevel > 0) + if (MilterLogLevel > 0) sm_syslog(LOG_ERR, e->e_id, "milter_data(%s): returned bogus response %c", m->mf_name, rcmd); - milter_error(m); + milter_error(m, e); break; } - if (rcmd != SMFIR_REPLYCODE && - response != NULL) + if (rcmd != SMFIR_REPLYCODE && response != NULL) { - sm_free(response); + sm_free(response); /* XXX */ response = NULL; } @@ -3440,7 +3658,7 @@ milter_data(e, state) { /* flush possible buffered character */ milter_replbody(NULL, 0, !replbody, e); - replbody = FALSE; + replbody = false; } if (m->mf_state == SMFS_ERROR) @@ -3458,21 +3676,17 @@ finishup: *state == SMFIR_ACCEPT) { *state = SMFIR_TEMPFAIL; - if (response != NULL) - { - sm_free(response); - response = NULL; - } + SM_FREE_CLR(response); } if (dfopen) { - (void) fclose(e->e_dfp); + (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); e->e_dfp = NULL; e->e_flags &= ~EF_HAS_DF; - dfopen = FALSE; + dfopen = false; } - rewind = FALSE; + rewind = false; } if ((dfopen && milter_reset_df(e) < 0) || @@ -3490,18 +3704,17 @@ finishup: *state == SMFIR_ACCEPT) { *state = SMFIR_TEMPFAIL; - if (response != NULL) - { - sm_free(response); - response = NULL; - } + SM_FREE_CLR(response); } errno = save_errno; syserr("milter_data: %s/df%s: read error", - qid_printqueue(e->e_queuedir), e->e_id); + qid_printqueue(e->e_qgrp, e->e_qdir), e->e_id); } + MILTER_CHECK_DONE_MSG(); + if (MilterLogLevel > 10 && *state == SMFIR_REJECT) + sm_syslog(LOG_INFO, e->e_id, "Milter: reject, data"); return response; } /* @@ -3521,7 +3734,7 @@ milter_quit(e) int i; if (tTd(64, 10)) - dprintf("milter_quit\n"); + sm_dprintf("milter_quit(%s)\n", e->e_id); for (i = 0; InputFilters[i] != NULL; i++) milter_quit_filter(InputFilters[i], e); @@ -3543,7 +3756,7 @@ milter_abort(e) int i; if (tTd(64, 10)) - dprintf("milter_abort\n"); + sm_dprintf("milter_abort\n"); for (i = 0; InputFilters[i] != NULL; i++) { @@ -3556,4 +3769,4 @@ milter_abort(e) milter_abort_filter(m, e); } } -#endif /* _FFR_MILTER */ +#endif /* MILTER */ diff --git a/gnu/usr.sbin/sendmail/sendmail/mime.c b/gnu/usr.sbin/sendmail/sendmail/mime.c index 71456a3e66b..00fa1e07a07 100644 --- a/gnu/usr.sbin/sendmail/sendmail/mime.c +++ b/gnu/usr.sbin/sendmail/sendmail/mime.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. + * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1994, 1996-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1994 @@ -14,15 +14,7 @@ #include <sendmail.h> #include <string.h> -#ifndef lint -static char id[] = "@(#)$Sendmail: mime.c,v 8.94.16.3 2000/10/09 02:46:10 gshapiro Exp $"; -#endif /* ! lint */ - -static int isboundary __P((char *, char **)); -static int mimeboundary __P((char *, char **)); -static int mime_fromqp __P((u_char *, u_char **, int, int)); -static int mime_getchar __P((FILE *, char **, int *)); -static int mime_getchar_crlf __P((FILE *, char **, int *)); +SM_RCSID("@(#)$Sendmail: mime.c,v 8.121 2001/09/04 22:43:04 ca Exp $") /* ** MIME support. @@ -42,6 +34,10 @@ static int mime_getchar_crlf __P((FILE *, char **, int *)); */ #if MIME8TO7 +static int isboundary __P((char *, char **)); +static int mimeboundary __P((char *, char **)); +static int mime_getchar __P((SM_FILE_T *, char **, int *)); +static int mime_getchar_crlf __P((SM_FILE_T *, char **, int *)); /* character set for hex and base64 encoding */ static char Base16Code[] = "0123456789ABCDEF"; @@ -113,26 +109,26 @@ mime8to7(mci, header, e, boundaries, flags) char **pvp; int argc = 0; char *bp; - bool use_qp = FALSE; + bool use_qp = false; struct args argv[MAXMIMEARGS]; char bbuf[128]; char buf[MAXLINE]; char pvpbuf[MAXLINE]; - extern u_char MimeTokenTab[256]; + extern unsigned char MimeTokenTab[256]; if (tTd(43, 1)) { - dprintf("mime8to7: flags = %x, boundaries =", flags); + sm_dprintf("mime8to7: flags = %x, boundaries =", flags); if (boundaries[0] == NULL) - dprintf(" <none>"); + sm_dprintf(" <none>"); else { for (i = 0; boundaries[i] != NULL; i++) - dprintf(" %s", boundaries[i]); + sm_dprintf(" %s", boundaries[i]); } - dprintf("\n"); + sm_dprintf("\n"); } - MapNLtoCRLF = TRUE; + MapNLtoCRLF = true; p = hvalue("Content-Transfer-Encoding", header); if (p == NULL || (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, @@ -144,7 +140,7 @@ mime8to7(mci, header, e, boundaries, flags) else { cataddr(pvp, NULL, buf, sizeof buf, '\0'); - cte = newstr(buf); + cte = sm_rpool_strdup_x(e->e_rpool, buf); } type = subtype = NULL; @@ -164,7 +160,7 @@ mime8to7(mci, header, e, boundaries, flags) if (tTd(43, 40)) { for (i = 0; pvp[i] != NULL; i++) - dprintf("pvp[%d] = \"%s\"\n", i, pvp[i]); + sm_dprintf("pvp[%d] = \"%s\"\n", i, pvp[i]); } type = *pvp++; if (*pvp != NULL && strcmp(*pvp, "/") == 0 && @@ -222,16 +218,16 @@ mime8to7(mci, header, e, boundaries, flags) ** just copy it through. */ - snprintf(buf, sizeof buf, "%.100s/%.100s", type, subtype); + (void) sm_snprintf(buf, sizeof buf, "%.100s/%.100s", type, subtype); if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e'))) flags |= M87F_NO8BIT; # ifdef USE_B_CLASS if (wordinclass(buf, 'b') || wordinclass(type, 'b')) - MapNLtoCRLF = FALSE; + MapNLtoCRLF = false; # endif /* USE_B_CLASS */ if (wordinclass(buf, 'q') || wordinclass(type, 'q')) - use_qp = TRUE; + use_qp = true; /* ** Multipart requires special processing. @@ -239,16 +235,16 @@ mime8to7(mci, header, e, boundaries, flags) ** Do a recursive descent into the message. */ - if (strcasecmp(type, "multipart") == 0 && + if (sm_strcasecmp(type, "multipart") == 0 && (!bitset(M87F_NO8BIT, flags) || bitset(M87F_NO8TO7, flags))) { - if (strcasecmp(subtype, "digest") == 0) + if (sm_strcasecmp(subtype, "digest") == 0) flags |= M87F_DIGEST; for (i = 0; i < argc; i++) { - if (strcasecmp(argv[i].a_field, "boundary") == 0) + if (sm_strcasecmp(argv[i].a_field, "boundary") == 0) break; } if (i >= argc || argv[i].a_value == NULL) @@ -265,7 +261,7 @@ mime8to7(mci, header, e, boundaries, flags) p = argv[i].a_value; stripquotes(p); } - if (strlcpy(bbuf, p, sizeof bbuf) >= sizeof bbuf) + if (sm_strlcpy(bbuf, p, sizeof bbuf) >= sizeof bbuf) { usrerr("mime8to7: multipart boundary \"%s\" too long", p); @@ -275,7 +271,8 @@ mime8to7(mci, header, e, boundaries, flags) } if (tTd(43, 1)) - dprintf("mime8to7: multipart boundary \"%s\"\n", bbuf); + sm_dprintf("mime8to7: multipart boundary \"%s\"\n", + bbuf); for (i = 0; i < MAXMIMENESTING; i++) { if (boundaries[i] == NULL) @@ -299,26 +296,28 @@ mime8to7(mci, header, e, boundaries, flags) putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; bt = MBT_FINAL; - while (fgets(buf, sizeof buf, e->e_dfp) != NULL) + while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof buf) + != NULL) { bt = mimeboundary(buf, boundaries); if (bt != MBT_NOTSEP) break; - putxline(buf, strlen(buf), mci, PXLF_MAPFROM|PXLF_STRIP8BIT); + putxline(buf, strlen(buf), mci, + PXLF_MAPFROM|PXLF_STRIP8BIT); if (tTd(43, 99)) - dprintf(" ...%s", buf); + sm_dprintf(" ...%s", buf); } - if (feof(e->e_dfp)) + if (sm_io_eof(e->e_dfp)) bt = MBT_FINAL; while (bt != MBT_FINAL) { auto HDR *hdr = NULL; - snprintf(buf, sizeof buf, "--%s", bbuf); + (void) sm_strlcpyn(buf, sizeof buf, 2, "--", bbuf); putline(buf, mci); if (tTd(43, 35)) - dprintf(" ...%s\n", buf); - collect(e->e_dfp, FALSE, &hdr, e); + sm_dprintf(" ...%s\n", buf); + collect(e->e_dfp, false, &hdr, e); if (tTd(43, 101)) putline("+++after collect", mci); putheader(mci, hdr, e, flags); @@ -326,27 +325,29 @@ mime8to7(mci, header, e, boundaries, flags) putline("+++after putheader", mci); bt = mime8to7(mci, hdr, e, boundaries, flags); } - snprintf(buf, sizeof buf, "--%s--", bbuf); + (void) sm_strlcpyn(buf, sizeof buf, 3, "--", bbuf, "--"); putline(buf, mci); if (tTd(43, 35)) - dprintf(" ...%s\n", buf); + sm_dprintf(" ...%s\n", buf); boundaries[i] = NULL; mci->mci_flags &= ~MCIF_INMIME; /* skip the late "comment" epilogue */ - while (fgets(buf, sizeof buf, e->e_dfp) != NULL) + while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof buf) + != NULL) { bt = mimeboundary(buf, boundaries); if (bt != MBT_NOTSEP) break; - putxline(buf, strlen(buf), mci, PXLF_MAPFROM|PXLF_STRIP8BIT); + putxline(buf, strlen(buf), mci, + PXLF_MAPFROM|PXLF_STRIP8BIT); if (tTd(43, 99)) - dprintf(" ...%s", buf); + sm_dprintf(" ...%s", buf); } - if (feof(e->e_dfp)) + if (sm_io_eof(e->e_dfp)) bt = MBT_FINAL; if (tTd(43, 3)) - dprintf("\t\t\tmime8to7=>%s (multipart)\n", + sm_dprintf("\t\t\tmime8to7=>%s (multipart)\n", MimeBoundaryNames[bt]); return bt; } @@ -357,7 +358,7 @@ mime8to7(mci, header, e, boundaries, flags) ** Class 's' is predefined to have "rfc822" only. */ - if (strcasecmp(type, "message") == 0) + if (sm_strcasecmp(type, "message") == 0) { if (!wordinclass(subtype, 's')) { @@ -370,7 +371,7 @@ mime8to7(mci, header, e, boundaries, flags) putline("", mci); mci->mci_flags |= MCIF_INMIME; - collect(e->e_dfp, FALSE, &hdr, e); + collect(e->e_dfp, false, &hdr, e); if (tTd(43, 101)) putline("+++after collect", mci); putheader(mci, hdr, e, flags); @@ -396,12 +397,13 @@ mime8to7(mci, header, e, boundaries, flags) if (!bitset(M87F_NO8BIT|M87F_NO8TO7, flags)) { /* remember where we were */ - offset = ftell(e->e_dfp); + offset = sm_io_tell(e->e_dfp, SM_TIME_DEFAULT); if (offset == -1) - syserr("mime8to7: cannot ftell on df%s", e->e_id); + syserr("mime8to7: cannot sm_io_tell on df%s", e->e_id); /* do a scan of this body type to count character types */ - while (fgets(buf, sizeof buf, e->e_dfp) != NULL) + while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof buf) + != NULL) { if (mimeboundary(buf, boundaries) != MBT_NOTSEP) break; @@ -426,10 +428,10 @@ mime8to7(mci, header, e, boundaries, flags) /* return to the original offset for processing */ /* XXX use relative seeks to handle >31 bit file sizes? */ - if (fseek(e->e_dfp, offset, SEEK_SET) < 0) - syserr("mime8to7: cannot fseek on df%s", e->e_id); + if (sm_io_seek(e->e_dfp, SM_TIME_DEFAULT, offset, SEEK_SET) < 0) + syserr("mime8to7: cannot sm_io_fseek on df%s", e->e_id); else - clearerr(e->e_dfp); + sm_io_clearerr(e->e_dfp); } /* @@ -442,13 +444,13 @@ mime8to7(mci, header, e, boundaries, flags) if (tTd(43, 8)) { - dprintf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s, type=%s/%s\n", + sm_dprintf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s, type=%s/%s\n", (long) sectionhighbits, (long) sectionsize, cte == NULL ? "[none]" : cte, type == NULL ? "[none]" : type, subtype == NULL ? "[none]" : subtype); } - if (cte != NULL && strcasecmp(cte, "binary") == 0) + if (cte != NULL && sm_strcasecmp(cte, "binary") == 0) sectionsize = sectionhighbits; linelen = 0; bp = buf; @@ -468,22 +470,23 @@ mime8to7(mci, header, e, boundaries, flags) ** situation. */ - snprintf(buf, sizeof buf, + (void) sm_snprintf(buf, sizeof buf, "Content-Transfer-Encoding: %.200s", cte); putline(buf, mci); if (tTd(43, 36)) - dprintf(" ...%s\n", buf); + sm_dprintf(" ...%s\n", buf); } putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; - while (fgets(buf, sizeof buf, e->e_dfp) != NULL) + while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof buf) + != NULL) { bt = mimeboundary(buf, boundaries); if (bt != MBT_NOTSEP) break; putline(buf, mci); } - if (feof(e->e_dfp)) + if (sm_io_eof(e->e_dfp)) bt = MBT_FINAL; } else if (!MapNLtoCRLF || @@ -493,15 +496,16 @@ mime8to7(mci, header, e, boundaries, flags) int c1, c2; if (tTd(43, 36)) - dprintf(" ...Content-Transfer-Encoding: base64\n"); + sm_dprintf(" ...Content-Transfer-Encoding: base64\n"); putline("Content-Transfer-Encoding: base64", mci); - snprintf(buf, sizeof buf, + (void) sm_snprintf(buf, sizeof buf, "X-MIME-Autoconverted: from 8bit to base64 by %s id %s", MyHostName, e->e_id); putline(buf, mci); putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; - while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != EOF) + while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != + SM_IO_EOF) { if (linelen > 71) { @@ -514,7 +518,7 @@ mime8to7(mci, header, e, boundaries, flags) *bp++ = Base64Code[(c1 >> 2)]; c1 = (c1 & 0x03) << 4; c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); - if (c2 == EOF) + if (c2 == SM_IO_EOF) { *bp++ = Base64Code[c1]; *bp++ = '='; @@ -525,7 +529,7 @@ mime8to7(mci, header, e, boundaries, flags) *bp++ = Base64Code[c1]; c1 = (c2 & 0x0f) << 2; c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); - if (c2 == EOF) + if (c2 == SM_IO_EOF) { *bp++ = Base64Code[c1]; *bp++ = '='; @@ -558,9 +562,9 @@ mime8to7(mci, header, e, boundaries, flags) setbitn(*p, badchars); if (tTd(43, 36)) - dprintf(" ...Content-Transfer-Encoding: quoted-printable\n"); + sm_dprintf(" ...Content-Transfer-Encoding: quoted-printable\n"); putline("Content-Transfer-Encoding: quoted-printable", mci); - snprintf(buf, sizeof buf, + (void) sm_snprintf(buf, sizeof buf, "X-MIME-Autoconverted: from 8bit to quoted-printable by %s id %s", MyHostName, e->e_id); putline(buf, mci); @@ -568,7 +572,8 @@ mime8to7(mci, header, e, boundaries, flags) mci->mci_flags &= ~MCIF_INHEADER; fromstate = 0; c2 = '\n'; - while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF) + while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != + SM_IO_EOF) { if (c1 == '\n') { @@ -657,18 +662,18 @@ mime8to7(mci, header, e, boundaries, flags) } if (tTd(43, 3)) - dprintf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]); + sm_dprintf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]); return bt; } /* ** MIME_GETCHAR -- get a character for MIME processing ** -** Treats boundaries as EOF. +** Treats boundaries as SM_IO_EOF. ** ** Parameters: ** fp -- the input file. ** boundaries -- the current MIME boundaries. -** btp -- if the return value is EOF, *btp is set to +** btp -- if the return value is SM_IO_EOF, *btp is set to ** the type of the boundary. ** ** Returns: @@ -677,16 +682,16 @@ mime8to7(mci, header, e, boundaries, flags) static int mime_getchar(fp, boundaries, btp) - register FILE *fp; + register SM_FILE_T *fp; char **boundaries; int *btp; { int c; - static u_char *bp = NULL; + static unsigned char *bp = NULL; static int buflen = 0; - static bool atbol = TRUE; /* at beginning of line */ - static int bt = MBT_SYNTAX; /* boundary type of next EOF */ - static u_char buf[128]; /* need not be a full line */ + static bool atbol = true; /* at beginning of line */ + static int bt = MBT_SYNTAX; /* boundary type of next SM_IO_EOF */ + static unsigned char buf[128]; /* need not be a full line */ int start = 0; /* indicates position of - in buffer */ if (buflen == 1 && *bp == '\n') @@ -700,33 +705,33 @@ mime_getchar(fp, boundaries, btp) return *bp++; } else - c = getc(fp); + c = sm_io_getc(fp, SM_TIME_DEFAULT); bp = buf; buflen = 0; if (c == '\n') { /* might be part of a MIME boundary */ *bp++ = c; - atbol = TRUE; - c = getc(fp); + atbol = true; + c = sm_io_getc(fp, SM_TIME_DEFAULT); if (c == '\n') { - (void) ungetc(c, fp); + (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c); return c; } start = 1; } - if (c != EOF) + if (c != SM_IO_EOF) *bp++ = c; else bt = MBT_FINAL; if (atbol && c == '-') { /* check for a message boundary */ - c = getc(fp); + c = sm_io_getc(fp, SM_TIME_DEFAULT); if (c != '-') { - if (c != EOF) + if (c != SM_IO_EOF) *bp++ = c; else bt = MBT_FINAL; @@ -738,11 +743,12 @@ mime_getchar(fp, boundaries, btp) /* got "--", now check for rest of separator */ *bp++ = '-'; while (bp < &buf[sizeof buf - 2] && - (c = getc(fp)) != EOF && c != '\n') + (c = sm_io_getc(fp, SM_TIME_DEFAULT)) != SM_IO_EOF && + c != '\n') { *bp++ = c; } - *bp = '\0'; + *bp = '\0'; /* XXX simply cut off? */ bt = mimeboundary((char *) &buf[start], boundaries); switch (bt) { @@ -751,11 +757,11 @@ mime_getchar(fp, boundaries, btp) /* we have a message boundary */ buflen = 0; *btp = bt; - return EOF; + return SM_IO_EOF; } atbol = c == '\n'; - if (c != EOF) + if (c != SM_IO_EOF) *bp++ = c; } @@ -763,7 +769,7 @@ mime_getchar(fp, boundaries, btp) if (buflen < 0) { *btp = bt; - return EOF; + return SM_IO_EOF; } bp = buf; return *bp++; @@ -774,7 +780,7 @@ mime_getchar(fp, boundaries, btp) ** Parameters: ** fp -- the input file. ** boundaries -- the current MIME boundaries. -** btp -- if the return value is EOF, *btp is set to +** btp -- if the return value is SM_IO_EOF, *btp is set to ** the type of the boundary. ** ** Returns: @@ -783,22 +789,22 @@ mime_getchar(fp, boundaries, btp) static int mime_getchar_crlf(fp, boundaries, btp) - register FILE *fp; + register SM_FILE_T *fp; char **boundaries; int *btp; { - static bool sendlf = FALSE; + static bool sendlf = false; int c; if (sendlf) { - sendlf = FALSE; + sendlf = false; return '\n'; } c = mime_getchar(fp, boundaries, btp); if (c == '\n' && MapNLtoCRLF) { - sendlf = TRUE; + sendlf = true; return '\r'; } return c; @@ -840,7 +846,7 @@ mimeboundary(line, boundaries) line[i] = '\0'; if (tTd(43, 5)) - dprintf("mimeboundary: line=\"%s\"... ", line); + sm_dprintf("mimeboundary: line=\"%s\"... ", line); /* check for this as an intermediate boundary */ if (isboundary(&line[2], boundaries) >= 0) @@ -856,7 +862,7 @@ mimeboundary(line, boundaries) line[i] = savec; if (tTd(43, 5)) - dprintf("%s\n", MimeBoundaryNames[type]); + sm_dprintf("%s\n", MimeBoundaryNames[type]); return type; } /* @@ -916,6 +922,7 @@ isboundary(line, boundaries) #endif /* MIME8TO7 */ #if MIME7TO8 +static int mime_fromqp __P((unsigned char *, unsigned char **, int, int)); /* ** MIME7TO8 -- output 7 bit encoded MIME body in 8 bit format @@ -965,11 +972,11 @@ mime7to8(mci, header, e) register char *p; char *cte; char **pvp; - u_char *fbufp; + unsigned char *fbufp; char buf[MAXLINE]; - u_char fbuf[MAXLINE + 1]; + unsigned char fbuf[MAXLINE + 1]; char pvpbuf[MAXLINE]; - extern u_char MimeTokenTab[256]; + extern unsigned char MimeTokenTab[256]; p = hvalue("Content-Transfer-Encoding", header); if (p == NULL || @@ -986,22 +993,23 @@ mime7to8(mci, header, e) /* cheap failsafe algorithm -- should work on text/plain */ if (p != NULL) { - snprintf(buf, sizeof buf, + (void) sm_snprintf(buf, sizeof buf, "Content-Transfer-Encoding: %s", p); putline(buf, mci); } putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; - while (fgets(buf, sizeof buf, e->e_dfp) != NULL) + while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof buf) + != NULL) putline(buf, mci); return; } cataddr(pvp, NULL, buf, sizeof buf, '\0'); - cte = newstr(buf); + cte = sm_rpool_strdup_x(e->e_rpool, buf); mci->mci_flags |= MCIF_INHEADER; putline("Content-Transfer-Encoding: 8bit", mci); - snprintf(buf, sizeof buf, + (void) sm_snprintf(buf, sizeof buf, "X-MIME-Autoconverted: from %.200s to 8bit by %s id %s", cte, MyHostName, e->e_id); putline(buf, mci); @@ -1014,35 +1022,36 @@ mime7to8(mci, header, e) ** it is not base64. */ - if (strcasecmp(cte, "base64") == 0) + if (sm_strcasecmp(cte, "base64") == 0) { int c1, c2, c3, c4; fbufp = fbuf; - while ((c1 = fgetc(e->e_dfp)) != EOF) + while ((c1 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != + SM_IO_EOF) { if (isascii(c1) && isspace(c1)) continue; do { - c2 = fgetc(e->e_dfp); + c2 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT); } while (isascii(c2) && isspace(c2)); - if (c2 == EOF) + if (c2 == SM_IO_EOF) break; do { - c3 = fgetc(e->e_dfp); + c3 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT); } while (isascii(c3) && isspace(c3)); - if (c3 == EOF) + if (c3 == SM_IO_EOF) break; do { - c4 = fgetc(e->e_dfp); + c4 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT); } while (isascii(c4) && isspace(c4)); - if (c4 == EOF) + if (c4 == SM_IO_EOF) break; if (c1 == '=' || c2 == '=') @@ -1092,9 +1101,10 @@ mime7to8(mci, header, e) { /* quoted-printable */ fbufp = fbuf; - while (fgets(buf, sizeof buf, e->e_dfp) != NULL) + while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof buf) + != NULL) { - if (mime_fromqp((u_char *) buf, &fbufp, 0, + if (mime_fromqp((unsigned char *) buf, &fbufp, 0, &fbuf[MAXLINE] - fbufp) == 0) continue; @@ -1112,14 +1122,14 @@ mime7to8(mci, header, e) putxline((char *) fbuf, fbufp - fbuf, mci, PXLF_MAPFROM); } if (tTd(43, 3)) - dprintf("\t\t\tmime7to8 => %s to 8bit done\n", cte); + sm_dprintf("\t\t\tmime7to8 => %s to 8bit done\n", cte); } /* ** The following is based on Borenstein's "codes.c" module, with simplifying ** changes as we do not deal with multipart, and to do the translation in-core, ** with an attempt to prevent overrun of output buffers. ** -** What is needed here are changes to defned this code better against +** What is needed here are changes to defend this code better against ** bad encodings. Questionable to always return 0xFF for bad mappings. */ @@ -1139,14 +1149,17 @@ static char index_hex[128] = static int mime_fromqp(infile, outfile, state, maxlen) - u_char *infile; - u_char **outfile; + unsigned char *infile; + unsigned char **outfile; int state; /* Decoding body (0) or header (1) */ int maxlen; /* Max # of chars allowed in outfile */ { int c1, c2; int nchar = 0; + if (maxlen < 0) + return 0; + while ((c1 = *infile++) != '\0') { if (c1 == '=') diff --git a/gnu/usr.sbin/sendmail/sendmail/newaliases.8 b/gnu/usr.sbin/sendmail/sendmail/newaliases.8 index 747c7f52835..ef49f960106 100644 --- a/gnu/usr.sbin/sendmail/sendmail/newaliases.8 +++ b/gnu/usr.sbin/sendmail/sendmail/newaliases.8 @@ -1,4 +1,4 @@ -.\" Copyright (c) 1998, 1999 Sendmail, Inc and its suppliers. +.\" Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. .\" All rights reserved. .\" Copyright (c) 1983, 1997 Eric P. Allman. All rights reserved. .\" Copyright (c) 1985, 1990, 1993 @@ -9,9 +9,9 @@ .\" the sendmail distribution. .\" .\" -.\" $Sendmail: newaliases.1,v 8.15.28.1 2000/12/14 23:08:15 gshapiro Exp $ +.\" $Sendmail: newaliases.1,v 8.17 2001/04/03 01:53:18 gshapiro Exp $ .\" -.Dd December 14, 2000 +.Dd April 3, 2001 .Dt NEWALIASES 8 .Os .Sh NAME diff --git a/gnu/usr.sbin/sendmail/sendmail/parseaddr.c b/gnu/usr.sbin/sendmail/sendmail/parseaddr.c index f43ecafb125..30267fc36f4 100644 --- a/gnu/usr.sbin/sendmail/sendmail/parseaddr.c +++ b/gnu/usr.sbin/sendmail/sendmail/parseaddr.c @@ -11,16 +11,15 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: parseaddr.c,v 8.234.4.13 2001/08/14 23:08:13 ca Exp $"; -#endif /* ! lint */ - #include <sendmail.h> -static void allocaddr __P((ADDRESS *, int, char *)); +SM_RCSID("@(#)$Sendmail: parseaddr.c,v 8.340 2001/09/04 22:43:04 ca Exp $") + +static void allocaddr __P((ADDRESS *, int, char *, ENVELOPE *)); static int callsubr __P((char**, int, ENVELOPE *)); static char *map_lookup __P((STAB *, char *, char **, int *, ENVELOPE *)); static ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *)); +static bool hasctrlchar __P((register char *, bool)); /* ** PARSEADDR -- Parse an address @@ -39,7 +38,7 @@ static ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *)); ** Parameters: ** addr -- the address to parse. ** a -- a pointer to the address descriptor buffer. -** If NULL, a header will be created. +** If NULL, an address will be created. ** flags -- describe detail for parsing. See RF_ definitions ** in sendmail.h. ** delim -- the character to terminate the address, passed @@ -47,6 +46,8 @@ static ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *)); ** delimptr -- if non-NULL, set to the location of the ** delim character that was found. ** e -- the envelope that will contain this address. +** isrcpt -- true if the address denotes a recipient; false +** indicates a sender. ** ** Returns: ** A pointer to the address descriptor header (`a' if @@ -54,22 +55,23 @@ static ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *)); ** NULL on error. ** ** Side Effects: -** none +** e->e_to = addr */ /* following delimiters are inherent to the internal algorithms */ #define DELIMCHARS "()<>,;\r\n" /* default word delimiters */ ADDRESS * -parseaddr(addr, a, flags, delim, delimptr, e) +parseaddr(addr, a, flags, delim, delimptr, e, isrcpt) char *addr; register ADDRESS *a; int flags; int delim; char **delimptr; register ENVELOPE *e; + bool isrcpt; { - register char **pvp; + char **pvp; auto char *delimptrbuf; bool qup; char pvpbuf[PSBUFSIZE]; @@ -80,7 +82,7 @@ parseaddr(addr, a, flags, delim, delimptr, e) e->e_to = addr; if (tTd(20, 1)) - dprintf("\n--parseaddr(%s)\n", addr); + sm_dprintf("\n--parseaddr(%s)\n", addr); if (delimptr == NULL) delimptr = &delimptrbuf; @@ -89,14 +91,14 @@ parseaddr(addr, a, flags, delim, delimptr, e) if (pvp == NULL) { if (tTd(20, 1)) - dprintf("parseaddr-->NULL\n"); + sm_dprintf("parseaddr-->NULL\n"); return NULL; } - if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr)) + if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr, isrcpt)) { if (tTd(20, 1)) - dprintf("parseaddr-->bad address\n"); + sm_dprintf("parseaddr-->bad address\n"); return NULL; } @@ -114,7 +116,7 @@ parseaddr(addr, a, flags, delim, delimptr, e) if (savec != '\0') **delimptr = '\0'; - e->e_to = addr = newstr(addr); + e->e_to = addr = sm_rpool_strdup_x(e->e_rpool, addr); if (savec != '\0') **delimptr = savec; } @@ -124,12 +126,11 @@ parseaddr(addr, a, flags, delim, delimptr, e) ** Ruleset 0 does basic parsing. It must resolve. */ - qup = FALSE; - if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL) - qup = TRUE; - if (rewrite(pvp, 0, 0, e) == EX_TEMPFAIL) - qup = TRUE; - + qup = false; + if (REWRITE(pvp, 3, e) == EX_TEMPFAIL) + qup = true; + if (REWRITE(pvp, 0, e) == EX_TEMPFAIL) + qup = true; /* ** Build canonical address from pvp. @@ -137,16 +138,57 @@ parseaddr(addr, a, flags, delim, delimptr, e) a = buildaddr(pvp, a, flags, e); + if (hasctrlchar(a->q_user, isrcpt)) + { + if (tTd(20, 1)) + sm_dprintf("parseaddr-->bad q_user\n"); + return NULL; + } + /* ** Make local copies of the host & user and then ** transport them out. */ - allocaddr(a, flags, addr); + allocaddr(a, flags, addr, e); if (QS_IS_BADADDR(a->q_state)) return a; /* + ** Select a queue directory for recipient addresses. + ** This is done here and in split_across_queue_groups(), + ** but the latter applies to addresses after aliasing, + ** and only if splitting is done. + */ + + if ((a->q_qgrp == NOAQGRP || a->q_qgrp == ENVQGRP) && + !bitset(RF_SENDERADDR|RF_HEADERADDR, flags) && + OpMode != MD_INITALIAS) + { + int r; + + /* call ruleset which should return a queue group name */ + r = rscap(RS_QUEUEGROUP, a->q_user, NULL, e, &pvp, pvpbuf, + sizeof(pvpbuf)); + if (r == EX_OK && + pvp != NULL && pvp[0] != NULL && + (pvp[0][0] & 0377) == CANONNET && + pvp[1] != NULL && pvp[1][0] != '\0') + { + r = name2qid(pvp[1]); + if (r == NOQGRP && LogLevel > 10) + sm_syslog(LOG_INFO, NOQID, + "can't find queue group name %s, selection ignored", + pvp[1]); + if (tTd(20, 4) && r != NOQGRP) + sm_syslog(LOG_INFO, NOQID, + "queue group name %s -> %d", + pvp[1], r); + a->q_qgrp = r == NOQGRP ? ENVQGRP : r; + } + } + + /* ** If there was a parsing failure, mark it for queueing. */ @@ -157,10 +199,10 @@ parseaddr(addr, a, flags, delim, delimptr, e) if (e->e_sendmode == SM_DEFER) msg = "Deferring message until queue run"; if (tTd(20, 1)) - dprintf("parseaddr: queuing message\n"); + sm_dprintf("parseaddr: queuing message\n"); message(msg); if (e->e_message == NULL && e->e_sendmode != SM_DEFER) - e->e_message = newstr(msg); + e->e_message = sm_rpool_strdup_x(e->e_rpool, msg); a->q_state = QS_QUEUEUP; a->q_status = "4.4.3"; } @@ -171,59 +213,142 @@ parseaddr(addr, a, flags, delim, delimptr, e) if (tTd(20, 1)) { - dprintf("parseaddr-->"); - printaddr(a, FALSE); + sm_dprintf("parseaddr-->"); + printaddr(a, false); } return a; } /* -** INVALIDADDR -- check for address containing meta-characters +** INVALIDADDR -- check for address containing characters used for macros ** ** Parameters: ** addr -- the address to check. +** delimptr -- if non-NULL: end of address to check, i.e., +** a pointer in the address string. +** isrcpt -- true iff the address is for a recipient. ** ** Returns: -** TRUE -- if the address has any "wierd" characters -** FALSE -- otherwise. +** true -- if the address has characters that are reservered +** for macros or is too long. +** false -- otherwise. */ bool -invalidaddr(addr, delimptr) +invalidaddr(addr, delimptr, isrcpt) register char *addr; char *delimptr; + bool isrcpt; { + bool result = false; char savedelim = '\0'; + int len = 0; if (delimptr != NULL) { + /* delimptr points to the end of the address to test */ savedelim = *delimptr; - if (savedelim != '\0') - *delimptr = '\0'; + if (savedelim != '\0') /* if that isn't '\0' already: */ + *delimptr = '\0'; /* set it */ } - if (strlen(addr) > MAXNAME - 1) + for (; *addr != '\0'; addr++) { - usrerr("553 5.1.1 Address too long (%d bytes max)", - MAXNAME - 1); - goto failure; + if ((*addr & 0340) == 0200) + { + setstat(EX_USAGE); + result = true; + break; + } + if (++len > MAXNAME - 1) + { + usrerr("553 5.1.0 Address too long (%d bytes max)", + MAXNAME - 1); + result = true; + goto delim; + } } + if (result) + { + if (isrcpt) + usrerr("501 5.1.3 Syntax error in mailbox address"); + else + usrerr("501 5.1.7 Syntax error in mailbox address"); + } +delim: + if (delimptr != NULL && savedelim != '\0') + *delimptr = savedelim; /* restore old character at delimptr */ + return result; +} +/* +** HASCTRLCHAR -- check for address containing meta-characters +** +** Checks that the address contains no meta-characters, and contains +** no "non-printable" characters unless they are quoted or escaped. +** Quoted or escaped characters are literals. +** +** Parameters: +** addr -- the address to check. +** isrcpt -- true if the address is for a recipient; false +** indicates a from. +** +** Returns: +** true -- if the address has any "wierd" characters or +** non-printable characters or if a quote is unbalanced. +** false -- otherwise. +*/ + +static bool +hasctrlchar(addr, isrcpt) + register char *addr; + bool isrcpt; +{ + bool result = false; + int len = 0; + bool quoted = false; + + if (addr == NULL) + return false; for (; *addr != '\0'; addr++) { + if (!quoted && (*addr < 32 || *addr == 127)) + { + result = true; /* a non-printable */ + break; + } + if (*addr == '"') + quoted = !quoted; + else if (*addr == '\\') + { + /* XXX Generic problem: no '\0' in strings. */ + if (*++addr == '\0') + { + result = true; + break; + } + } if ((*addr & 0340) == 0200) + { + setstat(EX_USAGE); + result = true; break; + } + if (++len > MAXNAME - 1) + { + usrerr("553 5.1.0 Address too long (%d bytes max)", + MAXNAME - 1); + return true; + } } - if (*addr == '\0') + if (quoted) + result = true; /* unbalanced quote */ + if (result) { - if (delimptr != NULL && savedelim != '\0') - *delimptr = savedelim; - return FALSE; + if (isrcpt) + usrerr("501 5.1.3 Syntax error in mailbox address"); + else + usrerr("501 5.1.7 Syntax error in mailbox address"); } - setstat(EX_USAGE); - usrerr("553 5.1.1 Address contained invalid control characters"); -failure: - if (delimptr != NULL && savedelim != '\0') - *delimptr = savedelim; - return TRUE; + return result; } /* ** ALLOCADDR -- do local allocations of address on demand. @@ -235,6 +360,7 @@ failure: ** flags -- the copy flag (see RF_ definitions in sendmail.h ** for a description). ** paddr -- the printname of the address. +** e -- envelope ** ** Returns: ** none. @@ -244,30 +370,32 @@ failure: */ static void -allocaddr(a, flags, paddr) +allocaddr(a, flags, paddr, e) register ADDRESS *a; int flags; char *paddr; + ENVELOPE *e; { if (tTd(24, 4)) - dprintf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr); + sm_dprintf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr); a->q_paddr = paddr; if (a->q_user == NULL) - a->q_user = newstr(""); + a->q_user = ""; if (a->q_host == NULL) - a->q_host = newstr(""); + a->q_host = ""; if (bitset(RF_COPYPARSE, flags)) { - a->q_host = newstr(a->q_host); + a->q_host = sm_rpool_strdup_x(e->e_rpool, a->q_host); if (a->q_user != a->q_paddr) - a->q_user = newstr(a->q_user); + a->q_user = sm_rpool_strdup_x(e->e_rpool, a->q_user); } if (a->q_paddr == NULL) - a->q_paddr = newstr(a->q_user); + a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user); + a->q_qgrp = NOAQGRP; } /* ** PRESCAN -- Prescan name and make it canonical @@ -332,7 +460,7 @@ static short StateTab[NSTATES][NSTATES] = }; /* token type table -- it gets modified with $o characters */ -static u_char TokTypeTab[256] = +static unsigned char TokTypeTab[256] = { /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, @@ -370,7 +498,7 @@ static u_char TokTypeTab[256] = }; /* token type table for MIME parsing */ -u_char MimeTokenTab[256] = +unsigned char MimeTokenTab[256] = { /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL, @@ -408,7 +536,7 @@ u_char MimeTokenTab[256] = }; /* token type table: don't strip comments */ -u_char TokTypeNoC[256] = +unsigned char TokTypeNoC[256] = { /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, @@ -455,7 +583,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) char pvpbuf[]; int pvpbsize; char **delimptr; - u_char *toktab; + unsigned char *toktab; { register char *p; register char *q; @@ -470,7 +598,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) int newstate; char *saveto = CurEnv->e_to; static char *av[MAXATOM + 1]; - static char firsttime = TRUE; + static bool firsttime = true; extern int errno; if (firsttime) @@ -478,7 +606,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) /* initialize the token type table */ char obuf[50]; - firsttime = FALSE; + firsttime = false; if (OperatorChars == NULL) { if (ConfigLevel < 7) @@ -488,7 +616,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) } expand(OperatorChars, obuf, sizeof obuf - sizeof DELIMCHARS, CurEnv); - (void) strlcat(obuf, DELIMCHARS, sizeof obuf); + (void) sm_strlcat(obuf, DELIMCHARS, sizeof obuf); for (p = obuf; *p != '\0'; p++) { if (TokTypeTab[*p & 0xff] == ATM) @@ -504,8 +632,8 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) errno = 0; q = pvpbuf; - bslashmode = FALSE; - route_syntax = FALSE; + bslashmode = false; + route_syntax = false; cmntcnt = 0; anglecnt = 0; avp = av; @@ -515,9 +643,9 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) CurEnv->e_to = p; if (tTd(22, 11)) { - dprintf("prescan: "); + sm_dprintf("prescan: "); xputs(p); - dprintf("\n"); + sm_dprintf("\n"); } do @@ -533,7 +661,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) if (q >= &pvpbuf[pvpbsize - 5]) { usrerr("553 5.1.1 Address too long"); - if (strlen(addr) > (SIZE_T) MAXNAME) + if (strlen(addr) > MAXNAME) addr[MAXNAME] = '\0'; returnnull: if (delimptr != NULL) @@ -553,18 +681,18 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) /* diagnose and patch up bad syntax */ if (state == QST) { - usrerr("653 Unbalanced '\"'"); + usrerr("553 Unbalanced '\"'"); c = '"'; } else if (cmntcnt > 0) { - usrerr("653 Unbalanced '('"); + usrerr("553 Unbalanced '('"); c = ')'; } else if (anglecnt > 0) { c = '>'; - usrerr("653 Unbalanced '<'"); + usrerr("553 Unbalanced '<'"); } else break; @@ -579,20 +707,20 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) /* special case for better error management */ if (delim == ',' && !route_syntax) { - usrerr("653 Unbalanced '<'"); + usrerr("553 Unbalanced '<'"); c = '>'; p--; } } if (tTd(22, 101)) - dprintf("c=%c, s=%d; ", c, state); + sm_dprintf("c=%c, s=%d; ", c, state); /* chew up special characters */ *q = '\0'; if (bslashmode) { - bslashmode = FALSE; + bslashmode = false; /* kludge \! for naive users */ if (cmntcnt > 0) @@ -609,7 +737,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) if (c == '\\') { - bslashmode = TRUE; + bslashmode = true; } else if (state == QST) { @@ -625,7 +753,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) { if (cmntcnt <= 0) { - usrerr("653 Unbalanced ')'"); + usrerr("553 Unbalanced ')'"); c = NOCHAR; } else @@ -643,18 +771,18 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) while (isascii(*ptr) && isspace(*ptr)) ptr++; if (*ptr == '@') - route_syntax = TRUE; + route_syntax = true; } else if (c == '>') { if (anglecnt <= 0) { - usrerr("653 Unbalanced '>'"); + usrerr("553 Unbalanced '>'"); c = NOCHAR; } else anglecnt--; - route_syntax = FALSE; + route_syntax = false; } else if (delim == ' ' && isascii(c) && isspace(c)) c = ' '; @@ -668,14 +796,14 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) newstate = StateTab[state][toktab[c & 0xff]]; if (tTd(22, 101)) - dprintf("ns=%02o\n", newstate); + sm_dprintf("ns=%02o\n", newstate); state = newstate & TYPE; if (state == ILL) { if (isascii(c) && isprint(c)) - usrerr("653 Illegal character %c", c); + usrerr("553 Illegal character %c", c); else - usrerr("653 Illegal character 0x%02x", c); + usrerr("553 Illegal character 0x%02x", c); } if (bitset(M, newstate)) c = NOCHAR; @@ -689,9 +817,9 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) *q++ = '\0'; if (tTd(22, 36)) { - dprintf("tok="); + sm_dprintf("tok="); xputs(tok); - dprintf("\n"); + sm_dprintf("\n"); } if (avp >= &av[MAXATOM]) { @@ -712,14 +840,14 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) *delimptr = p; if (tTd(22, 12)) { - dprintf("prescan==>"); + sm_dprintf("prescan==>"); printav(av); } CurEnv->e_to = saveto; if (av[0] == NULL) { if (tTd(22, 1)) - dprintf("prescan: null leading token\n"); + sm_dprintf("prescan: null leading token\n"); return NULL; } return av; @@ -751,6 +879,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) ** ruleset -- the ruleset to use for rewriting. ** reclevel -- recursion level (to catch loops). ** e -- the current envelope. +** maxatom -- maximum length of buffer (usually MAXATOM) ** ** Returns: ** A status code. If EX_TEMPFAIL, higher level code should @@ -768,11 +897,12 @@ struct match }; int -rewrite(pvp, ruleset, reclevel, e) +rewrite(pvp, ruleset, reclevel, e, maxatom) char **pvp; int ruleset; int reclevel; register ENVELOPE *e; + int maxatom; { register char *ap; /* address pointer */ register char *rp; /* rewrite pointer */ @@ -798,7 +928,7 @@ rewrite(pvp, ruleset, reclevel, e) rulename = RuleSetNames[ruleset]; if (rulename == NULL) { - snprintf(name, sizeof name, "%d", ruleset); + (void) sm_snprintf(name, sizeof name, "%d", ruleset); rulename = name; } if (OpMode == MD_TEST) @@ -807,12 +937,13 @@ rewrite(pvp, ruleset, reclevel, e) prefix = "rewrite: ruleset "; if (OpMode == MD_TEST) { - printf("%s%-16.16s input:", prefix, rulename); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "%s%-16.16s input:", prefix, rulename); printav(pvp); } else if (tTd(21, 1)) { - dprintf("%s%-16.16s input:", prefix, rulename); + sm_dprintf("%s%-16.16s input:", prefix, rulename); printav(pvp); } if (reclevel++ > MaxRuleRecursion) @@ -842,10 +973,10 @@ rewrite(pvp, ruleset, reclevel, e) if (tTd(21, 12)) { if (tTd(21, 15)) - dprintf("-----trying rule (line %d):", + sm_dprintf("-----trying rule (line %d):", rwr->r_line); else - dprintf("-----trying rule:"); + sm_dprintf("-----trying rule:"); printav(rwr->r_lhs); } @@ -859,7 +990,7 @@ rewrite(pvp, ruleset, reclevel, e) rulename, ruleno); if (tTd(21, 1)) { - dprintf("workspace: "); + sm_dprintf("workspace: "); printav(pvp); } break; @@ -870,11 +1001,11 @@ rewrite(pvp, ruleset, reclevel, e) rp = *rvp; if (tTd(21, 35)) { - dprintf("ADVANCE rp="); + sm_dprintf("ADVANCE rp="); xputs(rp); - dprintf(", ap="); + sm_dprintf(", ap="); xputs(ap); - dprintf("\n"); + sm_dprintf("\n"); } if (rp == NULL) { @@ -905,16 +1036,16 @@ rewrite(pvp, ruleset, reclevel, e) { if (tTd(21, 36)) { - dprintf("EXTEND rp="); + sm_dprintf("EXTEND rp="); xputs(rp); - dprintf(", ap="); + sm_dprintf(", ap="); xputs(ap); - dprintf("\n"); + sm_dprintf("\n"); } goto extendclass; } if (tTd(21, 36)) - dprintf("CLMATCH\n"); + sm_dprintf("CLMATCH\n"); mlp++; break; @@ -958,7 +1089,7 @@ rewrite(pvp, ruleset, reclevel, e) ap = macvalue(rp[1], e); mlp->match_first = avp; if (tTd(21, 2)) - dprintf("rewrite: LHS $&%s => \"%s\"\n", + sm_dprintf("rewrite: LHS $&%s => \"%s\"\n", macname(rp[1]), ap == NULL ? "(NULL)" : ap); @@ -967,7 +1098,8 @@ rewrite(pvp, ruleset, reclevel, e) while (*ap != '\0') { if (*avp == NULL || - strncasecmp(ap, *avp, strlen(*avp)) != 0) + sm_strncasecmp(ap, *avp, + strlen(*avp)) != 0) { /* no match */ avp = mlp->match_first; @@ -1002,11 +1134,11 @@ rewrite(pvp, ruleset, reclevel, e) if (tTd(21, 36)) { - dprintf("BACKUP rp="); + sm_dprintf("BACKUP rp="); xputs(rp); - dprintf(", ap="); + sm_dprintf(", ap="); xputs(ap); - dprintf("\n"); + sm_dprintf("\n"); } if (ap == NULL) @@ -1045,7 +1177,7 @@ rewrite(pvp, ruleset, reclevel, e) if (mlp < mlist || *rvp != NULL) { if (tTd(21, 10)) - dprintf("----- rule fails\n"); + sm_dprintf("----- rule fails\n"); rwr = rwr->r_next; ruleno++; loopcount = 0; @@ -1055,7 +1187,7 @@ rewrite(pvp, ruleset, reclevel, e) rvp = rwr->r_rhs; if (tTd(21, 12)) { - dprintf("-----rule matches:"); + sm_dprintf("-----rule matches:"); printav(rvp); } @@ -1095,23 +1227,28 @@ rewrite(pvp, ruleset, reclevel, e) } if (tTd(21, 15)) { - dprintf("$%c:", rp[1]); + sm_dprintf("$%c:", rp[1]); pp = m->match_first; while (pp <= m->match_last) { - dprintf(" %lx=\"", - (u_long) *pp); - (void) dflush(); - dprintf("%s\"", *pp++); + sm_dprintf(" %p=\"", *pp); + sm_dflush(); + sm_dprintf("%s\"", *pp++); } - dprintf("\n"); + sm_dprintf("\n"); } pp = m->match_first; while (pp <= m->match_last) { - if (avp >= &npvp[MAXATOM]) + if (avp >= &npvp[maxatom]) { syserr("554 5.3.0 rewrite: expansion too long"); + if (LogLevel > 9) + sm_syslog(LOG_ERR, + e->e_id, + "rewrite: expansion too long, ruleset=%s, ruleno=%d", + rulename, + ruleno); return EX_DATAERR; } *avp++ = *pp++; @@ -1120,10 +1257,14 @@ rewrite(pvp, ruleset, reclevel, e) else { /* some sort of replacement */ - if (avp >= &npvp[MAXATOM]) + if (avp >= &npvp[maxatom]) { toolong: syserr("554 5.3.0 rewrite: expansion too long"); + if (LogLevel > 9) + sm_syslog(LOG_ERR, e->e_id, + "rewrite: expansion too long, ruleset=%s, ruleno=%d", + rulename, ruleno); return EX_DATAERR; } if ((*rp & 0377) != MACRODEXPAND) @@ -1142,7 +1283,7 @@ rewrite(pvp, ruleset, reclevel, e) char pvpbuf[PSBUFSIZE]; if (tTd(21, 2)) - dprintf("rewrite: RHS $&%s => \"%s\"\n", + sm_dprintf("rewrite: RHS $&%s => \"%s\"\n", macname(rp[1]), mval == NULL ? "(NULL)" : mval); if (mval == NULL || *mval == '\0') @@ -1155,7 +1296,8 @@ rewrite(pvp, ruleset, reclevel, e) { if (pvpb1 != NULL) sm_free(pvpb1); - pvpb1 = (char **)xalloc(trsize); + pvpb1 = (char **) + sm_pmalloc_x(trsize); pvpb1_size = trsize; } @@ -1177,15 +1319,16 @@ rewrite(pvp, ruleset, reclevel, e) while (*xpvp != NULL) { if (tTd(21, 19)) - dprintf(" ... %s\n", + sm_dprintf(" ... %s\n", *xpvp); - *avp++ = newstr(*xpvp); - if (avp >= &npvp[MAXATOM]) + *avp++ = sm_rpool_strdup_x( + e->e_rpool, *xpvp); + if (avp >= &npvp[maxatom]) goto toolong; xpvp++; } if (tTd(21, 19)) - dprintf(" ... DONE\n"); + sm_dprintf(" ... DONE\n"); /* restore the old trailing input */ memmove((char *) pvp, @@ -1335,15 +1478,15 @@ rewrite(pvp, ruleset, reclevel, e) /* append it to the token list */ for (avp = hbrvp; *xpvp != NULL; xpvp++) { - *avp++ = newstr(*xpvp); - if (avp >= &npvp[MAXATOM]) + *avp++ = sm_rpool_strdup_x(e->e_rpool, *xpvp); + if (avp >= &npvp[maxatom]) goto toolong; } /* restore the old trailing information */ rvp = avp - 1; for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; ) - if (avp >= &npvp[MAXATOM]) + if (avp >= &npvp[maxatom]) goto toolong; } @@ -1363,19 +1506,20 @@ rewrite(pvp, ruleset, reclevel, e) if (tTd(21, 4)) { - dprintf("rewritten as:"); + sm_dprintf("rewritten as:"); printav(pvp); } } if (OpMode == MD_TEST) { - printf("%s%-16.16s returns:", prefix, rulename); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "%s%-16.16s returns:", prefix, rulename); printav(pvp); } else if (tTd(21, 1)) { - dprintf("%s%-16.16s returns:", prefix, rulename); + sm_dprintf("%s%-16.16s returns:", prefix, rulename); printav(pvp); } return rstat; @@ -1402,14 +1546,25 @@ callsubr(pvp, reclevel, e) ENVELOPE *e; { char **avp; - char **rvp; register int i; - int subr; + int subr, j; + int nsubr; int status; int rstat = EX_OK; - char *tpvp[MAXATOM + 1]; +#define MAX_SUBR 16 + int subrnumber[MAX_SUBR]; + int subrindex[MAX_SUBR]; - for (avp = pvp; *avp != NULL; avp++) + nsubr = 0; + + /* + ** Look for subroutine calls in pvp, collect them into subr*[] + ** We will perform the calls in the next loop, because we will + ** call the "last" subroutine first to avoid recursive calls + ** and too much copying. + */ + + for (avp = pvp, j = 0; *avp != NULL; avp++, j++) { if ((**avp & 0377) == CALLSUBR && avp[1] != NULL) { @@ -1417,73 +1572,66 @@ callsubr(pvp, reclevel, e) subr = strtorwset(avp[1], NULL, ST_FIND); if (subr < 0) { - syserr("Unknown ruleset %s", avp[1]); + syserr("554 5.3.5 Unknown ruleset %s", avp[1]); return EX_CONFIG; } - if (tTd(21, 3)) - dprintf("-----callsubr %s (%d)\n", - avp[1], subr); - /* - ** Take care of possible inner calls first. - ** use a full size temporary buffer to avoid - ** overflows in rewrite, but strip off the - ** subroutine call. + ** XXX instead of doing this we could optimize + ** the rules after reading them: just remove + ** calls to empty rulesets */ - for (i = 2; avp[i] != NULL; i++) - tpvp[i - 2] = avp[i]; - tpvp[i - 2] = NULL; - - status = callsubr(tpvp, reclevel, e); - if (rstat == EX_OK || status == EX_TEMPFAIL) - rstat = status; - - /* - ** Now we need to call the ruleset specified for - ** the subroutine. we can do this with the - ** temporary buffer that we set up earlier, - ** since it has all the data we want to rewrite. - */ - - status = rewrite(tpvp, subr, reclevel, e); - if (rstat == EX_OK || status == EX_TEMPFAIL) - rstat = status; - - /* - ** Find length of tpvp and current offset into - ** pvp, if the total is greater than MAXATOM, - ** then it would overflow the buffer if we copied - ** it back in to pvp, in which case we throw a - ** fit. - */ - - for (rvp = tpvp; *rvp != NULL; rvp++) + /* subroutine is an empty ruleset? don't call it */ + if (RewriteRules[subr] == NULL) + { + if (tTd(21, 3)) + sm_dprintf("-----skip subr %s (%d)\n", + avp[1], subr); + for (i = 2; avp[i] != NULL; i++) + avp[i - 2] = avp[i]; + avp[i - 2] = NULL; continue; - if (((rvp - tpvp) + (avp - pvp)) > MAXATOM) + } + if (++nsubr >= MAX_SUBR) { - syserr("554 5.3.0 callsubr: expansion too long"); - return EX_DATAERR; + syserr("554 5.3.0 Too many subroutine calls (%d max)", + MAX_SUBR); + return EX_CONFIG; } + subrnumber[nsubr] = subr; + subrindex[nsubr] = j; + } + } - /* - ** Now we can copy the rewritten code over - ** the initial subroutine call in the buffer. - */ + /* + ** Perform the actual subroutines calls, "last" one first, i.e., + ** go from the right to the left through all calls, + ** do the rewriting in place. + */ - for (i = 0; tpvp[i] != NULL; i++) - avp[i] = tpvp[i]; - avp[i] = NULL; + for (; nsubr > 0; nsubr--) + { + subr = subrnumber[nsubr]; + avp = pvp + subrindex[nsubr]; - /* - ** If we got this far, we've processed the left - ** most subroutine, and recursively called ourselves - ** to handle any other subroutines. We're done. - */ + /* remove the subroutine call and name */ + for (i = 2; avp[i] != NULL; i++) + avp[i - 2] = avp[i]; + avp[i - 2] = NULL; - break; - } + /* + ** Now we need to call the ruleset specified for + ** the subroutine. we can do this inplace since + ** we call the "last" subroutine first. + */ + + status = rewrite(avp, subr, reclevel, e, + MAXATOM - subrindex[nsubr]); + if (status != EX_OK && status != EX_TEMPFAIL) + return status; + if (rstat == EX_OK || status == EX_TEMPFAIL) + rstat = status; } return rstat; } @@ -1491,7 +1639,7 @@ callsubr(pvp, reclevel, e) ** MAP_LOOKUP -- do lookup in map ** ** Parameters: -** map -- the map to use for the lookup. +** smap -- the map to use for the lookup. ** key -- the key to look up. ** argvect -- arguments to pass to the map lookup. ** pstat -- a pointer to an integer in which to store the @@ -1526,7 +1674,7 @@ map_lookup(smap, key, argvect, pstat, e) { /* don't do any map lookups */ if (tTd(60, 1)) - dprintf("map_lookup(%s, %s) => DEFERRED\n", + sm_dprintf("map_lookup(%s, %s) => DEFERRED\n", smap->s_name, key); *pstat = EX_TEMPFAIL; return NULL; @@ -1537,19 +1685,19 @@ map_lookup(smap, key, argvect, pstat, e) if (tTd(60, 1)) { - dprintf("map_lookup(%s, %s", smap->s_name, key); + sm_dprintf("map_lookup(%s, %s", smap->s_name, key); if (tTd(60, 5)) { int i; for (i = 0; argvect[i] != NULL; i++) - dprintf(", %%%d=%s", i, argvect[i]); + sm_dprintf(", %%%d=%s", i, argvect[i]); } - dprintf(") => "); + sm_dprintf(") => "); } replac = (*map->map_class->map_lookup)(map, key, argvect, &status); if (tTd(60, 1)) - dprintf("%s (%d)\n", + sm_dprintf("%s (%d)\n", replac != NULL ? replac : "NOT FOUND", status); @@ -1558,17 +1706,17 @@ map_lookup(smap, key, argvect, pstat, e) { *pstat = EX_TEMPFAIL; if (tTd(60, 1)) - dprintf("map_lookup(%s, %s) tempfail: errno=%d\n", + sm_dprintf("map_lookup(%s, %s) tempfail: errno=%d\n", smap->s_name, key, errno); if (e->e_message == NULL) { char mbuf[320]; - snprintf(mbuf, sizeof mbuf, + (void) sm_snprintf(mbuf, sizeof mbuf, "%.80s map: lookup (%s): deferred", smap->s_name, shortenstring(key, MAXSHORTSTR)); - e->e_message = newstr(mbuf); + e->e_message = sm_rpool_strdup_x(e->e_rpool, mbuf); } } if (status == EX_TEMPFAIL && map->map_tapp != NULL) @@ -1582,11 +1730,11 @@ map_lookup(smap, key, argvect, pstat, e) if (rwbuf != NULL) sm_free(rwbuf); rwbuflen = i; - rwbuf = (char *) xalloc(rwbuflen); + rwbuf = (char *) sm_pmalloc_x(rwbuflen); } - snprintf(rwbuf, rwbuflen, "%s%s", key, map->map_tapp); + (void) sm_strlcpyn(rwbuf, rwbuflen, 2, key, map->map_tapp); if (tTd(60, 4)) - dprintf("map_lookup tempfail: returning \"%s\"\n", + sm_dprintf("map_lookup tempfail: returning \"%s\"\n", rwbuf); return rwbuf; } @@ -1660,13 +1808,10 @@ static struct errcodes { "software", EX_SOFTWARE }, { "tempfail", EX_TEMPFAIL }, { "protocol", EX_PROTOCOL }, -#ifdef EX_CONFIG { "config", EX_CONFIG }, -#endif /* EX_CONFIG */ { NULL, EX_UNAVAILABLE } }; - static ADDRESS * buildaddr(tv, a, flags, e) register char **tv; @@ -1684,12 +1829,12 @@ buildaddr(tv, a, flags, e) if (tTd(24, 5)) { - dprintf("buildaddr, flags=%x, tv=", flags); + sm_dprintf("buildaddr, flags=%x, tv=", flags); printav(tv); } if (a == NULL) - a = (ADDRESS *) xalloc(sizeof *a); + a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof *a); memset((char *) a, '\0', sizeof *a); hbuf[0] = '\0'; @@ -1731,20 +1876,22 @@ badaddr: cataddr(++tv, NULL, ubuf, sizeof ubuf, ' '); /* save away the host name */ - if (strcasecmp(mname, "error") == 0) + if (sm_strcasecmp(mname, "error") == 0) { /* Set up triplet for use by -bv */ a->q_mailer = &errormailer; - a->q_user = newstr(ubuf); + a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf); + /* XXX wrong place? */ if (hostp != NULL) { register struct errcodes *ep; - a->q_host = newstr(hbuf); + a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf); if (strchr(hbuf, '.') != NULL) { - a->q_status = newstr(hbuf); + a->q_status = sm_rpool_strdup_x(e->e_rpool, + hbuf); setstat(dsntoexitstat(hbuf)); } else if (isascii(hbuf[0]) && isdigit(hbuf[0])) @@ -1754,7 +1901,7 @@ badaddr: else { for (ep = ErrorCodes; ep->ec_name != NULL; ep++) - if (strcasecmp(ep->ec_name, hbuf) == 0) + if (sm_strcasecmp(ep->ec_name, hbuf) == 0) break; setstat(ep->ec_code); } @@ -1780,7 +1927,7 @@ badaddr: off = 4; ubuf[3] = '\0'; } - (void) snprintf(fmt, sizeof fmt, "%s %%s", ubuf); + (void) sm_strlcpyn(fmt, sizeof fmt, 2, ubuf, " %s"); if (off > 4) usrerr(fmt, ubuf + off); else if (isenhsc(hbuf, '\0') > 0) @@ -1798,7 +1945,7 @@ badaddr: for (mp = Mailer; (m = *mp++) != NULL; ) { - if (strcasecmp(m->m_name, mname) == 0) + if (sm_strcasecmp(m->m_name, mname) == 0) break; } if (m == NULL) @@ -1819,7 +1966,7 @@ badaddr: a->q_host = NULL; } else - a->q_host = newstr(hbuf); + a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf); /* figure out the user */ p = ubuf; @@ -1841,40 +1988,31 @@ badaddr: { /* may be :include: */ stripquotes(ubuf); - if (strncasecmp(ubuf, ":include:", 9) == 0) + if (sm_strncasecmp(ubuf, ":include:", 9) == 0) { /* if :include:, don't need further rewriting */ a->q_mailer = m = InclMailer; - a->q_user = newstr(&ubuf[9]); + a->q_user = sm_rpool_strdup_x(e->e_rpool, &ubuf[9]); return a; } } /* rewrite according recipient mailer rewriting rules */ - define('h', a->q_host, e); - -#if _FFR_ADDR_TYPE - /* - ** Note, change the 9 to a 10 before removing #if FFR check - ** in a future version. - */ + macdefine(&e->e_macro, A_PERM, 'h', a->q_host); - if (ConfigLevel >= 9 || + if (ConfigLevel >= 10 || !bitset(RF_SENDERADDR|RF_HEADERADDR, flags)) -#else /* _FFR_ADDR_TYPE */ - if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags)) -#endif /* _FFR_ADDR_TYPE */ { /* sender addresses done later */ - (void) rewrite(tv, 2, 0, e); + (void) REWRITE(tv, 2, e); if (m->m_re_rwset > 0) - (void) rewrite(tv, m->m_re_rwset, 0, e); + (void) REWRITE(tv, m->m_re_rwset, e); } - (void) rewrite(tv, 4, 0, e); + (void) REWRITE(tv, 4, e); /* save the result for the command line/RCPT argument */ cataddr(tv, NULL, ubuf, sizeof ubuf, '\0'); - a->q_user = newstr(ubuf); + a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf); /* ** Do mapping to lower case as requested by mailer @@ -1887,8 +2025,8 @@ badaddr: if (tTd(24, 6)) { - dprintf("buildaddr => "); - printaddr(a, FALSE); + sm_dprintf("buildaddr => "); + printaddr(a, false); } return a; } @@ -1901,7 +2039,7 @@ badaddr: ** use entire pvp. ** buf -- buffer to build the string into. ** sz -- size of buf. -** spacesub -- the space separator character; if null, +** spacesub -- the space separator character; if '\0', ** use SpaceSub. ** ** Returns: @@ -1919,8 +2057,8 @@ cataddr(pvp, evp, buf, sz, spacesub) register int sz; int spacesub; { - bool oatomtok = FALSE; - bool natomtok = FALSE; + bool oatomtok = false; + bool natomtok = false; register int i; register char *p; @@ -1937,15 +2075,17 @@ cataddr(pvp, evp, buf, sz, spacesub) } p = buf; sz -= 2; - while (*pvp != NULL && (i = strlen(*pvp)) < sz - 1) + while (*pvp != NULL && sz > 0) { natomtok = (TokTypeTab[**pvp & 0xff] == ATM); if (oatomtok && natomtok) { *p++ = spacesub; - --sz; + if (--sz <= 0) + break; } - (void) strlcpy(p, *pvp, sz); + if ((i = sm_strlcpy(p, *pvp, sz)) >= sz) + break; oatomtok = natomtok; p += i; sz -= i; @@ -1964,8 +2104,8 @@ cataddr(pvp, evp, buf, sz, spacesub) ** a, b -- pointers to the internal forms to compare. ** ** Returns: -** TRUE -- they represent the same mailbox. -** FALSE -- they don't. +** true -- they represent the same mailbox. +** false -- they don't. ** ** Side Effects: ** none. @@ -1980,11 +2120,11 @@ sameaddr(a, b) /* if they don't have the same mailer, forget it */ if (a->q_mailer != b->q_mailer) - return FALSE; + return false; /* if the user isn't the same, we can drop out */ if (strcmp(a->q_user, b->q_user) != 0) - return FALSE; + return false; /* if we have good uids for both but they differ, these are different */ if (a->q_mailer == ProgMailer) @@ -1994,24 +2134,24 @@ sameaddr(a, b) if (ca != NULL && cb != NULL && bitset(QGOODUID, ca->q_flags & cb->q_flags) && ca->q_uid != cb->q_uid) - return FALSE; + return false; } /* otherwise compare hosts (but be careful for NULL ptrs) */ if (a->q_host == b->q_host) { /* probably both null pointers */ - return TRUE; + return true; } if (a->q_host == NULL || b->q_host == NULL) { /* only one is a null pointer */ - return FALSE; + return false; } if (strcmp(a->q_host, b->q_host) != 0) - return FALSE; + return false; - return TRUE; + return true; } /* ** PRINTADDR -- print address (for debugging) @@ -2029,8 +2169,8 @@ sameaddr(a, b) struct qflags { - char *qf_name; - u_long qf_bit; + char *qf_name; + unsigned long qf_bit; }; static struct qflags AddressFlags[] = @@ -2066,14 +2206,14 @@ printaddr(a, follow) if (a == NULL) { - printf("[NULL]\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "[NULL]\n"); return; } while (a != NULL) { - printf("%lx=", (u_long) a); - (void) fflush(stdout); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%p=", a); + (void) sm_io_flush(smioout, SM_TIME_DEFAULT); /* find the mailer -- carefully */ m = a->q_mailer; @@ -2084,104 +2224,133 @@ printaddr(a, follow) m->m_name = "NULL"; } - printf("%s:\n\tmailer %d (%s), host `%s'\n", - a->q_paddr == NULL ? "<null>" : a->q_paddr, - m->m_mno, m->m_name, - a->q_host == NULL ? "<null>" : a->q_host); - printf("\tuser `%s', ruser `%s'\n", - a->q_user, - a->q_ruser == NULL ? "<null>" : a->q_ruser); - printf("\tstate="); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "%s:\n\tmailer %d (%s), host `%s'\n", + a->q_paddr == NULL ? "<null>" : a->q_paddr, + m->m_mno, m->m_name, + a->q_host == NULL ? "<null>" : a->q_host); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "\tuser `%s', ruser `%s'\n", + a->q_user, + a->q_ruser == NULL ? "<null>" : a->q_ruser); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\tstate="); switch (a->q_state) { case QS_OK: - printf("OK"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK"); break; case QS_DONTSEND: - printf("DONTSEND"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "DONTSEND"); break; case QS_BADADDR: - printf("BADADDR"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "BADADDR"); break; case QS_QUEUEUP: - printf("QUEUEUP"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "QUEUEUP"); + break; + + case QS_RETRY: + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "RETRY"); break; case QS_SENT: - printf("SENT"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "SENT"); break; case QS_VERIFIED: - printf("VERIFIED"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "VERIFIED"); break; case QS_EXPANDED: - printf("EXPANDED"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "EXPANDED"); break; case QS_SENDER: - printf("SENDER"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "SENDER"); break; case QS_CLONED: - printf("CLONED"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "CLONED"); break; case QS_DISCARDED: - printf("DISCARDED"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "DISCARDED"); break; case QS_REPLACED: - printf("REPLACED"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "REPLACED"); break; case QS_REMOVED: - printf("REMOVED"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "REMOVED"); break; case QS_DUPLICATE: - printf("DUPLICATE"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "DUPLICATE"); break; case QS_INCLUDED: - printf("INCLUDED"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "INCLUDED"); break; default: - printf("%d", a->q_state); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "%d", a->q_state); break; } - printf(", next=%lx, alias %lx, uid %d, gid %d\n", - (u_long) a->q_next, (u_long) a->q_alias, - (int) a->q_uid, (int) a->q_gid); - printf("\tflags=%lx<", a->q_flags); - firstone = TRUE; + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + ", next=%p, alias %p, uid %d, gid %d\n", + a->q_next, a->q_alias, + (int) a->q_uid, (int) a->q_gid); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\tflags=%lx<", + a->q_flags); + firstone = true; for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++) { if (!bitset(qfp->qf_bit, a->q_flags)) continue; if (!firstone) - printf(","); - firstone = FALSE; - printf("%s", qfp->qf_name); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + ","); + firstone = false; + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s", + qfp->qf_name); } - printf(">\n"); - printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n", - a->q_owner == NULL ? "(none)" : a->q_owner, - a->q_home == NULL ? "(none)" : a->q_home, - a->q_fullname == NULL ? "(none)" : a->q_fullname); - printf("\torcpt=\"%s\", statmta=%s, status=%s\n", - a->q_orcpt == NULL ? "(none)" : a->q_orcpt, - a->q_statmta == NULL ? "(none)" : a->q_statmta, - a->q_status == NULL ? "(none)" : a->q_status); - printf("\trstatus=\"%s\"\n", - a->q_rstatus == NULL ? "(none)" : a->q_rstatus); - printf("\tspecificity=%d, statdate=%s\n", - a->q_specificity, - a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate)); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, ">\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "\towner=%s, home=\"%s\", fullname=\"%s\"\n", + a->q_owner == NULL ? "(none)" : a->q_owner, + a->q_home == NULL ? "(none)" : a->q_home, + a->q_fullname == NULL ? "(none)" : a->q_fullname); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "\torcpt=\"%s\", statmta=%s, status=%s\n", + a->q_orcpt == NULL ? "(none)" : a->q_orcpt, + a->q_statmta == NULL ? "(none)" : a->q_statmta, + a->q_status == NULL ? "(none)" : a->q_status); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "\tfinalrcpt=\"%s\"\n", + a->q_finalrcpt == NULL ? "(none)" : a->q_finalrcpt); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "\trstatus=\"%s\"\n", + a->q_rstatus == NULL ? "(none)" : a->q_rstatus); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "\tstatdate=%s\n", + a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate)); if (!follow) return; @@ -2189,15 +2358,15 @@ printaddr(a, follow) } } /* -** EMPTYADDR -- return TRUE if this address is empty (``<>'') +** EMPTYADDR -- return true if this address is empty (``<>'') ** ** Parameters: ** a -- pointer to the address ** ** Returns: -** TRUE -- if this address is "empty" (i.e., no one should +** true -- if this address is "empty" (i.e., no one should ** ever generate replies to it. -** FALSE -- if it is a "regular" (read: replyable) address. +** false -- if it is a "regular" (read: replyable) address. */ bool @@ -2239,44 +2408,36 @@ remotename(name, m, flags, pstat, e) register ENVELOPE *e; { register char **pvp; - char *fancy; - char *oldg = macvalue('g', e); + char *SM_NONVOLATILE fancy; + char *oldg; int rwset; static char buf[MAXNAME + 1]; char lbuf[MAXNAME + 1]; char pvpbuf[PSBUFSIZE]; -#if _FFR_ADDR_TYPE char addrtype[4]; -#endif /* _FFR_ADDR_TYPE */ if (tTd(12, 1)) - dprintf("remotename(%s)\n", name); + sm_dprintf("remotename(%s)\n", name); /* don't do anything if we are tagging it as special */ if (bitset(RF_SENDERADDR, flags)) { rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset : m->m_se_rwset; -#if _FFR_ADDR_TYPE addrtype[2] = 's'; -#endif /* _FFR_ADDR_TYPE */ } else { rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset : m->m_re_rwset; -#if _FFR_ADDR_TYPE addrtype[2] = 'r'; -#endif /* _FFR_ADDR_TYPE */ } if (rwset < 0) return name; -#if _FFR_ADDR_TYPE addrtype[1] = ' '; addrtype[3] = '\0'; addrtype[0] = bitset(RF_HEADERADDR, flags) ? 'h' : 'e'; - define(macid("{addr_type}", NULL), addrtype, e); -#endif /* _FFR_ADDR_TYPE */ + macdefine(&e->e_macro, A_TEMP, macid("{addr_type}"), addrtype); /* ** Do a heuristic crack of this name to extract any comment info. @@ -2299,7 +2460,7 @@ remotename(name, m, flags, pstat, e) pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL); if (pvp == NULL) return name; - if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL) + if (REWRITE(pvp, 3, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL) { @@ -2328,7 +2489,7 @@ remotename(name, m, flags, pstat, e) break; } } - if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL) + if (REWRITE(pvp, 3, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; } } @@ -2342,17 +2503,17 @@ remotename(name, m, flags, pstat, e) if (bitset(RF_SENDERADDR, flags)) { - if (rewrite(pvp, 1, 0, e) == EX_TEMPFAIL) + if (REWRITE(pvp, 1, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; } else { - if (rewrite(pvp, 2, 0, e) == EX_TEMPFAIL) + if (REWRITE(pvp, 2, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; } if (rwset > 0) { - if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL) + if (REWRITE(pvp, rwset, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; } @@ -2363,7 +2524,7 @@ remotename(name, m, flags, pstat, e) ** may be used as a default to the above rules. */ - if (rewrite(pvp, 4, 0, e) == EX_TEMPFAIL) + if (REWRITE(pvp, 4, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; /* @@ -2371,18 +2532,21 @@ remotename(name, m, flags, pstat, e) */ cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0'); - define('g', lbuf, e); - - /* need to make sure route-addrs have <angle brackets> */ - if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@') - expand("<\201g>", buf, sizeof buf, e); - else - expand(fancy, buf, sizeof buf, e); + oldg = macget(&e->e_macro, 'g'); + macset(&e->e_macro, 'g', lbuf); - define('g', oldg, e); + SM_TRY + /* need to make sure route-addrs have <angle brackets> */ + if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@') + expand("<\201g>", buf, sizeof buf, e); + else + expand(fancy, buf, sizeof buf, e); + SM_FINALLY + macset(&e->e_macro, 'g', oldg); + SM_END_TRY if (tTd(12, 1)) - dprintf("remotename => `%s'\n", buf); + sm_dprintf("remotename => `%s'\n", buf); return buf; } /* @@ -2401,7 +2565,8 @@ remotename(name, m, flags, pstat, e) #define Q_COPYFLAGS (QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\ Q_PINGFLAGS|QHASNOTIFY|\ - QRELAYED|QEXPANDED|QDELIVERED|QDELAYED) + QRELAYED|QEXPANDED|QDELIVERED|QDELAYED|\ + QBYTRACE|QBYNDELAY|QBYNRELAY) void maplocaluser(a, sendq, aliaslevel, e) @@ -2417,29 +2582,27 @@ maplocaluser(a, sendq, aliaslevel, e) if (tTd(29, 1)) { - dprintf("maplocaluser: "); - printaddr(a, FALSE); + sm_dprintf("maplocaluser: "); + printaddr(a, false); } pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL); if (pvp == NULL) { if (tTd(29, 9)) - dprintf("maplocaluser: cannot prescan %s\n", + sm_dprintf("maplocaluser: cannot prescan %s\n", a->q_user); return; } - define('h', a->q_host, e); - define('u', a->q_user, e); - define('z', a->q_home, e); + macdefine(&e->e_macro, A_PERM, 'h', a->q_host); + macdefine(&e->e_macro, A_PERM, 'u', a->q_user); + macdefine(&e->e_macro, A_PERM, 'z', a->q_home); -#if _FFR_ADDR_TYPE - define(macid("{addr_type}", NULL), "e r", e); -#endif /* _FFR_ADDR_TYPE */ - if (rewrite(pvp, 5, 0, e) == EX_TEMPFAIL) + macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r"); + if (REWRITE(pvp, 5, e) == EX_TEMPFAIL) { if (tTd(29, 9)) - dprintf("maplocaluser: rewrite tempfail\n"); + sm_dprintf("maplocaluser: rewrite tempfail\n"); a->q_state = QS_QUEUEUP; a->q_status = "4.4.3"; return; @@ -2447,49 +2610,58 @@ maplocaluser(a, sendq, aliaslevel, e) if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) { if (tTd(29, 9)) - dprintf("maplocaluser: doesn't resolve\n"); + sm_dprintf("maplocaluser: doesn't resolve\n"); return; } + SM_TRY + a1 = buildaddr(pvp, NULL, 0, e); + SM_EXCEPT(exc, "E:mta.quickabort") + + /* + ** mark address as bad, S5 returned an error + ** and we gave that back to the SMTP client. + */ + + a->q_state = QS_DONTSEND; + sm_exc_raisenew_x(&EtypeQuickAbort, 2); + SM_END_TRY + /* if non-null, mailer destination specified -- has it changed? */ - a1 = buildaddr(pvp, NULL, 0, e); if (a1 == NULL || sameaddr(a, a1)) { if (tTd(29, 9)) - dprintf("maplocaluser: address unchanged\n"); - if (a1 != NULL) - sm_free(a1); + sm_dprintf("maplocaluser: address unchanged\n"); return; } /* make new address take on flags and print attributes of old */ a1->q_flags &= ~Q_COPYFLAGS; a1->q_flags |= a->q_flags & Q_COPYFLAGS; - a1->q_paddr = newstr(a->q_paddr); + a1->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_paddr); + a1->q_finalrcpt = a->q_finalrcpt; a1->q_orcpt = a->q_orcpt; /* mark old address as dead; insert new address */ a->q_state = QS_REPLACED; if (tTd(29, 5)) { - dprintf("maplocaluser: QS_REPLACED "); - printaddr(a, FALSE); + sm_dprintf("maplocaluser: QS_REPLACED "); + printaddr(a, false); } a1->q_alias = a; - allocaddr(a1, RF_COPYALL, newstr(a->q_paddr)); + allocaddr(a1, RF_COPYALL, sm_rpool_strdup_x(e->e_rpool, a->q_paddr), e); (void) recipient(a1, sendq, aliaslevel, e); } /* ** DEQUOTE_INIT -- initialize dequote map ** -** This is a no-op. -** ** Parameters: ** map -- the internal map structure. ** args -- arguments. ** ** Returns: -** TRUE. +** true. */ bool @@ -2530,7 +2702,7 @@ dequote_init(map, args) if (map->map_app != NULL) map->map_app = newstr(map->map_app); - return TRUE; + return true; } /* ** DEQUOTE_MAP -- unquote an address @@ -2562,15 +2734,15 @@ dequote_map(map, name, av, statp) int cmntcnt = 0; int quotecnt = 0; int spacecnt = 0; - bool quotemode = FALSE; - bool bslashmode = FALSE; + bool quotemode = false; + bool bslashmode = false; char spacesub = map->map_spacesub; for (p = q = name; (c = *p++) != '\0'; ) { if (bslashmode) { - bslashmode = FALSE; + bslashmode = false; *q++ = c; continue; } @@ -2581,7 +2753,7 @@ dequote_map(map, name, av, statp) switch (c) { case '\\': - bslashmode = TRUE; + bslashmode = true; break; case '(': @@ -2640,8 +2812,9 @@ dequote_map(map, name, av, statp) ** e -- the current envelope. ** rmcomm -- remove comments? ** cnt -- count rejections (statistics)? -** logl -- logging level +** logl -- logging level. ** host -- NULL or relay host. +** logid -- id for sm_syslog. ** ** Returns: ** EX_OK -- if the rwset doesn't resolve to $#error @@ -2649,7 +2822,7 @@ dequote_map(map, name, av, statp) */ int -rscheck(rwset, p1, p2, e, rmcomm, cnt, logl, host) +rscheck(rwset, p1, p2, e, rmcomm, cnt, logl, host, logid) char *rwset; char *p1; char *p2; @@ -2657,14 +2830,15 @@ rscheck(rwset, p1, p2, e, rmcomm, cnt, logl, host) bool rmcomm, cnt; int logl; char *host; + char *logid; { - char *buf; + char *volatile buf; int bufsize; int saveexitstat; - int rstat = EX_OK; + int volatile rstat = EX_OK; char **pvp; int rsno; - bool discard = FALSE; + bool volatile discard = false; auto ADDRESS a1; bool saveQuickAbort = QuickAbort; bool saveSuprErrs = SuprErrs; @@ -2673,7 +2847,7 @@ rscheck(rwset, p1, p2, e, rmcomm, cnt, logl, host) extern char MsgBuf[]; if (tTd(48, 2)) - dprintf("rscheck(%s, %s, %s)\n", rwset, p1, + sm_dprintf("rscheck(%s, %s, %s)\n", rwset, p1, p2 == NULL ? "(NULL)" : p2); rsno = strtorwset(rwset, NULL, ST_FIND); @@ -2684,130 +2858,227 @@ rscheck(rwset, p1, p2, e, rmcomm, cnt, logl, host) { bufsize = strlen(p1) + strlen(p2) + 2; if (bufsize > sizeof buf0) - buf = xalloc(bufsize); + buf = sm_malloc_x(bufsize); else { buf = buf0; bufsize = sizeof buf0; } - (void) snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2); + (void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2); } else { bufsize = strlen(p1) + 1; if (bufsize > sizeof buf0) - buf = xalloc(bufsize); + buf = sm_malloc_x(bufsize); else { buf = buf0; bufsize = sizeof buf0; } - (void) snprintf(buf, bufsize, "%s", p1); + (void) sm_strlcpy(buf, p1, bufsize); } - SuprErrs = TRUE; - QuickAbort = FALSE; - pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL, - rmcomm ? NULL : TokTypeNoC); - SuprErrs = saveSuprErrs; - if (pvp == NULL) + SM_TRY { - if (tTd(48, 2)) - dprintf("rscheck: cannot prescan input\n"); -/* - syserr("rscheck: cannot prescan input: \"%s\"", - shortenstring(buf, MAXSHORTSTR)); - rstat = EX_DATAERR; -*/ - goto finis; - } + SuprErrs = true; + QuickAbort = false; + pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL, + rmcomm ? NULL : TokTypeNoC); + SuprErrs = saveSuprErrs; + if (pvp == NULL) + { + if (tTd(48, 2)) + sm_dprintf("rscheck: cannot prescan input\n"); + /* + syserr("rscheck: cannot prescan input: \"%s\"", + shortenstring(buf, MAXSHORTSTR)); + rstat = EX_DATAERR; + */ + goto finis; + } + (void) REWRITE(pvp, rsno, e); + if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET || + pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 && + strcmp(pvp[1], "discard") != 0)) + { + goto finis; + } - MapOpenErr = FALSE; - (void) rewrite(pvp, rsno, 0, e); - if (MapOpenErr) - { - usrerrenh("4.3.0", "451 Temporary failure"); - rstat = EX_TEMPFAIL; - goto finis; - } + if (strcmp(pvp[1], "discard") == 0) + { + if (tTd(48, 2)) + sm_dprintf("rscheck: discard mailer selected\n"); + e->e_flags |= EF_DISCARD; + discard = true; + } + else + { + int savelogusrerrs = LogUsrErrs; + static bool logged = false; + + /* got an error -- process it */ + saveexitstat = ExitStat; + LogUsrErrs = false; + (void) buildaddr(pvp, &a1, 0, e); + LogUsrErrs = savelogusrerrs; + rstat = ExitStat; + ExitStat = saveexitstat; + if (!logged) + { + if (cnt) + markstats(e, &a1, true); + logged = true; + } + } - if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET || - pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 && - strcmp(pvp[1], "discard") != 0)) + if (LogLevel > logl) + { + char *relay; + char *p; + char lbuf[MAXLINE]; + + p = lbuf; + if (p2 != NULL) + { + (void) sm_snprintf(p, SPACELEFT(lbuf, p), + ", arg2=%s", + p2); + p += strlen(p); + } + + if (host != NULL) + relay = host; + else + relay = macvalue('_', e); + if (relay != NULL) + { + (void) sm_snprintf(p, SPACELEFT(lbuf, p), + ", relay=%s", relay); + p += strlen(p); + } + *p = '\0'; + if (discard) + sm_syslog(LOG_NOTICE, logid, + "ruleset=%s, arg1=%s%s, discard", + rwset, p1, lbuf); + else + sm_syslog(LOG_NOTICE, logid, + "ruleset=%s, arg1=%s%s, reject=%s", + rwset, p1, lbuf, MsgBuf); + } + + finis: ; + } + SM_FINALLY { - goto finis; + /* clean up */ + if (buf != buf0) + sm_free(buf); + QuickAbort = saveQuickAbort; } + SM_END_TRY + + setstat(rstat); + if (rstat != EX_OK && QuickAbort) + sm_exc_raisenew_x(&EtypeQuickAbort, 2); + return rstat; +} +/* +** RSCAP -- call rewriting set to return capabilities +** +** Parameters: +** rwset -- the rewriting set to use. +** p1 -- the first string to check. +** p2 -- the second string to check -- may be null. +** e -- the current envelope. +** pvp -- pointer to token vector. +** pvpbuf -- buffer space. +** +** Returns: +** EX_UNAVAILABLE -- ruleset doesn't exist. +** EX_DATAERR -- prescan() failed. +** EX_OK -- rewrite() was successful. +** else -- return status from rewrite(). +*/ + +int +rscap(rwset, p1, p2, e, pvp, pvpbuf, size) + char *rwset; + char *p1; + char *p2; + ENVELOPE *e; + char ***pvp; + char *pvpbuf; + int size; +{ + char *volatile buf; + int bufsize; + int volatile rstat = EX_OK; + int rsno; + bool saveQuickAbort = QuickAbort; + bool saveSuprErrs = SuprErrs; + char buf0[MAXLINE]; + extern char MsgBuf[]; + + if (tTd(48, 2)) + sm_dprintf("rscap(%s, %s, %s)\n", rwset, p1, + p2 == NULL ? "(NULL)" : p2); + + if (pvp != NULL) + *pvp = NULL; + rsno = strtorwset(rwset, NULL, ST_FIND); + if (rsno < 0) + return EX_UNAVAILABLE; - if (strcmp(pvp[1], "discard") == 0) + if (p2 != NULL) { - if (tTd(48, 2)) - dprintf("rscheck: discard mailer selected\n"); - e->e_flags |= EF_DISCARD; - discard = TRUE; + bufsize = strlen(p1) + strlen(p2) + 2; + if (bufsize > sizeof buf0) + buf = sm_malloc_x(bufsize); + else + { + buf = buf0; + bufsize = sizeof buf0; + } + (void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2); } else { - int savelogusrerrs = LogUsrErrs; - static bool logged = FALSE; - - /* got an error -- process it */ - saveexitstat = ExitStat; - LogUsrErrs = FALSE; - (void) buildaddr(pvp, &a1, 0, e); - LogUsrErrs = savelogusrerrs; - rstat = ExitStat; - ExitStat = saveexitstat; - if (!logged) + bufsize = strlen(p1) + 1; + if (bufsize > sizeof buf0) + buf = sm_malloc_x(bufsize); + else { - if (cnt) - markstats(e, &a1, TRUE); - logged = TRUE; + buf = buf0; + bufsize = sizeof buf0; } + (void) sm_strlcpy(buf, p1, bufsize); } - - if (LogLevel >= logl) + SM_TRY { - char *relay; - char *p; - char lbuf[MAXLINE]; - - p = lbuf; - if (p2 != NULL) - { - snprintf(p, SPACELEFT(lbuf, p), - ", arg2=%s", - p2); - p += strlen(p); - } - - if (host != NULL) - relay = host; + SuprErrs = true; + QuickAbort = false; + *pvp = prescan(buf, '\0', pvpbuf, size, NULL, NULL); + if (*pvp != NULL) + rstat = REWRITE(*pvp, rsno, e); else - relay = macvalue('_', e); - if (relay != NULL) { - snprintf(p, SPACELEFT(lbuf, p), - ", relay=%s", relay); - p += strlen(p); + if (tTd(48, 2)) + sm_dprintf("rscap: cannot prescan input\n"); + rstat = EX_DATAERR; } - *p = '\0'; - if (discard) - sm_syslog(LOG_NOTICE, e->e_id, - "ruleset=%s, arg1=%s%s, discard", - rwset, p1, lbuf); - else - sm_syslog(LOG_NOTICE, e->e_id, - "ruleset=%s, arg1=%s%s, reject=%s", - rwset, p1, lbuf, MsgBuf); } + SM_FINALLY + { + /* clean up */ + if (buf != buf0) + sm_free(buf); + SuprErrs = saveSuprErrs; + QuickAbort = saveQuickAbort; - finis: - /* clean up */ - QuickAbort = saveQuickAbort; - setstat(rstat); - if (buf != buf0) - sm_free(buf); - - if (rstat != EX_OK && QuickAbort) - longjmp(TopFrame, 2); + /* prevent information leak, this may contain rewrite error */ + MsgBuf[0] = '\0'; + } + SM_END_TRY return rstat; } diff --git a/gnu/usr.sbin/sendmail/sendmail/queue.c b/gnu/usr.sbin/sendmail/sendmail/queue.c index 23ec59c3bac..25ede72069d 100644 --- a/gnu/usr.sbin/sendmail/sendmail/queue.c +++ b/gnu/usr.sbin/sendmail/sendmail/queue.c @@ -11,28 +11,38 @@ * */ - #include <sendmail.h> -#ifndef lint -# if QUEUE -static char id[] = "@(#)$Sendmail: queue.c,v 8.343.4.62 2001/07/20 00:53:01 gshapiro Exp $ (with queueing)"; -# else /* QUEUE */ -static char id[] = "@(#)$Sendmail: queue.c,v 8.343.4.62 2001/07/20 00:53:01 gshapiro Exp $ (without queueing)"; -# endif /* QUEUE */ -#endif /* ! lint */ +SM_RCSID("@(#)$Sendmail: queue.c,v 8.768 2001/09/08 01:21:09 gshapiro Exp $") + +#include <dirent.h> + +#if SM_CONF_SHM +# include <sm/shm.h> +#endif /* SM_CONF_SHM */ + +# define RELEASE_QUEUE (void) 0 +# define ST_INODE(st) (st).st_ino -# include <dirent.h> -#if QUEUE +/* +** Historical notes: +** QF_VERSION==4 was sendmail 8.10/8.11 without _FFR_QUEUEDELAY +** QF_VERSION==5 was sendmail 8.10/8.11 with _FFR_QUEUEDELAY +*/ -# if _FFR_QUEUEDELAY -# define QF_VERSION 5 /* version number of this queue format */ +#if _FFR_QUEUEDELAY +# define QF_VERSION 7 /* version number of this queue format */ static time_t queuedelay __P((ENVELOPE *)); -# else /* _FFR_QUEUEDELAY */ -# define QF_VERSION 4 /* version number of this queue format */ -# define queuedelay(e) MinQueueAge -# endif /* _FFR_QUEUEDELAY */ +#define queuedelay_qfver_unsupported(qfver) false +#else /* _FFR_QUEUEDELAY */ +# define QF_VERSION 6 /* version number of this queue format */ +# define queuedelay(e) MinQueueAge +#define queuedelay_qfver_unsupported(qfver) ((qfver) == 5 || (qfver) == 7) +#endif /* _FFR_QUEUEDELAY */ + + +/* Naming convention: qgrp: index of queue group, qg: QUEUEGROUP */ /* ** Work queue. @@ -45,28 +55,215 @@ struct work bool w_lock; /* is message locked? */ bool w_tooyoung; /* is it too young to run? */ long w_pri; /* priority of message, see below */ - time_t w_ctime; /* creation time of message */ + time_t w_ctime; /* creation time */ + time_t w_mtime; /* modification time */ + int w_qgrp; /* queue group located in */ + int w_qdir; /* queue directory located in */ struct work *w_next; /* next in queue */ }; typedef struct work WORK; -static WORK *WorkQ; /* queue of things to be done */ +static WORK *WorkQ; /* queue of things to be done */ +static int NumWorkGroups; /* number of work groups */ + +/* +** use of DoQueueRun: +** NumQueue: indicates that a queue run is needed, look at individual bits +** 0 - NumQueue-1: indicates that a queue run for this queue group +** is needed. +*/ + +static BITMAP256 volatile DoQueueRun; /* non-interrupt time queue run needed */ + +/* +** Work group definition structure. +** Each work group contains one or more queue groups. This is done +** to manage the number of queue group runners active at the same time +** to be within the constraints of MaxQueueChildren (if it is set). +** The number of queue groups that can be run on the next work run +** is kept track of. The queue groups are run in a round robin. +*/ + +struct workgrp +{ + int wg_numqgrp; /* number of queue groups in work grp */ + int wg_runners; /* total runners */ + int wg_curqgrp; /* current queue group */ + QUEUEGRP **wg_qgs; /* array of queue groups */ + int wg_maxact; /* max # of active runners */ + time_t wg_lowqintvl; /* lowest queue interval */ + int wg_restart; /* needs restarting? */ + int wg_restartcnt; /* count of times restarted */ +}; + +typedef struct workgrp WORKGRP; + +static WORKGRP volatile WorkGrp[MAXWORKGROUPS + 1]; /* work groups */ + +#if SM_HEAP_CHECK +static SM_DEBUG_T DebugLeakQ = SM_DEBUG_INITIALIZER("leak_q", + "@(#)$Debug: leak_q - trace memory leaks during queue processing $"); +#endif /* SM_HEAP_CHECK */ + +/* +** We use EmptyString instead of "" to avoid +** 'zero-length format string' warnings from gcc +*/ -static void grow_wlist __P((int)); -static int orderq __P((int, bool)); -static void printctladdr __P((ADDRESS *, FILE *)); -static int print_single_queue __P((int)); +static const char EmptyString[] = ""; + +static void grow_wlist __P((int, int)); +static int multiqueue_cache __P((char *, int, QUEUEGRP *, int, unsigned int *)); +static int gatherq __P((int, int, bool, bool *, bool *)); +static int sortq __P((int)); +static void printctladdr __P((ADDRESS *, SM_FILE_T *)); +static int print_single_queue __P((int, int)); static bool readqf __P((ENVELOPE *)); -static void runqueueevent __P((void)); -static int run_single_queue __P((int, bool, bool)); +static void restart_work_group __P((int)); +static void runner_work __P((ENVELOPE *, int, bool, int, int)); +static void schedule_queue_runs __P((bool, int)); static char *strrev __P((char *)); -static ADDRESS *setctluser __P((char *, int)); +static ADDRESS *setctluser __P((char *, int, ENVELOPE *)); +#if _FFR_RHS +static int sm_strshufflecmp __P((char *, char *)); +static void init_shuffle_alphabet __P(()); +#endif /* _FFR_RHS */ static int workcmpf0(); static int workcmpf1(); static int workcmpf2(); static int workcmpf3(); static int workcmpf4(); +static int workcmpf5(); +static int workcmpf6(); +#if _FFR_RHS +static int workcmpf7(); +#endif /* _FFR_RHS */ + +#if RANDOMSHIFT +# define get_rand_mod(m) ((get_random() >> RANDOMSHIFT) % (m)) +#else /* RANDOMSHIFT */ +# define get_rand_mod(m) (get_random() % (m)) +#endif /* RANDOMSHIFT */ + +/* +** File system definition. +** Used to keep track of how much free space is available +** on a file system in which one or more queue directories reside. +*/ + +typedef struct filesys_shared FILESYS; + +struct filesys_shared +{ + dev_t fs_dev; /* unique device id */ + long fs_avail; /* number of free blocks available */ + long fs_blksize; /* block size, in bytes */ +}; + +/* probably kept in shared memory */ +static FILESYS FileSys[MAXFILESYS]; /* queue file systems */ +static char *FSPath[MAXFILESYS]; /* pathnames for file systems */ + +#if SM_CONF_SHM + +/* +** Shared memory data +** +** Current layout: +** size -- size of shared memory segment +** pid -- pid of owner, should be a unique id to avoid misinterpretations +** by other processes. +** tag -- should be a unique id to avoid misinterpretations by others. +** idea: hash over configuration data that will be stored here. +** NumFileSys -- number of file systems. +** FileSys -- (arrary of) structure for used file systems. +** RSATmpCnt -- counter for number of uses of ephemeral RSA key. +** QShm -- (array of) structure for information about queue directories. +*/ + +/* +** Queue data in shared memory +*/ + +typedef struct queue_shared QUEUE_SHM_T; + +struct queue_shared +{ + int qs_entries; /* number of entries */ + /* XXX more to follow? */ +}; + +static void *Pshm; /* pointer to shared memory */ +static FILESYS *PtrFileSys; /* pointer to queue file system array */ +int ShmId = SM_SHM_NO_ID; /* shared memory id */ +static QUEUE_SHM_T *QShm; /* pointer to shared queue data */ + +# define SHM_OFF_PID(p) (((char *) (p)) + sizeof(int)) +# define SHM_OFF_TAG(p) (((char *) (p)) + sizeof(pid_t) + sizeof(int)) +# define SHM_OFF_HEAD (sizeof(pid_t) + sizeof(int) * 2) + +/* how to access FileSys */ +# define FILE_SYS(i) (PtrFileSys[i]) + +/* first entry is a tag, for now just the size */ +# define OFF_FILE_SYS(p) (((char *) (p)) + SHM_OFF_HEAD) + +/* offset for PNumFileSys */ +# define OFF_NUM_FILE_SYS(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys)) + +/* offset for PRSATmpCnt */ +# define OFF_RSA_TMP_CNT(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int)) +int *PRSATmpCnt; + +/* offset for queue_shm */ +# define OFF_QUEUE_SHM(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2) + +#define QSHM_ENTRIES(i) QShm[i].qs_entries + +/* basic size of shared memory segment */ +# define SM_T_SIZE (SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2) + +static unsigned int hash_q __P((char *, unsigned int)); + +/* +** HASH_Q -- simple hash function +** +** Parameters: +** p -- string to hash. +** h -- hash start value (from previous run). +** +** Returns: +** hash value. +*/ + +static unsigned int +hash_q(p, h) + char *p; + unsigned int h; +{ + int c, d; + + while (*p != '\0') + { + d = *p++; + c = d; + c ^= c<<6; + h += (c<<11) ^ (c>>1); + h ^= (d<<14) + (d<<7) + (d<<4) + d; + } + return h; +} + +#else /* SM_CONF_SHM */ +# define FILE_SYS(i) FileSys[i] +#endif /* SM_CONF_SHM */ + +/* access to the various components of file system data */ +#define FILE_SYS_NAME(i) FSPath[i] +#define FILE_SYS_AVAIL(i) FILE_SYS(i).fs_avail +#define FILE_SYS_BLKSIZE(i) FILE_SYS(i).fs_blksize +#define FILE_SYS_DEV(i) FILE_SYS(i).fs_dev /* ** Current qf file field assignments: @@ -75,9 +272,10 @@ static int workcmpf4(); ** B body type ** C controlling user ** D data file name +** d data file directory name (added in 8.12) ** E error recipient ** F flag bits -** G queue delay algorithm +** G queue delay algorithm (_FFR_QUEUEDELAY) ** H header ** I data file's inode number ** K time of last delivery attempt @@ -86,13 +284,15 @@ static int workcmpf4(); ** N number of delivery attempts ** P message priority ** Q original recipient (ORCPT=) +** r final recipient (Final-Recipient: DSN field) ** R recipient ** S sender ** T init time ** V queue file version -** X character set (_FFR_SAVE_CHARSET) -** Y current delay +** X free (was: character set if _FFR_SAVE_CHARSET) +** Y current delay (_FFR_QUEUEDELAY) ** Z original envelope id from ESMTP +** ! deliver by (added in 8.12) ** $ define macro ** . terminate file */ @@ -102,13 +302,14 @@ static int workcmpf4(); ** ** Parameters: ** e -- the envelope to queue up. -** announce -- if TRUE, tell when you are queueing up. +** announce -- if true, tell when you are queueing up. +** msync -- if true, then fsync() if SuperSafe interactive mode. ** ** Returns: ** none. ** ** Side Effects: -** The current request are saved in a control file. +** The current request is saved in a control file. ** The queue file is left locked. */ @@ -116,12 +317,13 @@ static int workcmpf4(); # define LOSEQF_LETTER 'Q' void -queueup(e, announce) +queueup(e, announce, msync) register ENVELOPE *e; bool announce; + bool msync; { char *qf; - register FILE *tfp; + register SM_FILE_T *tfp; register HDR *h; register ADDRESS *q; int tfd = -1; @@ -131,6 +333,7 @@ queueup(e, announce) MAILER nullmailer; MCI mcibuf; char tf[MAXPATHLEN]; + char dfname[MAXPATHLEN]; char buf[MAXLINE]; /* @@ -138,36 +341,28 @@ queueup(e, announce) */ newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags); - - /* if newid, queuename will create a locked qf file in e->lockfp */ - (void) strlcpy(tf, queuename(e, 't'), sizeof tf); + (void) sm_strlcpy(tf, queuename(e, 't'), sizeof tf); tfp = e->e_lockfp; if (tfp == NULL) - newid = FALSE; + newid = false; /* if newid, just write the qf file directly (instead of tf file) */ if (!newid) { - int flags; - - flags = O_CREAT|O_WRONLY|O_EXCL; + const int flags = O_CREAT|O_WRONLY|O_EXCL; /* get a locked tf file */ for (i = 0; i < 128; i++) { if (tfd < 0) { -#if _FFR_QUEUE_FILE_MODE - MODE_T oldumask; + MODE_T oldumask = 0; if (bitset(S_IWGRP, QueueFileMode)) oldumask = umask(002); tfd = open(tf, flags, QueueFileMode); if (bitset(S_IWGRP, QueueFileMode)) (void) umask(oldumask); -#else /* _FFR_QUEUE_FILE_MODE */ - tfd = open(tf, flags, FileMode); -#endif /* _FFR_QUEUE_FILE_MODE */ if (tfd < 0) { @@ -176,7 +371,8 @@ queueup(e, announce) if (LogLevel > 0 && (i % 32) == 0) sm_syslog(LOG_ALERT, e->e_id, "queueup: cannot create %s, uid=%d: %s", - tf, geteuid(), errstring(errno)); + tf, geteuid(), + sm_errstring(errno)); } } if (tfd >= 0) @@ -186,7 +382,7 @@ queueup(e, announce) else if (LogLevel > 0 && (i % 32) == 0) sm_syslog(LOG_ALERT, e->e_id, "queueup: cannot lock %s: %s", - tf, errstring(errno)); + tf, sm_errstring(errno)); if ((i % 32) == 31) { (void) close(tfd); @@ -202,11 +398,13 @@ queueup(e, announce) else (void) sleep(i % 32); } - if (tfd < 0 || (tfp = fdopen(tfd, "w")) == NULL) + if (tfd < 0 || (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, + (void *) tfd, SM_IO_WRONLY, + NULL)) == NULL) { int save_errno = errno; - printopenfds(TRUE); + printopenfds(true); errno = save_errno; syserr("!queueup: cannot create queue temp file %s, uid=%d", tf, geteuid()); @@ -214,66 +412,83 @@ queueup(e, announce) } if (tTd(40, 1)) - dprintf("\n>>>>> queueing %s/qf%s%s >>>>>\n", - qid_printqueue(e->e_queuedir), e->e_id, + sm_dprintf("\n>>>>> queueing %s/qf%s%s >>>>>\n", + qid_printqueue(e->e_qgrp, e->e_qdir), e->e_id, newid ? " (new id)" : ""); if (tTd(40, 3)) { - dprintf(" e_flags="); + sm_dprintf(" e_flags="); printenvflags(e); } if (tTd(40, 32)) { - dprintf(" sendq="); - printaddr(e->e_sendqueue, TRUE); + sm_dprintf(" sendq="); + printaddr(e->e_sendqueue, true); } if (tTd(40, 9)) { - dprintf(" tfp="); - dumpfd(fileno(tfp), TRUE, FALSE); - dprintf(" lockfp="); + sm_dprintf(" tfp="); + dumpfd(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL), true, false); + sm_dprintf(" lockfp="); if (e->e_lockfp == NULL) - dprintf("NULL\n"); + sm_dprintf("NULL\n"); else - dumpfd(fileno(e->e_lockfp), TRUE, FALSE); + dumpfd(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL), + true, false); } /* ** If there is no data file yet, create one. */ + (void) sm_strlcpy(dfname, queuename(e, 'd'), sizeof dfname); if (bitset(EF_HAS_DF, e->e_flags)) { - if (e->e_dfp != NULL && bfcommit(e->e_dfp) < 0) + if (e->e_dfp != NULL && + SuperSafe != SAFE_REALLY && + sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 && + errno != EINVAL) syserr("!queueup: cannot commit data file %s, uid=%d", queuename(e, 'd'), geteuid()); + + if (e->e_dfp != NULL && + SuperSafe == SAFE_INTERACTIVE && msync) + { + if (tTd(40,32)) + sm_syslog(LOG_INFO, e->e_id, + "queueup: fsync(e->e_dfp)"); + + if (fsync(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, + NULL)) < 0) + { + if (newid) + syserr("!552 Error writing data file %s", + dfname); + else + syserr("!452 Error writing data file %s", + dfname); + } + } } else { int dfd; - register FILE *dfp = NULL; - char dfname[MAXPATHLEN]; + MODE_T oldumask = 0; + register SM_FILE_T *dfp = NULL; struct stat stbuf; - if (e->e_dfp != NULL && bftest(e->e_dfp)) + if (e->e_dfp != NULL && + sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE)) syserr("committing over bf file"); - (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname); -#if _FFR_QUEUE_FILE_MODE - { - MODE_T oldumask; - - if (bitset(S_IWGRP, QueueFileMode)) - oldumask = umask(002); - dfd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, - QueueFileMode); - if (bitset(S_IWGRP, QueueFileMode)) - (void) umask(oldumask); - } -#else /* _FFR_QUEUE_FILE_MODE */ - dfd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode); -#endif /* _FFR_QUEUE_FILE_MODE */ - if (dfd < 0 || (dfp = fdopen(dfd, "w")) == NULL) + if (bitset(S_IWGRP, QueueFileMode)) + oldumask = umask(002); + dfd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, QueueFileMode); + if (bitset(S_IWGRP, QueueFileMode)) + (void) umask(oldumask); + if (dfd < 0 || (dfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, + (void *) dfd, SM_IO_WRONLY, + NULL)) == NULL) syserr("!queueup: cannot create data temp file %s, uid=%d", dfname, geteuid()); if (fstat(dfd, &stbuf) < 0) @@ -281,14 +496,33 @@ queueup(e, announce) else { e->e_dfdev = stbuf.st_dev; - e->e_dfino = stbuf.st_ino; + e->e_dfino = ST_INODE(stbuf); } e->e_flags |= EF_HAS_DF; memset(&mcibuf, '\0', sizeof mcibuf); mcibuf.mci_out = dfp; mcibuf.mci_mailer = FileMailer; (*e->e_putbody)(&mcibuf, e, NULL); - if (fclose(dfp) < 0) + + if (SuperSafe == SAFE_REALLY || + (SuperSafe == SAFE_INTERACTIVE && msync)) + { + if (tTd(40,32)) + sm_syslog(LOG_INFO, e->e_id, + "queueup: fsync(dfp)"); + + if (fsync(sm_io_getinfo(dfp, SM_IO_WHAT_FD, NULL)) < 0) + { + if (newid) + syserr("!552 Error writing data file %s", + dfname); + else + syserr("!452 Error writing data file %s", + dfname); + } + } + + if (sm_io_close(dfp, SM_TIME_DEFAULT) < 0) syserr("!queueup: cannot save data temp file %s, uid=%d", dfname, geteuid()); e->e_putbody = putbody; @@ -301,59 +535,60 @@ queueup(e, announce) */ /* output queue version number (must be first!) */ - fprintf(tfp, "V%d\n", QF_VERSION); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "V%d\n", QF_VERSION); /* output creation time */ - fprintf(tfp, "T%ld\n", (long) e->e_ctime); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "T%ld\n", (long) e->e_ctime); /* output last delivery time */ -# if _FFR_QUEUEDELAY - fprintf(tfp, "K%ld\n", (long) e->e_dtime); - fprintf(tfp, "G%d\n", e->e_queuealg); - fprintf(tfp, "Y%ld\n", (long) e->e_queuedelay); +#if _FFR_QUEUEDELAY + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "G%d\n", e->e_queuealg); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Y%ld\n", (long) e->e_queuedelay); if (tTd(40, 64)) sm_syslog(LOG_INFO, e->e_id, "queue alg: %d delay %ld next: %ld (now: %ld)\n", e->e_queuealg, e->e_queuedelay, e->e_dtime, curtime()); -# else /* _FFR_QUEUEDELAY */ - fprintf(tfp, "K%ld\n", (long) e->e_dtime); -# endif /* _FFR_QUEUEDELAY */ +#else /* _FFR_QUEUEDELAY */ + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime); +#endif /* _FFR_QUEUEDELAY */ /* output number of delivery attempts */ - fprintf(tfp, "N%d\n", e->e_ntries); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "N%d\n", e->e_ntries); /* output message priority */ - fprintf(tfp, "P%ld\n", e->e_msgpriority); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "P%ld\n", e->e_msgpriority); + + /* + ** If df file is in a different directory than the qf file, + ** output a "d" record naming the directory of the df file. + */ + + if (e->e_dfqgrp != e->e_qgrp) + { + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "d%s\n", + Queue[e->e_dfqgrp]->qg_qpaths[e->e_dfqdir].qp_name); + } /* output inode number of data file */ /* XXX should probably include device major/minor too */ if (e->e_dfino != -1) { - /*CONSTCOND*/ - if (sizeof e->e_dfino > sizeof(long)) - fprintf(tfp, "I%ld/%ld/%s\n", - (long) major(e->e_dfdev), - (long) minor(e->e_dfdev), - quad_to_string(e->e_dfino)); - else - fprintf(tfp, "I%ld/%ld/%lu\n", - (long) major(e->e_dfdev), - (long) minor(e->e_dfdev), - (unsigned long) e->e_dfino); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "I%ld/%ld/%llu\n", + (long) major(e->e_dfdev), + (long) minor(e->e_dfdev), + (ULONGLONG_T) e->e_dfino); } /* output body type */ if (e->e_bodytype != NULL) - fprintf(tfp, "B%s\n", denlstring(e->e_bodytype, TRUE, FALSE)); - -# if _FFR_SAVE_CHARSET - if (e->e_charset != NULL) - fprintf(tfp, "X%s\n", denlstring(e->e_charset, TRUE, FALSE)); -# endif /* _FFR_SAVE_CHARSET */ + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "B%s\n", + denlstring(e->e_bodytype, true, false)); /* message from envelope, if it exists */ if (e->e_message != NULL) - fprintf(tfp, "M%s\n", denlstring(e->e_message, TRUE, FALSE)); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n", + denlstring(e->e_message, true, false)); /* send various flag bits through */ p = buf; @@ -369,28 +604,35 @@ queueup(e, announce) *p++ = 'd'; if (bitset(EF_NO_BODY_RETN, e->e_flags)) *p++ = 'n'; + if (bitset(EF_SPLIT, e->e_flags)) + *p++ = 's'; *p++ = '\0'; if (buf[0] != '\0') - fprintf(tfp, "F%s\n", buf); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "F%s\n", buf); /* save $={persistentMacros} macro values */ - queueup_macros(macid("{persistentMacros}", NULL), tfp, e); + queueup_macros(macid("{persistentMacros}"), tfp, e); /* output name of sender */ if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) p = e->e_sender; else p = e->e_from.q_paddr; - fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE)); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "S%s\n", + denlstring(p, true, false)); /* output ESMTP-supplied "original" information */ if (e->e_envid != NULL) - fprintf(tfp, "Z%s\n", denlstring(e->e_envid, TRUE, FALSE)); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Z%s\n", + denlstring(e->e_envid, true, false)); /* output AUTH= parameter */ if (e->e_auth_param != NULL) - fprintf(tfp, "A%s\n", denlstring(e->e_auth_param, - TRUE, FALSE)); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "A%s\n", + denlstring(e->e_auth_param, true, false)); + if (e->e_dlvr_flag != 0) + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "!%c %ld\n", + (char) e->e_dlvr_flag, e->e_deliver_by); /* output list of recipient addresses */ printctladdr(NULL, NULL); @@ -399,27 +641,38 @@ queueup(e, announce) if (!QS_IS_UNDELIVERED(q->q_state)) continue; + /* message for this recipient, if it exists */ + if (q->q_message != NULL) + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n", + denlstring(q->q_message, true, + false)); + printctladdr(q, tfp); if (q->q_orcpt != NULL) - fprintf(tfp, "Q%s\n", - denlstring(q->q_orcpt, TRUE, FALSE)); - - (void) putc('R', tfp); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Q%s\n", + denlstring(q->q_orcpt, true, + false)); + if (q->q_finalrcpt != NULL) + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "r%s\n", + denlstring(q->q_finalrcpt, true, + false)); + (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'R'); if (bitset(QPRIMARY, q->q_flags)) - (void) putc('P', tfp); + (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'P'); if (bitset(QHASNOTIFY, q->q_flags)) - (void) putc('N', tfp); + (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'N'); if (bitset(QPINGONSUCCESS, q->q_flags)) - (void) putc('S', tfp); + (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'S'); if (bitset(QPINGONFAILURE, q->q_flags)) - (void) putc('F', tfp); + (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'F'); if (bitset(QPINGONDELAY, q->q_flags)) - (void) putc('D', tfp); + (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'D'); if (q->q_alias != NULL && bitset(QALIAS, q->q_alias->q_flags)) - (void) putc('A', tfp); - (void) putc(':', tfp); - (void) fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE)); + (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'A'); + (void) sm_io_putc(tfp, SM_TIME_DEFAULT, ':'); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s\n", + denlstring(q->q_paddr, true, false)); if (announce) { e->e_to = q->q_paddr; @@ -431,8 +684,8 @@ queueup(e, announce) } if (tTd(40, 1)) { - dprintf("queueing "); - printaddr(q, FALSE); + sm_dprintf("queueing "); + printaddr(q, false); } } @@ -454,7 +707,7 @@ queueup(e, announce) mcibuf.mci_mailer = &nullmailer; mcibuf.mci_out = tfp; - define('g', "\201f", e); + macdefine(&e->e_macro, A_PERM, 'g', "\201f"); for (h = e->e_header; h != NULL; h = h->h_link) { if (h->h_value == NULL) @@ -474,16 +727,18 @@ queueup(e, announce) } /* output this header */ - fprintf(tfp, "H?"); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "H?"); /* output conditional macro if present */ if (h->h_macro != '\0') { if (bitset(0200, h->h_macro)) - fprintf(tfp, "${%s}", - macname(bitidx(h->h_macro))); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, + "${%s}", + macname(bitidx(h->h_macro))); else - fprintf(tfp, "$%c", h->h_macro); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, + "$%c", h->h_macro); } else if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) @@ -493,28 +748,29 @@ queueup(e, announce) /* if conditional, output the set of conditions */ for (j = '\0'; j <= '\177'; j++) if (bitnset(j, h->h_mflags)) - (void) putc(j, tfp); + (void) sm_io_putc(tfp, SM_TIME_DEFAULT, + j); } - (void) putc('?', tfp); + (void) sm_io_putc(tfp, SM_TIME_DEFAULT, '?'); /* output the header: expand macros, convert addresses */ if (bitset(H_DEFAULT, h->h_flags) && !bitset(H_BINDLATE, h->h_flags)) { - fprintf(tfp, "%s: %s\n", - h->h_field, - denlstring(buf, FALSE, TRUE)); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s: %s\n", + h->h_field, + denlstring(buf, false, true)); } else if (bitset(H_FROM|H_RCPT, h->h_flags) && !bitset(H_BINDLATE, h->h_flags)) { bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); - FILE *savetrace = TrafficLogFile; + SM_FILE_T *savetrace = TrafficLogFile; TrafficLogFile = NULL; if (bitset(H_FROM, h->h_flags)) - oldstyle = FALSE; + oldstyle = false; commaize(h, h->h_value, oldstyle, &mcibuf, e); @@ -522,9 +778,10 @@ queueup(e, announce) } else { - fprintf(tfp, "%s: %s\n", - h->h_field, - denlstring(h->h_value, FALSE, TRUE)); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s: %s\n", + h->h_field, + denlstring(h->h_value, false, + true)); } } @@ -535,11 +792,13 @@ queueup(e, announce) ** scurrilous crackers from appending any data. */ - fprintf(tfp, ".\n"); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ".\n"); - if (fflush(tfp) != 0 || - (SuperSafe && fsync(fileno(tfp)) < 0) || - ferror(tfp)) + if (sm_io_flush(tfp, SM_TIME_DEFAULT) != 0 || + ((SuperSafe == SAFE_REALLY || + (SuperSafe == SAFE_INTERACTIVE && msync)) && + fsync(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL)) < 0) || + sm_io_error(tfp)) { if (newid) syserr("!552 Error writing control file %s", tf); @@ -554,19 +813,27 @@ queueup(e, announce) if (rename(tf, qf) < 0) syserr("cannot rename(%s, %s), uid=%d", tf, qf, geteuid()); + /* - ** fsync() after renaming to make sure - ** metadata is written to disk on - ** filesystems in which renames are - ** not guaranteed such as softupdates. + ** fsync() after renaming to make sure metadata is + ** written to disk on filesystems in which renames are + ** not guaranteed. */ - if (tfd >= 0 && SuperSafe && fsync(tfd) < 0) - syserr("!queueup: cannot fsync queue temp file %s", tf); + if (SuperSafe != SAFE_NO) + { + /* for softupdates */ + if (tfd >= 0 && fsync(tfd) < 0) + { + syserr("!queueup: cannot fsync queue temp file %s", + tf); + } + SYNC_DIR(qf, true); + } /* close and unlock old (locked) qf */ if (e->e_lockfp != NULL) - (void) fclose(e->e_lockfp); + (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); e->e_lockfp = tfp; } else @@ -579,14 +846,29 @@ queueup(e, announce) sm_syslog(LOG_DEBUG, e->e_id, "queueup, qf=%s", qf); if (tTd(40, 1)) - dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id); + sm_dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id); return; } +/* +** PRINTCTLADDR -- print control address to file. +** +** Parameters: +** a -- address. +** tfp -- file pointer. +** +** Returns: +** none. +** +** Side Effects: +** The control address (if changed) is printed to the file. +** The last control address and uid are saved. +*/ + static void printctladdr(a, tfp) register ADDRESS *a; - FILE *tfp; + SM_FILE_T *tfp; { char *user; register ADDRESS *q; @@ -599,7 +881,7 @@ printctladdr(a, tfp) if (a == NULL || a->q_alias == NULL || tfp == NULL) { if (lastctladdr != NULL && tfp != NULL) - fprintf(tfp, "C\n"); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C\n"); lastctladdr = NULL; lastuid = 0; return; @@ -629,11 +911,297 @@ printctladdr(a, tfp) lastctladdr = a; if (uid == 0 || user == NULL || user[0] == '\0') - fprintf(tfp, "C"); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C"); + else + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C%s:%ld:%ld", + denlstring(user, true, false), (long) uid, + (long) gid); + (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ":%s\n", + denlstring(a->q_paddr, true, false)); +} + +/* +** RUNNERS_SIGTERM -- propagate a SIGTERM to queue runner process +** +** This propagates the signal to the child processes that are queue +** runners. This is for a queue runner "cleanup". After all of the +** child queue runner processees are signaled (it should be SIGTERM +** being the sig) then the old signal handler (Oldsh) is called +** to handle any cleanup set for this process (provided it is not +** SIG_DFL or SIG_IGN). The signal may not be handled immediately +** if the BlockOldsh flag is set. If the current process doesn't +** have a parent then handle the signal immediately, regardless of +** BlockOldsh. +** +** Parameters: +** sig -- the signal number being sent +** +** Returns: +** none. +** +** Side Effects: +** Sets the NoMoreRunners boolean to true to stop more runners +** from being started in runqueue(). +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. +*/ + +static bool volatile NoMoreRunners = false; +static sigfunc_t Oldsh_term = SIG_DFL; +static sigfunc_t Oldsh_hup = SIG_DFL; +static sigfunc_t volatile Oldsh = SIG_DFL; +static bool BlockOldsh = false; +static int volatile Oldsig = 0; +static SIGFUNC_DECL runners_sigterm __P((int)); +static SIGFUNC_DECL runners_sighup __P((int)); + +static SIGFUNC_DECL +runners_sigterm(sig) + int sig; +{ + int save_errno = errno; + + FIX_SYSV_SIGNAL(sig, runners_sigterm); + errno = save_errno; + CHECK_CRITICAL(sig); + NoMoreRunners = true; + Oldsh = Oldsh_term; + Oldsig = sig; + proc_list_signal(PROC_QUEUE, sig); + + if (!BlockOldsh || getppid() <= 1) + { + /* Check that a valid 'old signal handler' is callable */ + if (Oldsh_term != SIG_DFL && Oldsh_term != SIG_IGN && + Oldsh_term != runners_sigterm) + (*Oldsh_term)(sig); + } + errno = save_errno; + return SIGFUNC_RETURN; +} +/* +** RUNNERS_SIGHUP -- propagate a SIGHUP to queue runner process +** +** This propagates the signal to the child processes that are queue +** runners. This is for a queue runner "cleanup". After all of the +** child queue runner processees are signaled (it should be SIGHUP +** being the sig) then the old signal handler (Oldsh) is called to +** handle any cleanup set for this process (provided it is not SIG_DFL +** or SIG_IGN). The signal may not be handled immediately if the +** BlockOldsh flag is set. If the current process doesn't have +** a parent then handle the signal immediately, regardless of +** BlockOldsh. +** +** Parameters: +** sig -- the signal number being sent +** +** Returns: +** none. +** +** Side Effects: +** Sets the NoMoreRunners boolean to true to stop more runners +** from being started in runqueue(). +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. +*/ + +static SIGFUNC_DECL +runners_sighup(sig) + int sig; +{ + int save_errno = errno; + + FIX_SYSV_SIGNAL(sig, runners_sighup); + errno = save_errno; + CHECK_CRITICAL(sig); + NoMoreRunners = true; + Oldsh = Oldsh_hup; + Oldsig = sig; + proc_list_signal(PROC_QUEUE, sig); + + if (!BlockOldsh || getppid() <= 1) + { + /* Check that a valid 'old signal handler' is callable */ + if (Oldsh_hup != SIG_DFL && Oldsh_hup != SIG_IGN && + Oldsh_hup != runners_sighup) + (*Oldsh_hup)(sig); + } + errno = save_errno; + return SIGFUNC_RETURN; +} +/* +** MARK_WORK_GROUP_RESTART -- mark a work group as needing a restart +** +** Sets a workgroup for restarting. +** +** Parameters: +** wgrp -- the work group id to restart. +** reason -- why (signal?). +** +** Returns: +** none. +** +** Side effects: +** Sets global RestartWorkGroup to true. +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. +*/ + +void +mark_work_group_restart(wgrp, reason) + int wgrp; + int reason; +{ + if (wgrp < 0 || wgrp > NumWorkGroups) + return; + + WorkGrp[wgrp].wg_restart = reason; + RestartWorkGroup = true; +} +/* +** RESTART_MARKED_WORK_GROUPS -- restart work groups marked as needing restart +** +** Restart any workgroup marked as needing a restart provided more +** runners are allowed. +** +** Parameters: +** none. +** +** Returns: +** none. +** +** Side effects: +** Sets global RestartWorkGroup to false. +*/ + +void +restart_marked_work_groups() +{ + int i; + int wasblocked; + + if (NoMoreRunners) + return; + + /* Block SIGCHLD so reapchild() doesn't mess with us */ + wasblocked = sm_blocksignal(SIGCHLD); + + for (i = 0; i < NumWorkGroups; i++) + { + if (WorkGrp[i].wg_restart >= 0) + { + if (LogLevel > 8) + sm_syslog(LOG_ERR, NOQID, + "restart queue runner=%d due to signal 0x%x", + i, WorkGrp[i].wg_restart); + restart_work_group(i); + } + } + RestartWorkGroup = false; + + if (wasblocked == 0) + (void) sm_releasesignal(SIGCHLD); +} +/* +** RESTART_WORK_GROUP -- restart a specific work group +** +** Restart a specific workgroup provided more runners are allowed. +** If the requested work group has been restarted too many times log +** this and refuse to restart. +** +** Parameters: +** wgrp -- the work group id to restart +** +** Returns: +** none. +** +** Side Effects: +** starts another process doing the work of wgrp +*/ + +#define MAX_PERSIST_RESTART 10 /* max allowed number of restarts */ + +static void +restart_work_group(wgrp) + int wgrp; +{ + if (NoMoreRunners || + wgrp < 0 || wgrp > NumWorkGroups) + return; + + WorkGrp[wgrp].wg_restart = -1; + if (WorkGrp[wgrp].wg_restartcnt < MAX_PERSIST_RESTART) + { + /* avoid overflow; increment here */ + WorkGrp[wgrp].wg_restartcnt++; + (void) run_work_group(wgrp, true, false, true, true); + } else - fprintf(tfp, "C%s:%ld:%ld", - denlstring(user, TRUE, FALSE), (long) uid, (long) gid); - fprintf(tfp, ":%s\n", denlstring(a->q_paddr, TRUE, FALSE)); + { + sm_syslog(LOG_ERR, NOQID, + "ERROR: persistent queue runner=%d restarted too many times, queue runner lost", + wgrp); + } +} +/* +** SCHEDULE_QUEUE_RUNS -- schedule the next queue run for a work group. +** +** Parameters: +** runall -- schedule even if individual bit is not set. +** wgrp -- the work group id to schedule. +** +** Returns: +** nothing +*/ + +#define INCR_MOD(v, m) if (++v >= m) \ + v = 0; \ + else + +static void +schedule_queue_runs(runall, wgrp) + bool runall; + int wgrp; +{ + int qgrp, cgrp, endgrp; + + /* + ** This is a bit ugly since we have to duplicate the + ** code that "walks" through a work queue group. + */ + + cgrp = endgrp = WorkGrp[wgrp].wg_curqgrp; + do + { + time_t qintvl; + + qgrp = WorkGrp[wgrp].wg_qgs[cgrp]->qg_index; + if (Queue[qgrp]->qg_queueintvl > 0) + qintvl = Queue[qgrp]->qg_queueintvl; + else if (QueueIntvl > 0) + qintvl = QueueIntvl; + else + qintvl = (time_t) 0; + if ((runall || bitnset(qgrp, DoQueueRun)) && qintvl > 0) + (void) sm_setevent(qintvl, runqueueevent, qgrp); +#if _FFR_QUEUE_SCHED_DBG + if (tTd(69, 10)) + sm_syslog(LOG_INFO, NOQID, + "sqr: wgrp=%d, cgrp=%d, qgrp=%d, intvl=%ld, QI=%ld, runall=%d, bit=%d, sched=%d", + wgrp, cgrp, qgrp, Queue[qgrp]->qg_queueintvl, + QueueIntvl, runall, bitnset(qgrp, DoQueueRun), + (runall || bitnset(qgrp, DoQueueRun)) && + qintvl > 0); +#endif /* _FFR_QUEUE_SCHED_DBG */ + clrbitn(qgrp, DoQueueRun); + INCR_MOD(cgrp, WorkGrp[wgrp].wg_numqgrp); + } while (endgrp != cgrp); } /* ** RUNQUEUE -- run the jobs in the queue. @@ -642,64 +1210,114 @@ printctladdr(a, tfp) ** order and processes them. ** ** Parameters: -** forkflag -- TRUE if the queue scanning should be done in +** forkflag -- true if the queue scanning should be done in ** a child process. We double-fork so it is not our ** child and we don't have to clean up after it. -** FALSE can be ignored if we have multiple queues. -** verbose -- if TRUE, print out status information. +** false can be ignored if we have multiple queues. +** verbose -- if true, print out status information. +** persistent -- persistent queue runner? +** runall -- run all groups or only a subset (DoQueueRun)? ** ** Returns: -** TRUE if the queue run successfully began. +** true if the queue run successfully began. ** ** Side Effects: -** runs things in the mail queue. +** runs things in the mail queue using run_work_group(). +** maybe schedules next queue run. +** */ static ENVELOPE QueueEnvelope; /* the queue run envelope */ -int NumQueues = 0; /* number of queues */ static time_t LastQueueTime = 0; /* last time a queue ID assigned */ static pid_t LastQueuePid = -1; /* last PID which had a queue ID */ -struct qpaths_s -{ - char *qp_name; /* name of queue dir */ - short qp_subdirs; /* use subdirs? */ -}; - -typedef struct qpaths_s QPATHS; - /* values for qp_supdirs */ #define QP_NOSUB 0x0000 /* No subdirectories */ #define QP_SUBDF 0x0001 /* "df" subdirectory */ #define QP_SUBQF 0x0002 /* "qf" subdirectory */ #define QP_SUBXF 0x0004 /* "xf" subdirectory */ -static QPATHS *QPaths = NULL; /* list of queue directories */ - bool -runqueue(forkflag, verbose) +runqueue(forkflag, verbose, persistent, runall) bool forkflag; bool verbose; + bool persistent; + bool runall; { int i; - bool ret = TRUE; + bool ret = true; static int curnum = 0; + sigfunc_t cursh; +#if SM_HEAP_CHECK + SM_NONVOLATILE int oldgroup = 0; + + if (sm_debug_active(&DebugLeakQ, 1)) + { + oldgroup = sm_heap_group(); + sm_heap_newgroup(); + sm_dprintf("runqueue() heap group #%d\n", sm_heap_group()); + } +#endif /* SM_HEAP_CHECK */ + + /* queue run has been started, don't do any more this time */ + clrbitn(NumQueue, DoQueueRun); - DoQueueRun = FALSE; + /* more then one queue or more then one directory per queue */ + if (!forkflag && !verbose && + (WorkGrp[0].wg_qgs[0]->qg_numqueues > 1 || NumWorkGroups > 1 || + WorkGrp[0].wg_numqgrp > 1)) + forkflag = true; + /* + ** For controlling queue runners via signals sent to this process. + ** Oldsh* will get called too by runners_sig* (if it is not SIG_IGN + ** or SIG_DFL) to preserve cleanup behavior. Now that this process + ** will have children (and perhaps grandchildren) this handler will + ** be left in place. This is because this process, once it has + ** finished spinning off queue runners, may go back to doing something + ** else (like being a daemon). And we still want on a SIG{TERM,HUP} to + ** clean up the child queue runners. Only install 'runners_sig*' once + ** else we'll get stuck looping forever. + */ - if (!forkflag && NumQueues > 1 && !verbose) - forkflag = TRUE; + cursh = sm_signal(SIGTERM, runners_sigterm); + if (cursh != runners_sigterm) + Oldsh_term = cursh; + cursh = sm_signal(SIGHUP, runners_sighup); + if (cursh != runners_sighup) + Oldsh_hup = cursh; - for (i = 0; i < NumQueues; i++) + for (i = 0; i < NumWorkGroups && !NoMoreRunners; i++) { /* - ** Pick up where we left off, in case we - ** used up all the children last time - ** without finishing. + ** If MaxQueueChildren active then test whether the start + ** of the next queue group's additional queue runners (maximum) + ** will result in MaxQueueChildren being exceeded. + ** + ** Note: do not use continue; even though another workgroup + ** may have fewer queue runners, this would be "unfair", + ** i.e., this work group might "starve" then. + */ + +#if _FFR_QUEUE_SCHED_DBG + if (tTd(69, 10)) + sm_syslog(LOG_INFO, NOQID, + "rq: curnum=%d, MaxQueueChildren=%d, CurRunners=%d, WorkGrp[curnum].wg_maxact=%d", + curnum, MaxQueueChildren, CurRunners, + WorkGrp[curnum].wg_maxact); +#endif /* _FFR_QUEUE_SCHED_DBG */ + if (MaxQueueChildren > 0 && + CurRunners + WorkGrp[curnum].wg_maxact > MaxQueueChildren) + break; + + /* + ** Pick up where we left off (curnum), in case we + ** used up all the children last time without finishing. + ** This give a round-robin fairness to queue runs. */ - ret = run_single_queue(curnum, forkflag, verbose); + ret = run_work_group(curnum, forkflag, verbose, persistent, + runall); /* ** Failure means a message was printed for ETRN @@ -709,71 +1327,289 @@ runqueue(forkflag, verbose) if (!ret) break; - if (++curnum >= NumQueues) - curnum = 0; + /* Success means the runner count needs to be updated. */ + CurRunners += WorkGrp[curnum].wg_maxact; + if (!persistent) + schedule_queue_runs(runall, curnum); + INCR_MOD(curnum, NumWorkGroups); + } + + /* schedule left over queue runs */ + if (i < NumWorkGroups && !NoMoreRunners && !persistent) + { + int h; + + for (h = curnum; i < NumWorkGroups; i++) + { + schedule_queue_runs(runall, h); + INCR_MOD(h, NumWorkGroups); + } } - if (QueueIntvl != 0) - (void) setevent(QueueIntvl, runqueueevent, 0); + + +#if SM_HEAP_CHECK + if (sm_debug_active(&DebugLeakQ, 1)) + sm_heap_setgroup(oldgroup); +#endif /* SM_HEAP_CHECK */ return ret; } /* -** RUN_SINGLE_QUEUE -- run the jobs in a single queue. +** RUNNER_WORK -- have a queue runner do its work +** +** Have a queue runner do its work a list of entries. +** When work isn't directly being done then this process can take a signal +** and terminate immediately (in a clean fashion of course). +** When work is directly being done, it's not to be interrupted +** immediately: the work should be allowed to finish at a clean point +** before termination (in a clean fashion of course). +** +** Parameters: +** e -- envelope. +** sequenceno -- 'th process to run WorkQ. +** didfork -- did the calling process fork()? +** skip -- process only each skip'th item. +** njobs -- number of jobs in WorkQ. +** +** Returns: +** none. +** +** Side Effects: +** runs things in the mail queue. +*/ + +/* Get new load average every 30 seconds. */ +#define GET_NEW_LA_TIME 30 + +static void +runner_work(e, sequenceno, didfork, skip, njobs) + register ENVELOPE *e; + int sequenceno; + bool didfork; + int skip; + int njobs; +{ + int n; + WORK *w; + time_t current_la_time, now; + + current_la_time = curtime(); + + /* + ** Here we temporarily block the second calling of the handlers. + ** This allows us to handle the signal without terminating in the + ** middle of direct work. If a signal does come, the test for + ** NoMoreRunners will find it. + */ + + BlockOldsh = true; + + /* process them once at a time */ + while (WorkQ != NULL) + { +#if SM_HEAP_CHECK + SM_NONVOLATILE int oldgroup = 0; + + if (sm_debug_active(&DebugLeakQ, 1)) + { + oldgroup = sm_heap_group(); + sm_heap_newgroup(); + sm_dprintf("run_queue_group() heap group #%d\n", + sm_heap_group()); + } +#endif /* SM_HEAP_CHECK */ + + /* do no more work */ + if (NoMoreRunners) + { + /* Check that a valid signal handler is callable */ + if (Oldsh != SIG_DFL && Oldsh != SIG_IGN && + Oldsh != runners_sighup && + Oldsh != runners_sigterm) + (*Oldsh)(Oldsig); + break; + } + + w = WorkQ; /* assign current work item */ + + /* + ** Set the head of the WorkQ to the next work item. + ** It is set 'skip' ahead (the number of parallel queue + ** runners working on WorkQ together) since each runner + ** works on every 'skip'th (N-th) item. + */ + + for (n = 0; n < skip && WorkQ != NULL; n++) + WorkQ = WorkQ->w_next; + e->e_to = NULL; + + /* + ** Ignore jobs that are too expensive for the moment. + ** + ** Get new load average every GET_NEW_LA_TIME seconds. + */ + + now = curtime(); + if (current_la_time < now - GET_NEW_LA_TIME) + { + sm_getla(); + current_la_time = now; + } + if (shouldqueue(WkRecipFact, current_la_time)) + { + char *msg = "Aborting queue run: load average too high"; + + if (Verbose) + message("%s", msg); + if (LogLevel > 8) + sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg); + break; + } + if (shouldqueue(w->w_pri, w->w_ctime)) + { + if (Verbose) + message(EmptyString); + if (QueueSortOrder == QSO_BYPRIORITY) + { + if (Verbose) + message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue", + qid_printqueue(w->w_qgrp, + w->w_qdir), + w->w_name + 2, sequenceno, + njobs); + if (LogLevel > 8) + sm_syslog(LOG_INFO, NOQID, + "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)", + qid_printqueue(w->w_qgrp, + w->w_qdir), + w->w_name + 2, w->w_pri, + CurrentLA, sequenceno, + njobs); + break; + } + else if (Verbose) + message("Skipping %s/%s (sequence %d of %d)", + qid_printqueue(w->w_qgrp, w->w_qdir), + w->w_name + 2, sequenceno, njobs); + } + else + { + if (Verbose) + { + message(EmptyString); + message("Running %s/%s (sequence %d of %d)", + qid_printqueue(w->w_qgrp, w->w_qdir), + w->w_name + 2, sequenceno, njobs); + } + if (didfork && MaxQueueChildren > 0) + { + sm_blocksignal(SIGCHLD); + (void) sm_signal(SIGCHLD, reapchild); + } + if (tTd(63, 100)) + sm_syslog(LOG_DEBUG, NOQID, + "runqueue %s dowork(%s)", + qid_printqueue(w->w_qgrp, w->w_qdir), + w->w_name + 2); + + (void) dowork(w->w_qgrp, w->w_qdir, w->w_name + 2, + false, false, e); + errno = 0; + } + sm_free(w->w_name); /* XXX */ + if (w->w_host != NULL) + sm_free(w->w_host); /* XXX */ + sm_free((char *) w); /* XXX */ + sequenceno += skip; /* next sequence number */ +#if SM_HEAP_CHECK + if (sm_debug_active(&DebugLeakQ, 1)) + sm_heap_setgroup(oldgroup); +#endif /* SM_HEAP_CHECK */ + } + + BlockOldsh = false; + + /* check the signals didn't happen during the revert */ + if (NoMoreRunners) + { + /* Check that a valid signal handler is callable */ + if (Oldsh != SIG_DFL && Oldsh != SIG_IGN && + Oldsh != runners_sighup && Oldsh != runners_sigterm) + (*Oldsh)(Oldsig); + } + + Oldsh = SIG_DFL; /* after the NoMoreRunners check */ +} +/* +** RUN_WORK_GROUP -- run the jobs in a queue group from a work group. ** ** Gets the stuff out of the queue in some presumably logical ** order and processes them. ** ** Parameters: -** queuedir -- queue to process -** forkflag -- TRUE if the queue scanning should be done in +** wgrp -- work group to process. +** forkflag -- true if the queue scanning should be done in ** a child process. We double-fork so it is not our ** child and we don't have to clean up after it. -** verbose -- if TRUE, print out status information. +** verbose -- if true, print out status information. +** persistent -- persistent queue runner? +** runall -- true: run all of the queue groups in this work group ** ** Returns: -** TRUE if the queue run successfully began. +** true if the queue run successfully began. ** ** Side Effects: ** runs things in the mail queue. */ -static bool -run_single_queue(queuedir, forkflag, verbose) - int queuedir; +/* Minimum sleep time for persistent queue runners */ +#define MIN_SLEEP_TIME 5 + +bool +run_work_group(wgrp, forkflag, verbose, persistent, runall) + int wgrp; bool forkflag; bool verbose; + bool persistent; + bool runall; { register ENVELOPE *e; - int njobs; - int sequenceno = 0; - time_t current_la_time, now; + int njobs, qdir; + int sequenceno = 1; + int qgrp, endgrp, h, i; + time_t current_la_time; + bool full, more; + SM_RPOOL_T *rpool; + extern void rmexpstab __P((void)); extern ENVELOPE BlankEnvelope; + extern SIGFUNC_DECL reapchild __P((int)); + + if (wgrp < 0) + return false; /* ** If no work will ever be selected, don't even bother reading ** the queue. */ - CurrentLA = sm_getla(NULL); /* get load average */ + sm_getla(); /* get load average */ current_la_time = curtime(); - if (shouldqueue(WkRecipFact, current_la_time)) + if (!persistent && shouldqueue(WkRecipFact, current_la_time)) { char *msg = "Skipping queue run -- load average too high"; if (verbose) message("458 %s\n", msg); if (LogLevel > 8) - sm_syslog(LOG_INFO, NOQID, - "runqueue: %s", - msg); - return FALSE; + sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg); + return false; } /* ** See if we already have too many children. */ - if (forkflag && QueueIntvl != 0 && + if (forkflag && WorkGrp[wgrp].wg_lowqintvl > 0 && !persistent && MaxChildren > 0 && CurChildren >= MaxChildren) { char *msg = "Skipping queue run -- too many children"; @@ -781,10 +1617,9 @@ run_single_queue(queuedir, forkflag, verbose) if (verbose) message("458 %s (%d)\n", msg, CurChildren); if (LogLevel > 8) - sm_syslog(LOG_INFO, NOQID, - "runqueue: %s (%d)", + sm_syslog(LOG_INFO, NOQID, "runqueue: %s (%d)", msg, CurChildren); - return FALSE; + return false; } /* @@ -795,81 +1630,84 @@ run_single_queue(queuedir, forkflag, verbose) { pid_t pid; - (void) blocksignal(SIGCHLD); - (void) setsignal(SIGCHLD, reapchild); + (void) sm_blocksignal(SIGCHLD); + (void) sm_signal(SIGCHLD, reapchild); pid = dofork(); if (pid == -1) { const char *msg = "Skipping queue run -- fork() failed"; - const char *err = errstring(errno); + const char *err = sm_errstring(errno); if (verbose) message("458 %s: %s\n", msg, err); if (LogLevel > 8) - sm_syslog(LOG_INFO, NOQID, - "runqueue: %s: %s", + sm_syslog(LOG_INFO, NOQID, "runqueue: %s: %s", msg, err); - (void) releasesignal(SIGCHLD); - return FALSE; + (void) sm_releasesignal(SIGCHLD); + return false; } if (pid != 0) { /* parent -- pick up intermediate zombie */ - (void) blocksignal(SIGALRM); - proc_list_add(pid, "Queue runner", PROC_QUEUE); - (void) releasesignal(SIGALRM); - (void) releasesignal(SIGCHLD); - return TRUE; + (void) sm_blocksignal(SIGALRM); + + /* wgrp only used when queue runners are persistent */ + proc_list_add(pid, "Queue runner", PROC_QUEUE, + WorkGrp[wgrp].wg_maxact, + persistent ? wgrp : -1); + (void) sm_releasesignal(SIGALRM); + (void) sm_releasesignal(SIGCHLD); + return true; } + /* child -- clean up signals */ /* Reset global flags */ RestartRequest = NULL; + RestartWorkGroup = false; ShutdownRequest = NULL; PendingSignal = 0; + CurrentPid = getpid(); + /* + ** Initialize exception stack and default exception + ** handler for child process. + */ + + sm_exc_newthread(fatal_error); clrcontrol(); proc_list_clear(); /* Add parent process as first child item */ - proc_list_add(getpid(), "Queue runner child process", - PROC_QUEUE_CHILD); - (void) releasesignal(SIGCHLD); - (void) setsignal(SIGCHLD, SIG_DFL); - (void) setsignal(SIGHUP, SIG_DFL); - (void) setsignal(SIGTERM, intsig); + proc_list_add(CurrentPid, "Queue runner child process", + PROC_QUEUE_CHILD, 0, -1); + (void) sm_releasesignal(SIGCHLD); + (void) sm_signal(SIGCHLD, SIG_DFL); + (void) sm_signal(SIGHUP, SIG_DFL); + (void) sm_signal(SIGTERM, intsig); } - sm_setproctitle(TRUE, CurEnv, "running queue: %s", - qid_printqueue(queuedir)); - - if (LogLevel > 69 || tTd(63, 99)) - sm_syslog(LOG_DEBUG, NOQID, - "runqueue %s, pid=%d, forkflag=%d", - qid_printqueue(queuedir), (int) getpid(), forkflag); - /* ** Release any resources used by the daemon code. */ -# if DAEMON clrdaemon(); -# endif /* DAEMON */ /* force it to run expensive jobs */ - NoConnect = FALSE; + NoConnect = false; /* drop privileges */ if (geteuid() == (uid_t) 0) - (void) drop_privileges(FALSE); + (void) drop_privileges(false); /* ** Create ourselves an envelope */ CurEnv = &QueueEnvelope; - e = newenvelope(&QueueEnvelope, CurEnv); + rpool = sm_rpool_new_x(NULL); + e = newenvelope(&QueueEnvelope, CurEnv, rpool); e->e_flags = BlankEnvelope.e_flags; e->e_parent = NULL; @@ -877,7 +1715,7 @@ run_single_queue(queuedir, forkflag, verbose) if (forkflag) { disconnect(1, e); - QuickAbort = FALSE; + QuickAbort = false; } /* @@ -888,198 +1726,464 @@ run_single_queue(queuedir, forkflag, verbose) if (QueueLimitId != NULL || QueueLimitSender != NULL || QueueLimitRecipient != NULL) { - IgnoreHostStatus = TRUE; + IgnoreHostStatus = true; MinQueueAge = 0; } /* + ** Here is where we choose the queue group from the work group. + ** The caller of the "domorework" label must setup a new envelope. + */ + + endgrp = WorkGrp[wgrp].wg_curqgrp; /* to not spin endlessly */ + + domorework: + + /* + ** Run a queue group if: + ** runall is set or the bit for this group is set. + */ + + for (;;) + { + /* + ** Find the next queue group within the work group that + ** has been marked as needing a run. + */ + + qgrp = WorkGrp[wgrp].wg_qgs[WorkGrp[wgrp].wg_curqgrp]->qg_index; + WorkGrp[wgrp].wg_curqgrp++; /* advance */ + WorkGrp[wgrp].wg_curqgrp %= WorkGrp[wgrp].wg_numqgrp; /* wrap */ + if (runall || bitnset(qgrp, DoQueueRun)) + break; + if (endgrp == WorkGrp[wgrp].wg_curqgrp) + { + e->e_id = NULL; + if (forkflag) + finis(true, ExitStat); + return true; /* we're done */ + } + } + + qdir = Queue[qgrp]->qg_curnum; /* round-robin init of queue position */ +#if _FFR_QUEUE_SCHED_DBG + if (tTd(69, 12)) + sm_syslog(LOG_INFO, NOQID, + "rwg: wgrp=%d, qgrp=%d, qdir=%d, name=%s, curqgrp=%d, numgrps=%d", + wgrp, qgrp, qdir, qid_printqueue(qgrp, qdir), + WorkGrp[wgrp].wg_curqgrp, WorkGrp[wgrp].wg_numqgrp); +#endif /* _FFR_QUEUE_SCHED_DBG */ + + /* tweak niceness of queue runs */ + if (Queue[qgrp]->qg_nice > 0) + (void) nice(Queue[qgrp]->qg_nice); + + /* XXX running queue group... */ + sm_setproctitle(true, CurEnv, "running queue: %s", + qid_printqueue(qgrp, qdir)); + + if (LogLevel > 69 || tTd(63, 99)) + sm_syslog(LOG_DEBUG, NOQID, + "runqueue %s, pid=%d, forkflag=%d", + qid_printqueue(qgrp, qdir), (int) CurrentPid, + forkflag); + + /* ** Start making passes through the queue. ** First, read and sort the entire queue. ** Then, process the work in that order. ** But if you take too long, start over. */ + for (i = 0; i < Queue[qgrp]->qg_numqueues; i++) + { + h = gatherq(qgrp, qdir, false, &full, &more); +#if SM_CONF_SHM + if (ShmId != SM_SHM_NO_ID) + QSHM_ENTRIES(Queue[qgrp]->qg_qpaths[qdir].qp_idx) = h; +#endif /* SM_CONF_SHM */ + /* If there are no more items in this queue advance */ + if (!more) + { + /* A round-robin advance */ + qdir++; + qdir %= Queue[qgrp]->qg_numqueues; + } + + /* Has the WorkList reached the limit? */ + if (full) + break; /* don't try to gather more */ + } + /* order the existing work requests */ - njobs = orderq(queuedir, FALSE); + njobs = sortq(Queue[qgrp]->qg_maxlist); + Queue[qgrp]->qg_curnum = qdir; /* update */ - /* process them once at a time */ - while (WorkQ != NULL) + if (!Verbose && bitnset(QD_FORK, Queue[qgrp]->qg_flags)) { - WORK *w = WorkQ; - - WorkQ = WorkQ->w_next; - e->e_to = NULL; + int loop, maxrunners; + pid_t pid; /* - ** Ignore jobs that are too expensive for the moment. - ** - ** Get new load average every 30 seconds. + ** For this WorkQ we want to fork off N children (maxrunners) + ** at this point. Each child has a copy of WorkQ. Each child + ** will process every N-th item. The parent will wait for all + ** of the children to finish before moving on to the next + ** queue group within the work group. This saves us forking + ** a new runner-child for each work item. + ** It's valid for qg_maxqrun == 0 since this may be an + ** explicit "don't run this queue" setting. */ - now = curtime(); - if (current_la_time < now - 30) + maxrunners = Queue[qgrp]->qg_maxqrun; + + /* No need to have more runners then there are jobs */ + if (maxrunners > njobs) + maxrunners = njobs; + for (loop = 0; loop < maxrunners; loop++) { - CurrentLA = sm_getla(e); - current_la_time = now; + /* + ** Since the delivery may happen in a child and the + ** parent does not wait, the parent may close the + ** maps thereby removing any shared memory used by + ** the map. Therefore, close the maps now so the + ** child will dynamically open them if necessary. + */ + + closemaps(false); + + pid = fork(); + if (pid < 0) + { + syserr("run_work_group: cannot fork"); + return 0; + } + else if (pid > 0) + { + /* parent -- clean out connection cache */ + mci_flush(false, NULL); + WorkQ = WorkQ->w_next; /* for the skip */ + sequenceno++; + proc_list_add(pid, "Queue child runner process", + PROC_QUEUE_CHILD, 0, -1); + + /* No additional work, no additional runners */ + if (WorkQ == NULL) + break; + } + else + { + /* Reset global flags */ + RestartRequest = NULL; + RestartWorkGroup = false; + ShutdownRequest = NULL; + PendingSignal = 0; + CurrentPid = getpid(); + + /* + ** Initialize exception stack and default + ** exception handler for child process. + ** When fork()'d the child now has a private + ** copy of WorkQ at its current position. + */ + + sm_exc_newthread(fatal_error); + if (MaxQueueChildren > 0) + { + proc_list_clear(); + sm_releasesignal(SIGCHLD); + (void) sm_signal(SIGCHLD, SIG_DFL); + } + + /* child -- error messages to the transcript */ + QuickAbort = OnlyOneError = false; + runner_work(e, sequenceno, true, + maxrunners, njobs); + finis(true, ExitStat); /* This child is done */ + /* NOTREACHED */ + } } - if (shouldqueue(WkRecipFact, current_la_time)) + + sm_releasesignal(SIGCHLD); + + /* + ** Wait until all of the runners have completed before + ** seeing if there is another queue group in the + ** work group to process. + ** XXX Future enhancement: don't wait() for all children + ** here, just go ahead and make sure that overall the number + ** of children is not exceeded. + */ + + while (CurChildren > 0) { - char *msg = "Aborting queue run: load average too high"; + int status; + pid_t ret; - if (Verbose) - message("%s", msg); - if (LogLevel > 8) - sm_syslog(LOG_INFO, NOQID, - "runqueue: %s", - msg); - break; + while ((ret = sm_wait(&status)) <= 0) + continue; + (void) proc_list_drop(ret, NULL, NULL); } - sequenceno++; - if (shouldqueue(w->w_pri, w->w_ctime)) + } + else + { + /* + ** When current process will not fork children to do the work, + ** it will do the work itself. The 'skip' will be 1 since + ** there are no child runners to divide the work across. + */ + + runner_work(e, sequenceno, false, 1, njobs); + } + + /* free memory allocated by newenvelope() above */ + sm_rpool_free(rpool); + QueueEnvelope.e_rpool = NULL; + + /* Are there still more queues in the work group to process? */ + if (endgrp != WorkGrp[wgrp].wg_curqgrp) + { + rpool = sm_rpool_new_x(NULL); + e = newenvelope(&QueueEnvelope, CurEnv, rpool); + e->e_flags = BlankEnvelope.e_flags; + goto domorework; + } + + /* No more queues in work group to process. Now check persistent. */ + if (persistent) + { + time_t now; + + sequenceno = 1; + sm_setproctitle(true, CurEnv, "running queue: %s", + qid_printqueue(qgrp, qdir)); + + /* + ** close bogus maps, i.e., maps which caused a tempfail, + ** so we get fresh map connections on the next lookup. + ** closemaps() is also called when children are started. + */ + + closemaps(true); + + /* Close any cached connections. */ + mci_flush(true, NULL); + + /* Clean out expired related entries. */ + rmexpstab(); + +#if NAMED_BIND + /* Update MX records for FallBackMX. */ + if (FallBackMX != NULL) + (void) getfallbackmxrr(FallBackMX); +#endif /* NAMED_BIND */ + +#if USERDB + /* close UserDatabase */ + _udbx_close(); +#endif /* USERDB */ + +#if SM_HEAP_CHECK + if (sm_debug_active(&SmHeapCheck, 2) + && access("memdump", F_OK) == 0 + ) { - if (Verbose) - message(""); - if (QueueSortOrder == QSO_BYPRIORITY) + SM_FILE_T *out; + + remove("memdump"); + out = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, + "memdump.out", SM_IO_APPEND, NULL); + if (out != NULL) { - if (Verbose) - message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue", - qid_printqueue(queuedir), - w->w_name + 2, - sequenceno, - njobs); - if (LogLevel > 8) - sm_syslog(LOG_INFO, NOQID, - "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)", - qid_printqueue(queuedir), - w->w_name + 2, - w->w_pri, - CurrentLA, - sequenceno, - njobs); - break; + (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n"); + sm_heap_report(out, + sm_debug_level(&SmHeapCheck) - 1); + (void) sm_io_close(out, SM_TIME_DEFAULT); } - else if (Verbose) - message("Skipping %s/%s (sequence %d of %d)", - qid_printqueue(queuedir), - w->w_name + 2, - sequenceno, njobs); } +#endif /* SM_HEAP_CHECK */ + + /* let me rest for a second to catch my breath */ + if (njobs == 0 && WorkGrp[wgrp].wg_lowqintvl < MIN_SLEEP_TIME) + sleep(MIN_SLEEP_TIME); + else if (WorkGrp[wgrp].wg_lowqintvl <= 0) + sleep(QueueIntvl > 0 ? QueueIntvl : MIN_SLEEP_TIME); else - { - pid_t pid; + sleep(WorkGrp[wgrp].wg_lowqintvl); - if (Verbose) - { - message(""); - message("Running %s/%s (sequence %d of %d)", - qid_printqueue(queuedir), - w->w_name + 2, - sequenceno, njobs); - } - if (tTd(63, 100)) - sm_syslog(LOG_DEBUG, NOQID, - "runqueue %s dowork(%s)", - qid_printqueue(queuedir), - w->w_name + 2); + /* + ** Get the LA outside the WorkQ loop if necessary. + ** In a persistent queue runner the code is repeated over + ** and over but orderq() may ignore entries due to + ** shouldqueue() (do we really have to do this twice?). + ** Hence the queue runners would just idle around when once + ** CurrentLA caused all entries in a queue to be ignored. + */ - pid = dowork(queuedir, w->w_name + 2, - ForkQueueRuns, FALSE, e); - errno = 0; - if (pid != 0) - (void) waitfor(pid); + now = curtime(); + if (njobs == 0 && current_la_time < now - GET_NEW_LA_TIME) + { + sm_getla(); + current_la_time = now; } - sm_free(w->w_name); - if (w->w_host) - sm_free(w->w_host); - sm_free((char *) w); + rpool = sm_rpool_new_x(NULL); + e = newenvelope(&QueueEnvelope, CurEnv, rpool); + e->e_flags = BlankEnvelope.e_flags; + goto domorework; } /* exit without the usual cleanup */ e->e_id = NULL; if (forkflag) - finis(TRUE, ExitStat); + finis(true, ExitStat); /* NOTREACHED */ - return TRUE; + return true; +} + +/* +** DOQUEUERUN -- do a queue run? +*/ + +bool +doqueuerun() +{ + return bitnset(NumQueue, DoQueueRun); } /* -** RUNQUEUEEVENT -- stub for use in setevent +** RUNQUEUEEVENT -- stub for use in sm_setevent +** +** Sets the bit to indicate that on the next run this queue should be +** processed. The work group that the queue group is a member of has its +** count of queue's to process updated. ** ** Parameters: -** none. +** qgrp -- the index of the queue group. ** ** Returns: ** none. ** +** Side Effects: +** The work group that the queue group is a member of has its +** count of queues to process updated. +** The invocation of this function via an alarm may interrupt +** a set of actions. Thus errno may be set in that context. +** We need to restore errno at the end of this function to ensure +** that any work done here that sets errno doesn't return a +** misleading/false errno value. Errno may be EINTR upon entry to +** this function because of non-restartable/continuable system +** API was active. Iff this is true we will override errno as +** a timeout (as a more accurate error message). +** ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE ** DOING. */ -static void -runqueueevent() +void +runqueueevent(qgrp) + int qgrp; { - DoQueueRun = TRUE; + int i; + int save_errno = errno; + + /* + ** Set the general bit that we want a queue run, + ** tested in doqueuerun() + */ + + setbitn(NumQueue, DoQueueRun); + + /* if it is a specific group: set that bit */ + if (qgrp != NOQGRP) + { + setbitn(qgrp, DoQueueRun); + goto ret; + } + + /* for all others: set the bit if it doesn't have a queue interval */ + for (i = 0; i < NumQueue; i++) + { + if (Queue[i]->qg_queueintvl <= 0) + setbitn(i, DoQueueRun); + } + + ret: + errno = save_errno; + if (errno == EINTR) + errno = ETIMEDOUT; } /* -** ORDERQ -- order the work queue. +** GATHERQ -- gather messages from the message queue(s) the work queue. ** ** Parameters: -** queuedir -- the index of the queue directory. +** qgrp -- the index of the queue group. +** qdir -- the index of the queue directory. ** doall -- if set, include everything in the queue (even ** the jobs that cannot be run because the load -** average is too high). Otherwise, exclude those -** jobs. +** average is too high, or MaxQueueRun is reached). +** Otherwise, exclude those jobs. +** full -- (optional) to be set 'true' if WorkList is full +** more -- (optional) to be set 'true' if there are still more +** messages in this queue not added to WorkList ** ** Returns: ** The number of request in the queue (not necessarily -** the number of requests in WorkQ however). +** the number of requests in WorkList however). ** ** Side Effects: -** Sets WorkQ to the queue of available work, in order. +** prepares available work into WorkList */ -# define NEED_P 001 -# define NEED_T 002 -# define NEED_R 004 -# define NEED_S 010 -# define NEED_H 020 +#define NEED_P 001 /* 'P': priority */ +#define NEED_T 002 /* 'T': time */ +#define NEED_R 004 /* 'R': recipient */ +#define NEED_S 010 /* 'S': sender */ +#define NEED_H 020 /* host */ -static WORK *WorkList = NULL; -static int WorkListSize = 0; +static WORK *WorkList = NULL; /* list of unsort work */ +static int WorkListSize = 0; /* current max size of WorkList */ +static int WorkListCount = 0; /* # of work items in WorkList */ static int -orderq(queuedir, doall) - int queuedir; +gatherq(qgrp, qdir, doall, full, more) + int qgrp; + int qdir; bool doall; + bool *full; + bool *more; { register struct dirent *d; register WORK *w; register char *p; DIR *f; - register int i; - int wn = -1; - int wc; + int i, num_ent; + int wn; QUEUE_CHAR *check; char qd[MAXPATHLEN]; char qf[MAXPATHLEN]; - if (queuedir == NOQDIR) - (void) strlcpy(qd, ".", sizeof qd); + wn = WorkListCount - 1; + num_ent = 0; + if (qdir == NOQDIR) + (void) sm_strlcpy(qd, ".", sizeof qd); else - (void) snprintf(qd, sizeof qd, "%s%s", - QPaths[queuedir].qp_name, - (bitset(QP_SUBQF, QPaths[queuedir].qp_subdirs) ? "/qf" : "")); + (void) sm_strlcpyn(qd, sizeof qd, 2, + Queue[qgrp]->qg_qpaths[qdir].qp_name, + (bitset(QP_SUBQF, + Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) + ? "/qf" : "")); if (tTd(41, 1)) { - dprintf("orderq:\n"); + sm_dprintf("orderq:\n"); check = QueueLimitId; while (check != NULL) { - dprintf("\tQueueLimitId = %s\n", + sm_dprintf("\tQueueLimitId = %s%s\n", + check->queue_negate ? "!" : "", check->queue_match); check = check->queue_next; } @@ -1087,7 +2191,8 @@ orderq(queuedir, doall) check = QueueLimitSender; while (check != NULL) { - dprintf("\tQueueLimitSender = %s\n", + sm_dprintf("\tQueueLimitSender = %s%s\n", + check->queue_negate ? "!" : "", check->queue_match); check = check->queue_next; } @@ -1095,30 +2200,23 @@ orderq(queuedir, doall) check = QueueLimitRecipient; while (check != NULL) { - dprintf("\tQueueLimitRecipient = %s\n", + sm_dprintf("\tQueueLimitRecipient = %s%s\n", + check->queue_negate ? "!" : "", check->queue_match); check = check->queue_next; } } - /* clear out old WorkQ */ - for (w = WorkQ; w != NULL; ) - { - register WORK *nw = w->w_next; - - WorkQ = nw; - sm_free(w->w_name); - if (w->w_host != NULL) - sm_free(w->w_host); - sm_free((char *) w); - w = nw; - } - /* open the queue directory */ f = opendir(qd); if (f == NULL) { - syserr("orderq: cannot open \"%s\"", qid_printqueue(queuedir)); + syserr("orderq: cannot open \"%s\"", + qid_printqueue(qgrp, qdir)); + if (full != NULL) + *full = WorkListCount >= MaxQueueRun && MaxQueueRun > 0; + if (more != NULL) + *more = false; return 0; } @@ -1128,13 +2226,13 @@ orderq(queuedir, doall) while ((d = readdir(f)) != NULL) { - FILE *cf; + SM_FILE_T *cf; int qfver = 0; char lbuf[MAXNAME + 1]; struct stat sbuf; if (tTd(41, 50)) - dprintf("orderq: checking %s\n", d->d_name); + sm_dprintf("orderq: checking %s\n", d->d_name); /* is this an interesting entry? */ if (d->d_name[0] != 'q' || d->d_name[1] != 'f') @@ -1143,8 +2241,9 @@ orderq(queuedir, doall) if (strlen(d->d_name) >= MAXQFNAME) { if (Verbose) - printf("orderq: %s too long, %d max characters\n", - d->d_name, MAXQFNAME); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "orderq: %s too long, %d max characters\n", + d->d_name, MAXQFNAME); if (LogLevel > 0) sm_syslog(LOG_ALERT, NOQID, "orderq: %s too long, %d max characters", @@ -1155,7 +2254,8 @@ orderq(queuedir, doall) check = QueueLimitId; while (check != NULL) { - if (strcontainedin(check->queue_match, d->d_name)) + if (strcontainedin(check->queue_match, d->d_name) != + check->queue_negate) break; else check = check->queue_next; @@ -1169,76 +2269,101 @@ orderq(queuedir, doall) if (wn == MaxQueueRun && LogLevel > 0) sm_syslog(LOG_WARNING, NOQID, "WorkList for %s maxed out at %d", - qid_printqueue(queuedir), + qid_printqueue(qgrp, qdir), MaxQueueRun); - continue; + if (doall) + continue; /* just count entries */ + break; } if (wn >= WorkListSize) { - grow_wlist(queuedir); + grow_wlist(qgrp, qdir); if (wn >= WorkListSize) continue; } + SM_ASSERT(wn >= 0); w = &WorkList[wn]; - (void) snprintf(qf, sizeof qf, "%s/%s", qd, d->d_name); + (void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", d->d_name); if (stat(qf, &sbuf) < 0) { if (errno != ENOENT) sm_syslog(LOG_INFO, NOQID, "orderq: can't stat %s/%s", - qid_printqueue(queuedir), d->d_name); + qid_printqueue(qgrp, qdir), + d->d_name); wn--; continue; } if (!bitset(S_IFREG, sbuf.st_mode)) { /* Yikes! Skip it or we will hang on open! */ - syserr("orderq: %s/%s is not a regular file", - qid_printqueue(queuedir), d->d_name); + if (!((d->d_name[0] == 'd' || d->d_name[0] == 'q' || + d->d_name[0] == 'x') && + d->d_name[1] == 'f' && d->d_name[2] == '\0')) + syserr("orderq: %s/%s is not a regular file", + qid_printqueue(qgrp, qdir), d->d_name); wn--; continue; } /* avoid work if possible */ - if (QueueSortOrder == QSO_BYFILENAME && + if ((QueueSortOrder == QSO_BYFILENAME || + QueueSortOrder == QSO_BYMODTIME || + QueueSortOrder == QSO_RANDOM) && QueueLimitSender == NULL && QueueLimitRecipient == NULL) { + w->w_qgrp = qgrp; + w->w_qdir = qdir; w->w_name = newstr(d->d_name); w->w_host = NULL; - w->w_lock = w->w_tooyoung = FALSE; + w->w_lock = w->w_tooyoung = false; w->w_pri = 0; w->w_ctime = 0; + w->w_mtime = sbuf.st_mtime; + ++num_ent; continue; } /* open control file */ - cf = fopen(qf, "r"); - - if (cf == NULL) + cf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY, + NULL); + if (cf == NULL && OpMode != MD_PRINT) { /* this may be some random person sending hir msgs */ - /* syserr("orderq: cannot open %s", cbuf); */ if (tTd(41, 2)) - dprintf("orderq: cannot open %s: %s\n", - d->d_name, errstring(errno)); + sm_dprintf("orderq: cannot open %s: %s\n", + d->d_name, sm_errstring(errno)); errno = 0; wn--; continue; } + w->w_qgrp = qgrp; + w->w_qdir = qdir; w->w_name = newstr(d->d_name); w->w_host = NULL; - w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB); - w->w_tooyoung = FALSE; + if (cf != NULL) + { + w->w_lock = !lockfile(sm_io_getinfo(cf, SM_IO_WHAT_FD, + NULL), + w->w_name, NULL, + LOCK_SH|LOCK_NB); + } + w->w_tooyoung = false; /* make sure jobs in creation don't clog queue */ w->w_pri = 0x7fffffff; w->w_ctime = 0; + w->w_mtime = sbuf.st_mtime; /* extract useful information */ - i = NEED_P | NEED_T; - if (QueueSortOrder == QSO_BYHOST) + i = NEED_P|NEED_T; + if (QueueSortOrder == QSO_BYHOST +#if _FFR_RHS + || QueueSortOrder == QSO_BYSHUFFLE +#endif /* _FFR_RHS */ + ) { /* need w_host set for host sort order */ i |= NEED_H; @@ -1247,7 +2372,9 @@ orderq(queuedir, doall) i |= NEED_S; if (QueueLimitRecipient != NULL) i |= NEED_R; - while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) + while (cf != NULL && i != 0 && + sm_io_fgets(cf, SM_TIME_DEFAULT, lbuf, + sizeof lbuf) != NULL) { int c; time_t age; @@ -1258,7 +2385,8 @@ orderq(queuedir, doall) else { /* flush rest of overly long line */ - while ((c = getc(cf)) != EOF && c != '\n') + while ((c = sm_io_getc(cf, SM_TIME_DEFAULT)) + != SM_IO_EOF && c != '\n') continue; } @@ -1282,7 +2410,12 @@ orderq(queuedir, doall) if (w->w_host == NULL && (p = strrchr(&lbuf[1], '@')) != NULL) { - w->w_host = strrev(&p[1]); +#if _FFR_RHS + if (QueueSortOrder == QSO_BYSHUFFLE) + w->w_host = newstr(&p[1]); + else +#endif /* _FFR_RHS */ + w->w_host = strrev(&p[1]); makelower(w->w_host); i &= ~NEED_H; } @@ -1303,7 +2436,8 @@ orderq(queuedir, doall) while (check != NULL) { if (strcontainedin(check->queue_match, - p)) + p) != + check->queue_negate) break; else check = check->queue_next; @@ -1317,7 +2451,8 @@ orderq(queuedir, doall) while (check != NULL) { if (strcontainedin(check->queue_match, - &lbuf[1])) + &lbuf[1]) != + check->queue_negate) break; else check = check->queue_next; @@ -1330,15 +2465,15 @@ orderq(queuedir, doall) age = curtime() - (time_t) atol(&lbuf[1]); if (age >= 0 && MinQueueAge > 0 && age < MinQueueAge) - w->w_tooyoung = TRUE; + w->w_tooyoung = true; break; case 'N': if (atol(&lbuf[1]) == 0) - w->w_tooyoung = FALSE; + w->w_tooyoung = false; break; -# if _FFR_QUEUEDELAY +#if _FFR_QUEUEDELAY /* case 'G': queuealg = atoi(lbuf[1]); @@ -1347,32 +2482,100 @@ orderq(queuedir, doall) queuedelay = (time_t) atol(&lbuf[1]); break; */ -# endif /* _FFR_QUEUEDELAY */ +#endif /* _FFR_QUEUEDELAY */ } } - (void) fclose(cf); + if (cf != NULL) + (void) sm_io_close(cf, SM_TIME_DEFAULT); if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) || bitset(NEED_R|NEED_S, i)) { /* don't even bother sorting this job in */ if (tTd(41, 49)) - dprintf("skipping %s (%x)\n", w->w_name, i); - sm_free(w->w_name); - if (w->w_host) - sm_free(w->w_host); + sm_dprintf("skipping %s (%x)\n", w->w_name, i); + sm_free(w->w_name); /* XXX */ + if (w->w_host != NULL) + sm_free(w->w_host); /* XXX */ wn--; } + else + ++num_ent; } (void) closedir(f); wn++; - WorkQ = NULL; - if (WorkList == NULL) + i = wn - WorkListCount; + WorkListCount += SM_MIN(num_ent, WorkListSize); + + if (more != NULL) + *more = WorkListCount < wn; + + if (full != NULL) + *full = (wn >= MaxQueueRun && MaxQueueRun > 0) || + (WorkList == NULL && wn > 0); + + return i; +} +/* +** SORTQ -- sort the work list +** +** First the old WorkQ is cleared away. Then the WorkList is sorted +** for all items so that important (higher sorting value) items are not +** trunctated off. Then the most important items are moved from +** WorkList to WorkQ. The lower count of 'max' or MaxListCount items +** are moved. +** +** Parameters: +** max -- maximum number of items to be placed in WorkQ +** +** Returns: +** the number of items in WorkQ +** +** Side Effects: +** WorkQ gets released and filled with new work. WorkList +** gets released. Work items get sorted in order. +*/ + +static int +sortq(max) + int max; +{ + register int i; /* local counter */ + register WORK *w; /* tmp item pointer */ + int wc = WorkListCount; /* trim size for WorkQ */ + + if (WorkQ != NULL) + { + /* Clear out old WorkQ. */ + for (w = WorkQ; w != NULL; ) + { + register WORK *nw = w->w_next; + + WorkQ = nw; + sm_free(w->w_name); /* XXX */ + if (w->w_host != NULL) + sm_free(w->w_host); /* XXX */ + sm_free((char *) w); /* XXX */ + w = nw; + } + sm_free((char *) WorkQ); + WorkQ = NULL; + } + + if (WorkList == NULL || wc <= 0) return 0; - wc = min(wn, WorkListSize); - if (wc > MaxQueueRun && MaxQueueRun > 0) - wc = MaxQueueRun; + + /* Check if the per queue group item limit will be exceeded */ + if (wc > max && max > 0) + wc = max; + + /* + ** The sort now takes place using all of the items in WorkList. + ** The list gets trimmed to the most important items after the sort. + ** If the trim were to happen before the sort then one or more + ** important items might get truncated off -- not what we want. + */ if (QueueSortOrder == QSO_BYHOST) { @@ -1401,11 +2604,12 @@ orderq(queuedir, doall) { if (WorkList[i].w_host == NULL && w->w_host == NULL) - WorkList[i].w_lock = TRUE; + WorkList[i].w_lock = true; else if (WorkList[i].w_host != NULL && w->w_host != NULL && - sm_strcasecmp(WorkList[i].w_host, w->w_host) == 0) - WorkList[i].w_lock = TRUE; + sm_strcasecmp(WorkList[i].w_host, + w->w_host) == 0) + WorkList[i].w_lock = true; else break; } @@ -1434,6 +2638,37 @@ orderq(queuedir, doall) qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf4); } + else if (QueueSortOrder == QSO_RANDOM) + { + /* + ** Sort randomly. + ** workcmpf5() retuns a random 1 or -1. + ** As long as nobody does a verification pass over the + ** sorted list, we should be golden. + */ + + qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf5); + } + else if (QueueSortOrder == QSO_BYMODTIME) + { + /* + ** Simple sort based on modification time of qf file. + ** This puts the oldest items first. + */ + + qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf6); + } +#if _FFR_RHS + else if (QueueSortOrder == QSO_BYSHUFFLE) + { + /* + ** Simple sort based on shuffled host name. + */ + + init_shuffle_alphabet(); + qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf7); + } +#endif /* _FFR_RHS */ else { /* @@ -1446,45 +2681,52 @@ orderq(queuedir, doall) /* ** Convert the work list into canonical form. ** Should be turning it into a list of envelopes here perhaps. + ** Only take the most important items up to the per queue group + ** maximum. */ for (i = wc; --i >= 0; ) { w = (WORK *) xalloc(sizeof *w); + w->w_qgrp = WorkList[i].w_qgrp; + w->w_qdir = WorkList[i].w_qdir; w->w_name = WorkList[i].w_name; w->w_host = WorkList[i].w_host; w->w_lock = WorkList[i].w_lock; w->w_tooyoung = WorkList[i].w_tooyoung; w->w_pri = WorkList[i].w_pri; w->w_ctime = WorkList[i].w_ctime; + w->w_mtime = WorkList[i].w_mtime; w->w_next = WorkQ; WorkQ = w; } if (WorkList != NULL) - sm_free(WorkList); + sm_free(WorkList); /* XXX */ WorkList = NULL; WorkListSize = 0; + WorkListCount = 0; if (tTd(40, 1)) { for (w = WorkQ; w != NULL; w = w->w_next) { if (w->w_host != NULL) - dprintf("%22s: pri=%ld %s\n", + sm_dprintf("%22s: pri=%ld %s\n", w->w_name, w->w_pri, w->w_host); else - dprintf("%32s: pri=%ld\n", + sm_dprintf("%32s: pri=%ld\n", w->w_name, w->w_pri); } } - return wn; + return wc; /* return number of WorkQ items */ } /* ** GROW_WLIST -- make the work list larger ** ** Parameters: -** queuedir -- the index for the queue directory. +** qgrp -- the index for the queue group. +** qdir -- the index for the queue directory. ** ** Returns: ** none. @@ -1496,11 +2738,12 @@ orderq(queuedir, doall) */ static void -grow_wlist(queuedir) - int queuedir; +grow_wlist(qgrp, qdir) + int qgrp; + int qdir; { if (tTd(41, 1)) - dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize); + sm_dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize); if (WorkList == NULL) { WorkList = (WORK *) xalloc((sizeof *WorkList) * @@ -1510,8 +2753,8 @@ grow_wlist(queuedir) else { int newsize = WorkListSize + QUEUESEGSIZE; - WORK *newlist = (WORK *) xrealloc((char *)WorkList, - (unsigned)sizeof(WORK) * (newsize + 1)); + WORK *newlist = (WORK *) sm_realloc((char *) WorkList, + (unsigned) sizeof(WORK) * (newsize + 1)); if (newlist != NULL) { @@ -1521,7 +2764,7 @@ grow_wlist(queuedir) { sm_syslog(LOG_INFO, NOQID, "grew WorkList for %s to %d", - qid_printqueue(queuedir), + qid_printqueue(qgrp, qdir), WorkListSize); } } @@ -1529,11 +2772,11 @@ grow_wlist(queuedir) { sm_syslog(LOG_ALERT, NOQID, "FAILED to grow WorkList for %s to %d", - qid_printqueue(queuedir), newsize); + qid_printqueue(qgrp, qdir), newsize); } } if (tTd(41, 1)) - dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize); + sm_dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize); } /* ** WORKCMPF0 -- simple priority-only compare function. @@ -1547,8 +2790,6 @@ grow_wlist(queuedir) ** 0 if a == b ** +1 if a > b ** -** Side Effects: -** none. */ static int @@ -1580,8 +2821,6 @@ workcmpf0(a, b) ** 0 if a == b ** >0 if a > b ** -** Side Effects: -** none. */ static int @@ -1621,8 +2860,6 @@ workcmpf1(a, b) ** 0 if a == b ** >0 if a > b ** -** Side Effects: -** none. */ static int @@ -1660,8 +2897,6 @@ workcmpf2(a, b) ** 0 if a == b ** +1 if a > b ** -** Side Effects: -** none. */ static int @@ -1688,8 +2923,6 @@ workcmpf3(a, b) ** 0 if a == b ** +1 if a > b ** -** Side Effects: -** none. */ static int @@ -1700,6 +2933,92 @@ workcmpf4(a, b) return strcmp(a->w_name, b->w_name); } /* +** WORKCMPF5 -- compare based on assigned random number +** +** Parameters: +** a -- the first argument (ignored). +** b -- the second argument (ignored). +** +** Returns: +** randomly 1/-1 +*/ + +/* ARGSUSED0 */ +static int +workcmpf5(a, b) + register WORK *a; + register WORK *b; +{ + return (get_rand_mod(2)) ? 1 : -1; +} +/* +** WORKCMPF6 -- simple modification-time-only compare function. +** +** Parameters: +** a -- the first argument. +** b -- the second argument. +** +** Returns: +** -1 if a < b +** 0 if a == b +** +1 if a > b +** +*/ + +static int +workcmpf6(a, b) + register WORK *a; + register WORK *b; +{ + if (a->w_mtime > b->w_mtime) + return 1; + else if (a->w_mtime < b->w_mtime) + return -1; + else + return 0; +} +#if _FFR_RHS +/* +** WORKCMPF7 -- compare function for ordering work based on shuffled host name. +** +** Sorts on lock status, host name, and priority in that order. +** +** Parameters: +** a -- the first argument. +** b -- the second argument. +** +** Returns: +** <0 if a < b +** 0 if a == b +** >0 if a > b +** +*/ + +static int +workcmpf7(a, b) + register WORK *a; + register WORK *b; +{ + int i; + + /* lock status */ + if (a->w_lock != b->w_lock) + return a->w_lock - b->w_lock; + + /* host name */ + if (a->w_host != NULL && b->w_host == NULL) + return 1; + else if (a->w_host == NULL && b->w_host != NULL) + return -1; + if (a->w_host != NULL && b->w_host != NULL && + (i = sm_strshufflecmp(a->w_host, b->w_host)) != 0) + return i; + + /* job priority */ + return workcmpf0(a, b); +} +#endif /* _FFR_RHS */ +/* ** STRREV -- reverse string ** ** Returns a pointer to a new string that is the reverse of @@ -1727,11 +3046,71 @@ strrev(fwd) rev[len] = '\0'; return rev; } + +#if _FFR_RHS + +#define NASCII 128 +#define NCHAR 256 + +static unsigned char ShuffledAlphabet[NCHAR]; + +void +init_shuffle_alphabet() +{ + static bool init = false; + int i; + + if (init) + return; + + /* fill the ShuffledAlphabet */ + for (i = 0; i < NCHAR; i++) + ShuffledAlphabet[i] = i; + + /* mix it */ + for (i = 1; i < NCHAR; i++) + { + register int j = get_random() % NCHAR; + register int tmp; + + tmp = ShuffledAlphabet[j]; + ShuffledAlphabet[j] = ShuffledAlphabet[i]; + ShuffledAlphabet[i] = tmp; + } + + /* make it case insensitive */ + for (i = 'A'; i <= 'Z'; i++) + ShuffledAlphabet[i] = ShuffledAlphabet[i + 'a' - 'A']; + + /* fill the upper part */ + for (i = 0; i < NCHAR; i++) + ShuffledAlphabet[i + NCHAR] = ShuffledAlphabet[i]; + init = true; +} + +static int +sm_strshufflecmp(a, b) + char *a; + char *b; +{ + const unsigned char *us1 = (const unsigned char *) a; + const unsigned char *us2 = (const unsigned char *) b; + + while (ShuffledAlphabet[*us1] == ShuffledAlphabet[*us2++]) + { + if (*us1++ == '\0') + return 0; + } + return (ShuffledAlphabet[*us1] - ShuffledAlphabet[*--us2]); +} +#endif /* _FFR_RHS */ + /* ** DOWORK -- do a work request. ** ** Parameters: -** queuedir -- the index of the queue directory for the job. +** qgrp -- the index of the queue group for the job. +** qdir -- the index of the queue directory for the job. ** id -- the ID of the job to run. ** forkflag -- if set, run this in background. ** requeueflag -- if set, reinstantiate the queue quickly. @@ -1748,17 +3127,19 @@ strrev(fwd) */ pid_t -dowork(queuedir, id, forkflag, requeueflag, e) - int queuedir; +dowork(qgrp, qdir, id, forkflag, requeueflag, e) + int qgrp; + int qdir; char *id; bool forkflag; bool requeueflag; register ENVELOPE *e; { register pid_t pid; + SM_RPOOL_T *rpool; if (tTd(40, 1)) - dprintf("dowork(%s/%s)\n", qid_printqueue(queuedir), id); + sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id); /* ** Fork for work. @@ -1774,7 +3155,7 @@ dowork(queuedir, id, forkflag, requeueflag, e) ** child will dynamically open them if necessary. */ - closemaps(); + closemaps(false); pid = fork(); if (pid < 0) @@ -1785,12 +3166,31 @@ dowork(queuedir, id, forkflag, requeueflag, e) else if (pid > 0) { /* parent -- clean out connection cache */ - mci_flush(FALSE, NULL); + mci_flush(false, NULL); } else { + /* + ** Initialize exception stack and default exception + ** handler for child process. + */ + + /* Reset global flags */ + RestartRequest = NULL; + RestartWorkGroup = false; + ShutdownRequest = NULL; + PendingSignal = 0; + CurrentPid = getpid(); + sm_exc_newthread(fatal_error); + if (MaxQueueChildren > 0) + { + proc_list_clear(); + sm_releasesignal(SIGCHLD); + (void) sm_signal(SIGCHLD, SIG_DFL); + } + /* child -- error messages to the transcript */ - QuickAbort = OnlyOneError = FALSE; + QuickAbort = OnlyOneError = false; } } else @@ -1808,32 +3208,37 @@ dowork(queuedir, id, forkflag, requeueflag, e) ** can recover on interrupt. */ - /* Reset global flags */ - RestartRequest = NULL; - ShutdownRequest = NULL; - PendingSignal = 0; + if (forkflag) + { + /* Reset global flags */ + RestartRequest = NULL; + RestartWorkGroup = false; + ShutdownRequest = NULL; + PendingSignal = 0; + } /* set basic modes, etc. */ - (void) alarm(0); + sm_clear_events(); clearstats(); - clearenvelope(e, FALSE); + rpool = sm_rpool_new_x(NULL); + clearenvelope(e, false, rpool); e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS; set_delivery_mode(SM_DELIVER, e); e->e_errormode = EM_MAIL; e->e_id = id; - e->e_queuedir = queuedir; - GrabTo = UseErrorsTo = FALSE; + e->e_qgrp = qgrp; + e->e_qdir = qdir; + GrabTo = UseErrorsTo = false; ExitStat = EX_OK; if (forkflag) { disconnect(1, e); - OpMode = MD_QUEUERUN; + set_op_mode(MD_QUEUERUN); } - sm_setproctitle(TRUE, e, "%s: from queue", qid_printname(e)); + sm_setproctitle(true, e, "%s from queue", qid_printname(e)); if (LogLevel > 76) - sm_syslog(LOG_DEBUG, e->e_id, - "dowork, pid=%d", - (int) getpid()); + sm_syslog(LOG_DEBUG, e->e_id, "dowork, pid=%d", + (int) CurrentPid); /* don't use the headers from sendmail.cf... */ e->e_header = NULL; @@ -1842,33 +3247,224 @@ dowork(queuedir, id, forkflag, requeueflag, e) if (!readqf(e)) { if (tTd(40, 4) && e->e_id != NULL) - dprintf("readqf(%s) failed\n", + sm_dprintf("readqf(%s) failed\n", qid_printname(e)); e->e_id = NULL; if (forkflag) - finis(FALSE, EX_OK); + finis(false, EX_OK); else + { + /* adding this frees 8 bytes */ + clearenvelope(e, false, rpool); + + /* adding this frees 12 bytes */ + sm_rpool_free(rpool); + e->e_rpool = NULL; return 0; + } } e->e_flags |= EF_INQUEUE; - eatheader(e, requeueflag); + eatheader(e, requeueflag, true); if (requeueflag) - queueup(e, FALSE); + queueup(e, false, false); /* do the delivery */ sendall(e, SM_DELIVER); /* finish up and exit */ if (forkflag) - finis(TRUE, ExitStat); + finis(true, ExitStat); else - dropenvelope(e, TRUE); + { + dropenvelope(e, true, false); + sm_rpool_free(rpool); + e->e_rpool = NULL; + } } e->e_id = NULL; return pid; } + +/* +** DOWORKLIST -- process a list of envelopes as work requests +** +** Similar to dowork(), except that after forking, it processes an +** envelope and its siblings, treating each envelope as a work request. +** +** Parameters: +** el -- envelope to be processed including its siblings. +** forkflag -- if set, run this in background. +** requeueflag -- if set, reinstantiate the queue quickly. +** This is used when expanding aliases in the queue. +** If forkflag is also set, it doesn't wait for the +** child. +** +** Returns: +** process id of process that is running the queue job. +** +** Side Effects: +** The work request is satisfied if possible. +*/ + +pid_t +doworklist(el, forkflag, requeueflag) + ENVELOPE *el; + bool forkflag; + bool requeueflag; +{ + register pid_t pid; + ENVELOPE *ei; + + if (tTd(40, 1)) + sm_dprintf("doworklist()\n"); + + /* + ** Fork for work. + */ + + if (forkflag) + { + /* + ** Since the delivery may happen in a child and the + ** parent does not wait, the parent may close the + ** maps thereby removing any shared memory used by + ** the map. Therefore, close the maps now so the + ** child will dynamically open them if necessary. + */ + + closemaps(false); + + pid = fork(); + if (pid < 0) + { + syserr("doworklist: cannot fork"); + return 0; + } + else if (pid > 0) + { + /* parent -- clean out connection cache */ + mci_flush(false, NULL); + } + else + { + /* + ** Initialize exception stack and default exception + ** handler for child process. + */ + + /* Reset global flags */ + RestartRequest = NULL; + RestartWorkGroup = false; + ShutdownRequest = NULL; + PendingSignal = 0; + CurrentPid = getpid(); + sm_exc_newthread(fatal_error); + if (MaxQueueChildren > 0) + { + proc_list_clear(); + sm_releasesignal(SIGCHLD); + (void) sm_signal(SIGCHLD, SIG_DFL); + } + + /* child -- error messages to the transcript */ + QuickAbort = OnlyOneError = false; + } + } + else + { + pid = 0; + } + + if (pid != 0) + return pid; + + /* + ** IN CHILD + ** Lock the control file to avoid duplicate deliveries. + ** Then run the file as though we had just read it. + ** We save an idea of the temporary name so we + ** can recover on interrupt. + */ + + if (forkflag) + { + /* Reset global flags */ + RestartRequest = NULL; + RestartWorkGroup = false; + ShutdownRequest = NULL; + PendingSignal = 0; + } + + /* set basic modes, etc. */ + sm_clear_events(); + clearstats(); + GrabTo = UseErrorsTo = false; + ExitStat = EX_OK; + if (forkflag) + { + disconnect(1, el); + set_op_mode(MD_QUEUERUN); + } + if (LogLevel > 76) + sm_syslog(LOG_DEBUG, el->e_id, "doworklist, pid=%d", + (int) CurrentPid); + + for (ei = el; ei != NULL; ei = ei->e_sibling) + { + ENVELOPE e; + SM_RPOOL_T *rpool; + + if (WILL_BE_QUEUED(ei->e_sendmode)) + continue; + + rpool = sm_rpool_new_x(NULL); + clearenvelope(&e, true, rpool); + e.e_flags |= EF_QUEUERUN|EF_GLOBALERRS; + set_delivery_mode(SM_DELIVER, &e); + e.e_errormode = EM_MAIL; + e.e_id = ei->e_id; + e.e_qgrp = ei->e_qgrp; + e.e_qdir = ei->e_qdir; + openxscript(&e); + sm_setproctitle(true, &e, "%s from queue", qid_printname(&e)); + + /* don't use the headers from sendmail.cf... */ + e.e_header = NULL; + CurEnv = &e; + + /* read the queue control file -- return if locked */ + if (readqf(&e)) + { + e.e_flags |= EF_INQUEUE; + eatheader(&e, requeueflag, true); + + if (requeueflag) + queueup(&e, false, false); + + /* do the delivery */ + sendall(&e, SM_DELIVER); + dropenvelope(&e, true, false); + } + else + { + if (tTd(40, 4) && e.e_id != NULL) + sm_dprintf("readqf(%s) failed\n", + qid_printname(&e)); + } + sm_rpool_free(rpool); + ei->e_id = NULL; + } + + /* restore CurEnv */ + CurEnv = el; + + /* finish up and exit */ + if (forkflag) + finis(true, ExitStat); + return 0; +} /* ** READQF -- read queue file and set up environment. ** @@ -1876,8 +3472,8 @@ dowork(queuedir, id, forkflag, requeueflag, e) ** e -- the envelope of the job to run. ** ** Returns: -** TRUE if it successfully read the queue file. -** FALSE otherwise. +** true if it successfully read the queue file. +** false otherwise. ** ** Side Effects: ** The queue file is returned locked. @@ -1887,15 +3483,17 @@ static bool readqf(e) register ENVELOPE *e; { - register FILE *qfp; + register SM_FILE_T *qfp; ADDRESS *ctladdr; struct stat st, stf; char *bp; int qfver = 0; long hdrsize = 0; register char *p; + char *frcpt = NULL; char *orcpt = NULL; - bool nomore = FALSE; + bool nomore = false; + bool bogus = false; MODE_T qsafe; char qf[MAXPATHLEN]; char buf[MAXLINE]; @@ -1904,33 +3502,37 @@ readqf(e) ** Read and process the file. */ - (void) strlcpy(qf, queuename(e, 'q'), sizeof qf); - qfp = fopen(qf, "r+"); + (void) sm_strlcpy(qf, queuename(e, 'q'), sizeof qf); + qfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDWR, NULL); if (qfp == NULL) { int save_errno = errno; if (tTd(40, 8)) - dprintf("readqf(%s): fopen failure (%s)\n", - qf, errstring(errno)); + sm_dprintf("readqf(%s): sm_io_open failure (%s)\n", + qf, sm_errstring(errno)); errno = save_errno; if (errno != ENOENT ) syserr("readqf: no control file %s", qf); - return FALSE; + RELEASE_QUEUE; + return false; } - if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB)) + if (!lockfile(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), qf, NULL, + LOCK_EX|LOCK_NB)) { /* being processed by another queuer */ if (Verbose) - printf("%s: locked\n", e->e_id); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "%s: locked\n", e->e_id); if (tTd(40, 8)) - dprintf("%s: locked\n", e->e_id); + sm_dprintf("%s: locked\n", e->e_id); if (LogLevel > 19) sm_syslog(LOG_DEBUG, e->e_id, "locked"); - (void) fclose(qfp); - return FALSE; + (void) sm_io_close(qfp, SM_TIME_DEFAULT); + RELEASE_QUEUE; + return false; } /* @@ -1952,35 +3554,38 @@ readqf(e) */ if (stat(qf, &stf) < 0 || - fstat(fileno(qfp), &st) < 0) + fstat(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), &st) < 0) { /* must have been being processed by someone else */ if (tTd(40, 8)) - dprintf("readqf(%s): [f]stat failure (%s)\n", - qf, errstring(errno)); - (void) fclose(qfp); - return FALSE; + sm_dprintf("readqf(%s): [f]stat failure (%s)\n", + qf, sm_errstring(errno)); + (void) sm_io_close(qfp, SM_TIME_DEFAULT); + RELEASE_QUEUE; + return false; } if (st.st_nlink != stf.st_nlink || st.st_dev != stf.st_dev || - st.st_ino != stf.st_ino || -# if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */ + ST_INODE(st) != ST_INODE(stf) || +#if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */ st.st_gen != stf.st_gen || -# endif /* HAS_ST_GEN && 0 */ +#endif /* HAS_ST_GEN && 0 */ st.st_uid != stf.st_uid || st.st_gid != stf.st_gid || st.st_size != stf.st_size) { /* changed after opened */ if (Verbose) - printf("%s: changed\n", e->e_id); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "%s: changed\n", e->e_id); if (tTd(40, 8)) - dprintf("%s: changed\n", e->e_id); + sm_dprintf("%s: changed\n", e->e_id); if (LogLevel > 19) sm_syslog(LOG_DEBUG, e->e_id, "changed"); - (void) fclose(qfp); - return FALSE; + (void) sm_io_close(qfp, SM_TIME_DEFAULT); + RELEASE_QUEUE; + return false; } /* @@ -1988,15 +3593,50 @@ readqf(e) */ qsafe = S_IWOTH|S_IWGRP; -#if _FFR_QUEUE_FILE_MODE if (bitset(S_IWGRP, QueueFileMode)) qsafe &= ~S_IWGRP; -#endif /* _FFR_QUEUE_FILE_MODE */ - if ((st.st_uid != geteuid() && - st.st_uid != TrustedUid && - geteuid() != RealUid) || - bitset(qsafe, st.st_mode)) + bogus = st.st_uid != geteuid() && + st.st_uid != TrustedUid && + geteuid() != RealUid; + + /* + ** If this qf file results from a set-group-ID binary, then + ** we check whether the directory is group-writable, + ** the queue file mode contains the group-writable bit, and + ** the groups are the same. + ** Notice: this requires that the set-group-ID binary is used to + ** run the queue! + */ + + if (bogus && st.st_gid == getegid() && UseMSP) + { + char delim; + struct stat dst; + + bp = SM_LAST_DIR_DELIM(qf); + if (bp == NULL) + delim = '\0'; + else + { + delim = *bp; + *bp = '\0'; + } + if (stat(delim == '\0' ? "." : qf, &dst) < 0) + syserr("readqf: cannot stat directory %s", + delim == '\0' ? "." : qf); + else + { + bogus = !(bitset(S_IWGRP, QueueFileMode) && + bitset(S_IWGRP, dst.st_mode) && + dst.st_gid == st.st_gid); + } + if (delim != '\0') + *bp = delim; + } + if (!bogus) + bogus = bitset(qsafe, st.st_mode); + if (bogus) { if (LogLevel > 0) { @@ -2005,10 +3645,11 @@ readqf(e) st.st_uid, st.st_mode); } if (tTd(40, 8)) - dprintf("readqf(%s): bogus file\n", qf); + sm_dprintf("readqf(%s): bogus file\n", qf); loseqfile(e, "bogus file uid in mqueue"); - (void) fclose(qfp); - return FALSE; + (void) sm_io_close(qfp, SM_TIME_DEFAULT); + RELEASE_QUEUE; + return false; } if (st.st_size == 0) @@ -2019,8 +3660,9 @@ readqf(e) (void) xunlink(queuename(e, 'd')); (void) xunlink(queuename(e, 'q')); } - (void) fclose(qfp); - return FALSE; + (void) sm_io_close(qfp, SM_TIME_DEFAULT); + RELEASE_QUEUE; + return false; } if (st.st_nlink == 0) @@ -2030,151 +3672,162 @@ readqf(e) ** unlinked. Just assume it is zero length. */ - (void) fclose(qfp); - return FALSE; + (void) sm_io_close(qfp, SM_TIME_DEFAULT); + RELEASE_QUEUE; + return false; } + /* If we don't own the file mark it as unsafe */ + if (st.st_uid != geteuid()) + e->e_flags |= EF_UNSAFE; + /* good file -- save this lock */ e->e_lockfp = qfp; /* do basic system initialization */ initsys(e); - define('i', e->e_id, e); + macdefine(&e->e_macro, A_PERM, 'i', e->e_id); LineNumber = 0; e->e_flags |= EF_GLOBALERRS; - OpMode = MD_QUEUERUN; + set_op_mode(MD_QUEUERUN); ctladdr = NULL; + e->e_dfqgrp = e->e_qgrp; + e->e_dfqdir = e->e_qdir; +#if _FFR_QUEUE_MACRO + macdefine(&e->e_macro, A_TEMP, macid("{queue}"), + qid_printqueue(e->e_qgrp, e->e_qdir)); +#endif /* _FFR_QUEUE_MACRO */ e->e_dfino = -1; e->e_msgsize = -1; -# if _FFR_QUEUEDELAY +#if _FFR_QUEUEDELAY e->e_queuealg = QD_LINEAR; e->e_queuedelay = (time_t) 0; -# endif /* _FFR_QUEUEDELAY */ +#endif /* _FFR_QUEUEDELAY */ while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL) { - u_long qflags; + unsigned long qflags; ADDRESS *q; int mid; time_t now; auto char *ep; if (tTd(40, 4)) - dprintf("+++++ %s\n", bp); + sm_dprintf("+++++ %s\n", bp); if (nomore) { /* hack attack */ syserr("SECURITY ALERT: extra data in qf: %s", bp); - (void) fclose(qfp); + (void) sm_io_close(qfp, SM_TIME_DEFAULT); loseqfile(e, "bogus queue line"); - return FALSE; + RELEASE_QUEUE; + return false; } switch (bp[0]) { - case 'V': /* queue file version number */ - qfver = atoi(&bp[1]); - if (qfver <= QF_VERSION) - break; - syserr("Version number in qf (%d) greater than max (%d)", - qfver, QF_VERSION); - (void) fclose(qfp); - loseqfile(e, "unsupported qf file version"); - return FALSE; + case 'A': /* AUTH= parameter */ + e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, &bp[1]); + break; + + case 'B': /* body type */ + e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, &bp[1]); + break; case 'C': /* specify controlling user */ - ctladdr = setctluser(&bp[1], qfver); + ctladdr = setctluser(&bp[1], qfver, e); break; - case 'Q': /* original recipient */ - orcpt = newstr(&bp[1]); + case 'D': /* data file name */ + /* obsolete -- ignore */ break; - case 'R': /* specify recipient */ - p = bp; - qflags = 0; - if (qfver >= 1) + case 'd': /* data file directory name */ { - /* get flag bits */ - while (*++p != '\0' && *p != ':') + int qgrp, qdir; + + for (qgrp = 0; + qgrp < NumQueue && Queue[qgrp] != NULL; + ++qgrp) { - switch (*p) + for (qdir = 0; + qdir < Queue[qgrp]->qg_numqueues; + ++qdir) { - case 'N': - qflags |= QHASNOTIFY; - break; - - case 'S': - qflags |= QPINGONSUCCESS; - break; - - case 'F': - qflags |= QPINGONFAILURE; - break; - - case 'D': - qflags |= QPINGONDELAY; - break; - - case 'P': - qflags |= QPRIMARY; - break; - - case 'A': - if (ctladdr != NULL) - ctladdr->q_flags |= QALIAS; - break; + if (strcmp(&bp[1], + Queue[qgrp]->qg_qpaths[qdir].qp_name) + == 0) + { + e->e_dfqgrp = qgrp; + e->e_dfqdir = qdir; + goto done; + } } } + loseqfile(e, "bogus queue file directory"); + RELEASE_QUEUE; + return false; + done: + break; } - else - qflags |= QPRIMARY; - q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e); - if (q != NULL) - { - q->q_alias = ctladdr; - if (qfver >= 1) - q->q_flags &= ~Q_PINGFLAGS; - q->q_flags |= qflags; - q->q_orcpt = orcpt; - (void) recipient(q, &e->e_sendqueue, 0, e); - } - orcpt = NULL; - break; case 'E': /* specify error recipient */ /* no longer used */ break; - case 'H': /* header */ - (void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e); - hdrsize += strlen(&bp[1]); - break; + case 'F': /* flag bits */ + if (strncmp(bp, "From ", 5) == 0) + { + /* we are being spoofed! */ + syserr("SECURITY ALERT: bogus qf line %s", bp); + (void) sm_io_close(qfp, SM_TIME_DEFAULT); + loseqfile(e, "bogus queue line"); + RELEASE_QUEUE; + return false; + } + for (p = &bp[1]; *p != '\0'; p++) + { + switch (*p) + { + case '8': /* has 8 bit data */ + e->e_flags |= EF_HAS8BIT; + break; - case 'L': /* Solaris Content-Length: */ - case 'M': /* message */ - /* ignore this; we want a new message next time */ - break; + case 'b': /* delete Bcc: header */ + e->e_flags |= EF_DELETE_BCC; + break; - case 'S': /* sender */ - setsender(newstr(&bp[1]), e, NULL, '\0', TRUE); - break; + case 'd': /* envelope has DSN RET= */ + e->e_flags |= EF_RET_PARAM; + break; - case 'B': /* body type */ - e->e_bodytype = newstr(&bp[1]); - break; + case 'n': /* don't return body */ + e->e_flags |= EF_NO_BODY_RETN; + break; + + case 'r': /* response */ + e->e_flags |= EF_RESPONSE; + break; + + case 's': /* split */ + e->e_flags |= EF_SPLIT; + break; -# if _FFR_SAVE_CHARSET - case 'X': /* character set */ - e->e_charset = newstr(&bp[1]); + case 'w': /* warning sent */ + e->e_flags |= EF_WARNING; + break; + } + } break; -# endif /* _FFR_SAVE_CHARSET */ - case 'D': /* data file name */ - /* obsolete -- ignore */ +#if _FFR_QUEUEDELAY + case 'G': /* queue delay algorithm */ + e->e_queuealg = atoi(&buf[1]); break; +#endif /* _FFR_QUEUEDELAY */ - case 'T': /* init time */ - e->e_ctime = atol(&bp[1]); + case 'H': /* header */ + (void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e); + hdrsize += strlen(&bp[1]); break; case 'I': /* data file's inode number */ @@ -2185,14 +3838,10 @@ readqf(e) e->e_dtime = atol(&buf[1]); break; -# if _FFR_QUEUEDELAY - case 'G': /* queue delay algorithm */ - e->e_queuealg = atoi(&buf[1]); - break; - case 'Y': /* current delay */ - e->e_queuedelay = (time_t) atol(&buf[1]); + case 'L': /* Solaris Content-Length: */ + case 'M': /* message */ + /* ignore this; we want a new message next time */ break; -# endif /* _FFR_QUEUEDELAY */ case 'N': /* number of delivery attempts */ e->e_ntries = atoi(&buf[1]); @@ -2204,12 +3853,14 @@ readqf(e) { char *howlong; - howlong = pintvl(now - e->e_dtime, TRUE); + howlong = pintvl(now - e->e_dtime, true); if (Verbose) - printf("%s: too young (%s)\n", - e->e_id, howlong); + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "%s: too young (%s)\n", + e->e_id, howlong); if (tTd(40, 8)) - dprintf("%s: too young (%s)\n", + sm_dprintf("%s: too young (%s)\n", e->e_id, howlong); if (LogLevel > 19) sm_syslog(LOG_DEBUG, e->e_id, @@ -2217,11 +3868,13 @@ readqf(e) howlong); e->e_id = NULL; unlockqueue(e); - return FALSE; + RELEASE_QUEUE; + return false; } - define(macid("{ntries}", NULL), newstr(&buf[1]), e); + macdefine(&e->e_macro, A_TEMP, + macid("{ntries}"), &buf[1]); -# if NAMED_BIND +#if NAMED_BIND /* adjust BIND parameters immediately */ if (e->e_ntries == 0) { @@ -2233,108 +3886,151 @@ readqf(e) _res.retry = TimeOuts.res_retry[RES_TO_NORMAL]; _res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL]; } -# endif /* NAMED_BIND */ +#endif /* NAMED_BIND */ break; case 'P': /* message priority */ e->e_msgpriority = atol(&bp[1]) + WkTimeFact; break; - case 'F': /* flag bits */ - if (strncmp(bp, "From ", 5) == 0) - { - /* we are being spoofed! */ - syserr("SECURITY ALERT: bogus qf line %s", bp); - (void) fclose(qfp); - loseqfile(e, "bogus queue line"); - return FALSE; - } - for (p = &bp[1]; *p != '\0'; p++) + case 'Q': /* original recipient */ + orcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]); + break; + + case 'r': /* original recipient */ + frcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]); + break; + + case 'R': /* specify recipient */ + p = bp; + qflags = 0; + if (qfver >= 1) { - switch (*p) + /* get flag bits */ + while (*++p != '\0' && *p != ':') { - case 'w': /* warning sent */ - e->e_flags |= EF_WARNING; - break; + switch (*p) + { + case 'N': + qflags |= QHASNOTIFY; + break; - case 'r': /* response */ - e->e_flags |= EF_RESPONSE; - break; + case 'S': + qflags |= QPINGONSUCCESS; + break; - case '8': /* has 8 bit data */ - e->e_flags |= EF_HAS8BIT; - break; + case 'F': + qflags |= QPINGONFAILURE; + break; - case 'b': /* delete Bcc: header */ - e->e_flags |= EF_DELETE_BCC; - break; + case 'D': + qflags |= QPINGONDELAY; + break; - case 'd': /* envelope has DSN RET= */ - e->e_flags |= EF_RET_PARAM; - break; + case 'P': + qflags |= QPRIMARY; + break; - case 'n': /* don't return body */ - e->e_flags |= EF_NO_BODY_RETN; - break; + case 'A': + if (ctladdr != NULL) + ctladdr->q_flags |= QALIAS; + break; + + default: /* ignore or complain? */ + break; + } } } + else + qflags |= QPRIMARY; + q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e, + true); + if (q != NULL) + { + q->q_alias = ctladdr; + if (qfver >= 1) + q->q_flags &= ~Q_PINGFLAGS; + q->q_flags |= qflags; + q->q_finalrcpt = frcpt; + q->q_orcpt = orcpt; + (void) recipient(q, &e->e_sendqueue, 0, e); + } + frcpt = NULL; + orcpt = NULL; break; - case 'Z': /* original envelope id from ESMTP */ - e->e_envid = newstr(&bp[1]); - define(macid("{dsn_envid}", NULL), newstr(&bp[1]), e); + case 'S': /* sender */ + setsender(sm_rpool_strdup_x(e->e_rpool, &bp[1]), + e, NULL, '\0', true); break; - case 'A': /* AUTH= parameter */ - e->e_auth_param = newstr(&bp[1]); + case 'T': /* init time */ + e->e_ctime = atol(&bp[1]); break; + case 'V': /* queue file version number */ + qfver = atoi(&bp[1]); + if (queuedelay_qfver_unsupported(qfver)) + syserr("qf version %d not supported: %s", + qfver, + "sendmail not compiled with _FFR_QUEUEDELAY"); + if (qfver <= QF_VERSION) + break; + syserr("Version number in qf (%d) greater than max (%d)", + qfver, QF_VERSION); + (void) sm_io_close(qfp, SM_TIME_DEFAULT); + loseqfile(e, "unsupported qf file version"); + RELEASE_QUEUE; + return false; + /* NOTREACHED */ + break; + +#if _FFR_QUEUEDELAY + case 'Y': /* current delay */ + e->e_queuedelay = (time_t) atol(&buf[1]); + break; +#endif /* _FFR_QUEUEDELAY */ + + case 'Z': /* original envelope id from ESMTP */ + e->e_envid = sm_rpool_strdup_x(e->e_rpool, &bp[1]); + macdefine(&e->e_macro, A_PERM, + macid("{dsn_envid}"), e->e_envid); + break; + + case '!': /* deliver by */ + + /* format: flag (1 char) space long-integer */ + e->e_dlvr_flag = buf[1]; + e->e_deliver_by = strtol(&buf[3], NULL, 10); + case '$': /* define macro */ { char *p; - mid = macid(&bp[1], &ep); + /* XXX elimate p? */ + mid = macid_parse(&bp[1], &ep); if (mid == 0) break; - - p = newstr(ep); - define(mid, p, e); - - /* - ** HACK ALERT: Unfortunately, 8.10 and - ** 8.11 reused the ${if_addr} and - ** ${if_family} macros for both the incoming - ** interface address/family (getrequests()) - ** and the outgoing interface address/family - ** (makeconnection()). In order for D_BINDIF - ** to work properly, have to preserve the - ** incoming information in the queue file for - ** later delivery attempts. The original - ** information is stored in the envelope - ** in readqf() so it can be stored in - ** queueup_macros(). This should be fixed - ** in 8.12. - */ - - if (strcmp(macname(mid), "if_addr") == 0) - e->e_if_macros[EIF_ADDR] = p; + p = sm_rpool_strdup_x(e->e_rpool, ep); + macdefine(&e->e_macro, A_PERM, mid, p); } break; case '.': /* terminate file */ - nomore = TRUE; + nomore = true; break; default: syserr("readqf: %s: line %d: bad line \"%s\"", qf, LineNumber, shortenstring(bp, MAXSHORTSTR)); - (void) fclose(qfp); + (void) sm_io_close(qfp, SM_TIME_DEFAULT); loseqfile(e, "unrecognized line"); - return FALSE; + RELEASE_QUEUE; + return false; } if (bp != buf) - sm_free(bp); + sm_free(bp); /* XXX */ } /* @@ -2345,17 +4041,20 @@ readqf(e) if (LineNumber == 0) { errno = 0; - e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; - return TRUE; + e->e_flags |= EF_CLRQUEUE|EF_FATALERRS|EF_RESPONSE; + RELEASE_QUEUE; + return true; } /* possibly set ${dsn_ret} macro */ if (bitset(EF_RET_PARAM, e->e_flags)) { if (bitset(EF_NO_BODY_RETN, e->e_flags)) - define(macid("{dsn_ret}", NULL), "hdrs", e); + macdefine(&e->e_macro, A_PERM, + macid("{dsn_ret}"), "hdrs"); else - define(macid("{dsn_ret}", NULL), "full", e); + macdefine(&e->e_macro, A_PERM, + macid("{dsn_ret}"), "full"); } /* @@ -2363,7 +4062,8 @@ readqf(e) */ p = queuename(e, 'd'); - e->e_dfp = fopen(p, "r"); + e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, p, SM_IO_RDONLY, + NULL); if (e->e_dfp == NULL) { syserr("readqf: cannot open %s", p); @@ -2371,15 +4071,17 @@ readqf(e) else { e->e_flags |= EF_HAS_DF; - if (fstat(fileno(e->e_dfp), &st) >= 0) + if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &st) + >= 0) { e->e_msgsize = st.st_size + hdrsize; e->e_dfdev = st.st_dev; - e->e_dfino = st.st_ino; + e->e_dfino = ST_INODE(st); } } - return TRUE; + RELEASE_QUEUE; + return true; } /* ** PRTSTR -- print a string, "unprintable" characters are shown as \oct @@ -2389,7 +4091,7 @@ readqf(e) ** ml -- maximum length of output ** ** Returns: -** none. +** number of entries ** ** Side Effects: ** Prints a string on stdout. @@ -2410,18 +4112,102 @@ prtstr(s, ml) { if (ml-- > 0) { - putchar(c); - putchar(c); + (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); + (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); } } else if (isascii(c) && isprint(c)) - putchar(c); + (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); else { if ((ml -= 3) > 0) - printf("\\%03o", c); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "\\%03o", c & 0xFF); + } + } +} +/* +** PRINTNQE -- print out number of entries in the mail queue +** +** Parameters: +** out -- output file pointer. +** prefix -- string to output in front of each line. +** +** Returns: +** none. +*/ + +void +printnqe(out, prefix) + SM_FILE_T *out; + char *prefix; +{ +#if SM_CONF_SHM + int i, k = 0, nrequests = 0; + bool unknown = false; + + if (ShmId == SM_SHM_NO_ID) + { + if (prefix == NULL) + (void) sm_io_fprintf(out, SM_TIME_DEFAULT, + "Data unavailable: shared memory not updated\n"); + else + (void) sm_io_fprintf(out, SM_TIME_DEFAULT, + "%sNOTCONFIGURED:-1\r\n", prefix); + return; + } + for (i = 0; i < NumQueue && Queue[i] != NULL; i++) + { + int j; + + k++; + for (j = 0; j < Queue[i]->qg_numqueues; j++) + { + int n; + + if (StopRequest) + stop_sendmail(); + + n = QSHM_ENTRIES(Queue[i]->qg_qpaths[j].qp_idx); + if (prefix != NULL) + (void) sm_io_fprintf(out, SM_TIME_DEFAULT, + "%s%s:%d\r\n", + prefix, qid_printqueue(i, j), n); + else if (n < 0) + { + (void) sm_io_fprintf(out, SM_TIME_DEFAULT, + "%s: unknown number of entries\n", + qid_printqueue(i, j)); + unknown = true; + } + else if (n == 0) + { + (void) sm_io_fprintf(out, SM_TIME_DEFAULT, + "%s is empty\n", + qid_printqueue(i, j)); + } + else if (n > 0) + { + (void) sm_io_fprintf(out, SM_TIME_DEFAULT, + "%s: entries=%d\n", + qid_printqueue(i, j), n); + nrequests += n; + k++; + } } } + if (prefix == NULL && k > 1) + (void) sm_io_fprintf(out, SM_TIME_DEFAULT, + "\t\tTotal requests: %d%s\n", + nrequests, unknown ? " (about)" : ""); +#else /* SM_CONF_SHM */ + if (prefix == NULL) + (void) sm_io_fprintf(out, SM_TIME_DEFAULT, + "Data unavailable without shared memory support\n"); + else + (void) sm_io_fprintf(out, SM_TIME_DEFAULT, + "%sNOTAVAILABLE:-1\r\n", prefix); +#endif /* SM_CONF_SHM */ } /* ** PRINTQUEUE -- print out a representation of the mail queue @@ -2439,54 +4225,69 @@ prtstr(s, ml) void printqueue() { - int i, nrequests = 0; + int i, k = 0, nrequests = 0; - for (i = 0; i < NumQueues; i++) + for (i = 0; i < NumQueue && Queue[i] != NULL; i++) { - if (StopRequest) - stop_sendmail(); - nrequests += print_single_queue(i); + int j; + + k++; + for (j = 0; j < Queue[i]->qg_numqueues; j++) + { + if (StopRequest) + stop_sendmail(); + nrequests += print_single_queue(i, j); + k++; + } } - if (NumQueues > 1) - printf("\t\tTotal Requests: %d\n", nrequests); + if (k > 1) + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "\t\tTotal requests: %d\n", + nrequests); } /* ** PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue ** ** Parameters: -** queuedir -- queue directory +** qgrp -- the index of the queue group. +** qdir -- the queue directory. ** ** Returns: -** number of entries +** none. ** ** Side Effects: ** Prints a listing of the mail queue on the standard output. */ static int -print_single_queue(queuedir) - int queuedir; +print_single_queue(qgrp, qdir) + int qgrp; + int qdir; { register WORK *w; - FILE *f; + SM_FILE_T *f; int nrequests; char qd[MAXPATHLEN]; char qddf[MAXPATHLEN]; char buf[MAXLINE]; - if (queuedir == NOQDIR) + if (qdir == NOQDIR) { - (void) strlcpy(qd, ".", sizeof qd); - (void) strlcpy(qddf, ".", sizeof qddf); + (void) sm_strlcpy(qd, ".", sizeof qd); + (void) sm_strlcpy(qddf, ".", sizeof qddf); } else { - (void) snprintf(qd, sizeof qd, "%s%s", - QPaths[queuedir].qp_name, - (bitset(QP_SUBQF, QPaths[queuedir].qp_subdirs) ? "/qf" : "")); - (void) snprintf(qddf, sizeof qddf, "%s%s", - QPaths[queuedir].qp_name, - (bitset(QP_SUBDF, QPaths[queuedir].qp_subdirs) ? "/df" : "")); + (void) sm_strlcpyn(qd, sizeof qd, 2, + Queue[qgrp]->qg_qpaths[qdir].qp_name, + (bitset(QP_SUBQF, + Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) + ? "/qf" : "")); + (void) sm_strlcpyn(qddf, sizeof qddf, 2, + Queue[qgrp]->qg_qpaths[qdir].qp_name, + (bitset(QP_SUBDF, + Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) + ? "/df" : "")); } /* @@ -2496,17 +4297,18 @@ print_single_queue(queuedir) if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0) { struct stat st; -# ifdef NGROUPS_MAX +#ifdef NGROUPS_MAX int n; extern GIDSET_T InitialGidSet[NGROUPS_MAX]; -# endif /* NGROUPS_MAX */ +#endif /* NGROUPS_MAX */ if (stat(qd, &st) < 0) { - syserr("Cannot stat %s", qid_printqueue(queuedir)); + syserr("Cannot stat %s", + qid_printqueue(qgrp, qdir)); return 0; } -# ifdef NGROUPS_MAX +#ifdef NGROUPS_MAX n = NGROUPS_MAX; while (--n >= 0) { @@ -2514,9 +4316,9 @@ print_single_queue(queuedir) break; } if (n < 0 && RealGid != st.st_gid) -# else /* NGROUPS_MAX */ +#else /* NGROUPS_MAX */ if (RealGid != st.st_gid) -# endif /* NGROUPS_MAX */ +#endif /* NGROUPS_MAX */ { usrerr("510 You are not permitted to see the queue"); setstat(EX_NOPERM); @@ -2528,7 +4330,8 @@ print_single_queue(queuedir) ** Read and order the queue. */ - nrequests = orderq(queuedir, TRUE); + nrequests = gatherq(qgrp, qdir, true, NULL, NULL); + (void) sortq(Queue[qgrp]->qg_maxlist); /* ** Print the work list that we have read. @@ -2537,20 +4340,25 @@ print_single_queue(queuedir) /* first see if there is anything */ if (nrequests <= 0) { - printf("%s is empty\n", qid_printqueue(queuedir)); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s is empty\n", + qid_printqueue(qgrp, qdir)); return 0; } - CurrentLA = sm_getla(NULL); /* get load average */ + sm_getla(); /* get load average */ - printf("\t\t%s (%d request%s", qid_printqueue(queuedir), nrequests, - nrequests == 1 ? "" : "s"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\t\t%s (%d request%s", + qid_printqueue(qgrp, qdir), + nrequests, nrequests == 1 ? "" : "s"); if (MaxQueueRun > 0 && nrequests > MaxQueueRun) - printf(", only %d printed", MaxQueueRun); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + ", only %d printed", MaxQueueRun); if (Verbose) - printf(")\n----Q-ID---- --Size-- -Priority- ---Q-Time--- ---------Sender/Recipient--------\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + ")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n"); else - printf(")\n----Q-ID---- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + ")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n"); for (w = WorkQ; w != NULL; w = w->w_next) { struct stat st; @@ -2565,34 +4373,73 @@ print_single_queue(queuedir) if (StopRequest) stop_sendmail(); - printf("%12s", w->w_name + 2); - (void) snprintf(qf, sizeof qf, "%s/%s", qd, w->w_name); - f = fopen(qf, "r"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%13s", + w->w_name + 2); + (void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", w->w_name); + f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY, + NULL); if (f == NULL) { - printf(" (job completed)\n"); + if (errno == EPERM) + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + " (permission denied)\n"); + else if (errno == ENOENT) + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + " (job completed)\n"); + else + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + " (%s)\n", + sm_errstring(errno)); errno = 0; continue; } w->w_name[0] = 'd'; - (void) snprintf(qf, sizeof qf, "%s/%s", qddf, w->w_name); + (void) sm_strlcpyn(qf, sizeof qf, 3, qddf, "/", w->w_name); if (stat(qf, &st) >= 0) dfsize = st.st_size; else + { + ENVELOPE e; + + /* + ** Maybe the df file can't be statted because + ** it is in a different directory than the qf file. + ** In order to find out, we must read the qf file. + */ + + newenvelope(&e, &BlankEnvelope, sm_rpool_new_x(NULL)); + e.e_id = w->w_name + 2; + e.e_qgrp = qgrp; + e.e_qdir = qdir; dfsize = -1; + if (readqf(&e)) + { + char *df = queuename(&e, 'd'); + if (stat(df, &st) >= 0) + dfsize = st.st_size; + } + if (e.e_lockfp != NULL) + { + (void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT); + e.e_lockfp = NULL; + } + clearenvelope(&e, false, e.e_rpool); + sm_rpool_free(e.e_rpool); + } if (w->w_lock) - printf("*"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "*"); else if (w->w_tooyoung) - printf("-"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "-"); else if (shouldqueue(w->w_pri, w->w_ctime)) - printf("X"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "X"); else - printf(" "); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " "); + errno = 0; statmsg[0] = bodytype[0] = '\0'; qfver = 0; - while (fgets(buf, sizeof buf, f) != NULL) + while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) { register int i; register char *p; @@ -2600,7 +4447,7 @@ print_single_queue(queuedir) if (StopRequest) stop_sendmail(); - fixcrlf(buf, TRUE); + fixcrlf(buf, true); switch (buf[0]) { case 'V': /* queue file version */ @@ -2624,33 +4471,47 @@ print_single_queue(queuedir) case 'S': /* sender name */ if (Verbose) { - printf("%8ld %10ld%c%.12s ", - dfsize, - w->w_pri, - bitset(EF_WARNING, flags) ? '+' : ' ', - ctime(&submittime) + 4); + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "%8ld %10ld%c%.12s ", + dfsize, + w->w_pri, + bitset(EF_WARNING, flags) + ? '+' : ' ', + ctime(&submittime) + 4); prtstr(&buf[1], 78); } else { - printf("%8ld %.16s ", dfsize, - ctime(&submittime)); - prtstr(&buf[1], 40); + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "%8ld %.16s ", + dfsize, + ctime(&submittime)); + prtstr(&buf[1], 39); } if (statmsg[0] != '\0' || bodytype[0] != '\0') { - printf("\n %10.10s", bodytype); + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "\n %10.10s", + bodytype); if (statmsg[0] != '\0') - printf(" (%.*s)", - Verbose ? 100 : 60, - statmsg); + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + " (%.*s)", + Verbose ? 100 : 60, + statmsg); + statmsg[0] = '\0'; } break; case 'C': /* controlling user */ if (Verbose) - printf("\n\t\t\t\t (---%.74s---)", - &buf[1]); + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "\n\t\t\t\t\t\t(---%.64s---)", + &buf[1]); break; case 'R': /* recipient name */ @@ -2664,13 +4525,25 @@ print_single_queue(queuedir) } if (Verbose) { - printf("\n\t\t\t\t\t "); - prtstr(p, 73); + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "\n\t\t\t\t\t\t"); + prtstr(p, 71); } else { - printf("\n\t\t\t\t "); - prtstr(p, 40); + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "\n\t\t\t\t\t "); + prtstr(p, 38); + } + if (Verbose && statmsg[0] != '\0') + { + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "\n\t\t (%.100s)", + statmsg); + statmsg[0] = '\0'; } break; @@ -2691,9 +4564,10 @@ print_single_queue(queuedir) } } if (submittime == (time_t) 0) - printf(" (no control file)"); - printf("\n"); - (void) fclose(f); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + " (no control file)"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); + (void) sm_io_close(f, SM_TIME_DEFAULT); } return nrequests; } @@ -2719,50 +4593,104 @@ queuename(e, type) register ENVELOPE *e; int type; { - char *sub = ""; + int qd, qg; + char *sub = "/"; + char pref[3]; static char buf[MAXPATHLEN]; /* Assign an ID if needed */ if (e->e_id == NULL) assign_queueid(e); - /* Assign a queue directory if needed */ - if (e->e_queuedir == NOQDIR) - setnewqueue(e); + /* begin of filename */ + pref[0] = (char) type; + pref[1] = 'f'; + pref[2] = '\0'; + + /* Assign a queue group/directory if needed */ + if (type == 'x') + { + /* + ** We don't want to call setnewqueue() if we are fetching + ** the pathname of the transcript file, because setnewqueue + ** chooses a queue, and sometimes we need to write to the + ** transcript file before we have gathered enough information + ** to choose a queue. + */ + + if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR) + { + if (e->e_qgrp != NOQGRP && e->e_qdir != NOQDIR) + { + e->e_xfqgrp = e->e_qgrp; + e->e_xfqdir = e->e_qdir; + } + else + { + e->e_xfqgrp = 0; + if (Queue[e->e_xfqgrp]->qg_numqueues <= 1) + e->e_xfqdir = 0; + else + { + e->e_xfqdir = get_rand_mod( + Queue[e->e_xfqgrp]->qg_numqueues); + } + } + } + qd = e->e_xfqdir; + qg = e->e_xfqgrp; + } + else + { + if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR) + setnewqueue(e); + if (type == 'd') + { + qd = e->e_dfqdir; + qg = e->e_dfqgrp; + } + else + { + qd = e->e_qdir; + qg = e->e_qgrp; + } + } - if (e->e_queuedir == NOQDIR) - (void) snprintf(buf, sizeof buf, "%cf%s", - type, e->e_id); + if (e->e_qdir == NOQDIR) + (void) sm_strlcpyn(buf, sizeof buf, 2, pref, e->e_id); else { switch (type) { case 'd': - if (bitset(QP_SUBDF, QPaths[e->e_queuedir].qp_subdirs)) - sub = "/df"; + if (bitset(QP_SUBDF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) + sub = "/df/"; break; case TEMPQF_LETTER: case 't': case LOSEQF_LETTER: case 'q': - if (bitset(QP_SUBQF, QPaths[e->e_queuedir].qp_subdirs)) - sub = "/qf"; + if (bitset(QP_SUBQF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) + sub = "/qf/"; break; case 'x': - if (bitset(QP_SUBXF, QPaths[e->e_queuedir].qp_subdirs)) - sub = "/xf"; + if (bitset(QP_SUBXF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) + sub = "/xf/"; break; + + default: + sm_abort("queuename: bad queue file type %d", type); } - (void) snprintf(buf, sizeof buf, "%s%s/%cf%s", - QPaths[e->e_queuedir].qp_name, - sub, type, e->e_id); + (void) sm_strlcpyn(buf, sizeof buf, 4, + Queue[qg]->qg_qpaths[qd].qp_name, + sub, pref, e->e_id); } if (tTd(7, 2)) - dprintf("queuename: %s\n", buf); + sm_dprintf("queuename: %s\n", buf); return buf; } /* @@ -2771,8 +4699,8 @@ queuename(e, type) ** Assigns an id code if one does not already exist. ** This code assumes that nothing will remain in the queue for ** longer than 60 years. It is critical that files with the given -** name not already exist in the queue. -** Also initializes e_queuedir to NOQDIR. +** name do not already exist in the queue. +** [No longer initializes e_qdir to NOQDIR.] ** ** Parameters: ** e -- envelope to set it in. @@ -2783,22 +4711,26 @@ queuename(e, type) static const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx"; # define QIC_LEN 60 +# define queuenextid() CurrentPid + void assign_queueid(e) register ENVELOPE *e; { - pid_t pid = getpid(); - static char cX = 0; + pid_t pid = queuenextid(); + static int cX = 0; static long random_offset; struct tm *tm; char idbuf[MAXQFNAME - 2]; + int seq; if (e->e_id != NULL) return; /* see if we need to get a new base time/pid */ - if (cX >= QIC_LEN || LastQueueTime == 0 || LastQueuePid != pid) + if (cX >= QIC_LEN * QIC_LEN || LastQueueTime == 0 || + LastQueuePid != pid) { time_t then = LastQueueTime; @@ -2811,12 +4743,22 @@ assign_queueid(e) { (void) sleep(1); } - LastQueuePid = getpid(); + LastQueuePid = queuenextid(); cX = 0; } + + /* + ** Generate a new sequence number between 0 and QIC_LEN*QIC_LEN-1. + ** This lets us generate up to QIC_LEN*QIC_LEN unique queue ids + ** per second, per process. With envelope splitting, + ** a single message can consume many queue ids. + */ + + seq = (int)((cX + random_offset) % (QIC_LEN * QIC_LEN)); + ++cX; if (tTd(7, 50)) - dprintf("assign_queueid: random_offset = %ld (%d)\n", - random_offset, (int)(cX + random_offset) % QIC_LEN); + sm_dprintf("assign_queueid: random_offset = %ld (%d)\n", + random_offset, seq); tm = gmtime(&LastQueueTime); idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN]; @@ -2825,15 +4767,21 @@ assign_queueid(e) idbuf[3] = QueueIdChars[tm->tm_hour]; idbuf[4] = QueueIdChars[tm->tm_min]; idbuf[5] = QueueIdChars[tm->tm_sec]; - idbuf[6] = QueueIdChars[((int)cX++ + random_offset) % QIC_LEN]; - (void) snprintf(&idbuf[7], sizeof idbuf - 7, "%05d", - (int) LastQueuePid); - e->e_id = newstr(idbuf); - define('i', e->e_id, e); - e->e_queuedir = NOQDIR; + idbuf[6] = QueueIdChars[seq / QIC_LEN]; + idbuf[7] = QueueIdChars[seq % QIC_LEN]; + (void) sm_snprintf(&idbuf[8], sizeof idbuf - 8, "%06d", + (int) LastQueuePid); + e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf); + macdefine(&e->e_macro, A_PERM, 'i', e->e_id); +#if 0 + /* XXX: inherited from MainEnvelope */ + e->e_qgrp = NOQGRP; /* too early to do anything else */ + e->e_qdir = NOQDIR; + e->e_xfqgrp = NOQGRP; +#endif /* 0 */ if (tTd(7, 1)) - dprintf("assign_queueid: assigned id %s, e=%lx\n", - e->e_id, (u_long) e); + sm_dprintf("assign_queueid: assigned id %s, e=%p\n", + e->e_id, e); if (LogLevel > 93) sm_syslog(LOG_DEBUG, e->e_id, "assigned id"); } @@ -2852,17 +4800,18 @@ assign_queueid(e) ** Returns: ** none */ + void sync_queue_time() { -# if FAST_PID_RECYCLE +#if FAST_PID_RECYCLE if (OpMode != MD_TEST && OpMode != MD_VERIFY && LastQueueTime > 0 && - LastQueuePid == getpid() && + LastQueuePid == CurrentPid && curtime() == LastQueueTime) (void) sleep(1); -# endif /* FAST_PID_RECYCLE */ +#endif /* FAST_PID_RECYCLE */ } /* ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope @@ -2882,13 +4831,13 @@ unlockqueue(e) ENVELOPE *e; { if (tTd(51, 4)) - dprintf("unlockqueue(%s)\n", + sm_dprintf("unlockqueue(%s)\n", e->e_id == NULL ? "NOQUEUE" : e->e_id); /* if there is a lock file in the envelope, close it */ if (e->e_lockfp != NULL) - (void) fclose(e->e_lockfp); + (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); e->e_lockfp = NULL; /* don't create a queue id if we don't already have one */ @@ -2899,8 +4848,7 @@ unlockqueue(e) if (LogLevel > 87) sm_syslog(LOG_DEBUG, e->e_id, "unlock"); if (!tTd(51, 104)) - xunlink(queuename(e, 'x')); - + (void) xunlink(queuename(e, 'x')); } /* ** SETCTLUSER -- create a controlling address @@ -2911,18 +4859,19 @@ unlockqueue(e) ** Parameters: ** user -- the user name of the controlling user. ** qfver -- the version stamp of this qf file. +** e -- envelope ** ** Returns: -** An address descriptor for the controlling user. +** An address descriptor for the controlling user, +** using storage allocated from e->e_rpool. ** -** Side Effects: -** none. */ static ADDRESS * -setctluser(user, qfver) +setctluser(user, qfver, e) char *user; int qfver; + ENVELOPE *e; { register ADDRESS *a; struct passwd *pw; @@ -2939,23 +4888,18 @@ setctluser(user, qfver) ** Set up addr fields for controlling user. */ - a = (ADDRESS *) xalloc(sizeof *a); + a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof *a); memset((char *) a, '\0', sizeof *a); - if (*user == '\0') - { - p = NULL; - a->q_user = newstr(DefUser); - } - else if (*user == ':') + if (*user == ':') { p = &user[1]; - a->q_user = newstr(p); + a->q_user = sm_rpool_strdup_x(e->e_rpool, p); } else { p = strtok(user, ":"); - a->q_user = newstr(user); + a->q_user = sm_rpool_strdup_x(e->e_rpool, user); if (qfver >= 2) { if ((p = strtok(NULL, ":")) != NULL) @@ -2980,7 +4924,7 @@ setctluser(user, qfver) else if (strcmp(pw->pw_dir, "/") == 0) a->q_home = ""; else - a->q_home = newstr(pw->pw_dir); + a->q_home = sm_rpool_strdup_x(e->e_rpool, pw->pw_dir); a->q_uid = pw->pw_uid; a->q_gid = pw->pw_gid; a->q_flags |= QGOODUID; @@ -2990,9 +4934,9 @@ setctluser(user, qfver) a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ a->q_mailer = LocalMailer; if (p == NULL) - a->q_paddr = newstr(a->q_user); + a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user); else - a->q_paddr = newstr(p); + a->q_paddr = sm_rpool_strdup_x(e->e_rpool, p); return a; } /* @@ -3017,15 +4961,44 @@ loseqfile(e, why) if (e == NULL || e->e_id == NULL) return; p = queuename(e, 'q'); - if (strlen(p) >= (SIZE_T) sizeof buf) + if (sm_strlcpy(buf, p, sizeof buf) >= sizeof buf) return; - (void) strlcpy(buf, p, sizeof buf); + if (!bitset(EF_INQUEUE, e->e_flags)) + queueup(e, false, true); p = queuename(e, LOSEQF_LETTER); if (rename(buf, p) < 0) syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid()); else if (LogLevel > 0) sm_syslog(LOG_ALERT, e->e_id, "Losing %s: %s", buf, why); + if (e->e_dfp != NULL) + { + (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); + e->e_dfp = NULL; + } + e->e_flags &= ~EF_HAS_DF; +} +/* +** NAME2QID -- translate a queue group name to a queue group id +** +** Parameters: +** queuename -- name of queue group. +** +** Returns: +** queue group id if found. +** NOQGRP otherwise. +*/ + +int +name2qid(queuename) + char *queuename; +{ + register STAB *s; + + s = stab(queuename, ST_QUEUE, ST_FIND); + if (s == NULL) + return NOQGRP; + return s->s_quegrp->qg_index; } /* ** QID_PRINTNAME -- create externally printable version of queue id @@ -3052,99 +5025,224 @@ qid_printname(e) else id = e->e_id; - if (e->e_queuedir == NOQDIR) + if (e->e_qdir == NOQDIR) return id; - (void) snprintf(idbuf, sizeof idbuf, "%.32s/%s", - QPaths[e->e_queuedir].qp_name, id); + (void) sm_snprintf(idbuf, sizeof idbuf, "%.32s/%s", + Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_name, + id); return idbuf; } /* ** QID_PRINTQUEUE -- create full version of queue directory for df files ** ** Parameters: -** queuedir -- the short version of the queue directory +** qgrp -- index in queue group. +** qdir -- the short version of the queue directory ** ** Returns: -** the full pathname to the queue (static) +** the full pathname to the queue (might point to a static var) */ char * -qid_printqueue(queuedir) - int queuedir; +qid_printqueue(qgrp, qdir) + int qgrp; + int qdir; { char *subdir; static char dir[MAXPATHLEN]; - if (queuedir == NOQDIR) - return QueueDir; + if (qdir == NOQDIR) + return Queue[qgrp]->qg_qdir; - if (strcmp(QPaths[queuedir].qp_name, ".") == 0) + if (strcmp(Queue[qgrp]->qg_qpaths[qdir].qp_name, ".") == 0) subdir = NULL; else - subdir = QPaths[queuedir].qp_name; + subdir = Queue[qgrp]->qg_qpaths[qdir].qp_name; - (void) snprintf(dir, sizeof dir, "%s%s%s%s", QueueDir, + (void) sm_strlcpyn(dir, sizeof dir, 4, + Queue[qgrp]->qg_qdir, subdir == NULL ? "" : "/", subdir == NULL ? "" : subdir, - (bitset(QP_SUBDF, QPaths[queuedir].qp_subdirs) ? "/df" : "")); + (bitset(QP_SUBDF, + Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) + ? "/df" : "")); return dir; } -/* -** SETNEWQUEUE -- Sets a new queue directory + +/* +** PICKQDIR -- Pick a queue directory from a queue group ** -** Assign a queue directory to an envelope and store the directory -** in e->e_queuedir. The queue is chosen at random. +** Parameters: +** qg -- queue group +** fsize -- file size in bytes +** e -- envelope, or NULL ** -** This routine may be improved in the future to allow for more -** elaborate queueing schemes. Suggestions and code contributions -** are welcome. +** Result: +** NOQDIR if no queue directory in qg has enough free space to +** hold a file of size 'fsize', otherwise the index of +** a randomly selected queue directory which resides on a +** file system with enough disk space. +** XXX This could be extended to select a queuedir with +** a few (the fewest?) number of entries. That data +** is available if shared memory is used. +** +** Side Effects: +** If the request fails and e != NULL then sm_syslog is called. +*/ + +int +pickqdir(qg, fsize, e) + QUEUEGRP *qg; + long fsize; + ENVELOPE *e; +{ + int qdir; + int i; + long avail = 0; + + /* Pick a random directory, as a starting point. */ + if (qg->qg_numqueues <= 1) + qdir = 0; + else + qdir = get_rand_mod(qg->qg_numqueues); + + if (MinBlocksFree <= 0 && fsize <= 0) + return qdir; + + /* + ** Now iterate over the queue directories, + ** looking for a directory with enough space for this message. + */ + + i = qdir; + do + { + QPATHS *qp = &qg->qg_qpaths[i]; + long needed = 0; + long fsavail = 0; + + if (fsize > 0) + needed += fsize / FILE_SYS_BLKSIZE(qp->qp_fsysidx) + + ((fsize % FILE_SYS_BLKSIZE(qp->qp_fsysidx) + > 0) ? 1 : 0); + if (MinBlocksFree > 0) + needed += MinBlocksFree; + fsavail = FILE_SYS_AVAIL(qp->qp_fsysidx); +#if SM_CONF_SHM + if (fsavail <= 0) + { + long blksize; + + /* + ** might be not correctly updated, + ** let's try to get the info directly. + */ + + fsavail = freediskspace(FILE_SYS_NAME(qp->qp_fsysidx), + &blksize); + if (fsavail < 0) + fsavail = 0; + } +#endif /* SM_CONF_SHM */ + if (needed <= fsavail) + return i; + if (avail < fsavail) + avail = fsavail; + + if (qg->qg_numqueues > 0) + i = (i + 1) % qg->qg_numqueues; + } while (i != qdir); + + if (e != NULL && LogLevel > 0) + sm_syslog(LOG_ALERT, e->e_id, + "low on space (%s needs %ld bytes + %ld blocks in %s), max avail: %ld", + CurHostName == NULL ? "SMTP-DAEMON" : CurHostName, + fsize, MinBlocksFree, + qg->qg_qdir, avail); + return NOQDIR; +} +/* +** SETNEWQUEUE -- Sets a new queue group and directory +** +** Assign a queue group and directory to an envelope and store the +** directory in e->e_qdir. ** ** Parameters: ** e -- envelope to assign a queue for. ** ** Returns: -** none. +** true if successful +** false otherwise +** +** Side Effects: +** On success, e->e_qgrp and e->e_qdir are non-negative. +** On failure (not enough disk space), +** e->qgrp = NOQGRP, e->e_qdir = NOQDIR +** and usrerr() is invoked (which could raise an exception). */ -void +bool setnewqueue(e) ENVELOPE *e; { - int idx; - if (tTd(41, 20)) - dprintf("setnewqueue: called\n"); + sm_dprintf("setnewqueue: called\n"); + + /* not set somewhere else */ + if (e->e_qgrp == NOQGRP) + { + /* + ** Use the queue group of the first recipient, as set by + ** the "queuegroup" rule set. If that is not defined, then + ** use the queue group of the mailer of the first recipient. + ** If that is not defined either, then use the default + ** queue group. + */ + + if (e->e_sendqueue == NULL) + e->e_qgrp = 0; + else if (e->e_sendqueue->q_qgrp >= 0) + e->e_qgrp = e->e_sendqueue->q_qgrp; + else if (e->e_sendqueue->q_mailer != NULL && + ISVALIDQGRP(e->e_sendqueue->q_mailer->m_qgrp)) + e->e_qgrp = e->e_sendqueue->q_mailer->m_qgrp; + else + e->e_qgrp = 0; + e->e_dfqgrp = e->e_qgrp; + } - if (e->e_queuedir != NOQDIR) + if (ISVALIDQDIR(e->e_qdir) && ISVALIDQDIR(e->e_dfqdir)) { if (tTd(41, 20)) - dprintf("setnewqueue: e_queuedir already assigned (%s)\n", - qid_printqueue(e->e_queuedir)); - return; + sm_dprintf("setnewqueue: e_qdir already assigned (%s)\n", + qid_printqueue(e->e_qgrp, e->e_qdir)); + return true; } - if (NumQueues <= 1) - idx = 0; - else + filesys_update(); + e->e_qdir = pickqdir(Queue[e->e_qgrp], e->e_msgsize, e); + if (e->e_qdir == NOQDIR) { -#if RANDOMSHIFT - /* lower bits are not random "enough", select others */ - idx = (get_random() >> RANDOMSHIFT) % NumQueues; -#else /* RANDOMSHIFT */ - idx = get_random() % NumQueues; -#endif /* RANDOMSHIFT */ - if (tTd(41, 15)) - dprintf("setnewqueue: get_random() %% %d = %d\n", - NumQueues, idx); + e->e_qgrp = NOQGRP; + if (!bitset(EF_FATALERRS, e->e_flags)) + usrerr("452 4.4.5 Insufficient disk space; try again later"); + e->e_flags |= EF_FATALERRS; + return false; } - e->e_queuedir = idx; if (tTd(41, 3)) - dprintf("setnewqueue: Assigned queue directory %s\n", - qid_printqueue(e->e_queuedir)); -} + sm_dprintf("setnewqueue: Assigned queue directory %s\n", + qid_printqueue(e->e_qgrp, e->e_qdir)); + if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR) + { + e->e_xfqgrp = e->e_qgrp; + e->e_xfqdir = e->e_qdir; + } + e->e_dfqdir = e->e_qdir; + return true; +} /* ** CHKQDIR -- check a queue directory ** @@ -3167,51 +5265,52 @@ chkqdir(name, sff) /* skip over . and .. directories */ if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) - return FALSE; -# if HASLSTAT + return false; +#if HASLSTAT if (lstat(name, &statb) < 0) -# else /* HASLSTAT */ +#else /* HASLSTAT */ if (stat(name, &statb) < 0) -# endif /* HASLSTAT */ +#endif /* HASLSTAT */ { if (tTd(41, 2)) - dprintf("multiqueue_cache: stat(\"%s\"): %s\n", - name, errstring(errno)); - return FALSE; + sm_dprintf("chkqdir: stat(\"%s\"): %s\n", + name, sm_errstring(errno)); + return false; } -# if HASLSTAT +#if HASLSTAT if (S_ISLNK(statb.st_mode)) { /* ** For a symlink we need to make sure the ** target is a directory */ + if (stat(name, &statb) < 0) { if (tTd(41, 2)) - dprintf("multiqueue_cache: stat(\"%s\"): %s\n", - name, errstring(errno)); - return FALSE; + sm_dprintf("chkqdir: stat(\"%s\"): %s\n", + name, sm_errstring(errno)); + return false; } } -# endif /* HASLSTAT */ +#endif /* HASLSTAT */ if (!S_ISDIR(statb.st_mode)) { if (tTd(41, 2)) - dprintf("multiqueue_cache: \"%s\": Not a directory\n", + sm_dprintf("chkqdir: \"%s\": Not a directory\n", name); - return FALSE; + return false; } /* Print a warning if unsafe (but still use it) */ + /* XXX do this only if we want the warning? */ i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0); if (i != 0 && tTd(41, 2)) - dprintf("multiqueue_cache: \"%s\": Not safe: %s\n", - name, errstring(i)); - return TRUE; + sm_dprintf("chkqdir: \"%s\": Not safe: %s\n", + name, sm_errstring(i)); + return true; } - /* ** MULTIQUEUE_CACHE -- cache a list of paths to queues. ** @@ -3221,205 +5320,1208 @@ chkqdir(name, sff) ** (although code for that is not ready yet). ** ** Parameters: -** none +** basedir -- base of all queue directories. +** blen -- strlen(basedir). +** qg -- queue group. +** qn -- number of queue directories already cached. +** phash -- pointer to hash value over queue dirs. +#if SM_CONF_SHM +** only used if shared memory is active. +#endif * SM_CONF_SHM * ** ** Returns: -** none +** new number of queue directories. */ -void -multiqueue_cache() +#define INITIAL_SLOTS 20 +#define ADD_SLOTS 10 + +static int +multiqueue_cache(basedir, blen, qg, qn, phash) + char *basedir; + int blen; + QUEUEGRP *qg; + int qn; + unsigned int *phash; { - register DIR *dp; - register struct dirent *d; char *cp; int i, len; int slotsleft = 0; long sff = SFF_ANYFILE; char qpath[MAXPATHLEN]; char subdir[MAXPATHLEN]; + char prefix[MAXPATHLEN]; /* dir relative to basedir */ if (tTd(41, 20)) - dprintf("multiqueue_cache: called\n"); + sm_dprintf("multiqueue_cache: called\n"); - if (NumQueues != 0 && QPaths != NULL) + /* Initialize to current directory */ + prefix[0] = '.'; + prefix[1] = '\0'; + if (qg->qg_numqueues != 0 && qg->qg_qpaths != NULL) { - for (i = 0; i < NumQueues; i++) + for (i = 0; i < qg->qg_numqueues; i++) { - if (QPaths[i].qp_name != NULL) - sm_free(QPaths[i].qp_name); + if (qg->qg_qpaths[i].qp_name != NULL) + (void) sm_free(qg->qg_qpaths[i].qp_name); /* XXX */ } - sm_free((char *)QPaths); - QPaths = NULL; - NumQueues = 0; + (void) sm_free((char *) qg->qg_qpaths); /* XXX */ + qg->qg_qpaths = NULL; + qg->qg_numqueues = 0; } /* If running as root, allow safedirpath() checks to use privs */ if (RunAsUid == 0) sff |= SFF_ROOTOK; - (void) snprintf(qpath, sizeof qpath, "%s", QueueDir); - len = strlen(qpath) - 1; - cp = &qpath[len]; + if (!SM_IS_DIR_START(qg->qg_qdir)) + { + /* + ** XXX we could add basedir, but then we have to realloc() + ** the string... Maybe another time. + */ + + syserr("QueuePath %s not absolute", qg->qg_qdir); + ExitStat = EX_CONFIG; + return qn; + } + + /* qpath: directory of current workgroup */ + len = sm_strlcpy(qpath, qg->qg_qdir, sizeof qpath); + if (len >= sizeof qpath) + { + syserr("QueuePath %.256s too long (%d max)", + qg->qg_qdir, (int) sizeof qpath); + ExitStat = EX_CONFIG; + return qn; + } + + /* begin of qpath must be same as basedir */ + if (strncmp(basedir, qpath, blen) != 0 && + (strncmp(basedir, qpath, blen - 1) != 0 || len != blen - 1)) + { + syserr("QueuePath %s not subpath of QueueDirectory %s", + qpath, basedir); + ExitStat = EX_CONFIG; + return qn; + } + + /* Do we have a nested subdirectory? */ + if (blen < len && SM_FIRST_DIR_DELIM(qg->qg_qdir + blen) != NULL) + { + + /* Copy subdirectory into prefix for later use */ + if (sm_strlcpy(prefix, qg->qg_qdir + blen, sizeof prefix) >= + sizeof prefix) + { + syserr("QueuePath %.256s too long (%d max)", + qg->qg_qdir, (int) sizeof qpath); + ExitStat = EX_CONFIG; + return qn; + } + cp = SM_LAST_DIR_DELIM(prefix); + SM_ASSERT(cp != NULL); + *cp = '\0'; /* cut off trailing / */ + } + + /* This is guaranteed by the basedir check above */ + SM_ASSERT(len >= blen - 1); + cp = &qpath[len - 1]; if (*cp == '*') { - *cp = '\0'; - if ((cp = strrchr(qpath, '/')) == NULL) + register DIR *dp; + register struct dirent *d; + int off; + char *delim; + char relpath[MAXPATHLEN]; + + *cp = '\0'; /* Overwrite wildcard */ + if ((cp = SM_LAST_DIR_DELIM(qpath)) == NULL) { syserr("QueueDirectory: can not wildcard relative path"); if (tTd(41, 2)) - dprintf("multiqueue_cache: \"%s\": Can not wildcard relative path.\n", + sm_dprintf("multiqueue_cache: \"%s*\": Can not wildcard relative path.\n", qpath); ExitStat = EX_CONFIG; - return; + return qn; } if (cp == qpath) { /* ** Special case of top level wildcard, like /foo* + ** Change to //foo* */ - (void) snprintf(qpath + 1, sizeof qpath - 1, - "%s", qpath); + (void) sm_strlcpy(qpath + 1, qpath, sizeof qpath - 1); ++cp; } - *(cp++) = '\0'; - len = strlen(cp); + delim = cp; + *(cp++) = '\0'; /* Replace / with \0 */ + len = strlen(cp); /* Last component of queue directory */ + + /* + ** Path relative to basedir, with trailing / + ** It will be modified below to specify the subdirectories + ** so they can be opened without chdir(). + */ + + off = sm_strlcpyn(relpath, sizeof relpath, 2, prefix, "/"); + SM_ASSERT(off < sizeof relpath); if (tTd(41, 2)) - dprintf("multiqueue_cache: prefix=\"%s\"\n", cp); + sm_dprintf("multiqueue_cache: prefix=\"%s%s\"\n", + relpath, cp); - QueueDir = newstr(qpath); + /* It is always basedir: we don't need to store it per group */ + /* XXX: optimize this! -> one more global? */ + qg->qg_qdir = newstr(basedir); + qg->qg_qdir[blen - 1] = '\0'; /* cut off trailing / */ /* ** XXX Should probably wrap this whole loop in a timeout ** in case some wag decides to NFS mount the queues. */ - /* test path to get warning messages */ - i= safedirpath(QueueDir, RunAsUid, RunAsGid, NULL, sff, 0, 0); - if (i != 0 && tTd(41, 2)) - dprintf("multiqueue_cache: \"%s\": Not safe: %s\n", - QueueDir, errstring(i)); - - if (chdir(QueueDir) < 0) + /* Test path to get warning messages. */ + if (qn == 0) { - syserr("can not chdir(%s)", QueueDir); - if (tTd(41, 2)) - dprintf("multiqueue_cache: \"%s\": %s\n", - qpath, errstring(errno)); - ExitStat = EX_CONFIG; - return; + /* XXX qg_runasuid and qg_runasgid for specials? */ + i = safedirpath(basedir, RunAsUid, RunAsGid, NULL, + sff, 0, 0); + if (i != 0 && tTd(41, 2)) + sm_dprintf("multiqueue_cache: \"%s\": Not safe: %s\n", + basedir, sm_errstring(i)); } - if ((dp = opendir(".")) == NULL) + if ((dp = opendir(prefix)) == NULL) { - syserr("can not opendir(%s)", QueueDir); + syserr("can not opendir(%s/%s)", qg->qg_qdir, prefix); if (tTd(41, 2)) - dprintf("multiqueue_cache: opendir(\"%s\"): %s\n", - QueueDir, errstring(errno)); + sm_dprintf("multiqueue_cache: opendir(\"%s/%s\"): %s\n", + qg->qg_qdir, prefix, + sm_errstring(errno)); ExitStat = EX_CONFIG; - return; + return qn; } while ((d = readdir(dp)) != NULL) { - if (strncmp(d->d_name, cp, len) != 0) + i = strlen(d->d_name); + if (i < len || strncmp(d->d_name, cp, len) != 0) { if (tTd(41, 5)) - dprintf("multiqueue_cache: \"%s\", skipped\n", + sm_dprintf("multiqueue_cache: \"%s\", skipped\n", d->d_name); continue; } - if (!chkqdir(d->d_name, sff)) + + /* Create relative pathname: prefix + local directory */ + i = sizeof(relpath) - off; + if (sm_strlcpy(relpath + off, d->d_name, i) >= i) + continue; /* way too long */ + + if (!chkqdir(relpath, sff)) continue; - if (QPaths == NULL) + if (qg->qg_qpaths == NULL) { - slotsleft = 20; - QPaths = (QPATHS *)xalloc((sizeof *QPaths) * - slotsleft); - NumQueues = 0; + slotsleft = INITIAL_SLOTS; + qg->qg_qpaths = (QPATHS *)xalloc((sizeof *qg->qg_qpaths) * + slotsleft); + qg->qg_numqueues = 0; } else if (slotsleft < 1) { - QPaths = (QPATHS *)xrealloc((char *)QPaths, - (sizeof *QPaths) * - (NumQueues + 10)); - if (QPaths == NULL) + qg->qg_qpaths = (QPATHS *)sm_realloc((char *)qg->qg_qpaths, + (sizeof *qg->qg_qpaths) * + (qg->qg_numqueues + + ADD_SLOTS)); + if (qg->qg_qpaths == NULL) { (void) closedir(dp); - return; + return qn; } - slotsleft += 10; + slotsleft += ADD_SLOTS; } /* check subdirs */ - QPaths[NumQueues].qp_subdirs = QP_NOSUB; - (void) snprintf(subdir, sizeof subdir, "%s/%s/%s", - qpath, d->d_name, "qf"); - if (chkqdir(subdir, sff)) - QPaths[NumQueues].qp_subdirs |= QP_SUBQF; - - (void) snprintf(subdir, sizeof subdir, "%s/%s/%s", - qpath, d->d_name, "df"); - if (chkqdir(subdir, sff)) - QPaths[NumQueues].qp_subdirs |= QP_SUBDF; - - (void) snprintf(subdir, sizeof subdir, "%s/%s/%s", - qpath, d->d_name, "xf"); - if (chkqdir(subdir, sff)) - QPaths[NumQueues].qp_subdirs |= QP_SUBXF; + qg->qg_qpaths[qg->qg_numqueues].qp_subdirs = QP_NOSUB; + +#define CHKRSUBDIR(name, flag) \ + (void) sm_strlcpyn(subdir, sizeof subdir, 3, relpath, "/", name); \ + if (chkqdir(subdir, sff)) \ + qg->qg_qpaths[qg->qg_numqueues].qp_subdirs |= flag; \ + else + + + CHKRSUBDIR("qf", QP_SUBQF); + CHKRSUBDIR("df", QP_SUBDF); + CHKRSUBDIR("xf", QP_SUBXF); /* assert(strlen(d->d_name) < MAXPATHLEN - 14) */ /* maybe even - 17 (subdirs) */ - QPaths[NumQueues].qp_name = newstr(d->d_name); + + if (prefix[0] != '.') + qg->qg_qpaths[qg->qg_numqueues].qp_name = + newstr(relpath); + else + qg->qg_qpaths[qg->qg_numqueues].qp_name = + newstr(d->d_name); + if (tTd(41, 2)) - dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n", - NumQueues, d->d_name, - QPaths[NumQueues].qp_subdirs); - NumQueues++; + sm_dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n", + qg->qg_numqueues, relpath, + qg->qg_qpaths[qg->qg_numqueues].qp_subdirs); +#if SM_CONF_SHM + qg->qg_qpaths[qg->qg_numqueues].qp_idx = qn; + *phash = hash_q(relpath, *phash); +#endif /* SM_CONF_SHM */ + qg->qg_numqueues++; + ++qn; slotsleft--; } (void) closedir(dp); + + /* undo damage */ + *delim = '/'; } - if (NumQueues == 0) + if (qg->qg_numqueues == 0) { - if (*cp != '*' && tTd(41, 2)) - dprintf("multiqueue_cache: \"%s\": No wildcard suffix character\n", - QueueDir); - QPaths = (QPATHS *)xalloc(sizeof *QPaths); - QPaths[0].qp_name = newstr("."); - QPaths[0].qp_subdirs = QP_NOSUB; - NumQueues = 1; + qg->qg_qpaths = (QPATHS *) xalloc(sizeof *qg->qg_qpaths); /* test path to get warning messages */ - (void) safedirpath(QueueDir, RunAsUid, RunAsGid, - NULL, sff, 0, 0); - if (chdir(QueueDir) < 0) + i = safedirpath(qpath, RunAsUid, RunAsGid, NULL, sff, 0, 0); + if (i == ENOENT) { - syserr("can not chdir(%s)", QueueDir); + syserr("can not opendir(%s)", qpath); if (tTd(41, 2)) - dprintf("multiqueue_cache: \"%s\": %s\n", - QueueDir, errstring(errno)); + sm_dprintf("multiqueue_cache: opendir(\"%s\"): %s\n", + qpath, sm_errstring(i)); ExitStat = EX_CONFIG; + return qn; } + qg->qg_qpaths[0].qp_subdirs = QP_NOSUB; + qg->qg_numqueues = 1; + /* check subdirs */ - (void) snprintf(subdir, sizeof subdir, "%s/qf", QueueDir); - if (chkqdir(subdir, sff)) - QPaths[0].qp_subdirs |= QP_SUBQF; +#define CHKSUBDIR(name, flag) \ + (void) sm_strlcpyn(subdir, sizeof subdir, 3, qg->qg_qdir, "/", name); \ + if (chkqdir(subdir, sff)) \ + qg->qg_qpaths[0].qp_subdirs |= flag; \ + else + + CHKSUBDIR("qf", QP_SUBQF); + CHKSUBDIR("df", QP_SUBDF); + CHKSUBDIR("xf", QP_SUBXF); + + if (qg->qg_qdir[blen - 1] != '\0' && + qg->qg_qdir[blen] != '\0') + { + /* + ** Copy the last component into qpaths and + ** cut off qdir + */ + + qg->qg_qpaths[0].qp_name = newstr(qg->qg_qdir + blen); + qg->qg_qdir[blen - 1] = '\0'; + } + else + qg->qg_qpaths[0].qp_name = newstr("."); + +#if SM_CONF_SHM + qg->qg_qpaths[0].qp_idx = qn; + *phash = hash_q(qg->qg_qpaths[0].qp_name, *phash); +#endif /* SM_CONF_SHM */ + ++qn; + } + return qn; +} + +/* +** FILESYS_FIND -- find entry in FileSys table, or add new one +** +** Given the pathname of a directory, determine the file system +** in which that directory resides, and return a pointer to the +** entry in the FileSys table that describes the file system. +** A new entry is added if necessary (and requested). +** If the directory does not exist, -1 is returned. +** +** Parameters: +** path -- pathname of directory +** add -- add to structure if not found. +** +** Returns: +** >=0: found: index in file system table +** <0: some error, i.e., +** FSF_TOO_MANY: too many filesystems (-> syserr()) +** FSF_STAT_FAIL: can't stat() filesystem (-> syserr()) +** FSF_NOT_FOUND: not in list +*/ + +static short filesys_find __P((char *, bool)); + +#define FSF_NOT_FOUND (-1) +#define FSF_STAT_FAIL (-2) +#define FSF_TOO_MANY (-3) + +static short +filesys_find(path, add) + char *path; + bool add; +{ + struct stat st; + short i; + + if (stat(path, &st) < 0) + { + syserr("cannot stat queue directory %s", path); + return FSF_STAT_FAIL; + } + for (i = 0; i < NumFileSys; ++i) + { + if (FILE_SYS_DEV(i) == st.st_dev) + return i; + } + if (i >= MAXFILESYS) + { + syserr("too many queue file systems (%d max)", MAXFILESYS); + return FSF_TOO_MANY; + } + if (!add) + return FSF_NOT_FOUND; + + ++NumFileSys; + FILE_SYS_NAME(i) = path; + FILE_SYS_DEV(i) = st.st_dev; + FILE_SYS_AVAIL(i) = 0; + FILE_SYS_BLKSIZE(i) = 1024; /* avoid divide by zero */ + return i; +} + +/* +** FILESYS_SETUP -- set up mapping from queue directories to file systems +** +** This data structure is used to efficiently check the amount of +** free space available in a set of queue directories. +** +** Parameters: +** add -- initialize structure if necessary. +** +** Returns: +** 0: success +** <0: some error, i.e., +** FSF_NOT_FOUND: not in list +** FSF_STAT_FAIL: can't stat() filesystem (-> syserr()) +** FSF_TOO_MANY: too many filesystems (-> syserr()) +*/ + +static int filesys_setup __P((bool)); + +static int +filesys_setup(add) + bool add; +{ + int i, j; + short fs; + int ret; + + ret = 0; + for (i = 0; i < NumQueue && Queue[i] != NULL; i++) + { + for (j = 0; j < Queue[i]->qg_numqueues; ++j) + { + QPATHS *qp = &Queue[i]->qg_qpaths[j]; + + fs = filesys_find(qp->qp_name, add); + if (fs >= 0) + qp->qp_fsysidx = fs; + else + qp->qp_fsysidx = 0; + if (fs < ret) + ret = fs; + } + } + return ret; +} + +/* +** FILESYS_UPDATE -- update amount of free space on all file systems +** +** The FileSys table is used to cache the amount of free space +** available on all queue directory file systems. +** This function updates the cached information if it has expired. +** +** Parameters: +** none. +** +** Returns: +** none. +** +** Side Effects: +** Updates FileSys table. +*/ + +void +filesys_update() +{ + int i; + long avail, blksize; + time_t now; + static time_t nextupdate = 0; + +#if SM_CONF_SHM + /* only the daemon updates this structure */ + if (ShmId != SM_SHM_NO_ID && DaemonPid != CurrentPid) + return; +#endif /* SM_CONF_SHM */ + now = curtime(); + if (now < nextupdate) + return; + nextupdate = now + FILESYS_UPDATE_INTERVAL; + for (i = 0; i < NumFileSys; ++i) + { + FILESYS *fs = &FILE_SYS(i); + + avail = freediskspace(FILE_SYS_NAME(i), &blksize); + if (avail < 0 || blksize <= 0) + { + if (LogLevel > 5) + sm_syslog(LOG_ERR, NOQID, + "filesys_update failed: %s, fs=%s, avail=%ld, blocksize=%ld", + sm_errstring(errno), + FILE_SYS_NAME(i), avail, blksize); + fs->fs_avail = 0; + fs->fs_blksize = 1024; /* avoid divide by zero */ + nextupdate = now + 2; /* let's do this soon again */ + } + else + { + fs->fs_avail = avail; + fs->fs_blksize = blksize; + } + } +} + +#if _FFR_ANY_FREE_FS +/* +** FILESYS_FREE -- check whether there is at least one fs with enough space. +** +** Parameters: +** fsize -- file size in bytes +** +** Returns: +** true iff there is one fs with more than fsize bytes free. +*/ + +bool +filesys_free(fsize) + long fsize; +{ + int i; + + if (fsize <= 0) + return true; + for (i = 0; i < NumFileSys; ++i) + { + long needed = 0; + + if (FILE_SYS_AVAIL(i) < 0 || FILE_SYS_BLKSIZE(i) <= 0) + continue; + needed += fsize / FILE_SYS_BLKSIZE(i) + + ((fsize % FILE_SYS_BLKSIZE(i) + > 0) ? 1 : 0) + + MinBlocksFree; + if (needed <= FILE_SYS_AVAIL(i)) + return true; + } + return false; +} +#endif /* _FFR_ANY_FREE_FS */ + +#if _FFR_CONTROL_MSTAT +/* +** DISK_STATUS -- show amount of free space in queue directories +** +** Parameters: +** out -- output file pointer. +** prefix -- string to output in front of each line. +** +** Returns: +** none. +*/ + +void +disk_status(out, prefix) + SM_FILE_T *out; + char *prefix; +{ + int i; + long avail, blksize; + long free; + + for (i = 0; i < NumFileSys; ++i) + { + avail = freediskspace(FILE_SYS_NAME(i), &blksize); + if (avail >= 0 && blksize > 0) + { + free = (long)((double) avail * + ((double) blksize / 1024)); + } + else + free = -1; + (void) sm_io_fprintf(out, SM_TIME_DEFAULT, + "%s%d/%s/%ld\r\n", + prefix, i, + FILE_SYS_NAME(i), + free); + } +} +#endif /* _FFR_CONTROL_MSTAT */ + +#if SM_CONF_SHM +/* +** UPD_QS -- update information about queue when adding/deleting an entry +** +** Parameters: +** e -- envelope. +** delete -- delete/add entry. +** avail -- update the space available as well. +** +** Returns: +** none. +** +** Side Effects: +** Modifies available space in filesystem. +** Changes number of entries in queue directory. +*/ + +void +upd_qs(e, delete, avail) + ENVELOPE *e; + bool delete; + bool avail; +{ + short fidx; + int idx; + long s; + + if (ShmId == SM_SHM_NO_ID || e == NULL) + return; + if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR) + return; + idx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_idx; + + /* XXX in theory this needs to be protected with a mutex */ + if (QSHM_ENTRIES(idx) >= 0) + { + if (delete) + --QSHM_ENTRIES(idx); + else + ++QSHM_ENTRIES(idx); + } + + fidx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_fsysidx; + if (fidx < 0) + return; + + /* update available space also? (might be loseqfile) */ + if (!avail) + return; + + /* convert size to blocks; this causes rounding errors */ + s = e->e_msgsize / FILE_SYS_BLKSIZE(fidx); + if (s == 0) + return; + + /* XXX in theory this needs to be protected with a mutex */ + if (delete) + FILE_SYS_AVAIL(fidx) += s; + else + FILE_SYS_AVAIL(fidx) -= s; + +} +/* +** INIT_SHM -- initialize shared memory structure +** +** Initialize or attach to shared memory segment. +** Currently it is not a fatal error if this doesn't work. +** However, it causes us to have a "fallback" storage location +** for everything that is supposed to be in the shared memory, +** which makes the code slightly ugly. +** +** Parameters: +** qn -- number of queue directories. +** owner -- owner of shared memory. +** hash -- identifies data that is stored in shared memory. +** +** Returns: +** none. +*/ + +static void init_shm __P((int, bool, unsigned int)); + +static void +init_shm(qn, owner, hash) + int qn; + bool owner; + unsigned int hash; +{ + int i; + + PtrFileSys = &FileSys[0]; + PNumFileSys = &Numfilesys; + + /* This allows us to disable shared memory at runtime. */ + if (ShmKey != 0) + { + int count; + int save_errno; + size_t shms; + + count = 0; + shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T); + for (;;) + { + /* XXX: maybe allow read access for group? */ + Pshm = sm_shmstart(ShmKey, shms, SHM_R|SHM_W, &ShmId, + owner); + save_errno = errno; + if (Pshm != NULL || save_errno != EEXIST) + break; + if (++count >= 3) + break; + sleep(count); + } + if (Pshm != NULL) + { + int *p; + + p = (int *) Pshm; + if (owner) + { + *p = (int) shms; + *((pid_t *) SHM_OFF_PID(Pshm)) = CurrentPid; + p = (int *) SHM_OFF_TAG(Pshm); + *p = hash; + } + else + { + if (*p != (int) shms) + { + save_errno = EINVAL; + cleanup_shm(false); + goto error; + } + p = (int *) SHM_OFF_TAG(Pshm); + if (*p != (int) hash) + { + save_errno = EINVAL; + cleanup_shm(false); + goto error; + } + + /* + ** XXX how to check the pid? + ** Read it from the pid-file? That does + ** not need to exist. + ** We could disable shm if we can't confirm + ** that it is the right one. + */ + } + + PtrFileSys = (FILESYS *) OFF_FILE_SYS(Pshm); + PNumFileSys = (int *) OFF_NUM_FILE_SYS(Pshm); + QShm = (QUEUE_SHM_T *) OFF_QUEUE_SHM(Pshm); + PRSATmpCnt = (int *) OFF_RSA_TMP_CNT(Pshm); + *PRSATmpCnt = 0; + if (owner) + { + /* initialize values in shared memory */ + NumFileSys = 0; + for (i = 0; i < qn; i++) + QShm[i].qs_entries = -1; + } + return; + } + error: + if (LogLevel > (owner ? 8 : 11)) + { + sm_syslog(owner ? LOG_ERR : LOG_NOTICE, NOQID, + "can't %s shared memory, key=%ld: %s", + owner ? "initialize" : "attach to", + (long) ShmKey, sm_errstring(save_errno)); + } + } +} +#endif /* SM_CONF_SHM */ + +/* +** SETUP_QUEUES -- setup all queue groups +** +** Parameters: +** owner -- owner of shared memory. +** +** Returns: +** none. +** +#if SM_CONF_SHM +** Side Effects: +** attaches shared memory. +#endif * SM_CONF_SHM * +*/ + +void +setup_queues(owner) + bool owner; +{ + int i, qn, len; + unsigned int hashval; + char basedir[MAXPATHLEN]; + struct stat st; + + /* + ** Determine basedir for all queue directories. + ** All queue directories must be (first level) subdirectories + ** of the basedir. The basedir is the QueueDir + ** without wildcards, but with trailing / + */ + + hashval = 0; + errno = 0; + len = sm_strlcpy(basedir, QueueDir, sizeof basedir); + if (len >= sizeof basedir) + { + syserr("QueueDirectory: path too long: %d, max %d", + len, (int) sizeof basedir); + ExitStat = EX_CONFIG; + return; + } + SM_ASSERT(len > 0); + if (basedir[len - 1] == '*') + { + char *cp; + + cp = SM_LAST_DIR_DELIM(basedir); + if (cp == NULL) + { + syserr("QueueDirectory: can not wildcard relative path \"%s\"", + QueueDir); + if (tTd(41, 2)) + sm_dprintf("setup_queues: \"%s\": Can not wildcard relative path.\n", + QueueDir); + ExitStat = EX_CONFIG; + return; + } + + /* cut off wildcard pattern */ + *++cp = '\0'; + len = cp - basedir; + } + else if (!SM_IS_DIR_DELIM(basedir[len - 1])) + { + /* append trailing slash since it is a directory */ + basedir[len] = '/'; + basedir[++len] = '\0'; + } + + /* len counts up to the last directory delimiter */ + SM_ASSERT(basedir[len - 1] == '/'); - (void) snprintf(subdir, sizeof subdir, "%s/df", QueueDir); - if (chkqdir(subdir, sff)) - QPaths[0].qp_subdirs |= QP_SUBDF; + if (chdir(basedir) < 0) + { + int save_errno = errno; - (void) snprintf(subdir, sizeof subdir, "%s/xf", QueueDir); - if (chkqdir(subdir, sff)) - QPaths[0].qp_subdirs |= QP_SUBXF; + syserr("can not chdir(%s)", basedir); + if (save_errno == EACCES) + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "Program mode requires special privileges, e.g., root or TrustedUser.\n"); + if (tTd(41, 2)) + sm_dprintf("setup_queues: \"%s\": %s\n", + basedir, sm_errstring(errno)); + ExitStat = EX_CONFIG; + return; } +#if SM_CONF_SHM + hashval = hash_q(basedir, hashval); +#endif /* SM_CONF_SHM */ + + /* initialize map for queue runs */ + clrbitmap(DoQueueRun); + + + if (UseMSP && OpMode != MD_TEST) + { + long sff = SFF_CREAT; + + if (stat(".", &st) < 0) + { + syserr("can not stat(%s)", basedir); + if (tTd(41, 2)) + sm_dprintf("setup_queues: \"%s\": %s\n", + basedir, sm_errstring(errno)); + ExitStat = EX_CONFIG; + return; + } + if (RunAsUid == 0) + sff |= SFF_ROOTOK; + + /* + ** Check queue directory permissions. + ** Can we write to a group writable queue directory? + */ + + if (bitset(S_IWGRP, QueueFileMode) && + bitset(S_IWGRP, st.st_mode) && + safefile(" ", RunAsUid, RunAsGid, RunAsUserName, sff, + QueueFileMode, NULL) != 0) + { + syserr("can not write to queue directory %s (RunAsGid=%d, required=%d)", + basedir, (int) RunAsGid, (int) st.st_gid); + } + } + + /* initial number of queue directories */ + qn = 0; + for (i = 0; i < NumQueue && Queue[i] != NULL; i++) + qn = multiqueue_cache(basedir, len, Queue[i], qn, &hashval); + +#if SM_CONF_SHM + init_shm(qn, owner, hashval); + i = filesys_setup(owner || ShmId == SM_SHM_NO_ID); + if (i == FSF_NOT_FOUND) + { + /* + ** We didn't get the right filesystem data + ** This may happen if we don't have the right shared memory. + ** So let's do this without shared memory. + */ + + SM_ASSERT(!owner); + cleanup_shm(false); /* release shared memory */ + i = filesys_setup(false); + if (i < 0) + syserr("filesys_setup failed twice, result=%d", i); + else if (LogLevel > 8) + sm_syslog(LOG_WARNING, NOQID, + "shared memory does not contain expected data, ignored"); + } +#else /* SM_CONF_SHM */ + i = filesys_setup(true); +#endif /* SM_CONF_SHM */ + if (i < 0) + ExitStat = EX_CONFIG; } -# if 0 +#if SM_CONF_SHM +/* +** CLEANUP_SHM -- do some cleanup work for shared memory etc +** +** Parameters: +** owner -- owner of shared memory? +** +** Returns: +** none. +** +** Side Effects: +** detaches shared memory. +*/ + +void +cleanup_shm(owner) + bool owner; +{ + if (ShmId != SM_SHM_NO_ID) + { + if (sm_shmstop(Pshm, ShmId, owner) < 0 && LogLevel > 8) + sm_syslog(LOG_INFO, NOQID, "sh_shmstop failed=%s", + sm_errstring(errno)); + Pshm = NULL; + ShmId = SM_SHM_NO_ID; + } +} +#endif /* SM_CONF_SHM */ + +/* +** CLEANUP_QUEUES -- do some cleanup work for queues +** +** Parameters: +** none. +** +** Returns: +** none. +** +*/ + +void +cleanup_queues() +{ + sync_queue_time(); +} +/* +** SET_DEF_QUEUEVAL -- set default values for a queue group. +** +** Parameters: +** qg -- queue group +** all -- set all values (true for default group)? +** +** Returns: +** none. +** +** Side Effects: +** sets default values for the queue group. +*/ + +void +set_def_queueval(qg, all) + QUEUEGRP *qg; + bool all; +{ + if (bitnset(QD_DEFINED, qg->qg_flags)) + return; + if (all) + qg->qg_qdir = QueueDir; +#if 0 + qg->qg_sortorder = QueueSortOrder; +#endif /* 0 */ + qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1; + qg->qg_nice = NiceQueueRun; +} +/* +** MAKEQUEUE -- define a new queue. +** +** Parameters: +** line -- description of queue. This is in labeled fields. +** The fields are: +** F -- the flags associated with the queue +** I -- the interval between running the queue +** J -- the maximum # of jobs in work list +** [M -- the maximum # of jobs in a queue run] +** N -- the niceness at which to run +** P -- the path to the queue +** S -- the queue sorting order +** R -- number of parallel queue runners +** r -- max recipients per envelope +** The first word is the canonical name of the queue. +** qdef -- this is a 'Q' definition from .cf +** +** Returns: +** none. +** +** Side Effects: +** enters the queue into the queue table. +*/ + +void +makequeue(line, qdef) + char *line; + bool qdef; +{ + register char *p; + register QUEUEGRP *qg; + register STAB *s; + int i; + char fcode; + + /* allocate a queue and set up defaults */ + qg = (QUEUEGRP *) xalloc(sizeof *qg); + memset((char *) qg, '\0', sizeof *qg); + + if (line[0] == '\0') + { + syserr("name required for queue"); + return; + } + + /* collect the queue name */ + for (p = line; + *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); + p++) + continue; + if (*p != '\0') + *p++ = '\0'; + qg->qg_name = newstr(line); + + /* set default values, can be overridden below */ + set_def_queueval(qg, false); + + /* now scan through and assign info from the fields */ + while (*p != '\0') + { + auto char *delimptr; + + while (*p != '\0' && + (*p == ',' || (isascii(*p) && isspace(*p)))) + p++; + + /* p now points to field code */ + fcode = *p; + while (*p != '\0' && *p != '=' && *p != ',') + p++; + if (*p++ != '=') + { + syserr("queue %s: `=' expected", qg->qg_name); + return; + } + while (isascii(*p) && isspace(*p)) + p++; + + /* p now points to the field body */ + p = munchstring(p, &delimptr, ','); + + /* install the field into the queue struct */ + switch (fcode) + { + case 'P': /* pathname */ + if (*p == '\0') + syserr("queue %s: empty path name", + qg->qg_name); + else + qg->qg_qdir = newstr(p); + break; + + case 'F': /* flags */ + for (; *p != '\0'; p++) + if (!(isascii(*p) && isspace(*p))) + setbitn(*p, qg->qg_flags); + break; + + /* + ** Do we need two intervals here: + ** One for persistent queue runners, + ** one for "normal" queue runs? + */ + + case 'I': /* interval between running the queue */ + qg->qg_queueintvl = convtime(p, 'm'); + break; + + case 'N': /* run niceness */ + qg->qg_nice = atoi(p); + break; + + case 'R': /* maximum # of runners for the group */ + i = atoi(p); + + /* can't have more runners than allowed total */ + if (MaxQueueChildren > 0 && i > MaxQueueChildren) + { + qg->qg_maxqrun = MaxQueueChildren; + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Q=%s: R=%d exceeds MaxQueueChildren=%d, set to MaxQueueChildren\n", + qg->qg_name, i, + MaxQueueChildren); + } + else + qg->qg_maxqrun = i; + break; + + case 'J': /* maximum # of jobs in work list */ + qg->qg_maxlist = atoi(p); + break; + + case 'r': /* max recipients per envelope */ + qg->qg_maxrcpt = atoi(p); + break; + +#if 0 + case 'S': /* queue sorting order */ + switch (*p) + { + case 'h': /* Host first */ + case 'H': + qg->qg_sortorder = QSO_BYHOST; + break; + + case 'p': /* Priority order */ + case 'P': + qg->qg_sortorder = QSO_BYPRIORITY; + break; + + case 't': /* Submission time */ + case 'T': + qg->qg_sortorder = QSO_BYTIME; + break; + + case 'f': /* File name */ + case 'F': + qg->qg_sortorder = QSO_BYFILENAME; + break; + + case 'm': /* Modification time */ + case 'M': + qgrp->qg_sortorder = QSO_BYMODTIME; + break; + + default: + syserr("Invalid queue sort order \"%s\"", p); + } + break; +#endif /* 0 */ + + default: + syserr("Q%s: unknown queue equate %c=", + qg->qg_name, fcode); + break; + } + + p = delimptr; + } + + /* do some rationality checking */ + if (NumQueue >= MAXQUEUEGROUPS) + { + syserr("too many queue groups defined (%d max)", + MAXQUEUEGROUPS); + return; + } + + if (qg->qg_qdir == NULL) + { + if (QueueDir == NULL || *QueueDir == '\0') + { + syserr("QueueDir must be defined before queue groups"); + return; + } + qg->qg_qdir = newstr(QueueDir); + } + + if (qg->qg_maxqrun > 1 && !bitnset(QD_FORK, qg->qg_flags)) + { + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: Q=%s: R=%d: multiple queue runners specified\n\tbut flag '%c' is not set\n", + qg->qg_name, qg->qg_maxqrun, QD_FORK); + } + + /* enter the queue into the symbol table */ + if (tTd(37, 8)) + sm_syslog(LOG_INFO, NOQID, + "Adding %s to stab, path: %s", qg->qg_name, + qg->qg_qdir); + s = stab(qg->qg_name, ST_QUEUE, ST_ENTER); + if (s->s_quegrp != NULL) + { + i = s->s_quegrp->qg_index; + + /* XXX what about the pointers inside this struct? */ + sm_free(s->s_quegrp); /* XXX */ + } + else + i = NumQueue++; + Queue[i] = s->s_quegrp = qg; + qg->qg_index = i; + + /* set default value for max queue runners */ + if (qg->qg_maxqrun < 0) + { + if (MaxRunnersPerQueue > 0) + qg->qg_maxqrun = MaxRunnersPerQueue; + else + qg->qg_maxqrun = 1; + } + if (qdef) + setbitn(QD_DEFINED, qg->qg_flags); +} +#if 0 /* ** HASHFQN -- calculate a hash value for a fully qualified host name ** @@ -3441,7 +6543,6 @@ hashfqn(fqn, buckets) { register char *p; register int h = 0, hash, cnt; -# define WATERINC (1000) if (fqn == NULL) return -1; @@ -3465,9 +6566,9 @@ hashfqn(fqn, buckets) return hash; } -# endif /* 0 */ +#endif /* 0 */ -# if _FFR_QUEUEDELAY +#if _FFR_QUEUEDELAY /* ** QUEUEDELAY -- compute queue delay time ** @@ -3503,5 +6604,865 @@ queuedelay(e) qd = MinQueueAge; return qd; } -# endif /* _FFR_QUEUEDELAY */ -#endif /* QUEUE */ +#endif /* _FFR_QUEUEDELAY */ + +/* +** A structure for sorting Queue according to maxqrun without +** screwing up Queue itself. +*/ + +struct sortqgrp +{ + int sg_idx; /* original index */ + int sg_maxqrun; /* max queue runners */ +}; +typedef struct sortqgrp SORTQGRP_T; +static int cmpidx __P((const void *, const void *)); + +static int +cmpidx(a, b) + const void *a; + const void *b; +{ + /* The sort is highest to lowest, so the comparison is reversed */ + if (((SORTQGRP_T *)a)->sg_maxqrun < ((SORTQGRP_T *)b)->sg_maxqrun) + return 1; + else if (((SORTQGRP_T *)a)->sg_maxqrun > ((SORTQGRP_T *)b)->sg_maxqrun) + return -1; + else + return 0; +} + +/* +** MAKEWORKGROUP -- balance queue groups into work groups per MaxQueueChildren +** +** Take the now defined queue groups and assign them to work groups. +** This is done to balance out the number of concurrently active +** queue runners such that MaxQueueChildren is not exceeded. This may +** result in more than one queue group per work group. In such a case +** the number of running queue groups in that work group will have no +** more than the work group maximum number of runners (a "fair" portion +** of MaxQueueRunners). All queue groups within a work group will get a +** chance at running. +** +** Parameters: +** none. +** +** Returns: +** nothing. +** +** Side Effects: +** Sets up WorkGrp structure. +*/ + +void +makeworkgroups() +{ + int i, j, total_runners = 0; + int dir; + SORTQGRP_T si[MAXQUEUEGROUPS + 1]; + + if (NumQueue == 1 && strcmp(Queue[0]->qg_name, "mqueue") == 0) + { + /* + ** There is only the "mqueue" queue group (a default) + ** containing all of the queues. We want to provide to + ** this queue group the maximum allowable queue runners. + ** To match older behavior (8.10/8.11) we'll try for + ** 1 runner per queue capping it at MaxQueueChildren. + ** So if there are N queues, then there will be N runners + ** for the "mqueue" queue group (where N is kept less than + ** MaxQueueChildren). + */ + + NumWorkGroups = 1; + WorkGrp[0].wg_numqgrp = 1; + WorkGrp[0].wg_qgs = (QUEUEGRP **) xalloc(sizeof(QUEUEGRP *)); + WorkGrp[0].wg_qgs[0] = Queue[0]; + if (MaxQueueChildren > 0 && + Queue[0]->qg_numqueues > MaxQueueChildren) + WorkGrp[0].wg_runners = MaxQueueChildren; + else + WorkGrp[0].wg_runners = Queue[0]->qg_numqueues; + + Queue[0]->qg_wgrp = 0; + + /* can't have more runners than allowed total */ + if (MaxQueueChildren > 0 && + Queue[0]->qg_maxqrun > MaxQueueChildren) + Queue[0]->qg_maxqrun = MaxQueueChildren; + WorkGrp[0].wg_maxact = Queue[0]->qg_maxqrun; + WorkGrp[0].wg_lowqintvl = Queue[0]->qg_queueintvl; + return; + } + + for (i = 0; i < NumQueue; i++) + { + si[i].sg_maxqrun = Queue[i]->qg_maxqrun; + si[i].sg_idx = i; + } + qsort(si, NumQueue, sizeof(si[0]), cmpidx); + + NumWorkGroups = 0; + for (i = 0; i < NumQueue; i++) + { + total_runners += si[i].sg_maxqrun; + if (MaxQueueChildren <= 0 || total_runners <= MaxQueueChildren) + NumWorkGroups++; + else + break; + } + + if (NumWorkGroups < 1) + NumWorkGroups = 1; /* gotta have one at least */ + else if (NumWorkGroups > MAXWORKGROUPS) + NumWorkGroups = MAXWORKGROUPS; /* the limit */ + + /* + ** We now know the number of work groups to pack the queue groups + ** into. The queue groups in 'Queue' are sorted from highest + ** to lowest for the number of runners per queue group. + ** We put the queue groups with the largest number of runners + ** into work groups first. Then the smaller ones are fitted in + ** where it looks best. + */ + + j = 0; + dir = 1; + for (i = 0; i < NumQueue; i++) + { + /* a to-and-fro packing scheme, continue from last position */ + if (j >= NumWorkGroups) + { + dir = -1; + j = NumWorkGroups - 1; + } + else if (j < 0) + { + j = 0; + dir = 1; + } + + WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs, + sizeof(QUEUEGRP *) * + (WorkGrp[j].wg_numqgrp + 1)); + if (WorkGrp[j].wg_qgs == NULL) + { + syserr("@cannot allocate memory for work queues, need %d bytes", + (int) (sizeof(QUEUEGRP *) * + (WorkGrp[j].wg_numqgrp + 1))); + } + + WorkGrp[j].wg_qgs[WorkGrp[j].wg_numqgrp] = Queue[si[i].sg_idx]; + WorkGrp[j].wg_numqgrp++; + WorkGrp[j].wg_runners += Queue[i]->qg_maxqrun; + Queue[si[i].sg_idx]->qg_wgrp = j; + + if (WorkGrp[j].wg_maxact == 0) + { + /* can't have more runners than allowed total */ + if (MaxQueueChildren > 0 && + Queue[i]->qg_maxqrun > MaxQueueChildren) + Queue[i]->qg_maxqrun = MaxQueueChildren; + WorkGrp[j].wg_maxact = Queue[i]->qg_maxqrun; + } + + /* + ** XXX: must wg_lowqintvl be the GCD? + ** qg1: 2m, qg2: 3m, minimum: 2m, when do queue runs for + ** qg2 occur? + */ + + /* keep track of the lowest interval for a persistent runner */ + if (Queue[si[i].sg_idx]->qg_queueintvl > 0 && + WorkGrp[j].wg_lowqintvl < Queue[si[i].sg_idx]->qg_queueintvl) + WorkGrp[j].wg_lowqintvl = Queue[si[i].sg_idx]->qg_queueintvl; + j += dir; + } + if (tTd(41, 9)) + { + for (i = 0; i < NumWorkGroups; i++) + { + sm_dprintf("Workgroup[%d]=", i); + for (j = 0; j < WorkGrp[i].wg_numqgrp; j++) + { + sm_dprintf("%s, ", + WorkGrp[i].wg_qgs[j]->qg_name); + } + sm_dprintf("\n"); + } + } +} + +/* +** DUP_DF -- duplicate envelope data file +** +** Copy the data file from the 'old' envelope to the 'new' envelope +** in the most efficient way possible. +** +** Create a hard link from the 'old' data file to the 'new' data file. +** If the old and new queue directories are on different file systems, +** then the new data file link is created in the old queue directory, +** and the new qf file will contain a 'd' record pointing to the +** directory containing the new df file. +** +** Parameters: +** old -- old envelope. +** new -- new envelope. +** +** Results: +** Returns true on success, false on failure. +** +** Side Effects: +** On success, the new data file is created. +** On failure, EF_FATALERRS is set in new->e_flags. +*/ + +static bool dup_df __P((ENVELOPE *, ENVELOPE *)); + +static bool +dup_df(old, new) + ENVELOPE *old; + ENVELOPE *new; +{ + int ofs, nfs, r; + char opath[MAXPATHLEN]; + char npath[MAXPATHLEN]; + + SM_REQUIRE(bitset(EF_HAS_DF, old->e_flags)); + SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir)); + SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir)); + + (void) sm_strlcpy(opath, queuename(old, 'd'), sizeof opath); + (void) sm_strlcpy(npath, queuename(new, 'd'), sizeof npath); + + if (old->e_dfp != NULL) + { + r = sm_io_setinfo(old->e_dfp, SM_BF_COMMIT, NULL); + if (r < 0 && errno != EINVAL) + { + syserr("@can't commit %s", opath); + new->e_flags |= EF_FATALERRS; + return false; + } + } + + /* + ** Attempt to create a hard link, if we think both old and new + ** are on the same file system, otherwise copy the file. + ** + ** Don't waste time attempting a hard link unless old and new + ** are on the same file system. + ** + ** We unlink the new file if it previously existed. This protects + ** us from abandoned data files left behind as a result of + ** a prior system crash. The envelope ids of these old files + ** can conflict with the envelope ids we are generating right now + ** if the clock was set back. + */ + + ofs = Queue[old->e_qgrp]->qg_qpaths[old->e_qdir].qp_fsysidx; + nfs = Queue[new->e_qgrp]->qg_qpaths[new->e_qdir].qp_fsysidx; + if (FILE_SYS_DEV(ofs) == FILE_SYS_DEV(nfs)) + { + if (link(opath, npath) == 0) + { + new->e_flags |= EF_HAS_DF; + SYNC_DIR(npath, true); + return true; + } + if (errno == EEXIST) + { + if (unlink(npath) < 0) + { + syserr("@can't unlink %s", npath); + new->e_flags |= EF_FATALERRS; + return false; + } + if (link(opath, npath) == 0) + { + new->e_flags |= EF_HAS_DF; + SYNC_DIR(npath, true); + return true; + } + } + } + + /* + ** Can't link across queue directories, so try to create a hard + ** link in the same queue directory as the old df file. + ** The qf file will refer to the new df file using a 'd' record. + */ + + new->e_dfqgrp = old->e_dfqgrp; + new->e_dfqdir = old->e_dfqdir; + (void) sm_strlcpy(npath, queuename(new, 'd'), sizeof npath); + if (link(opath, npath) == 0) + { + new->e_flags |= EF_HAS_DF; + SYNC_DIR(npath, true); + return true; + } + if (errno == EEXIST) + { + if (unlink(npath) < 0) + { + syserr("@can't unlink %s", npath); + new->e_flags |= EF_FATALERRS; + return false; + } + if (link(opath, npath) == 0) + { + new->e_flags |= EF_HAS_DF; + SYNC_DIR(npath, true); + return true; + } + } + syserr("@can't link %s to %s", opath, npath); + new->e_flags |= EF_FATALERRS; + return false; +} + +/* +** SPLIT_ENV -- Allocate a new envelope based on a given envelope. +** +** Parameters: +** e -- envelope. +** sendqueue -- sendqueue for new envelope. +** qgrp -- index of queue group. +** qdir -- queue directory. +** +** Results: +** new envelope. +** +*/ + +static ENVELOPE *split_env __P((ENVELOPE *, ADDRESS *, int, int)); + +static ENVELOPE * +split_env(e, sendqueue, qgrp, qdir) + ENVELOPE *e; + ADDRESS *sendqueue; + int qgrp; + int qdir; +{ + ENVELOPE *ee; + + ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, sizeof *ee); + STRUCTCOPY(*e, *ee); + ee->e_message = NULL; /* XXX use original message? */ + ee->e_id = NULL; + assign_queueid(ee); + ee->e_sendqueue = sendqueue; + ee->e_flags &= ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS + |EF_SENDRECEIPT|EF_RET_PARAM|EF_HAS_DF); + ee->e_flags |= EF_NORECEIPT; /* XXX really? */ + ee->e_from.q_state = QS_SENDER; + ee->e_dfp = NULL; + ee->e_lockfp = NULL; + if (e->e_xfp != NULL) + ee->e_xfp = sm_io_dup(e->e_xfp); + ee->e_qgrp = ee->e_dfqgrp = qgrp; + ee->e_qdir = ee->e_dfqdir = qdir; + ee->e_errormode = EM_MAIL; + ee->e_statmsg = NULL; + + /* + ** XXX Not sure if this copying is necessary. + ** sendall() does this copying, but I don't know if that is + ** because of the storage management discipline we were using + ** before rpools were introduced, or if it is because these lists + ** can be modified later. + */ + + ee->e_header = copyheader(e->e_header, ee->e_rpool); + ee->e_errorqueue = copyqueue(e->e_errorqueue, ee->e_rpool); + + return ee; +} + +/* return values from split functions, check also below! */ +#define SM_SPLIT_FAIL (0) +#define SM_SPLIT_NONE (1) +#define SM_SPLIT_NEW(n) (1 + (n)) + +/* +** SPLIT_ACROSS_QUEUE_GROUPS +** +** This function splits an envelope across multiple queue groups +** based on the queue group of each recipient. +** +** Parameters: +** e -- envelope. +** +** Results: +** SM_SPLIT_FAIL on failure +** SM_SPLIT_NONE if no splitting occurred, +** or 1 + the number of additional envelopes created. +** +** Side Effects: +** On success, e->e_sibling points to a list of zero or more +** additional envelopes, and the associated data files exist +** on disk. But the 'qf' files are not created. +** +** On failure, e->e_flags & EF_FATALERRS is set. +** e->e_sibling is not changed. +** The order of recipients in e->e_sendqueue is permuted. +** Abandoned data files for additional envelopes that failed +** to be created may exist on disk. +*/ + +static int q_qgrp_compare __P((const void *, const void *)); +static int e_filesys_compare __P((const void *, const void *)); + +static int +q_qgrp_compare(p1, p2) + const void *p1; + const void *p2; +{ + ADDRESS **pq1 = (ADDRESS **) p1; + ADDRESS **pq2 = (ADDRESS **) p2; + + return (*pq1)->q_qgrp - (*pq2)->q_qgrp; +} + +static int +e_filesys_compare(p1, p2) + const void *p1; + const void *p2; +{ + ENVELOPE **pe1 = (ENVELOPE **) p1; + ENVELOPE **pe2 = (ENVELOPE **) p2; + int fs1, fs2; + + fs1 = Queue[(*pe1)->e_qgrp]->qg_qpaths[(*pe1)->e_qdir].qp_fsysidx; + fs2 = Queue[(*pe2)->e_qgrp]->qg_qpaths[(*pe2)->e_qdir].qp_fsysidx; + if (FILE_SYS_DEV(fs1) < FILE_SYS_DEV(fs2)) + return -1; + if (FILE_SYS_DEV(fs1) > FILE_SYS_DEV(fs2)) + return 1; + return 0; +} + +static int +split_across_queue_groups(e) + ENVELOPE *e; +{ + int naddrs, nsplits, i; + char **pvp; + ADDRESS *q, **addrs; + ENVELOPE *ee, *es; + ENVELOPE *splits[MAXQUEUEGROUPS]; + char pvpbuf[PSBUFSIZE]; + + SM_REQUIRE(ISVALIDQGRP(e->e_qgrp)); + + /* Count addresses and assign queue groups. */ + naddrs = 0; + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (QS_IS_DEAD(q->q_state)) + continue; + ++naddrs; + + /* bad addresses and those already sent stay put */ + if (QS_IS_BADADDR(q->q_state) || + QS_IS_SENT(q->q_state)) + q->q_qgrp = e->e_qgrp; + else if (!ISVALIDQGRP(q->q_qgrp)) + { + /* call ruleset which should return a queue group */ + i = rscap(RS_QUEUEGROUP, q->q_user, NULL, e, &pvp, + pvpbuf, sizeof(pvpbuf)); + if (i == EX_OK && + pvp != NULL && pvp[0] != NULL && + (pvp[0][0] & 0377) == CANONNET && + pvp[1] != NULL && pvp[1][0] != '\0') + { + i = name2qid(pvp[1]); + if (ISVALIDQGRP(i)) + { + q->q_qgrp = i; + if (tTd(20, 4)) + sm_syslog(LOG_INFO, NOQID, + "queue group name %s -> %d", + pvp[1], i); + continue; + } + else if (LogLevel > 10) + sm_syslog(LOG_INFO, NOQID, + "can't find queue group name %s, selection ignored", + pvp[1]); + } + if (q->q_mailer != NULL && + ISVALIDQGRP(q->q_mailer->m_qgrp)) + q->q_qgrp = q->q_mailer->m_qgrp; + else + q->q_qgrp = 0; + } + } + + /* only one address? nothing to split. */ + if (naddrs <= 1) + return SM_SPLIT_NONE; + + /* sort the addresses by queue group */ + addrs = sm_rpool_malloc_x(e->e_rpool, naddrs * sizeof(ADDRESS *)); + for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (QS_IS_DEAD(q->q_state)) + continue; + addrs[i++] = q; + } + qsort(addrs, naddrs, sizeof(ADDRESS *), q_qgrp_compare); + + /* split into multiple envelopes, by queue group */ + nsplits = 0; + es = NULL; + e->e_sendqueue = NULL; + for (i = 0; i < naddrs; ++i) + { + if (i == naddrs - 1 || addrs[i]->q_qgrp != addrs[i + 1]->q_qgrp) + addrs[i]->q_next = NULL; + else + addrs[i]->q_next = addrs[i + 1]; + + /* same queue group as original envelope? */ + if (addrs[i]->q_qgrp == e->e_qgrp) + { + if (e->e_sendqueue == NULL) + e->e_sendqueue = addrs[i]; + continue; + } + + /* different queue group than original envelope */ + if (es == NULL || addrs[i]->q_qgrp != es->e_qgrp) + { + ee = split_env(e, addrs[i], addrs[i]->q_qgrp, NOQDIR); + es = ee; + splits[nsplits++] = ee; + } + } + + /* no splits? return right now. */ + if (nsplits <= 0) + return SM_SPLIT_NONE; + + /* assign a queue directory to each additional envelope */ + for (i = 0; i < nsplits; ++i) + { + es = splits[i]; +#if 0 + es->e_qdir = pickqdir(Queue[es->e_qgrp], es->e_msgsize, es); +#endif /* 0 */ + if (!setnewqueue(es)) + goto failure; + } + + /* sort the additional envelopes by queue file system */ + qsort(splits, nsplits, sizeof(ENVELOPE *), e_filesys_compare); + + /* create data files for each additional envelope */ + if (!dup_df(e, splits[0])) + goto failure; + for (i = 1; i < nsplits; ++i) + { + /* copy or link to the previous data file */ + if (!dup_df(splits[i - 1], splits[i])) + goto failure; + } + + /* success: prepend the new envelopes to the e->e_sibling list */ + for (i = 0; i < nsplits; ++i) + { + es = splits[i]; + es->e_sibling = e->e_sibling; + e->e_sibling = es; + } + return SM_SPLIT_NEW(nsplits); + + /* failure: clean up */ + failure: + e->e_flags |= EF_FATALERRS; + e->e_sendqueue = addrs[0]; + for (i = 0; i < naddrs - 1; ++i) + addrs[i]->q_next = addrs[i + 1]; + addrs[naddrs - 1]->q_next = NULL; + return SM_SPLIT_FAIL; +} + +/* +** SPLIT_WITHIN_QUEUE +** +** Split an envelope with multiple recipients into several +** envelopes within the same queue directory, if the number of +** recipients exceeds the limit for the queue group. +** +** Parameters: +** e -- envelope. +** +** Results: +** SM_SPLIT_FAIL on failure +** SM_SPLIT_NONE if no splitting occurred, +** or 1 + the number of additional envelopes created. +*/ + +#define SPLIT_LOG_LEVEL 8 + +static int split_within_queue __P((ENVELOPE *)); + +static int +split_within_queue(e) + ENVELOPE *e; +{ + int maxrcpt, nrcpt, ndead, nsplit, i; + int j, l; + char *lsplits; + ADDRESS *q, **addrs; + ENVELOPE *ee, *firstsibling; + + if (!ISVALIDQGRP(e->e_qgrp) || bitset(EF_SPLIT, e->e_flags)) + return SM_SPLIT_NONE; + + /* don't bother if there is no recipient limit */ + maxrcpt = Queue[e->e_qgrp]->qg_maxrcpt; + if (maxrcpt <= 0) + return SM_SPLIT_NONE; + + /* count recipients */ + nrcpt = 0; + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (QS_IS_DEAD(q->q_state)) + continue; + ++nrcpt; + } + if (nrcpt <= maxrcpt) + return SM_SPLIT_NONE; + + /* + ** Preserve the recipient list + ** so that we can restore it in case of error. + ** (But we discard dead addresses.) + */ + + addrs = sm_rpool_malloc_x(e->e_rpool, nrcpt * sizeof(ADDRESS *)); + for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (QS_IS_DEAD(q->q_state)) + continue; + addrs[i++] = q; + } + + /* + ** Partition the recipient list so that bad and sent addresses + ** come first. These will go with the original envelope, and + ** do not count towards the maxrcpt limit. + ** addrs[] does not contain QS_IS_DEAD() addresses. + */ + + ndead = 0; + for (i = 0; i < nrcpt; ++i) + { + if (QS_IS_BADADDR(addrs[i]->q_state) || + QS_IS_SENT(addrs[i]->q_state) || + QS_IS_DEAD(addrs[i]->q_state)) /* for paranoia's sake */ + { + if (i > ndead) + { + ADDRESS *tmp = addrs[i]; + + addrs[i] = addrs[ndead]; + addrs[ndead] = tmp; + } + ++ndead; + } + } + + /* Check if no splitting required. */ + if (nrcpt - ndead <= maxrcpt) + return SM_SPLIT_NONE; + + /* fix links */ + for (i = 0; i < nrcpt - 1; ++i) + addrs[i]->q_next = addrs[i + 1]; + addrs[nrcpt - 1]->q_next = NULL; + e->e_sendqueue = addrs[0]; + + /* prepare buffer for logging */ + if (LogLevel > SPLIT_LOG_LEVEL) + { + l = MAXLINE; + lsplits = sm_malloc(l); + if (lsplits != NULL) + *lsplits = '\0'; + j = 0; + } + else + { + /* get rid of stupid compiler warnings */ + lsplits = NULL; + j = l = 0; + } + + /* split the envelope */ + firstsibling = e->e_sibling; + i = maxrcpt + ndead; + nsplit = 0; + for (;;) + { + addrs[i - 1]->q_next = NULL; + ee = split_env(e, addrs[i], e->e_qgrp, e->e_qdir); + if (!dup_df(e, ee)) + { + /* Error. Restore e's sibling & recipient lists. */ + e->e_sibling = firstsibling; + for (i = 0; i < nrcpt - 1; ++i) + addrs[i]->q_next = addrs[i + 1]; + return SM_SPLIT_FAIL; + } + + /* prepend the new envelope to e->e_sibling */ + ee->e_sibling = e->e_sibling; + e->e_sibling = ee; + ++nsplit; + if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL) + { + if (j >= l - strlen(ee->e_id) - 3) + { + char *p; + + l += MAXLINE; + p = sm_realloc(lsplits, l); + if (p == NULL) + { + /* let's try to get this done */ + sm_free(lsplits); + lsplits = NULL; + } + else + lsplits = p; + } + if (lsplits != NULL) + { + if (j == 0) + j += sm_strlcat(lsplits + j, + ee->e_id, + l - j); + else + j += sm_strlcat2(lsplits + j, + "; ", + ee->e_id, + l - j); + SM_ASSERT(j < l); + } + } + if (nrcpt - i <= maxrcpt) + break; + i += maxrcpt; + } + if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && nsplit > 0) + { + sm_syslog(LOG_NOTICE, e->e_id, + "split: maxrcpts=%d, rcpts=%d, count=%d, id%s=%s", + maxrcpt, nrcpt - ndead, nsplit, + nsplit > 1 ? "s" : "", lsplits); + sm_free(lsplits); + } + return SM_SPLIT_NEW(nsplit); +} +/* +** SPLIT_BY_RECIPIENT +** +** Split an envelope with multiple recipients into multiple +** envelopes as required by the sendmail configuration. +** +** Parameters: +** e -- envelope. +** +** Results: +** Returns true on success, false on failure. +** +** Side Effects: +** see split_across_queue_groups(), split_within_queue(e) +*/ + +bool +split_by_recipient(e) + ENVELOPE *e; +{ + int split, n, i, j, l; + char *lsplits; + ENVELOPE *ee, *next, *firstsibling; + + if (OpMode == SM_VERIFY || !ISVALIDQGRP(e->e_qgrp) || + bitset(EF_SPLIT, e->e_flags)) + return true; + n = split_across_queue_groups(e); + if (n == SM_SPLIT_FAIL) + return false; + firstsibling = ee = e->e_sibling; + if (n > 1 && LogLevel > SPLIT_LOG_LEVEL) + { + l = MAXLINE; + lsplits = sm_malloc(l); + if (lsplits != NULL) + *lsplits = '\0'; + j = 0; + } + else + { + /* get rid of stupid compiler warnings */ + lsplits = NULL; + j = l = 0; + } + for (i = 1; i < n; ++i) + { + next = ee->e_sibling; + if (split_within_queue(ee) == SM_SPLIT_FAIL) + { + e->e_sibling = firstsibling; + return false; + } + ee->e_flags |= EF_SPLIT; + if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL) + { + if (j >= l - strlen(ee->e_id) - 3) + { + char *p; + + l += MAXLINE; + p = sm_realloc(lsplits, l); + if (p == NULL) + { + /* let's try to get this done */ + sm_free(lsplits); + lsplits = NULL; + } + else + lsplits = p; + } + if (lsplits != NULL) + { + if (j == 0) + j += sm_strlcat(lsplits + j, + ee->e_id, l - j); + else + j += sm_strlcat2(lsplits + j, "; ", + ee->e_id, l - j); + SM_ASSERT(j < l); + } + } + ee = next; + } + if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && n > 1) + { + sm_syslog(LOG_NOTICE, e->e_id, "split: count=%d, id%s=%s", + n - 1, n > 2 ? "s" : "", lsplits); + sm_free(lsplits); + } + split = split_within_queue(e) != SM_SPLIT_FAIL; + if (split) + e->e_flags |= EF_SPLIT; + return split; +} diff --git a/gnu/usr.sbin/sendmail/sendmail/readcf.c b/gnu/usr.sbin/sendmail/sendmail/readcf.c index f9eaa2ffc0d..7706b62d294 100644 --- a/gnu/usr.sbin/sendmail/sendmail/readcf.c +++ b/gnu/usr.sbin/sendmail/sendmail/readcf.c @@ -11,12 +11,9 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: readcf.c,v 8.382.4.43 2001/08/14 23:08:13 ca Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +SM_RCSID("@(#)$Sendmail: readcf.c,v 8.582 2001/09/04 22:43:05 ca Exp $") #if NETINET || NETINET6 # include <arpa/inet.h> @@ -31,6 +28,7 @@ static void fileclass __P((int, char *, char *, bool, bool)); static char **makeargv __P((char *)); static void settimeout __P((char *, char *, bool)); static void toomany __P((int, int)); +static char *extrquotstr __P((char *, char **, char *, bool *)); /* ** READCF -- read configuration file. @@ -57,7 +55,10 @@ static void toomany __P((int, int)); ** Mn arg=val... Define mailer. n is the internal name. ** Args specify mailer parameters. ** Oxvalue Set option x to value. +** O option value Set option (long name) to value. ** Pname=value Set precedence name to value. +** Qn arg=val... Define queue groups. n is the internal name. +** Args specify queue parameters. ** Vversioncode[/vendorcode] ** Version level/vendor name of ** configuration syntax. @@ -68,8 +69,8 @@ static void toomany __P((int, int)); ** ** Parameters: ** cfname -- configuration file name. -** safe -- TRUE if this is the system config file; -** FALSE otherwise. +** safe -- true if this is the system config file; +** false otherwise. ** e -- the main envelope. ** ** Returns: @@ -85,7 +86,7 @@ readcf(cfname, safe, e) bool safe; register ENVELOPE *e; { - FILE *cf; + SM_FILE_T *cf; int ruleset = -1; char *q; struct rewrite *rwp = NULL; @@ -94,6 +95,7 @@ readcf(cfname, safe, e) int nfuzzy; char *file; bool optional; + bool ok; int mid; register char *p; long sff = SFF_OPENASROOT; @@ -102,7 +104,7 @@ readcf(cfname, safe, e) char exbuf[MAXLINE]; char pvpbuf[MAXLINE + MAXATOM]; static char *null_list[1] = { NULL }; - extern u_char TokTypeNoC[]; + extern unsigned char TokTypeNoC[]; FileName = cfname; LineNumber = 0; @@ -113,33 +115,34 @@ readcf(cfname, safe, e) if (cf == NULL) { syserr("cannot open"); - finis(FALSE, EX_OSFILE); + finis(false, EX_OSFILE); } - if (fstat(fileno(cf), &statb) < 0) + if (fstat(sm_io_getinfo(cf, SM_IO_WHAT_FD, NULL), &statb) < 0) { syserr("cannot fstat"); - finis(FALSE, EX_OSFILE); + finis(false, EX_OSFILE); } if (!S_ISREG(statb.st_mode)) { syserr("not a plain file"); - finis(FALSE, EX_OSFILE); + finis(false, EX_OSFILE); } if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) { if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS) - fprintf(stderr, "%s: WARNING: dangerous write permissions\n", - FileName); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: WARNING: dangerous write permissions\n", + FileName); if (LogLevel > 0) sm_syslog(LOG_CRIT, NOQID, "%s: WARNING: dangerous write permissions", FileName); } -#ifdef XLA +#if XLA xla_zero(); #endif /* XLA */ @@ -148,7 +151,7 @@ readcf(cfname, safe, e) if (bp[0] == '#') { if (bp != buf) - sm_free(bp); + sm_free(bp); /* XXX */ continue; } @@ -202,7 +205,7 @@ readcf(cfname, safe, e) { register char **ap; - rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); + rwp->r_lhs = copyplist(rwp->r_lhs, true, NULL); /* count the number of fuzzy matches in LHS */ for (ap = rwp->r_lhs; *ap != NULL; ap++) @@ -288,7 +291,7 @@ readcf(cfname, safe, e) { register char **ap; - rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); + rwp->r_rhs = copyplist(rwp->r_rhs, true, NULL); /* check no out-of-bounds replacements */ nfuzzy += '0'; @@ -326,6 +329,36 @@ readcf(cfname, safe, e) case MATCHNCLASS: botch = "$~"; break; + +#if 0 +/* +** This doesn't work yet as there are maps defined *after* the cf +** is read such as host, user, and alias. So for now, it's removed. +** When it comes back, the RELEASE_NOTES entry will be: +** Emit warnings for unknown maps when reading the .cf file. Based on +** patch from Robert Harker of Harker Systems. +*/ + + case LOOKUPBEGIN: + /* + ** Got a database lookup, + ** check if map is defined. + */ + + ep = *(ap + 1); + if ((*ep & 0377) != MACRODEXPAND && + stab(ep, ST_MAP, + ST_FIND) == NULL) + { + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "Warning: %s: line %d: map %s not found\n", + FileName, + LineNumber, + ep); + } + break; +#endif /* 0 */ } if (botch != NULL) syserr("Inappropriate use of %s on RHS", @@ -349,22 +382,24 @@ readcf(cfname, safe, e) if (rwp != NULL) { if (OpMode == MD_TEST) - printf("WARNING: Ruleset %s has multiple definitions\n", - &bp[1]); + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "WARNING: Ruleset %s has multiple definitions\n", + &bp[1]); if (tTd(37, 1)) - dprintf("WARNING: Ruleset %s has multiple definitions\n", - &bp[1]); + sm_dprintf("WARNING: Ruleset %s has multiple definitions\n", + &bp[1]); while (rwp->r_next != NULL) rwp = rwp->r_next; } break; case 'D': /* macro definition */ - mid = macid(&bp[1], &ep); + mid = macid_parse(&bp[1], &ep); if (mid == 0) break; p = munchstring(ep, NULL, '\0'); - define(mid, newstr(p), e); + macdefine(&e->e_macro, A_TEMP, mid, p); break; case 'H': /* required header line */ @@ -375,7 +410,7 @@ readcf(cfname, safe, e) case 'T': /* trusted user (set class `t') */ if (bp[0] == 'C') { - mid = macid(&bp[1], &ep); + mid = macid_parse(&bp[1], &ep); if (mid == 0) break; expand(ep, exbuf, sizeof exbuf, e); @@ -405,27 +440,40 @@ readcf(cfname, safe, e) break; case 'F': /* word class from file */ - mid = macid(&bp[1], &ep); + mid = macid_parse(&bp[1], &ep); if (mid == 0) break; for (p = ep; isascii(*p) && isspace(*p); ) p++; if (p[0] == '-' && p[1] == 'o') { - optional = TRUE; - while (*p != '\0' && !(isascii(*p) && isspace(*p))) + optional = true; + while (*p != '\0' && + !(isascii(*p) && isspace(*p))) p++; while (isascii(*p) && isspace(*p)) p++; + file = p; } else - optional = FALSE; + optional = false; - file = p; - q = p; - while (*q != '\0' && !(isascii(*q) && isspace(*q))) - q++; - if (*file == '|') + if (*p == '@') + { + /* use entire spec */ + file = p; + } + else + { + file = extrquotstr(p, &q, " ", &ok); + if (!ok) + { + syserr("illegal filename '%s'", p); + break; + } + } + + if (*file == '|' || *file == '@') p = "%s"; else { @@ -442,7 +490,7 @@ readcf(cfname, safe, e) fileclass(mid, file, p, safe, optional); break; -#ifdef XLA +#if XLA case 'L': /* extended load average description */ xla_init(&bp[1]); break; @@ -463,7 +511,7 @@ readcf(cfname, safe, e) break; case 'O': /* set option */ - setoption(bp[1], &bp[2], safe, FALSE, e); + setoption(bp[1], &bp[2], safe, false, e); break; case 'P': /* set precedence */ @@ -482,6 +530,10 @@ readcf(cfname, safe, e) NumPriorities++; break; + case 'Q': /* define queue */ + makequeue(&bp[1], true); + break; + case 'V': /* configuration syntax version */ for (p = &bp[1]; isascii(*p) && isspace(*p); p++) continue; @@ -502,12 +554,15 @@ readcf(cfname, safe, e) /* level 5 configs have short name in $w */ p = macvalue('w', e); if (p != NULL && (p = strchr(p, '.')) != NULL) + { *p = '\0'; - define('w', macvalue('w', e), e); + macdefine(&e->e_macro, A_TEMP, 'w', + macvalue('w', e)); + } } if (ConfigLevel >= 6) { - ColonOkInAddr = FALSE; + ColonOkInAddr = false; } /* @@ -539,25 +594,28 @@ readcf(cfname, safe, e) setuserenv(&bp[1], p); break; -#if _FFR_MILTER case 'X': /* mail filter */ +#if MILTER milter_setup(&bp[1]); +#else /* MILTER */ + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: Filter usage ('X') requires Milter support (-DMILTER)\n"); +#endif /* MILTER */ break; -#endif /* _FFR_MILTER */ default: badline: syserr("unknown configuration line \"%s\"", bp); } if (bp != buf) - sm_free(bp); + sm_free(bp); /* XXX */ } - if (ferror(cf)) + if (sm_io_error(cf)) { syserr("I/O read error"); - finis(FALSE, EX_OSFILE); + finis(false, EX_OSFILE); } - (void) fclose(cf); + (void) sm_io_close(cf, SM_TIME_DEFAULT); FileName = NULL; /* initialize host maps from local service tables */ @@ -573,32 +631,18 @@ readcf(cfname, safe, e) short mapreturn[MAXMAPACTIONS]; nmaps = switch_map_find("hosts", maptype, mapreturn); - UseNameServer = FALSE; + UseNameServer = false; if (nmaps > 0 && nmaps <= MAXMAPSTACK) { register int mapno; - for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++) + for (mapno = 0; mapno < nmaps && !UseNameServer; + mapno++) { if (strcmp(maptype[mapno], "dns") == 0) - UseNameServer = TRUE; + UseNameServer = true; } } - -#ifdef HESIOD - nmaps = switch_map_find("passwd", maptype, mapreturn); - UseHesiod = FALSE; - if (nmaps > 0 && nmaps <= MAXMAPSTACK) - { - register int mapno; - - for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++) - { - if (strcmp(maptype[mapno], "hesiod") == 0) - UseHesiod = TRUE; - } - } -#endif /* HESIOD */ } } /* @@ -627,7 +671,6 @@ translate_dollars(bp) { if (*p == '#' && p > bp && ConfigLevel >= 3) { - /* this is an on-line comment */ register char *e; switch (*--p & 0377) @@ -639,7 +682,7 @@ translate_dollars(bp) case '\\': /* it's backslash escaped */ - (void) strlcpy(p, p + 1, strlen(p)); + (void) sm_strlcpy(p, p + 1, strlen(p)); break; default: @@ -648,7 +691,7 @@ translate_dollars(bp) *p != '\n' && p > bp) p--; if ((e = strchr(++p, '\n')) != NULL) - (void) strlcpy(p, e, strlen(p)); + (void) sm_strlcpy(p, e, strlen(p)); else *p-- = '\0'; break; @@ -662,7 +705,7 @@ translate_dollars(bp) if (p[1] == '$') { /* actual dollar sign.... */ - (void) strlcpy(p, p + 1, strlen(p)); + (void) sm_strlcpy(p, p + 1, strlen(p)); continue; } @@ -674,9 +717,9 @@ translate_dollars(bp) p++; /* convert macro name to code */ - *p = macid(p, &ep); + *p = macid_parse(p, &ep); if (ep != p + 1) - (void) strlcpy(p + 1, ep, strlen(p + 1)); + (void) sm_strlcpy(p + 1, ep, strlen(p + 1)); } /* strip trailing white space from the line */ @@ -719,11 +762,41 @@ toomany(id, maxcnt) ** none ** ** Side Effects: -** ** puts all lines in filename that match a scanf into ** the named class. */ +/* +** Break up the match into words and add to class. +*/ + +static void +parse_class_words(class, line) + int class; + char *line; +{ + while (line != NULL && *line != '\0') + { + register char *q; + + /* strip leading spaces */ + while (isascii(*line) && isspace(*line)) + line++; + if (*line == '\0') + break; + + /* find the end of the word */ + q = line; + while (*line != '\0' && !(isascii(*line) && isspace(*line))) + line++; + if (*line != '\0') + *line++ = '\0'; + + /* enter the word in the symbol table */ + setclass(class, q); + } +} + static void fileclass(class, filename, fmt, safe, optional) int class; @@ -732,34 +805,166 @@ fileclass(class, filename, fmt, safe, optional) bool safe; bool optional; { - FILE *f; + SM_FILE_T *f; long sff; pid_t pid; register char *p; char buf[MAXLINE]; if (tTd(37, 2)) - dprintf("fileclass(%s, fmt=%s)\n", filename, fmt); + sm_dprintf("fileclass(%s, fmt=%s)\n", filename, fmt); + + if (*filename == '\0') + { + syserr("fileclass: missing file name"); + return; + } + else if (!SM_IS_DIR_DELIM(*filename) && *filename != '|' && + (p = strchr(filename, '@')) != NULL) + { + int status = 0; + char *key; + char *mn; + char *cl, *spec; + STAB *mapclass; + MAP map; + + mn = newstr(macname(class)); + + key = filename; + + /* skip past '@' */ + *p++ = '\0'; + cl = p; + + if (strcmp(cl, "LDAP") == 0) + { + int n; + char *lc; + char jbuf[MAXHOSTNAMELEN]; + char lcbuf[MAXLINE]; + + /* Get $j */ + expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope); + if (jbuf[0] == '\0') + { + (void) sm_strlcpy(jbuf, "localhost", + sizeof jbuf); + } + + /* impose the default schema */ + lc = macvalue(macid("{sendmailMTACluster}"), CurEnv); + if (lc == NULL) + lc = ""; + else + { + expand(lc, lcbuf, sizeof lcbuf, CurEnv); + lc = lcbuf; + } + + cl = "ldap"; + n = sm_snprintf(buf, sizeof buf, + "-k (&(objectClass=sendmailMTAClass)(sendmailMTAClassName=%s)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))) -v sendmailMTAClassValue", + mn, lc, jbuf); + if (n >= sizeof buf) + { + syserr("fileclass: F{%s}: Default LDAP string too long", + mn); + sm_free(mn); + return; + } + spec = buf; + } + else + { + if ((spec = strchr(cl, ':')) == NULL) + { + syserr("fileclass: F{%s}: missing map class", + mn); + sm_free(mn); + return; + } + *spec++ ='\0'; + } + + /* set up map structure */ + mapclass = stab(cl, ST_MAPCLASS, ST_FIND); + if (mapclass == NULL) + { + syserr("fileclass: F{%s}: class %s not available", + mn, cl); + sm_free(mn); + return; + } + memset(&map, '\0', sizeof map); + map.map_class = &mapclass->s_mapclass; + map.map_mname = mn; + map.map_mflags |= MF_FILECLASS; + + /* parse map spec */ + if (!map.map_class->map_parse(&map, spec)) + { + /* map_parse() showed the error already */ + sm_free(mn); + return; + } + map.map_mflags |= MF_VALID; + + /* open map */ + if (map.map_class->map_open(&map, O_RDONLY)) + { + map.map_mflags |= MF_OPEN; + map.map_pid = getpid(); + } + else + { + if (!optional && + !bitset(MF_OPTIONAL, map.map_mflags)) + syserr("fileclass: F{%s}: map open failed", + mn); + sm_free(mn); + return; + } - if (filename[0] == '|') + /* lookup */ + p = (*map.map_class->map_lookup)(&map, key, NULL, &status); + if (status != EX_OK && status != EX_NOTFOUND) + { + if (!optional) + syserr("fileclass: F{%s}: map lookup failed", + mn); + p = NULL; + } + + /* use the results */ + if (p != NULL) + parse_class_words(class, p); + + /* close map */ + map.map_mflags |= MF_CLOSING; + map.map_class->map_close(&map); + map.map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); + sm_free(mn); + return; + } + else if (filename[0] == '|') { auto int fd; int i; char *argv[MAXPV + 1]; i = 0; - for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t")) - { - if (i >= MAXPV) - break; + for (p = strtok(&filename[1], " \t"); + p != NULL && i < MAXPV; + p = strtok(NULL, " \t")) argv[i++] = p; - } argv[i] = NULL; pid = prog_open(argv, &fd, CurEnv); if (pid < 0) f = NULL; else - f = fdopen(fd, "r"); + f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, + (void *) fd, SM_IO_RDONLY, NULL); } else { @@ -783,7 +988,7 @@ fileclass(class, filename, fmt, safe, optional) return; } - while (fgets(buf, sizeof buf, f) != NULL) + while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) { #if SCANF char wordbuf[MAXLINE + 1]; @@ -792,40 +997,23 @@ fileclass(class, filename, fmt, safe, optional) if (buf[0] == '#') continue; #if SCANF - if (sscanf(buf, fmt, wordbuf) != 1) + if (sm_io_sscanf(buf, fmt, wordbuf) != 1) continue; p = wordbuf; #else /* SCANF */ p = buf; #endif /* SCANF */ + parse_class_words(class, p); + /* - ** Break up the match into words. + ** If anything else is added here, + ** check if the '@' map case above + ** needs the code as well. */ - - while (*p != '\0') - { - register char *q; - - /* strip leading spaces */ - while (isascii(*p) && isspace(*p)) - p++; - if (*p == '\0') - break; - - /* find the end of the word */ - q = p; - while (*p != '\0' && !(isascii(*p) && isspace(*p))) - p++; - if (*p != '\0') - *p++ = '\0'; - - /* enter the word in the symbol table */ - setclass(class, q); - } } - (void) fclose(f); + (void) sm_io_close(f, SM_TIME_DEFAULT); if (pid > 0) (void) waitfor(pid); } @@ -844,12 +1032,14 @@ fileclass(class, filename, fmt, safe, optional) ** M -- the maximum message size ** N -- the niceness at which to run ** P -- the path to the mailer +** Q -- the queue group for the mailer ** R -- the recipient rewriting set ** S -- the sender rewriting set ** T -- the mailer type (for DSNs) ** U -- the uid to run as ** W -- the time to wait at the end ** m -- maximum messages per connection +** r -- maximum number of recipients per message ** / -- new root directory ** The first word is the canonical name of the mailer. ** @@ -870,14 +1060,17 @@ makemailer(line) int i; char fcode; auto char *endp; - extern int NextMailer; + static int nextmailer = 0; /* "free" index into Mailer struct */ /* allocate a mailer and set up defaults */ m = (struct mailer *) xalloc(sizeof *m); memset((char *) m, '\0', sizeof *m); + errno = 0; /* avoid bogus error text */ /* collect the mailer name */ - for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) + for (p = line; + *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); + p++) continue; if (*p != '\0') *p++ = '\0'; @@ -893,7 +1086,8 @@ makemailer(line) { auto char *delimptr; - while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) + while (*p != '\0' && + (*p == ',' || (isascii(*p) && isspace(*p)))) p++; /* p now points to field code */ @@ -915,16 +1109,24 @@ makemailer(line) switch (fcode) { case 'P': /* pathname */ - if (*p == '\0') - syserr("mailer %s: empty path name", m->m_name); - else + if (*p != '\0') /* error is issued below */ m->m_mailer = newstr(p); break; case 'F': /* flags */ for (; *p != '\0'; p++) + { if (!(isascii(*p) && isspace(*p))) + { +#if _FFR_DEPRECATE_MAILER_FLAG_I + if (*p == M_INTERNAL) + sm_syslog(LOG_WARNING, NOQID, + "WARNING: mailer=%s, flag=%c deprecated", + m->m_name, *p); +#endif /* _FFR_DEPRECATE_MAILER_FLAG_I */ setbitn(bitidx(*p), m->m_flags); + } + } break; case 'S': /* sender rewriting ruleset */ @@ -959,10 +1161,7 @@ makemailer(line) break; case 'A': /* argument vector */ - if (*p == '\0') - syserr("mailer %s: null argument vector", - m->m_name); - else + if (*p != '\0') /* error is issued below */ m->m_argv = makeargv(p); break; @@ -974,11 +1173,9 @@ makemailer(line) m->m_maxdeliveries = atoi(p); break; -#if _FFR_DYNAMIC_TOBUF case 'r': /* max recipient per envelope */ m->m_maxrcpt = atoi(p); break; -#endif /* _FFR_DYNAMIC_TOBUF */ case 'L': /* maximum line length */ m->m_linelimit = atoi(p); @@ -1005,6 +1202,20 @@ makemailer(line) m->m_defcharset = newstr(p); break; + case 'Q': /* queue for this mailer */ + if (*p == '\0') + { + syserr("mailer %s: null queue", m->m_name); + break; + } + s = stab(p, ST_QUEUE, ST_FIND); + if (s == NULL) + syserr("mailer %s: unknown queue %s", + m->m_name, p); + else + m->m_qgrp = s->s_quegrp->qg_index; + break; + case 'T': /* MTA-Name/Address/Diagnostic types */ /* extract MTA name type; default to "dns" */ m->m_mtatype = newstr(p); @@ -1145,16 +1356,14 @@ makemailer(line) return; } - if (NextMailer >= MAXMAILERS) + if (nextmailer >= MAXMAILERS) { syserr("too many mailers defined (%d max)", MAXMAILERS); return; } -#if _FFR_DYNAMIC_TOBUF if (m->m_maxrcpt <= 0) m->m_maxrcpt = DEFAULT_MAX_RCPT; -#endif /* _FFR_DYNAMIC_TOBUF */ /* do some heuristic cleanup for back compatibility */ if (bitnset(M_LIMITS, m->m_flags)) @@ -1167,21 +1376,12 @@ makemailer(line) if (strcmp(m->m_mailer, "[TCP]") == 0) { -#if _FFR_REMOVE_TCP_MAILER_PATH - syserr("M%s: P=[TCP] is deprecated, use P=[IPC] instead\n", + syserr("M%s: P=[TCP] must be replaced by P=[IPC]\n", m->m_name); return; -#else /* _FFR_REMOVE_TCP_MAILER_PATH */ - printf("M%s: Warning: P=[TCP] is deprecated, use P=[IPC] instead\n", - m->m_name); -#endif /* _FFR_REMOVE_TCP_MAILER_PATH */ } - if (strcmp(m->m_mailer, "[IPC]") == 0 -#if !_FFR_REMOVE_TCP_MAILER_PATH - || strcmp(m->m_mailer, "[TCP]") == 0 -#endif /* !_FFR_REMOVE_TCP_MAILER_PATH */ - ) + if (strcmp(m->m_mailer, "[IPC]") == 0) { /* Use the second argument for host or path to socket */ if (m->m_argv[0] == NULL || m->m_argv[1] == NULL || @@ -1195,21 +1395,30 @@ makemailer(line) #if NETUNIX && strcmp(m->m_argv[0], "FILE") != 0 #endif /* NETUNIX */ -#if !_FFR_DEPRECATE_IPC_MAILER_ARG - && strcmp(m->m_argv[0], "IPC") != 0 -#endif /* !_FFR_DEPRECATE_IPC_MAILER_ARG */ ) { - printf("M%s: Warning: first argument in %s mailer must be %s\n", - m->m_name, m->m_mailer, + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "M%s: Warning: first argument in %s mailer must be %s\n", + m->m_name, m->m_mailer, #if NETUNIX - "TCP or FILE" + "TCP or FILE" #else /* NETUNIX */ - "TCP" + "TCP" #endif /* NETUNIX */ - ); + ); + } + if (m->m_mtatype == NULL) + m->m_mtatype = "dns"; + if (m->m_addrtype == NULL) + m->m_addrtype = "rfc822"; + if (m->m_diagtype == NULL) + { + if (m->m_argv[0] != NULL && + strcmp(m->m_argv[0], "FILE") == 0) + m->m_diagtype = "x-unix"; + else + m->m_diagtype = "smtp"; } - } else if (strcmp(m->m_mailer, "[FILE]") == 0) { @@ -1231,23 +1440,6 @@ makemailer(line) } } - if (strcmp(m->m_mailer, "[IPC]") == 0 || - strcmp(m->m_mailer, "[TCP]") == 0) - { - if (m->m_mtatype == NULL) - m->m_mtatype = "dns"; - if (m->m_addrtype == NULL) - m->m_addrtype = "rfc822"; - if (m->m_diagtype == NULL) - { - if (m->m_argv[0] != NULL && - strcmp(m->m_argv[0], "FILE") == 0) - m->m_diagtype = "x-unix"; - else - m->m_diagtype = "smtp"; - } - } - if (m->m_eol == NULL) { char **pp; @@ -1274,11 +1466,11 @@ makemailer(line) if (s->s_mailer != NULL) { i = s->s_mailer->m_mno; - sm_free(s->s_mailer); + sm_free(s->s_mailer); /* XXX */ } else { - i = NextMailer++; + i = nextmailer++; } Mailer[i] = s->s_mailer = m; m->m_mno = i; @@ -1307,8 +1499,8 @@ munchstring(p, delimptr, delim) int delim; { register char *q; - bool backslash = FALSE; - bool quotemode = FALSE; + bool backslash = false; + bool quotemode = false; static char buf[MAXLINE]; for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++) @@ -1316,7 +1508,7 @@ munchstring(p, delimptr, delim) if (backslash) { /* everything is roughly literal */ - backslash = FALSE; + backslash = false; switch (*p) { case 'r': /* carriage return */ @@ -1340,7 +1532,7 @@ munchstring(p, delimptr, delim) else { if (*p == '\\') - backslash = TRUE; + backslash = true; else if (*p == '"') quotemode = !quotemode; else if (quotemode || *p != delim) @@ -1356,6 +1548,66 @@ munchstring(p, delimptr, delim) return buf; } /* +** EXTRQUOTSTR -- extract a (quoted) string. +** +** This routine deals with quoted (") strings and escaped +** spaces (\\ ). +** +** Parameters: +** p -- source string. +** delimptr -- if non-NULL, set to the pointer of the +** field delimiter character. +** delimbuf -- delimiters for the field. +** st -- if non-NULL, store the return value (whether the +** string was correctly quoted) here. +** +** Returns: +** the extracted string. +** +** Side Effects: +** the returned string is a local static buffer. +** it must be copied before the function is called again. +*/ + +static char * +extrquotstr(p, delimptr, delimbuf, st) + register char *p; + char **delimptr; + char *delimbuf; + bool *st; +{ + register char *q; + bool backslash = false; + bool quotemode = false; + static char buf[MAXLINE]; + + for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++) + { + if (backslash) + { + backslash = false; + if (*p != ' ') + *q++ = '\\'; + } + if (*p == '\\') + backslash = true; + else if (*p == '"') + quotemode = !quotemode; + else if (quotemode || + strchr(delimbuf, (int) *p) == NULL) + *q++ = *p; + else + break; + } + + if (delimptr != NULL) + *delimptr = p; + *q++ = '\0'; + if (st != NULL) + *st = !(quotemode || backslash); + return buf; +} +/* ** MAKEARGV -- break up a string into words ** ** Parameters: @@ -1419,13 +1671,16 @@ printrules() { if (RewriteRules[ruleset] == NULL) continue; - printf("\n----Rule Set %d:", ruleset); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "\n----Rule Set %d:", ruleset); for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) { - printf("\nLHS:"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "\nLHS:"); printav(rwp->r_lhs); - printf("RHS:"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "RHS:"); printav(rwp->r_rhs); } } @@ -1446,52 +1701,66 @@ printmailer(m) { int j; - printf("mailer %d (%s): P=%s S=", m->m_mno, m->m_name, m->m_mailer); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "mailer %d (%s): P=%s S=", m->m_mno, m->m_name, + m->m_mailer); if (RuleSetNames[m->m_se_rwset] == NULL) - printf("%d/", m->m_se_rwset); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d/", + m->m_se_rwset); else - printf("%s/", RuleSetNames[m->m_se_rwset]); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s/", + RuleSetNames[m->m_se_rwset]); if (RuleSetNames[m->m_sh_rwset] == NULL) - printf("%d R=", m->m_sh_rwset); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d R=", + m->m_sh_rwset); else - printf("%s R=", RuleSetNames[m->m_sh_rwset]); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s R=", + RuleSetNames[m->m_sh_rwset]); if (RuleSetNames[m->m_re_rwset] == NULL) - printf("%d/", m->m_re_rwset); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d/", + m->m_re_rwset); else - printf("%s/", RuleSetNames[m->m_re_rwset]); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s/", + RuleSetNames[m->m_re_rwset]); if (RuleSetNames[m->m_rh_rwset] == NULL) - printf("%d ", m->m_rh_rwset); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d ", + m->m_rh_rwset); else - printf("%s ", RuleSetNames[m->m_rh_rwset]); - printf("M=%ld U=%d:%d F=", m->m_maxsize, - (int) m->m_uid, (int) m->m_gid); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s ", + RuleSetNames[m->m_rh_rwset]); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "M=%ld U=%d:%d F=", + m->m_maxsize, (int) m->m_uid, (int) m->m_gid); for (j = '\0'; j <= '\177'; j++) if (bitnset(j, m->m_flags)) - (void) putchar(j); - printf(" L=%d E=", m->m_linelimit); + (void) sm_io_putc(smioout, SM_TIME_DEFAULT, j); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " L=%d E=", + m->m_linelimit); xputs(m->m_eol); if (m->m_defcharset != NULL) - printf(" C=%s", m->m_defcharset); - printf(" T=%s/%s/%s", - m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype, - m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype, - m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype); -#if _FFR_DYNAMIC_TOBUF - printf(" r=%d", m->m_maxrcpt); -#endif /* _FFR_DYNAMIC_TOBUF */ + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " C=%s", + m->m_defcharset); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " T=%s/%s/%s", + m->m_mtatype == NULL + ? "<undefined>" : m->m_mtatype, + m->m_addrtype == NULL + ? "<undefined>" : m->m_addrtype, + m->m_diagtype == NULL + ? "<undefined>" : m->m_diagtype); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " r=%d", m->m_maxrcpt); if (m->m_argv != NULL) { char **a = m->m_argv; - printf(" A="); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " A="); while (*a != NULL) { if (a != m->m_argv) - printf(" "); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + " "); xputs(*a++); } } - printf("\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); } /* ** SETOPTION -- set global processing option @@ -1532,6 +1801,9 @@ static struct resolverflags { "defnames", RES_DEFNAMES }, { "stayopen", RES_STAYOPEN }, { "dnsrch", RES_DNSRCH }, +# ifdef RES_USE_INET6 + { "use_inet6", RES_USE_INET6 }, +# endif /* RES_USE_INET6 */ { "true", 0 }, /* avoid error on old syntax */ { NULL, 0 } }; @@ -1544,9 +1816,9 @@ static struct resolverflags static struct optioninfo { - char *o_name; /* long name of option */ - u_char o_code; /* short name of option */ - u_short o_flags; /* option flags */ + char *o_name; /* long name of option */ + unsigned char o_code; /* short name of option */ + unsigned short o_flags; /* option flags */ } OptionTab[] = { #if defined(SUN_EXTENSIONS) && defined(REMOTE_MODE) @@ -1560,9 +1832,6 @@ static struct optioninfo { "MinFreeBlocks", 'b', OI_SAFE }, { "CheckpointInterval", 'C', OI_SAFE }, { "HoldExpensive", 'c', OI_NONE }, -#if !_FFR_REMOVE_AUTOREBUILD - { "AutoRebuildAliases", 'D', OI_NONE }, -#endif /* !_FFR_REMOVE_AUTOREBUILD */ { "DeliveryMode", 'd', OI_SAFE }, { "ErrorHeader", 'E', OI_NONE }, { "ErrorMode", 'e', OI_SAFE }, @@ -1704,49 +1973,92 @@ static struct optioninfo { "DataFileBufferSize", O_DF_BUFSIZE, OI_NONE }, #define O_XF_BUFSIZE 0xb1 { "XscriptFileBufferSize", O_XF_BUFSIZE, OI_NONE }, -# define O_LDAPDEFAULTSPEC 0xb2 +#define O_LDAPDEFAULTSPEC 0xb2 { "LDAPDefaultSpec", O_LDAPDEFAULTSPEC, OI_NONE }, #if _FFR_QUEUEDELAY -#define O_QUEUEDELAY 0xb3 +# define O_QUEUEDELAY 0xb3 { "QueueDelay", O_QUEUEDELAY, OI_NONE }, #endif /* _FFR_QUEUEDELAY */ -# define O_SRVCERTFILE 0xb4 +#define O_SRVCERTFILE 0xb4 { "ServerCertFile", O_SRVCERTFILE, OI_NONE }, -# define O_SRVKEYFILE 0xb5 +#define O_SRVKEYFILE 0xb5 { "Serverkeyfile", O_SRVKEYFILE, OI_NONE }, -# define O_CLTCERTFILE 0xb6 +#define O_CLTCERTFILE 0xb6 { "ClientCertFile", O_CLTCERTFILE, OI_NONE }, -# define O_CLTKEYFILE 0xb7 +#define O_CLTKEYFILE 0xb7 { "Clientkeyfile", O_CLTKEYFILE, OI_NONE }, -# define O_CACERTFILE 0xb8 +#define O_CACERTFILE 0xb8 { "CACERTFile", O_CACERTFILE, OI_NONE }, -# define O_CACERTPATH 0xb9 +#define O_CACERTPATH 0xb9 { "CACERTPath", O_CACERTPATH, OI_NONE }, -# define O_DHPARAMS 0xba +#define O_DHPARAMS 0xba { "DHParameters", O_DHPARAMS, OI_NONE }, -#if _FFR_MILTER #define O_INPUTMILTER 0xbb { "InputMailFilters", O_INPUTMILTER, OI_NONE }, #define O_MILTER 0xbc { "Milter", O_MILTER, OI_SUBOPT }, -#endif /* _FFR_MILTER */ #define O_SASLOPTS 0xbd { "AuthOptions", O_SASLOPTS, OI_NONE }, -#if _FFR_QUEUE_FILE_MODE #define O_QUEUE_FILE_MODE 0xbe { "QueueFileMode", O_QUEUE_FILE_MODE, OI_NONE }, -#endif /* _FFR_QUEUE_FILE_MODE */ -# if _FFR_TLS_1 +#if _FFR_TLS_1 # define O_DHPARAMS5 0xbf { "DHParameters512", O_DHPARAMS5, OI_NONE }, # define O_CIPHERLIST 0xc0 { "CipherList", O_CIPHERLIST, OI_NONE }, -# endif /* _FFR_TLS_1 */ -# define O_RANDFILE 0xc1 +#endif /* _FFR_TLS_1 */ +#define O_RANDFILE 0xc1 { "RandFile", O_RANDFILE, OI_NONE }, +#define O_TLS_SRV_OPTS 0xc2 + { "TLSSrvOptions", O_TLS_SRV_OPTS, OI_NONE }, +#define O_RCPTTHROT 0xc3 + { "BadRcptThrottle", O_RCPTTHROT, OI_SAFE }, +#define O_DLVR_MIN 0xc4 + { "DeliverByMin", O_DLVR_MIN, OI_NONE }, +#define O_MAXQUEUECHILDREN 0xc5 + { "MaxQueueChildren", O_MAXQUEUECHILDREN, OI_NONE }, +#define O_MAXRUNNERSPERQUEUE 0xc6 + { "MaxRunnersPerQueue", O_MAXRUNNERSPERQUEUE, OI_NONE }, +#define O_DIRECTSUBMODIFIERS 0xc7 + { "DirectSubmissionModifiers", O_DIRECTSUBMODIFIERS, OI_NONE }, +#define O_NICEQUEUERUN 0xc8 + { "NiceQueueRun", O_NICEQUEUERUN, OI_NONE }, +#define O_SHMKEY 0xc9 + { "SharedMemoryKey", O_SHMKEY, OI_NONE }, +#define O_SASLBITS 0xca + { "AuthMaxBits", O_SASLBITS, OI_NONE }, +#define O_MBDB 0xcb + { "MailboxDatabase", O_MBDB, OI_NONE }, +#define O_MSQ 0xcc + { "UseMSP", O_MSQ, OI_NONE }, +#define O_DELAY_LA 0xcd + { "DelayLA", O_DELAY_LA, OI_NONE }, +#define O_FASTSPLIT 0xce + { "FastSplit", O_FASTSPLIT, OI_NONE }, +#if _FFR_SOFT_BOUNCE +# define O_SOFTBOUNCE 0xcf + { "SoftBounce", O_SOFTBOUNCE, OI_NONE }, +#endif /* _FFR_SOFT_BOUNCE */ { NULL, '\0', OI_NONE } }; +# define CANONIFY(val) + +# define SET_OPT_DEFAULT(opt, val) opt = val + +/* set a string option by expanding the value and assigning it */ +/* WARNING this belongs ONLY into a case statement! */ +#define SET_STRING_EXP(str) \ + expand(val, exbuf, sizeof exbuf, e); \ + newval = sm_pstrdup_x(exbuf); \ + if (str != NULL) \ + sm_free(str); \ + CANONIFY(newval); \ + str = newval; \ + break + +#define OPTNAME o->o_name == NULL ? "<unknown>" : o->o_name + void setoption(opt, val, safe, sticky, e) int opt; @@ -1764,8 +2076,12 @@ setoption(opt, val, safe, sticky, e) char buf[50]; extern bool Warn_Q_option; #if _FFR_ALLOW_SASLINFO - extern int SubmitMode; + extern unsigned int SubmitMode; #endif /* _FFR_ALLOW_SASLINFO */ +#if STARTTLS + char *newval; + char exbuf[MAXLINE]; +#endif /* STARTTLS */ errno = 0; if (opt == ' ') @@ -1795,7 +2111,7 @@ setoption(opt, val, safe, sticky, e) sel = NULL; for (o = OptionTab; o->o_name != NULL; o++) { - if (strncasecmp(o->o_name, val, strlen(val)) != 0) + if (sm_strncasecmp(o->o_name, val, strlen(val)) != 0) continue; if (strlen(o->o_name) == strlen(val)) { @@ -1845,21 +2161,18 @@ setoption(opt, val, safe, sticky, e) if (subopt != NULL && !bitset(OI_SUBOPT, o->o_flags)) { if (tTd(37, 1)) - dprintf("setoption: %s does not support suboptions, ignoring .%s\n", - o->o_name == NULL ? "<unknown>" : o->o_name, - subopt); + sm_dprintf("setoption: %s does not support suboptions, ignoring .%s\n", + OPTNAME, subopt); subopt = NULL; } if (tTd(37, 1)) { - dprintf(isascii(opt) && isprint(opt) ? - "setoption %s (%c)%s%s=" : - "setoption %s (0x%x)%s%s=", - o->o_name == NULL ? "<unknown>" : o->o_name, - opt, - subopt == NULL ? "" : ".", - subopt == NULL ? "" : subopt); + sm_dprintf(isascii(opt) && isprint(opt) ? + "setoption %s (%c)%s%s=" : + "setoption %s (0x%x)%s%s=", + OPTNAME, opt, subopt == NULL ? "" : ".", + subopt == NULL ? "" : subopt); xputs(val); } @@ -1870,7 +2183,7 @@ setoption(opt, val, safe, sticky, e) if (!sticky && bitnset(opt, StickyOpt)) { if (tTd(37, 1)) - dprintf(" (ignored)\n"); + sm_dprintf(" (ignored)\n"); return; } @@ -1879,7 +2192,7 @@ setoption(opt, val, safe, sticky, e) */ if (!safe && RealUid == 0) - safe = TRUE; + safe = true; if (!safe && !bitset(OI_SAFE, o->o_flags)) { if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) @@ -1887,13 +2200,13 @@ setoption(opt, val, safe, sticky, e) int dp; if (tTd(37, 1)) - dprintf(" (unsafe)"); - dp = drop_privileges(TRUE); + sm_dprintf(" (unsafe)"); + dp = drop_privileges(true); setstat(dp); } } if (tTd(37, 1)) - dprintf("\n"); + sm_dprintf("\n"); switch (opt & 0xff) { @@ -1905,14 +2218,14 @@ setoption(opt, val, safe, sticky, e) #if MIME8TO7 switch (*val) { - case 'm': /* convert 8-bit, convert MIME */ - MimeMode = MM_CVTMIME|MM_MIME8BIT; - break; - case 'p': /* pass 8 bit, convert MIME */ MimeMode = MM_CVTMIME|MM_PASS8BIT; break; + case 'm': /* convert 8-bit, convert MIME */ + MimeMode = MM_CVTMIME|MM_MIME8BIT; + break; + case 's': /* strict adherence */ MimeMode = MM_CVTMIME; break; @@ -1937,23 +2250,30 @@ setoption(opt, val, safe, sticky, e) default: syserr("Unknown 8-bit mode %c", *val); - finis(FALSE, EX_USAGE); + finis(false, EX_USAGE); } #else /* MIME8TO7 */ - printf("Warning: Option EightBitMode requires MIME8TO7 support\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: Option: %s requires MIME8TO7 support\n", + OPTNAME); #endif /* MIME8TO7 */ break; case 'A': /* set default alias file */ if (val[0] == '\0') - setalias("aliases"); + { + char *al; + + SET_OPT_DEFAULT(al, "aliases"); + setalias(al); + } else setalias(val); break; case 'a': /* look N minutes for "@:@" in alias file */ if (val[0] == '\0') - SafeAlias = 5 * 60; /* five minutes */ + SafeAlias = 5 MINUTES; else SafeAlias = convtime(val, 'm'); break; @@ -1991,12 +2311,6 @@ setoption(opt, val, safe, sticky, e) case SM_QUEUE: /* queue only */ case SM_DEFER: /* queue only and defer map lookups */ -#if !QUEUE - syserr("need QUEUE to set -odqueue or -oddefer"); - break; -#endif /* !QUEUE */ - /* FALLTHROUGH */ - case SM_DELIVER: /* do everything */ case SM_FORK: /* fork after verification */ set_delivery_mode(*val, e); @@ -2004,16 +2318,10 @@ setoption(opt, val, safe, sticky, e) default: syserr("Unknown delivery mode %c", *val); - finis(FALSE, EX_USAGE); + finis(false, EX_USAGE); } break; -#if !_FFR_REMOVE_AUTOREBUILD - case 'D': /* rebuild alias database as needed */ - AutoRebuild = atobool(val); - break; -#endif /* !_FFR_REMOVE_AUTOREBUILD */ - case 'E': /* error message header/header file */ if (*val != '\0') ErrMsgFile = newstr(val); @@ -2064,9 +2372,12 @@ setoption(opt, val, safe, sticky, e) case 'H': /* help file */ if (val[0] == '\0') - HelpFile = "helpfile"; + { + SET_OPT_DEFAULT(HelpFile, "helpfile"); + } else { + CANONIFY(val); HelpFile = newstr(val); } break; @@ -2087,9 +2398,9 @@ setoption(opt, val, safe, sticky, e) p++; if (*p == '\0') break; - clearmode = FALSE; + clearmode = false; if (*p == '-') - clearmode = TRUE; + clearmode = true; else if (*p != '+') p--; p++; @@ -2098,21 +2409,19 @@ setoption(opt, val, safe, sticky, e) p++; if (*p != '\0') *p++ = '\0'; - if (strcasecmp(q, "HasWildcardMX") == 0) + if (sm_strcasecmp(q, "HasWildcardMX") == 0) { HasWildcardMX = !clearmode; continue; } -#if _FFR_WORKAROUND_BROKEN_NAMESERVERS if (sm_strcasecmp(q, "WorkAroundBrokenAAAA") == 0) { WorkAroundBrokenAAAA = !clearmode; continue; } -#endif /* _FFR_WORKAROUND_BROKEN_NAMESERVERS */ for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) { - if (strcasecmp(q, rfp->rf_name) == 0) + if (sm_strcasecmp(q, rfp->rf_name) == 0) break; } if (rfp->rf_name == NULL) @@ -2123,8 +2432,8 @@ setoption(opt, val, safe, sticky, e) _res.options |= rfp->rf_bits; } if (tTd(8, 2)) - dprintf("_res.options = %x, HasWildcardMX = %d\n", - (u_int) _res.options, HasWildcardMX); + sm_dprintf("_res.options = %x, HasWildcardMX = %d\n", + (unsigned int) _res.options, HasWildcardMX); #else /* NAMED_BIND */ usrerr("name server (I option) specified but BIND not compiled in"); #endif /* NAMED_BIND */ @@ -2139,6 +2448,7 @@ setoption(opt, val, safe, sticky, e) break; case 'J': /* .forward search path */ + CANONIFY(val); ForwardPath = newstr(val); break; @@ -2162,14 +2472,14 @@ setoption(opt, val, safe, sticky, e) break; case 'M': /* define macro */ - sticky = FALSE; - mid = macid(val, &ep); + sticky = false; + mid = macid_parse(val, &ep); if (mid == 0) break; p = newstr(ep); if (!safe) cleanstrcpy(p, p, MAXNAME); - define(mid, p, CurEnv); + macdefine(&CurEnv->e_macro, A_TEMP, mid, p); break; case 'm': /* send to me too */ @@ -2183,12 +2493,8 @@ setoption(opt, val, safe, sticky, e) /* 'N' available -- was "net name" */ case 'O': /* daemon options */ -#if DAEMON if (!setdaemonoptions(val)) syserr("too many daemons defined (%d max)", MAXDAEMONS); -#else /* DAEMON */ - syserr("DaemonPortOptions (O option) set but DAEMON not compiled in"); -#endif /* DAEMON */ break; case 'o': /* assume old style headers */ @@ -2217,7 +2523,7 @@ setoption(opt, val, safe, sticky, e) for (pv = PrivacyValues; pv->pv_name != NULL; pv++) { - if (strcasecmp(val, pv->pv_name) == 0) + if (sm_strcasecmp(val, pv->pv_name) == 0) break; } if (pv->pv_name == NULL) @@ -2225,7 +2531,7 @@ setoption(opt, val, safe, sticky, e) else PrivacyFlags |= pv->pv_flag; } - sticky = FALSE; + sticky = false; break; case 'P': /* postmaster copy address for returned mail */ @@ -2246,7 +2552,7 @@ setoption(opt, val, safe, sticky, e) QueueDir = newstr(val); } if (RealUid != 0 && !safe) - Warn_Q_option = TRUE; + Warn_Q_option = true; break; case 'R': /* don't prune routes */ @@ -2262,15 +2568,21 @@ setoption(opt, val, safe, sticky, e) case 'S': /* status file */ if (val[0] == '\0') - StatFile = "statistics"; + { + SET_OPT_DEFAULT(StatFile, "statistics"); + } else { + CANONIFY(val); StatFile = newstr(val); } break; case 's': /* be super safe, even if expensive */ - SuperSafe = atobool(val); + if (tolower(*val) == 'i') + SuperSafe = SAFE_INTERACTIVE; + else + SuperSafe = atobool(val) ? SAFE_REALLY : SAFE_NO; break; case 'T': /* queue timeout */ @@ -2324,14 +2636,14 @@ setoption(opt, val, safe, sticky, e) } } -#ifdef UID_MAX +# ifdef UID_MAX if (DefUid > UID_MAX) { syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored", - (long) DefUid, (long) UID_MAX); + (long)DefUid, (long)UID_MAX); break; } -#endif /* UID_MAX */ +# endif /* UID_MAX */ /* handle the group if it is there */ if (*p == '\0') @@ -2358,10 +2670,14 @@ setoption(opt, val, safe, sticky, e) QueueLA = atoi(val); break; - case 'X': /* load avg at which to auto-reject connections */ + case 'X': /* load avg at which to auto-reject connections */ RefuseLA = atoi(val); break; + case O_DELAY_LA: /* load avg at which to delay connections */ + DelayLA = atoi(val); + break; + case 'y': /* work recipient factor */ WkRecipFact = atoi(val); break; @@ -2382,11 +2698,21 @@ setoption(opt, val, safe, sticky, e) case O_QUEUESORTORD: /* queue sorting order */ switch (*val) { + case 'f': /* File Name */ + case 'F': + QueueSortOrder = QSO_BYFILENAME; + break; + case 'h': /* Host first */ case 'H': QueueSortOrder = QSO_BYHOST; break; + case 'm': /* Modification time */ + case 'M': + QueueSortOrder = QSO_BYMODTIME; + break; + case 'p': /* Priority order */ case 'P': QueueSortOrder = QSO_BYPRIORITY; @@ -2397,10 +2723,17 @@ setoption(opt, val, safe, sticky, e) QueueSortOrder = QSO_BYTIME; break; - case 'f': /* File Name */ - case 'F': - QueueSortOrder = QSO_BYFILENAME; + case 'r': /* Random */ + case 'R': + QueueSortOrder = QSO_RANDOM; + break; + +#if _FFR_RHS + case 's': /* Shuffled host name */ + case 'S': + QueueSortOrder = QSO_BYSHUFFLE; break; +#endif /* _FFR_RHS */ default: syserr("Invalid queue sort order \"%s\"", val); @@ -2445,6 +2778,7 @@ setoption(opt, val, safe, sticky, e) #endif /* _FFR_QUEUEDELAY */ case O_HOSTSFILE: /* pathname of /etc/hosts file */ + CANONIFY(val); HostsFile = newstr(val); break; @@ -2453,10 +2787,11 @@ setoption(opt, val, safe, sticky, e) break; case O_DEFCHARSET: /* default character set for mimefying */ - DefaultCharSet = newstr(denlstring(val, TRUE, TRUE)); + DefaultCharSet = newstr(denlstring(val, true, true)); break; case O_SSFILE: /* service switch file */ + CANONIFY(val); ServiceSwitchFile = newstr(val); break; @@ -2465,15 +2800,15 @@ setoption(opt, val, safe, sticky, e) break; case O_NORCPTACTION: /* what to do if no recipient */ - if (strcasecmp(val, "none") == 0) + if (sm_strcasecmp(val, "none") == 0) NoRecipientAction = NRA_NO_ACTION; - else if (strcasecmp(val, "add-to") == 0) + else if (sm_strcasecmp(val, "add-to") == 0) NoRecipientAction = NRA_ADD_TO; - else if (strcasecmp(val, "add-apparently-to") == 0) + else if (sm_strcasecmp(val, "add-apparently-to") == 0) NoRecipientAction = NRA_ADD_APPARENTLY_TO; - else if (strcasecmp(val, "add-bcc") == 0) + else if (sm_strcasecmp(val, "add-bcc") == 0) NoRecipientAction = NRA_ADD_BCC; - else if (strcasecmp(val, "add-to-undisclosed") == 0) + else if (sm_strcasecmp(val, "add-to-undisclosed") == 0) NoRecipientAction = NRA_ADD_TO_UNDISCLOSED; else syserr("Invalid NoRecipientAction: %s", val); @@ -2492,13 +2827,37 @@ setoption(opt, val, safe, sticky, e) break; case O_MAXQUEUERUN: /* max # of jobs in a single queue run */ - MaxQueueRun = atol(val); + MaxQueueRun = atoi(val); break; case O_MAXCHILDREN: /* max # of children of daemon */ MaxChildren = atoi(val); break; + case O_MAXQUEUECHILDREN: /* max # of children of daemon */ + MaxQueueChildren = atoi(val); + break; + + case O_MAXRUNNERSPERQUEUE: /* max # runners in a queue group */ + MaxRunnersPerQueue = atoi(val); + break; + + case O_NICEQUEUERUN: /* nice queue runs */ + + /* XXX do we want to check the range? > 0 ? */ + NiceQueueRun = atoi(val); + break; + + case O_SHMKEY : /* shared memory key */ +#if SM_CONF_SHM + ShmKey = atol(val); +#else /* SM_CONF_SHM */ + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: Option: %s requires shared memory support (-DSM_CONF_SHM)\n", + OPTNAME); +#endif /* SM_CONF_SHM */ + break; + #if _FFR_MAX_FORWARD_ENTRIES case O_MAXFORWARD: /* max # of forward entries */ MaxForwardEntries = atoi(val); @@ -2510,11 +2869,12 @@ setoption(opt, val, safe, sticky, e) break; case O_MUSTQUOTE: /* must quote these characters in phrases */ - (void) strlcpy(buf, "@,;:\\()[]", sizeof buf); - if (strlen(val) < (SIZE_T) sizeof buf - 10) - (void) strlcat(buf, val, sizeof buf); + (void) sm_strlcpy(buf, "@,;:\\()[]", sizeof buf); + if (strlen(val) < sizeof buf - 10) + (void) sm_strlcat(buf, val, sizeof buf); else - printf("Warning: MustQuoteChars too long, ignored.\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: MustQuoteChars too long, ignored.\n"); MustQuoteChars = newstr(buf); break; @@ -2528,7 +2888,8 @@ setoption(opt, val, safe, sticky, e) case O_OPCHARS: /* operator characters (old $o macro) */ if (OperatorChars != NULL) - printf("Warning: OperatorChars is being redefined.\n It should only be set before ruleset definitions.\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: OperatorChars is being redefined.\n It should only be set before ruleset definitions.\n"); OperatorChars = newstr(munchstring(val, NULL, '\0')); break; @@ -2559,15 +2920,13 @@ setoption(opt, val, safe, sticky, e) break; case O_DBLBOUNCE: /* address to which to send double bounces */ - if (val[0] != '\0') - DoubleBounceAddr = newstr(val); - else - syserr("readcf: option DoubleBounceAddress: value required"); + DoubleBounceAddr = newstr(val); break; case O_HSDIR: /* persistent host status directory */ if (val[0] != '\0') { + CANONIFY(val); HostStatDir = newstr(val); } break; @@ -2607,21 +2966,37 @@ setoption(opt, val, safe, sticky, e) RunAsUid = pw->pw_uid; RunAsGid = pw->pw_gid; } + else if (EffGid == pw->pw_gid) + RunAsGid = pw->pw_gid; + else if (UseMSP && *p == '\0') + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "WARNING: RunAsGid for MSP ignored, check group ids (egid=%d, want=%d)\n", + (int) EffGid, + (int) pw->pw_gid); } -#ifdef UID_MAX +# ifdef UID_MAX if (RunAsUid > UID_MAX) { syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored", (long) RunAsUid, (long) UID_MAX); break; } -#endif /* UID_MAX */ +# endif /* UID_MAX */ if (*p != '\0') { if (isascii(*p) && isdigit(*p)) { - if (can_setuid) - RunAsGid = atoi(p); + gid_t runasgid; + + runasgid = (gid_t) atoi(p); + if (can_setuid || EffGid == runasgid) + RunAsGid = runasgid; + else if (UseMSP) + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "WARNING: RunAsGid for MSP ignored, check group ids (egid=%d, want=%d)\n", + (int) EffGid, + (int) runasgid); } else { @@ -2631,13 +3006,19 @@ setoption(opt, val, safe, sticky, e) if (gr == NULL) syserr("readcf: option RunAsUser: unknown group %s", p); - else if (can_setuid) + else if (can_setuid || EffGid == gr->gr_gid) RunAsGid = gr->gr_gid; + else if (UseMSP) + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "WARNING: RunAsGid for MSP ignored, check group ids (egid=%d, want=%d)\n", + (int) EffGid, + (int) gr->gr_gid); } } if (tTd(47, 5)) - dprintf("readcf: RunAsUser = %d:%d\n", - (int)RunAsUid, (int)RunAsGid); + sm_dprintf("readcf: RunAsUser = %d:%d\n", + (int) RunAsUid, (int) RunAsGid); break; case O_DSN_RRT: @@ -2645,12 +3026,10 @@ setoption(opt, val, safe, sticky, e) break; case O_PIDFILE: - if (PidFile != NULL) - sm_free(PidFile); - PidFile = newstr(val); + PSTRSET(PidFile, val); break; - case O_DONTBLAMESENDMAIL: + case O_DONTBLAMESENDMAIL: p = val; for (;;) { @@ -2670,7 +3049,7 @@ setoption(opt, val, safe, sticky, e) for (dbs = DontBlameSendmailValues; dbs->dbs_name != NULL; dbs++) { - if (strcasecmp(val, dbs->dbs_name) == 0) + if (sm_strcasecmp(val, dbs->dbs_name) == 0) break; } if (dbs->dbs_name == NULL) @@ -2680,21 +3059,29 @@ setoption(opt, val, safe, sticky, e) else setbitn(dbs->dbs_flag, DontBlameSendmail); } - sticky = FALSE; + sticky = false; break; case O_DPI: - DontProbeInterfaces = atobool(val); + if (sm_strcasecmp(val, "loopback") == 0) + DontProbeInterfaces = DPI_SKIPLOOPBACK; + else if (atobool(val)) + DontProbeInterfaces = DPI_PROBENONE; + else + DontProbeInterfaces = DPI_PROBEALL; break; case O_MAXRCPT: MaxRcptPerMsg = atoi(val); break; + case O_RCPTTHROT: + BadRcptThrottle = atoi(val); + break; + case O_DEADLETTER: - if (DeadLetterDrop != NULL) - sm_free(DeadLetterDrop); - DeadLetterDrop = newstr(val); + CANONIFY(val); + PSTRSET(DeadLetterDrop, val); break; #if _FFR_DONTLOCKFILESFORREAD_OPTION @@ -2710,26 +3097,36 @@ setoption(opt, val, safe, sticky, e) case O_CNCTONLYTO: /* XXX should probably use gethostbyname */ #if NETINET || NETINET6 + ConnectOnlyTo.sa.sa_family = AF_UNSPEC; # if NETINET6 - if (inet_addr(val) == INADDR_NONE) - { + if (anynet_pton(AF_INET6, val, + &ConnectOnlyTo.sin6.sin6_addr) != 1) ConnectOnlyTo.sa.sa_family = AF_INET6; - if (inet_pton(AF_INET6, val, - &ConnectOnlyTo.sin6.sin6_addr) != 1) - syserr("readcf: option ConnectOnlyTo: invalid IP address %s", - val); - } else # endif /* NETINET6 */ +# if NETINET { - ConnectOnlyTo.sa.sa_family = AF_INET; ConnectOnlyTo.sin.sin_addr.s_addr = inet_addr(val); + if (ConnectOnlyTo.sin.sin_addr.s_addr != INADDR_NONE) + ConnectOnlyTo.sa.sa_family = AF_INET; + } + +# endif /* NETINET */ + if (ConnectOnlyTo.sa.sa_family == AF_UNSPEC) + { + syserr("readcf: option ConnectOnlyTo: invalid IP address %s", + val); + break; } #endif /* NETINET || NETINET6 */ break; case O_TRUSTUSER: -#if HASFCHOWN +# if !HASFCHOWN + if (!UseMSP) + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "readcf: option TrustedUser may cause problems on systems\n which do not support fchown() if UseMSP is not set.\n"); +# endif /* !HASFCHOWN */ if (isascii(*val) && isdigit(*val)) TrustedUid = atoi(val); else @@ -2755,9 +3152,6 @@ setoption(opt, val, safe, sticky, e) TrustedUid = 0; } # endif /* UID_MAX */ -#else /* HASFCHOWN */ - syserr("readcf: option TrustedUser: can not be used on systems which do not support fchown()"); -#endif /* HASFCHOWN */ break; case O_MAXMIMEHDRLEN: @@ -2773,18 +3167,18 @@ setoption(opt, val, safe, sticky, e) if (MaxMimeHeaderLength < 0) MaxMimeHeaderLength = 0; else if (MaxMimeHeaderLength < 128) - printf("Warning: MaxMimeHeaderLength: header length limit set lower than 128\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: MaxMimeHeaderLength: header length limit set lower than 128\n"); if (MaxMimeFieldLength < 0) MaxMimeFieldLength = 0; else if (MaxMimeFieldLength < 40) - printf("Warning: MaxMimeHeaderLength: field length limit set lower than 40\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: MaxMimeHeaderLength: field length limit set lower than 40\n"); break; case O_CONTROLSOCKET: - if (ControlSocketName != NULL) - sm_free(ControlSocketName); - ControlSocketName = newstr(val); + PSTRSET(ControlSocketName, val); break; case O_MAXHDRSLEN: @@ -2792,20 +3186,23 @@ setoption(opt, val, safe, sticky, e) if (MaxHeadersLength > 0 && MaxHeadersLength < (MAXHDRSLEN / 2)) - printf("Warning: MaxHeadersLength: headers length limit set lower than %d\n", (MAXHDRSLEN / 2)); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: MaxHeadersLength: headers length limit set lower than %d\n", + (MAXHDRSLEN / 2)); break; case O_PROCTITLEPREFIX: - if (ProcTitlePrefix != NULL) - sm_free(ProcTitlePrefix); - ProcTitlePrefix = newstr(val); + PSTRSET(ProcTitlePrefix, val); break; #if SASL case O_SASLINFO: -#if _FFR_ALLOW_SASLINFO +# if _FFR_ALLOW_SASLINFO /* - ** Allow users to select their own authinfo file. + ** Allow users to select their own authinfo file + ** under certain circumstances, otherwise just ignore + ** the option. If the option isn't ignored, several + ** commands don't work very well, e.g., mailq. ** However, this is not a "perfect" solution. ** If mail is queued, the authentication info ** will not be used in subsequent delivery attempts. @@ -2814,22 +3211,14 @@ setoption(opt, val, safe, sticky, e) */ if (!bitset(SUBMIT_MSA, SubmitMode) && RealUid != 0 && RunAsUid != RealUid) - { - errno = 0; - syserr("Error: %s only allowed with -U\n", - o->o_name == NULL ? "<unknown>" : o->o_name); - ExitStat = EX_USAGE; break; - } -#endif /* _FFR_ALLOW_SASLINFO */ - if (SASLInfo != NULL) - sm_free(SASLInfo); - SASLInfo = newstr(val); +# endif /* _FFR_ALLOW_SASLINFO */ + PSTRSET(SASLInfo, val); break; case O_SASLMECH: if (AuthMechanisms != NULL) - sm_free(AuthMechanisms); + sm_free(AuthMechanisms); /* XXX */ if (*val != '\0') AuthMechanisms = newstr(val); else @@ -2839,12 +3228,11 @@ setoption(opt, val, safe, sticky, e) case O_SASLOPTS: while (val != NULL && *val != '\0') { - switch(*val) + switch (*val) { case 'A': SASLOpts |= SASL_AUTH_AUTH; break; -# if _FFR_SASL_OPTS case 'a': SASLOpts |= SASL_SEC_NOACTIVE; break; @@ -2863,13 +3251,17 @@ setoption(opt, val, safe, sticky, e) case 'y': SASLOpts |= SASL_SEC_NOANONYMOUS; break; -# endif /* _FFR_SASL_OPTS */ + case ' ': /* ignore */ + case '\t': /* ignore */ + case ',': /* ignore */ + break; default: - printf("Warning: Option: %s unknown parameter '%c'\n", - o->o_name == NULL ? "<unknown>" - : o->o_name, - (isascii(*val) && isprint(*val)) ? *val - : '?'); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: Option: %s unknown parameter '%c'\n", + OPTNAME, + (isascii(*val) && + isprint(*val)) + ? *val : '?'); break; } ++val; @@ -2878,80 +3270,99 @@ setoption(opt, val, safe, sticky, e) ++val; } break; + case O_SASLBITS: + MaxSLBits = atoi(val); + break; #else /* SASL */ case O_SASLINFO: case O_SASLMECH: case O_SASLOPTS: - printf("Warning: Option: %s requires SASL support (-DSASL)\n", - o->o_name == NULL ? "<unknown>" : o->o_name); + case O_SASLBITS: + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: Option: %s requires SASL support (-DSASL)\n", + OPTNAME); break; #endif /* SASL */ #if STARTTLS case O_SRVCERTFILE: - if (SrvCERTfile != NULL) - sm_free(SrvCERTfile); - SrvCERTfile = newstr(val); - break; - + SET_STRING_EXP(SrvCERTfile); case O_SRVKEYFILE: - if (Srvkeyfile != NULL) - sm_free(Srvkeyfile); - Srvkeyfile = newstr(val); - break; - + SET_STRING_EXP(Srvkeyfile); case O_CLTCERTFILE: - if (CltCERTfile != NULL) - sm_free(CltCERTfile); - CltCERTfile = newstr(val); - break; - + SET_STRING_EXP(CltCERTfile); case O_CLTKEYFILE: - if (Cltkeyfile != NULL) - sm_free(Cltkeyfile); - Cltkeyfile = newstr(val); - break; - + SET_STRING_EXP(Cltkeyfile); case O_CACERTFILE: - if (CACERTfile != NULL) - sm_free(CACERTfile); - CACERTfile = newstr(val); - break; - + SET_STRING_EXP(CACERTfile); case O_CACERTPATH: - if (CACERTpath != NULL) - sm_free(CACERTpath); - CACERTpath = newstr(val); - break; - + SET_STRING_EXP(CACERTpath); case O_DHPARAMS: - if (DHParams != NULL) - sm_free(DHParams); - DHParams = newstr(val); - break; - -# if _FFR_TLS_1 + SET_STRING_EXP(DHParams); +# if _FFR_TLS_1 case O_DHPARAMS5: - if (DHParams5 != NULL) - sm_free(DHParams5); - DHParams5 = newstr(val); - break; - + SET_STRING_EXP(DHParams5); case O_CIPHERLIST: - if (CipherList != NULL) - sm_free(CipherList); - CipherList = newstr(val); + SET_STRING_EXP(CipherList); +# endif /* _FFR_TLS_1 */ + + /* + ** XXX How about options per daemon/client instead of globally? + ** This doesn't work well for some options, e.g., no server cert, + ** but fine for others. + ** + ** XXX Some people may want different certs per server. + ** + ** See also srvfeatures() + */ + + case O_TLS_SRV_OPTS: + while (val != NULL && *val != '\0') + { + switch (*val) + { + case 'V': + TLS_Srv_Opts |= TLS_I_NO_VRFY; + break; +# if _FFR_TLS_1 + /* + ** Server without a cert? That works only if + ** AnonDH is enabled as cipher, which is not in the + ** default list. Hence the CipherList option must + ** be available. Moreover: which clients support this + ** besides sendmail with this setting? + */ + + case 'C': + TLS_Srv_Opts &= ~TLS_I_SRV_CERT; + break; +# endif /* _FFR_TLS_1 */ + case ' ': /* ignore */ + case '\t': /* ignore */ + case ',': /* ignore */ + break; + default: + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: Option: %s unknown parameter '%c'\n", + OPTNAME, + (isascii(*val) && + isprint(*val)) + ? *val : '?'); + break; + } + ++val; + val = strpbrk(val, ", \t"); + if (val != NULL) + ++val; + } break; -# endif /* _FFR_TLS_1 */ case O_RANDFILE: - if (RandFile != NULL) - sm_free(RandFile); - RandFile= newstr(val); + PSTRSET(RandFile, val); break; -# else /* STARTTLS */ +#else /* STARTTLS */ case O_SRVCERTFILE: case O_SRVKEYFILE: case O_CLTCERTFILE: @@ -2959,23 +3370,20 @@ setoption(opt, val, safe, sticky, e) case O_CACERTFILE: case O_CACERTPATH: case O_DHPARAMS: -# if _FFR_TLS_1 +# if _FFR_TLS_1 case O_DHPARAMS5: case O_CIPHERLIST: -# endif /* _FFR_TLS_1 */ +# endif /* _FFR_TLS_1 */ case O_RANDFILE: - printf("Warning: Option: %s requires TLS support\n", - o->o_name == NULL ? "<unknown>" : o->o_name); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: Option: %s requires TLS support\n", + OPTNAME); break; -# endif /* STARTTLS */ +#endif /* STARTTLS */ case O_CLIENTPORT: -#if DAEMON setclientoptions(val); -#else /* DAEMON */ - syserr("ClientPortOptions (O option) set but DAEMON not compiled in"); -#endif /* DAEMON */ break; case O_DF_BUFSIZE: @@ -2987,37 +3395,80 @@ setoption(opt, val, safe, sticky, e) break; case O_LDAPDEFAULTSPEC: -#ifdef LDAPMAP +#if LDAPMAP ldapmap_set_defaults(val); #else /* LDAPMAP */ - printf("Warning: Option: %s requires LDAP support (-DLDAPMAP)\n", - o->o_name == NULL ? "<unknown>" : o->o_name); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: Option: %s requires LDAP support (-DLDAPMAP)\n", + OPTNAME); #endif /* LDAPMAP */ break; -#if _FFR_MILTER case O_INPUTMILTER: +#if MILTER InputFilterList = newstr(val); +#else /* MILTER */ + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: Option: %s requires Milter support (-DMILTER)\n", + OPTNAME); +#endif /* MILTER */ break; case O_MILTER: +#if MILTER milter_set_option(subopt, val, sticky); +#else /* MILTER */ + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Warning: Option: %s requires Milter support (-DMILTER)\n", + OPTNAME); +#endif /* MILTER */ break; -#endif /* _FFR_MILTER */ -#if _FFR_QUEUE_FILE_MODE case O_QUEUE_FILE_MODE: /* queue file mode */ QueueFileMode = atooct(val) & 0777; break; -#endif /* _FFR_QUEUE_FILE_MODE */ + + case O_DLVR_MIN: /* deliver by minimum time */ + DeliverByMin = convtime(val, 's'); + break; + + /* modifiers {daemon_flags} for direct submissions */ + case O_DIRECTSUBMODIFIERS: + { + BITMAP256 m; /* ignored */ + extern ENVELOPE BlankEnvelope; + + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{daemon_flags}"), + getmodifiers(val, m)); + } + break; + + case O_FASTSPLIT: + FastSplit = atoi(val); + break; + + case O_MBDB: + Mbdb = newstr(val); + break; + + case O_MSQ: + UseMSP = atobool(val); + break; + +#if _FFR_SOFT_BOUNCE + case O_SOFTBOUNCE: + SoftBounce = atobool(val); + break; +#endif /* _FFR_SOFT_BOUNCE */ default: if (tTd(37, 1)) { if (isascii(opt) && isprint(opt)) - dprintf("Warning: option %c unknown\n", opt); + sm_dprintf("Warning: option %c unknown\n", opt); else - dprintf("Warning: option 0x%x unknown\n", opt); + sm_dprintf("Warning: option 0x%x unknown\n", opt); } break; } @@ -3059,19 +3510,19 @@ setclass(class, str) int mid; str++; - mid = macid(str, NULL); + mid = macid(str); if (mid == 0) return; if (tTd(37, 8)) - dprintf("setclass(%s, $=%s)\n", - macname(class), macname(mid)); + sm_dprintf("setclass(%s, $=%s)\n", + macname(class), macname(mid)); copy_class(mid, class); } else { if (tTd(37, 8)) - dprintf("setclass(%s, %s)\n", macname(class), str); + sm_dprintf("setclass(%s, %s)\n", macname(class), str); s = stab(str, ST_CLASS, ST_ENTER); setbitn(bitidx(class), s->s_class); @@ -3133,7 +3584,8 @@ makemapentry(line) class = stab(classname, ST_MAPCLASS, ST_FIND); if (class == NULL) { - syserr("readcf: map %s: class %s not available", mapname, classname); + syserr("readcf: map %s: class %s not available", mapname, + classname); return NULL; } @@ -3147,16 +3599,13 @@ makemapentry(line) if (tTd(37, 5)) { - dprintf("map %s, class %s, flags %lx, file %s,\n", - s->s_map.map_mname, s->s_map.map_class->map_cname, - s->s_map.map_mflags, - s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); - dprintf("\tapp %s, domain %s, rebuild %s\n", - s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, - s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, - s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); + sm_dprintf("map %s, class %s, flags %lx, file %s,\n", + s->s_map.map_mname, s->s_map.map_class->map_cname, + s->s_map.map_mflags, s->s_map.map_file); + sm_dprintf("\tapp %s, domain %s, rebuild %s\n", + s->s_map.map_app, s->s_map.map_domain, + s->s_map.map_rebuild); } - return &s->s_map; } /* @@ -3277,7 +3726,7 @@ strtorwset(p, endp, stabmode) char *h = NULL; if (RuleSetNames[ruleset] != NULL) - sm_free(RuleSetNames[ruleset]); + sm_free(RuleSetNames[ruleset]); /* XXX */ if (delim != '\0' && (h = strchr(q, delim)) != NULL) *h = '\0'; RuleSetNames[ruleset] = newstr(q); @@ -3305,8 +3754,8 @@ static BITMAP256 StickyTimeoutOpt; static struct timeoutinfo { - char *to_name; /* long name of timeout */ - u_char to_code; /* code for option */ + char *to_name; /* long name of timeout */ + unsigned char to_code; /* code for option */ } TimeOutTab[] = { #define TO_INITIAL 0x01 @@ -3373,6 +3822,14 @@ static struct timeoutinfo { "resolver.retry.first", TO_RESOLVER_RETRY_FIRST }, #define TO_CONTROL 0x1F { "control", TO_CONTROL }, +#define TO_LHLO 0x20 + { "lhlo", TO_LHLO }, +#define TO_AUTH 0x21 + { "auth", TO_AUTH }, +#define TO_STARTTLS 0x22 + { "starttls", TO_STARTTLS }, +#define TO_ACONNECT 0x23 + { "aconnect", TO_ACONNECT }, { NULL, 0 }, }; @@ -3384,16 +3841,15 @@ settimeout(name, val, sticky) bool sticky; { register struct timeoutinfo *to; - int i; - int addopts; + int i, addopts; time_t toval; if (tTd(37, 2)) - dprintf("settimeout(%s = %s)", name, val); + sm_dprintf("settimeout(%s = %s)", name, val); for (to = TimeOutTab; to->to_name != NULL; to++) { - if (strcasecmp(to->to_name, name) == 0) + if (sm_strcasecmp(to->to_name, name) == 0) break; } @@ -3411,12 +3867,12 @@ settimeout(name, val, sticky) if (!sticky && bitnset(to->to_code, StickyTimeoutOpt)) { if (tTd(37, 2)) - dprintf(" (ignored)\n"); + sm_dprintf(" (ignored)\n"); return; } if (tTd(37, 2)) - dprintf("\n"); + sm_dprintf("\n"); toval = convtime(val, 'm'); addopts = 0; @@ -3483,6 +3939,10 @@ settimeout(name, val, sticky) TimeOuts.to_iconnect = toval; break; + case TO_ACONNECT: + TimeOuts.to_aconnect = toval; + break; + case TO_QUEUEWARN: toval = convtime(val, 'h'); TimeOuts.to_q_warning[TOC_NORMAL] = toval; @@ -3570,6 +4030,22 @@ settimeout(name, val, sticky) TimeOuts.to_control = toval; break; + case TO_LHLO: + TimeOuts.to_lhlo = toval; + break; + +#if SASL + case TO_AUTH: + TimeOuts.to_auth = toval; + break; +#endif /* SASL */ + +#if STARTTLS + case TO_STARTTLS: + TimeOuts.to_starttls = toval; + break; +#endif /* STARTTLS */ + default: syserr("settimeout: invalid timeout %s", name); break; @@ -3605,10 +4081,11 @@ inittimeouts(val, sticky) register char *p; if (tTd(37, 2)) - dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val); + sm_dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val); if (val == NULL) { TimeOuts.to_connect = (time_t) 0 SECONDS; + TimeOuts.to_aconnect = (time_t) 0 SECONDS; TimeOuts.to_initial = (time_t) 5 MINUTES; TimeOuts.to_helo = (time_t) 5 MINUTES; TimeOuts.to_mail = (time_t) 10 MINUTES; @@ -3627,24 +4104,44 @@ inittimeouts(val, sticky) #endif /* IDENTPROTO */ TimeOuts.to_fileopen = (time_t) 60 SECONDS; TimeOuts.to_control = (time_t) 2 MINUTES; + TimeOuts.to_lhlo = (time_t) 2 MINUTES; +#if SASL + TimeOuts.to_auth = (time_t) 10 MINUTES; +#endif /* SASL */ +#if STARTTLS + TimeOuts.to_starttls = (time_t) 1 HOUR; +#endif /* STARTTLS */ if (tTd(37, 5)) { - dprintf("Timeouts:\n"); - dprintf(" connect = %ld\n", (long)TimeOuts.to_connect); - dprintf(" initial = %ld\n", (long)TimeOuts.to_initial); - dprintf(" helo = %ld\n", (long)TimeOuts.to_helo); - dprintf(" mail = %ld\n", (long)TimeOuts.to_mail); - dprintf(" rcpt = %ld\n", (long)TimeOuts.to_rcpt); - dprintf(" datainit = %ld\n", (long)TimeOuts.to_datainit); - dprintf(" datablock = %ld\n", (long)TimeOuts.to_datablock); - dprintf(" datafinal = %ld\n", (long)TimeOuts.to_datafinal); - dprintf(" rset = %ld\n", (long)TimeOuts.to_rset); - dprintf(" quit = %ld\n", (long)TimeOuts.to_quit); - dprintf(" nextcommand = %ld\n", (long)TimeOuts.to_nextcommand); - dprintf(" miscshort = %ld\n", (long)TimeOuts.to_miscshort); - dprintf(" ident = %ld\n", (long)TimeOuts.to_ident); - dprintf(" fileopen = %ld\n", (long)TimeOuts.to_fileopen); - dprintf(" control = %ld\n", (long)TimeOuts.to_control); + sm_dprintf("Timeouts:\n"); + sm_dprintf(" connect = %ld\n", + (long) TimeOuts.to_connect); + sm_dprintf(" aconnect = %ld\n", + (long) TimeOuts.to_aconnect); + sm_dprintf(" initial = %ld\n", + (long) TimeOuts.to_initial); + sm_dprintf(" helo = %ld\n", (long) TimeOuts.to_helo); + sm_dprintf(" mail = %ld\n", (long) TimeOuts.to_mail); + sm_dprintf(" rcpt = %ld\n", (long) TimeOuts.to_rcpt); + sm_dprintf(" datainit = %ld\n", + (long) TimeOuts.to_datainit); + sm_dprintf(" datablock = %ld\n", + (long) TimeOuts.to_datablock); + sm_dprintf(" datafinal = %ld\n", + (long) TimeOuts.to_datafinal); + sm_dprintf(" rset = %ld\n", (long) TimeOuts.to_rset); + sm_dprintf(" quit = %ld\n", (long) TimeOuts.to_quit); + sm_dprintf(" nextcommand = %ld\n", + (long) TimeOuts.to_nextcommand); + sm_dprintf(" miscshort = %ld\n", + (long) TimeOuts.to_miscshort); + sm_dprintf(" ident = %ld\n", (long) TimeOuts.to_ident); + sm_dprintf(" fileopen = %ld\n", + (long) TimeOuts.to_fileopen); + sm_dprintf(" lhlo = %ld\n", + (long) TimeOuts.to_lhlo); + sm_dprintf(" control = %ld\n", + (long) TimeOuts.to_control); } return; } diff --git a/gnu/usr.sbin/sendmail/sendmail/recipient.c b/gnu/usr.sbin/sendmail/sendmail/recipient.c index e2dbfd39fa7..4b1fb0b1a51 100644 --- a/gnu/usr.sbin/sendmail/sendmail/recipient.c +++ b/gnu/usr.sbin/sendmail/sendmail/recipient.c @@ -11,15 +11,125 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: recipient.c,v 8.231.14.11 2001/05/03 17:24:14 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +SM_RCSID("@(#)$Sendmail: recipient.c,v 8.322 2001/09/04 22:43:05 ca Exp $") static void includetimeout __P((void)); static ADDRESS *self_reference __P((ADDRESS *)); +static int sortexpensive __P((ADDRESS *, ADDRESS *)); +static int sortbysignature __P((ADDRESS *, ADDRESS *)); +static int sorthost __P((ADDRESS *, ADDRESS *)); + +typedef int sortfn_t __P((ADDRESS *, ADDRESS *)); + +/* +** SORTHOST -- strcmp()-like func for host portion of an ADDRESS +** +** Parameters: +** xx -- first ADDRESS +** yy -- second ADDRESS +** +** Returns: +** <0 when xx->q_host is less than yy->q_host +** >0 when xx->q_host is greater than yy->q_host +** 0 when equal +*/ + +static int +sorthost(xx, yy) + register ADDRESS *xx; + register ADDRESS *yy; +{ +#if _FFR_HOST_SORT_REVERSE + /* XXX maybe compare hostnames from the end? */ + return sm_strrevcasecmp(xx->q_host, yy->q_host); +#else /* _FFR_HOST_SORT_REVERSE */ + return sm_strcasecmp(xx->q_host, yy->q_host); +#endif /* _FFR_HOST_SORT_REVERSE */ +} + +/* +** SORTEXPENSIVE -- strcmp()-like func for expensive mailers +** +** The mailer has been noted already as "expensive" for 'xx'. This +** will give a result relative to 'yy'. Expensive mailers get rated +** "greater than" non-expensive mailers because during the delivery phase +** it will get queued -- no use it getting in the way of less expensive +** recipients. We avoid an MX RR lookup when both 'xx' and 'yy' are +** expensive since an MX RR lookup happens when extracted from the queue +** later. +** +** Parameters: +** xx -- first ADDRESS +** yy -- second ADDRESS +** +** Returns: +** <0 when xx->q_host is less than yy->q_host and both are +** expensive +** >0 when xx->q_host is greater than yy->q_host, or when +** 'yy' is non-expensive +** 0 when equal (by expense and q_host) +*/ + +static int +sortexpensive(xx, yy) + ADDRESS *xx; + ADDRESS *yy; +{ + if (!bitnset(M_EXPENSIVE, yy->q_mailer->m_flags)) + return 1; /* xx should go later */ +#if _FFR_HOST_SORT_REVERSE + /* XXX maybe compare hostnames from the end? */ + return sm_strrevcasecmp(xx->q_host, yy->q_host); +#else /* _FFR_HOST_SORT_REVERSE */ + return sm_strcasecmp(xx->q_host, yy->q_host); +#endif /* _FFR_HOST_SORT_REVERSE */ +} + +/* +** SORTBYSIGNATURE -- a strcmp()-like func for q_mailer and q_host in ADDRESS +** +** Parameters: +** xx -- first ADDRESS +** yy -- second ADDRESS +** +** Returns: +** 0 when the "signature"'s are same +** <0 when xx->q_signature is less than yy->q_signature +** >0 when xx->q_signature is greater than yy->q_signature +** +** Side Effect: +** May set ADDRESS pointer for q_signature if not already set. +*/ + +static int +sortbysignature(xx, yy) + ADDRESS *xx; + ADDRESS *yy; +{ + register int ret; + + /* Let's avoid redoing the signature over and over again */ + if (xx->q_signature == NULL) + xx->q_signature = hostsignature(xx->q_mailer, xx->q_host); + if (yy->q_signature == NULL) + yy->q_signature = hostsignature(yy->q_mailer, yy->q_host); + ret = strcmp(xx->q_signature, yy->q_signature); + + /* + ** If the two signatures are the same then we will return a sort + ** value based on 'q_user'. But note that we have reversed xx and yy + ** on purpose. This additional compare helps reduce the number of + ** sameaddr() calls and loops in recipient() for the case when + ** the rcpt list has been provided already in-order. + */ + + if (ret == 0) + return strcmp(yy->q_user, xx->q_user); + else + return ret; +} /* ** SENDTOLIST -- Designate a send list. @@ -58,12 +168,12 @@ sendtolist(list, ctladdr, sendq, aliaslevel, e) register ENVELOPE *e; { register char *p; - register ADDRESS *al; /* list of addresses to send to */ - char delimiter; /* the address delimiter */ - int naddrs; - int i; + register ADDRESS *SM_NONVOLATILE al; /* list of addresses to send to */ + SM_NONVOLATILE char delimiter; /* the address delimiter */ + SM_NONVOLATILE int naddrs; + SM_NONVOLATILE int i; char *oldto = e->e_to; - char *bufp; + char *SM_NONVOLATILE bufp; char buf[MAXNAME + 1]; if (list == NULL) @@ -74,8 +184,8 @@ sendtolist(list, ctladdr, sendq, aliaslevel, e) if (tTd(25, 1)) { - dprintf("sendto: %s\n ctladdr=", list); - printaddr(ctladdr, FALSE); + sm_dprintf("sendto: %s\n ctladdr=", list); + printaddr(ctladdr, false); } /* heuristic to determine old versus new style addresses */ @@ -98,105 +208,111 @@ sendtolist(list, ctladdr, sendq, aliaslevel, e) i = sizeof buf; } else - bufp = xalloc(i); - (void) strlcpy(bufp, denlstring(list, FALSE, TRUE), i); - -#if _FFR_ADDR_TYPE - define(macid("{addr_type}", NULL), "e r", e); -#endif /* _FFR_ADDR_TYPE */ - for (p = bufp; *p != '\0'; ) - { - auto char *delimptr; - register ADDRESS *a; - - /* parse the address */ - while ((isascii(*p) && isspace(*p)) || *p == ',') - p++; - a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter, &delimptr, e); - p = delimptr; - if (a == NULL) - continue; - a->q_next = al; - a->q_alias = ctladdr; + bufp = sm_malloc_x(i); + + SM_TRY + { + (void) sm_strlcpy(bufp, denlstring(list, false, true), i); - /* arrange to inherit attributes from parent */ - if (ctladdr != NULL) + macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r"); + for (p = bufp; *p != '\0'; ) { - ADDRESS *b; + auto char *delimptr; + register ADDRESS *a; + + /* parse the address */ + while ((isascii(*p) && isspace(*p)) || *p == ',') + p++; + a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter, + &delimptr, e, true); + p = delimptr; + if (a == NULL) + continue; + a->q_next = al; + a->q_alias = ctladdr; - /* self reference test */ - if (sameaddr(ctladdr, a)) + /* arrange to inherit attributes from parent */ + if (ctladdr != NULL) { - if (tTd(27, 5)) - { - dprintf("sendtolist: QSELFREF "); - printaddr(ctladdr, FALSE); - } - ctladdr->q_flags |= QSELFREF; - } + ADDRESS *b; - /* check for address loops */ - b = self_reference(a); - if (b != NULL) - { - b->q_flags |= QSELFREF; - if (tTd(27, 5)) + /* self reference test */ + if (sameaddr(ctladdr, a)) { - dprintf("sendtolist: QSELFREF "); - printaddr(b, FALSE); + if (tTd(27, 5)) + { + sm_dprintf("sendtolist: QSELFREF "); + printaddr(ctladdr, false); + } + ctladdr->q_flags |= QSELFREF; } - if (a != b) + + /* check for address loops */ + b = self_reference(a); + if (b != NULL) { + b->q_flags |= QSELFREF; if (tTd(27, 5)) { - dprintf("sendtolist: QS_DONTSEND "); - printaddr(a, FALSE); + sm_dprintf("sendtolist: QSELFREF "); + printaddr(b, false); + } + if (a != b) + { + if (tTd(27, 5)) + { + sm_dprintf("sendtolist: QS_DONTSEND "); + printaddr(a, false); + } + a->q_state = QS_DONTSEND; + b->q_flags |= a->q_flags & QNOTREMOTE; + continue; } - a->q_state = QS_DONTSEND; - b->q_flags |= a->q_flags & QNOTREMOTE; - continue; } - } - /* full name */ - if (a->q_fullname == NULL) - a->q_fullname = ctladdr->q_fullname; + /* full name */ + if (a->q_fullname == NULL) + a->q_fullname = ctladdr->q_fullname; + + /* various flag bits */ + a->q_flags &= ~QINHERITEDBITS; + a->q_flags |= ctladdr->q_flags & QINHERITEDBITS; - /* various flag bits */ - a->q_flags &= ~QINHERITEDBITS; - a->q_flags |= ctladdr->q_flags & QINHERITEDBITS; + /* DSN recipient information */ + a->q_finalrcpt = ctladdr->q_finalrcpt; + a->q_orcpt = ctladdr->q_orcpt; + } - /* original recipient information */ - a->q_orcpt = ctladdr->q_orcpt; + al = a; } - al = a; - } + /* arrange to send to everyone on the local send list */ + while (al != NULL) + { + register ADDRESS *a = al; - /* arrange to send to everyone on the local send list */ - while (al != NULL) + al = a->q_next; + a = recipient(a, sendq, aliaslevel, e); + naddrs++; + } + } + SM_FINALLY { - register ADDRESS *a = al; - - al = a->q_next; - a = recipient(a, sendq, aliaslevel, e); - naddrs++; + e->e_to = oldto; + if (bufp != buf) + sm_free(bufp); + macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), NULL); } - - e->e_to = oldto; - if (bufp != buf) - sm_free(bufp); -#if _FFR_ADDR_TYPE - define(macid("{addr_type}", NULL), NULL, e); -#endif /* _FFR_ADDR_TYPE */ + SM_END_TRY return naddrs; } +#if MILTER /* ** REMOVEFROMLIST -- Remove addresses from a send list. ** ** The parameter is a comma-separated list of recipients to remove. ** Note that it only deletes matching addresses. If those addresses -** have been expended already in the sendq, it won't mark the +** have been expanded already in the sendq, it won't mark the ** expanded recipients as QS_REMOVED. ** ** Parameters: @@ -216,12 +332,12 @@ removefromlist(list, sendq, e) ADDRESS **sendq; ENVELOPE *e; { - char delimiter; /* the address delimiter */ - int naddrs; - int i; + SM_NONVOLATILE char delimiter; /* the address delimiter */ + SM_NONVOLATILE int naddrs; + SM_NONVOLATILE int i; char *p; char *oldto = e->e_to; - char *bufp; + char *SM_NONVOLATILE bufp; char buf[MAXNAME + 1]; if (list == NULL) @@ -231,7 +347,7 @@ removefromlist(list, sendq, e) } if (tTd(25, 1)) - dprintf("removefromlist: %s\n", list); + sm_dprintf("removefromlist: %s\n", list); /* heuristic to determine old versus new style addresses */ if (strchr(list, ',') != NULL || strchr(list, ';') != NULL || @@ -251,61 +367,65 @@ removefromlist(list, sendq, e) i = sizeof buf; } else - bufp = xalloc(i); - (void) strlcpy(bufp, denlstring(list, FALSE, TRUE), i); - -#if _FFR_ADDR_TYPE - define(macid("{addr_type}", NULL), "e r", e); -#endif /* _FFR_ADDR_TYPE */ - for (p = bufp; *p != '\0'; ) - { - ADDRESS a; /* parsed address to be removed */ - ADDRESS *q; - ADDRESS **pq; - char *delimptr; - - /* parse the address */ - while ((isascii(*p) && isspace(*p)) || *p == ',') - p++; - if (parseaddr(p, &a, RF_COPYALL, - delimiter, &delimptr, e) == NULL) + bufp = sm_malloc_x(i); + + SM_TRY + { + (void) sm_strlcpy(bufp, denlstring(list, false, true), i); + + macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r"); + for (p = bufp; *p != '\0'; ) { + ADDRESS a; /* parsed address to be removed */ + ADDRESS *q; + ADDRESS **pq; + char *delimptr; + + /* parse the address */ + while ((isascii(*p) && isspace(*p)) || *p == ',') + p++; + if (parseaddr(p, &a, RF_COPYALL, + delimiter, &delimptr, e, true) == NULL) + { + p = delimptr; + continue; + } p = delimptr; - continue; - } - p = delimptr; - for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next) - { - if (!QS_IS_DEAD(q->q_state) && - sameaddr(q, &a)) + for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next) { - if (tTd(25, 5)) + if (!QS_IS_DEAD(q->q_state) && + sameaddr(q, &a)) { - dprintf("removefromlist: QS_REMOVED "); - printaddr(&a, FALSE); + if (tTd(25, 5)) + { + sm_dprintf("removefromlist: QS_REMOVED "); + printaddr(&a, false); + } + q->q_state = QS_REMOVED; + naddrs++; + break; } - q->q_state = QS_REMOVED; - naddrs++; - break; } } } - - e->e_to = oldto; - if (bufp != buf) - sm_free(bufp); -#if _FFR_ADDR_TYPE - define(macid("{addr_type}", NULL), NULL, e); -#endif /* _FFR_ADDR_TYPE */ + SM_FINALLY + { + e->e_to = oldto; + if (bufp != buf) + sm_free(bufp); + macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), NULL); + } + SM_END_TRY return naddrs; } +#endif /* MILTER */ /* ** RECIPIENT -- Designate a message recipient ** ** Saves the named person for future mailing. ** ** Parameters: -** a -- the (preparsed) address header for the recipient. +** new -- the (preparsed) address header for the recipient. ** sendq -- a pointer to the head of a queue to put the ** recipient in. Duplicate suppression is done ** in this queue. @@ -316,62 +436,121 @@ removefromlist(list, sendq, e) ** The actual address in the queue. This will be "a" if ** the address is not a duplicate, else the original address. ** -** Side Effects: -** none. */ ADDRESS * -recipient(a, sendq, aliaslevel, e) - register ADDRESS *a; +recipient(new, sendq, aliaslevel, e) + register ADDRESS *new; register ADDRESS **sendq; int aliaslevel; register ENVELOPE *e; { register ADDRESS *q; ADDRESS **pq; + ADDRESS **prev; register struct mailer *m; - register char *p = NULL; - bool quoted = FALSE; /* set if the addr has a quote bit */ - int findusercount = 0; - bool initialdontsend = QS_IS_DEAD(a->q_state); + register char *p; int i, buflen; + bool quoted; /* set if the addr has a quote bit */ + bool insert; + int findusercount; + bool initialdontsend; char *buf; char buf0[MAXNAME + 1]; /* unquoted image of the user name */ - - e->e_to = a->q_paddr; - m = a->q_mailer; + sortfn_t *sortfn; + + p = NULL; + quoted = false; + insert = false; + findusercount = 0; + initialdontsend = QS_IS_DEAD(new->q_state); + e->e_to = new->q_paddr; + m = new->q_mailer; errno = 0; if (aliaslevel == 0) - a->q_flags |= QPRIMARY; + new->q_flags |= QPRIMARY; if (tTd(26, 1)) { - dprintf("\nrecipient (%d): ", aliaslevel); - printaddr(a, FALSE); + sm_dprintf("\nrecipient (%d): ", aliaslevel); + printaddr(new, false); } - /* if this is primary, add it to the original recipient list */ - if (a->q_alias == NULL) + /* if this is primary, use it as original recipient */ + if (new->q_alias == NULL) { if (e->e_origrcpt == NULL) - e->e_origrcpt = a->q_paddr; - else if (e->e_origrcpt != a->q_paddr) + e->e_origrcpt = new->q_paddr; + else if (e->e_origrcpt != new->q_paddr) e->e_origrcpt = ""; } + /* find parent recipient for finalrcpt and orcpt */ + for (q = new; q->q_alias != NULL; q = q->q_alias) + continue; + + /* find final recipient DSN address */ + if (new->q_finalrcpt == NULL && + e->e_from.q_mailer != NULL) + { + char frbuf[MAXLINE]; + + p = e->e_from.q_mailer->m_addrtype; + if (p == NULL) + p = "rfc822"; + if (sm_strcasecmp(p, "rfc822") != 0) + { + (void) sm_snprintf(frbuf, sizeof frbuf, "%s; %.800s", + q->q_mailer->m_addrtype, + q->q_user); + } + else if (strchr(q->q_user, '@') != NULL) + { + (void) sm_snprintf(frbuf, sizeof frbuf, "%s; %.800s", + p, q->q_user); + } + else if (strchr(q->q_paddr, '@') != NULL) + { + char *qp; + bool b; + + qp = q->q_paddr; + + /* strip brackets from address */ + b = false; + if (*qp == '<') + { + b = qp[strlen(qp) - 1] == '>'; + if (b) + qp[strlen(qp) - 1] = '\0'; + qp++; + } + (void) sm_snprintf(frbuf, sizeof frbuf, "%s; %.800s", + p, qp); + + /* undo damage */ + if (b) + qp[strlen(qp)] = '>'; + } + else + { + (void) sm_snprintf(frbuf, sizeof frbuf, + "%s; %.700s@%.100s", + p, q->q_user, MyHostName); + } + new->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool, frbuf); + } + #if _FFR_GEN_ORCPT /* set ORCPT DSN arg if not already set */ - if (a->q_orcpt == NULL) + if (new->q_orcpt == NULL) { - for (q = a; q->q_alias != NULL; q = q->q_alias) - continue; - /* check for an existing ORCPT */ if (q->q_orcpt != NULL) - a->q_orcpt = q->q_orcpt; + new->q_orcpt = q->q_orcpt; else { /* make our own */ - bool b = FALSE; + bool b = false; char *qp; char obuf[MAXLINE]; @@ -379,8 +558,7 @@ recipient(a, sendq, aliaslevel, e) p = e->e_from.q_mailer->m_addrtype; if (p == NULL) p = "rfc822"; - (void) strlcpy(obuf, p, sizeof obuf); - (void) strlcat(obuf, ";", sizeof obuf); + (void) sm_strlcpyn(obuf, sizeof obuf, 2, p, ";"); qp = q->q_paddr; @@ -395,9 +573,9 @@ recipient(a, sendq, aliaslevel, e) qp++; } - p = xtextify(denlstring(qp, TRUE, FALSE), NULL); + p = xtextify(denlstring(qp, true, false), NULL); - if (strlcat(obuf, p, sizeof obuf) >= sizeof obuf) + if (sm_strlcat(obuf, p, sizeof obuf) >= sizeof obuf) { /* if too big, don't use it */ obuf[0] = '\0'; @@ -408,7 +586,8 @@ recipient(a, sendq, aliaslevel, e) qp[strlen(qp)] = '>'; if (obuf[0] != '\0') - a->q_orcpt = newstr(obuf); + new->q_orcpt = + sm_rpool_strdup_x(e->e_rpool, obuf); } } #endif /* _FFR_GEN_ORCPT */ @@ -416,12 +595,12 @@ recipient(a, sendq, aliaslevel, e) /* break aliasing loops */ if (aliaslevel > MaxAliasRecursion) { - a->q_state = QS_BADADDR; - a->q_status = "5.4.6"; - usrerrenh(a->q_status, + new->q_state = QS_BADADDR; + new->q_status = "5.4.6"; + usrerrenh(new->q_status, "554 aliasing/forwarding loop broken (%d aliases deep; %d max)", aliaslevel, MaxAliasRecursion); - return a; + return new; } /* @@ -429,7 +608,7 @@ recipient(a, sendq, aliaslevel, e) */ /* get unquoted user for file, program or user.name check */ - i = strlen(a->q_user); + i = strlen(new->q_user); if (i >= sizeof buf0) { buflen = i + 1; @@ -440,45 +619,46 @@ recipient(a, sendq, aliaslevel, e) buf = buf0; buflen = sizeof buf0; } - (void) strlcpy(buf, a->q_user, buflen); + (void) sm_strlcpy(buf, new->q_user, buflen); for (p = buf; *p != '\0' && !quoted; p++) { if (*p == '\\') - quoted = TRUE; + quoted = true; } stripquotes(buf); /* check for direct mailing to restricted mailers */ if (m == ProgMailer) { - if (a->q_alias == NULL) + if (new->q_alias == NULL || UseMSP || + bitset(EF_UNSAFE, e->e_flags)) { - a->q_state = QS_BADADDR; - a->q_status = "5.7.1"; - usrerrenh(a->q_status, + new->q_state = QS_BADADDR; + new->q_status = "5.7.1"; + usrerrenh(new->q_status, "550 Cannot mail directly to programs"); } - else if (bitset(QBOGUSSHELL, a->q_alias->q_flags)) + else if (bitset(QBOGUSSHELL, new->q_alias->q_flags)) { - a->q_state = QS_BADADDR; - a->q_status = "5.7.1"; - if (a->q_alias->q_ruser == NULL) - usrerrenh(a->q_status, + new->q_state = QS_BADADDR; + new->q_status = "5.7.1"; + if (new->q_alias->q_ruser == NULL) + usrerrenh(new->q_status, "550 UID %d is an unknown user: cannot mail to programs", - a->q_alias->q_uid); + new->q_alias->q_uid); else - usrerrenh(a->q_status, + usrerrenh(new->q_status, "550 User %s@%s doesn't have a valid shell for mailing to programs", - a->q_alias->q_ruser, MyHostName); + new->q_alias->q_ruser, MyHostName); } - else if (bitset(QUNSAFEADDR, a->q_alias->q_flags)) + else if (bitset(QUNSAFEADDR, new->q_alias->q_flags)) { - a->q_state = QS_BADADDR; - a->q_status = "5.7.1"; - a->q_rstatus = newstr("550 Unsafe for mailing to programs"); - usrerrenh(a->q_status, + new->q_state = QS_BADADDR; + new->q_status = "5.7.1"; + new->q_rstatus = "550 Unsafe for mailing to programs"; + usrerrenh(new->q_status, "550 Address %s is unsafe for mailing to programs", - a->q_alias->q_paddr); + new->q_alias->q_paddr); } } @@ -491,50 +671,118 @@ recipient(a, sendq, aliaslevel, e) ** [Please note: the emphasis is on "hack."] */ + prev = NULL; + + /* + ** If this message is going to the queue or FastSplit is set + ** and it is the first try and the envelope hasn't split, then we + ** avoid doing an MX RR lookup now because one will be done when the + ** message is extracted from the queue later. It can go to the queue + ** because all messages are going to the queue or this mailer of + ** the current recipient is marked expensive. + */ + + if (WILL_BE_QUEUED(e->e_sendmode) || + (!bitset(EF_SPLIT, e->e_flags) && e->e_ntries == 0 && + FastSplit > 0)) + sortfn = sorthost; + else if (NoConnect && bitnset(M_EXPENSIVE, new->q_mailer->m_flags)) + sortfn = sortexpensive; + else + sortfn = sortbysignature; + for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next) { - if (sameaddr(q, a) && - (bitset(QRCPTOK, q->q_flags) || - !bitset(QPRIMARY, q->q_flags))) + /* + ** If address is "less than" it should be inserted now. + ** If address is "greater than" current comparison it'll + ** insert later in the list; so loop again (if possible). + ** If address is "equal" (different equal than sameaddr() + ** call) then check if sameaddr() will be true. + ** Because this list is now sorted, it'll mean fewer + ** comparisons and fewer loops which is important for more + ** recipients. + */ + + i = (*sortfn)(new, q); + if (i == 0) /* equal */ { - if (tTd(26, 1)) - { - dprintf("%s in sendq: ", a->q_paddr); - printaddr(q, FALSE); - } - if (!bitset(QPRIMARY, q->q_flags)) - { - if (!QS_IS_DEAD(a->q_state)) - message("duplicate suppressed"); - else - q->q_state = QS_DUPLICATE; - q->q_flags |= a->q_flags; - } - else if (bitset(QSELFREF, q->q_flags) -#if _FFR_MILTER - || q->q_state == QS_REMOVED -#endif /* _FFR_MILTER */ - ) + /* + ** Sortbysignature() has said that the two have + ** equal MX RR's and the same user. Calling sameaddr() + ** now checks if the two hosts are as identical as the + ** MX RR's are (which might not be the case) + ** before saying these are the identical addresses. + */ + + if (sameaddr(q, new) && + (bitset(QRCPTOK, q->q_flags) || + !bitset(QPRIMARY, q->q_flags))) { -#if _FFR_MILTER - /* - ** If an earlier milter removed the address, - ** a later one can still add it back. - */ -#endif /* _FFR_MILTER */ - q->q_state = a->q_state; - q->q_flags |= a->q_flags; + if (tTd(26, 1)) + { + sm_dprintf("%s in sendq: ", + new->q_paddr); + printaddr(q, false); + } + if (!bitset(QPRIMARY, q->q_flags)) + { + if (!QS_IS_DEAD(new->q_state)) + message("duplicate suppressed"); + else + q->q_state = QS_DUPLICATE; + q->q_flags |= new->q_flags; + } + else if (bitset(QSELFREF, q->q_flags) + || q->q_state == QS_REMOVED) + { + /* + ** If an earlier milter removed the + ** address, a later one can still add + ** it back. + */ + + q->q_state = new->q_state; + q->q_flags |= new->q_flags; + } + new = q; + goto done; } - a = q; - goto done; } + else if (i < 0) /* less than */ + { + insert = true; + break; + } + prev = pq; } + /* pq should point to an address, never NULL */ + SM_ASSERT(pq != NULL); + /* add address on list */ - if (pq != NULL) + if (insert) + { + /* + ** insert before 'pq'. Only possible when at least 1 + ** ADDRESS is in the list already. + */ + + new->q_next = *pq; + if (prev == NULL) + *sendq = new; /* To be the first ADDRESS */ + else + (*prev)->q_next = new; + } + else { - *pq = a; - a->q_next = NULL; + /* + ** Place in list at current 'pq' position. Possible + ** when there are 0 or more ADDRESS's in the list. + */ + + new->q_next = NULL; + *pq = new; } /* @@ -544,108 +792,120 @@ recipient(a, sendq, aliaslevel, e) trylocaluser: if (tTd(29, 7)) { - dprintf("at trylocaluser: "); - printaddr(a, FALSE); + sm_dprintf("at trylocaluser: "); + printaddr(new, false); } - if (!QS_IS_OK(a->q_state)) + if (!QS_IS_OK(new->q_state)) + { + if (QS_IS_UNDELIVERED(new->q_state)) + e->e_nrcpts++; goto testselfdestruct; + } if (m == InclMailer) { - a->q_state = QS_INCLUDED; - if (a->q_alias == NULL) + new->q_state = QS_INCLUDED; + if (new->q_alias == NULL || UseMSP || + bitset(EF_UNSAFE, e->e_flags)) { - a->q_state = QS_BADADDR; - a->q_status = "5.7.1"; - usrerrenh(a->q_status, + new->q_state = QS_BADADDR; + new->q_status = "5.7.1"; + usrerrenh(new->q_status, "550 Cannot mail directly to :include:s"); } else { int ret; - message("including file %s", a->q_user); - ret = include(a->q_user, FALSE, a, sendq, aliaslevel, e); + message("including file %s", new->q_user); + ret = include(new->q_user, false, new, + sendq, aliaslevel, e); if (transienterror(ret)) { if (LogLevel > 2) sm_syslog(LOG_ERR, e->e_id, "include %s: transient error: %s", - shortenstring(a->q_user, MAXSHORTSTR), - errstring(ret)); - a->q_state = QS_QUEUEUP; + shortenstring(new->q_user, + MAXSHORTSTR), + sm_errstring(ret)); + new->q_state = QS_QUEUEUP; usrerr("451 4.2.4 Cannot open %s: %s", - shortenstring(a->q_user, MAXSHORTSTR), - errstring(ret)); + shortenstring(new->q_user, + MAXSHORTSTR), + sm_errstring(ret)); } else if (ret != 0) { - a->q_state = QS_BADADDR; - a->q_status = "5.2.4"; - usrerrenh(a->q_status, + new->q_state = QS_BADADDR; + new->q_status = "5.2.4"; + usrerrenh(new->q_status, "550 Cannot open %s: %s", - shortenstring(a->q_user, MAXSHORTSTR), - errstring(ret)); + shortenstring(new->q_user, + MAXSHORTSTR), + sm_errstring(ret)); } } } else if (m == FileMailer) { - /* check if writable or creatable */ - if (a->q_alias == NULL) + /* check if allowed */ + if (new->q_alias == NULL || UseMSP || + bitset(EF_UNSAFE, e->e_flags)) { - a->q_state = QS_BADADDR; - a->q_status = "5.7.1"; - usrerrenh(a->q_status, + new->q_state = QS_BADADDR; + new->q_status = "5.7.1"; + usrerrenh(new->q_status, "550 Cannot mail directly to files"); } - else if (bitset(QBOGUSSHELL, a->q_alias->q_flags)) + else if (bitset(QBOGUSSHELL, new->q_alias->q_flags)) { - a->q_state = QS_BADADDR; - a->q_status = "5.7.1"; - if (a->q_alias->q_ruser == NULL) - usrerrenh(a->q_status, + new->q_state = QS_BADADDR; + new->q_status = "5.7.1"; + if (new->q_alias->q_ruser == NULL) + usrerrenh(new->q_status, "550 UID %d is an unknown user: cannot mail to files", - a->q_alias->q_uid); + new->q_alias->q_uid); else - usrerrenh(a->q_status, + usrerrenh(new->q_status, "550 User %s@%s doesn't have a valid shell for mailing to files", - a->q_alias->q_ruser, MyHostName); + new->q_alias->q_ruser, MyHostName); } - else if (bitset(QUNSAFEADDR, a->q_alias->q_flags)) + else if (bitset(QUNSAFEADDR, new->q_alias->q_flags)) { - a->q_state = QS_BADADDR; - a->q_status = "5.7.1"; - a->q_rstatus = newstr("550 Unsafe for mailing to files"); - usrerrenh(a->q_status, + new->q_state = QS_BADADDR; + new->q_status = "5.7.1"; + new->q_rstatus = "550 Unsafe for mailing to files"; + usrerrenh(new->q_status, "550 Address %s is unsafe for mailing to files", - a->q_alias->q_paddr); + new->q_alias->q_paddr); } } /* try aliasing */ - if (!quoted && QS_IS_OK(a->q_state) && + if (!quoted && QS_IS_OK(new->q_state) && bitnset(M_ALIASABLE, m->m_flags)) - alias(a, sendq, aliaslevel, e); + alias(new, sendq, aliaslevel, e); #if USERDB /* if not aliased, look it up in the user database */ - if (!bitset(QNOTREMOTE, a->q_flags) && - QS_IS_SENDABLE(a->q_state) && + if (!bitset(QNOTREMOTE, new->q_flags) && + QS_IS_SENDABLE(new->q_state) && bitnset(M_CHECKUDB, m->m_flags)) { - if (udbexpand(a, sendq, aliaslevel, e) == EX_TEMPFAIL) + if (udbexpand(new, sendq, aliaslevel, e) == EX_TEMPFAIL) { - a->q_state = QS_QUEUEUP; + new->q_state = QS_QUEUEUP; if (e->e_message == NULL) - e->e_message = newstr("Deferred: user database error"); + e->e_message = "Deferred: user database error"; + if (new->q_message == NULL) + new->q_message = "Deferred: user database error"; if (LogLevel > 8) sm_syslog(LOG_INFO, e->e_id, "deferred: udbexpand: %s", - errstring(errno)); + sm_errstring(errno)); message("queued (user database error): %s", - errstring(errno)); + sm_errstring(errno)); e->e_nrcpts++; goto testselfdestruct; } @@ -655,22 +915,22 @@ recipient(a, sendq, aliaslevel, e) /* ** If we have a level two config file, then pass the name through ** Ruleset 5 before sending it off. Ruleset 5 has the right - ** to send rewrite it to another mailer. This gives us a hook + ** to rewrite it to another mailer. This gives us a hook ** after local aliasing has been done. */ if (tTd(29, 5)) { - dprintf("recipient: testing local? cl=%d, rr5=%lx\n\t", - ConfigLevel, (u_long) RewriteRules[5]); - printaddr(a, FALSE); + sm_dprintf("recipient: testing local? cl=%d, rr5=%p\n\t", + ConfigLevel, RewriteRules[5]); + printaddr(new, false); } if (ConfigLevel >= 2 && RewriteRules[5] != NULL && bitnset(M_TRYRULESET5, m->m_flags) && - !bitset(QNOTREMOTE, a->q_flags) && - QS_IS_OK(a->q_state)) + !bitset(QNOTREMOTE, new->q_flags) && + QS_IS_OK(new->q_state)) { - maplocaluser(a, sendq, aliaslevel + 1, e); + maplocaluser(new, sendq, aliaslevel + 1, e); } /* @@ -678,90 +938,99 @@ recipient(a, sendq, aliaslevel, e) ** and deliver it. */ - if (QS_IS_OK(a->q_state) && + if (QS_IS_OK(new->q_state) && bitnset(M_HASPWENT, m->m_flags)) { auto bool fuzzy; - register struct passwd *pw; + SM_MBDB_T user; + int status; /* warning -- finduser may trash buf */ - pw = finduser(buf, &fuzzy); - if (pw == NULL || strlen(pw->pw_name) > MAXNAME) - { - { - a->q_state = QS_BADADDR; - a->q_status = "5.1.1"; - a->q_rstatus = newstr("550 5.1.1 User unknown"); - giveresponse(EX_NOUSER, a->q_status, m, NULL, - a->q_alias, (time_t) 0, e); - } - } - else + status = finduser(buf, &fuzzy, &user); + switch (status) { - char nbuf[MAXNAME + 1]; - + case EX_TEMPFAIL: + new->q_state = QS_QUEUEUP; + new->q_status = "4.5.2"; + giveresponse(EX_TEMPFAIL, new->q_status, m, NULL, + new->q_alias, (time_t) 0, e, new); + break; + default: + new->q_state = QS_BADADDR; + new->q_status = "5.1.1"; + new->q_rstatus = "550 5.1.1 User unknown"; + giveresponse(EX_NOUSER, new->q_status, m, NULL, + new->q_alias, (time_t) 0, e, new); + break; + case EX_OK: if (fuzzy) { /* name was a fuzzy match */ - a->q_user = newstr(pw->pw_name); + new->q_user = sm_rpool_strdup_x(e->e_rpool, + user.mbdb_name); if (findusercount++ > 3) { - a->q_state = QS_BADADDR; - a->q_status = "5.4.6"; - usrerrenh(a->q_status, + new->q_state = QS_BADADDR; + new->q_status = "5.4.6"; + usrerrenh(new->q_status, "554 aliasing/forwarding loop for %s broken", - pw->pw_name); + user.mbdb_name); goto done; } /* see if it aliases */ - (void) strlcpy(buf, pw->pw_name, buflen); + (void) sm_strlcpy(buf, user.mbdb_name, buflen); goto trylocaluser; } - if (*pw->pw_dir == '\0') - a->q_home = NULL; - else if (strcmp(pw->pw_dir, "/") == 0) - a->q_home = ""; + if (*user.mbdb_homedir == '\0') + new->q_home = NULL; + else if (strcmp(user.mbdb_homedir, "/") == 0) + new->q_home = ""; else - a->q_home = newstr(pw->pw_dir); - a->q_uid = pw->pw_uid; - a->q_gid = pw->pw_gid; - a->q_ruser = newstr(pw->pw_name); - a->q_flags |= QGOODUID; - buildfname(pw->pw_gecos, pw->pw_name, nbuf, sizeof nbuf); - if (nbuf[0] != '\0') - a->q_fullname = newstr(nbuf); - if (!usershellok(pw->pw_name, pw->pw_shell)) + new->q_home = sm_rpool_strdup_x(e->e_rpool, + user.mbdb_homedir); + if (user.mbdb_uid != SM_NO_UID) + { + new->q_uid = user.mbdb_uid; + new->q_gid = user.mbdb_gid; + new->q_flags |= QGOODUID; + } + new->q_ruser = sm_rpool_strdup_x(e->e_rpool, + user.mbdb_name); + if (user.mbdb_fullname[0] != '\0') + new->q_fullname = sm_rpool_strdup_x(e->e_rpool, + user.mbdb_fullname); + if (!usershellok(user.mbdb_name, user.mbdb_shell)) { - a->q_flags |= QBOGUSSHELL; + new->q_flags |= QBOGUSSHELL; } if (bitset(EF_VRFYONLY, e->e_flags)) { /* don't do any more now */ - a->q_state = QS_VERIFIED; + new->q_state = QS_VERIFIED; } else if (!quoted) - forward(a, sendq, aliaslevel, e); + forward(new, sendq, aliaslevel, e); } } - if (!QS_IS_DEAD(a->q_state)) + if (!QS_IS_DEAD(new->q_state)) e->e_nrcpts++; testselfdestruct: - a->q_flags |= QTHISPASS; + new->q_flags |= QTHISPASS; if (tTd(26, 8)) { - dprintf("testselfdestruct: "); - printaddr(a, FALSE); + sm_dprintf("testselfdestruct: "); + printaddr(new, false); if (tTd(26, 10)) { - dprintf("SENDQ:\n"); - printaddr(*sendq, TRUE); - dprintf("----\n"); + sm_dprintf("SENDQ:\n"); + printaddr(*sendq, true); + sm_dprintf("----\n"); } } - if (a->q_alias == NULL && a != &e->e_from && - QS_IS_DEAD(a->q_state)) + if (new->q_alias == NULL && new != &e->e_from && + QS_IS_DEAD(new->q_state)) { for (q = *sendq; q != NULL; q = q->q_next) { @@ -770,17 +1039,17 @@ recipient(a, sendq, aliaslevel, e) } if (q == NULL) { - a->q_state = QS_BADADDR; - a->q_status = "5.4.6"; - usrerrenh(a->q_status, + new->q_state = QS_BADADDR; + new->q_status = "5.4.6"; + usrerrenh(new->q_status, "554 aliasing/forwarding loop broken"); } } done: - a->q_flags |= QTHISPASS; + new->q_flags |= QTHISPASS; if (buf != buf0) - sm_free(buf); + sm_free(buf); /* XXX leak if above code raises exception */ /* ** If we are at the top level, check to see if this has @@ -821,16 +1090,18 @@ recipient(a, sendq, aliaslevel, e) { /* arrange for return receipt */ e->e_flags |= EF_SENDRECEIPT; - a->q_flags |= QEXPANDED; + new->q_flags |= QEXPANDED; if (e->e_xfp != NULL && - bitset(QPINGONSUCCESS, a->q_flags)) - fprintf(e->e_xfp, - "%s... expanded to multiple addresses\n", - a->q_paddr); + bitset(QPINGONSUCCESS, new->q_flags)) + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + "%s... expanded to multiple addresses\n", + new->q_paddr); } } - a->q_flags |= QRCPTOK; - return a; + new->q_flags |= QRCPTOK; + (void) sm_snprintf(buf0, sizeof buf0, "%d", e->e_nrcpts); + macdefine(&e->e_macro, A_TEMP, macid("{nrcpts}"), buf0); + return new; } /* ** FINDUSER -- find the password entry for a user. @@ -843,33 +1114,39 @@ recipient(a, sendq, aliaslevel, e) ** ** Parameters: ** name -- the name to match against. -** fuzzyp -- an outarg that is set to TRUE if this entry +** fuzzyp -- an outarg that is set to true if this entry ** was found using the fuzzy matching algorithm; -** set to FALSE otherwise. +** set to false otherwise. +** user -- structure to fill in if user is found ** ** Returns: -** A pointer to a pw struct. -** NULL if name is unknown or ambiguous. +** On success, fill in *user, set *fuzzyp and return EX_OK. +** If the user was not found, return EX_NOUSER. +** On error, return EX_TEMPFAIL or EX_OSERR. ** ** Side Effects: ** may modify name. */ -struct passwd * -finduser(name, fuzzyp) +int +finduser(name, fuzzyp, user) char *name; bool *fuzzyp; + SM_MBDB_T *user; { +#if MATCHGECOS register struct passwd *pw; +#endif /* MATCHGECOS */ register char *p; bool tryagain; + int status; if (tTd(29, 4)) - dprintf("finduser(%s): ", name); + sm_dprintf("finduser(%s): ", name); - *fuzzyp = FALSE; + *fuzzyp = false; -#ifdef HESIOD +#if HESIOD /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */ for (p = name; *p != '\0'; p++) if (!isascii(*p) || !isdigit(*p)) @@ -877,35 +1154,36 @@ finduser(name, fuzzyp) if (*p == '\0') { if (tTd(29, 4)) - dprintf("failed (numeric input)\n"); - return NULL; + sm_dprintf("failed (numeric input)\n"); + return EX_NOUSER; } #endif /* HESIOD */ /* look up this login name using fast path */ - if ((pw = sm_getpwnam(name)) != NULL) + status = sm_mbdb_lookup(name, user); + if (status != EX_NOUSER) { if (tTd(29, 4)) - dprintf("found (non-fuzzy)\n"); - return pw; + sm_dprintf("%s (non-fuzzy)\n", sm_strexit(status)); + return status; } /* try mapping it to lower case */ - tryagain = FALSE; + tryagain = false; for (p = name; *p != '\0'; p++) { if (isascii(*p) && isupper(*p)) { *p = tolower(*p); - tryagain = TRUE; + tryagain = true; } } - if (tryagain && (pw = sm_getpwnam(name)) != NULL) + if (tryagain && (status = sm_mbdb_lookup(name, user)) != EX_NOUSER) { if (tTd(29, 4)) - dprintf("found (lower case)\n"); - *fuzzyp = TRUE; - return pw; + sm_dprintf("%s (lower case)\n", sm_strexit(status)); + *fuzzyp = true; + return status; } #if MATCHGECOS @@ -913,8 +1191,8 @@ finduser(name, fuzzyp) if (!MatchGecos) { if (tTd(29, 4)) - dprintf("not found (fuzzy disabled)\n"); - return NULL; + sm_dprintf("not found (fuzzy disabled)\n"); + return EX_NOUSER; } /* search for a matching full name instead */ @@ -929,35 +1207,38 @@ finduser(name, fuzzyp) char buf[MAXNAME + 1]; # if 0 - if (strcasecmp(pw->pw_name, name) == 0) + if (sm_strcasecmp(pw->pw_name, name) == 0) { if (tTd(29, 4)) - dprintf("found (case wrapped)\n"); + sm_dprintf("found (case wrapped)\n"); break; } # endif /* 0 */ - buildfname(pw->pw_gecos, pw->pw_name, buf, sizeof buf); - if (strchr(buf, ' ') != NULL && strcasecmp(buf, name) == 0) + sm_pwfullname(pw->pw_gecos, pw->pw_name, buf, sizeof buf); + if (strchr(buf, ' ') != NULL && sm_strcasecmp(buf, name) == 0) { if (tTd(29, 4)) - dprintf("fuzzy matches %s\n", pw->pw_name); + sm_dprintf("fuzzy matches %s\n", pw->pw_name); message("sending to login name %s", pw->pw_name); break; } } if (pw != NULL) - *fuzzyp = TRUE; + *fuzzyp = true; else if (tTd(29, 4)) - dprintf("no fuzzy match found\n"); + sm_dprintf("no fuzzy match found\n"); # if DEC_OSF_BROKEN_GETPWENT /* DEC OSF/1 3.2 or earlier */ endpwent(); # endif /* DEC_OSF_BROKEN_GETPWENT */ - return pw; + if (pw == NULL) + return EX_NOUSER; + sm_mbdb_frompw(user, pw); + return EX_OK; #else /* MATCHGECOS */ if (tTd(29, 4)) - dprintf("not found (fuzzy disabled)\n"); - return NULL; + sm_dprintf("not found (fuzzy disabled)\n"); + return EX_NOUSER; #endif /* MATCHGECOS */ } /* @@ -977,8 +1258,8 @@ finduser(name, fuzzyp) ** flags -- SFF_* flags to control the function. ** ** Returns: -** TRUE -- if we will be able to write this file. -** FALSE -- if we cannot write this file. +** true -- if we will be able to write this file. +** false -- if we cannot write this file. ** ** Side Effects: ** none. @@ -995,7 +1276,7 @@ writable(filename, ctladdr, flags) char *user = NULL; if (tTd(44, 5)) - dprintf("writable(%s, 0x%lx)\n", filename, flags); + sm_dprintf("writable(%s, 0x%lx)\n", filename, flags); /* ** File does exist -- check that it is writable. @@ -1057,8 +1338,8 @@ writable(filename, ctladdr, flags) ** ** Parameters: ** fname -- filename to include. -** forwarding -- if TRUE, we are reading a .forward file. -** if FALSE, it's a :include: file. +** forwarding -- if true, we are reading a .forward file. +** if false, it's a :include: file. ** ctladdr -- address template to use to fill in these ** addresses -- effective user/group id are ** the important things. @@ -1099,14 +1380,14 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e) int aliaslevel; ENVELOPE *e; { - FILE *volatile fp = NULL; + SM_FILE_T *volatile fp = NULL; char *oldto = e->e_to; char *oldfilename = FileName; int oldlinenumber = LineNumber; - register EVENT *ev = NULL; + register SM_EVENT *ev = NULL; int nincludes; int mode; - volatile bool maxreached = FALSE; + volatile bool maxreached = false; register ADDRESS *ca; volatile uid_t saveduid; volatile gid_t savedgid; @@ -1116,29 +1397,30 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e) int rval = 0; volatile long sfflags = SFF_REGONLY; register char *p; - bool safechown = FALSE; - volatile bool safedir = FALSE; + bool safechown = false; + volatile bool safedir = false; + bool oldsplit; struct stat st; char buf[MAXLINE]; if (tTd(27, 2)) - dprintf("include(%s)\n", fname); + sm_dprintf("include(%s)\n", fname); if (tTd(27, 4)) - dprintf(" ruid=%d euid=%d\n", + sm_dprintf(" ruid=%d euid=%d\n", (int) getuid(), (int) geteuid()); if (tTd(27, 14)) { - dprintf("ctladdr "); - printaddr(ctladdr, FALSE); + sm_dprintf("ctladdr "); + printaddr(ctladdr, false); } if (tTd(27, 9)) - dprintf("include: old uid = %d/%d\n", - (int) getuid(), (int) geteuid()); + sm_dprintf("include: old uid = %d/%d\n", + (int) getuid(), (int) geteuid()); -#if _FFR_UNSAFE_WRITABLE_INCLUDE if (forwarding) { + sfflags |= SFF_MUSTOWN|SFF_ROOTOK|SFF_NOWLINK; if (!bitnset(DBS_GROUPWRITABLEFORWARDFILE, DontBlameSendmail)) sfflags |= SFF_NOGWFILES; if (!bitnset(DBS_WORLDWRITABLEFORWARDFILE, DontBlameSendmail)) @@ -1151,10 +1433,6 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e) if (!bitnset(DBS_WORLDWRITABLEINCLUDEFILE, DontBlameSendmail)) sfflags |= SFF_NOWWFILES; } -#endif /* _FFR_UNSAFE_WRITABLE_INCLUDE */ - - if (forwarding) - sfflags |= SFF_MUSTOWN|SFF_ROOTOK|SFF_NOWLINK; /* ** If RunAsUser set, won't be able to run programs as user @@ -1165,8 +1443,8 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e) !bitnset(DBS_NONROOTSAFEADDR, DontBlameSendmail)) { if (tTd(27, 4)) - dprintf("include: not safe (euid=%d, RunAsUid=%d)\n", - (int) geteuid(), (int) RunAsUid); + sm_dprintf("include: not safe (euid=%d, RunAsUid=%d)\n", + (int) geteuid(), (int) RunAsUid); ctladdr->q_flags |= QUNSAFEADDR; } @@ -1243,8 +1521,8 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e) #endif /* MAILER_SETUID_METHOD != USE_SETUID */ if (tTd(27, 9)) - dprintf("include: new uid = %d/%d\n", - (int) getuid(), (int) geteuid()); + sm_dprintf("include: new uid = %d/%d\n", + (int) getuid(), (int) geteuid()); /* ** If home directory is remote mounted but server is down, @@ -1261,7 +1539,7 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e) goto resetuid; } if (TimeOuts.to_fileopen > 0) - ev = setevent(TimeOuts.to_fileopen, includetimeout, 0); + ev = sm_setevent(TimeOuts.to_fileopen, includetimeout, 0); else ev = NULL; @@ -1278,7 +1556,7 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e) if (ret == 0) { /* in safe directory: relax chown & link rules */ - safedir = TRUE; + safedir = true; sfflags |= SFF_NOPATHCHECK; } else @@ -1314,7 +1592,7 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e) DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE), DontBlameSendmail)) { - if (LogLevel >= 12) + if (LogLevel > 11) sm_syslog(LOG_INFO, e->e_id, "%s: unsafe directory path, marked unsafe", shortenstring(fname, MAXSHORTSTR)); @@ -1337,23 +1615,24 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e) { /* don't use this :include: file */ if (tTd(27, 4)) - dprintf("include: not safe (uid=%d): %s\n", - (int) uid, errstring(rval)); + sm_dprintf("include: not safe (uid=%d): %s\n", + (int) uid, sm_errstring(rval)); } - else if ((fp = fopen(fname, "r")) == NULL) + else if ((fp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, fname, + SM_IO_RDONLY, NULL)) == NULL) { rval = errno; if (tTd(27, 4)) - dprintf("include: open: %s\n", errstring(rval)); + sm_dprintf("include: open: %s\n", sm_errstring(rval)); } - else if (filechanged(fname, fileno(fp), &st)) + else if (filechanged(fname, sm_io_getinfo(fp,SM_IO_WHAT_FD, NULL), &st)) { rval = E_SM_FILECHANGE; if (tTd(27, 4)) - dprintf("include: file changed after open\n"); + sm_dprintf("include: file changed after open\n"); } if (ev != NULL) - clrevent(ev); + sm_clrevent(ev); resetuid: @@ -1365,25 +1644,27 @@ resetuid: # if USESETEUID if (seteuid(0) < 0) syserr("!seteuid(0) failure (real=%d, eff=%d)", - getuid(), geteuid()); + (int) getuid(), (int) geteuid()); # else /* USESETEUID */ if (setreuid(-1, 0) < 0) syserr("!setreuid(-1, 0) failure (real=%d, eff=%d)", - getuid(), geteuid()); + (int) getuid(), (int) geteuid()); if (setreuid(RealUid, 0) < 0) syserr("!setreuid(%d, 0) failure (real=%d, eff=%d)", - RealUid, getuid(), geteuid()); + (int) RealUid, (int) getuid(), + (int) geteuid()); # endif /* USESETEUID */ } if (setgid(savedgid) < 0) syserr("!setgid(%d) failure (real=%d eff=%d)", - savedgid, getgid(), getegid()); + (int) savedgid, (int) getgid(), + (int) getegid()); } #endif /* HASSETREUID || USESETEUID */ if (tTd(27, 9)) - dprintf("include: reset uid = %d/%d\n", - (int) getuid(), (int) geteuid()); + sm_dprintf("include: reset uid = %d/%d\n", + (int) getuid(), (int) geteuid()); if (rval == E_SM_OPENTIMEOUT) usrerr("451 4.4.1 open timeout on %s", fname); @@ -1391,21 +1672,20 @@ resetuid: if (fp == NULL) return rval; - if (fstat(fileno(fp), &st) < 0) + if (fstat(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), &st) < 0) { rval = errno; syserr("Cannot fstat %s!", fname); - (void) fclose(fp); + (void) sm_io_close(fp, SM_TIME_DEFAULT); return rval; } /* if path was writable, check to avoid file giveaway tricks */ - safechown = chownsafe(fileno(fp), safedir); + safechown = chownsafe(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), safedir); if (tTd(27, 6)) - dprintf("include: parent of %s is %s, chown is %ssafe\n", - fname, - safedir ? "safe" : "dangerous", - safechown ? "" : "un"); + sm_dprintf("include: parent of %s is %s, chown is %ssafe\n", + fname, safedir ? "safe" : "dangerous", + safechown ? "" : "un"); /* if no controlling user or coming from an alias delivery */ if (safechown && @@ -1436,17 +1716,19 @@ resetuid: { char *sh; - ctladdr->q_ruser = newstr(pw->pw_name); + ctladdr->q_ruser = sm_rpool_strdup_x(e->e_rpool, + pw->pw_name); if (safechown) sh = pw->pw_shell; else sh = "/SENDMAIL/ANY/SHELL/"; if (!usershellok(pw->pw_name, sh)) { - if (LogLevel >= 12) + if (LogLevel > 11) sm_syslog(LOG_INFO, e->e_id, "%s: user %s has bad shell %s, marked %s", - shortenstring(fname, MAXSHORTSTR), + shortenstring(fname, + MAXSHORTSTR), pw->pw_name, sh, safechown ? "bogus" : "unsafe"); if (safechown) @@ -1462,7 +1744,7 @@ resetuid: /* don't do any more now */ ctladdr->q_state = QS_VERIFIED; e->e_nrcpts++; - (void) fclose(fp); + (void) sm_io_close(fp, SM_TIME_DEFAULT); return rval; } @@ -1484,10 +1766,11 @@ resetuid: if (bitset(mode, st.st_mode)) { if (tTd(27, 6)) - dprintf("include: %s is %s writable, marked unsafe\n", - shortenstring(fname, MAXSHORTSTR), - bitset(S_IWOTH, st.st_mode) ? "world" : "group"); - if (LogLevel >= 12) + sm_dprintf("include: %s is %s writable, marked unsafe\n", + shortenstring(fname, MAXSHORTSTR), + bitset(S_IWOTH, st.st_mode) ? "world" + : "group"); + if (LogLevel > 11) sm_syslog(LOG_INFO, e->e_id, "%s: %s writable %s file, marked unsafe", shortenstring(fname, MAXSHORTSTR), @@ -1501,9 +1784,12 @@ resetuid: LineNumber = 0; ctladdr->q_flags &= ~QSELFREF; nincludes = 0; - while (fgets(buf, sizeof buf, fp) != NULL && !maxreached) + oldsplit = bitset(EF_SPLIT, e->e_flags); + e->e_flags &= ~EF_SPLIT; + while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof buf) != NULL && + !maxreached) { - fixcrlf(buf, TRUE); + fixcrlf(buf, true); LineNumber++; if (buf[0] == '#' || buf[0] == '\0') continue; @@ -1542,28 +1828,34 @@ resetuid: nincludes >= MaxForwardEntries) { /* just stop reading and processing further entries */ - /* additional: (?) +#if 0 + /* additional: (?) */ ctladdr->q_state = QS_DONTSEND; - **/ +#endif /* 0 */ + syserr("Attempt to forward to more then %d addresses (in %s)!", MaxForwardEntries,fname); - maxreached = TRUE; + maxreached = true; } } - if (ferror(fp) && tTd(27, 3)) - dprintf("include: read error: %s\n", errstring(errno)); + if (sm_io_error(fp) && tTd(27, 3)) + sm_dprintf("include: read error: %s\n", sm_errstring(errno)); if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags)) { if (tTd(27, 5)) { - dprintf("include: QS_DONTSEND "); - printaddr(ctladdr, FALSE); + sm_dprintf("include: QS_DONTSEND "); + printaddr(ctladdr, false); } ctladdr->q_state = QS_DONTSEND; } - (void) fclose(fp); + /* if nothing included: restore old split flag */ + if (nincludes <= 0 && oldsplit) + e->e_flags |= EF_SPLIT; + + (void) sm_io_close(fp, SM_TIME_DEFAULT); FileName = oldfilename; LineNumber = oldlinenumber; e->e_to = oldto; @@ -1605,9 +1897,7 @@ sendtoargv(argv, e) register char *p; while ((p = *argv++) != NULL) - { (void) sendtolist(p, NULLADDR, &e->e_sendqueue, 0, e); - } } /* ** GETCTLADDR -- get controlling address from an address header. @@ -1619,9 +1909,6 @@ sendtoargv(argv, e) ** ** Returns: ** the controlling address. -** -** Side Effects: -** none. */ ADDRESS * @@ -1659,7 +1946,7 @@ self_reference(a) ADDRESS *c; /* entry that point to a real mail box */ if (tTd(27, 1)) - dprintf("self_reference(%s)\n", a->q_paddr); + sm_dprintf("self_reference(%s)\n", a->q_paddr); for (b = a->q_alias; b != NULL; b = b->q_alias) { @@ -1670,40 +1957,42 @@ self_reference(a) if (b == NULL) { if (tTd(27, 1)) - dprintf("\t... no self ref\n"); + sm_dprintf("\t... no self ref\n"); return NULL; } /* ** Pick the first address that resolved to a real mail box - ** i.e has a pw entry. The returned value will be marked + ** i.e has a mbdb entry. The returned value will be marked ** QSELFREF in recipient(), which in turn will disable alias() ** from marking it as QS_IS_DEAD(), which mean it will be used ** as a deliverable address. ** ** The 2 key thing to note here are: ** 1) we are in a recursive call sequence: - ** alias->sentolist->recipient->alias + ** alias->sendtolist->recipient->alias ** 2) normally, when we return back to alias(), the address ** will be marked QS_EXPANDED, since alias() assumes the ** expanded form will be used instead of the current address. ** This behaviour is turned off if the address is marked - ** QSELFREF We set QSELFREF when we return to recipient(). + ** QSELFREF. We set QSELFREF when we return to recipient(). */ c = a; while (c != NULL) { if (tTd(27, 10)) - dprintf(" %s", c->q_user); + sm_dprintf(" %s", c->q_user); if (bitnset(M_HASPWENT, c->q_mailer->m_flags)) { + SM_MBDB_T user; + if (tTd(27, 2)) - dprintf("\t... getpwnam(%s)... ", c->q_user); - if (sm_getpwnam(c->q_user) != NULL) + sm_dprintf("\t... getpwnam(%s)... ", c->q_user); + if (sm_mbdb_lookup(c->q_user, &user) == EX_OK) { if (tTd(27, 2)) - dprintf("found\n"); + sm_dprintf("found\n"); /* ought to cache results here */ if (sameaddr(b, c)) @@ -1712,7 +2001,7 @@ self_reference(a) return c; } if (tTd(27, 2)) - dprintf("failed\n"); + sm_dprintf("failed\n"); } else { @@ -1721,7 +2010,7 @@ self_reference(a) b->q_mailer == c->q_mailer) { if (tTd(27, 2)) - dprintf("\t... local match (%s)\n", + sm_dprintf("\t... local match (%s)\n", c->q_user); if (sameaddr(b, c)) return b; @@ -1730,12 +2019,12 @@ self_reference(a) } } if (tTd(27, 10)) - dprintf("\n"); + sm_dprintf("\n"); c = c->q_alias; } if (tTd(27, 1)) - dprintf("\t... cannot break loop for \"%s\"\n", a->q_paddr); + sm_dprintf("\t... cannot break loop for \"%s\"\n", a->q_paddr); return NULL; } diff --git a/gnu/usr.sbin/sendmail/sendmail/savemail.c b/gnu/usr.sbin/sendmail/sendmail/savemail.c index c341e7894a4..0f90db8d443 100644 --- a/gnu/usr.sbin/sendmail/sendmail/savemail.c +++ b/gnu/usr.sbin/sendmail/sendmail/savemail.c @@ -11,12 +11,9 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: savemail.c,v 8.212.4.13 2001/05/03 17:24:15 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +SM_RCSID("@(#)$Sendmail: savemail.c,v 8.287 2001/09/04 22:43:05 ca Exp $") static void errbody __P((MCI *, ENVELOPE *, char *)); static bool pruneroute __P((char *)); @@ -31,11 +28,11 @@ static bool pruneroute __P((char *)); ** ** Parameters: ** e -- the envelope containing the message in error. -** sendbody -- if TRUE, also send back the body of the +** sendbody -- if true, also send back the body of the ** message; otherwise just send the header. ** ** Returns: -** none +** true if the df file should be preserved by dropenvelope() ** ** Side Effects: ** Saves the letter, by writing or mailing it back to the @@ -53,14 +50,13 @@ static bool pruneroute __P((char *)); #define ESM_PANIC 6 /* call loseqfile() */ #define ESM_DONE 7 /* message is successfully delivered */ - -void +bool savemail(e, sendbody) register ENVELOPE *e; bool sendbody; { - register struct passwd *pw; - register FILE *fp; + register SM_FILE_T *fp; + bool savedf = false; int state; auto ADDRESS *q = NULL; register char *p; @@ -68,19 +64,21 @@ savemail(e, sendbody) int flags; long sff; char buf[MAXLINE + 1]; + SM_MBDB_T user; + if (tTd(6, 1)) { - dprintf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=", + sm_dprintf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=", e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id, ExitStat); - printaddr(&e->e_from, FALSE); + printaddr(&e->e_from, false); } if (e->e_id == NULL) { /* can't return a message with no id */ - return; + return savedf; } /* @@ -92,10 +90,11 @@ savemail(e, sendbody) { e->e_sender = "Postmaster"; if (parseaddr(e->e_sender, &e->e_from, - RF_COPYPARSE|RF_SENDERADDR, '\0', NULL, e) == NULL) + RF_COPYPARSE|RF_SENDERADDR, + '\0', NULL, e, false) == NULL) { syserr("553 5.3.5 Cannot parse Postmaster!"); - finis(TRUE, EX_SOFTWARE); + finis(true, EX_SOFTWARE); } } e->e_to = NULL; @@ -135,7 +134,7 @@ savemail(e, sendbody) case EM_QUIET: /* no need to return anything at all */ - return; + return savedf; default: syserr("554 5.3.0 savemail: bogus errormode x%x\n", @@ -151,7 +150,7 @@ savemail(e, sendbody) bitset(EF_RESPONSE, e->e_parent->e_flags)) { /* got an error sending a response -- can it */ - return; + return savedf; } state = ESM_POSTMASTER; } @@ -159,7 +158,7 @@ savemail(e, sendbody) while (state != ESM_DONE) { if (tTd(6, 5)) - dprintf(" state %d\n", state); + sm_dprintf(" state %d\n", state); switch (state) { @@ -178,29 +177,40 @@ savemail(e, sendbody) */ p = ttypath(); - if (p == NULL || freopen(p, "w", stdout) == NULL) + if (p == NULL || sm_io_reopen(SmFtStdio, + SM_TIME_DEFAULT, + p, SM_IO_WRONLY, NULL, + smioout) == NULL) { state = ESM_MAIL; break; } expand("\201n", buf, sizeof buf, e); - printf("\r\nMessage from %s...\r\n", buf); - printf("Errors occurred while sending mail.\r\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "\r\nMessage from %s...\r\n", buf); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Errors occurred while sending mail.\r\n"); if (e->e_xfp != NULL) { (void) bfrewind(e->e_xfp); - printf("Transcript follows:\r\n"); - while (fgets(buf, sizeof buf, e->e_xfp) != NULL && - !ferror(stdout)) - (void) fputs(buf, stdout); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Transcript follows:\r\n"); + while (sm_io_fgets(e->e_xfp, SM_TIME_DEFAULT, + buf, sizeof buf) != NULL && + !sm_io_error(smioout)) + (void) sm_io_fputs(smioout, + SM_TIME_DEFAULT, + buf); } else { syserr("Cannot open %s", queuename(e, 'x')); - printf("Transcript of session is unavailable.\r\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Transcript of session is unavailable.\r\n"); } - printf("Original message will be saved in dead.letter.\r\n"); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Original message will be saved in dead.letter.\r\n"); state = ESM_DEADLETTER; break; @@ -225,20 +235,19 @@ savemail(e, sendbody) if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE) { - (void) sendtolist("postmaster", - NULLADDR, &e->e_errorqueue, 0, e); + (void) sendtolist("postmaster", NULLADDR, + &e->e_errorqueue, 0, e); } if (!emptyaddr(&e->e_from)) { char from[TOBUFSIZE]; - if (strlen(e->e_from.q_paddr) >= sizeof from) + if (sm_strlcpy(from, e->e_from.q_paddr, + sizeof from) >= sizeof from) { state = ESM_POSTMASTER; break; } - (void) strlcpy(from, e->e_from.q_paddr, - sizeof from); if (!DontPruneRoutes && pruneroute(from)) { @@ -297,6 +306,17 @@ savemail(e, sendbody) q = NULL; expand(DoubleBounceAddr, buf, sizeof buf, e); + + /* + ** Just drop it on the floor if DoubleBounceAddr + ** expands to an empty string. + */ + + if (*buf == '\0') + { + state = ESM_DONE; + break; + } if (sendtolist(buf, NULLADDR, &q, 0, e) <= 0) { syserr("553 5.3.0 cannot parse %s!", buf); @@ -332,9 +352,10 @@ savemail(e, sendbody) { if (e->e_from.q_home != NULL) p = e->e_from.q_home; - else if ((pw = sm_getpwnam(e->e_from.q_user)) != NULL && - *pw->pw_dir != '\0') - p = pw->pw_dir; + else if (sm_mbdb_lookup(e->e_from.q_user, &user) + == EX_OK && + *user.mbdb_homedir != '\0') + p = user.mbdb_homedir; } if (p == NULL || e->e_dfp == NULL) { @@ -344,11 +365,11 @@ savemail(e, sendbody) } /* we have a home directory; write dead.letter */ - define('z', p, e); + macdefine(&e->e_macro, A_TEMP, 'z', p); /* get the sender for the UnixFromLine */ p = macvalue('g', e); - define('g', e->e_sender, e); + macdefine(&e->e_macro, A_PERM, 'g', e->e_sender); expand("\201z/dead.letter", buf, sizeof buf, e); sff = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID; @@ -365,11 +386,11 @@ savemail(e, sendbody) if (Verbose > 0) message("Saved message in %s", buf); Verbose = oldverb; - define('g', p, e); + macdefine(&e->e_macro, A_PERM, 'g', p); state = ESM_DONE; break; } - define('g', p, e); + macdefine(&e->e_macro, A_PERM, 'g', p); state = ESM_MAIL; break; @@ -409,15 +430,15 @@ savemail(e, sendbody) /* get the sender for the UnixFromLine */ p = macvalue('g', e); - define('g', e->e_sender, e); + macdefine(&e->e_macro, A_PERM, 'g', e->e_sender); putfromline(&mcibuf, e); (*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER); (*e->e_putbody)(&mcibuf, e, NULL); - putline("\n", &mcibuf); - (void) fflush(fp); - if (ferror(fp) || - fclose(fp) < 0) + putline("\n", &mcibuf); /* XXX EOL from FileMailer? */ + (void) sm_io_flush(fp, SM_TIME_DEFAULT); + if (sm_io_error(fp) || + sm_io_close(fp, SM_TIME_DEFAULT) < 0) state = ESM_PANIC; else { @@ -435,21 +456,24 @@ savemail(e, sendbody) DeadLetterDrop); state = ESM_DONE; } - define('g', p, e); + macdefine(&e->e_macro, A_PERM, 'g', p); break; default: syserr("554 5.3.5 savemail: unknown state %d", state); - /* FALLTHROUGH */ case ESM_PANIC: /* leave the locked queue & transcript files around */ loseqfile(e, "savemail panic"); + savedf = true; errno = 0; - syserr("!554 savemail: cannot save rejected email anywhere"); + syserr("554 savemail: cannot save rejected email anywhere"); + state = ESM_DONE; + break; } } + return savedf; } /* ** RETURNTOSENDER -- return a message to the sender with an error. @@ -468,8 +492,7 @@ savemail(e, sendbody) ** else -- some error. ** ** Side Effects: -** Returns the current message to the sender via -** mail. +** Returns the current message to the sender via mail. */ #define MAXRETURNS 6 /* max depth of returning messages */ @@ -498,13 +521,13 @@ returntosender(msg, returnq, flags, e) if (tTd(6, 1)) { - dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%lx, returnq=", - msg, returndepth, (u_long) e); - printaddr(returnq, TRUE); + sm_dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%p, returnq=", + msg, returndepth, e); + printaddr(returnq, true); if (tTd(6, 20)) { - dprintf("Sendq="); - printaddr(e->e_sendqueue, TRUE); + sm_dprintf("Sendq="); + printaddr(e->e_sendqueue, true); } } @@ -518,32 +541,32 @@ returntosender(msg, returnq, flags, e) return 0; } - define('g', e->e_sender, e); - define('u', NULL, e); + macdefine(&e->e_macro, A_PERM, 'g', e->e_sender); + macdefine(&e->e_macro, A_PERM, 'u', NULL); /* initialize error envelope */ - ee = newenvelope(&errenvelope, e); - define('a', "\201b", ee); - define('r', "", ee); - define('s', "localhost", ee); - define('_', "localhost", ee); + ee = newenvelope(&errenvelope, e, sm_rpool_new_x(NULL)); + macdefine(&ee->e_macro, A_PERM, 'a', "\201b"); + macdefine(&ee->e_macro, A_PERM, 'r', ""); + macdefine(&ee->e_macro, A_PERM, 's', "localhost"); + macdefine(&ee->e_macro, A_PERM, '_', "localhost"); #if SASL - define(macid("{auth_type}", NULL), "", ee); - define(macid("{auth_authen}", NULL), "", ee); - define(macid("{auth_author}", NULL), "", ee); - define(macid("{auth_ssf}", NULL), "", ee); + macdefine(&ee->e_macro, A_PERM, macid("{auth_type}"), ""); + macdefine(&ee->e_macro, A_PERM, macid("{auth_authen}"), ""); + macdefine(&ee->e_macro, A_PERM, macid("{auth_author}"), ""); + macdefine(&ee->e_macro, A_PERM, macid("{auth_ssf}"), ""); #endif /* SASL */ #if STARTTLS - define(macid("{cert_issuer}", NULL), "", ee); - define(macid("{cert_subject}", NULL), "", ee); - define(macid("{cipher_bits}", NULL), "", ee); - define(macid("{cipher}", NULL), "", ee); - define(macid("{tls_version}", NULL), "", ee); - define(macid("{verify}", NULL), "", ee); + macdefine(&ee->e_macro, A_PERM, macid("{cert_issuer}"), ""); + macdefine(&ee->e_macro, A_PERM, macid("{cert_subject}"), ""); + macdefine(&ee->e_macro, A_PERM, macid("{cipher_bits}"), ""); + macdefine(&ee->e_macro, A_PERM, macid("{cipher}"), ""); + macdefine(&ee->e_macro, A_PERM, macid("{tls_version}"), ""); + macdefine(&ee->e_macro, A_PERM, macid("{verify}"), ""); # if _FFR_TLS_1 - define(macid("{alg_bits}", NULL), "", ee); - define(macid("{cn_issuer}", NULL), "", ee); - define(macid("{cn_subject}", NULL), "", ee); + macdefine(&ee->e_macro, A_PERM, macid("{alg_bits}"), ""); + macdefine(&ee->e_macro, A_PERM, macid("{cn_issuer}"), ""); + macdefine(&ee->e_macro, A_PERM, macid("{cn_subject}"), ""); # endif /* _FFR_TLS_1 */ #endif /* STARTTLS */ @@ -573,6 +596,15 @@ returntosender(msg, returnq, flags, e) ee->e_msgsize += e->e_msgsize; else ee->e_flags |= EF_NO_BODY_RETN; + + if (!setnewqueue(ee)) + { + syserr("554 5.3.0 returntosender: cannot select queue for %s", + returnq->q_paddr); + ExitStat = EX_UNAVAILABLE; + returndepth--; + return -1; + } initsys(ee); #if NAMED_BIND @@ -591,7 +623,7 @@ returntosender(msg, returnq, flags, e) ee->e_nrcpts++; if (q->q_alias == NULL) - addheader("To", q->q_paddr, 0, &ee->e_header); + addheader("To", q->q_paddr, 0, ee); } if (LogLevel > 5) @@ -604,72 +636,69 @@ returntosender(msg, returnq, flags, e) p = "postmaster notify"; else p = "DSN"; - sm_syslog(LOG_INFO, e->e_id, - "%s: %s: %s", + sm_syslog(LOG_INFO, e->e_id, "%s: %s: %s", ee->e_id, p, shortenstring(msg, MAXSHORTSTR)); } if (SendMIMEErrors) { - addheader("MIME-Version", "1.0", 0, &ee->e_header); - - (void) snprintf(buf, sizeof buf, "%s.%ld/%.100s", - ee->e_id, (long) curtime(), MyHostName); - ee->e_msgboundary = newstr(buf); - (void) snprintf(buf, sizeof buf, + addheader("MIME-Version", "1.0", 0, ee); + (void) sm_snprintf(buf, sizeof buf, "%s.%ld/%.100s", + ee->e_id, (long)curtime(), MyHostName); + ee->e_msgboundary = sm_rpool_strdup_x(ee->e_rpool, buf); + (void) sm_snprintf(buf, sizeof buf, #if DSN "multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"", #else /* DSN */ "multipart/mixed; boundary=\"%s\"", #endif /* DSN */ ee->e_msgboundary); - addheader("Content-Type", buf, 0, &ee->e_header); + addheader("Content-Type", buf, 0, ee); p = hvalue("Content-Transfer-Encoding", e->e_header); - if (p != NULL && strcasecmp(p, "binary") != 0) + if (p != NULL && sm_strcasecmp(p, "binary") != 0) p = NULL; if (p == NULL && bitset(EF_HAS8BIT, e->e_flags)) p = "8bit"; if (p != NULL) - addheader("Content-Transfer-Encoding", - p, 0, &ee->e_header); + addheader("Content-Transfer-Encoding", p, 0, ee); } if (strncmp(msg, "Warning:", 8) == 0) { - addheader("Subject", msg, 0, &ee->e_header); + addheader("Subject", msg, 0, ee); p = "warning-timeout"; } else if (strncmp(msg, "Postmaster warning:", 19) == 0) { - addheader("Subject", msg, 0, &ee->e_header); + addheader("Subject", msg, 0, ee); p = "postmaster-warning"; } else if (strcmp(msg, "Return receipt") == 0) { - addheader("Subject", msg, 0, &ee->e_header); + addheader("Subject", msg, 0, ee); p = "return-receipt"; } else if (bitset(RTSF_PM_BOUNCE, flags)) { - snprintf(buf, sizeof buf, + (void) sm_snprintf(buf, sizeof buf, "Postmaster notify: see transcript for details"); - addheader("Subject", buf, 0, &ee->e_header); + addheader("Subject", buf, 0, ee); p = "postmaster-notification"; } else { - snprintf(buf, sizeof buf, + (void) sm_snprintf(buf, sizeof buf, "Returned mail: see transcript for details"); - addheader("Subject", buf, 0, &ee->e_header); + addheader("Subject", buf, 0, ee); p = "failure"; } - (void) snprintf(buf, sizeof buf, "auto-generated (%s)", p); - addheader("Auto-Submitted", buf, 0, &ee->e_header); + (void) sm_snprintf(buf, sizeof buf, "auto-generated (%s)", p); + addheader("Auto-Submitted", buf, 0, ee); /* fake up an address header for the from person */ expand("\201n", buf, sizeof buf, e); if (parseaddr(buf, &ee->e_from, - RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL) + RF_COPYALL|RF_SENDERADDR, '\0', NULL, e, false) == NULL) { syserr("553 5.3.5 Can't parse myself!"); ExitStat = EX_SOFTWARE; @@ -682,18 +711,19 @@ returntosender(msg, returnq, flags, e) /* push state into submessage */ CurEnv = ee; - define('f', "\201n", ee); - define('x', "Mail Delivery Subsystem", ee); - eatheader(ee, TRUE); + macdefine(&ee->e_macro, A_PERM, 'f', "\201n"); + macdefine(&ee->e_macro, A_PERM, 'x', "Mail Delivery Subsystem"); + eatheader(ee, true, true); /* mark statistics */ - markstats(ee, NULLADDR, FALSE); + markstats(ee, NULLADDR, false); /* actually deliver the error message */ sendall(ee, SM_DELIVER); /* restore state */ - dropenvelope(ee, TRUE); + dropenvelope(ee, true, false); + sm_rpool_free(ee->e_rpool); CurEnv = oldcur; returndepth--; @@ -717,7 +747,7 @@ returntosender(msg, returnq, flags, e) ** Parameters: ** mci -- the mailer connection information. ** e -- the envelope we are working in. -** separator -- any possible MIME separator. +** separator -- any possible MIME separator (unused). ** ** Returns: ** none @@ -737,9 +767,10 @@ errbody(mci, e, separator) bool sendbody; bool pm_notify; int save_errno; - register FILE *xfile; + register SM_FILE_T *xfile; char *p; register ADDRESS *q = NULL; + char actual[MAXLINE]; char buf[MAXLINE]; if (bitset(MCIF_INHEADER, mci->mci_flags)) @@ -762,7 +793,7 @@ errbody(mci, e, separator) { putline("This is a MIME-encapsulated message", mci); putline("", mci); - (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary); + (void) sm_strlcpyn(buf, sizeof buf, 2, "--", e->e_msgboundary); putline(buf, mci); putline("", mci); } @@ -771,10 +802,10 @@ errbody(mci, e, separator) ** Output introductory information. */ - pm_notify = FALSE; + pm_notify = false; p = hvalue("subject", e->e_header); if (p != NULL && strncmp(p, "Postmaster ", 11) == 0) - pm_notify = TRUE; + pm_notify = true; else { for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) @@ -796,8 +827,9 @@ errbody(mci, e, separator) mci); putline("", mci); } - snprintf(buf, sizeof buf, "The original message was received at %s", - arpadate(ctime(&e->e_parent->e_ctime))); + (void) sm_snprintf(buf, sizeof buf, + "The original message was received at %s", + arpadate(ctime(&e->e_parent->e_ctime))); putline(buf, mci); expand("from \201_", buf, sizeof buf, e->e_parent); putline(buf, mci); @@ -805,7 +837,8 @@ errbody(mci, e, separator) /* include id in postmaster copies */ if (pm_notify && e->e_parent->e_id != NULL) { - snprintf(buf, sizeof buf, "with id %s", e->e_parent->e_id); + (void) sm_strlcpyn(buf, sizeof buf, 2, "with id ", + e->e_parent->e_id); putline(buf, mci); } putline("", mci); @@ -829,13 +862,14 @@ errbody(mci, e, separator) xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff); if (xfile != NULL) { - while (fgets(buf, sizeof buf, xfile) != NULL) + while (sm_io_fgets(xfile, SM_TIME_DEFAULT, buf, + sizeof buf) != NULL) { translate_dollars(buf); expand(buf, buf, sizeof buf, e); putline(buf, mci); } - (void) fclose(xfile); + (void) sm_io_close(xfile, SM_TIME_DEFAULT); putline("\n", mci); } } @@ -851,7 +885,8 @@ errbody(mci, e, separator) ** Output message introduction */ - printheader = TRUE; + /* permanent fatal errors */ + printheader = true; for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) { if (!QS_IS_BADADDR(q->q_state) || @@ -862,35 +897,39 @@ errbody(mci, e, separator) { putline(" ----- The following addresses had permanent fatal errors -----", mci); - printheader = FALSE; + printheader = false; } - snprintf(buf, sizeof buf, "%s", - shortenstring(q->q_paddr, MAXSHORTSTR)); + (void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR), + sizeof buf); putline(buf, mci); if (q->q_rstatus != NULL) { - snprintf(buf, sizeof buf, " (reason: %s)", - shortenstring(exitstat(q->q_rstatus), - MAXSHORTSTR)); + (void) sm_snprintf(buf, sizeof buf, + " (reason: %s)", + shortenstring(exitstat(q->q_rstatus), + MAXSHORTSTR)); putline(buf, mci); } if (q->q_alias != NULL) { - snprintf(buf, sizeof buf, " (expanded from: %s)", - shortenstring(q->q_alias->q_paddr, - MAXSHORTSTR)); + (void) sm_snprintf(buf, sizeof buf, + " (expanded from: %s)", + shortenstring(q->q_alias->q_paddr, + MAXSHORTSTR)); putline(buf, mci); } } if (!printheader) putline("", mci); - printheader = TRUE; + /* transient non-fatal errors */ + printheader = true; for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) { if (QS_IS_BADADDR(q->q_state) || !bitset(QPRIMARY, q->q_flags) || + !bitset(QBYNDELAY, q->q_flags) || !bitset(QDELAYED, q->q_flags)) continue; @@ -898,30 +937,37 @@ errbody(mci, e, separator) { putline(" ----- The following addresses had transient non-fatal errors -----", mci); - printheader = FALSE; + printheader = false; } - snprintf(buf, sizeof buf, "%s", - shortenstring(q->q_paddr, MAXSHORTSTR)); + (void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR), + sizeof buf); putline(buf, mci); if (q->q_alias != NULL) { - snprintf(buf, sizeof buf, " (expanded from: %s)", - shortenstring(q->q_alias->q_paddr, - MAXSHORTSTR)); + (void) sm_snprintf(buf, sizeof buf, + " (expanded from: %s)", + shortenstring(q->q_alias->q_paddr, + MAXSHORTSTR)); putline(buf, mci); } } if (!printheader) putline("", mci); - printheader = TRUE; + /* successful delivery notifications */ + printheader = true; for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) { if (QS_IS_BADADDR(q->q_state) || !bitset(QPRIMARY, q->q_flags) || + bitset(QBYNDELAY, q->q_flags) || bitset(QDELAYED, q->q_flags)) continue; + else if (bitset(QBYNRELAY, q->q_flags)) + p = "Deliver-By notify: relayed"; + else if (bitset(QBYTRACE, q->q_flags)) + p = "Deliver-By trace: relayed"; else if (!bitset(QPINGONSUCCESS, q->q_flags)) continue; else if (bitset(QRELAYED, q->q_flags)) @@ -942,17 +988,18 @@ errbody(mci, e, separator) { putline(" ----- The following addresses had successful delivery notifications -----", mci); - printheader = FALSE; + printheader = false; } - snprintf(buf, sizeof buf, "%s (%s)", + (void) sm_snprintf(buf, sizeof buf, "%s (%s)", shortenstring(q->q_paddr, MAXSHORTSTR), p); putline(buf, mci); if (q->q_alias != NULL) { - snprintf(buf, sizeof buf, " (expanded from: %s)", - shortenstring(q->q_alias->q_paddr, - MAXSHORTSTR)); + (void) sm_snprintf(buf, sizeof buf, + " (expanded from: %s)", + shortenstring(q->q_alias->q_paddr, + MAXSHORTSTR)); putline(buf, mci); } } @@ -963,7 +1010,7 @@ errbody(mci, e, separator) ** Output transcript of errors */ - (void) fflush(stdout); + (void) sm_io_flush(smioout, SM_TIME_DEFAULT); if (e->e_parent->e_xfp == NULL) { putline(" ----- Transcript of session is unavailable -----\n", @@ -971,16 +1018,17 @@ errbody(mci, e, separator) } else { - printheader = TRUE; + printheader = true; (void) bfrewind(e->e_parent->e_xfp); if (e->e_xfp != NULL) - (void) fflush(e->e_xfp); - while (fgets(buf, sizeof buf, e->e_parent->e_xfp) != NULL) + (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); + while (sm_io_fgets(e->e_parent->e_xfp, SM_TIME_DEFAULT, buf, + sizeof buf) != NULL) { if (printheader) putline(" ----- Transcript of session follows -----\n", mci); - printheader = FALSE; + printheader = false; putline(buf, mci); } } @@ -993,10 +1041,8 @@ errbody(mci, e, separator) if (e->e_msgboundary != NULL) { - time_t now = curtime(); - putline("", mci); - (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary); + (void) sm_strlcpyn(buf, sizeof buf, 2, "--", e->e_msgboundary); putline(buf, mci); putline("Content-Type: message/delivery-status", mci); putline("", mci); @@ -1008,15 +1054,15 @@ errbody(mci, e, separator) /* original envelope id from MAIL FROM: line */ if (e->e_parent->e_envid != NULL) { - (void) snprintf(buf, sizeof buf, + (void) sm_snprintf(buf, sizeof buf, "Original-Envelope-Id: %.800s", xuntextify(e->e_parent->e_envid)); putline(buf, mci); } /* Reporting-MTA: is us (required) */ - (void) snprintf(buf, sizeof buf, "Reporting-MTA: dns; %.800s", - MyHostName); + (void) sm_snprintf(buf, sizeof buf, + "Reporting-MTA: dns; %.800s", MyHostName); putline(buf, mci); /* DSN-Gateway: not relevant since we are not translating */ @@ -1028,24 +1074,35 @@ errbody(mci, e, separator) if (e->e_parent->e_from.q_mailer == NULL || (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL) p = "dns"; - (void) snprintf(buf, sizeof buf, + (void) sm_snprintf(buf, sizeof buf, "Received-From-MTA: %s; %.800s", p, RealHostName); putline(buf, mci); } /* Arrival-Date: -- when it arrived here */ - (void) snprintf(buf, sizeof buf, "Arrival-Date: %s", + (void) sm_strlcpyn(buf, sizeof buf, 2, "Arrival-Date: ", arpadate(ctime(&e->e_parent->e_ctime))); putline(buf, mci); + /* Deliver-By-Date: -- when it should have been delivered */ + if (IS_DLVR_BY(e->e_parent)) + { + time_t dbyd; + + dbyd = e->e_parent->e_ctime + e->e_parent->e_deliver_by; + (void) sm_strlcpyn(buf, sizeof buf, 2, + "Deliver-By-Date: ", + arpadate(ctime(&dbyd))); + putline(buf, mci); + } + /* ** Output per-address information. */ for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) { - register ADDRESS *r; char *action; if (QS_IS_BADADDR(q->q_state)) @@ -1071,6 +1128,12 @@ errbody(mci, e, separator) action = "expanded (to multi-recipient alias)"; else if (bitset(QDELAYED, q->q_flags)) action = "delayed"; + else if (bitset(QBYTRACE, q->q_flags)) + action = "relayed (Deliver-By trace mode)"; + else if (bitset(QBYNDELAY, q->q_flags)) + action = "delayed (Deliver-By notify mode)"; + else if (bitset(QBYNRELAY, q->q_flags)) + action = "relayed (Deliver-By notify mode)"; else continue; @@ -1079,63 +1142,15 @@ errbody(mci, e, separator) /* Original-Recipient: -- passed from on high */ if (q->q_orcpt != NULL) { - (void) snprintf(buf, sizeof buf, + (void) sm_snprintf(buf, sizeof buf, "Original-Recipient: %.800s", q->q_orcpt); putline(buf, mci); } - /* Final-Recipient: -- the name from the RCPT command */ - p = e->e_parent->e_from.q_mailer->m_addrtype; - if (p == NULL) - p = "rfc822"; - for (r = q; r->q_alias != NULL; r = r->q_alias) - continue; - if (strcasecmp(p, "rfc822") != 0) - { - (void) snprintf(buf, sizeof buf, - "Final-Recipient: %s; %.800s", - r->q_mailer->m_addrtype, - r->q_user); - } - else if (strchr(r->q_user, '@') != NULL) - { - (void) snprintf(buf, sizeof buf, - "Final-Recipient: %s; %.800s", - p, r->q_user); - } - else if (strchr(r->q_paddr, '@') != NULL) - { - char *qp; - bool b; - - qp = r->q_paddr; - /* strip brackets from address */ - b = FALSE; - if (*qp == '<') - { - b = qp[strlen(qp) - 1] == '>'; - if (b) - qp[strlen(qp) - 1] = '\0'; - qp++; - } - (void) snprintf(buf, sizeof buf, - "Final-Recipient: %s; %.800s", - p, qp); - /* undo damage */ - if (b) - qp[strlen(qp)] = '>'; - } - else - { - (void) snprintf(buf, sizeof buf, - "Final-Recipient: %s; %.700s@%.100s", - p, r->q_user, MyHostName); - } - putline(buf, mci); - - /* X-Actual-Recipient: -- the real problem address */ - if (r != q && q->q_user[0] != '\0') + /* Figure out actual recipient */ + actual[0] = '\0'; + if (q->q_user[0] != '\0') { if (q->q_mailer != NULL && q->q_mailer->m_addrtype != NULL) @@ -1143,25 +1158,59 @@ errbody(mci, e, separator) else p = "rfc822"; - if (strcasecmp(p, "rfc822") == 0 && + if (sm_strcasecmp(p, "rfc822") == 0 && strchr(q->q_user, '@') == NULL) { - (void) snprintf(buf, sizeof buf, - "X-Actual-Recipient: %s; %.700s@%.100s", - p, q->q_user, - MyHostName); + (void) sm_snprintf(actual, + sizeof actual, + "%s; %.700s@%.100s", + p, q->q_user, + MyHostName); } else { - (void) snprintf(buf, sizeof buf, - "X-Actual-Recipient: %s; %.800s", - p, q->q_user); + (void) sm_snprintf(actual, + sizeof actual, + "%s; %.800s", + p, q->q_user); } + } + + /* Final-Recipient: -- the name from the RCPT command */ + if (q->q_finalrcpt == NULL) + { + /* should never happen */ + sm_syslog(LOG_ERR, e->e_id, + "returntosender: q_finalrcpt is NULL"); + + /* try to fall back to the actual recipient */ + if (actual[0] != '\0') + q->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool, + actual); + } + + if (q->q_finalrcpt != NULL) + { + (void) sm_snprintf(buf, sizeof buf, + "Final-Recipient: %s", + q->q_finalrcpt); + putline(buf, mci); + } + + /* X-Actual-Recipient: -- the real problem address */ + if (actual[0] != '\0' && + q->q_finalrcpt != NULL && + strcmp(actual, q->q_finalrcpt) != 0) + { + (void) sm_snprintf(buf, sizeof buf, + "X-Actual-Recipient: %s", + actual); putline(buf, mci); } /* Action: -- what happened? */ - snprintf(buf, sizeof buf, "Action: %s", action); + (void) sm_strlcpyn(buf, sizeof buf, 2, "Action: ", + action); putline(buf, mci); /* Status: -- what _really_ happened? */ @@ -1173,7 +1222,7 @@ errbody(mci, e, separator) p = "4.0.0"; else p = "2.0.0"; - snprintf(buf, sizeof buf, "Status: %s", p); + (void) sm_strlcpyn(buf, sizeof buf, 2, "Status: ", p); putline(buf, mci); /* Remote-MTA: -- who was I talking to? */ @@ -1182,7 +1231,7 @@ errbody(mci, e, separator) if (q->q_mailer == NULL || (p = q->q_mailer->m_mtatype) == NULL) p = "dns"; - (void) snprintf(buf, sizeof buf, + (void) sm_snprintf(buf, sizeof buf, "Remote-MTA: %s; %.800s", p, q->q_statmta); p = &buf[strlen(buf) - 1]; @@ -1197,7 +1246,7 @@ errbody(mci, e, separator) p = q->q_mailer->m_diagtype; if (p == NULL) p = "smtp"; - (void) snprintf(buf, sizeof buf, + (void) sm_snprintf(buf, sizeof buf, "Diagnostic-Code: %s; %.800s", p, q->q_rstatus); putline(buf, mci); @@ -1205,9 +1254,9 @@ errbody(mci, e, separator) /* Last-Attempt-Date: -- fine granularity */ if (q->q_statdate == (time_t) 0L) - q->q_statdate = now; - (void) snprintf(buf, sizeof buf, - "Last-Attempt-Date: %s", + q->q_statdate = curtime(); + (void) sm_strlcpyn(buf, sizeof buf, 2, + "Last-Attempt-Date: ", arpadate(ctime(&q->q_statdate))); putline(buf, mci); @@ -1218,8 +1267,8 @@ errbody(mci, e, separator) xdate = e->e_parent->e_ctime + TimeOuts.to_q_return[e->e_parent->e_timeoutclass]; - snprintf(buf, sizeof buf, - "Will-Retry-Until: %s", + (void) sm_strlcpyn(buf, sizeof buf, 2, + "Will-Retry-Until: ", arpadate(ctime(&xdate))); putline(buf, mci); } @@ -1246,25 +1295,25 @@ errbody(mci, e, separator) } else { - (void) snprintf(buf, sizeof buf, "--%s", + (void) sm_strlcpyn(buf, sizeof buf, 2, "--", e->e_msgboundary); putline(buf, mci); - (void) snprintf(buf, sizeof buf, "Content-Type: %s", + (void) sm_strlcpyn(buf, sizeof buf, 2, "Content-Type: ", sendbody ? "message/rfc822" : "text/rfc822-headers"); putline(buf, mci); p = hvalue("Content-Transfer-Encoding", e->e_parent->e_header); - if (p != NULL && strcasecmp(p, "binary") != 0) + if (p != NULL && sm_strcasecmp(p, "binary") != 0) p = NULL; if (p == NULL && bitset(EF_HAS8BIT, e->e_parent->e_flags)) p = "8bit"; if (p != NULL) { - (void) snprintf(buf, sizeof buf, + (void) sm_snprintf(buf, sizeof buf, "Content-Transfer-Encoding: %s", p); putline(buf, mci); @@ -1290,11 +1339,12 @@ errbody(mci, e, separator) if (e->e_msgboundary != NULL) { putline("", mci); - (void) snprintf(buf, sizeof buf, "--%s--", e->e_msgboundary); + (void) sm_strlcpyn(buf, sizeof buf, 3, "--", e->e_msgboundary, + "--"); putline(buf, mci); } putline("", mci); - (void) fflush(mci->mci_out); + (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT); /* ** Cleanup and exit @@ -1311,6 +1361,11 @@ errbody(mci, e, separator) ** ** Returns: ** The DSN version of the status code. +** +** Storage Management: +** smtptodsn() returns a pointer to a character string literal, +** which will remain valid forever, and thus does not need to +** be copied. Current code relies on this property. */ char * @@ -1405,6 +1460,11 @@ xtextify(t, taboo) nbogus++; l++; } + if (nbogus < 0) + { + /* since nbogus is ssize_t and wrapped, 2 * size_t would wrap */ + syserr("!xtextify string too long"); + } if (nbogus == 0) return t; l += nbogus * 2 + 1; @@ -1413,8 +1473,8 @@ xtextify(t, taboo) if (l > bplen) { if (bp != NULL) - sm_free(bp); - bp = xalloc(l); + sm_free(bp); /* XXX */ + bp = sm_pmalloc_x(l); bplen = l; } @@ -1466,7 +1526,7 @@ xuntextify(t) if (l > bplen) { if (bp != NULL) - sm_free(bp); + sm_free(bp); /* XXX */ bp = xalloc(l); bplen = l; } @@ -1528,8 +1588,8 @@ xuntextify(t) ** s -- the string to check. ** ** Returns: -** TRUE -- if 's' is legal xtext. -** FALSE -- if it has any illegal characters in it. +** true -- if 's' is legal xtext. +** false -- if it has any illegal characters in it. */ bool @@ -1544,15 +1604,15 @@ xtextok(s) { c = *s++; if (!isascii(c) || !isxdigit(c)) - return FALSE; + return false; c = *s++; if (!isascii(c) || !isxdigit(c)) - return FALSE; + return false; } else if (c < '!' || c > '~' || c == '=') - return FALSE; + return false; } - return TRUE; + return true; } /* ** PRUNEROUTE -- prune an RFC-822 source route @@ -1564,8 +1624,8 @@ xtextok(s) ** addr -- the address ** ** Returns: -** TRUE -- address was modified -** FALSE -- address could not be pruned +** true -- address was modified +** false -- address could not be pruned ** ** Side Effects: ** modifies addr in-place @@ -1578,6 +1638,7 @@ pruneroute(addr) #if NAMED_BIND char *start, *at, *comma; char c; + int braclev; int rcode; int i; char hostbuf[BUFSIZ]; @@ -1585,37 +1646,60 @@ pruneroute(addr) /* check to see if this is really a route-addr */ if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>') - return FALSE; - start = strchr(addr, ':'); + return false; + + /* + ** Can't simply find the first ':' is the address might be in the + ** form: "<@[IPv6:::1]:user@host>" and the first ':' in inside + ** the IPv6 address. + */ + + start = addr; + braclev = 0; + while (*start != '\0') + { + if (*start == ':' && braclev <= 0) + break; + else if (*start == '[') + braclev++; + else if (*start == ']' && braclev > 0) + braclev--; + start++; + } + if (braclev > 0 || *start != ':') + return false; + at = strrchr(addr, '@'); - if (start == NULL || at == NULL || at < start) - return FALSE; + if (at == NULL || at < start) + return false; /* slice off the angle brackets */ i = strlen(at + 1); - if (i >= (SIZE_T) sizeof hostbuf) - return FALSE; - (void) strlcpy(hostbuf, at + 1, sizeof hostbuf); + if (i >= sizeof hostbuf) + return false; + (void) sm_strlcpy(hostbuf, at + 1, sizeof hostbuf); hostbuf[i - 1] = '\0'; - while (start) + while (start != NULL) { - if (getmxrr(hostbuf, mxhosts, NULL, FALSE, &rcode) > 0) + if (getmxrr(hostbuf, mxhosts, NULL, false, + &rcode, true, NULL) > 0) { - (void) strlcpy(addr + 1, start + 1, strlen(addr) - 1); - return TRUE; + (void) sm_strlcpy(addr + 1, start + 1, + strlen(addr) - 1); + return true; } c = *start; *start = '\0'; comma = strrchr(addr, ','); if (comma != NULL && comma[1] == '@' && - strlen(comma + 2) < (SIZE_T) sizeof hostbuf) - (void) strlcpy(hostbuf, comma + 2, sizeof hostbuf); + strlen(comma + 2) < sizeof hostbuf) + (void) sm_strlcpy(hostbuf, comma + 2, sizeof hostbuf); else comma = NULL; *start = c; start = comma; } #endif /* NAMED_BIND */ - return FALSE; + return false; } diff --git a/gnu/usr.sbin/sendmail/sendmail/sendmail.8 b/gnu/usr.sbin/sendmail/sendmail/sendmail.8 index 530730ef0d7..f07ba0d68ad 100644 --- a/gnu/usr.sbin/sendmail/sendmail/sendmail.8 +++ b/gnu/usr.sbin/sendmail/sendmail/sendmail.8 @@ -1,4 +1,4 @@ -.\" Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. +.\" Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. .\" All rights reserved. .\" Copyright (c) 1983, 1997 Eric P. Allman. All rights reserved. .\" Copyright (c) 1988, 1991, 1993 @@ -9,9 +9,9 @@ .\" the sendmail distribution. .\" .\" -.\" $Sendmail: sendmail.8,v 8.36.8.3 2000/12/14 23:08:15 gshapiro Exp $ +.\" $Sendmail: sendmail.8,v 8.49 2001/03/23 22:10:00 ca Exp $ .\" -.Dd December 14, 2000 +.Dd March 23, 2001 .Dt SENDMAIL 8 .Os .Sh NAME @@ -62,6 +62,13 @@ and `group' includes `john' in the expansion, then the letter will also be delivered to `john'. .Ss Parameters .Bl -tag -width Fl +.\" XXX - how to prevent Ac from being interpreted as angle close quote? +.It Fl A Ns c +Use submit.cf even if the operation mode does not indicate +an initial mail submission. +.It Fl Am +Use sendmail.cf even if the operation mode indicates +an initial mail submission. .It Fl B Ns Ar type Set the body type to .Ar type . @@ -106,7 +113,10 @@ Initialize the alias database. .It Fl bm Deliver mail in the usual way (default). .It Fl bp -Print a listing of the queue. +Print a listing of the queue(s). +.It Fl bP +Print number of entries in the queue(s); +only available with shared memory support. .It Fl bs Use the .Tn SMTP @@ -213,7 +223,7 @@ Set the name of the protocol used to receive the message. This can be a simple protocol name such as ``UUCP'' or a protocol and hostname, such as ``UUCP:ucbvax''. .It Fl q Ns Bq Ar time -Processed saved messages in the queue at given intervals. +Process saved messages in the queue at given intervals. If .Ar time is omitted, process the queue once. @@ -223,7 +233,7 @@ with .Ql s being seconds, .Ql m -being minutes, +being minutes (default), .Ql h being hours, .Ql d @@ -236,25 +246,51 @@ For example, or .Ql \-q90m would both set the timeout to one hour thirty minutes. -If -.Ar time -is specified, +By default, .Nm sendmail will run in background. This option can be used safely with .Fl bd . -.It Fl qI Ns Ar substr +.It Fl qp Ns Op Ar time +Similar to +.Fl q Ns Ar time , +except that instead of periodically forking a child to process the queue, +sendmail forks a single persistent child for each queue +that alternates between processing the queue and sleeping. +The sleep time is given as the argument; it defaults to 1 second. +The process will always sleep at least 5 seconds if the queue was +empty in the previous queue run. +.It Fl q Ns f +Process saved messages in the queue once and do not fork(), +but run in the foreground. +.It Fl q Ns G Ar name +Process jobs in queue group called +.Ar name +only. +.It Xo Fl q Ns Op Ar ! Ns +.No I Ar substr +.Xc Limit processed jobs to those containing .Ar substr -as a substring of the queue id. -.It Fl qR Ns Ar substr +as a substring of the queue id or not when +.Em ! +is specified. +.It Xo Fl q Ns Op Ar ! Ns +.No R Ar substr +.Xc Limit processed jobs to those containing .Ar substr -as a substring of one of the recipients. -.It Fl qS Ns Ar substr +as a substring of one of the recipients or not when +.Em ! +is specified. +.It Xo Fl q Ns Op Ar ! Ns +.No S Ar substr +.Xc Limit processed jobs to those containing .Ar substr -as a substring of the sender. +as a substring of the sender or not when +.Em ! +is specified. .It Fl R Ar return Set the amount of the message to be returned if the message bounces. @@ -274,17 +310,6 @@ flag. Read message for recipients. To:, Cc:, and Bcc: lines will be scanned for recipient addresses. The Bcc: line will be deleted before transmission. -.It Fl U -Initial (user) submission. This should -.Em always -be set when called from a user agent such as -.Nm Mail -or -.Nm exmh -and -.Em never -be set when called by a network delivery agent such as -.Nm rmail . .It Fl V Ar envid Set the original envelope id. This is propagated across SMTP to servers that support DSNs diff --git a/gnu/usr.sbin/sendmail/sendmail/sendmail.h b/gnu/usr.sbin/sendmail/sendmail/sendmail.h index d953cdfbf4f..22a32fc348d 100644 --- a/gnu/usr.sbin/sendmail/sendmail/sendmail.h +++ b/gnu/usr.sbin/sendmail/sendmail/sendmail.h @@ -15,13 +15,10 @@ */ #ifndef _SENDMAIL_H -#define _SENDMAIL_H 1 +# define _SENDMAIL_H 1 #ifdef _DEFINE # define EXTERN -# ifndef lint -static char SmailId[] = "@(#)$Sendmail: sendmail.h,v 8.517.4.70 2001/08/14 23:08:12 ca Exp $"; -# endif /* ! lint */ #else /* _DEFINE */ # define EXTERN extern #endif /* _DEFINE */ @@ -29,18 +26,9 @@ static char SmailId[] = "@(#)$Sendmail: sendmail.h,v 8.517.4.70 2001/08/14 23:08 #include <unistd.h> -#if SFIO -# include <sfio/stdio.h> -# if defined(SFIO_VERSION) && SFIO_VERSION > 20000000L - ERROR README: SFIO 2000 does not work with sendmail, use SFIO 1999 instead. -# endif /* defined(SFIO_VERSION) && SFIO_VERSION > 20000000L */ -#endif /* SFIO */ - #include <stddef.h> #include <stdlib.h> -#if !SFIO -# include <stdio.h> -#endif /* !SFIO */ +#include <stdio.h> #include <ctype.h> #include <setjmp.h> #include <string.h> @@ -48,11 +36,35 @@ static char SmailId[] = "@(#)$Sendmail: sendmail.h,v 8.517.4.70 2001/08/14 23:08 # ifdef EX_OK # undef EX_OK /* for SVr4.2 SMP */ # endif /* EX_OK */ -#include <sysexits.h> #include "sendmail/sendmail.h" + +/* profiling? */ +#if MONCONTROL +# define SM_PROF(x) moncontrol(x) +#else /* MONCONTROL */ +# define SM_PROF(x) +#endif /* MONCONTROL */ + +#ifdef _DEFINE +# ifndef lint +SM_UNUSED(static char SmailId[]) = "@(#)$Sendmail: sendmail.h,v 8.876 2001/09/04 22:43:05 ca Exp $"; +# endif /* ! lint */ +#endif /* _DEFINE */ + #include "bf.h" #include "timers.h" +#include <sm/exc.h> +#include <sm/heap.h> +#include <sm/debug.h> +#include <sm/rpool.h> +#include <sm/io.h> +#include <sm/path.h> +#include <sm/signal.h> +#include <sm/clock.h> +#include <sm/mbdb.h> +#include <sm/errstring.h> +#include <sm/sysexits.h> #ifdef LOG # include <syslog.h> @@ -91,9 +103,12 @@ static char SmailId[] = "@(#)$Sendmail: sendmail.h,v 8.517.4.70 2001/08/14 23:08 # undef NOERROR /* avoid <sys/streams.h> conflict */ # endif /* NOERROR */ # include <resolv.h> +# else /* NAMED_BIND */ +# undef SM_SET_H_ERRNO +# define SM_SET_H_ERRNO(err) # endif /* NAMED_BIND */ -# ifdef HESIOD +# if HESIOD # include <hesiod.h> # if !defined(HES_ER_OK) || defined(HESIOD_INTERFACES) # define HESIOD_INIT /* support for the new interface */ @@ -101,13 +116,10 @@ static char SmailId[] = "@(#)$Sendmail: sendmail.h,v 8.517.4.70 2001/08/14 23:08 # endif /* HESIOD */ #if STARTTLS -# if !SFIO && !_FFR_TLS_TOREK - ERROR README: STARTTLS requires SFIO -# endif /* !SFIO && !_FFR_TLS_TOREK */ -# if SFIO && _FFR_TLS_TOREK - ERROR README: Can not do both SFIO and _FFR_TLS_TOREK -# endif /* SFIO && _FFR_TLS_TOREK */ # include <openssl/ssl.h> +# if !TLS_NO_RSA +# define RSA_KEYLENGTH 512 +# endif /* !TLS_NO_RSA */ #endif /* STARTTLS */ #if SASL /* include the sasl include files if we have them */ @@ -165,9 +177,26 @@ static char SmailId[] = "@(#)$Sendmail: sendmail.h,v 8.517.4.70 2001/08/14 23:08 #endif /* ! INADDR_NONE */ +/* +** An 'argument class' describes the storage allocation status +** of an object pointed to by an argument to a function. +*/ + +typedef enum +{ + A_HEAP, /* the storage was allocated by malloc, and the + * ownership of the storage is ceded by the caller + * to the called function. */ + A_TEMP, /* The storage is temporary, and is only guaranteed + * to be valid for the duration of the function call. */ + A_PERM /* The storage is 'permanent': this might mean static + * storage, or rpool storage. */ +} ARGCLASS_T; + /* forward references for prototypes */ typedef struct envelope ENVELOPE; typedef struct mailer MAILER; +typedef struct queuegrp QUEUEGRP; /* ** Address structure. @@ -181,7 +210,7 @@ struct address char *q_ruser; /* real user name, or NULL if q_user */ char *q_host; /* host name */ struct mailer *q_mailer; /* mailer to use */ - u_long q_flags; /* status flags, see below */ + unsigned long q_flags; /* status flags, see below */ uid_t q_uid; /* user-id of receiver (if known) */ gid_t q_gid; /* group-id of receiver (if known) */ char *q_home; /* home dir (local mailer only) */ @@ -190,13 +219,20 @@ struct address struct address *q_alias; /* address this results from */ char *q_owner; /* owner of q_alias */ struct address *q_tchain; /* temporary use chain */ +#if PIPELINING + struct address *q_pchain; /* chain for pipelining */ +#endif /* PIPELINING */ + char *q_finalrcpt; /* Final-Recipient: DSN header */ char *q_orcpt; /* ORCPT parameter from RCPT TO: line */ char *q_status; /* status code for DSNs */ char *q_rstatus; /* remote status message for DSNs */ time_t q_statdate; /* date of status messages */ char *q_statmta; /* MTA generating q_rstatus */ short q_state; /* address state, see below */ - short q_specificity; /* how "specific" this address is */ + char *q_signature; /* MX-based sorting value */ + int q_qgrp; /* index into queue groups */ + int q_qdir; /* queue directory inside group */ + char *q_message; /* error message */ }; typedef struct address ADDRESS; @@ -211,12 +247,15 @@ typedef struct address ADDRESS; #define QPINGONSUCCESS 0x00000040 /* give return on successful delivery */ #define QPINGONFAILURE 0x00000080 /* give return on failure */ #define QPINGONDELAY 0x00000100 /* give return on message delay */ -#define QHASNOTIFY 0x00000200 /* propogate notify parameter */ +#define QHASNOTIFY 0x00000200 /* propagate notify parameter */ #define QRELAYED 0x00000400 /* DSN: relayed to non-DSN aware sys */ #define QEXPANDED 0x00000800 /* DSN: undergone list expansion */ #define QDELIVERED 0x00001000 /* DSN: successful final delivery */ #define QDELAYED 0x00002000 /* DSN: message delayed */ #define QALIAS 0x00004000 /* expanded alias */ +#define QBYTRACE 0x00008000 /* DeliverBy: trace */ +#define QBYNDELAY 0x00010000 /* DeliverBy: notify, delay */ +#define QBYNRELAY 0x00020000 /* DeliverBy: notify, relayed */ #define QTHISPASS 0x40000000 /* temp: address set this pass */ #define QRCPTOK 0x80000000 /* recipient() processed address */ @@ -227,41 +266,47 @@ typedef struct address ADDRESS; #define QS_SENT 1 /* good address, delivery complete */ #define QS_BADADDR 2 /* illegal address */ #define QS_QUEUEUP 3 /* save address in queue */ -#define QS_VERIFIED 4 /* verified, but not expanded */ -#define QS_DONTSEND 5 /* don't send to this address */ -#define QS_EXPANDED 6 /* QS_DONTSEND: expanded */ -#define QS_SENDER 7 /* QS_DONTSEND: message sender (MeToo) */ -#define QS_CLONED 8 /* QS_DONTSEND: addr cloned to split envelope */ -#define QS_DISCARDED 9 /* QS_DONTSEND: rcpt discarded (EF_DISCARD) */ -#define QS_REPLACED 10 /* QS_DONTSEND: maplocaluser()/UserDB replaced */ -#define QS_REMOVED 11 /* QS_DONTSEND: removed (removefromlist()) */ -#define QS_DUPLICATE 12 /* QS_DONTSEND: duplicate suppressed */ -#define QS_INCLUDED 13 /* QS_DONTSEND: :include: delivery */ +#define QS_RETRY 4 /* retry delivery for next MX */ +#define QS_VERIFIED 5 /* verified, but not expanded */ + +/* +** Notice: all of the following values are variations of QS_DONTSEND. +** If new states are added, they must be inserted in the proper place! +** See the macro definition of QS_IS_DEAD() down below. +*/ + +#define QS_DONTSEND 6 /* don't send to this address */ +#define QS_EXPANDED 7 /* expanded */ +#define QS_SENDER 8 /* message sender (MeToo) */ +#define QS_CLONED 9 /* addr cloned to split envelope */ +#define QS_DISCARDED 10 /* rcpt discarded (EF_DISCARD) */ +#define QS_REPLACED 11 /* maplocaluser()/UserDB replaced */ +#define QS_REMOVED 12 /* removed (removefromlist()) */ +#define QS_DUPLICATE 13 /* duplicate suppressed */ +#define QS_INCLUDED 14 /* :include: delivery */ /* address state testing primitives */ #define QS_IS_OK(s) ((s) == QS_OK) #define QS_IS_SENT(s) ((s) == QS_SENT) #define QS_IS_BADADDR(s) ((s) == QS_BADADDR) #define QS_IS_QUEUEUP(s) ((s) == QS_QUEUEUP) +#define QS_IS_RETRY(s) ((s) == QS_RETRY) #define QS_IS_VERIFIED(s) ((s) == QS_VERIFIED) #define QS_IS_EXPANDED(s) ((s) == QS_EXPANDED) #define QS_IS_REMOVED(s) ((s) == QS_REMOVED) #define QS_IS_UNDELIVERED(s) ((s) == QS_OK || \ (s) == QS_QUEUEUP || \ + (s) == QS_RETRY || \ (s) == QS_VERIFIED) +#define QS_IS_UNMARKED(s) ((s) == QS_OK || \ + (s) == QS_RETRY) #define QS_IS_SENDABLE(s) ((s) == QS_OK || \ - (s) == QS_QUEUEUP) + (s) == QS_QUEUEUP || \ + (s) == QS_RETRY) #define QS_IS_ATTEMPTED(s) ((s) == QS_QUEUEUP || \ + (s) == QS_RETRY || \ (s) == QS_SENT) -#define QS_IS_DEAD(s) ((s) == QS_DONTSEND || \ - (s) == QS_CLONED || \ - (s) == QS_SENDER || \ - (s) == QS_DISCARDED || \ - (s) == QS_REPLACED || \ - (s) == QS_REMOVED || \ - (s) == QS_DUPLICATE || \ - (s) == QS_INCLUDED || \ - (s) == QS_EXPANDED) +#define QS_IS_DEAD(s) ((s) >= QS_DONTSEND) #define NULLADDR ((ADDRESS *) NULL) @@ -274,18 +319,24 @@ extern char *crackaddr __P((char *)); extern bool emptyaddr __P((ADDRESS *)); extern ADDRESS *getctladdr __P((ADDRESS *)); extern int include __P((char *, bool, ADDRESS *, ADDRESS **, int, ENVELOPE *)); -extern bool invalidaddr __P((char *, char *)); -extern ADDRESS *parseaddr __P((char *, ADDRESS *, int, int, char **, ENVELOPE *)); -extern char **prescan __P((char *, int, char[], int, char **, u_char *)); +extern bool invalidaddr __P((char *, char *, bool)); +extern ADDRESS *parseaddr __P((char *, ADDRESS *, int, int, char **, + ENVELOPE *, bool)); +extern char **prescan __P((char *, int, char[], int, char **, unsigned char *)); extern void printaddr __P((ADDRESS *, bool)); extern ADDRESS *recipient __P((ADDRESS *, ADDRESS **, int, ENVELOPE *)); extern char *remotename __P((char *, MAILER *, int, int *, ENVELOPE *)); -extern int rewrite __P((char **, int, int, ENVELOPE *)); +extern int rewrite __P((char **, int, int, ENVELOPE *, int)); extern bool sameaddr __P((ADDRESS *, ADDRESS *)); extern int sendtolist __P((char *, ADDRESS *, ADDRESS **, int, ENVELOPE *)); +#if MILTER extern int removefromlist __P((char *, ADDRESS **, ENVELOPE *)); +#endif /* MILTER */ extern void setsender __P((char *, ENVELOPE *, char **, int, bool)); +/* macro to simplify the common call to rewrite() */ +#define REWRITE(pvp, rs, env) rewrite(pvp, rs, 0, env, MAXATOM) + /* ** Mailer definition structure. ** Every mailer known to the system is declared in this @@ -323,13 +374,12 @@ struct mailer gid_t m_gid; /* GID to run as */ char *m_defcharset; /* default character set */ time_t m_wait; /* timeout to wait for end */ -#if _FFR_DYNAMIC_TOBUF int m_maxrcpt; /* max recipients per envelope client-side */ -#endif /* _FFR_DYNAMIC_TOBUF */ + short m_qgrp; /* queue group for this mailer */ }; /* bits for m_flags */ -#define M_ESMTP 'a' /* run Extended SMTP protocol */ +#define M_ESMTP 'a' /* run Extended SMTP */ #define M_ALIASABLE 'A' /* user can be LHS of an alias */ #define M_BLANKEND 'b' /* ensure blank line at end of message */ #define M_NOCOMMENT 'c' /* don't include comment part of address */ @@ -370,8 +420,10 @@ struct mailer /* 'x' CF: include Full-Name: */ #define M_XDOT 'X' /* use hidden-dot algorithm */ #define M_LMTP 'z' /* run Local Mail Transport Protocol */ +#define M_DIALDELAY 'Z' /* apply dial delay sleeptime */ #define M_NOMX '0' /* turn off MX lookups */ #define M_NONULLS '1' /* don't send null bytes */ +#define M_FSMTP '2' /* force SMTP (no ESMTP even if offered) */ #define M_EBCDIC '3' /* extend Q-P encoding for EBCDIC */ #define M_TRYRULESET5 '5' /* use ruleset 5 after local aliasing */ #define M_7BITHDRS '6' /* strip headers to 7 bits even in 8 bit path */ @@ -390,6 +442,160 @@ struct mailer /* functions */ extern void initerrmailers __P((void)); extern void makemailer __P((char *)); +extern void makequeue __P((char *, bool)); +extern void runqueueevent __P((int)); + +EXTERN MAILER *FileMailer; /* ptr to *file* mailer */ +EXTERN MAILER *InclMailer; /* ptr to *include* mailer */ +EXTERN MAILER *LocalMailer; /* ptr to local mailer */ +EXTERN MAILER *ProgMailer; /* ptr to program mailer */ +EXTERN MAILER *Mailer[MAXMAILERS + 1]; + +/* +** Queue group definition structure. +** Every queue group known to the system is declared in this structure. +** It defines the basic pathname of the queue group, some flags +** associated with it, and the argument vector to pass to it. +*/ + +struct qpaths_s +{ + char *qp_name; /* name of queue dir, relative path */ + short qp_subdirs; /* use subdirs? */ + short qp_fsysidx; /* file system index of this directory */ +# if SM_CONF_SHM + int qp_idx; /* index into array for queue information */ +# endif /* SM_CONF_SHM */ +}; + +typedef struct qpaths_s QPATHS; + +struct queuegrp +{ + char *qg_name; /* symbolic name of this queue group */ + + /* + ** For now this is the same across all queue groups. + ** Otherwise we have to play around with chdir(). + */ + + char *qg_qdir; /* common component of queue directory */ + short qg_index; /* queue number internally, index in Queue[] */ + int qg_maxqrun; /* max # of jobs in 1 queuerun */ + int qg_numqueues; /* number of queues in this queue */ + + /* + ** qg_queueintvl == 0 denotes that no individual value is used. + ** Whatever accesses this must deal with "<= 0" as + ** "not set, use appropriate default". + */ + + time_t qg_queueintvl; /* interval for queue runs */ + QPATHS *qg_qpaths; /* list of queue directories */ + BITMAP256 qg_flags; /* status flags, see below */ + short qg_nice; /* niceness for queue run */ + int qg_wgrp; /* Assigned to this work group */ + int qg_maxlist; /* max items in work queue for this group */ + int qg_curnum; /* current number of queue for queue runs */ + int qg_maxrcpt; /* max recipients per envelope, 0==no limit */ + +#if 0 + short qg_sortorder; /* how do we sort this queuerun */ + long qg_wkrcptfact; /* multiplier for # recipients -> priority */ + long qg_qfactor; /* slope of queue function */ + bool qg_doqueuerun; /* XXX flag is it time to do a queuerun */ +#endif /* 0 */ +}; + +/* bits for qg_flags (XXX: unused as of now) */ +#define QD_DEFINED ((char) 1) /* queue group has been defined */ +#define QD_FORK 'f' /* fork queue runs */ + +extern void filesys_update __P((void)); +#if _FFR_ANY_FREE_FS +extern bool filesys_free __P((long)); +#endif /* _FFR_ANY_FREE_FS */ + +#if SASL +/* +** SASL +*/ + +/* lines in authinfo file or index into SASL_AI_T */ +# define SASL_WRONG (-1) +# define SASL_USER 0 /* authorization id (user) */ +# define SASL_AUTHID 1 /* authentication id */ +# define SASL_PASSWORD 2 /* password fuer authid */ +# define SASL_DEFREALM 3 /* realm to use */ +# define SASL_MECHLIST 4 /* list of mechanisms to try */ +# define SASL_ID_REALM 5 /* authid@defrealm */ + +/* +** Current mechanism; this is just used to convey information between +** invocation of SASL callback functions. +** It must be last in the list, because it's not allocated by us +** and hence we don't free() it. +*/ +# define SASL_MECH 6 +# define SASL_ENTRIES 7 /* number of entries in array */ + +# define SASL_USER_BIT (1 << SASL_USER) +# define SASL_AUTHID_BIT (1 << SASL_AUTHID) +# define SASL_PASSWORD_BIT (1 << SASL_PASSWORD) +# define SASL_DEFREALM_BIT (1 << SASL_DEFREALM) +# define SASL_MECHLIST_BIT (1 << SASL_MECHLIST) + +/* authenticated? */ +# define SASL_NOT_AUTH 0 /* not authenticated */ +# define SASL_PROC_AUTH 1 /* in process of authenticating */ +# define SASL_IS_AUTH 2 /* authenticated */ + +/* SASL options */ +# define SASL_AUTH_AUTH 0x1000 /* use auth= only if authenticated */ +# define SASL_SEC_MASK 0x0fff /* mask for SASL_SEC_* values: sasl.h */ +# if (SASL_SEC_NOPLAINTEXT & SASL_SEC_MASK) == 0 || \ + (SASL_SEC_NOACTIVE & SASL_SEC_MASK) == 0 || \ + (SASL_SEC_NODICTIONARY & SASL_SEC_MASK) == 0 || \ + (SASL_SEC_FORWARD_SECRECY & SASL_SEC_MASK) == 0 || \ + (SASL_SEC_NOANONYMOUS & SASL_SEC_MASK) == 0 || \ + (SASL_SEC_PASS_CREDENTIALS & SASL_SEC_MASK) == 0 +ERROR: change SASL_SEC_MASK_ notify sendmail.org! +# endif /* SASL_SEC_NOPLAINTEXT & SASL_SEC_MASK) == 0 ... */ +# define MAXOUTLEN 1024 /* length of output buffer */ + +/* functions */ +extern char *intersect __P((char *, char *, SM_RPOOL_T *)); +extern char *iteminlist __P((char *, char *, char *)); +extern int proxy_policy __P((void *, const char *, const char *, const char **, const char **)); +# if SASL > 10515 +extern int safesaslfile __P((void *, char *, int)); +# else /* SASL > 10515 */ +extern int safesaslfile __P((void *, char *)); +# endif /* SASL > 10515 */ +extern int sasl_decode64 __P((const char *, unsigned, char *, unsigned *)); +extern int sasl_encode64 __P((const char *, unsigned, char *, unsigned, unsigned *)); +extern void stop_sasl_client __P((void)); + +/* structure to store authinfo */ +typedef char *SASL_AI_T[SASL_ENTRIES]; + +EXTERN char *AuthMechanisms; /* AUTH mechanisms */ +EXTERN char *SASLInfo; /* file with AUTH info */ +EXTERN int SASLOpts; /* options for SASL */ +EXTERN int MaxSLBits; /* max. encryption bits for SASL */ +#endif /* SASL */ + +/* +** Structure to store macros. +*/ +typedef struct +{ + SM_RPOOL_T *mac_rpool; /* resource pool */ + BITMAP256 mac_allocated; /* storage has been alloc()? */ + char *mac_table[MAXMACROID + 1]; /* macros */ +} MACROS_T; + +EXTERN MACROS_T GlobalMacros; /* ** Information about currently open connections to mailers, or to @@ -400,20 +606,15 @@ extern void makemailer __P((char *)); MCI { - u_long mci_flags; /* flag bits, see below */ + unsigned long mci_flags; /* flag bits, see below */ short mci_errno; /* error number on last connection */ short mci_herrno; /* h_errno from last DNS lookup */ short mci_exitstat; /* exit status from last connection */ short mci_state; /* SMTP state */ int mci_deliveries; /* delivery attempts for connection */ long mci_maxsize; /* max size this server will accept */ -#if SFIO - Sfio_t *mci_in; /* input side of connection */ - Sfio_t *mci_out; /* output side of connection */ -#else /* SFIO */ - FILE *mci_in; /* input side of connection */ - FILE *mci_out; /* output side of connection */ -#endif /* SFIO */ + SM_FILE_T *mci_in; /* input side of connection */ + SM_FILE_T *mci_out; /* output side of connection */ pid_t mci_pid; /* process id of subordinate proc */ char *mci_phase; /* SMTP phase string */ struct mailer *mci_mailer; /* ptr to the mailer for this conn */ @@ -421,9 +622,18 @@ MCI char *mci_status; /* DSN status to be copied to addrs */ char *mci_rstatus; /* SMTP status to be copied to addrs */ time_t mci_lastuse; /* last usage time */ - FILE *mci_statfile; /* long term status file */ + SM_FILE_T *mci_statfile; /* long term status file */ char *mci_heloname; /* name to use as HELO arg */ + long mci_min_by; /* minimum DELIVERBY */ + bool mci_retryrcpt; /* tempfail for at least one rcpt */ + char *mci_tolist; /* list of valid recipients */ + SM_RPOOL_T *mci_rpool; /* resource pool */ +#if PIPELINING + int mci_okrcpts; /* number of valid recipients */ + ADDRESS *mci_nextaddr; /* next address for pipelined status */ +#endif /* PIPELINING */ #if SASL + SASL_AI_T mci_sai; /* authentication info */ bool mci_sasl_auth; /* authenticated? */ int mci_sasl_string_len; char *mci_sasl_string; /* sasl reply string */ @@ -433,12 +643,13 @@ MCI #if STARTTLS SSL *mci_ssl; /* SSL connection */ #endif /* STARTTLS */ + MACROS_T mci_macro; /* macro definitions */ }; /* flag bits */ #define MCIF_VALID 0x00000001 /* this entry is valid */ -#define MCIF_TEMP 0x00000002 /* don't cache this connection */ +/* 0x00000002 unused, was MCIF_TEMP */ #define MCIF_CACHED 0x00000004 /* currently in open cache */ #define MCIF_ESMTP 0x00000008 /* this host speaks ESMTP */ #define MCIF_EXPN 0x00000010 /* EXPN command supported */ @@ -455,6 +666,7 @@ MCI #define MCIF_AUTH 0x00008000 /* AUTH= supported */ #define MCIF_AUTHACT 0x00010000 /* SASL (AUTH) active */ #define MCIF_ENHSTAT 0x00020000 /* ENHANCEDSTATUSCODES supported */ +#define MCIF_PIPELINED 0x00040000 /* PIPELINING supported */ #if STARTTLS #define MCIF_TLS 0x00100000 /* STARTTLS supported */ #define MCIF_TLSACT 0x00200000 /* STARTTLS active */ @@ -462,17 +674,22 @@ MCI #else /* STARTTLS */ #define MCIF_EXTENS (MCIF_EXPN | MCIF_SIZE | MCIF_8BITMIME | MCIF_DSN | MCIF_8BITOK | MCIF_AUTH | MCIF_ENHSTAT) #endif /* STARTTLS */ +#define MCIF_DLVR_BY 0x00400000 /* DELIVERBY */ +#if _FFR_IGNORE_EXT_ON_HELO +# define MCIF_HELO 0x00800000 /* we used HELO: ignore extensions */ +#endif /* _FFR_IGNORE_EXT_ON_HELO */ #define MCIF_ONLY_EHLO 0x10000000 /* use only EHLO in smtpinit */ - /* states */ #define MCIS_CLOSED 0 /* no traffic on this connection */ #define MCIS_OPENING 1 /* sending initial protocol */ #define MCIS_OPEN 2 /* open, initial protocol sent */ -#define MCIS_ACTIVE 3 /* message being sent */ -#define MCIS_QUITING 4 /* running quit protocol */ -#define MCIS_SSD 5 /* service shutting down */ -#define MCIS_ERROR 6 /* I/O error on connection */ +#define MCIS_MAIL 3 /* MAIL command sent */ +#define MCIS_RCPT 4 /* RCPT commands being sent */ +#define MCIS_DATA 5 /* DATA command sent */ +#define MCIS_QUITING 6 /* running quit protocol */ +#define MCIS_SSD 7 /* service shutting down */ +#define MCIS_ERROR 8 /* I/O error on connection */ /* functions */ extern void mci_cache __P((MCI *)); @@ -490,6 +707,10 @@ extern void mci_store_persistent __P((MCI *)); extern int mci_traverse_persistent __P((int (*)(), char *)); extern void mci_unlock_host __P((MCI *)); +EXTERN int MaxMciCache; /* maximum entries in MCI cache */ +EXTERN time_t MciCacheTimeout; /* maximum idle time on connections */ +EXTERN time_t MciInfoTimeout; /* how long 'til we retry down hosts */ + /* ** Header structure. ** This structure is used internally to store header items. @@ -500,8 +721,8 @@ struct header char *h_field; /* the name of the field */ char *h_value; /* the value of that field */ struct header *h_link; /* the next header */ - u_char h_macro; /* include header if macro defined */ - u_long h_flags; /* status bits, see below */ + unsigned char h_macro; /* include header if macro defined */ + unsigned long h_flags; /* status bits, see below */ BITMAP256 h_mflags; /* m_flags bits needed */ }; @@ -515,9 +736,9 @@ typedef struct header HDR; struct hdrinfo { - char *hi_field; /* the name of the field */ - u_long hi_flags; /* status bits, see below */ - char *hi_ruleset; /* validity check ruleset */ + char *hi_field; /* the name of the field */ + unsigned long hi_flags; /* status bits, see below */ + char *hi_ruleset; /* validity check ruleset */ }; extern struct hdrinfo HdrInfo[]; @@ -550,11 +771,11 @@ extern struct hdrinfo HdrInfo[]; #define CHHDR_QUEUE 0x0008 /* header from qf file */ /* functions */ -extern void addheader __P((char *, char *, int, HDR **)); -extern u_long chompheader __P((char *, int, HDR **, ENVELOPE *)); +extern void addheader __P((char *, char *, int, ENVELOPE *)); +extern unsigned long chompheader __P((char *, int, HDR **, ENVELOPE *)); extern void commaize __P((HDR *, char *, bool, MCI *, ENVELOPE *)); -extern HDR *copyheader __P((HDR *)); -extern void eatheader __P((ENVELOPE *, bool)); +extern HDR *copyheader __P((HDR *, SM_RPOOL_T *)); +extern void eatheader __P((ENVELOPE *, bool, bool)); extern char *hvalue __P((char *, HDR *)); extern bool isheader __P((char *)); extern void putfromline __P((MCI *, ENVELOPE *)); @@ -589,7 +810,7 @@ struct envelope HDR *e_header; /* head of header list */ long e_msgpriority; /* adjusted priority of this message */ time_t e_ctime; /* time message appeared in the queue */ - char *e_to; /* the target person */ + char *e_to; /* (list of) target person(s) */ ADDRESS e_from; /* the person it is from */ char *e_sender; /* e_from.q_paddr w comments stripped */ char **e_fromdomain; /* the domain part of the sender */ @@ -601,8 +822,10 @@ struct envelope ** to unsigned. We don't use unsigned and == ULONG_MAX because ** some libc's don't have strtoul(), see mail_esmtp_args(). */ + long e_msgsize; /* size of the message in bytes */ - long e_flags; /* flags, see below */ + char *e_msgid; /* message id (for logging) */ + unsigned long e_flags; /* flags, see below */ int e_nrcpts; /* number of recipients */ short e_class; /* msg class (priority, junk, etc.) */ short e_hopcount; /* number of times processed */ @@ -617,13 +840,22 @@ struct envelope ENVELOPE *e_parent; /* the message this one encloses */ ENVELOPE *e_sibling; /* the next envelope of interest */ char *e_bodytype; /* type of message body */ - FILE *e_dfp; /* data file */ + SM_FILE_T *e_dfp; /* data file */ char *e_id; /* code for this entry in queue */ - int e_queuedir; /* index into queue directories */ - FILE *e_xfp; /* transcript file */ - FILE *e_lockfp; /* the lock file for this message */ - char *e_message; /* error message */ - char *e_statmsg; /* stat msg (changes per delivery) */ + int e_qgrp; /* queue group (index into queues) */ + int e_qdir; /* index into queue directories */ + int e_dfqgrp; /* data file queue group index */ + int e_dfqdir; /* data file queue directory index */ + int e_xfqgrp; /* queue group (index into queues) */ + int e_xfqdir; /* index into queue directories (xf) */ + SM_FILE_T *e_xfp; /* transcript file */ + SM_FILE_T *e_lockfp; /* the lock file for this message */ + char *e_message; /* error message; readonly; NULL, or + * static storage, or allocated from + * e_rpool */ + char *e_statmsg; /* stat msg (changes per delivery). + * readonly. NULL or allocated from + * e_rpool. */ char *e_msgboundary; /* MIME-style message part boundary */ char *e_origrcpt; /* original recipient (one only) */ char *e_envid; /* envelope id from MAIL FROM: line */ @@ -632,14 +864,18 @@ struct envelope int e_ntries; /* number of delivery attempts */ dev_t e_dfdev; /* df file's device, for crash recov */ ino_t e_dfino; /* df file's ino, for crash recovery */ - char *e_macro[MAXMACROID + 1]; /* macro definitions */ - char *e_if_macros[2]; /* HACK: incoming interface info */ - char *e_auth_param; + MACROS_T e_macro; /* macro definitions */ + MCI *e_mci; /* connection info */ + char *e_auth_param; /* readonly; NULL or static storage or + * allocated from e_rpool */ TIMERS e_timers; /* per job timers */ #if _FFR_QUEUEDELAY int e_queuealg; /* algorithm for queue delay */ time_t e_queuedelay; /* current delay */ #endif /* _FFR_QUEUEDELAY */ + long e_deliver_by; /* deliver by */ + int e_dlvr_flag; /* deliver by flag */ + SM_RPOOL_T *e_rpool; /* resource pool for this envelope */ }; /* values for e_flags */ @@ -669,14 +905,23 @@ struct envelope #define EF_DONT_MIME 0x0800000L /* never MIME this message */ #define EF_DISCARD 0x1000000L /* discard the message */ #define EF_TOOBIG 0x2000000L /* message is too big */ +#define EF_SPLIT 0x4000000L /* envelope has been split */ +#define EF_UNSAFE 0x8000000L /* unsafe: read from untrusted source */ + +#define DLVR_NOTIFY 0x01 +#define DLVR_RETURN 0x02 +#define DLVR_TRACE 0x10 +#define IS_DLVR_NOTIFY(e) (((e)->e_dlvr_flag & DLVR_NOTIFY) != 0) +#define IS_DLVR_RETURN(e) (((e)->e_dlvr_flag & DLVR_RETURN) != 0) +#define IS_DLVR_TRACE(e) (((e)->e_dlvr_flag & DLVR_TRACE) != 0) +#define IS_DLVR_BY(e) ((e)->e_dlvr_flag != 0) -/* values for e_if_macros */ -#define EIF_ADDR 0 /* ${if_addr} */ +extern ENVELOPE BlankEnvelope; /* functions */ -extern void clearenvelope __P((ENVELOPE *, bool)); -extern void dropenvelope __P((ENVELOPE *, bool)); -extern ENVELOPE *newenvelope __P((ENVELOPE *, ENVELOPE *)); +extern void clearenvelope __P((ENVELOPE *, bool, SM_RPOOL_T *)); +extern void dropenvelope __P((ENVELOPE *, bool, bool)); +extern ENVELOPE *newenvelope __P((ENVELOPE *, ENVELOPE *, SM_RPOOL_T *)); extern void printenvflags __P((ENVELOPE *)); extern void putbody __P((MCI *, ENVELOPE *, char *)); extern void putheader __P((MCI *, HDR *, ENVELOPE *, int)); @@ -711,6 +956,9 @@ struct priority int pri_val; /* internal value for same */ }; +EXTERN int NumPriorities; /* pointer into Priorities */ +EXTERN struct priority Priorities[MAXPRIORITIES]; + /* ** Rewrite rules. */ @@ -732,35 +980,35 @@ struct rewrite */ /* left hand side items */ -#define MATCHZANY ((u_char)0220) /* match zero or more tokens */ -#define MATCHANY ((u_char)0221) /* match one or more tokens */ -#define MATCHONE ((u_char)0222) /* match exactly one token */ -#define MATCHCLASS ((u_char)0223) /* match one token in a class */ -#define MATCHNCLASS ((u_char)0224) /* match anything not in class */ -#define MATCHREPL ((u_char)0225) /* replacement on RHS for above */ +#define MATCHZANY ((unsigned char)0220) /* match zero or more tokens */ +#define MATCHANY ((unsigned char)0221) /* match one or more tokens */ +#define MATCHONE ((unsigned char)0222) /* match exactly one token */ +#define MATCHCLASS ((unsigned char)0223) /* match one token in a class */ +#define MATCHNCLASS ((unsigned char)0224) /* match anything not in class */ +#define MATCHREPL ((unsigned char)0225) /* replacement on RHS for above */ /* right hand side items */ -#define CANONNET ((u_char)0226) /* canonical net, next token */ -#define CANONHOST ((u_char)0227) /* canonical host, next token */ -#define CANONUSER ((u_char)0230) /* canonical user, next N tokens */ -#define CALLSUBR ((u_char)0231) /* call another rewriting set */ +#define CANONNET ((unsigned char)0226) /* canonical net, next token */ +#define CANONHOST ((unsigned char)0227) /* canonical host, next token */ +#define CANONUSER ((unsigned char)0230) /* canonical user, next N tokens */ +#define CALLSUBR ((unsigned char)0231) /* call another rewriting set */ /* conditionals in macros */ -#define CONDIF ((u_char)0232) /* conditional if-then */ -#define CONDELSE ((u_char)0233) /* conditional else */ -#define CONDFI ((u_char)0234) /* conditional fi */ +#define CONDIF ((unsigned char)0232) /* conditional if-then */ +#define CONDELSE ((unsigned char)0233) /* conditional else */ +#define CONDFI ((unsigned char)0234) /* conditional fi */ /* bracket characters for host name lookup */ -#define HOSTBEGIN ((u_char)0235) /* hostname lookup begin */ -#define HOSTEND ((u_char)0236) /* hostname lookup end */ +#define HOSTBEGIN ((unsigned char)0235) /* hostname lookup begin */ +#define HOSTEND ((unsigned char)0236) /* hostname lookup end */ /* bracket characters for generalized lookup */ -#define LOOKUPBEGIN ((u_char)0205) /* generalized lookup begin */ -#define LOOKUPEND ((u_char)0206) /* generalized lookup end */ +#define LOOKUPBEGIN ((unsigned char)0205) /* generalized lookup begin */ +#define LOOKUPEND ((unsigned char)0206) /* generalized lookup end */ /* macro substitution character */ -#define MACROEXPAND ((u_char)0201) /* macro expansion */ -#define MACRODEXPAND ((u_char)0202) /* deferred macro expansion */ +#define MACROEXPAND ((unsigned char)0201) /* macro expansion */ +#define MACRODEXPAND ((unsigned char)0202) /* deferred macro expansion */ /* to make the code clearer */ #define MATCHZERO CANONHOST @@ -770,20 +1018,44 @@ struct rewrite /* external <==> internal mapping table */ struct metamac { - char metaname; /* external code (after $) */ - u_char metaval; /* internal code (as above) */ + char metaname; /* external code (after $) */ + unsigned char metaval; /* internal code (as above) */ }; /* values for macros with external names only */ #define MID_OPMODE 0202 /* operation mode */ /* functions */ -extern void define __P((int, char *, ENVELOPE *)); +#if SM_HEAP_CHECK +extern void +macdefine_tagged __P(( + MACROS_T *_mac, + ARGCLASS_T _vclass, + int _id, + char *_value, + char *_file, + int _line, + int _group)); +# define macdefine(mac,c,id,v) \ + macdefine_tagged(mac,c,id,v,__FILE__,__LINE__,sm_heap_group()) +#else /* SM_HEAP_CHECK */ +extern void +macdefine __P(( + MACROS_T *_mac, + ARGCLASS_T _vclass, + int _id, + char *_value)); +# define macdefine_tagged(mac,c,id,v,file,line,grp) macdefine(mac,c,id,v) +#endif /* SM_HEAP_CHECK */ +extern void macset __P((MACROS_T *, int, char *)); +#define macget(mac, i) (mac)->mac_table[i] extern void expand __P((char *, char *, size_t, ENVELOPE *)); -extern int macid __P((char *, char **)); +extern int macid_parse __P((char *, char **)); +#define macid(name) macid_parse(name, NULL) extern char *macname __P((int)); extern char *macvalue __P((int, ENVELOPE *)); -extern int rscheck __P((char *, char *, char *, ENVELOPE *, bool, bool, int, char *)); +extern int rscheck __P((char *, char *, char *, ENVELOPE *, bool, bool, int, char *, char *)); +extern int rscap __P((char *, char *, char *, ENVELOPE *, char ***, char *, int)); extern void setclass __P((int, char *)); extern int strtorwset __P((char *, char **, int)); extern void translate_dollars __P((char *)); @@ -808,14 +1080,27 @@ NAMECANON short nc_stat; /* cached exit status code */ short nc_flags; /* flag bits */ char *nc_cname; /* the canonical name */ + time_t nc_exp; /* entry expires at */ }; /* values for nc_flags */ #define NCF_VALID 0x0001 /* entry valid */ +/* hostsignature structure */ + +struct hostsig_t +{ + char *hs_sig; /* hostsignature */ + time_t hs_exp; /* entry expires at */ +}; + +typedef struct hostsig_t HOSTSIG_T; + /* functions */ -extern bool getcanonname __P((char *, int, bool)); -extern int getmxrr __P((char *, char **, u_short *, bool, int *)); +extern bool getcanonname __P((char *, int, bool, int *)); +extern int getmxrr __P((char *, char **, unsigned short *, bool, int *, bool, int *)); +extern char *hostsignature __P((MAILER *, char *)); +extern int getfallbackmxrr __P((char *)); /* ** Mapping functions @@ -836,6 +1121,7 @@ extern int getmxrr __P((char *, char **, u_short *, bool, int *)); MAP { MAPCLASS *map_class; /* the class of this map */ + MAPCLASS *map_orgclass; /* the original class of this map */ char *map_mname; /* name of this map */ long map_mflags; /* flags, see below */ char *map_file; /* the (nominal) filename */ @@ -843,8 +1129,8 @@ MAP ARBPTR_T map_db2; /* an "extra" database pointer */ char *map_keycolnm; /* key column name */ char *map_valcolnm; /* value column name */ - u_char map_keycolno; /* key column number */ - u_char map_valcolno; /* value column number */ + unsigned char map_keycolno; /* key column number */ + unsigned char map_valcolno; /* value column number */ char map_coldelim; /* column delimiter */ char map_spacesub; /* spacesub */ char *map_app; /* to append to successful matches */ @@ -852,6 +1138,8 @@ MAP char *map_domain; /* the (nominal) NIS domain */ char *map_rebuild; /* program to run to do auto-rebuild */ time_t map_mtime; /* last database modification time */ + time_t map_timeout; /* timeout for map accesses */ + int map_retry; /* # of retries for map accesses */ pid_t map_pid; /* PID of process which opened map */ int map_lockfd; /* auxiliary lock file descriptor */ short map_specificity; /* specificity of aliases */ @@ -883,7 +1171,9 @@ MAP #define MF_DEFER 0x00080000 /* don't lookup map in defer mode */ #define MF_SINGLEMATCH 0x00100000 /* successful only if match one key */ #define MF_NOREWRITE 0x00200000 /* don't rewrite result, return as-is */ -#define MF_CLOSING 0x00400000 /* map is being closed */ +#define MF_FILECLASS 0x00400000 /* this is a file class map */ +#define MF_OPENBOGUS 0x00800000 /* open failed, don't call map_close */ +#define MF_CLOSING 0x01000000 /* map is being closed */ #define DYNOPENMAP(map) if (!bitset(MF_OPEN, (map)->map_mflags)) \ { \ @@ -897,6 +1187,15 @@ MAP #define MA_UNAVAIL 1 /* member map is not available */ #define MA_TRYAGAIN 2 /* member map returns temp failure */ +/* macros to handle MapTempFail */ +#define BIT_IS_MTP 0x01 /* temp.failure occurred */ +#define BIT_ASK_MTP 0x02 /* do we care about MapTempFail? */ +#define RESET_MAPTEMPFAIL MapTempFail = 0 +#define INIT_MAPTEMPFAIL MapTempFail = BIT_ASK_MTP +#define SET_MAPTEMPFAIL MapTempFail |= BIT_IS_MTP +#define IS_MAPTEMPFAIL bitset(BIT_IS_MTP, MapTempFail) +#define ASK_MAPTEMPFAIL bitset(BIT_ASK_MTP, MapTempFail) + /* ** The class of a map -- essentially the functions to call */ @@ -923,9 +1222,10 @@ MAPCLASS #define MCF_ALIASONLY 0x0002 /* usable only for aliases */ #define MCF_REBUILDABLE 0x0004 /* can rebuild alias files */ #define MCF_OPTFILE 0x0008 /* file name is optional */ +#define MCF_NOTPERSIST 0x0010 /* don't keep map open all the time */ /* functions */ -extern void closemaps __P((void)); +extern void closemaps __P((bool)); extern bool impl_map_open __P((MAP *, int)); extern void initmaps __P((void)); extern MAP *makemapentry __P((char *)); @@ -938,47 +1238,13 @@ extern bool openmap __P((MAP *)); #if USERDB extern void _udbx_close __P((void)); extern int udbexpand __P((ADDRESS *, ADDRESS **, int, ENVELOPE *)); -extern char *udbsender __P((char *)); +extern char *udbsender __P((char *, SM_RPOOL_T *)); #endif /* USERDB */ + /* ** LDAP related items */ -#ifdef LDAPMAP -struct ldapmap_struct -{ - /* needed for ldap_open or ldap_init */ - char *ldap_host; - int ldap_port; - - /* options set in ld struct before ldap_bind_s */ - int ldap_deref; - time_t ldap_timelimit; - int ldap_sizelimit; - int ldap_options; - - /* args for ldap_bind_s */ - LDAP *ldap_ld; - char *ldap_binddn; - char *ldap_secret; - int ldap_method; - - /* args for ldap_search */ - char *ldap_base; - int ldap_scope; - char *ldap_filter; - char *ldap_attr[LDAPMAP_MAX_ATTR + 1]; - bool ldap_attrsonly; - - /* args for ldap_result */ - struct timeval ldap_timeout; - LDAPMessage *ldap_res; - - /* Linked list of maps sharing the same LDAP binding */ - MAP *ldap_next; -}; - -typedef struct ldapmap_struct LDAPMAP_STRUCT; - +#if LDAPMAP /* struct defining LDAP Auth Methods */ struct lamvalues { @@ -1009,31 +1275,25 @@ extern void ldapmap_set_defaults __P((char *)); ** PH related items */ -#ifdef PH_MAP +#if PH_MAP + +# include <phclient.h> + struct ph_map_struct { - char *ph_servers; /* list of ph servers */ - char *ph_field_list; /* list of fields to search for match */ - FILE *ph_to_server; - FILE *ph_from_server; - int ph_sockfd; - time_t ph_timeout; + char *ph_servers; /* list of ph servers */ + char *ph_field_list; /* list of fields to search for match */ + PH *ph; /* PH server handle */ + int ph_fastclose; /* send "quit" command on close */ + time_t ph_timeout; /* timeout interval */ }; typedef struct ph_map_struct PH_MAP_STRUCT; -# define DEFAULT_PH_MAP_FIELDS "alias callsign name spacedname" #endif /* PH_MAP */ /* ** Process List (proclist) */ -struct procs -{ - pid_t proc_pid; - char *proc_task; - int proc_type; -}; - #define NO_PID ((pid_t) 0) #ifndef PROC_LIST_SEG # define PROC_LIST_SEG 32 /* number of pids to alloc at a time */ @@ -1049,12 +1309,13 @@ struct procs #define PROC_CONTROL_CHILD 5 /* functions */ -extern void proc_list_add __P((pid_t, char *, int)); +extern void proc_list_add __P((pid_t, char *, int, int, int)); extern void proc_list_clear __P((void)); -extern void proc_list_display __P((FILE *)); -extern int proc_list_drop __P((pid_t)); +extern void proc_list_display __P((SM_FILE_T *, char *)); +extern int proc_list_drop __P((pid_t, int *, int *)); extern void proc_list_probe __P((void)); extern void proc_list_set __P((pid_t, char *)); +extern void proc_list_signal __P((int, int)); /* ** Symbol table definitions @@ -1074,19 +1335,20 @@ struct symtab char *sv_alias; /* alias */ MAPCLASS sv_mapclass; /* mapping function class */ MAP sv_map; /* mapping function */ - char *sv_hostsig; /* host signature */ + HOSTSIG_T sv_hostsig; /* host signature */ MCI sv_mci; /* mailer connection info */ NAMECANON sv_namecanon; /* canonical name cache */ int sv_macro; /* macro name => id mapping */ int sv_ruleset; /* ruleset index */ struct hdrinfo sv_header; /* header metainfo */ char *sv_service[MAXMAPSTACK]; /* service switch */ -#ifdef LDAPMAP +#if LDAPMAP MAP *sv_lmap; /* Maps for LDAP connection */ #endif /* LDAPMAP */ -#if _FFR_MILTER +#if MILTER struct milter *sv_milter; /* milter filter name */ -#endif /* _FFR_MILTER */ +#endif /* MILTER */ + QUEUEGRP *sv_queue; /* pointer to queue */ } s_value; }; @@ -1106,12 +1368,15 @@ typedef struct symtab STAB; #define ST_RULESET 10 /* ruleset index */ #define ST_SERVICE 11 /* service switch entry */ #define ST_HEADER 12 /* special header flags */ -#ifdef LDAPMAP +#if LDAPMAP # define ST_LMAP 13 /* List head of maps for LDAP connection */ #endif /* LDAPMAP */ -#if _FFR_MILTER +#if MILTER # define ST_MILTER 14 /* milter filter */ -#endif /* _FFR_MILTER */ +#endif /* MILTER */ +#define ST_QUEUE 15 /* a queue entry */ + +/* This entry must be last */ #define ST_MCI 16 /* mailer connection info (offset) */ #define s_class s_value.sv_class @@ -1127,12 +1392,13 @@ typedef struct symtab STAB; #define s_ruleset s_value.sv_ruleset #define s_service s_value.sv_service #define s_header s_value.sv_header -#ifdef LDAPMAP +#if LDAPMAP # define s_lmap s_value.sv_lmap #endif /* LDAPMAP */ -#if _FFR_MILTER +#if MILTER # define s_milter s_value.sv_milter -#endif /* _FFR_MILTER */ +#endif /* MILTER */ +#define s_quegrp s_value.sv_queue /* opcodes to stab */ #define ST_FIND 0 /* find entry */ @@ -1143,33 +1409,6 @@ extern STAB *stab __P((char *, int, int)); extern void stabapply __P((void (*)(STAB *, int), int)); /* -** STRUCT EVENT -- event queue. -** -** Maintained in sorted order. -** -** We store the pid of the process that set this event to insure -** that when we fork we will not take events intended for the parent. -*/ - -struct event -{ - time_t ev_time; /* time of the function call */ - void (*ev_func)__P((int)); - /* function to call */ - int ev_arg; /* argument to ev_func */ - pid_t ev_pid; /* pid that set this event */ - struct event *ev_link; /* link to next item */ -}; - -typedef struct event EVENT; - -/* functions */ -extern void clrevent __P((EVENT *)); -extern void clear_events __P((void)); -extern EVENT *setevent __P((time_t, void(*)(), int)); -extern EVENT *sigsafe_setevent __P((time_t, void(*)(), int)); - -/* ** Operation, send, error, and MIME modes ** ** The operation mode describes the basic operation of sendmail. @@ -1177,7 +1416,7 @@ extern EVENT *sigsafe_setevent __P((time_t, void(*)(), int)); ** default. ** ** The send mode tells how to send mail. It can be set in the -** configuration file. It's setting determines how quickly the +** configuration file. Its setting determines how quickly the ** mail will be delivered versus the load on your system. If the ** -v (verbose) flag is given, it will be forced to SM_DELIVER ** mode. @@ -1194,11 +1433,14 @@ extern EVENT *sigsafe_setevent __P((time_t, void(*)(), int)); #define MD_TEST 't' /* test mode: resolve addrs only */ #define MD_INITALIAS 'i' /* initialize alias database */ #define MD_PRINT 'p' /* print the queue */ +#define MD_PRINTNQE 'P' /* print number of entries in queue */ #define MD_FREEZE 'z' /* freeze the configuration file */ #define MD_HOSTSTAT 'h' /* print persistent host stat info */ #define MD_PURGESTAT 'H' /* purge persistent host stat info */ #define MD_QUEUERUN 'q' /* queue run */ +/* Note: see also include/sendmail/pathnames.h: GET_CLIENT_CF */ + /* values for e_sendmode -- send modes */ #define SM_DELIVER 'i' /* interactive delivery */ #define SM_FORK 'b' /* deliver in background */ @@ -1206,6 +1448,7 @@ extern EVENT *sigsafe_setevent __P((time_t, void(*)(), int)); #define SM_DEFER 'd' /* defer map lookups as well as queue */ #define SM_VERIFY 'v' /* verify only (used internally) */ +#define WILL_BE_QUEUED(m) ((m) == SM_QUEUE || (m) == SM_DEFER) /* used only as a parameter to sendall */ #define SM_DEFAULT '\0' /* unspecified, use SendMode */ @@ -1246,30 +1489,33 @@ extern void set_delivery_mode __P((int, ENVELOPE *)); ** These are bit values for the PrivacyFlags word. */ -#define PRIV_PUBLIC 0 /* what have I got to hide? */ -#define PRIV_NEEDMAILHELO 0x0001 /* insist on HELO for MAIL, at least */ -#define PRIV_NEEDEXPNHELO 0x0002 /* insist on HELO for EXPN */ -#define PRIV_NEEDVRFYHELO 0x0004 /* insist on HELO for VRFY */ -#define PRIV_NOEXPN 0x0008 /* disallow EXPN command entirely */ -#define PRIV_NOVRFY 0x0010 /* disallow VRFY command entirely */ -#define PRIV_AUTHWARNINGS 0x0020 /* flag possible authorization probs */ -#define PRIV_NORECEIPTS 0x0040 /* disallow return receipts */ -#define PRIV_NOVERB 0x0100 /* disallow VERB command entirely */ -#define PRIV_RESTRICTMAILQ 0x1000 /* restrict mailq command */ -#define PRIV_RESTRICTQRUN 0x2000 /* restrict queue run */ -#define PRIV_NOETRN 0x4000 /* disallow ETRN command entirely */ -#define PRIV_NOBODYRETN 0x8000 /* do not return bodies on bounces */ +#define PRIV_PUBLIC 0 /* what have I got to hide? */ +#define PRIV_NEEDMAILHELO 0x00000001 /* insist on HELO for MAIL */ +#define PRIV_NEEDEXPNHELO 0x00000002 /* insist on HELO for EXPN */ +#define PRIV_NEEDVRFYHELO 0x00000004 /* insist on HELO for VRFY */ +#define PRIV_NOEXPN 0x00000008 /* disallow EXPN command */ +#define PRIV_NOVRFY 0x00000010 /* disallow VRFY command */ +#define PRIV_AUTHWARNINGS 0x00000020 /* flag possible auth probs */ +#define PRIV_NOVERB 0x00000040 /* disallow VERB command */ +#define PRIV_RESTRICTMAILQ 0x00010000 /* restrict mailq command */ +#define PRIV_RESTRICTQRUN 0x00020000 /* restrict queue run */ +#define PRIV_RESTRICTEXPAND 0x00040000 /* restrict alias/forward expansion */ +#define PRIV_NOETRN 0x00080000 /* disallow ETRN command */ +#define PRIV_NOBODYRETN 0x00100000 /* do not return bodies on bounces */ +#define PRIV_NORECEIPTS 0x00200000 /* disallow return receipts */ /* don't give no info, anyway, anyhow */ -#define PRIV_GOAWAY (0x0fff & ~PRIV_NORECEIPTS) +#define PRIV_GOAWAY 0x0000ffff /* struct defining such things */ struct prival { - char *pv_name; /* name of privacy flag */ - u_short pv_flag; /* numeric level */ + char *pv_name; /* name of privacy flag */ + unsigned long pv_flag; /* numeric level */ }; +EXTERN unsigned long PrivacyFlags; /* privacy flags */ + /* ** Flags passed to remotename, parseaddr, allocaddr, and buildaddr. @@ -1344,35 +1590,32 @@ union bigsockaddr extern char *anynet_ntoa __P((SOCKADDR *)); # if NETINET6 extern char *anynet_ntop __P((struct in6_addr *, char *, size_t)); +extern int anynet_pton __P((int, const char *, void *)); # endif /* NETINET6 */ extern char *hostnamebyanyaddr __P((SOCKADDR *)); -# if DAEMON extern char *validate_connection __P((SOCKADDR *, char *, ENVELOPE *)); -# endif /* DAEMON */ #endif /* NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25 */ -#if _FFR_MILTER +#if MILTER /* ** Mail Filters (milter) */ -#include <libmilter/milter.h> +# define SMFTO_WRITE 0 /* Timeout for sending information */ +# define SMFTO_READ 1 /* Timeout waiting for a response */ +# define SMFTO_EOM 2 /* Timeout for ACK/NAK to EOM */ +# define SMFTO_CONNECT 3 /* Timeout for connect() */ -#define SMFTO_WRITE 0 /* Timeout for sending information */ -#define SMFTO_READ 1 /* Timeout waiting for a response */ -#define SMFTO_EOM 2 /* Timeout for ACK/NAK to EOM */ -#define SMFTO_CONNECT 3 /* Timeout for connect() */ - -#define SMFTO_NUM_TO 4 /* Total number of timeouts */ +# define SMFTO_NUM_TO 4 /* Total number of timeouts */ struct milter { char *mf_name; /* filter name */ BITMAP256 mf_flags; /* MTA flags */ - u_long mf_fvers; /* filter version */ - u_long mf_fflags; /* filter flags */ - u_long mf_pflags; /* protocol flags */ + unsigned long mf_fvers; /* filter version */ + unsigned long mf_fflags; /* filter flags */ + unsigned long mf_pflags; /* protocol flags */ char *mf_conn; /* connection info */ int mf_sock; /* connected socket */ char mf_state; /* state of filter */ @@ -1392,12 +1635,22 @@ struct milter # define SMFS_ERROR 'E' /* error state */ # define SMFS_READY 'R' /* ready for action */ -/* 32-bit type used by milter */ -typedef SM_INT32 mi_int32; - EXTERN struct milter *InputFilters[MAXFILTERS]; EXTERN char *InputFilterList; -#endif /* _FFR_MILTER */ +EXTERN int MilterLogLevel; + +# if _FFR_MILTER_PERDAEMON +/* functions */ +extern void setup_daemon_milters __P(()); +# endif /* _FFR_MILTER_PERDAEMON */ +#endif /* MILTER */ + +/* +** 32-bit type used by milter +** (needed by libmilter even if MILTER isn't defined) +*/ + +typedef SM_INT32 mi_int32; /* ** Vendor codes @@ -1441,55 +1694,29 @@ struct termescape ** Additional definitions */ -/* d_flags, see daemon.c */ -/* general rule: lower case: required, upper case: No */ +/* +** d_flags, see daemon.c +** general rule: lower case: required, upper case: No +*/ + #define D_AUTHREQ 'a' /* authentication required */ #define D_BINDIF 'b' /* use if_addr for outgoing connection */ #define D_CANONREQ 'c' /* canonification required (cf) */ #define D_IFNHELO 'h' /* use if name for HELO */ #define D_FQMAIL 'f' /* fq sender address required (cf) */ -#if _FFR_TLS_CLT1 -#define D_CLTNOTLS 'S' /* don't use STARTTLS in client */ -#endif /* _FFR_TLS_CLT1 */ #define D_FQRCPT 'r' /* fq recipient address required (cf) */ +#if _FFR_SMTP_SSL +# define D_SMTPS 's' /* SMTP over SSL (smtps) */ +#endif /* _FFR_SMTP_SSL */ #define D_UNQUALOK 'u' /* unqualified address is ok (cf) */ +#define D_NOAUTH 'A' /* no AUTH */ #define D_NOCANON 'C' /* no canonification (cf) */ #define D_NOETRN 'E' /* no ETRN (MSA) */ +#define D_NOTLS 'S' /* don't use STARTTLS */ #define D_ETRNONLY ((char)0x01) /* allow only ETRN (disk low) */ #define D_OPTIONAL 'O' /* optional socket */ #define D_DISABLE ((char)0x02) /* optional socket disabled */ - -/* Flags for submitmode */ -#define SUBMIT_UNKNOWN 0x0000 /* unknown agent type */ -#define SUBMIT_MTA 0x0001 /* act like a message transfer agent */ -#define SUBMIT_MSA 0x0002 /* act like a message submission agent */ - -#if SASL -/* -** SASL -*/ - -/* authenticated? */ -# define SASL_NOT_AUTH 0 /* not authenticated */ -# define SASL_PROC_AUTH 1 /* in process of authenticating */ -# define SASL_IS_AUTH 2 /* authenticated */ - -/* SASL options */ -# define SASL_AUTH_AUTH 0x1000 /* use auth= only if authenticated */ -# if _FFR_SASL_OPTS -# define SASL_SEC_MASK 0x0fff /* mask for SASL_SEC_* values: sasl.h */ -# if (SASL_SEC_NOPLAINTEXT & SASL_SEC_MASK) == 0 || \ - (SASL_SEC_NOACTIVE & SASL_SEC_MASK) == 0 || \ - (SASL_SEC_NODICTIONARY & SASL_SEC_MASK) == 0 || \ - (SASL_SEC_FORWARD_SECRECY & SASL_SEC_MASK) == 0 || \ - (SASL_SEC_NOANONYMOUS & SASL_SEC_MASK) == 0 || \ - (SASL_SEC_PASS_CREDENTIALS & SASL_SEC_MASK) == 0 -ERROR: change SASL_SEC_MASK_ notify sendmail.org! -# endif -# endif /* _FFR_SASL_OPTS */ - -# define MAXOUTLEN 1024 /* length of output buffer */ -#endif /* SASL */ +#define D_ISSET ((char)0x03) /* this client struct is set */ #if STARTTLS /* @@ -1519,22 +1746,51 @@ ERROR: change SASL_SEC_MASK_ notify sendmail.org! #define TLS_I_DH512 0x00040000 /* generate 512bit DH param */ #define TLS_I_DH1024 0x00080000 /* generate 1024bit DH param */ #define TLS_I_DH2048 0x00100000 /* generate 2048bit DH param */ +#define TLS_I_NO_VRFY 0x00200000 /* do not require authentication */ +#define TLS_I_KEY_OUNR 0x00400000 /* KEY must be o unreadable */ + +/* require server cert */ +#define TLS_I_SRV_CERT (TLS_I_CERT_EX | TLS_I_KEY_EX | \ + TLS_I_KEY_UNR | TLS_I_KEY_OUNR | \ + TLS_I_CERTP_EX | TLS_I_CERTF_EX | \ + TLS_I_USE_KEY | TLS_I_USE_CERT) /* server requirements */ -#define TLS_I_SRV (TLS_I_CERT_EX | TLS_I_KEY_EX | TLS_I_KEY_UNR | \ - TLS_I_CERTP_EX | TLS_I_CERTF_EX | TLS_I_RSA_TMP | \ - TLS_I_USE_KEY | TLS_I_USE_CERT | TLS_I_VRFY_PATH | \ - TLS_I_VRFY_LOC | TLS_I_TRY_DH | \ - TLS_I_DH512) +#define TLS_I_SRV (TLS_I_SRV_CERT | TLS_I_RSA_TMP | TLS_I_VRFY_PATH | \ + TLS_I_VRFY_LOC | TLS_I_TRY_DH | TLS_I_DH512) /* client requirements */ -#define TLS_I_CLT (TLS_I_KEY_UNR) +#define TLS_I_CLT (TLS_I_KEY_UNR | TLS_I_KEY_OUNR) #define TLS_AUTH_OK 0 #define TLS_AUTH_NO 1 #define TLS_AUTH_FAIL (-1) -#endif /* STARTTLS */ +/* functions */ +extern bool init_tls_library __P((void)); +extern bool inittls __P((SSL_CTX **, unsigned long, bool, char *, char *, char *, char *, char *)); +extern bool initclttls __P((bool)); +extern void setclttls __P((bool)); +extern bool initsrvtls __P((bool)); +extern int tls_get_info __P((SSL *, bool, char *, MACROS_T *, bool)); +extern int endtls __P((SSL *, char *)); +extern void tlslogerr __P((char *)); + + +EXTERN char *CACERTpath; /* path to CA certificates (dir. with hashes) */ +EXTERN char *CACERTfile; /* file with CA certificate */ +EXTERN char *CltCERTfile; /* file with client certificate */ +EXTERN char *Cltkeyfile; /* file with client private key */ +# if _FFR_TLS_1 +EXTERN char *CipherList; /* list of ciphers */ +EXTERN char *DHParams5; /* file with DH parameters (512) */ +# endif /* _FFR_TLS_1 */ +EXTERN char *DHParams; /* file with DH parameters */ +EXTERN char *RandFile; /* source of random data */ +EXTERN char *SrvCERTfile; /* file with server certificate */ +EXTERN char *Srvkeyfile; /* file with server private key */ +EXTERN unsigned long TLS_Srv_Opts; /* TLS server options */ +#endif /* STARTTLS */ /* ** Queue related items @@ -1545,39 +1801,90 @@ ERROR: change SASL_SEC_MASK_ notify sendmail.org! #define QSO_BYHOST 1 /* sort by first host name */ #define QSO_BYTIME 2 /* sort by submission time */ #define QSO_BYFILENAME 3 /* sort by file name only */ +#define QSO_RANDOM 4 /* sort in random order */ +#define QSO_BYMODTIME 5 /* sort by modification time */ +#if _FFR_RHS +# define QSO_BYSHUFFLE 6 /* sort by shuffled host name */ +#endif /* _FFR_RHS */ #if _FFR_QUEUEDELAY -#define QD_LINEAR 0 /* linear (old) delay alg */ -#define QD_EXP 1 /* exponential delay alg */ +# define QD_LINEAR 0 /* linear (old) delay alg */ +# define QD_EXP 1 /* exponential delay alg */ #endif /* _FFR_QUEUEDELAY */ -#define NOQDIR (-1) /* no queue directory (yet) */ +#define NOQGRP (-1) /* no queue group (yet) */ +#define ENVQGRP (-2) /* use queue group of envelope */ +#define NOAQGRP (-3) /* no queue group in addr (yet) */ +#define ISVALIDQGRP(x) ((x) >= 0) /* valid queue group? */ +#define NOQDIR (-1) /* no queue directory (yet) */ +#define ENVQDIR (-2) /* use queue directory of envelope */ +#define NOAQDIR (-3) /* no queue directory in addr (yet) */ +#define ISVALIDQDIR(x) ((x) >= 0) /* valid queue directory? */ +#define RS_QUEUEGROUP "queuegroup" /* ruleset for queue group selection */ -#define NOW ((time_t) (-1)) /* queue return: now */ +#define NOW ((time_t) (-1)) /* queue return: now */ + +/* SuperSafe values */ +#define SAFE_NO 0 /* no fsync(): don't use... */ +#define SAFE_INTERACTIVE 1 /* limit fsync() in -odi */ +#define SAFE_REALLY 2 /* always fsync() */ /* Queue Run Limitations */ struct queue_char { - char *queue_match; /* string to match */ - struct queue_char *queue_next; + char *queue_match; /* string to match */ + bool queue_negate; /* or not match, if set */ + struct queue_char *queue_next; }; -typedef struct queue_char QUEUE_CHAR; +typedef struct queue_char QUEUE_CHAR; + +EXTERN int volatile CurRunners; /* current number of runner children */ +EXTERN int MaxQueueRun; /* maximum number of jobs in one queue run */ +EXTERN int MaxQueueChildren; /* max # of forked queue children */ +EXTERN int MaxRunnersPerQueue; /* max # proc's active in queue group */ +EXTERN int NiceQueueRun; /* nice queue runs to this value */ +EXTERN int NumQueue; /* number of queue groups */ +EXTERN int QueueFileMode; /* mode on qf/tf/df files */ +EXTERN int QueueSortOrder; /* queue sorting order algorithm */ +EXTERN time_t MinQueueAge; /* min delivery interval */ +EXTERN time_t QueueIntvl; /* intervals between running the queue */ +EXTERN char *QueueDir; /* location of queue directory */ +#if _FFR_QUEUEDELAY +EXTERN int QueueAlg; /* algorithm for queue delays */ +EXTERN time_t QueueInitDelay; /* initial queue delay */ +EXTERN time_t QueueMaxDelay; /* maximum queue delay */ +#endif /* _FFR_QUEUEDELAY */ +EXTERN QUEUE_CHAR *QueueLimitId; /* limit queue run to id */ +EXTERN QUEUE_CHAR *QueueLimitRecipient; /* limit queue run to rcpt */ +EXTERN QUEUE_CHAR *QueueLimitSender; /* limit queue run to sender */ +EXTERN QUEUEGRP *Queue[MAXQUEUEGROUPS + 1]; /* queue groups */ /* functions */ extern void assign_queueid __P((ENVELOPE *)); -extern ADDRESS *copyqueue __P((ADDRESS *)); +extern ADDRESS *copyqueue __P((ADDRESS *, SM_RPOOL_T *)); +extern void cleanup_queues __P((void)); +extern bool doqueuerun __P((void)); extern void initsys __P((ENVELOPE *)); extern void loseqfile __P((ENVELOPE *, char *)); -extern void multiqueue_cache __P((void)); +extern int name2qid __P((char *)); extern char *qid_printname __P((ENVELOPE *)); -extern char *qid_printqueue __P((int)); +extern char *qid_printqueue __P((int, int)); extern char *queuename __P((ENVELOPE *, int)); -extern void queueup __P((ENVELOPE *, bool)); -extern bool runqueue __P((bool, bool)); -extern void setnewqueue __P((ENVELOPE *)); +extern void queueup __P((ENVELOPE *, bool, bool)); +extern bool runqueue __P((bool, bool, bool, bool)); +extern int run_work_group __P((int, bool, bool, bool, bool)); +extern void set_def_queueval __P((QUEUEGRP *, bool)); +extern void setup_queues __P((bool)); +extern bool setnewqueue __P((ENVELOPE *)); extern bool shouldqueue __P((long, time_t)); extern void sync_queue_time __P((void)); +#if REQUIRES_DIR_FSYNC +# define SYNC_DIR(path, panic) sync_dir(path, panic) +extern void sync_dir __P((char *, bool)); +#else /* REQUIRES_DIR_FSYNC */ +# define SYNC_DIR(path, panic) ((void) 0) +#endif /* REQUIRES_DIR_FSYNC */ /* ** Timeouts @@ -1598,6 +1905,7 @@ EXTERN struct /* following timeouts are not mentioned in RFC 1123 */ time_t to_iconnect; /* initial connection timeout (first try) */ time_t to_connect; /* initial connection timeout (later tries) */ + time_t to_aconnect; /* all connections timeout (MX and A records) */ time_t to_rset; /* RSET command */ time_t to_helo; /* HELO command */ time_t to_quit; /* QUIT command */ @@ -1605,6 +1913,13 @@ EXTERN struct time_t to_ident; /* IDENT protocol requests */ time_t to_fileopen; /* opening :include: and .forward files */ time_t to_control; /* process a control socket command */ + time_t to_lhlo; /* LMTP: LHLO command */ +#if SASL + time_t to_auth; /* AUTH dialogue [10m] */ +#endif /* SASL */ +#if STARTTLS + time_t to_starttls; /* STARTTLS dialogue [10m] */ +#endif /* STARTTLS */ /* following are per message */ time_t to_q_return[MAXTOCLASS]; /* queue return timeouts */ time_t to_q_warning[MAXTOCLASS]; /* queue warning timeouts */ @@ -1626,61 +1941,23 @@ EXTERN struct extern void inittimeouts __P((char *, bool)); /* -** Trace information +** Interface probing */ -/* macros for debugging flags */ -#define tTd(flag, level) (tTdvect[flag] >= (u_char)level) -#define tTdlevel(flag) (tTdvect[flag]) +#define DPI_PROBENONE 0 /* Don't probe any interfaces */ +#define DPI_PROBEALL 1 /* Probe all interfaces */ +#define DPI_SKIPLOOPBACK 2 /* Don't probe loopback interfaces */ -/* variables */ -extern u_char tTdvect[100]; /* trace vector */ -/* -** Critical signal sections +/* +** Trace information */ -#define PEND_SIGHUP 0x0001 -#define PEND_SIGINT 0x0002 -#define PEND_SIGTERM 0x0004 -#define PEND_SIGUSR1 0x0008 - -#define ENTER_CRITICAL() InCriticalSection++ - -#define LEAVE_CRITICAL() \ -do \ -{ \ - if (InCriticalSection > 0) \ - InCriticalSection--; \ -} while (0) - -#define CHECK_CRITICAL(sig) \ -do \ -{ \ - if (InCriticalSection > 0 && (sig) != 0) \ - { \ - pend_signal((sig)); \ - return SIGFUNC_RETURN; \ - } \ -} while (0) - -/* reset signal in case System V semantics */ -#ifdef SYS5SIGNALS -# define FIX_SYSV_SIGNAL(sig, handler) \ -{ \ - if ((sig) != 0) \ - (void) setsignal((sig), (handler)); \ -} -#else /* SYS5SIGNALS */ -# define FIX_SYSV_SIGNAL(sig, handler) { /* EMPTY */ } -#endif /* SYS5SIGNALS */ +/* macros for debugging flags */ +#define tTd(flag, level) (tTdvect[flag] >= (unsigned char)level) +#define tTdlevel(flag) (tTdvect[flag]) /* variables */ -EXTERN u_int volatile InCriticalSection; /* >0 if in a critical section */ -EXTERN int volatile PendingSignal; /* pending signal to resend */ - -/* functions */ -extern void pend_signal __P((int)); - +extern unsigned char tTdvect[100]; /* trace vector */ /* ** Miscellaneous information. */ @@ -1691,6 +1968,10 @@ extern void pend_signal __P((int)); #define NOQID "*~*" +/* use id or NOQID (to avoid NOQUEUE in logfile) */ +#define E_ID(id) ((id) == NULL ? NOQID : (id)) + +#define CURHOSTNAME (CurHostName == NULL ? "local" : CurHostName) /* ** Some in-line functions @@ -1706,25 +1987,59 @@ extern void pend_signal __P((int)); #define newstr(s) strcpy(xalloc(strlen(s) + 1), s) #define STRUCTCOPY(s, d) d = s + +/* free a pointer if it isn't NULL and set it to NULL */ +#define SM_FREE_CLR(p) \ + if ((p) != NULL) \ + { \ + sm_free(p); \ + (p) = NULL; \ + } \ + else + +/* +** Update a permanent string variable with a new value. +** The old value is freed, the new value is strdup'ed. +** +** We use sm_pstrdup_x to duplicate the string because it raises +** an exception on error, and because it allocates "permanent storage" +** which is not expected to be freed before process exit. +** The latter is important for memory leak analysis. +** +** If an exception occurs while strdup'ing the new value, +** then the variable remains set to the old value. +** That's why the strdup must occur before we free the old value. +** +** The macro uses a do loop so that this idiom will work: +** if (...) +** PSTRSET(var, val1); +** else +** PSTRSET(var, val2); +*/ +#define PSTRSET(var, val) \ + do \ + { \ + char *_newval = sm_pstrdup_x(val); \ + if (var != NULL) \ + sm_free(var); \ + var = _newval; \ + } while (0) + /* ** Global variables. */ EXTERN bool AllowBogusHELO; /* allow syntax errors on HELO command */ -#if !_FFR_REMOVE_AUTOREBUILD -EXTERN bool AutoRebuild; /* auto-rebuild the alias database as needed */ -#endif /* !_FFR_REMOVE_AUTOREBUILD */ EXTERN bool CheckAliases; /* parse addresses during newaliases */ -EXTERN bool ChownAlwaysSafe; /* treat chown(2) as safe */ EXTERN bool ColonOkInAddr; /* single colon legal in address */ +#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) EXTERN bool ConfigFileRead; /* configuration file has been read */ +#endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */ EXTERN bool volatile DataProgress; /* have we sent anything since last check */ EXTERN bool DisConnected; /* running with OutChannel redirected to xf */ -EXTERN bool volatile DoQueueRun; /* non-interrupt time queue run needed */ EXTERN bool DontExpandCnames; /* do not $[...$] expand CNAMEs */ EXTERN bool DontInitGroups; /* avoid initgroups() because of NIS cost */ EXTERN bool DontLockReadFiles; /* don't read lock support files */ -EXTERN bool DontProbeInterfaces; /* don't probe interfaces for names */ EXTERN bool DontPruneRoutes; /* don't prune source routes */ EXTERN bool ForkQueueRuns; /* fork for each job when running the queue */ EXTERN bool FromFlag; /* if set, "From" person is explicit */ @@ -1734,42 +2049,46 @@ EXTERN bool HasWildcardMX; /* don't use MX records when canonifying */ EXTERN bool HoldErrs; /* only output errors to transcript */ EXTERN bool IgnoreHostStatus; /* ignore long term host status files */ EXTERN bool IgnrDot; /* don't let dot end messages */ -EXTERN bool InChild; /* true if running in an SMTP subprocess */ EXTERN bool LogUsrErrs; /* syslog user errors (e.g., SMTP RCPT cmd) */ -EXTERN bool MapOpenErr; /* error opening a non-optional map */ EXTERN bool MatchGecos; /* look for user names in gecos field */ EXTERN bool MeToo; /* send to the sender also */ EXTERN bool NoAlias; /* suppress aliasing */ EXTERN bool NoConnect; /* don't connect to non-local mailers */ EXTERN bool OnlyOneError; /* .... or only want to give one SMTP reply */ EXTERN bool QuickAbort; /* .... but only if we want a quick abort */ +EXTERN bool ResNoAliases; /* don't use $HOSTALIASES */ +EXTERN bool volatile RestartWorkGroup; /* daemon needs to restart some work groups */ EXTERN bool RrtImpliesDsn; /* turn Return-Receipt-To: into DSN */ EXTERN bool SaveFrom; /* save leading "From" lines */ EXTERN bool SendMIMEErrors; /* send error messages in MIME format */ EXTERN bool SevenBitInput; /* force 7-bit data on input */ EXTERN bool SingleLineFromHeader; /* force From: header to be one line */ EXTERN bool SingleThreadDelivery; /* single thread hosts on delivery */ +#if _FFR_SOFT_BOUNCE +EXTERN bool SoftBounce; /* replace 5xy by 4xy (for testing) */ +#endif /* _FFR_SOFT_BOUNCE */ EXTERN bool volatile StopRequest; /* stop sending output */ -EXTERN bool SuperSafe; /* be extra careful, even if expensive */ EXTERN bool SuprErrs; /* set if we are suppressing errors */ EXTERN bool TryNullMXList; /* if we are the best MX, try host directly */ -#if _FFR_WORKAROUND_BROKEN_NAMESERVERS +EXTERN bool UseMSP; /* mail submission: group writable queue ok? */ EXTERN bool WorkAroundBrokenAAAA; /* some nameservers return SERVFAIL on AAAA queries */ -#endif /* _FFR_WORKAROUND_BROKEN_NAMESERVERS */ EXTERN bool UseErrorsTo; /* use Errors-To: header (back compat) */ -EXTERN bool UseHesiod; /* using Hesiod -- interpret Hesiod errors */ EXTERN bool UseNameServer; /* using DNS -- interpret h_errno & MX RRs */ EXTERN char InetMode; /* default network for daemon mode */ EXTERN char OpMode; /* operation mode, see below */ EXTERN char SpaceSub; /* substitution for <lwsp> */ +EXTERN int BadRcptThrottle; /* Throttle rejected RCPTs per SMTP message */ EXTERN int CheckpointInterval; /* queue file checkpoint interval */ EXTERN int ConfigLevel; /* config file level */ EXTERN int ConnRateThrottle; /* throttle for SMTP connection rate */ EXTERN int volatile CurChildren; /* current number of daemonic children */ EXTERN int CurrentLA; /* current load average */ EXTERN int DefaultNotify; /* default DSN notification flags */ +EXTERN int DelayLA; /* load average to delay connections */ +EXTERN int DontProbeInterfaces; /* don't probe interfaces for names */ EXTERN int Errors; /* set if errors (local to single pass) */ EXTERN int ExitStat; /* exit status code */ +EXTERN int FastSplit; /* fast initial splitting of envelopes */ EXTERN int FileMode; /* mode on files */ EXTERN int LineNumber; /* line number in current input */ EXTERN int LogLevel; /* level of logging to perform */ @@ -1779,43 +2098,46 @@ EXTERN int MaxForwardEntries; /* maximum number of forward entries */ EXTERN int MaxHeadersLength; /* max length of headers */ EXTERN int MaxHopCount; /* max # of hops until bounce */ EXTERN int MaxMacroRecursion; /* maximum depth of macro recursion */ -EXTERN int MaxMciCache; /* maximum entries in MCI cache */ EXTERN int MaxMimeFieldLength; /* maximum MIME field length */ EXTERN int MaxMimeHeaderLength; /* maximum MIME header length */ - -EXTERN int MaxQueueRun; /* maximum number of jobs in one queue run */ EXTERN int MaxRcptPerMsg; /* max recipients per SMTP message */ EXTERN int MaxRuleRecursion; /* maximum depth of ruleset recursion */ EXTERN int MimeMode; /* MIME processing mode */ EXTERN int NoRecipientAction; -EXTERN int NumPriorities; /* pointer into Priorities */ -EXTERN u_short PrivacyFlags; /* privacy flags */ -#if _FFR_QUEUE_FILE_MODE -EXTERN int QueueFileMode; /* mode on qf/tf/df files */ -#endif /* _FFR_QUEUE_FILE_MODE */ + +#if SM_CONF_SHM +EXTERN int Numfilesys; /* number of queue file systems */ +EXTERN int *PNumFileSys; +# define NumFileSys (*PNumFileSys) +# else /* SM_CONF_SHM */ +EXTERN int NumFileSys; /* number of queue file systems */ +# endif /* SM_CONF_SHM */ + EXTERN int QueueLA; /* load average starting forced queueing */ -EXTERN int QueueSortOrder; /* queue sorting order algorithm */ -EXTERN int RefuseLA; /* load average refusing connections are */ +EXTERN int RefuseLA; /* load average refusing connections */ +EXTERN int SuperSafe; /* be extra careful, even if expensive */ EXTERN int VendorCode; /* vendor-specific operation enhancements */ EXTERN int Verbose; /* set if blow-by-blow desired */ EXTERN gid_t DefGid; /* default gid to run as */ EXTERN gid_t RealGid; /* real gid of caller */ EXTERN gid_t RunAsGid; /* GID to become for bulk of run */ +EXTERN gid_t EffGid; /* effective gid */ +#if SM_CONF_SHM +EXTERN key_t ShmKey; /* shared memory key */ +#endif /* SM_CONF_SHM */ +EXTERN pid_t CurrentPid; /* current process id */ +EXTERN pid_t DaemonPid; /* process id of daemon */ EXTERN uid_t DefUid; /* default uid to run as */ EXTERN uid_t RealUid; /* real uid of caller */ EXTERN uid_t RunAsUid; /* UID to become for bulk of run */ EXTERN uid_t TrustedUid; /* uid of trusted user for files and startup */ EXTERN size_t DataFileBufferSize; /* size of buffer for in-core df */ -EXTERN size_t XscriptFileBufferSize; /* size of buffer for in-core xf */ +EXTERN time_t DeliverByMin; /* deliver by minimum time */ EXTERN time_t DialDelay; /* delay between dial-on-demand tries */ -EXTERN time_t MciCacheTimeout; /* maximum idle time on connections */ -EXTERN time_t MciInfoTimeout; /* how long 'til we retry down hosts */ -EXTERN time_t MinQueueAge; /* min delivery interval */ -EXTERN time_t QueueIntvl; /* intervals between running the queue */ EXTERN time_t SafeAlias; /* interval to wait until @:@ in alias file */ EXTERN time_t ServiceCacheMaxAge; /* refresh interval for cache */ -EXTERN time_t ServiceCacheTime; /* time service switch was cached */ +EXTERN size_t XscriptFileBufferSize; /* size of buffer for in-core xf */ EXTERN MODE_T OldUmask; /* umask when sendmail starts up */ EXTERN long MaxMessageSize; /* advertised max size we will accept */ EXTERN long MinBlocksFree; /* min # of blocks free on queue fs */ @@ -1823,26 +2145,6 @@ EXTERN long QueueFactor; /* slope of queue function */ EXTERN long WkClassFact; /* multiplier for message class -> priority */ EXTERN long WkRecipFact; /* multiplier for # of recipients -> priority */ EXTERN long WkTimeFact; /* priority offset each time this job is run */ -#if SASL -EXTERN char *AuthMechanisms; /* AUTH mechanisms */ -EXTERN char *SASLInfo; /* file with AUTH info */ -#endif /* SASL */ -EXTERN int SASLOpts; /* options for SASL */ -#if STARTTLS -EXTERN char *CACERTpath; /* path to CA certificates (dir. with hashes) */ -EXTERN char *CACERTfile; /* file with CA certificate */ -EXTERN char *SrvCERTfile; /* file with server certificate */ -EXTERN char *Srvkeyfile; /* file with server private key */ -EXTERN char *CltCERTfile; /* file with client certificate */ -EXTERN char *Cltkeyfile; /* file with client private key */ -EXTERN char *DHParams; /* file with DH parameters */ -EXTERN char *RandFile; /* source of random data */ -# if _FFR_TLS_1 -EXTERN char *DHParams5; /* file with DH parameters (512) */ -EXTERN char *CipherList; /* list of ciphers */ -# endif /* _FFR_TLS_1 */ -#endif /* STARTTLS */ -EXTERN char *ConfFile; /* location of configuration file [conf.c] */ EXTERN char *ControlSocketName; /* control socket filename [control.c] */ EXTERN char *CurHostName; /* current host we are dealing with */ EXTERN char *DeadLetterDrop; /* path to dead letter office */ @@ -1856,18 +2158,13 @@ EXTERN char *ForwardPath; /* path to search for .forward files */ EXTERN char *HelpFile; /* location of SMTP help file */ EXTERN char *HostStatDir; /* location of host status information */ EXTERN char *HostsFile; /* path to /etc/hosts file */ +extern char *Mbdb; /* mailbox database type */ EXTERN char *MustQuoteChars; /* quote these characters in phrases */ EXTERN char *MyHostName; /* name of this host for SMTP messages */ EXTERN char *OperatorChars; /* operators (old $o macro) */ EXTERN char *PidFile; /* location of proc id file [conf.c] */ EXTERN char *PostMasterCopy; /* address to get errs cc's */ EXTERN char *ProcTitlePrefix; /* process title prefix */ -EXTERN char *QueueDir; /* location of queue directory */ -#if _FFR_QUEUEDELAY -EXTERN int QueueAlg; /* algorithm for queue delays */ -EXTERN time_t QueueInitDelay; /* initial queue delay */ -EXTERN time_t QueueMaxDelay; /* maximum queue delay */ -#endif /* _FFR_QUEUEDELAY */ EXTERN char *RealHostName; /* name of host we are talking to */ EXTERN char *RealUserName; /* real user name of caller */ EXTERN char *volatile RestartRequest;/* a sendmail restart has been requested */ @@ -1882,70 +2179,30 @@ EXTERN char *StatFile; /* location of statistics summary */ EXTERN char *TimeZoneSpec; /* override time zone specification */ EXTERN char *UdbSpec; /* user database source spec */ EXTERN char *UnixFromLine; /* UNIX From_ line (old $l macro) */ -EXTERN char **ExternalEnviron; /* input environment */ - /* saved user environment */ +EXTERN char **ExternalEnviron; /* saved user (input) environment */ EXTERN char **SaveArgv; /* argument vector for re-execing */ EXTERN BITMAP256 DontBlameSendmail; /* DontBlameSendmail bits */ -#if SFIO -EXTERN Sfio_t *InChannel; /* input connection */ -EXTERN Sfio_t *OutChannel; /* output connection */ -#else /* SFIO */ -EXTERN FILE *InChannel; /* input connection */ -EXTERN FILE *OutChannel; /* output connection */ -#endif /* SFIO */ -EXTERN FILE *TrafficLogFile; /* file in which to log all traffic */ -#ifdef HESIOD +EXTERN SM_FILE_T *InChannel; /* input connection */ +EXTERN SM_FILE_T *OutChannel; /* output connection */ +EXTERN SM_FILE_T *TrafficLogFile; /* file in which to log all traffic */ +#if HESIOD EXTERN void *HesiodContext; #endif /* HESIOD */ EXTERN ENVELOPE *CurEnv; /* envelope currently being processed */ -EXTERN MAILER *LocalMailer; /* ptr to local mailer */ -EXTERN MAILER *ProgMailer; /* ptr to program mailer */ -EXTERN MAILER *FileMailer; /* ptr to *file* mailer */ -EXTERN MAILER *InclMailer; /* ptr to *include* mailer */ -EXTERN QUEUE_CHAR *QueueLimitRecipient; /* limit queue run to rcpt */ -EXTERN QUEUE_CHAR *QueueLimitSender; /* limit queue run to sender */ -EXTERN QUEUE_CHAR *QueueLimitId; /* limit queue run to id */ -EXTERN MAILER *Mailer[MAXMAILERS + 1]; -EXTERN struct rewrite *RewriteRules[MAXRWSETS]; EXTERN char *RuleSetNames[MAXRWSETS]; /* ruleset number to name */ EXTERN char *UserEnviron[MAXUSERENVIRON + 1]; -EXTERN struct priority Priorities[MAXPRIORITIES]; +EXTERN struct rewrite *RewriteRules[MAXRWSETS]; EXTERN struct termescape TermEscape; /* terminal escape codes */ EXTERN SOCKADDR ConnectOnlyTo; /* override connection address (for testing) */ EXTERN SOCKADDR RealHostAddr; /* address of host we are talking to */ -EXTERN jmp_buf TopFrame; /* branch-to-top-of-loop-on-error frame */ -EXTERN TIMERS Timers; +extern const SM_EXC_TYPE_T EtypeQuickAbort; /* type of a QuickAbort exception */ + + /* ** Declarations of useful functions */ -#if SASL -extern char *intersect __P((char *, char *)); -extern char *iteminlist __P((char *, char *, char *)); -extern int proxy_policy __P((void *, const char *, const char *, const char **, const char **)); -# if SASL > 10515 -extern int safesaslfile __P((void *, char *, int)); -# else /* SASL > 10515 */ -extern int safesaslfile __P((void *, char *)); -# endif /* SASL > 10515 */ -extern int sasl_decode64 __P((const char *, unsigned, char *, unsigned *)); -extern int sasl_encode64 __P((const char *, unsigned, char *, unsigned, unsigned *)); -#endif /* SASL */ - -#if STARTTLS -extern void apps_ssl_info_cb __P((SSL *, int , int)); -extern bool init_tls_library __P((void)); -extern bool inittls __P((SSL_CTX **, u_long, bool, char *, char *, char *, char *, char *)); -extern bool initclttls __P((void)); -extern bool initsrvtls __P((void)); -extern int tls_get_info __P((SSL *, ENVELOPE *, bool, char *, bool)); -extern int endtls __P((SSL *, char *)); -extern int endtlsclt __P((MCI *)); -extern void tlslogerr __P((void)); -extern bool tls_rand_init __P((char *, int)); -#endif /* STARTTLS */ - /* Transcript file */ extern void closexscript __P((ENVELOPE *)); extern void openxscript __P((ENVELOPE *)); @@ -1953,11 +2210,11 @@ extern void openxscript __P((ENVELOPE *)); /* error related */ extern void buffer_errors __P((void)); extern void flush_errors __P((bool)); -extern void message __P((const char *, ...)); -extern void nmessage __P((const char *, ...)); -extern void syserr __P((const char *, ...)); -extern void usrerrenh __P((char *, const char *, ...)); -extern void usrerr __P((const char *, ...)); +extern void PRINTFLIKE(1, 2) message __P((const char *, ...)); +extern void PRINTFLIKE(1, 2) nmessage __P((const char *, ...)); +extern void PRINTFLIKE(1, 2) syserr __P((const char *, ...)); +extern void PRINTFLIKE(2, 3) usrerrenh __P((char *, const char *, ...)); +extern void PRINTFLIKE(1, 2) usrerr __P((const char *, ...)); extern int isenhsc __P((const char *, int)); extern int extenhsc __P((const char *, int, char *)); @@ -1965,23 +2222,23 @@ extern int extenhsc __P((const char *, int, char *)); extern void alias __P((ADDRESS *, ADDRESS **, int, ENVELOPE *)); extern bool aliaswait __P((MAP *, char *, bool)); extern void forward __P((ADDRESS *, ADDRESS **, int, ENVELOPE *)); -extern void readaliases __P((MAP *, FILE *, bool, bool)); +extern void readaliases __P((MAP *, SM_FILE_T *, bool, bool)); extern bool rebuildaliases __P((MAP *, bool)); extern void setalias __P((char *)); /* logging */ extern void logdelivery __P((MAILER *, MCI *, char *, const char *, ADDRESS *, time_t, ENVELOPE *)); extern void logsender __P((ENVELOPE *, char *)); -extern void sm_syslog __P((int, const char *, const char *, ...)); +extern void PRINTFLIKE(3, 4) sm_syslog __P((int, const char *, const char *, ...)); /* SMTP */ -extern void giveresponse __P((int, char *, MAILER *, MCI *, ADDRESS *, time_t, ENVELOPE *)); +extern void giveresponse __P((int, char *, MAILER *, MCI *, ADDRESS *, time_t, ENVELOPE *, ADDRESS *)); extern int reply __P((MAILER *, MCI *, ENVELOPE *, time_t, void (*)(), char **)); extern void smtp __P((char *volatile, BITMAP256, ENVELOPE *volatile)); #if SASL extern int smtpauth __P((MAILER *, MCI *, ENVELOPE *)); #endif /* SASL */ -extern int smtpdata __P((MAILER *, MCI *, ENVELOPE *)); +extern int smtpdata __P((MAILER *, MCI *, ENVELOPE *, ADDRESS *, time_t)); extern int smtpgetstat __P((MAILER *, MCI *, ENVELOPE *)); extern int smtpmailfrom __P((MAILER *, MCI *, ENVELOPE *)); extern void smtpmessage __P((char *, MAILER *, MCI *, ...)); @@ -1989,7 +2246,7 @@ extern void smtpinit __P((MAILER *, MCI *, ENVELOPE *, bool)); extern char *smtptodsn __P((int)); extern int smtpprobe __P((MCI *)); extern void smtpquit __P((MAILER *, MCI *, ENVELOPE *)); -extern int smtprcpt __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *)); +extern int smtprcpt __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *, ADDRESS *, time_t)); extern void smtprset __P((MAILER *, MCI *, ENVELOPE *)); #define ISSMTPCODE(c) (isascii(c[0]) && isdigit(c[0]) && \ @@ -1999,7 +2256,8 @@ extern void smtprset __P((MAILER *, MCI *, ENVELOPE *)); (c[3] == ' ' || c[3] == '-' || c[3] == '\0')) /* delivery */ -extern pid_t dowork __P((int, char *, bool, bool, ENVELOPE *)); +extern pid_t dowork __P((int, int, char *, bool, bool, ENVELOPE *)); +extern pid_t doworklist __P((ENVELOPE *, bool, bool)); extern int endmailer __P((MCI *, ENVELOPE *, char **)); extern int mailfile __P((char *volatile, MAILER *volatile, ADDRESS *, volatile long, ENVELOPE *)); extern void sendall __P((ENVELOPE *, int)); @@ -2015,13 +2273,13 @@ extern void clrcontrol __P((void)); extern void control_command __P((int, ENVELOPE *)); extern int opencontrolsocket __P((void)); -#if _FFR_MILTER +#if MILTER /* milter functions */ -extern void milter_parse_list __P((char *, struct milter **, int)); +extern void milter_config __P((char *, struct milter **, int)); extern void milter_setup __P((char *)); extern void milter_set_option __P((char *, char *, bool)); extern bool milter_can_delrcpts __P((void)); -extern void milter_init __P((ENVELOPE *, char *)); +extern bool milter_init __P((ENVELOPE *, char *)); extern void milter_quit __P((ENVELOPE *)); extern void milter_abort __P((ENVELOPE *)); extern char *milter_connect __P((char *, SOCKADDR, ENVELOPE *, char *)); @@ -2029,10 +2287,9 @@ extern char *milter_helo __P((char *, ENVELOPE *, char *)); extern char *milter_envfrom __P((char **, ENVELOPE *, char *)); extern char *milter_envrcpt __P((char **, ENVELOPE *, char *)); extern char *milter_data __P((ENVELOPE *, char *)); -#endif /* _FFR_MILTER */ +#endif /* MILTER */ -extern char *addquotes __P((char *)); -extern void allsignals __P((bool)); +extern char *addquotes __P((char *, SM_RPOOL_T *)); extern char *arpadate __P((char *)); extern bool atobool __P((char *)); extern int atooct __P((char *)); @@ -2041,6 +2298,8 @@ extern int blocksignal __P((int)); extern bool bitintersect __P((BITMAP256, BITMAP256)); extern bool bitzerop __P((BITMAP256)); extern void buildfname __P((char *, char *, char *, int)); +extern bool chkclientmodifiers __P((int)); +extern bool chkdaemonmodifiers __P((int)); extern int checkcompat __P((ADDRESS *, ENVELOPE *)); #ifdef XDEBUG extern void checkfd012 __P((char *)); @@ -2049,40 +2308,45 @@ extern void checkfdopen __P((int, char *)); extern void checkfds __P((char *)); extern bool chownsafe __P((int, bool)); extern void cleanstrcpy __P((char *, char *, int)); +#if SM_CONF_SHM +extern void cleanup_shm __P((bool)); +#endif /* SM_CONF_SHM */ extern void clrdaemon __P((void)); -extern void collect __P((FILE *, bool, HDR **, ENVELOPE *)); +extern void collect __P((SM_FILE_T *, bool, HDR **, ENVELOPE *)); extern time_t convtime __P((char *, int)); -extern char **copyplist __P((char **, bool)); +extern char **copyplist __P((char **, bool, SM_RPOOL_T *)); extern void copy_class __P((int, int)); extern time_t curtime __P((void)); extern char *defcharset __P((ENVELOPE *)); extern char *denlstring __P((char *, bool, bool)); extern void disconnect __P((int, ENVELOPE *)); -extern bool dns_getcanonname __P((char *, int, bool, int *)); +#if _FFR_CONTROL_MSTAT +extern void disk_status __P((SM_FILE_T *, char *)); +#endif /* _FFR_CONTROL_MSTAT */ +extern bool dns_getcanonname __P((char *, int, bool, int *, int *)); extern pid_t dofork __P((void)); extern int drop_privileges __P((bool)); extern int dsntoexitstat __P((char *)); extern void dumpfd __P((int, bool, bool)); extern void dumpstate __P((char *)); -extern bool enoughdiskspace __P((long, bool)); +extern bool enoughdiskspace __P((long, ENVELOPE *)); extern char *exitstat __P((char *)); -extern char *fgetfolded __P((char *, int, FILE *)); +extern void fatal_error __P((SM_EXC_T *)); +extern char *fgetfolded __P((char *, int, SM_FILE_T *)); extern void fill_fd __P((int, char *)); extern char *find_character __P((char *, int)); -extern struct passwd *finduser __P((char *, bool *)); +extern int finduser __P((char *, bool *, SM_MBDB_T *)); extern void finis __P((bool, volatile int)); extern void fixcrlf __P((char *, bool)); extern long freediskspace __P((char *, long *)); #if NETINET6 && NEEDSGETIPNODE -# if _FFR_FREEHOSTENT extern void freehostent __P((struct hostent *)); -# endif /* _FFR_FREEHOSTENT */ -#endif /* NEEDSGETIPNODE && NETINET6 */ +#endif /* NETINET6 && NEEDSGETIPNODE */ extern char *get_column __P((char *, int, int, char *, int)); extern char *getauthinfo __P((int, bool *)); -extern char *getcfname __P((void)); -extern char *getextenv __P((const char *)); extern int getdtsize __P((void)); +extern int getla __P((void)); +extern char *getmodifiers __P((char *, BITMAP256)); extern BITMAP256 *getrequests __P((ENVELOPE *)); extern char *getvendor __P((int)); extern void help __P((char *, ENVELOPE *)); @@ -2097,31 +2361,37 @@ extern bool isloopback __P((SOCKADDR sa)); extern void load_if_names __P((void)); extern bool lockfile __P((int, char *, char *, int)); extern void log_sendmail_pid __P((ENVELOPE *)); +extern void logundelrcpts __P((ENVELOPE *, char *, int, bool)); extern char lower __P((int)); extern void makelower __P((char *)); extern int makeconnection_ds __P((char *, MCI *)); -extern int makeconnection __P((char *, volatile u_int, MCI *, ENVELOPE *)); +extern int makeconnection __P((char *, volatile unsigned int, MCI *, ENVELOPE *, time_t)); +extern void makeworkgroups __P((void)); +extern void mark_work_group_restart __P((int, int)); extern char * munchstring __P((char *, char **, int)); extern struct hostent *myhostname __P((char *, int)); extern char *nisplus_default_domain __P((void)); /* extern for Sun */ extern bool path_is_dir __P((char *, bool)); +extern int pickqdir __P((QUEUEGRP *qg, long fsize, ENVELOPE *e)); extern char *pintvl __P((time_t, bool)); extern void printav __P((char **)); extern void printmailer __P((MAILER *)); +extern void printnqe __P((SM_FILE_T *, char *)); extern void printopenfds __P((bool)); extern void printqueue __P((void)); extern void printrules __P((void)); extern pid_t prog_open __P((char **, int *, ENVELOPE *)); extern void putline __P((char *, MCI *)); extern void putxline __P((char *, size_t, MCI *, int)); -extern void queueup_macros __P((int, FILE *, ENVELOPE *)); +extern void queueup_macros __P((int, SM_FILE_T *, ENVELOPE *)); extern void readcf __P((char *, bool, ENVELOPE *)); extern SIGFUNC_DECL reapchild __P((int)); extern int releasesignal __P((int)); extern void resetlimits __P((void)); +extern void restart_daemon __P((void)); +extern void restart_marked_work_groups __P(()); extern bool rfc822_string __P((char *)); -extern FILE *safefopen __P((char *, int, int, long)); -extern void savemail __P((ENVELOPE *, bool)); +extern bool savemail __P((ENVELOPE *, bool)); extern void seed_random __P((void)); extern void sendtoargv __P((char **, ENVELOPE *)); extern void setclientoptions __P((char *)); @@ -2129,49 +2399,66 @@ extern bool setdaemonoptions __P((char *)); extern void setdefaults __P((ENVELOPE *)); extern void setdefuser __P((void)); extern bool setvendor __P((char *)); +extern void set_op_mode __P((int)); extern void setoption __P((int, char *, bool, bool, ENVELOPE *)); extern sigfunc_t setsignal __P((int, sigfunc_t)); extern void setuserenv __P((const char *, const char *)); extern void settime __P((ENVELOPE *)); -extern char *sfgets __P((char *, int, FILE *, time_t, char *)); -extern char *shortenstring __P((const char *, int)); +extern char *sfgets __P((char *, int, SM_FILE_T *, time_t, char *)); +extern char *shortenstring __P((const char *, size_t)); extern char *shorten_hostname __P((char [])); extern bool shorten_rfc822_string __P((char *, size_t)); extern void shutdown_daemon __P((void)); -extern void sm_dopr __P((char *, const char *, va_list)); -extern void sm_free __P((void *)); extern struct hostent *sm_gethostbyname __P((char *, int)); extern struct hostent *sm_gethostbyaddr __P((char *, int, int)); -extern int sm_getla __P((ENVELOPE *)); +extern void sm_getla __P((void)); extern struct passwd *sm_getpwnam __P((char *)); extern struct passwd *sm_getpwuid __P((UID_T)); extern void sm_setproctitle __P((bool, ENVELOPE *, const char *, ...)); -extern SIGFUNC_DECL sm_signal_noop __P((int)); -extern int sm_strcasecmp __P((const char *, const char *)); +extern pid_t sm_wait __P((int *)); +extern bool split_by_recipient __P((ENVELOPE *e)); extern void stop_sendmail __P((void)); +extern char *str2prt __P((char *)); extern bool strcontainedin __P((char *, char *)); -extern void stripquotes __P((char *)); extern int switch_map_find __P((char *, char *[], short [])); extern bool transienterror __P((int)); +#if _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI +extern void truncate_at_delim __P((char *, size_t, int)); +#endif /* _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI */ extern void tTflag __P((char *)); -extern void tTsetup __P((u_char *, int, char *)); +extern void tTsetup __P((unsigned char *, unsigned int, char *)); +extern SIGFUNC_DECL tick __P((int)); extern char *ttypath __P((void)); extern void unlockqueue __P((ENVELOPE *)); #if !HASUNSETENV extern void unsetenv __P((char *)); #endif /* !HASUNSETENV */ + +/* update file system information: +/- some blocks */ +#if SM_CONF_SHM +extern void upd_qs __P((ENVELOPE *, bool, bool)); +# define updfs(e, delete, avail) upd_qs(e, delete, avail) +#else /* SM_CONF_SHM */ +# define updfs(e, delete, avail) +#endif /* SM_CONF_SHM */ + extern char *username __P((void)); extern bool usershellok __P((char *, char *)); extern void vendor_post_defaults __P((ENVELOPE *)); extern void vendor_pre_defaults __P((ENVELOPE *)); extern int waitfor __P((pid_t)); extern bool writable __P((char *, ADDRESS *, long)); -extern char *xalloc __P((int)); -extern char *xcalloc __P((size_t, size_t)); -extern char *xrealloc __P((void *, size_t)); +#if SM_HEAP_CHECK +# define xalloc(size) xalloc_tagged(size, __FILE__, __LINE__) +extern char *xalloc_tagged __P((int, char*, int)); +#else /* SM_HEAP_CHECK */ +extern char *xalloc __P((int)); +#endif /* SM_HEAP_CHECK */ extern void xputs __P((const char *)); extern char *xtextify __P((char *, char *)); extern bool xtextok __P((char *)); -extern void xunlink __P((char *)); +extern int xunlink __P((char *)); extern char *xuntextify __P((char *)); -#endif /* _SENDMAIL_H */ + + +#endif /* ! _SENDMAIL_H */ diff --git a/gnu/usr.sbin/sendmail/sendmail/sfsasl.c b/gnu/usr.sbin/sendmail/sendmail/sfsasl.c index de922e34b06..984cd50bafd 100644 --- a/gnu/usr.sbin/sendmail/sendmail/sfsasl.c +++ b/gnu/usr.sbin/sendmail/sendmail/sfsasl.c @@ -8,40 +8,179 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: sfsasl.c,v 8.17.4.15 2001/07/11 17:37:07 gshapiro Exp $"; -#endif /* ! lint */ - -#if SFIO -# include <sfio/stdio.h> -#endif /* SFIO */ - +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: sfsasl.c,v 8.84 2001/09/03 22:35:38 gshapiro Exp $") #include <stdlib.h> #include <sendmail.h> +#include <errno.h> +#if SASL +# include <sasl.h> +# include "sfsasl.h" + +/* Structure used by the "sasl" file type */ +struct sasl_obj +{ + SM_FILE_T *fp; + sasl_conn_t *conn; +}; + +struct sasl_info +{ + SM_FILE_T *fp; + sasl_conn_t *conn; +}; -#if SASL && SFIO /* -** SASL +** SASL_GETINFO - returns requested information about a "sasl" file +** descriptor. +** +** Parameters: +** fp -- the file descriptor +** what -- the type of information requested +** valp -- the thang to return the information in +** +** Returns: +** -1 for unknown requests +** >=0 on success with valp filled in (if possible). */ -# include <sasl.h> -# include "sfsasl.h" +static int sasl_getinfo __P((SM_FILE_T *, int, void *)); + +static int +sasl_getinfo(fp, what, valp) + SM_FILE_T *fp; + int what; + void *valp; +{ + struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie; + + switch (what) + { + case SM_IO_WHAT_FD: + if (so->fp == NULL) + return -1; + return so->fp->f_file; /* for stdio fileno() compatability */ + + case SM_IO_IS_READABLE: + if (so->fp == NULL) + return 0; + + /* get info from underlying file */ + return sm_io_getinfo(so->fp, what, valp); + + default: + return -1; + } +} + +/* +** SASL_OPEN -- creates the sasl specific information for opening a +** file of the sasl type. +** +** Parameters: +** fp -- the file pointer associated with the new open +** info -- contains the sasl connection information pointer and +** the original SM_FILE_T that holds the open +** flags -- ignored +** rpool -- ignored +** +** Returns: +** 0 on success +*/ + +static int sasl_open __P((SM_FILE_T *, const void *, int, const void *)); + +/* ARGSUSED2 */ +static int +sasl_open(fp, info, flags, rpool) + SM_FILE_T *fp; + const void *info; + int flags; + const void *rpool; +{ + struct sasl_obj *so; + struct sasl_info *si = (struct sasl_info *) info; + + so = (struct sasl_obj *) sm_malloc(sizeof(struct sasl_obj)); + so->fp = si->fp; + so->conn = si->conn; + + /* + ** The underlying 'fp' is set to SM_IO_NOW so that the entire + ** encoded string is written in one chunk. Otherwise there is + ** the possibility that it may appear illegal, bogus or + ** mangled to the other side of the connection. + ** We will read or write through 'fp' since it is the opaque + ** connection for the communications. We need to treat it this + ** way in case the encoded string is to be sent down a TLS + ** connection rather than, say, sm_io's stdio. + */ + + (void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0); + fp->f_cookie = so; + return 0; +} + +/* +** SASL_CLOSE -- close the sasl specific parts of the sasl file pointer +** +** Parameters: +** fp -- the file pointer to close +** +** Returns: +** 0 on success +*/ + +static int sasl_close __P((SM_FILE_T *)); + +static int +sasl_close(fp) + SM_FILE_T *fp; +{ + struct sasl_obj *so; + + so = (struct sasl_obj *) fp->f_cookie; + if (so->fp != NULL) + { + sm_io_close(so->fp, SM_TIME_DEFAULT); + so->fp = NULL; + } + sm_free(so); + so = NULL; + return 0; +} /* how to deallocate a buffer allocated by SASL */ -# define SASL_DEALLOC(b) sm_free(b) +extern void sm_sasl_free __P((void *)); +# define SASL_DEALLOC(b) sm_sasl_free(b) + +/* +** SASL_READ -- read encrypted information and decrypt it for the caller +** +** Parameters: +** fp -- the file pointer +** buf -- the location to place the decrypted information +** size -- the number of bytes to read after decryption +** +** Results: +** -1 on error +** otherwise the number of bytes read +*/ + +static ssize_t sasl_read __P((SM_FILE_T *, char *, size_t)); static ssize_t -sasl_read(f, buf, size, disc) - Sfio_t *f; - Void_t *buf; +sasl_read(fp, buf, size) + SM_FILE_T *fp; + char *buf; size_t size; - Sfdisc_t *disc; { - int len, result; + int result; + ssize_t len; static char *outbuf = NULL; static unsigned int outlen = 0; static unsigned int offset = 0; - Sasldisc_t *sd = (Sasldisc_t *) disc; + struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie; /* ** sasl_decode() may require more data than a single read() returns. @@ -54,10 +193,11 @@ sasl_read(f, buf, size, disc) while (outbuf == NULL && outlen == 0) { - len = sfrd(f, buf, size, disc); + len = sm_io_read(so->fp, SM_TIME_DEFAULT, buf, size); if (len <= 0) return len; - result = sasl_decode(sd->conn, buf, len, &outbuf, &outlen); + result = sasl_decode(so->conn, buf, + (unsigned int) len, &outbuf, &outlen); if (result != SASL_OK) { outbuf = NULL; @@ -67,66 +207,108 @@ sasl_read(f, buf, size, disc) } } - if (outbuf != NULL) + if (outbuf == NULL) { - if (outlen - offset > size) - { - /* return another part of the buffer */ - (void) memcpy(buf, outbuf + offset, (size_t) size); - offset += size; - result = size; - } - else - { - /* return the rest of the buffer */ - result = outlen - offset; - (void) memcpy(buf, outbuf + offset, (size_t) result); - SASL_DEALLOC(outbuf); - outbuf = NULL; - offset = 0; - outlen = 0; - } + /* be paranoid: outbuf == NULL but outlen != 0 */ + syserr("@sasl_read failure: outbuf == NULL but outlen != 0"); + /* NOTREACHED */ + } + if (outlen - offset > size) + { + /* return another part of the buffer */ + (void) memcpy(buf, outbuf + offset, size); + offset += size; + len = size; } else { - /* be paranoid: outbuf == NULL but outlen != 0 */ - syserr("!sasl_read failure: outbuf == NULL but outlen != 0"); + /* return the rest of the buffer */ + len = outlen - offset; + (void) memcpy(buf, outbuf + offset, (size_t) len); + SASL_DEALLOC(outbuf); + outbuf = NULL; + offset = 0; + outlen = 0; } - return result; + return len; } +/* +** SASL_WRITE -- write information out after encrypting it +** +** Parameters: +** fp -- the file pointer +** buf -- holds the data to be encrypted and written +** size -- the number of bytes to have encrypted and written +** +** Returns: +** -1 on error +** otherwise number of bytes written +*/ + +static ssize_t sasl_write __P((SM_FILE_T *, const char *, size_t)); + static ssize_t -sasl_write(f, buf, size, disc) - Sfio_t *f; - const Void_t *buf; +sasl_write(fp, buf, size) + SM_FILE_T *fp; + const char *buf; size_t size; - Sfdisc_t *disc; { int result; char *outbuf; unsigned int outlen; - Sasldisc_t *sd = (Sasldisc_t *) disc; + size_t ret = 0, total = 0; + struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie; - result = sasl_encode(sd->conn, buf, size, &outbuf, &outlen); + result = sasl_encode(so->conn, buf, + (unsigned int) size, &outbuf, &outlen); if (result != SASL_OK) return -1; if (outbuf != NULL) { - sfwr(f, outbuf, outlen, disc); + while (outlen > 0) + { + ret = sm_io_write(so->fp, SM_TIME_DEFAULT, + &outbuf[total], outlen); + outlen -= ret; + total += ret; + } SASL_DEALLOC(outbuf); } return size; } +/* +** SFDCSASL -- create sasl file type and open in and out file pointers +** for sendmail to read from and write to. +** +** Parameters: +** fin -- the sm_io file encrypted data to be read from +** fout -- the sm_io file encrypted data to be writen to +** conn -- the sasl connection pointer +** +** Returns: +** -1 on error +** 0 on success +** +** Side effects: +** The arguments "fin" and "fout" are replaced with the new +** SM_FILE_T pointers. +*/ + int sfdcsasl(fin, fout, conn) - Sfio_t *fin; - Sfio_t *fout; + SM_FILE_T **fin; + SM_FILE_T **fout; sasl_conn_t *conn; { - Sasldisc_t *saslin, *saslout; + SM_FILE_T *newin, *newout; + SM_FILE_T SM_IO_SET_TYPE(sasl_vector, "sasl", sasl_open, sasl_close, + sasl_read, sasl_write, NULL, sasl_getinfo, NULL, + SM_TIME_FOREVER); + struct sasl_info info; if (conn == NULL) { @@ -134,265 +316,392 @@ sfdcsasl(fin, fout, conn) return 0; } - saslin = (Sasldisc_t *) xalloc(sizeof(Sasldisc_t)); - saslout = (Sasldisc_t *) xalloc(sizeof(Sasldisc_t)); - saslin->disc.readf = sasl_read; - saslin->disc.writef = sasl_write; - saslin->disc.seekf = NULL; - saslin->disc.exceptf = NULL; + SM_IO_INIT_TYPE(sasl_vector, "sasl", sasl_open, sasl_close, + sasl_read, sasl_write, NULL, sasl_getinfo, NULL, + SM_TIME_FOREVER); + info.fp = *fin; + info.conn = conn; + newin = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info, SM_IO_RDONLY, + NULL); - saslout->disc.readf = sasl_read; - saslout->disc.writef = sasl_write; - saslout->disc.seekf = NULL; - saslout->disc.exceptf = NULL; + if (newin == NULL) + return -1; - saslin->conn = conn; - saslout->conn = conn; + info.fp = *fout; + info.conn = conn; + newout = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info, SM_IO_WRONLY, + NULL); - if (sfdisc(fin, (Sfdisc_t *) saslin) != (Sfdisc_t *) saslin || - sfdisc(fout, (Sfdisc_t *) saslout) != (Sfdisc_t *) saslout) + if (newout == NULL) { - sm_free(saslin); - sm_free(saslout); + (void) sm_io_close(newin, SM_TIME_DEFAULT); return -1; } + sm_io_automode(newin, newout); + + *fin = newin; + *fout = newout; return 0; } -#endif /* SASL && SFIO */ +#endif /* SASL */ + +#if STARTTLS +# include "sfsasl.h" +# include <openssl/err.h> + +/* Structure used by the "tls" file type */ +struct tls_obj +{ + SM_FILE_T *fp; + SSL *con; +}; + +struct tls_info +{ + SM_FILE_T *fp; + SSL *con; +}; -#if STARTTLS && (SFIO || _FFR_TLS_TOREK) /* -** STARTTLS +** TLS_GETINFO - returns requested information about a "tls" file +** descriptor. +** +** Parameters: +** fp -- the file descriptor +** what -- the type of information requested +** valp -- the thang to return the information in (unused) +** +** Returns: +** -1 for unknown requests +** >=0 on success with valp filled in (if possible). */ -# include "sfsasl.h" -# include <openssl/err.h> +static int tls_getinfo __P((SM_FILE_T *, int, void *)); -# if SFIO -static ssize_t -tls_read(f, buf, size, disc) - Sfio_t *f; - Void_t *buf; - size_t size; - Sfdisc_t *disc; -# else /* SFIO */ +/* ARGSUSED2 */ static int -tls_read(disc, buf, size) - void *disc; - char *buf; - int size; -# endif /* SFIO */ +tls_getinfo(fp, what, valp) + SM_FILE_T *fp; + int what; + void *valp; { - int r; - Tlsdisc_t *sd; + struct tls_obj *so = (struct tls_obj *) fp->f_cookie; - /* Cast back to correct type */ - sd = (Tlsdisc_t *) disc; - - r = SSL_read(sd->con, (char *) buf, size); - if (r < 0 && LogLevel > 7) + switch (what) { - char *err; + case SM_IO_WHAT_FD: + if (so->fp == NULL) + return -1; + return so->fp->f_file; /* for stdio fileno() compatability */ + + case SM_IO_IS_READABLE: + return SSL_pending(so->con) > 0; + + default: + return -1; + } +} - err = NULL; - switch (SSL_get_error(sd->con, r)) - { - case SSL_ERROR_NONE: - break; - case SSL_ERROR_WANT_WRITE: - err = "write W BLOCK"; - break; - case SSL_ERROR_WANT_READ: - err = "write R BLOCK"; - break; - case SSL_ERROR_WANT_X509_LOOKUP: - err = "write X BLOCK"; - break; - case SSL_ERROR_ZERO_RETURN: - break; - case SSL_ERROR_SYSCALL: - err = "syscall error"; /* - get_last_socket_error()); +** TLS_OPEN -- creates the tls specific information for opening a +** file of the tls type. +** +** Parameters: +** fp -- the file pointer associated with the new open +** info -- the sm_io file pointer holding the open and the +** TLS encryption connection to be read from or written to +** flags -- ignored +** rpool -- ignored +** +** Returns: +** 0 on success */ - break; - case SSL_ERROR_SSL: - err = "generic SSL error"; - break; - } - if (err != NULL) - sm_syslog(LOG_WARNING, NOQID, "TLS: read error: %s", - err); + +static int tls_open __P((SM_FILE_T *, const void *, int, const void *)); + +/* ARGSUSED2 */ +static int +tls_open(fp, info, flags, rpool) + SM_FILE_T *fp; + const void *info; + int flags; + const void *rpool; +{ + struct tls_obj *so; + struct tls_info *ti = (struct tls_info *) info; + + so = (struct tls_obj *) sm_malloc(sizeof(struct tls_obj)); + so->fp = ti->fp; + so->con = ti->con; + + /* + ** We try to get the "raw" file descriptor that TLS uses to + ** do the actual read/write with. This is to allow us control + ** over the file descriptor being a blocking or non-blocking type. + ** Under the covers TLS handles the change and this allows us + ** to do timeouts with sm_io. + */ + + fp->f_file = sm_io_getinfo(so->fp, SM_IO_WHAT_FD, NULL); + (void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0); + fp->f_cookie = so; + return 0; +} + +/* +** TLS_CLOSE -- close the tls specific parts of the tls file pointer +** +** Parameters: +** fp -- the file pointer to close +** +** Returns: +** 0 on success +*/ + +static int tls_close __P((SM_FILE_T *)); + +static int +tls_close(fp) + SM_FILE_T *fp; +{ + struct tls_obj *so; + + so = (struct tls_obj *) fp->f_cookie; + if (so->fp != NULL) + { + sm_io_close(so->fp, SM_TIME_DEFAULT); + so->fp = NULL; } - return r; + sm_free(so); + so = NULL; + return 0; } -# if SFIO +/* maximum number of retries for TLS related I/O due to handshakes */ +# define MAX_TLS_IOS 4 + +/* +** TLS_READ -- read secured information for the caller +** +** Parameters: +** fp -- the file pointer +** buf -- the location to place the data +** size -- the number of bytes to read from connection +** +** Results: +** -1 on error +** otherwise the number of bytes read +*/ + +static ssize_t tls_read __P((SM_FILE_T *, char *, size_t)); + static ssize_t -tls_write(f, buf, size, disc) - Sfio_t *f; - const Void_t *buf; +tls_read(fp, buf, size) + SM_FILE_T *fp; + char *buf; size_t size; - Sfdisc_t *disc; -# else /* SFIO */ -static int -tls_write(disc, buf, size) - void *disc; - const char *buf; - int size; -# endif /* SFIO */ { int r; - Tlsdisc_t *sd; + static int again = MAX_TLS_IOS; + struct tls_obj *so = (struct tls_obj *) fp->f_cookie; + char *err; - /* Cast back to correct type */ - sd = (Tlsdisc_t *) disc; + r = SSL_read(so->con, (char *) buf, size); - r = SSL_write(sd->con, (char *)buf, size); - if (r < 0 && LogLevel > 7) + if (r > 0) { - char *err; + again = MAX_TLS_IOS; + return r; + } - err = NULL; - switch (SSL_get_error(sd->con, r)) - { - case SSL_ERROR_NONE: - break; - case SSL_ERROR_WANT_WRITE: - err = "write W BLOCK"; - break; - case SSL_ERROR_WANT_READ: - err = "write R BLOCK"; - break; - case SSL_ERROR_WANT_X509_LOOKUP: - err = "write X BLOCK"; - break; - case SSL_ERROR_ZERO_RETURN: - break; - case SSL_ERROR_SYSCALL: - err = "syscall error"; -/* - get_last_socket_error()); -*/ - break; - case SSL_ERROR_SSL: - err = "generic SSL error"; + err = NULL; + switch (SSL_get_error(so->con, r)) + { + case SSL_ERROR_NONE: + case SSL_ERROR_ZERO_RETURN: + again = MAX_TLS_IOS; + break; + case SSL_ERROR_WANT_WRITE: + if (--again <= 0) + err = "read W BLOCK"; + else + errno = EAGAIN; + break; + case SSL_ERROR_WANT_READ: + if (--again <= 0) + err = "read R BLOCK"; + else + errno = EAGAIN; + break; + case SSL_ERROR_WANT_X509_LOOKUP: + err = "write X BLOCK"; + break; + case SSL_ERROR_SYSCALL: + if (r == 0 && errno == 0) /* out of protocol EOF found */ + break; + err = "syscall error"; /* - ERR_GET_REASON(ERR_peek_error())); + get_last_socket_error()); */ - break; - } - if (err != NULL) - sm_syslog(LOG_WARNING, NOQID, "TLS: write error: %s", - err); + break; + case SSL_ERROR_SSL: + err = "generic SSL error"; + if (LogLevel > 9) + tlslogerr("read"); + break; + } + if (err != NULL) + { + again = MAX_TLS_IOS; + if (errno == 0) + errno = EIO; + if (LogLevel > 7) + sm_syslog(LOG_WARNING, NOQID, + "STARTTLS: read error=%s (%d)", err, r); } return r; } -# if !SFIO -static int -tls_close(cookie) - void *cookie; +/* +** TLS_WRITE -- write information out through secure connection +** +** Parameters: +** fp -- the file pointer +** buf -- holds the data to be securely written +** size -- the number of bytes to write +** +** Returns: +** -1 on error +** otherwise number of bytes written +*/ + +static ssize_t tls_write __P((SM_FILE_T *, const char *, size_t)); + +static ssize_t +tls_write(fp, buf, size) + SM_FILE_T *fp; + const char *buf; + size_t size; { - int retval = 0; - Tlsdisc_t *tc; + int r; + static int again = MAX_TLS_IOS; + struct tls_obj *so = (struct tls_obj *) fp->f_cookie; + char *err; - /* Cast back to correct type */ - tc = (Tlsdisc_t *)cookie; + r = SSL_write(so->con, (char *) buf, size); - if (tc->fp != NULL) + if (r > 0) { - retval = fclose(tc->fp); - tc->fp = NULL; + again = MAX_TLS_IOS; + return r; } - - sm_free(tc); - return retval; + err = NULL; + switch (SSL_get_error(so->con, r)) + { + case SSL_ERROR_NONE: + case SSL_ERROR_ZERO_RETURN: + again = MAX_TLS_IOS; + break; + case SSL_ERROR_WANT_WRITE: + if (--again <= 0) + err = "write W BLOCK"; + else + errno = EAGAIN; + break; + case SSL_ERROR_WANT_READ: + if (--again <= 0) + err = "write R BLOCK"; + else + errno = EAGAIN; + break; + case SSL_ERROR_WANT_X509_LOOKUP: + err = "write X BLOCK"; + break; + case SSL_ERROR_SYSCALL: + if (r == 0 && errno == 0) /* out of protocol EOF found */ + break; + err = "syscall error"; +/* + get_last_socket_error()); +*/ + break; + case SSL_ERROR_SSL: + err = "generic SSL error"; +/* + ERR_GET_REASON(ERR_peek_error())); +*/ + if (LogLevel > 9) + tlslogerr("write"); + break; + } + if (err != NULL) + { + again = MAX_TLS_IOS; + if (errno == 0) + errno = EIO; + if (LogLevel > 7) + sm_syslog(LOG_WARNING, NOQID, + "STARTTLS: write error=%s (%d)", err, r); + } + return r; } -# endif /* !SFIO */ + +/* +** SFDCTLS -- create tls file type and open in and out file pointers +** for sendmail to read from and write to. +** +** Parameters: +** fin -- data input source being replaced +** fout -- data output source being replaced +** conn -- the tls connection pointer +** +** Returns: +** -1 on error +** 0 on success +** +** Side effects: +** The arguments "fin" and "fout" are replaced with the new +** SM_FILE_T pointers. +** The original "fin" and "fout" are preserved in the tls file +** type but are not actually used because of the design of TLS. +*/ int sfdctls(fin, fout, con) -# if SFIO - Sfio_t *fin; - Sfio_t *fout; -# else /* SFIO */ - FILE **fin; - FILE **fout; -# endif /* SFIO */ + SM_FILE_T **fin; + SM_FILE_T **fout; SSL *con; { - Tlsdisc_t *tlsin, *tlsout; -# if !SFIO - FILE *fp; -# else /* !SFIO */ - int rfd, wfd; -# endif /* !SFIO */ - - if (con == NULL) - return 0; - - tlsin = (Tlsdisc_t *) xalloc(sizeof(Tlsdisc_t)); - tlsout = (Tlsdisc_t *) xalloc(sizeof(Tlsdisc_t)); -# if SFIO - tlsin->disc.readf = tls_read; - tlsin->disc.writef = tls_write; - tlsin->disc.seekf = NULL; - tlsin->disc.exceptf = NULL; - tlsin->con = con; - - tlsout->disc.readf = tls_read; - tlsout->disc.writef = tls_write; - tlsout->disc.seekf = NULL; - tlsout->disc.exceptf = NULL; - tlsout->con = con; - - rfd = fileno(fin); - wfd = fileno(fout); - if (rfd < 0 || wfd < 0 || - SSL_set_rfd(con, rfd) <= 0 || SSL_set_wfd(con, wfd) <= 0) - { - sm_free(tlsin); - sm_free(tlsout); - return -1; - } - if (sfdisc(fin, (Sfdisc_t *) tlsin) != (Sfdisc_t *) tlsin || - sfdisc(fout, (Sfdisc_t *) tlsout) != (Sfdisc_t *) tlsout) - { - sm_free(tlsin); - sm_free(tlsout); + SM_FILE_T *tlsin, *tlsout; + SM_FILE_T SM_IO_SET_TYPE(tls_vector, "tls", tls_open, tls_close, + tls_read, tls_write, NULL, tls_getinfo, NULL, + SM_TIME_FOREVER); + struct tls_info info; + + SM_ASSERT(con != NULL); + + SM_IO_INIT_TYPE(tls_vector, "tls", tls_open, tls_close, + tls_read, tls_write, NULL, tls_getinfo, NULL, + SM_TIME_FOREVER); + info.fp = *fin; + info.con = con; + tlsin = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_RDONLY, + NULL); + if (tlsin == NULL) return -1; - } -# else /* SFIO */ - tlsin->fp = *fin; - tlsin->con = con; - fp = funopen(tlsin, tls_read, tls_write, NULL, tls_close); - if (fp == NULL) - { - sm_free(tlsin); - return -1; - } - *fin = fp; - tlsout->fp = *fout; - tlsout->con = con; - fp = funopen(tlsout, tls_read, tls_write, NULL, tls_close); - if (fp == NULL) + info.fp = *fout; + tlsout = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_WRONLY, + NULL); + if (tlsout == NULL) { - FILE *save; - - /* Hack: Don't close underlying fp */ - save = tlsin->fp; - tlsin->fp = NULL; - fclose(*fin); - *fin = save; - sm_free(tlsout); + (void) sm_io_close(tlsin, SM_TIME_DEFAULT); return -1; } - *fout = fp; - SSL_set_rfd(con, fileno(tlsin->fp)); - SSL_set_wfd(con, fileno(tlsout->fp)); -# endif /* SFIO */ + sm_io_automode(tlsin, tlsout); + + *fin = tlsin; + *fout = tlsout; return 0; } -#endif /* STARTTLS && (SFIO || _FFR_TLS_TOREK) */ +#endif /* STARTTLS */ diff --git a/gnu/usr.sbin/sendmail/sendmail/sfsasl.h b/gnu/usr.sbin/sendmail/sendmail/sfsasl.h index 493b8d3b1cd..3bb2d4ae079 100644 --- a/gnu/usr.sbin/sendmail/sendmail/sfsasl.h +++ b/gnu/usr.sbin/sendmail/sendmail/sfsasl.h @@ -6,55 +6,18 @@ * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * - * $Sendmail: sfsasl.h,v 8.13.4.4 2000/07/18 18:44:51 gshapiro Exp $" + * $Sendmail: sfsasl.h,v 8.17 2000/09/19 21:30:49 ca Exp $" */ #ifndef SFSASL_H # define SFSASL_H -# if SFIO -# include <sfio.h> -# endif /* SFIO */ - -# if SASL -# if SFIO - -/* sf discipline to add sasl */ -typedef struct _sasldisc -{ - Sfdisc_t disc; - sasl_conn_t *conn; -} Sasldisc_t; - -extern int sfdcsasl __P((Sfio_t *, Sfio_t *, sasl_conn_t *)); - -# endif /* SFIO */ -# endif /* SASL */ +#if SASL +extern int sfdcsasl __P((SM_FILE_T **, SM_FILE_T **, sasl_conn_t *)); +#endif /* SASL */ # if STARTTLS -# if SFIO - -/* sf discipline to add tls */ -typedef struct _tlsdisc -{ - Sfdisc_t disc; - SSL *con; -} Tlsdisc_t; - -extern int sfdctls __P((Sfio_t *, Sfio_t *, SSL *)); - -# else /* SFIO */ -# if _FFR_TLS_TOREK - -typedef struct tls_conn -{ - FILE *fp; /* original FILE * */ - SSL *con; /* SSL context */ -} Tlsdisc_t; - -extern int sfdctls __P((FILE **, FILE **, SSL *)); - -# endif /* _FFR_TLS_TOREK */ -# endif /* SFIO */ +extern int sfdctls __P((SM_FILE_T **, SM_FILE_T **, SSL *)); # endif /* STARTTLS */ + #endif /* ! SFSASL_H */ diff --git a/gnu/usr.sbin/sendmail/sendmail/shmticklib.c b/gnu/usr.sbin/sendmail/sendmail/shmticklib.c index e2a228fe746..395624de032 100644 --- a/gnu/usr.sbin/sendmail/sendmail/shmticklib.c +++ b/gnu/usr.sbin/sendmail/sendmail/shmticklib.c @@ -10,16 +10,10 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: shmticklib.c,v 8.6 2000/02/26 01:32:27 gshapiro Exp $"; -#endif /* ! lint */ +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: shmticklib.c,v 8.11 2000/09/04 19:13:19 ca Exp $") #if _FFR_SHM_STATUS -# if SFIO -# include <sfio/stdio.h> -# else /* !SFIO */ -# include <stdio.h> -# endif /* SFIO */ # include <sys/types.h> # include <sys/ipc.h> # include <sys/shm.h> @@ -54,10 +48,10 @@ shmtick(inc_me, what) if (shmid < 0) return; } - if ((unsigned long *)sp == (unsigned long *)-1) + if ((unsigned long *) sp == (unsigned long *)-1) { - sp = (STATUSD_SHM *)shmat(shmid, NULL, 0); - if ((unsigned long *)sp == (unsigned long *)-1) + sp = (STATUSD_SHM *) shmat(shmid, NULL, 0); + if ((unsigned long *) sp == (unsigned long *) -1) return; } if (sp->magic != STATUSD_MAGIC) diff --git a/gnu/usr.sbin/sendmail/sendmail/srvrsmtp.c b/gnu/usr.sbin/sendmail/sendmail/srvrsmtp.c index 6f475909ea3..ab31a677a81 100644 --- a/gnu/usr.sbin/sendmail/sendmail/srvrsmtp.c +++ b/gnu/usr.sbin/sendmail/sendmail/srvrsmtp.c @@ -11,69 +11,88 @@ * */ - #include <sendmail.h> - -#ifndef lint -# if SMTP -static char id[] = "@(#)$Sendmail: srvrsmtp.c,v 8.471.2.2.2.78 2001/06/26 18:52:21 gshapiro Exp $ (with SMTP)"; -# else /* SMTP */ -static char id[] = "@(#)$Sendmail: srvrsmtp.c,v 8.471.2.2.2.78 2001/06/26 18:52:21 gshapiro Exp $ (without SMTP)"; -# endif /* SMTP */ -#endif /* ! lint */ - -#if SMTP -# if SASL || STARTTLS -# include "sfsasl.h" -# endif /* SASL || STARTTLS */ -# if SASL -# define ENC64LEN(l) (((l) + 2) * 4 / 3 + 1) +#if MILTER +# include <libmilter/mfdef.h> +#endif /* MILTER */ + +SM_RCSID("@(#)$Sendmail: srvrsmtp.c,v 8.778 2001/09/04 22:43:06 ca Exp $") + +#if SASL || STARTTLS +# include <sys/time.h> +# include "sfsasl.h" +#endif /* SASL || STARTTLS */ +#if SASL +# define ENC64LEN(l) (((l) + 2) * 4 / 3 + 1) static int saslmechs __P((sasl_conn_t *, char **)); -# endif /* SASL */ -# if STARTTLS -# include <sysexits.h> -# include <openssl/err.h> -# include <openssl/bio.h> -# include <openssl/pem.h> -# ifndef HASURANDOMDEV -# include <openssl/rand.h> -# endif /* !HASURANDOMDEV */ - -static SSL *srv_ssl = NULL; -static SSL_CTX *srv_ctx = NULL; -# if !TLS_NO_RSA -static RSA *rsa = NULL; -# endif /* !TLS_NO_RSA */ -static bool tls_ok_srv = FALSE; -static int tls_verify_cb __P((X509_STORE_CTX *)); -# if !TLS_NO_RSA -# define RSA_KEYLENGTH 512 -# endif /* !TLS_NO_RSA */ -# endif /* STARTTLS */ - -static time_t checksmtpattack __P((volatile int *, int, bool, +#endif /* SASL */ +#if STARTTLS +# include <sysexits.h> + +static SSL_CTX *srv_ctx = NULL; /* TLS server context */ +static SSL *srv_ssl = NULL; /* per connection context */ + +static bool tls_ok_srv = false; + +extern void tls_set_verify __P((SSL_CTX *, SSL *, bool)); +# define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \ + bitset(SRV_VRFY_CLT, features)) +#endif /* STARTTLS */ + +/* server features */ +#define SRV_NONE 0x0000 /* none... */ +#define SRV_OFFER_TLS 0x0001 /* offer STARTTLS */ +#define SRV_VRFY_CLT 0x0002 /* request a cert */ +#define SRV_OFFER_AUTH 0x0004 /* offer AUTH */ +#define SRV_OFFER_ETRN 0x0008 /* offer ETRN */ +#define SRV_OFFER_VRFY 0x0010 /* offer VRFY (not yet used) */ +#define SRV_OFFER_EXPN 0x0020 /* offer EXPN */ +#define SRV_OFFER_VERB 0x0040 /* offer VERB */ +#define SRV_OFFER_DSN 0x0080 /* offer DSN */ +#if PIPELINING +# define SRV_OFFER_PIPE 0x0100 /* offer PIPELINING */ +# if _FFR_NO_PIPE +# define SRV_NO_PIPE 0x0200 /* disable PIPELINING, sleep if used */ +# endif /* _FFR_NO_PIPE */ +#endif /* PIPELINING */ +#define SRV_REQ_AUTH 0x0400 /* require AUTH */ +#define SRV_TMP_FAIL 0x1000 /* ruleset caused a temporary failure */ + +static unsigned int srvfeatures __P((ENVELOPE *, char *, unsigned int)); + +static time_t checksmtpattack __P((volatile unsigned int *, int, bool, char *, ENVELOPE *)); static void mail_esmtp_args __P((char *, char *, ENVELOPE *)); static void printvrfyaddr __P((ADDRESS *, bool, bool)); static void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *)); -static int runinchild __P((char *, ENVELOPE *)); static char *skipword __P((char *volatile, char *)); +static void setup_smtpd_io __P((void)); extern ENVELOPE BlankEnvelope; +#define SKIP_SPACE(s) while (isascii(*s) && isspace(*s)) \ + (s)++ + /* ** SMTP -- run the SMTP protocol. ** ** Parameters: ** nullserver -- if non-NULL, rejection message for -** all SMTP commands. +** (almost) all SMTP commands. +** d_flags -- daemon flags ** e -- the envelope. ** ** Returns: ** never. ** ** Side Effects: -** Reads commands from the input channel and processes -** them. +** Reads commands from the input channel and processes them. +*/ + +/* +** Notice: The smtp server doesn't have a session context like the client +** side has (mci). Therefore some data (session oriented) is allocated +** or assigned to the "wrong" structure (esp. STARTTLS, AUTH). +** This should be fixed in a successor version. */ struct cmd @@ -83,40 +102,37 @@ struct cmd }; /* values for cmd_code */ -# define CMDERROR 0 /* bad command */ -# define CMDMAIL 1 /* mail -- designate sender */ -# define CMDRCPT 2 /* rcpt -- designate recipient */ -# define CMDDATA 3 /* data -- send message text */ -# define CMDRSET 4 /* rset -- reset state */ -# define CMDVRFY 5 /* vrfy -- verify address */ -# define CMDEXPN 6 /* expn -- expand address */ -# define CMDNOOP 7 /* noop -- do nothing */ -# define CMDQUIT 8 /* quit -- close connection and die */ -# define CMDHELO 9 /* helo -- be polite */ -# define CMDHELP 10 /* help -- give usage info */ -# define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ -# define CMDETRN 12 /* etrn -- flush queue */ -# if SASL -# define CMDAUTH 13 /* auth -- SASL authenticate */ -# endif /* SASL */ -# if STARTTLS -# define CMDSTLS 14 /* STARTTLS -- start TLS session */ -# endif /* STARTTLS */ +#define CMDERROR 0 /* bad command */ +#define CMDMAIL 1 /* mail -- designate sender */ +#define CMDRCPT 2 /* rcpt -- designate recipient */ +#define CMDDATA 3 /* data -- send message text */ +#define CMDRSET 4 /* rset -- reset state */ +#define CMDVRFY 5 /* vrfy -- verify address */ +#define CMDEXPN 6 /* expn -- expand address */ +#define CMDNOOP 7 /* noop -- do nothing */ +#define CMDQUIT 8 /* quit -- close connection and die */ +#define CMDHELO 9 /* helo -- be polite */ +#define CMDHELP 10 /* help -- give usage info */ +#define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ +#define CMDETRN 12 /* etrn -- flush queue */ +#if SASL +# define CMDAUTH 13 /* auth -- SASL authenticate */ +#endif /* SASL */ +#if STARTTLS +# define CMDSTLS 14 /* STARTTLS -- start TLS session */ +#endif /* STARTTLS */ /* non-standard commands */ -# define CMDONEX 16 /* onex -- sending one transaction only */ -# define CMDVERB 17 /* verb -- go into verbose mode */ -# define CMDXUSR 18 /* xusr -- initial (user) submission */ +#define CMDVERB 17 /* verb -- go into verbose mode */ /* unimplemented commands from RFC 821 */ -# define CMDUNIMPL 19 /* unimplemented rfc821 commands */ +#define CMDUNIMPL 19 /* unimplemented rfc821 commands */ /* use this to catch and log "door handle" attempts on your system */ -# define CMDLOGBOGUS 23 /* bogus command that should be logged */ +#define CMDLOGBOGUS 23 /* bogus command that should be logged */ /* debugging-only commands, only enabled if SMTPDEBUG is defined */ -# define CMDDBGQSHOW 24 /* showq -- show send queue */ -# define CMDDBGDEBUG 25 /* debug -- set debug mode */ +#define CMDDBGQSHOW 24 /* showq -- show send queue */ +#define CMDDBGDEBUG 25 /* debug -- set debug mode */ /* -** Note: If you change this list, -** remember to update 'helpfile' +** Note: If you change this list, remember to update 'helpfile' */ static struct cmd CmdTab[] = @@ -134,18 +150,16 @@ static struct cmd CmdTab[] = { "ehlo", CMDEHLO }, { "etrn", CMDETRN }, { "verb", CMDVERB }, - { "onex", CMDONEX }, - { "xusr", CMDXUSR }, { "send", CMDUNIMPL }, { "saml", CMDUNIMPL }, { "soml", CMDUNIMPL }, { "turn", CMDUNIMPL }, -# if SASL +#if SASL { "auth", CMDAUTH, }, -# endif /* SASL */ -# if STARTTLS +#endif /* SASL */ +#if STARTTLS { "starttls", CMDSTLS, }, -# endif /* STARTTLS */ +#endif /* STARTTLS */ /* remaining commands are here only to trap and log attempts to use them */ { "showq", CMDDBGQSHOW }, { "debug", CMDDBGDEBUG }, @@ -154,20 +168,163 @@ static struct cmd CmdTab[] = { NULL, CMDERROR } }; -static bool OneXact = FALSE; /* one xaction only this run */ static char *CurSmtpClient; /* who's at the other end of channel */ -# define MAXBADCOMMANDS 25 /* maximum number of bad commands */ -# define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */ -# define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */ -# define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */ -# define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */ -# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */ +#ifndef MAXBADCOMMANDS +# define MAXBADCOMMANDS 25 /* maximum number of bad commands */ +#endif +#ifndef MAXNOOPCOMMANDS +# define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */ +#endif +#ifndef MAXHELOCOMMANDS +# define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */ +#endif +#ifndef MAXVRFYCOMMANDS +# define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */ +#endif +#ifndef MAXETRNCOMMANDS +# define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */ +#endif +#ifndef MAXTIMEOUT +# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */ +#endif + +#if SM_HEAP_CHECK +static SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp", + "@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $"); +#endif /* SM_HEAP_CHECK */ + +typedef struct +{ + bool sm_gotmail; /* mail command received */ + unsigned int sm_nrcpts; /* number of RCPT commands */ +#if _FFR_ADAPTIVE_EOL + bool sm_crlf; /* input in CRLF form? */ +#endif /* _FFR_ADAPTIVE_EOL */ + bool sm_discard; +#if MILTER + bool sm_milterize; + bool sm_milterlist; /* any filters in the list? */ +#endif /* MILTER */ +} SMTP_T; + +static void smtp_data __P((SMTP_T *, ENVELOPE *)); + +#define MSG_TEMPFAIL "451 4.7.1 Please try again later" + +#if MILTER +# define MILTER_ABORT(e) milter_abort((e)) +# define MILTER_REPLY(str) \ + { \ + int savelogusrerrs = LogUsrErrs; \ + \ + switch (state) \ + { \ + case SMFIR_REPLYCODE: \ + if (MilterLogLevel > 3) \ + { \ + sm_syslog(LOG_INFO, e->e_id, \ + "Milter: %s=%s, reject=%s", \ + str, addr, response); \ + LogUsrErrs = false; \ + } \ + usrerr(response); \ + break; \ + \ + case SMFIR_REJECT: \ + if (MilterLogLevel > 3) \ + { \ + sm_syslog(LOG_INFO, e->e_id, \ + "Milter: %s=%s, reject=550 5.7.1 Command rejected", \ + str, addr); \ + LogUsrErrs = false; \ + } \ + usrerr("550 5.7.1 Command rejected"); \ + break; \ + \ + case SMFIR_DISCARD: \ + if (MilterLogLevel > 3) \ + sm_syslog(LOG_INFO, e->e_id, \ + "Milter: %s=%s, discard", \ + str, addr); \ + e->e_flags |= EF_DISCARD; \ + break; \ + \ + case SMFIR_TEMPFAIL: \ + if (MilterLogLevel > 3) \ + { \ + sm_syslog(LOG_INFO, e->e_id, \ + "Milter: %s=%s, reject=%s", \ + str, addr, MSG_TEMPFAIL); \ + LogUsrErrs = false; \ + } \ + usrerr(MSG_TEMPFAIL); \ + break; \ + } \ + LogUsrErrs = savelogusrerrs; \ + if (response != NULL) \ + sm_free(response); /* XXX */ \ + } + +#else /* MILTER */ +# define MILTER_ABORT(e) +#endif /* MILTER */ + +/* clear all SMTP state (for HELO/EHLO/RSET) */ +#define CLEAR_STATE(cmd) \ +{ \ + /* abort milter filters */ \ + MILTER_ABORT(e); \ + \ + if (smtp.sm_nrcpts > 0) \ + { \ + logundelrcpts(e, cmd, 10, false); \ + smtp.sm_nrcpts = 0; \ + macdefine(&e->e_macro, A_PERM, \ + macid("{nrcpts}"), "0"); \ + } \ + \ + e->e_sendqueue = NULL; \ + e->e_flags |= EF_CLRQUEUE; \ + \ + if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) \ + logsender(e, NULL); \ + e->e_flags &= ~EF_LOGSENDER; \ + \ + /* clean up a bit */ \ + smtp.sm_gotmail = false; \ + SuprErrs = true; \ + dropenvelope(e, true, false); \ + sm_rpool_free(e->e_rpool); \ + e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL)); \ + CurEnv = e; \ +} + +/* sleep to flatten out connection load */ +#define MIN_DELAY_LOG 15 /* wait before logging this again */ + +/* is it worth setting the process title for 1s? */ +#define DELAY_CONN(cmd) \ + if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA) \ + { \ + time_t dnow; \ + \ + sm_setproctitle(true, e, \ + "%s: %s: delaying %s: load average: %d", \ + qid_printname(e), CurSmtpClient, \ + cmd, DelayLA); \ + if (LogLevel > 8 && (dnow = curtime()) > log_delay) \ + { \ + sm_syslog(LOG_INFO, e->e_id, \ + "delaying=%s, load average=%d >= %d", \ + cmd, CurrentLA, DelayLA); \ + log_delay = dnow + MIN_DELAY_LOG; \ + } \ + (void) sleep(1); \ + sm_setproctitle(true, e, "%s %s: %.80s", \ + qid_printname(e), CurSmtpClient, inp); \ + } -/* runinchild() returns */ -# define RIC_INCHILD 0 /* in a child process */ -# define RIC_INPARENT 1 /* still in parent process */ -# define RIC_TEMPFAIL 2 /* temporary failure occurred */ void smtp(nullserver, d_flags, e) @@ -180,7 +337,6 @@ smtp(nullserver, d_flags, e) char *cmd; auto ADDRESS *vrfyqueue; ADDRESS *a; - volatile bool gotmail; /* mail command received */ volatile bool gothello; /* helo command received */ bool vrfy; /* set if this is a vrfy command */ char *volatile protocol; /* sending protocol */ @@ -188,72 +344,93 @@ smtp(nullserver, d_flags, e) char *volatile peerhostname; /* name of SMTP peer or "localhost" */ auto char *delimptr; char *id; - volatile int nrcpts = 0; /* number of RCPT commands */ - int ric; - bool doublequeue; - volatile bool discard; - volatile int badcommands = 0; /* count of bad commands */ - volatile int nverifies = 0; /* count of VRFY/EXPN commands */ - volatile int n_etrn = 0; /* count of ETRN commands */ - volatile int n_noop = 0; /* count of NOOP/VERB/ONEX etc cmds */ - volatile int n_helo = 0; /* count of HELO/EHLO commands */ - volatile int delay = 1; /* timeout for bad commands */ + volatile unsigned int n_badcmds = 0; /* count of bad commands */ + volatile unsigned int n_badrcpts = 0; /* number of rejected RCPT */ + volatile unsigned int n_verifies = 0; /* count of VRFY/EXPN */ + volatile unsigned int n_etrn = 0; /* count of ETRN */ + volatile unsigned int n_noop = 0; /* count of NOOP/VERB/etc */ + volatile unsigned int n_helo = 0; /* count of HELO/EHLO */ + volatile unsigned int delay = 1; /* timeout for bad commands */ bool ok; - volatile bool tempfail = FALSE; -# if _FFR_MILTER - volatile bool milterize = (nullserver == NULL); -# endif /* _FFR_MILTER */ +#if _FFR_ADAPTIVE_EOL + volatile bool first; +#endif /* _FFR_ADAPTIVE_EOL */ + volatile bool tempfail = false; volatile time_t wt; /* timeout after too many commands */ volatile time_t previous; /* time after checksmtpattack() */ - volatile bool lognullconnection = TRUE; + volatile bool lognullconnection = true; register char *q; + SMTP_T smtp; char *addr; char *greetcode = "220"; + char *hostname; /* my hostname ($j) */ QUEUE_CHAR *new; int argno; char *args[MAXSMTPARGS]; char inp[MAXLINE]; char cmdbuf[MAXLINE]; -# if SASL +#if SASL sasl_conn_t *conn; volatile bool sasl_ok; - volatile int n_auth = 0; /* count of AUTH commands */ + volatile unsigned int n_auth = 0; /* count of AUTH commands */ bool ismore; int result; volatile int authenticating; - char *hostname; char *user; char *in, *out, *out2; const char *errstr; - int inlen, out2len; + unsigned int inlen, out2len; unsigned int outlen; char *volatile auth_type; char *mechlist; - volatile int n_mechs; - int len; + volatile unsigned int n_mechs; + unsigned int len; sasl_security_properties_t ssp; sasl_external_properties_t ext_ssf; -# if SFIO sasl_ssf_t *ssf; -# endif /* SFIO */ -# endif /* SASL */ -# if STARTTLS +#endif /* SASL */ +#if STARTTLS int r; int rfd, wfd; - volatile bool usetls = TRUE; - volatile bool tls_active = FALSE; + volatile bool tls_active = false; +# if _FFR_SMTP_SSL + volatile bool smtps = false; +# endif /* _FFR_SMTP_SSL */ bool saveQuickAbort; bool saveSuprErrs; -# endif /* STARTTLS */ - - if (fileno(OutChannel) != fileno(stdout)) + time_t tlsstart; +#endif /* STARTTLS */ + volatile unsigned int features; +#if PIPELINING +# if _FFR_NO_PIPE + int np_log = 0; +# endif /* _FFR_NO_PIPE */ +#endif /* PIPELINING */ + volatile time_t log_delay = (time_t) 0; + + smtp.sm_nrcpts = 0; +#if MILTER + smtp.sm_milterize = (nullserver == NULL); + smtp.sm_milterlist = false; +#endif /* MILTER */ + + /* setup I/O fd correctly for the SMTP server */ + setup_smtpd_io(); + +#if SM_HEAP_CHECK + if (sm_debug_active(&DebugLeakSmtp, 1)) { - /* arrange for debugging output to go to remote host */ - (void) dup2(fileno(OutChannel), fileno(stdout)); + sm_heap_newgroup(); + sm_dprintf("smtp() heap group #%d\n", sm_heap_group()); } +#endif /* SM_HEAP_CHECK */ + + /* XXX the rpool should be set when e is initialized in main() */ + e->e_rpool = sm_rpool_new_x(NULL); + e->e_macro.mac_rpool = e->e_rpool; settime(e); - (void)sm_getla(e); + sm_getla(); peerhostname = RealHostName; if (peerhostname == NULL) peerhostname = "localhost"; @@ -263,27 +440,87 @@ smtp(nullserver, d_flags, e) CurSmtpClient = CurHostName; /* check_relay may have set discard bit, save for later */ - discard = bitset(EF_DISCARD, e->e_flags); - - sm_setproctitle(TRUE, e, "server %s startup", CurSmtpClient); + smtp.sm_discard = bitset(EF_DISCARD, e->e_flags); + +#if PIPELINING + /* auto-flush output when reading input */ + (void) sm_io_autoflush(InChannel, OutChannel); +#endif /* PIPELINING */ + + sm_setproctitle(true, e, "server %s startup", CurSmtpClient); + + /* Set default features for server. */ + features = ((bitset(PRIV_NOETRN, PrivacyFlags) || + bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN) + | (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE) + | (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE + : (SRV_OFFER_EXPN + | (bitset(PRIV_NOVERB, PrivacyFlags) + ? SRV_NONE : SRV_OFFER_VERB))) + | (bitset(PRIV_NORECEIPTS, PrivacyFlags) ? SRV_NONE + : SRV_OFFER_DSN) +#if SASL + | (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH) +#endif /* SASL */ +#if PIPELINING + | SRV_OFFER_PIPE +#endif /* PIPELINING */ +#if STARTTLS + | (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS) + | (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE + : SRV_VRFY_CLT) +#endif /* STARTTLS */ + ; + if (nullserver == NULL) + { + features = srvfeatures(e, CurSmtpClient, features); + if (bitset(SRV_TMP_FAIL, features)) + { + if (LogLevel > 4) + sm_syslog(LOG_ERR, NOQID, + "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled", + CurSmtpClient); + nullserver = "450 4.3.0 Please try again later."; + } +#if PIPELINING +# if _FFR_NO_PIPE + else if (bitset(SRV_NO_PIPE, features)) + { + /* for consistency */ + features &= ~SRV_OFFER_PIPE; + } +# endif /* _FFR_NO_PIPE */ +#endif /* PIPELINING */ + } -# if SASL - sasl_ok = FALSE; /* SASL can't be used (yet) */ + hostname = macvalue('j', e); +#if SASL + sasl_ok = bitset(SRV_OFFER_AUTH, features); n_mechs = 0; + authenticating = SASL_NOT_AUTH; /* SASL server new connection */ - hostname = macvalue('j', e); -# if SASL > 10505 - /* use empty realm: only works in SASL > 1.5.5 */ - result = sasl_server_new("smtp", hostname, "", NULL, 0, &conn); -# else /* SASL > 10505 */ - /* use no realm -> realm is set to hostname by SASL lib */ - result = sasl_server_new("smtp", hostname, NULL, NULL, 0, &conn); -# endif /* SASL > 10505 */ - if (result == SASL_OK) + if (sasl_ok) + { +# if SASL > 10505 + /* use empty realm: only works in SASL > 1.5.5 */ + result = sasl_server_new("smtp", hostname, "", NULL, 0, &conn); +# else /* SASL > 10505 */ + /* use no realm -> realm is set to hostname by SASL lib */ + result = sasl_server_new("smtp", hostname, NULL, NULL, 0, + &conn); +# endif /* SASL > 10505 */ + sasl_ok = result == SASL_OK; + if (!sasl_ok) + { + if (LogLevel > 9) + sm_syslog(LOG_WARNING, NOQID, + "AUTH error: sasl_server_new failed=%d", + result); + } + } + if (sasl_ok) { - sasl_ok = TRUE; - /* ** SASL set properties for sasl ** set local/remote IP @@ -293,8 +530,8 @@ smtp(nullserver, d_flags, e) ** Kerberos_v4 */ -# if NETINET - in = macvalue(macid("{daemon_family}", NULL), e); +#if NETINET + in = macvalue(macid("{daemon_family}"), e); if (in != NULL && strcmp(in, "inet") == 0) { SOCKADDR_LEN_T addrsize; @@ -302,135 +539,127 @@ smtp(nullserver, d_flags, e) struct sockaddr_in saddr_r; addrsize = sizeof(struct sockaddr_in); - if (getpeername(fileno(InChannel), + if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD, + NULL), (struct sockaddr *)&saddr_r, &addrsize) == 0) { sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r); addrsize = sizeof(struct sockaddr_in); - if (getsockname(fileno(InChannel), + if (getsockname(sm_io_getinfo(InChannel, + SM_IO_WHAT_FD, + NULL), (struct sockaddr *)&saddr_l, &addrsize) == 0) sasl_setprop(conn, SASL_IP_LOCAL, &saddr_l); } } -# endif /* NETINET */ +#endif /* NETINET */ - authenticating = SASL_NOT_AUTH; auth_type = NULL; mechlist = NULL; user = NULL; -# if 0 - define(macid("{auth_author}", NULL), NULL, &BlankEnvelope); -# endif /* 0 */ +# if 0 + macdefine(&BlankEnvelope.e_macro, A_PERM, + macid("{auth_author}"), NULL); +# endif /* 0 */ /* set properties */ (void) memset(&ssp, '\0', sizeof ssp); -# if SFIO + /* XXX should these be options settable via .cf ? */ /* ssp.min_ssf = 0; is default due to memset() */ +# if STARTTLS +# endif /* STARTTLS */ { - ssp.max_ssf = INT_MAX; + ssp.max_ssf = MaxSLBits; ssp.maxbufsize = MAXOUTLEN; } -# endif /* SFIO */ -# if _FFR_SASL_OPTS ssp.security_flags = SASLOpts & SASL_SEC_MASK; -# endif /* _FFR_SASL_OPTS */ sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK; if (sasl_ok) { /* ** external security strength factor; - ** we have none so zero -# if STARTTLS - ** we may have to change this for STARTTLS - ** (dynamically) -# endif + ** currently we have none so zero */ + ext_ssf.ssf = 0; ext_ssf.auth_id = NULL; sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL, &ext_ssf) == SASL_OK; } if (sasl_ok) - { n_mechs = saslmechs(conn, &mechlist); - sasl_ok = n_mechs > 0; - } - } - else - { - if (LogLevel > 9) - sm_syslog(LOG_WARNING, NOQID, - "SASL error: sasl_server_new failed=%d", - result); } -# endif /* SASL */ +#endif /* SASL */ -# if STARTTLS -# if _FFR_TLS_O_T - saveQuickAbort = QuickAbort; - saveSuprErrs = SuprErrs; - SuprErrs = TRUE; - QuickAbort = FALSE; - if (rscheck("offer_tls", CurSmtpClient, "", e, TRUE, FALSE, 8, - NULL) != EX_OK || Errors > 0) - usetls = FALSE; - QuickAbort = saveQuickAbort; - SuprErrs = saveSuprErrs; -# endif /* _FFR_TLS_O_T */ -# endif /* STARTTLS */ - -# if _FFR_MILTER - if (milterize) +#if MILTER + if (smtp.sm_milterize) { char state; /* initialize mail filter connection */ - milter_init(e, &state); + smtp.sm_milterlist = milter_init(e, &state); switch (state) { case SMFIR_REJECT: greetcode = "554"; nullserver = "Command rejected"; - milterize = FALSE; + smtp.sm_milterize = false; break; case SMFIR_TEMPFAIL: - tempfail = TRUE; - milterize = FALSE; + tempfail = true; + smtp.sm_milterize = false; break; } } - if (milterize && !bitset(EF_DISCARD, e->e_flags)) + if (smtp.sm_milterlist && smtp.sm_milterize && + !bitset(EF_DISCARD, e->e_flags)) { char state; - (void) milter_connect(peerhostname, RealHostAddr, - e, &state); + (void) milter_connect(peerhostname, RealHostAddr, e, &state); switch (state) { case SMFIR_REPLYCODE: /* REPLYCODE shouldn't happen */ case SMFIR_REJECT: greetcode = "554"; nullserver = "Command rejected"; - milterize = FALSE; + smtp.sm_milterize = false; break; case SMFIR_TEMPFAIL: - tempfail = TRUE; - milterize = FALSE; + tempfail = true; + smtp.sm_milterize = false; break; } } -# endif /* _FFR_MILTER */ +#endif /* MILTER */ + +#if STARTTLS +# if _FFR_SMTP_SSL + /* If this an smtps connection, start TLS now */ + smtps = bitnset(D_SMTPS, d_flags); + if (smtps) + goto starttls; + + greeting: + +# endif /* _FFR_SMTP_SSL */ +#endif /* STARTTLS */ /* output the first line, inserting "ESMTP" as second word */ - expand(SmtpGreeting, inp, sizeof inp, e); + if (*greetcode == '5') + (void) sm_snprintf(inp, sizeof inp, "%s not accepting messages", + hostname); + else + expand(SmtpGreeting, inp, sizeof inp, e); + p = strchr(inp, '\n'); if (p != NULL) *p++ = '\0'; @@ -438,10 +667,10 @@ smtp(nullserver, d_flags, e) if (id == NULL) id = &inp[strlen(inp)]; if (p == NULL) - snprintf(cmdbuf, sizeof cmdbuf, + (void) sm_snprintf(cmdbuf, sizeof cmdbuf, "%s %%.*s ESMTP%%s", greetcode); else - snprintf(cmdbuf, sizeof cmdbuf, + (void) sm_snprintf(cmdbuf, sizeof cmdbuf, "%s-%%.*s ESMTP%%s", greetcode); message(cmdbuf, (int) (id - inp), inp, id); @@ -451,70 +680,76 @@ smtp(nullserver, d_flags, e) *p++ = '\0'; if (isascii(*id) && isspace(*id)) id++; - (void) snprintf(cmdbuf, sizeof cmdbuf, "%s-%%s", greetcode); + (void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, "-%s"); message(cmdbuf, id); } if (id != NULL) { if (isascii(*id) && isspace(*id)) id++; - (void) snprintf(cmdbuf, sizeof cmdbuf, "%s %%s", greetcode); + (void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, " %s"); message(cmdbuf, id); } protocol = NULL; sendinghost = macvalue('s', e); - gothello = FALSE; - gotmail = FALSE; + + /* sendinghost's storage must outlive the current envelope */ + if (sendinghost != NULL) + sendinghost = sm_strdup_x(sendinghost); +#if _FFR_ADAPTIVE_EOL + first = true; +#endif /* _FFR_ADAPTIVE_EOL */ + gothello = false; + smtp.sm_gotmail = false; for (;;) { - /* arrange for backout */ - (void) setjmp(TopFrame); - QuickAbort = FALSE; - HoldErrs = FALSE; - SuprErrs = FALSE; - LogUsrErrs = FALSE; - OnlyOneError = TRUE; + SM_TRY + { + QuickAbort = false; + HoldErrs = false; + SuprErrs = false; + LogUsrErrs = false; + OnlyOneError = true; e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); /* setup for the read */ e->e_to = NULL; Errors = 0; FileName = NULL; - (void) fflush(stdout); + (void) sm_io_flush(smioout, SM_TIME_DEFAULT); /* read the input line */ SmtpPhase = "server cmd read"; - sm_setproctitle(TRUE, e, "server %s cmd read", CurSmtpClient); -# if SASL + sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient); +#if SASL /* - ** SMTP AUTH requires accepting any length, - ** at least for challenge/response - ** XXX + ** XXX SMTP AUTH requires accepting any length, + ** at least for challenge/response */ -# endif /* SASL */ +#endif /* SASL */ /* handle errors */ - if (ferror(OutChannel) || + if (sm_io_error(OutChannel) || (p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, SmtpPhase)) == NULL) { char *d; - d = macvalue(macid("{daemon_name}", NULL), e); + d = macvalue(macid("{daemon_name}"), e); if (d == NULL) d = "stdin"; /* end of file, just die */ disconnect(1, e); -# if _FFR_MILTER +#if MILTER /* close out milter filters */ milter_quit(e); -# endif /* _FFR_MILTER */ +#endif /* MILTER */ message("421 4.4.1 %s Lost input channel from %s", MyHostName, CurSmtpClient); - if (LogLevel > (gotmail ? 1 : 19)) + if (LogLevel > (smtp.sm_gotmail ? 1 : 19)) sm_syslog(LOG_NOTICE, e->e_id, "lost input channel from %.100s to %s after %s", CurSmtpClient, d, @@ -529,20 +764,64 @@ smtp(nullserver, d_flags, e) goto doquit; } +#if _FFR_ADAPTIVE_EOL + if (first) + { + char *p; + + smtp.sm_crlf = true; + p = strchr(inp, '\n'); + if (p == NULL || p <= inp || p[-1] != '\r') + { + smtp.sm_crlf = false; + if (tTd(66, 1) && LogLevel > 8) + { + /* how many bad guys are there? */ + sm_syslog(LOG_INFO, NOQID, + "%.100s did not use CRLF", + CurSmtpClient); + } + } + first = false; + } +#endif /* _FFR_ADAPTIVE_EOL */ + /* clean up end of line */ - fixcrlf(inp, TRUE); + fixcrlf(inp, true); -# if SASL +#if PIPELINING +# if _FFR_NO_PIPE + /* + ** if there is more input and pipelining is disabled: + ** delay ... (and maybe discard the input?) + ** XXX this doesn't really work, at least in tests using + ** telnet SM_IO_IS_READABLE only returns 1 if there were + ** more than 2 input lines available. + */ + + if (bitset(SRV_NO_PIPE, features) && + sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL)) + { + if (++np_log < 3) + sm_syslog(LOG_INFO, NOQID, + "unauthorized PIPELINING, sleeping"); + sleep(1); + } + +# endif /* _FFR_NO_PIPE */ +#endif /* PIPELINING */ + +#if SASL if (authenticating == SASL_PROC_AUTH) { -# if 0 +# if 0 if (*inp == '\0') { authenticating = SASL_NOT_AUTH; message("501 5.5.2 missing input"); continue; } -# endif /* 0 */ +# endif /* 0 */ if (*inp == '*' && *(inp + 1) == '\0') { authenticating = SASL_NOT_AUTH; @@ -574,97 +853,94 @@ smtp(nullserver, d_flags, e) authenticated: message("235 2.0.0 OK Authenticated"); authenticating = SASL_IS_AUTH; - define(macid("{auth_type}", NULL), - newstr(auth_type), &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, A_TEMP, + macid("{auth_type}"), auth_type); result = sasl_getprop(conn, SASL_USERNAME, (void **)&user); if (result != SASL_OK) { user = ""; - define(macid("{auth_authen}", NULL), - NULL, &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, + A_PERM, + macid("{auth_authen}"), NULL); } else { - define(macid("{auth_authen}", NULL), - newstr(user), &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, + A_TEMP, + macid("{auth_authen}"), user); } -# if 0 +# if 0 /* get realm? */ sasl_getprop(conn, SASL_REALM, (void **) &data); -# endif /* 0 */ - +# endif /* 0 */ -# if SFIO /* get security strength (features) */ result = sasl_getprop(conn, SASL_SSF, (void **) &ssf); if (result != SASL_OK) { - define(macid("{auth_ssf}", NULL), - "0", &BlankEnvelope); + macdefine(&BlankEnvelope.e_macro, + A_PERM, + macid("{auth_ssf}"), "0"); ssf = NULL; } else { char pbuf[8]; - snprintf(pbuf, sizeof pbuf, "%u", *ssf); - define(macid("{auth_ssf}", NULL), - newstr(pbuf), &BlankEnvelope); + (void) sm_snprintf(pbuf, sizeof pbuf, + "%u", *ssf); + macdefine(&BlankEnvelope.e_macro, + A_TEMP, + macid("{auth_ssf}"), pbuf); if (tTd(95, 8)) - dprintf("SASL auth_ssf: %u\n", - *ssf); + sm_dprintf("AUTH auth_ssf: %u\n", + *ssf); } + /* - ** only switch to encrypted connection + ** Only switch to encrypted connection ** if a security layer has been negotiated */ + if (ssf != NULL && *ssf > 0) { /* - ** convert sfio stuff to use SASL - ** check return values - ** if the call fails, - ** fall back to unencrypted version - ** unless some cf option requires - ** encryption then the connection must - ** be aborted + ** Convert I/O layer to use SASL. + ** If the call fails, the connection + ** is aborted. */ - if (sfdcsasl(InChannel, OutChannel, - conn) == 0) + + if (sfdcsasl(&InChannel, &OutChannel, + conn) == 0) { /* restart dialogue */ - gothello = FALSE; - OneXact = TRUE; n_helo = 0; +#if PIPELINING + (void) sm_io_autoflush(InChannel, + OutChannel); +#endif /* PIPELINING */ } else syserr("503 5.3.3 SASL TLS failed"); - if (LogLevel > 9) - sm_syslog(LOG_INFO, - NOQID, - "SASL: connection from %.64s: mech=%.16s, id=%.64s, bits=%d", - CurSmtpClient, - auth_type, user, - *ssf); } -# else /* SFIO */ - if (LogLevel > 9) + + /* NULL pointer ok since it's our function */ + if (LogLevel > 8) sm_syslog(LOG_INFO, NOQID, - "SASL: connection from %.64s: mech=%.16s, id=%.64s", - CurSmtpClient, auth_type, - user); -# endif /* SFIO */ + "AUTH=server, relay=%.100s, authid=%.32s, mech=%.16s, bits=%d", + CurSmtpClient, user, + auth_type, *ssf); } else if (result == SASL_CONTINUE) { len = ENC64LEN(outlen); out2 = xalloc(len); result = sasl_encode64(out, outlen, out2, len, - (u_int *)&out2len); + &out2len); if (result != SASL_OK) { /* correct code? XXX */ @@ -672,7 +948,7 @@ smtp(nullserver, d_flags, e) message("454 4.5.4 Internal error: unable to encode64"); if (LogLevel > 5) sm_syslog(LOG_WARNING, e->e_id, - "SASL encode64 error [%d for \"%s\"]", + "AUTH encode64 error [%d for \"%s\"]", result, out); /* start over? */ authenticating = SASL_NOT_AUTH; @@ -681,8 +957,8 @@ smtp(nullserver, d_flags, e) { message("334 %s", out2); if (tTd(95, 2)) - dprintf("SASL continue: msg='%s' len=%d\n", - out2, out2len); + sm_dprintf("AUTH continue: msg='%s' len=%u\n", + out2, out2len); } } else @@ -691,33 +967,33 @@ smtp(nullserver, d_flags, e) message("500 5.7.0 authentication failed"); if (LogLevel > 9) sm_syslog(LOG_WARNING, e->e_id, - "AUTH failure (%s): %s (%d)", + "AUTH failure (%s): %s (%d) %s", auth_type, sasl_errstring(result, NULL, NULL), - result); + result, + errstr == NULL ? "" : errstr); authenticating = SASL_NOT_AUTH; } } else { /* don't want to do any of this if authenticating */ -# endif /* SASL */ +#endif /* SASL */ /* echo command to transcript */ if (e->e_xfp != NULL) - fprintf(e->e_xfp, "<<< %s\n", inp); + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + "<<< %s\n", inp); - if (LogLevel >= 15) - sm_syslog(LOG_INFO, e->e_id, - "<-- %s", - inp); + if (LogLevel > 14) + sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp); if (e->e_id == NULL) - sm_setproctitle(TRUE, e, "%s: %.80s", + sm_setproctitle(true, e, "%s: %.80s", CurSmtpClient, inp); else - sm_setproctitle(TRUE, e, "%s %s: %.80s", + sm_setproctitle(true, e, "%s %s: %.80s", qid_printname(e), CurSmtpClient, inp); @@ -732,24 +1008,33 @@ smtp(nullserver, d_flags, e) *cmd = '\0'; /* throw away leading whitespace */ - while (isascii(*p) && isspace(*p)) - p++; + SKIP_SPACE(p); /* decode command */ for (c = CmdTab; c->cmd_name != NULL; c++) { - if (strcasecmp(c->cmd_name, cmdbuf) == 0) + if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0) break; } /* reset errors */ errno = 0; + /* check whether a "non-null" command has been used */ + switch (c->cmd_code) + { + case CMDMAIL: + case CMDEXPN: + case CMDVRFY: + case CMDETRN: + lognullconnection = false; + } + /* ** Process command. ** ** If we are running as a null server, return 550 - ** to everything. + ** to almost everything. */ if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags)) @@ -768,22 +1053,27 @@ smtp(nullserver, d_flags, e) if (bitnset(D_ETRNONLY, d_flags) && nullserver == NULL) break; + DELAY_CONN("ETRN"); /* FALLTHROUGH */ default: - if (++badcommands > MAXBADCOMMANDS) +#if MAXBADCOMMANDS > 0 + /* theoretically this could overflow */ + if (++n_badcmds > MAXBADCOMMANDS) { delay *= 2; if (delay >= MAXTIMEOUT) delay = MAXTIMEOUT; (void) sleep(delay); } +#endif /* MAXBADCOMMANDS > 0 */ if (nullserver != NULL) { if (ISSMTPREPLY(nullserver)) usrerr(nullserver); else - usrerr("550 5.0.0 %s", nullserver); + usrerr("550 5.0.0 %s", + nullserver); } else usrerr("452 4.4.5 Insufficient disk space; try again later"); @@ -791,21 +1081,12 @@ smtp(nullserver, d_flags, e) } } - /* non-null server */ - switch (c->cmd_code) - { - case CMDMAIL: - case CMDEXPN: - case CMDVRFY: - case CMDETRN: - lognullconnection = FALSE; - } - switch (c->cmd_code) { -# if SASL +#if SASL case CMDAUTH: /* sasl */ - if (!sasl_ok) + DELAY_CONN("AUTH"); + if (!sasl_ok || n_mechs <= 0) { message("503 5.3.3 AUTH not available"); break; @@ -815,7 +1096,7 @@ smtp(nullserver, d_flags, e) message("503 5.5.0 Already Authenticated"); break; } - if (gotmail) + if (smtp.sm_gotmail) { message("503 5.5.0 AUTH not permitted during a mail transaction"); break; @@ -830,13 +1111,13 @@ smtp(nullserver, d_flags, e) break; } - ismore = FALSE; + ismore = false; /* crude way to avoid crack attempts */ - (void) checksmtpattack(&n_auth, n_mechs + 1, TRUE, + (void) checksmtpattack(&n_auth, n_mechs + 1, true, "AUTH", e); - /* make sure it's a valid string */ + /* make sure mechanism (p) is a valid string */ for (q = p; *q != '\0' && isascii(*q); q++) { if (isspace(*q)) @@ -854,7 +1135,7 @@ smtp(nullserver, d_flags, e) /* check whether mechanism is available */ if (iteminlist(p, mechlist, " ") == NULL) { - message("503 5.3.3 AUTH mechanism %s not available", + message("503 5.3.3 AUTH mechanism %.32s not available", p); break; } @@ -862,16 +1143,16 @@ smtp(nullserver, d_flags, e) if (ismore) { /* could this be shorter? XXX */ - in = xalloc(strlen(q)); + in = sm_rpool_malloc(e->e_rpool, strlen(q)); result = sasl_decode64(q, strlen(q), in, - (u_int *)&inlen); + &inlen); if (result != SASL_OK) { message("501 5.5.4 cannot BASE64 decode '%s'", q); if (LogLevel > 5) sm_syslog(LOG_WARNING, e->e_id, - "SASL decode64 error [%d for \"%s\"]", + "AUTH decode64 error [%d for \"%s\"]", result, q); /* start over? */ authenticating = SASL_NOT_AUTH; @@ -879,23 +1160,6 @@ smtp(nullserver, d_flags, e) inlen = 0; break; } -# if 0 - if (tTd(95, 99)) - { - int i; - - dprintf("AUTH: more \""); - for (i = 0; i < inlen; i++) - { - if (isascii(in[i]) && - isprint(in[i])) - dprintf("%c", in[i]); - else - dprintf("_"); - } - dprintf("\"\n"); - } -# endif /* 0 */ } else { @@ -912,11 +1176,12 @@ smtp(nullserver, d_flags, e) message("500 5.7.0 authentication failed"); if (LogLevel > 9) sm_syslog(LOG_ERR, e->e_id, - "AUTH failure (%s): %s (%d)", + "AUTH failure (%s): %s (%d) %s", p, sasl_errstring(result, NULL, NULL), - result); + result, + errstr); break; } auth_type = newstr(p); @@ -932,14 +1197,14 @@ smtp(nullserver, d_flags, e) len = ENC64LEN(outlen); out2 = xalloc(len); result = sasl_encode64(out, outlen, out2, len, - (u_int *)&out2len); + &out2len); if (result != SASL_OK) { message("454 4.5.4 Temporary authentication failure"); if (LogLevel > 5) sm_syslog(LOG_WARNING, e->e_id, - "SASL encode64 error [%d for \"%s\"]", + "AUTH encode64 error [%d for \"%s\"]", result, out); /* start over? */ @@ -950,18 +1215,18 @@ smtp(nullserver, d_flags, e) message("334 %s", out2); authenticating = SASL_PROC_AUTH; } - break; -# endif /* SASL */ +#endif /* SASL */ -# if STARTTLS +#if STARTTLS case CMDSTLS: /* starttls */ + DELAY_CONN("STARTTLS"); if (*p != '\0') { message("501 5.5.2 Syntax error (no parameters allowed)"); break; } - if (!usetls) + if (!bitset(SRV_OFFER_TLS, features)) { message("503 5.5.0 TLS not available"); break; @@ -971,7 +1236,7 @@ smtp(nullserver, d_flags, e) message("454 4.3.3 TLS not available after start"); break; } - if (gotmail) + if (smtp.sm_gotmail) { message("503 5.5.0 TLS not permitted during a mail transaction"); break; @@ -985,32 +1250,51 @@ smtp(nullserver, d_flags, e) usrerr("454 4.7.1 Please try again later"); break; } +# if _FFR_SMTP_SSL + starttls: +# endif /* _FFR_SMTP_SSL */ # if TLS_NO_RSA /* ** XXX do we need a temp key ? */ # else /* TLS_NO_RSA */ - if (SSL_CTX_need_tmp_RSA(srv_ctx) && - !SSL_CTX_set_tmp_rsa(srv_ctx, - (rsa = RSA_generate_key(RSA_KEYLENGTH, RSA_F4, - NULL, NULL))) - ) - { - message("454 4.3.3 TLS not available: error generating RSA temp key"); - if (rsa != NULL) - RSA_free(rsa); - break; - } # endif /* TLS_NO_RSA */ + +# if TLS_VRFY_PER_CTX + /* + ** Note: this sets the verification globally + ** (per SSL_CTX) + ** it's ok since it applies only to one transaction + */ + + TLS_VERIFY_CLIENT(); +# endif /* TLS_VRFY_PER_CTX */ + if (srv_ssl != NULL) SSL_clear(srv_ssl); else if ((srv_ssl = SSL_new(srv_ctx)) == NULL) { message("454 4.3.3 TLS not available: error generating SSL handle"); +# if _FFR_SMTP_SSL + goto tls_done; +# else /* _FFR_SMTP_SSL */ break; +# endif /* _FFR_SMTP_SSL */ } - rfd = fileno(InChannel); - wfd = fileno(OutChannel); + +# if !TLS_VRFY_PER_CTX + /* + ** this could be used if it were possible to set + ** verification per SSL (connection) + ** not just per SSL_CTX (global) + */ + + TLS_VERIFY_CLIENT(); +# endif /* !TLS_VRFY_PER_CTX */ + + rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL); + wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL); + if (rfd < 0 || wfd < 0 || SSL_set_rfd(srv_ssl, rfd) <= 0 || SSL_set_wfd(srv_ssl, wfd) <= 0) @@ -1018,27 +1302,97 @@ smtp(nullserver, d_flags, e) message("454 4.3.3 TLS not available: error set fd"); SSL_free(srv_ssl); srv_ssl = NULL; +# if _FFR_SMTP_SSL + goto tls_done; +# else /* _FFR_SMTP_SSL */ break; +# endif /* _FFR_SMTP_SSL */ } - message("220 2.0.0 Ready to start TLS"); +# if _FFR_SMTP_SSL + if (!smtps) +# endif /* _FFR_SMTP_SSL */ + message("220 2.0.0 Ready to start TLS"); +# if PIPELINING + (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); +# endif /* PIPELINING */ + SSL_set_accept_state(srv_ssl); # define SSL_ACC(s) SSL_accept(s) + + tlsstart = curtime(); + ssl_retry: if ((r = SSL_ACC(srv_ssl)) <= 0) { int i; + bool timedout; + time_t left; + time_t now = curtime(); + struct timeval tv; /* what to do in this case? */ i = SSL_get_error(srv_ssl, r); + + /* + ** For SSL_ERROR_WANT_{READ,WRITE}: + ** There is no SSL record available yet + ** or there is only a partial SSL record + ** removed from the network (socket) buffer + ** into the SSL buffer. The SSL_accept will + ** only succeed when a full SSL record is + ** available (assuming a "real" error + ** doesn't happen). To handle when a "real" + ** error does happen the select is set for + ** exceptions too. + ** The connection may be re-negotiated + ** during this time so both read and write + ** "want errors" need to be handled. + ** A select() exception loops back so that + ** a proper SSL error message can be gotten. + */ + + left = TimeOuts.to_starttls - (now - tlsstart); + timedout = left <= 0; + if (!timedout) + { + tv.tv_sec = left; + tv.tv_usec = 0; + } + + /* XXX what about SSL_pending() ? */ + if (!timedout && i == SSL_ERROR_WANT_READ) + { + fd_set ssl_maskr, ssl_maskx; + + FD_ZERO(&ssl_maskr); + FD_SET(rfd, &ssl_maskr); + FD_ZERO(&ssl_maskx); + FD_SET(rfd, &ssl_maskx); + if (select(rfd + 1, &ssl_maskr, NULL, + &ssl_maskx, &tv) > 0) + goto ssl_retry; + } + if (!timedout && i == SSL_ERROR_WANT_WRITE) + { + fd_set ssl_maskw, ssl_maskx; + + FD_ZERO(&ssl_maskw); + FD_SET(wfd, &ssl_maskw); + FD_ZERO(&ssl_maskx); + FD_SET(rfd, &ssl_maskx); + if (select(wfd + 1, NULL, &ssl_maskw, + &ssl_maskx, &tv) > 0) + goto ssl_retry; + } if (LogLevel > 5) { - sm_syslog(LOG_WARNING, e->e_id, - "TLS: error: accept failed=%d (%d)", - r, i); - if (LogLevel > 9) - tlslogerr(); + sm_syslog(LOG_WARNING, NOQID, + "STARTTLS=server, error: accept failed=%d, SSL_error=%d, timedout=%d", + r, i, (int) timedout); + if (LogLevel > 8) + tlslogerr("server"); } - tls_ok_srv = FALSE; + tls_ok_srv = false; SSL_free(srv_ssl); srv_ssl = NULL; @@ -1053,8 +1407,10 @@ smtp(nullserver, d_flags, e) } /* ignore return code for now, it's in {verify} */ - (void) tls_get_info(srv_ssl, &BlankEnvelope, TRUE, - CurSmtpClient, TRUE); + (void) tls_get_info(srv_ssl, true, + CurSmtpClient, + &BlankEnvelope.e_macro, + bitset(SRV_VRFY_CLT, features)); /* ** call Stls_client to find out whether @@ -1063,12 +1419,13 @@ smtp(nullserver, d_flags, e) saveQuickAbort = QuickAbort; saveSuprErrs = SuprErrs; - SuprErrs = TRUE; - QuickAbort = FALSE; + SuprErrs = true; + QuickAbort = false; if (rscheck("tls_client", - macvalue(macid("{verify}", NULL), e), - "STARTTLS", e, TRUE, TRUE, 6, NULL) != - EX_OK || Errors > 0) + macvalue(macid("{verify}"), e), + "STARTTLS", e, true, true, 5, + NULL, NOQID) != EX_OK || + Errors > 0) { extern char MsgBuf[]; @@ -1080,48 +1437,36 @@ smtp(nullserver, d_flags, e) QuickAbort = saveQuickAbort; SuprErrs = saveSuprErrs; - tls_ok_srv = FALSE; /* don't offer STARTTLS again */ - gothello = FALSE; /* discard info */ + tls_ok_srv = false; /* don't offer STARTTLS again */ n_helo = 0; - OneXact = TRUE; /* only one xaction this run */ -# if SASL +# if SASL if (sasl_ok) { char *s; - if ((s = macvalue(macid("{cipher_bits}", NULL), e)) != NULL && - (ext_ssf.ssf = atoi(s)) > 0) + s = macvalue(macid("{cipher_bits}"), e); + if (s != NULL && (ext_ssf.ssf = atoi(s)) > 0) { -# if _FFR_EXT_MECH - ext_ssf.auth_id = macvalue(macid("{cert_subject}", - NULL), + ext_ssf.auth_id = macvalue(macid("{cert_subject}"), e); -# endif /* _FFR_EXT_MECH */ sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL, &ext_ssf) == SASL_OK; - if (mechlist != NULL) - sm_free(mechlist); mechlist = NULL; if (sasl_ok) - { n_mechs = saslmechs(conn, &mechlist); - sasl_ok = n_mechs > 0; - } } } -# endif /* SASL */ +# endif /* SASL */ /* switch to secure connection */ -#if SFIO - r = sfdctls(InChannel, OutChannel, srv_ssl); -#else /* SFIO */ -# if _FFR_TLS_TOREK - r = sfdctls(&InChannel, &OutChannel, srv_ssl); -# endif /* _FFR_TLS_TOREK */ -#endif /* SFIO */ - if (r == 0) - tls_active = TRUE; + if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0) + { + tls_active = true; +# if PIPELINING + (void) sm_io_autoflush(InChannel, OutChannel); +# endif /* PIPELINING */ + } else { /* @@ -1132,14 +1477,26 @@ smtp(nullserver, d_flags, e) ** encrypted layer, but we could not... ** just "hang up"? */ + nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer"; - syserr("TLS: can't switch to encrypted layer"); + syserr("STARTTLS: can't switch to encrypted layer"); + } +# if _FFR_SMTP_SSL + tls_done: + if (smtps) + { + if (tls_active) + goto greeting; + else + goto doquit; } +# endif /* _FFR_SMTP_SSL */ break; -# endif /* STARTTLS */ +#endif /* STARTTLS */ case CMDHELO: /* hello -- introduce yourself */ case CMDEHLO: /* extended hello */ + DELAY_CONN("EHLO"); if (c->cmd_code == CMDEHLO) { protocol = "ESMTP"; @@ -1152,9 +1509,11 @@ smtp(nullserver, d_flags, e) } /* avoid denial-of-service */ - (void) checksmtpattack(&n_helo, MAXHELOCOMMANDS, TRUE, + (void) checksmtpattack(&n_helo, MAXHELOCOMMANDS, true, "HELO/EHLO", e); +#if 0 + /* RFC2821 4.1.4 allows duplicate HELO/EHLO */ /* check for duplicate HELO/EHLO per RFC 1651 4.2 */ if (gothello) { @@ -1162,6 +1521,7 @@ smtp(nullserver, d_flags, e) MyHostName); break; } +#endif /* 0 */ /* check for valid domain name (re 1123 5.2.5) */ if (*p == '\0' && !AllowBogusHELO) @@ -1200,7 +1560,7 @@ smtp(nullserver, d_flags, e) if (*q == '\0') { q = "pleased to meet you"; - sendinghost = newstr(p); + sendinghost = sm_strdup_x(p); } else if (!AllowBogusHELO) { @@ -1216,10 +1576,12 @@ smtp(nullserver, d_flags, e) q = "accepting invalid domain name"; } - gothello = TRUE; + if (gothello) + CLEAR_STATE(cmdbuf); -# if _FFR_MILTER - if (milterize && !bitset(EF_DISCARD, e->e_flags)) +#if MILTER + if (smtp.sm_milterlist && smtp.sm_milterize && + !bitset(EF_DISCARD, e->e_flags)) { char state; char *response; @@ -1229,21 +1591,22 @@ smtp(nullserver, d_flags, e) { case SMFIR_REPLYCODE: nullserver = response; - milterize = FALSE; + smtp.sm_milterize = false; break; case SMFIR_REJECT: nullserver = "Command rejected"; - milterize = FALSE; + smtp.sm_milterize = false; break; case SMFIR_TEMPFAIL: - tempfail = TRUE; - milterize = FALSE; + tempfail = true; + smtp.sm_milterize = false; break; } } -# endif /* _FFR_MILTER */ +#endif /* MILTER */ + gothello = true; /* print HELO response message */ if (c->cmd_code != CMDEHLO) @@ -1267,48 +1630,56 @@ smtp(nullserver, d_flags, e) ** print EHLO features list ** ** Note: If you change this list, - ** remember to update 'helpfile' + ** remember to update 'helpfile' */ message("250-ENHANCEDSTATUSCODES"); - if (!bitset(PRIV_NOEXPN, PrivacyFlags)) +#if PIPELINING + if (bitset(SRV_OFFER_PIPE, features)) + message("250-PIPELINING"); +#endif /* PIPELINING */ + if (bitset(SRV_OFFER_EXPN, features)) { message("250-EXPN"); - if (!bitset(PRIV_NOVERB, PrivacyFlags)) + if (bitset(SRV_OFFER_VERB, features)) message("250-VERB"); } -# if MIME8TO7 +#if MIME8TO7 message("250-8BITMIME"); -# endif /* MIME8TO7 */ +#endif /* MIME8TO7 */ if (MaxMessageSize > 0) message("250-SIZE %ld", MaxMessageSize); else message("250-SIZE"); -# if DSN - if (SendMIMEErrors && - !bitset(PRIV_NORECEIPTS, PrivacyFlags)) +#if DSN + if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features)) message("250-DSN"); -# endif /* DSN */ - message("250-ONEX"); - if (!bitset(PRIV_NOETRN, PrivacyFlags) && - !bitnset(D_NOETRN, d_flags)) +#endif /* DSN */ + if (bitset(SRV_OFFER_ETRN, features)) message("250-ETRN"); - message("250-XUSR"); - -# if SASL +#if SASL if (sasl_ok && mechlist != NULL && *mechlist != '\0') message("250-AUTH %s", mechlist); -# endif /* SASL */ -# if STARTTLS - if (tls_ok_srv && usetls) +#endif /* SASL */ +#if STARTTLS + if (tls_ok_srv && bitset(SRV_OFFER_TLS, features)) message("250-STARTTLS"); -# endif /* STARTTLS */ +#endif /* STARTTLS */ + if (DeliverByMin > 0) + message("250-DELIVERBY %ld", + (long) DeliverByMin); + else if (DeliverByMin == 0) + message("250-DELIVERBY"); + + /* < 0: no deliver-by */ + message("250 HELP"); break; case CMDMAIL: /* mail -- designate sender */ SmtpPhase = "server MAIL"; + DELAY_CONN("MAIL"); /* check for validity of this command */ if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) @@ -1316,25 +1687,19 @@ smtp(nullserver, d_flags, e) usrerr("503 5.0.0 Polite people say HELO first"); break; } - if (gotmail) + if (smtp.sm_gotmail) { usrerr("503 5.5.0 Sender already specified"); break; } - if (InChild) - { - errno = 0; - syserr("503 5.5.0 Nested MAIL command: MAIL %s", p); - finis(TRUE, ExitStat); - } -# if SASL - if (bitnset(D_AUTHREQ, d_flags) && +#if SASL + if (bitset(SRV_REQ_AUTH, features) && authenticating != SASL_IS_AUTH) { usrerr("530 5.7.0 Authentication required"); break; } -# endif /* SASL */ +#endif /* SASL */ p = skipword(p, "from"); if (p == NULL) @@ -1345,7 +1710,7 @@ smtp(nullserver, d_flags, e) sm_syslog(LOG_INFO, e->e_id, "SMTP MAIL command (%.100s) from %.100s tempfailed (due to previous checks)", p, CurSmtpClient); - usrerr("451 4.7.1 Please try again later"); + usrerr(MSG_TEMPFAIL); break; } @@ -1354,103 +1719,88 @@ smtp(nullserver, d_flags, e) sendinghost = peerhostname; - /* fork a subprocess to process this command */ - ric = runinchild("SMTP-MAIL", e); - - /* Catch a problem and stop processing */ - if (ric == RIC_TEMPFAIL && nullserver == NULL) - nullserver = "452 4.3.0 Internal software error"; - if (ric != RIC_INCHILD) - break; +#if SM_HEAP_CHECK + if (sm_debug_active(&DebugLeakSmtp, 1)) + { + sm_heap_newgroup(); + sm_dprintf("smtp() heap group #%d\n", + sm_heap_group()); + } +#endif /* SM_HEAP_CHECK */ if (Errors > 0) - goto undo_subproc_no_pm; + goto undo_no_pm; if (!gothello) { - auth_warning(e, - "%s didn't use HELO protocol", - CurSmtpClient); + auth_warning(e, "%s didn't use HELO protocol", + CurSmtpClient); } -# ifdef PICKY_HELO_CHECK - if (strcasecmp(sendinghost, peerhostname) != 0 && - (strcasecmp(peerhostname, "localhost") != 0 || - strcasecmp(sendinghost, MyHostName) != 0)) +#ifdef PICKY_HELO_CHECK + if (sm_strcasecmp(sendinghost, peerhostname) != 0 && + (sm_strcasecmp(peerhostname, "localhost") != 0 || + sm_strcasecmp(sendinghost, MyHostName) != 0)) { auth_warning(e, "Host %s claimed to be %s", - CurSmtpClient, sendinghost); + CurSmtpClient, sendinghost); } -# endif /* PICKY_HELO_CHECK */ +#endif /* PICKY_HELO_CHECK */ if (protocol == NULL) protocol = "SMTP"; - define('r', protocol, e); - define('s', sendinghost, e); + macdefine(&e->e_macro, A_PERM, 'r', protocol); + macdefine(&e->e_macro, A_PERM, 's', sendinghost); if (Errors > 0) - goto undo_subproc_no_pm; - nrcpts = 0; - define(macid("{ntries}", NULL), "0", e); + goto undo_no_pm; + smtp.sm_nrcpts = 0; + n_badrcpts = 0; + macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0"); + macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0"); e->e_flags |= EF_CLRQUEUE; - sm_setproctitle(TRUE, e, "%s %s: %.80s", + sm_setproctitle(true, e, "%s %s: %.80s", qid_printname(e), CurSmtpClient, inp); - /* child -- go do the processing */ - if (setjmp(TopFrame) > 0) - { - /* this failed -- undo work */ - undo_subproc_no_pm: - e->e_flags &= ~EF_PM_NOTIFY; - undo_subproc: - if (InChild) - { - QuickAbort = FALSE; - SuprErrs = TRUE; - e->e_flags &= ~EF_FATALERRS; - - if (LogLevel > 4 && - bitset(EF_LOGSENDER, e->e_flags)) - logsender(e, NULL); - e->e_flags &= ~EF_LOGSENDER; - - finis(TRUE, ExitStat); - } - break; - } - QuickAbort = TRUE; + /* do the processing */ + SM_TRY + { + QuickAbort = true; /* must parse sender first */ delimptr = NULL; - setsender(p, e, &delimptr, ' ', FALSE); + setsender(p, e, &delimptr, ' ', false); if (delimptr != NULL && *delimptr != '\0') *delimptr++ = '\0'; if (Errors > 0) - goto undo_subproc_no_pm; + sm_exc_raisenew_x(&EtypeQuickAbort, 1); /* Successfully set e_from, allow logging */ e->e_flags |= EF_LOGSENDER; /* put resulting triple from parseaddr() into macros */ if (e->e_from.q_mailer != NULL) - define(macid("{mail_mailer}", NULL), - e->e_from.q_mailer->m_name, e); + macdefine(&e->e_macro, A_PERM, + macid("{mail_mailer}"), + e->e_from.q_mailer->m_name); else - define(macid("{mail_mailer}", NULL), - NULL, e); + macdefine(&e->e_macro, A_PERM, + macid("{mail_mailer}"), NULL); if (e->e_from.q_host != NULL) - define(macid("{mail_host}", NULL), - e->e_from.q_host, e); + macdefine(&e->e_macro, A_PERM, + macid("{mail_host}"), + e->e_from.q_host); else - define(macid("{mail_host}", NULL), - "localhost", e); + macdefine(&e->e_macro, A_PERM, + macid("{mail_host}"), "localhost"); if (e->e_from.q_user != NULL) - define(macid("{mail_addr}", NULL), - e->e_from.q_user, e); + macdefine(&e->e_macro, A_PERM, + macid("{mail_addr}"), + e->e_from.q_user); else - define(macid("{mail_addr}", NULL), - NULL, e); + macdefine(&e->e_macro, A_PERM, + macid("{mail_addr}"), NULL); if (Errors > 0) - goto undo_subproc_no_pm; + sm_exc_raisenew_x(&EtypeQuickAbort, 1); /* check for possible spoofing */ if (RealUid != 0 && OpMode == MD_SMTP && @@ -1476,8 +1826,7 @@ smtp(nullserver, d_flags, e) char *equal = NULL; /* locate the beginning of the keyword */ - while (isascii(*p) && isspace(*p)) - p++; + SKIP_SPACE(p); if (*p == '\0') break; kp = p; @@ -1502,7 +1851,7 @@ smtp(nullserver, d_flags, e) *p++ = '\0'; if (tTd(19, 1)) - dprintf("MAIL: got arg %s=\"%s\"\n", kp, + sm_dprintf("MAIL: got arg %s=\"%s\"\n", kp, vp == NULL ? "<null>" : vp); mail_esmtp_args(kp, vp, e); @@ -1512,17 +1861,22 @@ smtp(nullserver, d_flags, e) if (argno >= MAXSMTPARGS - 1) usrerr("501 5.5.4 Too many parameters"); if (Errors > 0) - goto undo_subproc_no_pm; + sm_exc_raisenew_x(&EtypeQuickAbort, 1); } args[argno] = NULL; if (Errors > 0) - goto undo_subproc_no_pm; + sm_exc_raisenew_x(&EtypeQuickAbort, 1); /* do config file checking of the sender */ + macdefine(&e->e_macro, A_PERM, + macid("{addr_type}"), "e s"); if (rscheck("check_mail", addr, - NULL, e, TRUE, TRUE, 4, NULL) != EX_OK || + NULL, e, true, true, 3, NULL, + e->e_id) != EX_OK || Errors > 0) - goto undo_subproc_no_pm; + sm_exc_raisenew_x(&EtypeQuickAbort, 1); + macdefine(&e->e_macro, A_PERM, + macid("{addr_type}"), NULL); if (MaxMessageSize > 0 && (e->e_msgsize > MaxMessageSize || @@ -1530,80 +1884,97 @@ smtp(nullserver, d_flags, e) { usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)", MaxMessageSize); - goto undo_subproc_no_pm; + sm_exc_raisenew_x(&EtypeQuickAbort, 1); } - if (!enoughdiskspace(e->e_msgsize, TRUE)) + /* + ** XXX always check whether there is at least one fs + ** with enough space? + ** However, this may not help much: the queue group + ** selection may later on select a FS that hasn't + ** enough space. + */ + + if ((NumFileSys == 1 || NumQueue == 1) && + !enoughdiskspace(e->e_msgsize, e) +#if _FFR_ANY_FREE_FS + && !filesys_free(e->e_msgsize) +#endif /* _FFR_ANY_FREE_FS */ + ) { + /* + ** We perform this test again when the + ** queue directory is selected, in collect. + */ + usrerr("452 4.4.5 Insufficient disk space; try again later"); - goto undo_subproc_no_pm; + sm_exc_raisenew_x(&EtypeQuickAbort, 1); } if (Errors > 0) - goto undo_subproc_no_pm; + sm_exc_raisenew_x(&EtypeQuickAbort, 1); -# if _FFR_MILTER - LogUsrErrs = TRUE; - if (milterize && !bitset(EF_DISCARD, e->e_flags)) + LogUsrErrs = true; +#if MILTER + if (smtp.sm_milterlist && smtp.sm_milterize && + !bitset(EF_DISCARD, e->e_flags)) { char state; char *response; response = milter_envfrom(args, e, &state); - switch (state) - { - case SMFIR_REPLYCODE: - usrerr(response); - break; - - case SMFIR_REJECT: - usrerr("550 5.7.1 Command rejected"); - break; - - case SMFIR_DISCARD: - e->e_flags |= EF_DISCARD; - break; - - case SMFIR_TEMPFAIL: - usrerr("451 4.7.1 Please try again later"); - break; - } - if (response != NULL) - sm_free(response); + MILTER_REPLY("from"); } -# endif /* _FFR_MILTER */ +#endif /* MILTER */ if (Errors > 0) - goto undo_subproc_no_pm; + sm_exc_raisenew_x(&EtypeQuickAbort, 1); message("250 2.1.0 Sender ok"); - gotmail = TRUE; + smtp.sm_gotmail = true; + } + SM_EXCEPT(exc, "[!F]*") + { + /* + ** An error occurred while processing a MAIL command. + ** Jump to the common error handling code. + */ + + sm_exc_free(exc); + goto undo_no_pm; + } + SM_END_TRY + break; + + undo_no_pm: + e->e_flags &= ~EF_PM_NOTIFY; + undo: break; case CMDRCPT: /* rcpt -- designate recipient */ - if (!gotmail) + DELAY_CONN("RCPT"); + if (!smtp.sm_gotmail) { usrerr("503 5.0.0 Need MAIL before RCPT"); break; } SmtpPhase = "server RCPT"; - if (setjmp(TopFrame) > 0) - { - e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY); - break; - } - QuickAbort = TRUE; - LogUsrErrs = TRUE; + SM_TRY + { + QuickAbort = true; + LogUsrErrs = true; /* limit flooding of our machine */ - if (MaxRcptPerMsg > 0 && nrcpts >= MaxRcptPerMsg) + if (MaxRcptPerMsg > 0 && + smtp.sm_nrcpts >= MaxRcptPerMsg) { + /* sleep(1); / * slow down? */ usrerr("452 4.5.3 Too many recipients"); - break; + goto rcpt_done; } if (e->e_sendmode != SM_DELIVER) e->e_flags |= EF_VRFYONLY; -# if _FFR_MILTER +#if MILTER /* ** If the filter will be deleting recipients, ** don't expand them at RCPT time (in the call @@ -1615,24 +1986,46 @@ smtp(nullserver, d_flags, e) if (milter_can_delrcpts()) e->e_flags |= EF_VRFYONLY; -# endif /* _FFR_MILTER */ +#endif /* MILTER */ p = skipword(p, "to"); if (p == NULL) - break; -# if _FFR_ADDR_TYPE - define(macid("{addr_type}", NULL), "e r", e); -# endif /* _FFR_ADDR_TYPE */ - a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); -#if _FFR_ADDR_TYPE - define(macid("{addr_type}", NULL), NULL, e); -#endif /* _FFR_ADDR_TYPE */ + goto rcpt_done; + macdefine(&e->e_macro, A_PERM, + macid("{addr_type}"), "e r"); + a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, + e, true); + macdefine(&e->e_macro, A_PERM, + macid("{addr_type}"), NULL); + if (BadRcptThrottle > 0 && + n_badrcpts >= BadRcptThrottle) + { + if (LogLevel > 5 && + n_badrcpts == BadRcptThrottle) + { + sm_syslog(LOG_INFO, e->e_id, + "%.100s: Possible SMTP RCPT flood, throttling.", + CurSmtpClient); + + /* To avoid duplicated message */ + n_badrcpts++; + } + + /* + ** Don't use exponential backoff for now. + ** Some servers will open more connections + ** and actually overload the receiver even + ** more. + */ + + (void) sleep(1); + } if (Errors > 0) - break; + goto rcpt_done; if (a == NULL) { usrerr("501 5.0.0 Missing recipient"); - break; + goto rcpt_done; } if (delimptr != NULL && *delimptr != '\0') @@ -1640,25 +2033,26 @@ smtp(nullserver, d_flags, e) /* put resulting triple from parseaddr() into macros */ if (a->q_mailer != NULL) - define(macid("{rcpt_mailer}", NULL), - a->q_mailer->m_name, e); + macdefine(&e->e_macro, A_PERM, + macid("{rcpt_mailer}"), + a->q_mailer->m_name); else - define(macid("{rcpt_mailer}", NULL), - NULL, e); + macdefine(&e->e_macro, A_PERM, + macid("{rcpt_mailer}"), NULL); if (a->q_host != NULL) - define(macid("{rcpt_host}", NULL), - a->q_host, e); + macdefine(&e->e_macro, A_PERM, + macid("{rcpt_host}"), a->q_host); else - define(macid("{rcpt_host}", NULL), - "localhost", e); + macdefine(&e->e_macro, A_PERM, + macid("{rcpt_host}"), "localhost"); if (a->q_user != NULL) - define(macid("{rcpt_addr}", NULL), - a->q_user, e); + macdefine(&e->e_macro, A_PERM, + macid("{rcpt_addr}"), a->q_user); else - define(macid("{rcpt_addr}", NULL), - NULL, e); + macdefine(&e->e_macro, A_PERM, + macid("{rcpt_addr}"), NULL); if (Errors > 0) - break; + goto rcpt_done; /* now parse ESMTP arguments */ addr = p; @@ -1672,8 +2066,7 @@ smtp(nullserver, d_flags, e) char *equal = NULL; /* locate the beginning of the keyword */ - while (isascii(*p) && isspace(*p)) - p++; + SKIP_SPACE(p); if (*p == '\0') break; kp = p; @@ -1698,7 +2091,7 @@ smtp(nullserver, d_flags, e) *p++ = '\0'; if (tTd(19, 1)) - dprintf("RCPT: got arg %s=\"%s\"\n", kp, + sm_dprintf("RCPT: got arg %s=\"%s\"\n", kp, vp == NULL ? "<null>" : vp); rcpt_esmtp_args(a, kp, vp, e); @@ -1712,325 +2105,110 @@ smtp(nullserver, d_flags, e) } args[argno] = NULL; if (Errors > 0) - break; + goto rcpt_done; /* do config file checking of the recipient */ + macdefine(&e->e_macro, A_PERM, + macid("{addr_type}"), "e r"); if (rscheck("check_rcpt", addr, - NULL, e, TRUE, TRUE, 4, NULL) != EX_OK || + NULL, e, true, true, 3, NULL, + e->e_id) != EX_OK || Errors > 0) - break; + goto rcpt_done; + macdefine(&e->e_macro, A_PERM, + macid("{addr_type}"), NULL); -# if _FFR_MILTER - if (milterize && !bitset(EF_DISCARD, e->e_flags)) +#if MILTER + if (smtp.sm_milterlist && smtp.sm_milterize && + !bitset(EF_DISCARD, e->e_flags)) { char state; char *response; response = milter_envrcpt(args, e, &state); - switch (state) - { - case SMFIR_REPLYCODE: - usrerr(response); - break; - - case SMFIR_REJECT: - usrerr("550 5.7.1 Command rejected"); - break; - - case SMFIR_DISCARD: - e->e_flags |= EF_DISCARD; - break; - - case SMFIR_TEMPFAIL: - usrerr("451 4.7.1 Please try again later"); - break; - } - if (response != NULL) - sm_free(response); - } -# endif /* _FFR_MILTER */ - - define(macid("{rcpt_mailer}", NULL), NULL, e); - define(macid("{rcpt_relay}", NULL), NULL, e); - define(macid("{rcpt_addr}", NULL), NULL, e); - define(macid("{dsn_notify}", NULL), NULL, e); + MILTER_REPLY("to"); + } +#endif /* MILTER */ + + macdefine(&e->e_macro, A_PERM, + macid("{rcpt_mailer}"), NULL); + macdefine(&e->e_macro, A_PERM, + macid("{rcpt_relay}"), NULL); + macdefine(&e->e_macro, A_PERM, + macid("{rcpt_addr}"), NULL); + macdefine(&e->e_macro, A_PERM, + macid("{dsn_notify}"), NULL); if (Errors > 0) - break; + goto rcpt_done; /* save in recipient list after ESMTP mods */ a = recipient(a, &e->e_sendqueue, 0, e); if (Errors > 0) - break; + goto rcpt_done; /* no errors during parsing, but might be a duplicate */ e->e_to = a->q_paddr; if (!QS_IS_BADADDR(a->q_state)) { - if (e->e_queuedir == NOQDIR) + if (smtp.sm_nrcpts == 0) initsys(e); message("250 2.1.5 Recipient ok%s", QS_IS_QUEUEUP(a->q_state) ? " (will queue)" : ""); - nrcpts++; + smtp.sm_nrcpts++; } else { /* punt -- should keep message in ADDRESS.... */ usrerr("550 5.1.1 Addressee unknown"); } + rcpt_done: + if (Errors > 0) + ++n_badrcpts; + } + SM_EXCEPT(exc, "[!F]*") + { + /* An exception occurred while processing RCPT */ + e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY); + ++n_badrcpts; + } + SM_END_TRY break; case CMDDATA: /* data -- text of mail */ - SmtpPhase = "server DATA"; - if (!gotmail) - { - usrerr("503 5.0.0 Need MAIL command"); - break; - } - else if (nrcpts <= 0) - { - usrerr("503 5.0.0 Need RCPT (recipient)"); - break; - } - - /* put back discard bit */ - if (discard) - e->e_flags |= EF_DISCARD; - - /* check to see if we need to re-expand aliases */ - /* also reset QS_BADADDR on already-diagnosted addrs */ - doublequeue = FALSE; - for (a = e->e_sendqueue; a != NULL; a = a->q_next) - { - if (QS_IS_VERIFIED(a->q_state) && - !bitset(EF_DISCARD, e->e_flags)) - { - /* need to re-expand aliases */ - doublequeue = TRUE; - } - if (QS_IS_BADADDR(a->q_state)) - { - /* make this "go away" */ - a->q_state = QS_DONTSEND; - } - } - - /* collect the text of the message */ - SmtpPhase = "collect"; - buffer_errors(); - collect(InChannel, TRUE, NULL, e); - -# if _FFR_MILTER - if (milterize && - Errors <= 0 && - !bitset(EF_DISCARD, e->e_flags)) - { - char state; - char *response; - - response = milter_data(e, &state); - switch (state) - { - case SMFIR_REPLYCODE: - usrerr(response); - break; - - case SMFIR_REJECT: - usrerr("554 5.7.1 Command rejected"); - break; - - case SMFIR_DISCARD: - e->e_flags |= EF_DISCARD; - break; - - case SMFIR_TEMPFAIL: - usrerr("451 4.7.1 Please try again later"); - break; - } - if (response != NULL) - sm_free(response); - } - - /* abort message filters that didn't get the body */ - if (milterize) - milter_abort(e); -# endif /* _FFR_MILTER */ - - /* redefine message size */ - if ((q = macvalue(macid("{msg_size}", NULL), e)) - != NULL) - sm_free(q); - snprintf(inp, sizeof inp, "%ld", e->e_msgsize); - define(macid("{msg_size}", NULL), newstr(inp), e); - if (Errors > 0) - { - /* Log who the mail would have gone to */ - if (LogLevel > 8 && - e->e_message != NULL) - { - for (a = e->e_sendqueue; - a != NULL; - a = a->q_next) - { - if (!QS_IS_UNDELIVERED(a->q_state)) - continue; - - e->e_to = a->q_paddr; - logdelivery(NULL, NULL, - a->q_status, - e->e_message, - NULL, - (time_t) 0, e); - } - e->e_to = NULL; - } - flush_errors(TRUE); - buffer_errors(); - goto abortmessage; - } - - /* make sure we actually do delivery */ - e->e_flags &= ~EF_CLRQUEUE; - - /* from now on, we have to operate silently */ - buffer_errors(); - e->e_errormode = EM_MAIL; - - /* - ** Arrange to send to everyone. - ** If sending to multiple people, mail back - ** errors rather than reporting directly. - ** In any case, don't mail back errors for - ** anything that has happened up to - ** now (the other end will do this). - ** Truncate our transcript -- the mail has gotten - ** to us successfully, and if we have - ** to mail this back, it will be easier - ** on the reader. - ** Then send to everyone. - ** Finally give a reply code. If an error has - ** already been given, don't mail a - ** message back. - ** We goose error returns by clearing error bit. - */ - - SmtpPhase = "delivery"; - (void) bftruncate(e->e_xfp); - id = e->e_id; - - /* - ** If a header/body check (header checks or milter) - ** set EF_DISCARD, don't queueup the message -- - ** that would lose the EF_DISCARD bit and deliver - ** the message. - */ - - if (bitset(EF_DISCARD, e->e_flags)) - doublequeue = FALSE; - - if (doublequeue) - { - /* make sure it is in the queue */ - queueup(e, FALSE); - } - else - { - /* send to all recipients */ -# if NAMED_BIND - _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; - _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; -# endif /* NAMED_BIND */ - sendall(e, SM_DEFAULT); - } - e->e_to = NULL; - - /* issue success message */ - message("250 2.0.0 %s Message accepted for delivery", id); - - /* if we just queued, poke it */ - if (doublequeue && - e->e_sendmode != SM_QUEUE && - e->e_sendmode != SM_DEFER) - { - CurrentLA = sm_getla(e); - - if (!shouldqueue(e->e_msgpriority, e->e_ctime)) - { - /* close all the queue files */ - closexscript(e); - if (e->e_dfp != NULL) - (void) bfclose(e->e_dfp); - e->e_dfp = NULL; - unlockqueue(e); - - (void) dowork(e->e_queuedir, id, - TRUE, TRUE, e); - } - } - - abortmessage: - if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) - logsender(e, NULL); - e->e_flags &= ~EF_LOGSENDER; - - /* if in a child, pop back to our parent */ - if (InChild) - finis(TRUE, ExitStat); - - /* clean up a bit */ - gotmail = FALSE; - dropenvelope(e, TRUE); - CurEnv = e = newenvelope(e, CurEnv); - e->e_flags = BlankEnvelope.e_flags; + DELAY_CONN("DATA"); + smtp_data(&smtp, e); break; case CMDRSET: /* rset -- reset state */ -# if _FFR_MILTER - /* abort milter filters */ - milter_abort(e); -# endif /* _FFR_MILTER */ - if (tTd(94, 100)) message("451 4.0.0 Test failure"); else message("250 2.0.0 Reset state"); - - /* arrange to ignore any current send list */ - e->e_sendqueue = NULL; - e->e_flags |= EF_CLRQUEUE; - - if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) - logsender(e, NULL); - e->e_flags &= ~EF_LOGSENDER; - - if (InChild) - finis(TRUE, ExitStat); - - /* clean up a bit */ - gotmail = FALSE; - SuprErrs = TRUE; - dropenvelope(e, TRUE); - CurEnv = e = newenvelope(e, CurEnv); + CLEAR_STATE(cmdbuf); break; case CMDVRFY: /* vrfy -- verify address */ case CMDEXPN: /* expn -- expand address */ + vrfy = c->cmd_code == CMDVRFY; + DELAY_CONN(vrfy ? "VRFY" : "EXPN"); if (tempfail) { if (LogLevel > 9) sm_syslog(LOG_INFO, e->e_id, "SMTP %s command (%.100s) from %.100s tempfailed (due to previous checks)", - c->cmd_code == CMDVRFY ? "VRFY" : "EXPN", + vrfy ? "VRFY" : "EXPN", p, CurSmtpClient); + + /* RFC 821 doesn't allow 4xy reply code */ usrerr("550 5.7.1 Please try again later"); break; } - wt = checksmtpattack(&nverifies, MAXVRFYCOMMANDS, FALSE, - c->cmd_code == CMDVRFY ? "VRFY" : "EXPN", e); + wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS, + false, vrfy ? "VRFY" : "EXPN", e); previous = curtime(); - vrfy = c->cmd_code == CMDVRFY; if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, - PrivacyFlags)) + PrivacyFlags)) { if (vrfy) message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); @@ -2050,18 +2228,15 @@ smtp(nullserver, d_flags, e) usrerr("503 5.0.0 I demand that you introduce yourself first"); break; } - if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) - break; if (Errors > 0) - goto undo_subproc; + break; if (LogLevel > 5) - sm_syslog(LOG_INFO, e->e_id, - "%.100s: %s", + sm_syslog(LOG_INFO, e->e_id, "%.100s: %s", CurSmtpClient, shortenstring(inp, MAXSHORTSTR)); - if (setjmp(TopFrame) > 0) - goto undo_subproc; - QuickAbort = TRUE; + SM_TRY + { + QuickAbort = true; vrfyqueue = NULL; if (vrfy) e->e_flags |= EF_VRFYONLY; @@ -2075,9 +2250,10 @@ smtp(nullserver, d_flags, e) { /* do config file checking of the address */ if (rscheck(vrfy ? "check_vrfy" : "check_expn", - p, NULL, e, TRUE, FALSE, 4, NULL) - != EX_OK || Errors > 0) - goto undo_subproc; + p, NULL, e, true, false, 3, NULL, + NOQID) != EX_OK || + Errors > 0) + sm_exc_raisenew_x(&EtypeQuickAbort, 1); (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); } if (wt > 0) @@ -2089,7 +2265,7 @@ smtp(nullserver, d_flags, e) (void) sleep(t); } if (Errors > 0) - goto undo_subproc; + sm_exc_raisenew_x(&EtypeQuickAbort, 1); if (vrfyqueue == NULL) { usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN"); @@ -2110,13 +2286,22 @@ smtp(nullserver, d_flags, e) printvrfyaddr(vrfyqueue, a == NULL, vrfy); vrfyqueue = a; } - if (InChild) - finis(TRUE, ExitStat); + } + SM_EXCEPT(exc, "[!F]*") + { + /* + ** An exception occurred while processing VRFY/EXPN + */ + + sm_exc_free(exc); + goto undo; + } + SM_END_TRY break; case CMDETRN: /* etrn -- force queue flush */ - if (bitset(PRIV_NOETRN, PrivacyFlags) || - bitnset(D_NOETRN, d_flags)) + DELAY_CONN("ETRN"); + if (!bitset(SRV_OFFER_ETRN, features) || UseMSP) { /* different message for MSA ? */ message("502 5.7.0 Sorry, we do not allow this operation"); @@ -2133,7 +2318,7 @@ smtp(nullserver, d_flags, e) sm_syslog(LOG_INFO, e->e_id, "SMTP ETRN command (%.100s) from %.100s tempfailed (due to previous checks)", p, CurSmtpClient); - usrerr("451 4.7.1 Please try again later"); + usrerr(MSG_TEMPFAIL); break; } @@ -2144,80 +2329,119 @@ smtp(nullserver, d_flags, e) } /* crude way to avoid denial-of-service attacks */ - (void) checksmtpattack(&n_etrn, MAXETRNCOMMANDS, TRUE, + (void) checksmtpattack(&n_etrn, MAXETRNCOMMANDS, true, "ETRN", e); - /* do config file checking of the parameter */ - if (rscheck("check_etrn", p, NULL, e, TRUE, FALSE, 4, - NULL) != EX_OK || Errors > 0) + /* + ** Do config file checking of the parameter. + ** Even though we have srv_features now, we still + ** need this ruleset because the former is called + ** when the connection has been established, while + ** this ruleset is called when the command is + ** actually issued and therefore has all information + ** available to make a decision. + */ + + if (rscheck("check_etrn", p, NULL, e, true, false, 3, + NULL, NOQID) != EX_OK || Errors > 0) break; if (LogLevel > 5) sm_syslog(LOG_INFO, e->e_id, - "%.100s: ETRN %s", - CurSmtpClient, + "%.100s: ETRN %s", CurSmtpClient, shortenstring(p, MAXSHORTSTR)); id = p; + if (*id == '#') + { + int wgrp; + + id++; + wgrp = name2qid(id); + if (!ISVALIDQGRP(wgrp)) + { + usrerr("459 4.5.4 Queue %s unknown", + id); + break; + } + ok = run_work_group(wgrp, true, false, + false, true); + if (ok && Errors == 0) + message("250 2.0.0 Queuing for queue group %s started", id); + break; + } + if (*id == '@') id++; else *--id = '@'; - new = (QUEUE_CHAR *)xalloc(sizeof(QUEUE_CHAR)); + new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR)); + if (new == NULL) + { + syserr("500 5.5.0 ETRN out of memory"); + break; + } new->queue_match = id; + new->queue_negate = false; new->queue_next = NULL; QueueLimitRecipient = new; - ok = runqueue(TRUE, FALSE); - sm_free(QueueLimitRecipient); + ok = runqueue(true, false, false, true); + sm_free(QueueLimitRecipient); /* XXX */ QueueLimitRecipient = NULL; if (ok && Errors == 0) message("250 2.0.0 Queuing for node %s started", p); break; case CMDHELP: /* help -- give user info */ + DELAY_CONN("HELP"); help(p, e); break; case CMDNOOP: /* noop -- do nothing */ - (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE, + DELAY_CONN("NOOP"); + (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, true, "NOOP", e); message("250 2.0.0 OK"); break; case CMDQUIT: /* quit -- leave mail */ message("221 2.0.0 %s closing connection", MyHostName); +#if PIPELINING + (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); +#endif /* PIPELINING */ + + if (smtp.sm_nrcpts > 0) + logundelrcpts(e, "aborted by sender", 9, false); /* arrange to ignore any current send list */ e->e_sendqueue = NULL; -# if STARTTLS +#if STARTTLS /* shutdown TLS connection */ if (tls_active) { (void) endtls(srv_ssl, "server"); - tls_active = FALSE; + tls_active = false; } -# endif /* STARTTLS */ -# if SASL +#endif /* STARTTLS */ +#if SASL if (authenticating == SASL_IS_AUTH) { sasl_dispose(&conn); authenticating = SASL_NOT_AUTH; + /* XXX sasl_done(); this is a child */ } -# endif /* SASL */ +#endif /* SASL */ doquit: /* avoid future 050 messages */ disconnect(1, e); -# if _FFR_MILTER +#if MILTER /* close out milter filters */ milter_quit(e); -# endif /* _FFR_MILTER */ - - if (InChild) - ExitStat = EX_QUIT; +#endif /* MILTER */ if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) logsender(e, NULL); @@ -2227,49 +2451,48 @@ doquit: { char *d; - d = macvalue(macid("{daemon_name}", NULL), e); + d = macvalue(macid("{daemon_name}"), e); if (d == NULL) d = "stdin"; - sm_syslog(LOG_INFO, NULL, - "%.100s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s", + + /* + ** even though this id is "bogus", it makes + ** it simpler to "grep" related events, e.g., + ** timeouts for the same connection. + */ + + sm_syslog(LOG_INFO, e->e_id, + "%.100s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s", CurSmtpClient, d); } - finis(TRUE, ExitStat); +#if PROFILING + return; +#endif /* PROFILING */ + finis(true, ExitStat); /* NOTREACHED */ case CMDVERB: /* set verbose mode */ + DELAY_CONN("VERB"); if (bitset(PRIV_NOEXPN, PrivacyFlags) || + !bitset(SRV_OFFER_VERB, features) || bitset(PRIV_NOVERB, PrivacyFlags)) { /* this would give out the same info */ message("502 5.7.0 Verbose unavailable"); break; } - (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE, + (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, true, "VERB", e); Verbose = 1; set_delivery_mode(SM_DELIVER, e); message("250 2.0.0 Verbose mode"); break; - case CMDONEX: /* doing one transaction only */ - (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE, - "ONEX", e); - OneXact = TRUE; - message("250 2.0.0 Only one transaction"); - break; - - case CMDXUSR: /* initial (user) submission */ - (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE, - "XUSR", e); - define(macid("{daemon_flags}", NULL), "c u", CurEnv); - message("250 2.0.0 Initial submission"); - break; - -# if SMTPDEBUG +#if SMTPDEBUG case CMDDBGQSHOW: /* show queues */ - printf("Send Queue="); - printaddr(e->e_sendqueue, TRUE); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Send Queue="); + printaddr(e->e_sendqueue, true); break; case CMDDBGDEBUG: /* set debug mode */ @@ -2278,11 +2501,12 @@ doquit: message("200 2.0.0 Debug set"); break; -# else /* SMTPDEBUG */ +#else /* SMTPDEBUG */ case CMDDBGQSHOW: /* show queues */ case CMDDBGDEBUG: /* set debug mode */ -# endif /* SMTPDEBUG */ +#endif /* SMTPDEBUG */ case CMDLOGBOGUS: /* bogus command */ + DELAY_CONN("Bogus"); if (LogLevel > 0) sm_syslog(LOG_CRIT, e->e_id, "\"%s\" command from %.100s (%.100s)", @@ -2291,7 +2515,8 @@ doquit: /* FALLTHROUGH */ case CMDERROR: /* unknown command */ - if (++badcommands > MAXBADCOMMANDS) +#if MAXBADCOMMANDS > 0 + if (++n_badcmds > MAXBADCOMMANDS) { message("421 4.7.0 %s Too many bad commands; closing connection", MyHostName); @@ -2300,26 +2525,384 @@ doquit: e->e_sendqueue = NULL; goto doquit; } +#endif /* MAXBADCOMMANDS > 0 */ usrerr("500 5.5.1 Command unrecognized: \"%s\"", shortenstring(inp, MAXSHORTSTR)); break; case CMDUNIMPL: + DELAY_CONN("Unimpl"); usrerr("502 5.5.1 Command not implemented: \"%s\"", shortenstring(inp, MAXSHORTSTR)); break; default: + DELAY_CONN("default"); errno = 0; syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code); break; } -# if SASL +#if SASL } -# endif /* SASL */ +#endif /* SASL */ + } + SM_EXCEPT(exc, "[!F]*") + { + /* + ** The only possible exception is "E:mta.quickabort". + ** There is nothing to do except fall through and loop. + */ + } + SM_END_TRY + } +} +/* +** SMTP_DATA -- implement the SMTP DATA command. +** +** Parameters: +** smtp -- status of SMTP connection. +** e -- envelope. +** +** Returns: +** none. +** +** Side Effects: +** possibly sends message. +*/ + +static void +smtp_data(smtp, e) + SMTP_T *smtp; + ENVELOPE *e; +{ +#if MILTER + bool milteraccept; +#endif /* MILTER */ + bool aborting; + bool doublequeue; + ADDRESS *a; + ENVELOPE *ee; + char *id; + char buf[32]; + + SmtpPhase = "server DATA"; + if (!smtp->sm_gotmail) + { + usrerr("503 5.0.0 Need MAIL command"); + return; + } + else if (smtp->sm_nrcpts <= 0) + { + usrerr("503 5.0.0 Need RCPT (recipient)"); + return; + } + (void) sm_snprintf(buf, sizeof buf, "%u", smtp->sm_nrcpts); + if (rscheck("check_data", buf, NULL, e, + true, false, 3, NULL, e->e_id) != EX_OK) + return; + + /* put back discard bit */ + if (smtp->sm_discard) + e->e_flags |= EF_DISCARD; + + /* check to see if we need to re-expand aliases */ + /* also reset QS_BADADDR on already-diagnosted addrs */ + doublequeue = false; + for (a = e->e_sendqueue; a != NULL; a = a->q_next) + { + if (QS_IS_VERIFIED(a->q_state) && + !bitset(EF_DISCARD, e->e_flags)) + { + /* need to re-expand aliases */ + doublequeue = true; + } + if (QS_IS_BADADDR(a->q_state)) + { + /* make this "go away" */ + a->q_state = QS_DONTSEND; + } + } + + /* collect the text of the message */ + SmtpPhase = "collect"; + buffer_errors(); + +#if _FFR_ADAPTIVE_EOL + /* triggers error in collect, disabled for now */ + if (smtp->sm_crlf) + e->e_flags |= EF_NL_NOT_EOL; +#endif /* _FFR_ADAPTIVE_EOL */ + + collect(InChannel, true, NULL, e); + + /* redefine message size */ + (void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize); + macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf); + +#if _FFR_CHECK_EOM + /* rscheck() will set Errors or EF_DISCARD if it trips */ + (void) rscheck("check_eom", buf, NULL, e, false, + true, 3, NULL, e->e_id); +#endif /* _FFR_CHECK_EOM */ + +#if MILTER + milteraccept = true; + if (smtp->sm_milterlist && smtp->sm_milterize && + Errors <= 0 && + !bitset(EF_DISCARD, e->e_flags)) + { + char state; + char *response; + + response = milter_data(e, &state); + switch (state) + { + case SMFIR_REPLYCODE: + milteraccept = false; + usrerr(response); + break; + + case SMFIR_REJECT: + milteraccept = false; + if (MilterLogLevel > 8) + sm_syslog(LOG_INFO, e->e_id, + "Milter: reject, message data"); + usrerr("554 5.7.1 Command rejected"); + break; + + case SMFIR_DISCARD: + milteraccept = false; + e->e_flags |= EF_DISCARD; + break; + + case SMFIR_TEMPFAIL: + milteraccept = false; + usrerr(MSG_TEMPFAIL); + break; + } + if (response != NULL) + sm_free(response); } + /* Milter may have changed message size */ + (void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize); + macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf); + + /* abort message filters that didn't get the body & log msg is OK */ + if (smtp->sm_milterlist && smtp->sm_milterize) + { + milter_abort(e); + if (milteraccept && MilterLogLevel > 9) + sm_syslog(LOG_INFO, e->e_id, "Milter accept: message"); + } +#endif /* MILTER */ + + /* + ** If a header/body check (header checks or milter) + ** set EF_DISCARD, don't queueup the message -- + ** that would lose the EF_DISCARD bit and deliver + ** the message. + */ + + if (bitset(EF_DISCARD, e->e_flags)) + doublequeue = false; + + aborting = Errors > 0; + if (!aborting) + aborting = !split_by_recipient(e); + + if (aborting) + { + /* Log who the mail would have gone to */ + logundelrcpts(e, e->e_message, 8, false); + flush_errors(true); + buffer_errors(); + goto abortmessage; + } + + /* from now on, we have to operate silently */ + buffer_errors(); + +#if 0 + /* + ** Clear message, it may contain an error from the SMTP dialogue. + ** This error must not show up in the queue. + ** Some error message should show up, e.g., alias database + ** not available, but others shouldn't, e.g., from check_rcpt. + */ + + e->e_message = NULL; +#endif /* 0 */ + + /* + ** Arrange to send to everyone. + ** If sending to multiple people, mail back + ** errors rather than reporting directly. + ** In any case, don't mail back errors for + ** anything that has happened up to + ** now (the other end will do this). + ** Truncate our transcript -- the mail has gotten + ** to us successfully, and if we have + ** to mail this back, it will be easier + ** on the reader. + ** Then send to everyone. + ** Finally give a reply code. If an error has + ** already been given, don't mail a + ** message back. + ** We goose error returns by clearing error bit. + */ + + SmtpPhase = "delivery"; + (void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL); + id = e->e_id; + +#if NAMED_BIND + _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; + _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; +#endif /* NAMED_BIND */ + + for (ee = e; ee != NULL; ee = ee->e_sibling) + { + /* make sure we actually do delivery */ + ee->e_flags &= ~EF_CLRQUEUE; + + /* from now on, operate silently */ + ee->e_errormode = EM_MAIL; + + if (doublequeue) + { + /* make sure it is in the queue */ + queueup(ee, false, true); + } + else + { + /* send to all recipients */ + sendall(ee, SM_DEFAULT); + } + ee->e_to = NULL; + } + + /* issue success message */ + message("250 2.0.0 %s Message accepted for delivery", id); + + /* if we just queued, poke it */ + if (doublequeue) + { + bool anything_to_send = false; + + sm_getla(); + for (ee = e; ee != NULL; ee = ee->e_sibling) + { + if (WILL_BE_QUEUED(ee->e_sendmode)) + continue; + if (shouldqueue(ee->e_msgpriority, ee->e_ctime)) + { + ee->e_sendmode = SM_QUEUE; + continue; + } + anything_to_send = true; + + /* close all the queue files */ + closexscript(ee); + if (ee->e_dfp != NULL) + { + (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT); + ee->e_dfp = NULL; + } + unlockqueue(ee); + } + if (anything_to_send) + { +#if PIPELINING + /* + ** XXX if we don't do this, we get 250 twice + ** because it is also flushed in the child. + */ + + (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); +#endif /* PIPELINING */ + (void) doworklist(e, true, true); + } + } + + abortmessage: + if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) + logsender(e, NULL); + e->e_flags &= ~EF_LOGSENDER; + + /* clean up a bit */ + smtp->sm_gotmail = false; + + /* + ** Call dropenvelope if and only if the envelope is *not* + ** being processed by the child process forked by doworklist(). + */ + + if (aborting || bitset(EF_DISCARD, e->e_flags)) + dropenvelope(e, true, false); + else + { + for (ee = e; ee != NULL; ee = ee->e_sibling) + { + if (WILL_BE_QUEUED(ee->e_sendmode)) + dropenvelope(ee, true, false); + } + } + sm_rpool_free(e->e_rpool); + + /* + ** At this point, e == &MainEnvelope, but if we did splitting, + ** then CurEnv may point to an envelope structure that was just + ** freed with the rpool. So reset CurEnv *before* calling + ** newenvelope. + */ + + CurEnv = e; + newenvelope(e, e, sm_rpool_new_x(NULL)); + e->e_flags = BlankEnvelope.e_flags; +} +/* +** LOGUNDELRCPTS -- log undelivered (or all) recipients. +** +** Parameters: +** e -- envelope. +** msg -- message for Stat= +** level -- log level. +** all -- log all recipients. +** +** Returns: +** none. +** +** Side Effects: +** logs undelivered (or all) recipients +*/ + +void +logundelrcpts(e, msg, level, all) + ENVELOPE *e; + char *msg; + int level; + bool all; +{ + ADDRESS *a; + + if (LogLevel <= level || msg == NULL || *msg == '\0') + return; + + /* Clear $h so relay= doesn't get mislogged by logdelivery() */ + macdefine(&e->e_macro, A_PERM, 'h', NULL); + + /* Log who the mail would have gone to */ + for (a = e->e_sendqueue; a != NULL; a = a->q_next) + { + if (!QS_IS_UNDELIVERED(a->q_state) && !all) + continue; + e->e_to = a->q_paddr; + logdelivery(NULL, NULL, a->q_status, msg, NULL, + (time_t) 0, e); + } + e->e_to = NULL; } /* ** CHECKSMTPATTACK -- check for denial-of-service attack by repetition @@ -2341,12 +2924,15 @@ doquit: static time_t checksmtpattack(pcounter, maxcount, waitnow, cname, e) - volatile int *pcounter; + volatile unsigned int *pcounter; int maxcount; bool waitnow; char *cname; ENVELOPE *e; { + if (maxcount <= 0) /* no limit */ + return (time_t) 0; + if (++(*pcounter) >= maxcount) { time_t s; @@ -2354,23 +2940,100 @@ checksmtpattack(pcounter, maxcount, waitnow, cname, e) if (*pcounter == maxcount && LogLevel > 5) { sm_syslog(LOG_INFO, e->e_id, - "%.100s: possible SMTP attack: command=%.40s, count=%d", + "%.100s: possible SMTP attack: command=%.40s, count=%u", CurSmtpClient, cname, *pcounter); } s = 1 << (*pcounter - maxcount); - if (s >= MAXTIMEOUT) + if (s >= MAXTIMEOUT || s <= 0) s = MAXTIMEOUT; + /* sleep at least 1 second before returning */ (void) sleep(*pcounter / maxcount); s -= *pcounter / maxcount; if (waitnow) { (void) sleep(s); - return(0); + return 0; } - return(s); + return s; + } + return (time_t) 0; +} +/* +** SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server +** +** Parameters: +** none. +** +** Returns: +** nothing. +** +** Side Effects: +** may change I/O fd. +*/ + +static void +setup_smtpd_io() +{ + int inchfd, outchfd, outfd; + + inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL); + outchfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL); + outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL); + if (outchfd != outfd) + { + /* arrange for debugging output to go to remote host */ + (void) dup2(outchfd, outfd); + } + + /* + ** if InChannel and OutChannel are stdin/stdout + ** and connected to ttys + ** and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT, + ** then "chain" them together. + */ + + if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO && + isatty(inchfd) && isatty(outchfd)) + { + int inmode, outmode; + + inmode = fcntl(inchfd, F_GETFL, 0); + if (inmode == -1) + { + if (LogLevel > 11) + sm_syslog(LOG_INFO, NOQID, + "fcntl(inchfd, F_GETFL) failed: %s", + sm_errstring(errno)); + return; + } + outmode = fcntl(outchfd, F_GETFL, 0); + if (outmode == -1) + { + if (LogLevel > 11) + sm_syslog(LOG_INFO, NOQID, + "fcntl(outchfd, F_GETFL) failed: %s", + sm_errstring(errno)); + return; + } + if (bitset(O_NONBLOCK, inmode) || + bitset(O_NONBLOCK, outmode) || + fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1) + return; + outmode = fcntl(outchfd, F_GETFL, 0); + if (outmode != -1 && bitset(O_NONBLOCK, outmode)) + { + /* changing InChannel also changes OutChannel */ + sm_io_automode(OutChannel, InChannel); + if (tTd(97, 4) && LogLevel > 9) + sm_syslog(LOG_INFO, NOQID, + "set automode for I (%d)/O (%d) in SMTP server", + inchfd, outchfd); + } + + /* undo change of inchfd */ + (void) fcntl(inchfd, F_SETFL, inmode); } - return((time_t) 0); } /* ** SKIPWORD -- skip a fixed word. @@ -2396,8 +3059,7 @@ skipword(p, w) char *firstp = p; /* find beginning of word */ - while (isascii(*p) && isspace(*p)) - p++; + SKIP_SPACE(p); q = p; /* find end of word */ @@ -2413,14 +3075,13 @@ skipword(p, w) return NULL; } *p++ = '\0'; - while (isascii(*p) && isspace(*p)) - p++; + SKIP_SPACE(p); if (*p == '\0') goto syntax; /* see if the input word matches desired word */ - if (strcasecmp(q, w)) + if (sm_strcasecmp(q, w)) goto syntax; return p; @@ -2443,14 +3104,14 @@ mail_esmtp_args(kp, vp, e) char *vp; ENVELOPE *e; { - if (strcasecmp(kp, "size") == 0) + if (sm_strcasecmp(kp, "size") == 0) { if (vp == NULL) { usrerr("501 5.5.2 SIZE requires a value"); /* NOTREACHED */ } - define(macid("{msg_size}", NULL), newstr(vp), e); + macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp); e->e_msgsize = strtol(vp, (char **) NULL, 10); if (e->e_msgsize == LONG_MAX && errno == ERANGE) { @@ -2458,30 +3119,29 @@ mail_esmtp_args(kp, vp, e) /* NOTREACHED */ } } - else if (strcasecmp(kp, "body") == 0) + else if (sm_strcasecmp(kp, "body") == 0) { if (vp == NULL) { usrerr("501 5.5.2 BODY requires a value"); /* NOTREACHED */ } - else if (strcasecmp(vp, "8bitmime") == 0) + else if (sm_strcasecmp(vp, "8bitmime") == 0) { - SevenBitInput = FALSE; + SevenBitInput = false; } - else if (strcasecmp(vp, "7bit") == 0) + else if (sm_strcasecmp(vp, "7bit") == 0) { - SevenBitInput = TRUE; + SevenBitInput = true; } else { - usrerr("501 5.5.4 Unknown BODY type %s", - vp); + usrerr("501 5.5.4 Unknown BODY type %s", vp); /* NOTREACHED */ } - e->e_bodytype = newstr(vp); + e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp); } - else if (strcasecmp(kp, "envid") == 0) + else if (sm_strcasecmp(kp, "envid") == 0) { if (bitset(PRIV_NORECEIPTS, PrivacyFlags)) { @@ -2503,10 +3163,11 @@ mail_esmtp_args(kp, vp, e) usrerr("501 5.5.0 Duplicate ENVID parameter"); /* NOTREACHED */ } - e->e_envid = newstr(vp); - define(macid("{dsn_envid}", NULL), newstr(vp), e); + e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp); + macdefine(&e->e_macro, A_PERM, + macid("{dsn_envid}"), e->e_envid); } - else if (strcasecmp(kp, "ret") == 0) + else if (sm_strcasecmp(kp, "ret") == 0) { if (bitset(PRIV_NORECEIPTS, PrivacyFlags)) { @@ -2524,23 +3185,24 @@ mail_esmtp_args(kp, vp, e) /* NOTREACHED */ } e->e_flags |= EF_RET_PARAM; - if (strcasecmp(vp, "hdrs") == 0) + if (sm_strcasecmp(vp, "hdrs") == 0) e->e_flags |= EF_NO_BODY_RETN; - else if (strcasecmp(vp, "full") != 0) + else if (sm_strcasecmp(vp, "full") != 0) { usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp); /* NOTREACHED */ } - define(macid("{dsn_ret}", NULL), newstr(vp), e); + macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp); } -# if SASL - else if (strcasecmp(kp, "auth") == 0) +#if SASL + else if (sm_strcasecmp(kp, "auth") == 0) { int len; char *q; char *auth_param; /* the value of the AUTH=x */ bool saveQuickAbort = QuickAbort; bool saveSuprErrs = SuprErrs; + bool saveExitStat = ExitStat; char pbuf[256]; if (vp == NULL) @@ -2558,7 +3220,7 @@ mail_esmtp_args(kp, vp, e) else len = strlen(vp) + 1; auth_param = xalloc(len); - (void) strlcpy(auth_param, vp, len); + (void) sm_strlcpy(auth_param, vp, len); if (!xtextok(auth_param)) { usrerr("501 5.5.4 Syntax error in AUTH parameter value"); @@ -2567,11 +3229,11 @@ mail_esmtp_args(kp, vp, e) } /* XXX this might be cut off */ - snprintf(pbuf, sizeof pbuf, "%s", xuntextify(auth_param)); + (void) sm_strlcpy(pbuf, xuntextify(auth_param), sizeof pbuf); /* xalloc() the buffer instead? */ /* XXX define this always or only if trusted? */ - define(macid("{auth_author}", NULL), newstr(pbuf), e); + macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"), pbuf); /* ** call Strust_auth to find out whether @@ -2580,35 +3242,111 @@ mail_esmtp_args(kp, vp, e) ** (required by RFC, leave it to ruleset?) */ - SuprErrs = TRUE; - QuickAbort = FALSE; + SuprErrs = true; + QuickAbort = false; if (strcmp(auth_param, "<>") != 0 && - (rscheck("trust_auth", pbuf, NULL, e, TRUE, FALSE, 10, - NULL) != EX_OK || Errors > 0)) + (rscheck("trust_auth", pbuf, NULL, e, true, false, 9, + NULL, NOQID) != EX_OK || Errors > 0)) { if (tTd(95, 8)) { q = e->e_auth_param; - dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n", + sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n", pbuf, (q == NULL) ? "" : q); } + /* not trusted */ - e->e_auth_param = newstr("<>"); + e->e_auth_param = "<>"; } else { if (tTd(95, 8)) - dprintf("auth=\"%.100s\" trusted\n", pbuf); - e->e_auth_param = newstr(auth_param); + sm_dprintf("auth=\"%.100s\" trusted\n", pbuf); + e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, + auth_param); } - sm_free(auth_param); + sm_free(auth_param); /* XXX */ /* reset values */ Errors = 0; QuickAbort = saveQuickAbort; SuprErrs = saveSuprErrs; + ExitStat = saveExitStat; + } +#endif /* SASL */ +#define PRTCHAR(c) ((isascii(c) && isprint(c)) ? (c) : '?') + + /* + ** "by" is only accepted if DeliverByMin >= 0. + ** We maybe could add this to the list of server_features. + */ + + else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0) + { + char *s; + + if (vp == NULL) + { + usrerr("501 5.5.2 BY= requires a value"); + /* NOTREACHED */ + } + e->e_deliver_by = strtol(vp, &s, 10); + if (e->e_deliver_by == LONG_MIN || + e->e_deliver_by == LONG_MAX || + e->e_deliver_by > 999999999l || + e->e_deliver_by < -999999999l) + { + usrerr("501 5.5.2 BY=%s out of range", vp); + /* NOTREACHED */ + } + if (s == NULL || *s != ';') + { + usrerr("501 5.5.2 BY= missing ';'"); + /* NOTREACHED */ + } + e->e_dlvr_flag = 0; + ++s; /* XXX: spaces allowed? */ + SKIP_SPACE(s); + switch (tolower(*s)) + { + case 'n': + e->e_dlvr_flag = DLVR_NOTIFY; + break; + case 'r': + e->e_dlvr_flag = DLVR_RETURN; + if (e->e_deliver_by <= 0) + { + usrerr("501 5.5.4 mode R requires BY time > 0"); + /* NOTREACHED */ + } + if (DeliverByMin > 0 && e->e_deliver_by > 0 && + e->e_deliver_by < DeliverByMin) + { + usrerr("555 5.5.2 time %ld less than %ld", + e->e_deliver_by, (long) DeliverByMin); + /* NOTREACHED */ + } + break; + default: + usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s)); + /* NOTREACHED */ + } + ++s; /* XXX: spaces allowed? */ + SKIP_SPACE(s); + switch (tolower(*s)) + { + case 't': + e->e_dlvr_flag |= DLVR_TRACE; + break; + case '\0': + break; + default: + usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s)); + /* NOTREACHED */ + } + + /* XXX: check whether more characters follow? */ } -# endif /* SASL */ else { usrerr("555 5.5.4 %s parameter unrecognized", kp); @@ -2635,7 +3373,7 @@ rcpt_esmtp_args(a, kp, vp, e) char *vp; ENVELOPE *e; { - if (strcasecmp(kp, "notify") == 0) + if (sm_strcasecmp(kp, "notify") == 0) { char *p; @@ -2651,20 +3389,20 @@ rcpt_esmtp_args(a, kp, vp, e) } a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); a->q_flags |= QHASNOTIFY; - define(macid("{dsn_notify}", NULL), newstr(vp), e); + macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp); - if (strcasecmp(vp, "never") == 0) + if (sm_strcasecmp(vp, "never") == 0) return; for (p = vp; p != NULL; vp = p) { p = strchr(p, ','); if (p != NULL) *p++ = '\0'; - if (strcasecmp(vp, "success") == 0) + if (sm_strcasecmp(vp, "success") == 0) a->q_flags |= QPINGONSUCCESS; - else if (strcasecmp(vp, "failure") == 0) + else if (sm_strcasecmp(vp, "failure") == 0) a->q_flags |= QPINGONFAILURE; - else if (strcasecmp(vp, "delay") == 0) + else if (sm_strcasecmp(vp, "delay") == 0) a->q_flags |= QPINGONDELAY; else { @@ -2674,7 +3412,7 @@ rcpt_esmtp_args(a, kp, vp, e) } } } - else if (strcasecmp(kp, "orcpt") == 0) + else if (sm_strcasecmp(kp, "orcpt") == 0) { if (bitset(PRIV_NORECEIPTS, PrivacyFlags)) { @@ -2696,7 +3434,7 @@ rcpt_esmtp_args(a, kp, vp, e) usrerr("501 5.5.0 Duplicate ORCPT parameter"); /* NOTREACHED */ } - a->q_orcpt = newstr(vp); + a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp); } else { @@ -2708,7 +3446,7 @@ rcpt_esmtp_args(a, kp, vp, e) ** PRINTVRFYADDR -- print an entry in the verify queue ** ** Parameters: -** a -- the address to print +** a -- the address to print. ** last -- set if this is the last one. ** vrfy -- set if this is a VRFY command. ** @@ -2730,21 +3468,21 @@ printvrfyaddr(a, last, vrfy) if (vrfy && a->q_mailer != NULL && !bitnset(M_VRFY250, a->q_mailer->m_flags)) - (void) strlcpy(fmtbuf, "252", sizeof fmtbuf); + (void) sm_strlcpy(fmtbuf, "252", sizeof fmtbuf); else - (void) strlcpy(fmtbuf, "250", sizeof fmtbuf); + (void) sm_strlcpy(fmtbuf, "250", sizeof fmtbuf); fmtbuf[3] = last ? ' ' : '-'; - (void) strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4); + (void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4); if (a->q_fullname == NULL) { if ((a->q_mailer == NULL || a->q_mailer->m_addrtype == NULL || - strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) && + sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) && strchr(a->q_user, '@') == NULL) - (void) strlcpy(&fmtbuf[OFFF], "<%s@%s>", + (void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>", sizeof fmtbuf - OFFF); else - (void) strlcpy(&fmtbuf[OFFF], "<%s>", + (void) sm_strlcpy(&fmtbuf[OFFF], "<%s>", sizeof fmtbuf - OFFF); message(fmtbuf, a->q_user, MyHostName); } @@ -2752,127 +3490,27 @@ printvrfyaddr(a, last, vrfy) { if ((a->q_mailer == NULL || a->q_mailer->m_addrtype == NULL || - strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) && + sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) && strchr(a->q_user, '@') == NULL) - (void) strlcpy(&fmtbuf[OFFF], "%s <%s@%s>", + (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>", sizeof fmtbuf - OFFF); else - (void) strlcpy(&fmtbuf[OFFF], "%s <%s>", + (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>", sizeof fmtbuf - OFFF); message(fmtbuf, a->q_fullname, a->q_user, MyHostName); } } -/* -** RUNINCHILD -- return twice -- once in the child, then in the parent again -** -** Parameters: -** label -- a string used in error messages -** -** Returns: -** RIC_INCHILD in the child -** RIC_INPARENT in the parent -** RIC_TEMPFAIL tempfail condition -** -** Side Effects: -** none. -*/ - -static int -runinchild(label, e) - char *label; - register ENVELOPE *e; -{ - pid_t childpid; - - if (!OneXact) - { - extern int NumQueues; - - /* - ** advance state of PRNG - ** this is necessary because otherwise all child processes - ** will produce the same PRN sequence and hence the selection - ** of a queue directory is not "really" random. - */ - if (NumQueues > 1) - (void) get_random(); - - /* - ** Disable child process reaping, in case ETRN has preceded - ** MAIL command, and then fork. - */ - - (void) blocksignal(SIGCHLD); - - - childpid = dofork(); - if (childpid < 0) - { - syserr("451 4.3.0 %s: cannot fork", label); - (void) releasesignal(SIGCHLD); - return RIC_INPARENT; - } - if (childpid > 0) - { - auto int st; - - /* parent -- wait for child to complete */ - sm_setproctitle(TRUE, e, "server %s child wait", - CurSmtpClient); - st = waitfor(childpid); - if (st == -1) - syserr("451 4.3.0 %s: lost child", label); - else if (!WIFEXITED(st)) - { - syserr("451 4.3.0 %s: died on signal %d", - label, st & 0177); - return RIC_TEMPFAIL; - } - - /* if exited on a QUIT command, complete the process */ - if (WEXITSTATUS(st) == EX_QUIT) - { - disconnect(1, e); - finis(TRUE, ExitStat); - } - - /* restore the child signal */ - (void) releasesignal(SIGCHLD); - - return RIC_INPARENT; - } - else - { - /* child */ - InChild = TRUE; - QuickAbort = FALSE; - - /* Reset global flags */ - RestartRequest = NULL; - ShutdownRequest = NULL; - PendingSignal = 0; - - clearstats(); - clearenvelope(e, FALSE); - assign_queueid(e); - (void) setsignal(SIGCHLD, SIG_DFL); - (void) releasesignal(SIGCHLD); - } - } - return RIC_INCHILD; -} - -# if SASL +#if SASL /* ** SASLMECHS -- get list of possible AUTH mechanisms ** ** Parameters: -** conn -- SASL connection info -** mechlist -- output parameter for list of mechanisms +** conn -- SASL connection info. +** mechlist -- output parameter for list of mechanisms. ** ** Returns: -** number of mechs +** number of mechs. */ static int @@ -2885,35 +3523,36 @@ saslmechs(conn, mechlist) /* "user" is currently unused */ result = sasl_listmech(conn, "user", /* XXX */ "", " ", "", mechlist, - (u_int *)&len, (u_int *)&num); - if (result == SASL_OK && num > 0) - { - if (LogLevel > 11) - sm_syslog(LOG_INFO, NOQID, - "SASL: available mech=%s, allowed mech=%s", - *mechlist, AuthMechanisms); - *mechlist = intersect(AuthMechanisms, *mechlist); - } - else + (unsigned int *)&len, (unsigned int *)&num); + if (result != SASL_OK) { if (LogLevel > 9) sm_syslog(LOG_WARNING, NOQID, - "SASL error: listmech=%d, num=%d", + "AUTH error: listmech=%d, num=%d", result, num); num = 0; } + if (num > 0) + { + if (LogLevel > 11) + sm_syslog(LOG_INFO, NOQID, + "AUTH: available mech=%s, allowed mech=%s", + *mechlist, AuthMechanisms); + *mechlist = intersect(AuthMechanisms, *mechlist, NULL); + } + else + *mechlist = NULL; /* be paranoid... */ return num; } - /* ** PROXY_POLICY -- define proxy policy for AUTH ** ** Parameters: -** conntext -- unused -** auth_identity -- authentication identity -** requested_user -- authorization identity -** user -- allowed user (output) -** errstr -- possible error string (output) +** context -- unused. +** auth_identity -- authentication identity. +** requested_user -- authorization identity. +** user -- allowed user (output). +** errstr -- possible error string (output). ** ** Returns: ** ok? @@ -2932,1275 +3571,139 @@ proxy_policy(context, auth_identity, requested_user, user, errstr) *user = newstr(auth_identity); return SASL_OK; } +#endif /* SASL */ -# endif /* SASL */ - -# if STARTTLS -# if !TLS_NO_RSA -RSA *rsa_tmp; /* temporary RSA key */ -static RSA * tmp_rsa_key __P((SSL *, int, int)); -# endif /* !TLS_NO_RSA */ - -# if !NO_DH -static DH *get_dh512 __P((void)); - -static unsigned char dh512_p[] = -{ - 0xDA,0x58,0x3C,0x16,0xD9,0x85,0x22,0x89,0xD0,0xE4,0xAF,0x75, - 0x6F,0x4C,0xCA,0x92,0xDD,0x4B,0xE5,0x33,0xB8,0x04,0xFB,0x0F, - 0xED,0x94,0xEF,0x9C,0x8A,0x44,0x03,0xED,0x57,0x46,0x50,0xD3, - 0x69,0x99,0xDB,0x29,0xD7,0x76,0x27,0x6B,0xA2,0xD3,0xD4,0x12, - 0xE2,0x18,0xF4,0xDD,0x1E,0x08,0x4C,0xF6,0xD8,0x00,0x3E,0x7C, - 0x47,0x74,0xE8,0x33 -}; -static unsigned char dh512_g[] = -{ - 0x02 -}; - -static DH * -get_dh512() -{ - DH *dh = NULL; - - if ((dh = DH_new()) == NULL) - return(NULL); - dh->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL); - dh->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL); - if ((dh->p == NULL) || (dh->g == NULL)) - return(NULL); - return(dh); -} -# endif /* !NO_DH */ - -/* -** TLS_RAND_INIT -- initialize STARTTLS random generator -** -** Parameters: -** randfile -- name of file with random data -** logl -- loglevel -** -** Returns: -** success/failure -** -** Side Effects: -** initializes PRNG for tls library. -*/ - -#define MIN_RAND_BYTES 16 /* 128 bits */ - -bool -tls_rand_init(randfile, logl) - char *randfile; - int logl; -{ -# ifndef HASURANDOMDEV - /* not required if /dev/urandom exists, OpenSSL does it internally */ -#define RF_OK 0 /* randfile OK */ -#define RF_MISS 1 /* randfile == NULL || *randfile == '\0' */ -#define RF_UNKNOWN 2 /* unknown prefix for randfile */ - -#define RI_NONE 0 /* no init yet */ -#define RI_SUCCESS 1 /* init was successful */ -#define RI_FAIL 2 /* init failed */ - - bool ok; - int randdef; - static int done = RI_NONE; - - /* - ** initialize PRNG - */ - - /* did we try this before? if yes: return old value */ - if (done != RI_NONE) - return done == RI_SUCCESS; - - /* set default values */ - ok = FALSE; - done = RI_FAIL; - randdef = (randfile == NULL || *randfile == '\0') ? RF_MISS : RF_OK; -# if EGD - if (randdef == RF_OK && strncasecmp(randfile, "egd:", 4) == 0) - { - randfile += 4; - if (RAND_egd(randfile) < 0) - { - sm_syslog(LOG_WARNING, NOQID, - "TLS: RAND_egd(%s) failed: random number generator not seeded", - randfile); - } - else - ok = TRUE; - } - else -# endif /* EGD */ - if (randdef == RF_OK && strncasecmp(randfile, "file:", 5) == 0) - { - int fd; - long sff; - struct stat st; - - randfile += 5; - sff = SFF_SAFEDIRPATH | SFF_NOWLINK - | SFF_NOGWFILES | SFF_NOWWFILES - | SFF_NOGRFILES | SFF_NOWRFILES - | SFF_MUSTOWN | SFF_ROOTOK | SFF_OPENASROOT; - if ((fd = safeopen(randfile, O_RDONLY, 0, sff)) >= 0) - { - if (fstat(fd, &st) < 0) - { - if (LogLevel > logl) - sm_syslog(LOG_ERR, NOQID, - "TLS: can't fstat(%s)", - randfile); - } - else - { - bool use, problem; - - use = TRUE; - problem = FALSE; - if (st.st_mtime + 600 < curtime()) - { - use = bitnset(DBS_INSUFFICIENTENTROPY, - DontBlameSendmail); - problem = TRUE; - if (LogLevel > logl) - sm_syslog(LOG_ERR, NOQID, - "TLS: RandFile %s too old: %s", - randfile, - use ? "unsafe" : - "unusable"); - } - if (use && st.st_size < MIN_RAND_BYTES) - { - use = bitnset(DBS_INSUFFICIENTENTROPY, - DontBlameSendmail); - problem = TRUE; - if (LogLevel > logl) - sm_syslog(LOG_ERR, NOQID, - "TLS: size(%s) < %d: %s", - randfile, - MIN_RAND_BYTES, - use ? "unsafe" : - "unusable"); - } - if (use) - ok = RAND_load_file(randfile, -1) >= - MIN_RAND_BYTES; - if (use && !ok) - { - if (LogLevel > logl) - sm_syslog(LOG_WARNING, - NOQID, - "TLS: RAND_load_file(%s) failed: random number generator not seeded", - randfile); - } - if (problem) - ok = FALSE; - } - if (ok || bitnset(DBS_INSUFFICIENTENTROPY, - DontBlameSendmail)) - { - /* add this even if fstat() failed */ - RAND_seed((void *) &st, sizeof st); - } - (void) close(fd); - } - else - { - if (LogLevel > logl) - sm_syslog(LOG_WARNING, NOQID, - "TLS: Warning: safeopen(%s) failed", - randfile); - } - } - else if (randdef == RF_OK) - { - if (LogLevel > logl) - sm_syslog(LOG_WARNING, NOQID, - "TLS: Error: no proper random file definition %s", - randfile); - randdef = RF_UNKNOWN; - } - if (randdef == RF_MISS) - { - if (LogLevel > logl) - sm_syslog(LOG_WARNING, NOQID, - "TLS: Error: missing random file definition"); - } - if (!ok && bitnset(DBS_INSUFFICIENTENTROPY, DontBlameSendmail)) - { - int i; - long r; - unsigned char buf[MIN_RAND_BYTES]; - - /* assert((MIN_RAND_BYTES % sizeof(long)) == 0); */ - for (i = 0; i <= sizeof(buf) - sizeof(long); i += sizeof(long)) - { - r = get_random(); - (void) memcpy(buf + i, (void *) &r, sizeof(long)); - } - RAND_seed(buf, sizeof buf); - if (LogLevel > logl) - sm_syslog(LOG_WARNING, NOQID, - "TLS: Warning: random number generator not properly seeded"); - ok = TRUE; - } - done = ok ? RI_SUCCESS : RI_FAIL; - return ok; -# else /* !HASURANDOMDEV */ - return TRUE; -# endif /* !HASURANDOMDEV */ -} - -/* -** status in initialization -** these flags keep track of the status of the initialization -** i.e., whether a file exists (_EX) and whether it can be used (_OK) -** [due to permissions] -*/ -#define TLS_S_NONE 0x00000000 /* none yet */ -#define TLS_S_CERT_EX 0x00000001 /* CERT file exists */ -#define TLS_S_CERT_OK 0x00000002 /* CERT file is ok */ -#define TLS_S_KEY_EX 0x00000004 /* KEY file exists */ -#define TLS_S_KEY_OK 0x00000008 /* KEY file is ok */ -#define TLS_S_CERTP_EX 0x00000010 /* CA CERT PATH exists */ -#define TLS_S_CERTP_OK 0x00000020 /* CA CERT PATH is ok */ -#define TLS_S_CERTF_EX 0x00000040 /* CA CERT FILE exists */ -#define TLS_S_CERTF_OK 0x00000080 /* CA CERT FILE is ok */ - -# if _FFR_TLS_1 -#define TLS_S_CERT2_EX 0x00001000 /* 2nd CERT file exists */ -#define TLS_S_CERT2_OK 0x00002000 /* 2nd CERT file is ok */ -#define TLS_S_KEY2_EX 0x00004000 /* 2nd KEY file exists */ -#define TLS_S_KEY2_OK 0x00008000 /* 2nd KEY file is ok */ -# endif /* _FFR_TLS_1 */ - -#define TLS_S_DH_OK 0x00200000 /* DH cert is ok */ -#define TLS_S_DHPAR_EX 0x00400000 /* DH param file exists */ -#define TLS_S_DHPAR_OK 0x00800000 /* DH param file is ok to use */ - -/* -** TLS_OK_F -- can var be an absolute filename? -** -** Parameters: -** var -- filename -** fn -- what is the filename used for? -** -** Returns: -** ok? -*/ - -static bool -tls_ok_f(var, fn) - char *var; - char *fn; -{ - /* must be absolute pathname */ - if (var != NULL && *var == '/') - return TRUE; - if (LogLevel > 12) - sm_syslog(LOG_WARNING, NOQID, "TLS: file %s missing", fn); - return FALSE; -} - +#if STARTTLS /* -** TLS_SAFE_F -- is a file safe to use? -** -** Parameters: -** var -- filename -** sff -- flags for safefile() -** -** Returns: -** ok? -*/ - -static bool -tls_safe_f(var, sff) - char *var; - long sff; -{ - int ret; - - if ((ret = safefile(var, RunAsUid, RunAsGid, RunAsUserName, sff, - S_IRUSR, NULL)) == 0) - return TRUE; - if (LogLevel > 7) - sm_syslog(LOG_WARNING, NOQID, "TLS: file %s unsafe: %s", - var, errstring(ret)); - return FALSE; -} - -/* -** TLS_OK_F -- macro to simplify calls to tls_ok_f -** -** Parameters: -** var -- filename -** fn -- what is the filename used for? -** req -- is the file required? -** st -- status bit to set if ok -** -** Side Effects: -** uses r, ok; may change ok and status. -** -*/ - -#define TLS_OK_F(var, fn, req, st) if (ok) \ - { \ - r = tls_ok_f(var, fn); \ - if (r) \ - status |= st; \ - else if (req) \ - ok = FALSE; \ - } - -/* -** TLS_UNR -- macro to return whether a file should be unreadable -** -** Parameters: -** bit -- flag to test -** req -- flags -** -** Returns: -** 0/SFF_NORFILES -*/ -#define TLS_UNR(bit, req) (bitset(bit, req) ? SFF_NORFILES : 0) - -/* -** TLS_SAFE_F -- macro to simplify calls to tls_safe_f -** -** Parameters: -** var -- filename -** sff -- flags for safefile() -** req -- is the file required? -** ex -- does the file exist? -** st -- status bit to set if ok -** -** Side Effects: -** uses r, ok, ex; may change ok and status. -** -*/ - -#define TLS_SAFE_F(var, sff, req, ex, st) if (ex && ok) \ - { \ - r = tls_safe_f(var, sff); \ - if (r) \ - status |= st; \ - else if (req) \ - ok = FALSE; \ - } -/* -** INIT_TLS_LIBRARY -- calls functions which setup TLS library for global use +** INITSRVTLS -- initialize server side TLS ** ** Parameters: -** none. +** tls_ok -- should tls initialization be done? ** ** Returns: ** succeeded? ** ** Side Effects: -** Sets tls_ok_srv static, even when called from main() +** sets tls_ok_srv which is a static variable in this module. +** Do NOT remove assignments to it! */ bool -init_tls_library() +initsrvtls(tls_ok) + bool tls_ok; { - /* - ** basic TLS initialization - ** ignore result for now - */ - - SSL_library_init(); - SSL_load_error_strings(); -# if 0 - /* this is currently a macro for SSL_library_init */ - SSLeay_add_ssl_algorithms(); -# endif /* 0 */ - - /* initialize PRNG */ - tls_ok_srv = tls_rand_init(RandFile, 7); + if (!tls_ok) + return false; + /* do NOT remove assignment */ + tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, true, SrvCERTfile, + Srvkeyfile, CACERTpath, CACERTfile, DHParams); return tls_ok_srv; } +#endif /* STARTTLS */ /* -** INITTLS -- initialize TLS +** SRVFEATURES -- get features for SMTP server ** ** Parameters: -** ctx -- pointer to context -** req -- requirements for initialization (see sendmail.h) -** srv -- server side? -** certfile -- filename of certificate -** keyfile -- filename of private key -** cacertpath -- path to CAs -** cacertfile -- file with CA -** dhparam -- parameters for DH +** e -- envelope (should be session context). +** clientname -- name of client. +** features -- default features for this invocation. ** ** Returns: -** succeeded? +** server features. */ -bool -inittls(ctx, req, srv, certfile, keyfile, cacertpath, cacertfile, dhparam) - SSL_CTX **ctx; - u_long req; - bool srv; - char *certfile, *keyfile, *cacertpath, *cacertfile, *dhparam; +/* table with options: it uses just one character, how about strings? */ +static struct { -# if !NO_DH - static DH *dh = NULL; -# endif /* !NO_DH */ - int r; - bool ok; - long sff, status; - char *who; -# if _FFR_TLS_1 - char *cf2, *kf2; -# endif /* _FFR_TLS_1 */ - - status = TLS_S_NONE; - who = srv ? "srv" : "clt"; - if (ctx == NULL) - syserr("TLS: %s:inittls: ctx == NULL", who); - - /* already initialized? (we could re-init...) */ - if (*ctx != NULL) - return TRUE; - - /* PRNG seeded? */ - if (!tls_rand_init(RandFile, 10)) - return FALSE; - - /* let's start with the assumption it will work */ - ok = TRUE; - -# if _FFR_TLS_1 - /* - ** look for a second filename: it must be separated by a ',' - ** no blanks allowed (they won't be skipped). - ** we change a global variable here! this change will be undone - ** before return from the function but only if it returns TRUE. - ** this isn't a problem since in a failure case this function - ** won't be called again with the same (overwritten) values. - ** otherwise each return must be replaced with a goto endinittls. - */ - cf2 = NULL; - kf2 = NULL; - if (certfile != NULL && (cf2 = strchr(certfile, ',')) != NULL) - { - *cf2++ = '\0'; - if (keyfile != NULL && (kf2 = strchr(keyfile, ',')) != NULL) - *kf2++ = '\0'; - } -# endif /* _FFR_TLS_1 */ - - /* - ** what do we require from the client? - ** must it have CERTs? - ** introduce an option and decide based on that - */ - - TLS_OK_F(certfile, "CertFile", bitset(TLS_I_CERT_EX, req), - TLS_S_CERT_EX); - TLS_OK_F(keyfile, "KeyFile", bitset(TLS_I_KEY_EX, req), - TLS_S_KEY_EX); - TLS_OK_F(cacertpath, "CACERTPath", bitset(TLS_I_CERTP_EX, req), - TLS_S_CERTP_EX); - TLS_OK_F(cacertfile, "CACERTFile", bitset(TLS_I_CERTF_EX, req), - TLS_S_CERTF_EX); - -# if _FFR_TLS_1 - if (cf2 != NULL) - { - TLS_OK_F(cf2, "CertFile", bitset(TLS_I_CERT_EX, req), - TLS_S_CERT2_EX); - } - if (kf2 != NULL) - { - TLS_OK_F(kf2, "KeyFile", bitset(TLS_I_KEY_EX, req), - TLS_S_KEY2_EX); - } -# endif /* _FFR_TLS_1 */ - - /* - ** valid values for dhparam are (only the first char is checked) - ** none no parameters: don't use DH - ** 512 generate 512 bit parameters (fixed) - ** 1024 generate 1024 bit parameters - ** /file/name read parameters from /file/name - ** default is: 1024 for server, 512 for client (OK? XXX) - */ - if (bitset(TLS_I_TRY_DH, req)) - { - if (dhparam != NULL) - { - char c = *dhparam; - - if (c == '1') - req |= TLS_I_DH1024; - else if (c == '5') - req |= TLS_I_DH512; - else if (c != 'n' && c != 'N' && c != '/') - { - if (LogLevel > 12) - sm_syslog(LOG_WARNING, NOQID, - "TLS: error: illegal value '%s' for DHParam", - dhparam); - dhparam = NULL; - } - } - if (dhparam == NULL) - dhparam = srv ? "1" : "5"; - else if (*dhparam == '/') - { - TLS_OK_F(dhparam, "DHParameters", - bitset(TLS_I_DHPAR_EX, req), - TLS_S_DHPAR_EX); - } - } - if (!ok) - return ok; - - /* certfile etc. must be "safe". */ - sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK - | SFF_NOGWFILES | SFF_NOWWFILES - | SFF_MUSTOWN | SFF_ROOTOK | SFF_OPENASROOT; - if (DontLockReadFiles) - sff |= SFF_NOLOCK; - - TLS_SAFE_F(certfile, sff | TLS_UNR(TLS_I_CERT_UNR, req), - bitset(TLS_I_CERT_EX, req), - bitset(TLS_S_CERT_EX, status), TLS_S_CERT_OK); - TLS_SAFE_F(keyfile, sff | TLS_UNR(TLS_I_KEY_UNR, req), - bitset(TLS_I_KEY_EX, req), - bitset(TLS_S_KEY_EX, status), TLS_S_KEY_OK); - TLS_SAFE_F(cacertfile, sff | TLS_UNR(TLS_I_CERTF_UNR, req), - bitset(TLS_I_CERTF_EX, req), - bitset(TLS_S_CERTF_EX, status), TLS_S_CERTF_OK); - TLS_SAFE_F(dhparam, sff | TLS_UNR(TLS_I_DHPAR_UNR, req), - bitset(TLS_I_DHPAR_EX, req), - bitset(TLS_S_DHPAR_EX, status), TLS_S_DHPAR_OK); - if (!ok) - return ok; -# if _FFR_TLS_1 - if (cf2 != NULL) - { - TLS_SAFE_F(cf2, sff | TLS_UNR(TLS_I_CERT_UNR, req), - bitset(TLS_I_CERT_EX, req), - bitset(TLS_S_CERT2_EX, status), TLS_S_CERT2_OK); - } - if (kf2 != NULL) - { - TLS_SAFE_F(kf2, sff | TLS_UNR(TLS_I_KEY_UNR, req), - bitset(TLS_I_KEY_EX, req), - bitset(TLS_S_KEY2_EX, status), TLS_S_KEY2_OK); - } -# endif /* _FFR_TLS_1 */ - - /* create a method and a new context */ - if (srv) - { - if ((*ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) - { - if (LogLevel > 7) - sm_syslog(LOG_WARNING, NOQID, - "TLS: error: SSL_CTX_new(SSLv23_server_method()) failed"); - if (LogLevel > 9) - tlslogerr(); - return FALSE; - } - } - else - { - if ((*ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) - { - if (LogLevel > 7) - sm_syslog(LOG_WARNING, NOQID, - "TLS: error: SSL_CTX_new(SSLv23_client_method()) failed"); - if (LogLevel > 9) - tlslogerr(); - return FALSE; - } - } - -# if TLS_NO_RSA - /* turn off backward compatibility, required for no-rsa */ - SSL_CTX_set_options(*ctx, SSL_OP_NO_SSLv2); -# endif /* TLS_NO_RSA */ - + char srvf_opt; + unsigned int srvf_flag; +} srv_feat_table[] = +{ + { 'A', SRV_OFFER_AUTH }, + { 'B', SRV_OFFER_VERB }, + { 'D', SRV_OFFER_DSN }, + { 'E', SRV_OFFER_ETRN }, + { 'L', SRV_REQ_AUTH }, /* not documented in 8.12 */ +#if PIPELINING +# if _FFR_NO_PIPE + { 'N', SRV_NO_PIPE }, +# endif /* _FFR_NO_PIPE */ + { 'P', SRV_OFFER_PIPE }, +#endif /* PIPELINING */ + { 'R', SRV_VRFY_CLT }, + { 'S', SRV_OFFER_TLS }, +/* { 'T', SRV_TMP_FAIL }, */ + { 'V', SRV_VRFY_CLT }, + { 'X', SRV_OFFER_EXPN }, +/* { 'Y', SRV_OFFER_VRFY }, */ + { '\0', SRV_NONE } +}; -# if !TLS_NO_RSA - /* - ** Create a temporary RSA key - ** XXX Maybe we shouldn't create this always (even though it - ** is only at startup). - ** It is a time-consuming operation and it is not always necessary. - ** maybe we should do it only on demand... - */ - if (bitset(TLS_I_RSA_TMP, req) && - (rsa_tmp = RSA_generate_key(RSA_KEYLENGTH, RSA_F4, NULL, - NULL)) == NULL - ) - { - if (LogLevel > 7) - { - sm_syslog(LOG_WARNING, NOQID, - "TLS: error: %s: RSA_generate_key failed", - who); - if (LogLevel > 9) - tlslogerr(); - } - return FALSE; - } -# endif /* !TLS_NO_RSA */ +static unsigned int +srvfeatures(e, clientname, features) + ENVELOPE *e; + char *clientname; + unsigned int features; +{ + int r, i, j; + char **pvp, c, opt; + char pvpbuf[PSBUFSIZE]; + + pvp = NULL; + r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf, + sizeof(pvpbuf)); + if (r != EX_OK) + return features; + if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) + return features; + if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0) + return SRV_TMP_FAIL; /* - ** load private key - ** XXX change this for DSA-only version + ** General rule (see sendmail.h, d_flags): + ** lower case: required/offered, upper case: Not required/available + ** + ** Since we can change some features per daemon, we have both + ** cases here: turn on/off a feature. */ - if (bitset(TLS_S_KEY_OK, status) && - SSL_CTX_use_PrivateKey_file(*ctx, keyfile, - SSL_FILETYPE_PEM) <= 0) - { - if (LogLevel > 7) - { - sm_syslog(LOG_WARNING, NOQID, - "TLS: error: %s: SSL_CTX_use_PrivateKey_file(%s) failed", - who, keyfile); - if (LogLevel > 9) - tlslogerr(); - } - if (bitset(TLS_I_USE_KEY, req)) - return FALSE; - } - - /* get the certificate file */ - if (bitset(TLS_S_CERT_OK, status) && - SSL_CTX_use_certificate_file(*ctx, certfile, - SSL_FILETYPE_PEM) <= 0) - { - if (LogLevel > 7) - { - sm_syslog(LOG_WARNING, NOQID, - "TLS: error: %s: SSL_CTX_use_certificate_file(%s) failed", - who, certfile); - if (LogLevel > 9) - tlslogerr(); - } - if (bitset(TLS_I_USE_CERT, req)) - return FALSE; - } - - /* check the private key */ - if (bitset(TLS_S_KEY_OK, status) && - (r = SSL_CTX_check_private_key(*ctx)) <= 0) - { - /* Private key does not match the certificate public key */ - if (LogLevel > 5) - { - sm_syslog(LOG_WARNING, NOQID, - "TLS: error: %s: SSL_CTX_check_private_key failed(%s): %d", - who, keyfile, r); - if (LogLevel > 9) - tlslogerr(); - } - if (bitset(TLS_I_USE_KEY, req)) - return FALSE; - } - -# if _FFR_TLS_1 - /* XXX this code is pretty much duplicated from above! */ - - /* load private key */ - if (bitset(TLS_S_KEY2_OK, status) && - SSL_CTX_use_PrivateKey_file(*ctx, kf2, SSL_FILETYPE_PEM) <= 0) - { - if (LogLevel > 7) - { - sm_syslog(LOG_WARNING, NOQID, - "TLS: error: %s: SSL_CTX_use_PrivateKey_file(%s) failed", - who, kf2); - if (LogLevel > 9) - tlslogerr(); - } - } - - /* get the certificate file */ - if (bitset(TLS_S_CERT2_OK, status) && - SSL_CTX_use_certificate_file(*ctx, cf2, SSL_FILETYPE_PEM) <= 0) - { - if (LogLevel > 7) - { - sm_syslog(LOG_WARNING, NOQID, - "TLS: error: %s: SSL_CTX_use_certificate_file(%s) failed", - who, cf2); - if (LogLevel > 9) - tlslogerr(); - } - } - /* we should also check the private key: */ - if (bitset(TLS_S_KEY2_OK, status) && - (r = SSL_CTX_check_private_key(*ctx)) <= 0) + for (i = 1; pvp[i] != NULL; i++) { - /* Private key does not match the certificate public key */ - if (LogLevel > 5) + c = pvp[i][0]; + j = 0; + for (;;) { - sm_syslog(LOG_WARNING, NOQID, - "TLS: error: %s: SSL_CTX_check_private_key 2 failed: %d", - who, r); - if (LogLevel > 9) - tlslogerr(); - } - } -# endif /* _FFR_TLS_1 */ - - /* SSL_CTX_set_quiet_shutdown(*ctx, 1); violation of standard? */ - SSL_CTX_set_options(*ctx, SSL_OP_ALL); /* XXX bug compatibility? */ - -# if !NO_DH - /* Diffie-Hellman initialization */ - if (bitset(TLS_I_TRY_DH, req)) - { - if (bitset(TLS_S_DHPAR_OK, status)) - { - BIO *bio; - - if ((bio = BIO_new_file(dhparam, "r")) != NULL) - { - dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); - BIO_free(bio); - if (dh == NULL && LogLevel > 7) - { - u_long err; - - err = ERR_get_error(); - sm_syslog(LOG_WARNING, NOQID, - "TLS: error: %s: cannot read DH parameters(%s): %s", - who, dhparam, - ERR_error_string(err, NULL)); - if (LogLevel > 9) - tlslogerr(); - } - } - else - { - if (LogLevel > 5) - { - sm_syslog(LOG_WARNING, NOQID, - "TLS: error: %s: BIO_new_file(%s) failed", - who, dhparam); - if (LogLevel > 9) - tlslogerr(); - } - } - } - if (dh == NULL && bitset(TLS_I_DH1024, req)) - { - DSA *dsa; - - /* this takes a while! (7-130s on a 450MHz AMD K6-2) */ - dsa = DSA_generate_parameters(1024, NULL, 0, NULL, - NULL, 0, NULL); - dh = DSA_dup_DH(dsa); - DSA_free(dsa); - } - else - if (dh == NULL && bitset(TLS_I_DH512, req)) - dh = get_dh512(); - - if (dh == NULL) - { - if (LogLevel > 9) + if ((opt = srv_feat_table[j].srvf_opt) == '\0') { - u_long err; - - err = ERR_get_error(); - sm_syslog(LOG_WARNING, NOQID, - "TLS: error: %s: cannot read or set DH parameters(%s): %s", - who, dhparam, - ERR_error_string(err, NULL)); + if (LogLevel > 9) + sm_syslog(LOG_WARNING, e->e_id, + "srvfeatures: unknown feature %s", + pvp[i]); + break; } - if (bitset(TLS_I_REQ_DH, req)) - return FALSE; - } - else - { - SSL_CTX_set_tmp_dh(*ctx, dh); - - /* important to avoid small subgroup attacks */ - SSL_CTX_set_options(*ctx, SSL_OP_SINGLE_DH_USE); - if (LogLevel > 12) - sm_syslog(LOG_INFO, NOQID, - "TLS: %s: Diffie-Hellman init, key=%d bit (%c)", - who, 8 * DH_size(dh), *dhparam); - DH_free(dh); - } - } -# endif /* !NO_DH */ - - - /* XXX do we need this cache here? */ - if (bitset(TLS_I_CACHE, req)) - SSL_CTX_sess_set_cache_size(*ctx, 128); - /* timeout? SSL_CTX_set_timeout(*ctx, TimeOut...); */ - - /* load certificate locations and default CA paths */ - if (bitset(TLS_S_CERTP_EX, status) && bitset(TLS_S_CERTF_EX, status)) - { - if ((r = SSL_CTX_load_verify_locations(*ctx, cacertfile, - cacertpath)) == 1) - { -# if !TLS_NO_RSA - if (bitset(TLS_I_RSA_TMP, req)) - SSL_CTX_set_tmp_rsa_callback(*ctx, tmp_rsa_key); -# endif /* !TLS_NO_RSA */ - - /* ask to verify the peer */ - SSL_CTX_set_verify(*ctx, SSL_VERIFY_PEER, NULL); - - /* install verify callback */ - SSL_CTX_set_cert_verify_callback(*ctx, tls_verify_cb, - NULL); - SSL_CTX_set_client_CA_list(*ctx, - SSL_load_client_CA_file(cacertfile)); - } - else - { - /* - ** can't load CA data; do we care? - ** the data is necessary to authenticate the client, - ** which in turn would be necessary - ** if we want to allow relaying based on it. - */ - if (LogLevel > 5) + if (c == opt) { - sm_syslog(LOG_WARNING, NOQID, - "TLS: error: %s: %d load verify locs %s, %s", - who, r, cacertpath, cacertfile); - if (LogLevel > 9) - tlslogerr(); + features &= ~(srv_feat_table[j].srvf_flag); + break; } - if (bitset(TLS_I_VRFY_LOC, req)) - return FALSE; - } - } - - /* XXX: make this dependent on an option? */ - if (tTd(96, 9)) - SSL_CTX_set_info_callback(*ctx, apps_ssl_info_cb); - -# if _FFR_TLS_1 - /* - ** XXX install our own cipher list: option? - */ - if (CipherList != NULL && *CipherList != '\0') - { - if (SSL_CTX_set_cipher_list(*ctx, CipherList) <= 0) - { - if (LogLevel > 7) + if (c == tolower(opt)) { - sm_syslog(LOG_WARNING, NOQID, - "TLS: error: %s: SSL_CTX_set_cipher_list(%s) failed, list ignored", - who, CipherList); - - if (LogLevel > 9) - tlslogerr(); + features |= srv_feat_table[j].srvf_flag; + break; } - /* failure if setting to this list is required? */ + ++j; } } -# endif /* _FFR_TLS_1 */ - if (LogLevel > 12) - sm_syslog(LOG_INFO, NOQID, "TLS: init(%s)=%d", who, ok); - -# if _FFR_TLS_1 -# if 0 - /* - ** this label is required if we want to have a "clean" exit - ** see the comments above at the initialization of cf2 - */ - endinittls: -# endif /* 0 */ - - /* undo damage to global variables */ - if (cf2 != NULL) - *--cf2 = ','; - if (kf2 != NULL) - *--kf2 = ','; -# endif /* _FFR_TLS_1 */ - - return ok; -} -/* -** INITSRVTLS -- initialize server side TLS -** -** Parameters: -** none. -** -** Returns: -** succeeded? -** -** Side Effects: -** sets tls_ok_srv static, even when called from main() -*/ - -bool -initsrvtls() -{ - - tls_ok_srv = inittls(&srv_ctx, TLS_I_SRV, TRUE, SrvCERTfile, - Srvkeyfile, CACERTpath, CACERTfile, DHParams); - return tls_ok_srv; -} -/* -** TLS_GET_INFO -- get information about TLS connection -** -** Parameters: -** ssl -- SSL connection structure -** e -- current envelope -** srv -- server or client -** host -- hostname of other side -** log -- log connection information? -** -** Returns: -** result of authentication. -** -** Side Effects: -** sets ${cipher}, ${tls_version}, ${verify}, ${cipher_bits}, -** ${cert} -*/ - -int -tls_get_info(ssl, e, srv, host, log) - SSL *ssl; - ENVELOPE *e; - bool srv; - char *host; - bool log; -{ - SSL_CIPHER *c; - int b, r; - char *s; - char bitstr[16]; - X509 *cert; - - c = SSL_get_current_cipher(ssl); - define(macid("{cipher}", NULL), newstr(SSL_CIPHER_get_name(c)), e); - b = SSL_CIPHER_get_bits(c, &r); - (void) snprintf(bitstr, sizeof bitstr, "%d", b); - define(macid("{cipher_bits}", NULL), newstr(bitstr), e); -# if _FFR_TLS_1 - (void) snprintf(bitstr, sizeof bitstr, "%d", r); - define(macid("{alg_bits}", NULL), newstr(bitstr), e); -# endif /* _FFR_TLS_1 */ - s = SSL_CIPHER_get_version(c); - if (s == NULL) - s = "UNKNOWN"; - define(macid("{tls_version}", NULL), newstr(s), e); - - cert = SSL_get_peer_certificate(ssl); - if (log && LogLevel >= 14) - sm_syslog(LOG_INFO, e->e_id, - "TLS: get_verify in %s: %ld get_peer: 0x%lx", - srv ? "srv" : "clt", - SSL_get_verify_result(ssl), (u_long) cert); - if (cert != NULL) - { - char buf[MAXNAME]; - - X509_NAME_oneline(X509_get_subject_name(cert), - buf, sizeof buf); - define(macid("{cert_subject}", NULL), - newstr(xtextify(buf, "<>\")")), e); - X509_NAME_oneline(X509_get_issuer_name(cert), - buf, sizeof buf); - define(macid("{cert_issuer}", NULL), - newstr(xtextify(buf, "<>\")")), e); -# if _FFR_TLS_1 - X509_NAME_get_text_by_NID(X509_get_subject_name(cert), - NID_commonName, buf, sizeof buf); - define(macid("{cn_subject}", NULL), - newstr(xtextify(buf, "<>\")")), e); - X509_NAME_get_text_by_NID(X509_get_issuer_name(cert), - NID_commonName, buf, sizeof buf); - define(macid("{cn_issuer}", NULL), - newstr(xtextify(buf, "<>\")")), e); -# endif /* _FFR_TLS_1 */ - } - else - { - define(macid("{cert_subject}", NULL), "", e); - define(macid("{cert_issuer}", NULL), "", e); -# if _FFR_TLS_1 - define(macid("{cn_subject}", NULL), "", e); - define(macid("{cn_issuer}", NULL), "", e); -# endif /* _FFR_TLS_1 */ - } - switch(SSL_get_verify_result(ssl)) - { - case X509_V_OK: - if (cert != NULL) - { - s = "OK"; - r = TLS_AUTH_OK; - } - else - { - s = "NO"; - r = TLS_AUTH_NO; - } - break; - default: - s = "FAIL"; - r = TLS_AUTH_FAIL; - break; - } - define(macid("{verify}", NULL), newstr(s), e); - if (cert != NULL) - X509_free(cert); - - /* do some logging */ - if (log && LogLevel > 9) - { - char *vers, *s1, *s2, *bits; - - vers = macvalue(macid("{tls_version}", NULL), e); - bits = macvalue(macid("{cipher_bits}", NULL), e); - s1 = macvalue(macid("{verify}", NULL), e); - s2 = macvalue(macid("{cipher}", NULL), e); - sm_syslog(LOG_INFO, NOQID, - "TLS: connection %s %.64s, version=%.16s, verify=%.16s, cipher=%.64s, bits=%.6s", - srv ? "from" : "to", - host == NULL ? "none" : host, - vers == NULL ? "none" : vers, - s1 == NULL ? "none" : s1, - s2 == NULL ? "none" : s2, - bits == NULL ? "0" : bits); - if (LogLevel > 11) - { - /* - ** maybe run xuntextify on the strings? - ** that is easier to read but makes it maybe a bit - ** more complicated to figure out the right values - ** for the access map... - */ - s1 = macvalue(macid("{cert_subject}", NULL), e); - s2 = macvalue(macid("{cert_issuer}", NULL), e); - sm_syslog(LOG_INFO, NOQID, - "TLS: %s cert subject:%.128s, cert issuer=%.128s", - srv ? "client" : "server", - s1 == NULL ? "none" : s1, - s2 == NULL ? "none" : s2); - } - } - - return r; -} - -# if !TLS_NO_RSA -/* -** TMP_RSA_KEY -- return temporary RSA key -** -** Parameters: -** s -- SSL connection structure -** export -- -** keylength -- -** -** Returns: -** temporary RSA key. -*/ - -/* ARGUSED0 */ -static RSA * -tmp_rsa_key(s, export, keylength) - SSL *s; - int export; - int keylength; -{ - return rsa_tmp; -} -# endif /* !TLS_NO_RSA */ -/* -** APPS_SSL_INFO_CB -- info callback for TLS connections -** -** Parameters: -** s -- SSL connection structure -** where -- -** ret -- -** -** Returns: -** none. -*/ - -void -apps_ssl_info_cb(s, where, ret) - SSL *s; - int where; - int ret; -{ - char *str; - int w; - BIO *bio_err = NULL; - - if (LogLevel > 14) - sm_syslog(LOG_INFO, NOQID, - "info_callback where 0x%x ret %d", where, ret); - - w = where & ~SSL_ST_MASK; - if (bio_err == NULL) - bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); - - if (w & SSL_ST_CONNECT) - str = "SSL_connect"; - else if (w & SSL_ST_ACCEPT) - str = "SSL_accept"; - else - str = "undefined"; - - if (where & SSL_CB_LOOP) - { - if (LogLevel > 12) - sm_syslog(LOG_NOTICE, NOQID, - "%s:%s\n", str, SSL_state_string_long(s)); - } - else if (where & SSL_CB_ALERT) - { - str = (where & SSL_CB_READ) ? "read" : "write"; - if (LogLevel > 12) - sm_syslog(LOG_NOTICE, NOQID, - "SSL3 alert %s:%s:%s\n", - str, SSL_alert_type_string_long(ret), - SSL_alert_desc_string_long(ret)); - } - else if (where & SSL_CB_EXIT) - { - if (ret == 0) - { - if (LogLevel > 7) - sm_syslog(LOG_WARNING, NOQID, - "%s:failed in %s\n", - str, SSL_state_string_long(s)); - } - else if (ret < 0) - { - if (LogLevel > 7) - sm_syslog(LOG_WARNING, NOQID, - "%s:error in %s\n", - str, SSL_state_string_long(s)); - } - } -} -/* -** TLS_VERIFY_LOG -- log verify error for TLS certificates -** -** Parameters: -** ok -- verify ok? -** ctx -- x509 context -** -** Returns: -** 0 -- fatal error -** 1 -- ok -*/ - -static int -tls_verify_log(ok, ctx) - int ok; - X509_STORE_CTX *ctx; -{ - SSL *ssl; - X509 *cert; - int reason, depth; - char buf[512]; - - cert = X509_STORE_CTX_get_current_cert(ctx); - reason = X509_STORE_CTX_get_error(ctx); - depth = X509_STORE_CTX_get_error_depth(ctx); - ssl = (SSL *)X509_STORE_CTX_get_ex_data(ctx, - SSL_get_ex_data_X509_STORE_CTX_idx()); - - if (ssl == NULL) - { - /* internal error */ - sm_syslog(LOG_ERR, NOQID, - "TLS: internal error: tls_verify_cb: ssl == NULL"); - return 0; - } - - X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof buf); - sm_syslog(LOG_INFO, NOQID, - "TLS cert verify: depth=%d %s, state=%d, reason=%s\n", - depth, buf, ok, X509_verify_cert_error_string(reason)); - return 1; -} - -/* -** TLS_VERIFY_CB -- verify callback for TLS certificates -** -** Parameters: -** ctx -- x509 context -** -** Returns: -** accept connection? -** currently: always yes. -*/ - -static int -tls_verify_cb(ctx) - X509_STORE_CTX *ctx; -{ - int ok; - - ok = X509_verify_cert(ctx); - if (ok == 0) - { - if (LogLevel > 13) - return tls_verify_log(ok, ctx); - return 1; /* override it */ - } - return ok; -} - - -/* -** TLSLOGERR -- log the errors from the TLS error stack -** -** Parameters: -** none. -** -** Returns: -** none. -*/ - -void -tlslogerr() -{ - unsigned long l; - int line, flags; - unsigned long es; - char *file, *data; - char buf[256]; -#define CP (const char **) - - es = CRYPTO_thread_id(); - while ((l = ERR_get_error_line_data(CP &file, &line, CP &data, &flags)) - != 0) - { - sm_syslog(LOG_WARNING, NOQID, - "TLS: %lu:%s:%s:%d:%s\n", es, ERR_error_string(l, buf), - file, line, (flags & ERR_TXT_STRING) ? data : ""); - } + return features; } -# endif /* STARTTLS */ -#endif /* SMTP */ /* ** HELP -- implement the HELP command. ** ** Parameters: ** topic -- the topic we want help for. -** e -- envelope +** e -- envelope. ** ** Returns: ** none. @@ -4216,11 +3719,11 @@ help(topic, e) char *topic; ENVELOPE *e; { - register FILE *hf; + register SM_FILE_T *hf; register char *p; int len; bool noinfo; - bool first = TRUE; + bool first = true; long sff = SFF_OPENASROOT|SFF_REGONLY; char buf[MAXLINE]; char inp[MAXLINE]; @@ -4245,17 +3748,17 @@ help(topic, e) if (topic == NULL || *topic == '\0') { topic = "smtp"; - noinfo = FALSE; + noinfo = false; } else { makelower(topic); - noinfo = TRUE; + noinfo = true; } len = strlen(topic); - while (fgets(buf, sizeof buf, hf) != NULL) + while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) { if (buf[0] == '#') { @@ -4264,8 +3767,8 @@ help(topic, e) { int h; - if (sscanf(buf + strlen(HELPVSTR), "%d", - &h) == 1) + if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d", + &h) == 1) foundvers = h; } continue; @@ -4274,7 +3777,7 @@ help(topic, e) { if (first) { - first = FALSE; + first = false; /* print version if no/old vers# in file */ if (foundvers < 2 && !noinfo) @@ -4285,7 +3788,7 @@ help(topic, e) p = buf + strlen(buf) - 1; else p++; - fixcrlf(p, TRUE); + fixcrlf(p, true); if (foundvers >= 2) { translate_dollars(p); @@ -4293,7 +3796,7 @@ help(topic, e) p = inp; } message("214-2.0.0 %s", p); - noinfo = FALSE; + noinfo = false; } } @@ -4313,5 +3816,5 @@ help(topic, e) foundvers = 0; } - (void) fclose(hf); + (void) sm_io_close(hf, SM_TIME_DEFAULT); } diff --git a/gnu/usr.sbin/sendmail/sendmail/stab.c b/gnu/usr.sbin/sendmail/sendmail/stab.c index c207b61b40e..d83e1b40b64 100644 --- a/gnu/usr.sbin/sendmail/sendmail/stab.c +++ b/gnu/usr.sbin/sendmail/sendmail/stab.c @@ -11,12 +11,10 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: stab.c,v 8.40.16.7 2001/05/07 22:06:41 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +SM_RCSID("@(#)$Sendmail: stab.c,v 8.80 2001/09/04 22:43:06 ca Exp $") + /* ** STAB -- manage the symbol table ** @@ -24,8 +22,7 @@ static char id[] = "@(#)$Sendmail: stab.c,v 8.40.16.7 2001/05/07 22:06:41 gshapi ** name -- the name to be looked up or inserted. ** type -- the type of symbol. ** op -- what to do: -** ST_ENTER -- enter the name if not -** already present. +** ST_ENTER -- enter the name if not already present. ** ST_FIND -- find it only. ** ** Returns: @@ -37,6 +34,7 @@ static char id[] = "@(#)$Sendmail: stab.c,v 8.40.16.7 2001/05/07 22:06:41 gshapi */ #define STABSIZE 2003 +#define SM_LOWER(c) ((isascii(c) && isupper(c)) ? tolower(c) : (c)) static STAB *SymTab[STABSIZE]; @@ -53,7 +51,7 @@ stab(name, type, op) int len; if (tTd(36, 5)) - dprintf("STAB: %s %d ", name, type); + sm_dprintf("STAB: %s %d ", name, type); /* ** Compute the hashing function @@ -61,10 +59,10 @@ stab(name, type, op) hfunc = type; for (p = name; *p != '\0'; p++) - hfunc = ((hfunc << 1) ^ (lower(*p) & 0377)) % STABSIZE; + hfunc = ((hfunc << 1) ^ (SM_LOWER(*p) & 0377)) % STABSIZE; if (tTd(36, 9)) - dprintf("(hfunc=%d) ", hfunc); + sm_dprintf("(hfunc=%d) ", hfunc); ps = &SymTab[hfunc]; if (type == ST_MACRO || type == ST_RULESET) @@ -76,7 +74,7 @@ stab(name, type, op) else { while ((s = *ps) != NULL && - (s->s_type != type || strcasecmp(name, s->s_name))) + (s->s_type != type || sm_strcasecmp(name, s->s_name))) ps = &s->s_next; } @@ -89,12 +87,12 @@ stab(name, type, op) if (tTd(36, 5)) { if (s == NULL) - dprintf("not found\n"); + sm_dprintf("not found\n"); else { long *lp = (long *) s->s_class; - dprintf("type %d val %lx %lx %lx %lx\n", + sm_dprintf("type %d val %lx %lx %lx %lx\n", s->s_type, lp[0], lp[1], lp[2], lp[3]); } } @@ -106,7 +104,7 @@ stab(name, type, op) */ if (tTd(36, 5)) - dprintf("entered\n"); + sm_dprintf("entered\n"); /* determine size of new entry */ switch (type) @@ -159,21 +157,25 @@ stab(name, type, op) len = sizeof s->s_service; break; -#ifdef LDAPMAP +#if LDAPMAP case ST_LMAP: len = sizeof s->s_lmap; break; #endif /* LDAPMAP */ -#if _FFR_MILTER +#if MILTER case ST_MILTER: len = sizeof s->s_milter; break; -#endif /* _FFR_MILTER */ +#endif /* MILTER */ + + case ST_QUEUE: + len = sizeof s->s_quegrp; + break; default: /* - ** Each mailer has it's own MCI stab entry: + ** Each mailer has its own MCI stab entry: ** ** s = stab(host, ST_MCI + m->m_mno, ST_ENTER); ** @@ -192,12 +194,12 @@ stab(name, type, op) len += sizeof *s - sizeof s->s_value; if (tTd(36, 15)) - dprintf("size of stab entry: %d\n", len); + sm_dprintf("size of stab entry: %d\n", len); /* make new entry */ - s = (STAB *) xalloc(len); + s = (STAB *) sm_pmalloc_x(len); memset((char *) s, '\0', len); - s->s_name = newstr(name); + s->s_name = sm_pstrdup_x(name); s->s_type = type; s->s_len = len; @@ -214,8 +216,8 @@ stab(name, type, op) ** STABAPPLY -- apply function to all stab entries ** ** Parameters: -** func -- the function to apply. It will be given one -** parameter (the stab entry). +** func -- the function to apply. It will be given two +** parameters (the stab entry and the arg). ** arg -- an arbitrary argument, passed to func. ** ** Returns: @@ -235,7 +237,7 @@ stabapply(func, arg) for (s = *shead; s != NULL; s = s->s_next) { if (tTd(36, 90)) - dprintf("stabapply: trying %d/%s\n", + sm_dprintf("stabapply: trying %d/%s\n", s->s_type, s->s_name); func(s, arg); } @@ -259,7 +261,7 @@ stabapply(func, arg) void queueup_macros(class, qfp, e) int class; - FILE *qfp; + SM_FILE_T *qfp; ENVELOPE *e; { register STAB **shead; @@ -277,33 +279,15 @@ queueup_macros(class, qfp, e) char *p; if (s->s_type == ST_CLASS && - bitnset(class, s->s_class) && - (m = macid(s->s_name, NULL)) != '\0' && + bitnset(bitidx(class), s->s_class) && + (m = macid(s->s_name)) != '\0' && (p = macvalue(m, e)) != NULL) { - /* - ** HACK ALERT: Unfortunately, 8.10 and - ** 8.11 reused the ${if_addr} and - ** ${if_family} macros for both the incoming - ** interface address/family (getrequests()) - ** and the outgoing interface address/family - ** (makeconnection()). In order for D_BINDIF - ** to work properly, have to preserve the - ** incoming information in the queue file for - ** later delivery attempts. The original - ** information is stored in the envelope - ** in readqf() so it can be stored in - ** queueup_macros(). This should be fixed - ** in 8.12. - */ - - if (e->e_if_macros[EIF_ADDR] != NULL && - strcmp(s->s_name, "{if_addr}") == 0) - p = e->e_if_macros[EIF_ADDR]; - - fprintf(qfp, "$%s%s\n", - s->s_name, - denlstring(p, TRUE, FALSE)); + (void) sm_io_fprintf(qfp, SM_TIME_DEFAULT, + "$%s%s\n", + s->s_name, + denlstring(p, true, + false)); } } } @@ -339,3 +323,148 @@ copy_class(src, dst) } } } + +/* +** RMEXPSTAB -- remove expired entries from SymTab. +** +** These entries need to be removed in long-running processes, +** e.g., persistent queue runners, to avoid consuming memory. +** +** XXX It might be useful to restrict the maximum TTL to avoid +** caching data very long. +** +** Parameters: +** none. +** +** Returns: +** none. +** +** Side Effects: +** can remove entries from the symbol table. +*/ + +#define SM_STAB_FREE(x) \ + do \ + { \ + char *o = (x); \ + (x) = NULL; \ + if (o != NULL) \ + sm_free(o); \ + } while (0) + +void +rmexpstab() +{ + int i; + STAB *s, *p, *f; + time_t now; + + now = curtime(); + for (i = 0; i < STABSIZE; i++) + { + p = NULL; + s = SymTab[i]; + while (s != NULL) + { + switch (s->s_type) + { + case ST_HOSTSIG: + if (s->s_hostsig.hs_exp >= now) + goto next; /* not expired */ + SM_STAB_FREE(s->s_hostsig.hs_sig); /* XXX */ + break; + + case ST_NAMECANON: + if (s->s_namecanon.nc_exp >= now) + goto next; /* not expired */ + SM_STAB_FREE(s->s_namecanon.nc_cname); /* XXX */ + break; + + default: + if (s->s_type >= ST_MCI) + { + /* call mci_uncache? */ + SM_STAB_FREE(s->s_mci.mci_status); + SM_STAB_FREE(s->s_mci.mci_rstatus); + SM_STAB_FREE(s->s_mci.mci_heloname); +#if 0 + /* not dynamically allocated */ + SM_STAB_FREE(s->s_mci.mci_host); + SM_STAB_FREE(s->s_mci.mci_tolist); +#endif /* 0 */ +#if SASL + /* should always by NULL */ + SM_STAB_FREE(s->s_mci.mci_sasl_string); +#endif /* SASL */ + if (s->s_mci.mci_rpool != NULL) + { + sm_rpool_free(s->s_mci.mci_rpool); + s->s_mci.mci_macro.mac_rpool = NULL; + s->s_mci.mci_rpool = NULL; + } + break; + } + next: + p = s; + s = s->s_next; + continue; + } + + /* remove entry */ + SM_STAB_FREE(s->s_name); /* XXX */ + f = s; + s = s->s_next; + sm_free(f); /* XXX */ + if (p == NULL) + SymTab[i] = s; + else + p->s_next = s; + } + } +} + +#if SM_HEAP_CHECK +/* +** DUMPSTAB -- dump symbol table. +** +** For debugging. +*/ + +#define MAXSTTYPES (ST_MCI + 1) + +void +dumpstab() +{ + int i, t, total, types[MAXSTTYPES]; + STAB *s; + static int prevt[MAXSTTYPES], prev = 0; + + total = 0; + for (i = 0; i < MAXSTTYPES; i++) + types[i] = 0; + for (i = 0; i < STABSIZE; i++) + { + s = SymTab[i]; + while (s != NULL) + { + ++total; + t = s->s_type; + if (t > MAXSTTYPES - 1) + t = MAXSTTYPES - 1; + types[t]++; + s = s->s_next; + } + } + sm_syslog(LOG_INFO, NOQID, "stab: total=%d (%d)", total, total - prev); + prev = total; + for (i = 0; i < MAXSTTYPES; i++) + { + if (types[i] != 0) + { + sm_syslog(LOG_INFO, NOQID, "stab: type[%2d]=%2d (%d)", + i, types[i], types[i] - prevt[i]); + } + prevt[i] = types[i]; + } +} +#endif /* SM_HEAP_CHECK */ diff --git a/gnu/usr.sbin/sendmail/sendmail/stats.c b/gnu/usr.sbin/sendmail/sendmail/stats.c index 98ea1814da9..35a2fdacc39 100644 --- a/gnu/usr.sbin/sendmail/sendmail/stats.c +++ b/gnu/usr.sbin/sendmail/sendmail/stats.c @@ -11,17 +11,15 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: stats.c,v 8.36.14.5 2001/02/14 04:07:30 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> -#include <sendmail/mailstats.h> +SM_RCSID("@(#)$Sendmail: stats.c,v 8.47 2001/08/24 17:01:47 ca Exp $") + +#include <sendmail/mailstats.h> static struct statistics Stat; -static bool GotStats = FALSE; /* set when we have stats to merge */ +static bool GotStats = false; /* set when we have stats to merge */ /* See http://physics.nist.gov/cuu/Units/binary.html */ #define ONE_K 1000 /* one thousand (twenty-four?) */ @@ -76,7 +74,7 @@ markstats(e, to, reject) } - GotStats = TRUE; + GotStats = true; } /* ** CLEARSTATS -- clear statistics structure @@ -96,7 +94,7 @@ clearstats() { /* clear the structure to avoid future disappointment */ memset(&Stat, '\0', sizeof Stat); - GotStats = FALSE; + GotStats = false; } /* ** POSTSTATS -- post statistics in the statistics file @@ -115,13 +113,15 @@ void poststats(sfile) char *sfile; { - register int fd; + int fd; + static bool entered = false; long sff = SFF_REGONLY|SFF_OPENASROOT; struct statistics stats; extern off_t lseek(); - if (sfile == NULL || !GotStats) + if (sfile == NULL || *sfile == '\0' || !GotStats || entered) return; + entered = true; (void) time(&Stat.stat_itime); Stat.stat_size = sizeof Stat; @@ -138,8 +138,9 @@ poststats(sfile) { if (LogLevel > 12) sm_syslog(LOG_INFO, NOQID, "poststats: %s: %s", - sfile, errstring(errno)); + sfile, sm_errstring(errno)); errno = 0; + entered = false; return; } if (read(fd, (char *) &stats, sizeof stats) == sizeof stats && @@ -173,4 +174,5 @@ poststats(sfile) /* clear the structure to avoid future disappointment */ clearstats(); + entered = false; } diff --git a/gnu/usr.sbin/sendmail/sendmail/statusd_shm.h b/gnu/usr.sbin/sendmail/sendmail/statusd_shm.h index 5f61a447242..f9f57d99de4 100644 --- a/gnu/usr.sbin/sendmail/sendmail/statusd_shm.h +++ b/gnu/usr.sbin/sendmail/sendmail/statusd_shm.h @@ -1,22 +1,22 @@ /* - * Copyright (c) 1999 Sendmail, Inc. and its suppliers. + * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * - * $Sendmail: statusd_shm.h,v 8.4 1999/05/18 08:00:04 gshapiro Exp $ + * $Sendmail: statusd_shm.h,v 8.7 2000/09/17 17:30:06 gshapiro Exp $ * * Contributed by Exactis.com, Inc. * */ /* -** The shared memory part of statusd. +** The shared memory part of statusd. ** -** Attach to STATUSD_SHM_KEY and update the counter appropriate -** for your type of service. +** Attach to STATUSD_SHM_KEY and update the counter appropriate +** for your type of service. ** */ @@ -24,7 +24,8 @@ #define STATUSD_SHM_KEY (key_t)(13) #define STATUSD_LONGS (2) -typedef struct { +typedef struct +{ unsigned long magic; unsigned long ul[STATUSD_LONGS]; } STATUSD_SHM; diff --git a/gnu/usr.sbin/sendmail/sendmail/sysexits.c b/gnu/usr.sbin/sendmail/sendmail/sysexits.c index 9410e0cf12f..ceb6257f0bb 100644 --- a/gnu/usr.sbin/sendmail/sendmail/sysexits.c +++ b/gnu/usr.sbin/sendmail/sendmail/sysexits.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. + * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 @@ -11,58 +11,9 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: sysexits.c,v 8.25 1999/09/23 19:59:24 ca Exp $"; -#endif /* ! lint */ - #include <sendmail.h> -/* -** SYSEXITS.C -- error messages corresponding to sysexits.h -** -** If the first character of the string is a colon, interpolate -** the current errno after the rest of the string. -*/ - -char *SysExMsg[] = -{ - /* 64 USAGE */ " 500 5.0.0 Bad usage", - /* 65 DATAERR */ " 501 5.6.0 Data format error", - /* 66 NOINPUT */ ":550 5.3.0 Cannot open input", - /* 67 NOUSER */ " 550 5.1.1 User unknown", - /* 68 NOHOST */ " 550 5.1.2 Host unknown", - /* 69 UNAVAILABLE */ " 554 5.0.0 Service unavailable", - /* 70 SOFTWARE */ ":554 5.3.0 Internal error", - /* 71 OSERR */ ":451 4.0.0 Operating system error", - /* 72 OSFILE */ ":554 5.3.5 System file missing", - /* 73 CANTCREAT */ ":550 5.0.0 Can't create output", - /* 74 IOERR */ ":451 4.0.0 I/O error", - /* 75 TEMPFAIL */ " 450 4.0.0 Deferred", - /* 76 PROTOCOL */ " 554 5.5.0 Remote protocol error", - /* 77 NOPERM */ ":550 5.0.0 Insufficient permission", - /* 78 CONFIG */ " 554 5.3.5 Local configuration error", -}; - -int N_SysEx = sizeof(SysExMsg) / sizeof(SysExMsg[0]); - -static char *SysExitMsg[] = -{ - "command line usage error", - "data format error", - "cannot open input", - "addressee unknown", - "host name unknown", - "service unavailable", - "internal software error", - "system error (e.g., can't fork)", - "critical OS file missing", - "can't create (user) output file", - "input/output error", - "temp failure; user is invited to retry", - "remote error in protocol", - "permission denied", - "configuration error" -}; +SM_RCSID("@(#)$Sendmail: sysexits.c,v 8.30 2001/02/25 04:53:29 ca Exp $") /* ** DSNTOEXITSTAT -- convert DSN-style error code to EX_ style. @@ -181,7 +132,6 @@ dsntoexitstat(dsncode) } return EX_CONFIG; } - /* ** EXITSTAT -- convert EX_ value to error text. ** @@ -198,19 +148,15 @@ exitstat(excode) { char *c; int i; + char *exitmsg; if (excode == NULL || *excode == '\0') return excode; - i = 0; - for (c = excode; *c != '\0'; c++) - { - if (isascii(*c) && isdigit(*c)) - i = i * 10 + (*c - '0'); - else - return excode; - } - i -= EX__BASE; - if (i >= 0 && i <= N_SysEx) - return SysExitMsg[i]; + i = (int) strtol(excode, &c, 10); + if (*c != '\0') + return excode; + exitmsg = sm_sysexitmsg(i); + if (exitmsg != NULL) + return exitmsg; return excode; } diff --git a/gnu/usr.sbin/sendmail/sendmail/sysexits.h b/gnu/usr.sbin/sendmail/sendmail/sysexits.h index 6f17dc39c9e..9bfef7aae2d 100644 --- a/gnu/usr.sbin/sendmail/sendmail/sysexits.h +++ b/gnu/usr.sbin/sendmail/sendmail/sysexits.h @@ -6,69 +6,69 @@ * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * - * $Sendmail: sysexits.h,v 8.3.28.2 2000/04/24 23:15:16 ca Exp $ + * $Sendmail: sysexits.h,v 8.5 2000/11/26 02:13:20 ca Exp $ * @(#)sysexits.h 8.1 (Berkeley) 6/2/93 */ #ifndef _SYSEXITS_H_ -# define _SYSEXITS_H_ +# define _SYSEXITS_H_ /* - * SYSEXITS.H -- Exit status codes for system programs. - * - * This include file attempts to categorize possible error - * exit statuses for system programs, notably delivermail - * and the Berkeley network. - * - * Error numbers begin at EX__BASE to reduce the possibility of - * clashing with other exit statuses that random programs may - * already return. The meaning of the codes is approximately - * as follows: - * - * EX_USAGE -- The command was used incorrectly, e.g., with - * the wrong number of arguments, a bad flag, a bad - * syntax in a parameter, or whatever. - * EX_DATAERR -- The input data was incorrect in some way. - * This should only be used for user's data & not - * system files. - * EX_NOINPUT -- An input file (not a system file) did not - * exist or was not readable. This could also include - * errors like "No message" to a mailer (if it cared - * to catch it). - * EX_NOUSER -- The user specified did not exist. This might - * be used for mail addresses or remote logins. - * EX_NOHOST -- The host specified did not exist. This is used - * in mail addresses or network requests. - * EX_UNAVAILABLE -- A service is unavailable. This can occur - * if a support program or file does not exist. This - * can also be used as a catchall message when something - * you wanted to do doesn't work, but you don't know - * why. - * EX_SOFTWARE -- An internal software error has been detected. - * This should be limited to non-operating system related - * errors as possible. - * EX_OSERR -- An operating system error has been detected. - * This is intended to be used for such things as "cannot - * fork", "cannot create pipe", or the like. It includes - * things like getuid returning a user that does not - * exist in the passwd file. - * EX_OSFILE -- Some system file (e.g., /etc/passwd, /etc/utmp, - * etc.) does not exist, cannot be opened, or has some - * sort of error (e.g., syntax error). - * EX_CANTCREAT -- A (user specified) output file cannot be - * created. - * EX_IOERR -- An error occurred while doing I/O on some file. - * EX_TEMPFAIL -- temporary failure, indicating something that - * is not really an error. In sendmail, this means - * that a mailer (e.g.) could not create a connection, - * and the request should be reattempted later. - * EX_PROTOCOL -- the remote system returned something that - * was "not possible" during a protocol exchange. - * EX_NOPERM -- You did not have sufficient permission to - * perform the operation. This is not intended for - * file system problems, which should use NOINPUT or - * CANTCREAT, but rather for higher level permissions. - */ +** SYSEXITS.H -- Exit status codes for system programs. +** +** This include file attempts to categorize possible error +** exit statuses for system programs, notably delivermail +** and the Berkeley network. +** +** Error numbers begin at EX__BASE to reduce the possibility of +** clashing with other exit statuses that random programs may +** already return. The meaning of the codes is approximately +** as follows: +** +** EX_USAGE -- The command was used incorrectly, e.g., with +** the wrong number of arguments, a bad flag, a bad +** syntax in a parameter, or whatever. +** EX_DATAERR -- The input data was incorrect in some way. +** This should only be used for user's data & not +** system files. +** EX_NOINPUT -- An input file (not a system file) did not +** exist or was not readable. This could also include +** errors like "No message" to a mailer (if it cared +** to catch it). +** EX_NOUSER -- The user specified did not exist. This might +** be used for mail addresses or remote logins. +** EX_NOHOST -- The host specified did not exist. This is used +** in mail addresses or network requests. +** EX_UNAVAILABLE -- A service is unavailable. This can occur +** if a support program or file does not exist. This +** can also be used as a catchall message when something +** you wanted to do doesn't work, but you don't know +** why. +** EX_SOFTWARE -- An internal software error has been detected. +** This should be limited to non-operating system related +** errors as possible. +** EX_OSERR -- An operating system error has been detected. +** This is intended to be used for such things as "cannot +** fork", "cannot create pipe", or the like. It includes +** things like getuid returning a user that does not +** exist in the passwd file. +** EX_OSFILE -- Some system file (e.g., /etc/passwd, /etc/utmp, +** etc.) does not exist, cannot be opened, or has some +** sort of error (e.g., syntax error). +** EX_CANTCREAT -- A (user specified) output file cannot be +** created. +** EX_IOERR -- An error occurred while doing I/O on some file. +** EX_TEMPFAIL -- temporary failure, indicating something that +** is not really an error. In sendmail, this means +** that a mailer (e.g.) could not create a connection, +** and the request should be reattempted later. +** EX_PROTOCOL -- the remote system returned something that +** was "not possible" during a protocol exchange. +** EX_NOPERM -- You did not have sufficient permission to +** perform the operation. This is not intended for +** file system problems, which should use NOINPUT or +** CANTCREAT, but rather for higher level permissions. +*/ # define EX_OK 0 /* successful termination */ diff --git a/gnu/usr.sbin/sendmail/sendmail/timers.c b/gnu/usr.sbin/sendmail/sendmail/timers.c index c3523138793..002b7596aac 100644 --- a/gnu/usr.sbin/sendmail/sendmail/timers.c +++ b/gnu/usr.sbin/sendmail/sendmail/timers.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers. + * Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set @@ -10,9 +10,8 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: timers.c,v 8.13.16.1 2000/10/09 01:06:45 gshapiro Exp $"; -#endif /* ! lint */ +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: timers.c,v 8.22 2001/05/10 01:16:11 ca Exp $") #if _FFR_TIMERS # include <sys/types.h> @@ -34,17 +33,17 @@ warntimer(msg, va_alist) # endif /* __STDC__ */ { char buf[MAXLINE]; - VA_LOCAL_DECL + SM_VA_LOCAL_DECL # if 0 if (!tTd(98, 30)) return; # endif /* 0 */ - VA_START(msg); - vsnprintf(buf, sizeof buf, msg, ap); - VA_END; + SM_VA_START(ap, msg); + (void) sm_vsnprintf(buf, sizeof buf, msg, ap); + SM_VA_END(ap); sm_syslog(LOG_NOTICE, CurEnv->e_id, "%s; e_timers=0x%lx", - buf, (u_long) &CurEnv->e_timers); + buf, (unsigned long) &CurEnv->e_timers); } static void @@ -169,7 +168,7 @@ pushtimer(ptimer) if (TimerStack[i] == ptimer) { warntimer("Timer@0x%lx already on stack, index=%d, NTimers=%d", - (u_long) ptimer, i, NTimers); + (unsigned long) ptimer, i, NTimers); errno = save_errno; return; } @@ -211,7 +210,7 @@ poptimer(ptimer) if (i != NTimers - 1) warntimer("poptimer: odd pop (timer=0x%lx, index=%d, NTimers=%d)", - (u_long) ptimer, i, NTimers); + (unsigned long) ptimer, i, NTimers); NTimers = i; /* clean up and return */ @@ -224,7 +223,7 @@ strtimer(ptimer) { static char buf[40]; - snprintf(buf, sizeof buf, "%ld.%06ldr/%ld.%06ldc", + (void) sm_snprintf(buf, sizeof buf, "%ld.%06ldr/%ld.%06ldc", ptimer->ti_wall_sec, ptimer->ti_wall_usec, ptimer->ti_cpu_sec, ptimer->ti_cpu_usec); return buf; diff --git a/gnu/usr.sbin/sendmail/sendmail/timers.h b/gnu/usr.sbin/sendmail/sendmail/timers.h index 16f948814a0..7952eff3725 100644 --- a/gnu/usr.sbin/sendmail/sendmail/timers.h +++ b/gnu/usr.sbin/sendmail/sendmail/timers.h @@ -1,12 +1,12 @@ /* - * Copyright (c) 1999 Sendmail, Inc. and its suppliers. + * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * - * $Sendmail: timers.h,v 8.4 1999/11/04 19:31:26 ca Exp $ + * $Sendmail: timers.h,v 8.6 2001/04/03 01:53:18 gshapiro Exp $ * * Contributed by Exactis.com, Inc. * @@ -30,4 +30,4 @@ TIMER extern void pushtimer __P((TIMER *)); extern void poptimer __P((TIMER *)); extern char *strtimer __P((TIMER *)); -#endif /* TIMERS_H */ +#endif /* ! TIMERS_H */ diff --git a/gnu/usr.sbin/sendmail/sendmail/trace.c b/gnu/usr.sbin/sendmail/sendmail/trace.c index 2d9d4e414bf..f0c0795302d 100644 --- a/gnu/usr.sbin/sendmail/sendmail/trace.c +++ b/gnu/usr.sbin/sendmail/sendmail/trace.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. + * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 @@ -11,11 +11,14 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: trace.c,v 8.20.22.4 2001/08/15 13:05:43 ca Exp $"; -#endif /* ! lint */ - #include <sendmail.h> +#include <sm/debug.h> +#include <sm/string.h> + +SM_RCSID("@(#)$Sendmail: trace.c,v 8.35 2001/08/17 16:02:27 ca Exp $") + +static char *tTnewflag __P((char *)); +static char *tToldflag __P((char *)); /* ** TtSETUP -- set up for trace package. @@ -32,93 +35,190 @@ static char id[] = "@(#)$Sendmail: trace.c,v 8.20.22.4 2001/08/15 13:05:43 ca Ex ** environment is set up. */ -static u_char *tTvect; -static int tTsize; +static unsigned char *tTvect; +static unsigned int tTsize; static char *DefFlags; void tTsetup(vect, size, defflags) - u_char *vect; - int size; + unsigned char *vect; + unsigned int size; char *defflags; { tTvect = vect; tTsize = size; DefFlags = defflags; } -/* -** TtFLAG -- process an external trace flag description. + +/* +** tToldflag -- process an old style trace flag ** ** Parameters: -** s -- the trace flag. +** s -- points to a [\0, \t] terminated string, +** and the initial character is a digit. ** ** Returns: -** none. +** pointer to terminating [\0, \t] character ** ** Side Effects: -** sets/clears trace flags. +** modifies tTvect */ -void -tTflag(s) +static char * +tToldflag(s) register char *s; { unsigned int first, last; register unsigned int i; - if (*s == '\0') - s = DefFlags; + /* find first flag to set */ + i = 0; + while (isascii(*s) && isdigit(*s) && i < tTsize) + i = i * 10 + (*s++ - '0'); - for (;;) + /* + ** skip over rest of a too large number + ** Maybe we should complain if out-of-bounds values are used. + */ + + while (isascii(*s) && isdigit(*s) && i >= tTsize) + s++; + first = i; + + /* find last flag to set */ + if (*s == '-') { - /* find first flag to set */ i = 0; - while (isascii(*s) && isdigit(*s) && i < tTsize) - i = i * 10 + (*s++ - '0'); - - /* - ** skip over rest of a too large number - ** Maybe we should complain if out-of-bounds values are used. - */ + while (isascii(*++s) && isdigit(*s) && i < tTsize) + i = i * 10 + (*s - '0'); + /* skip over rest of a too large number */ while (isascii(*s) && isdigit(*s) && i >= tTsize) s++; - first = i; + } + last = i; - /* find last flag to set */ - if (*s == '-') - { - i = 0; - while (isascii(*++s) && isdigit(*s) && i < tTsize) - i = i * 10 + (*s - '0'); + /* find the level to set it to */ + i = 1; + if (*s == '.') + { + i = 0; + while (isascii(*++s) && isdigit(*s)) + i = i * 10 + (*s - '0'); + } - /* skip over rest of a too large number */ - while (isascii(*s) && isdigit(*s) && i >= tTsize) - s++; - } - last = i; + /* clean up args */ + if (first >= tTsize) + first = tTsize - 1; + if (last >= tTsize) + last = tTsize - 1; + + /* set the flags */ + while (first <= last) + tTvect[first++] = (unsigned char) i; + + /* skip trailing junk */ + while (*s != '\0' && *s != ',' && *s != ' ' && *s != '\t') + ++s; + + return s; +} + +/* +** tTnewflag -- process a new style trace flag +** +** Parameters: +** s -- Points to a non-empty [\0, \t] terminated string, +** of which the initial character is not a digit. +** +** Returns: +** pointer to terminating [\0, \t] character +** +** Side Effects: +** adds trace flag to libsm debug database +*/ - /* find the level to set it to */ - i = 1; - if (*s == '.') +static char * +tTnewflag(s) + register char *s; +{ + char *pat, *endpat; + int level; + + pat = s; + while (*s != '\0' && *s != ',' && *s != ' ' && *s != '\t' && *s != '.') + ++s; + endpat = s; + if (*s == '.') + { + ++s; + level = 0; + while (isascii(*s) && isdigit(*s)) { - i = 0; - while (isascii(*++s) && isdigit(*s)) - i = i * 10 + (*s - '0'); + level = level * 10 + (*s - '0'); + ++s; } + if (level < 0) + level = 0; + } + else + { + level = 1; + } + + sm_debug_addsetting_x(sm_strndup_x(pat, endpat - pat), level); - /* clean up args */ - if (first >= tTsize) - first = tTsize - 1; - if (last >= tTsize) - last = tTsize - 1; + /* skip trailing junk */ + while (*s != '\0' && *s != ',' && *s != ' ' && *s != '\t') + ++s; - /* set the flags */ - while (first <= last) - tTvect[first++] = i; + return s; +} - /* more arguments? */ - if (*s++ == '\0') +/* +** TtFLAG -- process an external trace flag list. +** +** Parameters: +** s -- the trace flag. +** +** The syntax of a trace flag list is as follows: +** +** <flags> ::= <flag> | <flags> "," <flag> +** <flag> ::= <categories> | <categories> "." <level> +** <categories> ::= <int> | <int> "-" <int> | <pattern> +** <pattern> ::= <an sh glob pattern matching a C identifier> +** +** White space is ignored before and after a flag. +** However, note that we skip over anything we don't +** understand, rather than report an error. +** +** Returns: +** none. +** +** Side Effects: +** sets/clears old-style trace flags. +** registers new-style trace flags with the libsm debug package. +*/ + +void +tTflag(s) + register char *s; +{ + if (*s == '\0') + s = DefFlags; + + for (;;) + { + if (*s == '\0') return; + if (*s == ',' || *s == ' ' || *s == '\t') + { + ++s; + continue; + } + if (isascii(*s) && isdigit(*s)) + s = tToldflag(s); + else + s = tTnewflag(s); } } diff --git a/gnu/usr.sbin/sendmail/sendmail/udb.c b/gnu/usr.sbin/sendmail/sendmail/udb.c index 754a41a8a94..cc568a68feb 100644 --- a/gnu/usr.sbin/sendmail/sendmail/udb.c +++ b/gnu/usr.sbin/sendmail/sendmail/udb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-1999, 2001 Sendmail, Inc. and its suppliers. + * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 @@ -13,17 +13,15 @@ #include <sendmail.h> -#ifndef lint -# if USERDB -static char id[] = "@(#)$Sendmail: udb.c,v 8.111.16.2 2001/05/03 17:24:17 gshapiro Exp $ (with USERDB)"; -# else /* USERDB */ -static char id[] = "@(#)$Sendmail: udb.c,v 8.111.16.2 2001/05/03 17:24:17 gshapiro Exp $ (without USERDB)"; -# endif /* USERDB */ -#endif /* ! lint */ +#if USERDB +SM_RCSID("@(#)$Sendmail: udb.c,v 8.150 2001/09/04 22:54:42 ca Exp $ (with USERDB)") +#else /* USERDB */ +SM_RCSID("@(#)$Sendmail: udb.c,v 8.150 2001/09/04 22:54:42 ca Exp $ (without USERDB)") +#endif /* USERDB */ #if USERDB -# ifdef NEWDB +# if NEWDB # include <db.h> # ifndef DB_VERSION_MAJOR # define DB_VERSION_MAJOR 1 @@ -70,7 +68,7 @@ struct udbent } udb_forward; # define udb_fwdhost udb_u.udb_forward._udb_fwdhost -# ifdef NEWDB +# if NEWDB /* type UE_FETCH -- lookup in local database */ struct { @@ -99,10 +97,10 @@ struct udb_option char *udbo_val; }; -# ifdef HESIOD +# if HESIOD static int hes_udb_get __P((DBT *, DBT *)); # endif /* HESIOD */ -static char *udbmatch __P((char *, char *)); +static char *udbmatch __P((char *, char *, SM_RPOOL_T *)); static int _udbx_init __P((ENVELOPE *)); static int _udb_parsespec __P((char *, struct udb_option [], int)); @@ -125,7 +123,7 @@ static int _udb_parsespec __P((char *, struct udb_option [], int)); */ static struct udbent UdbEnts[MAXUDBENT + 1]; -static bool UdbInitialized = FALSE; +static bool UdbInitialized = false; int udbexpand(a, sendq, aliaslevel, e) @@ -148,7 +146,7 @@ udbexpand(a, sendq, aliaslevel, e) memset(&info, '\0', sizeof info); if (tTd(28, 1)) - dprintf("udbexpand(%s)\n", a->q_paddr); + sm_dprintf("udbexpand(%s)\n", a->q_paddr); /* make certain we are supposed to send to this address */ if (!QS_IS_SENDABLE(a->q_state)) @@ -174,20 +172,19 @@ udbexpand(a, sendq, aliaslevel, e) if (user[0] == '\\') return EX_OK; - /* if name is too long, assume it won't match */ - if (strlen(user) > (SIZE_T) sizeof keybuf - 12) - return EX_OK; - /* if name begins with a colon, it indicates our metadata */ if (user[0] == ':') return EX_OK; + keylen = sm_strlcpyn(keybuf, sizeof keybuf, 2, user, ":maildrop"); + + /* if name is too long, assume it won't match */ + if (keylen > sizeof keybuf) + return EX_OK; + /* build actual database key */ - (void) strlcpy(keybuf, user, sizeof keybuf); - (void) strlcat(keybuf, ":maildrop", sizeof keybuf); - keylen = strlen(keybuf); - breakout = FALSE; + breakout = false; for (up = UdbEnts; !breakout; up++) { int usersize; @@ -215,12 +212,12 @@ udbexpand(a, sendq, aliaslevel, e) switch (up->udb_type) { -# ifdef NEWDB +# if NEWDB case UDB_DBFETCH: key.data = keybuf; key.size = keylen; if (tTd(28, 80)) - dprintf("udbexpand: trying %s (%d) via db\n", + sm_dprintf("udbexpand: trying %s (%d) via db\n", keybuf, keylen); # if DB_VERSION_MAJOR < 2 i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); @@ -243,7 +240,7 @@ udbexpand(a, sendq, aliaslevel, e) if (i > 0 || info.size <= 0) { if (tTd(28, 2)) - dprintf("udbexpand: no match on %s (%d)\n", + sm_dprintf("udbexpand: no match on %s (%d)\n", keybuf, keylen); # if DB_VERSION_MAJOR > 1 if (dbc != NULL) @@ -255,7 +252,7 @@ udbexpand(a, sendq, aliaslevel, e) break; } if (tTd(28, 80)) - dprintf("udbexpand: match %.*s: %.*s\n", + sm_dprintf("udbexpand: match %.*s: %.*s\n", (int) key.size, (char *) key.data, (int) info.size, (char *) info.data); @@ -278,7 +275,7 @@ udbexpand(a, sendq, aliaslevel, e) return EX_OK; } - breakout = TRUE; + breakout = true; if (info.size >= userleft - 1) { char *nuser; @@ -286,11 +283,11 @@ udbexpand(a, sendq, aliaslevel, e) if (info.size > MEMCHUNKSIZE) size = info.size; - nuser = xalloc(usersize + size); + nuser = sm_malloc_x(usersize + size); memmove(nuser, user, usersize); if (user != userbuf) - sm_free(user); + sm_free(user); /* XXX */ user = nuser; usersize += size; userleft += size; @@ -339,8 +336,8 @@ udbexpand(a, sendq, aliaslevel, e) { if (tTd(28, 5)) { - dprintf("udbexpand: QS_EXPANDED "); - printaddr(a, FALSE); + sm_dprintf("udbexpand: QS_EXPANDED "); + printaddr(a, false); } a->q_state = QS_EXPANDED; } @@ -358,8 +355,8 @@ udbexpand(a, sendq, aliaslevel, e) memset(&key, '\0', sizeof key); memset(&info, '\0', sizeof info); - (void) strlcpy(keybuf, a->q_user, sizeof keybuf); - (void) strlcat(keybuf, ":mailsender", sizeof keybuf); + (void) sm_strlcpyn(keybuf, sizeof keybuf, 2, a->q_user, + ":mailsender"); keylen = strlen(keybuf); key.data = keybuf; key.size = keylen; @@ -372,28 +369,29 @@ udbexpand(a, sendq, aliaslevel, e) # endif /* DB_VERSION_MAJOR < 2 */ if (i != 0 || info.size <= 0) break; - a->q_owner = xalloc(info.size + 1); + a->q_owner = sm_rpool_malloc_x(e->e_rpool, + info.size + 1); memmove(a->q_owner, info.data, info.size); a->q_owner[info.size] = '\0'; /* announce delivery; NORECEIPT bit set later */ if (e->e_xfp != NULL) { - fprintf(e->e_xfp, - "Message delivered to mailing list %s\n", - a->q_paddr); + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + "Message delivered to mailing list %s\n", + a->q_paddr); } e->e_flags |= EF_SENDRECEIPT; a->q_flags |= QDELIVERED|QEXPANDED; break; # endif /* NEWDB */ -# ifdef HESIOD +# if HESIOD case UDB_HESIOD: key.data = keybuf; key.size = keylen; if (tTd(28, 80)) - dprintf("udbexpand: trying %s (%d) via hesiod\n", + sm_dprintf("udbexpand: trying %s (%d) via hesiod\n", keybuf, keylen); /* look up the key via hesiod */ i = hes_udb_get(&key, &info); @@ -410,11 +408,11 @@ udbexpand(a, sendq, aliaslevel, e) # endif /* HES_GETMAILHOST */ if (tTd(28, 2)) - dprintf("udbexpand: no match on %s (%d)\n", + sm_dprintf("udbexpand: no match on %s (%d)\n", (char *) keybuf, (int) keylen); # if HES_GETMAILHOST if (tTd(28, 8)) - dprintf(" ... trying hes_getmailhost(%s)\n", + sm_dprintf(" ... trying hes_getmailhost(%s)\n", a->q_user); hp = hes_getmailhost(a->q_user); if (hp == NULL) @@ -426,7 +424,7 @@ udbexpand(a, sendq, aliaslevel, e) return EX_TEMPFAIL; } if (tTd(28, 2)) - dprintf("hes_getmailhost(%s): %d\n", + sm_dprintf("hes_getmailhost(%s): %d\n", a->q_user, hes_error()); break; } @@ -434,22 +432,22 @@ udbexpand(a, sendq, aliaslevel, e) sizeof pobuf - 2) { if (tTd(28, 2)) - dprintf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n", + sm_dprintf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n", a->q_user, hp->po_name, hp->po_host); break; } info.data = pobuf; - snprintf(pobuf, sizeof pobuf, "%s@%s", - hp->po_name, hp->po_host); + (void) sm_snprintf(pobuf, sizeof pobuf, + "%s@%s", hp->po_name, hp->po_host); info.size = strlen(info.data); # else /* HES_GETMAILHOST */ break; # endif /* HES_GETMAILHOST */ } if (tTd(28, 80)) - dprintf("udbexpand: match %.*s: %.*s\n", + sm_dprintf("udbexpand: match %.*s: %.*s\n", (int) key.size, (char *) key.data, (int) info.size, (char *) info.data); a->q_flags &= ~QSELFREF; @@ -460,9 +458,9 @@ udbexpand(a, sendq, aliaslevel, e) return EX_OK; } - breakout = TRUE; + breakout = true; if (info.size >= usersize) - user = xalloc(info.size + 1); + user = sm_malloc_x(info.size + 1); memmove(user, info.data, info.size); user[info.size] = '\0'; @@ -478,8 +476,8 @@ udbexpand(a, sendq, aliaslevel, e) { if (tTd(28, 5)) { - dprintf("udbexpand: QS_EXPANDED "); - printaddr(a, FALSE); + sm_dprintf("udbexpand: QS_EXPANDED "); + printaddr(a, false); } a->q_state = QS_EXPANDED; } @@ -489,15 +487,16 @@ udbexpand(a, sendq, aliaslevel, e) ** it into the envelope. */ - (void) strlcpy(keybuf, a->q_user, sizeof keybuf); - (void) strlcat(keybuf, ":mailsender", sizeof keybuf); + (void) sm_strlcpyn(keybuf, sizeof keybuf, 2, a->q_user, + ":mailsender"); keylen = strlen(keybuf); key.data = keybuf; key.size = keylen; i = hes_udb_get(&key, &info); if (i != 0 || info.size <= 0) break; - a->q_owner = xalloc(info.size + 1); + a->q_owner = sm_rpool_malloc_x(e->e_rpool, + info.size + 1); memmove(a->q_owner, info.data, info.size); a->q_owner[info.size] = '\0'; break; @@ -517,10 +516,10 @@ udbexpand(a, sendq, aliaslevel, e) if (i >= usersize) { usersize = i + 1; - user = xalloc(usersize); + user = sm_malloc_x(usersize); } - (void) snprintf(user, usersize, "%s@%s", - a->q_user, up->udb_fwdhost); + (void) sm_strlcpyn(user, usersize, 3, + a->q_user, "@", up->udb_fwdhost); message("expanded to %s", user); a->q_flags &= ~QSELFREF; naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e); @@ -528,24 +527,25 @@ udbexpand(a, sendq, aliaslevel, e) { if (tTd(28, 5)) { - dprintf("udbexpand: QS_EXPANDED "); - printaddr(a, FALSE); + sm_dprintf("udbexpand: QS_EXPANDED "); + printaddr(a, false); } a->q_state = QS_EXPANDED; } - breakout = TRUE; + breakout = true; break; case UDB_EOLIST: - breakout = TRUE; + breakout = true; break; default: /* unknown entry type */ break; } + /* XXX if an exception occurs, there is a storage leak */ if (user != userbuf) - sm_free(user); + sm_free(user); /* XXX */ } return EX_OK; } @@ -554,10 +554,11 @@ udbexpand(a, sendq, aliaslevel, e) ** ** Parameters: ** sender -- the name of the sender on the local machine. +** rpool -- resource pool from which to allocate result ** ** Returns: ** The external name for this sender, if derivable from the -** database. +** database. Storage allocated from rpool. ** NULL -- if nothing is changed from the database. ** ** Side Effects: @@ -565,10 +566,11 @@ udbexpand(a, sendq, aliaslevel, e) */ char * -udbsender(sender) +udbsender(sender, rpool) char *sender; + SM_RPOOL_T *rpool; { - return udbmatch(sender, "mailname"); + return udbmatch(sender, "mailname", rpool); } /* ** UDBMATCH -- match user in field, return result of lookup. @@ -576,10 +578,11 @@ udbsender(sender) ** Parameters: ** user -- the name of the user. ** field -- the field to lookup. +** rpool -- resource pool from which to allocate result ** ** Returns: ** The external name for this sender, if derivable from the -** database. +** database. Storage allocated from rpool. ** NULL -- if nothing is changed from the database. ** ** Side Effects: @@ -587,9 +590,10 @@ udbsender(sender) */ static char * -udbmatch(user, field) +udbmatch(user, field, rpool) char *user; char *field; + SM_RPOOL_T *rpool; { register char *p; register struct udbent *up; @@ -599,7 +603,7 @@ udbmatch(user, field) char keybuf[MAXKEY]; if (tTd(28, 1)) - dprintf("udbmatch(%s, %s)\n", user, field); + sm_dprintf("udbmatch(%s, %s)\n", user, field); if (!UdbInitialized) { @@ -627,7 +631,7 @@ udbmatch(user, field) return NULL; /* build database key */ - (void) snprintf(keybuf, sizeof keybuf, "%s:%s", user, field); + (void) sm_strlcpyn(keybuf, sizeof keybuf, 3, user, ":", field); keylen = strlen(keybuf); for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) @@ -638,7 +642,7 @@ udbmatch(user, field) switch (up->udb_type) { -# ifdef NEWDB +# if NEWDB case UDB_DBFETCH: memset(&key, '\0', sizeof key); memset(&info, '\0', sizeof info); @@ -653,20 +657,20 @@ udbmatch(user, field) if (i != 0 || info.size <= 0) { if (tTd(28, 2)) - dprintf("udbmatch: no match on %s (%d) via db\n", + sm_dprintf("udbmatch: no match on %s (%d) via db\n", keybuf, keylen); continue; } - p = xalloc(info.size + 1); + p = sm_rpool_malloc_x(rpool, info.size + 1); memmove(p, info.data, info.size); p[info.size] = '\0'; if (tTd(28, 1)) - dprintf("udbmatch ==> %s\n", p); + sm_dprintf("udbmatch ==> %s\n", p); return p; # endif /* NEWDB */ -# ifdef HESIOD +# if HESIOD case UDB_HESIOD: key.data = keybuf; key.size = keylen; @@ -674,16 +678,16 @@ udbmatch(user, field) if (i != 0 || info.size <= 0) { if (tTd(28, 2)) - dprintf("udbmatch: no match on %s (%d) via hesiod\n", + sm_dprintf("udbmatch: no match on %s (%d) via hesiod\n", keybuf, keylen); continue; } - p = xalloc(info.size + 1); + p = sm_rpool_malloc_x(rpool, info.size + 1); memmove(p, info.data, info.size); p[info.size] = '\0'; if (tTd(28, 1)) - dprintf("udbmatch ==> %s\n", p); + sm_dprintf("udbmatch ==> %s\n", p); return p; # endif /* HESIOD */ } @@ -699,15 +703,14 @@ udbmatch(user, field) */ /* build database key */ - (void) strlcpy(keybuf, user, sizeof keybuf); - (void) strlcat(keybuf, ":maildrop", sizeof keybuf); + (void) sm_strlcpyn(keybuf, sizeof keybuf, 2, user, ":maildrop"); keylen = strlen(keybuf); for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) { switch (up->udb_type) { -# ifdef NEWDB +# if NEWDB case UDB_DBFETCH: /* get the default case for this database */ if (up->udb_default == NULL) @@ -732,7 +735,7 @@ udbmatch(user, field) } /* save the default case */ - up->udb_default = xalloc(info.size + 1); + up->udb_default = sm_pmalloc_x(info.size + 1); memmove(up->udb_default, info.data, info.size); up->udb_default[info.size] = '\0'; } @@ -758,14 +761,14 @@ udbmatch(user, field) /* they exist -- build the actual address */ i = strlen(user) + strlen(up->udb_default) + 2; - p = xalloc(i); - (void) snprintf(p, i, "%s@%s", user, up->udb_default); + p = sm_rpool_malloc_x(rpool, i); + (void) sm_strlcpyn(p, i, 3, user, "@", up->udb_default); if (tTd(28, 1)) - dprintf("udbmatch ==> %s\n", p); + sm_dprintf("udbmatch ==> %s\n", p); return p; # endif /* NEWDB */ -# ifdef HESIOD +# if HESIOD case UDB_HESIOD: /* get the default case for this database */ if (up->udb_default == NULL) @@ -782,7 +785,7 @@ udbmatch(user, field) } /* save the default case */ - up->udb_default = xalloc(info.size + 1); + up->udb_default = sm_pmalloc_x(info.size + 1); memmove(up->udb_default, info.data, info.size); up->udb_default[info.size] = '\0'; } @@ -801,10 +804,10 @@ udbmatch(user, field) /* they exist -- build the actual address */ i = strlen(user) + strlen(up->udb_default) + 2; - p = xalloc(i); - (void) snprintf(p, i, "%s@%s", user, up->udb_default); + p = sm_rpool_malloc_x(rpool, i); + (void) sm_strlcpyn(p, i, 3, user, "@", up->udb_default); if (tTd(28, 1)) - dprintf("udbmatch ==> %s\n", p); + sm_dprintf("udbmatch ==> %s\n", p); return p; break; # endif /* HESIOD */ @@ -838,10 +841,11 @@ udb_map_lookup(map, name, av, statp) { char *val; char *key; + char *SM_NONVOLATILE result = NULL; char keybuf[MAXNAME + 1]; if (tTd(28, 20) || tTd(38, 20)) - dprintf("udb_map_lookup(%s, %s)\n", map->map_mname, name); + sm_dprintf("udb_map_lookup(%s, %s)\n", map->map_mname, name); if (bitset(MF_NOFOLDCASE, map->map_mflags)) { @@ -858,13 +862,18 @@ udb_map_lookup(map, name, av, statp) makelower(keybuf); key = keybuf; } - val = udbmatch(key, map->map_file); + val = udbmatch(key, map->map_file, NULL); if (val == NULL) return NULL; - if (bitset(MF_MATCHONLY, map->map_mflags)) - return map_rewrite(map, name, strlen(name), NULL); - else - return map_rewrite(map, val, strlen(val), av); + SM_TRY + if (bitset(MF_MATCHONLY, map->map_mflags)) + result = map_rewrite(map, name, strlen(name), NULL); + else + result = map_rewrite(map, val, strlen(val), av); + SM_FINALLY + sm_free(val); + SM_END_TRY + return result; } /* ** _UDBX_INIT -- parse the UDB specification, opening any valid entries. @@ -945,25 +954,25 @@ _udbx_init(e) { case '@': /* forward to remote host */ up->udb_type = UDB_FORWARD; - up->udb_pid = getpid(); + up->udb_pid = CurrentPid; up->udb_fwdhost = spec + 1; ents++; up++; break; -# ifdef HESIOD +# if HESIOD case 'h': /* use hesiod */ case 'H': - if (strcasecmp(spec, "hesiod") != 0) + if (sm_strcasecmp(spec, "hesiod") != 0) goto badspec; up->udb_type = UDB_HESIOD; - up->udb_pid = getpid(); + up->udb_pid = CurrentPid; ents++; up++; break; # endif /* HESIOD */ -# ifdef NEWDB +# if NEWDB case '/': /* look up remote name */ l = strlen(spec); if (l > 3 && strcmp(&spec[l - 3], ".db") == 0) @@ -972,9 +981,9 @@ _udbx_init(e) } else { - up->udb_dbname = xalloc(l + 4); - (void) strlcpy(up->udb_dbname, spec, l + 4); - (void) strlcat(up->udb_dbname, ".db", l + 4); + up->udb_dbname = sm_pmalloc_x(l + 4); + (void) sm_strlcpyn(up->udb_dbname, l + 4, 2, + spec, ".db"); } errno = 0; # if DB_VERSION_MAJOR < 2 @@ -1034,12 +1043,12 @@ _udbx_init(e) int save_errno = errno; # if DB_VERSION_MAJOR < 2 - dprintf("dbopen(%s): %s\n", + sm_dprintf("dbopen(%s): %s\n", # else /* DB_VERSION_MAJOR < 2 */ - dprintf("db_open(%s): %s\n", + sm_dprintf("db_open(%s): %s\n", # endif /* DB_VERSION_MAJOR < 2 */ up->udb_dbname, - errstring(errno)); + sm_errstring(errno)); errno = save_errno; } if (errno != ENOENT && errno != EACCES) @@ -1052,34 +1061,34 @@ _udbx_init(e) "db_open(%s): %s", # endif /* DB_VERSION_MAJOR < 2 */ up->udb_dbname, - errstring(errno)); + sm_errstring(errno)); up->udb_type = UDB_EOLIST; if (up->udb_dbname != spec) - sm_free(up->udb_dbname); + sm_free(up->udb_dbname); /* XXX */ goto tempfail; } if (up->udb_dbname != spec) - sm_free(up->udb_dbname); + sm_free(up->udb_dbname); /* XXX */ break; } if (tTd(28, 1)) { # if DB_VERSION_MAJOR < 2 - dprintf("_udbx_init: dbopen(%s)\n", + sm_dprintf("_udbx_init: dbopen(%s)\n", # else /* DB_VERSION_MAJOR < 2 */ - dprintf("_udbx_init: db_open(%s)\n", + sm_dprintf("_udbx_init: db_open(%s)\n", # endif /* DB_VERSION_MAJOR < 2 */ up->udb_dbname); } up->udb_type = UDB_DBFETCH; - up->udb_pid = getpid(); + up->udb_pid = CurrentPid; ents++; up++; break; # endif /* NEWDB */ default: -# ifdef HESIOD +# if HESIOD badspec: # endif /* HESIOD */ syserr("Unknown UDB spec %s", spec); @@ -1094,40 +1103,38 @@ badspec: { switch (up->udb_type) { -# if DAEMON case UDB_REMOTE: - dprintf("REMOTE: addr %s, timeo %d\n", - anynet_ntoa((SOCKADDR *) &up->udb_addr), - up->udb_timeout); + sm_dprintf("REMOTE: addr %s, timeo %d\n", + anynet_ntoa((SOCKADDR *) &up->udb_addr), + up->udb_timeout); break; -# endif /* DAEMON */ case UDB_DBFETCH: -# ifdef NEWDB - dprintf("FETCH: file %s\n", +# if NEWDB + sm_dprintf("FETCH: file %s\n", up->udb_dbname); # else /* NEWDB */ - dprintf("FETCH\n"); + sm_dprintf("FETCH\n"); # endif /* NEWDB */ break; case UDB_FORWARD: - dprintf("FORWARD: host %s\n", + sm_dprintf("FORWARD: host %s\n", up->udb_fwdhost); break; case UDB_HESIOD: - dprintf("HESIOD\n"); + sm_dprintf("HESIOD\n"); break; default: - dprintf("UNKNOWN\n"); + sm_dprintf("UNKNOWN\n"); break; } } } - UdbInitialized = TRUE; + UdbInitialized = true; errno = 0; return EX_OK; @@ -1136,7 +1143,7 @@ badspec: */ tempfail: -# ifdef NEWDB +# if NEWDB for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) { if (up->udb_type == UDB_DBFETCH) @@ -1147,7 +1154,7 @@ badspec: errno = (*up->udb_dbp->close)(up->udb_dbp, 0); # endif /* DB_VERSION_MAJOR < 2 */ if (tTd(28, 1)) - dprintf("_udbx_init: db->close(%s)\n", + sm_dprintf("_udbx_init: db->close(%s)\n", up->udb_dbname); } } @@ -1196,20 +1203,17 @@ _udb_parsespec(udbspec, opt, maxopts) void _udbx_close() { - pid_t pid; struct udbent *up; if (!UdbInitialized) return; - pid = getpid(); - for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) { - if (up->udb_pid != pid) + if (up->udb_pid != CurrentPid) continue; -# ifdef NEWDB +# if NEWDB if (up->udb_type == UDB_DBFETCH) { # if DB_VERSION_MAJOR < 2 @@ -1219,13 +1223,13 @@ _udbx_close() # endif /* DB_VERSION_MAJOR < 2 */ } if (tTd(28, 1)) - dprintf("_udbx_init: db->close(%s)\n", + sm_dprintf("_udbx_init: db->close(%s)\n", up->udb_dbname); # endif /* NEWDB */ } } -# ifdef HESIOD +# if HESIOD static int hes_udb_get(key, info) @@ -1236,7 +1240,7 @@ hes_udb_get(key, info) char **hp; char kbuf[MAXKEY + 1]; - if (strlcpy(kbuf, key->data, sizeof kbuf) >= (SIZE_T) sizeof kbuf) + if (sm_strlcpy(kbuf, key->data, sizeof kbuf) >= sizeof kbuf) return 0; name = kbuf; type = strrchr(name, ':'); @@ -1247,7 +1251,7 @@ hes_udb_get(key, info) return 1; if (tTd(28, 1)) - dprintf("hes_udb_get(%s, %s)\n", name, type); + sm_dprintf("hes_udb_get(%s, %s)\n", name, type); /* make the hesiod query */ # ifdef HESIOD_INIT @@ -1293,7 +1297,7 @@ hes_udb_get(key, info) } if (tTd(28, 80)) - dprintf("hes_udb_get => %s\n", *hp); + sm_dprintf("hes_udb_get => %s\n", *hp); return 0; } diff --git a/gnu/usr.sbin/sendmail/sendmail/usersmtp.c b/gnu/usr.sbin/sendmail/sendmail/usersmtp.c index f79e324724d..bc1d7d898fe 100644 --- a/gnu/usr.sbin/sendmail/sendmail/usersmtp.c +++ b/gnu/usr.sbin/sendmail/sendmail/usersmtp.c @@ -13,22 +13,21 @@ #include <sendmail.h> -#ifndef lint -# if SMTP -static char id[] = "@(#)$Sendmail: usersmtp.c,v 8.245.4.34 2001/06/26 21:55:23 gshapiro Exp $ (with SMTP)"; -# else /* SMTP */ -static char id[] = "@(#)$Sendmail: usersmtp.c,v 8.245.4.34 2001/06/26 21:55:23 gshapiro Exp $ (without SMTP)"; -# endif /* SMTP */ -#endif /* ! lint */ +SM_RCSID("@(#)$Sendmail: usersmtp.c,v 8.418 2001/09/04 22:43:06 ca Exp $") #include <sysexits.h> -#if SMTP - +extern void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int, bool)); static void datatimeout __P((void)); static void esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *)); static void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *)); +static int smtprcptstat __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *)); + +#if SASL +extern void *sm_sasl_malloc __P((unsigned long)); +extern void sm_sasl_free __P((void *)); +#endif /* SASL */ /* ** USERSMTP -- run SMTP protocol from the user end. @@ -36,11 +35,14 @@ static void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *)); ** This protocol is described in RFC821. */ -# define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ -# define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ -# define SMTPCLOSING 421 /* "Service Shutting Down" */ +#define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ +#define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ +#define SMTPCLOSING 421 /* "Service Shutting Down" */ -#define ENHSCN(e, d) (e) == NULL ? (d) : newstr(e) +#define ENHSCN(e, d) ((e) == NULL ? (d) : (e)) + +#define ENHSCN_RPOOL(e, d, rpool) \ + ((e) == NULL ? (d) : sm_rpool_strdup_x(rpool, e)) static char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ static char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ @@ -78,8 +80,8 @@ smtpinit(m, mci, e, onlyhelo) enhsc = NULL; if (tTd(18, 1)) { - dprintf("smtpinit "); - mci_dump(mci, FALSE); + sm_dprintf("smtpinit "); + mci_dump(mci, false); } /* @@ -90,10 +92,12 @@ smtpinit(m, mci, e, onlyhelo) CurHostName = mci->mci_host; /* XXX UGLY XXX */ if (CurHostName == NULL) CurHostName = MyHostName; - SmtpNeedIntro = TRUE; + SmtpNeedIntro = true; switch (mci->mci_state) { - case MCIS_ACTIVE: + case MCIS_MAIL: + case MCIS_RCPT: + case MCIS_DATA: /* need to clear old information */ smtprset(m, mci, e); /* FALLTHROUGH */ @@ -129,7 +133,7 @@ smtpinit(m, mci, e, onlyhelo) */ SmtpPhase = mci->mci_phase = "client greeting"; - sm_setproctitle(TRUE, e, "%s %s: %s", + sm_setproctitle(true, e, "%s %s: %s", qid_printname(e), CurHostName, mci->mci_phase); r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL); if (r < 0) @@ -150,12 +154,16 @@ helo: hn = mci->mci_heloname ? mci->mci_heloname : MyHostName; tryhelo: +#if _FFR_IGNORE_EXT_ON_HELO + mci->mci_flags &= ~MCIF_HELO; +#endif /* _FFR_IGNORE_EXT_ON_HELO */ if (bitnset(M_LMTP, m->m_flags)) { smtpmessage("LHLO %s", m, mci, hn); SmtpPhase = mci->mci_phase = "client LHLO"; } - else if (bitset(MCIF_ESMTP, mci->mci_flags)) + else if (bitset(MCIF_ESMTP, mci->mci_flags) && + !bitnset(M_FSMTP, m->m_flags)) { smtpmessage("EHLO %s", m, mci, hn); SmtpPhase = mci->mci_phase = "client EHLO"; @@ -164,10 +172,16 @@ tryhelo: { smtpmessage("HELO %s", m, mci, hn); SmtpPhase = mci->mci_phase = "client HELO"; +#if _FFR_IGNORE_EXT_ON_HELO + mci->mci_flags |= MCIF_HELO; +#endif /* _FFR_IGNORE_EXT_ON_HELO */ } - sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e), + sm_setproctitle(true, e, "%s %s: %s", qid_printname(e), CurHostName, mci->mci_phase); - r = reply(m, mci, e, TimeOuts.to_helo, helo_options, NULL); + r = reply(m, mci, e, + bitnset(M_LMTP, m->m_flags) ? TimeOuts.to_lhlo + : TimeOuts.to_helo, + helo_options, NULL); if (r < 0) goto tempfail1; else if (REPLYTYPE(r) == 5) @@ -195,7 +209,7 @@ tryhelo: *p = '\0'; if (!bitnset(M_NOLOOPCHECK, m->m_flags) && !bitnset(M_LMTP, m->m_flags) && - strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) + sm_strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) { syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)", CurHostName); @@ -206,6 +220,7 @@ tryhelo: return; } +#if !_FFR_DEPRECATE_MAILER_FLAG_I /* ** If this is expected to be another sendmail, send some internal ** commands. @@ -219,6 +234,7 @@ tryhelo: if (r < 0) goto tempfail1; } +#endif /* !_FFR_DEPRECATE_MAILER_FLAG_I */ if (mci->mci_state != MCIS_CLOSED) { @@ -239,6 +255,7 @@ tryhelo: tempfail2: if (mci->mci_errno == 0) mci->mci_errno = errno; + /* XXX should use code from other end iff ENHANCEDSTATUSCODES */ mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"), SmtpReplyBuffer); @@ -276,23 +293,38 @@ esmtp_check(line, firstline, m, mci, e) { if (strstr(line, "ESMTP") != NULL) mci->mci_flags |= MCIF_ESMTP; + + /* + ** Dirty hack below. Quoting the author: + ** This was a response to people who wanted SMTP transmission to be + ** just-send-8 by default. Essentially, you could put this tag into + ** your greeting message to behave as though the F=8 flag was set on + ** the mailer. + */ + if (strstr(line, "8BIT-OK") != NULL) mci->mci_flags |= MCIF_8BITOK; } -# if SASL + +#if SASL +/* specify prototype so compiler can check calls */ +static char *str_union __P((char *, char *, SM_RPOOL_T *)); + /* ** STR_UNION -- create the union of two lists ** ** Parameters: ** s1, s2 -- lists of items (separated by single blanks). +** rpool -- resource pool from which result is allocated. ** ** Returns: ** the union of both lists. */ static char * -str_union(s1, s2) +str_union(s1, s2, rpool) char *s1, *s2; + SM_RPOOL_T *rpool; { char *hr, *h1, *h, *res; int l1, l2, rl; @@ -304,8 +336,14 @@ str_union(s1, s2) l1 = strlen(s1); l2 = strlen(s2); rl = l1 + l2; - res = (char *)xalloc(rl + 2); - (void) strlcpy(res, s1, rl); + res = (char *) sm_rpool_malloc(rpool, rl + 2); + if (res == NULL) + { + if (l1 > l2) + return s1; + return s2; + } + (void) sm_strlcpy(res, s1, rl); hr = res + l1; h1 = s2; h = s2; @@ -340,7 +378,8 @@ str_union(s1, s2) } return res; } -# endif /* SASL */ +#endif /* SASL */ + /* ** HELO_OPTIONS -- process the options on a HELO line. ** @@ -349,7 +388,7 @@ str_union(s1, s2) ** firstline -- set if this is the first line of the reply. ** m -- the mailer. ** mci -- the mailer connection info. -** e -- the envelope. +** e -- the envelope (unused). ** ** Returns: ** none. @@ -364,62 +403,85 @@ helo_options(line, firstline, m, mci, e) ENVELOPE *e; { register char *p; +#if _FFR_IGNORE_EXT_ON_HELO + static bool logged = false; +#endif /* _FFR_IGNORE_EXT_ON_HELO */ if (firstline) { -# if SASL - if (mci->mci_saslcap != NULL) - sm_free(mci->mci_saslcap); +#if SASL mci->mci_saslcap = NULL; -# endif /* SASL */ +#endif /* SASL */ +#if _FFR_IGNORE_EXT_ON_HELO + logged = false; +#endif /* _FFR_IGNORE_EXT_ON_HELO */ + return; + } +#if _FFR_IGNORE_EXT_ON_HELO + else if (bitset(MCIF_HELO, mci->mci_flags)) + { + if (LogLevel > 8 && !logged) + { + sm_syslog(LOG_WARNING, NOQID, + "server=%s [%s] returned extensions despite HELO command", + macvalue(macid("{server_name}"), e), + macvalue(macid("{server_addr}"), e)); + logged = true; + } return; } +#endif /* _FFR_IGNORE_EXT_ON_HELO */ - if (strlen(line) < (SIZE_T) 5) + if (strlen(line) < 5) return; line += 4; p = strpbrk(line, " ="); if (p != NULL) *p++ = '\0'; - if (strcasecmp(line, "size") == 0) + if (sm_strcasecmp(line, "size") == 0) { mci->mci_flags |= MCIF_SIZE; if (p != NULL) mci->mci_maxsize = atol(p); } - else if (strcasecmp(line, "8bitmime") == 0) + else if (sm_strcasecmp(line, "8bitmime") == 0) { mci->mci_flags |= MCIF_8BITMIME; mci->mci_flags &= ~MCIF_7BIT; } - else if (strcasecmp(line, "expn") == 0) + else if (sm_strcasecmp(line, "expn") == 0) mci->mci_flags |= MCIF_EXPN; - else if (strcasecmp(line, "dsn") == 0) + else if (sm_strcasecmp(line, "dsn") == 0) mci->mci_flags |= MCIF_DSN; - else if (strcasecmp(line, "enhancedstatuscodes") == 0) + else if (sm_strcasecmp(line, "enhancedstatuscodes") == 0) mci->mci_flags |= MCIF_ENHSTAT; -# if STARTTLS - else if (strcasecmp(line, "starttls") == 0) + else if (sm_strcasecmp(line, "pipelining") == 0) + mci->mci_flags |= MCIF_PIPELINED; +#if STARTTLS + else if (sm_strcasecmp(line, "starttls") == 0) mci->mci_flags |= MCIF_TLS; -# endif /* STARTTLS */ -# if SASL - else if (strcasecmp(line, "auth") == 0) +#endif /* STARTTLS */ + else if (sm_strcasecmp(line, "deliverby") == 0) + { + mci->mci_flags |= MCIF_DLVR_BY; + if (p != NULL) + mci->mci_min_by = atol(p); + } +#if SASL + else if (sm_strcasecmp(line, "auth") == 0) { if (p != NULL && *p != '\0') { if (mci->mci_saslcap != NULL) { - char *h; - /* - ** create the union with previous auth + ** Create the union with previous auth ** offerings because we recognize "auth " ** and "auth=" (old format). */ - h = mci->mci_saslcap; - mci->mci_saslcap = str_union(h, p); - if (h != mci->mci_saslcap) - sm_free(h); + + mci->mci_saslcap = str_union(mci->mci_saslcap, + p, mci->mci_rpool); mci->mci_flags |= MCIF_AUTH; } else @@ -427,34 +489,115 @@ helo_options(line, firstline, m, mci, e) int l; l = strlen(p) + 1; - mci->mci_saslcap = (char *)xalloc(l); - (void) strlcpy(mci->mci_saslcap, p, l); - mci->mci_flags |= MCIF_AUTH; + mci->mci_saslcap = (char *) + sm_rpool_malloc(mci->mci_rpool, l); + if (mci->mci_saslcap != NULL) + { + (void) sm_strlcpy(mci->mci_saslcap, p, + l); + mci->mci_flags |= MCIF_AUTH; + } } } } -# endif /* SASL */ +#endif /* SASL */ +} +#if SASL + +static int getsimple __P((void *, int, const char **, unsigned *)); +static int getsecret __P((sasl_conn_t *, void *, int, sasl_secret_t **)); +static int saslgetrealm __P((void *, int, const char **, const char **)); +static int readauth __P((char *, bool, SASL_AI_T *m, SM_RPOOL_T *)); +static int getauth __P((MCI *, ENVELOPE *, SASL_AI_T *)); +static char *removemech __P((char *, char *, SM_RPOOL_T *)); +static int attemptauth __P((MAILER *, MCI *, ENVELOPE *, SASL_AI_T *)); + +static sasl_callback_t callbacks[] = +{ + { SASL_CB_GETREALM, &saslgetrealm, NULL }, +#define CB_GETREALM_IDX 0 + { SASL_CB_PASS, &getsecret, NULL }, +#define CB_PASS_IDX 1 + { SASL_CB_USER, &getsimple, NULL }, +#define CB_USER_IDX 2 + { SASL_CB_AUTHNAME, &getsimple, NULL }, +#define CB_AUTHNAME_IDX 3 + { SASL_CB_VERIFYFILE, &safesaslfile, NULL }, +#define CB_SAFESASL_IDX 4 + { SASL_CB_LIST_END, NULL, NULL } +}; + +/* +** INIT_SASL_CLIENT -- initialize client side of Cyrus-SASL +** +** Parameters: +** none. +** +** Returns: +** SASL_OK -- if successful. +** SASL error code -- otherwise. +** +** Side Effects: +** checks/sets sasl_clt_init. +*/ + +static bool sasl_clt_init = false; + +static int +init_sasl_client() +{ + int result; + + if (sasl_clt_init) + return SASL_OK; + result = sasl_client_init(callbacks); + + /* should we retry later again or just remember that it failed? */ + if (result == SASL_OK) + sasl_clt_init = true; + return result; } -# if SASL +/* +** STOP_SASL_CLIENT -- shutdown client side of Cyrus-SASL +** +** Parameters: +** none. +** +** Returns: +** none. +** +** Side Effects: +** checks/sets sasl_clt_init. +*/ +void +stop_sasl_client() +{ + if (!sasl_clt_init) + return; + sasl_clt_init = false; + sasl_done(); +} /* ** GETSASLDATA -- process the challenges from the SASL protocol ** ** This gets the relevant sasl response data out of the reply -** from the server +** from the server. ** ** Parameters: ** line -- the response line. ** firstline -- set if this is the first line of the reply. ** m -- the mailer. ** mci -- the mailer connection info. -** e -- the envelope. +** e -- the envelope (unused). ** ** Returns: ** none. */ -void +static void getsasldata __P((char *, bool, MAILER *, MCI *, ENVELOPE *)); + +static void getsasldata(line, firstline, m, mci, e) char *line; bool firstline; @@ -463,96 +606,94 @@ getsasldata(line, firstline, m, mci, e) ENVELOPE *e; { int len; - char *out; int result; + char *out; /* if not a continue we don't care about it */ - if ((strlen(line) <= 4) || + len = strlen(line); + if ((len <= 4) || (line[0] != '3') || - (line[1] != '3') || - (line[2] != '4')) + !isascii(line[1]) || !isdigit(line[1]) || + !isascii(line[2]) || !isdigit(line[2])) { - mci->mci_sasl_string = NULL; + SM_FREE_CLR(mci->mci_sasl_string); return; } /* forget about "334 " */ line += 4; - len = strlen(line); + len -= 4; - out = xalloc(len + 1); - result = sasl_decode64(line, len, out, (u_int *)&len); + out = (char *) sm_rpool_malloc_x(mci->mci_rpool, len + 1); + result = sasl_decode64(line, len, out, (unsigned int *)&len); if (result != SASL_OK) { len = 0; *out = '\0'; } + + /* + ** mci_sasl_string is "shared" with Cyrus-SASL library; hence + ** it can't be in an rpool unless we use the same memory + ** management mechanism (with same rpool!) for Cyrus SASL. + */ + if (mci->mci_sasl_string != NULL) { if (mci->mci_sasl_string_len <= len) { - sm_free(mci->mci_sasl_string); + sm_free(mci->mci_sasl_string); /* XXX */ mci->mci_sasl_string = xalloc(len + 1); } } else mci->mci_sasl_string = xalloc(len + 1); - /* XXX this is probably leaked */ + memcpy(mci->mci_sasl_string, out, len); mci->mci_sasl_string[len] = '\0'; mci->mci_sasl_string_len = len; - sm_free(out); return; } - /* -** READAUTH -- read auth value from a file +** READAUTH -- read auth values from a file ** ** Parameters: -** l -- line to define. ** filename -- name of file to read. ** safe -- if set, this is a safe read. +** sai -- where to store auth_info. +** rpool -- resource pool for sai. ** ** Returns: -** line from file -** -** Side Effects: -** overwrites local static buffer. The caller should copy -** the result. -** +** EX_OK -- data succesfully read. +** EX_UNAVAILABLE -- no valid filename. +** EX_TEMPFAIL -- temporary failure. */ -/* lines in authinfo file */ -# define SASL_USER 1 -# define SASL_AUTHID 2 -# define SASL_PASSWORD 3 -# define SASL_DEFREALM 4 -# define SASL_MECH 5 - static char *sasl_info_name[] = { - "", "user id", - "authorization id", + "authentication id", "password", "realm", - "mechanism" + "mechlist" }; - -static char * -readauth(l, filename, safe) - int l; +static int +readauth(filename, safe, sai, rpool) char *filename; bool safe; + SASL_AI_T *sai; + SM_RPOOL_T *rpool; { - FILE *f; + SM_FILE_T *f; long sff; pid_t pid; int lc; - static char buf[MAXLINE]; + char *s; + char buf[MAXLINE]; if (filename == NULL || filename[0] == '\0') - return ""; + return EX_UNAVAILABLE; + #if !_FFR_ALLOW_SASLINFO /* ** make sure we don't use a program that is not @@ -560,6 +701,7 @@ readauth(l, filename, safe) ** However, currently we don't pass this info (authinfo file ** specified by user) around, so we just turn off program access. */ + if (filename[0] == '|') { auto int fd; @@ -580,22 +722,29 @@ readauth(l, filename, safe) if (pid < 0) f = NULL; else - f = fdopen(fd, "r"); + f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, + (void *) fd, SM_IO_RDONLY, NULL); } else #endif /* !_FFR_ALLOW_SASLINFO */ { pid = -1; - sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK - | SFF_NOGWFILES | SFF_NOWWFILES | SFF_NORFILES; + sff = SFF_REGONLY|SFF_SAFEDIRPATH|SFF_NOWLINK + |SFF_NOGWFILES|SFF_NOWWFILES|SFF_NOWRFILES; +# if _FFR_GROUPREADABLEAUTHINFOFILE + if (!bitnset(DBS_GROUPREADABLEAUTHINFOFILE, DontBlameSendmail)) +# endif /* _FFR_GROUPREADABLEAUTHINFOFILE */ + sff |= SFF_NOGRFILES; if (DontLockReadFiles) sff |= SFF_NOLOCK; + #if _FFR_ALLOW_SASLINFO /* ** XXX: make sure we don't read or open files that are not ** accesible to the user who specified a different authinfo ** file. */ + sff |= SFF_MUSTOWN; #else /* _FFR_ALLOW_SASLINFO */ if (safe) @@ -606,62 +755,208 @@ readauth(l, filename, safe) } if (f == NULL) { - syserr("readauth: cannot open %s", filename); - return ""; + if (LogLevel > 5) + sm_syslog(LOG_ERR, NOQID, + "AUTH=client, error: can't open %s: %s", + filename, sm_errstring(errno)); + return EX_TEMPFAIL; } lc = 0; - while (lc < l && fgets(buf, sizeof buf, f) != NULL) + while (lc <= SASL_MECHLIST && + sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) { if (buf[0] != '#') + { + (*sai)[lc] = sm_rpool_strdup_x(rpool, buf); + if ((s = strchr((*sai)[lc], '\n')) != NULL) + *s = '\0'; lc++; + } } - (void) fclose(f); + (void) sm_io_close(f, SM_TIME_DEFAULT); if (pid > 0) (void) waitfor(pid); - if (lc < l) - { - if (LogLevel >= 9) - sm_syslog(LOG_WARNING, NOQID, "SASL: error: can't read %s from %s", - sasl_info_name[l], filename); - return ""; - } - lc = strlen(buf) - 1; - if (lc >= 0) - buf[lc] = '\0'; - if (tTd(95, 6)) - dprintf("readauth(%s, %d) = '%s'\n", filename, l, buf); - return buf; + if (lc < SASL_PASSWORD) + { + if (LogLevel > 8) + sm_syslog(LOG_ERR, NOQID, + "AUTH=client, error: can't read %s from %s", + sasl_info_name[lc + 1], filename); + return EX_TEMPFAIL; + } + return EX_OK; } -# ifndef __attribute__ -# define __attribute__(x) -# endif /* ! __attribute__ */ - -static int getsimple __P((void *, int, const char **, unsigned *)); -static int getsecret __P((sasl_conn_t *, void *, int, sasl_secret_t **)); -static int saslgetrealm __P((void *, int, const char **, const char **)); +/* +** GETAUTH -- get authinfo from ruleset call +** +** {server_name}, {server_addr} must be set +** +** Parameters: +** mci -- the mailer connection structure. +** e -- the envelope (including the sender to specify). +** sai -- pointer to authinfo (result). +** +** Returns: +** EX_OK -- ruleset was succesfully called, data may not +** be available, sai must be checked. +** EX_UNAVAILABLE -- ruleset unavailable (or failed). +** EX_TEMPFAIL -- temporary failure (from ruleset). +** +** Side Effects: +** Fills in sai if successful. +*/ -static sasl_callback_t callbacks[] = +static int +getauth(mci, e, sai) + MCI *mci; + ENVELOPE *e; + SASL_AI_T *sai; { - { SASL_CB_GETREALM, &saslgetrealm, NULL }, -# define CB_GETREALM_IDX 0 - { SASL_CB_PASS, &getsecret, NULL }, -# define CB_PASS_IDX 1 - { SASL_CB_USER, &getsimple, NULL }, -# define CB_USER_IDX 2 - { SASL_CB_AUTHNAME, &getsimple, NULL }, -# define CB_AUTHNAME_IDX 3 - { SASL_CB_VERIFYFILE, &safesaslfile, NULL }, - { SASL_CB_LIST_END, NULL, NULL } -}; + int i, r, l, got, ret; + char **pvp; + char pvpbuf[PSBUFSIZE]; + + r = rscap("authinfo", macvalue(macid("{server_name}"), e), + macvalue(macid("{server_addr}"), e), e, + &pvp, pvpbuf, sizeof(pvpbuf)); + + if (r != EX_OK) + return EX_UNAVAILABLE; + + /* other than expected return value: ok (i.e., no auth) */ + if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) + return EX_OK; + if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0) + return EX_TEMPFAIL; + + /* + ** parse the data, put it into sai + ** format: "TDstring" (including the '"' !) + ** where T is a tag: 'U', ... + ** D is a delimiter: ':' or '=' + */ + ret = EX_OK; /* default return value */ + i = 0; + got = 0; + while (i < SASL_ENTRIES) + { + if (pvp[i + 1] == NULL) + break; + if (pvp[i + 1][0] != '"') + break; + switch (pvp[i + 1][1]) + { + case 'U': + case 'u': + r = SASL_USER; + break; + case 'I': + case 'i': + r = SASL_AUTHID; + break; + case 'P': + case 'p': + r = SASL_PASSWORD; + break; + case 'R': + case 'r': + r = SASL_DEFREALM; + break; + case 'M': + case 'm': + r = SASL_MECHLIST; + break; + default: + goto fail; + } + l = strlen(pvp[i + 1]); + + /* remove closing quote */ + if (l > 3 && pvp[i + 1][l - 1] == '"') + pvp[i + 1][l - 1] = '\0'; + else + goto fail; + + /* remove "TD and " */ + l -= 4; + (*sai)[r] = (char *) sm_rpool_malloc(mci->mci_rpool, l + 1); + if ((*sai)[r] == NULL) + goto tempfail; + if (pvp[i + 1][2] == ':') + { + /* ':text' (just copy) */ + (void) sm_strlcpy((*sai)[r], pvp[i + 1] + 3, l + 1); + got |= 1 << r; + } + else if (pvp[i + 1][2] == '=') + { + unsigned int len; + + /* '=base64' (decode) */ + r = sasl_decode64(pvp[i + 1] + 3, + (unsigned int) l, (*sai)[r], &len); + if (r != SASL_OK) + goto fail; + got |= 1 << r; + } + else + goto fail; + if (tTd(95, 5)) + sm_syslog(LOG_WARNING, NOQID, "getauth %s=%s", + sasl_info_name[r], (*sai)[r]); + ++i; + } + + /* did we get the expected data? */ + if (!(bitset(SASL_USER_BIT|SASL_AUTHID_BIT, got) && + bitset(SASL_PASSWORD_BIT, got))) + goto fail; + + /* no authid? copy uid */ + if (!bitset(SASL_AUTHID_BIT, got)) + { + l = strlen((*sai)[SASL_USER]) + 1; + (*sai)[SASL_AUTHID] = (char *) sm_rpool_malloc(mci->mci_rpool, + l + 1); + if ((*sai)[SASL_AUTHID] == NULL) + goto tempfail; + (void) sm_strlcpy((*sai)[SASL_AUTHID], (*sai)[SASL_USER], l); + } + + /* no uid? copy authid */ + if (!bitset(SASL_USER_BIT, got)) + { + l = strlen((*sai)[SASL_AUTHID]) + 1; + (*sai)[SASL_USER] = (char *) sm_rpool_malloc(mci->mci_rpool, + l + 1); + if ((*sai)[SASL_USER] == NULL) + goto tempfail; + (void) sm_strlcpy((*sai)[SASL_USER], (*sai)[SASL_AUTHID], l); + } + return EX_OK; + + tempfail: + ret = EX_TEMPFAIL; + fail: + if (LogLevel > 8) + sm_syslog(LOG_WARNING, NOQID, + "AUTH=client, relay=%.64s [%.16s], authinfo %sfailed", + macvalue(macid("{server_name}"), e), + macvalue(macid("{server_addr}"), e), + ret == EX_TEMPFAIL ? "temp" : ""); + for (i = 0; i <= SASL_MECHLIST; i++) + (*sai)[i] = NULL; /* just clear; rpool */ + return ret; +} /* ** GETSIMPLE -- callback to get userid or authid ** ** Parameters: -** context -- unused +** context -- sai ** id -- what to do ** result -- (pointer to) result ** len -- (pointer to) length of result @@ -672,80 +967,110 @@ static sasl_callback_t callbacks[] = static int getsimple(context, id, result, len) - void *context __attribute__((unused)); + void *context; int id; const char **result; unsigned *len; { - char *h; -# if SASL > 10509 - int addrealm; - static int addedrealm = FALSE; -# endif /* SASL > 10509 */ - static char *user = NULL; - static char *authid = NULL; - - if (result == NULL) + char *h, *s; +# if SASL > 10509 + bool addrealm; +# endif /* SASL > 10509 */ + size_t l; + SASL_AI_T *sai; + char *authid = NULL; + + if (result == NULL || context == NULL) return SASL_BADPARAM; + sai = (SASL_AI_T *) context; + + /* + ** Unfortunately it is not clear whether this routine should + ** return a copy of a string or just a pointer to a string. + ** The Cyrus-SASL plugins treat these return values differently, e.g., + ** plugins/cram.c free()s authid, plugings/digestmd5.c does not. + ** The best solution to this problem is to fix Cyrus-SASL, but it + ** seems there is nobody who creates patches... Hello CMU!? + ** The second best solution is to have flags that tell this routine + ** whether to return an malloc()ed copy. + ** The next best solution is to always return an malloc()ed copy, + ** and suffer from some memory leak, which is ugly for persistent + ** queue runners. + ** For now we go with the last solution... + ** We can't use rpools (which would avoid this particular problem) + ** as explained in sasl.c. + */ switch (id) { case SASL_CB_USER: - if (user == NULL) + l = strlen((*sai)[SASL_USER]) + 1; + s = sm_sasl_malloc(l); + if (s == NULL) { - h = readauth(SASL_USER, SASLInfo, TRUE); - user = newstr(h); + if (len != NULL) + *len = 0; + *result = NULL; + return SASL_NOMEM; } - *result = user; + (void) sm_strlcpy(s, (*sai)[SASL_USER], l); + *result = s; if (tTd(95, 5)) - dprintf("AUTH username '%s'\n", *result); + sm_syslog(LOG_WARNING, NOQID, "AUTH username '%s'", + *result); if (len != NULL) - *len = user ? strlen(user) : 0; + *len = *result != NULL ? strlen(*result) : 0; break; case SASL_CB_AUTHNAME: -# if SASL > 10509 + h = (*sai)[SASL_AUTHID]; +# if SASL > 10509 /* XXX maybe other mechanisms too?! */ - addrealm = context != NULL && - strcasecmp(context, "CRAM-MD5") == 0; - if (addedrealm != addrealm && authid != NULL) - { -# if SASL > 10522 - /* - ** digest-md5 prior to 1.5.23 doesn't copy the - ** value it gets from the callback, but free()s - ** it later on - ** workaround: don't free() it here - ** this can cause a memory leak! - */ - - sm_free(authid); -# endif /* SASL > 10522 */ - authid = NULL; - addedrealm = addrealm; - } -# endif /* SASL > 10509 */ - if (authid == NULL) + addrealm = (*sai)[SASL_MECH] != NULL && + sm_strcasecmp((*sai)[SASL_MECH], "CRAM-MD5") == 0; + if (addrealm && h != NULL && strchr(h, '@') == NULL) { - h = readauth(SASL_AUTHID, SASLInfo, TRUE); -# if SASL > 10509 - if (addrealm && strchr(h, '@') == NULL) + if ((*sai)[SASL_ID_REALM] == NULL) { - size_t l; char *realm; - realm = callbacks[CB_GETREALM_IDX].context; + realm = (*sai)[SASL_DEFREALM]; l = strlen(h) + strlen(realm) + 2; - authid = xalloc(l); - snprintf(authid, l, "%s@%s", h, realm); + + /* should use rpool, but how to get it? */ + authid = sm_sasl_malloc(l); + if (authid != NULL) + { + (void) sm_snprintf(authid, l, "%s@%s", + h, realm); + (*sai)[SASL_ID_REALM] = authid; + } + else + { + authid = h; + (*sai)[SASL_ID_REALM] = NULL; + } } else -# endif /* SASL > 10509 */ - authid = newstr(h); + authid = (*sai)[SASL_ID_REALM]; + } + else +# endif /* SASL > 10509 */ + authid = h; + l = strlen(authid) + 1; + s = sm_sasl_malloc(l); + if (s == NULL) + { + if (len != NULL) + *len = 0; + *result = NULL; + return SASL_NOMEM; } - *result = authid; + (void) sm_strlcpy(s, authid, l); + *result = s; if (tTd(95, 5)) - dprintf("AUTH authid '%s'\n", *result); + sm_syslog(LOG_WARNING, NOQID, "AUTH authid '%s'", + *result); if (len != NULL) *len = authid ? strlen(authid) : 0; break; @@ -761,13 +1086,12 @@ getsimple(context, id, result, len) } return SASL_OK; } - /* ** GETSECRET -- callback to get password ** ** Parameters: ** conn -- connection information -** context -- unused +** context -- sai ** id -- what to do ** psecret -- (pointer to) result ** @@ -778,29 +1102,28 @@ getsimple(context, id, result, len) static int getsecret(conn, context, id, psecret) sasl_conn_t *conn; - void *context __attribute__((unused)); + SM_UNUSED(void *context); int id; sasl_secret_t **psecret; { - char *h; int len; - static char *authpass = NULL; + char *authpass; + SASL_AI_T *sai; if (conn == NULL || psecret == NULL || id != SASL_CB_PASS) return SASL_BADPARAM; - if (authpass == NULL) - { - h = readauth(SASL_PASSWORD, SASLInfo, TRUE); - authpass = newstr(h); - } + sai = (SASL_AI_T *) context; + authpass = (*sai)[SASL_PASSWORD]; len = strlen(authpass); - *psecret = (sasl_secret_t *) xalloc(sizeof(sasl_secret_t) + len + 1); - (void) strlcpy((*psecret)->data, authpass, len + 1); - (*psecret)->len = len; + *psecret = (sasl_secret_t *) sm_sasl_malloc(sizeof(sasl_secret_t) + + len + 1); + if (*psecret == NULL) + return SASL_FAIL; + (void) sm_strlcpy((*psecret)->data, authpass, len + 1); + (*psecret)->len = (unsigned long) len; return SASL_OK; } - /* ** SAFESASLFILE -- callback for sasl: is file safe? ** @@ -810,66 +1133,70 @@ getsecret(conn, context, id, psecret) ** type -- type of file to check ** ** Returns: -** SASL_OK: file can be used -** SASL_CONTINUE: don't use file -** SASL_FAIL: failure (not used here) +** SASL_OK -- file can be used +** SASL_CONTINUE -- don't use file +** SASL_FAIL -- failure (not used here) ** */ + int -# if SASL > 10515 +#if SASL > 10515 safesaslfile(context, file, type) -# else /* SASL > 10515 */ +#else /* SASL > 10515 */ safesaslfile(context, file) -# endif /* SASL > 10515 */ +#endif /* SASL > 10515 */ void *context; char *file; -# if SASL > 10515 +#if SASL > 10515 int type; -# endif /* SASL > 10515 */ +#endif /* SASL > 10515 */ { long sff; int r; +#if SASL <= 10515 + size_t len; +#endif /* SASL <= 10515 */ char *p; if (file == NULL || *file == '\0') return SASL_OK; - - sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOGWFILES|SFF_NOWWFILES|SFF_ROOTOK; + sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOWWFILES|SFF_ROOTOK; if ((p = strrchr(file, '/')) == NULL) p = file; else ++p; -# if SASL <= 10515 +#if SASL <= 10515 /* everything beside libs and .conf files must not be readable */ - r = strlen(p); - if ((r <= 3 || strncmp(p, "lib", 3) != 0) && - (r <= 5 || strncmp(p + r - 5, ".conf", 5) != 0) -# if _FFR_UNSAFE_SASL - && !bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail) -# endif /* _FFR_UNSAFE_SASL */ - ) - sff |= SFF_NORFILES; -# else /* SASL > 10515 */ + len = strlen(p); + if ((len <= 3 || strncmp(p, "lib", 3) != 0) && + (len <= 5 || strncmp(p + len - 5, ".conf", 5) != 0)) + { + if (!bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail)) + sff |= SFF_NORFILES; + if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail)) + sff |= SFF_NOGWFILES; + } +#else /* SASL <= 10515 */ /* files containing passwords should be not readable */ if (type == SASL_VRFY_PASSWD) { -# if _FFR_UNSAFE_SASL - if (bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail)) + if (bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail)) sff |= SFF_NOWRFILES; else -# endif /* _FFR_UNSAFE_SASL */ sff |= SFF_NORFILES; + if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail)) + sff |= SFF_NOGWFILES; } -# endif /* SASL <= 10515 */ +#endif /* SASL <= 10515 */ p = file; if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff, S_IRUSR, NULL)) == 0) return SASL_OK; - if (LogLevel >= 11 || (r != ENOENT && LogLevel >= 9)) + if (LogLevel > (r != ENOENT ? 8 : 10)) sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s", - p, errstring(r)); + p, sm_errstring(r)); return SASL_CONTINUE; } @@ -880,7 +1207,6 @@ safesaslfile(context, file) ** ** Parameters: ** context -- context shared between invocations -** here: realm to return ** availrealms -- list of available realms ** {realm, realm, ...} ** result -- pointer to result @@ -888,6 +1214,7 @@ safesaslfile(context, file) ** Returns: ** failure/success */ + static int saslgetrealm(context, id, availrealms, result) void *context; @@ -895,14 +1222,22 @@ saslgetrealm(context, id, availrealms, result) const char **availrealms; const char **result; { - if (LogLevel > 12) - sm_syslog(LOG_INFO, NOQID, "saslgetrealm: realm %s available realms %s", - context == NULL ? "<No Context>" : (char *) context, - (availrealms == NULL || *availrealms == NULL) ? "<No Realms>" : *availrealms); - if (context == NULL) + char *r; + SASL_AI_T *sai; + + sai = (SASL_AI_T *) context; + if (sai == NULL) return SASL_FAIL; + r = (*sai)[SASL_DEFREALM]; - /* check whether context is in list? */ + if (LogLevel > 12) + sm_syslog(LOG_INFO, NOQID, + "AUTH=client, realm=%s, available realms=%s", + r == NULL ? "<No Realm>" : r, + (availrealms == NULL || *availrealms == NULL) + ? "<No Realms>" : *availrealms); + + /* check whether context is in list */ if (availrealms != NULL && *availrealms != NULL) { if (iteminlist(context, (char *)(*availrealms + 1), " ,}") == @@ -910,12 +1245,12 @@ saslgetrealm(context, id, availrealms, result) { if (LogLevel > 8) sm_syslog(LOG_ERR, NOQID, - "saslgetrealm: realm %s not in list %s", - context, *availrealms); + "AUTH=client, realm=%s not in list=%s", + r, *availrealms); return SASL_FAIL; } } - *result = (char *)context; + *result = r; return SASL_OK; } /* @@ -952,7 +1287,7 @@ iteminlist(item, list, delim) len = strlen(item); while (s != NULL && *s != '\0') { - if (strncasecmp(s, item, len) == 0 && + if (sm_strncasecmp(s, item, len) == 0 && (s[len] == '\0' || strchr(delim, s[len]) != NULL)) return s; s = strpbrk(s, delim); @@ -968,15 +1303,17 @@ iteminlist(item, list, delim) ** Parameters: ** rem -- item to remove ** list -- list of items +** rpool -- resource pool from which result is allocated. ** ** Returns: ** pointer to new list (NULL in case of error). */ -char * -removemech(rem, list) +static char * +removemech(rem, list, rpool) char *rem; char *list; + SM_RPOOL_T *rpool; { char *ret; char *needle; @@ -999,13 +1336,13 @@ removemech(rem, list) /* length of string without rem */ len = strlen(list) - strlen(rem); - if (len == 0) + if (len <= 0) { - ret = xalloc(1); /* XXX leaked */ + ret = (char *) sm_rpool_malloc_x(rpool, 1); *ret = '\0'; return ret; } - ret = xalloc(len); /* XXX leaked */ + ret = (char *) sm_rpool_malloc_x(rpool, len); memset(ret, '\0', len); /* copy from start to removed item */ @@ -1025,125 +1362,68 @@ removemech(rem, list) return ret; } /* -** INTERSECT -- create the intersection between two lists -** -** Parameters: -** s1, s2 -- lists of items (separated by single blanks). -** -** Returns: -** the intersection of both lists. -*/ - -char * -intersect(s1, s2) - char *s1, *s2; -{ - char *hr, *h1, *h, *res; - int l1, l2, rl; - - if (s1 == NULL || s2 == NULL) /* NULL string(s) -> NULL result */ - return NULL; - l1 = strlen(s1); - l2 = strlen(s2); - rl = min(l1, l2); - res = (char *)xalloc(rl + 1); - *res = '\0'; - if (rl == 0) /* at least one string empty? */ - return res; - hr = res; - h1 = s1; - h = s1; - - /* walk through s1 */ - while (h != NULL && *h1 != '\0') - { - /* is there something after the current word? */ - if ((h = strchr(h1, ' ')) != NULL) - *h = '\0'; - l1 = strlen(h1); - - /* does the current word appear in s2 ? */ - if (iteminlist(h1, s2, " ") != NULL) - { - /* add a blank if not first item */ - if (hr != res) - *hr++ = ' '; - - /* copy the item */ - memcpy(hr, h1, l1); - - /* advance pointer in result list */ - hr += l1; - *hr = '\0'; - } - if (h != NULL) - { - /* there are more items */ - *h = ' '; - h1 = h + 1; - } - } - return res; -} -/* ** ATTEMPTAUTH -- try to AUTHenticate using one mechanism ** ** Parameters: ** m -- the mailer. ** mci -- the mailer connection structure. ** e -- the envelope (including the sender to specify). -** mechused - filled in with mechanism used +** sai - sasl authinfo ** ** Returns: -** EX_OK/EX_TEMPFAIL +** EX_OK -- authentication was successful. +** EX_NOPERM -- authentication failed. +** EX_IOERR -- authentication dialogue failed (I/O problem?). +** EX_TEMPFAIL -- temporary failure. +** */ -int -attemptauth(m, mci, e, mechused) +static int +attemptauth(m, mci, e, sai) MAILER *m; MCI *mci; ENVELOPE *e; - char **mechused; + SASL_AI_T *sai; { int saslresult, smtpresult; sasl_external_properties_t ssf; sasl_interact_t *client_interact = NULL; char *out; unsigned int outlen; - static char *mechusing; + char *mechusing; sasl_security_properties_t ssp; char in64[MAXOUTLEN]; -# if NETINET +#if NETINET extern SOCKADDR CurHostAddr; -# endif /* NETINET */ +#endif /* NETINET */ - *mechused = NULL; + /* no mechanism selected (yet) */ + (*sai)[SASL_MECH] = NULL; + + /* dispose old connection */ if (mci->mci_conn != NULL) - { sasl_dispose(&(mci->mci_conn)); - /* just in case, sasl_dispose() should take care of it */ - mci->mci_conn = NULL; - } - /* make a new client sasl connection */ saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp" : "smtp", CurHostName, NULL, 0, &mci->mci_conn); + if (saslresult != SASL_OK) + return EX_TEMPFAIL; /* set properties */ (void) memset(&ssp, '\0', sizeof ssp); -# if SFIO + /* XXX should these be options settable via .cf ? */ - /* ssp.min_ssf = 0; is default due to memset() */ +# if STARTTLS +#endif /* STARTTLS */ { - ssp.max_ssf = INT_MAX; + ssp.max_ssf = MaxSLBits; ssp.maxbufsize = MAXOUTLEN; -# if 0 +# if 0 ssp.security_flags = SASL_SEC_NOPLAINTEXT; -# endif /* 0 */ +# endif /* 0 */ } -# endif /* SFIO */ saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp); if (saslresult != SASL_OK) return EX_TEMPFAIL; @@ -1151,19 +1431,19 @@ attemptauth(m, mci, e, mechused) /* external security strength factor, authentication id */ ssf.ssf = 0; ssf.auth_id = NULL; -# if _FFR_EXT_MECH - out = macvalue(macid("{cert_subject}", NULL), e); +#if STARTTLS + out = macvalue(macid("{cert_subject}"), e); if (out != NULL && *out != '\0') ssf.auth_id = out; - out = macvalue(macid("{cipher_bits}", NULL), e); + out = macvalue(macid("{cipher_bits}"), e); if (out != NULL && *out != '\0') ssf.ssf = atoi(out); -# endif /* _FFR_EXT_MECH */ +#endif /* STARTTLS */ saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf); if (saslresult != SASL_OK) return EX_TEMPFAIL; -# if NETINET +#if NETINET /* set local/remote ipv4 addresses */ if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET) { @@ -1175,7 +1455,8 @@ attemptauth(m, mci, e, mechused) != SASL_OK) return EX_TEMPFAIL; addrsize = sizeof(struct sockaddr_in); - if (getsockname(fileno(mci->mci_out), + if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, + NULL), (struct sockaddr *) &saddr_l, &addrsize) == 0) { if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL, @@ -1183,28 +1464,26 @@ attemptauth(m, mci, e, mechused) return EX_TEMPFAIL; } } -# endif /* NETINET */ +#endif /* NETINET */ /* start client side of sasl */ saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap, NULL, &client_interact, &out, &outlen, (const char **)&mechusing); - callbacks[CB_AUTHNAME_IDX].context = mechusing; if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) { -# if SFIO if (saslresult == SASL_NOMECH && LogLevel > 8) { sm_syslog(LOG_NOTICE, e->e_id, - "available AUTH mechanisms do not fulfill requirements"); + "AUTH=client, available mechanisms do not fulfill requirements"); } -# endif /* SFIO */ return EX_TEMPFAIL; } - *mechused = mechusing; + /* just point current mechanism to the data in the sasl library */ + (*sai)[SASL_MECH] = mechusing; /* send the info across the wire */ if (outlen > 0) @@ -1223,30 +1502,30 @@ attemptauth(m, mci, e, mechused) { smtpmessage("AUTH %s", m, mci, mechusing); } + sm_sasl_free(out); /* XXX only if no rpool is used */ /* get the reply */ - smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, getsasldata, NULL); - /* which timeout? XXX */ + smtpresult = reply(m, mci, e, TimeOuts.to_auth, getsasldata, NULL); for (;;) { /* check return code from server */ if (smtpresult == 235) { - define(macid("{auth_type}", NULL), - newstr(mechusing), e); -# if !SFIO - if (LogLevel > 9) - sm_syslog(LOG_INFO, NOQID, - "SASL: outgoing connection to %.64s: mech=%.16s", - mci->mci_host, mechusing); -# endif /* !SFIO */ + macdefine(&mci->mci_macro, A_TEMP, macid("{auth_type}"), + mechusing); return EX_OK; } if (smtpresult == -1) return EX_IOERR; - if (smtpresult != 334) + if (REPLYTYPE(smtpresult) == 5) + return EX_NOPERM; /* ugly, but ... */ + if (REPLYTYPE(smtpresult) != 3) + { + /* should we fail deliberately, see RFC 2554 4. ? */ + /* smtpmessage("*", m, mci); */ return EX_TEMPFAIL; + } saslresult = sasl_client_step(mci->mci_conn, mci->mci_sasl_string, @@ -1257,11 +1536,11 @@ attemptauth(m, mci, e, mechused) if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) { if (tTd(95, 5)) - dprintf("AUTH FAIL: %s (%d)\n", + sm_dprintf("AUTH FAIL=%s (%d)\n", sasl_errstring(saslresult, NULL, NULL), saslresult); - /* fail deliberately, see RFC 2254 4. */ + /* fail deliberately, see RFC 2554 4. */ smtpmessage("*", m, mci); /* @@ -1269,9 +1548,9 @@ attemptauth(m, mci, e, mechused) ** mechanism; how to do that? */ - smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, + smtpresult = reply(m, mci, e, TimeOuts.to_auth, getsasldata, NULL); - return EX_TEMPFAIL; + return EX_NOPERM; } if (outlen > 0) @@ -1287,14 +1566,13 @@ attemptauth(m, mci, e, mechused) } else in64[0] = '\0'; + sm_sasl_free(out); /* XXX only if no rpool is used */ smtpmessage("%s", m, mci, in64); - smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, + smtpresult = reply(m, mci, e, TimeOuts.to_auth, getsasldata, NULL); - /* which timeout? XXX */ } /* NOTREACHED */ } - /* ** SMTPAUTH -- try to AUTHenticate ** @@ -1309,7 +1587,16 @@ attemptauth(m, mci, e, mechused) ** e -- the envelope. ** ** Returns: -** EX_OK/EX_TEMPFAIL +** EX_OK -- authentication was successful +** EX_UNAVAILABLE -- authentication not possible, e.g., +** no data available. +** EX_NOPERM -- authentication failed. +** EX_TEMPFAIL -- temporary failure. +** +** Notice: AuthInfo is used for all connections, hence we must +** return EX_TEMPFAIL only if we really want to retry, i.e., +** iff getauth() tempfailed or getauth() was used and +** authentication tempfailed. */ int @@ -1319,57 +1606,93 @@ smtpauth(m, mci, e) ENVELOPE *e; { int result; - char *mechused; - char *h; - static char *defrealm = NULL; - static char *mechs = NULL; + int i; + bool usedgetauth; - mci->mci_sasl_auth = FALSE; - if (defrealm == NULL) - { - h = readauth(SASL_DEFREALM, SASLInfo, TRUE); - if (h != NULL && *h != '\0') - defrealm = newstr(h); - } - if (defrealm == NULL || *defrealm == '\0') - defrealm = newstr(macvalue('j', CurEnv)); - callbacks[CB_GETREALM_IDX].context = defrealm; + mci->mci_sasl_auth = false; + for (i = 0; i < SASL_MECH ; i++) + mci->mci_sai[i] = NULL; + + result = getauth(mci, e, &(mci->mci_sai)); + if (result == EX_TEMPFAIL) + return result; + usedgetauth = true; -# if _FFR_DEFAUTHINFO_MECHS - if (mechs == NULL) + /* no data available: don't try to authenticate */ + if (result == EX_OK && mci->mci_sai[SASL_AUTHID] == NULL) + return result; + if (result != EX_OK) { - h = readauth(SASL_MECH, SASLInfo, TRUE); - if (h != NULL && *h != '\0') - mechs = newstr(h); + if (SASLInfo == NULL) + return EX_UNAVAILABLE; + + /* read authinfo from file */ + result = readauth(SASLInfo, true, &(mci->mci_sai), + mci->mci_rpool); + if (result != EX_OK) + return result; + usedgetauth = false; } -# endif /* _FFR_DEFAUTHINFO_MECHS */ - if (mechs == NULL || *mechs == '\0') - mechs = AuthMechanisms; - mci->mci_saslcap = intersect(mechs, mci->mci_saslcap); + + /* check whether sufficient data is available */ + if (mci->mci_sai[SASL_PASSWORD] == NULL || + *(mci->mci_sai)[SASL_PASSWORD] == '\0') + return EX_UNAVAILABLE; + if ((mci->mci_sai[SASL_AUTHID] == NULL || + *(mci->mci_sai)[SASL_AUTHID] == '\0') && + (mci->mci_sai[SASL_USER] == NULL || + *(mci->mci_sai)[SASL_USER] == '\0')) + return EX_UNAVAILABLE; + + /* set the context for the callback function to sai */ + callbacks[CB_PASS_IDX].context = (void *)&mci->mci_sai; + callbacks[CB_USER_IDX].context = (void *)&mci->mci_sai; + callbacks[CB_AUTHNAME_IDX].context = (void *)&mci->mci_sai; + callbacks[CB_GETREALM_IDX].context = (void *)&mci->mci_sai; +#if 0 + callbacks[CB_SAFESASL_IDX].context = (void *)&mci->mci_sai; +#endif /* 0 */ + + /* set default value for realm */ + if ((mci->mci_sai)[SASL_DEFREALM] == NULL || + *(mci->mci_sai)[SASL_DEFREALM] == '\0') + (mci->mci_sai)[SASL_DEFREALM] = sm_rpool_strdup_x(e->e_rpool, + macvalue('j', CurEnv)); + + /* set default value for list of mechanism to use */ + if ((mci->mci_sai)[SASL_MECHLIST] == NULL || + *(mci->mci_sai)[SASL_MECHLIST] == '\0') + (mci->mci_sai)[SASL_MECHLIST] = AuthMechanisms; + + /* create list of mechanisms to try */ + mci->mci_saslcap = intersect((mci->mci_sai)[SASL_MECHLIST], + mci->mci_saslcap, mci->mci_rpool); /* initialize sasl client library */ - result = sasl_client_init(callbacks); + result = init_sasl_client(); if (result != SASL_OK) - return EX_TEMPFAIL; + return usedgetauth ? EX_TEMPFAIL : EX_UNAVAILABLE; do { - result = attemptauth(m, mci, e, &mechused); + result = attemptauth(m, mci, e, &(mci->mci_sai)); if (result == EX_OK) - mci->mci_sasl_auth = TRUE; - else if (result == EX_TEMPFAIL) + mci->mci_sasl_auth = true; + else if (result == EX_TEMPFAIL || result == EX_NOPERM) { - mci->mci_saslcap = removemech(mechused, - mci->mci_saslcap); + mci->mci_saslcap = removemech((mci->mci_sai)[SASL_MECH], + mci->mci_saslcap, + mci->mci_rpool); if (mci->mci_saslcap == NULL || *(mci->mci_saslcap) == '\0') - return EX_TEMPFAIL; + return usedgetauth ? result + : EX_UNAVAILABLE; } - else /* all others for now */ - return EX_TEMPFAIL; + else + return result; } while (result != EX_OK); return result; } -# endif /* SASL */ +#endif /* SASL */ /* ** SMTPMAILFROM -- send MAIL command @@ -1389,18 +1712,19 @@ smtpmailfrom(m, mci, e) int r; char *bufp; char *bodytype; + char *enhsc; char buf[MAXNAME + 1]; char optbuf[MAXLINE]; - char *enhsc; if (tTd(18, 2)) - dprintf("smtpmailfrom: CurHost=%s\n", CurHostName); + sm_dprintf("smtpmailfrom: CurHost=%s\n", CurHostName); enhsc = NULL; /* set up appropriate options to include */ if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) { - snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize); + (void) sm_snprintf(optbuf, sizeof optbuf, " SIZE=%ld", + e->e_msgsize); bufp = &optbuf[strlen(optbuf)]; } else @@ -1421,7 +1745,7 @@ smtpmailfrom(m, mci, e) if (bodytype != NULL && SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7) { - snprintf(bufp, SPACELEFT(optbuf, bufp), + (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp), " BODY=%s", bodytype); bufp += strlen(bufp); } @@ -1433,7 +1757,7 @@ smtpmailfrom(m, mci, e) /* EMPTY */ /* just pass it through */ } -# if MIME8TO7 +#if MIME8TO7 else if (bitset(MM_CVTMIME, MimeMode) && !bitset(EF_DONT_MIME, e->e_flags) && (!bitset(MM_PASS8BIT, MimeMode) || @@ -1442,7 +1766,7 @@ smtpmailfrom(m, mci, e) /* must convert from 8bit MIME format to 7bit encoded */ mci->mci_flags |= MCIF_CVT8TO7; } -# endif /* MIME8TO7 */ +#endif /* MIME8TO7 */ else if (!bitset(MM_PASS8BIT, MimeMode)) { /* cannot just send a 8-bit version */ @@ -1458,7 +1782,7 @@ smtpmailfrom(m, mci, e) if (e->e_envid != NULL && SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7) { - snprintf(bufp, SPACELEFT(optbuf, bufp), + (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp), " ENVID=%s", e->e_envid); bufp += strlen(bufp); } @@ -1467,7 +1791,7 @@ smtpmailfrom(m, mci, e) if (bitset(EF_RET_PARAM, e->e_flags) && SPACELEFT(optbuf, bufp) > 9) { - snprintf(bufp, SPACELEFT(optbuf, bufp), + (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp), " RET=%s", bitset(EF_NO_BODY_RETN, e->e_flags) ? "HDRS" : "FULL"); @@ -1477,22 +1801,50 @@ smtpmailfrom(m, mci, e) if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL && SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7 -# if SASL +#if SASL && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth) -# endif /* SASL */ +#endif /* SASL */ ) { - snprintf(bufp, SPACELEFT(optbuf, bufp), + (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp), " AUTH=%s", e->e_auth_param); bufp += strlen(bufp); } /* + ** 17 is the max length required, we could use log() to compute + ** the exact length (and check IS_DLVR_TRACE()) + */ + + if (bitset(MCIF_DLVR_BY, mci->mci_flags) && + IS_DLVR_BY(e) && SPACELEFT(optbuf, bufp) > 17) + { + long dby; + + /* + ** Avoid problems with delays (for R) since the check + ** in deliver() whether min-deliver-time is sufficient. + ** Alternatively we could pass the computed time to this + ** function. + */ + + dby = e->e_deliver_by - (curtime() - e->e_ctime); + if (dby <= 0 && IS_DLVR_RETURN(e)) + dby = mci->mci_min_by <= 0 ? 1 : mci->mci_min_by; + (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp), + " BY=%ld;%c%s", + dby, + IS_DLVR_RETURN(e) ? 'R' : 'N', + IS_DLVR_TRACE(e) ? "T" : ""); + bufp += strlen(bufp); + } + + /* ** Send the MAIL command. ** Designates the sender. */ - mci->mci_state = MCIS_ACTIVE; + mci->mci_state = MCIS_MAIL; if (bitset(EF_RESPONSE, e->e_flags) && !bitnset(M_NO_NULL_FROM, m->m_flags)) @@ -1520,7 +1872,7 @@ smtpmailfrom(m, mci, e) *bufp == '@' ? ',' : ':', bufp, optbuf); } SmtpPhase = mci->mci_phase = "client MAIL"; - sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e), + sm_setproctitle(true, e, "%s %s: %s", qid_printname(e), CurHostName, mci->mci_phase); r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc); if (r < 0) @@ -1611,58 +1963,92 @@ smtpmailfrom(m, mci, e) */ int -smtprcpt(to, m, mci, e) +smtprcpt(to, m, mci, e, ctladdr, xstart) ADDRESS *to; register MAILER *m; MCI *mci; ENVELOPE *e; + ADDRESS *ctladdr; + time_t xstart; { - register int r; char *bufp; char optbuf[MAXLINE]; - char *enhsc; - enhsc = NULL; +#if PIPELINING + /* + ** If there is status waiting from the other end, read it. + ** This should normally happen because of SMTP pipelining. + */ + + while (mci->mci_nextaddr != NULL && + sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL)) + { + int r; + + r = smtprcptstat(mci->mci_nextaddr, m, mci, e); + if (r != EX_OK) + { + markfailure(e, mci->mci_nextaddr, mci, r, false); + giveresponse(r, mci->mci_nextaddr->q_status, m, mci, + ctladdr, xstart, e, to); + } + mci->mci_nextaddr = mci->mci_nextaddr->q_pchain; + } +#endif /* PIPELINING */ + optbuf[0] = '\0'; bufp = optbuf; /* - ** warning: in the following it is assumed that the free space + ** Warning: in the following it is assumed that the free space ** in bufp is sizeof optbuf */ + if (bitset(MCIF_DSN, mci->mci_flags)) { + if (IS_DLVR_NOTIFY(e) && + !bitset(MCIF_DLVR_BY, mci->mci_flags)) + { + /* RFC 2852: 4.1.4.2 */ + if (!bitset(QHASNOTIFY, to->q_flags)) + to->q_flags |= QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY; + else if (bitset(QPINGONSUCCESS, to->q_flags) || + bitset(QPINGONFAILURE, to->q_flags) || + bitset(QPINGONDELAY, to->q_flags)) + to->q_flags |= QPINGONDELAY; + } + /* NOTIFY= parameter */ if (bitset(QHASNOTIFY, to->q_flags) && bitset(QPRIMARY, to->q_flags) && !bitnset(M_LOCALMAILER, m->m_flags)) { - bool firstone = TRUE; + bool firstone = true; - (void) strlcat(bufp, " NOTIFY=", sizeof optbuf); + (void) sm_strlcat(bufp, " NOTIFY=", sizeof optbuf); if (bitset(QPINGONSUCCESS, to->q_flags)) { - (void) strlcat(bufp, "SUCCESS", sizeof optbuf); - firstone = FALSE; + (void) sm_strlcat(bufp, "SUCCESS", sizeof optbuf); + firstone = false; } if (bitset(QPINGONFAILURE, to->q_flags)) { if (!firstone) - (void) strlcat(bufp, ",", + (void) sm_strlcat(bufp, ",", sizeof optbuf); - (void) strlcat(bufp, "FAILURE", sizeof optbuf); - firstone = FALSE; + (void) sm_strlcat(bufp, "FAILURE", sizeof optbuf); + firstone = false; } if (bitset(QPINGONDELAY, to->q_flags)) { if (!firstone) - (void) strlcat(bufp, ",", + (void) sm_strlcat(bufp, ",", sizeof optbuf); - (void) strlcat(bufp, "DELAY", sizeof optbuf); - firstone = FALSE; + (void) sm_strlcat(bufp, "DELAY", sizeof optbuf); + firstone = false; } if (firstone) - (void) strlcat(bufp, "NEVER", sizeof optbuf); + (void) sm_strlcat(bufp, "NEVER", sizeof optbuf); bufp += strlen(bufp); } @@ -1670,39 +2056,98 @@ smtprcpt(to, m, mci, e) if (to->q_orcpt != NULL && SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7) { - snprintf(bufp, SPACELEFT(optbuf, bufp), + (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp), " ORCPT=%s", to->q_orcpt); bufp += strlen(bufp); } } smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf); + mci->mci_state = MCIS_RCPT; SmtpPhase = mci->mci_phase = "client RCPT"; - sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e), + sm_setproctitle(true, e, "%s %s: %s", qid_printname(e), CurHostName, mci->mci_phase); + +#if PIPELINING + /* + ** If running SMTP pipelining, we will pick up status later + */ + + if (bitset(MCIF_PIPELINED, mci->mci_flags)) + return EX_OK; +#endif /* PIPELINING */ + + return smtprcptstat(to, m, mci, e); +} +/* +** SMTPRCPTSTAT -- get recipient status +** +** This is only called during SMTP pipelining +** +** Parameters: +** to -- address of recipient. +** m -- mailer being sent to. +** mci -- the mailer connection information. +** e -- the envelope for this message. +** +** Returns: +** EX_* -- protocol status +*/ + +static int +smtprcptstat(to, m, mci, e) + ADDRESS *to; + MAILER *m; + register MCI *mci; + register ENVELOPE *e; +{ + int r; + char *enhsc; + + enhsc = NULL; r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc); - to->q_rstatus = newstr(SmtpReplyBuffer); - to->q_status = ENHSCN(enhsc, smtptodsn(r)); + to->q_rstatus = sm_rpool_strdup_x(e->e_rpool, SmtpReplyBuffer); + to->q_status = ENHSCN_RPOOL(enhsc, smtptodsn(r), e->e_rpool); if (!bitnset(M_LMTP, m->m_flags)) to->q_statmta = mci->mci_host; if (r < 0 || REPLYTYPE(r) == 4) + { + mci->mci_retryrcpt = true; return EX_TEMPFAIL; + } else if (REPLYTYPE(r) == 2) + { + char *t; + + if ((t = mci->mci_tolist) != NULL) + { + char *p; + + *t++ = ','; + for (p = to->q_paddr; *p != '\0'; *t++ = *p++) + continue; + *t = '\0'; + mci->mci_tolist = t; + } +#if PIPELINING + mci->mci_okrcpts++; +#endif /* PIPELINING */ return EX_OK; + } else if (r == 550) { - to->q_status = ENHSCN(enhsc, "5.1.1"); + to->q_status = ENHSCN_RPOOL(enhsc, "5.1.1", e->e_rpool); return EX_NOUSER; } else if (r == 551) { - to->q_status = ENHSCN(enhsc, "5.1.6"); + to->q_status = ENHSCN_RPOOL(enhsc, "5.1.6", e->e_rpool); return EX_NOUSER; } else if (r == 553) { - to->q_status = ENHSCN(enhsc, "5.1.3"); + to->q_status = ENHSCN_RPOOL(enhsc, "5.1.3", e->e_rpool); return EX_NOUSER; } else if (REPLYTYPE(r) == 5) @@ -1732,19 +2177,18 @@ smtprcpt(to, m, mci, e) ** ** Returns: ** exit status corresponding to DATA command. -** -** Side Effects: -** none. */ static jmp_buf CtxDataTimeout; -static EVENT *volatile DataTimeout = NULL; +static SM_EVENT *volatile DataTimeout = NULL; int -smtpdata(m, mci, e) +smtpdata(m, mci, e, ctladdr, xstart) MAILER *m; register MCI *mci; register ENVELOPE *e; + ADDRESS *ctladdr; + time_t xstart; { register int r; int rstat; @@ -1757,15 +2201,46 @@ smtpdata(m, mci, e) /* ** Send the data. ** First send the command and check that it is ok. - ** Then send the data. + ** Then send the data (if there are valid recipients). ** Follow it up with a dot to terminate. ** Finally get the results of the transaction. */ /* send the command and check ok to proceed */ smtpmessage("DATA", m, mci); + +#if PIPELINING + if (mci->mci_nextaddr != NULL) + { + char *oldto = e->e_to; + + /* pick up any pending RCPT responses for SMTP pipelining */ + while (mci->mci_nextaddr != NULL) + { + int r; + + e->e_to = mci->mci_nextaddr->q_paddr; + r = smtprcptstat(mci->mci_nextaddr, m, mci, e); + if (r != EX_OK) + { + markfailure(e, mci->mci_nextaddr, mci, r, + false); + giveresponse(r, mci->mci_nextaddr->q_status, m, + mci, ctladdr, xstart, e, + mci->mci_nextaddr); + if (r == EX_TEMPFAIL) + mci->mci_nextaddr->q_state = QS_RETRY; + } + mci->mci_nextaddr = mci->mci_nextaddr->q_pchain; + } + e->e_to = oldto; + } +#endif /* PIPELINING */ + + /* now proceed with DATA phase */ SmtpPhase = mci->mci_phase = "client DATA 354"; - sm_setproctitle(TRUE, e, "%s %s: %s", + mci->mci_state = MCIS_DATA; + sm_setproctitle(true, e, "%s %s: %s", qid_printname(e), CurHostName, mci->mci_phase); r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc); if (r < 0 || REPLYTYPE(r) == 4) @@ -1776,6 +2251,11 @@ smtpdata(m, mci, e) else if (REPLYTYPE(r) == 5) { smtprset(m, mci, e); +#if PIPELINING + if (mci->mci_okrcpts <= 0) + return mci->mci_retryrcpt ? EX_TEMPFAIL + : EX_UNAVAILABLE; +#endif /* PIPELINING */ return EX_UNAVAILABLE; } else if (REPLYTYPE(r) != 3) @@ -1790,9 +2270,19 @@ smtpdata(m, mci, e) smtprset(m, mci, e); mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"), SmtpReplyBuffer); +#if PIPELINING + if (mci->mci_okrcpts <= 0) + return mci->mci_retryrcpt ? EX_TEMPFAIL + : EX_PROTOCOL; +#endif /* PIPELINING */ return EX_PROTOCOL; } +#if PIPELINING + if (mci->mci_okrcpts > 0) + { +#endif /* PIPELINING */ + /* ** Set timeout around data writes. Make it at least large ** enough for DNS timeouts on all recipients plus some fudge @@ -1828,7 +2318,7 @@ smtpdata(m, mci, e) else timeout = DATA_PROGRESS_TIMEOUT; - DataTimeout = setevent(timeout, datatimeout, 0); + DataTimeout = sm_setevent(timeout, datatimeout, 0); /* @@ -1850,42 +2340,36 @@ smtpdata(m, mci, e) */ if (DataTimeout != NULL) - clrevent(DataTimeout); + sm_clrevent(DataTimeout); -# if _FFR_CATCH_BROKEN_MTAS - { - fd_set readfds; - struct timeval timeout; +#if PIPELINING + } +#endif /* PIPELINING */ - FD_ZERO(&readfds); - FD_SET(fileno(mci->mci_in), &readfds); - timeout.tv_sec = 0; - timeout.tv_usec = 0; - if (select(fileno(mci->mci_in) + 1, FDSET_CAST &readfds, - NULL, NULL, &timeout) > 0 && - FD_ISSET(fileno(mci->mci_in), &readfds)) - { - /* terminate the message */ - fprintf(mci->mci_out, ".%s", m->m_eol); - if (TrafficLogFile != NULL) - fprintf(TrafficLogFile, "%05d >>> .\n", - (int) getpid()); - if (Verbose) - nmessage(">>> ."); +#if _FFR_CATCH_BROKEN_MTAS + if (sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL)) + { + /* terminate the message */ + (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s", + m->m_eol); + if (TrafficLogFile != NULL) + (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, + "%05d >>> .\n", (int) CurrentPid); + if (Verbose) + nmessage(">>> ."); - sm_syslog(LOG_CRIT, e->e_id, - "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot", - CurHostName); - mci->mci_errno = EIO; - mci->mci_state = MCIS_ERROR; - mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL); - smtpquit(m, mci, e); - return EX_PROTOCOL; - } + sm_syslog(LOG_CRIT, e->e_id, + "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot", + CurHostName); + mci->mci_errno = EIO; + mci->mci_state = MCIS_ERROR; + mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL); + smtpquit(m, mci, e); + return EX_PROTOCOL; } -# endif /* _FFR_CATCH_BROKEN_MTAS */ +#endif /* _FFR_CATCH_BROKEN_MTAS */ - if (ferror(mci->mci_out)) + if (sm_io_error(mci->mci_out)) { /* error during processing -- don't send the dot */ mci->mci_errno = EIO; @@ -1896,15 +2380,16 @@ smtpdata(m, mci, e) } /* terminate the message */ - fprintf(mci->mci_out, ".%s", m->m_eol); + (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s", m->m_eol); if (TrafficLogFile != NULL) - fprintf(TrafficLogFile, "%05d >>> .\n", (int) getpid()); + (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, + "%05d >>> .\n", (int) CurrentPid); if (Verbose) nmessage(">>> ."); /* check for the results of the transaction */ SmtpPhase = mci->mci_phase = "client DATA status"; - sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e), + sm_setproctitle(true, e, "%s %s: %s", qid_printname(e), CurHostName, mci->mci_phase); if (bitnset(M_LMTP, m->m_flags)) return EX_OK; @@ -1920,26 +2405,24 @@ smtpdata(m, mci, e) rstat = EX_TEMPFAIL; else if (REPLYTYPE(r) == 4) rstat = xstat = EX_TEMPFAIL; - else if (REPLYCLASS(r) != 5) - rstat = xstat = EX_PROTOCOL; else if (REPLYTYPE(r) == 2) rstat = xstat = EX_OK; + else if (REPLYCLASS(r) != 5) + rstat = xstat = EX_PROTOCOL; else if (REPLYTYPE(r) == 5) rstat = EX_UNAVAILABLE; else rstat = EX_PROTOCOL; mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)), SmtpReplyBuffer); - if (e->e_statmsg != NULL) - sm_free(e->e_statmsg); if (bitset(MCIF_ENHSTAT, mci->mci_flags) && (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0) r += 5; else r = 4; - e->e_statmsg = newstr(&SmtpReplyBuffer[r]); + e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[r]); SmtpPhase = mci->mci_phase = "idle"; - sm_setproctitle(TRUE, e, "%s: %s", CurHostName, mci->mci_phase); + sm_setproctitle(true, e, "%s: %s", CurHostName, mci->mci_phase); if (rstat != EX_PROTOCOL) return rstat; if (LogLevel > 1) @@ -1952,7 +2435,6 @@ smtpdata(m, mci, e) return rstat; } - static void datatimeout() { @@ -1978,8 +2460,8 @@ datatimeout() timeout = DATA_PROGRESS_TIMEOUT; /* reset the timeout */ - DataTimeout = sigsafe_setevent(timeout, datatimeout, 0); - DataProgress = FALSE; + DataTimeout = sm_sigsafe_setevent(timeout, datatimeout, 0); + DataProgress = false; } else { @@ -1993,7 +2475,6 @@ datatimeout() errno = ETIMEDOUT; longjmp(CtxDataTimeout, 1); } - errno = save_errno; } /* @@ -2015,10 +2496,11 @@ smtpgetstat(m, mci, e) ENVELOPE *e; { int r; - int status; + int status, xstat; char *enhsc; enhsc = NULL; + /* check for the results of the transaction */ r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc); if (r < 0) @@ -2026,25 +2508,24 @@ smtpgetstat(m, mci, e) smtpquit(m, mci, e); return EX_TEMPFAIL; } + xstat = EX_NOTSTICKY; if (REPLYTYPE(r) == 4) status = EX_TEMPFAIL; - else if (REPLYCLASS(r) != 5) - status = EX_PROTOCOL; else if (REPLYTYPE(r) == 2) - status = EX_OK; + status = xstat = EX_OK; + else if (REPLYCLASS(r) != 5) + status = xstat = EX_PROTOCOL; else if (REPLYTYPE(r) == 5) status = EX_UNAVAILABLE; else status = EX_PROTOCOL; - if (e->e_statmsg != NULL) - sm_free(e->e_statmsg); if (bitset(MCIF_ENHSTAT, mci->mci_flags) && (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0) r += 5; else r = 4; - e->e_statmsg = newstr(&SmtpReplyBuffer[r]); - mci_setstat(mci, status, ENHSCN(enhsc, smtptodsn(r)), + e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[r]); + mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)), SmtpReplyBuffer); if (LogLevel > 1 && status == EX_PROTOCOL) { @@ -2078,11 +2559,17 @@ smtpquit(m, mci, e) { bool oldSuprErrs = SuprErrs; int rcode; + char *oldcurhost; + oldcurhost = CurHostName; CurHostName = mci->mci_host; /* XXX UGLY XXX */ if (CurHostName == NULL) CurHostName = MyHostName; +#if PIPELINING + mci->mci_okrcpts = 0; +#endif /* PIPELINING */ + /* ** Suppress errors here -- we may be processing a different ** job when we do the quit connection, and we don't want the @@ -2090,7 +2577,7 @@ smtpquit(m, mci, e) ** problem. */ - SuprErrs = TRUE; + SuprErrs = true; /* send the quit message if we haven't gotten I/O error */ if (mci->mci_state != MCIS_ERROR && @@ -2105,7 +2592,7 @@ smtpquit(m, mci, e) SuprErrs = oldSuprErrs; if (mci->mci_state == MCIS_CLOSED || origstate == MCIS_CLOSED) - return; + goto end; } /* now actually close the connection and pick up the zombie */ @@ -2127,6 +2614,10 @@ smtpquit(m, mci, e) } SuprErrs = oldSuprErrs; + + end: + CurHostName = oldcurhost; + return; } /* ** SMTPRSET -- send a RSET (reset) command @@ -2155,6 +2646,10 @@ smtprset(m, mci, e) if (CurHostName == NULL) CurHostName = MyHostName; +#if PIPELINING + mci->mci_okrcpts = 0; +#endif /* PIPELINING */ + SmtpPhase = "client RSET"; smtpmessage("RSET", m, mci); r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL); @@ -2221,6 +2716,7 @@ smtpprobe(mci) ** timeout -- the timeout for reads. ** pfunc -- processing function called on each line of response. ** If null, no special processing is done. +** enhstat -- optional, returns enhanced error code string (if set) ** ** Returns: ** reply code it reads. @@ -2240,16 +2736,25 @@ reply(m, mci, e, timeout, pfunc, enhstat) { register char *bufp; register int r; - bool firstline = TRUE; + bool firstline = true; char junkbuf[MAXLINE]; static char enhstatcode[ENHSCLEN]; int save_errno; + /* + ** Flush the output before reading response. + ** + ** For SMTP pipelining, it would be better if we didn't do + ** this if there was already data waiting to be read. But + ** to do it properly means pushing it to the I/O library, + ** since it really needs to be done below the buffer layer. + */ + if (mci->mci_out != NULL) - (void) fflush(mci->mci_out); + (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT); if (tTd(18, 1)) - dprintf("reply\n"); + sm_dprintf("reply\n"); /* ** Read the input line, being careful not to hang. @@ -2261,18 +2766,19 @@ reply(m, mci, e, timeout, pfunc, enhstat) register char *p; /* actually do the read */ - if (e->e_xfp != NULL) - (void) fflush(e->e_xfp); /* for debugging */ + if (e->e_xfp != NULL) /* for debugging */ + (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); /* if we are in the process of closing just give the code */ if (mci->mci_state == MCIS_CLOSED) return SMTPCLOSING; if (mci->mci_out != NULL) - (void) fflush(mci->mci_out); + (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT); /* get the line from the other side */ p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); + save_errno = errno; mci->mci_lastuse = curtime(); if (p == NULL) @@ -2281,18 +2787,19 @@ reply(m, mci, e, timeout, pfunc, enhstat) extern char MsgBuf[]; /* if the remote end closed early, fake an error */ + errno = save_errno; if (errno == 0) -# ifdef ECONNRESET +#ifdef ECONNRESET errno = ECONNRESET; -# else /* ECONNRESET */ +#else /* ECONNRESET */ errno = EPIPE; -# endif /* ECONNRESET */ +#endif /* ECONNRESET */ mci->mci_errno = errno; oldholderrs = HoldErrs; - HoldErrs = TRUE; + HoldErrs = true; usrerr("451 4.4.1 reply: read error from %s", - CurHostName == NULL ? "NO_HOST" : CurHostName); + CURHOSTNAME); /* errors on QUIT should not be persistent */ if (strncmp(SmtpMsgBuffer, "QUIT", 4) != 0) @@ -2302,35 +2809,31 @@ reply(m, mci, e, timeout, pfunc, enhstat) if (tTd(18, 100)) (void) pause(); mci->mci_state = MCIS_ERROR; - save_errno = errno; smtpquit(m, mci, e); -# if XDEBUG +#if XDEBUG { char wbuf[MAXLINE]; - int wbufleft = sizeof wbuf; p = wbuf; if (e->e_to != NULL) { - int plen; - - snprintf(p, wbufleft, "%s... ", - shortenstring(e->e_to, MAXSHORTSTR)); - plen = strlen(p); - p += plen; - wbufleft -= plen; + (void) sm_snprintf(p, + SPACELEFT(wbuf, p), + "%s... ", + shortenstring(e->e_to, MAXSHORTSTR)); + p += strlen(p); } - snprintf(p, wbufleft, "reply(%.100s) during %s", - CurHostName == NULL ? "NO_HOST" : CurHostName, - SmtpPhase); + (void) sm_snprintf(p, SPACELEFT(wbuf, p), + "reply(%.100s) during %s", + CURHOSTNAME, SmtpPhase); checkfd012(wbuf); } -# endif /* XDEBUG */ - errno = save_errno; +#endif /* XDEBUG */ HoldErrs = oldholderrs; + errno = save_errno; return -1; } - fixcrlf(bufp, TRUE); + fixcrlf(bufp, true); /* EHLO failure is not a real error */ if (e->e_xfp != NULL && (bufp[0] == '4' || @@ -2340,17 +2843,20 @@ reply(m, mci, e, timeout, pfunc, enhstat) if (SmtpNeedIntro) { /* inform user who we are chatting with */ - fprintf(CurEnv->e_xfp, - "... while talking to %s:\n", - CurHostName == NULL ? "NO_HOST" : CurHostName); - SmtpNeedIntro = FALSE; + (void) sm_io_fprintf(CurEnv->e_xfp, + SM_TIME_DEFAULT, + "... while talking to %s:\n", + CURHOSTNAME); + SmtpNeedIntro = false; } if (SmtpMsgBuffer[0] != '\0') - fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + ">>> %s\n", SmtpMsgBuffer); SmtpMsgBuffer[0] = '\0'; /* now log the message as from the other side */ - fprintf(e->e_xfp, "<<< %s\n", bufp); + (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, + "<<< %s\n", bufp); } /* display the input for verbose mode */ @@ -2370,7 +2876,7 @@ reply(m, mci, e, timeout, pfunc, enhstat) if (pfunc != NULL) (*pfunc)(bufp, firstline, m, mci, e); - firstline = FALSE; + firstline = false; /* decode the reply code */ r = atoi(bufp); @@ -2393,9 +2899,8 @@ reply(m, mci, e, timeout, pfunc, enhstat) */ /* save temporary failure messages for posterity */ - if (SmtpReplyBuffer[0] == '4' && - (bitnset(M_LMTP, m->m_flags) || SmtpError[0] == '\0')) - snprintf(SmtpError, sizeof SmtpError, "%s", SmtpReplyBuffer); + if (SmtpReplyBuffer[0] == '4') + (void) sm_strlcpy(SmtpError, SmtpReplyBuffer, sizeof SmtpError); /* reply code 421 is "Service Shutting Down" */ if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) @@ -2424,36 +2929,36 @@ reply(m, mci, e, timeout, pfunc, enhstat) /*VARARGS1*/ void -# ifdef __STDC__ +#ifdef __STDC__ smtpmessage(char *f, MAILER *m, MCI *mci, ...) -# else /* __STDC__ */ +#else /* __STDC__ */ smtpmessage(f, m, mci, va_alist) char *f; MAILER *m; MCI *mci; va_dcl -# endif /* __STDC__ */ +#endif /* __STDC__ */ { - VA_LOCAL_DECL + SM_VA_LOCAL_DECL - VA_START(mci); - (void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap); - VA_END; + SM_VA_START(ap, mci); + (void) sm_vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap); + SM_VA_END(ap); if (tTd(18, 1) || Verbose) nmessage(">>> %s", SmtpMsgBuffer); if (TrafficLogFile != NULL) - fprintf(TrafficLogFile, "%05d >>> %s\n", - (int) getpid(), SmtpMsgBuffer); + (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, + "%05d >>> %s\n", (int) CurrentPid, + SmtpMsgBuffer); if (mci->mci_out != NULL) { - fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, - m == NULL ? "\r\n" : m->m_eol); + (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s%s", + SmtpMsgBuffer, m == NULL ? "\r\n" + : m->m_eol); } else if (tTd(18, 1)) { - dprintf("smtpmessage: NULL mci_out\n"); + sm_dprintf("smtpmessage: NULL mci_out\n"); } } - -#endif /* SMTP */ diff --git a/gnu/usr.sbin/sendmail/sendmail/util.c b/gnu/usr.sbin/sendmail/sendmail/util.c index fa4814961a2..6bdacc2fe50 100644 --- a/gnu/usr.sbin/sendmail/sendmail/util.c +++ b/gnu/usr.sbin/sendmail/sendmail/util.c @@ -11,73 +11,30 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: util.c,v 8.225.2.1.2.26 2001/06/01 08:23:25 gshapiro Exp $"; -#endif /* ! lint */ - #include <sendmail.h> -#include <sysexits.h> +SM_RCSID("@(#)$Sendmail: util.c,v 8.347 2001/09/04 22:43:06 ca Exp $") -static void readtimeout __P((time_t)); +#include <sysexits.h> +#include <sm/xtrap.h> /* -** STRIPQUOTES -- Strip quotes & quote bits from a string. -** -** Runs through a string and strips off unquoted quote -** characters and quote bits. This is done in place. -** -** Parameters: -** s -- the string to strip. -** -** Returns: -** none. -** -** Side Effects: -** none. -*/ - -void -stripquotes(s) - char *s; -{ - register char *p; - register char *q; - register char c; - - if (s == NULL) - return; - - p = q = s; - do - { - c = *p++; - if (c == '\\') - c = *p++; - else if (c == '"') - continue; - *q++ = c; - } while (c != '\0'); -} -/* ** ADDQUOTES -- Adds quotes & quote bits to a string. ** -** Runs through a string and adds characters and quote bits. +** Runs through a string and adds backslashes and quote bits. ** ** Parameters: ** s -- the string to modify. +** rpool -- resource pool from which to allocate result ** ** Returns: ** pointer to quoted string. -** -** Side Effects: -** none. -** */ char * -addquotes(s) +addquotes(s, rpool) char *s; + SM_RPOOL_T *rpool; { int len = 0; char c; @@ -94,7 +51,7 @@ addquotes(s) len++; } - q = r = xalloc(len + 3); + q = r = sm_rpool_malloc_x(rpool, len + 3); p = s; /* add leading quote */ @@ -121,24 +78,19 @@ addquotes(s) ** s -- the string to modify. ** ** Returns: -** TRUE -- if the string is RFC822 compliant. -** FALSE -- if the string is not RFC822 compliant. -** -** Side Effects: -** none. -** +** true iff the string is RFC822 compliant, false otherwise. */ bool rfc822_string(s) char *s; { - bool quoted = FALSE; + bool quoted = false; int commentlev = 0; char *c = s; if (s == NULL) - return FALSE; + return false; while (*c != '\0') { @@ -147,7 +99,7 @@ rfc822_string(s) { c++; if (*c == '\0') - return FALSE; + return false; } else if (commentlev == 0 && *c == '"') quoted = !quoted; @@ -157,7 +109,7 @@ rfc822_string(s) { /* unbalanced ')' */ if (commentlev == 0) - return FALSE; + return false; else commentlev--; } @@ -165,15 +117,13 @@ rfc822_string(s) commentlev++; else if (commentlev == 0 && strchr(MustQuoteChars, *c) != NULL) - return FALSE; + return false; } c++; } + /* unbalanced '"' or '(' */ - if (quoted || commentlev != 0) - return FALSE; - else - return TRUE; + return !quoted && commentlev == 0; } /* ** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string @@ -186,7 +136,7 @@ rfc822_string(s) ** length -- the maximum size, 0 if no maximum ** ** Returns: -** TRUE if string is changed, FALSE otherwise +** true if string is changed, false otherwise ** ** Side Effects: ** Changes string in place, possibly resulting @@ -198,9 +148,9 @@ shorten_rfc822_string(string, length) char *string; size_t length; { - bool backslash = FALSE; - bool modified = FALSE; - bool quoted = FALSE; + bool backslash = false; + bool modified = false; + bool quoted = false; size_t slen; int parencount = 0; char *ptr = string; @@ -218,12 +168,12 @@ shorten_rfc822_string(string, length) { if (backslash) { - backslash = FALSE; + backslash = false; goto increment; } if (*ptr == '\\') - backslash = TRUE; + backslash = true; else if (*ptr == '(') { if (!quoted) @@ -247,11 +197,11 @@ increment: { /* Not enough, backtrack */ if (*ptr == '\\') - backslash = FALSE; + backslash = false; else if (*ptr == '(' && !quoted) parencount--; else if (*ptr == '"' && parencount == 0) - quoted = FALSE; + quoted = false; break; } ptr++; @@ -262,7 +212,7 @@ increment: { if (*ptr != ')') { - modified = TRUE; + modified = true; *ptr = ')'; } ptr++; @@ -271,14 +221,14 @@ increment: { if (*ptr != '"') { - modified = TRUE; + modified = true; *ptr = '"'; } ptr++; } if (*ptr != '\0') { - modified = TRUE; + modified = true; *ptr = '\0'; } return modified; @@ -304,15 +254,15 @@ find_character(string, character) char *string; int character; { - bool backslash = FALSE; - bool quoted = FALSE; + bool backslash = false; + bool quoted = false; int parencount = 0; while (string != NULL && *string != '\0') { if (backslash) { - backslash = FALSE; + backslash = false; if (!quoted && character == '\\' && *string == '\\') break; string++; @@ -321,7 +271,7 @@ find_character(string, character) switch (*string) { case '\\': - backslash = TRUE; + backslash = true; break; case '(': @@ -352,170 +302,116 @@ find_character(string, character) /* Return pointer to the character */ return string; } +#if _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI /* -** XALLOC -- Allocate memory and bitch wildly on failure. -** -** THIS IS A CLUDGE. This should be made to give a proper -** error -- but after all, what can we do? +** TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..." ** ** Parameters: -** sz -- size of area to allocate. +** str -- string to truncate +** len -- maximum length (including '\0') (0 for unlimited) +** delim -- delimiter character ** ** Returns: -** pointer to data region. -** -** Side Effects: -** Memory is allocated. +** None. */ -char * -xalloc(sz) - register int sz; +void +truncate_at_delim(str, len, delim) + char *str; + size_t len; + int delim; { - register char *p; + char *p; - /* some systems can't handle size zero mallocs */ - if (sz <= 0) - sz = 1; + if (str == NULL || len == 0 || strlen(str) < len) + return; - ENTER_CRITICAL(); - p = malloc((unsigned) sz); - LEAVE_CRITICAL(); - if (p == NULL) + *(str + len - 1) = '\0'; + while ((p = strrchr(str, delim)) != NULL) { - syserr("!Out of memory!!"); - - /* NOTREACHED */ - exit(EX_UNAVAILABLE); + *p = '\0'; + if (p - str + 4 < len) + { + *p++ = ':'; + *p = '\0'; + (void) sm_strlcat(str, "...", len); + return; + } } - return p; -} -/* -** XREALLOC -- Reallocate memory and bitch wildly on failure. -** -** THIS IS A CLUDGE. This should be made to give a proper -** error -- but after all, what can we do? -** -** Parameters: -** ptr -- original area. -** sz -- size of new area to allocate. -** -** Returns: -** pointer to data region. -** -** Side Effects: -** Memory is allocated. -*/ - -char * -xrealloc(ptr, sz) - void *ptr; - size_t sz; -{ - register char *p; - /* some systems can't handle size zero mallocs */ - if (sz <= 0) - sz = 1; - - ENTER_CRITICAL(); - p = realloc(ptr, (unsigned) sz); - LEAVE_CRITICAL(); - if (p == NULL) - { - syserr("!Out of memory!!"); - - /* NOTREACHED */ - exit(EX_UNAVAILABLE); - } - return p; + /* Couldn't find a place to append "..." */ + if (len > 3) + (void) sm_strlcpy(str, "...", len); + else + str[0] = '\0'; } +#endif /* _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI */ /* -** XCALLOC -- Allocate memory and bitch wildly on failure. -** -** THIS IS A CLUDGE. This should be made to give a proper -** error -- but after all, what can we do? +** XALLOC -- Allocate memory, raise an exception on error ** ** Parameters: -** num -- number of items to allocate -** sz -- size of new area to allocate. +** sz -- size of area to allocate. ** ** Returns: ** pointer to data region. ** +** Exceptions: +** SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory +** ** Side Effects: ** Memory is allocated. */ char * -xcalloc(num, sz) - size_t num; - size_t sz; +#if SM_HEAP_CHECK +xalloc_tagged(sz, file, line) + register int sz; + char *file; + int line; +#else /* SM_HEAP_CHECK */ +xalloc(sz) + register int sz; +#endif /* SM_HEAP_CHECK */ { register char *p; /* some systems can't handle size zero mallocs */ - if (num <= 0) - num = 1; if (sz <= 0) sz = 1; - ENTER_CRITICAL(); - p = calloc((unsigned) num, (unsigned) sz); - LEAVE_CRITICAL(); + /* scaffolding for testing error handling code */ + sm_xtrap_raise_x(&SmHeapOutOfMemory); + + p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group()); if (p == NULL) { - syserr("!Out of memory!!"); - - /* NOTREACHED */ - exit(EX_UNAVAILABLE); + sm_exc_raise_x(&SmHeapOutOfMemory); } return p; } /* -** SM_FREE -- Free memory safely. -** -** Parameters: -** ptr -- area to free -** -** Returns: -** none. -** -** Side Effects: -** Memory is freed. -*/ - -void -sm_free(ptr) - void *ptr; -{ - ENTER_CRITICAL(); - free(ptr); - LEAVE_CRITICAL(); -} -/* ** COPYPLIST -- copy list of pointers. ** -** This routine is the equivalent of newstr for lists of +** This routine is the equivalent of strdup for lists of ** pointers. ** ** Parameters: ** list -- list of pointers to copy. ** Must be NULL terminated. -** copycont -- if TRUE, copy the contents of the vector +** copycont -- if true, copy the contents of the vector ** (which must be a string) also. +** rpool -- resource pool from which to allocate storage, +** or NULL ** ** Returns: ** a copy of 'list'. -** -** Side Effects: -** none. */ char ** -copyplist(list, copycont) +copyplist(list, copycont, rpool) char **list; bool copycont; + SM_RPOOL_T *rpool; { register char **vp; register char **newvp; @@ -525,13 +421,13 @@ copyplist(list, copycont) vp++; - newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); + newvp = (char **) sm_rpool_malloc_x(rpool, (vp - list) * sizeof *vp); memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof *vp); if (copycont) { for (vp = newvp; *vp != NULL; vp++) - *vp = newstr(*vp); + *vp = sm_rpool_strdup_x(rpool, *vp); } return newvp; @@ -539,22 +435,21 @@ copyplist(list, copycont) /* ** COPYQUEUE -- copy address queue. ** -** This routine is the equivalent of newstr for address queues +** This routine is the equivalent of strdup for address queues; ** addresses marked as QS_IS_DEAD() aren't copied ** ** Parameters: ** addr -- list of address structures to copy. +** rpool -- resource pool from which to allocate storage ** ** Returns: ** a copy of 'addr'. -** -** Side Effects: -** none. */ ADDRESS * -copyqueue(addr) +copyqueue(addr, rpool) ADDRESS *addr; + SM_RPOOL_T *rpool; { register ADDRESS *newaddr; ADDRESS *ret; @@ -564,7 +459,8 @@ copyqueue(addr) { if (!QS_IS_DEAD(addr->q_state)) { - newaddr = (ADDRESS *) xalloc(sizeof *newaddr); + newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool, + sizeof *newaddr); STRUCTCOPY(*addr, *newaddr); *tail = newaddr; tail = &newaddr->q_next; @@ -585,7 +481,7 @@ copyqueue(addr) ** none. ** ** Side Effects: -** writes pidfile. +** writes pidfile, logs command line. */ void @@ -593,8 +489,9 @@ log_sendmail_pid(e) ENVELOPE *e; { long sff; - FILE *pidf; + SM_FILE_T *pidf; char pidpath[MAXPATHLEN + 1]; + extern char *CommandLineArgs; /* write the pid to the log file for posterity */ sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT; @@ -605,24 +502,27 @@ log_sendmail_pid(e) if (pidf == NULL) { sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s", - pidpath, errstring(errno)); + pidpath, sm_errstring(errno)); } else { pid_t pid; - extern char *CommandLineArgs; pid = getpid(); /* write the process id on line 1 */ - fprintf(pidf, "%ld\n", (long) pid); + (void) sm_io_fprintf(pidf, SM_TIME_DEFAULT, "%ld\n", + (long) pid); /* line 2 contains all command line flags */ - fprintf(pidf, "%s\n", CommandLineArgs); + (void) sm_io_fprintf(pidf, SM_TIME_DEFAULT, "%s\n", + CommandLineArgs); /* flush and close */ - (void) fclose(pidf); + (void) sm_io_close(pidf, SM_TIME_DEFAULT); } + if (LogLevel > 9) + sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs); } /* ** SET_DELIVERY_MODE -- set and record the delivery mode @@ -635,7 +535,7 @@ log_sendmail_pid(e) ** none. ** ** Side Effects: -** sets $&{deliveryMode} macro +** sets {deliveryMode} macro */ void @@ -645,56 +545,63 @@ set_delivery_mode(mode, e) { char buf[2]; - e->e_sendmode = (char)mode; - buf[0] = (char)mode; + e->e_sendmode = (char) mode; + buf[0] = (char) mode; buf[1] = '\0'; - define(macid("{deliveryMode}", NULL), newstr(buf), e); + macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf); } /* -** PRINTAV -- print argument vector. +** SET_OP_MODE -- set and record the op mode ** ** Parameters: -** av -- argument vector. +** mode -- op mode +** e -- the current envelope. ** ** Returns: ** none. ** ** Side Effects: -** prints av. +** sets {opMode} macro */ void -printav(av) - register char **av; +set_op_mode(mode) + int mode; { - while (*av != NULL) - { - if (tTd(0, 44)) - dprintf("\n\t%08lx=", (u_long) *av); - else - (void) putchar(' '); - xputs(*av++); - } - (void) putchar('\n'); + char buf[2]; + extern ENVELOPE BlankEnvelope; + + OpMode = (char) mode; + buf[0] = (char) mode; + buf[1] = '\0'; + macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf); } /* -** LOWER -- turn letter into lower case. +** PRINTAV -- print argument vector. ** ** Parameters: -** c -- character to turn into lower case. +** av -- argument vector. ** ** Returns: -** c, in lower case. +** none. ** ** Side Effects: -** none. +** prints av. */ -char -lower(c) - register int c; +void +printav(av) + register char **av; { - return ((isascii(c) && isupper(c)) ? tolower(c) : c); + while (*av != NULL) + { + if (tTd(0, 44)) + sm_dprintf("\n\t%08lx=", (unsigned long) *av); + else + (void) sm_io_putc(smioout, SM_TIME_DEFAULT, ' '); + xputs(*av++); + } + (void) sm_io_putc(smioout, SM_TIME_DEFAULT, '\n'); } /* ** XPUTS -- put string doing control escapes. @@ -715,27 +622,53 @@ xputs(s) { register int c; register struct metamac *mp; - bool shiftout = FALSE; + bool shiftout = false; extern struct metamac MetaMacros[]; + static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI", + "@(#)$Debug: ANSI - enable reverse video in debug output $"); + + /* + ** TermEscape is set here, rather than in main(), + ** because ANSI mode can be turned on or off at any time + ** if we are in -bt rule testing mode. + */ + + if (sm_debug_unknown(&DebugANSI)) + { + if (sm_debug_active(&DebugANSI, 1)) + { + TermEscape.te_rv_on = "\033[7m"; + TermEscape.te_rv_off = "\033[0m"; + } + else + { + TermEscape.te_rv_on = ""; + TermEscape.te_rv_off = ""; + } + } if (s == NULL) { - printf("%s<null>%s", TermEscape.te_rv_on, TermEscape.te_rv_off); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s<null>%s", + TermEscape.te_rv_on, TermEscape.te_rv_off); return; } while ((c = (*s++ & 0377)) != '\0') { if (shiftout) { - printf("%s", TermEscape.te_rv_off); - shiftout = FALSE; + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s", + TermEscape.te_rv_off); + shiftout = false; } if (!isascii(c)) { if (c == MATCHREPL) { - printf("%s$", TermEscape.te_rv_on); - shiftout = TRUE; + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "%s$", + TermEscape.te_rv_on); + shiftout = true; if (*s == '\0') continue; c = *s++ & 0377; @@ -743,50 +676,70 @@ xputs(s) } if (c == MACROEXPAND || c == MACRODEXPAND) { - printf("%s$", TermEscape.te_rv_on); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "%s$", + TermEscape.te_rv_on); if (c == MACRODEXPAND) - (void) putchar('&'); - shiftout = TRUE; + (void) sm_io_putc(smioout, + SM_TIME_DEFAULT, '&'); + shiftout = true; if (*s == '\0') continue; if (strchr("=~&?", *s) != NULL) - (void) putchar(*s++); + (void) sm_io_putc(smioout, + SM_TIME_DEFAULT, + *s++); if (bitset(0200, *s)) - printf("{%s}", macname(bitidx(*s++))); + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "{%s}", + macname(bitidx(*s++))); else - printf("%c", *s++); + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "%c", + *s++); continue; } for (mp = MetaMacros; mp->metaname != '\0'; mp++) { - if ((mp->metaval & 0377) == c) + if (bitidx(mp->metaval) == c) { - printf("%s$%c", - TermEscape.te_rv_on, - mp->metaname); - shiftout = TRUE; + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "%s$%c", + TermEscape.te_rv_on, + mp->metaname); + shiftout = true; break; } } if (c == MATCHCLASS || c == MATCHNCLASS) { if (bitset(0200, *s)) - printf("{%s}", macname(*s++ & 0377)); + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "{%s}", + macname(bitidx(*s++))); else if (*s != '\0') - printf("%c", *s++); + (void) sm_io_fprintf(smioout, + SM_TIME_DEFAULT, + "%c", + *s++); } if (mp->metaname != '\0') continue; /* unrecognized meta character */ - printf("%sM-", TermEscape.te_rv_on); - shiftout = TRUE; + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%sM-", + TermEscape.te_rv_on); + shiftout = true; c &= 0177; } printchar: if (isprint(c)) { - (void) putchar(c); + (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); continue; } @@ -807,23 +760,25 @@ xputs(s) } if (!shiftout) { - printf("%s", TermEscape.te_rv_on); - shiftout = TRUE; + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s", + TermEscape.te_rv_on); + shiftout = true; } if (isprint(c)) { - (void) putchar('\\'); - (void) putchar(c); + (void) sm_io_putc(smioout, SM_TIME_DEFAULT, '\\'); + (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); } else { - (void) putchar('^'); - (void) putchar(c ^ 0100); + (void) sm_io_putc(smioout, SM_TIME_DEFAULT, '^'); + (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c ^ 0100); } } if (shiftout) - printf("%s", TermEscape.te_rv_off); - (void) fflush(stdout); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s", + TermEscape.te_rv_off); + (void) sm_io_flush(smioout, SM_TIME_DEFAULT); } /* ** MAKELOWER -- Translate a line into lower case @@ -852,59 +807,6 @@ makelower(p) *p = tolower(c); } /* -** BUILDFNAME -- build full name from gecos style entry. -** -** This routine interprets the strange entry that would appear -** in the GECOS field of the password file. -** -** Parameters: -** p -- name to build. -** user -- the login name of this user (for &). -** buf -- place to put the result. -** buflen -- length of buf. -** -** Returns: -** none. -** -** Side Effects: -** none. -*/ - -void -buildfname(gecos, user, buf, buflen) - register char *gecos; - char *user; - char *buf; - int buflen; -{ - register char *p; - register char *bp = buf; - - if (*gecos == '*') - gecos++; - - /* copy gecos, interpolating & to be full name */ - for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) - { - if (bp >= &buf[buflen - 1]) - { - /* buffer overflow -- just use login name */ - snprintf(buf, buflen, "%s", user); - return; - } - if (*p == '&') - { - /* interpolate full name */ - snprintf(bp, buflen - (bp - buf), "%s", user); - *bp = toupper(*bp); - bp += strlen(bp); - } - else - *bp++ = *p; - } - *bp = '\0'; -} -/* ** FIXCRLF -- fix <CR><LF> in line. ** ** Looks for the <CR><LF> combination and turns it into the @@ -953,7 +855,7 @@ fixcrlf(line, stripnl) ** none ** ** Side Effects: -** output of l to fp. +** output of l to mci->mci_out. */ void @@ -982,7 +884,7 @@ putline(l, mci) ** none ** ** Side Effects: -** output of l to fp. +** output of l to mci->mci_out. */ void @@ -992,7 +894,7 @@ putxline(l, len, mci, pxflags) register MCI *mci; int pxflags; { - bool dead = FALSE; + bool dead = false; register char *p, *end; int slop = 0; @@ -1016,7 +918,8 @@ putxline(l, len, mci, pxflags) p = end; if (TrafficLogFile != NULL) - fprintf(TrafficLogFile, "%05d >>> ", (int) getpid()); + (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, + "%05d >>> ", (int) CurrentPid); /* check for line overflow */ while (mci->mci_mailer->m_linelimit > 0 && @@ -1028,71 +931,82 @@ putxline(l, len, mci, pxflags) if (l[0] == '.' && slop == 0 && bitnset(M_XDOT, mci->mci_mailer->m_flags)) { - if (putc('.', mci->mci_out) == EOF) - dead = TRUE; + if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, + '.') == SM_IO_EOF) + dead = true; else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } if (TrafficLogFile != NULL) - (void) putc('.', TrafficLogFile); + (void) sm_io_putc(TrafficLogFile, + SM_TIME_DEFAULT, '.'); } else if (l[0] == 'F' && slop == 0 && bitset(PXLF_MAPFROM, pxflags) && strncmp(l, "From ", 5) == 0 && bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) { - if (putc('>', mci->mci_out) == EOF) - dead = TRUE; + if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, + '>') == SM_IO_EOF) + dead = true; else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } if (TrafficLogFile != NULL) - (void) putc('>', TrafficLogFile); + (void) sm_io_putc(TrafficLogFile, + SM_TIME_DEFAULT, + '>'); } if (dead) break; while (l < q) { - if (putc((unsigned char) *l++, mci->mci_out) == - EOF) + if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, + (unsigned char) *l++) == SM_IO_EOF) { - dead = TRUE; + dead = true; break; } else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } } if (dead) break; - if (putc('!', mci->mci_out) == EOF || - fputs(mci->mci_mailer->m_eol, - mci->mci_out) == EOF || - putc(' ', mci->mci_out) == EOF) + if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '!') == + SM_IO_EOF || + sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, + mci->mci_mailer->m_eol) == + SM_IO_EOF || + sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, ' ') == + SM_IO_EOF) { - dead = TRUE; + dead = true; break; } else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } if (TrafficLogFile != NULL) { for (l = l_base; l < q; l++) - (void) putc((unsigned char)*l, - TrafficLogFile); - fprintf(TrafficLogFile, "!\n%05d >>> ", - (int) getpid()); + (void) sm_io_putc(TrafficLogFile, + SM_TIME_DEFAULT, + (unsigned char)*l); + (void) sm_io_fprintf(TrafficLogFile, + SM_TIME_DEFAULT, + "!\n%05d >>> ", + (int) CurrentPid); } slop = 1; } @@ -1104,74 +1018,89 @@ putxline(l, len, mci, pxflags) if (l[0] == '.' && slop == 0 && bitnset(M_XDOT, mci->mci_mailer->m_flags)) { - if (putc('.', mci->mci_out) == EOF) + if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') == + SM_IO_EOF) break; else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } if (TrafficLogFile != NULL) - (void) putc('.', TrafficLogFile); + (void) sm_io_putc(TrafficLogFile, + SM_TIME_DEFAULT, '.'); } else if (l[0] == 'F' && slop == 0 && bitset(PXLF_MAPFROM, pxflags) && strncmp(l, "From ", 5) == 0 && bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) { - if (putc('>', mci->mci_out) == EOF) + if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') == + SM_IO_EOF) break; else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } if (TrafficLogFile != NULL) - (void) putc('>', TrafficLogFile); + (void) sm_io_putc(TrafficLogFile, + SM_TIME_DEFAULT, '>'); } for ( ; l < p; ++l) { if (TrafficLogFile != NULL) - (void) putc((unsigned char)*l, TrafficLogFile); - if (putc((unsigned char) *l, mci->mci_out) == EOF) + (void) sm_io_putc(TrafficLogFile, + SM_TIME_DEFAULT, + (unsigned char)*l); + if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, + (unsigned char) *l) == SM_IO_EOF) { - dead = TRUE; + dead = true; break; } else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } } if (dead) break; if (TrafficLogFile != NULL) - (void) putc('\n', TrafficLogFile); - if (fputs(mci->mci_mailer->m_eol, mci->mci_out) == EOF) + (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT, + '\n'); + if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, + mci->mci_mailer->m_eol) == SM_IO_EOF) break; else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } if (l < end && *l == '\n') { if (*++l != ' ' && *l != '\t' && *l != '\0' && bitset(PXLF_HEADER, pxflags)) { - if (putc(' ', mci->mci_out) == EOF) + if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, + ' ') == SM_IO_EOF) break; else { /* record progress for DATA timeout */ - DataProgress = TRUE; + DataProgress = true; } + if (TrafficLogFile != NULL) - (void) putc(' ', TrafficLogFile); + (void) sm_io_putc(TrafficLogFile, + SM_TIME_DEFAULT, ' '); } } + + /* record progress for DATA timeout */ + DataProgress = true; } while (l < end); } /* @@ -1181,28 +1110,28 @@ putxline(l, len, mci, pxflags) ** f -- name of file to unlink. ** ** Returns: -** none. +** return value of unlink() ** ** Side Effects: ** f is unlinked. */ -void +int xunlink(f) char *f; { register int i; if (LogLevel > 98) - sm_syslog(LOG_DEBUG, CurEnv->e_id, - "unlink %s", - f); + sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f); i = unlink(f); if (i < 0 && LogLevel > 97) - sm_syslog(LOG_DEBUG, CurEnv->e_id, - "%s: unlink-fail %d", + sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d", f, errno); + if (i >= 0) + SYNC_DIR(f, false); + return i; } /* ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. @@ -1215,86 +1144,85 @@ xunlink(f) ** during -- what we are trying to read (for error messages). ** ** Returns: -** NULL on error (including timeout). This will also leave +** NULL on error (including timeout). This may also leave ** buf containing a null string. ** buf otherwise. -** -** Side Effects: -** none. */ -static jmp_buf CtxReadTimeout; - char * sfgets(buf, siz, fp, timeout, during) char *buf; int siz; - FILE *fp; + SM_FILE_T *fp; time_t timeout; char *during; { - register EVENT *ev = NULL; register char *p; int save_errno; + int io_timeout; + + SM_REQUIRE(siz > 0); + SM_REQUIRE(buf != NULL); if (fp == NULL) { buf[0] = '\0'; + errno = EBADF; return NULL; } - /* set the timeout */ - if (timeout != 0) + /* try to read */ + p = NULL; + errno = 0; + + /* convert the timeout to sm_io notation */ + io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000; + while (!sm_io_eof(fp) && !sm_io_error(fp)) { - if (setjmp(CtxReadTimeout) != 0) + errno = 0; + p = sm_io_fgets(fp, io_timeout, buf, siz); + if (p == NULL && errno == EAGAIN) { + /* The sm_io_fgets() call timedout */ if (LogLevel > 1) sm_syslog(LOG_NOTICE, CurEnv->e_id, "timeout waiting for input from %.100s during %s", - CurHostName ? CurHostName : "local", + CURHOSTNAME, during); buf[0] = '\0'; #if XDEBUG checkfd012(during); #endif /* XDEBUG */ if (TrafficLogFile != NULL) - fprintf(TrafficLogFile, "%05d <<< [TIMEOUT]\n", - (int) getpid()); - errno = 0; + (void) sm_io_fprintf(TrafficLogFile, + SM_TIME_DEFAULT, + "%05d <<< [TIMEOUT]\n", + (int) CurrentPid); + errno = ETIMEDOUT; return NULL; } - ev = setevent(timeout, readtimeout, 0); - } - - /* try to read */ - p = NULL; - errno = 0; - while (!feof(fp) && !ferror(fp)) - { - errno = 0; - p = fgets(buf, siz, fp); if (p != NULL || errno != EINTR) break; - clearerr(fp); + (void) sm_io_clearerr(fp); } save_errno = errno; - /* clear the event if it has not sprung */ - clrevent(ev); - /* clean up the books and exit */ LineNumber++; if (p == NULL) { buf[0] = '\0'; if (TrafficLogFile != NULL) - fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid()); + (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, + "%05d <<< [EOF]\n", + (int) CurrentPid); errno = save_errno; return NULL; } if (TrafficLogFile != NULL) - fprintf(TrafficLogFile, "%05d <<< %s", (int) getpid(), buf); + (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, + "%05d <<< %s", (int) CurrentPid, buf); if (SevenBitInput) { for (p = buf; *p != '\0'; p++) @@ -1306,30 +1234,15 @@ sfgets(buf, siz, fp, timeout, during) { if (bitset(0200, *p)) { - HasEightBits = TRUE; + HasEightBits = true; break; } } } return buf; } - -/* ARGSUSED */ -static void -readtimeout(timeout) - time_t timeout; -{ - /* - ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD - ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE - ** DOING. - */ - - errno = ETIMEDOUT; - longjmp(CtxReadTimeout, 1); -} /* -** FGETFOLDED -- like fgets, but know about folded lines. +** FGETFOLDED -- like fgets, but knows about folded lines. ** ** Parameters: ** buf -- place to put result. @@ -1337,9 +1250,9 @@ readtimeout(timeout) ** f -- file to read from. ** ** Returns: -** input line(s) on success, NULL on error or EOF. +** input line(s) on success, NULL on error or SM_IO_EOF. ** This will normally be buf -- unless the line is too -** long, when it will be xalloc()ed. +** long, when it will be sm_malloc_x()ed. ** ** Side Effects: ** buf gets lines from f, with continuation lines (lines @@ -1351,22 +1264,32 @@ char * fgetfolded(buf, n, f) char *buf; register int n; - FILE *f; + SM_FILE_T *f; { register char *p = buf; char *bp = buf; register int i; + SM_REQUIRE(n > 0); + SM_REQUIRE(buf != NULL); + if (f == NULL) + { + buf[0] = '\0'; + errno = EBADF; + return NULL; + } + n--; - while ((i = getc(f)) != EOF) + while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF) { if (i == '\r') { - i = getc(f); + i = sm_io_getc(f, SM_TIME_DEFAULT); if (i != '\n') { - if (i != EOF) - (void) ungetc(i, f); + if (i != SM_IO_EOF) + (void) sm_io_ungetc(f, SM_TIME_DEFAULT, + i); i = '\r'; } } @@ -1381,7 +1304,7 @@ fgetfolded(buf, n, f) nn *= 2; else nn += MEMCHUNKSIZE; - nbp = xalloc(nn); + nbp = sm_malloc_x(nn); memmove(nbp, bp, p - bp); p = &nbp[p - bp]; if (bp != buf) @@ -1393,9 +1316,9 @@ fgetfolded(buf, n, f) if (i == '\n') { LineNumber++; - i = getc(f); - if (i != EOF) - (void) ungetc(i, f); + i = sm_io_getc(f, SM_TIME_DEFAULT); + if (i != SM_IO_EOF) + (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i); if (i != ' ' && i != '\t') break; } @@ -1415,9 +1338,6 @@ fgetfolded(buf, n, f) ** ** Returns: ** the current time. -** -** Side Effects: -** none. */ time_t @@ -1431,17 +1351,14 @@ curtime() /* ** ATOBOOL -- convert a string representation to boolean. ** -** Defaults to "TRUE" +** Defaults to false ** ** Parameters: -** s -- string to convert. Takes "tTyY" as true, +** s -- string to convert. Takes "tTyY", empty, and NULL as true, ** others as false. ** ** Returns: ** A boolean representation of the string. -** -** Side Effects: -** none. */ bool @@ -1449,8 +1366,8 @@ atobool(s) register char *s; { if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) - return TRUE; - return FALSE; + return true; + return false; } /* ** ATOOCT -- convert a string representation to octal. @@ -1461,9 +1378,6 @@ atobool(s) ** Returns: ** An integer representing the string interpreted as an ** octal number. -** -** Side Effects: -** none. */ int @@ -1483,11 +1397,8 @@ atooct(s) ** a, b -- the bitmaps in question ** ** Returns: -** TRUE if they have a non-null intersection -** FALSE otherwise -** -** Side Effects: -** none. +** true if they have a non-null intersection +** false otherwise */ bool @@ -1500,9 +1411,9 @@ bitintersect(a, b) for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) { if ((a[i] & b[i]) != 0) - return TRUE; + return true; } - return FALSE; + return false; } /* ** BITZEROP -- tell if a bitmap is all zero @@ -1511,11 +1422,8 @@ bitintersect(a, b) ** map -- the bit map to check ** ** Returns: -** TRUE if map is all zero. -** FALSE if there are any bits set in map. -** -** Side Effects: -** none. +** true if map is all zero. +** false if there are any bits set in map. */ bool @@ -1527,9 +1435,9 @@ bitzerop(map) for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) { if (map[i] != 0) - return FALSE; + return false; } - return TRUE; + return true; } /* ** STRCONTAINEDIN -- tell if one string is contained in another @@ -1539,8 +1447,8 @@ bitzerop(map) ** b -- possible superstring. ** ** Returns: -** TRUE if a is contained in b. -** FALSE otherwise. +** true if a is contained in b (case insensitive). +** false otherwise. */ bool @@ -1561,10 +1469,10 @@ strcontainedin(a, b) { if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c) continue; - if (strncasecmp(a, b, la) == 0) - return TRUE; + if (sm_strncasecmp(a, b, la) == 0) + return true; } - return FALSE; + return false; } /* ** CHECKFD012 -- check low numbered file descriptors @@ -1612,7 +1520,7 @@ checkfdopen(fd, where) if (fstat(fd, &st) < 0 && errno == EBADF) { syserr("checkfdopen(%d): %s not open as expected!", fd, where); - printopenfds(TRUE); + printopenfds(true); } #endif /* XDEBUG */ } @@ -1635,7 +1543,7 @@ checkfds(where) { int maxfd; register int fd; - bool printhdr = TRUE; + bool printhdr = true; int save_errno = errno; static BITMAP256 baseline; extern int DtableSize; @@ -1670,9 +1578,9 @@ checkfds(where) sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: changed fds:", where); - printhdr = FALSE; + printhdr = false; } - dumpfd(fd, TRUE, TRUE); + dumpfd(fd, true, true); } errno = save_errno; } @@ -1699,7 +1607,7 @@ printopenfds(logit) extern int DtableSize; for (fd = 0; fd < DtableSize; fd++) - dumpfd(fd, FALSE, logit); + dumpfd(fd, false, logit); } /* ** DUMPFD -- dump a file descriptor @@ -1709,6 +1617,9 @@ printopenfds(logit) ** printclosed -- if set, print a notification even if ** it is closed; otherwise print nothing. ** logit -- if set, send output to syslog instead of stdout. +** +** Returns: +** none. */ void @@ -1732,7 +1643,7 @@ dumpfd(fd, printclosed, logit) char buf[200]; p = buf; - snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); + (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); p += strlen(p); if ( @@ -1745,13 +1656,14 @@ dumpfd(fd, printclosed, logit) { if (errno != EBADF) { - snprintf(p, SPACELEFT(buf, p), "CANNOT STAT (%s)", - errstring(errno)); + (void) sm_snprintf(p, SPACELEFT(buf, p), + "CANNOT STAT (%s)", + sm_errstring(errno)); goto printit; } else if (printclosed) { - snprintf(p, SPACELEFT(buf, p), "CLOSED"); + (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED"); goto printit; } return; @@ -1760,23 +1672,24 @@ dumpfd(fd, printclosed, logit) i = fcntl(fd, F_GETFL, NULL); if (i != -1) { - snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i); + (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i); p += strlen(p); } - snprintf(p, SPACELEFT(buf, p), "mode=%o: ", (int) st.st_mode); + (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ", + (int) st.st_mode); p += strlen(p); switch (st.st_mode & S_IFMT) { #ifdef S_IFSOCK case S_IFSOCK: - snprintf(p, SPACELEFT(buf, p), "SOCK "); + (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK "); p += strlen(p); memset(&sa, '\0', sizeof sa); slen = sizeof sa; if (getsockname(fd, &sa.sa, &slen) < 0) - snprintf(p, SPACELEFT(buf, p), "(%s)", - errstring(errno)); + (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", + sm_errstring(errno)); else { hp = hostnamebyanyaddr(&sa); @@ -1787,23 +1700,25 @@ dumpfd(fd, printclosed, logit) } # if NETINET else if (sa.sa.sa_family == AF_INET) - snprintf(p, SPACELEFT(buf, p), "%s/%d", - hp, ntohs(sa.sin.sin_port)); + (void) sm_snprintf(p, SPACELEFT(buf, p), + "%s/%d", hp, ntohs(sa.sin.sin_port)); # endif /* NETINET */ # if NETINET6 else if (sa.sa.sa_family == AF_INET6) - snprintf(p, SPACELEFT(buf, p), "%s/%d", - hp, ntohs(sa.sin6.sin6_port)); + (void) sm_snprintf(p, SPACELEFT(buf, p), + "%s/%d", hp, ntohs(sa.sin6.sin6_port)); # endif /* NETINET6 */ else - snprintf(p, SPACELEFT(buf, p), "%s", hp); + (void) sm_snprintf(p, SPACELEFT(buf, p), + "%s", hp); } p += strlen(p); - snprintf(p, SPACELEFT(buf, p), "->"); + (void) sm_snprintf(p, SPACELEFT(buf, p), "->"); p += strlen(p); slen = sizeof sa; if (getpeername(fd, &sa.sa, &slen) < 0) - snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno)); + (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", + sm_errstring(errno)); else { hp = hostnamebyanyaddr(&sa); @@ -1814,75 +1729,65 @@ dumpfd(fd, printclosed, logit) } # if NETINET else if (sa.sa.sa_family == AF_INET) - snprintf(p, SPACELEFT(buf, p), "%s/%d", - hp, ntohs(sa.sin.sin_port)); + (void) sm_snprintf(p, SPACELEFT(buf, p), + "%s/%d", hp, ntohs(sa.sin.sin_port)); # endif /* NETINET */ # if NETINET6 else if (sa.sa.sa_family == AF_INET6) - snprintf(p, SPACELEFT(buf, p), "%s/%d", - hp, ntohs(sa.sin6.sin6_port)); + (void) sm_snprintf(p, SPACELEFT(buf, p), + "%s/%d", hp, ntohs(sa.sin6.sin6_port)); # endif /* NETINET6 */ else - snprintf(p, SPACELEFT(buf, p), "%s", hp); + (void) sm_snprintf(p, SPACELEFT(buf, p), + "%s", hp); } break; #endif /* S_IFSOCK */ case S_IFCHR: - snprintf(p, SPACELEFT(buf, p), "CHR: "); + (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: "); p += strlen(p); goto defprint; +#ifdef S_IFBLK case S_IFBLK: - snprintf(p, SPACELEFT(buf, p), "BLK: "); + (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: "); p += strlen(p); goto defprint; +#endif /* S_IFBLK */ #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) case S_IFIFO: - snprintf(p, SPACELEFT(buf, p), "FIFO: "); + (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: "); p += strlen(p); goto defprint; #endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */ #ifdef S_IFDIR case S_IFDIR: - snprintf(p, SPACELEFT(buf, p), "DIR: "); + (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: "); p += strlen(p); goto defprint; #endif /* S_IFDIR */ #ifdef S_IFLNK case S_IFLNK: - snprintf(p, SPACELEFT(buf, p), "LNK: "); + (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: "); p += strlen(p); goto defprint; #endif /* S_IFLNK */ default: defprint: - /*CONSTCOND*/ - if (sizeof st.st_ino > sizeof (long)) - snprintf(p, SPACELEFT(buf, p), - "dev=%d/%d, ino=%s, nlink=%d, u/gid=%d/%d, ", - major(st.st_dev), minor(st.st_dev), - quad_to_string(st.st_ino), - (int) st.st_nlink, (int) st.st_uid, - (int) st.st_gid); - else - snprintf(p, SPACELEFT(buf, p), - "dev=%d/%d, ino=%lu, nlink=%d, u/gid=%d/%d, ", - major(st.st_dev), minor(st.st_dev), - (unsigned long) st.st_ino, - (int) st.st_nlink, (int) st.st_uid, - (int) st.st_gid); - /*CONSTCOND*/ - if (sizeof st.st_size > sizeof (long)) - snprintf(p, SPACELEFT(buf, p), "size=%s", - quad_to_string(st.st_size)); - else - snprintf(p, SPACELEFT(buf, p), "size=%lu", - (unsigned long) st.st_size); + (void) sm_snprintf(p, SPACELEFT(buf, p), + "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ", + major(st.st_dev), minor(st.st_dev), + (ULONGLONG_T) st.st_ino, + (int) st.st_nlink, (int) st.st_uid, + (int) st.st_gid); + p += strlen(p); + (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu", + (ULONGLONG_T) st.st_size); break; } @@ -1891,7 +1796,7 @@ printit: sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL, "%.800s", buf); else - printf("%s\n", buf); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", buf); } /* ** SHORTEN_HOSTNAME -- strip local domain information off of hostname. @@ -1900,7 +1805,7 @@ printit: ** host -- the host to shorten (stripped in place). ** ** Returns: -** place where string was trunacted, NULL if not truncated. +** place where string was truncated, NULL if not truncated. */ char * @@ -1910,14 +1815,15 @@ shorten_hostname(host) register char *p; char *mydom; int i; - bool canon = FALSE; + bool canon = false; /* strip off final dot */ - p = &host[strlen(host) - 1]; + i = strlen(host); + p = &host[(i == 0) ? 0 : i - 1]; if (*p == '.') { *p = '\0'; - canon = TRUE; + canon = true; } /* see if there is any domain at all -- if not, we are done */ @@ -1930,8 +1836,9 @@ shorten_hostname(host) if (mydom == NULL) mydom = ""; i = strlen(++p); - if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 && - (mydom[i] == '.' || mydom[i] == '\0')) + if ((canon ? sm_strcasecmp(p, mydom) + : sm_strncasecmp(p, mydom, i)) == 0 && + (mydom[i] == '.' || mydom[i] == '\0')) { *--p = '\0'; return p; @@ -1959,6 +1866,8 @@ prog_open(argv, pfd, e) pid_t pid; int i; int save_errno; + int sff; + int ret; int fdv[2]; char *p, *q; char buf[MAXLINE + 1]; @@ -1985,13 +1894,22 @@ prog_open(argv, pfd, e) return pid; } - /* child -- close stdin */ - (void) close(0); - /* Reset global flags */ RestartRequest = NULL; + RestartWorkGroup = false; ShutdownRequest = NULL; PendingSignal = 0; + CurrentPid = getpid(); + + /* + ** Initialize exception stack and default exception + ** handler for child process. + */ + + sm_exc_newthread(fatal_error); + + /* child -- close stdin */ + (void) close(0); /* stdout goes back to parent */ (void) close(fdv[0]); @@ -2007,7 +1925,7 @@ prog_open(argv, pfd, e) { int xfd; - xfd = fileno(e->e_xfp); + xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL); if (xfd >= 0 && dup2(xfd, 2) < 0) { syserr("%s: cannot dup2 for stderr", argv[0]); @@ -2017,7 +1935,7 @@ prog_open(argv, pfd, e) /* this process has no right to the queue file */ if (e->e_lockfp != NULL) - (void) close(fileno(e->e_lockfp)); + (void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL)); /* chroot to the program mailer directory, if defined */ if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL) @@ -2037,6 +1955,7 @@ prog_open(argv, pfd, e) /* run as default user */ endpwent(); + sm_mbdb_terminate(); if (setgid(DefGid) < 0 && geteuid() == 0) { syserr("prog_open: setgid(%ld) failed", (long) DefGid); @@ -2071,6 +1990,20 @@ prog_open(argv, pfd, e) (void) chdir("/"); } + /* Check safety of program to be run */ + sff = SFF_ROOTOK|SFF_EXECOK; + if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail)) + sff |= SFF_NOGWFILES|SFF_NOWWFILES; + if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail)) + sff |= SFF_NOPATHCHECK; + else + sff |= SFF_SAFEDIRPATH; + ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL); + if (ret != 0) + sm_syslog(LOG_INFO, e->e_id, + "Warning: prog_open: program %s unsafe: %s", + argv[0], sm_errstring(ret)); + /* arrange for all the files to be closed */ for (i = 3; i < DtableSize; i++) { @@ -2120,23 +2053,23 @@ get_column(line, col, delim, buf, buflen) int i; char delimbuf[4]; - if ((char)delim == '\0') - (void) strlcpy(delimbuf, "\n\t ", sizeof delimbuf); + if ((char) delim == '\0') + (void) sm_strlcpy(delimbuf, "\n\t ", sizeof delimbuf); else { - delimbuf[0] = (char)delim; + delimbuf[0] = (char) delim; delimbuf[1] = '\0'; } p = line; if (*p == '\0') return NULL; /* line empty */ - if (*p == (char)delim && col == 0) + if (*p == (char) delim && col == 0) return NULL; /* first column empty */ begin = line; - if (col == 0 && (char)delim == '\0') + if (col == 0 && (char) delim == '\0') { while (*begin != '\0' && isascii(*begin) && isspace(*begin)) begin++; @@ -2147,7 +2080,7 @@ get_column(line, col, delim, buf, buflen) if ((begin = strpbrk(begin, delimbuf)) == NULL) return NULL; /* no such column */ begin++; - if ((char)delim == '\0') + if ((char) delim == '\0') { while (*begin != '\0' && isascii(*begin) && isspace(*begin)) begin++; @@ -2161,7 +2094,7 @@ get_column(line, col, delim, buf, buflen) i = end - begin; if (i >= buflen) i = buflen - 1; - (void) strlcpy(buf, begin, i + 1); + (void) sm_strlcpy(buf, begin, i + 1); return buf; } /* @@ -2183,7 +2116,7 @@ cleanstrcpy(t, f, l) int l; { /* check for newlines and log if necessary */ - (void) denlstring(f, TRUE, TRUE); + (void) denlstring(f, true, true); if (l <= 0) syserr("!cleanstrcpy: length == 0"); @@ -2201,7 +2134,6 @@ cleanstrcpy(t, f, l) } *t = '\0'; } - /* ** DENLSTRING -- convert newlines in a string to spaces ** @@ -2237,12 +2169,14 @@ denlstring(s, strict, logattacks) if (bl < l) { /* allocate more space */ + char *nbp = sm_pmalloc_x(l); + if (bp != NULL) sm_free(bp); - bp = xalloc(l); + bp = nbp; bl = l; } - (void) strlcpy(bp, s, l); + (void) sm_strlcpy(bp, s, l); for (p = bp; (p = strchr(p, '\n')) != NULL; ) *p++ = ' '; @@ -2257,6 +2191,92 @@ denlstring(s, strict, logattacks) return bp; } /* +** STR2PRT -- convert "unprintable" characters in a string to \oct +** +** Parameters: +** s -- string to convert +** +** Returns: +** converted string. +** This is a static local buffer, string must be copied +** before this function is called again! +*/ + +char * +str2prt(s) + char *s; +{ + int l; + char c, *h; + bool ok; + static int len = 0; + static char *buf = NULL; + + if (s == NULL) + return NULL; + ok = true; + for (h = s, l = 1; *h != '\0'; h++, l++) + { + if (*h == '\\') + { + ++l; + ok = false; + } + else if (!(isascii(*h) && isprint(*h))) + { + l += 3; + ok = false; + } + } + if (ok) + return s; + if (l > len) + { + char *nbuf = sm_pmalloc_x(l); + + if (buf != NULL) + sm_free(buf); + len = l; + buf = nbuf; + } + for (h = buf; *s != '\0' && l > 0; s++, l--) + { + c = *s; + if (isascii(c) && isprint(c) && c != '\\') + { + *h++ = c; + } + else + { + *h++ = '\\'; + --l; + switch (c) + { + case '\\': + *h++ = '\\'; + break; + case '\t': + *h++ = 't'; + break; + case '\n': + *h++ = 'n'; + break; + case '\r': + *h++ = 'r'; + break; + default: + (void) sm_snprintf(h, l, "%03o", (int) c); + l -= 2; + h += 3; + break; + } + } + } + *h = '\0'; + buf[len - 1] = '\0'; + return buf; +} +/* ** PATH_IS_DIR -- check to see if file exists and is a directory. ** ** There are some additional checks for security violations in @@ -2268,8 +2288,8 @@ denlstring(s, strict, logattacks) ** createflag -- if set, create directory if needed. ** ** Returns: -** TRUE -- if the indicated pathname is a directory -** FALSE -- otherwise +** true -- if the indicated pathname is a directory +** false -- otherwise */ int @@ -2286,25 +2306,24 @@ path_is_dir(pathname, createflag) #endif /* HASLSTAT */ { if (errno != ENOENT || !createflag) - return FALSE; + return false; if (mkdir(pathname, 0755) < 0) - return FALSE; - return TRUE; + return false; + return true; } if (!S_ISDIR(statbuf.st_mode)) { errno = ENOTDIR; - return FALSE; + return false; } /* security: don't allow writable directories */ if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode)) { errno = EACCES; - return FALSE; + return false; } - - return TRUE; + return true; } /* ** PROC_LIST_ADD -- add process id to list of our children @@ -2313,19 +2332,37 @@ path_is_dir(pathname, createflag) ** pid -- pid to add to list. ** task -- task of pid. ** type -- type of process. +** count -- number of processes. +** other -- other information for this type. ** ** Returns: ** none +** +** Side Effects: +** May increase CurChildren. May grow ProcList. */ -static struct procs *volatile ProcListVec = NULL; -static int ProcListSize = 0; +typedef struct procs PROCS_T; + +struct procs +{ + pid_t proc_pid; + char *proc_task; + int proc_type; + int proc_count; + int proc_other; +}; + +static PROCS_T *volatile ProcListVec = NULL; +static int ProcListSize = 0; void -proc_list_add(pid, task, type) +proc_list_add(pid, task, type, count, other) pid_t pid; char *task; int type; + int count; + int other; { int i; @@ -2349,16 +2386,19 @@ proc_list_add(pid, task, type) if (i >= ProcListSize) { /* grow process list */ - struct procs *npv; + PROCS_T *npv; - npv = (struct procs *) xalloc((sizeof *npv) * - (ProcListSize + PROC_LIST_SEG)); + SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG); + npv = (PROCS_T *) sm_pmalloc_x((sizeof *npv) * + (ProcListSize + PROC_LIST_SEG)); if (ProcListSize > 0) { memmove(npv, ProcListVec, - ProcListSize * sizeof (struct procs)); + ProcListSize * sizeof (PROCS_T)); sm_free(ProcListVec); } + + /* XXX just use memset() to initialize this part? */ for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) { npv[i].proc_pid = NO_PID; @@ -2370,14 +2410,17 @@ proc_list_add(pid, task, type) ProcListVec = npv; } ProcListVec[i].proc_pid = pid; - if (ProcListVec[i].proc_task != NULL) - sm_free(ProcListVec[i].proc_task); - ProcListVec[i].proc_task = newstr(task); + PSTRSET(ProcListVec[i].proc_task, task); ProcListVec[i].proc_type = type; + ProcListVec[i].proc_count = count; + ProcListVec[i].proc_other = other; /* if process adding itself, it's not a child */ - if (pid != getpid()) + if (pid != CurrentPid) + { + SM_ASSERT(CurChildren < INT_MAX); CurChildren++; + } } /* ** PROC_LIST_SET -- set pid task in process list @@ -2401,9 +2444,7 @@ proc_list_set(pid, task) { if (ProcListVec[i].proc_pid == pid) { - if (ProcListVec[i].proc_task != NULL) - sm_free(ProcListVec[i].proc_task); - ProcListVec[i].proc_task = newstr(task); + PSTRSET(ProcListVec[i].proc_task, task); break; } } @@ -2413,18 +2454,25 @@ proc_list_set(pid, task) ** ** Parameters: ** pid -- pid to drop +** count -- pointer to number of processes (return). +** other -- storage for proc_other (return). ** ** Returns: ** type of process ** +** Side Effects: +** May decrease CurChildren. +** ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE ** DOING. */ int -proc_list_drop(pid) +proc_list_drop(pid, count, other) pid_t pid; + int *count; + int *other; { int i; int type = PROC_NONE; @@ -2435,6 +2483,10 @@ proc_list_drop(pid) { ProcListVec[i].proc_pid = NO_PID; type = ProcListVec[i].proc_type; + if (count != NULL) + *count = ProcListVec[i].proc_count; + if (other != NULL) + *other = ProcListVec[i].proc_other; break; } } @@ -2452,6 +2504,9 @@ proc_list_drop(pid) ** ** Returns: ** none. +** +** Side Effects: +** Sets CurChildren to zero. */ void @@ -2461,9 +2516,7 @@ proc_list_clear() /* start from 1 since 0 is the daemon itself */ for (i = 1; i < ProcListSize; i++) - { ProcListVec[i].proc_pid = NO_PID; - } CurChildren = 0; } /* @@ -2474,6 +2527,9 @@ proc_list_clear() ** ** Returns: ** none +** +** Side Effects: +** May decrease CurChildren. */ void @@ -2493,25 +2549,29 @@ proc_list_probe() "proc_list_probe: lost pid %d", (int) ProcListVec[i].proc_pid); ProcListVec[i].proc_pid = NO_PID; + SM_FREE_CLR(ProcListVec[i].proc_task); CurChildren--; } } if (CurChildren < 0) CurChildren = 0; } + /* ** PROC_LIST_DISPLAY -- display the process list ** ** Parameters: ** out -- output file pointer +** prefix -- string to output in front of each line. ** ** Returns: ** none. */ void -proc_list_display(out) - FILE *out; +proc_list_display(out, prefix) + SM_FILE_T *out; + char *prefix; { int i; @@ -2520,202 +2580,60 @@ proc_list_display(out) if (ProcListVec[i].proc_pid == NO_PID) continue; - fprintf(out, "%d %s%s\n", (int) ProcListVec[i].proc_pid, - ProcListVec[i].proc_task != NULL ? - ProcListVec[i].proc_task : "(unknown)", - (OpMode == MD_SMTP || - OpMode == MD_DAEMON || - OpMode == MD_ARPAFTP) ? "\r" : ""); + (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n", + prefix, + (int) ProcListVec[i].proc_pid, + ProcListVec[i].proc_task != NULL ? + ProcListVec[i].proc_task : "(unknown)", + (OpMode == MD_SMTP || + OpMode == MD_DAEMON || + OpMode == MD_ARPAFTP) ? "\r" : ""); } } -/* -** SAFEFOPEN -- do a file open with extra checking + +/* +** PROC_LIST_SIGNAL -- send a signal to a type of process in the list ** ** Parameters: -** fn -- the file name to open. -** omode -- the open-style mode flags. -** cmode -- the create-style mode flags. -** sff -- safefile flags. +** type -- type of process to signal +** signal -- the type of signal to send ** -** Returns: -** Same as fopen. +** Results: +** none. +** +** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD +** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE +** DOING. */ -FILE * -safefopen(fn, omode, cmode, sff) - char *fn; - int omode; - int cmode; - long sff; +void +proc_list_signal(type, signal) + int type; + int signal; { - int fd; - int save_errno; - FILE *fp; - char *fmode; - - switch (omode & O_ACCMODE) - { - case O_RDONLY: - fmode = "r"; - break; - - case O_WRONLY: - if (bitset(O_APPEND, omode)) - fmode = "a"; - else - fmode = "w"; - break; + int chldwasblocked; + int alrmwasblocked; + int i; + pid_t mypid = getpid(); - case O_RDWR: - if (bitset(O_TRUNC, omode)) - fmode = "w+"; - else if (bitset(O_APPEND, omode)) - fmode = "a+"; - else - fmode = "r+"; - break; + /* block these signals so that we may signal cleanly */ + chldwasblocked = sm_blocksignal(SIGCHLD); + alrmwasblocked = sm_blocksignal(SIGALRM); - default: - syserr("554 5.3.5 safefopen: unknown omode %o", omode); - fmode = "x"; - } - fd = safeopen(fn, omode, cmode, sff); - if (fd < 0) + /* Find all processes of type and send signal */ + for (i = 0; i < ProcListSize; i++) { - save_errno = errno; - if (tTd(44, 10)) - dprintf("safefopen: safeopen failed: %s\n", - errstring(errno)); - errno = save_errno; - return NULL; + if (ProcListVec[i].proc_pid == NO_PID || + ProcListVec[i].proc_pid == mypid) + continue; + if (ProcListVec[i].proc_type != type) + continue; + (void) kill(ProcListVec[i].proc_pid, signal); } - fp = fdopen(fd, fmode); - if (fp != NULL) - return fp; - save_errno = errno; - if (tTd(44, 10)) - { - dprintf("safefopen: fdopen(%s, %s) failed: omode=%x, sff=%lx, err=%s\n", - fn, fmode, omode, sff, errstring(errno)); - } - (void) close(fd); - errno = save_errno; - return NULL; -} -/* -** SM_STRCASECMP -- 8-bit clean version of strcasecmp -** -** Thank you, vendors, for making this all necessary. -*/ - -/* - * Copyright (c) 1987, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)strcasecmp.c 8.1 (Berkeley) 6/4/93"; -#endif /* defined(LIBC_SCCS) && !defined(lint) */ - -/* - * This array is designed for mapping upper and lower case letter - * together for a case independent comparison. The mappings are - * based upon ascii character sequences. - */ -static const u_char charmap[] = { - 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, - 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, - 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, - 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, - 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, - 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, - 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, - 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, - 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147, - 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, - 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, - 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137, - 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147, - 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, - 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, - 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, - 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, - 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, - 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, - 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, - 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, - 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, - 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, - 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, - 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, - 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, - 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, - 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, - 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, - 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, - 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, - 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377, -}; - -int -sm_strcasecmp(s1, s2) - const char *s1, *s2; -{ - register const u_char *cm = charmap, - *us1 = (const u_char *)s1, - *us2 = (const u_char *)s2; - - while (cm[*us1] == cm[*us2++]) - if (*us1++ == '\0') - return 0; - return (cm[*us1] - cm[*--us2]); -} - -int -sm_strncasecmp(s1, s2, n) - const char *s1, *s2; - register size_t n; -{ - if (n != 0) { - register const u_char *cm = charmap, - *us1 = (const u_char *)s1, - *us2 = (const u_char *)s2; - - do { - if (cm[*us1] != cm[*us2++]) - return (cm[*us1] - cm[*--us2]); - if (*us1++ == '\0') - break; - } while (--n != 0); - } - return 0; + /* restore the signals */ + if (alrmwasblocked == 0) + (void) sm_releasesignal(SIGALRM); + if (chldwasblocked == 0) + (void) sm_releasesignal(SIGCHLD); } diff --git a/gnu/usr.sbin/sendmail/sendmail/version.c b/gnu/usr.sbin/sendmail/sendmail/version.c index b3a2b4a73b1..759feefd9b8 100644 --- a/gnu/usr.sbin/sendmail/sendmail/version.c +++ b/gnu/usr.sbin/sendmail/sendmail/version.c @@ -11,8 +11,8 @@ * */ -#ifndef lint -static char id[] = "@(#)$Sendmail: version.c,v 8.43.4.39 2001/08/20 14:45:34 gshapiro Exp $"; -#endif /* ! lint */ +#include <sm/gen.h> -char Version[] = "8.11.6"; +SM_RCSID("@(#)$Sendmail: version.c,v 8.79 2001/09/07 20:59:16 ca Exp $") + +char Version[] = "8.12.0"; diff --git a/gnu/usr.sbin/sendmail/smrsh/Makefile b/gnu/usr.sbin/sendmail/smrsh/Makefile index 0a18d699c98..fa19225f9de 100644 --- a/gnu/usr.sbin/sendmail/smrsh/Makefile +++ b/gnu/usr.sbin/sendmail/smrsh/Makefile @@ -1,10 +1,10 @@ -# $OpenBSD: Makefile,v 1.5 2001/09/09 16:41:33 deraadt Exp $ +# $OpenBSD: Makefile,v 1.6 2001/09/11 19:02:50 millert Exp $ PROG= smrsh MAN= smrsh.8 ENVDEF= -DNOT_SENDMAIL -DCMDDIR=\"/usr/libexec/sm.bin\" -WANT_LIBSMUTIL=1 +WANT_LIBSM=1 -BINDIR?= /usr/libexec +.include "../../../../libexec/Makefile.inc" .include <bsd.prog.mk> diff --git a/gnu/usr.sbin/sendmail/smrsh/README b/gnu/usr.sbin/sendmail/smrsh/README index 158e7f34c52..687360aad38 100644 --- a/gnu/usr.sbin/sendmail/smrsh/README +++ b/gnu/usr.sbin/sendmail/smrsh/README @@ -75,7 +75,7 @@ You should NOT include interpreter programs such as sh(1), csh(1), perl(1), uudecode(1) or the stream editor sed(1) in your list of acceptable commands. -If your platform doesn't have a default CMDDIR setting, you will +If your platform doesn't have a default SMRSH_CMDDIR setting, you will next need to create the directory /usr/adm/sm.bin and populate it with the programs that your site feels are allowable for sendmail to execute. This directory is explicitly specified in the source @@ -153,4 +153,4 @@ a typical system follows: host.domain# /usr/sbin/sendmail -bd -q30m -$Revision: 1.3 $, Last updated $Date: 2001/01/15 21:09:11 $ +$Revision: 1.4 $, Last updated $Date: 2001/09/11 19:02:50 $ diff --git a/gnu/usr.sbin/sendmail/smrsh/smrsh.8 b/gnu/usr.sbin/sendmail/smrsh/smrsh.8 index 8e51659adad..0abcdb07e6a 100644 --- a/gnu/usr.sbin/sendmail/smrsh/smrsh.8 +++ b/gnu/usr.sbin/sendmail/smrsh/smrsh.8 @@ -9,10 +9,10 @@ .\" the sendmail distribution. .\" .\" -.\" $OpenBSD: smrsh.8,v 1.5 2001/01/17 05:26:51 millert Exp $ -.\" $Sendmail: smrsh.8,v 8.11.16.2 2000/12/15 19:50:46 gshapiro Exp $ +.\" $OpenBSD: smrsh.8,v 1.6 2001/09/11 19:02:50 millert Exp $ +.\" $Sendmail: smrsh.8,v 8.15 2001/01/24 00:40:47 gshapiro Exp $ .\" -.Dd December 15, 2000 +.Dd January 24, 2001 .Dt SMRSH 8 .Os .Sh NAME diff --git a/gnu/usr.sbin/sendmail/smrsh/smrsh.c b/gnu/usr.sbin/sendmail/smrsh/smrsh.c index fac4dfc7361..8df557a4319 100644 --- a/gnu/usr.sbin/sendmail/smrsh/smrsh.c +++ b/gnu/usr.sbin/sendmail/smrsh/smrsh.c @@ -11,18 +11,16 @@ * */ -#ifndef lint -static char copyright[] = +#include <sm/gen.h> + +SM_IDSTR(copyright, "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ All rights reserved.\n\ Copyright (c) 1993 Eric P. Allman. All rights reserved.\n\ Copyright (c) 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* ! lint */ + The Regents of the University of California. All rights reserved.\n") -#ifndef lint -static char id[] = "@(#)$Sendmail: smrsh.c,v 8.31.4.9 2001/04/24 04:11:51 ca Exp $"; -#endif /* ! lint */ +SM_IDSTR(id, "@(#)$Sendmail: smrsh.c,v 8.53 2001/08/31 18:36:04 gshapiro Exp $") /* ** SMRSH -- sendmail restricted shell @@ -55,7 +53,8 @@ static char id[] = "@(#)$Sendmail: smrsh.c,v 8.31.4.9 2001/04/24 04:11:51 ca Exp */ #include <unistd.h> -#include <stdio.h> +#include <sm/io.h> +#include <sm/string.h> #include <sys/file.h> #include <string.h> #include <ctype.h> @@ -67,18 +66,16 @@ static char id[] = "@(#)$Sendmail: smrsh.c,v 8.31.4.9 2001/04/24 04:11:51 ca Exp #include <syslog.h> #include <stdlib.h> -#ifndef TRUE -# define TRUE 1 -# define FALSE 0 -#endif /* ! TRUE */ +#include <sm/conf.h> +#include <sm/errstring.h> /* directory in which all commands must reside */ #ifndef CMDDIR -# if defined(HPUX10) || defined(HPUX11) || SOLARIS >= 20800 -# define CMDDIR "/var/adm/sm.bin" -# else /* HPUX10 || HPUX11 || SOLARIS >= 20800 */ +# ifdef SMRSH_CMDDIR +# define CMDDIR SMRSH_CMDDIR +# else /* SMRSH_CMDDIR */ # define CMDDIR "/usr/adm/sm.bin" -# endif /* HPUX10 || HPUX11 || SOLARIS >= 20800 */ +# endif /* SMRSH_CMDDIR */ #endif /* ! CMDDIR */ /* characters disallowed in the shell "-c" argument */ @@ -86,16 +83,13 @@ static char id[] = "@(#)$Sendmail: smrsh.c,v 8.31.4.9 2001/04/24 04:11:51 ca Exp /* default search path */ #ifndef PATH -# define PATH "/bin:/usr/bin:/usr/ucb" +# ifdef SMRSH_PATH +# define PATH SMRSH_PATH +# else /* SMRSH_PATH */ +# define PATH "/bin:/usr/bin:/usr/ucb" +# endif /* SMRSH_PATH */ #endif /* ! PATH */ -#ifndef __P -# include "sendmail/cdefs.h" -#endif /* ! __P */ - -extern size_t strlcpy __P((char *, const char *, size_t)); -extern size_t strlcat __P((char *, const char *, size_t)); - char newcmdbuf[1000]; char *prg, *par; @@ -115,8 +109,8 @@ char *prg, *par; void addcmd(s, cmd, len) char *s; - int cmd; - int len; + bool cmd; + size_t len; { if (s == NULL || *s == '\0') return; @@ -124,7 +118,8 @@ addcmd(s, cmd, len) if (sizeof newcmdbuf - strlen(newcmdbuf) <= len + (cmd ? (strlen(CMDDIR) + 1) : 0)) { - fprintf(stderr, "%s: command too long: %s\n", prg, par); + (void)sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: command too long: %s\n", prg, par); #ifndef DEBUG syslog(LOG_WARNING, "command too long: %.40s", par); #endif /* ! DEBUG */ @@ -132,10 +127,10 @@ addcmd(s, cmd, len) } if (cmd) { - (void) strlcat(newcmdbuf, CMDDIR, sizeof newcmdbuf); - (void) strlcat(newcmdbuf, "/", sizeof newcmdbuf); + (void) sm_strlcat(newcmdbuf, CMDDIR, sizeof newcmdbuf); + (void) sm_strlcat(newcmdbuf, "/", sizeof newcmdbuf); } - (void) strlcat(newcmdbuf, s, sizeof newcmdbuf); + (void) sm_strlcat(newcmdbuf, s, sizeof newcmdbuf); } int @@ -147,7 +142,6 @@ main(argc, argv) register char *q; register char *r; register char *cmd; - int i; int isexec; int save_errno; char *newenv[2]; @@ -163,8 +157,8 @@ main(argc, argv) # endif /* ! LOG_MAIL */ #endif /* ! DEBUG */ - (void) strlcpy(pathbuf, "PATH=", sizeof pathbuf); - (void) strlcat(pathbuf, PATH, sizeof pathbuf); + (void) sm_strlcpy(pathbuf, "PATH=", sizeof pathbuf); + (void) sm_strlcat(pathbuf, PATH, sizeof pathbuf); newenv[0] = pathbuf; newenv[1] = NULL; @@ -176,7 +170,8 @@ main(argc, argv) if (argc != 3 || strcmp(argv[1], "-c") != 0) { - fprintf(stderr, "Usage: %s -c command\n", prg); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "Usage: %s -c command\n", prg); #ifndef DEBUG syslog(LOG_ERR, "usage"); #endif /* ! DEBUG */ @@ -199,19 +194,19 @@ main(argc, argv) #endif /* ! DEBUG */ exit(EX_UNAVAILABLE); } - (void) strlcpy(specialbuf, SPECIALS, sizeof specialbuf); + (void) sm_strlcpy(specialbuf, SPECIALS, sizeof specialbuf); for (p = specialbuf; *p != '\0'; p++) *p |= '\200'; - (void) strlcat(specialbuf, SPECIALS, sizeof specialbuf); + (void) sm_strlcat(specialbuf, SPECIALS, sizeof specialbuf); /* ** Do a quick sanity check on command line length. */ - i = strlen(par); - if (i > (sizeof newcmdbuf - sizeof CMDDIR - 2)) + if (strlen(par) > (sizeof newcmdbuf - sizeof CMDDIR - 2)) { - fprintf(stderr, "%s: command too long: %s\n", prg, par); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: command too long: %s\n", prg, par); #ifndef DEBUG syslog(LOG_WARNING, "command too long: %.40s", par); #endif /* ! DEBUG */ @@ -220,7 +215,7 @@ main(argc, argv) q = par; newcmdbuf[0] = '\0'; - isexec = FALSE; + isexec = false; while (*q) { @@ -236,10 +231,11 @@ main(argc, argv) { if (isexec) { - fprintf(stderr, "%s: missing command to exec\n", - prg); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: missing command to exec\n", + prg); #ifndef DEBUG - syslog(LOG_CRIT, "uid %d: missing command to exec", getuid()); + syslog(LOG_CRIT, "uid %d: missing command to exec", (int) getuid()); #endif /* ! DEBUG */ exit(EX_UNAVAILABLE); } @@ -269,15 +265,15 @@ main(argc, argv) /* allow a few shell builtins */ if (strcmp(q, "exec") == 0 && p != NULL) { - addcmd("exec ", FALSE, strlen("exec ")); + addcmd("exec ", false, strlen("exec ")); /* test _next_ arg */ q = ++p; - isexec = TRUE; + isexec = true; continue; } else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0) { - addcmd(cmd, FALSE, strlen(cmd)); + addcmd(cmd, false, strlen(cmd)); /* test following chars */ } else @@ -285,23 +281,24 @@ main(argc, argv) /* ** Check to see if the command name is legal. */ - (void) strlcpy(cmdbuf, CMDDIR, sizeof cmdbuf); - (void) strlcat(cmdbuf, "/", sizeof cmdbuf); - (void) strlcat(cmdbuf, cmd, sizeof cmdbuf); + (void) sm_strlcpy(cmdbuf, CMDDIR, sizeof cmdbuf); + (void) sm_strlcat(cmdbuf, "/", sizeof cmdbuf); + (void) sm_strlcat(cmdbuf, cmd, sizeof cmdbuf); #ifdef DEBUG - printf("Trying %s\n", cmdbuf); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, + "Trying %s\n", cmdbuf); #endif /* DEBUG */ if (access(cmdbuf, X_OK) < 0) { /* oops.... crack attack possiblity */ - fprintf(stderr, - "%s: %s not available for sendmail programs\n", - prg, cmd); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: %s not available for sendmail programs\n", + prg, cmd); if (p != NULL) *p = ' '; #ifndef DEBUG syslog(LOG_CRIT, "uid %d: attempt to use %s", - getuid(), cmd); + (int) getuid(), cmd); #endif /* ! DEBUG */ exit(EX_UNAVAILABLE); } @@ -310,9 +307,9 @@ main(argc, argv) ** Create the actual shell input. */ - addcmd(cmd, TRUE, strlen(cmd)); + addcmd(cmd, true, strlen(cmd)); } - isexec = FALSE; + isexec = false; if (p != NULL) *p = ' '; @@ -320,13 +317,15 @@ main(argc, argv) break; r = strpbrk(p, specialbuf); - if (r == NULL) { - addcmd(p, FALSE, strlen(p)); + if (r == NULL) + { + addcmd(p, false, strlen(p)); break; } #if ALLOWSEMI - if (*r == ';') { - addcmd(p, FALSE, r - p + 1); + if (*r == ';') + { + addcmd(p, false, r - p + 1); q = r + 1; continue; } @@ -334,30 +333,34 @@ main(argc, argv) if ((*r == '&' && *(r + 1) == '&') || (*r == '|' && *(r + 1) == '|')) { - addcmd(p, FALSE, r - p + 2); + addcmd(p, false, r - p + 2); q = r + 2; continue; } - fprintf(stderr, "%s: cannot use %c in command\n", prg, *r); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: cannot use %c in command\n", prg, *r); #ifndef DEBUG syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s", - getuid(), *r, par); + (int) getuid(), *r, par); #endif /* ! DEBUG */ exit(EX_UNAVAILABLE); } /* end of while *q */ if (isexec) { - fprintf(stderr, "%s: missing command to exec\n", prg); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "%s: missing command to exec\n", prg); #ifndef DEBUG - syslog(LOG_CRIT, "uid %d: missing command to exec", getuid()); + syslog(LOG_CRIT, "uid %d: missing command to exec", + (int) getuid()); #endif /* ! DEBUG */ exit(EX_UNAVAILABLE); } /* make sure we created something */ if (newcmdbuf[0] == '\0') { - fprintf(stderr, "Usage: %s -c command\n", prg); + (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "Usage: %s -c command\n", prg); #ifndef DEBUG syslog(LOG_ERR, "usage"); #endif /* ! DEBUG */ @@ -369,15 +372,15 @@ main(argc, argv) */ #ifdef DEBUG - printf("%s\n", newcmdbuf); + (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", newcmdbuf); #endif /* DEBUG */ (void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf, (char *)NULL, newenv); save_errno = errno; #ifndef DEBUG - syslog(LOG_CRIT, "Cannot exec /bin/sh: %m"); + syslog(LOG_CRIT, "Cannot exec /bin/sh: %s", sm_errstring(errno)); #endif /* ! DEBUG */ errno = save_errno; - perror("/bin/sh"); + sm_perror("/bin/sh"); exit(EX_OSFILE); /* NOTREACHED */ return EX_OSFILE; diff --git a/gnu/usr.sbin/sendmail/vacation/Makefile b/gnu/usr.sbin/sendmail/vacation/Makefile index 8b8ea2e1585..e3829ee117d 100644 --- a/gnu/usr.sbin/sendmail/vacation/Makefile +++ b/gnu/usr.sbin/sendmail/vacation/Makefile @@ -1,8 +1,9 @@ -# $OpenBSD: Makefile,v 1.3 2000/08/16 22:06:19 millert Exp $ +# $OpenBSD: Makefile,v 1.4 2001/09/11 19:02:50 millert Exp $ PROG= vacation ENVDEF= -DNOT_SENDMAIL +WANT_LIBSM=1 WANT_LIBSMDB=1 WANT_LIBSMUTIL=1 diff --git a/gnu/usr.sbin/sendmail/vacation/vacation.1 b/gnu/usr.sbin/sendmail/vacation/vacation.1 index 17b5c9d4855..de3e0244275 100644 --- a/gnu/usr.sbin/sendmail/vacation/vacation.1 +++ b/gnu/usr.sbin/sendmail/vacation/vacation.1 @@ -9,29 +9,34 @@ .\" the sendmail distribution. .\" .\" -.\" $Sendmail: vacation.1,v 8.11.4.8 2001/07/20 04:19:38 gshapiro Exp $ +.\" $Sendmail: vacation.1,v 8.25 2001/07/13 21:35:35 gshapiro Exp $ .\" -.TH VACATION 1 "$Date: 2001/08/01 01:01:41 $" +.TH VACATION 1 "$Date: 2001/09/11 19:02:51 $" .SH NAME vacation \- return ``I am not here'' indication .SH SYNOPSIS .B vacation -.RB [ \-i ] -.RB [ \-I ] -.RB [ \-r -.IR interval ] -.RB [ \-x ] .RB [ \-a .IR alias ] +.RB [ \-C +.IR cffile ] +.RB [ \-d ] .RB [ \-f .IR database ] +.RB [ \-i ] +.RB [ \-I ] +.RB [ \-l ] .RB [ \-m .IR message ] +.RB [ \-r +.IR interval ] .RB [ \-s .IR address ] .RB [ \-t .IR time ] +.RB [ \-U ] +.RB [ \-x ] .RB [ \-z ] .I login .SH DESCRIPTION @@ -59,6 +64,23 @@ Handle messages for in the same manner as those received for the user's login name. .TP +.BI \-C " cfpath" +Specify pathname of the sendmail configuration file. +This option is ignored if +.B \-U +is specified. +This option defaults to the standard sendmail configuration file, +located at /etc/mail/sendmail.cf on most systems. +.TP +.B \-d +Send error/debug messages to stdout instead of syslog. +Otherwise, fatal errors, such as calling +.B vacation +with incorrect arguments, or with non-existent +.IR login s, +are logged in the system log file, using +syslog(8). +.TP .BI \-f " filename" Use .I filename @@ -88,6 +110,10 @@ Unless the .I filename starts with / it is relative to ~. .TP +.B \-l +List the content of the vacation database file including the address +and the associated time of the last auto-response to that address. +.TP .BI \-r " interval" Set the reply interval to .I interval @@ -108,6 +134,13 @@ line as the recipient for the vacation message. Ignored, available only for compatibility with Sun's vacation program. .TP +.B \-U +Do not attempt to lookup +.I login +in the password file. +The -f and -m options must be used to specify the database and message file +since there is no home directory for the default settings for these options. +.TP .B \-x reads an exclusion list from stdin (one address per line). Mails coming from an address @@ -184,13 +217,6 @@ Sendmail(8) includes this ``From'' line automatically. -.PP -Fatal errors, such as calling -.B vacation -with incorrect arguments, or with non-existent -.IR login s, -are logged in the system log file, using -syslog(8). .SH FILES .TP 1.8i ~/.vacation.db diff --git a/gnu/usr.sbin/sendmail/vacation/vacation.c b/gnu/usr.sbin/sendmail/vacation/vacation.c index 92e79520514..582b839be74 100644 --- a/gnu/usr.sbin/sendmail/vacation/vacation.c +++ b/gnu/usr.sbin/sendmail/vacation/vacation.c @@ -11,18 +11,16 @@ * */ -#ifndef lint -static char copyright[] = +#include <sm/gen.h> + +SM_IDSTR(copyright, "@(#) Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers.\n\ All rights reserved.\n\ Copyright (c) 1983, 1987, 1993\n\ The Regents of the University of California. All rights reserved.\n\ - Copyright (c) 1983 Eric P. Allman. All rights reserved.\n"; -#endif /* ! lint */ + Copyright (c) 1983 Eric P. Allman. All rights reserved.\n") -#ifndef lint -static char id[] = "@(#)$Sendmail: vacation.c,v 8.68.4.21 2001/05/07 22:06:41 gshapiro Exp $"; -#endif /* ! lint */ +SM_IDSTR(id, "@(#)$Sendmail: vacation.c,v 8.127 2001/09/08 01:21:15 gshapiro Exp $") #include <ctype.h> @@ -33,27 +31,17 @@ static char id[] = "@(#)$Sendmail: vacation.c,v 8.68.4.21 2001/05/07 22:06:41 gs #ifdef EX_OK # undef EX_OK /* unistd.h may have another use for this */ #endif /* EX_OK */ -#include <sysexits.h> +#include <sm/sysexits.h> +#include <sm/cf.h> +#include <sm/mbdb.h> #include "sendmail/sendmail.h" +#include <sendmail/pathnames.h> #include "libsmdb/smdb.h" -#if defined(__hpux) && !defined(HPUX11) -# undef syslog /* Undo hard_syslog conf.h change */ -#endif /* defined(__hpux) && !defined(HPUX11) */ - -#ifndef _PATH_SENDMAIL -# define _PATH_SENDMAIL "/usr/lib/sendmail" -#endif /* ! _PATH_SENDMAIL */ - #define ONLY_ONCE ((time_t) 0) /* send at most one reply */ #define INTERVAL_UNDEF ((time_t) (-1)) /* no value given */ -#ifndef TRUE -# define TRUE 1 -# define FALSE 0 -#endif /* ! TRUE */ - uid_t RealUid; gid_t RealGid; char *RealUserName; @@ -61,7 +49,7 @@ uid_t RunAsUid; uid_t RunAsGid; char *RunAsUserName; int Verbose = 2; -bool DontInitGroups = FALSE; +bool DontInitGroups = false; uid_t TrustedUid = 0; BITMAP256 DontBlameSendmail; @@ -79,15 +67,6 @@ BITMAP256 DontBlameSendmail; #define SECSPERDAY (60 * 60 * 24) #define DAYSPERWEEK 7 -#ifndef __P -# ifdef __STDC__ -# define __P(protos) protos -# else /* __STDC__ */ -# define __P(protos) () -# define const -# endif /* __STDC__ */ -#endif /* ! __P */ - typedef struct alias { char *name; @@ -100,20 +79,39 @@ SMDB_DATABASE *Db; char From[MAXLINE]; -#if _FFR_DEBUG -void (*msglog)(int, const char *, ...) = &syslog; -static void debuglog __P((int, const char *, ...)); -#else /* _FFR_DEBUG */ -# define msglog syslog -#endif /* _FFR_DEBUG */ - +#if defined(__hpux) || defined(__osf__) +# ifndef SM_CONF_SYSLOG_INT +# define SM_CONF_SYSLOG_INT 1 +# endif /* SM_CONF_SYSLOG_INT */ +#endif /* defined(__hpux) || defined(__osf__) */ + +#if SM_CONF_SYSLOG_INT +# define SYSLOG_RET_T int +# define SYSLOG_RET return 0 +#else /* SM_CONF_SYSLOG_INT */ +# define SYSLOG_RET_T void +# define SYSLOG_RET +#endif /* SM_CONF_SYSLOG_INT */ + +typedef SYSLOG_RET_T SYSLOG_T __P((int, const char *, ...)); +SYSLOG_T *msglog = syslog; +static SYSLOG_RET_T debuglog __P((int, const char *, ...)); static void eatmsg __P((void)); +static void listdb __P((void)); /* exit after reading input */ -#define EXITIT(excode) { \ - eatmsg(); \ - return excode; \ - } +#define EXITIT(excode) \ +{ \ + eatmsg(); \ + return excode; \ +} + +#define EXITM(excode) \ +{ \ + if (!iflag && !lflag) \ + eatmsg(); \ + exit(excode); \ +} int main(argc, argv) @@ -121,12 +119,8 @@ main(argc, argv) char **argv; { bool iflag, emptysender, exclude; -#if _FFR_BLACKBOX - bool runasuser = FALSE; -#endif /* _FFR_BLACKBOX */ -#if _FFR_LISTDB - bool lflag = FALSE; -#endif /* _FFR_LISTDB */ + bool runasuser = false; + bool lflag = false; int mfail = 0, ufail = 0; int ch; int result; @@ -136,6 +130,7 @@ main(argc, argv) ALIAS *cur; char *dbfilename = NULL; char *msgfilename = NULL; + char *cfpath = NULL; char *name; SMDB_USER_INFO user_info; static char rnamebuf[MAXNAME]; @@ -147,20 +142,7 @@ main(argc, argv) extern bool recent __P((void)); extern void setreply __P((char *, time_t)); extern void sendmessage __P((char *, char *, bool)); - extern void xclude __P((FILE *)); -#if _FFR_LISTDB -#define EXITM(excode) { \ - if (!iflag && !lflag) \ - eatmsg(); \ - exit(excode); \ - } -#else /* _FFR_LISTDB */ -#define EXITM(excode) { \ - if (!iflag) \ - eatmsg(); \ - exit(excode); \ - } -#endif /* _FFR_LISTDB */ + extern void xclude __P((SM_FILE_T *)); /* Vars needed to link with smutil */ clrbitmap(DontBlameSendmail); @@ -171,11 +153,11 @@ main(argc, argv) { if (strlen(pw->pw_name) > MAXNAME - 1) pw->pw_name[MAXNAME] = '\0'; - snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name); + sm_snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name); } else - snprintf(rnamebuf, sizeof rnamebuf, - "Unknown UID %d", (int) RealUid); + sm_snprintf(rnamebuf, sizeof rnamebuf, + "Unknown UID %d", (int) RealUid); RunAsUserName = RealUserName = rnamebuf; # ifdef LOG_MAIL @@ -185,13 +167,13 @@ main(argc, argv) # endif /* LOG_MAIL */ opterr = 0; - iflag = FALSE; - emptysender = FALSE; - exclude = FALSE; + iflag = false; + emptysender = false; + exclude = false; interval = INTERVAL_UNDEF; *From = '\0'; -#define OPTIONS "a:df:Iilm:r:s:t:Uxz" +#define OPTIONS "a:C:df:Iilm:r:s:t:Uxz" while (mfail == 0 && ufail == 0 && (ch = getopt(argc, argv, OPTIONS)) != -1) @@ -199,7 +181,7 @@ main(argc, argv) switch((char)ch) { case 'a': /* alias */ - cur = (ALIAS *)malloc((u_int)sizeof(ALIAS)); + cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS)); if (cur == NULL) { mfail++; @@ -210,11 +192,13 @@ main(argc, argv) Names = cur; break; -#if _FFR_DEBUG + case 'C': + cfpath = optarg; + break; + case 'd': /* debug mode */ - msglog = &debuglog; + msglog = debuglog; break; -#endif /* _FFR_DEBUG */ case 'f': /* alternate database */ dbfilename = optarg; @@ -222,14 +206,12 @@ main(argc, argv) case 'I': /* backward compatible */ case 'i': /* init the database */ - iflag = TRUE; + iflag = true; break; -#if _FFR_LISTDB case 'l': - lflag = TRUE; /* list the database */ + lflag = true; /* list the database */ break; -#endif /* _FFR_LISTDB */ case 'm': /* alternate message file */ msgfilename = optarg; @@ -247,24 +229,22 @@ main(argc, argv) break; case 's': /* alternate sender name */ - (void) strlcpy(From, optarg, sizeof From); + (void) sm_strlcpy(From, optarg, sizeof From); break; case 't': /* SunOS: -t1d (default expire) */ break; -#if _FFR_BLACKBOX case 'U': /* run as single user mode */ - runasuser = TRUE; + runasuser = true; break; -#endif /* _FFR_BLACKBOX */ case 'x': - exclude = TRUE; + exclude = true; break; case 'z': - emptysender = TRUE; + emptysender = true; break; case '?': @@ -287,11 +267,7 @@ main(argc, argv) if (argc != 1) { - if (!iflag && -#if _FFR_LISTDB - !lflag && -#endif /* _FFR_LISTDB */ - !exclude) + if (!iflag && !lflag && !exclude) usage(); if ((pw = getpwuid(getuid())) == NULL) { @@ -302,16 +278,16 @@ main(argc, argv) name = pw->pw_name; user_info.smdbu_id = pw->pw_uid; user_info.smdbu_group_id = pw->pw_gid; - (void) strlcpy(user_info.smdbu_name, pw->pw_name, - SMDB_MAX_USER_NAME_LEN); + (void) sm_strlcpy(user_info.smdbu_name, pw->pw_name, + SMDB_MAX_USER_NAME_LEN); if (chdir(pw->pw_dir) != 0) { - msglog(LOG_NOTICE, "vacation: no such directory %s.\n", + msglog(LOG_NOTICE, + "vacation: no such directory %s.\n", pw->pw_dir); EXITM(EX_NOINPUT); } } -#if _FFR_BLACKBOX else if (runasuser) { name = *argv; @@ -323,27 +299,51 @@ main(argc, argv) } user_info.smdbu_id = pw->pw_uid; user_info.smdbu_group_id = pw->pw_gid; - (void) strlcpy(user_info.smdbu_name, pw->pw_name, + (void) sm_strlcpy(user_info.smdbu_name, pw->pw_name, SMDB_MAX_USER_NAME_LEN); } -#endif /* _FFR_BLACKBOX */ - else if ((pw = getpwnam(*argv)) == NULL) - { - msglog(LOG_ERR, "vacation: no such user %s.\n", *argv); - EXITM(EX_NOUSER); - } else { - name = pw->pw_name; - if (chdir(pw->pw_dir) != 0) + int err; + SM_CF_OPT_T mbdbname; + SM_MBDB_T user; + + cfpath = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL); + mbdbname.opt_name = "MailboxDatabase"; + mbdbname.opt_val = "pw"; + (void) sm_cf_getopt(cfpath, 1, &mbdbname); + err = sm_mbdb_initialize(mbdbname.opt_val); + if (err != EX_OK) { - msglog(LOG_NOTICE, "vacation: no such directory %s.\n", - pw->pw_dir); + msglog(LOG_ERR, + "vacation: can't open mailbox database: %s.\n", + sm_strexit(err)); + EXITM(err); + } + err = sm_mbdb_lookup(*argv, &user); + if (err == EX_NOUSER) + { + msglog(LOG_ERR, "vacation: no such user %s.\n", *argv); + EXITM(EX_NOUSER); + } + if (err != EX_OK) + { + msglog(LOG_ERR, + "vacation: can't read mailbox database: %s.\n", + sm_strexit(err)); + EXITM(err); + } + name = user.mbdb_name; + if (chdir(user.mbdb_homedir) != 0) + { + msglog(LOG_NOTICE, + "vacation: no such directory %s.\n", + user.mbdb_homedir); EXITM(EX_NOINPUT); } - user_info.smdbu_id = pw->pw_uid; - user_info.smdbu_group_id = pw->pw_gid; - (void) strlcpy(user_info.smdbu_name, pw->pw_name, + user_info.smdbu_id = user.mbdb_uid; + user_info.smdbu_group_id = user.mbdb_gid; + (void) sm_strlcpy(user_info.smdbu_name, user.mbdb_name, SMDB_MAX_USER_NAME_LEN); } @@ -353,14 +353,12 @@ main(argc, argv) msgfilename = VMSG; sff = SFF_CREAT; -#if _FFR_BLACKBOX if (getegid() != getgid()) { - /* Allow a set-group-id vacation binary */ + /* Allow a set-group-ID vacation binary */ RunAsGid = user_info.smdbu_group_id = getegid(); - sff |= SFF_NOPATHCHECK|SFF_OPENASROOT; + sff |= SFF_OPENASROOT; } -#endif /* _FFR_BLACKBOX */ result = smdb_open_database(&Db, dbfilename, O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0), @@ -369,20 +367,16 @@ main(argc, argv) if (result != SMDBE_OK) { msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename, - errstring(result)); + sm_errstring(result)); EXITM(EX_DATAERR); } -#if _FFR_LISTDB if (lflag) { - static void listdb __P((void)); - listdb(); (void) Db->smdb_close(Db); exit(EX_OK); } -#endif /* _FFR_LISTDB */ if (interval != INTERVAL_UNDEF) setinterval(interval); @@ -395,12 +389,12 @@ main(argc, argv) if (exclude) { - xclude(stdin); + xclude(smioin); (void) Db->smdb_close(Db); EXITM(EX_OK); } - if ((cur = (ALIAS *)malloc((u_int)sizeof(ALIAS))) == NULL) + if ((cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS))) == NULL) { msglog(LOG_NOTICE, "vacation: can't allocate memory for username.\n"); @@ -474,16 +468,17 @@ readheaders() extern bool junkmail __P((char *)); extern bool nsearch __P((char *, char *)); - cont = tome = FALSE; - while (fgets(buf, sizeof(buf), stdin) && *buf != '\n') + cont = tome = false; + while (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, sizeof(buf)) && + *buf != '\n') { switch(*buf) { case 'F': /* "From " */ - cont = FALSE; + cont = false; if (strncmp(buf, "From ", 5) == 0) { - bool quoted = FALSE; + bool quoted = false; p = buf + 5; while (*p != '\0') @@ -517,8 +512,8 @@ readheaders() /* ok since both strings have MAXLINE length */ if (*From == '\0') - (void) strlcpy(From, buf + 5, - sizeof From); + (void) sm_strlcpy(From, buf + 5, + sizeof From); if ((p = strchr(buf + 5, '\n')) != NULL) *p = '\0'; if (junkmail(buf + 5)) @@ -528,7 +523,7 @@ readheaders() case 'P': /* "Precedence:" */ case 'p': - cont = FALSE; + cont = false; if (strlen(buf) <= 10 || strncasecmp(buf, "Precedence", 10) != 0 || (buf[10] != ':' && buf[10] != ' ' && @@ -549,20 +544,20 @@ readheaders() case 'c': if (strncasecmp(buf, "Cc:", 3) != 0) break; - cont = TRUE; + cont = true; goto findme; case 'T': /* "To:" */ case 't': if (strncasecmp(buf, "To:", 3) != 0) break; - cont = TRUE; + cont = true; goto findme; default: if (!isascii(*buf) || !isspace(*buf) || !cont || tome) { - cont = FALSE; + cont = false; break; } findme: @@ -619,9 +614,9 @@ nsearch(name, str) strncasecmp(name, s, len) == 0 && (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) && (!isascii(*(s + len)) || !isalnum(*(s + len)))) - return TRUE; + return true; } - return FALSE; + return false; } /* @@ -689,7 +684,7 @@ junkmail(from) ** From site!site!SENDER%site.domain%site.domain@site.domain */ - quot = FALSE; + quot = false; e = from; len = 0; while (*e != '\0' && (quot || !isdelim(*e))) @@ -728,10 +723,10 @@ junkmail(from) sender[MAX_USER_LEN - 1] = '\0'; if (len <= 0) - return FALSE; + return false; #if 0 if (quot) - return FALSE; /* syntax error... */ + return false; /* syntax error... */ #endif /* 0 */ /* test prefixes */ @@ -739,7 +734,7 @@ junkmail(from) { if (len >= cur->len && strncasecmp(cur->name, sender, cur->len) == 0) - return TRUE; + return true; } /* @@ -752,14 +747,14 @@ junkmail(from) */ if (len > MAX_USER_LEN) - return FALSE; + return false; /* test full local parts */ for (cur = ignore; cur->name != NULL; ++cur) { if (len == cur->len && strncasecmp(cur->name, sender, cur->len) == 0) - return TRUE; + return true; } /* test postfixes */ @@ -768,9 +763,9 @@ junkmail(from) if (len >= cur->len && strncasecmp(cur->name, e - cur->len - 1, cur->len) == 0) - return TRUE; + return true; } - return FALSE; + return false; } #define VIT "__VACATION__INTERVAL__TIMER__" @@ -783,7 +778,7 @@ junkmail(from) ** none. ** ** Returns: -** TRUE iff user has gotten a vacation message recently. +** true iff user has gotten a vacation message recently. ** */ @@ -792,7 +787,7 @@ recent() { SMDB_DBENT key, data; time_t then, next; - bool trydomain = FALSE; + bool trydomain = false; int st; char *domain; @@ -823,7 +818,7 @@ recent() memmove(&then, data.data, sizeof(then)); if (next == ONLY_ONCE || then == ONLY_ONCE || then + next > time(NULL)) - return TRUE; + return true; } if ((trydomain = !trydomain) && (domain = strchr(From, '@')) != NULL) @@ -832,7 +827,7 @@ recent() key.size = strlen(domain); } } while (trydomain); - return FALSE; + return false; } /* @@ -913,13 +908,13 @@ setreply(from, when) void xclude(f) - FILE *f; + SM_FILE_T *f; { char buf[MAXLINE], *p; if (f == NULL) return; - while (fgets(buf, sizeof buf, f)) + while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf)) { if ((p = strchr(buf, '\n')) != NULL) *p = '\0'; @@ -949,13 +944,13 @@ sendmessage(myname, msgfn, emptysender) char *msgfn; bool emptysender; { - FILE *mfp, *sfp; + SM_FILE_T *mfp, *sfp; int i; int pvect[2]; char *pv[8]; char buf[MAXLINE]; - mfp = fopen(msgfn, "r"); + mfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, msgfn, SM_IO_RDONLY, NULL); if (mfp == NULL) { if (msgfn[0] == '/') @@ -967,7 +962,7 @@ sendmessage(myname, msgfn, emptysender) } if (pipe(pvect) < 0) { - msglog(LOG_ERR, "vacation: pipe: %s", errstring(errno)); + msglog(LOG_ERR, "vacation: pipe: %s", sm_errstring(errno)); exit(EX_OSERR); } pv[0] = "sendmail"; @@ -983,7 +978,7 @@ sendmessage(myname, msgfn, emptysender) i = fork(); if (i < 0) { - msglog(LOG_ERR, "vacation: fork: %s", errstring(errno)); + msglog(LOG_ERR, "vacation: fork: %s", sm_errstring(errno)); exit(EX_OSERR); } if (i == 0) @@ -991,26 +986,28 @@ sendmessage(myname, msgfn, emptysender) (void) dup2(pvect[0], 0); (void) close(pvect[0]); (void) close(pvect[1]); - (void) fclose(mfp); + (void) sm_io_close(mfp, SM_TIME_DEFAULT); (void) execv(_PATH_SENDMAIL, pv); msglog(LOG_ERR, "vacation: can't exec %s: %s", - _PATH_SENDMAIL, errstring(errno)); + _PATH_SENDMAIL, sm_errstring(errno)); exit(EX_UNAVAILABLE); } /* check return status of the following calls? XXX */ (void) close(pvect[0]); - if ((sfp = fdopen(pvect[1], "w")) != NULL) + if ((sfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *)pvect[1], + SM_IO_WRONLY, NULL)) != NULL) { - (void) fprintf(sfp, "To: %s\n", From); - (void) fprintf(sfp, "Auto-Submitted: auto-generated\n"); - while (fgets(buf, sizeof buf, mfp)) - (void) fputs(buf, sfp); - (void) fclose(mfp); - (void) fclose(sfp); + (void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, "To: %s\n", From); + (void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, + "Auto-Submitted: auto-replied\n"); + while (sm_io_fgets(mfp, SM_TIME_DEFAULT, buf, sizeof buf)) + (void) sm_io_fputs(sfp, SM_TIME_DEFAULT, buf); + (void) sm_io_close(mfp, SM_TIME_DEFAULT); + (void) sm_io_close(sfp, SM_TIME_DEFAULT); } else { - (void) fclose(mfp); + (void) sm_io_close(mfp, SM_TIME_DEFAULT); msglog(LOG_ERR, "vacation: can't open pipe to sendmail"); exit(EX_UNAVAILABLE); } @@ -1020,28 +1017,11 @@ void usage() { msglog(LOG_NOTICE, - "uid %u: usage: vacation [-a alias]%s [-f db] [-i]%s [-m msg] [-r interval] [-s sender] [-t time]%s [-x] [-z] login\n", - getuid(), -#if _FFR_DEBUG - " [-d]", -#else /* _FFR_DEBUG */ - "", -#endif /* _FFR_DEBUG */ -#if _FFR_LISTDB - " [-l]", -#else /* _FFR_LISTDB */ - "", -#endif /* _FFR_LISTDB */ -#if _FFR_BLACKBOX - " [-U]" -#else /* _FFR_BLACKBOX */ - "" -#endif /* _FFR_BLACKBOX */ - ); + "uid %u: usage: vacation [-a alias] [-C cfpath] [-d] [-f db] [-i] [-l] [-m msg] [-r interval] [-s sender] [-t time] [-U] [-x] [-z] login\n", + getuid()); exit(EX_USAGE); } -#if _FFR_LISTDB /* ** LISTDB -- list the contents of the vacation database ** @@ -1066,8 +1046,9 @@ listdb() result = Db->smdb_cursor(Db, &cursor, 0); if (result != SMDBE_OK) { - fprintf(stderr, "vacation: set cursor: %s\n", - errstring(result)); + sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "vacation: set cursor: %s\n", + sm_errstring(result)); return; } @@ -1075,7 +1056,7 @@ listdb() SMDB_CURSOR_GET_NEXT)) == SMDBE_OK) { /* skip magic VIT entry */ - if ((int)db_key.size -1 == strlen(VIT) && + if ((int)db_key.size - 1 == strlen(VIT) && strncmp((char *)db_key.data, VIT, (int)db_key.size - 1) == 0) continue; @@ -1083,8 +1064,9 @@ listdb() /* skip bogus values */ if (db_value.size != sizeof t) { - fprintf(stderr, "vacation: %.*s invalid time stamp\n", - (int) db_key.size, (char *) db_key.data); + sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "vacation: %.*s invalid time stamp\n", + (int) db_key.size, (char *) db_key.data); continue; } @@ -1093,8 +1075,9 @@ listdb() if (db_key.size > 40) db_key.size = 40; - printf("%-40.*s %-10s", - (int) db_key.size, (char *) db_key.data, ctime(&t)); + sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%-40.*s %-10s", + (int) db_key.size, (char *) db_key.data, + ctime(&t)); memset(&db_key, '\0', sizeof db_key); memset(&db_value, '\0', sizeof db_value); @@ -1102,8 +1085,9 @@ listdb() if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY) { - fprintf(stderr, "vacation: get value at cursor: %s\n", - errstring(result)); + sm_io_fprintf(smioerr, SM_TIME_DEFAULT, + "vacation: get value at cursor: %s\n", + sm_errstring(result)); if (cursor != NULL) { (void) cursor->smdbc_close(cursor); @@ -1114,9 +1098,7 @@ listdb() (void) cursor->smdbc_close(cursor); cursor = NULL; } -#endif /* _FFR_LISTDB */ -#if _FFR_DEBUG /* ** DEBUGLOG -- write message to standard error ** @@ -1132,7 +1114,7 @@ listdb() */ /*VARARGS2*/ -static void +static SYSLOG_RET_T #ifdef __STDC__ debuglog(int i, const char *fmt, ...) #else /* __STDC__ */ @@ -1143,67 +1125,10 @@ debuglog(i, fmt, va_alist) #endif /* __STDC__ */ { - VA_LOCAL_DECL + SM_VA_LOCAL_DECL - VA_START(fmt); - vfprintf(stderr, fmt, ap); - VA_END; -} -#endif /* _FFR_DEBUG */ - -/*VARARGS1*/ -void -#ifdef __STDC__ -message(const char *msg, ...) -#else /* __STDC__ */ -message(msg, va_alist) - const char *msg; - va_dcl -#endif /* __STDC__ */ -{ - const char *m; - VA_LOCAL_DECL - - m = msg; - if (isascii(m[0]) && isdigit(m[0]) && - isascii(m[1]) && isdigit(m[1]) && - isascii(m[2]) && isdigit(m[2]) && m[3] == ' ') - m += 4; - VA_START(msg); - (void) vfprintf(stderr, m, ap); - VA_END; - (void) fprintf(stderr, "\n"); -} - -/*VARARGS1*/ -void -#ifdef __STDC__ -syserr(const char *msg, ...) -#else /* __STDC__ */ -syserr(msg, va_alist) - const char *msg; - va_dcl -#endif /* __STDC__ */ -{ - const char *m; - VA_LOCAL_DECL - - m = msg; - if (isascii(m[0]) && isdigit(m[0]) && - isascii(m[1]) && isdigit(m[1]) && - isascii(m[2]) && isdigit(m[2]) && m[3] == ' ') - m += 4; - VA_START(msg); - (void) vfprintf(stderr, m, ap); - VA_END; - (void) fprintf(stderr, "\n"); -} - -void -dumpfd(fd, printclosed, logit) - int fd; - bool printclosed; - bool logit; -{ - return; + SM_VA_START(ap, fmt); + sm_io_vfprintf(smioerr, SM_TIME_DEFAULT, fmt, ap); + SM_VA_END(ap); + SYSLOG_RET; } |