Apply by doing:
	cd /usr/src
	patch -p0 < 028_sendmail.patch

And then rebuild, install and restart sendmail:
	cd gnu/usr.sbin/sendmail
	make obj
	make depend
	make
	make install
	kill -HUP `sed 1q /var/run/sendmail.pid`

Index: gnu/usr.sbin/sendmail/KNOWNBUGS
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/KNOWNBUGS,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- gnu/usr.sbin/sendmail/KNOWNBUGS	2000/04/02 19:48:09	1.2
+++ gnu/usr.sbin/sendmail/KNOWNBUGS	2001/01/15 21:08:50	1.3
@@ -1,7 +1,6 @@
 
 
 	     K N O W N   B U G S   I N   S E N D M A I L
-			     (for 8.9.3)
 
 
 The following are bugs or deficiencies in sendmail that I am aware of
@@ -13,7 +12,21 @@
 
 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:
+
+  timeout waiting for input from local during Draining Input
+
+  Make sure that the program does not generate output beyond a
+  status message (corresponding to the exit status).  This may
+  require a wrapper around the actual program to redirect output
+  to /dev/null.
+
+  Such a problem has been reported for bulk_mailer.
+
 * Null bytes are not handled properly in headers.
 
   Sendmail should handle full binary data.  As it stands, it handles
@@ -198,4 +211,4 @@
   state.  This option and it's use is deprecated and will be removed from a
   future version of sendmail.
 
-$Revision: 1.2 $, Last updated $Date: 2000/04/02 19:48:09 $
+$Revision: 1.3 $, Last updated $Date: 2001/01/15 21:08:50 $
Index: gnu/usr.sbin/sendmail/LICENSE
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/LICENSE,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -r1.2 -r1.4
--- gnu/usr.sbin/sendmail/LICENSE	2000/04/02 19:48:09	1.2
+++ gnu/usr.sbin/sendmail/LICENSE	2001/02/28 02:43:48	1.4
@@ -33,7 +33,7 @@
    forth as paragraph 6 below, in the documentation and/or other materials
    provided with the distribution.  For the purposes of binary distribution
    the "Copyright Notice" refers to the following language:
-   "Copyright (c) 1998-2000 Sendmail, Inc.  All rights reserved."
+   "Copyright (c) 1998-2001 Sendmail, Inc.  All rights reserved."
 
 4. Neither the name of Sendmail, Inc. nor the University of California nor
    the names of their contributors may be used to endorse or promote
@@ -76,4 +76,4 @@
    (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.2 $, Last updated $Date: 2000/04/02 19:48:09 $
+$Revision: 1.4 $, Last updated $Date: 2001/02/28 02:43:48 $
Index: gnu/usr.sbin/sendmail/README
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/README,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- gnu/usr.sbin/sendmail/README	2000/04/07 19:20:25	1.3
+++ gnu/usr.sbin/sendmail/README	2001/01/15 21:08:50	1.4
@@ -47,10 +47,9 @@
 
 You will probably have to tweak this for your environment (for example,
 some systems put the spool directory into /usr/spool instead of
-/var/spool and use /etc/mail for aliases file instead of /etc).  If you
-set the RunAsUser option in your sendmail.cf, the /var/spool/mqueue
-directory will have to be owned by the RunAsUser user.  As a general rule,
-after you have compiled sendmail, run the command
+/var/spool).  If you set the RunAsUser option in your sendmail.cf, the
+/var/spool/mqueue directory will have to be owned by the RunAsUser user.
+As a general rule, after you have compiled sendmail, run the command
 
 	sendmail -v -bi
 
@@ -90,8 +89,8 @@
 delivery.
 
 Other files affected by this strengthened security include class
-files (i.e. Fw /etc/sendmail.cw), persistent host status files, and
-the files specified by the ErrorHeader and HelpFile options.  Similar
+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.
 
@@ -187,6 +186,7 @@
 	RFC2033 Local Mail Transfer Protocol (LMTP)
 	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
 
 Other standards that may be of interest (but which are less directly
@@ -200,6 +200,27 @@
 RFC1035.
 
 
++---------+
+| WARNING |
++---------+
+
+Since sendmail 8.11 and later includes hooks to cryptography, the
+following information from OpenSSL applies to sendmail as well.
+
+PLEASE REMEMBER THAT EXPORT/IMPORT AND/OR USE OF STRONG CRYPTOGRAPHY
+SOFTWARE, PROVIDING CRYPTOGRAPHY HOOKS OR EVEN JUST COMMUNICATING
+TECHNICAL DETAILS ABOUT CRYPTOGRAPHY SOFTWARE IS ILLEGAL IN SOME
+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.
+
+If you use OpenSSL then make sure you read their README file which
+contains information about patents etc.
+
+
 +-------------------+
 | DATABASE ROUTINES |
 +-------------------+
@@ -364,4 +385,4 @@
 test		Some test scripts (currently only for compilation aids).
 vacation	Source for the vacation program.  NOT PART OF SENDMAIL!
 
-$Revision: 1.3 $, Last updated $Date: 2000/04/07 19:20:25 $
+$Revision: 1.4 $, Last updated $Date: 2001/01/15 21:08:50 $
Index: gnu/usr.sbin/sendmail/RELEASE_NOTES
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/RELEASE_NOTES,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/RELEASE_NOTES	2000/04/07 19:20:26	1.2
+++ gnu/usr.sbin/sendmail/RELEASE_NOTES	2001/05/29 01:31:10	1.5
@@ -1,18 +1,628 @@
 			SENDMAIL RELEASE NOTES
-      $Sendmail: RELEASE_NOTES,v 8.561 2000/04/06 23:51:49 gshapiro Exp $
+      $Sendmail: RELEASE_NOTES,v 8.561.2.5.2.235 2001/05/27 21:39:16 gshapiro 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.11.4/8.11.4	2001/05/28
+	Clean up signal handling routines to reduce the chances of heap
+		corruption and other potential race conditions.
+		Terminating and restarting the daemon may not be
+		instantaneous due to this change.  Also, non-root users can
+		no longer send out-of-band signals.  Problem reported by
+		Michal Zalewski of BindView.
+	If LogLevel is greater than 9 and SASL fails to negotiate an
+		encryption layer, avoid core dump logging the encryption
+		strength.  Problem noted by Miroslav Zubcic of Crol.
+	If a server offers "AUTH=" and "AUTH " and the list of mechanisms is
+		different in those two lines, sendmail might not have
+		recognized (and used) all of the offered mechanisms.
+	Fix an IP address lookup problem on Solaris 2.0 - 2.3.  Patch
+		from Kenji Miyake.
+	This time, really don't use the .. directory when expanding
+		QueueDirectory wildcards.
+	If a process is interrupted while closing a map, don't try to close
+		the same map again while exiting.
+	Allow local mailers (F=l) to contact remote hosts (e.g., via
+		LMTP).  Problem noted by Norbert Klasen of the University
+		of Tuebingen.
+	If Timeout.QueueReturn was set to a value less the time it took
+		to write a new queue file (e.g., 0 seconds), the bounce
+		message would be lost.  Problem noted by Lorraine L Goff of
+		Oklahoma State University.
+	Pass map argument vector into map rewriting engine for the regex
+		and prog map types.  Problem noted by Stephen Gildea of
+		InTouch Systems, Inc.
+	When closing an LDAP map due to a temporary error, close all of the
+		other LDAP maps which share the original map's connection
+		to the LDAP server.  Patch from Victor Duchovni of
+		Morgan Stanley.
+	To detect changes of NDBM aliases files check the timestamp of the
+		.pag file instead of the .dir file.  Problem noted by Neil
+		Rickert of Northern Illinois University.
+	Don't treat temporary hesiod lookup failures as permanent.  Patch
+		from Werner Wiethege.
+	If ClientPortOptions is set, make sure to create the outgoing socket
+		with the family set in that option.  Patch from Sean Farley.
+	Avoid a segmentation fault trying to dereference a NULL pointer
+		when logging a MaxHopCount exceeded error with an empty
+		recipient list.  Problem noted by Chris Adams of HiWAAY
+		Internet Services.
+	Fix DSN for "Too many hops" bounces.  Problem noticed by Ulrich
+		Windl of the Universitaet Regensburg.
+	Fix DSN for "mail loops back to me" bounces.  Problem noticed by
+		Kari Hurtta of the Finnish Meteorological Institute.
+	Portability:
+		OpenBSD has a broken setreuid() implementation.
+	CONFIG: Undo change from 8.11.1: change 501 SMTP reply code back
+		to 553 since it is allowed by DRUMS.
+	CONFIG: Add OSTYPE(freebsd4) for FreeBSD 4.X.
+	DEVTOOLS: install.sh did not properly handle paths in the source
+		file name argument.  Noted by Kari Hurtta of the Finnish
+		Meteorological Institute.
+	DEVTOOLS: Add FAST_PID_RECYCLE to compile time options for OpenBSD
+		since it generates random process ids.
+	PRALIASES: Add back adaptive algorithm to deal with different endings
+		of entries in the database (with/without trailing '\0').
+		Patch from John Beck of Sun Microsystems.
+	New Files:
+		cf/ostype/freebsd4.m4
+
+8.11.3/8.11.3	2001/02/27
+	Prevent a segmentation fault when a bogus value was used in the
+		LDAPDefaultSpec option's -r, -s, or -M flags and if a bogus
+		option was used.  Problem noted by Allan E Johannesen of
+		Worcester Polytechnic Institute.
+	Prevent "token too long" message by shortening {currHeader} which
+		could be too long if the last copied character was a quote.
+		Problem detected by Jan Krueger of digitalanswers
+		communications consulting gmbh.
+	Additional IPv6 check for unspecified addresses.  Patch from
+		Jun-ichiro itojun Hagino of the KAME Project.
+	Do not ignore the ClientPortOptions setting if DaemonPortOptions
+		Modifier=b (bind to same interface) is set and the
+		connection came in from the command line.
+	Do not bind to the loopback address if DaemonPortOptions
+		Modifier=b (bind to same interface) is set.  Patch from
+		John Beck of Sun Microsystems.
+	Properly deal with open failures on non-optional maps used in
+		check_* rulesets by returning a temporary failure.
+	Buffered file I/O files were not being properly fsync'ed to disk
+		when they were committed.
+	Properly encode '=' for the AUTH= parameter of the MAIL command.
+		Problem noted by Hadmut Danisch.
+	Under certain circumstances the macro {server_name} could be set
+		to the wrong hostname (of a previous connection), which may
+		cause some rulesets to return wrong results.  This would
+		usually cause mail to be queued up and delivered later on.
+	Ignore F=z (LMTP) mailer flag if $u is given in the mailer A=
+		equate.  Problem noted by Motonori Nakamura of Kyoto
+		University.
+	Work around broken accept() implementations which only partially
+		fill in the peer address if the socket is closed before
+		accept() completes.
+	Return an SMTP "421" temporary failure if the data file can't be
+		opened where the "354" reply would normally be given.
+	Prevent a CPU loop in trying to expand a macro which doesn't exist
+		in a queue run.  Problem noted by Gordon Lack of Glaxo
+		Wellcome.
+	If delivering via a program and that program exits with EX_TEMPFAIL,
+		note that fact for the mailq display instead of just showing
+		"Deferred".  Problem noted by Motonori Nakamura of Kyoto
+		University.
+	If doing canonification via /etc/hosts, try both the fully
+		qualified hostname as well as the first portion of the
+		hostname.  Problem noted by David Bremner of the
+		University of New Brunswick.
+	Portability:
+		Fix a compilation problem for mail.local and rmail if SFIO
+			is in use.  Problem noted by Auteria Wally
+			Winzer Jr. of Champion Nutrition.
+		IPv6 changes for platforms using KAME.  Patch from
+			Jun-ichiro itojun Hagino of the KAME Project.
+		OpenBSD 2.7 and higher has srandomdev(3).  OpenBSD 2.8 and
+			higher has BSDI-style login classes.  Patch from
+			Todd C.  Miller of Courtesan Consulting.
+		Unixware 7.1.1 doesn't allow h_errno to be set directly if
+			sendmail is being compiled with -kthread.  Problem
+			noted by Orion Poplawski of CQG, Inc.
+	CONTRIB: buildvirtuser: Substitute current domain for $DOMAIN and
+		current left hand side for $LHS in virtuser files.
+	DEVTOOLS: Do not pass make targets to recursive Build invocations.
+		Problem noted by Jeff Bronson of J.D. Bronson, Inc.
+	MAIL.LOCAL: In LMTP mode, do not return errors regarding problems
+		storing the temporary message file until after the remote
+		side has sent the final DATA termination dot.  Problem
+		noted by Allan E Johannesen of Worcester Polytechnic
+		Institute.
+	MAIL.LOCAL: If LMTP mode is set, give a temporary error if users
+		are also specified on the command line.  Patch from
+		Motonori Nakamura of Kyoto University.
+	PRALIASES: Skip over AliasFile specifications which aren't based on
+		database files (i.e., only show dbm, hash, and btree).
+	Renamed Files:
+		devtools/OS/OSF1.V5.0 => devtools/OS/OSF1.V5.x
+
+8.11.2/8.11.2	2000/12/29
+	Prevent a segmentation fault when trying to set a class in
+		address test mode due to a negative array index.  Audit
+		other array indexing.  This bug is not believed to be
+		exploitable.  Noted by Michal Zalewski of the "Internet for
+		Schools" project (IdS).
+	Add an FFR (for future release) to drop privileges when using
+		address test mode.  This will be turned on in 8.12. It can
+		be enabled by compiling with:
+		APPENDDEF(`conf_sendmail_ENVDEF', `-D_FFR_TESTMODE_DROP_PRIVS')
+		in your devtools/Site/site.config.m4 file.  Suggested by
+		Michal Zalewski of the "Internet for Schools" project (IdS).
+	Fix potential problem with Cyrus-SASL security layer which may have
+		caused I/O errors, especially for mechanism DIGEST-MD5.
+	When QueueSortOrder was set to host, sendmail might not read
+		enough of the queue file to determine the host, making the
+		sort sub-optimal.  Problem noted by Jeff Earickson of
+		Colby College.
+	Don't issue DSNs for addresses which use the NOTIFY parameter (per
+		RFC 1891) but don't have FAILURE as value.
+	Initialize Cyrus-SASL library before the SMTP daemon is started.
+		This implies that every change to SASL related files requires
+		a restart of the daemon, e.g., Sendmail.conf, new SASL
+		mechanisms (in form of shared libraries).
+	Properly set the STARTTLS related macros during a queue run for
+		a cached connection.  Bug reported by Michael Kellen of
+		NxNetworks, Inc.
+	Log the server name in relay= for ruleset tls_server instead of the
+		client name.
+	Include original length of bad field/header when reporting
+		MaxMimeHeaderLength problems.  Requested by Ulrich Windl of
+		the Universitat Regensburg.
+	Fix delivery to set-user-ID files that are expanded from aliases in
+		DeliveryMode queue.  Problem noted by Ric Anderson of the
+		University of Arizona.
+	Fix LDAP map -m (match only) flag.  Problem noted by Jeff Giuliano
+		of Collective Technologies.
+	Avoid using a negative argument for sleep() calls when delaying answers
+		to EXPN/VRFY commands on systems which respond very slowly.
+		Problem noted by Mikolaj J. Habryn of Optus Internet
+		Engineering.
+	Make sure the F=u flag is set in the default prog mailer
+		definition.  Problem noted by Kari Hurtta of the Finnish
+		Meteorological Institute.
+	Fix IPv6 check for unspecified addresses.  Patch from
+		Jun-ichiro itojun Hagino of the KAME Project.
+	Fix return values for IRIX nsd map.  From Kari Hurtta of the Finnish
+		Meteorological Institute.
+	Fix parsing of DaemonPortOptions and ClientPortOptions.  Read all
+		of the parameters to find Family= setting before trying to
+		interpret Addr= and Port=.  Problem noted by Valdis
+		Kletnieks of Virginia Tech.
+	When delivering to a file directly from an alias, do not call
+		initgroups(); instead use the DefaultUser group information.
+		Problem noted by Marc Schaefer of ALPHANET NF.
+	RunAsUser now overrides the ownership of the control socket, if
+		created.  Otherwise, sendmail can not remove it upon
+		close.  Problem noted by Werner Wiethege.
+	Fix ConnectionRateThrottle counting as the option is the number of
+		overall connections, not the number of connections per
+		socket.  A future version may change this to per socket
+		counting.
+	Portability:
+		Clean up libsmdb so it functions properly on platforms
+			where sizeof(u_int32_t) != sizeof(size_t).  Problem
+			noted by Rein Tollevik of Basefarm AS.
+		Fix man page formatting for compatibility with Solaris'
+			whatis.  From Stephen Gildea of InTouch Systems, Inc.
+		UnixWare 7 includes snprintf() support.  From Larry
+			Rosenman.
+		IPv6 changes for platforms using KAME.  Patch from
+			Jun-ichiro itojun Hagino of the KAME Project.
+		Avoid a typedef compile conflict with Berkeley DB 3.X and
+			Solaris 2.5 or earlier.  Problem noted by Bob Hughes
+			of Pacific Access.
+		Add preliminary support for AIX 5.  Contributed by
+			Valdis Kletnieks of Virginia Tech.
+		Solaris 9 load average support from Andrew Tucker of Sun
+			Microsystems.
+	CONFIG: Reject addresses of the form a!b if FEATURE(`nouucp', `r')
+		is used.  Problem noted by Phil Homewood of Asia Online,
+		patch from Neil Rickert of Northern Illinois University.
+	CONFIG: Change the default DNS based blacklist server for
+		FEATURE(`dnsbl') to blackholes.mail-abuse.org.
+	CONFIG: Deal correctly with the 'C' flag in {daemon_flags}, i.e.,
+		implicitly assume canonical host names.
+	CONFIG: Deal with "::" in IPv6 addresses for access_db.  Based on
+		patch by Motonori Nakamura of Kyoto University.
+	CONFIG: New OSTYPE(`aix5') contributed by Valdis Kletnieks of
+		Virginia Tech.
+	CONFIG: Pass the illegal header form <list:;> through untouched
+		instead of making it worse.  Problem noted by Motonori
+		Nakamura of Kyoto University.
+	CONTRIB: Added buildvirtuser (see `perldoc contrib/buildvirtuser`).
+	CONTRIB: qtool.pl: An empty queue is not an error.  Problem noted
+		by Jan Krueger of digitalanswers communications consulting
+		gmbh.
+	CONTRIB: domainmap.m4: Handle domains with '-' in them.  From Mark
+		Roth of the University of Illinois at Urbana-Champaign.
+	DEVTOOLS: Change the internal devtools OS, REL, and ARCH m4
+		variables into bldOS, bldREL, and bldARCH to prevent
+		namespace collisions.  Problem noted by Motonori Nakamura
+		of Kyoto University.
+	RMAIL: Undo the 8.11.1 change to use -G when calling sendmail.  It
+		causes some changes in behavior and may break rmail for
+		installations where sendmail is actually a wrapper to
+		another MTA.  The change will re-appear in a future
+		version.
+	SMRSH: Use the vendor supplied directory on HPUX 10.X, HPUX 11.X,
+		and SunOS 5.8.  Requested by Jeff A. Earickson of Colby
+		College and John Beck of Sun Microsystems.
+	VACATION: Fix pattern matching for addresses to ignore.
+	VACATION: Don't reply to addresses of the form owner-*
+		or *-owner.
+	New Files:
+		cf/ostype/aix5.m4
+		contrib/buildvirtuser
+		devtools/OS/AIX.5.0
+
+8.11.1/8.11.1	2000/09/27
+	Fix SMTP EXPN command output if the address expands to a single
+		name.  Fix from John Beck of Sun Microsystems.
+	Don't try STARTTLS in the client if the PRNG has not been properly
+		seeded.  This problem only occurs on systems without
+		/dev/urandom.  Problem detected by Jan Krueger of
+		digitalanswers communications consulting gmbh and
+		Neil Rickert of Northern Illinois University.
+	Don't use the . and .. directories when expanding QueueDirectory
+		wildcards.
+	Do not try to cache LDAP connections across processes as a parent
+		process may close the connection before the child process
+		has completed.  Problem noted by Lai Yiu Fai of the Hong
+		Kong University of Science and Technology and Wolfgang
+		Hottgenroth of UUNET.
+	Use Timeout.fileopen to limit the amount of time spent trying to
+		read the LDAP secret from a file.
+	Prevent SIGTERM from removing a command line submitted item after
+		the user submits the message and before the first delivery
+		attempt completes.  Problem noted by Max France of AlphaNet.
+		Fix from Neil Rickert of Northern Illinois University.
+	Deal correctly with MaxMessageSize restriction if message size is
+		greater than 2^31.  Problem noted by Tim "Darth Dice" Bosserman
+		of EarthLink.
+	Turn off queue checkpointing if CheckpointInterval is set to zero.
+	Treat an empty home directory (from getpw*() or $HOME) as
+		non-existent instead of treating it as /.  Problem noted by
+		Todd C. Miller of Courtesan Consulting.
+	Don't drop duplicate headers when reading a queued item.  Problem
+		noted by Motonori Nakamura of Kyoto University.
+	Avoid bogus error text when logging the savemail panic "cannot
+		save rejected email anywhere".  Problem noted by Marc G.
+		Fournier of Acadia University.
+	If an LDAP search fails because the LDAP server went down, close
+		the map so subsequent searches reopen the map.  If there are
+		multiple LDAP servers, the down server will be skipped and
+		one of the others may be able to take over.
+	Set the ${load_avg} macro to the current load average, not the
+		previous load average query result.
+	If a non-optional map used in a check_* ruleset can't be opened,
+		return a temporary failure to the remote SMTP client
+		instead of ignoring the map.  Problem noted by Allan E
+		Johannesen of Worcester Polytechnic Institute.
+	Avoid a race condition when queuing up split envelopes by saving
+		the split envelopes before the original envelope.
+	Fix a bug in the PH_MAP code which caused mail to bounce instead of
+		defer if the PH server could not be contacted.  From Mark
+		Roth of the University of Illinois at Urbana-Champaign.
+	Prevent QueueSortOrder=Filename from interfering with -qR, -qS, and
+		ETRN.  Problem noted by Erik R. Leo of SoVerNet.
+	Change error code for unrecognized parameters to the SMTP MAIL and
+		RCPT commands from 501 to 555 per RFC 1869.  Problem
+		reported to Postfix by Robert Norris of Monash University.
+	Prevent overwriting the argument of -B on certain OS.  Problem
+		noted by Matteo Gelosa of I.NET S.p.A.
+	Use the proper routine for freeing memory with Netscape's LDAP
+		client libraries.  Patch from Paul Hilchey of the
+		University of British Columbia.
+	Portability:
+		Move the NETINET6 define to devtools/OS/SunOS.5.{8,9}
+			instead of defining it in conf.h so users can
+			override the setting.  Suggested by
+			Henrik Nordstrom of Ericsson.
+		On HP-UX 10.X and 11.X, use /usr/sbin/sendmail instead of
+			/usr/lib/sendmail for rmail and vacation.  From
+			Jeff A. Earickson of Colby College.
+		On HP-UX 11.X, use /usr/sbin instead of /usr/libexec (which
+			does not exist).  From Jeff A. Earickson of Colby
+			College.
+		Avoid using the UCB subsystem on NCR MP-RAS 3.x.  From
+			Tom Moore of NCR.
+		NeXT 3.X and 4.X installs man pages in /usr/man.  From
+			Hisanori Gogota of NTT/InterCommunicationCenter.
+		Solaris 8 and later include /var/run.  The default PID file
+			location is now /var/run/sendmail.pid.  From John
+			Beck of Sun Microsystems.
+		SFIO includes snprintf() for those operating systems
+			which do not.  From Todd C. Miller of Courtesan
+			Consulting.
+	CONFIG: Use the result of _CERT_REGEX_SUBJECT_ not {cert_subject}.
+		Problem noted by Kaspar Brand of futureLab AG.
+	CONFIG: Change 553 SMTP reply code to 501 to avoid problems with
+		errors in the MAIL address.
+	CONFIG: Fix FEATURE(nouucp) usage in example .mc files.  Problem
+		noted by Ron Jarrell of Virginia Tech.
+	CONFIG: Add support for Solaris 8 (and later) as OSTYPE(solaris8).
+		Contributed by John Beck of Sun Microsystems.
+	CONFIG: Set confFROM_HEADER such that the mail hub can possibly add
+		GECOS information for an address.  This more closely
+		matches pre-8.10 nullclient behavior.  From Per Hedeland of
+		Ericsson.
+	CONFIG: Fix MODIFY_MAILER_FLAGS(): apply the flag modifications for
+		SMTP to all *smtp* mailers and those for RELAY to the relay
+		mailer as described in cf/README.
+	MAIL.LOCAL: Open the mailbox as the recipient not root so quotas
+		are obeyed.  Problem noted by Damian Kuczynski of NIK.
+	MAKEMAP: Do not change a map's owner to the TrustedUser if using
+		makemap to 'unmake' the map.
+	RMAIL: Avoid overflowing the list of recipients being passed to
+		sendmail.
+	RMAIL: Invoke sendmail with '-G' to indicate this is a gateway
+		submission.  Problem noted by Kari Hurtta of the Finnish
+		Meteorological Institute.
+	VACATION: Read the complete message to avoid "broken pipe" signals.
+	VACATION: Do not cut off vacation.msg files which have a single
+		dot as the only character on the line.
+	New Files:
+		cf/ostype/solaris8.m4
+
+8.11.0/8.11.0	2000/07/19
+	SECURITY: If sendmail is installed as a non-root set-user-ID binary
+		(not the normal case), some operating systems will still
+		keep a saved-uid of the effective-uid when sendmail tries
+		to drop all of its privileges.  If sendmail needs to drop
+		these privileges and the operating system doesn't set the
+		saved-uid as well, exit with an error.  Problem noted by
+		Kari Hurtta of the Finnish Meteorological Institute.
+	SECURITY: sendmail depends on snprintf() NUL terminating the string
+		it populates.  It is possible that some broken
+		implementations of snprintf() exist that do not do this.
+		Systems in this category should compile with
+		-DSNPRINTF_IS_BROKEN=1.  Use test/t_snprintf.c to test your
+		system and report broken implementations to
+		sendmail-bugs@sendmail.org and your OS vendor.  Problem
+		noted by Slawomir Piotrowski of TELSAT GP.
+	Support SMTP Service Extension for Secure SMTP (RFC 2487) (STARTTLS).
+		Implementation influenced by the example programs of
+		OpenSSL and the work of Lutz Jaenicke of TU Cottbus.
+	Add new STARTTLS related options CACERTPath, CACERTFile,
+		ClientCertFile, ClientKeyFile, DHParameters, RandFile,
+		ServerCertFile, and ServerKeyFile.  These are documented in
+		cf/README and doc/op/op.*.
+	New STARTTLS related macros: ${cert_issuer}, ${cert_subject},
+		${tls_version}, ${cipher}, ${cipher_bits}, ${verify},
+		${server_name}, and ${server_addr}.  These are documented
+		in cf/README and doc/op/op.*.
+	Add support for the Entropy Gathering Daemon (EGD) for better
+		random data.
+	New DontBlameSendmail option InsufficientEntropy for systems which
+		don't properly seed the PRNG for OpenSSL but want to
+		try to use STARTTLS despite the security problems.
+	Support the security layer in SMTP AUTH for mechanisms which
+		support encryption.  Based on code contributed by Tim
+		Martin of CMU.
+	Add new macro ${auth_ssf} to reflect the SMTP AUTH security
+		strength factor.
+	LDAP's -1 (single match only) flag was not honored if the -z
+		(delimiter) flag was not given.  Problem noted by ST Wong of
+		the Chinese University of Hong Kong.  Fix from Mark Adamson
+		of CMU.
+	Add more protection from accidentally tripping OpenLDAP 1.X's
+		ld_errno == LDAP_DECODING_ERROR hack on ldap_next_attribute().
+		Suggested by Kurt Zeilenga of OpenLDAP.
+	Fix the default family selection for DaemonPortOptions.  As
+		documented, unless a family is specified in a
+		DaemonPortOptions option, "inet" is the default.  It is
+		also the default if no DaemonPortOptions value is set.
+		Therefore, IPv6 users should configure additional sockets
+		by adding DaemonPortOptions settings with Family=inet6 if
+		they wish to also listen on IPv6 interfaces.  Problem noted
+		by Jun-ichiro itojun Hagino of the KAME Project.
+	Set ${if_family} when setting ${if_addr} and ${if_name} to reflect
+		the interface information for an outgoing connection.
+		Not doing so was creating a mismatch between the socket
+		family and address used in subsequent connections if the
+		M=b modifier was set in DaemonPortOptions.  Problem noted
+		by John Beck of Sun Microsystems.
+	If DaemonPortOptions modifier M=b is used, determine the socket
+		family based on the IP address.  ${if_family} is no longer
+		persistent (i.e., saved in qf files).  Patch from John Beck
+		of Sun Microsystems.
+	sendmail 8.10 and 8.11 reused the ${if_addr} and ${if_family}
+		macros for both the incoming interface address/family and
+		the outgoing interface address/family.  In order for M=b
+		modifier in DaemonPortOptions to work properly, preserve
+		the incoming information in the queue file for later
+		delivery attempts.
+	Use SMTP error code and enhanced status code from check_relay in
+		responses to commands.  Problem noted by Jeff Wasilko of
+		smoe.org.
+	Add more vigilance in checking for putc() errors on output streams
+		to protect from a bug in Solaris 2.6's putc().  Problem
+		noted by Graeme Hewson of Oracle.
+	The LDAP map -n option (return attribute names only) wasn't working.
+		Problem noted by Ajay Matia.
+	Under certain circumstances, an address could be listed as deferred
+		but would be bounced back to the sender as failed to be
+		delivered when it really should have been queued.  Problem
+		noted by Allan E Johannesen of Worcester Polytechnic Institute.
+	Prevent a segmentation fault in a child SMTP process from getting
+		the SMTP transaction out of sync.  Problem noted by Per
+		Hedeland of Ericsson.
+	Turn off RES_DEBUG if SFIO is defined unless SFIO_STDIO_COMPAT
+		is defined to avoid a core dump due to incompatibilities
+		between sfio and stdio.  Problem noted by Neil Rickert
+		of Northern Illinois University.
+	Don't log useless envelope ID on initial connection log.  Problem
+		noted by Kari Hurtta of the Finnish Meteorological Institute.
+	Convert the free disk space shown in a control socket status query
+		to kilobyte units.
+	If TryNullMXList is True and there is a temporary DNS failure
+		looking up the hostname, requeue the message for a later
+		attempt.  Problem noted by Ari Heikkinen of Pohjois-Savo
+		Polytechnic.
+	Under the proper circumstances, failed connections would be recorded
+		as "Bad file number" instead of "Connection failed" in the
+		queue file and persistent host status.  Problem noted by
+		Graeme Hewson of Oracle.
+	Avoid getting into an endless loop if a non-hoststat directory exists
+		within the hoststatus directory (e.g., lost+found).
+		Patch from Valdis Kletnieks of Virginia Tech.
+	Make sure Timeout.queuereturn=now returns a bounce message to the
+		sender.  Problem noted by Per Hedeland of Ericsson.
+	If a message data file can't be opened at delivery time, panic and
+		abort the attempt instead of delivering a message that
+		states "<<< No Message Collected >>>".
+	Fixup the GID checking code from 8.10.2 as it was overly
+		restrictive.  Problem noted by Mark G. Thomas of Mark
+		G. Thomas Consulting.
+	Preserve source port number instead of replacing it with the ident
+		port number (113).
+	Document the queue status characters in the mailq man page.
+		Suggested by Ulrich Windl of the Universitat Regensburg.
+	Process queued items in which none of the recipient addresses have
+		host portions (or there are no recipients).  Problem noted
+		by Valdis Kletnieks of Virginia Tech.
+	If a cached LDAP connection is used for multiple maps, make sure
+		only the first to open the connection is allowed to close
+		it so a later map close doesn't break the connection for
+		other maps.  Problem noted by Wolfgang Hottgenroth of UUNET.
+	Netscape's LDAP libraries do not support Kerberos V4
+		authentication.  Patch from Rainer Schoepf of the
+		University of Mainz.
+	Provide workaround for inconsistent handling of data passed
+		via callbacks to Cyrus SASL prior to version 1.5.23.
+	Mention ENHANCEDSTATUSCODES in the SMTP HELP helpfile.  Omission
+		noted by Ulrich Windl of the Universitat Regensburg.
+	Portability:
+		Add the ability to read IPv6 interface addresses into class
+			'w' under FreeBSD (and possibly others).  From Jun
+			Kuriyama of IMG SRC, Inc. and the FreeBSD Project.
+		Replace code for finding the number of CPUs on HPUX.
+		NCRUNIX MP-RAS 3.02 SO_REUSEADDR socket option does not
+			work properly causing problems if the accept()
+			fails and the socket needs to be reopened.  Patch
+			from Tom Moore of NCR.
+		NetBSD uses a .0 extension of formatted man pages.  From
+			Andrew Brown of Crossbar Security.
+		Return to using the IPv6 AI_DEFAULT flag instead of AI_V4MAPPED
+			for calls to getipnodebyname().  The Linux
+			implementation is broken so AI_ADDRCONFIG is stripped
+			under Linux.  From John Beck of Sun Microsystems and
+			John Kennedy of Cal State University, Chico.
+	CONFIG: Catch invalid addresses containing a ',' at the wrong place.
+		Patch from Neil Rickert of Northern Illinois University.
+	CONFIG: New variables for the new sendmail options:
+		confCACERT_PATH			CACERTPath
+		confCACERT			CACERTFile
+		confCLIENT_CERT			ClientCertFile
+		confCLIENT_KEY			ClientKeyFile
+		confDH_PARAMETERS		DHParameters
+		confRAND_FILE			RandFile
+		confSERVER_CERT			ServerCertFile
+		confSERVER_KEY			ServerKeyFile
+	CONFIG: Provide basic rulesets for TLS policy control and add new
+		tags to the access database to support these policies.  See
+		cf/README for more information.
+	CONFIG: Add TLS information to the Received: header.
+	CONFIG: Call tls_client ruleset from check_mail in case it wasn't
+		called due to a STARTTLS command.
+	CONFIG: If TLS_PERM_ERR is defined, TLS related errors are permanent
+		instead of temporary.
+	CONFIG: FEATURE(`relay_hosts_only') didn't work in combination with
+		the access map and relaying to a domain without using a To:
+		tag.  Problem noted by Mark G. Thomas of Mark G. Thomas
+		Consulting.
+	CONFIG: Set confEBINDIR to /usr/sbin to match the devtools entry in
+		OSTYPE(`linux') and OSTYPE(`mklinux').  From Tim Pierce of
+		RootsWeb.com.
+	CONFIG: Make sure FEATURE(`nullclient') doesn't use aliasing and
+		forwarding to make it as close to the old behavior as
+		possible.  Problem noted by George W. Baltz of the
+		University of Maryland.
+	CONFIG: Added OSTYPE(`darwin') for Mac OS X and Darwin users.  From
+		Wilfredo Sanchez of Apple Computer, Inc.
+	CONFIG: Changed the map names used by FEATURE(`ldap_routing') from
+		ldap_mailhost and ldap_mailroutingaddress to ldapmh and
+		ldapmra as underscores in map names cause problems if
+		underscore is in OperatorChars.  Problem noted by Bob Zeitz
+		of the University of Alberta.
+	CONFIG: Apply blacklist_recipients also to hosts in class {w}.
+		Patch from Michael Tratz of Esosoft Corporation.
+	CONFIG: Use A=TCP ... instead of A=IPC ... in SMTP mailers.
+	CONTRIB: Add link_hash.sh to create symbolic links to the hash
+		of X.509 certificates.
+	CONTRIB: passwd-to-alias.pl:  More protection from special characters;
+		treat special shells as root aliases; skip entries where the
+		GECOS full name and username match.  From Ulrich Windl of the
+		Universitat Regensburg.
+	CONTRIB: qtool.pl: Add missing last_modified_time method and fix a
+		typo.  Patch from Graeme Hewson of Oracle.
+	CONTRIB: re-mqueue.pl: Improve handling of a race between re-mqueue
+		and sendmail.  Patch from Graeme Hewson of Oracle.
+	CONTRIB: re-mqueue.pl: Don't exit(0) at end so can be called as
+		subroutine Patch from Graeme Hewson of Oracle.
+	CONTRIB: Add movemail.pl (move old mail messages between queues by
+		calling re-mqueue.pl) and movemail.conf (configuration
+		script for movemail.pl).  From Graeme Hewson of Oracle.
+	CONTRIB: Add cidrexpand (expands CIDR blocks as a preprocessor to
+		makemap).  From Derek J. Balling of Yahoo,Inc.
+	DEVTOOLS: INSTALL_RAWMAN installation option mistakenly applied any
+		extension modifications (e.g., MAN8EXT) to the installation
+		target.  Patch from James Ralston of Carnegie Mellon
+		University.
+	DEVTOOLS: Add support for SunOS 5.9.
+	DEVTOOLS: New option confLN contains the command used to create
+		links.
+	LIBSMDB: Berkeley DB 2.X and 3.X errors might be lost and not
+		reported.
+	MAIL.LOCAL: DG/UX portability.  Problem noted by Tim Boyer of
+		Denman Tire Corporation.
+	MAIL.LOCAL: Prevent a possible DoS attack when compiled with
+		-DCONTENTLENGTH.  Based on patch from 3APA3A@SECURITY.NNOV.RU.
+	MAILSTATS: Fix usage statement (-p and -o are optional).
+	MAKEMAP: Change man page layout as workaround for problem with nroff
+		and -man on Solaris 7.  Patch from Larry Williamson.
+	RMAIL: AIX 4.3 has snprintf().  Problem noted by David Hayes of
+		Black Diamond Equipment, Limited.
+	RMAIL: Prevent a segmentation fault if the incoming message does not
+		have a From line.
+	VACATION: Read all of the headers before deciding whether or not
+		to respond instead of stopping after finding recipient.
+	Added Files:
+		cf/ostype/darwin.m4
+		contrib/cidrexpand
+		contrib/link_hash.sh
+		contrib/movemail.conf
+		contrib/movemail.pl
+		devtools/OS/SunOS.5.9
+		test/t_snprintf.c
+
+8.10.2/8.10.2	2000/06/07
+	SECURITY: Work around broken Linux setuid() implementation.
+		On Linux, a normal user process has the ability to subvert
+		the setuid() call such that it is impossible for a root
+		process to drop its privileges.  Problem noted by Wojciech
+		Purczynski of elzabsoft.pl.
+	SECURITY: Add more vigilance around set*uid(), setgid(), setgroups(),
+		initgroups(), and chroot() calls.
+	Added Files:
+		test/t_setuid.c
+
 8.10.1/8.10.1	2000/04/06
 	SECURITY: Limit the choice of outgoing (client-side) SMTP
 		Authentication mechanisms to those specified in
 		AuthMechanisms to prevent information leakage.  We do not
 		recommend use of PLAIN for outgoing mail as it sends the
 		password in clear text to possibly untrusted servers.  See
-		cf/README's DefAuthInfo section for additional information.
+		cf/README's DefaultAuthInfo section for additional information.
 	Copy the ident argument for openlog() to avoid problems on some
 		OSs.  Based on patch from Rob Bajorek from Webhelp.com.
 	Avoid bogus error message when reporting an alias line as too long.
@@ -505,7 +1115,7 @@
 		"(user=%s)"' and a lookup is done on "*", this would be
 		equivalent to '-k "(user=*)"' -- matching ANY record with a
 		user attribute.  Instead, if the LDAP map specification
-		contains '-k "(user=%0)"' and a lookup is one on "*", this
+		contains '-k "(user=%0)"' and a lookup is done on "*", this
 		would be equivalent to '-k "(user=\2A)"' -- matching a user
 		with the name "*".
 	New LDAP map flags: "-1" requires a single match to be returned, if
@@ -531,7 +1141,7 @@
 		Ulrich Windl of the Universitat Regensburg.
 	Add new F=% mailer flag to allow for a store and forward
 		configuration.  Mailers which have this flag will not attempt
-		delivery on initial recipient of a message or on queue runs
+		delivery on initial receipt of a message or on queue runs
 		unless the queued message is selected using one of the
 		-qI/-qR/-qS queue run modifiers or an ETRN request.  Code
 		provided by Philip Guenther of Gustavus Adolphus College.
@@ -570,9 +1180,8 @@
 	Macro expand PostmasterCopy and DoubleBounceAddress options.
 	New "ph" map for performing ph queries in rulesets.  More
 		information is available at
-		http://www-wsg.cso.uiuc.edu/sendmail/patches/.
-		Contributed by Mark Roth of the University of Illinois at
-		Urbana-Champaign.
+		http://www-dev.cso.uiuc.edu/sendmail/.  Contributed by Mark
+		Roth of the University of Illinois at Urbana-Champaign.
 	Detect temporary lookup failures in the host map if looking up a
 		bracketed IP address.  Problem noted by Kari Hurtta of the
 		Finnish Meteorological Institute.
@@ -1251,7 +1860,7 @@
 		MAILLOCK when compiling.  Also requires linking with
 		-lmail.  Patch from Neil Rickert of Northern Illinois
 		University.
-	MAIL.LOCAL: Create a Content-Length; header if CONTENTLENGTH is
+	MAIL.LOCAL: Create a Content-Length: header if CONTENTLENGTH is
 		defined when compiling.  Automatically set for Solaris 2.3
 		and later.  Patch from Neil Rickert of Northern Illinois
 		University.
@@ -1266,15 +1875,15 @@
 	MAIL.LOCAL: Support group writable mail spool files when MAILGID is
 		set to the gid to use (-DMAILGID=6) when compiling.
 		Patch from Neil Rickert of Northern Illinois University.
-	MAIL.LOCAL: When a mail message includes lines longer than 2046
-		characters (in LMTP mode), mail.local will split the
-		incoming line up into 2046-character output lines
-		(excluding the newline).  If an input line is 2047
-		characters long (excluding CR-LF) and the last character is
-		a '.', mail.local will see it as the end of input, transfer
-		it to the user mailbox and try to write an `ok' back to
-		sendmail.  If the message was much longer, both sendmail
-		and mail.local will deadlock waiting for each other to read
+	MAIL.LOCAL: When a mail message included lines longer than 2046
+		characters (in LMTP mode), mail.local split the incoming
+		line up into 2046-character output lines (excluding the
+		newline).  If an input line was 2047 characters long
+		(excluding CR-LF) and the last character was a '.',
+		mail.local saw it as the end of input, transfered it to the
+		user mailbox and tried to write an `ok' back to sendmail.
+		If the message was much longer, both sendmail and
+		mail.local would deadlock waiting for each other to read
 		what they have written.  Problem noted by Peter Jeremy of
 		Alcatel Australia Limited.
 	MAIL.LOCAL: New option -b to return a permanent error instead of a
@@ -2106,7 +2715,7 @@
 	CONFIG: new FEATURE(relay_based_on_MX) to allow relaying based on
 		the MX records of the host portion of an incoming recipient.
 	CONFIG: new FEATURE(access_db) which turns on the access database
-		feature.  This database give you the ability to allow
+		feature.  This database gives you the ability to allow
 		or refuse to accept mail from specified domains for
 		administrative reasons.  By default, names that are listed
 		as "OK" in the access db are domain names, not host names.
@@ -2958,9 +3567,9 @@
 		first" error message.  Problem pointed out by Chris Thomas
 		of UCLA; patch from John Beck of SunSoft.
 	Handle "sendmail -bp -qSfoobar" properly if restrictqrun is set
-		 in PrivacyOptions.  The -q shouldn't turn this command off.
-		 Problem noted by Murray Kucherawy of Pacific Bell Internet;
-		 based on a patch from Gregory Neil Shapiro of WPI.
+		in PrivacyOptions.  The -q shouldn't turn this command off.
+		Problem noted by Murray Kucherawy of Pacific Bell Internet;
+		based on a patch from Gregory Neil Shapiro of WPI.
 	Don't consider SMTP reply codes 452 or 552 (exceeded storage allocation)
 		in a DATA transaction to be sticky; these can occur because
 		a message is too large, and smaller messages should still go
Index: gnu/usr.sbin/sendmail/cf/README
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/cf/README,v
retrieving revision 1.3
retrieving revision 1.6
diff -u -r1.3 -r1.6
--- gnu/usr.sbin/sendmail/cf/README	2000/04/07 19:20:28	1.3
+++ gnu/usr.sbin/sendmail/cf/README	2001/05/29 01:31:10	1.6
@@ -6,8 +6,8 @@
 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-1.4.tar.gz (check for the
-latset version).  EXCEPTIONS: DEC's m4 on Digital UNIX 4.x is broken (3.x
+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),
@@ -54,7 +54,7 @@
 
 	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 Eric P. Allman.  All rights reserved.
 	# Copyright (c) 1988, 1993
@@ -254,12 +254,13 @@
 QUEUE_DIR		[/var/spool/mqueue] The directory containing
 			queue files.  To use multiple queues, supply
 			a value ending with an asterisk.  For
-			example, /var/spool/mqueue/q* will use all of the
+			example, /var/spool/mqueue/qd* will use all of the
 			directories or symbolic links to directories
-			beginning with 'q' in /var/spool/mqueue as queue
+			beginning with 'qd' in /var/spool/mqueue as queue
 			directories.  The names 'qf', 'df', and 'xf' are
-			used as specific subdirectories for the corresponding
-			queue file types.
+			reserved as specific subdirectories for the
+			corresponding queue file types as explained in
+			doc/op/op.me.
 STATUS_FILE		[/etc/mail/statistics] The file containing status
 			information.
 LOCAL_MAILER_PATH	[/bin/mail] The program used to deliver local mail.
@@ -309,13 +310,13 @@
 SMTP_MAILER_MAXMSGS	[undefined] If defined, the maximum number of
 			messages to deliver in a single connection for the
 			smtp, smtp8, esmtp, or dsmtp mailers.
-SMTP_MAILER_ARGS	[IPC $h] The arguments passed to the smtp mailer.
+SMTP_MAILER_ARGS	[TCP $h] The arguments passed to the smtp mailer.
 			About the only reason you would want to change this
 			would be to change the default port.
-ESMTP_MAILER_ARGS	[IPC $h] The arguments passed to the esmtp mailer.
-SMTP8_MAILER_ARGS	[IPC $h] The arguments passed to the smtp8 mailer.
-DSMTP_MAILER_ARGS	[IPC $h] The arguments passed to the dsmtp mailer.
-RELAY_MAILER_ARGS	[IPC $h] The arguments passed to the relay mailer.
+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.
 RELAY_MAILER_MAXMSGS	[undefined] If defined, the maximum number of
 			messages to deliver in a single connection for the
 			relay mailer.
@@ -426,13 +427,16 @@
 		of the form node::user will not work.
 FAX_RELAY	The host that will accept mail to the .FAX pseudo-domain.
 		The "fax" mailer overrides this value.
-LOCAL_RELAY	DEPRECATED.  The site that will handle unqualified
-		names -- that is, names with out an @domain extension.
-		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 alias database.  This
-		only works at small sites, and only with some user
-		agents.
+LOCAL_RELAY	The site that will handle unqualified names -- that
+		is, names with out 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
+		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
+		alias database.  This only works at small sites,
+		and only with some user agents.
 LUSER_RELAY	The site that will handle lusers -- that is, apparently
 		local names that aren't local accounts or aliases.  To
 		specify a local user instead of a site, set this to
@@ -488,7 +492,7 @@
 		demand delivery, and "relay" for transmission to the
 		RELAY_HOST, LUSER_RELAY, or MAIL_HUB.
 
-uucp		The Unix-to-Unix Copy Program mailer.  Actually, this
+uucp		The UNIX-to-UNIX Copy Program mailer.  Actually, this
 		defines two mailers, "uucp-old" (a.k.a. "uucp") and
 		"uucp-new" (a.k.a. "suucp").  The latter is for when you
 		know that the UUCP mailer at the other end can handle
@@ -513,7 +517,7 @@
 
 fax		Facsimile transmission.  This is experimental and based
 		on Sam Leffler's HylaFAX software.  For more information,
-		see http://www.vix.com/hylafax/.
+		see http://www.hylafax.org/.
 
 pop		Post Office Protocol.
 
@@ -623,8 +627,10 @@
 		given as parameter.
 
 nocanonify	Don't pass addresses to $[ ... $] for canonification
-		by default.  It can be changed by setting the
-		DaemonPortOptions modifiers (M=).  That is,
+		by default, i.e., host/domain names are considered canonical,
+		except for unqualified names, which must not be used in this
+		mode (violation of the standard).  It can be changed by
+		setting the DaemonPortOptions modifiers (M=).  That is,
 		FEATURE(`nocanonify') will be overridden by setting the
 		'c' flag.  Conversely, if FEATURE(`nocanonify') is not used,
 		it can be emulated by setting the 'C' flag
@@ -656,14 +662,22 @@
 		<user@host>, will be canonified (and hopefully fully
 		qualified), too.
 
-stickyhost	If set, email sent to "user@local.host" are marked
-		as "sticky" -- that is, the local addresses aren't
-		matched against UDB and don't go through ruleset 5.
-		This is used if you want a set up where "user" is
-		not necessarily the same as "user@local.host", e.g.,
-		to make a distinct domain-wide namespace.  Prior to
-		8.7 this was the default, and notsticky was used to
-		turn this off.
+stickyhost	This feature is sometimes used with LOCAL_RELAY,
+		although it can be used for a different effect with
+		MAIL_HUB.
+
+		When used without MAIL_HUB, email sent to
+		"user@local.host" are marked as "sticky" -- that
+		is, the local addresses aren't matched against UDB,
+		don't go through ruleset 5, and are not forwarded to
+		the LOCAL_RELAY (if defined).
+
+		With MAIL_HUB, mail addressed to "user@local.host"
+		is forwarded to the mail hub, with the envelope
+		address still remaining "user@local.host".
+		Without stickyhost, the envelope would be changed
+		to "user@mail_hub", in order to protect against
+		mailing loops.
 
 mailertable	Include a "mailer table" which can be used to override
 		routing for particular domains (which are not in class {w},
@@ -966,12 +980,13 @@
 promiscuous_relay
 		By default, the sendmail configuration files do not permit
 		mail relaying (that is, accepting mail from outside your
-		domain and sending it to another host outside your domain).
-		This option sets your site to allow mail relaying from any
-		site to any site.  In general, it is better to control the
-		relaying more carefully with the access db and class {R}.
-		Domains can be added to class {R} by the macros RELAY_DOMAIN
-		or RELAY_DOMAIN_FILE (analogously to MASQUERADE_DOMAIN and
+		local host (class {w}) and sending it to another host than
+		your local host).  This option sets your site to allow
+		mail relaying from any site to any site.  In almost all
+		cases, it is better to control relaying more carefully
+		with the access map, class {R}, or authentication.  Domains
+		can be added to class {R} by the macros RELAY_DOMAIN or
+		RELAY_DOMAIN_FILE (analogously to MASQUERADE_DOMAIN and
 		MASQUERADE_DOMAIN_FILE, see below).
 
 relay_entire_domain
@@ -1003,11 +1018,15 @@
 		FEATURE(`loose_relay_check').
 
 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.
+		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.
 
 relay_local_from
 		Allows relaying if the domain portion of the mail sender
@@ -1059,6 +1078,12 @@
 		described in the anti-spam configuration control section
 		later in this document.
 
+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
@@ -1069,13 +1094,13 @@
 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
-		rbl.maps.vix.com.  An explanation for an DNS based rejection
-		list can be found http://maps.vix.com/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.
+		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.
 
 loose_relay_check
 		Normally, if % addressing is used for a recipient, e.g.
@@ -1118,15 +1143,6 @@
 lists of UUCP hosts they speak with directly.  This can get a bit more
 tricky.  For an example of a "complex" site, see cf/ucbvax.mc.
 
-If your host is known by several different names, you need to augment
-class {w}.  This is a list of names by which you are known, and
-anything sent to an address using a host name in this list will be
-treated as local mail.  You can do this in two ways:  either create the
-file /etc/mail/local-host-names containing a list of your aliases (one per
-line), and use ``FEATURE(`use_cw_file')'' in the .mc file, or add
-``LOCAL_DOMAIN(`alias.host.name')''.  Be sure you use the fully-qualified
-name of the host, rather than a short name.
-
 The SITECONFIG macro allows you to indirectly reference site-dependent
 configuration information stored in the siteconfig subdirectory.  For
 example, the line
@@ -1231,7 +1247,7 @@
 On host grasp.insa-lyon.fr (UUCP host name "grasp"), the following
 summarizes the sender rewriting for various mailers.
 
-Mailer          sender		rewriting in the envelope
+Mailer		sender		rewriting in the envelope
 ------		------		-------------------------
 uucp-{old,new}	wolf		grasp!wolf
 uucp-dom	wolf		wolf@grasp.insa-lyon.fr
@@ -1569,14 +1585,20 @@
 * Access database.
 * Header checks.
 
-Relaying (transmission of messages from a site outside your domain to
-another site outside your domain) is denied by default.  Note that
-this changed in sendmail 8.9; previous versions allowed relaying by
-default.  If you 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).
+Relaying (transmission of messages from a site outside your host (class
+{w}) to another site except yours) is denied by default.  Note that this
+changed in sendmail 8.9; previous versions allowed relaying by default.
+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.,
+
+	sendmail.org
+	128.32
+	1:2:3:4:5:6:7
+	host.mydomain.com
 
 If you use
 
@@ -1704,15 +1726,14 @@
 	REJECT		Reject the sender or recipient with a general
 			purpose message.
 	DISCARD		Discard the message completely using the
-			$#discard mailer.  For sender addresses it
-			indicates that you should discard anything
-			received from the indicated domain.  If it
-			is used for recipients, it affects only
-			the designated recipients, not the whole
-			message.
-	### any text	where ### is an RFC 821 compliant error code
-			and "any text" is a message to return for
-			the command.
+			$#discard mailer.  If it is used in check_compat,
+			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.
+	### 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.
 	ERROR:### any text
 			as above, but useful to mark error messages as such.
 	ERROR:D.S.N:### any text
@@ -1721,7 +1742,7 @@
 
 For example:
 
-	cyberspammer.com	550 We don't accept mail from spammers
+	cyberspammer.com	ERROR:"550 We don't accept mail from spammers"
 	okay.cyberspammer.com	OK
 	sendmail.org		RELAY
 	128.32			RELAY
@@ -1759,7 +1780,7 @@
 You can also use the access database to block sender addresses based on
 the username portion of the address.  For example:
 
-	FREE.STEALTH.MAILER@	550 Spam not accepted
+	FREE.STEALTH.MAILER@	ERROR:550 Spam not accepted
 
 Note that you must include the @ after the username to signify that
 this database entry is for checking only the username portion of the
@@ -1772,9 +1793,9 @@
 then you can add entries to the map for local users, hosts in your
 domains, or addresses in your domain which should not receive mail:
 
-	badlocaluser@		550 Mailbox disabled for this username
-	host.mydomain.com	550 That host does not accept mail
-	user@otherhost.mydomain.com	550 Mailbox disabled for this recipient
+	badlocaluser@		ERROR:550 Mailbox disabled for this username
+	host.mydomain.com	ERROR:550 That host does not accept mail
+	user@otherhost.mydomain.com	ERROR:550 Mailbox disabled for this recipient
 
 This would prevent a recipient of badlocaluser@mydomain.com, any
 user at host.mydomain.com, and the single address
@@ -1800,12 +1821,15 @@
 This will cause sendmail to reject mail from any site in the
 Realtime Blackhole List database.  You can specify an alternative
 RBL domain to check by specifying an argument to the FEATURE.
-A second argument can be used to change the default error message
-Mail from $&{client_addr} refused by blackhole site DOMAIN
-where DOMAIN is replaced by the first argument.  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 default error message is
+
+	Mail from $&{client_addr} refused by blackhole site DOMAIN
 
+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,
@@ -1837,8 +1861,8 @@
 access map according to their type.  Three tags are available:
 
 	Connect:	connection information (${client_addr}, ${client_name})
-	From:		sender
-	To:		recipient
+	From:		envelope sender
+	To:		envelope recipient
 
 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
@@ -1871,10 +1895,32 @@
 respectively.  Instead, those rulesets will be called by the check_rcpt
 ruleset; they will be skipped if a sender has been authenticated using
 a "trusted" mechanism, i.e., one that is defined via TRUST_AUTH_MECH().
-Moreover, an argument can be specified for this option:
-
-	friend: enable spamfriend test
-	hater: enable spamhater test
+If check_mail returns an error then the RCPT TO command will be rejected
+with that error.  If it returns some other result starting with $# then
+check_relay will be skipped.  If the sender address (or a part of it) is
+listed in the access map and it has a RHS of OK or RELAY, then check_relay
+will be skipped.  This has an interesting side effect: if your domain is
+my.domain and you have
+
+	my.domain	RELAY
+
+in the access map, then all e-mail with a sender address of
+<user@my.domain> gets through, even if check_relay would reject it
+(e.g., based on the hostname or IP address).  This allows spammers
+to get around DNS based blacklist by faking the sender address.  To
+avoid this problem you have to use tagged entries:
+
+	To:my.domain		RELAY
+	Connect:my.domain	RELAY
+
+if you need those entries at all (class {R} may take care of them).
+
+FEATURE(`delay_checks') can take an optional argument:
+
+	FEATURE(`delay_checks', `friend')
+		 enables spamfriend test
+	FEATURE(`delay_checks', `hater')
+		 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
@@ -1921,6 +1967,11 @@
 
 	H*: $>CheckHdr
 
+Notice: 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
+to $&{currHeader}.
+
 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
 headers and the size of all of the headers in bytes separated by $|.  One
@@ -1955,10 +2006,144 @@
 	# Otherwise, reject the mail
 	R$*			$#error $: 553 Header Error
 
-+--------------------------------+
-| SMTP AUTHENTICATION            |
-+--------------------------------+
++----------+
+| STARTTLS |
++----------+
+
+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.
+
+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).
+${tls_version} the TLS/SSL version used for the connection, e.g., TLSv1,
+	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.
+	SOFTWARE STARTTLS handshake failed.
+${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
+	connection.
+
+Relaying
+
+SMTP STARTTLS can allow relaying for senders who have successfully
+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
+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
+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:
+
+/C=US/ST=California/O=endmail.org/OU=private/CN=Darth Mail (Cert)/Email=
+darth+cert@endmail.org
+
+is encoded as:
+
+/C=US/ST=California/O=endmail.org/OU=private/CN=
+Darth+20Mail+20+28Cert+29/Email=darth+2Bcert@endmail.org
+
+(line breaks have been inserted for readability).
+
+Of course it is also possible to write a simple rulesets that allows
+relaying for everyone who can present a cert that can be verified, e.g.,
+
+LOCAL_RULESETS
+SLocal_check_rcpt
+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).
+
+tls_server is called when sendmail acts as client after a STARTTLS command
+(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
+${verify} and STARTTLS or MAIL, respectively.
+
+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}
+(${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:
+
+VERIFY		verification must have succeeded
+VERIFY:bits	verification must have succeeded and ${cipher_bits} must
+		be greater than or equal bits.
+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)
+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.
+
+Example: e-mail send 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.
+TLS_Srv:secure.example.com      ENCR:112
+TLS_Clt:laptop.example.com      PERM+VERIFY:112
+
+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.
+
+
+Received: Header
+
+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})
+
++---------------------+
+| SMTP AUTHENTICATION |
++---------------------+
+
 The macros ${auth_authen}, ${auth_author}, and ${auth_type} can be
 used in anti-relay rulesets to allow relaying for those users that
 authenticated themselves.  A very simple example is:
@@ -1989,6 +2174,12 @@
 Per default, relaying is allowed for any user who authenticated
 via a "trusted" mechanism, i.e., one that is defined via
 TRUST_AUTH_MECH(`list of mechanisms')
+For example:
+TRUST_AUTH_MECH(`KERBEROS_V4 DIGEST-MD5')
+
+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}.
 
 +--------------------------------+
 | ADDING NEW MAILERS OR RULESETS |
@@ -2008,9 +2199,9 @@
 
 
 #if _FFR_MILTER
-+---------------------------+
-| ADDING NEW MAILER FILTERS |
-+---------------------------+
++-------------------------+
+| ADDING NEW MAIL FILTERS |
++-------------------------+
 
 Sendmail supports mail filters to filter incoming SMTP messages according
 to the "Sendmail Mail Filter API" documentation.  These filters can be
@@ -2115,6 +2306,24 @@
 	define(`confDOMAIN_NAME', `$w.$m')dnl
 
 
++-----------------------------------+
+| ACCEPTING MAIL FOR MULTIPLE NAMES |
++-----------------------------------+
+
+If your host is known by several different names, you need to augment
+class {w}.  This is a list of names by which your host is known, and
+anything sent to an address using a host name in this list will be
+treated as local mail.  You can do this in two ways:  either create the
+file /etc/mail/local-host-names containing a list of your aliases (one per
+line), and use ``FEATURE(`use_cw_file')'' in the .mc file, or add
+``LOCAL_DOMAIN(`alias.host.name')''.  Be sure you use the fully-qualified
+name of the host, rather than a short name.
+
+If you want to have different address in different domains, take
+a look at the virtusertable feature, which is also explained at
+http://www.sendmail.org/virtual-hosting.html
+
+
 +--------------------+
 | USING MAILERTABLES |
 +--------------------+
@@ -2134,13 +2343,15 @@
 
 The semantics are simple.  Any LHS entry that does not begin with
 a dot matches the full host name indicated.  LHS entries beginning
-with a dot match anything ending with that domain name -- that is,
-they can be thought of as having a leading "*" wildcard.  Matching
-is done in order of most-to-least qualified -- for example, even
-though ".my.domain" is listed first in the above example, an entry
-of "uuhost1.my.domain" will match the second entry since it is
-more explicit.  Note: e-mail to "user@my.domain" does not match
-any entry in the above table. You need to have something like:
+with a dot match anything ending with that domain name (including
+the leading dot) -- that is, they can be thought of as having a
+leading ".+" regular expression pattern for a non-empty sequence of
+characters.  Matching is done in order of most-to-least qualified
+-- for example, even though ".my.domain" is listed first in the
+above example, an entry of "uuhost1.my.domain" will match the second
+entry since it is more explicit.  Note: e-mail to "user@my.domain"
+does not match any entry in the above table.  You need to have
+something like:
 
 	my.domain		esmtp:host.my.domain
 
@@ -2190,7 +2401,7 @@
 
 As a general rule, it is an extremely bad idea to using full names
 as e-mail addresses, since they are not in any sense unique.  For
-example, the Unix software-development community has at least two
+example, the UNIX software-development community has at least two
 well-known Peter Deutsches, and at one time Bell Labs had two
 Stephen R. Bournes with offices along the same hallway.  Which one
 will be forced to suffer the indignity of being Stephen_R_Bourne_2?
@@ -2587,7 +2798,7 @@
 					rejected.  If not set or <= 0, there is
 					no limit.
 confMAX_HEADERS_LENGTH	MaxHeadersLength
-					[undefined] Maximum length of the sum
+					[32768] Maximum length of the sum
 					of all headers.
 confMAX_MIME_HEADER_LENGTH  MaxMimeHeaderLength
 					[undefined] Maximum length of
@@ -2788,7 +2999,7 @@
 					intersection of this list and the list
 					of available mechanisms as determined
 					by the CYRUS SASL library.
-confDEF_AUTH_INFO	DefaultAuthInfo	[undefined] Filename that contains
+confDEF_AUTH_INFO	DefaultAuthInfo	[undefined] Name of file that contains
 					authentication information for
 					outgoing connections.  This file
 					must contain the user id, the
@@ -2829,6 +3040,33 @@
 					maps unless they are specified in
 					the individual map specification
 					('K' command).
+confCACERT_PATH		CACERTPath	[undefined] Path to directory
+					with certs of CAs.
+confCACERT		CACERTFile	[undefined] File containing one CA
+					cert.
+confSERVER_CERT		ServerCertFile	[undefined] File containing the
+					cert of the server, i.e., this cert
+					is used when sendmail acts as
+					server.
+confSERVER_KEY		ServerKeyFile	[undefined] File containing the
+					private key belonging to the server
+					cert.
+confCLIENT_CERT		ClientCertFile	[undefined] File containing the
+					cert of the client, i.e., this cert
+					is used when sendmail acts as
+					client.
+confCLIENT_KEY		ClientKeyFile	[undefined] File containing the
+					private key belonging to the client
+					cert.
+confDH_PARAMETERS	DHParameters	[undefined] File containing the
+					DH parameters.
+confRAND_FILE		RandFile	[undefined] File containing random
+					data (use prefix file:) or the
+					name of the UNIX socket if EGD is
+					used (use prefix egd:).  STARTTLS
+					requires this option if the compile
+					flag HASURANDOM is not set (see
+					sendmail/README).
 
 See also the description of OSTYPE for some parameters that can be
 tweaked (generally pathnames to mailers).
@@ -2863,6 +3101,11 @@
 Note that if the first of those DAEMON_OPTIONS lines were omitted, then
 there would be no listener on the standard SMTP port.
 
+Example 3: To listen on both IPv4 and IPv6 interfaces, use
+
+	DAEMON_OPTIONS(`Name=MTA-v4, Family=inet')
+	DAEMON_OPTIONS(`Name=MTA-v6, Family=inet6')
+
 A "Message Submission Agent" still uses all of the same rulesets for
 processing the message (and therefore still allows message rejection via
 the check_* rulesets).  In accordance with the RFC, the MSA will ensure
@@ -2870,6 +3113,7 @@
 relayed to another MTA.  It will also enforce the normal address syntax
 rules and log error messages.  Additionally, by using the M=a modifier
 you can require authentication before messages are accepted by the MSA.
+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.
 
@@ -2900,7 +3144,7 @@
 		site dependent; for example, "CS.Berkeley.EDU.m4"
 		describes hosts in the CS.Berkeley.EDU subdomain.
 
-mailer		Descriptions of mailers.   These are referenced using
+mailer		Descriptions of mailers.  These are referenced using
 		the MAILER macro in the .mc file.
 
 sh		Shell files used when building the .cf file from the
@@ -3034,4 +3278,4 @@
    8	DNS based blacklists
    9	special local rulesets (1 and 2)
 
-$Revision: 1.3 $, Last updated $Date: 2000/04/07 19:20:28 $
+$Revision: 1.6 $, Last updated $Date: 2001/05/29 01:31:10 $
Index: gnu/usr.sbin/sendmail/cf/cf/Makefile
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/cf/cf/Makefile,v
retrieving revision 1.6
retrieving revision 1.10
diff -u -r1.6 -r1.10
--- gnu/usr.sbin/sendmail/cf/cf/Makefile	2000/06/18 03:13:08	1.6
+++ gnu/usr.sbin/sendmail/cf/cf/Makefile	2001/05/29 01:31:11	1.10
@@ -1,8 +1,8 @@
-#	$OpenBSD: Makefile,v 1.6 2000/06/18 03:13:08 itojun Exp $
+#	$OpenBSD: Makefile,v 1.10 2001/05/29 01:31:11 millert Exp $
 #
 #  Makefile for configuration files.
 #
-#	$Sendmail: Makefile,v 8.40 2000/02/01 22:07:15 gshapiro Exp $
+#	$Sendmail: Makefile,v 8.40.8.5 2001/04/12 22:39:52 gshapiro Exp $
 #
 
 #
@@ -26,9 +26,8 @@
 	$(CHMOD) $(ROMODE) $@
 
 ALL=	clientproto.cf openbsd-proto.cf courtesan.cf courtesan-nonet.cf \
-	courtesan-lists.cf  openbsd-lists.cf gandalf.cf saruman.cf alatar.cf \
-	nettan.cf waldorf.cf lucifier.cf elbereth.cf corpse.cf knecht.cf \
-	openbsd-proto-IPv4only.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)
 
@@ -37,11 +36,9 @@
 
 depend install:
 
-distribution: openbsd-proto.cf openbsd-proto-IPv4only.cf
+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-proto-IPv4only.cf ${DESTDIR}/etc/mail/sendmail-IPv4only.cf
 
 # this is overkill, but....
 M4FILES=\
@@ -113,7 +110,6 @@
 	${CFDIR}/ostype/aix4.m4 \
 	${CFDIR}/ostype/altos.m4 \
 	${CFDIR}/ostype/amdahl-uts.m4 \
-	${CFDIR}/ostype/aux.m4 \
 	${CFDIR}/ostype/bsd4.3.m4 \
 	${CFDIR}/ostype/bsd4.4.m4 \
 	${CFDIR}/ostype/bsdi.m4 \
Index: gnu/usr.sbin/sendmail/cf/cf/knecht.mc
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/cf/cf/knecht.mc,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- gnu/usr.sbin/sendmail/cf/cf/knecht.mc	2000/04/02 19:05:51	1.1.1.1
+++ gnu/usr.sbin/sendmail/cf/cf/knecht.mc	2001/02/28 02:43:49	1.3
@@ -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
@@ -17,8 +17,8 @@
 #
 
 divert(0)dnl
-VERSIONID(`$Sendmail: knecht.mc,v 8.37 1999/11/19 05:18:12 gshapiro Exp $')
-OSTYPE(bsdi)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
@@ -28,10 +28,16 @@
 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
-MODIFY_MAILER_FLAGS(`LOCAL', `+P')dnl
 MAILER(local)dnl
 MAILER(smtp)dnl
 
@@ -61,7 +67,7 @@
 
 SCheckMessageId
 R< $+ @ $+ >			$@ OK
-R$*				$#error $: "553 Header error"
+R$*				$#error $: "554 Header error"
 
 LOCAL_RULESETS
 SLocal_check_mail
Index: gnu/usr.sbin/sendmail/cf/cf/openbsd-proto.mc
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/cf/cf/openbsd-proto.mc,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- gnu/usr.sbin/sendmail/cf/cf/openbsd-proto.mc	2000/06/18 00:14:40	1.2
+++ gnu/usr.sbin/sendmail/cf/cf/openbsd-proto.mc	2001/01/16 01:38:37	1.3
@@ -17,13 +17,13 @@
 #
 
 divert(0)dnl
-VERSIONID(`@(#)openbsd-proto.mc $Revision: 1.2 $')
+VERSIONID(`@(#)openbsd-proto.mc $Revision: 1.3 $')
 OSTYPE(openbsd)
 FEATURE(nouucp, `reject')
 MAILER(local)
 MAILER(smtp)
 DAEMON_OPTIONS(`Family=inet, address=0.0.0.0, Name=MTA')dnl
-DAEMON_OPTIONS(`Family=inet6, address=::, Name=MTA6')dnl
+DAEMON_OPTIONS(`Family=inet6, address=::, Name=MTA6, M=O')dnl
 dnl
 dnl Enforce valid Message-Id to help stop spammers
 dnl
Index: gnu/usr.sbin/sendmail/cf/feature/dnsbl.m4
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/cf/feature/dnsbl.m4,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/cf/feature/dnsbl.m4	2000/04/02 19:05:52	1.1.1.1
+++ gnu/usr.sbin/sendmail/cf/feature/dnsbl.m4	2001/01/15 21:08:54	1.2
@@ -11,9 +11,9 @@
 
 divert(0)
 ifdef(`_DNSBL_R_',`dnl',`dnl
-VERSIONID(`$Sendmail: dnsbl.m4,v 8.18 1999/08/03 04:30:56 gshapiro Exp $')')
+VERSIONID(`$Sendmail: dnsbl.m4,v 8.18.16.1 2000/11/22 01:13:21 ca Exp $')')
 divert(-1)
-define(`_DNSBL_SRV_', `ifelse(len(X`'_ARG_),`1',`rbl.maps.vix.com',_ARG_)')dnl
+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
 divert(8)
 # DNS based IP address spam list _DNSBL_SRV_
Index: gnu/usr.sbin/sendmail/cf/feature/ldap_routing.m4
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/cf/feature/ldap_routing.m4,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/cf/feature/ldap_routing.m4	2000/04/02 19:05:53	1.1.1.1
+++ gnu/usr.sbin/sendmail/cf/feature/ldap_routing.m4	2001/01/15 21:08:55	1.2
@@ -10,7 +10,7 @@
 #
 
 divert(0)
-VERSIONID(`$Sendmail: ldap_routing.m4,v 8.5 2000/02/26 01:32:03 gshapiro Exp $')
+VERSIONID(`$Sendmail: ldap_routing.m4,v 8.5.4.1 2000/07/15 18:05:05 gshapiro Exp $')
 divert(-1)
 
 # Check first two arguments.  If they aren't set, may need to warn in proto.m4
@@ -25,10 +25,10 @@
 
 LOCAL_CONFIG
 # LDAP routing maps
-Kldap_mailhost ifelse(len(X`'_ARG1_), `1',
-		      `ldap -1 -v mailHost -k (&(objectClass=inetLocalMailRecipient)(mailLocalAddress=%0))',
-		      `_ARG1_')
+Kldapmh ifelse(len(X`'_ARG1_), `1',
+	       `ldap -1 -v mailHost -k (&(objectClass=inetLocalMailRecipient)(mailLocalAddress=%0))',
+	       `_ARG1_')
 
-Kldap_mailroutingaddress ifelse(len(X`'_ARG2_), `1',
-				`ldap -1 -v mailRoutingAddress -k (&(objectClass=inetLocalMailRecipient)(mailLocalAddress=%0))',
-				`_ARG2_')
+Kldapmra ifelse(len(X`'_ARG2_), `1',
+		`ldap -1 -v mailRoutingAddress -k (&(objectClass=inetLocalMailRecipient)(mailLocalAddress=%0))',
+		`_ARG2_')
Index: gnu/usr.sbin/sendmail/cf/feature/nullclient.m4
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/cf/feature/nullclient.m4,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/cf/feature/nullclient.m4	2000/04/02 19:05:53	1.1.1.1
+++ gnu/usr.sbin/sendmail/cf/feature/nullclient.m4	2001/01/15 21:08:55	1.2
@@ -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
@@ -22,13 +22,15 @@
 #
 
 divert(0)
-VERSIONID(`$Sendmail: nullclient.m4,v 8.21 1999/08/06 01:48:57 gshapiro Exp $')
+VERSIONID(`$Sendmail: nullclient.m4,v 8.21.16.3 2000/09/17 17:04:22 gshapiro Exp $')
 divert(-1)
 
 undefine(`ALIAS_FILE')
 define(`MAIL_HUB', _NULL_CLIENT_)
 define(`SMART_HOST', _NULL_CLIENT_)
 define(`confFORWARD_PATH', `')
+ifdef(`confFROM_HEADER',, `define(`confFROM_HEADER', `<$g>')')
+define(`_DEF_LOCAL_MAILER_FLAGS', `lsDFM5q')
 MASQUERADE_AS(_NULL_CLIENT_)
 FEATURE(`allmasquerade')
 FEATURE(`masquerade_envelope')
Index: gnu/usr.sbin/sendmail/cf/m4/cfhead.m4
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/cf/m4/cfhead.m4,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/cf/m4/cfhead.m4	2000/04/07 19:20:30	1.2
+++ gnu/usr.sbin/sendmail/cf/m4/cfhead.m4	2001/05/29 01:31:11	1.5
@@ -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 Eric P. Allman.  All rights reserved.
 # Copyright (c) 1988, 1993
@@ -16,10 +16,11 @@
 #####
 #####		SENDMAIL CONFIGURATION FILE
 #####
-define(`TEMPFILE', maketemp(/tmp/cfXXXXXX))dnl
+ifdef(`unix', `dnl
+ifdef(`TEMPFILE', `dnl', `define(`TEMPFILE', maketemp(/tmp/cfXXXXXX))dnl
 syscmd(sh _CF_DIR_`'sh/makeinfo.sh _CF_DIR_ > TEMPFILE)dnl
 include(TEMPFILE)dnl
-syscmd(rm -f TEMPFILE)dnl
+syscmd(rm -f TEMPFILE)dnl')', `dnl')
 #####
 ######################################################################
 ######################################################################
@@ -153,26 +154,28 @@
 		CONCAT(CY, $'1`),
 		CONCAT(C, $3, $'1`))')
 sinclude(_CF_DIR_`'siteconfig/$1.m4)')
-define(`EXPOSED_USER', `PUSHDIVERT(5)CE$1
+define(`EXPOSED_USER', `PUSHDIVERT(5)C{E}$1
 POPDIVERT`'dnl`'')
-define(`LOCAL_USER', `PUSHDIVERT(5)CL$1
+ifdef(`_FFR_EXPOSED_USER_FILE', `define(`EXPOSED_USER_FILE', `PUSHDIVERT(5)F{E}$1
+POPDIVERT`'dnl`'')', `dnl')
+define(`LOCAL_USER', `PUSHDIVERT(5)C{L}$1
 POPDIVERT`'dnl`'')
 define(`MASQUERADE_AS', `define(`MASQUERADE_NAME', $1)')
-define(`MASQUERADE_DOMAIN', `PUSHDIVERT(5)CM$1
+define(`MASQUERADE_DOMAIN', `PUSHDIVERT(5)C{M}$1
 POPDIVERT`'dnl`'')
-define(`MASQUERADE_EXCEPTION', `PUSHDIVERT(5)CN$1
+define(`MASQUERADE_EXCEPTION', `PUSHDIVERT(5)C{N}$1
 POPDIVERT`'dnl`'')
-define(`MASQUERADE_DOMAIN_FILE', `PUSHDIVERT(5)FM$1
+define(`MASQUERADE_DOMAIN_FILE', `PUSHDIVERT(5)F{M}$1
 POPDIVERT`'dnl`'')
-define(`LOCAL_DOMAIN', `PUSHDIVERT(5)Cw$1
+define(`LOCAL_DOMAIN', `PUSHDIVERT(5)C{w}$1
 POPDIVERT`'dnl`'')
 define(`CANONIFY_DOMAIN', `PUSHDIVERT(5)C{Canonify}$1
 POPDIVERT`'dnl`'')
 define(`CANONIFY_DOMAIN_FILE', `PUSHDIVERT(5)F{Canonify}$1
 POPDIVERT`'dnl`'')
-define(`GENERICS_DOMAIN', `PUSHDIVERT(5)CG$1
+define(`GENERICS_DOMAIN', `PUSHDIVERT(5)C{G}$1
 POPDIVERT`'dnl`'')
-define(`GENERICS_DOMAIN_FILE', `PUSHDIVERT(5)FG$1
+define(`GENERICS_DOMAIN_FILE', `PUSHDIVERT(5)F{G}$1
 POPDIVERT`'dnl`'')
 define(`LDAPROUTE_DOMAIN', `PUSHDIVERT(5)C{LDAPRoute}$1
 POPDIVERT`'dnl`'')
@@ -184,9 +187,9 @@
 define(`VIRTUSER_DOMAIN_FILE', `PUSHDIVERT(5)F{VirtHost}$1
 define(`_VIRTHOSTS_')
 POPDIVERT`'dnl`'')
-define(`RELAY_DOMAIN', `PUSHDIVERT(5)CR$1
+define(`RELAY_DOMAIN', `PUSHDIVERT(5)C{R}$1
 POPDIVERT`'dnl`'')
-define(`RELAY_DOMAIN_FILE', `PUSHDIVERT(5)FR$1
+define(`RELAY_DOMAIN_FILE', `PUSHDIVERT(5)F{R}$1
 POPDIVERT`'dnl`'')
 define(`TRUST_AUTH_MECH', `PUSHDIVERT(5)C{TrustAuthMech}$1
 POPDIVERT`'dnl`'')
@@ -212,12 +215,14 @@
 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_BY_', `$.by $j ($v/$Z)$?r with $r$. id $i$?{tls_version}')
 define(`confRECEIVED_HEADER', `_REC_HDR_
-	_REC_AUTH_)
-	$.by $j ($v/$Z)$?r with $r$. id $i$?u
+	_REC_AUTH_$?{auth_ssf} (${auth_ssf} bits)$.)
+	_REC_BY_
+	_REC_TLS_
 	_REC_END_')
 define(`confSEVEN_BIT_INPUT', `False')
-define(`confEIGHT_BIT_HANDLING', `pass8')
 define(`confALIAS_WAIT', `10')
 define(`confMIN_FREE_BLOCKS', `100')
 define(`confBLANK_SUB', `.')
@@ -240,9 +245,10 @@
 define(`confFORWARD_PATH', `$z/.forward.$w:$z/.forward')
 define(`confCR_FILE', `-o MAIL_SETTINGS_DIR`'relay-domains')
 define(`confMILTER_MACROS_CONNECT', ``j, _, {daemon_name}, {if_name}, {if_addr}'')
-define(`confMILTER_MACROS_ENVFROM', ``i, {auth_type}, {auth_authen}, {auth_author}, {mail_mailer}, {mail_host}, {mail_addr}'')
+define(`confMILTER_MACROS_HELO', ``{tls_version}, {cipher}, {cipher_bits}, {cert_subject}, {cert_issuer}'')
+define(`confMILTER_MACROS_ENVFROM', ``i, {auth_type}, {auth_authen}, {auth_ssf}, {auth_author}, {mail_mailer}, {mail_host}, {mail_addr}'')
 define(`confMILTER_MACROS_ENVRCPT', ``{rcpt_mailer}, {rcpt_host}, {rcpt_addr}'')
 
 
 divert(0)dnl
-VERSIONID(`$Sendmail: cfhead.m4,v 8.76 2000/03/21 23:56:59 gshapiro Exp $')
+VERSIONID(`$Sendmail: cfhead.m4,v 8.76.4.16 2001/03/06 22:56:36 ca Exp $')
Index: gnu/usr.sbin/sendmail/cf/m4/proto.m4
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/cf/m4/proto.m4,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -r1.2 -r1.4
--- gnu/usr.sbin/sendmail/cf/m4/proto.m4	2000/04/07 19:20:30	1.2
+++ gnu/usr.sbin/sendmail/cf/m4/proto.m4	2001/05/29 01:31:11	1.4
@@ -13,7 +13,7 @@
 #
 divert(0)
 
-VERSIONID(`$Sendmail: proto.m4,v 8.446 2000/04/06 06:29:45 gshapiro Exp $')
+VERSIONID(`$Sendmail: proto.m4,v 8.446.2.5.2.41 2001/05/23 21:32:16 ca Exp $')
 
 MAILER(local)dnl
 
@@ -77,6 +77,7 @@
 define(`_U_',ifdef(`_DELAY_CHECKS_',`',`_'))
 dnl default relaying denied message
 ifdef(`confRELAY_MSG', `', `define(`confRELAY_MSG', `"550 Relaying denied"')')
+define(`CODE553', `553')
 divert(0)dnl
 
 # override file safeties - setting this option compromises system security,
@@ -160,11 +161,31 @@
 # Resolve map (to check if a host exists in check_mail)
 Kresolve host -a<OK> -T<TEMP>')
 
+ifdef(`_FFR_5_', `# macro storage map
+Kmacro macro')
+
 ifdef(`confCR_FILE', `dnl
-# Hosts that will permit relaying ($=R)
+# Hosts for which relaying is permitted ($=R)
 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
+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
+C{tls}VERIFY ENCR', `dnl')
+ifdef(`_CERT_REGEX_ISSUER_', `dnl
+# extract relevant part from cert issuer
+KCERTIssuer regex _CERT_REGEX_ISSUER_', `dnl')
+ifdef(`_CERT_REGEX_SUBJECT_', `dnl
+# extract relevant part from cert subject
+KCERTSubject regex _CERT_REGEX_SUBJECT_', `dnl')
 
 # who I send unqualified names to (null means deliver locally)
 DR`'ifdef(`LOCAL_RELAY', LOCAL_RELAY)
@@ -201,7 +222,7 @@
 _OPTION(SevenBitInput, `confSEVEN_BIT_INPUT', `False')
 
 # 8-bit data handling
-_OPTION(EightBitMode, `confEIGHT_BIT_HANDLING', `adaptive')
+_OPTION(EightBitMode, `confEIGHT_BIT_HANDLING', `pass8')
 
 # wait for alias file rebuild (default units: minutes)
 _OPTION(AliasWait, `confALIAS_WAIT', `5m')
@@ -297,7 +318,9 @@
 `errprint(WARNING: `confDAEMON_OPTIONS' is no longer valid.  See cf/README for more information.
 )'dnl
 `DAEMON_OPTIONS(`confDAEMON_OPTIONS')')
-ifelse(defn(`_DPO_'), `', `O DaemonPortOptions=Name=MTA', `_DPO_')
+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(`_NO_MSA_', `dnl', `O DaemonPortOptions=Port=587, Name=MSA, M=E')
 
 # SMTP client options
@@ -387,7 +410,7 @@
 _OPTION(MaxDaemonChildren, `confMAX_DAEMON_CHILDREN', `12')
 
 # maximum number of new connections per second
-_OPTION(ConnectionRateThrottle, `confCONNECTION_RATE_THROTTLE', `3')
+_OPTION(ConnectionRateThrottle, `confCONNECTION_RATE_THROTTLE', `0')
 
 # work recipient factor
 _OPTION(RecipientFactor, `confWORK_RECIPIENT_FACTOR', `30000')
@@ -525,6 +548,22 @@
 _OPTION(Milter.macros.envfrom, `confMILTER_MACROS_ENVFROM', `')
 _OPTION(Milter.macros.envrcpt, `confMILTER_MACROS_ENVRCPT', `')')
 
+# CA directory
+_OPTION(CACERTPath, `confCACERT_PATH', `')
+# CA file
+_OPTION(CACERTFile, `confCACERT', `')
+# Server Cert
+_OPTION(ServerCertFile, `confSERVER_CERT', `')
+# Server private key
+_OPTION(ServerKeyFile, `confSERVER_KEY', `')
+# Client Cert
+_OPTION(ClientCertFile, `confCLIENT_CERT', `')
+# Client private key
+_OPTION(ClientKeyFile, `confCLIENT_KEY', `')
+# DHParameters (only required if DSA/DH is used)
+_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)
@@ -597,6 +636,7 @@
 R$* : $* <@>		$: $2				strip colon if marked
 R$* <@>			$: $1				unmark
 R$* ;			   $1				strip trailing semi
+R$* < $+ :; > $*	$@ $2 :; <@>			catch <list:;>
 R$* < $* ; >		   $1 < $2 >			bogus bracketed semi
 
 # null input now results from list:; syntax
@@ -741,6 +781,9 @@
 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
+dnl should we do this for every hostname: even unqualified?
+R$* CC $* $| $* < @ $+.$+ > $*	$: $3 < @ $4.$5 . > $6
 R$* CC $* $| $*			$: $3
 # pass to name server to make hostname canonical
 R$* $| $* < @ $* > $*		$: $2 < @ $[ $3 $] > $4')
@@ -768,6 +811,7 @@
 ##################################################
 Sfinal=4
 
+R$+ :; <@>		$@ $1 :				handle <list:;>
 R$* <@>			$@				handle <> and list:;
 
 # strip trailing dot off possibly canonical name
@@ -824,24 +868,26 @@
 
 SParse0
 R<@>			$@ <@>			special case error msgs
-R$* : $* ; <@>		$#error $@ 5.1.3 $: "553 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 $: "553 User address required"
+R<@ $+>			$#error $@ 5.1.3 $: "CODE553 User address required"
 R$*			$: <> $1
 R<> $* < @ [ $+ ] > $*	$1 < @ [ $2 ] > $3
-R<> $* <$* : $* > $*	$#error $@ 5.1.3 $: "553 Colon illegal in host name part"
+R<> $* <$* : $* > $*	$#error $@ 5.1.3 $: "CODE553 Colon illegal in host name part"
 R<> $*			$1
-R$* < @ . $* > $*	$#error $@ 5.1.2 $: "553 Invalid host name"
-R$* < @ $* .. $* > $*	$#error $@ 5.1.2 $: "553 Invalid host name"
+R$* < @ . $* > $*	$#error $@ 5.1.2 $: "CODE553 Invalid host name"
+R$* < @ $* .. $* > $*	$#error $@ 5.1.2 $: "CODE553 Invalid host name"
+dnl comma only allowed before @; this check is not complete
+R$* , $~O $*		$#error $@ 5.1.2 $: "CODE553 Invalid route address"
 
 # 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 $: "553 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 $: "553 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
@@ -890,12 +936,16 @@
 R<!> $+			$: $1
 R< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
 R< error : $- $+ > $* 	$#error $@ $(dequote $1 $) $: $2
-R< $+ > $+ < @ $+ >	$: $>Recurse $1',
-`dnl')
+ifdef(`_NO_VIRTUSER_RECURSION_',
+`R< $+ > $+ < @ $+ >	$: $>ParseLocal $>Parse0 $>canonify $1',
+`R< $+ > $+ < @ $+ >	$: $>Recurse $1')
+dnl', `dnl')
 
 # short circuit local delivery so forwarded email works
 ifdef(`_MAILER_usenet_', `dnl
 R$+ . USENET < @ $=w . >	$#usenet $@ usenet $: $1	handle usenet specially', `dnl')
+
+
 ifdef(`_STICKY_LOCAL_DOMAIN_',
 `R$+ < @ $=w . >		$: < $H > $1 < @ $2 . >		first try hub
 R< $+ > $+ < $+ >	$>MailerToTriple < $1 > $2 < $3 >	yep ....
@@ -966,7 +1016,7 @@
 # deal with other remote names
 ifdef(`_MAILER_smtp_',
 `R$* < @$* > $*		$#_SMTP_ $@ $2 $: $1 < @ $2 > $3	user@host.domain',
-`R$* < @$* > $*		$#error $@ 5.1.2 $: "553 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
@@ -982,31 +1032,44 @@
 R$+ $| $#$*		$#$2
 R$+ $| $*		$: $1
 
-# deal with plussed users so aliases work nicely
-R$+ + *			$#_LOCAL_ $@ $&h $: $1
-R$+ + $*		$#_LOCAL_ $@ + $2 $: $1 + *
+ifdef(`_FFR_5_', `
+# Preserve host in a macro
+R$+			$: $(macro {LocalAddrHost} $) $1
+R$+ @ $+		$: $(macro {LocalAddrHost} $@ @ $2 $) $1')
 
+ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `', `
+# deal with plussed users so aliases work nicely
+R$+ + *			$#_LOCAL_ $@ $&h $: $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
+R$+ + $*		$#_LOCAL_ $@ + $2 $: $1 + *`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
+')
 # prepend an empty "forward host" on the front
 R$+			$: <> $1
 
 ifdef(`LUSER_RELAY', `dnl
 # send unrecognized local users to a relay host
+ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `
+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',
+R< $* > $+ <>		$: < > $2			found; strip $L')',
 `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', `
 R< > $+			$: < > < $1 <> $&h >		nope, restore +detail
 R< > < $+ <> + $* >	$: < > < $1 + $2 >		check whether +detail
 R< > < $+ <> $* >	$: < > < $1 >			else discard
 R< > < $+ + $* > $*	   < > < $1 > + $2 $3		find the user part
-R< > < $+ > + $*	$#_LOCAL_ $@ $2 $: @ $1		strip the extra +
+R< > < $+ > + $*	$#_LOCAL_ $@ $2 $: @ $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')		strip the extra +
 R< > < $+ >		$@ $1				no +detail
 R$+			$: $1 <> $&h			add +detail back in
 R$+ <> + $*		$: $1 + $2			check whether +detail
-R$+ <> $*		$: $1				else discard
+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 >
@@ -1164,8 +1227,7 @@
 ifdef(`_LDAP_ROUTING_', `dnl
 SLDAPExpand
 # do the LDAP lookups
-R<$+><$+>
-	$: <$(ldap_mailroutingaddress $2 $: $)> <$(ldap_mailhost $2 $: $)> <$1> <$2>
+R<$+><$+>		$: <$(ldapmra $2 $: $)> <$(ldapmh $2 $: $)> <$1> <$2>
 
 # if mailRoutingAddress and local or non-existant mailHost,
 # return the new mailRoutingAddress
@@ -1232,6 +1294,7 @@
 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 not found, but subdomain: try again
 R<?> <$+.$+> <$+> <$*> <$*>	$@ $>LookUpDomain <$2> <$3> <$4> <$5>
@@ -1261,7 +1324,8 @@
 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>
+R<?> <$+:$-> <$+> <$*> <$*>	$@ $>LookUpAddress <$1> <$3> <$4> <$5>
 dnl no match; IPv4: remove last part
 R<?> <$+.$-> <$+> <$*> <$*>	$@ $>LookUpAddress <$1> <$3> <$4> <$5>
 dnl no match: return default
@@ -1366,17 +1430,22 @@
 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<$={Accept}> < $* >	$@ $1
+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
 dnl error tag
-R<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
-R<ERROR:$+> $*		$#error $: $1
+R<ERROR:$-.$-.$-:$+> <$*>	$#error $@ $1.$2.$3 $: $4
+R<ERROR:$+> <$*>		$#error $: $1
 dnl generic error from access map
-R<$+> $*		$#error $: $1', `dnl')
+R<$+> <$*>		$#error $: $1', `dnl')
 
 ifdef(`_RBL_',`dnl
 # DNS based IP address spam list
@@ -1404,6 +1473,14 @@
 R< d > $*		$@ deferred
 R< $* > $*		$: $2
 
+# authenticated?
+dnl done first: we can require authentication for every mail transaction
+dnl workspace: address as given by MAIL FROM: (sender)
+R$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
+R$* $| $#$+		$#$2
+dnl undo damage: remove result of tls_client call
+R$* $| $*		$: $1
+
 dnl workspace: address as given by MAIL FROM:
 R<>			$@ <OK>			we MUST accept <> (RFC 1123)
 ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
@@ -1448,7 +1525,7 @@
 dnl	or:    <address>
 dnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
 R<? $=w> $*		$: $2			local client: ok
-R<? $+> <$+>		$#error $@ 5.5.4 $: "553 Real domain name required"
+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>)
@@ -1498,13 +1575,13 @@
 R$* $| $*		$: $2
 R<?> $*			$: < ? $&{client_name} > $1
 R<?> $*			$@ <OK>				...local unqualed ok
-R<? $+> $*		$#error $@ 5.5.4 $: "553 Domain name required"
+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<TEMP> $*		$#error $@ 4.1.8 $: "451 Domain of sender address " $&f " does not resolve"
-R<PERM> $*		$#error $@ 5.1.8 $: "501 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<DISCARD> $*		$#discard $: discard
@@ -1570,7 +1647,7 @@
 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@>
+R<?> $+ < @ $=w >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <U:$1@> <H:$2>
 R<?> $+ < @ $* >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <H:$2>
 R<?> $+			$: <> <$1> $| <U:$1@>
 dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
@@ -1596,6 +1673,16 @@
 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}
 dnl empty ${auth_type}?
@@ -1605,8 +1692,10 @@
 R$* $| $={TrustAuthMech}	$# RELAYAUTH
 dnl undo addition of ${auth_type}
 R$* $| $*		$: $1
+dnl workspace: localpart<@domain> | localpart
 ifelse(defn(`_NO_UUCP_'), `r',
-`R$* ! $* < @ $* >	$: <REMOTE> $2 < @ BANG_PATH >', `dnl')
+`R$* ! $* < @ $* >	$: <REMOTE> $2 < @ BANG_PATH >
+R$* ! $* 		$: <REMOTE> $2 < @ BANG_PATH >', `dnl')
 # anything terminating locally is ok
 ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
 R$+ < @ $* $=m >	$@ RELAYTO', `dnl')
@@ -1615,11 +1704,13 @@
 `R$+ < @ $=R >		$@ RELAYTO
 ifdef(`_ACCESS_TABLE_', `dnl
 R$+ < @ $+ >		$: <$(access To:$2 $: ? $)> <$1 < @ $2 >>
-R$+ < @ $+ >		$: <$(access $2 $: ? $)> <$1 < @ $2 >>',`dnl')',
+dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
+R<?> <$+ < @ $+ >>	$: <$(access $2 $: ? $)> <$1 < @ $2 >>',`dnl')',
 `R$+ < @ $* $=R >	$@ RELAYTO
 ifdef(`_ACCESS_TABLE_', `dnl
 R$+ < @ $+ >		$: $>LookUpDomain <$2> <?> <$1 < @ $2 >> <+To>',`dnl')')
 ifdef(`_ACCESS_TABLE_', `dnl
+dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
 R<RELAY> $*		$@ RELAYTO
 R<$*> <$*>		$: $2',`dnl')
 
@@ -1810,8 +1901,11 @@
 ###	return: <RHS of lookup> or <?> (not found)
 ######################################################################
 
+# class with valid marks for SearchList
+dnl if A is activated: add it
+C{src}E F H U
 SSearchList
-# if it is H: do lookup?
+# mark H: lookup domain
 R<$+> $| <H:$+> <$*>		$: <$1> $| <@> $>LookUpDomain <$2> <?> <$3> <$1>
 R<$+> $| <@> <$+> <$*>		$: <$1> $| <$2> <$3>
 dnl A: NOT YET REQUIRED
@@ -1819,9 +1913,9 @@
 dnl R<$+> $| <@> <$+> <$*>	$: <$1> $| <$2> <$3>
 dnl lookup of the item with tag
 dnl this applies to F: U: E:
-R<$- $-> $| <$-:$+> <$*>	$: <$1 $2> $| <$(access $2`'_TAG_DELIM_`'$4 $: $3:$4 $)> <$5>
+R<$- $-> $| <$={src}:$+> <$*>	$: <$1 $2> $| <$(access $2`'_TAG_DELIM_`'$4 $: $3:$4 $)> <$5>
 dnl no match, try without tag
-R<+ $-> $| <$-:$+> <$*>		$: <+ $1> $| <$(access $3 $: $2:$3 $)> <$4>
+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:
@@ -1832,13 +1926,12 @@
 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 special case for ERROR because this matches the input mark:address
-R<$+> $| <ERROR:$+> <>		$@ <ERROR:$2>
 dnl no match, try rest of list
-R<$+> $| <$-:$+> <$+>		$@ $>SearchList <$1> $| <$4>
+R<$+> $| <$={src}:$+> <$+>	$@ $>SearchList <$1> $| <$4>
 dnl no match, list empty: return failure
-R<$+> $| <$-:$+> <>		$@ <?>
+R<$+> $| <$={src}:$+> <>	$@ <?>
 dnl got result, return it
 R<$+> $| <$+> <$*>		$@ <$2>
 dnl return result from recursive invocation
@@ -1862,6 +1955,136 @@
 dnl empty ruleset definition so it can be called
 SLocal_trust_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: $: ? $)>
+R<?>$*		$@ OK
+R<NO> <>	$#error $@ 5.7.1 $: "550 do not offer TLS for " $&{client_name} " ["$&{client_addr}"]"
+
+Stry_tls
+R$*		$: $>LookUpDomain <$&{server_name}> <?> <> <! TLS_TRY_TAG>
+R<?>$*		$: $>LookUpAddress <$&{server_addr}> <?> <> <! TLS_TRY_TAG>
+R<?>$*		$: <$(access TLS_TRY_TAG: $: ? $)>
+R<?>$*		$@ OK
+R<NO>$*		$#error $@ 5.7.1 $: "550 do not try TLS with " $&{server_name} " ["$&{server_addr}"]"
+')dnl
+
+# is connection with client "good" enough? (done in server)
+# input: ${verify} $| (MAIL|STARTTLS)
+dnl MAIL: called from check_mail
+dnl STARTTLS: called from smtp() after STARTTLS has been accepted
+Stls_client
+ifdef(`_ACCESS_TABLE_', `dnl
+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>
+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')
+
+# is connection with server "good" enough? (done in client)
+dnl i.e. has the server been authenticated and is encryption active?
+dnl called from deliver() after STARTTLS command
+# input: ${verify}
+Stls_server
+ifdef(`_ACCESS_TABLE_', `dnl
+R$*		$: $1 $| $>LookUpDomain <$&{server_name}> <?> <> <! TLS_SRV_TAG>
+R$* $| <?>$*	$: $1 $| $>LookUpAddress <$&{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')
+
+Stls_connection
+ifdef(`_ACCESS_TABLE_', `dnl
+dnl common ruleset for tls_{client|server}
+dnl input: $&{verify} $| <ResultOfLookup> [<>]
+dnl remove optional <>
+R$* $| <$*>$*			$: $1 $| <$2>
+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
+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
+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 only verification required and it succeeded
+R<$*><VERIFY> OK		$@ OK
+dnl verification required + some level of encryption
+R<$*><VERIFY:$-> OK		$: <$1> <REQ:$2>
+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"
+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}
+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
+
+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."')
+
+SRelayAuth
+# 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
+dnl CA in CERTPath so we can authenticate users, we do not allow
+dnl them to abuse our server (they might be easier to get hold of,
+dnl but anyway).
+dnl so here is the trick: if the verification succeeded
+dnl we look up the cert issuer in the access map
+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
+ifdef(`_CERT_REGEX_ISSUER_', `dnl
+R$*			$: $1 $| $(CERTIssuer $&{cert_issuer} $)',
+`R$*			$: $1 $| $&{cert_issuer}')
+R$* $| $+		$: $1 $| $(access CERTISSUER:$2 $)
+dnl use $# to stop further checks (delay_check)
+R$* $| RELAY		$# RELAYCERTISSUER
+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')
+
 undivert(9)dnl LOCAL_RULESETS
 ifdef(`_FFR_MILTER', `
 #
@@ -1882,3 +2105,4 @@
 ######################################################################
 ######################################################################
 undivert(7)dnl MAILER_DEFINITIONS
+
Index: gnu/usr.sbin/sendmail/cf/m4/version.m4
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/cf/m4/version.m4,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/cf/m4/version.m4	2000/04/07 19:20:30	1.2
+++ gnu/usr.sbin/sendmail/cf/m4/version.m4	2001/05/29 01:31:11	1.5
@@ -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 Eric P. Allman.  All rights reserved.
 # Copyright (c) 1988, 1993
@@ -11,8 +11,8 @@
 # the sendmail distribution.
 #
 #
-VERSIONID(`$Sendmail: version.m4,v 8.39 2000/04/06 20:30:53 gshapiro Exp $')
+VERSIONID(`$Sendmail: version.m4,v 8.39.4.29 2001/05/27 21:39:20 gshapiro Exp $')
 #
 divert(0)
 # Configuration version number
-DZ8.10.1`'ifdef(`confCF_VERSION', `/confCF_VERSION')
+DZ8.11.4`'ifdef(`confCF_VERSION', `/confCF_VERSION')
Index: gnu/usr.sbin/sendmail/cf/mailer/local.m4
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/cf/mailer/local.m4,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/cf/mailer/local.m4	2000/04/02 19:05:56	1.1.1.1
+++ gnu/usr.sbin/sendmail/cf/mailer/local.m4	2001/01/15 21:08:57	1.2
@@ -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
@@ -27,7 +27,7 @@
 ###   Local and Program Mailer specification   ###
 ##################################################
 
-VERSIONID(`$Sendmail: local.m4,v 8.50 1999/11/21 19:02:08 ca Exp $')
+VERSIONID(`$Sendmail: local.m4,v 8.50.16.2 2000/09/17 17:04:22 gshapiro Exp $')
 
 #
 #  Envelope sender rewriting
@@ -78,7 +78,7 @@
 `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_CHARSET', `C=', `, ')T=DNS/RFC822/LOCAL_MAILER_DSN_DIAGNOSTIC_CODE,
+		_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,
 		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,
Index: gnu/usr.sbin/sendmail/cf/mailer/smtp.m4
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/cf/mailer/smtp.m4,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- gnu/usr.sbin/sendmail/cf/mailer/smtp.m4	2000/04/07 19:20:31	1.2
+++ gnu/usr.sbin/sendmail/cf/mailer/smtp.m4	2001/01/15 21:08:57	1.3
@@ -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,17 +14,17 @@
 _DEFIFNOT(`_DEF_SMTP_MAILER_FLAGS', `mDFMuX')
 _DEFIFNOT(`SMTP_MAILER_FLAGS',`')
 _DEFIFNOT(`RELAY_MAILER_FLAGS', `SMTP_MAILER_FLAGS')
-ifdef(`SMTP_MAILER_ARGS',, `define(`SMTP_MAILER_ARGS', `IPC $h')')
-ifdef(`ESMTP_MAILER_ARGS',, `define(`ESMTP_MAILER_ARGS', `IPC $h')')
-ifdef(`SMTP8_MAILER_ARGS',, `define(`SMTP8_MAILER_ARGS', `IPC $h')')
-ifdef(`DSMTP_MAILER_ARGS',, `define(`DSMTP_MAILER_ARGS', `IPC $h')')
-ifdef(`RELAY_MAILER_ARGS',, `define(`RELAY_MAILER_ARGS', `IPC $h')')
+ifdef(`SMTP_MAILER_ARGS',, `define(`SMTP_MAILER_ARGS', `TCP $h')')
+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')')
 POPDIVERT
 #####################################
 ###   SMTP Mailer specification   ###
 #####################################
 
-VERSIONID(`$Sendmail: smtp.m4,v 8.56 2000/04/03 20:54:55 ca Exp $')
+VERSIONID(`$Sendmail: smtp.m4,v 8.56.2.1.2.3 2000/09/25 13:53:27 ca Exp $')
 
 #
 #  common sender and masquerading recipient rewriting
@@ -101,17 +101,17 @@
 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_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,
 		A=SMTP_MAILER_ARGS
-Mesmtp,		P=[IPC], F=CONCAT(_DEF_SMTP_MAILER_FLAGS, `a', SMTP_MAILER_FLAGS), 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_CHARSET', `C=', `, ')T=DNS/RFC822/SMTP,
+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,
 		A=ESMTP_MAILER_ARGS
-Msmtp8,		P=[IPC], F=CONCAT(_DEF_SMTP_MAILER_FLAGS, `8', SMTP_MAILER_FLAGS), 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_CHARSET', `C=', `, ')T=DNS/RFC822/SMTP,
+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,
 		A=SMTP8_MAILER_ARGS
-Mdsmtp,		P=[IPC], F=CONCAT(_DEF_SMTP_MAILER_FLAGS, `a%', SMTP_MAILER_FLAGS), 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_CHARSET', `C=', `, ')T=DNS/RFC822/SMTP,
+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,
 		A=DSMTP_MAILER_ARGS
-Mrelay,		P=[IPC], F=CONCAT(_DEF_SMTP_MAILER_FLAGS, `a8', RELAY_MAILER_FLAGS), 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=', `, ')T=DNS/RFC822/SMTP,
+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,
 		A=RELAY_MAILER_ARGS
Index: gnu/usr.sbin/sendmail/contrib/bitdomain.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/contrib/bitdomain.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.2
diff -u -r1.1.1.1 -r1.1.1.2
--- gnu/usr.sbin/sendmail/contrib/bitdomain.c	2000/04/02 19:05:57	1.1.1.1
+++ gnu/usr.sbin/sendmail/contrib/bitdomain.c	2001/01/15 20:52:40	1.1.1.2
@@ -51,7 +51,7 @@
 {
     int opt;
 
-    while ((opt = getopt(argc, argv, "o:")) != EOF) {
+    while ((opt = getopt(argc, argv, "o:")) != -1) {
 	switch (opt) {
 	case 'o':
 	    if (!freopen(optarg, "w", stdout)) {
@@ -187,7 +187,7 @@
 	    case NO_DATA:
 		err = "registered in DNS, but not mailable";
 		break;
-		
+
 	    default:
 		err = "unknown nameserver error";
 		break;
@@ -210,7 +210,7 @@
 	int hbsize;
 {
 	register u_char *eom, *ap;
-	register int n; 
+	register int n;
 	HEADER *hp;
 	querybuf answer;
 	int ancount, qdcount;
@@ -406,4 +406,4 @@
 	}
     }
 }
-	    
+
Index: gnu/usr.sbin/sendmail/contrib/domainmap.m4
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/contrib/domainmap.m4,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.2
diff -u -r1.1.1.1 -r1.1.1.2
--- gnu/usr.sbin/sendmail/contrib/domainmap.m4	2000/04/02 19:05:57	1.1.1.1
+++ gnu/usr.sbin/sendmail/contrib/domainmap.m4	2001/01/15 20:52:40	1.1.1.2
@@ -58,7 +58,7 @@
 ifdef(`_DOMAIN_MAP_',`',`dnl
 LOCAL_RULE_0
 # do mapping for domains where applicable
-R$* $=O $* <@ $={MappedDomain} .>	$@ $>97 $1 $2 $3	Strip extraneous routing
+R$* $=O $* <@ $={MappedDomain} .>	$@ $>Recurse $1 $2 $3	Strip extraneous routing
 R$+ <@ $={MappedDomain} .>		$>DomainMapLookup $1 <@ $2 .>	domain mapping
 
 LOCAL_RULESETS
@@ -69,22 +69,35 @@
 SDomainMapLookup
 R $=L <@ $=w .>		$@ $1 <@ $2 .>		weed out local users, in case
 #						Cw contains a mapped domain
-R $+ <@ $+ .>		$1 <@ $2 >		strip trailing dot
-R $+ <@ $+ . $+ >	$1 <@ $(dequote $2 "_" $3 $) >
+ifdef(`DOMAINMAP_NO_REGEX',`dnl
+R $+ <@ $+>		$: $1 <@ $2> <$2>	find domain
+R $+ <$+> <$+ . $+>	$1 <$2> < $(dequote $3 "_" $4 $) >
 #						change "." to "_"
-R $+ <@ $+ >		$: $1 <@ $(dequote "domain_" $2 $) >
+R $+ <$+> <$+ .>	$: $1 <$2> < $(dequote "domain_" $3 $) >
 #						prepend "domain_"
-R $+ + $+ <@ $*>	$1 <@ $3 > <+> $2	handle user+list syntax
-R $+ <@ $* > $*		$( $2 $1 $: <ERROR> $) $3
+dnl',`dnl
+R $+ <@ $+>		$: $1 <@ $2> <$2 :NOTDONE:>	find domain
+R $+ <$+> <$+ . :NOTDONE:>	$1 <$2> < $(domainmap_regex $3 $: $3 $) >
+#						change "." and "-" to "_"
+R $+ <$+> <$+>		$: $1 <$2> < $(dequote "domain_" $3 $) >
+#						prepend "domain_"
+dnl')
+R $+ <$+> <$+>		$: $1 <$2> <$3> $1	find user name
+R $+ <$+> <$+> $+ + $*	$: $1 <$2> <$3> $4	handle user+detail syntax
+R $+ <$+> <$+> $+	$: $1 <$2> $( $3 $4 $: <ERROR> $)
 #						do actual domain map lookup
-R <ERROR> $*		$#error $@ 5.1.1 $: "550 email address lookup in domain map failed"
-R $* <TEMP> $*		$#error $@ 4.3.0 $: "450 domain map temporarily unavailable"
-R $+ @ $+ <+> $+	$1 + $3 @ $2		reset original user+list
-R $+ <+> $*		$1			paranoid check - remove <+>
-R $+ @ $+ .		$1 @ $2			strip trailing dot
-R $+ @ $+		$@ $>97 $1 @ $2		recanonify
-define(`_DOMAIN_MAP_',`1')')
+R $+ <$+> <ERROR>	$#error $@ 5.1.1 $: "550 email address lookup in domain map failed"
+R $+ <@ $+> $* <TEMP> $*	$#dsmtp $@ localhost $: $1 @ $2
+#						queue it up for later delivery
+R $+ + $* <$+> $+ @ $+		$: $1 + $2 <$3> $4 + $2 @ $5
+#						reset original user+detail
+R $+ <$+> $+		$@ $>Recurse $3		recanonify
+
+ifdef(`DOMAINMAP_NO_REGEX',`',`dnl
+LOCAL_CONFIG
+K domainmap_regex regex -a.:NOTDONE: -s1,2 -d_ (.*)[-\.]([^-\.]*)$
+')define(`_DOMAIN_MAP_',`1')')
 
 LOCAL_CONFIG
 C{MappedDomain} _ARG_
-K `domain_'translit(_ARG_, `.', `_') _ARG2_ -T<TEMP>
+K `domain_'translit(_ARG_, `.-', `__') _ARG2_ -T<TEMP>
Index: gnu/usr.sbin/sendmail/contrib/passwd-to-alias.pl
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/contrib/passwd-to-alias.pl,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.2
diff -u -r1.1.1.1 -r1.1.1.2
--- gnu/usr.sbin/sendmail/contrib/passwd-to-alias.pl	2000/04/02 19:05:57	1.1.1.1
+++ gnu/usr.sbin/sendmail/contrib/passwd-to-alias.pl	2001/01/15 20:52:41	1.1.1.2
@@ -8,22 +8,23 @@
 
 print "# Generated from passwd by $0\n";
 
+$wordpat = '([a-zA-Z]+?[a-zA-Z0-9-]*)?[a-zA-Z0-9]';	# 'DB2'
 while (@a = getpwent) {
     ($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = @a;
 
     ($fullname = $gcos) =~ s/,.*$//;
 
-    if (!-d $dir || !-x $shell) {
-	print "$name: root\n";
+    if (!-d $dir || !-x $shell || $shell =~ m!/bin/(false|true)$!) {
+	print "$name: root\n";				# handle pseudo user
     }
 
     $fullname =~ s/\.*[ _]+\.*/./g;
-    $fullname =~ tr [�������] [aaoAAOe];  # <hakan@af.lu.se> 1997-06-15
-    if ($fullname =~ /^[a-zA-Z][a-zA-Z-]+(\.[a-zA-Z][a-zA-Z-]+)+$/) {  
-#   if ($fullname =~ /^[a-zA-Z]+(\.[a-zA-Z]+)+$/) {    # Kari E. Hurtta
+    $fullname =~ tr [���������] [aaeouAAOU];  # <hakan@af.lu.se> 1997-06-15
+    next if (!$fullname || lc($fullname) eq $name);	# avoid nonsense
+    if ($fullname =~ /^$wordpat(\.$wordpat)*$/o) {	# Ulrich Windl
 	print "$fullname: $name\n";
     } else {
-	print "# $fullname: $name\n";
+	print "# $fullname: $name\n";			# avoid strange names
     }
 };
 
Index: gnu/usr.sbin/sendmail/contrib/qtool.8
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/contrib/qtool.8,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/contrib/qtool.8	2000/04/02 19:05:57	1.1.1.1
+++ gnu/usr.sbin/sendmail/contrib/qtool.8	2001/01/15 21:09:00	1.2
@@ -6,11 +6,11 @@
 .\" the sendmail distribution.
 .\"
 .\"
-.\"     $Sendmail: qtool.8,v 8.9 1999/08/26 00:04:10 cying Exp $
+.\"     $Sendmail: qtool.8,v 8.9.16.2 2000/12/15 19:50:41 gshapiro Exp $
 .\"
-.TH QTOOL 8 "July 12, 1999"
+.TH QTOOL 8 "$Date: 2001/01/15 21:09:00 $"
 .SH NAME
-.B qtool
+qtool
 \- manipulate sendmail queues
 .SH SYNOPSIS
 .B qtool.pl
Index: gnu/usr.sbin/sendmail/contrib/qtool.pl
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/contrib/qtool.pl,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/contrib/qtool.pl	2000/04/02 19:05:57	1.1.1.1
+++ gnu/usr.sbin/sendmail/contrib/qtool.pl	2001/01/15 21:09:00	1.2
@@ -1,9 +1,9 @@
 #!/usr/bin/env perl
 ##
-## Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+## Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
 ##       All rights reserved.
 ##
-## $Sendmail: qtool.pl,v 8.15 1999/08/30 19:18:37 peterh Exp $
+## $Sendmail: qtool.pl,v 8.15.16.4 2000/11/30 07:14:01 gshapiro Exp $
 ##
 use strict;
 use File::Basename;
@@ -133,13 +133,12 @@
 	if ($result)
 	{
 		print("$result.\n");
+		exit;
 	}
 }
 
 if (keys(%sources) == 0)
 {
-	print("You must at least specify at least one source.\n");
-	usage();
 	exit;
 }
 
@@ -164,7 +163,7 @@
 	print("    -b                   Bounce the messages specified by source.\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 older than seconds.\n");
+	print("    -s [seconds]         Move only messages whose qf file is older than seconds.\n");
 }
 
 ##
@@ -705,6 +704,14 @@
 	}
 }
 
+sub last_modified_time
+{
+	my $self = shift;
+	my @result;
+	@result = stat($self->{data_file}->{file_name});
+	return $result[9];
+}
+
 sub TIEHASH
 {
 	my $this = shift;
@@ -914,7 +921,7 @@
 	}
 
 	@control_files = grep { /^qf.*/ && -f "$control_dir/$_" } readdir(QUEUE_DIR);
-	closedir(DIR);
+	closedir(QUEUE_DIR);
 	foreach $file_name (@control_files)
 	{
 		$id = substr($file_name, 2);
Index: gnu/usr.sbin/sendmail/contrib/re-mqueue.pl
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/contrib/re-mqueue.pl,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.2
diff -u -r1.1.1.1 -r1.1.1.2
--- gnu/usr.sbin/sendmail/contrib/re-mqueue.pl	2000/04/02 19:05:58	1.1.1.1
+++ gnu/usr.sbin/sendmail/contrib/re-mqueue.pl	2001/01/15 20:52:41	1.1.1.2
@@ -93,6 +93,17 @@
 #	Allow zero-length df files (empty message body)
 #	Preserve $! for error messages
 #
+# Updated by Graeme Hewson <ghewson@uk.oracle.com> April 2000
+#
+#	Improve handling of race between re-mqueue and sendmail
+#
+# Updated by Graeme Hewson <graeme.hewson@oracle.com> June 2000
+#
+#	Don't exit(0) at end so can be called as subroutine
+#
+# NB This program can't handle separate qf/df/xf subdirectories
+# as introduced in sendmail 8.10.0.
+#
 
 use Sys::Syslog;
 
@@ -136,18 +147,17 @@
     ($qfile = $dfile) =~ s/^d/q/;
     ($xfile = $dfile) =~ s/^d/x/;
     ($mfile = $dfile) =~ s/^df//;
-    if (! -e $dfile) {
-	print "$dfile is gone - skipping\n" if ($debug);
-	next;
-    }
     if (! -e $qfile || -z $qfile) {
 	print "$qfile is gone or zero bytes - skipping\n" if ($debug);
 	next;
     }
 
-    $mtime = $now;
     ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
      $atime,$mtime,$ctime,$blksize,$blocks) = stat($dfile);
+    if (! defined $mtime) {
+	print "$dfile is gone - skipping\n" if ($debug);
+	next;
+    }
 
     # Compare timestamps
     if (($mtime + $age) > $now) {
@@ -182,6 +192,17 @@
     }
     print "$qfile now flock()ed\n" if ($debug);
 
+    # Check df* file again in case sendmail got in
+    if (! -e $dfile) {
+	print "$mfile sent - skipping\n" if ($debug);
+	# qf* file created by ourselves at open? (Almost certainly)
+	if (-z $qfile) {
+	   unlink($qfile);
+	}
+	close(QF);
+	next;
+    }
+
     # Show time!  Do the link()s
     if (link("$dfile", "$queueB/$dfile") == 0) {
 	$bang = $!;
@@ -235,4 +256,3 @@
     &syslog('info', '%s moved to %s', $mfile, $queueB);
     print "Done with $dfile $qfile\n\n" if ($debug);
 }
-exit 0;
Index: gnu/usr.sbin/sendmail/contrib/smcontrol.pl
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/contrib/smcontrol.pl,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.2
diff -u -r1.1.1.1 -r1.1.1.2
--- gnu/usr.sbin/sendmail/contrib/smcontrol.pl	2000/04/02 19:05:58	1.1.1.1
+++ gnu/usr.sbin/sendmail/contrib/smcontrol.pl	2001/01/15 20:52:41	1.1.1.2
@@ -162,7 +162,7 @@
 	my $cooked = "";
 	my $daemonStatus = "";
 
-	if ($raw =~ /^(\d+)\/(\d+)\/(\d+)\/(\d+)$/mg)
+	if ($raw =~ /^(\d+)\/(\d+)\/(\d+)\/(\d+)/mg)
 	{
 		$cooked .= "Current number of children: $1";
 		if ($2 > 0)
Index: gnu/usr.sbin/sendmail/doc/op/op.me
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/doc/op/op.me,v
retrieving revision 1.3
retrieving revision 1.6
diff -u -r1.3 -r1.6
--- gnu/usr.sbin/sendmail/doc/op/op.me	2000/04/07 19:20:32	1.3
+++ gnu/usr.sbin/sendmail/doc/op/op.me	2001/05/29 01:31:11	1.6
@@ -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, 1995 Eric P. Allman.  All rights reserved.
 .\" Copyright (c) 1983, 1993
@@ -9,7 +9,7 @@
 .\" the sendmail distribution.
 .\"
 .\"
-.\"	$Sendmail: op.me,v 8.317 2000/04/06 21:05:27 gshapiro Exp $
+.\"	$Sendmail: op.me,v 8.317.4.64 2001/05/24 16:45:49 ca Exp $
 .\"
 .\" eqn op.me | pic | troff -me
 .eh 'SMM:08-%''Sendmail Installation and Operation Guide'
@@ -32,7 +32,6 @@
 \\$1 \\$2.  \\$3
 .)x
 ..
-.sc
 .+c
 .(l C
 .sz 16
@@ -54,10 +53,10 @@
 .de Ve
 Version \\$2
 ..
-.Ve $Revision: 1.3 $
+.Ve $Revision: 1.6 $
 .rm Ve
 .sp
-For Sendmail Version 8.10
+For Sendmail Version 8.11
 .)l
 .(f
 Sendmail is a trademark of Sendmail, Inc.
@@ -100,12 +99,13 @@
 RFC1870 (SMTP SIZE Extension),
 RFC1891 (SMTP Delivery Status Notifications),
 RFC1892 (Multipart/Report),
-RFC1893 (Mail System Status Codes),
+RFC1893 (Enhanced Mail System Status Codes),
 RFC1894 (Delivery Status Notifications),
 RFC1985 (SMTP Service Extension for Remote Message Queue Starting),
 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),
 and
 RFC2554 (SMTP Service Extension for Authentication).
 However, since
@@ -148,12 +148,6 @@
 The appendixes give a brief
 but detailed explanation of a number of features
 not described in the rest of the paper.
-.bp
-.rs
-.sp |4i
-.ce 2
-This page intentionally left blank;
-replace it with a blank sheet for double-sided output.
 .bp 7
 .sh 1 "BASIC INSTALLATION"
 .pp
@@ -265,6 +259,7 @@
 .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 "Tweaking the Makefile"
 .pp
 .\" .b "XXX This should all be in the Site Configuration File section."
@@ -629,7 +624,7 @@
 .pp
 This command is also a link to
 .i sendmail .
-It flushes all information that is stored in the
+It flushes expired (Timeout.hoststatus) information that is stored in the
 .b HostStatusDirectory
 tree.
 .sh 3 "/var/spool/mqueue"
@@ -649,9 +644,9 @@
 To use multiple queues,
 supply a value ending with an asterisk.
 For example,
-.i /var/spool/mqueue/q*
+.i /var/spool/mqueue/qd*
 will use all of the directories or symbolic links to directories
-beginning with `q' in
+beginning with `qd' in
 .i /var/spool/mqueue
 as queue directories.
 Do not change the queue directory structure
@@ -915,6 +910,10 @@
 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 relay
 The machine from which it was received.
 .lp
@@ -929,7 +928,7 @@
 whose credentials we use for delivery.
 .ip delay
 The total delay between the time this message was received
-and the time it was delivered.
+and the current delivery attempt.
 .ip xdelay
 The amount of time needed in this delivery attempt
 (normally indicative of the speed of the connection).
@@ -937,6 +936,8 @@
 The name of the mailer used to deliver to this recipient.
 .ip relay
 The name of the host that actually accepted (or rejected) this recipient.
+.ip dsn
+The enhanced error code (RFC2034) if available.
 .ip stat
 The delivery status.
 .lp
@@ -1140,6 +1141,9 @@
 the
 .i purgestat
 command and is completely safe.
+However,
+.i purgestat
+only removes expired (Timeout.hoststatus) data.
 The information in these directories can
 be perused with the
 .i hoststat
@@ -1157,7 +1161,7 @@
 .b Timeout.hoststatus
 option.
 .pp
-The connection information stored on disk may be purged at any time
+The connection information stored on disk may be expired at any time
 with the
 .i purgestat
 command or by invoking sendmail with the
@@ -1173,7 +1177,8 @@
 The implementation of certain system services
 such as host and user name lookup
 is controlled by the service switch.
-If the host operating system supports such a switch
+If the host operating system supports such a switch,
+and sendmail knows about it,
 .i sendmail
 will use the native version.
 Ultrix, Solaris, and DEC OSF/1 are examples of such systems\**.
@@ -1516,6 +1521,7 @@
 .i sendmail
 redirects mail for that user
 to the list of addresses listed in the .forward file.
+Note that aliases are fully expanded before forward files are referenced.
 For example, if the home directory for user
 .q mckusick
 has a .forward file with contents:
@@ -1583,6 +1589,9 @@
 The Precedence: header can be used as a crude control of message priority.
 It tweaks the sort order in the queue
 and can be configured to change the message timeout values.
+The precedence of a message also controls how
+delivery status notifications (DSNs)
+are processed for that message.
 .sh 2 "IDENT Protocol Support"
 .pp
 .i Sendmail
@@ -1824,7 +1833,7 @@
 gives up its setuid root permissions
 when you use this flag, so it is common to use a publicly writable directory
 (such as /tmp)
-as the spool directory (QueueDirectory or Q option) while testing.
+as the queue directory (QueueDirectory or Q option) while testing.
 .sh 2 "Logging Traffic"
 .pp
 Many SMTP implementations do not fully implement the protocol.
@@ -2033,6 +2042,9 @@
 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.
 .sh 3 "Read timeouts"
 .pp
@@ -2401,7 +2413,7 @@
 .b QueueLA
 option
 plus one
-exceeds the priority of the message \(em
+is less than the priority of the message \(em
 that is, the message is queued iff:
 .EQ
 pri > { bold QueueFactor } over { LA - { bold QueueLA } + 1 }
@@ -2443,7 +2455,7 @@
 i	deliver interactively (synchronously)
 b	deliver in background (asynchronously)
 q	queue only (don't deliver)
-d	defer delvery attempts (don't deliver)
+d	defer delivery attempts (don't deliver)
 .)b
 There are tradeoffs.
 Mode
@@ -2521,7 +2533,8 @@
 Messages being deferred
 (due to a host being down, etc.).
 .ip 10
-Database expansion (alias, forward, and userdb lookups).
+Database expansion (alias, forward, and userdb lookups)
+and authentication information.
 .ip 11
 NIS errors and end of job processing.
 .ip 12
@@ -2602,6 +2615,8 @@
 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,
@@ -2682,7 +2697,7 @@
 Assume that the
 .i chown
 system call is restricted to root.
-Since some versions of Unix permit regular users
+Since some versions of UNIX permit regular users
 to give away their files to other users on some filesystems,
 .i sendmail
 often cannot assume that a given file was created by the owner,
@@ -2702,6 +2717,25 @@
 Allow the file named in the
 .b ErrorHeader
 option to be in an unsafe directory.
+.ip FileDeliveryToHardLink
+Allow delivery to files that are hard links.
+.ip FileDeliveryToSymLink
+Allow delivery to files that are symbolic links.
+.ip ForwardFileInGroupWritableDirPath
+Allow
+.i \&.forward
+files in group writable directories.
+.ip ForwardFileInUnsafeDirPath
+Allow
+.i \&.forward
+files in unsafe directories.
+.ip ForwardFileInUnsafeDirPathSafe
+Allow a
+.i \&.forward
+file that is in an unsafe directory to include references
+to program and files.
+.ip GroupWritableAliasFile
+Allow group-writable alias files.
 .ip GroupWritableDirPathSafe
 Change the definition of
 .q "unsafe directory"
@@ -2710,53 +2744,31 @@
 .ip GroupWritableForwardFileSafe
 Accept group-writable
 .i \&.forward
-files.
+files as safe for program and file delivery.
 .ip GroupWritableIncludeFileSafe
 Accept group-writable
 .i :include:
-files.
-.ip GroupWritableAliasFile
-Allow group-writable alias files.
+files as safe for program and file delivery.
 .ip HelpFileInUnsafeDirPath
 Allow the file named in the
 .b HelpFile
 option to be in an unsafe directory.
-.ip WorldWritableAliasFile
-Accept world-writable alias files.
-.ip ForwardFileInGroupWritableDirPath
-Allow
-.i \&.forward
-files in group writable directories.
 .ip IncludeFileInGroupWritableDirPath
 Allow
 .i :include:
 files in group writable directories.
-.ip ForwardFileInUnsafeDirPath
-Allow
-.i \&.forward
-files in unsafe directories.
 .ip IncludeFileInUnsafeDirPath
 Allow
 .i :include:
 files in unsafe directories.
-.ip ForwardFileInUnsafeDirPathSafe
-Allow a
-.i \&.forward
-file that is in an unsafe directory to include references
-to program and files.
 .ip IncludeFileInUnsafeDirPathSafe
 Allow a
 .i :include:
 file that is in an unsafe directory to include references
 to program and files.
-.ip MapInUnsafeDirPath
-Allow maps (e.g.,
-.i hash ,
-.i btree ,
-and
-.i dbm
-files)
-in unsafe directories.
+.ip InsufficientEntropy
+Try to use STARTTLS even if the PRNG for OpenSSL is not properly seeded
+despite the security problems.
 .ip LinkedAliasFileInWritableDir
 Allow an alias file that is a link in a writable directory.
 .ip LinkedClassFileInWritableDir
@@ -2774,14 +2786,28 @@
 .ip LinkedServiceSwitchFileInWritableDir
 Allow the service switch file to be a link
 even if the directory is writable.
-.ip FileDeliveryToHardLink
-Allow delivery to files that are hard links.
-.ip FileDeliveryToSymLink
-Allow delivery to files that are symbolic links.
+.ip MapInUnsafeDirPath
+Allow maps (e.g.,
+.i hash ,
+.i btree ,
+and
+.i dbm
+files)
+in unsafe directories.
+.ip NonRootSafeAddr
+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.
 .ip RunWritableProgram
 Go ahead and run programs that are group- or world-writable.
+.ip TrustStickyBit
+Allow group or world writable directories
+if the sticky bit is set on the directory.
+Do not set this on systems which do not honor
+the sticky bit on directories.
+.ip WorldWritableAliasFile
+Accept world-writable alias files.
 .ip WriteMapToHardLink
 Allow writes to maps that are hard links.
 .ip WriteMapToSymLink
@@ -2790,14 +2816,6 @@
 Allow the status file to be a hard link.
 .ip WriteStatsToSymLink
 Allow the status file to be a symbolic link.
-.ip TrustStickyBit
-Allow group or world writable directories
-if the sticky bit is set on the directory.
-Do not set this on systems which do not honor
-the sticky bit on directories.
-.ip NonRootSafeAddr
-Do not mark file and program deliveries as unsafe
-if sendmail is not running with root privileges.
 .sh 2 "Connection Caching"
 .pp
 When processing the queue,
@@ -3177,6 +3195,10 @@
 .b $ \c
 .i x
 are performed when the configuration file is read.
+A literal
+.b $
+can be included using
+.b $$ .
 Expansions of the form
 .b $& \c
 .i x
@@ -3335,13 +3357,14 @@
 .b $#
 syntax should
 .i only
-be used in ruleset zero
-or a subroutine of ruleset zero.
+be used in ruleset zero,
+a subroutine of ruleset zero,
+or rulesets that return decisions (e.g., check_rcpt).
 It causes evaluation of the ruleset to terminate immediately,
 and signals to
 .i sendmail
 that the address has completely resolved.
-The complete syntax is:
+The complete syntax for ruleset 0 is:
 .(b
 \fB$#\fP\fImailer\fP \fB$@\fP\fIhost\fP \fB$:\fP\fIuser\fP
 .)b
@@ -3467,8 +3490,8 @@
 .)c
 
 .\}
-.el .ie !"\*(.T"" \
-\{\
+.el \{\
+.ie !"\*(.T"" \{\
 .PS
 boxwid = 0.3i
 boxht = 0.3i
@@ -3499,6 +3522,7 @@
 .PE
 .\}
 .el .sp 2i
+.\}
 .ce
 Figure 1 \*- Rewriting set semantics
 .(c
@@ -3596,7 +3620,10 @@
 .pp
 The
 .i check_relay
-ruleset is called after a connection is accepted.
+ruleset is called after a connection is accepted by the daemon.
+It is not called when sendmail is started using the
+.b \-bs
+option.
 It is passed
 .(b
 client.host.name $| client.host.address
@@ -3727,6 +3754,31 @@
 .q error
 mailer the AUTH= parameter is not trusted and hence
 not passed on to the next relay.
+.sh 4 "tls_client"
+.pp
+The
+.i tls_client
+ruleset is called when sendmail acts as server, after a STARTTLS command
+has been issued, and from
+.i check_mail.
+The parameter is the value of
+.b ${verify}
+and STARTTLS or MAIL, respectively.
+If the ruleset does resolve to the
+.q error
+mailer, the appropriate error code is returned to the client.
+.sh 4 "tls_server"
+.pp
+The
+.i tls_server
+ruleset is called when sendmail acts as client after a STARTTLS command
+(should) have been issued.
+The parameter is the value of
+.b ${verify} .
+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 3 "IPC mailers"
 .pp
 Some special processing occurs
@@ -3780,6 +3832,8 @@
 .pp
 Macros are named with a single character
 or with a word in {braces}.
+The names ``x'' and ``{x}'' denote the same macro
+for every single character ``x''.
 Single character names may be selected from the entire ASCII set,
 but user-defined macros
 should be selected from the set of upper case letters only.
@@ -4024,10 +4078,24 @@
 .ip ${auth_type}
 The mechanism used for authentication
 (only set if successful).
+.ip ${auth_ssf}
+The keylength (in bits) of the symmetric encryption algorithm
+used for the security layer of a SASL mechanism.
 .ip ${bodytype}
 The message body type
 (7BIT or 8BITMIME),
 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).
+.ip ${cert_subject}
+The DN of the presented certificate (called the cert subject).
+.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.
+.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.
 Defined in the SMTP server only.
@@ -4035,8 +4103,9 @@
 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
-IP address is not resolvable, or if the resolved
-name doesn't match ${client_name}.
+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.
 .ip ${client_port}
 The port number of the SMTP client.
@@ -4116,12 +4185,17 @@
 .ip ${if_addr}
 The IP address of the interface of an incoming connection
 unless it is in the loopback net.
+.ip ${if_family}
+The IP family of the interface of an incoming connection
+unless it is in the loopback net.
 .ip ${if_name}
 The name of the interface of an incoming connection.
 This macro can be used for
 SmtpGreetingMessage and HReceived for virtual hosting.
 For example:
-O SmtpGreetingMessage=$?{if_name}${if_name}$|$j$. Sendmail $v/$Z; $b
+.(b
+O SmtpGreetingMessage=$?{if_name}${if_name}$|$j$. MTA
+.)b
 .ip ${mail_addr}
 The address part of the resolved triple of the address given for the
 .sm "SMTP MAIL"
@@ -4137,6 +4211,13 @@
 .sm "SMTP MAIL"
 command.
 Defined in the SMTP server only.
+.ip ${msg_size}
+The value of the SIZE= parameter,
+i.e., usually the size of the message (in an ESMTP dialogue),
+before the message has been collected, thereafter
+the message size as computed by
+.i sendmail 
+(and can be used in check_compat).
 .ip ${ntries}
 The number of delivery attempts.
 .ip ${opMode}
@@ -4168,6 +4249,28 @@
 .sm "SMTP RCPT"
 command.
 Defined in the SMTP server only.
+.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.
+.ip ${verify}
+The result of the verification of the presented cert.
+Possible values are:
+.(b
+.ta 9n
+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.
+SOFTWARE STARTTLS handshake failed,
+	which is a fatal error for this session,
+	the e-mail will be queued.
+.)b
 .pp
 There are three types of dates that can be used.
 The
@@ -4408,6 +4511,9 @@
 .br
 .b F \c
 .i c\|file
+.br
+.b F \c
+.i c\||program
 .)b
 The first form defines the class
 .i c
@@ -4434,11 +4540,22 @@
 CHucbmonet
 .)b
 are equivalent.
-The ``F'' form
-reads the elements of the class
+The ``F'' forms
+read the elements of the class
 .i c
 from the named
-.i file .
+.i file
+or
+.i program .
+Each element should be listed on a separate line.
+To specify an optional file, use ``-o'' between the class
+name and the file name, e.g.,
+.(b
+Fc -o /path/to/file
+.)b
+If the file can't be used,
+.i sendmail
+will not complain but silently ignore it.
 .pp
 Elements of classes can be accessed in rules using
 .b $=
@@ -5178,12 +5295,18 @@
 (as with the other
 .b check_ *
 rulesets).
+The ruleset receives the header field-body as argument,
+i.e., not the header field-name; see also
+${hdr_name} and ${currHeader}.
 The header is treated as a structured field,
 that is,
-comments (in parentheses) are deleted before processing,
+text in parentheses is deleted before processing,
 unless the second form
 .b $>+
 is used.
+Note: only one ruleset can be associated with a header;
+.i sendmail
+will silently ignore multiple entries.
 .pp
 For example, the configuration lines:
 .(b
@@ -5271,7 +5394,7 @@
 ``\c
 .i class \c
 .b :
-.i file ''
+.i info ''
 where
 .i class \c
 .b :
@@ -5286,6 +5409,10 @@
 (if
 .sm NEWDB
 is specified),
+.q btree
+(if
+.sm NEWDB
+is specified),
 .q dbm
 (if
 .sm NDBM
@@ -5293,6 +5420,13 @@
 .q stab
 (internal symbol table \*- not normally used
 unless you have no other database lookup),
+.q sequence
+(use a sequence of maps
+previously declared),
+.q ldap
+(if
+.sm LDAPMAP
+is specified),
 or
 .q nis
 (if
@@ -5362,6 +5496,14 @@
 .i c .
 Unquoted spaces in addresses are replaced by this character.
 Defaults to space (i.e., no change is made).
+.ip CACERTPath
+[no short name]
+Path to directory with certificates of CAs.
+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.
 .ip CheckAliases
 [n]
 Validate the RHS of aliases when rebuilding the alias database.
@@ -5373,7 +5515,7 @@
 addresses sent.
 If your system crashes during delivery to a large list,
 this prevents retransmission to any but the last
-.I N
+.i N
 recipients.
 .ip ClassFactor=\fIfact\fP
 [z]
@@ -5387,6 +5529,10 @@
 and subtracted from the priority.
 Thus, messages with a higher Priority: will be favored.
 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.
 .ip ClientPortOptions=\fIoptions\fP
 [O]
 Set client SMTP options.
@@ -5416,6 +5562,9 @@
 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.
 .ip ColonOkInAddr
 [no short name]
 If set, colons are acceptable in e-mail addresses
@@ -5498,9 +5647,13 @@
 and the load average of the machine expressed as an integer.
 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.
 .ip DaemonPortOptions=\fIoptions\fP
 [O]
 Set server SMTP options.
+Each instance of DaemonPortOptions leads to an additional incoming socket.
 The options are
 .i key=value
 pairs.
@@ -5523,12 +5676,17 @@
 .i Addr ess
 mask may be a numeric address in dot notation
 or a network name.
+The 
+.i Family
+key defaults to INET (IPv4).
+IPv6 users who wish to also accept IPv6 connections
+should add additional Family=inet6 DaemonPortOptions lines.
 .i Modifier
 can be a sequence (without any delimiters)
 of the following characters:
 .(b
 .ta 1i
-a	require authentication
+a	always require authentication
 b	bind to interface through which mail has been received
 c	perform hostname canonification (.cf)
 f	require fully qualified hostname (.cf)
@@ -5537,7 +5695,7 @@
 E	disallow ETRN (see RFC 2476)
 .)b
 That is, one way to specify a message submission agent (MSA) that
-requires authentication is:
+always requires authentication is:
 .(b
 O DaemonPortOptions=Name=MSA, Port=587, M=Ea
 .)b
@@ -5545,6 +5703,12 @@
 effect in the standard configuration file, in which
 they are available via
 .b ${daemon_flags} .
+Notice: Do
+.b not
+use the ``a'' modifier on a public accessible MTA!
+It should only be used for a MSA that is accessed by authorized
+users for initial mail submission.
+Users must authenticate to use a MSA which has this option turned on.
 The flags ``c'' and ``C'' can change the default for
 hostname canonification in the
 .i sendmail.cf
@@ -5710,6 +5874,7 @@
 IncludeFileInUnsafeDirPath
 IncludeFileInUnsafeDirPathSafe
 IncludeFileIngroupWritableDirPath
+InsufficientEntropy
 LinkedAliasFileInWritableDir
 LinkedClassFileInWritableDir
 LinkedForwardFileInWritableDir
@@ -6017,7 +6182,7 @@
 .i sendmail
 will refuse connections when it has more than
 .i N
-children processing incoming mail.
+children processing incoming mail or automatic queue runs.
 This does not limit the number of outgoing connections.
 If not set, there is no limit to the number of children --
 that is, the system load averaging controls this.
@@ -6093,7 +6258,7 @@
 gives a 452 response
 to the MAIL command.
 This invites the sender to try again later.
-.ip MinQueueAge=\fPage\fP
+.ip MinQueueAge=\fIage\fP
 [no short name]
 Don't process any queued jobs
 that have been in the queue less than the indicated time interval.
@@ -6178,6 +6343,7 @@
 copies of error messages will be sent to the named
 .i postmaster .
 Only the header of the failed message is sent.
+Errors resulting from messages with a negative precedence will not be sent.
 Since most errors are user problems,
 this is probably not a good idea on large sites,
 and arguably contains all sorts of privacy violations,
@@ -6211,6 +6377,7 @@
 nobodyreturn	Don't return the body of a message with DSNs
 goaway	Disallow essentially all SMTP status queries
 authwarnings	Put X-Authentication-Warning: headers in messages
+		and log warnings
 .)b
 .(f
 \**N.B.:
@@ -6319,6 +6486,13 @@
 Use that form instead of the
 .q QueueTimeout
 form.
+.ip RandFile
+[no short name]
+Name of file containing random data or the name of the UNIX socket
+if EGD is used.
+A (required) prefix "egd:" or "file:" specifies the type.
+STARTTLS requires this filename if the compile flag HASURANDOMDEV is not set
+(see sendmail/README).
 .ip ResolverOptions=\fIoptions\fP
 [I]
 Set resolver options.
@@ -6399,7 +6573,7 @@
 .i user
 Also, all file and program deliveries will be marked unsafe
 unless the option
-.b DontBlameSendmail=NonRootAddrSafe
+.b DontBlameSendmail=NonRootSafeAddr
 is set,
 in which case the delivery will be done as
 .i user .
@@ -6468,7 +6642,7 @@
 .ip SaveFromLine
 [f]
 Save
-Unix-style
+UNIX-style
 .q From
 lines at the front of headers.
 Normally they are assumed redundant
@@ -6482,6 +6656,13 @@
 will not return the DSN keyword in response to an EHLO
 and will not do Delivery Status Notification processing as described in
 RFC1891.
+.ip ServerCertFile
+[no short name]
+File containing the certificate of the server, i.e., this certificate
+is used when sendmail acts as server.
+.ip ServerKeyFile
+[no short name]
+File containing the private key belonging to the server certificate.
 .ip ServiceSwitchFile=\fIfilename\fP
 [no short name]
 If your host operating system has a service switch abstraction
@@ -6653,13 +6834,13 @@
 (very unlikely).
 .ip UnsafeGroupWrites
 [no short name]
-If set,
+If set (default),
 :include: and .forward files that are group writable are considered
 .q unsafe ,
 that is,
 they cannot reference programs or write directly to files.
 World writable :include: and .forward files
-are always unsafe..
+are always unsafe.
 .ip UseErrorsTo
 [l]
 If there is an
@@ -7114,7 +7295,7 @@
 Mark Roth, roth@uiuc.edu.
 For more information,
 consult the web site
-.q http://www-wsg.cso.uiuc.edu/sendmail/sendmail-phmap/ .
+.q http://www-dev.cso.uiuc.edu/sendmail/ .
 .ip nsd
 nsd map for IRIX 6.5 and later.
 Contributed and supported by Bob Mende of SGI,
@@ -7258,6 +7439,14 @@
 .(b
 -s1,3,4
 .)b
+Notes: to match a
+.b $ 
+in a string,
+\\$$
+must be used.
+If the pattern contains spaces, they must be replaced
+with the blank substitution character, unless it is
+space itself.
 .ip program
 The arguments on the
 .b K
@@ -7489,6 +7678,10 @@
 Set search scope to one of base, one (one level), or sub (subtree).
 .ip "\-h\fIhost\fP"
 LDAP server hostname.
+Some LDAP libraries allow you to specify multiple, space-separated hosts for
+redundancy.
+In addition, each of the hosts listed can be followed by a colon and a port
+number to override the default LDAP port.
 .ip "\-b\fIbase\fP"
 LDAP search base.
 .ip "\-p\fIport\fP"
@@ -7791,6 +7984,14 @@
 .ip SASL
 Compile in support for SASL,
 a required component for SMTP Authentication support.
+.ip STARTTLS
+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
@@ -7851,6 +8052,7 @@
 .ip "MAXMAILERS [25]"
 The maximum number of mailers that may be defined
 in the configuration file.
+This value is defined in include/sendmail/sendmail.h.
 .ip "MAXRWSETS [200]"
 The maximum number of rewriting sets
 that may be defined.
@@ -7911,6 +8113,7 @@
 .ip NETINET6\(dg
 If set,
 support for IPv6 networking is compiled in.
+It must be separately enabled by adding DaemonPortOptions settings.
 .ip NETISO\(dg
 If set,
 support for ISO protocol networking is compiled in
@@ -8493,6 +8696,92 @@
 .b $]
 lookups.
 We now recommend that you create a new keyed map instead.
+.sh 2 "Certificates for STARTTLS"
+.pp
+In this section we assume that
+.i sendmail
+has been compiled with support for STARTTLS.
+When acting as a server,
+.i sendmail
+requires X.509 certificates to support STARTTLS:
+one as certificate for the server (ServerCertFile)
+at least one root CA (CACERTFile),
+i.e., a certificate that is used to sign other certificates,
+and a path to a directory which contains other CAs (CACERTPath).
+The file specified via
+CACERTFile
+can contain several certificates of CAs.
+The DNs of these certificates are sent
+to the client during the TLS handshake (as part of the
+CertificateRequest) as the list of acceptable CAs.
+The CACERTPath directory must contain the hashes of each CA certificate
+as filenames (or as links to them).
+Symbolic links can be generated with the following
+two (Bourne) shell commands:
+.(b
+C=FileName_of_CA_Certificate
+ln -s $C `openssl x509 -noout -hash < $C`.0
+.)b
+An X.509 certificate is also required for authentication in client mode
+(ClientCertFile), however,
+.i sendmail
+will always use STARTTLS when offered by a server.
+The client and server certificates can be identical.
+Certificates can be obtained from a certificate authority
+or created with the help of OpenSSL.
+The required format for certificates and private keys is PEM.
+To allow for automatic startup of sendmail, private keys
+(ServerKeyFile, ClientKeyFile)
+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"
+.pp
+STARTTLS requires a strong pseudo random number generator (PRNG)
+to operate properly.
+Depending on the TLS library you use, it may be required to explicitly
+initialize the PRNG with random data.
+OpenSSL makes use of
+.b /dev/urandom(4)
+if available (this corresponds to the compile flag HASURANDOMDEV).
+On systems which lack this support, a random file must be specified in the
+.i sendmail.cf
+file using the option RandFile.
+It is
+.b strongly
+advised to use the "Entropy Gathering Daemon" EGD
+from Brian Warner on those systems to provide useful random data.
+In this case,
+.i sendmail 
+must be compiled with the flag EGD, and the
+RandFile option must point to the EGD socket.
+If neither
+.b /dev/urandom(4)
+nor EGD are available, you have to make sure
+that useful random data is available all the time in RandFile.
+If the file hasn't been modified in the last 10 minutes before
+it is supposed to be used by
+.i sendmail 
+the content is considered obsolete.
+One method for generating this file is:
+.(b
+openssl rand -out /etc/mail/randfile -rand \c
+.i /path/to/file:... \c
+256
+.)b
+See the OpenSSL documentation for more information.
+In this case, the PRNG for TLS is only
+seeded with other random data if the
+.b DontBlameSendmail
+option
+.b InsufficientEntropy
+is set.
+This is most likely not sufficient for certain actions, e.g.,
+generation of (temporary) keys.
+.pp
+Please see the OpenSSL documentation or other sources
+for further information about certificates, their creation and their usage,
+the importance of a good PRNG, and other aspects of TLS.
 .sh 1 "ACKNOWLEDGEMENTS"
 .pp
 I've worked on
@@ -8685,7 +8974,7 @@
 .i value
 (for long form option names).
 These options are described in Section 5.6.
-.ip \-M\fIx\|value
+.ip \-M\fIx\|value\fP
 Set macro
 .i x
 to the specified
@@ -8859,7 +9148,7 @@
 .ip N
 Envelope number
 .ip PPPPP
-First five digits of the process ID
+At least five digits of the process ID
 .pp
 All files with the same id collectively define one message.
 If memory-buffered files are available,
@@ -8901,6 +9190,11 @@
 Defaults to version zero.
 Must be the first line of the file if present.
 For 8.10 the version number is 4.
+.ip A
+The information given by the AUTH= parameter of the
+.q "MAIL FROM:"
+command or $f@$j
+if sendmail has been called directly.
 .ip H
 A header definition.
 There may be any number of these lines.
@@ -8982,10 +9276,6 @@
 .ip $
 A macro definition.
 The values of certain macros
-(as of this writing, only
-.b $r
-and
-.b $s )
 are passed through to the queue run phase.
 .ip B
 The body type.
@@ -9013,26 +9303,31 @@
 nothing can replace looking at what your own system generates.
 .)f
 .(b
-P835771
-T404261372
+V4
+T711358135
+K904446490
+N0
+P2100941
+$_eric@localhost
+${daemon_flags}
 Seric
-Ceric:sendmail@vangogh.CS.Berkeley.EDU
-Reric@mammoth.Berkeley.EDU
-Rbostic@okeeffe.CS.Berkeley.EDU
-H?P?Return-path: <owner-sendmail@vangogh.CS.Berkeley.EDU>
-HReceived: by vangogh.CS.Berkeley.EDU (5.108/2.7) id AAA06703;
+Ceric:100:1000:sendmail@vangogh.CS.Berkeley.EDU
+RPFD:eric@mammoth.Berkeley.EDU
+RPFD:bostic@okeeffe.CS.Berkeley.EDU
+H?P?Return-path: <^g>
+H??Received: by vangogh.CS.Berkeley.EDU (5.108/2.7) id AAA06703;
 	Fri, 17 Jul 1992 00:28:55 -0700
-HReceived: from mail.CS.Berkeley.EDU by vangogh.CS.Berkeley.EDU (5.108/2.7)
+H??Received: from mail.CS.Berkeley.EDU by vangogh.CS.Berkeley.EDU (5.108/2.7)
 	id AAA06698; Fri, 17 Jul 1992 00:28:54 -0700
-HReceived: from [128.32.31.21] by mail.CS.Berkeley.EDU (5.96/2.5)
+H??Received: from [128.32.31.21] by mail.CS.Berkeley.EDU (5.96/2.5)
 	id AA22777; Fri, 17 Jul 1992 03:29:14 -0400
-HReceived: by foo.bar.baz.de (5.57/Ultrix3.0-C)
+H??Received: by foo.bar.baz.de (5.57/Ultrix3.0-C)
 	id AA22757; Fri, 17 Jul 1992 09:31:25 GMT
 H?F?From: eric@foo.bar.baz.de (Eric Allman)
 H?x?Full-name: Eric Allman
-HMessage-id: <9207170931.AA22757@foo.bar.baz.de>
-HTo: sendmail@vangogh.CS.Berkeley.EDU
-HSubject: this is an example message
+H??Message-id: <9207170931.AA22757@foo.bar.baz.de>
+H??To: sendmail@vangogh.CS.Berkeley.EDU
+H??Subject: this is an example message
 .)b
 This shows
 the person who sent the message,
@@ -9130,7 +9425,7 @@
 .\".sz 10
 .\"Eric Allman
 .\".sp
-.\"Version $Revision: 1.3 $
+.\"Version $Revision: 1.6 $
 .\".ce 0
 .bp 3
 .ce
Index: gnu/usr.sbin/sendmail/include/libmilter/mfapi.h
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/include/libmilter/mfapi.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/include/libmilter/mfapi.h	2000/04/02 19:05:49	1.1.1.1
+++ gnu/usr.sbin/sendmail/include/libmilter/mfapi.h	2001/01/15 21:09:01	1.2
@@ -7,7 +7,7 @@
  * the sendmail distribution.
  *
  *
- *	$Sendmail: mfapi.h,v 8.13 2000/02/26 19:13:36 gshapiro Exp $
+ *	$Sendmail: mfapi.h,v 8.13.4.12 2000/09/09 02:11:48 ca Exp $
  */
 
 /*
@@ -17,126 +17,75 @@
 #ifndef _LIBMILTER_MFAPI_H
 # define _LIBMILTER_MFAPI_H	1
 
-/*
-**  Access common MTA/libmilter constants
-*/
+# define LIBMILTER_API		extern
 
-# include "libmilter/milter.h"
-
-/*
-**  Currently this is a C-fied version of ~eric/public_html/mfapi.html
-**  It does not (yet) conform with the coding standard...
-*/
 
-/*
-**  status codes
-*/
+# include <sys/types.h>
 
-/* XXX maybe use enum? */
+#ifndef _SOCK_ADDR
+# include <sys/socket.h>
+# define _SOCK_ADDR	struct sockaddr
+#endif /* ! _SOCK_ADDR */
 
 /*
-**  Continue processing message/connection.
+**  libmilter functions return one of the following to indicate
+**  success/failure:
 */
 
-#define SMFIS_CONTINUE	0
-
-/*
-**  Reject the message/connection.
-**  No further routines will be called for this message
-**  (or connection, if returned from a connection-oriented routine).
-*/
+#define MI_SUCCESS	0
+#define MI_FAILURE	(-1)
 
-#define SMFIS_REJECT	1
-
-/*
-**  Accept the message,
-**  but silently discard the message.
-**  No further routines will be called for this message.
-**  This is only meaningful from message-oriented routines.
-*/
-
-#define SMFIS_DISCARD	2
+/* "forward" declarations */
+typedef struct smfi_str SMFICTX;
+typedef struct smfi_str *SMFICTX_PTR;
 
-/*
-**  Accept the message/connection.
-**  No further routines will be called for this message
-**  (or connection, if returned from a connection-oriented routine;
-**  in this case, it causes all messages on this connection
-**  to be accepted without filtering).
-*/
+typedef struct smfiDesc smfiDesc_str;
+typedef struct smfiDesc	*smfiDesc_ptr;
 
-#define SMFIS_ACCEPT	3
+#define SMFI_VERSION	2		/* version number */
 
 /*
-**  Return a temporary failure, i.e.,
-**  the corresponding SMTP command will return a 4xx status code.
-**  In some cases this may prevent further routines from
-**  being called on this message or connection,
-**  although in other cases (e.g., when processing an envelope
-**  recipient) processing of the message will continue.
+**  What the filter might do -- values to be ORed together for
+**  smfiDesc.xxfi_flags.
 */
-#define SMFIS_TEMPFAIL	4
-
-/* type to store return value */
-typedef int	sfsistat;
-
-/* for now ... */
-#if SOCKADDRHACK
-# ifndef _SOCK_ADDR
-#  define _SOCK_ADDR	struct sockaddr_in
-# endif /* !_SOCK_ADDR */
-#else /* SOCKADDRHACK */
-# define NOT_SENDMAIL	1
-# include "sendmail.h"
-# define _SOCK_ADDR	SOCKADDR
-#endif /* SOCKADDRHACK */
-
-#include <pthread.h>
-
-#ifndef MI_SUCCESS
-# define MI_SUCCESS	0
-#endif /* MI_SUCCESS */
-#ifndef MI_FAILURE
-# define MI_FAILURE	(-1)
-#endif /* MI_FAILURE */
-
-/* "forward" declarations */
-typedef struct smfi_str SMFICTX;
-typedef struct smfi_str *SMFICTX_PTR;
 
-typedef struct smfiDesc smfiDesc_str;
-typedef struct smfiDesc	* smfiDesc_ptr;
+#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 MAX_MACROS_ENTRIES	4	/* max size of macro pointer array */
+#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 */
 
 /*
-**  context for milter
-**  implementation hint:
-**  macros are stored in mac_buf[] as sequence of:
-**  macro_name \0 macro_value
-**  (just as read from the MTA)
-**  mac_ptr is a list of pointers into mac_buf to the beginning of each
-**  entry, i.e., macro_name, macro_value, ...
+**  Type which callbacks should return to indicate message status.
+**  This may take on one of the SMFIS_* values listed below.
 */
 
-struct smfi_str
-{
-	pthread_t	ctx_id;		/* thread id */
-	int		ctx_fd;		/* filedescriptor */
-	int		ctx_dbg;	/* debug level */
-	time_t		ctx_timeout;	/* timeout */
-	int		ctx_state;	/* state */
-	smfiDesc_ptr	ctx_smfi;	/* filter description */
-	char		**ctx_mac_ptr[MAX_MACROS_ENTRIES];
-	char		*ctx_mac_buf[MAX_MACROS_ENTRIES];
-	char		*ctx_reply;	/* reply code */
-	void		*ctx_privdata;	/* private data */
-};
+typedef int	sfsistat;
 
 /*
 **  structure describing one milter
 */
 
+#if defined(__linux__) && defined(__GNUC__) && defined(__cplusplus) && __GNUC_MINOR__ >= 8
+# define SM__P(X)	__PMT(X)
+#else /* __linux__ && __GNUC__ && __cplusplus && _GNUC_MINOR__ >= 8 */
+# 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 */
@@ -144,64 +93,92 @@
 	u_long		xxfi_flags;	/* flags */
 
 	/* connection info filter */
-	sfsistat	(*xxfi_connect) __P((SMFICTX *, char *, _SOCK_ADDR *));
+	sfsistat	(*xxfi_connect) SM__P((SMFICTX *, char *, _SOCK_ADDR *));
 
 	/* SMTP HELO command filter */
-	sfsistat	(*xxfi_helo) __P((SMFICTX *, char *));
+	sfsistat	(*xxfi_helo) SM__P((SMFICTX *, char *));
 
 	/* envelope sender filter */
-	sfsistat	(*xxfi_envfrom) __P((SMFICTX *, char **));
+	sfsistat	(*xxfi_envfrom) SM__P((SMFICTX *, char **));
 
 	/* envelope recipient filter */
-	sfsistat	(*xxfi_envrcpt) __P((SMFICTX *, char **));
+	sfsistat	(*xxfi_envrcpt) SM__P((SMFICTX *, char **));
 
 	/* header filter */
-	sfsistat	(*xxfi_header) __P((SMFICTX *, char *, char *));
+	sfsistat	(*xxfi_header) SM__P((SMFICTX *, char *, char *));
 
 	/* end of header */
-	sfsistat	(*xxfi_eoh) __P((SMFICTX *));
+	sfsistat	(*xxfi_eoh) SM__P((SMFICTX *));
 
 	/* body block */
-	sfsistat	(*xxfi_body) __P((SMFICTX *, u_char *, size_t));
+	sfsistat	(*xxfi_body) SM__P((SMFICTX *, u_char *, size_t));
 
 	/* end of message */
-	sfsistat	(*xxfi_eom) __P((SMFICTX *));
+	sfsistat	(*xxfi_eom) SM__P((SMFICTX *));
 
 	/* message aborted */
-	sfsistat	(*xxfi_abort) __P((SMFICTX *));
+	sfsistat	(*xxfi_abort) SM__P((SMFICTX *));
 
 	/* connection cleanup */
-	sfsistat	(*xxfi_close) __P((SMFICTX *));
+	sfsistat	(*xxfi_close) SM__P((SMFICTX *));
 };
 
-#if 0
-simple example what a filter program should do:
+LIBMILTER_API int smfi_register __P((struct smfiDesc));
+LIBMILTER_API int smfi_main __P((void));
+LIBMILTER_API int smfi_setdbg __P((int));
+LIBMILTER_API int smfi_settimeout __P((int));
+LIBMILTER_API int smfi_setconn __P((char *));
+LIBMILTER_API int smfi_stop __P((void));
 
-int
-main(argc, argv)
-	int argc;
-	char **argv;
-{
-	struct smfiDesc	XxFilterDesc;
+/*
+**  Continue processing message/connection.
+*/
 
-	/* fill in elements */
-	smfi_register(XxFilterDesc);
-	smfi_main();
-	/* NOTREACHED */
-}
-#endif /* 0 */
+#define SMFIS_CONTINUE	0
+
+/*
+**  Reject the message/connection.
+**  No further routines will be called for this message
+**  (or connection, if returned from a connection-oriented routine).
+*/
 
-extern int smfi_register __P((smfiDesc_str));
-extern int smfi_main __P((void));
-extern int smfi_setdbg __P((int));
-extern int smfi_settimeout __P((int));
-extern int smfi_setconn __P((char *));
+#define SMFIS_REJECT	1
 
 /*
-**  Filter Routine Details
+**  Accept the message,
+**  but silently discard the message.
+**  No further routines will be called for this message.
+**  This is only meaningful from message-oriented routines.
+*/
+
+#define SMFIS_DISCARD	2
+
+/*
+**  Accept the message/connection.
+**  No further routines will be called for this message
+**  (or connection, if returned from a connection-oriented routine;
+**  in this case, it causes all messages on this connection
+**  to be accepted without filtering).
 */
 
+#define SMFIS_ACCEPT	3
+
+/*
+**  Return a temporary failure, i.e.,
+**  the corresponding SMTP command will return a 4xx status code.
+**  In some cases this may prevent further routines from
+**  being called on this message or connection,
+**  although in other cases (e.g., when processing an envelope
+**  recipient) processing of the message will continue.
+*/
+
+#define SMFIS_TEMPFAIL	4
+
 #if 0
+/*
+**  Filter Routine Details
+*/
+
 /* connection info filter */
 extern sfsistat	xxfi_connect __P((SMFICTX *, char *, _SOCK_ADDR *));
 
@@ -265,7 +242,6 @@
 /*
 **  xxfi_eoh(ctx) Invoked at end of header
 */
-#endif /* 0 */
 
 /* body block */
 extern sfsistat	xxfi_body __P((SMFICTX *, u_char *, size_t));
@@ -304,7 +280,7 @@
 **  xxfi_close(ctx) Invoked at end of the connection. This is called on
 **  close even if the previous mail transaction was aborted.
 */
-
+#endif /* 0 */
 
 /*
 **  Additional information is passed in to the vendor filter routines using
@@ -313,7 +289,7 @@
 */
 
 /* Return the value of a symbol. */
-extern char * smfi_getsymval __P((SMFICTX *, char *));
+LIBMILTER_API char * smfi_getsymval __P((SMFICTX *, char *));
 
 /*
 **  Return the value of a symbol.
@@ -327,7 +303,7 @@
 **  the MTA for use in SMTP replies may call smfi_setreply before returning.
 */
 
-extern int smfi_setreply __P((SMFICTX *, char *, char *, char *));
+LIBMILTER_API int smfi_setreply __P((SMFICTX *, char *, char *, char *));
 
 /*
 **  Set the specific reply code to be used in response to the active
@@ -348,7 +324,7 @@
 **  routine other than xxfi_eom.
 */
 
-extern int smfi_addheader __P((SMFICTX *, char *, char *));
+LIBMILTER_API int smfi_addheader __P((SMFICTX *, char *, char *));
 
 /*
 **  Add a header to the message. This header is not passed to other
@@ -360,8 +336,21 @@
 **	char *headerf; Header field name
 **	char *headerv; Header field value
 */
+
+LIBMILTER_API int smfi_chgheader __P((SMFICTX *, char *, int, char *));
+
+/*
+**  Change/delete a header in the message.  It is not checked for standards
+**  compliance; the mail filter must ensure that no protocols are violated
+**  as a result of adding this header.
+**
+**	SMFICTX *ctx; Opaque context structure
+**	char *headerf; Header field name
+**	int index; The Nth occurence of header field name
+**	char *headerv; New header field value (empty for delete header)
+*/
 
-extern int smfi_addrcpt __P((SMFICTX *, char *));
+LIBMILTER_API int smfi_addrcpt __P((SMFICTX *, char *));
 
 /*
 **  Add a recipient to the envelope
@@ -370,7 +359,7 @@
 **	char *rcpt; Recipient to be added
 */
 
-extern int smfi_delrcpt __P((SMFICTX *, char *));
+LIBMILTER_API int smfi_delrcpt __P((SMFICTX *, char *));
 
 /*
 **  Delete a recipient from the envelope
@@ -381,7 +370,7 @@
 **		not be deleted.
 */
 
-extern int smfi_replacebody __P((SMFICTX *, u_char *, int));
+LIBMILTER_API int smfi_replacebody __P((SMFICTX *, u_char *, int));
 
 /*
 **  Replace the body of the message. This routine may be called multiple
@@ -405,7 +394,7 @@
 **  data using smfi_getpriv.
 */
 
-extern int smfi_setpriv __P((SMFICTX *, void *));
+LIBMILTER_API int smfi_setpriv __P((SMFICTX *, void *));
 
 /*
 **  Set the private data pointer
@@ -413,7 +402,8 @@
 **	SMFICTX *ctx; Opaque context structure
 **	void *privatedata; Pointer to private data area
 */
+
+LIBMILTER_API void *smfi_getpriv __P((SMFICTX *));
 
-extern void *smfi_getpriv __P((SMFICTX *));
 
 #endif /* !_LIBMILTER_MFAPI_H */
Index: gnu/usr.sbin/sendmail/include/libmilter/milter.h
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/include/libmilter/milter.h,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- gnu/usr.sbin/sendmail/include/libmilter/milter.h	2000/04/02 19:05:49	1.1.1.1
+++ gnu/usr.sbin/sendmail/include/libmilter/milter.h	2001/05/29 01:31:12	1.3
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -7,7 +7,7 @@
  * the sendmail distribution.
  *
  *
- *	$Sendmail: milter.h,v 8.24 1999/11/28 05:54:20 gshapiro Exp $
+ *	$Sendmail: milter.h,v 8.24.16.9 2001/03/02 21:22:48 geir Exp $
  */
 
 /*
@@ -17,10 +17,13 @@
 #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 */
-# define SMFI_VERSION		1	/* version number */
 
 /* address families */
 # define SMFIA_UNKNOWN		'U'	/* unknown */
@@ -49,22 +52,56 @@
 # 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 */
+
+/* socket and thread portability */
+# include <pthread.h>
+typedef pthread_t	sthread_t;
+typedef int		socket_t;
+
+# define MAX_MACROS_ENTRIES	4	/* max size of macro pointer array */
+
+/*
+**  context for milter
+**  implementation hint:
+**  macros are stored in mac_buf[] as sequence of:
+**  macro_name \0 macro_value
+**  (just as read from the MTA)
+**  mac_ptr is a list of pointers into mac_buf to the beginning of each
+**  entry, i.e., macro_name, macro_value, ...
+*/
 
-/* values for filter negotiation flags */
-# define SMFIF_MODHDRS	0x00000001L	/* filter may add headers */
-# define SMFIF_MODBODY	0x00000002L	/* filter may replace body */
-# define SMFIF_ADDRCPT	0x00000004L	/* filter may add recipients */
-# define SMFIF_DELRCPT	0x00000008L	/* filter may delete recipients */
-# define SMFIF_NOCONNECT 0x00000010L	/* MTA should not send connect info */
-# define SMFIF_NOHELO	0x00000020L	/* MTA should not send HELO info */
-# define SMFIF_NOMAIL	0x00000040L	/* MTA should not send MAIL info */
-# define SMFIF_NORCPT	0x00000080L	/* MTA should not send RCPT info */
-# define SMFIF_NOBODY	0x00000100L	/* MTA should not send body */
-# define SMFIF_NOHDRS	0x00000200L	/* MTA should not send headers */
+struct smfi_str
+{
+	sthread_t	ctx_id;		/* thread id */
+	socket_t	ctx_sd;		/* socket descriptor */
+	int		ctx_dbg;	/* debug level */
+	time_t		ctx_timeout;	/* timeout */
+	int		ctx_state;	/* state */
+	smfiDesc_ptr	ctx_smfi;	/* filter description */
+	u_long		ctx_pflags;	/* protocol flags */
+	char		**ctx_mac_ptr[MAX_MACROS_ENTRIES];
+	char		*ctx_mac_buf[MAX_MACROS_ENTRIES];
+	char		*ctx_reply;	/* reply code */
+	void		*ctx_privdata;	/* private data */
+};
 
 #endif /* !_LIBMILTER_MILTER_H */
Index: gnu/usr.sbin/sendmail/include/libsmdb/smdb.h
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/include/libsmdb/smdb.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- gnu/usr.sbin/sendmail/include/libsmdb/smdb.h	2000/04/07 19:20:33	1.2
+++ gnu/usr.sbin/sendmail/include/libsmdb/smdb.h	2001/01/15 21:09:01	1.3
@@ -6,7 +6,7 @@
 ** forth in the LICENSE file which can be found at the top level of
 ** the sendmail distribution.
 **
-** $Sendmail: smdb.h,v 8.29 2000/03/17 07:32:42 gshapiro Exp $
+** $Sendmail: smdb.h,v 8.29.2.1.2.2 2000/10/05 22:23:55 gshapiro Exp $
 */
 
 #ifndef _SMDB_H_
@@ -18,6 +18,12 @@
 #  include "sendmail/cdefs.h"
 # endif /* __P */
 
+# ifndef NDBM
+#  ifndef NEWDB
+ERROR	NDBM or NEWDB must be defined.
+#  endif /* ! NEWDB */
+# endif /* ! NDBM */
+
 # ifdef NDBM
 #  include <ndbm.h>
 # endif /* NDBM */
@@ -47,7 +53,7 @@
 
 typedef struct database_struct SMDB_DATABASE;
 typedef struct cursor_struct SMDB_CURSOR;
-typedef union database_entity_union SMDB_DBENT;
+typedef struct entry_struct SMDB_DBENT;
 
 
 /*
@@ -183,6 +189,7 @@
 typedef int (*db_cursor_func) __P((SMDB_DATABASE *db,
 			      SMDB_CURSOR **cursor, u_int flags));
 
+typedef int (*db_lockfd_func) __P((SMDB_DATABASE *db));
 
 struct database_struct
 {
@@ -194,6 +201,7 @@
 	db_sync_func		smdb_sync;
 	db_set_owner_func	smdb_set_owner;
 	db_cursor_func		smdb_cursor;
+	db_lockfd_func		smdb_lockfd;
 	void			*smdb_impl;
 };
 
@@ -304,22 +312,12 @@
 
 typedef struct database_user_struct SMDB_USER_INFO;
 
-union database_entity_union
+struct entry_struct
 {
-# ifdef NDBM
-	datum	dbm;
-# endif /* NDBM */
-# ifdef NEWDB
-	DBT	db;
-# endif /* NEWDB */
-	struct
-	{
-		char	*data;
-		size_t	size;
-	} data;
+	void	*data;
+	size_t	size;
 };
 
-
 typedef char *SMDB_DBTYPE;
 typedef u_int SMDB_FLAG;
 
@@ -370,4 +368,6 @@
 					      struct stat *));
 extern void		smdb_print_available_types __P((void));
 extern char		*smdb_db_definition __P((SMDB_DBTYPE));
+extern int		smdb_lock_map __P((SMDB_DATABASE *, int));
+extern int		smdb_unlock_map __P((SMDB_DATABASE *));
 #endif /* ! _SMDB_H_ */
Index: gnu/usr.sbin/sendmail/include/sendmail/pathnames.h
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/include/sendmail/pathnames.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/include/sendmail/pathnames.h	2000/04/02 19:05:49	1.1.1.1
+++ gnu/usr.sbin/sendmail/include/sendmail/pathnames.h	2001/01/15 21:09:02	1.2
@@ -9,26 +9,28 @@
  * the sendmail distribution.
  *
  *
- *	$Sendmail: pathnames.h,v 8.16 2000/02/01 05:49:50 gshapiro Exp $
+ *	$Sendmail: pathnames.h,v 8.16.8.8 2000/09/28 21:26:39 gshapiro Exp $
  */
 
-#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_HOSTS
+#  define _PATH_HOSTS		"/etc/hosts"
+# endif /* ! _PATH_HOSTS */
+
 
Index: gnu/usr.sbin/sendmail/include/sendmail/sendmail.h
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/include/sendmail/sendmail.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- gnu/usr.sbin/sendmail/include/sendmail/sendmail.h	2000/04/07 19:20:34	1.2
+++ gnu/usr.sbin/sendmail/include/sendmail/sendmail.h	2001/01/15 21:09:02	1.3
@@ -10,14 +10,18 @@
  * the sendmail distribution.
  *
  *
- *	$Sendmail: sendmail.h,v 8.34 2000/03/16 22:05:28 gshapiro Exp $
+ *	$Sendmail: sendmail.h,v 8.34.4.7 2000/10/09 16:15:26 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 "conf.h"
 #include "sendmail/errstring.h"
@@ -49,6 +53,9 @@
 
 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))
 
@@ -144,9 +151,16 @@
 #define DBS_NONROOTSAFEADDR				31
 #define DBS_TRUSTSTICKYBIT				32
 #define DBS_DONTWARNFORWARDFILEINUNSAFEDIRPATH		33
+#define DBS_INSUFFICIENTENTROPY				34
 #if _FFR_UNSAFE_SASL
-#define DBS_GROUPREADABLESASLFILE			34
+# 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 */
 
 /* struct defining such things */
 struct dbsval
@@ -163,11 +177,10 @@
 #define dflush()	fflush(stdout)
 #endif /* _FFR_DPRINTF */
 
-#if !HASSNPRINTF
-extern int	snprintf __P((char *, size_t, const char *, ...));
-extern int	vsnprintf __P((char *, size_t, const char *, va_list));
-#endif /* !HASSNPRINTF */
+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));
+
Index: gnu/usr.sbin/sendmail/libmilter/README
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libmilter/README,v
retrieving revision 1.3
retrieving revision 1.6
diff -u -r1.3 -r1.6
--- gnu/usr.sbin/sendmail/libmilter/README	2000/04/07 19:20:34	1.3
+++ gnu/usr.sbin/sendmail/libmilter/README	2001/05/29 01:31:12	1.6
@@ -10,7 +10,7 @@
 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.10.X, you must compiled
+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:
 
@@ -18,6 +18,9 @@
 	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.
+
 +-------------------+
 | BUILDING A FILTER |
 +-------------------+
@@ -32,7 +35,8 @@
 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'.
+-lsocket -lnsl'.  Depending on your OS 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
@@ -40,6 +44,11 @@
 the Makefile in your appropriate obj.*/libmilter build subdirectory if you
 are unsure of the local flag used.
 
+Note that since filters use threads, it may be necessary to alter per
+process limits in your filter.  For example, you might look at using
+setrlimit() to increase the number of open file descriptors if your filter
+is going to be busy.
+
 
 +----------------------------------------+
 | SPECIFYING FILTERS IN SENDMAIL CONFIGS |
@@ -49,14 +58,14 @@
 
 For example:
 
-	Xfilter1, S=unix:/var/run/f1.sock, F=R
+	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
 
 specifies three filters.  Filters can be specified in your .mc file using
 the following:
 
-	INPUT_MAIL_FILTER(`filter1', `S=unix:/var/run/f1.sock, F=R')
+	INPUT_MAIL_FILTER(`filter1', `S=local:/var/run/f1.sock, F=R')
 	INPUT_MAIL_FILTER(`filter2', `S=inet6:999@localhost, F=T, T=S:1s;R:1s;E:5m')
 	INPUT_MAIL_FILTER(`filter3', `S=inet:3333@localhost')
 
@@ -67,6 +76,9 @@
 	R		Reject connection if filter unavailable
 	T		Temporary fail connection if filter unavailable
 
+If neither F=R nor F=T is specified, the message is passed through sendmail
+as if the filter were not present.
+
 Finally, you can override the default timeouts used by sendmail when
 talking to the filters using the T= equate.  There are three fields inside
 of the T= equate:
@@ -84,13 +96,18 @@
 
 where 's' is seconds and 'm' is minutes.
 
-Actual sequencing is handled by the InputMailFilters option which is set
-automatically according to the order of the INPUT_MAIL_FILTER commands
-in your .mc file.  Alternatively, you can reset it's value by setting
-confINPUT_MAIL_FILTERS in your .mc file.  This options causes the three
-filters to be called in the same order they were specified.  It allows
-for possible future filtering on output (although this is not intended
-for this release).
+Which filters are invoked and their sequencing is handled by the 
+InputMailFilters option. Note: if InputMailFilters is not defined no filters
+will be used.
+
+	O InputMailFilters=filter1, filter2, filter3
+
+This is is set automatically according to the order of the
+INPUT_MAIL_FILTER commands in your .mc file.  Alternatively, you can
+reset its value by setting confINPUT_MAIL_FILTERS in your .mc file.
+This options causes the three filters to be called in the same order
+they were specified.  It allows for possible future filtering on output
+(although this is not intended for this release).
 
 Also note that a filter can be defined without adding it to the input
 filter list by using MAIL_FILTER() instead of INPUT_MAIL_FILTER() in your
@@ -99,7 +116,7 @@
 To test sendmail with the sample filter, the following might be added (in
 the appropriate locations) to your .mc file:
 
-	INPUT_MAIL_FILTER(`sample', `S=unix:/var/run/f1.sock')
+	INPUT_MAIL_FILTER(`sample', `S=local:/var/run/f1.sock')
 
 
 +------------------+
@@ -115,7 +132,7 @@
 consistency with the suggested options for sendmail.cf, this would be the
 UNIX domain socket located in /var/run/f1.sock.
 
-	% ./sample -p unix:/var/run/f1.sock
+	% ./sample -p local:/var/run/f1.sock
 
 If the sample filter returns immediately to a command line, there was either
 an error with your command or a problem creating the specified socket.
@@ -130,7 +147,7 @@
 the use of standard SMTP commands.
 
 % sendmail -bs
-220 test.sendmail.com ESMTP Sendmail 8.10.0.Beta8/8.10.0.Beta8; Mon, 6 Dec 1999 19:34:23 -0800 (PST)
+220 test.sendmail.com ESMTP Sendmail 8.11.0/8.11.0; Tue, 10 Nov 1970 13:05:23 -0500 (EST)
 HELO localhost
 250 test.sendmail.com Hello testy@localhost, pleased to meet you
 MAIL From:<testy>
@@ -167,10 +184,30 @@
 | SOURCE FOR SAMPLE FILTER |
 +--------------------------+
 
+Note that the filter below may not be thread safe on some operating
+systems.  You should check your system man pages for the functions used
+below to verify the functions are thread safe.
+
 /* A trivial filter that logs all email to a file. */
 
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
 #include "libmilter/mfapi.h"
 
+typedef int bool;
+
+#ifndef FALSE
+# define FALSE	0
+#endif /* ! FALSE*/
+#ifndef TRUE
+# define TRUE	1
+#endif /* ! TRUE*/
+
 struct mlfiPriv
 {
 	char	*mlfi_fname;
@@ -187,7 +224,7 @@
 	char **envfrom;
 {
 	struct mlfiPriv *priv;
-	int fd;
+	int fd = -1;
 
 	/* allocate some private memory */
 	priv = malloc(sizeof *priv);
@@ -208,6 +245,8 @@
 	if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
 	    (priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
 	{
+		if (fd >= 0)
+			(void) close(fd);
 		free(priv->mlfi_fname);
 		free(priv);
 		return SMFIS_TEMPFAIL;
@@ -336,7 +375,7 @@
 {
 	"SampleFilter",	/* filter name */
 	SMFI_VERSION,	/* version code -- do not change */
-	SMFIF_MODHDRS,	/* flags */
+	SMFIF_ADDHDRS,	/* flags */
 	NULL,		/* connection info filter */
 	NULL,		/* SMTP HELO command filter */
 	mlfi_envfrom,	/* envelope sender filter */
@@ -355,7 +394,7 @@
 	int argc;
 	char *argv[];
 {
-	char c;
+	int c;
 	const char *args = "p:";
 
 	/* Process command line options */
@@ -385,4 +424,4 @@
 
 /* eof */
 
-$Revision: 1.3 $, Last updated $Date: 2000/04/07 19:20:34 $
+$Revision: 1.6 $, Last updated $Date: 2001/05/29 01:31:12 $
Index: gnu/usr.sbin/sendmail/libmilter/comm.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libmilter/comm.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/libmilter/comm.c	2000/04/02 19:05:58	1.1.1.1
+++ gnu/usr.sbin/sendmail/libmilter/comm.c	2001/01/15 21:09:02	1.2
@@ -9,24 +9,24 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: comm.c,v 8.30 2000/02/11 00:12:29 ca Exp $";
+static char id[] = "@(#)$Sendmail: comm.c,v 8.30.4.6 2000/10/05 22:44:01 gshapiro Exp $";
 #endif /* ! lint */
 
 #if _FFR_MILTER
 #include "libmilter.h"
 
 #define FD_Z	FD_ZERO(&readset);	\
-		FD_SET(fd, &readset);	\
+		FD_SET((u_int) sd, &readset);	\
 		FD_ZERO(&excset);	\
-		FD_SET(fd, &excset)
+		FD_SET((u_int) sd, &excset)
 
 /*
 **  MI_RD_CMD -- read a command
 **
 **	Parameters:
-**		fd -- file descriptor
+**		sd -- socket descriptor
 **		timeout -- maximum time to wait
-**		cmd -- single character command read from fd
+**		cmd -- single character command read from sd
 **		rlen -- pointer to length of result
 **		name -- name of milter
 **
@@ -37,8 +37,8 @@
 */
 
 char *
-mi_rd_cmd(fd, timeout, cmd, rlen, name)
-	int fd;
+mi_rd_cmd(sd, timeout, cmd, rlen, name)
+	socket_t sd;
 	struct timeval *timeout;
 	char *cmd;
 	size_t *rlen;
@@ -55,23 +55,25 @@
 
 	*cmd = '\0';
 	*rlen = 0;
-	if (fd >= FD_SETSIZE)
+
+	if (sd >= FD_SETSIZE)
 	{
 		smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
-			name, fd, FD_SETSIZE);
+			name, sd, FD_SETSIZE);
 		*cmd = SMFIC_SELECT;
 		return NULL;
 	}
+
 	FD_Z;
 	i = 0;
-	while ((ret = select(fd + 1, &readset, NULL, &excset, timeout)) >= 1)
+	while ((ret = select(sd + 1, &readset, NULL, &excset, timeout)) >= 1)
 	{
-		if (FD_ISSET(fd, &excset))
+		if (FD_ISSET(sd, &excset))
 		{
 			*cmd = SMFIC_SELECT;
 			return NULL;
 		}
-		if ((len = read(fd, data + i, sizeof data - i)) < 0)
+		if ((len = MI_SOCK_READ(sd, data + i, sizeof data - i)) < 0)
 		{
 			smi_log(SMI_LOG_ERR,
 				"%s, mi_rd_cmd: read returned %d: %s",
@@ -84,7 +86,7 @@
 			*cmd = SMFIC_EOF;
 			return NULL;
 		}
-		if (len >= sizeof data - i)
+		if (len >= (ssize_t) sizeof data - i)
 			break;
 		i += len;
 		FD_Z;
@@ -123,15 +125,15 @@
 
 	i = 0;
 	FD_Z;
-	while ((ret = select(fd + 1, &readset, NULL, &excset, timeout)) == 1)
+	while ((ret = select(sd + 1, &readset, NULL, &excset, timeout)) == 1)
 	{
-		if (FD_ISSET(fd, &excset))
+		if (FD_ISSET(sd, &excset))
 		{
 			*cmd = SMFIC_SELECT;
 			free(buf);
 			return NULL;
 		}
-		if ((len = read(fd, buf + i, expl - i)) < 0)
+		if ((len = MI_SOCK_READ(sd, buf + i, expl - i)) < 0)
 		{
 			smi_log(SMI_LOG_ERR,
 				"%s: mi_rd_cmd: read returned %d: %s",
@@ -181,10 +183,10 @@
 	return NULL;
 }
 /*
-**  MI_WR_CMD -- write a cmd to fd
+**  MI_WR_CMD -- write a cmd to sd
 **
 **	Parameters:
-**		fd -- file descriptor
+**		sd -- socket descriptor
 **		timeout -- maximum time to wait (currently unused)
 **		cmd -- single character command to write
 **		buf -- buffer with further data
@@ -195,8 +197,8 @@
 */
 
 int
-mi_wr_cmd(fd, timeout, cmd, buf, len)
-	int fd;
+mi_wr_cmd(sd, timeout, cmd, buf, len)
+	socket_t sd;
 	struct timeval *timeout;
 	int cmd;
 	char *buf;
@@ -217,17 +219,19 @@
 	i = 0;
 	sl = MILTER_LEN_BYTES + 1;
 
-	do {
+	do
+	{
 		FD_ZERO(&wrtset);
-		FD_SET(fd, &wrtset);
-		if ((ret = select(fd + 1, NULL, &wrtset, NULL, timeout)) == 0)
+		FD_SET((u_int) sd, &wrtset);
+		if ((ret = select(sd + 1, NULL, &wrtset, NULL, timeout)) == 0)
 			return MI_FAILURE;
 	} while (ret < 0 && errno == EINTR);
 	if (ret < 0)
 		return MI_FAILURE;
 
 	/* use writev() instead to send the whole stuff at once? */
-	while ((l = write(fd, (void *) (data + i), sl - i)) < sl)
+	while ((l = MI_SOCK_WRITE(sd, (void *) (data + i),
+				  sl - i)) < (ssize_t) sl)
 	{
 		if (l < 0)
 			return MI_FAILURE;
@@ -241,15 +245,17 @@
 		return MI_SUCCESS;
 	i = 0;
 	sl = len;
-	do {
+	do
+	{
 		FD_ZERO(&wrtset);
-		FD_SET(fd, &wrtset);
-		if ((ret = select(fd + 1, NULL, &wrtset, NULL, timeout)) == 0)
+		FD_SET((u_int) sd, &wrtset);
+		if ((ret = select(sd + 1, NULL, &wrtset, NULL, timeout)) == 0)
 			return MI_FAILURE;
 	} while (ret < 0 && errno == EINTR);
 	if (ret < 0)
 		return MI_FAILURE;
-	while ((l = write(fd, (void *) (buf + i), sl - i)) < sl)
+	while ((l = MI_SOCK_WRITE(sd, (void *) (buf + i),
+				  sl - i)) < (ssize_t) sl)
 	{
 		if (l < 0)
 			return MI_FAILURE;
Index: gnu/usr.sbin/sendmail/libmilter/engine.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libmilter/engine.c,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -r1.2 -r1.4
--- gnu/usr.sbin/sendmail/libmilter/engine.c	2000/04/07 19:20:34	1.2
+++ gnu/usr.sbin/sendmail/libmilter/engine.c	2001/02/28 02:43:51	1.4
@@ -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
@@ -9,7 +9,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: engine.c,v 8.67 2000/03/27 05:04:16 ca Exp $";
+static char id[] = "@(#)$Sendmail: engine.c,v 8.67.4.17 2001/01/22 19:00:16 gshapiro Exp $";
 #endif /* ! lint */
 
 #if _FFR_MILTER
@@ -20,9 +20,6 @@
 # include <arpa/inet.h>
 #endif /* NETINET || NETINET6 */
 
-/* length of options: two 32bit integers */
-#define MILTER_OPTLEN	8
-
 /* generic argument for functions in the command table */
 struct arg_struct
 {
@@ -87,7 +84,7 @@
 static int	st_rcpt __P((genarg *));
 static int	st_eoh __P((genarg *));
 static int	st_quit __P((genarg *));
-static int	sendreply __P((sfsistat, int, struct timeval *, SMFICTX_PTR));
+static int	sendreply __P((sfsistat, socket_t, struct timeval *, SMFICTX_PTR));
 static void	fix_stm __P((SMFICTX_PTR));
 static bool	trans_ok __P((int, int));
 static char	**dec_argv __P((char *, size_t));
@@ -110,6 +107,9 @@
 #define ST_LAST	ST_ABRT
 #define ST_SKIP	15	/* not a state but required for the state table */
 
+/* in a mail transaction? must be before eom according to spec. */
+#define ST_IN_MAIL(st)	((st) >= ST_MAIL && (st) < ST_ENDM)
+
 /*
 **  set of next states
 **  each state (ST_*) corresponds to bit in an int value (1 << state)
@@ -122,7 +122,7 @@
 #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_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_HDRS	(MASK(ST_EOHS) | MASK(ST_HDRS) | MASK(ST_ABRT))
@@ -187,11 +187,13 @@
 	SMFICTX_PTR ctx;
 {
 	size_t len;
-	int i, fd;
+	int i;
+	socket_t sd;
 	int ret = MI_SUCCESS;
 	int ncmds = sizeof(cmds) / sizeof(cmdfct);
 	int curstate = ST_INIT;
 	int newstate;
+	bool call_abort;
 	sfsistat r;
 	char cmd;
 	char *buf = NULL;
@@ -199,13 +201,17 @@
 	struct timeval timeout;
 	int (*f) __P((genarg *));
 	sfsistat (*fi_abort) __P((SMFICTX *));
+	sfsistat (*fi_close) __P((SMFICTX *));
 
 	arg.a_ctx = ctx;
-	fd = ctx->ctx_fd;
+	sd = ctx->ctx_sd;
 	fi_abort = ctx->ctx_smfi->xxfi_abort;
 	mi_clr_macros(ctx, 0);
 	fix_stm(ctx);
-	do {
+	do
+	{
+		/* call abort only if in a mail transaction */
+		call_abort = ST_IN_MAIL(curstate);
 		timeout.tv_sec = ctx->ctx_timeout;
 		timeout.tv_usec = 0;
 		if (mi_stop() == MILTER_ABRT)
@@ -216,12 +222,12 @@
 			ret = MI_FAILURE;
 			break;
 		}
-		if ((buf = mi_rd_cmd(fd, &timeout, &cmd, &len,
+		if ((buf = mi_rd_cmd(sd, &timeout, &cmd, &len,
 				     ctx->ctx_smfi->xxfi_name)) == NULL &&
 		    cmd < SMFIC_VALIDCMD)
 		{
 			if (ctx->ctx_dbg > 5)
-				dprintf("[%d] error (%x)\n",
+				dprintf("[%d] mi_engine: mi_rd_cmd error (%x)\n",
 					(int) ctx->ctx_id, (int) cmd);
 
 			/*
@@ -275,7 +281,9 @@
 					curstate, MASK(curstate),
 					newstate, MASK(newstate),
 					next_states[curstate]);
-			if (fi_abort != NULL)
+
+			/* call abort only if in a mail transaction */
+			if (fi_abort != NULL && call_abort)
 				(void) (*fi_abort)(ctx);
 
 			/*
@@ -303,12 +311,13 @@
 			free(buf);
 			buf = NULL;
 		}
-		if (sendreply(r, fd, &timeout, ctx) != MI_SUCCESS)
+		if (sendreply(r, sd, &timeout, ctx) != MI_SUCCESS)
 		{
 			ret = MI_FAILURE;
 			break;
 		}
 
+		call_abort = ST_IN_MAIL(curstate);
 		if (r == SMFIS_ACCEPT)
 		{
 			/* accept mail, no further actions taken */
@@ -337,16 +346,14 @@
 
 	if (ret != MI_SUCCESS)
 	{
-		if (fi_abort != NULL)
+		/* call abort only if in a mail transaction */
+		if (fi_abort != NULL && call_abort)
 			(void) (*fi_abort)(ctx);
 	}
-	else
-	{
-		sfsistat (*fi_close) __P((SMFICTX *));
 
-		if ((fi_close = ctx->ctx_smfi->xxfi_close) != NULL)
-			(void) (*fi_close)(ctx);
-	}
+	/* close must always be called */
+	if ((fi_close = ctx->ctx_smfi->xxfi_close) != NULL)
+		(void) (*fi_close)(ctx);
 	if (buf != NULL)
 		free(buf);
 	mi_clr_macros(ctx, 0);
@@ -357,7 +364,7 @@
 **
 **	Parameters:
 **		r -- reply code
-**		fd -- file descriptor
+**		sd -- socket descriptor
 **		timeout_ptr -- (ptr to) timeout to use for sending
 **		ctx -- context structure
 **
@@ -366,24 +373,24 @@
 */
 
 static int
-sendreply(r, fd, timeout_ptr, ctx)
+sendreply(r, sd, timeout_ptr, ctx)
 	sfsistat r;
-	int fd;
+	socket_t sd;
 	struct timeval *timeout_ptr;
 	SMFICTX_PTR ctx;
 {
 	int ret = MI_SUCCESS;
 
-	switch(r)
+	switch (r)
 	{
 	  case SMFIS_CONTINUE:
-		ret = mi_wr_cmd(fd, timeout_ptr, SMFIR_CONTINUE, NULL, 0);
+		ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_CONTINUE, NULL, 0);
 		break;
 	  case SMFIS_TEMPFAIL:
 	  case SMFIS_REJECT:
 		if (ctx->ctx_reply != NULL)
 		{
-			ret = mi_wr_cmd(fd, timeout_ptr, SMFIR_REPLYCODE,
+			ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_REPLYCODE,
 					ctx->ctx_reply,
 					strlen(ctx->ctx_reply) + 1);
 			free(ctx->ctx_reply);
@@ -391,15 +398,15 @@
 		}
 		else
 		{
-			ret = mi_wr_cmd(fd, timeout_ptr, r == SMFIS_REJECT ?
+			ret = mi_wr_cmd(sd, timeout_ptr, r == SMFIS_REJECT ?
 					SMFIR_REJECT : SMFIR_TEMPFAIL, NULL, 0);
 		}
 		break;
 	  case SMFIS_DISCARD:
-		ret = mi_wr_cmd(fd, timeout_ptr, SMFIR_DISCARD, NULL, 0);
+		ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_DISCARD, NULL, 0);
 		break;
 	  case SMFIS_ACCEPT:
-		ret = mi_wr_cmd(fd, timeout_ptr, SMFIR_ACCEPT, NULL, 0);
+		ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_ACCEPT, NULL, 0);
 		break;
 	  case _SMFIS_OPTIONS:
 		{
@@ -411,7 +418,10 @@
 			v = htonl(ctx->ctx_smfi->xxfi_flags);
 			(void) memcpy(&(buf[MILTER_LEN_BYTES]), (void *) &v,
 				      MILTER_LEN_BYTES);
-			ret = mi_wr_cmd(fd, timeout_ptr, SMFIC_OPTNEG, buf,
+			v = htonl(ctx->ctx_pflags);
+			(void) memcpy(&(buf[MILTER_LEN_BYTES * 2]), (void *) &v,
+				      MILTER_LEN_BYTES);
+			ret = mi_wr_cmd(sd, timeout_ptr, SMFIC_OPTNEG, buf,
 				       MILTER_OPTLEN);
 		}
 		break;
@@ -466,15 +476,17 @@
 st_optionneg(g)
 	genarg *g;
 {
-	mi_int32 i, version;
+	mi_int32 i, v;
 
 	if (g == NULL || g->a_ctx->ctx_smfi == NULL)
 		return SMFIS_CONTINUE;
 	mi_clr_macros(g->a_ctx, g->a_idx + 1);
-	if (g->a_len != MILTER_OPTLEN)
+
+	/* check for minimum length */
+	if (g->a_len < MILTER_OPTLEN)
 	{
 		smi_log(SMI_LOG_ERR,
-			"%s: st_optionneg[%d]: len mismatch %d != %d",
+			"%s: st_optionneg[%d]: len too short %d < %d",
 			g->a_ctx->ctx_smfi->xxfi_name,
 			(int) g->a_ctx->ctx_id, g->a_len,
 			MILTER_OPTLEN);
@@ -483,24 +495,52 @@
 
 	(void) memcpy((void *) &i, (void *) &(g->a_buf[0]),
 		      MILTER_LEN_BYTES);
-	version = ntohl(i);
-	if (version != g->a_ctx->ctx_smfi->xxfi_version)
+	v = ntohl(i);
+	if (v < g->a_ctx->ctx_smfi->xxfi_version)
 	{
+		/* hard failure for now! */
 		smi_log(SMI_LOG_ERR,
-			"%s: st_optionneg[%d]: version mismatch %d != %d",
+			"%s: st_optionneg[%d]: version mismatch MTA: %d < milter: %d",
 			g->a_ctx->ctx_smfi->xxfi_name,
-			(int) g->a_ctx->ctx_id, (int) version,
+			(int) g->a_ctx->ctx_id, (int) v,
 			g->a_ctx->ctx_smfi->xxfi_version);
 		return _SMFIS_ABORT;
 	}
 
-#if 0
-	/* flags are currently ignored */
 	(void) memcpy((void *) &i, (void *) &(g->a_buf[MILTER_LEN_BYTES]),
 		      MILTER_LEN_BYTES);
-	flags = ntohl(i);
-#endif /* 0 */
+	v = ntohl(i);
+
+	/* no flags? set to default value for V1 actions */
+	if (v == 0)
+		v = SMFI_V1_ACTS;
+	i = g->a_ctx->ctx_smfi->xxfi_flags;
+	if ((v & i) != i)
+	{
+		smi_log(SMI_LOG_ERR,
+			"%s: st_optionneg[%d]: 0x%x does not fulfill action requirements 0x%x",
+			g->a_ctx->ctx_smfi->xxfi_name,
+			(int) g->a_ctx->ctx_id, v, i);
+		return _SMFIS_ABORT;
+	}
 
+	(void) memcpy((void *) &i, (void *) &(g->a_buf[MILTER_LEN_BYTES * 2]),
+		      MILTER_LEN_BYTES);
+	v = ntohl(i);
+
+	/* no flags? set to default value for V1 protocol */
+	if (v == 0)
+		v = SMFI_V1_PROT;
+	i = g->a_ctx->ctx_pflags;
+	if ((v & i) != i)
+	{
+		smi_log(SMI_LOG_ERR,
+			"%s: st_optionneg[%d]: 0x%x does not fulfill protocol requirements 0x%x",
+			g->a_ctx->ctx_smfi->xxfi_name,
+			(int) g->a_ctx->ctx_id, v, i);
+		return _SMFIS_ABORT;
+	}
+
 	return _SMFIS_OPTIONS;
 }
 /*
@@ -518,9 +558,9 @@
 	genarg *g;
 {
 	size_t l;
-	int i;
+	size_t i;
 	char *s, family;
-	u_short port;
+	u_short port = 0;
 	_SOCK_ADDR sockaddr;
 	sfsistat (*fi_connect) __P((SMFICTX *, char *, _SOCK_ADDR *));
 
@@ -569,6 +609,8 @@
 				return _SMFIS_ABORT;
 			}
 			sockaddr.sa.sa_family = AF_INET;
+			if (port > 0)
+				sockaddr.sin.sin_port = port;
 		}
 		else
 # endif /* NETINET */
@@ -585,6 +627,8 @@
 				return _SMFIS_ABORT;
 			}
 			sockaddr.sa.sa_family = AF_INET6;
+			if (port > 0)
+				sockaddr.sin6.sin6_port = port;
 		}
 		else
 # endif /* NETINET6 */
@@ -763,7 +807,7 @@
 		return _SMFIS_FAIL;
 	if ((argv = dec_argv(g->a_buf + 1, g->a_len - 1)) == NULL)
 		return _SMFIS_FAIL;
-	switch(g->a_buf[0])
+	switch (g->a_buf[0])
 	{
 	  case SMFIC_CONNECT:
 		i = CI_CONN;
@@ -857,15 +901,15 @@
 		if ((fi_body = g->a_ctx->ctx_smfi->xxfi_body) != NULL &&
 		    g->a_len > 0)
 		{
-			int fd;
+			socket_t sd;
 			struct timeval timeout;
 
 			timeout.tv_sec = g->a_ctx->ctx_timeout;
 			timeout.tv_usec = 0;
-			fd = g->a_ctx->ctx_fd;
+			sd = g->a_ctx->ctx_sd;
 			r = (*fi_body)(g->a_ctx, (u_char *)g->a_buf, g->a_len);
 			if (r != SMFIS_CONTINUE &&
-			    sendreply(r, fd, &timeout, g->a_ctx) != MI_SUCCESS)
+			    sendreply(r, sd, &timeout, g->a_ctx) != MI_SUCCESS)
 				return _SMFIS_ABORT;
 		}
 	}
@@ -915,7 +959,8 @@
 	int s, n;
 
 	s = old;
-	do {
+	do
+	{
 		/* is this state transition allowed? */
 		if ((MASK(new) & next_states[s]) != 0)
 			return TRUE;
@@ -960,21 +1005,20 @@
 
 	if (ctx == NULL || ctx->ctx_smfi == NULL)
 		return;
-	fl = ctx->ctx_smfi->xxfi_flags;
-	if (bitset(SMFIF_NOCONNECT, fl))
+	fl = ctx->ctx_pflags;
+	if (bitset(SMFIP_NOCONNECT, fl))
 		next_states[ST_CONN] |= NX_SKIP;
-	if (bitset(SMFIF_NOHELO, fl))
+	if (bitset(SMFIP_NOHELO, fl))
 		next_states[ST_HELO] |= NX_SKIP;
-	if (bitset(SMFIF_NOMAIL, fl))
+	if (bitset(SMFIP_NOMAIL, fl))
 		next_states[ST_MAIL] |= NX_SKIP;
-	if (bitset(SMFIF_NORCPT, fl))
+	if (bitset(SMFIP_NORCPT, fl))
 		next_states[ST_RCPT] |= NX_SKIP;
-	if (bitset(SMFIF_NOHDRS, fl))
-	{
+	if (bitset(SMFIP_NOHDRS, fl))
 		next_states[ST_HDRS] |= NX_SKIP;
+	if (bitset(SMFIP_NOEOH, fl))
 		next_states[ST_EOHS] |= NX_SKIP;
-	}
-	if (bitset(SMFIF_NOBODY, fl))
+	if (bitset(SMFIP_NOBODY, fl))
 		next_states[ST_BODY] |= NX_SKIP;
 }
 /*
@@ -1019,7 +1063,7 @@
 
 	/* overwrite last entry */
 	s[elem] = NULL;
-	return (s);
+	return s;
 }
 /*
 **  DEC_ARG2 -- split a buffer into two strings
@@ -1040,7 +1084,7 @@
 	char **s1;
 	char **s2;
 {
-	int i;
+	size_t i;
 
 	*s1 = buf;
 	for (i = 1; i < len && buf[i] != '\0'; i++)
Index: gnu/usr.sbin/sendmail/libmilter/handler.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libmilter/handler.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/libmilter/handler.c	2000/04/02 19:05:58	1.1.1.1
+++ gnu/usr.sbin/sendmail/libmilter/handler.c	2001/01/15 21:09:02	1.2
@@ -9,14 +9,15 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: handler.c,v 8.19 2000/02/11 00:12:29 ca Exp $";
+static char id[] = "@(#)$Sendmail: handler.c,v 8.19.4.3 2000/12/29 19:45:39 gshapiro Exp $";
 #endif /* ! lint */
 
 #if _FFR_MILTER
 #include "libmilter.h"
 
+
 /*
-**  HANDLE_SESSION -- Handle a connected session in it's own context
+**  HANDLE_SESSION -- Handle a connected session in its own context
 **
 **	Parameters:
 **		ctx -- context structure
@@ -33,7 +34,7 @@
 
 	if (ctx == NULL)
 		return MI_FAILURE;
-	ctx->ctx_id = pthread_self();
+	ctx->ctx_id = (sthread_t) sthread_get_id();
 
 	/*
 	**  detach so resources are free when the thread returns
@@ -42,10 +43,16 @@
 	if (pthread_detach(ctx->ctx_id) != 0)
 		return MI_FAILURE;
 	ret = mi_engine(ctx);
-	if (ctx->ctx_fd >= 0)
-		(void) close(ctx->ctx_fd);
+	if (ValidSocket(ctx->ctx_sd))
+	{
+		(void) close(ctx->ctx_sd);
+		ctx->ctx_sd = INVALID_SOCKET;
+	}
 	if (ctx->ctx_reply != NULL)
+	{
 		free(ctx->ctx_reply);
+		ctx->ctx_reply = NULL;
+	}
 	if (ctx->ctx_privdata != NULL)
 	{
 		smi_log(SMI_LOG_WARN,
Index: gnu/usr.sbin/sendmail/libmilter/libmilter.h
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libmilter/libmilter.h,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- gnu/usr.sbin/sendmail/libmilter/libmilter.h	2000/04/02 19:05:58	1.1.1.1
+++ gnu/usr.sbin/sendmail/libmilter/libmilter.h	2001/05/29 01:31:12	1.3
@@ -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,7 +8,7 @@
  */
 
 /*
-**  MILTER.H -- include file for mail filter library functions
+**  LIBMILTER.H -- include file for mail filter library functions
 */
 
 #ifndef _LIBMILTER_H
@@ -17,21 +17,40 @@
 # define EXTERN
 # define INIT(x)	= x
 # ifndef lint
-static char MilterlId[] = "@(#)$Sendmail: libmilter.h,v 8.3 2000/02/26 01:32:13 gshapiro Exp $";
+static char MilterlId[] = "@(#)$Sendmail: libmilter.h,v 8.3.6.14 2001/05/27 14:31:12 ca Exp $";
 # endif /* ! lint */
 #else /* _DEFINE */
 # define EXTERN extern
 # define INIT(x)
 #endif /* _DEFINE */
 
+
+#define NOT_SENDMAIL	1
+#define _SOCK_ADDR	union bigsockaddr
+#include "sendmail.h"
+
 #include "libmilter/milter.h"
-#include "libmilter/mfapi.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))
+# define MI_SOCK_WRITE(s, b, l)	(write(s, b, l))
+
+# define thread_create(ptid,wr,arg) pthread_create(ptid, NULL, wr, arg)
+# define sthread_get_id()	pthread_self()
+
+typedef pthread_mutex_t smutex_t;
+# define smutex_init(mp)	(pthread_mutex_init(mp, NULL) == 0)
+# define smutex_destroy(mp)	(pthread_mutex_destroy(mp) == 0)
+# define smutex_lock(mp)	(pthread_mutex_lock(mp) == 0)
+# define smutex_unlock(mp)	(pthread_mutex_unlock(mp) == 0)
+# define smutex_trylock(mp)	(pthread_mutex_trylock(mp) == 0)
+
 #include <sys/time.h>
 
 /* version info */
@@ -39,12 +58,19 @@
 #define MILTER_VERSION		100
 
 /* some defaults */
-#define MI_TIMEOUT	1800		/* default timeout for read/write */
+#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 */
+
 /* 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() */
 
 /* internal "commands", i.e., error codes */
 #define SMFIC_TIMEOUT	((char) 1)	/* timeout */
@@ -65,8 +91,6 @@
 #define SMI_LOG_INFO	LOG_INFO
 #define SMI_LOG_DEBUG	LOG_DEBUG
 
-#define MI_INVALID_SOCKET	(-1)
-
 /* stop? */
 #define MILTER_CONT	0
 #define MILTER_STOP	1
@@ -75,17 +99,18 @@
 /* functions */
 extern int	mi_handle_session __P((SMFICTX_PTR));
 extern int	mi_engine __P((SMFICTX_PTR));
-extern int	mi_listener __P((char *, int, smfiDesc_ptr, time_t));
+extern int	mi_listener __P((char *, int, smfiDesc_ptr, time_t, int));
 extern void	mi_clr_macros __P((SMFICTX_PTR, int));
 extern int	mi_stop __P((void));
 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 void	mi_closener __P((void));
 
 /* communication functions */
-extern char	*mi_rd_cmd __P((int, struct timeval *, char *, size_t *, char *));
-extern int	mi_wr_cmd __P((int, struct timeval *, int, char *, size_t));
+extern char	*mi_rd_cmd __P((socket_t, struct timeval *, char *, size_t *, char *));
+extern int	mi_wr_cmd __P((socket_t, struct timeval *, int, char *, size_t));
 extern bool	mi_sendok __P((SMFICTX_PTR, int));
 
 #endif /* !_LIBMILTER_H */
Index: gnu/usr.sbin/sendmail/libmilter/listener.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libmilter/listener.c,v
retrieving revision 1.1.1.1
retrieving revision 1.4
diff -u -r1.1.1.1 -r1.4
--- gnu/usr.sbin/sendmail/libmilter/listener.c	2000/04/02 19:05:58	1.1.1.1
+++ gnu/usr.sbin/sendmail/libmilter/listener.c	2001/05/29 01:31:12	1.4
@@ -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
@@ -9,7 +9,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: listener.c,v 8.38 2000/02/11 00:12:30 ca Exp $";
+static char id[] = "@(#)$Sendmail: listener.c,v 8.38.2.1.2.22 2001/05/16 17:15:58 ca Exp $";
 #endif /* ! lint */
 
 #if _FFR_MILTER
@@ -19,10 +19,10 @@
 
 #include "libmilter.h"
 
-#if NETINET || NETINET6
-# include <arpa/inet.h>
-#endif /* NETINET || NETINET6 */
 
+# if NETINET || NETINET6
+#  include <arpa/inet.h>
+# endif /* NETINET || NETINET6 */
 /*
 **  MI_MILTEROPEN -- setup socket to listen on
 **
@@ -30,31 +30,33 @@
 **		conn -- connection description
 **		backlog -- listen backlog
 **		socksize -- socksize of created socket
+**		family -- family of created socket
+**		name -- name for logging
 **
 **	Returns:
 **		socket upon success, error code otherwise.
 */
 
-static int
-mi_milteropen(conn, backlog, socksize, name)
+static socket_t
+mi_milteropen(conn, backlog, socksize, family, name)
 	char *conn;
 	int backlog;
 	SOCKADDR_LEN_T *socksize;
+	int *family;
 	char *name;
 {
-	int sock = 0;
+	socket_t sock;
 	int sockopt = 1;
 	char *p;
 	char *colon;
 	char *at;
-	struct hostent *hp = NULL;
 	SOCKADDR addr;
 
 	if (conn == NULL || conn[0] == '\0')
 	{
 		smi_log(SMI_LOG_ERR, "%s: empty or missing socket information",
 			name);
-		return MI_INVALID_SOCKET;
+		return INVALID_SOCKET;
 	}
 	(void) memset(&addr, '\0', sizeof addr);
 
@@ -86,7 +88,7 @@
 			smi_log(SMI_LOG_ERR,
 				"%s: no valid socket protocols available",
 				name);
-			return MI_INVALID_SOCKET;
+			return INVALID_SOCKET;
 #  endif /* NETINET6 */
 # endif /* NETINET */
 #endif /* NETUNIX */
@@ -117,7 +119,7 @@
 		{
 			smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
 				name, p);
-			return MI_INVALID_SOCKET;
+			return INVALID_SOCKET;
 		}
 		*colon++ = ':';
 	}
@@ -141,7 +143,7 @@
 #  else /* NETINET6 */
 		smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
 			name, p);
-		return MI_INVALID_SOCKET;
+		return INVALID_SOCKET;
 #  endif /* NETINET6 */
 # endif /* NETINET */
 #endif /* NETUNIX */
@@ -162,7 +164,7 @@
 			errno = EINVAL;
 			smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long",
 				name, colon);
-			return MI_INVALID_SOCKET;
+			return INVALID_SOCKET;
 		}
 # if 0
 		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
@@ -174,7 +176,7 @@
 			smi_log(SMI_LOG_ERR,
 				"%s: UNIX socket name %s unsafe",
 				name, colon);
-			return MI_INVALID_SOCKET;
+			return INVALID_SOCKET;
 		}
 # endif /* 0 */
 
@@ -219,13 +221,13 @@
 			*at = '\0';
 
 		if (isascii(*colon) && isdigit(*colon))
-			port = htons(atoi(colon));
+			port = htons((u_short) atoi(colon));
 		else
 		{
 # ifdef NO_GETSERVBYNAME
 			smi_log(SMI_LOG_ERR, "%s: invalid port number %s",
 				name, colon);
-			return MI_INVALID_SOCKET;
+			return INVALID_SOCKET;
 # else /* NO_GETSERVBYNAME */
 			register struct servent *sp;
 
@@ -235,7 +237,7 @@
 				smi_log(SMI_LOG_ERR,
 					"%s: unknown port name %s",
 					name, colon);
-				return MI_INVALID_SOCKET;
+				return INVALID_SOCKET;
 			}
 			port = sp->s_port;
 # endif /* NO_GETSERVBYNAME */
@@ -286,7 +288,7 @@
 						smi_log(SMI_LOG_ERR,
 							"%s: Invalid numeric domain spec \"%s\"",
 							name, at);
-						return MI_INVALID_SOCKET;
+						return INVALID_SOCKET;
 					}
 				}
 				else
@@ -294,18 +296,20 @@
 					smi_log(SMI_LOG_ERR,
 						"%s: Invalid numeric domain spec \"%s\"",
 						name, at);
-					return MI_INVALID_SOCKET;
+					return INVALID_SOCKET;
 				}
 			}
 			else
 			{
+				struct hostent *hp = NULL;
+
 				hp = mi_gethostbyname(at, addr.sa.sa_family);
 				if (hp == NULL)
 				{
 					smi_log(SMI_LOG_ERR,
 						"%s: Unknown host name %s",
 						name, at);
-					return MI_INVALID_SOCKET;
+					return INVALID_SOCKET;
 				}
 				addr.sa.sa_family = hp->h_addrtype;
 				switch (hp->h_addrtype)
@@ -332,8 +336,11 @@
 					smi_log(SMI_LOG_ERR,
 						"%s: Unknown protocol for %s (%d)",
 						name, at, hp->h_addrtype);
-					return MI_INVALID_SOCKET;
+					return INVALID_SOCKET;
 				}
+# if _FFR_FREEHOSTENT && NETINET6
+				freehostent(hp);
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
 			}
 		}
 		else
@@ -356,12 +363,12 @@
 #endif /* NETINET || NETINET6 */
 
 	sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
-	if (sock < 0)
+	if (!ValidSocket(sock))
 	{
 		smi_log(SMI_LOG_ERR,
 			"%s: Unable to create new socket: %s",
 			name, strerror(errno));
-		return MI_INVALID_SOCKET;
+		return INVALID_SOCKET;
 	}
 
 	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt,
@@ -370,7 +377,7 @@
 		smi_log(SMI_LOG_ERR,
 			"%s: Unable to setsockopt: %s", name, strerror(errno));
 		(void) close(sock);
-		return MI_INVALID_SOCKET;
+		return INVALID_SOCKET;
 	}
 
 	if (bind(sock, &addr.sa, *socksize) < 0)
@@ -379,7 +386,7 @@
 			"%s: Unable to bind to port %s: %s",
 			name, conn, strerror(errno));
 		(void) close(sock);
-		return MI_INVALID_SOCKET;
+		return INVALID_SOCKET;
 	}
 
 	if (listen(sock, backlog) < 0)
@@ -387,9 +394,9 @@
 		smi_log(SMI_LOG_ERR,
 			"%s: listen call failed: %s", name, strerror(errno));
 		(void) close(sock);
-		return MI_INVALID_SOCKET;
+		return INVALID_SOCKET;
 	}
-
+	*family = addr.sa.sa_family;
 	return sock;
 }
 /*
@@ -409,8 +416,34 @@
 	return (void *) mi_handle_session(arg);
 }
 
+static socket_t listenfd = INVALID_SOCKET;
+
+static smutex_t L_Mutex;
+
+/*
+**  MI_CLOSENER -- close listen socket
+**
+**	Parameters:
+**		none.
+**
+**	Returns:
+**		none.
+*/
+
+void
+mi_closener()
+{
+	(void) smutex_lock(&L_Mutex);
+	if (ValidSocket(listenfd))
+	{
+		(void) close(listenfd);
+		listenfd = INVALID_SOCKET;
+	}
+	(void) smutex_unlock(&L_Mutex);
+}
+
 /*
-**  MI_MILTER_LISTENER -- Generic listener harness
+**  MI_LISTENER -- Generic listener harness
 **
 **	Open up listen port
 **	Wait for connections
@@ -427,24 +460,67 @@
 **		MI_FAILURE -- Network initialization failed.
 */
 
+# if BROKEN_PTHREAD_SLEEP
+
+/*
+**  Solaris 2.6, perhaps others, gets an internal threads library panic
+**  when sleep() is used:
+**
+**  thread_create() failed, returned 11 (EINVAL)
+**  co_enable, thr_create() returned error = 24
+**  libthread panic: co_enable failed (PID: 17793 LWP 1)
+**  stacktrace:
+**	ef526b10
+**	ef52646c
+**	ef534cbc
+**	156a4
+**	14644
+**	1413c
+**	135e0
+**	0
+*/
+
+#  define MI_SLEEP(s)							\
+{									\
+	int rs = 0;							\
+	struct timeval st;						\
+									\
+	st.tv_sec = (s);						\
+	st.tv_usec = 0;							\
+	if (st.tv_sec > 0)						\
+		rs = select(0, NULL, NULL, NULL, &st);			\
+	if (rs != 0)							\
+	{								\
+		smi_log(SMI_LOG_ERR,					\
+			"MI_SLEEP(): select() returned non-zero result %d, errno = %d",								\
+			rs, errno);					\
+	}								\
+}
+# else /* BROKEN_PTHREAD_SLEEP */
+#  define MI_SLEEP(s)	sleep((s))
+# endif /* BROKEN_PTHREAD_SLEEP */
+
 int
-mi_listener(conn, dbg, smfi, timeout)
+mi_listener(conn, dbg, smfi, timeout, backlog)
 	char *conn;
 	int dbg;
 	smfiDesc_ptr smfi;
 	time_t timeout;
+	int backlog;
 {
-	int connfd = -1;
-	int listenfd = -1;
-	int clilen;
+	socket_t connfd = INVALID_SOCKET;
+	int family = AF_UNSPEC;
 	int sockopt = 1;
 	int r;
 	int ret = MI_SUCCESS;
-	int cnt_m = 0;
-	int cnt_t = 0;
-	pthread_t thread_id;
+	int mcnt = 0;
+	int tcnt = 0;
+	int acnt = 0;
+	int save_errno = 0;
+	sthread_t thread_id;
 	_SOCK_ADDR cliaddr;
 	SOCKADDR_LEN_T socksize;
+	SOCKADDR_LEN_T clilen;
 	SMFICTX_PTR ctx;
 	fd_set readset, excset;
 	struct timeval chktime;
@@ -453,37 +529,56 @@
 		smi_log(SMI_LOG_DEBUG,
 			"%s: Opening listen socket on conn %s",
 			smfi->xxfi_name, conn);
-	if ((listenfd = mi_milteropen(conn, SOMAXCONN, &socksize,
-				      smfi->xxfi_name)) < 0)
+	(void) smutex_init(&L_Mutex);
+	(void) smutex_lock(&L_Mutex);
+	listenfd = mi_milteropen(conn, backlog, &socksize, &family,
+				 smfi->xxfi_name);
+	if (!ValidSocket(listenfd))
 	{
 		smi_log(SMI_LOG_FATAL,
 			"%s: Unable to create listening socket on conn %s",
 			smfi->xxfi_name, conn);
+		(void) smutex_unlock(&L_Mutex);
 		return MI_FAILURE;
 	}
 	clilen = socksize;
+
 	if (listenfd >= FD_SETSIZE)
 	{
 		smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
 			smfi->xxfi_name, listenfd, FD_SETSIZE);
+		(void) smutex_unlock(&L_Mutex);
 		return MI_FAILURE;
 	}
+	(void) smutex_unlock(&L_Mutex);
 
 	while (mi_stop() == MILTER_CONT)
 	{
+		(void) smutex_lock(&L_Mutex);
+		if (!ValidSocket(listenfd))
+		{
+			(void) smutex_unlock(&L_Mutex);
+			break;
+		}
+
 		/* select on interface ports */
 		FD_ZERO(&readset);
-		FD_SET(listenfd, &readset);
 		FD_ZERO(&excset);
-		FD_SET(listenfd, &excset);
+		FD_SET((u_int) listenfd, &readset);
+		FD_SET((u_int) listenfd, &excset);
 		chktime.tv_sec = MI_CHK_TIME;
 		chktime.tv_usec = 0;
 		r = select(listenfd + 1, &readset, NULL, &excset, &chktime);
 		if (r == 0)		/* timeout */
+		{
+			(void) smutex_unlock(&L_Mutex);
 			continue;	/* just check mi_stop() */
+		}
 		if (r < 0)
 		{
-			if (errno == EINTR)
+			save_errno = errno;
+			(void) smutex_unlock(&L_Mutex);
+			if (save_errno == EINTR)
 				continue;
 			ret = MI_FAILURE;
 			break;
@@ -492,17 +587,48 @@
 		{
 			/* some error: just stop for now... */
 			ret = MI_FAILURE;
+			(void) smutex_unlock(&L_Mutex);
 			break;
 		}
 
+		memset(&cliaddr, '\0', sizeof cliaddr);
 		connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
 				&clilen);
+		save_errno = errno;
+		(void) smutex_unlock(&L_Mutex);
 
-		if (connfd < 0)
+		/*
+		**  If remote side closes before
+		**  accept() finishes, sockaddr
+		**  might not be fully filled in.
+		*/
+
+		if (ValidSocket(connfd) &&
+		    (clilen == 0 ||
+# ifdef BSD4_4_SOCKADDR
+		     cliaddr.sa.sa_len == 0 ||
+# endif /* BSD4_4_SOCKADDR */
+		     cliaddr.sa.sa_family != family))
 		{
+			(void) close(connfd);
+			connfd = INVALID_SOCKET;
+			save_errno = EINVAL;
+		}
+
+		if (!ValidSocket(connfd))
+		{
 			smi_log(SMI_LOG_ERR,
-				"%s: accept() returned invalid socket",
-				smfi->xxfi_name);
+				"%s: accept() returned invalid socket (%s)",
+				smfi->xxfi_name, strerror(save_errno));
+			if (save_errno == EINTR)
+				continue;
+			acnt++;
+			MI_SLEEP(acnt);
+			if (acnt >= MAX_FAILS_A)
+			{
+				ret = MI_FAILURE;
+				break;
+			}
 			continue;
 		}
 
@@ -518,17 +644,19 @@
 			(void) close(connfd);
 			smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed",
 				smfi->xxfi_name);
-			sleep(++cnt_m);
-			if (cnt_m >= MAX_FAILS_M)
+			mcnt++;
+			MI_SLEEP(mcnt);
+			if (mcnt >= MAX_FAILS_M)
 			{
 				ret = MI_FAILURE;
 				break;
 			}
 			continue;
 		}
-		cnt_m = 0;
+		mcnt = 0;
+		acnt = 0;
 		memset(ctx, '\0', sizeof *ctx);
-		ctx->ctx_fd = connfd;
+		ctx->ctx_sd = connfd;
 		ctx->ctx_dbg = dbg;
 		ctx->ctx_timeout = timeout;
 		ctx->ctx_smfi = smfi;
@@ -539,41 +667,45 @@
 		if (smfi->xxfi_close == NULL)
 #endif /* 0 */
 		if (smfi->xxfi_connect == NULL)
-			smfi->xxfi_flags |= SMFIF_NOCONNECT;
+			ctx->ctx_pflags |= SMFIP_NOCONNECT;
 		if (smfi->xxfi_helo == NULL)
-			smfi->xxfi_flags |= SMFIF_NOHELO;
+			ctx->ctx_pflags |= SMFIP_NOHELO;
 		if (smfi->xxfi_envfrom == NULL)
-			smfi->xxfi_flags |= SMFIF_NOMAIL;
+			ctx->ctx_pflags |= SMFIP_NOMAIL;
 		if (smfi->xxfi_envrcpt == NULL)
-			smfi->xxfi_flags |= SMFIF_NORCPT;
+			ctx->ctx_pflags |= SMFIP_NORCPT;
 		if (smfi->xxfi_header == NULL)
-			smfi->xxfi_flags |= SMFIF_NOHDRS;
+			ctx->ctx_pflags |= SMFIP_NOHDRS;
+		if (smfi->xxfi_eoh == NULL)
+			ctx->ctx_pflags |= SMFIP_NOEOH;
 		if (smfi->xxfi_body == NULL)
-			smfi->xxfi_flags |= SMFIF_NOBODY;
+			ctx->ctx_pflags |= SMFIP_NOBODY;
 
-		if ((r = pthread_create(&thread_id, NULL,
+		if ((r = thread_create(&thread_id,
 					mi_thread_handle_wrapper,
 					(void *) ctx)) != 0)
 		{
 			smi_log(SMI_LOG_ERR,
-				"%s: pthread_create() failed: %d",
+				"%s: thread_create() failed: %d",
 				smfi->xxfi_name,  r);
-			sleep(++cnt_t);
+			tcnt++;
+			MI_SLEEP(tcnt);
 			(void) close(connfd);
 			free(ctx);
-			if (cnt_t >= MAX_FAILS_T)
+			if (tcnt >= MAX_FAILS_T)
 			{
 				ret = MI_FAILURE;
 				break;
 			}
 			continue;
 		}
-		cnt_t = 0;
+		tcnt = 0;
 	}
 	if (ret != MI_SUCCESS)
 		mi_stop_milters(MILTER_ABRT);
-	if (listenfd >= 0)
-		(void) close(listenfd);
+	else
+		mi_closener();
+	(void) smutex_destroy(&L_Mutex);
 	return ret;
 }
 #endif /* _FFR_MILTER */
Index: gnu/usr.sbin/sendmail/libmilter/main.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libmilter/main.c,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- gnu/usr.sbin/sendmail/libmilter/main.c	2000/04/02 19:05:58	1.1.1.1
+++ gnu/usr.sbin/sendmail/libmilter/main.c	2001/05/29 01:31:12	1.3
@@ -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
@@ -9,7 +9,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: main.c,v 8.34 2000/02/11 02:43:45 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: main.c,v 8.34.4.11 2001/05/07 22:06:37 gshapiro Exp $";
 #endif /* ! lint */
 
 #if _FFR_MILTER
@@ -18,6 +18,7 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 
+
 static smfiDesc_ptr smfi = NULL;
 
 /*
@@ -42,7 +43,7 @@
 		if (smfi == NULL)
 			return MI_FAILURE;
 	}
-	(void)memcpy(smfi, &smfilter, sizeof *smfi);
+	(void) memcpy(smfi, &smfilter, sizeof *smfi);
 	if (smfilter.xxfi_name == NULL)
 		smfilter.xxfi_name = "Unknown";
 
@@ -51,12 +52,42 @@
 	if (smfi->xxfi_name == NULL)
 		return MI_FAILURE;
 	(void) strlcpy(smfi->xxfi_name, smfilter.xxfi_name, len);
+
+	/* compare milter version with hard coded version */
+	if (smfi->xxfi_version != SMFI_VERSION)
+	{
+		/* hard failure for now! */
+		smi_log(SMI_LOG_ERR,
+			"%s: smfi_register: version mismatch application: %d != milter: %d",
+			smfi->xxfi_name, smfi->xxfi_version,
+			(int) SMFI_VERSION);
+		return MI_FAILURE;
+	}
+
+	return MI_SUCCESS;
+}
+
+/*
+**  SMFI_STOP -- stop milter
+**
+**	Parameters:
+**		none.
+**
+**	Returns:
+**		success.
+*/
+
+int
+smfi_stop()
+{
+	mi_stop_milters(MILTER_STOP);
 	return MI_SUCCESS;
 }
 
 static int dbg = 0;
 static char *conn = NULL;
 static int timeout = MI_TIMEOUT;
+static int backlog= MI_SOMAXCONN;
 
 int
 smfi_setdbg(odbg)
@@ -91,14 +122,26 @@
 }
 
 int
+smfi_setbacklog(obacklog)
+	int obacklog;
+{
+	if (obacklog <= 0)
+		return MI_FAILURE;
+	backlog = obacklog;
+	return MI_SUCCESS;
+}
+
+
+int
 smfi_main()
 {
+
 	signal(SIGPIPE, SIG_IGN);
 	if (conn == NULL)
 	{
 		smi_log(SMI_LOG_FATAL, "%s: missing connection information",
 			smfi->xxfi_name);
-		exit(EX_DATAERR);
+		return MI_FAILURE;
 	}
 
 	(void) atexit(mi_clean_signals);
@@ -107,13 +150,14 @@
 		smi_log(SMI_LOG_FATAL,
 			"%s: Couldn't start signal thread",
 			smfi->xxfi_name);
-		exit(EX_OSERR);
+		return MI_FAILURE;
 	}
 
+
 	/* Startup the listener */
-	if (mi_listener(conn, dbg, smfi, timeout) != MI_SUCCESS)
-		return(MI_FAILURE);
+	if (mi_listener(conn, dbg, smfi, timeout, backlog) != MI_SUCCESS)
+		return MI_FAILURE;
 
-	return(MI_SUCCESS);
+	return MI_SUCCESS;
 }
 #endif /* _FFR_MILTER */
Index: gnu/usr.sbin/sendmail/libmilter/signal.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libmilter/signal.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/libmilter/signal.c	2000/04/02 19:05:58	1.1.1.1
+++ gnu/usr.sbin/sendmail/libmilter/signal.c	2001/01/15 21:09:02	1.2
@@ -9,29 +9,21 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: signal.c,v 8.10 2000/02/26 01:32:14 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: signal.c,v 8.10.4.8 2000/11/20 21:15:37 ca Exp $";
 #endif /* ! lint */
 
 #if _FFR_MILTER
 #include "libmilter.h"
 
 /*
-** thread to handle signals
+**  thread to handle signals
 */
 
-typedef pthread_mutex_t smutex_t;
-#define smutex_init(mp)		(pthread_mutex_init(mp, NULL) == 0)
-#define smutex_destroy(mp)	(pthread_mutex_destroy(mp) == 0)
-#define smutex_lock(mp)		(pthread_mutex_lock(mp) == 0)
-#define smutex_unlock(mp)	(pthread_mutex_unlock(mp) == 0)
-#define smutex_trylock(mp)	(pthread_mutex_trylock(mp) == 0)
-
 static smutex_t M_Mutex;
 
 static int MilterStop = MILTER_CONT;
-
 
-/*
+/*
 **  MI_STOP -- return value of MilterStop
 **
 **	Parameters:
@@ -44,11 +36,9 @@
 int
 mi_stop()
 {
-	return(MilterStop);
+	return MilterStop;
 }
-
-
-/*
+/*
 **  MI_STOP_MILTERS -- set value of MilterStop
 **
 **	Parameters:
@@ -65,10 +55,12 @@
 	(void) smutex_lock(&M_Mutex);
 	if (MilterStop < v)
 		MilterStop = v;
+
+	/* close listen socket */
+	mi_closener();
 	(void) smutex_unlock(&M_Mutex);
 }
-
-/*
+/*
 **  MI_CLEAN_SIGNALS -- clean up signal handler thread
 **
 **	Parameters:
@@ -83,9 +75,8 @@
 {
 	(void) smutex_destroy(&M_Mutex);
 }
-
-/*
-**  MI -- thread to deal with signals
+/*
+**  MI_SIGNAL_THREAD -- thread to deal with signals
 **
 **	Parameters:
 **		name -- name of milter
@@ -147,8 +138,7 @@
 		}
 	}
 }
-
-/*
+/*
 **  MI_SPAWN_SIGNAL_THREAD -- spawn thread to handle signals
 **
 **	Parameters:
@@ -162,8 +152,8 @@
 mi_spawn_signal_thread(name)
 	char *name;
 {
+	sthread_t tid;
 	sigset_t set;
-	pthread_t tid;
 
 	/* Mask HUP and KILL signals */
 	sigemptyset(&set);
@@ -177,8 +167,8 @@
 			"%s: Couldn't mask HUP and KILL signals", name);
 		return MI_FAILURE;
 	}
-	if (pthread_create(&tid, NULL, mi_signal_thread, (void *)name)
-	    != MI_SUCCESS)
+	if (thread_create(&tid, mi_signal_thread,
+			  (void *)name) != MI_SUCCESS)
 	{
 		smi_log(SMI_LOG_ERR,
 			"%s: Couldn't start signal thread", name);
@@ -186,8 +176,7 @@
 	}
 	return MI_SUCCESS;
 }
-
-/*
+/*
 **  MI_CONTROL_STARTUP -- startup for thread to handle signals
 **
 **	Parameters:
Index: gnu/usr.sbin/sendmail/libmilter/sm_gethost.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libmilter/sm_gethost.c,v
retrieving revision 1.1.1.1
retrieving revision 1.4
diff -u -r1.1.1.1 -r1.4
--- gnu/usr.sbin/sendmail/libmilter/sm_gethost.c	2000/04/02 19:05:58	1.1.1.1
+++ gnu/usr.sbin/sendmail/libmilter/sm_gethost.c	2001/05/29 01:31:13	1.4
@@ -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
@@ -9,7 +9,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: sm_gethost.c,v 8.7 2000/01/20 21:51:52 geir Exp $";
+static char id[] = "@(#)$Sendmail: sm_gethost.c,v 8.7.8.10 2001/05/09 20:57:12 gshapiro Exp $";
 #endif /* ! lint */
 
 #if _FFR_MILTER
@@ -29,7 +29,7 @@
 **	Support IPv6 as well as IPv4.
 */
 
-#if NETINET6 && NEEDSGETIPNODE && __RES < 19990909
+#if NETINET6 && NEEDSGETIPNODE
 
 # ifndef AI_V4MAPPED
 #  define AI_V4MAPPED	0	/* dummy */
@@ -39,7 +39,7 @@
 # endif /* ! AI_ALL */
 
 static struct hostent *
-mi_getipnodebyname(name, family, flags, err)
+getipnodebyname(name, family, flags, err)
 	char *name;
 	int family;
 	int flags;
@@ -54,15 +54,29 @@
 		resv6 = bitset(RES_USE_INET6, _res.options);
 		_res.options |= RES_USE_INET6;
 	}
-	h_errno = 0;
+	SM_SET_H_ERRNO(0);
 	h = gethostbyname(name);
 	*err = h_errno;
 	if (family == AF_INET6 && !resv6)
 		_res.options &= ~RES_USE_INET6;
 	return h;
 }
-#endif /* NEEDSGETIPNODE && NETINET6 && __RES < 19990909 */
 
+# if _FFR_FREEHOSTENT
+void
+freehostent(h)
+	struct hostent *h;
+{
+	/*
+	**  Stub routine -- if they don't have getipnodeby*(),
+	**  they probably don't have the free routine either.
+	*/
+
+	return;
+}
+# endif /* _FFR_FREEHOSTENT */
+#endif /* NEEDSGETIPNODE && NETINET6 */
+
 struct hostent *
 mi_gethostbyname(name, family)
 	char *name;
@@ -87,8 +101,8 @@
 # endif /* NETINET6 */
 
 # if NETINET6
-	h = mi_getipnodebyname(name, family, AI_V4MAPPED|AI_ALL, &err);
-	h_errno = err;
+	h = getipnodebyname(name, family, AI_V4MAPPED|AI_ALL, &err);
+	SM_SET_H_ERRNO(err);
 # else /* NETINET6 */
 	h = gethostbyname(name);
 # endif /* NETINET6 */
Index: gnu/usr.sbin/sendmail/libmilter/smfi.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libmilter/smfi.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/libmilter/smfi.c	2000/04/02 19:05:58	1.1.1.1
+++ gnu/usr.sbin/sendmail/libmilter/smfi.c	2001/01/15 21:09:03	1.2
@@ -9,7 +9,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: smfi.c,v 8.28 2000/02/26 01:32:15 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: smfi.c,v 8.28.4.6 2000/06/28 23:48:56 gshapiro Exp $";
 #endif /* ! lint */
 
 #if _FFR_MILTER
@@ -40,9 +40,9 @@
 	char *buf;
 	struct timeval timeout;
 
-	if (headerf == NULL || headerv == NULL)
+	if (headerf == NULL || *headerf == '\0' || headerv == NULL)
 		return MI_FAILURE;
-	if (!mi_sendok(ctx, SMFIF_MODHDRS))
+	if (!mi_sendok(ctx, SMFIF_ADDHDRS))
 		return MI_FAILURE;
 	timeout.tv_sec = ctx->ctx_timeout;
 	timeout.tv_usec = 0;
@@ -54,10 +54,62 @@
 		return MI_FAILURE;
 	(void) memcpy(buf, headerf, l1 + 1);
 	(void) memcpy(buf + l1 + 1, headerv, l2 + 1);
-	r = mi_wr_cmd(ctx->ctx_fd, &timeout, SMFIR_ADDHEADER, buf, len);
+	r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDHEADER, buf, len);
 	free(buf);
 	return r;
 }
+
+/*
+**  SMFI_CHGHEADER -- send a changed header to the MTA
+**
+**	Parameters:
+**		ctx -- Opaque context structure
+**		headerf -- Header field name
+**		hdridx -- Header index value
+**		headerv -- Header field value
+**
+**	Returns:
+**		MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_chgheader(ctx, headerf, hdridx, headerv)
+	SMFICTX *ctx;
+	char *headerf;
+	mi_int32 hdridx;
+	char *headerv;
+{
+	/* do we want to copy the stuff or have a special mi_wr_cmd call? */
+	size_t len, l1, l2;
+	int r;
+	mi_int32 v;
+	char *buf;
+	struct timeval timeout;
+
+	if (headerf == NULL || *headerf == '\0')
+		return MI_FAILURE;
+	if (hdridx < 0)
+		return MI_FAILURE;
+	if (!mi_sendok(ctx, SMFIF_CHGHDRS))
+		return MI_FAILURE;
+	timeout.tv_sec = ctx->ctx_timeout;
+	timeout.tv_usec = 0;
+	if (headerv == NULL)
+		headerv = "";
+	l1 = strlen(headerf);
+	l2 = strlen(headerv);
+	len = l1 + l2 + 2 + MILTER_LEN_BYTES;
+	buf = malloc(len);
+	if (buf == NULL)
+		return MI_FAILURE;
+	v = htonl(hdridx);
+	(void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
+	(void) memcpy(buf + MILTER_LEN_BYTES, headerf, l1 + 1);
+	(void) memcpy(buf + MILTER_LEN_BYTES + l1 + 1, headerv, l2 + 1);
+	r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_CHGHEADER, buf, len);
+	free(buf);
+	return r;
+}
 /*
 **  SMFI_ADDRCPT -- send an additional recipient to the MTA
 **
@@ -77,14 +129,14 @@
 	size_t len;
 	struct timeval timeout;
 
-	if (rcpt == NULL)
+	if (rcpt == NULL || *rcpt == '\0')
 		return MI_FAILURE;
 	if (!mi_sendok(ctx, SMFIF_ADDRCPT))
 		return MI_FAILURE;
 	timeout.tv_sec = ctx->ctx_timeout;
 	timeout.tv_usec = 0;
 	len = strlen(rcpt) + 1;
-	return mi_wr_cmd(ctx->ctx_fd, &timeout, SMFIR_ADDRCPT, rcpt, len);
+	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
 }
 /*
 **  SMFI_DELRCPT -- send a recipient to be removed to the MTA
@@ -105,14 +157,14 @@
 	size_t len;
 	struct timeval timeout;
 
-	if (rcpt == NULL)
+	if (rcpt == NULL || *rcpt == '\0')
 		return MI_FAILURE;
 	if (!mi_sendok(ctx, SMFIF_DELRCPT))
 		return MI_FAILURE;
 	timeout.tv_sec = ctx->ctx_timeout;
 	timeout.tv_usec = 0;
 	len = strlen(rcpt) + 1;
-	return mi_wr_cmd(ctx->ctx_fd, &timeout, SMFIR_DELRCPT, rcpt, len);
+	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
 }
 /*
 **  SMFI_REPLACEBODY -- send a body chunk to the MTA
@@ -137,7 +189,7 @@
 
 	if (bodyp == NULL && bodylen > 0)
 		return MI_FAILURE;
-	if (!mi_sendok(ctx, SMFIF_MODBODY))
+	if (!mi_sendok(ctx, SMFIF_CHGBODY))
 		return MI_FAILURE;
 	timeout.tv_sec = ctx->ctx_timeout;
 	timeout.tv_usec = 0;
@@ -148,7 +200,7 @@
 	{
 		len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
 						       bodylen;
-		if ((r = mi_wr_cmd(ctx->ctx_fd, &timeout, SMFIR_REPLBODY,
+		if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
 				(char *) (bodyp + off), len)) != MI_SUCCESS)
 			return r;
 		off += len;
@@ -302,10 +354,29 @@
 {
 	int i;
 	char **s;
+	char one[2];
+	char braces[4];
 
 	if (ctx == NULL || symname == NULL || *symname == '\0')
 		return NULL;
 
+	if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
+	{
+		one[0] = symname[1];
+		one[1] = '\0';
+	}
+	else
+		one[0] = '\0';
+	if (strlen(symname) == 1)
+	{
+		braces[0] = '{';
+		braces[1] = *symname;
+		braces[2] = '}';
+		braces[3] = '\0';
+	}
+	else
+		braces[0] = '\0';
+
 	/* search backwards through the macro array */
 	for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
 	{
@@ -315,6 +386,10 @@
 		while (s != NULL && *s != NULL)
 		{
 			if (strcmp(*s, symname) == 0)
+				return *++s;
+			if (one[0] != '\0' && strcmp(*s, one) == 0)
+				return *++s;
+			if (braces[0] != '\0' && strcmp(*s, braces) == 0)
 				return *++s;
 			++s;	/* skip over macro value */
 			++s;	/* points to next macro name */
Index: gnu/usr.sbin/sendmail/libsmdb/smdb.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libsmdb/smdb.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- gnu/usr.sbin/sendmail/libsmdb/smdb.c	2000/04/07 19:20:35	1.2
+++ gnu/usr.sbin/sendmail/libsmdb/smdb.c	2001/01/15 21:09:03	1.3
@@ -8,13 +8,14 @@
 */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: smdb.c,v 8.37 2000/03/17 07:32:43 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: smdb.c,v 8.37.4.2 2000/08/24 17:08:00 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <fcntl.h>
 #include <stdlib.h>
 #include <unistd.h>
 
+
 #include <sendmail/sendmail.h>
 #include <libsmdb/smdb.h>
 
@@ -61,7 +62,108 @@
 		free(database);
 }
 
+/*
+**  SMDB_LOCKFILE -- lock a file using flock or (shudder) fcntl locking
+**
+**	Parameters:
+**		fd -- the file descriptor of the file.
+**		type -- type of the lock.  Bits can be:
+**			LOCK_EX -- exclusive lock.
+**			LOCK_NB -- non-blocking.
+**
+**	Returns:
+**		TRUE if the lock was acquired.
+**		FALSE otherwise.
+*/
+
+static bool
+smdb_lockfile(fd, type)
+	int fd;
+	int type;
+{
+	int i;
+	int save_errno;
+#if !HASFLOCK
+	int action;
+	struct flock lfd;
+
+	memset(&lfd, '\0', sizeof lfd);
+	if (bitset(LOCK_UN, type))
+		lfd.l_type = F_UNLCK;
+	else if (bitset(LOCK_EX, type))
+		lfd.l_type = F_WRLCK;
+	else
+		lfd.l_type = F_RDLCK;
+
+	if (bitset(LOCK_NB, type))
+		action = F_SETLK;
+	else
+		action = F_SETLKW;
+
+	while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR)
+		continue;
+	if (i >= 0)
+	{
+		return TRUE;
+	}
+	save_errno = errno;
+
+	/*
+	**  On SunOS, if you are testing using -oQ/tmp/mqueue or
+	**  -oA/tmp/aliases or anything like that, and /tmp is mounted
+	**  as type "tmp" (that is, served from swap space), the
+	**  previous fcntl will fail with "Invalid argument" errors.
+	**  Since this is fairly common during testing, we will assume
+	**  that this indicates that the lock is successfully grabbed.
+	*/
+
+	if (save_errno == EINVAL)
+	{
+		return TRUE;
+	}
+
+	if (!bitset(LOCK_NB, type) ||
+	    (save_errno != EACCES && save_errno != EAGAIN))
+	{
+		int omode = -1;
+# ifdef F_GETFL
+		(void) fcntl(fd, F_GETFL, &omode);
+		errno = save_errno;
+# 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());
+# endif /* 0 */
+		return FALSE;
+	}
+#else /* !HASFLOCK */
 
+	while ((i = flock(fd, type)) < 0 && errno == EINTR)
+		continue;
+	if (i >= 0)
+	{
+		return TRUE;
+	}
+	save_errno = errno;
+
+	if (!bitset(LOCK_NB, type) || save_errno != EWOULDBLOCK)
+	{
+		int omode = -1;
+# ifdef F_GETFL
+		(void) fcntl(fd, F_GETFL, &omode);
+		errno = save_errno;
+# 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());
+# endif /* 0 */
+		return FALSE;
+	}
+#endif /* !HASFLOCK */
+	errno = save_errno;
+	return FALSE;
+}
+
 /*
 ** SMDB_OPEN_DATABASE -- Opens a database.
 **
@@ -265,6 +367,59 @@
 
 	return SMDBE_OK;
 }
+
+/*
+**  SMDB_LOCK_MAP -- Locks a database.
+**
+**	Parameters:
+**		database -- database description.
+**		type -- type of the lock.  Bits can be:
+**			LOCK_EX -- exclusive lock.
+**			LOCK_NB -- non-blocking.
+**
+**	Returns:
+**		SMDBE_OK -- Success, otherwise errno.
+*/
+
+int
+smdb_lock_map(database, type)
+	SMDB_DATABASE *database;
+	int type;
+{
+	int fd;
+
+	fd = database->smdb_lockfd(database);
+	if (fd < 0)
+		return SMDBE_NOT_FOUND;
+	if (!smdb_lockfile(fd, type))
+		return SMDBE_LOCK_NOT_GRANTED;
+	return SMDBE_OK;
+}
+
+/*
+**  SMDB_UNLOCK_MAP -- Unlocks a database
+**
+**	Parameters:
+**		database -- database description.
+**
+**	Returns:
+**		SMDBE_OK -- Success, otherwise errno.
+*/
+
+int
+smdb_unlock_map(database)
+	SMDB_DATABASE *database;
+{
+	int fd;
+
+	fd = database->smdb_lockfd(database);
+	if (fd < 0)
+		return SMDBE_NOT_FOUND;
+	if (!smdb_lockfile(fd, LOCK_UN))
+		return SMDBE_LOCK_NOT_HELD;
+	return SMDBE_OK;
+}
+
 
 /*
 **  SMDB_SETUP_FILE -- Gets db file ready for use.
Index: gnu/usr.sbin/sendmail/libsmdb/smdb1.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libsmdb/smdb1.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- gnu/usr.sbin/sendmail/libsmdb/smdb1.c	2000/04/07 19:20:36	1.2
+++ gnu/usr.sbin/sendmail/libsmdb/smdb1.c	2001/01/15 21:09:03	1.3
@@ -8,7 +8,7 @@
 */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: smdb1.c,v 8.43 2000/03/17 07:32:43 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: smdb1.c,v 8.43.4.3 2000/10/05 23:06:30 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <unistd.h>
@@ -175,8 +175,12 @@
 	u_int flags;
 {
 	DB *db = ((SMDB_DB1_DATABASE *) database->smdb_impl)->smdb1_db;
+	DBT dbkey;
 
-	return db->del(db, &key->db, flags);
+	memset(&dbkey, '\0', sizeof dbkey);
+	dbkey.data = key->data;
+	dbkey.size = key->size;
+	return db->del(db, &dbkey, flags);
 }
 
 int
@@ -194,6 +198,16 @@
 }
 
 int
+smdb1_lockfd(database)
+	SMDB_DATABASE *database;
+{
+	SMDB_DB1_DATABASE *db1 = (SMDB_DB1_DATABASE *) database->smdb_impl;
+
+	return db1->smdb1_lock_fd;
+}
+
+
+int
 smdb1_get(database, key, data, flags)
 	SMDB_DATABASE *database;
 	SMDB_DBENT *key;
@@ -202,14 +216,22 @@
 {
 	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);
+	dbkey.data = key->data;
+	dbkey.size = key->size;
 
-	result = db->get(db, &key->db, &data->db, flags);
+	result = db->get(db, &dbkey, &dbdata, flags);
 	if (result != 0)
 	{
 		if (result == 1)
 			return SMDBE_NOT_FOUND;
 		return errno;
 	}
+	data->data = dbdata.data;
+	data->size = dbdata.size;
 	return SMDBE_OK;
 }
 
@@ -221,9 +243,17 @@
 	u_int flags;
 {
 	DB *db = ((SMDB_DB1_DATABASE *) database->smdb_impl)->smdb1_db;
+	DBT dbkey, dbdata;
 
-	return db->put(db, &key->db, &data->db,
-			    smdb_put_flags_to_db1_flags(flags));
+	memset(&dbdata, '\0', sizeof dbdata);
+	memset(&dbkey, '\0', sizeof dbkey);
+	dbkey.data = key->data;
+	dbkey.size = key->size;
+	dbdata.data = data->data;
+	dbdata.size = data->size;
+
+	return db->put(db, &dbkey, &dbdata,
+		       smdb_put_flags_to_db1_flags(flags));
 }
 
 int
@@ -299,13 +329,21 @@
 	SMDB_DB1_CURSOR *db1_cursor = (SMDB_DB1_CURSOR *) cursor->smdbc_impl;
 	SMDB_DB1_DATABASE *db1 = db1_cursor->db;
 	DB *db = db1->smdb1_db;
+	DBT dbkey, dbdata;
+
+	memset(&dbdata, '\0', sizeof dbdata);
+	memset(&dbkey, '\0', sizeof dbkey);
 
 	db1_flags = smdb_cursor_get_flags_to_smdb1(flags);
-	result = db->seq(db, &key->db, &value->db, db1_flags);
+	result = db->seq(db, &dbkey, &dbdata, db1_flags);
 	if (result == -1)
 		return errno;
 	if (result == 1)
 		return SMDBE_LAST_ENTRY;
+	value->data = dbdata.data;
+	value->size = dbdata.size;
+	key->data = dbkey.data;
+	key->size = dbkey.size;
 	return SMDBE_OK;
 }
 
@@ -319,8 +357,16 @@
 	SMDB_DB1_CURSOR *db1_cursor = (SMDB_DB1_CURSOR *) cursor->smdbc_impl;
 	SMDB_DB1_DATABASE *db1 = db1_cursor->db;
 	DB *db = db1->smdb1_db;
+	DBT dbkey, dbdata;
+
+	memset(&dbdata, '\0', sizeof dbdata);
+	memset(&dbkey, '\0', sizeof dbkey);
+	dbkey.data = key->data;
+	dbkey.size = key->size;
+	dbdata.data = value->data;
+	dbdata.size = value->size;
 
-	return db->put(db, &key->db, &value->db, R_CURSOR);
+	return db->put(db, &dbkey, &dbdata, R_CURSOR);
 }
 
 int
@@ -482,6 +528,7 @@
 		smdb_db->smdb_close = smdb1_close;
 		smdb_db->smdb_del = smdb1_del;
 		smdb_db->smdb_fd = smdb1_fd;
+		smdb_db->smdb_lockfd = smdb1_lockfd;
 		smdb_db->smdb_get = smdb1_get;
 		smdb_db->smdb_put = smdb1_put;
 		smdb_db->smdb_set_owner = smdb1_set_owner;
Index: gnu/usr.sbin/sendmail/libsmdb/smdb2.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libsmdb/smdb2.c,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -r1.2 -r1.4
--- gnu/usr.sbin/sendmail/libsmdb/smdb2.c	2000/04/07 19:20:36	1.2
+++ gnu/usr.sbin/sendmail/libsmdb/smdb2.c	2001/02/28 02:43:52	1.4
@@ -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,14 @@
 */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: smdb2.c,v 8.53 2000/03/17 07:32:43 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: smdb2.c,v 8.53.2.1.2.7 2001/02/14 04:07:24 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <fcntl.h>
 #include <stdlib.h>
 #include <unistd.h>
 
+
 #include <sendmail/sendmail.h>
 #include <libsmdb/smdb.h>
 
@@ -137,7 +138,7 @@
 			break;
 
 		default:
-			result = errno;
+			result = error;
 	}
 	return result;
 }
@@ -249,8 +250,12 @@
 	u_int flags;
 {
 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
+	DBT dbkey;
 
-	return db2_error_to_smdb(db->del(db, NULL, &key->db, flags));
+	memset(&dbkey, '\0', sizeof dbkey);
+	dbkey.data = key->data;
+	dbkey.size = key->size;
+	return db2_error_to_smdb(db->del(db, NULL, &dbkey, flags));
 }
 
 int
@@ -264,15 +269,35 @@
 }
 
 int
+smdb2_lockfd(database)
+	SMDB_DATABASE *database;
+{
+	SMDB_DB2_DATABASE *db2 = (SMDB_DB2_DATABASE *) database->smdb_impl;
+
+	return db2->smdb2_lock_fd;
+}
+
+
+int
 smdb2_get(database, key, data, flags)
 	SMDB_DATABASE *database;
 	SMDB_DBENT *key;
 	SMDB_DBENT *data;
 	u_int flags;
 {
+	int result;
 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
+	DBT dbkey, dbdata;
 
-	return db2_error_to_smdb(db->get(db, NULL, &key->db, &data->db, flags));
+	memset(&dbdata, '\0', sizeof dbdata);
+	memset(&dbkey, '\0', sizeof dbkey);
+	dbkey.data = key->data;
+	dbkey.size = key->size;
+
+	result = db->get(db, NULL, &dbkey, &dbdata, flags);
+	data->data = dbdata.data;
+	data->size = dbdata.size;
+	return db2_error_to_smdb(result);
 }
 
 int
@@ -283,8 +308,16 @@
 	u_int flags;
 {
 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
+	DBT dbkey, dbdata;
 
-	return db2_error_to_smdb(db->put(db, NULL, &key->db, &data->db,
+	memset(&dbdata, '\0', sizeof dbdata);
+	memset(&dbkey, '\0', sizeof dbkey);
+	dbkey.data = key->data;
+	dbkey.size = key->size;
+	dbdata.data = data->data;
+	dbdata.size = data->size;
+
+	return db2_error_to_smdb(db->put(db, NULL, &dbkey, &dbdata,
 					 smdb_put_flags_to_db2_flags(flags)));
 }
 
@@ -326,9 +359,12 @@
 smdb2_cursor_close(cursor)
 	SMDB_CURSOR *cursor;
 {
+	int ret;
 	DBC *dbc = (DBC *) cursor->smdbc_impl;
 
-	return db2_error_to_smdb(dbc->c_close(dbc));
+	ret = db2_error_to_smdb(dbc->c_close(dbc));
+	free(cursor);
+	return ret;
 }
 
 int
@@ -351,11 +387,19 @@
 	int db2_flags;
 	int result;
 	DBC *dbc = (DBC *) cursor->smdbc_impl;
+	DBT dbkey, dbdata;
+
+	memset(&dbdata, '\0', sizeof dbdata);
+	memset(&dbkey, '\0', sizeof dbkey);
 
 	db2_flags = smdb_cursor_get_flags_to_db2(flags);
-	result = dbc->c_get(dbc, &key->db, &value->db, db2_flags);
+	result = dbc->c_get(dbc, &dbkey, &dbdata, db2_flags);
 	if (result == DB_NOTFOUND)
 		return SMDBE_LAST_ENTRY;
+	key->data = dbkey.data;
+	key->size = dbkey.size;
+	value->data = dbdata.data;
+	value->size = dbdata.size;
 	return db2_error_to_smdb(result);
 }
 
@@ -367,8 +411,16 @@
 	SMDB_FLAG flags;
 {
 	DBC *dbc = (DBC *) cursor->smdbc_impl;
+	DBT dbkey, dbdata;
+
+	memset(&dbdata, '\0', sizeof dbdata);
+	memset(&dbkey, '\0', sizeof dbkey);
+	dbkey.data = key->data;
+	dbkey.size = key->size;
+	dbdata.data = value->data;
+	dbdata.size = value->size;
 
-	return db2_error_to_smdb(dbc->c_put(dbc, &key->db, &value->db, 0));
+	return db2_error_to_smdb(dbc->c_put(dbc, &dbkey, &dbdata, 0));
 }
 
 int
@@ -620,6 +672,7 @@
 		smdb_db->smdb_close = smdb2_close;
 		smdb_db->smdb_del = smdb2_del;
 		smdb_db->smdb_fd = smdb2_fd;
+		smdb_db->smdb_lockfd = smdb2_lockfd;
 		smdb_db->smdb_get = smdb2_get;
 		smdb_db->smdb_put = smdb2_put;
 		smdb_db->smdb_set_owner = smdb2_set_owner;
Index: gnu/usr.sbin/sendmail/libsmdb/smndbm.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libsmdb/smndbm.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- gnu/usr.sbin/sendmail/libsmdb/smndbm.c	2000/04/07 19:20:36	1.2
+++ gnu/usr.sbin/sendmail/libsmdb/smndbm.c	2001/01/15 21:09:03	1.3
@@ -8,7 +8,7 @@
 */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: smndbm.c,v 8.40 2000/03/19 05:03:30 ca Exp $";
+static char id[] = "@(#)$Sendmail: smndbm.c,v 8.40.4.3 2000/10/05 22:27:50 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <fcntl.h>
@@ -124,9 +124,14 @@
 {
 	int result;
 	DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
+	datum dbkey;
 
+	memset(&dbkey, '\0', sizeof dbkey);
+	dbkey.dptr = key->data;
+	dbkey.dsize = key->size;
+
 	errno = 0;
-	result = dbm_delete(dbm, key->dbm);
+	result = dbm_delete(dbm, dbkey);
 	if (result != 0)
 	{
 		int save_errno = errno;
@@ -157,6 +162,15 @@
 }
 
 int
+smdbm_lockfd(database)
+	SMDB_DATABASE *database;
+{
+	SMDB_DBM_DATABASE *db = (SMDB_DBM_DATABASE *) database->smdb_impl;
+
+	return db->smndbm_lock_fd;
+}
+
+int
 smdbm_get(database, key, data, flags)
 	SMDB_DATABASE *database;
 	SMDB_DBENT *key;
@@ -164,10 +178,16 @@
 	u_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);
+	dbkey.dptr = key->data;
+	dbkey.dsize = key->size;
 
 	errno = 0;
-	data->dbm = dbm_fetch(dbm, key->dbm);
-	if (data->dbm.dptr == NULL)
+	dbdata = dbm_fetch(dbm, dbkey);
+	if (dbdata.dptr == NULL)
 	{
 		int save_errno = errno;
 
@@ -179,7 +199,8 @@
 
 		return SMDBE_NOT_FOUND;
 	}
-
+	data->data = dbdata.dptr;
+	data->size = dbdata.dsize;
 	return SMDBE_OK;
 }
 
@@ -193,9 +214,17 @@
 	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);
+	dbkey.dptr = key->data;
+	dbkey.dsize = key->size;
+	dbdata.dptr = data->data;
+	dbdata.dsize = data->size;
+
 	errno = 0;
-	result = dbm_store(dbm, key->dbm, data->dbm,
+	result = dbm_store(dbm, dbkey, dbdata,
 			   smdb_put_flags_to_ndbm_flags(flags));
 	switch (result)
 	{
@@ -312,6 +341,10 @@
 	SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
 	SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
 	DBM *dbm = db->smndbm_dbm;
+	datum dbkey, dbdata;
+
+	memset(&dbkey, '\0', sizeof dbkey);
+	memset(&dbdata, '\0', sizeof dbdata);
 
 	if (flags == SMDB_CURSOR_GET_RANGE)
 		return SMDBE_UNSUPPORTED;
@@ -338,8 +371,8 @@
 	}
 
 	errno = 0;
-	value->dbm = dbm_fetch(dbm, dbm_cursor->smndbmc_current_key);
-	if (value->dbm.dptr == NULL)
+	dbdata = dbm_fetch(dbm, dbm_cursor->smndbmc_current_key);
+	if (dbdata.dptr == NULL)
 	{
 		int save_errno = errno;
 
@@ -351,7 +384,10 @@
 
 		return SMDBE_NOT_FOUND;
 	}
-	key->dbm = dbm_cursor->smndbmc_current_key;
+	value->data = dbdata.dptr;
+	value->size = dbdata.dsize;
+	key->data = dbm_cursor->smndbmc_current_key.dptr;
+	key->size = dbm_cursor->smndbmc_current_key.dsize;
 
 	return SMDBE_OK;
 }
@@ -368,9 +404,14 @@
 	SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
 	SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
 	DBM *dbm = db->smndbm_dbm;
+	datum dbdata;
+
+	memset(&dbdata, '\0', sizeof dbdata);
+	dbdata.dptr = value->data;
+	dbdata.dsize = value->size;
 
 	errno = 0;
-	result = dbm_store(dbm, dbm_cursor->smndbmc_current_key, value->dbm,
+	result = dbm_store(dbm, dbm_cursor->smndbmc_current_key, dbdata,
 			   smdb_put_flags_to_ndbm_flags(flags));
 	switch (result)
 	{
@@ -555,6 +596,7 @@
 		smdb_db->smdb_close = smdbm_close;
 		smdb_db->smdb_del = smdbm_del;
 		smdb_db->smdb_fd = smdbm_fd;
+		smdb_db->smdb_lockfd = smdbm_lockfd;
 		smdb_db->smdb_get = smdbm_get;
 		smdb_db->smdb_put = smdbm_put;
 		smdb_db->smdb_set_owner = smndbm_set_owner;
Index: gnu/usr.sbin/sendmail/libsmutil/lockfile.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libsmutil/lockfile.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/libsmutil/lockfile.c	2000/04/02 19:05:41	1.1.1.1
+++ gnu/usr.sbin/sendmail/libsmutil/lockfile.c	2001/01/15 21:09:04	1.2
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
  *	All rights reserved.
  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
  * Copyright (c) 1988, 1993
@@ -12,7 +12,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: lockfile.c,v 8.3 1999/08/31 15:38:27 ca Exp $";
+static char id[] = "@(#)$Sendmail: lockfile.c,v 8.3.16.11 2000/11/16 02:54:28 geir Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
@@ -27,6 +27,7 @@
 **		type -- type of the lock.  Bits can be:
 **			LOCK_EX -- exclusive lock.
 **			LOCK_NB -- non-blocking.
+**			LOCK_UN -- unlock.
 **
 **	Returns:
 **		TRUE if the lock was acquired.
Index: gnu/usr.sbin/sendmail/libsmutil/safefile.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libsmutil/safefile.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/libsmutil/safefile.c	2000/04/02 19:05:41	1.1.1.1
+++ gnu/usr.sbin/sendmail/libsmutil/safefile.c	2001/01/15 21:09:04	1.2
@@ -12,12 +12,14 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: safefile.c,v 8.81 2000/02/26 01:32:17 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: safefile.c,v 8.81.4.7 2000/09/01 21:09:23 ca Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
+
+
 /*
-**  SAFEFILE -- return true if a file exists and is safe for a user.
+**  SAFEFILE -- return 0 if a file exists and is safe for a user.
 **
 **	Parameters:
 **		fn -- filename to check.
@@ -77,12 +79,12 @@
 		flags &= ~SFF_SAFEDIRPATH;
 
 	/* first check to see if the file exists at all */
-#if HASLSTAT
+# if HASLSTAT
 	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
 					: stat(fn, st)) < 0)
-#else /* HASLSTAT */
+# else /* HASLSTAT */
 	if (stat(fn, st) < 0)
-#endif /* HASLSTAT */
+# endif /* HASLSTAT */
 	{
 		file_errno = errno;
 	}
@@ -96,21 +98,21 @@
 		**  soon here!
 		*/
 
-#ifdef SUID_ROOT_FILES_OK
+# ifdef SUID_ROOT_FILES_OK
 		if (bitset(S_ISUID, st->st_mode))
-#else /* SUID_ROOT_FILES_OK */
+# else /* SUID_ROOT_FILES_OK */
 		if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 &&
 		    st->st_uid != TrustedUid)
-#endif /* SUID_ROOT_FILES_OK */
+# endif /* SUID_ROOT_FILES_OK */
 		{
 			uid = st->st_uid;
 			user = NULL;
 		}
-#ifdef SUID_ROOT_FILES_OK
+# ifdef SUID_ROOT_FILES_OK
 		if (bitset(S_ISGID, st->st_mode))
-#else /* SUID_ROOT_FILES_OK */
+# else /* SUID_ROOT_FILES_OK */
 		if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
-#endif /* SUID_ROOT_FILES_OK */
+# endif /* SUID_ROOT_FILES_OK */
 			gid = st->st_gid;
 	}
 
@@ -141,7 +143,7 @@
 		}
 		else
 		{
-#if HASLSTAT
+# if HASLSTAT
 			/* Need lstat() information if called stat() before */
 			if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0)
 			{
@@ -150,7 +152,7 @@
 					dprintf("\t%s\n", errstring(ret));
 				return ret;
 			}
-#endif /* HASLSTAT */
+# endif /* HASLSTAT */
 			/* directory is writable: disallow links */
 			flags |= SFF_NOLINK;
 		}
@@ -216,7 +218,7 @@
 				if (stbuf.st_gid == gid)
 					/* EMPTY */
 					;
-#ifndef NO_GROUP_SET
+# ifndef NO_GROUP_SET
 				else if (user != NULL && !DontInitGroups &&
 					 ((gr != NULL &&
 					   gr->gr_gid == stbuf.st_gid) ||
@@ -230,7 +232,7 @@
 					if (*gp == NULL)
 						md >>= 3;
 				}
-#endif /* ! NO_GROUP_SET */
+# endif /* ! NO_GROUP_SET */
 				else
 					md >>= 3;
 			}
@@ -248,7 +250,7 @@
 		return ret;
 	}
 
-#ifdef S_ISLNK
+# ifdef S_ISLNK
 	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
 	{
 		if (tTd(44, 4))
@@ -256,7 +258,7 @@
 				(u_long) st->st_mode);
 		return E_SM_NOSLINK;
 	}
-#endif /* S_ISLNK */
+# endif /* S_ISLNK */
 	if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
 	{
 		if (tTd(44, 4))
@@ -328,7 +330,7 @@
 		if (st->st_gid == gid)
 			/* EMPTY */
 			;
-#ifndef NO_GROUP_SET
+# ifndef NO_GROUP_SET
 		else if (user != NULL && !DontInitGroups &&
 			 ((gr != NULL && gr->gr_gid == st->st_gid) ||
 			  (gr = getgrgid(st->st_gid)) != NULL))
@@ -341,7 +343,7 @@
 			if (*gp == NULL)
 				mode >>= 3;
 		}
-#endif /* ! NO_GROUP_SET */
+# endif /* ! NO_GROUP_SET */
 		else
 			mode >>= 3;
 	}
@@ -463,18 +465,18 @@
 		if (tTd(44, 20))
 			dprintf("\t[dir %s]\n", s);
 
-#if HASLSTAT
+# if HASLSTAT
 		ret = lstat(s, &stbuf);
-#else /* HASLSTAT */
+# else /* HASLSTAT */
 		ret = stat(s, &stbuf);
-#endif /* HASLSTAT */
+# endif /* HASLSTAT */
 		if (ret < 0)
 		{
 			ret = errno;
 			break;
 		}
 
-#ifdef S_ISLNK
+# ifdef S_ISLNK
 		/* Follow symlinks */
 		if (S_ISLNK(stbuf.st_mode))
 		{
@@ -619,7 +621,7 @@
 		if (stbuf.st_gid == gid &&
 		    bitset(S_IXGRP, stbuf.st_mode))
 			continue;
-#ifndef NO_GROUP_SET
+# ifndef NO_GROUP_SET
 		if (user != NULL && !DontInitGroups &&
 		    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
 		     (gr = getgrgid(stbuf.st_gid)) != NULL))
@@ -633,7 +635,7 @@
 			    bitset(S_IXGRP, stbuf.st_mode))
 				continue;
 		}
-#endif /* ! NO_GROUP_SET */
+# endif /* ! NO_GROUP_SET */
 		if (!bitset(S_IXOTH, stbuf.st_mode))
 		{
 			ret = EACCES;
@@ -830,13 +832,13 @@
 
 	if (stb->st_mode == ST_MODE_NOFILE)
 	{
-#if HASLSTAT && BOGUS_O_EXCL
+# if HASLSTAT && BOGUS_O_EXCL
 		/* only necessary if exclusive open follows symbolic links */
 		if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
 			return TRUE;
-#else /* HASLSTAT && BOGUS_O_EXCL */
+# else /* HASLSTAT && BOGUS_O_EXCL */
 		return FALSE;
-#endif /* HASLSTAT && BOGUS_O_EXCL */
+# endif /* HASLSTAT && BOGUS_O_EXCL */
 	}
 	if (fstat(fd, &sta) < 0)
 		return TRUE;
@@ -844,9 +846,9 @@
 	if (sta.st_nlink != stb->st_nlink ||
 	    sta.st_dev != stb->st_dev ||
 	    sta.st_ino != stb->st_ino ||
-#if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
+# if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
 	    sta.st_gen != stb->st_gen ||
-#endif /* HAS_ST_GEN && 0 */
+# endif /* HAS_ST_GEN && 0 */
 	    sta.st_uid != stb->st_uid ||
 	    sta.st_gid != stb->st_gid)
 	{
@@ -868,10 +870,10 @@
 				dprintf(" ino	= %lu/%lu\n",
 					(unsigned long) stb->st_ino,
 					(unsigned long) sta.st_ino);
-#if HAS_ST_GEN
+# if HAS_ST_GEN
 			dprintf(" gen	= %ld/%ld\n",
 				(long) stb->st_gen, (long) sta.st_gen);
-#endif /* HAS_ST_GEN */
+# endif /* HAS_ST_GEN */
 			dprintf(" uid	= %ld/%ld\n",
 				(long) stb->st_uid, (long) sta.st_uid);
 			dprintf(" gid	= %ld/%ld\n",
Index: gnu/usr.sbin/sendmail/libsmutil/snprintf.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libsmutil/snprintf.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/libsmutil/snprintf.c	2000/04/02 19:05:41	1.1.1.1
+++ gnu/usr.sbin/sendmail/libsmutil/snprintf.c	2001/01/15 21:09:04	1.2
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
  *	All rights reserved.
  * Copyright (c) 1997 Eric P. Allman.  All rights reserved.
  * Copyright (c) 1988, 1993
@@ -12,7 +12,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: snprintf.c,v 8.27 1999/10/13 03:27:08 ca Exp $";
+static char id[] = "@(#)$Sendmail: snprintf.c,v 8.27.16.2 2000/09/17 17:04:24 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
@@ -44,14 +44,20 @@
 char	*DoprEnd;
 int	SnprfOverflow;
 
-#if !HASSNPRINTF
+#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__
-snprintf(char *str, size_t count, const char *fmt, ...)
+sm_snprintf(char *str, size_t count, const char *fmt, ...)
 # else /* __STDC__ */
-snprintf(str, count, fmt, va_alist)
+sm_snprintf(str, count, fmt, va_alist)
 	char *str;
 	size_t count;
 	const char *fmt;
@@ -62,15 +68,13 @@
 	VA_LOCAL_DECL
 
 	VA_START(fmt);
-	len = vsnprintf(str, count, fmt, ap);
+	len = sm_vsnprintf(str, count, fmt, ap);
 	VA_END;
 	return len;
 }
 
-
-# ifndef luna2
 int
-vsnprintf(str, count, fmt, args)
+sm_vsnprintf(str, count, fmt, args)
 	char *str;
 	size_t count;
 	const char *fmt;
@@ -87,9 +91,6 @@
 			(long) count, shortenstring(str, MAXSHORTSTR));
 	return strlen(str);
 }
-
-# endif /* ! luna2 */
-#endif /* !HASSNPRINTF */
 
 /*
  * sm_dopr(): poor man's version of doprintf
Index: gnu/usr.sbin/sendmail/libsmutil/strl.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/libsmutil/strl.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/libsmutil/strl.c	2000/04/02 19:05:41	1.1.1.1
+++ gnu/usr.sbin/sendmail/libsmutil/strl.c	2001/01/15 21:09:04	1.2
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -9,7 +9,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: strl.c,v 8.5 1999/12/29 22:13:46 ca Exp $";
+static char id[] = "@(#)$Sendmail: strl.c,v 8.5.14.2 2000/09/17 17:04:24 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
@@ -77,7 +77,7 @@
 
 	o = strlen(dst);
 	if (len < o + 1)
-	  return o + strlen(src);
+		return o + strlen(src);
 	len -= o + 1;
 	for (i = 0, j = o; i < len && (dst[j] = src[i]) != 0; i++, j++)
 		continue;
Index: gnu/usr.sbin/sendmail/mail.local/mail.local.8
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/mail.local/mail.local.8,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- gnu/usr.sbin/sendmail/mail.local/mail.local.8	2000/04/02 19:48:32	1.2
+++ gnu/usr.sbin/sendmail/mail.local/mail.local.8	2001/01/15 21:09:04	1.3
@@ -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) 1990, 1993
 .\"	The Regents of the University of California.  All rights reserved.
@@ -8,15 +8,17 @@
 .\" the sendmail distribution.
 .\"
 .\"
-.\"	$Sendmail: mail.local.8,v 8.14 1999/08/26 15:49:20 ca Exp $
+.\"	$Sendmail: mail.local.8,v 8.14.14.5 2000/12/29 18:12:16 gshapiro Exp $
 .\"
-.TH MAIL.LOCAL 8 "$Date: 2000/04/02 19:48:32 $"
+.TH MAIL.LOCAL 8 "$Date: 2001/01/15 21:09:04 $"
 .SH NAME
-.B mail.local
+mail.local
 \- store mail in a mailbox
 .SH SYNOPSIS
 .B mail.local
-.RB [ \-7 "] [" \-d "] [" \-l "] [" \-f 
+.RB [ \-7 "] [" \-b "] [" \-d "] [" \-l "] [" \-f
+.IR from "] "
+.RB [ \-r 
 .IR from "] " "user ..."
 .SH DESCRIPTION
 .B Mail.local
@@ -96,6 +98,12 @@
 getservbyname(3), 
 comsat(8), 
 sendmail(8)
+.SH WARNING
+.B mail.local
+escapes only "^From " lines that follow an empty line.
+If all lines starting with "From " should be escaped,
+use the 'E' flag for the local mailer in the
+sendmail.cf file.
 .SH HISTORY
 A superset of
 .B mail.local
Index: gnu/usr.sbin/sendmail/mail.local/mail.local.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/mail.local/mail.local.c,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -r1.2 -r1.4
--- gnu/usr.sbin/sendmail/mail.local/mail.local.c	2000/04/07 19:20:36	1.2
+++ gnu/usr.sbin/sendmail/mail.local/mail.local.c	2001/02/28 02:43:52	1.4
@@ -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, 1994
  *	The Regents of the University of California.  All rights reserved.
@@ -12,14 +12,14 @@
 
 #ifndef lint
 static char copyright[] =
-"@(#) Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.\n\
+"@(#) 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 */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: mail.local.c,v 8.143 2000/03/17 07:32:44 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: mail.local.c,v 8.143.4.57 2001/02/11 20:08:20 gshapiro Exp $";
 #endif /* ! lint */
 
 /*
@@ -31,205 +31,206 @@
 **  work on such architectures.
 */
 
-#include <sys/types.h>
-#include <sys/param.h>
-#include <sys/stat.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>
-
-#ifndef __P
-# include "sendmail/cdefs.h"
-#endif /* ! __P */
-#include "sendmail/useful.h"
 
+/* additional mode for open() */
+# define EXTRA_MODE 0
+
+# include <sys/types.h>
+# include <sys/param.h>
+# include <sys/stat.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>
+
+# 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>
+# 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__ */
+#  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
-# define USE_VSYSLOG	0
-#endif /* defined(_AIX) */
-
-#if defined(__hpux)
-# define USE_LOCKF	1
-# define USE_SETRESUID	1
-# define USE_VSYSLOG	0
-#endif /* defined(__hpux) */
-
-#if defined(_CRAY)
-# if !defined(MAXPATHLEN)
-#  define MAXPATHLEN PATHSIZE
-# endif /* !defined(MAXPATHLEN) */
-# define USE_VSYSLOG   0
-#  define _PATH_MAILDIR	"/usr/spool/mail"
-#endif /* defined(_CRAY) */
-
-#if defined(ultrix)
-# define USE_VSYSLOG	0
-#endif /* defined(ultrix) */
-
-#if defined(__osf__)
-# define USE_VSYSLOG	0
-#endif /* defined(__osf__) */
-
-#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) */
+# 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) */
 
 /*
  * If you don't have flock, you could try using lockf instead.
  */
 
-#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 USE_VSYSLOG
-# define USE_VSYSLOG	1
-#endif /* ! USE_VSYSLOG */
-
-#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
+# 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) */
+#  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 2.6 */
-#endif /* HPUX11 */
-
-#if _AIX4 >= 40300
-# define HASSNPRINTF	1		/* has snprintf starting in 4.3 */
-#endif /* _AIX4 >= 40300 */
+# 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
+# if !HASSNPRINTF && !SFIO
 extern int	snprintf __P((char *, size_t, const char *, ...));
-# ifndef _CRAY
+#  ifndef _CRAY
 extern int	vsnprintf __P((char *, size_t, const char *, ...));
-# endif /* ! _CRAY */
-#endif /* !HASSNPRINTF */
+#  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.
 */
-#ifdef USE_SETEUID
-# define setreuid(r, e)		seteuid(e)
-#endif /* USE_SETEUID */
+
+# ifdef USE_SETEUID
+#  define setreuid(r, e)		seteuid(e)
+# endif /* USE_SETEUID */
 
 /*
 **  And of course on hpux you have setresuid()
 */
-#ifdef USE_SETRESUID
-# define setreuid(r, e)		setresuid(-1, e, -1)
-#endif /* USE_SETRESUID */
-
-#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 S_ISREG
-# define S_ISREG(mode)	(((mode) & _S_IFMT) == S_IFREG)
-#endif /* ! S_ISREG */
+
+# ifdef USE_SETRESUID
+#  define setreuid(r, e)		setresuid(-1, e, -1)
+# endif /* USE_SETRESUID */
+
+# 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 S_ISREG
+#  define S_ISREG(mode)	(((mode) & _S_IFMT) == S_IFREG)
+# endif /* ! S_ISREG */
+
+# ifdef MAILLOCK
+#  include <maillock.h>
+# endif /* MAILLOCK */
+
+# define U_UID pw->pw_uid
+# define U_GID pw->pw_gid
 
 #ifndef INADDRSZ
 # define INADDRSZ	4		/* size of an IPv4 address in bytes */
@@ -239,10 +240,6 @@
 # define MAILER_DAEMON	"MAILER-DAEMON"
 #endif /* ! MAILER_DAEMON */
 
-#ifdef MAILLOCK
-# include <maillock.h>
-#endif /* MAILLOCK */
-
 #ifdef CONTENTLENGTH
 char	ContentHdr[40] = "Content-Length: ";
 off_t	HeaderLength;
@@ -250,19 +247,22 @@
 #endif /* CONTENTLENGTH */
 
 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	BounceQuota = FALSE;		/* permanent error when over quota */
 
-void	deliver __P((int, char *, bool));
+void	deliver __P((int, char *));
 int	e_to_sys __P((int));
 void	notifybiff __P((char *));
-int	store __P((char *, int));
+int	store __P((char *, int, bool *));
 void	usage __P((void));
-void	vwarn __P((const char *, _BSD_VA_LIST_));
 int	lockmbox __P((char *));
 void	unlockmbox __P((void));
 void	mailerr __P((const char *, const char *, ...));
+void	flush_error __P((void));
+
 
 int
 main(argc, argv)
@@ -275,8 +275,8 @@
 	char *from;
 	extern char *optarg;
 	extern int optind;
-	extern void dolmtp __P((bool));
 
+
 	/* make sure we have some open file descriptors */
 	for (fd = 10; fd < 30; fd++)
 		(void) close(fd);
@@ -284,14 +284,14 @@
 	/* use a reasonable umask */
 	(void) umask(0077);
 
-#ifdef LOG_MAIL
+# ifdef LOG_MAIL
 	openlog("mail.local", 0, LOG_MAIL);
-#else /* LOG_MAIL */
+# else /* LOG_MAIL */
 	openlog("mail.local", 0);
-#endif /* LOG_MAIL */
+# endif /* LOG_MAIL */
 
 	from = NULL;
-	while ((ch = getopt(argc, argv, "7bdf:r:l")) != EOF)
+	while ((ch = getopt(argc, argv, "7bdf:r:l")) != -1)
 	{
 		switch(ch)
 		{
@@ -300,7 +300,7 @@
 			break;
 
 		  case 'b':		/* bounce mail when over quota. */
-			bouncequota = TRUE;
+			BounceQuota = TRUE;
 			break;
 
 		  case 'd':		/* Backward compatible. */
@@ -310,7 +310,7 @@
 		  case 'r':		/* Backward compatible. */
 			if (from != NULL)
 			{
-				mailerr(NULL, "multiple -f options");
+				mailerr(NULL, "Multiple -f options");
 				usage();
 			}
 			from = optarg;
@@ -332,8 +332,21 @@
 	notifybiff(NULL);
 
 	if (LMTPMode)
-		dolmtp(bouncequota);
+	{
+		extern void dolmtp __P((void));
+
+		if (argc > 0)
+		{
+			mailerr("421", "Users should not be specified in command line if LMTP required");
+			exit(EX_TEMPFAIL);
+		}
+
+		dolmtp();
+		/* NOTREACHED */
+		exit(EX_OK);
+	}
 
+	/* Non-LMTP from here on out */
 	if (*argv == '\0')
 		usage();
 
@@ -342,6 +355,7 @@
 	**  uid matches, otherwise, use the name from the password file
 	**  corresponding to the uid.
 	*/
+
 	uid = getuid();
 
 	if (from == NULL && ((from = getlogin()) == NULL ||
@@ -358,8 +372,17 @@
 	**  failures.  This results in the delivery being reattempted later
 	**  at the expense of repeated failures and multiple deliveries.
 	*/
-	for (fd = store(from, 0); *argv; ++argv)
-		deliver(fd, *argv, bouncequota);
+
+	HoldErrs = TRUE;
+	fd = store(from, 0, NULL);
+	HoldErrs = FALSE;
+	if (fd < 0)
+	{
+		flush_error();
+		exit(ExitVal);
+	}
+	for (; *argv != NULL; ++argv)
+		deliver(fd, *argv);
 	exit(ExitVal);
 	/* NOTREACHED */
 	return ExitVal;
@@ -447,7 +470,7 @@
 	p = malloc(l);
 	if (p == NULL)
 	{
-		printf("421 4.3.0 memory exhausted\r\n");
+		mailerr("421 4.3.0", "Memory exhausted");
 		exit(EX_TEMPFAIL);
 	}
 
@@ -460,15 +483,14 @@
 	char *addr;
 {
 	if (getpwnam(addr) == NULL)
-		return "550 5.1.1 user unknown";
+		return "550 5.1.1 User unknown";
 	return NULL;
 }
 
 #define RCPT_GROW	30
 
 void
-dolmtp(bouncequota)
-	bool bouncequota;
+dolmtp()
 {
 	char *return_path = NULL;
 	char **rcpt_addr = NULL;
@@ -482,7 +504,10 @@
 	char myhostname[1024];
 	char buf[4096];
 
+	memset(myhostname, '\0', sizeof myhostname);
 	(void) gethostname(myhostname, sizeof myhostname - 1);
+	if (myhostname[0] == '\0')
+		strlcpy(myhostname, "localhost", sizeof myhostname);
 
 	printf("220 %s LMTP ready\r\n", myhostname);
 	for (;;)
@@ -502,23 +527,37 @@
 		  case 'D':
 			if (strcasecmp(buf, "data") == 0)
 			{
+				bool inbody = FALSE;
+
 				if (rcpt_num == 0)
 				{
-					printf("503 5.5.1 No recipients\r\n");
+					mailerr("503 5.5.1", "No recipients");
 					continue;
 				}
-				msgfd = store(return_path, rcpt_num);
-				if (msgfd == -1)
+				HoldErrs = TRUE;
+				msgfd = store(return_path, rcpt_num, &inbody);
+				HoldErrs = FALSE;
+				if (msgfd < 0 && !inbody)
+				{
+					flush_error();
 					continue;
+				}
 
 				for (i = 0; i < rcpt_num; i++)
 				{
+					if (msgfd < 0)
+					{
+						/* print error for rcpt */
+						flush_error();
+						continue;
+					}
 					p = strchr(rcpt_addr[i], '+');
 					if (p != NULL)
-						*p++ = '\0';
-					deliver(msgfd, rcpt_addr[i], bouncequota);
+						*p = '\0';
+					deliver(msgfd, rcpt_addr[i]);
 				}
-				(void) close(msgfd);
+				if (msgfd >= 0)
+					(void) close(msgfd);
 				goto rset;
 			}
 			goto syntaxerr;
@@ -532,7 +571,7 @@
 				/* check for duplicate per RFC 1651 4.2 */
 				if (gotlhlo)
 				{
-					printf("503 %s Duplicate LHLO\r\n",
+					mailerr("503", "%s Duplicate LHLO",
 					       myhostname);
 					continue;
 				}
@@ -554,17 +593,19 @@
 			{
 				if (return_path != NULL)
 				{
-					printf("503 5.5.1 Nested MAIL command\r\n");
+					mailerr("503 5.5.1",
+						"Nested MAIL command");
 					continue;
 				}
 				if (strncasecmp(buf+5, "from:", 5) != 0 ||
 				    ((return_path = parseaddr(buf + 10,
 							      FALSE)) == NULL))
 				{
-					printf("501 5.5.4 Syntax error in parameters\r\n");
+					mailerr("501 5.5.4",
+						"Syntax error in parameters");
 					continue;
 				}
-				printf("250 2.5.0 ok\r\n");
+				printf("250 2.5.0 Ok\r\n");
 				continue;
 			}
 			goto syntaxerr;
@@ -575,7 +616,7 @@
 		  case 'N':
 			if (strcasecmp(buf, "noop") == 0)
 			{
-				printf("250 2.0.0 ok\r\n");
+				printf("250 2.0.0 Ok\r\n");
 				continue;
 			}
 			goto syntaxerr;
@@ -586,7 +627,7 @@
 		  case 'Q':
 			if (strcasecmp(buf, "quit") == 0)
 			{
-				printf("221 2.0.0 bye\r\n");
+				printf("221 2.0.0 Bye\r\n");
 				exit(EX_OK);
 			}
 			goto syntaxerr;
@@ -599,19 +640,21 @@
 			{
 				if (return_path == NULL)
 				{
-					printf("503 5.5.1 Need MAIL command\r\n");
+					mailerr("503 5.5.1",
+						"Need MAIL command");
 					continue;
 				}
 				if (rcpt_num >= rcpt_alloc)
 				{
 					rcpt_alloc += RCPT_GROW;
 					rcpt_addr = (char **)
-						REALLOC((char *)rcpt_addr,
+						REALLOC((char *) rcpt_addr,
 							rcpt_alloc *
 							sizeof(char **));
 					if (rcpt_addr == NULL)
 					{
-						printf("421 4.3.0 memory exhausted\r\n");
+						mailerr("421 4.3.0",
+							"Memory exhausted");
 						exit(EX_TEMPFAIL);
 					}
 				}
@@ -619,24 +662,26 @@
 				    ((rcpt_addr[rcpt_num] = parseaddr(buf + 8,
 								      TRUE)) == NULL))
 				{
-					printf("501 5.5.4 Syntax error in parameters\r\n");
+					mailerr("501 5.5.4",
+						"Syntax error in parameters");
 					continue;
 				}
-				if ((err = process_recipient(rcpt_addr[rcpt_num])) != NULL)
+				err = process_recipient(rcpt_addr[rcpt_num]);
+				if (err != NULL)
 				{
-					printf("%s\r\n", err);
+					mailerr(NULL, "%s", err);
 					continue;
 				}
 				rcpt_num++;
-				printf("250 2.1.5 ok\r\n");
+				printf("250 2.1.5 Ok\r\n");
 				continue;
 			}
 			else if (strcasecmp(buf, "rset") == 0)
 			{
-				printf("250 2.0.0 ok\r\n");
+				printf("250 2.0.0 Ok\r\n");
 
 rset:
-				while (rcpt_num)
+				while (rcpt_num > 0)
 					free(rcpt_addr[--rcpt_num]);
 				if (return_path != NULL)
 					free(return_path);
@@ -651,7 +696,7 @@
 		  case 'V':
 			if (strncasecmp(buf, "vrfy ", 5) == 0)
 			{
-				printf("252 2.3.3 try RCPT to attempt delivery\r\n");
+				printf("252 2.3.3 Try RCPT to attempt delivery\r\n");
 				continue;
 			}
 			goto syntaxerr;
@@ -660,7 +705,7 @@
 
 		  default:
   syntaxerr:
-			printf("500 5.5.2 Syntax error\r\n");
+			mailerr("500 5.5.2", "Syntax error");
 			continue;
 			/* NOTREACHED */
 			break;
@@ -669,40 +714,39 @@
 }
 
 int
-store(from, lmtprcpts)
+store(from, lmtprcpts, inbody)
 	char *from;
 	int lmtprcpts;
+	bool *inbody;
 {
 	FILE *fp = NULL;
 	time_t tval;
 	bool eline;
-	bool fullline = TRUE;
+	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;
+
 	(void) umask(0077);
 	(void) strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf);
-	if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL)
+	if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL)
 	{
-		if (lmtprcpts)
-		{
-			printf("451 4.3.0 unable to open temporary file\r\n");
-			return -1;
-		}
-		else
-		{
-			mailerr("451 4.3.0", "unable to open temporary file");
-			exit(ExitVal);
-		}
+		mailerr("451 4.3.0", "Unable to open temporary file");
+		return -1;
 	}
 	(void) unlink(tmpbuf);
 
 	if (LMTPMode)
 	{
-		printf("354 go ahead\r\n");
+		printf("354 Go ahead\r\n");
 		(void) fflush(stdout);
 	}
+	if (inbody != NULL)
+		*inbody = TRUE;
 
 	(void) time(&tval);
 	(void) fprintf(fp, "From %s %s", from, ctime(&tval));
@@ -713,16 +757,19 @@
 #endif /* CONTENTLENGTH */
 
 	line[0] = '\0';
-	for (eline = TRUE; fgets(line, sizeof(line), stdin); )
+	eline = TRUE;
+	while (fgets(line, sizeof(line), stdin) != (char *) NULL)
 	{
 		size_t line_len = 0;
 		int peek;
+
+		prevfl = fullline;	/* preserve state of previous line */
 		while (line[line_len] != '\n' && line_len < sizeof(line) - 2)
 			line_len++;
 		line_len++;
 
 		/* Check for dot-stuffing */
-		if (fullline && lmtprcpts && line[0] == '.')
+		if (prevfl && LMTPMode && line[0] == '.')
 		{
 			if (line[1] == '\n' ||
 			    (line[1] == '\r' && line[2] == '\n'))
@@ -731,7 +778,7 @@
 			line_len--;
 		}
 
-		/* Check to see if we have the full line from the fgets() */
+		/* Check to see if we have the full line from fgets() */
 		fullline = FALSE;
 		if (line_len > 0)
 		{
@@ -739,12 +786,11 @@
 			{
 				if (line_len >= 2 &&
 				    line[line_len - 2] == '\r')
-				    {
-					(void) strlcpy(line + line_len - 2,
-						       "\n", sizeof line -
-							     line_len + 2);
+				{
+					line[line_len - 2] = '\n';
+					line[line_len - 1] = '\0';
 					line_len--;
-				    }
+				}
 				fullline = TRUE;
 			}
 			else if (line[line_len - 1] == '\r')
@@ -764,10 +810,11 @@
 			fullline = TRUE;
 
 #ifdef CONTENTLENGTH
-		if (line[0] == '\n' && HeaderLength == 0)
+		if (prevfl && line[0] == '\n' && HeaderLength == 0)
 		{
 			eline = FALSE;
-			HeaderLength = ftell(fp);
+			if (fp != NULL)
+				HeaderLength = ftell(fp);
 			if (HeaderLength <= 0)
 			{
 				/*
@@ -779,58 +826,65 @@
 			}
 		}
 #else /* CONTENTLENGTH */
-		if (line[0] == '\n')
+		if (prevfl && line[0] == '\n')
 			eline = TRUE;
 #endif /* CONTENTLENGTH */
 		else
 		{
 			if (eline && line[0] == 'F' &&
+			    fp != NULL &&
 			    !memcmp(line, "From ", 5))
-				(void)putc('>', fp);
+				(void) putc('>', fp);
 			eline = FALSE;
 #ifdef CONTENTLENGTH
 			/* discard existing "Content-Length:" headers */
-			if (HeaderLength == 0 &&
+			if (prevfl && HeaderLength == 0 &&
 			    (line[0] == 'C' || line[0] == 'c') &&
 			    strncasecmp(line, ContentHdr, 15) == 0)
-					continue;
+			{
+				/*
+				**  be paranoid: clear the line
+				**  so no "wrong matches" may occur later
+				*/
+				line[0] = '\0';
+				continue;
+			}
 #endif /* CONTENTLENGTH */
 
 		}
-		(void) fwrite(line, sizeof(char), line_len, fp);
-		if (ferror(fp))
+		if (fp != NULL)
 		{
-			if (lmtprcpts)
-			{
-				while (lmtprcpts--)
-					printf("451 4.3.0 temporary file write error\r\n");
-				(void) fclose(fp);
-				return -1;
-			}
-			else
+			(void) fwrite(line, sizeof(char), line_len, fp);
+			if (ferror(fp))
 			{
 				mailerr("451 4.3.0",
-					"temporary file write error");
+					"Temporary file write error");
 				(void) fclose(fp);
-				exit(ExitVal);
+				fp = NULL;
+				continue;
 			}
 		}
 	}
+
+	/* check if an error occurred */
+	if (fp == NULL)
+		return -1;
 
-	if (lmtprcpts)
+	if (LMTPMode)
 	{
 		/* Got a premature EOF -- toss message and exit */
 		exit(EX_OK);
 	}
 
 	/* If message not newline terminated, need an extra. */
-	if (strchr(line, '\n') == NULL)
+	if (fp != NULL && strchr(line, '\n') == NULL)
 		(void) putc('\n', fp);
 
   lmtpdot:
 
 #ifdef CONTENTLENGTH
-	BodyLength = ftell(fp);
+	if (fp != NULL)
+		BodyLength = ftell(fp);
 	if (HeaderLength == 0 && BodyLength > 0)	/* empty body */
 	{
 		HeaderLength = BodyLength;
@@ -847,7 +901,8 @@
 			snprintf(line, sizeof line, "%s\n",
 				 quad_to_string(BodyLength));
 		else
-			snprintf(line, sizeof line, "%ld\n", (long) BodyLength);
+			snprintf(line, sizeof line, "%ld\n",
+				 (long) BodyLength);
 		strlcpy(&ContentHdr[16], line, sizeof(ContentHdr) - 16);
 	}
 	else
@@ -855,38 +910,31 @@
 #endif /* CONTENTLENGTH */
 
 	/* Output a newline; note, empty messages are allowed. */
-	(void) putc('\n', fp);
+	if (fp != NULL)
+		(void) putc('\n', fp);
 
-	if (fflush(fp) == EOF || ferror(fp) != 0)
+	if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0)
 	{
-		if (lmtprcpts)
-		{
-			while (lmtprcpts--)
-				printf("451 4.3.0 temporary file write error\r\n");
+		mailerr("451 4.3.0", "Temporary file write error");
+		if (fp != NULL)
 			(void) fclose(fp);
-			return -1;
-		}
-		else
-		{
-			mailerr("451 4.3.0", "temporary file write error");
-			(void) fclose(fp);
-			exit(ExitVal);
-		}
+		return -1;
 	}
 	return fd;
 }
 
 void
-deliver(fd, name, bouncequota)
+deliver(fd, name)
 	int fd;
 	char *name;
-	bool bouncequota;
 {
-	struct stat fsb, sb;
+	struct stat fsb;
+	struct stat sb;
 	struct passwd *pw;
 	char path[MAXPATHLEN];
-	int mbfd, nr = 0, nw, off;
+	int mbfd = -1, nr = 0, nw, off;
 	char *p;
+	char *errcode;
 	off_t curoff;
 #ifdef CONTENTLENGTH
 	off_t headerbytes;
@@ -900,27 +948,17 @@
 	**  Disallow delivery to unknown names -- special mailboxes can be
 	**  handled in the sendmail aliases file.
 	*/
+
 	if ((pw = getpwnam(name)) == NULL)
 	{
-		if (ExitVal != EX_TEMPFAIL)
-			ExitVal = EX_UNAVAILABLE;
-		if (LMTPMode)
-		{
-			if (ExitVal == EX_TEMPFAIL)
-				printf("451 4.3.0 cannot lookup name: %s\r\n", name);
-			else
-				printf("550 5.1.1 unknown name: %s\r\n", name);
-		}
+		if (ExitVal == EX_TEMPFAIL)
+			errcode = "451 4.3.0";
 		else
 		{
-			char *errcode = NULL;
-
-			if (ExitVal == EX_TEMPFAIL)
-				errcode = "451 4.3.0";
-			else
-				errcode = "550 5.1.1";
-			mailerr(errcode, "unknown name: %s", name);
+			ExitVal = EX_UNAVAILABLE;
+			errcode = "550 5.1.1";
 		}
+		mailerr(errcode, "Unknown name: %s", name);
 		return;
 	}
 	endpwent();
@@ -943,8 +981,10 @@
 			*p = '.';
 	}
 
+
 	(void) snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);
 
+
 	/*
 	**  If the mailbox is linked or a symlink, fail.  There's an obvious
 	**  race here, that the file was replaced with a symbolic link after
@@ -978,16 +1018,13 @@
 		if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL)
 		{
 			ExitVal = EX_TEMPFAIL;
-			mailerr("451 4.3.0",
-				"lockmailbox %s failed; error code %d %s",
-				p, off, errno > 0 ? errstring(errno) : "");
+			errcode = "451 4.3.0";
 		}
 		else
-		{
-			mailerr("551 5.3.0",
-				"lockmailbox %s failed; error code %d %s",
-				p, off, errno > 0 ? errstring(errno) : "");
-		}
+			errcode = "551 5.3.0";
+
+		mailerr(errcode, "lockmailbox %s failed; error code %d %s",
+			p, off, errno > 0 ? errstring(errno) : "");
 		return;
 	}
 
@@ -995,7 +1032,7 @@
 	{
 		int save_errno;
 		int mode = S_IRUSR|S_IWUSR;
-		gid_t gid = pw->pw_gid;
+		gid_t gid = U_GID;
 
 #ifdef MAILGID
 		(void) umask(0007);
@@ -1003,8 +1040,8 @@
 		mode |= S_IRGRP|S_IWGRP;
 #endif /* MAILGID */
 
-		mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY, mode);
-
+		mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY|EXTRA_MODE,
+			    mode);
 		save_errno = errno;
 
 		if (lstat(path, &sb) < 0)
@@ -1014,36 +1051,61 @@
 				"%s: lstat: file changed after open", path);
 			goto err1;
 		}
-		else
-			sb.st_uid = pw->pw_uid;
-		if (mbfd == -1)
+		if (mbfd < 0)
 		{
 			if (save_errno == EEXIST)
 				goto tryagain;
+
+			/* open failed, don't try again */
+			mailerr("450 4.2.0", "%s: %s", path,
+				errstring(save_errno));
+			goto err0;
 		}
-		else if (fchown(mbfd, pw->pw_uid, gid) < 0)
+		else if (fchown(mbfd, U_UID, gid) < 0)
 		{
 			mailerr("451 4.3.0", "chown %u.%u: %s",
-				pw->pw_uid, gid, name);
+				U_UID, gid, name);
 			goto err1;
 		}
+		else
+		{
+			/*
+			**  open() was successful, now close it so can
+			**  be opened as the right owner again.
+			**  Paranoia: reset mbdf since the file descriptor
+			**  is no longer valid; better safe than sorry.
+			*/
+
+			sb.st_uid = U_UID;
+			(void) close(mbfd);
+			mbfd = -1;
+		}
 	}
 	else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode))
 	{
 		mailerr("550 5.2.0", "%s: irregular file", path);
 		goto err0;
 	}
-	else if (sb.st_uid != pw->pw_uid)
+	else if (sb.st_uid != U_UID)
 	{
 		ExitVal = EX_CANTCREAT;
 		mailerr("550 5.2.0", "%s: wrong ownership (%d)",
 			path, sb.st_uid);
 		goto err0;
 	}
-	else
-		mbfd = open(path, O_APPEND|O_WRONLY, 0);
 
-	if (mbfd == -1)
+	/* change UID for quota checks */
+	if (setreuid(0, U_UID) < 0)
+	{
+		mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",
+			U_UID, errstring(errno), getuid(), geteuid());
+		goto err1;
+	}
+#ifdef DEBUG
+	fprintf(stderr, "new euid = %d\n", 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));
 		goto err0;
@@ -1054,9 +1116,9 @@
 		 !S_ISREG(fsb.st_mode) ||
 		 sb.st_dev != fsb.st_dev ||
 		 sb.st_ino != fsb.st_ino ||
-#if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
+# if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
 		 sb.st_gen != fsb.st_gen ||
-#endif /* HAS_ST_GEN && 0 */
+# endif /* HAS_ST_GEN && 0 */
 		 sb.st_uid != fsb.st_uid)
 	{
 		ExitVal = EX_TEMPFAIL;
@@ -1074,29 +1136,23 @@
 	}
 
 	/* Get the starting offset of the new message for biff. */
-	curoff = lseek(mbfd, (off_t)0, SEEK_END);
+	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));
+		(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) snprintf(biffmsg, sizeof(biffmsg), "%s@%ld\n",
+				name, (long) curoff);
 
 	/* Copy the message into the file. */
-	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1)
+	if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1)
 	{
-		mailerr("450 4.2.0", "temporary file: %s",
+		mailerr("450 4.2.0", "Temporary file: %s",
 			errstring(errno));
 		goto err1;
 	}
-	if (setreuid(0, pw->pw_uid) < 0)
-	{
-		mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",
-		     pw->pw_uid, errstring(errno), getuid(), geteuid());
-		goto err1;
-	}
 #ifdef DEBUG
-	fprintf(stderr, "new euid = %d\n", geteuid());
+	fprintf(stderr, "before writing: euid = %d\n", geteuid());
 #endif /* DEBUG */
 #ifdef CONTENTLENGTH
 	headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ;
@@ -1128,13 +1184,12 @@
 		{
 			if ((nw = write(mbfd, buf + off, nr - off)) < 0)
 			{
+				errcode = "450 4.2.0";
 #ifdef EDQUOT
-				if (errno == EDQUOT && bouncequota)
-					mailerr("552 5.2.2", "%s: %s",
-						path, errstring(errno));
-				else
+				if (errno == EDQUOT && BounceQuota)
+					errcode = "552 5.2.2";
 #endif /* EDQUOT */
-				mailerr("450 4.2.0", "%s: %s",
+				mailerr(errcode, "%s: %s",
 					path, errstring(errno));
 				goto err3;
 			}
@@ -1142,7 +1197,7 @@
 	}
 	if (nr < 0)
 	{
-		mailerr("450 4.2.0", "temporary file: %s",
+		mailerr("450 4.2.0", "Temporary file: %s",
 			errstring(errno));
 		goto err3;
 	}
@@ -1152,20 +1207,13 @@
 	{
 		mailerr("450 4.2.0", "%s: %s", path, errstring(errno));
 err3:
-		if (setreuid(0, 0) < 0)
-		{
-#if 0
-			/* already printed an error above for this recipient */
-			(void) e_to_sys(errno);
-			mailerr("450 4.2.0", "setreuid(0, 0): %s",
-				errstring(errno));
-#endif /* 0 */
-		}
+		(void) setreuid(0, 0);
 #ifdef DEBUG
 		fprintf(stderr, "reset euid = %d\n", geteuid());
 #endif /* DEBUG */
 		(void) ftruncate(mbfd, curoff);
-err1:		(void) close(mbfd);
+err1:		if (mbfd >= 0)
+			(void) close(mbfd);
 err0:		unlockmbox();
 		return;
 	}
@@ -1173,12 +1221,12 @@
 	/* Close and check -- NFS doesn't write until the close. */
 	if (close(mbfd))
 	{
+		errcode = "450 4.2.0";
 #ifdef EDQUOT
-		if (errno == EDQUOT && bouncequota)
-			mailerr("552 5.2.2", "%s: %s", path, errstring(errno));
-		else
+		if (errno == EDQUOT && BounceQuota)
+			errcode = "552 5.2.2";
 #endif /* EDQUOT */
-		mailerr("450 4.2.0", "%s: %s", path, errstring(errno));
+		mailerr(errcode, "%s: %s", path, errstring(errno));
 		(void) truncate(path, curoff);
 	}
 	else
@@ -1195,7 +1243,7 @@
 #endif /* DEBUG */
 	unlockmbox();
 	if (LMTPMode)
-		printf("250 2.1.5 %s OK\r\n", name);
+		printf("250 2.1.5 %s Ok\r\n", name);
 }
 
 /*
@@ -1212,7 +1260,7 @@
 lockmbox(name)
 	char *name;
 {
-	int r;
+	int r = 0;
 
 	if (Locked)
 		return 0;
@@ -1350,7 +1398,7 @@
 	if (addr.sin_family == AF_UNSPEC)
 		return;
 
-	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
 		return;
 	len = strlen(msg) + 1;
 	(void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr));
@@ -1360,7 +1408,7 @@
 usage()
 {
 	ExitVal = EX_USAGE;
-	mailerr(NULL, "usage: mail.local [-l] [-f from] user ...");
+	mailerr(NULL, "usage: mail.local [-7] [-b] [-l] [-f from] user ...");
 	exit(ExitVal);
 }
 
@@ -1374,61 +1422,53 @@
 	va_dcl
 #endif /* __STDC__ */
 {
+	size_t len = 0;
 	va_list ap;
 
+	(void) e_to_sys(errno);
+
 #ifdef __STDC__
 	va_start(ap, fmt);
 #else /* __STDC__ */
 	va_start(ap);
 #endif /* __STDC__ */
+
 	if (LMTPMode)
 	{
 		if (hdr != NULL)
-			printf("%s ", hdr);
-		(void) vprintf(fmt, ap);
-		(void) printf("\r\n");
-	}
-	else
-	{
-		(void) e_to_sys(errno);
-		vwarn(fmt, ap);
+		{
+			snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr);
+			len = strlen(ErrBuf);
+		}
 	}
+	(void) vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap);
+
+	if (!HoldErrs)
+		flush_error();
+
+	/* Log the message to syslog. */
+	if (!LMTPMode)
+		syslog(LOG_ERR, "%s", ErrBuf);
 }
 
 void
-vwarn(fmt, ap)
-	const char *fmt;
-	_BSD_VA_LIST_ ap;
+flush_error()
 {
-	/*
-	**  Log the message to stderr.
-	**
-	**  Don't use LOG_PERROR as an openlog() flag to do this,
-	**  it's not portable enough.
-	*/
-
-	if (ExitVal != EX_USAGE)
-		(void) fprintf(stderr, "mail.local: ");
-	(void) vfprintf(stderr, fmt, ap);
-	(void) fprintf(stderr, "\n");
-
-#if USE_VSYSLOG
-	/* Log the message to syslog. */
-	vsyslog(LOG_ERR, fmt, ap);
-#else /* USE_VSYSLOG */
+	if (LMTPMode)
+		printf("%s\r\n", ErrBuf);
+	else
 	{
-		char fmtbuf[10240];
-
-		(void) vsnprintf(fmtbuf, sizeof fmtbuf, fmt, ap);
-		syslog(LOG_ERR, "%s", fmtbuf);
+		if (ExitVal != EX_USAGE)
+			(void) fprintf(stderr, "mail.local: ");
+		fprintf(stderr, "%s\n", ErrBuf);
 	}
-#endif /* USE_VSYSLOG */
 }
 
 /*
  * e_to_sys --
  *	Guess which errno's are temporary.  Gag me.
  */
+
 int
 e_to_sys(num)
 	int num;
@@ -1441,7 +1481,7 @@
 	{
 #ifdef EDQUOT
 	  case EDQUOT:		/* Disc quota exceeded */
-		if (bouncequota)
+		if (BounceQuota)
 		{
 			ExitVal = EX_UNAVAILABLE;
 			break;
@@ -1582,15 +1622,6 @@
 	return (_gettemp(path, &fd) ? fd : -1);
 }
 
-# if 0
-char *
-mktemp(path)
-	char *path;
-{
-	return(_gettemp(path, (int *)NULL) ? path : (char *)NULL);
-}
-# endif /* 0 */
-
 static
 _gettemp(path, doopen)
 	char *path;
@@ -1636,8 +1667,8 @@
 	{
 		if (doopen)
 		{
-			if ((*doopen =
-			    open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
+			if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR,
+					    0600)) >= 0)
 				return(1);
 			if (errno != EEXIST)
 				return(0);
Index: gnu/usr.sbin/sendmail/mailstats/mailstats.8
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/mailstats/mailstats.8,v
retrieving revision 1.4
retrieving revision 1.7
diff -u -r1.4 -r1.7
--- gnu/usr.sbin/sendmail/mailstats/mailstats.8	2000/04/06 18:59:28	1.4
+++ gnu/usr.sbin/sendmail/mailstats/mailstats.8	2001/05/29 01:31:13	1.7
@@ -1,4 +1,4 @@
-.\" Copyright (c) 1998 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
@@ -6,9 +6,9 @@
 .\" the sendmail distribution.
 .\"
 .\"
-.\"	$Sendmail: mailstats.8,v 8.16 2000/02/01 05:49:53 gshapiro Exp $
+.\"	$Sendmail: mailstats.8,v 8.17.4.6 2001/05/07 22:06:38 gshapiro Exp $
 .\"
-.Dd April 25, 1996
+.Dd May 7, 2001
 .Dt MAILSTATS 1
 .Os
 .Sh NAME
@@ -52,11 +52,12 @@
 .El
 .Pp
 After this display, a line totaling the values for all of the mailers
-is displayed,
+is displayed (preceded with a
+.Dq T ) ,
 separated from the previous information by a line containing only equals
 .Pq Dq \&=
 characters.
-Another line preceeded with a
+Another line preceded with a
 .Dq C
 lists the number of connections.
 .Pp
@@ -65,14 +66,12 @@
 .It Fl C
 Read the specified file instead of the default
 .Nm sendmail
-.Dq cf
-file.
+configuration file.
 .It Fl f
 Read the specified statistics file instead of the statistics file
 specified in the
 .Nm sendmail
-.Dq cf
-file.
+configuration file.
 .It Fl p
 Output information in program-readable mode and clear statistics.
 .It Fl o
Index: gnu/usr.sbin/sendmail/mailstats/mailstats.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/mailstats/mailstats.c,v
retrieving revision 1.1.1.1
retrieving revision 1.4
diff -u -r1.1.1.1 -r1.4
--- gnu/usr.sbin/sendmail/mailstats/mailstats.c	2000/04/02 19:05:42	1.1.1.1
+++ gnu/usr.sbin/sendmail/mailstats/mailstats.c	2001/05/29 01:31:13	1.4
@@ -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 Eric P. Allman.  All rights reserved.
  * Copyright (c) 1988, 1993
@@ -14,14 +14,14 @@
 
 #ifndef lint
 static char copyright[] =
-"@(#) Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.\n\
+"@(#) 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 */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: mailstats.c,v 8.53 1999/10/13 05:43:54 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: mailstats.c,v 8.53.16.13 2001/05/07 22:06:38 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <unistd.h>
@@ -39,8 +39,10 @@
 #include <sendmail/mailstats.h>
 #include <sendmail/pathnames.h>
 
+
 #define MNAMELEN	20	/* max length of mailer name */
 
+
 int
 main(argc, argv)
 	int argc;
@@ -66,11 +68,12 @@
 	extern char *optarg;
 	extern int optind;
 
+
 	cfile = _PATH_SENDMAILCF;
 	sfile = NULL;
 	mnames = TRUE;
 	progmode = FALSE;
-	while ((ch = getopt(argc, argv, "C:f:op")) != EOF)
+	while ((ch = getopt(argc, argv, "C:f:op")) != -1)
 	{
 		switch (ch)
 		{
@@ -93,7 +96,7 @@
 		  case '?':
 		  default:
   usage:
-			(void) fputs("usage: mailstats [-C cffile] [-f stfile] -o -p\n",
+			(void) fputs("usage: mailstats [-C cffile] [-f stfile] [-o] [-p]\n",
 				     stderr);
 			exit(EX_USAGE);
 		}
@@ -202,8 +205,8 @@
 		exit (EX_OSFILE);
 	}
 
-	if ((fd = open(sfile, O_RDONLY)) < 0 ||
-	    (i = read(fd, &stats, sizeof stats)) < 0)
+	fd = open(sfile, O_RDONLY);
+	if ((fd < 0) || (i = read(fd, &stats, sizeof stats)) < 0)
 	{
 		save_errno = errno;
 		(void) fputs("mailstats: ", stderr);
Index: gnu/usr.sbin/sendmail/makemap/makemap.8
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/makemap/makemap.8,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -r1.2 -r1.4
--- gnu/usr.sbin/sendmail/makemap/makemap.8	2000/04/04 04:50:14	1.2
+++ gnu/usr.sbin/sendmail/makemap/makemap.8	2001/01/17 04:57:56	1.4
@@ -8,9 +8,9 @@
 .\" the sendmail distribution.
 .\"
 .\"
-.\"     $Sendmail: makemap.8,v 8.21 1999/07/30 06:15:31 gshapiro Exp $
+.\"     $Sendmail: makemap.8,v 8.21.16.5 2000/12/29 18:12:20 gshapiro Exp $
 .\"
-.Dd November 16, 1992
+.Dd December 29, 2000
 .Dt MAKEMAP 8
 .Os
 .Sh NAME
@@ -86,7 +86,9 @@
 .Ss Flags
 .Bl -tag -width Fl
 .It Fl C Ar file
-Use the specified sendmail configuration
+Use the specified
+.Nm
+configuration
 .Ar file
 for looking up the TrustedUser option.
 .It Fl N
Index: gnu/usr.sbin/sendmail/makemap/makemap.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/makemap/makemap.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- gnu/usr.sbin/sendmail/makemap/makemap.c	2000/04/07 19:20:37	1.2
+++ gnu/usr.sbin/sendmail/makemap/makemap.c	2001/01/15 21:09:05	1.3
@@ -21,9 +21,10 @@
 #endif /* ! lint */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: makemap.c,v 8.135 2000/04/07 17:05:21 ca Exp $";
+static char id[] = "@(#)$Sendmail: makemap.c,v 8.135.4.13 2000/10/05 23:00:50 gshapiro Exp $";
 #endif /* ! lint */
 
+
 #include <sys/types.h>
 #ifndef ISC_UNIX
 # include <sys/file.h>
@@ -57,6 +58,7 @@
 # define ISSEP(c) (isascii(c) && isspace(c))
 #endif /* _FFR_DELIM */
 
+
 static void
 usage(progname)
 	char *progname;
@@ -132,8 +134,8 @@
 	if (pw != NULL)
 		(void) strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
 	else
-		snprintf(rnamebuf, sizeof rnamebuf,
-			"Unknown UID %d", (int) RealUid);
+		(void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
+				(int) RealUid);
 	RunAsUserName = RealUserName = rnamebuf;
 	user_info.smdbu_id = RunAsUid;
 	user_info.smdbu_group_id = RunAsGid;
@@ -142,7 +144,7 @@
 
 
 #define OPTIONS		"C:Nc:t:deflorsuv"
-	while ((opt = getopt(argc, argv, OPTIONS)) != EOF)
+	while ((opt = getopt(argc, argv, OPTIONS)) != -1)
 	{
 		switch (opt)
 		{
@@ -336,7 +338,7 @@
 
 	(void) database->smdb_sync(database, 0);
 
-	if (geteuid() == 0 && TrustedUid != 0)
+	if (!unmake && geteuid() == 0 && TrustedUid != 0)
 	{
 		errno = database->smdb_set_owner(database, TrustedUid, -1);
 		if (errno != SMDBE_OK)
@@ -354,7 +356,6 @@
 	exitstat = EX_OK;
 	if (unmake)
 	{
-		bool stop;
 		errno = database->smdb_cursor(database, &cursor, 0);
 		if (errno != SMDBE_OK)
 		{
@@ -368,20 +369,18 @@
 		memset(&db_key, '\0', sizeof db_key);
 		memset(&db_val, '\0', sizeof db_val);
 
-		for (stop = FALSE, lineno = 0; !stop; lineno++)
+		for (lineno = 0; ; lineno++)
 		{
 			errno = cursor->smdbc_get(cursor, &db_key, &db_val,
 						  SMDB_CURSOR_GET_NEXT);
 			if (errno != SMDBE_OK)
-			{
-				stop = TRUE;
-			}
-			if (!stop)
-				printf("%.*s\t%.*s\n",
-				       (int) db_key.data.size,
-				       (char *) db_key.data.data,
-				       (int) db_val.data.size,
-				       (char *)db_val.data.data);
+				break;
+
+			printf("%.*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);
@@ -428,16 +427,16 @@
 
 			memset(&db_key, '\0', sizeof db_key);
 			memset(&db_val, '\0', sizeof db_val);
-			db_key.data.data = ibuf;
+			db_key.data = ibuf;
 
 			for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++)
 			{
 				if (foldcase && isascii(*p) && isupper(*p))
 					*p = tolower(*p);
 			}
-			db_key.data.size = p - ibuf;
+			db_key.size = p - ibuf;
 			if (inclnull)
-				db_key.data.size++;
+				db_key.size++;
 
 			if (*p != '\0')
 				*p++ = '\0';
@@ -448,15 +447,15 @@
 				fprintf(stderr,
 					"%s: %s: line %d: no RHS for LHS %s\n",
 					progname, mapname, lineno,
-					(char *) db_key.data.data);
+					(char *) db_key.data);
 				exitstat = EX_DATAERR;
 				continue;
 			}
 
-			db_val.data.data = p;
-			db_val.data.size = strlen(p);
+			db_val.data = p;
+			db_val.size = strlen(p);
 			if (inclnull)
-				db_val.data.size++;
+				db_val.size++;
 
 			/*
 			**  Do the database insert.
@@ -465,8 +464,8 @@
 			if (verbose)
 			{
 				printf("key=`%s', val=`%s'\n",
-				       (char *) db_key.data.data,
-				       (char *) db_val.data.data);
+				       (char *) db_key.data,
+				       (char *) db_val.data);
 			}
 
 			errno = database->smdb_put(database, &db_key, &db_val,
@@ -491,7 +490,7 @@
 				fprintf(stderr,
 					"%s: %s: line %d: key %s: put error: %s\n",
 					progname, mapname, lineno,
-					(char *) db_key.data.data,
+					(char *) db_key.data,
 					errstring(errno));
 				exitstat = EX_IOERR;
 			}
@@ -500,7 +499,7 @@
 				fprintf(stderr,
 					"%s: %s: line %d: key %s: duplicate key\n",
 					progname, mapname,
-					lineno, (char *) db_key.data.data);
+					lineno, (char *) db_key.data);
 				exitstat = EX_DATAERR;
 			}
 		}
Index: gnu/usr.sbin/sendmail/praliases/praliases.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/praliases/praliases.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/praliases/praliases.c	2000/04/03 02:52:11	1.2
+++ gnu/usr.sbin/sendmail/praliases/praliases.c	2001/05/29 01:31:13	1.5
@@ -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 Eric P. Allman.  All rights reserved.
  * Copyright (c) 1988, 1993
@@ -13,7 +13,7 @@
 
 #ifndef lint
 static char copyright[] =
-"@(#) Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.\n\
+"@(#) 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\
@@ -21,7 +21,7 @@
 #endif /* ! lint */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: praliases.c,v 8.59 2000/03/17 07:32:47 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: praliases.c,v 8.59.4.19 2001/02/28 02:37:57 ca Exp $";
 #endif /* ! lint */
 
 #include <sys/types.h>
@@ -33,6 +33,7 @@
 #endif /* EX_OK */
 #include <sysexits.h>
 
+
 #ifndef NOT_SENDMAIL
 # define NOT_SENDMAIL
 #endif /* ! NOT_SENDMAIL */
@@ -55,6 +56,9 @@
 
 extern void	syserr __P((const char *, ...));
 
+# define DELIMITERS		" ,/"
+# define PATH_SEPARATOR		':'
+
 int
 main(argc, argv)
 	int argc;
@@ -71,6 +75,7 @@
 	extern char *optarg;
 	extern int optind;
 
+
 	clrbitmap(DontBlameSendmail);
 	RunAsUid = RealUid = getuid();
 	RunAsGid = RealGid = getgid();
@@ -82,12 +87,12 @@
 		snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
 	}
 	else
-		snprintf(rnamebuf, sizeof rnamebuf,
-			 "Unknown UID %d", (int) RealUid);
+		(void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
+				(int) RealUid);
 	RunAsUserName = RealUserName = rnamebuf;
 
 	cfile = _PATH_SENDMAILCF;
-	while ((ch = getopt(argc, argv, "C:f:")) != EOF)
+	while ((ch = getopt(argc, argv, "C:f:")) != -1)
 	{
 		switch ((char)ch) {
 		case 'C':
@@ -167,7 +172,7 @@
 					break;
 				b = p;
 
-				p = strpbrk(p, " ,/");
+				p = strpbrk(p, DELIMITERS);
 
 				/* find end of spec */
 				if (p != NULL)
@@ -241,7 +246,7 @@
 	SMDB_DBPARAMS params;
 	SMDB_USER_INFO user_info;
 
-	colon = strchr(filename, ':');
+	colon = strchr(filename, PATH_SEPARATOR);
 	if (colon == NULL)
 	{
 		db_name = filename;
@@ -259,6 +264,7 @@
 	{
 		while (isascii(*db_name) && isspace(*db_name))
 			db_name++;
+
 		if (*db_name != '-')
 			break;
 		while (*db_name != '\0' &&
@@ -266,6 +272,21 @@
 			db_name++;
 	}
 
+	/* Skip non-file based DB types */
+	if (db_type != NULL && *db_type != '\0')
+	{
+		if (db_type != SMDB_TYPE_DEFAULT &&
+		    strcmp(db_type, "hash") != 0 &&
+		    strcmp(db_type, "btree") != 0 &&
+		    strcmp(db_type, "dbm") != 0)
+		{
+			fprintf(stderr,
+				"praliases: Skipping non-file based alias type %s\n",
+				db_type);
+			return;
+		}
+	}
+
 	if (*db_name == '\0' || (db_type != NULL && *db_type == '\0'))
 	{
 		if (colon != NULL)
@@ -310,20 +331,20 @@
 		{
 #if 0
 			/* skip magic @:@ entry */
-			if (db_key.data.size == 2 &&
-			    db_key.data.data[0] == '@' &&
-			    db_key.data.data[1] == '\0' &&
-			    db_value.data.size == 2 &&
-			    db_value.data.data[0] == '@' &&
-			    db_value.data.data[1] == '\0')
+			if (db_key.size == 2 &&
+			    db_key.data[0] == '@' &&
+			    db_key.data[1] == '\0' &&
+			    db_value.size == 2 &&
+			    db_value.data[0] == '@' &&
+			    db_value.data[1] == '\0')
 				continue;
 #endif /* 0 */
 
 			printf("%.*s:%.*s\n",
-			       (int) db_key.data.size,
-			       (char *) db_key.data.data,
-			       (int) db_value.data.size,
-			       (char *) db_value.data.data);
+			       (int) db_key.size,
+			       (char *) db_key.data,
+			       (int) db_value.size,
+			       (char *) db_value.data);
 		}
 
 		if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
@@ -336,21 +357,29 @@
 	}
 	else for (; *argv != NULL; ++argv)
 	{
+		int get_res;
+
 		memset(&db_key, '\0', sizeof db_key);
 		memset(&db_value, '\0', sizeof db_value);
-		db_key.data.data = *argv;
-		db_key.data.size = strlen(*argv) + 1;
-		if (database->smdb_get(database, &db_key,
-				       &db_value, 0) == SMDBE_OK)
+		db_key.data = *argv;
+		db_key.size = strlen(*argv);
+		get_res = database->smdb_get(database, &db_key, &db_value, 0);
+		if (get_res == SMDBE_NOT_FOUND)
+		{
+			db_key.size++;
+			get_res = database->smdb_get(database, &db_key,
+						     &db_value, 0);
+		}
+		if (get_res == SMDBE_OK)
 		{
 			printf("%.*s:%.*s\n",
-			       (int) db_key.data.size,
-			       (char *) db_key.data.data,
-			       (int) db_value.data.size,
-			       (char *) db_value.data.data);
+			       (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.data);
+			printf("%s: No such key\n", (char *) db_key.data);
 	}
 
  fatal:
Index: gnu/usr.sbin/sendmail/rmail/rmail.8
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/rmail/rmail.8,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- gnu/usr.sbin/sendmail/rmail/rmail.8	2000/04/02 19:48:34	1.2
+++ gnu/usr.sbin/sendmail/rmail/rmail.8	2001/01/15 21:09:06	1.3
@@ -8,14 +8,17 @@
 .\" the sendmail distribution.
 .\"
 .\"
-.\"	$Sendmail: rmail.8,v 8.1 1999/06/22 20:41:33 tony Exp $
+.\"	$Sendmail: rmail.8,v 8.1.16.2 2000/12/29 18:12:22 gshapiro Exp $
 .\"
-.TH RMAIL 8 "$Date: 2000/04/02 19:48:34 $"
+.TH RMAIL 8 "$Date: 2001/01/15 21:09:06 $"
 .SH NAME
-.B rmail
+rmail
 \- handle remote mail received via uucp
 .SH SYNOPSIS
 .B rmail
+.RB [ \-D
+.IR domain ]
+.RB [ \-T ]
 .I 
 user ...
 .SH DESCRIPTION
@@ -34,6 +37,15 @@
 uucp 
 and 
 sendmail.
+.SS Flags
+.TP
+.B \-D
+Use the specified
+.I domain
+instead of the default domain of ``UUCP''.
+.TP
+.B \-T
+Turn on debugging.
 .SH SEE ALSO
 uucp(1), 
 mail.local(8), 
Index: gnu/usr.sbin/sendmail/rmail/rmail.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/rmail/rmail.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/rmail/rmail.c	2000/04/07 19:20:37	1.2
+++ gnu/usr.sbin/sendmail/rmail/rmail.c	2001/05/29 01:31:13	1.5
@@ -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) 1988, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -12,14 +12,14 @@
 
 #ifndef lint
 static char copyright[] =
-"@(#) Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.\n\
+"@(#) 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 */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: rmail.c,v 8.39 2000/03/17 07:32:47 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: rmail.c,v 8.39.4.12 2001/05/07 22:06:39 gshapiro Exp $";
 #endif /* ! lint */
 
 /*
@@ -89,17 +89,17 @@
 # define STDIN_FILENO	0
 #endif /* ! STDIN_FILENO */
 
-#if defined(BSD4_4) || defined(linux) || SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206)
+#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) */
+#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
+#if !HASSNPRINTF && !SFIO
 extern int	snprintf __P((char *, size_t, const char *, ...));
-#endif /* !HASSNPRINTF */
+#endif /* !HASSNPRINTF && !SFIO */
 
 #if defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__) || defined(IRIX64) || defined(IRIX5) || defined(IRIX6)
 # ifndef HASSTRERROR
@@ -151,14 +151,14 @@
 	FILE *fp;
 	char *addrp = NULL, *domain, *p, *t;
 	char *from_path, *from_sys, *from_user;
-	char *args[100], buf[2048], lbuf[2048];
+	char **args, buf[2048], lbuf[2048];
 	struct stat sb;
 	extern char *optarg;
 	extern int optind;
 
 	debug = 0;
 	domain = "UUCP";		/* Default "domain". */
-	while ((ch = getopt(argc, argv, "D:T")) != EOF)
+	while ((ch = getopt(argc, argv, "D:T")) != -1)
 	{
 		switch (ch)
 		{
@@ -206,7 +206,7 @@
 			break;
 		}
 
-		if (*addrp == '\0')
+		if (addrp == NULL || *addrp == '\0')
 			err(EX_DATAERR, "corrupted From line: %s", lbuf);
 
 		/* Use the "remote from" if it exists. */
@@ -310,6 +310,10 @@
 			offset = (off_t)ftell(stdin);
 	}
 
+
+	/* 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++] = "-oee";		/* No errors, just status. */
@@ -338,7 +342,7 @@
 	**  the address (helps to pass addrs like @gw1,@gw2:aa@bb)
 	*/
 
-	while (*argv)
+	while (*argv != NULL)
 	{
 		if (**argv == '-')
 			err(EX_USAGE, "dash precedes argument: %s", *argv);
@@ -353,13 +357,18 @@
 			snprintf(args[i++], len, "<%s>", *argv);
 		}
 		argv++;
+		argc--;
+
+		/* Paranoia check, argc used for args[] bound */
+		if (argc < 0)
+			err(EX_SOFTWARE, "Argument count mismatch");
 	}
-	args[i] = 0;
+	args[i] = NULL;
 
 	if (debug)
 	{
 		fprintf(stderr, "Sendmail arguments:\n");
-		for (i = 0; args[i]; i++)
+		for (i = 0; args[i] != NULL; i++)
 			fprintf(stderr, "\t%s\n", args[i]);
 	}
 
Index: gnu/usr.sbin/sendmail/sendmail/Makefile
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/Makefile,v
retrieving revision 1.9
retrieving revision 1.12
diff -u -r1.9 -r1.12
--- gnu/usr.sbin/sendmail/sendmail/Makefile	2000/10/09 23:45:00	1.9
+++ gnu/usr.sbin/sendmail/sendmail/Makefile	2001/05/05 22:00:17	1.12
@@ -1,11 +1,22 @@
-#	$OpenBSD: Makefile,v 1.9 2000/10/09 23:45:00 millert Exp $
+#	$OpenBSD: Makefile,v 1.12 2001/05/05 22:00:17 millert Exp $
 
 PROG=	sendmail
 
 WANT_LIBWRAP=1
 WANT_LIBSMUTIL=1
 
-# To casue sendmail to drop privs in test mode (-bt) uncomment the following
+# For TLS/SSL support
+ENVDEF+= -DSTARTTLS -D_FFR_TLS_TOREK
+LDADD+= -lssl -lcrypto
+DPADD=	${LIBSSL} ${LIBCRYPTO}
+
+# Work around broken name servers that return SERV_FAIL for AAAA records
+ENVDEF+= -D_FFR_WORKAROUND_BROKEN_NAMESERVERS
+
+# 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 \
Index: gnu/usr.sbin/sendmail/sendmail/README
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/README,v
retrieving revision 1.3
retrieving revision 1.6
diff -u -r1.3 -r1.6
--- gnu/usr.sbin/sendmail/sendmail/README	2000/04/07 19:20:38	1.3
+++ gnu/usr.sbin/sendmail/sendmail/README	2001/05/29 01:31:13	1.6
@@ -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, 1995-1997 Eric P. Allman.  All rights reserved.
 # Copyright (c) 1988
@@ -9,7 +9,7 @@
 # the sendmail distribution.
 #
 #
-#	$Sendmail: README,v 8.263 2000/04/06 20:27:44 gshapiro Exp $
+#	$Sendmail: README,v 8.263.2.1.2.35 2001/05/09 20:58:32 gshapiro Exp $
 #
 
 This directory contains the source files for sendmail(TM).
@@ -268,6 +268,7 @@
 		the stat structure (see stat(2)).
 HASSRANDOMDEV	Define this if your system has the srandomdev(3) function
 		call.
+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.
@@ -450,8 +451,18 @@
 FAST_PID_RECYCLE
 		Set this if your system can reuse the same PID in the same
 		second.
+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.
 
-
 +-----------------------+
 | COMPILE-TIME FEATURES |
 +-----------------------+
@@ -508,6 +519,8 @@
 		in conf.h.  You probably want this.
 NETINET6	Set this to get IPv6 support.  Other configuration may
 		be needed in conf.h for your particular operating system.
+		Also, DaemonPortOptions must be set appropriately for
+		sendmail to accept IPv6 connections.
 NETISO		Define this to get ISO networking support.
 NETUNIX		Define this to get Unix domain networking support.  Defined
 		by default.  A few bizarre systems (SCO, ISC, Altos) don't
@@ -565,6 +578,26 @@
 		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
+		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.
+		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.
 
 
 +---------------------+
@@ -598,7 +631,51 @@
 wildcard MX records that match your domain.  ANYTHING ELSE WILL GIVE
 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.
+
++----------------------------------------+
+| 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
+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(`confENVDEF', `-DSFIO')
+APPENDDEF(`confLIBS', `-lsfio')
+APPENDDEF(`conf_sendmail_ENVDEF', `-DSTARTTLS')
+APPENDDEF(`conf_sendmail_LIBS', `-lssl -lcrypto')
+
+Configuration information can be found in doc/op/op.me (required
+certificates) and cf/README (how to tell sendmail about certificates).
+
+To perform an initial test, connect to your sendmail daemon
+(telnet localhost 25) and issue a EHLO localhost and see whether
+250-STARTTLS
+is in the response.  If it isn't, run the daemon with
+-O LogLevel=14
+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. That is, libsmutil must be compiled with sfio, which
+is accomplished by the above config parameters. Another 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/
 
+
 +------------------------------------+
 | SASL COMPILATION AND CONFIGURATION |
 +------------------------------------+
@@ -622,8 +699,8 @@
 and try again.  Then take a look at the logfile and see whether
 there are any security related problems listed (unsafe files).
 
-Further information can be found at:
-http://www.sendmail.org/~ca/email/auth.html
+Further information can be found via:
+http://www.sendmail.org/tips/
 
 
 +-------------------------------------+
@@ -722,7 +799,7 @@
 	NOTE: The SunOS 4.X linker uses library paths specified during
 	compilation using -L for run-time shared library searches.
 	Therefore, it is vital that relative and unsafe directory paths not
-	be using when compiling sendmail.
+	be used when compiling sendmail.
 
 SunOS 4.0.2 (Sun 386i)
 	Date: Fri, 25 Aug 1995 11:13:58 +0200 (MET DST)
@@ -765,44 +842,6 @@
 	make sure /opt/SUNWspro/bin/cc is used instead of /usr/ucb/cc
 	(or it might complain about tm_zone).
 
-	To the best of my knowledge, Solaris does not have the
-	gethostbyname problem described above.  However, it does
-	have another one:
-
-	From a correspondent:
-
-	   For solaris 2.2, I have
-
-		hosts:      files dns
-
-	   in /etc/nsswitch.conf and /etc/hosts has to have the fully
-	   qualified host name. I think "files" has to be before "dns"
-	   in /etc/nsswitch.conf during bootup.
-
-	From another correspondent:
-
-	   When running sendmail under Solaris, the gethostbyname()
-	   hack in conf.c which should perform proper canonicalization
-	   of host names could fail.  Result: the host name is not
-	   canonicalized despite the hack, and you'll have to define $j
-	   and $m in sendmail.cf somewhere.
-
-	   The reason could be that /etc/nsswitch.conf is improperly
-	   configured (at least from sendmail's point of view).  For
-	   example, the line
-
-		hosts:      files nisplus dns
-
-	   will make gethostbyname() look in /etc/hosts first, then ask
-	   nisplus, then dns.  However, if /etc/hosts does not contain
-	   the full canonicalized hostname, then no amount of
-	   gethostbyname()s will work.
-
-	   Solution (or rather, a workaround): Ask nisplus first, then
-	   dns, then local files:
-
-		hosts:      nisplus dns [NOTFOUND=return] files
-
 	The Solaris "syslog" function is apparently limited to something
 	about 90 characters because of a kernel limitation.  If you have
 	source code, you can probably up this number.  You can get patches
@@ -854,12 +893,6 @@
 	>>
 	>> here, path 2 would be the first used.
 
-Solaris 2.6 (SunOS 5.6)
-	If you built sendmail 8.8.1 through 8.8.4 inclusive on a Solaris 2.5
-	system, that binary will not run on Solaris 2.6, due to problems with
-	incompatible snprintf(3s) calls.  This problem is fixed in sendmail
-	8.8.5.
-
 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
@@ -894,6 +927,25 @@
 	to ldap_set_option for LDAP_OPT_REFERRALS in ldapmap_setopts if
 	LDAP support is compiled in sendmail.
 
+Solaris
+	If you are using dns for hostname resolution on Solaris, make sure
+	that the 'dns' entry is last on the hosts line in
+	'/etc/nsswitch.conf'.  For example, use:
+
+		hosts:	nisplus files dns
+
+	Do not use:
+
+		host:  nisplus dns [NOTFOUND=return] files
+
+	Note that 'nisplus' above is an illustration.  The same comment
+	applies no matter what naming services you are using.  If you have
+	anything other than dns last, even after "[NOTFOUND=return]",
+	sendmail may not be able to determine whether an error was
+	temporary or permanent.  The error returned by the solaris
+	gethostbyname() is the error for the last lookup used, and other
+	naming services do not have the same concept of temporary failure.
+
 Ultrix
 	By default, the IDENT protocol is turned off on Ultrix.  If you
 	are running Ultrix 4.4 or later, or if you have included patch
@@ -990,6 +1042,12 @@
 	If you are using XFS filesystem, avoid using the -32 ABI switch to
 	the cc compiler if possible.
 
+	Broken inet_aton and inet_ntoa on IRIX using gcc: There's
+	a problem with gcc on IRIX, i.e., gcc can't pass structs
+	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
+
 IRIX 6.4
 	The IRIX 6.5.4 version of /bin/m4 does not work properly with
 	sendmail.  Either install fw_m4.sw.m4 off the Freeware_May99 CD and
@@ -1016,8 +1074,6 @@
 
 	in your .cf file.
 
-	You may have to use -DNeXT.
-
 BSDI (BSD/386) 1.0, NetBSD 0.9, FreeBSD 1.0
 	The "m4" from BSDI won't handle the config files properly.
 	I haven't had a chance to test this myself.
@@ -1050,9 +1106,11 @@
 	determined to continue to use your old, buggy version (or as
 	a shortcut to get sendmail working -- I'm sure you have the
 	best intentions to port a modern version of BIND), you can
-	copy ../contrib/oldbind.compat.c into sendmail and add
-	oldbind.compat.o to OBJADD in the Makefile.
+	copy ../contrib/oldbind.compat.c into sendmail and add the
+	following to devtools/Site/site.config.m4:
 
+	APPENDDEF(`confOBJADD', `oldbind.compat.o')
+
 A/UX
 	Date: Tue, 12 Oct 1993 18:28:28 -0400 (EDT)
 	From: "Eric C. Hagberg" <hagberg@med.cornell.edu>
@@ -1185,6 +1243,22 @@
 	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
+	changes that the change implied.  Therefore, compiling with
+	-DNETINET6 fails.
+
+	Workarounds:
+	1) Compile without -DNETINET6
+	2) Build against a real BIND 8.2.2 include/lib tree
+	3) Wait for glibc to fix it
+
 AIX 4.X
 	The AIX 4.X linker uses library paths specified during compilation
 	using -L for run-time shared library searches.  Therefore, it is
@@ -1206,7 +1280,21 @@
 
 	gcc -Wl,-rpath /usr/lib -Wl,-rpath /lib -Wl,-rpath /usr/local/lib
 
-AIX 4.2
+AIX 4.3.3
+	From: Valdis.Kletnieks@vt.edu
+	Date: Sun, 02 Jul 2000 03:58:02 -0400
+
+	Under AIX 4.3.3, after applying bos.adt.include 4.3.3.12 to close the
+	BIND 8.2.2 security holes, you can no longer build with  -DNETINET6
+	because they changed the value of __RES in resolv.h but failed to
+	actually provide the API changes that the change implied.
+
+	Workarounds:
+	1) Compile without -DNETINET6
+	2) Build against a real BIND 8.2.2 include/lib tree
+	3) Wait for IBM to fix it
+
+AIX 4.X
 	The AIX m4 implements a different mechanism for ifdef which is
 	inconsistent with other versions of m4.  Therefore, it will not
 	work properly with the sendmail Build architecture or m4
@@ -1269,10 +1357,6 @@
 	about the location of the 'getloadavg' routine if you use
 	the LA_SUBR define.
 
-
-	Manual pages will format correctly if given the mandoc macros
-	and used with nroff.  I have not tried groff.
-
 RISC/os
 	RISC/os from MIPS is a merged AT&T/Berkeley system.  When you
 	compile on that platform you will get duplicate definitions
@@ -1367,6 +1451,21 @@
 	problems.  You may want to turn this off if you have problems
 	running sendmail.  Reported by Jerry G. DeLapp <jgd@acl.lanl.gov>.
 
+Mac OS X (10.0.X)
+	From: Mike Zimmerman <zimmy@torrentnet.com>
+	From scratch here is what Darwin users need to do to the standard
+	10.0.0, 10.0.1 install to get sendmail working.
+	From http://www.macosx.com/forums/showthread.php?s=6dac0e9e1f3fd118a4870a8a9b559491&threadid=2242:
+	1. chmod g-w / /private /private/etc
+	2. Properly set HOSTNAME in /etc/hostconfig to your FQDN:
+	   HOSTNAME=-my.domain.com-
+	3. Edit /etc/rc.boot:
+	   hostname my.domain.com
+	   domainname domain.com
+	4. Edit /System/Library/StartupItems/Sendmail/Sendmail:
+	   Remove the "&" after the sendmail command:
+	   /usr/sbin/sendmail -bd -q1h
+
 GNU getopt
 	I'm told that GNU getopt has a problem in that it gets confused
 	by the double call.  Use the version in conf.c instead.
@@ -1426,9 +1525,37 @@
 	cause it to use "HELO hostname" (which Z-mail apparently requires
 	as well. :)
 
+OpenSSL
+	OpenSSL versions prior to 0.9.6 use a macro named Free which
+	conflicts with existing macro names on some platforms, such as
+	AIX.
+	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-wsg.cso.uiuc.edu/sendmail/patches/ .
+	described at http://www-dev.cso.uiuc.edu/sendmail/ .
 	Please contact Mark Roth for support and questions regarding the
 	map.
 
@@ -1504,12 +1631,15 @@
 
 The following list describes the files in this directory:
 
+Build		Shell script for building sendmail.
+Makefile	A convenience for calling ./Build.
 Makefile.m4	A template for constructing a makefile based on the
 		information in the devtools directory.
 README		This file.
 TRACEFLAGS	My own personal list of the trace flags -- not guaranteed
 		to be particularly up to date.
 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.
@@ -1533,33 +1663,39 @@
 deliver.c	Routines to deliver mail.
 domain.c	Routines that interface with DNS (the Domain Name
 		System).
-err.c		Routines to print error messages.
 envelope.c	Routines to manipulate the envelope structure.
+err.c		Routines to print error messages.
 headers.c	Routines to process message headers.
+helpfile	An example helpfile for the SMTP HELP command and -bt mode.
 macro.c		The macro expander.  This is used internally to
 		insert information from the configuration file.
+mailq.1		Man page for the mailq command.
 main.c		The main routine to sendmail.  This file also
 		contains some miscellaneous routines.
+makesendmail	A convenience for calling ./Build.
 map.c		Support for database maps.
 mci.c		Routines that handle mail connection information caching.
+milter.c	MTA portions of the mail filter API.
 mime.c		MIME conversion routines.
+newaliases.1	Man page for the newaliases command.
 parseaddr.c	The routines which do address parsing.
 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.
-safefile.c	Routines to do careful checking of file modes and permissions
-		when opening or creating files.
 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.h	Header file for sfsasl.c.
 shmticklib.c	Routines for shared memory counters.
-snprintf.c	Routines to manipulate strings but prevent buffer overflows.
 srvrsmtp.c	Routines to implement server SMTP.
 stab.c		Routines to manage the symbol table.
 stats.c		Routines to collect and post the statistics.
 statusd_shm.h	Data structure and function declarations for shmticklib.c.
 sysexits.c	List of error messages associated with error codes
 		in sysexits.h.
+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.
 trace.c		The trace package.  These routines allow setting and
@@ -1568,7 +1704,6 @@
 usersmtp.c	Routines to implement user SMTP.
 util.c		Some general purpose routines used by sendmail.
 version.c	The version number and information about this
-		version of sendmail.  Theoretically, this gets
-		modified on every change.
+		version of sendmail.
 
-(Version $Revision: 1.3 $, last update $Date: 2000/04/07 19:20:38 $ )
+(Version $Revision: 1.6 $, last update $Date: 2001/05/29 01:31:13 $ )
Index: gnu/usr.sbin/sendmail/sendmail/TRACEFLAGS
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/TRACEFLAGS,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- gnu/usr.sbin/sendmail/sendmail/TRACEFLAGS	2000/04/02 19:05:43	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/TRACEFLAGS	2001/05/29 01:31:14	1.3
@@ -1,4 +1,4 @@
-#	$Sendmail: TRACEFLAGS,v 8.29 1999/11/04 23:31:02 gshapiro Exp $
+#	$Sendmail: TRACEFLAGS,v 8.29.16.1 2001/05/03 17:24:00 gshapiro 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,6 +75,7 @@
 62	multiple	file descriptor checking
 63	queue.c		runqueue process watching
 64	multiple	Milter
+67	conf.c		signals
 80			content length
 81			sun remote mode
 91	mci.c		syslogging of MCI cache information
Index: gnu/usr.sbin/sendmail/sendmail/alias.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/alias.c,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -r1.2 -r1.4
--- gnu/usr.sbin/sendmail/sendmail/alias.c	2000/04/07 19:20:38	1.2
+++ gnu/usr.sbin/sendmail/sendmail/alias.c	2001/05/29 01:31:14	1.4
@@ -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
@@ -13,9 +13,12 @@
 #include <sendmail.h>
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: alias.c,v 8.142 2000/03/31 05:35:29 ca Exp $";
+static char id[] = "@(#)$Sendmail: alias.c,v 8.142.4.11 2001/05/03 17:24:01 gshapiro Exp $";
 #endif /* ! lint */
 
+# define SEPARATOR ':'
+# define ALIAS_SPEC_SEPARATORS	" ,/:"
+
 static MAP	*AliasFileMap = NULL;	/* the actual aliases.files map */
 static int	NAliasFileMaps;	/* the number of entries in AliasFileMap */
 
@@ -276,9 +279,8 @@
 		map = &s->s_map;
 		memset(map, '\0', sizeof *map);
 		map->map_mname = s->s_name;
-
-		p = strpbrk(p, " ,/:");
-		if (p != NULL && *p == ':')
+		p = strpbrk(p, ALIAS_SPEC_SEPARATORS);
+		if (p != NULL && *p == SEPARATOR)
 		{
 			/* map name */
 			*p++ = '\0';
@@ -403,8 +405,9 @@
 				dprintf("aliaswait: sleeping for %u seconds\n",
 					sleeptime);
 
+			map->map_mflags |= MF_CLOSING;
 			map->map_class->map_close(map);
-			map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
+			map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
 			(void) sleep(sleeptime);
 			sleeptime *= 2;
 			if (sleeptime > 60)
@@ -435,7 +438,8 @@
 	{
 #if !_FFR_REMOVE_AUTOREBUILD
 		/* database is out of date */
-		if (AutoRebuild && stb.st_ino != 0 &&
+		if (AutoRebuild &&
+		    stb.st_ino != 0 &&
 		    (stb.st_uid == geteuid() ||
 		     (geteuid() == 0 && stb.st_uid == TrustedUid)))
 		{
@@ -446,8 +450,9 @@
 			SuprErrs = TRUE;
 			if (isopen)
 			{
+				map->map_mflags |= MF_CLOSING;
 				map->map_class->map_close(map);
-				map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
+				map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
 			}
 			(void) rebuildaliases(map, TRUE);
 			isopen = map->map_class->map_open(map, O_RDONLY);
@@ -592,16 +597,17 @@
 	/* add distinguished entries and close the database */
 	if (bitset(MF_OPEN, map->map_mflags))
 	{
+		map->map_mflags |= MF_CLOSING;
 		map->map_class->map_close(map);
-		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
+		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
 	}
 
 	/* restore the old signals */
 	(void) setsignal(SIGINT, oldsigint);
 	(void) setsignal(SIGQUIT, oldsigquit);
-#ifdef SIGTSTP
+# ifdef SIGTSTP
 	(void) setsignal(SIGTSTP, oldsigtstp);
-#endif /* SIGTSTP */
+# endif /* SIGTSTP */
 	return success;
 }
 /*
@@ -729,7 +735,7 @@
 			register char *nlp;
 
 			nlp = &p[strlen(p)];
-			if (nlp[-1] == '\n')
+			if (nlp > p && nlp[-1] == '\n')
 				*--nlp = '\0';
 
 			if (CheckAliases)
@@ -824,21 +830,21 @@
 		}
 
 		if (al.q_paddr != NULL)
-			free(al.q_paddr);
+			sm_free(al.q_paddr);
 		if (al.q_host != NULL)
-			free(al.q_host);
+			sm_free(al.q_host);
 		if (al.q_user != NULL)
-			free(al.q_user);
+			sm_free(al.q_user);
 	}
 
 	CurEnv->e_to = NULL;
 	FileName = NULL;
 	if (Verbose || announcestats)
-		message("%s: %d aliases, longest %d bytes, %d bytes total",
+		message("%s: %ld aliases, longest %ld bytes, %ld bytes total",
 			map->map_file, naliases, longest, bytes);
 	if (LogLevel > 7 && logstats)
 		sm_syslog(LOG_INFO, NOQID,
-			"%s: %d aliases, longest %d bytes, %d bytes total",
+			"%s: %ld aliases, longest %ld bytes, %ld bytes total",
 			map->map_file, naliases, longest, bytes);
 }
 /*
@@ -900,12 +906,12 @@
 		char buf[MAXPATHLEN + 1];
 		struct stat st;
 
-		ep = strchr(pp, ':');
+		ep = strchr(pp, SEPARATOR);
 		if (ep != NULL)
 			*ep = '\0';
 		expand(pp, buf, sizeof buf, e);
 		if (ep != NULL)
-			*ep++ = ':';
+			*ep++ = SEPARATOR;
 		if (buf[0] == '\0')
 			continue;
 		if (tTd(27, 3))
Index: gnu/usr.sbin/sendmail/sendmail/aliases
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/aliases,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/sendmail/aliases	2000/04/02 19:05:43	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/aliases	2001/01/15 21:09:06	1.2
@@ -1,5 +1,5 @@
 #
-#	$Sendmail: aliases,v 8.1 1999/02/06 18:44:07 gshapiro Exp $
+#	$Sendmail: aliases,v 8.1.36.1 2000/10/16 20:18:39 gshapiro Exp $
 #	@(#)aliases	8.2 (Berkeley) 3/5/94
 #
 #  Aliases in this file will NOT be expanded in the header from
@@ -31,24 +31,3 @@
 
 # trap decode to catch security attacks
 decode:		root
-
-# OFFICIAL CSRG/BUG ADDRESSES
-
-# Ftp maintainer.
-ftp: ftp-bugs
-ftp-bugs: bigbug@cs.berkeley.edu
-
-# Distribution office.
-bsd-dist: bsd-dist@cs.berkeley.edu
-
-# Fortune maintainer.
-fortune: fortune@cs.berkeley.edu
-
-# Termcap maintainer.
-termcap: termcap@cs.berkeley.edu
-
-# General bug address.
-ucb-fixes: bigbug@cs.berkeley.edu
-ucb-fixes-request: bigbug@cs.berkeley.edu
-bugs: bugs@cs.berkeley.edu
-# END OFFICIAL BUG ADDRESSES
Index: gnu/usr.sbin/sendmail/sendmail/aliases.5
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/aliases.5,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- gnu/usr.sbin/sendmail/sendmail/aliases.5	2000/06/11 21:03:39	1.4
+++ gnu/usr.sbin/sendmail/sendmail/aliases.5	2001/01/15 21:09:06	1.5
@@ -9,9 +9,9 @@
 .\" the sendmail distribution.
 .\"
 .\"
-.\"     $Sendmail: aliases.5,v 8.15 2000/02/26 01:12:21 ca Exp $
+.\"     $Sendmail: aliases.5,v 8.15.4.2 2000/12/14 23:08:15 gshapiro Exp $
 .\"
-.Dd February 26, 1999
+.Dd December 14, 2000
 .Dt ALIASES 5
 .Os
 .Sh NAME
@@ -104,8 +104,8 @@
 command should be executed each time the aliases file is changed for the
 change to take effect.
 .Sh SEE ALSO
-.Xr dbopen 3 ,
 .Xr dbm 3 ,
+.Xr dbopen 3 ,
 .Xr newaliases 8 ,
 .Xr sendmail 8
 .Rs
Index: gnu/usr.sbin/sendmail/sendmail/arpadate.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/arpadate.c,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- gnu/usr.sbin/sendmail/sendmail/arpadate.c	2000/04/02 19:05:43	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/arpadate.c	2001/05/29 01:31:14	1.3
@@ -1,5 +1,5 @@
 /*
- * 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, 1995-1997 Eric P. Allman.  All rights reserved.
  * Copyright (c) 1988, 1993
@@ -12,7 +12,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: arpadate.c,v 8.23 1999/09/23 19:59:18 ca Exp $";
+static char id[] = "@(#)$Sendmail: arpadate.c,v 8.23.20.2 2001/05/07 22:07:26 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
@@ -117,11 +117,12 @@
 		*q++ = *p++;
 
 	/*
-	 * should really get the timezone from the time in "ud" (which
-	 * is only different if a non-null arg was passed which is different
-	 * from the current time), but for all practical purposes, returning
-	 * the current local zone will do (its all that is ever needed).
-	 */
+	**  should really get the timezone from the time in "ud" (which
+	**  is only different if a non-null arg was passed which is different
+	**  from the current time), but for all practical purposes, returning
+	**  the current local zone will do (its all that is ever needed).
+	*/
+
 	gmt = *gmtime(&t);
 	lt = localtime(&t);
 
Index: gnu/usr.sbin/sendmail/sendmail/bf.h
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/bf.h,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- gnu/usr.sbin/sendmail/sendmail/bf.h	2000/04/02 19:05:43	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/bf.h	2001/02/28 02:43:53	1.3
@@ -1,12 +1,12 @@
 /*
- * 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
  * forth in the LICENSE file which can be found at the top level of
  * the sendmail distribution.
  *
- *	$Sendmail: bf.h,v 8.5 1999/11/04 19:31:25 ca Exp $
+ *	$Sendmail: bf.h,v 8.5.16.2 2001/02/14 04:07:27 gshapiro Exp $
  *
  * Contributed by Exactis.com, Inc.
  *
@@ -20,6 +20,7 @@
 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 *));
 
Index: gnu/usr.sbin/sendmail/sendmail/bf_portable.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/bf_portable.c,v
retrieving revision 1.1.1.1
retrieving revision 1.4
diff -u -r1.1.1.1 -r1.4
--- gnu/usr.sbin/sendmail/sendmail/bf_portable.c	2000/04/02 19:05:43	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/bf_portable.c	2001/05/29 01:31:14	1.4
@@ -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
@@ -11,9 +11,12 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: bf_portable.c,v 8.25 2000/02/26 01:32:25 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: bf_portable.c,v 8.25.4.6 2001/05/03 17:24:01 gshapiro Exp $";
 #endif /* ! lint */
 
+#if SFIO
+# include <sfio/stdio.h>
+#endif /* SFIO */
 
 #include <unistd.h>
 #include <fcntl.h>
@@ -22,10 +25,15 @@
 #include <string.h>
 #include <sys/uio.h>
 #include <errno.h>
+#if !SFIO
 # include <stdio.h>
-#ifndef BF_STANDALONE
+#endif /* !SFIO */
+#ifdef BF_STANDALONE
+# define sm_free free
+# define xalloc malloc
+#else /* BF_STANDALONE */
 # include "sendmail.h"
-#endif /* ! BF_STANDALONE */
+#endif /* BF_STANDALONE */
 #include "bf_portable.h"
 #include "bf.h"
 
@@ -90,7 +98,7 @@
 	}
 
 	/* Allocate memory */
-	bfp = (struct bf *)malloc(sizeof(struct bf));
+	bfp = (struct bf *)xalloc(sizeof(struct bf));
 	if (bfp == NULL)
 	{
 		(void) fclose(retval);
@@ -105,10 +113,10 @@
 			filename, (long) sizeof(struct bf));
 
 	l = strlen(filename) + 1;
-	bfp->bf_filename = (char *)malloc(l);
+	bfp->bf_filename = (char *)xalloc(l);
 	if (bfp->bf_filename == NULL)
 	{
-		free(bfp);
+		sm_free(bfp);
 		(void) fclose(retval);
 
 		/* don't care about errors */
@@ -120,7 +128,7 @@
 
 	/* Fill in the other fields, then add it to the list */
 	bfp->bf_key = retval;
-	bfp->bf_committed = 0;
+	bfp->bf_committed = FALSE;
 	bfp->bf_refcount = 1;
 
 	bfinsert(bfp);
@@ -219,7 +227,6 @@
 
 	/* check to see if there is an error on the stream */
 	err = ferror(fp);
-
 	(void) fflush(fp);
 
 	/*
@@ -278,6 +285,47 @@
 }
 
 /*
+**  BFFSYNC -- fsync the fd associated with the FILE *
+**
+**	Parameters:
+**		fp -- FILE * to fsync
+**
+**	Returns:
+**		0 on success, -1 on error
+**
+**	Sets errno:
+**		EINVAL if FILE * not bfcommitted yet.
+**		any value of errno specified by fsync()
+*/
+
+int
+bffsync(fp)
+	FILE *fp;
+{
+	int fd;
+	struct bf *bfp;
+
+	/* Get associated bf structure */
+	bfp = bflookup(fp);
+
+	/* If called on a normal FILE *, noop */
+	if (bfp != NULL && !bfp->bf_committed)
+		fd = -1;
+	else
+		fd = fileno(fp);
+
+	if (tTd(58, 10))
+		dprintf("bffsync: fd = %d\n", fd);
+
+	if (fd < 0)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	return fsync(fd);
+}
+
+/*
 **  BFCLOSE -- close a buffered file
 **
 **	Parameters:
@@ -333,8 +381,8 @@
 		if (!bfp->bf_committed)
 			retval = unlink(bfp->bf_filename);
 
-		free(bfp->bf_filename);
-		free(bfp);
+		sm_free(bfp->bf_filename);
+		sm_free(bfp);
 		if (tTd(58, 8))
 			dprintf("bfclose: freed %ld\n",
 				(long) sizeof(struct bf));
Index: gnu/usr.sbin/sendmail/sendmail/bf_torek.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/bf_torek.c,v
retrieving revision 1.1.1.1
retrieving revision 1.4
diff -u -r1.1.1.1 -r1.4
--- gnu/usr.sbin/sendmail/sendmail/bf_torek.c	2000/04/02 19:05:43	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/bf_torek.c	2001/05/29 01:31:14	1.4
@@ -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
@@ -11,9 +11,13 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: bf_torek.c,v 8.19 1999/10/11 23:37:26 ca Exp $";
+static char id[] = "@(#)$Sendmail: bf_torek.c,v 8.19.18.6 2001/05/08 06:52:19 gshapiro Exp $";
 #endif /* ! lint */
 
+#if SFIO
+   ERROR README: Can not use bf_torek.c with SFIO.
+#endif /* SFIO */
+
 #include <sys/types.h>
 #include <sys/uio.h>
 #include <fcntl.h>
@@ -22,9 +26,12 @@
 #include <string.h>
 #include <errno.h>
 #include <stdio.h>
-#ifndef BF_STANDALONE
+#ifdef BF_STANDALONE
+# define sm_free free
+# define xalloc malloc
+#else /* BF_STANDALONE */
 # include "sendmail.h"
-#endif /* ! BF_STANDALONE */
+#endif /* BF_STANDALONE */
 #include "bf_torek.h"
 #include "bf.h"
 
@@ -86,7 +93,7 @@
 	}
 
 	/* Allocate memory */
-	bfp = (struct bf *)malloc(sizeof(struct bf));
+	bfp = (struct bf *)xalloc(sizeof(struct bf));
 	if (bfp == NULL)
 	{
 		errno = ENOMEM;
@@ -96,10 +103,10 @@
 	/* A zero bsize is valid, just don't allocate memory */
 	if (bsize > 0)
 	{
-		bfp->bf_buf = (char *)malloc(bsize);
+		bfp->bf_buf = (char *)xalloc(bsize);
 		if (bfp->bf_buf == NULL)
 		{
-			free(bfp);
+			sm_free(bfp);
 			errno = ENOMEM;
 			return NULL;
 		}
@@ -115,12 +122,12 @@
 	bfp->bf_bufsize = bsize;
 	bfp->bf_buffilled = 0;
 	l = strlen(filename) + 1;
-	bfp->bf_filename = (char *)malloc(l);
+	bfp->bf_filename = (char *)xalloc(l);
 	if (bfp->bf_filename == NULL)
 	{
-		free(bfp);
 		if (bfp->bf_buf != NULL)
-			free(bfp->bf_buf);
+			sm_free(bfp->bf_buf);
+		sm_free(bfp);
 		errno = ENOMEM;
 		return NULL;
 	}
@@ -138,10 +145,10 @@
 	{
 		/* Just in case free() sets errno */
 		save_errno = errno;
-		free(bfp);
-		free(bfp->bf_filename);
+		sm_free(bfp->bf_filename);
 		if (bfp->bf_buf != NULL)
-			free(bfp->bf_buf);
+			sm_free(bfp->bf_buf);
+		sm_free(bfp);
 		errno = save_errno;
 		return NULL;
 	}
@@ -281,7 +288,7 @@
 	{
 		/* Don't need buffer anymore; free it */
 		bfp->bf_bufsize = 0;
-		free(bfp->bf_buf);
+		sm_free(bfp->bf_buf);
 	}
 	return 0;
 }
@@ -313,7 +320,6 @@
 
 	/* check to see if there is an error on the stream */
 	err = ferror(fp);
-
 	(void) fflush(fp);
 
 	/*
@@ -377,6 +383,51 @@
 }
 
 /*
+**  BFFSYNC -- fsync the fd associated with the FILE *
+**
+**	Parameters:
+**		fp -- FILE * to fsync
+**
+**	Returns:
+**		0 on success, -1 on error
+**
+**	Sets errno:
+**		EINVAL if FILE * not bfcommitted yet.
+**		any value of errno specified by fsync()
+*/
+
+int
+bffsync(fp)
+	FILE *fp;
+{
+	int fd;
+	struct bf *bfp;
+
+	if (bftest(fp))
+	{
+		/* Get bf structure */
+		bfp = (struct bf *)fp->_cookie;
+
+		if (bfp->bf_ondisk && bfp->bf_committed)
+			fd = bfp->bf_disk_fd;
+		else
+			fd = -1;
+	}
+	else
+		fd = fileno(fp);
+
+	if (tTd(58, 10))
+		dprintf("bffsync: fd = %d\n", fd);
+
+	if (fd < 0)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	return fsync(fd);
+}
+
+/*
 **  BFCLOSE -- close a buffered file
 **
 **	Parameters:
@@ -481,10 +532,10 @@
 
 	/* Need to free the buffer */
 	if (bfp->bf_bufsize > 0)
-		free(bfp->bf_buf);
+		sm_free(bfp->bf_buf);
 
 	/* Finally, free the structure */
-	free(bfp);
+	sm_free(bfp);
 
 	return 0;
 }
Index: gnu/usr.sbin/sendmail/sendmail/clock.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/clock.c,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- gnu/usr.sbin/sendmail/sendmail/clock.c	2000/04/02 19:05:43	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/clock.c	2001/05/29 01:31:14	1.3
@@ -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
@@ -12,7 +12,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: clock.c,v 8.52 1999/10/13 22:16:42 ca Exp $";
+static char id[] = "@(#)$Sendmail: clock.c,v 8.52.18.14 2001/05/17 18:12:28 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
@@ -21,8 +21,10 @@
 # define sigmask(s)	(1 << ((s) - 1))
 #endif /* ! sigmask */
 
+static SIGFUNC_DECL	tick __P((int));
 static void	endsleep __P((void));
 
+
 /*
 **  SETEVENT -- set an event to happen at a specific time.
 **
@@ -41,7 +43,8 @@
 **		none.
 */
 
-EVENT	*FreeEventList;		/* list of free events */
+static EVENT	*volatile EventQueue;		/* head of event queue */
+static EVENT	*volatile FreeEventList;	/* list of free events */
 
 EVENT *
 setevent(intvl, func, arg)
@@ -49,10 +52,7 @@
 	void (*func)();
 	int arg;
 {
-	register EVENT **evp;
 	register EVENT *ev;
-	auto time_t now;
-	int wasblocked;
 
 	if (intvl <= 0)
 	{
@@ -60,34 +60,89 @@
 		return NULL;
 	}
 
+	ENTER_CRITICAL();
+	if (FreeEventList == NULL)
+	{
+		FreeEventList = (EVENT *) xalloc(sizeof *FreeEventList);
+		FreeEventList->ev_link = NULL;
+	}
+	LEAVE_CRITICAL();
+
+	ev = sigsafe_setevent(intvl, func, arg);
+
+	if (tTd(5, 5))
+		dprintf("setevent: intvl=%ld, for=%ld, func=%lx, arg=%d, ev=%lx\n",
+			(long) intvl, (long) (curtime() + intvl),
+			(u_long) func, arg,
+			ev == NULL ? 0 : (u_long) ev);
+
+	return ev;
+}
+
+/*
+**
+**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
+**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+**		DOING.
+*/
+
+EVENT *
+sigsafe_setevent(intvl, func, arg)
+	time_t intvl;
+	void (*func)();
+	int arg;
+{
+	register EVENT **evp;
+	register EVENT *ev;
+	auto time_t now;
+	int wasblocked;
+
+	if (intvl <= 0)
+		return NULL;
+
 	wasblocked = blocksignal(SIGALRM);
 	now = curtime();
 
 	/* search event queue for correct position */
-	for (evp = &EventQueue; (ev = *evp) != NULL; evp = &ev->ev_link)
+	for (evp = (EVENT **) (&EventQueue);
+	     (ev = *evp) != NULL;
+	     evp = &ev->ev_link)
 	{
 		if (ev->ev_time >= now + intvl)
 			break;
 	}
 
-	/* insert new event */
-	ev = FreeEventList;
-	if (ev == NULL)
-		ev = (EVENT *) xalloc(sizeof *ev);
+	ENTER_CRITICAL();
+	if (FreeEventList == NULL)
+	{
+		/*
+		**  This shouldn't happen.  If called from setevent(),
+		**  we have just malloced a FreeEventList entry.  If
+		**  called from a signal handler, it should have been
+		**  from an existing event which tick() just added to the
+		**  FreeEventList.
+		*/
+
+		LEAVE_CRITICAL();
+		return NULL;
+	}
 	else
+	{
+		ev = FreeEventList;
 		FreeEventList = ev->ev_link;
+	}
+	LEAVE_CRITICAL();
+
+	/* insert new event */
 	ev->ev_time = now + intvl;
 	ev->ev_func = func;
 	ev->ev_arg = arg;
 	ev->ev_pid = getpid();
+	ENTER_CRITICAL();
 	ev->ev_link = *evp;
 	*evp = ev;
+	LEAVE_CRITICAL();
 
-	if (tTd(5, 5))
-		dprintf("setevent: intvl=%ld, for=%ld, func=%lx, arg=%d, ev=%lx\n",
-			(long) intvl, (long)(now + intvl), (u_long) func,
-			arg, (u_long) ev);
-
 	(void) setsignal(SIGALRM, tick);
 	intvl = EventQueue->ev_time - now;
 	(void) alarm((unsigned) intvl < 1 ? 1 : intvl);
@@ -122,7 +177,9 @@
 
 	/* find the parent event */
 	wasblocked = blocksignal(SIGALRM);
-	for (evp = &EventQueue; *evp != NULL; evp = &(*evp)->ev_link)
+	for (evp = (EVENT **) (&EventQueue);
+	     *evp != NULL;
+	     evp = &(*evp)->ev_link)
 	{
 		if (*evp == ev)
 			break;
@@ -131,9 +188,11 @@
 	/* now remove it */
 	if (*evp != NULL)
 	{
+		ENTER_CRITICAL();
 		*evp = ev->ev_link;
 		ev->ev_link = FreeEventList;
 		FreeEventList = ev;
+		LEAVE_CRITICAL();
 	}
 
 	/* restore clocks and pick up anything spare */
@@ -177,9 +236,11 @@
 	for (ev = EventQueue; ev->ev_link != NULL; ev = ev->ev_link)
 		continue;
 
+	ENTER_CRITICAL();
 	ev->ev_link = FreeEventList;
 	FreeEventList = EventQueue;
 	EventQueue = NULL;
+	LEAVE_CRITICAL();
 
 	/* restore clocks and pick up anything spare */
 	if (wasblocked == 0)
@@ -200,6 +261,10 @@
 **
 **	Side Effects:
 **		calls the next function in EventQueue.
+**
+**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
+**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+**		DOING.
 */
 
 /* ARGSUSED */
@@ -209,27 +274,67 @@
 {
 	register time_t now;
 	register EVENT *ev;
-	int mypid = getpid();
+	pid_t mypid;
 	int save_errno = errno;
 
 	(void) alarm(0);
-	now = curtime();
 
+	FIX_SYSV_SIGNAL(sig, tick);
+
+	errno = save_errno;
+	CHECK_CRITICAL(sig);
+
+	mypid = getpid();
+	while (PendingSignal != 0)
+	{
+		int sigbit;
+		int sig;
+
+		if (bitset(PEND_SIGHUP, PendingSignal))
+		{
+			sigbit = PEND_SIGHUP;
+			sig = SIGHUP;
+		}
+		else if (bitset(PEND_SIGINT, PendingSignal))
+		{
+			sigbit = PEND_SIGINT;
+			sig = SIGINT;
+		}
+		else if (bitset(PEND_SIGTERM, PendingSignal))
+		{
+			sigbit = PEND_SIGTERM;
+			sig = SIGTERM;
+		}
+		else if (bitset(PEND_SIGUSR1, PendingSignal))
+		{
+			sigbit = PEND_SIGUSR1;
+			sig = SIGUSR1;
+		}
+		else
+		{
+			/* If we get here, we are in trouble */
+			abort();
+		}
+		PendingSignal &= ~sigbit;
+		kill(mypid, sig);
+	}
+
+	now = curtime();
 	if (tTd(5, 4))
 		dprintf("tick: now=%ld\n", (long) now);
 
-	/* reset signal in case System V semantics */
-	(void) setsignal(SIGALRM, tick);
 	while ((ev = EventQueue) != NULL &&
 	       (ev->ev_time <= now || ev->ev_pid != mypid))
 	{
 		void (*f)();
 		int arg;
-		int pid;
+		pid_t pid;
 
 		/* process the event on the top of the queue */
+		ENTER_CRITICAL();
 		ev = EventQueue;
 		EventQueue = EventQueue->ev_link;
+		LEAVE_CRITICAL();
 		if (tTd(5, 6))
 			dprintf("tick: ev=%lx, func=%lx, arg=%d, pid=%d\n",
 				(u_long) ev, (u_long) ev->ev_func,
@@ -239,9 +344,11 @@
 		f = ev->ev_func;
 		arg = ev->ev_arg;
 		pid = ev->ev_pid;
+		ENTER_CRITICAL();
 		ev->ev_link = FreeEventList;
 		FreeEventList = ev;
-		if (pid != getpid())
+		LEAVE_CRITICAL();
+		if (pid != mypid)
 			continue;
 		if (EventQueue != NULL)
 		{
@@ -263,6 +370,72 @@
 	return SIGFUNC_RETURN;
 }
 /*
+**  PEND_SIGNAL -- Add a signal to the pending signal list
+**
+**	Parameters:
+**		sig -- signal to add
+**
+**	Returns:
+**		none.
+**
+**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
+**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+**		DOING.
+*/
+
+void
+pend_signal(sig)
+	int sig;
+{
+	int sigbit;
+	int save_errno = errno;
+
+	/*
+	**  Don't want to interrupt something critical, hence delay
+	**  the alarm for one second.  Hopefully, by then we
+	**  will be out of the critical section.  If not, then
+	**  we will just delay again.  The events to be run will
+	**  still all be run, maybe just a little bit late.
+	*/
+
+	switch (sig)
+	{
+	  case SIGHUP:
+		sigbit = PEND_SIGHUP;
+		break;
+
+	  case SIGINT:
+		sigbit = PEND_SIGINT;
+		break;
+
+	  case SIGTERM:
+		sigbit = PEND_SIGTERM;
+		break;
+
+	  case SIGUSR1:
+		sigbit = PEND_SIGUSR1;
+		break;
+
+	  case SIGALRM:
+		/* don't have to pend these */
+		sigbit = 0;
+		break;
+
+	  default:
+		/* If we get here, we are in trouble */
+		abort();
+
+		/* NOTREACHED */
+		break;
+	}
+
+	if (sigbit != 0)
+		PendingSignal |= sigbit;
+	(void) setsignal(SIGALRM, tick);
+	(void) alarm(1);
+	errno = save_errno;
+}
+/*
 **  SLEEP -- a version of sleep that works with this stuff
 **
 **	Because sleep uses the alarm facility, I must reimplement
@@ -279,8 +452,9 @@
 **		be run during that interval.
 */
 
-static bool	SleepDone;
 
+static bool	volatile SleepDone;
+
 #ifndef SLEEP_T
 # define SLEEP_T	unsigned int
 #endif /* ! SLEEP_T */
@@ -306,5 +480,11 @@
 static void
 endsleep()
 {
+	/*
+	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
+	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+	**	DOING.
+	*/
+
 	SleepDone = TRUE;
 }
Index: gnu/usr.sbin/sendmail/sendmail/collect.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/collect.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/sendmail/collect.c	2000/04/07 19:20:38	1.2
+++ gnu/usr.sbin/sendmail/sendmail/collect.c	2001/05/29 01:31:14	1.5
@@ -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
@@ -12,11 +12,12 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: collect.c,v 8.136 2000/03/15 21:47:27 ca Exp $";
+static char id[] = "@(#)$Sendmail: collect.c,v 8.136.4.21 2001/05/17 18:10:14 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
 
+
 static void	collecttimeout __P((time_t));
 static void	dferror __P((FILE *volatile, char *, ENVELOPE *));
 static void	eatfrom __P((char *volatile, ENVELOPE *));
@@ -46,8 +47,8 @@
 */
 
 static jmp_buf	CtxCollectTimeout;
-static bool	CollectProgress;
-static EVENT	*CollectTimeout;
+static bool	volatile CollectProgress;
+static EVENT	*volatile CollectTimeout = NULL;
 
 /* values for input state machine */
 #define IS_NORM		0	/* middle of line */
@@ -62,41 +63,6 @@
 #define MS_BODY		2	/* reading message body */
 #define MS_DISCARD	3	/* discarding rest of message */
 
-#if _FFR_MILTER
-# define MILTER_EOH() \
-{ \
-	if (bitset(CHHDR_MILTER, chompflags) && \
-	    rstat == EX_OK && \
-	    !bitset(EF_DISCARD, e->e_flags)) \
-	{ \
-		char state; \
-		char *response; \
- \
-		response = milter_eoh(e, &state); \
-		chompflags &= ~CHHDR_MILTER; \
-		switch (state) \
-		{ \
-		  case SMFIR_REPLYCODE: \
-			usrerr(response); \
-			break; \
- \
-		  case SMFIR_REJECT: \
-			usrerr("554 5.7.1 Message rejected"); \
-			break; \
- \
-		  case SMFIR_DISCARD: \
-			e->e_flags |= EF_DISCARD; \
-			break; \
- \
-		  case SMFIR_TEMPFAIL: \
-			usrerr("451 4.7.1 Try again later"); \
-			break; \
-		} \
-	} \
-}
-# endif /* _FFR_MILTER */
-
-
 void
 collect(fp, smtpmode, hdrp, e)
 	FILE *fp;
@@ -118,8 +84,6 @@
 	volatile int hdrslen = 0;
 	volatile int numhdrs = 0;
 	volatile int dfd;
-	volatile int afd;
-	volatile int chompflags = CHHDR_CHECK|CHHDR_USER;
 	volatile int rstat = EX_OK;
 	u_char *volatile pbp;
 	u_char peekbuf[8];
@@ -137,7 +101,9 @@
 	if (!headeronly)
 	{
 		struct stat stbuf;
+		long sff = SFF_OPENASROOT;
 
+
 		(void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
 #if _FFR_QUEUE_FILE_MODE
 		{
@@ -145,18 +111,21 @@
 
 			if (bitset(S_IWGRP, QueueFileMode))
 				oldumask = umask(002);
-			df = bfopen(dfname, QueueFileMode, DataFileBufferSize,
-				    SFF_OPENASROOT);
+			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_OPENASROOT);
+		df = bfopen(dfname, FileMode, DataFileBufferSize, sff);
 #endif /* _FFR_QUEUE_FILE_MODE */
 		if (df == NULL)
 		{
-			syserr("Cannot create %s", dfname);
+			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 */
@@ -179,12 +148,7 @@
 	*/
 
 	if (smtpmode)
-	{
 		message("354 Enter mail, end with \".\" on a line by itself");
-#if _FFR_MILTER
-		chompflags |= CHHDR_MILTER;
-# endif /* _FFR_MILTER */
-	}
 
 	if (tTd(30, 2))
 		dprintf("collect\n");
@@ -235,18 +199,25 @@
 				{
 					errno = 0;
 					c = getc(fp);
-					if (errno != EINTR)
-						break;
-					clearerr(fp);
+
+					if (c == EOF && errno == EINTR)
+					{
+						/* Interrupted, retry */
+						clearerr(fp);
+						continue;
+					}
+					break;
 				}
 				CollectProgress = TRUE;
 				if (TrafficLogFile != NULL && !headeronly)
 				{
 					if (istate == IS_BOL)
-						(void) fprintf(TrafficLogFile, "%05d <<< ",
-							(int) getpid());
+						(void) fprintf(TrafficLogFile,
+							       "%05d <<< ",
+							       (int) getpid());
 					if (c == EOF)
-						(void) fprintf(TrafficLogFile, "[EOF]\n");
+						(void) fprintf(TrafficLogFile,
+							       "[EOF]\n");
 					else
 						(void) putc(c, TrafficLogFile);
 				}
@@ -326,15 +297,23 @@
 
 bufferchar:
 			if (!headeronly)
-				e->e_msgsize++;
+			{
+				/* no overflow? */
+				if (e->e_msgsize >= 0)
+				{
+					e->e_msgsize++;
+					if (MaxMessageSize > 0 &&
+					    !bitset(EF_TOOBIG, e->e_flags) &&
+					    e->e_msgsize > MaxMessageSize)
+						 e->e_flags |= EF_TOOBIG;
+				}
+			}
 			switch (mstate)
 			{
 			  case MS_BODY:
 				/* just put the character out */
-				if (MaxMessageSize <= 0 ||
-				    e->e_msgsize <= MaxMessageSize)
+				if (!bitset(EF_TOOBIG, e->e_flags))
 					(void) putc(c, df);
-
 				/* FALLTHROUGH */
 
 			  case MS_DISCARD:
@@ -359,7 +338,7 @@
 				memmove(buf, obuf, bp - obuf);
 				bp = &buf[bp - obuf];
 				if (obuf != bufbuf)
-					free(obuf);
+					sm_free(obuf);
 			}
 			if (c >= 0200 && c <= 0237)
 			{
@@ -372,8 +351,9 @@
 			else if (c != '\0')
 			{
 				*bp++ = c;
+				hdrslen++;
 				if (MaxHeadersLength > 0 &&
-				    ++hdrslen > MaxHeadersLength)
+				    hdrslen > MaxHeadersLength)
 				{
 					sm_syslog(LOG_NOTICE, e->e_id,
 						  "headers too large (%d max) from %s during message collect",
@@ -424,7 +404,7 @@
 				clearerr(fp);
 				errno = 0;
 				c = getc(fp);
-			} while (errno == EINTR);
+			} while (c == EOF && errno == EINTR);
 			if (c != EOF)
 				(void) ungetc(c, fp);
 			if (c == ' ' || c == '\t')
@@ -438,7 +418,8 @@
 				bp++;
 			*bp = '\0';
 
-			if (bitset(H_EOH, chompheader(buf, (int *)&chompflags,
+			if (bitset(H_EOH, chompheader(buf,
+						      CHHDR_CHECK | CHHDR_USER,
 						      hdrp, e)))
 			{
 				mstate = MS_BODY;
@@ -461,19 +442,8 @@
 				dprintf("collect: rscheck(\"check_eoh\", \"%s $| %s\")\n",
 					hnum, hsize);
 			rstat = rscheck("check_eoh", hnum, hsize, e, FALSE,
-					TRUE, 4);
-
-#if _FFR_MILTER
-			/*
-			**  see if a header check already rejected
-			**  this message or if the check_eoh call
-			**  resulted in an error.  Also, don't call
-			**  filters if we are discarding the message.
-			*/
+					TRUE, 4, NULL);
 
-			MILTER_EOH();
-# endif /* _FFR_MILTER */
-
 			bp = buf;
 
 			/* toss blank line */
@@ -486,8 +456,7 @@
 			}
 
 			/* if not a blank separator, write it out */
-			if (MaxMessageSize <= 0 ||
-			    e->e_msgsize <= MaxMessageSize)
+			if (!bitset(EF_TOOBIG, e->e_flags))
 			{
 				while (*bp != '\0')
 					(void) putc(*bp++, df);
@@ -510,17 +479,9 @@
 		inputerr = TRUE;
 	}
 
-#if _FFR_MILTER
-	/*
-	**  If the message was completely empty (no headers, no body),
-	**  milter hasn't been sent the EOH so do it now.
-	*/
-
-	MILTER_EOH();
-# endif /* _FFR_MILTER */
-
 	/* reset global timer */
-	clrevent(CollectTimeout);
+	if (CollectTimeout != NULL)
+		clrevent(CollectTimeout);
 
 	if (headeronly)
 		return;
@@ -542,13 +503,6 @@
 		/* skip next few clauses */
 		/* EMPTY */
 	}
-	else if ((afd = fileno(df)) >= 0 && fsync(afd) < 0)
-	{
-		dferror(df, "fsync", e);
-		flush_errors(TRUE);
-		finis(TRUE, ExitStat);
-		/* NOTREACHED */
-	}
 	else if (bfcommit(df) < 0)
 	{
 		int save_errno = errno;
@@ -563,7 +517,7 @@
 				st.st_size = -1;
 			errno = EEXIST;
 			syserr("collect: bfcommit(%s): already on disk, size = %ld",
-			       dfile, st.st_size);
+			       dfile, (long) st.st_size);
 			dfd = fileno(df);
 			if (dfd >= 0)
 				dumpfd(dfd, TRUE, TRUE);
@@ -573,6 +527,13 @@
 		flush_errors(TRUE);
 		finis(save_errno != EEXIST, ExitStat);
 	}
+	else if (bffsync(df) < 0)
+	{
+		dferror(df, "bffsync", e);
+		flush_errors(TRUE);
+		finis(TRUE, ExitStat);
+		/* NOTREACHED */
+	}
 	else if (bfclose(df) < 0)
 	{
 		dferror(df, "bfclose", e);
@@ -686,11 +647,11 @@
 			break;
 
 		  case NRA_ADD_BCC:
-			addheader("Bcc", " ", &e->e_header);
+			addheader("Bcc", " ", 0, &e->e_header);
 			break;
 
 		  case NRA_ADD_TO_UNDISCLOSED:
-			addheader("To", "undisclosed-recipients:;", &e->e_header);
+			addheader("To", "undisclosed-recipients:;", 0, &e->e_header);
 			break;
 		}
 
@@ -703,13 +664,13 @@
 				if (tTd(30, 3))
 					dprintf("Adding %s: %s\n",
 						hdr, q->q_paddr);
-				addheader(hdr, q->q_paddr, &e->e_header);
+				addheader(hdr, q->q_paddr, 0, &e->e_header);
 			}
 		}
 	}
 
 	/* check for message too large */
-	if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
+	if (bitset(EF_TOOBIG, e->e_flags))
 	{
 		e->e_flags |= EF_NO_BODY_RETN|EF_CLRQUEUE;
 		e->e_status = "5.2.3";
@@ -762,15 +723,37 @@
 collecttimeout(timeout)
 	time_t timeout;
 {
-	/* if no progress was made, die now */
-	if (!CollectProgress)
+	int save_errno = errno;
+
+	/*
+	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
+	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+	**	DOING.
+	*/
+
+	if (CollectProgress)
+	{
+		/* reset the timeout */
+		CollectTimeout = sigsafe_setevent(timeout, collecttimeout,
+						  timeout);
+		CollectProgress = FALSE;
+	}
+	else
+	{
+		/* event is done */
+		CollectTimeout = NULL;
+	}
+
+	/* if no progress was made or problem resetting event, die now */
+	if (CollectTimeout == NULL)
+	{
+		errno = ETIMEDOUT;
 		longjmp(CtxCollectTimeout, 1);
+	}
 
-	/* otherwise reset the timeout */
-	CollectTimeout = setevent(timeout, collecttimeout, timeout);
-	CollectProgress = FALSE;
+	errno = save_errno;
 }
-/*
+/*
 **  DFERROR -- signal error on writing the data file.
 **
 **	Parameters:
@@ -900,6 +883,11 @@
 			p++;
 		while (*p == ' ')
 			p++;
+		if (strlen(p) < 17)
+		{
+			/* no room for the date */
+			return;
+		}
 		if (!(isascii(*p) && isupper(*p)) ||
 		    p[3] != ' ' || p[13] != ':' || p[16] != ':')
 			continue;
@@ -912,8 +900,10 @@
 			continue;
 
 		for (dt = MonthList; *dt != NULL; dt++)
+		{
 			if (strncmp(*dt, &p[4], 3) == 0)
 				break;
+		}
 		if (*dt != NULL)
 			break;
 	}
Index: gnu/usr.sbin/sendmail/sendmail/conf.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/conf.c,v
retrieving revision 1.4
retrieving revision 1.7
diff -u -r1.4 -r1.7
--- gnu/usr.sbin/sendmail/sendmail/conf.c	2000/04/22 21:07:03	1.4
+++ gnu/usr.sbin/sendmail/sendmail/conf.c	2001/05/29 01:31:14	1.7
@@ -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
@@ -12,13 +12,15 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: conf.c,v 8.646 2000/03/21 19:31:53 ca Exp $";
+static char id[] = "@(#)$Sendmail: conf.c,v 8.646.2.2.2.86 2001/05/17 18:18:40 ca Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
 #include <sendmail/pathnames.h>
-#include <sys/ioctl.h>
-#include <sys/param.h>
+
+# include <sys/ioctl.h>
+# include <sys/param.h>
+
 #include <limits.h>
 #if NETINET || NETINET6
 # include <arpa/inet.h>
@@ -27,6 +29,7 @@
 # include <ulimit.h>
 #endif /* HASULIMIT && defined(HPUX11) */
 
+
 static void	setupmaps __P((void));
 static void	setupmailers __P((void));
 static int	get_num_procs_online __P((void));
@@ -190,9 +193,16 @@
 	{ "truststickybit",		DBS_TRUSTSTICKYBIT		},
 	{ "dontwarnforwardfileinunsafedirpath",
 					DBS_DONTWARNFORWARDFILEINUNSAFEDIRPATH },
+	{ "insufficiententropy",	DBS_INSUFFICIENTENTROPY },
 #if _FFR_UNSAFE_SASL
 	{ "groupreadablesaslfile",	DBS_GROUPREADABLESASLFILE	},
 #endif /* _FFR_UNSAFE_SASL */
+#if _FFR_UNSAFE_WRITABLE_INCLUDE
+	{ "groupwritableforwardfile",	DBS_GROUPWRITABLEFORWARDFILE	},
+	{ "groupwritableincludefile",	DBS_GROUPWRITABLEINCLUDEFILE	},
+	{ "worldwritableforwardfile",	DBS_WORLDWRITABLEFORWARDFILE	},
+	{ "worldwritableincludefile",	DBS_WORLDWRITABLEINCLUDEFILE	},
+#endif /* _FFR_UNSAFE_WRITABLE_INCLUDE */
 	{ NULL,				0				}
 };
 
@@ -370,7 +380,7 @@
 {
 	char buf[100];
 
-	(void) strlcpy(buf, "prog, P=/bin/sh, F=lsoDq9, T=X-Unix/X-Unix/X-Unix, A=sh -c \201u",
+	(void) strlcpy(buf, "prog, P=/bin/sh, F=lsouDq9, T=X-Unix/X-Unix/X-Unix, A=sh -c \201u",
 		sizeof buf);
 	makemailer(buf);
 
@@ -815,7 +825,7 @@
 	char *maptype[MAXMAPSTACK];
 	short mapreturn[MAXMAPACTIONS];
 {
-	int svcno;
+	int svcno = 0;
 	int save_errno = errno;
 
 #ifdef _USE_SUN_NSSWITCH_
@@ -835,7 +845,7 @@
 	else
 		lk = nsw_conf->lookups;
 	svcno = 0;
-	while (lk != NULL)
+	while (lk != NULL && svcno < MAXMAPSTACK)
 	{
 		maptype[svcno] = lk->service_name;
 		if (lk->actions[__NSW_NOTFOUND] == __NSW_RETURN)
@@ -872,7 +882,7 @@
 		errno = save_errno;
 		return -1;
 	}
-	for (svcno = 0; svcno < SVC_PATHSIZE; svcno++)
+	for (svcno = 0; svcno < SVC_PATHSIZE && svcno < MAXMAPSTACK; svcno++)
 	{
 		switch (svcinfo->svcpath[svc][svcno])
 		{
@@ -966,7 +976,7 @@
 
 				st = stab(buf, ST_SERVICE, ST_ENTER);
 				if (st->s_service[0] != NULL)
-					free((void *) st->s_service[0]);
+					sm_free((void *) st->s_service[0]);
 				p = newstr(p);
 				for (svcno = 0; svcno < MAXMAPSTACK; )
 				{
@@ -1216,6 +1226,10 @@
 **  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
@@ -1223,56 +1237,123 @@
 	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
-	struct sigaction n, o;
 
+# ifdef SA_RESTART
 	memset(&n, '\0', sizeof n);
-# if USE_SA_SIGACTION
+#  if USE_SA_SIGACTION
 	n.sa_sigaction = (void(*)(int, siginfo_t *, void *)) handler;
 	n.sa_flags = SA_RESTART|SA_SIGINFO;
-# else /* USE_SA_SIGACTION */
+#  else /* USE_SA_SIGACTION */
 	n.sa_handler = handler;
 	n.sa_flags = SA_RESTART;
-# endif /* USE_SA_SIGACTION */
+#  endif /* USE_SA_SIGACTION */
 	if (sigaction(sig, &n, &o) < 0)
 		return SIG_ERR;
 	return o.sa_handler;
-#else /* SA_RESTART */
+# else /* SA_RESTART */
 
 	/*
 	**  Else check for SYS5SIGNALS or
 	**  BSD4_3 signals
 	*/
 
-# if defined(SYS5SIGNALS) || defined(BSD4_3)
-#  ifdef BSD4_3
+#  if defined(SYS5SIGNALS) || defined(BSD4_3)
+#   ifdef BSD4_3
 	return signal(sig, handler);
-#  else /* BSD4_3 */
+#   else /* BSD4_3 */
 	return sigset(sig, handler);
-#  endif /* BSD4_3 */
-# else /* defined(SYS5SIGNALS) || defined(BSD4_3) */
+#   endif /* BSD4_3 */
+#  else /* defined(SYS5SIGNALS) || defined(BSD4_3) */
 
 	/*
 	**  Finally, if nothing else is available,
 	**  go for a default
 	*/
 
-	struct sigaction n, o;
-
 	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 */
+#  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:
@@ -1288,13 +1369,13 @@
 blocksignal(sig)
 	int sig;
 {
-#ifdef BSD4_3
-# ifndef sigmask
-#  define sigmask(s)	(1 << ((s) - 1))
-# endif /* ! sigmask */
+# 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
+# else /* BSD4_3 */
+#  ifdef ALTOS_SYSTEM_V
 	sigfunc_t handler;
 
 	handler = sigset(sig, SIG_HOLD);
@@ -1302,7 +1383,7 @@
 		return -1;
 	else
 		return handler == SIG_HOLD;
-# else /* ALTOS_SYSTEM_V */
+#  else /* ALTOS_SYSTEM_V */
 	sigset_t sset, oset;
 
 	(void) sigemptyset(&sset);
@@ -1311,8 +1392,8 @@
 		return -1;
 	else
 		return sigismember(&oset, sig);
-# endif /* ALTOS_SYSTEM_V */
-#endif /* BSD4_3 */
+#  endif /* ALTOS_SYSTEM_V */
+# endif /* BSD4_3 */
 }
 /*
 **  RELEASESIGNAL -- release a held signal
@@ -1330,10 +1411,10 @@
 releasesignal(sig)
 	int sig;
 {
-#ifdef BSD4_3
+# ifdef BSD4_3
 	return (sigsetmask(sigblock(0) & ~sigmask(sig)) & sigmask(sig)) != 0;
-#else /* BSD4_3 */
-# ifdef ALTOS_SYSTEM_V
+# else /* BSD4_3 */
+#  ifdef ALTOS_SYSTEM_V
 	sigfunc_t handler;
 
 	handler = sigset(sig, SIG_HOLD);
@@ -1341,7 +1422,7 @@
 		return -1;
 	else
 		return handler == SIG_HOLD;
-# else /* ALTOS_SYSTEM_V */
+#  else /* ALTOS_SYSTEM_V */
 	sigset_t sset, oset;
 
 	(void) sigemptyset(&sset);
@@ -1350,8 +1431,8 @@
 		return -1;
 	else
 		return sigismember(&oset, sig);
-# endif /* ALTOS_SYSTEM_V */
-#endif /* BSD4_3 */
+#  endif /* ALTOS_SYSTEM_V */
+# endif /* BSD4_3 */
 }
 /*
 **  HOLDSIGS -- arrange to hold all signals
@@ -1445,6 +1526,7 @@
 # endif /* _SCO_unix_ */
 #endif /* SECUREWARE || defined(_SCO_unix_) */
 
+
 #ifdef VENDOR_DEFAULT
 	VendorCode = VENDOR_DEFAULT;
 #else /* VENDOR_DEFAULT */
@@ -1501,6 +1583,7 @@
 #define LA_KSTAT	12	/* special Solaris kstat(3k) implementation */
 #define LA_DEVSHORT	13	/* read short from a device */
 #define LA_ALPHAOSF	14	/* Digital UNIX (OSF/1 on Alpha) table() call */
+#define LA_PSET		15	/* Solaris per-processor-set load average */
 
 /* do guesses based on general OS type */
 #ifndef LA_TYPE
@@ -2058,6 +2141,28 @@
 
 #endif /* LA_TYPE == LA_ALPHAOSF */
 
+#if LA_TYPE == LA_PSET
+
+static int
+getla()
+{
+	double avenrun[3];
+
+	if (pset_getloadavg(PS_MYID, avenrun,
+			    sizeof(avenrun) / sizeof(avenrun[0])) < 0)
+	{
+		if (tTd(3, 1))
+			dprintf("getla: pset_getloadavg failed: %s",
+				errstring(errno));
+		return -1;
+	}
+	if (tTd(3, 1))
+		dprintf("getla: %d\n", (int) (avenrun[0] +0.5));
+	return ((int) (avenrun[0] + 0.5));
+}
+
+#endif /* LA_TYPE == LA_PSET */
+
 #if LA_TYPE == LA_ZERO
 
 static int
@@ -2140,7 +2245,7 @@
 	{
 		char labuf[8];
 
-		snprintf(labuf, sizeof labuf, "%d", CurrentLA);
+		snprintf(labuf, sizeof labuf, "%d", la);
 		define(macid("{load_avg}", NULL), newstr(labuf), e);
 	}
 	return la;
@@ -2217,34 +2322,12 @@
 	ENVELOPE *e;
 	int d;
 {
-	time_t now;
-	static time_t lastconn[MAXDAEMONS];
-	static int conncnt[MAXDAEMONS];
-
 #ifdef XLA
 	if (!xla_smtp_ok())
 		return TRUE;
 #endif /* XLA */
-
-	now = curtime();
-	if (now != lastconn[d])
-	{
-		lastconn[d] = now;
-		conncnt[d] = 0;
-	}
-	else if (conncnt[d]++ > ConnRateThrottle && ConnRateThrottle > 0)
-	{
-		/* sleep to flatten out connection load */
-		sm_setproctitle(TRUE, e, "deferring connections on daemon %s: %d per second",
-				name, ConnRateThrottle);
-		if (LogLevel >= 9)
-			sm_syslog(LOG_INFO, NOQID,
-				"deferring connections on daemon %s: %d per second",
-				name, ConnRateThrottle);
-		(void) sleep(1);
-	}
 
-	CurrentLA = getla();
+	CurrentLA = sm_getla(NULL);
 	if (RefuseLA > 0 && CurrentLA >= RefuseLA)
 	{
 		sm_setproctitle(TRUE, e, "rejecting connections on daemon %s: load average: %d",
@@ -2301,6 +2384,7 @@
 # define SPT_TYPE	SPT_REUSEARGV
 #endif /* ! SPT_TYPE */
 
+
 #if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN
 
 # if SPT_TYPE == SPT_PSTAT
@@ -2428,7 +2512,7 @@
 #  if SPT_TYPE == SPT_SCO
 	off_t seek_off;
 	static int kmem = -1;
-	static int kmempid = -1;
+	static pid_t kmempid = -1;
 	struct user u;
 #  endif /* SPT_TYPE == SPT_SCO */
 
@@ -2460,7 +2544,7 @@
 	if (kmem < 0 || kmempid != getpid())
 	{
 		if (kmem >= 0)
-			close(kmem);
+			(void) close(kmem);
 		kmem = open(_PATH_KMEM, O_RDWR, 0);
 		if (kmem < 0)
 			return;
@@ -2562,37 +2646,37 @@
 waitfor(pid)
 	pid_t pid;
 {
-#ifdef WAITUNION
+# ifdef WAITUNION
 	union wait st;
-#else /* WAITUNION */
+# else /* WAITUNION */
 	auto int st;
-#endif /* WAITUNION */
+# endif /* WAITUNION */
 	pid_t i;
-#if defined(ISC_UNIX) || defined(_SCO_unix_)
+# if defined(ISC_UNIX) || defined(_SCO_unix_)
 	int savesig;
-#endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */
+# endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */
 
 	do
 	{
 		errno = 0;
-#if defined(ISC_UNIX) || defined(_SCO_unix_)
+# if defined(ISC_UNIX) || defined(_SCO_unix_)
 		savesig = releasesignal(SIGCHLD);
-#endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */
+# endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */
 		i = wait(&st);
-#if defined(ISC_UNIX) || defined(_SCO_unix_)
+# if defined(ISC_UNIX) || defined(_SCO_unix_)
 		if (savesig > 0)
 			blocksignal(SIGCHLD);
-#endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */
+# endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */
 		if (i > 0)
 			(void) proc_list_drop(i);
 	} while ((i >= 0 || errno == EINTR) && i != pid);
 	if (i < 0)
 		return -1;
-#ifdef WAITUNION
+# ifdef WAITUNION
 	return st.w_status;
-#else /* WAITUNION */
+# else /* WAITUNION */
 	return st;
-#endif /* WAITUNION */
+# endif /* WAITUNION */
 }
 /*
 **  REAPCHILD -- pick up the body of my child, lest it become a zombie
@@ -2606,6 +2690,10 @@
 **	Side Effects:
 **		Picks up extant zombies.
 **		Control socket exits may restart/shutdown daemon.
+**
+**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
+**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+**		DOING.
 */
 
 /* ARGSUSED0 */
@@ -2616,32 +2704,30 @@
 	int save_errno = errno;
 	int st;
 	pid_t pid;
-#if HASWAITPID
+# 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)
 	{
 		st = status;
 		if (count++ > 1000)
-		{
-			if (LogLevel > 0)
-				sm_syslog(LOG_ALERT, NOQID,
-					"reapchild: waitpid loop: pid=%d, status=%x",
-					pid, status);
 			break;
-		}
-#else /* HASWAITPID */
-# ifdef WNOHANG
-	union wait status;
-
+# else /* HASWAITPID */
+#  ifdef WNOHANG
 	while ((pid = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0)
 	{
 		st = status.w_status;
-# else /* WNOHANG */
-	auto int status;
-
+#  else /* WNOHANG */
 	/*
 	**  Catch one zombie -- we will be re-invoked (we hope) if there
 	**  are more.  Unreliable signals probably break this, but this
@@ -2652,8 +2738,8 @@
 	if ((pid = wait(&status)) > 0)
 	{
 		st = status;
-# endif /* WNOHANG */
-#endif /* HASWAITPID */
+#  endif /* WNOHANG */
+# endif /* HASWAITPID */
 		/* Drop PID and check if it was a control socket child */
 		if (proc_list_drop(pid) == PROC_CONTROL &&
 		    WIFEXITED(st))
@@ -2661,21 +2747,17 @@
 			/* if so, see if we need to restart or shutdown */
 			if (WEXITSTATUS(st) == EX_RESTART)
 			{
-				/* emulate a SIGHUP restart */
-				sighup(0);
-				/* NOTREACHED */
+				RestartRequest = "control socket";
 			}
 			else if (WEXITSTATUS(st) == EX_SHUTDOWN)
 			{
 				/* emulate a SIGTERM shutdown */
-				intsig(0);
+				ShutdownRequest = "control socket";
 				/* NOTREACHED */
 			}
 		}
 	}
-#ifdef SYS5SIGNALS
-	(void) setsignal(SIGCHLD, reapchild);
-#endif /* SYS5SIGNALS */
+	FIX_SYSV_SIGNAL(sig, reapchild);
 	errno = save_errno;
 	return SIGFUNC_RETURN;
 }
@@ -2740,18 +2822,14 @@
 	 */
 	if (first)
 	{
-		newenv = (char **) malloc(sizeof(char *) * (envlen + 2));
-		if (newenv == NULL)
-			return -1;
-
+		newenv = (char **) xalloc(sizeof(char *) * (envlen + 2));
 		first = FALSE;
 		(void) memcpy(newenv, environ, sizeof(char *) * envlen);
 	}
 	else
 	{
-		newenv = (char **) realloc((char *)environ, sizeof(char *) * (envlen + 2));
-		if (newenv == NULL)
-			return -1;
+		newenv = (char **) xrealloc((char *)environ,
+					    sizeof(char *) * (envlen + 2));
 	}
 
 	/* actually add in the new entry */
@@ -2848,22 +2926,22 @@
 int
 getdtsize()
 {
-#ifdef RLIMIT_NOFILE
+# ifdef RLIMIT_NOFILE
 	struct rlimit rl;
 
 	if (getrlimit(RLIMIT_NOFILE, &rl) >= 0)
 		return rl.rlim_cur;
-#endif /* RLIMIT_NOFILE */
+# endif /* RLIMIT_NOFILE */
 
-#if HASGETDTABLESIZE
+# if HASGETDTABLESIZE
 	return getdtablesize();
-#else /* HASGETDTABLESIZE */
-# ifdef _SC_OPEN_MAX
+# else /* HASGETDTABLESIZE */
+#  ifdef _SC_OPEN_MAX
 	return sysconf(_SC_OPEN_MAX);
-# else /* _SC_OPEN_MAX */
+#  else /* _SC_OPEN_MAX */
 	return NOFILE;
-# endif /* _SC_OPEN_MAX */
-#endif /* HASGETDTABLESIZE */
+#  endif /* _SC_OPEN_MAX */
+# endif /* HASGETDTABLESIZE */
 }
 /*
 **  UNAME -- get the UUCP name of this system.
@@ -2908,7 +2986,7 @@
 			return 0;
 	}
 
-# if 0
+#  if 0
 	/*
 	**  Popen is known to have security holes.
 	*/
@@ -2924,7 +3002,7 @@
 		if (name->nodename[0] != '\0')
 			return 0;
 	}
-# endif /* 0 */
+#  endif /* 0 */
 
 	return -1;
 }
@@ -2971,21 +3049,21 @@
 pid_t
 setsid __P ((void))
 {
-# ifdef TIOCNOTTY
+#  ifdef TIOCNOTTY
 	int fd;
 
 	fd = open("/dev/tty", O_RDWR, 0);
 	if (fd >= 0)
 	{
-		(void) ioctl(fd, (int) TIOCNOTTY, (char *) 0);
+		(void) ioctl(fd, TIOCNOTTY, (char *) 0);
 		(void) close(fd);
 	}
-# endif /* TIOCNOTTY */
-# ifdef SYS5SETPGRP
+#  endif /* TIOCNOTTY */
+#  ifdef SYS5SETPGRP
 	return setpgrp();
-# else /* SYS5SETPGRP */
+#  else /* SYS5SETPGRP */
 	return setpgid(0, getpid());
-# endif /* SYS5SETPGRP */
+#  endif /* SYS5SETPGRP */
 }
 
 #endif /* !HASSETSID */
@@ -3240,7 +3318,7 @@
 	char *user;
 	char *shell;
 {
-#if HASGETUSERSHELL
+# if HASGETUSERSHELL
 	register char *p;
 	extern char *getusershell();
 
@@ -3254,10 +3332,10 @@
 			break;
 	endusershell();
 	return p != NULL;
-#else /* HASGETUSERSHELL */
-# if USEGETCONFATTR
+# else /* HASGETUSERSHELL */
+#  if USEGETCONFATTR
 	auto char *v;
-# endif /* USEGETCONFATTR */
+#  endif /* USEGETCONFATTR */
 	register FILE *shellf;
 	char buf[MAXLINE];
 
@@ -3265,7 +3343,7 @@
 	    ConfigLevel <= 1)
 		return TRUE;
 
-# if USEGETCONFATTR
+#  if USEGETCONFATTR
 	/*
 	**  Naturally IBM has a "better" idea.....
 	**
@@ -3289,7 +3367,7 @@
 		}
 		return FALSE;
 	}
-# endif /* USEGETCONFATTR */
+#  endif /* USEGETCONFATTR */
 
 	shellf = fopen(_PATH_SHELLS, "r");
 	if (shellf == NULL)
@@ -3331,7 +3409,7 @@
 	}
 	(void) fclose(shellf);
 	return FALSE;
-#endif /* HASGETUSERSHELL */
+# endif /* HASGETUSERSHELL */
 }
 /*
 **  FREEDISKSPACE -- see how much free space is on the queue filesystem
@@ -3344,7 +3422,7 @@
 **			block size is stored.
 **
 **	Returns:
-**		The number of bytes free on the queue filesystem.
+**		The number of blocks free on the queue filesystem.
 **		-1 if the statfs call fails.
 **
 **	Side effects:
@@ -3385,48 +3463,48 @@
 	char *dir;
 	long *bsize;
 {
-#if SFS_TYPE != SFS_NONE
-# if SFS_TYPE == SFS_USTAT
+# if SFS_TYPE != SFS_NONE
+#  if SFS_TYPE == SFS_USTAT
 	struct ustat fs;
 	struct stat statbuf;
-#  define FSBLOCKSIZE	DEV_BSIZE
-#  define SFS_BAVAIL	f_tfree
-# else /* SFS_TYPE == SFS_USTAT */
-#  if defined(ultrix)
+#   define FSBLOCKSIZE	DEV_BSIZE
+#   define SFS_BAVAIL	f_tfree
+#  else /* SFS_TYPE == SFS_USTAT */
+#   if defined(ultrix)
 	struct fs_data fs;
-#   define SFS_BAVAIL	fd_bfreen
-#   define FSBLOCKSIZE	1024L
-#  else /* defined(ultrix) */
-#   if SFS_TYPE == SFS_STATVFS
+#    define SFS_BAVAIL	fd_bfreen
+#    define FSBLOCKSIZE	1024L
+#   else /* defined(ultrix) */
+#    if SFS_TYPE == SFS_STATVFS
 	struct statvfs fs;
-#    define FSBLOCKSIZE	fs.f_frsize
-#   else /* SFS_TYPE == SFS_STATVFS */
+#     define FSBLOCKSIZE	fs.f_frsize
+#    else /* SFS_TYPE == SFS_STATVFS */
 	struct statfs fs;
-#    define FSBLOCKSIZE	fs.f_bsize
-#   endif /* SFS_TYPE == SFS_STATVFS */
-#  endif /* defined(ultrix) */
-# endif /* SFS_TYPE == SFS_USTAT */
-# ifndef SFS_BAVAIL
-#  define SFS_BAVAIL f_bavail
-# endif /* ! SFS_BAVAIL */
+#     define FSBLOCKSIZE	fs.f_bsize
+#    endif /* SFS_TYPE == SFS_STATVFS */
+#   endif /* defined(ultrix) */
+#  endif /* SFS_TYPE == SFS_USTAT */
+#  ifndef SFS_BAVAIL
+#   define SFS_BAVAIL f_bavail
+#  endif /* ! SFS_BAVAIL */
 
-# if SFS_TYPE == SFS_USTAT
+#  if SFS_TYPE == SFS_USTAT
 	if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0)
-# else /* SFS_TYPE == SFS_USTAT */
-#  if SFS_TYPE == SFS_4ARGS
+#  else /* SFS_TYPE == SFS_USTAT */
+#   if SFS_TYPE == SFS_4ARGS
 	if (statfs(dir, &fs, sizeof fs, 0) == 0)
-#  else /* SFS_TYPE == SFS_4ARGS */
-#   if SFS_TYPE == SFS_STATVFS
+#   else /* SFS_TYPE == SFS_4ARGS */
+#    if SFS_TYPE == SFS_STATVFS
 	if (statvfs(dir, &fs) == 0)
-#   else /* SFS_TYPE == SFS_STATVFS */
-#    if defined(ultrix)
+#    else /* SFS_TYPE == SFS_STATVFS */
+#     if defined(ultrix)
 	if (statfs(dir, &fs) > 0)
-#    else /* defined(ultrix) */
+#     else /* defined(ultrix) */
 	if (statfs(dir, &fs) == 0)
-#    endif /* defined(ultrix) */
-#   endif /* SFS_TYPE == SFS_STATVFS */
-#  endif /* SFS_TYPE == SFS_4ARGS */
-# endif /* SFS_TYPE == SFS_USTAT */
+#     endif /* defined(ultrix) */
+#    endif /* SFS_TYPE == SFS_STATVFS */
+#   endif /* SFS_TYPE == SFS_4ARGS */
+#  endif /* SFS_TYPE == SFS_USTAT */
 	{
 		if (bsize != NULL)
 			*bsize = FSBLOCKSIZE;
@@ -3437,7 +3515,7 @@
 		else
 			return (long) fs.SFS_BAVAIL;
 	}
-#endif /* SFS_TYPE != SFS_NONE */
+# endif /* SFS_TYPE != SFS_NONE */
 	return -1;
 }
 /*
@@ -3461,7 +3539,8 @@
 	long msize;
 	bool log;
 {
-	long bfree, bsize;
+	long bfree;
+	long bsize;
 
 	if (MinBlocksFree <= 0 && msize <= 0)
 	{
@@ -3470,7 +3549,8 @@
 		return TRUE;
 	}
 
-	if ((bfree = freediskspace(QueueDir, &bsize)) >= 0)
+	bfree = freediskspace(QueueDir, &bsize);
+	if (bfree >= 0)
 	{
 		if (tTd(4, 80))
 			dprintf("enoughdiskspace: bavail=%ld, need=%ld\n",
@@ -3614,6 +3694,7 @@
 **		type -- type of the lock.  Bits can be:
 **			LOCK_EX -- exclusive lock.
 **			LOCK_NB -- non-blocking.
+**			LOCK_UN -- unlock.
 **
 **	Returns:
 **		TRUE if the lock was acquired.
@@ -3629,7 +3710,7 @@
 {
 	int i;
 	int save_errno;
-#if !HASFLOCK
+# if !HASFLOCK
 	int action;
 	struct flock lfd;
 
@@ -3686,15 +3767,15 @@
 	    (save_errno != EACCES && save_errno != EAGAIN))
 	{
 		int omode = -1;
-# ifdef F_GETFL
+#  ifdef F_GETFL
 		(void) fcntl(fd, F_GETFL, &omode);
 		errno = save_errno;
-# endif /* F_GETFL */
+#  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);
 	}
-#else /* !HASFLOCK */
+# else /* !HASFLOCK */
 	if (ext == NULL)
 		ext = "";
 
@@ -3717,15 +3798,15 @@
 	if (!bitset(LOCK_NB, type) || save_errno != EWOULDBLOCK)
 	{
 		int omode = -1;
-# ifdef F_GETFL
+#  ifdef F_GETFL
 		(void) fcntl(fd, F_GETFL, &omode);
 		errno = save_errno;
-# endif /* F_GETFL */
+#  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);
 	}
-#endif /* !HASFLOCK */
+# endif /* !HASFLOCK */
 	if (tTd(55, 60))
 		dprintf("FAIL\n");
 	errno = save_errno;
@@ -3793,7 +3874,7 @@
 	int fd;
 	bool safedir;
 {
-#if (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && \
+# if (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && \
     (defined(_PC_CHOWN_RESTRICTED) || defined(_GNU_TYPES_H))
 	int rval;
 
@@ -3809,14 +3890,14 @@
 
 	errno = 0;
 	rval = fpathconf(fd, _PC_CHOWN_RESTRICTED);
-# if SAFENFSPATHCONF
+#  if SAFENFSPATHCONF
 	return errno == 0 && rval IS_SAFE_CHOWN;
-# else /* SAFENFSPATHCONF */
+#  else /* SAFENFSPATHCONF */
 	return safedir && errno == 0 && rval IS_SAFE_CHOWN;
-# endif /* SAFENFSPATHCONF */
-#else /* (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && \ */
+#  endif /* SAFENFSPATHCONF */
+# 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
@@ -4121,31 +4202,17 @@
 		dprintf("validate_connection(%s, %s)\n",
 			hostname, anynet_ntoa(sap));
 
-	if (rscheck("check_relay", hostname, anynet_ntoa(sap), e, TRUE, TRUE, 4)
-	    != EX_OK)
+	if (rscheck("check_relay", hostname, anynet_ntoa(sap),
+		    e, TRUE, TRUE, 4, NULL) != EX_OK)
 	{
 		static char reject[BUFSIZ*2];
 		extern char MsgBuf[];
 
 		if (tTd(48, 4))
 			dprintf("  ... validate_connection: BAD (rscheck)\n");
-
-		if (strlen(MsgBuf) > 5)
-		{
-			if (ISSMTPCODE(MsgBuf))
-			{
-				int off;
 
-				if ((off = isenhsc(MsgBuf + 4, ' ')) > 0)
-					off += 5;
-				else
-					off = 4;
-				(void) strlcpy(reject, &MsgBuf[off],
-					       sizeof reject);
-			}
-			else
-				(void) strlcpy(reject, MsgBuf, sizeof reject);
-		}
+		if (strlen(MsgBuf) >= 3)
+			(void) strlcpy(reject, MsgBuf, sizeof reject);
 		else
 			(void) strlcpy(reject, "Access denied", sizeof reject);
 
@@ -4325,8 +4392,14 @@
 **	Support IPv6 as well as IPv4.
 */
 
-#if NETINET6 && NEEDSGETIPNODE && __RES < 19990909
+#if NETINET6 && NEEDSGETIPNODE
 
+# ifndef AI_DEFAULT
+#  define AI_DEFAULT	0	/* dummy */
+# endif /* ! AI_DEFAULT */
+# ifndef AI_ADDRCONFIG
+#  define AI_ADDRCONFIG	0	/* dummy */
+# endif /* ! AI_ADDRCONFIG */
 # ifndef AI_V4MAPPED
 #  define AI_V4MAPPED	0	/* dummy */
 # endif /* ! AI_V4MAPPED */
@@ -4350,7 +4423,7 @@
 		resv6 = bitset(RES_USE_INET6, _res.options);
 		_res.options |= RES_USE_INET6;
 	}
-	h_errno = 0;
+	SM_SET_H_ERRNO(0);
 	h = gethostbyname(name);
 	*err = h_errno;
 	if (family == AF_INET6 && !resv6)
@@ -4367,18 +4440,33 @@
 {
 	struct hostent *h;
 
-	h_errno = 0;
+	SM_SET_H_ERRNO(0);
 	h = gethostbyaddr(addr, len, family);
 	*err = h_errno;
 	return h;
 }
-#endif /* NEEDSGETIPNODE && NETINET6 && __RES < 19990909 */
 
+# if _FFR_FREEHOSTENT
+void
+freehostent(h)
+	struct hostent *h;
+{
+	/*
+	**  Stub routine -- if they don't have getipnodeby*(),
+	**  they probably don't have the free routine either.
+	*/
+
+	return;
+}
+# endif /* _FFR_FREEHOSTENT */
+#endif /* NEEDSGETIPNODE && NETINET6 */
+
 struct hostent *
 sm_gethostbyname(name, family)
 	char *name;
 	int family;
 {
+	int save_errno;
 	struct hostent *h = NULL;
 #if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4))
 # if SOLARIS == 20300 || SOLARIS == 203
@@ -4389,19 +4477,21 @@
 	if (tTd(61, 10))
 		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);
 	h = __switch_gethostbyname(name);
+	save_errno = errno;
 # endif /* SOLARIS == 20300 || SOLARIS == 203 */
 #else /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4)) */
 	int nmaps;
 # if NETINET6
+	int flags = AI_DEFAULT|AI_ALL;
 	int err;
 # endif /* NETINET6 */
-	int save_errno;
 	char *maptype[MAXMAPSTACK];
 	short mapreturn[MAXMAPACTIONS];
 	char hbuf[MAXNAME];
@@ -4410,8 +4500,11 @@
 		dprintf("sm_gethostbyname(%s, %d)... ", name, family);
 
 # if NETINET6
-	h = getipnodebyname(name, family, AI_V4MAPPED|AI_ALL, &err);
-	h_errno = err;
+#  if ADDRCONFIG_IS_BROKEN
+	flags &= ~AI_ADDRCONFIG;
+#  endif /* ADDRCONFIG_IS_BROKEN */
+	h = getipnodebyname(name, family, flags, &err);
+	SM_SET_H_ERRNO(err);
 # else /* NETINET6 */
 	h = gethostbyname(name);
 # endif /* NETINET6 */
@@ -4424,9 +4517,12 @@
 
 		nmaps = switch_map_find("hosts", maptype, mapreturn);
 		while (--nmaps >= 0)
+		{
 			if (strcmp(maptype[nmaps], "nis") == 0 ||
 			    strcmp(maptype[nmaps], "files") == 0)
 				break;
+		}
+
 		if (nmaps >= 0)
 		{
 			/* try short name */
@@ -4436,7 +4532,7 @@
 				return NULL;
 			}
 			(void) strlcpy(hbuf, name, sizeof hbuf);
-			shorten_hostname(hbuf);
+			(void) shorten_hostname(hbuf);
 
 			/* if it hasn't been shortened, there's no point */
 			if (strcmp(hbuf, name) != 0)
@@ -4449,7 +4545,7 @@
 				h = getipnodebyname(hbuf, family,
 						    AI_V4MAPPED|AI_ALL,
 						    &err);
-				h_errno = err;
+				SM_SET_H_ERRNO(err);
 				save_errno = errno;
 # else /* NETINET6 */
 				h = gethostbyname(hbuf);
@@ -4512,31 +4608,47 @@
 	int type;
 {
 	struct hostent *hp;
+
+#if NETINET6
+	if (type == AF_INET6 &&
+	    IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *) addr))
+	{
+		/* Avoid reverse lookup for IPv6 unspecified address */
+		SM_SET_H_ERRNO(HOST_NOT_FOUND);
+		return NULL;
+	}
+#endif /* NETINET6 */
+
 #if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204)
 # if SOLARIS == 20300 || SOLARIS == 203
-	static struct hostent he;
-	static char buf[1000];
-	extern struct hostent *_switch_gethostbyaddr_r();
+	{
+		static struct hostent he;
+		static char buf[1000];
+		extern struct hostent *_switch_gethostbyaddr_r();
 
-	hp = _switch_gethostbyaddr_r(addr, len, type, &he, buf, sizeof(buf), &h_errno);
+		hp = _switch_gethostbyaddr_r(addr, len, type, &he,
+					     buf, sizeof(buf), &h_errno);
+	}
 # else /* SOLARIS == 20300 || SOLARIS == 203 */
-	extern struct hostent *__switch_gethostbyaddr();
+	{
+		extern struct hostent *__switch_gethostbyaddr();
 
-	hp = __switch_gethostbyaddr(addr, len, type);
+		hp = __switch_gethostbyaddr(addr, len, type);
+	}
 # endif /* SOLARIS == 20300 || SOLARIS == 203 */
 #else /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) */
 # if NETINET6
-	int err;
-# endif /* NETINET6 */
+	{
+		int err;
 
-# if NETINET6
-	hp = getipnodebyaddr(addr, len, type, &err);
-	h_errno = err;
+		hp = getipnodebyaddr(addr, len, type, &err);
+		SM_SET_H_ERRNO(err);
+	}
 # else /* NETINET6 */
 	hp = gethostbyaddr(addr, len, type);
 # endif /* NETINET6 */
-	return hp;
 #endif /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) */
+	return hp;
 }
 /*
 **  SM_GETPW{NAM,UID} -- wrapper for getpwnam and getpwuid
@@ -4657,14 +4769,16 @@
 #if NETINET
 		case AF_INET:
 			hp = sm_gethostbyaddr((char *) &sa->sin.sin_addr,
-				sizeof(sa->sin.sin_addr), sa->sa.sa_family);
+					      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);
+					      sizeof(sa->sin6.sin6_addr),
+					      sa->sa.sa_family);
 			break;
 #endif /* NETINET6 */
 
@@ -4737,6 +4851,9 @@
 					*ha);
 		}
 	}
+#if _FFR_FREEHOSTENT && NETINET6
+	freehostent(hp);
+#endif /* _FFR_FREEHOSTENT && NETINET6 */
 	return 0;
 }
 /*
@@ -4783,7 +4900,7 @@
 		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)
@@ -4800,12 +4917,12 @@
 			dprintf("system has %d interfaces\n", numifs);
 	}
 	if (numifs < 0)
-# endif /* SIOCGLIFNUM */
+#   endif /* SIOCGLIFNUM */
 		numifs = MAXINTERFACES;
 
 	if (numifs <= 0)
 	{
-		close(s);
+		(void) close(s);
 		return;
 	}
 	lifc.lifc_len = numifs * sizeof (struct lifreq);
@@ -4816,7 +4933,8 @@
 	{
 		if (tTd(0, 4))
 			dprintf("SIOCGLIFCONF failed: %s\n", errstring(errno));
-		close(s);
+		(void) close(s);
+		sm_free(lifc.lifc_buf);
 		return;
 	}
 
@@ -4832,9 +4950,9 @@
 		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;
@@ -4849,7 +4967,10 @@
 
 		s = socket(af, SOCK_DGRAM, 0);
 		if (s == -1)
+		{
+			sm_free(lifc.lifc_buf);
 			return;
+		}
 
 		/*
 		**  If we don't have a complete ifr structure,
@@ -4859,11 +4980,11 @@
 		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))
@@ -4872,7 +4993,7 @@
 		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,
 			       sizeof(ifrf.lifr_name));
@@ -4889,7 +5010,7 @@
 
 		if (!bitset(IFF_UP, ifrf.lifr_flags))
 			continue;
-# endif /* SIOCGLIFFLAGS */
+#   endif /* SIOCGLIFFLAGS */
 
 		ip_addr[0] = '\0';
 
@@ -4897,8 +5018,22 @@
 		switch (af)
 		{
 		  case AF_INET6:
+#  ifdef __KAME__
+			/* convert into proper scoped address */
+			if ((IN6_IS_ADDR_LINKLOCAL(&sa->sin6.sin6_addr) ||
+			     IN6_IS_ADDR_SITELOCAL(&sa->sin6.sin6_addr)) &&
+			    sa->sin6.sin6_scope_id == 0)
+			{
+				struct in6_addr *ia6p;
+
+				ia6p = &sa->sin6.sin6_addr;
+				sa->sin6.sin6_scope_id = ntohs(ia6p->s6_addr[3] |
+							       ((unsigned int)ia6p->s6_addr[2] << 8));
+				ia6p->s6_addr[2] = ia6p->s6_addr[3] = 0;
+			}
+#  endif /* __KAME__ */
 			ia6 = sa->sin6.sin6_addr;
-			if (ia6.s6_addr == in6addr_any.s6_addr)
+			if (IN6_IS_ADDR_UNSPECIFIED(&ia6))
 			{
 				addr = anynet_ntop(&ia6, buf6, sizeof buf6);
 				message("WARNING: interface %s is UP with %s address",
@@ -4912,7 +5047,7 @@
 			if (addr != NULL)
 				(void) snprintf(ip_addr, sizeof ip_addr,
 						"[%.*s]",
-						sizeof ip_addr - 3, addr);
+						(int) sizeof ip_addr - 3, addr);
 			break;
 
 		  case AF_INET:
@@ -4927,7 +5062,7 @@
 
 			/* save IP address in text from */
 			(void) snprintf(ip_addr, sizeof ip_addr, "[%.*s]",
-					sizeof ip_addr - 3, inet_ntoa(ia));
+					(int) sizeof ip_addr - 3, inet_ntoa(ia));
 			break;
 		}
 
@@ -4941,15 +5076,15 @@
 				dprintf("\ta.k.a.: %s\n", ip_addr);
 		}
 
-# ifdef SIOCGLIFFLAGS
+#   ifdef SIOCGLIFFLAGS
 		/* skip "loopback" interface "lo" */
 		if (bitset(IFF_LOOPBACK, ifrf.lifr_flags))
 			continue;
-# endif /* SIOCGLIFFLAGS */
+#   endif /* SIOCGLIFFLAGS */
 		(void) add_hostnames(sa);
 	}
-	free(lifc.lifc_buf);
-	close(s);
+	sm_free(lifc.lifc_buf);
+	(void) close(s);
 #else /* NETINET6 && defined(SIOCGLIFCONF) */
 # if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN
 	int s;
@@ -4988,6 +5123,7 @@
 		if (tTd(0, 4))
 			dprintf("SIOCGIFCONF failed: %s\n", errstring(errno));
 		(void) close(s);
+		sm_free(ifc.ifc_buf);
 		return;
 	}
 
@@ -4998,13 +5134,21 @@
 
 	for (i = 0; i < ifc.ifc_len; )
 	{
+		int af;
 		struct ifreq *ifr = (struct ifreq *) &ifc.ifc_buf[i];
 		SOCKADDR *sa = (SOCKADDR *) &ifr->ifr_addr;
+#   if NETINET6
+		char *addr;
+		struct in6_addr ia6;
+#   endif /* NETINET6 */
 		struct in_addr ia;
-#  ifdef SIOCGIFFLAGS
+#   ifdef SIOCGIFFLAGS
 		struct ifreq ifrf;
-#  endif /* SIOCGIFFLAGS */
+#   endif /* SIOCGIFFLAGS */
 		char ip_addr[256];
+#   if NETINET6
+		char buf6[INET6_ADDRSTRLEN];
+#   endif /* NETINET6 */
 
 		/*
 		**  If we don't have a complete ifr structure,
@@ -5014,20 +5158,25 @@
 		if ((ifc.ifc_len - i) < sizeof *ifr)
 			break;
 
-#  ifdef BSD4_4_SOCKADDR
+#   ifdef BSD4_4_SOCKADDR
 		if (sa->sa.sa_len > sizeof ifr->ifr_addr)
 			i += sizeof ifr->ifr_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));
 
-		if (ifr->ifr_addr.sa_family != AF_INET)
+		af = ifr->ifr_addr.sa_family;
+		if (af != AF_INET
+#   if NETINET6
+		    && af != AF_INET6
+#   endif /* NETINET6 */
+		    )
 			continue;
 
-#  ifdef SIOCGIFFLAGS
+#   ifdef SIOCGIFFLAGS
 		memset(&ifrf, '\0', sizeof(struct ifreq));
 		(void) strlcpy(ifrf.ifr_name, ifr->ifr_name,
 			       sizeof(ifrf.ifr_name));
@@ -5035,25 +5184,74 @@
 		if (tTd(0, 41))
 			dprintf("\tflags: %lx\n",
 				(unsigned long) ifrf.ifr_flags);
-#   define IFRFREF ifrf
-#  else /* SIOCGIFFLAGS */
-#   define IFRFREF (*ifr)
-#  endif /* SIOCGIFFLAGS */
+#    define IFRFREF ifrf
+#   else /* SIOCGIFFLAGS */
+#    define IFRFREF (*ifr)
+#   endif /* SIOCGIFFLAGS */
+
 		if (!bitset(IFF_UP, IFRFREF.ifr_flags))
 			continue;
 
+		ip_addr[0] = '\0';
+
 		/* extract IP address from the list*/
-		ia = sa->sin.sin_addr;
-		if (ia.s_addr == INADDR_ANY || ia.s_addr == INADDR_NONE)
+		switch (af)
 		{
-			message("WARNING: interface %s is UP with %s address",
-				ifr->ifr_name, inet_ntoa(ia));
-			continue;
+		  case AF_INET:
+			ia = sa->sin.sin_addr;
+			if (ia.s_addr == INADDR_ANY ||
+			    ia.s_addr == INADDR_NONE)
+			{
+				message("WARNING: interface %s is UP with %s address",
+					ifr->ifr_name, inet_ntoa(ia));
+				continue;
+			}
+
+			/* save IP address in text from */
+			(void) snprintf(ip_addr, sizeof ip_addr, "[%.*s]",
+					(int) sizeof ip_addr - 3,
+					inet_ntoa(ia));
+			break;
+
+#   if NETINET6
+		  case AF_INET6:
+#    ifdef __KAME__
+			/* convert into proper scoped address */
+			if ((IN6_IS_ADDR_LINKLOCAL(&sa->sin6.sin6_addr) ||
+			     IN6_IS_ADDR_SITELOCAL(&sa->sin6.sin6_addr)) &&
+			    sa->sin6.sin6_scope_id == 0)
+			{
+				struct in6_addr *ia6p;
+
+				ia6p = &sa->sin6.sin6_addr;
+				sa->sin6.sin6_scope_id = ntohs(ia6p->s6_addr[3] |
+							       ((unsigned int)ia6p->s6_addr[2] << 8));
+				ia6p->s6_addr[2] = ia6p->s6_addr[3] = 0;
+			}
+#    endif /* __KAME__ */
+			ia6 = sa->sin6.sin6_addr;
+			if (IN6_IS_ADDR_UNSPECIFIED(&ia6))
+			{
+				addr = anynet_ntop(&ia6, buf6, sizeof buf6);
+				message("WARNING: interface %s is UP with %s address",
+					ifr->ifr_name,
+					addr == NULL ? "(NULL)" : addr);
+				continue;
+			}
+
+			/* 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);
+			break;
+
+#   endif /* NETINET6 */
 		}
 
-		/* save IP address in text from */
-		(void) snprintf(ip_addr, sizeof ip_addr, "[%.*s]",
-				(int) sizeof ip_addr - 3, inet_ntoa(ia));
+		if (ip_addr[0] == '\0')
+			continue;
 
 		if (!wordinclass(ip_addr, 'w'))
 		{
@@ -5068,7 +5266,7 @@
 
 		(void) add_hostnames(sa);
 	}
-	free(ifc.ifc_buf);
+	sm_free(ifc.ifc_buf);
 	(void) close(s);
 #  undef IFRFREF
 # endif /* defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN */
@@ -5130,9 +5328,13 @@
 # ifdef _SC_NPROCESSORS_ONLN
 	nproc = (int) sysconf(_SC_NPROCESSORS_ONLN);
 # else /* _SC_NPROCESSORS_ONLN */
-#  ifdef MPC_GETNUMSPUS
-	nproc = mpctl(MPC_GETNUMSPUS, 0, 0);
-#  endif /* MPC_GETNUMSPUS */
+#  ifdef __hpux
+#   include <sys/pstat.h>
+	struct pst_dynamic psd;
+
+	if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) != -1)
+		nproc = psd.psd_proc_cnt;
+#  endif /* __hpux */
 # endif /* _SC_NPROCESSORS_ONLN */
 #endif /* USESYSCTL */
 
@@ -5198,6 +5400,7 @@
 	static char *buf = NULL;
 	static size_t bufsize;
 	char *begin, *end;
+	int save_errno;
 	int seq = 1;
 	int idlen;
 	char buf0[MAXLINE];
@@ -5206,7 +5409,7 @@
 	extern char *DoprEnd;
 	VA_LOCAL_DECL
 
-	SyslogErrno = errno;
+	save_errno = SyslogErrno = errno;
 	if (id == NULL)
 		id = "NOQUEUE";
 	else if (strcmp(id, NOQID) == 0)
@@ -5237,7 +5440,7 @@
 		/* String too small, redo with correct size */
 		bufsize += SnprfOverflow + 1;
 		if (buf != buf0)
-			free(buf);
+			sm_free(buf);
 		buf = xalloc(bufsize * sizeof (char));
 	}
 	if ((strlen(buf) + idlen + 1) < SYSLOG_BUFSIZE)
@@ -5256,6 +5459,7 @@
 #endif /* LOG */
 		if (buf == buf0)
 			buf = NULL;
+		errno = save_errno;
 		return;
 	}
 
@@ -5310,6 +5514,7 @@
 #endif /* LOG */
 	if (buf == buf0)
 		buf = NULL;
+	errno = save_errno;
 }
 /*
 **  HARD_SYSLOG -- call syslog repeatedly until it works
@@ -5392,6 +5597,9 @@
 
 char	*CompileOptions[] =
 {
+#if EGD
+	"EGD",
+#endif /* EGD */
 #ifdef HESIOD
 	"HESIOD",
 #endif /* HESIOD */
@@ -5467,12 +5675,18 @@
 #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
 	"SUID_ROOT_FILES_OK",
 #endif /* SUID_ROOT_FILES_OK */
@@ -5555,6 +5769,9 @@
 #if HASSRANDOMDEV
 	"HASSRANDOMDEV",
 #endif /* HASSRANDOMDEV */
+#if HASURANDOMDEV
+	"HASURANDOMDEV",
+#endif /* HASURANDOMDEV */
 #if HASSTRERROR
 	"HASSTRERROR",
 #endif /* HASSTRERROR */
@@ -5603,6 +5820,12 @@
 #if SIOCGIFNUM_IS_BROKEN
 	"SIOCGIFNUM_IS_BROKEN",
 #endif /* SIOCGIFNUM_IS_BROKEN */
+#if SNPRINTF_IS_BROKEN
+	"SNPRINTF_IS_BROKEN",
+#endif /* SNPRINTF_IS_BROKEN */
+#if SO_REUSEADDR_IS_BROKEN
+	"SO_REUSEADDR_IS_BROKEN",
+#endif /* SO_REUSEADDR_IS_BROKEN */
 #if SYS5SETPGRP
 	"SYS5SETPGRP",
 #endif /* SYS5SETPGRP */
@@ -5620,3 +5843,4 @@
 #endif /* USESETEUID */
 	NULL
 };
+
Index: gnu/usr.sbin/sendmail/sendmail/conf.h
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/conf.h,v
retrieving revision 1.6
retrieving revision 1.9
diff -u -r1.6 -r1.9
--- gnu/usr.sbin/sendmail/sendmail/conf.h	2000/08/20 18:59:13	1.6
+++ gnu/usr.sbin/sendmail/sendmail/conf.h	2001/05/29 01:31:14	1.9
@@ -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,7 +10,7 @@
  * the sendmail distribution.
  *
  *
- *	$Sendmail: conf.h,v 8.496 2000/04/06 02:15:29 gshapiro Exp $
+ *	$Sendmail: conf.h,v 8.496.4.43 2001/05/20 22:29:59 gshapiro Exp $
  */
 
 /*
@@ -27,20 +27,23 @@
 struct rusage;	/* forward declaration to get gcc to shut up in wait.h */
 #endif /* __GNUC__ */
 
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifndef __QNX__
+# 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 */
-# include <sys/file.h>
-#endif /* ! __QNX__ */
-#include <sys/wait.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <netdb.h>
-#include <pwd.h>
-#include <grp.h>
+#  include <sys/file.h>
+# endif /* ! __QNX__ */
+# include <sys/wait.h>
+# include <limits.h>
+# include <fcntl.h>
+# include <signal.h>
+# include <netdb.h>
+# include <pwd.h>
+# include <grp.h>
 
 /* make sure TOBUFSIZ isn't larger than system limit for size of exec() args */
 #ifdef ARG_MAX
@@ -81,12 +84,13 @@
 #define MAXMIMEARGS	20		/* max args in Content-Type: */
 #define MAXMIMENESTING	20		/* max MIME multipart nesting */
 #define QUEUESEGSIZE	1000		/* increment for queue size */
-#define MAXQFNAME	20		/* max qf file name length */
+#define MAXQFNAME	21		/* max qf file name length */
 #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 */
 #define MAXMACNAMELEN	25		/* max macro name length */
 #define MAXMACROID	0377		/* max macro id number */
+					/* Must match (BITMAPBITS - 1) */
 #ifndef MAXHDRSLEN
 # define MAXHDRSLEN	(32 * 1024)	/* max size of message headers */
 #endif /* ! MAXHDRSLEN */
@@ -100,12 +104,18 @@
 #define MAXLINKPATHLEN	(MAXPATHLEN * MAXSYMLINKS) /* max link-expanded file */
 #define DATA_PROGRESS_TIMEOUT	300	/* how ofter 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 */
 
 #if SASL
 # ifndef AUTH_MECHANISMS
-#  define AUTH_MECHANISMS	"GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5"
+#  if STARTTLS && _FFR_EXT_MECH
+#   define AUTH_MECHANISMS	"EXTERNAL GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5"
+#  else /* STARTTLS && _FFR_EXT_MECH */
+#   define AUTH_MECHANISMS	"GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5"
+#  endif /* STARTTLS && _FFR_EXT_MECH */
 # endif /* ! AUTH_MECHANISMS */
-#else /* SASL */
 #endif /* SASL */
 
 #ifdef LDAPMAP
@@ -227,6 +237,7 @@
 #  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 */
@@ -477,6 +488,9 @@
 #  endif /* ! __svr4__ */
 #  define GIDSET_T	gid_t
 #  define USE_SA_SIGACTION	1	/* use sa_sigaction field */
+#  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 */
@@ -504,6 +518,9 @@
 #    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
@@ -512,19 +529,31 @@
 #  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>
-#    define LA_TYPE	LA_SUBR		/* getloadavg(3c) appears in 2.7 */
+#    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)
-#   undef NETINET6
-#   define NETINET6	1	/* IPv6 added in 2.8 */
 #   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 */
@@ -772,7 +801,7 @@
 **	Also used for Apple Darwin support.
 */
 
-#if defined(__APPLE__) && !defined(NeXT)
+#if defined(DARWIN)
 # define HASFCHMOD	1	/* has fchmod(2) syscall */
 # define HASFLOCK	1	/* has flock(2) syscall */
 # define HASUNAME	1	/* has uname(2) syscall */
@@ -800,7 +829,7 @@
 # define SPT_TYPE	SPT_PSSTRINGS
 # define SPT_PADCHAR	'\0'	/* pad process title with nulls */
 # define ERRLIST_PREDEFINED	/* don't declare sys_errlist */
-#endif /* __APPLE__ && ! NeXT */
+#endif /* DARWIN */
 
 
 /*
@@ -931,6 +960,7 @@
 # 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 */
@@ -939,10 +969,14 @@
 #  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
@@ -971,12 +1005,18 @@
 #  undef SPT_TYPE
 #  define SPT_TYPE	SPT_BUILTIN	/* setproctitle is in libc */
 #  define HASSETLOGIN	1	/* has setlogin(2) */
-#  define HASSRANDOMDEV	1	/* has srandomdev(3) */
-#  define HASSETUSERCONTEXT 1	/* has setusercontext(3) */
+#  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__) */
@@ -1365,6 +1405,10 @@
 */
 
 #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) */
@@ -1384,7 +1428,6 @@
 # endif /* ! HAS_IN_H */
 # define USE_SIGLONGJMP	1	/* sigsetjmp needed for signal handling */
 # ifndef HASFLOCK
-#  include <linux/version.h>
 #  if LINUX_VERSION_CODE < 66399
 #   define HASFLOCK	0	/* flock(2) is broken after 0.99.13 */
 #  else /* LINUX_VERSION_CODE < 66399 */
@@ -1396,6 +1439,11 @@
 # 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 */
@@ -1406,6 +1454,14 @@
 # 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.
    */
@@ -1416,10 +1472,10 @@
 #   else /* (GLIBC_VERSION >= 0x201) */
 #    include <linux/in6.h>	/* IPv6 support */
 #   endif /* (GLIBC_VERSION >= 0x201) */
-#   if (GLIBC_VERSION == 0x201 && !defined(NEEDSGETIPNODE))
+#   if (GLIBC_VERSION >= 0x201 && !defined(NEEDSGETIPNODE))
      /* Have APIs in <netdb.h>, but no support in glibc */
 #    define NEEDSGETIPNODE	1
-#   endif /* (GLIBC_VERSION == 0x201 && ! NEEDSGETIPNODE) */
+#   endif /* (GLIBC_VERSION >= 0x201 && !defined(NEEDSGETIPNODE)) */
 #   undef GLIBC_VERSION
 #  endif /* defined(__GLIBC__) && defined(__GLIBC_MINOR__) */
 # endif /* NETINET6 */
@@ -1663,6 +1719,7 @@
 # define __svr4__
 # define SYS5SIGNALS		1
 # define HASSETSID		1
+# define HASSNPRINTF		1
 # define HASSETREUID		1
 # define HASWAITPID		1
 # define HASGETDTABLESIZE	1
@@ -1682,6 +1739,10 @@
 # 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__ */
 
 /* ###################################################################### */
@@ -1774,6 +1835,7 @@
 # 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
@@ -1853,7 +1915,7 @@
 /*
 **  Amdahl UTS System V 2.1.5 (SVr3-based)
 **
-**	From: Janet Jackson <janet@dialix.oz.au>.
+**    From: Janet Jackson <janet@dialix.oz.au>.
 */
 
 #ifdef _UTS
@@ -2174,6 +2236,7 @@
 # define _PATH_SENDMAILPID	"/var/run/sendmail.pid"
 #endif /* MOTO */
 
+
 /**********************************************************************
 **  End of Per-Operating System defines
 **********************************************************************/
@@ -2184,7 +2247,9 @@
 /* general BSD defines */
 #ifdef BSD
 # define HASGETDTABLESIZE 1	/* has getdtablesize(2) call */
-# define HASSETREUID	1	/* has setreuid(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 */
@@ -2495,9 +2560,9 @@
 # define NAMED_BIND	1	/* not one without the other */
 #endif /* HESIOD && !defined(NAMED_BIND) */
 
-#if NAMED_BIND && !defined(__ksr__) && !defined(h_errno)
+# if NAMED_BIND && !defined( __ksr__ ) && !defined( h_errno )
 extern int	h_errno;
-#endif /* NAMED_BIND && !defined(__ksr__) && !defined(h_errno) */
+# endif /* NAMED_BIND && !defined( __ksr__ ) && !defined( h_errno ) */
 
 #ifdef LDAPMAP
 # include <sys/time.h>
@@ -2675,6 +2740,10 @@
 # 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
@@ -2726,6 +2795,20 @@
 #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.
 */
 
@@ -2740,5 +2823,17 @@
 # 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 */
 
 #endif /* CONF_H */
Index: gnu/usr.sbin/sendmail/sendmail/control.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/control.c,v
retrieving revision 1.1.1.1
retrieving revision 1.4
diff -u -r1.1.1.1 -r1.4
--- gnu/usr.sbin/sendmail/sendmail/control.c	2000/04/02 19:05:44	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/control.c	2001/05/29 01:31:14	1.4
@@ -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.
  *
  * By using this file, you agree to the terms and conditions set
@@ -9,11 +9,34 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: control.c,v 8.44 1999/11/29 22:03:49 ca Exp $";
+static char id[] = "@(#)$Sendmail: control.c,v 8.44.14.20 2001/05/03 17:24:03 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
 
+/* 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 */
+
+struct cmd
+{
+	char	*cmd_name;	/* command name */
+	int	cmd_code;	/* internal code, see below */
+};
+
+static struct cmd	CmdTab[] =
+{
+	{ "help",	CMDHELP		},
+	{ "restart",	CMDRESTART	},
+	{ "shutdown",	CMDSHUTDOWN	},
+	{ "status",	CMDSTATUS	},
+	{ NULL,		CMDERROR	}
+};
+
+
 int ControlSocket = -1;
 
 /*
@@ -76,16 +99,26 @@
 		return -1;
 	}
 
-	if (geteuid() == 0 && TrustedUid != 0)
+	if (geteuid() == 0)
 	{
-		if (chown(ControlSocketName, TrustedUid, -1) < 0)
+		uid_t u = 0;
+
+		if (RunAsUid != 0)
+			u = RunAsUid;
+		else if (TrustedUid != 0)
+			u = TrustedUid;
+
+		if (u != 0 &&
+		    chown(ControlSocketName, u, -1) < 0)
 		{
 			save_errno = errno;
 			sm_syslog(LOG_ALERT, NOQID,
-				  "ownership change on %s failed: %s",
-				  ControlSocketName, errstring(save_errno));
-			message("050 ownership change on %s failed: %s",
-				ControlSocketName, errstring(save_errno));
+				  "ownership change on %s to uid %d failed: %s",
+				  ControlSocketName, (int) u,
+				  errstring(save_errno));
+			message("050 ownership change on %s to uid %d failed: %s",
+				ControlSocketName, (int) u,
+				errstring(save_errno));
 			closecontrolsocket(TRUE);
 			errno = save_errno;
 			return -1;
@@ -140,8 +173,8 @@
 			ControlSocket = -1;
 		}
 
-		rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName,
-				sff, S_IRUSR|S_IWUSR, NULL);
+		rval = safefile(ControlSocketName, RunAsUid, RunAsGid,
+				RunAsUserName, sff, S_IRUSR|S_IWUSR, NULL);
 
 		/* if not safe, don't unlink */
 		if (rval != 0)
@@ -197,34 +230,19 @@
 **		none.
 */
 
-struct cmd
-{
-	char	*cmd_name;	/* command name */
-	int	cmd_code;	/* internal code, see below */
-};
-
-/* 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 */
-
-static struct cmd	CmdTab[] =
-{
-	{ "help",	CMDHELP		},
-	{ "restart",	CMDRESTART	},
-	{ "shutdown",	CMDSHUTDOWN	},
-	{ "status",	CMDSTATUS	},
-	{ NULL,		CMDERROR	}
-};
-
 static jmp_buf	CtxControlTimeout;
 
 static void
 controltimeout(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(CtxControlTimeout, 1);
 }
 
@@ -300,7 +318,7 @@
 	/* decode command */
 	for (c = CmdTab; c->cmd_name != NULL; c++)
 	{
-		if (!strcasecmp(c->cmd_name, cmdbuf))
+		if (strcasecmp(c->cmd_name, cmdbuf) == 0)
 			break;
 	}
 
@@ -328,8 +346,23 @@
 
 	  case CMDSTATUS:	/* daemon status */
 		proc_list_probe();
-		fprintf(s, "%d/%d/%ld/%d\r\n", CurChildren, MaxChildren,
-			freediskspace(QueueDir, NULL), sm_getla(NULL));
+		{
+			long bsize;
+			long free;
+
+			free = freediskspace(QueueDir, &bsize);
+
+			/*
+			**  Prevent overflow and don't lose
+			**  precision (if bsize == 512)
+			*/
+
+			free = (long)((double)free * ((double)bsize / 1024));
+
+			fprintf(s, "%d/%d/%ld/%d\r\n",
+				CurChildren, MaxChildren,
+				free, sm_getla(NULL));
+		}
 		proc_list_display(s);
 		break;
 
@@ -343,3 +376,4 @@
 	exit(exitstat);
 }
 #endif /* ! NOT_SENDMAIL */
+
Index: gnu/usr.sbin/sendmail/sendmail/daemon.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/daemon.c,v
retrieving revision 1.3
retrieving revision 1.7
diff -u -r1.3 -r1.7
--- gnu/usr.sbin/sendmail/sendmail/daemon.c	2000/06/18 00:04:21	1.3
+++ gnu/usr.sbin/sendmail/sendmail/daemon.c	2001/05/29 01:31:14	1.7
@@ -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
@@ -13,11 +13,12 @@
 
 #include <sendmail.h>
 
+
 #ifndef lint
 # ifdef DAEMON
-static char id[] = "@(#)$Sendmail: daemon.c,v 8.401 2000/03/11 20:52:46 gshapiro Exp $ (with daemon mode)";
+static char id[] = "@(#)$Sendmail: daemon.c,v 8.401.4.61 2001/05/27 22:14:40 gshapiro Exp $ (with daemon mode)";
 # else /* DAEMON */
-static char id[] = "@(#)$Sendmail: daemon.c,v 8.401 2000/03/11 20:52:46 gshapiro Exp $ (without daemon mode)";
+static char id[] = "@(#)$Sendmail: daemon.c,v 8.401.4.61 2001/05/27 22:14:40 gshapiro Exp $ (without daemon mode)";
 # endif /* DAEMON */
 #endif /* ! lint */
 
@@ -38,6 +39,10 @@
 
 #if DAEMON
 
+# if STARTTLS
+#    include <openssl/rand.h>
+# endif /* STARTTLS */
+
 # include <sys/time.h>
 
 # if IP_SRCROUTE && NETINET
@@ -82,6 +87,8 @@
 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));
 
 /*
 **  DAEMON.C -- routines to use when running as a daemon.
@@ -157,7 +164,6 @@
 # endif /* NETUNIX */
 	extern ENVELOPE BlankEnvelope;
 
-#define D(x,idx)	x[idx]
 
 	for (idx = 0; idx < ndaemons; idx++)
 	{
@@ -165,6 +171,7 @@
 		Daemons[idx].d_firsttime = TRUE;
 		Daemons[idx].d_refuse_connections_until = (time_t) 0;
 	}
+
 	/*
 	**  Try to actually open the connection.
 	*/
@@ -172,9 +179,11 @@
 	if (tTd(15, 1))
 	{
 		for (idx = 0; idx < ndaemons; idx++)
+		{
 			dprintf("getrequests: daemon %s: port %d\n",
 				Daemons[idx].d_name,
 				ntohs(Daemons[idx].d_port));
+		}
 	}
 
 	/* get a socket for the SMTP connection */
@@ -187,6 +196,10 @@
 			  ControlSocketName, errstring(errno));
 
 	(void) setsignal(SIGCHLD, reapchild);
+	(void) setsignal(SIGHUP, sighup);
+
+	/* workaround: can't seem to release the signal in the parent */
+	(void) releasesignal(SIGHUP);
 
 	/* write the pid to file */
 	log_sendmail_pid(e);
@@ -219,67 +232,137 @@
 		bool control = FALSE;
 		int save_errno;
 		int pipefd[2];
+		time_t timenow;
+# if STARTTLS
+		long seed;
+# endif /* STARTTLS */
+		extern bool refuseconnections __P((char *, ENVELOPE *, int));
 
 		/* see if we are rejecting connections */
 		(void) blocksignal(SIGALRM);
 
+		if (ShutdownRequest != NULL)
+			shutdown_daemon();
+		else if (RestartRequest != NULL)
+			restart_daemon();
+
+		timenow = curtime();
+
+		/*
+		**  Use ConnRateThrottle only if the
+		**  last pass was for a connection
+		*/
+
+		if (ConnRateThrottle > 0 && curdaemon >= 0)
+		{
+			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);
+			}
+		}
+
 		for (idx = 0; idx < ndaemons; idx++)
 		{
-			if (curtime() < Daemons[idx].d_refuse_connections_until)
+			if (timenow < 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 (Daemons[idx].d_socket >= 0)
 				{
-				       /* close socket so peer fails quickly */
-				       (void) close(Daemons[idx].d_socket);
-				       Daemons[idx].d_socket = -1;
+					/* close socket so peer fails quickly */
+					(void) close(Daemons[idx].d_socket);
+					Daemons[idx].d_socket = -1;
 				}
 
 				/* refuse connections for next 15 seconds */
-				Daemons[idx].d_refuse_connections_until = curtime() + 15;
+				Daemons[idx].d_refuse_connections_until = timenow + 15;
 			}
 			else if (Daemons[idx].d_socket < 0 ||
 				 Daemons[idx].d_firsttime)
 			{
-			      if (!Daemons[idx].d_firsttime && LogLevel >= 9)
-				sm_syslog(LOG_INFO, NOQID,
-					  "accepting connections again for daemon %s",
-					  Daemons[idx].d_name);
+				if (!Daemons[idx].d_firsttime && LogLevel >= 9)
+					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;
+				/* arrange to (re)open the socket if needed */
+				(void) opendaemonsocket(&Daemons[idx], FALSE);
+				Daemons[idx].d_firsttime = FALSE;
 			}
 		}
-		if (curtime() >= last_disk_space_check)
+
+		/* May have been sleeping above, check again */
+		if (ShutdownRequest != NULL)
+			shutdown_daemon();
+		else if (RestartRequest != NULL)
+			restart_daemon();
+
+		if (timenow >= last_disk_space_check)
 		{
+			bool logged = FALSE;
+
 			if (!enoughdiskspace(MinBlocksFree + 1, FALSE))
 			{
-				if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags))
+				for (idx = 0; idx < ndaemons; idx++)
 				{
-					/* log only if not logged before */
-					if (LogLevel >= 9)
-						sm_syslog(LOG_INFO, NOQID,
-							  "rejecting new messages: min free: %d",
-							  MinBlocksFree);
-					sm_setproctitle(TRUE, e,
-							"rejecting new messages: min free: %d",
-							 MinBlocksFree);
-					setbitn(D_ETRNONLY, Daemons[idx].d_flags);
+					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 if (bitnset(D_ETRNONLY, Daemons[idx].d_flags))
+			else
 			{
-				/* log only if not logged before */
-				if (LogLevel >= 9)
-					sm_syslog(LOG_INFO, NOQID,
-						  "accepting new messages (again)");
-				/* title will be set below */
-				clrbitn(D_ETRNONLY, Daemons[idx].d_flags);
+				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 = curtime() + 60;
+			last_disk_space_check = timenow + 60;
 		}
 
 # if XDEBUG
@@ -320,10 +403,16 @@
 
 		for (;;)
 		{
+			bool setproc = FALSE;
 			int highest = -1;
 			fd_set readfds;
 			struct timeval timeout;
 
+			if (ShutdownRequest != NULL)
+				shutdown_daemon();
+			else if (RestartRequest != NULL)
+				restart_daemon();
+
 			FD_ZERO(&readfds);
 
 			for (idx = 0; idx < ndaemons; idx++)
@@ -331,14 +420,17 @@
 				/* wait for a connection */
 				if (Daemons[idx].d_socket >= 0)
 				{
-					if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags))
+					if (!setproc &&
+					    !bitnset(D_ETRNONLY,
+						     Daemons[idx].d_flags))
 					{
 						sm_setproctitle(TRUE, e,
 								"accepting connections");
+						setproc = TRUE;
 					}
 					if (Daemons[idx].d_socket > highest)
 						highest = Daemons[idx].d_socket;
-					FD_SET(Daemons[idx].d_socket, &readfds);
+					FD_SET((u_int)Daemons[idx].d_socket, &readfds);
 				}
 			}
 
@@ -351,25 +443,24 @@
 			}
 # endif /* NETUNIX */
 
-			/*
-			**  if one socket is closed, set the timeout
-			**  to 5 seconds (so it might get reopened soon),
-			**  otherwise (all sockets open) 60.
-			*/
-			idx = 0;
-			while (idx < ndaemons && Daemons[idx].d_socket >= 0)
-				idx++;
-			if (idx < ndaemons)
-				timeout.tv_sec = 5;
-			else
-				timeout.tv_sec = 60;
+			timeout.tv_sec = 5;
 			timeout.tv_usec = 0;
 
 			t = select(highest + 1, FDSET_CAST &readfds,
 				   NULL, NULL, &timeout);
 
+			/* Did someone signal while waiting? */
+			if (ShutdownRequest != NULL)
+				shutdown_daemon();
+			else if (RestartRequest != NULL)
+				restart_daemon();
+
+
+
 			if (DoQueueRun)
 				(void) runqueue(TRUE, FALSE);
+
+			curdaemon = -1;
 			if (t <= 0)
 			{
 				timedout = TRUE;
@@ -378,7 +469,6 @@
 
 			control = FALSE;
 			errno = 0;
-			curdaemon = -1;
 
 			/* look "round-robin" for an active socket */
 			if ((idx = olddaemon + 1) >= ndaemons)
@@ -389,9 +479,29 @@
 				    FD_ISSET(Daemons[idx].d_socket, &readfds))
 				{
 					lotherend = Daemons[idx].d_socksize;
+					memset(&RealHostAddr, '\0',
+					       sizeof RealHostAddr);
 					t = accept(Daemons[idx].d_socket,
 						   (struct sockaddr *)&RealHostAddr,
 						   &lotherend);
+
+					/*
+					**  If remote side closes before
+					**  accept() finishes, sockaddr
+					**  might not be fully filled in.
+					*/
+
+					if (t >= 0 &&
+					    (lotherend == 0 ||
+# ifdef BSD4_4_SOCKADDR
+					     RealHostAddr.sa.sa_len == 0 ||
+# endif /* BSD4_4_SOCKADDR */
+					     RealHostAddr.sa.sa_family != Daemons[idx].d_addr.sa.sa_family))
+					{
+						(void) close(t);
+						t = -1;
+						errno = EINVAL;
+					}
 					olddaemon = curdaemon = idx;
 					break;
 				}
@@ -400,15 +510,41 @@
 			}
 # if NETUNIX
 			if (curdaemon == -1 && ControlSocket >= 0 &&
-				 FD_ISSET(ControlSocket, &readfds))
+			    FD_ISSET(ControlSocket, &readfds))
 			{
 				struct sockaddr_un sa_un;
 
 				lotherend = sizeof sa_un;
+				memset(&sa_un, '\0', sizeof sa_un);
 				t = accept(ControlSocket,
 					   (struct sockaddr *)&sa_un,
 					   &lotherend);
-				control = TRUE;
+
+				/*
+				**  If remote side closes before
+				**  accept() finishes, sockaddr
+				**  might not be fully filled in.
+				*/
+
+				if (t >= 0 &&
+				    (lotherend == 0 ||
+# ifdef BSD4_4_SOCKADDR
+				     sa_un.sun_len == 0 ||
+# endif /* BSD4_4_SOCKADDR */
+				     sa_un.sun_family != AF_UNIX))
+				{
+					(void) close(t);
+					t = -1;
+					errno = EINVAL;
+				}
+				if (t >= 0)
+					control = TRUE;
+			}
+# else /* NETUNIX */
+			if (curdaemon == -1)
+			{
+				/* No daemon to service */
+				continue;
 			}
 # endif /* NETUNIX */
 			if (t >= 0 || errno != EINTR)
@@ -420,6 +556,7 @@
 			continue;
 		}
 		save_errno = errno;
+		timenow = curtime();
 		(void) blocksignal(SIGALRM);
 		if (t < 0)
 		{
@@ -429,6 +566,15 @@
 			/* 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
+			/*
+			**  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 */
 			continue;
 		}
 
@@ -497,8 +643,17 @@
 		**  of a queue directory (and other things, e.g., MX selection)
 		**  are not "really" random.
 		*/
+# if STARTTLS
+		seed = get_random();
+		RAND_seed((void *) &last_disk_space_check,
+			sizeof last_disk_space_check);
+		RAND_seed((void *) &timenow, sizeof timenow);
+		RAND_seed((void *) &seed, sizeof seed);
+# else /* STARTTLS */
 		(void) get_random();
+# endif /* STARTTLS */
 
+#ifndef DEBUG_NO_FORK
 		/*
 		**  Create a pipe to keep the child from writing to the
 		**  socket until after the parent has closed it.  Otherwise
@@ -523,6 +678,9 @@
 			(void) close(t);
 			continue;
 		}
+#else /* ! DEBUG_NO_FORK */
+		pid = 0;
+#endif /* ! DEBUG_NO_FORK */
 
 		if (pid == 0)
 		{
@@ -535,6 +693,18 @@
 			**	Verify calling user id if possible here.
 			*/
 
+			/* Reset global flags */
+			RestartRequest = NULL;
+			ShutdownRequest = NULL;
+			PendingSignal = 0;
+
+			(void) releasesignal(SIGALRM);
+			(void) releasesignal(SIGCHLD);
+			(void) setsignal(SIGCHLD, SIG_DFL);
+			(void) setsignal(SIGHUP, SIG_DFL);
+			(void) setsignal(SIGTERM, intsig);
+
+
 			if (!control)
 			{
 				define(macid("{daemon_addr}", NULL),
@@ -546,10 +716,6 @@
 				       newstr(status), &BlankEnvelope);
 			}
 
-			(void) releasesignal(SIGALRM);
-			(void) releasesignal(SIGCHLD);
-			(void) setsignal(SIGCHLD, SIG_DFL);
-			(void) setsignal(SIGHUP, intsig);
 			for (idx = 0; idx < ndaemons; idx++)
 			{
 				if (Daemons[idx].d_socket >= 0)
@@ -562,7 +728,7 @@
 			{
 				/* Add control socket process */
 				proc_list_add(getpid(), "console socket child",
-					      PROC_CONTROL_CHILD);
+					PROC_CONTROL_CHILD);
 			}
 			else
 			{
@@ -579,6 +745,7 @@
 						anynet_ntoa(&RealHostAddr));
 			}
 
+#ifndef DEBUG_NO_FORK
 			if (pipefd[0] != -1)
 			{
 				auto char c;
@@ -600,11 +767,13 @@
 					continue;
 				(void) close(pipefd[0]);
 			}
+#endif /* ! DEBUG_NO_FORK */
 
 			/* control socket processing */
 			if (control)
 			{
 				control_command(t, e);
+
 				/* NOTREACHED */
 				exit(EX_SOFTWARE);
 			}
@@ -698,8 +867,7 @@
 		/* parent -- keep track of children */
 		if (control)
 		{
-			snprintf(status, sizeof status,
-				 "control socket server child");
+			snprintf(status, sizeof status, "control socket server child");
 			proc_list_add(pid, status, PROC_CONTROL);
 		}
 		else
@@ -713,15 +881,22 @@
 
 		/* close the read end of the synchronization pipe */
 		if (pipefd[0] != -1)
+		{
 			(void) close(pipefd[0]);
+			pipefd[0] = -1;
+		}
 
 		/* close the port so that others will hang (for a while) */
 		(void) close(t);
 
 		/* release the child by closing the read end of the sync pipe */
 		if (pipefd[1] != -1)
+		{
 			(void) close(pipefd[1]);
+			pipefd[1] = -1;
+		}
 	}
+
 	if (tTd(15, 2))
 		dprintf("getreq: returning\n");
 	return &Daemons[curdaemon].d_flags;
@@ -771,6 +946,14 @@
 			{
 				save_errno = errno;
 				syserr("opendaemonsocket: daemon %s: can't create server SMTP socket", d->d_name);
+				if (bitnset(D_OPTIONAL, d->d_flags) &&
+				    (save_errno == EAFNOSUPPORT ||
+				     save_errno == EPROTONOSUPPORT))
+				{
+					syserr("opendaemonsocket: daemon %s: optional socket disabled", d->d_name);
+					setbitn(D_DISABLE, d->d_flags);
+					return -1;
+				}
 			  severe:
 				if (LogLevel > 0)
 					sm_syslog(LOG_ALERT, NOQID,
@@ -1009,10 +1192,12 @@
 	struct daemon *d;
 {
 # if NETISO
-	short port;
+	short portno;
 # endif /* NETISO */
 	int l;
 	char *h, *flags;
+	char *port = NULL;
+	char *addr = NULL;
 
 # if NETINET
 	if (d->d_addr.sa.sa_family == AF_UNSPEC)
@@ -1071,152 +1256,11 @@
 			break;
 
 		  case 'A':		/* address */
-			switch (d->d_addr.sa.sa_family)
-			{
-# if NETINET
-			  case AF_INET:
-				if (!isascii(*v) || !isdigit(*v) ||
-				    ((d->d_addr.sin.sin_addr.s_addr = inet_addr(v)) == INADDR_NONE))
-				{
-					register struct hostent *hp;
-
-					hp = sm_gethostbyname(v, AF_INET);
-					if (hp == NULL)
-						syserr("554 5.3.0 host \"%s\" unknown",
-						       v);
-					else
-					{
-						while (*(hp->h_addr_list) &&
-						       hp->h_addrtype != AF_INET)
-							hp->h_addr_list++;
-						if (*(hp->h_addr_list) == NULL)
-							syserr("554 5.3.0 host \"%s\" unknown",
-							       v);
-						else
-							memmove(&d->d_addr.sin.sin_addr,
-								*(hp->h_addr_list),
-								INADDRSZ);
-					}
-				}
-				break;
-# endif /* NETINET */
-
-# if NETINET6
-			  case AF_INET6:
-				if (!isascii(*v) || !isxdigit(*v) ||
-				    inet_pton(AF_INET6, v,
-					      &d->d_addr.sin6.sin6_addr) != 1)
-				{
-					register struct hostent *hp;
-
-					hp = sm_gethostbyname(v, AF_INET6);
-					if (hp == NULL)
-						syserr("554 5.3.0 host \"%s\" unknown",
-						       v);
-					else
-					{
-						while (*(hp->h_addr_list) &&
-						       hp->h_addrtype != AF_INET6)
-							hp->h_addr_list++;
-						if (*(hp->h_addr_list) == NULL)
-							syserr("554 5.3.0 host \"%s\" unknown",
-							       v);
-						else
-							memmove(&d->d_addr.sin6.sin6_addr,
-								*(hp->h_addr_list),
-								IN6ADDRSZ);
-					}
-				}
-				break;
-# endif /* NETINET6 */
-
-			  default:
-				syserr("554 5.3.5 address= option unsupported for family %d",
-				       d->d_addr.sa.sa_family);
-				break;
-			}
+			addr = v;
 			break;
 
 		  case 'P':		/* port */
-			switch (d->d_addr.sa.sa_family)
-			{
-# if NETINET
-			  case AF_INET:
-				if (isascii(*v) && isdigit(*v))
-					d->d_addr.sin.sin_port = htons(atoi(v));
-				else
-				{
-#  ifdef NO_GETSERVBYNAME
-					syserr("554 5.3.5 invalid port number: %s",
-					       v);
-#  else /* NO_GETSERVBYNAME */
-					register struct servent *sp;
-
-					sp = getservbyname(v, "tcp");
-					if (sp == NULL)
-						syserr("554 5.3.5 service \"%s\" unknown",
-						       v);
-					else
-						d->d_addr.sin.sin_port = sp->s_port;
-#  endif /* NO_GETSERVBYNAME */
-				}
-				break;
-# endif /* NETINET */
-
-# if NETINET6
-			  case AF_INET6:
-				if (isascii(*v) && isdigit(*v))
-					d->d_addr.sin6.sin6_port = htons(atoi(v));
-				else
-				{
-#  ifdef NO_GETSERVBYNAME
-					syserr("554 5.3.5 invalid port number: %s",
-					       v);
-#  else /* NO_GETSERVBYNAME */
-					register struct servent *sp;
-
-					sp = getservbyname(v, "tcp");
-					if (sp == NULL)
-						syserr("554 5.3.5 service \"%s\" unknown",
-						       v);
-					else
-						d->d_addr.sin6.sin6_port = sp->s_port;
-#  endif /* NO_GETSERVBYNAME */
-				}
-				break;
-# endif /* NETINET6 */
-
-# if NETISO
-			  case AF_ISO:
-				/* assume two byte transport selector */
-				if (isascii(*v) && isdigit(*v))
-					port = htons(atoi(v));
-				else
-				{
-#  ifdef NO_GETSERVBYNAME
-					syserr("554 5.3.5 invalid port number: %s",
-					       v);
-#  else /* NO_GETSERVBYNAME */
-					register struct servent *sp;
-
-					sp = getservbyname(v, "tcp");
-					if (sp == NULL)
-						syserr("554 5.3.5 service \"%s\" unknown",
-						       v);
-					else
-						port = sp->s_port;
-#  endif /* NO_GETSERVBYNAME */
-				}
-				memmove(TSEL(&d->d_addr.siso),
-					(char *) &port, 2);
-				break;
-# endif /* NETISO */
-
-			  default:
-				syserr("554 5.3.5 Port= option unsupported for family %d",
-				       d->d_addr.sa.sa_family);
-				break;
-			}
+			port = v;
 			break;
 
 		  case 'L':		/* listen queue size */
@@ -1233,7 +1277,7 @@
 				if (!(isascii(*h) && isspace(*h)))
 				{
 					if (flags != d->d_mflags)
-						*f++ = ' ';
+						*flags++ = ' ';
 					*flags++ = *h;
 					if (isupper(*h))
 						*flags++ = *h;
@@ -1242,7 +1286,7 @@
 			*flags++ = '\0';
 			for (; *v != '\0'; v++)
 				if (!(isascii(*v) && isspace(*v)))
-					setbitn(*v, d->d_flags);
+					setbitn(bitidx(*v), d->d_flags);
 			break;
 
 		  case 'S':		/* send buffer size */
@@ -1262,6 +1306,167 @@
 			       f);
 		}
 	}
+
+	/* Check addr and port after finding family */
+	if (addr != NULL)
+	{
+		switch (d->d_addr.sa.sa_family)
+		{
+# if NETINET
+		  case AF_INET:
+			if (!isascii(*addr) || !isdigit(*addr) ||
+			    ((d->d_addr.sin.sin_addr.s_addr = inet_addr(addr)) == INADDR_NONE))
+			{
+				register struct hostent *hp;
+
+				hp = sm_gethostbyname(addr, AF_INET);
+				if (hp == NULL)
+					syserr("554 5.3.0 host \"%s\" unknown",
+					       addr);
+				else
+				{
+					while (*(hp->h_addr_list) != NULL &&
+					       hp->h_addrtype != AF_INET)
+						hp->h_addr_list++;
+					if (*(hp->h_addr_list) == NULL)
+						syserr("554 5.3.0 host \"%s\" unknown",
+						       addr);
+					else
+						memmove(&d->d_addr.sin.sin_addr,
+							*(hp->h_addr_list),
+							INADDRSZ);
+#  if _FFR_FREEHOSTENT && NETINET6
+					freehostent(hp);
+					hp = NULL;
+#  endif /* _FFR_FREEHOSTENT && NETINET6 */
+				}
+			}
+			break;
+# endif /* NETINET */
+
+# if NETINET6
+		  case AF_INET6:
+			if (!isascii(*addr) ||
+			    (!isxdigit(*addr) && *addr != ':') ||
+			    inet_pton(AF_INET6, addr,
+				      &d->d_addr.sin6.sin6_addr) != 1)
+			{
+				register struct hostent *hp;
+
+				hp = sm_gethostbyname(addr, AF_INET6);
+				if (hp == NULL)
+					syserr("554 5.3.0 host \"%s\" unknown",
+					       addr);
+				else
+				{
+					while (*(hp->h_addr_list) != NULL &&
+					       hp->h_addrtype != AF_INET6)
+						hp->h_addr_list++;
+					if (*(hp->h_addr_list) == NULL)
+						syserr("554 5.3.0 host \"%s\" unknown",
+						       addr);
+					else
+						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 */
+
+		  default:
+			syserr("554 5.3.5 address= option unsupported for family %d",
+			       d->d_addr.sa.sa_family);
+			break;
+		}
+	}
+
+	if (port != NULL)
+	{
+		switch (d->d_addr.sa.sa_family)
+		{
+# if NETINET
+		  case AF_INET:
+			if (isascii(*port) && isdigit(*port))
+				d->d_addr.sin.sin_port = htons((u_short)atoi((const char *)port));
+			else
+			{
+#  ifdef NO_GETSERVBYNAME
+				syserr("554 5.3.5 invalid port number: %s",
+				       port);
+#  else /* NO_GETSERVBYNAME */
+				register struct servent *sp;
+
+				sp = getservbyname(port, "tcp");
+				if (sp == NULL)
+					syserr("554 5.3.5 service \"%s\" unknown",
+					       port);
+				else
+					d->d_addr.sin.sin_port = sp->s_port;
+#  endif /* NO_GETSERVBYNAME */
+			}
+			break;
+# endif /* NETINET */
+
+# if NETINET6
+		  case AF_INET6:
+			if (isascii(*port) && isdigit(*port))
+				d->d_addr.sin6.sin6_port = htons((u_short)atoi(port));
+			else
+			{
+#  ifdef NO_GETSERVBYNAME
+				syserr("554 5.3.5 invalid port number: %s",
+				       port);
+#  else /* NO_GETSERVBYNAME */
+				register struct servent *sp;
+
+				sp = getservbyname(port, "tcp");
+				if (sp == NULL)
+					syserr("554 5.3.5 service \"%s\" unknown",
+					       port);
+				else
+					d->d_addr.sin6.sin6_port = sp->s_port;
+#  endif /* NO_GETSERVBYNAME */
+			}
+			break;
+# endif /* NETINET6 */
+
+# if NETISO
+		  case AF_ISO:
+			/* assume two byte transport selector */
+			if (isascii(*port) && isdigit(*port))
+				portno = htons((u_short)atoi(port));
+			else
+			{
+#  ifdef NO_GETSERVBYNAME
+				syserr("554 5.3.5 invalid port number: %s",
+				       port);
+#  else /* NO_GETSERVBYNAME */
+				register struct servent *sp;
+
+				sp = getservbyname(port, "tcp");
+				if (sp == NULL)
+					syserr("554 5.3.5 service \"%s\" unknown",
+					       port);
+				else
+					portno = sp->s_port;
+#  endif /* NO_GETSERVBYNAME */
+			}
+			memmove(TSEL(&d->d_addr.siso),
+				(char *) &portno, 2);
+			break;
+# endif /* NETISO */
+
+		  default:
+			syserr("554 5.3.5 Port= option unsupported for family %d",
+			       d->d_addr.sa.sa_family);
+			break;
+		}
+	}
 }
 /*
 **  SETDAEMONOPTIONS -- set options for running the MTA daemon
@@ -1362,6 +1567,47 @@
 		define(macid("{client_flags}", NULL), "", &BlankEnvelope);
 }
 /*
+**  ADDR_FAMILY -- determine address family from address
+**
+**	Parameters:
+**		addr -- the string representation of the address
+**
+**	Returns:
+**		AF_INET, AF_INET6 or AF_UNSPEC
+**
+**	Side Effects:
+**		none.
+*/
+
+static int
+addr_family(addr)
+	char *addr;
+{
+# if NETINET6
+	SOCKADDR clt_addr;
+# endif /* NETINET6 */
+
+# if NETINET
+	if (inet_addr(addr) != INADDR_NONE)
+	{
+		if (tTd(16, 9))
+			printf("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)
+	{
+		if (tTd(16, 9))
+			printf("addr_family(%s): INET6\n", addr);
+		return AF_INET6;
+	}
+# endif /* NETINET6 */
+	if (tTd(16, 9))
+		printf("addr_family(%s): UNSPEC\n", addr);
+	return AF_UNSPEC;
+}
+/*
 **  MAKECONNECTION -- make a connection to an SMTP socket on a machine.
 **
 **	Parameters:
@@ -1417,7 +1663,7 @@
 		for (; *p != '\0'; p++)
 		{
 			if (!(isascii(*p) && isspace(*p)))
-				setbitn(*p, d_flags);
+				setbitn(bitidx(*p), d_flags);
 		}
 	}
 
@@ -1429,7 +1675,7 @@
 			/* look for just this one flag */
 			if (*p == D_IFNHELO)
 			{
-				setbitn(*p, d_flags);
+				setbitn(bitidx(*p), d_flags);
 				break;
 			}
 		}
@@ -1442,36 +1688,28 @@
 
 	/* 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}", NULL), e)) != NULL &&
+	    *p != '\0')
 	{
-		char *f;
 # if NETINET6
 		char p6[INET6_ADDRSTRLEN];
 # endif /* NETINET6 */
 
 		memset(&clt_addr, '\0', sizeof clt_addr);
 
-		/* XXX set all necessary values... */
-		if ((f = macvalue(macid("{if_family}", NULL), e)) != NULL)
-			clt_addr.sa.sa_family = atoi(f);
-		else
-			clt_addr.sa.sa_family = family;
+		/* infer the address family from the address itself */
+		clt_addr.sa.sa_family = addr_family(p);
 		switch (clt_addr.sa.sa_family)
 		{
 # if NETINET
 		  case AF_INET:
-			if ((clt_addr.sin.sin_addr.s_addr = inet_addr(p))
-			    != INADDR_NONE)
+			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;
 				socksize = sizeof (struct sockaddr_in);
 			}
-			else if (clt_addr.sin.sin_port != 0)
-			{
-				clt_addr.sin.sin_addr.s_addr = INADDR_ANY;
-				clt_bind = TRUE;
-				socksize = sizeof (struct sockaddr_in);
-			}
 			break;
 # endif /* NETINET */
 
@@ -1482,15 +1720,9 @@
 			else
 				strlcpy(p6, p, sizeof p6);
 			if (inet_pton(AF_INET6, p6,
-				      &clt_addr.sin6.sin6_addr) == 1)
-			{
-				clt_bind = TRUE;
-				socksize = sizeof (struct sockaddr_in6);
-			}
-			else if (clt_addr.sin6.sin6_port != 0)
+				      &clt_addr.sin6.sin6_addr) == 1 &&
+			    !IN6_IS_ADDR_LOOPBACK(&clt_addr.sin6.sin6_addr))
 			{
-				if (IN6_IS_ADDR_UNSPECIFIED(&clt_addr.sin6.sin6_addr))
-					clt_addr.sin6.sin6_addr = in6addr_any;
 				clt_bind = TRUE;
 				socksize = sizeof (struct sockaddr_in6);
 			}
@@ -1504,12 +1736,14 @@
 			break;
 # endif /* 0 */
 		}
+		if (clt_bind)
+			family = clt_addr.sa.sa_family;
 	}
 	else
 	{
 		STRUCTCOPY(ClientAddr, clt_addr);
 		if (clt_addr.sa.sa_family == AF_UNSPEC)
-			clt_addr.sa.sa_family = InetMode;
+			clt_addr.sa.sa_family = family;
 		switch (clt_addr.sa.sa_family)
 		{
 # if NETINET
@@ -1551,7 +1785,7 @@
 	*/
 
 # if NAMED_BIND
-	h_errno = 0;
+	SM_SET_H_ERRNO(0);
 # endif /* NAMED_BIND */
 	errno = 0;
 	memset(&CurHostAddr, '\0', sizeof CurHostAddr);
@@ -1771,6 +2005,10 @@
 		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 (hp != NULL)
+			freehostent(hp);
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
 		return EX_NOHOST;
 	}
 
@@ -1781,15 +2019,22 @@
 # ifdef XLA
 	/* if too many connections, don't bother trying */
 	if (!xla_noqueue_ok(host))
+	{
+#  if _FFR_FREEHOSTENT && NETINET6
+		if (hp != NULL)
+			freehostent(hp);
+#  endif /* _FFR_FREEHOSTENT && NETINET6 */
 		return EX_TEMPFAIL;
+	}
 # endif /* XLA */
 
 	firstconnect = TRUE;
 	for (;;)
 	{
 		if (tTd(16, 1))
-			dprintf("makeconnection (%s [%s])\n",
-				host, anynet_ntoa(&addr));
+			dprintf("makeconnection (%s [%s].%d (%d))\n",
+				host, anynet_ntoa(&addr), ntohs(port),
+				addr.sa.sa_family);
 
 		/* save for logging */
 		CurHostAddr = addr;
@@ -1802,7 +2047,7 @@
 		}
 		else
 		{
-			s = socket(addr.sa.sa_family, SOCK_STREAM, 0);
+			s = socket(clt_addr.sa.sa_family, SOCK_STREAM, 0);
 		}
 		if (s < 0)
 		{
@@ -1812,6 +2057,10 @@
 			xla_host_end(host);
 # endif /* XLA */
 			mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
+# if _FFR_FREEHOSTENT && NETINET6
+			if (hp != NULL)
+				freehostent(hp);
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
 			errno = save_errno;
 			return EX_TEMPFAIL;
 		}
@@ -1885,6 +2134,10 @@
 				errno = save_errno;
 				syserr("makeconnection: cannot bind socket [%s]",
 				       anynet_ntoa(&clt_addr));
+# if _FFR_FREEHOSTENT && NETINET6
+				if (hp != NULL)
+					freehostent(hp);
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
 				errno = save_errno;
 				return EX_TEMPFAIL;
 			}
@@ -1900,9 +2153,11 @@
 			int i;
 
 			if (e->e_ntries <= 0 && TimeOuts.to_iconnect != 0)
-				ev = setevent(TimeOuts.to_iconnect, connecttimeout, 0);
+				ev = setevent(TimeOuts.to_iconnect,
+					      connecttimeout, 0);
 			else if (TimeOuts.to_connect != 0)
-				ev = setevent(TimeOuts.to_connect, connecttimeout, 0);
+				ev = setevent(TimeOuts.to_connect,
+					      connecttimeout, 0);
 			else
 				ev = NULL;
 
@@ -1983,6 +2238,7 @@
 			}
 			continue;
 		}
+		errno = save_errno;
 
 # if NETINET6
 		if (family == AF_INET6)
@@ -1992,6 +2248,13 @@
 					errstring(save_errno));
 			v6found = TRUE;
 			family = AF_INET;
+#  if _FFR_FREEHOSTENT
+			if (hp != NULL)
+			{
+				freehostent(hp);
+				hp = NULL;
+			}
+#  endif /* _FFR_FREEHOSTENT */
 			goto v4retry;
 		}
 	v6tempfail:
@@ -2008,10 +2271,22 @@
 		xla_host_end(host);
 # endif /* XLA */
 		mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL);
+# if _FFR_FREEHOSTENT && NETINET6
+		if (hp != NULL)
+			freehostent(hp);
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
 		errno = save_errno;
 		return EX_TEMPFAIL;
 	}
 
+# if _FFR_FREEHOSTENT && NETINET6
+	if (hp != NULL)
+	{
+		freehostent(hp);
+		hp = NULL;
+	}
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
+
 	/* connection ok, put it into canonical form */
 	mci->mci_out = NULL;
 	if ((mci->mci_out = fdopen(s, "w")) == NULL ||
@@ -2033,9 +2308,14 @@
 	if (getsockname(s, &addr.sa, &len) == 0)
 	{
 		char *name;
+		char *p;
 
 		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);
+
 		name = hostnamebyanyaddr(&addr);
 		define(macid("{if_name}", NULL), newstr(name), &BlankEnvelope);
 		if (LogLevel > 11)
@@ -2054,6 +2334,7 @@
 	{
 		define(macid("{if_name}", NULL), NULL, &BlankEnvelope);
 		define(macid("{if_addr}", NULL), NULL, &BlankEnvelope);
+		define(macid("{if_family}", NULL), NULL, &BlankEnvelope);
 	}
 	mci_setstat(mci, EX_OK, NULL, NULL);
 	return EX_OK;
@@ -2062,6 +2343,12 @@
 static void
 connecttimeout()
 {
+	/*
+	**  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(CtxConnectTimeout, 1);
 }
@@ -2162,6 +2449,124 @@
 }
 # endif /* NETUNIX */
 /*
+**  SIGHUP -- handle a SIGHUP signal
+**
+**	Parameters:
+**		sig -- incoming signal.
+**
+**	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.
+*/
+
+/* ARGSUSED */
+static SIGFUNC_DECL
+sighup(sig)
+	int sig;
+{
+	int save_errno = errno;
+
+	FIX_SYSV_SIGNAL(sig, sighup);
+	RestartRequest = "signal";
+	errno = save_errno;
+	return SIGFUNC_RETURN;
+}
+/*
+**  RESTART_DAEMON -- Performs a clean restart of the daemon
+**
+**	Parameters:
+**		none.
+**
+**	Returns:
+**		none.
+**
+**	Side Effects:
+**		restarts the daemon or exits if restart fails.
+*/
+
+static void
+restart_daemon()
+{
+	int i;
+	int save_errno;
+	char *reason;
+	sigfunc_t oalrm, ochld, ohup, oint, opipe, oterm, ousr1;
+	extern int DtableSize;
+
+	allsignals(TRUE);
+
+	reason = RestartRequest;
+	RestartRequest = NULL;
+	PendingSignal = 0;
+
+	if (SaveArgv[0][0] != '/')
+	{
+		if (LogLevel > 3)
+			sm_syslog(LOG_INFO, NOQID,
+				  "could not restart: need full path");
+		finis(FALSE, EX_OSFILE);
+	}
+	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)
+	{
+		if (LogLevel > 0)
+			sm_syslog(LOG_ALERT, NOQID,
+				  "could not set[ug]id(%d, %d): %m",
+				  RunAsUid, RunAsGid);
+		finis(FALSE, EX_OSERR);
+	}
+
+	/* arrange for all the files to be closed */
+	for (i = 3; i < DtableSize; i++)
+	{
+		register int j;
+
+		if ((j = fcntl(i, F_GETFD, 0)) != -1)
+			(void) fcntl(i, F_SETFD, j | FD_CLOEXEC);
+	}
+
+	/* need to allow signals before execve() so make them harmless */
+	oalrm = setsignal(SIGALRM, SIG_DFL);
+	ochld = setsignal(SIGCHLD, SIG_DFL);
+	ohup = setsignal(SIGHUP, SIG_DFL);
+	oint = setsignal(SIGINT, SIG_DFL);
+	opipe = setsignal(SIGPIPE, SIG_DFL);
+	oterm = setsignal(SIGTERM, SIG_DFL);
+	ousr1 = setsignal(SIGUSR1, SIG_DFL);
+	allsignals(FALSE);
+
+	(void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
+	save_errno = errno;
+
+	/* restore signals */
+	allsignals(TRUE);
+	(void) setsignal(SIGALRM, oalrm);
+	(void) setsignal(SIGCHLD, ochld);
+	(void) setsignal(SIGHUP, ohup);
+	(void) setsignal(SIGINT, oint);
+	(void) setsignal(SIGPIPE, opipe);
+	(void) setsignal(SIGTERM, oterm);
+	(void) setsignal(SIGUSR1, ousr1);
+
+	errno = save_errno;
+	if (LogLevel > 0)
+		sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m",
+			  SaveArgv[0]);
+	finis(FALSE, EX_OSFILE);
+}
+/*
 **  MYHOSTNAME -- return the name of this host.
 **
 **	Parameters:
@@ -2182,10 +2587,8 @@
 {
 	register struct hostent *hp;
 
-	if (gethostname(hostbuf, size) < 0)
-	{
+	if (gethostname(hostbuf, size) < 0 || hostbuf[0] == '\0')
 		(void) strlcpy(hostbuf, "localhost", size);
-	}
 	hp = sm_gethostbyname(hostbuf, InetMode);
 	if (hp == NULL)
 		return NULL;
@@ -2326,6 +2729,13 @@
 static void
 authtimeout()
 {
+	/*
+	**  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(CtxAuthTimeout, 1);
 }
 
@@ -2334,6 +2744,7 @@
 	int fd;
 	bool *may_be_forged;
 {
+	volatile u_short port = 0;
 	SOCKADDR_LEN_T falen;
 	register char *volatile p = NULL;
 	SOCKADDR la;
@@ -2366,7 +2777,7 @@
 			errno = 0;
 		}
 		(void) snprintf(hbuf, sizeof hbuf, "%s@localhost",
-			RealUserName);
+				RealUserName);
 		if (tTd(9, 1))
 			dprintf("getauthinfo: %s\n", hbuf);
 		return hbuf;
@@ -2404,6 +2815,10 @@
 				if (addrcmp(hp, *ha, &RealHostAddr) == 0)
 					break;
 			*may_be_forged = *ha == NULL;
+# if _FFR_FREEHOSTENT && NETINET6
+			freehostent(hp);
+			hp = NULL;
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
 		}
 	}
 
@@ -2422,6 +2837,7 @@
 			/* no ident info */
 			goto noident;
 		}
+		port = RealHostAddr.sin.sin_port;
 
 		/* create ident query */
 		(void) snprintf(ibuf, sizeof ibuf, "%d,%d\r\n",
@@ -2453,6 +2869,7 @@
 			/* no ident info */
 			goto noident;
 		}
+		port = RealHostAddr.sin6.sin6_port;
 
 		/* create ident query */
 		(void) snprintf(ibuf, sizeof ibuf, "%d,%d\r\n",
@@ -2490,6 +2907,7 @@
 	/* put a timeout around the whole thing */
 	ev = 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)
@@ -2599,6 +3017,24 @@
 	clrevent(ev);
 
 noident:
+	/* put back the original incoming port */
+	switch (RealHostAddr.sa.sa_family)
+	{
+# if NETINET
+	  case AF_INET:
+		if (port > 0)
+			RealHostAddr.sin.sin_port = port;
+		break;
+# endif /* NETINET */
+
+# if NETINET6
+	  case AF_INET6:
+		if (port > 0)
+			RealHostAddr.sin6.sin6_port = port;
+		break;
+# endif /* NETINET6 */
+	}
+
 	if (RealHostName == NULL)
 	{
 		if (tTd(9, 1))
@@ -2728,6 +3164,25 @@
 # endif /* IP_SRCROUTE */
 	if (tTd(9, 1))
 		dprintf("getauthinfo: %s\n", hbuf);
+
+	/* put back the original incoming port */
+	switch (RealHostAddr.sa.sa_family)
+	{
+# if NETINET
+	  case AF_INET:
+		if (port > 0)
+			RealHostAddr.sin.sin_port = port;
+		break;
+# endif /* NETINET */
+
+# if NETINET6
+	  case AF_INET6:
+		if (port > 0)
+			RealHostAddr.sin6.sin6_port = port;
+		break;
+# endif /* NETINET6 */
+	}
+
 	return hbuf;
 }
 /*
@@ -2786,7 +3241,7 @@
 					: s->s_namecanon.nc_cname);
 		errno = s->s_namecanon.nc_errno;
 # if NAMED_BIND
-		h_errno = s->s_namecanon.nc_herrno;
+		SM_SET_H_ERRNO(s->s_namecanon.nc_herrno);
 # endif /* NAMED_BIND */
 		*statp = s->s_namecanon.nc_stat;
 		if (*statp == EX_TEMPFAIL)
@@ -2848,7 +3303,11 @@
 	else
 	{
 		if ((cp = strchr(name, ']')) == NULL)
+		{
+			if (tTd(9, 1))
+				dprintf("FAILED\n");
 			return NULL;
+		}
 		*cp = '\0';
 
 		hp = NULL;
@@ -2869,6 +3328,10 @@
 		{
 			/* found a match -- copy out */
 			ans = denlstring((char *) hp->h_name, TRUE, TRUE);
+# if _FFR_FREEHOSTENT && NETINET6
+			freehostent(hp);
+			hp = NULL;
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
 		}
 	}
 
@@ -2883,6 +3346,8 @@
 			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);
 		return cp;
 	}
 
@@ -2952,6 +3417,8 @@
 		fixcrlf(hostbuf, TRUE);
 		(void) fclose(f);
 	}
+	if (hostbuf[0] == '\0')
+		(void) strlcpy(hostbuf, "localhost", size);
 	return NULL;
 }
 /*
@@ -3053,6 +3520,9 @@
 		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;
 }
 
@@ -3305,8 +3775,35 @@
 	    && inet_addr(hp->h_name) == INADDR_NONE
 #  endif /* NETINET */
 	    )
-		return denlstring((char *) hp->h_name, TRUE, TRUE);
+	{
+		char *name;
+
+		name = denlstring((char *) hp->h_name, TRUE, TRUE);
+
+#  if _FFR_FREEHOSTENT && 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);
+			name = n;
+		}
+
+		freehostent(hp);
+#  endif /* _FFR_FREEHOSTENT && NETINET6 */
+		return name;
+	}
 # endif /* NETINET || NETINET6 */
+
+# if _FFR_FREEHOSTENT && NETINET6
+	if (hp != NULL)
+	{
+		freehostent(hp);
+		hp = NULL;
+	}
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
+
 # if NETUNIX
 	if (sap->sa.sa_family == AF_UNIX && sap->sunix.sun_path[0] == '\0')
 		return "localhost";
Index: gnu/usr.sbin/sendmail/sendmail/deliver.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/deliver.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/sendmail/deliver.c	2000/04/07 19:20:40	1.2
+++ gnu/usr.sbin/sendmail/sendmail/deliver.c	2001/05/29 01:31:14	1.5
@@ -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
@@ -12,15 +12,19 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: deliver.c,v 8.600 2000/04/06 00:50:14 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: deliver.c,v 8.600.2.1.2.81 2001/05/23 02:15:42 ca Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
 
+
 #if HASSETUSERCONTEXT
 # include <login_cap.h>
 #endif /* HASSETUSERCONTEXT */
 
+#if STARTTLS || (SASL && SFIO)
+# include "sfsasl.h"
+#endif /* STARTTLS || (SASL && SFIO) */
 
 static int	deliver __P((ENVELOPE *, ADDRESS *));
 static void	dup_queue_file __P((ENVELOPE *, ENVELOPE *, int));
@@ -31,6 +35,9 @@
 static char	*hostsignature __P((MAILER *, char *));
 
 #if SMTP
+# if STARTTLS
+static int	starttls __P((MAILER *, MCI *, ENVELOPE *));
+# endif /* STARTTLS */
 #endif /* SMTP */
 
 /*
@@ -128,21 +135,31 @@
 
 	if (e->e_hopcount > MaxHopCount)
 	{
+		char *recip;
+
+		if (e->e_sendqueue != NULL &&
+		    e->e_sendqueue->q_paddr != NULL)
+			recip = e->e_sendqueue->q_paddr;
+		else
+			recip = "(nobody)";
+
 		errno = 0;
 #if QUEUE
 		queueup(e, mode == SM_QUEUE || mode == SM_DEFER);
 #endif /* QUEUE */
 		e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
 		ExitStat = EX_UNAVAILABLE;
-		syserr("554 5.0.0 Too many hops %d (%d max): from %s via %s, to %s",
-			e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
-			RealHostName == NULL ? "localhost" : RealHostName,
-			e->e_sendqueue->q_paddr);
+		syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s",
+		       e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
+		       RealHostName == NULL ? "localhost" : RealHostName,
+		       recip);
 		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
 		{
 			if (QS_IS_DEAD(q->q_state))
 				continue;
+			q->q_state = QS_BADADDR;
 			q->q_status = "5.4.6";
+			q->q_rstatus = "554 5.4.6 Too many hops";
 		}
 		return;
 	}
@@ -455,17 +472,19 @@
 	if (!somedeliveries && mode != SM_QUEUE && mode != SM_DEFER &&
 	    mode != SM_VERIFY)
 	{
+		time_t now = curtime();
+
 		if (tTd(13, 29))
 			dprintf("No deliveries: auto-queuing\n");
 		mode = SM_QUEUE;
 
 		/* treat this as a delivery in terms of counting tries */
-		e->e_dtime = curtime();
+		e->e_dtime = now;
 		if (!expensive)
 			e->e_ntries++;
 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
 		{
-			ee->e_dtime = curtime();
+			ee->e_dtime = now;
 			if (!expensive)
 				ee->e_ntries++;
 		}
@@ -476,10 +495,16 @@
 	     (mode != SM_VERIFY && SuperSafe)) &&
 	    (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL))
 	{
-		/* be sure everything is instantiated in the queue */
-		queueup(e, mode == SM_QUEUE || mode == SM_DEFER);
+		/*
+		**  Be sure everything is instantiated in the queue.
+		**  Split envelopes first in case the machine crashes.
+		**  If the original were done first, we may lose
+		**  recipients.
+		*/
+
 		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);
 	}
 #endif /* QUEUE */
 
@@ -619,6 +644,20 @@
 			return;
 		}
 
+		/* Reset global flags */
+		RestartRequest = NULL;
+		ShutdownRequest = NULL;
+		PendingSignal = 0;
+
+		/*
+		**  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);
+
 		/* double fork to avoid zombies */
 		pid = fork();
 		if (pid > 0)
@@ -779,7 +818,8 @@
 			**  Checkpoint the send list every few addresses
 			*/
 
-			if (e->e_nsent >= CheckpointInterval)
+			if (CheckpointInterval > 0 &&
+			    e->e_nsent >= CheckpointInterval)
 			{
 				queueup(e, FALSE);
 				e->e_nsent = 0;
@@ -905,7 +945,7 @@
 **		returns twice, once in parent and once in child.
 */
 
-int
+pid_t
 dofork()
 {
 	register pid_t pid = -1;
@@ -975,11 +1015,18 @@
 	bool anyok;			/* at least one address was OK */
 	bool goodmxfound = FALSE;	/* at least one MX was OK */
 	bool ovr;
+#if _FFR_DYNAMIC_TOBUF
+	int strsize;
+	int rcptcount;
+	static int tobufsize = 0;
+	static char *tobuf = NULL;
+#else /* _FFR_DYNAMIC_TOBUF */
+	char tobuf[TOBUFSIZE];		/* text line of to people */
+#endif /* _FFR_DYNAMIC_TOBUF */
 	int mpvect[2];
 	int rpvect[2];
 	char *mxhosts[MAXMXHOSTS + 1];
 	char *pv[MAXPV + 1];
-	char tobuf[TOBUFSIZE];		/* text line of to people */
 	char buf[MAXNAME + 1];
 	char rpathbuf[MAXNAME + 1];	/* translated return path */
 
@@ -1099,7 +1146,7 @@
 
 	if (*mvp == NULL)
 	{
-		/* running SMTP */
+		/* running LMTP or SMTP */
 #if SMTP
 		clever = TRUE;
 		*pvp = NULL;
@@ -1109,6 +1156,14 @@
 		return EX_SOFTWARE;
 #endif /* SMTP */
 	}
+	else if (bitnset(M_LMTP, m->m_flags))
+	{
+		/* not running LMTP */
+		sm_syslog(LOG_ERR, NULL,
+			  "Warning: mailer %s: LMTP flag (F=z) turned off",
+			  m->m_name);
+		clrbitn(M_LMTP, m->m_flags);
+	}
 
 	/*
 	**  At this point *mvp points to the argument with $u.  We
@@ -1117,15 +1172,27 @@
 	**  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);
 	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) ||
@@ -1135,8 +1202,17 @@
 			continue;
 
 		/* avoid overflowing tobuf */
+#if _FFR_DYNAMIC_TOBUF
+		strsize += strlen(to->q_paddr) + 1;
+		if (!clever && 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))
 		{
@@ -1160,9 +1236,11 @@
 		/*
 		**  Check to see that these people are allowed to
 		**  talk to each other.
+		**  Check also for overflow of e_msgsize.
 		*/
 
-		if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize)
+		if (m->m_maxsize != 0 &&
+		    (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0))
 		{
 			e->e_flags |= EF_NO_BODY_RETN;
 			if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags))
@@ -1179,13 +1257,13 @@
 			continue;
 		}
 #if NAMED_BIND
-		h_errno = 0;
+		SM_SET_H_ERRNO(0);
 #endif /* NAMED_BIND */
 
 		ovr = TRUE;
 		/* do config file checking of compatibility */
 		rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr,
-				e, TRUE, TRUE, 4);
+				e, TRUE, TRUE, 4, NULL);
 		if (rcode == EX_OK)
 		{
 			/* do in-code checking if not discarding */
@@ -1306,9 +1384,14 @@
 		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 */
 		p = to->q_home;
 		if (p == NULL && ctladdr != NULL)
@@ -1358,13 +1441,44 @@
 	}
 
 	/* 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;
 	}
 
 	/* print out messages as full list */
+#if _FFR_DYNAMIC_TOBUF
+	{
+		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);
+		}
+	}
+#endif /* _FFR_DYNAMIC_TOBUF */
 	e->e_to = tobuf + 1;
 
 	/*
@@ -1406,7 +1520,7 @@
 	}
 	errno = 0;
 #if NAMED_BIND
-	h_errno = 0;
+	SM_SET_H_ERRNO(0);
 #endif /* NAMED_BIND */
 
 	CurHostName = NULL;
@@ -1508,7 +1622,7 @@
 # endif /* NETUNIX */
 		    )
 		{
-			port = htons(atoi(pv[2]));
+			port = htons((u_short)atoi(pv[2]));
 			if (port == 0)
 			{
 # ifdef NO_GETSERVBYNAME
@@ -1619,10 +1733,10 @@
 						m->m_name);
 				i = makeconnection(hostbuf, port, mci, e);
 			}
+			mci->mci_errno = errno;
 			mci->mci_lastuse = curtime();
 			mci->mci_deliveries = 0;
 			mci->mci_exitstat = i;
-			mci->mci_errno = errno;
 # if NAMED_BIND
 			mci->mci_herrno = h_errno;
 # endif /* NAMED_BIND */
@@ -1784,8 +1898,11 @@
 			(void) fflush(e->e_xfp);	/* for debugging */
 		(void) fflush(stdout);
 		(void) setsignal(SIGCHLD, SIG_DFL);
+
+
 		DOFORK(FORK);
 		/* pid is set by DOFORK */
+
 		if (pid < 0)
 		{
 			/* failure */
@@ -1810,6 +1927,11 @@
 			struct stat stb;
 			extern int DtableSize;
 
+			/* Reset global flags */
+			RestartRequest = NULL;
+			ShutdownRequest = NULL;
+			PendingSignal = 0;
+
 			if (e->e_lockfp != NULL)
 				(void) close(fileno(e->e_lockfp));
 
@@ -1821,7 +1943,7 @@
 			if (m != FileMailer || stat(tochain->q_user, &stb) < 0)
 				stb.st_mode = 0;
 
-#if HASSETUSERCONTEXT
+# if HASSETUSERCONTEXT
 			/*
 			**  Set user resources.
 			*/
@@ -1839,7 +1961,7 @@
 						pwd, pwd->pw_uid,
 						LOGIN_SETRESOURCES|LOGIN_SETPRIORITY);
 			}
-#endif /* HASSETUSERCONTEXT */
+# endif /* HASSETUSERCONTEXT */
 
 			/* tweak niceness */
 			if (m->m_nice != 0)
@@ -1860,8 +1982,11 @@
 						u = ctladdr->q_user;
 
 					if (initgroups(u, ctladdr->q_gid) == -1 && suidwarn)
+					{
 						syserr("openmailer: initgroups(%s, %d) failed",
 							u, ctladdr->q_gid);
+						exit(EX_TEMPFAIL);
+					}
 				}
 				else
 				{
@@ -1869,7 +1994,10 @@
 
 					gidset[0] = ctladdr->q_gid;
 					if (setgroups(1, gidset) == -1 && suidwarn)
+					{
 						syserr("openmailer: setgroups() failed");
+						exit(EX_TEMPFAIL);
+					}
 				}
 				new_gid = ctladdr->q_gid;
 			}
@@ -1878,8 +2006,11 @@
 				if (!DontInitGroups)
 				{
 					if (initgroups(DefUser, DefGid) == -1 && suidwarn)
+					{
 						syserr("openmailer: initgroups(%s, %d) failed",
 							DefUser, DefGid);
+						exit(EX_TEMPFAIL);
+					}
 				}
 				else
 				{
@@ -1887,16 +2018,35 @@
 
 					gidset[0] = DefGid;
 					if (setgroups(1, gidset) == -1 && suidwarn)
+					{
 						syserr("openmailer: setgroups() failed");
+						exit(EX_TEMPFAIL);
+					}
 				}
 				if (m->m_gid == 0)
 					new_gid = DefGid;
 				else
 					new_gid = m->m_gid;
+			}
+			if (new_gid != NO_GID)
+			{
+				if (RunAsUid != 0 &&
+				    bitnset(M_SPECIFIC_UID, m->m_flags) &&
+				    new_gid != getgid() &&
+				    new_gid != getegid())
+				{
+					/* Only root can change the gid */
+					syserr("openmailer: insufficient privileges to change gid");
+					exit(EX_TEMPFAIL);
+				}
+
+				if (setgid(new_gid) < 0 && suidwarn)
+				{
+					syserr("openmailer: setgid(%ld) failed",
+					       (long) new_gid);
+					exit(EX_TEMPFAIL);
+				}
 			}
-			if (new_gid != NO_GID && setgid(new_gid) < 0 && suidwarn)
-				syserr("openmailer: setgid(%ld) failed",
-					(long) new_gid);
 
 			/* change root to some "safe" directory */
 			if (m->m_rootdir != NULL)
@@ -1906,10 +2056,16 @@
 					dprintf("openmailer: chroot %s\n",
 						buf);
 				if (chroot(buf) < 0)
+				{
 					syserr("openmailer: Cannot chroot(%s)",
 					       buf);
+					exit(EX_TEMPFAIL);
+				}
 				if (chdir("/") < 0)
+				{
 					syserr("openmailer: cannot chdir(/)");
+					exit(EX_TEMPFAIL);
+				}
 			}
 
 			/* reset user id */
@@ -1926,29 +2082,48 @@
 				new_ruid = DefUid;
 			if (new_euid != NO_UID)
 			{
+				if (RunAsUid != 0 && new_euid != RunAsUid)
+				{
+					/* Only root can change the uid */
+					syserr("openmailer: insufficient privileges to change uid");
+					exit(EX_TEMPFAIL);
+				}
+
 				vendor_set_uid(new_euid);
-#if MAILER_SETUID_METHOD == USE_SETEUID
+# if MAILER_SETUID_METHOD == USE_SETEUID
 				if (seteuid(new_euid) < 0 && suidwarn)
+				{
 					syserr("openmailer: seteuid(%ld) failed",
 						(long) new_euid);
-#endif /* MAILER_SETUID_METHOD == USE_SETEUID */
-#if MAILER_SETUID_METHOD == USE_SETREUID
+					exit(EX_TEMPFAIL);
+				}
+# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
+# if MAILER_SETUID_METHOD == USE_SETREUID
 				if (setreuid(new_ruid, new_euid) < 0 && suidwarn)
+				{
 					syserr("openmailer: setreuid(%ld, %ld) failed",
 						(long) new_ruid, (long) new_euid);
-#endif /* MAILER_SETUID_METHOD == USE_SETREUID */
-#if MAILER_SETUID_METHOD == USE_SETUID
+					exit(EX_TEMPFAIL);
+				}
+# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
+# if MAILER_SETUID_METHOD == USE_SETUID
 				if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn)
+				{
 					syserr("openmailer: setuid(%ld) failed",
 						(long) new_euid);
-#endif /* MAILER_SETUID_METHOD == USE_SETUID */
+					exit(EX_TEMPFAIL);
+				}
+# endif /* MAILER_SETUID_METHOD == USE_SETUID */
 			}
 			else if (new_ruid != NO_UID)
 			{
 				vendor_set_uid(new_ruid);
 				if (setuid(new_ruid) < 0 && suidwarn)
+				{
 					syserr("openmailer: setuid(%ld) failed",
 						(long) new_ruid);
+					exit(EX_TEMPFAIL);
+				}
 			}
 
 			if (tTd(11, 2))
@@ -2092,23 +2267,248 @@
 #if SMTP
 	if (clever && mci->mci_state != MCIS_CLOSED)
 	{
-		static u_short again = 0;
-# define ONLY_HELO_B	0x04
-# define ONLY_HELO	bitset(ONLY_HELO_B, again)
-# define SET_HELO	again |= ONLY_HELO_B
-# define CLR_HELO	again &= ~ONLY_HELO_B
+# if SASL && SFIO
+#  define DONE_AUTH(f)		bitset(MCIF_AUTHACT, f)
+# endif /* SASL && SFIO */
+# if STARTTLS
+#  define DONE_STARTTLS(f)	bitset(MCIF_TLSACT, f)
+# endif /* STARTTLS */
+# define ONLY_HELO(f)		bitset(MCIF_ONLY_EHLO, f)
+# define SET_HELO(f)		f |= MCIF_ONLY_EHLO
+# define CLR_HELO(f)		f &= ~MCIF_ONLY_EHLO
+
 
+# if STARTTLS || (SASL && SFIO)
+reconnect:	/* after switching to an authenticated connection */
+# endif /* STARTTLS || (SASL && SFIO) */
 
 # if SASL
 		mci->mci_saslcap = NULL;
 # endif /* SASL */
-		smtpinit(m, mci, e, ONLY_HELO);
-		CLR_HELO;
+		smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags));
+		CLR_HELO(mci->mci_flags);
+
+# 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 (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
+				olderrors = Errors;
+				QuickAbort = FALSE;
+				SuprErrs = TRUE;
+				if (rscheck("try_tls", srvname, NULL,
+					    e, TRUE, FALSE, 8, host) != EX_OK
+				    || Errors > olderrors)
+					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)
+				{
+					/* start again without STARTTLS */
+					mci->mci_flags |= MCIF_TLSACT;
+				}
+				else
+				{
+					char *s;
+
+					/*
+					**  TLS negotation failed, what to do?
+					**  fall back to unencrypted connection
+					**  or abort? How to decide?
+					**  set a macro and call a ruleset.
+					*/
+					mci->mci_flags &= ~MCIF_TLS;
+					switch (rcode)
+					{
+					  case EX_TEMPFAIL:
+						s = "TEMP";
+						break;
+					  case EX_USAGE:
+						s = "USAGE";
+						break;
+					  case EX_PROTOCOL:
+						s = "PROTOCOL";
+						break;
+					  case EX_SOFTWARE:
+						s = "SOFTWARE";
+						break;
+
+					  /* everything else is a failure */
+					  default:
+						s = "FAILURE";
+						rcode = EX_TEMPFAIL;
+					}
+					define(macid("{verify}", NULL),
+					       newstr(s), e);
+				}
+			}
+			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);
+			olderrors = Errors;
+			QuickAbort = FALSE;
+			SuprErrs = TRUE;
+
+			/*
+			**  rcode == EX_SOFTWARE is special:
+			**  the TLS negotation failed
+			**  we have to drop the connection no matter what
+			**  However, we call tls_server to give it the chance
+			**  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 ||
+			    Errors > olderrors ||
+			    rcode == EX_SOFTWARE)
+			{
+				char enhsc[ENHSCLEN];
+				extern char MsgBuf[];
+
+				if (ISSMTPCODE(MsgBuf) &&
+				    extenhsc(MsgBuf + 4, ' ', enhsc) > 0)
+				{
+					p = newstr(MsgBuf);
+				}
+				else
+				{
+					p = "403 4.7.0 server not authenticated.";
+					(void) strlcpy(enhsc, "4.7.0",
+						       sizeof enhsc);
+				}
+				SuprErrs = saveSuprErrs;
+				QuickAbort = saveQuickAbort;
+
+				if (rcode == EX_SOFTWARE)
+				{
+					/* drop the connection */
+					mci->mci_state = MCIS_QUITING;
+					if (mci->mci_in != NULL)
+					{
+						(void) fclose(mci->mci_in);
+						mci->mci_in = NULL;
+					}
+					mci->mci_flags &= ~MCIF_TLSACT;
+					(void) endmailer(mci, e, pv);
+				}
+				else
+				{
+					/* abort transfer */
+					smtpquit(m, mci, e);
+				}
+
+				/* avoid bogus error msg */
+				mci->mci_errno = 0;
+
+				/* temp or permanent failure? */
+				rcode = (*p == '4') ? EX_TEMPFAIL
+						    : EX_UNAVAILABLE;
+				mci_setstat(mci, rcode, newstr(enhsc), p);
+
+				/*
+				**  hack to get the error message into
+				**  the envelope (done in giveresponse())
+				*/
+				(void) strlcpy(SmtpError, p, sizeof SmtpError);
+			}
+			QuickAbort = saveQuickAbort;
+			SuprErrs = saveSuprErrs;
+			if (DONE_STARTTLS(mci->mci_flags) &&
+			    mci->mci_state != MCIS_CLOSED)
+			{
+				SET_HELO(mci->mci_flags);
+				mci->mci_flags &= ~MCIF_EXTENS;
+				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)
 		{
 			/*
@@ -2117,6 +2517,49 @@
 			*/
 			if (smtpauth(m, mci, e) == EX_OK)
 			{
+#  if SFIO
+				int result;
+				sasl_ssf_t *ssf;
+
+				/* get security strength (features) */
+				result = sasl_getprop(mci->mci_conn, SASL_SSF,
+						      (void **) &ssf);
+				if (LogLevel > 9)
+					sm_syslog(LOG_INFO, NOQID,
+						  "SASL: outgoing connection to %.64s: mech=%.16s, bits=%d",
+						  mci->mci_host,
+						  macvalue(macid("{auth_type}",
+								 NULL), e),
+						  result == SASL_OK ? *ssf
+						  		    : 0);
+
+				/*
+				**  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
+					*/
+					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;
+						goto reconnect;
+					}
+					syserr("SASL TLS switch failed in client");
+				}
+				/* else? XXX */
+#  endif /* SFIO */
 				mci->mci_flags |= MCIF_AUTHACT;
 
 			}
@@ -2163,7 +2606,7 @@
 		rcode = mci->mci_exitstat;
 		errno = mci->mci_errno;
 #if NAMED_BIND
-		h_errno = mci->mci_herrno;
+		SM_SET_H_ERRNO(mci->mci_herrno);
 #endif /* NAMED_BIND */
 		if (rcode == EX_OK)
 		{
@@ -2194,6 +2637,18 @@
 
 		/* get the exit status */
 		rcode = endmailer(mci, e, pv);
+		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);
+		}
 	}
 	else
 #if SMTP
@@ -2210,16 +2665,34 @@
 
 			/* send the recipient list */
 			tobuf[0] = '\0';
+
 			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 */
 					continue;
 				}
+#endif /* !_FFR_DYNAMIC_TOBUF */
 
+# if STARTTLS
+#  if _FFR_TLS_RCPT
+				i = rscheck("tls_rcpt", to->q_user, NULL, e,
+					    TRUE, TRUE, 4, mci->mci_host);
+				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);
+					continue;
+				}
+#  endif /* _FFR_TLS_RCPT */
+# endif /* STARTTLS */
 
 				if ((i = smtprcpt(to, m, mci, e)) != EX_OK)
 				{
@@ -2306,6 +2779,10 @@
 				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)
 				{
@@ -2318,6 +2795,7 @@
 					(void) strlcat(tobuf, to->q_paddr,
 						       sizeof tobuf);
 				}
+#endif /* _FFR_DYNAMIC_TOBUF */
 				anyok = TRUE;
 			}
 			else
@@ -2353,7 +2831,7 @@
 		**  Checkpoint the send list every few addresses
 		*/
 
-		if (e->e_nsent >= CheckpointInterval)
+		if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval)
 		{
 			queueup(e, FALSE);
 			e->e_nsent = 0;
@@ -2427,6 +2905,7 @@
 
 	errno = 0;
 	define('g', (char *) NULL, e);
+	e->e_to = NULL;
 	return rcode;
 }
 
@@ -2583,6 +3062,12 @@
 static void
 endwaittimeout()
 {
+	/*
+	**  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(EndWaitTimeout, 1);
 }
@@ -2598,31 +3083,36 @@
 	char buf[MAXLINE];
 	EVENT *ev = NULL;
 
-	mci_unlock_host(mci);
-
-#if SASL
-	/* shutdown SASL */
-	if (bitset(MCIF_AUTHACT, mci->mci_flags))
-	{
-		sasl_dispose(&mci->mci_conn);
-		mci->mci_flags &= ~MCIF_AUTHACT;
-	}
-#endif /* SASL */
 
+	mci_unlock_host(mci);
 
 	/* close output to mailer */
 	if (mci->mci_out != NULL)
 		(void) fclose(mci->mci_out);
 
 	/* copy any remaining input to transcript */
-	if (mci->mci_in != NULL && e->e_xfp != NULL)
+	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)
-		/* while (fgets(buf, sizeof buf, mci->mci_in) != NULL) */
 			(void) fputs(buf, e->e_xfp);
 	}
 
+#if SASL
+	/* shutdown SASL */
+	if (bitset(MCIF_AUTHACT, mci->mci_flags))
+	{
+		sasl_dispose(&mci->mci_conn);
+		mci->mci_flags &= ~MCIF_AUTHACT;
+	}
+#endif /* SASL */
+
+#if STARTTLS
+	/* shutdown TLS */
+	(void) endtlsclt(mci);
+#endif /* STARTTLS */
+
 	/* now close the input */
 	if (mci->mci_in != NULL)
 		(void) fclose(mci->mci_in);
@@ -2643,9 +3133,9 @@
 				      endwaittimeout, 0);
 		else
 		{
-			syserr("endmailer %s: wait timeout (%d)",
+			syserr("endmailer %s: wait timeout (%ld)",
 			       mci->mci_mailer->m_name,
-			       mci->mci_mailer->m_wait);
+			       (long) mci->mci_mailer->m_wait);
 			return EX_TEMPFAIL;
 		}
 	}
@@ -2912,12 +3402,12 @@
 	if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL))
 	{
 		if (e->e_message != NULL)
-			free(e->e_message);
+			sm_free(e->e_message);
 		e->e_message = newstr(statmsg + off);
 	}
 	errno = 0;
 #if NAMED_BIND
-	h_errno = 0;
+	SM_SET_H_ERRNO(0);
 #endif /* NAMED_BIND */
 }
 /*
@@ -2958,6 +3448,7 @@
 	register char *bp;
 	register char *p;
 	int l;
+	time_t now;
 	char buf[1024];
 
 #if (SYSLOG_BUFSIZE) >= 256
@@ -2978,14 +3469,15 @@
 	}
 
 	/* delay & xdelay: max 41 bytes */
+	now = curtime();
 	snprintf(bp, SPACELEFT(buf, bp), ", delay=%s",
-		 pintvl(curtime() - e->e_ctime, TRUE));
+		 pintvl(now - e->e_ctime, TRUE));
 	bp += strlen(bp);
 
 	if (xstart != (time_t) 0)
 	{
 		snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s",
-			 pintvl(curtime() - xstart, TRUE));
+			 pintvl(now - xstart, TRUE));
 		bp += strlen(bp);
 	}
 
@@ -3067,16 +3559,32 @@
 	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
 	while (strlen(p) >= (SIZE_T) l)
 	{
-		register char *q = strchr(p + l, ',');
+		register char *q;
 
+#if _FFR_DYNAMIC_TOBUF
+		for (q = p + l; q > p; q--)
+		{
+			if (*q == ',')
+				break;
+		}
+		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",
-			  ++q - p, p, buf);
+			  (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 */
 
@@ -3084,16 +3592,32 @@
 	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
 	while (strlen(p) >= (SIZE_T) l)
 	{
-		register char *q = strchr(p + l, ',');
+		register char *q;
 
+#if _FFR_DYNAMIC_TOBUF
+		for (q = p + l; q > p; q--)
+		{
+			if (*q == ',')
+				break;
+		}
+		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]",
-			  ++q - p, p);
+			  (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)
 	{
@@ -3111,12 +3635,12 @@
 	}
 	bp = buf;
 	snprintf(bp, SPACELEFT(buf, bp), "delay=%s",
-		 pintvl(curtime() - e->e_ctime, TRUE));
+		 pintvl(now - e->e_ctime, TRUE));
 	bp += strlen(bp);
 	if (xstart != (time_t) 0)
 	{
 		snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s",
-			 pintvl(curtime() - xstart, TRUE));
+			 pintvl(now - xstart, TRUE));
 		bp += strlen(bp);
 	}
 
@@ -3271,8 +3795,13 @@
 
 		e->e_dfp = fopen(df, "r");
 		if (e->e_dfp == NULL)
-			syserr("putbody: Cannot open %s for %s from %s",
-				df, e->e_to, e->e_from.q_paddr);
+		{
+			char *msg = "!putbody: Cannot open %s for %s from %s";
+
+			if (errno == ENOENT)
+				msg++;
+			syserr(msg, df, e->e_to, e->e_from.q_paddr);
+		}
 	}
 	if (e->e_dfp == NULL)
 	{
@@ -3284,6 +3813,7 @@
 		putline("<<< No Message Collected >>>", mci);
 		goto endofmessage;
 	}
+
 	if (e->e_dfino == (ino_t) 0)
 	{
 		struct stat stbuf;
@@ -3384,7 +3914,7 @@
 		ostate = OS_HEAD;
 		bp = buf;
 		pbp = peekbuf;
-		while (!ferror(mci->mci_out))
+		while (!ferror(mci->mci_out) && !dead)
 		{
 			if (pbp > peekbuf)
 				c = *--pbp;
@@ -3440,27 +3970,39 @@
 						(void) putc(padc,
 							    TrafficLogFile);
 					for (xp = buf; xp < bp; xp++)
-						(void) putc(*xp, TrafficLogFile);
+						(void) putc((unsigned char) *xp,
+							    TrafficLogFile);
 					if (c == '\n')
 						(void) fputs(mci->mci_mailer->m_eol,
-						      TrafficLogFile);
+							     TrafficLogFile);
 				}
 				if (padc != EOF)
 				{
 					if (putc(padc, mci->mci_out) == EOF)
+					{
+						dead = TRUE;
 						continue;
+					}
+					else
+					{
+						/* record progress for DATA timeout */
+						DataProgress = TRUE;
+					}
 					pos++;
 				}
 				for (xp = buf; xp < bp; xp++)
 				{
-					if (putc(*xp, mci->mci_out) == EOF)
+					if (putc((unsigned char) *xp,
+						 mci->mci_out) == EOF)
 					{
 						dead = TRUE;
 						break;
 					}
-
-					/* record progress for DATA timeout */
-					DataProgress = TRUE;
+					else
+					{
+						/* record progress for DATA timeout */
+						DataProgress = TRUE;
+					}
 				}
 				if (dead)
 					continue;
@@ -3469,6 +4011,11 @@
 					if (fputs(mci->mci_mailer->m_eol,
 						  mci->mci_out) == EOF)
 						break;
+					else
+					{
+						/* record progress for DATA timeout */
+						DataProgress = TRUE;
+					}
 					pos = 0;
 				}
 				else
@@ -3478,8 +4025,6 @@
 						*pbp++ = c;
 				}
 
-				/* record progress for DATA timeout */
-				DataProgress = TRUE;
 				bp = buf;
 
 				/* determine next state */
@@ -3498,9 +4043,11 @@
 					if (fputs(mci->mci_mailer->m_eol,
 						  mci->mci_out) == EOF)
 						continue;
-
-					/* record progress for DATA timeout */
-					DataProgress = TRUE;
+					else
+					{
+						/* record progress for DATA timeout */
+						DataProgress = TRUE;
+					}
 
 					if (TrafficLogFile != NULL)
 					{
@@ -3544,9 +4091,19 @@
 					if (d == '\n' || d == EOF)
 					{
 						if (TrafficLogFile != NULL)
-							(void) putc(c, TrafficLogFile);
-						if (putc(c, mci->mci_out) == EOF)
+							(void) putc((unsigned char) c,
+							    TrafficLogFile);
+						if (putc((unsigned char) c,
+							 mci->mci_out) == EOF)
+						{
+							dead = TRUE;
 							continue;
+						}
+						else
+						{
+							/* record progress for DATA timeout */
+							DataProgress = TRUE;
+						}
 						pos++;
 						continue;
 					}
@@ -3554,10 +4111,15 @@
 					if (putc('!', mci->mci_out) == EOF ||
 					    fputs(mci->mci_mailer->m_eol,
 						  mci->mci_out) == EOF)
+					{
+						dead = TRUE;
 						continue;
-
-					/* record progress for DATA timeout */
-					DataProgress = TRUE;
+					}
+					else
+					{
+						/* record progress for DATA timeout */
+						DataProgress = TRUE;
+					}
 
 					if (TrafficLogFile != NULL)
 					{
@@ -3576,21 +4138,33 @@
 					if (fputs(mci->mci_mailer->m_eol,
 						  mci->mci_out) == EOF)
 						continue;
+					else
+					{
+						/* record progress for DATA timeout */
+						DataProgress = TRUE;
+					}
 					pos = 0;
 					ostate = OS_HEAD;
 				}
 				else
 				{
 					if (TrafficLogFile != NULL)
-						(void) putc(c, TrafficLogFile);
-					if (putc(c, mci->mci_out) == EOF)
+						(void) putc((unsigned char) c,
+							    TrafficLogFile);
+					if (putc((unsigned char) c,
+						 mci->mci_out) == EOF)
+					{
+						dead = TRUE;
 						continue;
+					}
+					else
+					{
+						/* record progress for DATA timeout */
+						DataProgress = TRUE;
+					}
 					pos++;
 					ostate = OS_INLINE;
 				}
-
-				/* record progress for DATA timeout */
-				DataProgress = TRUE;
 				break;
 			}
 		}
@@ -3601,18 +4175,22 @@
 			if (TrafficLogFile != NULL)
 			{
 				for (xp = buf; xp < bp; xp++)
-					(void) putc(*xp, TrafficLogFile);
+					(void) putc((unsigned char) *xp,
+						    TrafficLogFile);
 			}
 			for (xp = buf; xp < bp; xp++)
 			{
-				if (putc(*xp, mci->mci_out) == EOF)
+				if (putc((unsigned char) *xp, mci->mci_out) ==
+				    EOF)
 				{
 					dead = TRUE;
 					break;
 				}
-
-				/* record progress for DATA timeout */
-				DataProgress = TRUE;
+				else
+				{
+					/* record progress for DATA timeout */
+					DataProgress = TRUE;
+				}
 			}
 			pos += bp - buf;
 		}
@@ -3651,7 +4229,7 @@
 		(void) bfrewind(e->e_dfp);
 
 	/* some mailers want extra blank line at end of message */
-	if (bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
+	if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
 	    buf[0] != '\0' && buf[0] != '\n')
 		putline("", mci);
 
@@ -3824,6 +4402,11 @@
 		int err;
 		volatile int oflags = O_WRONLY|O_APPEND;
 
+		/* Reset global flags */
+		RestartRequest = NULL;
+		ShutdownRequest = NULL;
+		PendingSignal = 0;
+
 		if (e->e_lockfp != NULL)
 			(void) close(fileno(e->e_lockfp));
 
@@ -3840,7 +4423,8 @@
 		}
 
 		if (TimeOuts.to_fileopen > 0)
-			ev = setevent(TimeOuts.to_fileopen, mailfiletimeout, 0);
+			ev = setevent(TimeOuts.to_fileopen,
+				      mailfiletimeout, 0);
 		else
 			ev = NULL;
 
@@ -3884,6 +4468,12 @@
 			{
 				RealUserName = NULL;
 				RealUid = mailer->m_uid;
+				if (RunAsUid != 0 && RealUid != RunAsUid)
+				{
+					/* Only root can change the uid */
+					syserr("mailfile: insufficient privileges to change uid");
+					exit(EX_TEMPFAIL);
+				}
 			}
 			else if (bitset(S_ISUID, mode))
 			{
@@ -3911,15 +4501,34 @@
 
 			/* select a new group to run as */
 			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
+			{
 				RealGid = mailer->m_gid;
+				if (RunAsUid != 0 &&
+				    (RealGid != getgid() ||
+				     RealGid != getegid()))
+				{
+					/* Only root can change the gid */
+					syserr("mailfile: insufficient privileges to change gid");
+					exit(EX_TEMPFAIL);
+				}
+			}
 			else if (bitset(S_ISGID, mode))
 				RealGid = stb.st_gid;
-			else if (ctladdr != NULL && ctladdr->q_uid != 0)
-				RealGid = ctladdr->q_gid;
 			else if (ctladdr != NULL &&
 				 ctladdr->q_uid == DefUid &&
 				 ctladdr->q_gid == 0)
+			{
+				/*
+				**  Special case:  This means it is an
+				**  alias and we should act as DefaultUser.
+				**  See alias()'s comments.
+				*/
+
 				RealGid = DefGid;
+				RealUserName = DefUser;
+			}
+			else if (ctladdr != NULL && ctladdr->q_uid != 0)
+				RealGid = ctladdr->q_gid;
 			else if (mailer != NULL && mailer->m_gid != 0)
 				RealGid = mailer->m_gid;
 			else
@@ -3939,8 +4548,11 @@
 		if (RealUserName != NULL && !DontInitGroups)
 		{
 			if (initgroups(RealUserName, RealGid) == -1 && suidwarn)
+			{
 				syserr("mailfile: initgroups(%s, %d) failed",
 					RealUserName, RealGid);
+				exit(EX_TEMPFAIL);
+			}
 		}
 		else
 		{
@@ -3948,7 +4560,10 @@
 
 			gidset[0] = RealGid;
 			if (setgroups(1, gidset) == -1 && suidwarn)
+			{
 				syserr("mailfile: setgroups() failed");
+				exit(EX_TEMPFAIL);
+			}
 		}
 
 		/*
@@ -3973,15 +4588,24 @@
 			dprintf("mailfile: deliver to %s\n", realfile);
 
 		if (chdir("/") < 0)
+		{
 			syserr("mailfile: cannot chdir(/)");
+			exit(EX_CANTCREAT);
+		}
 
 		/* now reset the group and user ids */
 		endpwent();
 		if (setgid(RealGid) < 0 && suidwarn)
+		{
 			syserr("mailfile: setgid(%ld) failed", (long) RealGid);
+			exit(EX_TEMPFAIL);
+		}
 		vendor_set_uid(RealUid);
 		if (setuid(RealUid) < 0 && suidwarn)
+		{
 			syserr("mailfile: setuid(%ld) failed", (long) RealUid);
+			exit(EX_TEMPFAIL);
+		}
 
 		if (tTd(11, 2))
 			dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n",
@@ -4114,7 +4738,7 @@
 		(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER);
 		(*e->e_putbody)(&mcibuf, e, NULL);
 		putline("\n", &mcibuf);
-		if (fflush(f) < 0 ||
+		if (fflush(f) != 0 ||
 		    (SuperSafe && fsync(fileno(f)) < 0) ||
 		    ferror(f))
 		{
@@ -4164,6 +4788,13 @@
 static void
 mailfiletimeout()
 {
+	/*
+	**  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(CtxMailfileTimeout, 1);
 }
 /*
@@ -4199,6 +4830,7 @@
 	int len;
 	int nmx;
 	int hl;
+	time_t now;
 	char *hp;
 	char *endp;
 	int oldoptions = _res.options;
@@ -4210,17 +4842,19 @@
 		dprintf("hostsignature(%s)\n", host);
 
 	/*
-	**  If local delivery, just return a constant.
+	**  If local delivery (and not remote), just return a constant.
 	*/
 
-	if (bitnset(M_LOCALMAILER, m->m_flags))
+	p = m->m_mailer;
+	if (bitnset(M_LOCALMAILER, m->m_flags) &&
+	    strcmp(p, "[IPC]") != 0 &&
+	    strcmp(p, "[TCP]") != 0)
 		return "localhost";
 
 	/*
 	**  Check to see if this uses IPC -- if not, it can't have MX records.
 	*/
 
-	p = m->m_mailer;
 	if (strcmp(p, "[IPC]") != 0 &&
 	    strcmp(p, "[TCP]") != 0)
 	{
@@ -4257,6 +4891,7 @@
 	if (ConfigLevel < 2)
 		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
 
+	now = curtime();
 	for (hp = host; hp != NULL; hp = endp)
 	{
 #if NETINET6
@@ -4296,7 +4931,7 @@
 				mci = mci_get(hp, m);
 				mci->mci_errno = errno;
 				mci->mci_herrno = h_errno;
-				mci->mci_lastuse = curtime();
+				mci->mci_lastuse = now;
 				if (rcode == EX_NOHOST)
 					mci_setstat(mci, rcode, "5.1.2",
 						"550 Host unknown");
@@ -4327,7 +4962,7 @@
 		if (s->s_hostsig != NULL)
 		{
 			(void) strlcpy(p, s->s_hostsig, len);
-			free(s->s_hostsig);
+			sm_free(s->s_hostsig);
 			s->s_hostsig = p;
 			hl = strlen(p);
 			p += hl;
@@ -4496,4 +5131,206 @@
 }
 
 #if SMTP
+# if STARTTLS
+static SSL_CTX	*clt_ctx = NULL;
+
+/*
+**  INITCLTTLS -- initialize client side TLS
+**
+**	Parameters:
+**		none.
+**
+**	Returns:
+**		succeeded?
+*/
+
+bool
+initclttls()
+{
+	if (clt_ctx != NULL)
+		return TRUE;	/* already done */
+	return inittls(&clt_ctx, TLS_I_CLT, FALSE, CltCERTfile, Cltkeyfile,
+		       CACERTpath, CACERTfile, DHParams);
+}
+
+/*
+**  STARTTLS -- try to start secure connection (client side)
+**
+**	Parameters:
+**		m -- the mailer.
+**		mci -- the mailer connection info.
+**		e -- the envelope.
+**
+**	Returns:
+**		success?
+**		(maybe this should be some other code than EX_
+**		that denotes which stage failed.)
+*/
+
+static int
+starttls(m, mci, e)
+	MAILER *m;
+	MCI *mci;
+	ENVELOPE *e;
+{
+	int smtpresult;
+	int result = 0;
+	int rfd, wfd;
+	SSL *clt_ssl = NULL;
+
+	if (clt_ctx == NULL && !initclttls())
+		return EX_TEMPFAIL;
+	smtpmessage("STARTTLS", m, mci);
+
+	/* get the reply */
+	smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, NULL, NULL);
+	/* which timeout? XXX */
+
+	/* check return code from server */
+	if (smtpresult == 454)
+		return EX_TEMPFAIL;
+	if (smtpresult == 501)
+		return EX_USAGE;
+	if (smtpresult == -1)
+		return smtpresult;
+	if (smtpresult != 220)
+		return EX_PROTOCOL;
+
+	if (LogLevel > 13)
+		sm_syslog(LOG_INFO, e->e_id, "TLS: start client");
+
+	/* 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");
+			if (LogLevel > 9)
+				tlslogerr();
+		}
+		return EX_SOFTWARE;
+	}
+
+	rfd = fileno(mci->mci_in);
+	wfd = fileno(mci->mci_out);
+
+	/* 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)
+	{
+		if (LogLevel > 5)
+		{
+			sm_syslog(LOG_ERR, e->e_id,
+				  "TLS: error: SSL_set_xfd failed=%d", result);
+			if (LogLevel > 9)
+				tlslogerr();
+		}
+		return EX_SOFTWARE;
+	}
+	SSL_set_connect_state(clt_ssl);
+	if ((result = SSL_connect(clt_ssl)) <= 0)
+	{
+		int i;
+
+		/* what to do in this case? */
+		i = SSL_get_error(clt_ssl, result);
+		if (LogLevel > 5)
+		{
+			sm_syslog(LOG_ERR, e->e_id,
+				  "TLS: error: SSL_connect failed=%d (%d)",
+				  result, i);
+			if (LogLevel > 9)
+				tlslogerr();
+		}
+		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);
+
+	/* 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
+	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)
+**
+**	Parameters:
+**		mci -- the mailer connection info.
+**
+**	Returns:
+**		success?
+*/
+int
+endtlsclt(mci)
+	MCI *mci;
+{
+	int r;
+
+	if (!bitset(MCIF_TLSACT, mci->mci_flags))
+		return EX_OK;
+	r = endtls(mci->mci_ssl, "client");
+	mci->mci_flags &= ~MCIF_TLSACT;
+	return r;
+}
+/*
+**  ENDTLS -- shutdown secure connection
+**
+**	Parameters:
+**		ssl -- SSL connection information.
+**		side -- srv/clt (for logging).
+**
+**	Returns:
+**		success?
+*/
+
+int
+endtls(ssl, side)
+	SSL *ssl;
+	char *side;
+{
+	int ret = EX_OK;
+
+	if (ssl != NULL)
+	{
+		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;
+	}
+	return ret;
+}
+# endif /* STARTTLS */
 #endif /* SMTP */
Index: gnu/usr.sbin/sendmail/sendmail/domain.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/domain.c,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- gnu/usr.sbin/sendmail/sendmail/domain.c	2000/04/02 19:05:45	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/domain.c	2001/02/28 02:43:54	1.3
@@ -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) 1986, 1995-1997 Eric P. Allman.  All rights reserved.
  * Copyright (c) 1988, 1993
@@ -15,12 +15,13 @@
 
 #ifndef lint
 # if NAMED_BIND
-static char id[] = "@(#)$Sendmail: domain.c,v 8.114 2000/02/01 05:49:56 gshapiro Exp $ (with name server)";
+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 2000/02/01 05:49:56 gshapiro Exp $ (without name server)";
+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
 
 # include <arpa/inet.h>
@@ -227,12 +228,16 @@
 	hp = (HEADER *)&answer;
 	cp = (u_char *)&answer + HFIXEDSZ;
 	eom = (u_char *)&answer + n;
-	for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ)
+	for (qdcount = ntohs((u_short)hp->qdcount);
+	     qdcount--;
+	     cp += n + QFIXEDSZ)
+	{
 		if ((n = dn_skipname(cp, eom)) < 0)
 			goto punt;
+	}
 	buflen = sizeof(MXHostBuf) - 1;
 	bp = MXHostBuf;
-	ancount = ntohs(hp->ancount);
+	ancount = ntohs((u_short)hp->ancount);
 	while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
 	{
 		if ((n = dn_expand((u_char *)&answer,
@@ -333,14 +338,10 @@
 	if (nmx == 0)
 	{
 punt:
-		if (seenlocal &&
-		    (!TryNullMXList ||
-		     (sm_gethostbyname(host, AF_INET) == NULL
-# if NETINET6
-		      && sm_gethostbyname(host, AF_INET6) == NULL
-# endif /* NETINET6 */
-		      )))
+		if (seenlocal)
 		{
+			struct hostent *h = NULL;
+
 			/*
 			**  If we have deleted all MX entries, this is
 			**  an error -- we should NEVER send to a host that
@@ -353,10 +354,49 @@
 			**  bad idea, but it's up to you....
 			*/
 
-			*rcode = EX_CONFIG;
-			syserr("MX list for %s points back to %s",
-				host, MyHostName);
-			return -1;
+			if (TryNullMXList)
+			{
+				SM_SET_H_ERRNO(0);
+				errno = 0;
+				h = sm_gethostbyname(host, AF_INET);
+				if (h == NULL)
+				{
+					if (errno == ETIMEDOUT ||
+					    h_errno == TRY_AGAIN ||
+					    (errno == ECONNREFUSED &&
+					     UseNameServer))
+					{
+						*rcode = EX_TEMPFAIL;
+						return -1;
+					}
+# if NETINET6
+					SM_SET_H_ERRNO(0);
+					errno = 0;
+					h = sm_gethostbyname(host, AF_INET6);
+					if (h == NULL &&
+					    (errno == ETIMEDOUT ||
+					     h_errno == TRY_AGAIN ||
+					     (errno == ECONNREFUSED &&
+					      UseNameServer)))
+					{
+						*rcode = EX_TEMPFAIL;
+						return -1;
+					}
+# endif /* NETINET6 */
+				}
+			}
+
+			if (h == NULL)
+			{
+				*rcode = EX_CONFIG;
+				syserr("MX list for %s points back to %s",
+				       host, MyHostName);
+				return -1;
+			}
+# if _FFR_FREEHOSTENT && NETINET6
+			freehostent(h);
+			hp = NULL;
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
 		}
 		if (strlen(host) >= (SIZE_T) sizeof MXHostBuf)
 		{
@@ -604,6 +644,8 @@
 		return FALSE;
 	}
 
+	*statp = EX_OK;
+
 	/*
 	**  Initialize domain search list.  If there is at least one
 	**  dot in the name, search the unmodified name first so we
@@ -694,6 +736,7 @@
 				qtype == T_A ? "A" :
 				qtype == T_MX ? "MX" :
 				"???");
+		errno = 0;
 		ret = res_querydomain(host, *dp, C_IN, qtype,
 				      answer.qb2, sizeof(answer.qb2));
 		if (ret <= 0)
@@ -704,15 +747,19 @@
 
 			if (errno == ECONNREFUSED || h_errno == TRY_AGAIN)
 			{
-				/* the name server seems to be down */
-				h_errno = TRY_AGAIN;
+				/*
+				**  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 black 53/TCP
+				**  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.
@@ -721,8 +768,27 @@
 				**  the cache so this isn't dangerous.
 				*/
 
+#if _FFR_WORKAROUND_BROKEN_NAMESERVERS
+				if (WorkAroundBrokenAAAA)
+				{
+					/*
+					**  Only return if not TRY_AGAIN as an
+					**  attempt with a different qtype may
+					**  succeed (res_querydomain() calls
+					**  res_query() calls res_send() which
+					**  sets errno to ETIMEDOUT if the
+					**  nameservers could be contacted but
+					**  didn't give an answer).
+					*/
+
+					if (qtype != T_ANY &&
+					    errno != ETIMEDOUT)
+						return FALSE;
+				}
+#else /* _FFR_WORKAROUND_BROKEN_NAMESERVERS */
 				if (qtype != T_ANY)
 					return FALSE;
+#endif /* _FFR_WORKAROUND_BROKEN_NAMESERVERS */
 			}
 
 			if (h_errno != HOST_NOT_FOUND)
@@ -775,21 +841,24 @@
 		eom = (u_char *) &answer + ret;
 
 		/* skip question part of response -- we know what we asked */
-		for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ)
+		for (qdcount = ntohs((u_short)hp->qdcount);
+		     qdcount--;
+		     ap += ret + QFIXEDSZ)
 		{
 			if ((ret = dn_skipname(ap, eom)) < 0)
 			{
 				if (tTd(8, 20))
 					dprintf("qdcount failure (%d)\n",
-						ntohs(hp->qdcount));
+						ntohs((u_short)hp->qdcount));
 				*statp = EX_SOFTWARE;
 				return FALSE;		/* ???XXX??? */
 			}
 		}
 
 		amatch = FALSE;
-		for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom;
-									ap += n)
+		for (ancount = ntohs((u_short)hp->ancount);
+		     --ancount >= 0 && ap < eom;
+		     ap += n)
 		{
 			n = dn_expand((u_char *) &answer, eom, ap,
 				      (RES_UNC_T) nbuf, sizeof nbuf);
@@ -866,7 +935,7 @@
 							host);
 						CurEnv->e_message = newstr(ebuf);
 					}
-					h_errno = NO_RECOVERY;
+					SM_SET_H_ERRNO(NO_RECOVERY);
 					*statp = EX_CONFIG;
 					return FALSE;
 				}
@@ -937,7 +1006,8 @@
 	/* if nothing was found, we are done */
 	if (mxmatch == NULL)
 	{
-		*statp = EX_NOHOST;
+		if (*statp == EX_OK)
+			*statp = EX_NOHOST;
 		return FALSE;
 	}
 
Index: gnu/usr.sbin/sendmail/sendmail/envelope.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/envelope.c,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -r1.2 -r1.4
--- gnu/usr.sbin/sendmail/sendmail/envelope.c	2000/08/02 04:10:45	1.2
+++ gnu/usr.sbin/sendmail/sendmail/envelope.c	2001/05/29 01:31:15	1.4
@@ -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
@@ -12,11 +12,12 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: envelope.c,v 8.180 1999/12/03 03:39:44 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: envelope.c,v 8.180.14.10 2001/05/03 17:24:06 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
 
+
 /*
 **  NEWENVELOPE -- allocate a new envelope
 **
@@ -87,8 +88,10 @@
 	bool delay_return = FALSE;
 	bool success_return = FALSE;
 	bool pmnotify = bitset(EF_PM_NOTIFY, e->e_flags);
+	bool done = FALSE;
 	register ADDRESS *q;
 	char *id = e->e_id;
+	time_t now;
 	char buf[MAXLINE];
 
 	if (tTd(50, 1))
@@ -128,7 +131,8 @@
 	**  Extract state information from dregs of send list.
 	*/
 
-	if (curtime() > e->e_ctime + TimeOuts.to_q_return[e->e_timeoutclass])
+	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 &&
@@ -146,13 +150,20 @@
 
 		/* see if a notification is needed */
 		if (bitset(QPINGONFAILURE, q->q_flags) &&
-		    ((message_timeout && QS_IS_QUEUEUP(q->q_state)) ||
-		     QS_IS_BADADDR(q->q_state)))
+		    ((message_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))))
+
 		{
 			failure_return = TRUE;
-			if (q->q_owner == NULL && !emptyaddr(&e->e_from))
+			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;
+			}
 		}
 		else if (bitset(QPINGONSUCCESS, q->q_flags) &&
 			 ((QS_IS_SENT(q->q_state) &&
@@ -178,10 +189,10 @@
 		if (failure_return)
 		{
 			(void) snprintf(buf, sizeof buf,
-				"Cannot send message within %s",
-				pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE));
+					"Cannot send message for %s",
+					pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE));
 			if (e->e_message != NULL)
-				free(e->e_message);
+				sm_free(e->e_message);
 			e->e_message = newstr(buf);
 			message(buf);
 			e->e_flags |= EF_CLRQUEUE;
@@ -199,7 +210,7 @@
 		}
 	}
 	else if (TimeOuts.to_q_warning[e->e_timeoutclass] > 0 &&
-	    curtime() > e->e_ctime + TimeOuts.to_q_warning[e->e_timeoutclass])
+		 now >= e->e_ctime + TimeOuts.to_q_warning[e->e_timeoutclass])
 	{
 		if (!bitset(EF_WARNING|EF_RESPONSE, e->e_flags) &&
 		    e->e_class >= 0 &&
@@ -211,7 +222,7 @@
 		{
 			for (q = e->e_sendqueue; q != NULL; q = q->q_next)
 			{
-				if (QS_IS_QUEUEUP(q->q_state) &&
+				if (QS_IS_UNDELIVERED(q->q_state) &&
 #if _FFR_NODELAYDSN_ON_HOLD
 				    !bitnset(M_HOLD, q->q_mailer->m_flags) &&
 #endif /* _FFR_NODELAYDSN_ON_HOLD */
@@ -228,7 +239,7 @@
 				"Warning: could not send message for past %s",
 				pintvl(TimeOuts.to_q_warning[e->e_timeoutclass], FALSE));
 			if (e->e_message != NULL)
-				free(e->e_message);
+				sm_free(e->e_message);
 			e->e_message = newstr(buf);
 			message(buf);
 			e->e_flags |= EF_WARNING;
@@ -253,7 +264,8 @@
 	{
 		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
 		{
-			if (QS_IS_UNDELIVERED(q->q_state) &&
+			if ((QS_IS_OK(q->q_state) ||
+			     QS_IS_VERIFIED(q->q_state)) &&
 			    bitset(QPINGONFAILURE, q->q_flags))
 			{
 				failure_return = TRUE;
@@ -582,6 +594,7 @@
 	p = queuename(e, 'x');
 	e->e_xfp = bfopen(p, FileMode, XscriptFileBufferSize,
 			  SFF_NOTEXCL|SFF_OPENASROOT);
+
 	if (e->e_xfp == NULL)
 	{
 		syserr("Can't create transcript file %s", p);
@@ -800,7 +813,9 @@
 			*/
 
 			/* extract home directory */
-			if (strcmp(pw->pw_dir, "/") == 0)
+			if (*pw->pw_dir == '\0')
+				e->e_from.q_home = NULL;
+			else if (strcmp(pw->pw_dir, "/") == 0)
 				e->e_from.q_home = newstr("");
 			else
 				e->e_from.q_home = newstr(pw->pw_dir);
@@ -952,7 +967,7 @@
 	{ "HAS_DF",		EF_HAS_DF	},
 	{ "IS_MIME",		EF_IS_MIME	},
 	{ "DONT_MIME",		EF_DONT_MIME	},
-	{ NULL }
+	{ NULL,			0		}
 };
 
 void
Index: gnu/usr.sbin/sendmail/sendmail/err.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/err.c,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- gnu/usr.sbin/sendmail/sendmail/err.c	2000/04/02 19:05:45	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/err.c	2001/05/29 01:31:15	1.3
@@ -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
@@ -12,7 +12,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: err.c,v 8.120 2000/02/17 21:32:05 ca Exp $";
+static char id[] = "@(#)$Sendmail: err.c,v 8.120.4.2 2001/05/03 17:24:06 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
@@ -21,6 +21,7 @@
 # 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 *,
@@ -106,7 +107,7 @@
 	if (!panic && CurEnv != NULL)
 	{
 		if (CurEnv->e_message != NULL)
-			free(CurEnv->e_message);
+			sm_free(CurEnv->e_message);
 		CurEnv->e_message = newstr(errtxt);
 	}
 
@@ -121,13 +122,13 @@
 			dprintf("syserr: ExitStat = %d\n", ExitStat);
 	}
 
-	pw = sm_getpwuid(getuid());
+	pw = sm_getpwuid(RealUid);
 	if (pw != NULL)
 		user = pw->pw_name;
 	else
 	{
 		user = ubuf;
-		snprintf(ubuf, sizeof ubuf, "UID%d", (int) getuid());
+		snprintf(ubuf, sizeof ubuf, "UID%d", (int) RealUid);
 	}
 
 	if (LogLevel > 0)
@@ -156,6 +157,8 @@
 #ifdef ESTALE
 	  case ESTALE:
 #endif /* ESTALE */
+
+
 		printopenfds(TRUE);
 		mci_dump_all(TRUE);
 		break;
@@ -234,7 +237,7 @@
 	  case '5':
 	  case '6':
 		if (CurEnv->e_message != NULL)
-			free(CurEnv->e_message);
+			sm_free(CurEnv->e_message);
 		if (MsgBuf[0] == '6')
 		{
 			char buf[MAXLINE];
@@ -320,7 +323,7 @@
 	  case '5':
 	  case '6':
 		if (CurEnv->e_message != NULL)
-			free(CurEnv->e_message);
+			sm_free(CurEnv->e_message);
 		if (MsgBuf[0] == '6')
 		{
 			char buf[MAXLINE];
@@ -389,7 +392,7 @@
 
 	  case '5':
 		if (CurEnv->e_message != NULL)
-			free(CurEnv->e_message);
+			sm_free(CurEnv->e_message);
 		CurEnv->e_message = newstr(errtxt);
 		break;
 	}
@@ -443,7 +446,7 @@
 
 	  case '5':
 		if (CurEnv->e_message != NULL)
-			free(CurEnv->e_message);
+			sm_free(CurEnv->e_message);
 		CurEnv->e_message = newstr(errtxt);
 		break;
 	}
Index: gnu/usr.sbin/sendmail/sendmail/headers.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/headers.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/sendmail/headers.c	2000/04/07 19:20:41	1.2
+++ gnu/usr.sbin/sendmail/sendmail/headers.c	2001/05/29 01:31:15	1.5
@@ -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
@@ -12,12 +12,12 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: headers.c,v 8.203 2000/03/15 21:47:29 ca Exp $";
+static char id[] = "@(#)$Sendmail: headers.c,v 8.203.4.13 2001/05/03 17:24:06 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
 
-static bool	fix_mime_header __P((char *));
+static size_t	fix_mime_header __P((char *));
 static int	priencode __P((char *));
 static void	put_vanilla_header __P((HDR *, char *, MCI *));
 
@@ -51,9 +51,7 @@
 **
 **	Parameters:
 **		line -- header as a text line.
-**		pflag -- flags:
-**			CHHDR_DEF: this is a default value.
-**			CHHDR_CHECK: call rulesets.
+**		pflag -- flags for chompheader() (from sendmail.h)
 **		hdrp -- a pointer to the place to save the header.
 **		e -- the envelope including this header.
 **
@@ -70,7 +68,7 @@
 u_long
 chompheader(line, pflag, hdrp, e)
 	char *line;
-	int *pflag;
+	int pflag;
 	HDR **hdrp;
 	register ENVELOPE *e;
 {
@@ -81,6 +79,7 @@
 	char *fname;
 	char *fvalue;
 	bool cond = FALSE;
+	bool dropfrom;
 	bool headeronly;
 	STAB *s;
 	struct hdrinfo *hi;
@@ -101,7 +100,7 @@
 	/* strip off options */
 	clrbitmap(mopts);
 	p = line;
-	if (!bitset(*pflag, CHHDR_USER) && *p == '?')
+	if (!bitset(pflag, CHHDR_USER) && *p == '?')
 	{
 		int c;
 		register char *q;
@@ -165,7 +164,7 @@
 					goto hse;
 				}
 
-				setbitn(*p, mopts);
+				setbitn(bitidx(*p), mopts);
 				cond = TRUE;
 				p++;
 			}
@@ -204,7 +203,7 @@
 		return H_EOH;
 
 	/* check to see if it represents a ruleset call */
-	if (bitset(*pflag, CHHDR_DEF))
+	if (bitset(pflag, CHHDR_DEF))
 	{
 		char hbuf[50];
 
@@ -249,12 +248,12 @@
 	}
 
 	/* see if this is a resent message */
-	if (!bitset(*pflag, CHHDR_DEF) && !headeronly &&
+	if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
 	    bitset(H_RESENT, hi->hi_flags))
 		e->e_flags |= EF_RESENT;
 
 	/* if this is an Errors-To: header keep track of it now */
-	if (UseErrorsTo && !bitset(*pflag, CHHDR_DEF) && !headeronly &&
+	if (UseErrorsTo && !bitset(pflag, CHHDR_DEF) && !headeronly &&
 	    bitset(H_ERRORSTO, hi->hi_flags))
 		(void) sendtolist(fvalue, NULLADDR, &e->e_errorqueue, 0, e);
 
@@ -280,7 +279,7 @@
 	**  If there is a check ruleset, verify it against the header.
 	*/
 
-	if (bitset(*pflag, CHHDR_CHECK))
+	if (bitset(pflag, CHHDR_CHECK))
 	{
 		bool stripcom = FALSE;
 		char *rs;
@@ -309,7 +308,7 @@
 			dp = qval;
 			l = 0;
 			dp[l++] = '"';
-			for (sp = fvalue; *sp != '\0' && l < MAXNAME - 2; sp++)
+			for (sp = fvalue; *sp != '\0' && l < MAXNAME - 3; sp++)
 			{
 				switch(*sp)
 				{
@@ -338,63 +337,29 @@
 				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);
+						  fname, rs, l, MAXNAME - 1);
 			}
 			if ((sp = macvalue(macid("{currHeader}", NULL), e)) !=
 			    NULL)
-				free(sp);
+				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);
+			(void) rscheck(rs, fvalue, NULL, e, stripcom, TRUE, 4,
+				       NULL);
 		}
 	}
 
-#if _FFR_MILTER
-	/* Call milter */
-	if (bitset(*pflag, CHHDR_MILTER) &&
-	    !bitset(EF_DISCARD, e->e_flags))
-	{
-		char state;
-		char *response;
-
-		response = milter_header(fname, fvalue, e, &state);
-		switch (state)
-		{
-		  case SMFIR_REPLYCODE:
-			*pflag &= ~CHHDR_MILTER;
-			usrerr(response);
-			break;
-
-		  case SMFIR_REJECT:
-			*pflag &= ~CHHDR_MILTER;
-			usrerr("554 5.7.1 Message rejected");
-			break;
-
-		  case SMFIR_DISCARD:
-			*pflag &= ~CHHDR_MILTER;
-			e->e_flags |= EF_DISCARD;
-			break;
-
-		  case SMFIR_TEMPFAIL:
-			*pflag &= ~CHHDR_MILTER;
-			usrerr("451 4.7.1 Try again later");
-			break;
-		}
-		if (response != NULL)
-			free(response);
-	}
-#endif /* _FFR_MILTER */
-
 	/*
 	**  Drop explicit From: if same as what we would generate.
 	**  This is to make MH (which doesn't always give a full name)
 	**  insert the full name information in all circumstances.
 	*/
 
+	dropfrom = FALSE;
 	p = "resent-from";
 	if (!bitset(EF_RESENT, e->e_flags))
 		p += 7;
-	if (!bitset(*pflag, CHHDR_DEF) && !headeronly &&
+	if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
 	    !bitset(EF_QUEUERUN, e->e_flags) && strcasecmp(fname, p) == 0)
 	{
 		if (tTd(31, 2))
@@ -407,14 +372,14 @@
 		    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))
-			return hi->hi_flags;
+			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 &&
-		    bitset(H_DEFAULT, h->h_flags) &&
+		    !bitset(H_USER, h->h_flags) &&
 		    !bitset(H_FORCE, h->h_flags))
 		{
 			if (nullheader)
@@ -422,6 +387,12 @@
 				/* user-supplied value was null */
 				return 0;
 			}
+			if (dropfrom)
+			{
+				/* make this look like the user entered it */
+				h->h_flags |= H_USER;
+				return hi->hi_flags;
+			}
 			h->h_value = NULL;
 			if (!cond)
 			{
@@ -442,17 +413,19 @@
 	h->h_macro = mid;
 	*hp = h;
 	h->h_flags = hi->hi_flags;
+	if (bitset(pflag, CHHDR_USER) || bitset(pflag, CHHDR_QUEUE))
+		h->h_flags |= H_USER;
 
 	/* strip EOH flag if parsing MIME headers */
 	if (headeronly)
 		h->h_flags &= ~H_EOH;
-	if (bitset(*pflag, CHHDR_DEF))
+	if (bitset(pflag, CHHDR_DEF))
 		h->h_flags |= H_DEFAULT;
 	if (cond || mid != '\0')
 		h->h_flags |= H_CHECK;
 
 	/* hack to see if this is a new format message */
-	if (!bitset(*pflag, CHHDR_DEF) && !headeronly &&
+	if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
 	    bitset(H_RCPT|H_FROM, h->h_flags) &&
 	    (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL ||
 	     strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL))
@@ -470,6 +443,7 @@
 **	Parameters:
 **		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.
 **
 **	Returns:
@@ -480,9 +454,10 @@
 */
 
 void
-addheader(field, value, hdrlist)
+addheader(field, value, flags, hdrlist)
 	char *field;
 	char *value;
+	int flags;
 	HDR **hdrlist;
 {
 	register HDR *h;
@@ -504,7 +479,7 @@
 	h->h_field = field;
 	h->h_value = newstr(value);
 	h->h_link = *hp;
-	h->h_flags = H_DEFAULT;
+	h->h_flags = flags;
 	if (s != NULL)
 		h->h_flags |= s->s_header.hi_flags;
 	clrbitmap(h->h_mflags);
@@ -917,7 +892,7 @@
 	p = macvalue(macid("{auth_type}", NULL), e);
 	if (p != NULL)
 	{
-		(void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", mech=%.10s", p);
+		(void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", mech=%.12s", p);
 		sbp += strlen(sbp);
 	}
 	p = macvalue(macid("{auth_author}", NULL), e);
@@ -982,7 +957,7 @@
 
 	for (i = 0; i < NumPriorities; i++)
 	{
-		if (!strcasecmp(p, Priorities[i].pri_name))
+		if (strcasecmp(p, Priorities[i].pri_name) == 0)
 			return Priorities[i].pri_val;
 	}
 
@@ -1405,19 +1380,27 @@
 			xputs(p);
 		}
 
+		/* Skip empty headers */
+		if (h->h_value == NULL)
+			continue;
+
 		/* heuristic shortening of MIME fields to avoid MUA overflows */
 		if (MaxMimeFieldLength > 0 &&
 		    wordinclass(h->h_field,
 				macid("{checkMIMEFieldHeaders}", NULL)))
 		{
-			if (fix_mime_header(h->h_value))
+			size_t len;
+
+			len = fix_mime_header(h->h_value);
+			if (len > 0)
 			{
 				sm_syslog(LOG_ALERT, e->e_id,
-					  "Truncated MIME %s header due to field size (possible attack)",
-					  h->h_field);
+					  "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 (possible attack)\n",
-						h->h_field);
+					dprintf("  truncated MIME %s header due to field size  (length = %ld) (possible attack)\n",
+						h->h_field,
+						(unsigned long) len);
 			}
 		}
 
@@ -1425,15 +1408,19 @@
 		    wordinclass(h->h_field,
 				macid("{checkMIMETextHeaders}", NULL)))
 		{
-			if (strlen(h->h_value) > MaxMimeHeaderLength)
+			size_t len;
+
+			len = strlen(h->h_value);
+			if (len > (size_t) MaxMimeHeaderLength)
 			{
 				h->h_value[MaxMimeHeaderLength - 1] = '\0';
 				sm_syslog(LOG_ALERT, e->e_id,
-					  "Truncated long MIME %s header (possible attack)",
-					  h->h_field);
+					  "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 (possible attack)\n",
-						h->h_field);
+					dprintf("  truncated long MIME %s header (length = %ld) (possible attack)\n",
+						h->h_field,
+						(unsigned long) len);
 			}
 		}
 
@@ -1441,14 +1428,19 @@
 		    wordinclass(h->h_field,
 				macid("{checkMIMEHeaders}", NULL)))
 		{
-			if (shorten_rfc822_string(h->h_value, MaxMimeHeaderLength))
+			size_t len;
+
+			len = strlen(h->h_value);
+			if (shorten_rfc822_string(h->h_value,
+						  MaxMimeHeaderLength))
 			{
 				sm_syslog(LOG_ALERT, e->e_id,
-					  "Truncated long MIME %s header (possible attack)",
-					  h->h_field);
+					  "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 (possible attack)\n",
-						h->h_field);
+					dprintf("  truncated long MIME %s header (length = %ld) (possible attack)\n",
+						h->h_field,
+						(unsigned long) len);
 			}
 		}
 
@@ -1479,7 +1471,7 @@
 		if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
 		    !bitintersect(h->h_mflags, mci->mci_mailer->m_flags) &&
 		    (h->h_macro == '\0' ||
-		     macvalue(h->h_macro & 0377, e) == NULL))
+		     macvalue(bitidx(h->h_macro), e) == NULL))
 		{
 			if (tTd(34, 11))
 				dprintf(" (skipped)\n");
@@ -1612,7 +1604,7 @@
 		int l;
 
 		l = nlp - v;
-		if (SPACELEFT(obuf, obp) - 1 < l)
+		if (SPACELEFT(obuf, obp) - 1 < (size_t)l)
 			l = SPACELEFT(obuf, obp) - 1;
 
 		snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v);
@@ -1841,22 +1833,23 @@
 **		string -- the full header
 **
 **	Returns:
-**		TRUE if the header was modified, FALSE otherwise
+**		length of last offending field, 0 if all ok.
 **
 **	Side Effects:
 **		string modified in place
 */
 
-static bool
+static size_t
 fix_mime_header(string)
 	char *string;
 {
-	bool modified = FALSE;
 	char *begin = string;
 	char *end;
+	size_t len = 0;
+	size_t retlen = 0;
 
 	if (string == NULL || *string == '\0')
-		return FALSE;
+		return 0;
 
 	/* Split on each ';' */
 	while ((end = find_character(begin, ';')) != NULL)
@@ -1866,9 +1859,11 @@
 
 		*end = '\0';
 
+		len = strlen(begin);
+
 		/* Shorten individual parameter */
 		if (shorten_rfc822_string(begin, MaxMimeFieldLength))
-			modified = TRUE;
+			retlen = len;
 
 		/* Collapse the possibly shortened string with rest */
 		bp = begin + strlen(begin);
@@ -1892,5 +1887,5 @@
 		/* Move past ';' */
 		begin = end + 1;
 	}
-	return modified;
+	return retlen;
 }
Index: gnu/usr.sbin/sendmail/sendmail/helpfile
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/helpfile,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/sendmail/helpfile	2000/04/02 19:05:45	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/helpfile	2001/01/15 21:09:08	1.2
@@ -1,6 +1,6 @@
 #vers	2
 cpyr
-cpyr	Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+cpyr	Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
 cpyr	    All rights reserved.
 cpyr	Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
 cpyr	Copyright (c) 1988, 1993
@@ -11,13 +11,14 @@
 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 1999/11/14 21:42:03 gshapiro Exp $$
+cpyr	$$Sendmail: helpfile,v 8.31.16.4 2000/09/17 14:21:00 ca Exp $$
 cpyr
 smtp	This is sendmail version $v
 smtp	Topics:
 smtp		HELO	EHLO	MAIL	RCPT	DATA
 smtp		RSET	NOOP	QUIT	HELP	VRFY
 smtp		EXPN	VERB	ETRN	DSN	AUTH
+smtp		STARTTLS
 smtp	For more info use "HELP <topic>".
 smtp	To report bugs in the implementation send email to
 smtp		sendmail-bugs@sendmail.org.
@@ -44,7 +45,10 @@
 ehlo		PIPELINING	Command Pipelining		[RFC1854]
 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]
 mail	MAIL FROM: <sender> [ <parameters> ]
 mail		Specifies the sender.  Parameters are ESMTP extensions.
 mail		See "HELP DSN" for details.
@@ -60,6 +64,8 @@
 quit		Exit sendmail (SMTP).
 auth	AUTH mechanism [initial-response]
 auth		Start authentication.
+starttls	STARTTLS
+starttls		Start TLS negotiation.
 verb	VERB
 verb		Go into verbose mode.  This sends 0xy responses that are
 verb		not RFC821 standard (but should be)  They are recognized
Index: gnu/usr.sbin/sendmail/sendmail/macro.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/macro.c,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -r1.2 -r1.4
--- gnu/usr.sbin/sendmail/sendmail/macro.c	2000/10/09 23:45:01	1.2
+++ gnu/usr.sbin/sendmail/sendmail/macro.c	2001/02/28 02:43:54	1.4
@@ -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
@@ -12,14 +12,17 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: macro.c,v 8.40 1999/11/22 19:10:16 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: macro.c,v 8.40.16.9 2001/02/22 01:16:55 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
 
-char	*MacroName[256];	/* macro id to name table */
-int	NextMacroId = 0240;	/* codes for long named macros */
+#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 */
 
 /*
 **  EXPAND -- macro expand a string using $x escapes.
@@ -111,7 +114,7 @@
 			continue;
 
 		  case MACROEXPAND:	/* macro interpolation */
-			c = *++s & 0377;
+			c = bitidx(*++s);
 			if (c != '\0')
 				q = macvalue(c, e);
 			else
@@ -169,7 +172,7 @@
 
 	/* copy results out */
 	i = xp - xbuf;
-	if (i >= bufsize)
+	if ((size_t)i >= bufsize)
 		i = bufsize - 1;
 	memmove(buf, xbuf, i);
 	buf[i] = '\0';
@@ -247,7 +250,7 @@
 {
 	int m;
 
-	m = n & 0377;
+	m = bitidx(n);
 	if (tTd(35, 9))
 	{
 		dprintf("%sdefine(%s as ",
@@ -285,13 +288,15 @@
 	int n;
 	register ENVELOPE *e;
 {
-	n &= 0377;
+	n = bitidx(n);
 	while (e != NULL)
 	{
 		register char *p = e->e_macro[n];
 
 		if (p != NULL)
 			return p;
+		if (e == e->e_parent)
+			break;
 		e = e->e_parent;
 	}
 	return NULL;
@@ -315,7 +320,7 @@
 {
 	static char mbuf[2];
 
-	n &= 0377;
+	n = bitidx(n);
 	if (bitset(0200, n))
 	{
 		char *p = MacroName[n];
@@ -368,7 +373,7 @@
 			*ep = p;
 		if (tTd(35, 14))
 			dprintf("NULL\n");
-		return '\0';
+		return 0;
 	}
 	if (*p != '{')
 	{
@@ -376,8 +381,8 @@
 		if (ep != NULL)
 			*ep = p + 1;
 		if (tTd(35, 14))
-			dprintf("%c\n", *p);
-		return ((unsigned int)*p) & 0xff;
+			dprintf("%c\n", bitidx(*p));
+		return bitidx(*p);
 	}
 	bp = mbuf;
 	while (*++p != '\0' && *p != '}' && bp < &mbuf[sizeof mbuf - 1])
@@ -401,7 +406,7 @@
 	else if (mbuf[1] == '\0')
 	{
 		/* ${x} == $x */
-		mid = ((unsigned int)mbuf[0]) & 0xff;
+		mid = bitidx(mbuf[0]);
 		p++;
 	}
 	else
@@ -431,7 +436,9 @@
 	if (mid < 0 || mid > MAXMACROID)
 	{
 		syserr("Unable to assign macro/class ID (mid = 0x%x)", mid);
-		mid = 0;
+		if (tTd(35, 14))
+			dprintf("NULL\n");
+		return 0;
 	}
 	if (tTd(35, 14))
 		dprintf("0x%x\n", mid);
@@ -457,5 +464,5 @@
 	register STAB *s;
 
 	s = stab(str, ST_CLASS, ST_FIND);
-	return s != NULL && bitnset(cl & 0xff, s->s_class);
+	return s != NULL && bitnset(bitidx(cl), s->s_class);
 }
Index: gnu/usr.sbin/sendmail/sendmail/mailq.1
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/mailq.1,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- gnu/usr.sbin/sendmail/sendmail/mailq.1	2000/04/04 04:50:14	1.3
+++ gnu/usr.sbin/sendmail/sendmail/mailq.1	2001/01/15 21:09:08	1.4
@@ -9,9 +9,9 @@
 .\" the sendmail distribution.
 .\"
 .\"
-.\"     $Sendmail: mailq.1,v 8.14 1999/06/22 20:41:34 tony Exp $
+.\"     $Sendmail: mailq.1,v 8.14.28.3 2000/12/14 23:08:15 gshapiro Exp $
 .\"
-.Dd June 22, 1999
+.Dd December 14, 2000
 .Dt MAILQ 1
 .Os
 .Sh NAME
@@ -26,7 +26,7 @@
 .Pp
 The first line printed for each message
 shows the internal identifier used on this host
-for the message,
+for the message with a possible status character,
 the size of the message in bytes,
 the date and time the message was accepted into the queue,
 and the envelope sender of the message.
@@ -34,6 +34,13 @@
 to be retained in the queue;
 it will not be present if the message is being processed
 for the first time.
+The status characters are either
+.Sq *
+to indicate the job is being processed;
+.Sq X
+to indicate that the load is too high to process the job; and
+.Sq -
+to indicate that the job is too young to process.
 The following lines show message recipients,
 one per line.
 .Pp
Index: gnu/usr.sbin/sendmail/sendmail/main.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/main.c,v
retrieving revision 1.3
retrieving revision 1.6
diff -u -r1.3 -r1.6
--- gnu/usr.sbin/sendmail/sendmail/main.c	2000/10/09 23:45:01	1.3
+++ gnu/usr.sbin/sendmail/sendmail/main.c	2001/05/29 01:31:15	1.6
@@ -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
@@ -13,7 +13,7 @@
 
 #ifndef lint
 static char copyright[] =
-"@(#) Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.\n\
+"@(#) 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\
      Copyright (c) 1988, 1993\n\
@@ -21,16 +21,22 @@
 #endif /* ! lint */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: main.c,v 8.485 2000/03/11 19:53:01 ca Exp $";
+static char id[] = "@(#)$Sendmail: main.c,v 8.485.4.60 2001/05/27 22:00:26 gshapiro Exp $";
 #endif /* ! lint */
 
 #define	_DEFINE
 
 #include <sendmail.h>
+
+
 #if NETINET || NETINET6
 # include <arpa/inet.h>
 #endif /* NETINET || NETINET6 */
 
+static SIGFUNC_DECL	intindebug __P((int));
+static SIGFUNC_DECL	quiesce __P((int));
+static SIGFUNC_DECL	sigusr1 __P((int));
+static SIGFUNC_DECL	term_daemon __P((int));
 static void	dump_class __P((STAB *, int));
 static void	obsolete __P((char **));
 static void	testmodeline __P((char *, ENVELOPE *));
@@ -74,7 +80,6 @@
 		{ "", "", NULL, "" };
 char		*CommandLineArgs;	/* command line args for pid file */
 bool		Warn_Q_option = FALSE;	/* warn about Q option use */
-char		**SaveArgv;	/* argument vector for re-execing */
 static int	MissingFds = 0;	/* bit map of fds missing on startup */
 
 #ifdef NGROUPS_MAX
@@ -91,11 +96,13 @@
 #define MAXCONFIGLEVEL	9	/* highest config version level known */
 
 #if SASL
-static sasl_callback_t srvcallbacks[] = {
+static sasl_callback_t srvcallbacks[] =
+{
 	{	SASL_CB_VERIFYFILE,	&safesaslfile,	NULL	},
 	{	SASL_CB_PROXY_POLICY,	&proxy_policy,	NULL	},
 	{	SASL_CB_LIST_END,	NULL,		NULL	}
 };
+
 #endif /* SASL */
 
 int SubmitMode;
@@ -113,6 +120,7 @@
 	STAB *st;
 	register int i;
 	int j;
+	int dp;
 	bool safecf = TRUE;
 	BITMAP256 *p_flags = NULL;	/* daemon flags */
 	bool warn_C_flag = FALSE;
@@ -130,6 +138,9 @@
 	char jbuf[MAXHOSTNAMELEN];	/* holds MyHostName */
 	static char rnamebuf[MAXNAME];	/* holds RealUserName */
 	char *emptyenviron[1];
+# if STARTTLS
+	bool tls_ok;
+# endif /* STARTTLS */
 	QUEUE_CHAR *new;
 	extern int DtableSize;
 	extern int optind;
@@ -153,9 +164,18 @@
 	/* avoid null pointer dereferences */
 	TermEscape.te_rv_on = TermEscape.te_rv_off = "";
 
+	/*
+	**  Seed the random number generator.
+	**  Used for queue file names, picking a queue directory, and
+	**  MX randomization.
+	*/
+
+	seed_random();
+
 	/* do machine-dependent initializations */
 	init_md(argc, argv);
 
+
 	/* in 4.4BSD, the table can be huge; impose a reasonable limit */
 	DtableSize = getdtsize();
 	if (DtableSize > 256)
@@ -179,11 +199,11 @@
 	errno = 0;
 
 #if LOG
-# ifdef LOG_MAIL
+#  ifdef LOG_MAIL
 	openlog("sendmail", LOG_PID, LOG_MAIL);
-# else /* LOG_MAIL */
+#  else /* LOG_MAIL */
 	openlog("sendmail", LOG_PID);
-# endif /* LOG_MAIL */
+#  endif /* LOG_MAIL */
 #endif /* LOG */
 
 	if (MissingFds != 0)
@@ -209,14 +229,6 @@
 	checkfd012("after openlog");
 #endif /* XDEBUG */
 
-	/*
-	**  Seed the random number generator.
-	**  Used for queue file names, picking a queue directory, and
-	**  MX randomization.
-	*/
-
-	seed_random();
-
 	tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
 
 #ifdef NGROUPS_MAX
@@ -229,12 +241,18 @@
 #endif /* NGROUPS_MAX */
 
 	/* drop group id privileges (RunAsUser not yet set) */
-	(void) drop_privileges(FALSE);
+	dp = drop_privileges(FALSE);
+	setstat(dp);
 
-#ifdef SIGUSR1
-	/* arrange to dump state on user-1 signal */
-	(void) setsignal(SIGUSR1, sigusr1);
-#endif /* 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);
+	}
+# endif /* SIGUSR1 */
 
 	/* initialize for setproctitle */
 	initsetproctitle(argc, argv, envp);
@@ -246,6 +264,7 @@
 	**  Do a quick prescan of the argument list.
 	*/
 
+
 #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"
 #endif /* defined(__osf__) || defined(_AIX3) */
@@ -289,18 +308,23 @@
 	}
 	opterr = 1;
 
+#if LOG
 	if (sysloglabel != NULL)
 	{
-#if LOG
+		/* Sanitize the string */
+		for (p = sysloglabel; *p != '\0'; p++)
+		{
+			if (!isascii(*p) || !isprint(*p) || *p == '%')
+				*p = '*';
+		}
 		closelog();
-# ifdef LOG_MAIL
+#  ifdef LOG_MAIL
 		openlog(sysloglabel, LOG_PID, LOG_MAIL);
-# else /* LOG_MAIL */
+#  else /* LOG_MAIL */
 		openlog(sysloglabel, LOG_PID);
-# endif /* LOG_MAIL */
-#endif /* LOG */
+#  endif /* LOG_MAIL */
 	}
-
+#endif /* LOG */
 
 	/* set up the blank envelope */
 	BlankEnvelope.e_puthdr = putheader;
@@ -326,6 +350,7 @@
 	else
 		(void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
 				(int) RealUid);
+
 	RealUserName = rnamebuf;
 
 	if (tTd(0, 101))
@@ -338,6 +363,7 @@
 	**  if running non-setuid binary as non-root, pretend
 	**  we are the RunAsUid
 	*/
+
 	if (RealUid != 0 && geteuid() == RealUid)
 	{
 		if (tTd(47, 1))
@@ -460,11 +486,8 @@
 
 	/* prime the child environment */
 	setuserenv("AGENT", "sendmail");
-
-	if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
-		(void) setsignal(SIGINT, intsig);
-	(void) setsignal(SIGTERM, intsig);
 	(void) setsignal(SIGPIPE, SIG_IGN);
+
 	OldUmask = umask(022);
 	OpMode = MD_DELIVER;
 	FullName = getextenv("NAME");
@@ -476,8 +499,17 @@
 #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
@@ -604,6 +636,10 @@
 			setclass('w', ipbuf);
 		}
 #endif /* NETINET || NETINET6 */
+#if _FFR_FREEHOSTENT && NETINET6
+		freehostent(hp);
+		hp = NULL;
+#endif /* _FFR_FREEHOSTENT && NETINET6 */
 	}
 
 	/* current time */
@@ -680,14 +716,15 @@
 			break;
 
 		  case 'B':	/* body type */
-			CurEnv->e_bodytype = optarg;
+			CurEnv->e_bodytype = newstr(optarg);
 			break;
 
 		  case 'C':	/* select configuration file (already done) */
 			if (RealUid != 0)
 				warn_C_flag = TRUE;
-			ConfFile = optarg;
-			(void) drop_privileges(TRUE);
+			ConfFile = newstr(optarg);
+			dp = drop_privileges(TRUE);
+			setstat(dp);
 			safecf = FALSE;
 			break;
 
@@ -716,7 +753,7 @@
 			break;
 
 		  case 'h':	/* hop count */
-			CurEnv->e_hopcount = strtol(optarg, &ep, 10);
+			CurEnv->e_hopcount = (short) strtol(optarg, &ep, 10);
 			if (*ep)
 			{
 				usrerr("Bad hop count (%s)", optarg);
@@ -885,7 +922,8 @@
 			break;
 
 		  case 'X':	/* traffic log file */
-			(void) 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");
@@ -999,6 +1037,59 @@
 	ConfigFileRead = TRUE;
 	vendor_post_defaults(CurEnv);
 
+	/* Remove the ability for a normal user to send signals */
+	if (RealUid != 0 &&
+	    RealUid != geteuid())
+	{
+		uid_t new_uid = geteuid();
+
+#if HASSETREUID
+		/*
+		**  Since we can differentiate between uid and euid,
+		**  make the uid a different user so the real user
+		**  can't send signals.  However, it doesn't need to be
+		**  root (euid has root).
+		*/
+
+		if (new_uid == 0)
+			new_uid = DefUid;
+		if (tTd(47, 5))
+			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);
+			/* NOTREACHED */
+		}
+		if (tTd(47, 10))
+			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
+		**  change them both to effective to keep privs.
+		*/
+
+		if (tTd(47, 5))
+			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);
+			/* NOTREACHED */
+		}
+		if (tTd(47, 10))
+			dprintf("Now running as e/ruid %d:%d\n",
+				(int) geteuid(), (int) getuid());
+#endif /* HASSETREUID */
+	}
+
+	/* set up the basic signal handlers */
+	if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
+		(void) setsignal(SIGINT, intsig);
+	(void) setsignal(SIGTERM, intsig);
+
 	/* Enforce use of local time (null string overrides this) */
 	if (TimeZoneSpec == NULL)
 		unsetenv("TZ");
@@ -1014,7 +1105,8 @@
 	if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
 	{
 		/* drop privileges -- daemon mode done after socket/bind */
-		(void) drop_privileges(FALSE);
+		dp = drop_privileges(FALSE);
+		setstat(dp);
 	}
 
 #if NAMED_BIND
@@ -1035,7 +1127,8 @@
 
 	/* set up the $=m class now, after .cf has a chance to redefine $m */
 	expand("\201m", jbuf, sizeof jbuf, CurEnv);
-	setclass('m', jbuf);
+	if (jbuf[0] != '\0')
+		setclass('m', jbuf);
 
 	/* probe interfaces and locate any additional names */
 	if (!DontProbeInterfaces)
@@ -1156,6 +1249,7 @@
 	  case MD_VERIFY:
 		CurEnv->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);
@@ -1177,10 +1271,7 @@
 		if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
 			sm_syslog(LOG_WARNING, NOQID,
 				  "daemon invoked without full pathname; kill -1 won't work");
-		(void) setsignal(SIGHUP, sighup);
-
-		/* workaround: can't seem to release the signal in the parent */
-		(void) releasesignal(SIGHUP);
+		(void) setsignal(SIGTERM, term_daemon);
 		break;
 
 	  case MD_INITALIAS:
@@ -1204,8 +1295,10 @@
 		/* full names can't have newlines */
 		if (strchr(FullName, '\n') != NULL)
 		{
-			FullName = full = newstr(denlstring(FullName, TRUE, TRUE));
+			full = newstr(denlstring(FullName, TRUE, TRUE));
+			FullName = full;
 		}
+
 		/* check for characters that may have to be quoted */
 		if (!rfc822_string(FullName))
 		{
@@ -1214,9 +1307,10 @@
 			**  as a comment so crackaddr() doesn't destroy
 			**  the name portion of the address.
 			*/
+
 			FullName = addquotes(FullName);
 			if (full != NULL)
-				free(full);
+				sm_free(full);
 		}
 	}
 
@@ -1268,10 +1362,13 @@
 
 	/* our name for SMTP codes */
 	expand("\201j", jbuf, sizeof jbuf, CurEnv);
-	MyHostName = jbuf;
-	if (strchr(jbuf, '.') == NULL)
+	if (jbuf[0] == '\0')
+		MyHostName = newstr("localhost");
+	else
+		MyHostName = jbuf;
+	if (strchr(MyHostName, '.') == NULL)
 		message("WARNING: local host name (%s) is not qualified; fix $j in config file",
-			jbuf);
+			MyHostName);
 
 	/* make certain that this name is part of the $=w class */
 	setclass('w', MyHostName);
@@ -1373,7 +1470,6 @@
 	setclass(macid("{persistentMacros}", NULL), "s");
 	setclass(macid("{persistentMacros}", NULL), "_");
 	setclass(macid("{persistentMacros}", NULL), "{if_addr}");
-	setclass(macid("{persistentMacros}", NULL), "{if_family}");
 	setclass(macid("{persistentMacros}", NULL), "{daemon_flags}");
 	setclass(macid("{persistentMacros}", NULL), "{client_flags}");
 
@@ -1432,6 +1528,7 @@
 		milter_parse_list(InputFilterList, InputFilters, MAXFILTERS);
 #endif /* _FFR_MILTER */
 
+
 	/* if we've had errors so far, exit now */
 	if (ExitStat != EX_OK && OpMode != MD_TEST)
 		finis(FALSE, ExitStat);
@@ -1554,6 +1651,9 @@
 	}
 
 #if SMTP
+# if STARTTLS
+	tls_ok = init_tls_library();
+# endif /* STARTTLS */
 #endif /* SMTP */
 
 #if QUEUE
@@ -1564,12 +1664,30 @@
 	if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
 	{
 # if SMTP
+#  if STARTTLS
+		if (tls_ok
+		   )
+		{
+			/* init TLS for client, ignore result for now */
+			(void) initclttls();
+		}
+#  endif /* STARTTLS */
 # endif /* SMTP */
 		(void) runqueue(FALSE, Verbose);
 		finis(TRUE, ExitStat);
 	}
 #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)
+			syserr("!sasl_server_init failed! [%s]",
+			       sasl_errstring(i, NULL, NULL));
+	}
+# endif /* SASL */
+
 	/*
 	**  If a daemon, wait for a request.
 	**	getrequests will always return in a child.
@@ -1630,10 +1748,13 @@
 			{
 				/* write the pid to file */
 				log_sendmail_pid(CurEnv);
+				(void) setsignal(SIGTERM, term_daemon);
 				for (;;)
 				{
 					(void) pause();
-					if (DoQueueRun)
+					if (ShutdownRequest != NULL)
+						shutdown_daemon();
+					else if (DoQueueRun)
 						(void) runqueue(TRUE, FALSE);
 				}
 			}
@@ -1642,6 +1763,10 @@
 		dropenvelope(CurEnv, TRUE);
 
 #if DAEMON
+# if STARTTLS
+		/* init TLS for server, ignore result for now */
+		(void) initsrvtls();
+# endif /* STARTTLS */
 		p_flags = getrequests(CurEnv);
 
 		/* drop privileges */
@@ -1662,8 +1787,7 @@
 	if (LogLevel > 9)
 	{
 		/* log connection information */
-		sm_syslog(LOG_INFO, CurEnv->e_id,
-			  "connect from %.100s", authinfo);
+		sm_syslog(LOG_INFO, NULL, "connect from %.100s", authinfo);
 	}
 
 #if SMTP
@@ -1719,12 +1843,6 @@
 		define(macid("{client_port}", NULL),
 		       newstr(pbuf), &BlankEnvelope);
 
-#if SASL
-		/* give a syserr or just disable AUTH ? */
-		if (sasl_server_init(srvcallbacks, "Sendmail") != SASL_OK)
-			syserr("!sasl_server_init failed!");
-#endif /* SASL */
-
 		if (OpMode == MD_DAEMON)
 		{
 			/* validate the connection */
@@ -1738,6 +1856,12 @@
 			p_flags = (BITMAP256 *) xalloc(sizeof *p_flags);
 			clrbitmap(p_flags);
 		}
+# if STARTTLS
+		if (OpMode == MD_SMTP)
+			(void) initsrvtls();
+# endif /* STARTTLS */
+
+
 		smtp(nullserver, *p_flags, CurEnv);
 	}
 #endif /* SMTP */
@@ -1791,7 +1915,7 @@
 			}
 			else
 				p = newstr(fv);
-			CurEnv->e_auth_param = newstr(xtextify(p, NULL));
+			CurEnv->e_auth_param = newstr(xtextify(p, "="));
 		}
 	}
 	if (macvalue('s', CurEnv) == NULL)
@@ -1802,6 +1926,7 @@
 		CurEnv->e_to = NULL;
 		CurEnv->e_flags |= EF_GLOBALERRS;
 		HoldErrs = FALSE;
+		SuperSafe = FALSE;
 		usrerr("Recipient names must be specified");
 
 		/* collect body for UUCP return */
@@ -1924,21 +2049,89 @@
 	/* 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 */
-SIGFUNC_DECL
+static SIGFUNC_DECL
 quiesce(sig)
 	int sig;
 {
-	clear_events();
-	finis(FALSE, EX_OK);
+	int save_errno = errno;
+
+	FIX_SYSV_SIGNAL(sig, quiesce);
+	StopRequest = TRUE;
+	errno = save_errno;
+	return SIGFUNC_RETURN;
+}
+/*
+**  STOP_SENDMAIL -- Stop the running program
+**
+**	Parameters:
+**		none.
+**
+**	Returns:
+**		none.
+**
+**	Side Effects:
+**		exits.
+*/
+
+void
+stop_sendmail()
+{
+	/* reset uid for process accounting */
+	endpwent();
+	(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 */
-SIGFUNC_DECL
+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;
 }
@@ -1961,6 +2154,9 @@
 	bool drop;
 	volatile int exitstat;
 {
+	/* Still want to process new timeouts added below */
+	clear_events();
+	releasesignal(SIGALRM);
 
 	if (tTd(2, 1))
 	{
@@ -2010,7 +2206,7 @@
 	if (LogLevel > 78)
 		sm_syslog(LOG_DEBUG, CurEnv->e_id,
 			  "finis, pid=%d",
-			  getpid());
+			  (int) getpid());
 	if (exitstat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
 		exitstat = EX_OK;
 
@@ -2022,6 +2218,71 @@
 	exit(exitstat);
 }
 /*
+**  TERM_DEAMON -- SIGTERM handler for the daemon
+**
+**	Parameters:
+**		sig -- signal number.
+**
+**	Returns:
+**		none.
+**
+**	Side Effects:
+**		Sets ShutdownRequest which will hopefully trigger
+**		the daemon to exit.
+**
+**	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
+term_daemon(sig)
+	int sig;
+{
+	int save_errno = errno;
+
+	FIX_SYSV_SIGNAL(sig, term_daemon);
+	ShutdownRequest = "signal";
+	errno = save_errno;
+	return SIGFUNC_RETURN;
+}
+/*
+**  SHUTDOWN_DAEMON -- Performs a clean shutdown of the daemon
+**
+**	Parameters:
+**		none.
+**
+**	Returns:
+**		none.
+**
+**	Side Effects:
+**		closes control socket, exits.
+*/
+
+void
+shutdown_daemon()
+{
+	char *reason;
+
+	allsignals(TRUE);
+
+	reason = ShutdownRequest;
+	ShutdownRequest = NULL;
+	PendingSignal = 0;
+
+	if (LogLevel > 79)
+		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
+
+	FileName = NULL;
+	closecontrolsocket(TRUE);
+#ifdef XLA
+	xla_all_end();
+#endif /* XLA */
+
+	finis(FALSE, EX_OK);
+}
+/*
 **  INTSIG -- clean up on interrupt
 **
 **	This just arranges to exit.  It pessimizes in that it
@@ -2035,6 +2296,12 @@
 **
 **	Side Effects:
 **		Unlocks the current job.
+**
+**	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 */
@@ -2043,15 +2310,15 @@
 	int sig;
 {
 	bool drop = FALSE;
+	int save_errno = errno;
 
-	clear_events();
+	FIX_SYSV_SIGNAL(sig, intsig);
+	errno = save_errno;
+	CHECK_CRITICAL(sig);
+	allsignals(TRUE);
 	if (sig != 0 && LogLevel > 79)
 		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
 	FileName = NULL;
-	closecontrolsocket(TRUE);
-#ifdef XLA
-	xla_all_end();
-#endif /* XLA */
 
 	/* Clean-up on aborted stdin message submission */
 	if (CurEnv->e_id != NULL &&
@@ -2121,7 +2388,7 @@
 	/* miscellaneous control characters */
 	{ '&', MACRODEXPAND },
 
-	{ '\0' }
+	{ '\0', '\0' }
 };
 
 #define MACBINDING(name, mid) \
@@ -2135,7 +2402,7 @@
 	register struct metamac *m;
 	register int c;
 	char buf[5];
-	extern char *MacroName[256];
+	extern char *MacroName[MAXMACROID + 1];
 
 	for (m = MetaMacros; m->metaname != '\0'; m++)
 	{
@@ -2252,7 +2519,7 @@
 	if (LogLevel > 71)
 		sm_syslog(LOG_DEBUG, e->e_id,
 			  "in background, pid=%d",
-			  getpid());
+			  (int) getpid());
 
 	errno = 0;
 }
@@ -2342,14 +2609,25 @@
 		static char hostbuf[48];
 
 		if (hostbuf[0] == '\0')
-			(void) myhostname(hostbuf, sizeof hostbuf);
+		{
+			struct hostent *hp;
 
+			hp = myhostname(hostbuf, sizeof hostbuf);
+#if _FFR_FREEHOSTENT && NETINET6
+			if (hp != NULL)
+			{
+				freehostent(hp);
+				hp = NULL;
+			}
+#endif /* _FFR_FREEHOSTENT && NETINET6 */
+		}
+
 		(void) snprintf(buf, sizeof buf, "%s: ", hostbuf);
 		p = &buf[strlen(buf)];
 		VA_START(msg);
 		vsnprintf(p, SPACELEFT(buf, p), msg, ap);
 		VA_END;
-		addheader("X-Authentication-Warning", buf, &e->e_header);
+		addheader("X-Authentication-Warning", buf, 0, &e->e_header);
 		if (LogLevel > 3)
 			sm_syslog(LOG_INFO, e->e_id,
 				  "Authentication-Warning: %.400s",
@@ -2480,69 +2758,36 @@
 	}
 	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
 }
-
+/*
+**  SIGUSR1 -- Signal a request to dump state.
+**
+**	Parameters:
+**		sig -- calling signal.
+**
+**	Returns:
+**		none.
+**
+**	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 */
-SIGFUNC_DECL
+static SIGFUNC_DECL
 sigusr1(sig)
 	int sig;
 {
+	int save_errno = errno;
+
+	FIX_SYSV_SIGNAL(sig, sigusr1);
+	errno = save_errno;
+	CHECK_CRITICAL(sig);
 	dumpstate("user signal");
+	errno = save_errno;
 	return SIGFUNC_RETURN;
 }
-
-
-/* ARGSUSED */
-SIGFUNC_DECL
-sighup(sig)
-	int sig;
-{
-	int i;
-	extern int DtableSize;
-
-	clear_events();
-	(void) alarm(0);
-	if (SaveArgv[0][0] != '/')
-	{
-		if (LogLevel > 3)
-			sm_syslog(LOG_INFO, NOQID,
-				  "could not restart: need full path");
-		finis(FALSE, EX_OSFILE);
-	}
-	if (LogLevel > 3)
-		sm_syslog(LOG_INFO, NOQID, "restarting %s %s",
-			  sig == 0 ? "due to control command" : "on signal",
-			  SaveArgv[0]);
-
-	/* Control socket restart? */
-	if (sig != 0)
-		(void) releasesignal(SIGHUP);
-
-	closecontrolsocket(TRUE);
-	if (drop_privileges(TRUE) != EX_OK)
-	{
-		if (LogLevel > 0)
-			sm_syslog(LOG_ALERT, NOQID,
-				  "could not set[ug]id(%d, %d): %m",
-				  RunAsUid, RunAsGid);
-		finis(FALSE, EX_OSERR);
-	}
-
-	/* arrange for all the files to be closed */
-	for (i = 3; i < DtableSize; i++)
-	{
-		register int j;
-
-		if ((j = fcntl(i, F_GETFD, 0)) != -1)
-			(void) fcntl(i, F_SETFD, j | FD_CLOEXEC);
-	}
-
-	(void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
-	if (LogLevel > 0)
-		sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m",
-			  SaveArgv[0]);
-	finis(FALSE, EX_OSFILE);
-}
 /*
 **  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
 **
@@ -2564,7 +2809,8 @@
 
 	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);
+			(int)to_real_uid, (int)RealUid,
+			(int)RealGid, (int)RunAsUid, (int)RunAsGid);
 
 	if (to_real_uid)
 	{
@@ -2579,19 +2825,61 @@
 	/* reset group permissions; these can be set later */
 	emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
 	if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
+	{
+		syserr("drop_privileges: setgroups(1, %d) failed",
+		       (int)emptygidset[0]);
 		rval = EX_OSERR;
+	}
 
 	/* reset primary group and user id */
 	if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
-		rval = EX_OSERR;
-	if ((to_real_uid || RunAsUid != 0) && setuid(RunAsUid) < 0)
+	{
+		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)
+		{
+			syserr("drop_privileges: setuid(%d) failed",
+			       (int)RunAsUid);
+			rval = EX_OSERR;
+		}
+		else if (RunAsUid != 0 && setuid(0) == 0)
+		{
+			/*
+			**  Believe it or not, the Linux capability model
+			**  allows a non-root process to override setuid()
+			**  on a process running as root and prevent that
+			**  process from dropping privileges.
+			*/
+
+			syserr("drop_privileges: setuid(0) succeeded (when it should not)");
+			rval = EX_OSERR;
+		}
+		else if (RunAsUid != euid && setuid(euid) == 0)
+		{
+			/*
+			**  Some operating systems will keep the saved-uid
+			**  if a non-root effective-uid calls setuid(real-uid)
+			**  making it possible to set it back again later.
+			*/
+
+			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());
+			(int)geteuid(), (int)getuid(),
+			(int)getegid(), (int)getgid());
 		dprintf("drop_privileges: RunAsUser = %d:%d\n",
 			(int)RunAsUid, (int)RunAsGid);
+		if (tTd(47, 10))
+			dprintf("drop_privileges: rval = %d\n", rval);
 	}
 	return rval;
 }
@@ -2673,6 +2961,11 @@
 #if _FFR_ADDR_TYPE
 	define(macid("{addr_type}", NULL), "e r", e);
 #endif /* _FFR_ADDR_TYPE */
+
+	/* skip leading spaces */
+	while (*line == ' ')
+		line++;
+
 	switch (line[0])
 	{
 	  case '#':
@@ -2688,7 +2981,7 @@
 		{
 		  case 'D':
 			mid = macid(&line[2], &delimptr);
-			if (mid == '\0')
+			if (mid == 0)
 				return;
 			translate_dollars(delimptr);
 			define(mid, newstr(delimptr), e);
@@ -2699,7 +2992,7 @@
 				return;
 
 			mid = macid(&line[2], &delimptr);
-			if (mid == '\0')
+			if (mid == 0)
 				return;
 			translate_dollars(delimptr);
 			expand(delimptr, exbuf, sizeof exbuf, e);
@@ -2805,12 +3098,12 @@
 		if (line[1] == '=')
 		{
 			mid = macid(&line[2], NULL);
-			if (mid != '\0')
+			if (mid != 0)
 				stabapply(dump_class, mid);
 			return;
 		}
 		mid = macid(&line[1], NULL);
-		if (mid == '\0')
+		if (mid == 0)
 			return;
 		p = macvalue(mid, e);
 		if (p == NULL)
@@ -3075,6 +3368,6 @@
 {
 	if (s->s_type != ST_CLASS)
 		return;
-	if (bitnset(id & 0xff, s->s_class))
+	if (bitnset(bitidx(id), s->s_class))
 		printf("%s\n", s->s_name);
 }
Index: gnu/usr.sbin/sendmail/sendmail/map.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/map.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/sendmail/map.c	2000/04/07 19:20:41	1.2
+++ gnu/usr.sbin/sendmail/sendmail/map.c	2001/05/29 01:31:15	1.5
@@ -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, 1995-1997 Eric P. Allman.  All rights reserved.
  * Copyright (c) 1992, 1993
@@ -12,11 +12,12 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: map.c,v 8.414 2000/03/15 06:13:16 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: map.c,v 8.414.4.53 2001/05/04 01:29:00 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
 
+
 #ifdef NDBM
 # include <ndbm.h>
 # ifdef R_FIRST
@@ -51,7 +52,7 @@
 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[], int));
+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 *));
@@ -240,6 +241,7 @@
 			map->map_mflags |= MF_NODEFER;
 			break;
 
+
 		  case 'S':
 			map->map_spacesub = *++p;
 			break;
@@ -368,7 +370,7 @@
 		/* need to malloc additional space */
 		buflen = len;
 		if (buf != NULL)
-			free(buf);
+			sm_free(buf);
 		buf = xalloc(buflen);
 	}
 
@@ -388,8 +390,8 @@
 			if (c != '%')
 			{
   pushc:
-			        if (--len <= 0)
-				     break;
+				if (--len <= 0)
+					break;
 				*bp++ = c;
 				continue;
 			}
@@ -490,8 +492,9 @@
 	/* if already open, close it (for nested open) */
 	if (bitset(MF_OPEN, map->map_mflags))
 	{
+		map->map_mflags |= MF_CLOSING;
 		map->map_class->map_close(map);
-		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
+		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
 	}
 
 	(void) rebuildaliases(map, FALSE);
@@ -566,6 +569,7 @@
 			map->map_class = &BogusMapClass;
 			map->map_mflags |= MF_OPEN;
 			map->map_pid = getpid();
+			MapOpenErr = TRUE;
 		}
 		else
 		{
@@ -624,6 +628,7 @@
 
 	if (!bitset(MF_VALID, map->map_mflags) ||
 	    !bitset(MF_OPEN, map->map_mflags) ||
+	    bitset(MF_CLOSING, map->map_mflags) ||
 	    map->map_pid != getpid())
 		return;
 
@@ -632,8 +637,9 @@
 			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);
+	map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
 }
 /*
 **  GETCANONNAME -- look up name using service switch
@@ -764,11 +770,10 @@
 
 #if NAMED_BIND
 	if (got_tempfail)
-		h_errno = TRY_AGAIN;
+		SM_SET_H_ERRNO(TRY_AGAIN);
 	else
-		h_errno = HOST_NOT_FOUND;
+		SM_SET_H_ERRNO(HOST_NOT_FOUND);
 #endif /* NAMED_BIND */
-
 	return FALSE;
 }
 /*
@@ -776,6 +781,7 @@
 **
 **	Parameters:
 **		name -- the name against which to match.
+**		dot -- where to reinsert '.' to get FQDN
 **		line -- the /etc/hosts line.
 **		cbuf -- the location to store the result.
 **		cbuflen -- the size of cbuf.
@@ -786,8 +792,9 @@
 */
 
 static bool
-extract_canonname(name, line, cbuf, cbuflen)
+extract_canonname(name, dot, line, cbuf, cbuflen)
 	char *name;
+	char *dot;
 	char *line;
 	char cbuf[];
 	int cbuflen;
@@ -816,6 +823,14 @@
 		}
 		if (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;
+			*dot = '\0';
+		}
 	}
 	if (found && strchr(cbuf, '.') == NULL)
 	{
@@ -823,7 +838,7 @@
 		char *domain = macvalue('m', CurEnv);
 
 		if (domain != NULL &&
-		    strlen(domain) + (i = strlen(cbuf)) + 1 < cbuflen)
+		    strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
 		{
 			p = &cbuf[i];
 			*p++ = '.';
@@ -1067,7 +1082,7 @@
 	**  map_mtime to be set
 	*/
 
-	if (fstat(dfd, &st) >= 0)
+	if (fstat(pfd, &st) >= 0)
 		map->map_mtime = st.st_mtime;
 
 	if (mode == O_RDONLY)
@@ -1087,7 +1102,7 @@
 		map->map_mflags |= MF_LOCKED;
 		if (geteuid() == 0 && TrustedUid != 0)
 		{
-# if HASFCHOWN
+#  if HASFCHOWN
 			if (fchown(dfd, TrustedUid, -1) < 0 ||
 			    fchown(pfd, TrustedUid, -1) < 0)
 			{
@@ -1099,7 +1114,7 @@
 				message("050 ownership change on %s failed: %s",
 					map->map_file, errstring(err));
 			}
-# endif /* HASFCHOWN */
+#  endif /* HASFCHOWN */
 		}
 	}
 	return TRUE;
@@ -1118,7 +1133,7 @@
 	int *statp;
 {
 	datum key, val;
-	int fd;
+	int dfd, pfd;
 	char keybuf[MAXNAME + 1];
 	struct stat stbuf;
 
@@ -1138,19 +1153,22 @@
 		key.dptr = keybuf;
 	}
 lockdbm:
-	fd = dbm_dirfno((DBM *) map->map_db1);
-	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
-		(void) lockfile(fd, map->map_file, ".dir", LOCK_SH);
-	if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
+	dfd = dbm_dirfno((DBM *) map->map_db1);
+	if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
+		(void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
+	pfd = dbm_pagfno((DBM *) map->map_db1);
+	if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
+	    stbuf.st_mtime > map->map_mtime)
 	{
 		/* Reopen the database to sync the cache */
 		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
 								 : O_RDONLY;
 
-		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
-			(void) lockfile(fd, map->map_file, ".dir", LOCK_UN);
+		if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
+			(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
+		map->map_mflags |= MF_CLOSING;
 		map->map_class->map_close(map);
-		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
+		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
 		if (map->map_class->map_open(map, omode))
 		{
 			map->map_mflags |= MF_OPEN;
@@ -1189,8 +1207,8 @@
 		if (val.dptr != NULL)
 			map->map_mflags &= ~MF_TRY0NULL;
 	}
-	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
-		(void) lockfile(fd, map->map_file, ".dir", LOCK_UN);
+	if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
+		(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
 	if (val.dptr == NULL)
 		return NULL;
 	if (bitset(MF_MATCHONLY, map->map_mflags))
@@ -1260,7 +1278,7 @@
 				if (data.dsize + old.dsize + 2 > bufsiz)
 				{
 					if (buf != NULL)
-						(void) free(buf);
+						sm_free(buf);
 					bufsiz = data.dsize + old.dsize + 2;
 					buf = xalloc(bufsiz);
 				}
@@ -1620,6 +1638,10 @@
 			ret = db->open(db, buf, NULL, dbtype, flags, DBMMODE);
 			if (ret != 0)
 			{
+#ifdef DB_OLD_VERSION
+				if (ret == DB_OLD_VERSION)
+					ret = EINVAL;
+#endif /* DB_OLD_VERSION */
 				(void) db->close(db, 0);
 				db = NULL;
 			}
@@ -1694,7 +1716,7 @@
 		(void) db->sync(db, 0);
 		if (geteuid() == 0 && TrustedUid != 0)
 		{
-# if HASFCHOWN
+#  if HASFCHOWN
 			if (fchown(fd, TrustedUid, -1) < 0)
 			{
 				int err = errno;
@@ -1705,7 +1727,7 @@
 				message("050 ownership change on %s failed: %s",
 					buf, errstring(err));
 			}
-# endif /* HASFCHOWN */
+#  endif /* HASFCHOWN */
 		}
 	}
 
@@ -1787,8 +1809,9 @@
 
 		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
 			(void) lockfile(fd, buf, ".db", LOCK_UN);
+		map->map_mflags |= MF_CLOSING;
 		map->map_class->map_close(map);
-		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
+		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
 		if (map->map_class->map_open(map, omode))
 		{
 			map->map_mflags |= MF_OPEN;
@@ -1964,10 +1987,10 @@
 			if (old.data != NULL)
 			{
 				old.size = strlen(old.data);
-				if (data.size + old.size + 2 > bufsiz)
+				if (data.size + old.size + 2 > (size_t)bufsiz)
 				{
 					if (buf != NULL)
-						(void) free(buf);
+						sm_free(buf);
 					bufsiz = data.size + old.size + 2;
 					buf = xalloc(bufsiz);
 				}
@@ -2125,7 +2148,7 @@
 		dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
 			map->map_domain, map->map_file, yperr_string(yperr));
 	if (vp != NULL)
-		free(vp);
+		sm_free(vp);
 
 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
 	{
@@ -2195,7 +2218,7 @@
 	{
 		if (vp != NULL)
 		{
-			free(vp);
+			sm_free(vp);
 			vp = NULL;
 		}
 		buflen++;
@@ -2209,7 +2232,7 @@
 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
 		if (vp != NULL)
-			free(vp);
+			sm_free(vp);
 		return NULL;
 	}
 	if (bitset(MF_MATCHONLY, map->map_mflags))
@@ -2220,7 +2243,7 @@
 
 		ret = map_rewrite(map, vp, vsize, av);
 		if (vp != NULL)
-			free(vp);
+			sm_free(vp);
 		return ret;
 	}
 }
@@ -2255,7 +2278,7 @@
 		*statp = EX_UNAVAILABLE;
 		return FALSE;
 	}
-	shorten_hostname(nbuf);
+	(void) shorten_hostname(nbuf);
 	keylen = strlen(nbuf);
 
 	if (yp_domain == NULL)
@@ -2274,7 +2297,7 @@
 	{
 		if (vp != NULL)
 		{
-			free(vp);
+			sm_free(vp);
 			vp = NULL;
 		}
 		keylen++;
@@ -2292,20 +2315,20 @@
 		else
 			*statp = EX_UNAVAILABLE;
 		if (vp != NULL)
-			free(vp);
+			sm_free(vp);
 		return FALSE;
 	}
 	(void) strlcpy(host_record, vp, sizeof host_record);
-	free(vp);
+	sm_free(vp);
 	if (tTd(38, 44))
 		dprintf("got record `%s'\n", host_record);
-	if (!extract_canonname(nbuf, host_record, cbuf, sizeof cbuf))
+	if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof cbuf))
 	{
 		/* this should not happen, but.... */
 		*statp = EX_NOHOST;
 		return FALSE;
 	}
-	if (hbsize < strlen(cbuf))
+	if (hbsize <= strlen(cbuf))
 	{
 		*statp = EX_UNAVAILABLE;
 		return FALSE;
@@ -2437,7 +2460,7 @@
 	/* verify the key column exist */
 	for (i = 0; i< max_col; i++)
 	{
-		if (!strcmp(map->map_keycolnm, COL_NAME(res,i)))
+		if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
 			break;
 	}
 	if (i == max_col)
@@ -2636,7 +2659,7 @@
 		return FALSE;
 	}
 	(void) strlcpy(nbuf, name, sizeof nbuf);
-	shorten_hostname(nbuf);
+	(void) shorten_hostname(nbuf);
 
 	p = strchr(nbuf, '.');
 	if (p == NULL)
@@ -2807,7 +2830,7 @@
 	STAB *s;
 
 	if (tTd(38, 2))
-		dprintf("ldapmap_open(%s, %d)\n", map->map_mname, mode);
+		dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
 
 	mode &= O_ACCMODE;
 
@@ -2834,19 +2857,29 @@
 	lmap = (LDAPMAP_STRUCT *) map->map_db1;
 
 	s = ldapmap_findconn(lmap);
-	if (s->s_ldap != NULL)
+	if (s->s_lmap != NULL)
 	{
 		/* Already have a connection open to this LDAP server */
-		lmap->ldap_ld = s->s_ldap;
+		lmap->ldap_ld = ((LDAPMAP_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;
 	}
 
+	if (tTd(38, 2))
+		dprintf("opening new connection\n");
+
 	/* No connection yet, connect */
 	if (!ldapmap_start(map))
 		return FALSE;
 
 	/* Save connection for reuse */
-	s->s_ldap = lmap->ldap_ld;
+	s->s_lmap = map;
 	return TRUE;
 }
 
@@ -2887,6 +2920,7 @@
 
 # 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
@@ -2969,6 +3003,7 @@
 	}
 # endif /* USE_LDAP_INIT */
 
+# ifdef LDAP_AUTH_KRBV4
 	if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
 	    lmap->ldap_secret != NULL)
 	{
@@ -2980,6 +3015,7 @@
 
 		(void) putenv(lmap->ldap_secret);
 	}
+# endif /* LDAP_AUTH_KRBV4 */
 
 	bind_result = ldap_bind_s(ld, lmap->ldap_binddn,
 				  lmap->ldap_secret, lmap->ldap_method);
@@ -3012,6 +3048,13 @@
 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);
 }
 
@@ -3026,26 +3069,34 @@
 	LDAPMAP_STRUCT *lmap;
 	STAB *s;
 
+	if (tTd(38, 2))
+		dprintf("ldapmap_close(%s)\n", map->map_mname);
+
 	lmap = (LDAPMAP_STRUCT *) map->map_db1;
 
 	/* Check if already closed */
 	if (lmap->ldap_ld == NULL)
 		return;
 
+	/* Close the LDAP connection */
+	ldap_unbind(lmap->ldap_ld);
+
+	/* Mark all the maps that share the connection as closed */
 	s = ldapmap_findconn(lmap);
 
-	/* Check if already closed */
-	if (s->s_ldap == NULL)
-		return;
+	while (s->s_lmap != NULL)
+	{
+		MAP *smap = s->s_lmap;
 
-	/* If same as saved connection, stored connection is going away */
-	if (s->s_ldap == lmap->ldap_ld)
-		s->s_ldap = NULL;
+		if (tTd(38, 2) && smap != map)
+			dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
+				map->map_mname, smap->map_mname);
 
-	if (lmap->ldap_ld != NULL)
-	{
-		ldap_unbind(lmap->ldap_ld);
+		smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
+		lmap = (LDAPMAP_STRUCT *) smap->map_db1;
 		lmap->ldap_ld = NULL;
+		s->s_lmap = lmap->ldap_next;
+		lmap->ldap_next = NULL;
 	}
 }
 
@@ -3133,7 +3184,7 @@
 		if (q[1] == 's')
 		{
 			snprintf(fp, SPACELEFT(filter, fp), "%.*s%s",
-				 q - p, p, keybuf);
+				 (int) (q - p), p, keybuf);
 			fp += strlen(fp);
 			p = q + 2;
 		}
@@ -3142,7 +3193,7 @@
 			char *k = keybuf;
 
 			snprintf(fp, SPACELEFT(filter, fp), "%.*s",
-				 q - p, p);
+				 (int) (q - p), p);
 			fp += strlen(fp);
 			p = q + 2;
 
@@ -3170,7 +3221,7 @@
 		else
 		{
 			snprintf(fp, SPACELEFT(filter, fp), "%.*s",
-				 q - p + 1, p);
+				 (int) (q - p + 1), p);
 			p = q + (q[1] == '%' ? 2 : 1);
 			fp += strlen(fp);
 		}
@@ -3187,17 +3238,29 @@
 			    lmap->ldap_attrsonly);
 	if (msgid == -1)
 	{
+		int save_errno;
+
 		errno = ldapmap_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_st using %s in map %s",
+				syserr("Error in ldap_search using %s in map %s",
 				       filter, map->map_mname);
 			else
-				syserr("421 4.0.0 Error in ldap_search_st using %s in map %s",
+				syserr("421 4.0.0 Error in ldap_search using %s in map %s",
 				       filter, map->map_mname);
 		}
 		*statp = EX_TEMPFAIL;
+#ifdef LDAP_SERVER_DOWN
+		errno = save_errno;
+		if (errno == LDAP_SERVER_DOWN + E_LDAPBASE)
+		{
+			/* server disappeared, try reopen on next search */
+			ldapmap_close(map);
+		}
+#endif /* LDAP_SERVER_DOWN */
+		errno = save_errno;
 		return NULL;
 	}
 
@@ -3227,13 +3290,17 @@
 				}
 				(void) ldap_abandon(lmap->ldap_ld, msgid);
 				if (vp != NULL)
-					free(vp);
+					sm_free(vp);
 				if (tTd(38, 25))
 					dprintf("ldap search found multiple on a single match query\n");
 				return NULL;
 			}
 		}
 
+		/* If we don't want multiple values and we have one, break */
+		if (map->map_coldelim == '\0' && vp != NULL)
+			break;
+
 		/* Cycle through all entries */
 		for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
 		     entry != NULL;
@@ -3241,7 +3308,7 @@
 		{
 			BerElement *ber;
 			char *attr;
-			char **vals;
+			char **vals = NULL;
 
 			/*
 			**  If matching only and found an entry,
@@ -3252,6 +3319,16 @@
 			    bitset(MF_MATCHONLY, map->map_mflags))
 				continue;
 
+# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
+			/*
+			**  Reset value to prevent lingering
+			**  LDAP_DECODING_ERROR due to
+			**  OpenLDAP 1.X's hack (see below)
+			*/
+
+			lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
+# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
+
 			for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
 							 &ber);
 			     attr != NULL;
@@ -3260,46 +3337,60 @@
 			{
 				char *tmp, *vp_tmp;
 
-				vals = ldap_get_values(lmap->ldap_ld, entry,
-						       attr);
-				if (vals == NULL)
+				if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
 				{
-					errno = ldapmap_geterrno(lmap->ldap_ld);
-					if (errno == LDAP_SUCCESS)
-						continue;
-
-					/* Must be an error */
-					errno += E_LDAPBASE;
-					if (!bitset(MF_OPTIONAL,
-						    map->map_mflags))
+					vals = ldap_get_values(lmap->ldap_ld,
+							       entry,
+							       attr);
+					if (vals == NULL)
 					{
-						if (bitset(MF_NODEFER,
-							   map->map_mflags))
-							syserr("Error getting LDAP values in map %s",
-							       map->map_mname);
-						else
-							syserr("421 4.0.0 Error getting LDAP values in map %s",
-							       map->map_mname);
-					}
-					*statp = EX_TEMPFAIL;
+						errno = ldapmap_geterrno(lmap->ldap_ld);
+						if (errno == LDAP_SUCCESS)
+							continue;
+
+						/* Must be an error */
+						errno += E_LDAPBASE;
+						if (!bitset(MF_OPTIONAL,
+							    map->map_mflags))
+						{
+							if (bitset(MF_NODEFER,
+								   map->map_mflags))
+								syserr("Error getting LDAP values in map %s",
+								       map->map_mname);
+							else
+								syserr("421 4.0.0 Error getting LDAP values in map %s",
+								       map->map_mname);
+						}
+						*statp = EX_TEMPFAIL;
 # if USING_NETSCAPE_LDAP
-					ldap_mem_free(attr);
+						ldap_memfree(attr);
 # endif /* USING_NETSCAPE_LDAP */
-					if (lmap->ldap_res != NULL)
-					{
-						ldap_msgfree(lmap->ldap_res);
-						lmap->ldap_res = NULL;
+						if (lmap->ldap_res != NULL)
+						{
+							ldap_msgfree(lmap->ldap_res);
+							lmap->ldap_res = NULL;
+						}
+						(void) ldap_abandon(lmap->ldap_ld,
+								    msgid);
+						if (vp != NULL)
+							sm_free(vp);
+						return NULL;
 					}
-					(void) ldap_abandon(lmap->ldap_ld,
-							    msgid);
-					if (vp != NULL)
-						free(vp);
-					return NULL;
 				}
 
 				*statp = EX_OK;
 
+# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
 				/*
+				**  Reset value to prevent lingering
+				**  LDAP_DECODING_ERROR due to
+				**  OpenLDAP 1.X's hack (see below)
+				*/
+
+				lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
+# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
+
+				/*
 				**  If matching only,
 				**  no need to spin through entries
 				*/
@@ -3314,11 +3405,20 @@
 
 				if (map->map_coldelim == '\0')
 				{
+					if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
+					{
+						vp = newstr(attr);
+# if USING_NETSCAPE_LDAP
+						ldap_memfree(attr);
+# endif /* USING_NETSCAPE_LDAP */
+						break;
+					}
+
 					if (vals[0] == NULL)
 					{
 						ldap_value_free(vals);
 # if USING_NETSCAPE_LDAP
-						ldap_mem_free(attr);
+						ldap_memfree(attr);
 # endif /* USING_NETSCAPE_LDAP */
 						continue;
 					}
@@ -3326,11 +3426,33 @@
 					vp = newstr(vals[0]);
 					ldap_value_free(vals);
 # if USING_NETSCAPE_LDAP
-					ldap_mem_free(attr);
+					ldap_memfree(attr);
 # endif /* USING_NETSCAPE_LDAP */
 					break;
 				}
 
+				/* attributes only */
+				if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
+				{
+					if (vp == NULL)
+						vp = newstr(attr);
+					else
+					{
+						vsize = strlen(vp) +
+							strlen(attr) + 2;
+						tmp = xalloc(vsize);
+						snprintf(tmp, vsize, "%s%c%s",
+							 vp, map->map_coldelim,
+							 attr);
+						sm_free(vp);
+						vp = tmp;
+					}
+# if USING_NETSCAPE_LDAP
+					ldap_memfree(attr);
+# endif /* USING_NETSCAPE_LDAP */
+					continue;
+				}
+
 				/*
 				**  If there is more than one,
 				**  munge then into a map_coldelim
@@ -3356,7 +3478,7 @@
 
 				ldap_value_free(vals);
 # if USING_NETSCAPE_LDAP
-				ldap_mem_free(attr);
+				ldap_memfree(attr);
 # endif /* USING_NETSCAPE_LDAP */
 				if (vp == NULL)
 				{
@@ -3368,8 +3490,8 @@
 				snprintf(tmp, vsize, "%s%c%s",
 					 vp, map->map_coldelim, vp_tmp);
 
-				free(vp);
-				free(vp_tmp);
+				sm_free(vp);
+				sm_free(vp_tmp);
 				vp = tmp;
 			}
 			errno = ldapmap_geterrno(lmap->ldap_ld);
@@ -3405,7 +3527,7 @@
 				}
 				(void) ldap_abandon(lmap->ldap_ld, msgid);
 				if (vp != NULL)
-					free(vp);
+					sm_free(vp);
 				return NULL;
 			}
 
@@ -3414,7 +3536,7 @@
 				break;
 		}
 		errno = ldapmap_geterrno(lmap->ldap_ld);
-		if (errno != LDAP_SUCCESS)
+		if (errno != LDAP_SUCCESS && errno != LDAP_DECODING_ERROR)
 		{
 			/* Must be an error */
 			errno += E_LDAPBASE;
@@ -3435,15 +3557,11 @@
 			}
 			(void) ldap_abandon(lmap->ldap_ld, msgid);
 			if (vp != NULL)
-				free(vp);
+				sm_free(vp);
 			return NULL;
 		}
 		ldap_msgfree(lmap->ldap_res);
 		lmap->ldap_res = NULL;
-
-		/* If we don't want multiple values and we have one, break */
-		if (map->map_coldelim == '\0' && vp != NULL)
-			break;
 	}
 
 	/*
@@ -3464,7 +3582,7 @@
 				lmap->ldap_res = NULL;
 			}
 			if (vp != NULL)
-				free(vp);
+				sm_free(vp);
 			return NULL;
 		}
 		*statp = EX_OK;
@@ -3476,9 +3594,13 @@
 		errno = ldapmap_geterrno(lmap->ldap_ld);
 	if (errno != LDAP_SUCCESS)
 	{
+		int save_errno;
+
 		/* Must be an error */
 		if (ret != 0)
 			errno += E_LDAPBASE;
+		save_errno = errno;
+
 		if (!bitset(MF_OPTIONAL, map->map_mflags))
 		{
 			if (bitset(MF_NODEFER, map->map_mflags))
@@ -3490,12 +3612,21 @@
 		}
 		*statp = EX_TEMPFAIL;
 		if (vp != NULL)
-			free(vp);
+			sm_free(vp);
+#ifdef LDAP_SERVER_DOWN
+		errno = save_errno;
+		if (errno == LDAP_SERVER_DOWN + E_LDAPBASE)
+		{
+			/* server disappeared, try reopen on next search */
+			ldapmap_close(map);
+		}
+#endif /* LDAP_SERVER_DOWN */
+		errno = save_errno;
 		return NULL;
 	}
 
 	/* Did we match anything? */
-	if (vp == NULL)
+	if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
 		return NULL;
 
 	/*
@@ -3507,22 +3638,26 @@
 
 	if (bitset(MF_NOREWRITE, map->map_mflags))
 	{
-		/* vp != NULL due to test above */
-		free(vp);
+		if (vp != NULL)
+			sm_free(vp);
 		return "";
 	}
 
 	if (*statp == EX_OK)
 	{
-		/* vp != NULL due to test above */
 		if (LogLevel > 9)
 			sm_syslog(LOG_INFO, CurEnv->e_id,
-				  "ldap %.100s => %s", name, vp);
+				  "ldap %.100s => %s", name,
+				  vp == NULL ? "<NULL>" : vp);
 		if (bitset(MF_MATCHONLY, map->map_mflags))
 			result = map_rewrite(map, name, strlen(name), NULL);
 		else
+		{
+			/* vp != NULL according to test above */
 			result = map_rewrite(map, vp, strlen(vp), av);
-		free(vp);
+		}
+		if (vp != NULL)
+			sm_free(vp);
 	}
 	return result;
 }
@@ -3531,8 +3666,10 @@
 **  LDAPMAP_FINDCONN -- find an LDAP connection to the server
 **
 **	Cache LDAP connections based on the host, port, bind DN,
-**	and secret so we don't have multiple connections open to
-**	the same server for different maps.
+**	secret, and PID so we don't have multiple connections open to
+**	the same server for different maps.  Need a separate connection
+**	per PID since a parent process may close the map before the
+**	child is done with it.
 **
 **	Parameters:
 **		lmap -- LDAP map information
@@ -3555,18 +3692,19 @@
 		(lmap->ldap_binddn == NULL ? 0 : strlen(lmap->ldap_binddn)) +
 		1 +
 		(lmap->ldap_secret == NULL ? 0 : strlen(lmap->ldap_secret)) +
-		1;
+		8 + 1;
 	nbuf = xalloc(len);
-	snprintf(nbuf, len, "%s%c%d%c%s%c%s",
+	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));
-	s = stab(nbuf, ST_LDAP, ST_ENTER);
-	free(nbuf);
+		 (lmap->ldap_secret == NULL ? "" : lmap->ldap_secret),
+		 (int) getpid());
+	s = stab(nbuf, ST_LMAP, ST_ENTER);
+	sm_free(nbuf);
 	return s;
 }
 /*
@@ -3661,7 +3799,9 @@
 {
 	{	"none",		LDAP_AUTH_NONE		},
 	{	"simple",	LDAP_AUTH_SIMPLE	},
+# ifdef LDAP_AUTH_KRBV4
 	{	"krbv4",	LDAP_AUTH_KRBV4		},
+# endif /* LDAP_AUTH_KRBV4 */
 	{	NULL,		0			}
 };
 
@@ -3848,7 +3988,7 @@
 
 					if ((ptr = strchr(p, ' ')) != NULL)
 						*ptr = '\0';
-					syserr("Deref must be [never|always|search|find] not %s in map %s",
+					syserr("Deref must be [never|always|search|find] (not %s) in map %s",
 						p, map->map_mname);
 					if (ptr != NULL)
 						*ptr = ' ';
@@ -3883,7 +4023,7 @@
 
 					if ((ptr = strchr(p, ' ')) != NULL)
 						*ptr = '\0';
-					syserr("Scope must be [base|one|sub] not %s in map %s",
+					syserr("Scope must be [base|one|sub] (not %s) in map %s",
 						p, map->map_mname);
 					if (ptr != NULL)
 						*ptr = ' ';
@@ -3955,7 +4095,7 @@
 
 					if ((ptr = strchr(p, ' ')) != NULL)
 						*ptr = '\0';
-					syserr("Method for binding must be [none|simple|krbv4] not %s in map %s",
+					syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
 						p, map->map_mname);
 					if (ptr != NULL)
 						*ptr = ' ';
@@ -4061,7 +4201,8 @@
 				return FALSE;
 			}
 			lmap->ldap_secret = sfgets(m_tmp, LDAPMAP_MAX_PASSWD,
-						   sfd, 0, "ldapmap_parseargs");
+						   sfd, TimeOuts.to_fileopen,
+						   "ldapmap_parseargs");
 			(void) fclose(sfd);
 			if (lmap->ldap_secret != NULL &&
 			    strlen(m_tmp) > 0)
@@ -4074,6 +4215,7 @@
 			}
 			break;
 
+# ifdef LDAP_AUTH_KRBV4
 		  case LDAP_AUTH_KRBV4:
 
 			/*
@@ -4086,6 +4228,7 @@
 				 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");
@@ -4151,7 +4294,7 @@
 			if (p != NULL)
 				*p++ = '\0';
 
-			if (i == LDAPMAP_MAX_ATTR)
+			if (i >= LDAPMAP_MAX_ATTR)
 			{
 				syserr("Too many return attributes in %s (max %d)",
 				       map->map_mname, LDAPMAP_MAX_ATTR);
@@ -4204,6 +4347,7 @@
 	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
@@ -4220,6 +4364,7 @@
 ldapmap_set_defaults(spec)
 	char *spec;
 {
+	STAB *class;
 	MAP map;
 
 	/* Allocate and set the default values */
@@ -4228,7 +4373,17 @@
 	ldapmap_clear(LDAPDefaults);
 
 	memset(&map, '\0', sizeof map);
+
+	/* look up the class */
+	class = stab("ldap", ST_MAPCLASS, ST_FIND);
+	if (class == NULL)
+	{
+		syserr("readcf: LDAPDefaultSpec: class ldap not available");
+		return;
+	}
+	map.map_class = &class->s_mapclass;
 	map.map_db1 = (ARBPTR_T) LDAPDefaults;
+	map.map_mname = "O LDAPDefaultSpec";
 
 	(void) ldapmap_parseargs(&map, spec);
 
@@ -4241,12 +4396,12 @@
 		syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
 		if (map.map_app != NULL)
 		{
-			free(map.map_app);
+			sm_free(map.map_app);
 			map.map_app = NULL;
 		}
 		if (map.map_tapp != NULL)
 		{
-			free(map.map_tapp);
+			sm_free(map.map_tapp);
 			map.map_tapp = NULL;
 		}
 	}
@@ -4478,9 +4633,16 @@
 
 /* ARGSUSED */
 static void
-ph_timeout_func(sig_no)
-	int sig_no;
+ph_timeout(sig)
+	int sig;
 {
+	/*
+	**  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(PHTimeout, 1);
 }
 #else /* _FFR_PHMAP_TIMEOUT */
@@ -4539,11 +4701,29 @@
 		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);
+
+		/*
+		** 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;
+	}
+
 	pmap = (PH_MAP_STRUCT *)map->map_db1;
 
 	hostlist = newstr(pmap->ph_servers);
 	tmp = strtok(hostlist, " ");
-	do {
+	do
+	{
 #if _FFR_PHMAP_TIMEOUT
 		if (pmap->ph_timeout != 0)
 		{
@@ -4557,11 +4737,11 @@
 # ifdef ETIMEDOUT
 				errno = ETIMEDOUT;
 # else /* ETIMEDOUT */
-				errno = 0;
+				errno = EAGAIN;
 # endif /* ETIMEDOUT */
 				goto ph_map_open_abort;
 			}
-			ev = setevent(pmap->ph_timeout, ph_timeout_func, 0);
+			ev = setevent(pmap->ph_timeout, ph_timeout, 0);
 		}
 		if (!OpenQiSock(tmp, &(pmap->ph_sockfd)) &&
 		    !Sock2FILEs(pmap->ph_sockfd, &(pmap->ph_to_server),
@@ -4580,7 +4760,7 @@
 		{
 			if (fprintf(pmap->ph_to_server,
 				    "id sendmail+phmap\n") < 0 ||
-			    fflush(pmap->ph_to_server) < 0 ||
+			    fflush(pmap->ph_to_server) != 0 ||
 			    (server_data = ReadQi(pmap->ph_from_server,
 						  &j)) == NULL ||
 			    server_data->code != 200)
@@ -4593,7 +4773,7 @@
 			if (server_data != NULL)
 				FreeQIR(server_data);
 #endif /* _FFR_PHMAP_TIMEOUT */
-			free(hostlist);
+			sm_free(hostlist);
 			return TRUE;
 		}
 #if _FFR_PHMAP_TIMEOUT
@@ -4614,16 +4794,18 @@
 #if !_FFR_PHMAP_TIMEOUT
 	errno = save_errno;
 #endif /* !_FFR_PHMAP_TIMEOUT */
-	if (!bitset(MF_OPTIONAL, map->map_mflags))
+	if (bitset(MF_NODEFER, map->map_mflags))
 	{
-		if (errno == 0 && !bitset(MF_NODEFER,map->map_mflags))
+		if (errno == 0)
 			errno = EAGAIN;
-		syserr("ph_map_open: cannot connect to PH server");
+		syserr("ph_map_open: %s: cannot connect to PH server",
+		       map->map_mname);
 	}
-	else if (LogLevel > 1)
+	else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
 		sm_syslog(LOG_NOTICE, CurEnv->e_id,
-			  "ph_map_open: cannot connect to PH server");
-	free(hostlist);
+			  "ph_map_open: %s: cannot connect to PH server",
+			  map->map_mname);
+	sm_free(hostlist);
 	return FALSE;
 }
 
@@ -4678,13 +4860,14 @@
 			*pstat = EX_TEMPFAIL;
 			goto ph_map_lookup_abort;
 		}
-		ev = setevent(pmap->ph_timeout, ph_timeout_func, 0);
+		ev = setevent(pmap->ph_timeout, ph_timeout, 0);
 	}
 
 #endif /* _FFR_PHMAP_TIMEOUT */
 	/* check all relevant fields */
 	tmp = pmap->ph_field_list;
-	do {
+	do
+	{
 #if _FFR_PHMAP_TIMEOUT
 		server_data = NULL;
 #endif /* _FFR_PHMAP_TIMEOUT */
@@ -4746,7 +4929,7 @@
 		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)
+		else if (fflush(pmap->ph_to_server) != 0)
 			message = "qi fflush failed";
 		else if ((server_data = ReadQi(pmap->ph_from_server,
 					       &j)) == NULL)
@@ -5134,6 +5317,7 @@
 	{
 		char *np;
 		int nl;
+		int save_errno;
 		char nbuf[MAXNAME];
 
 		nl = strlen(name);
@@ -5148,8 +5332,10 @@
 # else /* HESIOD_INIT */
 		hp = hes_resolve(np, map->map_file);
 # endif /* HESIOD_INIT */
+		save_errno = errno;
 		if (np != nbuf)
-			free(np);
+			sm_free(np);
+		errno = save_errno;
 	}
 	else
 	{
@@ -5160,11 +5346,8 @@
 # endif /* HESIOD_INIT */
 	}
 # ifdef HESIOD_INIT
-	if (hp == NULL)
-		return NULL;
-	if (*hp == NULL)
+	if (hp == NULL || *hp == NULL)
 	{
-		hesiod_free_list(HesiodContext, hp);
 		switch (errno)
 		{
 		  case ENOENT:
@@ -5179,6 +5362,7 @@
 			  *statp = EX_UNAVAILABLE;
 			  break;
 		}
+		hesiod_free_list(HesiodContext, hp);
 		return NULL;
 	}
 # else /* HESIOD_INIT */
@@ -5276,7 +5460,7 @@
 		res = map_rewrite(map, name, strlen(name), NULL);
 	else
 		res = map_rewrite(map, propval, strlen(propval), av);
-	free(propval);
+	sm_free(propval);
 	return res;
 }
 
@@ -5299,7 +5483,7 @@
 		*statp = EX_UNAVAILABLE;
 		return FALSE;
 	}
-	shorten_hostname(nbuf);
+	(void) shorten_hostname(nbuf);
 
 	/* we only accept single token search key */
 	if (strchr(nbuf, '.'))
@@ -5324,12 +5508,12 @@
 	if (hbsize >= strlen(vptr))
 	{
 		(void) strlcpy(name, vptr, hbsize);
-		free(vptr);
+		sm_free(vptr);
 		*statp = EX_OK;
 		return TRUE;
 	}
 	*statp = EX_UNAVAILABLE;
-	free(vptr);
+	sm_free(vptr);
 	return FALSE;
 }
 
@@ -5719,6 +5903,7 @@
 	int *statp;
 {
 	bool found;
+	char *dot;
 	FILE *f;
 	char linebuf[MAXLINE];
 	char cbuf[MAXNAME + 1];
@@ -5733,7 +5918,7 @@
 		return FALSE;
 	}
 	(void) strlcpy(nbuf, name, sizeof nbuf);
-	shorten_hostname(nbuf);
+	dot = shorten_hostname(nbuf);
 
 	f = fopen(HostsFile, "r");
 	if (f == NULL)
@@ -5749,7 +5934,8 @@
 		if (p != NULL)
 			*p = '\0';
 		if (linebuf[0] != '\0')
-			found = extract_canonname(nbuf, linebuf, cbuf, sizeof cbuf);
+			found = extract_canonname(nbuf, dot, linebuf,
+						  cbuf, sizeof cbuf);
 	}
 	(void) fclose(f);
 	if (!found)
@@ -6213,7 +6399,7 @@
 		if (bitset(MF_MATCHONLY, map->map_mflags))
 			rval = map_rewrite(map, name, strlen(name), NULL);
 		else
-			rval = map_rewrite(map, buf, strlen(buf), NULL);
+			rval = map_rewrite(map, buf, strlen(buf), av);
 
 		/* now flush any additional output */
 		while ((i = read(fd, buf, sizeof buf)) > 0)
@@ -6404,8 +6590,9 @@
 
 		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
 			continue;
+		mm->map_mflags |= MF_CLOSING;
 		mm->map_class->map_close(mm);
-		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
+		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
 	}
 }
 
@@ -6611,7 +6798,7 @@
 
 struct regex_map
 {
-	regex_t	regex_pattern_buf;	/* xalloc it */
+	regex_t	*regex_pattern_buf;	/* xalloc it */
 	int	*regex_subfields;	/* move to type MAP */
 	char	*regex_delim;		/* move to type MAP */
 };
@@ -6688,6 +6875,7 @@
 	p = ap;
 
 	map_p = (struct regex_map *) xnalloc(sizeof *map_p);
+	map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
 
 	for (;;)
 	{
@@ -6744,15 +6932,16 @@
 	if (tTd(38, 3))
 		dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
 
-	if ((regerr = regcomp(&(map_p->regex_pattern_buf), p, pflags)) != 0)
+	if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
 	{
 		/* Errorhandling */
 		char errbuf[ERRBUF_SIZE];
 
-		(void) regerror(regerr, &(map_p->regex_pattern_buf),
+		(void) regerror(regerr, map_p->regex_pattern_buf,
 			 errbuf, ERRBUF_SIZE);
 		syserr("pattern-compile-error: %s\n", errbuf);
-		free(map_p);
+		sm_free(map_p->regex_pattern_buf);
+		sm_free(map_p);
 		return FALSE;
 	}
 
@@ -6769,7 +6958,7 @@
 		int substrings;
 		int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
 
-		substrings = map_p->regex_pattern_buf.re_nsub + 1;
+		substrings = map_p->regex_pattern_buf->re_nsub + 1;
 
 		if (tTd(38, 3))
 			dprintf("regex_map_init: nr of substrings %d\n",
@@ -6778,7 +6967,8 @@
 		if (substrings >= MAX_MATCH)
 		{
 			syserr("too many substrings, %d max\n", MAX_MATCH);
-			free(map_p);
+			sm_free(map_p->regex_pattern_buf);
+			sm_free(map_p);
 			return FALSE;
 		}
 		if (sub_param != NULL && sub_param[0] != '\0')
@@ -6823,7 +7013,7 @@
 	if (bitset(MF_MATCHONLY, map->map_mflags))
 		return map_rewrite(map, av[0], strlen(av[0]), NULL);
 	else
-		return map_rewrite(map, s, slen, NULL);
+		return map_rewrite(map, s, slen, av);
 }
 
 char *
@@ -6847,7 +7037,7 @@
 	}
 
 	map_p = (struct regex_map *)(map->map_db1);
-	reg_res = regexec(&(map_p->regex_pattern_buf),
+	reg_res = regexec(map_p->regex_pattern_buf,
 			  name, MAX_MATCH, pmatch, 0);
 
 	if (bitset(MF_REGEX_NOT, map->map_mflags))
@@ -6879,7 +7069,7 @@
 		if (av[1] != NULL)
 		{
 			if (parse_fields(av[1], fields, MAX_MATCH + 1,
-					 (int) map_p->regex_pattern_buf.re_nsub + 1) == -1)
+					 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
 			{
 				*statp = EX_CONFIG;
 				return NULL;
@@ -6903,7 +7093,8 @@
 				first = FALSE;
 
 
-			if (pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
+			if (*ip >= MAX_MATCH ||
+			    pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
 				continue;
 
 			sp = name + pmatch[*ip].rm_so;
@@ -7020,7 +7211,7 @@
 	char **av;
 	int *statp;
 {
-	int buflen;
+	int buflen, r;
 	char *p;
 	ns_map_t *ns_map;
 	char keybuf[MAXNAME + 1];
@@ -7042,12 +7233,31 @@
 	{
 		if (tTd(38, 20))
 			dprintf("nsd_map_t_find failed\n");
+		*statp = EX_UNAVAILABLE;
 		return NULL;
 	}
-
-	if (ns_lookup(ns_map, NULL, map->map_file,
-		      keybuf, NULL, buf, MAXLINE) == NULL)
+	r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL, buf, MAXLINE);
+	if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
+	{
+		*statp = EX_TEMPFAIL;
+		return NULL;
+	}
+	if (r == NS_BADREQ
+# ifdef NS_NOPERM
+	    || r == NS_NOPERM
+# endif /* NS_NOPERM */
+	    )
+	{
+		*statp = EX_CONFIG;
 		return NULL;
+	}
+	if (r != NS_SUCCESS)
+	{
+		*statp = EX_NOTFOUND;
+		return NULL;
+	}
+
+	*statp = EX_OK;
 
 	/* Null out trailing \n */
 	if ((p = strchr(buf, '\n')) != NULL)
Index: gnu/usr.sbin/sendmail/sendmail/mci.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/mci.c,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- gnu/usr.sbin/sendmail/sendmail/mci.c	2000/04/02 19:05:46	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/mci.c	2001/05/29 01:31:15	1.3
@@ -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) 1995-1997 Eric P. Allman.  All rights reserved.
  * Copyright (c) 1988, 1993
@@ -12,13 +12,16 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: mci.c,v 8.133 2000/01/18 01:19:07 ca Exp $";
+static char id[] = "@(#)$Sendmail: mci.c,v 8.133.10.8 2001/05/03 17:24:10 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
+
+
 #if NETINET || NETINET6
 # include <arpa/inet.h>
 #endif /* NETINET || NETINET6 */
+
 #include <dirent.h>
 
 static int	mci_generate_persistent_path __P((const char *, char *,
@@ -271,8 +274,10 @@
 		return;
 
 	for (i = 0; i < MaxMciCache; i++)
+	{
 		if (allbut != MciCache[i])
 			mci_uncache(&MciCache[i], doquit);
+	}
 }
 /*
 **  MCI_GET -- get information about a particular host
@@ -297,7 +302,7 @@
 	(void) mci_scan(NULL);
 
 	if (m->m_mno < 0)
-		syserr("negative mno %d (%s)", m->m_mno, m->m_name);
+		syserr("!negative mno %d (%s)", m->m_mno, m->m_name);
 
 	s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
 	mci = &s->s_mci;
@@ -380,7 +385,7 @@
 	register MCI *mci;
 	register STAB *s;
 
-	if (m->m_mno < 0)
+	if (m->m_mno < 0 || m->m_mno > MAXMAILERS)
 		return FALSE;
 	s = stab(host, ST_MCI + m->m_mno, ST_FIND);
 	if (s == NULL)
@@ -417,7 +422,7 @@
 
 	mci->mci_status = dstat;
 	if (mci->mci_rstatus != NULL)
-		free(mci->mci_rstatus);
+		sm_free(mci->mci_rstatus);
 	if (rstat != NULL)
 		rstat = newstr(rstat);
 	mci->mci_rstatus = rstat;
@@ -457,7 +462,7 @@
 	{ MCIF_8BITOK,		"8BITOK"	},
 	{ MCIF_CVT7TO8,		"CVT7TO8"	},
 	{ MCIF_INMIME,		"INMIME"	},
-	{ 0,			NULL }
+	{ 0,			NULL		}
 };
 
 
@@ -802,7 +807,7 @@
 
 	mci->mci_status = NULL;
 	if (mci->mci_rstatus != NULL)
-		free(mci->mci_rstatus);
+		sm_free(mci->mci_rstatus);
 	mci->mci_rstatus = NULL;
 
 	rewind(fp);
@@ -949,6 +954,7 @@
 **		< 0 -- if any action routine returns a negative value, that
 **			value is returned.
 **		0 -- if we successfully went to completion.
+**		> 0 -- return status from action()
 */
 
 int
@@ -1020,6 +1026,8 @@
 					       sizeof newpath -
 					       (newptr - newpath));
 
+				if (StopRequest)
+					stop_sendmail();
 				ret = mci_traverse_persistent(action, newpath);
 				if (ret < 0)
 					break;
@@ -1120,6 +1128,9 @@
 	if (hostname == NULL)
 		return 0;
 
+	if (StopRequest)
+		stop_sendmail();
+
 	if (!initflag)
 	{
 		initflag = TRUE;
@@ -1192,7 +1203,7 @@
 **
 **	Returns:
 **		0 -- ok
-**		1 -- file too young to be deleted
+**		1 -- file not deleted (too young, incorrect format)
 **		< 0 -- some error occurred
 */
 
@@ -1232,7 +1243,7 @@
 	{
 		/* remove the directory */
 		if (*end != '.')
-			return 0;
+			return 1;
 
 		if (tTd(56, 1))
 			dprintf("mci_purge_persistent: dpurge %s\n", pathname);
@@ -1323,12 +1334,12 @@
 
 #if NETINET || NETINET6
 	/* check for bogus bracketed address */
-	if (host[0] == '[' &&
+	if (host[0] == '['
 # if NETINET6
-	    inet_pton(AF_INET6, t_host, &in6_addr) != 1 &&
+	    && inet_pton(AF_INET6, t_host, &in6_addr) != 1
 # endif /* NETINET6 */
 # if NETINET
-	    inet_addr(t_host) == INADDR_NONE
+	    && inet_addr(t_host) == INADDR_NONE
 # endif /* NETINET */
 	    )
 		return -1;
Index: gnu/usr.sbin/sendmail/sendmail/milter.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/milter.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/sendmail/milter.c	2000/04/07 19:20:42	1.2
+++ gnu/usr.sbin/sendmail/sendmail/milter.c	2001/05/29 01:31:15	1.5
@@ -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
@@ -9,7 +9,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: milter.c,v 8.50 2000/03/16 23:15:49 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: milter.c,v 8.50.4.46 2001/05/11 18:11:36 gshapiro Exp $";
 #endif /* ! lint */
 
 #if _FFR_MILTER
@@ -22,19 +22,20 @@
 #  include <arpa/inet.h>
 # endif /* NETINET || NETINET6 */
 
-/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
-/* To do:                                                            */
-/* - Optimize body chunk sending in milter_body()                    */
-/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+#  define SM_FD_SET	FD_SET
+#  define SM_FD_ISSET	FD_ISSET
+#  define SM_FD_SETSIZE	FD_SETSIZE
 
 static void	milter_error __P((struct milter *));
+static int	milter_open __P((struct milter *, bool, ENVELOPE *));
+static void	milter_parse_timeouts __P((char *, struct milter *));
 
 static char *MilterConnectMacros[MAXFILTERMACROS + 1];
 static char *MilterHeloMacros[MAXFILTERMACROS + 1];
 static char *MilterEnvFromMacros[MAXFILTERMACROS + 1];
 static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
 
-#define MILTER_CHECK_DONE_MSG() \
+# define MILTER_CHECK_DONE_MSG() \
 	if (*state == SMFIR_REPLYCODE || \
 	    *state == SMFIR_REJECT || \
 	    *state == SMFIR_DISCARD || \
@@ -44,24 +45,24 @@
 		milter_abort(e); \
 	}
 
-#define MILTER_CHECK_ERROR() \
+# define MILTER_CHECK_ERROR(action) \
 	if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \
 		*state = SMFIR_TEMPFAIL; \
 	else if (bitnset(SMF_REJECT, m->mf_flags)) \
 		*state = SMFIR_REJECT; \
 	else \
-		continue;
+		action;
 
-#define MILTER_CHECK_REPLYCODE(default) \
+# define MILTER_CHECK_REPLYCODE(default) \
 	if (response == NULL || \
-	    strlen(response) + 1 != rlen || \
+	    strlen(response) + 1 != (size_t) rlen || \
 	    rlen < 3 || \
 	    (response[0] != '4' && response[0] != '5') || \
 	    !isascii(response[1]) || !isdigit(response[1]) || \
 	    !isascii(response[2]) || !isdigit(response[2])) \
 	{ \
 		if (response != NULL) \
-			free(response); \
+			sm_free(response); \
 		response = newstr(default); \
 	} \
 	else \
@@ -73,7 +74,7 @@
 		{ \
 			if (*ptr == '%' && *++ptr != '%') \
 			{ \
-				free(response); \
+				sm_free(response); \
 				response = newstr(default); \
 				break; \
 			} \
@@ -81,6 +82,29 @@
 		} \
 	}
 
+# define MILTER_DF_ERROR(msg) \
+{ \
+	int save_errno = errno; \
+ \
+	if (tTd(64, 5)) \
+	{ \
+		dprintf(msg, dfname, errstring(save_errno)); \
+		dprintf("\n"); \
+	} \
+	if (LogLevel > 0) \
+		sm_syslog(LOG_ERR, e->e_id, msg, dfname, errstring(save_errno)); \
+	if (SuperSafe) \
+	{ \
+		if (e->e_dfp != NULL) \
+		{ \
+			(void) fclose(e->e_dfp); \
+			e->e_dfp = NULL; \
+		} \
+		e->e_flags &= ~EF_HAS_DF; \
+	} \
+	errno = save_errno; \
+}
+
 /*
 **  MILTER_TIMEOUT -- make sure socket is ready in time
 **
@@ -92,28 +116,28 @@
 **	Assumes 'm' is a milter structure for the current socket.
 */
 
-#define MILTER_TIMEOUT(routine, secs, write) \
+# define MILTER_TIMEOUT(routine, secs, write) \
 { \
 	int ret; \
 	int save_errno; \
 	fd_set fds; \
 	struct timeval tv; \
  \
-	if (m->mf_sock >= 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, FD_SETSIZE); \
+				routine, m->mf_name, m->mf_sock, SM_FD_SETSIZE); \
 		if (LogLevel > 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, FD_SETSIZE); \
+				  routine, m->mf_name, m->mf_sock, SM_FD_SETSIZE); \
 		milter_error(m); \
 		return NULL; \
 	} \
  \
 	FD_ZERO(&fds); \
-	FD_SET(m->mf_sock, &fds); \
+	SM_FD_SET(m->mf_sock, &fds); \
 	tv.tv_sec = secs; \
 	tv.tv_usec = 0; \
 	ret = select(m->mf_sock + 1, \
@@ -145,7 +169,7 @@
 		return NULL; \
  \
 	  default: \
-		if (FD_ISSET(m->mf_sock, &fds)) \
+		if (SM_FD_ISSET(m->mf_sock, &fds)) \
 			break; \
 		if (tTd(64, 5)) \
 			dprintf("%s(%s): socket not ready\n", \
@@ -178,86 +202,109 @@
 */
 
 static char *
-milter_read(m, cmd, rlen, to, e)
+milter_sysread(m, buf, sz, to, e)
 	struct milter *m;
-	char *cmd;
-	ssize_t *rlen;
+	char *buf;
+	ssize_t sz;
 	time_t to;
 	ENVELOPE *e;
 {
-	time_t readstart = (time_t) 0;
-	ssize_t len, expl;
-	mi_int32 i;
-	char *buf;
-	char data[MILTER_LEN_BYTES + 1];
+	time_t readstart = 0;
+	ssize_t len, curl;
 
-	*rlen = 0;
-	*cmd = '\0';
+	curl = 0;
 
 	if (to > 0)
-	{
 		readstart = curtime();
-		MILTER_TIMEOUT("milter_read", to, FALSE);
-	}
 
-	len = read(m->mf_sock, data, sizeof data);
-	if (len <= 0)
+	for (;;)
 	{
-		int save_errno = errno;
+		if (to > 0)
+		{
+			time_t now;
 
-		if (tTd(64, 5))
-			dprintf("milter_read(%s): read returned %ld: %s\n",
-				m->mf_name, (long) len, errstring(save_errno));
-		if (LogLevel > 0)
-			sm_syslog(LOG_ERR, e->e_id,
-				  "milter_read(%s): read returned %ld: %s",
-				  m->mf_name, (long) len,
-				  errstring(save_errno));
-		milter_error(m);
-		return NULL;
+			now = curtime();
+			if (now - readstart >= to)
+			{
+				if (tTd(64, 5))
+					dprintf("milter_read(%s): timeout before data read\n",
+						m->mf_name);
+				if (LogLevel > 0)
+					sm_syslog(LOG_ERR, e->e_id,
+						  "milter_read(%s): timeout before data read\n",
+						  m->mf_name);
+				milter_error(m);
+				return NULL;
+			}
+			to -= now - readstart;
+			readstart = now;
+			MILTER_TIMEOUT("milter_read", to, FALSE);
+		}
+
+		len = read(m->mf_sock, buf + curl, sz - curl);
+
+		if (len < 0)
+		{
+			int save_errno = errno;
+
+			if (tTd(64, 5))
+				dprintf("milter_read(%s): read returned %ld: %s\n",
+					m->mf_name, (long) len,
+					errstring(save_errno));
+			if (LogLevel > 0)
+				sm_syslog(LOG_ERR, e->e_id,
+					  "milter_read(%s): read returned %ld: %s",
+					  m->mf_name, (long) len,
+					  errstring(save_errno));
+			milter_error(m);
+			return NULL;
+		}
+
+		curl += len;
+		if (len == 0 || curl >= sz)
+			break;
+
 	}
 
-	if (len != sizeof data)
+	if (curl != sz)
 	{
 		if (tTd(64, 5))
-			dprintf("milter_read(%s): cmd read returned %ld, expecting %ld\n",
-				m->mf_name, (long) *rlen, (long) sizeof data);
+			dprintf("milter_read(%s): read returned %ld, expecting %ld\n",
+				m->mf_name, (long) curl, (long) sz);
 		if (LogLevel > 0)
 			sm_syslog(LOG_ERR, e->e_id,
-				  "milter_read(%s): cmd read returned %ld, expecting %ld",
-				  m->mf_name, (long) *rlen,
-				  (long) sizeof data);
+				  "milter_read(%s): read returned %ld, expecting %ld",
+				  m->mf_name, (long) curl, (long) sz);
 		milter_error(m);
 		return NULL;
 	}
+	return buf;
+}
 
-	*cmd = data[MILTER_LEN_BYTES];
-	data[MILTER_LEN_BYTES] = '\0';
-	(void) memcpy(&i, data, MILTER_LEN_BYTES);
-	expl = ntohl(i) - 1;
+static char *
+milter_read(m, cmd, rlen, to, e)
+	struct milter *m;
+	char *cmd;
+	ssize_t *rlen;
+	time_t to;
+	ENVELOPE *e;
+{
+	time_t readstart = 0;
+	ssize_t expl;
+	mi_int32 i;
+	char *buf;
+	char data[MILTER_LEN_BYTES + 1];
 
-	if (tTd(64, 25))
-		dprintf("milter_read(%s): expecting %ld bytes\n",
-			m->mf_name, (long) expl);
+	*rlen = 0;
+	*cmd = '\0';
 
-	if (expl < 0 || expl > MILTER_CHUNK_SIZE)
-	{
-		if (tTd(64, 5))
-			dprintf("milter_read(%s): read size %ld out of range\n",
-				m->mf_name, (long) expl);
-		if (LogLevel > 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);
-		return NULL;
-	}
+	if (to > 0)
+		readstart = curtime();
 
-	if (expl == 0)
+	if (milter_sysread(m, data, sizeof data, to, e) == NULL)
 		return NULL;
 
-	buf = (char *)xalloc(expl);
-
+	/* reset timeout */
 	if (to > 0)
 	{
 		time_t now;
@@ -274,47 +321,47 @@
 					  m->mf_name);
 			milter_error(m);
 			return NULL;
-		}
-		else
-		{
-			to -= now - readstart;
-			MILTER_TIMEOUT("milter_read", to, FALSE);
 		}
+		to -= now - readstart;
 	}
 
-	*rlen = read(m->mf_sock, buf, expl);
-	if (len <= 0)
-	{
-		int save_errno = errno;
+	*cmd = data[MILTER_LEN_BYTES];
+	data[MILTER_LEN_BYTES] = '\0';
+	(void) memcpy(&i, data, MILTER_LEN_BYTES);
+	expl = ntohl(i) - 1;
+
+	if (tTd(64, 25))
+		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 returned %ld: %s\n",
-				m->mf_name, (long) len, errstring(save_errno));
+			dprintf("milter_read(%s): read size %ld out of range\n",
+				m->mf_name, (long) expl);
 		if (LogLevel > 0)
 			sm_syslog(LOG_ERR, e->e_id,
-				  "milter_read(%s): read returned %ld: %s",
-				  m->mf_name, (long) len,
-				  errstring(save_errno));
-		free(buf);
+				  "milter_read(%s): read size %ld out of range",
+				  m->mf_name, (long) expl);
 		milter_error(m);
 		return NULL;
 	}
-	if (*rlen != expl)
+
+	if (expl == 0)
+		return NULL;
+
+	buf = (char *)xalloc(expl);
+
+	if (milter_sysread(m, buf, expl, to, e) == NULL)
 	{
-		if (tTd(64, 5))
-			dprintf("milter_read(%s): read returned %ld, expecting %ld\n",
-				m->mf_name, (long) *rlen, (long) len);
-		if (LogLevel > 0)
-			sm_syslog(LOG_ERR, e->e_id,
-				  "milter_read(%s): read returned %ld, expecting %ld",
-				  m->mf_name, (long) *rlen, (long) len);
-		free(buf);
-		milter_error(m);
+		sm_free(buf);
 		return NULL;
 	}
+
 	if (tTd(64, 50))
 		dprintf("milter_read(%s): Returning %*s\n",
-			m->mf_name, (int) *rlen, buf);
+			m->mf_name, (int) expl, buf);
+	*rlen = expl;
 	return buf;
 }
 /*
@@ -464,7 +511,7 @@
 **		-1 otherwise.
 */
 
-int
+static int
 milter_open(m, parseonly, e)
 	struct milter *m;
 	bool parseonly;
@@ -572,7 +619,7 @@
 # if NETUNIX
 	if (addr.sa.sa_family == AF_UNIX)
 	{
-		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN|SFF_EXECOK;
+		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
 
 		at = colon;
 		if (strlen(colon) >= sizeof addr.sunix.sun_path)
@@ -594,9 +641,18 @@
 		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
 				 S_IRUSR|S_IWUSR, NULL);
 
-		/* if not safe, don't create */
-		if (errno != 0)
+		/* if just parsing .cf file, socket doesn't need to exist */
+		if (parseonly && errno == ENOENT)
+		{
+			if (OpMode == MD_DAEMON ||
+			    OpMode == MD_FGDAEMON)
+				fprintf(stderr,
+					"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",
@@ -622,9 +678,10 @@
 			       sizeof addr.sunix.sun_path);
 		addrlen = sizeof (struct sockaddr_un);
 	}
+	else
 # endif /* NETUNIX */
 # if NETINET || NETINET6
-	else if (FALSE
+	if (FALSE
 #  if NETINET
 		 || addr.sa.sa_family == AF_INET
 #  endif /* NETINET */
@@ -654,7 +711,7 @@
 		}
 		*at = '\0';
 		if (isascii(*colon) && isdigit(*colon))
-			port = htons(atoi(colon));
+			port = htons((u_short) atoi(colon));
 		else
 		{
 #  ifdef NO_GETSERVBYNAME
@@ -823,12 +880,15 @@
 						  m->mf_name, at,
 						  hp->h_addrtype);
 				milter_error(m);
+#  if _FFR_FREEHOSTENT && NETINET6
+				freehostent(hp);
+#  endif /* _FFR_FREEHOSTENT && NETINET6 */
 				return -1;
 			}
 		}
 	}
-# endif /* NETINET || NETINET6 */
 	else
+# endif /* NETINET || NETINET6 */
 	{
 		if (tTd(64, 5))
 			dprintf("X%s: unknown socket protocol\n", m->mf_name);
@@ -845,6 +905,10 @@
 	if (parseonly)
 	{
 		m->mf_state = SMFS_READY;
+# if _FFR_FREEHOSTENT && NETINET6
+		if (hp != NULL)
+			freehostent(hp);
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
 		return 0;
 	}
 
@@ -857,6 +921,10 @@
 			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
+		if (hp != NULL)
+			freehostent(hp);
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
 		return -1;
 	}
 
@@ -875,6 +943,10 @@
 					  "X%s: error creating socket: %s",
 					  m->mf_name, errstring(save_errno));
 			milter_error(m);
+# if _FFR_FREEHOSTENT && NETINET6
+			if (hp != NULL)
+				freehostent(hp);
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
 			return -1;
 		}
 
@@ -883,6 +955,8 @@
 
 		/* couldn't connect.... try next address */
 		save_errno = errno;
+		p = CurHostName;
+		CurHostName = at;
 		if (tTd(64, 5))
 			dprintf("milter_open(%s): %s failed: %s\n",
 				m->mf_name, at, errstring(save_errno));
@@ -890,6 +964,7 @@
 			sm_syslog(LOG_INFO, e->e_id,
 				  "milter_open(%s): %s failed: %s",
 				  m->mf_name, at, errstring(save_errno));
+		CurHostName = p;
 		(void) close(sock);
 
 		/* try next address */
@@ -924,24 +999,142 @@
 						  m->mf_name, at,
 						  hp->h_addrtype);
 				milter_error(m);
+# if _FFR_FREEHOSTENT && NETINET6
+				freehostent(hp);
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
 				return -1;
 			}
 			continue;
 		}
 		if (tTd(64, 5))
-			dprintf("X%s: error connecting to filter\n",
-				m->mf_name);
+			dprintf("X%s: error connecting to filter: %s\n",
+				m->mf_name, errstring(save_errno));
 		if (LogLevel > 0)
 			sm_syslog(LOG_ERR, e->e_id,
-				  "X%s: error connecting to filter",
-				  m->mf_name);
+				  "X%s: error connecting to filter: %s",
+				  m->mf_name, errstring(save_errno));
 		milter_error(m);
+# if _FFR_FREEHOSTENT && NETINET6
+		if (hp != NULL)
+			freehostent(hp);
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
 		return -1;
 	}
 	m->mf_state = SMFS_OPEN;
+# if _FFR_FREEHOSTENT && NETINET6
+	if (hp != NULL)
+	{
+		freehostent(hp);
+		hp = NULL;
+	}
+# endif /* _FFR_FREEHOSTENT && NETINET6 */
 	return sock;
 }
 /*
+**  MILTER_SETUP -- setup structure for a mail filter
+**
+**	Parameters:
+**		line -- the options line.
+**
+**	Returns:
+**		none
+*/
+
+void
+milter_setup(line)
+	char *line;
+{
+	char fcode;
+	register char *p;
+	register struct milter *m;
+	STAB *s;
+
+	/* collect the filter name */
+	for (p = line;
+	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
+	     p++)
+		continue;
+	if (*p != '\0')
+		*p++ = '\0';
+	if (line[0] == '\0')
+	{
+		syserr("name required for mail filter");
+		return;
+	}
+	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_WRITE] = (time_t) 10;
+	m->mf_timeout[SMFTO_READ] = (time_t) 10;
+	m->mf_timeout[SMFTO_EOM] = (time_t) 300;
+
+	/* now scan through and assign info from the fields */
+	while (*p != '\0')
+	{
+		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("X%s: `=' expected", m->mf_name);
+			return;
+		}
+		while (isascii(*p) && isspace(*p))
+			p++;
+
+		/* p now points to the field body */
+		p = munchstring(p, &delimptr, ',');
+
+		/* install the field into the filter struct */
+		switch (fcode)
+		{
+		  case 'S':		/* socket */
+			if (p == NULL)
+				m->mf_conn = NULL;
+			else
+				m->mf_conn = newstr(p);
+			break;
+
+		  case 'F':		/* Milter flags configured on MTA */
+			for (; *p != '\0'; p++)
+			{
+				if (!(isascii(*p) && isspace(*p)))
+					setbitn(bitidx(*p), m->mf_flags);
+			}
+			break;
+
+		  case 'T':		/* timeouts */
+			milter_parse_timeouts(p, m);
+			break;
+
+		  default:
+			syserr("X%s: unknown filter equate %c=",
+			       m->mf_name, fcode);
+			break;
+		}
+		p = delimptr;
+	}
+
+	/* early check for errors */
+	(void) milter_open(m, TRUE, CurEnv);
+
+	/* enter the filter into the symbol table */
+	s = stab(m->mf_name, ST_MILTER, ST_ENTER);
+	if (s->s_milter != NULL)
+		syserr("X%s: duplicate filter definition", m->mf_name);
+	else
+		s->s_milter = m;
+}
+/*
 **  MILTER_PARSE_LIST -- parse option list into an array
 **
 **	Called when reading configuration file.
@@ -1012,7 +1205,7 @@
 **		none
 */
 
-void
+static void
 milter_parse_timeouts(spec, m)
 	char *spec;
 	struct milter *m;
@@ -1046,7 +1239,7 @@
 		/* p now points to the field body */
 		p = munchstring(p, &delimptr, ';');
 
-		/* install the field into the mailer struct */
+		/* install the field into the filter struct */
 		switch (fcode)
 		{
 		  case 'S':
@@ -1106,13 +1299,13 @@
 	u_char	mo_code;	/* code for option */
 } MilterOptTab[] =
 {
-#define MO_MACROS_CONNECT		0x01
+# define MO_MACROS_CONNECT		0x01
 	{ "macros.connect",		MO_MACROS_CONNECT		},
-#define MO_MACROS_HELO			0x02
+# define MO_MACROS_HELO			0x02
 	{ "macros.helo",		MO_MACROS_HELO			},
-#define MO_MACROS_ENVFROM		0x03
+# define MO_MACROS_ENVFROM		0x03
 	{ "macros.envfrom",		MO_MACROS_ENVFROM		},
-#define MO_MACROS_ENVRCPT		0x04
+# define MO_MACROS_ENVRCPT		0x04
 	{ "macros.envrcpt",		MO_MACROS_ENVRCPT		},
 	{ NULL,				0				},
 };
@@ -1217,48 +1410,160 @@
 		setbitn(mo->mo_code, StickyMilterOpt);
 }
 /*
-**  MILTER_CAN_DELRCPTS -- can any milter filters delete recipients?
+**  MILTER_REOPEN_DF -- open & truncate the df file (for replbody)
 **
 **	Parameters:
-**		none
+**		e -- current envelope.
 **
 **	Returns:
-**		TRUE if any filter deletes recipients, FALSE otherwise
+**		0 if succesful, -1 otherwise
 */
 
-bool
-milter_can_delrcpts()
+static int
+milter_reopen_df(e)
+	ENVELOPE *e;
 {
-	int i;
+	char dfname[MAXPATHLEN];
 
-	for (i = 0; InputFilters[i] != NULL; i++)
+	(void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
+
+	/*
+	**  In SuperSafe 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
+	**  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)
 	{
-		struct milter *m = InputFilters[i];
+		/* close read-only df */
+		if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
+		{
+			(void) fclose(e->e_dfp);
+			e->e_flags &= ~EF_HAS_DF;
+		}
 
-		if (bitset(SMFIF_DELRCPT, m->mf_fflags))
-			return TRUE;
+		/* open writable */
+		if ((e->e_dfp = fopen(dfname, "w+")) == NULL)
+		{
+			MILTER_DF_ERROR("milter_reopen_df: fopen %s: %s");
+			return -1;
+		}
 	}
-	return FALSE;
+	else if (e->e_dfp == NULL)
+	{
+		/* shouldn't happen */
+		errno = ENOENT;
+		MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)");
+		return -1;
+	}
+	return 0;
 }
 /*
-**  MILTER_QUIT_FILTER -- close down a single filter
+**  MILTER_RESET_DF -- re-open read-only the df file (for replbody)
 **
 **	Parameters:
-**		m -- milter structure of filter to close down.
 **		e -- current envelope.
 **
 **	Returns:
-**		none
+**		0 if succesful, -1 otherwise
 */
 
-static void
-milter_quit_filter(m, e)
-	struct milter *m;
+static int
+milter_reset_df(e)
 	ENVELOPE *e;
 {
-	if (tTd(64, 10))
-		dprintf("milter_quit_filter(%s)\n", m->mf_name);
-
+	int afd;
+	char dfname[MAXPATHLEN];
+
+	(void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
+
+	if (fflush(e->e_dfp) != 0 || ferror(e->e_dfp))
+	{
+		MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s");
+		return -1;
+	}
+	else if (!SuperSafe)
+	{
+		/* skip next few clauses */
+		/* EMPTY */
+	}
+	else if ((afd = fileno(e->e_dfp)) >= 0 && fsync(afd) < 0)
+	{
+		MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s");
+		return -1;
+	}
+	else if (fclose(e->e_dfp) < 0)
+	{
+		MILTER_DF_ERROR("milter_reset_df: error closing %s: %s");
+		return -1;
+	}
+	else if ((e->e_dfp = fopen(dfname, "r")) == NULL)
+	{
+		MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s");
+		return -1;
+	}
+	else
+		e->e_flags |= EF_HAS_DF;
+	return 0;
+}
+/*
+**  MILTER_CAN_DELRCPTS -- can any milter filters delete recipients?
+**
+**	Parameters:
+**		none
+**
+**	Returns:
+**		TRUE if any filter deletes recipients, FALSE otherwise
+*/
+
+bool
+milter_can_delrcpts()
+{
+	bool can = FALSE;
+	int i;
+
+	if (tTd(64, 10))
+		dprintf("milter_can_delrcpts:");
+
+	for (i = 0; InputFilters[i] != NULL; i++)
+	{
+		struct milter *m = InputFilters[i];
+
+		if (bitset(SMFIF_DELRCPT, m->mf_fflags))
+		{
+			can = TRUE;
+			break;
+		}
+	}
+	if (tTd(64, 10))
+		dprintf("%s\n", can ? "TRUE" : "FALSE");
+
+	return can;
+}
+/*
+**  MILTER_QUIT_FILTER -- close down a single filter
+**
+**	Parameters:
+**		m -- milter structure of filter to close down.
+**		e -- current envelope.
+**
+**	Returns:
+**		none
+*/
+
+static void
+milter_quit_filter(m, e)
+	struct milter *m;
+	ENVELOPE *e;
+{
+	if (tTd(64, 10))
+		dprintf("milter_quit_filter(%s)\n", m->mf_name);
+
 	/* Never replace error state */
 	if (m->mf_state == SMFS_ERROR)
 		return;
@@ -1274,8 +1579,11 @@
 
 	(void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0,
 			    m->mf_timeout[SMFTO_WRITE], e);
-	(void) close(m->mf_sock);
-	m->mf_sock = -1;
+	if (m->mf_sock >= 0)
+	{
+		(void) close(m->mf_sock);
+		m->mf_sock = -1;
+	}
 	if (m->mf_state != SMFS_ERROR)
 		m->mf_state = SMFS_CLOSED;
 }
@@ -1342,7 +1650,7 @@
 	for (i = 0; macros[i] != NULL; i++)
 	{
 		mid = macid(macros[i], NULL);
-		if (mid == '\0')
+		if (mid == 0)
 			continue;
 		v = macvalue(mid, e);
 		if (v == NULL)
@@ -1356,7 +1664,7 @@
 	for (i = 0; macros[i] != NULL; i++)
 	{
 		mid = macid(macros[i], NULL);
-		if (mid == '\0')
+		if (mid == 0)
 			continue;
 		v = macvalue(mid, e);
 		if (v == NULL)
@@ -1373,17 +1681,18 @@
 	}
 	(void) milter_write(m, SMFIC_MACRO, buf, s,
 			    m->mf_timeout[SMFTO_WRITE], e);
-	free(buf);
+	sm_free(buf);
 }
+
 /*
-**  MILTER_COMMAND -- send a command and return the response for each filter
+**  MILTER_SEND_COMMAND -- send a command and return the response for a filter
 **
 **	Parameters:
+**		m -- current milter filter
 **		command -- command to send.
 **		data -- optional command data.
 **		sz -- length of buf.
-**		macros -- macros to send for filter smfi_getsymval().
-**		e -- current envelope (for macro access).
+**		e -- current envelope (for e->e_id).
 **		state -- return state word.
 **
 **	Returns:
@@ -1391,61 +1700,68 @@
 */
 
 static char *
-milter_command(command, data, sz, macros, e, state)
+milter_send_command(m, command, data, sz, e, state)
+	struct milter *m;
 	char command;
 	void *data;
 	ssize_t sz;
-	char **macros;
 	ENVELOPE *e;
 	char *state;
 {
-	int i;
 	char rcmd;
+	ssize_t rlen;
 	u_long skipflag;
-	char *response = NULL;
 	char *defresponse;
-	ssize_t rlen;
+	char *response;
 
 	if (tTd(64, 10))
-		dprintf("milter_command: cmd %c len %ld\n",
-			(char) command, (long) sz);
+		dprintf("milter_send_command(%s): cmd %c len %ld\n",
+			m->mf_name, (char) command, (long) sz);
 
 	/* find skip flag and default failure */
 	switch (command)
 	{
 	  case SMFIC_CONNECT:
-		skipflag = SMFIF_NOCONNECT;
+		skipflag = SMFIP_NOCONNECT;
 		defresponse = "554 Command rejected";
 		break;
 
 	  case SMFIC_HELO:
-		skipflag = SMFIF_NOHELO;
+		skipflag = SMFIP_NOHELO;
 		defresponse = "550 Command rejected";
 		break;
 
 	  case SMFIC_MAIL:
-		skipflag = SMFIF_NOMAIL;
+		skipflag = SMFIP_NOMAIL;
 		defresponse = "550 5.7.1 Command rejected";
 		break;
 
 	  case SMFIC_RCPT:
-		skipflag = SMFIF_NORCPT;
+		skipflag = SMFIP_NORCPT;
 		defresponse = "550 5.7.1 Command rejected";
 		break;
 
 	  case SMFIC_HEADER:
-	  case SMFIC_EOH:
-		skipflag = SMFIF_NOHDRS;
+		skipflag = SMFIP_NOHDRS;
 		defresponse = "550 5.7.1 Command rejected";
 		break;
 
 	  case SMFIC_BODY:
+		skipflag = SMFIP_NOBODY;
+		defresponse = "554 5.7.1 Command rejected";
+		break;
+
+	  case SMFIC_EOH:
+		skipflag = SMFIP_NOEOH;
+		defresponse = "550 5.7.1 Command rejected";
+		break;
+
 	  case SMFIC_BODYEOB:
 	  case SMFIC_OPTNEG:
 	  case SMFIC_MACRO:
 	  case SMFIC_ABORT:
 	  case SMFIC_QUIT:
-		/* NOTE: not handled by milter_command() */
+		/* NOTE: not handled by milter_send_command() */
 		/* FALLTHROUGH */
 
 	  default:
@@ -1454,11 +1770,121 @@
 		break;
 	}
 
+	/* check if filter wants this command */
+	if (skipflag != 0 &&
+	    bitset(skipflag, m->mf_pflags))
+		return NULL;
+
+
+	(void) milter_write(m, command, data, sz,
+			    m->mf_timeout[SMFTO_WRITE], e);
+	if (m->mf_state == SMFS_ERROR)
+	{
+		MILTER_CHECK_ERROR(/* EMPTY */;);
+		return NULL;
+	}
+
+	response = milter_read(m, &rcmd, &rlen,
+			       m->mf_timeout[SMFTO_READ], e);
+	if (m->mf_state == SMFS_ERROR)
+	{
+		MILTER_CHECK_ERROR(/* EMPTY */;);
+		return NULL;
+	}
+
+	if (tTd(64, 10))
+		dprintf("milter_send_command(%s): returned %c\n",
+			m->mf_name, (char) rcmd);
+
+	switch (rcmd)
+	{
+	  case SMFIR_REPLYCODE:
+		MILTER_CHECK_REPLYCODE(defresponse);
+		/* FALLTHROUGH */
+
+	  case SMFIR_REJECT:
+	  case SMFIR_DISCARD:
+	  case SMFIR_TEMPFAIL:
+		*state = rcmd;
+		break;
+
+	  case SMFIR_ACCEPT:
+		/* this filter is done with message/connection */
+		if (command == SMFIC_HELO ||
+		    command == SMFIC_CONNECT)
+			m->mf_state = SMFS_CLOSABLE;
+		else
+			m->mf_state = SMFS_DONE;
+		break;
+
+	  case SMFIR_CONTINUE:
+		/* if MAIL command is ok, filter is in message state */
+		if (command == SMFIC_MAIL)
+			m->mf_state = SMFS_INMSG;
+		break;
+
+	  default:
+		/* Invalid response to command */
+		if (LogLevel > 0)
+			sm_syslog(LOG_ERR, e->e_id,
+				  "milter_send_command(%s): returned bogus response %c",
+				  m->mf_name, rcmd);
+		milter_error(m);
+		break;
+	}
+
+	if (*state != SMFIR_REPLYCODE &&
+	    response != NULL)
+	{
+		sm_free(response);
+		response = NULL;
+	}
+	return response;
+}
+
+/*
+**  MILTER_COMMAND -- send a command and return the response for each filter
+**
+**	Parameters:
+**		command -- command to send.
+**		data -- optional command data.
+**		sz -- length of buf.
+**		macros -- macros to send for filter smfi_getsymval().
+**		e -- current envelope (for macro access).
+**		state -- return state word.
+**
+**	Returns:
+**		response string (may be NULL)
+*/
+
+static char *
+milter_command(command, data, sz, macros, e, state)
+	char command;
+	void *data;
+	ssize_t sz;
+	char **macros;
+	ENVELOPE *e;
+	char *state;
+{
+	int i;
+	char *response = NULL;
+
+	if (tTd(64, 10))
+		dprintf("milter_command: cmd %c len %ld\n",
+			(char) command, (long) sz);
+
 	*state = SMFIR_CONTINUE;
 	for (i = 0; InputFilters[i] != NULL; i++)
 	{
 		struct milter *m = InputFilters[i];
 
+		/* previous problem? */
+		if (m->mf_state == SMFS_ERROR)
+		{
+			MILTER_CHECK_ERROR(continue);
+			break;
+		}
+
 		/* sanity check */
 		if (m->mf_sock < 0 ||
 		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
@@ -1470,78 +1896,12 @@
 			milter_send_macros(m, macros, command, e);
 			if (m->mf_state == SMFS_ERROR)
 			{
-				MILTER_CHECK_ERROR();
+				MILTER_CHECK_ERROR(continue);
 				break;
 			}
 		}
-
-		/* check if filter wants this command */
-		if (skipflag != 0 &&
-		    bitset(skipflag, m->mf_fflags))
-			continue;
-
-		(void) milter_write(m, command, data, sz,
-				    m->mf_timeout[SMFTO_WRITE], e);
-		if (m->mf_state == SMFS_ERROR)
-		{
-			MILTER_CHECK_ERROR();
-			break;
-		}
-
-		response = milter_read(m, &rcmd, &rlen,
-				       m->mf_timeout[SMFTO_READ], e);
-		if (m->mf_state == SMFS_ERROR)
-		{
-			MILTER_CHECK_ERROR();
-			break;
-		}
-
-		if (tTd(64, 10))
-			dprintf("milter_command(%s): returned %c%s%s\n",
-				m->mf_name, (char) rcmd,
-				response == NULL ? "" : ":",
-				response == NULL ? "" : response);
-
-		switch (rcmd)
-		{
-		  case SMFIR_REPLYCODE:
-			MILTER_CHECK_REPLYCODE(defresponse);
-			/* FALLTHROUGH */
-
-		  case SMFIR_REJECT:
-		  case SMFIR_DISCARD:
-		  case SMFIR_TEMPFAIL:
-			*state = rcmd;
-			break;
-
-		  case SMFIR_ACCEPT:
-			/* this filter is done with message/connection */
-			m->mf_state = SMFS_DONE;
-			break;
-
-		  case SMFIR_CONTINUE:
-			/* if MAIL command is ok, filter is in message state */
-			if (command == SMFIC_MAIL)
-				m->mf_state = SMFS_INMSG;
-			break;
-
-		  default:
-			/* Invalid response to command */
-			if (LogLevel > 0)
-				sm_syslog(LOG_ERR, e->e_id,
-					  "milter_command(%s): returned bogus response %c",
-					  m->mf_name, rcmd);
-			milter_error(m);
-			break;
-		}
-
-		if (*state != SMFIR_REPLYCODE &&
-		    response != NULL)
-		{
-			free(response);
-			response = NULL;
-		}
 
+		response = milter_send_command(m, command, data, sz, e, state);
 		if (*state != SMFIR_CONTINUE)
 			break;
 	}
@@ -1565,10 +1925,11 @@
 {
 	char rcmd;
 	mi_int32 fvers;
-	mi_int32 flags;
-	char *response = NULL;
+	mi_int32 fflags;
+	mi_int32 pflags;
+	char *response;
 	ssize_t rlen;
-	char data[MILTER_LEN_BYTES * 2];
+	char data[MILTER_OPTLEN];
 
 	/* sanity check */
 	if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN)
@@ -1582,10 +1943,13 @@
 	}
 
 	fvers = htonl(SMFI_VERSION);
-	flags = htonl(0);
+	fflags = htonl(SMFI_CURR_ACTS);
+	pflags = htonl(SMFI_CURR_PROT);
 	(void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES);
 	(void) memcpy(data + MILTER_LEN_BYTES,
-		      (char *) &flags, MILTER_LEN_BYTES);
+		      (char *) &fflags, MILTER_LEN_BYTES);
+	(void) memcpy(data + (MILTER_LEN_BYTES * 2),
+		      (char *) &pflags, MILTER_LEN_BYTES);
 	(void) milter_write(m, SMFIC_OPTNEG, data, sizeof data,
 			    m->mf_timeout[SMFTO_WRITE], e);
 
@@ -1606,12 +1970,13 @@
 				  "milter_negotiate(%s): returned %c instead of %c",
 				  m->mf_name, rcmd, SMFIC_OPTNEG);
 		if (response != NULL)
-			free(response);
+			sm_free(response);
 		milter_error(m);
 		return -1;
 	}
 
-	if (response == NULL || rlen != MILTER_LEN_BYTES * 2)
+	/* Make sure we have enough bytes for the version */
+	if (response == NULL || rlen < MILTER_LEN_BYTES)
 	{
 		if (tTd(64, 5))
 			dprintf("milter_negotiate(%s): did not return valid info\n",
@@ -1621,37 +1986,91 @@
 				  "milter_negotiate(%s): did not return valid info",
 				  m->mf_name);
 		if (response != NULL)
-			free(response);
+			sm_free(response);
 		milter_error(m);
 		return -1;
 	}
 
 	/* extract information */
 	(void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES);
-	(void) memcpy((char *) &flags, response + MILTER_LEN_BYTES,
+
+	/* Now make sure we have enough for the feature bitmap */
+	if (rlen != MILTER_OPTLEN)
+	{
+		if (tTd(64, 5))
+			dprintf("milter_negotiate(%s): did not return enough info\n",
+				m->mf_name);
+		if (LogLevel > 0)
+			sm_syslog(LOG_ERR, e->e_id,
+				  "milter_negotiate(%s): did not return enough info",
+				  m->mf_name);
+		if (response != NULL)
+			sm_free(response);
+		milter_error(m);
+		return -1;
+	}
+
+	(void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES,
 		      MILTER_LEN_BYTES);
-	free(response);
+	(void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2),
+		      MILTER_LEN_BYTES);
+	sm_free(response);
 	response = NULL;
 
-	/* check for version mismatch */
-	if (ntohl(fvers) != SMFI_VERSION)
+	m->mf_fvers = ntohl(fvers);
+	m->mf_fflags = ntohl(fflags);
+	m->mf_pflags = ntohl(pflags);
+
+	/* check for version compatibility */
+	if (m->mf_fvers == 1 ||
+	    m->mf_fvers > SMFI_VERSION)
 	{
 		if (tTd(64, 5))
 			dprintf("milter_negotiate(%s): version %lu != MTA milter version %d\n",
-				m->mf_name, (u_long) ntohl(fvers),
-				SMFI_VERSION);
+				m->mf_name, m->mf_fvers, SMFI_VERSION);
 		if (LogLevel > 0)
 			sm_syslog(LOG_ERR, e->e_id,
 				  "milter_negotiate(%s): version %ld != MTA milter version %d",
-				  m->mf_name, (u_long) ntohl(fvers),
-				  SMFI_VERSION);
+				  m->mf_name, m->mf_fvers, SMFI_VERSION);
+		milter_error(m);
+		return -1;
+	}
+
+	/* check for filter feature mismatch */
+	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",
+				m->mf_name, m->mf_fflags,
+				(u_long) SMFI_CURR_ACTS);
+		if (LogLevel > 0)
+			sm_syslog(LOG_ERR, e->e_id,
+				  "milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n",
+				  m->mf_name, m->mf_fflags,
+				  (u_long) SMFI_CURR_ACTS);
+		milter_error(m);
+		return -1;
+	}
+
+	/* check for protocol feature mismatch */
+	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",
+				m->mf_name, m->mf_pflags,
+				(u_long) SMFI_CURR_PROT);
+		if (LogLevel > 0)
+			sm_syslog(LOG_ERR, e->e_id,
+				  "milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n",
+				  m->mf_name, m->mf_pflags,
+				  (u_long) SMFI_CURR_PROT);
 		milter_error(m);
 		return -1;
 	}
-	m->mf_fflags = ntohl(flags);
+
 	if (tTd(64, 5))
-		dprintf("milter_negotiate(%s): version %lu, flags %lx\n",
-			m->mf_name, (u_long) ntohl(fvers), m->mf_fflags);
+		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;
 }
 /*
@@ -1677,7 +2096,7 @@
 	{
 		struct milter *m = InputFilters[i];
 
-		if (m->mf_state == SMFS_DONE)
+		if (m->mf_state == SMFS_CLOSABLE)
 			milter_quit_filter(m, e);
 	}
 }
@@ -1709,20 +2128,194 @@
 	}
 	m->mf_state = SMFS_ERROR;
 }
-
-/*
-**  Actions
-*/
-
 /*
-**  MILTER_ADDHEAER -- Add the supplied header to the message
+**  MILTER_HEADERS -- send headers to a single milter filter
 **
 **	Parameters:
-**		response -- encoded form of header/value.
-**		rlen -- length of response.
+**		m -- current filter.
 **		e -- current envelope.
+**		state -- return state from response.
 **
 **	Returns:
+**		response string (may be NULL)
+*/
+
+static char *
+milter_headers(m, e, state)
+	struct milter *m;
+	ENVELOPE *e;
+	char *state;
+{
+	char *response = NULL;
+	HDR *h;
+
+	for (h = e->e_header; h != NULL; h = h->h_link)
+	{
+		char *buf;
+		ssize_t s;
+
+		/* don't send over deleted headers */
+		if (h->h_value == NULL)
+		{
+			/* strip H_USER so not counted in milter_chgheader() */
+			h->h_flags &= ~H_USER;
+			continue;
+		}
+
+		/* skip auto-generated */
+		if (!bitset(H_USER, h->h_flags))
+			continue;
+
+		if (tTd(64, 10))
+			dprintf("milter_headers: %s: %s\n",
+				h->h_field, h->h_value);
+
+		s = strlen(h->h_field) + 1 +
+			strlen(h->h_value) + 1;
+		buf = (char *) xalloc(s);
+		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);
+		if (m->mf_state == SMFS_ERROR ||
+		    m->mf_state == SMFS_DONE ||
+		    *state != SMFIR_CONTINUE)
+			break;
+	}
+	return response;
+}
+/*
+**  MILTER_BODY -- send the body to a filter
+**
+**	Parameters:
+**		m -- current filter.
+**		e -- current envelope.
+**		state -- return state from response.
+**
+**	Returns:
+**		response string (may be NULL)
+*/
+
+static char *
+milter_body(m, e, state)
+	struct milter *m;
+	ENVELOPE *e;
+	char *state;
+{
+	char bufchar = '\0';
+	char prevchar = '\0';
+	int c;
+	char *response = NULL;
+	char *bp;
+	char buf[MILTER_CHUNK_SIZE];
+
+	if (tTd(64, 10))
+		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);
+		return NULL;
+	}
+
+	bp = buf;
+	while ((c = getc(e->e_dfp)) != EOF)
+	{
+		/*  Change LF to CRLF */
+		if (c == '\n')
+		{
+			/* Not a CRLF already? */
+			if (prevchar != '\r')
+			{
+				/* Room for CR now? */
+				if (bp + 2 > &buf[sizeof buf])
+				{
+					/* No room, buffer LF */
+					bufchar = c;
+
+					/* and send CR now */
+					c = '\r';
+				}
+				else
+				{
+					/* Room to do it now */
+					*bp++ = '\r';
+					prevchar = '\r';
+				}
+			}
+		}
+		*bp++ = (char) c;
+		prevchar = c;
+		if (bp >= &buf[sizeof buf])
+		{
+			/* send chunk */
+			response = milter_send_command(m, SMFIC_BODY, buf,
+						       bp - buf, e, state);
+			bp = buf;
+			if (bufchar != '\0')
+			{
+				*bp++ = bufchar;
+				bufchar = '\0';
+				prevchar = bufchar;
+			}
+		}
+		if (m->mf_state == SMFS_ERROR ||
+		    m->mf_state == SMFS_DONE ||
+		    *state != SMFIR_CONTINUE)
+			break;
+	}
+
+	/* check for read errors */
+	if (ferror(e->e_dfp))
+	{
+		ExitStat = EX_IOERR;
+		if (*state == SMFIR_CONTINUE ||
+		    *state == SMFIR_ACCEPT)
+		{
+			*state = SMFIR_TEMPFAIL;
+			if (response != NULL)
+			{
+				sm_free(response);
+				response = NULL;
+			}
+		}
+		syserr("milter_body: %s/df%s: read error",
+		       qid_printqueue(e->e_queuedir), e->e_id);
+		return response;
+	}
+
+	/* send last body chunk */
+	if (bp > buf &&
+	    m->mf_state != SMFS_ERROR &&
+	    m->mf_state != SMFS_DONE &&
+	    *state == SMFIR_CONTINUE)
+	{
+		/* send chunk */
+		response = milter_send_command(m, SMFIC_BODY, buf, bp - buf,
+					       e, state);
+		bp = buf;
+	}
+	return response;
+}
+
+/*
+**  Actions
+*/
+
+/*
+**  MILTER_ADDHEADER -- Add the supplied header to the message
+**
+**	Parameters:
+**		response -- encoded form of header/value.
+**		rlen -- length of response.
+**		e -- current envelope.
+**
+**	Returns:
 **		none
 */
 
@@ -1733,6 +2326,7 @@
 	ENVELOPE *e;
 {
 	char *val;
+	HDR *h;
 
 	if (tTd(64, 10))
 		dprintf("milter_addheader: ");
@@ -1745,7 +2339,7 @@
 		return;
 	}
 
-	if (rlen < 2 || strlen(response) + 1 >= rlen)
+	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
 	{
 		if (tTd(64, 10))
 			dprintf("didn't follow protocol (total len)\n");
@@ -1756,20 +2350,194 @@
 	val = response + strlen(response) + 1;
 
 	/* another sanity check */
-	if (strlen(response) + strlen(val) + 2 != rlen)
+	if (strlen(response) + strlen(val) + 2 != (size_t) rlen)
 	{
 		if (tTd(64, 10))
 			dprintf("didn't follow protocol (part len)\n");
 		return;
 	}
 
+	if (*response == '\0')
+	{
+		if (tTd(64, 10))
+			dprintf("empty field name\n");
+		return;
+	}
+
+	for (h = e->e_header; h != NULL; h = h->h_link)
+	{
+		if (strcasecmp(h->h_field, response) == 0 &&
+		    !bitset(H_USER, h->h_flags) &&
+		    !bitset(H_TRACE, h->h_flags))
+			break;
+	}
+
 	/* add to e_msgsize */
 	e->e_msgsize += strlen(response) + 2 + strlen(val);
 
+	if (h != NULL)
+	{
+		if (tTd(64, 10))
+			dprintf("Replace default header %s value with %s\n",
+				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);
+	}
+}
+/*
+**  MILTER_CHANGEHEADER -- Change the supplied header in the message
+**
+**	Parameters:
+**		response -- encoded form of header/index/value.
+**		rlen -- length of response.
+**		e -- current envelope.
+**
+**	Returns:
+**		none
+*/
+
+static void
+milter_changeheader(response, rlen, e)
+	char *response;
+	ssize_t rlen;
+	ENVELOPE *e;
+{
+	mi_int32 i, index;
+	char *field, *val;
+	HDR *h, *sysheader;
+
 	if (tTd(64, 10))
-		dprintf("%s: %s\n", response, val);
+		dprintf("milter_changeheader: ");
+
+	/* sanity checks */
+	if (response == NULL)
+	{
+		if (tTd(64, 10))
+			dprintf("NULL response\n");
+		return;
+	}
 
-	addheader(newstr(response), val, &e->e_header);
+	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
+	{
+		if (tTd(64, 10))
+			dprintf("didn't follow protocol (total len)\n");
+		return;
+	}
+
+	/* Find separating NUL */
+	(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
+	index = ntohl(i);
+	field = response + MILTER_LEN_BYTES;
+	val = field + strlen(field) + 1;
+
+	/* another sanity check */
+	if (MILTER_LEN_BYTES + strlen(field) + 1 +
+	    strlen(val) + 1 != (size_t) rlen)
+	{
+		if (tTd(64, 10))
+			dprintf("didn't follow protocol (part len)\n");
+		return;
+	}
+
+	if (*field == '\0')
+	{
+		if (tTd(64, 10))
+			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 (bitset(H_USER, h->h_flags) &&
+			    --index <= 0)
+			{
+				sysheader = NULL;
+				break;
+			}
+			else if (!bitset(H_USER, h->h_flags) &&
+				 !bitset(H_TRACE, h->h_flags))
+			{
+				/*
+				**  DRUMS msg-fmt draft says can only have
+				**  multiple occurences of trace fields,
+				**  so make sure we replace any non-trace,
+				**  non-user field.
+				*/
+
+				sysheader = h;
+			}
+		}
+	}
+
+	/* if not found as user-provided header at index, use sysheader */
+	if (h == NULL)
+		h = sysheader;
+
+	if (h == NULL)
+	{
+		if (*val == '\0')
+		{
+			if (tTd(64, 10))
+				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);
+		}
+		return;
+	}
+
+	if (tTd(64, 10))
+	{
+		if (*val == '\0')
+		{
+			dprintf("Delete%s %s: %s\n",
+				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);
+		}
+	}
+
+	if (h != sysheader && h->h_value != NULL)
+	{
+		e->e_msgsize -= strlen(h->h_value);
+		sm_free(h->h_value);
+	}
+
+	if (*val == '\0')
+	{
+		/* Remove "Field: " from message size */
+		if (h != sysheader)
+			e->e_msgsize -= strlen(h->h_field) + 2;
+		h->h_value = NULL;
+	}
+	else
+	{
+		h->h_value = newstr(val);
+		h->h_flags |= H_USER;
+		e->e_msgsize += strlen(h->h_value);
+	}
 }
 /*
 **  MILTER_ADDRCPT -- Add the supplied recipient to the message
@@ -1801,10 +2569,11 @@
 	}
 
 	if (*response == '\0' ||
-	    strlen(response) + 1 != rlen)
+	    strlen(response) + 1 != (size_t) rlen)
 	{
 		if (tTd(64, 10))
-			dprintf("didn't follow protocol (total len)\n");
+			dprintf("didn't follow protocol (total len %d != rlen %d)\n",
+				strlen(response), rlen -1);
 		return;
 	}
 
@@ -1843,7 +2612,7 @@
 	}
 
 	if (*response == '\0' ||
-	    strlen(response) + 1 != rlen)
+	    strlen(response) + 1 != (size_t) rlen)
 	{
 		if (tTd(64, 10))
 			dprintf("didn't follow protocol (total len)\n");
@@ -1859,12 +2628,9 @@
 **  MILTER_REPLBODY -- Replace the current df file with new body
 **
 **	Parameters:
-**		response -- encoded form of new body (first chunk).
-**			Used to return response buffer for return rcmd.
-**		rlen -- length of response.  Also return length of final
-**			response.
-**		rcmd -- current command (used to return new command).
-**		m -- milter filter to read further chunks from.
+**		response -- encoded form of new body.
+**		rlen -- length of response.
+**		newfilter -- if first time called by a new filter
 **		e -- current envelope.
 **
 **	Returns:
@@ -1872,275 +2638,100 @@
 */
 
 static int
-milter_replbody(response, rlen, rcmd, m, e)
-	char **response;
-	ssize_t *rlen;
-	char *rcmd;
-	struct milter *m;
+milter_replbody(response, rlen, newfilter, e)
+	char *response;
+	ssize_t rlen;
+	bool newfilter;
 	ENVELOPE *e;
 {
-	bool failure = FALSE;
-	char prevchar = '\0';
-	int afd;
+	static char prevchar;
 	int i;
-	int save_errno;
-	off_t newsize = 0;
-	struct stat st;
-	char dfname[MAXPATHLEN];
 
 	if (tTd(64, 10))
-		dprintf("milter_replbody(%s)\n", m->mf_name);
-
-	/* save the df file name for later use */
-	(void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
+		dprintf("milter_replbody\n");
 
-	/* Get the current df information */
-	if (bitset(EF_HAS_DF, e->e_flags) &&
-	    e->e_dfp != NULL)
+	/* If a new filter, reset previous character and truncate df */
+	if (newfilter)
 	{
-		afd = fileno(e->e_dfp);
-		if (afd < 0)
-		{
-			save_errno = errno;
-			if (tTd(64, 5))
-				dprintf("milter_replbody(%s): fstat %s: %s\n",
-					m->mf_name, dfname,
-					errstring(save_errno));
-			if (LogLevel > 0)
-				sm_syslog(LOG_ERR, e->e_id,
-					  "milter_replbody(%s): fstat %s: %s",
-					  m->mf_name, dfname,
-					  errstring(save_errno));
-			failure = TRUE;
-		}
-		else
-		{
-			/* fixup e->e_msgsize */
-			if (fstat(afd, &st) == 0)
-			{
-				newsize = e->e_msgsize - st.st_size;
-				if (newsize < 0)
-					newsize = 0;
-			}
-		}
-	}
+		off_t prevsize = 0;
+		char dfname[MAXPATHLEN];
 
-	/*
-	**  In SuperSafe 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
-	**  buffered file I/O descriptor, still open for writing
-	**  so there isn't as much work to do, just truncate it
-	**  and go.
-	*/
+		(void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
 
-	if (SuperSafe)
-	{
-		/* close read-only df */
-		if (bitset(EF_HAS_DF, e->e_flags) &&
-		    e->e_dfp != NULL)
-			(void) fclose(e->e_dfp);
+		/* Reset prevchar */
+		prevchar = '\0';
 
-		/* open writable */
-		if ((e->e_dfp = fopen(dfname, "w")) == NULL)
+		/* Get the current df information */
+		if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
 		{
-			save_errno = errno;
-			if (tTd(64, 5))
-				dprintf("milter_replbody(%s): fopen %s: %s\n",
-					m->mf_name, dfname,
-					errstring(save_errno));
-			if (LogLevel > 0)
-				sm_syslog(LOG_ERR, e->e_id,
-					  "milter_replbody(%s): fopen %s: %s",
-					  m->mf_name, dfname,
-					  errstring(save_errno));
-			e->e_flags &= ~EF_HAS_DF;
-			failure = TRUE;
-		}
-	}
-	else if (e->e_dfp == NULL)
-	{
-		/* shouldn't happen */
-		if (tTd(64, 5))
-			dprintf("milter_replbody(%s): NULL e_dfp (%s)\n",
-				m->mf_name, dfname);
-		if (LogLevel > 0)
-			sm_syslog(LOG_ERR, e->e_id,
-				  "milter_replbody(%s): NULL e_dfp (%s)\n",
-				  m->mf_name, dfname);
-			failure = TRUE;
-	}
-	else if (bftruncate(e->e_dfp) < 0)
-	{
-		save_errno = errno;
-		if (tTd(64, 5))
-			dprintf("milter_replbody(%s): bftruncate %s: %s\n",
-				m->mf_name, dfname, errstring(save_errno));
-		if (LogLevel > 0)
-			sm_syslog(LOG_ERR, e->e_id,
-				  "milter_replbody(%s): bftruncate %s: %s",
-				  m->mf_name, dfname, errstring(save_errno));
-		failure = TRUE;
-	}
+			int afd;
+			struct stat st;
 
-	for (;;)
-	{
-		if (*response != NULL)
-		{
-			/*
-			**  Can't simply return on failure, have to
-			**  collect all data from remote filter to
-			**  prevent the protocol from getting out of sync.
-			**  Another way to fix this would be to have the
-			**  MTA ACK/NAK each body chunk it receives.
-			*/
-
-			if (!failure)
-			{
-				for (i = 0; i < *rlen; i++)
-				{
-					/* Buffered char from last chunk */
-					if (i == 0 && prevchar == '\r')
-					{
-						/* Not CRLF, output prevchar */
-						if ((*response)[i] != '\n')
-						{
-							(void) putc(prevchar,
-								    e->e_dfp);
-							if (newsize > 0)
-								newsize++;
-						}
-						prevchar = '\0';
-					}
-
-					/* Turn CRLF into LF */
-					if ((*response)[i] == '\r')
-					{
-						/* check if at end of chunk */
-						if (i + 1 < *rlen)
-						{
-							/* If LF, strip CR */
-							if ((*response)[i + 1] == '\n')
-								i++;
-						}
-						else
-						{
-							/* check next chunk */
-							prevchar = '\r';
-						}
-					}
-					(void) putc((*response)[i], e->e_dfp);
-					if (newsize > 0)
-						newsize++;
-				}
-			}
-			free(*response);
-			*response = NULL;
+			afd = fileno(e->e_dfp);
+			if (afd > 0 && fstat(afd, &st) == 0)
+				prevsize = st.st_size;
 		}
-
-		/* Get next command (might be another body chunk) */
-		*response = milter_read(m, rcmd, rlen,
-					m->mf_timeout[SMFTO_READ], e);
 
-		if (m->mf_state == SMFS_ERROR)
+		/* truncate current df file */
+		if (bftruncate(e->e_dfp) < 0)
 		{
-			if (SuperSafe)
-			{
-				(void) fclose(e->e_dfp);
-				e->e_dfp = NULL;
-				e->e_flags &= ~EF_HAS_DF;
-			}
-			failure = TRUE;
-			break;
+			MILTER_DF_ERROR("milter_reopen_df: bftruncate %s: %s");
+			return -1;
 		}
-
-		/* If not another body chunk, save for returning */
-		if (*rcmd != SMFIR_REPLBODY)
-			break;
-
-		if (tTd(64, 10))
-			dprintf("milter_replbody(%s): returned %c%s%s\n",
-				m->mf_name, (char) *rcmd,
-				*response == NULL ? "" : ":",
-				*response == NULL ? "" : *response);
-	}
-
-	/* Now it's safe to return */
-	if (failure)
-		return -1;
-
-	if (fflush(e->e_dfp) != 0 || ferror(e->e_dfp))
-	{
-		save_errno = errno;
-		if (tTd(64, 5))
-			dprintf("milter_replbody(%s): error writing/flushing %s: %s\n",
-				m->mf_name, dfname, errstring(save_errno));
-		if (LogLevel > 0)
-			sm_syslog(LOG_ERR, e->e_id,
-				  "milter_replbody(%s): error writing/flushing %s: %s",
-				  m->mf_name, dfname, errstring(save_errno));
-		if (SuperSafe)
-		{
-			(void) fclose(e->e_dfp);
-			e->e_dfp = NULL;
-			e->e_flags &= ~EF_HAS_DF;
+		else
+		{
+			if (prevsize > e->e_msgsize)
+				e->e_msgsize = 0;
+			else
+				e->e_msgsize -= prevsize;
 		}
-		return -1;
-	}
-	else if (!SuperSafe)
-	{
-		/* skip next few clauses */
-		/* EMPTY */
-	}
-	else if ((afd = fileno(e->e_dfp)) >= 0 && fsync(afd) < 0)
-	{
-		save_errno = errno;
-		if (tTd(64, 5))
-			dprintf("milter_replbody(%s): error sync'ing %s: %s\n",
-				m->mf_name, dfname, errstring(save_errno));
-		if (LogLevel > 0)
-			sm_syslog(LOG_ERR, e->e_id,
-				  "milter_replbody(%s): error sync'ing %s: %s",
-				  m->mf_name, dfname, errstring(save_errno));
-		(void) fclose(e->e_dfp);
-		e->e_dfp = NULL;
-		e->e_flags &= ~EF_HAS_DF;
-		return -1;
 	}
-	else if (fclose(e->e_dfp) < 0)
+
+	if (response == NULL)
 	{
-		save_errno = errno;
-		if (tTd(64, 5))
-			dprintf("milter_replbody(%s): error closing %s: %s\n",
-				m->mf_name, dfname, errstring(save_errno));
-		if (LogLevel > 0)
-			sm_syslog(LOG_ERR, e->e_id,
-				  "milter_replbody(%s): error closing %s: %s",
-				  m->mf_name, dfname, errstring(save_errno));
-		e->e_flags &= ~EF_HAS_DF;
-		return -1;
+		/* Flush the buffered '\r' */
+		if (prevchar == '\r')
+		{
+			(void) putc(prevchar, e->e_dfp);
+			e->e_msgsize++;
+		}
+		return 0;
 	}
-	else if ((e->e_dfp = fopen(dfname, "r")) == NULL)
+
+	for (i = 0; i < rlen; i++)
 	{
-		save_errno = errno;
-		if (tTd(64, 5))
-			dprintf("milter_replbody(%s): error reopening %s: %s",
-				m->mf_name, dfname, errstring(save_errno));
-		if (LogLevel > 0)
-			sm_syslog(LOG_ERR, e->e_id,
-				  "milter_replbody(%s): error reopening %s: %s",
-				  m->mf_name, dfname, errstring(save_errno));
-		e->e_flags &= ~EF_HAS_DF;
-		return -1;
-	}
-	else
-		e->e_flags |= EF_HAS_DF;
+		/* Buffered char from last chunk */
+		if (i == 0 && prevchar == '\r')
+		{
+			/* Not CRLF, output prevchar */
+			if (response[i] != '\n')
+			{
+				(void) putc(prevchar, e->e_dfp);
+				e->e_msgsize++;
+			}
+			prevchar = '\0';
+		}
 
-	/* Set the message size */
-	if (newsize > 0)
-		e->e_msgsize = newsize;
+		/* Turn CRLF into LF */
+		if (response[i] == '\r')
+		{
+			/* check if at end of chunk */
+			if (i + 1 < rlen)
+			{
+				/* If LF, strip CR */
+				if (response[i + 1] == '\n')
+					i++;
+			}
+			else
+			{
+				/* check next chunk */
+				prevchar = '\r';
+				continue;
+			}
+		}
+		(void) putc(response[i], e->e_dfp);
+		e->e_msgsize++;
+	}
 	return 0;
 }
 
@@ -2178,7 +2769,7 @@
 		m->mf_sock = milter_open(m, FALSE, e);
 		if (m->mf_state == SMFS_ERROR)
 		{
-			MILTER_CHECK_ERROR();
+			MILTER_CHECK_ERROR(continue);
 			break;
 		}
 
@@ -2192,17 +2783,8 @@
 					m->mf_sock < 0 ? "open" : "negotiate");
 
 			/* if negotation failure, close socket */
-			if (m->mf_sock >= 0)
-			{
-				(void) close(m->mf_sock);
-				m->mf_sock = -1;
-			}
 			milter_error(m);
-			if (m->mf_state == SMFS_ERROR)
-			{
-				MILTER_CHECK_ERROR();
-				break;
-			}
+			MILTER_CHECK_ERROR(continue);
 		}
 	}
 
@@ -2285,7 +2867,7 @@
 
 	s = strlen(hostname) + 1 + sizeof(family);
 	if (family != SMFIA_UNKNOWN)
-		s += sizeof(port) + strlen(sockinfo);
+		s += sizeof(port) + strlen(sockinfo) + 1;
 
 	buf = (char *)xalloc(s);
 	bp = buf;
@@ -2300,12 +2882,14 @@
 	{
 		(void) memcpy(bp, &port, sizeof port);
 		bp += sizeof port;
-		(void) memcpy(bp, sockinfo, strlen(sockinfo));
+
+		/* include trailing '\0' */
+		(void) memcpy(bp, sockinfo, strlen(sockinfo) + 1);
 	}
 
 	response = milter_command(SMFIC_CONNECT, buf, s,
 				  MilterConnectMacros, e, state);
-	free(buf);
+	sm_free(buf);
 
 	/*
 	**  If this message connection is done for,
@@ -2332,7 +2916,7 @@
 			*state = SMFIR_REJECT;
 		if (response != NULL)
 		{
-			free(response);
+			sm_free(response);
 			response = NULL;
 		}
 	}
@@ -2356,11 +2940,31 @@
 	ENVELOPE *e;
 	char *state;
 {
+	int i;
 	char *response;
 
 	if (tTd(64, 10))
 		dprintf("milter_helo(%s)\n", helo);
 
+	/* HELO/EHLO can come after encryption is negotiated */
+	for (i = 0; InputFilters[i] != NULL; i++)
+	{
+		struct milter *m = InputFilters[i];
+
+		switch (m->mf_state)
+		{
+		  case SMFS_INMSG:
+			/* abort in message filters */
+			milter_abort_filter(m, e);
+			/* FALLTHROUGH */
+
+		  case SMFS_DONE:
+			/* reset done filters */
+			m->mf_state = SMFS_OPEN;
+			break;
+		}
+	}
+
 	response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1,
 				  MilterHeloMacros, e, state);
 	milter_per_connection_check(e);
@@ -2438,7 +3042,7 @@
 	/* send it over */
 	response = milter_command(SMFIC_MAIL, buf, s,
 				  MilterEnvFromMacros, e, state);
-	free(buf);
+	sm_free(buf);
 
 	/*
 	**  If filter rejects/discards a per message command,
@@ -2502,88 +3106,11 @@
 	/* send it over */
 	response = milter_command(SMFIC_RCPT, buf, s,
 				  MilterEnvRcptMacros, e, state);
-	free(buf);
-	return response;
-}
-/*
-**  MILTER_HEADER -- send single header to milter filters
-**
-**	Parameters:
-**		name -- header field name.
-**		value -- header value (including continuation lines).
-**		e -- current envelope.
-**		state -- return state from response.
-**
-**	Returns:
-**		response string (may be NULL)
-*/
-
-char *
-milter_header(name, value, e, state)
-	char *name;
-	char *value;
-	ENVELOPE *e;
-	char *state;
-{
-	char *buf;
-	char *response;
-	ssize_t s;
-
-	if (tTd(64, 10))
-		dprintf("milter_header: %s: %s\n", name, value);
-
-	s = strlen(name) + 1 + strlen(value) + 1;
-	buf = (char *) xalloc(s);
-	snprintf(buf, s, "%s%c%s", name, '\0', value);
-
-	/* send it over */
-	response = milter_command(SMFIC_HEADER, buf, s, (char **)NULL, e, state);
-	free(buf);
-
-	/*
-	**  If filter rejects/discards a per message command,
-	**  abort the other filters since we are done with the
-	**  current message.
-	*/
-
-	MILTER_CHECK_DONE_MSG();
-	return response;
-}
-/*
-**  MILTER_EOH -- notify milter filters that the headers are done
-**
-**	Parameters:
-**		e -- current envelope.
-**		state -- return state from response.
-**
-**	Returns:
-**		response string (may be NULL)
-*/
-
-char *
-milter_eoh(e, state)
-	ENVELOPE *e;
-	char *state;
-{
-	char *response;
-
-	if (tTd(64, 10))
-		dprintf("milter_eoh\n");
-
-	response = milter_command(SMFIC_EOH, (void *) NULL, 0,
-				  (char **)NULL, e, state);
-
-	/*
-	**  If filter rejects/discards a per message command,
-	**  abort the other filters since we are done with the
-	**  current message.
-	*/
-
-	MILTER_CHECK_DONE_MSG();
+	sm_free(buf);
 	return response;
 }
 /*
-**  MILTER_BODY -- send message body and gather final message results
+**  MILTER_DATA -- send message headers/body and gather final message results
 **
 **	Parameters:
 **		e -- current envelope.
@@ -2598,35 +3125,50 @@
 **		  modify the envelope or message.
 */
 
+# define MILTER_CHECK_RESULTS() \
+	if (*state == SMFIR_ACCEPT || \
+	    m->mf_state == SMFS_DONE || \
+	    m->mf_state == SMFS_ERROR) \
+	{ \
+		if (m->mf_state != SMFS_ERROR) \
+			m->mf_state = SMFS_DONE; \
+		continue;	/* to next filter */ \
+	} \
+	if (*state != SMFIR_CONTINUE) \
+	{ \
+		m->mf_state = SMFS_DONE; \
+		goto finishup; \
+	}
+
 char *
-milter_body(e, state)
+milter_data(e, state)
 	ENVELOPE *e;
 	char *state;
 {
-	bool replfailed = FALSE;
-	bool rewind = FALSE;
+	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;
-	char bufchar = '\0';
-	char prevchar = '\0';
 	int i;
-	int c;
 	int save_errno;
-	char *bp;
 	char *response = NULL;
 	time_t eomsent;
 	ssize_t rlen;
-	char buf[MILTER_CHUNK_SIZE];
 
 	if (tTd(64, 10))
-		dprintf("milter_body\n");
+		dprintf("milter_data\n");
 
 	*state = SMFIR_CONTINUE;
 
-/*
-**  XXX: Should actually send body chunks to each filter
-**  a chunk at a time instead of sending the whole body to
-**  each filter in turn
-*/
+	/*
+	**  XXX: Should actually send body chunks to each filter
+	**  a chunk at a time instead of sending the whole body to
+	**  each filter in turn.  However, only if the filters don't
+	**  change the body.
+	*/
+
 	for (i = 0; InputFilters[i] != NULL; i++)
 	{
 		struct milter *m = InputFilters[i];
@@ -2644,6 +3186,14 @@
 
 		/* Now reset state for later evaluation */
 		*state = SMFIR_CONTINUE;
+		newfilter = TRUE;
+
+		/* previous problem? */
+		if (m->mf_state == SMFS_ERROR)
+		{
+			MILTER_CHECK_ERROR(continue);
+			break;
+		}
 
 		/* sanity checks */
 		if (m->mf_sock < 0 ||
@@ -2651,141 +3201,38 @@
 			continue;
 
 		m->mf_state = SMFS_INMSG;
-		bp = buf;
 
-		/* check if filter wants the body */
-		if (bitset(SMFIF_NOBODY, m->mf_fflags))
+		/* check if filter wants the headers */
+		if (!bitset(SMFIP_NOHDRS, m->mf_pflags))
 		{
-			/* still need to send BODYEOB */
-			goto eob;
+			response = milter_headers(m, e, state);
+			MILTER_CHECK_RESULTS();
 		}
 
-		if (e->e_dfp == NULL)
+		/* check if filter wants EOH */
+		if (!bitset(SMFIP_NOEOH, m->mf_pflags))
 		{
-			/* shouldn't happen */
-			goto eob;
-		}
+			if (tTd(64, 10))
+				dprintf("milter_data: eoh\n");
 
-		if (bfrewind(e->e_dfp) < 0)
-		{
-			ExitStat = EX_IOERR;
-			*state = SMFIR_TEMPFAIL;
-			syserr("milter_body: %s/df%s: read error",
-			       qid_printqueue(e->e_queuedir), e->e_id);
-			break;
+			/* send it over */
+			response = milter_send_command(m, SMFIC_EOH, NULL, 0,
+						       e, state);
+			MILTER_CHECK_RESULTS();
 		}
 
-		rewind = TRUE;
-		while ((c = getc(e->e_dfp)) != EOF)
+		/* check if filter wants the body */
+		if (!bitset(SMFIP_NOBODY, m->mf_pflags) &&
+		    e->e_dfp != NULL)
 		{
-			/*  Change LF to CRLF */
-			if (c == '\n')
-			{
-				/* Not a CRLF already? */
-				if (prevchar != '\r')
-				{
-					/* Room for CR now? */
-					if (bp + 2 >= &buf[sizeof buf])
-					{
-						/* No room, buffer LF */
-						bufchar = c;
-
-						/* and send CR now */
-						c = '\r';
-					}
-					else
-					{
-						/* Room to do it all now */
-						*bp++ = '\r';
-						prevchar = '\r';
-					}
-				}
-			}
-			*bp++ = (char) c;
-			prevchar = c;
-			if (bp >= &buf[sizeof buf])
-			{
-				/* send chunk */
-				(void) milter_write(m, SMFIC_BODY, buf,
-						    bp - buf,
-						    m->mf_timeout[SMFTO_WRITE],
-						    e);
-				if (m->mf_state == SMFS_ERROR)
-					break;
-				bp = buf;
-				if (bufchar != '\0')
-				{
-					*bp++ = bufchar;
-					bufchar = '\0';
-					prevchar = bufchar;
-				}
-
-				/* get reply */
-				response = milter_read(m, &rcmd, &rlen,
-						       m->mf_timeout[SMFTO_READ],
-						       e);
-				if (m->mf_state == SMFS_ERROR)
-					break;
-				switch (rcmd)
-				{
-				  case SMFIR_REPLYCODE:
-					MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected");
-					/* FALLTHROUGH */
-
-				  case SMFIR_REJECT:
-				  case SMFIR_DISCARD:
-				  case SMFIR_TEMPFAIL:
-				  case SMFIR_ACCEPT:
-				  case SMFIR_CONTINUE:
-					*state = rcmd;
-					if (*state != SMFIR_CONTINUE)
-						m->mf_state = SMFS_DONE;
-					break;
-
-				  default:
-					/* Invalid response to command */
-					if (LogLevel > 0)
-						sm_syslog(LOG_ERR, e->e_id,
-							  "milter_body(%s): returned bogus response %c",
-							  m->mf_name, rcmd);
-					milter_error(m);
-					break;
-				}
-				if (rcmd != SMFIR_REPLYCODE &&
-				    response != NULL)
-				{
-					free(response);
-					response = NULL;
-				}
-			}
-			if (*state == SMFIR_ACCEPT)
-				break;
-
-			if (*state != SMFIR_CONTINUE)
-				goto finishup;
+			rewind = TRUE;
+			response = milter_body(m, e, state);
+			MILTER_CHECK_RESULTS();
 		}
-
-		/* check for read errors */
-		if (ferror(e->e_dfp))
-			goto death;
-
-		/* if filter accepted the message, move on to the next one */
-		if (*state == SMFIR_ACCEPT)
-			continue;
 
-eob:
-		/* Make sure there wasn't an error above before proceeding */
-		if (m->mf_state != SMFS_ERROR)
-		{
-			/* send the final body chunk */
-			(void) milter_write(m, SMFIC_BODYEOB, buf, bp - buf,
-					    m->mf_timeout[SMFTO_WRITE], e);
-		}
-		else
-		{
-			MILTER_CHECK_ERROR();
-			goto finishup;
-		}
+		/* send the final body chunk */
+		(void) milter_write(m, SMFIC_BODYEOB, NULL, 0,
+				    m->mf_timeout[SMFTO_WRITE], e);
 
 		/* Get time EOM sent for timeout */
 		eomsent = curtime();
@@ -2793,23 +3240,19 @@
 		/* deal with the possibility of multiple responses */
 		while (*state == SMFIR_CONTINUE)
 		{
-			/* From a prevous state */
-			if (m->mf_state == SMFS_ERROR)
-				break;
-
 			/* Check total timeout from EOM to final ACK/NAK */
 			if (m->mf_timeout[SMFTO_EOM] > 0 &&
 			    curtime() - eomsent >= m->mf_timeout[SMFTO_EOM])
 			{
 				if (tTd(64, 5))
-					dprintf("milter_body(%s): EOM ACK/NAK timeout\n",
+					dprintf("milter_data(%s): EOM ACK/NAK timeout\n",
 						m->mf_name);
 				if (LogLevel > 0)
 					sm_syslog(LOG_ERR, e->e_id,
-						  "milter_body(%s): EOM ACK/NAK timeout\n",
+						  "milter_data(%s): EOM ACK/NAK timeout\n",
 						  m->mf_name);
 				milter_error(m);
-				MILTER_CHECK_ERROR();
+				MILTER_CHECK_ERROR(continue);
 				break;
 			}
 
@@ -2818,12 +3261,9 @@
 			if (m->mf_state == SMFS_ERROR)
 				break;
 
-newstate:
 			if (tTd(64, 10))
-				dprintf("milter_body(%s): state %c%s%s\n",
-					m->mf_name, (char) rcmd,
-					response == NULL ? "" : ":",
-					response == NULL ? "" : response);
+				dprintf("milter_data(%s): state %c\n",
+					m->mf_name, (char) rcmd);
 
 			switch (rcmd)
 			{
@@ -2854,22 +3294,33 @@
 				break;
 
 			  case SMFIR_ADDHEADER:
-				if (!bitset(SMFIF_MODHDRS, m->mf_fflags))
+				if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
 				{
 					if (LogLevel > 9)
 						sm_syslog(LOG_WARNING, e->e_id,
-							  "milter_body(%s): lied about adding headers, honoring request anyway",
+							  "milter_data(%s): lied about adding headers, honoring request anyway",
 							  m->mf_name);
 				}
 				milter_addheader(response, rlen, e);
 				break;
 
+			  case SMFIR_CHGHEADER:
+				if (!bitset(SMFIF_CHGHDRS, m->mf_fflags))
+				{
+					if (LogLevel > 9)
+						sm_syslog(LOG_WARNING, e->e_id,
+							  "milter_data(%s): lied about changing headers, honoring request anyway",
+							  m->mf_name);
+				}
+				milter_changeheader(response, rlen, e);
+				break;
+
 			  case SMFIR_ADDRCPT:
 				if (!bitset(SMFIF_ADDRCPT, m->mf_fflags))
 				{
 					if (LogLevel > 9)
 						sm_syslog(LOG_WARNING, e->e_id,
-							  "milter_body(%s) lied about adding recipients, honoring request anyway",
+							  "milter_data(%s) lied about adding recipients, honoring request anyway",
 							  m->mf_name);
 				}
 				milter_addrcpt(response, rlen, e);
@@ -2880,7 +3331,7 @@
 				{
 					if (LogLevel > 9)
 						sm_syslog(LOG_WARNING, e->e_id,
-							  "milter_body(%s): lied about removing recipients, honoring request anyway",
+							  "milter_data(%s): lied about removing recipients, honoring request anyway",
 							  m->mf_name);
 				}
 				milter_delrcpt(response, rlen, e);
@@ -2891,37 +3342,39 @@
 				{
 					if (LogLevel > 0)
 						sm_syslog(LOG_ERR, e->e_id,
-							  "milter_body(%s): lied about replacing body, rejecting request and tempfailing message",
+							  "milter_data(%s): lied about replacing body, rejecting request and tempfailing message",
 							  m->mf_name);
 					replfailed = TRUE;
-					goto newstate;
+					break;
 				}
-				rewind = TRUE;
+
+				/* already failed in attempt */
+				if (replfailed)
+					break;
 
-				/* protect &response in next command */
-				if (response == NULL)
+				if (!dfopen)
 				{
-					response = newstr("");
-					rlen = 0;
+					if (milter_reopen_df(e) < 0)
+					{
+						replfailed = TRUE;
+						break;
+					}
+					dfopen = TRUE;
+					rewind = TRUE;
 				}
 
-				if (milter_replbody(&response, &rlen,
-						    &rcmd, m, e) < 0)
-				{
-					if (LogLevel > 0)
-						sm_syslog(LOG_ERR, e->e_id,
-							  "milter_body(%s): Failed to replace body, tempfailing message",
-							  m->mf_name);
+				if (milter_replbody(response, rlen,
+						    newfilter, e) < 0)
 					replfailed = TRUE;
-					rewind = FALSE;
-				}
-				goto newstate;
+				newfilter = FALSE;
+				replbody = TRUE;
+				break;
 
 			  default:
 				/* Invalid response to command */
 				if (LogLevel > 0)
 					sm_syslog(LOG_ERR, e->e_id,
-						  "milter_body(%s): returned bogus response %c",
+						  "milter_data(%s): returned bogus response %c",
 						  m->mf_name, rcmd);
 				milter_error(m);
 				break;
@@ -2929,22 +3382,56 @@
 			if (rcmd != SMFIR_REPLYCODE &&
 			    response != NULL)
 			{
-				free(response);
+				sm_free(response);
 				response = NULL;
 			}
+
+			if (m->mf_state == SMFS_ERROR)
+				break;
 		}
+
+		if (replbody && !replfailed)
+		{
+			/* flush possible buffered character */
+			milter_replbody(NULL, 0, !replbody, e);
+			replbody = FALSE;
+		}
+
 		if (m->mf_state == SMFS_ERROR)
 		{
-			MILTER_CHECK_ERROR();
+			MILTER_CHECK_ERROR(continue);
 			goto finishup;
 		}
 	}
 
 finishup:
 	/* leave things in the expected state if we touched it */
-	if (rewind && bfrewind(e->e_dfp) < 0)
+	if (replfailed)
+	{
+		if (*state == SMFIR_CONTINUE ||
+		    *state == SMFIR_ACCEPT)
+		{
+			*state = SMFIR_TEMPFAIL;
+			if (response != NULL)
+			{
+				sm_free(response);
+				response = NULL;
+			}
+		}
+
+		if (dfopen)
+		{
+			(void) fclose(e->e_dfp);
+			e->e_dfp = NULL;
+			e->e_flags &= ~EF_HAS_DF;
+			dfopen = FALSE;
+		}
+		rewind = FALSE;
+	}
+
+	if ((dfopen && milter_reset_df(e) < 0) ||
+	    (rewind && bfrewind(e->e_dfp) < 0))
 	{
-death:
 		save_errno = errno;
 		ExitStat = EX_IOERR;
 
@@ -2955,12 +3442,20 @@
 
 		if (*state == SMFIR_CONTINUE ||
 		    *state == SMFIR_ACCEPT)
+		{
 			*state = SMFIR_TEMPFAIL;
+			if (response != NULL)
+			{
+				sm_free(response);
+				response = NULL;
+			}
+		}
 
 		errno = save_errno;
-		syserr("milter_body: %s/df%s: read error",
+		syserr("milter_data: %s/df%s: read error",
 		       qid_printqueue(e->e_queuedir), e->e_id);
 	}
+	MILTER_CHECK_DONE_MSG();
 	return response;
 }
 /*
Index: gnu/usr.sbin/sendmail/sendmail/mime.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/mime.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/sendmail/mime.c	2000/04/02 19:05:46	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/mime.c	2001/01/15 21:09:09	1.2
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
  *	All rights reserved.
  * Copyright (c) 1994, 1996-1997 Eric P. Allman.  All rights reserved.
  * Copyright (c) 1994
@@ -15,7 +15,7 @@
 #include <string.h>
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: mime.c,v 8.94 1999/10/17 17:35:58 ca Exp $";
+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 **));
@@ -277,8 +277,10 @@
 		if (tTd(43, 1))
 			dprintf("mime8to7: multipart boundary \"%s\"\n", bbuf);
 		for (i = 0; i < MAXMIMENESTING; i++)
+		{
 			if (boundaries[i] == NULL)
 				break;
+		}
 		if (i >= MAXMIMENESTING)
 		{
 			usrerr("mime8to7: multipart nesting boundary too deep");
@@ -621,7 +623,7 @@
 					linelen++;
 				}
 			}
-			if (bitnset(c1 & 0xff, badchars))
+			if (bitnset(bitidx(c1), badchars))
 			{
 				*bp++ = '=';
 				*bp++ = Base16Code[(c1 >> 4) & 0x0f];
@@ -828,11 +830,11 @@
 	if (line[0] != '-' || line[1] != '-' || boundaries == NULL)
 		return MBT_NOTSEP;
 	i = strlen(line);
-	if (line[i - 1] == '\n')
+	if (i > 0 && line[i - 1] == '\n')
 		i--;
 
 	/* strip off trailing whitespace */
-	while (line[i - 1] == ' ' || line[i - 1] == '\t')
+	while (i > 0 && (line[i - 1] == ' ' || line[i - 1] == '\t'))
 		i--;
 	savec = line[i];
 	line[i] = '\0';
@@ -904,7 +906,7 @@
 {
 	register int i;
 
-	for (i = 0; boundaries[i] != NULL; i++)
+	for (i = 0; i <= MAXMIMENESTING && boundaries[i] != NULL; i++)
 	{
 		if (strcmp(line, boundaries[i]) == 0)
 			return i;
Index: gnu/usr.sbin/sendmail/sendmail/newaliases.8
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/newaliases.8,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- gnu/usr.sbin/sendmail/sendmail/newaliases.8	2000/06/11 20:43:22	1.1
+++ gnu/usr.sbin/sendmail/sendmail/newaliases.8	2001/01/15 21:09:09	1.2
@@ -9,9 +9,9 @@
 .\" the sendmail distribution.
 .\"
 .\"
-.\"     $Sendmail: newaliases.1,v 8.15 1999/06/22 20:41:34 tony Exp $
+.\"     $Sendmail: newaliases.1,v 8.15.28.1 2000/12/14 23:08:15 gshapiro Exp $
 .\"
-.Dd June 22, 1999
+.Dd December 14, 2000
 .Dt NEWALIASES 8
 .Os
 .Sh NAME
Index: gnu/usr.sbin/sendmail/sendmail/parseaddr.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/parseaddr.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/sendmail/parseaddr.c	2000/04/07 19:20:42	1.2
+++ gnu/usr.sbin/sendmail/sendmail/parseaddr.c	2001/05/29 01:31:15	1.5
@@ -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
@@ -12,7 +12,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: parseaddr.c,v 8.234 2000/03/17 07:32:48 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: parseaddr.c,v 8.234.4.12 2001/05/03 17:24:11 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
@@ -1154,10 +1154,10 @@
 					/* save the remainder of the input */
 					for (xpvp = pvp; *xpvp != NULL; xpvp++)
 						trsize += sizeof *xpvp;
-					if (trsize > pvpb1_size)
+					if ((size_t) trsize > pvpb1_size)
 					{
 						if (pvpb1 != NULL)
-							free(pvpb1);
+							sm_free(pvpb1);
 						pvpb1 = (char **)xalloc(trsize);
 						pvpb1_size = trsize;
 					}
@@ -1583,7 +1583,7 @@
 		if (i > rwbuflen)
 		{
 			if (rwbuf != NULL)
-				free(rwbuf);
+				sm_free(rwbuf);
 			rwbuflen = i;
 			rwbuf = (char *) xalloc(rwbuflen);
 		}
@@ -2054,7 +2054,7 @@
 	{ "QDELAYED",		QDELAYED	},
 	{ "QTHISPASS",		QTHISPASS	},
 	{ "QRCPTOK",		QRCPTOK		},
-	{ NULL }
+	{ NULL,			0		}
 };
 
 void
@@ -2461,7 +2461,7 @@
 		if (tTd(29, 9))
 			dprintf("maplocaluser: address unchanged\n");
 		if (a1 != NULL)
-			free(a1);
+			sm_free(a1);
 		return;
 	}
 
@@ -2644,6 +2644,7 @@
 **		rmcomm -- remove comments?
 **		cnt -- count rejections (statistics)?
 **		logl -- logging level
+**		host -- NULL or relay host.
 **
 **	Returns:
 **		EX_OK -- if the rwset doesn't resolve to $#error
@@ -2651,13 +2652,14 @@
 */
 
 int
-rscheck(rwset, p1, p2, e, rmcomm, cnt, logl)
+rscheck(rwset, p1, p2, e, rmcomm, cnt, logl, host)
 	char *rwset;
 	char *p1;
 	char *p2;
 	ENVELOPE *e;
 	bool rmcomm, cnt;
 	int logl;
+	char *host;
 {
 	char *buf;
 	int bufsize;
@@ -2721,7 +2723,16 @@
 */
 		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 (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET ||
 	    pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 &&
 			       strcmp(pvp[1], "discard") != 0))
@@ -2770,7 +2781,12 @@
 				p2);
 			p += strlen(p);
 		}
-		if ((relay = macvalue('_', e)) != NULL)
+
+		if (host != NULL)
+			relay = host;
+		else
+			relay = macvalue('_', e);
+		if (relay != NULL)
 		{
 			snprintf(p, SPACELEFT(lbuf, p),
 				", relay=%s", relay);
@@ -2792,7 +2808,7 @@
 	QuickAbort = saveQuickAbort;
 	setstat(rstat);
 	if (buf != buf0)
-		free(buf);
+		sm_free(buf);
 
 	if (rstat != EX_OK && QuickAbort)
 		longjmp(TopFrame, 2);
Index: gnu/usr.sbin/sendmail/sendmail/queue.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/queue.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/sendmail/queue.c	2000/04/07 19:20:43	1.2
+++ gnu/usr.sbin/sendmail/sendmail/queue.c	2001/05/29 01:31:15	1.5
@@ -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,17 +11,18 @@
  *
  */
 
+
 #include <sendmail.h>
 
 #ifndef lint
 # if QUEUE
-static char id[] = "@(#)$Sendmail: queue.c,v 8.343 2000/03/15 06:58:09 gshapiro Exp $ (with queueing)";
+static char id[] = "@(#)$Sendmail: queue.c,v 8.343.4.55 2001/05/03 23:37:11 gshapiro Exp $ (with queueing)";
 # else /* QUEUE */
-static char id[] = "@(#)$Sendmail: queue.c,v 8.343 2000/03/15 06:58:09 gshapiro Exp $ (without queueing)";
+static char id[] = "@(#)$Sendmail: queue.c,v 8.343.4.55 2001/05/03 23:37:11 gshapiro Exp $ (without queueing)";
 # endif /* QUEUE */
 #endif /* ! lint */
 
-#include <dirent.h>
+# include <dirent.h>
 
 #if QUEUE
 
@@ -82,6 +83,9 @@
 **		The queue file is left locked.
 */
 
+# define TEMPQF_LETTER 'T'
+# define LOSEQF_LETTER 'Q'
+
 void
 queueup(e, announce)
 	register ENVELOPE *e;
@@ -115,34 +119,38 @@
 	/* if newid, just write the qf file directly (instead of tf file) */
 	if (!newid)
 	{
+		int flags;
+
+		flags = O_CREAT|O_WRONLY|O_EXCL;
+
 		/* get a locked tf file */
 		for (i = 0; i < 128; i++)
 		{
-#if _FFR_QUEUE_FILE_MODE
+			if (tfd < 0)
 			{
+#if _FFR_QUEUE_FILE_MODE
 				MODE_T oldumask;
 
 				if (bitset(S_IWGRP, QueueFileMode))
 					oldumask = umask(002);
-				tfd = open(tf, O_CREAT|O_WRONLY|O_EXCL,
-					   QueueFileMode);
+				tfd = open(tf, flags, QueueFileMode);
 				if (bitset(S_IWGRP, QueueFileMode))
 					(void) umask(oldumask);
-			}
 #else /* _FFR_QUEUE_FILE_MODE */
-			tfd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode);
+				tfd = open(tf, flags, FileMode);
 #endif /* _FFR_QUEUE_FILE_MODE */
 
-			if (tfd < 0)
-			{
-				if (errno != EEXIST)
-					break;
-				if (LogLevel > 0 && (i % 32) == 0)
-					sm_syslog(LOG_ALERT, e->e_id,
-						  "queueup: cannot create %s, uid=%d: %s",
-						  tf, geteuid(), errstring(errno));
+				if (tfd < 0)
+				{
+					if (errno != EEXIST)
+						break;
+					if (LogLevel > 0 && (i % 32) == 0)
+						sm_syslog(LOG_ALERT, e->e_id,
+							  "queueup: cannot create %s, uid=%d: %s",
+							  tf, geteuid(), errstring(errno));
+				}
 			}
-			else
+			if (tfd >= 0)
 			{
 				if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB))
 					break;
@@ -150,13 +158,17 @@
 					sm_syslog(LOG_ALERT, e->e_id,
 						  "queueup: cannot lock %s: %s",
 						  tf, errstring(errno));
-				(void) close(tfd);
+				if ((i % 32) == 31)
+				{
+					(void) close(tfd);
+					tfd = -1;
+				}
 			}
 
 			if ((i % 32) == 31)
 			{
 				/* save the old temp file away */
-				(void) rename(tf, queuename(e, 'T'));
+				(void) rename(tf, queuename(e, TEMPQF_LETTER));
 			}
 			else
 				(void) sleep(i % 32);
@@ -373,6 +385,9 @@
 			(void) putc('F', tfp);
 		if (bitset(QPINGONDELAY, q->q_flags))
 			(void) putc('D', tfp);
+		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));
 		if (announce)
@@ -436,7 +451,7 @@
 		{
 			if (bitset(0200, h->h_macro))
 				fprintf(tfp, "${%s}",
-					macname(h->h_macro & 0377));
+					macname(bitidx(h->h_macro)));
 			else
 				fprintf(tfp, "$%c", h->h_macro);
 		}
@@ -492,7 +507,7 @@
 
 	fprintf(tfp, ".\n");
 
-	if (fflush(tfp) < 0 ||
+	if (fflush(tfp) != 0 ||
 	    (SuperSafe && fsync(fileno(tfp)) < 0) ||
 	    ferror(tfp))
 	{
@@ -509,7 +524,6 @@
 		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
@@ -518,8 +532,7 @@
 		*/
 
 		if (tfd >= 0 && SuperSafe && fsync(tfd) < 0)
-			syserr("!queueup: cannot fsync queue temp file %s",
-			       tf);
+			syserr("!queueup: cannot fsync queue temp file %s", tf);
 
 		/* close and unlock old (locked) qf */
 		if (e->e_lockfp != NULL)
@@ -642,6 +655,9 @@
 	bool ret = TRUE;
 	static int curnum = 0;
 
+	DoQueueRun = FALSE;
+
+
 	if (!forkflag && NumQueues > 1 && !verbose)
 		forkflag = TRUE;
 
@@ -699,11 +715,9 @@
 	register ENVELOPE *e;
 	int njobs;
 	int sequenceno = 0;
-	time_t current_la_time;
+	time_t current_la_time, now;
 	extern ENVELOPE BlankEnvelope;
 
-	DoQueueRun = FALSE;
-
 	/*
 	**  If no work will ever be selected, don't even bother reading
 	**  the queue.
@@ -779,6 +793,12 @@
 			return TRUE;
 		}
 		/* child -- clean up signals */
+
+		/* Reset global flags */
+		RestartRequest = NULL;
+		ShutdownRequest = NULL;
+		PendingSignal = 0;
+
 		clrcontrol();
 		proc_list_clear();
 
@@ -787,7 +807,8 @@
 			      PROC_QUEUE_CHILD);
 		(void) releasesignal(SIGCHLD);
 		(void) setsignal(SIGCHLD, SIG_DFL);
-		(void) setsignal(SIGHUP, intsig);
+		(void) setsignal(SIGHUP, SIG_DFL);
+		(void) setsignal(SIGTERM, intsig);
 	}
 
 	sm_setproctitle(TRUE, CurEnv, "running queue: %s",
@@ -796,7 +817,7 @@
 	if (LogLevel > 69 || tTd(63, 99))
 		sm_syslog(LOG_DEBUG, NOQID,
 			  "runqueue %s, pid=%d, forkflag=%d",
-			  qid_printqueue(queuedir), getpid(), forkflag);
+			  qid_printqueue(queuedir), (int) getpid(), forkflag);
 
 	/*
 	**  Release any resources used by the daemon code.
@@ -820,6 +841,7 @@
 	CurEnv = &QueueEnvelope;
 	e = newenvelope(&QueueEnvelope, CurEnv);
 	e->e_flags = BlankEnvelope.e_flags;
+	e->e_parent = NULL;
 
 	/* make sure we have disconnected from parent */
 	if (forkflag)
@@ -850,6 +872,7 @@
 	/* order the existing work requests */
 	njobs = orderq(queuedir, FALSE);
 
+
 	/* process them once at a time */
 	while (WorkQ != NULL)
 	{
@@ -864,10 +887,11 @@
 		**	Get new load average every 30 seconds.
 		*/
 
-		if (current_la_time < curtime() - 30)
+		now = curtime();
+		if (current_la_time < now - 30)
 		{
 			CurrentLA = sm_getla(e);
-			current_la_time = curtime();
+			current_la_time = now;
 		}
 		if (shouldqueue(WkRecipFact, current_la_time))
 		{
@@ -935,10 +959,10 @@
 			if (pid != 0)
 				(void) waitfor(pid);
 		}
-		free(w->w_name);
+		sm_free(w->w_name);
 		if (w->w_host)
-			free(w->w_host);
-		free((char *) w);
+			sm_free(w->w_host);
+		sm_free((char *) w);
 	}
 
 	/* exit without the usual cleanup */
@@ -951,6 +975,16 @@
 
 /*
 **  RUNQUEUEEVENT -- stub for use in setevent
+**
+**	Parameters:
+**		none.
+**
+**	Returns:
+**		none.
+**
+**	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
@@ -980,6 +1014,7 @@
 # define NEED_T		002
 # define NEED_R		004
 # define NEED_S		010
+# define NEED_H		020
 
 static WORK	*WorkList = NULL;
 static int	WorkListSize = 0;
@@ -1042,10 +1077,10 @@
 		register WORK *nw = w->w_next;
 
 		WorkQ = nw;
-		free(w->w_name);
-		if (w->w_host)
-			free(w->w_host);
-		free((char *) w);
+		sm_free(w->w_name);
+		if (w->w_host != NULL)
+			sm_free(w->w_host);
+		sm_free((char *) w);
 		w = nw;
 	}
 
@@ -1136,7 +1171,9 @@
 		}
 
 		/* avoid work if possible */
-		if (QueueSortOrder == QSO_BYFILENAME)
+		if (QueueSortOrder == QSO_BYFILENAME &&
+		    QueueLimitSender == NULL &&
+		    QueueLimitRecipient == NULL)
 		{
 			w->w_name = newstr(d->d_name);
 			w->w_host = NULL;
@@ -1148,6 +1185,7 @@
 
 		/* open control file */
 		cf = fopen(qf, "r");
+
 		if (cf == NULL)
 		{
 			/* this may be some random person sending hir msgs */
@@ -1170,9 +1208,14 @@
 
 		/* extract useful information */
 		i = NEED_P | NEED_T;
+		if (QueueSortOrder == QSO_BYHOST)
+		{
+			/* need w_host set for host sort order */
+			i |= NEED_H;
+		}
 		if (QueueLimitSender != NULL)
 			i |= NEED_S;
-		if (QueueSortOrder == QSO_BYHOST || QueueLimitRecipient != NULL)
+		if (QueueLimitRecipient != NULL)
 			i |= NEED_R;
 		while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL)
 		{
@@ -1211,6 +1254,7 @@
 				{
 					w->w_host = strrev(&p[1]);
 					makelower(w->w_host);
+					i &= ~NEED_H;
 				}
 				if (QueueLimitRecipient == NULL)
 				{
@@ -1239,17 +1283,17 @@
 				break;
 
 			  case 'S':
-				  check = QueueLimitSender;
-				  while (check != NULL)
-				  {
-					  if (strcontainedin(check->queue_match,
-							     &lbuf[1]))
-						  break;
-					  else
-						  check = check->queue_next;
-				  }
-				  if (check != NULL)
-					  i &= ~NEED_S;
+				check = QueueLimitSender;
+				while (check != NULL)
+				{
+					if (strcontainedin(check->queue_match,
+							   &lbuf[1]))
+						break;
+					else
+						check = check->queue_next;
+				}
+				if (check != NULL)
+					i &= ~NEED_S;
 				break;
 
 			  case 'K':
@@ -1284,9 +1328,9 @@
 			/* don't even bother sorting this job in */
 			if (tTd(41, 49))
 				dprintf("skipping %s (%x)\n", w->w_name, i);
-			free(w->w_name);
+			sm_free(w->w_name);
 			if (w->w_host)
-				free(w->w_host);
+				sm_free(w->w_host);
 			wn--;
 		}
 	}
@@ -1387,7 +1431,7 @@
 		WorkQ = w;
 	}
 	if (WorkList != NULL)
-		free(WorkList);
+		sm_free(WorkList);
 	WorkList = NULL;
 	WorkListSize = 0;
 
@@ -1436,8 +1480,8 @@
 	else
 	{
 		int newsize = WorkListSize + QUEUESEGSIZE;
-		WORK *newlist = (WORK *) realloc((char *)WorkList,
-					  (unsigned)sizeof(WORK) * (newsize + 1));
+		WORK *newlist = (WORK *) xrealloc((char *)WorkList,
+						  (unsigned)sizeof(WORK) * (newsize + 1));
 
 		if (newlist != NULL)
 		{
@@ -1531,7 +1575,7 @@
 		return b->w_lock - a->w_lock;
 
 	/* job priority */
-	return a->w_pri - b->w_pri;
+	return workcmpf0(a, b);
 }
 /*
 **  WORKCMPF2 -- second compare function for ordering work based on host name.
@@ -1572,7 +1616,7 @@
 		return i;
 
 	/* job priority */
-	return a->w_pri - b->w_pri;
+	return workcmpf0(a, b);
 }
 /*
 **  WORKCMPF3 -- simple submission-time-only compare function.
@@ -1734,6 +1778,11 @@
 		**		can recover on interrupt.
 		*/
 
+		/* Reset global flags */
+		RestartRequest = NULL;
+		ShutdownRequest = NULL;
+		PendingSignal = 0;
+
 		/* set basic modes, etc. */
 		(void) alarm(0);
 		clearstats();
@@ -1754,7 +1803,7 @@
 		if (LogLevel > 76)
 			sm_syslog(LOG_DEBUG, e->e_id,
 				  "dowork, pid=%d",
-				  getpid());
+				  (int) getpid());
 
 		/* don't use the headers from sendmail.cf... */
 		e->e_header = NULL;
@@ -1810,10 +1859,9 @@
 {
 	register FILE *qfp;
 	ADDRESS *ctladdr;
-	struct stat st;
+	struct stat st, stf;
 	char *bp;
 	int qfver = 0;
-	int chompflags;
 	long hdrsize = 0;
 	register char *p;
 	char *orcpt = NULL;
@@ -1836,7 +1884,8 @@
 			dprintf("readqf(%s): fopen failure (%s)\n",
 				qf, errstring(errno));
 		errno = save_errno;
-		if (errno != ENOENT)
+		if (errno != ENOENT
+		    )
 			syserr("readqf: no control file %s", qf);
 		return FALSE;
 	}
@@ -1855,19 +1904,59 @@
 	}
 
 	/*
-	**  Check the queue file for plausibility to avoid attacks.
+	**  Prevent locking race condition.
+	**
+	**  Process A: readqf(): qfp = fopen(qffile)
+	**  Process B: queueup(): rename(tf, qf)
+	**  Process B: unlocks(tf)
+	**  Process A: lockfile(qf);
+	**
+	**  Process A (us) has the old qf file (before the rename deleted
+	**  the directory entry) and will be delivering based on old data.
+	**  This can lead to multiple deliveries of the same recipients.
+	**
+	**  Catch this by checking if the underlying qf file has changed
+	**  *after* acquiring our lock and if so, act as though the file
+	**  was still locked (i.e., just return like the lockfile() case
+	**  above.
 	*/
 
-	if (fstat(fileno(qfp), &st) < 0)
+	if (stat(qf, &stf) < 0 ||
+	    fstat(fileno(qfp), &st) < 0)
 	{
 		/* must have been being processed by someone else */
 		if (tTd(40, 8))
-			dprintf("readqf(%s): fstat failure (%s)\n",
+			dprintf("readqf(%s): [f]stat failure (%s)\n",
 				qf, errstring(errno));
 		(void) fclose(qfp);
 		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.st_gen != stf.st_gen ||
+# 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);
+		if (tTd(40, 8))
+			dprintf("%s: changed\n", e->e_id);
+		if (LogLevel > 19)
+			sm_syslog(LOG_DEBUG, e->e_id, "changed");
+		(void) fclose(qfp);
+		return FALSE;
+	}
+
+	/*
+	**  Check the queue file for plausibility to avoid attacks.
+	*/
+
 	qsafe = S_IWOTH|S_IWGRP;
 #if _FFR_QUEUE_FILE_MODE
 	if (bitset(S_IWGRP, QueueFileMode))
@@ -1937,6 +2026,7 @@
 		u_long qflags;
 		ADDRESS *q;
 		int mid;
+		time_t now;
 		auto char *ep;
 
 		if (tTd(40, 4))
@@ -1998,6 +2088,11 @@
 					  case 'P':
 						qflags |= QPRIMARY;
 						break;
+
+					  case 'A':
+						if (ctladdr != NULL)
+							ctladdr->q_flags |= QALIAS;
+						break;
 					}
 				}
 			}
@@ -2021,8 +2116,7 @@
 			break;
 
 		  case 'H':		/* header */
-			chompflags = 0;
-			(void) chompheader(&bp[1], &chompflags, NULL, e);
+			(void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e);
 			hdrsize += strlen(&bp[1]);
 			break;
 
@@ -2074,12 +2168,13 @@
 			e->e_ntries = atoi(&buf[1]);
 
 			/* if this has been tried recently, let it be */
-			if (e->e_ntries > 0 && e->e_dtime <= curtime() &&
-			    curtime() < e->e_dtime + queuedelay(e))
+			now = curtime();
+			if (e->e_ntries > 0 && e->e_dtime <= now &&
+			    now < e->e_dtime + queuedelay(e))
 			{
 				char *howlong;
 
-				howlong = pintvl(curtime() - e->e_dtime, TRUE);
+				howlong = pintvl(now - e->e_dtime, TRUE);
 				if (Verbose)
 					printf("%s: too young (%s)\n",
 					       e->e_id, howlong);
@@ -2165,8 +2260,35 @@
 			break;
 
 		  case '$':		/* define macro */
-			mid = macid(&bp[1], &ep);
-			define(mid, newstr(ep), e);
+			{
+				char *p;
+
+				mid = macid(&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;
+			}
 			break;
 
 		  case '.':		/* terminate file */
@@ -2182,7 +2304,7 @@
 		}
 
 		if (bp != buf)
-			free(bp);
+			sm_free(bp);
 	}
 
 	/*
@@ -2290,7 +2412,11 @@
 	int i, nrequests = 0;
 
 	for (i = 0; i < NumQueues; i++)
+	{
+		if (StopRequest)
+			stop_sendmail();
 		nrequests += print_single_queue(i);
+	}
 	if (NumQueues > 1)
 		printf("\t\tTotal Requests: %d\n", nrequests);
 }
@@ -2301,7 +2427,7 @@
 **		queuedir -- queue directory
 **
 **	Returns:
-**		none.
+**		number of entries
 **
 **	Side Effects:
 **		Prints a listing of the mail queue on the standard output.
@@ -2406,6 +2532,9 @@
 		char bodytype[MAXNAME + 1];
 		char qf[MAXPATHLEN];
 
+		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");
@@ -2438,6 +2567,9 @@
 			register int i;
 			register char *p;
 
+			if (StopRequest)
+				stop_sendmail();
+
 			fixcrlf(buf, TRUE);
 			switch (buf[0])
 			{
@@ -2580,9 +2712,9 @@
 				sub = "/df";
 			break;
 
-		  case 'T':
+		  case TEMPQF_LETTER:
 		  case 't':
-		  case 'Q':
+		  case LOSEQF_LETTER:
 		  case 'q':
 			if (bitset(QP_SUBQF, QPaths[e->e_queuedir].qp_subdirs))
 				sub = "/qf";
@@ -2619,7 +2751,8 @@
 **		none.
 */
 
-static char	Base60Code[] =	"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx";
+static const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx";
+# define QIC_LEN	60
 
 void
 assign_queueid(e)
@@ -2635,7 +2768,7 @@
 		return;
 
 	/* see if we need to get a new base time/pid */
-	if (cX >= 60 || LastQueueTime == 0 || LastQueuePid != pid)
+	if (cX >= QIC_LEN || LastQueueTime == 0 || LastQueuePid != pid)
 	{
 		time_t then = LastQueueTime;
 
@@ -2653,16 +2786,16 @@
 	}
 	if (tTd(7, 50))
 		dprintf("assign_queueid: random_offset = %ld (%d)\n",
-			random_offset, (int)(cX + random_offset) % 60);
+			random_offset, (int)(cX + random_offset) % QIC_LEN);
 
 	tm = gmtime(&LastQueueTime);
-	idbuf[0] = Base60Code[tm->tm_year % 60];
-	idbuf[1] = Base60Code[tm->tm_mon];
-	idbuf[2] = Base60Code[tm->tm_mday];
-	idbuf[3] = Base60Code[tm->tm_hour];
-	idbuf[4] = Base60Code[tm->tm_min];
-	idbuf[5] = Base60Code[tm->tm_sec];
-	idbuf[6] = Base60Code[((int)cX++ + random_offset) % 60];
+	idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN];
+	idbuf[1] = QueueIdChars[tm->tm_mon];
+	idbuf[2] = QueueIdChars[tm->tm_mday];
+	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);
@@ -2722,6 +2855,7 @@
 		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);
@@ -2803,7 +2937,9 @@
 		}
 		else if ((pw = sm_getpwnam(user)) != NULL)
 		{
-			if (strcmp(pw->pw_dir, "/") == 0)
+			if (*pw->pw_dir == '\0')
+				a->q_home = NULL;
+			else if (strcmp(pw->pw_dir, "/") == 0)
 				a->q_home = "";
 			else
 				a->q_home = newstr(pw->pw_dir);
@@ -2846,7 +2982,7 @@
 	if (strlen(p) >= (SIZE_T) sizeof buf)
 		return;
 	(void) strlcpy(buf, p, sizeof buf);
-	p = queuename(e, 'Q');
+	p = queuename(e, LOSEQF_LETTER);
 	if (rename(buf, p) < 0)
 		syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid());
 	else if (LogLevel > 0)
@@ -2990,6 +3126,10 @@
 	struct stat statb;
 	int i;
 
+	/* skip over . and .. directories */
+	if (name[0] == '.' &&
+	    (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
+		return FALSE;
 # if HASLSTAT
 	if (lstat(name, &statb) < 0)
 # else /* HASLSTAT */
@@ -3069,9 +3209,9 @@
 		for (i = 0; i < NumQueues; i++)
 		{
 			if (QPaths[i].qp_name != NULL)
-				(void) free(QPaths[i].qp_name);
+				sm_free(QPaths[i].qp_name);
 		}
-		(void) free((char *)QPaths);
+		sm_free((char *)QPaths);
 		QPaths = NULL;
 		NumQueues = 0;
 	}
@@ -3091,7 +3231,7 @@
 			syserr("QueueDirectory: can not wildcard relative path");
 			if (tTd(41, 2))
 				dprintf("multiqueue_cache: \"%s\": Can not wildcard relative path.\n",
-					QueueDir);
+					qpath);
 			ExitStat = EX_CONFIG;
 			return;
 		}
@@ -3164,9 +3304,9 @@
 			}
 			else if (slotsleft < 1)
 			{
-				QPaths = (QPATHS *)realloc((char *)QPaths,
-							  (sizeof *QPaths) *
-							  (NumQueues + 10));
+				QPaths = (QPATHS *)xrealloc((char *)QPaths,
+							    (sizeof *QPaths) *
+							    (NumQueues + 10));
 				if (QPaths == NULL)
 				{
 					(void) closedir(dp);
Index: gnu/usr.sbin/sendmail/sendmail/readcf.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/readcf.c,v
retrieving revision 1.3
retrieving revision 1.6
diff -u -r1.3 -r1.6
--- gnu/usr.sbin/sendmail/sendmail/readcf.c	2000/10/09 23:45:01	1.3
+++ gnu/usr.sbin/sendmail/sendmail/readcf.c	2001/05/29 01:31:16	1.6
@@ -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
@@ -12,10 +12,12 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: readcf.c,v 8.382 2000/04/06 18:02:33 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: readcf.c,v 8.382.4.40 2001/05/03 17:24:13 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
+
+
 #if NETINET || NETINET6
 # include <arpa/inet.h>
 #endif /* NETINET || NETINET6 */
@@ -29,9 +31,6 @@
 static char	**makeargv __P((char *));
 static void	settimeout __P((char *, char *, bool));
 static void	toomany __P((int, int));
-#if _FFR_MILTER
-static void	milter_setup __P((char *));
-#endif /* _FFR_MILTER */
 
 /*
 **  READCF -- read configuration file.
@@ -96,7 +95,6 @@
 	char *file;
 	bool optional;
 	int mid;
-	int chompflags;
 	register char *p;
 	long sff = SFF_OPENASROOT;
 	struct stat statb;
@@ -150,7 +148,7 @@
 		if (bp[0] == '#')
 		{
 			if (bp != buf)
-				free(bp);
+				sm_free(bp);
 			continue;
 		}
 
@@ -358,13 +356,14 @@
 
 		  case 'D':		/* macro definition */
 			mid = macid(&bp[1], &ep);
+			if (mid == 0)
+				break;
 			p = munchstring(ep, NULL, '\0');
 			define(mid, newstr(p), e);
 			break;
 
 		  case 'H':		/* required header line */
-			chompflags = CHHDR_DEF;
-			(void) chompheader(&bp[1], &chompflags, NULL, e);
+			(void) chompheader(&bp[1], CHHDR_DEF, NULL, e);
 			break;
 
 		  case 'C':		/* word class */
@@ -372,6 +371,8 @@
 			if (bp[0] == 'C')
 			{
 				mid = macid(&bp[1], &ep);
+				if (mid == 0)
+					break;
 				expand(ep, exbuf, sizeof exbuf, e);
 				p = exbuf;
 			}
@@ -400,6 +401,8 @@
 
 		  case 'F':		/* word class from file */
 			mid = macid(&bp[1], &ep);
+			if (mid == 0)
+				break;
 			for (p = ep; isascii(*p) && isspace(*p); )
 				p++;
 			if (p[0] == '-' && p[1] == 'o')
@@ -412,13 +415,16 @@
 			}
 			else
 				optional = FALSE;
+
 			file = p;
+			q = p;
+			while (*q != '\0' && !(isascii(*q) && isspace(*q)))
+				q++;
 			if (*file == '|')
 				p = "%s";
 			else
 			{
-				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
-					p++;
+				p = q;
 				if (*p == '\0')
 					p = "%s";
 				else
@@ -539,7 +545,7 @@
 			syserr("unknown configuration line \"%s\"", bp);
 		}
 		if (bp != buf)
-			free(bp);
+			sm_free(bp);
 	}
 	if (ferror(cf))
 	{
@@ -768,7 +774,7 @@
 	if (f == NULL)
 	{
 		if (!optional)
-			syserr("fileclass: cannot open %s", filename);
+			syserr("fileclass: cannot open '%s'", filename);
 		return;
 	}
 
@@ -818,113 +824,7 @@
 	if (pid > 0)
 		(void) waitfor(pid);
 }
-#if _FFR_MILTER
 /*
-**  MILTER_SETUP -- setup structure for a mail filter
-**
-**	Parameters:
-**		line -- the options line.
-**
-**	Returns:
-**		none
-*/
-
-static void
-milter_setup(line)
-	char *line;
-{
-	char fcode;
-	register char *p;
-	register struct milter *m;
-	STAB *s;
-
-	/* collect the mailer name */
-	for (p = line;
-	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
-	     p++)
-		continue;
-	if (*p != '\0')
-		*p++ = '\0';
-	if (line[0] == '\0')
-	{
-		syserr("name required for mail filter");
-		return;
-	}
-	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_WRITE] = (time_t) 10;
-	m->mf_timeout[SMFTO_READ] = (time_t) 10;
-	m->mf_timeout[SMFTO_EOM] = (time_t) 5 MINUTES;
-
-	/* now scan through and assign info from the fields */
-	while (*p != '\0')
-	{
-		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("X%s: `=' expected", m->mf_name);
-			return;
-		}
-		while (isascii(*p) && isspace(*p))
-			p++;
-
-		/* p now points to the field body */
-		p = munchstring(p, &delimptr, ',');
-
-		/* install the field into the mailer struct */
-		switch (fcode)
-		{
-		  case 'S':		/* socket */
-			if (p == NULL)
-				m->mf_conn = NULL;
-			else
-				m->mf_conn = newstr(p);
-
-			/* early check for errors */
-			(void) milter_open(m, TRUE, CurEnv);
-			break;
-
-		  case 'F':		/* Milter flags configured on MTA */
-			for (; *p != '\0'; p++)
-			{
-				if (!(isascii(*p) && isspace(*p)))
-					setbitn(*p, m->mf_flags);
-			}
-			break;
-
-		  case 'T':		/* timeouts */
-			milter_parse_timeouts(p, m);
-			break;
-
-		  default:
-			syserr("X%s: unknown filter equate %c=",
-			       m->mf_name, fcode);
-			break;
-		}
-		p = delimptr;
-	}
-
-	/* enter the mailer into the symbol table */
-	s = stab(m->mf_name, ST_MILTER, ST_ENTER);
-	if (s->s_milter != NULL)
-		syserr("X%s: duplicate filter definition", m->mf_name);
-	else
-		s->s_milter = m;
-}
-#endif /* _FFR_MILTER */
-/*
 **  MAKEMAILER -- define a new mailer.
 **
 **	Parameters:
@@ -944,6 +844,8 @@
 **			   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
+**			   / -- new root directory
 **			The first word is the canonical name of the mailer.
 **
 **	Returns:
@@ -975,7 +877,10 @@
 	if (*p != '\0')
 		*p++ = '\0';
 	if (line[0] == '\0')
+	{
 		syserr("name required for mailer");
+		return;
+	}
 	m->m_name = newstr(line);
 
 	/* now scan through and assign info from the fields */
@@ -1007,13 +912,14 @@
 		  case 'P':		/* pathname */
 			if (*p == '\0')
 				syserr("mailer %s: empty path name", m->m_name);
-			m->m_mailer = newstr(p);
+			else
+				m->m_mailer = newstr(p);
 			break;
 
 		  case 'F':		/* flags */
 			for (; *p != '\0'; p++)
 				if (!(isascii(*p) && isspace(*p)))
-					setbitn(*p, m->m_flags);
+					setbitn(bitidx(*p), m->m_flags);
 			break;
 
 		  case 'S':		/* sender rewriting ruleset */
@@ -1043,14 +949,16 @@
 			if (*p == '\0')
 				syserr("mailer %s: null end-of-line string",
 					m->m_name);
-			m->m_eol = newstr(p);
+			else
+				m->m_eol = newstr(p);
 			break;
 
 		  case 'A':		/* argument vector */
 			if (*p == '\0')
 				syserr("mailer %s: null argument vector",
 					m->m_name);
-			m->m_argv = makeargv(p);
+			else
+				m->m_argv = makeargv(p);
 			break;
 
 		  case 'M':		/* maximum message size */
@@ -1061,6 +969,12 @@
 			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);
 			if (m->m_linelimit < 0)
@@ -1075,13 +989,15 @@
 			if (*p == '\0')
 				syserr("mailer %s: null working directory",
 					m->m_name);
-			m->m_execdir = newstr(p);
+			else
+				m->m_execdir = newstr(p);
 			break;
 
 		  case 'C':		/* default charset */
 			if (*p == '\0')
 				syserr("mailer %s: null charset", m->m_name);
-			m->m_defcharset = newstr(p);
+			else
+				m->m_defcharset = newstr(p);
 			break;
 
 		  case 'T':		/* MTA-Name/Address/Diagnostic types */
@@ -1130,11 +1046,17 @@
 				if (*p != '\0')
 					*p++ = '\0';
 				if (*q == '\0')
+				{
 					syserr("mailer %s: null user name",
 						m->m_name);
+					break;
+				}
 				pw = sm_getpwnam(q);
 				if (pw == NULL)
+				{
 					syserr("readcf: mailer U= flag: unknown user %s", q);
+					break;
+				}
 				else
 				{
 					m->m_uid = pw->pw_uid;
@@ -1165,11 +1087,17 @@
 					p++;
 				*p++ = '\0';
 				if (*q == '\0')
+				{
 					syserr("mailer %s: null group name",
 						m->m_name);
+					break;
+				}
 				gr = getgrnam(q);
 				if (gr == NULL)
+				{
 					syserr("readcf: mailer U= flag: unknown group %s", q);
+					break;
+				}
 				else
 					m->m_gid = gr->gr_gid;
 			}
@@ -1218,6 +1146,11 @@
 		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))
 	{
@@ -1229,18 +1162,19 @@
 
 	if (strcmp(m->m_mailer, "[TCP]") == 0)
 	{
-#if _FFR_REMOVE_TCP_PATH
+#if _FFR_REMOVE_TCP_MAILER_PATH
 		syserr("M%s: P=[TCP] is deprecated, use P=[IPC] instead\n",
 		       m->m_name);
-#else /* _FFR_REMOVE_TCP_PATH */
+		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_PATH */
+#endif /* _FFR_REMOVE_TCP_MAILER_PATH */
 	}
 
-	if (strcmp(m->m_mailer, "[IPC]") == 0 ||
+	if (strcmp(m->m_mailer, "[IPC]") == 0
 #if !_FFR_REMOVE_TCP_MAILER_PATH
-	    strcmp(m->m_mailer, "[TCP]") == 0
+	    || strcmp(m->m_mailer, "[TCP]") == 0
 #endif /* !_FFR_REMOVE_TCP_MAILER_PATH */
 	    )
 	{
@@ -1250,13 +1184,14 @@
 		{
 			syserr("M%s: too few parameters for %s mailer",
 			       m->m_name, m->m_mailer);
+			return;
 		}
-		if (strcmp(m->m_argv[0], "TCP") != 0 &&
+		if (strcmp(m->m_argv[0], "TCP") != 0
 #if NETUNIX
-		    strcmp(m->m_argv[0], "FILE") != 0 &&
+		    && strcmp(m->m_argv[0], "FILE") != 0
 #endif /* NETUNIX */
 #if !_FFR_DEPRECATE_IPC_MAILER_ARG
-		    strcmp(m->m_argv[0], "IPC") != 0
+		    && strcmp(m->m_argv[0], "IPC") != 0
 #endif /* !_FFR_DEPRECATE_IPC_MAILER_ARG */
 		    )
 		{
@@ -1281,11 +1216,13 @@
 			       m->m_name,
 			       (m->m_argv[0] == NULL ||
 				m->m_argv[1] == NULL) ? "few" : "many");
+			return;
 		}
 		else if (strcmp(m->m_argv[0], "FILE") != 0)
 		{
 			syserr("M%s: first argument in [FILE] mailer must be FILE",
 			       m->m_name);
+			return;
 		}
 	}
 
@@ -1332,7 +1269,7 @@
 	if (s->s_mailer != NULL)
 	{
 		i = s->s_mailer->m_mno;
-		free(s->s_mailer);
+		sm_free(s->s_mailer);
 	}
 	else
 	{
@@ -1352,6 +1289,10 @@
 **
 **	Returns:
 **		the munched string.
+**
+**	Side Effects:
+**		the munched string is a local static buffer.
+**		it must be copied before the function is called again.
 */
 
 char *
@@ -1530,6 +1471,9 @@
 		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 */
 	if (m->m_argv != NULL)
 	{
 		char **a = m->m_argv;
@@ -1604,9 +1548,7 @@
 	{ "RemoteMode",			'>',		OI_NONE	},
 #endif /* defined(SUN_EXTENSIONS) && defined(REMOTE_MODE) */
 	{ "SevenBitInput",		'7',		OI_SAFE	},
-#if MIME8TO7
 	{ "EightBitMode",		'8',		OI_SAFE	},
-#endif /* MIME8TO7 */
 	{ "AliasFile",			'A',		OI_NONE	},
 	{ "AliasWait",			'a',		OI_NONE	},
 	{ "BlankSub",			'B',		OI_NONE	},
@@ -1763,6 +1705,20 @@
 #define O_QUEUEDELAY	0xb3
 	{ "QueueDelay",			O_QUEUEDELAY,	OI_NONE	},
 #endif /* _FFR_QUEUEDELAY */
+# define O_SRVCERTFILE	0xb4
+	{ "ServerCertFile",		O_SRVCERTFILE,	OI_NONE	},
+# define O_SRVKEYFILE	0xb5
+	{ "Serverkeyfile",		O_SRVKEYFILE,	OI_NONE	},
+# define O_CLTCERTFILE	0xb6
+	{ "ClientCertFile",		O_CLTCERTFILE,	OI_NONE	},
+# define O_CLTKEYFILE	0xb7
+	{ "Clientkeyfile",		O_CLTKEYFILE,	OI_NONE	},
+# define O_CACERTFILE	0xb8
+	{ "CACERTFile",			O_CACERTFILE,	OI_NONE	},
+# define O_CACERTPATH	0xb9
+	{ "CACERTPath",			O_CACERTPATH,	OI_NONE	},
+# define O_DHPARAMS	0xba
+	{ "DHParameters",		O_DHPARAMS,	OI_NONE	},
 #if _FFR_MILTER
 #define O_INPUTMILTER	0xbb
 	{ "InputMailFilters",		O_INPUTMILTER,	OI_NONE	},
@@ -1775,6 +1731,14 @@
 #define O_QUEUE_FILE_MODE	0xbe
 	{ "QueueFileMode",		O_QUEUE_FILE_MODE, OI_NONE	},
 #endif /* _FFR_QUEUE_FILE_MODE */
+# 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
+	{ "RandFile",			O_RANDFILE,	OI_NONE	},
 	{ NULL,				'\0',		OI_NONE	}
 };
 
@@ -1915,9 +1879,12 @@
 	{
 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
 		{
+			int dp;
+
 			if (tTd(37, 1))
 				dprintf(" (unsafe)");
-			(void) drop_privileges(TRUE);
+			dp = drop_privileges(TRUE);
+			setstat(dp);
 		}
 	}
 	if (tTd(37, 1))
@@ -1929,8 +1896,8 @@
 		SevenBitInput = atobool(val);
 		break;
 
-#if MIME8TO7
 	  case '8':		/* handling of 8-bit input */
+#if MIME8TO7
 		switch (*val)
 		{
 		  case 'm':		/* convert 8-bit, convert MIME */
@@ -1967,8 +1934,10 @@
 			syserr("Unknown 8-bit mode %c", *val);
 			finis(FALSE, EX_USAGE);
 		}
-		break;
+#else /* MIME8TO7 */
+		printf("Warning: Option EightBitMode requires MIME8TO7 support\n");
 #endif /* MIME8TO7 */
+		break;
 
 	  case 'A':		/* set default alias file */
 		if (val[0] == '\0')
@@ -2019,6 +1988,7 @@
 		  case SM_DEFER:	/* queue only and defer map lookups */
 #if !QUEUE
 			syserr("need QUEUE to set -odqueue or -oddefer");
+			break;
 #endif /* !QUEUE */
 			/* FALLTHROUGH */
 
@@ -2091,7 +2061,9 @@
 		if (val[0] == '\0')
 			HelpFile = "helpfile";
 		else
+		{
 			HelpFile = newstr(val);
+		}
 		break;
 
 	  case 'h':		/* maximum hop count */
@@ -2126,6 +2098,13 @@
 				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)
@@ -2178,12 +2157,14 @@
 		break;
 
 	  case 'M':		/* define macro */
+		sticky = FALSE;
 		mid = macid(val, &ep);
+		if (mid == 0)
+			break;
 		p = newstr(ep);
 		if (!safe)
 			cleanstrcpy(p, p, MAXNAME);
 		define(mid, p, CurEnv);
-		sticky = FALSE;
 		break;
 
 	  case 'm':		/* send to me too */
@@ -2199,9 +2180,7 @@
 	  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 */
@@ -2238,7 +2217,8 @@
 			}
 			if (pv->pv_name == NULL)
 				syserr("readcf: Op line: %s unrecognized", val);
-			PrivacyFlags |= pv->pv_flag;
+			else
+				PrivacyFlags |= pv->pv_flag;
 		}
 		sticky = FALSE;
 		break;
@@ -2253,9 +2233,13 @@
 
 	  case 'Q':		/* queue directory */
 		if (val[0] == '\0')
+		{
 			QueueDir = "mqueue";
+		}
 		else
+		{
 			QueueDir = newstr(val);
+		}
 		if (RealUid != 0 && !safe)
 			Warn_Q_option = TRUE;
 		break;
@@ -2275,7 +2259,9 @@
 		if (val[0] == '\0')
 			StatFile = "statistics";
 		else
+		{
 			StatFile = newstr(val);
+		}
 		break;
 
 	  case 's':		/* be super safe, even if expensive */
@@ -2321,7 +2307,10 @@
 			DefUid = -1;
 			pw = sm_getpwnam(val);
 			if (pw == NULL)
+			{
 				syserr("readcf: option u: unknown user %s", val);
+				break;
+			}
 			else
 			{
 				DefUid = pw->pw_uid;
@@ -2334,7 +2323,8 @@
 		if (DefUid > UID_MAX)
 		{
 			syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored",
-				DefUid, UID_MAX);
+				(long) DefUid, (long) UID_MAX);
+			break;
 		}
 #endif /* UID_MAX */
 
@@ -2383,6 +2373,7 @@
 		WkTimeFact = atoi(val);
 		break;
 
+
 	  case O_QUEUESORTORD:	/* queue sorting order */
 		switch (*val)
 		{
@@ -2571,7 +2562,9 @@
 
 	  case O_HSDIR:		/* persistent host status directory */
 		if (val[0] != '\0')
+		{
 			HostStatDir = newstr(val);
+		}
 		break;
 
 	  case O_SINGTHREAD:	/* single thread deliveries (requires hsdir) */
@@ -2598,7 +2591,10 @@
 
 			pw = sm_getpwnam(val);
 			if (pw == NULL)
+			{
 				syserr("readcf: option RunAsUser: unknown user %s", val);
+				break;
+			}
 			else if (can_setuid)
 			{
 				if (*p == '\0')
@@ -2611,7 +2607,8 @@
 		if (RunAsUid > UID_MAX)
 		{
 			syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored",
-				RunAsUid, UID_MAX);
+				(long) RunAsUid, (long) UID_MAX);
+			break;
 		}
 #endif /* UID_MAX */
 		if (*p != '\0')
@@ -2644,7 +2641,7 @@
 
 	  case O_PIDFILE:
 		if (PidFile != NULL)
-			free(PidFile);
+			sm_free(PidFile);
 		PidFile = newstr(val);
 		break;
 
@@ -2691,7 +2688,7 @@
 
 	  case O_DEADLETTER:
 		if (DeadLetterDrop != NULL)
-			free(DeadLetterDrop);
+			sm_free(DeadLetterDrop);
 		DeadLetterDrop = newstr(val);
 		break;
 
@@ -2737,7 +2734,10 @@
 			TrustedUid = 0;
 			pw = sm_getpwnam(val);
 			if (pw == NULL)
+			{
 				syserr("readcf: option TrustedUser: unknown user %s", val);
+				break;
+			}
 			else
 				TrustedUid = pw->pw_uid;
 		}
@@ -2746,7 +2746,7 @@
 		if (TrustedUid > UID_MAX)
 		{
 			syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)",
-				TrustedUid, UID_MAX);
+				(long) TrustedUid, (long) UID_MAX);
 			TrustedUid = 0;
 		}
 # endif /* UID_MAX */
@@ -2778,7 +2778,7 @@
 
 	  case O_CONTROLSOCKET:
 		if (ControlSocketName != NULL)
-			free(ControlSocketName);
+			sm_free(ControlSocketName);
 		ControlSocketName = newstr(val);
 		break;
 
@@ -2792,7 +2792,7 @@
 
 	  case O_PROCTITLEPREFIX:
 		if (ProcTitlePrefix != NULL)
-			free(ProcTitlePrefix);
+			sm_free(ProcTitlePrefix);
 		ProcTitlePrefix = newstr(val);
 		break;
 
@@ -2818,13 +2818,13 @@
 		}
 #endif /* _FFR_ALLOW_SASLINFO */
 		if (SASLInfo != NULL)
-			free(SASLInfo);
+			sm_free(SASLInfo);
 		SASLInfo = newstr(val);
 		break;
 
 	  case O_SASLMECH:
 		if (AuthMechanisms != NULL)
-			free(AuthMechanisms);
+			sm_free(AuthMechanisms);
 		if (*val != '\0')
 			AuthMechanisms = newstr(val);
 		else
@@ -2832,7 +2832,7 @@
 		break;
 
 	  case O_SASLOPTS:
-		while (*val != '\0')
+		while (val != NULL && *val != '\0')
 		{
 			switch(*val)
 			{
@@ -2868,6 +2868,9 @@
 				break;
 			}
 			++val;
+			val = strpbrk(val, ", \t");
+			if (val != NULL)
+				++val;
 		}
 		break;
 
@@ -2880,7 +2883,88 @@
 		break;
 #endif /* SASL */
 
+#if STARTTLS
+	  case O_SRVCERTFILE:
+		if (SrvCERTfile != NULL)
+			sm_free(SrvCERTfile);
+		SrvCERTfile = newstr(val);
+		break;
+
+	  case O_SRVKEYFILE:
+		if (Srvkeyfile != NULL)
+			sm_free(Srvkeyfile);
+		Srvkeyfile = newstr(val);
+		break;
+
+	  case O_CLTCERTFILE:
+		if (CltCERTfile != NULL)
+			sm_free(CltCERTfile);
+		CltCERTfile = newstr(val);
+		break;
+
+	  case O_CLTKEYFILE:
+		if (Cltkeyfile != NULL)
+			sm_free(Cltkeyfile);
+		Cltkeyfile = newstr(val);
+		break;
+
+	  case O_CACERTFILE:
+		if (CACERTfile != NULL)
+			sm_free(CACERTfile);
+		CACERTfile = newstr(val);
+		break;
+
+	  case O_CACERTPATH:
+		if (CACERTpath != NULL)
+			sm_free(CACERTpath);
+		CACERTpath = newstr(val);
+		break;
+
+	  case O_DHPARAMS:
+		if (DHParams != NULL)
+			sm_free(DHParams);
+		DHParams = newstr(val);
+		break;
+
+#  if _FFR_TLS_1
+	  case O_DHPARAMS5:
+		if (DHParams5 != NULL)
+			sm_free(DHParams5);
+		DHParams5 = newstr(val);
+		break;
 
+	  case O_CIPHERLIST:
+		if (CipherList != NULL)
+			sm_free(CipherList);
+		CipherList = newstr(val);
+		break;
+#  endif /* _FFR_TLS_1 */
+
+	  case O_RANDFILE:
+		if (RandFile != NULL)
+			sm_free(RandFile);
+		RandFile= newstr(val);
+		break;
+
+# else /* STARTTLS */
+	  case O_SRVCERTFILE:
+	  case O_SRVKEYFILE:
+	  case O_CLTCERTFILE:
+	  case O_CLTKEYFILE:
+	  case O_CACERTFILE:
+	  case O_CACERTPATH:
+	  case O_DHPARAMS:
+#  if _FFR_TLS_1
+	  case O_DHPARAMS5:
+	  case O_CIPHERLIST:
+#  endif /* _FFR_TLS_1 */
+	  case O_RANDFILE:
+		printf("Warning: Option: %s requires TLS support\n",
+			o->o_name == NULL ? "<unknown>" : o->o_name);
+		break;
+
+# endif /* STARTTLS */
+
 	  case O_CLIENTPORT:
 #if DAEMON
 		setclientoptions(val);
@@ -2971,7 +3055,7 @@
 
 		str++;
 		mid = macid(str, NULL);
-		if (mid == '\0')
+		if (mid == 0)
 			return;
 
 		if (tTd(37, 8))
@@ -2985,7 +3069,7 @@
 			dprintf("setclass(%s, %s)\n", macname(class), str);
 
 		s = stab(str, ST_CLASS, ST_ENTER);
-		setbitn(((unsigned int)class) & 0xff, s->s_class);
+		setbitn(bitidx(class), s->s_class);
 	}
 }
 /*
@@ -3183,12 +3267,12 @@
 		{
 			s->s_ruleset = ruleset;
 		}
-		if (stabmode == ST_ENTER)
+		if (stabmode == ST_ENTER && ruleset >= 0)
 		{
 			char *h = NULL;
 
 			if (RuleSetNames[ruleset] != NULL)
-				free(RuleSetNames[ruleset]);
+				sm_free(RuleSetNames[ruleset]);
 			if (delim != '\0' && (h = strchr(q, delim)) != NULL)
 				*h = '\0';
 			RuleSetNames[ruleset] = newstr(q);
@@ -3308,7 +3392,11 @@
 	}
 
 	if (to->to_name == NULL)
+	{
+		errno = 0; /* avoid bogus error text */
 		syserr("settimeout: invalid timeout %s", name);
+		return;
+	}
 
 	/*
 	**  See if this option is preset for us.
Index: gnu/usr.sbin/sendmail/sendmail/recipient.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/recipient.c,v
retrieving revision 1.1.1.1
retrieving revision 1.4
diff -u -r1.1.1.1 -r1.4
--- gnu/usr.sbin/sendmail/sendmail/recipient.c	2000/04/02 19:05:47	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/recipient.c	2001/05/29 01:31:16	1.4
@@ -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
@@ -12,11 +12,12 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: recipient.c,v 8.231 2000/01/05 01:40:53 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: recipient.c,v 8.231.14.11 2001/05/03 17:24:14 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
 
+
 static void	includetimeout __P((void));
 static ADDRESS	*self_reference __P((ADDRESS *));
 
@@ -184,7 +185,7 @@
 
 	e->e_to = oldto;
 	if (bufp != buf)
-		free(bufp);
+		sm_free(bufp);
 #if _FFR_ADDR_TYPE
 	define(macid("{addr_type}", NULL), NULL, e);
 #endif /* _FFR_ADDR_TYPE */
@@ -292,7 +293,7 @@
 
 	e->e_to = oldto;
 	if (bufp != buf)
-		free(bufp);
+		sm_free(bufp);
 #if _FFR_ADDR_TYPE
 	define(macid("{addr_type}", NULL), NULL, e);
 #endif /* _FFR_ADDR_TYPE */
@@ -509,8 +510,18 @@
 					q->q_state = QS_DUPLICATE;
 				q->q_flags |= a->q_flags;
 			}
-			else if (bitset(QSELFREF, q->q_flags))
+			else if (bitset(QSELFREF, q->q_flags)
+#if _FFR_MILTER
+				 || q->q_state == QS_REMOVED
+#endif /* _FFR_MILTER */
+				 )
 			{
+#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;
 			}
@@ -677,11 +688,13 @@
 		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);
+			{
+				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
 		{
@@ -705,7 +718,9 @@
 				(void) strlcpy(buf, pw->pw_name, buflen);
 				goto trylocaluser;
 			}
-			if (strcmp(pw->pw_dir, "/") == 0)
+			if (*pw->pw_dir == '\0')
+				a->q_home = NULL;
+			else if (strcmp(pw->pw_dir, "/") == 0)
 				a->q_home = "";
 			else
 				a->q_home = newstr(pw->pw_dir);
@@ -765,7 +780,7 @@
   done:
 	a->q_flags |= QTHISPASS;
 	if (buf != buf0)
-		free(buf);
+		sm_free(buf);
 
 	/*
 	**  If we are at the top level, check to see if this has
@@ -923,7 +938,7 @@
 # endif /* 0 */
 
 		buildfname(pw->pw_gecos, pw->pw_name, buf, sizeof buf);
-		if (strchr(buf, ' ') != NULL && !strcasecmp(buf, name))
+		if (strchr(buf, ' ') != NULL && strcasecmp(buf, name) == 0)
 		{
 			if (tTd(29, 4))
 				dprintf("fuzzy matches %s\n", pw->pw_name);
@@ -975,9 +990,9 @@
 	ADDRESS *ctladdr;
 	long flags;
 {
-	uid_t euid;
-	gid_t egid;
-	char *user;
+	uid_t euid = 0;
+	gid_t egid = 0;
+	char *user = NULL;
 
 	if (tTd(44, 5))
 		dprintf("writable(%s, 0x%lx)\n", filename, flags);
@@ -1093,8 +1108,10 @@
 	int mode;
 	volatile bool maxreached = FALSE;
 	register ADDRESS *ca;
-	volatile uid_t saveduid, uid;
-	volatile gid_t savedgid, gid;
+	volatile uid_t saveduid;
+	volatile gid_t savedgid;
+	volatile uid_t uid;
+	volatile gid_t gid;
 	char *volatile user;
 	int rval = 0;
 	volatile long sfflags = SFF_REGONLY;
@@ -1119,7 +1136,24 @@
 		dprintf("include: old uid = %d/%d\n",
 			(int) getuid(), (int) geteuid());
 
+#if _FFR_UNSAFE_WRITABLE_INCLUDE
 	if (forwarding)
+	{
+		if (!bitnset(DBS_GROUPWRITABLEFORWARDFILE, DontBlameSendmail))
+			sfflags |= SFF_NOGWFILES;
+		if (!bitnset(DBS_WORLDWRITABLEFORWARDFILE, DontBlameSendmail))
+			sfflags |= SFF_NOWWFILES;
+	}
+	else
+	{
+		if (!bitnset(DBS_GROUPWRITABLEINCLUDEFILE, DontBlameSendmail))
+			sfflags |= SFF_NOGWFILES;
+		if (!bitnset(DBS_WORLDWRITABLEINCLUDEFILE, DontBlameSendmail))
+			sfflags |= SFF_NOWWFILES;
+	}
+#endif /* _FFR_UNSAFE_WRITABLE_INCLUDE */
+
+	if (forwarding)
 		sfflags |= SFF_MUSTOWN|SFF_ROOTOK|SFF_NOWLINK;
 
 	/*
@@ -1158,8 +1192,12 @@
 		if (!DontInitGroups)
 		{
 			if (initgroups(user, gid) == -1)
+			{
+				rval = EAGAIN;
 				syserr("include: initgroups(%s, %d) failed",
 					user, gid);
+				goto resetuid;
+			}
 		}
 		else
 		{
@@ -1167,22 +1205,38 @@
 
 			gidset[0] = gid;
 			if (setgroups(1, gidset) == -1)
+			{
+				rval = EAGAIN;
 				syserr("include: setgroups() failed");
+				goto resetuid;
+			}
 		}
 
 		if (gid != 0 && setgid(gid) < -1)
+		{
+			rval = EAGAIN;
 			syserr("setgid(%d) failure", gid);
+			goto resetuid;
+		}
 		if (uid != 0)
 		{
 # if MAILER_SETUID_METHOD == USE_SETEUID
 			if (seteuid(uid) < 0)
+			{
+				rval = EAGAIN;
 				syserr("seteuid(%d) failure (real=%d, eff=%d)",
 					uid, getuid(), geteuid());
+				goto resetuid;
+			}
 # endif /* MAILER_SETUID_METHOD == USE_SETEUID */
 # if MAILER_SETUID_METHOD == USE_SETREUID
 			if (setreuid(0, uid) < 0)
+			{
+				rval = EAGAIN;
 				syserr("setreuid(0, %d) failure (real=%d, eff=%d)",
 					uid, getuid(), geteuid());
+				goto resetuid;
+			}
 # endif /* MAILER_SETUID_METHOD == USE_SETREUID */
 		}
 	}
@@ -1211,6 +1265,7 @@
 	else
 		ev = NULL;
 
+
 	/* check for writable parent directory */
 	p = strrchr(fname, '/');
 	if (p != NULL)
@@ -1309,18 +1364,20 @@
 		{
 # if USESETEUID
 			if (seteuid(0) < 0)
-				syserr("seteuid(0) failure (real=%d, eff=%d)",
+				syserr("!seteuid(0) failure (real=%d, eff=%d)",
 					getuid(), geteuid());
 # else /* USESETEUID */
 			if (setreuid(-1, 0) < 0)
-				syserr("setreuid(-1, 0) failure (real=%d, eff=%d)",
+				syserr("!setreuid(-1, 0) failure (real=%d, eff=%d)",
 					getuid(), geteuid());
 			if (setreuid(RealUid, 0) < 0)
-				syserr("setreuid(%d, 0) failure (real=%d, eff=%d)",
+				syserr("!setreuid(%d, 0) failure (real=%d, eff=%d)",
 					RealUid, getuid(), geteuid());
 # endif /* USESETEUID */
 		}
-		(void) setgid(savedgid);
+		if (setgid(savedgid) < 0)
+			syserr("!setgid(%d) failure (real=%d eff=%d)",
+			       savedgid, getgid(), getegid());
 	}
 #endif /* HASSETREUID || USESETEUID */
 
@@ -1459,7 +1516,11 @@
 			    isascii(p[-1]) && isspace(p[-1]) &&
 			    (p[3] == '\0' || (isascii(p[3]) && isspace(p[3]))))
 			{
-				p[-1] = '\0';
+				--p;
+				while (p > buf && isascii(p[-1]) &&
+				       isspace(p[-1]))
+					--p;
+				p[0] = '\0';
 				break;
 			}
 		}
@@ -1512,6 +1573,13 @@
 static void
 includetimeout()
 {
+	/*
+	**  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(CtxIncludeTimeout, 1);
 }
 /*
Index: gnu/usr.sbin/sendmail/sendmail/savemail.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/savemail.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/sendmail/savemail.c	2000/04/07 19:20:43	1.2
+++ gnu/usr.sbin/sendmail/sendmail/savemail.c	2001/05/29 01:31:16	1.5
@@ -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
@@ -12,11 +12,12 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: savemail.c,v 8.212 2000/03/13 22:56:51 ca Exp $";
+static char id[] = "@(#)$Sendmail: savemail.c,v 8.212.4.13 2001/05/03 17:24:15 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
 
+
 static void	errbody __P((MCI *, ENVELOPE *, char *));
 static bool	pruneroute __P((char *));
 
@@ -331,7 +332,8 @@
 			{
 				if (e->e_from.q_home != NULL)
 					p = e->e_from.q_home;
-				else if ((pw = sm_getpwnam(e->e_from.q_user)) != NULL)
+				else if ((pw = sm_getpwnam(e->e_from.q_user)) != NULL &&
+					 *pw->pw_dir != '\0')
 					p = pw->pw_dir;
 			}
 			if (p == NULL || e->e_dfp == NULL)
@@ -444,6 +446,7 @@
 		  case ESM_PANIC:
 			/* leave the locked queue & transcript files around */
 			loseqfile(e, "savemail panic");
+			errno = 0;
 			syserr("!554 savemail: cannot save rejected email anywhere");
 		}
 	}
@@ -528,7 +531,21 @@
 	define(macid("{auth_type}", NULL), "", ee);
 	define(macid("{auth_authen}", NULL), "", ee);
 	define(macid("{auth_author}", NULL), "", ee);
+	define(macid("{auth_ssf}", NULL), "", ee);
 #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);
+# if _FFR_TLS_1
+	define(macid("{alg_bits}", NULL), "", ee);
+	define(macid("{cn_issuer}", NULL), "", ee);
+	define(macid("{cn_subject}", NULL), "", ee);
+# endif /* _FFR_TLS_1 */
+#endif /* STARTTLS */
 
 	ee->e_puthdr = putheader;
 	ee->e_putbody = errbody;
@@ -574,7 +591,7 @@
 			ee->e_nrcpts++;
 
 		if (q->q_alias == NULL)
-			addheader("To", q->q_paddr, &ee->e_header);
+			addheader("To", q->q_paddr, 0, &ee->e_header);
 	}
 
 	if (LogLevel > 5)
@@ -594,10 +611,10 @@
 
 	if (SendMIMEErrors)
 	{
-		addheader("MIME-Version", "1.0", &ee->e_header);
+		addheader("MIME-Version", "1.0", 0, &ee->e_header);
 
 		(void) snprintf(buf, sizeof buf, "%s.%ld/%.100s",
-				ee->e_id, curtime(), MyHostName);
+				ee->e_id, (long) curtime(), MyHostName);
 		ee->e_msgboundary = newstr(buf);
 		(void) snprintf(buf, sizeof buf,
 #if DSN
@@ -606,7 +623,7 @@
 				"multipart/mixed; boundary=\"%s\"",
 #endif /* DSN */
 				ee->e_msgboundary);
-		addheader("Content-Type", buf, &ee->e_header);
+		addheader("Content-Type", buf, 0, &ee->e_header);
 
 		p = hvalue("Content-Transfer-Encoding", e->e_header);
 		if (p != NULL && strcasecmp(p, "binary") != 0)
@@ -615,39 +632,39 @@
 			p = "8bit";
 		if (p != NULL)
 			addheader("Content-Transfer-Encoding",
-				  p, &ee->e_header);
+				  p, 0, &ee->e_header);
 	}
 	if (strncmp(msg, "Warning:", 8) == 0)
 	{
-		addheader("Subject", msg, &ee->e_header);
+		addheader("Subject", msg, 0, &ee->e_header);
 		p = "warning-timeout";
 	}
 	else if (strncmp(msg, "Postmaster warning:", 19) == 0)
 	{
-		addheader("Subject", msg, &ee->e_header);
+		addheader("Subject", msg, 0, &ee->e_header);
 		p = "postmaster-warning";
 	}
 	else if (strcmp(msg, "Return receipt") == 0)
 	{
-		addheader("Subject", msg, &ee->e_header);
+		addheader("Subject", msg, 0, &ee->e_header);
 		p = "return-receipt";
 	}
 	else if (bitset(RTSF_PM_BOUNCE, flags))
 	{
 		snprintf(buf, sizeof buf,
 			 "Postmaster notify: see transcript for details");
-		addheader("Subject", buf, &ee->e_header);
+		addheader("Subject", buf, 0, &ee->e_header);
 		p = "postmaster-notification";
 	}
 	else
 	{
 		snprintf(buf, sizeof buf,
 			 "Returned mail: see transcript for details");
-		addheader("Subject", buf, &ee->e_header);
+		addheader("Subject", buf, 0, &ee->e_header);
 		p = "failure";
 	}
 	(void) snprintf(buf, sizeof buf, "auto-generated (%s)", p);
-	addheader("Auto-Submitted", buf, &ee->e_header);
+	addheader("Auto-Submitted", buf, 0, &ee->e_header);
 
 	/* fake up an address header for the from person */
 	expand("\201n", buf, sizeof buf, e);
@@ -976,6 +993,8 @@
 
 	if (e->e_msgboundary != NULL)
 	{
+		time_t now = curtime();
+
 		putline("", mci);
 		(void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary);
 		putline(buf, mci);
@@ -996,7 +1015,8 @@
 		}
 
 		/* Reporting-MTA: is us (required) */
-		(void) snprintf(buf, sizeof buf, "Reporting-MTA: dns; %.800s", MyHostName);
+		(void) snprintf(buf, sizeof buf, "Reporting-MTA: dns; %.800s",
+				MyHostName);
 		putline(buf, mci);
 
 		/* DSN-Gateway: not relevant since we are not translating */
@@ -1029,7 +1049,13 @@
 			char *action;
 
 			if (QS_IS_BADADDR(q->q_state))
+			{
+				/* RFC 1891, 6.2.6 (b) */
+				if (bitset(QHASNOTIFY, q->q_flags) &&
+				    !bitset(QPINGONFAILURE, q->q_flags))
+					continue;
 				action = "failed";
+			}
 			else if (!bitset(QPRIMARY, q->q_flags))
 				continue;
 			else if (bitset(QDELIVERED, q->q_flags))
@@ -1179,7 +1205,7 @@
 
 			/* Last-Attempt-Date: -- fine granularity */
 			if (q->q_statdate == (time_t) 0L)
-				q->q_statdate = curtime();
+				q->q_statdate = now;
 			(void) snprintf(buf, sizeof buf,
 					"Last-Attempt-Date: %s",
 					arpadate(ctime(&q->q_statdate)));
@@ -1387,7 +1413,7 @@
 	if (l > bplen)
 	{
 		if (bp != NULL)
-			free(bp);
+			sm_free(bp);
 		bp = xalloc(l);
 		bplen = l;
 	}
@@ -1440,7 +1466,7 @@
 	if (l > bplen)
 	{
 		if (bp != NULL)
-			free(bp);
+			sm_free(bp);
 		bp = xalloc(l);
 		bplen = l;
 	}
Index: gnu/usr.sbin/sendmail/sendmail/sendmail.8
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/sendmail.8,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- gnu/usr.sbin/sendmail/sendmail/sendmail.8	2000/06/11 21:03:39	1.9
+++ gnu/usr.sbin/sendmail/sendmail/sendmail.8	2001/01/15 21:09:10	1.10
@@ -9,9 +9,9 @@
 .\" the sendmail distribution.
 .\"
 .\"
-.\"     $Sendmail: sendmail.8,v 8.36 2000/02/01 05:49:57 gshapiro Exp $
+.\"     $Sendmail: sendmail.8,v 8.36.8.3 2000/12/14 23:08:15 gshapiro Exp $
 .\"
-.Dd January 2, 2000
+.Dd December 14, 2000
 .Dt SENDMAIL 8
 .Os
 .Sh NAME
@@ -156,6 +156,11 @@
 Otherwise,
 an X-Authentication-Warning header
 will be added to the message.
+.It Fl G
+Relay (gateway) submission of a message, e.g., when
+.Nm rmail
+calls
+.Nm sendmail .
 .It Fl h Ns Ar N
 Set the hop count to
 .Ar N .
Index: gnu/usr.sbin/sendmail/sendmail/sendmail.h
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/sendmail.h,v
retrieving revision 1.2
retrieving revision 1.6
diff -u -r1.2 -r1.6
--- gnu/usr.sbin/sendmail/sendmail/sendmail.h	2000/04/07 19:20:43	1.2
+++ gnu/usr.sbin/sendmail/sendmail/sendmail.h	2001/05/29 01:31:16	1.6
@@ -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
@@ -20,7 +20,7 @@
 #ifdef _DEFINE
 # define EXTERN
 # ifndef lint
-static char SmailId[] =	"@(#)$Sendmail: sendmail.h,v 8.517 2000/03/21 04:57:53 ca Exp $";
+static char SmailId[] =	"@(#)$Sendmail: sendmail.h,v 8.517.4.64 2001/05/23 17:49:13 ca Exp $";
 # endif /* ! lint */
 #else /* _DEFINE */
 # define EXTERN extern
@@ -28,16 +28,26 @@
 
 
 #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 <ctype.h>
 #include <setjmp.h>
 #include <string.h>
 #include <time.h>
-#ifdef EX_OK
-# undef EX_OK			/* for SVr4.2 SMP */
-#endif /* EX_OK */
+# ifdef EX_OK
+#  undef EX_OK			/* for SVr4.2 SMP */
+# endif /* EX_OK */
 #include <sysexits.h>
 
 #include "sendmail/sendmail.h"
@@ -48,46 +58,57 @@
 # include <syslog.h>
 #endif /* LOG */
 
-#if NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25
-# include <sys/socket.h>
-#endif /* NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25 */
-#if NETUNIX
-# include <sys/un.h>
-#endif /* NETUNIX */
-#if NETINET || NETINET6
-# include <netinet/in.h>
-#endif /* NETINET || NETINET6 */
-#if NETINET6
+
+
+# if NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25
+#  include <sys/socket.h>
+# endif /* NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25 */
+# if NETUNIX
+#  include <sys/un.h>
+# endif /* NETUNIX */
+# if NETINET || NETINET6
+#  include <netinet/in.h>
+# endif /* NETINET || NETINET6 */
+# if NETINET6
 /*
 **  There is no standard yet for IPv6 includes.
 **  Specify OS specific implementation in conf.h
 */
-#endif /* NETINET6 */
-#if NETISO
-# include <netiso/iso.h>
-#endif /* NETISO */
-#if NETNS
-# include <netns/ns.h>
-#endif /* NETNS */
-#if NETX25
-# include <netccitt/x25.h>
-#endif /* NETX25 */
-
-#if NAMED_BIND
-# include <arpa/nameser.h>
-# ifdef NOERROR
-#  undef NOERROR		/* avoid <sys/streams.h> conflict */
-# endif /* NOERROR */
-# include <resolv.h>
-#endif /* NAMED_BIND */
-
-#ifdef HESIOD
-# include <hesiod.h>
-# if !defined(HES_ER_OK) || defined(HESIOD_INTERFACES)
-#  define HESIOD_INIT		/* support for the new interface */
-# endif /* !defined(HES_ER_OK) || defined(HESIOD_INTERFACES) */
-#endif /* HESIOD */
+# endif /* NETINET6 */
+# if NETISO
+#  include <netiso/iso.h>
+# endif /* NETISO */
+# if NETNS
+#  include <netns/ns.h>
+# endif /* NETNS */
+# if NETX25
+#  include <netccitt/x25.h>
+# endif /* NETX25 */
 
+# if NAMED_BIND
+#  include <arpa/nameser.h>
+#  ifdef NOERROR
+#   undef NOERROR		/* avoid <sys/streams.h> conflict */
+#  endif /* NOERROR */
+#  include <resolv.h>
+# endif /* NAMED_BIND */
+
+# ifdef HESIOD
+#  include <hesiod.h>
+#  if !defined(HES_ER_OK) || defined(HESIOD_INTERFACES)
+#   define HESIOD_INIT		/* support for the new interface */
+#  endif /* !defined(HES_ER_OK) || defined(HESIOD_INTERFACES) */
+# 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>
+#endif /* STARTTLS */
 
 #if SASL  /* include the sasl include files if we have them */
 # include <sasl.h>
@@ -131,6 +152,9 @@
 #ifndef INT32SZ
 # define INT32SZ	4		/* size of a 32 bit integer in bytes */
 #endif /* ! INT32SZ */
+#ifndef INADDR_LOOPBACK
+# define INADDR_LOOPBACK	0x7f000001	/* loopback address */
+#endif /* ! INADDR_LOOPBACK */
 
 /*
 **  Error return from inet_addr(3), in case not defined in /usr/include.
@@ -205,14 +229,14 @@
 #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		/* expanded */
-#define QS_SENDER	7		/* message sender (MeToo) */
-#define QS_CLONED	8		/* addr cloned to a split envelope */
-#define QS_DISCARDED	9		/* recipient discarded (EF_DISCARD) */
-#define QS_REPLACED	10		/* maplocaluser()/UserDB replaced */
-#define QS_REMOVED	11		/* removed (removefromlist()) */
-#define QS_DUPLICATE	12		/* duplicate suppressed */
-#define QS_INCLUDED	13		/* :include: delivery */
+#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 */
 
 /* address state testing primitives */
 #define QS_IS_OK(s)		((s) == QS_OK)
@@ -221,6 +245,7 @@
 #define QS_IS_QUEUEUP(s)	((s) == QS_QUEUEUP)
 #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_VERIFIED)
@@ -298,6 +323,9 @@
 	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 */
 };
 
 /* bits for m_flags */
@@ -379,8 +407,13 @@
 	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 */
 	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 */
@@ -397,6 +430,9 @@
 	char		*mci_saslcap;	/* SASL list of mechanisms */
 	sasl_conn_t	*mci_conn;	/* SASL connection */
 #endif /* SASL */
+#if STARTTLS
+	SSL		*mci_ssl;	/* SSL connection */
+#endif /* STARTTLS */
 };
 
 
@@ -419,7 +455,15 @@
 #define MCIF_AUTH	0x00008000	/* AUTH= supported */
 #define MCIF_AUTHACT	0x00010000	/* SASL (AUTH) active */
 #define MCIF_ENHSTAT	0x00020000	/* ENHANCEDSTATUSCODES supported */
+#if STARTTLS
+#define MCIF_TLS	0x00100000	/* STARTTLS supported */
+#define MCIF_TLSACT	0x00200000	/* STARTTLS active */
+#define MCIF_EXTENS	(MCIF_EXPN | MCIF_SIZE | MCIF_8BITMIME | MCIF_DSN | MCIF_8BITOK | MCIF_AUTH | MCIF_ENHSTAT | MCIF_TLS)
+#else /* STARTTLS */
 #define MCIF_EXTENS	(MCIF_EXPN | MCIF_SIZE | MCIF_8BITMIME | MCIF_DSN | MCIF_8BITOK | MCIF_AUTH | MCIF_ENHSTAT)
+#endif /* STARTTLS */
+#define MCIF_ONLY_EHLO	0x10000000	/* use only EHLO in smtpinit */
+
 
 /* states */
 #define MCIS_CLOSED	0		/* no traffic on this connection */
@@ -497,18 +541,17 @@
 #define H_ENCODABLE	0x00008000	/* field can be RFC 1522 encoded */
 #define H_STRIPCOMM	0x00010000	/* header check: strip comments */
 #define H_BINDLATE	0x00020000	/* only expand macros at deliver */
+#define H_USER		0x00040000	/* header came from the user/SMTP */
 
 /* bits for chompheader() */
 #define CHHDR_DEF	0x0001	/* default header */
 #define CHHDR_CHECK	0x0002	/* call ruleset for header */
 #define CHHDR_USER	0x0004	/* header from user */
-#if _FFR_MILTER
-# define CHHDR_MILTER	0x0008	/* call milter filter for header */
-#endif /* _FFR_MILTER */
+#define CHHDR_QUEUE	0x0008	/* header from qf file */
 
 /* functions */
-extern void	addheader __P((char *, char *, HDR **));
-extern u_long	chompheader __P((char *, int *, HDR **, ENVELOPE *));
+extern void	addheader __P((char *, char *, int, HDR **));
+extern u_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));
@@ -552,6 +595,12 @@
 	char		**e_fromdomain;	/* the domain part of the sender */
 	ADDRESS		*e_sendqueue;	/* list of message recipients */
 	ADDRESS		*e_errorqueue;	/* the queue for error responses */
+
+	/*
+	**  Overflow detection is based on < 0, so don't change this
+	**  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 */
 	int		e_nrcpts;	/* number of recipients */
@@ -583,7 +632,8 @@
 	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[256];	/* macro definitions */
+	char		*e_macro[MAXMACROID + 1]; /* macro definitions */
+	char		*e_if_macros[2]; /* HACK: incoming interface info */
 	char		*e_auth_param;
 	TIMERS		e_timers;	/* per job timers */
 #if _FFR_QUEUEDELAY
@@ -618,6 +668,10 @@
 #define EF_IS_MIME	0x0400000L	/* really is a MIME message */
 #define EF_DONT_MIME	0x0800000L	/* never MIME this message */
 #define EF_DISCARD	0x1000000L	/* discard the message */
+#define EF_TOOBIG	0x2000000L	/* message is too big */
+
+/* values for e_if_macros */
+#define EIF_ADDR	0		/* ${if_addr} */
 
 /* functions */
 extern void	clearenvelope __P((ENVELOPE *, bool));
@@ -727,7 +781,7 @@
 extern int	macid __P((char *, char **));
 extern char	*macname __P((int));
 extern char	*macvalue __P((int, ENVELOPE *));
-extern int	rscheck __P((char *, char *, char *, ENVELOPE *, bool, bool, int));
+extern int	rscheck __P((char *, char *, char *, ENVELOPE *, bool, bool, int, char *));
 extern void	setclass __P((int, char *));
 extern int	strtorwset __P((char *, char **, int));
 extern void	translate_dollars __P((char *));
@@ -803,6 +857,7 @@
 	short		map_return[MAXMAPACTIONS]; /* return bitmaps for stacked maps */
 };
 
+
 /* bit values for map_mflags */
 #define MF_VALID	0x00000001	/* this entry is valid */
 #define MF_INCLNULL	0x00000002	/* include null byte in key */
@@ -826,6 +881,7 @@
 #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 DYNOPENMAP(map) if (!bitset(MF_OPEN, (map)->map_mflags)) \
 	{	\
@@ -914,6 +970,9 @@
 	/* 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;
@@ -995,49 +1054,7 @@
 extern void	proc_list_probe __P((void));
 extern void	proc_list_set __P((pid_t, char *));
 
-#if _FFR_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_NUM_TO	3		/* Total number of timeouts */
-
-struct milter
-{
-	char		*mf_name;	/* filter name */
-	BITMAP256	mf_flags;	/* MTA flags */
-	u_long		mf_fflags;	/* filter flags */
-	char		*mf_conn;	/* connection info */
-	int		mf_sock;	/* connected socket */
-	char		mf_state;	/* state of filter */
-	time_t		mf_timeout[SMFTO_NUM_TO]; /* timeouts */
-};
-
-/* MTA flags */
-# define SMF_REJECT		'R'	/* Reject connection on filter fail */
-# define SMF_TEMPFAIL		'T'	/* tempfail connection on failure */
-
-/* states */
-# define SMFS_CLOSED		'C'	/* closed for all further actions */
-# define SMFS_OPEN		'O'	/* connected to remote milter filter */
-# define SMFS_INMSG		'M'	/* currently servicing a message */
-# define SMFS_DONE		'D'	/* done with current message */
-# 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 */
-/*
 **  Symbol table definitions
 */
 
@@ -1063,7 +1080,7 @@
 		struct hdrinfo	sv_header;	/* header metainfo */
 		char		*sv_service[MAXMAPSTACK]; /* service switch */
 #ifdef LDAPMAP
-		LDAP		*sv_ldap;	/* LDAP connection */
+		MAP		*sv_lmap;	/* Maps for LDAP connection */
 #endif /* LDAPMAP */
 #if _FFR_MILTER
 		struct milter	*sv_milter;	/* milter filter name */
@@ -1088,7 +1105,7 @@
 #define ST_SERVICE	11	/* service switch entry */
 #define ST_HEADER	12	/* special header flags */
 #ifdef LDAPMAP
-# define ST_LDAP	13	/* LDAP connection */
+# define ST_LMAP	13	/* List head of maps for LDAP connection */
 #endif /* LDAPMAP */
 #if _FFR_MILTER
 # define ST_MILTER	14	/* milter filter */
@@ -1109,7 +1126,7 @@
 #define s_service	s_value.sv_service
 #define s_header	s_value.sv_header
 #ifdef LDAPMAP
-# define s_ldap		s_value.sv_ldap
+# define s_lmap		s_value.sv_lmap
 #endif /* LDAPMAP */
 #if _FFR_MILTER
 # define s_milter	s_value.sv_milter
@@ -1138,7 +1155,7 @@
 	void		(*ev_func)__P((int));
 					/* function to call */
 	int		ev_arg;		/* argument to ev_func */
-	int		ev_pid;		/* pid that set this event */
+	pid_t		ev_pid;		/* pid that set this event */
 	struct event	*ev_link;	/* link to next item */
 };
 
@@ -1148,6 +1165,7 @@
 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
@@ -1332,7 +1350,52 @@
 
 #endif /* NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25 */
 
+#if _FFR_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_NUM_TO	3		/* 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 */
+	char		*mf_conn;	/* connection info */
+	int		mf_sock;	/* connected socket */
+	char		mf_state;	/* state of filter */
+	time_t		mf_timeout[SMFTO_NUM_TO]; /* timeouts */
+};
+
+/* MTA flags */
+# define SMF_REJECT		'R'	/* Reject connection on filter fail */
+# define SMF_TEMPFAIL		'T'	/* tempfail connection on failure */
+
+/* states */
+# define SMFS_CLOSED		'C'	/* closed for all further actions */
+# define SMFS_OPEN		'O'	/* connected to remote milter filter */
+# define SMFS_INMSG		'M'	/* currently servicing a message */
+# define SMFS_DONE		'D'	/* done with current message */
+# define SMFS_CLOSABLE		'Q'	/* done with current connection */
+# 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 */
+
 /*
 **  Vendor codes
 **
@@ -1376,17 +1439,22 @@
 */
 
 /* d_flags, see daemon.c */
-/* generic rule: lower case: required, upper case: No */
+/* 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) */
 #define D_UNQUALOK	'u'	/* unqualified address is ok (cf) */
 #define D_NOCANON	'C'	/* no canonification (cf) */
 #define D_NOETRN	'E'	/* no ETRN (MSA) */
 #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 */
@@ -1420,6 +1488,49 @@
 # define MAXOUTLEN 1024			/* length of output buffer */
 #endif /* SASL */
 
+#if STARTTLS
+/*
+**  TLS
+*/
+
+/* what to do in the TLS initialization */
+#define TLS_I_NONE	0x00000000	/* no requirements... */
+#define TLS_I_CERT_EX	0x00000001	/* CERT must exist */
+#define TLS_I_CERT_UNR	0x00000002	/* CERT must be g/o unreadable */
+#define TLS_I_KEY_EX	0x00000004	/* KEY must exist */
+#define TLS_I_KEY_UNR	0x00000008	/* KEY must be g/o unreadable */
+#define TLS_I_CERTP_EX	0x00000010	/* CA CERT PATH must exist */
+#define TLS_I_CERTP_UNR	0x00000020	/* CA CERT PATH must be g/o unreadable */
+#define TLS_I_CERTF_EX	0x00000040	/* CA CERT FILE must exist */
+#define TLS_I_CERTF_UNR	0x00000080	/* CA CERT FILE must be g/o unreadable */
+#define TLS_I_RSA_TMP	0x00000100	/* RSA TMP must be generated */
+#define TLS_I_USE_KEY	0x00000200	/* private key must usable */
+#define TLS_I_USE_CERT	0x00000400	/* certificate must be usable */
+#define TLS_I_VRFY_PATH	0x00000800	/* load verify path must succeed */
+#define TLS_I_VRFY_LOC	0x00001000	/* load verify default must succeed */
+#define TLS_I_CACHE	0x00002000	/* require cache */
+#define TLS_I_TRY_DH	0x00004000	/* try DH certificate */
+#define TLS_I_REQ_DH	0x00008000	/* require DH certificate */
+#define TLS_I_DHPAR_EX	0x00010000	/* require DH parameters */
+#define TLS_I_DHPAR_UNR	0x00020000	/* DH param. must be g/o unreadable */
+#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 */
+
+/* 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)
+
+/* client requirements */
+#define TLS_I_CLT	(TLS_I_KEY_UNR)
+
+#define TLS_AUTH_OK	0
+#define TLS_AUTH_NO	1
+#define TLS_AUTH_FAIL	(-1)
+#endif /* STARTTLS */
 
 
 /*
@@ -1522,6 +1633,51 @@
 /* variables */
 extern u_char	tTdvect[100];	/* trace vector */
 /*
+**  Critical signal sections
+*/
+
+#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)						\
+{									\
+	if (InCriticalSection > 0 && (sig) != 0)			\
+	{								\
+		pend_signal((sig));					\
+		return SIGFUNC_RETURN;					\
+	}								\
+}
+
+/* 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 */
+
+/* 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));
+
+/*
 **  Miscellaneous information.
 */
 
@@ -1558,9 +1714,9 @@
 EXTERN bool	ChownAlwaysSafe;	/* treat chown(2) as safe */
 EXTERN bool	ColonOkInAddr;	/* single colon legal in address */
 EXTERN bool	ConfigFileRead;	/* configuration file has been read */
-EXTERN bool	DataProgress;	/* have we sent anything since last check */
+EXTERN bool	volatile DataProgress;	/* have we sent anything since last check */
 EXTERN bool	DisConnected;	/* running with OutChannel redirected to xf */
-EXTERN bool	DoQueueRun;	/* non-interrupt time queue run needed */
+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 */
@@ -1576,6 +1732,7 @@
 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 */
@@ -1588,9 +1745,13 @@
 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 */
+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	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 */
@@ -1600,7 +1761,7 @@
 EXTERN int	CheckpointInterval;	/* queue file checkpoint interval */
 EXTERN int	ConfigLevel;	/* config file level */
 EXTERN int	ConnRateThrottle;	/* throttle for SMTP connection rate */
-EXTERN int	CurChildren;	/* current number of daemonic children */
+EXTERN int	volatile CurChildren;	/* current number of daemonic children */
 EXTERN int	CurrentLA;	/* current load average */
 EXTERN int	DefaultNotify;	/* default DSN notification flags */
 EXTERN int	Errors;		/* set if errors (local to single pass) */
@@ -1617,6 +1778,8 @@
 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 */
@@ -1661,6 +1824,20 @@
 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 */
@@ -1689,9 +1866,11 @@
 #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 */
 EXTERN char	*RunAsUserName;	/* user to become for bulk of run */
 EXTERN char	*SafeFileEnv;	/* chroot location for file delivery */
 EXTERN char	*ServiceSwitchFile;	/* backup service switch */
+EXTERN char	*volatile ShutdownRequest;/* a sendmail shutdown has been requested */
 EXTERN char	*SmtpGreeting;	/* SMTP greeting message (old $e macro) */
 EXTERN char	*SmtpPhase;	/* current phase in SMTP processing */
 EXTERN char	SmtpError[MAXLINE];	/* save failure error messages */
@@ -1701,15 +1880,20 @@
 EXTERN char	*UnixFromLine;	/* UNIX From_ line (old $l macro) */
 EXTERN char	**ExternalEnviron;	/* input environment */
 					/* saved user 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 void	*HesiodContext;
 #endif /* HESIOD */
 EXTERN ENVELOPE	*CurEnv;	/* envelope currently being processed */
-EXTERN EVENT	*EventQueue;	/* head of event queue */
 EXTERN MAILER	*LocalMailer;	/* ptr to local mailer */
 EXTERN MAILER	*ProgMailer;	/* ptr to program mailer */
 EXTERN MAILER	*FileMailer;	/* ptr to *file* mailer */
@@ -1745,6 +1929,18 @@
 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 *));
@@ -1817,9 +2013,8 @@
 
 #if _FFR_MILTER
 /* milter functions */
-extern int	milter_open __P((struct milter *, bool, ENVELOPE *));
 extern void	milter_parse_list __P((char *, struct milter **, int));
-extern void	milter_parse_timeouts __P((char *, struct milter *));
+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 *));
@@ -1829,12 +2024,11 @@
 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_header __P((char *, char *, ENVELOPE *, char *));
-extern char	*milter_eoh __P((ENVELOPE *, char *));
-extern char	*milter_body __P((ENVELOPE *, char *));
+extern char	*milter_data __P((ENVELOPE *, char *));
 #endif /* _FFR_MILTER */
 
 extern char	*addquotes __P((char *));
+extern void	allsignals __P((bool));
 extern char	*arpadate __P((char *));
 extern bool	atobool __P((char *));
 extern int	atooct __P((char *));
@@ -1861,7 +2055,7 @@
 extern char	*denlstring __P((char *, bool, bool));
 extern void	disconnect __P((int, ENVELOPE *));
 extern bool	dns_getcanonname __P((char *, int, bool, int *));
-extern int	dofork __P((void));
+extern pid_t	dofork __P((void));
 extern int	drop_privileges __P((bool));
 extern int	dsntoexitstat __P((char *));
 extern void	dumpfd __P((int, bool, bool));
@@ -1875,6 +2069,11 @@
 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 */
 extern char	*get_column __P((char *, int, int, char *, int));
 extern char	*getauthinfo __P((int, bool *));
 extern char	*getcfname __P((void));
@@ -1889,7 +2088,6 @@
 extern void	initmacros __P((ENVELOPE *));
 extern void	initsetproctitle __P((int, char **, char **));
 extern void	init_vendor_macros __P((ENVELOPE *));
-extern SIGFUNC_DECL	intindebug __P((int));
 extern SIGFUNC_DECL	intsig __P((int));
 extern bool	isloopback __P((SOCKADDR sa));
 extern void	load_if_names __P((void));
@@ -1909,14 +2107,12 @@
 extern void	printopenfds __P((bool));
 extern void	printqueue __P((void));
 extern void	printrules __P((void));
-extern int	prog_open __P((char **, int *, ENVELOPE *));
+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 SIGFUNC_DECL	quiesce __P((int));
 extern void	readcf __P((char *, bool, ENVELOPE *));
 extern SIGFUNC_DECL	reapchild __P((int));
-extern bool	refuseconnections __P((char *, ENVELOPE *, int));
 extern int	releasesignal __P((int));
 extern void	resetlimits __P((void));
 extern bool	rfc822_string __P((char *));
@@ -1934,11 +2130,11 @@
 extern void	settime __P((ENVELOPE *));
 extern char	*sfgets __P((char *, int, FILE *, time_t, char *));
 extern char	*shortenstring __P((const char *, int));
-extern void	shorten_hostname __P((char []));
+extern char	*shorten_hostname __P((char []));
 extern bool	shorten_rfc822_string __P((char *, size_t));
-extern SIGFUNC_DECL	sigusr1 __P((int));
-extern SIGFUNC_DECL	sighup __P((int));
+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 *));
@@ -1946,13 +2142,13 @@
 extern struct passwd	*sm_getpwuid __P((UID_T));
 extern void	sm_setproctitle __P((bool, ENVELOPE *, const char *, ...));
 extern int	sm_strcasecmp __P((const char *, const char *));
+extern void	stop_sendmail __P((void));
 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));
 extern void	tTflag __P((char *));
 extern void	tTsetup __P((u_char *, int, char *));
-extern SIGFUNC_DECL	tick __P((int));
 extern char	*ttypath __P((void));
 extern void	unlockqueue __P((ENVELOPE *));
 #if !HASUNSETENV
@@ -1965,6 +2161,8 @@
 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));
 extern void	xputs __P((const char *));
 extern char	*xtextify __P((char *, char *));
 extern bool	xtextok __P((char *));
Index: gnu/usr.sbin/sendmail/sendmail/sfsasl.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/sfsasl.c,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -r1.2 -r1.4
--- gnu/usr.sbin/sendmail/sendmail/sfsasl.c	2000/04/07 19:20:44	1.2
+++ gnu/usr.sbin/sendmail/sendmail/sfsasl.c	2001/05/29 01:31:16	1.4
@@ -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
@@ -9,6 +9,388 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: sfsasl.c,v 8.17 2000/03/10 17:58:19 ca Exp $";
+static char id[] = "@(#)$Sendmail: sfsasl.c,v 8.17.4.14 2001/05/03 17:24:16 gshapiro Exp $";
 #endif /* ! lint */
 
+#if SFIO
+# include <sfio/stdio.h>
+#endif /* SFIO */
+
+#include <stdlib.h>
+#include <sendmail.h>
+
+#if SASL && SFIO
+/*
+**  SASL
+*/
+
+# include <sasl.h>
+# include "sfsasl.h"
+
+/* how to deallocate a buffer allocated by SASL */
+#  define SASL_DEALLOC(b)	sm_free(b)
+
+static ssize_t
+sasl_read(f, buf, size, disc)
+	Sfio_t *f;
+	Void_t *buf;
+	size_t size;
+	Sfdisc_t *disc;
+{
+	int len, result;
+	static char *outbuf = NULL;
+	static unsigned int outlen = 0;
+	static unsigned int offset = 0;
+	Sasldisc_t *sd = (Sasldisc_t *) disc;
+
+	/*
+	**  sasl_decode() may require more data than a single read() returns.
+	**  Hence we have to put a loop around the decoding.
+	**  This also requires that we may have to split up the returned
+	**  data since it might be larger than the allowed size.
+	**  Therefore we use a static pointer and return portions of it
+	**  if necessary.
+	*/
+
+	while (outbuf == NULL && outlen == 0)
+	{
+		len = sfrd(f, buf, size, disc);
+		if (len <= 0)
+			return len;
+		result = sasl_decode(sd->conn, buf, len, &outbuf, &outlen);
+		if (result != SASL_OK)
+		{
+			outbuf = NULL;
+			offset = 0;
+			outlen = 0;
+			return -1;
+		}
+	}
+
+	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;
+		}
+	}
+	else
+	{
+		/* be paranoid: outbuf == NULL but outlen != 0 */
+		syserr("!sasl_read failure: outbuf == NULL but outlen != 0");
+	}
+	return result;
+}
+
+static ssize_t
+sasl_write(f, buf, size, disc)
+	Sfio_t *f;
+	const Void_t *buf;
+	size_t size;
+	Sfdisc_t *disc;
+{
+	int result;
+	char *outbuf;
+	unsigned int outlen;
+	Sasldisc_t *sd = (Sasldisc_t *) disc;
+
+	result = sasl_encode(sd->conn, buf, size, &outbuf, &outlen);
+
+	if (result != SASL_OK)
+		return -1;
+
+	if (outbuf != NULL)
+	{
+		sfwr(f, outbuf, outlen, disc);
+		SASL_DEALLOC(outbuf);
+	}
+	return size;
+}
+
+int
+sfdcsasl(fin, fout, conn)
+	Sfio_t *fin;
+	Sfio_t *fout;
+	sasl_conn_t *conn;
+{
+	Sasldisc_t *saslin, *saslout;
+
+	if (conn == NULL)
+	{
+		/* no need to do anything */
+		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;
+
+	saslout->disc.readf = sasl_read;
+	saslout->disc.writef = sasl_write;
+	saslout->disc.seekf = NULL;
+	saslout->disc.exceptf = NULL;
+
+	saslin->conn = conn;
+	saslout->conn = conn;
+
+	if (sfdisc(fin, (Sfdisc_t *) saslin) != (Sfdisc_t *) saslin ||
+	    sfdisc(fout, (Sfdisc_t *) saslout) != (Sfdisc_t *) saslout)
+	{
+		sm_free(saslin);
+		sm_free(saslout);
+		return -1;
+	}
+	return 0;
+}
+#endif /* SASL && SFIO */
+
+#if STARTTLS && (SFIO || _FFR_TLS_TOREK)
+/*
+**  STARTTLS
+*/
+
+# include "sfsasl.h"
+#  include <openssl/err.h>
+
+static ssize_t
+# if SFIO
+tls_read(f, buf, size, disc)
+	Sfio_t *f;
+	Void_t *buf;
+	size_t size;
+	Sfdisc_t *disc;
+# else /* SFIO */
+tls_read(disc, buf, size)
+	void *disc;
+	void *buf;
+	size_t size;
+# endif /* SFIO */
+{
+	int r;
+	Tlsdisc_t *sd;
+
+	/* Cast back to correct type */
+	sd = (Tlsdisc_t *) disc;
+
+	r = SSL_read(sd->con, (char *) buf, size);
+	if (r < 0 && LogLevel > 7)
+	{
+		char *err;
+
+		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";
+				break;
+		}
+		if (err != NULL)
+			sm_syslog(LOG_WARNING, NOQID, "TLS: read error:  %s",
+				  err);
+	}
+	return r;
+}
+
+static ssize_t
+# if SFIO
+tls_write(f, buf, size, disc)
+	Sfio_t *f;
+	const Void_t *buf;
+	size_t size;
+	Sfdisc_t *disc;
+# else /* SFIO */
+tls_write(disc, buf, size)
+	void *disc;
+	const void *buf;
+	size_t size;
+# endif /* SFIO */
+{
+	int r;
+	Tlsdisc_t *sd;
+
+	/* Cast back to correct type */
+	sd = (Tlsdisc_t *) disc;
+
+	r = SSL_write(sd->con, (char *)buf, size);
+	if (r < 0 && LogLevel > 7)
+	{
+		char *err;
+
+		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_GET_REASON(ERR_peek_error()));
+*/
+				break;
+		}
+		if (err != NULL)
+			sm_syslog(LOG_WARNING, NOQID, "TLS: write error:  %s",
+				  err);
+	}
+	return r;
+}
+
+# if !SFIO
+static int
+tls_close(cookie)
+	void *cookie;
+{
+	int retval = 0;
+	Tlsdisc_t *tc;
+
+	/* Cast back to correct type */
+	tc = (Tlsdisc_t *)cookie;
+
+	if (tc->fp != NULL)
+	{
+		retval = fclose(tc->fp);
+		tc->fp = NULL;
+	}
+
+	sm_free(tc);
+	return retval;
+}
+# endif /* !SFIO */
+
+int
+sfdctls(fin, fout, con)
+# if SFIO
+	Sfio_t *fin;
+	Sfio_t *fout;
+# else /* SFIO */
+	FILE **fin;
+	FILE **fout;
+# endif /* SFIO */
+	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);
+		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)
+	{
+		FILE *save;
+
+		/* Hack: Don't close underlying fp */
+		save = tlsin->fp;
+		tlsin->fp = NULL;
+		fclose(*fin);
+		*fin = save;
+		sm_free(tlsout);
+		return -1;
+	}
+	*fout = fp;
+	SSL_set_rfd(con, fileno(tlsin->fp));
+	SSL_set_wfd(con, fileno(tlsout->fp));
+# endif /* SFIO */
+	return 0;
+}
+#endif /* STARTTLS && (SFIO || _FFR_TLS_TOREK) */
Index: gnu/usr.sbin/sendmail/sendmail/sfsasl.h
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/sfsasl.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- gnu/usr.sbin/sendmail/sendmail/sfsasl.h	2000/04/07 19:20:44	1.2
+++ gnu/usr.sbin/sendmail/sendmail/sfsasl.h	2001/01/15 21:09:10	1.3
@@ -6,12 +6,55 @@
  * forth in the LICENSE file which can be found at the top level of
  * the sendmail distribution.
  *
- *	$Sendmail: sfsasl.h,v 8.13 2000/03/10 18:09:34 ca Exp $"
+ *	$Sendmail: sfsasl.h,v 8.13.4.4 2000/07/18 18:44:51 gshapiro 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 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 */
+# endif /* STARTTLS */
 #endif /* ! SFSASL_H */
Index: gnu/usr.sbin/sendmail/sendmail/shmticklib.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/shmticklib.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/sendmail/shmticklib.c	2000/04/02 19:05:48	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/shmticklib.c	2001/01/15 21:09:10	1.2
@@ -15,7 +15,11 @@
 #endif /* ! lint */
 
 #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>
Index: gnu/usr.sbin/sendmail/sendmail/srvrsmtp.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/srvrsmtp.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/sendmail/srvrsmtp.c	2000/04/07 19:20:44	1.2
+++ gnu/usr.sbin/sendmail/sendmail/srvrsmtp.c	2001/05/29 01:31:16	1.5
@@ -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,21 +11,45 @@
  *
  */
 
+
 #include <sendmail.h>
 
 #ifndef lint
 # if SMTP
-static char id[] = "@(#)$Sendmail: srvrsmtp.c,v 8.471 2000/04/06 08:39:58 gshapiro Exp $ (with SMTP)";
+static char id[] = "@(#)$Sendmail: srvrsmtp.c,v 8.471.2.2.2.77 2001/05/27 22:20:30 gshapiro Exp $ (with SMTP)";
 # else /* SMTP */
-static char id[] = "@(#)$Sendmail: srvrsmtp.c,v 8.471 2000/04/06 08:39:58 gshapiro Exp $ (without SMTP)";
+static char id[] = "@(#)$Sendmail: srvrsmtp.c,v 8.471.2.2.2.77 2001/05/27 22:20:30 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)
 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,
 				     char *, ENVELOPE *));
@@ -75,6 +99,9 @@
 # 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 */
@@ -87,6 +114,11 @@
 # define CMDDBGQSHOW	24	/* showq -- show send queue */
 # define CMDDBGDEBUG	25	/* debug -- set debug mode */
 
+/*
+**  Note: If you change this list,
+**        remember to update 'helpfile'
+*/
+
 static struct cmd	CmdTab[] =
 {
 	{ "mail",	CMDMAIL		},
@@ -111,6 +143,9 @@
 # if SASL
 	{ "auth",	CMDAUTH,	},
 # endif /* SASL */
+# if STARTTLS
+	{ "starttls",	CMDSTLS,	},
+# endif /* STARTTLS */
     /* remaining commands are here only to trap and log attempts to use them */
 	{ "showq",	CMDDBGQSHOW	},
 	{ "debug",	CMDDBGDEBUG	},
@@ -129,6 +164,11 @@
 # define MAXETRNCOMMANDS	8	/* max ETRN commands before slowdown */
 # define MAXTIMEOUT	(4 * 60)	/* max timeout for bad commands */
 
+/* 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)
 	char *volatile nullserver;
@@ -149,6 +189,7 @@
 	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 */
@@ -164,7 +205,7 @@
 # endif /* _FFR_MILTER */
 	volatile time_t wt;		/* timeout after too many commands */
 	volatile time_t previous;	/* time after checksmtpattack() */
-	volatile int lognullconnection = TRUE;
+	volatile bool lognullconnection = TRUE;
 	register char *q;
 	char *addr;
 	char *greetcode = "220";
@@ -192,7 +233,18 @@
 	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
+	int r;
+	int rfd, wfd;
+	volatile bool usetls = TRUE;
+	volatile bool tls_active = FALSE;
+	bool saveQuickAbort;
+	bool saveSuprErrs;
+# endif /* STARTTLS */
 
 	if (fileno(OutChannel) != fileno(stdout))
 	{
@@ -222,7 +274,7 @@
 	/* SASL server new connection */
 	hostname = macvalue('j', e);
 #  if SASL > 10505
-	/* use empty realm: doesn't work in SASL <= 1.5.5 */
+	/* 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 */
@@ -275,6 +327,14 @@
 
 		/* 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() */
+		{
+			ssp.max_ssf = INT_MAX;
+			ssp.maxbufsize = MAXOUTLEN;
+		}
+#  endif /* SFIO */
 #  if _FFR_SASL_OPTS
 		ssp.security_flags = SASLOpts & SASL_SEC_MASK;
 #  endif /* _FFR_SASL_OPTS */
@@ -285,6 +345,10 @@
 			/*
 			**  external security strength factor;
 			**  we have none so zero
+#   if STARTTLS
+			**  we may have to change this for STARTTLS
+			**  (dynamically)
+#   endif
 			*/
 			ext_ssf.ssf = 0;
 			ext_ssf.auth_id = NULL;
@@ -306,6 +370,20 @@
 	}
 # 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)
 	{
@@ -343,11 +421,6 @@
 			milterize = FALSE;
 			break;
 
-		  case SMFIR_DISCARD:
-			e->e_flags |= EF_DISCARD;
-			milterize = FALSE;
-			break;
-
 		  case SMFIR_TEMPFAIL:
 			tempfail = TRUE;
 			milterize = FALSE;
@@ -370,7 +443,7 @@
 	else
 		snprintf(cmdbuf, sizeof cmdbuf,
 			 "%s-%%.*s ESMTP%%s", greetcode);
-	message(cmdbuf, id - inp, inp, id);
+	message(cmdbuf, (int) (id - inp), inp, id);
 
 	/* output remaining lines */
 	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
@@ -524,11 +597,67 @@
 #  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);
+					ssf = NULL;
+				}
+				else
+				{
+					char pbuf[8];
+
+					snprintf(pbuf, sizeof pbuf, "%u", *ssf);
+					define(macid("{auth_ssf}", NULL),
+					       newstr(pbuf), &BlankEnvelope);
+					if (tTd(95, 8))
+						dprintf("SASL auth_ssf: %u\n",
+							*ssf);
+				}
+				/*
+				**  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
+					*/
+					if (sfdcsasl(InChannel, OutChannel,
+					    conn) == 0)
+					{
+						/* restart dialogue */
+						gothello = FALSE;
+						OneXact = TRUE;
+						n_helo = 0;
+					}
+					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)
 					sm_syslog(LOG_INFO, NOQID,
 						  "SASL: connection from %.64s: mech=%.16s, id=%.64s",
 						  CurSmtpClient, auth_type,
 						  user);
+#  endif /* SFIO */
 			}
 			else if (result == SASL_CONTINUE)
 			{
@@ -609,7 +738,7 @@
 		/* decode command */
 		for (c = CmdTab; c->cmd_name != NULL; c++)
 		{
-			if (!strcasecmp(c->cmd_name, cmdbuf))
+			if (strcasecmp(c->cmd_name, cmdbuf) == 0)
 				break;
 		}
 
@@ -691,6 +820,16 @@
 				message("503 5.5.0 AUTH not permitted during a mail transaction");
 				break;
 			}
+			if (tempfail)
+			{
+				if (LogLevel > 9)
+					sm_syslog(LOG_INFO, e->e_id,
+						  "SMTP AUTH command (%.100s) from %.100s tempfailed (due to previous checks)",
+						  p, CurSmtpClient);
+				usrerr("454 4.7.1 Please try again later");
+				break;
+			}
+
 			ismore = FALSE;
 
 			/* crude way to avoid crack attempts */
@@ -808,13 +947,196 @@
 			}
 			else
 			{
-				message("334 %s", *out2 == '\0' ? "=" : out2);
+				message("334 %s", out2);
 				authenticating = SASL_PROC_AUTH;
 			}
 
 			break;
 # endif /* SASL */
 
+# if STARTTLS
+		  case CMDSTLS: /* starttls */
+			if (*p != '\0')
+			{
+				message("501 5.5.2 Syntax error (no parameters allowed)");
+				break;
+			}
+			if (!usetls)
+			{
+				message("503 5.5.0 TLS not available");
+				break;
+			}
+			if (!tls_ok_srv)
+			{
+				message("454 4.3.3 TLS not available after start");
+				break;
+			}
+			if (gotmail)
+			{
+				message("503 5.5.0 TLS not permitted during a mail transaction");
+				break;
+			}
+			if (tempfail)
+			{
+				if (LogLevel > 9)
+					sm_syslog(LOG_INFO, e->e_id,
+						  "SMTP STARTTLS command (%.100s) from %.100s tempfailed (due to previous checks)",
+						  p, CurSmtpClient);
+				usrerr("454 4.7.1 Please try again later");
+				break;
+			}
+# 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 (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");
+				break;
+			}
+			rfd = fileno(InChannel);
+			wfd = fileno(OutChannel);
+			if (rfd < 0 || wfd < 0 ||
+			    SSL_set_rfd(srv_ssl, rfd) <= 0 ||
+			    SSL_set_wfd(srv_ssl, wfd) <= 0)
+			{
+				message("454 4.3.3 TLS not available: error set fd");
+				SSL_free(srv_ssl);
+				srv_ssl = NULL;
+				break;
+			}
+			message("220 2.0.0 Ready to start TLS");
+			SSL_set_accept_state(srv_ssl);
+
+#  define SSL_ACC(s)	SSL_accept(s)
+			if ((r = SSL_ACC(srv_ssl)) <= 0)
+			{
+				int i;
+
+				/* what to do in this case? */
+				i = SSL_get_error(srv_ssl, r);
+				if (LogLevel > 5)
+				{
+					sm_syslog(LOG_WARNING, e->e_id,
+						  "TLS: error: accept failed=%d (%d)",
+						  r, i);
+					if (LogLevel > 9)
+						tlslogerr();
+				}
+				tls_ok_srv = FALSE;
+				SSL_free(srv_ssl);
+				srv_ssl = NULL;
+
+				/*
+				**  according to the next draft of
+				**  RFC 2487 the connection should be dropped
+				*/
+
+				/* arrange to ignore any current send list */
+				e->e_sendqueue = NULL;
+				goto doquit;
+			}
+
+			/* ignore return code for now, it's in {verify} */
+			(void) tls_get_info(srv_ssl, &BlankEnvelope, TRUE,
+					    CurSmtpClient, TRUE);
+
+			/*
+			**  call Stls_client to find out whether
+			**  to accept the connection from the client
+			*/
+
+			saveQuickAbort = QuickAbort;
+			saveSuprErrs = SuprErrs;
+			SuprErrs = TRUE;
+			QuickAbort = FALSE;
+			if (rscheck("tls_client",
+				     macvalue(macid("{verify}", NULL), e),
+				     "STARTTLS", e, TRUE, TRUE, 6, NULL) !=
+			    EX_OK || Errors > 0)
+			{
+				extern char MsgBuf[];
+
+				if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf))
+					nullserver = newstr(MsgBuf);
+				else
+					nullserver = "503 5.7.0 Authentication required.";
+			}
+			QuickAbort = saveQuickAbort;
+			SuprErrs = saveSuprErrs;
+
+			tls_ok_srv = FALSE;	/* don't offer STARTTLS again */
+			gothello = FALSE;	/* discard info */
+			n_helo = 0;
+			OneXact = TRUE;	/* only one xaction this run */
+#  if SASL
+			if (sasl_ok)
+			{
+				char *s;
+
+				if ((s = macvalue(macid("{cipher_bits}", NULL), e)) != NULL &&
+				    (ext_ssf.ssf = atoi(s)) > 0)
+				{
+#  if _FFR_EXT_MECH
+					ext_ssf.auth_id = macvalue(macid("{cert_subject}",
+									 NULL),
+								   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 */
+
+			/* 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;
+			else
+			{
+				/*
+				**  XXX this is an internal error
+				**  how to deal with it?
+				**  we can't generate an error message
+				**  since the other side switched to an
+				**  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");
+			}
+			break;
+# endif /* STARTTLS */
 
 		  case CMDHELO:		/* hello -- introduce yourself */
 		  case CMDEHLO:		/* extended hello */
@@ -837,7 +1159,7 @@
 			if (gothello)
 			{
 				usrerr("503 %s Duplicate HELO/EHLO",
-					MyHostName);
+				       MyHostName);
 				break;
 			}
 
@@ -894,49 +1216,35 @@
 				q = "accepting invalid domain name";
 			}
 
+			gothello = TRUE;
+
 # if _FFR_MILTER
 			if (milterize && !bitset(EF_DISCARD, e->e_flags))
 			{
 				char state;
 				char *response;
 
-				ok = TRUE;
 				response = milter_helo(p, e, &state);
 				switch (state)
 				{
 				  case SMFIR_REPLYCODE:
+					nullserver = response;
 					milterize = FALSE;
-					ok = FALSE;
-					usrerr(response);
 					break;
 
 				  case SMFIR_REJECT:
-					ok = FALSE;
 					nullserver = "Command rejected";
 					milterize = FALSE;
-					usrerr("550 HELO/EHLO rejected");
 					break;
 
-				  case SMFIR_DISCARD:
-					e->e_flags |= EF_DISCARD;
-					milterize = FALSE;
-					break;
-
 				  case SMFIR_TEMPFAIL:
-					ok = FALSE;
 					tempfail = TRUE;
 					milterize = FALSE;
 					break;
 				}
-				if (response != NULL)
-					free(response);
-				if (!ok)
-					break;
 			}
 # endif /* _FFR_MILTER */
 
-			gothello = TRUE;
-
 			/* print HELO response message */
 			if (c->cmd_code != CMDEHLO)
 			{
@@ -954,8 +1262,15 @@
 				message("250 ENHANCEDSTATUSCODES");
 				break;
 			}
+
+			/*
+			**  print EHLO features list
+			**
+			**  Note: If you change this list,
+			**        remember to update 'helpfile'
+			*/
+
 
-			/* print EHLO features list */
 			message("250-ENHANCEDSTATUSCODES");
 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
 			{
@@ -985,6 +1300,10 @@
 			if (sasl_ok && mechlist != NULL && *mechlist != '\0')
 				message("250-AUTH %s", mechlist);
 # endif /* SASL */
+# if STARTTLS
+			if (tls_ok_srv && usetls)
+				message("250-STARTTLS");
+# endif /* STARTTLS */
 			message("250 HELP");
 			break;
 
@@ -1017,26 +1336,33 @@
 			}
 # endif /* SASL */
 
+			p = skipword(p, "from");
+			if (p == NULL)
+				break;
 			if (tempfail)
 			{
 				if (LogLevel > 9)
 					sm_syslog(LOG_INFO, e->e_id,
-						  "MAIL From:<%.100s> from %.100s tempfailed (from previous HELO/EHLO check)",
-						  args[0], CurSmtpClient);
+						  "SMTP MAIL command (%.100s) from %.100s tempfailed (due to previous checks)",
+						  p, CurSmtpClient);
 				usrerr("451 4.7.1 Please try again later");
 				break;
 			}
+
 			/* make sure we know who the sending host is */
 			if (sendinghost == NULL)
 				sendinghost = peerhostname;
 
-			p = skipword(p, "from");
-			if (p == NULL)
-				break;
 
 			/* fork a subprocess to process this command */
-			if (runinchild("SMTP-MAIL", e) > 0)
+			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 (Errors > 0)
 				goto undo_subproc_no_pm;
 			if (!gothello)
@@ -1194,11 +1520,13 @@
 
 			/* do config file checking of the sender */
 			if (rscheck("check_mail", addr,
-				    NULL, e, TRUE, TRUE, 4) != EX_OK ||
+				    NULL, e, TRUE, TRUE, 4, NULL) != EX_OK ||
 			    Errors > 0)
 				goto undo_subproc_no_pm;
 
-			if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
+			if (MaxMessageSize > 0 &&
+			    (e->e_msgsize > MaxMessageSize ||
+			     e->e_msgsize < 0))
 			{
 				usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
 					MaxMessageSize);
@@ -1214,6 +1542,7 @@
 				goto undo_subproc_no_pm;
 
 # if _FFR_MILTER
+			LogUsrErrs = TRUE;
 			if (milterize && !bitset(EF_DISCARD, e->e_flags))
 			{
 				char state;
@@ -1235,11 +1564,11 @@
 					break;
 
 				  case SMFIR_TEMPFAIL:
-					usrerr("451 4.7.1 Try again later");
+					usrerr("451 4.7.1 Please try again later");
 					break;
 				}
 				if (response != NULL)
-					free(response);
+					sm_free(response);
 			}
 # endif /* _FFR_MILTER */
 			if (Errors > 0)
@@ -1387,7 +1716,7 @@
 
 			/* do config file checking of the recipient */
 			if (rscheck("check_rcpt", addr,
-				    NULL, e, TRUE, TRUE, 4) != EX_OK ||
+				    NULL, e, TRUE, TRUE, 4, NULL) != EX_OK ||
 			    Errors > 0)
 				break;
 
@@ -1413,11 +1742,11 @@
 					break;
 
 				  case SMFIR_TEMPFAIL:
-					usrerr("451 4.7.1 Try again later");
+					usrerr("451 4.7.1 Please try again later");
 					break;
 				}
 				if (response != NULL)
-					free(response);
+					sm_free(response);
 			}
 # endif /* _FFR_MILTER */
 
@@ -1499,7 +1828,7 @@
 				char state;
 				char *response;
 
-				response = milter_body(e, &state);
+				response = milter_data(e, &state);
 				switch (state)
 				{
 				  case SMFIR_REPLYCODE:
@@ -1515,11 +1844,11 @@
 					break;
 
 				  case SMFIR_TEMPFAIL:
-					usrerr("451 4.7.1 Try again later");
+					usrerr("451 4.7.1 Please try again later");
 					break;
 				}
 				if (response != NULL)
-					free(response);
+					sm_free(response);
 			}
 
 			/* abort message filters that didn't get the body */
@@ -1530,7 +1859,7 @@
 			/* redefine message size */
 			if ((q = macvalue(macid("{msg_size}", NULL), e))
 			    != NULL)
-				free(q);
+				sm_free(q);
 			snprintf(inp, sizeof inp, "%ld", e->e_msgsize);
 			define(macid("{msg_size}", NULL), newstr(inp), e);
 			if (Errors > 0)
@@ -1686,6 +2015,16 @@
 
 		  case CMDVRFY:		/* vrfy -- verify address */
 		  case CMDEXPN:		/* expn -- expand address */
+			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",
+						  p, CurSmtpClient);
+				usrerr("550 5.7.1 Please try again later");
+				break;
+			}
 			wt = checksmtpattack(&nverifies, MAXVRFYCOMMANDS, FALSE,
 				c->cmd_code == CMDVRFY ? "VRFY" : "EXPN", e);
 			previous = curtime();
@@ -1736,13 +2075,19 @@
 			{
 				/* do config file checking of the address */
 				if (rscheck(vrfy ? "check_vrfy" : "check_expn",
-					    p, NULL, e, TRUE, FALSE, 4)
+					    p, NULL, e, TRUE, FALSE, 4, NULL)
 				    != EX_OK || Errors > 0)
 					goto undo_subproc;
 				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
 			}
 			if (wt > 0)
-				(void) sleep(wt - (curtime() - previous));
+			{
+				time_t t;
+
+				t = wt - (curtime() - previous);
+				if (t > 0)
+					(void) sleep(t);
+			}
 			if (Errors > 0)
 				goto undo_subproc;
 			if (vrfyqueue == NULL)
@@ -1760,7 +2105,7 @@
 				/* see if there is more in the vrfy list */
 				a = vrfyqueue;
 				while ((a = a->q_next) != NULL &&
-				       (!QS_IS_UNDELIVERED(vrfyqueue->q_state)))
+				       (!QS_IS_UNDELIVERED(a->q_state)))
 					continue;
 				printvrfyaddr(vrfyqueue, a == NULL, vrfy);
 				vrfyqueue = a;
@@ -1782,6 +2127,15 @@
 						  shortenstring(inp, MAXSHORTSTR));
 				break;
 			}
+			if (tempfail)
+			{
+				if (LogLevel > 9)
+					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");
+				break;
+			}
 
 			if (strlen(p) <= 0)
 			{
@@ -1794,8 +2148,8 @@
 					     "ETRN", e);
 
 			/* do config file checking of the parameter */
-			if (rscheck("check_etrn", p, NULL, e, TRUE, FALSE, 4)
-			    != EX_OK || Errors > 0)
+			if (rscheck("check_etrn", p, NULL, e, TRUE, FALSE, 4,
+				    NULL) != EX_OK || Errors > 0)
 				break;
 
 			if (LogLevel > 5)
@@ -1810,16 +2164,12 @@
 			else
 				*--id = '@';
 
-			if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
-			{
-				syserr("500 5.5.0 ETRN out of memory");
-				break;
-			}
+			new = (QUEUE_CHAR *)xalloc(sizeof(QUEUE_CHAR));
 			new->queue_match = id;
 			new->queue_next = NULL;
 			QueueLimitRecipient = new;
 			ok = runqueue(TRUE, FALSE);
-			free(QueueLimitRecipient);
+			sm_free(QueueLimitRecipient);
 			QueueLimitRecipient = NULL;
 			if (ok && Errors == 0)
 				message("250 2.0.0 Queuing for node %s started", p);
@@ -1841,6 +2191,14 @@
 			/* arrange to ignore any current send list */
 			e->e_sendqueue = NULL;
 
+# if STARTTLS
+			/* shutdown TLS connection */
+			if (tls_active)
+			{
+				(void) endtls(srv_ssl, "server");
+				tls_active = FALSE;
+			}
+# endif /* STARTTLS */
 # if SASL
 			if (authenticating == SASL_IS_AUTH)
 			{
@@ -1975,7 +2333,7 @@
 **		e -- the current envelope.
 **
 **	Returns:
-**		none.
+**		time to wait.
 **
 **	Side Effects:
 **		Slows down if we seem to be under attack.
@@ -1996,8 +2354,8 @@
 		if (*pcounter == maxcount && LogLevel > 5)
 		{
 			sm_syslog(LOG_INFO, e->e_id,
-				  "%.100s: %.40s attack?",
-				  CurSmtpClient, cname);
+				  "%.100s: possible SMTP attack: command=%.40s, count=%d",
+				  CurSmtpClient, cname, *pcounter);
 		}
 		s = 1 << (*pcounter - maxcount);
 		if (s >= MAXTIMEOUT)
@@ -2093,11 +2451,12 @@
 			/* NOTREACHED */
 		}
 		define(macid("{msg_size}", NULL), newstr(vp), e);
-# if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
-		e->e_msgsize = strtoul(vp, (char **) NULL, 10);
-# else /* defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY) */
 		e->e_msgsize = strtol(vp, (char **) NULL, 10);
-# endif /* defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY) */
+		if (e->e_msgsize == LONG_MAX && errno == ERANGE)
+		{
+			usrerr("552 5.2.3 Message size exceeds maximum value");
+			/* NOTREACHED */
+		}
 	}
 	else if (strcasecmp(kp, "body") == 0)
 	{
@@ -2224,8 +2583,8 @@
 		SuprErrs = TRUE;
 		QuickAbort = FALSE;
 		if (strcmp(auth_param, "<>") != 0 &&
-		     (rscheck("trust_auth", pbuf, NULL, e, TRUE, FALSE, 10)
-		      != EX_OK || Errors > 0))
+		     (rscheck("trust_auth", pbuf, NULL, e, TRUE, FALSE, 10,
+			      NULL) != EX_OK || Errors > 0))
 		{
 			if (tTd(95, 8))
 			{
@@ -2242,7 +2601,8 @@
 				dprintf("auth=\"%.100s\" trusted\n", pbuf);
 			e->e_auth_param = newstr(auth_param);
 		}
-		free(auth_param);
+		sm_free(auth_param);
+
 		/* reset values */
 		Errors = 0;
 		QuickAbort = saveQuickAbort;
@@ -2251,7 +2611,7 @@
 # endif /* SASL */
 	else
 	{
-		usrerr("501 5.5.4 %s parameter unrecognized", kp);
+		usrerr("555 5.5.4 %s parameter unrecognized", kp);
 		/* NOTREACHED */
 	}
 }
@@ -2340,7 +2700,7 @@
 	}
 	else
 	{
-		usrerr("501 5.5.4 %s parameter unrecognized", kp);
+		usrerr("555 5.5.4 %s parameter unrecognized", kp);
 		/* NOTREACHED */
 	}
 }
@@ -2409,8 +2769,9 @@
 **		label -- a string used in error messages
 **
 **	Returns:
-**		zero in the child
-**		one in the parent
+**		RIC_INCHILD in the child
+**		RIC_INPARENT in the parent
+**		RIC_TEMPFAIL tempfail condition
 **
 **	Side Effects:
 **		none.
@@ -2443,12 +2804,13 @@
 
 		(void) blocksignal(SIGCHLD);
 
+
 		childpid = dofork();
 		if (childpid < 0)
 		{
 			syserr("451 4.3.0 %s: cannot fork", label);
 			(void) releasesignal(SIGCHLD);
-			return 1;
+			return RIC_INPARENT;
 		}
 		if (childpid > 0)
 		{
@@ -2461,10 +2823,13 @@
 			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);
+				       label, st & 0177);
+				return RIC_TEMPFAIL;
+			}
 
-			/* if we exited on a QUIT command, complete the process */
+			/* if exited on a QUIT command, complete the process */
 			if (WEXITSTATUS(st) == EX_QUIT)
 			{
 				disconnect(1, e);
@@ -2474,13 +2839,19 @@
 			/* restore the child signal */
 			(void) releasesignal(SIGCHLD);
 
-			return 1;
+			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);
@@ -2488,7 +2859,7 @@
 			(void) releasesignal(SIGCHLD);
 		}
 	}
-	return 0;
+	return RIC_INCHILD;
 }
 
 # if SASL
@@ -2503,6 +2874,7 @@
 **	Returns:
 **		number of mechs
 */
+
 static int
 saslmechs(conn, mechlist)
 	sasl_conn_t *conn;
@@ -2528,6 +2900,7 @@
 			sm_syslog(LOG_WARNING, NOQID,
 				  "SASL error: listmech=%d, num=%d",
 				  result, num);
+		num = 0;
 	}
 	return num;
 }
@@ -2545,6 +2918,7 @@
 **	Returns:
 **		ok?
 */
+
 int
 proxy_policy(context, auth_identity, requested_user, user, errstr)
 	void *context;
@@ -2561,6 +2935,1265 @@
 
 # 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;
+}
+
+/*
+**  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
+**
+**	Parameters:
+**		none.
+**
+**	Returns:
+**		succeeded?
+**
+**	Side Effects:
+**		Sets tls_ok_srv static, even when called from main()
+*/
+
+bool
+init_tls_library()
+{
+	/*
+	**  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);
+
+	return tls_ok_srv;
+}
+/*
+**  INITTLS -- initialize TLS
+**
+**	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
+**
+**	Returns:
+**		succeeded?
+*/
+
+bool
+inittls(ctx, req, srv, certfile, keyfile, cacertpath, cacertfile, dhparam)
+	SSL_CTX **ctx;
+	u_long req;
+	bool srv;
+	char *certfile, *keyfile, *cacertpath, *cacertfile, *dhparam;
+{
+# 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 */
+
+
+#  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 */
+
+	/*
+	**  load private key
+	**  XXX change this for DSA-only version
+	*/
+	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)
+	{
+		/* 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 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)
+			{
+				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 (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)
+			{
+				sm_syslog(LOG_WARNING, NOQID,
+					  "TLS: error: %s: %d load verify locs %s, %s",
+					  who, r, cacertpath, cacertfile);
+				if (LogLevel > 9)
+					tlslogerr();
+			}
+			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)
+			{
+				sm_syslog(LOG_WARNING, NOQID,
+					  "TLS: error: %s: SSL_CTX_set_cipher_list(%s) failed, list ignored",
+					  who, CipherList);
+
+				if (LogLevel > 9)
+					tlslogerr();
+			}
+			/* failure if setting to this list is required? */
+		}
+	}
+#  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 : "");
+	}
+}
+
+# endif /* STARTTLS */
 #endif /* SMTP */
 /*
 **  HELP -- implement the HELP command.
Index: gnu/usr.sbin/sendmail/sendmail/stab.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/stab.c,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -r1.2 -r1.4
--- gnu/usr.sbin/sendmail/sendmail/stab.c	2000/10/09 23:45:01	1.2
+++ gnu/usr.sbin/sendmail/sendmail/stab.c	2001/05/29 01:31:16	1.4
@@ -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
@@ -12,7 +12,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: stab.c,v 8.40 1999/11/04 23:31:07 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: stab.c,v 8.40.16.7 2001/05/07 22:06:41 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
@@ -160,8 +160,8 @@
 		break;
 
 #ifdef LDAPMAP
-	  case ST_LDAP:
-		len = sizeof s->s_ldap;
+	  case ST_LMAP:
+		len = sizeof s->s_lmap;
 		break;
 #endif /* LDAPMAP */
 
@@ -268,6 +268,7 @@
 	if (e == NULL)
 		return;
 
+	class = bitidx(class);
 	for (shead = SymTab; shead < &SymTab[STABSIZE]; shead++)
 	{
 		for (s = *shead; s != NULL; s = s->s_next)
@@ -276,10 +277,30 @@
 			char *p;
 
 			if (s->s_type == ST_CLASS &&
-			    bitnset(class & 0xff, s->s_class) &&
+			    bitnset(class, s->s_class) &&
 			    (m = macid(s->s_name, NULL)) != '\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));
@@ -306,13 +327,14 @@
 	register STAB **shead;
 	register STAB *s;
 
-	dst = ((unsigned int)dst) & 0xff;
+	src = bitidx(src);
+	dst = bitidx(dst);
 	for (shead = SymTab; shead < &SymTab[STABSIZE]; shead++)
 	{
 		for (s = *shead; s != NULL; s = s->s_next)
 		{
 			if (s->s_type == ST_CLASS &&
-			    bitnset(src & 0xff, s->s_class))
+			    bitnset(src, s->s_class))
 				setbitn(dst, s->s_class);
 		}
 	}
Index: gnu/usr.sbin/sendmail/sendmail/stats.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/stats.c,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- gnu/usr.sbin/sendmail/sendmail/stats.c	2000/04/02 19:05:48	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/stats.c	2001/02/28 02:43:55	1.3
@@ -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
@@ -12,16 +12,18 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: stats.c,v 8.36 1999/12/06 21:17:02 ca Exp $";
+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>
 
+
 static struct statistics	Stat;
 
 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?) */
 #define KBYTES(x)	(((x) + (ONE_K - 1)) / ONE_K)
 /*
@@ -72,6 +74,8 @@
 		Stat.stat_nt[to->q_mailer->m_mno]++;
 		Stat.stat_bt[to->q_mailer->m_mno] += KBYTES(e->e_msgsize);
 	}
+
+
 	GotStats = TRUE;
 }
 /*
Index: gnu/usr.sbin/sendmail/sendmail/timers.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/timers.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/sendmail/timers.c	2000/04/02 19:05:48	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/timers.c	2001/01/15 21:09:11	1.2
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -11,7 +11,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: timers.c,v 8.13 1999/11/23 07:22:28 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: timers.c,v 8.13.16.1 2000/10/09 01:06:45 gshapiro Exp $";
 #endif /* ! lint */
 
 #if _FFR_TIMERS
@@ -204,8 +204,11 @@
 
 	/* pop back to this timer */
 	for (i = 0; i < NTimers; i++)
+	{
 		if (TimerStack[i] == ptimer)
 			break;
+	}
+
 	if (i != NTimers - 1)
 		warntimer("poptimer: odd pop (timer=0x%lx, index=%d, NTimers=%d)",
 			  (u_long) ptimer, i, NTimers);
Index: gnu/usr.sbin/sendmail/sendmail/trace.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/trace.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- gnu/usr.sbin/sendmail/sendmail/trace.c	2000/04/02 19:05:48	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/trace.c	2001/01/15 21:09:11	1.2
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
  *	All rights reserved.
  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
  * Copyright (c) 1988, 1993
@@ -12,7 +12,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: trace.c,v 8.20 1999/08/02 21:44:36 ca Exp $";
+static char id[] = "@(#)$Sendmail: trace.c,v 8.20.22.2 2000/09/17 17:04:27 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
@@ -63,7 +63,7 @@
 tTflag(s)
 	register char *s;
 {
-	unsigned int first, last;
+	int first, last;
 	register unsigned int i;
 
 	if (*s == '\0')
Index: gnu/usr.sbin/sendmail/sendmail/udb.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/udb.c,v
retrieving revision 1.1.1.1
retrieving revision 1.4
diff -u -r1.1.1.1 -r1.4
--- gnu/usr.sbin/sendmail/sendmail/udb.c	2000/04/02 19:05:48	1.1.1.1
+++ gnu/usr.sbin/sendmail/sendmail/udb.c	2001/05/29 01:31:16	1.4
@@ -1,5 +1,5 @@
 /*
- * 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, 1995-1997 Eric P. Allman.  All rights reserved.
  * Copyright (c) 1988, 1993
@@ -15,9 +15,9 @@
 
 #ifndef lint
 # if USERDB
-static char id[] = "@(#)$Sendmail: udb.c,v 8.111 1999/11/16 02:04:04 gshapiro Exp $ (with 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 1999/11/16 02:04:04 gshapiro Exp $ (without 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 */
 
@@ -290,7 +290,7 @@
 
 					memmove(nuser, user, usersize);
 					if (user != userbuf)
-						free(user);
+						sm_free(user);
 					user = nuser;
 					usersize += size;
 					userleft += size;
@@ -545,7 +545,7 @@
 			break;
 		}
 		if (user != userbuf)
-			free(user);
+			sm_free(user);
 	}
 	return EX_OK;
 }
@@ -1011,6 +1011,10 @@
 								0644);
 					if (ret != 0)
 					{
+#ifdef DB_OLD_VERSION
+						if (ret == DB_OLD_VERSION)
+							ret = EINVAL;
+#endif /* DB_OLD_VERSION */
 						(void) up->udb_dbp->close(up->udb_dbp, 0);
 						up->udb_dbp = NULL;
 					}
@@ -1051,11 +1055,11 @@
 							  errstring(errno));
 					up->udb_type = UDB_EOLIST;
 					if (up->udb_dbname != spec)
-						free(up->udb_dbname);
+						sm_free(up->udb_dbname);
 					goto tempfail;
 				}
 				if (up->udb_dbname != spec)
-					free(up->udb_dbname);
+					sm_free(up->udb_dbname);
 				break;
 			}
 			if (tTd(28, 1))
Index: gnu/usr.sbin/sendmail/sendmail/usersmtp.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/usersmtp.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/sendmail/usersmtp.c	2000/04/07 19:20:44	1.2
+++ gnu/usr.sbin/sendmail/sendmail/usersmtp.c	2001/05/29 01:31:16	1.5
@@ -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
@@ -15,9 +15,9 @@
 
 #ifndef lint
 # if SMTP
-static char id[] = "@(#)$Sendmail: usersmtp.c,v 8.245 2000/03/23 17:35:10 ca Exp $ (with SMTP)";
+static char id[] = "@(#)$Sendmail: usersmtp.c,v 8.245.4.33 2001/05/23 18:53:09 ca Exp $ (with SMTP)";
 # else /* SMTP */
-static char id[] = "@(#)$Sendmail: usersmtp.c,v 8.245 2000/03/23 17:35:10 ca Exp $ (without SMTP)";
+static char id[] = "@(#)$Sendmail: usersmtp.c,v 8.245.4.33 2001/05/23 18:53:09 ca Exp $ (without SMTP)";
 # endif /* SMTP */
 #endif /* ! lint */
 
@@ -25,6 +25,7 @@
 
 #if SMTP
 
+
 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 *));
@@ -198,7 +199,8 @@
 	{
 		syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
 			CurHostName);
-		mci_setstat(mci, EX_CONFIG, "5.3.5", "system config error");
+		mci_setstat(mci, EX_CONFIG, "5.3.5",
+			    "553 5.3.5 system config error");
 		mci->mci_errno = 0;
 		smtpquit(m, mci, e);
 		return;
@@ -277,7 +279,69 @@
 	if (strstr(line, "8BIT-OK") != NULL)
 		mci->mci_flags |= MCIF_8BITOK;
 }
+# if SASL
 /*
+**  STR_UNION -- create the union of two lists
+**
+**	Parameters:
+**		s1, s2 -- lists of items (separated by single blanks).
+**
+**	Returns:
+**		the union of both lists.
+*/
+
+static char *
+str_union(s1, s2)
+	char *s1, *s2;
+{
+	char *hr, *h1, *h, *res;
+	int l1, l2, rl;
+
+	if (s1 == NULL || *s1 == '\0')
+		return s2;
+	if (s2 == NULL || *s2 == '\0')
+		return s1;
+	l1 = strlen(s1);
+	l2 = strlen(s2);
+	rl = l1 + l2;
+	res = (char *)xalloc(rl + 2);
+	(void) strlcpy(res, s1, rl);
+	hr = res + l1;
+	h1 = s2;
+	h = s2;
+
+	/* walk through s2 */
+	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 s1 ? */
+		if (iteminlist(h1, s1, " ") == NULL)
+		{
+			/* add space as delimiter */
+			*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;
+}
+# endif /* SASL */
+/*
 **  HELO_OPTIONS -- process the options on a HELO line.
 **
 **	Parameters:
@@ -302,7 +366,14 @@
 	register char *p;
 
 	if (firstline)
+	{
+# if SASL
+		if (mci->mci_saslcap != NULL)
+			sm_free(mci->mci_saslcap);
+		mci->mci_saslcap = NULL;
+# endif /* SASL */
 		return;
+	}
 
 	if (strlen(line) < (SIZE_T) 5)
 		return;
@@ -327,26 +398,36 @@
 		mci->mci_flags |= MCIF_DSN;
 	else if (strcasecmp(line, "enhancedstatuscodes") == 0)
 		mci->mci_flags |= MCIF_ENHSTAT;
+# if STARTTLS
+	else if (strcasecmp(line, "starttls") == 0)
+		mci->mci_flags |= MCIF_TLS;
+# endif /* STARTTLS */
 # if SASL
 	else if (strcasecmp(line, "auth") == 0)
 	{
-		if (p == NULL || *p == '\0')
-		{
-			/* no parameter? */
-			mci->mci_saslcap = NULL;
-		}
-		else
+		if (p != NULL && *p != '\0')
 		{
-			int l;
-
 			if (mci->mci_saslcap != NULL)
-				free(mci->mci_saslcap);
-			l = strlen(p) + 1;
-			mci->mci_saslcap = (char *)malloc(l);
+			{
+				char *h;
 
-			/* XXX this may be leaked */
-			if (mci->mci_saslcap != NULL)
+				/*
+				**  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_flags |= MCIF_AUTH;
+			}
+			else
 			{
+				int l;
+
+				l = strlen(p) + 1;
+				mci->mci_saslcap = (char *)xalloc(l);
 				(void) strlcpy(mci->mci_saslcap, p, l);
 				mci->mci_flags |= MCIF_AUTH;
 			}
@@ -410,7 +491,7 @@
 	{
 		if (mci->mci_sasl_string_len <= len)
 		{
-			free(mci->mci_sasl_string);
+			sm_free(mci->mci_sasl_string);
 			mci->mci_sasl_string = xalloc(len + 1);
 		}
 	}
@@ -420,7 +501,7 @@
 	memcpy(mci->mci_sasl_string, out, len);
 	mci->mci_sasl_string[len] = '\0';
 	mci->mci_sasl_string_len = len;
-	free(out);
+	sm_free(out);
 	return;
 }
 
@@ -448,7 +529,8 @@
 # define SASL_DEFREALM	4
 # define SASL_MECH	5
 
-static char *sasl_info_name[] = {
+static char *sasl_info_name[] =
+{
 	"",
 	"user id",
 	"authorization id",
@@ -561,7 +643,8 @@
 static int getsecret	__P((sasl_conn_t *, void *, int, sasl_secret_t **));
 static int saslgetrealm	__P((void *, int, const char **, const char **));
 
-static sasl_callback_t callbacks[] = {
+static sasl_callback_t callbacks[] =
+{
 	{	SASL_CB_GETREALM,	&saslgetrealm,	NULL	},
 # define CB_GETREALM_IDX	0
 	{	SASL_CB_PASS,		&getsecret,	NULL	},
@@ -605,7 +688,8 @@
 	if (result == NULL)
 		return SASL_BADPARAM;
 
-	switch (id) {
+	switch (id)
+	{
 	  case SASL_CB_USER:
 		if (user == NULL)
 		{
@@ -626,7 +710,17 @@
 			   strcasecmp(context, "CRAM-MD5") == 0;
 		if (addedrealm != addrealm && authid != NULL)
 		{
-			free(authid);
+#  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;
 		}
@@ -701,9 +795,7 @@
 		authpass = newstr(h);
 	}
 	len = strlen(authpass);
-	*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len + 1);
-	if (*psecret == NULL)
-		return SASL_FAIL;
+	*psecret = (sasl_secret_t *) xalloc(sizeof(sasl_secret_t) + len + 1);
 	(void) strlcpy((*psecret)->data, authpass, len + 1);
 	(*psecret)->len = len;
 	return SASL_OK;
@@ -741,6 +833,7 @@
 
 	if (file == NULL || *file == '\0')
 		return SASL_OK;
+
 	sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOGWFILES|SFF_NOWWFILES|SFF_ROOTOK;
 	if ((p = strrchr(file, '/')) == NULL)
 		p = file;
@@ -770,12 +863,13 @@
 	}
 # endif /* SASL <= 10515 */
 
-	if ((r = safefile(file, RunAsUid, RunAsGid, RunAsUserName, sff,
+	p = file;
+	if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
 			  S_IRUSR, NULL)) == 0)
 		return SASL_OK;
 	if (LogLevel >= 11 || (r != ENOENT && LogLevel >= 9))
 		sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
-			  file, errstring(r));
+			  p, errstring(r));
 	return SASL_CONTINUE;
 }
 
@@ -803,13 +897,13 @@
 {
 	if (LogLevel > 12)
 		sm_syslog(LOG_INFO, NOQID, "saslgetrealm: realm %s available realms %s",
-			  context,
-			  availrealms == NULL ? "<No Realms>" : *availrealms);
+			  context == NULL ? "<No Context>" : (char *) context,
+			  (availrealms == NULL || *availrealms == NULL) ? "<No Realms>" : *availrealms);
 	if (context == NULL)
 		return SASL_FAIL;
 
 	/* check whether context is in list? */
-	if (availrealms != NULL)
+	if (availrealms != NULL && *availrealms != NULL)
 	{
 		if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
 		    NULL)
@@ -952,9 +1046,7 @@
 	l1 = strlen(s1);
 	l2 = strlen(s2);
 	rl = min(l1, l2);
-	res = (char *)malloc(rl + 1);
-	if (res == NULL)
-		return NULL;
+	res = (char *)xalloc(rl + 1);
 	*res = '\0';
 	if (rl == 0)	/* at least one string empty? */
 		return res;
@@ -1027,8 +1119,12 @@
 
 	*mechused = NULL;
 	if (mci->mci_conn != NULL)
-		free(mci->mci_conn);
-	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"
@@ -1037,13 +1133,32 @@
 
 	/* 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() */
+	{
+		ssp.max_ssf = INT_MAX;
+		ssp.maxbufsize = MAXOUTLEN;
+#   if 0
+		ssp.security_flags = SASL_SEC_NOPLAINTEXT;
+#   endif /* 0 */
+	}
+#  endif /* SFIO */
 	saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
 	if (saslresult != SASL_OK)
 		return EX_TEMPFAIL;
 
-	/* external security strength factor; we have none so zero */
+	/* 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 (out != NULL && *out != '\0')
+		ssf.auth_id = out;
+	out = macvalue(macid("{cipher_bits}", NULL), e);
+	if (out != NULL && *out != '\0')
+		ssf.ssf = atoi(out);
+# endif /* _FFR_EXT_MECH */
 	saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
 	if (saslresult != SASL_OK)
 		return EX_TEMPFAIL;
@@ -1061,7 +1176,7 @@
 			return EX_TEMPFAIL;
 		addrsize = sizeof(struct sockaddr_in);
 		if (getsockname(fileno(mci->mci_out),
-				(struct sockaddr *) &saddr_l, &addrsize) != 0)
+				(struct sockaddr *) &saddr_l, &addrsize) == 0)
 		{
 			if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
 					 &saddr_l) != SASL_OK)
@@ -1079,6 +1194,13 @@
 
 	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");
+		}
+#  endif /* SFIO */
 		return EX_TEMPFAIL;
 	}
 
@@ -1113,10 +1235,12 @@
 		{
 			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 */
 			return EX_OK;
 		}
 		if (smtpresult == -1)
@@ -1163,7 +1287,7 @@
 		}
 		else
 			in64[0] = '\0';
-		smtpmessage(in64, m, mci);
+		smtpmessage("%s", m, mci, in64);
 		smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
 				   getsasldata, NULL);
 		/* which timeout? XXX */
@@ -1227,7 +1351,8 @@
 	result = sasl_client_init(callbacks);
 	if (result != SASL_OK)
 		return EX_TEMPFAIL;
-	do {
+	do
+	{
 		result = attemptauth(m, mci, e, &mechused);
 		if (result == EX_OK)
 			mci->mci_sasl_auth = TRUE;
@@ -1392,7 +1517,7 @@
 	else
 	{
 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
-			*bufp == '@' ? ',' : ':', bufp, optbuf);
+			    *bufp == '@' ? ',' : ':', bufp, optbuf);
 	}
 	SmtpPhase = mci->mci_phase = "client MAIL";
 	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
@@ -1406,7 +1531,7 @@
 		smtpquit(m, mci, e);
 		return EX_TEMPFAIL;
 	}
-	else if (r == 421)
+	else if (r == SMTPCLOSING)
 	{
 		/* service shutting down */
 		mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
@@ -1613,6 +1738,7 @@
 */
 
 static jmp_buf	CtxDataTimeout;
+static EVENT	*volatile DataTimeout = NULL;
 
 int
 smtpdata(m, mci, e)
@@ -1621,13 +1747,13 @@
 	register ENVELOPE *e;
 {
 	register int r;
-	register EVENT *ev;
 	int rstat;
 	int xstat;
 	time_t timeout;
 	char *enhsc;
 
 	enhsc = NULL;
+
 	/*
 	**  Send the data.
 	**	First send the command and check that it is ok.
@@ -1702,26 +1828,29 @@
 	else
 		timeout = DATA_PROGRESS_TIMEOUT;
 
-	ev = setevent(timeout, datatimeout, 0);
+	DataTimeout = setevent(timeout, datatimeout, 0);
 
-	if (tTd(18, 101))
-	{
-		/* simulate a DATA timeout */
-		(void) sleep(1);
-	}
 
 	/*
 	**  Output the actual message.
 	*/
 
 	(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
+
+	if (tTd(18, 101))
+	{
+		/* simulate a DATA timeout */
+		(void) sleep(2);
+	}
+
 	(*e->e_putbody)(mci, e, NULL);
 
 	/*
 	**  Cleanup after sending message.
 	*/
 
-	clrevent(ev);
+	if (DataTimeout != NULL)
+		clrevent(DataTimeout);
 
 # if _FFR_CATCH_BROKEN_MTAS
 	{
@@ -1799,13 +1928,15 @@
 	mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
 		    SmtpReplyBuffer);
 	if (e->e_statmsg != NULL)
-		free(e->e_statmsg);
+		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]);
+	SmtpPhase = mci->mci_phase = "idle";
+	sm_setproctitle(TRUE, e, "%s: %s", CurHostName, mci->mci_phase);
 	if (rstat != EX_PROTOCOL)
 		return rstat;
 	if (LogLevel > 1)
@@ -1822,10 +1953,17 @@
 static void
 datatimeout()
 {
+	int save_errno = errno;
+
+	/*
+	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
+	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+	**	DOING.
+	*/
+
 	if (DataProgress)
 	{
 		time_t timeout;
-		register EVENT *ev;
 
 		/* check back again later */
 		if (tTd(18, 101))
@@ -1836,14 +1974,24 @@
 		else
 			timeout = DATA_PROGRESS_TIMEOUT;
 
+		/* reset the timeout */
+		DataTimeout = sigsafe_setevent(timeout, datatimeout, 0);
 		DataProgress = FALSE;
-		ev = setevent(timeout, datatimeout, 0);
 	}
 	else
+	{
+		/* event is done */
+		DataTimeout = NULL;
+	}
+
+	/* if no progress was made or problem resetting event, die now */
+	if (DataTimeout == NULL)
 	{
-		/* no progress, give up */
+		errno = ETIMEDOUT;
 		longjmp(CtxDataTimeout, 1);
 	}
+
+	errno = save_errno;
 }
 /*
 **  SMTPGETSTAT -- get status code from DATA in LMTP
@@ -1886,7 +2034,7 @@
 	else
 		status = EX_PROTOCOL;
 	if (e->e_statmsg != NULL)
-		free(e->e_statmsg);
+		sm_free(e->e_statmsg);
 	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
 	    (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
 		r += 5;
@@ -1969,7 +2117,7 @@
 
 		/* look for naughty mailers */
 		sm_syslog(LOG_ERR, e->e_id,
-			  "smtpquit: mailer%s%s exited with exit value %d\n",
+			  "smtpquit: mailer%s%s exited with exit value %d",
 			  mailer == NULL ? "" : " ",
 			  mailer == NULL ? "" : mailer,
 			  rcode);
@@ -1979,6 +2127,17 @@
 }
 /*
 **  SMTPRSET -- send a RSET (reset) command
+**
+**	Parameters:
+**		m -- a pointer to the mailer.
+**		mci -- the mailer connection information.
+**		e -- the current envelope.
+**
+**	Returns:
+**		none.
+**
+**	Side Effects:
+**		closes the connection if there is no reply to RSET.
 */
 
 void
@@ -2004,15 +2163,28 @@
 		**  Any response is deemed to be acceptable.
 		**  The standard does not state the proper action
 		**  to take when a value other than 250 is received.
+		**
+		**  However, if 421 is returned for the RSET, leave
+		**  mci_state as MCIS_SSD (set in reply()).
 		*/
 
-		mci->mci_state = MCIS_OPEN;
+		if (mci->mci_state != MCIS_SSD)
+			mci->mci_state = MCIS_OPEN;
 		return;
 	}
 	smtpquit(m, mci, e);
 }
 /*
 **  SMTPPROBE -- check the connection state
+**
+**	Parameters:
+**		mci -- the mailer connection information.
+**
+**	Returns:
+**		none.
+**
+**	Side Effects:
+**		closes the connection if there is no reply to RSET.
 */
 
 int
Index: gnu/usr.sbin/sendmail/sendmail/util.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/util.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/sendmail/util.c	2000/04/07 19:20:45	1.2
+++ gnu/usr.sbin/sendmail/sendmail/util.c	2001/05/29 01:31:16	1.5
@@ -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
@@ -12,12 +12,13 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: util.c,v 8.225 2000/03/28 21:55:22 ca Exp $";
+static char id[] = "@(#)$Sendmail: util.c,v 8.225.2.1.2.23 2001/05/17 18:10:18 gshapiro Exp $";
 #endif /* ! lint */
 
 #include <sendmail.h>
 #include <sysexits.h>
 
+
 static void	readtimeout __P((time_t));
 
 /*
@@ -208,6 +209,7 @@
 	**  If have to rebalance an already short enough string,
 	**  need to do it within allocated space.
 	*/
+
 	slen = strlen(string);
 	if (length == 0 || slen < length)
 		length = slen;
@@ -239,7 +241,7 @@
 
 increment:
 		/* Check for sufficient space for next character */
-		if (length - (ptr - string) <= ((backslash ? 1 : 0) +
+		if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) +
 						parencount +
 						(quoted ? 1 : 0)))
 		{
@@ -376,15 +378,122 @@
 	if (sz <= 0)
 		sz = 1;
 
+	ENTER_CRITICAL();
 	p = malloc((unsigned) sz);
+	LEAVE_CRITICAL();
 	if (p == NULL)
 	{
 		syserr("!Out of memory!!");
-		/* exit(EX_UNAVAILABLE); */
+
+		/* NOTREACHED */
+		exit(EX_UNAVAILABLE);
 	}
 	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;
+}
+/*
+**  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?
+**
+**	Parameters:
+**		num -- number of items to allocate
+**		sz -- size of new area to allocate.
+**
+**	Returns:
+**		pointer to data region.
+**
+**	Side Effects:
+**		Memory is allocated.
+*/
+
+char *
+xcalloc(num, sz)
+	size_t num;
+	size_t sz;
+{
+	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();
+	if (p == NULL)
+	{
+		syserr("!Out of memory!!");
+
+		/* NOTREACHED */
+		exit(EX_UNAVAILABLE);
+	}
+	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
@@ -495,14 +604,18 @@
 	pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, 0644, sff);
 	if (pidf == NULL)
 	{
-		sm_syslog(LOG_ERR, NOQID, "unable to write %s", pidpath);
+		sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s",
+			  pidpath, errstring(errno));
 	}
 	else
 	{
+		pid_t pid;
 		extern char *CommandLineArgs;
 
+		pid = getpid();
+
 		/* write the process id on line 1 */
-		fprintf(pidf, "%ld\n", (long) getpid());
+		fprintf(pidf, "%ld\n", (long) pid);
 
 		/* line 2 contains all command line flags */
 		fprintf(pidf, "%s\n", CommandLineArgs);
@@ -639,7 +752,7 @@
 				if (strchr("=~&?", *s) != NULL)
 					(void) putchar(*s++);
 				if (bitset(0200, *s))
-					printf("{%s}", macname(*s++ & 0377));
+					printf("{%s}", macname(bitidx(*s++)));
 				else
 					printf("%c", *s++);
 				continue;
@@ -917,6 +1030,11 @@
 			{
 				if (putc('.', mci->mci_out) == EOF)
 					dead = TRUE;
+				else
+				{
+					/* record progress for DATA timeout */
+					DataProgress = TRUE;
+				}
 				if (TrafficLogFile != NULL)
 					(void) putc('.', TrafficLogFile);
 			}
@@ -927,6 +1045,11 @@
 			{
 				if (putc('>', mci->mci_out) == EOF)
 					dead = TRUE;
+				else
+				{
+					/* record progress for DATA timeout */
+					DataProgress = TRUE;
+				}
 				if (TrafficLogFile != NULL)
 					(void) putc('>', TrafficLogFile);
 			}
@@ -935,14 +1058,17 @@
 
 			while (l < q)
 			{
-				if (putc(*l++, mci->mci_out) == EOF)
+				if (putc((unsigned char) *l++, mci->mci_out) ==
+				    EOF)
 				{
 					dead = TRUE;
 					break;
 				}
-
-				/* record progress for DATA timeout */
-				DataProgress = TRUE;
+				else
+				{
+					/* record progress for DATA timeout */
+					DataProgress = TRUE;
+				}
 			}
 			if (dead)
 				break;
@@ -955,14 +1081,16 @@
 				dead = TRUE;
 				break;
 			}
-
-			/* record progress for DATA timeout */
-			DataProgress = TRUE;
-
+			else
+			{
+				/* record progress for DATA timeout */
+				DataProgress = TRUE;
+			}
 			if (TrafficLogFile != NULL)
 			{
 				for (l = l_base; l < q; l++)
-					(void) putc(*l, TrafficLogFile);
+					(void) putc((unsigned char)*l,
+						    TrafficLogFile);
 				fprintf(TrafficLogFile, "!\n%05d >>>  ",
 					(int) getpid());
 			}
@@ -978,6 +1106,11 @@
 		{
 			if (putc('.', mci->mci_out) == EOF)
 				break;
+			else
+			{
+				/* record progress for DATA timeout */
+				DataProgress = TRUE;
+			}
 			if (TrafficLogFile != NULL)
 				(void) putc('.', TrafficLogFile);
 		}
@@ -988,21 +1121,28 @@
 		{
 			if (putc('>', mci->mci_out) == EOF)
 				break;
+			else
+			{
+				/* record progress for DATA timeout */
+				DataProgress = TRUE;
+			}
 			if (TrafficLogFile != NULL)
 				(void) putc('>', TrafficLogFile);
 		}
 		for ( ; l < p; ++l)
 		{
 			if (TrafficLogFile != NULL)
-				(void) putc(*l, TrafficLogFile);
-			if (putc(*l, mci->mci_out) == EOF)
+				(void) putc((unsigned char)*l, TrafficLogFile);
+			if (putc((unsigned char) *l, mci->mci_out) == EOF)
 			{
 				dead = TRUE;
 				break;
 			}
-
-			/* record progress for DATA timeout */
-			DataProgress = TRUE;
+			else
+			{
+				/* record progress for DATA timeout */
+				DataProgress = TRUE;
+			}
 		}
 		if (dead)
 			break;
@@ -1011,6 +1151,11 @@
 			(void) putc('\n', TrafficLogFile);
 		if (fputs(mci->mci_mailer->m_eol, mci->mci_out) == EOF)
 			break;
+		else
+		{
+			/* record progress for DATA timeout */
+			DataProgress = TRUE;
+		}
 		if (l < end && *l == '\n')
 		{
 			if (*++l != ' ' && *l != '\t' && *l != '\0' &&
@@ -1018,13 +1163,15 @@
 			{
 				if (putc(' ', mci->mci_out) == EOF)
 					break;
+				else
+				{
+					/* record progress for DATA timeout */
+					DataProgress = TRUE;
+				}
 				if (TrafficLogFile != NULL)
 					(void) putc(' ', TrafficLogFile);
 			}
 		}
-
-		/* record progress for DATA timeout */
-		DataProgress = TRUE;
 	} while (l < end);
 }
 /*
@@ -1076,6 +1223,7 @@
 **		none.
 */
 
+
 static jmp_buf	CtxReadTimeout;
 
 char *
@@ -1171,6 +1319,13 @@
 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);
 }
 /*
@@ -1230,7 +1385,7 @@
 			memmove(nbp, bp, p - bp);
 			p = &nbp[p - bp];
 			if (bp != buf)
-				free(bp);
+				sm_free(bp);
 			bp = nbp;
 			n = nn - (p - bp);
 		}
@@ -1343,8 +1498,10 @@
 	int i;
 
 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
+	{
 		if ((a[i] & b[i]) != 0)
 			return TRUE;
+	}
 	return FALSE;
 }
 /*
@@ -1368,8 +1525,10 @@
 	int i;
 
 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
+	{
 		if (map[i] != 0)
 			return FALSE;
+	}
 	return TRUE;
 }
 /*
@@ -1481,8 +1640,8 @@
 	static BITMAP256 baseline;
 	extern int DtableSize;
 
-	if (DtableSize > 256)
-		maxfd = 256;
+	if (DtableSize > BITMAPBITS)
+		maxfd = BITMAPBITS;
 	else
 		maxfd = DtableSize;
 	if (where == NULL)
@@ -1741,10 +1900,10 @@
 **		host -- the host to shorten (stripped in place).
 **
 **	Returns:
-**		none.
+**		place where string was trunacted, NULL if not truncated.
 */
 
-void
+char *
 shorten_hostname(host)
 	char host[];
 {
@@ -1764,7 +1923,7 @@
 	/* see if there is any domain at all -- if not, we are done */
 	p = strchr(host, '.');
 	if (p == NULL)
-		return;
+		return NULL;
 
 	/* yes, we have a domain -- see if it looks like us */
 	mydom = macvalue('m', CurEnv);
@@ -1773,7 +1932,11 @@
 	i = strlen(++p);
 	if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 &&
 	    (mydom[i] == '.' || mydom[i] == '\0'))
+	{
 		*--p = '\0';
+		return p;
+	}
+	return NULL;
 }
 /*
 **  PROG_OPEN -- open a program for reading
@@ -1787,13 +1950,13 @@
 **		pid of the process -- -1 if it failed.
 */
 
-int
+pid_t
 prog_open(argv, pfd, e)
 	char **argv;
 	int *pfd;
 	ENVELOPE *e;
 {
-	int pid;
+	pid_t pid;
 	int i;
 	int save_errno;
 	int fdv[2];
@@ -1825,6 +1988,11 @@
 	/* child -- close stdin */
 	(void) close(0);
 
+	/* Reset global flags */
+	RestartRequest = NULL;
+	ShutdownRequest = NULL;
+	PendingSignal = 0;
+
 	/* stdout goes back to parent */
 	(void) close(fdv[0]);
 	if (dup2(fdv[1], 1) < 0)
@@ -1856,17 +2024,29 @@
 	{
 		expand(ProgMailer->m_rootdir, buf, sizeof buf, e);
 		if (chroot(buf) < 0)
+		{
 			syserr("prog_open: cannot chroot(%s)", buf);
+			exit(EX_TEMPFAIL);
+		}
 		if (chdir("/") < 0)
+		{
 			syserr("prog_open: cannot chdir(/)");
+			exit(EX_TEMPFAIL);
+		}
 	}
 
 	/* run as default user */
 	endpwent();
 	if (setgid(DefGid) < 0 && geteuid() == 0)
+	{
 		syserr("prog_open: setgid(%ld) failed", (long) DefGid);
+		exit(EX_TEMPFAIL);
+	}
 	if (setuid(DefUid) < 0 && geteuid() == 0)
+	{
 		syserr("prog_open: setuid(%ld) failed", (long) DefUid);
+		exit(EX_TEMPFAIL);
+	}
 
 	/* run in some directory */
 	if (ProgMailer != NULL)
@@ -2058,7 +2238,7 @@
 	{
 		/* allocate more space */
 		if (bp != NULL)
-			free(bp);
+			sm_free(bp);
 		bp = xalloc(l);
 		bl = l;
 	}
@@ -2138,7 +2318,7 @@
 **		none
 */
 
-static struct procs	*ProcListVec = NULL;
+static struct procs	*volatile ProcListVec = NULL;
 static int		ProcListSize = 0;
 
 void
@@ -2177,7 +2357,7 @@
 		{
 			memmove(npv, ProcListVec,
 				ProcListSize * sizeof (struct procs));
-			free(ProcListVec);
+			sm_free(ProcListVec);
 		}
 		for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
 		{
@@ -2191,7 +2371,7 @@
 	}
 	ProcListVec[i].proc_pid = pid;
 	if (ProcListVec[i].proc_task != NULL)
-		free(ProcListVec[i].proc_task);
+		sm_free(ProcListVec[i].proc_task);
 	ProcListVec[i].proc_task = newstr(task);
 	ProcListVec[i].proc_type = type;
 
@@ -2222,7 +2402,7 @@
 		if (ProcListVec[i].proc_pid == pid)
 		{
 			if (ProcListVec[i].proc_task != NULL)
-				free(ProcListVec[i].proc_task);
+				sm_free(ProcListVec[i].proc_task);
 			ProcListVec[i].proc_task = newstr(task);
 			break;
 		}
@@ -2236,6 +2416,10 @@
 **
 **	Returns:
 **		type of process
+**
+**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
+**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+**		DOING.
 */
 
 int
@@ -2256,6 +2440,8 @@
 	}
 	if (CurChildren > 0)
 		CurChildren--;
+
+
 	return type;
 }
 /*
Index: gnu/usr.sbin/sendmail/sendmail/version.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/sendmail/version.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/sendmail/version.c	2000/04/07 19:20:45	1.2
+++ gnu/usr.sbin/sendmail/sendmail/version.c	2001/05/29 01:31:16	1.5
@@ -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 Eric P. Allman.  All rights reserved.
  * Copyright (c) 1988, 1993
@@ -12,7 +12,7 @@
  */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: version.c,v 8.43 2000/04/06 20:30:54 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: version.c,v 8.43.4.33 2001/05/27 21:39:21 gshapiro Exp $";
 #endif /* ! lint */
 
-char	Version[] = "8.10.1";
+char	Version[] = "8.11.4";
Index: gnu/usr.sbin/sendmail/smrsh/README
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/smrsh/README,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- gnu/usr.sbin/sendmail/smrsh/README	2000/04/02 19:48:36	1.2
+++ gnu/usr.sbin/sendmail/smrsh/README	2001/01/15 21:09:11	1.3
@@ -75,8 +75,8 @@
 perl(1), uudecode(1) or the stream editor sed(1) in your list of
 acceptable commands.
 
-
-You will next need to create the directory /usr/adm/sm.bin and populate
+If your platform doesn't have a default 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
 code for smrsh, so changing this directory must be accompanied with
@@ -153,4 +153,4 @@
 	host.domain# /usr/sbin/sendmail -bd -q30m
 
 
-$Revision: 1.2 $, Last updated $Date: 2000/04/02 19:48:36 $
+$Revision: 1.3 $, Last updated $Date: 2001/01/15 21:09:11 $
Index: gnu/usr.sbin/sendmail/smrsh/smrsh.8
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/smrsh/smrsh.8,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/smrsh/smrsh.8	2000/04/11 07:31:32	1.2
+++ gnu/usr.sbin/sendmail/smrsh/smrsh.8	2001/01/17 05:26:51	1.5
@@ -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) 1993 Eric P. Allman.  All rights reserved.
 .\" Copyright (c) 1993
@@ -9,80 +9,111 @@
 .\" the sendmail distribution.
 .\"
 .\"
-.\"     $Sendmail: smrsh.8,v 8.11 1999/06/09 16:51:07 ca Exp $
-.\"	$OpenBSD: smrsh.8,v 1.2 2000/04/11 07:31:32 form Exp $
+.\"     $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 $
 .\"
-.TH SMRSH 8 11/02/93
-.SH NAME
-smrsh \- restricted shell for sendmail
-.SH SYNOPSIS
-.B smrsh
-.B \-c
-command
-.SH DESCRIPTION
+.Dd December 15, 2000
+.Dt SMRSH 8
+.Os
+.Sh NAME
+.Nm smrsh
+.Nd restricted shell for sendmail
+.Sh SYNOPSIS
+.Nm smrsh
+.Fl c Ar command
+.Sh DESCRIPTION
 The
-.I smrsh
+.Nm smrsh
 program is intended as a replacement for
-.I sh
-for use in the ``prog'' mailer in
-.IR sendmail (8)
+.Pa /bin/sh
+for use in the
+.Dq prog
+mailer in
+.Xr sendmail 8
 configuration files.
 It sharply limits the commands that can be run using the
-``|program'' syntax of
-.I sendmail
-in order to improve the over all security of your system.
-Briefly, even if a ``bad guy'' can get sendmail to run a program
+.Dq |program
+syntax of
+.Xr sendmail 8
+in order to improve the overall security of your system.
+Briefly, even if a
+.Dq bad guy
+can get sendmail to run a program
 without going through an alias or forward file,
-.I smrsh
+.Nm smrsh
 limits the set of programs that he or she can execute.
-.PP
+.Pp
 Briefly,
-.I smrsh
-limits programs to be in the directory
-/usr/libexec/sm.bin,
+.Nm smrsh
+limits programs to be in a single directory, by default
+.Pa /usr/libexec/sm.bin ,
 allowing the system administrator to choose the set of acceptable commands,
-and to the shell builtin commands ``exec'', ``exit'', and ``echo''.
+and the shell builtin commands
+.Dq exec ,
+.Dq exit ,
+and
+.Dq echo .
 It also rejects any commands with the characters
-`\`', `<', `>', `;', `$', `(', `)', `\er' (carriage return),
-or `\en' (newline)
-on the command line to prevent ``end run'' attacks.
-It allows ``||'' and ``&&'' to enable commands like:
-``"|exec /usr/local/bin/procmail -f- /etc/procmailrcs/user || exit 75"''
-.PP
+.Sq \e ,
+.Sq < ,
+.Sq > ,
+.So
+;
+.Sc ,
+.Sq $ ,
+.So
+(
+.Sc ,
+.So
+)
+.Sc ,
+.Sq \er
+(carriage return), or
+.Sq \en
+(newline) on the command line to prevent
+.Dq end run
+attacks.
+It allows
+.Dq ||
+and
+.Dq &&
+to enable commands like:
+.Bd -literal -compact -offset "XXXX"
+.Qq "|exec /usr/local/bin/procmail -f- /etc/procmailrcs/user || exit 75"
+.Ed
+.Pp
 Initial pathnames on programs are stripped,
-so forwarding to ``/usr/ucb/vacation'',
-``/usr/bin/vacation'',
-``/home/server/mydir/bin/vacation'',
+so forwarding to
+.Pa /usr/ucb/vacation ,
+.Pa /usr/bin/vacation ,
+.Pa /home/server/mydir/bin/vacation ,
 and
-``vacation''
+.Pa vacation
 all actually forward to
-``/usr/libexec/sm.bin/vacation''.
-.PP
+.Pa /usr/libexec/sm.bin/vacation .
+.Pp
 System administrators should be conservative about populating
-/usr/libexec/sm.bin.
+the sm.bin directory.
 Reasonable additions are
-.IR vacation (1),
-.IR procmail (1),
+.Xr vacation 1 ,
+.Xr procmail 1 ,
 and the like.
 No matter how brow-beaten you may be,
 never include any shell or shell-like program
 (such as
-.IR perl (1))
+.Xr perl 1 )
 in the
 sm.bin
 directory.
 Note that this does not restrict the use of shell or perl scripts
-in the sm.bin directory (using the ``#!'' syntax);
+in the sm.bin directory (using the
+.Dq #!
+syntax);
 it simply disallows execution of arbitrary programs.
-.SH COMPILATION
-Compilation should be trivial on most systems.
-You may need to use \-DPATH=\e"\fIpath\fP\e"
-to adjust the default search path
-(defaults to ``/bin:/usr/bin'')
-and/or \-DCMDBIN=\e"\fIdir\fP\e"
-to change the default program directory
-(defaults to ``/usr/libexec/sm.bin'').
-.SH FILES
-/usr/libexec/sm.bin \- directory for restricted programs
-.SH SEE ALSO
-sendmail(8)
+.Sh FILES
+.Bl -tag -width "/usr/libexec/sm.bin" -compact
+.It Pa /usr/libexec/sm.bin
+directory for restricted programs
+.El
+.Sh SEE ALSO
+.Xr sendmail 8
Index: gnu/usr.sbin/sendmail/smrsh/smrsh.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/smrsh/smrsh.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/smrsh/smrsh.c	2000/04/07 19:20:46	1.2
+++ gnu/usr.sbin/sendmail/smrsh/smrsh.c	2001/05/29 01:31:17	1.5
@@ -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) 1993 Eric P. Allman.  All rights reserved.
  * Copyright (c) 1993
@@ -13,7 +13,7 @@
 
 #ifndef lint
 static char copyright[] =
-"@(#) Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.\n\
+"@(#) 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\
@@ -21,7 +21,7 @@
 #endif /* ! lint */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: smrsh.c,v 8.31 2000/03/17 07:32:49 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: smrsh.c,v 8.31.4.9 2001/04/24 04:11:51 ca Exp $";
 #endif /* ! lint */
 
 /*
@@ -74,7 +74,11 @@
 
 /* directory in which all commands must reside */
 #ifndef CMDDIR
-# define CMDDIR		"/usr/adm/sm.bin"
+# if defined(HPUX10) || defined(HPUX11) || SOLARIS >= 20800
+#  define CMDDIR	"/var/adm/sm.bin"
+# else /* HPUX10 || HPUX11 || SOLARIS >= 20800 */
+#  define CMDDIR	"/usr/adm/sm.bin"
+# endif /* HPUX10 || HPUX11 || SOLARIS >= 20800 */
 #endif /* ! CMDDIR */
 
 /* characters disallowed in the shell "-c" argument */
@@ -169,7 +173,6 @@
 	*/
 
 	prg = argv[0];
-	par = argv[2];
 
 	if (argc != 3 || strcmp(argv[1], "-c") != 0)
 	{
@@ -179,6 +182,8 @@
 #endif /* ! DEBUG */
 		exit(EX_USAGE);
 	}
+
+	par = argv[2];
 
 	/*
 	**  Disallow special shell syntax.  This is overly restrictive,
Index: gnu/usr.sbin/sendmail/vacation/vacation.1
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/vacation/vacation.1,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- gnu/usr.sbin/sendmail/vacation/vacation.1	2000/04/07 19:20:47	1.3
+++ gnu/usr.sbin/sendmail/vacation/vacation.1	2001/01/15 21:09:12	1.4
@@ -9,19 +9,19 @@
 .\" the sendmail distribution.
 .\"
 .\"
-.\"	$Sendmail: vacation.1,v 8.9 1999/10/27 03:42:07 ca Exp $
+.\"	$Sendmail: vacation.1,v 8.11.4.6 2000/12/29 18:12:23 gshapiro Exp $
 .\"
-.TH VACATION 1 "$Date: 2000/04/07 19:20:47 $"
+.TH VACATION 1 "$Date: 2001/01/15 21:09:12 $"
 .SH NAME
-.B vacation
+vacation
 \- return ``I am not here'' indication
 .SH SYNOPSIS
 .B vacation
-.B \-i
+.RB [ \-i ]
+.RB [ \-I ]
 .RB [ \-r 
 .IR interval ]
 .RB [ \-x ]
-.B vacation
 .RB [ \-a
 .IR alias ]
 .RB [ \-f
@@ -74,6 +74,11 @@
 .I .forward
 file.
 .TP
+.B \-I
+Same as
+.B \-i
+(for backwards compatibility).
+.TP
 .BI \-m " filename"
 Use
 .I filename
@@ -95,9 +100,9 @@
 .BI \-s " address"
 Use
 .I address
-instead of the sender address in the
+instead of the incoming message sender address on the
 .I From 
-line to determine the reply address.
+line as the recipient for the vacation message.
 .TP
 .BI \-t " time"
 Ignored, available only for compatibility with Sun's
@@ -132,6 +137,9 @@
 headers of the mail.
 No messages from
 ``???-REQUEST'',
+``???-RELAY'',
+``???-OWNER'',
+``OWNER-???'',
 ``Postmaster'',
 ``UUCP'',
 ``MAILER'',
@@ -186,10 +194,10 @@
 .SH FILES
 .TP 1.8i
 ~/.vacation.db
-database file
+default database file
 .TP
 ~/.vacation.msg
-message to send
+default message to send
 .SH SEE ALSO
 sendmail(8),
 syslog(8)
Index: gnu/usr.sbin/sendmail/vacation/vacation.c
===================================================================
RCS file: /cvs/src/gnu/usr.sbin/sendmail/vacation/vacation.c,v
retrieving revision 1.2
retrieving revision 1.5
diff -u -r1.2 -r1.5
--- gnu/usr.sbin/sendmail/vacation/vacation.c	2000/04/07 19:20:47	1.2
+++ gnu/usr.sbin/sendmail/vacation/vacation.c	2001/05/29 01:31:17	1.5
@@ -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.
  * Copyright (c) 1983, 1987, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -13,7 +13,7 @@
 
 #ifndef lint
 static char copyright[] =
-"@(#) Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.\n\
+"@(#) 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\
@@ -21,9 +21,10 @@
 #endif /* ! lint */
 
 #ifndef lint
-static char id[] = "@(#)$Sendmail: vacation.c,v 8.68 2000/03/17 07:32:51 gshapiro Exp $";
+static char id[] = "@(#)$Sendmail: vacation.c,v 8.68.4.21 2001/05/07 22:06:41 gshapiro Exp $";
 #endif /* ! lint */
 
+
 #include <ctype.h>
 #include <stdlib.h>
 #include <syslog.h>
@@ -48,6 +49,11 @@
 #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;
@@ -73,11 +79,6 @@
 #define SECSPERDAY	(60 * 60 * 24)
 #define DAYSPERWEEK	7
 
-#ifndef TRUE
-# define TRUE	1
-# define FALSE	0
-#endif /* ! TRUE */
-
 #ifndef __P
 # ifdef __STDC__
 #  define __P(protos)	protos
@@ -99,19 +100,42 @@
 
 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 */
+
+static void eatmsg __P((void));
+
+/* exit after reading input */
+#define EXITIT(excode)	{ \
+				eatmsg(); \
+				return excode; \
+			}
+
 int
 main(argc, argv)
 	int argc;
 	char **argv;
 {
 	bool iflag, emptysender, exclude;
+#if _FFR_BLACKBOX
+	bool runasuser = FALSE;
+#endif /* _FFR_BLACKBOX */
+#if _FFR_LISTDB
+	bool lflag = FALSE;
+#endif /* _FFR_LISTDB */
+	int mfail = 0, ufail = 0;
 	int ch;
 	int result;
+	long sff;
 	time_t interval;
 	struct passwd *pw;
 	ALIAS *cur;
-	char *dbfilename = VDB;
-	char *msgfilename = VMSG;
+	char *dbfilename = NULL;
+	char *msgfilename = NULL;
 	char *name;
 	SMDB_USER_INFO user_info;
 	static char rnamebuf[MAXNAME];
@@ -119,11 +143,24 @@
 	extern char *optarg;
 	extern void usage __P((void));
 	extern void setinterval __P((time_t));
-	extern void readheaders __P((void));
+	extern int readheaders __P((void));
 	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 */
 
 	/* Vars needed to link with smutil */
 	clrbitmap(DontBlameSendmail);
@@ -141,11 +178,11 @@
 			 "Unknown UID %d", (int) RealUid);
 	RunAsUserName = RealUserName = rnamebuf;
 
-#ifdef LOG_MAIL
+# ifdef LOG_MAIL
 	openlog("vacation", LOG_PID, LOG_MAIL);
-#else /* LOG_MAIL */
+# else /* LOG_MAIL */
 	openlog("vacation", LOG_PID);
-#endif /* LOG_MAIL */
+# endif /* LOG_MAIL */
 
 	opterr = 0;
 	iflag = FALSE;
@@ -153,7 +190,11 @@
 	exclude = FALSE;
 	interval = INTERVAL_UNDEF;
 	*From = '\0';
-	while ((ch = getopt(argc, argv, "a:f:Iim:r:s:t:xz")) != -1)
+
+#define OPTIONS		"a:df:Iilm:r:s:t:Uxz"
+
+	while (mfail == 0 && ufail == 0 &&
+	       (ch = getopt(argc, argv, OPTIONS)) != -1)
 	{
 		switch((char)ch)
 		{
@@ -161,9 +202,7 @@
 			cur = (ALIAS *)malloc((u_int)sizeof(ALIAS));
 			if (cur == NULL)
 			{
-				syslog(LOG_NOTICE,
-				       "vacation: can't allocate memory for alias %s.\n",
-				       optarg);
+				mfail++;
 				break;
 			}
 			cur->name = optarg;
@@ -171,6 +210,12 @@
 			Names = cur;
 			break;
 
+#if _FFR_DEBUG
+		  case 'd':			/* debug mode */
+			msglog = &debuglog;
+			break;
+#endif /* _FFR_DEBUG */
+
 		  case 'f':		/* alternate database */
 			dbfilename = optarg;
 			break;
@@ -180,6 +225,12 @@
 			iflag = TRUE;
 			break;
 
+#if _FFR_LISTDB
+		  case 'l':
+			lflag = TRUE;		/* list the database */
+			break;
+#endif /* _FFR_LISTDB */
+
 		  case 'm':		/* alternate message file */
 			msgfilename = optarg;
 			break;
@@ -189,7 +240,7 @@
 			{
 				interval = atol(optarg) * SECSPERDAY;
 				if (interval < 0)
-					usage();
+					ufail++;
 			}
 			else
 				interval = ONLY_ONCE;
@@ -202,6 +253,12 @@
 		  case 't':		/* SunOS: -t1d (default expire) */
 			break;
 
+#if _FFR_BLACKBOX
+		  case 'U':		/* run as single user mode */
+			runasuser = TRUE;
+			break;
+#endif /* _FFR_BLACKBOX */
+
 		  case 'x':
 			exclude = TRUE;
 			break;
@@ -212,102 +269,167 @@
 
 		  case '?':
 		  default:
-			usage();
+			ufail++;
 			break;
 		}
 	}
 	argc -= optind;
 	argv += optind;
 
+	if (mfail != 0)
+	{
+		msglog(LOG_NOTICE,
+		       "vacation: can't allocate memory for alias.\n");
+		EXITM(EX_TEMPFAIL);
+	}
+	if (ufail != 0)
+		usage();
+
 	if (argc != 1)
 	{
-		if (!iflag && !exclude)
+		if (!iflag &&
+#if _FFR_LISTDB
+		    !lflag &&
+#endif /* _FFR_LISTDB */
+		    !exclude)
 			usage();
 		if ((pw = getpwuid(getuid())) == NULL)
 		{
-			syslog(LOG_ERR,
+			msglog(LOG_ERR,
 			       "vacation: no such user uid %u.\n", getuid());
-			exit(EX_NOUSER);
+			EXITM(EX_NOUSER);
+		}
+		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);
+		if (chdir(pw->pw_dir) != 0)
+		{
+			msglog(LOG_NOTICE, "vacation: no such directory %s.\n",
+			       pw->pw_dir);
+			EXITM(EX_NOINPUT);
 		}
 	}
 #if _FFR_BLACKBOX
-	name = *argv;
-#else /* _FFR_BLACKBOX */
+	else if (runasuser)
+	{
+		name = *argv;
+		if (dbfilename == NULL || msgfilename == NULL)
+		{
+			msglog(LOG_NOTICE,
+			       "vacation: -U requires setting both -f and -m\n");
+			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,
+			       SMDB_MAX_USER_NAME_LEN);
+	}
+#endif /* _FFR_BLACKBOX */
 	else if ((pw = getpwnam(*argv)) == NULL)
 	{
-		syslog(LOG_ERR, "vacation: no such user %s.\n", *argv);
-		exit(EX_NOUSER);
+		msglog(LOG_ERR, "vacation: no such user %s.\n", *argv);
+		EXITM(EX_NOUSER);
 	}
-	name = pw->pw_name;
-	if (chdir(pw->pw_dir) != 0)
+	else
 	{
-		syslog(LOG_NOTICE,
-		       "vacation: no such directory %s.\n", pw->pw_dir);
-		exit(EX_NOINPUT);
+		name = pw->pw_name;
+		if (chdir(pw->pw_dir) != 0)
+		{
+			msglog(LOG_NOTICE, "vacation: no such directory %s.\n",
+			       pw->pw_dir);
+			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,
+			       SMDB_MAX_USER_NAME_LEN);
 	}
+
+	if (dbfilename == NULL)
+		dbfilename = VDB;
+	if (msgfilename == NULL)
+		msgfilename = VMSG;
+
+	sff = SFF_CREAT;
+#if _FFR_BLACKBOX
+	if (getegid() != getgid())
+	{
+		/* Allow a set-group-id vacation binary */
+		RunAsGid = user_info.smdbu_group_id = getegid();
+		sff |= SFF_NOPATHCHECK|SFF_OPENASROOT;
+	}
 #endif /* _FFR_BLACKBOX */
-	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);
 
 	result = smdb_open_database(&Db, dbfilename,
 				    O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0),
-				    S_IRUSR|S_IWUSR, SFF_CREAT,
+				    S_IRUSR|S_IWUSR, sff,
 				    SMDB_TYPE_DEFAULT, &user_info, NULL);
 	if (result != SMDBE_OK)
 	{
-		syslog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename,
+		msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename,
 		       errstring(result));
-		exit(EX_DATAERR);
+		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);
 
-	if (iflag)
+	if (iflag && !exclude)
 	{
-		result = Db->smdb_close(Db);
-		if (!exclude)
-			exit(EX_OK);
+		(void) Db->smdb_close(Db);
+		exit(EX_OK);
 	}
 
 	if (exclude)
 	{
 		xclude(stdin);
-		result = Db->smdb_close(Db);
-		exit(EX_OK);
+		(void) Db->smdb_close(Db);
+		EXITM(EX_OK);
 	}
 
 	if ((cur = (ALIAS *)malloc((u_int)sizeof(ALIAS))) == NULL)
 	{
-		syslog(LOG_NOTICE,
+		msglog(LOG_NOTICE,
 		       "vacation: can't allocate memory for username.\n");
-		exit(EX_OSERR);
+		(void) Db->smdb_close(Db);
+		EXITM(EX_OSERR);
 	}
 	cur->name = name;
 	cur->next = Names;
 	Names = cur;
 
-	readheaders();
-	if (!recent())
+	result = readheaders();
+	if (result == EX_OK && !recent())
 	{
 		time_t now;
 
 		(void) time(&now);
 		setreply(From, now);
-		result = Db->smdb_close(Db);
+		(void) Db->smdb_close(Db);
 		sendmessage(name, msgfilename, emptysender);
 	}
 	else
-		result = Db->smdb_close(Db);
-	exit(EX_OK);
-	/* NOTREACHED */
-	return EX_OK;
+		(void) Db->smdb_close(Db);
+	if (result == EX_NOUSER)
+		result = EX_OK;
+	exit(result);
 }
 
 /*
-** READHEADERS -- read mail headers
+** EATMSG -- read stdin till EOF
 **
 **	Parameters:
 **		none.
@@ -316,7 +438,33 @@
 **		nothing.
 **
 */
-void
+
+static void
+eatmsg()
+{
+	/*
+	**  read the rest of the e-mail and ignore it to avoid problems
+	**  with EPIPE in sendmail
+	*/
+	while (getc(stdin) != EOF)
+		continue;
+}
+
+/*
+** READHEADERS -- read mail headers
+**
+**	Parameters:
+**		none.
+**
+**	Returns:
+**		a exit code: NOUSER if no reply, OK if reply, * if error
+**
+**	Side Effects:
+**		may exit().
+**
+*/
+
+int
 readheaders()
 {
 	bool tome, cont;
@@ -327,7 +475,7 @@
 	extern bool nsearch __P((char *, char *));
 
 	cont = tome = FALSE;
-	while (!tome && fgets(buf, sizeof(buf), stdin) && *buf != '\n')
+	while (fgets(buf, sizeof(buf), stdin) && *buf != '\n')
 	{
 		switch(*buf)
 		{
@@ -346,9 +494,9 @@
 						p++;
 						if (*p == '\0')
 						{
-							syslog(LOG_NOTICE,
+							msglog(LOG_NOTICE,
 							       "vacation: badly formatted \"From \" line.\n");
-							exit(EX_DATAERR);
+							EXITIT(EX_DATAERR);
 						}
 					}
 					else if (*p == '"')
@@ -361,20 +509,20 @@
 				}
 				if (quoted)
 				{
-					syslog(LOG_NOTICE,
+					msglog(LOG_NOTICE,
 					       "vacation: badly formatted \"From \" line.\n");
-					exit(EX_DATAERR);
+					EXITIT(EX_DATAERR);
 				}
 				*p = '\0';
 
 				/* ok since both strings have MAXLINE length */
 				if (*From == '\0')
-					(void)strlcpy(From, buf + 5,
-						      sizeof From);
+					(void) strlcpy(From, buf + 5,
+						       sizeof From);
 				if ((p = strchr(buf + 5, '\n')) != NULL)
 					*p = '\0';
 				if (junkmail(buf + 5))
-					exit(EX_OK);
+					EXITIT(EX_NOUSER);
 			}
 			break;
 
@@ -394,7 +542,7 @@
 			if (strncasecmp(p, "junk", 4) == 0 ||
 			    strncasecmp(p, "bulk", 4) == 0 ||
 			    strncasecmp(p, "list", 4) == 0)
-				exit(EX_OK);
+				EXITIT(EX_NOUSER);
 			break;
 
 		  case 'C':		/* "Cc:" */
@@ -425,12 +573,13 @@
 		}
 	}
 	if (!tome)
-		exit(EX_OK);
+		EXITIT(EX_NOUSER);
 	if (*From == '\0')
 	{
-		syslog(LOG_NOTICE, "vacation: no initial \"From \" line.\n");
-		exit(EX_DATAERR);
+		msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n");
+		EXITIT(EX_DATAERR);
 	}
+	EXITIT(EX_OK);
 }
 
 /*
@@ -445,6 +594,7 @@
 **		is name a substring of str?
 **
 */
+
 bool
 nsearch(name, str)
 	register char *name, *str;
@@ -485,50 +635,139 @@
 **		is this some automated/junk/bulk/list mail?
 **
 */
+
+struct ignore
+{
+	char	*name;
+	size_t	len;
+};
+
+typedef struct ignore IGNORE_T;
+
+#define MAX_USER_LEN 256	/* maximum length of local part (sender) */
+
+/* delimiters for the local part of an address */
+#define isdelim(c)	((c) == '%' || (c) == '@' || (c) == '+')
+
 bool
 junkmail(from)
 	char *from;
 {
-	register size_t len;
-	register char *p;
-	register struct ignore *cur;
-	static struct ignore
-	{
-		char	*name;
-		size_t	len;
-	} ignore[] =
+	bool quot;
+	char *e;
+	size_t len;
+	IGNORE_T *cur;
+	char sender[MAX_USER_LEN];
+	static IGNORE_T ignore[] =
 	{
-		{ "-request",		8	},
 		{ "postmaster",		10	},
 		{ "uucp",		4	},
 		{ "mailer-daemon",	13	},
 		{ "mailer",		6	},
+		{ NULL,			0	}
+	};
+
+	static IGNORE_T ignorepost[] =
+	{
+		{ "-request",		8	},
 		{ "-relay",		6	},
+		{ "-owner",		6	},
 		{ NULL,			0	}
 	};
 
+	static IGNORE_T ignorepre[] =
+	{
+		{ "owner-",		6	},
+		{ NULL,			0	}
+	};
+
 	/*
-	 * This is mildly amusing, and I'm not positive it's right; trying
-	 * to find the "real" name of the sender, assuming that addresses
-	 * will be some variant of:
-	 *
-	 * From site!site!SENDER%site.domain%site.domain@site.domain
-	 */
-	if ((p = strchr(from, '%')) == NULL &&
-	    (p = strchr(from, '@')) == NULL)
+	**  This is mildly amusing, and I'm not positive it's right; trying
+	**  to find the "real" name of the sender, assuming that addresses
+	**  will be some variant of:
+	**
+	**  From site!site!SENDER%site.domain%site.domain@site.domain
+	*/
+
+	quot = FALSE;
+	e = from;
+	len = 0;
+	while (*e != '\0' && (quot || !isdelim(*e)))
 	{
-		if ((p = strrchr(from, '!')) != NULL)
-			++p;
-		else
-			p = from;
-		for (; *p; ++p)
+		if (*e == '"')
+		{
+			quot = !quot;
+			++e;
+			continue;
+		}
+		if (*e == '\\')
+		{
+			if (*(++e) == '\0')
+			{
+				/* '\\' at end of string? */
+				break;
+			}
+			if (len < MAX_USER_LEN)
+				sender[len++] = *e;
+			++e;
 			continue;
+		}
+		if (*e == '!' && !quot)
+		{
+			len = 0;
+			sender[len] = '\0';
+		}
+		else
+			if (len < MAX_USER_LEN)
+				sender[len++] = *e;
+		++e;
+	}
+	if (len < MAX_USER_LEN)
+		sender[len] = '\0';
+	else
+		sender[MAX_USER_LEN - 1] = '\0';
+
+	if (len <= 0)
+		return FALSE;
+#if 0
+	if (quot)
+		return FALSE;	/* syntax error... */
+#endif /* 0 */
+
+	/* test prefixes */
+	for (cur = ignorepre; cur->name != NULL; ++cur)
+	{
+		if (len >= cur->len &&
+		    strncasecmp(cur->name, sender, cur->len) == 0)
+			return TRUE;
 	}
-	len = p - from;
+
+	/*
+	**  If the name is truncated, don't test the rest.
+	**	We could extract the "tail" of the sender address and
+	**	compare it it ignorepost, however, it seems not worth
+	**	the effort.
+	**	The address surely can't match any entry in ignore[]
+	**	(as long as all of them are shorter than MAX_USER_LEN).
+	*/
+
+	if (len > MAX_USER_LEN)
+		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;
+	}
+
+	/* test postfixes */
+	for (cur = ignorepost; cur->name != NULL; ++cur)
+	{
 		if (len >= cur->len &&
-		    strncasecmp(cur->name, p - cur->len, cur->len) == 0)
+		    strncasecmp(cur->name, e - cur->len - 1,
+				cur->len) == 0)
 			return TRUE;
 	}
 	return FALSE;
@@ -547,6 +786,7 @@
 **		TRUE iff user has gotten a vacation message recently.
 **
 */
+
 bool
 recent()
 {
@@ -560,27 +800,27 @@
 	memset(&data, '\0', sizeof data);
 
 	/* get interval time */
-	key.data.data = VIT;
-	key.data.size = sizeof(VIT);
+	key.data = VIT;
+	key.size = sizeof(VIT);
 
 	st = Db->smdb_get(Db, &key, &data, 0);
 	if (st != SMDBE_OK)
 		next = SECSPERDAY * DAYSPERWEEK;
 	else
-		memmove(&next, data.data.data, sizeof(next));
+		memmove(&next, data.data, sizeof(next));
 
 	memset(&data, '\0', sizeof data);
 
 	/* get record for this address */
-	key.data.data = From;
-	key.data.size = strlen(From);
+	key.data = From;
+	key.size = strlen(From);
 
 	do
 	{
 		st = Db->smdb_get(Db, &key, &data, 0);
 		if (st == SMDBE_OK)
 		{
-			memmove(&then, data.data.data, sizeof(then));
+			memmove(&then, data.data, sizeof(then));
 			if (next == ONLY_ONCE || then == ONLY_ONCE ||
 			    then + next > time(NULL))
 				return TRUE;
@@ -588,8 +828,8 @@
 		if ((trydomain = !trydomain) &&
 		    (domain = strchr(From, '@')) != NULL)
 		{
-			key.data.data = domain;
-			key.data.size = strlen(domain);
+			key.data = domain;
+			key.size = strlen(domain);
 		}
 	} while (trydomain);
 	return FALSE;
@@ -608,6 +848,7 @@
 **	Side Effects:
 **		stores the reply interval in database.
 */
+
 void
 setinterval(interval)
 	time_t interval;
@@ -617,11 +858,11 @@
 	memset(&key, '\0', sizeof key);
 	memset(&data, '\0', sizeof data);
 
-	key.data.data = VIT;
-	key.data.size = sizeof(VIT);
-	data.data.data = (char*) &interval;
-	data.data.size = sizeof(interval);
-	(void)(Db->smdb_put)(Db, &key, &data, 0);
+	key.data = VIT;
+	key.size = sizeof(VIT);
+	data.data = (char*) &interval;
+	data.size = sizeof(interval);
+	(void) (Db->smdb_put)(Db, &key, &data, 0);
 }
 
 /*
@@ -638,6 +879,7 @@
 **	Side Effects:
 **		stores user/time in database.
 */
+
 void
 setreply(from, when)
 	char *from;
@@ -648,11 +890,11 @@
 	memset(&key, '\0', sizeof key);
 	memset(&data, '\0', sizeof data);
 
-	key.data.data = from;
-	key.data.size = strlen(from);
-	data.data.data = (char*) &when;
-	data.data.size = sizeof(when);
-	(void)(Db->smdb_put)(Db, &key, &data, 0);
+	key.data = from;
+	key.size = strlen(from);
+	data.data = (char*) &when;
+	data.size = sizeof(when);
+	(void) (Db->smdb_put)(Db, &key, &data, 0);
 }
 
 /*
@@ -668,6 +910,7 @@
 **	Side Effects:
 **		stores users in database.
 */
+
 void
 xclude(f)
 	FILE *f;
@@ -699,6 +942,7 @@
 **	Side Effects:
 **		sends vacation reply.
 */
+
 void
 sendmessage(myname, msgfn, emptysender)
 	char *myname;
@@ -708,27 +952,38 @@
 	FILE *mfp, *sfp;
 	int i;
 	int pvect[2];
+	char *pv[8];
 	char buf[MAXLINE];
 
 	mfp = fopen(msgfn, "r");
 	if (mfp == NULL)
 	{
 		if (msgfn[0] == '/')
-			syslog(LOG_NOTICE, "vacation: no %s file.\n", msgfn);
+			msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn);
 		else
-			syslog(LOG_NOTICE, "vacation: no ~%s/%s file.\n",
+			msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n",
 			       myname, msgfn);
 		exit(EX_NOINPUT);
 	}
 	if (pipe(pvect) < 0)
 	{
-		syslog(LOG_ERR, "vacation: pipe: %s", errstring(errno));
+		msglog(LOG_ERR, "vacation: pipe: %s", errstring(errno));
 		exit(EX_OSERR);
 	}
+	pv[0] = "sendmail";
+	pv[1] = "-oi";
+	pv[2] = "-f";
+	if (emptysender)
+		pv[3] = "<>";
+	else
+		pv[3] = myname;
+	pv[4] = "--";
+	pv[5] = From;
+	pv[6] = NULL;
 	i = fork();
 	if (i < 0)
 	{
-		syslog(LOG_ERR, "vacation: fork: %s", errstring(errno));
+		msglog(LOG_ERR, "vacation: fork: %s", errstring(errno));
 		exit(EX_OSERR);
 	}
 	if (i == 0)
@@ -737,11 +992,8 @@
 		(void) close(pvect[0]);
 		(void) close(pvect[1]);
 		(void) fclose(mfp);
-		if (emptysender)
-			myname = "<>";
-		(void) execl(_PATH_SENDMAIL, "sendmail", "-f", myname, "--",
-		      From, NULL);
-		syslog(LOG_ERR, "vacation: can't exec %s: %s",
+		(void) execv(_PATH_SENDMAIL, pv);
+		msglog(LOG_ERR, "vacation: can't exec %s: %s",
 			_PATH_SENDMAIL, errstring(errno));
 		exit(EX_UNAVAILABLE);
 	}
@@ -759,7 +1011,7 @@
 	else
 	{
 		(void) fclose(mfp);
-		syslog(LOG_ERR, "vacation: can't open pipe to sendmail");
+		msglog(LOG_ERR, "vacation: can't open pipe to sendmail");
 		exit(EX_UNAVAILABLE);
 	}
 }
@@ -767,10 +1019,137 @@
 void
 usage()
 {
-	syslog(LOG_NOTICE, "uid %u: usage: vacation [-i] [-a alias] [-f db] [-m msg] [-r interval] [-s sender] [-t time] [-x] [-z] login\n",
-	    getuid());
+	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 */
+	       );
 	exit(EX_USAGE);
 }
+
+#if _FFR_LISTDB
+/*
+** LISTDB -- list the contents of the vacation database
+**
+**	Parameters:
+**		none.
+**
+**	Returns:
+**		nothing.
+*/
+
+static void
+listdb()
+{
+	int result;
+	time_t t;
+	SMDB_CURSOR *cursor = NULL;
+	SMDB_DBENT db_key, db_value;
+
+	memset(&db_key, '\0', sizeof db_key);
+	memset(&db_value, '\0', sizeof db_value);
+
+	result = Db->smdb_cursor(Db, &cursor, 0);
+	if (result != SMDBE_OK)
+	{
+		fprintf(stderr, "vacation: set cursor: %s\n",
+			errstring(result));
+		return;
+	}
+
+	while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
+					   SMDB_CURSOR_GET_NEXT)) == SMDBE_OK)
+	{
+		/* skip magic VIT entry */
+		if ((int)db_key.size -1 == strlen(VIT) &&
+		    strncmp((char *)db_key.data, VIT,
+			    (int)db_key.size - 1) == 0)
+			continue;
+
+		/* 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);
+			continue;
+		}
+
+		memcpy(&t, db_value.data, sizeof t);
+
+		if (db_key.size > 40)
+			db_key.size = 40;
+
+		printf("%-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);
+	}
+
+	if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
+	{
+		fprintf(stderr,	"vacation: get value at cursor: %s\n",
+			errstring(result));
+		if (cursor != NULL)
+		{
+			(void) cursor->smdbc_close(cursor);
+			cursor = NULL;
+		}
+		return;
+	}
+	(void) cursor->smdbc_close(cursor);
+	cursor = NULL;
+}
+#endif /* _FFR_LISTDB */
+
+#if _FFR_DEBUG
+/*
+** DEBUGLOG -- write message to standard error
+**
+**	Append a message to the standard error for the convenience of
+**	end-users debugging without access to the syslog messages.
+**
+**	Parameters:
+**		i -- syslog log level
+**		fmt -- string format
+**
+**	Returns:
+**		nothing.
+*/
+
+/*VARARGS2*/
+static void
+#ifdef __STDC__
+debuglog(int i, const char *fmt, ...)
+#else /* __STDC__ */
+debuglog(i, fmt, va_alist)
+	int i;
+	const char *fmt;
+	va_dcl
+#endif /* __STDC__ */
+
+{
+	VA_LOCAL_DECL
+
+	VA_START(fmt);
+	vfprintf(stderr, fmt, ap);
+	VA_END;
+}
+#endif /* _FFR_DEBUG */
 
 /*VARARGS1*/
 void