summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/src
diff options
context:
space:
mode:
authorgshapiro <gshapiro@FreeBSD.org>2002-02-17 21:56:45 +0000
committergshapiro <gshapiro@FreeBSD.org>2002-02-17 21:56:45 +0000
commit514d1553cc1f49dd008e6e432664359124af60a9 (patch)
treee71b01386ad11f2e0c99bab2eab606f5c7360807 /contrib/sendmail/src
parentc2a47cdbe88de50d484d2cdb605874e1168626dc (diff)
parent8449595fe97f4474b9b9a7e4edee1ef35dcff393 (diff)
downloadFreeBSD-src-514d1553cc1f49dd008e6e432664359124af60a9.zip
FreeBSD-src-514d1553cc1f49dd008e6e432664359124af60a9.tar.gz
This commit was generated by cvs2svn to compensate for changes in r90792,
which included commits to RCS files with non-trunk default branches.
Diffstat (limited to 'contrib/sendmail/src')
-rw-r--r--contrib/sendmail/src/Makefile.m443
-rw-r--r--contrib/sendmail/src/README361
-rw-r--r--contrib/sendmail/src/SECURITY192
-rw-r--r--contrib/sendmail/src/TRACEFLAGS10
-rw-r--r--contrib/sendmail/src/TUNING232
-rw-r--r--contrib/sendmail/src/alias.c329
-rw-r--r--contrib/sendmail/src/aliases45
-rw-r--r--contrib/sendmail/src/arpadate.c9
-rw-r--r--contrib/sendmail/src/bf.c846
-rw-r--r--contrib/sendmail/src/bf.h30
-rw-r--r--contrib/sendmail/src/collect.c714
-rw-r--r--contrib/sendmail/src/control.c167
-rw-r--r--contrib/sendmail/src/convtime.c43
-rw-r--r--contrib/sendmail/src/daemon.c2508
-rw-r--r--contrib/sendmail/src/deliver.c2860
-rw-r--r--contrib/sendmail/src/domain.c536
-rw-r--r--contrib/sendmail/src/envelope.c653
-rw-r--r--contrib/sendmail/src/helpfile6
-rw-r--r--contrib/sendmail/src/macro.c350
-rw-r--r--contrib/sendmail/src/main.c3033
-rw-r--r--contrib/sendmail/src/map.c3298
-rw-r--r--contrib/sendmail/src/milter.c1310
-rw-r--r--contrib/sendmail/src/mime.c253
-rw-r--r--contrib/sendmail/src/newaliases.115
-rw-r--r--contrib/sendmail/src/parseaddr.c1233
-rw-r--r--contrib/sendmail/src/queue.c6910
-rw-r--r--contrib/sendmail/src/readcf.c1489
-rw-r--r--contrib/sendmail/src/recipient.c1196
-rw-r--r--contrib/sendmail/src/sasl.c208
-rw-r--r--contrib/sendmail/src/sendmail.h1415
-rw-r--r--contrib/sendmail/src/sfsasl.c827
-rw-r--r--contrib/sendmail/src/sfsasl.h49
-rw-r--r--contrib/sendmail/src/shmticklib.c18
-rw-r--r--contrib/sendmail/src/sm_resolve.c411
-rw-r--r--contrib/sendmail/src/sm_resolve.h142
-rw-r--r--contrib/sendmail/src/srvrsmtp.c4361
-rw-r--r--contrib/sendmail/src/stab.c248
-rw-r--r--contrib/sendmail/src/stats.c85
-rw-r--r--contrib/sendmail/src/statusd_shm.h13
-rw-r--r--contrib/sendmail/src/sysexits.c76
-rw-r--r--contrib/sendmail/src/timers.c23
-rw-r--r--contrib/sendmail/src/timers.h6
-rw-r--r--contrib/sendmail/src/tls.c1469
-rw-r--r--contrib/sendmail/src/trace.c210
-rw-r--r--contrib/sendmail/src/udb.c286
-rw-r--r--contrib/sendmail/src/usersmtp.c1721
-rw-r--r--contrib/sendmail/src/util.c1549
-rw-r--r--contrib/sendmail/src/version.c10
48 files changed, 27703 insertions, 14095 deletions
diff --git a/contrib/sendmail/src/Makefile.m4 b/contrib/sendmail/src/Makefile.m4
index 3fe0703..8004de4 100644
--- a/contrib/sendmail/src/Makefile.m4
+++ b/contrib/sendmail/src/Makefile.m4
@@ -1,12 +1,17 @@
include(confBUILDTOOLSDIR`/M4/switch.m4')
+define(`confREQUIRE_LIBSM', `true')
bldPRODUCT_START(`executable', `sendmail')
-define(`bldBIN_TYPE', `S')
+define(`bldBIN_TYPE', `G')
define(`bldINSTALL_DIR', `')
-define(`bldSOURCES', `main.c alias.c arpadate.c bf_'ifdef(`confSTDIO_TYPE', `confSTDIO_TYPE', `portable')`.c clock.c collect.c conf.c control.c convtime.c daemon.c deliver.c domain.c envelope.c err.c headers.c macro.c map.c mci.c milter.c mime.c parseaddr.c queue.c readcf.c recipient.c savemail.c sfsasl.c shmticklib.c srvrsmtp.c stab.c stats.c sysexits.c timers.c trace.c udb.c usersmtp.c util.c version.c ')
+define(`bldSOURCES', `main.c alias.c arpadate.c bf.c collect.c conf.c control.c convtime.c daemon.c deliver.c domain.c envelope.c err.c headers.c macro.c map.c mci.c milter.c mime.c parseaddr.c queue.c readcf.c recipient.c savemail.c sasl.c sfsasl.c shmticklib.c sm_resolve.c srvrsmtp.c stab.c stats.c sysexits.c timers.c tls.c trace.c udb.c usersmtp.c util.c version.c ')
PREPENDDEF(`confENVDEF', `confMAPDEF')
+bldPUSH_SMLIB(`sm')
bldPUSH_SMLIB(`smutil')
+dnl hack: /etc/mail is not defined as "location of .cf" in the build system
+define(`bldTARGET_INST_DEP', ifdef(`confINST_DEP', `confINST_DEP',
+`${DESTDIR}/etc/mail/submit.cf ${DESTDIR}${MSPQ}'))dnl
define(`bldTARGET_LINKS', ifdef(`confLINKS', `confLINKS',
`${DESTDIR}${UBINDIR}/newaliases ${DESTDIR}${UBINDIR}/mailq ${DESTDIR}${UBINDIR}/hoststat ${DESTDIR}${UBINDIR}/purgestat')
)dnl
@@ -31,11 +36,45 @@ divert(bldTARGETS_SECTION)
statistics:
${CP} /dev/null statistics
+${DESTDIR}/etc/mail/submit.cf:
+ @echo "Please read INSTALL if anything fails while installing the binary."
+ @echo "${DESTDIR}/etc/mail/submit.cf will be installed now."
+ cd ${SRCDIR}/cf/cf && make install-submit-cf
+
+MSPQ=ifdef(`confMSP_QUEUE_DIR', `confMSP_QUEUE_DIR', `/var/spool/clientmqueue')
+
+${DESTDIR}${MSPQ}:
+ @echo "Please read INSTALL if anything fails while installing the binary."
+ @echo "You must have setup a new user ${MSPQOWN} and a new group ${GBINGRP}"
+ @echo "as explained in sendmail/SECURITY."
+ mkdir -p ${DESTDIR}${MSPQ}
+ chown ${MSPQOWN} ${DESTDIR}${MSPQ}
+ chgrp ${GBINGRP} ${DESTDIR}${MSPQ}
+ chmod 0770 ${DESTDIR}${MSPQ}
+
divert(0)
+ifdef(`confSETUSERID_INSTALL', `bldPUSH_INSTALL_TARGET(`install-set-user-id')')
+ifdef(`confMTA_INSTALL', `bldPUSH_INSTALL_TARGET(`install-sm-mta')')
ifdef(`confNO_HELPFILE_INSTALL',, `bldPUSH_INSTALL_TARGET(`install-hf')')
ifdef(`confNO_STATISTICS_INSTALL',, `bldPUSH_INSTALL_TARGET(`install-st')')
divert(bldTARGETS_SECTION)
+
+install-set-user-id: bldCURRENT_PRODUCT ifdef(`confNO_HELPFILE_INSTALL',, `install-hf') ifdef(`confNO_STATISTICS_INSTALL',, `install-st') ifdef(`confNO_MAN_BUILD',, `install-docs')
+ ${INSTALL} -c -o ${S`'BINOWN} -g ${S`'BINGRP} -m ${S`'BINMODE} bldCURRENT_PRODUCT ${DESTDIR}${M`'BINDIR}
+ for i in ${sendmailTARGET_LINKS}; do \
+ rm -f $$i; \
+ ln -s ${M`'BINDIR}/sendmail $$i; \
+ done
+
+define(`confMTA_LINKS', `${DESTDIR}${UBINDIR}/newaliases ${DESTDIR}${UBINDIR}/mailq ${DESTDIR}${UBINDIR}/hoststat ${DESTDIR}${UBINDIR}/purgestat')
+install-sm-mta: bldCURRENT_PRODUCT
+ ${INSTALL} -c -o ${M`'BINOWN} -g ${M`'BINGRP} -m ${M`'BINMODE} bldCURRENT_PRODUCT ${DESTDIR}${M`'BINDIR}/sm-mta
+ for i in confMTA_LINKS; do \
+ rm -f $$i; \
+ ln -s ${M`'BINDIR}/sm-mta $$i; \
+ done
+
install-hf:
if [ ! -d ${DESTDIR}${HFDIR} ]; then mkdir -p ${DESTDIR}${HFDIR}; else :; fi
${INSTALL} -c -o ${UBINOWN} -g ${UBINGRP} -m 444 helpfile ${DESTDIR}${HFFILE}
diff --git a/contrib/sendmail/src/README b/contrib/sendmail/src/README
index fd8d5ee..3f984d9 100644
--- a/contrib/sendmail/src/README
+++ b/contrib/sendmail/src/README
@@ -1,4 +1,4 @@
-# Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
+# Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
# All rights reserved.
# Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
# Copyright (c) 1988
@@ -9,17 +9,14 @@
# the sendmail distribution.
#
#
-# $Id: README,v 8.263.2.1.2.38 2001/08/15 22:07:11 gshapiro Exp $
+# $Id: README,v 8.345 2002/01/09 18:04:30 ca Exp $
#
This directory contains the source files for sendmail(TM).
-*********************
-!! DO NOT USE MAKE !! in this directory to compile sendmail --
-********************* instead, use the "Build" script located in
-the sendmail directory. It will build an appropriate Makefile, and
-create an appropriate obj.* subdirectory so that multiplatform
-support works easily.
+ *******************************************************************
+ !! Read sendmail/SECURITY for important installation information !!
+ *******************************************************************
**********************************************************
** Read below for more details on building sendmail. **
@@ -32,7 +29,7 @@ support works easily.
For detailed instructions, please read the document ../doc/op/op.me:
- eqn ../doc/op/op.me | pic | ditroff -me
+ cd ../doc/op ; make op.ps op.txt
Sendmail is a trademark of Sendmail, Inc.
@@ -123,7 +120,9 @@ MAP_REGEX Regular Expression support. You will need to use an
operating system which comes with the POSIX regex()
routines or install a regexp library such as libregex from
the Free Software Foundation.
-PH_MAP PH map support. You will need the qi PH package.
+DNSMAP DNS map support. Requires NAMED_BIND.
+PH_MAP PH map support. You will need the libphclient library from
+ the nph package (http://www-dev.cso.uiuc.edu/ph/nph/).
MAP_NSD nsd map support (IRIX 6.5 and later).
>>> NOTE WELL for NEWDB support: If you want to get ndbm support, for
@@ -202,10 +201,13 @@ SYS5SIGNALS Use System V signal semantics -- the signal handler
signal handler stays in force until an exec or an
explicit delete. Implied by SYSTEM5.
SYS5SETPGRP Use System V setpgrp() semantics. Implied by SYSTEM5.
+HASNICE Define this to zero if you lack the nice(2) system call.
+HASRRESVPORT Define this to zero if you lack the rresvport(3) system call.
HASFCHMOD Define this to one if you have the fchmod(2) system call.
This improves security.
HASFCHOWN Define this to one if you have the fchown(2) system call.
- This is required for the TrustedUser option.
+ This is required for the TrustedUser option if sendmail
+ must rebuild an (alias) map.
HASFLOCK Set this if you prefer to use the flock(2) system call
rather than using fcntl-based locking. Fcntl locking
has some semantic gotchas, but many vendor systems
@@ -251,6 +253,15 @@ USESETEUID Define this to 1 if you have a seteuid(2) system call that
Posix.1. The test program ../test/t_seteuid.c will try
this out on your system. If you define both HASSETREUID
and USESETEUID, the former is ignored.
+HASSETEGID Define this if you have setegid(2) and it can be
+ used to set the saved gid. Please run t_dropgid in
+ test/ if you are not sure whether the call works.
+HASSETREGID Define this if you have setregid(2) and it can be
+ used to set the saved gid. Please run t_dropgid in
+ test/ if you are not sure whether the call works.
+HASSETRESGID Define this if you have setresgid(2) and it can be
+ used to set the saved gid. Please run t_dropgid in
+ test/ if you are not sure whether the call works.
HASLSTAT Define this if you have symbolic links (and thus the
lstat(2) system call). This improves security. Unlike
most other options, this one is on by default, so you
@@ -273,17 +284,13 @@ HASURANDOMDEV Define this if your system has /dev/urandom(4).
HASSTRERROR Define this if you have the libc strerror(3) function (which
should be declared in <errno.h>), and it should be used
instead of sys_errlist.
-NEEDGETOPT Define this if you need a reimplementation of getopt(3).
+SM_CONF_GETOPT Define this as 0 if you need a reimplementation of getopt(3).
On some systems, getopt does very odd things if called
to scan the arguments twice. This flag will ask sendmail
to compile in a local version of getopt that works
properly.
NEEDSTRTOL Define this if your standard C library does not define
strtol(3). This will compile in a local version.
-NEEDVPRINTF Define this if your standard C library does not define
- vprintf(3). Note that the resulting fake implementation
- is not very elegant and may not even work on some
- architectures.
NEEDFSYNC Define this if your standard C library does not define
fsync(2). This will try to simulate the operation using
fcntl(2); if that is not available it does nothing, which
@@ -446,9 +453,6 @@ SIOCGIFNUM_IS_BROKEN
Set this if your system has an SIOCGIFNUM ioctl defined,
but it doesn't behave the same way as "most" systems
(Solaris, HP-UX).
-NEED_PERCENTQ Set this if your system doesn't support the printf
- format strings %lld or %llu. If this is set, %qd and
- %qu are used instead.
FAST_PID_RECYCLE
Set this if your system can reuse the same PID in the same
second.
@@ -456,13 +460,22 @@ 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.
+PIPELINING Support SMTP PIPELINING (set by default).
+USING_NETSCAPE_LDAP
+ Set this if LDAPMAP is set and your LDAP libraries are from
+ (or are derived from) Netscape's implementation, which
+ requires that the return value of ldap_first_attribute()
+ and ldap_next_attribute() be ldap_memfree()'d.
+NEEDLINK Set this if your system doesn't have a link() call. It
+ will create a copy of the file instead of a hardlink.
+USE_ENVIRON Set this to 1 to access process environment variables from
+ the external variable environ instead of the third
+ parameter of main().
+USE_DOUBLE_FORK By default this is on (1). Set it to 0 to suppress the
+ extra fork() used to avoid intermediate zombies.
+-----------------------+
@@ -529,8 +542,6 @@ NETUNIX Define this to get Unix domain networking support. Defined
support this networking domain.
NETNS Define this to get NS networking support.
NETX25 Define this to get X.25 networking support.
-SMTP Define this to get the SMTP code. Implied by NETINET
- or NETISO.
NAMED_BIND If non-zero, include DNS (name daemon) support, including
MX support. The specs say you must use this if you run
SMTP. You don't have to be running a name server daemon
@@ -538,12 +549,6 @@ NAMED_BIND If non-zero, include DNS (name daemon) support, including
including remote access to another machine, requires this
option. Defined by default in conf.h. Define it to zero
ONLY on machines that do not use DNS in any way.
-QUEUE Define this to get queueing code. Implied by NETINET
- or NETISO; required by SMTP. This gives you other good
- stuff -- it should be on.
-DAEMON Define this to get general network support. Implied by
- NETINET or NETISO. Defined by default in conf.h. You
- almost certainly want it on.
MATCHGECOS Permit fuzzy matching of user names against the full
name (GECOS) field in the /etc/passwd file. This should
probably be on, since you can disable it from the config
@@ -568,7 +573,7 @@ SHARE_V1 Support for the fair share scheduler, version 1. Setting to
1 causes final delivery to be done using the recipients
resource limitations. So far as I know, this is only
supported on ConvexOS.
-SASL Enables SMTP AUTH (RFC 2554). This requires the Cyrus SASL
+SASL Enables SMTP AUTH (RFC 2554). This requires the Cyrus SASL
library (ftp://ftp.andrew.cmu.edu/pub/cyrus-mail/). Please
install at least version 1.5.13. See below for further
information: SASL COMPILATION AND CONFIGURATION. If your
@@ -576,31 +581,27 @@ SASL Enables SMTP AUTH (RFC 2554). This requires the Cyrus SASL
to its version number using a simple conversion: a.b.c
-> c + b*100 + a*10000, e.g. for 1.5.9 define SASL=10509.
Note: Using an older version than 1.5.5 of Cyrus SASL is
- not supported. Starting with version 1.5.10, setting SASL=1
- is sufficient. Any value other than 1 (or 0) will be
+ not supported. Starting with version 1.5.10, setting SASL=1
+ is sufficient. Any value other than 1 (or 0) will be
compared with the actual version found and if there is a
mismatch, compilation will fail.
EGD Define this if your system has EGD installed, see
- http://www.lothar.com/tech/crypto/ . It should be used to
+ http://www.lothar.com/tech/crypto/ . It should be used to
seed the PRNG for STARTTLS if HASURANDOMDEV is not defined.
-STARTTLS Enables SMTP STARTTLS (RFC 2487). This requires OpenSSL
- (http://www.OpenSSL.org/) and sfio (see below).
- Use OpenSSL 0.9.5a or later (if compatible with this
- version), do not use 0.9.3.
+STARTTLS Enables SMTP STARTTLS (RFC 2487). This requires OpenSSL
+ (http://www.OpenSSL.org/); use OpenSSL 0.9.5a or later
+ (if compatible with this version), do not use 0.9.3.
See STARTTLS COMPILATION AND CONFIGURATION for further
information.
TLS_NO_RSA Turn off support for RSA algorithms in STARTTLS.
-SFIO Uses sfio instead of stdio. sfio is available from AT&T
- (http://www.research.att.com/sw/tools/sfio/). If this
- compile flag is set, confSTDIO_TYPE must be set to portable.
- This compile flag is necessary for STARTTLS; it also
- enables the security layer of SASL. The sfio include file
- stdio.h must be installed in a subdirectory called sfio,
- i.e., if you install sfio in /usr/local, stdio.h should
- be in /usr/local/include/sfio, and libsfio.a should be in
- /usr/local/lib. Notice: read the sfio section in
- OPERATING SYSTEM AND COMPILE QUIRKS.
-
+MILTER Turn on support for external filters using the Milter API.
+ See libmilter/README for more information.
+REQUIRES_DIR_FSYNC Turn on support for file systems that require to
+ call fsync() for a directory if the meta-data in it has
+ been changed. This should be turned on at least for
+ ReiserFS; it is enabled by default for Linux. An alternative
+ to this compile time flag is to mount the queue directory
+ without the -async option, or using chattr +S on Linux.
Generic notice: If you enable a compile time option that needs
libraries or include files that don't come with sendmail or are
@@ -642,25 +643,21 @@ YOU HEADACHES!
When attempting to canonify a hostname, some broken name servers will
return SERVFAIL (a temporary failure) on T_AAAA (IPv6) lookups. If you
-want to excuse this behavior, compile sendmail with
--D_FFR_WORKAROUND_BROKEN_NAMESERVERS and add WorkAroundBrokenAAAA to your
-ResolverOptions setting. However, instead, we recommend catching the
-problem and reporting it to the name server administrator so we can rid the
-world of broken name servers.
+want to excuse this behavior, include WorkAroundBrokenAAAA in
+ResolverOptions. However, instead, we recommend catching the problem and
+reporting it to the name server administrator so we can rid the world of
+broken name servers.
+----------------------------------------+
| STARTTLS COMPILATION AND CONFIGURATION |
+----------------------------------------+
-Please read the docs accompanying the OpenSSL library and sfio.
-You have to compile and install both libraries before you can compile
+Please read the documentation accompanying the OpenSSL library. You
+have to compile and install the OpenSSL libraries before you can compile
sendmail. See devtools/README how to set the correct compile time
parameters; you should at least set the following variables:
-define(`confSTDIO_TYPE', `portable')
-APPENDDEF(`conf_sendmail_ENVDEF', `-DSFIO')
-APPENDDEF(`conf_sendmail_LIBS', `-lsfio')
APPENDDEF(`conf_sendmail_ENVDEF', `-DSTARTTLS')
APPENDDEF(`conf_sendmail_LIBS', `-lssl -lcrypto')
@@ -681,10 +678,6 @@ and try again. Then take a look at the logfile and see whether
there are any problems listed about permissions (unsafe files)
or the validity of X.509 certificates.
-Note: sfio must be used in all libraries with which sendmail exchanges
-file pointers. An example is PH map support. This does not apply to the
-usual libraries, e.g., OpenSSL, Berkeley DB, Cyrus SASL.
-
Further information can be found via:
http://www.sendmail.org/tips/
@@ -693,11 +686,11 @@ http://www.sendmail.org/tips/
| SASL COMPILATION AND CONFIGURATION |
+------------------------------------+
-Please read the docs accompanying the library (INSTALL and README).
-If you use Berkeley DB for Cyrus SASL then you must compile sendmail
-with the same version of Berkeley DB. See devtools/README how to
-set the correct compile time parameters; you should at least set
-the following variables:
+Please read the documentation accompanying the Cyrus SASL library
+(INSTALL and README). If you use Berkeley DB for Cyrus SASL then
+you must compile sendmail with the same version of Berkeley DB.
+See devtools/README how to set the correct compile time parameters;
+you should at least set the following variables:
APPENDDEF(`conf_sendmail_ENVDEF', `-DSASL')
APPENDDEF(`conf_sendmail_LIBS', `-lsasl')
@@ -709,10 +702,10 @@ BUILDING SENDMAIL.
You have to select and install authentication mechanisms and tell
sendmail where to find the sasl library and the include files (see
-devtools/README for the parameters to set). Setup the required
+devtools/README for the parameters to set). Setup the required
users and passwords as explained in the SASL documentation. See
-also cf/README for authentication related options (esp. DefaultAuthInfo
-if you want authentication between MTAs).
+also cf/README for authentication related options (especially
+DefaultAuthInfo if you want authentication between MTAs).
To perform an initial test, connect to your sendmail daemon
(telnet localhost 25) and issue a EHLO localhost and see whether
@@ -731,6 +724,9 @@ http://www.sendmail.org/tips/
+-------------------------------------+
GCC problems
+ When compiling with "gcc -O -Wall" specify "-DSM_OMIT_BOGUS_WARNINGS"
+ too (see include/sm/cdefs.h for more info).
+
*****************************************************************
** IMPORTANT: DO NOT USE OPTIMIZATION (``-O'') IF YOU ARE **
** RUNNING GCC 2.4.x or 2.5.x. THERE IS A BUG IN THE GCC **
@@ -774,9 +770,9 @@ Configuration file location
binary.
NETINFO systems use NETINFO to determine the location of
- sendmail.cf. The full path to sendmail.cf is stored as the value of
+ sendmail.cf. The full path to sendmail.cf is stored as the value of
the "sendmail.cf" property in the "/locations/sendmail"
- subdirectory of NETINFO. Set the value of this property to
+ subdirectory of NETINFO. Set the value of this property to
"/etc/mail/sendmail.cf" (without the quotes) to use this new
default location for Sendmail 8.10.0 and higher.
@@ -789,6 +785,23 @@ ControlSocket permissions
owned directories which do not permit non-root to r/w/x through them.
The long term fix is for all kernels to upgrade to 4.4BSD semantics.
+HP MPE/iX
+ The MPE-specific code within sendmail emulates a set-user-id root
+ environment for the sendmail binary. But there is no root uid 0 on
+ MPE, nor is there any support for set-user-id programs. Even when
+ sendmail thinks it is running as uid 0, it will still have the file
+ access rights of the underlying non-zero uid, but because sendmail is
+ an MPE priv-mode program it will still be able to call setuid() to
+ successfully switch to a new uid.
+
+ MPE setgid() semantics don't quite work the way sendmail expects, so
+ special emulation is done here also.
+
+ This uid/gid emulation is enabled via the setuid/setgid file mode bits
+ which are not currently used by MPE. Code in libsm/mpeix.c examines
+ these bits and enables emulation if they have been set, i.e.,
+ chmod u+s,g+s /SENDMAIL/CURRENT/SENDMAIL.
+
SunOS 4.x (Solaris 1.x)
You may have to use -lresolv on SunOS. However, beware that
this links in a new version of gethostbyname that does not
@@ -901,7 +914,7 @@ Solaris 2.4 (SunOS 5.4)
>> testing and debugging mechanisms. It was decided that the only
>> secure way to do this was to allow a `trusted' path to be used in
>> LD_LIBRARY_PATH. The only trusted directory we presently define
- >> is /usr/lib. Thus a setuid root developer could play with some
+ >> is /usr/lib. Thus a set-user-ID root developer could play with some
>> alternative shared object implementations and place them in
>> /usr/lib (being root we assume they'ed have access to write in this
>> directory). This change was made as part of 1155380 - after a
@@ -920,7 +933,7 @@ Solaris 2.5.1 (SunOS 5.5.1) and 2.6 (SunOS 5.6)
Apparently Solaris 2.5.1 patch 103663-01 installs a new
/usr/include/resolv.h file that defines the __P macro without
checking to see if it is already defined. This new resolv.h is also
- included in the Solaris 2.6 distribution. This causes compile
+ included in the Solaris 2.6 distribution. This causes compile
warnings such as:
In file included from daemon.c:51:
@@ -933,8 +946,7 @@ Solaris 2.5.1 (SunOS 5.5.1) and 2.6 (SunOS 5.6)
#undef __P
#include "/usr/include/resolv.h"
- Sun is aware of the problem (Sun bug ID 4081053) and it will be fixed
- in Solaris 2.7.
+ This problem was fixed in Solaris 7 (Sun bug ID 4081053).
Solaris 7 (SunOS 5.7)
Solaris 7 includes LDAP libraries but the implementation was
@@ -950,6 +962,23 @@ Solaris 7 (SunOS 5.7)
to ldap_set_option for LDAP_OPT_REFERRALS in ldapmap_setopts if
LDAP support is compiled in sendmail.
+Solaris 8 and later (SunOS 5.8 and later)
+ Solaris 8 and later can optionally install LDAP support. If you
+ have installed the Entire Distribution meta-cluster, you can use
+ the following in devtools/Site/site.SunOS.5.8.m4 (or other
+ appropriately versioned file) to enable LDAP:
+
+ APPENDDEF(`confMAPDEF', `-DLDAPMAP')
+ APPENDDEF(`confLIBS', `-lldap')
+
+Solaris 9 and later (SunOS 5.9 and later)
+ Solaris 9 and later have a revised LDAP library, libldap.so.5,
+ which is derived from a Netscape implementation, thus requiring
+ that USING_NETSCAPE_LDAP be defined in conjunction with LDAPMAP:
+
+ APPENDDEF(`confMAPDEF', `-DLDAPMAP -DUSING_NETSCAPE_LDAP')
+ APPENDDEF(`confLIBS', `-lldap')
+
Solaris
If you are using dns for hostname resolution on Solaris, make sure
that the 'dns' entry is last on the hosts line in
@@ -1070,6 +1099,9 @@ IRIX 6.x
less than 16 bits long unless they are 8 bits; IRIX 6.2 has
some other sized structs. See
http://www.bitmechanic.com/mail-archives/mysql/current/0418.html
+ This problem seems to be fixed by gcc v2.95.2, gcc v2.8.1
+ is reported as broken. Check your gcc version for this bug
+ before installing sendmail.
IRIX 6.4
The IRIX 6.5.4 version of /bin/m4 does not work properly with
@@ -1102,13 +1134,13 @@ BSDI (BSD/386) 1.0, NetBSD 0.9, FreeBSD 1.0
I haven't had a chance to test this myself.
The M4 shipped in FreeBSD and NetBSD 0.9 don't handle the config
- files properly. One must use either GNU m4 1.1 or the PD-M4
+ files properly. One must use either GNU m4 1.1 or the PD-M4
recently posted in comp.os.386bsd.bugs (and maybe others).
NetBSD-current includes the PD-M4 (as stated in the NetBSD file
CHANGES).
- FreeBSD 1.0 RELEASE has uname(2) now. Use -DUSEUNAME in order to
- use it (look into devtools/OS/FreeBSD). NetBSD-current may have
+ FreeBSD 1.0 RELEASE has uname(2) now. Use -DUSEUNAME in order to
+ use it (look into devtools/OS/FreeBSD). NetBSD-current may have
it too but it has not been verified.
The latest version of Berkeley DB uses a different naming
@@ -1134,6 +1166,12 @@ BSDI (BSD/386) 1.0, NetBSD 0.9, FreeBSD 1.0
APPENDDEF(`confOBJADD', `oldbind.compat.o')
+OpenBSD (up to 2.9 Release), NetBSD, FreeBSD (up to 4.3-RELEASE)
+ m4 from *BSD won't handle libsm/Makefile.m4 properly, since the
+ maximum length for strings is too short. You need to use GNU m4
+ or patch m4, see for example:
+ http://FreeBSD.org/cgi/cvsweb.cgi/src/usr.bin/m4/eval.c.diff?r1=1.11&r2=1.12
+
A/UX
Date: Tue, 12 Oct 1993 18:28:28 -0400 (EDT)
From: "Eric C. Hagberg" <hagberg@med.cornell.edu>
@@ -1151,7 +1189,7 @@ A/UX
What I did was to get the gnu-dbm-1.6 package, compile it, and
then re-compile sendmail with "-lgdbm", "-DNDBM", and using the
- ndbm.h header file that comes with the gnu-package. This makes
+ ndbm.h header file that comes with the gnu-package. This makes
things behave properly.
[NOTE: see comment above about GDBM]
@@ -1171,14 +1209,18 @@ SCO Unix
to know that if they are on SCO, they had better set
OI-dnsrch
or they will core dump as soon as they try to use the resolver.
- ie. although SCO has _res.dnsrch defined, and is kinda BIND 4.8.3, it
- does not inititialise it, nor does it understand 'search' in
+ i.e., although SCO has _res.dnsrch defined, and is kinda BIND 4.8.3,
+ it does not inititialise it, nor does it understand 'search' in
/etc/named.boot.
- sigh -
According to SCO, the m4 which ships with UnixWare 2.1.2 is broken.
We recommend installing GNU m4 before attempting to build sendmail.
+ On some versions a bogus error value is listed if connections
+ time out (large negative number). To avoid this explicitly set
+ Timeout.connect to a reasonable value (several minutes).
+
DG/UX
Doug Anderson <dlander@afterlife.ncsc.mil> has successfully run
V8 on the DG/UX 5.4.2 and 5.4R3.x platforms under heavy usage.
@@ -1205,8 +1247,8 @@ HP-UX 8.00
From: Kimmo Suominen <Kimmo.Suominen@lut.fi>
Subject: 8.6.5 w/ HP-UX 8.00 on s300
- Just compiled and fought with sendmail 8.6.5 on a HP9000/360 (ie. a
- series 300 machine) running HP-UX 8.00.
+ Just compiled and fought with sendmail 8.6.5 on a HP9000/360 (i.e.,
+ a series 300 machine) running HP-UX 8.00.
I was getting segmentation fault when delivering to a local user.
With debugging I saw it was faulting when doing _free@libc... *sigh*
@@ -1237,13 +1279,6 @@ Linux
with sendmail's version of cdefs.h. Deleting sendmail's version
on those systems should be non-harmful, and new versions don't care.
- Sendmail assumes that libc has snprintf, which has been true since
- libc 4.7.0. If you are running an older version, you will need to
- use -DHASSNPRINTF=0 in the Makefile. If may be able to use -lbsd
- (which includes snprintf) instead of turning this off on versions
- of libc between 4.4.4 and 4.7.0 (snprintf improves security, so
- you want to use this if at all possible).
-
NOTE ON LINUX & BIND: By default, the Makefile generated for Linux
includes header files in /usr/local/include and libraries in
/usr/local/lib. If you've installed BIND on your system, the header
@@ -1266,11 +1301,6 @@ Linux
implementation in the Linux 2.2.0 kernel and poll()-aware versions
of glib (at least up to 2.0.111).
- Some pre-glibc distributions of Linux include a syslog.h that does
- not work properly with SFIO. You can fix this by adding
- "#include <syslog.h>" to the SFIO version of stdio.h as the very
- first line.
-
glibc
glibc 2.2.1 (and possibly other versions) changed the value of
__RES in resolv.h but failed to actually provide the IPv6 API
@@ -1303,6 +1333,19 @@ AIX 4.X
gcc -Wl,-rpath /usr/lib -Wl,-rpath /lib -Wl,-rpath /usr/local/lib
+AIX 4.X If the test program t-event (and most others) in libsm fails,
+ check your compiler settings. It seems that the flags -qnoro or
+ -qnoroconst on some AIX versions trigger a compiler bug. Check
+ your compiler settings or use cc instead of xlc.
+
+AIX 4.0-4.2, maybe some AIX 4.3 versions
+ 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
+ configuration method. To work around this problem, please use
+ GNU m4 from ftp://ftp.gnu.org/pub/gnu/.
+ The problem seems to be solved in AIX 4.3.3 at least.
+
AIX 4.3.3
From: Valdis.Kletnieks@vt.edu
Date: Sun, 02 Jul 2000 03:58:02 -0400
@@ -1317,13 +1360,6 @@ AIX 4.3.3
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
- configuration method. To work around this problem, please use
- GNU m4 from ftp://ftp.gnu.org/pub/gnu/.
-
AIX 3.x
This version of sendmail does not support MB, MG, and MR resource
records, which are supported by AIX sendmail.
@@ -1342,44 +1378,6 @@ AIX 3.1.x
If you don't care about load average throttling, just turn off
load average checking using -DLA_TYPE=LA_ZERO.
-AIX 2.2.1
- Date: Mon Dec 4 14:14:56 CST 1995
- From: Mark Whetzel <markw@antimatr.houston.tx.us>
- Subject: Porting sendmail 8.7.2 to AIX V2 on the RT.
-
- This version of sendmail does not support MB, MG, and MR resource
- records, which are supported by AIX sendmail.
-
- AIX V2 on the RT does not have 'paths.h'. Create a null
- file in the 'obj' directory to remove this compile error.
-
- A patch file is needed to get the BSD 'db' library to compile
- for AIX/RT. I have sent the necessary updates to the author,
- but they may not be immediately available.
- [NOTE: Berkeley DB version 2.X runs on AIX/RT.]
-
- The original AIX/RT resolver libraries are very old, and you
- should get the latest BIND to replace it. The 4.8.3 version
- has been tested, but 4.9.x is out and should work.
-
- To make the load average code work correctly requires an
- external routine, as the kernel does not maintain system
- load averages, similar to AIX V3.1.x. A reverse port of the
- older 1.05 'monitor' load average daemon code written by
- Jussi Maki that will work on AIX V2 for the RT is available
- by E-mail to Mark Whetzel <markw@antimatr.houston.tx.us>.
- That code depends on an external daemon to collect system
- load information, and the external routine 'getloadavg',
- that will return that information. The 'LA_SUBR' define
- will handle this for AIX V2 on the RT.
-
- Note: You will have to change devtools/OS/AIX.2 to correctly
- point to the locatons of the updated BIND source tree and
- the location of the 'newdb' tree and library location.
- You will also have to change devtools/OS/AIX.2 to know
- about the location of the 'getloadavg' routine if you use
- the LA_SUBR define.
-
RISC/os
RISC/os from MIPS is a merged AT&T/Berkeley system. When you
compile on that platform you will get duplicate definitions
@@ -1475,7 +1473,8 @@ UNICOS 8.0.3.4
running sendmail. Reported by Jerry G. DeLapp <jgd@acl.lanl.gov>.
Mac OS X (10.0.X)
- From: Mike Zimmerman <zimmy@torrentnet.com>
+ 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:
@@ -1489,6 +1488,28 @@ Mac OS X (10.0.X)
Remove the "&" after the sendmail command:
/usr/sbin/sendmail -bd -q1h
+ From Carsten Klapp <carsten.klapp@home.com>:
+
+ The easiest workaround is to remove the group-writable permission
+ for the root directory and the symbolic /etc inherits this
+ change. While this does fix sendmail, the unfortunate side-effect
+ is the OS X admin will no longer be able to manipulate icons in the
+ top level of the Startup disk unless logged into the GUI as the
+ superuser.
+
+ In applying the alternate workaround, care must be taken while
+ swapping the symlink /etc with the directory /private/etc. In all
+ likelihood any admin who is concerned with this sendmail error has
+ enough experience to not accidentally harm anything in the process.
+
+ a. Swap the /etc symlink with /private/etc (as superuser):
+ rm /etc
+ mv /private/etc /etc
+ ln -s /etc /private/etc
+
+ b. Set / to group unwritable (as superuser):
+ chmod g-w /
+
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.
@@ -1555,30 +1576,14 @@ OpenSSL
Do not use 0.9.3, but OpenSSL 0.9.5a or later if compatible with
0.9.5a.
-sfio
- You may run into problems if you use sfio2000 (the body of a
- message is lost). Use sfio1999 instead; however, it also has
- a bug that can cause sendmail to fail. A patch has been provided
- by Petr Lampa of Brno University of Technology, which is given here:
-
-diff -rc ../../../../sfio/src/lib/sfio/sfputr.c ./sfputr.c
-*** ../../../../sfio/src/lib/sfio/sfputr.c Tue May 16 18:25:49 2000
---- ./sfputr.c Wed Sep 20 09:06:01 2000
-***************
-*** 24,29 ****
---- 24,30 ----
- for(w = 0; (*s || rc >= 0); )
- { SFWPEEK(f,ps,p);
-
-+ if(p == -1) return -1; /* PL */
- if(p == 0 || (f->flags&SF_WHOLE) )
- { n = strlen(s);
- if(p >= (n + (rc < 0 ? 0 : 1)) )
-
-
PH
PH support is provided by Mark Roth <roth@uiuc.edu>. The map is
described at http://www-dev.cso.uiuc.edu/sendmail/ .
+
+ NOTE: The "spacedname" pseudo-field which was used by earlier
+ versions of the PH map code is no longer supported! See the URL
+ listed above for more information.
+
Please contact Mark Roth for support and questions regarding the
map.
@@ -1605,7 +1610,7 @@ Regular Expressions (MAP_REGEX)
pattern-compile-error: : Operation not applicable
- Your libc does not include a running version of POSIX-regex. Use
+ Your libc does not include a running version of POSIX-regex. Use
librx or regex.o from the GNU Free Software Foundation,
ftp://ftp.gnu.org/pub/gnu/rx-?.?.tar.gz or
ftp://ftp.gnu.org/pub/gnu/regex-?.?.tar.gz.
@@ -1665,14 +1670,10 @@ TRACEFLAGS My own personal list of the trace flags -- not guaranteed
alias.c Does name aliasing in all forms.
aliases.5 Man page describing the format of the aliases file.
arpadate.c A subroutine which creates ARPANET standard dates.
-bf.h Buffered file I/O function declarations.
-bf_portable.c Stub routines for systems lacking the Torek stdio library.
-bf_portable.h Data structure and function declarations for bf_portable.c.
-bf_torek.c Routines to implement memory-buffered file system using
- hooks provided by Torek stdio library.
-bf_torek.h Data structure and function declarations for bf_torek.c.
-clock.c Routines to implement real-time oriented functions
- in sendmail -- e.g., timeouts.
+bf.c Routines to implement memory-buffered file system using
+ hooks provided by libsm now (formerly Torek stdio library).
+bf.h Buffered file I/O function declarations and
+ data structure and function declarations for bf.c.
collect.c The routine that actually reads the mail into a temp
file. It also does a certain amount of parsing of
the header, etc.
@@ -1681,9 +1682,9 @@ conf.c The configuration file. This contains information
controversial, or code compiled in for efficiency
reasons. Most of the configuration is in sendmail.cf.
conf.h Configuration that must be known everywhere.
+control.c Routines to implement control socket.
convtime.c A routine to sanely process times.
-daemon.c Routines to implement daemon mode. This version is
- specifically for Berkeley 4.1 IPC.
+daemon.c Routines to implement daemon mode.
deliver.c Routines to deliver mail.
domain.c Routines that interface with DNS (the Domain Name
System).
@@ -1707,12 +1708,15 @@ queue.c Routines to implement message queueing.
readcf.c The routine that reads the configuration file and
translates it to internal form.
recipient.c Routines that manipulate the recipient list.
+sasl.c Routines to interact with Cyrys-SASL.
savemail.c Routines which save the letter on processing errors.
sendmail.8 Man page for the sendmail command.
sendmail.h Main header file for sendmail.
-sfsasl.c I/O interface between SASL/TLS and the MTA using SFIO.
+sfsasl.c I/O interface between SASL/TLS and the MTA.
sfsasl.h Header file for sfsasl.c.
shmticklib.c Routines for shared memory counters.
+sm_resolve.c Routines for DNS lookups (for DNS map type).
+sm_resolve.h Header file for sm_resolve.c.
srvrsmtp.c Routines to implement server SMTP.
stab.c Routines to manage the symbol table.
stats.c Routines to collect and post the statistics.
@@ -1722,6 +1726,7 @@ sysexits.c List of error messages associated with error codes
sysexits.h List of error codes for systems that lack their own.
timers.c Routines to provide microtimers.
timers.h Data structure and function declarations for timers.h.
+tls.c Routines for TLS.
trace.c The trace package. These routines allow setting and
testing of trace flags with a high granularity.
udb.c The user database interface module.
@@ -1730,4 +1735,4 @@ util.c Some general purpose routines used by sendmail.
version.c The version number and information about this
version of sendmail.
-(Version $Revision: 8.263.2.1.2.38 $, last update $Date: 2001/08/15 22:07:11 $ )
+(Version $Revision: 8.345 $, last update $Date: 2002/01/09 18:04:30 $ )
diff --git a/contrib/sendmail/src/SECURITY b/contrib/sendmail/src/SECURITY
new file mode 100644
index 0000000..7e55024
--- /dev/null
+++ b/contrib/sendmail/src/SECURITY
@@ -0,0 +1,192 @@
+# Copyright (c) 2000-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.
+#
+# $Id: SECURITY,v 1.47 2001/09/23 02:29:05 ca Exp $
+#
+
+This file gives some hints how to configure and run sendmail for
+people who are very security conscious (you should be...).
+
+Even though sendmail goes through great lengths to assure that it
+can't be compromised even if the system it is running on is
+incorrectly or insecurely configured, it can't work around everything.
+This has been demonstrated by recent OS problems which have
+subsequently been used to compromise the root account using sendmail
+as a vector. One way to minimize the possibility of such problems
+is to install sendmail without set-user-ID root, which avoids local
+exploits. This configuration, which is the default starting with
+8.12, is described in the first section of this security guide.
+
+
+*****************************************************
+** sendmail configuration without set-user-ID root **
+*****************************************************
+
+sendmail needs to run as root for several purposes:
+
+- bind to port 25
+- call the local delivery agent (LDA) as root (or other user) if the LDA
+ isn't set-user-ID root (unless some other method of storing e-mail in
+ local mailboxes is used).
+- read .forward files
+- write e-mail submitted via the command line to the queue directory.
+
+Only the last item requires a set-user-ID/set-group-ID program to
+avoid problems with a world-writable directory. It is however
+sufficient to have a set-group-ID program and a group-writable
+queue directory. The other requirements listed above can be
+fulfilled by a sendmail daemon that is started by root. Hence this
+section explains how to use two sendmail configurations to accomplish
+the goal to have a sendmail binary that is not set-user-ID root,
+and hence is not open to system configuration/OS problems or at
+least less problematic in presence of those.
+
+The default configuration starting with sendmail 8.12 uses one
+sendmail binary which acts differently based on operation mode and
+supplied options.
+
+sendmail must be a set-group-ID (default group: smmsp, recommended
+gid: 25) program to allow for queueing mail in a group-writable
+directory. Two .cf files are required: sendmail.cf for the daemon
+and submit.cf for the submission program. The following permissions
+should be used:
+
+-r-xr-sr-x root smmsp ... /PATH/TO/sendmail
+drwxrwx--- smmsp smmsp ... /var/spool/clientmqueue
+drwx------ root wheel ... /var/spool/mqueue
+-r--r--r-- root wheel ... /etc/mail/sendmail.cf
+-r--r--r-- root wheel ... /etc/mail/submit.cf
+
+That is, the owner of sendmail is root, the group is smmsp, and
+the binary is set-group-ID. The client mail queue is owned by
+smmsp with group smmsp and is group writable. The client mail
+queue directory must be writable by smmsp, but it must not be
+accessible for others. That is, do not use world read or execute
+permissions. In submit.cf the option UseMSP must be set, and
+QueueFileMode must be set to 0660. submit.cf is available in
+cf/cf/, which has been built from cf/cf/submit.mc. The file can
+be used as-is, if you want to add more options, use cf/cf/submit.mc
+as starting point and read cf/README: MESSAGE SUBMISSION PROGRAM
+carefully.
+
+The .cf file is chosen based on the operation mode. For -bm (default),
+-bs, and -t it is submit.cf (if it exists) for all others it is
+sendmail.cf. This selection can be changed by -Ac or -Am (alternative
+.cf file: client or mta).
+
+The daemon must be started by root as usual, e.g.,
+
+/PATH/TO/sendmail -L sm-mta -bd -q1h
+
+(replace /PATH/TO with the right path for your OS, e.g.,
+/usr/sbin or /usr/lib).
+
+Notice: if you run sendmail from inetd (which in general is not a
+good idea), you must specify -Am in addition to -bs.
+
+Mail will end up in the client queue if the daemon doesn't accept
+connections or if an address is temporarily not resolvable. The
+latter problem can be minimized by using
+
+ FEATURE(`nocanonify', `canonify_hosts')
+ define(`confDIRECT_SUBMISSION_MODIFIERS', `C')
+
+which, however, may have undesired side effects. See cf/README for
+a discussion. In general it is necessary to clean the queue either
+via a cronjob or by running a daemon, e.g.,
+
+/PATH/TO/sendmail -L sm-msp-queue -Ac -q30m
+
+If the option UseMSP is not set, sendmail will complain during
+queue runs about bogus file permission. If you want a queue runner
+for the client queue, you probably have to change OS specific
+scripts to accomplish this (check the man pages of your OS for more
+information.) You can start this program as root, it will change
+its user id to RunAsUser (smmsp by default, recommended uid: 25).
+This way smmsp does not need a valid shell.
+
+Summary
+-------
+
+This is a brief summary how the two configuration files are used:
+
+sendmail.cf For the MTA (mail transmission agent)
+ The MTA is started by root as daemon:
+
+ /PATH/TO/sendmail -L sm-mta -bd -q1h
+
+ it accepts SMTP connections (on ports 25 and 587 by default);
+ it runs the main queue (/var/spool/mqueue by default).
+
+submit.cf For the MSP (mail submission program)
+ The MSP is used to submit e-mails, hence it is invoked
+ by programs (and maybe users); it does not run as SMTP
+ daemon; it uses /var/spool/clientmqueue by default; it
+ can be started to run that queue periodically:
+
+ /PATH/TO/sendmail -L sm-msp-queue -Ac -q30m
+
+
+Hints and Troubleshooting
+-------------------------
+
+RunAsUser: FEATURE(`msp') sets the option RunAsUser to smmsp.
+This user must have the group smmsp, i.e., the same group as the
+clientmqueue directory. If you specify a user whose primary group
+is not the same as that of the clientmqueue directory, then you
+should explicitly set the group, e.g.,
+
+ FEATURE(`msp')
+ define(`confRUN_AS_USER', `mailmsp:smmsp')
+
+STARTTLS: If sendmail is compiled with STARTTLS support on a platform
+that does not have HASURANDOMDEV defined, you either need to specify
+the RandFile option (as for the MTA), or you have to turn off
+STARTTLS in the MSP, e.g.,
+
+ DAEMON_OPTIONS(`Name=NoMTA, Addr=127.0.0.1, M=S')
+ FEATURE(`msp')
+ CLIENT_OPTIONS(`Family=inet, Address=0.0.0.0, M=S')
+
+The first option is used to turn off STARTTLS when the MSP is
+invoked with -bs as some MUAs do.
+
+
+What doesn't work anymore
+-------------------------
+
+Normal users can't use mailq anymore to see the MTA mail queue.
+There are several ways around it, e.g., changing QueueFileMode
+or giving users access via a program like sudo.
+
+sendmail -bv may give misleading output for normal users since it
+may not be able to access certain files, e.g., .forward files of
+other users.
+
+
+Alternative
+-----------
+
+Instead of having one set-group-ID binary, it is possible to use
+two with different permissions: one for message submission
+(set-group-ID), one acting as daemon etc, which is only executable
+by root. In that case it is possible to remove features from
+the message submission program to have a smaller binary.
+You can use
+
+ sh ./Build install-sm-mta
+
+to install a sendmail program to act as daemon etc under the name
+sm-mta.
+
+Set-User-Id
+-----------
+
+If you really have to install sendmail set-user-ID root, you can use
+
+ sh ./Build install-set-user-id
+
diff --git a/contrib/sendmail/src/TRACEFLAGS b/contrib/sendmail/src/TRACEFLAGS
index 09f7d35..d43bc5c 100644
--- a/contrib/sendmail/src/TRACEFLAGS
+++ b/contrib/sendmail/src/TRACEFLAGS
@@ -1,4 +1,4 @@
-# $Id: TRACEFLAGS,v 8.29.16.1 2001/05/03 17:24:00 gshapiro Exp $
+# $Id: TRACEFLAGS,v 8.35 2001/11/28 01:01:25 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,11 +75,17 @@
62 multiple file descriptor checking
63 queue.c runqueue process watching
64 multiple Milter
+65 main.c permission checks
+66 srvrsmtp.c conformance checks
67 conf.c signals
+69 queue.c scheduling
+#if _FFR_QUARANTINE
+70 queue.c quarantining
+#endif /* _FFR_QUARANTINE */
80 content length
81 sun remote mode
91 mci.c syslogging of MCI cache information
-94 srvrsmtp.c cause commands to fail (for protocol testing)
+94,>99 srvrsmtp.c cause commands to fail (for protocol testing)
95 srvrsmtp.c AUTH
95 usersmtp.c AUTH
98 * timers
diff --git a/contrib/sendmail/src/TUNING b/contrib/sendmail/src/TUNING
new file mode 100644
index 0000000..e055269
--- /dev/null
+++ b/contrib/sendmail/src/TUNING
@@ -0,0 +1,232 @@
+# Copyright (c) 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.
+#
+# $Id: TUNING,v 1.16 2001/08/19 21:03:38 gshapiro Exp $
+#
+
+********************************************
+** This is a DRAFT, comments are welcome! **
+********************************************
+
+
+If the default configuration of sendmail does not achieve the
+required performance, there are several configuration options that
+can be changed to accomplish higher performance. However, before
+those options are changed it is necessary to understand why the
+performance is not as good as desired. This may also involve hardware
+and software (OS) configurations which are not extensively explored
+in this document. We assume that your system is not limited by
+network bandwidth because optimizing for this situation is beyond
+the scope of this guide. In almost all other cases performance will
+be limited by disk I/O.
+
+
+This text assumes that all options which are mentioned here are
+familiar to the reader, they are explained in the Sendmail Installation
+and Operations Guide; doc/op/op.txt.
+
+There are basically three different scenarios which are treated
+in the following:
+* Mailing Lists and Large Aliases (1-n Mailing)
+* 1-1 Mass Mailing
+* High Volume Mail
+
+Depending on your requirements, these may need different options
+to optimize sendmail for the particular purpose. It is also possible
+to configure sendmail to achieve good performance in all cases, but
+it will not be optimal for any specific purpose. For example, it
+is non-trivival to combine low latency (fast delivery of incoming
+mail) with high overall throughput.
+
+Before we explore the different scenarios, a basic discussion about
+disk I/O, delivery modes, and queue control is required.
+
+
+* Disk I/O
+-----------------------------------------------
+
+In general mail will be written to disk up before a delivery attempt
+is made. This is required for reliability and should only be changed
+in a few specific cases that are mentioned later on. To achieve
+better disk I/O performance the queue directories can be spread
+over several disks to distribute the load. This is some basic tuning
+that should be done in all cases where the I/O speed of a single
+disk is exceeded, which is true for almost every high-volume
+situation except if a special disk subsystem with large (NV)RAM
+buffer is used.
+
+Depending on your OS there might be ways to speed up I/O, e.g.,
+using softupdates or turning on the noatime mount option. If this
+is done make sure the filesystem is still reliable, i.e., if fsync()
+returns without an error, the file has really been committed to
+disk.
+
+
+* Queueing Strategies and DeliveryMode
+-----------------------------------------------
+
+There are basically three delivery modes:
+
+background: incoming mail will be immediately delivered by a new process
+interactive: incoming mail will be immediately delivered by the same process
+queue: incoming mail will be queued and delivered by a queue runner later on
+
+The first offers the lowest latency without the disadvantage of the
+second, which keep the connection from the sender open until the
+delivery to the next hop succeeded or failed. However, it does not
+allow for a good control over the number of delivery processes other
+than limiting the total number of direct children of the daemon
+processes (MaxChildren) or by load control options (RefuseLA,
+DelayLA). Moreover, it can't make as good use as 'queue' mode can
+for connection caching.
+
+Interactive DeliveryMode should only be used in rare cases, e.g.,
+if the delivery time to the next hop is a known quantity or if the
+sender is under local control and it does not matter if it has to
+wait for delivery.
+
+Queueing up e-mail before delivery is done by a queue runner allows
+the best load control but does not achieve as low latency as the
+other two modes. However, this mode is probably also best for
+concurrent delivery since the number of queue runners can be specified
+on a queue group basis. Persistent queue runners (-qp) can be used
+to minimize the overhead for creating processes because they just
+sleep for the specified interval (which shold be short) instead of
+exiting after a queue run.
+
+
+* Queue Groups
+-----------------------------------------------
+
+In most situations disk I/O is a bottleneck which can be mitigated
+by spreading the load over several disks. This can easily be achieved
+with different queue directories. sendmail 8.12 introduces queue
+groups which are collections of queue directories with similar
+properties, i.e., number of processes to run the queues in the
+group, maximum number of recipients within an e-mail (envelope),
+etc. Queue groups allow control over the behaviour of different
+queues. Depending on the setup, it is usually possible to have
+several queue runners delivering mails concurrently which should
+increase throughput. The number of queue runners can be controlled
+per queue group (Runner=) and overall (MaxQueueChildren).
+
+
+* DNS Lookups
+-----------------------------------------------
+
+sendmail performs by default host name canonifications by using
+host name lookups. This process is meant to replace unqualified
+host name with qualified host names, and CNAMEs with the non-aliased
+name. However, these lookups can take a while for large address
+lists, e.g., mailing lists. If you can assure by other means that
+host names are canonical, you should use
+
+ FEATURE(`nocanonify', `canonify_hosts')
+
+in your .mc file. For further information on this feature and
+additional options see cf/README. If sendmail is invoked directly
+to send e-mail then either the -G option should be used or
+
+ define(`confDIRECT_SUBMISSION_MODIFIERS', `C')
+
+should be added to the .mc file.
+
+
+* Mailing Lists and Large Aliases (1-n Mailing)
+-----------------------------------------------
+
+Before 8.12 sendmail delivers an e-mail sequentially to all its
+recipients. For mailing lists or large aliases the overall delivery
+time can be substantial, especially if some of the recipients are located
+at hosts that are slow to accept e-mail. Some mailing list software
+therefore "split" up e-mails into smaller pieces with fewer recipients.
+sendmail 8.12 can do this itself, either across queue groups or
+within a queue directory. For the former the option SplitAcrossQueueGroups
+option must be set, the latter is controlled by the 'r=' field of
+a queue group declaration.
+
+Let's assume a simple example: a mailing lists where most of
+the recipients are at three domains: the local one (local.domain)
+and two remotes (one.domain, two.domain) and the rest is splittered
+over several other domains. For this case it is useful to specify
+three queue groups:
+
+QUEUE_GROUP(`local', `P=/var/spool/mqueue/local, F=f, R=2, I=1m')dnl
+QUEUE_GROUP(`one', `P=/var/spool/mqueue/one, F=f, r=50, R=3')dnl
+QUEUE_GROUP(`two', `P=/var/spool/mqueue/two, F=f, r=30, R=4')dnl
+QUEUE_GROUP(`remote', `P=/var/spool/mqueue/remote, F=f, r=5, R=8, I=2m')dnl
+define(`ESMTP_MAILER_QGRP', `remote')dnl
+define(`confSPLIT_ACROSS_QUEUEGROUPS', `True')dnl
+define(`confDELIVERY_MODE', `q')dnl
+define(`confMAX_QUEUE_CHILDREN', `50')dnl
+define(`confMIN_QUEUE_AGE', `27m')dnl
+
+and specify the queuegroup ruleset as follows:
+
+LOCAL_RULESETS
+Squeuegroup
+R$* @ local.domain $# local
+R$* @ $* one.domain $# one
+R$* @ $* two.domain $# two
+R$* @ $* $# remote
+R$* $# mqueue
+
+Now it is necessary to control the number of queue runners, which
+is done by MaxQueueChildren. Starting the daemon with the option
+-q5m assures that the first delivery attempt for each e-mail is
+done within 5 minutes, however, there are also individual queue
+intervals for the queue groups as specified above. MinQueueAge
+is set to 27 minutes to avoid that entries are run too often.
+
+Notice: if envelope splitting happens due to alias expansion, and
+DeliveryMode is not 'i'nteractive, then only one envelope is sent
+immediately. The rest (after splitting) are queued up and queue
+runners must come along and take care of them. Hence it is essential
+that the queue interval is very short.
+
+
+* 1-1 Mass Mailing
+-----------------------------------------------
+
+In this case some program generates e-mails which are sent to
+individual recipients (or at most very few per e-mail). A simple
+way to achieve high throughput is to set the delivery mode to
+'interactive', turn off the SuperSafe option and make sure that the
+program that generates the mails can deal with mail losses if the
+server loses power. In no other case should SuperSafe be set to
+'false'. If these conditions are met, sendmail does not need to
+commit mails to disk but can buffer them in memory which will greatly
+enhance performance, especially compared to normal disk subsystems, e.g.,
+non solid-state disks.
+
+
+* High Volume Mail
+-----------------------------------------------
+
+For high volume mail it is necessary to be able to control the load
+on the system. Therefore the 'queue' delivery mode should be used,
+and all options related to number of processes and the load should
+be set to reasonable values. It is important not to accept mail
+faster than it can be delivered otherwise the system will be
+overwhelmed. Hence RefuseLA should be lower than QueueLA, the number
+of daemon children should probably be lower than the number of queue
+runnners (MaxChildren vs. MaxQueueChildren). DelayLA is a new option
+in 8.12 which allows delaying connections instead of rejecting them.
+This may result in a smoother load distribution depending on how
+the mails are submitted to sendmail.
+
+
+* Miscellaneous
+-----------------------------------------------
+
+Other options that are interesting to tweak performance are
+(in no particular order):
+
+SuperSafe: if interactive DeliveryMode is used, then this can
+be set to the new value "interactive" in 8.12 to save some disk
+synchronizations which are not really necessary in that mode.
+
diff --git a/contrib/sendmail/src/alias.c b/contrib/sendmail/src/alias.c
index 070da59..28a76d5 100644
--- a/contrib/sendmail/src/alias.c
+++ b/contrib/sendmail/src/alias.c
@@ -8,23 +8,22 @@
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
+ *
*/
#include <sendmail.h>
-#ifndef lint
-static char id[] = "@(#)$Id: alias.c,v 8.142.4.11 2001/05/03 17:24:01 gshapiro Exp $";
-#endif /* ! lint */
+SM_RCSID("@(#)$Id: alias.c,v 8.211 2001/11/12 22:52:18 ca Exp $")
-# define SEPARATOR ':'
+#define SEPARATOR ':'
# define ALIAS_SPEC_SEPARATORS " ,/:"
static MAP *AliasFileMap = NULL; /* the actual aliases.files map */
static int NAliasFileMaps; /* the number of entries in AliasFileMap */
-static char *aliaslookup __P((char *, int *));
+static char *aliaslookup __P((char *, int *, char *));
- /*
+/*
** ALIAS -- Compute aliases.
**
** Scans the alias file for an alias for the given address.
@@ -62,7 +61,7 @@ alias(a, sendq, aliaslevel, e)
char obuf[MAXNAME + 7];
if (tTd(27, 1))
- dprintf("alias(%s)\n", a->q_user);
+ sm_dprintf("alias(%s)\n", a->q_user);
/* don't realias already aliased names */
if (!QS_IS_OK(a->q_state))
@@ -81,35 +80,38 @@ alias(a, sendq, aliaslevel, e)
** bounce messages inappropriately.
*/
-
#if _FFR_REDIRECTEMPTY
/*
** envelope <> can't be sent to mailing lists, only owner-
** send spam of this type to owner- of the list
** ---- to stop spam from going to mailing lists!
*/
+
if (e->e_sender != NULL && *e->e_sender == '\0')
{
/* Look for owner of alias */
- (void) strlcpy(obuf, "owner-", sizeof obuf);
- (void) strlcat(obuf, a->q_user, sizeof obuf);
- if (aliaslookup(obuf, &status) != NULL)
+ (void) sm_strlcpyn(obuf, sizeof obuf, 2, "owner-", a->q_user);
+ if (aliaslookup(obuf, &status, a->q_host) != NULL)
{
if (LogLevel > 8)
syslog(LOG_WARNING,
"possible spam from <> to list: %s, redirected to %s\n",
a->q_user, obuf);
- a->q_user = newstr(obuf);
+ a->q_user = sm_rpool_strdup_x(e->e_rpool, obuf);
}
}
#endif /* _FFR_REDIRECTEMPTY */
- p = aliaslookup(a->q_user, &status);
+ p = aliaslookup(a->q_user, &status, a->q_host);
if (status == EX_TEMPFAIL || status == EX_UNAVAILABLE)
{
a->q_state = QS_QUEUEUP;
if (e->e_message == NULL)
- e->e_message = newstr("alias database unavailable");
+ e->e_message = "alias database unavailable";
+
+ /* XXX msg only per recipient? */
+ if (a->q_message == NULL)
+ a->q_message = "alias database unavailable";
return;
}
if (p == NULL)
@@ -121,8 +123,8 @@ alias(a, sendq, aliaslevel, e)
*/
if (tTd(27, 1))
- dprintf("%s (%s, %s) aliased to %s\n",
- a->q_paddr, a->q_host, a->q_user, p);
+ sm_dprintf("%s (%s, %s) aliased to %s\n",
+ a->q_paddr, a->q_host, a->q_user, p);
if (bitset(EF_VRFYONLY, e->e_flags))
{
a->q_state = QS_VERIFIED;
@@ -131,13 +133,13 @@ alias(a, sendq, aliaslevel, e)
message("aliased to %s", shortenstring(p, MAXSHORTSTR));
if (LogLevel > 10)
sm_syslog(LOG_INFO, e->e_id,
- "alias %.100s => %s",
- a->q_paddr, shortenstring(p, MAXSHORTSTR));
+ "alias %.100s => %s",
+ a->q_paddr, shortenstring(p, MAXSHORTSTR));
a->q_flags &= ~QSELFREF;
if (tTd(27, 5))
{
- dprintf("alias: QS_EXPANDED ");
- printaddr(a, FALSE);
+ sm_dprintf("alias: QS_EXPANDED ");
+ printaddr(a, false);
}
a->q_state = QS_EXPANDED;
@@ -153,6 +155,7 @@ alias(a, sendq, aliaslevel, e)
a->q_flags |= QGOODUID|QALIAS;
(void) sendtolist(p, a, sendq, aliaslevel + 1, e);
+
if (bitset(QSELFREF, a->q_flags) && QS_IS_EXPANDED(a->q_state))
a->q_state = QS_OK;
@@ -160,34 +163,35 @@ alias(a, sendq, aliaslevel, e)
** Look for owner of alias
*/
- (void) strlcpy(obuf, "owner-", sizeof obuf);
if (strncmp(a->q_user, "owner-", 6) == 0 ||
- strlen(a->q_user) > (SIZE_T) sizeof obuf - 7)
- (void) strlcat(obuf, "owner", sizeof obuf);
+ strlen(a->q_user) > sizeof obuf - 7)
+ (void) sm_strlcpy(obuf, "owner-owner", sizeof obuf);
else
- (void) strlcat(obuf, a->q_user, sizeof obuf);
- owner = aliaslookup(obuf, &status);
+ (void) sm_strlcpyn(obuf, sizeof obuf, 2, "owner-", a->q_user);
+ owner = aliaslookup(obuf, &status, a->q_host);
if (owner == NULL)
return;
/* reflect owner into envelope sender */
if (strpbrk(owner, ",:/|\"") != NULL)
owner = obuf;
- a->q_owner = newstr(owner);
+ a->q_owner = sm_rpool_strdup_x(e->e_rpool, owner);
/* announce delivery to this alias; NORECEIPT bit set later */
if (e->e_xfp != NULL)
- fprintf(e->e_xfp, "Message delivered to mailing list %s\n",
- a->q_paddr);
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "Message delivered to mailing list %s\n",
+ a->q_paddr);
e->e_flags |= EF_SENDRECEIPT;
a->q_flags |= QDELIVERED|QEXPANDED;
}
- /*
+/*
** ALIASLOOKUP -- look up a name in the alias file.
**
** Parameters:
** name -- the name to look up.
** pstat -- a pointer to a place to put the status.
+** av -- argument for %1 expansion.
**
** Returns:
** the value of name.
@@ -201,11 +205,16 @@ alias(a, sendq, aliaslevel, e)
*/
static char *
-aliaslookup(name, pstat)
+aliaslookup(name, pstat, av)
char *name;
int *pstat;
+ char *av;
{
static MAP *map = NULL;
+#if _FFR_ALIAS_DETAIL
+ int i;
+ char *argv[4];
+#endif /* _FFR_ALIAS_DETAIL */
if (map == NULL)
{
@@ -218,12 +227,24 @@ aliaslookup(name, pstat)
DYNOPENMAP(map);
/* special case POstMastER -- always use lower case */
- if (strcasecmp(name, "postmaster") == 0)
+ if (sm_strcasecmp(name, "postmaster") == 0)
name = "postmaster";
+#if _FFR_ALIAS_DETAIL
+ i = 0;
+ argv[i++] = name;
+ argv[i++] = av;
+
+ /* XXX '+' is hardwired here as delimiter! */
+ if (av != NULL && *av == '+')
+ argv[i++] = av + 1;
+ argv[i++] = NULL;
+ return (*map->map_class->map_lookup)(map, name, argv, pstat);
+#else /* _FFR_ALIAS_DETAIL */
return (*map->map_class->map_lookup)(map, name, NULL, pstat);
+#endif /* _FFR_ALIAS_DETAIL */
}
- /*
+/*
** SETALIAS -- set up an alias map
**
** Called when reading configuration file.
@@ -245,7 +266,7 @@ setalias(spec)
STAB *s;
if (tTd(27, 8))
- dprintf("setalias(%s)\n", spec);
+ sm_dprintf("setalias(%s)\n", spec);
for (p = spec; p != NULL; )
{
@@ -265,8 +286,8 @@ setalias(spec)
}
if (AliasFileMap == NULL)
{
- (void) strlcpy(buf, "aliases.files sequence",
- sizeof buf);
+ (void) sm_strlcpy(buf, "aliases.files sequence",
+ sizeof buf);
AliasFileMap = makemapentry(buf);
if (AliasFileMap == NULL)
{
@@ -274,7 +295,7 @@ setalias(spec)
return;
}
}
- (void) snprintf(buf, sizeof buf, "Alias%d", NAliasFileMaps);
+ (void) sm_snprintf(buf, sizeof buf, "Alias%d", NAliasFileMaps);
s = stab(buf, ST_MAP, ST_ENTER);
map = &s->s_map;
memset(map, '\0', sizeof *map);
@@ -296,7 +317,7 @@ setalias(spec)
/* find end of spec */
if (p != NULL)
{
- bool quoted = FALSE;
+ bool quoted = false;
for (; *p != '\0'; p++)
{
@@ -320,7 +341,7 @@ setalias(spec)
*p++ = '\0';
if (tTd(27, 20))
- dprintf(" map %s:%s %s\n", class, s->s_name, spec);
+ sm_dprintf(" map %s:%s %s\n", class, s->s_name, spec);
/* look up class */
s = stab(class, ST_MAPCLASS, ST_FIND);
@@ -336,15 +357,16 @@ setalias(spec)
else
{
map->map_class = &s->s_mapclass;
+ map->map_mflags |= MF_ALIAS;
if (map->map_class->map_parse(map, spec))
{
- map->map_mflags |= MF_VALID|MF_ALIAS;
+ map->map_mflags |= MF_VALID;
AliasFileMap->map_stack[NAliasFileMaps++] = map;
}
}
}
}
- /*
+/*
** ALIASWAIT -- wait for distinguished @:@ token to appear.
**
** This can decide to reopen or rebuild the alias file
@@ -358,8 +380,8 @@ setalias(spec)
** just checking to see if it should be created.
**
** Returns:
-** TRUE -- if the database is open when we return.
-** FALSE -- if the database is closed when we return.
+** true -- if the database is open when we return.
+** false -- if the database is closed when we return.
*/
bool
@@ -368,14 +390,14 @@ aliaswait(map, ext, isopen)
char *ext;
bool isopen;
{
- bool attimeout = FALSE;
+ bool attimeout = false;
time_t mtime;
struct stat stb;
char buf[MAXNAME + 1];
if (tTd(27, 3))
- dprintf("aliaswait(%s:%s)\n",
- map->map_class->map_cname, map->map_file);
+ sm_dprintf("aliaswait(%s:%s)\n",
+ map->map_class->map_cname, map->map_file);
if (bitset(MF_ALIASWAIT, map->map_mflags))
return isopen;
map->map_mflags |= MF_ALIASWAIT;
@@ -383,8 +405,9 @@ aliaswait(map, ext, isopen)
if (SafeAlias > 0)
{
auto int st;
- time_t toolong = curtime() + SafeAlias;
unsigned int sleeptime = 2;
+ unsigned int loopcount = 0; /* only used for debugging */
+ time_t toolong = curtime() + SafeAlias;
while (isopen &&
map->map_class->map_lookup(map, "@", NULL, &st) == NULL)
@@ -392,7 +415,7 @@ aliaswait(map, ext, isopen)
if (curtime() > toolong)
{
/* we timed out */
- attimeout = TRUE;
+ attimeout = true;
break;
}
@@ -402,8 +425,11 @@ aliaswait(map, ext, isopen)
*/
if (tTd(27, 2))
- dprintf("aliaswait: sleeping for %u seconds\n",
- sleeptime);
+ {
+ loopcount++;
+ sm_dprintf("aliaswait: sleeping for %u seconds (loopcount = %u)\n",
+ sleeptime, loopcount);
+ }
map->map_mflags |= MF_CLOSING;
map->map_class->map_close(map);
@@ -420,64 +446,31 @@ aliaswait(map, ext, isopen)
if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
{
if (tTd(27, 3))
- dprintf("aliaswait: not rebuildable\n");
+ sm_dprintf("aliaswait: not rebuildable\n");
map->map_mflags &= ~MF_ALIASWAIT;
return isopen;
}
if (stat(map->map_file, &stb) < 0)
{
if (tTd(27, 3))
- dprintf("aliaswait: no source file\n");
+ sm_dprintf("aliaswait: no source file\n");
map->map_mflags &= ~MF_ALIASWAIT;
return isopen;
}
mtime = stb.st_mtime;
- snprintf(buf, sizeof buf, "%s%s",
- map->map_file, ext == NULL ? "" : ext);
+ (void) sm_strlcpyn(buf, sizeof buf, 2,
+ map->map_file, ext == NULL ? "" : ext);
if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout)
{
-#if !_FFR_REMOVE_AUTOREBUILD
- /* database is out of date */
- if (AutoRebuild &&
- stb.st_ino != 0 &&
- (stb.st_uid == geteuid() ||
- (geteuid() == 0 && stb.st_uid == TrustedUid)))
- {
- bool oldSuprErrs;
-
- message("auto-rebuilding alias database %s", buf);
- oldSuprErrs = SuprErrs;
- SuprErrs = TRUE;
- if (isopen)
- {
- map->map_mflags |= MF_CLOSING;
- map->map_class->map_close(map);
- map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
- }
- (void) rebuildaliases(map, TRUE);
- isopen = map->map_class->map_open(map, O_RDONLY);
- SuprErrs = oldSuprErrs;
- }
- else
- {
- if (LogLevel > 3)
- sm_syslog(LOG_INFO, NOQID,
- "alias database %s out of date",
- buf);
- message("Warning: alias database %s out of date", buf);
- }
-#else /* !_FFR_REMOVE_AUTOREBUILD */
if (LogLevel > 3)
sm_syslog(LOG_INFO, NOQID,
- "alias database %s out of date",
- buf);
+ "alias database %s out of date", buf);
message("Warning: alias database %s out of date", buf);
-#endif /* !_FFR_REMOVE_AUTOREBUILD */
}
map->map_mflags &= ~MF_ALIASWAIT;
return isopen;
}
- /*
+/*
** REBUILDALIASES -- rebuild the alias database.
**
** Parameters:
@@ -485,7 +478,7 @@ aliaswait(map, ext, isopen)
** automatic -- set if this was automatically generated.
**
** Returns:
-** TRUE if successful; FALSE otherwise.
+** true if successful; false otherwise.
**
** Side Effects:
** Reads the text version of the database, builds the
@@ -497,9 +490,9 @@ rebuildaliases(map, automatic)
register MAP *map;
bool automatic;
{
- FILE *af;
- bool nolock = FALSE;
- bool success = FALSE;
+ SM_FILE_T *af;
+ bool nolock = false;
+ bool success = false;
long sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK;
sigfunc_t oldsigint, oldsigquit;
#ifdef SIGTSTP
@@ -507,7 +500,7 @@ rebuildaliases(map, automatic)
#endif /* SIGTSTP */
if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
- return FALSE;
+ return false;
if (!bitnset(DBS_LINKEDALIASFILEINWRITABLEDIR, DontBlameSendmail))
sff |= SFF_NOWLINK;
@@ -527,25 +520,26 @@ rebuildaliases(map, automatic)
int saveerr = errno;
if (tTd(27, 1))
- dprintf("Can't open %s: %s\n",
- map->map_file, errstring(saveerr));
+ sm_dprintf("Can't open %s: %s\n",
+ map->map_file, sm_errstring(saveerr));
if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags))
message("newaliases: cannot open %s: %s",
- map->map_file, errstring(saveerr));
+ map->map_file, sm_errstring(saveerr));
errno = 0;
- return FALSE;
+ return false;
}
- nolock = TRUE;
+ nolock = true;
if (tTd(27, 1) ||
- fstat(fileno(af), &stb) < 0 ||
+ fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &stb) < 0 ||
bitset(S_IWUSR|S_IWGRP|S_IWOTH, stb.st_mode))
message("warning: cannot lock %s: %s",
- map->map_file, errstring(errno));
+ map->map_file, sm_errstring(errno));
}
/* see if someone else is rebuilding the alias file */
if (!nolock &&
- !lockfile(fileno(af), map->map_file, NULL, LOCK_EX|LOCK_NB))
+ !lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), map->map_file,
+ NULL, LOCK_EX|LOCK_NB))
{
/* yes, they are -- wait until done */
message("Alias file %s is locked (maybe being rebuilt)",
@@ -553,18 +547,18 @@ rebuildaliases(map, automatic)
if (OpMode != MD_INITALIAS)
{
/* wait for other rebuild to complete */
- (void) lockfile(fileno(af), map->map_file, NULL,
- LOCK_EX);
+ (void) lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL),
+ map->map_file, NULL, LOCK_EX);
}
- (void) fclose(af);
+ (void) sm_io_close(af, SM_TIME_DEFAULT);
errno = 0;
- return FALSE;
+ return false;
}
- oldsigint = setsignal(SIGINT, SIG_IGN);
- oldsigquit = setsignal(SIGQUIT, SIG_IGN);
+ oldsigint = sm_signal(SIGINT, SIG_IGN);
+ oldsigquit = sm_signal(SIGQUIT, SIG_IGN);
#ifdef SIGTSTP
- oldsigtstp = setsignal(SIGTSTP, SIG_IGN);
+ oldsigtstp = sm_signal(SIGTSTP, SIG_IGN);
#endif /* SIGTSTP */
if (map->map_class->map_open(map, O_RDWR))
@@ -577,22 +571,22 @@ rebuildaliases(map, automatic)
username());
}
map->map_mflags |= MF_OPEN|MF_WRITABLE;
- map->map_pid = getpid();
- readaliases(map, af, !automatic, TRUE);
- success = TRUE;
+ map->map_pid = CurrentPid;
+ readaliases(map, af, !automatic, true);
+ success = true;
}
else
{
if (tTd(27, 1))
- dprintf("Can't create database for %s: %s\n",
- map->map_file, errstring(errno));
+ sm_dprintf("Can't create database for %s: %s\n",
+ map->map_file, sm_errstring(errno));
if (!automatic)
syserr("Cannot create database for alias file %s",
map->map_file);
}
/* close the file, thus releasing locks */
- (void) fclose(af);
+ (void) sm_io_close(af, SM_TIME_DEFAULT);
/* add distinguished entries and close the database */
if (bitset(MF_OPEN, map->map_mflags))
@@ -603,14 +597,14 @@ rebuildaliases(map, automatic)
}
/* restore the old signals */
- (void) setsignal(SIGINT, oldsigint);
- (void) setsignal(SIGQUIT, oldsigquit);
-# ifdef SIGTSTP
- (void) setsignal(SIGTSTP, oldsigtstp);
-# endif /* SIGTSTP */
+ (void) sm_signal(SIGINT, oldsigint);
+ (void) sm_signal(SIGQUIT, oldsigquit);
+#ifdef SIGTSTP
+ (void) sm_signal(SIGTSTP, oldsigtstp);
+#endif /* SIGTSTP */
return success;
}
- /*
+/*
** READALIASES -- read and process the alias file.
**
** This routine implements the part of initaliases that occurs
@@ -634,7 +628,7 @@ rebuildaliases(map, automatic)
void
readaliases(map, af, announcestats, logstats)
register MAP *map;
- FILE *af;
+ SM_FILE_T *af;
bool announcestats;
bool logstats;
{
@@ -652,52 +646,56 @@ readaliases(map, af, announcestats, logstats)
FileName = map->map_file;
LineNumber = 0;
naliases = bytes = longest = 0;
- skipping = FALSE;
- while (fgets(line, sizeof line, af) != NULL)
+ skipping = false;
+ while (sm_io_fgets(af, SM_TIME_DEFAULT, line, sizeof line) != NULL)
{
int lhssize, rhssize;
int c;
LineNumber++;
p = strchr(line, '\n');
+
+ /* XXX what if line="a\\" ? */
while (p != NULL && p > line && p[-1] == '\\')
{
p--;
- if (fgets(p, SPACELEFT(line, p), af) == NULL)
+ if (sm_io_fgets(af, SM_TIME_DEFAULT, p,
+ SPACELEFT(line, p)) == NULL)
break;
LineNumber++;
p = strchr(p, '\n');
}
if (p != NULL)
*p = '\0';
- else if (!feof(af))
+ else if (!sm_io_eof(af))
{
errno = 0;
syserr("554 5.3.0 alias line too long");
/* flush to end of line */
- while ((c = getc(af)) != EOF && c != '\n')
+ while ((c = sm_io_getc(af, SM_TIME_DEFAULT)) !=
+ SM_IO_EOF && c != '\n')
continue;
/* skip any continuation lines */
- skipping = TRUE;
+ skipping = true;
continue;
}
switch (line[0])
{
case '#':
case '\0':
- skipping = FALSE;
+ skipping = false;
continue;
case ' ':
case '\t':
if (!skipping)
syserr("554 5.3.5 Non-continuation line starts with space");
- skipping = TRUE;
+ skipping = true;
continue;
}
- skipping = FALSE;
+ skipping = false;
/*
** Process the LHS
@@ -715,7 +713,8 @@ readaliases(map, af, announcestats, logstats)
syserr("554 5.3.5 missing colon");
continue;
}
- if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv) == NULL)
+ if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv, true)
+ == NULL)
{
syserr("554 5.3.5 %.40s... illegal alias name", line);
continue;
@@ -751,7 +750,8 @@ readaliases(map, af, announcestats, logstats)
if (*p == '\0')
break;
if (parseaddr(p, &bl, RF_COPYNONE, ',',
- &delimptr, CurEnv) == NULL)
+ &delimptr, CurEnv, true)
+ == NULL)
usrerr("553 5.3.5 %s... bad address", p);
p = delimptr;
}
@@ -762,24 +762,26 @@ readaliases(map, af, announcestats, logstats)
}
/* see if there should be a continuation line */
- c = getc(af);
- if (!feof(af))
- (void) ungetc(c, af);
+ c = sm_io_getc(af, SM_TIME_DEFAULT);
+ if (!sm_io_eof(af))
+ (void) sm_io_ungetc(af, SM_TIME_DEFAULT, c);
if (c != ' ' && c != '\t')
break;
/* read continuation line */
- if (fgets(p, sizeof line - (p - line), af) == NULL)
+ if (sm_io_fgets(af, SM_TIME_DEFAULT, p,
+ sizeof line - (p-line)) == NULL)
break;
LineNumber++;
/* check for line overflow */
- if (strchr(p, '\n') == NULL && !feof(af))
+ if (strchr(p, '\n') == NULL && !sm_io_eof(af))
{
usrerr("554 5.3.5 alias too long");
- while ((c = fgetc(af)) != EOF && c != '\n')
+ while ((c = sm_io_getc(af, SM_TIME_DEFAULT))
+ != SM_IO_EOF && c != '\n')
continue;
- skipping = TRUE;
+ skipping = true;
break;
}
}
@@ -800,7 +802,7 @@ readaliases(map, af, announcestats, logstats)
** Special case pOStmaStER -- always make it lower case.
*/
- if (strcasecmp(al.q_user, "postmaster") == 0)
+ if (sm_strcasecmp(al.q_user, "postmaster") == 0)
makelower(al.q_user);
lhssize = strlen(al.q_user);
@@ -829,12 +831,18 @@ readaliases(map, af, announcestats, logstats)
longest = rhssize;
}
+#if 0
+ /*
+ ** address strings are now stored in the envelope rpool,
+ ** and therefore cannot be freed.
+ */
if (al.q_paddr != NULL)
- sm_free(al.q_paddr);
+ sm_free(al.q_paddr); /* disabled */
if (al.q_host != NULL)
- sm_free(al.q_host);
+ sm_free(al.q_host); /* disabled */
if (al.q_user != NULL)
- sm_free(al.q_user);
+ sm_free(al.q_user); /* disabled */
+#endif /* 0 */
}
CurEnv->e_to = NULL;
@@ -847,7 +855,7 @@ readaliases(map, af, announcestats, logstats)
"%s: %ld aliases, longest %ld bytes, %ld bytes total",
map->map_file, naliases, longest, bytes);
}
- /*
+/*
** FORWARD -- Try to forward mail
**
** This is similar but not identical to aliasing.
@@ -881,11 +889,13 @@ forward(user, sendq, aliaslevel, e)
bool got_transient;
if (tTd(27, 1))
- dprintf("forward(%s)\n", user->q_paddr);
+ sm_dprintf("forward(%s)\n", user->q_paddr);
if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) ||
!QS_IS_OK(user->q_state))
return;
+ if (ForwardPath != NULL && *ForwardPath == '\0')
+ return;
if (user->q_home == NULL)
{
syserr("554 5.3.0 forward: no home");
@@ -893,13 +903,13 @@ forward(user, sendq, aliaslevel, e)
}
/* good address -- look for .forward file in home */
- define('z', user->q_home, e);
- define('u', user->q_user, e);
- define('h', user->q_host, e);
+ macdefine(&e->e_macro, A_PERM, 'z', user->q_home);
+ macdefine(&e->e_macro, A_PERM, 'u', user->q_user);
+ macdefine(&e->e_macro, A_PERM, 'h', user->q_host);
if (ForwardPath == NULL)
ForwardPath = newstr("\201z/.forward");
- got_transient = FALSE;
+ got_transient = false;
for (pp = ForwardPath; pp != NULL; pp = ep)
{
int err;
@@ -915,18 +925,18 @@ forward(user, sendq, aliaslevel, e)
if (buf[0] == '\0')
continue;
if (tTd(27, 3))
- dprintf("forward: trying %s\n", buf);
+ sm_dprintf("forward: trying %s\n", buf);
- err = include(buf, TRUE, user, sendq, aliaslevel, e);
+ err = include(buf, true, user, sendq, aliaslevel, e);
if (err == 0)
break;
else if (transienterror(err))
{
/* we may have to suspend this message */
- got_transient = TRUE;
+ got_transient = true;
if (tTd(27, 2))
- dprintf("forward: transient error on %s\n",
- buf);
+ sm_dprintf("forward: transient error on %s\n",
+ buf);
if (LogLevel > 2)
{
char *curhost = CurHostName;
@@ -934,7 +944,7 @@ forward(user, sendq, aliaslevel, e)
CurHostName = NULL;
sm_syslog(LOG_ERR, e->e_id,
"forward %s: transient error: %s",
- buf, errstring(err));
+ buf, sm_errstring(err));
CurHostName = curhost;
}
@@ -964,19 +974,18 @@ forward(user, sendq, aliaslevel, e)
case E_SM_ISEXEC:
case E_SM_WWFILE:
case E_SM_GWFILE:
- syserr("forward: %s: %s", buf, errstring(err));
+ syserr("forward: %s: %s", buf, sm_errstring(err));
break;
#endif /* _FFR_FORWARD_SYSERR */
default:
if (LogLevel > (RunAsUid == 0 ? 2 : 10))
sm_syslog(LOG_WARNING, e->e_id,
- "forward %s: %s", buf,
- errstring(err));
+ "forward %s: %s", buf,
+ sm_errstring(err));
if (Verbose)
message("forward: %s: %s",
- buf,
- errstring(err));
+ buf, sm_errstring(err));
break;
}
}
diff --git a/contrib/sendmail/src/aliases b/contrib/sendmail/src/aliases
index 1b19f41..73899d4 100644
--- a/contrib/sendmail/src/aliases
+++ b/contrib/sendmail/src/aliases
@@ -1,33 +1,66 @@
#
-# $Id: aliases,v 8.1.36.1 2000/10/16 20:18:39 gshapiro Exp $
+# $Id: aliases,v 8.4 2001/12/30 04:46:23 gshapiro Exp $
# @(#)aliases 8.2 (Berkeley) 3/5/94
#
# Aliases in this file will NOT be expanded in the header from
-# Mail, but WILL be visible over networks or from /bin/mail.
+# Mail, but WILL be visible over networks.
#
# >>>>>>>>>> The program "newaliases" must be run after
# >> NOTE >> this file is updated for any changes to
# >>>>>>>>>> show through to sendmail.
#
+#
+# See also RFC 2142, `MAILBOX NAMES FOR COMMON SERVICES, ROLES
+# AND FUNCTIONS', May 1997
+
+# Pretty much everything else in this file points to "root", so
+# you should forward root's email to the system administrator.
+# Delivering mail to root's mailbox or reading mail as root is
+# inadvisable.
-# Basic system aliases -- these MUST be present.
+# Uncomment and *CHANGE* this!
+# root: insert-human-being-here
+
+# Basic system aliases -- these MUST be present
MAILER-DAEMON: postmaster
postmaster: root
-# General redirections for pseudo accounts.
+# General redirections for pseudo accounts
bin: root
daemon: root
games: root
+mailnull: postmaster
+smmsp: postmaster
ingres: root
nobody: root
system: root
toor: root
uucp: root
-# Well-known aliases.
+# Well-known aliases
manager: root
dumper: root
operator: root
-# trap decode to catch security attacks
+# RFC 2142: BUSINESS-RELATED MAILBOX NAMES
+# info: root
+# marketing: root
+# sales: root
+# support: root
+
+# RFC 2142: NETWORK OPERATIONS MAILBOX NAMES
+abuse: root
+noc: root
+security: root
+
+# RFC 2142: SUPPORT MAILBOX NAMES FOR SPECIFIC INTERNET SERVICES
+hostmaster: root
+usenet: root
+news: usenet
+webmaster: root
+www: webmaster
+uucp: root
+ftp: root
+
+# Trap decode to catch security attacks
decode: root
diff --git a/contrib/sendmail/src/arpadate.c b/contrib/sendmail/src/arpadate.c
index c67c3b9..16082cd 100644
--- a/contrib/sendmail/src/arpadate.c
+++ b/contrib/sendmail/src/arpadate.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 1999, 2001 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
@@ -11,12 +11,10 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: arpadate.c,v 8.23.20.2 2001/05/07 22:07:26 gshapiro Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
+SM_RCSID("@(#)$Id: arpadate.c,v 8.30 2001/09/11 04:05:12 gshapiro Exp $")
+
/*
** ARPADATE -- Create date in ARPANET format
**
@@ -74,6 +72,7 @@ arpadate(ud)
** to resolve the timezone.
*/
+ /* SM_REQUIRE(ud == NULL || strlen(ud) >= 23); */
t = curtime();
if (ud == NULL)
ud = ctime(&t);
diff --git a/contrib/sendmail/src/bf.c b/contrib/sendmail/src/bf.c
new file mode 100644
index 0000000..031b1f7
--- /dev/null
+++ b/contrib/sendmail/src/bf.c
@@ -0,0 +1,846 @@
+/*
+ * 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.
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+/*
+** This is in transition. Changed from the original bf_torek.c code
+** to use sm_io function calls directly rather than through stdio
+** translation layer. Will be made a built-in file type of libsm
+** next (once safeopen() linkable from libsm).
+*/
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: bf.c,v 8.48 2001/11/04 17:10:49 ca Exp $")
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "sendmail.h"
+#include "bf.h"
+
+#include <syslog.h>
+
+/* bf io functions */
+static ssize_t sm_bfread __P((SM_FILE_T *, char *, size_t));
+static ssize_t sm_bfwrite __P((SM_FILE_T *, const char *, size_t));
+static off_t sm_bfseek __P((SM_FILE_T *, off_t, int));
+static int sm_bfclose __P((SM_FILE_T *));
+
+static int sm_bfopen __P((SM_FILE_T *, const void *, int, const void *));
+static int sm_bfsetinfo __P((SM_FILE_T *, int , void *));
+static int sm_bfgetinfo __P((SM_FILE_T *, int , void *));
+
+/*
+** Data structure for storing information about each buffered file
+** (Originally in sendmail/bf_torek.h for the curious.)
+*/
+
+struct bf
+{
+ bool bf_committed; /* Has this buffered file been committed? */
+ bool bf_ondisk; /* On disk: committed or buffer overflow */
+ long bf_flags;
+ int bf_disk_fd; /* If on disk, associated file descriptor */
+ char *bf_buf; /* Memory buffer */
+ int bf_bufsize; /* Length of above buffer */
+ int bf_buffilled; /* Bytes of buffer actually filled */
+ char *bf_filename; /* Name of buffered file, if ever committed */
+ MODE_T bf_filemode; /* Mode of buffered file, if ever committed */
+ off_t bf_offset; /* Currect file offset */
+ int bf_size; /* Total current size of file */
+ int bf_refcount; /* Reference count */
+};
+
+#ifdef BF_STANDALONE
+# define OPEN(fn, omode, cmode, sff) open(fn, omode, cmode)
+#else /* BF_STANDALONE */
+# define OPEN(fn, omode, cmode, sff) safeopen(fn, omode, cmode, sff)
+#endif /* BF_STANDALONE */
+
+struct bf_info
+{
+ char *bi_filename;
+ MODE_T bi_fmode;
+ size_t bi_bsize;
+ long bi_flags;
+};
+
+/*
+** SM_BFOPEN -- the "base" open function called by sm_io_open() for the
+** internal, file-type-specific info setup.
+**
+** Parameters:
+** fp -- file pointer being filled-in for file being open'd
+** filename -- name of the file being open'd
+** flags -- ignored
+** fmode -- file mode (stored for use later)
+** sflags -- "safeopen" flags (stored for use later)
+** rpool -- ignored (currently)
+**
+** Returns:
+** Failure: -1 and sets errno
+** Success: 0 (zero)
+*/
+
+static int
+sm_bfopen(fp, info, flags, rpool)
+ SM_FILE_T *fp;
+ const void *info;
+ int flags;
+ const void *rpool;
+{
+ char *filename;
+ MODE_T fmode;
+ size_t bsize;
+ long sflags;
+ struct bf *bfp;
+ int l;
+ struct stat st;
+
+ filename = ((struct bf_info *) info)->bi_filename;
+ fmode = ((struct bf_info *) info)->bi_fmode;
+ bsize = ((struct bf_info *) info)->bi_bsize;
+ sflags = ((struct bf_info *) info)->bi_flags;
+
+ /* Sanity checks */
+ if (*filename == '\0')
+ {
+ /* Empty filename string */
+ errno = ENOENT;
+ return -1;
+ }
+ if (stat(filename, &st) == 0)
+ {
+ /* File already exists on disk */
+ errno = EEXIST;
+ return -1;
+ }
+
+ /* Allocate memory */
+ bfp = (struct bf *) sm_malloc(sizeof(struct bf));
+ if (bfp == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* Assign data buffer */
+ /* A zero bsize is valid, just don't allocate memory */
+ if (bsize > 0)
+ {
+ bfp->bf_buf = (char *) sm_malloc(bsize);
+ if (bfp->bf_buf == NULL)
+ {
+ bfp->bf_bufsize = 0;
+ sm_free(bfp);
+ errno = ENOMEM;
+ return -1;
+ }
+ }
+ else
+ bfp->bf_buf = NULL;
+
+ /* Nearly home free, just set all the parameters now */
+ bfp->bf_committed = false;
+ bfp->bf_ondisk = false;
+ bfp->bf_refcount = 1;
+ bfp->bf_flags = sflags;
+ bfp->bf_bufsize = bsize;
+ bfp->bf_buffilled = 0;
+ l = strlen(filename) + 1;
+ bfp->bf_filename = (char *) sm_malloc(l);
+ if (bfp->bf_filename == NULL)
+ {
+ if (bfp->bf_buf != NULL)
+ sm_free(bfp->bf_buf);
+ sm_free(bfp);
+ errno = ENOMEM;
+ return -1;
+ }
+ (void) sm_strlcpy(bfp->bf_filename, filename, l);
+ bfp->bf_filemode = fmode;
+ bfp->bf_offset = 0;
+ bfp->bf_size = bsize;
+ bfp->bf_disk_fd = -1;
+ fp->f_cookie = bfp;
+
+ if (tTd(58, 8))
+ sm_dprintf("sm_bfopen(%s)\n", filename);
+
+ return 0;
+}
+
+/*
+** BFOPEN -- create a new buffered file
+**
+** Parameters:
+** filename -- the file's name
+** fmode -- what mode the file should be created as
+** bsize -- amount of buffer space to allocate (may be 0)
+** flags -- if running under sendmail, passed directly to safeopen
+**
+** Returns:
+** a SM_FILE_T * which may then be used with stdio functions,
+** or NULL on failure. SM_FILE_T * is opened for writing
+** "SM_IO_WHAT_VECTORS").
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** any value of errno specified by sm_io_setinfo_type()
+** any value of errno specified by sm_io_open()
+** any value of errno specified by sm_io_setinfo()
+*/
+
+SM_FILE_T *
+bfopen(filename, fmode, bsize, flags)
+ char *filename;
+ MODE_T fmode;
+ size_t bsize;
+ long flags;
+{
+ MODE_T omask;
+ SM_FILE_T SM_IO_SET_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
+ sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
+ SM_TIME_FOREVER);
+ struct bf_info info;
+
+ /*
+ ** Apply current umask to fmode as it may change by the time
+ ** the file is actually created. fmode becomes the true
+ ** permissions of the file, which OPEN() must obey.
+ */
+
+ omask = umask(0);
+ fmode &= ~omask;
+ (void) umask(omask);
+
+ SM_IO_INIT_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
+ sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
+ SM_TIME_FOREVER);
+ info.bi_filename = filename;
+ info.bi_fmode = fmode;
+ info.bi_bsize = bsize;
+ info.bi_flags = flags;
+
+ return sm_io_open(&vector, SM_TIME_DEFAULT, &info, SM_IO_RDWR, NULL);
+}
+
+/*
+** SM_BFGETINFO -- returns info about an open file pointer
+**
+** Parameters:
+** fp -- file pointer to get info about
+** what -- type of info to obtain
+** valp -- thing to return the info in
+*/
+
+static int
+sm_bfgetinfo(fp, what, valp)
+ SM_FILE_T *fp;
+ int what;
+ void *valp;
+{
+ struct bf *bfp;
+
+ bfp = (struct bf *) fp->f_cookie;
+ switch (what)
+ {
+ case SM_IO_WHAT_FD:
+ return bfp->bf_disk_fd;
+ default:
+ return -1;
+ }
+}
+
+/*
+** SM_BFCLOSE -- close a buffered file
+**
+** Parameters:
+** fp -- cookie of file to close
+**
+** Returns:
+** 0 to indicate success
+**
+** Side Effects:
+** deletes backing file, sm_frees memory.
+**
+** Sets errno:
+** never.
+*/
+
+static int
+sm_bfclose(fp)
+ SM_FILE_T *fp;
+{
+ struct bf *bfp;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *) fp->f_cookie;
+
+ /* Need to clean up the file */
+ if (bfp->bf_ondisk && !bfp->bf_committed)
+ unlink(bfp->bf_filename);
+ sm_free(bfp->bf_filename);
+
+ if (bfp->bf_disk_fd != -1)
+ close(bfp->bf_disk_fd);
+
+ /* Need to sm_free the buffer */
+ if (bfp->bf_bufsize > 0)
+ sm_free(bfp->bf_buf);
+
+ /* Finally, sm_free the structure */
+ sm_free(bfp);
+ return 0;
+}
+
+/*
+** SM_BFREAD -- read a buffered file
+**
+** Parameters:
+** cookie -- cookie of file to read
+** buf -- buffer to fill
+** nbytes -- how many bytes to read
+**
+** Returns:
+** number of bytes read or -1 indicate failure
+**
+** Side Effects:
+** none.
+**
+*/
+
+static ssize_t
+sm_bfread(fp, buf, nbytes)
+ SM_FILE_T *fp;
+ char *buf;
+ size_t nbytes;
+{
+ struct bf *bfp;
+ ssize_t count = 0; /* Number of bytes put in buf so far */
+ int retval;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *) fp->f_cookie;
+
+ if (bfp->bf_offset < bfp->bf_buffilled)
+ {
+ /* Need to grab some from buffer */
+ count = nbytes;
+ if ((bfp->bf_offset + count) > bfp->bf_buffilled)
+ count = bfp->bf_buffilled - bfp->bf_offset;
+
+ memcpy(buf, bfp->bf_buf + bfp->bf_offset, count);
+ }
+
+ if ((bfp->bf_offset + nbytes) > bfp->bf_buffilled)
+ {
+ /* Need to grab some from file */
+ if (!bfp->bf_ondisk)
+ {
+ /* Oops, the file doesn't exist. EOF. */
+ if (tTd(58, 8))
+ sm_dprintf("sm_bfread(%s): to disk\n",
+ bfp->bf_filename);
+ goto finished;
+ }
+
+ /* Catch a read() on an earlier failed write to disk */
+ if (bfp->bf_disk_fd < 0)
+ {
+ errno = EIO;
+ return -1;
+ }
+
+ if (lseek(bfp->bf_disk_fd,
+ bfp->bf_offset + count, SEEK_SET) < 0)
+ {
+ if ((errno == EINVAL) || (errno == ESPIPE))
+ {
+ /*
+ ** stdio won't be expecting these
+ ** errnos from read()! Change them
+ ** into something it can understand.
+ */
+
+ errno = EIO;
+ }
+ return -1;
+ }
+
+ while (count < nbytes)
+ {
+ retval = read(bfp->bf_disk_fd,
+ buf + count,
+ nbytes - count);
+ if (retval < 0)
+ {
+ /* errno is set implicitly by read() */
+ return -1;
+ }
+ else if (retval == 0)
+ goto finished;
+ else
+ count += retval;
+ }
+ }
+
+finished:
+ bfp->bf_offset += count;
+ return count;
+}
+
+/*
+** SM_BFSEEK -- seek to a position in a buffered file
+**
+** Parameters:
+** fp -- fp of file to seek
+** offset -- position to seek to
+** whence -- how to seek
+**
+** Returns:
+** new file offset or -1 indicate failure
+**
+** Side Effects:
+** none.
+**
+*/
+
+static off_t
+sm_bfseek(fp, offset, whence)
+ SM_FILE_T *fp;
+ off_t offset;
+ int whence;
+
+{
+ struct bf *bfp;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *) fp->f_cookie;
+
+ switch (whence)
+ {
+ case SEEK_SET:
+ bfp->bf_offset = offset;
+ break;
+
+ case SEEK_CUR:
+ bfp->bf_offset += offset;
+ break;
+
+ case SEEK_END:
+ bfp->bf_offset = bfp->bf_size + offset;
+ break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ return bfp->bf_offset;
+}
+
+/*
+** SM_BFWRITE -- write to a buffered file
+**
+** Parameters:
+** fp -- fp of file to write
+** buf -- data buffer
+** nbytes -- how many bytes to write
+**
+** Returns:
+** number of bytes written or -1 indicate failure
+**
+** Side Effects:
+** may create backing file if over memory limit for file.
+**
+*/
+
+static ssize_t
+sm_bfwrite(fp, buf, nbytes)
+ SM_FILE_T *fp;
+ const char *buf;
+ size_t nbytes;
+{
+ struct bf *bfp;
+ ssize_t count = 0; /* Number of bytes written so far */
+ int retval;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *) fp->f_cookie;
+
+ /* If committed, go straight to disk */
+ if (bfp->bf_committed)
+ {
+ if (lseek(bfp->bf_disk_fd, bfp->bf_offset, SEEK_SET) < 0)
+ {
+ if ((errno == EINVAL) || (errno == ESPIPE))
+ {
+ /*
+ ** stdio won't be expecting these
+ ** errnos from write()! Change them
+ ** into something it can understand.
+ */
+
+ errno = EIO;
+ }
+ return -1;
+ }
+
+ count = write(bfp->bf_disk_fd, buf, nbytes);
+ if (count < 0)
+ {
+ /* errno is set implicitly by write() */
+ return -1;
+ }
+ goto finished;
+ }
+
+ if (bfp->bf_offset < bfp->bf_bufsize)
+ {
+ /* Need to put some in buffer */
+ count = nbytes;
+ if ((bfp->bf_offset + count) > bfp->bf_bufsize)
+ count = bfp->bf_bufsize - bfp->bf_offset;
+
+ memcpy(bfp->bf_buf + bfp->bf_offset, buf, count);
+ if ((bfp->bf_offset + count) > bfp->bf_buffilled)
+ bfp->bf_buffilled = bfp->bf_offset + count;
+ }
+
+ if ((bfp->bf_offset + nbytes) > bfp->bf_bufsize)
+ {
+ /* Need to put some in file */
+ if (!bfp->bf_ondisk)
+ {
+ MODE_T omask;
+
+ /* Clear umask as bf_filemode are the true perms */
+ omask = umask(0);
+ retval = OPEN(bfp->bf_filename,
+ O_RDWR | O_CREAT | O_TRUNC,
+ bfp->bf_filemode, bfp->bf_flags);
+ (void) umask(omask);
+
+ /* Couldn't create file: failure */
+ if (retval < 0)
+ {
+ /*
+ ** stdio may not be expecting these
+ ** errnos from write()! Change to
+ ** something which it can understand.
+ ** Note that ENOSPC and EDQUOT are saved
+ ** because they are actually valid for
+ ** write().
+ */
+
+ if (!(errno == ENOSPC
+#ifdef EDQUOT
+ || errno == EDQUOT
+#endif /* EDQUOT */
+ ))
+ errno = EIO;
+
+ return -1;
+ }
+ bfp->bf_disk_fd = retval;
+ bfp->bf_ondisk = true;
+ }
+
+ /* Catch a write() on an earlier failed write to disk */
+ if (bfp->bf_ondisk && bfp->bf_disk_fd < 0)
+ {
+ errno = EIO;
+ return -1;
+ }
+
+ if (lseek(bfp->bf_disk_fd,
+ bfp->bf_offset + count, SEEK_SET) < 0)
+ {
+ if ((errno == EINVAL) || (errno == ESPIPE))
+ {
+ /*
+ ** stdio won't be expecting these
+ ** errnos from write()! Change them into
+ ** something which it can understand.
+ */
+
+ errno = EIO;
+ }
+ return -1;
+ }
+
+ while (count < nbytes)
+ {
+ retval = write(bfp->bf_disk_fd, buf + count,
+ nbytes - count);
+ if (retval < 0)
+ {
+ /* errno is set implicitly by write() */
+ return -1;
+ }
+ else
+ count += retval;
+ }
+ }
+
+finished:
+ bfp->bf_offset += count;
+ if (bfp->bf_offset > bfp->bf_size)
+ bfp->bf_size = bfp->bf_offset;
+ return count;
+}
+
+/*
+** BFREWIND -- rewinds the SM_FILE_T *
+**
+** Parameters:
+** fp -- SM_FILE_T * to rewind
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** rewinds the SM_FILE_T * and puts it into read mode. Normally one
+** would bfopen() a file, write to it, then bfrewind() and
+** fread(). If fp is not a buffered file, this is equivalent to
+** rewind().
+**
+** Sets errno:
+** any value of errno specified by sm_io_rewind()
+*/
+
+int
+bfrewind(fp)
+ SM_FILE_T *fp;
+{
+ (void) sm_io_flush(fp, SM_TIME_DEFAULT);
+ sm_io_clearerr(fp); /* quicker just to do it */
+ return sm_io_seek(fp, SM_TIME_DEFAULT, 0, SM_IO_SEEK_SET);
+}
+
+/*
+** SM_BFCOMMIT -- "commits" the buffered file
+**
+** Parameters:
+** fp -- SM_FILE_T * to commit to disk
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** Forces the given SM_FILE_T * to be written to disk if it is not
+** already, and ensures that it will be kept after closing. If
+** fp is not a buffered file, this is a no-op.
+**
+** Sets errno:
+** any value of errno specified by open()
+** any value of errno specified by write()
+** any value of errno specified by lseek()
+*/
+
+static int
+sm_bfcommit(fp)
+ SM_FILE_T *fp;
+{
+ struct bf *bfp;
+ int retval;
+ int byteswritten;
+
+ /* Get associated bf structure */
+ bfp = (struct bf *) fp->f_cookie;
+
+ /* If already committed, noop */
+ if (bfp->bf_committed)
+ return 0;
+
+ /* Do we need to open a file? */
+ if (!bfp->bf_ondisk)
+ {
+ MODE_T omask;
+ struct stat st;
+
+ if (tTd(58, 8))
+ {
+ sm_dprintf("bfcommit(%s): to disk\n", bfp->bf_filename);
+ if (tTd(58, 32))
+ sm_dprintf("bfcommit(): filemode %o flags %ld\n",
+ bfp->bf_filemode, bfp->bf_flags);
+ }
+
+ if (stat(bfp->bf_filename, &st) == 0)
+ {
+ errno = EEXIST;
+ return -1;
+ }
+
+ /* Clear umask as bf_filemode are the true perms */
+ omask = umask(0);
+ retval = OPEN(bfp->bf_filename, O_RDWR | O_CREAT | O_TRUNC,
+ bfp->bf_filemode, bfp->bf_flags);
+ (void) umask(omask);
+
+ /* Couldn't create file: failure */
+ if (retval < 0)
+ {
+ /* errno is set implicitly by open() */
+ return -1;
+ }
+
+ bfp->bf_disk_fd = retval;
+ bfp->bf_ondisk = true;
+ }
+
+ /* Write out the contents of our buffer, if we have any */
+ if (bfp->bf_buffilled > 0)
+ {
+ byteswritten = 0;
+
+ if (lseek(bfp->bf_disk_fd, 0, SEEK_SET) < 0)
+ {
+ /* errno is set implicitly by lseek() */
+ return -1;
+ }
+
+ while (byteswritten < bfp->bf_buffilled)
+ {
+ retval = write(bfp->bf_disk_fd,
+ bfp->bf_buf + byteswritten,
+ bfp->bf_buffilled - byteswritten);
+ if (retval < 0)
+ {
+ /* errno is set implicitly by write() */
+ return -1;
+ }
+ else
+ byteswritten += retval;
+ }
+ }
+ bfp->bf_committed = true;
+
+ /* Invalidate buf; all goes to file now */
+ bfp->bf_buffilled = 0;
+ if (bfp->bf_bufsize > 0)
+ {
+ /* Don't need buffer anymore; free it */
+ bfp->bf_bufsize = 0;
+ sm_free(bfp->bf_buf);
+ }
+ return 0;
+}
+
+/*
+** SM_BFTRUNCATE -- rewinds and truncates the SM_FILE_T *
+**
+** Parameters:
+** fp -- SM_FILE_T * to truncate
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** rewinds the SM_FILE_T *, truncates it to zero length, and puts
+** it into write mode.
+**
+** Sets errno:
+** any value of errno specified by fseek()
+** any value of errno specified by ftruncate()
+*/
+
+static int
+sm_bftruncate(fp)
+ SM_FILE_T *fp;
+{
+ struct bf *bfp;
+
+ if (bfrewind(fp) < 0)
+ return -1;
+
+ /* Get bf structure */
+ bfp = (struct bf *) fp->f_cookie;
+ bfp->bf_buffilled = 0;
+ bfp->bf_size = 0;
+
+ /* Need to zero the buffer */
+ if (bfp->bf_bufsize > 0)
+ memset(bfp->bf_buf, '\0', bfp->bf_bufsize);
+ if (bfp->bf_ondisk)
+ {
+#if NOFTRUNCATE
+ /* XXX: Not much we can do except rewind it */
+ errno = EINVAL;
+ return -1;
+#else /* NOFTRUNCATE */
+ return ftruncate(bfp->bf_disk_fd, 0);
+#endif /* NOFTRUNCATE */
+ }
+ else
+ return 0;
+}
+
+/*
+** SM_BFSETINFO -- set/change info for an open file pointer
+**
+** Parameters:
+** fp -- file pointer to get info about
+** what -- type of info to set/change
+** valp -- thing to set/change the info to
+**
+*/
+
+static int
+sm_bfsetinfo(fp, what, valp)
+ SM_FILE_T *fp;
+ int what;
+ void *valp;
+{
+ struct bf *bfp;
+ int bsize;
+
+ /* Get bf structure */
+ bfp = (struct bf *) fp->f_cookie;
+ switch (what)
+ {
+ case SM_BF_SETBUFSIZE:
+ bsize = *((int *) valp);
+ bfp->bf_bufsize = bsize;
+
+ /* A zero bsize is valid, just don't allocate memory */
+ if (bsize > 0)
+ {
+ bfp->bf_buf = (char *) sm_malloc(bsize);
+ if (bfp->bf_buf == NULL)
+ {
+ bfp->bf_bufsize = 0;
+ errno = ENOMEM;
+ return -1;
+ }
+ }
+ else
+ bfp->bf_buf = NULL;
+ return 0;
+ case SM_BF_COMMIT:
+ return sm_bfcommit(fp);
+ case SM_BF_TRUNCATE:
+ return sm_bftruncate(fp);
+ case SM_BF_TEST:
+ return 1; /* always */
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
diff --git a/contrib/sendmail/src/bf.h b/contrib/sendmail/src/bf.h
index 8fc86ae..030a6f8 100644
--- a/contrib/sendmail/src/bf.h
+++ b/contrib/sendmail/src/bf.h
@@ -1,27 +1,33 @@
/*
- * Copyright (c) 1999, 2001 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*
- * $Id: bf.h,v 8.5.16.2 2001/02/14 04:07:27 gshapiro Exp $
+ * $Id: bf.h,v 8.15 2001/05/31 21:02:53 ca Exp $
*
* Contributed by Exactis.com, Inc.
*
*/
#ifndef BF_H
-#define BF_H 1
+# define BF_H 1
-extern FILE *bfopen __P((char *, int, size_t, long));
-extern FILE *bfdup __P((FILE *));
-extern int bfcommit __P((FILE *));
-extern int bfrewind __P((FILE *));
-extern int bftruncate __P((FILE *));
-extern int bffsync __P((FILE *));
-extern int bfclose __P((FILE *));
-extern bool bftest __P((FILE *));
+extern SM_FILE_T *bfopen __P((char *, MODE_T, size_t, long));
+extern SM_FILE_T *bfdup __P((SM_FILE_T *));
+extern int bfcommit __P((SM_FILE_T *));
+extern int bfrewind __P((SM_FILE_T *));
+extern int bftruncate __P((SM_FILE_T *));
+extern int bfclose __P((SM_FILE_T *));
+extern bool bftest __P((SM_FILE_T *));
-#endif /* BF_H */
+/* "what" flags for sm_io_setinfo() for the SM_FILE_TYPE file type */
+# define SM_BF_SETBUFSIZE 1000 /* set buffer size */
+# define SM_BF_COMMIT 1001 /* commit file to disk */
+# define SM_BF_TRUNCATE 1002 /* truncate the file */
+# define SM_BF_TEST 1003 /* historical support; temp */
+
+# define BF_FILE_TYPE "SendmailBufferedFile"
+#endif /* ! BF_H */
diff --git a/contrib/sendmail/src/collect.c b/contrib/sendmail/src/collect.c
index fcf3117..95a14ae 100644
--- a/contrib/sendmail/src/collect.c
+++ b/contrib/sendmail/src/collect.c
@@ -11,18 +11,232 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: collect.c,v 8.136.4.22 2001/06/07 21:01:02 ca Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
+SM_RCSID("@(#)$Id: collect.c,v 8.237 2001/12/10 19:56:03 ca Exp $")
static void collecttimeout __P((time_t));
-static void dferror __P((FILE *volatile, char *, ENVELOPE *));
+static void dferror __P((SM_FILE_T *volatile, char *, ENVELOPE *));
static void eatfrom __P((char *volatile, ENVELOPE *));
+static void collect_doheader __P((ENVELOPE *));
+static SM_FILE_T *collect_dfopen __P((ENVELOPE *));
+static SM_FILE_T *collect_eoh __P((ENVELOPE *, int, int));
+
+/*
+** COLLECT_EOH -- end-of-header processing in collect()
+**
+** Called by collect() when it encounters the blank line
+** separating the header from the message body, or when it
+** encounters EOF in a message that contains only a header.
+**
+** Parameters:
+** e -- envelope
+** numhdrs -- number of headers
+** hdrslen -- length of headers
+**
+** Results:
+** NULL, or handle to open data file
+**
+** Side Effects:
+** end-of-header check ruleset is invoked.
+** envelope state is updated.
+** headers may be added and deleted.
+** selects the queue.
+** opens the data file.
+*/
+
+static SM_FILE_T *
+collect_eoh(e, numhdrs, hdrslen)
+ ENVELOPE *e;
+ int numhdrs;
+ int hdrslen;
+{
+ char hnum[16];
+ char hsize[16];
+
+ /* call the end-of-header check ruleset */
+ (void) sm_snprintf(hnum, sizeof hnum, "%d", numhdrs);
+ (void) sm_snprintf(hsize, sizeof hsize, "%d", hdrslen);
+ if (tTd(30, 10))
+ sm_dprintf("collect: rscheck(\"check_eoh\", \"%s $| %s\")\n",
+ hnum, hsize);
+ (void) rscheck("check_eoh", hnum, hsize, e, false, true, 3, NULL,
+ e->e_id);
+
+ /*
+ ** Process the header,
+ ** select the queue, open the data file.
+ */
+
+ collect_doheader(e);
+ return collect_dfopen(e);
+}
+
+/*
+** COLLECT_DOHEADER -- process header in collect()
+**
+** Called by collect() after it has finished parsing the header,
+** but before it selects the queue and creates the data file.
+** The results of processing the header will affect queue selection.
+**
+** Parameters:
+** e -- envelope
+**
+** Results:
+** none.
+**
+** Side Effects:
+** envelope state is updated.
+** headers may be added and deleted.
+*/
+
+static void
+collect_doheader(e)
+ ENVELOPE *e;
+{
+ /*
+ ** Find out some information from the headers.
+ ** Examples are who is the from person & the date.
+ */
+
+ eatheader(e, true, false);
+
+ if (GrabTo && e->e_sendqueue == NULL)
+ usrerr("No recipient addresses found in header");
+
+ /* collect statistics */
+ if (OpMode != MD_VERIFY)
+ markstats(e, (ADDRESS *) NULL, STATS_NORMAL);
+
+ /*
+ ** If we have a Return-Receipt-To:, turn it into a DSN.
+ */
+
+ if (RrtImpliesDsn && hvalue("return-receipt-to", e->e_header) != NULL)
+ {
+ ADDRESS *q;
+
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ if (!bitset(QHASNOTIFY, q->q_flags))
+ q->q_flags |= QHASNOTIFY|QPINGONSUCCESS;
+ }
+
+ /*
+ ** Add an appropriate recipient line if we have none.
+ */
+
+ if (hvalue("to", e->e_header) != NULL ||
+ hvalue("cc", e->e_header) != NULL ||
+ hvalue("apparently-to", e->e_header) != NULL)
+ {
+ /* have a valid recipient header -- delete Bcc: headers */
+ e->e_flags |= EF_DELETE_BCC;
+ }
+ else if (hvalue("bcc", e->e_header) == NULL)
+ {
+ /* no valid recipient headers */
+ register ADDRESS *q;
+ char *hdr = NULL;
+
+ /* create a recipient field */
+ switch (NoRecipientAction)
+ {
+ case NRA_ADD_APPARENTLY_TO:
+ hdr = "Apparently-To";
+ break;
+
+ case NRA_ADD_TO:
+ hdr = "To";
+ break;
+
+ case NRA_ADD_BCC:
+ addheader("Bcc", " ", 0, e);
+ break;
+
+ case NRA_ADD_TO_UNDISCLOSED:
+ addheader("To", "undisclosed-recipients:;", 0, e);
+ break;
+ }
+
+ if (hdr != NULL)
+ {
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (q->q_alias != NULL)
+ continue;
+ if (tTd(30, 3))
+ sm_dprintf("Adding %s: %s\n",
+ hdr, q->q_paddr);
+ addheader(hdr, q->q_paddr, 0, e);
+ }
+ }
+ }
+}
+
+/*
+** COLLECT_DFOPEN -- open the message data file
+**
+** Called by collect() after it has finished processing the header.
+** Queue selection occurs at this point, possibly based on the
+** envelope's recipient list and on header information.
+**
+** Parameters:
+** e -- envelope
+**
+** Results:
+** NULL, or a pointer to an open data file,
+** into which the message body will be written by collect().
+**
+** Side Effects:
+** Calls syserr, sets EF_FATALERRS and returns NULL
+** if there is insufficient disk space.
+** Aborts process if data file could not be opened.
+** Otherwise, the queue is selected,
+** e->e_{dfino,dfdev,msgsize,flags} are updated,
+** and a pointer to an open data file is returned.
+*/
+
+static SM_FILE_T *
+collect_dfopen(e)
+ ENVELOPE *e;
+{
+ MODE_T oldumask = 0;
+ int dfd;
+ struct stat stbuf;
+ SM_FILE_T *df;
+ char *dfname;
+
+ if (!setnewqueue(e))
+ return NULL;
+
+ dfname = queuename(e, DATAFL_LETTER);
+ if (bitset(S_IWGRP, QueueFileMode))
+ oldumask = umask(002);
+ df = bfopen(dfname, QueueFileMode, DataFileBufferSize,
+ SFF_OPENASROOT);
+ if (bitset(S_IWGRP, QueueFileMode))
+ (void) umask(oldumask);
+ if (df == NULL)
+ {
+ syserr("@Cannot create %s", dfname);
+ e->e_flags |= EF_NO_BODY_RETN;
+ flush_errors(true);
+ finis(true, true, ExitStat);
+ /* NOTREACHED */
+ }
+ dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
+ if (dfd < 0 || fstat(dfd, &stbuf) < 0)
+ e->e_dfino = -1;
+ else
+ {
+ e->e_dfdev = stbuf.st_dev;
+ e->e_dfino = stbuf.st_ino;
+ }
+ e->e_flags |= EF_HAS_DF;
+ return df;
+}
- /*
+/*
** COLLECT -- read & parse message header & make temp file.
**
** Creates a temporary file name and copies the standard
@@ -42,13 +256,20 @@ static void eatfrom __P((char *volatile, ENVELOPE *));
** none.
**
** Side Effects:
-** Temp file is created and filled.
-** The from person may be set.
+** If successful,
+** - Data file is created and filled, and e->e_dfp is set.
+** - The from person may be set.
+** If the "enough disk space" check fails,
+** - syserr is called.
+** - e->e_dfp is NULL.
+** - e->e_flags & EF_FATALERRS is set.
+** - collect() returns.
+** If data file cannot be created, the process is terminated.
*/
static jmp_buf CtxCollectTimeout;
static bool volatile CollectProgress;
-static EVENT *volatile CollectTimeout = NULL;
+static SM_EVENT *volatile CollectTimeout = NULL;
/* values for input state machine */
#define IS_NORM 0 /* middle of line */
@@ -65,83 +286,44 @@ static EVENT *volatile CollectTimeout = NULL;
void
collect(fp, smtpmode, hdrp, e)
- FILE *fp;
+ SM_FILE_T *fp;
bool smtpmode;
HDR **hdrp;
register ENVELOPE *e;
{
- register FILE *volatile df;
- volatile bool ignrdot = smtpmode ? FALSE : IgnrDot;
- volatile time_t dbto = smtpmode ? TimeOuts.to_datablock : 0;
+ register SM_FILE_T *volatile df;
+ volatile bool ignrdot;
+ volatile time_t dbto;
register char *volatile bp;
- volatile int c = EOF;
- volatile bool inputerr = FALSE;
+ volatile int c;
+ volatile bool inputerr;
bool headeronly;
char *volatile buf;
volatile int buflen;
volatile int istate;
volatile int mstate;
- volatile int hdrslen = 0;
- volatile int numhdrs = 0;
- volatile int dfd;
- volatile int rstat = EX_OK;
- u_char *volatile pbp;
- u_char peekbuf[8];
- char hsize[16];
- char hnum[16];
- char dfname[MAXPATHLEN];
+ volatile int hdrslen;
+ volatile int numhdrs;
+ volatile int afd;
+ unsigned char *volatile pbp;
+ unsigned char peekbuf[8];
char bufbuf[MAXLINE];
+ df = NULL;
+ ignrdot = smtpmode ? false : IgnrDot;
+ dbto = smtpmode ? TimeOuts.to_datablock : 0;
+ c = SM_IO_EOF;
+ inputerr = false;
headeronly = hdrp != NULL;
-
- /*
- ** Create the temp file name and create the file.
- */
-
- if (!headeronly)
- {
- struct stat stbuf;
- long sff = SFF_OPENASROOT;
-
-
- (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
-#if _FFR_QUEUE_FILE_MODE
- {
- MODE_T oldumask;
-
- if (bitset(S_IWGRP, QueueFileMode))
- oldumask = umask(002);
- df = bfopen(dfname, QueueFileMode,
- DataFileBufferSize, sff);
- if (bitset(S_IWGRP, QueueFileMode))
- (void) umask(oldumask);
- }
-#else /* _FFR_QUEUE_FILE_MODE */
- df = bfopen(dfname, FileMode, DataFileBufferSize, sff);
-#endif /* _FFR_QUEUE_FILE_MODE */
- if (df == NULL)
- {
- HoldErrs = FALSE;
- if (smtpmode)
- syserr("421 4.3.5 Unable to create data file");
- else
- syserr("Cannot create %s", dfname);
- e->e_flags |= EF_NO_BODY_RETN;
- finis(TRUE, ExitStat);
- /* NOTREACHED */
- }
- dfd = fileno(df);
- if (dfd < 0 || fstat(dfd, &stbuf) < 0)
- e->e_dfino = -1;
- else
- {
- e->e_dfdev = stbuf.st_dev;
- e->e_dfino = stbuf.st_ino;
- }
- HasEightBits = FALSE;
- e->e_msgsize = 0;
- e->e_flags |= EF_HAS_DF;
- }
+ hdrslen = 0;
+ numhdrs = 0;
+ HasEightBits = false;
+ buf = bp = bufbuf;
+ buflen = sizeof bufbuf;
+ pbp = peekbuf;
+ istate = IS_BOL;
+ mstate = SaveFrom ? MS_HEADER : MS_UFROM;
+ CollectProgress = false;
/*
** Tell ARPANET to go ahead.
@@ -151,7 +333,7 @@ collect(fp, smtpmode, hdrp, e)
message("354 Enter mail, end with \".\" on a line by itself");
if (tTd(30, 2))
- dprintf("collect\n");
+ sm_dprintf("collect\n");
/*
** Read the message.
@@ -162,13 +344,6 @@ collect(fp, smtpmode, hdrp, e)
** the larger picture (e.g., header versus body).
*/
- buf = bp = bufbuf;
- buflen = sizeof bufbuf;
- pbp = peekbuf;
- istate = IS_BOL;
- mstate = SaveFrom ? MS_HEADER : MS_UFROM;
- CollectProgress = FALSE;
-
if (dbto != 0)
{
/* handle possible input timeout */
@@ -176,52 +351,57 @@ collect(fp, smtpmode, hdrp, e)
{
if (LogLevel > 2)
sm_syslog(LOG_NOTICE, e->e_id,
- "timeout waiting for input from %s during message collect",
- CurHostName ? CurHostName : "<local machine>");
+ "timeout waiting for input from %s during message collect",
+ CURHOSTNAME);
errno = 0;
usrerr("451 4.4.1 timeout waiting for input during message collect");
goto readerr;
}
- CollectTimeout = setevent(dbto, collecttimeout, dbto);
+ CollectTimeout = sm_setevent(dbto, collecttimeout, dbto);
}
+ e->e_msgsize = 0;
for (;;)
{
if (tTd(30, 35))
- dprintf("top, istate=%d, mstate=%d\n", istate, mstate);
+ sm_dprintf("top, istate=%d, mstate=%d\n", istate,
+ mstate);
for (;;)
{
if (pbp > peekbuf)
c = *--pbp;
else
{
- while (!feof(fp) && !ferror(fp))
+ while (!sm_io_eof(fp) && !sm_io_error(fp))
{
errno = 0;
- c = getc(fp);
-
- if (c == EOF && errno == EINTR)
+ c = sm_io_getc(fp, SM_TIME_DEFAULT);
+ if (c == SM_IO_EOF && errno == EINTR)
{
/* Interrupted, retry */
- clearerr(fp);
+ sm_io_clearerr(fp);
continue;
}
break;
}
- CollectProgress = TRUE;
+ CollectProgress = true;
if (TrafficLogFile != NULL && !headeronly)
{
if (istate == IS_BOL)
- (void) fprintf(TrafficLogFile,
- "%05d <<< ",
- (int) getpid());
- if (c == EOF)
- (void) fprintf(TrafficLogFile,
- "[EOF]\n");
+ (void) sm_io_fprintf(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ "%05d <<< ",
+ (int) CurrentPid);
+ if (c == SM_IO_EOF)
+ (void) sm_io_fprintf(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ "[EOF]\n");
else
- (void) putc(c, TrafficLogFile);
+ (void) sm_io_putc(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ c);
}
- if (c == EOF)
+ if (c == SM_IO_EOF)
goto readerr;
if (SevenBitInput)
c &= 0x7f;
@@ -229,7 +409,7 @@ collect(fp, smtpmode, hdrp, e)
HasEightBits |= bitset(0x80, c);
}
if (tTd(30, 94))
- dprintf("istate=%d, c=%c (0x%x)\n",
+ sm_dprintf("istate=%d, c=%c (0x%x)\n",
istate, (char) c, c);
switch (istate)
{
@@ -278,7 +458,8 @@ collect(fp, smtpmode, hdrp, e)
istate = IS_BOL;
else
{
- (void) ungetc(c, fp);
+ (void) sm_io_ungetc(fp, SM_TIME_DEFAULT,
+ c);
c = '\r';
istate = IS_NORM;
}
@@ -290,7 +471,8 @@ collect(fp, smtpmode, hdrp, e)
istate = IS_CR;
continue;
}
- else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags))
+ else if (c == '\n' && !bitset(EF_NL_NOT_EOL,
+ e->e_flags))
istate = IS_BOL;
else
istate = IS_NORM;
@@ -313,7 +495,9 @@ bufferchar:
case MS_BODY:
/* just put the character out */
if (!bitset(EF_TOOBIG, e->e_flags))
- (void) putc(c, df);
+ (void) sm_io_putc(df, SM_TIME_DEFAULT,
+ c);
+
/* FALLTHROUGH */
case MS_DISCARD:
@@ -338,11 +522,22 @@ bufferchar:
memmove(buf, obuf, bp - obuf);
bp = &buf[bp - obuf];
if (obuf != bufbuf)
- sm_free(obuf);
+ sm_free(obuf); /* XXX */
}
+
+ /*
+ ** XXX Notice: the logic here is broken.
+ ** An input to sendmail that doesn't contain a
+ ** header but starts immediately with the body whose
+ ** first line contain characters which match the
+ ** following "if" will cause problems: those
+ ** characters will NOT appear in the output...
+ ** Do we care?
+ */
+
if (c >= 0200 && c <= 0237)
{
-#if 0 /* causes complaints -- figure out something for 8.11 */
+#if 0 /* causes complaints -- figure out something for 8.n+1 */
usrerr("Illegal character 0x%x in header", c);
#else /* 0 */
/* EMPTY */
@@ -351,7 +546,7 @@ bufferchar:
else if (c != '\0')
{
*bp++ = c;
- hdrslen++;
+ ++hdrslen;
if (!headeronly &&
MaxHeadersLength > 0 &&
hdrslen > MaxHeadersLength)
@@ -359,7 +554,7 @@ bufferchar:
sm_syslog(LOG_NOTICE, e->e_id,
"headers too large (%d max) from %s during message collect",
MaxHeadersLength,
- CurHostName != NULL ? CurHostName : "localhost");
+ CURHOSTNAME);
errno = 0;
e->e_flags |= EF_CLRQUEUE;
e->e_status = "5.6.0";
@@ -376,7 +571,7 @@ bufferchar:
nextstate:
if (tTd(30, 35))
- dprintf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n",
+ sm_dprintf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n",
istate, mstate, buf);
switch (mstate)
{
@@ -402,12 +597,12 @@ nextstate:
/* check for possible continuation line */
do
{
- clearerr(fp);
+ sm_io_clearerr(fp);
errno = 0;
- c = getc(fp);
- } while (c == EOF && errno == EINTR);
- if (c != EOF)
- (void) ungetc(c, fp);
+ c = sm_io_getc(fp, SM_TIME_DEFAULT);
+ } while (c == SM_IO_EOF && errno == EINTR);
+ if (c != SM_IO_EOF)
+ (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c);
if (c == ' ' || c == '\t')
{
/* yep -- defer this */
@@ -431,19 +626,14 @@ nextstate:
case MS_BODY:
if (tTd(30, 1))
- dprintf("EOH\n");
+ sm_dprintf("EOH\n");
if (headeronly)
goto readerr;
- /* call the end-of-header check ruleset */
- snprintf(hnum, sizeof hnum, "%d", numhdrs);
- snprintf(hsize, sizeof hsize, "%d", hdrslen);
- if (tTd(30, 10))
- dprintf("collect: rscheck(\"check_eoh\", \"%s $| %s\")\n",
- hnum, hsize);
- rstat = rscheck("check_eoh", hnum, hsize, e, FALSE,
- TRUE, 4, NULL);
+ df = collect_eoh(e, numhdrs, hdrslen);
+ if (df == NULL)
+ e->e_flags |= EF_TOOBIG;
bp = buf;
@@ -460,7 +650,8 @@ nextstate:
if (!bitset(EF_TOOBIG, e->e_flags))
{
while (*bp != '\0')
- (void) putc(*bp++, df);
+ (void) sm_io_putc(df, SM_TIME_DEFAULT,
+ *bp++);
}
break;
}
@@ -468,43 +659,54 @@ nextstate:
}
readerr:
- if ((feof(fp) && smtpmode) || ferror(fp))
+ if ((sm_io_eof(fp) && smtpmode) || sm_io_error(fp))
{
- const char *errmsg = errstring(errno);
+ const char *errmsg;
+ if (sm_io_eof(fp))
+ errmsg = "unexpected close";
+ else
+ errmsg = sm_errstring(errno);
if (tTd(30, 1))
- dprintf("collect: premature EOM: %s\n", errmsg);
- if (LogLevel >= 2)
+ sm_dprintf("collect: premature EOM: %s\n", errmsg);
+ if (LogLevel > 1)
sm_syslog(LOG_WARNING, e->e_id,
"collect: premature EOM: %s", errmsg);
- inputerr = TRUE;
+ inputerr = true;
}
/* reset global timer */
if (CollectTimeout != NULL)
- clrevent(CollectTimeout);
+ sm_clrevent(CollectTimeout);
if (headeronly)
return;
+ if (mstate != MS_BODY)
+ {
+ /* no body or discard, so we never opened the data file */
+ SM_ASSERT(df == NULL);
+ df = collect_eoh(e, numhdrs, hdrslen);
+ }
+
if (df == NULL)
{
/* skip next few clauses */
/* EMPTY */
}
- else if (fflush(df) != 0 || ferror(df))
+ else if (sm_io_flush(df, SM_TIME_DEFAULT) != 0 || sm_io_error(df))
{
- dferror(df, "fflush||ferror", e);
- flush_errors(TRUE);
- finis(TRUE, ExitStat);
+ dferror(df, "sm_io_flush||sm_io_error", e);
+ flush_errors(true);
+ finis(true, true, ExitStat);
/* NOTREACHED */
}
- else if (!SuperSafe)
+ else if (SuperSafe != SAFE_REALLY)
{
/* skip next few clauses */
/* EMPTY */
}
- else if (bfcommit(df) < 0)
+ else if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0 && errno != EINVAL)
{
int save_errno = errno;
@@ -512,40 +714,45 @@ readerr:
{
char *dfile;
struct stat st;
+ int dfd;
- dfile = queuename(e, 'd');
+ dfile = queuename(e, DATAFL_LETTER);
if (stat(dfile, &st) < 0)
st.st_size = -1;
errno = EEXIST;
- syserr("collect: bfcommit(%s): already on disk, size = %ld",
+ syserr("@collect: bfcommit(%s): already on disk, size = %ld",
dfile, (long) st.st_size);
- dfd = fileno(df);
+ dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
if (dfd >= 0)
- dumpfd(dfd, TRUE, TRUE);
+ dumpfd(dfd, true, true);
}
errno = save_errno;
dferror(df, "bfcommit", e);
- flush_errors(TRUE);
- finis(save_errno != EEXIST, ExitStat);
+ flush_errors(true);
+ finis(save_errno != EEXIST, true, ExitStat);
}
- else if (bffsync(df) < 0)
+ else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) >= 0 &&
+ fsync(afd) < 0)
{
- dferror(df, "bffsync", e);
- flush_errors(TRUE);
- finis(TRUE, ExitStat);
+ dferror(df, "fsync", e);
+ flush_errors(true);
+ finis(true, true, ExitStat);
/* NOTREACHED */
}
- else if (bfclose(df) < 0)
+ else if (sm_io_close(df, SM_TIME_DEFAULT) < 0)
{
- dferror(df, "bfclose", e);
- flush_errors(TRUE);
- finis(TRUE, ExitStat);
+ dferror(df, "sm_io_close", e);
+ flush_errors(true);
+ finis(true, true, ExitStat);
/* NOTREACHED */
}
else
{
/* everything is happily flushed to disk */
df = NULL;
+
+ /* remove from available space in filesystem */
+ updfs(e, false, true);
}
/* An EOF when running SMTP is an error */
@@ -558,19 +765,18 @@ readerr:
if (host == NULL)
host = "localhost";
- if (feof(fp))
+ if (sm_io_eof(fp))
problem = "unexpected close";
- else if (ferror(fp))
+ else if (sm_io_error(fp))
problem = "I/O error";
else
problem = "read timeout";
- if (LogLevel > 0 && feof(fp))
+ if (LogLevel > 0 && sm_io_eof(fp))
sm_syslog(LOG_NOTICE, e->e_id,
- "collect: %s on connection from %.100s, sender=%s: %s",
- problem, host,
- shortenstring(e->e_from.q_paddr, MAXSHORTSTR),
- errstring(errno));
- if (feof(fp))
+ "collect: %s on connection from %.100s, sender=%s",
+ problem, host,
+ shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
+ if (sm_io_eof(fp))
usrerr("451 4.4.1 collect: %s on connection from %s, from=%s",
problem, host,
shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
@@ -584,104 +790,32 @@ readerr:
e->e_flags &= ~EF_FATALERRS;
e->e_flags |= EF_CLRQUEUE;
- /* and don't try to deliver the partial message either */
- if (InChild)
- ExitStat = EX_QUIT;
- finis(TRUE, ExitStat);
+ finis(true, true, ExitStat);
/* NOTREACHED */
}
- /*
- ** Find out some information from the headers.
- ** Examples are who is the from person & the date.
- */
-
- eatheader(e, TRUE);
-
- if (GrabTo && e->e_sendqueue == NULL)
- usrerr("No recipient addresses found in header");
-
- /* collect statistics */
- if (OpMode != MD_VERIFY)
- markstats(e, (ADDRESS *) NULL, FALSE);
-
- /*
- ** If we have a Return-Receipt-To:, turn it into a DSN.
- */
-
- if (RrtImpliesDsn && hvalue("return-receipt-to", e->e_header) != NULL)
+ /* Log collection information. */
+ if (bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4)
{
- ADDRESS *q;
-
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- if (!bitset(QHASNOTIFY, q->q_flags))
- q->q_flags |= QHASNOTIFY|QPINGONSUCCESS;
- }
-
- /*
- ** Add an Apparently-To: line if we have no recipient lines.
- */
-
- if (hvalue("to", e->e_header) != NULL ||
- hvalue("cc", e->e_header) != NULL ||
- hvalue("apparently-to", e->e_header) != NULL)
- {
- /* have a valid recipient header -- delete Bcc: headers */
- e->e_flags |= EF_DELETE_BCC;
- }
- else if (hvalue("bcc", e->e_header) == NULL)
- {
- /* no valid recipient headers */
- register ADDRESS *q;
- char *hdr = NULL;
-
- /* create an Apparently-To: field */
- /* that or reject the message.... */
- switch (NoRecipientAction)
- {
- case NRA_ADD_APPARENTLY_TO:
- hdr = "Apparently-To";
- break;
-
- case NRA_ADD_TO:
- hdr = "To";
- break;
-
- case NRA_ADD_BCC:
- addheader("Bcc", " ", 0, &e->e_header);
- break;
-
- case NRA_ADD_TO_UNDISCLOSED:
- addheader("To", "undisclosed-recipients:;", 0, &e->e_header);
- break;
- }
-
- if (hdr != NULL)
- {
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (q->q_alias != NULL)
- continue;
- if (tTd(30, 3))
- dprintf("Adding %s: %s\n",
- hdr, q->q_paddr);
- addheader(hdr, q->q_paddr, 0, &e->e_header);
- }
- }
+ logsender(e, e->e_msgid);
+ e->e_flags &= ~EF_LOGSENDER;
}
/* check for message too large */
if (bitset(EF_TOOBIG, e->e_flags))
{
e->e_flags |= EF_NO_BODY_RETN|EF_CLRQUEUE;
- e->e_status = "5.2.3";
- usrerrenh(e->e_status,
- "552 Message exceeds maximum fixed size (%ld)",
- MaxMessageSize);
- if (LogLevel > 6)
- sm_syslog(LOG_NOTICE, e->e_id,
- "message size (%ld) exceeds maximum (%ld)",
- e->e_msgsize, MaxMessageSize);
+ if (!bitset(EF_FATALERRS, e->e_flags))
+ {
+ e->e_status = "5.2.3";
+ usrerrenh(e->e_status,
+ "552 Message exceeds maximum fixed size (%ld)",
+ MaxMessageSize);
+ if (LogLevel > 6)
+ sm_syslog(LOG_NOTICE, e->e_id,
+ "message size (%ld) exceeds maximum (%ld)",
+ e->e_msgsize, MaxMessageSize);
+ }
}
/* check for illegal 8-bit data */
@@ -699,27 +833,26 @@ readerr:
{
/* if it claimed to be 8 bits, well, it lied.... */
if (e->e_bodytype != NULL &&
- strcasecmp(e->e_bodytype, "8BITMIME") == 0)
+ sm_strcasecmp(e->e_bodytype, "8BITMIME") == 0)
e->e_bodytype = "7BIT";
}
- if (SuperSafe)
+ if (SuperSafe == SAFE_REALLY && !bitset(EF_FATALERRS, e->e_flags))
{
- if ((e->e_dfp = fopen(dfname, "r")) == NULL)
+ char *dfname = queuename(e, DATAFL_LETTER);
+ if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
+ SM_IO_RDONLY, NULL)) == NULL)
{
/* we haven't acked receipt yet, so just chuck this */
- syserr("Cannot reopen %s", dfname);
- finis(TRUE, ExitStat);
+ syserr("@Cannot reopen %s", dfname);
+ finis(true, true, ExitStat);
/* NOTREACHED */
}
}
else
e->e_dfp = df;
- if (e->e_dfp == NULL)
- syserr("!collect: no e_dfp");
}
-
static void
collecttimeout(timeout)
time_t timeout;
@@ -735,9 +868,9 @@ collecttimeout(timeout)
if (CollectProgress)
{
/* reset the timeout */
- CollectTimeout = sigsafe_setevent(timeout, collecttimeout,
- timeout);
- CollectProgress = FALSE;
+ CollectTimeout = sm_sigsafe_setevent(timeout, collecttimeout,
+ timeout);
+ CollectProgress = false;
}
else
{
@@ -751,12 +884,16 @@ collecttimeout(timeout)
errno = ETIMEDOUT;
longjmp(CtxCollectTimeout, 1);
}
-
errno = save_errno;
}
- /*
+/*
** DFERROR -- signal error on writing the data file.
**
+** Called by collect(). Collect() always terminates the process
+** immediately after calling dferror(), which means that the SMTP
+** session will be terminated, which means that any error message
+** issued by dferror must be a 421 error, as per RFC 821.
+**
** Parameters:
** df -- the file pointer for the data file.
** msg -- detailed message.
@@ -772,13 +909,13 @@ collecttimeout(timeout)
static void
dferror(df, msg, e)
- FILE *volatile df;
+ SM_FILE_T *volatile df;
char *msg;
register ENVELOPE *e;
{
char *dfname;
- dfname = queuename(e, 'd');
+ dfname = queuename(e, DATAFL_LETTER);
setstat(EX_IOERR);
if (errno == ENOSPC)
{
@@ -794,46 +931,54 @@ dferror(df, msg, e)
if (
#if STAT64 > 0
- fstat64(fileno(df), &st)
+ fstat64(sm_io_getinfo(df, SM_IO_WHAT_FD, NULL), &st)
#else /* STAT64 > 0 */
- fstat(fileno(df), &st)
+ fstat(sm_io_getinfo(df, SM_IO_WHAT_FD, NULL), &st)
#endif /* STAT64 > 0 */
< 0)
st.st_size = 0;
- (void) freopen(dfname, "w", df);
+ (void) sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, dfname,
+ SM_IO_WRONLY, NULL, df);
if (st.st_size <= 0)
- fprintf(df, "\n*** Mail could not be accepted");
- /*CONSTCOND*/
- else if (sizeof st.st_size > sizeof (long))
- fprintf(df, "\n*** Mail of at least %s bytes could not be accepted\n",
- quad_to_string(st.st_size));
+ (void) sm_io_fprintf(df, SM_TIME_DEFAULT,
+ "\n*** Mail could not be accepted");
else
- fprintf(df, "\n*** Mail of at least %lu bytes could not be accepted\n",
- (unsigned long) st.st_size);
- fprintf(df, "*** at %s due to lack of disk space for temp file.\n",
+ (void) sm_io_fprintf(df, SM_TIME_DEFAULT,
+ "\n*** Mail of at least %llu bytes could not be accepted\n",
+ (ULONGLONG_T) st.st_size);
+ (void) sm_io_fprintf(df, SM_TIME_DEFAULT,
+ "*** at %s due to lack of disk space for temp file.\n",
MyHostName);
- avail = freediskspace(qid_printqueue(e->e_queuedir), &bsize);
+ avail = freediskspace(qid_printqueue(e->e_qgrp, e->e_qdir),
+ &bsize);
if (avail > 0)
{
if (bsize > 1024)
avail *= bsize / 1024;
else if (bsize < 1024)
avail /= 1024 / bsize;
- fprintf(df, "*** Currently, %ld kilobytes are available for mail temp files.\n",
+ (void) sm_io_fprintf(df, SM_TIME_DEFAULT,
+ "*** Currently, %ld kilobytes are available for mail temp files.\n",
avail);
}
+#if 0
+ /* Wrong response code; should be 421. */
e->e_status = "4.3.1";
usrerrenh(e->e_status, "452 Out of disk space for temp file");
+#else /* 0 */
+ syserr("421 4.3.1 Out of disk space for temp file");
+#endif /* 0 */
}
else
- syserr("collect: Cannot write %s (%s, uid=%d)",
- dfname, msg, geteuid());
- if (freopen("/dev/null", "w", df) == NULL)
+ syserr("421 4.3.0 collect: Cannot write %s (%s, uid=%d, gid=%d)",
+ dfname, msg, geteuid(), getegid());
+ if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
+ SM_IO_WRONLY, NULL, df) == NULL)
sm_syslog(LOG_ERR, e->e_id,
- "dferror: freopen(\"/dev/null\") failed: %s",
- errstring(errno));
+ "dferror: sm_io_reopen(\"/dev/null\") failed: %s",
+ sm_errstring(errno));
}
- /*
+/*
** EATFROM -- chew up a UNIX style from line and process
**
** This does indeed make some assumptions about the format
@@ -873,7 +1018,7 @@ eatfrom(fm, e)
register char **dt;
if (tTd(30, 2))
- dprintf("eatfrom(%s)\n", fm);
+ sm_dprintf("eatfrom(%s)\n", fm);
/* find the date part */
p = fm;
@@ -911,13 +1056,12 @@ eatfrom(fm, e)
if (*p != '\0')
{
- char *q;
+ char *q, buf[25];
/* we have found a date */
- q = xalloc(25);
- (void) strlcpy(q, p, 25);
- q = arpadate(q);
- define('a', newstr(q), e);
+ (void) sm_strlcpy(buf, p, sizeof(buf));
+ q = arpadate(buf);
+ macdefine(&e->e_macro, A_TEMP, 'a', q);
}
}
#endif /* ! NOTUNIX */
diff --git a/contrib/sendmail/src/control.c b/contrib/sendmail/src/control.c
index b30c63f..c319192 100644
--- a/contrib/sendmail/src/control.c
+++ b/contrib/sendmail/src/control.c
@@ -8,18 +8,20 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: control.c,v 8.44.14.20 2001/05/03 17:24:03 gshapiro Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
+SM_RCSID("@(#)$Id: control.c,v 8.116 2001/12/13 21:51:38 gshapiro Exp $")
+
/* values for cmd_code */
-# define CMDERROR 0 /* bad command */
-# define CMDRESTART 1 /* restart daemon */
-# define CMDSHUTDOWN 2 /* end daemon */
-# define CMDHELP 3 /* help */
-# define CMDSTATUS 4 /* daemon status */
+#define CMDERROR 0 /* bad command */
+#define CMDRESTART 1 /* restart daemon */
+#define CMDSHUTDOWN 2 /* end daemon */
+#define CMDHELP 3 /* help */
+#define CMDSTATUS 4 /* daemon status */
+#define CMDMEMDUMP 5 /* dump memory, to find memory leaks */
+#if _FFR_CONTROL_MSTAT
+# define CMDMSTAT 6 /* daemon status, more info, tagged data */
+#endif /* _FFR_CONTROL_MSTAT */
struct cmd
{
@@ -33,13 +35,18 @@ static struct cmd CmdTab[] =
{ "restart", CMDRESTART },
{ "shutdown", CMDSHUTDOWN },
{ "status", CMDSTATUS },
+ { "memdump", CMDMEMDUMP },
+#if _FFR_CONTROL_MSTAT
+ { "mstat", CMDMSTAT },
+#endif /* _FFR_CONTROL_MSTAT */
{ NULL, CMDERROR }
};
+
int ControlSocket = -1;
- /*
+/*
** OPENCONTROLSOCKET -- create/open the daemon control named socket
**
** Creates and opens a named socket for external control over
@@ -55,13 +62,13 @@ int ControlSocket = -1;
int
opencontrolsocket()
{
-#if NETUNIX
+# if NETUNIX
int save_errno;
int rval;
long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
struct sockaddr_un controladdr;
- if (ControlSocketName == NULL)
+ if (ControlSocketName == NULL || *ControlSocketName == '\0')
return 0;
if (strlen(ControlSocketName) >= sizeof controladdr.sun_path)
@@ -87,8 +94,8 @@ opencontrolsocket()
(void) unlink(ControlSocketName);
memset(&controladdr, '\0', sizeof controladdr);
controladdr.sun_family = AF_UNIX;
- (void) strlcpy(controladdr.sun_path, ControlSocketName,
- sizeof controladdr.sun_path);
+ (void) sm_strlcpy(controladdr.sun_path, ControlSocketName,
+ sizeof controladdr.sun_path);
if (bind(ControlSocket, (struct sockaddr *) &controladdr,
sizeof controladdr) < 0)
@@ -115,11 +122,11 @@ opencontrolsocket()
sm_syslog(LOG_ALERT, NOQID,
"ownership change on %s to uid %d failed: %s",
ControlSocketName, (int) u,
- errstring(save_errno));
+ sm_errstring(save_errno));
message("050 ownership change on %s to uid %d failed: %s",
ControlSocketName, (int) u,
- errstring(save_errno));
- closecontrolsocket(TRUE);
+ sm_errstring(save_errno));
+ closecontrolsocket(true);
errno = save_errno;
return -1;
}
@@ -128,7 +135,7 @@ opencontrolsocket()
if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0)
{
save_errno = errno;
- closecontrolsocket(TRUE);
+ closecontrolsocket(true);
errno = save_errno;
return -1;
}
@@ -136,14 +143,14 @@ opencontrolsocket()
if (listen(ControlSocket, 8) < 0)
{
save_errno = errno;
- closecontrolsocket(TRUE);
+ closecontrolsocket(true);
errno = save_errno;
return -1;
}
-#endif /* NETUNIX */
+# endif /* NETUNIX */
return 0;
}
- /*
+/*
** CLOSECONTROLSOCKET -- close the daemon control named socket
**
** Close a named socket.
@@ -160,7 +167,7 @@ void
closecontrolsocket(fullclose)
bool fullclose;
{
-#if NETUNIX
+# if NETUNIX
long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
if (ControlSocket >= 0)
@@ -184,14 +191,14 @@ closecontrolsocket(fullclose)
{
sm_syslog(LOG_WARNING, NOQID,
"Could not remove control socket: %s",
- errstring(errno));
+ sm_errstring(errno));
return;
}
}
-#endif /* NETUNIX */
+# endif /* NETUNIX */
return;
}
- /*
+/*
** CLRCONTROL -- reset the control connection
**
** Parameters:
@@ -207,16 +214,13 @@ closecontrolsocket(fullclose)
void
clrcontrol()
{
-#if NETUNIX
+# if NETUNIX
if (ControlSocket >= 0)
(void) close(ControlSocket);
ControlSocket = -1;
-#endif /* NETUNIX */
+# endif /* NETUNIX */
}
-
-#ifndef NOT_SENDMAIL
-
- /*
+/*
** CONTROL_COMMAND -- read and process command from named socket
**
** Read and process the command from the opened socket.
@@ -232,6 +236,7 @@ clrcontrol()
static jmp_buf CtxControlTimeout;
+/* ARGSUSED0 */
static void
controltimeout(timeout)
time_t timeout;
@@ -252,17 +257,17 @@ control_command(sock, e)
ENVELOPE *e;
{
volatile int exitstat = EX_OK;
- FILE *s = NULL;
- EVENT *ev = NULL;
- FILE *traffic;
- FILE *oldout;
+ SM_FILE_T *s = NULL;
+ SM_EVENT *ev = NULL;
+ SM_FILE_T *traffic;
+ SM_FILE_T *oldout;
char *cmd;
char *p;
struct cmd *c;
char cmdbuf[MAXLINE];
char inp[MAXLINE];
- sm_setproctitle(FALSE, e, "control cmd read");
+ sm_setproctitle(false, e, "control cmd read");
if (TimeOuts.to_control > 0)
{
@@ -274,11 +279,12 @@ control_command(sock, e)
"timeout waiting for input during control command");
exit(EX_IOERR);
}
- ev = setevent(TimeOuts.to_control, controltimeout,
- TimeOuts.to_control);
+ ev = sm_setevent(TimeOuts.to_control, controltimeout,
+ TimeOuts.to_control);
}
- s = fdopen(sock, "r+");
+ s = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &sock,
+ SM_IO_RDWR, NULL);
if (s == NULL)
{
int save_errno = errno;
@@ -287,19 +293,20 @@ control_command(sock, e)
errno = save_errno;
exit(EX_IOERR);
}
- setbuf(s, NULL);
+ (void) sm_io_setvbuf(s, SM_TIME_DEFAULT, NULL,
+ SM_IO_NBF, SM_IO_BUFSIZ);
- if (fgets(inp, sizeof inp, s) == NULL)
+ if (sm_io_fgets(s, SM_TIME_DEFAULT, inp, sizeof inp) == NULL)
{
- (void) fclose(s);
+ (void) sm_io_close(s, SM_TIME_DEFAULT);
exit(EX_IOERR);
}
- (void) fflush(s);
+ (void) sm_io_flush(s, SM_TIME_DEFAULT);
/* clean up end of line */
- fixcrlf(inp, TRUE);
+ fixcrlf(inp, true);
- sm_setproctitle(FALSE, e, "control: %s", inp);
+ sm_setproctitle(false, e, "control: %s", inp);
/* break off command */
for (p = inp; isascii(*p) && isspace(*p); p++)
@@ -318,7 +325,7 @@ control_command(sock, e)
/* decode command */
for (c = CmdTab; c->cmd_name != NULL; c++)
{
- if (strcasecmp(c->cmd_name, cmdbuf) == 0)
+ if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
break;
}
@@ -335,45 +342,87 @@ control_command(sock, e)
break;
case CMDRESTART: /* restart the daemon */
- fprintf(s, "OK\r\n");
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
exitstat = EX_RESTART;
break;
case CMDSHUTDOWN: /* kill the daemon */
- fprintf(s, "OK\r\n");
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
exitstat = EX_SHUTDOWN;
break;
case CMDSTATUS: /* daemon status */
proc_list_probe();
{
+ int qgrp;
long bsize;
long free;
- free = freediskspace(QueueDir, &bsize);
+ /* XXX need to deal with different partitions */
+ qgrp = e->e_qgrp;
+ if (!ISVALIDQGRP(qgrp))
+ qgrp = 0;
+ free = freediskspace(Queue[qgrp]->qg_qdir, &bsize);
/*
** Prevent overflow and don't lose
** precision (if bsize == 512)
*/
- free = (long)((double)free * ((double)bsize / 1024));
+ if (free > 0)
+ free = (long)((double) free *
+ ((double) bsize / 1024));
- fprintf(s, "%d/%d/%ld/%d\r\n",
- CurChildren, MaxChildren,
- free, sm_getla(NULL));
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
+ "%d/%d/%ld/%d\r\n",
+ CurChildren, MaxChildren,
+ free, getla());
}
- proc_list_display(s);
+ proc_list_display(s, "");
+ break;
+
+# if _FFR_CONTROL_MSTAT
+ case CMDMSTAT: /* daemon status, extended, tagged format */
+ proc_list_probe();
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
+ "C:%d\r\nM:%d\r\nL:%d\r\n",
+ CurChildren, MaxChildren,
+ getla());
+ printnqe(s, "Q:");
+ disk_status(s, "D:");
+ proc_list_display(s, "P:");
+ break;
+# endif /* _FFR_CONTROL_MSTAT */
+
+ case CMDMEMDUMP: /* daemon memory dump, to find memory leaks */
+# if SM_HEAP_CHECK
+ /* dump the heap, if we are checking for memory leaks */
+ if (sm_debug_active(&SmHeapCheck, 2))
+ {
+ sm_heap_report(s, sm_debug_level(&SmHeapCheck) - 1);
+ }
+ else
+ {
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
+ "Memory dump unavailable.\r\n");
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
+ "To fix, run sendmail with -dsm_check_heap.4\r\n");
+ }
+# else /* SM_HEAP_CHECK */
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
+ "Memory dump unavailable.\r\n");
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
+ "To fix, rebuild with -DSM_HEAP_CHECK\r\n");
+# endif /* SM_HEAP_CHECK */
break;
case CMDERROR: /* unknown command */
- fprintf(s, "Bad command (%s)\r\n", cmdbuf);
+ (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
+ "Bad command (%s)\r\n", cmdbuf);
break;
}
- (void) fclose(s);
+ (void) sm_io_close(s, SM_TIME_DEFAULT);
if (ev != NULL)
- clrevent(ev);
+ sm_clrevent(ev);
exit(exitstat);
}
-#endif /* ! NOT_SENDMAIL */
-
diff --git a/contrib/sendmail/src/convtime.c b/contrib/sendmail/src/convtime.c
index 9bed853..36edc1a 100644
--- a/contrib/sendmail/src/convtime.c
+++ b/contrib/sendmail/src/convtime.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
@@ -11,12 +11,10 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: convtime.c,v 8.25 1999/06/16 21:11:26 ca Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
+SM_RCSID("@(#)$Id: convtime.c,v 8.39 2001/09/11 04:05:13 gshapiro Exp $")
+
/*
** CONVTIME -- convert time
**
@@ -47,10 +45,16 @@ convtime(p, units)
{
register time_t t, r;
register char c;
+ bool pos = true;
r = 0;
- if (strcasecmp(p, "now") == 0)
+ if (sm_strcasecmp(p, "now") == 0)
return NOW;
+ if (*p == '-')
+ {
+ pos = false;
+ ++p;
+ }
while (*p != '\0')
{
t = 0;
@@ -92,14 +96,14 @@ convtime(p, units)
r += t;
}
- return r;
+ return pos ? r : -r;
}
- /*
+/*
** PINTVL -- produce printable version of a time interval
**
** Parameters:
** intvl -- the interval to be converted
-** brief -- if TRUE, print this in an extremely compact form
+** brief -- if true, print this in an extremely compact form
** (basically used for logging).
**
** Returns:
@@ -154,38 +158,43 @@ pintvl(intvl, brief)
{
if (dy > 0)
{
- (void) snprintf(p, SPACELEFT(buf, p), "%d+", dy);
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "%d+", dy);
p += strlen(p);
}
- (void) snprintf(p, SPACELEFT(buf, p), "%02d:%02d:%02d",
- hr, mi, se);
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "%02d:%02d:%02d",
+ hr, mi, se);
return buf;
}
/* use the verbose form */
if (wk > 0)
{
- (void) snprintf(p, SPACELEFT(buf, p), ", %d week%s", wk, PLURAL(wk));
+ (void) sm_snprintf(p, SPACELEFT(buf, p), ", %d week%s", wk,
+ PLURAL(wk));
p += strlen(p);
}
if (dy > 0)
{
- (void) snprintf(p, SPACELEFT(buf, p), ", %d day%s", dy, PLURAL(dy));
+ (void) sm_snprintf(p, SPACELEFT(buf, p), ", %d day%s", dy,
+ PLURAL(dy));
p += strlen(p);
}
if (hr > 0)
{
- (void) snprintf(p, SPACELEFT(buf, p), ", %d hour%s", hr, PLURAL(hr));
+ (void) sm_snprintf(p, SPACELEFT(buf, p), ", %d hour%s", hr,
+ PLURAL(hr));
p += strlen(p);
}
if (mi > 0)
{
- (void) snprintf(p, SPACELEFT(buf, p), ", %d minute%s", mi, PLURAL(mi));
+ (void) sm_snprintf(p, SPACELEFT(buf, p), ", %d minute%s", mi,
+ PLURAL(mi));
p += strlen(p);
}
if (se > 0)
{
- (void) snprintf(p, SPACELEFT(buf, p), ", %d second%s", se, PLURAL(se));
+ (void) sm_snprintf(p, SPACELEFT(buf, p), ", %d second%s", se,
+ PLURAL(se));
p += strlen(p);
}
diff --git a/contrib/sendmail/src/daemon.c b/contrib/sendmail/src/daemon.c
index 954d253..c3e8dcc 100644
--- a/contrib/sendmail/src/daemon.c
+++ b/contrib/sendmail/src/daemon.c
@@ -13,20 +13,13 @@
#include <sendmail.h>
-
-#ifndef lint
-# ifdef DAEMON
-static char id[] = "@(#)$Id: daemon.c,v 8.401.4.68 2001/07/20 18:45:58 gshapiro Exp $ (with daemon mode)";
-# else /* DAEMON */
-static char id[] = "@(#)$Id: daemon.c,v 8.401.4.68 2001/07/20 18:45:58 gshapiro Exp $ (without daemon mode)";
-# endif /* DAEMON */
-#endif /* ! lint */
+SM_RCSID("@(#)$Id: daemon.c,v 8.603 2001/12/31 19:46:38 gshapiro Exp $")
#if defined(SOCK_STREAM) || defined(__GNU_LIBRARY__)
# define USE_SOCK_STREAM 1
#endif /* defined(SOCK_STREAM) || defined(__GNU_LIBRARY__) */
-#if DAEMON || defined(USE_SOCK_STREAM)
+#if defined(USE_SOCK_STREAM)
# if NETINET || NETINET6
# include <arpa/inet.h>
# endif /* NETINET || NETINET6 */
@@ -35,42 +28,42 @@ static char id[] = "@(#)$Id: daemon.c,v 8.401.4.68 2001/07/20 18:45:58 gshapiro
# define NO_DATA NO_ADDRESS
# endif /* ! NO_DATA */
# endif /* NAMED_BIND */
-#endif /* DAEMON || defined(USE_SOCK_STREAM) */
-
-#if DAEMON
-
-# if STARTTLS
-# include <openssl/rand.h>
-# endif /* STARTTLS */
-
-# include <sys/time.h>
-
-# if IP_SRCROUTE && NETINET
-# include <netinet/in_systm.h>
-# include <netinet/ip.h>
-# if HAS_IN_H
-# include <netinet/in.h>
-# ifndef IPOPTION
-# define IPOPTION ip_opts
-# define IP_LIST ip_opts
-# define IP_DST ip_dst
-# endif /* ! IPOPTION */
-# else /* HAS_IN_H */
-# include <netinet/ip_var.h>
-# ifndef IPOPTION
-# define IPOPTION ipoption
-# define IP_LIST ipopt_list
-# define IP_DST ipopt_dst
-# endif /* ! IPOPTION */
-# endif /* HAS_IN_H */
-# endif /* IP_SRCROUTE && NETINET */
-
-/* structure to describe a daemon */
+#endif /* defined(USE_SOCK_STREAM) */
+
+#if STARTTLS
+# include <openssl/rand.h>
+#endif /* STARTTLS */
+
+#include <sys/time.h>
+
+#if IP_SRCROUTE && NETINET
+# include <netinet/in_systm.h>
+# include <netinet/ip.h>
+# if HAS_IN_H
+# include <netinet/in.h>
+# ifndef IPOPTION
+# define IPOPTION ip_opts
+# define IP_LIST ip_opts
+# define IP_DST ip_dst
+# endif /* ! IPOPTION */
+# else /* HAS_IN_H */
+# include <netinet/ip_var.h>
+# ifndef IPOPTION
+# define IPOPTION ipoption
+# define IP_LIST ipopt_list
+# define IP_DST ipopt_dst
+# endif /* ! IPOPTION */
+# endif /* HAS_IN_H */
+#endif /* IP_SRCROUTE && NETINET */
+
+#include <sm/fdset.h>
+
+/* structure to describe a daemon or a client */
struct daemon
{
int d_socket; /* fd for socket */
SOCKADDR d_addr; /* socket for incoming */
- u_short d_port; /* port number */
+ unsigned short d_port; /* port number */
int d_listenqueue; /* size of listen queue */
int d_tcprcvbufsize; /* size of TCP receive buffer */
int d_tcpsndbufsize; /* size of TCP send buffer */
@@ -80,15 +73,20 @@ struct daemon
BITMAP256 d_flags; /* flags; see sendmail.h */
char *d_mflags; /* flags for use in macro */
char *d_name; /* user-supplied name */
+#if MILTER
+# if _FFR_MILTER_PERDAEMON
+ char *d_inputfilterlist;
+ struct milter *d_inputfilters[MAXFILTERS];
+# endif /* _FFR_MILTER_PERDAEMON */
+#endif /* MILTER */
};
typedef struct daemon DAEMON_T;
-static void connecttimeout __P((void));
-static int opendaemonsocket __P((struct daemon *, bool));
-static u_short setupdaemon __P((SOCKADDR *));
-static SIGFUNC_DECL sighup __P((int));
-static void restart_daemon __P((void));
+static void connecttimeout __P((void));
+static int opendaemonsocket __P((DAEMON_T *, bool));
+static unsigned short setupdaemon __P((SOCKADDR *));
+static void getrequests_checkdiskspace __P((ENVELOPE *e));
/*
** DAEMON.C -- routines to use when running as a daemon.
@@ -110,24 +108,20 @@ static void restart_daemon __P((void));
** etc., to avoid having extra file descriptors during
** the queue run and to avoid confusing the network
** code (if it cares).
-** makeconnection(host, port, outfile, infile, e)
+** makeconnection(host, port, mci, e, enough)
** Make a connection to the named host on the given
-** port. Set *outfile and *infile to the files
-** appropriate for communication. Returns zero on
-** success, else an exit status describing the
-** error.
+** port. Returns zero on success, else an exit status
+** describing the error.
** host_map_lookup(map, hbuf, avp, pstat)
** Convert the entry in hbuf into a canonical form.
*/
static DAEMON_T Daemons[MAXDAEMONS];
-static int ndaemons = 0; /* actual number of daemons */
+static int NDaemons = 0; /* actual number of daemons */
-/* options for client */
-static int TcpRcvBufferSize = 0; /* size of TCP receive buffer */
-static int TcpSndBufferSize = 0; /* size of TCP send buffer */
+static time_t NextDiskSpaceCheck = 0;
- /*
+/*
** GETREQUESTS -- open mail IPC port and get requests.
**
** Parameters:
@@ -143,6 +137,8 @@ static int TcpSndBufferSize = 0; /* size of TCP send buffer */
** routine is always in the child. The file pointers
** "InChannel" and "OutChannel" should be set to point
** to the communication channel.
+** May restart persistent queue runners if they have ended
+** for some reason.
*/
BITMAP256 *
@@ -150,12 +146,11 @@ getrequests(e)
ENVELOPE *e;
{
int t;
- time_t last_disk_space_check = 0;
int idx, curdaemon = -1;
int i, olddaemon = 0;
-# if XDEBUG
+#if XDEBUG
bool j_has_dot;
-# endif /* XDEBUG */
+#endif /* XDEBUG */
char status[MAXLINE];
SOCKADDR sa;
SOCKADDR_LEN_T len = sizeof sa;
@@ -163,12 +158,13 @@ getrequests(e)
extern int ControlSocket;
# endif /* NETUNIX */
extern ENVELOPE BlankEnvelope;
+ extern bool refuseconnections __P((char *, ENVELOPE *, int, bool));
- for (idx = 0; idx < ndaemons; idx++)
+ for (idx = 0; idx < NDaemons; idx++)
{
Daemons[idx].d_port = setupdaemon(&(Daemons[idx].d_addr));
- Daemons[idx].d_firsttime = TRUE;
+ Daemons[idx].d_firsttime = true;
Daemons[idx].d_refuse_connections_until = (time_t) 0;
}
@@ -178,48 +174,45 @@ getrequests(e)
if (tTd(15, 1))
{
- for (idx = 0; idx < ndaemons; idx++)
+ for (idx = 0; idx < NDaemons; idx++)
{
- dprintf("getrequests: daemon %s: port %d\n",
- Daemons[idx].d_name,
- ntohs(Daemons[idx].d_port));
+ sm_dprintf("getrequests: daemon %s: port %d\n",
+ Daemons[idx].d_name,
+ ntohs(Daemons[idx].d_port));
}
}
/* get a socket for the SMTP connection */
- for (idx = 0; idx < ndaemons; idx++)
- Daemons[idx].d_socksize = opendaemonsocket(&Daemons[idx], TRUE);
+ for (idx = 0; idx < NDaemons; idx++)
+ Daemons[idx].d_socksize = opendaemonsocket(&Daemons[idx], true);
if (opencontrolsocket() < 0)
sm_syslog(LOG_WARNING, NOQID,
"daemon could not open control socket %s: %s",
- ControlSocketName, errstring(errno));
+ ControlSocketName, sm_errstring(errno));
- (void) setsignal(SIGCHLD, reapchild);
- (void) setsignal(SIGHUP, sighup);
+ /* If there are any queue runners released reapchild() co-ord's */
+ (void) sm_signal(SIGCHLD, reapchild);
- /* workaround: can't seem to release the signal in the parent */
- (void) releasesignal(SIGHUP);
-
- /* write the pid to file */
+ /* write the pid to file, command line args to syslog */
log_sendmail_pid(e);
-# if XDEBUG
+#if XDEBUG
{
char jbuf[MAXHOSTNAMELEN];
expand("\201j", jbuf, sizeof jbuf, e);
j_has_dot = strchr(jbuf, '.') != NULL;
}
-# endif /* XDEBUG */
+#endif /* XDEBUG */
/* Add parent process as first item */
- proc_list_add(getpid(), "Sendmail daemon", PROC_DAEMON);
+ proc_list_add(CurrentPid, "Sendmail daemon", PROC_DAEMON, 0, -1);
if (tTd(15, 1))
{
- for (idx = 0; idx < ndaemons; idx++)
- dprintf("getrequests: daemon %s: %d\n",
+ for (idx = 0; idx < NDaemons; idx++)
+ sm_dprintf("getrequests: daemon %s: %d\n",
Daemons[idx].d_name,
Daemons[idx].d_socket);
}
@@ -228,60 +221,39 @@ getrequests(e)
{
register pid_t pid;
auto SOCKADDR_LEN_T lotherend;
- bool timedout = FALSE;
- bool control = FALSE;
+ bool timedout = false;
+ bool control = false;
int save_errno;
int pipefd[2];
- time_t timenow;
-# if STARTTLS
+ time_t now;
+#if STARTTLS
long seed;
-# endif /* STARTTLS */
- extern bool refuseconnections __P((char *, ENVELOPE *, int));
+#endif /* STARTTLS */
/* see if we are rejecting connections */
- (void) blocksignal(SIGALRM);
+ (void) sm_blocksignal(SIGALRM);
if (ShutdownRequest != NULL)
shutdown_daemon();
else if (RestartRequest != NULL)
restart_daemon();
+ else if (RestartWorkGroup)
+ restart_marked_work_groups();
- timenow = curtime();
-
- /*
- ** Use ConnRateThrottle only if the
- ** last pass was for a connection
- */
-
- if (ConnRateThrottle > 0 && curdaemon >= 0)
+ for (idx = 0; idx < NDaemons; idx++)
{
- static int conncnt = 0;
- static time_t lastconn = 0;
-
- if (timenow != lastconn)
- {
- lastconn = timenow;
- conncnt = 1;
- }
- else if (++conncnt > ConnRateThrottle)
- {
- /* sleep to flatten out connection load */
- sm_setproctitle(TRUE, e,
- "deferring connections: %d per second",
- ConnRateThrottle);
- if (LogLevel >= 9)
- sm_syslog(LOG_INFO, NOQID,
- "deferring connections: %d per second",
- ConnRateThrottle);
- (void) sleep(1);
- }
- }
+ /*
+ ** XXX do this call outside the loop?
+ ** no: refuse_connections may sleep().
+ */
- for (idx = 0; idx < ndaemons; idx++)
- {
- if (timenow < Daemons[idx].d_refuse_connections_until)
+ now = curtime();
+ if (now < Daemons[idx].d_refuse_connections_until)
continue;
- if (refuseconnections(Daemons[idx].d_name, e, idx))
+ if (bitnset(D_DISABLE, Daemons[idx].d_flags))
+ continue;
+ if (refuseconnections(Daemons[idx].d_name, e, idx,
+ curdaemon == idx))
{
if (Daemons[idx].d_socket >= 0)
{
@@ -291,19 +263,19 @@ getrequests(e)
}
/* refuse connections for next 15 seconds */
- Daemons[idx].d_refuse_connections_until = timenow + 15;
+ Daemons[idx].d_refuse_connections_until = now + 15;
}
else if (Daemons[idx].d_socket < 0 ||
Daemons[idx].d_firsttime)
{
- if (!Daemons[idx].d_firsttime && LogLevel >= 9)
+ if (!Daemons[idx].d_firsttime && LogLevel > 8)
sm_syslog(LOG_INFO, NOQID,
"accepting connections again for daemon %s",
Daemons[idx].d_name);
/* arrange to (re)open the socket if needed */
- (void) opendaemonsocket(&Daemons[idx], FALSE);
- Daemons[idx].d_firsttime = FALSE;
+ (void) opendaemonsocket(&Daemons[idx], false);
+ Daemons[idx].d_firsttime = false;
}
}
@@ -312,58 +284,12 @@ getrequests(e)
shutdown_daemon();
else if (RestartRequest != NULL)
restart_daemon();
+ else if (RestartWorkGroup)
+ restart_marked_work_groups();
- if (timenow >= last_disk_space_check)
- {
- bool logged = FALSE;
-
- if (!enoughdiskspace(MinBlocksFree + 1, FALSE))
- {
- for (idx = 0; idx < ndaemons; idx++)
- {
- if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags))
- {
- /* log only if not logged before */
- if (!logged)
- {
- if (LogLevel >= 9)
- sm_syslog(LOG_INFO, NOQID,
- "rejecting new messages: min free: %ld",
- MinBlocksFree);
- logged = TRUE;
- sm_setproctitle(TRUE, e,
- "rejecting new messages: min free: %ld",
- MinBlocksFree);
- }
- setbitn(D_ETRNONLY, Daemons[idx].d_flags);
- }
- }
- }
- else
- {
- for (idx = 0; idx < ndaemons; idx++)
- {
- if (bitnset(D_ETRNONLY, Daemons[idx].d_flags))
- {
- /* log only if not logged before */
- if (!logged)
- {
- if (LogLevel >= 9)
- sm_syslog(LOG_INFO, NOQID,
- "accepting new messages (again)");
- logged = TRUE;
- }
-
- /* title will be set below */
- clrbitn(D_ETRNONLY, Daemons[idx].d_flags);
- }
- }
- }
- /* only check disk space once a minute */
- last_disk_space_check = timenow + 60;
- }
+ getrequests_checkdiskspace(e);
-# if XDEBUG
+#if XDEBUG
/* check for disaster */
{
char jbuf[MAXHOSTNAMELEN];
@@ -384,9 +310,9 @@ getrequests(e)
abort();
}
}
-# endif /* XDEBUG */
+#endif /* XDEBUG */
-# if 0
+#if 0
/*
** Andrew Sun <asun@ieps-sun.ml.com> claims that this will
** fix the SVr4 problem. But it seems to have gone away,
@@ -394,14 +320,14 @@ getrequests(e)
*/
if (DaemonSocket >= 0 &&
- SetNonBlocking(DaemonSocket, FALSE) < 0)
+ SetNonBlocking(DaemonSocket, false) < 0)
log an error here;
-# endif /* 0 */
- (void) releasesignal(SIGALRM);
+#endif /* 0 */
+ (void) sm_releasesignal(SIGALRM);
for (;;)
{
- bool setproc = FALSE;
+ bool setproc = false;
int highest = -1;
fd_set readfds;
struct timeval timeout;
@@ -410,10 +336,12 @@ getrequests(e)
shutdown_daemon();
else if (RestartRequest != NULL)
restart_daemon();
+ else if (RestartWorkGroup)
+ restart_marked_work_groups();
FD_ZERO(&readfds);
- for (idx = 0; idx < ndaemons; idx++)
+ for (idx = 0; idx < NDaemons; idx++)
{
/* wait for a connection */
if (Daemons[idx].d_socket >= 0)
@@ -422,24 +350,25 @@ getrequests(e)
!bitnset(D_ETRNONLY,
Daemons[idx].d_flags))
{
- sm_setproctitle(TRUE, e,
+ sm_setproctitle(true, e,
"accepting connections");
- setproc = TRUE;
+ setproc = true;
}
if (Daemons[idx].d_socket > highest)
highest = Daemons[idx].d_socket;
- FD_SET((u_int)Daemons[idx].d_socket, &readfds);
+ SM_FD_SET(Daemons[idx].d_socket,
+ &readfds);
}
}
-# if NETUNIX
+#if NETUNIX
if (ControlSocket >= 0)
{
if (ControlSocket > highest)
highest = ControlSocket;
- FD_SET(ControlSocket, &readfds);
+ SM_FD_SET(ControlSocket, &readfds);
}
-# endif /* NETUNIX */
+#endif /* NETUNIX */
timeout.tv_sec = 5;
timeout.tv_usec = 0;
@@ -452,29 +381,32 @@ getrequests(e)
shutdown_daemon();
else if (RestartRequest != NULL)
restart_daemon();
+ else if (RestartWorkGroup)
+ restart_marked_work_groups();
- if (DoQueueRun)
- (void) runqueue(TRUE, FALSE);
-
curdaemon = -1;
+ if (doqueuerun())
+ (void) runqueue(true, false, false, false);
+
if (t <= 0)
{
- timedout = TRUE;
+ timedout = true;
break;
}
- control = FALSE;
+ control = false;
errno = 0;
/* look "round-robin" for an active socket */
- if ((idx = olddaemon + 1) >= ndaemons)
+ if ((idx = olddaemon + 1) >= NDaemons)
idx = 0;
- for (i = 0; i < ndaemons; i++)
+ for (i = 0; i < NDaemons; i++)
{
if (Daemons[idx].d_socket >= 0 &&
- FD_ISSET(Daemons[idx].d_socket, &readfds))
+ SM_FD_ISSET(Daemons[idx].d_socket,
+ &readfds))
{
lotherend = Daemons[idx].d_socksize;
memset(&RealHostAddr, '\0',
@@ -503,12 +435,12 @@ getrequests(e)
olddaemon = curdaemon = idx;
break;
}
- if (++idx >= ndaemons)
+ if (++idx >= NDaemons)
idx = 0;
}
-# if NETUNIX
+#if NETUNIX
if (curdaemon == -1 && ControlSocket >= 0 &&
- FD_ISSET(ControlSocket, &readfds))
+ SM_FD_ISSET(ControlSocket, &readfds))
{
struct sockaddr_un sa_un;
@@ -536,26 +468,25 @@ getrequests(e)
errno = EINVAL;
}
if (t >= 0)
- control = TRUE;
+ control = true;
}
-# else /* NETUNIX */
+#else /* NETUNIX */
if (curdaemon == -1)
{
/* No daemon to service */
continue;
}
-# endif /* NETUNIX */
+#endif /* NETUNIX */
if (t >= 0 || errno != EINTR)
break;
}
if (timedout)
{
- timedout = FALSE;
+ timedout = false;
continue;
}
save_errno = errno;
- timenow = curtime();
- (void) blocksignal(SIGALRM);
+ (void) sm_blocksignal(SIGALRM);
if (t < 0)
{
errno = save_errno;
@@ -564,15 +495,15 @@ getrequests(e)
/* arrange to re-open the socket next time around */
(void) close(Daemons[curdaemon].d_socket);
Daemons[curdaemon].d_socket = -1;
-# if SO_REUSEADDR_IS_BROKEN
+#if SO_REUSEADDR_IS_BROKEN
/*
** Give time for bound socket to be released.
** This creates a denial-of-service if you can
** force accept() to fail on affected systems.
*/
- Daemons[curdaemon].d_refuse_connections_until = timenow + 15;
-# endif /* SO_REUSEADDR_IS_BROKEN */
+ Daemons[curdaemon].d_refuse_connections_until = curtime() + 15;
+#endif /* SO_REUSEADDR_IS_BROKEN */
continue;
}
@@ -582,49 +513,58 @@ getrequests(e)
switch (Daemons[curdaemon].d_addr.sa.sa_family)
{
case AF_UNSPEC:
- define(macid("{daemon_family}", NULL),
- "unspec", &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{daemon_family}"), "unspec");
break;
-# if NETINET
+#if _FFR_DAEMON_NETUNIX
+# if NETUNIX
+ case AF_UNIX:
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{daemon_family}"), "local");
+ break;
+# endif /* NETUNIX */
+#endif /* _FFR_DAEMON_NETUNIX */
+#if NETINET
case AF_INET:
- define(macid("{daemon_family}", NULL),
- "inet", &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{daemon_family}"), "inet");
break;
-# endif /* NETINET */
-# if NETINET6
+#endif /* NETINET */
+#if NETINET6
case AF_INET6:
- define(macid("{daemon_family}", NULL),
- "inet6", &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{daemon_family}"), "inet6");
break;
-# endif /* NETINET6 */
-# if NETISO
+#endif /* NETINET6 */
+#if NETISO
case AF_ISO:
- define(macid("{daemon_family}", NULL),
- "iso", &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{daemon_family}"), "iso");
break;
-# endif /* NETISO */
-# if NETNS
+#endif /* NETISO */
+#if NETNS
case AF_NS:
- define(macid("{daemon_family}", NULL),
- "ns", &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{daemon_family}"), "ns");
break;
-# endif /* NETNS */
-# if NETX25
+#endif /* NETNS */
+#if NETX25
case AF_CCITT:
- define(macid("{daemon_family}", NULL),
- "x.25", &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{daemon_family}"), "x.25");
break;
-# endif /* NETX25 */
+#endif /* NETX25 */
}
- define(macid("{daemon_name}", NULL),
- Daemons[curdaemon].d_name, &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{daemon_name}"),
+ Daemons[curdaemon].d_name);
if (Daemons[curdaemon].d_mflags != NULL)
- define(macid("{daemon_flags}", NULL),
- Daemons[curdaemon].d_mflags,
- &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{daemon_flags}"),
+ Daemons[curdaemon].d_mflags);
else
- define(macid("{daemon_flags}", NULL),
- "", &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{daemon_flags}"), "");
}
/*
@@ -632,26 +572,38 @@ getrequests(e)
*/
if (tTd(15, 2))
- dprintf("getrequests: forking (fd = %d)\n", t);
+ sm_dprintf("getrequests: forking (fd = %d)\n", t);
/*
- ** advance state of PRNG
- ** this is necessary because otherwise all child processes
+ ** Advance state of PRNG.
+ ** This is necessary because otherwise all child processes
** will produce the same PRN sequence and hence the selection
** of a queue directory (and other things, e.g., MX selection)
** are not "really" random.
*/
-# if STARTTLS
+#if STARTTLS
+ /* XXX get some better "random" data? */
seed = get_random();
- RAND_seed((void *) &last_disk_space_check,
- sizeof last_disk_space_check);
- RAND_seed((void *) &timenow, sizeof timenow);
+ RAND_seed((void *) &NextDiskSpaceCheck,
+ sizeof NextDiskSpaceCheck);
+ RAND_seed((void *) &now, sizeof now);
RAND_seed((void *) &seed, sizeof seed);
-# else /* STARTTLS */
+#else /* STARTTLS */
(void) get_random();
-# endif /* STARTTLS */
+#endif /* STARTTLS */
-#ifndef DEBUG_NO_FORK
+#if NAMED_BIND
+ /*
+ ** Update MX records for FallBackMX.
+ ** Let's hope this is fast otherwise we screw up the
+ ** response time.
+ */
+
+ if (FallBackMX != NULL)
+ (void) getfallbackmxrr(FallBackMX);
+#endif /* NAMED_BIND */
+
+#if !PROFILING
/*
** Create a pipe to keep the child from writing to the
** socket until after the parent has closed it. Otherwise
@@ -661,7 +613,7 @@ getrequests(e)
if (pipe(pipefd) < 0)
pipefd[0] = pipefd[1] = -1;
- (void) blocksignal(SIGCHLD);
+ (void) sm_blocksignal(SIGCHLD);
pid = fork();
if (pid < 0)
{
@@ -671,19 +623,20 @@ getrequests(e)
(void) close(pipefd[0]);
(void) close(pipefd[1]);
}
- (void) releasesignal(SIGCHLD);
+ (void) sm_releasesignal(SIGCHLD);
(void) sleep(10);
(void) close(t);
continue;
}
-#else /* ! DEBUG_NO_FORK */
+
+#else /* !PROFILING */
pid = 0;
-#endif /* ! DEBUG_NO_FORK */
+#endif /* !PROFILING */
if (pid == 0)
{
char *p;
- FILE *inchannel, *outchannel = NULL;
+ SM_FILE_T *inchannel, *outchannel = NULL;
/*
** CHILD -- return to caller.
@@ -693,27 +646,39 @@ getrequests(e)
/* Reset global flags */
RestartRequest = NULL;
+ RestartWorkGroup = false;
ShutdownRequest = NULL;
PendingSignal = 0;
+ CurrentPid = getpid();
- (void) releasesignal(SIGALRM);
- (void) releasesignal(SIGCHLD);
- (void) setsignal(SIGCHLD, SIG_DFL);
- (void) setsignal(SIGHUP, SIG_DFL);
- (void) setsignal(SIGTERM, intsig);
+ (void) sm_releasesignal(SIGALRM);
+ (void) sm_releasesignal(SIGCHLD);
+ (void) sm_signal(SIGCHLD, SIG_DFL);
+ (void) sm_signal(SIGHUP, SIG_DFL);
+ (void) sm_signal(SIGTERM, intsig);
+
+ /* turn on profiling */
+ /* SM_PROF(0); */
+
+ /*
+ ** Initialize exception stack and default exception
+ ** handler for child process.
+ */
+
+ sm_exc_newthread(fatal_error);
if (!control)
{
- define(macid("{daemon_addr}", NULL),
- newstr(anynet_ntoa(&Daemons[curdaemon].d_addr)),
- &BlankEnvelope);
- (void) snprintf(status, sizeof status, "%d",
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{daemon_addr}"),
+ anynet_ntoa(&Daemons[curdaemon].d_addr));
+ (void) sm_snprintf(status, sizeof status, "%d",
ntohs(Daemons[curdaemon].d_port));
- define(macid("{daemon_port}", NULL),
- newstr(status), &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{daemon_port}"), status);
}
- for (idx = 0; idx < ndaemons; idx++)
+ for (idx = 0; idx < NDaemons; idx++)
{
if (Daemons[idx].d_socket >= 0)
(void) close(Daemons[idx].d_socket);
@@ -725,25 +690,29 @@ getrequests(e)
if (control)
{
/* Add control socket process */
- proc_list_add(getpid(), "console socket child",
- PROC_CONTROL_CHILD);
+ proc_list_add(CurrentPid,
+ "console socket child",
+ PROC_CONTROL_CHILD, 0, -1);
}
else
{
proc_list_clear();
+ /* clean up background delivery children */
+ (void) sm_signal(SIGCHLD, reapchild);
+
/* Add parent process as first child item */
- proc_list_add(getpid(), "daemon child",
- PROC_DAEMON_CHILD);
+ proc_list_add(CurrentPid, "daemon child",
+ PROC_DAEMON_CHILD, 0, -1);
/* don't schedule queue runs if ETRN */
QueueIntvl = 0;
- sm_setproctitle(TRUE, e, "startup with %s",
+ sm_setproctitle(true, e, "startup with %s",
anynet_ntoa(&RealHostAddr));
}
-#ifndef DEBUG_NO_FORK
+#if !PROFILING
if (pipefd[0] != -1)
{
auto char c;
@@ -765,99 +734,115 @@ getrequests(e)
continue;
(void) close(pipefd[0]);
}
-#endif /* ! DEBUG_NO_FORK */
+#endif /* !PROFILING */
/* control socket processing */
if (control)
{
control_command(t, e);
-
/* NOTREACHED */
exit(EX_SOFTWARE);
}
/* determine host name */
p = hostnamebyanyaddr(&RealHostAddr);
- if (strlen(p) > (SIZE_T) MAXNAME)
+ if (strlen(p) > MAXNAME) /* XXX - 1 ? */
p[MAXNAME] = '\0';
RealHostName = newstr(p);
if (RealHostName[0] == '[')
{
- /* TEMP, FAIL: which one? */
- define(macid("{client_resolve}", NULL),
- (h_errno == TRY_AGAIN) ? "TEMP" : "FAIL",
- &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{client_resolve}"),
+ h_errno == TRY_AGAIN ? "TEMP" : "FAIL");
}
else
- define(macid("{client_resolve}", NULL), "OK",
- &BlankEnvelope);
- sm_setproctitle(TRUE, e, "startup with %s", p);
-
- if ((inchannel = fdopen(t, "r")) == NULL ||
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{client_resolve}"), "OK");
+ sm_setproctitle(true, e, "startup with %s", p);
+
+ if ((inchannel = sm_io_open(SmFtStdiofd,
+ SM_TIME_DEFAULT,
+ (void *) &t,
+ SM_IO_RDONLY,
+ NULL)) == NULL ||
(t = dup(t)) < 0 ||
- (outchannel = fdopen(t, "w")) == NULL)
+ (outchannel = sm_io_open(SmFtStdiofd,
+ SM_TIME_DEFAULT,
+ (void *) &t,
+ SM_IO_WRONLY,
+ NULL)) == NULL)
{
- syserr("cannot open SMTP server channel, fd=%d", t);
- finis(FALSE, EX_OK);
+ syserr("cannot open SMTP server channel, fd=%d",
+ t);
+ finis(false, true, EX_OK);
}
+ sm_io_automode(inchannel, outchannel);
InChannel = inchannel;
OutChannel = outchannel;
- DisConnected = FALSE;
+ DisConnected = false;
-# ifdef XLA
+#if XLA
if (!xla_host_ok(RealHostName))
{
message("421 4.4.5 Too many SMTP sessions for this host");
- finis(FALSE, EX_OK);
+ finis(false, true, EX_OK);
}
-# endif /* XLA */
+#endif /* XLA */
/* find out name for interface of connection */
- if (getsockname(fileno(InChannel), &sa.sa,
- &len) == 0)
+ if (getsockname(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
+ NULL), &sa.sa, &len) == 0)
{
p = hostnamebyanyaddr(&sa);
if (tTd(15, 9))
- dprintf("getreq: got name %s\n", p);
- define(macid("{if_name}", NULL),
- newstr(p), &BlankEnvelope);
+ sm_dprintf("getreq: got name %s\n", p);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{if_name}"), p);
+
+ /*
+ ** Do this only if it is not the loopback
+ ** interface.
+ */
- /* do this only if it is not the loopback */
- /* interface: how to figure out? XXX */
if (!isloopback(sa))
{
- define(macid("{if_addr}", NULL),
- newstr(anynet_ntoa(&sa)),
- &BlankEnvelope);
- p = xalloc(5);
- snprintf(p, 4, "%d", sa.sa.sa_family);
- define(macid("{if_family}", NULL), p,
- &BlankEnvelope);
+ char *addr;
+ char family[5];
+
+ addr = anynet_ntoa(&sa);
+ (void) sm_snprintf(family,
+ sizeof(family),
+ "%d", sa.sa.sa_family);
+ macdefine(&BlankEnvelope.e_macro,
+ A_TEMP,
+ macid("{if_addr}"), addr);
+ macdefine(&BlankEnvelope.e_macro,
+ A_TEMP,
+ macid("{if_family}"), family);
if (tTd(15, 7))
- dprintf("getreq: got addr %s and family %s\n",
- macvalue(macid("{if_addr}", NULL),
- &BlankEnvelope),
- macvalue(macid("{if_addr}", NULL),
- &BlankEnvelope));
+ sm_dprintf("getreq: got addr %s and family %s\n",
+ addr, family);
}
else
{
- define(macid("{if_addr}", NULL), NULL,
- &BlankEnvelope);
- define(macid("{if_family}", NULL), NULL,
- &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro,
+ A_PERM,
+ macid("{if_addr}"), NULL);
+ macdefine(&BlankEnvelope.e_macro,
+ A_PERM,
+ macid("{if_family}"), NULL);
}
}
else
{
if (tTd(15, 7))
- dprintf("getreq: getsockname failed\n");
- define(macid("{if_name}", NULL), NULL,
- &BlankEnvelope);
- define(macid("{if_addr}", NULL), NULL,
- &BlankEnvelope);
- define(macid("{if_family}", NULL), NULL,
- &BlankEnvelope);
+ sm_dprintf("getreq: getsockname failed\n");
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{if_name}"), NULL);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{if_addr}"), NULL);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{if_family}"), NULL);
}
break;
}
@@ -865,17 +850,18 @@ getrequests(e)
/* parent -- keep track of children */
if (control)
{
- snprintf(status, sizeof status, "control socket server child");
- proc_list_add(pid, status, PROC_CONTROL);
+ (void) sm_snprintf(status, sizeof status,
+ "control socket server child");
+ proc_list_add(pid, status, PROC_CONTROL, 0, -1);
}
else
{
- snprintf(status, sizeof status,
- "SMTP server child for %s",
- anynet_ntoa(&RealHostAddr));
- proc_list_add(pid, status, PROC_DAEMON);
+ (void) sm_snprintf(status, sizeof status,
+ "SMTP server child for %s",
+ anynet_ntoa(&RealHostAddr));
+ proc_list_add(pid, status, PROC_DAEMON, 0, -1);
}
- (void) releasesignal(SIGCHLD);
+ (void) sm_releasesignal(SIGCHLD);
/* close the read end of the synchronization pipe */
if (pipefd[0] != -1)
@@ -894,12 +880,103 @@ getrequests(e)
pipefd[1] = -1;
}
}
-
if (tTd(15, 2))
- dprintf("getreq: returning\n");
+ sm_dprintf("getreq: returning\n");
+
+#if MILTER
+# if _FFR_MILTER_PERDAEMON
+ /* set the filters for this daemon */
+ if (Daemons[curdaemon].d_inputfilterlist != NULL)
+ {
+ for (i = 0;
+ (Daemons[curdaemon].d_inputfilters[i] != NULL &&
+ i < MAXFILTERS);
+ i++)
+ {
+ InputFilters[i] = Daemons[curdaemon].d_inputfilters[i];
+ }
+ if (i < MAXFILTERS)
+ InputFilters[i] = NULL;
+ }
+# endif /* _FFR_MILTER_PERDAEMON */
+#endif /* MILTER */
return &Daemons[curdaemon].d_flags;
}
- /*
+
+/*
+** GETREQUESTS_CHECKDISKSPACE -- check available diskspace.
+**
+** Parameters:
+** e -- envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Modifies Daemon flags (D_ETRNONLY) if not enough disk space.
+*/
+
+static void
+getrequests_checkdiskspace(e)
+ ENVELOPE *e;
+{
+ bool logged = false;
+ int idx;
+ time_t now;
+
+ now = curtime();
+ if (now < NextDiskSpaceCheck)
+ return;
+
+ /* Check if there is available disk space in all queue groups. */
+ if (!enoughdiskspace(0, NULL))
+ {
+ for (idx = 0; idx < NDaemons; ++idx)
+ {
+ if (bitnset(D_ETRNONLY, Daemons[idx].d_flags))
+ continue;
+
+ /* log only if not logged before */
+ if (!logged)
+ {
+ if (LogLevel > 8)
+ sm_syslog(LOG_INFO, NOQID,
+ "rejecting new messages: min free: %ld",
+ MinBlocksFree);
+ sm_setproctitle(true, e,
+ "rejecting new messages: min free: %ld",
+ MinBlocksFree);
+ logged = true;
+ }
+ setbitn(D_ETRNONLY, Daemons[idx].d_flags);
+ }
+ }
+ else
+ {
+ for (idx = 0; idx < NDaemons; ++idx)
+ {
+ if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags))
+ continue;
+
+ /* log only if not logged before */
+ if (!logged)
+ {
+ if (LogLevel > 8)
+ sm_syslog(LOG_INFO, NOQID,
+ "accepting new messages (again)");
+ logged = true;
+ }
+
+ /* title will be set later */
+ clrbitn(D_ETRNONLY, Daemons[idx].d_flags);
+ }
+ }
+
+ /* only check disk space once a minute */
+ NextDiskSpaceCheck = now + 60;
+}
+
+/*
** OPENDAEMONSOCKET -- open SMTP socket
**
** Deals with setting all appropriate options.
@@ -916,11 +993,11 @@ getrequests(e)
** Exits if the socket cannot be created.
*/
-# define MAXOPENTRIES 10 /* maximum number of tries to open connection */
+#define MAXOPENTRIES 10 /* maximum number of tries to open connection */
static int
opendaemonsocket(d, firsttime)
- struct daemon *d;
+ DAEMON_T *d;
bool firsttime;
{
int on = 1;
@@ -930,7 +1007,7 @@ opendaemonsocket(d, firsttime)
int save_errno;
if (tTd(15, 2))
- dprintf("opendaemonsocket(%s)\n", d->d_name);
+ sm_dprintf("opendaemonsocket(%s)\n", d->d_name);
do
{
@@ -938,16 +1015,55 @@ opendaemonsocket(d, firsttime)
(void) sleep(5);
if (firsttime || d->d_socket < 0)
{
+#if _FFR_DAEMON_NETUNIX
+# if NETUNIX
+ if (d->d_addr.sa.sa_family == AF_UNIX)
+ {
+ int rval;
+ long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_ROOTOK|SFF_EXECOK|SFF_CREAT;
+
+ /* if not safe, don't use it */
+ rval = safefile(d->d_addr.sunix.sun_path,
+ RunAsUid, RunAsGid,
+ RunAsUserName, sff,
+ S_IRUSR|S_IWUSR, NULL);
+ if (rval != 0)
+ {
+ save_errno = errno;
+ syserr("opendaemonsocket: daemon %s: unsafe domain socket %s",
+ d->d_name,
+ d->d_addr.sunix.sun_path);
+ goto fail;
+ }
+
+ /* Don't try to overtake an existing socket */
+ (void) unlink(d->d_addr.sunix.sun_path);
+ }
+# endif /* NETUNIX */
+#endif /* _FFR_DOMAIN_NETUNIX */
d->d_socket = socket(d->d_addr.sa.sa_family,
SOCK_STREAM, 0);
if (d->d_socket < 0)
{
save_errno = errno;
- syserr("opendaemonsocket: daemon %s: can't create server SMTP socket", d->d_name);
+ syserr("opendaemonsocket: daemon %s: can't create server SMTP socket",
+ d->d_name);
+ fail:
+ if (bitnset(D_OPTIONAL, d->d_flags) &&
+ (!transienterror(save_errno) ||
+ ntries >= MAXOPENTRIES - 1))
+ {
+ syserr("opendaemonsocket: daemon %s: optional socket disabled",
+ d->d_name);
+ setbitn(D_DISABLE, d->d_flags);
+ d->d_socket = -1;
+ return -1;
+ }
severe:
if (LogLevel > 0)
sm_syslog(LOG_ALERT, NOQID,
- "daemon %s: problem creating SMTP socket", d->d_name);
+ "daemon %s: problem creating SMTP socket",
+ d->d_name);
d->d_socket = -1;
continue;
}
@@ -963,7 +1079,7 @@ opendaemonsocket(d, firsttime)
(void) setsockopt(d->d_socket, SOL_SOCKET,
SO_KEEPALIVE, (char *)&on, sizeof on);
-# ifdef SO_RCVBUF
+#ifdef SO_RCVBUF
if (d->d_tcprcvbufsize > 0)
{
if (setsockopt(d->d_socket, SOL_SOCKET,
@@ -972,8 +1088,8 @@ opendaemonsocket(d, firsttime)
sizeof(d->d_tcprcvbufsize)) < 0)
syserr("opendaemonsocket: daemon %s: setsockopt(SO_RCVBUF)", d->d_name);
}
-# endif /* SO_RCVBUF */
-# ifdef SO_SNDBUF
+#endif /* SO_RCVBUF */
+#ifdef SO_SNDBUF
if (d->d_tcpsndbufsize > 0)
{
if (setsockopt(d->d_socket, SOL_SOCKET,
@@ -982,7 +1098,7 @@ opendaemonsocket(d, firsttime)
sizeof(d->d_tcpsndbufsize)) < 0)
syserr("opendaemonsocket: daemon %s: setsockopt(SO_SNDBUF)", d->d_name);
}
-# endif /* SO_SNDBUF */
+#endif /* SO_SNDBUF */
if ((fdflags = fcntl(d->d_socket, F_GETFD, 0)) == -1 ||
fcntl(d->d_socket, F_SETFD,
@@ -992,30 +1108,37 @@ opendaemonsocket(d, firsttime)
syserr("opendaemonsocket: daemon %s: failed to %s close-on-exec flag: %s",
d->d_name,
fdflags == -1 ? "get" : "set",
- errstring(save_errno));
+ sm_errstring(save_errno));
(void) close(d->d_socket);
goto severe;
}
switch (d->d_addr.sa.sa_family)
{
-# if NETINET
+#if _FFR_DAEMON_NETUNIX
+# ifdef NETUNIX
+ case AF_UNIX:
+ socksize = sizeof d->d_addr.sunix;
+ break;
+# endif /* NETUNIX */
+#endif /* _FFR_DAEMON_NETUNIX */
+#if NETINET
case AF_INET:
socksize = sizeof d->d_addr.sin;
break;
-# endif /* NETINET */
+#endif /* NETINET */
-# if NETINET6
+#if NETINET6
case AF_INET6:
socksize = sizeof d->d_addr.sin6;
break;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
-# if NETISO
+#if NETISO
case AF_ISO:
socksize = sizeof d->d_addr.siso;
break;
-# endif /* NETISO */
+#endif /* NETISO */
default:
socksize = sizeof d->d_addr;
@@ -1029,7 +1152,7 @@ opendaemonsocket(d, firsttime)
syserr("opendaemonsocket: daemon %s: cannot bind",
d->d_name);
(void) close(d->d_socket);
- goto severe;
+ goto fail;
}
}
if (!firsttime &&
@@ -1048,22 +1171,22 @@ opendaemonsocket(d, firsttime)
/* NOTREACHED */
return -1; /* avoid compiler warning on IRIX */
}
- /*
+/*
** SETUPDAEMON -- setup socket for daemon
**
** Parameters:
** daemonaddr -- socket for daemon
-** daemon -- number of daemon
**
** Returns:
** port number on which daemon should run
**
*/
-static u_short
+
+static unsigned short
setupdaemon(daemonaddr)
SOCKADDR *daemonaddr;
{
- u_short port;
+ unsigned short port;
/*
** Set up the address for the mailer.
@@ -1072,28 +1195,28 @@ setupdaemon(daemonaddr)
if (daemonaddr->sa.sa_family == AF_UNSPEC)
{
memset(daemonaddr, '\0', sizeof *daemonaddr);
-# if NETINET
+#if NETINET
daemonaddr->sa.sa_family = AF_INET;
-# endif /* NETINET */
+#endif /* NETINET */
}
switch (daemonaddr->sa.sa_family)
{
-# if NETINET
+#if NETINET
case AF_INET:
if (daemonaddr->sin.sin_addr.s_addr == 0)
daemonaddr->sin.sin_addr.s_addr = INADDR_ANY;
port = daemonaddr->sin.sin_port;
break;
-# endif /* NETINET */
+#endif /* NETINET */
-# if NETINET6
+#if NETINET6
case AF_INET6:
if (IN6_IS_ADDR_UNSPECIFIED(&daemonaddr->sin6.sin6_addr))
daemonaddr->sin6.sin6_addr = in6addr_any;
port = daemonaddr->sin6.sin6_port;
break;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
default:
/* unknown protocol */
@@ -1102,9 +1225,9 @@ setupdaemon(daemonaddr)
}
if (port == 0)
{
-# ifdef NO_GETSERVBYNAME
+#ifdef NO_GETSERVBYNAME
port = htons(25);
-# else /* NO_GETSERVBYNAME */
+#else /* NO_GETSERVBYNAME */
{
register struct servent *sp;
@@ -1117,30 +1240,30 @@ setupdaemon(daemonaddr)
else
port = sp->s_port;
}
-# endif /* NO_GETSERVBYNAME */
+#endif /* NO_GETSERVBYNAME */
}
switch (daemonaddr->sa.sa_family)
{
-# if NETINET
+#if NETINET
case AF_INET:
daemonaddr->sin.sin_port = port;
break;
-# endif /* NETINET */
+#endif /* NETINET */
-# if NETINET6
+#if NETINET6
case AF_INET6:
daemonaddr->sin6.sin6_port = port;
break;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
default:
/* unknown protocol */
break;
}
- return(port);
+ return port;
}
- /*
+/*
** CLRDAEMON -- reset the daemon connection
**
** Parameters:
@@ -1158,14 +1281,89 @@ clrdaemon()
{
int i;
- for (i = 0; i < ndaemons; i++)
+ for (i = 0; i < NDaemons; i++)
{
if (Daemons[i].d_socket >= 0)
(void) close(Daemons[i].d_socket);
Daemons[i].d_socket = -1;
}
}
- /*
+
+/*
+** GETMODIFIERS -- get modifier flags
+**
+** Parameters:
+** v -- the modifiers (input text line).
+** modifiers -- pointer to flag field to represent modifiers.
+**
+** Returns:
+** (xallocat()ed) string representation of modifiers.
+**
+** Side Effects:
+** fills in modifiers.
+*/
+
+char *
+getmodifiers(v, modifiers)
+ char *v;
+ BITMAP256 modifiers;
+{
+ int l;
+ char *h, *f, *flags;
+
+ /* maximum length of flags: upper case Option -> "OO " */
+ l = 3 * strlen(v) + 3;
+
+ /* is someone joking? */
+ if (l < 0 || l > 256)
+ {
+ if (LogLevel > 2)
+ sm_syslog(LOG_ERR, NOQID,
+ "getmodifiers too long, ignored");
+ return NULL;
+ }
+ flags = xalloc(l);
+ f = flags;
+ clrbitmap(modifiers);
+ for (h = v; *h != '\0'; h++)
+ {
+ if (isascii(*h) && !isspace(*h) && isprint(*h))
+ {
+ setbitn(*h, modifiers);
+ if (flags != f)
+ *flags++ = ' ';
+ *flags++ = *h;
+ if (isupper(*h))
+ *flags++ = *h;
+ }
+ }
+ *flags++ = '\0';
+ return f;
+}
+
+/*
+** CHKDAEMONMODIFIERS -- check whether all daemons have set a flag.
+**
+** Parameters:
+** flag -- the flag to test.
+**
+** Returns:
+** true iff all daemons have set flag.
+*/
+
+bool
+chkdaemonmodifiers(flag)
+ int flag;
+{
+ int i;
+
+ for (i = 0; i < NDaemons; i++)
+ if (!bitnset((char) flag, Daemons[i].d_flags))
+ return false;
+ return true;
+}
+
+/*
** SETSOCKADDROPTIONS -- set options for SOCKADDR (daemon or client)
**
** Parameters:
@@ -1179,20 +1377,18 @@ clrdaemon()
static void
setsockaddroptions(p, d)
register char *p;
- struct daemon *d;
+ DAEMON_T *d;
{
-# if NETISO
+#if NETISO
short portno;
-# endif /* NETISO */
- int l;
- char *h, *flags;
+#endif /* NETISO */
char *port = NULL;
char *addr = NULL;
-# if NETINET
+#if NETINET
if (d->d_addr.sa.sa_family == AF_UNSPEC)
d->d_addr.sa.sa_family = AF_INET;
-# endif /* NETINET */
+#endif /* NETINET */
while (p != NULL)
{
@@ -1220,26 +1416,33 @@ setsockaddroptions(p, d)
case 'F': /* address family */
if (isascii(*v) && isdigit(*v))
d->d_addr.sa.sa_family = atoi(v);
-# if NETINET
- else if (strcasecmp(v, "inet") == 0)
+#if _FFR_DAEMON_NETUNIX
+# ifdef NETUNIX
+ else if (sm_strcasecmp(v, "unix") == 0 ||
+ sm_strcasecmp(v, "local") == 0)
+ d->d_addr.sa.sa_family = AF_UNIX;
+# endif /* NETUNIX */
+#endif /* _FFR_DAEMON_NETUNIX */
+#if NETINET
+ else if (sm_strcasecmp(v, "inet") == 0)
d->d_addr.sa.sa_family = AF_INET;
-# endif /* NETINET */
-# if NETINET6
- else if (strcasecmp(v, "inet6") == 0)
+#endif /* NETINET */
+#if NETINET6
+ else if (sm_strcasecmp(v, "inet6") == 0)
d->d_addr.sa.sa_family = AF_INET6;
-# endif /* NETINET6 */
-# if NETISO
- else if (strcasecmp(v, "iso") == 0)
+#endif /* NETINET6 */
+#if NETISO
+ else if (sm_strcasecmp(v, "iso") == 0)
d->d_addr.sa.sa_family = AF_ISO;
-# endif /* NETISO */
-# if NETNS
- else if (strcasecmp(v, "ns") == 0)
+#endif /* NETISO */
+#if NETNS
+ else if (sm_strcasecmp(v, "ns") == 0)
d->d_addr.sa.sa_family = AF_NS;
-# endif /* NETNS */
-# if NETX25
- else if (strcasecmp(v, "x.25") == 0)
+#endif /* NETNS */
+#if NETX25
+ else if (sm_strcasecmp(v, "x.25") == 0)
d->d_addr.sa.sa_family = AF_CCITT;
-# endif /* NETX25 */
+#endif /* NETX25 */
else
syserr("554 5.3.5 Unknown address family %s in Family=option",
v);
@@ -1249,6 +1452,14 @@ setsockaddroptions(p, d)
addr = v;
break;
+#if MILTER
+# if _FFR_MILTER_PERDAEMON
+ case 'I':
+ d->d_inputfilterlist = v;
+ break;
+# endif /* _FFR_MILTER_PERDAEMON */
+#endif /* MILTER */
+
case 'P': /* port */
port = v;
break;
@@ -1258,25 +1469,7 @@ setsockaddroptions(p, d)
break;
case 'M': /* modifiers (flags) */
- l = 3 * strlen(v) + 3;
- h = v;
- flags = xalloc(l);
- d->d_mflags = flags;
- for (; *h != '\0'; h++)
- {
- if (!(isascii(*h) && isspace(*h)))
- {
- if (flags != d->d_mflags)
- *flags++ = ' ';
- *flags++ = *h;
- if (isupper(*h))
- *flags++ = *h;
- }
- }
- *flags++ = '\0';
- for (; *v != '\0'; v++)
- if (!(isascii(*v) && isspace(*v)))
- setbitn(bitidx(*v), d->d_flags);
+ d->d_mflags = getmodifiers(v, d->d_flags);
break;
case 'S': /* send buffer size */
@@ -1302,10 +1495,31 @@ setsockaddroptions(p, d)
{
switch (d->d_addr.sa.sa_family)
{
-# if NETINET
+#if _FFR_DAEMON_NETUNIX
+# if NETUNIX
+ case AF_UNIX:
+ if (strlen(addr) >= sizeof(d->d_addr.sunix.sun_path))
+ {
+ errno = ENAMETOOLONG;
+ syserr("setsockaddroptions: domain socket name too long: %s > %d",
+ addr, sizeof(d->d_addr.sunix.sun_path));
+ break;
+ }
+
+ /* file safety check done in opendaemonsocket() */
+ (void) memset(&d->d_addr.sunix.sun_path, '\0',
+ sizeof(d->d_addr.sunix.sun_path));
+ (void) sm_strlcpy((char *)&d->d_addr.sunix.sun_path,
+ addr,
+ sizeof(d->d_addr.sunix.sun_path));
+ break;
+# endif /* NETUNIX */
+#endif /* _FFR_DAEMON_NETUNIX */
+#if NETINET
case AF_INET:
if (!isascii(*addr) || !isdigit(*addr) ||
- ((d->d_addr.sin.sin_addr.s_addr = inet_addr(addr)) == INADDR_NONE))
+ ((d->d_addr.sin.sin_addr.s_addr = inet_addr(addr))
+ == INADDR_NONE))
{
register struct hostent *hp;
@@ -1325,21 +1539,19 @@ setsockaddroptions(p, d)
memmove(&d->d_addr.sin.sin_addr,
*(hp->h_addr_list),
INADDRSZ);
-# if _FFR_FREEHOSTENT && NETINET6
+# if NETINET6
freehostent(hp);
hp = NULL;
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+# endif /* NETINET6 */
}
}
break;
-# endif /* NETINET */
+#endif /* NETINET */
-# if NETINET6
+#if NETINET6
case AF_INET6:
- if (!isascii(*addr) ||
- (!isxdigit(*addr) && *addr != ':') ||
- inet_pton(AF_INET6, addr,
- &d->d_addr.sin6.sin6_addr) != 1)
+ if (anynet_pton(AF_INET6, addr,
+ &d->d_addr.sin6.sin6_addr) != 1)
{
register struct hostent *hp;
@@ -1359,14 +1571,12 @@ setsockaddroptions(p, d)
memmove(&d->d_addr.sin6.sin6_addr,
*(hp->h_addr_list),
IN6ADDRSZ);
-# if _FFR_FREEHOSTENT
freehostent(hp);
hp = NULL;
-# endif /* _FFR_FREEHOSTENT */
}
}
break;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
default:
syserr("554 5.3.5 address= option unsupported for family %d",
@@ -1379,16 +1589,17 @@ setsockaddroptions(p, d)
{
switch (d->d_addr.sa.sa_family)
{
-# if NETINET
+#if NETINET
case AF_INET:
if (isascii(*port) && isdigit(*port))
- d->d_addr.sin.sin_port = htons((u_short)atoi((const char *)port));
+ d->d_addr.sin.sin_port = htons((unsigned short)
+ atoi((const char *) port));
else
{
-# ifdef NO_GETSERVBYNAME
+# ifdef NO_GETSERVBYNAME
syserr("554 5.3.5 invalid port number: %s",
port);
-# else /* NO_GETSERVBYNAME */
+# else /* NO_GETSERVBYNAME */
register struct servent *sp;
sp = getservbyname(port, "tcp");
@@ -1397,21 +1608,22 @@ setsockaddroptions(p, d)
port);
else
d->d_addr.sin.sin_port = sp->s_port;
-# endif /* NO_GETSERVBYNAME */
+# endif /* NO_GETSERVBYNAME */
}
break;
-# endif /* NETINET */
+#endif /* NETINET */
-# if NETINET6
+#if NETINET6
case AF_INET6:
if (isascii(*port) && isdigit(*port))
- d->d_addr.sin6.sin6_port = htons((u_short)atoi(port));
+ d->d_addr.sin6.sin6_port = htons((unsigned short)
+ atoi(port));
else
{
-# ifdef NO_GETSERVBYNAME
+# ifdef NO_GETSERVBYNAME
syserr("554 5.3.5 invalid port number: %s",
port);
-# else /* NO_GETSERVBYNAME */
+# else /* NO_GETSERVBYNAME */
register struct servent *sp;
sp = getservbyname(port, "tcp");
@@ -1420,22 +1632,22 @@ setsockaddroptions(p, d)
port);
else
d->d_addr.sin6.sin6_port = sp->s_port;
-# endif /* NO_GETSERVBYNAME */
+# endif /* NO_GETSERVBYNAME */
}
break;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
-# if NETISO
+#if NETISO
case AF_ISO:
/* assume two byte transport selector */
if (isascii(*port) && isdigit(*port))
- portno = htons((u_short)atoi(port));
+ portno = htons((unsigned short) atoi(port));
else
{
-# ifdef NO_GETSERVBYNAME
+# ifdef NO_GETSERVBYNAME
syserr("554 5.3.5 invalid port number: %s",
port);
-# else /* NO_GETSERVBYNAME */
+# else /* NO_GETSERVBYNAME */
register struct servent *sp;
sp = getservbyname(port, "tcp");
@@ -1444,12 +1656,12 @@ setsockaddroptions(p, d)
port);
else
portno = sp->s_port;
-# endif /* NO_GETSERVBYNAME */
+# endif /* NO_GETSERVBYNAME */
}
memmove(TSEL(&d->d_addr.siso),
(char *) &portno, 2);
break;
-# endif /* NETISO */
+#endif /* NETISO */
default:
syserr("554 5.3.5 Port= option unsupported for family %d",
@@ -1458,50 +1670,62 @@ setsockaddroptions(p, d)
}
}
}
- /*
+/*
** SETDAEMONOPTIONS -- set options for running the MTA daemon
**
** Parameters:
** p -- the options line.
**
** Returns:
-** TRUE if successful, FALSE otherwise.
+** true if successful, false otherwise.
+**
+** Side Effects:
+** increments number of daemons.
*/
+#define DEF_LISTENQUEUE 10
+
bool
setdaemonoptions(p)
register char *p;
{
- if (ndaemons >= MAXDAEMONS)
- return FALSE;
- Daemons[ndaemons].d_socket = -1;
- Daemons[ndaemons].d_listenqueue = 10;
- clrbitmap(Daemons[ndaemons].d_flags);
- setsockaddroptions(p, &Daemons[ndaemons]);
-
- if (Daemons[ndaemons].d_name != NULL)
- Daemons[ndaemons].d_name = newstr(Daemons[ndaemons].d_name);
+ if (NDaemons >= MAXDAEMONS)
+ return false;
+ Daemons[NDaemons].d_socket = -1;
+ Daemons[NDaemons].d_listenqueue = DEF_LISTENQUEUE;
+ clrbitmap(Daemons[NDaemons].d_flags);
+ setsockaddroptions(p, &Daemons[NDaemons]);
+
+#if MILTER
+# if _FFR_MILTER_PERDAEMON
+ if (Daemons[NDaemons].d_inputfilterlist != NULL)
+ Daemons[NDaemons].d_inputfilterlist = newstr(Daemons[NDaemons].d_inputfilterlist);
+# endif /* _FFR_MILTER_PERDAEMON */
+#endif /* MILTER */
+
+ if (Daemons[NDaemons].d_name != NULL)
+ Daemons[NDaemons].d_name = newstr(Daemons[NDaemons].d_name);
else
{
char num[30];
- snprintf(num, sizeof num, "Daemon%d", ndaemons);
- Daemons[ndaemons].d_name = newstr(num);
+ (void) sm_snprintf(num, sizeof num, "Daemon%d", NDaemons);
+ Daemons[NDaemons].d_name = newstr(num);
}
if (tTd(37, 1))
{
- dprintf("Daemon %s flags: ", Daemons[ndaemons].d_name);
- if (bitnset(D_ETRNONLY, Daemons[ndaemons].d_flags))
- dprintf("ETRNONLY ");
- if (bitnset(D_NOETRN, Daemons[ndaemons].d_flags))
- dprintf("NOETRN ");
- dprintf("\n");
+ sm_dprintf("Daemon %s flags: ", Daemons[NDaemons].d_name);
+ if (bitnset(D_ETRNONLY, Daemons[NDaemons].d_flags))
+ sm_dprintf("ETRNONLY ");
+ if (bitnset(D_NOETRN, Daemons[NDaemons].d_flags))
+ sm_dprintf("NOETRN ");
+ sm_dprintf("\n");
}
- ++ndaemons;
- return TRUE;
+ ++NDaemons;
+ return true;
}
- /*
+/*
** INITDAEMON -- initialize daemon if not yet done.
**
** Parameters:
@@ -1513,18 +1737,19 @@ setdaemonoptions(p)
** Side Effects:
** initializes structure for one daemon.
*/
+
void
initdaemon()
{
- if (ndaemons == 0)
+ if (NDaemons == 0)
{
- Daemons[ndaemons].d_socket = -1;
- Daemons[ndaemons].d_listenqueue = 10;
- Daemons[ndaemons].d_name = "Daemon0";
- ndaemons = 1;
+ Daemons[NDaemons].d_socket = -1;
+ Daemons[NDaemons].d_listenqueue = DEF_LISTENQUEUE;
+ Daemons[NDaemons].d_name = "Daemon0";
+ NDaemons = 1;
}
}
- /*
+/*
** SETCLIENTOPTIONS -- set options for running the client
**
** Parameters:
@@ -1534,29 +1759,33 @@ initdaemon()
** none.
*/
-static SOCKADDR ClientAddr; /* address for client */
+static DAEMON_T ClientSettings[AF_MAX + 1];
void
setclientoptions(p)
register char *p;
{
- struct daemon d;
- extern ENVELOPE BlankEnvelope;
+ int family;
+ DAEMON_T d;
memset(&d, '\0', sizeof d);
setsockaddroptions(p, &d);
/* grab what we need */
- memcpy(&ClientAddr, &d.d_addr, sizeof ClientAddr);
- TcpSndBufferSize = d.d_tcpsndbufsize;
- TcpRcvBufferSize = d.d_tcprcvbufsize;
- if (d.d_mflags != NULL)
- define(macid("{client_flags}", NULL), d.d_mflags,
- &BlankEnvelope);
+ family = d.d_addr.sa.sa_family;
+ STRUCTCOPY(d, ClientSettings[family]);
+ setbitn(D_ISSET, ClientSettings[family].d_flags); /* mark as set */
+ if (d.d_name != NULL)
+ ClientSettings[family].d_name = newstr(d.d_name);
else
- define(macid("{client_flags}", NULL), "", &BlankEnvelope);
+ {
+ char num[30];
+
+ (void) sm_snprintf(num, sizeof num, "Client%d", family);
+ ClientSettings[family].d_name = newstr(num);
+ }
}
- /*
+/*
** ADDR_FAMILY -- determine address family from address
**
** Parameters:
@@ -1573,31 +1802,107 @@ static int
addr_family(addr)
char *addr;
{
-# if NETINET6
+#if NETINET6
SOCKADDR clt_addr;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
-# if NETINET
+#if NETINET
if (inet_addr(addr) != INADDR_NONE)
{
if (tTd(16, 9))
- printf("addr_family(%s): INET\n", addr);
+ sm_dprintf("addr_family(%s): INET\n", addr);
return AF_INET;
}
-# endif /* NETINET */
-# if NETINET6
- if (inet_pton(AF_INET6, addr, &clt_addr.sin6.sin6_addr) == 1)
+#endif /* NETINET */
+#if NETINET6
+ if (anynet_pton(AF_INET6, addr, &clt_addr.sin6.sin6_addr) == 1)
{
if (tTd(16, 9))
- printf("addr_family(%s): INET6\n", addr);
+ sm_dprintf("addr_family(%s): INET6\n", addr);
return AF_INET6;
}
-# endif /* NETINET6 */
+#endif /* NETINET6 */
+#if _FFR_DAEMON_NETUNIX
+# if NETUNIX
+ if (*addr == '/')
+ {
+ if (tTd(16, 9))
+ sm_dprintf("addr_family(%s): LOCAL\n", addr);
+ return AF_UNIX;
+ }
+# endif /* NETUNIX */
+#endif /* _FFR_DAEMON_NETUNIX */
if (tTd(16, 9))
- printf("addr_family(%s): UNSPEC\n", addr);
+ sm_dprintf("addr_family(%s): UNSPEC\n", addr);
return AF_UNSPEC;
}
- /*
+
+/*
+** CHKCLIENTMODIFIERS -- check whether all clients have set a flag.
+**
+** Parameters:
+** flag -- the flag to test.
+**
+** Returns:
+** true iff all configured clients have set the flag.
+*/
+
+bool
+chkclientmodifiers(flag)
+ int flag;
+{
+ int i;
+ bool flagisset;
+
+ flagisset = false;
+ for (i = 0; i < AF_MAX; i++)
+ {
+ if (bitnset(D_ISSET, ClientSettings[i].d_flags))
+ {
+ if (!bitnset((char) flag, ClientSettings[i].d_flags))
+ return false;
+ flagisset = true;
+ }
+ }
+ return flagisset;
+}
+
+#if MILTER
+# if _FFR_MILTER_PERDAEMON
+/*
+** SETUP_DAEMON_FILTERS -- Parse per-socket filters
+**
+** Parameters:
+** none
+**
+** Returns:
+** none
+*/
+
+void
+setup_daemon_milters()
+{
+ int idx;
+
+ if (OpMode == MD_SMTP)
+ {
+ /* no need to configure the daemons */
+ return;
+ }
+
+ for (idx = 0; idx < NDaemons; idx++)
+ {
+ if (Daemons[idx].d_inputfilterlist != NULL)
+ {
+ milter_config(Daemons[idx].d_inputfilterlist,
+ Daemons[idx].d_inputfilters,
+ MAXFILTERS);
+ }
+ }
+}
+# endif /* _FFR_MILTER_PERDAEMON */
+#endif /* MILTER */
+/*
** MAKECONNECTION -- make a connection to an SMTP socket on a machine.
**
** Parameters:
@@ -1606,6 +1911,8 @@ addr_family(addr)
** mci -- a pointer to the mail connection information
** structure to be filled in.
** e -- the current envelope.
+** enough -- time at which to stop further connection attempts.
+** (0 means no limit)
**
** Returns:
** An exit code telling whether the connection could be
@@ -1620,24 +1927,25 @@ static jmp_buf CtxConnectTimeout;
SOCKADDR CurHostAddr; /* address of current host */
int
-makeconnection(host, port, mci, e)
+makeconnection(host, port, mci, e, enough)
char *host;
- volatile u_int port;
+ volatile unsigned int port;
register MCI *mci;
ENVELOPE *e;
+ time_t enough;
{
register volatile int addrno = 0;
- register volatile int s;
- register struct hostent *volatile hp = (struct hostent *)NULL;
+ volatile int s;
+ register struct hostent *volatile hp = (struct hostent *) NULL;
SOCKADDR addr;
SOCKADDR clt_addr;
int save_errno = 0;
volatile SOCKADDR_LEN_T addrlen;
volatile bool firstconnect;
- EVENT *volatile ev = NULL;
-# if NETINET6
- volatile bool v6found = FALSE;
-# endif /* NETINET6 */
+ SM_EVENT *volatile ev = NULL;
+#if NETINET6
+ volatile bool v6found = false;
+#endif /* NETINET6 */
volatile int family = InetMode;
SOCKADDR_LEN_T len;
volatile SOCKADDR_LEN_T socksize = 0;
@@ -1646,9 +1954,9 @@ makeconnection(host, port, mci, e)
char *p;
extern ENVELOPE BlankEnvelope;
- /* retranslate ${daemon_flags} into bitmap */
+ /* retranslate {daemon_flags} into bitmap */
clrbitmap(d_flags);
- if ((p = macvalue(macid("{daemon_flags}", NULL), e)) != NULL)
+ if ((p = macvalue(macid("{daemon_flags}"), e)) != NULL)
{
for (; *p != '\0'; p++)
{
@@ -1657,33 +1965,19 @@ makeconnection(host, port, mci, e)
}
}
- /* "add" ${client_flags} to bitmap */
- if ((p = macvalue(macid("{client_flags}", NULL), e)) != NULL)
- {
- for (; *p != '\0'; p++)
- {
- /* look for just this one flag */
- if (*p == D_IFNHELO)
- {
- setbitn(bitidx(*p), d_flags);
- break;
- }
- }
- }
-
-# if NETINET6
+#if NETINET6
v4retry:
-# endif /* NETINET6 */
- clt_bind = FALSE;
+#endif /* NETINET6 */
+ clt_bind = false;
/* Set up the address for outgoing connection. */
if (bitnset(D_BINDIF, d_flags) &&
- (p = macvalue(macid("{if_addr}", NULL), e)) != NULL &&
+ (p = macvalue(macid("{if_addr}"), e)) != NULL &&
*p != '\0')
{
-# if NETINET6
+#if NETINET6
char p6[INET6_ADDRSTRLEN];
-# endif /* NETINET6 */
+#endif /* NETINET6 */
memset(&clt_addr, '\0', sizeof clt_addr);
@@ -1691,79 +1985,80 @@ makeconnection(host, port, mci, e)
clt_addr.sa.sa_family = addr_family(p);
switch (clt_addr.sa.sa_family)
{
-# if NETINET
+#if NETINET
case AF_INET:
clt_addr.sin.sin_addr.s_addr = inet_addr(p);
if (clt_addr.sin.sin_addr.s_addr != INADDR_NONE &&
clt_addr.sin.sin_addr.s_addr != INADDR_LOOPBACK)
{
- clt_bind = TRUE;
+ clt_bind = true;
socksize = sizeof (struct sockaddr_in);
}
break;
-# endif /* NETINET */
+#endif /* NETINET */
-# if NETINET6
+#if NETINET6
case AF_INET6:
if (inet_addr(p) != INADDR_NONE)
- snprintf(p6, sizeof p6, "::ffff:%s", p);
+ (void) sm_snprintf(p6, sizeof p6,
+ "IPv6:::ffff:%s", p);
else
- strlcpy(p6, p, sizeof p6);
- if (inet_pton(AF_INET6, p6,
- &clt_addr.sin6.sin6_addr) == 1 &&
+ (void) sm_strlcpy(p6, p, sizeof p6);
+ if (anynet_pton(AF_INET6, p6,
+ &clt_addr.sin6.sin6_addr) == 1 &&
!IN6_IS_ADDR_LOOPBACK(&clt_addr.sin6.sin6_addr))
{
- clt_bind = TRUE;
+ clt_bind = true;
socksize = sizeof (struct sockaddr_in6);
}
break;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
-# if 0
+#if 0
default:
syserr("554 5.3.5 Address= option unsupported for family %d",
clt_addr.sa.sa_family);
break;
-# endif /* 0 */
+#endif /* 0 */
}
if (clt_bind)
family = clt_addr.sa.sa_family;
}
- else
+
+ /* D_BINDIF not set or not available, fallback to ClientPortOptions */
+ if (!clt_bind)
{
- STRUCTCOPY(ClientAddr, clt_addr);
- if (clt_addr.sa.sa_family == AF_UNSPEC)
- clt_addr.sa.sa_family = family;
+ STRUCTCOPY(ClientSettings[family].d_addr, clt_addr);
switch (clt_addr.sa.sa_family)
{
-# if NETINET
+#if NETINET
case AF_INET:
if (clt_addr.sin.sin_addr.s_addr == 0)
clt_addr.sin.sin_addr.s_addr = INADDR_ANY;
else
- clt_bind = TRUE;
+ clt_bind = true;
if (clt_addr.sin.sin_port != 0)
- clt_bind = TRUE;
+ clt_bind = true;
socksize = sizeof (struct sockaddr_in);
break;
-# endif /* NETINET */
-# if NETINET6
+#endif /* NETINET */
+#if NETINET6
case AF_INET6:
if (IN6_IS_ADDR_UNSPECIFIED(&clt_addr.sin6.sin6_addr))
clt_addr.sin6.sin6_addr = in6addr_any;
else
- clt_bind = TRUE;
+ clt_bind = true;
socksize = sizeof (struct sockaddr_in6);
if (clt_addr.sin6.sin6_port != 0)
- clt_bind = TRUE;
+ clt_bind = true;
break;
-# endif /* NETINET6 */
-# if NETISO
+#endif /* NETINET6 */
+#if NETISO
case AF_ISO:
socksize = sizeof clt_addr.siso;
- clt_bind = TRUE;
+ clt_bind = true;
break;
-# endif /* NETISO */
+#endif /* NETISO */
default:
break;
}
@@ -1774,9 +2069,7 @@ makeconnection(host, port, mci, e)
** Accept "[a.b.c.d]" syntax for host name.
*/
-# if NAMED_BIND
SM_SET_H_ERRNO(0);
-# endif /* NAMED_BIND */
errno = 0;
memset(&CurHostAddr, '\0', sizeof CurHostAddr);
memset(&addr, '\0', sizeof addr);
@@ -1788,18 +2081,18 @@ makeconnection(host, port, mci, e)
p = strchr(host, ']');
if (p != NULL)
{
-# if NETINET
+#if NETINET
unsigned long hid = INADDR_NONE;
-# endif /* NETINET */
-# if NETINET6
+#endif /* NETINET */
+#if NETINET6
struct sockaddr_in6 hid6;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
*p = '\0';
-# if NETINET6
+#if NETINET6
memset(&hid6, '\0', sizeof hid6);
-# endif /* NETINET6 */
-# if NETINET
+#endif /* NETINET6 */
+#if NETINET
if (family == AF_INET &&
(hid = inet_addr(&host[1])) != INADDR_NONE)
{
@@ -1807,34 +2100,34 @@ makeconnection(host, port, mci, e)
addr.sin.sin_addr.s_addr = hid;
}
else
-# endif /* NETINET */
-# if NETINET6
+#endif /* NETINET */
+#if NETINET6
if (family == AF_INET6 &&
- inet_pton(AF_INET6, &host[1],
- &hid6.sin6_addr) == 1)
+ anynet_pton(AF_INET6, &host[1],
+ &hid6.sin6_addr) == 1)
{
addr.sin6.sin6_family = AF_INET6;
addr.sin6.sin6_addr = hid6.sin6_addr;
}
else
-# endif /* NETINET6 */
+#endif /* NETINET6 */
{
/* try it as a host name (avoid MX lookup) */
hp = sm_gethostbyname(&host[1], family);
if (hp == NULL && p[-1] == '.')
{
-# if NAMED_BIND
+#if NAMED_BIND
int oldopts = _res.options;
_res.options &= ~(RES_DEFNAMES|RES_DNSRCH);
-# endif /* NAMED_BIND */
+#endif /* NAMED_BIND */
p[-1] = '\0';
hp = sm_gethostbyname(&host[1],
family);
p[-1] = '.';
-# if NAMED_BIND
+#if NAMED_BIND
_res.options = oldopts;
-# endif /* NAMED_BIND */
+#endif /* NAMED_BIND */
}
*p = ']';
goto gothostent;
@@ -1861,34 +2154,54 @@ makeconnection(host, port, mci, e)
hp = sm_gethostbyname(host, family);
if (hp == NULL && *p == '.')
{
-# if NAMED_BIND
+#if NAMED_BIND
int oldopts = _res.options;
_res.options &= ~(RES_DEFNAMES|RES_DNSRCH);
-# endif /* NAMED_BIND */
+#endif /* NAMED_BIND */
*p = '\0';
hp = sm_gethostbyname(host, family);
*p = '.';
-# if NAMED_BIND
+#if NAMED_BIND
_res.options = oldopts;
-# endif /* NAMED_BIND */
+#endif /* NAMED_BIND */
}
}
gothostent:
if (hp == NULL)
{
-# if NAMED_BIND
+#if NAMED_BIND
/* check for name server timeouts */
- if (errno == ETIMEDOUT || h_errno == TRY_AGAIN ||
- (errno == ECONNREFUSED && UseNameServer))
+# if NETINET6
+ if (WorkAroundBrokenAAAA && family == AF_INET6 &&
+ errno == ETIMEDOUT)
{
- save_errno = errno;
- mci_setstat(mci, EX_TEMPFAIL, "4.4.3", NULL);
- errno = save_errno;
- return EX_TEMPFAIL;
+ /*
+ ** An attempt with family AF_INET may
+ ** succeed By skipping the next section
+ ** of code, we will try AF_INET before
+ ** failing.
+ */
+
+ if (tTd(16, 10))
+ sm_dprintf("makeconnection: WorkAroundBrokenAAAA: Trying AF_INET lookup (AF_INET6 failed)\n");
}
-# endif /* NAMED_BIND */
-# if NETINET6
+ else
+# endif /* NETINET6 */
+ {
+ if (errno == ETIMEDOUT ||
+ h_errno == TRY_AGAIN ||
+ (errno == ECONNREFUSED && UseNameServer))
+ {
+ save_errno = errno;
+ mci_setstat(mci, EX_TEMPFAIL,
+ "4.4.3", NULL);
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+ }
+#endif /* NAMED_BIND */
+#if NETINET6
/*
** Try v6 first, then fall back to v4.
** If we found a v6 address, but no v4
@@ -1902,7 +2215,7 @@ gothostent:
}
if (v6found)
goto v6tempfail;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
save_errno = errno;
mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
errno = save_errno;
@@ -1911,21 +2224,21 @@ gothostent:
addr.sa.sa_family = hp->h_addrtype;
switch (hp->h_addrtype)
{
-# if NETINET
+#if NETINET
case AF_INET:
memmove(&addr.sin.sin_addr,
hp->h_addr,
INADDRSZ);
break;
-# endif /* NETINET */
+#endif /* NETINET */
-# if NETINET6
+#if NETINET6
case AF_INET6:
memmove(&addr.sin6.sin6_addr,
hp->h_addr,
IN6ADDRSZ);
break;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
default:
if (hp->h_length > sizeof addr.sa.sa_data)
@@ -1936,9 +2249,7 @@ gothostent:
errno = EINVAL;
return EX_NOHOST;
}
- memmove(addr.sa.sa_data,
- hp->h_addr,
- hp->h_length);
+ memmove(addr.sa.sa_data, hp->h_addr, hp->h_length);
break;
}
addrno = 1;
@@ -1950,9 +2261,9 @@ gothostent:
if (port == 0)
{
-# ifdef NO_GETSERVBYNAME
+#ifdef NO_GETSERVBYNAME
port = htons(25);
-# else /* NO_GETSERVBYNAME */
+#else /* NO_GETSERVBYNAME */
register struct servent *sp = getservbyname("smtp", "tcp");
if (sp == NULL)
@@ -1964,41 +2275,56 @@ gothostent:
}
else
port = sp->s_port;
-# endif /* NO_GETSERVBYNAME */
+#endif /* NO_GETSERVBYNAME */
+ }
+
+#if NETINET6
+ if (addr.sa.sa_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr) &&
+ ClientSettings[AF_INET].d_addr.sa.sa_family != 0)
+ {
+ /*
+ ** Ignore mapped IPv4 address since
+ ** there is a ClientPortOptions setting
+ ** for IPv4.
+ */
+
+ goto nextaddr;
}
+#endif /* NETINET6 */
switch (addr.sa.sa_family)
{
-# if NETINET
+#if NETINET
case AF_INET:
addr.sin.sin_port = port;
addrlen = sizeof (struct sockaddr_in);
break;
-# endif /* NETINET */
+#endif /* NETINET */
-# if NETINET6
+#if NETINET6
case AF_INET6:
addr.sin6.sin6_port = port;
addrlen = sizeof (struct sockaddr_in6);
break;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
-# if NETISO
+#if NETISO
case AF_ISO:
/* assume two byte transport selector */
memmove(TSEL((struct sockaddr_iso *) &addr), (char *) &port, 2);
addrlen = sizeof (struct sockaddr_iso);
break;
-# endif /* NETISO */
+#endif /* NETISO */
default:
syserr("Can't connect to address family %d", addr.sa.sa_family);
mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
errno = EINVAL;
-# if _FFR_FREEHOSTENT && NETINET6
+#if NETINET6
if (hp != NULL)
freehostent(hp);
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+#endif /* NETINET6 */
return EX_NOHOST;
}
@@ -2006,29 +2332,30 @@ gothostent:
** Try to actually open the connection.
*/
-# ifdef XLA
+#if XLA
/* if too many connections, don't bother trying */
if (!xla_noqueue_ok(host))
{
-# if _FFR_FREEHOSTENT && NETINET6
+# if NETINET6
if (hp != NULL)
freehostent(hp);
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+# endif /* NETINET6 */
return EX_TEMPFAIL;
}
-# endif /* XLA */
+#endif /* XLA */
- firstconnect = TRUE;
+ firstconnect = true;
for (;;)
{
if (tTd(16, 1))
- dprintf("makeconnection (%s [%s].%d (%d))\n",
- host, anynet_ntoa(&addr), ntohs(port),
- addr.sa.sa_family);
+ sm_dprintf("makeconnection (%s [%s].%d (%d))\n",
+ host, anynet_ntoa(&addr), ntohs(port),
+ (int) addr.sa.sa_family);
/* save for logging */
CurHostAddr = addr;
+#if HASRRESVPORT
if (bitnset(M_SECURE_PORT, mci->mci_mailer->m_flags))
{
int rport = IPPORT_RESERVED - 1;
@@ -2036,47 +2363,47 @@ gothostent:
s = rresvport(&rport);
}
else
+#endif /* HASRRESVPORT */
{
- s = socket(clt_addr.sa.sa_family, SOCK_STREAM, 0);
+ s = socket(addr.sa.sa_family, SOCK_STREAM, 0);
}
if (s < 0)
{
save_errno = errno;
syserr("makeconnection: cannot create socket");
-# ifdef XLA
+#if XLA
xla_host_end(host);
-# endif /* XLA */
+#endif /* XLA */
mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
-# if _FFR_FREEHOSTENT && NETINET6
+#if NETINET6
if (hp != NULL)
freehostent(hp);
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+#endif /* NETINET6 */
errno = save_errno;
return EX_TEMPFAIL;
}
-# ifdef SO_SNDBUF
- if (TcpSndBufferSize > 0)
+#ifdef SO_SNDBUF
+ if (ClientSettings[family].d_tcpsndbufsize > 0)
{
if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
- (char *) &TcpSndBufferSize,
- sizeof(TcpSndBufferSize)) < 0)
+ (char *) &ClientSettings[family].d_tcpsndbufsize,
+ sizeof(ClientSettings[family].d_tcpsndbufsize)) < 0)
syserr("makeconnection: setsockopt(SO_SNDBUF)");
}
-# endif /* SO_SNDBUF */
-# ifdef SO_RCVBUF
- if (TcpRcvBufferSize > 0)
+#endif /* SO_SNDBUF */
+#ifdef SO_RCVBUF
+ if (ClientSettings[family].d_tcprcvbufsize > 0)
{
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
- (char *) &TcpRcvBufferSize,
- sizeof(TcpRcvBufferSize)) < 0)
+ (char *) &ClientSettings[family].d_tcprcvbufsize,
+ sizeof(ClientSettings[family].d_tcprcvbufsize)) < 0)
syserr("makeconnection: setsockopt(SO_RCVBUF)");
}
-# endif /* SO_RCVBUF */
-
+#endif /* SO_RCVBUF */
if (tTd(16, 1))
- dprintf("makeconnection: fd=%d\n", s);
+ sm_dprintf("makeconnection: fd=%d\n", s);
/* turn on network debugging? */
if (tTd(16, 101))
@@ -2086,9 +2413,9 @@ gothostent:
(void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
(char *)&on, sizeof on);
}
- if (e->e_xfp != NULL)
- (void) fflush(e->e_xfp); /* for debugging */
- errno = 0; /* for debugging */
+ if (e->e_xfp != NULL) /* for debugging */
+ (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
+ errno = 0; /* for debugging */
if (clt_bind)
{
@@ -2096,7 +2423,7 @@ gothostent:
switch (clt_addr.sa.sa_family)
{
-# if NETINET
+#if NETINET
case AF_INET:
if (clt_addr.sin.sin_port != 0)
(void) setsockopt(s, SOL_SOCKET,
@@ -2104,9 +2431,9 @@ gothostent:
(char *) &on,
sizeof on);
break;
-# endif /* NETINET */
+#endif /* NETINET */
-# if NETINET6
+#if NETINET6
case AF_INET6:
if (clt_addr.sin6.sin6_port != 0)
(void) setsockopt(s, SOL_SOCKET,
@@ -2114,7 +2441,7 @@ gothostent:
(char *) &on,
sizeof on);
break;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
}
if (bind(s, &clt_addr.sa, socksize) < 0)
@@ -2124,10 +2451,10 @@ gothostent:
errno = save_errno;
syserr("makeconnection: cannot bind socket [%s]",
anynet_ntoa(&clt_addr));
-# if _FFR_FREEHOSTENT && NETINET6
+#if NETINET6
if (hp != NULL)
freehostent(hp);
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+#endif /* NETINET6 */
errno = save_errno;
return EX_TEMPFAIL;
}
@@ -2143,34 +2470,34 @@ gothostent:
int i;
if (e->e_ntries <= 0 && TimeOuts.to_iconnect != 0)
- ev = setevent(TimeOuts.to_iconnect,
- connecttimeout, 0);
+ ev = sm_setevent(TimeOuts.to_iconnect,
+ connecttimeout, 0);
else if (TimeOuts.to_connect != 0)
- ev = setevent(TimeOuts.to_connect,
- connecttimeout, 0);
+ ev = sm_setevent(TimeOuts.to_connect,
+ connecttimeout, 0);
else
ev = NULL;
switch (ConnectOnlyTo.sa.sa_family)
{
-# if NETINET
+#if NETINET
case AF_INET:
addr.sin.sin_addr.s_addr = ConnectOnlyTo.sin.sin_addr.s_addr;
break;
-# endif /* NETINET */
+#endif /* NETINET */
-# if NETINET6
+#if NETINET6
case AF_INET6:
memmove(&addr.sin6.sin6_addr,
&ConnectOnlyTo.sin6.sin6_addr,
IN6ADDRSZ);
break;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
}
i = connect(s, (struct sockaddr *) &addr, addrlen);
save_errno = errno;
if (ev != NULL)
- clrevent(ev);
+ sm_clrevent(ev);
if (i >= 0)
break;
}
@@ -2178,12 +2505,13 @@ gothostent:
save_errno = errno;
/* if running demand-dialed connection, try again */
- if (DialDelay > 0 && firstconnect)
+ if (DialDelay > 0 && firstconnect &&
+ bitnset(M_DIALDELAY, mci->mci_mailer->m_flags))
{
if (tTd(16, 1))
- dprintf("Connect failed (%s); trying again...\n",
- errstring(save_errno));
- firstconnect = FALSE;
+ sm_dprintf("Connect failed (%s); trying again...\n",
+ sm_errstring(save_errno));
+ firstconnect = false;
(void) sleep(DialDelay);
continue;
}
@@ -2191,34 +2519,38 @@ gothostent:
/* couldn't connect.... figure out why */
(void) close(s);
- if (LogLevel >= 14)
+ if (LogLevel > 13)
sm_syslog(LOG_INFO, e->e_id,
"makeconnection (%s [%s]) failed: %s",
host, anynet_ntoa(&addr),
- errstring(save_errno));
+ sm_errstring(save_errno));
- if (hp != NULL && hp->h_addr_list[addrno] != NULL)
+#if NETINET6
+nextaddr:
+#endif /* NETINET6 */
+ if (hp != NULL && hp->h_addr_list[addrno] != NULL &&
+ (enough == 0 || curtime() < enough))
{
if (tTd(16, 1))
- dprintf("Connect failed (%s); trying new address....\n",
- errstring(save_errno));
+ sm_dprintf("Connect failed (%s); trying new address....\n",
+ sm_errstring(save_errno));
switch (addr.sa.sa_family)
{
-# if NETINET
+#if NETINET
case AF_INET:
memmove(&addr.sin.sin_addr,
hp->h_addr_list[addrno++],
INADDRSZ);
break;
-# endif /* NETINET */
+#endif /* NETINET */
-# if NETINET6
+#if NETINET6
case AF_INET6:
memmove(&addr.sin6.sin6_addr,
hp->h_addr_list[addrno++],
IN6ADDRSZ);
break;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
default:
memmove(addr.sa.sa_data,
@@ -2230,84 +2562,108 @@ gothostent:
}
errno = save_errno;
-# if NETINET6
+#if NETINET6
if (family == AF_INET6)
{
if (tTd(16, 1))
- dprintf("Connect failed (%s); retrying with AF_INET....\n",
- errstring(save_errno));
- v6found = TRUE;
+ sm_dprintf("Connect failed (%s); retrying with AF_INET....\n",
+ sm_errstring(save_errno));
+ v6found = true;
family = AF_INET;
-# if _FFR_FREEHOSTENT
if (hp != NULL)
{
freehostent(hp);
hp = NULL;
}
-# endif /* _FFR_FREEHOSTENT */
goto v4retry;
}
v6tempfail:
-# endif /* NETINET6 */
+#endif /* NETINET6 */
/* couldn't open connection */
-# if NETINET6
+#if NETINET6
/* Don't clobber an already saved errno from v4retry */
if (errno > 0)
-# endif /* NETINET6 */
+#endif /* NETINET6 */
save_errno = errno;
if (tTd(16, 1))
- dprintf("Connect failed (%s)\n", errstring(save_errno));
-# ifdef XLA
+ sm_dprintf("Connect failed (%s)\n",
+ sm_errstring(save_errno));
+#if XLA
xla_host_end(host);
-# endif /* XLA */
+#endif /* XLA */
mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL);
-# if _FFR_FREEHOSTENT && NETINET6
+#if NETINET6
if (hp != NULL)
freehostent(hp);
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+#endif /* NETINET6 */
errno = save_errno;
return EX_TEMPFAIL;
}
-# if _FFR_FREEHOSTENT && NETINET6
+#if NETINET6
if (hp != NULL)
{
freehostent(hp);
hp = NULL;
}
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+#endif /* NETINET6 */
/* connection ok, put it into canonical form */
mci->mci_out = NULL;
- if ((mci->mci_out = fdopen(s, "w")) == NULL ||
+ if ((mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
+ (void *) &s,
+ SM_IO_WRONLY, NULL)) == NULL ||
(s = dup(s)) < 0 ||
- (mci->mci_in = fdopen(s, "r")) == NULL)
+ (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
+ (void *) &s,
+ SM_IO_RDONLY, NULL)) == NULL)
{
save_errno = errno;
syserr("cannot open SMTP client channel, fd=%d", s);
mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
if (mci->mci_out != NULL)
- (void) fclose(mci->mci_out);
+ (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
(void) close(s);
errno = save_errno;
return EX_TEMPFAIL;
}
+ sm_io_automode(mci->mci_out, mci->mci_in);
+
+ /* set {client_flags} */
+ if (ClientSettings[addr.sa.sa_family].d_mflags != NULL)
+ {
+ macdefine(&mci->mci_macro, A_PERM,
+ macid("{client_flags}"),
+ ClientSettings[addr.sa.sa_family].d_mflags);
+ }
+ else
+ macdefine(&mci->mci_macro, A_PERM,
+ macid("{client_flags}"), "");
+
+ /* "add" {client_flags} to bitmap */
+ if (bitnset(D_IFNHELO, ClientSettings[addr.sa.sa_family].d_flags))
+ {
+ /* look for just this one flag */
+ setbitn(D_IFNHELO, d_flags);
+ }
/* find out name for Interface through which we connect */
len = sizeof addr;
if (getsockname(s, &addr.sa, &len) == 0)
{
char *name;
- char *p;
+ char family[5];
- define(macid("{if_addr}", NULL), newstr(anynet_ntoa(&addr)),
- &BlankEnvelope);
- p = xalloc(5);
- snprintf(p, 4, "%d", addr.sa.sa_family);
- define(macid("{if_family}", NULL), p, &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{if_addr_out}"), anynet_ntoa(&addr));
+ (void) sm_snprintf(family, sizeof(family), "%d",
+ addr.sa.sa_family);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{if_family_out}"), family);
name = hostnamebyanyaddr(&addr);
- define(macid("{if_name}", NULL), newstr(name), &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{if_name_out}"), name);
if (LogLevel > 11)
{
/* log connection information */
@@ -2322,9 +2678,12 @@ gothostent:
}
else
{
- define(macid("{if_name}", NULL), NULL, &BlankEnvelope);
- define(macid("{if_addr}", NULL), NULL, &BlankEnvelope);
- define(macid("{if_family}", NULL), NULL, &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{if_name_out}"), NULL);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{if_addr_out}"), NULL);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{if_family_out}"), NULL);
}
mci_setstat(mci, EX_OK, NULL, NULL);
return EX_OK;
@@ -2342,7 +2701,7 @@ connecttimeout()
errno = ETIMEDOUT;
longjmp(CtxConnectTimeout, 1);
}
- /*
+/*
** MAKECONNECTION_DS -- make a connection to a domain socket.
**
** Parameters:
@@ -2358,8 +2717,9 @@ connecttimeout()
** none.
*/
-# if NETUNIX
-int makeconnection_ds(mux_path, mci)
+#if NETUNIX
+int
+makeconnection_ds(mux_path, mci)
char *mux_path;
register MCI *mci;
{
@@ -2387,12 +2747,14 @@ int makeconnection_ds(mux_path, mci)
if (strlen(mux_path) >= sizeof unix_addr.sun_path)
{
syserr("makeconnection_ds: domain socket name too long");
- /* XXX why TEMPFAIL ? */
+
+ /* XXX why TEMPFAIL but 5.x.y ? */
mci_setstat(mci, EX_TEMPFAIL, "5.3.5", NULL);
errno = ENAMETOOLONG;
return EX_UNAVAILABLE;
}
- (void) strlcpy(unix_addr.sun_path, mux_path, sizeof unix_addr.sun_path);
+ (void) sm_strlcpy(unix_addr.sun_path, mux_path,
+ sizeof unix_addr.sun_path);
/* initialize domain socket */
sock = socket(AF_UNIX, SOCK_STREAM, 0);
@@ -2419,56 +2781,103 @@ int makeconnection_ds(mux_path, mci)
/* connection ok, put it into canonical form */
mci->mci_out = NULL;
- if ((mci->mci_out = fdopen(sock, "w")) == NULL ||
- (sock = dup(sock)) < 0 ||
- (mci->mci_in = fdopen(sock, "r")) == NULL)
+ if ((mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
+ (void *) &sock, SM_IO_WRONLY, NULL))
+ == NULL
+ || (sock = dup(sock)) < 0 ||
+ (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
+ (void *) &sock, SM_IO_RDONLY, NULL))
+ == NULL)
{
save_errno = errno;
syserr("cannot open SMTP client channel, fd=%d", sock);
mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
if (mci->mci_out != NULL)
- (void) fclose(mci->mci_out);
+ (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
(void) close(sock);
errno = save_errno;
return EX_TEMPFAIL;
}
+ sm_io_automode(mci->mci_out, mci->mci_in);
mci_setstat(mci, EX_OK, NULL, NULL);
errno = 0;
return EX_OK;
}
-# endif /* NETUNIX */
- /*
-** SIGHUP -- handle a SIGHUP signal
+#endif /* NETUNIX */
+/*
+** SHUTDOWN_DAEMON -- Performs a clean shutdown of the daemon
**
** Parameters:
-** sig -- incoming signal.
+** none.
**
** Returns:
** none.
**
** Side Effects:
-** Sets RestartRequest which should cause the daemon
-** to restart.
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
+** closes control socket, exits.
*/
-/* ARGSUSED */
-static SIGFUNC_DECL
-sighup(sig)
- int sig;
+void
+shutdown_daemon()
{
- int save_errno = errno;
+ int i;
+ char *reason;
- FIX_SYSV_SIGNAL(sig, sighup);
- RestartRequest = "signal";
- errno = save_errno;
- return SIGFUNC_RETURN;
+ sm_allsignals(true);
+
+ reason = ShutdownRequest;
+ ShutdownRequest = NULL;
+ PendingSignal = 0;
+
+ if (LogLevel > 79)
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt (%s)",
+ reason == NULL ? "implicit call" : reason);
+
+ FileName = NULL;
+ closecontrolsocket(true);
+#if XLA
+ xla_all_end();
+#endif /* XLA */
+
+ for (i = 0; i < NDaemons; i++)
+ {
+ if (Daemons[i].d_socket >= 0)
+ {
+ (void) close(Daemons[i].d_socket);
+ Daemons[i].d_socket = -1;
+
+#if _FFR_DAEMON_NETUNIX
+# if NETUNIX
+ /* Remove named sockets */
+ if (Daemons[i].d_addr.sa.sa_family == AF_UNIX)
+ {
+ int rval;
+ long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_MUSTOWN|SFF_EXECOK|SFF_CREAT;
+
+ /* if not safe, don't use it */
+ rval = safefile(Daemons[i].d_addr.sunix.sun_path,
+ RunAsUid, RunAsGid,
+ RunAsUserName, sff,
+ S_IRUSR|S_IWUSR, NULL);
+ if (rval == 0 &&
+ unlink(Daemons[i].d_addr.sunix.sun_path) < 0)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "Could not remove daemon %s socket: %s: %s",
+ Daemons[i].d_name,
+ Daemons[i].d_addr.sunix.sun_path,
+ sm_errstring(errno));
+ }
+ }
+# endif /* NETUNIX */
+#endif /* _FFR_DAEMON_NETUNIX */
+ }
+ }
+
+ finis(false, true, EX_OK);
}
- /*
+/*
** RESTART_DAEMON -- Performs a clean restart of the daemon
**
** Parameters:
@@ -2485,14 +2894,15 @@ sighup(sig)
#define SM_NOOP_SIGNAL(sig, old) \
do \
{ \
- (old) = setsignal((sig), sm_signal_noop); \
+ (old) = sm_signal((sig), sm_signal_noop); \
if ((old) == SIG_IGN || (old) == SIG_DFL) \
- (void) setsignal((sig), (old)); \
+ (void) sm_signal((sig), (old)); \
} while (0)
-static void
+void
restart_daemon()
{
+ bool drop;
int i;
int save_errno;
char *reason;
@@ -2500,8 +2910,8 @@ restart_daemon()
extern int DtableSize;
/* clear the events to turn off SIGALRMs */
- clear_events();
- allsignals(TRUE);
+ sm_clear_events();
+ sm_allsignals(true);
reason = RestartRequest;
RestartRequest = NULL;
@@ -2512,21 +2922,34 @@ restart_daemon()
if (LogLevel > 3)
sm_syslog(LOG_INFO, NOQID,
"could not restart: need full path");
- finis(FALSE, EX_OSFILE);
+ finis(false, true, EX_OSFILE);
+ /* NOTREACHED */
}
if (LogLevel > 3)
sm_syslog(LOG_INFO, NOQID, "restarting %s due to %s",
SaveArgv[0],
reason == NULL ? "implicit call" : reason);
- closecontrolsocket(TRUE);
- if (drop_privileges(TRUE) != EX_OK)
+ closecontrolsocket(true);
+
+ /*
+ ** Want to drop to the user who started the process in all cases
+ ** *but* when running as "smmsp" for the clientmqueue queue run
+ ** daemon. In that case, UseMSP will be true, RunAsUid should not
+ ** be root, and RealUid should be either 0 or RunAsUid.
+ */
+
+ drop = !(UseMSP && RunAsUid != 0 &&
+ (RealUid == 0 || RealUid == RunAsUid));
+
+ if (drop_privileges(drop) != EX_OK)
{
if (LogLevel > 0)
sm_syslog(LOG_ALERT, NOQID,
- "could not set[ug]id(%d, %d): %m",
- RunAsUid, RunAsGid);
- finis(FALSE, EX_OSERR);
+ "could not drop privileges: %s",
+ sm_errstring(errno));
+ finis(false, true, EX_OSERR);
+ /* NOTREACHED */
}
/* arrange for all the files to be closed */
@@ -2537,6 +2960,9 @@ restart_daemon()
if ((j = fcntl(i, F_GETFD, 0)) != -1)
(void) fcntl(i, F_SETFD, j | FD_CLOEXEC);
}
+#if SM_CONF_SHM
+ cleanup_shm(DaemonPid == getpid());
+#endif /* SM_CONF_SHM */
/*
** Need to allow signals before execve() to make them "harmless".
@@ -2554,29 +2980,30 @@ restart_daemon()
#ifdef SIGUSR1
SM_NOOP_SIGNAL(SIGUSR1, ousr1);
#endif /* SIGUSR1 */
- allsignals(FALSE);
+ sm_allsignals(false);
(void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
save_errno = errno;
/* block signals again and restore needed signals */
- allsignals(TRUE);
+ sm_allsignals(true);
/* For finis() events */
- (void) setsignal(SIGALRM, oalrm);
+ (void) sm_signal(SIGALRM, oalrm);
#ifdef SIGUSR1
/* For debugging finis() */
- (void) setsignal(SIGUSR1, ousr1);
+ (void) sm_signal(SIGUSR1, ousr1);
#endif /* SIGUSR1 */
errno = save_errno;
if (LogLevel > 0)
- sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m",
- SaveArgv[0]);
- finis(FALSE, EX_OSFILE);
+ sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %s",
+ SaveArgv[0], sm_errstring(errno));
+ finis(false, true, EX_OSFILE);
+ /* NOTREACHED */
}
- /*
+/*
** MYHOSTNAME -- return the name of this host.
**
** Parameters:
@@ -2598,9 +3025,9 @@ myhostname(hostbuf, size)
register struct hostent *hp;
if (gethostname(hostbuf, size) < 0 || hostbuf[0] == '\0')
- (void) strlcpy(hostbuf, "localhost", size);
+ (void) sm_strlcpy(hostbuf, "localhost", size);
hp = sm_gethostbyname(hostbuf, InetMode);
-# if NETINET && NETINET6
+#if NETINET && NETINET6
if (hp == NULL && InetMode == AF_INET6)
{
/*
@@ -2611,14 +3038,13 @@ myhostname(hostbuf, size)
hp = sm_gethostbyname(hostbuf, AF_INET);
}
-# endif /* NETINET && NETINET6 */
-
+#endif /* NETINET && NETINET6 */
if (hp == NULL)
return NULL;
if (strchr(hp->h_name, '.') != NULL || strchr(hostbuf, '.') == NULL)
(void) cleanstrcpy(hostbuf, hp->h_name, size);
-# if NETINFO
+#if NETINFO
if (strchr(hostbuf, '.') == NULL)
{
char *domainname;
@@ -2627,12 +3053,9 @@ myhostname(hostbuf, size)
"domain", '\0');
if (domainname != NULL &&
strlen(domainname) + strlen(hostbuf) + 1 < size)
- {
- (void) strlcat(hostbuf, ".", size);
- (void) strlcat(hostbuf, domainname, size);
- }
+ (void) sm_strlcat2(hostbuf, ".", domainname, size);
}
-# endif /* NETINFO */
+#endif /* NETINFO */
/*
** If there is still no dot in the name, try looking for a
@@ -2664,7 +3087,7 @@ myhostname(hostbuf, size)
*/
if (strchr(hostbuf, '.') == NULL &&
- !getcanonname(hostbuf, size, TRUE))
+ !getcanonname(hostbuf, size, true, NULL))
{
sm_syslog(LOG_CRIT, NOQID,
"My unqualified host name (%s) unknown; sleeping for retry",
@@ -2672,7 +3095,7 @@ myhostname(hostbuf, size)
message("My unqualified host name (%s) unknown; sleeping for retry",
hostbuf);
(void) sleep(60);
- if (!getcanonname(hostbuf, size, TRUE))
+ if (!getcanonname(hostbuf, size, true, NULL))
{
sm_syslog(LOG_ALERT, NOQID,
"unable to qualify my own domain name (%s) -- using short name",
@@ -2683,7 +3106,7 @@ myhostname(hostbuf, size)
}
return hp;
}
- /*
+/*
** ADDRCMP -- compare two host addresses
**
** Parameters:
@@ -2702,22 +3125,22 @@ addrcmp(hp, ha, sa)
char *ha;
SOCKADDR *sa;
{
-# if NETINET6
- u_char *a;
-# endif /* NETINET6 */
+#if NETINET6
+ unsigned char *a;
+#endif /* NETINET6 */
switch (sa->sa.sa_family)
{
-# if NETINET
+#if NETINET
case AF_INET:
if (hp->h_addrtype == AF_INET)
return memcmp(ha, (char *) &sa->sin.sin_addr, INADDRSZ);
break;
-# endif /* NETINET */
+#endif /* NETINET */
-# if NETINET6
+#if NETINET6
case AF_INET6:
- a = (u_char *) &sa->sin6.sin6_addr;
+ a = (unsigned char *) &sa->sin6.sin6_addr;
/* Straight binary comparison */
if (hp->h_addrtype == AF_INET6)
@@ -2728,20 +3151,20 @@ addrcmp(hp, ha, sa)
IN6_IS_ADDR_V4MAPPED(&sa->sin6.sin6_addr))
return memcmp(a + IN6ADDRSZ - INADDRSZ, ha, INADDRSZ);
break;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
}
return -1;
}
- /*
+/*
** GETAUTHINFO -- get the real host name associated with a file descriptor
**
** Uses RFC1413 protocol to try to get info from the other end.
**
** Parameters:
** fd -- the descriptor
-** may_be_forged -- an outage that is set to TRUE if the
+** may_be_forged -- an outage that is set to true if the
** forward lookup of RealHostName does not match
-** RealHostAddr; set to FALSE if they do match.
+** RealHostAddr; set to false if they do match.
**
** Returns:
** The user@host information associated with this descriptor.
@@ -2767,15 +3190,24 @@ getauthinfo(fd, may_be_forged)
int fd;
bool *may_be_forged;
{
- volatile u_short port = 0;
+ unsigned short SM_NONVOLATILE port = 0;
SOCKADDR_LEN_T falen;
register char *volatile p = NULL;
SOCKADDR la;
SOCKADDR_LEN_T lalen;
+#ifndef NO_GETSERVBYNAME
register struct servent *sp;
+# if NETINET
+ static unsigned short port4 = 0;
+# endif /* NETINET */
+# if NETINET6
+ static unsigned short port6 = 0;
+# endif /* NETINET6 */
+#endif /* ! NO_GETSERVBYNAME */
volatile int s;
int i = 0;
- EVENT *ev;
+ size_t len;
+ SM_EVENT *ev;
int nleft;
struct hostent *hp;
char *ostype = NULL;
@@ -2783,7 +3215,7 @@ getauthinfo(fd, may_be_forged)
char ibuf[MAXNAME + 1];
static char hbuf[MAXNAME * 2 + 11];
- *may_be_forged = FALSE;
+ *may_be_forged = false;
falen = sizeof RealHostAddr;
if (isatty(fd) || (i = getpeername(fd, &RealHostAddr.sa, &falen)) < 0 ||
falen <= 0 || RealHostAddr.sa.sa_family == 0)
@@ -2795,14 +3227,15 @@ getauthinfo(fd, may_be_forged)
** errno in this case, so a mis-report doesn't
** happen later.
*/
+
if (errno != ENOTSOCK)
return NULL;
errno = 0;
}
- (void) snprintf(hbuf, sizeof hbuf, "%s@localhost",
- RealUserName);
+ (void) sm_strlcpyn(hbuf, sizeof hbuf, 2, RealUserName,
+ "@localhost");
if (tTd(9, 1))
- dprintf("getauthinfo: %s\n", hbuf);
+ sm_dprintf("getauthinfo: %s\n", hbuf);
return hbuf;
}
@@ -2811,25 +3244,17 @@ getauthinfo(fd, may_be_forged)
/* translate that to a host name */
RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr));
if (strlen(RealHostName) > MAXNAME)
- RealHostName[MAXNAME] = '\0';
+ RealHostName[MAXNAME] = '\0'; /* XXX - 1 ? */
}
/* cross check RealHostName with forward DNS lookup */
- if (anynet_ntoa(&RealHostAddr)[0] == '[' ||
- RealHostName[0] == '[')
- {
- /*
- ** address is not a socket or have an
- ** IP address with no forward lookup
- */
- *may_be_forged = FALSE;
- }
- else
+ if (anynet_ntoa(&RealHostAddr)[0] != '[' &&
+ RealHostName[0] != '[')
{
int family;
family = RealHostAddr.sa.sa_family;
-# if NETINET6 && NEEDSGETIPNODE
+#if NETINET6 && NEEDSGETIPNODE
/*
** If RealHostAddr is an IPv6 connection with an
** IPv4-mapped address, we need RealHostName's IPv4
@@ -2846,22 +3271,24 @@ getauthinfo(fd, may_be_forged)
if (family == AF_INET6 &&
IN6_IS_ADDR_V4MAPPED(&RealHostAddr.sin6.sin6_addr))
family = AF_INET;
-# endif /* NETINET6 && NEEDSGETIPNODE */
+#endif /* NETINET6 && NEEDSGETIPNODE */
/* try to match the reverse against the forward lookup */
hp = sm_gethostbyname(RealHostName, family);
if (hp == NULL)
- *may_be_forged = TRUE;
+ *may_be_forged = true;
else
{
for (ha = hp->h_addr_list; *ha != NULL; ha++)
+ {
if (addrcmp(hp, *ha, &RealHostAddr) == 0)
break;
+ }
*may_be_forged = *ha == NULL;
-# if _FFR_FREEHOSTENT && NETINET6
+#if NETINET6
freehostent(hp);
hp = NULL;
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+#endif /* NETINET6 */
}
}
@@ -2871,7 +3298,7 @@ getauthinfo(fd, may_be_forged)
lalen = sizeof la;
switch (RealHostAddr.sa.sa_family)
{
-# if NETINET
+#if NETINET
case AF_INET:
if (getsockname(fd, &la.sa, &lalen) < 0 ||
lalen <= 0 ||
@@ -2883,7 +3310,7 @@ getauthinfo(fd, may_be_forged)
port = RealHostAddr.sin.sin_port;
/* create ident query */
- (void) snprintf(ibuf, sizeof ibuf, "%d,%d\r\n",
+ (void) sm_snprintf(ibuf, sizeof ibuf, "%d,%d\r\n",
ntohs(RealHostAddr.sin.sin_port),
ntohs(la.sin.sin_port));
@@ -2891,19 +3318,35 @@ getauthinfo(fd, may_be_forged)
la.sin.sin_port = 0;
/* create foreign address */
-# ifdef NO_GETSERVBYNAME
+# ifdef NO_GETSERVBYNAME
RealHostAddr.sin.sin_port = htons(113);
-# else /* NO_GETSERVBYNAME */
- sp = getservbyname("auth", "tcp");
- if (sp != NULL)
- RealHostAddr.sin.sin_port = sp->s_port;
- else
- RealHostAddr.sin.sin_port = htons(113);
+# else /* NO_GETSERVBYNAME */
+
+ /*
+ ** getservbyname() consumes about 5% of the time
+ ** when receiving a small message (almost all of the time
+ ** spent in this routine).
+ ** Hence we store the port in a static variable
+ ** to save this time.
+ ** The portnumber shouldn't change very often...
+ ** This code makes the assumption that the port number
+ ** is not 0.
+ */
+
+ if (port4 == 0)
+ {
+ sp = getservbyname("auth", "tcp");
+ if (sp != NULL)
+ port4 = sp->s_port;
+ else
+ port4 = htons(113);
+ }
+ RealHostAddr.sin.sin_port = port4;
break;
-# endif /* NO_GETSERVBYNAME */
-# endif /* NETINET */
+# endif /* NO_GETSERVBYNAME */
+#endif /* NETINET */
-# if NETINET6
+#if NETINET6
case AF_INET6:
if (getsockname(fd, &la.sa, &lalen) < 0 ||
lalen <= 0 ||
@@ -2915,7 +3358,7 @@ getauthinfo(fd, may_be_forged)
port = RealHostAddr.sin6.sin6_port;
/* create ident query */
- (void) snprintf(ibuf, sizeof ibuf, "%d,%d\r\n",
+ (void) sm_snprintf(ibuf, sizeof ibuf, "%d,%d\r\n",
ntohs(RealHostAddr.sin6.sin6_port),
ntohs(la.sin6.sin6_port));
@@ -2923,17 +3366,21 @@ getauthinfo(fd, may_be_forged)
la.sin6.sin6_port = 0;
/* create foreign address */
-# ifdef NO_GETSERVBYNAME
+# ifdef NO_GETSERVBYNAME
RealHostAddr.sin6.sin6_port = htons(113);
-# else /* NO_GETSERVBYNAME */
- sp = getservbyname("auth", "tcp");
- if (sp != NULL)
- RealHostAddr.sin6.sin6_port = sp->s_port;
- else
- RealHostAddr.sin6.sin6_port = htons(113);
+# else /* NO_GETSERVBYNAME */
+ if (port6 == 0)
+ {
+ sp = getservbyname("auth", "tcp");
+ if (sp != NULL)
+ port6 = sp->s_port;
+ else
+ port6 = htons(113);
+ }
+ RealHostAddr.sin6.sin6_port = port6;
break;
-# endif /* NO_GETSERVBYNAME */
-# endif /* NETINET6 */
+# endif /* NO_GETSERVBYNAME */
+#endif /* NETINET6 */
default:
/* no ident info */
goto noident;
@@ -2948,24 +3395,22 @@ getauthinfo(fd, may_be_forged)
}
/* put a timeout around the whole thing */
- ev = setevent(TimeOuts.to_ident, authtimeout, 0);
+ ev = sm_setevent(TimeOuts.to_ident, authtimeout, 0);
/* connect to foreign IDENT server using same address as SMTP socket */
s = socket(la.sa.sa_family, SOCK_STREAM, 0);
if (s < 0)
{
- clrevent(ev);
+ sm_clrevent(ev);
goto noident;
}
if (bind(s, &la.sa, lalen) < 0 ||
connect(s, &RealHostAddr.sa, lalen) < 0)
- {
goto closeident;
- }
if (tTd(9, 10))
- dprintf("getauthinfo: sent %s", ibuf);
+ sm_dprintf("getauthinfo: sent %s", ibuf);
/* send query */
if (write(s, ibuf, strlen(ibuf)) < 0)
@@ -2979,11 +3424,11 @@ getauthinfo(fd, may_be_forged)
p += i;
nleft -= i;
*p = '\0';
- if (strchr(ibuf, '\n') != NULL)
+ if (strchr(ibuf, '\n') != NULL || nleft <= 0)
break;
}
(void) close(s);
- clrevent(ev);
+ sm_clrevent(ev);
if (i < 0 || p == &ibuf[0])
goto noident;
@@ -2992,7 +3437,7 @@ getauthinfo(fd, may_be_forged)
*++p = '\0';
if (tTd(9, 3))
- dprintf("getauthinfo: got %s\n", ibuf);
+ sm_dprintf("getauthinfo: got %s\n", ibuf);
/* parse result */
p = strchr(ibuf, ':');
@@ -3003,7 +3448,7 @@ getauthinfo(fd, may_be_forged)
}
while (isascii(*++p) && isspace(*p))
continue;
- if (strncasecmp(p, "userid", 6) != 0)
+ if (sm_strncasecmp(p, "userid", 6) != 0)
{
/* presumably an error string */
goto noident;
@@ -3042,55 +3487,55 @@ getauthinfo(fd, may_be_forged)
continue;
/* p now points to the authenticated name -- copy carefully */
- if (strncasecmp(ostype, "other", 5) == 0 &&
+ if (sm_strncasecmp(ostype, "other", 5) == 0 &&
(ostype[5] == ' ' || ostype[5] == '\0'))
{
- snprintf(hbuf, sizeof hbuf, "IDENT:");
+ (void) sm_strlcpy(hbuf, "IDENT:", sizeof hbuf);
cleanstrcpy(&hbuf[6], p, MAXNAME);
}
else
cleanstrcpy(hbuf, p, MAXNAME);
- i = strlen(hbuf);
- snprintf(&hbuf[i], sizeof hbuf - i, "@%s",
- RealHostName == NULL ? "localhost" : RealHostName);
+ len = strlen(hbuf);
+ (void) sm_strlcpyn(&hbuf[len], sizeof hbuf - len, 2, "@",
+ RealHostName == NULL ? "localhost" : RealHostName);
goto postident;
closeident:
(void) close(s);
- clrevent(ev);
+ sm_clrevent(ev);
noident:
/* put back the original incoming port */
switch (RealHostAddr.sa.sa_family)
{
-# if NETINET
+#if NETINET
case AF_INET:
if (port > 0)
RealHostAddr.sin.sin_port = port;
break;
-# endif /* NETINET */
+#endif /* NETINET */
-# if NETINET6
+#if NETINET6
case AF_INET6:
if (port > 0)
RealHostAddr.sin6.sin6_port = port;
break;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
}
if (RealHostName == NULL)
{
if (tTd(9, 1))
- dprintf("getauthinfo: NULL\n");
+ sm_dprintf("getauthinfo: NULL\n");
return NULL;
}
- snprintf(hbuf, sizeof hbuf, "%s", RealHostName);
+ (void) sm_strlcpy(hbuf, RealHostName, sizeof hbuf);
postident:
-# if IP_SRCROUTE
-# ifndef GET_IPOPT_DST
-# define GET_IPOPT_DST(dst) (dst)
-# endif /* ! GET_IPOPT_DST */
+#if IP_SRCROUTE
+# ifndef GET_IPOPT_DST
+# define GET_IPOPT_DST(dst) (dst)
+# endif /* ! GET_IPOPT_DST */
/*
** Extract IP source routing information.
**
@@ -3108,8 +3553,8 @@ postident:
{
SOCKOPT_LEN_T ipoptlen;
int j;
- u_char *q;
- u_char *o;
+ unsigned char *q;
+ unsigned char *o;
int l;
struct IPOPTION ipopt;
@@ -3119,8 +3564,8 @@ postident:
goto noipsr;
if (ipoptlen == 0)
goto noipsr;
- o = (u_char *) ipopt.IP_LIST;
- while (o != NULL && o < (u_char *) &ipopt + ipoptlen)
+ o = (unsigned char *) ipopt.IP_LIST;
+ while (o != NULL && o < (unsigned char *) &ipopt + ipoptlen)
{
switch (*o)
{
@@ -3147,10 +3592,11 @@ postident:
p = &hbuf[strlen(hbuf)];
l = sizeof hbuf - (hbuf - p) - 6;
- snprintf(p, SPACELEFT(hbuf, p), " [%s@%.*s",
- *o == IPOPT_SSRR ? "!" : "",
- l > 240 ? 120 : l / 2,
- inet_ntoa(GET_IPOPT_DST(ipopt.IP_DST)));
+ (void) sm_snprintf(p, SPACELEFT(hbuf, p),
+ " [%s@%.*s",
+ *o == IPOPT_SSRR ? "!" : "",
+ l > 240 ? 120 : l / 2,
+ inet_ntoa(GET_IPOPT_DST(ipopt.IP_DST)));
i = strlen(p);
p += i;
l -= strlen(p);
@@ -3164,12 +3610,13 @@ postident:
struct in_addr addr;
memcpy(&addr, q, sizeof(addr));
- snprintf(p, SPACELEFT(hbuf, p),
- "%c%.*s",
- j != 0 ? '@' : ':',
- l > 240 ? 120 :
- j == 0 ? l : l / 2,
- inet_ntoa(addr));
+ (void) sm_snprintf(p,
+ SPACELEFT(hbuf, p),
+ "%c%.*s",
+ j != 0 ? '@' : ':',
+ l > 240 ? 120 :
+ j == 0 ? l : l / 2,
+ inet_ntoa(addr));
i = strlen(p);
p += i;
l -= i + 1;
@@ -3184,51 +3631,53 @@ postident:
break;
}
}
- snprintf(p, SPACELEFT(hbuf, p), "]");
+ (void) sm_snprintf(p, SPACELEFT(hbuf, p), "]");
goto postipsr;
}
noipsr:
-# endif /* IP_SRCROUTE */
+#endif /* IP_SRCROUTE */
if (RealHostName != NULL && RealHostName[0] != '[')
{
p = &hbuf[strlen(hbuf)];
- (void) snprintf(p, SPACELEFT(hbuf, p), " [%.100s]",
- anynet_ntoa(&RealHostAddr));
+ (void) sm_snprintf(p, SPACELEFT(hbuf, p), " [%.100s]",
+ anynet_ntoa(&RealHostAddr));
}
if (*may_be_forged)
{
p = &hbuf[strlen(hbuf)];
- (void) snprintf(p, SPACELEFT(hbuf, p), " (may be forged)");
+ (void) sm_strlcpy(p, " (may be forged)", SPACELEFT(hbuf, p));
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{client_resolve}"), "FORGED");
}
-# if IP_SRCROUTE
+#if IP_SRCROUTE
postipsr:
-# endif /* IP_SRCROUTE */
- if (tTd(9, 1))
- dprintf("getauthinfo: %s\n", hbuf);
+#endif /* IP_SRCROUTE */
/* put back the original incoming port */
switch (RealHostAddr.sa.sa_family)
{
-# if NETINET
+#if NETINET
case AF_INET:
if (port > 0)
RealHostAddr.sin.sin_port = port;
break;
-# endif /* NETINET */
+#endif /* NETINET */
-# if NETINET6
+#if NETINET6
case AF_INET6:
if (port > 0)
RealHostAddr.sin6.sin6_port = port;
break;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
}
+ if (tTd(9, 1))
+ sm_dprintf("getauthinfo: %s\n", hbuf);
return hbuf;
}
- /*
+/*
** HOST_MAP_LOOKUP -- turn a hostname into canonical form
**
** Parameters:
@@ -3258,34 +3707,39 @@ host_map_lookup(map, name, av, statp)
int *statp;
{
register struct hostent *hp;
-# if NETINET
+#if NETINET
struct in_addr in_addr;
-# endif /* NETINET */
-# if NETINET6
+#endif /* NETINET */
+#if NETINET6
struct in6_addr in6_addr;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
char *cp, *ans = NULL;
register STAB *s;
+ time_t now;
+#if NAMED_BIND
+ time_t SM_NONVOLATILE retrans = 0;
+ int SM_NONVOLATILE retry = 0;
+#endif /* NAMED_BIND */
char hbuf[MAXNAME + 1];
/*
** See if we have already looked up this name. If so, just
- ** return it.
+ ** return it (unless expired).
*/
+ now = curtime();
s = stab(name, ST_NAMECANON, ST_ENTER);
- if (bitset(NCF_VALID, s->s_namecanon.nc_flags))
+ if (bitset(NCF_VALID, s->s_namecanon.nc_flags) &&
+ s->s_namecanon.nc_exp >= now)
{
if (tTd(9, 1))
- dprintf("host_map_lookup(%s) => CACHE %s\n",
- name,
- s->s_namecanon.nc_cname == NULL
+ sm_dprintf("host_map_lookup(%s) => CACHE %s\n",
+ name,
+ s->s_namecanon.nc_cname == NULL
? "NULL"
: s->s_namecanon.nc_cname);
errno = s->s_namecanon.nc_errno;
-# if NAMED_BIND
SM_SET_H_ERRNO(s->s_namecanon.nc_herrno);
-# endif /* NAMED_BIND */
*statp = s->s_namecanon.nc_stat;
if (*statp == EX_TEMPFAIL)
{
@@ -3323,7 +3777,7 @@ host_map_lookup(map, name, av, statp)
bitset(MF_DEFER, map->map_mflags))
{
if (tTd(9, 1))
- dprintf("host_map_lookup(%s) => DEFERRED\n", name);
+ sm_dprintf("host_map_lookup(%s) => DEFERRED\n", name);
*statp = EX_TEMPFAIL;
return NULL;
}
@@ -3336,47 +3790,83 @@ host_map_lookup(map, name, av, statp)
*/
if (tTd(9, 1))
- dprintf("host_map_lookup(%s) => ", name);
+ sm_dprintf("host_map_lookup(%s) => ", name);
+#if NAMED_BIND
+ if (map->map_timeout > 0)
+ {
+ retrans = _res.retrans;
+ _res.retrans = map->map_timeout;
+ }
+ if (map->map_retry > 0)
+ {
+ retry = _res.retry;
+ _res.retry = map->map_retry;
+ }
+#endif /* NAMED_BIND */
+
+ /* set default TTL */
+ s->s_namecanon.nc_exp = now + SM_DEFAULT_TTL;
if (*name != '[')
{
- snprintf(hbuf, sizeof hbuf, "%s", name);
- if (getcanonname(hbuf, sizeof hbuf - 1, !HasWildcardMX))
+ int ttl;
+
+ (void) sm_strlcpy(hbuf, name, sizeof hbuf);
+ if (getcanonname(hbuf, sizeof hbuf - 1, !HasWildcardMX, &ttl))
+ {
ans = hbuf;
+ if (ttl > 0)
+ s->s_namecanon.nc_exp = now + SM_MIN(ttl,
+ SM_DEFAULT_TTL);
+ }
}
else
{
if ((cp = strchr(name, ']')) == NULL)
{
if (tTd(9, 1))
- dprintf("FAILED\n");
+ sm_dprintf("FAILED\n");
return NULL;
}
*cp = '\0';
hp = NULL;
-# if NETINET
+#if NETINET
if ((in_addr.s_addr = inet_addr(&name[1])) != INADDR_NONE)
hp = sm_gethostbyaddr((char *)&in_addr,
INADDRSZ, AF_INET);
-# endif /* NETINET */
-# if NETINET6
+#endif /* NETINET */
+#if NETINET6
if (hp == NULL &&
- inet_pton(AF_INET6, &name[1], &in6_addr) == 1)
+ anynet_pton(AF_INET6, &name[1], &in6_addr) == 1)
hp = sm_gethostbyaddr((char *)&in6_addr,
IN6ADDRSZ, AF_INET6);
-# endif /* NETINET6 */
+#endif /* NETINET6 */
*cp = ']';
if (hp != NULL)
{
/* found a match -- copy out */
- ans = denlstring((char *) hp->h_name, TRUE, TRUE);
-# if _FFR_FREEHOSTENT && NETINET6
+ ans = denlstring((char *) hp->h_name, true, true);
+#if NETINET6
+ if (ans == hp->h_name)
+ {
+ static char n[MAXNAME + 1];
+
+ /* hp->h_name is about to disappear */
+ (void) sm_strlcpy(n, ans, sizeof n);
+ ans = n;
+ }
freehostent(hp);
hp = NULL;
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+#endif /* NETINET6 */
}
}
+#if NAMED_BIND
+ if (map->map_timeout > 0)
+ _res.retrans = retrans;
+ if (map->map_retry > 0)
+ _res.retry = retry;
+#endif /* NAMED_BIND */
s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */
@@ -3384,23 +3874,25 @@ host_map_lookup(map, name, av, statp)
if (ans != NULL)
{
s->s_namecanon.nc_stat = *statp = EX_OK;
- s->s_namecanon.nc_cname = newstr(ans);
+ if (s->s_namecanon.nc_cname != NULL)
+ sm_free(s->s_namecanon.nc_cname);
+ s->s_namecanon.nc_cname = sm_strdup_x(ans);
if (bitset(MF_MATCHONLY, map->map_mflags))
cp = map_rewrite(map, name, strlen(name), NULL);
else
cp = map_rewrite(map, ans, strlen(ans), av);
if (tTd(9, 1))
- dprintf("FOUND %s\n", ans);
+ sm_dprintf("FOUND %s\n", ans);
return cp;
}
/* No match found */
s->s_namecanon.nc_errno = errno;
-# if NAMED_BIND
+#if NAMED_BIND
s->s_namecanon.nc_herrno = h_errno;
if (tTd(9, 1))
- dprintf("FAIL (%d)\n", h_errno);
+ sm_dprintf("FAIL (%d)\n", h_errno);
switch (h_errno)
{
case TRY_AGAIN:
@@ -3426,152 +3918,23 @@ host_map_lookup(map, name, av, statp)
*statp = EX_UNAVAILABLE;
break;
}
-# else /* NAMED_BIND */
+#else /* NAMED_BIND */
if (tTd(9, 1))
- dprintf("FAIL\n");
+ sm_dprintf("FAIL\n");
*statp = EX_NOHOST;
-# endif /* NAMED_BIND */
+#endif /* NAMED_BIND */
s->s_namecanon.nc_stat = *statp;
return NULL;
}
-#else /* DAEMON */
-/* code for systems without sophisticated networking */
-
/*
-** MYHOSTNAME -- stub version for case of no daemon code.
-**
-** Can't convert to upper case here because might be a UUCP name.
-**
-** Mark, you can change this to be anything you want......
-*/
-
-char **
-myhostname(hostbuf, size)
- char hostbuf[];
- int size;
-{
- register FILE *f;
-
- hostbuf[0] = '\0';
- f = fopen("/usr/include/whoami", "r");
- if (f != NULL)
- {
- (void) fgets(hostbuf, size, f);
- fixcrlf(hostbuf, TRUE);
- (void) fclose(f);
- }
- if (hostbuf[0] == '\0')
- (void) strlcpy(hostbuf, "localhost", size);
- return NULL;
-}
- /*
-** GETAUTHINFO -- get the real host name associated with a file descriptor
-**
-** Parameters:
-** fd -- the descriptor
-** may_be_forged -- an outage that is set to TRUE if the
-** forward lookup of RealHostName does not match
-** RealHostAddr; set to FALSE if they do match.
-**
-** Returns:
-** The host name associated with this descriptor, if it can
-** be determined.
-** NULL otherwise.
-**
-** Side Effects:
-** none
-*/
-
-char *
-getauthinfo(fd, may_be_forged)
- int fd;
- bool *may_be_forged;
-{
- *may_be_forged = FALSE;
- return NULL;
-}
- /*
-** HOST_MAP_LOOKUP -- turn a hostname into canonical form
+** HOST_MAP_INIT -- initialize host class structures
**
** Parameters:
-** map -- a pointer to the database map.
-** name -- a buffer containing a hostname.
-** avp -- a pointer to a (cf file defined) argument vector.
-** statp -- an exit status (out parameter).
+** map -- a pointer to this map.
+** args -- argument string.
**
** Returns:
-** mapped host name
-** FALSE otherwise.
-**
-** Side Effects:
-** Looks up the host specified in name. If it is not
-** the canonical name for that host, replace it with
-** the canonical name. If the name is unknown, or it
-** is already the canonical name, leave it unchanged.
-*/
-
-/*ARGSUSED*/
-char *
-host_map_lookup(map, name, avp, statp)
- MAP *map;
- char *name;
- char **avp;
- char *statp;
-{
- register struct hostent *hp = NULL;
- char *cp;
-
- hp = sm_gethostbyname(name, InetMode);
- if (hp == NULL && InetMode != AF_INET)
- hp = sm_gethostbyname(name, AF_INET);
- if (hp == NULL)
- {
-# if NAMED_BIND
- if (tTd(9, 1))
- dprintf("FAIL (%d)\n", h_errno);
- switch (h_errno)
- {
- case TRY_AGAIN:
- if (UseNameServer)
- {
- CurEnv->e_status = "4.4.3";
- message("851 %s: Name server timeout",
- shortenstring(name, 33));
- }
- *statp = EX_TEMPFAIL;
- break;
-
- case HOST_NOT_FOUND:
- case NO_DATA:
- *statp = EX_NOHOST;
- break;
-
- case NO_RECOVERY:
- *statp = EX_SOFTWARE;
- break;
-
- default:
- *statp = EX_UNAVAILABLE;
- break;
- }
-#else /* NAMED_BIND */
- *statp = EX_NOHOST;
-#endif /* NAMED_BIND */
- return NULL;
- }
- if (bitset(MF_MATCHONLY, map->map_mflags))
- cp = map_rewrite(map, name, strlen(name), NULL);
- else
- cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), avp);
-# if _FFR_FREEHOSTENT && NETINET6
- freehostent(hp);
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
- return cp;
-}
-
-#endif /* DAEMON */
- /*
-** HOST_MAP_INIT -- initialize host class structures
+** true.
*/
bool
@@ -3612,6 +3975,27 @@ host_map_init(map, args)
case 'D':
map->map_mflags |= MF_DEFER;
break;
+
+ case 'd':
+ {
+ char *h;
+
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ h = strchr(p, ' ');
+ if (h != NULL)
+ *h = '\0';
+ map->map_timeout = convtime(p, 's');
+ if (h != NULL)
+ *h = ' ';
+ }
+ break;
+
+ case 'r':
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ map->map_retry = atoi(p);
+ break;
}
while (*p != '\0' && !(isascii(*p) && isspace(*p)))
p++;
@@ -3622,9 +4006,9 @@ host_map_init(map, args)
map->map_app = newstr(map->map_app);
if (map->map_tapp != NULL)
map->map_tapp = newstr(map->map_tapp);
- return TRUE;
+ return true;
}
-
+
#if NETINET6
/*
** ANYNET_NTOP -- convert an IPv6 network address to printable form.
@@ -3637,6 +4021,7 @@ host_map_init(map, args)
** Returns:
** A printable version of that structure.
*/
+
char *
anynet_ntop(s6a, dst, dst_len)
struct in6_addr *s6a;
@@ -3650,11 +4035,56 @@ anynet_ntop(s6a, dst, dst_len)
&s6a->s6_addr[IN6ADDRSZ - INADDRSZ],
dst, dst_len);
else
+ {
+ char *d;
+ size_t sz;
+
+ /* Save pointer to beginning of string */
+ d = dst;
+
+ /* Add IPv6: protocol tag */
+ sz = sm_strlcpy(dst, "IPv6:", dst_len);
+ if (sz >= dst_len)
+ return NULL;
+ dst += sz;
+ dst_len -= sz;
ap = (char *) inet_ntop(AF_INET6, s6a, dst, dst_len);
+
+ /* Restore pointer to beginning of string */
+ if (ap != NULL)
+ ap = d;
+ }
return ap;
}
+
+/*
+** ANYNET_PTON -- convert printed form to network address.
+**
+** Wrapper for inet_pton() which handles IPv6: labels.
+**
+** Parameters:
+** family -- address family
+** src -- string
+** dst -- destination address structure
+**
+** Returns:
+** 1 if the address was valid
+** 0 if the address wasn't parseable
+** -1 if error
+*/
+
+int
+anynet_pton(family, src, dst)
+ int family;
+ const char *src;
+ void *dst;
+{
+ if (family == AF_INET6 && sm_strncasecmp(src, "IPv6:", 5) == 0)
+ src += 5;
+ return inet_pton(family, src, dst);
+}
#endif /* NETINET6 */
- /*
+/*
** ANYNET_NTOA -- convert a network address to printable form.
**
** Parameters:
@@ -3690,10 +4120,10 @@ anynet_ntoa(sap)
# if NETUNIX
case AF_UNIX:
if (sap->sunix.sun_path[0] != '\0')
- snprintf(buf, sizeof buf, "[UNIX: %.64s]",
- sap->sunix.sun_path);
+ (void) sm_snprintf(buf, sizeof buf, "[UNIX: %.64s]",
+ sap->sunix.sun_path);
else
- snprintf(buf, sizeof buf, "[UNIX: localhost]");
+ (void) sm_strlcpy(buf, "[UNIX: localhost]", sizeof buf);
return buf;
# endif /* NETUNIX */
@@ -3712,8 +4142,8 @@ anynet_ntoa(sap)
# if NETLINK
case AF_LINK:
- snprintf(buf, sizeof buf, "[LINK: %s]",
- link_ntoa((struct sockaddr_dl *) &sap->sa));
+ (void) sm_snprintf(buf, sizeof buf, "[LINK: %s]",
+ link_ntoa((struct sockaddr_dl *) &sap->sa));
return buf;
# endif /* NETLINK */
default:
@@ -3723,18 +4153,19 @@ anynet_ntoa(sap)
}
/* unknown family -- just dump bytes */
- (void) snprintf(buf, sizeof buf, "Family %d: ", sap->sa.sa_family);
+ (void) sm_snprintf(buf, sizeof buf, "Family %d: ", sap->sa.sa_family);
bp = &buf[strlen(buf)];
ap = sap->sa.sa_data;
for (l = sizeof sap->sa.sa_data; --l >= 0; )
{
- (void) snprintf(bp, SPACELEFT(buf, bp), "%02x:", *ap++ & 0377);
+ (void) sm_snprintf(bp, SPACELEFT(buf, bp), "%02x:",
+ *ap++ & 0377);
bp += 3;
}
*--bp = '\0';
return buf;
}
- /*
+/*
** HOSTNAMEBYANYADDR -- return name of host based on address
**
** Parameters:
@@ -3771,24 +4202,21 @@ hostnamebyanyaddr(sap)
# if NETINET
case AF_INET:
hp = sm_gethostbyaddr((char *) &sap->sin.sin_addr,
- INADDRSZ,
- AF_INET);
+ INADDRSZ, AF_INET);
break;
# endif /* NETINET */
# if NETINET6
case AF_INET6:
hp = sm_gethostbyaddr((char *) &sap->sin6.sin6_addr,
- IN6ADDRSZ,
- AF_INET6);
+ IN6ADDRSZ, AF_INET6);
break;
# endif /* NETINET6 */
# if NETISO
case AF_ISO:
hp = sm_gethostbyaddr((char *) &sap->siso.siso_addr,
- sizeof sap->siso.siso_addr,
- AF_ISO);
+ sizeof sap->siso.siso_addr, AF_ISO);
break;
# endif /* NETISO */
@@ -3799,9 +4227,8 @@ hostnamebyanyaddr(sap)
# endif /* NETUNIX */
default:
- hp = sm_gethostbyaddr(sap->sa.sa_data,
- sizeof sap->sa.sa_data,
- sap->sa.sa_family);
+ hp = sm_gethostbyaddr(sap->sa.sa_data, sizeof sap->sa.sa_data,
+ sap->sa.sa_family);
break;
}
@@ -3821,31 +4248,29 @@ hostnamebyanyaddr(sap)
{
char *name;
- name = denlstring((char *) hp->h_name, TRUE, TRUE);
-
-# if _FFR_FREEHOSTENT && NETINET6
+ name = denlstring((char *) hp->h_name, true, true);
+# if NETINET6
if (name == hp->h_name)
{
static char n[MAXNAME + 1];
/* Copy the string, hp->h_name is about to disappear */
- strlcpy(n, name, sizeof n);
+ (void) sm_strlcpy(n, name, sizeof n);
name = n;
}
-
freehostent(hp);
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+# endif /* NETINET6 */
return name;
}
# endif /* NETINET || NETINET6 */
-# if _FFR_FREEHOSTENT && NETINET6
+# if NETINET6
if (hp != NULL)
{
freehostent(hp);
hp = NULL;
}
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+# endif /* NETINET6 */
# if NETUNIX
if (sap->sa.sa_family == AF_UNIX && sap->sunix.sun_path[0] == '\0')
@@ -3854,7 +4279,8 @@ hostnamebyanyaddr(sap)
{
static char buf[203];
- (void) snprintf(buf, sizeof buf, "[%.200s]", anynet_ntoa(sap));
+ (void) sm_snprintf(buf, sizeof buf, "[%.200s]",
+ anynet_ntoa(sap));
return buf;
}
}
diff --git a/contrib/sendmail/src/deliver.c b/contrib/sendmail/src/deliver.c
index 70b774d..402eac4 100644
--- a/contrib/sendmail/src/deliver.c
+++ b/contrib/sendmail/src/deliver.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
@@ -11,34 +11,35 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: deliver.c,v 8.600.2.1.2.86 2001/07/20 21:52:55 gshapiro Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
+#include <sys/time.h>
+SM_RCSID("@(#)$Id: deliver.c,v 8.928 2002/01/10 03:23:29 gshapiro Exp $")
#if HASSETUSERCONTEXT
# include <login_cap.h>
#endif /* HASSETUSERCONTEXT */
-#if STARTTLS || (SASL && SFIO)
+#if STARTTLS || SASL
# include "sfsasl.h"
-#endif /* STARTTLS || (SASL && SFIO) */
+#endif /* STARTTLS || SASL */
+void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int, bool));
static int deliver __P((ENVELOPE *, ADDRESS *));
static void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int));
static void mailfiletimeout __P((void));
-static void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int, bool));
static int parse_hostsignature __P((char *, char **, MAILER *));
static void sendenvelope __P((ENVELOPE *, int));
-static char *hostsignature __P((MAILER *, char *));
+extern MCI *mci_new __P((SM_RPOOL_T *));
+static int coloncmp __P((const char *, const char *));
-#if SMTP
-# if STARTTLS
+#if STARTTLS
static int starttls __P((MAILER *, MCI *, ENVELOPE *));
-# endif /* STARTTLS */
-#endif /* SMTP */
+static int endtlsclt __P((MCI *));
+#endif /* STARTTLS */
+# if STARTTLS || SASL
+static bool iscltflgset __P((ENVELOPE *, int));
+# endif /* STARTTLS || SASL */
/*
** SENDALL -- actually send all the messages.
@@ -70,7 +71,7 @@ sendall(e, mode)
register ENVELOPE *ee;
ENVELOPE *splitenv = NULL;
int oldverbose = Verbose;
- bool somedeliveries = FALSE, expensive = FALSE;
+ bool somedeliveries = false, expensive = false;
pid_t pid;
/*
@@ -81,11 +82,13 @@ sendall(e, mode)
if (bitset(EF_DISCARD, e->e_flags))
{
if (tTd(13, 1))
- dprintf("sendall: discarding id %s\n", e->e_id);
+ sm_dprintf("sendall: discarding id %s\n", e->e_id);
e->e_flags |= EF_CLRQUEUE;
- if (LogLevel > 4)
+ if (LogLevel > 9)
+ logundelrcpts(e, "discarded", 9, true);
+ else if (LogLevel > 4)
sm_syslog(LOG_INFO, e->e_id, "discarded");
- markstats(e, NULL, TRUE);
+ markstats(e, NULL, STATS_REJECT);
return;
}
@@ -114,13 +117,13 @@ sendall(e, mode)
if (tTd(13, 1))
{
- dprintf("\n===== SENDALL: mode %c, id %s, e_from ",
+ sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ",
mode, e->e_id);
- printaddr(&e->e_from, FALSE);
- dprintf("\te_flags = ");
+ printaddr(&e->e_from, false);
+ sm_dprintf("\te_flags = ");
printenvflags(e);
- dprintf("sendqueue:\n");
- printaddr(e->e_sendqueue, TRUE);
+ sm_dprintf("sendqueue:\n");
+ printaddr(e->e_sendqueue, true);
}
/*
@@ -144,9 +147,7 @@ sendall(e, mode)
recip = "(nobody)";
errno = 0;
-#if QUEUE
- queueup(e, mode == SM_QUEUE || mode == SM_DEFER);
-#endif /* QUEUE */
+ queueup(e, WILL_BE_QUEUED(mode), false);
e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
ExitStat = EX_UNAVAILABLE;
syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s",
@@ -178,8 +179,8 @@ sendall(e, mode)
{
if (tTd(13, 5))
{
- dprintf("sendall: QS_SENDER ");
- printaddr(&e->e_from, FALSE);
+ sm_dprintf("sendall: QS_SENDER ");
+ printaddr(&e->e_from, false);
}
e->e_from.q_state = QS_SENDER;
(void) recipient(&e->e_from, &e->e_sendqueue, 0, e);
@@ -209,8 +210,8 @@ sendall(e, mode)
if (tTd(13, 25))
{
- dprintf("\nAfter first owner pass, sendq =\n");
- printaddr(e->e_sendqueue, TRUE);
+ sm_dprintf("\nAfter first owner pass, sendq =\n");
+ printaddr(e->e_sendqueue, true);
}
owner = "";
@@ -218,8 +219,8 @@ sendall(e, mode)
while (owner != NULL && otherowners > 0)
{
if (tTd(13, 28))
- dprintf("owner = \"%s\", otherowners = %d\n",
- owner, otherowners);
+ sm_dprintf("owner = \"%s\", otherowners = %d\n",
+ owner, otherowners);
owner = NULL;
otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0;
@@ -227,19 +228,19 @@ sendall(e, mode)
{
if (tTd(13, 30))
{
- dprintf("Checking ");
- printaddr(q, FALSE);
+ sm_dprintf("Checking ");
+ printaddr(q, false);
}
if (QS_IS_DEAD(q->q_state))
{
if (tTd(13, 30))
- dprintf(" ... QS_IS_DEAD\n");
+ sm_dprintf(" ... QS_IS_DEAD\n");
continue;
}
if (tTd(13, 29) && !tTd(13, 30))
{
- dprintf("Checking ");
- printaddr(q, FALSE);
+ sm_dprintf("Checking ");
+ printaddr(q, false);
}
if (q->q_owner != NULL)
@@ -247,8 +248,8 @@ sendall(e, mode)
if (owner == NULL)
{
if (tTd(13, 40))
- dprintf(" ... First owner = \"%s\"\n",
- q->q_owner);
+ sm_dprintf(" ... First owner = \"%s\"\n",
+ q->q_owner);
owner = q->q_owner;
}
else if (owner != q->q_owner)
@@ -256,8 +257,8 @@ sendall(e, mode)
if (strcmp(owner, q->q_owner) == 0)
{
if (tTd(13, 40))
- dprintf(" ... Same owner = \"%s\"\n",
- owner);
+ sm_dprintf(" ... Same owner = \"%s\"\n",
+ owner);
/* make future comparisons cheap */
q->q_owner = owner;
@@ -265,27 +266,27 @@ sendall(e, mode)
else
{
if (tTd(13, 40))
- dprintf(" ... Another owner \"%s\"\n",
- q->q_owner);
+ sm_dprintf(" ... Another owner \"%s\"\n",
+ q->q_owner);
otherowners++;
}
owner = q->q_owner;
}
else if (tTd(13, 40))
- dprintf(" ... Same owner = \"%s\"\n",
- owner);
+ sm_dprintf(" ... Same owner = \"%s\"\n",
+ owner);
}
else
{
if (tTd(13, 40))
- dprintf(" ... Null owner\n");
+ sm_dprintf(" ... Null owner\n");
otherowners++;
}
if (QS_IS_BADADDR(q->q_state))
{
if (tTd(13, 30))
- dprintf(" ... QS_IS_BADADDR\n");
+ sm_dprintf(" ... QS_IS_BADADDR\n");
continue;
}
@@ -302,28 +303,27 @@ sendall(e, mode)
if (FallBackMX != NULL &&
!wordinclass(FallBackMX, 'w') &&
mode != SM_VERIFY &&
- (strcmp(m->m_mailer, "[IPC]") == 0 ||
- strcmp(m->m_mailer, "[TCP]") == 0) &&
+ !bitnset(M_NOMX, m->m_flags) &&
+ strcmp(m->m_mailer, "[IPC]") == 0 &&
m->m_argv[0] != NULL &&
- (strcmp(m->m_argv[0], "TCP") == 0 ||
- strcmp(m->m_argv[0], "IPC") == 0))
+ strcmp(m->m_argv[0], "TCP") == 0)
{
int len;
char *p;
if (tTd(13, 30))
- dprintf(" ... FallBackMX\n");
+ sm_dprintf(" ... FallBackMX\n");
- len = strlen(FallBackMX) + 3;
- p = xalloc(len);
- snprintf(p, len, "[%s]", FallBackMX);
+ len = strlen(FallBackMX) + 1;
+ p = sm_rpool_malloc_x(e->e_rpool, len);
+ (void) sm_strlcpy(p, FallBackMX, len);
q->q_state = QS_OK;
q->q_host = p;
}
else
{
if (tTd(13, 30))
- dprintf(" ... QS_IS_QUEUEUP\n");
+ sm_dprintf(" ... QS_IS_QUEUEUP\n");
continue;
}
}
@@ -341,9 +341,9 @@ sendall(e, mode)
bitnset(M_EXPENSIVE, q->q_mailer->m_flags))
{
if (tTd(13, 30))
- dprintf(" ... expensive\n");
+ sm_dprintf(" ... expensive\n");
q->q_state = QS_QUEUEUP;
- expensive = TRUE;
+ expensive = true;
}
else if (bitnset(M_HOLD, q->q_mailer->m_flags) &&
QueueLimitId == NULL &&
@@ -351,15 +351,26 @@ sendall(e, mode)
QueueLimitRecipient == NULL)
{
if (tTd(13, 30))
- dprintf(" ... hold\n");
+ sm_dprintf(" ... hold\n");
+ q->q_state = QS_QUEUEUP;
+ expensive = true;
+ }
+#if _FFR_QUARANTINE
+ else if (QueueMode != QM_QUARANTINE &&
+ e->e_quarmsg != NULL)
+ {
+ if (tTd(13, 30))
+ sm_dprintf(" ... quarantine: %s\n",
+ e->e_quarmsg);
q->q_state = QS_QUEUEUP;
- expensive = TRUE;
+ expensive = true;
}
+#endif /* _FFR_QUARANTINE */
else
{
if (tTd(13, 30))
- dprintf(" ... deliverable\n");
- somedeliveries = TRUE;
+ sm_dprintf(" ... deliverable\n");
+ somedeliveries = true;
}
}
@@ -369,35 +380,45 @@ sendall(e, mode)
** Split this envelope into two.
*/
- ee = (ENVELOPE *) xalloc(sizeof *ee);
- *ee = *e;
+ ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool,
+ sizeof *ee);
+ STRUCTCOPY(*e, *ee);
ee->e_message = NULL;
ee->e_id = NULL;
assign_queueid(ee);
if (tTd(13, 1))
- dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n",
- e->e_id, ee->e_id, owner, otherowners);
-
- ee->e_header = copyheader(e->e_header);
- ee->e_sendqueue = copyqueue(e->e_sendqueue);
- ee->e_errorqueue = copyqueue(e->e_errorqueue);
+ sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n",
+ e->e_id, ee->e_id, owner,
+ otherowners);
+
+ ee->e_header = copyheader(e->e_header, ee->e_rpool);
+ ee->e_sendqueue = copyqueue(e->e_sendqueue,
+ ee->e_rpool);
+ ee->e_errorqueue = copyqueue(e->e_errorqueue,
+ ee->e_rpool);
ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM);
ee->e_flags |= EF_NORECEIPT;
- setsender(owner, ee, NULL, '\0', TRUE);
+ setsender(owner, ee, NULL, '\0', true);
if (tTd(13, 5))
{
- dprintf("sendall(split): QS_SENDER ");
- printaddr(&ee->e_from, FALSE);
+ sm_dprintf("sendall(split): QS_SENDER ");
+ printaddr(&ee->e_from, false);
}
ee->e_from.q_state = QS_SENDER;
ee->e_dfp = NULL;
ee->e_lockfp = NULL;
ee->e_xfp = NULL;
- ee->e_queuedir = e->e_queuedir;
+ ee->e_qgrp = e->e_qgrp;
+ ee->e_qdir = e->e_qdir;
ee->e_errormode = EM_MAIL;
ee->e_sibling = splitenv;
ee->e_statmsg = NULL;
+#if _FFR_QUARANTINE
+ if (e->e_quarmsg != NULL)
+ ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
+ e->e_quarmsg);
+#endif /* _FFR_QUARANTINE */
splitenv = ee;
for (q = e->e_sendqueue; q != NULL; q = q->q_next)
@@ -406,8 +427,8 @@ sendall(e, mode)
{
q->q_state = QS_CLONED;
if (tTd(13, 6))
- dprintf("\t... stripping %s from original envelope\n",
- q->q_paddr);
+ sm_dprintf("\t... stripping %s from original envelope\n",
+ q->q_paddr);
}
}
for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
@@ -416,8 +437,8 @@ sendall(e, mode)
{
q->q_state = QS_CLONED;
if (tTd(13, 6))
- dprintf("\t... dropping %s from cloned envelope\n",
- q->q_paddr);
+ sm_dprintf("\t... dropping %s from cloned envelope\n",
+ q->q_paddr);
}
else
{
@@ -425,13 +446,13 @@ sendall(e, mode)
q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
q->q_flags |= DefaultNotify & ~QPINGONSUCCESS;
if (tTd(13, 6))
- dprintf("\t... moving %s to cloned envelope\n",
- q->q_paddr);
+ sm_dprintf("\t... moving %s to cloned envelope\n",
+ q->q_paddr);
}
}
if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags))
- dup_queue_file(e, ee, 'd');
+ dup_queue_file(e, ee, DATAFL_LETTER);
/*
** Give the split envelope access to the parent
@@ -441,26 +462,26 @@ sendall(e, mode)
*/
if (e->e_xfp != NULL)
- ee->e_xfp = bfdup(e->e_xfp);
+ ee->e_xfp = sm_io_dup(e->e_xfp);
/* failed to dup e->e_xfp, start a new transcript */
if (ee->e_xfp == NULL)
openxscript(ee);
if (mode != SM_VERIFY && LogLevel > 4)
- sm_syslog(LOG_INFO, ee->e_id,
- "clone %s, owner=%s",
- e->e_id, owner);
+ sm_syslog(LOG_INFO, e->e_id,
+ "%s: clone: owner=%s",
+ ee->e_id, owner);
}
}
if (owner != NULL)
{
- setsender(owner, e, NULL, '\0', TRUE);
+ setsender(owner, e, NULL, '\0', true);
if (tTd(13, 5))
{
- dprintf("sendall(owner): QS_SENDER ");
- printaddr(&e->e_from, FALSE);
+ sm_dprintf("sendall(owner): QS_SENDER ");
+ printaddr(&e->e_from, false);
}
e->e_from.q_state = QS_SENDER;
e->e_errormode = EM_MAIL;
@@ -469,14 +490,15 @@ sendall(e, mode)
}
/* if nothing to be delivered, just queue up everything */
- if (!somedeliveries && mode != SM_QUEUE && mode != SM_DEFER &&
+ if (!somedeliveries && !WILL_BE_QUEUED(mode) &&
mode != SM_VERIFY)
{
- time_t now = curtime();
+ time_t now;
if (tTd(13, 29))
- dprintf("No deliveries: auto-queuing\n");
+ sm_dprintf("No deliveries: auto-queuing\n");
mode = SM_QUEUE;
+ now = curtime();
/* treat this as a delivery in terms of counting tries */
e->e_dtime = now;
@@ -490,11 +512,12 @@ sendall(e, mode)
}
}
-#if QUEUE
- if ((mode == SM_QUEUE || mode == SM_DEFER || mode == SM_FORK ||
- (mode != SM_VERIFY && SuperSafe)) &&
+ if ((WILL_BE_QUEUED(mode) || mode == SM_FORK ||
+ (mode != SM_VERIFY && SuperSafe == SAFE_REALLY)) &&
(!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL))
{
+ bool msync;
+
/*
** Be sure everything is instantiated in the queue.
** Split envelopes first in case the machine crashes.
@@ -502,11 +525,16 @@ sendall(e, mode)
** recipients.
*/
+#if !HASFLOCK
+ msync = false;
+#else /* !HASFLOCK */
+ msync = mode == SM_FORK;
+#endif /* !HASFLOCK */
+
for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- queueup(ee, mode == SM_QUEUE || mode == SM_DEFER);
- queueup(e, mode == SM_QUEUE || mode == SM_DEFER);
+ queueup(ee, WILL_BE_QUEUED(mode), msync);
+ queueup(e, WILL_BE_QUEUED(mode), msync);
}
-#endif /* QUEUE */
if (tTd(62, 10))
checkfds("after envelope splitting");
@@ -517,20 +545,20 @@ sendall(e, mode)
if (tTd(13, 20))
{
- dprintf("sendall: final mode = %c\n", mode);
+ sm_dprintf("sendall: final mode = %c\n", mode);
if (tTd(13, 21))
{
- dprintf("\n================ Final Send Queue(s) =====================\n");
- dprintf("\n *** Envelope %s, e_from=%s ***\n",
- e->e_id, e->e_from.q_paddr);
- printaddr(e->e_sendqueue, TRUE);
+ sm_dprintf("\n================ Final Send Queue(s) =====================\n");
+ sm_dprintf("\n *** Envelope %s, e_from=%s ***\n",
+ e->e_id, e->e_from.q_paddr);
+ printaddr(e->e_sendqueue, true);
for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
{
- dprintf("\n *** Envelope %s, e_from=%s ***\n",
- ee->e_id, ee->e_from.q_paddr);
- printaddr(ee->e_sendqueue, TRUE);
+ sm_dprintf("\n *** Envelope %s, e_from=%s ***\n",
+ ee->e_id, ee->e_from.q_paddr);
+ printaddr(ee->e_sendqueue, true);
}
- dprintf("==========================================================\n\n");
+ sm_dprintf("==========================================================\n\n");
}
}
switch (mode)
@@ -546,18 +574,18 @@ sendall(e, mode)
#endif /* HASFLOCK */
if (e->e_nrcpts > 0)
e->e_flags |= EF_INQUEUE;
- dropenvelope(e, splitenv != NULL);
+ dropenvelope(e, splitenv != NULL, true);
for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
{
if (ee->e_nrcpts > 0)
ee->e_flags |= EF_INQUEUE;
- dropenvelope(ee, FALSE);
+ dropenvelope(ee, false, true);
}
return;
case SM_FORK:
if (e->e_xfp != NULL)
- (void) fflush(e->e_xfp);
+ (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
#if !HASFLOCK
/*
@@ -573,7 +601,7 @@ sendall(e, mode)
/* now drop the envelope in the parent */
e->e_flags |= EF_INQUEUE;
- dropenvelope(e, splitenv != NULL);
+ dropenvelope(e, splitenv != NULL, false);
/* arrange to reacquire lock after fork */
e->e_id = qid;
@@ -586,7 +614,7 @@ sendall(e, mode)
/* drop envelope in parent */
ee->e_flags |= EF_INQUEUE;
- dropenvelope(ee, FALSE);
+ dropenvelope(ee, false, false);
/* and save qid for reacquisition */
ee->e_id = qid;
@@ -602,7 +630,7 @@ sendall(e, mode)
** them if necessary.
*/
- closemaps();
+ closemaps(false);
pid = fork();
if (pid < 0)
@@ -624,13 +652,13 @@ sendall(e, mode)
/* close any random open files in the envelope */
closexscript(e);
if (e->e_dfp != NULL)
- (void) bfclose(e->e_dfp);
+ (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
e->e_dfp = NULL;
e->e_flags &= ~EF_HAS_DF;
/* can't call unlockqueue to avoid unlink of xfp */
if (e->e_lockfp != NULL)
- (void) fclose(e->e_lockfp);
+ (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
else
syserr("%s: sendall: null lockfp", e->e_id);
e->e_lockfp = NULL;
@@ -639,30 +667,44 @@ sendall(e, mode)
/* make sure the parent doesn't own the envelope */
e->e_id = NULL;
+#if USE_DOUBLE_FORK
/* catch intermediate zombie */
(void) waitfor(pid);
+#endif /* USE_DOUBLE_FORK */
return;
}
/* Reset global flags */
RestartRequest = NULL;
+ RestartWorkGroup = false;
ShutdownRequest = NULL;
PendingSignal = 0;
/*
+ ** Initialize exception stack and default exception
+ ** handler for child process.
+ */
+
+ sm_exc_newthread(fatal_error);
+
+ /*
** Since we have accepted responsbility for the message,
** change the SIGTERM handler. intsig() (the old handler)
** would remove the envelope if this was a command line
** message submission.
*/
- (void) setsignal(SIGTERM, SIG_DFL);
+ (void) sm_signal(SIGTERM, SIG_DFL);
+#if USE_DOUBLE_FORK
/* double fork to avoid zombies */
pid = fork();
if (pid > 0)
exit(EX_OK);
save_errno = errno;
+#endif /* USE_DOUBLE_FORK */
+
+ CurrentPid = getpid();
/* be sure we are immune from the terminal */
disconnect(2, e);
@@ -678,11 +720,11 @@ sendall(e, mode)
#else /* HASFLOCK */
e->e_id = NULL;
#endif /* HASFLOCK */
- finis(TRUE, ExitStat);
+ finis(true, true, ExitStat);
}
/* be sure to give error messages in child */
- QuickAbort = FALSE;
+ QuickAbort = false;
/*
** Close any cached connections.
@@ -694,7 +736,7 @@ sendall(e, mode)
** message.
*/
- mci_flush(FALSE, NULL);
+ mci_flush(false, NULL);
#if HASFLOCK
break;
@@ -708,31 +750,31 @@ sendall(e, mode)
{
ENVELOPE *sibling = ee->e_sibling;
- (void) dowork(ee->e_queuedir, ee->e_id,
- FALSE, FALSE, ee);
+ (void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id,
+ false, false, ee);
ee->e_sibling = sibling;
}
- (void) dowork(e->e_queuedir, e->e_id,
- FALSE, FALSE, e);
- finis(TRUE, ExitStat);
+ (void) dowork(e->e_qgrp, e->e_qdir, e->e_id,
+ false, false, e);
+ finis(true, true, ExitStat);
#endif /* HASFLOCK */
}
sendenvelope(e, mode);
- dropenvelope(e, TRUE);
+ dropenvelope(e, true, true);
for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
{
CurEnv = ee;
if (mode != SM_VERIFY)
openxscript(ee);
sendenvelope(ee, mode);
- dropenvelope(ee, TRUE);
+ dropenvelope(ee, true, true);
}
CurEnv = e;
Verbose = oldverbose;
if (mode == SM_FORK)
- finis(TRUE, ExitStat);
+ finis(true, true, ExitStat);
}
static void
@@ -744,9 +786,9 @@ sendenvelope(e, mode)
bool didany;
if (tTd(13, 10))
- dprintf("sendenvelope(%s) e_flags=0x%lx\n",
- e->e_id == NULL ? "[NOQUEUE]" : e->e_id,
- e->e_flags);
+ sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n",
+ e->e_id == NULL ? "[NOQUEUE]" : e->e_id,
+ e->e_flags);
if (LogLevel > 80)
sm_syslog(LOG_DEBUG, e->e_id,
"sendenvelope, flags=0x%lx",
@@ -766,9 +808,15 @@ sendenvelope(e, mode)
return;
}
- /* Don't attempt deliveries if we want to bounce now */
+ /*
+ ** Don't attempt deliveries if we want to bounce now
+ ** or if deliver-by time is exceeded.
+ */
+
if (!bitset(EF_RESPONSE, e->e_flags) &&
- TimeOuts.to_q_return[e->e_timeoutclass] == NOW)
+ (TimeOuts.to_q_return[e->e_timeoutclass] == NOW ||
+ (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 &&
+ curtime() > e->e_ctime + e->e_deliver_by)))
return;
/*
@@ -781,9 +829,52 @@ sendenvelope(e, mode)
e->e_nsent = 0;
e->e_flags |= EF_GLOBALERRS;
- define(macid("{envid}", NULL), e->e_envid, e);
- define(macid("{bodytype}", NULL), e->e_bodytype, e);
- didany = FALSE;
+ macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid);
+ macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype);
+ didany = false;
+
+ if (!bitset(EF_SPLIT, e->e_flags))
+ {
+ ENVELOPE *oldsib;
+ ENVELOPE *ee;
+
+ /*
+ ** Save old sibling and set it to NULL to avoid
+ ** queueing up the same envelopes again.
+ ** This requires that envelopes in that list have
+ ** been take care of before (or at some other place).
+ */
+
+ oldsib = e->e_sibling;
+ e->e_sibling = NULL;
+ if (!split_by_recipient(e) &&
+ bitset(EF_FATALERRS, e->e_flags))
+ {
+ if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
+ e->e_flags |= EF_CLRQUEUE;
+ return;
+ }
+ for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
+ queueup(ee, false, true);
+
+ /* clean up */
+ for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
+ {
+ /* now unlock the job */
+ closexscript(ee);
+ unlockqueue(ee);
+
+ /* this envelope is marked unused */
+ if (ee->e_dfp != NULL)
+ {
+ (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
+ ee->e_dfp = NULL;
+ }
+ ee->e_id = NULL;
+ ee->e_flags &= ~EF_HAS_DF;
+ }
+ e->e_sibling = oldsib;
+ }
/* now run through the queue */
for (q = e->e_sendqueue; q != NULL; q = q->q_next)
@@ -791,8 +882,8 @@ sendenvelope(e, mode)
#if XDEBUG
char wbuf[MAXNAME + 20];
- (void) snprintf(wbuf, sizeof wbuf, "sendall(%.*s)",
- MAXNAME, q->q_paddr);
+ (void) sm_snprintf(wbuf, sizeof wbuf, "sendall(%.*s)",
+ MAXNAME, q->q_paddr);
checkfd012(wbuf);
#endif /* XDEBUG */
if (mode == SM_VERIFY)
@@ -813,7 +904,6 @@ sendenvelope(e, mode)
}
else if (QS_IS_OK(q->q_state))
{
-#if QUEUE
/*
** Checkpoint the send list every few addresses
*/
@@ -821,12 +911,11 @@ sendenvelope(e, mode)
if (CheckpointInterval > 0 &&
e->e_nsent >= CheckpointInterval)
{
- queueup(e, FALSE);
+ queueup(e, false, false);
e->e_nsent = 0;
}
-#endif /* QUEUE */
(void) deliver(e, q);
- didany = TRUE;
+ didany = true;
}
}
if (didany)
@@ -839,13 +928,66 @@ sendenvelope(e, mode)
checkfd012("end of sendenvelope");
#endif /* XDEBUG */
}
- /*
+
+#if REQUIRES_DIR_FSYNC
+/*
+** SYNC_DIR -- fsync a directory based on a filename
+**
+** Parameters:
+** filename -- path of file
+** panic -- panic?
+**
+** Returns:
+** none
+*/
+
+void
+sync_dir(filename, panic)
+ char *filename;
+ bool panic;
+{
+ int dirfd;
+ char *dirp;
+ char dir[MAXPATHLEN];
+
+ /* filesystems which require the directory be synced */
+ dirp = strrchr(filename, '/');
+ if (dirp != NULL)
+ {
+ if (sm_strlcpy(dir, filename, sizeof dir) >= sizeof dir)
+ return;
+ dir[dirp - filename] = '\0';
+ dirp = dir;
+ }
+ else
+ dirp = ".";
+ dirfd = open(dirp, O_RDONLY, 0700);
+ if (tTd(40,32))
+ sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)",
+ dirp, dirfd);
+ if (dirfd >= 0)
+ {
+ if (fsync(dirfd) < 0)
+ {
+ if (panic)
+ syserr("!sync_dir: cannot fsync directory %s",
+ dirp);
+ else if (LogLevel > 1)
+ sm_syslog(LOG_ERR, NOQID,
+ "sync_dir: cannot fsync directory %s: %s",
+ dirp, sm_errstring(errno));
+ }
+ (void) close(dirfd);
+ }
+}
+#endif /* REQUIRES_DIR_FSYNC */
+/*
** DUP_QUEUE_FILE -- duplicate a queue file into a split queue
**
** Parameters:
** e -- the existing envelope
** ee -- the new envelope
-** type -- the queue file type (e.g., 'd')
+** type -- the queue file type (e.g., DATAFL_LETTER)
**
** Returns:
** none
@@ -853,7 +995,7 @@ sendenvelope(e, mode)
static void
dup_queue_file(e, ee, type)
- struct envelope *e, *ee;
+ ENVELOPE *e, *ee;
int type;
{
char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN];
@@ -865,8 +1007,8 @@ dup_queue_file(e, ee, type)
** Make sure both are in the same directory.
*/
- snprintf(f1buf, sizeof f1buf, "%s", queuename(e, type));
- snprintf(f2buf, sizeof f2buf, "%s", queuename(ee, type));
+ (void) sm_strlcpy(f1buf, queuename(e, type), sizeof f1buf);
+ (void) sm_strlcpy(f2buf, queuename(ee, type), sizeof f2buf);
if (link(f1buf, f2buf) < 0)
{
int save_errno = errno;
@@ -877,19 +1019,20 @@ dup_queue_file(e, ee, type)
if (unlink(f2buf) < 0)
{
syserr("!sendall: unlink(%s): permanent",
- f2buf);
+ f2buf);
/* NOTREACHED */
}
if (link(f1buf, f2buf) < 0)
{
syserr("!sendall: link(%s, %s): permanent",
- f1buf, f2buf);
+ f1buf, f2buf);
/* NOTREACHED */
}
}
}
+ SYNC_DIR(f2buf, true);
}
- /*
+/*
** DOFORK -- do a fork, retrying a couple of times on failure.
**
** This MUST be a macro, since after a vfork we are running
@@ -930,7 +1073,7 @@ dup_queue_file(e, ee, type)
(void) sleep((unsigned) NFORKTRIES - i);\
}\
}
- /*
+/*
** DOFORK -- simple fork interface to DOFORK.
**
** Parameters:
@@ -953,7 +1096,73 @@ dofork()
DOFORK(fork);
return pid;
}
- /*
+
+/*
+** COLONCMP -- compare host-signatures up to first ':' or EOS
+**
+** This takes two strings which happen to be host-signatures and
+** compares them. If the lowest preference portions of the MX-RR's
+** match (up to ':' or EOS, whichever is first), then we have
+** match. This is used for coattail-piggybacking messages during
+** message delivery.
+** If the signatures are the same up to the first ':' the remainder of
+** the signatures are then compared with a normal strcmp(). This saves
+** re-examining the first part of the signatures.
+**
+** Parameters:
+** a - first host-signature
+** b - second host-signature
+**
+** Returns:
+** HS_MATCH_NO -- no "match".
+** HS_MATCH_FIRST -- "match" for the first MX preference
+** (up to the first colon (':')).
+** HS_MATCH_FULL -- match for the entire MX record.
+**
+** Side Effects:
+** none.
+*/
+
+#define HS_MATCH_NO 0
+#define HS_MATCH_FIRST 1
+#define HS_MATCH_FULL 2
+
+static int
+coloncmp(a, b)
+ register const char *a;
+ register const char *b;
+{
+ int ret = HS_MATCH_NO;
+ int braclev = 0;
+
+ while (*a == *b++)
+ {
+ /* Need to account for IPv6 bracketed addresses */
+ if (*a == '[')
+ braclev++;
+ else if (*a == '[' && braclev > 0)
+ braclev--;
+ else if (*a == ':' && braclev <= 0)
+ {
+ ret = HS_MATCH_FIRST;
+ a++;
+ break;
+ }
+ else if (*a == '\0')
+ return HS_MATCH_FULL; /* a full match */
+ a++;
+ }
+ if (ret == HS_MATCH_NO &&
+ braclev <= 0 &&
+ ((*a == '\0' && *(b - 1) == ':') ||
+ (*a == ':' && *(b - 1) == '\0')))
+ return HS_MATCH_FIRST;
+ if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0)
+ return HS_MATCH_FULL;
+
+ return ret;
+}
+/*
** DELIVER -- Deliver a message to a list of addresses.
**
** This routine delivers to everyone on the same host as the
@@ -962,6 +1171,45 @@ dofork()
** that it will deliver to all these addresses however -- so
** deliver should be called once for each address on the
** list.
+** Deliver tries to be as opportunistic as possible about piggybacking
+** messages. Some definitions to make understanding easier follow below.
+** Piggybacking occurs when an existing connection to a mail host can
+** be used to send the same message to more than one recipient at the
+** same time. So "no piggybacking" means one message for one recipient
+** per connection. "Intentional piggybacking" happens when the
+** recipients' host address (not the mail host address) is used to
+** attempt piggybacking. Recipients with the same host address
+** have the same mail host. "Coincidental piggybacking" relies on
+** piggybacking based on all the mail host addresses in the MX-RR. This
+** is "coincidental" in the fact it could not be predicted until the
+** MX Resource Records for the hosts were obtained and examined. For
+** example (preference order and equivalence is important, not values):
+** domain1 IN MX 10 mxhost-A
+** IN MX 20 mxhost-B
+** domain2 IN MX 4 mxhost-A
+** IN MX 8 mxhost-B
+** Domain1 and domain2 can piggyback the same message to mxhost-A or
+** mxhost-B (if mxhost-A cannot be reached).
+** "Coattail piggybacking" relaxes the strictness of "coincidental
+** piggybacking" in the hope that most significant (lowest value)
+** MX preference host(s) can create more piggybacking. For example
+** (again, preference order and equivalence is important, not values):
+** domain3 IN MX 100 mxhost-C
+** IN MX 100 mxhost-D
+** IN MX 200 mxhost-E
+** domain4 IN MX 50 mxhost-C
+** IN MX 50 mxhost-D
+** IN MX 80 mxhost-F
+** A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C
+** is available. Same with mxhost-D because in both RR's the preference
+** value is the same as mxhost-C, respectively.
+** So deliver attempts coattail piggybacking when possible. If the
+** first MX preference level hosts cannot be used then the piggybacking
+** reverts to coincidental piggybacking. Using the above example you
+** cannot deliver to mxhost-F for domain3 regardless of preference value.
+** ("Coattail" from "riding on the coattails of your predecessor" meaning
+** gaining benefit from a predecessor effort with no or little addition
+** effort. The predecessor here being the preceding MX RR).
**
** Parameters:
** e -- the envelope to deliver.
@@ -994,41 +1242,44 @@ deliver(e, firstto)
register char *p;
register MAILER *m; /* mailer for this recipient */
ADDRESS *volatile ctladdr;
+#if HASSETUSERCONTEXT
ADDRESS *volatile contextaddr = NULL;
+#endif /* HASSETUSERCONTEXT */
register MCI *volatile mci;
- register ADDRESS *to = firstto;
- volatile bool clever = FALSE; /* running user smtp to this mailer */
+ register ADDRESS *SM_NONVOLATILE to = firstto;
+ volatile bool clever = false; /* running user smtp to this mailer */
ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */
int rcode; /* response code */
- int lmtp_rcode = EX_OK;
- int nummxhosts = 0; /* number of MX hosts available */
- int hostnum = 0; /* current MX host index */
+ SM_NONVOLATILE int lmtp_rcode = EX_OK;
+ SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */
+ SM_NONVOLATILE int hostnum = 0; /* current MX host index */
char *firstsig; /* signature of firstto */
- pid_t pid = -1;
+ volatile pid_t pid = -1;
char *volatile curhost;
- register u_short port = 0;
+ SM_NONVOLATILE unsigned short port = 0;
+ SM_NONVOLATILE time_t enough = 0;
#if NETUNIX
- char *mux_path = NULL; /* path to UNIX domain socket */
+ char *SM_NONVOLATILE mux_path = NULL; /* path to UNIX domain socket */
#endif /* NETUNIX */
time_t xstart;
bool suidwarn;
bool anyok; /* at least one address was OK */
- bool goodmxfound = FALSE; /* at least one MX was OK */
+ SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */
bool ovr;
-#if _FFR_DYNAMIC_TOBUF
+#if _FFR_QUARANTINE
+ bool quarantine;
+#endif /* _FFR_QUARANTINE */
int strsize;
int rcptcount;
+ int ret;
static int tobufsize = 0;
static char *tobuf = NULL;
-#else /* _FFR_DYNAMIC_TOBUF */
- char tobuf[TOBUFSIZE]; /* text line of to people */
-#endif /* _FFR_DYNAMIC_TOBUF */
+ char *rpath; /* translated return path */
int mpvect[2];
int rpvect[2];
char *mxhosts[MAXMXHOSTS + 1];
char *pv[MAXPV + 1];
char buf[MAXNAME + 1];
- char rpathbuf[MAXNAME + 1]; /* translated return path */
errno = 0;
if (!QS_IS_OK(to->q_state))
@@ -1040,29 +1291,32 @@ deliver(e, firstto)
host = to->q_host;
CurEnv = e; /* just in case */
e->e_statmsg = NULL;
-#if SMTP
SmtpError[0] = '\0';
-#endif /* SMTP */
xstart = curtime();
if (tTd(10, 1))
- dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
+ sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
e->e_id, m->m_name, host, to->q_user);
if (tTd(10, 100))
- printopenfds(FALSE);
+ printopenfds(false);
/*
- ** Clear $&{client_*} macros if this is a bounce message to
+ ** Clear {client_*} macros if this is a bounce message to
** prevent rejection by check_compat ruleset.
*/
if (bitset(EF_RESPONSE, e->e_flags))
{
- define(macid("{client_name}", NULL), "", e);
- define(macid("{client_addr}", NULL), "", e);
- define(macid("{client_port}", NULL), "", e);
+ macdefine(&e->e_macro, A_PERM, macid("{client_name}"), "");
+ macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), "");
+ macdefine(&e->e_macro, A_PERM, macid("{client_port}"), "");
+ macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), "");
}
+ SM_TRY
+ {
+ ADDRESS *skip_back = NULL;
+
/*
** Do initial argv setup.
** Insert the mailer name. Notice that $x expansion is
@@ -1080,15 +1334,18 @@ deliver(e, firstto)
p = e->e_sender;
else
p = e->e_from.q_paddr;
- p = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e);
- if (strlen(p) >= (SIZE_T) sizeof rpathbuf)
+ rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e);
+ if (strlen(rpath) > MAXSHORTSTR)
{
- p = shortenstring(p, MAXSHORTSTR);
- syserr("remotename: huge return %s", p);
+ rpath = shortenstring(rpath, MAXSHORTSTR);
+
+ /* avoid bogus errno */
+ errno = 0;
+ syserr("remotename: huge return path %s", rpath);
}
- snprintf(rpathbuf, sizeof rpathbuf, "%s", p);
- define('g', rpathbuf, e); /* translated return path */
- define('h', host, e); /* to host */
+ rpath = sm_rpool_strdup_x(e->e_rpool, rpath);
+ macdefine(&e->e_macro, A_PERM, 'g', rpath);
+ macdefine(&e->e_macro, A_PERM, 'h', host);
Errors = 0;
pvp = pv;
*pvp++ = m->m_argv[0];
@@ -1102,7 +1359,7 @@ deliver(e, firstto)
*pvp++ = "-f";
else
*pvp++ = "-r";
- *pvp++ = newstr(rpathbuf);
+ *pvp++ = rpath;
}
/*
@@ -1129,12 +1386,13 @@ deliver(e, firstto)
/* this entry is safe -- go ahead and process it */
expand(*mvp, buf, sizeof buf, e);
- *pvp++ = newstr(buf);
+ *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
if (pvp >= &pv[MAXPV - 3])
{
syserr("554 5.3.5 Too many parameters to %s before $u",
pv[0]);
- return -1;
+ rcode = -1;
+ goto cleanup;
}
}
@@ -1147,14 +1405,8 @@ deliver(e, firstto)
if (*mvp == NULL)
{
/* running LMTP or SMTP */
-#if SMTP
- clever = TRUE;
+ clever = true;
*pvp = NULL;
-#else /* SMTP */
- /* oops! we don't implement SMTP */
- syserr("554 5.3.5 SMTP style mailer not implemented");
- return EX_SOFTWARE;
-#endif /* SMTP */
}
else if (bitnset(M_LMTP, m->m_flags))
{
@@ -1172,62 +1424,85 @@ deliver(e, firstto)
** always send another copy later.
*/
-#if _FFR_DYNAMIC_TOBUF
e->e_to = NULL;
strsize = 2;
rcptcount = 0;
-#else /* _FFR_DYNAMIC_TOBUF */
- tobuf[0] = '\0';
- e->e_to = tobuf;
-#endif /* _FFR_DYNAMIC_TOBUF */
-
ctladdr = NULL;
- firstsig = hostsignature(firstto->q_mailer, firstto->q_host);
+ if (firstto->q_signature == NULL)
+ firstto->q_signature = hostsignature(firstto->q_mailer,
+ firstto->q_host);
+ firstsig = firstto->q_signature;
+
for (; to != NULL; to = to->q_next)
{
/* avoid sending multiple recipients to dumb mailers */
-#if _FFR_DYNAMIC_TOBUF
if (tochain != NULL && !bitnset(M_MUSER, m->m_flags))
break;
-#else /* _FFR_DYNAMIC_TOBUF */
- if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags))
- break;
-#endif /* _FFR_DYNAMIC_TOBUF */
/* if already sent or not for this host, don't send */
- if (!QS_IS_OK(to->q_state) ||
- to->q_mailer != firstto->q_mailer ||
- strcmp(hostsignature(to->q_mailer, to->q_host),
- firstsig) != 0)
+ if (!QS_IS_OK(to->q_state)) /* already sent; look at next */
continue;
- /* avoid overflowing tobuf */
-#if _FFR_DYNAMIC_TOBUF
- strsize += strlen(to->q_paddr) + 1;
- if (!clever && strsize > TOBUFSIZE)
+ /*
+ ** Must be same mailer to keep grouping rcpts.
+ ** If mailers don't match: continue; sendqueue is not
+ ** sorted by mailers, so don't break;
+ */
+
+ if (to->q_mailer != firstto->q_mailer)
+ continue;
+
+ if (to->q_signature == NULL) /* for safety */
+ to->q_signature = hostsignature(to->q_mailer,
+ to->q_host);
+
+ /*
+ ** This is for coincidental and tailcoat piggybacking messages
+ ** to the same mail host. While the signatures are identical
+ ** (that's the MX-RR's are identical) we can do coincidental
+ ** piggybacking. We try hard for coattail piggybacking
+ ** with the same mail host when the next recipient has the
+ ** same host at lowest preference. It may be that this
+ ** won't work out, so 'skip_back' is maintained if a backup
+ ** to coincidental piggybacking or full signature must happen.
+ */
+
+ ret = firstto == to ? HS_MATCH_FULL :
+ coloncmp(to->q_signature, firstsig);
+ if (ret == HS_MATCH_FULL)
+ skip_back = to;
+ else if (ret == HS_MATCH_NO)
break;
+ if (!clever)
+ {
+ /* avoid overflowing tobuf */
+ strsize += strlen(to->q_paddr) + 1;
+ if (strsize > TOBUFSIZE)
+ break;
+ }
+
if (++rcptcount > to->q_mailer->m_maxrcpt)
break;
-#else /* _FFR_DYNAMIC_TOBUF */
- if (sizeof tobuf < (strlen(to->q_paddr) + strlen(tobuf) + 2))
- break;
-#endif /* _FFR_DYNAMIC_TOBUF */
if (tTd(10, 1))
{
- dprintf("\nsend to ");
- printaddr(to, FALSE);
+ sm_dprintf("\nsend to ");
+ printaddr(to, false);
}
/* compute effective uid/gid when sending */
if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
+# if HASSETUSERCONTEXT
contextaddr = ctladdr = getctladdr(to);
+# else /* HASSETUSERCONTEXT */
+ ctladdr = getctladdr(to);
+# endif /* HASSETUSERCONTEXT */
if (tTd(10, 2))
{
- dprintf("ctladdr=");
- printaddr(ctladdr, FALSE);
+ sm_dprintf("ctladdr=");
+ printaddr(ctladdr, false);
}
user = to->q_user;
@@ -1247,45 +1522,61 @@ deliver(e, firstto)
to->q_status = "5.2.3";
else
to->q_status = "5.3.4";
+
/* set to->q_rstatus = NULL; or to the following? */
usrerrenh(to->q_status,
"552 Message is too large; %ld bytes max",
m->m_maxsize);
- markfailure(e, to, NULL, EX_UNAVAILABLE, FALSE);
+ markfailure(e, to, NULL, EX_UNAVAILABLE, false);
giveresponse(EX_UNAVAILABLE, to->q_status, m,
- NULL, ctladdr, xstart, e);
+ NULL, ctladdr, xstart, e, to);
continue;
}
-#if NAMED_BIND
SM_SET_H_ERRNO(0);
-#endif /* NAMED_BIND */
+ ovr = true;
- ovr = TRUE;
/* do config file checking of compatibility */
+#if _FFR_QUARANTINE
+ quarantine = (e->e_quarmsg != NULL);
+#endif /* _FFR_QUARANTINE */
rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr,
- e, TRUE, TRUE, 4, NULL);
+ e, true, true, 3, NULL, e->e_id);
if (rcode == EX_OK)
{
/* do in-code checking if not discarding */
if (!bitset(EF_DISCARD, e->e_flags))
{
rcode = checkcompat(to, e);
- ovr = FALSE;
+ ovr = false;
}
}
if (rcode != EX_OK)
{
markfailure(e, to, NULL, rcode, ovr);
giveresponse(rcode, to->q_status, m,
- NULL, ctladdr, xstart, e);
+ NULL, ctladdr, xstart, e, to);
continue;
}
+#if _FFR_QUARANTINE
+ if (!quarantine && e->e_quarmsg != NULL)
+ {
+ /*
+ ** check_compat or checkcompat() has tried
+ ** to quarantine but that isn't supported.
+ ** Revert the attempt.
+ */
+
+ e->e_quarmsg = NULL;
+ macdefine(&e->e_macro, A_PERM,
+ macid("{quarantine}"), "");
+ }
+#endif /* _FFR_QUARANTINE */
if (bitset(EF_DISCARD, e->e_flags))
{
if (tTd(10, 5))
{
- dprintf("deliver: discarding recipient ");
- printaddr(to, FALSE);
+ sm_dprintf("deliver: discarding recipient ");
+ printaddr(to, false);
}
/* pretend the message was sent */
@@ -1340,11 +1631,11 @@ deliver(e, firstto)
if (strcmp(m->m_mailer, "[FILE]") == 0)
{
- define('u', user, e); /* to user */
+ macdefine(&e->e_macro, A_PERM, 'u', user);
p = to->q_home;
if (p == NULL && ctladdr != NULL)
p = ctladdr->q_home;
- define('z', p, e); /* user's home */
+ macdefine(&e->e_macro, A_PERM, 'z', p);
expand(m->m_argv[1], buf, sizeof buf, e);
if (strlen(buf) > 0)
rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e);
@@ -1355,8 +1646,8 @@ deliver(e, firstto)
rcode = EX_CONFIG;
}
giveresponse(rcode, to->q_status, m, NULL,
- ctladdr, xstart, e);
- markfailure(e, to, NULL, rcode, TRUE);
+ ctladdr, xstart, e, to);
+ markfailure(e, to, NULL, rcode, true);
e->e_nsent++;
if (rcode == EX_OK)
{
@@ -1366,12 +1657,14 @@ deliver(e, firstto)
{
to->q_flags |= QDELIVERED;
to->q_status = "2.1.5";
- fprintf(e->e_xfp, "%s... Successfully delivered\n",
- to->q_paddr);
+ (void) sm_io_fprintf(e->e_xfp,
+ SM_TIME_DEFAULT,
+ "%s... Successfully delivered\n",
+ to->q_paddr);
}
}
to->q_statdate = curtime();
- markstats(e, to, FALSE);
+ markstats(e, to, STATS_NORMAL);
continue;
}
@@ -1383,20 +1676,13 @@ deliver(e, firstto)
/* link together the chain of recipients */
to->q_tchain = tochain;
tochain = to;
-
-#if _FFR_DYNAMIC_TOBUF
e->e_to = "[CHAIN]";
-#else /* _FFR_DYNAMIC_TOBUF */
- /* create list of users for error messages */
- (void) strlcat(tobuf, ",", sizeof tobuf);
- (void) strlcat(tobuf, to->q_paddr, sizeof tobuf);
-#endif /* _FFR_DYNAMIC_TOBUF */
- define('u', user, e); /* to user */
+ macdefine(&e->e_macro, A_PERM, 'u', user); /* to user */
p = to->q_home;
if (p == NULL && ctladdr != NULL)
p = ctladdr->q_home;
- define('z', p, e); /* user's home */
+ macdefine(&e->e_macro, A_PERM, 'z', p); /* user's home */
/* set the ${dsn_notify} macro if applicable */
if (bitset(QHASNOTIFY, to->q_flags))
@@ -1405,24 +1691,28 @@ deliver(e, firstto)
notify[0] = '\0';
if (bitset(QPINGONSUCCESS, to->q_flags))
- (void) strlcat(notify, "SUCCESS,",
- sizeof notify);
+ (void) sm_strlcat(notify, "SUCCESS,",
+ sizeof notify);
if (bitset(QPINGONFAILURE, to->q_flags))
- (void) strlcat(notify, "FAILURE,",
- sizeof notify);
+ (void) sm_strlcat(notify, "FAILURE,",
+ sizeof notify);
if (bitset(QPINGONDELAY, to->q_flags))
- (void) strlcat(notify, "DELAY,", sizeof notify);
+ (void) sm_strlcat(notify, "DELAY,",
+ sizeof notify);
/* Set to NEVER or drop trailing comma */
if (notify[0] == '\0')
- (void) strlcat(notify, "NEVER", sizeof notify);
+ (void) sm_strlcat(notify, "NEVER",
+ sizeof notify);
else
notify[strlen(notify) - 1] = '\0';
- define(macid("{dsn_notify}", NULL), newstr(notify), e);
+ macdefine(&e->e_macro, A_TEMP,
+ macid("{dsn_notify}"), notify);
}
else
- define(macid("{dsn_notify}", NULL), NULL, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{dsn_notify}"), NULL);
/*
** Expand out this user into argument list.
@@ -1431,7 +1721,7 @@ deliver(e, firstto)
if (!clever)
{
expand(*mvp, buf, sizeof buf, e);
- *pvp++ = newstr(buf);
+ *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
if (pvp >= &pv[MAXPV - 2])
{
/* allow some space for trailing parms */
@@ -1441,57 +1731,48 @@ deliver(e, firstto)
}
/* see if any addresses still exist */
-#if _FFR_DYNAMIC_TOBUF
if (tochain == NULL)
-#else /* _FFR_DYNAMIC_TOBUF */
- if (tobuf[0] == '\0')
-#endif /* _FFR_DYNAMIC_TOBUF */
{
- define('g', (char *) NULL, e);
- e->e_to = NULL;
- return 0;
+ rcode = 0;
+ goto cleanup;
}
/* print out messages as full list */
-#if _FFR_DYNAMIC_TOBUF
+ strsize = 1;
+ for (to = tochain; to != NULL; to = to->q_tchain)
+ strsize += strlen(to->q_paddr) + 1;
+ if (strsize < TOBUFSIZE)
+ strsize = TOBUFSIZE;
+ if (strsize > tobufsize)
{
- int l = 1;
- char *tobufptr;
-
- for (to = tochain; to != NULL; to = to->q_tchain)
- l += strlen(to->q_paddr) + 1;
- if (l < TOBUFSIZE)
- l = TOBUFSIZE;
- if (l > tobufsize)
- {
- if (tobuf != NULL)
- sm_free(tobuf);
- tobufsize = l;
- tobuf = xalloc(tobufsize);
- }
- tobufptr = tobuf;
- *tobufptr = '\0';
- for (to = tochain; to != NULL; to = to->q_tchain)
- {
- snprintf(tobufptr, tobufsize - (tobufptr - tobuf),
- ",%s", to->q_paddr);
- tobufptr += strlen(tobufptr);
- }
+ SM_FREE_CLR(tobuf);
+ tobuf = sm_pmalloc_x(strsize);
+ tobufsize = strsize;
+ }
+ p = tobuf;
+ *p = '\0';
+ for (to = tochain; to != NULL; to = to->q_tchain)
+ {
+ (void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2,
+ ",", to->q_paddr);
+ p += strlen(p);
}
-#endif /* _FFR_DYNAMIC_TOBUF */
e->e_to = tobuf + 1;
/*
** Fill out any parameters after the $u parameter.
*/
- while (!clever && *++mvp != NULL)
+ if (!clever)
{
- expand(*mvp, buf, sizeof buf, e);
- *pvp++ = newstr(buf);
- if (pvp >= &pv[MAXPV])
- syserr("554 5.3.0 deliver: pv overflow after $u for %s",
- pv[0]);
+ while (*++mvp != NULL)
+ {
+ expand(*mvp, buf, sizeof buf, e);
+ *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
+ if (pvp >= &pv[MAXPV])
+ syserr("554 5.3.0 deliver: pv overflow after $u for %s",
+ pv[0]);
+ }
}
*pvp++ = NULL;
@@ -1515,14 +1796,11 @@ deliver(e, firstto)
if (tTd(11, 1))
{
- dprintf("openmailer:");
+ sm_dprintf("openmailer:");
printav(pv);
}
errno = 0;
-#if NAMED_BIND
SM_SET_H_ERRNO(0);
-#endif /* NAMED_BIND */
-
CurHostName = NULL;
/*
@@ -1543,8 +1821,9 @@ deliver(e, firstto)
char wbuf[MAXLINE];
/* make absolutely certain 0, 1, and 2 are in use */
- snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)",
- shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
+ (void) sm_snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)",
+ shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name);
checkfd012(wbuf);
}
#endif /* XDEBUG */
@@ -1559,7 +1838,7 @@ deliver(e, firstto)
{
e->e_status = "5.6.3";
usrerrenh(e->e_status,
- "554 Cannot send 8-bit data to 7-bit destination");
+ "554 Cannot send 8-bit data to 7-bit destination");
rcode = EX_DATAERR;
goto give_up;
}
@@ -1570,17 +1849,50 @@ deliver(e, firstto)
/* check for Local Person Communication -- not for mortals!!! */
if (strcmp(m->m_mailer, "[LPC]") == 0)
{
- mci = (MCI *) xalloc(sizeof *mci);
- memset((char *) mci, '\0', sizeof *mci);
- mci->mci_in = stdin;
- mci->mci_out = stdout;
+#if _FFR_CACHE_LPC
+ if (clever)
+ {
+ /* flush any expired connections */
+ (void) mci_scan(NULL);
+
+ /* try to get a cached connection or just a slot */
+ mci = mci_get(m->m_name, m);
+ if (mci->mci_host == NULL)
+ mci->mci_host = m->m_name;
+ CurHostName = mci->mci_host;
+ if (mci->mci_state != MCIS_CLOSED)
+ {
+ message("Using cached SMTP/LPC connection for %s...",
+ m->m_name);
+ mci->mci_deliveries++;
+ goto do_transfer;
+ }
+ }
+ else
+ {
+ mci = mci_new(e->e_rpool);
+ }
+ mci->mci_in = smioin;
+ mci->mci_out = smioout;
+ mci->mci_mailer = m;
+ mci->mci_host = m->m_name;
+ if (clever)
+ {
+ mci->mci_state = MCIS_OPENING;
+ mci_cache(mci);
+ }
+ else
+ mci->mci_state = MCIS_OPEN;
+#else /* _FFR_CACHE_LPC */
+ mci = mci_new(e->e_rpool);
+ mci->mci_in = smioin;
+ mci->mci_out = smioout;
mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN;
mci->mci_mailer = m;
+#endif /* _FFR_CACHE_LPC */
}
- else if (strcmp(m->m_mailer, "[IPC]") == 0 ||
- strcmp(m->m_mailer, "[TCP]") == 0)
+ else if (strcmp(m->m_mailer, "[IPC]") == 0)
{
-#if DAEMON
register int i;
if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
@@ -1622,7 +1934,7 @@ deliver(e, firstto)
# endif /* NETUNIX */
)
{
- port = htons((u_short)atoi(pv[2]));
+ port = htons((unsigned short) atoi(pv[2]));
if (port == 0)
{
# ifdef NO_GETSERVBYNAME
@@ -1639,6 +1951,8 @@ deliver(e, firstto)
}
nummxhosts = parse_hostsignature(curhost, mxhosts, m);
+ if (TimeOuts.to_aconnect > 0)
+ enough = curtime() + TimeOuts.to_aconnect;
tryhost:
while (hostnum < nummxhosts)
{
@@ -1664,6 +1978,24 @@ tryhost:
*endp = '\0';
}
+ if (hostnum == 1 && skip_back != NULL)
+ {
+ /*
+ ** Coattail piggybacking is no longer an
+ ** option with the mail host next to be tried
+ ** no longer the lowest MX preference
+ ** (hostnum == 1 meaning we're on the second
+ ** preference). We do not try to coattail
+ ** piggyback more than the first MX preference.
+ ** Revert 'tochain' to last location for
+ ** coincidental piggybacking. This works this
+ ** easily because the q_tchain kept getting
+ ** added to the top of the linked list.
+ */
+
+ tochain = skip_back;
+ }
+
if (*mxhosts[hostnum] == '\0')
{
syserr("deliver: null host name in signature");
@@ -1672,8 +2004,8 @@ tryhost:
*endp = sep;
continue;
}
- (void) strlcpy(hostbuf, mxhosts[hostnum],
- sizeof hostbuf);
+ (void) sm_strlcpy(hostbuf, mxhosts[hostnum],
+ sizeof hostbuf);
hostnum++;
if (endp != NULL)
*endp = sep;
@@ -1683,15 +2015,22 @@ tryhost:
mci = mci_get(hostbuf, m);
if (mci->mci_state != MCIS_CLOSED)
{
+ char *type;
+
if (tTd(11, 1))
{
- dprintf("openmailer: ");
- mci_dump(mci, FALSE);
+ sm_dprintf("openmailer: ");
+ mci_dump(mci, false);
}
CurHostName = mci->mci_host;
- message("Using cached %sSMTP connection to %s via %s...",
- bitset(MCIF_ESMTP, mci->mci_flags) ? "E" : "",
- hostbuf, m->m_name);
+ if (bitnset(M_LMTP, m->m_flags))
+ type = "L";
+ else if (bitset(MCIF_ESMTP, mci->mci_flags))
+ type = "ES";
+ else
+ type = "S";
+ message("Using cached %sMTP connection to %s via %s...",
+ type, hostbuf, m->m_name);
mci->mci_deliveries++;
break;
}
@@ -1699,19 +2038,19 @@ tryhost:
if (mci->mci_exitstat != EX_OK)
{
if (mci->mci_exitstat == EX_TEMPFAIL)
- goodmxfound = TRUE;
+ goodmxfound = true;
continue;
}
if (mci_lock_host(mci) != EX_OK)
{
mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
- goodmxfound = TRUE;
+ goodmxfound = true;
continue;
}
/* try the connection */
- sm_setproctitle(TRUE, e, "%s %s: %s",
+ sm_setproctitle(true, e, "%s %s: %s",
qid_printname(e),
hostbuf, "user open");
# if NETUNIX
@@ -1719,7 +2058,7 @@ tryhost:
{
message("Connecting to %s via %s...",
mux_path, m->m_name);
- i = makeconnection_ds(mux_path, mci);
+ i = makeconnection_ds((char *) mux_path, mci);
}
else
# endif /* NETUNIX */
@@ -1731,7 +2070,8 @@ tryhost:
message("Connecting to %s port %d via %s...",
hostbuf, ntohs(port),
m->m_name);
- i = makeconnection(hostbuf, port, mci, e);
+ i = makeconnection(hostbuf, port, mci, e,
+ enough);
}
mci->mci_errno = errno;
mci->mci_lastuse = curtime();
@@ -1740,23 +2080,55 @@ tryhost:
# if NAMED_BIND
mci->mci_herrno = h_errno;
# endif /* NAMED_BIND */
+
+ /*
+ ** Have we tried long enough to get a connection?
+ ** If yes, skip to the fallback MX hosts
+ ** (if existent).
+ */
+
+ if (enough > 0 && mci->mci_lastuse >= enough)
+ {
+ int h;
+# if NAMED_BIND
+ extern int NumFallBackMXHosts;
+# else /* NAMED_BIND */
+ const int NumFallBackMXHosts = 0;
+# endif /* NAMED_BIND */
+
+ if (hostnum < nummxhosts && LogLevel > 9)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Timeout.to_aconnect occurred before exhausting all addresses");
+
+ /* turn off timeout if fallback available */
+ if (NumFallBackMXHosts > 0)
+ enough = 0;
+
+ /* skip to a fallback MX host */
+ h = nummxhosts - NumFallBackMXHosts;
+ if (hostnum < h)
+ hostnum = h;
+ }
if (i == EX_OK)
{
- goodmxfound = TRUE;
+ goodmxfound = true;
mci->mci_state = MCIS_OPENING;
mci_cache(mci);
if (TrafficLogFile != NULL)
- fprintf(TrafficLogFile, "%05d === CONNECT %s\n",
- (int) getpid(), hostbuf);
+ (void) sm_io_fprintf(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ "%05d === CONNECT %s\n",
+ (int) CurrentPid,
+ hostbuf);
break;
}
else
{
if (tTd(11, 1))
- dprintf("openmailer: makeconnection => stat=%d, errno=%d\n",
- i, errno);
+ sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n",
+ i, errno);
if (i == EX_TEMPFAIL)
- goodmxfound = TRUE;
+ goodmxfound = true;
mci_unlock_host(mci);
}
@@ -1772,13 +2144,6 @@ tryhost:
goto give_up;
}
mci->mci_pid = 0;
-#else /* DAEMON */
- syserr("554 5.3.5 openmailer: no IPC");
- if (tTd(11, 1))
- dprintf("openmailer: NULL\n");
- rcode = EX_UNAVAILABLE;
- goto give_up;
-#endif /* DAEMON */
}
else
{
@@ -1786,7 +2151,6 @@ tryhost:
(void) mci_scan(NULL);
mci = NULL;
-#if SMTP
if (bitnset(M_LMTP, m->m_flags))
{
/* try to get a cached connection */
@@ -1802,7 +2166,6 @@ tryhost:
goto do_transfer;
}
}
-#endif /* SMTP */
/* announce the connection to verbose listeners */
if (host == NULL || host[0] == '\0')
@@ -1813,10 +2176,14 @@ tryhost:
{
char **av;
- fprintf(TrafficLogFile, "%05d === EXEC", (int) getpid());
+ (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
+ "%05d === EXEC", (int) CurrentPid);
for (av = pv; *av != NULL; av++)
- fprintf(TrafficLogFile, " %s", *av);
- fprintf(TrafficLogFile, "\n");
+ (void) sm_io_fprintf(TrafficLogFile,
+ SM_TIME_DEFAULT, " %s",
+ *av);
+ (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
+ "\n");
}
#if XDEBUG
@@ -1827,9 +2194,9 @@ tryhost:
if (pipe(mpvect) < 0)
{
syserr("%s... openmailer(%s): pipe (to mailer)",
- shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
+ shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
if (tTd(11, 1))
- dprintf("openmailer: NULL\n");
+ sm_dprintf("openmailer: NULL\n");
rcode = EX_OSERR;
goto give_up;
}
@@ -1839,11 +2206,11 @@ tryhost:
if (mpvect[0] < 3 || mpvect[1] < 3)
{
syserr("%s... openmailer(%s): bogus mpvect %d %d",
- shortenstring(e->e_to, MAXSHORTSTR), m->m_name,
- mpvect[0], mpvect[1]);
- printopenfds(TRUE);
+ shortenstring(e->e_to, MAXSHORTSTR), m->m_name,
+ mpvect[0], mpvect[1]);
+ printopenfds(true);
if (tTd(11, 1))
- dprintf("openmailer: NULL\n");
+ sm_dprintf("openmailer: NULL\n");
rcode = EX_OSERR;
goto give_up;
}
@@ -1853,18 +2220,21 @@ tryhost:
checkfdopen(mpvect[1], "mpvect[1]");
if (mpvect[0] == mpvect[1] ||
(e->e_lockfp != NULL &&
- (mpvect[0] == fileno(e->e_lockfp) ||
- mpvect[1] == fileno(e->e_lockfp))))
+ (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
+ NULL) ||
+ mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
+ NULL))))
{
if (e->e_lockfp == NULL)
syserr("%s... openmailer(%s): overlapping mpvect %d %d",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name, mpvect[0], mpvect[1]);
+ shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name, mpvect[0], mpvect[1]);
else
syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name, mpvect[0], mpvect[1],
- fileno(e->e_lockfp));
+ shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name, mpvect[0], mpvect[1],
+ sm_io_getinfo(e->e_lockfp,
+ SM_IO_WHAT_FD, NULL));
}
#endif /* XDEBUG */
@@ -1872,12 +2242,12 @@ tryhost:
if (pipe(rpvect) < 0)
{
syserr("%s... openmailer(%s): pipe (from mailer)",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name);
+ shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name);
(void) close(mpvect[0]);
(void) close(mpvect[1]);
if (tTd(11, 1))
- dprintf("openmailer: NULL\n");
+ sm_dprintf("openmailer: NULL\n");
rcode = EX_OSERR;
goto give_up;
}
@@ -1894,10 +2264,10 @@ tryhost:
** around so that endmailer will get it.
*/
- if (e->e_xfp != NULL)
- (void) fflush(e->e_xfp); /* for debugging */
- (void) fflush(stdout);
- (void) setsignal(SIGCHLD, SIG_DFL);
+ if (e->e_xfp != NULL) /* for debugging */
+ (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
+ (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
+ (void) sm_signal(SIGCHLD, SIG_DFL);
DOFORK(FORK);
@@ -1907,13 +2277,13 @@ tryhost:
{
/* failure */
syserr("%s... openmailer(%s): cannot fork",
- shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
+ shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
(void) close(mpvect[0]);
(void) close(mpvect[1]);
(void) close(rpvect[0]);
(void) close(rpvect[1]);
if (tTd(11, 1))
- dprintf("openmailer: NULL\n");
+ sm_dprintf("openmailer: NULL\n");
rcode = EX_OSERR;
goto give_up;
}
@@ -1921,31 +2291,38 @@ tryhost:
{
int i;
int save_errno;
+ int sff;
int new_euid = NO_UID;
int new_ruid = NO_UID;
int new_gid = NO_GID;
+ char *user = NULL;
struct stat stb;
extern int DtableSize;
+ CurrentPid = getpid();
+
/* clear the events to turn off SIGALRMs */
- clear_events();
+ sm_clear_events();
/* Reset global flags */
RestartRequest = NULL;
+ RestartWorkGroup = false;
ShutdownRequest = NULL;
PendingSignal = 0;
if (e->e_lockfp != NULL)
- (void) close(fileno(e->e_lockfp));
+ (void) close(sm_io_getinfo(e->e_lockfp,
+ SM_IO_WHAT_FD,
+ NULL));
/* child -- set up input & exec mailer */
- (void) setsignal(SIGALRM, sm_signal_noop);
- (void) setsignal(SIGCHLD, SIG_DFL);
- (void) setsignal(SIGHUP, SIG_IGN);
- (void) setsignal(SIGINT, SIG_IGN);
- (void) setsignal(SIGTERM, SIG_DFL);
+ (void) sm_signal(SIGALRM, sm_signal_noop);
+ (void) sm_signal(SIGCHLD, SIG_DFL);
+ (void) sm_signal(SIGHUP, SIG_IGN);
+ (void) sm_signal(SIGINT, SIG_IGN);
+ (void) sm_signal(SIGTERM, SIG_DFL);
# ifdef SIGUSR1
- (void) setsignal(SIGUSR1, sm_signal_noop);
+ (void) sm_signal(SIGUSR1, sm_signal_noop);
# endif /* SIGUSR1 */
if (m != FileMailer || stat(tochain->q_user, &stb) < 0)
@@ -1966,14 +2343,16 @@ tryhost:
pwd = sm_getpwnam(contextaddr->q_user);
if (pwd != NULL)
(void) setusercontext(NULL,
- pwd, pwd->pw_uid,
- LOGIN_SETRESOURCES|LOGIN_SETPRIORITY);
+ pwd, pwd->pw_uid,
+ LOGIN_SETRESOURCES|LOGIN_SETPRIORITY);
}
# endif /* HASSETUSERCONTEXT */
+#if HASNICE
/* tweak niceness */
if (m->m_nice != 0)
(void) nice(m->m_nice);
+#endif /* HASNICE */
/* reset group id */
if (bitnset(M_SPECIFIC_UID, m->m_flags))
@@ -1984,15 +2363,16 @@ tryhost:
{
if (!DontInitGroups)
{
- char *u = ctladdr->q_ruser;
-
- if (u == NULL)
- u = ctladdr->q_user;
+ user = ctladdr->q_ruser;
+ if (user == NULL)
+ user = ctladdr->q_user;
- if (initgroups(u, ctladdr->q_gid) == -1 && suidwarn)
+ if (initgroups(user,
+ ctladdr->q_gid) == -1
+ && suidwarn)
{
syserr("openmailer: initgroups(%s, %d) failed",
- u, ctladdr->q_gid);
+ user, ctladdr->q_gid);
exit(EX_TEMPFAIL);
}
}
@@ -2001,7 +2381,8 @@ tryhost:
GIDSET_T gidset[1];
gidset[0] = ctladdr->q_gid;
- if (setgroups(1, gidset) == -1 && suidwarn)
+ if (setgroups(1, gidset) == -1
+ && suidwarn)
{
syserr("openmailer: setgroups() failed");
exit(EX_TEMPFAIL);
@@ -2013,10 +2394,12 @@ tryhost:
{
if (!DontInitGroups)
{
- if (initgroups(DefUser, DefGid) == -1 && suidwarn)
+ user = DefUser;
+ if (initgroups(DefUser, DefGid) == -1 &&
+ suidwarn)
{
syserr("openmailer: initgroups(%s, %d) failed",
- DefUser, DefGid);
+ DefUser, DefGid);
exit(EX_TEMPFAIL);
}
}
@@ -2025,7 +2408,8 @@ tryhost:
GIDSET_T gidset[1];
gidset[0] = DefGid;
- if (setgroups(1, gidset) == -1 && suidwarn)
+ if (setgroups(1, gidset) == -1
+ && suidwarn)
{
syserr("openmailer: setgroups() failed");
exit(EX_TEMPFAIL);
@@ -2044,7 +2428,9 @@ tryhost:
new_gid != getegid())
{
/* Only root can change the gid */
- syserr("openmailer: insufficient privileges to change gid");
+ syserr("openmailer: insufficient privileges to change gid, RunAsUid=%d, new_gid=%d, gid=%d, egid=%d",
+ (int) RunAsUid, (int) new_gid,
+ (int) getgid(), (int) getegid());
exit(EX_TEMPFAIL);
}
@@ -2061,12 +2447,12 @@ tryhost:
{
expand(m->m_rootdir, buf, sizeof buf, e);
if (tTd(11, 20))
- dprintf("openmailer: chroot %s\n",
- buf);
+ sm_dprintf("openmailer: chroot %s\n",
+ buf);
if (chroot(buf) < 0)
{
syserr("openmailer: Cannot chroot(%s)",
- buf);
+ buf);
exit(EX_TEMPFAIL);
}
if (chdir("/") < 0)
@@ -2078,6 +2464,7 @@ tryhost:
/* reset user id */
endpwent();
+ sm_mbdb_terminate();
if (bitnset(M_SPECIFIC_UID, m->m_flags))
{
new_euid = m->m_uid;
@@ -2090,7 +2477,21 @@ tryhost:
*/
if (RealUid != 0 && RealUid != getuid())
+ {
+# if MAILER_SETUID_METHOD == USE_SETEUID
+# if HASSETREUID
+ if (setreuid(RealUid, geteuid()) < 0)
+ {
+ syserr("openmailer: setreuid(%d, %d) failed",
+ (int) RealUid, (int) geteuid());
+ exit(EX_OSERR);
+ }
+# endif /* HASSETREUID */
+# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
+# if MAILER_SETUID_METHOD == USE_SETREUID
new_ruid = RealUid;
+# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
+ }
}
else if (bitset(S_ISUID, stb.st_mode))
new_ruid = stb.st_uid;
@@ -2105,32 +2506,17 @@ tryhost:
if (RunAsUid != 0 && new_euid != RunAsUid)
{
/* Only root can change the uid */
- syserr("openmailer: insufficient privileges to change uid");
+ syserr("openmailer: insufficient privileges to change uid, new_euid=%d, RunAsUid=%d",
+ (int) new_euid, (int) RunAsUid);
exit(EX_TEMPFAIL);
}
vendor_set_uid(new_euid);
# if MAILER_SETUID_METHOD == USE_SETEUID
-# if HASSETREUID
- /*
- ** Undo the effects of the uid change in main
- ** for signal handling. The real uid may
- ** be used by mailer in adding a "From "
- ** line.
- */
-
- if (new_ruid != NO_UID &&
- setreuid(RealUid, geteuid()) < 0)
- {
- syserr("openmailer: setreuid(%d, %d) failed",
- (int) new_ruid, (int) geteuid());
- exit(EX_OSERR);
- }
-# endif /* HASSETREUID */
if (seteuid(new_euid) < 0 && suidwarn)
{
syserr("openmailer: seteuid(%ld) failed",
- (long) new_euid);
+ (long) new_euid);
exit(EX_TEMPFAIL);
}
# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
@@ -2138,7 +2524,7 @@ tryhost:
if (setreuid(new_ruid, new_euid) < 0 && suidwarn)
{
syserr("openmailer: setreuid(%ld, %ld) failed",
- (long) new_ruid, (long) new_euid);
+ (long) new_ruid, (long) new_euid);
exit(EX_TEMPFAIL);
}
# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
@@ -2146,7 +2532,7 @@ tryhost:
if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn)
{
syserr("openmailer: setuid(%ld) failed",
- (long) new_euid);
+ (long) new_euid);
exit(EX_TEMPFAIL);
}
# endif /* MAILER_SETUID_METHOD == USE_SETUID */
@@ -2157,15 +2543,15 @@ tryhost:
if (setuid(new_ruid) < 0 && suidwarn)
{
syserr("openmailer: setuid(%ld) failed",
- (long) new_ruid);
+ (long) new_ruid);
exit(EX_TEMPFAIL);
}
}
if (tTd(11, 2))
- dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n",
- (int) getuid(), (int) geteuid(),
- (int) getgid(), (int) getegid());
+ sm_dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n",
+ (int) getuid(), (int) geteuid(),
+ (int) getgid(), (int) getegid());
/* move into some "safe" directory */
if (m->m_execdir != NULL)
@@ -2181,13 +2567,30 @@ tryhost:
if (q != NULL)
*q++ = ':';
if (tTd(11, 20))
- dprintf("openmailer: trydir %s\n",
- buf);
+ sm_dprintf("openmailer: trydir %s\n",
+ buf);
if (buf[0] != '\0' && chdir(buf) >= 0)
break;
}
}
+ /* Check safety of program to be run */
+ sff = SFF_ROOTOK|SFF_EXECOK;
+ if (!bitnset(DBS_RUNWRITABLEPROGRAM,
+ DontBlameSendmail))
+ sff |= SFF_NOGWFILES|SFF_NOWWFILES;
+ if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH,
+ DontBlameSendmail))
+ sff |= SFF_NOPATHCHECK;
+ else
+ sff |= SFF_SAFEDIRPATH;
+ ret = safefile(m->m_mailer, getuid(), getgid(),
+ user, sff, 0, NULL);
+ if (ret != 0)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Warning: program %s unsafe: %s",
+ m->m_mailer, sm_errstring(ret));
+
/* arrange to filter std & diag output of command */
(void) close(rpvect[0]);
if (dup2(rpvect[1], STDOUT_FILENO) < 0)
@@ -2202,8 +2605,8 @@ tryhost:
if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
{
syserr("%s... openmailer(%s): cannot dup stdout for stderr",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name);
+ shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name);
_exit(EX_OSERR);
}
@@ -2212,8 +2615,8 @@ tryhost:
if (dup2(mpvect[0], STDIN_FILENO) < 0)
{
syserr("%s... openmailer(%s): cannot dup pipe %d for stdin",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name, mpvect[0]);
+ shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name, mpvect[0]);
_exit(EX_OSERR);
}
(void) close(mpvect[0]);
@@ -2248,8 +2651,25 @@ tryhost:
if (mci == NULL)
{
- mci = (MCI *) xalloc(sizeof *mci);
- memset((char *) mci, '\0', sizeof *mci);
+ if (clever)
+ {
+ /*
+ ** Allocate from general heap, not
+ ** envelope rpool, because this mci
+ ** is going to be cached.
+ */
+
+ mci = mci_new(NULL);
+ }
+ else
+ {
+ /*
+ ** Prevent a storage leak by allocating
+ ** this from the envelope rpool.
+ */
+
+ mci = mci_new(e->e_rpool);
+ }
}
mci->mci_mailer = m;
if (clever)
@@ -2263,11 +2683,13 @@ tryhost:
}
mci->mci_pid = pid;
(void) close(mpvect[0]);
- mci->mci_out = fdopen(mpvect[1], "w");
+ mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
+ (void *) &(mpvect[1]), SM_IO_WRONLY,
+ NULL);
if (mci->mci_out == NULL)
{
syserr("deliver: cannot create mailer output channel, fd=%d",
- mpvect[1]);
+ mpvect[1]);
(void) close(mpvect[1]);
(void) close(rpvect[0]);
(void) close(rpvect[1]);
@@ -2276,21 +2698,19 @@ tryhost:
}
(void) close(rpvect[1]);
- mci->mci_in = fdopen(rpvect[0], "r");
+ mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
+ (void *) &(rpvect[0]), SM_IO_RDONLY,
+ NULL);
if (mci->mci_in == NULL)
{
syserr("deliver: cannot create mailer input channel, fd=%d",
mpvect[1]);
(void) close(rpvect[0]);
- (void) fclose(mci->mci_out);
+ (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
mci->mci_out = NULL;
rcode = EX_OSERR;
goto give_up;
}
-
- /* Don't cache non-clever connections */
- if (!clever)
- mci->mci_flags |= MCIF_TEMP;
}
/*
@@ -2300,12 +2720,17 @@ tryhost:
if (bitnset(M_7BITS, m->m_flags) &&
(!clever || mci->mci_state == MCIS_OPENING))
mci->mci_flags |= MCIF_7BIT;
-#if SMTP
if (clever && mci->mci_state != MCIS_CLOSED)
{
-# if SASL && SFIO
+# if STARTTLS || SASL
+ int dotpos;
+ char *srvname;
+ extern SOCKADDR CurHostAddr;
+# endif /* STARTTLS || SASL */
+
+# if SASL
# define DONE_AUTH(f) bitset(MCIF_AUTHACT, f)
-# endif /* SASL && SFIO */
+# endif /* SASL */
# if STARTTLS
# define DONE_STARTTLS(f) bitset(MCIF_TLSACT, f)
# endif /* STARTTLS */
@@ -2313,97 +2738,130 @@ tryhost:
# define SET_HELO(f) f |= MCIF_ONLY_EHLO
# define CLR_HELO(f) f &= ~MCIF_ONLY_EHLO
+# if STARTTLS || SASL
+ /* don't use CurHostName, it is changed in many places */
+ if (mci->mci_host != NULL)
+ {
+ srvname = mci->mci_host;
+ dotpos = strlen(srvname) - 1;
+ if (dotpos >= 0)
+ {
+ if (srvname[dotpos] == '.')
+ srvname[dotpos] = '\0';
+ else
+ dotpos = -1;
+ }
+ }
+ else if (mci->mci_mailer != NULL)
+ {
+ srvname = mci->mci_mailer->m_name;
+ dotpos = -1;
+ }
+ else
+ {
+ srvname = "local";
+ dotpos = -1;
+ }
+
+ /* don't set {server_name} to NULL or "": see getauth() */
+ macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"),
+ srvname);
+
+ /* CurHostAddr is set by makeconnection() and mci_get() */
+ if (CurHostAddr.sa.sa_family != 0)
+ {
+ macdefine(&mci->mci_macro, A_TEMP,
+ macid("{server_addr}"),
+ anynet_ntoa(&CurHostAddr));
+ }
+ else if (mci->mci_mailer != NULL)
+ {
+ /* mailer name is unique, use it as address */
+ macdefine(&mci->mci_macro, A_PERM,
+ macid("{server_addr}"),
+ mci->mci_mailer->m_name);
+ }
+ else
+ {
+ /* don't set it to NULL or "": see getauth() */
+ macdefine(&mci->mci_macro, A_PERM,
+ macid("{server_addr}"), "0");
+ }
-# if STARTTLS || (SASL && SFIO)
-reconnect: /* after switching to an authenticated connection */
-# endif /* STARTTLS || (SASL && SFIO) */
+ /* undo change of srvname (mci->mci_host) */
+ if (dotpos >= 0)
+ srvname[dotpos] = '.';
+reconnect: /* after switching to an encrypted connection */
+# endif /* STARTTLS || SASL */
+
+ /* set the current connection information */
+ e->e_mci = mci;
# if SASL
mci->mci_saslcap = NULL;
# endif /* SASL */
smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags));
CLR_HELO(mci->mci_flags);
+ if (IS_DLVR_RETURN(e))
+ {
+ /*
+ ** Check whether other side can deliver e-mail
+ ** fast enough
+ */
+
+ if (!bitset(MCIF_DLVR_BY, mci->mci_flags))
+ {
+ e->e_status = "5.4.7";
+ usrerrenh(e->e_status,
+ "554 Server does not support Deliver By");
+ rcode = EX_UNAVAILABLE;
+ goto give_up;
+ }
+ if (e->e_deliver_by > 0 &&
+ e->e_deliver_by - (curtime() - e->e_ctime) <
+ mci->mci_min_by)
+ {
+ e->e_status = "5.4.7";
+ usrerrenh(e->e_status,
+ "554 Message can't be delivered in time; %ld < %ld",
+ e->e_deliver_by - (curtime() - e->e_ctime),
+ mci->mci_min_by);
+ rcode = EX_UNAVAILABLE;
+ goto give_up;
+ }
+ }
+
# if STARTTLS
/* first TLS then AUTH to provide a security layer */
if (mci->mci_state != MCIS_CLOSED &&
!DONE_STARTTLS(mci->mci_flags))
{
int olderrors;
- int dotpos;
bool usetls;
bool saveQuickAbort = QuickAbort;
bool saveSuprErrs = SuprErrs;
char *host = NULL;
-# if _FFR_TLS_CLT1
- char *p;
-# endif /* _FFR_TLS_CLT1 */
- char *srvname;
- extern SOCKADDR CurHostAddr;
rcode = EX_OK;
usetls = bitset(MCIF_TLS, mci->mci_flags);
-# if _FFR_TLS_CLT1
- if (usetls &&
- (p = macvalue(macid("{client_flags}", NULL), e))
- != NULL)
- {
- for (; *p != '\0'; p++)
- {
- /* look for just this one flag */
- if (*p == D_CLTNOTLS)
- {
- usetls = FALSE;
- break;
- }
- }
- }
-# endif /* _FFR_TLS_CLT1 */
+ if (usetls)
+ usetls = !iscltflgset(e, D_NOTLS);
- if (mci->mci_host != NULL)
- {
- srvname = mci->mci_host;
- dotpos = strlen(srvname) - 1;
- if (dotpos >= 0)
- {
- if (srvname[dotpos] == '.')
- srvname[dotpos] = '\0';
- else
- dotpos = -1;
- }
- }
- else
- {
- srvname = "";
- dotpos = -1;
- }
- define(macid("{server_name}", NULL),
- newstr(srvname), e);
- if (CurHostAddr.sa.sa_family != 0)
- define(macid("{server_addr}", NULL),
- newstr(anynet_ntoa(&CurHostAddr)), e);
- else
- define(macid("{server_addr}", NULL), NULL, e);
if (usetls)
{
- host = macvalue(macid("{server_name}", NULL),
- e);
-# if _FFR_TLS_O_T
+ host = macvalue(macid("{server_name}"), e);
olderrors = Errors;
- QuickAbort = FALSE;
- SuprErrs = TRUE;
- if (rscheck("try_tls", srvname, NULL,
- e, TRUE, FALSE, 8, host) != EX_OK
+ QuickAbort = false;
+ SuprErrs = true;
+ if (rscheck("try_tls", host, NULL, e, true,
+ false, 7, host, NOQID) != EX_OK
|| Errors > olderrors)
- usetls = FALSE;
+ usetls = false;
SuprErrs = saveSuprErrs;
QuickAbort = saveQuickAbort;
-# endif /* _FFR_TLS_O_T */
}
- /* undo change of srvname */
- if (dotpos >= 0)
- srvname[dotpos] = '.';
if (usetls)
{
if ((rcode = starttls(m, mci, e)) == EX_OK)
@@ -2421,6 +2879,7 @@ reconnect: /* after switching to an authenticated connection */
** or abort? How to decide?
** set a macro and call a ruleset.
*/
+
mci->mci_flags &= ~MCIF_TLS;
switch (rcode)
{
@@ -2442,21 +2901,16 @@ reconnect: /* after switching to an authenticated connection */
s = "FAILURE";
rcode = EX_TEMPFAIL;
}
- define(macid("{verify}", NULL),
- newstr(s), e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{verify}"), s);
}
}
- else if (mci->mci_ssl != NULL)
- {
- /* active TLS connection, use that data */
- (void) tls_get_info(mci->mci_ssl, e, FALSE,
- mci->mci_host, FALSE);
- }
else
- define(macid("{verify}", NULL), "NONE", e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{verify}"), "NONE");
olderrors = Errors;
- QuickAbort = FALSE;
- SuprErrs = TRUE;
+ QuickAbort = false;
+ SuprErrs = true;
/*
** rcode == EX_SOFTWARE is special:
@@ -2466,9 +2920,11 @@ reconnect: /* after switching to an authenticated connection */
** to log the problem and return an appropriate
** error code.
*/
+
if (rscheck("tls_server",
- macvalue(macid("{verify}", NULL), e),
- NULL, e, TRUE, TRUE, 6, host) != EX_OK ||
+ macvalue(macid("{verify}"), e),
+ NULL, e, true, true, 5, host,
+ NOQID) != EX_OK ||
Errors > olderrors ||
rcode == EX_SOFTWARE)
{
@@ -2478,13 +2934,14 @@ reconnect: /* after switching to an authenticated connection */
if (ISSMTPCODE(MsgBuf) &&
extenhsc(MsgBuf + 4, ' ', enhsc) > 0)
{
- p = newstr(MsgBuf);
+ p = sm_rpool_strdup_x(e->e_rpool,
+ MsgBuf);
}
else
{
p = "403 4.7.0 server not authenticated.";
- (void) strlcpy(enhsc, "4.7.0",
- sizeof enhsc);
+ (void) sm_strlcpy(enhsc, "4.7.0",
+ sizeof enhsc);
}
SuprErrs = saveSuprErrs;
QuickAbort = saveQuickAbort;
@@ -2495,7 +2952,8 @@ reconnect: /* after switching to an authenticated connection */
mci->mci_state = MCIS_QUITING;
if (mci->mci_in != NULL)
{
- (void) fclose(mci->mci_in);
+ (void) sm_io_close(mci->mci_in,
+ SM_TIME_DEFAULT);
mci->mci_in = NULL;
}
mci->mci_flags &= ~MCIF_TLSACT;
@@ -2513,13 +2971,15 @@ reconnect: /* after switching to an authenticated connection */
/* temp or permanent failure? */
rcode = (*p == '4') ? EX_TEMPFAIL
: EX_UNAVAILABLE;
- mci_setstat(mci, rcode, newstr(enhsc), p);
+ mci_setstat(mci, rcode, enhsc, p);
/*
** hack to get the error message into
** the envelope (done in giveresponse())
*/
- (void) strlcpy(SmtpError, p, sizeof SmtpError);
+
+ (void) sm_strlcpy(SmtpError, p,
+ sizeof SmtpError);
}
QuickAbort = saveQuickAbort;
SuprErrs = saveSuprErrs;
@@ -2531,79 +2991,85 @@ reconnect: /* after switching to an authenticated connection */
goto reconnect;
}
}
- else if (mci->mci_ssl != NULL)
- {
- /* active TLS connection, use that data */
- (void) tls_get_info(mci->mci_ssl, e, FALSE,
- mci->mci_host, FALSE);
- }
# endif /* STARTTLS */
# if SASL
/* if other server supports authentication let's authenticate */
if (mci->mci_state != MCIS_CLOSED &&
mci->mci_saslcap != NULL &&
-# if SFIO
- !DONE_AUTH(mci->mci_flags) &&
-# endif /* SFIO */
- SASLInfo != NULL)
+ !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH))
{
- /*
- ** should we require some minimum authentication?
- ** XXX ignore result?
- */
- if (smtpauth(m, mci, e) == EX_OK)
+ /* Should we require some minimum authentication? */
+ if ((ret = smtpauth(m, mci, e)) == EX_OK)
{
-# if SFIO
int result;
- sasl_ssf_t *ssf;
+ sasl_ssf_t *ssf = NULL;
- /* get security strength (features) */
+ /* Get security strength (features) */
result = sasl_getprop(mci->mci_conn, SASL_SSF,
(void **) &ssf);
+
+ /* XXX authid? */
if (LogLevel > 9)
sm_syslog(LOG_INFO, NOQID,
- "SASL: outgoing connection to %.64s: mech=%.16s, bits=%d",
+ "AUTH=client, relay=%.100s, mech=%.16s, bits=%d",
mci->mci_host,
- macvalue(macid("{auth_type}",
- NULL), e),
- result == SASL_OK ? *ssf
- : 0);
+ macvalue(macid("{auth_type}"), e),
+ result == SASL_OK ? *ssf : 0);
/*
- ** only switch to encrypted connection
+ ** Only switch to encrypted connection
** if a security layer has been negotiated
*/
+
if (result == SASL_OK && *ssf > 0)
{
/*
- ** convert sfio stuff to use SASL
- ** check return values
- ** if the call fails,
- ** fall back to unencrypted version
- ** unless some cf option requires
- ** encryption then the connection must
- ** be aborted
+ ** Convert I/O layer to use SASL.
+ ** If the call fails, the connection
+ ** is aborted.
*/
- if (sfdcsasl(mci->mci_in, mci->mci_out,
+
+ if (sfdcsasl(&mci->mci_in,
+ &mci->mci_out,
mci->mci_conn) == 0)
{
- SET_HELO(mci->mci_flags);
mci->mci_flags &= ~MCIF_EXTENS;
- mci->mci_flags |= MCIF_AUTHACT;
+ mci->mci_flags |= MCIF_AUTHACT|
+ MCIF_ONLY_EHLO;
goto reconnect;
}
- syserr("SASL TLS switch failed in client");
+ syserr("AUTH TLS switch failed in client");
}
/* else? XXX */
-# endif /* SFIO */
mci->mci_flags |= MCIF_AUTHACT;
}
+ else if (ret == EX_TEMPFAIL)
+ {
+ if (LogLevel > 8)
+ sm_syslog(LOG_ERR, NOQID,
+ "AUTH=client, relay=%.100s, temporary failure, connection abort",
+ mci->mci_host);
+ smtpquit(m, mci, e);
+
+ /* avoid bogus error msg */
+ mci->mci_errno = 0;
+ rcode = EX_TEMPFAIL;
+ mci_setstat(mci, rcode, "4.7.1", p);
+
+ /*
+ ** hack to get the error message into
+ ** the envelope (done in giveresponse())
+ */
+
+ (void) sm_strlcpy(SmtpError,
+ "Temporary AUTH failure",
+ sizeof SmtpError);
+ }
}
# endif /* SASL */
}
-#endif /* SMTP */
do_transfer:
/* clear out per-message flags from connection structure */
@@ -2618,13 +3084,13 @@ do_transfer:
if (bitnset(M_MAKE8BIT, m->m_flags) &&
!bitset(MCIF_7BIT, mci->mci_flags) &&
(p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
- (strcasecmp(p, "quoted-printable") == 0 ||
- strcasecmp(p, "base64") == 0) &&
+ (sm_strcasecmp(p, "quoted-printable") == 0 ||
+ sm_strcasecmp(p, "base64") == 0) &&
(p = hvalue("Content-Type", e->e_header)) != NULL)
{
/* may want to convert 7 -> 8 */
/* XXX should really parse it here -- and use a class XXX */
- if (strncasecmp(p, "text/plain", 10) == 0 &&
+ if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
(p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
mci->mci_flags |= MCIF_CVT7TO8;
}
@@ -2632,34 +3098,61 @@ do_transfer:
if (tTd(11, 1))
{
- dprintf("openmailer: ");
- mci_dump(mci, FALSE);
+ sm_dprintf("openmailer: ");
+ mci_dump(mci, false);
}
+#if _FFR_CLIENT_SIZE
+ /*
+ ** See if we know the maximum size and
+ ** abort if the message is too big.
+ **
+ ** NOTE: _FFR_CLIENT_SIZE is untested.
+ */
+
+ if (bitset(MCIF_SIZE, mci->mci_flags) &&
+ mci->mci_maxsize > 0 &&
+ e->e_msgsize > mci->mci_maxsize)
+ {
+ e->e_flags |= EF_NO_BODY_RETN;
+ if (bitnset(M_LOCALMAILER, m->m_flags))
+ e->e_status = "5.2.3";
+ else
+ e->e_status = "5.3.4";
+
+ usrerrenh(e->e_status,
+ "552 Message is too large; %ld bytes max",
+ mci->mci_maxsize);
+ rcode = EX_DATAERR;
+
+ /* Need an e_message for error */
+ (void) sm_snprintf(SmtpError, sizeof SmtpError,
+ "Message is too large; %ld bytes max",
+ mci->mci_maxsize);
+ goto give_up;
+ }
+#endif /* _FFR_CLIENT_SIZE */
+
if (mci->mci_state != MCIS_OPEN)
{
/* couldn't open the mailer */
rcode = mci->mci_exitstat;
errno = mci->mci_errno;
-#if NAMED_BIND
SM_SET_H_ERRNO(mci->mci_herrno);
-#endif /* NAMED_BIND */
if (rcode == EX_OK)
{
/* shouldn't happen */
syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s",
- (u_long) mci, rcode, errno, mci->mci_state,
- firstsig);
- mci_dump_all(TRUE);
+ (unsigned long) mci, rcode, errno,
+ mci->mci_state, firstsig);
+ mci_dump_all(true);
rcode = EX_SOFTWARE;
}
-#if DAEMON
else if (nummxhosts > hostnum)
{
/* try next MX site */
goto tryhost;
}
-#endif /* DAEMON */
}
else if (!clever)
{
@@ -2673,80 +3166,106 @@ do_transfer:
/* get the exit status */
rcode = endmailer(mci, e, pv);
- if (rcode == EX_TEMPFAIL &&
- SmtpError[0] == '\0')
+ if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0')
{
/*
** Need an e_message for mailq display.
** We set SmtpError as
*/
- snprintf(SmtpError, sizeof SmtpError,
- "%s mailer (%s) exited with EX_TEMPFAIL",
- m->m_name, m->m_mailer);
+ (void) sm_snprintf(SmtpError, sizeof SmtpError,
+ "%s mailer (%s) exited with EX_TEMPFAIL",
+ m->m_name, m->m_mailer);
}
}
else
-#if SMTP
{
/*
** Send the MAIL FROM: protocol
*/
+ /* XXX this isn't pipelined... */
rcode = smtpmailfrom(m, mci, e);
if (rcode == EX_OK)
{
- register char *t = tobuf;
register int i;
+# if PIPELINING
+ ADDRESS *volatile pchain;
+# endif /* PIPELINING */
/* send the recipient list */
tobuf[0] = '\0';
+ mci->mci_retryrcpt = false;
+ mci->mci_tolist = tobuf;
+# if PIPELINING
+ pchain = NULL;
+ mci->mci_nextaddr = NULL;
+# endif /* PIPELINING */
for (to = tochain; to != NULL; to = to->q_tchain)
{
- e->e_to = to->q_paddr;
-#if !_FFR_DYNAMIC_TOBUF
- if (strlen(to->q_paddr) +
- (t - tobuf) + 2 > sizeof tobuf)
- {
- /* not enough room */
+ if (!QS_IS_UNMARKED(to->q_state))
continue;
- }
-#endif /* !_FFR_DYNAMIC_TOBUF */
+ /* mark recipient state as "ok so far" */
+ to->q_state = QS_OK;
+ e->e_to = to->q_paddr;
# if STARTTLS
-# if _FFR_TLS_RCPT
i = rscheck("tls_rcpt", to->q_user, NULL, e,
- TRUE, TRUE, 4, mci->mci_host);
+ true, true, 3, mci->mci_host,
+ e->e_id);
if (i != EX_OK)
{
- /* avoid bogus error msg */
- errno = 0;
- markfailure(e, to, mci, i, FALSE);
- giveresponse(i, to->q_status, m,
- mci, ctladdr, xstart, e);
+ markfailure(e, to, mci, i, false);
+ giveresponse(i, to->q_status, m, mci,
+ ctladdr, xstart, e, to);
+ if (i == EX_TEMPFAIL)
+ {
+ mci->mci_retryrcpt = true;
+ to->q_state = QS_RETRY;
+ }
continue;
}
-# endif /* _FFR_TLS_RCPT */
# endif /* STARTTLS */
- if ((i = smtprcpt(to, m, mci, e)) != EX_OK)
+ i = smtprcpt(to, m, mci, e, ctladdr, xstart);
+# if PIPELINING
+ if (i == EX_OK &&
+ bitset(MCIF_PIPELINED, mci->mci_flags))
{
- markfailure(e, to, mci, i, FALSE);
- giveresponse(i, to->q_status, m,
- mci, ctladdr, xstart, e);
+ /*
+ ** Add new element to list of
+ ** recipients for pipelining.
+ */
+
+ to->q_pchain = NULL;
+ if (mci->mci_nextaddr == NULL)
+ mci->mci_nextaddr = to;
+ if (pchain == NULL)
+ pchain = to;
+ else
+ {
+ pchain->q_pchain = to;
+ pchain = pchain->q_pchain;
+ }
}
- else
+# endif /* PIPELINING */
+ if (i != EX_OK)
{
- *t++ = ',';
- for (p = to->q_paddr; *p; *t++ = *p++)
- continue;
- *t = '\0';
+ markfailure(e, to, mci, i, false);
+ giveresponse(i, to->q_status, m, mci,
+ ctladdr, xstart, e, to);
+ if (i == EX_TEMPFAIL)
+ to->q_state = QS_RETRY;
}
}
- /* now send the data */
- if (tobuf[0] == '\0')
+ /* No recipients in list and no missing responses? */
+ if (tobuf[0] == '\0'
+# if PIPELINING
+ && mci->mci_nextaddr == NULL
+# endif /* PIPELINING */
+ )
{
rcode = EX_OK;
e->e_to = NULL;
@@ -2756,24 +3275,15 @@ do_transfer:
else
{
e->e_to = tobuf + 1;
- rcode = smtpdata(m, mci, e);
+ rcode = smtpdata(m, mci, e, ctladdr, xstart);
}
}
-# if DAEMON
if (rcode == EX_TEMPFAIL && nummxhosts > hostnum)
{
/* try next MX site */
goto tryhost;
}
-# endif /* DAEMON */
- }
-#else /* SMTP */
- {
- syserr("554 5.3.5 deliver: need SMTP compiled to use clever mailer");
- rcode = EX_CONFIG;
- goto give_up;
}
-#endif /* SMTP */
#if NAMED_BIND
if (ConfigLevel < 2)
_res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */
@@ -2790,15 +3300,14 @@ do_transfer:
*/
give_up:
-#if SMTP
if (bitnset(M_LMTP, m->m_flags))
{
lmtp_rcode = rcode;
tobuf[0] = '\0';
- anyok = FALSE;
+ anyok = false;
+ strsize = 0;
}
else
-#endif /* SMTP */
anyok = rcode == EX_OK;
for (to = tochain; to != NULL; to = to->q_tchain)
@@ -2807,7 +3316,6 @@ do_transfer:
if (!QS_IS_OK(to->q_state))
continue;
-#if SMTP
/* if running LMTP, get the status for each address */
if (bitnset(M_LMTP, m->m_flags))
{
@@ -2815,44 +3323,30 @@ do_transfer:
rcode = smtpgetstat(m, mci, e);
if (rcode == EX_OK)
{
-#if _FFR_DYNAMIC_TOBUF
- (void) strlcat(tobuf, ",", tobufsize);
- (void) strlcat(tobuf, to->q_paddr, tobufsize);
-#else /* _FFR_DYNAMIC_TOBUF */
- if (strlen(to->q_paddr) +
- strlen(tobuf) + 2 > sizeof tobuf)
- {
- syserr("LMTP tobuf overflow");
- }
- else
- {
- (void) strlcat(tobuf, ",",
- sizeof tobuf);
- (void) strlcat(tobuf, to->q_paddr,
- sizeof tobuf);
- }
-#endif /* _FFR_DYNAMIC_TOBUF */
- anyok = TRUE;
+ strsize += sm_strlcat2(tobuf + strsize, ",",
+ to->q_paddr,
+ tobufsize - strsize);
+ SM_ASSERT(strsize < tobufsize);
+ anyok = true;
}
else
{
e->e_to = to->q_paddr;
- markfailure(e, to, mci, rcode, TRUE);
+ markfailure(e, to, mci, rcode, true);
giveresponse(rcode, to->q_status, m, mci,
- ctladdr, xstart, e);
+ ctladdr, xstart, e, to);
e->e_to = tobuf + 1;
continue;
}
}
else
-#endif /* SMTP */
{
/* mark bad addresses */
if (rcode != EX_OK)
{
if (goodmxfound && rcode == EX_NOHOST)
rcode = EX_TEMPFAIL;
- markfailure(e, to, mci, rcode, TRUE);
+ markfailure(e, to, mci, rcode, true);
continue;
}
}
@@ -2862,37 +3356,63 @@ do_transfer:
to->q_statdate = curtime();
e->e_nsent++;
-#if QUEUE
/*
** Checkpoint the send list every few addresses
*/
if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval)
{
- queueup(e, FALSE);
+ queueup(e, false, false);
e->e_nsent = 0;
}
-#endif /* QUEUE */
if (bitnset(M_LOCALMAILER, m->m_flags) &&
bitset(QPINGONSUCCESS, to->q_flags))
{
to->q_flags |= QDELIVERED;
to->q_status = "2.1.5";
- fprintf(e->e_xfp, "%s... Successfully delivered\n",
- to->q_paddr);
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "%s... Successfully delivered\n",
+ to->q_paddr);
}
else if (bitset(QPINGONSUCCESS, to->q_flags) &&
bitset(QPRIMARY, to->q_flags) &&
!bitset(MCIF_DSN, mci->mci_flags))
{
to->q_flags |= QRELAYED;
- fprintf(e->e_xfp, "%s... relayed; expect no further notifications\n",
- to->q_paddr);
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "%s... relayed; expect no further notifications\n",
+ to->q_paddr);
+ }
+ else if (IS_DLVR_NOTIFY(e) &&
+ !bitset(MCIF_DLVR_BY, mci->mci_flags) &&
+ bitset(QPRIMARY, to->q_flags) &&
+ (!bitset(QHASNOTIFY, to->q_flags) ||
+ bitset(QPINGONSUCCESS, to->q_flags) ||
+ bitset(QPINGONFAILURE, to->q_flags) ||
+ bitset(QPINGONDELAY, to->q_flags)))
+ {
+ /* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */
+ to->q_flags |= QBYNRELAY;
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "%s... Deliver-by notify: relayed\n",
+ to->q_paddr);
+ }
+ else if (IS_DLVR_TRACE(e) &&
+ (!bitset(QHASNOTIFY, to->q_flags) ||
+ bitset(QPINGONSUCCESS, to->q_flags) ||
+ bitset(QPINGONFAILURE, to->q_flags) ||
+ bitset(QPINGONDELAY, to->q_flags)) &&
+ bitset(QPRIMARY, to->q_flags))
+ {
+ /* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */
+ to->q_flags |= QBYTRACE;
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "%s... Deliver-By trace: relayed\n",
+ to->q_paddr);
}
}
-#if SMTP
if (bitnset(M_LMTP, m->m_flags))
{
/*
@@ -2904,48 +3424,90 @@ do_transfer:
e->e_statmsg = NULL;
/* reset the mci state for the next transaction */
- if (mci != NULL && mci->mci_state == MCIS_ACTIVE)
+ if (mci != NULL &&
+ (mci->mci_state == MCIS_MAIL ||
+ mci->mci_state == MCIS_RCPT ||
+ mci->mci_state == MCIS_DATA))
mci->mci_state = MCIS_OPEN;
}
-#endif /* SMTP */
if (tobuf[0] != '\0')
- giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e);
+ {
+ giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain);
+#if 0
+ /*
+ ** This code is disabled for now because I am not
+ ** sure that copying status from the first recipient
+ ** to all non-status'ed recipients is a good idea.
+ */
+
+ if (tochain->q_message != NULL &&
+ !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK)
+ {
+ for (to = tochain->q_tchain; to != NULL;
+ to = to->q_tchain)
+ {
+ /* see if address already marked */
+ if (QS_IS_QUEUEUP(to->q_state) &&
+ to->q_message == NULL)
+ to->q_message = sm_rpool_strdup_x(e->e_rpool,
+ tochain->q_message);
+ }
+ }
+#endif /* 0 */
+ }
if (anyok)
- markstats(e, tochain, FALSE);
+ markstats(e, tochain, STATS_NORMAL);
mci_store_persistent(mci);
-#if SMTP
+ /* Some recipients were tempfailed, try them on the next host */
+ if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum)
+ {
+ /* try next MX site */
+ goto tryhost;
+ }
+
/* now close the connection */
if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED &&
!bitset(MCIF_CACHED, mci->mci_flags))
smtpquit(m, mci, e);
-#endif /* SMTP */
-
- /*
- ** Restore state and return.
- */
-#if XDEBUG
+cleanup: ;
+ }
+ SM_FINALLY
{
+ /*
+ ** Restore state and return.
+ */
+#if XDEBUG
char wbuf[MAXLINE];
/* make absolutely certain 0, 1, and 2 are in use */
- snprintf(wbuf, sizeof wbuf, "%s... end of deliver(%s)",
- e->e_to == NULL ? "NO-TO-LIST"
- : shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name);
+ (void) sm_snprintf(wbuf, sizeof wbuf,
+ "%s... end of deliver(%s)",
+ e->e_to == NULL ? "NO-TO-LIST"
+ : shortenstring(e->e_to,
+ MAXSHORTSTR),
+ m->m_name);
checkfd012(wbuf);
- }
#endif /* XDEBUG */
- errno = 0;
- define('g', (char *) NULL, e);
- e->e_to = NULL;
+ errno = 0;
+
+ /*
+ ** It was originally necessary to set macro 'g' to NULL
+ ** because it previously pointed to an auto buffer.
+ ** We don't do this any more, so this may be unnecessary.
+ */
+
+ macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL);
+ e->e_to = NULL;
+ }
+ SM_END_TRY
return rcode;
}
- /*
+/*
** MARKFAILURE -- mark a failure on a specific address.
**
** Parameters:
@@ -2964,7 +3526,7 @@ do_transfer:
** the message will be queued, as appropriate.
*/
-static void
+void
markfailure(e, q, mci, rcode, ovr)
register ENVELOPE *e;
register ADDRESS *q;
@@ -2972,6 +3534,7 @@ markfailure(e, q, mci, rcode, ovr)
int rcode;
bool ovr;
{
+ int save_errno = errno;
char *status = NULL;
char *rstatus = NULL;
@@ -2994,9 +3557,10 @@ markfailure(e, q, mci, rcode, ovr)
/* find most specific error code possible */
if (mci != NULL && mci->mci_status != NULL)
{
- status = mci->mci_status;
+ status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status);
if (mci->mci_rstatus != NULL)
- rstatus = newstr(mci->mci_rstatus);
+ rstatus = sm_rpool_strdup_x(e->e_rpool,
+ mci->mci_rstatus);
else
rstatus = NULL;
}
@@ -3059,20 +3623,23 @@ markfailure(e, q, mci, rcode, ovr)
}
if (rcode != EX_OK && q->q_rstatus == NULL &&
q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL &&
- strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0)
+ sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0)
{
char buf[16];
- (void) snprintf(buf, sizeof buf, "%d", rcode);
- q->q_rstatus = newstr(buf);
+ (void) sm_snprintf(buf, sizeof buf, "%d", rcode);
+ q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf);
}
q->q_statdate = curtime();
if (CurHostName != NULL && CurHostName[0] != '\0' &&
mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags))
- q->q_statmta = newstr(CurHostName);
+ q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName);
+
+ /* restore errno */
+ errno = save_errno;
}
- /*
+/*
** ENDMAILER -- Wait for mailer to terminate.
**
** We should never get fatal errors (e.g., segmentation
@@ -3117,26 +3684,26 @@ endmailer(mci, e, pv)
int st;
int save_errno = errno;
char buf[MAXLINE];
- EVENT *ev = NULL;
+ SM_EVENT *ev = NULL;
mci_unlock_host(mci);
/* close output to mailer */
if (mci->mci_out != NULL)
- (void) fclose(mci->mci_out);
+ (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
/* copy any remaining input to transcript */
if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR &&
e->e_xfp != NULL)
{
while (sfgets(buf, sizeof buf, mci->mci_in,
- TimeOuts.to_quit, "Draining Input") != NULL)
- (void) fputs(buf, e->e_xfp);
+ TimeOuts.to_quit, "Draining Input") != NULL)
+ (void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf);
}
#if SASL
- /* shutdown SASL */
+ /* close SASL connection */
if (bitset(MCIF_AUTHACT, mci->mci_flags))
{
sasl_dispose(&mci->mci_conn);
@@ -3151,7 +3718,7 @@ endmailer(mci, e, pv)
/* now close the input */
if (mci->mci_in != NULL)
- (void) fclose(mci->mci_in);
+ (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
mci->mci_in = mci->mci_out = NULL;
mci->mci_state = MCIS_CLOSED;
@@ -3165,8 +3732,8 @@ endmailer(mci, e, pv)
if (mci->mci_mailer->m_wait > 0)
{
if (setjmp(EndWaitTimeout) == 0)
- ev = setevent(mci->mci_mailer->m_wait,
- endwaittimeout, 0);
+ ev = sm_setevent(mci->mci_mailer->m_wait,
+ endwaittimeout, 0);
else
{
syserr("endmailer %s: wait timeout (%ld)",
@@ -3180,7 +3747,7 @@ endmailer(mci, e, pv)
st = waitfor(mci->mci_pid);
save_errno = errno;
if (ev != NULL)
- clrevent(ev);
+ sm_clrevent(ev);
errno = save_errno;
if (st == -1)
@@ -3206,16 +3773,17 @@ endmailer(mci, e, pv)
{
register char **av;
- fprintf(e->e_xfp, "Arguments:");
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:");
for (av = pv; *av != NULL; av++)
- fprintf(e->e_xfp, " %s", *av);
- fprintf(e->e_xfp, "\n");
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s",
+ *av);
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n");
}
ExitStat = EX_TEMPFAIL;
return EX_TEMPFAIL;
}
- /*
+/*
** GIVERESPONSE -- Interpret an error response from a mailer
**
** Parameters:
@@ -3231,6 +3799,7 @@ endmailer(mci, e, pv)
** xstart -- the transaction start time, for computing
** transaction delays.
** e -- the current envelope.
+** to -- the current recipient (NULL if none).
**
** Returns:
** none.
@@ -3241,7 +3810,7 @@ endmailer(mci, e, pv)
*/
void
-giveresponse(status, dsn, m, mci, ctladdr, xstart, e)
+giveresponse(status, dsn, m, mci, ctladdr, xstart, e, to)
int status;
char *dsn;
register MAILER *m;
@@ -3249,15 +3818,15 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e)
ADDRESS *ctladdr;
time_t xstart;
ENVELOPE *e;
+ ADDRESS *to;
{
register const char *statmsg;
- extern char *SysExMsg[];
- register int i;
int errnum = errno;
int off = 4;
- extern int N_SysEx;
+ bool usestat = false;
char dsnbuf[ENHSCLEN];
char buf[MAXLINE];
+ char *exmsg;
if (e == NULL)
syserr("giveresponse: null envelope");
@@ -3266,48 +3835,43 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e)
** Compute status message from code.
*/
- i = status - EX__BASE;
+ exmsg = sm_sysexmsg(status);
if (status == 0)
{
statmsg = "250 2.0.0 Sent";
if (e->e_statmsg != NULL)
{
- (void) snprintf(buf, sizeof buf, "%s (%s)",
- statmsg,
- shortenstring(e->e_statmsg, 403));
+ (void) sm_snprintf(buf, sizeof buf, "%s (%s)",
+ statmsg,
+ shortenstring(e->e_statmsg, 403));
statmsg = buf;
}
}
- else if (i < 0 || i >= N_SysEx)
+ else if (exmsg == NULL)
{
- (void) snprintf(buf, sizeof buf,
- "554 5.3.0 unknown mailer error %d",
- status);
+ (void) sm_snprintf(buf, sizeof buf,
+ "554 5.3.0 unknown mailer error %d",
+ status);
status = EX_UNAVAILABLE;
statmsg = buf;
+ usestat = true;
}
else if (status == EX_TEMPFAIL)
{
char *bp = buf;
- snprintf(bp, SPACELEFT(buf, bp), "%s", SysExMsg[i] + 1);
+ (void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp));
bp += strlen(bp);
#if NAMED_BIND
if (h_errno == TRY_AGAIN)
- statmsg = errstring(h_errno+E_DNSBASE);
+ statmsg = sm_errstring(h_errno + E_DNSBASE);
else
#endif /* NAMED_BIND */
{
if (errnum != 0)
- statmsg = errstring(errnum);
+ statmsg = sm_errstring(errnum);
else
- {
-#if SMTP
statmsg = SmtpError;
-#else /* SMTP */
- statmsg = NULL;
-#endif /* SMTP */
- }
}
if (statmsg != NULL && statmsg[0] != '\0')
{
@@ -3333,33 +3897,39 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e)
#endif /* EHOSTUNREACH */
if (mci->mci_host != NULL)
{
- snprintf(bp, SPACELEFT(buf, bp),
- ": %s", mci->mci_host);
+ (void) sm_strlcpyn(bp,
+ SPACELEFT(buf, bp),
+ 2, ": ",
+ mci->mci_host);
bp += strlen(bp);
}
break;
}
- snprintf(bp, SPACELEFT(buf, bp), ": %s", statmsg);
+ (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ",
+ statmsg);
+ usestat = true;
}
statmsg = buf;
}
#if NAMED_BIND
else if (status == EX_NOHOST && h_errno != 0)
{
- statmsg = errstring(h_errno + E_DNSBASE);
- (void) snprintf(buf, sizeof buf, "%s (%s)",
- SysExMsg[i] + 1, statmsg);
+ statmsg = sm_errstring(h_errno + E_DNSBASE);
+ (void) sm_snprintf(buf, sizeof buf, "%s (%s)", exmsg + 1,
+ statmsg);
statmsg = buf;
+ usestat = true;
}
#endif /* NAMED_BIND */
else
{
- statmsg = SysExMsg[i];
+ statmsg = exmsg;
if (*statmsg++ == ':' && errnum != 0)
{
- (void) snprintf(buf, sizeof buf, "%s: %s",
- statmsg, errstring(errnum));
+ (void) sm_snprintf(buf, sizeof buf, "%s: %s", statmsg,
+ sm_errstring(errnum));
statmsg = buf;
+ usestat = true;
}
}
@@ -3375,8 +3945,8 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e)
{
if (dsn == NULL)
{
- snprintf(dsnbuf, sizeof dsnbuf,
- "%.*s", off, statmsg + 4);
+ (void) sm_snprintf(dsnbuf, sizeof dsnbuf,
+ "%.*s", off, statmsg + 4);
dsn = dsnbuf;
}
off += 5;
@@ -3387,7 +3957,8 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e)
}
message("%s", statmsg + off);
if (status == EX_TEMPFAIL && e->e_xfp != NULL)
- fprintf(e->e_xfp, "%s\n", &MsgBuf[4]);
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n",
+ &MsgBuf[4]);
}
else
{
@@ -3399,18 +3970,21 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e)
{
if (dsn == NULL)
{
- snprintf(dsnbuf, sizeof dsnbuf,
- "%.*s", off, statmsg + 4);
+ (void) sm_snprintf(dsnbuf, sizeof dsnbuf,
+ "%.*s", off, statmsg + 4);
dsn = dsnbuf;
}
off += 5;
- (void) strlcpy(mbuf, statmsg, off);
- (void) strlcat(mbuf, " %s", sizeof mbuf);
+
+ /* copy only part of statmsg to mbuf */
+ (void) sm_strlcpy(mbuf, statmsg, off);
+ (void) sm_strlcat(mbuf, " %s", sizeof mbuf);
}
else
{
dsnbuf[0] = '\0';
- (void) snprintf(mbuf, sizeof mbuf, "%.3s %%s", statmsg);
+ (void) sm_snprintf(mbuf, sizeof mbuf, "%.3s %%s",
+ statmsg);
off = 4;
}
usrerr(mbuf, &statmsg[off]);
@@ -3428,25 +4002,29 @@ giveresponse(status, dsn, m, mci, ctladdr, xstart, e)
logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e);
if (tTd(11, 2))
- dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s\n",
- status,
- dsn == NULL ? "<NULL>" : dsn,
- e->e_message == NULL ? "<NULL>" : e->e_message);
+ sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n",
+ status,
+ dsn == NULL ? "<NULL>" : dsn,
+ e->e_message == NULL ? "<NULL>" : e->e_message,
+ errnum);
if (status != EX_TEMPFAIL)
setstat(status);
if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL))
+ e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off);
+ if (status != EX_OK && to != NULL && to->q_message == NULL)
{
- if (e->e_message != NULL)
- sm_free(e->e_message);
- e->e_message = newstr(statmsg + off);
+ if (!usestat && e->e_message != NULL)
+ to->q_message = sm_rpool_strdup_x(e->e_rpool,
+ e->e_message);
+ else
+ to->q_message = sm_rpool_strdup_x(e->e_rpool,
+ statmsg + off);
}
errno = 0;
-#if NAMED_BIND
SM_SET_H_ERRNO(0);
-#endif /* NAMED_BIND */
}
- /*
+/*
** LOGDELIVERY -- log the delivery in the system log
**
** Care is taken to avoid logging lines that are too long, because
@@ -3484,7 +4062,7 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e)
register char *bp;
register char *p;
int l;
- time_t now;
+ time_t now = curtime();
char buf[1024];
#if (SYSLOG_BUFSIZE) >= 256
@@ -3492,68 +4070,74 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e)
bp = buf;
if (ctladdr != NULL)
{
- snprintf(bp, SPACELEFT(buf, bp), ", ctladdr=%s",
- shortenstring(ctladdr->q_paddr, 83));
+ (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=",
+ shortenstring(ctladdr->q_paddr, 83));
bp += strlen(bp);
if (bitset(QGOODUID, ctladdr->q_flags))
{
- (void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
- (int) ctladdr->q_uid,
- (int) ctladdr->q_gid);
+ (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
+ (int) ctladdr->q_uid,
+ (int) ctladdr->q_gid);
bp += strlen(bp);
}
}
/* delay & xdelay: max 41 bytes */
- now = curtime();
- snprintf(bp, SPACELEFT(buf, bp), ", delay=%s",
- pintvl(now - e->e_ctime, TRUE));
+ (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=",
+ pintvl(now - e->e_ctime, true));
bp += strlen(bp);
if (xstart != (time_t) 0)
{
- snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s",
- pintvl(now - xstart, TRUE));
+ (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
+ pintvl(now - xstart, true));
bp += strlen(bp);
}
/* mailer: assume about 19 bytes (max 10 byte mailer name) */
if (m != NULL)
{
- snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", m->m_name);
+ (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
+ m->m_name);
bp += strlen(bp);
}
/* pri: changes with each delivery attempt */
- snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld", e->e_msgpriority);
+ (void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld",
+ e->e_msgpriority);
bp += strlen(bp);
/* relay: max 66 bytes for IPv4 addresses */
if (mci != NULL && mci->mci_host != NULL)
{
-# if DAEMON
extern SOCKADDR CurHostAddr;
-# endif /* DAEMON */
- snprintf(bp, SPACELEFT(buf, bp), ", relay=%s",
- shortenstring(mci->mci_host, 40));
+ (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=",
+ shortenstring(mci->mci_host, 40));
bp += strlen(bp);
-# if DAEMON
if (CurHostAddr.sa.sa_family != 0)
{
- snprintf(bp, SPACELEFT(buf, bp), " [%s]",
- anynet_ntoa(&CurHostAddr));
+ (void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]",
+ anynet_ntoa(&CurHostAddr));
}
-# endif /* DAEMON */
}
+#if _FFR_QUARANTINE
+ else if (strcmp(status, "quarantined") == 0)
+ {
+ if (e->e_quarmsg != NULL)
+ (void) sm_snprintf(bp, SPACELEFT(buf, bp),
+ ", quarantine=%s",
+ shortenstring(e->e_quarmsg, 40));
+ }
+#endif /* _FFR_QUARANTINE */
else if (strcmp(status, "queued") != 0)
{
p = macvalue('h', e);
if (p != NULL && p[0] != '\0')
{
- snprintf(bp, SPACELEFT(buf, bp), ", relay=%s",
- shortenstring(p, 40));
+ (void) sm_snprintf(bp, SPACELEFT(buf, bp),
+ ", relay=%s", shortenstring(p, 40));
}
}
bp += strlen(bp);
@@ -3561,8 +4145,8 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e)
/* dsn */
if (dsn != NULL && *dsn != '\0')
{
- snprintf(bp, SPACELEFT(buf, bp), ", dsn=%s",
- shortenstring(dsn, ENHSCLEN));
+ (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=",
+ shortenstring(dsn, ENHSCLEN));
bp += strlen(bp);
}
@@ -3581,23 +4165,25 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e)
{
/* desperation move -- truncate data */
bp = buf + sizeof buf - ((STATLEN) + 17);
- (void) strlcpy(bp, "...", SPACELEFT(buf, bp));
+ (void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp));
bp += 3;
}
- (void) strlcpy(bp, ", stat=", SPACELEFT(buf, bp));
+ (void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp));
bp += strlen(bp);
- (void) strlcpy(bp, shortenstring(status, STATLEN), SPACELEFT(buf, bp));
+ (void) sm_strlcpy(bp, shortenstring(status, STATLEN),
+ SPACELEFT(buf, bp));
/* id, to: max 13 + TOBUFSIZE bytes */
l = SYSLOG_BUFSIZE - 100 - strlen(buf);
+ if (l < 0)
+ l = 0;
p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
- while (strlen(p) >= (SIZE_T) l)
+ while (strlen(p) >= l)
{
register char *q;
-#if _FFR_DYNAMIC_TOBUF
for (q = p + l; q > p; q--)
{
if (*q == ',')
@@ -3605,32 +4191,22 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e)
}
if (p == q)
break;
-#else /* _FFR_DYNAMIC_TOBUF */
- q = strchr(p + l, ',');
- if (q == NULL)
- break;
-#endif /* _FFR_DYNAMIC_TOBUF */
-
- sm_syslog(LOG_INFO, e->e_id,
- "to=%.*s [more]%s",
+ sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s",
(int) (++q - p), p, buf);
p = q;
}
-#if _FFR_DYNAMIC_TOBUF
sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf);
-#else /* _FFR_DYNAMIC_TOBUF */
- sm_syslog(LOG_INFO, e->e_id, "to=%s%s", p, buf);
-#endif /* _FFR_DYNAMIC_TOBUF */
#else /* (SYSLOG_BUFSIZE) >= 256 */
l = SYSLOG_BUFSIZE - 85;
+ if (l < 0)
+ l = 0;
p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
- while (strlen(p) >= (SIZE_T) l)
+ while (strlen(p) >= l)
{
register char *q;
-#if _FFR_DYNAMIC_TOBUF
for (q = p + l; q > p; q--)
{
if (*q == ',')
@@ -3638,51 +4214,42 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e)
}
if (p == q)
break;
-#else /* _FFR_DYNAMIC_TOBUF */
- q = strchr(p + l, ',');
- if (q == NULL)
- break;
-#endif /* _FFR_DYNAMIC_TOBUF */
- sm_syslog(LOG_INFO, e->e_id,
- "to=%.*s [more]",
+ sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]",
(int) (++q - p), p);
p = q;
}
-#if _FFR_DYNAMIC_TOBUF
sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p);
-#else /* _FFR_DYNAMIC_TOBUF */
- sm_syslog(LOG_INFO, e->e_id, "to=%s", p);
-#endif /* _FFR_DYNAMIC_TOBUF */
if (ctladdr != NULL)
{
bp = buf;
- snprintf(bp, SPACELEFT(buf, bp), "ctladdr=%s",
- shortenstring(ctladdr->q_paddr, 83));
+ (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=",
+ shortenstring(ctladdr->q_paddr, 83));
bp += strlen(bp);
if (bitset(QGOODUID, ctladdr->q_flags))
{
- (void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
- ctladdr->q_uid, ctladdr->q_gid);
+ (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
+ ctladdr->q_uid, ctladdr->q_gid);
bp += strlen(bp);
}
sm_syslog(LOG_INFO, e->e_id, "%s", buf);
}
bp = buf;
- snprintf(bp, SPACELEFT(buf, bp), "delay=%s",
- pintvl(now - e->e_ctime, TRUE));
+ (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=",
+ pintvl(now - e->e_ctime, true));
bp += strlen(bp);
if (xstart != (time_t) 0)
{
- snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s",
- pintvl(now - xstart, TRUE));
+ (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
+ pintvl(now - xstart, true));
bp += strlen(bp);
}
if (m != NULL)
{
- snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", m->m_name);
+ (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
+ m->m_name);
bp += strlen(bp);
}
sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
@@ -3691,24 +4258,31 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e)
bp = buf;
if (mci != NULL && mci->mci_host != NULL)
{
-# if DAEMON
extern SOCKADDR CurHostAddr;
-# endif /* DAEMON */
- snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", mci->mci_host);
+ (void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s",
+ mci->mci_host);
bp += strlen(bp);
-# if DAEMON
if (CurHostAddr.sa.sa_family != 0)
- snprintf(bp, SPACELEFT(buf, bp), " [%.100s]",
- anynet_ntoa(&CurHostAddr));
-# endif /* DAEMON */
+ (void) sm_snprintf(bp, SPACELEFT(buf, bp),
+ " [%.100s]",
+ anynet_ntoa(&CurHostAddr));
+ }
+#if _FFR_QUARANTINE
+ else if (strcmp(status, "quarantined") == 0)
+ {
+ if (e->e_quarmsg != NULL)
+ (void) sm_snprintf(bp, SPACELEFT(buf, bp),
+ ", quarantine=%.100s",
+ e->e_quarmsg);
}
+#endif /* _FFR_QUARANTINE */
else if (strcmp(status, "queued") != 0)
{
p = macvalue('h', e);
if (p != NULL && p[0] != '\0')
- snprintf(buf, sizeof buf, "relay=%.100s", p);
+ (void) sm_snprintf(buf, sizeof buf, "relay=%.100s", p);
}
if (buf[0] != '\0')
sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
@@ -3716,7 +4290,7 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e)
sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63));
#endif /* (SYSLOG_BUFSIZE) >= 256 */
}
- /*
+/*
** PUTFROMLINE -- output a UNIX-style from line (or whatever)
**
** This can be made an arbitrary message separator by changing $l
@@ -3774,23 +4348,23 @@ putfromline(mci, e)
}
else
*at++ = '\0';
- (void) snprintf(xbuf, sizeof xbuf,
- "From %.800s \201d remote from %.100s\n",
- buf, at);
+ (void) sm_snprintf(xbuf, sizeof xbuf,
+ "From %.800s \201d remote from %.100s\n",
+ buf, at);
}
else
{
*bang++ = '\0';
- (void) snprintf(xbuf, sizeof xbuf,
- "From %.800s \201d remote from %.100s\n",
- bang, buf);
+ (void) sm_snprintf(xbuf, sizeof xbuf,
+ "From %.800s \201d remote from %.100s\n",
+ bang, buf);
template = xbuf;
}
}
expand(template, buf, sizeof buf, e);
putxline(buf, strlen(buf), mci, PXLF_HEADER);
}
- /*
+/*
** PUTBODY -- put the body of a message.
**
** Parameters:
@@ -3817,9 +4391,11 @@ putbody(mci, e, separator)
register ENVELOPE *e;
char *separator;
{
- bool dead = FALSE;
+ bool dead = false;
char buf[MAXLINE];
+#if MIME8TO7
char *boundaries[MAXMIMENESTING + 1];
+#endif /* MIME8TO7 */
/*
** Output the body of the message
@@ -3827,9 +4403,10 @@ putbody(mci, e, separator)
if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
{
- char *df = queuename(e, 'd');
+ char *df = queuename(e, DATAFL_LETTER);
- e->e_dfp = fopen(df, "r");
+ e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
+ SM_IO_RDONLY, NULL);
if (e->e_dfp == NULL)
{
char *msg = "!putbody: Cannot open %s for %s from %s";
@@ -3838,6 +4415,7 @@ putbody(mci, e, separator)
msg++;
syserr(msg, df, e->e_to, e->e_from.q_paddr);
}
+
}
if (e->e_dfp == NULL)
{
@@ -3854,7 +4432,8 @@ putbody(mci, e, separator)
{
struct stat stbuf;
- if (fstat(fileno(e->e_dfp), &stbuf) < 0)
+ if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf)
+ < 0)
e->e_dfino = -1;
else
{
@@ -3863,7 +4442,7 @@ putbody(mci, e, separator)
}
}
- /* paranoia: the df file should always be in a rewound state */
+ /* paranoia: the data file should always be in a rewound state */
(void) bfrewind(e->e_dfp);
#if MIME8TO7
@@ -3879,9 +4458,9 @@ putbody(mci, e, separator)
if (hvalue("Content-Type", e->e_header) == NULL)
{
- snprintf(buf, sizeof buf,
- "Content-Type: text/plain; charset=%s",
- defcharset(e));
+ (void) sm_snprintf(buf, sizeof buf,
+ "Content-Type: text/plain; charset=%s",
+ defcharset(e));
putline(buf, mci);
}
@@ -3913,7 +4492,7 @@ putbody(mci, e, separator)
*/
if (bitset(EF_DONT_MIME, e->e_flags))
- SuprErrs = TRUE;
+ SuprErrs = true;
(void) mime8to7(mci, e->e_header, e, boundaries,
M87F_OUTER|M87F_NO8TO7);
@@ -3950,22 +4529,22 @@ putbody(mci, e, separator)
ostate = OS_HEAD;
bp = buf;
pbp = peekbuf;
- while (!ferror(mci->mci_out) && !dead)
+ while (!sm_io_error(mci->mci_out) && !dead)
{
if (pbp > peekbuf)
c = *--pbp;
- else if ((c = getc(e->e_dfp)) == EOF)
+ else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT))
+ == SM_IO_EOF)
break;
if (bitset(MCIF_7BIT, mci->mci_flags))
c &= 0x7f;
switch (ostate)
{
case OS_HEAD:
-#if _FFR_NONULLS
if (c == '\0' &&
- bitnset(M_NONULLS, mci->mci_mailer->m_flags))
+ bitnset(M_NONULLS,
+ mci->mci_mailer->m_flags))
break;
-#endif /* _FFR_NONULLS */
if (c != '\r' && c != '\n' && bp < buflim)
{
*bp++ = c;
@@ -3975,10 +4554,10 @@ putbody(mci, e, separator)
/* check beginning of line for special cases */
*bp = '\0';
pos = 0;
- padc = EOF;
+ padc = SM_IO_EOF;
if (buf[0] == 'F' &&
- bitnset(M_ESCFROM, mci->mci_mailer->m_flags) &&
- strncmp(buf, "From ", 5) == 0)
+ bitnset(M_ESCFROM, mci->mci_mailer->m_flags)
+ && strncmp(buf, "From ", 5) == 0)
{
padc = '>';
}
@@ -3988,7 +4567,8 @@ putbody(mci, e, separator)
/* possible separator */
int sl = strlen(separator);
- if (strncmp(&buf[2], separator, sl) == 0)
+ if (strncmp(&buf[2], separator, sl)
+ == 0)
padc = ' ';
}
if (buf[0] == '.' &&
@@ -4000,57 +4580,68 @@ putbody(mci, e, separator)
/* now copy out saved line */
if (TrafficLogFile != NULL)
{
- fprintf(TrafficLogFile, "%05d >>> ",
- (int) getpid());
- if (padc != EOF)
- (void) putc(padc,
- TrafficLogFile);
+ (void) sm_io_fprintf(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ "%05d >>> ",
+ (int) CurrentPid);
+ if (padc != SM_IO_EOF)
+ (void) sm_io_putc(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ padc);
for (xp = buf; xp < bp; xp++)
- (void) putc((unsigned char) *xp,
- TrafficLogFile);
+ (void) sm_io_putc(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ (unsigned char) *xp);
if (c == '\n')
- (void) fputs(mci->mci_mailer->m_eol,
- TrafficLogFile);
+ (void) sm_io_fputs(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ mci->mci_mailer->m_eol);
}
- if (padc != EOF)
+ if (padc != SM_IO_EOF)
{
- if (putc(padc, mci->mci_out) == EOF)
+ if (sm_io_putc(mci->mci_out,
+ SM_TIME_DEFAULT, padc)
+ == SM_IO_EOF)
{
- dead = TRUE;
+ dead = true;
continue;
}
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
pos++;
}
for (xp = buf; xp < bp; xp++)
{
- if (putc((unsigned char) *xp,
- mci->mci_out) == EOF)
+ if (sm_io_putc(mci->mci_out,
+ SM_TIME_DEFAULT,
+ (unsigned char) *xp)
+ == SM_IO_EOF)
{
- dead = TRUE;
+ dead = true;
break;
}
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
}
if (dead)
continue;
if (c == '\n')
{
- if (fputs(mci->mci_mailer->m_eol,
- mci->mci_out) == EOF)
+ if (sm_io_fputs(mci->mci_out,
+ SM_TIME_DEFAULT,
+ mci->mci_mailer->m_eol)
+ == SM_IO_EOF)
break;
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
pos = 0;
}
@@ -4076,19 +4667,22 @@ putbody(mci, e, separator)
if (c == '\n')
{
/* got CRLF */
- if (fputs(mci->mci_mailer->m_eol,
- mci->mci_out) == EOF)
+ if (sm_io_fputs(mci->mci_out,
+ SM_TIME_DEFAULT,
+ mci->mci_mailer->m_eol)
+ == SM_IO_EOF)
continue;
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
if (TrafficLogFile != NULL)
{
- (void) fputs(mci->mci_mailer->m_eol,
- TrafficLogFile);
+ (void) sm_io_fputs(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ mci->mci_mailer->m_eol);
}
ostate = OS_HEAD;
continue;
@@ -4106,11 +4700,10 @@ putbody(mci, e, separator)
ostate = OS_CR;
continue;
}
-#if _FFR_NONULLS
if (c == '\0' &&
- bitnset(M_NONULLS, mci->mci_mailer->m_flags))
+ bitnset(M_NONULLS,
+ mci->mci_mailer->m_flags))
break;
-#endif /* _FFR_NONULLS */
putch:
if (mci->mci_mailer->m_linelimit > 0 &&
pos >= mci->mci_mailer->m_linelimit - 1 &&
@@ -4121,46 +4714,57 @@ putch:
/* check next character for EOL */
if (pbp > peekbuf)
d = *(pbp - 1);
- else if ((d = getc(e->e_dfp)) != EOF)
+ else if ((d = sm_io_getc(e->e_dfp,
+ SM_TIME_DEFAULT))
+ != SM_IO_EOF)
*pbp++ = d;
- if (d == '\n' || d == EOF)
+ if (d == '\n' || d == SM_IO_EOF)
{
if (TrafficLogFile != NULL)
- (void) putc((unsigned char) c,
- TrafficLogFile);
- if (putc((unsigned char) c,
- mci->mci_out) == EOF)
+ (void) sm_io_putc(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ (unsigned char) c);
+ if (sm_io_putc(mci->mci_out,
+ SM_TIME_DEFAULT,
+ (unsigned char) c)
+ == SM_IO_EOF)
{
- dead = TRUE;
+ dead = true;
continue;
}
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
pos++;
continue;
}
- if (putc('!', mci->mci_out) == EOF ||
- fputs(mci->mci_mailer->m_eol,
- mci->mci_out) == EOF)
+ if (sm_io_putc(mci->mci_out,
+ SM_TIME_DEFAULT, '!')
+ == SM_IO_EOF ||
+ sm_io_fputs(mci->mci_out,
+ SM_TIME_DEFAULT,
+ mci->mci_mailer->m_eol)
+ == SM_IO_EOF)
{
- dead = TRUE;
+ dead = true;
continue;
}
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
if (TrafficLogFile != NULL)
{
- fprintf(TrafficLogFile, "!%s",
- mci->mci_mailer->m_eol);
+ (void) sm_io_fprintf(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ "!%s",
+ mci->mci_mailer->m_eol);
}
ostate = OS_HEAD;
*pbp++ = c;
@@ -4169,15 +4773,18 @@ putch:
if (c == '\n')
{
if (TrafficLogFile != NULL)
- (void) fputs(mci->mci_mailer->m_eol,
- TrafficLogFile);
- if (fputs(mci->mci_mailer->m_eol,
- mci->mci_out) == EOF)
+ (void) sm_io_fputs(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ mci->mci_mailer->m_eol);
+ if (sm_io_fputs(mci->mci_out,
+ SM_TIME_DEFAULT,
+ mci->mci_mailer->m_eol)
+ == SM_IO_EOF)
continue;
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
pos = 0;
ostate = OS_HEAD;
@@ -4185,18 +4792,21 @@ putch:
else
{
if (TrafficLogFile != NULL)
- (void) putc((unsigned char) c,
- TrafficLogFile);
- if (putc((unsigned char) c,
- mci->mci_out) == EOF)
+ (void) sm_io_putc(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ (unsigned char) c);
+ if (sm_io_putc(mci->mci_out,
+ SM_TIME_DEFAULT,
+ (unsigned char) c)
+ == SM_IO_EOF)
{
- dead = TRUE;
+ dead = true;
continue;
}
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
pos++;
ostate = OS_INLINE;
@@ -4211,21 +4821,23 @@ putch:
if (TrafficLogFile != NULL)
{
for (xp = buf; xp < bp; xp++)
- (void) putc((unsigned char) *xp,
- TrafficLogFile);
+ (void) sm_io_putc(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ (unsigned char) *xp);
}
for (xp = buf; xp < bp; xp++)
{
- if (putc((unsigned char) *xp, mci->mci_out) ==
- EOF)
+ if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
+ (unsigned char) *xp)
+ == SM_IO_EOF)
{
- dead = TRUE;
+ dead = true;
break;
}
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
}
pos += bp - buf;
@@ -4233,19 +4845,22 @@ putch:
if (!dead && pos > 0)
{
if (TrafficLogFile != NULL)
- (void) fputs(mci->mci_mailer->m_eol,
- TrafficLogFile);
- (void) fputs(mci->mci_mailer->m_eol, mci->mci_out);
+ (void) sm_io_fputs(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ mci->mci_mailer->m_eol);
+ (void) sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
+ mci->mci_mailer->m_eol);
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
}
- if (ferror(e->e_dfp))
+ if (sm_io_error(e->e_dfp))
{
- syserr("putbody: %s/df%s: read error",
- qid_printqueue(e->e_queuedir), e->e_id);
+ syserr("putbody: %s/%cf%s: read error",
+ qid_printqueue(e->e_dfqgrp, e->e_dfqdir),
+ DATAFL_LETTER, e->e_id);
ExitStat = EX_IOERR;
}
@@ -4269,8 +4884,8 @@ endofmessage:
buf[0] != '\0' && buf[0] != '\n')
putline("", mci);
- (void) fflush(mci->mci_out);
- if (ferror(mci->mci_out) && errno != EPIPE)
+ (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
+ if (sm_io_error(mci->mci_out) && errno != EPIPE)
{
syserr("putbody: write error");
ExitStat = EX_IOERR;
@@ -4278,11 +4893,11 @@ endofmessage:
errno = 0;
}
- /*
+/*
** MAILFILE -- Send a message to a file.
**
-** If the file has the setuid/setgid bits set, but NO execute
-** bits, sendmail will try to become the owner of that file
+** If the file has the set-user-ID/set-group-ID bits set, but NO
+** execute bits, sendmail will try to become the owner of that file
** rather than the real user. Obviously, this only works if
** sendmail runs as root.
**
@@ -4308,6 +4923,8 @@ endofmessage:
** none.
*/
+# define RETURN(st) exit(st);
+
static jmp_buf CtxMailfileTimeout;
int
@@ -4318,7 +4935,7 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
volatile long sfflags;
register ENVELOPE *e;
{
- register FILE *f;
+ register SM_FILE_T *f;
register pid_t pid = -1;
volatile int mode;
int len;
@@ -4326,28 +4943,28 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
bool suidwarn = geteuid() == 0;
char *p;
char *volatile realfile;
- EVENT *ev;
+ SM_EVENT *ev;
char buf[MAXLINE + 1];
char targetfile[MAXPATHLEN + 1];
if (tTd(11, 1))
{
- dprintf("mailfile %s\n ctladdr=", filename);
- printaddr(ctladdr, FALSE);
+ sm_dprintf("mailfile %s\n ctladdr=", filename);
+ printaddr(ctladdr, false);
}
if (mailer == NULL)
mailer = FileMailer;
if (e->e_xfp != NULL)
- (void) fflush(e->e_xfp);
+ (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
/*
** Special case /dev/null. This allows us to restrict file
** delivery to regular files only.
*/
- if (strcmp(filename, "/dev/null") == 0)
+ if (sm_path_isdevnull(filename))
return EX_OK;
/* check for 8-bit available */
@@ -4360,7 +4977,8 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
{
e->e_status = "5.6.3";
usrerrenh(e->e_status,
- "554 Cannot send 8-bit data to 7-bit destination");
+ "554 Cannot send 8-bit data to 7-bit destination");
+ errno = 0;
return EX_DATAERR;
}
@@ -4372,19 +4990,19 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
if (strncmp(SafeFileEnv, filename, len) == 0)
filename += len;
- if (len + strlen(filename) + 1 > MAXPATHLEN)
+ if (len + strlen(filename) + 1 >= sizeof targetfile)
{
syserr("mailfile: filename too long (%s/%s)",
SafeFileEnv, filename);
return EX_CANTCREAT;
}
- (void) strlcpy(targetfile, SafeFileEnv, sizeof targetfile);
+ (void) sm_strlcpy(targetfile, SafeFileEnv, sizeof targetfile);
realfile = targetfile + len;
if (targetfile[len - 1] != '/')
- (void) strlcat(targetfile, "/", sizeof targetfile);
+ (void) sm_strlcat(targetfile, "/", sizeof targetfile);
if (*filename == '/')
filename++;
- (void) strlcat(targetfile, filename, sizeof targetfile);
+ (void) sm_strlcat(targetfile, filename, sizeof targetfile);
}
else if (mailer->m_rootdir != NULL)
{
@@ -4394,7 +5012,7 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
if (strncmp(targetfile, filename, len) == 0)
filename += len;
- if (len + strlen(filename) + 1 > MAXPATHLEN)
+ if (len + strlen(filename) + 1 >= sizeof targetfile)
{
syserr("mailfile: filename too long (%s/%s)",
targetfile, filename);
@@ -4402,21 +5020,22 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
}
realfile = targetfile + len;
if (targetfile[len - 1] != '/')
- (void) strlcat(targetfile, "/", sizeof targetfile);
+ (void) sm_strlcat(targetfile, "/", sizeof targetfile);
if (*filename == '/')
- (void) strlcat(targetfile, filename + 1,
- sizeof targetfile);
+ (void) sm_strlcat(targetfile, filename + 1,
+ sizeof targetfile);
else
- (void) strlcat(targetfile, filename, sizeof targetfile);
+ (void) sm_strlcat(targetfile, filename,
+ sizeof targetfile);
}
else
{
- if (strlen(filename) > MAXPATHLEN)
+ if (sm_strlcpy(targetfile, filename, sizeof targetfile) >=
+ sizeof targetfile)
{
syserr("mailfile: filename too long (%s)", filename);
return EX_CANTCREAT;
}
- (void) strlcpy(targetfile, filename, sizeof targetfile);
realfile = targetfile;
}
@@ -4440,31 +5059,34 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
/* Reset global flags */
RestartRequest = NULL;
+ RestartWorkGroup = false;
ShutdownRequest = NULL;
PendingSignal = 0;
+ CurrentPid = getpid();
if (e->e_lockfp != NULL)
- (void) close(fileno(e->e_lockfp));
+ (void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
+ NULL));
- (void) setsignal(SIGINT, SIG_DFL);
- (void) setsignal(SIGHUP, SIG_DFL);
- (void) setsignal(SIGTERM, SIG_DFL);
+ (void) sm_signal(SIGINT, SIG_DFL);
+ (void) sm_signal(SIGHUP, SIG_DFL);
+ (void) sm_signal(SIGTERM, SIG_DFL);
(void) umask(OldUmask);
e->e_to = filename;
ExitStat = EX_OK;
if (setjmp(CtxMailfileTimeout) != 0)
{
- exit(EX_TEMPFAIL);
+ RETURN(EX_TEMPFAIL);
}
if (TimeOuts.to_fileopen > 0)
- ev = setevent(TimeOuts.to_fileopen,
- mailfiletimeout, 0);
+ ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout,
+ 0);
else
ev = NULL;
- /* check file mode to see if setuid */
+ /* check file mode to see if set-user-ID */
if (stat(targetfile, &stb) < 0)
mode = FileMode;
else
@@ -4478,18 +5100,19 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) ||
bitset(SFF_RUNASREALUID, sfflags))
{
- /* ignore setuid and setgid bits */
+ /* ignore set-user-ID and set-group-ID bits */
mode &= ~(S_ISGID|S_ISUID);
if (tTd(11, 20))
- dprintf("mailfile: ignoring setuid/setgid bits\n");
+ sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n");
}
- /* we have to open the dfile BEFORE setuid */
+ /* we have to open the data file BEFORE setuid() */
if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
{
- char *df = queuename(e, 'd');
+ char *df = queuename(e, DATAFL_LETTER);
- e->e_dfp = fopen(df, "r");
+ e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
+ SM_IO_RDONLY, NULL);
if (e->e_dfp == NULL)
{
syserr("mailfile: Cannot open %s for %s from %s",
@@ -4507,8 +5130,9 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
if (RunAsUid != 0 && RealUid != RunAsUid)
{
/* Only root can change the uid */
- syserr("mailfile: insufficient privileges to change uid");
- exit(EX_TEMPFAIL);
+ syserr("mailfile: insufficient privileges to change uid, RunAsUid=%d, RealUid=%d",
+ (int) RunAsUid, (int) RealUid);
+ RETURN(EX_TEMPFAIL);
}
}
else if (bitset(S_ISUID, mode))
@@ -4544,8 +5168,10 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
RealGid != getegid()))
{
/* Only root can change the gid */
- syserr("mailfile: insufficient privileges to change gid");
- exit(EX_TEMPFAIL);
+ syserr("mailfile: insufficient privileges to change gid, RealGid=%d, RunAsUid=%d, gid=%d, egid=%d",
+ (int) RealGid, (int) RunAsUid,
+ (int) getgid(), (int) getegid());
+ RETURN(EX_TEMPFAIL);
}
}
else if (bitset(S_ISGID, mode))
@@ -4587,7 +5213,7 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
{
syserr("mailfile: initgroups(%s, %d) failed",
RealUserName, RealGid);
- exit(EX_TEMPFAIL);
+ RETURN(EX_TEMPFAIL);
}
}
else
@@ -4598,7 +5224,7 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
if (setgroups(1, gidset) == -1 && suidwarn)
{
syserr("mailfile: setgroups() failed");
- exit(EX_TEMPFAIL);
+ RETURN(EX_TEMPFAIL);
}
}
@@ -4610,41 +5236,42 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
{
*realfile = '\0';
if (tTd(11, 20))
- dprintf("mailfile: chroot %s\n", targetfile);
+ sm_dprintf("mailfile: chroot %s\n", targetfile);
if (chroot(targetfile) < 0)
{
syserr("mailfile: Cannot chroot(%s)",
targetfile);
- exit(EX_CANTCREAT);
+ RETURN(EX_CANTCREAT);
}
*realfile = '/';
}
if (tTd(11, 40))
- dprintf("mailfile: deliver to %s\n", realfile);
+ sm_dprintf("mailfile: deliver to %s\n", realfile);
if (chdir("/") < 0)
{
syserr("mailfile: cannot chdir(/)");
- exit(EX_CANTCREAT);
+ RETURN(EX_CANTCREAT);
}
/* now reset the group and user ids */
endpwent();
+ sm_mbdb_terminate();
if (setgid(RealGid) < 0 && suidwarn)
{
syserr("mailfile: setgid(%ld) failed", (long) RealGid);
- exit(EX_TEMPFAIL);
+ RETURN(EX_TEMPFAIL);
}
vendor_set_uid(RealUid);
if (setuid(RealUid) < 0 && suidwarn)
{
syserr("mailfile: setuid(%ld) failed", (long) RealUid);
- exit(EX_TEMPFAIL);
+ RETURN(EX_TEMPFAIL);
}
if (tTd(11, 2))
- dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n",
+ sm_dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n",
(int) getuid(), (int) geteuid(),
(int) getgid(), (int) getegid());
@@ -4663,7 +5290,8 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
if (q != NULL)
*q++ = ':';
if (tTd(11, 20))
- dprintf("mailfile: trydir %s\n", buf);
+ sm_dprintf("mailfile: trydir %s\n",
+ buf);
if (buf[0] != '\0' && chdir(buf) >= 0)
break;
}
@@ -4713,32 +5341,34 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
{
usrerr("454 4.3.0 cannot open %s: %s",
shortenstring(realfile, MAXSHORTSTR),
- errstring(errno));
- exit(EX_TEMPFAIL);
+ sm_errstring(errno));
+ RETURN(EX_TEMPFAIL);
}
else
{
usrerr("554 5.3.0 cannot open %s: %s",
shortenstring(realfile, MAXSHORTSTR),
- errstring(errno));
- exit(EX_CANTCREAT);
+ sm_errstring(errno));
+ RETURN(EX_CANTCREAT);
}
}
- if (filechanged(realfile, fileno(f), &stb))
+ if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
+ &stb))
{
syserr("554 5.3.0 file changed after open");
- exit(EX_CANTCREAT);
+ RETURN(EX_CANTCREAT);
}
- if (fstat(fileno(f), &stb) < 0)
+ if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0)
{
- syserr("554 5.3.0 cannot fstat %s", errstring(errno));
- exit(EX_CANTCREAT);
+ syserr("554 5.3.0 cannot fstat %s",
+ sm_errstring(errno));
+ RETURN(EX_CANTCREAT);
}
curoff = stb.st_size;
if (ev != NULL)
- clrevent(ev);
+ sm_clrevent(ev);
memset(&mcibuf, '\0', sizeof mcibuf);
mcibuf.mci_mailer = mailer;
@@ -4758,13 +5388,13 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
if (bitnset(M_MAKE8BIT, mailer->m_flags) &&
!bitset(MCIF_7BIT, mcibuf.mci_flags) &&
(p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
- (strcasecmp(p, "quoted-printable") == 0 ||
- strcasecmp(p, "base64") == 0) &&
+ (sm_strcasecmp(p, "quoted-printable") == 0 ||
+ sm_strcasecmp(p, "base64") == 0) &&
(p = hvalue("Content-Type", e->e_header)) != NULL)
{
/* may want to convert 7 -> 8 */
/* XXX should really parse it here -- and use a class XXX */
- if (strncasecmp(p, "text/plain", 10) == 0 &&
+ if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
(p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
mcibuf.mci_flags |= MCIF_CVT7TO8;
}
@@ -4774,25 +5404,28 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER);
(*e->e_putbody)(&mcibuf, e, NULL);
putline("\n", &mcibuf);
- if (fflush(f) != 0 ||
- (SuperSafe && fsync(fileno(f)) < 0) ||
- ferror(f))
+ if (sm_io_flush(f, SM_TIME_DEFAULT) != 0 ||
+ (SuperSafe != SAFE_NO &&
+ fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) ||
+ sm_io_error(f))
{
setstat(EX_IOERR);
#if !NOFTRUNCATE
- (void) ftruncate(fileno(f), curoff);
+ (void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
+ curoff);
#endif /* !NOFTRUNCATE */
}
/* reset ISUID & ISGID bits for paranoid systems */
#if HASFCHMOD
- (void) fchmod(fileno(f), (MODE_T) mode);
+ (void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
+ (MODE_T) mode);
#else /* HASFCHMOD */
(void) chmod(filename, (MODE_T) mode);
#endif /* HASFCHMOD */
- if (fclose(f) < 0)
+ if (sm_io_close(f, SM_TIME_DEFAULT) < 0)
setstat(EX_IOERR);
- (void) fflush(stdout);
+ (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
(void) setuid(RealUid);
exit(ExitStat);
/* NOTREACHED */
@@ -4809,7 +5442,10 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
return EX_SOFTWARE;
}
if (WIFEXITED(st))
+ {
+ errno = 0;
return (WEXITSTATUS(st));
+ }
else
{
syserr("mailfile: %s: child died on signal %d",
@@ -4833,7 +5469,7 @@ mailfiletimeout()
errno = ETIMEDOUT;
longjmp(CtxMailfileTimeout, 1);
}
- /*
+/*
** HOSTSIGNATURE -- return the "signature" for a host.
**
** The signature describes how we are going to send this -- it
@@ -4850,15 +5486,17 @@ mailfiletimeout()
** Side Effects:
** Can tweak the symbol table.
*/
+
#define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */
-static char *
+char *
hostsignature(m, host)
register MAILER *m;
char *host;
{
register char *p;
register STAB *s;
+ time_t now;
#if NAMED_BIND
char sep = ':';
char prevsep = ':';
@@ -4866,35 +5504,33 @@ hostsignature(m, host)
int len;
int nmx;
int hl;
- time_t now;
char *hp;
char *endp;
int oldoptions = _res.options;
char *mxhosts[MAXMXHOSTS + 1];
- u_short mxprefs[MAXMXHOSTS + 1];
+ unsigned short mxprefs[MAXMXHOSTS + 1];
#endif /* NAMED_BIND */
if (tTd(17, 3))
- dprintf("hostsignature(%s)\n", host);
+ sm_dprintf("hostsignature(%s)\n", host);
/*
** If local delivery (and not remote), just return a constant.
*/
- p = m->m_mailer;
if (bitnset(M_LOCALMAILER, m->m_flags) &&
- strcmp(p, "[IPC]") != 0 &&
- strcmp(p, "[TCP]") != 0)
+ strcmp(m->m_mailer, "[IPC]") != 0 &&
+ !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0))
return "localhost";
/*
** Check to see if this uses IPC -- if not, it can't have MX records.
*/
- if (strcmp(p, "[IPC]") != 0 &&
- strcmp(p, "[TCP]") != 0)
+ if (strcmp(m->m_mailer, "[IPC]") != 0 ||
+ CurEnv->e_sendmode == SM_DEFER)
{
- /* just an ordinary mailer */
+ /* just an ordinary mailer or deferred mode */
return host;
}
#if NETUNIX
@@ -4910,24 +5546,34 @@ hostsignature(m, host)
** Look it up in the symbol table.
*/
+ now = curtime();
s = stab(host, ST_HOSTSIG, ST_ENTER);
- if (s->s_hostsig != NULL)
+ if (s->s_hostsig.hs_sig != NULL)
{
- if (tTd(17, 3))
- dprintf("hostsignature(): stab(%s) found %s\n", host,
- s->s_hostsig);
- return s->s_hostsig;
+ if (s->s_hostsig.hs_exp >= now)
+ {
+ if (tTd(17, 3))
+ sm_dprintf("hostsignature(): stab(%s) found %s\n", host,
+ s->s_hostsig.hs_sig);
+ return s->s_hostsig.hs_sig;
+ }
+
+ /* signature is expired: clear it */
+ sm_free(s->s_hostsig.hs_sig);
+ s->s_hostsig.hs_sig = NULL;
}
+ /* set default TTL */
+ s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL;
+
/*
- ** Not already there -- create a signature.
+ ** Not already there or expired -- create a signature.
*/
#if NAMED_BIND
if (ConfigLevel < 2)
_res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */
- now = curtime();
for (hp = host; hp != NULL; hp = endp)
{
#if NETINET6
@@ -4957,8 +5603,10 @@ hostsignature(m, host)
else
{
auto int rcode;
+ int ttl;
- nmx = getmxrr(hp, mxhosts, mxprefs, TRUE, &rcode);
+ nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true,
+ &ttl);
if (nmx <= 0)
{
int save_errno;
@@ -4972,7 +5620,7 @@ hostsignature(m, host)
mci->mci_lastuse = now;
if (rcode == EX_NOHOST)
mci_setstat(mci, rcode, "5.1.2",
- "550 Host unknown");
+ "550 Host unknown");
else
mci_setstat(mci, rcode, NULL, NULL);
@@ -4981,34 +5629,41 @@ hostsignature(m, host)
mxhosts[0] = hp;
}
if (tTd(17, 3))
- dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n",
- nmx, mxhosts[0]);
+ sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n",
+ nmx, mxhosts[0]);
+
+ /*
+ ** Set new TTL: we use only one!
+ ** We could try to use the minimum instead.
+ */
+
+ s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL);
}
len = 0;
for (i = 0; i < nmx; i++)
len += strlen(mxhosts[i]) + 1;
- if (s->s_hostsig != NULL)
- len += strlen(s->s_hostsig) + 1;
- if (len >= MAXHOSTSIGNATURE)
+ if (s->s_hostsig.hs_sig != NULL)
+ len += strlen(s->s_hostsig.hs_sig) + 1;
+ if (len < 0 || len >= MAXHOSTSIGNATURE)
{
sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d",
host, MAXHOSTSIGNATURE, len);
len = MAXHOSTSIGNATURE;
}
- p = xalloc(len);
- if (s->s_hostsig != NULL)
+ p = sm_pmalloc_x(len);
+ if (s->s_hostsig.hs_sig != NULL)
{
- (void) strlcpy(p, s->s_hostsig, len);
- sm_free(s->s_hostsig);
- s->s_hostsig = p;
+ (void) sm_strlcpy(p, s->s_hostsig.hs_sig, len);
+ sm_free(s->s_hostsig.hs_sig); /* XXX */
+ s->s_hostsig.hs_sig = p;
hl = strlen(p);
p += hl;
*p++ = prevsep;
len -= hl + 1;
}
else
- s->s_hostsig = p;
+ s->s_hostsig.hs_sig = p;
for (i = 0; i < nmx; i++)
{
hl = strlen(mxhosts[i]);
@@ -5026,7 +5681,7 @@ hostsignature(m, host)
*p++ = ':';
len--;
}
- (void) strlcpy(p, mxhosts[i], len);
+ (void) sm_strlcpy(p, mxhosts[i], len);
p += hl;
len -= hl;
}
@@ -5043,18 +5698,22 @@ hostsignature(m, host)
*endp++ = sep;
prevsep = sep;
}
- makelower(s->s_hostsig);
+ makelower(s->s_hostsig.hs_sig);
if (ConfigLevel < 2)
_res.options = oldoptions;
#else /* NAMED_BIND */
/* not using BIND -- the signature is just the host name */
- s->s_hostsig = host;
+ /*
+ ** 'host' points to storage that will be freed after we are
+ ** done processing the current envelope, so we copy it.
+ */
+ s->s_hostsig.hs_sig = sm_pstrdup_x(host);
#endif /* NAMED_BIND */
if (tTd(17, 1))
- dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig);
- return s->s_hostsig;
+ sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig);
+ return s->s_hostsig.hs_sig;
}
- /*
+/*
** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array.
**
** The signature describes how we are going to send this -- it
@@ -5065,6 +5724,7 @@ hostsignature(m, host)
** Parameters:
** sig -- the host signature.
** mxhosts -- array to populate.
+** mailer -- mailer.
**
** Returns:
** The number of hosts inserted into mxhosts array.
@@ -5079,11 +5739,10 @@ parse_hostsignature(sig, mxhosts, mailer)
char **mxhosts;
MAILER *mailer;
{
- int nmx = 0;
- int curpref = 0;
- int i, j;
+ unsigned short curpref = 0;
+ int nmx = 0, i, j; /* NOTE: i, j, and nmx must have same type */
char *hp, *endp;
- u_short prefer[MAXMXHOSTS];
+ unsigned short prefer[MAXMXHOSTS];
long rndm[MAXMXHOSTS];
for (hp = sig; hp != NULL; hp = endp)
@@ -5149,7 +5808,7 @@ parse_hostsignature(sig, mxhosts, mailer)
if (prefer[i] > prefer[j] ||
(prefer[i] == prefer[j] && rndm[i] > rndm[j]))
{
- register u_short tempp;
+ register unsigned short tempp;
register long tempr;
register char *temp1;
@@ -5168,30 +5827,60 @@ parse_hostsignature(sig, mxhosts, mailer)
return nmx;
}
-#if SMTP
# if STARTTLS
static SSL_CTX *clt_ctx = NULL;
+static bool tls_ok_clt = true;
- /*
-** INITCLTTLS -- initialize client side TLS
+/*
+** SETCLTTLS -- client side TLS: allow/disallow.
**
** Parameters:
+** tls_ok -- should tls be done?
+**
+** Returns:
** none.
**
+** Side Effects:
+** sets tls_ok_clt (static variable in this module)
+*/
+
+void
+setclttls(tls_ok)
+ bool tls_ok;
+{
+ tls_ok_clt = tls_ok;
+ return;
+}
+/*
+** INITCLTTLS -- initialize client side TLS
+**
+** Parameters:
+** tls_ok -- should tls initialization be done?
+**
** Returns:
** succeeded?
+**
+** Side Effects:
+** sets tls_ok_clt (static variable in this module)
*/
bool
-initclttls()
+initclttls(tls_ok)
+ bool tls_ok;
{
+ if (!tls_ok_clt)
+ return false;
+ tls_ok_clt = tls_ok;
+ if (!tls_ok_clt)
+ return false;
if (clt_ctx != NULL)
- return TRUE; /* already done */
- return inittls(&clt_ctx, TLS_I_CLT, FALSE, CltCERTfile, Cltkeyfile,
- CACERTpath, CACERTfile, DHParams);
+ return true; /* already done */
+ tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, false, CltCERTfile,
+ Cltkeyfile, CACERTpath, CACERTfile, DHParams);
+ return tls_ok_clt;
}
- /*
+/*
** STARTTLS -- try to start secure connection (client side)
**
** Parameters:
@@ -5215,14 +5904,14 @@ starttls(m, mci, e)
int result = 0;
int rfd, wfd;
SSL *clt_ssl = NULL;
+ time_t tlsstart;
- if (clt_ctx == NULL && !initclttls())
+ if (clt_ctx == NULL && !initclttls(true))
return EX_TEMPFAIL;
smtpmessage("STARTTLS", m, mci);
/* get the reply */
- smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, NULL, NULL);
- /* which timeout? XXX */
+ smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL);
/* check return code from server */
if (smtpresult == 454)
@@ -5235,78 +5924,127 @@ starttls(m, mci, e)
return EX_PROTOCOL;
if (LogLevel > 13)
- sm_syslog(LOG_INFO, e->e_id, "TLS: start client");
+ sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok");
/* start connection */
if ((clt_ssl = SSL_new(clt_ctx)) == NULL)
{
if (LogLevel > 5)
{
- sm_syslog(LOG_ERR, e->e_id,
- "TLS: error: client: SSL_new failed");
+ sm_syslog(LOG_ERR, NOQID,
+ "STARTTLS=client, error: SSL_new failed");
if (LogLevel > 9)
- tlslogerr();
+ tlslogerr("client");
}
return EX_SOFTWARE;
}
- rfd = fileno(mci->mci_in);
- wfd = fileno(mci->mci_out);
+ rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL);
+ wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL);
/* SSL_clear(clt_ssl); ? */
if (rfd < 0 || wfd < 0 ||
- (result = SSL_set_rfd(clt_ssl, rfd)) <= 0 ||
- (result = SSL_set_wfd(clt_ssl, wfd)) <= 0)
+ (result = SSL_set_rfd(clt_ssl, rfd)) != 1 ||
+ (result = SSL_set_wfd(clt_ssl, wfd)) != 1)
{
if (LogLevel > 5)
{
- sm_syslog(LOG_ERR, e->e_id,
- "TLS: error: SSL_set_xfd failed=%d", result);
+ sm_syslog(LOG_ERR, NOQID,
+ "STARTTLS=client, error: SSL_set_xfd failed=%d",
+ result);
if (LogLevel > 9)
- tlslogerr();
+ tlslogerr("client");
}
return EX_SOFTWARE;
}
SSL_set_connect_state(clt_ssl);
+ tlsstart = curtime();
+
+ssl_retry:
if ((result = SSL_connect(clt_ssl)) <= 0)
{
int i;
+ bool timedout;
+ time_t left;
+ time_t now = curtime();
+ struct timeval tv;
/* what to do in this case? */
i = SSL_get_error(clt_ssl, result);
+
+ /*
+ ** For SSL_ERROR_WANT_{READ,WRITE}:
+ ** There is not a complete SSL record available yet
+ ** or there is only a partial SSL record removed from
+ ** the network (socket) buffer into the SSL buffer.
+ ** The SSL_connect will only succeed when a full
+ ** SSL record is available (assuming a "real" error
+ ** doesn't happen). To handle when a "real" error
+ ** does happen the select is set for exceptions too.
+ ** The connection may be re-negotiated during this time
+ ** so both read and write "want errors" need to be handled.
+ ** A select() exception loops back so that a proper SSL
+ ** error message can be gotten.
+ */
+
+ left = TimeOuts.to_starttls - (now - tlsstart);
+ timedout = left <= 0;
+ if (!timedout)
+ {
+ tv.tv_sec = left;
+ tv.tv_usec = 0;
+ }
+
+ if (!timedout && i == SSL_ERROR_WANT_READ)
+ {
+ fd_set ssl_maskr, ssl_maskx;
+
+ FD_ZERO(&ssl_maskr);
+ FD_SET(rfd, &ssl_maskr);
+ FD_ZERO(&ssl_maskx);
+ FD_SET(rfd, &ssl_maskx);
+ if (select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx, &tv)
+ > 0)
+ goto ssl_retry;
+ }
+ if (!timedout && i == SSL_ERROR_WANT_WRITE)
+ {
+ fd_set ssl_maskw, ssl_maskx;
+
+ FD_ZERO(&ssl_maskw);
+ FD_SET(wfd, &ssl_maskw);
+ FD_ZERO(&ssl_maskx);
+ FD_SET(rfd, &ssl_maskx);
+ if (select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx, &tv)
+ > 0)
+ goto ssl_retry;
+ }
if (LogLevel > 5)
{
sm_syslog(LOG_ERR, e->e_id,
- "TLS: error: SSL_connect failed=%d (%d)",
- result, i);
- if (LogLevel > 9)
- tlslogerr();
+ "STARTTLS=client, error: connect failed=%d, SSL_error=%d, timedout=%d",
+ result, i, (int) timedout);
+ if (LogLevel > 8)
+ tlslogerr("client");
}
SSL_free(clt_ssl);
clt_ssl = NULL;
return EX_SOFTWARE;
}
mci->mci_ssl = clt_ssl;
- result = tls_get_info(clt_ssl, e, FALSE, mci->mci_host, TRUE);
+ result = tls_get_info(mci->mci_ssl, false, mci->mci_host,
+ &mci->mci_macro, true);
- /* switch to use SSL... */
-#if SFIO
- if (sfdctls(mci->mci_in, mci->mci_out, mci->mci_ssl) == 0)
- return EX_OK;
-#else /* SFIO */
-# if _FFR_TLS_TOREK
+ /* switch to use TLS... */
if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0)
return EX_OK;
-# endif /* _FFR_TLS_TOREK */
-#endif /* SFIO */
/* failure */
SSL_free(clt_ssl);
clt_ssl = NULL;
return EX_SOFTWARE;
}
-
- /*
+/*
** ENDTLSCLT -- shutdown secure connection (client side)
**
** Parameters:
@@ -5315,7 +6053,8 @@ starttls(m, mci, e)
** Returns:
** success?
*/
-int
+
+static int
endtlsclt(mci)
MCI *mci;
{
@@ -5327,48 +6066,35 @@ endtlsclt(mci)
mci->mci_flags &= ~MCIF_TLSACT;
return r;
}
- /*
-** ENDTLS -- shutdown secure connection
+# endif /* STARTTLS */
+# if STARTTLS || SASL
+/*
+** ISCLTFLGSET -- check whether client flag is set.
**
** Parameters:
-** ssl -- SSL connection information.
-** side -- srv/clt (for logging).
+** e -- envelope.
+** flag -- flag to check in {client_flags}
**
** Returns:
-** success?
+** true iff flag is set.
*/
-int
-endtls(ssl, side)
- SSL *ssl;
- char *side;
+static bool
+iscltflgset(e, flag)
+ ENVELOPE *e;
+ int flag;
{
- int ret = EX_OK;
+ char *p;
- if (ssl != NULL)
+ p = macvalue(macid("{client_flags}"), e);
+ if (p == NULL)
+ return false;
+ for (; *p != '\0'; p++)
{
- int r;
-
- if ((r = SSL_shutdown(ssl)) < 0)
- {
- if (LogLevel > 11)
- sm_syslog(LOG_WARNING, NOQID,
- "SSL_shutdown %s failed: %d",
- side, r);
- ret = EX_SOFTWARE;
- }
- else if (r == 0)
- {
- if (LogLevel > 13)
- sm_syslog(LOG_WARNING, NOQID,
- "SSL_shutdown %s not done",
- side);
- ret = EX_SOFTWARE;
- }
- SSL_free(ssl);
- ssl = NULL;
+ /* look for just this one flag */
+ if (*p == (char) flag)
+ return true;
}
- return ret;
+ return false;
}
-# endif /* STARTTLS */
-#endif /* SMTP */
+# endif /* STARTTLS || SASL */
diff --git a/contrib/sendmail/src/domain.c b/contrib/sendmail/src/domain.c
index 18a092b..f34df4c 100644
--- a/contrib/sendmail/src/domain.c
+++ b/contrib/sendmail/src/domain.c
@@ -13,19 +13,17 @@
#include <sendmail.h>
-#ifndef lint
-# if NAMED_BIND
-static char id[] = "@(#)$Id: 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[] = "@(#)$Id: domain.c,v 8.114.6.1.2.8 2001/02/12 21:40:19 gshapiro Exp $ (without name server)";
-# endif /* NAMED_BIND */
-#endif /* ! lint */
-
+#if NAMED_BIND
+SM_RCSID("@(#)$Id: domain.c,v 8.177 2001/12/12 01:16:15 ca Exp $ (with name server)")
+#else /* NAMED_BIND */
+SM_RCSID("@(#)$Id: domain.c,v 8.177 2001/12/12 01:16:15 ca Exp $ (without name server)")
+#endif /* NAMED_BIND */
#if NAMED_BIND
# include <arpa/inet.h>
+
/*
** The standard udp packet size PACKETSZ (512) is not sufficient for some
** nameserver answers containing very many resource records. The resolver
@@ -41,8 +39,8 @@ static char id[] = "@(#)$Id: domain.c,v 8.114.6.1.2.8 2001/02/12 21:40:19 gshapi
typedef union
{
- HEADER qb1;
- u_char qb2[MAXPACKET];
+ HEADER qb1;
+ unsigned char qb2[MAXPACKET];
} querybuf;
# ifndef MXHOSTBUFSIZE
@@ -50,6 +48,9 @@ typedef union
# endif /* ! MXHOSTBUFSIZE */
static char MXHostBuf[MXHOSTBUFSIZE];
+#if (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2)
+ ERROR: _MXHOSTBUFSIZE is out of range
+#endif /* (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) */
# ifndef MAXDNSRCH
# define MAXDNSRCH 6 /* number of possible domains to search */
@@ -59,10 +60,6 @@ static char MXHostBuf[MXHOSTBUFSIZE];
# define RES_DNSRCH_VARIABLE _res.dnsrch
# endif /* ! RES_DNSRCH_VARIABLE */
-# ifndef MAX
-# define MAX(a, b) ((a) > (b) ? (a) : (b))
-# endif /* ! MAX */
-
# ifndef NO_DATA
# define NO_DATA NO_ADDRESS
# endif /* ! NO_DATA */
@@ -76,13 +73,108 @@ static char MXHostBuf[MXHOSTBUFSIZE];
# if defined(__RES) && (__RES >= 19940415)
# define RES_UNC_T char *
# else /* defined(__RES) && (__RES >= 19940415) */
-# define RES_UNC_T u_char *
+# define RES_UNC_T unsigned char *
# endif /* defined(__RES) && (__RES >= 19940415) */
static char *gethostalias __P((char *));
static int mxrand __P((char *));
+static int fallbackmxrr __P((int, unsigned short *, char **));
+
+/*
+** GETFALLBACKMXRR -- get MX resource records for fallback MX host.
+**
+** We have to initialize this once before doing anything else.
+** Moreover, we have to repeat this from time to time to avoid
+** stale data, e.g., in persistent queue runners.
+** This should be done in a parent process so the child
+** processes have the right data.
+**
+** Parameters:
+** host -- the name of the fallback MX host.
+**
+** Returns:
+** number of MX records.
+**
+** Side Effects:
+** Populates NumFallBackMXHosts and fbhosts.
+** Sets renewal time (based on TTL).
+*/
+
+int NumFallBackMXHosts = 0; /* Number of fallback MX hosts (after MX expansion) */
+static char *fbhosts[MAXMXHOSTS + 1];
+
+int
+getfallbackmxrr(host)
+ char *host;
+{
+ int i, rcode;
+ int ttl;
+ static time_t renew = 0;
+
+#if 0
+ /* This is currently done before this function is called. */
+ if (host == NULL || *host == '\0')
+ return 0;
+#endif /* 0 */
+ if (NumFallBackMXHosts > 0 && renew > curtime())
+ return NumFallBackMXHosts;
+ if (host[0] == '[')
+ {
+ fbhosts[0] = host;
+ NumFallBackMXHosts = 1;
+ }
+ else
+ {
+ /* free old data */
+ for (i = 0; i < NumFallBackMXHosts; i++)
+ sm_free(fbhosts[i]);
+
+ /* get new data */
+ NumFallBackMXHosts = getmxrr(host, fbhosts, NULL, false,
+ &rcode, false, &ttl);
+ renew = curtime() + ttl;
+ for (i = 0; i < NumFallBackMXHosts; i++)
+ fbhosts[i] = newstr(fbhosts[i]);
+ }
+ return NumFallBackMXHosts;
+}
+
+/*
+** FALLBACKMXRR -- add MX resource records for fallback MX host to list.
+**
+** Parameters:
+** nmx -- current number of MX records.
+** prefs -- array of preferences.
+** mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS)
+**
+** Returns:
+** new number of MX records.
+**
+** Side Effects:
+** If FallBackMX was set, it appends the MX records for
+** that host to mxhosts (and modifies prefs accordingly).
+*/
- /*
+static int
+fallbackmxrr(nmx, prefs, mxhosts)
+ int nmx;
+ unsigned short *prefs;
+ char **mxhosts;
+{
+ int i;
+
+ for (i = 0; i < NumFallBackMXHosts && nmx < MAXMXHOSTS; i++)
+ {
+ if (nmx > 0)
+ prefs[nmx] = prefs[nmx - 1] + 1;
+ else
+ prefs[nmx] = 0;
+ mxhosts[nmx++] = fbhosts[i];
+ }
+ return nmx;
+}
+
+/*
** GETMXRR -- get MX resource records for a domain
**
** Parameters:
@@ -90,50 +182,60 @@ static int mxrand __P((char *));
** mxhosts -- a pointer to a return buffer of MX records.
** mxprefs -- a pointer to a return buffer of MX preferences.
** If NULL, don't try to populate.
-** droplocalhost -- If TRUE, all MX records less preferred
+** droplocalhost -- If true, all MX records less preferred
** than the local host (as determined by $=w) will
** be discarded.
** rcode -- a pointer to an EX_ status code.
+** tryfallback -- add also fallback MX host?
+** pttl -- pointer to return TTL (can be NULL).
**
** Returns:
** The number of MX records found.
** -1 if there is an internal failure.
** If no MX records are found, mxhosts[0] is set to host
** and 1 is returned.
+**
+** Side Effects:
+** The entries made for mxhosts point to a static array
+** MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied,
+** if it must be preserved across calls to this function.
*/
int
-getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode)
+getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode, tryfallback, pttl)
char *host;
char **mxhosts;
- u_short *mxprefs;
+ unsigned short *mxprefs;
bool droplocalhost;
int *rcode;
+ bool tryfallback;
+ int *pttl;
{
- register u_char *eom, *cp;
+ register unsigned char *eom, *cp;
register int i, j, n;
int nmx = 0;
register char *bp;
HEADER *hp;
querybuf answer;
int ancount, qdcount, buflen;
- bool seenlocal = FALSE;
- u_short pref, type;
- u_short localpref = 256;
+ bool seenlocal = false;
+ unsigned short pref, type;
+ unsigned short localpref = 256;
char *fallbackMX = FallBackMX;
- bool trycanon = FALSE;
- u_short *prefs;
+ bool trycanon = false;
+ unsigned short *prefs;
int (*resfunc)();
- u_short prefer[MAXMXHOSTS];
+ unsigned short prefer[MAXMXHOSTS];
int weight[MAXMXHOSTS];
+ int ttl = 0;
extern int res_query(), res_search();
if (tTd(8, 2))
- dprintf("getmxrr(%s, droplocalhost=%d)\n",
- host, droplocalhost);
+ sm_dprintf("getmxrr(%s, droplocalhost=%d)\n",
+ host, droplocalhost);
- if (fallbackMX != NULL && droplocalhost &&
- wordinclass(fallbackMX, 'w'))
+ if ((fallbackMX != NULL && droplocalhost &&
+ wordinclass(fallbackMX, 'w')) || !tryfallback)
{
/* don't use fallback for this pass */
fallbackMX = NULL;
@@ -146,7 +248,6 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode)
else
prefs = prefer;
-
/* efficiency hack -- numeric or non-MX lookups */
if (host[0] == '[')
goto punt;
@@ -167,16 +268,17 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode)
resfunc = res_search;
errno = 0;
- n = (*resfunc)(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer));
+ n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer,
+ sizeof(answer));
if (n < 0)
{
if (tTd(8, 1))
- dprintf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n",
- (host == NULL) ? "<NULL>" : host, errno, h_errno);
+ sm_dprintf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n",
+ host == NULL ? "<NULL>" : host, errno, h_errno);
switch (h_errno)
{
case NO_DATA:
- trycanon = TRUE;
+ trycanon = true;
/* FALLTHROUGH */
case NO_RECOVERY:
@@ -188,7 +290,7 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode)
case 0: /* Ultrix resolver retns failure w/ h_errno=0 */
# endif /* BROKEN_RES_SEARCH */
/* host doesn't exist in DNS; might be in /etc/hosts */
- trycanon = TRUE;
+ trycanon = true;
*rcode = EX_NOHOST;
goto punt;
@@ -198,19 +300,14 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode)
if (fallbackMX != NULL)
{
/* name server is hosed -- push to fallback */
- if (nmx > 0)
- prefs[nmx] = prefs[nmx - 1] + 1;
- else
- prefs[nmx] = 0;
- mxhosts[nmx++] = fallbackMX;
- return nmx;
+ return fallbackmxrr(nmx, prefs, mxhosts);
}
/* it might come up later; better queue it up */
*rcode = EX_TEMPFAIL;
break;
default:
- syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n",
+ syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)",
host, h_errno);
*rcode = EX_OSERR;
break;
@@ -226,50 +323,69 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode)
/* find first satisfactory answer */
hp = (HEADER *)&answer;
- cp = (u_char *)&answer + HFIXEDSZ;
- eom = (u_char *)&answer + n;
- for (qdcount = ntohs((u_short)hp->qdcount);
+ cp = (unsigned char *)&answer + HFIXEDSZ;
+ eom = (unsigned char *)&answer + n;
+ for (qdcount = ntohs((unsigned short) hp->qdcount);
qdcount--;
cp += n + QFIXEDSZ)
{
if ((n = dn_skipname(cp, eom)) < 0)
goto punt;
}
+
+ /* NOTE: see definition of MXHostBuf! */
buflen = sizeof(MXHostBuf) - 1;
+ SM_ASSERT(buflen > 0);
bp = MXHostBuf;
- ancount = ntohs((u_short)hp->ancount);
+ ancount = ntohs((unsigned short) hp->ancount);
+
+ /* See RFC 1035 for layout of RRs. */
+ /* XXX leave room for FallBackMX ? */
while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
{
- if ((n = dn_expand((u_char *)&answer,
- eom, cp, (RES_UNC_T) bp, buflen)) < 0)
+ if ((n = dn_expand((unsigned char *)&answer, eom, cp,
+ (RES_UNC_T) bp, buflen)) < 0)
break;
cp += n;
GETSHORT(type, cp);
- cp += INT16SZ + INT32SZ;
- GETSHORT(n, cp);
+ cp += INT16SZ; /* skip over class */
+ GETLONG(ttl, cp);
+ GETSHORT(n, cp); /* rdlength */
if (type != T_MX)
{
if (tTd(8, 8) || _res.options & RES_DEBUG)
- dprintf("unexpected answer type %d, size %d\n",
+ sm_dprintf("unexpected answer type %d, size %d\n",
type, n);
cp += n;
continue;
}
GETSHORT(pref, cp);
- if ((n = dn_expand((u_char *)&answer, eom, cp,
+ if ((n = dn_expand((unsigned char *)&answer, eom, cp,
(RES_UNC_T) bp, buflen)) < 0)
break;
cp += n;
+ n = strlen(bp);
+# if 0
+ /* Can this happen? */
+ if (n == 0)
+ {
+ if (LogLevel > 4)
+ sm_syslog(LOG_ERR, NOQID,
+ "MX records for %s contain empty string",
+ host);
+ continue;
+ }
+# endif /* 0 */
if (wordinclass(bp, 'w'))
{
if (tTd(8, 3))
- dprintf("found localhost (%s) in MX list, pref=%d\n",
+ sm_dprintf("found localhost (%s) in MX list, pref=%d\n",
bp, pref);
if (droplocalhost)
{
if (!seenlocal || pref < localpref)
localpref = pref;
- seenlocal = TRUE;
+ seenlocal = true;
continue;
}
weight[nmx] = 0;
@@ -278,7 +394,6 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode)
weight[nmx] = mxrand(bp);
prefs[nmx] = pref;
mxhosts[nmx++] = bp;
- n = strlen(bp);
bp += n;
if (bp[-1] != '.')
{
@@ -286,9 +401,18 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode)
n++;
}
*bp++ = '\0';
+ if (buflen < n + 1)
+ {
+ /* don't want to wrap buflen */
+ break;
+ }
buflen -= n + 1;
}
+ /* return only one TTL entry, that should be sufficient */
+ if (ttl > 0 && pttl != NULL)
+ *pttl = ttl;
+
/* sort the records */
for (i = 0; i < nmx; i++)
{
@@ -321,7 +445,7 @@ getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode)
/* delete duplicates from list (yes, some bozos have duplicates) */
for (i = 0; i < nmx - 1; )
{
- if (strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0)
+ if (sm_strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0)
i++;
else
{
@@ -393,19 +517,19 @@ punt:
host, MyHostName);
return -1;
}
-# if _FFR_FREEHOSTENT && NETINET6
+# if NETINET6
freehostent(h);
hp = NULL;
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+# endif /* NETINET6 */
}
- if (strlen(host) >= (SIZE_T) sizeof MXHostBuf)
+ if (strlen(host) >= sizeof MXHostBuf)
{
*rcode = EX_CONFIG;
syserr("Host name %s too long",
shortenstring(host, MAXSHORTSTR));
return -1;
}
- snprintf(MXHostBuf, sizeof MXHostBuf, "%s", host);
+ (void) sm_strlcpy(MXHostBuf, host, sizeof MXHostBuf);
mxhosts[0] = MXHostBuf;
prefs[0] = 0;
if (host[0] == '[')
@@ -427,8 +551,8 @@ punt:
*p = ']';
}
# if NETINET6
- else if (inet_pton(AF_INET6, &MXHostBuf[1],
- &tmp6.sin6_addr) == 1)
+ else if (anynet_pton(AF_INET6, &MXHostBuf[1],
+ &tmp6.sin6_addr) == 1)
{
nmx++;
*p = ']';
@@ -436,14 +560,15 @@ punt:
# endif /* NETINET6 */
else
{
- trycanon = TRUE;
+ trycanon = true;
mxhosts[0]++;
}
}
}
if (trycanon &&
- getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE))
+ getcanonname(mxhosts[0], sizeof MXHostBuf - 2, false, pttl))
{
+ /* XXX MXHostBuf == "" ? is that possible? */
bp = &MXHostBuf[strlen(MXHostBuf)];
if (bp[-1] != '.')
{
@@ -457,16 +582,11 @@ punt:
/* if we have a default lowest preference, include that */
if (fallbackMX != NULL && !seenlocal)
{
- if (nmx > 0)
- prefs[nmx] = prefs[nmx - 1] + 1;
- else
- prefs[nmx] = 0;
- mxhosts[nmx++] = fallbackMX;
+ nmx = fallbackmxrr(nmx, prefs, mxhosts);
}
-
return nmx;
}
- /*
+/*
** MXRAND -- create a randomizer for equal MX preferences
**
** If two MX hosts have equal preferences we want to randomize
@@ -479,9 +599,6 @@ punt:
**
** Returns:
** A random but repeatable value based on the host name.
-**
-** Side Effects:
-** none.
*/
static int
@@ -499,7 +616,7 @@ mxrand(host)
}
if (tTd(17, 9))
- dprintf("mxrand(%s)", host);
+ sm_dprintf("mxrand(%s)", host);
hfunc = seed;
while (*host != '\0')
@@ -515,10 +632,10 @@ mxrand(host)
hfunc++;
if (tTd(17, 9))
- dprintf(" = %d\n", hfunc);
+ sm_dprintf(" = %d\n", hfunc);
return hfunc;
}
- /*
+/*
** BESTMX -- find the best MX for a name
**
** This is really a hack, but I don't see any obvious way
@@ -535,13 +652,19 @@ bestmx_map_lookup(map, name, av, statp)
{
int nmx;
int saveopts = _res.options;
- int i, len = 0;
- char *p;
+ int i;
+ ssize_t len = 0;
+ char *result;
char *mxhosts[MAXMXHOSTS + 1];
+#if _FFR_BESTMX_BETTER_TRUNCATION
+ char *buf;
+#else /* _FFR_BESTMX_BETTER_TRUNCATION */
+ char *p;
char buf[PSBUFSIZE / 2];
+#endif /* _FFR_BESTMX_BETTER_TRUNCATION */
_res.options &= ~(RES_DNSRCH|RES_DEFNAMES);
- nmx = getmxrr(name, mxhosts, NULL, FALSE, statp);
+ nmx = getmxrr(name, mxhosts, NULL, false, statp, true, NULL);
_res.options = saveopts;
if (nmx <= 0)
return NULL;
@@ -554,10 +677,49 @@ bestmx_map_lookup(map, name, av, statp)
** We were given a -z flag (return all MXs) and there are multiple
** ones. We need to build them all into a list.
*/
+
+#if _FFR_BESTMX_BETTER_TRUNCATION
+ for (i = 0; i < nmx; i++)
+ {
+ if (strchr(mxhosts[i], map->map_coldelim) != NULL)
+ {
+ syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
+ mxhosts[i], map->map_coldelim);
+ return NULL;
+ }
+ len += strlen(mxhosts[i]) + 1;
+ if (len < 0)
+ {
+ len -= strlen(mxhosts[i]) + 1;
+ break;
+ }
+ }
+ buf = (char *) sm_malloc(len);
+ if (buf == NULL)
+ {
+ *statp = EX_UNAVAILABLE;
+ return NULL;
+ }
+ *buf = '\0';
+ for (i = 0; i < nmx; i++)
+ {
+ int end;
+
+ end = sm_strlcat(buf, mxhosts[i], len);
+ if (i != nmx && end + 1 < len)
+ {
+ buf[end] = map->map_coldelim;
+ buf[end + 1] = '\0';
+ }
+ }
+
+ /* Cleanly truncate for rulesets */
+ truncate_at_delim(buf, PSBUFSIZE / 2, map->map_coldelim);
+#else /* _FFR_BESTMX_BETTER_TRUNCATION */
p = buf;
for (i = 0; i < nmx; i++)
{
- int slen;
+ size_t slen;
if (strchr(mxhosts[i], map->map_coldelim) != NULL)
{
@@ -573,13 +735,19 @@ bestmx_map_lookup(map, name, av, statp)
*p++ = map->map_coldelim;
len++;
}
- (void) strlcpy(p, mxhosts[i], sizeof buf - len);
+ (void) sm_strlcpy(p, mxhosts[i], sizeof buf - len);
p += slen;
len += slen;
}
- return map_rewrite(map, buf, len, av);
+#endif /* _FFR_BESTMX_BETTER_TRUNCATION */
+
+ result = map_rewrite(map, buf, len, av);
+#if _FFR_BESTMX_BETTER_TRUNCATION
+ sm_free(buf);
+#endif /* _FFR_BESTMX_BETTER_TRUNCATION */
+ return result;
}
- /*
+/*
** DNS_GETCANONNAME -- get the canonical name for named host using DNS
**
** This algorithm tries to be smart about wildcard MX records.
@@ -603,20 +771,28 @@ bestmx_map_lookup(map, name, av, statp)
** hbsize -- the size of the host buffer.
** trymx -- if set, try MX records as well as A and CNAME.
** statp -- pointer to place to store status.
+** pttl -- pointer to return TTL (can be NULL).
**
** Returns:
-** TRUE -- if the host matched.
-** FALSE -- otherwise.
+** true -- if the host matched.
+** false -- otherwise.
*/
+# if NETINET6
+# define SM_T_INITIAL T_AAAA
+# else /* NETINET6 */
+# define SM_T_INITIAL T_A
+# endif /* NETINET6 */
+
bool
-dns_getcanonname(host, hbsize, trymx, statp)
+dns_getcanonname(host, hbsize, trymx, statp, pttl)
char *host;
int hbsize;
bool trymx;
int *statp;
+ int *pttl;
{
- register u_char *eom, *ap;
+ register unsigned char *eom, *ap;
register char *cp;
register int n;
HEADER *hp;
@@ -625,23 +801,24 @@ dns_getcanonname(host, hbsize, trymx, statp)
int ret;
char **domain;
int type;
+ int ttl = 0;
char **dp;
char *mxmatch;
bool amatch;
- bool gotmx = FALSE;
+ bool gotmx = false;
int qtype;
int loopcnt;
char *xp;
- char nbuf[MAX(MAXPACKET, MAXDNAME*2+2)];
+ char nbuf[SM_MAX(MAXPACKET, MAXDNAME*2+2)];
char *searchlist[MAXDNSRCH+2];
if (tTd(8, 2))
- dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx);
+ sm_dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx);
if ((_res.options & RES_INIT) == 0 && res_init() == -1)
{
*statp = EX_UNAVAILABLE;
- return FALSE;
+ return false;
}
*statp = EX_OK;
@@ -669,6 +846,7 @@ cnameloop:
** If this is a simple name, determine whether it matches an
** alias in the file defined by the environment variable HOSTALIASES.
*/
+
if (n == 0 && (xp = gethostalias(host)) != NULL)
{
if (loopcnt++ > MAXCNAMEDEPTH)
@@ -677,7 +855,7 @@ cnameloop:
}
else
{
- (void) strlcpy(host, xp, hbsize);
+ (void) sm_strlcpy(host, xp, hbsize);
goto cnameloop;
}
}
@@ -720,16 +898,15 @@ cnameloop:
*/
mxmatch = NULL;
- qtype = T_ANY;
+ qtype = SM_T_INITIAL;
for (dp = searchlist; *dp != NULL; )
{
- if (qtype == T_ANY)
- gotmx = FALSE;
+ if (qtype == SM_T_INITIAL)
+ gotmx = false;
if (tTd(8, 5))
- dprintf("dns_getcanonname: trying %s.%s (%s)\n",
+ sm_dprintf("dns_getcanonname: trying %s.%s (%s)\n",
host, *dp,
- qtype == T_ANY ? "ANY" :
# if NETINET6
qtype == T_AAAA ? "AAAA" :
# endif /* NETINET6 */
@@ -741,34 +918,21 @@ cnameloop:
answer.qb2, sizeof(answer.qb2));
if (ret <= 0)
{
+ int save_errno = errno;
+
if (tTd(8, 7))
- dprintf("\tNO: errno=%d, h_errno=%d\n",
- errno, h_errno);
+ sm_dprintf("\tNO: errno=%d, h_errno=%d\n",
+ save_errno, h_errno);
- if (errno == ECONNREFUSED || h_errno == TRY_AGAIN)
+ if (save_errno == ECONNREFUSED || h_errno == TRY_AGAIN)
{
/*
- ** the name server seems to be down or
- ** broken.
+ ** the name server seems to be down or broken.
*/
SM_SET_H_ERRNO(TRY_AGAIN);
*statp = EX_TEMPFAIL;
- /*
- ** If the ANY query is larger than the
- ** UDP packet size, the resolver will
- ** fall back to TCP. However, some
- ** misconfigured firewalls block 53/TCP
- ** so the ANY lookup fails whereas an MX
- ** or A record might work. Therefore,
- ** don't fail on ANY queries.
- **
- ** The ANY query is really meant to prime
- ** the cache so this isn't dangerous.
- */
-
-#if _FFR_WORKAROUND_BROKEN_NAMESERVERS
if (WorkAroundBrokenAAAA)
{
/*
@@ -781,37 +945,26 @@ cnameloop:
** didn't give an answer).
*/
- if (qtype != T_ANY &&
- errno != ETIMEDOUT)
- return FALSE;
+ if (save_errno != ETIMEDOUT)
+ return false;
}
-#else /* _FFR_WORKAROUND_BROKEN_NAMESERVERS */
- if (qtype != T_ANY)
- return FALSE;
-#endif /* _FFR_WORKAROUND_BROKEN_NAMESERVERS */
+ else
+ return false;
}
if (h_errno != HOST_NOT_FOUND)
{
/* might have another type of interest */
- if (qtype == T_ANY)
- {
# if NETINET6
- qtype = T_AAAA;
-# else /* NETINET6 */
- qtype = T_A;
-# endif /* NETINET6 */
- continue;
- }
-# if NETINET6
- else if (qtype == T_AAAA)
+ if (qtype == T_AAAA)
{
qtype = T_A;
continue;
}
+ else
# endif /* NETINET6 */
- else if (qtype == T_A && !gotmx &&
- (trymx || **dp == '\0'))
+ if (qtype == T_A && !gotmx &&
+ (trymx || **dp == '\0'))
{
qtype = T_MX;
continue;
@@ -820,15 +973,20 @@ cnameloop:
/* definite no -- try the next domain */
dp++;
- qtype = T_ANY;
+ qtype = SM_T_INITIAL;
continue;
}
else if (tTd(8, 7))
- dprintf("\tYES\n");
+ sm_dprintf("\tYES\n");
/* avoid problems after truncation in tcp packets */
if (ret > sizeof(answer))
ret = sizeof(answer);
+ if (ret < 0)
+ {
+ *statp = EX_SOFTWARE;
+ return false;
+ }
/*
** Appear to have a match. Confirm it by searching for A or
@@ -837,41 +995,42 @@ cnameloop:
*/
hp = (HEADER *) &answer;
- ap = (u_char *) &answer + HFIXEDSZ;
- eom = (u_char *) &answer + ret;
+ ap = (unsigned char *) &answer + HFIXEDSZ;
+ eom = (unsigned char *) &answer + ret;
/* skip question part of response -- we know what we asked */
- for (qdcount = ntohs((u_short)hp->qdcount);
+ for (qdcount = ntohs((unsigned short) hp->qdcount);
qdcount--;
ap += ret + QFIXEDSZ)
{
if ((ret = dn_skipname(ap, eom)) < 0)
{
if (tTd(8, 20))
- dprintf("qdcount failure (%d)\n",
- ntohs((u_short)hp->qdcount));
+ sm_dprintf("qdcount failure (%d)\n",
+ ntohs((unsigned short) hp->qdcount));
*statp = EX_SOFTWARE;
- return FALSE; /* ???XXX??? */
+ return false; /* ???XXX??? */
}
}
- amatch = FALSE;
- for (ancount = ntohs((u_short)hp->ancount);
+ amatch = false;
+ for (ancount = ntohs((unsigned short) hp->ancount);
--ancount >= 0 && ap < eom;
ap += n)
{
- n = dn_expand((u_char *) &answer, eom, ap,
+ n = dn_expand((unsigned char *) &answer, eom, ap,
(RES_UNC_T) nbuf, sizeof nbuf);
if (n < 0)
break;
ap += n;
GETSHORT(type, ap);
- ap += INT16SZ + INT32SZ;
- GETSHORT(n, ap);
+ ap += INT16SZ; /* skip over class */
+ GETLONG(ttl, ap);
+ GETSHORT(n, ap); /* rdlength */
switch (type)
{
case T_MX:
- gotmx = TRUE;
+ gotmx = true;
if (**dp != '\0' && HasWildcardMX)
{
/*
@@ -900,7 +1059,7 @@ cnameloop:
# if NETINET6
case T_AAAA:
/* Flag that a good match was found */
- amatch = TRUE;
+ amatch = true;
/* continue in case a CNAME also exists */
continue;
@@ -908,7 +1067,7 @@ cnameloop:
case T_A:
/* Flag that a good match was found */
- amatch = TRUE;
+ amatch = true;
/* continue in case a CNAME also exists */
continue;
@@ -917,7 +1076,7 @@ cnameloop:
if (DontExpandCnames)
{
/* got CNAME -- guaranteed canonical */
- amatch = TRUE;
+ amatch = true;
break;
}
@@ -930,21 +1089,25 @@ cnameloop:
{
char ebuf[MAXLINE];
- snprintf(ebuf, sizeof ebuf,
+ (void) sm_snprintf(ebuf,
+ sizeof ebuf,
"Deferred: DNS failure: CNAME loop for %.100s",
host);
- CurEnv->e_message = newstr(ebuf);
+ CurEnv->e_message =
+ sm_rpool_strdup_x(
+ CurEnv->e_rpool, ebuf);
}
SM_SET_H_ERRNO(NO_RECOVERY);
*statp = EX_CONFIG;
- return FALSE;
+ return false;
}
/* value points at name */
- if ((ret = dn_expand((u_char *)&answer,
- eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0)
+ if ((ret = dn_expand((unsigned char *)&answer,
+ eom, ap, (RES_UNC_T) nbuf,
+ sizeof(nbuf))) < 0)
break;
- (void)strlcpy(host, nbuf, hbsize);
+ (void) sm_strlcpy(host, nbuf, hbsize);
/*
** RFC 1034 section 3.6 specifies that CNAME
@@ -973,32 +1136,21 @@ cnameloop:
/*
** Nothing definitive yet.
- ** If this was a T_ANY query, we don't really know what
- ** was returned -- it might have been a T_NS,
- ** for example. Try T_A to be more specific
- ** during the next pass.
** If this was a T_A query and we haven't yet found a MX
** match, try T_MX if allowed to do so.
** Otherwise, try the next domain.
*/
- if (qtype == T_ANY)
- {
# if NETINET6
- qtype = T_AAAA;
-# else /* NETINET6 */
- qtype = T_A;
-# endif /* NETINET6 */
- }
-# if NETINET6
- else if (qtype == T_AAAA)
+ if (qtype == T_AAAA)
qtype = T_A;
+ else
# endif /* NETINET6 */
- else if (qtype == T_A && !gotmx && (trymx || **dp == '\0'))
+ if (qtype == T_A && !gotmx && (trymx || **dp == '\0'))
qtype = T_MX;
else
{
- qtype = T_ANY;
+ qtype = SM_T_INITIAL;
dp++;
}
}
@@ -1008,7 +1160,7 @@ cnameloop:
{
if (*statp == EX_OK)
*statp = EX_NOHOST;
- return FALSE;
+ return false;
}
/*
@@ -1017,14 +1169,18 @@ cnameloop:
** Otherwise append the saved domain name.
*/
- (void) snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host,
- *mxmatch == '\0' ? "" : ".",
- MAXDNAME, mxmatch);
- (void) strlcpy(host, nbuf, hbsize);
+ (void) sm_snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host,
+ *mxmatch == '\0' ? "" : ".",
+ MAXDNAME, mxmatch);
+ (void) sm_strlcpy(host, nbuf, hbsize);
if (tTd(8, 5))
- dprintf("dns_getcanonname: %s\n", host);
+ sm_dprintf("dns_getcanonname: %s\n", host);
*statp = EX_OK;
- return TRUE;
+
+ /* return only one TTL entry, that should be sufficient */
+ if (ttl > 0 && pttl != NULL)
+ *pttl = ttl;
+ return true;
}
static char *
@@ -1032,19 +1188,21 @@ gethostalias(host)
char *host;
{
char *fname;
- FILE *fp;
+ SM_FILE_T *fp;
register char *p = NULL;
long sff = SFF_REGONLY;
char buf[MAXLINE];
static char hbuf[MAXDNAME];
+ if (ResNoAliases)
+ return NULL;
if (DontLockReadFiles)
sff |= SFF_NOLOCK;
fname = getenv("HOSTALIASES");
if (fname == NULL ||
(fp = safefopen(fname, O_RDONLY, 0, sff)) == NULL)
return NULL;
- while (fgets(buf, sizeof buf, fp) != NULL)
+ while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
{
for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++)
continue;
@@ -1054,17 +1212,17 @@ gethostalias(host)
continue;
}
*p++ = '\0';
- if (strcasecmp(buf, host) == 0)
+ if (sm_strcasecmp(buf, host) == 0)
break;
}
- if (feof(fp))
+ if (sm_io_eof(fp))
{
/* no match */
- (void) fclose(fp);
+ (void) sm_io_close(fp, SM_TIME_DEFAULT);
return NULL;
}
- (void) fclose(fp);
+ (void) sm_io_close(fp, SM_TIME_DEFAULT);
/* got a match; extract the equivalent name */
while (*p != '\0' && isascii(*p) && isspace(*p))
@@ -1073,7 +1231,7 @@ gethostalias(host)
while (*p != '\0' && !(isascii(*p) && isspace(*p)))
p++;
*p = '\0';
- (void) strlcpy(hbuf, host, sizeof hbuf);
+ (void) sm_strlcpy(hbuf, host, sizeof hbuf);
return hbuf;
}
#endif /* NAMED_BIND */
diff --git a/contrib/sendmail/src/envelope.c b/contrib/sendmail/src/envelope.c
index bed63e4..16449f1 100644
--- a/contrib/sendmail/src/envelope.c
+++ b/contrib/sendmail/src/envelope.c
@@ -11,21 +11,21 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: envelope.c,v 8.180.14.10 2001/05/03 17:24:06 gshapiro Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
+SM_RCSID("@(#)$Id: envelope.c,v 8.279 2001/12/10 19:56:04 ca Exp $")
/*
-** NEWENVELOPE -- allocate a new envelope
+** NEWENVELOPE -- fill in a new envelope
**
** Supports inheritance.
**
** Parameters:
** e -- the new envelope to fill in.
** parent -- the envelope to be the parent of e.
+** rpool -- either NULL, or a pointer to a resource pool
+** from which envelope memory is allocated, and
+** to which envelope resources are attached.
**
** Returns:
** e.
@@ -35,13 +35,23 @@ static char id[] = "@(#)$Id: envelope.c,v 8.180.14.10 2001/05/03 17:24:06 gshapi
*/
ENVELOPE *
-newenvelope(e, parent)
+newenvelope(e, parent, rpool)
register ENVELOPE *e;
register ENVELOPE *parent;
+ SM_RPOOL_T *rpool;
{
- if (e == parent && e->e_parent != NULL)
+ /*
+ ** This code used to read:
+ ** if (e == parent && e->e_parent != NULL)
+ ** parent = e->e_parent;
+ ** So if e == parent && e->e_parent == NULL then we would
+ ** set e->e_parent = e, which creates a loop in the e_parent chain.
+ ** This meant macvalue() could go into an infinite loop.
+ */
+
+ if (e == parent)
parent = e->e_parent;
- clearenvelope(e, TRUE);
+ clearenvelope(e, true, rpool);
if (e == CurEnv)
memmove((char *) &e->e_from,
(char *) &NullAddress,
@@ -54,20 +64,52 @@ newenvelope(e, parent)
assign_queueid(e);
e->e_ctime = curtime();
if (parent != NULL)
+ {
e->e_msgpriority = parent->e_msgsize;
+#if _FFR_QUARANTINE
+ if (parent->e_quarmsg == NULL)
+ {
+ e->e_quarmsg = NULL;
+ macdefine(&e->e_macro, A_PERM,
+ macid("{quarantine}"), "");
+ }
+ else
+ {
+ e->e_quarmsg = sm_rpool_strdup_x(rpool,
+ parent->e_quarmsg);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{quarantine}"), e->e_quarmsg);
+ }
+#endif /* _FFR_QUARANTINE */
+ }
e->e_puthdr = putheader;
e->e_putbody = putbody;
if (CurEnv->e_xfp != NULL)
- (void) fflush(CurEnv->e_xfp);
+ (void) sm_io_flush(CurEnv->e_xfp, SM_TIME_DEFAULT);
return e;
}
- /*
+
+/* values for msg_timeout, see also IS_* below for usage (bit layout) */
+#define MSG_T_O 0x01 /* normal timeout */
+#define MSG_T_O_NOW 0x02 /* NOW timeout */
+#define MSG_NOT_BY 0x04 /* Deliver-By time exceeded, mode R */
+#define MSG_WARN 0x10 /* normal queue warning */
+#define MSG_WARN_BY 0x20 /* Deliver-By time exceeded, mode N */
+
+#define IS_MSG_ERR(x) (((x) & 0x0f) != 0) /* return an error */
+
+/* immediate return */
+#define IS_IMM_RET(x) (((x) & (MSG_T_O_NOW|MSG_NOT_BY)) != 0)
+#define IS_MSG_WARN(x) (((x) & 0xf0) != 0) /* return a warning */
+
+/*
** DROPENVELOPE -- deallocate an envelope.
**
** Parameters:
** e -- the envelope to deallocate.
** fulldrop -- if set, do return receipts.
+** split -- if true, split by recipient if message is queued up
**
** Returns:
** none.
@@ -78,17 +120,19 @@ newenvelope(e, parent)
*/
void
-dropenvelope(e, fulldrop)
+dropenvelope(e, fulldrop, split)
register ENVELOPE *e;
bool fulldrop;
+ bool split;
{
- bool queueit = FALSE;
- bool message_timeout = FALSE;
- bool failure_return = FALSE;
- bool delay_return = FALSE;
- bool success_return = FALSE;
+ bool panic = false;
+ bool queueit = false;
+ int msg_timeout = 0;
+ bool failure_return = false;
+ bool delay_return = false;
+ bool success_return = false;
bool pmnotify = bitset(EF_PM_NOTIFY, e->e_flags);
- bool done = FALSE;
+ bool done = false;
register ADDRESS *q;
char *id = e->e_id;
time_t now;
@@ -96,21 +140,21 @@ dropenvelope(e, fulldrop)
if (tTd(50, 1))
{
- dprintf("dropenvelope %lx: id=", (u_long) e);
+ sm_dprintf("dropenvelope %p: id=", e);
xputs(e->e_id);
- dprintf(", flags=");
+ sm_dprintf(", flags=");
printenvflags(e);
if (tTd(50, 10))
{
- dprintf("sendq=");
- printaddr(e->e_sendqueue, TRUE);
+ sm_dprintf("sendq=");
+ printaddr(e->e_sendqueue, true);
}
}
if (LogLevel > 84)
sm_syslog(LOG_DEBUG, id,
"dropenvelope, e_flags=0x%lx, OpMode=%c, pid=%d",
- e->e_flags, OpMode, getpid());
+ e->e_flags, OpMode, (int) CurrentPid);
/* we must have an id to remove disk files */
if (id == NULL)
@@ -133,12 +177,18 @@ dropenvelope(e, fulldrop)
now = curtime();
if (now >= e->e_ctime + TimeOuts.to_q_return[e->e_timeoutclass])
- message_timeout = TRUE;
-
- if (TimeOuts.to_q_return[e->e_timeoutclass] == NOW &&
+ msg_timeout = MSG_T_O;
+ if (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 &&
+ now >= e->e_ctime + e->e_deliver_by &&
!bitset(EF_RESPONSE, e->e_flags))
{
- message_timeout = TRUE;
+ msg_timeout = MSG_NOT_BY;
+ e->e_flags |= EF_FATALERRS|EF_CLRQUEUE;
+ }
+ else if (TimeOuts.to_q_return[e->e_timeoutclass] == NOW &&
+ !bitset(EF_RESPONSE, e->e_flags))
+ {
+ msg_timeout = MSG_T_O_NOW;
e->e_flags |= EF_FATALERRS|EF_CLRQUEUE;
}
@@ -146,31 +196,32 @@ dropenvelope(e, fulldrop)
for (q = e->e_sendqueue; q != NULL; q = q->q_next)
{
if (QS_IS_UNDELIVERED(q->q_state))
- queueit = TRUE;
+ queueit = true;
/* see if a notification is needed */
if (bitset(QPINGONFAILURE, q->q_flags) &&
- ((message_timeout && QS_IS_UNDELIVERED(q->q_state)) ||
+ ((IS_MSG_ERR(msg_timeout) &&
+ QS_IS_UNDELIVERED(q->q_state)) ||
QS_IS_BADADDR(q->q_state) ||
- (TimeOuts.to_q_return[e->e_timeoutclass] == NOW &&
- !bitset(EF_RESPONSE, e->e_flags))))
-
+ IS_IMM_RET(msg_timeout)))
{
- failure_return = TRUE;
+ failure_return = true;
if (!done && q->q_owner == NULL &&
!emptyaddr(&e->e_from))
{
(void) sendtolist(e->e_from.q_paddr, NULLADDR,
&e->e_errorqueue, 0, e);
- done = TRUE;
+ done = true;
}
}
- else if (bitset(QPINGONSUCCESS, q->q_flags) &&
- ((QS_IS_SENT(q->q_state) &&
- bitnset(M_LOCALMAILER, q->q_mailer->m_flags)) ||
- bitset(QRELAYED|QEXPANDED|QDELIVERED, q->q_flags)))
+ else if ((bitset(QPINGONSUCCESS, q->q_flags) &&
+ ((QS_IS_SENT(q->q_state) &&
+ bitnset(M_LOCALMAILER, q->q_mailer->m_flags)) ||
+ bitset(QRELAYED|QEXPANDED|QDELIVERED, q->q_flags))) ||
+ bitset(QBYTRACE, q->q_flags) ||
+ bitset(QBYNRELAY, q->q_flags))
{
- success_return = TRUE;
+ success_return = true;
}
}
@@ -184,75 +235,146 @@ dropenvelope(e, fulldrop)
if (!queueit)
/* EMPTY */
/* nothing to do */ ;
- else if (message_timeout)
+ else if (IS_MSG_ERR(msg_timeout))
{
if (failure_return)
{
- (void) snprintf(buf, sizeof buf,
+ if (msg_timeout == MSG_NOT_BY)
+ {
+ (void) sm_snprintf(buf, sizeof buf,
+ "delivery time expired %lds",
+ e->e_deliver_by);
+ }
+ else
+ {
+ (void) sm_snprintf(buf, sizeof buf,
"Cannot send message for %s",
- pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE));
- if (e->e_message != NULL)
- sm_free(e->e_message);
- e->e_message = newstr(buf);
+ pintvl(TimeOuts.to_q_return[e->e_timeoutclass],
+ false));
+ }
+
+ /* don't free, allocated from e_rpool */
+ e->e_message = sm_rpool_strdup_x(e->e_rpool, buf);
message(buf);
e->e_flags |= EF_CLRQUEUE;
}
- fprintf(e->e_xfp, "Message could not be delivered for %s\n",
- pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE));
- fprintf(e->e_xfp, "Message will be deleted from queue\n");
+ if (msg_timeout == MSG_NOT_BY)
+ {
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "Delivery time (%lds) expired\n",
+ e->e_deliver_by);
+ }
+ else
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "Message could not be delivered for %s\n",
+ pintvl(TimeOuts.to_q_return[e->e_timeoutclass],
+ false));
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "Message will be deleted from queue\n");
for (q = e->e_sendqueue; q != NULL; q = q->q_next)
{
if (QS_IS_UNDELIVERED(q->q_state))
{
q->q_state = QS_BADADDR;
- q->q_status = "4.4.7";
+ if (msg_timeout == MSG_NOT_BY)
+ q->q_status = "5.4.7";
+ else
+ q->q_status = "4.4.7";
}
}
}
- else if (TimeOuts.to_q_warning[e->e_timeoutclass] > 0 &&
- now >= e->e_ctime + TimeOuts.to_q_warning[e->e_timeoutclass])
+ else
{
- if (!bitset(EF_WARNING|EF_RESPONSE, e->e_flags) &&
- e->e_class >= 0 &&
- e->e_from.q_paddr != NULL &&
- strcmp(e->e_from.q_paddr, "<>") != 0 &&
- strncasecmp(e->e_from.q_paddr, "owner-", 6) != 0 &&
- (strlen(e->e_from.q_paddr) <= (SIZE_T) 8 ||
- strcasecmp(&e->e_from.q_paddr[strlen(e->e_from.q_paddr) - 8], "-request") != 0))
+ if (TimeOuts.to_q_warning[e->e_timeoutclass] > 0 &&
+ now >= e->e_ctime +
+ TimeOuts.to_q_warning[e->e_timeoutclass])
+ msg_timeout = MSG_WARN;
+ else if (IS_DLVR_NOTIFY(e) &&
+ e->e_deliver_by > 0 &&
+ now >= e->e_ctime + e->e_deliver_by)
+ msg_timeout = MSG_WARN_BY;
+
+ if (IS_MSG_WARN(msg_timeout))
{
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ if (!bitset(EF_WARNING|EF_RESPONSE, e->e_flags) &&
+ e->e_class >= 0 &&
+ e->e_from.q_paddr != NULL &&
+ strcmp(e->e_from.q_paddr, "<>") != 0 &&
+ sm_strncasecmp(e->e_from.q_paddr, "owner-", 6) != 0 &&
+ (strlen(e->e_from.q_paddr) <= 8 ||
+ sm_strcasecmp(&e->e_from.q_paddr[strlen(e->e_from.q_paddr) - 8],
+ "-request") != 0))
{
- if (QS_IS_UNDELIVERED(q->q_state) &&
+ for (q = e->e_sendqueue; q != NULL;
+ q = q->q_next)
+ {
+ if (QS_IS_UNDELIVERED(q->q_state)
#if _FFR_NODELAYDSN_ON_HOLD
- !bitnset(M_HOLD, q->q_mailer->m_flags) &&
+ && !bitnset(M_HOLD,
+ q->q_mailer->m_flags)
#endif /* _FFR_NODELAYDSN_ON_HOLD */
- bitset(QPINGONDELAY, q->q_flags))
+ )
+ {
+ if (msg_timeout ==
+ MSG_WARN_BY &&
+ (bitset(QPINGONDELAY,
+ q->q_flags) ||
+ !bitset(QHASNOTIFY,
+ q->q_flags))
+ )
+ {
+ q->q_flags |= QBYNDELAY;
+ delay_return = true;
+ }
+ if (bitset(QPINGONDELAY,
+ q->q_flags))
+ {
+ q->q_flags |= QDELAYED;
+ delay_return = true;
+ }
+ }
+ }
+ }
+ if (delay_return)
+ {
+ if (msg_timeout == MSG_WARN_BY)
{
- q->q_flags |= QDELAYED;
- delay_return = TRUE;
+ (void) sm_snprintf(buf, sizeof buf,
+ "Warning: Delivery time (%lds) exceeded",
+ e->e_deliver_by);
}
+ else
+ (void) sm_snprintf(buf, sizeof buf,
+ "Warning: could not send message for past %s",
+ pintvl(TimeOuts.to_q_warning[e->e_timeoutclass],
+ false));
+
+ /* don't free, allocated from e_rpool */
+ e->e_message = sm_rpool_strdup_x(e->e_rpool,
+ buf);
+ message(buf);
+ e->e_flags |= EF_WARNING;
}
+ if (msg_timeout == MSG_WARN_BY)
+ {
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "Warning: Delivery time (%lds) exceeded\n",
+ e->e_deliver_by);
+ }
+ else
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "Warning: message still undelivered after %s\n",
+ pintvl(TimeOuts.to_q_warning[e->e_timeoutclass],
+ false));
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "Will keep trying until message is %s old\n",
+ pintvl(TimeOuts.to_q_return[e->e_timeoutclass],
+ false));
}
- if (delay_return)
- {
- (void) snprintf(buf, sizeof buf,
- "Warning: could not send message for past %s",
- pintvl(TimeOuts.to_q_warning[e->e_timeoutclass], FALSE));
- if (e->e_message != NULL)
- sm_free(e->e_message);
- e->e_message = newstr(buf);
- message(buf);
- e->e_flags |= EF_WARNING;
- }
- fprintf(e->e_xfp,
- "Warning: message still undelivered after %s\n",
- pintvl(TimeOuts.to_q_warning[e->e_timeoutclass], FALSE));
- fprintf(e->e_xfp, "Will keep trying until message is %s old\n",
- pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE));
}
if (tTd(50, 2))
- dprintf("failure_return=%d delay_return=%d success_return=%d queueit=%d\n",
+ sm_dprintf("failure_return=%d delay_return=%d success_return=%d queueit=%d\n",
failure_return, delay_return, success_return, queueit);
/*
@@ -268,7 +390,7 @@ dropenvelope(e, fulldrop)
QS_IS_VERIFIED(q->q_state)) &&
bitset(QPINGONFAILURE, q->q_flags))
{
- failure_return = TRUE;
+ failure_return = true;
q->q_state = QS_BADADDR;
}
}
@@ -285,7 +407,7 @@ dropenvelope(e, fulldrop)
auto ADDRESS *rlist = NULL;
if (tTd(50, 8))
- dprintf("dropenvelope(%s): sending return receipt\n",
+ sm_dprintf("dropenvelope(%s): sending return receipt\n",
id);
e->e_flags |= EF_SENDRECEIPT;
(void) sendtolist(e->e_from.q_paddr, NULLADDR, &rlist, 0, e);
@@ -300,8 +422,8 @@ dropenvelope(e, fulldrop)
if ((failure_return || delay_return) && e->e_errormode != EM_QUIET)
{
if (tTd(50, 8))
- dprintf("dropenvelope(%s): saving mail\n", id);
- savemail(e, !bitset(EF_NO_BODY_RETN, e->e_flags));
+ sm_dprintf("dropenvelope(%s): saving mail\n", id);
+ panic = savemail(e, !bitset(EF_NO_BODY_RETN, e->e_flags));
}
/*
@@ -321,7 +443,7 @@ dropenvelope(e, fulldrop)
expand(PostMasterCopy, pcopy, sizeof pcopy, e);
if (tTd(50, 8))
- dprintf("dropenvelope(%s): sending postmaster copy to %s\n",
+ sm_dprintf("dropenvelope(%s): sending postmaster copy to %s\n",
id, pcopy);
(void) sendtolist(pcopy, NULLADDR, &rlist, 0, e);
}
@@ -338,47 +460,110 @@ dropenvelope(e, fulldrop)
simpledrop:
if (tTd(50, 8))
- dprintf("dropenvelope(%s): at simpledrop, queueit=%d\n",
+ sm_dprintf("dropenvelope(%s): at simpledrop, queueit=%d\n",
id, queueit);
if (!queueit || bitset(EF_CLRQUEUE, e->e_flags))
{
if (tTd(50, 1))
{
- dprintf("\n===== Dropping [dq]f%s... queueit=%d, e_flags=",
+ sm_dprintf("\n===== Dropping queue files for %s... queueit=%d, e_flags=",
e->e_id, queueit);
printenvflags(e);
}
- xunlink(queuename(e, 'd'));
- xunlink(queuename(e, 'q'));
+ if (!panic)
+ (void) xunlink(queuename(e, DATAFL_LETTER));
+#if _FFR_QUARANTINE
+ if (panic && QueueMode == QM_LOST)
+ {
+ /*
+ ** leave the Qf file behind as
+ ** the delivery attempt failed.
+ */
+
+ /* EMPTY */
+ }
+ else
+#endif /* _FFR_QUARANTINE */
+ if (xunlink(queuename(e, ANYQFL_LETTER)) == 0)
+ {
+ /* add to available space in filesystem */
+ updfs(e, true, !panic);
+ }
if (e->e_ntries > 0 && LogLevel > 9)
sm_syslog(LOG_INFO, id, "done; delay=%s, ntries=%d",
- pintvl(curtime() - e->e_ctime, TRUE),
+ pintvl(curtime() - e->e_ctime, true),
e->e_ntries);
}
else if (queueit || !bitset(EF_INQUEUE, e->e_flags))
{
-#if QUEUE
- queueup(e, FALSE);
-#else /* QUEUE */
- syserr("554 5.3.0 dropenvelope: queueup");
-#endif /* QUEUE */
+ if (!split)
+ queueup(e, false, true);
+ else
+ {
+ ENVELOPE *oldsib;
+ ENVELOPE *ee;
+
+ /*
+ ** Save old sibling and set it to NULL to avoid
+ ** queueing up the same envelopes again.
+ ** This requires that envelopes in that list have
+ ** been take care of before (or at some other place).
+ */
+
+ oldsib = e->e_sibling;
+ e->e_sibling = NULL;
+ if (!split_by_recipient(e) &&
+ bitset(EF_FATALERRS, e->e_flags))
+ {
+ syserr("!dropenvelope(%s): cannot commit data file %s, uid=%d",
+ e->e_id, queuename(e, DATAFL_LETTER),
+ geteuid());
+ }
+ for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
+ queueup(ee, false, true);
+ queueup(e, false, true);
+
+ /* clean up */
+ for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
+ {
+ /* now unlock the job */
+ if (tTd(50, 8))
+ sm_dprintf("dropenvelope(%s): unlocking job\n",
+ ee->e_id);
+ closexscript(ee);
+ unlockqueue(ee);
+
+ /* this envelope is marked unused */
+ if (ee->e_dfp != NULL)
+ {
+ (void) sm_io_close(ee->e_dfp,
+ SM_TIME_DEFAULT);
+ ee->e_dfp = NULL;
+ }
+ ee->e_id = NULL;
+ ee->e_flags &= ~EF_HAS_DF;
+ }
+ e->e_sibling = oldsib;
+ }
}
/* now unlock the job */
if (tTd(50, 8))
- dprintf("dropenvelope(%s): unlocking job\n", id);
+ sm_dprintf("dropenvelope(%s): unlocking job\n", id);
closexscript(e);
unlockqueue(e);
/* make sure that this envelope is marked unused */
if (e->e_dfp != NULL)
- (void) bfclose(e->e_dfp);
- e->e_dfp = NULL;
+ {
+ (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
+ e->e_dfp = NULL;
+ }
e->e_id = NULL;
e->e_flags &= ~EF_HAS_DF;
}
- /*
+/*
** CLEARENVELOPE -- clear an envelope without unlocking
**
** This is normally used by a child process to get a clean
@@ -389,6 +574,9 @@ simpledrop:
** fullclear - if set, the current envelope is total
** garbage and should be ignored; otherwise,
** release any resources it may indicate.
+** rpool -- either NULL, or a pointer to a resource pool
+** from which envelope memory is allocated, and
+** to which envelope resources are attached.
**
** Returns:
** none.
@@ -399,40 +587,81 @@ simpledrop:
*/
void
-clearenvelope(e, fullclear)
+clearenvelope(e, fullclear, rpool)
register ENVELOPE *e;
bool fullclear;
+ SM_RPOOL_T *rpool;
{
register HDR *bh;
register HDR **nhp;
extern ENVELOPE BlankEnvelope;
+ char **p;
if (!fullclear)
{
/* clear out any file information */
if (e->e_xfp != NULL)
- (void) bfclose(e->e_xfp);
+ (void) sm_io_close(e->e_xfp, SM_TIME_DEFAULT);
if (e->e_dfp != NULL)
- (void) bfclose(e->e_dfp);
+ (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
e->e_xfp = e->e_dfp = NULL;
}
- /* now clear out the data */
- STRUCTCOPY(BlankEnvelope, *e);
+ /*
+ ** Copy BlankEnvelope into *e.
+ ** It is not safe to simply copy pointers to strings;
+ ** the strings themselves must be copied (or set to NULL).
+ ** The problem is that when we assign a new string value to
+ ** a member of BlankEnvelope, we free the old string.
+ ** We did not need to do this copying in sendmail 8.11 :-(
+ ** and it is a potential performance hit. Reference counted
+ ** strings are one way out.
+ */
+
+ *e = BlankEnvelope;
e->e_message = NULL;
+#if _FFR_QUARANTINE
+ e->e_qfletter = '\0';
+ e->e_quarmsg = NULL;
+ macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), "");
+#endif /* _FFR_QUARANTINE */
+
+ /*
+ ** Copy the macro table.
+ ** We might be able to avoid this by zeroing the macro table
+ ** and always searching BlankEnvelope.e_macro after e->e_macro
+ ** in macvalue().
+ */
+
+ for (p = &e->e_macro.mac_table[0];
+ p <= &e->e_macro.mac_table[MAXMACROID];
+ ++p)
+ {
+ if (*p != NULL)
+ *p = sm_rpool_strdup_x(rpool, *p);
+ }
+
+ /*
+ ** XXX There are many strings in the envelope structure
+ ** XXX that we are not attempting to copy here.
+ ** XXX Investigate this further.
+ */
+
+ e->e_rpool = rpool;
+ e->e_macro.mac_rpool = rpool;
if (Verbose)
set_delivery_mode(SM_DELIVER, e);
bh = BlankEnvelope.e_header;
nhp = &e->e_header;
while (bh != NULL)
{
- *nhp = (HDR *) xalloc(sizeof *bh);
+ *nhp = (HDR *) sm_rpool_malloc_x(rpool, sizeof *bh);
memmove((char *) *nhp, (char *) bh, sizeof *bh);
bh = bh->h_link;
nhp = &(*nhp)->h_link;
}
}
- /*
+/*
** INITSYS -- initialize instantiation of system
**
** In Daemon mode, this is done in the child.
@@ -453,8 +682,7 @@ void
initsys(e)
register ENVELOPE *e;
{
- char cbuf[5]; /* holds hop count */
- char pbuf[10]; /* holds pid */
+ char buf[10];
#ifdef TTYNAME
static char ybuf[60]; /* holds tty id */
register char *p;
@@ -464,11 +692,14 @@ initsys(e)
/*
** Give this envelope a reality.
** I.e., an id, a transcript, and a creation time.
+ ** We don't select the queue until all of the recipients are known.
*/
- setnewqueue(e);
openxscript(e);
e->e_ctime = curtime();
+#if _FFR_QUARANTINE
+ e->e_qfletter = '\0';
+#endif /* _FFR_QUARANTINE */
#if _FFR_QUEUEDELAY
e->e_queuealg = QueueAlg;
e->e_queuedelay = QueueInitDelay;
@@ -490,18 +721,18 @@ initsys(e)
*/
/* process id */
- (void) snprintf(pbuf, sizeof pbuf, "%d", (int) getpid());
- define('p', newstr(pbuf), e);
+ (void) sm_snprintf(buf, sizeof buf, "%d", (int) CurrentPid);
+ macdefine(&e->e_macro, A_TEMP, 'p', buf);
/* hop count */
- (void) snprintf(cbuf, sizeof cbuf, "%d", e->e_hopcount);
- define('c', newstr(cbuf), e);
+ (void) sm_snprintf(buf, sizeof buf, "%d", e->e_hopcount);
+ macdefine(&e->e_macro, A_TEMP, 'c', buf);
/* time as integer, unix time, arpa time */
settime(e);
/* Load average */
- (void)sm_getla(e);
+ sm_getla();
#ifdef TTYNAME
/* tty name */
@@ -512,13 +743,13 @@ initsys(e)
{
if (strrchr(p, '/') != NULL)
p = strrchr(p, '/') + 1;
- snprintf(ybuf, sizeof ybuf, "%s", p);
- define('y', ybuf, e);
+ (void) sm_strlcpy(ybuf, sizeof ybuf, p);
+ macdefine(&e->e_macro, A_PERM, 'y', ybuf);
}
}
#endif /* TTYNAME */
}
- /*
+/*
** SETTIME -- set the current time.
**
** Parameters:
@@ -537,27 +768,25 @@ settime(e)
{
register char *p;
auto time_t now;
- char tbuf[20]; /* holds "current" time */
- char dbuf[30]; /* holds ctime(tbuf) */
+ char buf[30];
register struct tm *tm;
now = curtime();
tm = gmtime(&now);
- (void) snprintf(tbuf, sizeof tbuf, "%04d%02d%02d%02d%02d", tm->tm_year + 1900,
- tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min);
- define('t', newstr(tbuf), e);
- (void) strlcpy(dbuf, ctime(&now), sizeof dbuf);
- p = strchr(dbuf, '\n');
+ (void) sm_snprintf(buf, sizeof buf, "%04d%02d%02d%02d%02d",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min);
+ macdefine(&e->e_macro, A_TEMP, 't', buf);
+ (void) sm_strlcpy(buf, ctime(&now), sizeof buf);
+ p = strchr(buf, '\n');
if (p != NULL)
*p = '\0';
- define('d', newstr(dbuf), e);
- p = arpadate(dbuf);
- p = newstr(p);
+ macdefine(&e->e_macro, A_TEMP, 'd', buf);
+ macdefine(&e->e_macro, A_TEMP, 'b', arpadate(buf));
if (macvalue('a', e) == NULL)
- define('a', p, e);
- define('b', p, e);
+ macdefine(&e->e_macro, A_PERM, 'a', macvalue('b', e));
}
- /*
+/*
** OPENXSCRIPT -- Open transcript file
**
** Creates a transcript file for possible eventual mailing or
@@ -591,29 +820,27 @@ openxscript(e)
syserr("openxscript: job not locked");
#endif /* 0 */
- p = queuename(e, 'x');
+ p = queuename(e, XSCRPT_LETTER);
e->e_xfp = bfopen(p, FileMode, XscriptFileBufferSize,
SFF_NOTEXCL|SFF_OPENASROOT);
if (e->e_xfp == NULL)
{
syserr("Can't create transcript file %s", p);
- e->e_xfp = fopen("/dev/null", "r+");
+ e->e_xfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
+ SM_PATH_DEVNULL, SM_IO_RDWR, NULL);
if (e->e_xfp == NULL)
- syserr("!Can't open /dev/null");
+ syserr("!Can't open %s", SM_PATH_DEVNULL);
}
-#if HASSETVBUF
- (void) setvbuf(e->e_xfp, NULL, _IOLBF, 0);
-#else /* HASSETVBUF */
- (void) setlinebuf(e->e_xfp);
-#endif /* HASSETVBUF */
+ (void) sm_io_setvbuf(e->e_xfp, SM_TIME_DEFAULT, NULL, SM_IO_LBF, 0);
if (tTd(46, 9))
{
- dprintf("openxscript(%s):\n ", p);
- dumpfd(fileno(e->e_xfp), TRUE, FALSE);
+ sm_dprintf("openxscript(%s):\n ", p);
+ dumpfd(sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL), true,
+ false);
}
}
- /*
+/*
** CLOSEXSCRIPT -- close the transcript file.
**
** Parameters:
@@ -636,10 +863,10 @@ closexscript(e)
if (e->e_lockfp == NULL)
syserr("closexscript: job not locked");
#endif /* 0 */
- (void) bfclose(e->e_xfp);
+ (void) sm_io_close(e->e_xfp, SM_TIME_DEFAULT);
e->e_xfp = NULL;
}
- /*
+/*
** SETSENDER -- set the person who this message is from
**
** Under certain circumstances allow the user to say who
@@ -688,14 +915,13 @@ setsender(from, e, delimptr, delimchar, internal)
{
register char **pvp;
char *realname = NULL;
- register struct passwd *pw;
char *bp;
char buf[MAXNAME + 2];
char pvpbuf[PSBUFSIZE];
extern char *FullName;
if (tTd(45, 1))
- dprintf("setsender(%s)\n", from == NULL ? "" : from);
+ sm_dprintf("setsender(%s)\n", from == NULL ? "" : from);
/*
** Figure out the real user executing us.
@@ -709,17 +935,16 @@ setsender(from, e, delimptr, delimchar, internal)
realname = username();
if (ConfigLevel < 2)
- SuprErrs = TRUE;
+ SuprErrs = true;
+
+ macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e s");
-#if _FFR_ADDR_TYPE
- define(macid("{addr_type}", NULL), "e s", e);
-#endif /* _FFR_ADDR_TYPE */
/* preset state for then clause in case from == NULL */
e->e_from.q_state = QS_BADADDR;
e->e_from.q_flags = 0;
if (from == NULL ||
parseaddr(from, &e->e_from, RF_COPYALL|RF_SENDERADDR,
- delimchar, delimptr, e) == NULL ||
+ delimchar, delimptr, e, false) == NULL ||
QS_IS_BADADDR(e->e_from.q_state) ||
e->e_from.q_mailer == ProgMailer ||
e->e_from.q_mailer == FileMailer ||
@@ -738,9 +963,9 @@ setsender(from, e, delimptr, delimchar, internal)
if (host == NULL)
host = MyHostName;
- (void) snprintf(ebuf, sizeof ebuf, "%.*s@%.*s",
- MAXNAME, realname,
- MAXNAME, host);
+ (void) sm_snprintf(ebuf, sizeof ebuf,
+ "%.*s@%.*s", MAXNAME,
+ realname, MAXNAME, host);
p = ebuf;
}
sm_syslog(LOG_NOTICE, e->e_id,
@@ -756,39 +981,41 @@ setsender(from, e, delimptr, delimchar, internal)
usrerrenh(e->e_status,
"553 Invalid sender address");
}
- SuprErrs = TRUE;
+ SuprErrs = true;
}
if (from == realname ||
- parseaddr(from = newstr(realname), &e->e_from,
- RF_COPYALL|RF_SENDERADDR, ' ', NULL, e) == NULL)
+ parseaddr(from = realname,
+ &e->e_from, RF_COPYALL|RF_SENDERADDR, ' ',
+ NULL, e, false) == NULL)
{
char nbuf[100];
- SuprErrs = TRUE;
+ SuprErrs = true;
expand("\201n", nbuf, sizeof nbuf, e);
- if (parseaddr(from = newstr(nbuf), &e->e_from,
- RF_COPYALL, ' ', NULL, e) == NULL &&
+ from = sm_rpool_strdup_x(e->e_rpool, nbuf);
+ if (parseaddr(from, &e->e_from, RF_COPYALL, ' ',
+ NULL, e, false) == NULL &&
parseaddr(from = "postmaster", &e->e_from,
- RF_COPYALL, ' ', NULL, e) == NULL)
+ RF_COPYALL, ' ', NULL, e, false) == NULL)
syserr("553 5.3.0 setsender: can't even parse postmaster!");
}
}
else
- FromFlag = TRUE;
+ FromFlag = true;
e->e_from.q_state = QS_SENDER;
if (tTd(45, 5))
{
- dprintf("setsender: QS_SENDER ");
- printaddr(&e->e_from, FALSE);
+ sm_dprintf("setsender: QS_SENDER ");
+ printaddr(&e->e_from, false);
}
- SuprErrs = FALSE;
+ SuprErrs = false;
#if USERDB
if (bitnset(M_CHECKUDB, e->e_from.q_mailer->m_flags))
{
register char *p;
- p = udbsender(e->e_from.q_user);
+ p = udbsender(e->e_from.q_user, e->e_rpool);
if (p != NULL)
from = p;
}
@@ -796,6 +1023,8 @@ setsender(from, e, delimptr, delimchar, internal)
if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
{
+ SM_MBDB_T user;
+
if (!internal)
{
/* if the user already given fullname don't redefine */
@@ -806,34 +1035,36 @@ setsender(from, e, delimptr, delimchar, internal)
}
if (e->e_from.q_user[0] != '\0' &&
- (pw = sm_getpwnam(e->e_from.q_user)) != NULL)
+ sm_mbdb_lookup(e->e_from.q_user, &user) == EX_OK)
{
/*
** Process passwd file entry.
*/
/* extract home directory */
- if (*pw->pw_dir == '\0')
+ if (*user.mbdb_homedir == '\0')
e->e_from.q_home = NULL;
- else if (strcmp(pw->pw_dir, "/") == 0)
- e->e_from.q_home = newstr("");
+ else if (strcmp(user.mbdb_homedir, "/") == 0)
+ e->e_from.q_home = "";
else
- e->e_from.q_home = newstr(pw->pw_dir);
- define('z', e->e_from.q_home, e);
+ e->e_from.q_home = sm_rpool_strdup_x(e->e_rpool,
+ user.mbdb_homedir);
+ macdefine(&e->e_macro, A_PERM, 'z', e->e_from.q_home);
/* extract user and group id */
- e->e_from.q_uid = pw->pw_uid;
- e->e_from.q_gid = pw->pw_gid;
- e->e_from.q_flags |= QGOODUID;
+ if (user.mbdb_uid != SM_NO_UID)
+ {
+ e->e_from.q_uid = user.mbdb_uid;
+ e->e_from.q_gid = user.mbdb_gid;
+ e->e_from.q_flags |= QGOODUID;
+ }
/* extract full name from passwd file */
- if (FullName == NULL && pw->pw_gecos != NULL &&
- strcmp(pw->pw_name, e->e_from.q_user) == 0 &&
- !internal)
+ if (FullName == NULL && !internal &&
+ user.mbdb_fullname[0] != '\0' &&
+ strcmp(user.mbdb_name, e->e_from.q_user) == 0)
{
- buildfname(pw->pw_gecos, e->e_from.q_user, buf, sizeof buf);
- if (buf[0] != '\0')
- FullName = newstr(buf);
+ FullName = newstr(user.mbdb_fullname);
}
}
else
@@ -841,7 +1072,7 @@ setsender(from, e, delimptr, delimchar, internal)
e->e_from.q_home = NULL;
}
if (FullName != NULL && !internal)
- define('x', FullName, e);
+ macdefine(&e->e_macro, A_PERM, 'x', FullName);
}
else if (!internal && OpMode != MD_DAEMON && OpMode != MD_SMTP)
{
@@ -874,24 +1105,22 @@ setsender(from, e, delimptr, delimchar, internal)
sm_syslog(LOG_NOTICE, e->e_id,
"cannot prescan from (%s)",
shortenstring(from, MAXSHORTSTR));
- finis(TRUE, ExitStat);
+ finis(true, true, ExitStat);
}
- (void) rewrite(pvp, 3, 0, e);
- (void) rewrite(pvp, 1, 0, e);
- (void) rewrite(pvp, 4, 0, e);
-#if _FFR_ADDR_TYPE
- define(macid("{addr_type}", NULL), NULL, e);
-#endif /* _FFR_ADDR_TYPE */
+ (void) REWRITE(pvp, 3, e);
+ (void) REWRITE(pvp, 1, e);
+ (void) REWRITE(pvp, 4, e);
+ macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), NULL);
bp = buf + 1;
cataddr(pvp, NULL, bp, sizeof buf - 2, '\0');
if (*bp == '@' && !bitnset(M_NOBRACKET, e->e_from.q_mailer->m_flags))
{
/* heuristic: route-addr: add angle brackets */
- (void) strlcat(bp, ">", sizeof buf - 1);
+ (void) sm_strlcat(bp, ">", sizeof buf - 1);
*--bp = '<';
}
- e->e_sender = newstr(bp);
- define('f', e->e_sender, e);
+ e->e_sender = sm_rpool_strdup_x(e->e_rpool, bp);
+ macdefine(&e->e_macro, A_PERM, 'f', e->e_sender);
/* save the domain spec if this mailer wants it */
if (e->e_from.q_mailer != NULL &&
@@ -900,15 +1129,11 @@ setsender(from, e, delimptr, delimchar, internal)
char **lastat;
/* get rid of any pesky angle brackets */
-#if _FFR_ADDR_TYPE
- define(macid("{addr_type}", NULL), "e s", e);
-#endif /* _FFR_ADDR_TYPE */
- (void) rewrite(pvp, 3, 0, e);
- (void) rewrite(pvp, 1, 0, e);
- (void) rewrite(pvp, 4, 0, e);
-#if _FFR_ADDR_TYPE
- define(macid("{addr_type}", NULL), NULL, e);
-#endif /* _FFR_ADDR_TYPE */
+ macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e s");
+ (void) REWRITE(pvp, 3, e);
+ (void) REWRITE(pvp, 1, e);
+ (void) REWRITE(pvp, 4, e);
+ macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), NULL);
/* strip off to the last "@" sign */
for (lastat = NULL; *pvp != NULL; pvp++)
@@ -916,16 +1141,16 @@ setsender(from, e, delimptr, delimchar, internal)
lastat = pvp;
if (lastat != NULL)
{
- e->e_fromdomain = copyplist(lastat, TRUE);
+ e->e_fromdomain = copyplist(lastat, true, e->e_rpool);
if (tTd(45, 3))
{
- dprintf("Saving from domain: ");
+ sm_dprintf("Saving from domain: ");
printav(e->e_fromdomain);
}
}
}
}
- /*
+/*
** PRINTENVFLAGS -- print envelope flags for debugging
**
** Parameters:
@@ -937,8 +1162,8 @@ setsender(from, e, delimptr, delimchar, internal)
struct eflags
{
- char *ef_name;
- u_long ef_bit;
+ char *ef_name;
+ unsigned long ef_bit;
};
static struct eflags EnvelopeFlags[] =
@@ -967,6 +1192,10 @@ static struct eflags EnvelopeFlags[] =
{ "HAS_DF", EF_HAS_DF },
{ "IS_MIME", EF_IS_MIME },
{ "DONT_MIME", EF_DONT_MIME },
+ { "DISCARD", EF_DISCARD },
+ { "TOOBIG", EF_TOOBIG },
+ { "SPLIT", EF_SPLIT },
+ { "UNSAFE", EF_UNSAFE },
{ NULL, 0 }
};
@@ -975,19 +1204,21 @@ printenvflags(e)
register ENVELOPE *e;
{
register struct eflags *ef;
- bool first = TRUE;
+ bool first = true;
- printf("%lx", e->e_flags);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%lx", e->e_flags);
for (ef = EnvelopeFlags; ef->ef_name != NULL; ef++)
{
if (!bitset(ef->ef_bit, e->e_flags))
continue;
if (first)
- printf("<%s", ef->ef_name);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "<%s",
+ ef->ef_name);
else
- printf(",%s", ef->ef_name);
- first = FALSE;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, ",%s",
+ ef->ef_name);
+ first = false;
}
if (!first)
- printf(">\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, ">\n");
}
diff --git a/contrib/sendmail/src/helpfile b/contrib/sendmail/src/helpfile
index 5731626..02e6138 100644
--- a/contrib/sendmail/src/helpfile
+++ b/contrib/sendmail/src/helpfile
@@ -11,7 +11,7 @@ cpyr By using this file, you agree to the terms and conditions set
cpyr forth in the LICENSE file which can be found at the top level of
cpyr the sendmail distribution.
cpyr
-cpyr $$Id: helpfile,v 8.31.16.4 2000/09/17 14:21:00 ca Exp $$
+cpyr $$Id: helpfile,v 8.38 2000/10/15 17:18:44 ca Exp $$
cpyr
smtp This is sendmail version $v
smtp Topics:
@@ -39,7 +39,6 @@ ehlo TURN Turn the operation around [RFC821]
ehlo 8BITMIME Use 8-bit data [RFC1652]
ehlo SIZE Message size declaration [RFC1870]
ehlo VERB Verbose [Allman]
-ehlo ONEX One message transaction only [Allman]
ehlo CHUNKING Chunking [RFC1830]
ehlo BINARYMIME Binary MIME [RFC1830]
ehlo PIPELINING Command Pipelining [RFC1854]
@@ -47,8 +46,8 @@ ehlo DSN Delivery Status Notification [RFC1891]
ehlo ETRN Remote Message Queue Starting [RFC1985]
ehlo STARTTLS Secure SMTP [RFC2487]
ehlo AUTH Authentication [RFC2554]
-ehlo XUSR Initial (user) submission [Allman]
ehlo ENHANCEDSTATUSCODES Enhanced status codes [RFC2034]
+ehlo DELIVERBY Deliver By [RFC2852]
mail MAIL FROM: <sender> [ <parameters> ]
mail Specifies the sender. Parameters are ESMTP extensions.
mail See "HELP DSN" for details.
@@ -134,3 +133,4 @@ control help This message.
control restart Restart sendmail.
control shutdown Shutdown sendmail.
control status Show sendmail status.
+control memdump Dump allocated memory list.
diff --git a/contrib/sendmail/src/macro.c b/contrib/sendmail/src/macro.c
index 70e18e4..fc7a2c2 100644
--- a/contrib/sendmail/src/macro.c
+++ b/contrib/sendmail/src/macro.c
@@ -11,19 +11,89 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: macro.c,v 8.40.16.9 2001/02/22 01:16:55 gshapiro Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
+SM_RCSID("@(#)$Id: macro.c,v 8.86 2001/09/11 04:05:14 gshapiro Exp $")
+
#if MAXMACROID != (BITMAPBITS - 1)
ERROR Read the comment in conf.h
#endif /* MAXMACROID != (BITMAPBITS - 1) */
-char *MacroName[MAXMACROID + 1]; /* macro id to name table */
-int NextMacroId = 0240; /* codes for long named macros */
+static char *MacroName[MAXMACROID + 1]; /* macro id to name table */
+int NextMacroId = 0240; /* codes for long named macros */
+
+/*
+** INITMACROS -- initialize the macro system
+**
+** This just involves defining some macros that are actually
+** used internally as metasymbols to be themselves.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** initializes several macros to be themselves.
+*/
+
+struct metamac MetaMacros[] =
+{
+ /* LHS pattern matching characters */
+ { '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE },
+ { '=', MATCHCLASS }, { '~', MATCHNCLASS },
+
+ /* these are RHS metasymbols */
+ { '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER },
+ { '>', CALLSUBR },
+
+ /* the conditional operations */
+ { '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI },
+
+ /* the hostname lookup characters */
+ { '[', HOSTBEGIN }, { ']', HOSTEND },
+ { '(', LOOKUPBEGIN }, { ')', LOOKUPEND },
+
+ /* miscellaneous control characters */
+ { '&', MACRODEXPAND },
+
+ { '\0', '\0' }
+};
+
+#define MACBINDING(name, mid) \
+ stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
+ MacroName[mid] = name;
+void
+initmacros(e)
+ register ENVELOPE *e;
+{
+ register struct metamac *m;
+ register int c;
+ char buf[5];
+
+ for (m = MetaMacros; m->metaname != '\0'; m++)
+ {
+ buf[0] = m->metaval;
+ buf[1] = '\0';
+ macdefine(&e->e_macro, A_TEMP, m->metaname, buf);
+ }
+ buf[0] = MATCHREPL;
+ buf[2] = '\0';
+ for (c = '0'; c <= '9'; c++)
+ {
+ buf[1] = c;
+ macdefine(&e->e_macro, A_TEMP, c, buf);
+ }
+
+ /* set defaults for some macros sendmail will use later */
+ macdefine(&e->e_macro, A_PERM, 'n', "MAILER-DAEMON");
+
+ /* set up external names for some internal macros */
+ MACBINDING("opMode", MID_OPMODE);
+ /*XXX should probably add equivalents for all short macros here XXX*/
+}
/*
** EXPAND -- macro expand a string using $x escapes.
**
@@ -50,8 +120,8 @@ expand(s, buf, bufsize, e)
register char *xp;
register char *q;
bool skipping; /* set if conditionally skipping output */
- bool recurse = FALSE; /* set if recursion required */
- int i;
+ bool recurse; /* set if recursion required */
+ size_t i;
int skiplev; /* skipping nesting level */
int iflev; /* if nesting level */
char xbuf[MACBUFSIZE];
@@ -59,12 +129,13 @@ expand(s, buf, bufsize, e)
if (tTd(35, 24))
{
- dprintf("expand(");
+ sm_dprintf("expand(");
xputs(s);
- dprintf(")\n");
+ sm_dprintf(")\n");
}
- skipping = FALSE;
+ recurse = false;
+ skipping = false;
skiplev = 0;
iflev = 0;
if (s == NULL)
@@ -98,17 +169,17 @@ expand(s, buf, bufsize, e)
case CONDELSE: /* change state of skipping */
if (iflev == 0)
- break;
+ break; /* XXX: error */
if (skiplev == 0)
skipping = !skipping;
continue;
case CONDFI: /* stop skipping */
if (iflev == 0)
- break;
+ break; /* XXX: error */
iflev--;
if (skiplev == 0)
- skipping = FALSE;
+ skipping = false;
if (skipping)
skiplev--;
continue;
@@ -142,7 +213,7 @@ expand(s, buf, bufsize, e)
{
/* check for any sendmail metacharacters */
if ((c & 0340) == 0200)
- recurse = TRUE;
+ recurse = true;
*xp++ = c;
}
}
@@ -151,9 +222,9 @@ expand(s, buf, bufsize, e)
if (tTd(35, 24))
{
- dprintf("expand ==> ");
+ sm_dprintf("expand ==> ");
xputs(xbuf);
- dprintf("\n");
+ sm_dprintf("\n");
}
/* recurse as appropriate */
@@ -172,109 +243,155 @@ expand(s, buf, bufsize, e)
/* copy results out */
i = xp - xbuf;
- if ((size_t)i >= bufsize)
+ if (i >= bufsize)
i = bufsize - 1;
memmove(buf, xbuf, i);
buf[i] = '\0';
}
- /*
-** DEFINE -- define a macro.
+
+/*
+** MACDEFINE -- bind a macro name to a value
**
-** this would be better done using a #define macro.
+** Set a macro to a value, with fancy storage management.
+** macdefine will make a copy of the value, if required,
+** and will ensure that the storage for the previous value
+** is not leaked.
**
** Parameters:
-** n -- the macro name.
-** v -- the macro value.
-** e -- the envelope to store the definition in.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** e->e_macro[n] is defined.
-**
-** Notes:
-** There is one macro for each ASCII character,
-** although they are not all used. The currently
-** defined macros are:
-**
-** $a date in ARPANET format (preferring the Date: line
-** of the message)
-** $b the current date (as opposed to the date as found
-** the message) in ARPANET format
-** $c hop count
-** $d (current) date in UNIX (ctime) format
-** $e the SMTP entry message+
-** $f raw from address
-** $g translated from address
-** $h to host
-** $i queue id
-** $j official SMTP hostname, used in messages+
-** $k UUCP node name
-** $l UNIX-style from line+
-** $m The domain part of our full name.
-** $n name of sendmail ("MAILER-DAEMON" on local
-** net typically)+
-** $o delimiters ("operators") for address tokens+
-** (set via OperatorChars option in V6 or later
-** sendmail.cf files)
-** $p my process id in decimal
-** $q the string that becomes an address -- this is
-** normally used to combine $g & $x.
-** $r protocol used to talk to sender
-** $s sender's host name
-** $t the current time in seconds since 1/1/1970
-** $u to user
-** $v version number of sendmail
-** $w our host name (if it can be determined)
-** $x signature (full name) of from person
-** $y the tty id of our terminal
-** $z home directory of to person
-** $_ RFC1413 authenticated sender address
-**
-** Macros marked with + must be defined in the
-** configuration file and are used internally, but
-** are not set.
-**
-** There are also some macros that can be used
-** arbitrarily to make the configuration file
-** cleaner. In general all upper-case letters
-** are available.
+** mac -- Macro table.
+** vclass -- storage class of 'value', ignored if value==NULL.
+** A_HEAP means that the value was allocated by
+** malloc, and that macdefine owns the storage.
+** A_TEMP means that value points to temporary storage,
+** and thus macdefine needs to make a copy.
+** A_PERM means that value points to storage that
+** will remain allocated and unchanged for
+** at least the lifetime of mac. Use A_PERM if:
+** -- value == NULL,
+** -- value points to a string literal,
+** -- value was allocated from mac->mac_rpool
+** or (in the case of an envelope macro)
+** from e->e_rpool,
+** -- in the case of an envelope macro,
+** value is a string member of the envelope
+** such as e->e_sender.
+** id -- Macro id. This is a single character macro name
+** such as 'g', or a value returned by macid().
+** value -- Macro value: either NULL, or a string.
*/
void
-define(n, v, e)
- int n;
- char *v;
- register ENVELOPE *e;
+#if SM_HEAP_CHECK
+macdefine_tagged(mac, vclass, id, value, file, line, grp)
+#else /* SM_HEAP_CHECK */
+macdefine(mac, vclass, id, value)
+#endif /* SM_HEAP_CHECK */
+ MACROS_T *mac;
+ ARGCLASS_T vclass;
+ int id;
+ char *value;
+#if SM_HEAP_CHECK
+ char *file;
+ int line;
+ int grp;
+#endif /* SM_HEAP_CHECK */
{
- int m;
+ char *newvalue;
+
+ if (id < 0 || id > MAXMACROID)
+ return;
- m = bitidx(n);
if (tTd(35, 9))
{
- dprintf("%sdefine(%s as ",
- (e->e_macro[m] == NULL) ? ""
- : "re", macname(n));
- xputs(v);
- dprintf(")\n");
+ sm_dprintf("%sdefine(%s as ",
+ mac->mac_table[id] == NULL ? "" : "re", macname(id));
+ xputs(value);
+ sm_dprintf(")\n");
+ }
+
+ if (mac->mac_rpool == NULL)
+ {
+ char *freeit = NULL;
+
+ if (mac->mac_table[id] != NULL &&
+ bitnset(id, mac->mac_allocated))
+ freeit = mac->mac_table[id];
+
+ if (value == NULL || vclass == A_HEAP)
+ {
+ sm_heap_checkptr_tagged(value, file, line);
+ newvalue = value;
+ clrbitn(id, mac->mac_allocated);
+ }
+ else
+ {
+ newvalue = sm_strdup_tagged_x(value, file, line, 0);
+ setbitn(id, mac->mac_allocated);
+ }
+ mac->mac_table[id] = newvalue;
+ if (freeit != NULL)
+ sm_free(freeit);
+ }
+ else
+ {
+ if (value == NULL || vclass == A_PERM)
+ newvalue = value;
+ else
+ newvalue = sm_rpool_strdup_x(mac->mac_rpool, value);
+ mac->mac_table[id] = newvalue;
+ if (vclass == A_HEAP)
+ sm_free(value);
}
- e->e_macro[m] = v;
#if _FFR_RESET_MACRO_GLOBALS
- switch (m)
+ switch (id)
{
case 'j':
- MyHostName = v;
+ PSTRSET(MyHostName, value);
break;
}
#endif /* _FFR_RESET_MACRO_GLOBALS */
}
- /*
+
+/*
+** MACSET -- set a named macro to a value (low level)
+**
+** No fancy storage management; the caller takes full responsibility.
+** Often used with macget; see also macdefine.
+**
+** Parameters:
+** mac -- Macro table.
+** i -- Macro name, specified as an integer offset.
+** value -- Macro value: either NULL, or a string.
+*/
+
+void
+macset(mac, i, value)
+ MACROS_T *mac;
+ int i;
+ char *value;
+{
+ if (i < 0 || i > MAXMACROID)
+ return;
+
+ if (tTd(35, 9))
+ {
+ sm_dprintf("macset(%s as ", macname(i));
+ xputs(value);
+ sm_dprintf(")\n");
+ }
+ mac->mac_table[i] = value;
+}
+
+/*
** MACVALUE -- return uninterpreted value of a macro.
**
+** Does fancy path searching.
+** The low level counterpart is macget.
+**
** Parameters:
** n -- the name of the macro.
+** e -- envelope in which to start looking for the macro.
**
** Returns:
** The value of n.
@@ -289,9 +406,16 @@ macvalue(n, e)
register ENVELOPE *e;
{
n = bitidx(n);
+ if (e != NULL && e->e_mci != NULL)
+ {
+ register char *p = e->e_mci->mci_macro.mac_table[n];
+
+ if (p != NULL)
+ return p;
+ }
while (e != NULL)
{
- register char *p = e->e_macro[n];
+ register char *p = e->e_macro.mac_table[n];
if (p != NULL)
return p;
@@ -299,9 +423,9 @@ macvalue(n, e)
break;
e = e->e_parent;
}
- return NULL;
+ return GlobalMacros.mac_table[n];
}
- /*
+/*
** MACNAME -- return the name of a macro given its internal id
**
** Parameter:
@@ -333,8 +457,8 @@ macname(n)
mbuf[1] = '\0';
return mbuf;
}
- /*
-** MACID -- return id of macro identified by its name
+/*
+** MACID_PARSE -- return id of macro identified by its name
**
** Parameters:
** p -- pointer to name string -- either a single
@@ -343,15 +467,16 @@ macname(n)
** after the name.
**
** Returns:
-** The internal id code for this macro. This will
-** fit into a single byte.
+** 0 -- An error was detected.
+** 1..255 -- The internal id code for this macro.
**
** Side Effects:
** If this is a new macro name, a new id is allocated.
+** On error, syserr is called.
*/
int
-macid(p, ep)
+macid_parse(p, ep)
register char *p;
char **ep;
{
@@ -361,9 +486,9 @@ macid(p, ep)
if (tTd(35, 14))
{
- dprintf("macid(");
+ sm_dprintf("macid(");
xputs(p);
- dprintf(") => ");
+ sm_dprintf(") => ");
}
if (*p == '\0' || (p[0] == '{' && p[1] == '}'))
@@ -372,7 +497,7 @@ macid(p, ep)
if (ep != NULL)
*ep = p;
if (tTd(35, 14))
- dprintf("NULL\n");
+ sm_dprintf("NULL\n");
return 0;
}
if (*p != '{')
@@ -381,7 +506,7 @@ macid(p, ep)
if (ep != NULL)
*ep = p + 1;
if (tTd(35, 14))
- dprintf("%c\n", bitidx(*p));
+ sm_dprintf("%c\n", bitidx(*p));
return bitidx(*p);
}
bp = mbuf;
@@ -401,7 +526,7 @@ macid(p, ep)
else if (*p != '}')
{
syserr("Macro/class name ({%s}) too long (%d chars max)",
- mbuf, sizeof mbuf - 1);
+ mbuf, (int) (sizeof mbuf - 1));
}
else if (mbuf[1] == '\0')
{
@@ -420,7 +545,8 @@ macid(p, ep)
{
if (NextMacroId > MAXMACROID)
{
- syserr("Macro/class {%s}: too many long names", mbuf);
+ syserr("Macro/class {%s}: too many long names",
+ mbuf);
s->s_macro = -1;
}
else
@@ -437,14 +563,14 @@ macid(p, ep)
{
syserr("Unable to assign macro/class ID (mid = 0x%x)", mid);
if (tTd(35, 14))
- dprintf("NULL\n");
+ sm_dprintf("NULL\n");
return 0;
}
if (tTd(35, 14))
- dprintf("0x%x\n", mid);
+ sm_dprintf("0x%x\n", mid);
return mid;
}
- /*
+/*
** WORDINCLASS -- tell if a word is in a specific class
**
** Parameters:
@@ -452,8 +578,8 @@ macid(p, ep)
** cl -- the class name.
**
** Returns:
-** TRUE if str can be found in cl.
-** FALSE otherwise.
+** true if str can be found in cl.
+** false otherwise.
*/
bool
diff --git a/contrib/sendmail/src/main.c b/contrib/sendmail/src/main.c
index c09daa5..3d3f3cf 100644
--- a/contrib/sendmail/src/main.c
+++ b/contrib/sendmail/src/main.c
@@ -11,8 +11,13 @@
*
*/
+#define _DEFINE
+#include <sendmail.h>
+#include <sm/xtrap.h>
+#include <sm/signal.h>
+
#ifndef lint
-static char copyright[] =
+SM_UNUSED(static char copyright[]) =
"@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
All rights reserved.\n\
Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.\n\
@@ -20,28 +25,32 @@ static char copyright[] =
The Regents of the University of California. All rights reserved.\n";
#endif /* ! lint */
-#ifndef lint
-static char id[] = "@(#)$Id: main.c,v 8.485.4.65 2001/07/20 00:53:00 gshapiro Exp $";
-#endif /* ! lint */
-
-#define _DEFINE
-
-#include <sendmail.h>
+SM_RCSID("@(#)$Id: main.c,v 8.868 2001/12/29 04:54:38 ca Exp $")
#if NETINET || NETINET6
# include <arpa/inet.h>
#endif /* NETINET || NETINET6 */
-static SIGFUNC_DECL intindebug __P((int));
-static SIGFUNC_DECL quiesce __P((int));
-#ifdef SIGUSR1
-static SIGFUNC_DECL sigusr1 __P((int));
-# endif /* SIGUSR1 */
-static SIGFUNC_DECL term_daemon __P((int));
+/* for getcfname() */
+#include <sendmail/pathnames.h>
+
+static SM_DEBUG_T
+DebugNoPRestart = SM_DEBUG_INITIALIZER("no_persistent_restart",
+ "@(#)$Debug: no_persistent_restart - don't restart, log only $");
+
static void dump_class __P((STAB *, int));
static void obsolete __P((char **));
static void testmodeline __P((char *, ENVELOPE *));
+static char *getextenv __P((const char *));
+static void sm_printoptions __P((char **));
+static SIGFUNC_DECL intindebug __P((int));
+static SIGFUNC_DECL sighup __P((int));
+static SIGFUNC_DECL sigpipe __P((int));
+static SIGFUNC_DECL sigterm __P((int));
+#ifdef SIGUSR1
+static SIGFUNC_DECL sigusr1 __P((int));
+#endif /* SIGUSR1 */
/*
** SENDMAIL -- Post mail to a set of destinations.
@@ -59,7 +68,7 @@ static void testmodeline __P((char *, ENVELOPE *));
**
** See the associated documentation for details.
**
-** Author:
+** Authors:
** Eric Allman, UCB/INGRES (until 10/81).
** Britton-Lee, Inc., purveyors of fine
** database computers (11/81 - 10/88).
@@ -71,31 +80,30 @@ static void testmodeline __P((char *, ENVELOPE *));
** The support of the my employers is gratefully acknowledged.
** Few of them (Britton-Lee in particular) have had
** anything to gain from my involvement in this project.
+**
+** Gregory Neil Shapiro,
+** Worcester Polytechnic Institute (until 3/98).
+** Sendmail, Inc. (3/98 - present).
+**
+** Claus Assmann,
+** Sendmail, Inc. (12/98 - present).
*/
-
-int NextMailer; /* "free" index into Mailer struct */
char *FullName; /* sender's full name */
ENVELOPE BlankEnvelope; /* a "blank" envelope */
static ENVELOPE MainEnvelope; /* the envelope around the basic letter */
ADDRESS NullAddress = /* a null address */
{ "", "", NULL, "" };
char *CommandLineArgs; /* command line args for pid file */
-bool Warn_Q_option = FALSE; /* warn about Q option use */
+bool Warn_Q_option = false; /* warn about Q option use */
static int MissingFds = 0; /* bit map of fds missing on startup */
+char *Mbdb = "pw"; /* mailbox database defaults to /etc/passwd */
#ifdef NGROUPS_MAX
GIDSET_T InitialGidSet[NGROUPS_MAX];
#endif /* NGROUPS_MAX */
-#if DAEMON && !SMTP
-ERROR %%%% Cannot have DAEMON mode without SMTP %%%% ERROR
-#endif /* DAEMON && !SMTP */
-#if SMTP && !QUEUE
-ERROR %%%% Cannot have SMTP mode without QUEUE %%%% ERROR
-#endif /* SMTP && !QUEUE */
-
-#define MAXCONFIGLEVEL 9 /* highest config version level known */
+#define MAXCONFIGLEVEL 10 /* highest config version level known */
#if SASL
static sasl_callback_t srvcallbacks[] =
@@ -104,10 +112,37 @@ static sasl_callback_t srvcallbacks[] =
{ SASL_CB_PROXY_POLICY, &proxy_policy, NULL },
{ SASL_CB_LIST_END, NULL, NULL }
};
-
#endif /* SASL */
-int SubmitMode;
+unsigned int SubmitMode;
+int SyslogPrefixLen; /* estimated length of syslog prefix */
+#define PIDLEN 6 /* pid length for computing SyslogPrefixLen */
+#ifndef SL_FUDGE
+# define SL_FUDGE 10 /* fudge offset for SyslogPrefixLen */
+#endif /* ! SL_FUDGE */
+#define SLDLL 8 /* est. length of default syslog label */
+
+
+/* Some options are dangerous to allow users to use in non-submit mode */
+#define CHECK_AGAINST_OPMODE(cmd) \
+{ \
+ if (extraprivs && \
+ OpMode != MD_DELIVER && OpMode != MD_SMTP && \
+ OpMode != MD_VERIFY && OpMode != MD_TEST) \
+ { \
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, \
+ "WARNING: Ignoring submission mode -%c option (not in submission mode)\n", \
+ (cmd)); \
+ break; \
+ } \
+ if (extraprivs && queuerun) \
+ { \
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, \
+ "WARNING: Ignoring submission mode -%c option with -q\n", \
+ (cmd)); \
+ break; \
+ } \
+}
int
main(argc, argv, envp)
@@ -123,32 +158,60 @@ main(argc, argv, envp)
register int i;
int j;
int dp;
- bool safecf = TRUE;
+ int fill_errno;
+ int qgrp = NOQGRP; /* queue group to process */
+ bool safecf = true;
BITMAP256 *p_flags = NULL; /* daemon flags */
- bool warn_C_flag = FALSE;
- bool auth = TRUE; /* whether to set e_auth_param */
+ bool warn_C_flag = false;
+ bool auth = true; /* whether to set e_auth_param */
char warn_f_flag = '\0';
- bool run_in_foreground = FALSE; /* -bD mode */
- static bool reenter = FALSE;
+ bool run_in_foreground = false; /* -bD mode */
+ bool queuerun = false, debug = false;
struct passwd *pw;
struct hostent *hp;
char *nullserver = NULL;
char *authinfo = NULL;
char *sysloglabel = NULL; /* label for syslog */
- bool forged;
+ char *conffile = NULL; /* name of .cf file */
+ char *queuegroup = NULL; /* queue group to process */
+#if _FFR_QUARANTINE
+ char *quarantining = NULL; /* quarantine queue items? */
+#endif /* _FFR_QUARANTINE */
+ bool extraprivs;
+ bool forged, negate;
+ bool queuepersistent = false; /* queue runner process runs forever */
+ bool foregroundqueue = false; /* queue run in foreground */
+ bool save_val; /* to save some bool var. */
+ int cftype; /* which cf file to use? */
+ static time_t starttime = 0; /* when was process started */
struct stat traf_st; /* for TrafficLog FIFO check */
+ char buf[MAXLINE];
char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */
static char rnamebuf[MAXNAME]; /* holds RealUserName */
char *emptyenviron[1];
-# if STARTTLS
+#if STARTTLS
bool tls_ok;
-# endif /* STARTTLS */
+#endif /* STARTTLS */
QUEUE_CHAR *new;
+ ENVELOPE *e;
extern int DtableSize;
extern int optind;
extern int opterr;
extern char *optarg;
extern char **environ;
+#if SASL
+ extern void sm_sasl_init __P((void));
+#endif /* SASL */
+
+#if USE_ENVIRON
+ envp = environ;
+#endif /* USE_ENVIRON */
+
+ /* turn off profiling */
+ SM_PROF(0);
+
+ /* install default exception handler */
+ sm_exc_newthread(fatal_error);
/*
** Check to see if we reentered.
@@ -156,27 +219,27 @@ main(argc, argv, envp)
** were NULL when invoked.
*/
- if (reenter)
+ if (starttime != 0)
{
syserr("main: reentered!");
abort();
}
- reenter = TRUE;
+ starttime = curtime();
/* avoid null pointer dereferences */
TermEscape.te_rv_on = TermEscape.te_rv_off = "";
- /*
- ** Seed the random number generator.
- ** Used for queue file names, picking a queue directory, and
- ** MX randomization.
- */
+ RealUid = getuid();
+ RealGid = getgid();
- seed_random();
+ /* Check if sendmail is running with extra privs */
+ extraprivs = (RealUid != 0 &&
+ (geteuid() != getuid() || getegid() != getgid()));
- /* do machine-dependent initializations */
- init_md(argc, argv);
+ CurrentPid = getpid();
+ /* get whatever .cf file is right for the opmode */
+ cftype = SM_GET_RIGHT_CF;
/* in 4.4BSD, the table can be huge; impose a reasonable limit */
DtableSize = getdtsize();
@@ -188,39 +251,51 @@ main(argc, argv, envp)
** But also be sure that 0, 1, & 2 are open.
*/
+ /* reset errno and fill_errno; the latter is used way down below */
+ errno = fill_errno = 0;
fill_fd(STDIN_FILENO, NULL);
+ if (errno != 0)
+ fill_errno = errno;
fill_fd(STDOUT_FILENO, NULL);
+ if (errno != 0)
+ fill_errno = errno;
fill_fd(STDERR_FILENO, NULL);
+ if (errno != 0)
+ fill_errno = errno;
i = DtableSize;
while (--i > 0)
{
- if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
+ if (i != STDIN_FILENO && i != STDOUT_FILENO &&
+ i != STDERR_FILENO)
(void) close(i);
}
errno = 0;
#if LOG
+# ifndef SM_LOG_STR
+# define SM_LOG_STR "sendmail"
+# endif /* ! SM_LOG_STR */
# ifdef LOG_MAIL
- openlog("sendmail", LOG_PID, LOG_MAIL);
+ openlog(SM_LOG_STR, LOG_PID, LOG_MAIL);
# else /* LOG_MAIL */
- openlog("sendmail", LOG_PID);
+ openlog(SM_LOG_STR, LOG_PID);
# endif /* LOG_MAIL */
#endif /* LOG */
- if (MissingFds != 0)
- {
- char mbuf[MAXLINE];
+ /*
+ ** Seed the random number generator.
+ ** Used for queue file names, picking a queue directory, and
+ ** MX randomization.
+ */
- mbuf[0] = '\0';
- if (bitset(1 << STDIN_FILENO, MissingFds))
- (void) strlcat(mbuf, ", stdin", sizeof mbuf);
- if (bitset(1 << STDOUT_FILENO, MissingFds))
- (void) strlcat(mbuf, ", stdout", sizeof mbuf);
- if (bitset(1 << STDERR_FILENO, MissingFds))
- (void) strlcat(mbuf, ", stderr", sizeof mbuf);
- syserr("File descriptors missing on startup: %s", &mbuf[2]);
- }
+ seed_random();
+
+ /* do machine-dependent initializations */
+ init_md(argc, argv);
+
+
+ SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + SL_FUDGE + SLDLL;
/* reset status from syserr() calls for missing file descriptors */
Errors = 0;
@@ -231,30 +306,32 @@ main(argc, argv, envp)
checkfd012("after openlog");
#endif /* XDEBUG */
- tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
+ tTsetup(tTdvect, sizeof tTdvect, "0-99.1,*_trace_*.1");
#ifdef NGROUPS_MAX
/* save initial group set for future checks */
i = getgroups(NGROUPS_MAX, InitialGidSet);
- if (i == 0)
+ if (i <= 0)
+ {
InitialGidSet[0] = (GID_T) -1;
+ i = 0;
+ }
while (i < NGROUPS_MAX)
InitialGidSet[i++] = InitialGidSet[0];
#endif /* NGROUPS_MAX */
/* drop group id privileges (RunAsUser not yet set) */
- dp = drop_privileges(FALSE);
+ dp = drop_privileges(false);
setstat(dp);
-# ifdef SIGUSR1
+#ifdef SIGUSR1
/* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */
- if (getuid() == 0 ||
- (getuid() == geteuid() && getgid() == getegid()))
+ if (extraprivs)
{
/* arrange to dump state on user-1 signal */
- (void) setsignal(SIGUSR1, sigusr1);
+ (void) sm_signal(SIGUSR1, sigusr1);
}
-# endif /* SIGUSR1 */
+#endif /* SIGUSR1 */
/* initialize for setproctitle */
initsetproctitle(argc, argv, envp);
@@ -267,49 +344,121 @@ main(argc, argv, envp)
*/
-#if defined(__osf__) || defined(_AIX3)
-# define OPTIONS "B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:x"
-#endif /* defined(__osf__) || defined(_AIX3) */
-#if defined(sony_news)
-# define OPTIONS "B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
-#endif /* defined(sony_news) */
-#ifndef OPTIONS
-# define OPTIONS "B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
-#endif /* ! OPTIONS */
+ /* find initial opMode */
+ OpMode = MD_DELIVER;
+ av = argv;
+ p = strrchr(*av, '/');
+ if (p++ == NULL)
+ p = *av;
+ if (strcmp(p, "newaliases") == 0)
+ OpMode = MD_INITALIAS;
+ else if (strcmp(p, "mailq") == 0)
+ OpMode = MD_PRINT;
+ else if (strcmp(p, "smtpd") == 0)
+ OpMode = MD_DAEMON;
+ else if (strcmp(p, "hoststat") == 0)
+ OpMode = MD_HOSTSTAT;
+ else if (strcmp(p, "purgestat") == 0)
+ OpMode = MD_PURGESTAT;
+
+#if _FFR_QUARANTINE
+# if defined(__osf__) || defined(_AIX3)
+# define OPTIONS "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:xQ:"
+# endif /* defined(__osf__) || defined(_AIX3) */
+# if defined(sony_news)
+# define OPTIONS "A:B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtV:vX:Q:"
+# endif /* defined(sony_news) */
+# ifndef OPTIONS
+# define OPTIONS "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:Q:"
+# endif /* ! OPTIONS */
+#else /* _FFR_QUARANTINE */
+# if defined(__osf__) || defined(_AIX3)
+# define OPTIONS "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:x"
+# endif /* defined(__osf__) || defined(_AIX3) */
+# if defined(sony_news)
+# define OPTIONS "A:B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtV:vX:"
+# endif /* defined(sony_news) */
+# ifndef OPTIONS
+# define OPTIONS "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:"
+# endif /* ! OPTIONS */
+#endif /* _FFR_QUARANTINE */
+
opterr = 0;
while ((j = getopt(argc, argv, OPTIONS)) != -1)
{
switch (j)
{
- case 'd':
- /* hack attack -- see if should use ANSI mode */
- if (strcmp(optarg, "ANSI") == 0)
+ case 'b': /* operations mode */
+ switch (j = *optarg)
{
- TermEscape.te_rv_on = "\033[7m";
- TermEscape.te_rv_off = "\033[0m";
+ case MD_DAEMON:
+ case MD_FGDAEMON:
+ case MD_SMTP:
+ case MD_INITALIAS:
+ case MD_DELIVER:
+ case MD_VERIFY:
+ case MD_TEST:
+ case MD_PRINT:
+ case MD_PRINTNQE:
+ case MD_HOSTSTAT:
+ case MD_PURGESTAT:
+ case MD_ARPAFTP:
+ OpMode = j;
break;
+
+ case MD_FREEZE:
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Frozen configurations unsupported\n");
+ return EX_USAGE;
+
+ default:
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Invalid operation mode %c\n",
+ j);
+ return EX_USAGE;
}
+ break;
+
+ case 'd':
+ debug = true;
tTflag(optarg);
- setbuf(stdout, (char *) NULL);
+ (void) sm_io_setvbuf(smioout, SM_TIME_DEFAULT,
+ (char *) NULL, SM_IO_NBF,
+ SM_IO_BUFSIZ);
break;
case 'G': /* relay (gateway) submission */
- SubmitMode |= SUBMIT_MTA;
+ SubmitMode = SUBMIT_MTA;
break;
case 'L':
- j = min(strlen(optarg), 24) + 1;
+ j = SM_MIN(strlen(optarg), 24) + 1;
sysloglabel = xalloc(j);
- (void) strlcpy(sysloglabel, optarg, j);
+ (void) sm_strlcpy(sysloglabel, optarg, j);
+ SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) +
+ SL_FUDGE + j;
break;
- case 'U': /* initial (user) submission */
- SubmitMode |= SUBMIT_MSA;
+#if _FFR_QUARANTINE
+ case 'Q':
+#endif /* _FFR_QUARANTINE */
+ case 'q':
+ /* just check if it is there */
+ queuerun = true;
break;
}
}
opterr = 1;
+ /* Don't leak queue information via debug flags */
+ if (extraprivs && queuerun && debug)
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "WARNING: Can not use -d with -q. Disabling debugging.\n");
+ sm_debug_setfile(NULL);
+ (void) memset(tTdvect, '\0', sizeof tTdvect);
+ }
+
#if LOG
if (sysloglabel != NULL)
{
@@ -342,56 +491,66 @@ main(argc, argv, envp)
*/
setdefaults(&BlankEnvelope);
+ initmacros(&BlankEnvelope);
- RealUid = getuid();
- RealGid = getgid();
+ /* reset macro */
+ set_op_mode(OpMode);
pw = sm_getpwuid(RealUid);
if (pw != NULL)
- (void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
+ (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
else
- (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
- (int) RealUid);
+ (void) sm_snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
+ (int) RealUid);
RealUserName = rnamebuf;
if (tTd(0, 101))
{
- dprintf("Version %s\n", Version);
- finis(FALSE, EX_OK);
+ sm_dprintf("Version %s\n", Version);
+ finis(false, true, EX_OK);
+ /* NOTREACHED */
}
/*
- ** if running non-setuid binary as non-root, pretend
+ ** if running non-set-user-ID binary as non-root, pretend
** we are the RunAsUid
*/
if (RealUid != 0 && geteuid() == RealUid)
{
if (tTd(47, 1))
- dprintf("Non-setuid binary: RunAsUid = RealUid = %d\n",
- (int)RealUid);
+ sm_dprintf("Non-set-user-ID binary: RunAsUid = RealUid = %d\n",
+ (int) RealUid);
RunAsUid = RealUid;
}
else if (geteuid() != 0)
RunAsUid = geteuid();
- if (RealUid != 0 && getegid() == RealGid)
+ EffGid = getegid();
+ if (RealUid != 0 && EffGid == RealGid)
RunAsGid = RealGid;
if (tTd(47, 5))
{
- dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
- (int)geteuid(), (int)getuid(),
- (int)getegid(), (int)getgid());
- dprintf("main: RunAsUser = %d:%d\n",
- (int)RunAsUid, (int)RunAsGid);
+ sm_dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
+ (int) geteuid(), (int) getuid(),
+ (int) getegid(), (int) getgid());
+ sm_dprintf("main: RunAsUser = %d:%d\n",
+ (int) RunAsUid, (int) RunAsGid);
}
/* save command line arguments */
j = 0;
for (av = argv; *av != NULL; )
j += strlen(*av++) + 1;
+ if (j < 0 || j > SM_ARG_MAX)
+ {
+ syserr("!Arguments too long");
+
+ /* NOTREACHED */
+ return EX_USAGE;
+ }
SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
CommandLineArgs = xalloc(j);
p = CommandLineArgs;
@@ -402,7 +561,7 @@ main(argc, argv, envp)
SaveArgv[i++] = newstr(*av);
if (av != argv)
*p++ = ' ';
- (void) strlcpy(p, *av++, j);
+ (void) sm_strlcpy(p, *av++, j);
h = strlen(p);
p += h;
j -= h + 1;
@@ -411,60 +570,48 @@ main(argc, argv, envp)
if (tTd(0, 1))
{
- int ll;
extern char *CompileOptions[];
- dprintf("Version %s\n Compiled with:", Version);
- av = CompileOptions;
- ll = 7;
- while (*av != NULL)
- {
- if (ll + strlen(*av) > 63)
- {
- dprintf("\n");
- ll = 0;
- }
- if (ll == 0)
- dprintf("\t\t");
- else
- dprintf(" ");
- dprintf("%s", *av);
- ll += strlen(*av++) + 1;
- }
- dprintf("\n");
+ sm_dprintf("Version %s\n Compiled with:", Version);
+ sm_printoptions(CompileOptions);
}
if (tTd(0, 10))
{
- int ll;
extern char *OsCompileOptions[];
- dprintf(" OS Defines:");
- av = OsCompileOptions;
- ll = 7;
- while (*av != NULL)
- {
- if (ll + strlen(*av) > 63)
- {
- dprintf("\n");
- ll = 0;
- }
- if (ll == 0)
- dprintf("\t\t");
- else
- dprintf(" ");
- dprintf("%s", *av);
- ll += strlen(*av++) + 1;
- }
- dprintf("\n");
+ sm_dprintf(" OS Defines:");
+ sm_printoptions(OsCompileOptions);
#ifdef _PATH_UNIX
- dprintf("Kernel symbols:\t%s\n", _PATH_UNIX);
+ sm_dprintf("Kernel symbols:\t%s\n", _PATH_UNIX);
#endif /* _PATH_UNIX */
- dprintf(" Def Conf file:\t%s\n", getcfname());
- dprintf(" Def Pid file:\t%s\n", PidFile);
+
+ sm_dprintf(" Conf file:\t%s (default for MSP)\n",
+ getcfname(OpMode, SubmitMode, SM_GET_SUBMIT_CF,
+ conffile));
+ sm_dprintf(" Conf file:\t%s (default for MTA)\n",
+ getcfname(OpMode, SubmitMode, SM_GET_SENDMAIL_CF,
+ conffile));
+ sm_dprintf(" Pid file:\t%s (default)\n", PidFile);
+ }
+
+ if (tTd(0, 12))
+ {
+ extern char *SmCompileOptions[];
+
+ sm_dprintf(" libsm Defines:");
+ sm_printoptions(SmCompileOptions);
+ }
+
+ if (tTd(0, 13))
+ {
+ extern char *FFRCompileOptions[];
+
+ sm_dprintf(" FFR Defines:");
+ sm_printoptions(FFRCompileOptions);
}
- InChannel = stdin;
- OutChannel = stdout;
+ InChannel = smioin;
+ OutChannel = smioout;
/* clear sendmail's environment */
ExternalEnviron = environ;
@@ -475,23 +622,26 @@ main(argc, argv, envp)
** restore any original TZ setting until TimeZoneSpec has been
** determined - or early log messages may get bogus time stamps
*/
+
if ((p = getextenv("TZ")) != NULL)
{
char *tz;
int tzlen;
+ /* XXX check for reasonable length? */
tzlen = strlen(p) + 4;
tz = xalloc(tzlen);
- (void) snprintf(tz, tzlen, "TZ=%s", p);
+ (void) sm_strlcpyn(tz, tzlen, 2, "TZ=", p);
+
+ /* XXX check return code? */
(void) putenv(tz);
}
/* prime the child environment */
setuserenv("AGENT", "sendmail");
- (void) setsignal(SIGPIPE, SIG_IGN);
+ (void) sm_signal(SIGPIPE, SIG_IGN);
OldUmask = umask(022);
- OpMode = MD_DELIVER;
FullName = getextenv("NAME");
/*
@@ -501,20 +651,13 @@ main(argc, argv, envp)
#if NAMED_BIND
if (!bitset(RES_INIT, _res.options))
(void) res_init();
-
- /*
- ** hack to avoid crashes when debugging for the resolver is
- ** turned on and sfio is used
- */
if (tTd(8, 8))
-# if !SFIO || SFIO_STDIO_COMPAT
_res.options |= RES_DEBUG;
-# else /* !SFIO || SFIO_STDIO_COMPAT */
- dprintf("RES_DEBUG not available due to SFIO\n");
-# endif /* !SFIO || SFIO_STDIO_COMPAT */
else
_res.options &= ~RES_DEBUG;
# ifdef RES_NOALIASES
+ if (bitset(RES_NOALIASES, _res.options))
+ ResNoAliases = true;
_res.options |= RES_NOALIASES;
# endif /* RES_NOALIASES */
TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry;
@@ -529,22 +672,21 @@ main(argc, argv, envp)
from = NULL;
/* initialize some macros, etc. */
- initmacros(CurEnv);
- init_vendor_macros(CurEnv);
+ init_vendor_macros(&BlankEnvelope);
/* version */
- define('v', Version, CurEnv);
+ macdefine(&BlankEnvelope.e_macro, A_PERM, 'v', Version);
/* hostname */
hp = myhostname(jbuf, sizeof jbuf);
if (jbuf[0] != '\0')
{
- struct utsname utsname;
+ struct utsname utsname;
if (tTd(0, 4))
- dprintf("canonical name: %s\n", jbuf);
- define('w', newstr(jbuf), CurEnv); /* must be new string */
- define('j', newstr(jbuf), CurEnv);
+ sm_dprintf("Canonical name: %s\n", jbuf);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, 'w', jbuf);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, 'j', jbuf);
setclass('w', jbuf);
p = strchr(jbuf, '.');
@@ -552,13 +694,14 @@ main(argc, argv, envp)
{
if (p[1] != '\0')
{
- define('m', newstr(&p[1]), CurEnv);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, 'm',
+ &p[1]);
}
while (p != NULL && strchr(&p[1], '.') != NULL)
{
*p = '\0';
if (tTd(0, 4))
- dprintf("\ta.k.a.: %s\n", jbuf);
+ sm_dprintf("\ta.k.a.: %s\n", jbuf);
setclass('w', jbuf);
*p++ = '.';
p = strchr(p, '.');
@@ -570,15 +713,14 @@ main(argc, argv, envp)
else
{
if (tTd(0, 22))
- dprintf("uname failed (%s)\n",
- errstring(errno));
+ sm_dprintf("uname failed (%s)\n",
+ sm_errstring(errno));
makelower(jbuf);
p = jbuf;
}
if (tTd(0, 4))
- dprintf(" UUCP nodename: %s\n", p);
- p = newstr(p);
- define('k', p, CurEnv);
+ sm_dprintf(" UUCP nodename: %s\n", p);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, 'k', p);
setclass('k', p);
setclass('w', p);
}
@@ -587,11 +729,11 @@ main(argc, argv, envp)
for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
{
if (tTd(0, 4))
- dprintf("\ta.k.a.: %s\n", *av);
+ sm_dprintf("\ta.k.a.: %s\n", *av);
setclass('w', *av);
}
#if NETINET || NETINET6
- for (i = 0; hp->h_addr_list[i] != NULL; i++)
+ for (i = 0; i >= 0 && hp->h_addr_list[i] != NULL; i++)
{
# if NETINET6
char *addr;
@@ -612,8 +754,8 @@ main(argc, argv, envp)
break;
memmove(&ia, hp->h_addr_list[i], INADDRSZ);
- (void) snprintf(ipbuf, sizeof ipbuf,
- "[%.100s]", inet_ntoa(ia));
+ (void) sm_snprintf(ipbuf, sizeof ipbuf,
+ "[%.100s]", inet_ntoa(ia));
break;
# endif /* NETINET */
@@ -625,8 +767,8 @@ main(argc, argv, envp)
memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ);
addr = anynet_ntop(&ia6, buf6, sizeof buf6);
if (addr != NULL)
- (void) snprintf(ipbuf, sizeof ipbuf,
- "[%.100s]", addr);
+ (void) sm_snprintf(ipbuf, sizeof ipbuf,
+ "[%.100s]", addr);
break;
# endif /* NETINET6 */
}
@@ -634,128 +776,97 @@ main(argc, argv, envp)
break;
if (tTd(0, 4))
- dprintf("\ta.k.a.: %s\n", ipbuf);
+ sm_dprintf("\ta.k.a.: %s\n", ipbuf);
setclass('w', ipbuf);
}
#endif /* NETINET || NETINET6 */
-#if _FFR_FREEHOSTENT && NETINET6
+#if NETINET6
freehostent(hp);
hp = NULL;
-#endif /* _FFR_FREEHOSTENT && NETINET6 */
+#endif /* NETINET6 */
}
/* current time */
- define('b', arpadate((char *) NULL), CurEnv);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, 'b', arpadate((char *) NULL));
+
/* current load average */
- CurrentLA = sm_getla(CurEnv);
+ sm_getla();
QueueLimitRecipient = (QUEUE_CHAR *) NULL;
QueueLimitSender = (QUEUE_CHAR *) NULL;
QueueLimitId = (QUEUE_CHAR *) NULL;
+#if _FFR_QUARANTINE
+ QueueLimitQuarantine = (QUEUE_CHAR *) NULL;
+#endif /* _FFR_QUARANTINE */
/*
** Crack argv.
*/
- av = argv;
- p = strrchr(*av, '/');
- if (p++ == NULL)
- p = *av;
- if (strcmp(p, "newaliases") == 0)
- OpMode = MD_INITALIAS;
- else if (strcmp(p, "mailq") == 0)
- OpMode = MD_PRINT;
- else if (strcmp(p, "smtpd") == 0)
- OpMode = MD_DAEMON;
- else if (strcmp(p, "hoststat") == 0)
- OpMode = MD_HOSTSTAT;
- else if (strcmp(p, "purgestat") == 0)
- OpMode = MD_PURGESTAT;
-
optind = 1;
while ((j = getopt(argc, argv, OPTIONS)) != -1)
{
switch (j)
{
case 'b': /* operations mode */
- switch (j = *optarg)
- {
- case MD_DAEMON:
- case MD_FGDAEMON:
-#if !DAEMON
- usrerr("Daemon mode not implemented");
- ExitStat = EX_USAGE;
- break;
-#endif /* !DAEMON */
- case MD_SMTP:
-#if !SMTP
- usrerr("I don't speak SMTP");
- ExitStat = EX_USAGE;
- break;
-#endif /* !SMTP */
-
- case MD_INITALIAS:
- case MD_DELIVER:
- case MD_VERIFY:
- case MD_TEST:
- case MD_PRINT:
- case MD_HOSTSTAT:
- case MD_PURGESTAT:
- case MD_ARPAFTP:
- OpMode = j;
- break;
-
- case MD_FREEZE:
- usrerr("Frozen configurations unsupported");
- ExitStat = EX_USAGE;
- break;
+ /* already done */
+ break;
- default:
- usrerr("Invalid operation mode %c", j);
- ExitStat = EX_USAGE;
- break;
- }
+ case 'A': /* use Alternate sendmail/submit.cf */
+ cftype = optarg[0] == 'c' ? SM_GET_SUBMIT_CF
+ : SM_GET_SENDMAIL_CF;
break;
case 'B': /* body type */
- CurEnv->e_bodytype = newstr(optarg);
+ CHECK_AGAINST_OPMODE(j);
+ BlankEnvelope.e_bodytype = newstr(optarg);
break;
case 'C': /* select configuration file (already done) */
if (RealUid != 0)
- warn_C_flag = TRUE;
- ConfFile = newstr(optarg);
- dp = drop_privileges(TRUE);
+ warn_C_flag = true;
+ conffile = newstr(optarg);
+ dp = drop_privileges(true);
setstat(dp);
- safecf = FALSE;
+ safecf = false;
break;
- case 'd': /* debugging -- already done */
+ case 'd': /* debugging */
+ /* already done */
break;
case 'f': /* from address */
case 'r': /* obsolete -f flag */
+ CHECK_AGAINST_OPMODE(j);
if (from != NULL)
{
usrerr("More than one \"from\" person");
ExitStat = EX_USAGE;
break;
}
- from = newstr(denlstring(optarg, TRUE, TRUE));
+ from = newstr(denlstring(optarg, true, true));
if (strcmp(RealUserName, from) != 0)
warn_f_flag = j;
break;
case 'F': /* set full name */
+ CHECK_AGAINST_OPMODE(j);
FullName = newstr(optarg);
break;
case 'G': /* relay (gateway) submission */
/* already set */
+ CHECK_AGAINST_OPMODE(j);
break;
case 'h': /* hop count */
- CurEnv->e_hopcount = (short) strtol(optarg, &ep, 10);
+ CHECK_AGAINST_OPMODE(j);
+ BlankEnvelope.e_hopcount = (short) strtol(optarg, &ep,
+ 10);
+ (void) sm_snprintf(buf, sizeof buf, "%d",
+ BlankEnvelope.e_hopcount);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, 'c', buf);
+
if (*ep)
{
usrerr("Bad hop count (%s)", optarg);
@@ -768,25 +879,27 @@ main(argc, argv, envp)
break;
case 'n': /* don't alias */
- NoAlias = TRUE;
+ CHECK_AGAINST_OPMODE(j);
+ NoAlias = true;
break;
case 'N': /* delivery status notifications */
+ CHECK_AGAINST_OPMODE(j);
DefaultNotify |= QHASNOTIFY;
- define(macid("{dsn_notify}", NULL),
- newstr(optarg), CurEnv);
- if (strcasecmp(optarg, "never") == 0)
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{dsn_notify}"), optarg);
+ if (sm_strcasecmp(optarg, "never") == 0)
break;
for (p = optarg; p != NULL; optarg = p)
{
p = strchr(p, ',');
if (p != NULL)
*p++ = '\0';
- if (strcasecmp(optarg, "success") == 0)
+ if (sm_strcasecmp(optarg, "success") == 0)
DefaultNotify |= QPINGONSUCCESS;
- else if (strcasecmp(optarg, "failure") == 0)
+ else if (sm_strcasecmp(optarg, "failure") == 0)
DefaultNotify |= QPINGONFAILURE;
- else if (strcasecmp(optarg, "delay") == 0)
+ else if (sm_strcasecmp(optarg, "delay") == 0)
DefaultNotify |= QPINGONDELAY;
else
{
@@ -797,40 +910,64 @@ main(argc, argv, envp)
break;
case 'o': /* set option */
- setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv);
+ setoption(*optarg, optarg + 1, false, true,
+ &BlankEnvelope);
break;
case 'O': /* set option (long form) */
- setoption(' ', optarg, FALSE, TRUE, CurEnv);
+ setoption(' ', optarg, false, true, &BlankEnvelope);
break;
case 'p': /* set protocol */
+ CHECK_AGAINST_OPMODE(j);
p = strchr(optarg, ':');
if (p != NULL)
{
*p++ = '\0';
if (*p != '\0')
{
- ep = xalloc(strlen(p) + 1);
+ ep = sm_malloc_x(strlen(p) + 1);
cleanstrcpy(ep, p, MAXNAME);
- define('s', ep, CurEnv);
+ macdefine(&BlankEnvelope.e_macro,
+ A_HEAP, 's', ep);
}
}
if (*optarg != '\0')
{
- ep = xalloc(strlen(optarg) + 1);
+ ep = sm_malloc_x(strlen(optarg) + 1);
cleanstrcpy(ep, optarg, MAXNAME);
- define('r', ep, CurEnv);
+ macdefine(&BlankEnvelope.e_macro, A_HEAP,
+ 'r', ep);
}
break;
+#if _FFR_QUARANTINE
+ case 'Q': /* change quarantining on queued items */
+ /* sanity check */
+ if (OpMode != MD_DELIVER &&
+ OpMode != MD_QUEUERUN)
+ {
+ usrerr("Can not use -Q with -b%c", OpMode);
+ ExitStat = EX_USAGE;
+ break;
+ }
+
+ if (OpMode == MD_DELIVER)
+ set_op_mode(MD_QUEUERUN);
+
+ FullName = NULL;
+
+ quarantining = newstr(optarg);
+ break;
+#endif /* _FFR_QUARANTINE */
+
case 'q': /* run queue files at intervals */
-#if QUEUE
/* sanity check */
if (OpMode != MD_DELIVER &&
OpMode != MD_DAEMON &&
OpMode != MD_FGDAEMON &&
OpMode != MD_PRINT &&
+ OpMode != MD_PRINTNQE &&
OpMode != MD_QUEUERUN)
{
usrerr("Can not use -q with -b%c", OpMode);
@@ -840,33 +977,89 @@ main(argc, argv, envp)
/* don't override -bd, -bD or -bp */
if (OpMode == MD_DELIVER)
- OpMode = MD_QUEUERUN;
+ set_op_mode(MD_QUEUERUN);
FullName = NULL;
+ negate = optarg[0] == '!';
+ if (negate)
+ {
+ /* negate meaning of pattern match */
+ optarg++; /* skip '!' for next switch */
+ }
switch (optarg[0])
{
- case 'I':
+ case 'G': /* Limit by queue group name */
+ if (negate)
+ {
+ usrerr("Can not use -q!G");
+ ExitStat = EX_USAGE;
+ break;
+ }
+ if (queuegroup != NULL)
+ {
+ usrerr("Can not use multiple -qG options");
+ ExitStat = EX_USAGE;
+ break;
+ }
+ queuegroup = newstr(&optarg[1]);
+ break;
+
+ case 'I': /* Limit by ID */
new = (QUEUE_CHAR *) xalloc(sizeof *new);
new->queue_match = newstr(&optarg[1]);
+ new->queue_negate = negate;
new->queue_next = QueueLimitId;
QueueLimitId = new;
break;
- case 'R':
+ case 'R': /* Limit by recipient */
new = (QUEUE_CHAR *) xalloc(sizeof *new);
new->queue_match = newstr(&optarg[1]);
+ new->queue_negate = negate;
new->queue_next = QueueLimitRecipient;
QueueLimitRecipient = new;
break;
- case 'S':
+ case 'S': /* Limit by sender */
new = (QUEUE_CHAR *) xalloc(sizeof *new);
new->queue_match = newstr(&optarg[1]);
+ new->queue_negate = negate;
new->queue_next = QueueLimitSender;
QueueLimitSender = new;
break;
+ case 'f': /* foreground queue run */
+ foregroundqueue = true;
+ break;
+
+#if _FFR_QUARANTINE
+ case 'Q': /* Limit by quarantine message */
+ if (optarg[1] != '\0')
+ {
+ new = (QUEUE_CHAR *) xalloc(sizeof *new);
+ new->queue_match = newstr(&optarg[1]);
+ new->queue_negate = negate;
+ new->queue_next = QueueLimitQuarantine;
+ QueueLimitQuarantine = new;
+ }
+ QueueMode = QM_QUARANTINE;
+ break;
+
+ case 'L': /* act on lost items */
+ QueueMode = QM_LOST;
+ break;
+#endif /* _FFR_QUARANTINE */
+
+ case 'p': /* Persistent queue */
+ queuepersistent = true;
+ if (QueueIntvl == 0)
+ QueueIntvl = 1;
+ if (optarg[1] == '\0')
+ break;
+ ++optarg;
+ /* FALLTHROUGH */
+
default:
i = Errors;
QueueIntvl = convtime(optarg, 'm');
@@ -876,40 +1069,35 @@ main(argc, argv, envp)
ExitStat = EX_USAGE;
break;
}
-#else /* QUEUE */
- usrerr("I don't know about queues");
- ExitStat = EX_USAGE;
-#endif /* QUEUE */
break;
case 'R': /* DSN RET: what to return */
- if (bitset(EF_RET_PARAM, CurEnv->e_flags))
+ CHECK_AGAINST_OPMODE(j);
+ if (bitset(EF_RET_PARAM, BlankEnvelope.e_flags))
{
usrerr("Duplicate -R flag");
ExitStat = EX_USAGE;
break;
}
- CurEnv->e_flags |= EF_RET_PARAM;
- if (strcasecmp(optarg, "hdrs") == 0)
- CurEnv->e_flags |= EF_NO_BODY_RETN;
- else if (strcasecmp(optarg, "full") != 0)
+ BlankEnvelope.e_flags |= EF_RET_PARAM;
+ if (sm_strcasecmp(optarg, "hdrs") == 0)
+ BlankEnvelope.e_flags |= EF_NO_BODY_RETN;
+ else if (sm_strcasecmp(optarg, "full") != 0)
{
usrerr("Invalid -R value");
ExitStat = EX_USAGE;
}
- define(macid("{dsn_ret}", NULL),
- newstr(optarg), CurEnv);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{dsn_ret}"), optarg);
break;
case 't': /* read recipients from message */
- GrabTo = TRUE;
- break;
-
- case 'U': /* initial (user) submission */
- /* already set */
+ CHECK_AGAINST_OPMODE(j);
+ GrabTo = true;
break;
case 'V': /* DSN ENVID: set "original" envelope id */
+ CHECK_AGAINST_OPMODE(j);
if (!xtextok(optarg))
{
usrerr("Invalid syntax in -V flag");
@@ -917,31 +1105,34 @@ main(argc, argv, envp)
}
else
{
- CurEnv->e_envid = newstr(optarg);
- define(macid("{dsn_envid}", NULL),
- newstr(optarg), CurEnv);
+ BlankEnvelope.e_envid = newstr(optarg);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{dsn_envid}"), optarg);
}
break;
case 'X': /* traffic log file */
- dp = drop_privileges(TRUE);
+ dp = drop_privileges(true);
setstat(dp);
if (stat(optarg, &traf_st) == 0 &&
S_ISFIFO(traf_st.st_mode))
- TrafficLogFile = fopen(optarg, "w");
+ TrafficLogFile = sm_io_open(SmFtStdio,
+ SM_TIME_DEFAULT,
+ optarg,
+ SM_IO_WRONLY, NULL);
else
- TrafficLogFile = fopen(optarg, "a");
+ TrafficLogFile = sm_io_open(SmFtStdio,
+ SM_TIME_DEFAULT,
+ optarg,
+ SM_IO_APPEND, NULL);
if (TrafficLogFile == NULL)
{
syserr("cannot open %s", optarg);
ExitStat = EX_CANTCREAT;
break;
}
-#if HASSETVBUF
- (void) setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
-#else /* HASSETVBUF */
- (void) setlinebuf(TrafficLogFile);
-#endif /* HASSETVBUF */
+ (void) sm_io_setvbuf(TrafficLogFile, SM_TIME_DEFAULT,
+ NULL, SM_IO_LBF, 0);
break;
/* compatibility flags */
@@ -950,21 +1141,21 @@ main(argc, argv, envp)
case 'm': /* send to me too */
case 'T': /* set timeout interval */
case 'v': /* give blow-by-blow description */
- setoption(j, "T", FALSE, TRUE, CurEnv);
+ setoption(j, "T", false, true, &BlankEnvelope);
break;
case 'e': /* error message disposition */
case 'M': /* define macro */
- setoption(j, optarg, FALSE, TRUE, CurEnv);
+ setoption(j, optarg, false, true, &BlankEnvelope);
break;
case 's': /* save From lines in headers */
- setoption('f', "T", FALSE, TRUE, CurEnv);
+ setoption('f', "T", false, true, &BlankEnvelope);
break;
#ifdef DBM
case 'I': /* initialize alias DBM file */
- OpMode = MD_INITALIAS;
+ set_op_mode(MD_INITALIAS);
break;
#endif /* DBM */
@@ -980,39 +1171,30 @@ main(argc, argv, envp)
#endif /* defined(sony_news) */
default:
- finis(TRUE, EX_USAGE);
+ finis(true, true, EX_USAGE);
+ /* NOTREACHED */
break;
}
}
- av += optind;
- if (bitset(SUBMIT_MTA, SubmitMode) &&
- bitset(SUBMIT_MSA, SubmitMode))
+ /* if we've had errors so far, exit now */
+ if ((ExitStat != EX_OK && OpMode != MD_TEST) ||
+ ExitStat == EX_OSERR)
{
- /* sanity check */
- errno = 0; /* reset to avoid bogus error messages */
- syserr("Cannot use both -G and -U together");
+ finis(false, true, ExitStat);
+ /* NOTREACHED */
}
- else if (bitset(SUBMIT_MTA, SubmitMode))
- define(macid("{daemon_flags}", NULL), "CC f", CurEnv);
- else if (bitset(SUBMIT_MSA, SubmitMode))
- {
- define(macid("{daemon_flags}", NULL), "c u", CurEnv);
- /* check for wrong OpMode */
- if (OpMode != MD_DELIVER && OpMode != MD_SMTP)
- {
- errno = 0; /* reset to avoid bogus error msgs */
- syserr("Cannot use -U and -b%c", OpMode);
- }
+ if (bitset(SUBMIT_MTA, SubmitMode))
+ {
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{daemon_flags}"), "CC f");
}
- else
+ else if (OpMode == MD_DELIVER || OpMode == MD_SMTP)
{
-#if _FFR_DEFAULT_SUBMIT_TO_MSA
- define(macid("{daemon_flags}", NULL), "c u", CurEnv);
-#else /* _FFR_DEFAULT_SUBMIT_TO_MSA */
- /* EMPTY */
-#endif /* _FFR_DEFAULT_SUBMIT_TO_MSA */
+ SubmitMode = SUBMIT_MSA;
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{daemon_flags}"), "c u");
}
/*
@@ -1021,27 +1203,39 @@ main(argc, argv, envp)
** Extract special fields for local use.
*/
- /* set up ${opMode} for use in config file */
- {
- char mbuf[2];
-
- mbuf[0] = OpMode;
- mbuf[1] = '\0';
- define(MID_OPMODE, newstr(mbuf), CurEnv);
- }
-
#if XDEBUG
checkfd012("before readcf");
#endif /* XDEBUG */
- vendor_pre_defaults(CurEnv);
+ vendor_pre_defaults(&BlankEnvelope);
- readcf(getcfname(), safecf, CurEnv);
- ConfigFileRead = TRUE;
- vendor_post_defaults(CurEnv);
+ readcf(getcfname(OpMode, SubmitMode, cftype, conffile),
+ safecf, &BlankEnvelope);
+#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
+ ConfigFileRead = true;
+#endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */
+ vendor_post_defaults(&BlankEnvelope);
+
+ /* now we can complain about missing fds */
+ if (MissingFds != 0 && LogLevel > 8)
+ {
+ char mbuf[MAXLINE];
+
+ mbuf[0] = '\0';
+ if (bitset(1 << STDIN_FILENO, MissingFds))
+ (void) sm_strlcat(mbuf, ", stdin", sizeof mbuf);
+ if (bitset(1 << STDOUT_FILENO, MissingFds))
+ (void) sm_strlcat(mbuf, ", stdout", sizeof mbuf);
+ if (bitset(1 << STDERR_FILENO, MissingFds))
+ (void) sm_strlcat(mbuf, ", stderr", sizeof mbuf);
+
+ /* Notice: fill_errno is from high above: fill_fd() */
+ sm_syslog(LOG_WARNING, NOQID,
+ "File descriptors missing on startup: %s; %s",
+ &mbuf[2], sm_errstring(fill_errno));
+ }
/* Remove the ability for a normal user to send signals */
- if (RealUid != 0 &&
- RealUid != geteuid())
+ if (RealUid != 0 && RealUid != geteuid())
{
uid_t new_uid = geteuid();
@@ -1056,17 +1250,17 @@ main(argc, argv, envp)
if (new_uid == 0)
new_uid = DefUid;
if (tTd(47, 5))
- dprintf("Changing real uid to %d\n", (int) new_uid);
+ sm_dprintf("Changing real uid to %d\n", (int) new_uid);
if (setreuid(new_uid, geteuid()) < 0)
{
syserr("main: setreuid(%d, %d) failed",
(int) new_uid, (int) geteuid());
- finis(FALSE, EX_OSERR);
+ finis(false, true, EX_OSERR);
/* NOTREACHED */
}
if (tTd(47, 10))
- dprintf("Now running as e/ruid %d:%d\n",
- (int) geteuid(), (int) getuid());
+ sm_dprintf("Now running as e/ruid %d:%d\n",
+ (int) geteuid(), (int) getuid());
#else /* HASSETREUID */
/*
** Have to change both effective and real so need to
@@ -1074,23 +1268,45 @@ main(argc, argv, envp)
*/
if (tTd(47, 5))
- dprintf("Changing uid to %d\n", (int) new_uid);
+ sm_dprintf("Changing uid to %d\n", (int) new_uid);
if (setuid(new_uid) < 0)
{
syserr("main: setuid(%d) failed", (int) new_uid);
- finis(FALSE, EX_OSERR);
+ finis(false, true, EX_OSERR);
/* NOTREACHED */
}
if (tTd(47, 10))
- dprintf("Now running as e/ruid %d:%d\n",
- (int) geteuid(), (int) getuid());
+ sm_dprintf("Now running as e/ruid %d:%d\n",
+ (int) geteuid(), (int) getuid());
#endif /* HASSETREUID */
}
+#if NAMED_BIND
+ if (FallBackMX != NULL)
+ (void) getfallbackmxrr(FallBackMX);
+#endif /* NAMED_BIND */
+
+ if (SuperSafe == SAFE_INTERACTIVE && CurEnv->e_sendmode != SM_DELIVER)
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "WARNING: SuperSafe=interactive should only be used with\n DeliveryMode=interactive\n");
+ }
+
+ if (UseMSP && (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON))
+ {
+ usrerr("Mail submission program cannot be used as daemon");
+ finis(false, true, EX_USAGE);
+ }
+
+ if (OpMode == MD_DELIVER || OpMode == MD_SMTP ||
+ OpMode == MD_QUEUERUN || OpMode == MD_ARPAFTP ||
+ OpMode == MD_DAEMON || OpMode == MD_FGDAEMON)
+ makeworkgroups();
+
/* set up the basic signal handlers */
- if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
- (void) setsignal(SIGINT, intsig);
- (void) setsignal(SIGTERM, intsig);
+ if (sm_signal(SIGINT, SIG_IGN) != SIG_IGN)
+ (void) sm_signal(SIGINT, intsig);
+ (void) sm_signal(SIGTERM, intsig);
/* Enforce use of local time (null string overrides this) */
if (TimeZoneSpec == NULL)
@@ -1101,14 +1317,39 @@ main(argc, argv, envp)
setuserenv("TZ", NULL);
tzset();
+ /* initialize mailbox database */
+ i = sm_mbdb_initialize(Mbdb);
+ if (i != EX_OK)
+ {
+ usrerr("Can't initialize mailbox database \"%s\": %s",
+ Mbdb, sm_strexit(i));
+ ExitStat = i;
+ }
+
/* avoid denial-of-service attacks */
resetlimits();
- if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
+ if (OpMode == MD_TEST)
+ {
+ /* can't be done after readcf if RunAs* is used */
+ dp = drop_privileges(true);
+ if (dp != EX_OK)
+ {
+ finis(false, true, dp);
+ /* NOTREACHED */
+ }
+ }
+ else if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
{
/* drop privileges -- daemon mode done after socket/bind */
- dp = drop_privileges(FALSE);
+ dp = drop_privileges(false);
setstat(dp);
+ if (dp == EX_OK && UseMSP && (geteuid() == 0 || getuid() == 0))
+ {
+ usrerr("Mail submission program must have RunAsUser set to non root user");
+ finis(false, true, EX_CONFIG);
+ /* NOTREACHED */
+ }
}
#if NAMED_BIND
@@ -1121,33 +1362,41 @@ main(argc, argv, envp)
*/
authinfo = getauthinfo(STDIN_FILENO, &forged);
- define('_', authinfo, CurEnv);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
/* suppress error printing if errors mailed back or whatever */
- if (CurEnv->e_errormode != EM_PRINT)
- HoldErrs = TRUE;
+ if (BlankEnvelope.e_errormode != EM_PRINT)
+ HoldErrs = true;
/* set up the $=m class now, after .cf has a chance to redefine $m */
- expand("\201m", jbuf, sizeof jbuf, CurEnv);
+ expand("\201m", jbuf, sizeof jbuf, &BlankEnvelope);
if (jbuf[0] != '\0')
setclass('m', jbuf);
/* probe interfaces and locate any additional names */
- if (!DontProbeInterfaces)
+ if (DontProbeInterfaces != DPI_PROBENONE)
load_if_names();
+ if (tTd(0, 10))
+ {
+ /* Now we know which .cf file we use */
+ sm_dprintf(" Conf file:\t%s (selected)\n",
+ getcfname(OpMode, SubmitMode, cftype, conffile));
+ sm_dprintf(" Pid file:\t%s (selected)\n", PidFile);
+ }
+
if (tTd(0, 1))
{
- dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
- dprintf("\n (short domain name) $w = ");
- xputs(macvalue('w', CurEnv));
- dprintf("\n (canonical domain name) $j = ");
- xputs(macvalue('j', CurEnv));
- dprintf("\n (subdomain name) $m = ");
- xputs(macvalue('m', CurEnv));
- dprintf("\n (node name) $k = ");
- xputs(macvalue('k', CurEnv));
- dprintf("\n========================================================\n\n");
+ sm_dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
+ sm_dprintf("\n (short domain name) $w = ");
+ xputs(macvalue('w', &BlankEnvelope));
+ sm_dprintf("\n (canonical domain name) $j = ");
+ xputs(macvalue('j', &BlankEnvelope));
+ sm_dprintf("\n (subdomain name) $m = ");
+ xputs(macvalue('m', &BlankEnvelope));
+ sm_dprintf("\n (node name) $k = ");
+ xputs(macvalue('k', &BlankEnvelope));
+ sm_dprintf("\n========================================================\n\n");
}
/*
@@ -1157,24 +1406,25 @@ main(argc, argv, envp)
/* process authorization warnings from command line */
if (warn_C_flag)
- auth_warning(CurEnv, "Processed by %s with -C %s",
- RealUserName, ConfFile);
+ auth_warning(&BlankEnvelope, "Processed by %s with -C %s",
+ RealUserName, conffile);
if (Warn_Q_option && !wordinclass(RealUserName, 't'))
- auth_warning(CurEnv, "Processed from queue %s", QueueDir);
+ auth_warning(&BlankEnvelope, "Processed from queue %s",
+ QueueDir);
+ if (sysloglabel != NULL && !wordinclass(RealUserName, 't') &&
+ RealUid != 0 && RealUid != TrustedUid && LogLevel > 1)
+ sm_syslog(LOG_WARNING, NOQID, "user %d changed syslog label",
+ (int) RealUid);
/* check body type for legality */
- if (CurEnv->e_bodytype == NULL)
- /* EMPTY */
- /* nothing */ ;
- else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0)
- SevenBitInput = TRUE;
- else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0)
- SevenBitInput = FALSE;
- else
+ i = check_bodytype(BlankEnvelope.e_bodytype);
+ if (i == BODYTYPE_ILLEGAL)
{
- usrerr("Illegal body type %s", CurEnv->e_bodytype);
- CurEnv->e_bodytype = NULL;
+ usrerr("Illegal body type %s", BlankEnvelope.e_bodytype);
+ BlankEnvelope.e_bodytype = NULL;
}
+ else if (i != BODYTYPE_NONE)
+ SevenBitInput = (i == BODYTYPE_7BIT);
/* tweak default DSN notifications */
if (DefaultNotify == 0)
@@ -1188,50 +1438,156 @@ main(argc, argv, envp)
if (ConfigLevel > MAXCONFIGLEVEL)
{
syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
- ConfigLevel, Version, MAXCONFIGLEVEL);
+ ConfigLevel, Version, MAXCONFIGLEVEL);
}
/* need MCI cache to have persistence */
if (HostStatDir != NULL && MaxMciCache == 0)
{
HostStatDir = NULL;
- printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
}
/* need HostStatusDir in order to have SingleThreadDelivery */
if (SingleThreadDelivery && HostStatDir == NULL)
{
- SingleThreadDelivery = FALSE;
- printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n");
+ SingleThreadDelivery = false;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: HostStatusDirectory required for SingleThreadDelivery\n");
}
/* check for permissions */
- if ((OpMode == MD_DAEMON ||
- OpMode == MD_FGDAEMON ||
- OpMode == MD_PURGESTAT) &&
- RealUid != 0 &&
+ if (RealUid != 0 &&
RealUid != TrustedUid)
{
- if (LogLevel > 1)
- sm_syslog(LOG_ALERT, NOQID,
- "user %d attempted to %s",
- RealUid,
- OpMode != MD_PURGESTAT ? "run daemon"
- : "purge host status");
- usrerr("Permission denied");
- finis(FALSE, EX_USAGE);
- }
- if (OpMode == MD_INITALIAS &&
- RealUid != 0 &&
- RealUid != TrustedUid &&
- !wordinclass(RealUserName, 't'))
- {
- if (LogLevel > 1)
- sm_syslog(LOG_ALERT, NOQID,
- "user %d attempted to rebuild the alias map",
- RealUid);
- usrerr("Permission denied");
- finis(FALSE, EX_USAGE);
+ char *action = NULL;
+
+ switch (OpMode)
+ {
+ case MD_QUEUERUN:
+#if _FFR_QUARANTINE
+ if (quarantining != NULL)
+ action = "quarantine jobs";
+ else
+#endif /* _FFR_QUARANTINE */
+ /* Normal users can do a single queue run */
+ if (QueueIntvl == 0)
+ break;
+
+ /* but not persistent queue runners */
+ if (action == NULL)
+ action = "start a queue runner daemon";
+ /* FALLTHROUGH */
+
+ case MD_PURGESTAT:
+ if (action == NULL)
+ action = "purge host status";
+ /* FALLTHROUGH */
+
+ case MD_DAEMON:
+ case MD_FGDAEMON:
+ if (action == NULL)
+ action = "run daemon";
+
+ if (tTd(65, 1))
+ sm_dprintf("Deny user %d attempt to %s\n",
+ (int) RealUid, action);
+
+ if (LogLevel > 1)
+ sm_syslog(LOG_ALERT, NOQID,
+ "user %d attempted to %s",
+ (int) RealUid, action);
+ HoldErrs = false;
+ usrerr("Permission denied (real uid not trusted)");
+ finis(false, true, EX_USAGE);
+ /* NOTREACHED */
+ break;
+
+ case MD_VERIFY:
+ if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags))
+ {
+ /*
+ ** If -bv and RestrictExpand,
+ ** drop privs to prevent normal
+ ** users from reading private
+ ** aliases/forwards/:include:s
+ */
+
+ if (tTd(65, 1))
+ sm_dprintf("Drop privs for user %d attempt to expand (RestrictExpand)\n",
+ (int) RealUid);
+
+ dp = drop_privileges(true);
+
+ /* Fake address safety */
+ if (tTd(65, 1))
+ sm_dprintf("Faking DontBlameSendmail=NonRootSafeAddr\n");
+ setbitn(DBS_NONROOTSAFEADDR, DontBlameSendmail);
+
+ if (dp != EX_OK)
+ {
+ if (tTd(65, 1))
+ sm_dprintf("Failed to drop privs for user %d attempt to expand, exiting\n",
+ (int) RealUid);
+ CurEnv->e_id = NULL;
+ finis(true, true, dp);
+ /* NOTREACHED */
+ }
+ }
+ break;
+
+ case MD_TEST:
+ case MD_PRINT:
+ case MD_PRINTNQE:
+ case MD_FREEZE:
+ case MD_HOSTSTAT:
+ /* Nothing special to check */
+ break;
+
+ case MD_INITALIAS:
+ if (!wordinclass(RealUserName, 't'))
+ {
+ if (tTd(65, 1))
+ sm_dprintf("Deny user %d attempt to rebuild the alias map\n",
+ (int) RealUid);
+ if (LogLevel > 1)
+ sm_syslog(LOG_ALERT, NOQID,
+ "user %d attempted to rebuild the alias map",
+ (int) RealUid);
+ HoldErrs = false;
+ usrerr("Permission denied (real uid not trusted)");
+ finis(false, true, EX_USAGE);
+ /* NOTREACHED */
+ }
+ if (UseMSP)
+ {
+ HoldErrs = false;
+ usrerr("User %d cannot rebuild aliases in mail submission program",
+ (int) RealUid);
+ finis(false, true, EX_USAGE);
+ /* NOTREACHED */
+ }
+ /* FALLTHROUGH */
+
+ default:
+ if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags) &&
+ Verbose != 0)
+ {
+ /*
+ ** If -v and RestrictExpand, reset
+ ** Verbose to prevent normal users
+ ** from seeing the expansion of
+ ** aliases/forwards/:include:s
+ */
+
+ if (tTd(65, 1))
+ sm_dprintf("Dropping verbosity for user %d (RestrictExpand)\n",
+ (int) RealUid);
+ Verbose = 0;
+ }
+ break;
+ }
}
if (MeToo)
@@ -1244,48 +1600,49 @@ main(argc, argv, envp)
HostStatDir = NULL;
if (Verbose == 0)
Verbose = 2;
- CurEnv->e_errormode = EM_PRINT;
- HoldErrs = FALSE;
+ BlankEnvelope.e_errormode = EM_PRINT;
+ HoldErrs = false;
break;
case MD_VERIFY:
- CurEnv->e_errormode = EM_PRINT;
- HoldErrs = FALSE;
-
+ BlankEnvelope.e_errormode = EM_PRINT;
+ HoldErrs = false;
/* arrange to exit cleanly on hangup signal */
- if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
- (void) setsignal(SIGHUP, intsig);
+ if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
+ (void) sm_signal(SIGHUP, intsig);
+ if (geteuid() != 0)
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Notice: -bv may give misleading output for non-privileged user\n");
break;
case MD_FGDAEMON:
- run_in_foreground = TRUE;
- OpMode = MD_DAEMON;
+ run_in_foreground = true;
+ set_op_mode(MD_DAEMON);
/* FALLTHROUGH */
case MD_DAEMON:
- vendor_daemon_setup(CurEnv);
+ vendor_daemon_setup(&BlankEnvelope);
/* remove things that don't make sense in daemon mode */
FullName = NULL;
- GrabTo = FALSE;
+ GrabTo = false;
/* arrange to restart on hangup signal */
if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
sm_syslog(LOG_WARNING, NOQID,
"daemon invoked without full pathname; kill -1 won't work");
- (void) setsignal(SIGTERM, term_daemon);
break;
case MD_INITALIAS:
Verbose = 2;
- CurEnv->e_errormode = EM_PRINT;
- HoldErrs = FALSE;
+ BlankEnvelope.e_errormode = EM_PRINT;
+ HoldErrs = false;
/* FALLTHROUGH */
default:
/* arrange to exit cleanly on hangup signal */
- if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
- (void) setsignal(SIGHUP, intsig);
+ if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
+ (void) sm_signal(SIGHUP, intsig);
break;
}
@@ -1297,7 +1654,7 @@ main(argc, argv, envp)
/* full names can't have newlines */
if (strchr(FullName, '\n') != NULL)
{
- full = newstr(denlstring(FullName, TRUE, TRUE));
+ full = newstr(denlstring(FullName, true, true));
FullName = full;
}
@@ -1310,9 +1667,9 @@ main(argc, argv, envp)
** the name portion of the address.
*/
- FullName = addquotes(FullName);
+ FullName = addquotes(FullName, NULL);
if (full != NULL)
- sm_free(full);
+ sm_free(full); /* XXX */
}
}
@@ -1320,10 +1677,10 @@ main(argc, argv, envp)
if (Verbose)
{
/* turn off noconnect option */
- setoption('c', "F", TRUE, FALSE, CurEnv);
+ setoption('c', "F", true, false, &BlankEnvelope);
/* turn on interactive delivery */
- setoption('d', "", TRUE, FALSE, CurEnv);
+ setoption('d', "", true, false, &BlankEnvelope);
}
#ifdef VENDOR_CODE
@@ -1343,19 +1700,21 @@ main(argc, argv, envp)
}
if (ConfigLevel < 3)
- UseErrorsTo = TRUE;
+ UseErrorsTo = true;
/* set options that were previous macros */
if (SmtpGreeting == NULL)
{
- if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL)
+ if (ConfigLevel < 7 &&
+ (p = macvalue('e', &BlankEnvelope)) != NULL)
SmtpGreeting = newstr(p);
else
SmtpGreeting = "\201j Sendmail \201v ready at \201b";
}
if (UnixFromLine == NULL)
{
- if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL)
+ if (ConfigLevel < 7 &&
+ (p = macvalue('l', &BlankEnvelope)) != NULL)
UnixFromLine = newstr(p);
else
UnixFromLine = "From \201g \201d";
@@ -1363,11 +1722,11 @@ main(argc, argv, envp)
SmtpError[0] = '\0';
/* our name for SMTP codes */
- expand("\201j", jbuf, sizeof jbuf, CurEnv);
+ expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
if (jbuf[0] == '\0')
- MyHostName = newstr("localhost");
+ PSTRSET(MyHostName, "localhost");
else
- MyHostName = jbuf;
+ PSTRSET(MyHostName, jbuf);
if (strchr(MyHostName, '.') == NULL)
message("WARNING: local host name (%s) is not qualified; fix $j in config file",
MyHostName);
@@ -1375,6 +1734,13 @@ main(argc, argv, envp)
/* make certain that this name is part of the $=w class */
setclass('w', MyHostName);
+ /* fill in the structure of the *default* queue */
+ st = stab("mqueue", ST_QUEUE, ST_FIND);
+ if (st == NULL)
+ syserr("No default queue (mqueue) defined");
+ else
+ set_def_queueval(st->s_quegrp, true);
+
/* the indices of built-in mailers */
st = stab("local", ST_MAILER, ST_FIND);
if (st != NULL)
@@ -1454,29 +1820,28 @@ main(argc, argv, envp)
#endif /* USE_B_CLASS */
/* MIME headers which have fields to check for overflow */
- setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-disposition");
- setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-type");
+ setclass(macid("{checkMIMEFieldHeaders}"), "content-disposition");
+ setclass(macid("{checkMIMEFieldHeaders}"), "content-type");
/* MIME headers to check for length overflow */
- setclass(macid("{checkMIMETextHeaders}", NULL), "content-description");
+ setclass(macid("{checkMIMETextHeaders}"), "content-description");
/* MIME headers to check for overflow and rebalance */
- setclass(macid("{checkMIMEHeaders}", NULL), "content-disposition");
- setclass(macid("{checkMIMEHeaders}", NULL), "content-id");
- setclass(macid("{checkMIMEHeaders}", NULL), "content-transfer-encoding");
- setclass(macid("{checkMIMEHeaders}", NULL), "content-type");
- setclass(macid("{checkMIMEHeaders}", NULL), "mime-version");
-
- /* Macros to save in the qf file -- don't remove any */
- setclass(macid("{persistentMacros}", NULL), "r");
- setclass(macid("{persistentMacros}", NULL), "s");
- setclass(macid("{persistentMacros}", NULL), "_");
- setclass(macid("{persistentMacros}", NULL), "{if_addr}");
- setclass(macid("{persistentMacros}", NULL), "{daemon_flags}");
- setclass(macid("{persistentMacros}", NULL), "{client_flags}");
+ setclass(macid("{checkMIMEHeaders}"), "content-disposition");
+ setclass(macid("{checkMIMEHeaders}"), "content-id");
+ setclass(macid("{checkMIMEHeaders}"), "content-transfer-encoding");
+ setclass(macid("{checkMIMEHeaders}"), "content-type");
+ setclass(macid("{checkMIMEHeaders}"), "mime-version");
+
+ /* Macros to save in the queue file -- don't remove any */
+ setclass(macid("{persistentMacros}"), "r");
+ setclass(macid("{persistentMacros}"), "s");
+ setclass(macid("{persistentMacros}"), "_");
+ setclass(macid("{persistentMacros}"), "{if_addr}");
+ setclass(macid("{persistentMacros}"), "{daemon_flags}");
/* operate in queue directory */
- if (QueueDir == NULL)
+ if (QueueDir == NULL || *QueueDir == '\0')
{
if (OpMode != MD_TEST)
{
@@ -1486,29 +1851,22 @@ main(argc, argv, envp)
}
else
{
- /*
- ** If multiple queues wildcarded, use one for
- ** the daemon's home. Note that this preconditions
- ** a wildcarded QueueDir to a real pathname.
- */
-
if (OpMode != MD_TEST)
- multiqueue_cache();
+ setup_queues(OpMode == MD_DAEMON);
}
/* check host status directory for validity */
- if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE))
+ if (HostStatDir != NULL && !path_is_dir(HostStatDir, false))
{
/* cannot use this value */
- if (tTd(0, 2))
- dprintf("Cannot use HostStatusDirectory = %s: %s\n",
- HostStatDir, errstring(errno));
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: Cannot use HostStatusDirectory = %s: %s\n",
+ HostStatDir, sm_errstring(errno));
HostStatDir = NULL;
}
-#if QUEUE
- if (OpMode == MD_QUEUERUN && RealUid != 0 &&
- bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
+ if (OpMode == MD_QUEUERUN &&
+ RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
{
struct stat stbuf;
@@ -1518,22 +1876,48 @@ main(argc, argv, envp)
if (stbuf.st_uid != RealUid)
{
/* nope, really a botch */
+ HoldErrs = false;
usrerr("You do not have permission to process the queue");
- finis(FALSE, EX_NOPERM);
+ finis(false, true, EX_NOPERM);
+ /* NOTREACHED */
}
}
-#endif /* QUEUE */
-#if _FFR_MILTER
+#if MILTER
/* sanity checks on milter filters */
if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
- milter_parse_list(InputFilterList, InputFilters, MAXFILTERS);
-#endif /* _FFR_MILTER */
+ {
+ milter_config(InputFilterList, InputFilters, MAXFILTERS);
+# if _FFR_MILTER_PERDAEMON
+ setup_daemon_milters();
+# endif /* _FFR_MILTER_PERDAEMON */
+ }
+#endif /* MILTER */
+ /* Convert queuegroup string to qgrp number */
+ if (queuegroup != NULL)
+ {
+ qgrp = name2qid(queuegroup);
+ if (qgrp == NOQGRP)
+ {
+ HoldErrs = false;
+ usrerr("Queue group %s unknown", queuegroup);
+ finis(false, true, ExitStat);
+ /* NOTREACHED */
+ }
+ }
/* if we've had errors so far, exit now */
if (ExitStat != EX_OK && OpMode != MD_TEST)
- finis(FALSE, ExitStat);
+ {
+ finis(false, true, ExitStat);
+ /* NOTREACHED */
+ }
+
+#if SASL
+ /* sendmail specific SASL initialization */
+ sm_sasl_init();
+#endif /* SASL */
#if XDEBUG
checkfd012("before main() initmaps");
@@ -1547,43 +1931,89 @@ main(argc, argv, envp)
{
case MD_PRINT:
/* print the queue */
-#if QUEUE
- dropenvelope(CurEnv, TRUE);
- (void) setsignal(SIGPIPE, quiesce);
+ HoldErrs = false;
+ dropenvelope(&BlankEnvelope, true, false);
+ (void) sm_signal(SIGPIPE, sigpipe);
+ if (qgrp != NOQGRP)
+ {
+ int j;
+
+ /* Selecting a particular queue group to run */
+ for (j = 0; j < Queue[qgrp]->qg_numqueues; j++)
+ {
+ if (StopRequest)
+ stop_sendmail();
+ (void) print_single_queue(qgrp, j);
+ }
+ finis(false, true, EX_OK);
+ /* NOTREACHED */
+ }
printqueue();
- finis(FALSE, EX_OK);
-#else /* QUEUE */
- usrerr("No queue to print");
- finis(FALSE, EX_UNAVAILABLE);
-#endif /* QUEUE */
+ finis(false, true, EX_OK);
+ /* NOTREACHED */
+ break;
+
+ case MD_PRINTNQE:
+ /* print number of entries in queue */
+ dropenvelope(&BlankEnvelope, true, false);
+ (void) sm_signal(SIGPIPE, sigpipe);
+ printnqe(smioout, NULL);
+ finis(false, true, EX_OK);
+ /* NOTREACHED */
break;
+#if _FFR_QUARANTINE
+ case MD_QUEUERUN:
+ /* only handle quarantining here */
+ if (quarantining == NULL)
+ break;
+
+ if (QueueMode != QM_QUARANTINE &&
+ QueueMode != QM_NORMAL)
+ {
+ HoldErrs = false;
+ usrerr("Can not use -Q with -q%c", QueueMode);
+ ExitStat = EX_USAGE;
+ finis(false, true, ExitStat);
+ /* NOTREACHED */
+ }
+ quarantine_queue(quarantining, qgrp);
+ finis(false, true, EX_OK);
+ break;
+#endif /* _FFR_QUARANTINE */
+
case MD_HOSTSTAT:
- (void) setsignal(SIGPIPE, quiesce);
+ (void) sm_signal(SIGPIPE, sigpipe);
(void) mci_traverse_persistent(mci_print_persistent, NULL);
- finis(FALSE, EX_OK);
+ finis(false, true, EX_OK);
+ /* NOTREACHED */
break;
case MD_PURGESTAT:
(void) mci_traverse_persistent(mci_purge_persistent, NULL);
- finis(FALSE, EX_OK);
+ finis(false, true, EX_OK);
+ /* NOTREACHED */
break;
case MD_INITALIAS:
/* initialize maps */
initmaps();
- finis(FALSE, ExitStat);
+ finis(false, true, ExitStat);
+ /* NOTREACHED */
break;
case MD_SMTP:
case MD_DAEMON:
/* reset DSN parameters */
DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
- define(macid("{dsn_notify}", NULL), NULL, CurEnv);
- CurEnv->e_envid = NULL;
- define(macid("{dsn_envid}", NULL), NULL, CurEnv);
- CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
- define(macid("{dsn_ret}", NULL), NULL, CurEnv);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{dsn_notify}"), NULL);
+ BlankEnvelope.e_envid = NULL;
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{dsn_envid}"), NULL);
+ BlankEnvelope.e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{dsn_ret}"), NULL);
/* don't open maps for daemon -- done below in child */
break;
@@ -1605,7 +2035,8 @@ main(argc, argv, envp)
** Switch to the main envelope.
*/
- CurEnv = newenvelope(&MainEnvelope, CurEnv);
+ CurEnv = newenvelope(&MainEnvelope, &BlankEnvelope,
+ sm_rpool_new_x(NULL));
MainEnvelope.e_flags = BlankEnvelope.e_flags;
/*
@@ -1614,82 +2045,191 @@ main(argc, argv, envp)
if (OpMode == MD_TEST)
{
- char buf[MAXLINE];
-
-#if _FFR_TESTMODE_DROP_PRIVS
- dp = drop_privileges(TRUE);
- if (dp != EX_OK)
- {
- CurEnv->e_id = NULL;
- finis(TRUE, dp);
- }
-#endif /* _FFR_TESTMODE_DROP_PRIVS */
-
- if (isatty(fileno(stdin)))
+ if (isatty(sm_io_getinfo(smioin, SM_IO_WHAT_FD, NULL)))
Verbose = 2;
if (Verbose)
{
- printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
- printf("Enter <ruleset> <address>\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Enter <ruleset> <address>\n");
}
- if (setjmp(TopFrame) > 0)
- printf("\n");
- (void) setsignal(SIGINT, intindebug);
+ macdefine(&(MainEnvelope.e_macro), A_PERM,
+ macid("{addr_type}"), "e r");
for (;;)
{
- if (Verbose == 2)
- printf("> ");
- (void) fflush(stdout);
- if (fgets(buf, sizeof buf, stdin) == NULL)
- testmodeline("/quit", CurEnv);
- p = strchr(buf, '\n');
- if (p != NULL)
- *p = '\0';
- if (Verbose < 2)
- printf("> %s\n", buf);
- testmodeline(buf, CurEnv);
+ SM_TRY
+ {
+ (void) sm_signal(SIGINT, intindebug);
+ (void) sm_releasesignal(SIGINT);
+ if (Verbose == 2)
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "> ");
+ (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
+ if (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf,
+ sizeof buf) == NULL)
+ testmodeline("/quit", &MainEnvelope);
+ p = strchr(buf, '\n');
+ if (p != NULL)
+ *p = '\0';
+ if (Verbose < 2)
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "> %s\n", buf);
+ testmodeline(buf, &MainEnvelope);
+ }
+ SM_EXCEPT(exc, "[!F]*")
+ {
+ /*
+ ** 8.10 just prints \n on interrupt.
+ ** I'm printing the exception here in case
+ ** sendmail is extended to raise additional
+ ** exceptions in this context.
+ */
+
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\n");
+ sm_exc_print(exc, smioout);
+ }
+ SM_END_TRY
}
}
-#if SMTP
-# if STARTTLS
- tls_ok = init_tls_library();
-# endif /* STARTTLS */
-#endif /* SMTP */
+#if STARTTLS
+ tls_ok = true;
+ if (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER)
+ {
+ /* check whether STARTTLS is turned off for the client */
+ if (chkclientmodifiers(D_NOTLS))
+ tls_ok = false;
+ }
+ else if (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON ||
+ OpMode == MD_SMTP)
+ {
+ /* check whether STARTTLS is turned off for the server */
+ if (chkdaemonmodifiers(D_NOTLS))
+ tls_ok = false;
+ }
+ else /* other modes don't need STARTTLS */
+ tls_ok = false;
+
+ if (tls_ok)
+ {
+ /* basic TLS initialization */
+ tls_ok = init_tls_library();
+ }
+
+ if (!tls_ok && (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER))
+ {
+ /* disable TLS for client */
+ setclttls(false);
+ }
+#endif /* STARTTLS */
-#if QUEUE
/*
** If collecting stuff from the queue, go start doing that.
*/
if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
{
-# if SMTP
-# if STARTTLS
- if (tls_ok
- )
+ pid_t pid = -1;
+
+#if STARTTLS
+ /* init TLS for client, ignore result for now */
+ (void) initclttls(tls_ok);
+#endif /* STARTTLS */
+
+ /*
+ ** The parent process of the caller of runqueue() needs
+ ** to stay around for a possible SIGTERM. The SIGTERM will
+ ** tell this process that all of the queue runners children
+ ** need to be sent SIGTERM as well. At the same time, we
+ ** want to return control to the command line. So we do an
+ ** extra fork().
+ */
+
+ if (Verbose || foregroundqueue || (pid = fork()) <= 0)
{
- /* init TLS for client, ignore result for now */
- (void) initclttls();
+ /*
+ ** If the fork() failed we should still try to do
+ ** the queue run. If it succeeded then the child
+ ** is going to start the run and wait for all
+ ** of the children to finish.
+ */
+
+ if (pid == 0)
+ {
+ /* Reset global flags */
+ RestartRequest = NULL;
+ ShutdownRequest = NULL;
+ PendingSignal = 0;
+
+ /* disconnect from terminal */
+ disconnect(2, CurEnv);
+ }
+
+ CurrentPid = getpid();
+ if (qgrp != NOQGRP)
+ {
+ /*
+ ** To run a specific queue group mark it to
+ ** be run, select the work group it's in and
+ ** increment the work counter.
+ */
+
+ runqueueevent(qgrp);
+ (void) run_work_group(Queue[qgrp]->qg_wgrp,
+ false, Verbose,
+ queuepersistent, false);
+ }
+ else
+ (void) runqueue(false, Verbose,
+ queuepersistent, true);
+
+ /* set the title to make it easier to find */
+ sm_setproctitle(true, CurEnv, "Queue control");
+ (void) sm_signal(SIGCHLD, SIG_DFL);
+ while (CurChildren > 0)
+ {
+ int status;
+ pid_t ret;
+
+ while ((ret = sm_wait(&status)) <= 0)
+ continue;
+
+ /* Only drop when a child gives status */
+ if (WIFSTOPPED(status))
+ continue;
+
+ proc_list_drop(ret, status, NULL);
+ }
}
-# endif /* STARTTLS */
-# endif /* SMTP */
- (void) runqueue(FALSE, Verbose);
- finis(TRUE, ExitStat);
+ finis(true, true, ExitStat);
+ /* NOTREACHED */
}
-#endif /* QUEUE */
# if SASL
if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
{
- /* give a syserr or just disable AUTH ? */
- if ((i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK)
+ /* check whether AUTH is turned off for the server */
+ if (!chkdaemonmodifiers(D_NOAUTH) &&
+ (i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK)
syserr("!sasl_server_init failed! [%s]",
- sasl_errstring(i, NULL, NULL));
+ sasl_errstring(i, NULL, NULL));
}
# endif /* SASL */
+ if (OpMode == MD_SMTP)
+ {
+ proc_list_add(CurrentPid, "Sendmail SMTP Agent",
+ PROC_DAEMON, 0, -1);
+
+ /* clean up background delivery children */
+ (void) sm_signal(SIGCHLD, reapchild);
+ }
+
/*
** If a daemon, wait for a request.
** getrequests will always return in a child.
@@ -1710,80 +2250,203 @@ main(argc, argv, envp)
if (i < 0)
syserr("daemon: cannot fork");
if (i != 0)
- finis(FALSE, EX_OK);
+ {
+ finis(false, true, EX_OK);
+ /* NOTREACHED */
+ }
+
+ /*
+ ** Initialize exception stack and default exception
+ ** handler for child process.
+ */
+
+ /* Reset global flags */
+ RestartRequest = NULL;
+ RestartWorkGroup = false;
+ ShutdownRequest = NULL;
+ PendingSignal = 0;
+ CurrentPid = getpid();
+
+ sm_exc_newthread(fatal_error);
/* disconnect from our controlling tty */
- disconnect(2, CurEnv);
+ disconnect(2, &MainEnvelope);
}
dtype[0] = '\0';
if (OpMode == MD_DAEMON)
- (void) strlcat(dtype, "+SMTP", sizeof dtype);
+ {
+ (void) sm_strlcat(dtype, "+SMTP", sizeof dtype);
+ DaemonPid = CurrentPid;
+ }
if (QueueIntvl != 0)
{
- (void) strlcat(dtype, "+queueing@", sizeof dtype);
- (void) strlcat(dtype, pintvl(QueueIntvl, TRUE),
- sizeof dtype);
+ (void) sm_strlcat2(dtype,
+ queuepersistent
+ ? "+persistent-queueing@"
+ : "+queueing@",
+ pintvl(QueueIntvl, true),
+ sizeof dtype);
}
if (tTd(0, 1))
- (void) strlcat(dtype, "+debugging", sizeof dtype);
+ (void) sm_strlcat(dtype, "+debugging", sizeof dtype);
sm_syslog(LOG_INFO, NOQID,
"starting daemon (%s): %s", Version, dtype + 1);
-#ifdef XLA
+#if XLA
xla_create_file();
#endif /* XLA */
/* save daemon type in a macro for possible PidFile use */
- define(macid("{daemon_info}", NULL),
- newstr(dtype + 1), &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{daemon_info}"), dtype + 1);
/* save queue interval in a macro for possible PidFile use */
- define(macid("{queue_interval}", NULL),
- newstr(pintvl(QueueIntvl, TRUE)), CurEnv);
+ macdefine(&MainEnvelope.e_macro, A_TEMP,
+ macid("{queue_interval}"), pintvl(QueueIntvl, true));
+
+ /* workaround: can't seem to release the signal in the parent */
+ (void) sm_signal(SIGHUP, sighup);
+ (void) sm_releasesignal(SIGHUP);
+ (void) sm_signal(SIGTERM, sigterm);
-#if QUEUE
if (QueueIntvl != 0)
{
- (void) runqueue(TRUE, FALSE);
+ (void) runqueue(true, false, queuepersistent, true);
+
+ /*
+ ** If queuepersistent but not in daemon mode then
+ ** we're going to do the queue runner monitoring here.
+ ** If in daemon mode then the monitoring will happen
+ ** elsewhere.
+ */
+
+ if (OpMode != MD_DAEMON && queuepersistent)
+ {
+ /* set the title to make it easier to find */
+ sm_setproctitle(true, CurEnv, "Queue control");
+ (void) sm_signal(SIGCHLD, SIG_DFL);
+ while (CurChildren > 0)
+ {
+ int status;
+ pid_t ret;
+ int group;
+
+ if (ShutdownRequest != NULL)
+ shutdown_daemon();
+ else if (RestartRequest != NULL)
+ restart_daemon();
+ else if (RestartWorkGroup)
+ restart_marked_work_groups();
+
+ while ((ret = sm_wait(&status)) <= 0)
+ continue;
+
+ if (WIFSTOPPED(status))
+ continue;
+
+ /* Probe only on a child status */
+ proc_list_drop(ret, status, &group);
+
+ if (WIFSIGNALED(status))
+ {
+ if (WCOREDUMP(status))
+ {
+ sm_syslog(LOG_ERR, NOQID,
+ "persistent queue runner=%d core dumped, signal=%d",
+ group, WTERMSIG(status));
+
+ /* don't restart this one */
+ mark_work_group_restart(group, -1);
+ continue;
+ }
+
+ sm_syslog(LOG_ERR, NOQID,
+ "persistent queue runner=%d died, signal=%d",
+ group, WTERMSIG(status));
+ }
+
+ /*
+ ** When debugging active, don't
+ ** restart the persistent queues.
+ ** But do log this as info.
+ */
+
+ if (sm_debug_active(&DebugNoPRestart,
+ 1))
+ {
+ sm_syslog(LOG_DEBUG, NOQID,
+ "persistent queue runner=%d, exited",
+ group);
+ mark_work_group_restart(group, -1);
+ }
+ }
+ finis(true, true, ExitStat);
+ /* NOTREACHED */
+ }
+
if (OpMode != MD_DAEMON)
{
- /* write the pid to file */
- log_sendmail_pid(CurEnv);
- (void) setsignal(SIGTERM, term_daemon);
+ char qtype[200];
+
+ /*
+ ** Write the pid to file
+ ** XXX Overwrites sendmail.pid
+ */
+
+ log_sendmail_pid(&MainEnvelope);
+
+ /* set the title to make it easier to find */
+ qtype[0] = '\0';
+ (void) sm_strlcpyn(qtype, sizeof qtype, 4,
+ "Queue runner@",
+ pintvl(QueueIntvl, true),
+ " for ",
+ QueueDir);
+ sm_setproctitle(true, CurEnv, qtype);
for (;;)
{
(void) pause();
if (ShutdownRequest != NULL)
shutdown_daemon();
- else if (DoQueueRun)
- (void) runqueue(TRUE, FALSE);
+ else if (RestartRequest != NULL)
+ restart_daemon();
+ else if (RestartWorkGroup)
+ restart_marked_work_groups();
+
+ if (doqueuerun())
+ (void) runqueue(true, false,
+ false, false);
}
}
}
-#endif /* QUEUE */
- dropenvelope(CurEnv, TRUE);
+ dropenvelope(&MainEnvelope, true, false);
-#if DAEMON
-# if STARTTLS
+#if STARTTLS
/* init TLS for server, ignore result for now */
- (void) initsrvtls();
-# endif /* STARTTLS */
- p_flags = getrequests(CurEnv);
+ (void) initsrvtls(tls_ok);
+#endif /* STARTTLS */
+#if PROFILING
+ nextreq:
+#endif /* PROFILING */
+ p_flags = getrequests(&MainEnvelope);
/* drop privileges */
- (void) drop_privileges(FALSE);
-
- /* at this point we are in a child: reset state */
- (void) newenvelope(CurEnv, CurEnv);
+ (void) drop_privileges(false);
/*
** Get authentication data
+ ** Set _ macro in BlankEnvelope before calling newenvelope().
*/
- authinfo = getauthinfo(fileno(InChannel), &forged);
- define('_', authinfo, &BlankEnvelope);
-#endif /* DAEMON */
+ authinfo = getauthinfo(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
+ NULL), &forged);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
+
+ /* at this point we are in a child: reset state */
+ sm_rpool_free(MainEnvelope.e_rpool);
+ (void) newenvelope(&MainEnvelope, &MainEnvelope,
+ sm_rpool_new_x(NULL));
}
if (LogLevel > 9)
@@ -1792,7 +2455,6 @@ main(argc, argv, envp)
sm_syslog(LOG_INFO, NULL, "connect from %.100s", authinfo);
}
-#if SMTP
/*
** If running SMTP protocol, start collecting and executing
** commands. This will never return.
@@ -1810,91 +2472,98 @@ main(argc, argv, envp)
{
char ipbuf[103];
- (void) snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
- anynet_ntoa(&RealHostAddr));
- define(macid("{client_name}", NULL),
- newstr(ipbuf), &BlankEnvelope);
- define(macid("{client_resolve}", NULL),
- "FORGED", &BlankEnvelope);
+ (void) sm_snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
+ anynet_ntoa(&RealHostAddr));
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{client_name}"), ipbuf);
}
else
- define(macid("{client_name}", NULL), RealHostName,
- &BlankEnvelope);
- define(macid("{client_addr}", NULL),
- newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope);
- (void)sm_getla(&BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{client_name}"), RealHostName);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{client_addr}"), anynet_ntoa(&RealHostAddr));
+ sm_getla();
- switch(RealHostAddr.sa.sa_family)
+ switch (RealHostAddr.sa.sa_family)
{
-# if NETINET
+#if NETINET
case AF_INET:
- (void) snprintf(pbuf, sizeof pbuf, "%d",
- RealHostAddr.sin.sin_port);
+ (void) sm_snprintf(pbuf, sizeof pbuf, "%d",
+ RealHostAddr.sin.sin_port);
break;
-# endif /* NETINET */
-# if NETINET6
+#endif /* NETINET */
+#if NETINET6
case AF_INET6:
- (void) snprintf(pbuf, sizeof pbuf, "%d",
- RealHostAddr.sin6.sin6_port);
+ (void) sm_snprintf(pbuf, sizeof pbuf, "%d",
+ RealHostAddr.sin6.sin6_port);
break;
-# endif /* NETINET6 */
+#endif /* NETINET6 */
default:
- (void) snprintf(pbuf, sizeof pbuf, "0");
+ (void) sm_snprintf(pbuf, sizeof pbuf, "0");
break;
}
- define(macid("{client_port}", NULL),
- newstr(pbuf), &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{client_port}"), pbuf);
if (OpMode == MD_DAEMON)
{
/* validate the connection */
- HoldErrs = TRUE;
+ HoldErrs = true;
nullserver = validate_connection(&RealHostAddr,
- RealHostName, CurEnv);
- HoldErrs = FALSE;
+ RealHostName,
+ &MainEnvelope);
+ HoldErrs = false;
}
else if (p_flags == NULL)
{
p_flags = (BITMAP256 *) xalloc(sizeof *p_flags);
clrbitmap(p_flags);
}
-# if STARTTLS
+#if STARTTLS
if (OpMode == MD_SMTP)
- (void) initsrvtls();
-# endif /* STARTTLS */
-
-
- smtp(nullserver, *p_flags, CurEnv);
+ (void) initsrvtls(tls_ok);
+#endif /* STARTTLS */
+
+ /* turn off profiling */
+ SM_PROF(1);
+ smtp(nullserver, *p_flags, &MainEnvelope);
+#if PROFILING
+ /* turn off profiling */
+ SM_PROF(0);
+ if (OpMode == MD_DAEMON)
+ goto nextreq;
+#endif /* PROFILING */
}
-#endif /* SMTP */
- clearenvelope(CurEnv, FALSE);
+ sm_rpool_free(MainEnvelope.e_rpool);
+ clearenvelope(&MainEnvelope, false, sm_rpool_new_x(NULL));
if (OpMode == MD_VERIFY)
{
- set_delivery_mode(SM_VERIFY, CurEnv);
+ set_delivery_mode(SM_VERIFY, &MainEnvelope);
PostMasterCopy = NULL;
}
else
{
/* interactive -- all errors are global */
- CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
+ MainEnvelope.e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
}
/*
** Do basic system initialization and set the sender
*/
- initsys(CurEnv);
- define(macid("{ntries}", NULL), "0", CurEnv);
- setsender(from, CurEnv, NULL, '\0', FALSE);
+ initsys(&MainEnvelope);
+ macdefine(&MainEnvelope.e_macro, A_PERM, macid("{ntries}"), "0");
+ macdefine(&MainEnvelope.e_macro, A_PERM, macid("{nrcpts}"), "0");
+ setsender(from, &MainEnvelope, NULL, '\0', false);
if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') &&
- (!bitnset(M_LOCALMAILER, CurEnv->e_from.q_mailer->m_flags) ||
- strcmp(CurEnv->e_from.q_user, RealUserName) != 0))
+ (!bitnset(M_LOCALMAILER, MainEnvelope.e_from.q_mailer->m_flags) ||
+ strcmp(MainEnvelope.e_from.q_user, RealUserName) != 0))
{
- auth_warning(CurEnv, "%s set sender to %s using -%c",
- RealUserName, from, warn_f_flag);
+ auth_warning(&MainEnvelope, "%s set sender to %s using -%c",
+ RealUserName, from, warn_f_flag);
#if SASL
- auth = FALSE;
+ auth = false;
#endif /* SASL */
}
if (auth)
@@ -1902,46 +2571,54 @@ main(argc, argv, envp)
char *fv;
/* set the initial sender for AUTH= to $f@$j */
- fv = macvalue('f', CurEnv);
+ fv = macvalue('f', &MainEnvelope);
if (fv == NULL || *fv == '\0')
- CurEnv->e_auth_param = NULL;
+ MainEnvelope.e_auth_param = NULL;
else
{
if (strchr(fv, '@') == NULL)
{
- i = strlen(fv) + strlen(macvalue('j', CurEnv))
- + 2;
- p = xalloc(i);
- (void) snprintf(p, i, "%s@%s", fv,
- macvalue('j', CurEnv));
+ i = strlen(fv) + strlen(macvalue('j',
+ &MainEnvelope)) + 2;
+ p = sm_malloc_x(i);
+ (void) sm_strlcpyn(p, i, 3, fv, "@",
+ macvalue('j',
+ &MainEnvelope));
}
else
- p = newstr(fv);
- CurEnv->e_auth_param = newstr(xtextify(p, "="));
+ p = sm_strdup_x(fv);
+ MainEnvelope.e_auth_param = sm_rpool_strdup_x(MainEnvelope.e_rpool,
+ xtextify(p, "="));
+ sm_free(p); /* XXX */
}
}
- if (macvalue('s', CurEnv) == NULL)
- define('s', RealHostName, CurEnv);
+ if (macvalue('s', &MainEnvelope) == NULL)
+ macdefine(&MainEnvelope.e_macro, A_PERM, 's', RealHostName);
+ av = argv + optind;
if (*av == NULL && !GrabTo)
{
- CurEnv->e_to = NULL;
- CurEnv->e_flags |= EF_GLOBALERRS;
- HoldErrs = FALSE;
- SuperSafe = FALSE;
+ MainEnvelope.e_to = NULL;
+ MainEnvelope.e_flags |= EF_GLOBALERRS;
+ HoldErrs = false;
+ SuperSafe = SAFE_NO;
usrerr("Recipient names must be specified");
/* collect body for UUCP return */
if (OpMode != MD_VERIFY)
- collect(InChannel, FALSE, NULL, CurEnv);
- finis(TRUE, EX_USAGE);
+ collect(InChannel, false, NULL, &MainEnvelope);
+ finis(true, true, EX_USAGE);
+ /* NOTREACHED */
}
/*
** Scan argv and deliver the message to everyone.
*/
- sendtoargv(av, CurEnv);
+ save_val = LogUsrErrs;
+ LogUsrErrs = true;
+ sendtoargv(av, &MainEnvelope);
+ LogUsrErrs = save_val;
/* if we have had errors sofar, arrange a meaningful exit stat */
if (Errors > 0 && ExitStat == EX_OK)
@@ -1957,7 +2634,7 @@ main(argc, argv, envp)
{
ADDRESS *q;
- for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
+ for (q = MainEnvelope.e_sendqueue; q != NULL; q = q->q_next)
q->q_state = QS_REMOVED;
}
#endif /* _FFR_FIX_DASHT */
@@ -1966,122 +2643,120 @@ main(argc, argv, envp)
** Read the input mail.
*/
- CurEnv->e_to = NULL;
+ MainEnvelope.e_to = NULL;
if (OpMode != MD_VERIFY || GrabTo)
{
- int savederrors = Errors;
- long savedflags = CurEnv->e_flags & EF_FATALERRS;
+ int savederrors;
+ unsigned long savedflags;
- CurEnv->e_flags |= EF_GLOBALERRS;
- CurEnv->e_flags &= ~EF_FATALERRS;
+ /*
+ ** workaround for compiler warning on Irix:
+ ** do not initialize variable in the definition, but
+ ** later on:
+ ** warning(1548): transfer of control bypasses
+ ** initialization of:
+ ** variable "savederrors" (declared at line 2570)
+ ** variable "savedflags" (declared at line 2571)
+ ** goto giveup;
+ */
+
+ savederrors = Errors;
+ savedflags = MainEnvelope.e_flags & EF_FATALERRS;
+ MainEnvelope.e_flags |= EF_GLOBALERRS;
+ MainEnvelope.e_flags &= ~EF_FATALERRS;
Errors = 0;
buffer_errors();
- collect(InChannel, FALSE, NULL, CurEnv);
+ collect(InChannel, false, NULL, &MainEnvelope);
/* header checks failed */
if (Errors > 0)
{
- /* Log who the mail would have gone to */
- if (LogLevel > 8 && CurEnv->e_message != NULL &&
- !GrabTo)
+ giveup:
+ if (!GrabTo)
{
- ADDRESS *a;
-
- for (a = CurEnv->e_sendqueue;
- a != NULL;
- a = a->q_next)
- {
- if (!QS_IS_UNDELIVERED(a->q_state))
- continue;
-
- CurEnv->e_to = a->q_paddr;
- logdelivery(NULL, NULL, NULL,
- CurEnv->e_message,
- NULL, (time_t) 0, CurEnv);
- }
- CurEnv->e_to = NULL;
+ /* Log who the mail would have gone to */
+ logundelrcpts(&MainEnvelope,
+ MainEnvelope.e_message,
+ 8, false);
}
- flush_errors(TRUE);
- finis(TRUE, ExitStat);
+ flush_errors(true);
+ finis(true, true, ExitStat);
/* NOTREACHED */
return -1;
}
/* bail out if message too large */
- if (bitset(EF_CLRQUEUE, CurEnv->e_flags))
+ if (bitset(EF_CLRQUEUE, MainEnvelope.e_flags))
{
- finis(TRUE, ExitStat != EX_OK ? ExitStat : EX_DATAERR);
+ finis(true, true, ExitStat != EX_OK ? ExitStat
+ : EX_DATAERR);
/* NOTREACHED */
return -1;
}
Errors = savederrors;
- CurEnv->e_flags |= savedflags;
+ MainEnvelope.e_flags |= savedflags;
}
errno = 0;
if (tTd(1, 1))
- dprintf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
+ sm_dprintf("From person = \"%s\"\n",
+ MainEnvelope.e_from.q_paddr);
+
+#if _FFR_QUARANTINE
+ /* Check if quarantining stats should be updated */
+ if (MainEnvelope.e_quarmsg != NULL)
+ markstats(&MainEnvelope, NULL, STATS_QUARANTINE);
+#endif /* _FFR_QUARANTINE */
/*
** Actually send everything.
** If verifying, just ack.
*/
- CurEnv->e_from.q_state = QS_SENDER;
- if (tTd(1, 5))
+ if (Errors == 0)
{
- dprintf("main: QS_SENDER ");
- printaddr(&CurEnv->e_from, FALSE);
+ if (!split_by_recipient(&MainEnvelope) &&
+ bitset(EF_FATALERRS, MainEnvelope.e_flags))
+ goto giveup;
}
- CurEnv->e_to = NULL;
- CurrentLA = sm_getla(CurEnv);
- GrabTo = FALSE;
+
+ /* make sure we deliver at least the first envelope */
+ i = FastSplit > 0 ? 0 : -1;
+ for (e = &MainEnvelope; e != NULL; e = e->e_sibling, i++)
+ {
+ ENVELOPE *next;
+
+ e->e_from.q_state = QS_SENDER;
+ if (tTd(1, 5))
+ {
+ sm_dprintf("main[%d]: QS_SENDER ", i);
+ printaddr(&e->e_from, false);
+ }
+ e->e_to = NULL;
+ sm_getla();
+ GrabTo = false;
#if NAMED_BIND
- _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
- _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
+ _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
+ _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
#endif /* NAMED_BIND */
- sendall(CurEnv, SM_DEFAULT);
+ next = e->e_sibling;
+ e->e_sibling = NULL;
+
+ /* after FastSplit envelopes: queue up */
+ sendall(e, i >= FastSplit ? SM_QUEUE : SM_DEFAULT);
+ e->e_sibling = next;
+ }
/*
** All done.
** Don't send return error message if in VERIFY mode.
*/
- finis(TRUE, ExitStat);
+ finis(true, true, ExitStat);
/* NOTREACHED */
return ExitStat;
}
- /*
-** QUIESCE -- signal handler for SIGPIPE
-**
-** Parameters:
-** sig -- incoming signal.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Sets StopRequest which should cause the mailq/hoststatus
-** display to stop.
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
-*/
-
-/* ARGSUSED */
-static SIGFUNC_DECL
-quiesce(sig)
- int sig;
-{
- int save_errno = errno;
-
- FIX_SYSV_SIGNAL(sig, quiesce);
- StopRequest = TRUE;
- errno = save_errno;
- return SIGFUNC_RETURN;
-}
- /*
+/*
** STOP_SENDMAIL -- Stop the running program
**
** Parameters:
@@ -2102,46 +2777,12 @@ stop_sendmail()
(void) setuid(RealUid);
exit(EX_OK);
}
-
- /*
-** INTINDEBUG -- signal handler for SIGINT in -bt mode
-**
-** Parameters:
-** sig -- incoming signal.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** longjmps back to test mode loop.
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
-**
-** XXX: More work is needed for this signal handler.
-*/
-
-/* ARGSUSED */
-static SIGFUNC_DECL
-intindebug(sig)
- int sig;
-{
- int save_errno = errno;
-
- FIX_SYSV_SIGNAL(sig, intindebug);
- errno = save_errno;
- CHECK_CRITICAL(sig);
-
- errno = save_errno;
- longjmp(TopFrame, 1);
- return SIGFUNC_RETURN;
-}
- /*
+/*
** FINIS -- Clean up and exit.
**
** Parameters:
** drop -- whether or not to drop CurEnv envelope
+** cleanup -- call exit() or _exit()?
** exitstat -- exit status to use for exit() call
**
** Returns:
@@ -2152,75 +2793,144 @@ intindebug(sig)
*/
void
-finis(drop, exitstat)
+finis(drop, cleanup, exitstat)
bool drop;
+ bool cleanup;
volatile int exitstat;
{
/* Still want to process new timeouts added below */
- clear_events();
- (void) releasesignal(SIGALRM);
+ sm_clear_events();
+ (void) sm_releasesignal(SIGALRM);
if (tTd(2, 1))
{
- dprintf("\n====finis: stat %d e_id=%s e_flags=",
- exitstat,
- CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
+ sm_dprintf("\n====finis: stat %d e_id=%s e_flags=",
+ exitstat,
+ CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
printenvflags(CurEnv);
}
if (tTd(2, 9))
- printopenfds(FALSE);
+ printopenfds(false);
- /* if we fail in finis(), just exit */
- if (setjmp(TopFrame) != 0)
- {
- /* failed -- just give it up */
- goto forceexit;
- }
+ SM_TRY
+ /*
+ ** Clean up. This might raise E:mta.quickabort
+ */
- /* clean up temp files */
- CurEnv->e_to = NULL;
- if (drop)
- {
- if (CurEnv->e_id != NULL)
- dropenvelope(CurEnv, TRUE);
- else
- poststats(StatFile);
- }
+ /* clean up temp files */
+ CurEnv->e_to = NULL;
+ if (drop)
+ {
+ if (CurEnv->e_id != NULL)
+ {
+ dropenvelope(CurEnv, true, false);
+ sm_rpool_free(CurEnv->e_rpool);
+ CurEnv->e_rpool = NULL;
+ }
+ else
+ poststats(StatFile);
+ }
- /* flush any cached connections */
- mci_flush(TRUE, NULL);
+ /* flush any cached connections */
+ mci_flush(true, NULL);
- /* close maps belonging to this pid */
- closemaps();
+ /* close maps belonging to this pid */
+ closemaps(false);
#if USERDB
- /* close UserDatabase */
- _udbx_close();
+ /* close UserDatabase */
+ _udbx_close();
#endif /* USERDB */
-#ifdef XLA
- /* clean up extended load average stuff */
- xla_all_end();
+#if SASL
+ stop_sasl_client();
+#endif /* SASL */
+
+#if XLA
+ /* clean up extended load average stuff */
+ xla_all_end();
#endif /* XLA */
- /* and exit */
- forceexit:
- if (LogLevel > 78)
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "finis, pid=%d",
- (int) getpid());
- if (exitstat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
- exitstat = EX_OK;
+ SM_FINALLY
+ /*
+ ** And exit.
+ */
+
+ if (LogLevel > 78)
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "finis, pid=%d",
+ (int) CurrentPid);
+ if (exitstat == EX_TEMPFAIL ||
+ CurEnv->e_errormode == EM_BERKNET)
+ exitstat = EX_OK;
+
+ /* XXX clean up queues and related data structures */
+ cleanup_queues();
+#if SM_CONF_SHM
+ cleanup_shm(DaemonPid == getpid());
+#endif /* SM_CONF_SHM */
+
+ /* reset uid for process accounting */
+ endpwent();
+ sm_mbdb_terminate();
+ (void) setuid(RealUid);
+#if SM_HEAP_CHECK
+ /* dump the heap, if we are checking for memory leaks */
+ if (sm_debug_active(&SmHeapCheck, 2))
+ sm_heap_report(smioout,
+ sm_debug_level(&SmHeapCheck) - 1);
+#endif /* SM_HEAP_CHECK */
+ if (sm_debug_active(&SmXtrapReport, 1))
+ sm_dprintf("xtrap count = %d\n", SmXtrapCount);
+ if (cleanup)
+ exit(exitstat);
+ else
+ _exit(exitstat);
+ SM_END_TRY
+}
+/*
+** INTINDEBUG -- signal handler for SIGINT in -bt mode
+**
+** Parameters:
+** sig -- incoming signal.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** longjmps back to test mode loop.
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
- sync_queue_time();
+/* Type of an exception generated on SIGINT during address test mode. */
+static const SM_EXC_TYPE_T EtypeInterrupt =
+{
+ SmExcTypeMagic,
+ "S:mta.interrupt",
+ "",
+ sm_etype_printf,
+ "interrupt",
+};
- /* reset uid for process accounting */
- endpwent();
- (void) setuid(RealUid);
- exit(exitstat);
+/* ARGSUSED */
+static SIGFUNC_DECL
+intindebug(sig)
+ int sig;
+{
+ int save_errno = errno;
+
+ FIX_SYSV_SIGNAL(sig, intindebug);
+ errno = save_errno;
+ CHECK_CRITICAL(sig);
+ errno = save_errno;
+ sm_exc_raisenew_x(&EtypeInterrupt);
+ errno = save_errno;
+ return SIGFUNC_RETURN;
}
- /*
-** TERM_DEAMON -- SIGTERM handler for the daemon
+/*
+** SIGTERM -- SIGTERM handler for the daemon
**
** Parameters:
** sig -- signal number.
@@ -2239,53 +2949,77 @@ finis(drop, exitstat)
/* ARGSUSED */
static SIGFUNC_DECL
-term_daemon(sig)
+sigterm(sig)
int sig;
{
int save_errno = errno;
- FIX_SYSV_SIGNAL(sig, term_daemon);
+ FIX_SYSV_SIGNAL(sig, sigterm);
ShutdownRequest = "signal";
errno = save_errno;
return SIGFUNC_RETURN;
}
- /*
-** SHUTDOWN_DAEMON -- Performs a clean shutdown of the daemon
+/*
+** SIGHUP -- handle a SIGHUP signal
**
** Parameters:
-** none.
+** sig -- incoming signal.
**
** Returns:
** none.
**
** Side Effects:
-** closes control socket, exits.
+** Sets RestartRequest which should cause the daemon
+** to restart.
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
*/
-void
-shutdown_daemon()
+/* ARGSUSED */
+static SIGFUNC_DECL
+sighup(sig)
+ int sig;
{
- char *reason;
-
- allsignals(TRUE);
-
- reason = ShutdownRequest;
- ShutdownRequest = NULL;
- PendingSignal = 0;
+ int save_errno = errno;
- if (LogLevel > 79)
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt (%s)",
- reason == NULL ? "implicit call" : reason);
+ FIX_SYSV_SIGNAL(sig, sighup);
+ RestartRequest = "signal";
+ errno = save_errno;
+ return SIGFUNC_RETURN;
+}
+/*
+** SIGPIPE -- signal handler for SIGPIPE
+**
+** Parameters:
+** sig -- incoming signal.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Sets StopRequest which should cause the mailq/hoststatus
+** display to stop.
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
- FileName = NULL;
- closecontrolsocket(TRUE);
-#ifdef XLA
- xla_all_end();
-#endif /* XLA */
+/* ARGSUSED */
+static SIGFUNC_DECL
+sigpipe(sig)
+ int sig;
+{
+ int save_errno = errno;
- finis(FALSE, EX_OK);
+ FIX_SYSV_SIGNAL(sig, sigpipe);
+ StopRequest = true;
+ errno = save_errno;
+ return SIGFUNC_RETURN;
}
- /*
+/*
** INTSIG -- clean up on interrupt
**
** This just arranges to exit. It pessimizes in that it
@@ -2312,13 +3046,14 @@ SIGFUNC_DECL
intsig(sig)
int sig;
{
- bool drop = FALSE;
+ bool drop = false;
int save_errno = errno;
FIX_SYSV_SIGNAL(sig, intsig);
errno = save_errno;
CHECK_CRITICAL(sig);
- allsignals(TRUE);
+ sm_allsignals(true);
+
if (sig != 0 && LogLevel > 79)
sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
FileName = NULL;
@@ -2344,91 +3079,17 @@ intsig(sig)
for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
q->q_state = QS_DONTSEND;
- /* and don't try to deliver the partial message either */
- if (InChild)
- ExitStat = EX_QUIT;
-
- drop = TRUE;
+ drop = true;
}
else if (OpMode != MD_TEST)
- unlockqueue(CurEnv);
-
- finis(drop, EX_OK);
-}
- /*
-** INITMACROS -- initialize the macro system
-**
-** This just involves defining some macros that are actually
-** used internally as metasymbols to be themselves.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** initializes several macros to be themselves.
-*/
-
-struct metamac MetaMacros[] =
-{
- /* LHS pattern matching characters */
- { '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE },
- { '=', MATCHCLASS }, { '~', MATCHNCLASS },
-
- /* these are RHS metasymbols */
- { '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER },
- { '>', CALLSUBR },
-
- /* the conditional operations */
- { '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI },
-
- /* the hostname lookup characters */
- { '[', HOSTBEGIN }, { ']', HOSTEND },
- { '(', LOOKUPBEGIN }, { ')', LOOKUPEND },
-
- /* miscellaneous control characters */
- { '&', MACRODEXPAND },
-
- { '\0', '\0' }
-};
-
-#define MACBINDING(name, mid) \
- stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
- MacroName[mid] = name;
-
-void
-initmacros(e)
- register ENVELOPE *e;
-{
- register struct metamac *m;
- register int c;
- char buf[5];
- extern char *MacroName[MAXMACROID + 1];
-
- for (m = MetaMacros; m->metaname != '\0'; m++)
- {
- buf[0] = m->metaval;
- buf[1] = '\0';
- define(m->metaname, newstr(buf), e);
- }
- buf[0] = MATCHREPL;
- buf[2] = '\0';
- for (c = '0'; c <= '9'; c++)
{
- buf[1] = c;
- define(c, newstr(buf), e);
+ unlockqueue(CurEnv);
}
- /* set defaults for some macros sendmail will use later */
- define('n', "MAILER-DAEMON", e);
-
- /* set up external names for some internal macros */
- MACBINDING("opMode", MID_OPMODE);
- /*XXX should probably add equivalents for all short macros here XXX*/
+ finis(drop, false, EX_OK);
+ /* NOTREACHED */
}
- /*
+/*
** DISCONNECT -- remove our connection with any foreground process
**
** Parameters:
@@ -2456,11 +3117,12 @@ disconnect(droplev, e)
int fd;
if (tTd(52, 1))
- dprintf("disconnect: In %d Out %d, e=%lx\n",
- fileno(InChannel), fileno(OutChannel), (u_long) e);
+ sm_dprintf("disconnect: In %d Out %d, e=%p\n",
+ sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL),
+ sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL), e);
if (tTd(52, 100))
{
- dprintf("don't\n");
+ sm_dprintf("don't\n");
return;
}
if (LogLevel > 93)
@@ -2469,40 +3131,64 @@ disconnect(droplev, e)
droplev);
/* be sure we don't get nasty signals */
- (void) setsignal(SIGINT, SIG_IGN);
- (void) setsignal(SIGQUIT, SIG_IGN);
+ (void) sm_signal(SIGINT, SIG_IGN);
+ (void) sm_signal(SIGQUIT, SIG_IGN);
/* we can't communicate with our caller, so.... */
- HoldErrs = TRUE;
+ HoldErrs = true;
CurEnv->e_errormode = EM_MAIL;
Verbose = 0;
- DisConnected = TRUE;
+ DisConnected = true;
/* all input from /dev/null */
- if (InChannel != stdin)
+ if (InChannel != smioin)
{
- (void) fclose(InChannel);
- InChannel = stdin;
+ (void) sm_io_close(InChannel, SM_TIME_DEFAULT);
+ InChannel = smioin;
}
- if (freopen("/dev/null", "r", stdin) == NULL)
+ if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
+ SM_IO_RDONLY, NULL, smioin) == NULL)
sm_syslog(LOG_ERR, e->e_id,
- "disconnect: freopen(\"/dev/null\") failed: %s",
- errstring(errno));
+ "disconnect: sm_io_reopen(\"%s\") failed: %s",
+ SM_PATH_DEVNULL, sm_errstring(errno));
- /* output to the transcript */
- if (OutChannel != stdout)
+ /*
+ ** output to the transcript
+ ** We also compare the fd numbers here since OutChannel
+ ** might be a layer on top of smioout due to encryption
+ ** (see sfsasl.c).
+ */
+
+ if (OutChannel != smioout &&
+ sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL) !=
+ sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL))
{
- (void) fclose(OutChannel);
- OutChannel = stdout;
+ (void) sm_io_close(OutChannel, SM_TIME_DEFAULT);
+ OutChannel = smioout;
+
+#if 0
+ /*
+ ** Has smioout been closed? Reopen it.
+ ** This shouldn't happen anymore, the code is here
+ ** just as a reminder.
+ */
+
+ if (smioout->sm_magic == NULL &&
+ sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
+ SM_IO_WRONLY, NULL, smioout) == NULL)
+ sm_syslog(LOG_ERR, e->e_id,
+ "disconnect: sm_io_reopen(\"%s\") failed: %s",
+ SM_PATH_DEVNULL, sm_errstring(errno));
+#endif /* 0 */
}
if (droplev > 0)
{
- fd = open("/dev/null", O_WRONLY, 0666);
+ fd = open(SM_PATH_DEVNULL, O_WRONLY, 0666);
if (fd == -1)
sm_syslog(LOG_ERR, e->e_id,
- "disconnect: open(\"/dev/null\") failed: %s",
- errstring(errno));
- (void) fflush(stdout);
+ "disconnect: open(\"%s\") failed: %s",
+ SM_PATH_DEVNULL, sm_errstring(errno));
+ (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
(void) dup2(fd, STDOUT_FILENO);
(void) dup2(fd, STDERR_FILENO);
(void) close(fd);
@@ -2520,9 +3206,8 @@ disconnect(droplev, e)
#endif /* XDEBUG */
if (LogLevel > 71)
- sm_syslog(LOG_DEBUG, e->e_id,
- "in background, pid=%d",
- (int) getpid());
+ sm_syslog(LOG_DEBUG, e->e_id, "in background, pid=%d",
+ (int) CurrentPid);
errno = 0;
}
@@ -2540,6 +3225,18 @@ obsolete(argv)
if (ap[0] != '-' || ap[1] == '-')
return;
+#if _FFR_QUARANTINE
+ /* Don't allow users to use "-Q." or "-Q ." */
+ if ((ap[1] == 'Q' && ap[2] == '.') ||
+ (ap[1] == 'Q' && argv[1] != NULL &&
+ argv[1][0] == '.' && argv[1][1] == '\0'))
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Can not use -Q.\n");
+ exit(EX_USAGE);
+ }
+#endif /* _FFR_QUARANTINE */
+
/* skip over options that do have a value */
op = strchr(OPTIONS, ap[1]);
if (op != NULL && *++op == ':' && ap[2] == '\0' &&
@@ -2554,18 +3251,24 @@ obsolete(argv)
}
/* If -C doesn't have an argument, use sendmail.cf. */
-#define __DEFPATH "sendmail.cf"
+#define __DEFPATH "sendmail.cf"
if (ap[1] == 'C' && ap[2] == '\0')
{
*argv = xalloc(sizeof(__DEFPATH) + 2);
- (void) snprintf(argv[0], sizeof(__DEFPATH) + 2, "-C%s",
- __DEFPATH);
+ (void) sm_strlcpyn(argv[0], sizeof(__DEFPATH) + 2, 2,
+ "-C", __DEFPATH);
}
/* If -q doesn't have an argument, run it once. */
if (ap[1] == 'q' && ap[2] == '\0')
*argv = "-q0";
+#if _FFR_QUARANTINE
+ /* If -Q doesn't have an argument, disable quarantining */
+ if (ap[1] == 'Q' && ap[2] == '\0')
+ *argv = "-Q.";
+#endif /* _FFR_QUARANTINE */
+
/* if -d doesn't have an argument, use 0-99.1 */
if (ap[1] == 'd' && ap[2] == '\0')
*argv = "-d0-99.1";
@@ -2581,7 +3284,7 @@ obsolete(argv)
#endif /* defined(sony_news) */
}
}
- /*
+/*
** AUTH_WARNING -- specify authorization warning
**
** Parameters:
@@ -2604,7 +3307,7 @@ auth_warning(e, msg, va_alist)
#endif /* __STDC__ */
{
char buf[MAXLINE];
- VA_LOCAL_DECL
+ SM_VA_LOCAL_DECL
if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
{
@@ -2616,28 +3319,28 @@ auth_warning(e, msg, va_alist)
struct hostent *hp;
hp = myhostname(hostbuf, sizeof hostbuf);
-#if _FFR_FREEHOSTENT && NETINET6
+#if NETINET6
if (hp != NULL)
{
freehostent(hp);
hp = NULL;
}
-#endif /* _FFR_FREEHOSTENT && NETINET6 */
+#endif /* NETINET6 */
}
- (void) snprintf(buf, sizeof buf, "%s: ", hostbuf);
+ (void) sm_strlcpyn(buf, sizeof buf, 2, hostbuf, ": ");
p = &buf[strlen(buf)];
- VA_START(msg);
- vsnprintf(p, SPACELEFT(buf, p), msg, ap);
- VA_END;
- addheader("X-Authentication-Warning", buf, 0, &e->e_header);
+ SM_VA_START(ap, msg);
+ (void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap);
+ SM_VA_END(ap);
+ addheader("X-Authentication-Warning", buf, 0, e);
if (LogLevel > 3)
sm_syslog(LOG_INFO, e->e_id,
"Authentication-Warning: %.400s",
buf);
}
}
- /*
+/*
** GETEXTENV -- get from external environment
**
** Parameters:
@@ -2647,7 +3350,7 @@ auth_warning(e, msg, va_alist)
** The value, if any.
*/
-char *
+static char *
getextenv(envar)
const char *envar;
{
@@ -2662,8 +3365,8 @@ getextenv(envar)
}
return NULL;
}
- /*
-** SETUSERENV -- set an environment in the propogated environment
+/*
+** SETUSERENV -- set an environment in the propagated environment
**
** Parameters:
** envar -- the name of the environment variable.
@@ -2692,10 +3395,11 @@ setuserenv(envar, value)
return;
}
+ /* XXX enforce reasonable size? */
i = strlen(envar) + 1;
l = strlen(value) + i + 1;
p = (char *) xalloc(l);
- (void) snprintf(p, l, "%s=%s", envar, value);
+ (void) sm_strlcpyn(p, l, 3, envar, "=", value);
while (*evp != NULL && strncmp(*evp, p, i) != 0)
evp++;
@@ -2713,7 +3417,7 @@ setuserenv(envar, value)
if (putenv(p) < 0)
syserr("setuserenv: putenv(%s) failed", p);
}
- /*
+/*
** DUMPSTATE -- dump state
**
** For debugging.
@@ -2738,12 +3442,12 @@ dumpstate(when)
"*** $j not in $=w ***");
}
sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)\n",
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)",
NextMacroId, MAXMACROID);
sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
- printopenfds(TRUE);
+ printopenfds(true);
sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
- mci_dump_all(TRUE);
+ mci_dump_all(true);
rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
if (rs > 0)
{
@@ -2752,7 +3456,7 @@ dumpstate(when)
char *pv[MAXATOM + 1];
pv[0] = NULL;
- status = rewrite(pv, rs, 0, CurEnv);
+ status = REWRITE(pv, rs, CurEnv);
sm_syslog(LOG_DEBUG, CurEnv->e_id,
"--- ruleset debug_dumpstate returns stat %d, pv: ---",
status);
@@ -2761,8 +3465,9 @@ dumpstate(when)
}
sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
}
+
#ifdef SIGUSR1
- /*
+/*
** SIGUSR1 -- Signal a request to dump state.
**
** Parameters:
@@ -2784,16 +3489,23 @@ sigusr1(sig)
int sig;
{
int save_errno = errno;
+# if SM_HEAP_CHECK
+ extern void dumpstab __P((void));
+# endif /* SM_HEAP_CHECK */
FIX_SYSV_SIGNAL(sig, sigusr1);
errno = save_errno;
CHECK_CRITICAL(sig);
dumpstate("user signal");
+# if SM_HEAP_CHECK
+ dumpstab();
+# endif /* SM_HEAP_CHECK */
errno = save_errno;
return SIGFUNC_RETURN;
}
-# endif /* SIGUSR1 */
- /*
+#endif /* SIGUSR1 */
+
+/*
** DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
**
** Parameters:
@@ -2813,9 +3525,12 @@ drop_privileges(to_real_uid)
GIDSET_T emptygidset[1];
if (tTd(47, 1))
- dprintf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n",
- (int)to_real_uid, (int)RealUid,
- (int)RealGid, (int)RunAsUid, (int)RunAsGid);
+ sm_dprintf("drop_privileges(%d): Real[UG]id=%d:%d, get[ug]id=%d:%d, gete[ug]id=%d:%d, RunAs[UG]id=%d:%d\n",
+ (int) to_real_uid,
+ (int) RealUid, (int) RealGid,
+ (int) getuid(), (int) getgid(),
+ (int) geteuid(), (int) getegid(),
+ (int) RunAsUid, (int) RunAsGid);
if (to_real_uid)
{
@@ -2826,33 +3541,131 @@ drop_privileges(to_real_uid)
/* make sure no one can grab open descriptors for secret files */
endpwent();
+ sm_mbdb_terminate();
/* reset group permissions; these can be set later */
emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
+
+ /*
+ ** Notice: on some OS (Linux...) the setgroups() call causes
+ ** a logfile entry if sendmail is not run by root.
+ ** However, it is unclear (no POSIX standard) whether
+ ** setgroups() can only succeed if executed by root.
+ ** So for now we keep it as it is; if you want to change it, use
+ ** if (geteuid() == 0 && setgroups(1, emptygidset) == -1)
+ */
+
if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
{
syserr("drop_privileges: setgroups(1, %d) failed",
- (int)emptygidset[0]);
+ (int) emptygidset[0]);
rval = EX_OSERR;
}
- /* reset primary group and user id */
- if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
+ /* reset primary group id */
+ if (to_real_uid)
{
- syserr("drop_privileges: setgid(%d) failed", (int)RunAsGid);
- rval = EX_OSERR;
+ /*
+ ** Drop gid to real gid.
+ ** On some OS we must reset the effective[/real[/saved]] gid,
+ ** and then use setgid() to finally drop all group privileges.
+ ** Later on we check whether we can get back the
+ ** effective gid.
+ */
+
+#if HASSETEGID
+ if (setegid(RunAsGid) < 0)
+ {
+ syserr("drop_privileges: setegid(%d) failed",
+ (int) RunAsGid);
+ rval = EX_OSERR;
+ }
+#else /* HASSETEGID */
+# if HASSETREGID
+ if (setregid(RunAsGid, RunAsGid) < 0)
+ {
+ syserr("drop_privileges: setregid(%d, %d) failed",
+ (int) RunAsGid, (int) RunAsGid);
+ rval = EX_OSERR;
+ }
+# else /* HASSETREGID */
+# if HASSETRESGID
+ if (setresgid(RunAsGid, RunAsGid, RunAsGid) < 0)
+ {
+ syserr("drop_privileges: setresgid(%d, %d, %d) failed",
+ (int) RunAsGid, (int) RunAsGid, (int) RunAsGid);
+ rval = EX_OSERR;
+ }
+# endif /* HASSETRESGID */
+# endif /* HASSETREGID */
+#endif /* HASSETEGID */
+ }
+ if (rval == EX_OK && (to_real_uid || RunAsGid != 0))
+ {
+ if (setgid(RunAsGid) < 0 && (!UseMSP || getegid() != RunAsGid))
+ {
+ syserr("drop_privileges: setgid(%d) failed",
+ (int) RunAsGid);
+ rval = EX_OSERR;
+ }
+ errno = 0;
+ if (rval == EX_OK && getegid() != RunAsGid)
+ {
+ syserr("drop_privileges: Unable to set effective gid=%d to RunAsGid=%d",
+ (int) getegid(), (int) RunAsGid);
+ rval = EX_OSERR;
+ }
}
+
+ /* fiddle with uid */
if (to_real_uid || RunAsUid != 0)
{
uid_t euid = geteuid();
- if (setuid(RunAsUid) < 0)
+ /*
+ ** Try to setuid(RunAsUid).
+ ** euid must be RunAsUid,
+ ** ruid must be RunAsUid unless it's the MSP and the euid
+ ** wasn't 0 and we didn't have to drop privileges to the
+ ** real uid.
+ */
+
+ if (setuid(RunAsUid) < 0 ||
+ (getuid() != RunAsUid &&
+ (!UseMSP || euid == 0 || to_real_uid )) ||
+ geteuid() != RunAsUid)
{
- syserr("drop_privileges: setuid(%d) failed",
- (int)RunAsUid);
- rval = EX_OSERR;
+#if HASSETREUID
+ /*
+ ** if ruid != RunAsUid, euid == RunAsUid, then
+ ** try resetting just the real uid, then using
+ ** setuid() to drop the saved-uid as well.
+ */
+
+ if (euid == RunAsUid)
+ {
+ if (setreuid(RunAsUid, -1) < 0)
+ {
+ syserr("drop_privileges: setreuid(%d, -1) failed",
+ (int) RunAsUid);
+ rval = EX_OSERR;
+ }
+ if (setuid(RunAsUid) < 0)
+ {
+ syserr("drop_privileges: second setuid(%d) attempt failed",
+ (int) RunAsUid);
+ rval = EX_OSERR;
+ }
+ }
+ else
+#endif /* HASSETREUID */
+ {
+ syserr("drop_privileges: setuid(%d) failed",
+ (int) RunAsUid);
+ rval = EX_OSERR;
+ }
}
- else if (RunAsUid != 0 && setuid(0) == 0)
+ if (RunAsUid != 0 && setuid(0) == 0)
{
/*
** Believe it or not, the Linux capability model
@@ -2872,23 +3685,37 @@ drop_privileges(to_real_uid)
** making it possible to set it back again later.
*/
- syserr("drop_privileges: Unable to drop non-root set-user-id privileges");
+ syserr("drop_privileges: Unable to drop non-root set-user-ID privileges");
rval = EX_OSERR;
}
}
+
+ if ((to_real_uid || RunAsGid != 0) &&
+ rval == EX_OK && RunAsGid != EffGid &&
+ getuid() != 0 && geteuid() != 0)
+ {
+ errno = 0;
+ if (setgid(EffGid) == 0)
+ {
+ syserr("drop_privileges: setgid(%d) succeeded (when it should not)",
+ (int) EffGid);
+ rval = EX_OSERR;
+ }
+ }
+
if (tTd(47, 5))
{
- dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
- (int)geteuid(), (int)getuid(),
- (int)getegid(), (int)getgid());
- dprintf("drop_privileges: RunAsUser = %d:%d\n",
- (int)RunAsUid, (int)RunAsGid);
+ sm_dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
+ (int) geteuid(), (int) getuid(),
+ (int) getegid(), (int) getgid());
+ sm_dprintf("drop_privileges: RunAsUser = %d:%d\n",
+ (int) RunAsUid, (int) RunAsGid);
if (tTd(47, 10))
- dprintf("drop_privileges: rval = %d\n", rval);
+ sm_dprintf("drop_privileges: rval = %d\n", rval);
}
return rval;
}
- /*
+/*
** FILL_FD -- make sure a file descriptor has been properly allocated
**
** Used to make sure that stdin/out/err are allocated on startup
@@ -2901,6 +3728,9 @@ drop_privileges(to_real_uid)
**
** Returns:
** none
+**
+** Side Effects:
+** possibly changes MissingFds
*/
void
@@ -2918,11 +3748,11 @@ fill_fd(fd, where)
syserr("fill_fd: %s: fd %d not open", where, fd);
else
MissingFds |= 1 << fd;
- i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666);
+ i = open(SM_PATH_DEVNULL, fd == 0 ? O_RDONLY : O_WRONLY, 0666);
if (i < 0)
{
- syserr("!fill_fd: %s: cannot open /dev/null",
- where == NULL ? "startup" : where);
+ syserr("!fill_fd: %s: cannot open %s",
+ where == NULL ? "startup" : where, SM_PATH_DEVNULL);
}
if (fd != i)
{
@@ -2930,7 +3760,42 @@ fill_fd(fd, where)
(void) close(i);
}
}
- /*
+/*
+** SM_PRINTOPTIONS -- print options
+**
+** Parameters:
+** options -- array of options.
+**
+** Returns:
+** none.
+*/
+
+static void
+sm_printoptions(options)
+ char **options;
+{
+ int ll;
+ char **av;
+
+ av = options;
+ ll = 7;
+ while (*av != NULL)
+ {
+ if (ll + strlen(*av) > 63)
+ {
+ sm_dprintf("\n");
+ ll = 0;
+ }
+ if (ll == 0)
+ sm_dprintf("\t\t");
+ else
+ sm_dprintf(" ");
+ sm_dprintf("%s", *av);
+ ll += strlen(*av++) + 1;
+ }
+ sm_dprintf("\n");
+}
+/*
** TESTMODELINE -- process a test mode input line
**
** Parameters:
@@ -2961,11 +3826,7 @@ testmodeline(line, e)
ADDRESS a;
static int tryflags = RF_COPYNONE;
char exbuf[MAXLINE];
- extern u_char TokTypeNoC[];
-
-#if _FFR_ADDR_TYPE
- define(macid("{addr_type}", NULL), "e r", e);
-#endif /* _FFR_ADDR_TYPE */
+ extern unsigned char TokTypeNoC[];
/* skip leading spaces */
while (*line == ' ')
@@ -2985,18 +3846,18 @@ testmodeline(line, e)
switch (line[1])
{
case 'D':
- mid = macid(&line[2], &delimptr);
+ mid = macid_parse(&line[2], &delimptr);
if (mid == 0)
return;
translate_dollars(delimptr);
- define(mid, newstr(delimptr), e);
+ macdefine(&e->e_macro, A_TEMP, mid, delimptr);
break;
case 'C':
if (line[2] == '\0') /* not to call syserr() */
return;
- mid = macid(&line[2], &delimptr);
+ mid = macid_parse(&line[2], &delimptr);
if (mid == 0)
return;
translate_dollars(delimptr);
@@ -3021,11 +3882,13 @@ testmodeline(line, e)
break;
case '\0':
- printf("Usage: .[DC]macro value(s)\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: .[DC]macro value(s)\n");
break;
default:
- printf("Unknown \".\" command %s\n", line);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Unknown \".\" command %s\n", line);
break;
}
return;
@@ -3037,7 +3900,8 @@ testmodeline(line, e)
rs = strtorwset(&line[2], NULL, ST_FIND);
if (rs < 0)
{
- printf("Undefined ruleset %s\n", &line[2]);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Undefined ruleset %s\n", &line[2]);
return;
}
rw = RewriteRules[rs];
@@ -3045,22 +3909,28 @@ testmodeline(line, e)
return;
do
{
- (void) putchar('R');
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
+ 'R');
s = rw->r_lhs;
while (*s != NULL)
{
xputs(*s++);
- (void) putchar(' ');
+ (void) sm_io_putc(smioout,
+ SM_TIME_DEFAULT, ' ');
}
- (void) putchar('\t');
- (void) putchar('\t');
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
+ '\t');
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
+ '\t');
s = rw->r_rhs;
while (*s != NULL)
{
xputs(*s++);
- (void) putchar(' ');
+ (void) sm_io_putc(smioout,
+ SM_TIME_DEFAULT, ' ');
}
- (void) putchar('\n');
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
+ '\n');
} while ((rw = rw->r_next) != NULL);
break;
@@ -3073,11 +3943,13 @@ testmodeline(line, e)
break;
case '\0':
- printf("Usage: =Sruleset or =M\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: =Sruleset or =M\n");
break;
default:
- printf("Unknown \"=\" command %s\n", line);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Unknown \"=\" command %s\n", line);
break;
}
return;
@@ -3090,11 +3962,13 @@ testmodeline(line, e)
break;
case '\0':
- printf("Usage: -d{debug arguments}\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: -d{debug arguments}\n");
break;
default:
- printf("Unknown \"-\" command %s\n", line);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Unknown \"-\" command %s\n", line);
break;
}
return;
@@ -3102,21 +3976,23 @@ testmodeline(line, e)
case '$':
if (line[1] == '=')
{
- mid = macid(&line[2], NULL);
+ mid = macid(&line[2]);
if (mid != 0)
stabapply(dump_class, mid);
return;
}
- mid = macid(&line[1], NULL);
+ mid = macid(&line[1]);
if (mid == 0)
return;
p = macvalue(mid, e);
if (p == NULL)
- printf("Undefined\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Undefined\n");
else
{
xputs(p);
- printf("\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\n");
}
return;
@@ -3134,15 +4010,17 @@ testmodeline(line, e)
p = "";
if (line[1] == '\0')
{
- printf("Usage: /[canon|map|mx|parse|try|tryflags]\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: /[canon|map|mx|parse|try|tryflags]\n");
return;
}
- if (strcasecmp(&line[1], "quit") == 0)
+ if (sm_strcasecmp(&line[1], "quit") == 0)
{
CurEnv->e_id = NULL;
- finis(TRUE, ExitStat);
+ finis(true, true, ExitStat);
+ /* NOTREACHED */
}
- if (strcasecmp(&line[1], "mx") == 0)
+ if (sm_strcasecmp(&line[1], "mx") == 0)
{
#if NAMED_BIND
/* look up MX records */
@@ -3152,75 +4030,95 @@ testmodeline(line, e)
if (*p == '\0')
{
- printf("Usage: /mx address\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: /mx address\n");
return;
}
- nmx = getmxrr(p, mxhosts, NULL, FALSE, &rcode);
- printf("getmxrr(%s) returns %d value(s):\n", p, nmx);
+ nmx = getmxrr(p, mxhosts, NULL, false, &rcode, true,
+ NULL);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "getmxrr(%s) returns %d value(s):\n",
+ p, nmx);
for (i = 0; i < nmx; i++)
- printf("\t%s\n", mxhosts[i]);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\t%s\n", mxhosts[i]);
#else /* NAMED_BIND */
- printf("No MX code compiled in\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "No MX code compiled in\n");
#endif /* NAMED_BIND */
}
- else if (strcasecmp(&line[1], "canon") == 0)
+ else if (sm_strcasecmp(&line[1], "canon") == 0)
{
char host[MAXHOSTNAMELEN];
if (*p == '\0')
{
- printf("Usage: /canon address\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: /canon address\n");
return;
}
- else if (strlcpy(host, p, sizeof host) >= sizeof host)
+ else if (sm_strlcpy(host, p, sizeof host) >= sizeof host)
{
- printf("Name too long\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Name too long\n");
return;
}
- (void) getcanonname(host, sizeof host, HasWildcardMX);
- printf("getcanonname(%s) returns %s\n", p, host);
+ (void) getcanonname(host, sizeof host, HasWildcardMX,
+ NULL);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "getcanonname(%s) returns %s\n",
+ p, host);
}
- else if (strcasecmp(&line[1], "map") == 0)
+ else if (sm_strcasecmp(&line[1], "map") == 0)
{
auto int rcode = EX_OK;
char *av[2];
if (*p == '\0')
{
- printf("Usage: /map mapname key\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: /map mapname key\n");
return;
}
- for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++)
+ for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++)
continue;
if (*q == '\0')
{
- printf("No key specified\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "No key specified\n");
return;
}
*q++ = '\0';
map = stab(p, ST_MAP, ST_FIND);
if (map == NULL)
{
- printf("Map named \"%s\" not found\n", p);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Map named \"%s\" not found\n", p);
return;
}
if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
!openmap(&(map->s_map)))
{
- printf("Map named \"%s\" not open\n", p);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Map named \"%s\" not open\n", p);
return;
}
- printf("map_lookup: %s (%s) ", p, q);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "map_lookup: %s (%s) ", p, q);
av[0] = q;
av[1] = NULL;
p = (*map->s_map.map_class->map_lookup)
(&map->s_map, q, av, &rcode);
if (p == NULL)
- printf("no match (%d)\n", rcode);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "no match (%d)\n",
+ rcode);
else
- printf("returns %s (%d)\n", p, rcode);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "returns %s (%d)\n", p,
+ rcode);
}
- else if (strcasecmp(&line[1], "try") == 0)
+ else if (sm_strcasecmp(&line[1], "try") == 0)
{
MAILER *m;
STAB *st;
@@ -3234,30 +4132,36 @@ testmodeline(line, e)
}
if (q == NULL || *q == '\0')
{
- printf("Usage: /try mailer address\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: /try mailer address\n");
return;
}
st = stab(p, ST_MAILER, ST_FIND);
if (st == NULL)
{
- printf("Unknown mailer %s\n", p);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Unknown mailer %s\n", p);
return;
}
m = st->s_mailer;
- printf("Trying %s %s address %s for mailer %s\n",
- bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
- bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient",
- q, p);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Trying %s %s address %s for mailer %s\n",
+ bitset(RF_HEADERADDR, tryflags) ? "header"
+ : "envelope",
+ bitset(RF_SENDERADDR, tryflags) ? "sender"
+ : "recipient", q, p);
p = remotename(q, m, tryflags, &rcode, CurEnv);
- printf("Rcode = %d, addr = %s\n",
- rcode, p == NULL ? "<NULL>" : p);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Rcode = %d, addr = %s\n",
+ rcode, p == NULL ? "<NULL>" : p);
e->e_to = NULL;
}
- else if (strcasecmp(&line[1], "tryflags") == 0)
+ else if (sm_strcasecmp(&line[1], "tryflags") == 0)
{
if (*p == '\0')
{
- printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
return;
}
for (; *p != '\0'; p++)
@@ -3285,40 +4189,53 @@ testmodeline(line, e)
break;
}
}
-#if _FFR_ADDR_TYPE
exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e';
exbuf[1] = ' ';
exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r';
exbuf[3] = '\0';
- define(macid("{addr_type}", NULL), newstr(exbuf), e);
-#endif /* _FFR_ADDR_TYPE */
+ macdefine(&e->e_macro, A_TEMP,
+ macid("{addr_type}"), exbuf);
}
- else if (strcasecmp(&line[1], "parse") == 0)
+ else if (sm_strcasecmp(&line[1], "parse") == 0)
{
if (*p == '\0')
{
- printf("Usage: /parse address\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: /parse address\n");
return;
}
q = crackaddr(p);
- printf("Cracked address = ");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Cracked address = ");
xputs(q);
- printf("\nParsing %s %s address\n",
- bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
- bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient");
- if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL)
- printf("Cannot parse\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\nParsing %s %s address\n",
+ bitset(RF_HEADERADDR, tryflags) ?
+ "header" : "envelope",
+ bitset(RF_SENDERADDR, tryflags) ?
+ "sender" : "recipient");
+ if (parseaddr(p, &a, tryflags, '\0', NULL, e, true)
+ == NULL)
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Cannot parse\n");
else if (a.q_host != NULL && a.q_host[0] != '\0')
- printf("mailer %s, host %s, user %s\n",
- a.q_mailer->m_name, a.q_host, a.q_user);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "mailer %s, host %s, user %s\n",
+ a.q_mailer->m_name,
+ a.q_host,
+ a.q_user);
else
- printf("mailer %s, user %s\n",
- a.q_mailer->m_name, a.q_user);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "mailer %s, user %s\n",
+ a.q_mailer->m_name,
+ a.q_user);
e->e_to = NULL;
}
else
{
- printf("Unknown \"/\" command %s\n", line);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Unknown \"/\" command %s\n",
+ line);
}
return;
}
@@ -3330,11 +4247,12 @@ testmodeline(line, e)
p++;
if (*p == '\0')
{
- printf("No address!\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "No address!\n");
return;
}
*p = '\0';
- if (invalidaddr(p + 1, NULL))
+ if (invalidaddr(p + 1, NULL, true))
return;
do
{
@@ -3353,13 +4271,16 @@ testmodeline(line, e)
rs = strtorwset(p, NULL, ST_FIND);
if (rs < 0)
{
- printf("Undefined ruleset %s\n", p);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Undefined ruleset %s\n",
+ p);
break;
}
- status = rewrite(pvp, rs, 0, e);
+ status = REWRITE(pvp, rs, e);
if (status != EX_OK)
- printf("== Ruleset %s (%d) status %d\n",
- p, rs, status);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "== Ruleset %s (%d) status %d\n",
+ p, rs, status);
while (*p != '\0' && *p++ != ',')
continue;
}
@@ -3371,8 +4292,26 @@ dump_class(s, id)
register STAB *s;
int id;
{
- if (s->s_type != ST_CLASS)
+ if (s->s_symtype != ST_CLASS)
return;
if (bitnset(bitidx(id), s->s_class))
- printf("%s\n", s->s_name);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "%s\n", s->s_name);
}
+
+/*
+** An exception type used to create QuickAbort exceptions.
+** This is my first cut at converting QuickAbort from longjmp to exceptions.
+** These exceptions have a single integer argument, which is the argument
+** to longjmp in the original code (either 1 or 2). I don't know the
+** significance of 1 vs 2: the calls to setjmp don't care.
+*/
+
+const SM_EXC_TYPE_T EtypeQuickAbort =
+{
+ SmExcTypeMagic,
+ "E:mta.quickabort",
+ "i",
+ sm_etype_printf,
+ "quick abort %0",
+};
diff --git a/contrib/sendmail/src/map.c b/contrib/sendmail/src/map.c
index 78f8a02..7205da0 100644
--- a/contrib/sendmail/src/map.c
+++ b/contrib/sendmail/src/map.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1992, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1992, 1993
@@ -11,14 +11,15 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: map.c,v 8.414.4.55 2001/08/15 22:08:58 gshapiro Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
+SM_RCSID("@(#)$Id: map.c,v 8.618 2002/01/11 22:06:52 gshapiro Exp $")
+
+#if LDAPMAP
+# include <sm/ldap.h>
+#endif /* LDAPMAP */
-#ifdef NDBM
+#if NDBM
# include <ndbm.h>
# ifdef R_FIRST
ERROR README: You are running the Berkeley DB version of ndbm.h. See
@@ -27,21 +28,21 @@ static char id[] = "@(#)$Id: map.c,v 8.414.4.55 2001/08/15 22:08:58 gshapiro Exp
ERROR README: and use -DNEWDB instead.
# endif /* R_FIRST */
#endif /* NDBM */
-#ifdef NEWDB
+#if NEWDB
# include <db.h>
# ifndef DB_VERSION_MAJOR
# define DB_VERSION_MAJOR 1
# endif /* ! DB_VERSION_MAJOR */
#endif /* NEWDB */
-#ifdef NIS
+#if NIS
struct dom_binding; /* forward reference needed on IRIX */
# include <rpcsvc/ypclnt.h>
-# ifdef NDBM
+# if NDBM
# define NDBM_YP_COMPAT /* create YP-compatible NDBM files */
# endif /* NDBM */
#endif /* NIS */
-#ifdef NEWDB
+#if NEWDB
# if DB_VERSION_MAJOR < 2
static bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
# endif /* DB_VERSION_MAJOR < 2 */
@@ -53,20 +54,15 @@ static bool db_map_open __P((MAP *, int, char *, DBTYPE, void **));
# endif /* DB_VERSION_MAJOR > 2 */
#endif /* NEWDB */
static bool extract_canonname __P((char *, char *, char *, char[], int));
-#ifdef LDAPMAP
-static void ldapmap_clear __P((LDAPMAP_STRUCT *));
-static STAB *ldapmap_findconn __P((LDAPMAP_STRUCT *));
-static int ldapmap_geterrno __P((LDAP *));
-static void ldapmap_setopts __P((LDAP *, LDAPMAP_STRUCT *));
-static bool ldapmap_start __P((MAP *));
-static void ldaptimeout __P((int));
-#endif /* LDAPMAP */
static void map_close __P((STAB *, int));
static void map_init __P((STAB *, int));
-#ifdef NISPLUS
+#ifdef LDAPMAP
+static STAB * ldapmap_findconn __P((SM_LDAP_STRUCT *));
+#endif /* LDAPMAP */
+#if NISPLUS
static bool nisplus_getcanonname __P((char *, int, int *));
#endif /* NISPLUS */
-#ifdef NIS
+#if NIS
static bool nis_getcanonname __P((char *, int, int *));
#endif /* NIS */
#if NETINFO
@@ -74,14 +70,25 @@ static bool ni_getcanonname __P((char *, int, int *));
#endif /* NETINFO */
static bool text_getcanonname __P((char *, int, int *));
+/* default error message for trying to open a map in write mode */
+#ifdef ENOSYS
+# define SM_EMAPCANTWRITE ENOSYS
+#else /* ENOSYS */
+# ifdef EFTYPE
+# define SM_EMAPCANTWRITE EFTYPE
+# else /* EFTYPE */
+# define SM_EMAPCANTWRITE ENXIO
+# endif /* EFTYPE */
+#endif /* ENOSYS */
+
/*
** MAP.C -- implementations for various map classes.
**
** Each map class implements a series of functions:
**
** bool map_parse(MAP *map, char *args)
-** Parse the arguments from the config file. Return TRUE
-** if they were ok, FALSE otherwise. Fill in map with the
+** Parse the arguments from the config file. Return true
+** if they were ok, false otherwise. Fill in map with the
** values.
**
** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
@@ -99,9 +106,9 @@ static bool text_getcanonname __P((char *, int, int *));
**
** bool map_open(MAP *map, int mode)
** Open the map for the indicated mode. Mode should
-** be either O_RDONLY or O_RDWR. Return TRUE if it
-** was opened successfully, FALSE otherwise. If the open
-** failed an the MF_OPTIONAL flag is not set, it should
+** be either O_RDONLY or O_RDWR. Return true if it
+** was opened successfully, false otherwise. If the open
+** failed and the MF_OPTIONAL flag is not set, it should
** also print an error. If the MF_ALIAS bit is set
** and this map class understands the @:@ convention, it
** should call aliaswait() before returning.
@@ -116,17 +123,13 @@ static bool text_getcanonname __P((char *, int, int *));
#define DBMMODE 0644
-#ifndef EX_NOTFOUND
-# define EX_NOTFOUND EX_NOHOST
-#endif /* ! EX_NOTFOUND */
-
#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
# define LOCK_ON_OPEN 1 /* we can open/create a locked file */
#else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
# define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
#endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
- /*
+/*
** MAP_PARSEARGS -- parse config line arguments for database lookup
**
** This is a generic version of the map_parse method.
@@ -136,8 +139,8 @@ static bool text_getcanonname __P((char *, int, int *));
** ap -- a pointer to the args on the config line.
**
** Returns:
-** TRUE -- if everything parsed OK.
-** FALSE -- otherwise.
+** true -- if everything parsed OK.
+** false -- otherwise.
**
** Side Effects:
** null terminates the filename; stores it in map
@@ -151,10 +154,11 @@ map_parseargs(map, ap)
register char *p = ap;
/*
- ** there is no check whether there is really an argument,
- ** but that's not important enough to warrant extra code
+ ** There is no check whether there is really an argument,
+ ** but that's not important enough to warrant extra code.
*/
- map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
+
+ map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
map->map_spacesub = SpaceSub; /* default value */
for (;;)
{
@@ -285,11 +289,11 @@ map_parseargs(map, ap)
{
syserr("No file name for %s map %s",
map->map_class->map_cname, map->map_mname);
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
- /*
+/*
** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
**
** It also adds the map_app string. It can be used as a utility
@@ -304,9 +308,6 @@ map_parseargs(map, ap)
** Returns:
** Pointer to rewritten result. This is static data that
** should be copied if it is to be saved!
-**
-** Side Effects:
-** none.
*/
char *
@@ -327,15 +328,15 @@ map_rewrite(map, s, slen, av)
if (tTd(39, 1))
{
- dprintf("map_rewrite(%.*s), av =", (int)slen, s);
+ sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
if (av == NULL)
- dprintf(" (nullv)");
+ sm_dprintf(" (nullv)");
else
{
for (avp = av; *avp != NULL; avp++)
- dprintf("\n\t%s", *avp);
+ sm_dprintf("\n\t%s", *avp);
}
- dprintf("\n");
+ sm_dprintf("\n");
}
/* count expected size of output (can safely overestimate) */
@@ -368,7 +369,7 @@ map_rewrite(map, s, slen, av)
buflen = len;
if (buf != NULL)
sm_free(buf);
- buf = xalloc(buflen);
+ buf = sm_pmalloc_x(buflen);
}
bp = buf;
@@ -388,7 +389,7 @@ map_rewrite(map, s, slen, av)
{
pushc:
if (--len <= 0)
- break;
+ break;
*bp++ = c;
continue;
}
@@ -413,14 +414,14 @@ map_rewrite(map, s, slen, av)
}
}
if (map->map_app != NULL && len > 0)
- (void) strlcpy(bp, map->map_app, len);
+ (void) sm_strlcpy(bp, map->map_app, len);
else
*bp = '\0';
if (tTd(39, 1))
- dprintf("map_rewrite => %s\n", buf);
+ sm_dprintf("map_rewrite => %s\n", buf);
return buf;
}
- /*
+/*
** INITMAPS -- rebuild alias maps
**
** Parameters:
@@ -441,7 +442,7 @@ initmaps()
checkfd012("exiting initmaps");
#endif /* XDEBUG */
}
- /*
+/*
** MAP_INIT -- rebuild a map
**
** Parameters:
@@ -464,7 +465,7 @@ map_init(s, unused)
register MAP *map;
/* has to be a map */
- if (s->s_type != ST_MAP)
+ if (s->s_symtype != ST_MAP)
return;
map = &s->s_map;
@@ -472,7 +473,7 @@ map_init(s, unused)
return;
if (tTd(38, 2))
- dprintf("map_init(%s:%s, %s)\n",
+ sm_dprintf("map_init(%s:%s, %s)\n",
map->map_class->map_cname == NULL ? "NULL" :
map->map_class->map_cname,
map->map_mname == NULL ? "NULL" : map->map_mname,
@@ -482,7 +483,7 @@ map_init(s, unused)
!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
{
if (tTd(38, 3))
- dprintf("\tnot rebuildable\n");
+ sm_dprintf("\tnot rebuildable\n");
return;
}
@@ -494,10 +495,10 @@ map_init(s, unused)
map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
}
- (void) rebuildaliases(map, FALSE);
+ (void) rebuildaliases(map, false);
return;
}
- /*
+/*
** OPENMAP -- open a map
**
** Parameters:
@@ -505,39 +506,38 @@ map_init(s, unused)
**
** Returns:
** whether open succeeded.
-**
*/
bool
openmap(map)
MAP *map;
{
- bool restore = FALSE;
+ bool restore = false;
bool savehold = HoldErrs;
bool savequick = QuickAbort;
int saveerrors = Errors;
if (!bitset(MF_VALID, map->map_mflags))
- return FALSE;
+ return false;
/* better safe than sorry... */
if (bitset(MF_OPEN, map->map_mflags))
- return TRUE;
+ return true;
/* Don't send a map open error out via SMTP */
if ((OnlyOneError || QuickAbort) &&
(OpMode == MD_SMTP || OpMode == MD_DAEMON))
{
- restore = TRUE;
- HoldErrs = TRUE;
- QuickAbort = FALSE;
+ restore = true;
+ HoldErrs = true;
+ QuickAbort = false;
}
errno = 0;
if (map->map_class->map_open(map, O_RDONLY))
{
if (tTd(38, 4))
- dprintf("openmap()\t%s:%s %s: valid\n",
+ sm_dprintf("openmap()\t%s:%s %s: valid\n",
map->map_class->map_cname == NULL ? "NULL" :
map->map_class->map_cname,
map->map_mname == NULL ? "NULL" :
@@ -545,12 +545,12 @@ openmap(map)
map->map_file == NULL ? "NULL" :
map->map_file);
map->map_mflags |= MF_OPEN;
- map->map_pid = getpid();
+ map->map_pid = CurrentPid;
}
else
{
if (tTd(38, 4))
- dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
+ sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
map->map_class->map_cname == NULL ? "NULL" :
map->map_class->map_cname,
map->map_mname == NULL ? "NULL" :
@@ -558,15 +558,15 @@ openmap(map)
map->map_file == NULL ? "NULL" :
map->map_file,
errno == 0 ? "" : ": ",
- errno == 0 ? "" : errstring(errno));
+ errno == 0 ? "" : sm_errstring(errno));
if (!bitset(MF_OPTIONAL, map->map_mflags))
{
extern MAPCLASS BogusMapClass;
+ map->map_orgclass = map->map_class;
map->map_class = &BogusMapClass;
- map->map_mflags |= MF_OPEN;
- map->map_pid = getpid();
- MapOpenErr = TRUE;
+ map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
+ map->map_pid = CurrentPid;
}
else
{
@@ -584,27 +584,28 @@ openmap(map)
return bitset(MF_OPEN, map->map_mflags);
}
- /*
+/*
** CLOSEMAPS -- close all open maps opened by the current pid.
**
** Parameters:
-** none
+** bogus -- only close bogus maps.
**
** Returns:
** none.
*/
void
-closemaps()
+closemaps(bogus)
+ bool bogus;
{
- stabapply(map_close, 0);
+ stabapply(map_close, bogus);
}
- /*
+/*
** MAP_CLOSE -- close a map opened by the current pid.
**
** Parameters:
-** s -- STAB entry: if map: try to open
-** second parameter is unused (required by stabapply())
+** s -- STAB entry: if map: try to close
+** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
**
** Returns:
** none.
@@ -612,78 +613,100 @@ closemaps()
/* ARGSUSED1 */
static void
-map_close(s, unused)
+map_close(s, bogus)
register STAB *s;
- int unused;
+ int bogus; /* int because of stabapply(), used as bool */
{
MAP *map;
+ extern MAPCLASS BogusMapClass;
- if (s->s_type != ST_MAP)
+ if (s->s_symtype != ST_MAP)
return;
map = &s->s_map;
+ /*
+ ** close the map iff:
+ ** it is valid and open and opened by this process
+ ** and (!bogus or it's a bogus map or it is not persistent)
+ ** negate this: return iff
+ ** it is not valid or it is not open or not opened by this process
+ ** or (bogus and it's not a bogus map and it's not not-persistent)
+ */
+
if (!bitset(MF_VALID, map->map_mflags) ||
!bitset(MF_OPEN, map->map_mflags) ||
bitset(MF_CLOSING, map->map_mflags) ||
- map->map_pid != getpid())
+ map->map_pid != CurrentPid ||
+ (bogus && map->map_class != &BogusMapClass &&
+ !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
return;
+ if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
+ map->map_orgclass != &BogusMapClass)
+ map->map_class = map->map_orgclass;
if (tTd(38, 5))
- dprintf("closemaps: closing %s (%s)\n",
+ sm_dprintf("closemaps: closing %s (%s)\n",
map->map_mname == NULL ? "NULL" : map->map_mname,
map->map_file == NULL ? "NULL" : map->map_file);
- map->map_mflags |= MF_CLOSING;
- map->map_class->map_close(map);
- map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
+ if (!bitset(MF_OPENBOGUS, map->map_mflags))
+ {
+ map->map_mflags |= MF_CLOSING;
+ map->map_class->map_close(map);
+ }
+ map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
}
- /*
+/*
** GETCANONNAME -- look up name using service switch
**
** Parameters:
** host -- the host name to look up.
** hbsize -- the size of the host buffer.
** trymx -- if set, try MX records.
+** pttl -- pointer to return TTL (can be NULL).
**
** Returns:
-** TRUE -- if the host was found.
-** FALSE -- otherwise.
+** true -- if the host was found.
+** false -- otherwise.
*/
bool
-getcanonname(host, hbsize, trymx)
+getcanonname(host, hbsize, trymx, pttl)
char *host;
int hbsize;
bool trymx;
+ int *pttl;
{
int nmaps;
int mapno;
- bool found = FALSE;
- bool got_tempfail = FALSE;
+ bool found = false;
+ bool got_tempfail = false;
auto int status;
char *maptype[MAXMAPSTACK];
short mapreturn[MAXMAPACTIONS];
nmaps = switch_map_find("hosts", maptype, mapreturn);
+ if (pttl != 0)
+ *pttl = SM_DEFAULT_TTL;
for (mapno = 0; mapno < nmaps; mapno++)
{
int i;
if (tTd(38, 20))
- dprintf("getcanonname(%s), trying %s\n",
+ sm_dprintf("getcanonname(%s), trying %s\n",
host, maptype[mapno]);
if (strcmp("files", maptype[mapno]) == 0)
{
found = text_getcanonname(host, hbsize, &status);
}
-#ifdef NIS
+#if NIS
else if (strcmp("nis", maptype[mapno]) == 0)
{
found = nis_getcanonname(host, hbsize, &status);
}
#endif /* NIS */
-#ifdef NISPLUS
+#if NISPLUS
else if (strcmp("nisplus", maptype[mapno]) == 0)
{
found = nisplus_getcanonname(host, hbsize, &status);
@@ -692,7 +715,7 @@ getcanonname(host, hbsize, trymx)
#if NAMED_BIND
else if (strcmp("dns", maptype[mapno]) == 0)
{
- found = dns_getcanonname(host, hbsize, trymx, &status);
+ found = dns_getcanonname(host, hbsize, trymx, &status, pttl);
}
#endif /* NAMED_BIND */
#if NETINFO
@@ -703,7 +726,7 @@ getcanonname(host, hbsize, trymx)
#endif /* NETINFO */
else
{
- found = FALSE;
+ found = false;
status = EX_UNAVAILABLE;
}
@@ -723,7 +746,7 @@ getcanonname(host, hbsize, trymx)
if (status == EX_TEMPFAIL)
{
i = MA_TRYAGAIN;
- got_tempfail = TRUE;
+ got_tempfail = true;
}
else if (status == EX_NOTFOUND)
i = MA_NOTFOUND;
@@ -738,7 +761,7 @@ getcanonname(host, hbsize, trymx)
char *d;
if (tTd(38, 20))
- dprintf("getcanonname(%s), found\n", host);
+ sm_dprintf("getcanonname(%s), found\n", host);
/*
** If returned name is still single token, compensate
@@ -753,27 +776,29 @@ getcanonname(host, hbsize, trymx)
hbsize > (int) (strlen(host) + strlen(d) + 1))
{
if (host[strlen(host) - 1] != '.')
- (void) strlcat(host, ".", hbsize);
- (void) strlcat(host, d, hbsize);
+ (void) sm_strlcat2(host, ".", d,
+ hbsize);
+ else
+ (void) sm_strlcat(host, d, hbsize);
}
else
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
if (tTd(38, 20))
- dprintf("getcanonname(%s), failed, status=%d\n", host, status);
+ sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
+ status);
-#if NAMED_BIND
if (got_tempfail)
SM_SET_H_ERRNO(TRY_AGAIN);
else
SM_SET_H_ERRNO(HOST_NOT_FOUND);
-#endif /* NAMED_BIND */
- return FALSE;
+
+ return false;
}
- /*
+/*
** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
**
** Parameters:
@@ -784,8 +809,8 @@ getcanonname(host, hbsize, trymx)
** cbuflen -- the size of cbuf.
**
** Returns:
-** TRUE -- if the line matched the desired name.
-** FALSE -- otherwise.
+** true -- if the line matched the desired name.
+** false -- otherwise.
*/
static bool
@@ -798,11 +823,11 @@ extract_canonname(name, dot, line, cbuf, cbuflen)
{
int i;
char *p;
- bool found = FALSE;
+ bool found = false;
cbuf[0] = '\0';
if (line[0] == '#')
- return FALSE;
+ return false;
for (i = 1; ; i++)
{
@@ -816,16 +841,16 @@ extract_canonname(name, dot, line, cbuf, cbuflen)
if (cbuf[0] == '\0' ||
(strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
{
- snprintf(cbuf, cbuflen, "%s", p);
+ (void) sm_strlcpy(cbuf, p, cbuflen);
}
- if (strcasecmp(name, p) == 0)
- found = TRUE;
+ if (sm_strcasecmp(name, p) == 0)
+ found = true;
else if (dot != NULL)
{
/* try looking for the FQDN as well */
*dot = '.';
- if (strcasecmp(name, p) == 0)
- found = TRUE;
+ if (sm_strcasecmp(name, p) == 0)
+ found = true;
*dot = '\0';
}
}
@@ -839,16 +864,484 @@ extract_canonname(name, dot, line, cbuf, cbuflen)
{
p = &cbuf[i];
*p++ = '.';
- (void) strlcpy(p, domain, cbuflen - i - 1);
+ (void) sm_strlcpy(p, domain, cbuflen - i - 1);
}
}
return found;
}
- /*
+
+/*
+** DNS modules
+*/
+
+#if NAMED_BIND
+# if DNSMAP
+
+# include "sm_resolve.h"
+# if NETINET || NETINET6
+# include <arpa/inet.h>
+# endif /* NETINET || NETINET6 */
+
+/*
+** DNS_MAP_OPEN -- stub to check proper value for dns map type
+*/
+
+bool
+dns_map_open(map, mode)
+ MAP *map;
+ int mode;
+{
+ if (tTd(38,2))
+ sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
+
+ mode &= O_ACCMODE;
+ if (mode != O_RDONLY)
+ {
+ /* issue a pseudo-error message */
+ errno = SM_EMAPCANTWRITE;
+ return false;
+ }
+ return true;
+}
+
+/*
+** DNS_MAP_PARSEARGS -- parse dns map definition args.
+**
+** Parameters:
+** map -- pointer to MAP
+** args -- pointer to the args on the config line.
+**
+** Returns:
+** true -- if everything parsed OK.
+** false -- otherwise.
+*/
+
+# if _FFR_DNSMAP_MULTILIMIT
+# if !_FFR_DNSMAP_MULTI
+ ERROR README: You must define _FFR_DNSMAP_MULTI to use _FFR_DNSMAP_MULTILIMIT
+# endif /* ! _FFR_DNSMAP_MULTI */
+# endif /* _FFR_DNSMAP_MULTILIMIT */
+
+# if _FFR_DNSMAP_MULTI
+# if _FFR_DNSMAP_MULTILIMIT
+# define map_sizelimit map_lockfd /* overload field */
+# endif /* _FFR_DNSMAP_MULTILIMIT */
+# endif /* _FFR_DNSMAP_MULTI */
+
+struct dns_map
+{
+ int dns_m_type;
+};
+
+bool
+dns_map_parseargs(map,args)
+ MAP *map;
+ char *args;
+{
+ register char *p = args;
+ struct dns_map *map_p;
+
+ map_p = (struct dns_map *) xalloc(sizeof *map_p);
+ map_p->dns_m_type = -1;
+ map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
+
+ for (;;)
+ {
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p != '-')
+ break;
+ switch (*++p)
+ {
+ case 'N':
+ map->map_mflags |= MF_INCLNULL;
+ map->map_mflags &= ~MF_TRY0NULL;
+ break;
+
+ case 'O':
+ map->map_mflags &= ~MF_TRY1NULL;
+ break;
+
+ case 'o':
+ map->map_mflags |= MF_OPTIONAL;
+ break;
+
+ case 'f':
+ map->map_mflags |= MF_NOFOLDCASE;
+ break;
+
+ case 'm':
+ map->map_mflags |= MF_MATCHONLY;
+ break;
+
+ case 'A':
+ map->map_mflags |= MF_APPEND;
+ break;
+
+ case 'q':
+ map->map_mflags |= MF_KEEPQUOTES;
+ break;
+
+ case 't':
+ map->map_mflags |= MF_NODEFER;
+ break;
+
+ case 'a':
+ map->map_app = ++p;
+ break;
+
+ case 'T':
+ map->map_tapp = ++p;
+ break;
+
+ case 'd':
+ {
+ char *h;
+
+ ++p;
+ h = strchr(p, ' ');
+ if (h != NULL)
+ *h = '\0';
+ map->map_timeout = convtime(p, 's');
+ if (h != NULL)
+ *h = ' ';
+ }
+ break;
+
+ case 'r':
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ map->map_retry = atoi(p);
+ break;
+
+# if _FFR_DNSMAP_MULTI
+ case 'z':
+ if (*++p != '\\')
+ map->map_coldelim = *p;
+ else
+ {
+ switch (*++p)
+ {
+ case 'n':
+ map->map_coldelim = '\n';
+ break;
+
+ case 't':
+ map->map_coldelim = '\t';
+ break;
+
+ default:
+ map->map_coldelim = '\\';
+ }
+ }
+ break;
+
+# if _FFR_DNSMAP_MULTILIMIT
+ case 'Z':
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ map->map_sizelimit = atoi(p);
+ break;
+# endif /* _FFR_DNSMAP_MULTILIMIT */
+# endif /* _FFR_DNSMAP_MULTI */
+
+ /* Start of dns_map specific args */
+ case 'R': /* search field */
+ {
+ char *h;
+
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ h = strchr(p, ' ');
+ if (h != NULL)
+ *h = '\0';
+ map_p->dns_m_type = dns_string_to_type(p);
+ if (h != NULL)
+ *h = ' ';
+ if (map_p->dns_m_type < 0)
+ syserr("dns map %s: wrong type %s",
+ map->map_mname, p);
+ }
+ break;
+
+# if _FFR_DNSMAP_BASE
+ case 'B': /* base domain */
+ {
+ char *h;
+
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ h = strchr(p, ' ');
+ if (h != NULL)
+ *h = '\0';
+
+ /*
+ ** slight abuse of map->map_file; it isn't
+ ** used otherwise in this map type.
+ */
+
+ map->map_file = newstr(p);
+ if (h != NULL)
+ *h = ' ';
+ }
+ break;
+# endif /* _FFR_DNSMAP_BASE */
+
+ }
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+ }
+ if (map_p->dns_m_type < 0)
+ syserr("dns map %s: missing -R type", map->map_mname);
+ if (map->map_app != NULL)
+ map->map_app = newstr(map->map_app);
+ if (map->map_tapp != NULL)
+ map->map_tapp = newstr(map->map_tapp);
+
+ /*
+ ** Assumption: assert(sizeof int <= sizeof(ARBPTR_T));
+ ** Even if this assumption is wrong, we use only one byte,
+ ** so it doesn't really matter.
+ */
+
+ map->map_db1 = (ARBPTR_T) map_p;
+ return true;
+}
+
+/*
+** DNS_MAP_LOOKUP -- perform dns map lookup.
+**
+** Parameters:
+** map -- pointer to MAP
+** name -- name to lookup
+** av -- arguments to interpolate into buf.
+** statp -- pointer to status (EX_)
+**
+** Returns:
+** result of lookup if succeeded.
+** NULL -- otherwise.
+*/
+
+char *
+dns_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+# if _FFR_DNSMAP_MULTI
+# if _FFR_DNSMAP_MULTILIMIT
+ int resnum = 0;
+# endif /* _FFR_DNSMAP_MULTILIMIT */
+# endif /* _FFR_DNSMAP_MULTI */
+ char *vp = NULL, *result = NULL;
+ size_t vsize;
+ struct dns_map *map_p;
+ RESOURCE_RECORD_T *rr = NULL;
+ DNS_REPLY_T *r = NULL;
+# if NETINET6
+ static char buf6[INET6_ADDRSTRLEN];
+# endif /* NETINET6 */
+
+ if (tTd(38, 20))
+ sm_dprintf("dns_map_lookup(%s, %s)\n",
+ map->map_mname, name);
+
+ map_p = (struct dns_map *)(map->map_db1);
+# if _FFR_DNSMAP_BASE
+ if (map->map_file != NULL && *map->map_file != '\0')
+ {
+ size_t len;
+ char *appdomain;
+
+ len = strlen(map->map_file) + strlen(name) + 2;
+ appdomain = (char *) sm_malloc(len);
+ if (appdomain == NULL)
+ {
+ *statp = EX_UNAVAILABLE;
+ return NULL;
+ }
+ (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
+ r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
+ map->map_timeout, map->map_retry);
+ sm_free(appdomain);
+ }
+ else
+# endif /* _FFR_DNSMAP_BASE */
+ {
+ r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
+ map->map_timeout, map->map_retry);
+ }
+
+ if (r == NULL)
+ {
+ result = NULL;
+ if (errno == ETIMEDOUT || h_errno == TRY_AGAIN ||
+ errno == ECONNREFUSED)
+ *statp = EX_TEMPFAIL;
+ else
+ *statp = EX_NOTFOUND;
+ goto cleanup;
+ }
+ *statp = EX_OK;
+ for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
+ {
+ char *type = NULL;
+ char *value = NULL;
+
+ switch (rr->rr_type)
+ {
+ case T_NS:
+ type = "T_NS";
+ value = rr->rr_u.rr_txt;
+ break;
+ case T_CNAME:
+ type = "T_CNAME";
+ value = rr->rr_u.rr_txt;
+ break;
+ case T_AFSDB:
+ type = "T_AFSDB";
+ value = rr->rr_u.rr_mx->mx_r_domain;
+ break;
+ case T_SRV:
+ type = "T_SRV";
+ value = rr->rr_u.rr_srv->srv_r_target;
+ break;
+ case T_PTR:
+ type = "T_PTR";
+ value = rr->rr_u.rr_txt;
+ break;
+ case T_TXT:
+ type = "T_TXT";
+ value = rr->rr_u.rr_txt;
+ break;
+ case T_MX:
+ type = "T_MX";
+ value = rr->rr_u.rr_mx->mx_r_domain;
+ break;
+# if NETINET
+ case T_A:
+ type = "T_A";
+ value = inet_ntoa(*(rr->rr_u.rr_a));
+ break;
+# endif /* NETINET */
+# if NETINET6
+ case T_AAAA:
+ type = "T_AAAA";
+ value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
+ sizeof buf6);
+ break;
+# endif /* NETINET6 */
+ }
+
+ if (map_p->dns_m_type != rr->rr_type)
+ {
+ if (tTd(38, 40))
+ sm_dprintf("\tskipping type %s (%d) value %s\n",
+ type != NULL ? type : "<UNKNOWN>",
+ rr->rr_type,
+ value != NULL ? value : "<NO VALUE>");
+ continue;
+ }
+
+# if NETINET6
+ if (rr->rr_type == T_AAAA && value == NULL)
+ {
+ result = NULL;
+ *statp = EX_DATAERR;
+ if (tTd(38, 40))
+ sm_dprintf("\tbad T_AAAA conversion\n");
+ goto cleanup;
+ }
+# endif /* NETINET6 */
+ if (tTd(38, 40))
+ sm_dprintf("\tfound type %s (%d) value %s\n",
+ type != NULL ? type : "<UNKNOWN>",
+ rr->rr_type,
+ value != NULL ? value : "<NO VALUE>");
+# if _FFR_DNSMAP_MULTI
+ if (value != NULL &&
+ (map->map_coldelim == '\0' ||
+# if _FFR_DNSMAP_MULTILIMIT
+ map->map_sizelimit == 1 ||
+# endif /* _FFR_DNSMAP_MULTILIMIT */
+ bitset(MF_MATCHONLY, map->map_mflags)))
+ {
+ /* Only care about the first match */
+ vp = newstr(value);
+ break;
+ }
+ else if (vp == NULL)
+ {
+ /* First result */
+ vp = newstr(value);
+ }
+ else
+ {
+ /* concatenate the results */
+ int sz;
+ char *new;
+
+ sz = strlen(vp) + strlen(value) + 2;
+ new = xalloc(sz);
+ (void) sm_snprintf(new, sz, "%s%c%s",
+ vp, map->map_coldelim, value);
+ sm_free(vp);
+ vp = new;
+# if _FFR_DNSMAP_MULTILIMIT
+ if (map->map_sizelimit > 0 &&
+ ++resnum >= map->map_sizelimit)
+ break;
+# endif /* _FFR_DNSMAP_MULTILIMIT */
+ }
+# else /* _FFR_DNSMAP_MULTI */
+ vp = value;
+ break;
+# endif /* _FFR_DNSMAP_MULTI */
+ }
+ if (vp == NULL)
+ {
+ result = NULL;
+ *statp = EX_NOTFOUND;
+ if (tTd(38, 40))
+ sm_dprintf("\tno match found\n");
+ goto cleanup;
+ }
+
+# if _FFR_DNSMAP_MULTI
+ /* Cleanly truncate for rulesets */
+ truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
+# endif /* _FFR_DNSMAP_MULTI */
+
+ vsize = strlen(vp);
+
+ if (LogLevel > 9)
+ sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
+ name, vp);
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ result = map_rewrite(map, name, strlen(name), NULL);
+ else
+ result = map_rewrite(map, vp, vsize, av);
+
+ cleanup:
+# if _FFR_DNSMAP_MULTI
+ if (vp != NULL)
+ sm_free(vp);
+# endif /* _FFR_DNSMAP_MULTI */
+ if (r != NULL)
+ dns_free_data(r);
+ return result;
+}
+# endif /* DNSMAP */
+#endif /* NAMED_BIND */
+
+/*
** NDBM modules
*/
-#ifdef NDBM
+#if NDBM
/*
** NDBM_MAP_OPEN -- DBM-style map open
@@ -872,14 +1365,14 @@ ndbm_map_open(map, mode)
struct stat std, stp;
if (tTd(38, 2))
- dprintf("ndbm_map_open(%s, %s, %d)\n",
+ sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
map->map_lockfd = -1;
mode &= O_ACCMODE;
/* do initial file and directory checks */
- snprintf(dirfile, sizeof dirfile, "%s.dir", map->map_file);
- snprintf(pagfile, sizeof pagfile, "%s.pag", map->map_file);
+ (void) sm_strlcpyn(dirfile, sizeof dirfile, 2, map->map_file, ".dir");
+ (void) sm_strlcpyn(pagfile, sizeof pagfile, 2, map->map_file, ".pag");
sff = SFF_ROOTOK|SFF_REGONLY;
if (mode == O_RDWR)
{
@@ -898,31 +1391,11 @@ ndbm_map_open(map, mode)
if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
sff |= SFF_SAFEDIRPATH;
ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
- sff, smode, &std);
+ sff, smode, &std);
if (ret == 0)
ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
sff, smode, &stp);
-# if !_FFR_REMOVE_AUTOREBUILD
- if (ret == ENOENT && AutoRebuild &&
- bitset(MCF_REBUILDABLE, map->map_class->map_cflags) &&
- (bitset(MF_IMPL_NDBM, map->map_mflags) ||
- bitset(MF_ALIAS, map->map_mflags)) &&
- mode == O_RDONLY)
- {
- bool impl = bitset(MF_IMPL_NDBM, map->map_mflags);
-
- /* may be able to rebuild */
- map->map_mflags &= ~MF_IMPL_NDBM;
- if (!rebuildaliases(map, TRUE))
- return FALSE;
- if (impl)
- return impl_map_open(map, O_RDONLY);
- else
- return ndbm_map_open(map, O_RDONLY);
- }
-# endif /* !_FFR_REMOVE_AUTOREBUILD */
-
if (ret != 0)
{
char *prob = "unsafe";
@@ -931,11 +1404,11 @@ ndbm_map_open(map, mode)
if (ret == ENOENT)
prob = "missing";
if (tTd(38, 2))
- dprintf("\t%s map file: %d\n", prob, ret);
+ sm_dprintf("\t%s map file: %d\n", prob, ret);
if (!bitset(MF_OPTIONAL, map->map_mflags))
syserr("dbm map \"%s\": %s map file %s",
map->map_mname, prob, map->map_file);
- return FALSE;
+ return false;
}
if (std.st_mode == ST_MODE_NOFILE)
mode |= O_CREAT|O_EXCL;
@@ -986,7 +1459,7 @@ ndbm_map_open(map, mode)
errno = save_errno;
syserr("ndbm_map_open: cannot create database %s",
map->map_file);
- return FALSE;
+ return false;
}
if (ftruncate(dirfd, (off_t) 0) < 0 ||
ftruncate(pagfd, (off_t) 0) < 0)
@@ -997,7 +1470,7 @@ ndbm_map_open(map, mode)
errno = save_errno;
syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
map->map_file);
- return FALSE;
+ return false;
}
/* if new file, get "before" bits for later filechanged check */
@@ -1010,7 +1483,7 @@ ndbm_map_open(map, mode)
errno = save_errno;
syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
map->map_file);
- return FALSE;
+ return false;
}
/* have to save the lock for the duration (bletch) */
@@ -1029,8 +1502,8 @@ ndbm_map_open(map, mode)
{
save_errno = errno;
if (bitset(MF_ALIAS, map->map_mflags) &&
- aliaswait(map, ".pag", FALSE))
- return TRUE;
+ aliaswait(map, ".pag", false))
+ return true;
# if !LOCK_ON_OPEN && !NOFTRUNCATE
if (map->map_lockfd >= 0)
(void) close(map->map_lockfd);
@@ -1038,7 +1511,7 @@ ndbm_map_open(map, mode)
errno = save_errno;
if (!bitset(MF_OPTIONAL, map->map_mflags))
syserr("Cannot open DBM database %s", map->map_file);
- return FALSE;
+ return false;
}
dfd = dbm_dirfno(dbm);
pfd = dbm_pagfno(dbm);
@@ -1053,7 +1526,7 @@ ndbm_map_open(map, mode)
errno = 0;
syserr("dbm map \"%s\": cannot support GDBM",
map->map_mname);
- return FALSE;
+ return false;
}
if (filechanged(dirfile, dfd, &std) ||
@@ -1068,7 +1541,7 @@ ndbm_map_open(map, mode)
errno = save_errno;
syserr("ndbm_map_open(%s): file changed after open",
map->map_file);
- return FALSE;
+ return false;
}
map->map_db1 = (ARBPTR_T) dbm;
@@ -1091,8 +1564,8 @@ ndbm_map_open(map, mode)
(void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
# endif /* LOCK_ON_OPEN */
if (bitset(MF_ALIAS, map->map_mflags) &&
- !aliaswait(map, ".pag", TRUE))
- return FALSE;
+ !aliaswait(map, ".pag", true))
+ return false;
}
else
{
@@ -1107,14 +1580,20 @@ ndbm_map_open(map, mode)
sm_syslog(LOG_ALERT, NOQID,
"ownership change on %s failed: %s",
- map->map_file, errstring(err));
+ map->map_file, sm_errstring(err));
message("050 ownership change on %s failed: %s",
- map->map_file, errstring(err));
+ map->map_file, sm_errstring(err));
}
+# else /* HASFCHOWN */
+ sm_syslog(LOG_ALERT, NOQID,
+ "no fchown(): cannot change ownership on %s",
+ map->map_file);
+ message("050 no fchown(): cannot change ownership on %s",
+ map->map_file);
# endif /* HASFCHOWN */
}
}
- return TRUE;
+ return true;
}
@@ -1135,7 +1614,7 @@ ndbm_map_lookup(map, name, av, statp)
struct stat stbuf;
if (tTd(38, 20))
- dprintf("ndbm_map_lookup(%s, %s)\n",
+ sm_dprintf("ndbm_map_lookup(%s, %s)\n",
map->map_mname, name);
key.dptr = name;
@@ -1169,7 +1648,7 @@ lockdbm:
if (map->map_class->map_open(map, omode))
{
map->map_mflags |= MF_OPEN;
- map->map_pid = getpid();
+ map->map_pid = CurrentPid;
if ((omode && O_ACCMODE) == O_RDWR)
map->map_mflags |= MF_WRITABLE;
goto lockdbm;
@@ -1181,9 +1660,10 @@ lockdbm:
extern MAPCLASS BogusMapClass;
*statp = EX_TEMPFAIL;
+ map->map_orgclass = map->map_class;
map->map_class = &BogusMapClass;
map->map_mflags |= MF_OPEN;
- map->map_pid = getpid();
+ map->map_pid = CurrentPid;
syserr("Cannot reopen NDBM database %s",
map->map_file);
}
@@ -1231,7 +1711,7 @@ ndbm_map_store(map, lhs, rhs)
char keybuf[MAXNAME + 1];
if (tTd(38, 12))
- dprintf("ndbm_map_store(%s, %s, %s)\n",
+ sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
map->map_mname, lhs, rhs);
key.dsize = strlen(lhs);
@@ -1268,23 +1748,23 @@ ndbm_map_store(map, lhs, rhs)
datum old;
old.dptr = ndbm_map_lookup(map, key.dptr,
- (char **)NULL, &xstat);
+ (char **) NULL, &xstat);
if (old.dptr != NULL && *(char *) old.dptr != '\0')
{
old.dsize = strlen(old.dptr);
if (data.dsize + old.dsize + 2 > bufsiz)
{
if (buf != NULL)
- sm_free(buf);
+ (void) sm_free(buf);
bufsiz = data.dsize + old.dsize + 2;
- buf = xalloc(bufsiz);
+ buf = sm_pmalloc_x(bufsiz);
}
- snprintf(buf, bufsiz, "%s,%s",
- data.dptr, old.dptr);
+ (void) sm_strlcpyn(buf, bufsiz, 3,
+ data.dptr, ",", old.dptr);
data.dsize = data.dsize + old.dsize + 1;
data.dptr = buf;
if (tTd(38, 9))
- dprintf("ndbm_map_store append=%s\n",
+ sm_dprintf("ndbm_map_store append=%s\n",
data.dptr);
}
}
@@ -1305,7 +1785,7 @@ ndbm_map_close(map)
register MAP *map;
{
if (tTd(38, 9))
- dprintf("ndbm_map_close(%s, %s, %lx)\n",
+ sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
map->map_mname, map->map_file, map->map_mflags);
if (bitset(MF_WRITABLE, map->map_mflags))
@@ -1323,7 +1803,7 @@ ndbm_map_close(map)
map->map_mflags |= MF_NOFOLDCASE;
- (void) snprintf(buf, sizeof buf, "%010ld", curtime());
+ (void) sm_snprintf(buf, sizeof buf, "%010ld", curtime());
ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
(void) gethostname(buf, sizeof buf);
@@ -1349,11 +1829,11 @@ ndbm_map_close(map)
}
#endif /* NDBM */
- /*
+/*
** NEWDB (Hash and BTree) Modules
*/
-#ifdef NEWDB
+#if NEWDB
/*
** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
@@ -1395,7 +1875,7 @@ bt_map_open(map, mode)
# endif /* DB_VERSION_MAJOR > 2 */
if (tTd(38, 2))
- dprintf("bt_map_open(%s, %s, %d)\n",
+ sm_dprintf("bt_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
# if DB_VERSION_MAJOR < 3
@@ -1424,7 +1904,7 @@ hash_map_open(map, mode)
# endif /* DB_VERSION_MAJOR > 2 */
if (tTd(38, 2))
- dprintf("hash_map_open(%s, %s, %d)\n",
+ sm_dprintf("hash_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
# if DB_VERSION_MAJOR < 3
@@ -1467,10 +1947,10 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
char buf[MAXNAME + 1];
/* do initial file and directory checks */
- (void) strlcpy(buf, map->map_file, sizeof buf - 3);
+ (void) sm_strlcpy(buf, map->map_file, sizeof buf - 3);
i = strlen(buf);
if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
- (void) strlcat(buf, ".db", sizeof buf);
+ (void) sm_strlcat(buf, ".db", sizeof buf);
mode &= O_ACCMODE;
omode = mode;
@@ -1494,27 +1974,6 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
sff |= SFF_SAFEDIRPATH;
i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
-# if !_FFR_REMOVE_AUTOREBUILD
- if (i == ENOENT && AutoRebuild &&
- bitset(MCF_REBUILDABLE, map->map_class->map_cflags) &&
- (bitset(MF_IMPL_HASH, map->map_mflags) ||
- bitset(MF_ALIAS, map->map_mflags)) &&
- mode == O_RDONLY)
- {
- bool impl = bitset(MF_IMPL_HASH, map->map_mflags);
-
- /* may be able to rebuild */
- map->map_mflags &= ~MF_IMPL_HASH;
- if (!rebuildaliases(map, TRUE))
- return FALSE;
- if (impl)
- return impl_map_open(map, O_RDONLY);
- else
- return db_map_open(map, O_RDONLY, mapclassname,
- dbtype, openinfo);
- }
-# endif /* !_FFR_REMOVE_AUTOREBUILD */
-
if (i != 0)
{
char *prob = "unsafe";
@@ -1523,12 +1982,12 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
if (i == ENOENT)
prob = "missing";
if (tTd(38, 2))
- dprintf("\t%s map file: %s\n", prob, errstring(i));
+ sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
errno = i;
if (!bitset(MF_OPTIONAL, map->map_mflags))
syserr("%s map \"%s\": %s map file %s",
mapclassname, map->map_mname, prob, buf);
- return FALSE;
+ return false;
}
if (st.st_mode == ST_MODE_NOFILE)
omode |= O_CREAT|O_EXCL;
@@ -1552,7 +2011,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
{
if (!bitset(MF_OPTIONAL, map->map_mflags))
syserr("db_map_open: cannot pre-open database %s", buf);
- return FALSE;
+ return false;
}
/* make sure no baddies slipped in just before the open... */
@@ -1562,7 +2021,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
(void) close(fd);
errno = save_errno;
syserr("db_map_open(%s): file changed after pre-open", buf);
- return FALSE;
+ return false;
}
/* if new file, get the "before" bits for later filechanged check */
@@ -1573,7 +2032,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
errno = save_errno;
syserr("db_map_open(%s): cannot fstat pre-opened file",
buf);
- return FALSE;
+ return false;
}
/* actually lock the pre-opened file */
@@ -1662,8 +2121,8 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
if (db == NULL)
{
if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
- aliaswait(map, ".db", FALSE))
- return TRUE;
+ aliaswait(map, ".db", false))
+ return true;
# if !LOCK_ON_OPEN
if (map->map_lockfd >= 0)
(void) close(map->map_lockfd);
@@ -1672,7 +2131,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
if (!bitset(MF_OPTIONAL, map->map_mflags))
syserr("Cannot open %s database %s",
mapclassname, buf);
- return FALSE;
+ return false;
}
# if DB_VERSION_MAJOR < 2
@@ -1695,7 +2154,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
# endif /* !LOCK_ON_OPEN */
errno = save_errno;
syserr("db_map_open(%s): file changed after open", buf);
- return FALSE;
+ return false;
}
if (mode == O_RDWR)
@@ -1720,10 +2179,16 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
sm_syslog(LOG_ALERT, NOQID,
"ownership change on %s failed: %s",
- buf, errstring(err));
+ buf, sm_errstring(err));
message("050 ownership change on %s failed: %s",
- buf, errstring(err));
+ buf, sm_errstring(err));
}
+# else /* HASFCHOWN */
+ sm_syslog(LOG_ALERT, NOQID,
+ "no fchown(): cannot change ownership on %s",
+ map->map_file);
+ message("050 no fchown(): cannot change ownership on %s",
+ map->map_file);
# endif /* HASFCHOWN */
}
}
@@ -1740,9 +2205,9 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
map->map_mtime = st.st_mtime;
if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
- !aliaswait(map, ".db", TRUE))
- return FALSE;
- return TRUE;
+ !aliaswait(map, ".db", true))
+ return false;
+ return true;
}
@@ -1771,13 +2236,13 @@ db_map_lookup(map, name, av, statp)
memset(&val, '\0', sizeof val);
if (tTd(38, 20))
- dprintf("db_map_lookup(%s, %s)\n",
+ sm_dprintf("db_map_lookup(%s, %s)\n",
map->map_mname, name);
i = strlen(map->map_file);
if (i > MAXNAME)
i = MAXNAME;
- (void) strlcpy(buf, map->map_file, i + 1);
+ (void) sm_strlcpy(buf, map->map_file, i + 1);
if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
buf[i - 3] = '\0';
@@ -1812,7 +2277,7 @@ db_map_lookup(map, name, av, statp)
if (map->map_class->map_open(map, omode))
{
map->map_mflags |= MF_OPEN;
- map->map_pid = getpid();
+ map->map_pid = CurrentPid;
if ((omode && O_ACCMODE) == O_RDWR)
map->map_mflags |= MF_WRITABLE;
db = (DB *) map->map_db2;
@@ -1825,9 +2290,10 @@ db_map_lookup(map, name, av, statp)
extern MAPCLASS BogusMapClass;
*statp = EX_TEMPFAIL;
+ map->map_orgclass = map->map_class;
map->map_class = &BogusMapClass;
map->map_mflags |= MF_OPEN;
- map->map_pid = getpid();
+ map->map_pid = CurrentPid;
syserr("Cannot reopen DB database %s",
map->map_file);
}
@@ -1924,7 +2390,7 @@ db_map_store(map, lhs, rhs)
memset(&data, '\0', sizeof data);
if (tTd(38, 12))
- dprintf("db_map_store(%s, %s, %s)\n",
+ sm_dprintf("db_map_store(%s, %s, %s)\n",
map->map_mname, lhs, rhs);
key.size = strlen(lhs);
@@ -1980,23 +2446,24 @@ db_map_store(map, lhs, rhs)
memset(&old, '\0', sizeof old);
old.data = db_map_lookup(map, key.data,
- (char **)NULL, &status);
+ (char **) NULL, &status);
if (old.data != NULL)
{
old.size = strlen(old.data);
- if (data.size + old.size + 2 > (size_t)bufsiz)
+ if (data.size + old.size + 2 > (size_t) bufsiz)
{
if (buf != NULL)
sm_free(buf);
bufsiz = data.size + old.size + 2;
- buf = xalloc(bufsiz);
+ buf = sm_pmalloc_x(bufsiz);
}
- snprintf(buf, bufsiz, "%s,%s",
- (char *) data.data, (char *) old.data);
+ (void) sm_strlcpyn(buf, bufsiz, 3,
+ (char *) data.data, ",",
+ (char *) old.data);
data.size = data.size + old.size + 1;
data.data = buf;
if (tTd(38, 9))
- dprintf("db_map_store append=%s\n",
+ sm_dprintf("db_map_store append=%s\n",
(char *) data.data);
}
}
@@ -2022,7 +2489,7 @@ db_map_close(map)
register DB *db = map->map_db2;
if (tTd(38, 9))
- dprintf("db_map_close(%s, %s, %lx)\n",
+ sm_dprintf("db_map_close(%s, %s, %lx)\n",
map->map_mname, map->map_file, map->map_mflags);
if (bitset(MF_WRITABLE, map->map_mflags))
@@ -2054,7 +2521,8 @@ db_map_close(map)
** process, do not close the map but recover
** the file descriptor.
*/
- if (map->map_pid != getpid())
+
+ if (map->map_pid != CurrentPid)
{
int fd = -1;
@@ -2070,11 +2538,11 @@ db_map_close(map)
map->map_mname, map->map_file, map->map_mflags);
}
#endif /* NEWDB */
- /*
+/*
** NIS Modules
*/
-#ifdef NIS
+#if NIS
# ifndef YPERR_BUSY
# define YPERR_BUSY 16
@@ -2095,23 +2563,15 @@ nis_map_open(map, mode)
auto int vsize;
if (tTd(38, 2))
- dprintf("nis_map_open(%s, %s, %d)\n",
+ sm_dprintf("nis_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
mode &= O_ACCMODE;
if (mode != O_RDONLY)
{
/* issue a pseudo-error message */
-# ifdef ENOSYS
- errno = ENOSYS;
-# else /* ENOSYS */
-# ifdef EFTYPE
- errno = EFTYPE;
-# else /* EFTYPE */
- errno = ENXIO;
-# endif /* EFTYPE */
-# endif /* ENOSYS */
- return FALSE;
+ errno = SM_EMAPCANTWRITE;
+ return false;
}
p = strchr(map->map_file, '@');
@@ -2133,7 +2593,7 @@ nis_map_open(map, mode)
if (!bitset(MF_OPTIONAL, map->map_mflags))
syserr("421 4.3.5 NIS map %s specified, but NIS not running",
map->map_file);
- return FALSE;
+ return false;
}
}
@@ -2142,7 +2602,7 @@ nis_map_open(map, mode)
yperr = yp_match(map->map_domain, map->map_file, "@", 1,
&vp, &vsize);
if (tTd(38, 10))
- dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
+ sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
map->map_domain, map->map_file, yperr_string(yperr));
if (vp != NULL)
sm_free(vp);
@@ -2158,9 +2618,9 @@ nis_map_open(map, mode)
# if 0
if (!bitset(MF_ALIAS, map->map_mflags) ||
- aliaswait(map, NULL, TRUE))
+ aliaswait(map, NULL, true))
# endif /* 0 */
- return TRUE;
+ return true;
}
if (!bitset(MF_OPTIONAL, map->map_mflags))
@@ -2169,7 +2629,7 @@ nis_map_open(map, mode)
map->map_file, map->map_domain, yperr_string(yperr));
}
- return FALSE;
+ return false;
}
@@ -2190,9 +2650,10 @@ nis_map_lookup(map, name, av, statp)
int buflen;
int yperr;
char keybuf[MAXNAME + 1];
+ char *SM_NONVOLATILE result = NULL;
if (tTd(38, 20))
- dprintf("nis_map_lookup(%s, %s)\n",
+ sm_dprintf("nis_map_lookup(%s, %s)\n",
map->map_mname, name);
buflen = strlen(name);
@@ -2213,11 +2674,7 @@ nis_map_lookup(map, name, av, statp)
}
if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
{
- if (vp != NULL)
- {
- sm_free(vp);
- vp = NULL;
- }
+ SM_FREE_CLR(vp);
buflen++;
yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
&vp, &vsize);
@@ -2232,17 +2689,16 @@ nis_map_lookup(map, name, av, statp)
sm_free(vp);
return NULL;
}
- if (bitset(MF_MATCHONLY, map->map_mflags))
- return map_rewrite(map, name, strlen(name), NULL);
- else
- {
- char *ret;
-
- ret = map_rewrite(map, vp, vsize, av);
+ SM_TRY
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ result = map_rewrite(map, name, strlen(name), NULL);
+ else
+ result = map_rewrite(map, vp, vsize, av);
+ SM_FINALLY
if (vp != NULL)
sm_free(vp);
- return ret;
- }
+ SM_END_TRY
+ return result;
}
@@ -2260,20 +2716,20 @@ nis_getcanonname(name, hbsize, statp)
auto int vsize;
int keylen;
int yperr;
- static bool try0null = TRUE;
- static bool try1null = TRUE;
+ static bool try0null = true;
+ static bool try1null = true;
static char *yp_domain = NULL;
char host_record[MAXLINE];
char cbuf[MAXNAME];
char nbuf[MAXNAME + 1];
if (tTd(38, 20))
- dprintf("nis_getcanonname(%s)\n", name);
+ sm_dprintf("nis_getcanonname(%s)\n", name);
- if (strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
+ if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
{
*statp = EX_UNAVAILABLE;
- return FALSE;
+ return false;
}
(void) shorten_hostname(nbuf);
keylen = strlen(nbuf);
@@ -2288,20 +2744,16 @@ nis_getcanonname(name, hbsize, statp)
yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
&vp, &vsize);
if (yperr == 0)
- try1null = FALSE;
+ try1null = false;
}
if (yperr == YPERR_KEY && try1null)
{
- if (vp != NULL)
- {
- sm_free(vp);
- vp = NULL;
- }
+ SM_FREE_CLR(vp);
keylen++;
yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
&vp, &vsize);
if (yperr == 0)
- try0null = FALSE;
+ try0null = false;
}
if (yperr != 0)
{
@@ -2313,36 +2765,38 @@ nis_getcanonname(name, hbsize, statp)
*statp = EX_UNAVAILABLE;
if (vp != NULL)
sm_free(vp);
- return FALSE;
+ return false;
}
- (void) strlcpy(host_record, vp, sizeof host_record);
+ (void) sm_strlcpy(host_record, vp, sizeof host_record);
sm_free(vp);
if (tTd(38, 44))
- dprintf("got record `%s'\n", host_record);
+ sm_dprintf("got record `%s'\n", host_record);
+ vp = strpbrk(host_record, "#\n");
+ if (vp != NULL)
+ *vp = '\0';
if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof cbuf))
{
/* this should not happen, but.... */
*statp = EX_NOHOST;
- return FALSE;
+ return false;
}
- if (hbsize <= strlen(cbuf))
+ if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
{
*statp = EX_UNAVAILABLE;
- return FALSE;
+ return false;
}
- (void) strlcpy(name, cbuf, hbsize);
*statp = EX_OK;
- return TRUE;
+ return true;
}
#endif /* NIS */
- /*
+/*
** NISPLUS Modules
**
** This code donated by Sun Microsystems.
*/
-#ifdef NISPLUS
+#if NISPLUS
# undef NIS /* symbol conflict in nis.h */
# undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
@@ -2368,14 +2822,14 @@ nisplus_map_open(map, mode)
char qbuf[MAXLINE + NIS_MAXNAMELEN];
if (tTd(38, 2))
- dprintf("nisplus_map_open(%s, %s, %d)\n",
+ sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
mode &= O_ACCMODE;
if (mode != O_RDONLY)
{
errno = EPERM;
- return FALSE;
+ return false;
}
if (*map->map_file == '\0')
@@ -2386,19 +2840,19 @@ nisplus_map_open(map, mode)
/* set default NISPLUS Domain to $m */
map->map_domain = newstr(nisplus_default_domain());
if (tTd(38, 2))
- dprintf("nisplus_map_open(%s): using domain %s\n",
+ sm_dprintf("nisplus_map_open(%s): using domain %s\n",
map->map_file, map->map_domain);
}
if (!PARTIAL_NAME(map->map_file))
{
map->map_domain = newstr("");
- snprintf(qbuf, sizeof qbuf, "%s", map->map_file);
+ (void) sm_strlcpy(qbuf, map->map_file, sizeof qbuf);
}
else
{
/* check to see if this map actually exists */
- snprintf(qbuf, sizeof qbuf, "%s.%s",
- map->map_file, map->map_domain);
+ (void) sm_strlcpyn(qbuf, sizeof qbuf, 3,
+ map->map_file, ".", map->map_domain);
}
retry_cnt = 0;
@@ -2416,7 +2870,7 @@ nisplus_map_open(map, mode)
if (retry_cnt++ > 4)
{
errno = EAGAIN;
- return FALSE;
+ return false;
}
/* try not to overwhelm hosed server */
sleep(2);
@@ -2430,7 +2884,7 @@ nisplus_map_open(map, mode)
nis_sperrno(res->status));
# endif /* 0 */
errno = EAGAIN;
- return FALSE;
+ return false;
}
}
@@ -2438,7 +2892,7 @@ nisplus_map_open(map, mode)
(NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
{
if (tTd(38, 10))
- dprintf("nisplus_map_open: %s is not a table\n", qbuf);
+ sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
# if 0
if (!bitset(MF_OPTIONAL, map->map_mflags))
syserr("421 4.0.0 %s.%s: %s is not a table",
@@ -2446,7 +2900,7 @@ nisplus_map_open(map, mode)
nis_sperrno(res->status));
# endif /* 0 */
errno = EBADF;
- return FALSE;
+ return false;
}
/* default key column is column 0 */
if (map->map_keycolnm == NULL)
@@ -2455,7 +2909,7 @@ nisplus_map_open(map, mode)
max_col = COL_MAX(res);
/* verify the key column exist */
- for (i = 0; i< max_col; i++)
+ for (i = 0; i < max_col; i++)
{
if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
break;
@@ -2463,17 +2917,17 @@ nisplus_map_open(map, mode)
if (i == max_col)
{
if (tTd(38, 2))
- dprintf("nisplus_map_open(%s): can not find key column %s\n",
+ sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
map->map_file, map->map_keycolnm);
errno = ENOENT;
- return FALSE;
+ return false;
}
/* default value column is the last column */
if (map->map_valcolnm == NULL)
{
map->map_valcolno = max_col - 1;
- return TRUE;
+ return true;
}
for (i = 0; i< max_col; i++)
@@ -2481,15 +2935,15 @@ nisplus_map_open(map, mode)
if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
{
map->map_valcolno = i;
- return TRUE;
+ return true;
}
}
if (tTd(38, 2))
- dprintf("nisplus_map_open(%s): can not find column %s\n",
+ sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
map->map_file, map->map_keycolnm);
errno = ENOENT;
- return FALSE;
+ return false;
}
@@ -2513,7 +2967,7 @@ nisplus_map_lookup(map, name, av, statp)
nis_result *result;
if (tTd(38, 20))
- dprintf("nisplus_map_lookup(%s, %s)\n",
+ sm_dprintf("nisplus_map_lookup(%s, %s)\n",
map->map_mname, name);
if (!bitset(MF_OPEN, map->map_mflags))
@@ -2521,7 +2975,7 @@ nisplus_map_lookup(map, name, av, statp)
if (nisplus_map_open(map, O_RDONLY))
{
map->map_mflags |= MF_OPEN;
- map->map_pid = getpid();
+ map->map_pid = CurrentPid;
}
else
{
@@ -2569,15 +3023,15 @@ nisplus_map_lookup(map, name, av, statp)
/* construct the query */
if (PARTIAL_NAME(map->map_file))
- snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s",
+ (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s",
map->map_keycolnm, search_key, map->map_file,
map->map_domain);
else
- snprintf(qbuf, sizeof qbuf, "[%s=%s],%s",
+ (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s",
map->map_keycolnm, search_key, map->map_file);
if (tTd(38, 20))
- dprintf("qbuf=%s\n", qbuf);
+ sm_dprintf("qbuf=%s\n", qbuf);
result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
if (result->status == NIS_SUCCESS)
{
@@ -2593,7 +3047,7 @@ nisplus_map_lookup(map, name, av, statp)
/* ignore second entry */
if (tTd(38, 20))
- dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
+ sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
name, count);
}
@@ -2603,7 +3057,7 @@ nisplus_map_lookup(map, name, av, statp)
p = "";
vsize = strlen(p);
if (tTd(38, 20))
- dprintf("nisplus_map_lookup(%s), found %s\n",
+ sm_dprintf("nisplus_map_lookup(%s), found %s\n",
name, p);
if (bitset(MF_MATCHONLY, map->map_mflags))
str = map_rewrite(map, name, strlen(name), NULL);
@@ -2626,7 +3080,7 @@ nisplus_map_lookup(map, name, av, statp)
}
}
if (tTd(38, 20))
- dprintf("nisplus_map_lookup(%s), failed\n", name);
+ sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
nis_freeresult(result);
return NULL;
}
@@ -2650,39 +3104,39 @@ nisplus_getcanonname(name, hbsize, statp)
char nbuf[MAXNAME + 1];
char qbuf[MAXLINE + NIS_MAXNAMELEN];
- if (strlen(name) >= sizeof nbuf)
+ if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
{
*statp = EX_UNAVAILABLE;
- return FALSE;
+ return false;
}
- (void) strlcpy(nbuf, name, sizeof nbuf);
(void) shorten_hostname(nbuf);
p = strchr(nbuf, '.');
if (p == NULL)
{
/* single token */
- snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir", nbuf);
+ (void) sm_snprintf(qbuf, sizeof qbuf,
+ "[name=%s],hosts.org_dir", nbuf);
}
else if (p[1] != '\0')
{
/* multi token -- take only first token in nbuf */
*p = '\0';
- snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir.%s",
- nbuf, &p[1]);
+ (void) sm_snprintf(qbuf, sizeof qbuf,
+ "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
}
else
{
*statp = EX_NOHOST;
- return FALSE;
+ return false;
}
if (tTd(38, 20))
- dprintf("\nnisplus_getcanoname(%s), qbuf=%s\n",
- name, qbuf);
+ sm_dprintf("\nnisplus_getcanoname(%s), qbuf=%s\n",
+ name, qbuf);
result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
- NULL, NULL);
+ NULL, NULL);
if (result->status == NIS_SUCCESS)
{
@@ -2698,20 +3152,20 @@ nisplus_getcanonname(name, hbsize, statp)
/* ignore second entry */
if (tTd(38, 20))
- dprintf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n",
- name, count);
+ sm_dprintf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n",
+ name, count);
}
if (tTd(38, 20))
- dprintf("nisplus_getcanoname(%s), found in directory \"%s\"\n",
- name, (NIS_RES_OBJECT(result))->zo_domain);
+ sm_dprintf("nisplus_getcanoname(%s), found in directory \"%s\"\n",
+ name, (NIS_RES_OBJECT(result))->zo_domain);
vp = ((NIS_RES_OBJECT(result))->EN_col(0));
vsize = strlen(vp);
if (tTd(38, 20))
- dprintf("nisplus_getcanonname(%s), found %s\n",
- name, vp);
+ sm_dprintf("nisplus_getcanonname(%s), found %s\n",
+ name, vp);
if (strchr(vp, '.') != NULL)
{
domain = "";
@@ -2725,15 +3179,16 @@ nisplus_getcanonname(name, hbsize, statp)
if (hbsize > vsize + (int) strlen(domain) + 1)
{
if (domain[0] == '\0')
- (void) strlcpy(name, vp, hbsize);
+ (void) sm_strlcpy(name, vp, hbsize);
else
- snprintf(name, hbsize, "%s.%s", vp, domain);
+ (void) sm_snprintf(name, hbsize,
+ "%s.%s", vp, domain);
*statp = EX_OK;
}
else
*statp = EX_NOHOST;
nis_freeresult(result);
- return TRUE;
+ return true;
}
else
{
@@ -2745,10 +3200,10 @@ nisplus_getcanonname(name, hbsize, statp)
*statp = EX_UNAVAILABLE;
}
if (tTd(38, 20))
- dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
- name, result->status, *statp);
+ sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
+ name, result->status, *statp);
nis_freeresult(result);
- return FALSE;
+ return false;
}
char *
@@ -2761,12 +3216,12 @@ nisplus_default_domain()
return default_domain;
p = nis_local_directory();
- snprintf(default_domain, sizeof default_domain, "%s", p);
+ (void) sm_strlcpy(default_domain, p, sizeof default_domain);
return default_domain;
}
#endif /* NISPLUS */
- /*
+/*
** LDAP Modules
*/
@@ -2776,11 +3231,13 @@ nisplus_default_domain()
#if defined(LDAPMAP) || defined(PH_MAP)
-# ifdef PH_MAP
+# if PH_MAP
# define ph_map_dequote ldapmap_dequote
# endif /* PH_MAP */
-char *
+static char *ldapmap_dequote __P((char *));
+
+static char *
ldapmap_dequote(str)
char *str;
{
@@ -2806,9 +3263,9 @@ ldapmap_dequote(str)
}
#endif /* defined(LDAPMAP) || defined(PH_MAP) */
-#ifdef LDAPMAP
+#if LDAPMAP
-LDAPMAP_STRUCT *LDAPDefaults = NULL;
+static SM_LDAP_STRUCT *LDAPDefaults = NULL;
/*
** LDAPMAP_OPEN -- open LDAP map
@@ -2823,11 +3280,11 @@ ldapmap_open(map, mode)
MAP *map;
int mode;
{
- LDAPMAP_STRUCT *lmap;
+ SM_LDAP_STRUCT *lmap;
STAB *s;
if (tTd(38, 2))
- dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
+ sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
mode &= O_ACCMODE;
@@ -2835,127 +3292,48 @@ ldapmap_open(map, mode)
if (mode != O_RDONLY)
{
/* issue a pseudo-error message */
-# ifdef ENOSYS
- errno = ENOSYS;
-# else /* ENOSYS */
-# ifdef EFTYPE
- errno = EFTYPE;
-# else /* EFTYPE */
- errno = ENXIO;
-# endif /* EFTYPE */
-# endif /* ENOSYS */
- return FALSE;
- }
-
- /* Comma separate if used as an alias file */
- if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
- map->map_coldelim = ',';
+ errno = SM_EMAPCANTWRITE;
+ return false;
+ }
- lmap = (LDAPMAP_STRUCT *) map->map_db1;
+ lmap = (SM_LDAP_STRUCT *) map->map_db1;
s = ldapmap_findconn(lmap);
if (s->s_lmap != NULL)
{
/* Already have a connection open to this LDAP server */
- lmap->ldap_ld = ((LDAPMAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
+ lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
+ lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
/* Add this map as head of linked list */
lmap->ldap_next = s->s_lmap;
s->s_lmap = map;
if (tTd(38, 2))
- dprintf("using cached connection\n");
- return TRUE;
+ sm_dprintf("using cached connection\n");
+ return true;
}
if (tTd(38, 2))
- dprintf("opening new connection\n");
+ sm_dprintf("opening new connection\n");
/* No connection yet, connect */
- if (!ldapmap_start(map))
- return FALSE;
-
- /* Save connection for reuse */
- s->s_lmap = map;
- return TRUE;
-}
-
-/*
-** LDAPMAP_START -- actually connect to an LDAP server
-**
-** Parameters:
-** map -- the map being opened.
-**
-** Returns:
-** TRUE if connection is successful, FALSE otherwise.
-**
-** Side Effects:
-** Populates lmap->ldap_ld.
-*/
-
-static jmp_buf LDAPTimeout;
-
-static bool
-ldapmap_start(map)
- MAP *map;
-{
- register int bind_result;
- int save_errno;
- register EVENT *ev = NULL;
- LDAPMAP_STRUCT *lmap;
- LDAP *ld;
-
- if (tTd(38, 2))
- dprintf("ldapmap_start(%s)\n", map->map_mname);
-
- lmap = (LDAPMAP_STRUCT *) map->map_db1;
-
- if (tTd(38,9))
- dprintf("ldapmap_start(%s, %d)\n",
- lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host,
- lmap->ldap_port);
-
-# if USE_LDAP_INIT
- ld = ldap_init(lmap->ldap_host, lmap->ldap_port);
- save_errno = errno;
-# else /* USE_LDAP_INIT */
- /*
- ** If using ldap_open(), the actual connection to the server
- ** happens now so we need the timeout here. For ldap_init(),
- ** the connection happens at bind time.
- */
-
- /* set the timeout */
- if (lmap->ldap_timeout.tv_sec != 0)
+ if (!sm_ldap_start(map->map_mname, lmap))
{
- if (setjmp(LDAPTimeout) != 0)
+ if (errno == ETIMEDOUT)
{
if (LogLevel > 1)
sm_syslog(LOG_NOTICE, CurEnv->e_id,
"timeout conning to LDAP server %.100s",
lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host);
- return FALSE;
}
- ev = setevent(lmap->ldap_timeout.tv_sec, ldaptimeout, 0);
- }
-
- ld = ldap_open(lmap->ldap_host, lmap->ldap_port);
- save_errno = errno;
- /* clear the event if it has not sprung */
- if (ev != NULL)
- clrevent(ev);
-# endif /* USE_LDAP_INIT */
-
- errno = save_errno;
- if (ld == NULL)
- {
if (!bitset(MF_OPTIONAL, map->map_mflags))
{
if (bitset(MF_NODEFER, map->map_mflags))
syserr("%s failed to %s in map %s",
# if USE_LDAP_INIT
- "ldap_init",
+ "ldap_init/ldap_bind",
# else /* USE_LDAP_INIT */
"ldap_open",
# endif /* USE_LDAP_INIT */
@@ -2965,7 +3343,7 @@ ldapmap_start(map)
else
syserr("421 4.0.0 %s failed to %s in map %s",
# if USE_LDAP_INIT
- "ldap_init",
+ "ldap_init/ldap_bind",
# else /* USE_LDAP_INIT */
"ldap_open",
# endif /* USE_LDAP_INIT */
@@ -2973,86 +3351,12 @@ ldapmap_start(map)
: lmap->ldap_host,
map->map_mname);
}
- return FALSE;
- }
-
- ldapmap_setopts(ld, lmap);
-
-# if USE_LDAP_INIT
- /*
- ** If using ldap_init(), the actual connection to the server
- ** happens at ldap_bind_s() so we need the timeout here.
- */
-
- /* set the timeout */
- if (lmap->ldap_timeout.tv_sec != 0)
- {
- if (setjmp(LDAPTimeout) != 0)
- {
- if (LogLevel > 1)
- sm_syslog(LOG_NOTICE, CurEnv->e_id,
- "timeout conning to LDAP server %.100s",
- lmap->ldap_host == NULL ? "localhost"
- : lmap->ldap_host);
- return FALSE;
- }
- ev = setevent(lmap->ldap_timeout.tv_sec, ldaptimeout, 0);
- }
-# endif /* USE_LDAP_INIT */
-
-# ifdef LDAP_AUTH_KRBV4
- if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
- lmap->ldap_secret != NULL)
- {
- /*
- ** Need to put ticket in environment here instead of
- ** during parseargs as there may be different tickets
- ** for different LDAP connections.
- */
-
- (void) putenv(lmap->ldap_secret);
- }
-# endif /* LDAP_AUTH_KRBV4 */
-
- bind_result = ldap_bind_s(ld, lmap->ldap_binddn,
- lmap->ldap_secret, lmap->ldap_method);
-
-# if USE_LDAP_INIT
- /* clear the event if it has not sprung */
- if (ev != NULL)
- clrevent(ev);
-# endif /* USE_LDAP_INIT */
-
- if (bind_result != LDAP_SUCCESS)
- {
- errno = bind_result + E_LDAPBASE;
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- {
- syserr("421 4.0.0 Cannot bind to map %s in ldap server %s",
- map->map_mname,
- lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host);
- }
- return FALSE;
+ return false;
}
- /* We need to cast ld into the map structure */
- lmap->ldap_ld = ld;
- return TRUE;
-}
-
-/* ARGSUSED */
-static void
-ldaptimeout(sig_no)
- int sig_no;
-{
- /*
- ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
- ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
- ** DOING.
- */
-
- errno = ETIMEDOUT;
- longjmp(LDAPTimeout, 1);
+ /* Save connection for reuse */
+ s->s_lmap = map;
+ return true;
}
/*
@@ -3063,20 +3367,20 @@ void
ldapmap_close(map)
MAP *map;
{
- LDAPMAP_STRUCT *lmap;
+ SM_LDAP_STRUCT *lmap;
STAB *s;
if (tTd(38, 2))
- dprintf("ldapmap_close(%s)\n", map->map_mname);
+ sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
- lmap = (LDAPMAP_STRUCT *) map->map_db1;
+ lmap = (SM_LDAP_STRUCT *) map->map_db1;
/* Check if already closed */
if (lmap->ldap_ld == NULL)
return;
/* Close the LDAP connection */
- ldap_unbind(lmap->ldap_ld);
+ sm_ldap_close(lmap);
/* Mark all the maps that share the connection as closed */
s = ldapmap_findconn(lmap);
@@ -3086,11 +3390,10 @@ ldapmap_close(map)
MAP *smap = s->s_lmap;
if (tTd(38, 2) && smap != map)
- dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
- map->map_mname, smap->map_mname);
-
+ sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
+ map->map_mname, smap->map_mname);
smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
- lmap = (LDAPMAP_STRUCT *) smap->map_db1;
+ lmap = (SM_LDAP_STRUCT *) smap->map_db1;
lmap->ldap_ld = NULL;
s->s_lmap = lmap->ldap_next;
lmap->ldap_next = NULL;
@@ -3099,11 +3402,11 @@ ldapmap_close(map)
# ifdef SUNET_ID
/*
-** SUNET_ID_HASH -- Convert a string to it's Sunet_id canonical form
+** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
** This only makes sense at Stanford University.
*/
-char *
+static char *
sunet_id_hash(str)
char *str;
{
@@ -3146,22 +3449,21 @@ ldapmap_lookup(map, name, av, statp)
int entries = 0;
int msgid;
int ret;
+ int save_errno;
int vsize;
- char *fp, *vp;
- char *p, *q;
+ char *vp, *p;
char *result = NULL;
- LDAPMAP_STRUCT *lmap = NULL;
+ SM_LDAP_STRUCT *lmap = NULL;
char keybuf[MAXNAME + 1];
- char filter[LDAPMAP_MAX_FILTER + 1];
if (tTd(38, 20))
- dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name);
+ sm_dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name);
/* Get ldap struct pointer from map */
- lmap = (LDAPMAP_STRUCT *) map->map_db1;
- ldapmap_setopts(lmap->ldap_ld, lmap);
+ lmap = (SM_LDAP_STRUCT *) map->map_db1;
+ sm_ldap_setopts(lmap->ldap_ld, lmap);
- (void) strlcpy(keybuf, name, sizeof keybuf);
+ (void) sm_strlcpy(keybuf, name, sizeof keybuf);
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
{
@@ -3172,91 +3474,32 @@ ldapmap_lookup(map, name, av, statp)
# endif /* SUNET_ID */
}
- /* substitute keybuf into filter, perhaps multiple times */
- memset(filter, '\0', sizeof filter);
- fp = filter;
- p = lmap->ldap_filter;
- while ((q = strchr(p, '%')) != NULL)
- {
- if (q[1] == 's')
- {
- snprintf(fp, SPACELEFT(filter, fp), "%.*s%s",
- (int) (q - p), p, keybuf);
- fp += strlen(fp);
- p = q + 2;
- }
- else if (q[1] == '0')
- {
- char *k = keybuf;
-
- snprintf(fp, SPACELEFT(filter, fp), "%.*s",
- (int) (q - p), p);
- fp += strlen(fp);
- p = q + 2;
-
- /* Properly escape LDAP special characters */
- while (SPACELEFT(filter, fp) > 0 &&
- *k != '\0')
- {
- if (*k == '*' || *k == '(' ||
- *k == ')' || *k == '\\')
- {
- (void) strlcat(fp,
- (*k == '*' ? "\\2A" :
- (*k == '(' ? "\\28" :
- (*k == ')' ? "\\29" :
- (*k == '\\' ? "\\5C" :
- "\00")))),
- SPACELEFT(filter, fp));
- fp += strlen(fp);
- k++;
- }
- else
- *fp++ = *k++;
- }
- }
- else
- {
- snprintf(fp, SPACELEFT(filter, fp), "%.*s",
- (int) (q - p + 1), p);
- p = q + (q[1] == '%' ? 2 : 1);
- fp += strlen(fp);
- }
- }
- snprintf(fp, SPACELEFT(filter, fp), "%s", p);
- if (tTd(38, 20))
- dprintf("ldap search filter=%s\n", filter);
-
- lmap->ldap_res = NULL;
- msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base, lmap->ldap_scope,
- filter,
- (lmap->ldap_attr[0] == NULL ? NULL :
- lmap->ldap_attr),
- lmap->ldap_attrsonly);
+ msgid = sm_ldap_search(lmap, keybuf);
if (msgid == -1)
{
- int save_errno;
-
- errno = ldapmap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
+ errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
save_errno = errno;
if (!bitset(MF_OPTIONAL, map->map_mflags))
{
if (bitset(MF_NODEFER, map->map_mflags))
syserr("Error in ldap_search using %s in map %s",
- filter, map->map_mname);
+ keybuf, map->map_mname);
else
syserr("421 4.0.0 Error in ldap_search using %s in map %s",
- filter, map->map_mname);
+ keybuf, map->map_mname);
}
*statp = EX_TEMPFAIL;
-#ifdef LDAP_SERVER_DOWN
- errno = save_errno;
- if (errno == LDAP_SERVER_DOWN + E_LDAPBASE)
+ switch (save_errno - E_LDAPBASE)
{
+#ifdef LDAP_SERVER_DOWN
+ case LDAP_SERVER_DOWN:
+#endif /* LDAP_SERVER_DOWN */
+ case LDAP_TIMEOUT:
+ case LDAP_UNAVAILABLE:
/* server disappeared, try reopen on next search */
ldapmap_close(map);
+ break;
}
-#endif /* LDAP_SERVER_DOWN */
errno = save_errno;
return NULL;
}
@@ -3264,9 +3507,58 @@ ldapmap_lookup(map, name, av, statp)
*statp = EX_NOTFOUND;
vp = NULL;
- /* Get results (all if MF_NOREWRITE, otherwise one by one) */
- while ((ret = ldap_result(lmap->ldap_ld, msgid,
- bitset(MF_NOREWRITE, map->map_mflags),
+# if _FFR_LDAP_RECURSION
+ {
+ int flags;
+ SM_RPOOL_T *rpool;
+
+ flags = 0;
+ if (bitset(MF_SINGLEMATCH, map->map_mflags))
+ flags |= SM_LDAP_SINGLEMATCH;
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ flags |= SM_LDAP_MATCHONLY;
+
+ /* Create an rpool for search related memory usage */
+ rpool = sm_rpool_new_x(NULL);
+
+ p = NULL;
+ *statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
+ rpool, &p, NULL);
+ save_errno = errno;
+
+ /* Copy result so rpool can be freed */
+ if (*statp == EX_OK && p != NULL)
+ vp = newstr(p);
+ sm_rpool_free(rpool);
+
+ /* need to restart LDAP connection? */
+ if (*statp == EX_RESTART)
+ {
+ *statp = EX_TEMPFAIL;
+ ldapmap_close(map);
+ }
+
+ errno = save_errno;
+ if (*statp != EX_OK && *statp != EX_NOTFOUND)
+ {
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ {
+ if (bitset(MF_NODEFER, map->map_mflags))
+ syserr("Error getting LDAP results in map %s",
+ map->map_mname);
+ else
+ syserr("421 4.0.0 Error getting LDAP results in map %s",
+ map->map_mname);
+ }
+ errno = save_errno;
+ return NULL;
+ }
+ goto finishlookup;
+ }
+# endif /* _FFR_LDAP_RECURSION */
+
+ /* Get results */
+ while ((ret = ldap_result(lmap->ldap_ld, msgid, 0,
(lmap->ldap_timeout.tv_sec == 0 ? NULL :
&(lmap->ldap_timeout)),
&(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY)
@@ -3287,9 +3579,9 @@ ldapmap_lookup(map, name, av, statp)
}
(void) ldap_abandon(lmap->ldap_ld, msgid);
if (vp != NULL)
- sm_free(vp);
+ sm_free(vp); /* XXX */
if (tTd(38, 25))
- dprintf("ldap search found multiple on a single match query\n");
+ sm_dprintf("ldap search found multiple on a single match query\n");
return NULL;
}
}
@@ -3341,15 +3633,21 @@ ldapmap_lookup(map, name, av, statp)
attr);
if (vals == NULL)
{
- errno = ldapmap_geterrno(lmap->ldap_ld);
- if (errno == LDAP_SUCCESS)
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
+ if (save_errno == LDAP_SUCCESS)
+ {
+# if USING_NETSCAPE_LDAP
+ ldap_memfree(attr);
+# endif /* USING_NETSCAPE_LDAP */
continue;
+ }
/* Must be an error */
- errno += E_LDAPBASE;
+ save_errno += E_LDAPBASE;
if (!bitset(MF_OPTIONAL,
map->map_mflags))
{
+ errno = save_errno;
if (bitset(MF_NODEFER,
map->map_mflags))
syserr("Error getting LDAP values in map %s",
@@ -3370,7 +3668,8 @@ ldapmap_lookup(map, name, av, statp)
(void) ldap_abandon(lmap->ldap_ld,
msgid);
if (vp != NULL)
- sm_free(vp);
+ sm_free(vp); /* XXX */
+ errno = save_errno;
return NULL;
}
}
@@ -3393,7 +3692,15 @@ ldapmap_lookup(map, name, av, statp)
*/
if (bitset(MF_MATCHONLY, map->map_mflags))
+ {
+ if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
+ ldap_value_free(vals);
+
+# if USING_NETSCAPE_LDAP
+ ldap_memfree(attr);
+# endif /* USING_NETSCAPE_LDAP */
continue;
+ }
/*
** If we don't want multiple values,
@@ -3420,7 +3727,18 @@ ldapmap_lookup(map, name, av, statp)
continue;
}
- vp = newstr(vals[0]);
+ vsize = strlen(vals[0]) + 1;
+ if (lmap->ldap_attrsep != '\0')
+ vsize += strlen(attr) + 1;
+ vp = xalloc(vsize);
+ if (lmap->ldap_attrsep != '\0')
+ sm_snprintf(vp, vsize,
+ "%s%c%s",
+ attr,
+ lmap->ldap_attrsep,
+ vals[0]);
+ else
+ sm_strlcpy(vp, vals[0], vsize);
ldap_value_free(vals);
# if USING_NETSCAPE_LDAP
ldap_memfree(attr);
@@ -3438,10 +3756,11 @@ ldapmap_lookup(map, name, av, statp)
vsize = strlen(vp) +
strlen(attr) + 2;
tmp = xalloc(vsize);
- snprintf(tmp, vsize, "%s%c%s",
- vp, map->map_coldelim,
- attr);
- sm_free(vp);
+ (void) sm_snprintf(tmp,
+ vsize, "%s%c%s",
+ vp, map->map_coldelim,
+ attr);
+ sm_free(vp); /* XXX */
vp = tmp;
}
# if USING_NETSCAPE_LDAP
@@ -3458,15 +3777,25 @@ ldapmap_lookup(map, name, av, statp)
vsize = 0;
for (i = 0; vals[i] != NULL; i++)
+ {
vsize += strlen(vals[i]) + 1;
+ if (lmap->ldap_attrsep != '\0')
+ vsize += strlen(attr) + 1;
+ }
vp_tmp = xalloc(vsize);
*vp_tmp = '\0';
p = vp_tmp;
for (i = 0; vals[i] != NULL; i++)
{
- p += strlcpy(p, vals[i],
- vsize - (p - vp_tmp));
+ if (lmap->ldap_attrsep != '\0')
+ {
+ p += sm_strlcpy(p, attr,
+ vsize - (p - vp_tmp));
+ *p++ = lmap->ldap_attrsep;
+ }
+ p += sm_strlcpy(p, vals[i],
+ vsize - (p - vp_tmp));
if (p >= vp_tmp + vsize)
syserr("ldapmap_lookup: Internal error: buffer too small for LDAP values");
if (vals[i + 1] != NULL)
@@ -3484,14 +3813,14 @@ ldapmap_lookup(map, name, av, statp)
}
vsize = strlen(vp) + strlen(vp_tmp) + 2;
tmp = xalloc(vsize);
- snprintf(tmp, vsize, "%s%c%s",
+ (void) sm_snprintf(tmp, vsize, "%s%c%s",
vp, map->map_coldelim, vp_tmp);
- sm_free(vp);
- sm_free(vp_tmp);
+ sm_free(vp); /* XXX */
+ sm_free(vp_tmp); /* XXX */
vp = tmp;
}
- errno = ldapmap_geterrno(lmap->ldap_ld);
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
/*
** We check errno != LDAP_DECODING_ERROR since
@@ -3502,13 +3831,14 @@ ldapmap_lookup(map, name, av, statp)
** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
*/
- if (errno != LDAP_SUCCESS &&
- errno != LDAP_DECODING_ERROR)
+ if (save_errno != LDAP_SUCCESS &&
+ save_errno != LDAP_DECODING_ERROR)
{
/* Must be an error */
- errno += E_LDAPBASE;
+ save_errno += E_LDAPBASE;
if (!bitset(MF_OPTIONAL, map->map_mflags))
{
+ errno = save_errno;
if (bitset(MF_NODEFER, map->map_mflags))
syserr("Error getting LDAP attributes in map %s",
map->map_mname);
@@ -3524,7 +3854,8 @@ ldapmap_lookup(map, name, av, statp)
}
(void) ldap_abandon(lmap->ldap_ld, msgid);
if (vp != NULL)
- sm_free(vp);
+ sm_free(vp); /* XXX */
+ errno = save_errno;
return NULL;
}
@@ -3532,13 +3863,15 @@ ldapmap_lookup(map, name, av, statp)
if (map->map_coldelim == '\0' && vp != NULL)
break;
}
- errno = ldapmap_geterrno(lmap->ldap_ld);
- if (errno != LDAP_SUCCESS && errno != LDAP_DECODING_ERROR)
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
+ if (save_errno != LDAP_SUCCESS &&
+ save_errno != LDAP_DECODING_ERROR)
{
/* Must be an error */
- errno += E_LDAPBASE;
+ save_errno += E_LDAPBASE;
if (!bitset(MF_OPTIONAL, map->map_mflags))
{
+ errno = save_errno;
if (bitset(MF_NODEFER, map->map_mflags))
syserr("Error getting LDAP entries in map %s",
map->map_mname);
@@ -3554,52 +3887,26 @@ ldapmap_lookup(map, name, av, statp)
}
(void) ldap_abandon(lmap->ldap_ld, msgid);
if (vp != NULL)
- sm_free(vp);
+ sm_free(vp); /* XXX */
+ errno = save_errno;
return NULL;
}
ldap_msgfree(lmap->ldap_res);
lmap->ldap_res = NULL;
}
- /*
- ** If grabbing all results at once for MF_NOREWRITE and
- ** only want a single match, make sure that's all we have
- */
-
- if (ret == LDAP_RES_SEARCH_RESULT &&
- bitset(MF_NOREWRITE|MF_SINGLEMATCH, map->map_mflags))
- {
- entries += ldap_count_entries(lmap->ldap_ld, lmap->ldap_res);
- if (entries > 1)
- {
- *statp = EX_NOTFOUND;
- if (lmap->ldap_res != NULL)
- {
- ldap_msgfree(lmap->ldap_res);
- lmap->ldap_res = NULL;
- }
- if (vp != NULL)
- sm_free(vp);
- return NULL;
- }
- *statp = EX_OK;
- }
-
if (ret == 0)
- errno = ETIMEDOUT;
+ save_errno = ETIMEDOUT;
else
- errno = ldapmap_geterrno(lmap->ldap_ld);
- if (errno != LDAP_SUCCESS)
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
+ if (save_errno != LDAP_SUCCESS)
{
- int save_errno;
-
- /* Must be an error */
if (ret != 0)
- errno += E_LDAPBASE;
- save_errno = errno;
+ save_errno += E_LDAPBASE;
if (!bitset(MF_OPTIONAL, map->map_mflags))
{
+ errno = save_errno;
if (bitset(MF_NODEFER, map->map_mflags))
syserr("Error getting LDAP results in map %s",
map->map_mname);
@@ -3609,37 +3916,31 @@ ldapmap_lookup(map, name, av, statp)
}
*statp = EX_TEMPFAIL;
if (vp != NULL)
- sm_free(vp);
-#ifdef LDAP_SERVER_DOWN
- errno = save_errno;
- if (errno == LDAP_SERVER_DOWN + E_LDAPBASE)
+ sm_free(vp); /* XXX */
+
+ switch (save_errno - E_LDAPBASE)
{
+#ifdef LDAP_SERVER_DOWN
+ case LDAP_SERVER_DOWN:
+#endif /* LDAP_SERVER_DOWN */
+ case LDAP_TIMEOUT:
+ case LDAP_UNAVAILABLE:
/* server disappeared, try reopen on next search */
ldapmap_close(map);
+ break;
}
-#endif /* LDAP_SERVER_DOWN */
errno = save_errno;
return NULL;
}
+# if _FFR_LDAP_RECURSION
+finishlookup:
+# endif /* _FFR_LDAP_RECURSION */
+
/* Did we match anything? */
if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
return NULL;
- /*
- ** If MF_NOREWRITE, we are special map which doesn't
- ** actually return a map value. Instead, we don't free
- ** ldap_res and let the calling function process the LDAP
- ** results. The caller should ldap_msgfree(lmap->ldap_res).
- */
-
- if (bitset(MF_NOREWRITE, map->map_mflags))
- {
- if (vp != NULL)
- sm_free(vp);
- return "";
- }
-
if (*statp == EX_OK)
{
if (LogLevel > 9)
@@ -3654,7 +3955,7 @@ ldapmap_lookup(map, name, av, statp)
result = map_rewrite(map, vp, strlen(vp), av);
}
if (vp != NULL)
- sm_free(vp);
+ sm_free(vp); /* XXX */
}
return result;
}
@@ -3673,126 +3974,39 @@ ldapmap_lookup(map, name, av, statp)
**
** Returns:
** Symbol table entry for the LDAP connection.
-**
*/
static STAB *
ldapmap_findconn(lmap)
- LDAPMAP_STRUCT *lmap;
+ SM_LDAP_STRUCT *lmap;
{
- int len;
char *nbuf;
- STAB *s;
-
- len = (lmap->ldap_host == NULL ? strlen("localhost") :
- strlen(lmap->ldap_host)) + 1 + 8 + 1 +
- (lmap->ldap_binddn == NULL ? 0 : strlen(lmap->ldap_binddn)) +
- 1 +
- (lmap->ldap_secret == NULL ? 0 : strlen(lmap->ldap_secret)) +
- 8 + 1;
- nbuf = xalloc(len);
- snprintf(nbuf, len, "%s%c%d%c%s%c%s%d",
- (lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host),
- CONDELSE,
- lmap->ldap_port,
- CONDELSE,
- (lmap->ldap_binddn == NULL ? "" : lmap->ldap_binddn),
- CONDELSE,
- (lmap->ldap_secret == NULL ? "" : lmap->ldap_secret),
- (int) getpid());
- s = stab(nbuf, ST_LMAP, ST_ENTER);
- sm_free(nbuf);
+ STAB *SM_NONVOLATILE s = NULL;
+
+ nbuf = sm_stringf_x("%s%c%d%c%s%c%s%d",
+ (lmap->ldap_host == NULL ? "localhost"
+ : lmap->ldap_host),
+ CONDELSE,
+ lmap->ldap_port,
+ CONDELSE,
+ (lmap->ldap_binddn == NULL ? ""
+ : lmap->ldap_binddn),
+ CONDELSE,
+ (lmap->ldap_secret == NULL ? ""
+ : lmap->ldap_secret),
+ (int) CurrentPid);
+ SM_TRY
+ s = stab(nbuf, ST_LMAP, ST_ENTER);
+ SM_FINALLY
+ sm_free(nbuf);
+ SM_END_TRY
return s;
}
- /*
-** LDAPMAP_SETOPTS -- set LDAP options
-**
-** Parameters:
-** ld -- LDAP session handle
-** lmap -- LDAP map information
-**
-** Returns:
-** None.
-**
-*/
-
-static void
-ldapmap_setopts(ld, lmap)
- LDAP *ld;
- LDAPMAP_STRUCT *lmap;
-{
-# if USE_LDAP_SET_OPTION
- ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref);
- if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options))
- ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
- else
- ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
- ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit);
- ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit);
-# else /* USE_LDAP_SET_OPTION */
- /* From here on in we can use ldap internal timelimits */
- ld->ld_deref = lmap->ldap_deref;
- ld->ld_options = lmap->ldap_options;
- ld->ld_sizelimit = lmap->ldap_sizelimit;
- ld->ld_timelimit = lmap->ldap_timelimit;
-# endif /* USE_LDAP_SET_OPTION */
-}
- /*
-** LDAPMAP_GETERRNO -- get ldap errno value
-**
-** Parameters:
-** ld -- LDAP session handle
-**
-** Returns:
-** LDAP errno.
-**
-*/
-
-static int
-ldapmap_geterrno(ld)
- LDAP *ld;
-{
- int err = LDAP_SUCCESS;
-
-# if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
- (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
-# else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
-# ifdef LDAP_OPT_SIZELIMIT
- err = ldap_get_lderrno(ld, NULL, NULL);
-# else /* LDAP_OPT_SIZELIMIT */
- err = ld->ld_errno;
-
- /*
- ** Reset value to prevent lingering LDAP_DECODING_ERROR due to
- ** OpenLDAP 1.X's hack (see above)
- */
-
- ld->ld_errno = LDAP_SUCCESS;
-# endif /* LDAP_OPT_SIZELIMIT */
-# endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
- return err;
-}
-
-/*
-** LDAPX_MAP_PARSEARGS -- print warning about use of ldapx map.
-*/
-
-bool
-ldapx_map_parseargs(map, args)
- MAP *map;
- char *args;
-{
- printf("Warning: The \"ldapx\" map class is deprecated and will be removed in a future\n");
- printf(" version. Use the \"ldap\" map class instead for map \"%s\".\n",
- map->map_mname);
- return ldapmap_parseargs(map, args);
-}
-
/*
** LDAPMAP_PARSEARGS -- parse ldap map definition args.
*/
-struct lamvalues LDAPAuthMethods[] =
+static struct lamvalues LDAPAuthMethods[] =
{
{ "none", LDAP_AUTH_NONE },
{ "simple", LDAP_AUTH_SIMPLE },
@@ -3802,7 +4016,7 @@ struct lamvalues LDAPAuthMethods[] =
{ NULL, 0 }
};
-struct ladvalues LDAPAliasDereference[] =
+static struct ladvalues LDAPAliasDereference[] =
{
{ "never", LDAP_DEREF_NEVER },
{ "always", LDAP_DEREF_ALWAYS },
@@ -3811,7 +4025,7 @@ struct ladvalues LDAPAliasDereference[] =
{ NULL, 0 }
};
-struct lssvalues LDAPSearchScope[] =
+static struct lssvalues LDAPSearchScope[] =
{
{ "base", LDAP_SCOPE_BASE },
{ "one", LDAP_SCOPE_ONELEVEL },
@@ -3824,25 +4038,26 @@ ldapmap_parseargs(map, args)
MAP *map;
char *args;
{
- bool secretread = TRUE;
+ bool secretread = true;
int i;
register char *p = args;
- LDAPMAP_STRUCT *lmap;
+ SM_LDAP_STRUCT *lmap;
struct lamvalues *lam;
struct ladvalues *lad;
struct lssvalues *lss;
+ char ldapfilt[MAXLINE];
char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
/* Get ldap struct pointer from map */
- lmap = (LDAPMAP_STRUCT *) map->map_db1;
+ lmap = (SM_LDAP_STRUCT *) map->map_db1;
/* Check if setting the initial LDAP defaults */
if (lmap == NULL || lmap != LDAPDefaults)
{
- /* We need to alloc an LDAPMAP_STRUCT struct */
- lmap = (LDAPMAP_STRUCT *) xalloc(sizeof *lmap);
+ /* We need to alloc an SM_LDAP_STRUCT struct */
+ lmap = (SM_LDAP_STRUCT *) xalloc(sizeof *lmap);
if (LDAPDefaults == NULL)
- ldapmap_clear(lmap);
+ sm_ldap_clear(lmap);
else
STRUCTCOPY(*LDAPDefaults, *lmap);
}
@@ -3850,6 +4065,58 @@ ldapmap_parseargs(map, args)
/* there is no check whether there is really an argument */
map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
map->map_spacesub = SpaceSub; /* default value */
+
+ /* Check if setting up an alias or file class LDAP map */
+ if (bitset(MF_ALIAS, map->map_mflags))
+ {
+ /* Comma separate if used as an alias file */
+ map->map_coldelim = ',';
+ if (*args == '\0')
+ {
+ int n;
+ char *lc;
+ char jbuf[MAXHOSTNAMELEN];
+ char lcbuf[MAXLINE];
+
+ /* Get $j */
+ expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
+ if (jbuf[0] == '\0')
+ {
+ (void) sm_strlcpy(jbuf, "localhost",
+ sizeof jbuf);
+ }
+
+ lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
+ if (lc == NULL)
+ lc = "";
+ else
+ {
+ expand(lc, lcbuf, sizeof lcbuf, CurEnv);
+ lc = lcbuf;
+ }
+
+ n = sm_snprintf(ldapfilt, sizeof ldapfilt,
+ "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
+ lc, jbuf);
+ if (n >= sizeof ldapfilt)
+ {
+ syserr("%s: Default LDAP string too long",
+ map->map_mname);
+ return false;
+ }
+
+ /* default args for an alias LDAP entry */
+ lmap->ldap_filter = ldapfilt;
+ lmap->ldap_attr[0] = "sendmailMTAAliasValue";
+ lmap->ldap_attr[1] = NULL;
+ }
+ }
+ else if (bitset(MF_FILECLASS, map->map_mflags))
+ {
+ /* Space separate if used as a file class file */
+ map->map_coldelim = ' ';
+ }
+
for (;;)
{
while (isascii(*p) && isspace(*p))
@@ -3929,6 +4196,27 @@ ldapmap_parseargs(map, args)
break;
/* Start of ldapmap specific args */
+ case 'V':
+ if (*++p != '\\')
+ lmap->ldap_attrsep = *p;
+ else
+ {
+ switch (*++p)
+ {
+ case 'n':
+ lmap->ldap_attrsep = '\n';
+ break;
+
+ case 't':
+ lmap->ldap_attrsep = '\t';
+ break;
+
+ default:
+ lmap->ldap_attrsep = '\\';
+ }
+ }
+ break;
+
case 'k': /* search field */
while (isascii(*++p) && isspace(*p))
continue;
@@ -3951,7 +4239,7 @@ ldapmap_parseargs(map, args)
# ifdef LDAP_REFERRALS
lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
# else /* LDAP_REFERRALS */
- syserr("compile with -DLDAP_REFERRALS for referral support\n");
+ syserr("compile with -DLDAP_REFERRALS for referral support");
# endif /* LDAP_REFERRALS */
break;
@@ -3963,14 +4251,14 @@ ldapmap_parseargs(map, args)
while (isascii(*++p) && isspace(*p))
continue;
- if (strncasecmp(p, "LDAP_DEREF_", 11) == 0)
+ if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
p += 11;
for (lad = LDAPAliasDereference;
lad != NULL && lad->lad_name != NULL; lad++)
{
- if (strncasecmp(p, lad->lad_name,
- strlen(lad->lad_name)) == 0)
+ if (sm_strncasecmp(p, lad->lad_name,
+ strlen(lad->lad_name)) == 0)
break;
}
if (lad->lad_name != NULL)
@@ -3989,7 +4277,7 @@ ldapmap_parseargs(map, args)
p, map->map_mname);
if (ptr != NULL)
*ptr = ' ';
- return FALSE;
+ return false;
}
}
break;
@@ -3998,14 +4286,14 @@ ldapmap_parseargs(map, args)
while (isascii(*++p) && isspace(*p))
continue;
- if (strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
+ if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
p += 11;
for (lss = LDAPSearchScope;
lss != NULL && lss->lss_name != NULL; lss++)
{
- if (strncasecmp(p, lss->lss_name,
- strlen(lss->lss_name)) == 0)
+ if (sm_strncasecmp(p, lss->lss_name,
+ strlen(lss->lss_name)) == 0)
break;
}
if (lss->lss_name != NULL)
@@ -4024,7 +4312,7 @@ ldapmap_parseargs(map, args)
p, map->map_mname);
if (ptr != NULL)
*ptr = ' ';
- return FALSE;
+ return false;
}
}
break;
@@ -4070,14 +4358,14 @@ ldapmap_parseargs(map, args)
while (isascii(*++p) && isspace(*p))
continue;
- if (strncasecmp(p, "LDAP_AUTH_", 10) == 0)
+ if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
p += 10;
for (lam = LDAPAuthMethods;
lam != NULL && lam->lam_name != NULL; lam++)
{
- if (strncasecmp(p, lam->lam_name,
- strlen(lam->lam_name)) == 0)
+ if (sm_strncasecmp(p, lam->lam_name,
+ strlen(lam->lam_name)) == 0)
break;
}
if (lam->lam_name != NULL)
@@ -4096,7 +4384,7 @@ ldapmap_parseargs(map, args)
p, map->map_mname);
if (ptr != NULL)
*ptr = ' ';
- return FALSE;
+ return false;
}
}
@@ -4111,7 +4399,7 @@ ldapmap_parseargs(map, args)
while (isascii(*++p) && isspace(*p))
continue;
lmap->ldap_secret = p;
- secretread = FALSE;
+ secretread = false;
break;
default:
@@ -4165,7 +4453,7 @@ ldapmap_parseargs(map, args)
LDAPDefaults == lmap ||
LDAPDefaults->ldap_secret != lmap->ldap_secret))
{
- FILE *sfd;
+ SM_FILE_T *sfd;
long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
if (DontLockReadFiles)
@@ -4195,12 +4483,12 @@ ldapmap_parseargs(map, args)
{
syserr("LDAP map: cannot open secret %s",
ldapmap_dequote(lmap->ldap_secret));
- return FALSE;
+ return false;
}
lmap->ldap_secret = sfgets(m_tmp, LDAPMAP_MAX_PASSWD,
sfd, TimeOuts.to_fileopen,
"ldapmap_parseargs");
- (void) fclose(sfd);
+ (void) sm_io_close(sfd, SM_TIME_DEFAULT);
if (lmap->ldap_secret != NULL &&
strlen(m_tmp) > 0)
{
@@ -4220,16 +4508,18 @@ ldapmap_parseargs(map, args)
** stashed
*/
- snprintf(m_tmp, MAXPATHLEN + LDAPMAP_MAX_PASSWD,
- "KRBTKFILE=%s",
- ldapmap_dequote(lmap->ldap_secret));
+ (void) sm_snprintf(m_tmp,
+ MAXPATHLEN + LDAPMAP_MAX_PASSWD,
+ "KRBTKFILE=%s",
+ ldapmap_dequote(lmap->ldap_secret));
lmap->ldap_secret = m_tmp;
break;
# endif /* LDAP_AUTH_KRBV4 */
default: /* Should NEVER get here */
syserr("LDAP map: Illegal value in lmap method");
- return FALSE;
+ return false;
+ /* NOTREACHED */
break;
}
}
@@ -4259,7 +4549,7 @@ ldapmap_parseargs(map, args)
/* If setting defaults, don't process ldap_filter and ldap_attr */
if (lmap == LDAPDefaults)
- return TRUE;
+ return true;
if (lmap->ldap_filter != NULL)
lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
@@ -4268,12 +4558,17 @@ ldapmap_parseargs(map, args)
if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
{
syserr("No filter given in map %s", map->map_mname);
- return FALSE;
+ return false;
}
}
if (lmap->ldap_attr[0] != NULL)
{
+#if _FFR_LDAP_RECURSION
+ bool recurse = false;
+ int final = 0;
+#endif /* _FFR_LDAP_RECURSION */
+
i = 0;
p = ldapmap_dequote(lmap->ldap_attr[0]);
lmap->ldap_attr[0] = NULL;
@@ -4295,58 +4590,88 @@ ldapmap_parseargs(map, args)
{
syserr("Too many return attributes in %s (max %d)",
map->map_mname, LDAPMAP_MAX_ATTR);
- return FALSE;
+ return false;
}
if (*v != '\0')
- lmap->ldap_attr[i++] = newstr(v);
+ {
+#if _FFR_LDAP_RECURSION
+ char *type;
+
+ type = strchr(v, ':');
+ if (type != NULL)
+ *type++ = '\0';
+#endif /* _FFR_LDAP_RECURSION */
+
+ lmap->ldap_attr[i] = newstr(v);
+
+#if _FFR_LDAP_RECURSION
+ if (type != NULL)
+ {
+ if (sm_strcasecmp(type, "normal") == 0)
+ {
+ lmap->ldap_attr_type[i] = LDAPMAP_ATTR_NORMAL;
+ }
+ else if (sm_strcasecmp(type, "dn") == 0)
+ {
+ recurse = true;
+ lmap->ldap_attr_type[i] = LDAPMAP_ATTR_DN;
+ }
+ else if (sm_strcasecmp(type, "filter") == 0)
+ {
+ recurse = true;
+ lmap->ldap_attr_type[i] = LDAPMAP_ATTR_FILTER;
+ }
+ else if (sm_strcasecmp(type, "url") == 0)
+ {
+ recurse = true;
+ lmap->ldap_attr_type[i] = LDAPMAP_ATTR_URL;
+ }
+ else if (sm_strcasecmp(type, "final") == 0)
+ {
+ lmap->ldap_attr_type[i] = LDAPMAP_ATTR_FINAL;
+ if (final >= LDAPMAP_MAX_ATTR)
+ {
+ syserr("Too many FINAL attributes in %s (max %d)",
+ map->map_mname, LDAPMAP_MAX_ATTR);
+ return false;
+ }
+ lmap->ldap_attr_final[final++] = lmap->ldap_attr[i];
+ }
+ else
+ {
+ syserr("Unknown attribute type (%s) in %s",
+ type, map->map_mname);
+ return false;
+ }
+ }
+ else
+ lmap->ldap_attr_type[i] = LDAPMAP_ATTR_NORMAL;
+#endif /* _FFR_LDAP_RECURSION */
+ i++;
+ }
}
lmap->ldap_attr[i] = NULL;
+#if _FFR_LDAP_RECURSION
+ lmap->ldap_attr_final[final] = NULL;
+ if (recurse && lmap->ldap_attr_final[0] == NULL)
+ {
+ syserr("LDAP recursion requested in %s but no FINAL attribute given",
+ map->map_mname);
+ return false;
+ }
+ if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
+ {
+ syserr("LDAP recursion requested in %s can not be used with -n",
+ map->map_mname);
+ return false;
+ }
+#endif /* _FFR_LDAP_RECURSION */
}
-
map->map_db1 = (ARBPTR_T) lmap;
- return TRUE;
+ return true;
}
/*
-** LDAPMAP_CLEAR -- set default values for LDAPMAP_STRUCT
-**
-** Parameters:
-** lmap -- pointer to LDAPMAP_STRUCT to clear
-**
-** Returns:
-** None.
-**
-*/
-
-static void
-ldapmap_clear(lmap)
- LDAPMAP_STRUCT *lmap;
-{
- lmap->ldap_host = NULL;
- lmap->ldap_port = LDAP_PORT;
- lmap->ldap_deref = LDAP_DEREF_NEVER;
- lmap->ldap_timelimit = LDAP_NO_LIMIT;
- lmap->ldap_sizelimit = LDAP_NO_LIMIT;
-# ifdef LDAP_REFERRALS
- lmap->ldap_options = LDAP_OPT_REFERRALS;
-# else /* LDAP_REFERRALS */
- lmap->ldap_options = 0;
-# endif /* LDAP_REFERRALS */
- lmap->ldap_binddn = NULL;
- lmap->ldap_secret = NULL;
- lmap->ldap_method = LDAP_AUTH_SIMPLE;
- lmap->ldap_base = NULL;
- lmap->ldap_scope = LDAP_SCOPE_SUBTREE;
- lmap->ldap_attrsonly = LDAPMAP_FALSE;
- lmap->ldap_timeout.tv_sec = 0;
- lmap->ldap_timeout.tv_usec = 0;
- lmap->ldap_ld = NULL;
- lmap->ldap_filter = NULL;
- lmap->ldap_attr[0] = NULL;
- lmap->ldap_res = NULL;
- lmap->ldap_next = NULL;
-}
- /*
** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
**
** Parameters:
@@ -4354,7 +4679,6 @@ ldapmap_clear(lmap)
**
** Returns:
** None.
-**
*/
void
@@ -4366,8 +4690,8 @@ ldapmap_set_defaults(spec)
/* Allocate and set the default values */
if (LDAPDefaults == NULL)
- LDAPDefaults = (LDAPMAP_STRUCT *) xalloc(sizeof *LDAPDefaults);
- ldapmap_clear(LDAPDefaults);
+ LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof *LDAPDefaults);
+ sm_ldap_clear(LDAPDefaults);
memset(&map, '\0', sizeof map);
@@ -4391,16 +4715,8 @@ ldapmap_set_defaults(spec)
map.map_tapp != NULL)
{
syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
- if (map.map_app != NULL)
- {
- sm_free(map.map_app);
- map.map_app = NULL;
- }
- if (map.map_tapp != NULL)
- {
- sm_free(map.map_tapp);
- map.map_tapp = NULL;
- }
+ SM_FREE_CLR(map.map_app);
+ SM_FREE_CLR(map.map_tapp);
}
if (LDAPDefaults->ldap_filter != NULL)
@@ -4418,11 +4734,11 @@ ldapmap_set_defaults(spec)
}
}
#endif /* LDAPMAP */
- /*
+/*
** PH map
*/
-#ifdef PH_MAP
+#if PH_MAP
/*
** Support for the CCSO Nameserver (ph/qi).
@@ -4430,8 +4746,11 @@ ldapmap_set_defaults(spec)
** Contributed by Mark D. Roth <roth@uiuc.edu>. Contact him for support.
*/
-# include <qiapi.h>
-# include <qicode.h>
+/* what version of the ph map code we're running */
+static char phmap_id[PH_BUF_SIZE];
+
+/* sendmail version for phmap id string */
+extern const char Version[];
/*
** PH_MAP_PARSEARGS -- parse ph map definition args.
@@ -4442,20 +4761,23 @@ ph_map_parseargs(map, args)
MAP *map;
char *args;
{
- int i;
- register int done;
- PH_MAP_STRUCT *pmap = NULL;
+ register bool done;
register char *p = args;
+ PH_MAP_STRUCT *pmap = NULL;
+
+ /* initialize version string */
+ (void) sm_snprintf(phmap_id, sizeof phmap_id,
+ "sendmail-%s phmap-20010529 libphclient-%s",
+ Version, libphclient_version);
pmap = (PH_MAP_STRUCT *) xalloc(sizeof *pmap);
/* defaults */
pmap->ph_servers = NULL;
pmap->ph_field_list = NULL;
- pmap->ph_to_server = NULL;
- pmap->ph_from_server = NULL;
- pmap->ph_sockfd = -1;
+ pmap->ph = NULL;
pmap->ph_timeout = 0;
+ pmap->ph_fastclose = 0;
map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
for (;;)
@@ -4507,13 +4829,11 @@ ph_map_parseargs(map, args)
map->map_tapp = ++p;
break;
-#if _FFR_PHMAP_TIMEOUT
case 'l':
while (isascii(*++p) && isspace(*p))
continue;
pmap->ph_timeout = atoi(p);
break;
-#endif /* _FFR_PHMAP_TIMEOUT */
case 'S':
map->map_spacesub = *++p;
@@ -4529,14 +4849,20 @@ ph_map_parseargs(map, args)
pmap->ph_servers = p;
break;
- case 'v': /* fields to search for */
+ case 'v':
+ sm_syslog(LOG_WARNING, NULL,
+ "ph_map_parseargs: WARNING: -v option will be removed in a future release - please use -k instead");
+ /* intentional fallthrough for backward compatibility */
+ /* FALLTHROUGH */
+
+ case 'k': /* fields to search for */
while (isascii(*++p) && isspace(*p))
continue;
pmap->ph_field_list = p;
break;
default:
- syserr("ph_map_parseargs: unknown option -%c\n", *p);
+ syserr("ph_map_parseargs: unknown option -%c", *p);
}
/* try to account for quoted strings */
@@ -4566,72 +4892,51 @@ ph_map_parseargs(map, args)
if (pmap->ph_field_list != NULL)
pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
- else
- pmap->ph_field_list = DEFAULT_PH_MAP_FIELDS;
if (pmap->ph_servers != NULL)
pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
else
{
syserr("ph_map_parseargs: -h flag is required");
- return FALSE;
+ return false;
}
map->map_db1 = (ARBPTR_T) pmap;
- return TRUE;
+ return true;
}
-#if _FFR_PHMAP_TIMEOUT
/*
** PH_MAP_CLOSE -- close the connection to the ph server
*/
-static void
-ph_map_safeclose(map)
+void
+ph_map_close(map)
MAP *map;
{
- int save_errno = errno;
PH_MAP_STRUCT *pmap;
pmap = (PH_MAP_STRUCT *)map->map_db1;
+ if (tTd(38, 9))
+ sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d",
+ map->map_mname, pmap->ph_fastclose);
- if (pmap->ph_sockfd != -1)
- {
- (void) close(pmap->ph_sockfd);
- pmap->ph_sockfd = -1;
- }
- if (pmap->ph_from_server != NULL)
- {
- (void) fclose(pmap->ph_from_server);
- pmap->ph_from_server = NULL;
- }
- if (pmap->ph_to_server != NULL)
+
+ if (pmap->ph != NULL)
{
- (void) fclose(pmap->ph_to_server);
- pmap->ph_to_server = NULL;
+ ph_set_sendhook(pmap->ph, NULL);
+ ph_set_recvhook(pmap->ph, NULL);
+ ph_close(pmap->ph, pmap->ph_fastclose);
}
- map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
- errno = save_errno;
-}
-
-void
-ph_map_close(map)
- MAP *map;
-{
- PH_MAP_STRUCT *pmap;
- pmap = (PH_MAP_STRUCT *)map->map_db1;
- (void) fprintf(pmap->ph_to_server, "quit\n");
- (void) fflush(pmap->ph_to_server);
- ph_map_safeclose(map);
+ map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
}
static jmp_buf PHTimeout;
/* ARGSUSED */
static void
-ph_timeout(sig)
- int sig;
+ph_timeout(unused)
+ int unused;
{
/*
** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
@@ -4642,25 +4947,30 @@ ph_timeout(sig)
errno = ETIMEDOUT;
longjmp(PHTimeout, 1);
}
-#else /* _FFR_PHMAP_TIMEOUT */
-/*
-** PH_MAP_CLOSE -- close the connection to the ph server
-*/
-void
-ph_map_close(map)
- MAP *map;
+static void
+ph_map_send_debug(text)
+ char *text;
{
- PH_MAP_STRUCT *pmap;
+ if (LogLevel > 9)
+ sm_syslog(LOG_NOTICE, CurEnv->e_id,
+ "ph_map_send_debug: ==> %s", text);
+ if (tTd(38, 20))
+ sm_dprintf("ph_map_send_debug: ==> %s\n", text);
+}
- pmap = (PH_MAP_STRUCT *)map->map_db1;
- CloseQi(pmap->ph_to_server, pmap->ph_from_server);
- pmap->ph_to_server = NULL;
- pmap->ph_from_server = NULL;
+static void
+ph_map_recv_debug(text)
+ char *text;
+{
+ if (LogLevel > 10)
+ sm_syslog(LOG_NOTICE, CurEnv->e_id,
+ "ph_map_recv_debug: <== %s", text);
+ if (tTd(38, 21))
+ sm_dprintf("ph_map_recv_debug: <== %s\n", text);
}
-#endif /* _FFR_PHMAP_TIMEOUT */
- /*
+/*
** PH_MAP_OPEN -- sub for opening PH map
*/
bool
@@ -4668,60 +4978,49 @@ ph_map_open(map, mode)
MAP *map;
int mode;
{
-#if !_FFR_PHMAP_TIMEOUT
- int save_errno = 0;
-#endif /* !_FFR_PHMAP_TIMEOUT */
- int j;
- char *hostlist, *tmp;
- QIR *server_data = NULL;
PH_MAP_STRUCT *pmap;
-#if _FFR_PHMAP_TIMEOUT
- register EVENT *ev = NULL;
-#endif /* _FFR_PHMAP_TIMEOUT */
+ register SM_EVENT *ev = NULL;
+ int save_errno = 0;
+ char *hostlist, *host;
if (tTd(38, 2))
- dprintf("ph_map_open(%s)\n", map->map_mname);
+ sm_dprintf("ph_map_open(%s)\n", map->map_mname);
mode &= O_ACCMODE;
if (mode != O_RDONLY)
{
/* issue a pseudo-error message */
-# ifdef ENOSYS
- errno = ENOSYS;
-# else /* ENOSYS */
-# ifdef EFTYPE
- errno = EFTYPE;
-# else /* EFTYPE */
- errno = ENXIO;
-# endif /* EFTYPE */
-# endif /* ENOSYS */
- return FALSE;
+ errno = SM_EMAPCANTWRITE;
+ return false;
}
if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
bitset(MF_DEFER, map->map_mflags))
{
if (tTd(9, 1))
- dprintf("ph_map_open(%s) => DEFERRED\n",
- map->map_mname);
+ sm_dprintf("ph_map_open(%s) => DEFERRED\n",
+ map->map_mname);
/*
- ** Unset MF_DEFER here so that map_lookup() returns
- ** a temporary failure using the bogus map and
- ** map->map_tapp instead of the default permanent error.
+ ** Unset MF_DEFER here so that map_lookup() returns
+ ** a temporary failure using the bogus map and
+ ** map->map_tapp instead of the default permanent error.
*/
map->map_mflags &= ~MF_DEFER;
- return FALSE;
+ return false;
}
pmap = (PH_MAP_STRUCT *)map->map_db1;
+ pmap->ph_fastclose = 0; /* refresh field for reopen */
+ /* try each host in the list */
hostlist = newstr(pmap->ph_servers);
- tmp = strtok(hostlist, " ");
- do
+ for (host = strtok(hostlist, " ");
+ host != NULL;
+ host = strtok(NULL, " "))
{
-#if _FFR_PHMAP_TIMEOUT
+ /* set timeout */
if (pmap->ph_timeout != 0)
{
if (setjmp(PHTimeout) != 0)
@@ -4730,67 +5029,33 @@ ph_map_open(map, mode)
if (LogLevel > 1)
sm_syslog(LOG_NOTICE, CurEnv->e_id,
"timeout connecting to PH server %.100s",
- tmp);
-# ifdef ETIMEDOUT
+ host);
errno = ETIMEDOUT;
-# else /* ETIMEDOUT */
- errno = EAGAIN;
-# endif /* ETIMEDOUT */
goto ph_map_open_abort;
}
- ev = setevent(pmap->ph_timeout, ph_timeout, 0);
+ ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
}
- if (!OpenQiSock(tmp, &(pmap->ph_sockfd)) &&
- !Sock2FILEs(pmap->ph_sockfd, &(pmap->ph_to_server),
- &(pmap->ph_from_server)) &&
- fprintf(pmap->ph_to_server, "id sendmail+phmap\n") >= 0 &&
- fflush(pmap->ph_to_server) == 0 &&
- (server_data = ReadQi(pmap->ph_from_server, &j)) != NULL &&
- server_data->code == 200)
+
+ /* open connection to server */
+ if (!ph_open(&(pmap->ph), host, PH_ROUNDROBIN|PH_DONTID,
+ ph_map_send_debug, ph_map_recv_debug) &&
+ !ph_id(pmap->ph, phmap_id))
{
if (ev != NULL)
- clrevent(ev);
- FreeQIR(server_data);
-#else /* _FFR_PHMAP_TIMEOUT */
- if (OpenQi(tmp, &(pmap->ph_to_server),
- &(pmap->ph_from_server)) >= 0)
- {
- if (fprintf(pmap->ph_to_server,
- "id sendmail+phmap\n") < 0 ||
- fflush(pmap->ph_to_server) != 0 ||
- (server_data = ReadQi(pmap->ph_from_server,
- &j)) == NULL ||
- server_data->code != 200)
- {
- save_errno = errno;
- CloseQi(pmap->ph_to_server,
- pmap->ph_from_server);
- continue;
- }
- if (server_data != NULL)
- FreeQIR(server_data);
-#endif /* _FFR_PHMAP_TIMEOUT */
- sm_free(hostlist);
- return TRUE;
+ sm_clrevent(ev);
+ sm_free(hostlist); /* XXX */
+ return true;
}
-#if _FFR_PHMAP_TIMEOUT
+
ph_map_open_abort:
- if (ev != NULL)
- clrevent(ev);
- ph_map_safeclose(map);
- if (server_data != NULL)
- {
- FreeQIR(server_data);
- server_data = NULL;
- }
-#else /* _FFR_PHMAP_TIMEOUT */
save_errno = errno;
-#endif /* _FFR_PHMAP_TIMEOUT */
- } while (tmp = strtok(NULL, " "));
+ if (ev != NULL)
+ sm_clrevent(ev);
+ pmap->ph_fastclose = PH_FASTCLOSE;
+ ph_map_close(map);
+ errno = save_errno;
+ }
-#if !_FFR_PHMAP_TIMEOUT
- errno = save_errno;
-#endif /* !_FFR_PHMAP_TIMEOUT */
if (bitset(MF_NODEFER, map->map_mflags))
{
if (errno == 0)
@@ -4802,18 +5067,14 @@ ph_map_open(map, mode)
sm_syslog(LOG_NOTICE, CurEnv->e_id,
"ph_map_open: %s: cannot connect to PH server",
map->map_mname);
- sm_free(hostlist);
- return FALSE;
+ sm_free(hostlist); /* XXX */
+ return false;
}
/*
** PH_MAP_LOOKUP -- look up key from ph server
*/
-#if _FFR_PHMAP_TIMEOUT
-# define MAX_PH_FIELDS 20
-#endif /* _FFR_PHMAP_TIMEOUT */
-
char *
ph_map_lookup(map, key, args, pstat)
MAP *map;
@@ -4821,25 +5082,16 @@ ph_map_lookup(map, key, args, pstat)
char **args;
int *pstat;
{
- int j;
- size_t sz;
- char *tmp, *tmp2;
- char *message = NULL, *field = NULL, *fmtkey;
- QIR *server_data = NULL;
- QIR *qirp;
- char keybuf[MAXKEY + 1], fieldbuf[101];
-#if _FFR_PHMAP_TIMEOUT
- QIR *hold_data[MAX_PH_FIELDS];
- int hold_data_idx = 0;
- register EVENT *ev = NULL;
-#endif /* _FFR_PHMAP_TIMEOUT */
+ int i, save_errno = 0;
+ register SM_EVENT *ev = NULL;
PH_MAP_STRUCT *pmap;
+ char *value = NULL;
pmap = (PH_MAP_STRUCT *)map->map_db1;
*pstat = EX_OK;
-#if _FFR_PHMAP_TIMEOUT
+ /* set timeout */
if (pmap->ph_timeout != 0)
{
if (setjmp(PHTimeout) != 0)
@@ -4849,263 +5101,52 @@ ph_map_lookup(map, key, args, pstat)
sm_syslog(LOG_NOTICE, CurEnv->e_id,
"timeout during PH lookup of %.100s",
key);
-# ifdef ETIMEDOUT
errno = ETIMEDOUT;
-# else /* ETIMEDOUT */
- errno = 0;
-# endif /* ETIMEDOUT */
*pstat = EX_TEMPFAIL;
goto ph_map_lookup_abort;
}
- ev = setevent(pmap->ph_timeout, ph_timeout, 0);
+ ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
}
-#endif /* _FFR_PHMAP_TIMEOUT */
- /* check all relevant fields */
- tmp = pmap->ph_field_list;
- do
- {
-#if _FFR_PHMAP_TIMEOUT
- server_data = NULL;
-#endif /* _FFR_PHMAP_TIMEOUT */
- while (isascii(*tmp) && isspace(*tmp))
- tmp++;
- if (*tmp == '\0')
- break;
- sz = strcspn(tmp, " ") + 1;
- if (sz > sizeof fieldbuf)
- sz = sizeof fieldbuf;
- (void) strlcpy(fieldbuf, tmp, sz);
- field = fieldbuf;
- tmp += sz;
-
- (void) strlcpy(keybuf, key, sizeof keybuf);
- fmtkey = keybuf;
- if (strcmp(field, "alias") == 0)
- {
- /*
- ** for alias lookups, replace any punctuation
- ** characters with '-'
- */
-
- for (tmp2 = fmtkey; *tmp2 != '\0'; tmp2++)
- {
- if (isascii(*tmp2) && ispunct(*tmp2))
- *tmp2 = '-';
- }
- tmp2 = field;
- }
- else if (strcmp(field,"spacedname") == 0)
- {
- /*
- ** for "spaced" name lookups, replace any
- ** punctuation characters with a space
- */
-
- for (tmp2 = fmtkey; *tmp2 != '\0'; tmp2++)
- {
- if (isascii(*tmp2) && ispunct(*tmp2) &&
- *tmp2 != '*')
- *tmp2 = ' ';
- }
- tmp2 = &(field[6]);
- }
- else
- tmp2 = field;
-
- if (LogLevel > 9)
- sm_syslog(LOG_NOTICE, CurEnv->e_id,
- "ph_map_lookup: query %s=\"%s\" return email",
- tmp2, fmtkey);
- if (tTd(38, 20))
- dprintf("ph_map_lookup: query %s=\"%s\" return email\n",
- tmp2, fmtkey);
-
- j = 0;
-
- if (fprintf(pmap->ph_to_server, "query %s=%s return email\n",
- tmp2, fmtkey) < 0)
- message = "qi query command failed";
- else if (fflush(pmap->ph_to_server) != 0)
- message = "qi fflush failed";
- else if ((server_data = ReadQi(pmap->ph_from_server,
- &j)) == NULL)
- message = "ReadQi() returned NULL";
-
-#if _FFR_PHMAP_TIMEOUT
- if ((hold_data[hold_data_idx] = server_data) != NULL)
- {
- /* save pointer for later free() */
- hold_data_idx++;
- }
-#endif /* _FFR_PHMAP_TIMEOUT */
-
- if (server_data == NULL ||
- (server_data->code >= 400 &&
- server_data->code < 500))
- {
- /* temporary failure */
- *pstat = EX_TEMPFAIL;
-#if _FFR_PHMAP_TIMEOUT
- break;
-#else /* _FFR_PHMAP_TIMEOUT */
- if (server_data != NULL)
- {
- FreeQIR(server_data);
- server_data = NULL;
- }
- return NULL;
-#endif /* _FFR_PHMAP_TIMEOUT */
- }
-
- /*
- ** if we found a single match, break out.
- ** otherwise, try the next field.
- */
-
- if (j == 1)
- break;
-
- /*
- ** check for a single response which is an error:
- ** ReadQi() doesn't set j on error responses,
- ** but we should stop here instead of moving on if
- ** it happens (e.g., alias found but email field empty)
- */
-
- for (qirp = server_data;
- qirp != NULL && qirp->code < 0;
- qirp++)
- {
- if (tTd(38, 20))
- dprintf("ph_map_lookup: QIR: %d:%d:%d:%s\n",
- qirp->code, qirp->subcode, qirp->field,
- (qirp->message ? qirp->message
- : "[NULL]"));
- if (qirp->code <= -500)
- {
- j = 0;
- goto ph_map_lookup_abort;
- }
- }
-
-#if _FFR_PHMAP_TIMEOUT
- } while (*tmp != '\0' && hold_data_idx < MAX_PH_FIELDS);
-#else /* _FFR_PHMAP_TIMEOUT */
- } while (*tmp != '\0');
-#endif /* _FFR_PHMAP_TIMEOUT */
+ /* perform lookup */
+ i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
+ if (i == -1)
+ *pstat = EX_TEMPFAIL;
+ else if (i == PH_NOMATCH || i == PH_DATAERR)
+ *pstat = EX_UNAVAILABLE;
ph_map_lookup_abort:
-#if _FFR_PHMAP_TIMEOUT
if (ev != NULL)
- clrevent(ev);
+ sm_clrevent(ev);
/*
- ** Return EX_TEMPFAIL if the timer popped
+ ** Close the connection if the timer popped
** or we got a temporary PH error
*/
if (*pstat == EX_TEMPFAIL)
- ph_map_safeclose(map);
-
- /* if we didn't find a single match, bail out */
- if (*pstat == EX_OK && j != 1)
- *pstat = EX_UNAVAILABLE;
+ {
+ save_errno = errno;
+ pmap->ph_fastclose = PH_FASTCLOSE;
+ ph_map_close(map);
+ errno = save_errno;
+ }
if (*pstat == EX_OK)
{
- /*
- ** skip leading whitespace and chop at first address
- */
-
- for (tmp = server_data->message;
- isascii(*tmp) && isspace(*tmp);
- tmp++)
- continue;
-
- for (tmp2 = tmp; *tmp2 != '\0'; tmp2++)
- {
- if (isascii(*tmp2) && isspace(*tmp2))
- {
- *tmp2 = '\0';
- break;
- }
- }
-
if (tTd(38,20))
- dprintf("ph_map_lookup: %s => %s\n", key, tmp);
+ sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
if (bitset(MF_MATCHONLY, map->map_mflags))
- message = map_rewrite(map, key, strlen(key), NULL);
+ return map_rewrite(map, key, strlen(key), NULL);
else
- message = map_rewrite(map, tmp, strlen(tmp), args);
+ return map_rewrite(map, value, strlen(value), args);
}
- /*
- ** Deferred free() of returned server_data values
- ** the deferral is to avoid the risk of a free() being
- ** interrupted by the event timer. By now the timeout event
- ** has been cleared and none of the data is still in use.
- */
-
- while (--hold_data_idx >= 0)
- {
- if (hold_data[hold_data_idx] != NULL)
- FreeQIR(hold_data[hold_data_idx]);
- }
-
- if (*pstat == EX_OK)
- return message;
-
return NULL;
-#else /* _FFR_PHMAP_TIMEOUT */
- /* if we didn't find a single match, bail out */
- if (j != 1)
- {
- *pstat = EX_UNAVAILABLE;
- if (server_data != NULL)
- {
- FreeQIR(server_data);
- server_data = NULL;
- }
- return NULL;
- }
-
- /*
- ** skip leading whitespace and chop at first address
- */
-
- for (tmp = server_data->message;
- isascii(*tmp) && isspace(*tmp);
- tmp++)
- continue;
-
- for (tmp2 = tmp; *tmp2 != '\0'; tmp2++)
- {
- if (isascii(*tmp2) && isspace(*tmp2))
- {
- *tmp2 = '\0';
- break;
- }
- }
-
- if (tTd(38,20))
- dprintf("ph_map_lookup: %s => %s\n", key, tmp);
-
- if (bitset(MF_MATCHONLY, map->map_mflags))
- message = map_rewrite(map, key, strlen(key), NULL);
- else
- message = map_rewrite(map, tmp, strlen(tmp), args);
- if (server_data != NULL)
- {
- FreeQIR(server_data);
- server_data = NULL;
- }
- return message;
-#endif /* _FFR_PHMAP_TIMEOUT */
}
#endif /* PH_MAP */
- /*
+/*
** syslog map
*/
@@ -5165,56 +5206,56 @@ syslog_map_parseargs(map, args)
map->map_prio = LOG_INFO;
else
{
- if (strncasecmp("LOG_", priority, 4) == 0)
+ if (sm_strncasecmp("LOG_", priority, 4) == 0)
priority += 4;
#ifdef LOG_EMERG
- if (strcasecmp("EMERG", priority) == 0)
+ if (sm_strcasecmp("EMERG", priority) == 0)
map->map_prio = LOG_EMERG;
else
#endif /* LOG_EMERG */
#ifdef LOG_ALERT
- if (strcasecmp("ALERT", priority) == 0)
+ if (sm_strcasecmp("ALERT", priority) == 0)
map->map_prio = LOG_ALERT;
else
#endif /* LOG_ALERT */
#ifdef LOG_CRIT
- if (strcasecmp("CRIT", priority) == 0)
+ if (sm_strcasecmp("CRIT", priority) == 0)
map->map_prio = LOG_CRIT;
else
#endif /* LOG_CRIT */
#ifdef LOG_ERR
- if (strcasecmp("ERR", priority) == 0)
+ if (sm_strcasecmp("ERR", priority) == 0)
map->map_prio = LOG_ERR;
else
#endif /* LOG_ERR */
#ifdef LOG_WARNING
- if (strcasecmp("WARNING", priority) == 0)
+ if (sm_strcasecmp("WARNING", priority) == 0)
map->map_prio = LOG_WARNING;
else
#endif /* LOG_WARNING */
#ifdef LOG_NOTICE
- if (strcasecmp("NOTICE", priority) == 0)
+ if (sm_strcasecmp("NOTICE", priority) == 0)
map->map_prio = LOG_NOTICE;
else
#endif /* LOG_NOTICE */
#ifdef LOG_INFO
- if (strcasecmp("INFO", priority) == 0)
+ if (sm_strcasecmp("INFO", priority) == 0)
map->map_prio = LOG_INFO;
else
#endif /* LOG_INFO */
#ifdef LOG_DEBUG
- if (strcasecmp("DEBUG", priority) == 0)
+ if (sm_strcasecmp("DEBUG", priority) == 0)
map->map_prio = LOG_DEBUG;
else
#endif /* LOG_DEBUG */
{
- syserr("syslog_map_parseargs: Unknown priority %s\n",
+ syserr("syslog_map_parseargs: Unknown priority %s",
priority);
- return FALSE;
+ return false;
}
}
- return TRUE;
+ return true;
}
/*
@@ -5233,7 +5274,7 @@ syslog_map_lookup(map, string, args, statp)
if (ptr != NULL)
{
if (tTd(38, 20))
- dprintf("syslog_map_lookup(%s (priority %d): %s\n",
+ sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
map->map_mname, map->map_prio, ptr);
sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
@@ -5243,11 +5284,11 @@ syslog_map_lookup(map, string, args, statp)
return "";
}
- /*
+/*
** HESIOD Modules
*/
-#ifdef HESIOD
+#if HESIOD
bool
hes_map_open(map, mode)
@@ -5255,32 +5296,24 @@ hes_map_open(map, mode)
int mode;
{
if (tTd(38, 2))
- dprintf("hes_map_open(%s, %s, %d)\n",
+ sm_dprintf("hes_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
if (mode != O_RDONLY)
{
/* issue a pseudo-error message */
-# ifdef ENOSYS
- errno = ENOSYS;
-# else /* ENOSYS */
-# ifdef EFTYPE
- errno = EFTYPE;
-# else /* EFTYPE */
- errno = ENXIO;
-# endif /* EFTYPE */
-# endif /* ENOSYS */
- return FALSE;
+ errno = SM_EMAPCANTWRITE;
+ return false;
}
# ifdef HESIOD_INIT
if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
- return TRUE;
+ return true;
if (!bitset(MF_OPTIONAL, map->map_mflags))
syserr("421 4.0.0 cannot initialize Hesiod map (%s)",
- errstring(errno));
- return FALSE;
+ sm_errstring(errno));
+ return false;
# else /* HESIOD_INIT */
if (hes_error() == HES_ER_UNINIT)
hes_init();
@@ -5288,13 +5321,13 @@ hes_map_open(map, mode)
{
case HES_ER_OK:
case HES_ER_NOTFOUND:
- return TRUE;
+ return true;
}
if (!bitset(MF_OPTIONAL, map->map_mflags))
syserr("421 4.0.0 cannot initialize Hesiod map (%d)", hes_error());
- return FALSE;
+ return false;
# endif /* HESIOD_INIT */
}
@@ -5308,7 +5341,7 @@ hes_map_lookup(map, name, av, statp)
char **hp;
if (tTd(38, 20))
- dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
+ sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
if (name[0] == '\\')
{
@@ -5323,7 +5356,7 @@ hes_map_lookup(map, name, av, statp)
else
np = xalloc(strlen(name) + 2);
np[0] = '\\';
- (void) strlcpy(&np[1], name, (sizeof nbuf) - 1);
+ (void) sm_strlcpy(&np[1], name, (sizeof nbuf) - 1);
# ifdef HESIOD_INIT
hp = hesiod_resolve(HesiodContext, np, map->map_file);
# else /* HESIOD_INIT */
@@ -5331,7 +5364,7 @@ hes_map_lookup(map, name, av, statp)
# endif /* HESIOD_INIT */
save_errno = errno;
if (np != nbuf)
- sm_free(np);
+ sm_free(np); /* XXX */
errno = save_errno;
}
else
@@ -5351,9 +5384,9 @@ hes_map_lookup(map, name, av, statp)
*statp = EX_NOTFOUND;
break;
case ECONNREFUSED:
- case EMSGSIZE:
*statp = EX_TEMPFAIL;
break;
+ case EMSGSIZE:
case ENOMEM:
default:
*statp = EX_UNAVAILABLE;
@@ -5394,8 +5427,29 @@ hes_map_lookup(map, name, av, statp)
return map_rewrite(map, hp[0], strlen(hp[0]), av);
}
+/*
+** HES_MAP_CLOSE -- free the Hesiod context
+*/
+
+void
+hes_map_close(map)
+ MAP *map;
+{
+ if (tTd(38, 20))
+ sm_dprintf("hes_map_close(%s)\n", map->map_file);
+
+# ifdef HESIOD_INIT
+ /* Free the hesiod context */
+ if (HesiodContext != NULL)
+ {
+ hesiod_end(HesiodContext);
+ HesiodContext = NULL;
+ }
+# endif /* HESIOD_INIT */
+}
+
#endif /* HESIOD */
- /*
+/*
** NeXT NETINFO Modules
*/
@@ -5414,7 +5468,7 @@ ni_map_open(map, mode)
int mode;
{
if (tTd(38, 2))
- dprintf("ni_map_open(%s, %s, %d)\n",
+ sm_dprintf("ni_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
mode &= O_ACCMODE;
@@ -5424,10 +5478,14 @@ ni_map_open(map, mode)
if (map->map_valcolnm == NULL)
map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
- if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
- map->map_coldelim = ',';
-
- return TRUE;
+ if (map->map_coldelim == '\0')
+ {
+ if (bitset(MF_ALIAS, map->map_mflags))
+ map->map_coldelim = ',';
+ else if (bitset(MF_FILECLASS, map->map_mflags))
+ map->map_coldelim = ' ';
+ }
+ return true;
}
@@ -5446,7 +5504,7 @@ ni_map_lookup(map, name, av, statp)
char *propval;
if (tTd(38, 20))
- dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
+ sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
propval = ni_propval(map->map_file, map->map_keycolnm, name,
map->map_valcolnm, map->map_coldelim);
@@ -5454,11 +5512,14 @@ ni_map_lookup(map, name, av, statp)
if (propval == NULL)
return NULL;
- if (bitset(MF_MATCHONLY, map->map_mflags))
- res = map_rewrite(map, name, strlen(name), NULL);
- else
- res = map_rewrite(map, propval, strlen(propval), av);
- sm_free(propval);
+ SM_TRY
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ res = map_rewrite(map, name, strlen(name), NULL);
+ else
+ res = map_rewrite(map, propval, strlen(propval), av);
+ SM_FINALLY
+ sm_free(propval);
+ SM_END_TRY
return res;
}
@@ -5474,12 +5535,12 @@ ni_getcanonname(name, hbsize, statp)
char nbuf[MAXNAME + 1];
if (tTd(38, 20))
- dprintf("ni_getcanonname(%s)\n", name);
+ sm_dprintf("ni_getcanonname(%s)\n", name);
- if (strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
+ if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
{
*statp = EX_UNAVAILABLE;
- return FALSE;
+ return false;
}
(void) shorten_hostname(nbuf);
@@ -5487,7 +5548,7 @@ ni_getcanonname(name, hbsize, statp)
if (strchr(nbuf, '.'))
{
*statp = EX_NOHOST;
- return FALSE;
+ return false;
}
/* Do the search */
@@ -5496,211 +5557,25 @@ ni_getcanonname(name, hbsize, statp)
if (vptr == NULL)
{
*statp = EX_NOHOST;
- return FALSE;
+ return false;
}
/* Only want the first machine name */
if ((ptr = strchr(vptr, '\n')) != NULL)
*ptr = '\0';
- if (hbsize >= strlen(vptr))
+ if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
{
- (void) strlcpy(name, vptr, hbsize);
sm_free(vptr);
- *statp = EX_OK;
- return TRUE;
+ *statp = EX_UNAVAILABLE;
+ return true;
}
- *statp = EX_UNAVAILABLE;
sm_free(vptr);
- return FALSE;
-}
-
-
-/*
-** NI_PROPVAL -- NetInfo property value lookup routine
-**
-** Parameters:
-** keydir -- the NetInfo directory name in which to search
-** for the key.
-** keyprop -- the name of the property in which to find the
-** property we are interested. Defaults to "name".
-** keyval -- the value for which we are really searching.
-** valprop -- the property name for the value in which we
-** are interested.
-** sepchar -- if non-nil, this can be multiple-valued, and
-** we should return a string separated by this
-** character.
-**
-** Returns:
-** NULL -- if:
-** 1. the directory is not found
-** 2. the property name is not found
-** 3. the property contains multiple values
-** 4. some error occurred
-** else -- the value of the lookup.
-**
-** Example:
-** To search for an alias value, use:
-** ni_propval("/aliases", "name", aliasname, "members", ',')
-**
-** Notes:
-** Caller should free the return value of ni_proval
-*/
-
-# include <netinfo/ni.h>
-
-# define LOCAL_NETINFO_DOMAIN "."
-# define PARENT_NETINFO_DOMAIN ".."
-# define MAX_NI_LEVELS 256
-
-char *
-ni_propval(keydir, keyprop, keyval, valprop, sepchar)
- char *keydir;
- char *keyprop;
- char *keyval;
- char *valprop;
- int sepchar;
-{
- char *propval = NULL;
- int i;
- int j, alen, l;
- void *ni = NULL;
- void *lastni = NULL;
- ni_status nis;
- ni_id nid;
- ni_namelist ninl;
- register char *p;
- char keybuf[1024];
-
- /*
- ** Create the full key from the two parts.
- **
- ** Note that directory can end with, e.g., "name=" to specify
- ** an alternate search property.
- */
-
- i = strlen(keydir) + strlen(keyval) + 2;
- if (keyprop != NULL)
- i += strlen(keyprop) + 1;
- if (i >= sizeof keybuf)
- return NULL;
- (void) strlcpy(keybuf, keydir, sizeof keybuf);
- (void) strlcat(keybuf, "/", sizeof keybuf);
- if (keyprop != NULL)
- {
- (void) strlcat(keybuf, keyprop, sizeof keybuf);
- (void) strlcat(keybuf, "=", sizeof keybuf);
- }
- (void) strlcat(keybuf, keyval, sizeof keybuf);
-
- if (tTd(38, 21))
- dprintf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n",
- keydir, keyprop, keyval, valprop, sepchar, keybuf);
- /*
- ** If the passed directory and property name are found
- ** in one of netinfo domains we need to search (starting
- ** from the local domain moving all the way back to the
- ** root domain) set propval to the property's value
- ** and return it.
- */
-
- for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++)
- {
- if (i == 0)
- {
- nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni);
- if (tTd(38, 20))
- dprintf("ni_open(LOCAL) = %d\n", nis);
- }
- else
- {
- if (lastni != NULL)
- ni_free(lastni);
- lastni = ni;
- nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni);
- if (tTd(38, 20))
- dprintf("ni_open(PARENT) = %d\n", nis);
- }
-
- /*
- ** Don't bother if we didn't get a handle on a
- ** proper domain. This is not necessarily an error.
- ** We would get a positive ni_status if, for instance
- ** we never found the directory or property and tried
- ** to open the parent of the root domain!
- */
-
- if (nis != 0)
- break;
-
- /*
- ** Find the path to the server information.
- */
-
- if (ni_pathsearch(ni, &nid, keybuf) != 0)
- continue;
-
- /*
- ** Find associated value information.
- */
-
- if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0)
- continue;
-
- if (tTd(38, 20))
- dprintf("ni_lookupprop: len=%d\n",
- ninl.ni_namelist_len);
-
- /*
- ** See if we have an acceptable number of values.
- */
-
- if (ninl.ni_namelist_len <= 0)
- continue;
-
- if (sepchar == '\0' && ninl.ni_namelist_len > 1)
- {
- ni_namelist_free(&ninl);
- continue;
- }
-
- /*
- ** Calculate number of bytes needed and build result
- */
-
- alen = 1;
- for (j = 0; j < ninl.ni_namelist_len; j++)
- alen += strlen(ninl.ni_namelist_val[j]) + 1;
- propval = p = xalloc(alen);
- for (j = 0; j < ninl.ni_namelist_len; j++)
- {
- (void) strlcpy(p, ninl.ni_namelist_val[j], alen);
- l = strlen(p);
- p += l;
- *p++ = sepchar;
- alen -= l + 1;
- }
- *--p = '\0';
-
- ni_namelist_free(&ninl);
- }
-
- /*
- ** Clean up.
- */
-
- if (ni != NULL)
- ni_free(ni);
- if (lastni != NULL && ni != lastni)
- ni_free(lastni);
- if (tTd(38, 20))
- dprintf("ni_propval returns: '%s'\n", propval);
-
- return propval;
+ *statp = EX_OK;
+ return false;
}
-
#endif /* NETINFO */
- /*
+/*
** TEXT (unindexed text file) Modules
**
** This code donated by Sun Microsystems.
@@ -5722,28 +5597,28 @@ text_map_open(map, mode)
int i;
if (tTd(38, 2))
- dprintf("text_map_open(%s, %s, %d)\n",
+ sm_dprintf("text_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
mode &= O_ACCMODE;
if (mode != O_RDONLY)
{
errno = EPERM;
- return FALSE;
+ return false;
}
if (*map->map_file == '\0')
{
syserr("text map \"%s\": file name required",
map->map_mname);
- return FALSE;
+ return false;
}
if (map->map_file[0] != '/')
{
syserr("text map \"%s\": file name must be fully qualified",
map->map_mname);
- return FALSE;
+ return false;
}
sff = SFF_ROOTOK|SFF_REGONLY;
@@ -5758,12 +5633,12 @@ text_map_open(map, mode)
/* cannot open this map */
if (tTd(38, 2))
- dprintf("\tunsafe map file: %d\n", i);
+ sm_dprintf("\tunsafe map file: %d\n", i);
errno = save_errno;
if (!bitset(MF_OPTIONAL, map->map_mflags))
syserr("text map \"%s\": unsafe map file %s",
map->map_mname, map->map_file);
- return FALSE;
+ return false;
}
if (map->map_keycolnm == NULL)
@@ -5775,7 +5650,7 @@ text_map_open(map, mode)
syserr("text map \"%s\", file %s: -k should specify a number, not %s",
map->map_mname, map->map_file,
map->map_keycolnm);
- return FALSE;
+ return false;
}
map->map_keycolno = atoi(map->map_keycolnm);
}
@@ -5789,23 +5664,23 @@ text_map_open(map, mode)
syserr("text map \"%s\", file %s: -v should specify a number, not %s",
map->map_mname, map->map_file,
map->map_valcolnm);
- return FALSE;
+ return false;
}
map->map_valcolno = atoi(map->map_valcolnm);
}
if (tTd(38, 2))
{
- dprintf("text_map_open(%s, %s): delimiter = ",
+ sm_dprintf("text_map_open(%s, %s): delimiter = ",
map->map_mname, map->map_file);
if (map->map_coldelim == '\0')
- dprintf("(white space)\n");
+ sm_dprintf("(white space)\n");
else
- dprintf("%c\n", map->map_coldelim);
+ sm_dprintf("%c\n", map->map_coldelim);
}
map->map_sff = sff;
- return TRUE;
+ return true;
}
@@ -5823,7 +5698,7 @@ text_map_lookup(map, name, av, statp)
char *vp;
auto int vsize;
int buflen;
- FILE *f;
+ SM_FILE_T *f;
char delim;
int key_idx;
bool found_it;
@@ -5832,13 +5707,13 @@ text_map_lookup(map, name, av, statp)
char linebuf[MAXLINE];
char buf[MAXNAME + 1];
- found_it = FALSE;
+ found_it = false;
if (tTd(38, 20))
- dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name);
+ sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name);
buflen = strlen(name);
if (buflen > sizeof search_key - 1)
- buflen = sizeof search_key - 1;
+ buflen = sizeof search_key - 1; /* XXX just cut if off? */
memmove(search_key, name, buflen);
search_key[buflen] = '\0';
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
@@ -5853,7 +5728,7 @@ text_map_lookup(map, name, av, statp)
}
key_idx = map->map_keycolno;
delim = map->map_coldelim;
- while (fgets(linebuf, MAXLINE, f) != NULL)
+ while (sm_io_fgets(f, SM_TIME_DEFAULT, linebuf, MAXLINE) != NULL)
{
char *p;
@@ -5864,13 +5739,13 @@ text_map_lookup(map, name, av, statp)
if (p != NULL)
*p = '\0';
p = get_column(linebuf, key_idx, delim, buf, sizeof buf);
- if (p != NULL && strcasecmp(search_key, p) == 0)
+ if (p != NULL && sm_strcasecmp(search_key, p) == 0)
{
- found_it = TRUE;
+ found_it = true;
break;
}
}
- (void) fclose(f);
+ (void) sm_io_close(f, SM_TIME_DEFAULT);
if (!found_it)
{
*statp = EX_NOTFOUND;
@@ -5902,30 +5777,31 @@ text_getcanonname(name, hbsize, statp)
{
bool found;
char *dot;
- FILE *f;
+ SM_FILE_T *f;
char linebuf[MAXLINE];
char cbuf[MAXNAME + 1];
char nbuf[MAXNAME + 1];
if (tTd(38, 20))
- dprintf("text_getcanonname(%s)\n", name);
+ sm_dprintf("text_getcanonname(%s)\n", name);
- if (strlen(name) >= (SIZE_T) sizeof nbuf)
+ if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
{
*statp = EX_UNAVAILABLE;
- return FALSE;
+ return false;
}
- (void) strlcpy(nbuf, name, sizeof nbuf);
dot = shorten_hostname(nbuf);
- f = fopen(HostsFile, "r");
+ f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
+ NULL);
if (f == NULL)
{
*statp = EX_UNAVAILABLE;
- return FALSE;
+ return false;
}
- found = FALSE;
- while (!found && fgets(linebuf, MAXLINE, f) != NULL)
+ found = false;
+ while (!found &&
+ sm_io_fgets(f, SM_TIME_DEFAULT, linebuf, MAXLINE) != NULL)
{
char *p = strpbrk(linebuf, "#\n");
@@ -5935,23 +5811,22 @@ text_getcanonname(name, hbsize, statp)
found = extract_canonname(nbuf, dot, linebuf,
cbuf, sizeof cbuf);
}
- (void) fclose(f);
+ (void) sm_io_close(f, SM_TIME_DEFAULT);
if (!found)
{
*statp = EX_NOHOST;
- return FALSE;
+ return false;
}
- if ((SIZE_T) hbsize >= strlen(cbuf))
+ if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
{
- (void) strlcpy(name, cbuf, hbsize);
- *statp = EX_OK;
- return TRUE;
+ *statp = EX_UNAVAILABLE;
+ return false;
}
- *statp = EX_UNAVAILABLE;
- return FALSE;
+ *statp = EX_OK;
+ return true;
}
- /*
+/*
** STAB (Symbol Table) Modules
*/
@@ -5971,7 +5846,7 @@ stab_map_lookup(map, name, av, pstat)
register STAB *s;
if (tTd(38, 20))
- dprintf("stab_lookup(%s, %s)\n",
+ sm_dprintf("stab_lookup(%s, %s)\n",
map->map_mname, name);
s = stab(name, ST_ALIAS, ST_FIND);
@@ -6012,19 +5887,19 @@ stab_map_open(map, mode)
register MAP *map;
int mode;
{
- FILE *af;
+ SM_FILE_T *af;
long sff;
struct stat st;
if (tTd(38, 2))
- dprintf("stab_map_open(%s, %s, %d)\n",
+ sm_dprintf("stab_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
mode &= O_ACCMODE;
if (mode != O_RDONLY)
{
errno = EPERM;
- return FALSE;
+ return false;
}
sff = SFF_ROOTOK|SFF_REGONLY;
@@ -6034,16 +5909,16 @@ stab_map_open(map, mode)
sff |= SFF_SAFEDIRPATH;
af = safefopen(map->map_file, O_RDONLY, 0444, sff);
if (af == NULL)
- return FALSE;
- readaliases(map, af, FALSE, FALSE);
+ return false;
+ readaliases(map, af, false, false);
- if (fstat(fileno(af), &st) >= 0)
+ if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
map->map_mtime = st.st_mtime;
- (void) fclose(af);
+ (void) sm_io_close(af, SM_TIME_DEFAULT);
- return TRUE;
+ return true;
}
- /*
+/*
** Implicit Modules
**
** Tries several types. For back compatibility of aliases.
@@ -6062,14 +5937,14 @@ impl_map_lookup(map, name, av, pstat)
int *pstat;
{
if (tTd(38, 20))
- dprintf("impl_map_lookup(%s, %s)\n",
+ sm_dprintf("impl_map_lookup(%s, %s)\n",
map->map_mname, name);
-#ifdef NEWDB
+#if NEWDB
if (bitset(MF_IMPL_HASH, map->map_mflags))
return db_map_lookup(map, name, av, pstat);
#endif /* NEWDB */
-#ifdef NDBM
+#if NDBM
if (bitset(MF_IMPL_NDBM, map->map_mflags))
return ndbm_map_lookup(map, name, av, pstat);
#endif /* NDBM */
@@ -6087,13 +5962,13 @@ impl_map_store(map, lhs, rhs)
char *rhs;
{
if (tTd(38, 12))
- dprintf("impl_map_store(%s, %s, %s)\n",
+ sm_dprintf("impl_map_store(%s, %s, %s)\n",
map->map_mname, lhs, rhs);
-#ifdef NEWDB
+#if NEWDB
if (bitset(MF_IMPL_HASH, map->map_mflags))
db_map_store(map, lhs, rhs);
#endif /* NEWDB */
-#ifdef NDBM
+#if NDBM
if (bitset(MF_IMPL_NDBM, map->map_mflags))
ndbm_map_store(map, lhs, rhs);
#endif /* NDBM */
@@ -6110,27 +5985,27 @@ impl_map_open(map, mode)
int mode;
{
if (tTd(38, 2))
- dprintf("impl_map_open(%s, %s, %d)\n",
+ sm_dprintf("impl_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
mode &= O_ACCMODE;
-#ifdef NEWDB
+#if NEWDB
map->map_mflags |= MF_IMPL_HASH;
if (hash_map_open(map, mode))
{
# ifdef NDBM_YP_COMPAT
if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
# endif /* NDBM_YP_COMPAT */
- return TRUE;
+ return true;
}
else
map->map_mflags &= ~MF_IMPL_HASH;
#endif /* NEWDB */
-#ifdef NDBM
+#if NDBM
map->map_mflags |= MF_IMPL_NDBM;
if (ndbm_map_open(map, mode))
{
- return TRUE;
+ return true;
}
else
map->map_mflags &= ~MF_IMPL_NDBM;
@@ -6149,7 +6024,7 @@ impl_map_open(map, mode)
if (mode == O_RDONLY)
return stab_map_open(map, mode);
else
- return FALSE;
+ return false;
}
@@ -6162,9 +6037,9 @@ impl_map_close(map)
MAP *map;
{
if (tTd(38, 9))
- dprintf("impl_map_close(%s, %s, %lx)\n",
+ sm_dprintf("impl_map_close(%s, %s, %lx)\n",
map->map_mname, map->map_file, map->map_mflags);
-#ifdef NEWDB
+#if NEWDB
if (bitset(MF_IMPL_HASH, map->map_mflags))
{
db_map_close(map);
@@ -6172,7 +6047,7 @@ impl_map_close(map)
}
#endif /* NEWDB */
-#ifdef NDBM
+#if NDBM
if (bitset(MF_IMPL_NDBM, map->map_mflags))
{
ndbm_map_close(map);
@@ -6180,7 +6055,7 @@ impl_map_close(map)
}
#endif /* NDBM */
}
- /*
+/*
** User map class.
**
** Provides access to the system password file.
@@ -6198,48 +6073,40 @@ user_map_open(map, mode)
int mode;
{
if (tTd(38, 2))
- dprintf("user_map_open(%s, %d)\n",
+ sm_dprintf("user_map_open(%s, %d)\n",
map->map_mname, mode);
mode &= O_ACCMODE;
if (mode != O_RDONLY)
{
/* issue a pseudo-error message */
-#ifdef ENOSYS
- errno = ENOSYS;
-#else /* ENOSYS */
-# ifdef EFTYPE
- errno = EFTYPE;
-# else /* EFTYPE */
- errno = ENXIO;
-# endif /* EFTYPE */
-#endif /* ENOSYS */
- return FALSE;
+ errno = SM_EMAPCANTWRITE;
+ return false;
}
if (map->map_valcolnm == NULL)
/* EMPTY */
/* nothing */ ;
- else if (strcasecmp(map->map_valcolnm, "name") == 0)
+ else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
map->map_valcolno = 1;
- else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
+ else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
map->map_valcolno = 2;
- else if (strcasecmp(map->map_valcolnm, "uid") == 0)
+ else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
map->map_valcolno = 3;
- else if (strcasecmp(map->map_valcolnm, "gid") == 0)
+ else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
map->map_valcolno = 4;
- else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
+ else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
map->map_valcolno = 5;
- else if (strcasecmp(map->map_valcolnm, "dir") == 0)
+ else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
map->map_valcolno = 6;
- else if (strcasecmp(map->map_valcolnm, "shell") == 0)
+ else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
map->map_valcolno = 7;
else
{
syserr("User map %s: unknown column name %s",
map->map_mname, map->map_valcolnm);
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
@@ -6255,15 +6122,15 @@ user_map_lookup(map, key, av, statp)
char **av;
int *statp;
{
- struct passwd *pw;
auto bool fuzzy;
+ SM_MBDB_T user;
if (tTd(38, 20))
- dprintf("user_map_lookup(%s, %s)\n",
+ sm_dprintf("user_map_lookup(%s, %s)\n",
map->map_mname, key);
- pw = finduser(key, &fuzzy);
- if (pw == NULL)
+ *statp = finduser(key, &fuzzy, &user);
+ if (*statp != EX_OK)
return NULL;
if (bitset(MF_MATCHONLY, map->map_mflags))
return map_rewrite(map, key, strlen(key), NULL);
@@ -6276,39 +6143,41 @@ user_map_lookup(map, key, av, statp)
{
case 0:
case 1:
- rwval = pw->pw_name;
+ rwval = user.mbdb_name;
break;
case 2:
- rwval = pw->pw_passwd;
+ rwval = "x"; /* passwd no longer supported */
break;
case 3:
- snprintf(buf, sizeof buf, "%d", (int) pw->pw_uid);
+ (void) sm_snprintf(buf, sizeof buf, "%d",
+ (int) user.mbdb_uid);
rwval = buf;
break;
case 4:
- snprintf(buf, sizeof buf, "%d", (int) pw->pw_gid);
+ (void) sm_snprintf(buf, sizeof buf, "%d",
+ (int) user.mbdb_gid);
rwval = buf;
break;
case 5:
- rwval = pw->pw_gecos;
+ rwval = user.mbdb_fullname;
break;
case 6:
- rwval = pw->pw_dir;
+ rwval = user.mbdb_homedir;
break;
case 7:
- rwval = pw->pw_shell;
+ rwval = user.mbdb_shell;
break;
}
return map_rewrite(map, rwval, strlen(rwval), av);
}
}
- /*
+/*
** Program map type.
**
** This provides access to arbitrary programs. It should be used
@@ -6334,14 +6203,14 @@ prog_map_lookup(map, name, av, statp)
char buf[MAXLINE];
if (tTd(38, 20))
- dprintf("prog_map_lookup(%s, %s) %s\n",
+ sm_dprintf("prog_map_lookup(%s, %s) %s\n",
map->map_mname, name, map->map_file);
i = 0;
argv[i++] = map->map_file;
if (map->map_rebuild != NULL)
{
- snprintf(buf, sizeof buf, "%s", map->map_rebuild);
+ (void) sm_strlcpy(buf, map->map_rebuild, sizeof buf);
for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
{
if (i >= MAXPV - 1)
@@ -6353,21 +6222,21 @@ prog_map_lookup(map, name, av, statp)
argv[i] = NULL;
if (tTd(38, 21))
{
- dprintf("prog_open:");
+ sm_dprintf("prog_open:");
for (i = 0; argv[i] != NULL; i++)
- dprintf(" %s", argv[i]);
- dprintf("\n");
+ sm_dprintf(" %s", argv[i]);
+ sm_dprintf("\n");
}
- (void) blocksignal(SIGCHLD);
+ (void) sm_blocksignal(SIGCHLD);
pid = prog_open(argv, &fd, CurEnv);
if (pid < 0)
{
if (!bitset(MF_OPTIONAL, map->map_mflags))
syserr("prog_map_lookup(%s) failed (%s) -- closing",
- map->map_mname, errstring(errno));
+ map->map_mname, sm_errstring(errno));
else if (tTd(38, 9))
- dprintf("prog_map_lookup(%s) failed (%s) -- closing",
- map->map_mname, errstring(errno));
+ sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
+ map->map_mname, sm_errstring(errno));
map->map_mflags &= ~(MF_VALID|MF_OPEN);
*statp = EX_OSFILE;
return NULL;
@@ -6375,15 +6244,15 @@ prog_map_lookup(map, name, av, statp)
i = read(fd, buf, sizeof buf - 1);
if (i < 0)
{
- syserr("prog_map_lookup(%s): read error %s\n",
- map->map_mname, errstring(errno));
+ syserr("prog_map_lookup(%s): read error %s",
+ map->map_mname, sm_errstring(errno));
rval = NULL;
}
else if (i == 0)
{
if (tTd(38, 20))
- dprintf("prog_map_lookup(%s): empty answer\n",
- map->map_mname);
+ sm_dprintf("prog_map_lookup(%s): empty answer\n",
+ map->map_mname);
rval = NULL;
}
else
@@ -6408,13 +6277,13 @@ prog_map_lookup(map, name, av, statp)
(void) close(fd);
status = waitfor(pid);
save_errno = errno;
- (void) releasesignal(SIGCHLD);
+ (void) sm_releasesignal(SIGCHLD);
errno = save_errno;
if (status == -1)
{
- syserr("prog_map_lookup(%s): wait error %s\n",
- map->map_mname, errstring(errno));
+ syserr("prog_map_lookup(%s): wait error %s",
+ map->map_mname, sm_errstring(errno));
*statp = EX_SOFTWARE;
rval = NULL;
}
@@ -6426,13 +6295,13 @@ prog_map_lookup(map, name, av, statp)
else
{
syserr("prog_map_lookup(%s): child died on signal %d",
- map->map_mname, status);
+ map->map_mname, status);
*statp = EX_UNAVAILABLE;
rval = NULL;
}
return rval;
}
- /*
+/*
** Sequenced map type.
**
** Tries each map in order until something matches, much like
@@ -6446,7 +6315,7 @@ prog_map_lookup(map, name, av, statp)
** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
**
** We don't need an explicit open, since all maps are
-** opened during startup, including underlying maps.
+** opened on demand.
*/
/*
@@ -6461,7 +6330,7 @@ seq_map_parse(map, ap)
int maxmap;
if (tTd(38, 2))
- dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
+ sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
maxmap = 0;
while (*ap != '\0')
{
@@ -6490,7 +6359,7 @@ seq_map_parse(map, ap)
syserr("Sequence map %s: unknown member map %s",
map->map_mname, ap);
}
- else if (maxmap == MAXMAPSTACK)
+ else if (maxmap >= MAXMAPSTACK)
{
syserr("Sequence map %s: too many member maps (%d max)",
map->map_mname, MAXMAPSTACK);
@@ -6502,10 +6371,9 @@ seq_map_parse(map, ap)
}
ap = p;
}
- return TRUE;
+ return true;
}
-
/*
** SWITCH_MAP_OPEN -- open a switched map
**
@@ -6527,19 +6395,19 @@ switch_map_open(map, mode)
char *maptype[MAXMAPSTACK];
if (tTd(38, 2))
- dprintf("switch_map_open(%s, %s, %d)\n",
+ sm_dprintf("switch_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
mode &= O_ACCMODE;
nmaps = switch_map_find(map->map_file, maptype, map->map_return);
if (tTd(38, 19))
{
- dprintf("\tswitch_map_find => %d\n", nmaps);
+ sm_dprintf("\tswitch_map_find => %d\n", nmaps);
for (mapno = 0; mapno < nmaps; mapno++)
- dprintf("\t\t%s\n", maptype[mapno]);
+ sm_dprintf("\t\t%s\n", maptype[mapno]);
}
if (nmaps <= 0 || nmaps > MAXMAPSTACK)
- return FALSE;
+ return false;
for (mapno = 0; mapno < nmaps; mapno++)
{
@@ -6548,8 +6416,8 @@ switch_map_open(map, mode)
if (maptype[mapno] == NULL)
continue;
- (void) snprintf(nbuf, sizeof nbuf, "%s.%s",
- map->map_mname, maptype[mapno]);
+ (void) sm_strlcpyn(nbuf, sizeof nbuf, 3,
+ map->map_mname, ".", maptype[mapno]);
s = stab(nbuf, ST_MAP, ST_FIND);
if (s == NULL)
{
@@ -6560,15 +6428,16 @@ switch_map_open(map, mode)
{
map->map_stack[mapno] = &s->s_map;
if (tTd(38, 4))
- dprintf("\tmap_stack[%d] = %s:%s\n",
- mapno, s->s_map.map_class->map_cname,
- nbuf);
+ sm_dprintf("\tmap_stack[%d] = %s:%s\n",
+ mapno,
+ s->s_map.map_class->map_cname,
+ nbuf);
}
}
- return TRUE;
+ return true;
}
-
+#if 0
/*
** SEQ_MAP_CLOSE -- close all underlying maps
*/
@@ -6580,7 +6449,7 @@ seq_map_close(map)
int mapno;
if (tTd(38, 9))
- dprintf("seq_map_close(%s)\n", map->map_mname);
+ sm_dprintf("seq_map_close(%s)\n", map->map_mname);
for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
{
@@ -6593,7 +6462,7 @@ seq_map_close(map)
mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
}
}
-
+#endif /* 0 */
/*
** SEQ_MAP_LOOKUP -- sequenced map lookup
@@ -6608,10 +6477,10 @@ seq_map_lookup(map, key, args, pstat)
{
int mapno;
int mapbit = 0x01;
- bool tempfail = FALSE;
+ bool tempfail = false;
if (tTd(38, 20))
- dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
+ sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
{
@@ -6638,7 +6507,7 @@ seq_map_lookup(map, key, args, pstat)
{
if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
return NULL;
- tempfail = TRUE;
+ tempfail = true;
}
else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
break;
@@ -6650,7 +6519,6 @@ seq_map_lookup(map, key, args, pstat)
return NULL;
}
-
/*
** SEQ_MAP_STORE -- sequenced map store
*/
@@ -6664,7 +6532,7 @@ seq_map_store(map, key, val)
int mapno;
if (tTd(38, 12))
- dprintf("seq_map_store(%s, %s, %s)\n",
+ sm_dprintf("seq_map_store(%s, %s, %s)\n",
map->map_mname, key, val);
for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
@@ -6680,7 +6548,7 @@ seq_map_store(map, key, val)
syserr("seq_map_store(%s, %s, %s): no writable map",
map->map_mname, key, val);
}
- /*
+/*
** NULL stubs
*/
@@ -6690,7 +6558,7 @@ null_map_open(map, mode)
MAP *map;
int mode;
{
- return TRUE;
+ return true;
}
/* ARGSUSED */
@@ -6722,7 +6590,6 @@ null_map_store(map, key, val)
return;
}
-
/*
** BOGUS stubs
*/
@@ -6740,11 +6607,11 @@ bogus_map_lookup(map, key, args, pstat)
MAPCLASS BogusMapClass =
{
- "bogus-map", NULL, 0,
- NULL, bogus_map_lookup, null_map_store,
- null_map_open, null_map_close,
+ "bogus-map", NULL, 0,
+ NULL, bogus_map_lookup, null_map_store,
+ null_map_open, null_map_close,
};
- /*
+/*
** MACRO modules
*/
@@ -6758,37 +6625,35 @@ macro_map_lookup(map, name, av, statp)
int mid;
if (tTd(38, 20))
- dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
+ sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
name == NULL ? "NULL" : name);
if (name == NULL ||
*name == '\0' ||
- (mid = macid(name, NULL)) == '\0')
+ (mid = macid(name)) == 0)
{
*statp = EX_CONFIG;
return NULL;
}
if (av[1] == NULL)
- define(mid, NULL, CurEnv);
+ macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
else
- define(mid, newstr(av[1]), CurEnv);
+ macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
*statp = EX_OK;
return "";
}
- /*
+/*
** REGEX modules
*/
-#ifdef MAP_REGEX
+#if MAP_REGEX
# include <regex.h>
# define DEFAULT_DELIM CONDELSE
-
# define END_OF_FIELDS -1
-
# define ERRBUF_SIZE 80
# define MAX_MATCH 32
@@ -6810,7 +6675,7 @@ parse_fields(s, ibuf, blen, nr_substrings)
{
register char *cp;
int i = 0;
- bool lastone = FALSE;
+ bool lastone = false;
blen--; /* for terminating END_OF_FIELDS */
cp = s;
@@ -6825,7 +6690,7 @@ parse_fields(s, ibuf, blen, nr_substrings)
}
if (*cp == '\0')
{
- lastone = TRUE;
+ lastone = true;
break;
}
}
@@ -6843,7 +6708,7 @@ parse_fields(s, ibuf, blen, nr_substrings)
}
else
{
- syserr("too many fields, %d max\n", blen);
+ syserr("too many fields, %d max", blen);
return -1;
}
s = ++cp;
@@ -6862,16 +6727,14 @@ regex_map_init(map, ap)
register char *p;
char *sub_param = NULL;
int pflags;
- static char defdstr[] = { (char)DEFAULT_DELIM, '\0' };
+ static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
if (tTd(38, 2))
- dprintf("regex_map_init: mapname '%s', args '%s'\n",
+ sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
map->map_mname, ap);
pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
-
p = ap;
-
map_p = (struct regex_map *) xnalloc(sizeof *map_p);
map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
@@ -6928,7 +6791,7 @@ regex_map_init(map, ap)
*p++ = '\0';
}
if (tTd(38, 3))
- dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
+ sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
{
@@ -6936,11 +6799,11 @@ regex_map_init(map, ap)
char errbuf[ERRBUF_SIZE];
(void) regerror(regerr, map_p->regex_pattern_buf,
- errbuf, ERRBUF_SIZE);
- syserr("pattern-compile-error: %s\n", errbuf);
- sm_free(map_p->regex_pattern_buf);
- sm_free(map_p);
- return FALSE;
+ errbuf, sizeof errbuf);
+ syserr("pattern-compile-error: %s", errbuf);
+ sm_free(map_p->regex_pattern_buf); /* XXX */
+ sm_free(map_p); /* XXX */
+ return false;
}
if (map->map_app != NULL)
@@ -6959,28 +6822,28 @@ regex_map_init(map, ap)
substrings = map_p->regex_pattern_buf->re_nsub + 1;
if (tTd(38, 3))
- dprintf("regex_map_init: nr of substrings %d\n",
+ sm_dprintf("regex_map_init: nr of substrings %d\n",
substrings);
if (substrings >= MAX_MATCH)
{
- syserr("too many substrings, %d max\n", MAX_MATCH);
- sm_free(map_p->regex_pattern_buf);
- sm_free(map_p);
- return FALSE;
+ syserr("too many substrings, %d max", MAX_MATCH);
+ sm_free(map_p->regex_pattern_buf); /* XXX */
+ sm_free(map_p); /* XXX */
+ return false;
}
if (sub_param != NULL && sub_param[0] != '\0')
{
/* optional parameter -sfields */
if (parse_fields(sub_param, fields,
MAX_MATCH + 1, substrings) == -1)
- return FALSE;
+ return false;
}
else
{
- /* set default fields */
int i;
+ /* set default fields */
for (i = 0; i < substrings; i++)
fields[i] = i;
fields[i] = END_OF_FIELDS;
@@ -6990,15 +6853,14 @@ regex_map_init(map, ap)
{
int *ip;
- dprintf("regex_map_init: subfields");
+ sm_dprintf("regex_map_init: subfields");
for (ip = fields; *ip != END_OF_FIELDS; ip++)
- dprintf(" %d", *ip);
- dprintf("\n");
+ sm_dprintf(" %d", *ip);
+ sm_dprintf("\n");
}
}
- map->map_db1 = (ARBPTR_T)map_p; /* dirty hack */
-
- return TRUE;
+ map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */
+ return true;
}
static char *
@@ -7029,9 +6891,9 @@ regex_map_lookup(map, name, av, statp)
{
char **cpp;
- dprintf("regex_map_lookup: key '%s'\n", name);
+ sm_dprintf("regex_map_lookup: key '%s'\n", name);
for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
- dprintf("regex_map_lookup: arg '%s'\n", *cpp);
+ sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
}
map_p = (struct regex_map *)(map->map_db1);
@@ -7042,7 +6904,7 @@ regex_map_lookup(map, name, av, statp)
{
/* option -n */
if (reg_res == REG_NOMATCH)
- return regex_map_rewrite(map, "", (size_t)0, av);
+ return regex_map_rewrite(map, "", (size_t) 0, av);
else
return NULL;
}
@@ -7054,9 +6916,9 @@ regex_map_lookup(map, name, av, statp)
/* option -s */
static char retbuf[MAXNAME];
int fields[MAX_MATCH + 1];
- bool first = TRUE;
+ bool first = true;
int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
- bool quotemode = FALSE, bslashmode = FALSE;
+ bool quotemode = false, bslashmode = false;
register char *dp, *sp;
char *endp, *ldp;
int *ip;
@@ -7088,8 +6950,7 @@ regex_map_lookup(map, name, av, statp)
}
}
else
- first = FALSE;
-
+ first = false;
if (*ip >= MAX_MATCH ||
pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
@@ -7104,40 +6965,40 @@ regex_map_lookup(map, name, av, statp)
if (bslashmode)
{
*dp++ = *sp;
- bslashmode = FALSE;
+ bslashmode = false;
}
else if (quotemode && *sp != '"' &&
*sp != '\\')
{
*dp++ = *sp;
}
- else switch(*dp++ = *sp)
+ else switch (*dp++ = *sp)
{
- case '\\':
- bslashmode = TRUE;
+ case '\\':
+ bslashmode = true;
break;
- case '(':
+ case '(':
cmntcnt++;
break;
- case ')':
+ case ')':
cmntcnt--;
break;
- case '<':
+ case '<':
anglecnt++;
break;
- case '>':
+ case '>':
anglecnt--;
break;
- case ' ':
+ case ' ':
spacecnt++;
break;
- case '"':
+ case '"':
quotemode = !quotemode;
break;
}
@@ -7160,10 +7021,10 @@ regex_map_lookup(map, name, av, statp)
return regex_map_rewrite(map, "", (size_t)0, av);
}
#endif /* MAP_REGEX */
- /*
+/*
** NSD modules
*/
-#ifdef MAP_NSD
+#if MAP_NSD
# include <ndbm.h>
# define _DATUM_DEFINED
@@ -7171,9 +7032,9 @@ regex_map_lookup(map, name, av, statp)
typedef struct ns_map_list
{
- ns_map_t *map;
- char *mapname;
- struct ns_map_list *next;
+ ns_map_t *map; /* XXX ns_ ? */
+ char *mapname;
+ struct ns_map_list *next;
} ns_map_list_t;
static ns_map_t *
@@ -7216,11 +7077,11 @@ nsd_map_lookup(map, name, av, statp)
char buf[MAXLINE];
if (tTd(38, 20))
- dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
+ sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
buflen = strlen(name);
if (buflen > sizeof keybuf - 1)
- buflen = sizeof keybuf - 1;
+ buflen = sizeof keybuf - 1; /* XXX simply cut off? */
memmove(keybuf, name, buflen);
keybuf[buflen] = '\0';
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
@@ -7230,7 +7091,7 @@ nsd_map_lookup(map, name, av, statp)
if (ns_map == NULL)
{
if (tTd(38, 20))
- dprintf("nsd_map_t_find failed\n");
+ sm_dprintf("nsd_map_t_find failed\n");
*statp = EX_UNAVAILABLE;
return NULL;
}
@@ -7274,19 +7135,19 @@ arith_map_lookup(map, name, av, statp)
{
long r;
long v[2];
- bool res = FALSE;
+ bool res = false;
bool boolres;
static char result[16];
char **cpp;
if (tTd(38, 2))
{
- dprintf("arith_map_lookup: key '%s'\n", name);
+ sm_dprintf("arith_map_lookup: key '%s'\n", name);
for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
- dprintf("arith_map_lookup: arg '%s'\n", *cpp);
+ sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
}
r = 0;
- boolres = FALSE;
+ boolres = false;
cpp = av;
*statp = EX_OK;
@@ -7295,15 +7156,15 @@ arith_map_lookup(map, name, av, statp)
** - no check is made whether they are really numbers
** - just ignores args after the second
*/
+
for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
v[r++] = strtol(*cpp, NULL, 0);
/* operator and (at least) two operands given? */
if (name != NULL && r == 2)
{
- switch(*name)
+ switch (*name)
{
-#if _FFR_ARITH
case '|':
r = v[0] | v[1];
break;
@@ -7317,8 +7178,6 @@ arith_map_lookup(map, name, av, statp)
return NULL;
r = v[0] % v[1];
break;
-#endif /* _FFR_ARITH */
-
case '+':
r = v[0] + v[1];
break;
@@ -7339,12 +7198,12 @@ arith_map_lookup(map, name, av, statp)
case 'l':
res = v[0] < v[1];
- boolres = TRUE;
+ boolres = true;
break;
case '=':
res = v[0] == v[1];
- boolres = TRUE;
+ boolres = true;
break;
default:
@@ -7357,9 +7216,10 @@ arith_map_lookup(map, name, av, statp)
return NULL;
}
if (boolres)
- snprintf(result, sizeof result, res ? "TRUE" : "FALSE");
+ (void) sm_snprintf(result, sizeof result,
+ res ? "TRUE" : "FALSE");
else
- snprintf(result, sizeof result, "%ld", r);
+ (void) sm_snprintf(result, sizeof result, "%ld", r);
return result;
}
*statp = EX_CONFIG;
diff --git a/contrib/sendmail/src/milter.c b/contrib/sendmail/src/milter.c
index 9782004..844a3ba 100644
--- a/contrib/sendmail/src/milter.c
+++ b/contrib/sendmail/src/milter.c
@@ -8,13 +8,14 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: milter.c,v 8.50.4.53 2001/08/15 02:01:03 ca Exp $";
-#endif /* ! lint */
+#include <sendmail.h>
-#if _FFR_MILTER
+SM_RCSID("@(#)$Id: milter.c,v 8.185 2001/11/21 02:21:15 gshapiro Exp $")
+
+#if MILTER
+# include <libmilter/mfapi.h>
+# include <libmilter/mfdef.h>
-# include <sendmail.h>
# include <errno.h>
# include <sys/time.h>
@@ -22,12 +23,10 @@ static char id[] = "@(#)$Id: milter.c,v 8.50.4.53 2001/08/15 02:01:03 ca Exp $";
# include <arpa/inet.h>
# endif /* NETINET || NETINET6 */
-# define SM_FD_SET FD_SET
-# define SM_FD_ISSET FD_ISSET
-# define SM_FD_SETSIZE FD_SETSIZE
+# include <sm/fdset.h>
static void milter_connect_timeout __P((void));
-static void milter_error __P((struct milter *));
+static void milter_error __P((struct milter *, ENVELOPE *));
static int milter_open __P((struct milter *, bool, ENVELOPE *));
static void milter_parse_timeouts __P((char *, struct milter *));
@@ -63,7 +62,7 @@ static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
!isascii(response[2]) || !isdigit(response[2])) \
{ \
if (response != NULL) \
- sm_free(response); \
+ sm_free(response); /* XXX */ \
response = newstr(default); \
} \
else \
@@ -75,7 +74,7 @@ static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
{ \
if (*ptr == '%' && *++ptr != '%') \
{ \
- sm_free(response); \
+ sm_free(response); /* XXX */ \
response = newstr(default); \
break; \
} \
@@ -89,16 +88,16 @@ static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
\
if (tTd(64, 5)) \
{ \
- dprintf(msg, dfname, errstring(save_errno)); \
- dprintf("\n"); \
+ sm_dprintf(msg, dfname, sm_errstring(save_errno)); \
+ sm_dprintf("\n"); \
} \
- if (LogLevel > 0) \
- sm_syslog(LOG_ERR, e->e_id, msg, dfname, errstring(save_errno)); \
- if (SuperSafe) \
+ if (MilterLogLevel > 0) \
+ sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \
+ if (SuperSafe == SAFE_REALLY) \
{ \
if (e->e_dfp != NULL) \
{ \
- (void) fclose(e->e_dfp); \
+ (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \
e->e_dfp = NULL; \
} \
e->e_flags &= ~EF_HAS_DF; \
@@ -113,73 +112,85 @@ static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
** routine -- routine name for debug/logging
** secs -- number of seconds in timeout
** write -- waiting to read or write?
+** started -- whether this is part of a previous sequence
**
** Assumes 'm' is a milter structure for the current socket.
*/
-# define MILTER_TIMEOUT(routine, secs, write) \
+# define MILTER_TIMEOUT(routine, secs, write, started) \
{ \
int ret; \
int save_errno; \
fd_set fds; \
struct timeval tv; \
\
- if (SM_FD_SETSIZE != 0 && m->mf_sock >= SM_FD_SETSIZE) \
+ if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \
{ \
if (tTd(64, 5)) \
- dprintf("%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
- routine, m->mf_name, m->mf_sock, SM_FD_SETSIZE); \
- if (LogLevel > 0) \
+ sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
+ (routine), m->mf_name, m->mf_sock, \
+ SM_FD_SETSIZE); \
+ if (MilterLogLevel > 0) \
sm_syslog(LOG_ERR, e->e_id, \
- "%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
- routine, m->mf_name, m->mf_sock, SM_FD_SETSIZE); \
- milter_error(m); \
+ "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \
+ m->mf_name, (routine), m->mf_sock, \
+ SM_FD_SETSIZE); \
+ milter_error(m, e); \
return NULL; \
} \
\
FD_ZERO(&fds); \
SM_FD_SET(m->mf_sock, &fds); \
- tv.tv_sec = secs; \
+ tv.tv_sec = (secs); \
tv.tv_usec = 0; \
ret = select(m->mf_sock + 1, \
- write ? NULL : &fds, \
- write ? &fds : NULL, \
+ (write) ? NULL : &fds, \
+ (write) ? &fds : NULL, \
NULL, &tv); \
\
switch (ret) \
{ \
case 0: \
if (tTd(64, 5)) \
- dprintf("%s(%s): timeout\n", routine, m->mf_name); \
- if (LogLevel > 0) \
- sm_syslog(LOG_ERR, e->e_id, "%s(%s): timeout\n", \
- routine, m->mf_name); \
- milter_error(m); \
+ sm_dprintf("milter_%s(%s): timeout\n", (routine), \
+ m->mf_name); \
+ if (MilterLogLevel > 0) \
+ sm_syslog(LOG_ERR, e->e_id, \
+ "Milter (%s): %s %s %s %s", \
+ m->mf_name, "timeout", \
+ started ? "during" : "before", \
+ "data", (routine)); \
+ milter_error(m, e); \
return NULL; \
\
case -1: \
save_errno = errno; \
if (tTd(64, 5)) \
- dprintf("%s(%s): select: %s\n", \
- routine, m->mf_name, errstring(save_errno)); \
- if (LogLevel > 0) \
+ sm_dprintf("milter_%s(%s): select: %s\n", (routine), \
+ m->mf_name, sm_errstring(save_errno)); \
+ if (MilterLogLevel > 0) \
+ { \
sm_syslog(LOG_ERR, e->e_id, \
- "%s(%s): select: %s\n", \
- routine, m->mf_name, errstring(save_errno)); \
- milter_error(m); \
+ "Milter (%s): select(%s): %s", \
+ m->mf_name, (routine), \
+ sm_errstring(save_errno)); \
+ } \
+ milter_error(m, e); \
return NULL; \
\
default: \
if (SM_FD_ISSET(m->mf_sock, &fds)) \
break; \
if (tTd(64, 5)) \
- dprintf("%s(%s): socket not ready\n", \
- routine, m->mf_name); \
- if (LogLevel > 0) \
+ sm_dprintf("milter_%s(%s): socket not ready\n", \
+ (routine), m->mf_name); \
+ if (MilterLogLevel > 0) \
+ { \
sm_syslog(LOG_ERR, e->e_id, \
- "%s(%s): socket not ready\n", \
- m->mf_name, routine); \
- milter_error(m); \
+ "Milter (%s): socket(%s) not ready", \
+ m->mf_name, (routine)); \
+ } \
+ milter_error(m, e); \
return NULL; \
} \
}
@@ -188,7 +199,7 @@ static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
** Low level functions
*/
- /*
+/*
** MILTER_READ -- read from a remote milter filter
**
** Parameters:
@@ -212,6 +223,7 @@ milter_sysread(m, buf, sz, to, e)
{
time_t readstart = 0;
ssize_t len, curl;
+ bool started = false;
curl = 0;
@@ -228,18 +240,22 @@ milter_sysread(m, buf, sz, to, e)
if (now - readstart >= to)
{
if (tTd(64, 5))
- dprintf("milter_read(%s): timeout before data read\n",
- m->mf_name);
- if (LogLevel > 0)
+ sm_dprintf("milter_read (%s): %s %s %s",
+ m->mf_name, "timeout",
+ started ? "during" : "before",
+ "data read");
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_read(%s): timeout before data read\n",
- m->mf_name);
- milter_error(m);
+ "Milter (%s): %s %s %s",
+ m->mf_name, "timeout",
+ started ? "during" : "before",
+ "data read");
+ milter_error(m, e);
return NULL;
}
to -= now - readstart;
readstart = now;
- MILTER_TIMEOUT("milter_read", to, FALSE);
+ MILTER_TIMEOUT("read", to, false, started);
}
len = read(m->mf_sock, buf + curl, sz - curl);
@@ -249,18 +265,19 @@ milter_sysread(m, buf, sz, to, e)
int save_errno = errno;
if (tTd(64, 5))
- dprintf("milter_read(%s): read returned %ld: %s\n",
+ sm_dprintf("milter_read(%s): read returned %ld: %s\n",
m->mf_name, (long) len,
- errstring(save_errno));
- if (LogLevel > 0)
+ sm_errstring(save_errno));
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_read(%s): read returned %ld: %s",
+ "Milter (%s): read returned %ld: %s",
m->mf_name, (long) len,
- errstring(save_errno));
- milter_error(m);
+ sm_errstring(save_errno));
+ milter_error(m, e);
return NULL;
}
+ started = true;
curl += len;
if (len == 0 || curl >= sz)
break;
@@ -270,13 +287,13 @@ milter_sysread(m, buf, sz, to, e)
if (curl != sz)
{
if (tTd(64, 5))
- dprintf("milter_read(%s): read returned %ld, expecting %ld\n",
+ sm_dprintf("milter_read(%s): cmd read returned %ld, expecting %ld\n",
m->mf_name, (long) curl, (long) sz);
- if (LogLevel > 0)
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_read(%s): read returned %ld, expecting %ld",
+ "milter_read(%s): cmd read returned %ld, expecting %ld",
m->mf_name, (long) curl, (long) sz);
- milter_error(m);
+ milter_error(m, e);
return NULL;
}
return buf;
@@ -314,13 +331,13 @@ milter_read(m, cmd, rlen, to, e)
if (now - readstart >= to)
{
if (tTd(64, 5))
- dprintf("milter_read(%s): timeout before data read\n",
+ sm_dprintf("milter_read(%s): timeout before data read\n",
m->mf_name);
- if (LogLevel > 0)
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_read(%s): timeout before data read\n",
+ "Milter read(%s): timeout before data read",
m->mf_name);
- milter_error(m);
+ milter_error(m, e);
return NULL;
}
to -= now - readstart;
@@ -332,40 +349,40 @@ milter_read(m, cmd, rlen, to, e)
expl = ntohl(i) - 1;
if (tTd(64, 25))
- dprintf("milter_read(%s): expecting %ld bytes\n",
+ sm_dprintf("milter_read(%s): expecting %ld bytes\n",
m->mf_name, (long) expl);
if (expl < 0)
{
if (tTd(64, 5))
- dprintf("milter_read(%s): read size %ld out of range\n",
+ sm_dprintf("milter_read(%s): read size %ld out of range\n",
m->mf_name, (long) expl);
- if (LogLevel > 0)
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
"milter_read(%s): read size %ld out of range",
m->mf_name, (long) expl);
- milter_error(m);
+ milter_error(m, e);
return NULL;
}
if (expl == 0)
return NULL;
- buf = (char *)xalloc(expl);
+ buf = (char *) xalloc(expl);
if (milter_sysread(m, buf, expl, to, e) == NULL)
{
- sm_free(buf);
+ sm_free(buf); /* XXX */
return NULL;
}
if (tTd(64, 50))
- dprintf("milter_read(%s): Returning %*s\n",
+ sm_dprintf("milter_read(%s): Returning %*s\n",
m->mf_name, (int) expl, buf);
*rlen = expl;
return buf;
}
- /*
+/*
** MILTER_WRITE -- write to a remote milter filter
**
** Parameters:
@@ -395,23 +412,24 @@ milter_write(m, cmd, buf, len, to, e)
ssize_t sl, i;
mi_int32 nl;
char data[MILTER_LEN_BYTES + 1];
+ bool started = false;
if (len < 0 || len > MILTER_CHUNK_SIZE)
{
if (tTd(64, 5))
- dprintf("milter_write(%s): length %ld out of range\n",
+ sm_dprintf("milter_write(%s): length %ld out of range\n",
m->mf_name, (long) len);
- if (LogLevel > 0)
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
"milter_write(%s): length %ld out of range",
m->mf_name, (long) len);
- milter_error(m);
+ milter_error(m, e);
return NULL;
}
if (tTd(64, 20))
- dprintf("milter_write(%s): cmd %c, len %ld\n",
- m->mf_name, cmd, (long) len);
+ sm_dprintf("milter_write(%s): cmd %c, len %ld\n",
+ m->mf_name, cmd, (long) len);
nl = htonl(len + 1); /* add 1 for the cmd char */
(void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES);
@@ -421,7 +439,7 @@ milter_write(m, cmd, buf, len, to, e)
if (to > 0)
{
writestart = curtime();
- MILTER_TIMEOUT("milter_write", to, TRUE);
+ MILTER_TIMEOUT("write", to, true, started);
}
/* use writev() instead to send the whole stuff at once? */
@@ -431,15 +449,15 @@ milter_write(m, cmd, buf, len, to, e)
int save_errno = errno;
if (tTd(64, 5))
- dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
- m->mf_name, cmd, (long) i, (long) sl,
- errstring(save_errno));
- if (LogLevel > 0)
+ sm_dprintf("milter_write (%s): write(%c) returned %ld, expected %ld: %s\n",
+ m->mf_name, cmd, (long) i, (long) sl,
+ sm_errstring(save_errno));
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_write(%s): write(%c) returned %ld, expected %ld: %s",
+ "Milter (%s): write(%c) returned %ld, expected %ld: %s",
m->mf_name, cmd, (long) i, (long) sl,
- errstring(save_errno));
- milter_error(m);
+ sm_errstring(save_errno));
+ milter_error(m, e);
return buf;
}
@@ -447,8 +465,9 @@ milter_write(m, cmd, buf, len, to, e)
return buf;
if (tTd(64, 50))
- dprintf("milter_write(%s): Sending %*s\n",
- m->mf_name, (int) len, buf);
+ sm_dprintf("milter_write(%s): Sending %*s\n",
+ m->mf_name, (int) len, buf);
+ started = true;
if (to > 0)
{
@@ -458,19 +477,19 @@ milter_write(m, cmd, buf, len, to, e)
if (now - writestart >= to)
{
if (tTd(64, 5))
- dprintf("milter_write(%s): timeout before data send\n",
- m->mf_name);
- if (LogLevel > 0)
+ sm_dprintf("milter_write(%s): timeout before data write\n",
+ m->mf_name);
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_write(%s): timeout before data send\n",
+ "Milter (%s): timeout before data write",
m->mf_name);
- milter_error(m);
+ milter_error(m, e);
return NULL;
}
else
{
to -= now - writestart;
- MILTER_TIMEOUT("milter_write", to, TRUE);
+ MILTER_TIMEOUT("write", to, true, started);
}
}
@@ -480,15 +499,15 @@ milter_write(m, cmd, buf, len, to, e)
int save_errno = errno;
if (tTd(64, 5))
- dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
- m->mf_name, cmd, (long) i, (long) sl,
- errstring(save_errno));
- if (LogLevel > 0)
+ sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
+ m->mf_name, cmd, (long) i, (long) sl,
+ sm_errstring(save_errno));
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_write(%s): write(%c) returned %ld, expected %ld: %s",
+ "Milter (%s): write(%c) returned %ld, expected %ld: %s",
m->mf_name, cmd, (long) i, (long) len,
- errstring(save_errno));
- milter_error(m);
+ sm_errstring(save_errno));
+ milter_error(m, e);
return NULL;
}
return buf;
@@ -498,7 +517,7 @@ milter_write(m, cmd, buf, len, to, e)
** Utility functions
*/
- /*
+/*
** MILTER_OPEN -- connect to remote milter filter
**
** Parameters:
@@ -533,16 +552,16 @@ milter_open(m, parseonly, e)
if (m->mf_conn == NULL || m->mf_conn[0] == '\0')
{
if (tTd(64, 5))
- dprintf("X%s: empty or missing socket information\n",
- m->mf_name);
+ sm_dprintf("X%s: empty or missing socket information\n",
+ m->mf_name);
if (parseonly)
syserr("X%s: empty or missing socket information",
m->mf_name);
- else if (LogLevel > 10)
+ else if (MilterLogLevel > 10)
sm_syslog(LOG_ERR, e->e_id,
- "X%s: empty or missing socket information",
+ "Milter (%s): empty or missing socket information",
m->mf_name);
- milter_error(m);
+ milter_error(m, e);
return -1;
}
@@ -569,25 +588,25 @@ milter_open(m, parseonly, e)
# else /* NETINET6 */
/* no protocols available */
sm_syslog(LOG_ERR, e->e_id,
- "X%s: no valid socket protocols available",
+ "Milter (%s): no valid socket protocols available",
m->mf_name);
- milter_error(m);
+ milter_error(m, e);
return -1;
# endif /* NETINET6 */
# endif /* NETINET */
# endif /* NETUNIX */
}
# if NETUNIX
- else if (strcasecmp(p, "unix") == 0 ||
- strcasecmp(p, "local") == 0)
+ else if (sm_strcasecmp(p, "unix") == 0 ||
+ sm_strcasecmp(p, "local") == 0)
addr.sa.sa_family = AF_UNIX;
# endif /* NETUNIX */
# if NETINET
- else if (strcasecmp(p, "inet") == 0)
+ else if (sm_strcasecmp(p, "inet") == 0)
addr.sa.sa_family = AF_INET;
# endif /* NETINET */
# if NETINET6
- else if (strcasecmp(p, "inet6") == 0)
+ else if (sm_strcasecmp(p, "inet6") == 0)
addr.sa.sa_family = AF_INET6;
# endif /* NETINET6 */
else
@@ -598,16 +617,16 @@ milter_open(m, parseonly, e)
errno = EINVAL;
# endif /* EPROTONOSUPPORT */
if (tTd(64, 5))
- dprintf("X%s: unknown socket type %s\n",
+ sm_dprintf("X%s: unknown socket type %s\n",
m->mf_name, p);
if (parseonly)
syserr("X%s: unknown socket type %s",
m->mf_name, p);
- else if (LogLevel > 10)
+ else if (MilterLogLevel > 10)
sm_syslog(LOG_ERR, e->e_id,
- "X%s: unknown socket type %s",
+ "Milter (%s): unknown socket type %s",
m->mf_name, p);
- milter_error(m);
+ milter_error(m, e);
return -1;
}
*colon++ = ':';
@@ -628,17 +647,17 @@ milter_open(m, parseonly, e)
if (strlen(colon) >= sizeof addr.sunix.sun_path)
{
if (tTd(64, 5))
- dprintf("X%s: local socket name %s too long\n",
+ sm_dprintf("X%s: local socket name %s too long\n",
m->mf_name, colon);
errno = EINVAL;
if (parseonly)
syserr("X%s: local socket name %s too long",
m->mf_name, colon);
- else if (LogLevel > 10)
+ else if (MilterLogLevel > 10)
sm_syslog(LOG_ERR, e->e_id,
- "X%s: local socket name %s too long",
+ "Milter (%s): local socket name %s too long",
m->mf_name, colon);
- milter_error(m);
+ milter_error(m, e);
return -1;
}
errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
@@ -649,16 +668,16 @@ milter_open(m, parseonly, e)
{
if (OpMode == MD_DAEMON ||
OpMode == MD_FGDAEMON)
- fprintf(stderr,
- "WARNING: X%s: local socket name %s missing\n",
- m->mf_name, colon);
+ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
+ "WARNING: X%s: local socket name %s missing\n",
+ m->mf_name, colon);
}
else if (errno != 0)
{
/* if not safe, don't create */
save_errno = errno;
if (tTd(64, 5))
- dprintf("X%s: local socket name %s unsafe\n",
+ sm_dprintf("X%s: local socket name %s unsafe\n",
m->mf_name, colon);
errno = save_errno;
if (parseonly)
@@ -669,22 +688,22 @@ milter_open(m, parseonly, e)
syserr("X%s: local socket name %s unsafe",
m->mf_name, colon);
}
- else if (LogLevel > 10)
+ else if (MilterLogLevel > 10)
sm_syslog(LOG_ERR, e->e_id,
- "X%s: local socket name %s unsafe",
+ "Milter (%s): local socket name %s unsafe",
m->mf_name, colon);
- milter_error(m);
+ milter_error(m, e);
return -1;
}
- (void) strlcpy(addr.sunix.sun_path, colon,
+ (void) sm_strlcpy(addr.sunix.sun_path, colon,
sizeof addr.sunix.sun_path);
addrlen = sizeof (struct sockaddr_un);
}
else
# endif /* NETUNIX */
# if NETINET || NETINET6
- if (FALSE
+ if (false
# if NETINET
|| addr.sa.sa_family == AF_INET
# endif /* NETINET */
@@ -693,42 +712,42 @@ milter_open(m, parseonly, e)
# endif /* NETINET6 */
)
{
- u_short port;
+ unsigned short port;
/* Parse port@host */
at = strchr(colon, '@');
if (at == NULL)
{
if (tTd(64, 5))
- dprintf("X%s: bad address %s (expected port@host)\n",
+ sm_dprintf("X%s: bad address %s (expected port@host)\n",
m->mf_name, colon);
if (parseonly)
syserr("X%s: bad address %s (expected port@host)",
m->mf_name, colon);
- else if (LogLevel > 10)
+ else if (MilterLogLevel > 10)
sm_syslog(LOG_ERR, e->e_id,
- "X%s: bad address %s (expected port@host)",
+ "Milter (%s): bad address %s (expected port@host)",
m->mf_name, colon);
- milter_error(m);
+ milter_error(m, e);
return -1;
}
*at = '\0';
if (isascii(*colon) && isdigit(*colon))
- port = htons((u_short) atoi(colon));
+ port = htons((unsigned short) atoi(colon));
else
{
# ifdef NO_GETSERVBYNAME
if (tTd(64, 5))
- dprintf("X%s: invalid port number %s\n",
+ sm_dprintf("X%s: invalid port number %s\n",
m->mf_name, colon);
if (parseonly)
syserr("X%s: invalid port number %s",
m->mf_name, colon);
- else if (LogLevel > 10)
+ else if (MilterLogLevel > 10)
sm_syslog(LOG_ERR, e->e_id,
- "X%s: invalid port number %s",
+ "Milter (%s): invalid port number %s",
m->mf_name, colon);
- milter_error(m);
+ milter_error(m, e);
return -1;
# else /* NO_GETSERVBYNAME */
register struct servent *sp;
@@ -738,17 +757,17 @@ milter_open(m, parseonly, e)
{
save_errno = errno;
if (tTd(64, 5))
- dprintf("X%s: unknown port name %s\n",
+ sm_dprintf("X%s: unknown port name %s\n",
m->mf_name, colon);
errno = save_errno;
if (parseonly)
syserr("X%s: unknown port name %s",
m->mf_name, colon);
- else if (LogLevel > 10)
+ else if (MilterLogLevel > 10)
sm_syslog(LOG_ERR, e->e_id,
- "X%s: unknown port name %s",
+ "Milter (%s): unknown port name %s",
m->mf_name, colon);
- milter_error(m);
+ milter_error(m, e);
return -1;
}
port = sp->s_port;
@@ -762,7 +781,7 @@ milter_open(m, parseonly, e)
end = strchr(at, ']');
if (end != NULL)
{
- bool found = FALSE;
+ bool found = false;
# if NETINET
unsigned long hid = INADDR_NONE;
# endif /* NETINET */
@@ -777,50 +796,50 @@ milter_open(m, parseonly, e)
{
addr.sin.sin_addr.s_addr = hid;
addr.sin.sin_port = port;
- found = TRUE;
+ found = true;
}
# endif /* NETINET */
# if NETINET6
(void) memset(&hid6, '\0', sizeof hid6);
if (addr.sa.sa_family == AF_INET6 &&
- inet_pton(AF_INET6, &at[1],
- &hid6.sin6_addr) == 1)
+ anynet_pton(AF_INET6, &at[1],
+ &hid6.sin6_addr) == 1)
{
addr.sin6.sin6_addr = hid6.sin6_addr;
addr.sin6.sin6_port = port;
- found = TRUE;
+ found = true;
}
# endif /* NETINET6 */
*end = ']';
if (!found)
{
if (tTd(64, 5))
- dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
+ sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
m->mf_name, at);
if (parseonly)
syserr("X%s: Invalid numeric domain spec \"%s\"",
m->mf_name, at);
- else if (LogLevel > 10)
+ else if (MilterLogLevel > 10)
sm_syslog(LOG_ERR, e->e_id,
- "X%s: Invalid numeric domain spec \"%s\"",
+ "Milter (%s): Invalid numeric domain spec \"%s\"",
m->mf_name, at);
- milter_error(m);
+ milter_error(m, e);
return -1;
}
}
else
{
if (tTd(64, 5))
- dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
+ sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
m->mf_name, at);
if (parseonly)
syserr("X%s: Invalid numeric domain spec \"%s\"",
m->mf_name, at);
- else if (LogLevel > 10)
+ else if (MilterLogLevel > 10)
sm_syslog(LOG_ERR, e->e_id,
- "X%s: Invalid numeric domain spec \"%s\"",
+ "Milter (%s): Invalid numeric domain spec \"%s\"",
m->mf_name, at);
- milter_error(m);
+ milter_error(m, e);
return -1;
}
}
@@ -831,17 +850,17 @@ milter_open(m, parseonly, e)
{
save_errno = errno;
if (tTd(64, 5))
- dprintf("X%s: Unknown host name %s\n",
- m->mf_name, at);
+ sm_dprintf("X%s: Unknown host name %s\n",
+ m->mf_name, at);
errno = save_errno;
if (parseonly)
syserr("X%s: Unknown host name %s",
m->mf_name, at);
- else if (LogLevel > 10)
+ else if (MilterLogLevel > 10)
sm_syslog(LOG_ERR, e->e_id,
- "X%s: Unknown host name %s",
+ "Milter (%s): Unknown host name %s",
m->mf_name, at);
- milter_error(m);
+ milter_error(m, e);
return -1;
}
addr.sa.sa_family = hp->h_addrtype;
@@ -850,8 +869,7 @@ milter_open(m, parseonly, e)
# if NETINET
case AF_INET:
memmove(&addr.sin.sin_addr,
- hp->h_addr,
- INADDRSZ);
+ hp->h_addr, INADDRSZ);
addr.sin.sin_port = port;
addrlen = sizeof (struct sockaddr_in);
addrno = 1;
@@ -861,8 +879,7 @@ milter_open(m, parseonly, e)
# if NETINET6
case AF_INET6:
memmove(&addr.sin6.sin6_addr,
- hp->h_addr,
- IN6ADDRSZ);
+ hp->h_addr, IN6ADDRSZ);
addr.sin6.sin6_port = port;
addrlen = sizeof (struct sockaddr_in6);
addrno = 1;
@@ -871,21 +888,21 @@ milter_open(m, parseonly, e)
default:
if (tTd(64, 5))
- dprintf("X%s: Unknown protocol for %s (%d)\n",
- m->mf_name, at,
- hp->h_addrtype);
+ sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
+ m->mf_name, at,
+ hp->h_addrtype);
if (parseonly)
syserr("X%s: Unknown protocol for %s (%d)",
m->mf_name, at, hp->h_addrtype);
- else if (LogLevel > 10)
+ else if (MilterLogLevel > 10)
sm_syslog(LOG_ERR, e->e_id,
- "X%s: Unknown protocol for %s (%d)",
+ "Milter (%s): Unknown protocol for %s (%d)",
m->mf_name, at,
hp->h_addrtype);
- milter_error(m);
-# if _FFR_FREEHOSTENT && NETINET6
+ milter_error(m, e);
+# if NETINET6
freehostent(hp);
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+# endif /* NETINET6 */
return -1;
}
}
@@ -894,13 +911,15 @@ milter_open(m, parseonly, e)
# endif /* NETINET || NETINET6 */
{
if (tTd(64, 5))
- dprintf("X%s: unknown socket protocol\n", m->mf_name);
+ sm_dprintf("X%s: unknown socket protocol\n",
+ m->mf_name);
if (parseonly)
syserr("X%s: unknown socket protocol", m->mf_name);
- else if (LogLevel > 10)
+ else if (MilterLogLevel > 10)
sm_syslog(LOG_ERR, e->e_id,
- "X%s: unknown socket protocol", m->mf_name);
- milter_error(m);
+ "Milter (%s): unknown socket protocol",
+ m->mf_name);
+ milter_error(m, e);
return -1;
}
@@ -908,10 +927,10 @@ milter_open(m, parseonly, e)
if (parseonly)
{
m->mf_state = SMFS_READY;
-# if _FFR_FREEHOSTENT && NETINET6
+# if NETINET6
if (hp != NULL)
freehostent(hp);
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+# endif /* NETINET6 */
return 0;
}
@@ -921,13 +940,13 @@ milter_open(m, parseonly, e)
{
/* shouldn't happen */
if (tTd(64, 1))
- dprintf("milter_open(%s): Trying to open filter in state %c\n",
- m->mf_name, (char) m->mf_state);
- milter_error(m);
-# if _FFR_FREEHOSTENT && NETINET6
+ sm_dprintf("Milter (%s): Trying to open filter in state %c\n",
+ m->mf_name, (char) m->mf_state);
+ milter_error(m, e);
+# if NETINET6
if (hp != NULL)
freehostent(hp);
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+# endif /* NETINET6 */
return -1;
}
@@ -939,33 +958,34 @@ milter_open(m, parseonly, e)
{
save_errno = errno;
if (tTd(64, 5))
- dprintf("X%s: error creating socket: %s\n",
- m->mf_name, errstring(save_errno));
- if (LogLevel > 0)
+ sm_dprintf("Milter (%s): error creating socket: %s\n",
+ m->mf_name,
+ sm_errstring(save_errno));
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "X%s: error creating socket: %s",
- m->mf_name, errstring(save_errno));
- milter_error(m);
-# if _FFR_FREEHOSTENT && NETINET6
+ "Milter (%s): error creating socket: %s",
+ m->mf_name, sm_errstring(save_errno));
+ milter_error(m, e);
+# if NETINET6
if (hp != NULL)
freehostent(hp);
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+# endif /* NETINET6 */
return -1;
}
if (setjmp(MilterConnectTimeout) == 0)
{
- EVENT *ev = NULL;
+ SM_EVENT *ev = NULL;
int i;
if (m->mf_timeout[SMFTO_CONNECT] > 0)
- ev = setevent(m->mf_timeout[SMFTO_CONNECT],
- milter_connect_timeout, 0);
+ ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT],
+ milter_connect_timeout, 0);
i = connect(sock, (struct sockaddr *) &addr, addrlen);
save_errno = errno;
if (ev != NULL)
- clrevent(ev);
+ sm_clrevent(ev);
errno = save_errno;
if (i >= 0)
break;
@@ -976,12 +996,12 @@ milter_open(m, parseonly, e)
p = CurHostName;
CurHostName = at;
if (tTd(64, 5))
- dprintf("milter_open(%s): %s failed: %s\n",
- m->mf_name, at, errstring(save_errno));
- if (LogLevel >= 14)
+ sm_dprintf("milter_open (%s): open %s failed: %s\n",
+ m->mf_name, at, sm_errstring(save_errno));
+ if (MilterLogLevel > 13)
sm_syslog(LOG_INFO, e->e_id,
- "milter_open(%s): %s failed: %s",
- m->mf_name, at, errstring(save_errno));
+ "Milter (%s): open %s failed: %s",
+ m->mf_name, at, sm_errstring(save_errno));
CurHostName = p;
(void) close(sock);
@@ -1008,18 +1028,18 @@ milter_open(m, parseonly, e)
default:
if (tTd(64, 5))
- dprintf("X%s: Unknown protocol for %s (%d)\n",
- m->mf_name, at,
- hp->h_addrtype);
- if (LogLevel > 0)
+ sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
+ m->mf_name, at,
+ hp->h_addrtype);
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "X%s: Unknown protocol for %s (%d)",
+ "Milter (%s): Unknown protocol for %s (%d)",
m->mf_name, at,
hp->h_addrtype);
- milter_error(m);
-# if _FFR_FREEHOSTENT && NETINET6
+ milter_error(m, e);
+# if NETINET6
freehostent(hp);
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+# endif /* NETINET6 */
return -1;
}
continue;
@@ -1027,28 +1047,28 @@ milter_open(m, parseonly, e)
p = CurHostName;
CurHostName = at;
if (tTd(64, 5))
- dprintf("X%s: error connecting to filter: %s\n",
- m->mf_name, errstring(save_errno));
- if (LogLevel > 0)
+ sm_dprintf("X%s: error connecting to filter: %s\n",
+ m->mf_name, sm_errstring(save_errno));
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "X%s: error connecting to filter: %s",
- m->mf_name, errstring(save_errno));
+ "Milter (%s): error connecting to filter: %s",
+ m->mf_name, sm_errstring(save_errno));
CurHostName = p;
- milter_error(m);
-# if _FFR_FREEHOSTENT && NETINET6
+ milter_error(m, e);
+# if NETINET6
if (hp != NULL)
freehostent(hp);
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+# endif /* NETINET6 */
return -1;
}
m->mf_state = SMFS_OPEN;
-# if _FFR_FREEHOSTENT && NETINET6
+# if NETINET6
if (hp != NULL)
{
freehostent(hp);
hp = NULL;
}
-# endif /* _FFR_FREEHOSTENT && NETINET6 */
+# endif /* NETINET6 */
return sock;
}
@@ -1064,7 +1084,7 @@ milter_connect_timeout()
errno = ETIMEDOUT;
longjmp(MilterConnectTimeout, 1);
}
- /*
+/*
** MILTER_SETUP -- setup structure for a mail filter
**
** Parameters:
@@ -1095,12 +1115,12 @@ milter_setup(line)
syserr("name required for mail filter");
return;
}
- m = (struct milter *)xalloc(sizeof *m);
+ m = (struct milter *) xalloc(sizeof *m);
memset((char *) m, '\0', sizeof *m);
m->mf_name = newstr(line);
m->mf_state = SMFS_READY;
m->mf_sock = -1;
- m->mf_timeout[SMFTO_CONNECT] = (time_t) 0;
+ m->mf_timeout[SMFTO_CONNECT] = (time_t) 300;
m->mf_timeout[SMFTO_WRITE] = (time_t) 10;
m->mf_timeout[SMFTO_READ] = (time_t) 10;
m->mf_timeout[SMFTO_EOM] = (time_t) 300;
@@ -1160,7 +1180,7 @@ milter_setup(line)
}
/* early check for errors */
- (void) milter_open(m, TRUE, CurEnv);
+ (void) milter_open(m, true, CurEnv);
/* enter the filter into the symbol table */
s = stab(m->mf_name, ST_MILTER, ST_ENTER);
@@ -1169,8 +1189,8 @@ milter_setup(line)
else
s->s_milter = m;
}
- /*
-** MILTER_PARSE_LIST -- parse option list into an array
+/*
+** MILTER_CONFIG -- parse option list into an array and check config
**
** Called when reading configuration file.
**
@@ -1184,7 +1204,7 @@ milter_setup(line)
*/
void
-milter_parse_list(spec, list, max)
+milter_config(spec, list, max)
char *spec;
struct milter **list;
int max;
@@ -1212,7 +1232,11 @@ milter_parse_list(spec, list, max)
list[0] = NULL;
return;
}
+#if _FFR_MILTER_PERDAEMON
+ p = strpbrk(p, ";,");
+#else /* _FFR_MILTER_PERDAEMON */
p = strpbrk(p, ",");
+#endif /* _FFR_MILTER_PERDAEMON */
if (p != NULL)
*p++ = '\0';
@@ -1226,8 +1250,12 @@ milter_parse_list(spec, list, max)
list[numitems++] = s->s_milter;
}
list[numitems] = NULL;
+
+ /* if not set, set to LogLevel */
+ if (MilterLogLevel == -1)
+ MilterLogLevel = LogLevel;
}
- /*
+/*
** MILTER_PARSE_TIMEOUTS -- parse timeout list
**
** Called when reading configuration file.
@@ -1280,39 +1308,39 @@ milter_parse_timeouts(spec, m)
case 'C':
m->mf_timeout[SMFTO_CONNECT] = convtime(p, 's');
if (tTd(64, 5))
- printf("X%s: %c=%ld\n",
- m->mf_name, fcode,
- (u_long) m->mf_timeout[SMFTO_CONNECT]);
+ sm_dprintf("X%s: %c=%lu\n",
+ m->mf_name, fcode,
+ (unsigned long) m->mf_timeout[SMFTO_CONNECT]);
break;
case 'S':
m->mf_timeout[SMFTO_WRITE] = convtime(p, 's');
if (tTd(64, 5))
- printf("X%s: %c=%ld\n",
- m->mf_name, fcode,
- (u_long) m->mf_timeout[SMFTO_WRITE]);
+ sm_dprintf("X%s: %c=%lu\n",
+ m->mf_name, fcode,
+ (unsigned long) m->mf_timeout[SMFTO_WRITE]);
break;
case 'R':
m->mf_timeout[SMFTO_READ] = convtime(p, 's');
if (tTd(64, 5))
- printf("X%s: %c=%ld\n",
- m->mf_name, fcode,
- (u_long) m->mf_timeout[SMFTO_READ]);
+ sm_dprintf("X%s: %c=%lu\n",
+ m->mf_name, fcode,
+ (unsigned long) m->mf_timeout[SMFTO_READ]);
break;
case 'E':
m->mf_timeout[SMFTO_EOM] = convtime(p, 's');
if (tTd(64, 5))
- printf("X%s: %c=%ld\n",
- m->mf_name, fcode,
- (u_long) m->mf_timeout[SMFTO_EOM]);
+ sm_dprintf("X%s: %c=%lu\n",
+ m->mf_name, fcode,
+ (unsigned long) m->mf_timeout[SMFTO_EOM]);
break;
default:
if (tTd(64, 5))
- printf("X%s: %c unknown\n",
- m->mf_name, fcode);
+ sm_dprintf("X%s: %c unknown\n",
+ m->mf_name, fcode);
syserr("X%s: unknown filter timeout %c",
m->mf_name, fcode);
break;
@@ -1320,7 +1348,7 @@ milter_parse_timeouts(spec, m)
p = delimptr;
}
}
- /*
+/*
** MILTER_SET_OPTION -- set an individual milter option
**
** Parameters:
@@ -1338,8 +1366,8 @@ static BITMAP256 StickyMilterOpt;
static struct milteropt
{
- char *mo_name; /* long name of milter option */
- u_char mo_code; /* code for option */
+ char *mo_name; /* long name of milter option */
+ unsigned char mo_code; /* code for option */
} MilterOptTab[] =
{
# define MO_MACROS_CONNECT 0x01
@@ -1350,6 +1378,8 @@ static struct milteropt
{ "macros.envfrom", MO_MACROS_ENVFROM },
# define MO_MACROS_ENVRCPT 0x04
{ "macros.envrcpt", MO_MACROS_ENVRCPT },
+# define MO_LOGLEVEL 0x05
+ { "loglevel", MO_LOGLEVEL },
{ NULL, 0 },
};
@@ -1365,16 +1395,19 @@ milter_set_option(name, val, sticky)
char **macros = NULL;
if (tTd(37, 2) || tTd(64, 5))
- dprintf("milter_set_option(%s = %s)", name, val);
+ sm_dprintf("milter_set_option(%s = %s)", name, val);
for (mo = MilterOptTab; mo->mo_name != NULL; mo++)
{
- if (strcasecmp(mo->mo_name, name) == 0)
+ if (sm_strcasecmp(mo->mo_name, name) == 0)
break;
}
if (mo->mo_name == NULL)
+ {
syserr("milter_set_option: invalid Milter option %s", name);
+ return;
+ }
/*
** See if this option is preset for us.
@@ -1383,15 +1416,19 @@ milter_set_option(name, val, sticky)
if (!sticky && bitnset(mo->mo_code, StickyMilterOpt))
{
if (tTd(37, 2) || tTd(64,5))
- dprintf(" (ignored)\n");
+ sm_dprintf(" (ignored)\n");
return;
}
if (tTd(37, 2) || tTd(64,5))
- dprintf("\n");
+ sm_dprintf("\n");
switch (mo->mo_code)
{
+ case MO_LOGLEVEL:
+ MilterLogLevel = atoi(val);
+ break;
+
case MO_MACROS_CONNECT:
if (macros == NULL)
macros = MilterConnectMacros;
@@ -1448,12 +1485,11 @@ milter_set_option(name, val, sticky)
syserr("milter_set_option: invalid Milter option %s", name);
break;
}
-
if (sticky)
setbitn(mo->mo_code, StickyMilterOpt);
}
- /*
-** MILTER_REOPEN_DF -- open & truncate the df file (for replbody)
+/*
+** MILTER_REOPEN_DF -- open & truncate the data file (for replbody)
**
** Parameters:
** e -- current envelope.
@@ -1468,32 +1504,33 @@ milter_reopen_df(e)
{
char dfname[MAXPATHLEN];
- (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
+ (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof dfname);
/*
- ** In SuperSafe mode, e->e_dfp is a read-only FP so
+ ** In SuperSafe == SAFE_REALLY mode, e->e_dfp is a read-only FP so
** close and reopen writable (later close and reopen
** read only again).
**
- ** In !SuperSafe mode, e->e_dfp still points at the
+ ** In SuperSafe != SAFE_REALLY mode, e->e_dfp still points at the
** buffered file I/O descriptor, still open for writing
** so there isn't as much work to do, just truncate it
** and go.
*/
- if (SuperSafe)
+ if (SuperSafe == SAFE_REALLY)
{
- /* close read-only df */
+ /* close read-only data file */
if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
{
- (void) fclose(e->e_dfp);
+ (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
e->e_flags &= ~EF_HAS_DF;
}
/* open writable */
- if ((e->e_dfp = fopen(dfname, "w+")) == NULL)
+ if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
+ SM_IO_RDWR, NULL)) == NULL)
{
- MILTER_DF_ERROR("milter_reopen_df: fopen %s: %s");
+ MILTER_DF_ERROR("milter_reopen_df: sm_io_open %s: %s");
return -1;
}
}
@@ -1506,8 +1543,8 @@ milter_reopen_df(e)
}
return 0;
}
- /*
-** MILTER_RESET_DF -- re-open read-only the df file (for replbody)
+/*
+** MILTER_RESET_DF -- re-open read-only the data file (for replbody)
**
** Parameters:
** e -- current envelope.
@@ -1523,29 +1560,32 @@ milter_reset_df(e)
int afd;
char dfname[MAXPATHLEN];
- (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
+ (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof dfname);
- if (fflush(e->e_dfp) != 0 || ferror(e->e_dfp))
+ if (sm_io_flush(e->e_dfp, SM_TIME_DEFAULT) != 0 ||
+ sm_io_error(e->e_dfp))
{
MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s");
return -1;
}
- else if (!SuperSafe)
+ else if (SuperSafe != SAFE_REALLY)
{
/* skip next few clauses */
/* EMPTY */
}
- else if ((afd = fileno(e->e_dfp)) >= 0 && fsync(afd) < 0)
+ else if ((afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL)) >= 0
+ && fsync(afd) < 0)
{
MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s");
return -1;
}
- else if (fclose(e->e_dfp) < 0)
+ else if (sm_io_close(e->e_dfp, SM_TIME_DEFAULT) < 0)
{
MILTER_DF_ERROR("milter_reset_df: error closing %s: %s");
return -1;
}
- else if ((e->e_dfp = fopen(dfname, "r")) == NULL)
+ else if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
+ SM_IO_RDONLY, NULL)) == NULL)
{
MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s");
return -1;
@@ -1554,24 +1594,24 @@ milter_reset_df(e)
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
+** true if any filter deletes recipients, false otherwise
*/
bool
milter_can_delrcpts()
{
- bool can = FALSE;
+ bool can = false;
int i;
if (tTd(64, 10))
- dprintf("milter_can_delrcpts:");
+ sm_dprintf("milter_can_delrcpts:");
for (i = 0; InputFilters[i] != NULL; i++)
{
@@ -1579,16 +1619,16 @@ milter_can_delrcpts()
if (bitset(SMFIF_DELRCPT, m->mf_fflags))
{
- can = TRUE;
+ can = true;
break;
}
}
if (tTd(64, 10))
- dprintf("%s\n", can ? "TRUE" : "FALSE");
+ sm_dprintf("%s\n", can ? "true" : "false");
return can;
}
- /*
+/*
** MILTER_QUIT_FILTER -- close down a single filter
**
** Parameters:
@@ -1605,7 +1645,10 @@ milter_quit_filter(m, e)
ENVELOPE *e;
{
if (tTd(64, 10))
- dprintf("milter_quit_filter(%s)\n", m->mf_name);
+ sm_dprintf("milter_quit_filter(%s)\n", m->mf_name);
+ if (MilterLogLevel > 18)
+ sm_syslog(LOG_INFO, e->e_id, "Milter (%s): quit filter",
+ m->mf_name);
/* Never replace error state */
if (m->mf_state == SMFS_ERROR)
@@ -1630,7 +1673,7 @@ milter_quit_filter(m, e)
if (m->mf_state != SMFS_ERROR)
m->mf_state = SMFS_CLOSED;
}
- /*
+/*
** MILTER_ABORT_FILTER -- tell filter to abort current message
**
** Parameters:
@@ -1647,7 +1690,10 @@ milter_abort_filter(m, e)
ENVELOPE *e;
{
if (tTd(64, 10))
- dprintf("milter_abort_filter(%s)\n", m->mf_name);
+ sm_dprintf("milter_abort_filter(%s)\n", m->mf_name);
+ if (MilterLogLevel > 10)
+ sm_syslog(LOG_INFO, e->e_id, "Milter (%s): abort filter",
+ m->mf_name);
if (m->mf_sock < 0 ||
m->mf_state != SMFS_INMSG)
@@ -1658,7 +1704,7 @@ milter_abort_filter(m, e)
if (m->mf_state != SMFS_ERROR)
m->mf_state = SMFS_DONE;
}
- /*
+/*
** MILTER_SEND_MACROS -- provide macros to the filters
**
** Parameters:
@@ -1692,7 +1738,7 @@ milter_send_macros(m, macros, cmd, e)
s = 1; /* for the command character */
for (i = 0; macros[i] != NULL; i++)
{
- mid = macid(macros[i], NULL);
+ mid = macid(macros[i]);
if (mid == 0)
continue;
v = macvalue(mid, e);
@@ -1701,12 +1747,15 @@ milter_send_macros(m, macros, cmd, e)
s += strlen(macros[i]) + 1 + strlen(v) + 1;
}
- buf = (char *)xalloc(s);
+ if (s < 0)
+ return;
+
+ buf = (char *) xalloc(s);
bp = buf;
*bp++ = cmd;
for (i = 0; macros[i] != NULL; i++)
{
- mid = macid(macros[i], NULL);
+ mid = macid(macros[i]);
if (mid == 0)
continue;
v = macvalue(mid, e);
@@ -1714,20 +1763,20 @@ milter_send_macros(m, macros, cmd, e)
continue;
if (tTd(64, 10))
- dprintf("milter_send_macros(%s, %c): %s=%s\n",
+ sm_dprintf("milter_send_macros(%s, %c): %s=%s\n",
m->mf_name, cmd, macros[i], v);
- (void) strlcpy(bp, macros[i], s - (bp - buf));
+ (void) sm_strlcpy(bp, macros[i], s - (bp - buf));
bp += strlen(bp) + 1;
- (void) strlcpy(bp, v, s - (bp - buf));
+ (void) sm_strlcpy(bp, v, s - (bp - buf));
bp += strlen(bp) + 1;
}
(void) milter_write(m, SMFIC_MACRO, buf, s,
m->mf_timeout[SMFTO_WRITE], e);
- sm_free(buf);
+ sm_free(buf); /* XXX */
}
- /*
+/*
** MILTER_SEND_COMMAND -- send a command and return the response for a filter
**
** Parameters:
@@ -1753,12 +1802,13 @@ milter_send_command(m, command, data, sz, e, state)
{
char rcmd;
ssize_t rlen;
- u_long skipflag;
+ unsigned long skipflag;
+ char *action;
char *defresponse;
char *response;
if (tTd(64, 10))
- dprintf("milter_send_command(%s): cmd %c len %ld\n",
+ sm_dprintf("milter_send_command(%s): cmd %c len %ld\n",
m->mf_name, (char) command, (long) sz);
/* find skip flag and default failure */
@@ -1766,36 +1816,43 @@ milter_send_command(m, command, data, sz, e, state)
{
case SMFIC_CONNECT:
skipflag = SMFIP_NOCONNECT;
+ action = "connect";
defresponse = "554 Command rejected";
break;
case SMFIC_HELO:
skipflag = SMFIP_NOHELO;
+ action = "helo";
defresponse = "550 Command rejected";
break;
case SMFIC_MAIL:
skipflag = SMFIP_NOMAIL;
+ action = "mail";
defresponse = "550 5.7.1 Command rejected";
break;
case SMFIC_RCPT:
skipflag = SMFIP_NORCPT;
+ action = "rcpt";
defresponse = "550 5.7.1 Command rejected";
break;
case SMFIC_HEADER:
skipflag = SMFIP_NOHDRS;
+ action = "header";
defresponse = "550 5.7.1 Command rejected";
break;
case SMFIC_BODY:
skipflag = SMFIP_NOBODY;
+ action = "body";
defresponse = "554 5.7.1 Command rejected";
break;
case SMFIC_EOH:
skipflag = SMFIP_NOEOH;
+ action = "eoh";
defresponse = "550 5.7.1 Command rejected";
break;
@@ -1809,6 +1866,7 @@ milter_send_command(m, command, data, sz, e, state)
default:
skipflag = 0;
+ action = "default";
defresponse = "550 5.7.1 Command rejected";
break;
}
@@ -1818,7 +1876,7 @@ milter_send_command(m, command, data, sz, e, state)
bitset(skipflag, m->mf_pflags))
return NULL;
-
+ /* send the command to the filter */
(void) milter_write(m, command, data, sz,
m->mf_timeout[SMFTO_WRITE], e);
if (m->mf_state == SMFS_ERROR)
@@ -1827,6 +1885,7 @@ milter_send_command(m, command, data, sz, e, state)
return NULL;
}
+ /* get the response from the filter */
response = milter_read(m, &rcmd, &rlen,
m->mf_timeout[SMFTO_READ], e);
if (m->mf_state == SMFS_ERROR)
@@ -1836,18 +1895,37 @@ milter_send_command(m, command, data, sz, e, state)
}
if (tTd(64, 10))
- dprintf("milter_send_command(%s): returned %c\n",
- m->mf_name, (char) rcmd);
+ sm_dprintf("milter_send_command(%s): returned %c\n",
+ m->mf_name, (char) rcmd);
switch (rcmd)
{
case SMFIR_REPLYCODE:
MILTER_CHECK_REPLYCODE(defresponse);
- /* FALLTHROUGH */
+ if (MilterLogLevel > 10)
+ sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, reject=%s",
+ m->mf_name, action, response);
+ *state = rcmd;
+ break;
case SMFIR_REJECT:
+ if (MilterLogLevel > 10)
+ sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, reject",
+ m->mf_name, action);
+ *state = rcmd;
+ break;
+
case SMFIR_DISCARD:
+ if (MilterLogLevel > 10)
+ sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, discard",
+ m->mf_name, action);
+ *state = rcmd;
+ break;
+
case SMFIR_TEMPFAIL:
+ if (MilterLogLevel > 10)
+ sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, tempfail",
+ m->mf_name, action);
*state = rcmd;
break;
@@ -1858,34 +1936,40 @@ milter_send_command(m, command, data, sz, e, state)
m->mf_state = SMFS_CLOSABLE;
else
m->mf_state = SMFS_DONE;
+ if (MilterLogLevel > 10)
+ sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, accepted",
+ m->mf_name, action);
break;
case SMFIR_CONTINUE:
/* if MAIL command is ok, filter is in message state */
if (command == SMFIC_MAIL)
m->mf_state = SMFS_INMSG;
+ if (MilterLogLevel > 12)
+ sm_syslog(LOG_INFO, e->e_id, "milter=%s, action=%s, continue",
+ m->mf_name, action);
break;
default:
/* Invalid response to command */
- if (LogLevel > 0)
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_send_command(%s): returned bogus response %c",
- m->mf_name, rcmd);
- milter_error(m);
+ "milter_send_command(%s): action=%s returned bogus response %c",
+ m->mf_name, action, rcmd);
+ milter_error(m, e);
break;
}
if (*state != SMFIR_REPLYCODE &&
response != NULL)
{
- sm_free(response);
+ sm_free(response); /* XXX */
response = NULL;
}
return response;
}
- /*
+/*
** MILTER_COMMAND -- send a command and return the response for each filter
**
** Parameters:
@@ -1911,9 +1995,10 @@ milter_command(command, data, sz, macros, e, state)
{
int i;
char *response = NULL;
+ time_t tn = 0;
if (tTd(64, 10))
- dprintf("milter_command: cmd %c len %ld\n",
+ sm_dprintf("milter_command: cmd %c len %ld\n",
(char) command, (long) sz);
*state = SMFIR_CONTINUE;
@@ -1944,13 +2029,25 @@ milter_command(command, data, sz, macros, e, state)
}
}
+ if (MilterLogLevel > 21)
+ tn = curtime();
+
response = milter_send_command(m, command, data, sz, e, state);
+
+ if (MilterLogLevel > 21)
+ {
+ /* log the time it took for the command per filter */
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter (%s): time command (%c), %d",
+ m->mf_name, command, (int) (tn - curtime()));
+ }
+
if (*state != SMFIR_CONTINUE)
break;
}
return response;
}
- /*
+/*
** MILTER_NEGOTIATE -- get version and flags from filter
**
** Parameters:
@@ -1977,11 +2074,11 @@ milter_negotiate(m, e)
/* sanity check */
if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN)
{
- if (LogLevel > 0)
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_negotiate(%s): impossible state",
+ "Milter (%s): negotiate, impossible state",
m->mf_name);
- milter_error(m);
+ milter_error(m, e);
return -1;
}
@@ -2006,15 +2103,15 @@ milter_negotiate(m, e)
if (rcmd != SMFIC_OPTNEG)
{
if (tTd(64, 5))
- dprintf("milter_negotiate(%s): returned %c instead of %c\n",
+ sm_dprintf("milter_negotiate(%s): returned %c instead of %c\n",
m->mf_name, rcmd, SMFIC_OPTNEG);
- if (LogLevel > 0)
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_negotiate(%s): returned %c instead of %c",
+ "Milter (%s): negotiate: returned %c instead of %c",
m->mf_name, rcmd, SMFIC_OPTNEG);
if (response != NULL)
- sm_free(response);
- milter_error(m);
+ sm_free(response); /* XXX */
+ milter_error(m, e);
return -1;
}
@@ -2022,15 +2119,15 @@ milter_negotiate(m, e)
if (response == NULL || rlen < MILTER_LEN_BYTES)
{
if (tTd(64, 5))
- dprintf("milter_negotiate(%s): did not return valid info\n",
+ sm_dprintf("milter_negotiate(%s): did not return valid info\n",
m->mf_name);
- if (LogLevel > 0)
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_negotiate(%s): did not return valid info",
+ "Milter (%s): negotiate: did not return valid info",
m->mf_name);
if (response != NULL)
- sm_free(response);
- milter_error(m);
+ sm_free(response); /* XXX */
+ milter_error(m, e);
return -1;
}
@@ -2041,15 +2138,15 @@ milter_negotiate(m, e)
if (rlen != MILTER_OPTLEN)
{
if (tTd(64, 5))
- dprintf("milter_negotiate(%s): did not return enough info\n",
+ sm_dprintf("milter_negotiate(%s): did not return enough info\n",
m->mf_name);
- if (LogLevel > 0)
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_negotiate(%s): did not return enough info",
+ "Milter (%s): negotiate: did not return enough info",
m->mf_name);
if (response != NULL)
- sm_free(response);
- milter_error(m);
+ sm_free(response); /* XXX */
+ milter_error(m, e);
return -1;
}
@@ -2057,7 +2154,7 @@ milter_negotiate(m, e)
MILTER_LEN_BYTES);
(void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2),
MILTER_LEN_BYTES);
- sm_free(response);
+ sm_free(response); /* XXX */
response = NULL;
m->mf_fvers = ntohl(fvers);
@@ -2069,13 +2166,13 @@ milter_negotiate(m, e)
m->mf_fvers > SMFI_VERSION)
{
if (tTd(64, 5))
- dprintf("milter_negotiate(%s): version %lu != MTA milter version %d\n",
+ sm_dprintf("milter_negotiate(%s): version %lu != MTA milter version %d\n",
m->mf_name, m->mf_fvers, SMFI_VERSION);
- if (LogLevel > 0)
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_negotiate(%s): version %ld != MTA milter version %d",
+ "Milter (%s): negotiate: version %ld != MTA milter version %d",
m->mf_name, m->mf_fvers, SMFI_VERSION);
- milter_error(m);
+ milter_error(m, e);
return -1;
}
@@ -2083,15 +2180,15 @@ milter_negotiate(m, e)
if ((m->mf_fflags & SMFI_CURR_ACTS) != m->mf_fflags)
{
if (tTd(64, 5))
- dprintf("milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n",
+ sm_dprintf("milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n",
m->mf_name, m->mf_fflags,
- (u_long) SMFI_CURR_ACTS);
- if (LogLevel > 0)
+ (unsigned long) SMFI_CURR_ACTS);
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n",
+ "Milter (%s): negotiate: filter abilities 0x%lx != MTA milter abilities 0x%lx",
m->mf_name, m->mf_fflags,
- (u_long) SMFI_CURR_ACTS);
- milter_error(m);
+ (unsigned long) SMFI_CURR_ACTS);
+ milter_error(m, e);
return -1;
}
@@ -2099,24 +2196,24 @@ milter_negotiate(m, e)
if ((m->mf_pflags & SMFI_CURR_PROT) != m->mf_pflags)
{
if (tTd(64, 5))
- dprintf("milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n",
+ sm_dprintf("milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n",
m->mf_name, m->mf_pflags,
- (u_long) SMFI_CURR_PROT);
- if (LogLevel > 0)
+ (unsigned long) SMFI_CURR_PROT);
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n",
+ "Milter (%s): negotiate: protocol abilities 0x%lx != MTA milter abilities 0x%lx",
m->mf_name, m->mf_pflags,
- (u_long) SMFI_CURR_PROT);
- milter_error(m);
+ (unsigned long) SMFI_CURR_PROT);
+ milter_error(m, e);
return -1;
}
if (tTd(64, 5))
- dprintf("milter_negotiate(%s): version %lu, fflags 0x%lx, pflags 0x%lx\n",
+ sm_dprintf("milter_negotiate(%s): version %lu, fflags 0x%lx, pflags 0x%lx\n",
m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags);
return 0;
}
- /*
+/*
** MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands
**
** Reduce code duplication by putting these checks in one place
@@ -2143,7 +2240,7 @@ milter_per_connection_check(e)
milter_quit_filter(m, e);
}
}
- /*
+/*
** MILTER_ERROR -- Put a milter filter into error state
**
** Parameters:
@@ -2154,8 +2251,9 @@ milter_per_connection_check(e)
*/
static void
-milter_error(m)
+milter_error(m, e)
struct milter *m;
+ ENVELOPE *e;
{
/*
** We could send a quit here but
@@ -2170,8 +2268,12 @@ milter_error(m)
m->mf_sock = -1;
}
m->mf_state = SMFS_ERROR;
+
+ if (MilterLogLevel > 0)
+ sm_syslog(LOG_INFO, e->e_id, "Milter (%s): to error state",
+ m->mf_name);
}
- /*
+/*
** MILTER_HEADERS -- send headers to a single milter filter
**
** Parameters:
@@ -2192,6 +2294,10 @@ milter_headers(m, e, state)
char *response = NULL;
HDR *h;
+ if (MilterLogLevel > 17)
+ sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, send",
+ m->mf_name);
+
for (h = e->e_header; h != NULL; h = h->h_link)
{
char *buf;
@@ -2210,26 +2316,34 @@ milter_headers(m, e, state)
continue;
if (tTd(64, 10))
- dprintf("milter_headers: %s: %s\n",
+ sm_dprintf("milter_headers: %s: %s\n",
h->h_field, h->h_value);
+ if (MilterLogLevel > 21)
+ sm_syslog(LOG_INFO, e->e_id, "Milter (%s): header, %s",
+ m->mf_name, h->h_field);
- s = strlen(h->h_field) + 1 +
- strlen(h->h_value) + 1;
+ s = strlen(h->h_field) + 1 + strlen(h->h_value) + 1;
+ if (s < 0)
+ continue;
buf = (char *) xalloc(s);
- snprintf(buf, s, "%s%c%s", h->h_field, '\0', h->h_value);
+ (void) sm_snprintf(buf, s, "%s%c%s",
+ h->h_field, '\0', h->h_value);
/* send it over */
response = milter_send_command(m, SMFIC_HEADER, buf,
s, e, state);
- sm_free(buf);
+ sm_free(buf); /* XXX */
if (m->mf_state == SMFS_ERROR ||
m->mf_state == SMFS_DONE ||
*state != SMFIR_CONTINUE)
break;
}
+ if (MilterLogLevel > 17)
+ sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, sent",
+ m->mf_name);
return response;
}
- /*
+/*
** MILTER_BODY -- send the body to a filter
**
** Parameters:
@@ -2255,19 +2369,23 @@ milter_body(m, e, state)
char buf[MILTER_CHUNK_SIZE];
if (tTd(64, 10))
- dprintf("milter_body\n");
+ sm_dprintf("milter_body\n");
if (bfrewind(e->e_dfp) < 0)
{
ExitStat = EX_IOERR;
*state = SMFIR_TEMPFAIL;
- syserr("milter_body: %s/df%s: rewind error",
- qid_printqueue(e->e_queuedir), e->e_id);
+ syserr("milter_body: %s/%cf%s: rewind error",
+ qid_printqueue(e->e_qgrp, e->e_qdir),
+ DATAFL_LETTER, e->e_id);
return NULL;
}
+ if (MilterLogLevel > 17)
+ sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, send",
+ m->mf_name);
bp = buf;
- while ((c = getc(e->e_dfp)) != EOF)
+ while ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != SM_IO_EOF)
{
/* Change LF to CRLF */
if (c == '\n')
@@ -2314,7 +2432,7 @@ milter_body(m, e, state)
}
/* check for read errors */
- if (ferror(e->e_dfp))
+ if (sm_io_error(e->e_dfp))
{
ExitStat = EX_IOERR;
if (*state == SMFIR_CONTINUE ||
@@ -2323,12 +2441,13 @@ milter_body(m, e, state)
*state = SMFIR_TEMPFAIL;
if (response != NULL)
{
- sm_free(response);
+ sm_free(response); /* XXX */
response = NULL;
}
}
- syserr("milter_body: %s/df%s: read error",
- qid_printqueue(e->e_queuedir), e->e_id);
+ syserr("milter_body: %s/%cf%s: read error",
+ qid_printqueue(e->e_qgrp, e->e_qdir),
+ DATAFL_LETTER, e->e_id);
return response;
}
@@ -2343,6 +2462,9 @@ milter_body(m, e, state)
e, state);
bp = buf;
}
+ if (MilterLogLevel > 17)
+ sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, sent",
+ m->mf_name);
return response;
}
@@ -2350,7 +2472,7 @@ milter_body(m, e, state)
** Actions
*/
- /*
+/*
** MILTER_ADDHEADER -- Add the supplied header to the message
**
** Parameters:
@@ -2372,20 +2494,20 @@ milter_addheader(response, rlen, e)
HDR *h;
if (tTd(64, 10))
- dprintf("milter_addheader: ");
+ sm_dprintf("milter_addheader: ");
/* sanity checks */
if (response == NULL)
{
if (tTd(64, 10))
- dprintf("NULL response\n");
+ sm_dprintf("NULL response\n");
return;
}
if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
{
if (tTd(64, 10))
- dprintf("didn't follow protocol (total len)\n");
+ sm_dprintf("didn't follow protocol (total len)\n");
return;
}
@@ -2396,20 +2518,20 @@ milter_addheader(response, rlen, e)
if (strlen(response) + strlen(val) + 2 != (size_t) rlen)
{
if (tTd(64, 10))
- dprintf("didn't follow protocol (part len)\n");
+ sm_dprintf("didn't follow protocol (part len)\n");
return;
}
if (*response == '\0')
{
if (tTd(64, 10))
- dprintf("empty field name\n");
+ sm_dprintf("empty field name\n");
return;
}
for (h = e->e_header; h != NULL; h = h->h_link)
{
- if (strcasecmp(h->h_field, response) == 0 &&
+ if (sm_strcasecmp(h->h_field, response) == 0 &&
!bitset(H_USER, h->h_flags) &&
!bitset(H_TRACE, h->h_flags))
break;
@@ -2421,19 +2543,26 @@ milter_addheader(response, rlen, e)
if (h != NULL)
{
if (tTd(64, 10))
- dprintf("Replace default header %s value with %s\n",
- h->h_field, val);
+ sm_dprintf("Replace default header %s value with %s\n",
+ h->h_field, val);
+ if (MilterLogLevel > 8)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter change: default header %s value with %s",
+ h->h_field, val);
h->h_value = newstr(val);
h->h_flags |= H_USER;
}
else
{
if (tTd(64, 10))
- dprintf("Add %s: %s\n", response, val);
- addheader(newstr(response), val, H_USER, &e->e_header);
+ sm_dprintf("Add %s: %s\n", response, val);
+ if (MilterLogLevel > 8)
+ sm_syslog(LOG_INFO, e->e_id, "Milter add: header: %s: %s",
+ response, val);
+ addheader(newstr(response), val, H_USER, e);
}
}
- /*
+/*
** MILTER_CHANGEHEADER -- Change the supplied header in the message
**
** Parameters:
@@ -2456,20 +2585,20 @@ milter_changeheader(response, rlen, e)
HDR *h, *sysheader;
if (tTd(64, 10))
- dprintf("milter_changeheader: ");
+ sm_dprintf("milter_changeheader: ");
/* sanity checks */
if (response == NULL)
{
if (tTd(64, 10))
- dprintf("NULL response\n");
+ sm_dprintf("NULL response\n");
return;
}
if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
{
if (tTd(64, 10))
- dprintf("didn't follow protocol (total len)\n");
+ sm_dprintf("didn't follow protocol (total len)\n");
return;
}
@@ -2484,21 +2613,21 @@ milter_changeheader(response, rlen, e)
strlen(val) + 1 != (size_t) rlen)
{
if (tTd(64, 10))
- dprintf("didn't follow protocol (part len)\n");
+ sm_dprintf("didn't follow protocol (part len)\n");
return;
}
if (*field == '\0')
{
if (tTd(64, 10))
- dprintf("empty field name\n");
+ sm_dprintf("empty field name\n");
return;
}
sysheader = NULL;
for (h = e->e_header; h != NULL; h = h->h_link)
{
- if (strcasecmp(h->h_field, field) == 0)
+ if (sm_strcasecmp(h->h_field, field) == 0)
{
if (bitset(H_USER, h->h_flags) &&
--index <= 0)
@@ -2530,15 +2659,14 @@ milter_changeheader(response, rlen, e)
if (*val == '\0')
{
if (tTd(64, 10))
- dprintf("Delete (noop) %s:\n", field);
+ sm_dprintf("Delete (noop) %s:\n", field);
}
else
{
/* treat modify value with no existing header as add */
if (tTd(64, 10))
- dprintf("Add %s: %s\n", field, val);
-
- addheader(newstr(field), val, H_USER, &e->e_header);
+ sm_dprintf("Add %s: %s\n", field, val);
+ addheader(newstr(field), val, H_USER, e);
}
return;
}
@@ -2547,32 +2675,67 @@ milter_changeheader(response, rlen, e)
{
if (*val == '\0')
{
- dprintf("Delete%s %s: %s\n",
- h == sysheader ? " (default header)" : "",
- field,
- h->h_value == NULL ? "<NULL>" : h->h_value);
+ sm_dprintf("Delete%s %s: %s\n",
+ h == sysheader ? " (default header)" : "",
+ field,
+ h->h_value == NULL ? "<NULL>" : h->h_value);
}
else
{
- dprintf("Change%s %s: from %s to %s\n",
- h == sysheader ? " (default header)" : "",
- field,
- h->h_value == NULL ? "<NULL>" : h->h_value,
- val);
+ sm_dprintf("Change%s %s: from %s to %s\n",
+ h == sysheader ? " (default header)" : "",
+ field,
+ h->h_value == NULL ? "<NULL>" : h->h_value,
+ val);
+ }
+ }
+
+ if (MilterLogLevel > 8)
+ {
+ if (*val == '\0')
+ {
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter delete: header %s %s: %s",
+ h == sysheader ? " (default header)" : "",
+ field,
+ h->h_value == NULL ? "<NULL>" : h->h_value);
+ }
+ else
+ {
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter change: header %s %s: from %s to %s",
+ h == sysheader ? " (default header)" : "",
+ field,
+ h->h_value == NULL ? "<NULL>" : h->h_value,
+ val);
}
}
if (h != sysheader && h->h_value != NULL)
{
- e->e_msgsize -= strlen(h->h_value);
- sm_free(h->h_value);
+ size_t l;
+
+ l = strlen(h->h_value);
+ if (l > e->e_msgsize)
+ e->e_msgsize = 0;
+ else
+ e->e_msgsize -= l;
+ /* rpool, don't free: sm_free(h->h_value); XXX */
}
if (*val == '\0')
{
/* Remove "Field: " from message size */
if (h != sysheader)
- e->e_msgsize -= strlen(h->h_field) + 2;
+ {
+ size_t l;
+
+ l = strlen(h->h_field) + 2;
+ if (l > e->e_msgsize)
+ e->e_msgsize = 0;
+ else
+ e->e_msgsize -= l;
+ }
h->h_value = NULL;
}
else
@@ -2582,7 +2745,7 @@ milter_changeheader(response, rlen, e)
e->e_msgsize += strlen(h->h_value);
}
}
- /*
+/*
** MILTER_ADDRCPT -- Add the supplied recipient to the message
**
** Parameters:
@@ -2601,13 +2764,13 @@ milter_addrcpt(response, rlen, e)
ENVELOPE *e;
{
if (tTd(64, 10))
- dprintf("milter_addrcpt: ");
+ sm_dprintf("milter_addrcpt: ");
/* sanity checks */
if (response == NULL)
{
if (tTd(64, 10))
- dprintf("NULL response\n");
+ sm_dprintf("NULL response\n");
return;
}
@@ -2615,17 +2778,19 @@ milter_addrcpt(response, rlen, e)
strlen(response) + 1 != (size_t) rlen)
{
if (tTd(64, 10))
- dprintf("didn't follow protocol (total len %d != rlen %d)\n",
- strlen(response), rlen -1);
+ sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
+ (int) strlen(response), (int) (rlen - 1));
return;
}
if (tTd(64, 10))
- dprintf("%s\n", response);
+ sm_dprintf("%s\n", response);
+ if (MilterLogLevel > 8)
+ sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response);
(void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e);
return;
}
- /*
+/*
** MILTER_DELRCPT -- Delete the supplied recipient from the message
**
** Parameters:
@@ -2644,13 +2809,13 @@ milter_delrcpt(response, rlen, e)
ENVELOPE *e;
{
if (tTd(64, 10))
- dprintf("milter_delrcpt: ");
+ sm_dprintf("milter_delrcpt: ");
/* sanity checks */
if (response == NULL)
{
if (tTd(64, 10))
- dprintf("NULL response\n");
+ sm_dprintf("NULL response\n");
return;
}
@@ -2658,17 +2823,20 @@ milter_delrcpt(response, rlen, e)
strlen(response) + 1 != (size_t) rlen)
{
if (tTd(64, 10))
- dprintf("didn't follow protocol (total len)\n");
+ sm_dprintf("didn't follow protocol (total len)\n");
return;
}
if (tTd(64, 10))
- dprintf("%s\n", response);
+ sm_dprintf("%s\n", response);
+ if (MilterLogLevel > 8)
+ sm_syslog(LOG_INFO, e->e_id, "Milter delete: rcpt %s",
+ response);
(void) removefromlist(response, &e->e_sendqueue, e);
return;
}
- /*
-** MILTER_REPLBODY -- Replace the current df file with new body
+/*
+** MILTER_REPLBODY -- Replace the current data file with new body
**
** Parameters:
** response -- encoded form of new body.
@@ -2691,51 +2859,88 @@ milter_replbody(response, rlen, newfilter, e)
int i;
if (tTd(64, 10))
- dprintf("milter_replbody\n");
+ sm_dprintf("milter_replbody\n");
- /* If a new filter, reset previous character and truncate df */
+ /* If a new filter, reset previous character and truncate data file */
if (newfilter)
{
off_t prevsize = 0;
char dfname[MAXPATHLEN];
- (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
+ (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER),
+ sizeof dfname);
/* Reset prevchar */
prevchar = '\0';
- /* Get the current df information */
+ /* Get the current data file information */
if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
{
int afd;
struct stat st;
- afd = fileno(e->e_dfp);
+ afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL);
if (afd > 0 && fstat(afd, &st) == 0)
prevsize = st.st_size;
}
- /* truncate current df file */
- if (bftruncate(e->e_dfp) < 0)
+ /* truncate current data file */
+ if (sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
{
- MILTER_DF_ERROR("milter_reopen_df: bftruncate %s: %s");
- return -1;
+ if (sm_io_setinfo(e->e_dfp, SM_BF_TRUNCATE, NULL) < 0)
+ {
+ MILTER_DF_ERROR("milter_replbody: sm_io truncate %s: %s");
+ return -1;
+ }
}
else
{
- if (prevsize > e->e_msgsize)
- e->e_msgsize = 0;
- else
- e->e_msgsize -= prevsize;
+ int err;
+
+# if NOFTRUNCATE
+ /* XXX: Not much we can do except rewind it */
+ err = sm_io_error(e->e_dfp);
+ (void) sm_io_flush(e->e_dfp, SM_TIME_DEFAULT);
+
+ /*
+ ** Clear error if tried to fflush()
+ ** a read-only file pointer and
+ ** there wasn't a previous error.
+ */
+
+ if (err == 0)
+ sm_io_clearerr(e->e_dfp);
+
+ /* errno is set implicitly by fseek() before return */
+ err = sm_io_seek(e->e_dfp, SM_TIME_DEFAULT,
+ 0, SEEK_SET);
+# else /* NOFTRUNCATE */
+ err = ftruncate(sm_io_getinfo(e->e_dfp,
+ SM_IO_WHAT_FD, NULL),
+ 0);
+# endif /* NOFTRUNCATE */
+ if (err < 0)
+ {
+ MILTER_DF_ERROR("milter_replbody: sm_io ftruncate %s: %s");
+ return -1;
+ }
}
+
+ if (prevsize > e->e_msgsize)
+ e->e_msgsize = 0;
+ else
+ e->e_msgsize -= prevsize;
}
+ if (newfilter && MilterLogLevel > 8)
+ sm_syslog(LOG_INFO, e->e_id, "Milter message: body replaced");
+
if (response == NULL)
{
/* Flush the buffered '\r' */
if (prevchar == '\r')
{
- (void) putc(prevchar, e->e_dfp);
+ (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, prevchar);
e->e_msgsize++;
}
return 0;
@@ -2749,7 +2954,8 @@ milter_replbody(response, rlen, newfilter, e)
/* Not CRLF, output prevchar */
if (response[i] != '\n')
{
- (void) putc(prevchar, e->e_dfp);
+ (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT,
+ prevchar);
e->e_msgsize++;
}
prevchar = '\0';
@@ -2772,7 +2978,7 @@ milter_replbody(response, rlen, newfilter, e)
continue;
}
}
- (void) putc(response[i], e->e_dfp);
+ (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, response[i]);
e->e_msgsize++;
}
return 0;
@@ -2782,7 +2988,7 @@ milter_replbody(response, rlen, newfilter, e)
** MTA callouts
*/
- /*
+/*
** MILTER_INIT -- open and negotiate with all of the filters
**
** Parameters:
@@ -2790,11 +2996,11 @@ milter_replbody(response, rlen, newfilter, e)
** state -- return state from response.
**
** Returns:
-** none
+** true iff at least one filter is active
*/
/* ARGSUSED */
-void
+bool
milter_init(e, state)
ENVELOPE *e;
char *state;
@@ -2802,14 +3008,22 @@ milter_init(e, state)
int i;
if (tTd(64, 10))
- dprintf("milter_init\n");
+ sm_dprintf("milter_init\n");
*state = SMFIR_CONTINUE;
+ if (InputFilters[0] == NULL)
+ {
+ if (MilterLogLevel > 10)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: no active filter");
+ return false;
+ }
+
for (i = 0; InputFilters[i] != NULL; i++)
{
struct milter *m = InputFilters[i];
- m->mf_sock = milter_open(m, FALSE, e);
+ m->mf_sock = milter_open(m, false, e);
if (m->mf_state == SMFS_ERROR)
{
MILTER_CHECK_ERROR(continue);
@@ -2821,14 +3035,26 @@ milter_init(e, state)
m->mf_state == SMFS_ERROR)
{
if (tTd(64, 5))
- dprintf("milter_init(%s): failed to %s\n",
- m->mf_name,
- m->mf_sock < 0 ? "open" : "negotiate");
+ sm_dprintf("milter_init(%s): failed to %s\n",
+ m->mf_name,
+ m->mf_sock < 0 ? "open" :
+ "negotiate");
+ if (MilterLogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "Milter (%s): init failed to %s",
+ m->mf_name,
+ m->mf_sock < 0 ? "open" :
+ "negotiate");
/* if negotation failure, close socket */
- milter_error(m);
+ milter_error(m, e);
MILTER_CHECK_ERROR(continue);
}
+ if (MilterLogLevel > 9)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter (%s): init success to %s",
+ m->mf_name,
+ m->mf_sock < 0 ? "open" : "negotiate");
}
/*
@@ -2839,8 +3065,10 @@ milter_init(e, state)
if (*state != SMFIR_CONTINUE)
milter_quit(e);
+
+ return true;
}
- /*
+/*
** MILTER_CONNECT -- send connection info to milter filters
**
** Parameters:
@@ -2861,7 +3089,7 @@ milter_connect(hostname, addr, e, state)
char *state;
{
char family;
- u_short port;
+ unsigned short port;
char *buf, *bp;
char *response;
char *sockinfo = NULL;
@@ -2871,7 +3099,9 @@ milter_connect(hostname, addr, e, state)
# endif /* NETINET6 */
if (tTd(64, 10))
- dprintf("milter_connect(%s)\n", hostname);
+ sm_dprintf("milter_connect(%s)\n", hostname);
+ if (MilterLogLevel > 9)
+ sm_syslog(LOG_INFO, e->e_id, "Milter: connect to filters");
/* gather data */
switch (addr.sa.sa_family)
@@ -2915,7 +3145,7 @@ milter_connect(hostname, addr, e, state)
if (family != SMFIA_UNKNOWN)
s += sizeof(port) + strlen(sockinfo) + 1;
- buf = (char *)xalloc(s);
+ buf = (char *) xalloc(s);
bp = buf;
/* put together data */
@@ -2935,7 +3165,7 @@ milter_connect(hostname, addr, e, state)
response = milter_command(SMFIC_CONNECT, buf, s,
MilterConnectMacros, e, state);
- sm_free(buf);
+ sm_free(buf); /* XXX */
/*
** If this message connection is done for,
@@ -2943,7 +3173,11 @@ milter_connect(hostname, addr, e, state)
*/
if (*state != SMFIR_CONTINUE)
+ {
+ if (MilterLogLevel > 9)
+ sm_syslog(LOG_INFO, e->e_id, "Milter: connect, ending");
milter_quit(e);
+ }
else
milter_per_connection_check(e);
@@ -2962,13 +3196,13 @@ milter_connect(hostname, addr, e, state)
*state = SMFIR_REJECT;
if (response != NULL)
{
- sm_free(response);
+ sm_free(response); /* XXX */
response = NULL;
}
}
return response;
}
- /*
+/*
** MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters
**
** Parameters:
@@ -2990,9 +3224,9 @@ milter_helo(helo, e, state)
char *response;
if (tTd(64, 10))
- dprintf("milter_helo(%s)\n", helo);
+ sm_dprintf("milter_helo(%s)\n", helo);
- /* HELO/EHLO can come after encryption is negotiated */
+ /* HELO/EHLO can come at any point */
for (i = 0; InputFilters[i] != NULL; i++)
{
struct milter *m = InputFilters[i];
@@ -3016,7 +3250,7 @@ milter_helo(helo, e, state)
milter_per_connection_check(e);
return response;
}
- /*
+/*
** MILTER_ENVFROM -- send SMTP MAIL command info to milter filters
**
** Parameters:
@@ -3041,16 +3275,19 @@ milter_envfrom(args, e, state)
if (tTd(64, 10))
{
- dprintf("milter_envfrom:");
+ sm_dprintf("milter_envfrom:");
for (i = 0; args[i] != NULL; i++)
- dprintf(" %s", args[i]);
- dprintf("\n");
+ sm_dprintf(" %s", args[i]);
+ sm_dprintf("\n");
}
/* sanity check */
if (args[0] == NULL)
{
*state = SMFIR_REJECT;
+ if (MilterLogLevel > 10)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: reject, no sender");
return NULL;
}
@@ -3077,18 +3314,28 @@ milter_envfrom(args, e, state)
s = 0;
for (i = 0; args[i] != NULL; i++)
s += strlen(args[i]) + 1;
- buf = (char *)xalloc(s);
+
+ if (s < 0)
+ {
+ *state = SMFIR_TEMPFAIL;
+ return NULL;
+ }
+
+ buf = (char *) xalloc(s);
bp = buf;
for (i = 0; args[i] != NULL; i++)
{
- (void) strlcpy(bp, args[i], s - (bp - buf));
+ (void) sm_strlcpy(bp, args[i], s - (bp - buf));
bp += strlen(bp) + 1;
}
+ if (MilterLogLevel > 14)
+ sm_syslog(LOG_INFO, e->e_id, "Milter: senders: %s", buf);
+
/* send it over */
response = milter_command(SMFIC_MAIL, buf, s,
MilterEnvFromMacros, e, state);
- sm_free(buf);
+ sm_free(buf); /* XXX */
/*
** If filter rejects/discards a per message command,
@@ -3097,9 +3344,11 @@ milter_envfrom(args, e, state)
*/
MILTER_CHECK_DONE_MSG();
+ if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
+ sm_syslog(LOG_INFO, e->e_id, "Milter: reject, senders");
return response;
}
- /*
+/*
** MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters
**
** Parameters:
@@ -3124,16 +3373,18 @@ milter_envrcpt(args, e, state)
if (tTd(64, 10))
{
- dprintf("milter_envrcpt:");
+ sm_dprintf("milter_envrcpt:");
for (i = 0; args[i] != NULL; i++)
- dprintf(" %s", args[i]);
- dprintf("\n");
+ sm_dprintf(" %s", args[i]);
+ sm_dprintf("\n");
}
/* sanity check */
if (args[0] == NULL)
{
*state = SMFIR_REJECT;
+ if (MilterLogLevel > 10)
+ sm_syslog(LOG_INFO, e->e_id, "Milter: reject, no rcpt");
return NULL;
}
@@ -3141,21 +3392,31 @@ milter_envrcpt(args, e, state)
s = 0;
for (i = 0; args[i] != NULL; i++)
s += strlen(args[i]) + 1;
- buf = (char *)xalloc(s);
+
+ if (s < 0)
+ {
+ *state = SMFIR_TEMPFAIL;
+ return NULL;
+ }
+
+ buf = (char *) xalloc(s);
bp = buf;
for (i = 0; args[i] != NULL; i++)
{
- (void) strlcpy(bp, args[i], s - (bp - buf));
+ (void) sm_strlcpy(bp, args[i], s - (bp - buf));
bp += strlen(bp) + 1;
}
+ if (MilterLogLevel > 14)
+ sm_syslog(LOG_INFO, e->e_id, "Milter: rcpts: %s", buf);
+
/* send it over */
response = milter_command(SMFIC_RCPT, buf, s,
MilterEnvRcptMacros, e, state);
- sm_free(buf);
+ sm_free(buf); /* XXX */
return response;
}
- /*
+/*
** MILTER_DATA -- send message headers/body and gather final message results
**
** Parameters:
@@ -3191,10 +3452,10 @@ milter_data(e, state)
ENVELOPE *e;
char *state;
{
- bool replbody = FALSE; /* milter_replbody() called? */
- bool replfailed = FALSE; /* milter_replbody() failed? */
- bool rewind = FALSE; /* rewind df file? */
- bool dfopen = FALSE; /* df open for writing? */
+ bool replbody = false; /* milter_replbody() called? */
+ bool replfailed = false; /* milter_replbody() failed? */
+ bool rewind = false; /* rewind data file? */
+ bool dfopen = false; /* data file open for writing? */
bool newfilter; /* reset on each new filter */
char rcmd;
int i;
@@ -3204,7 +3465,7 @@ milter_data(e, state)
ssize_t rlen;
if (tTd(64, 10))
- dprintf("milter_data\n");
+ sm_dprintf("milter_data\n");
*state = SMFIR_CONTINUE;
@@ -3232,7 +3493,7 @@ milter_data(e, state)
/* Now reset state for later evaluation */
*state = SMFIR_CONTINUE;
- newfilter = TRUE;
+ newfilter = true;
/* previous problem? */
if (m->mf_state == SMFS_ERROR)
@@ -3259,7 +3520,7 @@ milter_data(e, state)
if (!bitset(SMFIP_NOEOH, m->mf_pflags))
{
if (tTd(64, 10))
- dprintf("milter_data: eoh\n");
+ sm_dprintf("milter_data: eoh\n");
/* send it over */
response = milter_send_command(m, SMFIC_EOH, NULL, 0,
@@ -3271,7 +3532,7 @@ milter_data(e, state)
if (!bitset(SMFIP_NOBODY, m->mf_pflags) &&
e->e_dfp != NULL)
{
- rewind = TRUE;
+ rewind = true;
response = milter_body(m, e, state);
MILTER_CHECK_RESULTS();
}
@@ -3291,13 +3552,13 @@ milter_data(e, state)
curtime() - eomsent >= m->mf_timeout[SMFTO_EOM])
{
if (tTd(64, 5))
- dprintf("milter_data(%s): EOM ACK/NAK timeout\n",
+ sm_dprintf("milter_data(%s): EOM ACK/NAK timeout\n",
m->mf_name);
- if (LogLevel > 0)
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
- "milter_data(%s): EOM ACK/NAK timeout\n",
+ "milter_data(%s): EOM ACK/NAK timeout",
m->mf_name);
- milter_error(m);
+ milter_error(m, e);
MILTER_CHECK_ERROR(continue);
break;
}
@@ -3308,20 +3569,40 @@ milter_data(e, state)
break;
if (tTd(64, 10))
- dprintf("milter_data(%s): state %c\n",
- m->mf_name, (char) rcmd);
+ sm_dprintf("milter_data(%s): state %c\n",
+ m->mf_name, (char) rcmd);
switch (rcmd)
{
case SMFIR_REPLYCODE:
MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected");
+ if (MilterLogLevel > 12)
+ sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s",
+ m->mf_name, response);
+ *state = rcmd;
+ m->mf_state = SMFS_DONE;
+ break;
+
+ case SMFIR_REJECT: /* log msg at end of function */
+ if (MilterLogLevel > 12)
+ sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject",
+ m->mf_name);
*state = rcmd;
m->mf_state = SMFS_DONE;
break;
- case SMFIR_REJECT:
case SMFIR_DISCARD:
+ if (MilterLogLevel > 12)
+ sm_syslog(LOG_INFO, e->e_id, "milter=%s, discard",
+ m->mf_name);
+ *state = rcmd;
+ m->mf_state = SMFS_DONE;
+ break;
+
case SMFIR_TEMPFAIL:
+ if (MilterLogLevel > 12)
+ sm_syslog(LOG_INFO, e->e_id, "milter=%s, tempfail",
+ m->mf_name);
*state = rcmd;
m->mf_state = SMFS_DONE;
break;
@@ -3339,10 +3620,32 @@ milter_data(e, state)
case SMFIR_PROGRESS:
break;
+# if _FFR_QUARANTINE
+ case SMFIR_QUARANTINE:
+ if (!bitset(SMFIF_QUARANTINE, m->mf_fflags))
+ {
+ if (MilterLogLevel > 9)
+ sm_syslog(LOG_WARNING, e->e_id,
+ "milter_data(%s): lied about quarantining, honoring request anyway",
+ m->mf_name);
+ }
+ if (response == NULL)
+ response = newstr("");
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "milter=%s, quarantine=%s",
+ m->mf_name, response);
+ e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
+ response);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{quarantine}"), e->e_quarmsg);
+ break;
+# endif /* _FFR_QUARANTINE */
+
case SMFIR_ADDHEADER:
if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
{
- if (LogLevel > 9)
+ if (MilterLogLevel > 9)
sm_syslog(LOG_WARNING, e->e_id,
"milter_data(%s): lied about adding headers, honoring request anyway",
m->mf_name);
@@ -3353,7 +3656,7 @@ milter_data(e, state)
case SMFIR_CHGHEADER:
if (!bitset(SMFIF_CHGHDRS, m->mf_fflags))
{
- if (LogLevel > 9)
+ if (MilterLogLevel > 9)
sm_syslog(LOG_WARNING, e->e_id,
"milter_data(%s): lied about changing headers, honoring request anyway",
m->mf_name);
@@ -3364,7 +3667,7 @@ milter_data(e, state)
case SMFIR_ADDRCPT:
if (!bitset(SMFIF_ADDRCPT, m->mf_fflags))
{
- if (LogLevel > 9)
+ if (MilterLogLevel > 9)
sm_syslog(LOG_WARNING, e->e_id,
"milter_data(%s) lied about adding recipients, honoring request anyway",
m->mf_name);
@@ -3375,7 +3678,7 @@ milter_data(e, state)
case SMFIR_DELRCPT:
if (!bitset(SMFIF_DELRCPT, m->mf_fflags))
{
- if (LogLevel > 9)
+ if (MilterLogLevel > 9)
sm_syslog(LOG_WARNING, e->e_id,
"milter_data(%s): lied about removing recipients, honoring request anyway",
m->mf_name);
@@ -3386,11 +3689,11 @@ milter_data(e, state)
case SMFIR_REPLBODY:
if (!bitset(SMFIF_MODBODY, m->mf_fflags))
{
- if (LogLevel > 0)
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
"milter_data(%s): lied about replacing body, rejecting request and tempfailing message",
m->mf_name);
- replfailed = TRUE;
+ replfailed = true;
break;
}
@@ -3402,33 +3705,32 @@ milter_data(e, state)
{
if (milter_reopen_df(e) < 0)
{
- replfailed = TRUE;
+ replfailed = true;
break;
}
- dfopen = TRUE;
- rewind = TRUE;
+ dfopen = true;
+ rewind = true;
}
if (milter_replbody(response, rlen,
newfilter, e) < 0)
- replfailed = TRUE;
- newfilter = FALSE;
- replbody = TRUE;
+ replfailed = true;
+ newfilter = false;
+ replbody = true;
break;
default:
/* Invalid response to command */
- if (LogLevel > 0)
+ if (MilterLogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
"milter_data(%s): returned bogus response %c",
m->mf_name, rcmd);
- milter_error(m);
+ milter_error(m, e);
break;
}
- if (rcmd != SMFIR_REPLYCODE &&
- response != NULL)
+ if (rcmd != SMFIR_REPLYCODE && response != NULL)
{
- sm_free(response);
+ sm_free(response); /* XXX */
response = NULL;
}
@@ -3440,7 +3742,7 @@ milter_data(e, state)
{
/* flush possible buffered character */
milter_replbody(NULL, 0, !replbody, e);
- replbody = FALSE;
+ replbody = false;
}
if (m->mf_state == SMFS_ERROR)
@@ -3458,21 +3760,17 @@ finishup:
*state == SMFIR_ACCEPT)
{
*state = SMFIR_TEMPFAIL;
- if (response != NULL)
- {
- sm_free(response);
- response = NULL;
- }
+ SM_FREE_CLR(response);
}
if (dfopen)
{
- (void) fclose(e->e_dfp);
+ (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
e->e_dfp = NULL;
e->e_flags &= ~EF_HAS_DF;
- dfopen = FALSE;
+ dfopen = false;
}
- rewind = FALSE;
+ rewind = false;
}
if ((dfopen && milter_reset_df(e) < 0) ||
@@ -3490,21 +3788,21 @@ finishup:
*state == SMFIR_ACCEPT)
{
*state = SMFIR_TEMPFAIL;
- if (response != NULL)
- {
- sm_free(response);
- response = NULL;
- }
+ SM_FREE_CLR(response);
}
errno = save_errno;
- syserr("milter_data: %s/df%s: read error",
- qid_printqueue(e->e_queuedir), e->e_id);
+ syserr("milter_data: %s/%cf%s: read error",
+ qid_printqueue(e->e_qgrp, e->e_qdir),
+ DATAFL_LETTER, e->e_id);
}
+
MILTER_CHECK_DONE_MSG();
+ if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
+ sm_syslog(LOG_INFO, e->e_id, "Milter: reject, data");
return response;
}
- /*
+/*
** MILTER_QUIT -- informs the filter(s) we are done and closes connection(s)
**
** Parameters:
@@ -3521,12 +3819,12 @@ milter_quit(e)
int i;
if (tTd(64, 10))
- dprintf("milter_quit\n");
+ sm_dprintf("milter_quit(%s)\n", e->e_id);
for (i = 0; InputFilters[i] != NULL; i++)
milter_quit_filter(InputFilters[i], e);
}
- /*
+/*
** MILTER_ABORT -- informs the filter(s) that we are aborting current message
**
** Parameters:
@@ -3543,7 +3841,7 @@ milter_abort(e)
int i;
if (tTd(64, 10))
- dprintf("milter_abort\n");
+ sm_dprintf("milter_abort\n");
for (i = 0; InputFilters[i] != NULL; i++)
{
@@ -3556,4 +3854,4 @@ milter_abort(e)
milter_abort_filter(m, e);
}
}
-#endif /* _FFR_MILTER */
+#endif /* MILTER */
diff --git a/contrib/sendmail/src/mime.c b/contrib/sendmail/src/mime.c
index 2ba078f..f5980bb 100644
--- a/contrib/sendmail/src/mime.c
+++ b/contrib/sendmail/src/mime.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1994, 1996-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1994
@@ -14,15 +14,7 @@
#include <sendmail.h>
#include <string.h>
-#ifndef lint
-static char id[] = "@(#)$Id: mime.c,v 8.94.16.3 2000/10/09 02:46:10 gshapiro Exp $";
-#endif /* ! lint */
-
-static int isboundary __P((char *, char **));
-static int mimeboundary __P((char *, char **));
-static int mime_fromqp __P((u_char *, u_char **, int, int));
-static int mime_getchar __P((FILE *, char **, int *));
-static int mime_getchar_crlf __P((FILE *, char **, int *));
+SM_RCSID("@(#)$Id: mime.c,v 8.125 2001/09/11 04:05:15 gshapiro Exp $")
/*
** MIME support.
@@ -42,6 +34,10 @@ static int mime_getchar_crlf __P((FILE *, char **, int *));
*/
#if MIME8TO7
+static int isboundary __P((char *, char **));
+static int mimeboundary __P((char *, char **));
+static int mime_getchar __P((SM_FILE_T *, char **, int *));
+static int mime_getchar_crlf __P((SM_FILE_T *, char **, int *));
/* character set for hex and base64 encoding */
static char Base16Code[] = "0123456789ABCDEF";
@@ -60,7 +56,7 @@ static char *MimeBoundaryNames[] =
static bool MapNLtoCRLF;
- /*
+/*
** MIME8TO7 -- output 8 bit body in 7 bit format
**
** The header has already been output -- this has to do the
@@ -113,26 +109,26 @@ mime8to7(mci, header, e, boundaries, flags)
char **pvp;
int argc = 0;
char *bp;
- bool use_qp = FALSE;
+ bool use_qp = false;
struct args argv[MAXMIMEARGS];
char bbuf[128];
char buf[MAXLINE];
char pvpbuf[MAXLINE];
- extern u_char MimeTokenTab[256];
+ extern unsigned char MimeTokenTab[256];
if (tTd(43, 1))
{
- dprintf("mime8to7: flags = %x, boundaries =", flags);
+ sm_dprintf("mime8to7: flags = %x, boundaries =", flags);
if (boundaries[0] == NULL)
- dprintf(" <none>");
+ sm_dprintf(" <none>");
else
{
for (i = 0; boundaries[i] != NULL; i++)
- dprintf(" %s", boundaries[i]);
+ sm_dprintf(" %s", boundaries[i]);
}
- dprintf("\n");
+ sm_dprintf("\n");
}
- MapNLtoCRLF = TRUE;
+ MapNLtoCRLF = true;
p = hvalue("Content-Transfer-Encoding", header);
if (p == NULL ||
(pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL,
@@ -144,7 +140,7 @@ mime8to7(mci, header, e, boundaries, flags)
else
{
cataddr(pvp, NULL, buf, sizeof buf, '\0');
- cte = newstr(buf);
+ cte = sm_rpool_strdup_x(e->e_rpool, buf);
}
type = subtype = NULL;
@@ -164,7 +160,7 @@ mime8to7(mci, header, e, boundaries, flags)
if (tTd(43, 40))
{
for (i = 0; pvp[i] != NULL; i++)
- dprintf("pvp[%d] = \"%s\"\n", i, pvp[i]);
+ sm_dprintf("pvp[%d] = \"%s\"\n", i, pvp[i]);
}
type = *pvp++;
if (*pvp != NULL && strcmp(*pvp, "/") == 0 &&
@@ -222,16 +218,16 @@ mime8to7(mci, header, e, boundaries, flags)
** just copy it through.
*/
- snprintf(buf, sizeof buf, "%.100s/%.100s", type, subtype);
+ (void) sm_snprintf(buf, sizeof buf, "%.100s/%.100s", type, subtype);
if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e')))
flags |= M87F_NO8BIT;
# ifdef USE_B_CLASS
if (wordinclass(buf, 'b') || wordinclass(type, 'b'))
- MapNLtoCRLF = FALSE;
+ MapNLtoCRLF = false;
# endif /* USE_B_CLASS */
if (wordinclass(buf, 'q') || wordinclass(type, 'q'))
- use_qp = TRUE;
+ use_qp = true;
/*
** Multipart requires special processing.
@@ -239,16 +235,16 @@ mime8to7(mci, header, e, boundaries, flags)
** Do a recursive descent into the message.
*/
- if (strcasecmp(type, "multipart") == 0 &&
+ if (sm_strcasecmp(type, "multipart") == 0 &&
(!bitset(M87F_NO8BIT, flags) || bitset(M87F_NO8TO7, flags)))
{
- if (strcasecmp(subtype, "digest") == 0)
+ if (sm_strcasecmp(subtype, "digest") == 0)
flags |= M87F_DIGEST;
for (i = 0; i < argc; i++)
{
- if (strcasecmp(argv[i].a_field, "boundary") == 0)
+ if (sm_strcasecmp(argv[i].a_field, "boundary") == 0)
break;
}
if (i >= argc || argv[i].a_value == NULL)
@@ -265,7 +261,7 @@ mime8to7(mci, header, e, boundaries, flags)
p = argv[i].a_value;
stripquotes(p);
}
- if (strlcpy(bbuf, p, sizeof bbuf) >= sizeof bbuf)
+ if (sm_strlcpy(bbuf, p, sizeof bbuf) >= sizeof bbuf)
{
usrerr("mime8to7: multipart boundary \"%s\" too long",
p);
@@ -275,7 +271,8 @@ mime8to7(mci, header, e, boundaries, flags)
}
if (tTd(43, 1))
- dprintf("mime8to7: multipart boundary \"%s\"\n", bbuf);
+ sm_dprintf("mime8to7: multipart boundary \"%s\"\n",
+ bbuf);
for (i = 0; i < MAXMIMENESTING; i++)
{
if (boundaries[i] == NULL)
@@ -299,26 +296,28 @@ mime8to7(mci, header, e, boundaries, flags)
putline("", mci);
mci->mci_flags &= ~MCIF_INHEADER;
bt = MBT_FINAL;
- while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
+ while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof buf)
+ != NULL)
{
bt = mimeboundary(buf, boundaries);
if (bt != MBT_NOTSEP)
break;
- putxline(buf, strlen(buf), mci, PXLF_MAPFROM|PXLF_STRIP8BIT);
+ putxline(buf, strlen(buf), mci,
+ PXLF_MAPFROM|PXLF_STRIP8BIT);
if (tTd(43, 99))
- dprintf(" ...%s", buf);
+ sm_dprintf(" ...%s", buf);
}
- if (feof(e->e_dfp))
+ if (sm_io_eof(e->e_dfp))
bt = MBT_FINAL;
while (bt != MBT_FINAL)
{
auto HDR *hdr = NULL;
- snprintf(buf, sizeof buf, "--%s", bbuf);
+ (void) sm_strlcpyn(buf, sizeof buf, 2, "--", bbuf);
putline(buf, mci);
if (tTd(43, 35))
- dprintf(" ...%s\n", buf);
- collect(e->e_dfp, FALSE, &hdr, e);
+ sm_dprintf(" ...%s\n", buf);
+ collect(e->e_dfp, false, &hdr, e);
if (tTd(43, 101))
putline("+++after collect", mci);
putheader(mci, hdr, e, flags);
@@ -326,27 +325,29 @@ mime8to7(mci, header, e, boundaries, flags)
putline("+++after putheader", mci);
bt = mime8to7(mci, hdr, e, boundaries, flags);
}
- snprintf(buf, sizeof buf, "--%s--", bbuf);
+ (void) sm_strlcpyn(buf, sizeof buf, 3, "--", bbuf, "--");
putline(buf, mci);
if (tTd(43, 35))
- dprintf(" ...%s\n", buf);
+ sm_dprintf(" ...%s\n", buf);
boundaries[i] = NULL;
mci->mci_flags &= ~MCIF_INMIME;
/* skip the late "comment" epilogue */
- while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
+ while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof buf)
+ != NULL)
{
bt = mimeboundary(buf, boundaries);
if (bt != MBT_NOTSEP)
break;
- putxline(buf, strlen(buf), mci, PXLF_MAPFROM|PXLF_STRIP8BIT);
+ putxline(buf, strlen(buf), mci,
+ PXLF_MAPFROM|PXLF_STRIP8BIT);
if (tTd(43, 99))
- dprintf(" ...%s", buf);
+ sm_dprintf(" ...%s", buf);
}
- if (feof(e->e_dfp))
+ if (sm_io_eof(e->e_dfp))
bt = MBT_FINAL;
if (tTd(43, 3))
- dprintf("\t\t\tmime8to7=>%s (multipart)\n",
+ sm_dprintf("\t\t\tmime8to7=>%s (multipart)\n",
MimeBoundaryNames[bt]);
return bt;
}
@@ -357,7 +358,7 @@ mime8to7(mci, header, e, boundaries, flags)
** Class 's' is predefined to have "rfc822" only.
*/
- if (strcasecmp(type, "message") == 0)
+ if (sm_strcasecmp(type, "message") == 0)
{
if (!wordinclass(subtype, 's'))
{
@@ -370,7 +371,7 @@ mime8to7(mci, header, e, boundaries, flags)
putline("", mci);
mci->mci_flags |= MCIF_INMIME;
- collect(e->e_dfp, FALSE, &hdr, e);
+ collect(e->e_dfp, false, &hdr, e);
if (tTd(43, 101))
putline("+++after collect", mci);
putheader(mci, hdr, e, flags);
@@ -396,12 +397,14 @@ mime8to7(mci, header, e, boundaries, flags)
if (!bitset(M87F_NO8BIT|M87F_NO8TO7, flags))
{
/* remember where we were */
- offset = ftell(e->e_dfp);
+ offset = sm_io_tell(e->e_dfp, SM_TIME_DEFAULT);
if (offset == -1)
- syserr("mime8to7: cannot ftell on df%s", e->e_id);
+ syserr("mime8to7: cannot sm_io_tell on %cf%s",
+ DATAFL_LETTER, e->e_id);
/* do a scan of this body type to count character types */
- while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
+ while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof buf)
+ != NULL)
{
if (mimeboundary(buf, boundaries) != MBT_NOTSEP)
break;
@@ -426,10 +429,11 @@ mime8to7(mci, header, e, boundaries, flags)
/* return to the original offset for processing */
/* XXX use relative seeks to handle >31 bit file sizes? */
- if (fseek(e->e_dfp, offset, SEEK_SET) < 0)
- syserr("mime8to7: cannot fseek on df%s", e->e_id);
+ if (sm_io_seek(e->e_dfp, SM_TIME_DEFAULT, offset, SEEK_SET) < 0)
+ syserr("mime8to7: cannot sm_io_fseek on %cf%s",
+ DATAFL_LETTER, e->e_id);
else
- clearerr(e->e_dfp);
+ sm_io_clearerr(e->e_dfp);
}
/*
@@ -442,13 +446,13 @@ mime8to7(mci, header, e, boundaries, flags)
if (tTd(43, 8))
{
- dprintf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s, type=%s/%s\n",
+ sm_dprintf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s, type=%s/%s\n",
(long) sectionhighbits, (long) sectionsize,
cte == NULL ? "[none]" : cte,
type == NULL ? "[none]" : type,
subtype == NULL ? "[none]" : subtype);
}
- if (cte != NULL && strcasecmp(cte, "binary") == 0)
+ if (cte != NULL && sm_strcasecmp(cte, "binary") == 0)
sectionsize = sectionhighbits;
linelen = 0;
bp = buf;
@@ -468,22 +472,23 @@ mime8to7(mci, header, e, boundaries, flags)
** situation.
*/
- snprintf(buf, sizeof buf,
+ (void) sm_snprintf(buf, sizeof buf,
"Content-Transfer-Encoding: %.200s", cte);
putline(buf, mci);
if (tTd(43, 36))
- dprintf(" ...%s\n", buf);
+ sm_dprintf(" ...%s\n", buf);
}
putline("", mci);
mci->mci_flags &= ~MCIF_INHEADER;
- while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
+ while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof buf)
+ != NULL)
{
bt = mimeboundary(buf, boundaries);
if (bt != MBT_NOTSEP)
break;
putline(buf, mci);
}
- if (feof(e->e_dfp))
+ if (sm_io_eof(e->e_dfp))
bt = MBT_FINAL;
}
else if (!MapNLtoCRLF ||
@@ -493,15 +498,16 @@ mime8to7(mci, header, e, boundaries, flags)
int c1, c2;
if (tTd(43, 36))
- dprintf(" ...Content-Transfer-Encoding: base64\n");
+ sm_dprintf(" ...Content-Transfer-Encoding: base64\n");
putline("Content-Transfer-Encoding: base64", mci);
- snprintf(buf, sizeof buf,
+ (void) sm_snprintf(buf, sizeof buf,
"X-MIME-Autoconverted: from 8bit to base64 by %s id %s",
MyHostName, e->e_id);
putline(buf, mci);
putline("", mci);
mci->mci_flags &= ~MCIF_INHEADER;
- while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != EOF)
+ while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) !=
+ SM_IO_EOF)
{
if (linelen > 71)
{
@@ -514,7 +520,7 @@ mime8to7(mci, header, e, boundaries, flags)
*bp++ = Base64Code[(c1 >> 2)];
c1 = (c1 & 0x03) << 4;
c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt);
- if (c2 == EOF)
+ if (c2 == SM_IO_EOF)
{
*bp++ = Base64Code[c1];
*bp++ = '=';
@@ -525,7 +531,7 @@ mime8to7(mci, header, e, boundaries, flags)
*bp++ = Base64Code[c1];
c1 = (c2 & 0x0f) << 2;
c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt);
- if (c2 == EOF)
+ if (c2 == SM_IO_EOF)
{
*bp++ = Base64Code[c1];
*bp++ = '=';
@@ -558,9 +564,9 @@ mime8to7(mci, header, e, boundaries, flags)
setbitn(*p, badchars);
if (tTd(43, 36))
- dprintf(" ...Content-Transfer-Encoding: quoted-printable\n");
+ sm_dprintf(" ...Content-Transfer-Encoding: quoted-printable\n");
putline("Content-Transfer-Encoding: quoted-printable", mci);
- snprintf(buf, sizeof buf,
+ (void) sm_snprintf(buf, sizeof buf,
"X-MIME-Autoconverted: from 8bit to quoted-printable by %s id %s",
MyHostName, e->e_id);
putline(buf, mci);
@@ -568,7 +574,8 @@ mime8to7(mci, header, e, boundaries, flags)
mci->mci_flags &= ~MCIF_INHEADER;
fromstate = 0;
c2 = '\n';
- while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF)
+ while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) !=
+ SM_IO_EOF)
{
if (c1 == '\n')
{
@@ -657,18 +664,18 @@ mime8to7(mci, header, e, boundaries, flags)
}
if (tTd(43, 3))
- dprintf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]);
+ sm_dprintf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]);
return bt;
}
- /*
+/*
** MIME_GETCHAR -- get a character for MIME processing
**
-** Treats boundaries as EOF.
+** Treats boundaries as SM_IO_EOF.
**
** Parameters:
** fp -- the input file.
** boundaries -- the current MIME boundaries.
-** btp -- if the return value is EOF, *btp is set to
+** btp -- if the return value is SM_IO_EOF, *btp is set to
** the type of the boundary.
**
** Returns:
@@ -677,16 +684,16 @@ mime8to7(mci, header, e, boundaries, flags)
static int
mime_getchar(fp, boundaries, btp)
- register FILE *fp;
+ register SM_FILE_T *fp;
char **boundaries;
int *btp;
{
int c;
- static u_char *bp = NULL;
+ static unsigned char *bp = NULL;
static int buflen = 0;
- static bool atbol = TRUE; /* at beginning of line */
- static int bt = MBT_SYNTAX; /* boundary type of next EOF */
- static u_char buf[128]; /* need not be a full line */
+ static bool atbol = true; /* at beginning of line */
+ static int bt = MBT_SYNTAX; /* boundary type of next SM_IO_EOF */
+ static unsigned char buf[128]; /* need not be a full line */
int start = 0; /* indicates position of - in buffer */
if (buflen == 1 && *bp == '\n')
@@ -700,33 +707,33 @@ mime_getchar(fp, boundaries, btp)
return *bp++;
}
else
- c = getc(fp);
+ c = sm_io_getc(fp, SM_TIME_DEFAULT);
bp = buf;
buflen = 0;
if (c == '\n')
{
/* might be part of a MIME boundary */
*bp++ = c;
- atbol = TRUE;
- c = getc(fp);
+ atbol = true;
+ c = sm_io_getc(fp, SM_TIME_DEFAULT);
if (c == '\n')
{
- (void) ungetc(c, fp);
+ (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c);
return c;
}
start = 1;
}
- if (c != EOF)
+ if (c != SM_IO_EOF)
*bp++ = c;
else
bt = MBT_FINAL;
if (atbol && c == '-')
{
/* check for a message boundary */
- c = getc(fp);
+ c = sm_io_getc(fp, SM_TIME_DEFAULT);
if (c != '-')
{
- if (c != EOF)
+ if (c != SM_IO_EOF)
*bp++ = c;
else
bt = MBT_FINAL;
@@ -738,11 +745,12 @@ mime_getchar(fp, boundaries, btp)
/* got "--", now check for rest of separator */
*bp++ = '-';
while (bp < &buf[sizeof buf - 2] &&
- (c = getc(fp)) != EOF && c != '\n')
+ (c = sm_io_getc(fp, SM_TIME_DEFAULT)) != SM_IO_EOF &&
+ c != '\n')
{
*bp++ = c;
}
- *bp = '\0';
+ *bp = '\0'; /* XXX simply cut off? */
bt = mimeboundary((char *) &buf[start], boundaries);
switch (bt)
{
@@ -751,11 +759,11 @@ mime_getchar(fp, boundaries, btp)
/* we have a message boundary */
buflen = 0;
*btp = bt;
- return EOF;
+ return SM_IO_EOF;
}
atbol = c == '\n';
- if (c != EOF)
+ if (c != SM_IO_EOF)
*bp++ = c;
}
@@ -763,18 +771,18 @@ mime_getchar(fp, boundaries, btp)
if (buflen < 0)
{
*btp = bt;
- return EOF;
+ return SM_IO_EOF;
}
bp = buf;
return *bp++;
}
- /*
+/*
** MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF
**
** Parameters:
** fp -- the input file.
** boundaries -- the current MIME boundaries.
-** btp -- if the return value is EOF, *btp is set to
+** btp -- if the return value is SM_IO_EOF, *btp is set to
** the type of the boundary.
**
** Returns:
@@ -783,27 +791,27 @@ mime_getchar(fp, boundaries, btp)
static int
mime_getchar_crlf(fp, boundaries, btp)
- register FILE *fp;
+ register SM_FILE_T *fp;
char **boundaries;
int *btp;
{
- static bool sendlf = FALSE;
+ static bool sendlf = false;
int c;
if (sendlf)
{
- sendlf = FALSE;
+ sendlf = false;
return '\n';
}
c = mime_getchar(fp, boundaries, btp);
if (c == '\n' && MapNLtoCRLF)
{
- sendlf = TRUE;
+ sendlf = true;
return '\r';
}
return c;
}
- /*
+/*
** MIMEBOUNDARY -- determine if this line is a MIME boundary & its type
**
** Parameters:
@@ -840,7 +848,7 @@ mimeboundary(line, boundaries)
line[i] = '\0';
if (tTd(43, 5))
- dprintf("mimeboundary: line=\"%s\"... ", line);
+ sm_dprintf("mimeboundary: line=\"%s\"... ", line);
/* check for this as an intermediate boundary */
if (isboundary(&line[2], boundaries) >= 0)
@@ -856,10 +864,10 @@ mimeboundary(line, boundaries)
line[i] = savec;
if (tTd(43, 5))
- dprintf("%s\n", MimeBoundaryNames[type]);
+ sm_dprintf("%s\n", MimeBoundaryNames[type]);
return type;
}
- /*
+/*
** DEFCHARSET -- return default character set for message
**
** The first choice for character set is for the mailer
@@ -886,7 +894,7 @@ defcharset(e)
return DefaultCharSet;
return "unknown-8bit";
}
- /*
+/*
** ISBOUNDARY -- is a given string a currently valid boundary?
**
** Parameters:
@@ -914,8 +922,9 @@ isboundary(line, boundaries)
return -1;
}
#endif /* MIME8TO7 */
-
+
#if MIME7TO8
+static int mime_fromqp __P((unsigned char *, unsigned char **, int, int));
/*
** MIME7TO8 -- output 7 bit encoded MIME body in 8 bit format
@@ -965,11 +974,11 @@ mime7to8(mci, header, e)
register char *p;
char *cte;
char **pvp;
- u_char *fbufp;
+ unsigned char *fbufp;
char buf[MAXLINE];
- u_char fbuf[MAXLINE + 1];
+ unsigned char fbuf[MAXLINE + 1];
char pvpbuf[MAXLINE];
- extern u_char MimeTokenTab[256];
+ extern unsigned char MimeTokenTab[256];
p = hvalue("Content-Transfer-Encoding", header);
if (p == NULL ||
@@ -986,22 +995,23 @@ mime7to8(mci, header, e)
/* cheap failsafe algorithm -- should work on text/plain */
if (p != NULL)
{
- snprintf(buf, sizeof buf,
+ (void) sm_snprintf(buf, sizeof buf,
"Content-Transfer-Encoding: %s", p);
putline(buf, mci);
}
putline("", mci);
mci->mci_flags &= ~MCIF_INHEADER;
- while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
+ while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof buf)
+ != NULL)
putline(buf, mci);
return;
}
cataddr(pvp, NULL, buf, sizeof buf, '\0');
- cte = newstr(buf);
+ cte = sm_rpool_strdup_x(e->e_rpool, buf);
mci->mci_flags |= MCIF_INHEADER;
putline("Content-Transfer-Encoding: 8bit", mci);
- snprintf(buf, sizeof buf,
+ (void) sm_snprintf(buf, sizeof buf,
"X-MIME-Autoconverted: from %.200s to 8bit by %s id %s",
cte, MyHostName, e->e_id);
putline(buf, mci);
@@ -1014,35 +1024,36 @@ mime7to8(mci, header, e)
** it is not base64.
*/
- if (strcasecmp(cte, "base64") == 0)
+ if (sm_strcasecmp(cte, "base64") == 0)
{
int c1, c2, c3, c4;
fbufp = fbuf;
- while ((c1 = fgetc(e->e_dfp)) != EOF)
+ while ((c1 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) !=
+ SM_IO_EOF)
{
if (isascii(c1) && isspace(c1))
continue;
do
{
- c2 = fgetc(e->e_dfp);
+ c2 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT);
} while (isascii(c2) && isspace(c2));
- if (c2 == EOF)
+ if (c2 == SM_IO_EOF)
break;
do
{
- c3 = fgetc(e->e_dfp);
+ c3 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT);
} while (isascii(c3) && isspace(c3));
- if (c3 == EOF)
+ if (c3 == SM_IO_EOF)
break;
do
{
- c4 = fgetc(e->e_dfp);
+ c4 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT);
} while (isascii(c4) && isspace(c4));
- if (c4 == EOF)
+ if (c4 == SM_IO_EOF)
break;
if (c1 == '=' || c2 == '=')
@@ -1092,9 +1103,10 @@ mime7to8(mci, header, e)
{
/* quoted-printable */
fbufp = fbuf;
- while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
+ while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof buf)
+ != NULL)
{
- if (mime_fromqp((u_char *) buf, &fbufp, 0,
+ if (mime_fromqp((unsigned char *) buf, &fbufp, 0,
&fbuf[MAXLINE] - fbufp) == 0)
continue;
@@ -1112,14 +1124,14 @@ mime7to8(mci, header, e)
putxline((char *) fbuf, fbufp - fbuf, mci, PXLF_MAPFROM);
}
if (tTd(43, 3))
- dprintf("\t\t\tmime7to8 => %s to 8bit done\n", cte);
+ sm_dprintf("\t\t\tmime7to8 => %s to 8bit done\n", cte);
}
- /*
+/*
** The following is based on Borenstein's "codes.c" module, with simplifying
** changes as we do not deal with multipart, and to do the translation in-core,
** with an attempt to prevent overrun of output buffers.
**
-** What is needed here are changes to defned this code better against
+** What is needed here are changes to defend this code better against
** bad encodings. Questionable to always return 0xFF for bad mappings.
*/
@@ -1139,14 +1151,17 @@ static char index_hex[128] =
static int
mime_fromqp(infile, outfile, state, maxlen)
- u_char *infile;
- u_char **outfile;
+ unsigned char *infile;
+ unsigned char **outfile;
int state; /* Decoding body (0) or header (1) */
int maxlen; /* Max # of chars allowed in outfile */
{
int c1, c2;
int nchar = 0;
+ if (maxlen < 0)
+ return 0;
+
while ((c1 = *infile++) != '\0')
{
if (c1 == '=')
diff --git a/contrib/sendmail/src/newaliases.1 b/contrib/sendmail/src/newaliases.1
index 5a7c916..20fd0e7 100644
--- a/contrib/sendmail/src/newaliases.1
+++ b/contrib/sendmail/src/newaliases.1
@@ -1,4 +1,4 @@
-.\" Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+.\" Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
.\" All rights reserved.
.\" Copyright (c) 1983, 1997 Eric P. Allman. All rights reserved.
.\" Copyright (c) 1985, 1990, 1993
@@ -9,9 +9,9 @@
.\" the sendmail distribution.
.\"
.\"
-.\" $Id: newaliases.1,v 8.15.28.1 2000/12/14 23:08:15 gshapiro Exp $
+.\" $Id: newaliases.1,v 8.19 2001/10/10 03:23:17 ca Exp $
.\"
-.TH NEWALIASES 1 "$Date: 2000/12/14 23:08:15 $"
+.TH NEWALIASES 1 "$Date: 2001/10/10 03:23:17 $"
.SH NAME
newaliases
\- rebuild the data base for the mail aliases file
@@ -29,6 +29,15 @@ is identical to ``sendmail -bi''.
The
.B newaliases
utility exits 0 on success, and >0 if an error occurs.
+.PP
+Notice: do
+.B not
+use
+.B makemap
+to create the aliases data base, because
+.B newaliases
+puts a special token into the data base that is required by
+.B sendmail.
.SH FILES
.TP 2i
/etc/mail/aliases
diff --git a/contrib/sendmail/src/parseaddr.c b/contrib/sendmail/src/parseaddr.c
index 9b51c73..c456506 100644
--- a/contrib/sendmail/src/parseaddr.c
+++ b/contrib/sendmail/src/parseaddr.c
@@ -11,16 +11,15 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: parseaddr.c,v 8.234.4.13 2001/08/14 23:08:13 ca Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
-static void allocaddr __P((ADDRESS *, int, char *));
+SM_RCSID("@(#)$Id: parseaddr.c,v 8.349 2001/12/12 02:50:22 gshapiro Exp $")
+
+static void allocaddr __P((ADDRESS *, int, char *, ENVELOPE *));
static int callsubr __P((char**, int, ENVELOPE *));
static char *map_lookup __P((STAB *, char *, char **, int *, ENVELOPE *));
static ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *));
+static bool hasctrlchar __P((register char *, bool));
/*
** PARSEADDR -- Parse an address
@@ -39,7 +38,7 @@ static ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *));
** Parameters:
** addr -- the address to parse.
** a -- a pointer to the address descriptor buffer.
-** If NULL, a header will be created.
+** If NULL, an address will be created.
** flags -- describe detail for parsing. See RF_ definitions
** in sendmail.h.
** delim -- the character to terminate the address, passed
@@ -47,6 +46,8 @@ static ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *));
** delimptr -- if non-NULL, set to the location of the
** delim character that was found.
** e -- the envelope that will contain this address.
+** isrcpt -- true if the address denotes a recipient; false
+** indicates a sender.
**
** Returns:
** A pointer to the address descriptor header (`a' if
@@ -54,22 +55,23 @@ static ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *));
** NULL on error.
**
** Side Effects:
-** none
+** e->e_to = addr
*/
/* following delimiters are inherent to the internal algorithms */
#define DELIMCHARS "()<>,;\r\n" /* default word delimiters */
ADDRESS *
-parseaddr(addr, a, flags, delim, delimptr, e)
+parseaddr(addr, a, flags, delim, delimptr, e, isrcpt)
char *addr;
register ADDRESS *a;
int flags;
int delim;
char **delimptr;
register ENVELOPE *e;
+ bool isrcpt;
{
- register char **pvp;
+ char **pvp;
auto char *delimptrbuf;
bool qup;
char pvpbuf[PSBUFSIZE];
@@ -80,7 +82,7 @@ parseaddr(addr, a, flags, delim, delimptr, e)
e->e_to = addr;
if (tTd(20, 1))
- dprintf("\n--parseaddr(%s)\n", addr);
+ sm_dprintf("\n--parseaddr(%s)\n", addr);
if (delimptr == NULL)
delimptr = &delimptrbuf;
@@ -89,14 +91,14 @@ parseaddr(addr, a, flags, delim, delimptr, e)
if (pvp == NULL)
{
if (tTd(20, 1))
- dprintf("parseaddr-->NULL\n");
+ sm_dprintf("parseaddr-->NULL\n");
return NULL;
}
- if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr))
+ if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr, isrcpt))
{
if (tTd(20, 1))
- dprintf("parseaddr-->bad address\n");
+ sm_dprintf("parseaddr-->bad address\n");
return NULL;
}
@@ -114,7 +116,7 @@ parseaddr(addr, a, flags, delim, delimptr, e)
if (savec != '\0')
**delimptr = '\0';
- e->e_to = addr = newstr(addr);
+ e->e_to = addr = sm_rpool_strdup_x(e->e_rpool, addr);
if (savec != '\0')
**delimptr = savec;
}
@@ -124,12 +126,11 @@ parseaddr(addr, a, flags, delim, delimptr, e)
** Ruleset 0 does basic parsing. It must resolve.
*/
- qup = FALSE;
- if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
- qup = TRUE;
- if (rewrite(pvp, 0, 0, e) == EX_TEMPFAIL)
- qup = TRUE;
-
+ qup = false;
+ if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
+ qup = true;
+ if (REWRITE(pvp, 0, e) == EX_TEMPFAIL)
+ qup = true;
/*
** Build canonical address from pvp.
@@ -137,16 +138,57 @@ parseaddr(addr, a, flags, delim, delimptr, e)
a = buildaddr(pvp, a, flags, e);
+ if (hasctrlchar(a->q_user, isrcpt))
+ {
+ if (tTd(20, 1))
+ sm_dprintf("parseaddr-->bad q_user\n");
+ return NULL;
+ }
+
/*
** Make local copies of the host & user and then
** transport them out.
*/
- allocaddr(a, flags, addr);
+ allocaddr(a, flags, addr, e);
if (QS_IS_BADADDR(a->q_state))
return a;
/*
+ ** Select a queue directory for recipient addresses.
+ ** This is done here and in split_across_queue_groups(),
+ ** but the latter applies to addresses after aliasing,
+ ** and only if splitting is done.
+ */
+
+ if ((a->q_qgrp == NOAQGRP || a->q_qgrp == ENVQGRP) &&
+ !bitset(RF_SENDERADDR|RF_HEADERADDR, flags) &&
+ OpMode != MD_INITALIAS)
+ {
+ int r;
+
+ /* call ruleset which should return a queue group name */
+ r = rscap(RS_QUEUEGROUP, a->q_user, NULL, e, &pvp, pvpbuf,
+ sizeof(pvpbuf));
+ if (r == EX_OK &&
+ pvp != NULL && pvp[0] != NULL &&
+ (pvp[0][0] & 0377) == CANONNET &&
+ pvp[1] != NULL && pvp[1][0] != '\0')
+ {
+ r = name2qid(pvp[1]);
+ if (r == NOQGRP && LogLevel > 10)
+ sm_syslog(LOG_INFO, NOQID,
+ "can't find queue group name %s, selection ignored",
+ pvp[1]);
+ if (tTd(20, 4) && r != NOQGRP)
+ sm_syslog(LOG_INFO, NOQID,
+ "queue group name %s -> %d",
+ pvp[1], r);
+ a->q_qgrp = r == NOQGRP ? ENVQGRP : r;
+ }
+ }
+
+ /*
** If there was a parsing failure, mark it for queueing.
*/
@@ -157,10 +199,10 @@ parseaddr(addr, a, flags, delim, delimptr, e)
if (e->e_sendmode == SM_DEFER)
msg = "Deferring message until queue run";
if (tTd(20, 1))
- dprintf("parseaddr: queuing message\n");
+ sm_dprintf("parseaddr: queuing message\n");
message(msg);
if (e->e_message == NULL && e->e_sendmode != SM_DEFER)
- e->e_message = newstr(msg);
+ e->e_message = sm_rpool_strdup_x(e->e_rpool, msg);
a->q_state = QS_QUEUEUP;
a->q_status = "4.4.3";
}
@@ -171,61 +213,144 @@ parseaddr(addr, a, flags, delim, delimptr, e)
if (tTd(20, 1))
{
- dprintf("parseaddr-->");
- printaddr(a, FALSE);
+ sm_dprintf("parseaddr-->");
+ printaddr(a, false);
}
return a;
}
- /*
-** INVALIDADDR -- check for address containing meta-characters
+/*
+** INVALIDADDR -- check for address containing characters used for macros
**
** Parameters:
** addr -- the address to check.
+** delimptr -- if non-NULL: end of address to check, i.e.,
+** a pointer in the address string.
+** isrcpt -- true iff the address is for a recipient.
**
** Returns:
-** TRUE -- if the address has any "wierd" characters
-** FALSE -- otherwise.
+** true -- if the address has characters that are reservered
+** for macros or is too long.
+** false -- otherwise.
*/
bool
-invalidaddr(addr, delimptr)
+invalidaddr(addr, delimptr, isrcpt)
register char *addr;
char *delimptr;
+ bool isrcpt;
{
+ bool result = false;
char savedelim = '\0';
+ int len = 0;
if (delimptr != NULL)
{
+ /* delimptr points to the end of the address to test */
savedelim = *delimptr;
- if (savedelim != '\0')
- *delimptr = '\0';
+ if (savedelim != '\0') /* if that isn't '\0' already: */
+ *delimptr = '\0'; /* set it */
}
- if (strlen(addr) > MAXNAME - 1)
+ for (; *addr != '\0'; addr++)
{
- usrerr("553 5.1.1 Address too long (%d bytes max)",
- MAXNAME - 1);
- goto failure;
+ if ((*addr & 0340) == 0200)
+ {
+ setstat(EX_USAGE);
+ result = true;
+ break;
+ }
+ if (++len > MAXNAME - 1)
+ {
+ usrerr("553 5.1.0 Address too long (%d bytes max)",
+ MAXNAME - 1);
+ result = true;
+ goto delim;
+ }
}
+ if (result)
+ {
+ if (isrcpt)
+ usrerr("501 5.1.3 Syntax error in mailbox address");
+ else
+ usrerr("501 5.1.7 Syntax error in mailbox address");
+ }
+delim:
+ if (delimptr != NULL && savedelim != '\0')
+ *delimptr = savedelim; /* restore old character at delimptr */
+ return result;
+}
+/*
+** HASCTRLCHAR -- check for address containing meta-characters
+**
+** Checks that the address contains no meta-characters, and contains
+** no "non-printable" characters unless they are quoted or escaped.
+** Quoted or escaped characters are literals.
+**
+** Parameters:
+** addr -- the address to check.
+** isrcpt -- true if the address is for a recipient; false
+** indicates a from.
+**
+** Returns:
+** true -- if the address has any "wierd" characters or
+** non-printable characters or if a quote is unbalanced.
+** false -- otherwise.
+*/
+
+static bool
+hasctrlchar(addr, isrcpt)
+ register char *addr;
+ bool isrcpt;
+{
+ bool result = false;
+ int len = 0;
+ bool quoted = false;
+
+ if (addr == NULL)
+ return false;
for (; *addr != '\0'; addr++)
{
+ if (!quoted && (*addr < 32 || *addr == 127))
+ {
+ result = true; /* a non-printable */
+ break;
+ }
+ if (*addr == '"')
+ quoted = !quoted;
+ else if (*addr == '\\')
+ {
+ /* XXX Generic problem: no '\0' in strings. */
+ if (*++addr == '\0')
+ {
+ result = true;
+ break;
+ }
+ }
if ((*addr & 0340) == 0200)
+ {
+ setstat(EX_USAGE);
+ result = true;
break;
+ }
+ if (++len > MAXNAME - 1)
+ {
+ usrerr("553 5.1.0 Address too long (%d bytes max)",
+ MAXNAME - 1);
+ return true;
+ }
}
- if (*addr == '\0')
+ if (quoted)
+ result = true; /* unbalanced quote */
+ if (result)
{
- if (delimptr != NULL && savedelim != '\0')
- *delimptr = savedelim;
- return FALSE;
+ if (isrcpt)
+ usrerr("501 5.1.3 Syntax error in mailbox address");
+ else
+ usrerr("501 5.1.7 Syntax error in mailbox address");
}
- setstat(EX_USAGE);
- usrerr("553 5.1.1 Address contained invalid control characters");
-failure:
- if (delimptr != NULL && savedelim != '\0')
- *delimptr = savedelim;
- return TRUE;
+ return result;
}
- /*
+/*
** ALLOCADDR -- do local allocations of address on demand.
**
** Also lowercases the host name if requested.
@@ -235,6 +360,7 @@ failure:
** flags -- the copy flag (see RF_ definitions in sendmail.h
** for a description).
** paddr -- the printname of the address.
+** e -- envelope
**
** Returns:
** none.
@@ -244,32 +370,34 @@ failure:
*/
static void
-allocaddr(a, flags, paddr)
+allocaddr(a, flags, paddr, e)
register ADDRESS *a;
int flags;
char *paddr;
+ ENVELOPE *e;
{
if (tTd(24, 4))
- dprintf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
+ sm_dprintf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
a->q_paddr = paddr;
if (a->q_user == NULL)
- a->q_user = newstr("");
+ a->q_user = "";
if (a->q_host == NULL)
- a->q_host = newstr("");
+ a->q_host = "";
if (bitset(RF_COPYPARSE, flags))
{
- a->q_host = newstr(a->q_host);
+ a->q_host = sm_rpool_strdup_x(e->e_rpool, a->q_host);
if (a->q_user != a->q_paddr)
- a->q_user = newstr(a->q_user);
+ a->q_user = sm_rpool_strdup_x(e->e_rpool, a->q_user);
}
if (a->q_paddr == NULL)
- a->q_paddr = newstr(a->q_user);
+ a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user);
+ a->q_qgrp = NOAQGRP;
}
- /*
+/*
** PRESCAN -- Prescan name and make it canonical
**
** Scans a name and turns it into a set of tokens. This process
@@ -332,7 +460,7 @@ static short StateTab[NSTATES][NSTATES] =
};
/* token type table -- it gets modified with $o characters */
-static u_char TokTypeTab[256] =
+static unsigned char TokTypeTab[256] =
{
/* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */
ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
@@ -370,7 +498,7 @@ static u_char TokTypeTab[256] =
};
/* token type table for MIME parsing */
-u_char MimeTokenTab[256] =
+unsigned char MimeTokenTab[256] =
{
/* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */
ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL,
@@ -408,7 +536,7 @@ u_char MimeTokenTab[256] =
};
/* token type table: don't strip comments */
-u_char TokTypeNoC[256] =
+unsigned char TokTypeNoC[256] =
{
/* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */
ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
@@ -455,7 +583,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
char pvpbuf[];
int pvpbsize;
char **delimptr;
- u_char *toktab;
+ unsigned char *toktab;
{
register char *p;
register char *q;
@@ -470,7 +598,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
int newstate;
char *saveto = CurEnv->e_to;
static char *av[MAXATOM + 1];
- static char firsttime = TRUE;
+ static bool firsttime = true;
extern int errno;
if (firsttime)
@@ -478,7 +606,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
/* initialize the token type table */
char obuf[50];
- firsttime = FALSE;
+ firsttime = false;
if (OperatorChars == NULL)
{
if (ConfigLevel < 7)
@@ -488,7 +616,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
}
expand(OperatorChars, obuf, sizeof obuf - sizeof DELIMCHARS,
CurEnv);
- (void) strlcat(obuf, DELIMCHARS, sizeof obuf);
+ (void) sm_strlcat(obuf, DELIMCHARS, sizeof obuf);
for (p = obuf; *p != '\0'; p++)
{
if (TokTypeTab[*p & 0xff] == ATM)
@@ -504,8 +632,8 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
errno = 0;
q = pvpbuf;
- bslashmode = FALSE;
- route_syntax = FALSE;
+ bslashmode = false;
+ route_syntax = false;
cmntcnt = 0;
anglecnt = 0;
avp = av;
@@ -515,9 +643,9 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
CurEnv->e_to = p;
if (tTd(22, 11))
{
- dprintf("prescan: ");
+ sm_dprintf("prescan: ");
xputs(p);
- dprintf("\n");
+ sm_dprintf("\n");
}
do
@@ -533,7 +661,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
if (q >= &pvpbuf[pvpbsize - 5])
{
usrerr("553 5.1.1 Address too long");
- if (strlen(addr) > (SIZE_T) MAXNAME)
+ if (strlen(addr) > MAXNAME)
addr[MAXNAME] = '\0';
returnnull:
if (delimptr != NULL)
@@ -553,18 +681,18 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
/* diagnose and patch up bad syntax */
if (state == QST)
{
- usrerr("653 Unbalanced '\"'");
+ usrerr("553 Unbalanced '\"'");
c = '"';
}
else if (cmntcnt > 0)
{
- usrerr("653 Unbalanced '('");
+ usrerr("553 Unbalanced '('");
c = ')';
}
else if (anglecnt > 0)
{
c = '>';
- usrerr("653 Unbalanced '<'");
+ usrerr("553 Unbalanced '<'");
}
else
break;
@@ -579,20 +707,20 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
/* special case for better error management */
if (delim == ',' && !route_syntax)
{
- usrerr("653 Unbalanced '<'");
+ usrerr("553 Unbalanced '<'");
c = '>';
p--;
}
}
if (tTd(22, 101))
- dprintf("c=%c, s=%d; ", c, state);
+ sm_dprintf("c=%c, s=%d; ", c, state);
/* chew up special characters */
*q = '\0';
if (bslashmode)
{
- bslashmode = FALSE;
+ bslashmode = false;
/* kludge \! for naive users */
if (cmntcnt > 0)
@@ -609,7 +737,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
if (c == '\\')
{
- bslashmode = TRUE;
+ bslashmode = true;
}
else if (state == QST)
{
@@ -625,7 +753,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
{
if (cmntcnt <= 0)
{
- usrerr("653 Unbalanced ')'");
+ usrerr("553 Unbalanced ')'");
c = NOCHAR;
}
else
@@ -643,18 +771,18 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
while (isascii(*ptr) && isspace(*ptr))
ptr++;
if (*ptr == '@')
- route_syntax = TRUE;
+ route_syntax = true;
}
else if (c == '>')
{
if (anglecnt <= 0)
{
- usrerr("653 Unbalanced '>'");
+ usrerr("553 Unbalanced '>'");
c = NOCHAR;
}
else
anglecnt--;
- route_syntax = FALSE;
+ route_syntax = false;
}
else if (delim == ' ' && isascii(c) && isspace(c))
c = ' ';
@@ -668,14 +796,15 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
newstate = StateTab[state][toktab[c & 0xff]];
if (tTd(22, 101))
- dprintf("ns=%02o\n", newstate);
+ sm_dprintf("ns=%02o\n", newstate);
state = newstate & TYPE;
if (state == ILL)
{
if (isascii(c) && isprint(c))
- usrerr("653 Illegal character %c", c);
+ usrerr("553 Illegal character %c", c);
else
- usrerr("653 Illegal character 0x%02x", c);
+ usrerr("553 Illegal character 0x%02x",
+ c & 0x0ff);
}
if (bitset(M, newstate))
c = NOCHAR;
@@ -689,9 +818,9 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
*q++ = '\0';
if (tTd(22, 36))
{
- dprintf("tok=");
+ sm_dprintf("tok=");
xputs(tok);
- dprintf("\n");
+ sm_dprintf("\n");
}
if (avp >= &av[MAXATOM])
{
@@ -712,19 +841,19 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
*delimptr = p;
if (tTd(22, 12))
{
- dprintf("prescan==>");
+ sm_dprintf("prescan==>");
printav(av);
}
CurEnv->e_to = saveto;
if (av[0] == NULL)
{
if (tTd(22, 1))
- dprintf("prescan: null leading token\n");
+ sm_dprintf("prescan: null leading token\n");
return NULL;
}
return av;
}
- /*
+/*
** REWRITE -- apply rewrite rules to token vector.
**
** This routine is an ordered production system. Each rewrite
@@ -751,6 +880,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
** ruleset -- the ruleset to use for rewriting.
** reclevel -- recursion level (to catch loops).
** e -- the current envelope.
+** maxatom -- maximum length of buffer (usually MAXATOM)
**
** Returns:
** A status code. If EX_TEMPFAIL, higher level code should
@@ -768,11 +898,12 @@ struct match
};
int
-rewrite(pvp, ruleset, reclevel, e)
+rewrite(pvp, ruleset, reclevel, e, maxatom)
char **pvp;
int ruleset;
int reclevel;
register ENVELOPE *e;
+ int maxatom;
{
register char *ap; /* address pointer */
register char *rp; /* rewrite pointer */
@@ -798,7 +929,7 @@ rewrite(pvp, ruleset, reclevel, e)
rulename = RuleSetNames[ruleset];
if (rulename == NULL)
{
- snprintf(name, sizeof name, "%d", ruleset);
+ (void) sm_snprintf(name, sizeof name, "%d", ruleset);
rulename = name;
}
if (OpMode == MD_TEST)
@@ -807,12 +938,13 @@ rewrite(pvp, ruleset, reclevel, e)
prefix = "rewrite: ruleset ";
if (OpMode == MD_TEST)
{
- printf("%s%-16.16s input:", prefix, rulename);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "%s%-16.16s input:", prefix, rulename);
printav(pvp);
}
else if (tTd(21, 1))
{
- dprintf("%s%-16.16s input:", prefix, rulename);
+ sm_dprintf("%s%-16.16s input:", prefix, rulename);
printav(pvp);
}
if (reclevel++ > MaxRuleRecursion)
@@ -842,10 +974,10 @@ rewrite(pvp, ruleset, reclevel, e)
if (tTd(21, 12))
{
if (tTd(21, 15))
- dprintf("-----trying rule (line %d):",
+ sm_dprintf("-----trying rule (line %d):",
rwr->r_line);
else
- dprintf("-----trying rule:");
+ sm_dprintf("-----trying rule:");
printav(rwr->r_lhs);
}
@@ -859,7 +991,7 @@ rewrite(pvp, ruleset, reclevel, e)
rulename, ruleno);
if (tTd(21, 1))
{
- dprintf("workspace: ");
+ sm_dprintf("workspace: ");
printav(pvp);
}
break;
@@ -870,11 +1002,11 @@ rewrite(pvp, ruleset, reclevel, e)
rp = *rvp;
if (tTd(21, 35))
{
- dprintf("ADVANCE rp=");
+ sm_dprintf("ADVANCE rp=");
xputs(rp);
- dprintf(", ap=");
+ sm_dprintf(", ap=");
xputs(ap);
- dprintf("\n");
+ sm_dprintf("\n");
}
if (rp == NULL)
{
@@ -905,16 +1037,16 @@ rewrite(pvp, ruleset, reclevel, e)
{
if (tTd(21, 36))
{
- dprintf("EXTEND rp=");
+ sm_dprintf("EXTEND rp=");
xputs(rp);
- dprintf(", ap=");
+ sm_dprintf(", ap=");
xputs(ap);
- dprintf("\n");
+ sm_dprintf("\n");
}
goto extendclass;
}
if (tTd(21, 36))
- dprintf("CLMATCH\n");
+ sm_dprintf("CLMATCH\n");
mlp++;
break;
@@ -958,7 +1090,7 @@ rewrite(pvp, ruleset, reclevel, e)
ap = macvalue(rp[1], e);
mlp->match_first = avp;
if (tTd(21, 2))
- dprintf("rewrite: LHS $&%s => \"%s\"\n",
+ sm_dprintf("rewrite: LHS $&%s => \"%s\"\n",
macname(rp[1]),
ap == NULL ? "(NULL)" : ap);
@@ -967,7 +1099,8 @@ rewrite(pvp, ruleset, reclevel, e)
while (*ap != '\0')
{
if (*avp == NULL ||
- strncasecmp(ap, *avp, strlen(*avp)) != 0)
+ sm_strncasecmp(ap, *avp,
+ strlen(*avp)) != 0)
{
/* no match */
avp = mlp->match_first;
@@ -1002,11 +1135,11 @@ rewrite(pvp, ruleset, reclevel, e)
if (tTd(21, 36))
{
- dprintf("BACKUP rp=");
+ sm_dprintf("BACKUP rp=");
xputs(rp);
- dprintf(", ap=");
+ sm_dprintf(", ap=");
xputs(ap);
- dprintf("\n");
+ sm_dprintf("\n");
}
if (ap == NULL)
@@ -1045,7 +1178,7 @@ rewrite(pvp, ruleset, reclevel, e)
if (mlp < mlist || *rvp != NULL)
{
if (tTd(21, 10))
- dprintf("----- rule fails\n");
+ sm_dprintf("----- rule fails\n");
rwr = rwr->r_next;
ruleno++;
loopcount = 0;
@@ -1055,7 +1188,7 @@ rewrite(pvp, ruleset, reclevel, e)
rvp = rwr->r_rhs;
if (tTd(21, 12))
{
- dprintf("-----rule matches:");
+ sm_dprintf("-----rule matches:");
printav(rvp);
}
@@ -1095,23 +1228,28 @@ rewrite(pvp, ruleset, reclevel, e)
}
if (tTd(21, 15))
{
- dprintf("$%c:", rp[1]);
+ sm_dprintf("$%c:", rp[1]);
pp = m->match_first;
while (pp <= m->match_last)
{
- dprintf(" %lx=\"",
- (u_long) *pp);
- (void) dflush();
- dprintf("%s\"", *pp++);
+ sm_dprintf(" %p=\"", *pp);
+ sm_dflush();
+ sm_dprintf("%s\"", *pp++);
}
- dprintf("\n");
+ sm_dprintf("\n");
}
pp = m->match_first;
while (pp <= m->match_last)
{
- if (avp >= &npvp[MAXATOM])
+ if (avp >= &npvp[maxatom])
{
syserr("554 5.3.0 rewrite: expansion too long");
+ if (LogLevel > 9)
+ sm_syslog(LOG_ERR,
+ e->e_id,
+ "rewrite: expansion too long, ruleset=%s, ruleno=%d",
+ rulename,
+ ruleno);
return EX_DATAERR;
}
*avp++ = *pp++;
@@ -1120,10 +1258,14 @@ rewrite(pvp, ruleset, reclevel, e)
else
{
/* some sort of replacement */
- if (avp >= &npvp[MAXATOM])
+ if (avp >= &npvp[maxatom])
{
toolong:
syserr("554 5.3.0 rewrite: expansion too long");
+ if (LogLevel > 9)
+ sm_syslog(LOG_ERR, e->e_id,
+ "rewrite: expansion too long, ruleset=%s, ruleno=%d",
+ rulename, ruleno);
return EX_DATAERR;
}
if ((*rp & 0377) != MACRODEXPAND)
@@ -1142,7 +1284,7 @@ rewrite(pvp, ruleset, reclevel, e)
char pvpbuf[PSBUFSIZE];
if (tTd(21, 2))
- dprintf("rewrite: RHS $&%s => \"%s\"\n",
+ sm_dprintf("rewrite: RHS $&%s => \"%s\"\n",
macname(rp[1]),
mval == NULL ? "(NULL)" : mval);
if (mval == NULL || *mval == '\0')
@@ -1155,7 +1297,8 @@ rewrite(pvp, ruleset, reclevel, e)
{
if (pvpb1 != NULL)
sm_free(pvpb1);
- pvpb1 = (char **)xalloc(trsize);
+ pvpb1 = (char **)
+ sm_pmalloc_x(trsize);
pvpb1_size = trsize;
}
@@ -1177,15 +1320,16 @@ rewrite(pvp, ruleset, reclevel, e)
while (*xpvp != NULL)
{
if (tTd(21, 19))
- dprintf(" ... %s\n",
+ sm_dprintf(" ... %s\n",
*xpvp);
- *avp++ = newstr(*xpvp);
- if (avp >= &npvp[MAXATOM])
+ *avp++ = sm_rpool_strdup_x(
+ e->e_rpool, *xpvp);
+ if (avp >= &npvp[maxatom])
goto toolong;
xpvp++;
}
if (tTd(21, 19))
- dprintf(" ... DONE\n");
+ sm_dprintf(" ... DONE\n");
/* restore the old trailing input */
memmove((char *) pvp,
@@ -1335,15 +1479,15 @@ rewrite(pvp, ruleset, reclevel, e)
/* append it to the token list */
for (avp = hbrvp; *xpvp != NULL; xpvp++)
{
- *avp++ = newstr(*xpvp);
- if (avp >= &npvp[MAXATOM])
+ *avp++ = sm_rpool_strdup_x(e->e_rpool, *xpvp);
+ if (avp >= &npvp[maxatom])
goto toolong;
}
/* restore the old trailing information */
rvp = avp - 1;
for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
- if (avp >= &npvp[MAXATOM])
+ if (avp >= &npvp[maxatom])
goto toolong;
}
@@ -1363,24 +1507,25 @@ rewrite(pvp, ruleset, reclevel, e)
if (tTd(21, 4))
{
- dprintf("rewritten as:");
+ sm_dprintf("rewritten as:");
printav(pvp);
}
}
if (OpMode == MD_TEST)
{
- printf("%s%-16.16s returns:", prefix, rulename);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "%s%-16.16s returns:", prefix, rulename);
printav(pvp);
}
else if (tTd(21, 1))
{
- dprintf("%s%-16.16s returns:", prefix, rulename);
+ sm_dprintf("%s%-16.16s returns:", prefix, rulename);
printav(pvp);
}
return rstat;
}
- /*
+/*
** CALLSUBR -- call subroutines in rewrite vector
**
** Parameters:
@@ -1402,14 +1547,25 @@ callsubr(pvp, reclevel, e)
ENVELOPE *e;
{
char **avp;
- char **rvp;
register int i;
- int subr;
+ int subr, j;
+ int nsubr;
int status;
int rstat = EX_OK;
- char *tpvp[MAXATOM + 1];
+#define MAX_SUBR 16
+ int subrnumber[MAX_SUBR];
+ int subrindex[MAX_SUBR];
+
+ nsubr = 0;
- for (avp = pvp; *avp != NULL; avp++)
+ /*
+ ** Look for subroutine calls in pvp, collect them into subr*[]
+ ** We will perform the calls in the next loop, because we will
+ ** call the "last" subroutine first to avoid recursive calls
+ ** and too much copying.
+ */
+
+ for (avp = pvp, j = 0; *avp != NULL; avp++, j++)
{
if ((**avp & 0377) == CALLSUBR && avp[1] != NULL)
{
@@ -1417,81 +1573,74 @@ callsubr(pvp, reclevel, e)
subr = strtorwset(avp[1], NULL, ST_FIND);
if (subr < 0)
{
- syserr("Unknown ruleset %s", avp[1]);
+ syserr("554 5.3.5 Unknown ruleset %s", avp[1]);
return EX_CONFIG;
}
- if (tTd(21, 3))
- dprintf("-----callsubr %s (%d)\n",
- avp[1], subr);
-
- /*
- ** Take care of possible inner calls first.
- ** use a full size temporary buffer to avoid
- ** overflows in rewrite, but strip off the
- ** subroutine call.
- */
-
- for (i = 2; avp[i] != NULL; i++)
- tpvp[i - 2] = avp[i];
- tpvp[i - 2] = NULL;
-
- status = callsubr(tpvp, reclevel, e);
- if (rstat == EX_OK || status == EX_TEMPFAIL)
- rstat = status;
-
/*
- ** Now we need to call the ruleset specified for
- ** the subroutine. we can do this with the
- ** temporary buffer that we set up earlier,
- ** since it has all the data we want to rewrite.
+ ** XXX instead of doing this we could optimize
+ ** the rules after reading them: just remove
+ ** calls to empty rulesets
*/
- status = rewrite(tpvp, subr, reclevel, e);
- if (rstat == EX_OK || status == EX_TEMPFAIL)
- rstat = status;
-
- /*
- ** Find length of tpvp and current offset into
- ** pvp, if the total is greater than MAXATOM,
- ** then it would overflow the buffer if we copied
- ** it back in to pvp, in which case we throw a
- ** fit.
- */
-
- for (rvp = tpvp; *rvp != NULL; rvp++)
+ /* subroutine is an empty ruleset? don't call it */
+ if (RewriteRules[subr] == NULL)
+ {
+ if (tTd(21, 3))
+ sm_dprintf("-----skip subr %s (%d)\n",
+ avp[1], subr);
+ for (i = 2; avp[i] != NULL; i++)
+ avp[i - 2] = avp[i];
+ avp[i - 2] = NULL;
continue;
- if (((rvp - tpvp) + (avp - pvp)) > MAXATOM)
+ }
+ if (++nsubr >= MAX_SUBR)
{
- syserr("554 5.3.0 callsubr: expansion too long");
- return EX_DATAERR;
+ syserr("554 5.3.0 Too many subroutine calls (%d max)",
+ MAX_SUBR);
+ return EX_CONFIG;
}
+ subrnumber[nsubr] = subr;
+ subrindex[nsubr] = j;
+ }
+ }
- /*
- ** Now we can copy the rewritten code over
- ** the initial subroutine call in the buffer.
- */
+ /*
+ ** Perform the actual subroutines calls, "last" one first, i.e.,
+ ** go from the right to the left through all calls,
+ ** do the rewriting in place.
+ */
- for (i = 0; tpvp[i] != NULL; i++)
- avp[i] = tpvp[i];
- avp[i] = NULL;
+ for (; nsubr > 0; nsubr--)
+ {
+ subr = subrnumber[nsubr];
+ avp = pvp + subrindex[nsubr];
- /*
- ** If we got this far, we've processed the left
- ** most subroutine, and recursively called ourselves
- ** to handle any other subroutines. We're done.
- */
+ /* remove the subroutine call and name */
+ for (i = 2; avp[i] != NULL; i++)
+ avp[i - 2] = avp[i];
+ avp[i - 2] = NULL;
- break;
- }
+ /*
+ ** Now we need to call the ruleset specified for
+ ** the subroutine. we can do this inplace since
+ ** we call the "last" subroutine first.
+ */
+
+ status = rewrite(avp, subr, reclevel, e,
+ MAXATOM - subrindex[nsubr]);
+ if (status != EX_OK && status != EX_TEMPFAIL)
+ return status;
+ if (rstat == EX_OK || status == EX_TEMPFAIL)
+ rstat = status;
}
return rstat;
}
- /*
+/*
** MAP_LOOKUP -- do lookup in map
**
** Parameters:
-** map -- the map to use for the lookup.
+** smap -- the map to use for the lookup.
** key -- the key to look up.
** argvect -- arguments to pass to the map lookup.
** pstat -- a pointer to an integer in which to store the
@@ -1526,7 +1675,7 @@ map_lookup(smap, key, argvect, pstat, e)
{
/* don't do any map lookups */
if (tTd(60, 1))
- dprintf("map_lookup(%s, %s) => DEFERRED\n",
+ sm_dprintf("map_lookup(%s, %s) => DEFERRED\n",
smap->s_name, key);
*pstat = EX_TEMPFAIL;
return NULL;
@@ -1537,19 +1686,19 @@ map_lookup(smap, key, argvect, pstat, e)
if (tTd(60, 1))
{
- dprintf("map_lookup(%s, %s", smap->s_name, key);
+ sm_dprintf("map_lookup(%s, %s", smap->s_name, key);
if (tTd(60, 5))
{
int i;
for (i = 0; argvect[i] != NULL; i++)
- dprintf(", %%%d=%s", i, argvect[i]);
+ sm_dprintf(", %%%d=%s", i, argvect[i]);
}
- dprintf(") => ");
+ sm_dprintf(") => ");
}
replac = (*map->map_class->map_lookup)(map, key, argvect, &status);
if (tTd(60, 1))
- dprintf("%s (%d)\n",
+ sm_dprintf("%s (%d)\n",
replac != NULL ? replac : "NOT FOUND",
status);
@@ -1558,17 +1707,17 @@ map_lookup(smap, key, argvect, pstat, e)
{
*pstat = EX_TEMPFAIL;
if (tTd(60, 1))
- dprintf("map_lookup(%s, %s) tempfail: errno=%d\n",
+ sm_dprintf("map_lookup(%s, %s) tempfail: errno=%d\n",
smap->s_name, key, errno);
if (e->e_message == NULL)
{
char mbuf[320];
- snprintf(mbuf, sizeof mbuf,
+ (void) sm_snprintf(mbuf, sizeof mbuf,
"%.80s map: lookup (%s): deferred",
smap->s_name,
shortenstring(key, MAXSHORTSTR));
- e->e_message = newstr(mbuf);
+ e->e_message = sm_rpool_strdup_x(e->e_rpool, mbuf);
}
}
if (status == EX_TEMPFAIL && map->map_tapp != NULL)
@@ -1582,17 +1731,17 @@ map_lookup(smap, key, argvect, pstat, e)
if (rwbuf != NULL)
sm_free(rwbuf);
rwbuflen = i;
- rwbuf = (char *) xalloc(rwbuflen);
+ rwbuf = (char *) sm_pmalloc_x(rwbuflen);
}
- snprintf(rwbuf, rwbuflen, "%s%s", key, map->map_tapp);
+ (void) sm_strlcpyn(rwbuf, rwbuflen, 2, key, map->map_tapp);
if (tTd(60, 4))
- dprintf("map_lookup tempfail: returning \"%s\"\n",
+ sm_dprintf("map_lookup tempfail: returning \"%s\"\n",
rwbuf);
return rwbuf;
}
return replac;
}
- /*
+/*
** INITERRMAILERS -- initialize error and discard mailers
**
** Parameters:
@@ -1628,7 +1777,7 @@ initerrmailers()
errormailer.m_argv = errorargv;
}
}
- /*
+/*
** BUILDADDR -- build address from token vector.
**
** Parameters:
@@ -1660,13 +1809,10 @@ static struct errcodes
{ "software", EX_SOFTWARE },
{ "tempfail", EX_TEMPFAIL },
{ "protocol", EX_PROTOCOL },
-#ifdef EX_CONFIG
{ "config", EX_CONFIG },
-#endif /* EX_CONFIG */
{ NULL, EX_UNAVAILABLE }
};
-
static ADDRESS *
buildaddr(tv, a, flags, e)
register char **tv;
@@ -1684,12 +1830,12 @@ buildaddr(tv, a, flags, e)
if (tTd(24, 5))
{
- dprintf("buildaddr, flags=%x, tv=", flags);
+ sm_dprintf("buildaddr, flags=%x, tv=", flags);
printav(tv);
}
if (a == NULL)
- a = (ADDRESS *) xalloc(sizeof *a);
+ a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof *a);
memset((char *) a, '\0', sizeof *a);
hbuf[0] = '\0';
@@ -1731,20 +1877,22 @@ badaddr:
cataddr(++tv, NULL, ubuf, sizeof ubuf, ' ');
/* save away the host name */
- if (strcasecmp(mname, "error") == 0)
+ if (sm_strcasecmp(mname, "error") == 0)
{
/* Set up triplet for use by -bv */
a->q_mailer = &errormailer;
- a->q_user = newstr(ubuf);
+ a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);
+ /* XXX wrong place? */
if (hostp != NULL)
{
register struct errcodes *ep;
- a->q_host = newstr(hbuf);
+ a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);
if (strchr(hbuf, '.') != NULL)
{
- a->q_status = newstr(hbuf);
+ a->q_status = sm_rpool_strdup_x(e->e_rpool,
+ hbuf);
setstat(dsntoexitstat(hbuf));
}
else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
@@ -1754,7 +1902,7 @@ badaddr:
else
{
for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
- if (strcasecmp(ep->ec_name, hbuf) == 0)
+ if (sm_strcasecmp(ep->ec_name, hbuf) == 0)
break;
setstat(ep->ec_code);
}
@@ -1780,7 +1928,7 @@ badaddr:
off = 4;
ubuf[3] = '\0';
}
- (void) snprintf(fmt, sizeof fmt, "%s %%s", ubuf);
+ (void) sm_strlcpyn(fmt, sizeof fmt, 2, ubuf, " %s");
if (off > 4)
usrerr(fmt, ubuf + off);
else if (isenhsc(hbuf, '\0') > 0)
@@ -1798,7 +1946,7 @@ badaddr:
for (mp = Mailer; (m = *mp++) != NULL; )
{
- if (strcasecmp(m->m_name, mname) == 0)
+ if (sm_strcasecmp(m->m_name, mname) == 0)
break;
}
if (m == NULL)
@@ -1819,7 +1967,7 @@ badaddr:
a->q_host = NULL;
}
else
- a->q_host = newstr(hbuf);
+ a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);
/* figure out the user */
p = ubuf;
@@ -1841,40 +1989,31 @@ badaddr:
{
/* may be :include: */
stripquotes(ubuf);
- if (strncasecmp(ubuf, ":include:", 9) == 0)
+ if (sm_strncasecmp(ubuf, ":include:", 9) == 0)
{
/* if :include:, don't need further rewriting */
a->q_mailer = m = InclMailer;
- a->q_user = newstr(&ubuf[9]);
+ a->q_user = sm_rpool_strdup_x(e->e_rpool, &ubuf[9]);
return a;
}
}
/* rewrite according recipient mailer rewriting rules */
- define('h', a->q_host, e);
-
-#if _FFR_ADDR_TYPE
- /*
- ** Note, change the 9 to a 10 before removing #if FFR check
- ** in a future version.
- */
+ macdefine(&e->e_macro, A_PERM, 'h', a->q_host);
- if (ConfigLevel >= 9 ||
+ if (ConfigLevel >= 10 ||
!bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
-#else /* _FFR_ADDR_TYPE */
- if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
-#endif /* _FFR_ADDR_TYPE */
{
/* sender addresses done later */
- (void) rewrite(tv, 2, 0, e);
+ (void) REWRITE(tv, 2, e);
if (m->m_re_rwset > 0)
- (void) rewrite(tv, m->m_re_rwset, 0, e);
+ (void) REWRITE(tv, m->m_re_rwset, e);
}
- (void) rewrite(tv, 4, 0, e);
+ (void) REWRITE(tv, 4, e);
/* save the result for the command line/RCPT argument */
cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');
- a->q_user = newstr(ubuf);
+ a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);
/*
** Do mapping to lower case as requested by mailer
@@ -1887,12 +2026,12 @@ badaddr:
if (tTd(24, 6))
{
- dprintf("buildaddr => ");
- printaddr(a, FALSE);
+ sm_dprintf("buildaddr => ");
+ printaddr(a, false);
}
return a;
}
- /*
+/*
** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
**
** Parameters:
@@ -1901,7 +2040,7 @@ badaddr:
** use entire pvp.
** buf -- buffer to build the string into.
** sz -- size of buf.
-** spacesub -- the space separator character; if null,
+** spacesub -- the space separator character; if '\0',
** use SpaceSub.
**
** Returns:
@@ -1919,8 +2058,8 @@ cataddr(pvp, evp, buf, sz, spacesub)
register int sz;
int spacesub;
{
- bool oatomtok = FALSE;
- bool natomtok = FALSE;
+ bool oatomtok = false;
+ bool natomtok = false;
register int i;
register char *p;
@@ -1937,15 +2076,17 @@ cataddr(pvp, evp, buf, sz, spacesub)
}
p = buf;
sz -= 2;
- while (*pvp != NULL && (i = strlen(*pvp)) < sz - 1)
+ while (*pvp != NULL && sz > 0)
{
natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
if (oatomtok && natomtok)
{
*p++ = spacesub;
- --sz;
+ if (--sz <= 0)
+ break;
}
- (void) strlcpy(p, *pvp, sz);
+ if ((i = sm_strlcpy(p, *pvp, sz)) >= sz)
+ break;
oatomtok = natomtok;
p += i;
sz -= i;
@@ -1954,7 +2095,7 @@ cataddr(pvp, evp, buf, sz, spacesub)
}
*p = '\0';
}
- /*
+/*
** SAMEADDR -- Determine if two addresses are the same
**
** This is not just a straight comparison -- if the mailer doesn't
@@ -1964,8 +2105,8 @@ cataddr(pvp, evp, buf, sz, spacesub)
** a, b -- pointers to the internal forms to compare.
**
** Returns:
-** TRUE -- they represent the same mailbox.
-** FALSE -- they don't.
+** true -- they represent the same mailbox.
+** false -- they don't.
**
** Side Effects:
** none.
@@ -1980,11 +2121,11 @@ sameaddr(a, b)
/* if they don't have the same mailer, forget it */
if (a->q_mailer != b->q_mailer)
- return FALSE;
+ return false;
/* if the user isn't the same, we can drop out */
if (strcmp(a->q_user, b->q_user) != 0)
- return FALSE;
+ return false;
/* if we have good uids for both but they differ, these are different */
if (a->q_mailer == ProgMailer)
@@ -1994,26 +2135,26 @@ sameaddr(a, b)
if (ca != NULL && cb != NULL &&
bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
ca->q_uid != cb->q_uid)
- return FALSE;
+ return false;
}
/* otherwise compare hosts (but be careful for NULL ptrs) */
if (a->q_host == b->q_host)
{
/* probably both null pointers */
- return TRUE;
+ return true;
}
if (a->q_host == NULL || b->q_host == NULL)
{
/* only one is a null pointer */
- return FALSE;
+ return false;
}
if (strcmp(a->q_host, b->q_host) != 0)
- return FALSE;
+ return false;
- return TRUE;
+ return true;
}
- /*
+/*
** PRINTADDR -- print address (for debugging)
**
** Parameters:
@@ -2029,8 +2170,8 @@ sameaddr(a, b)
struct qflags
{
- char *qf_name;
- u_long qf_bit;
+ char *qf_name;
+ unsigned long qf_bit;
};
static struct qflags AddressFlags[] =
@@ -2066,14 +2207,14 @@ printaddr(a, follow)
if (a == NULL)
{
- printf("[NULL]\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "[NULL]\n");
return;
}
while (a != NULL)
{
- printf("%lx=", (u_long) a);
- (void) fflush(stdout);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%p=", a);
+ (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
/* find the mailer -- carefully */
m = a->q_mailer;
@@ -2084,120 +2225,149 @@ printaddr(a, follow)
m->m_name = "NULL";
}
- printf("%s:\n\tmailer %d (%s), host `%s'\n",
- a->q_paddr == NULL ? "<null>" : a->q_paddr,
- m->m_mno, m->m_name,
- a->q_host == NULL ? "<null>" : a->q_host);
- printf("\tuser `%s', ruser `%s'\n",
- a->q_user,
- a->q_ruser == NULL ? "<null>" : a->q_ruser);
- printf("\tstate=");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "%s:\n\tmailer %d (%s), host `%s'\n",
+ a->q_paddr == NULL ? "<null>" : a->q_paddr,
+ m->m_mno, m->m_name,
+ a->q_host == NULL ? "<null>" : a->q_host);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\tuser `%s', ruser `%s'\n",
+ a->q_user,
+ a->q_ruser == NULL ? "<null>" : a->q_ruser);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\tstate=");
switch (a->q_state)
{
case QS_OK:
- printf("OK");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK");
break;
case QS_DONTSEND:
- printf("DONTSEND");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "DONTSEND");
break;
case QS_BADADDR:
- printf("BADADDR");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "BADADDR");
break;
case QS_QUEUEUP:
- printf("QUEUEUP");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "QUEUEUP");
+ break;
+
+ case QS_RETRY:
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "RETRY");
break;
case QS_SENT:
- printf("SENT");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "SENT");
break;
case QS_VERIFIED:
- printf("VERIFIED");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "VERIFIED");
break;
case QS_EXPANDED:
- printf("EXPANDED");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "EXPANDED");
break;
case QS_SENDER:
- printf("SENDER");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "SENDER");
break;
case QS_CLONED:
- printf("CLONED");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "CLONED");
break;
case QS_DISCARDED:
- printf("DISCARDED");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "DISCARDED");
break;
case QS_REPLACED:
- printf("REPLACED");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "REPLACED");
break;
case QS_REMOVED:
- printf("REMOVED");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "REMOVED");
break;
case QS_DUPLICATE:
- printf("DUPLICATE");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "DUPLICATE");
break;
case QS_INCLUDED:
- printf("INCLUDED");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "INCLUDED");
break;
default:
- printf("%d", a->q_state);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "%d", a->q_state);
break;
}
- printf(", next=%lx, alias %lx, uid %d, gid %d\n",
- (u_long) a->q_next, (u_long) a->q_alias,
- (int) a->q_uid, (int) a->q_gid);
- printf("\tflags=%lx<", a->q_flags);
- firstone = TRUE;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ ", next=%p, alias %p, uid %d, gid %d\n",
+ a->q_next, a->q_alias,
+ (int) a->q_uid, (int) a->q_gid);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\tflags=%lx<",
+ a->q_flags);
+ firstone = true;
for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
{
if (!bitset(qfp->qf_bit, a->q_flags))
continue;
if (!firstone)
- printf(",");
- firstone = FALSE;
- printf("%s", qfp->qf_name);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ ",");
+ firstone = false;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s",
+ qfp->qf_name);
}
- printf(">\n");
- printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n",
- a->q_owner == NULL ? "(none)" : a->q_owner,
- a->q_home == NULL ? "(none)" : a->q_home,
- a->q_fullname == NULL ? "(none)" : a->q_fullname);
- printf("\torcpt=\"%s\", statmta=%s, status=%s\n",
- a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
- a->q_statmta == NULL ? "(none)" : a->q_statmta,
- a->q_status == NULL ? "(none)" : a->q_status);
- printf("\trstatus=\"%s\"\n",
- a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
- printf("\tspecificity=%d, statdate=%s\n",
- a->q_specificity,
- a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate));
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, ">\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\towner=%s, home=\"%s\", fullname=\"%s\"\n",
+ a->q_owner == NULL ? "(none)" : a->q_owner,
+ a->q_home == NULL ? "(none)" : a->q_home,
+ a->q_fullname == NULL ? "(none)" : a->q_fullname);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\torcpt=\"%s\", statmta=%s, status=%s\n",
+ a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
+ a->q_statmta == NULL ? "(none)" : a->q_statmta,
+ a->q_status == NULL ? "(none)" : a->q_status);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\tfinalrcpt=\"%s\"\n",
+ a->q_finalrcpt == NULL ? "(none)" : a->q_finalrcpt);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\trstatus=\"%s\"\n",
+ a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\tstatdate=%s\n",
+ a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate));
if (!follow)
return;
a = a->q_next;
}
}
- /*
-** EMPTYADDR -- return TRUE if this address is empty (``<>'')
+/*
+** EMPTYADDR -- return true if this address is empty (``<>'')
**
** Parameters:
** a -- pointer to the address
**
** Returns:
-** TRUE -- if this address is "empty" (i.e., no one should
+** true -- if this address is "empty" (i.e., no one should
** ever generate replies to it.
-** FALSE -- if it is a "regular" (read: replyable) address.
+** false -- if it is a "regular" (read: replyable) address.
*/
bool
@@ -2207,7 +2377,7 @@ emptyaddr(a)
return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 ||
a->q_user == NULL || strcmp(a->q_user, "<>") == 0;
}
- /*
+/*
** REMOTENAME -- return the name relative to the current mailer
**
** Parameters:
@@ -2239,44 +2409,36 @@ remotename(name, m, flags, pstat, e)
register ENVELOPE *e;
{
register char **pvp;
- char *fancy;
- char *oldg = macvalue('g', e);
+ char *SM_NONVOLATILE fancy;
+ char *oldg;
int rwset;
static char buf[MAXNAME + 1];
char lbuf[MAXNAME + 1];
char pvpbuf[PSBUFSIZE];
-#if _FFR_ADDR_TYPE
char addrtype[4];
-#endif /* _FFR_ADDR_TYPE */
if (tTd(12, 1))
- dprintf("remotename(%s)\n", name);
+ sm_dprintf("remotename(%s)\n", name);
/* don't do anything if we are tagging it as special */
if (bitset(RF_SENDERADDR, flags))
{
rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
: m->m_se_rwset;
-#if _FFR_ADDR_TYPE
addrtype[2] = 's';
-#endif /* _FFR_ADDR_TYPE */
}
else
{
rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
: m->m_re_rwset;
-#if _FFR_ADDR_TYPE
addrtype[2] = 'r';
-#endif /* _FFR_ADDR_TYPE */
}
if (rwset < 0)
return name;
-#if _FFR_ADDR_TYPE
addrtype[1] = ' ';
addrtype[3] = '\0';
addrtype[0] = bitset(RF_HEADERADDR, flags) ? 'h' : 'e';
- define(macid("{addr_type}", NULL), addrtype, e);
-#endif /* _FFR_ADDR_TYPE */
+ macdefine(&e->e_macro, A_TEMP, macid("{addr_type}"), addrtype);
/*
** Do a heuristic crack of this name to extract any comment info.
@@ -2299,7 +2461,7 @@ remotename(name, m, flags, pstat, e)
pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
if (pvp == NULL)
return name;
- if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
+ if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
*pstat = EX_TEMPFAIL;
if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
{
@@ -2328,7 +2490,7 @@ remotename(name, m, flags, pstat, e)
break;
}
}
- if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
+ if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
*pstat = EX_TEMPFAIL;
}
}
@@ -2342,17 +2504,17 @@ remotename(name, m, flags, pstat, e)
if (bitset(RF_SENDERADDR, flags))
{
- if (rewrite(pvp, 1, 0, e) == EX_TEMPFAIL)
+ if (REWRITE(pvp, 1, e) == EX_TEMPFAIL)
*pstat = EX_TEMPFAIL;
}
else
{
- if (rewrite(pvp, 2, 0, e) == EX_TEMPFAIL)
+ if (REWRITE(pvp, 2, e) == EX_TEMPFAIL)
*pstat = EX_TEMPFAIL;
}
if (rwset > 0)
{
- if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL)
+ if (REWRITE(pvp, rwset, e) == EX_TEMPFAIL)
*pstat = EX_TEMPFAIL;
}
@@ -2363,7 +2525,7 @@ remotename(name, m, flags, pstat, e)
** may be used as a default to the above rules.
*/
- if (rewrite(pvp, 4, 0, e) == EX_TEMPFAIL)
+ if (REWRITE(pvp, 4, e) == EX_TEMPFAIL)
*pstat = EX_TEMPFAIL;
/*
@@ -2371,21 +2533,24 @@ remotename(name, m, flags, pstat, e)
*/
cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
- define('g', lbuf, e);
+ oldg = macget(&e->e_macro, 'g');
+ macset(&e->e_macro, 'g', lbuf);
- /* need to make sure route-addrs have <angle brackets> */
- if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
- expand("<\201g>", buf, sizeof buf, e);
- else
- expand(fancy, buf, sizeof buf, e);
-
- define('g', oldg, e);
+ SM_TRY
+ /* need to make sure route-addrs have <angle brackets> */
+ if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
+ expand("<\201g>", buf, sizeof buf, e);
+ else
+ expand(fancy, buf, sizeof buf, e);
+ SM_FINALLY
+ macset(&e->e_macro, 'g', oldg);
+ SM_END_TRY
if (tTd(12, 1))
- dprintf("remotename => `%s'\n", buf);
+ sm_dprintf("remotename => `%s'\n", buf);
return buf;
}
- /*
+/*
** MAPLOCALUSER -- run local username through ruleset 5 for final redirection
**
** Parameters:
@@ -2401,7 +2566,8 @@ remotename(name, m, flags, pstat, e)
#define Q_COPYFLAGS (QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\
Q_PINGFLAGS|QHASNOTIFY|\
- QRELAYED|QEXPANDED|QDELIVERED|QDELAYED)
+ QRELAYED|QEXPANDED|QDELIVERED|QDELAYED|\
+ QBYTRACE|QBYNDELAY|QBYNRELAY)
void
maplocaluser(a, sendq, aliaslevel, e)
@@ -2411,35 +2577,33 @@ maplocaluser(a, sendq, aliaslevel, e)
ENVELOPE *e;
{
register char **pvp;
- register ADDRESS *a1 = NULL;
+ register ADDRESS *SM_NONVOLATILE a1 = NULL;
auto char *delimptr;
char pvpbuf[PSBUFSIZE];
if (tTd(29, 1))
{
- dprintf("maplocaluser: ");
- printaddr(a, FALSE);
+ sm_dprintf("maplocaluser: ");
+ printaddr(a, false);
}
pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
if (pvp == NULL)
{
if (tTd(29, 9))
- dprintf("maplocaluser: cannot prescan %s\n",
+ sm_dprintf("maplocaluser: cannot prescan %s\n",
a->q_user);
return;
}
- define('h', a->q_host, e);
- define('u', a->q_user, e);
- define('z', a->q_home, e);
+ macdefine(&e->e_macro, A_PERM, 'h', a->q_host);
+ macdefine(&e->e_macro, A_PERM, 'u', a->q_user);
+ macdefine(&e->e_macro, A_PERM, 'z', a->q_home);
-#if _FFR_ADDR_TYPE
- define(macid("{addr_type}", NULL), "e r", e);
-#endif /* _FFR_ADDR_TYPE */
- if (rewrite(pvp, 5, 0, e) == EX_TEMPFAIL)
+ macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r");
+ if (REWRITE(pvp, 5, e) == EX_TEMPFAIL)
{
if (tTd(29, 9))
- dprintf("maplocaluser: rewrite tempfail\n");
+ sm_dprintf("maplocaluser: rewrite tempfail\n");
a->q_state = QS_QUEUEUP;
a->q_status = "4.4.3";
return;
@@ -2447,49 +2611,58 @@ maplocaluser(a, sendq, aliaslevel, e)
if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
{
if (tTd(29, 9))
- dprintf("maplocaluser: doesn't resolve\n");
+ sm_dprintf("maplocaluser: doesn't resolve\n");
return;
}
+ SM_TRY
+ a1 = buildaddr(pvp, NULL, 0, e);
+ SM_EXCEPT(exc, "E:mta.quickabort")
+
+ /*
+ ** mark address as bad, S5 returned an error
+ ** and we gave that back to the SMTP client.
+ */
+
+ a->q_state = QS_DONTSEND;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 2);
+ SM_END_TRY
+
/* if non-null, mailer destination specified -- has it changed? */
- a1 = buildaddr(pvp, NULL, 0, e);
if (a1 == NULL || sameaddr(a, a1))
{
if (tTd(29, 9))
- dprintf("maplocaluser: address unchanged\n");
- if (a1 != NULL)
- sm_free(a1);
+ sm_dprintf("maplocaluser: address unchanged\n");
return;
}
/* make new address take on flags and print attributes of old */
a1->q_flags &= ~Q_COPYFLAGS;
a1->q_flags |= a->q_flags & Q_COPYFLAGS;
- a1->q_paddr = newstr(a->q_paddr);
+ a1->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_paddr);
+ a1->q_finalrcpt = a->q_finalrcpt;
a1->q_orcpt = a->q_orcpt;
/* mark old address as dead; insert new address */
a->q_state = QS_REPLACED;
if (tTd(29, 5))
{
- dprintf("maplocaluser: QS_REPLACED ");
- printaddr(a, FALSE);
+ sm_dprintf("maplocaluser: QS_REPLACED ");
+ printaddr(a, false);
}
a1->q_alias = a;
- allocaddr(a1, RF_COPYALL, newstr(a->q_paddr));
+ allocaddr(a1, RF_COPYALL, sm_rpool_strdup_x(e->e_rpool, a->q_paddr), e);
(void) recipient(a1, sendq, aliaslevel, e);
}
- /*
+/*
** DEQUOTE_INIT -- initialize dequote map
**
-** This is a no-op.
-**
** Parameters:
** map -- the internal map structure.
** args -- arguments.
**
** Returns:
-** TRUE.
+** true.
*/
bool
@@ -2530,9 +2703,9 @@ dequote_init(map, args)
if (map->map_app != NULL)
map->map_app = newstr(map->map_app);
- return TRUE;
+ return true;
}
- /*
+/*
** DEQUOTE_MAP -- unquote an address
**
** Parameters:
@@ -2562,15 +2735,15 @@ dequote_map(map, name, av, statp)
int cmntcnt = 0;
int quotecnt = 0;
int spacecnt = 0;
- bool quotemode = FALSE;
- bool bslashmode = FALSE;
+ bool quotemode = false;
+ bool bslashmode = false;
char spacesub = map->map_spacesub;
for (p = q = name; (c = *p++) != '\0'; )
{
if (bslashmode)
{
- bslashmode = FALSE;
+ bslashmode = false;
*q++ = c;
continue;
}
@@ -2581,7 +2754,7 @@ dequote_map(map, name, av, statp)
switch (c)
{
case '\\':
- bslashmode = TRUE;
+ bslashmode = true;
break;
case '(':
@@ -2630,7 +2803,7 @@ dequote_map(map, name, av, statp)
*q++ = '\0';
return map_rewrite(map, name, strlen(name), NULL);
}
- /*
+/*
** RSCHECK -- check string(s) for validity using rewriting sets
**
** Parameters:
@@ -2640,8 +2813,9 @@ dequote_map(map, name, av, statp)
** e -- the current envelope.
** rmcomm -- remove comments?
** cnt -- count rejections (statistics)?
-** logl -- logging level
+** logl -- logging level.
** host -- NULL or relay host.
+** logid -- id for sm_syslog.
**
** Returns:
** EX_OK -- if the rwset doesn't resolve to $#error
@@ -2649,7 +2823,7 @@ dequote_map(map, name, av, statp)
*/
int
-rscheck(rwset, p1, p2, e, rmcomm, cnt, logl, host)
+rscheck(rwset, p1, p2, e, rmcomm, cnt, logl, host, logid)
char *rwset;
char *p1;
char *p2;
@@ -2657,23 +2831,28 @@ rscheck(rwset, p1, p2, e, rmcomm, cnt, logl, host)
bool rmcomm, cnt;
int logl;
char *host;
+ char *logid;
{
- char *buf;
+ char *volatile buf;
int bufsize;
int saveexitstat;
- int rstat = EX_OK;
+ int volatile rstat = EX_OK;
char **pvp;
int rsno;
- bool discard = FALSE;
+ bool volatile discard = false;
auto ADDRESS a1;
bool saveQuickAbort = QuickAbort;
bool saveSuprErrs = SuprErrs;
+#if _FFR_QUARANTINE
+ bool quarantine = false;
+ char ubuf[BUFSIZ * 2];
+#endif /* _FFR_QUARANTINE */
char buf0[MAXLINE];
char pvpbuf[PSBUFSIZE];
extern char MsgBuf[];
if (tTd(48, 2))
- dprintf("rscheck(%s, %s, %s)\n", rwset, p1,
+ sm_dprintf("rscheck(%s, %s, %s)\n", rwset, p1,
p2 == NULL ? "(NULL)" : p2);
rsno = strtorwset(rwset, NULL, ST_FIND);
@@ -2684,130 +2863,258 @@ rscheck(rwset, p1, p2, e, rmcomm, cnt, logl, host)
{
bufsize = strlen(p1) + strlen(p2) + 2;
if (bufsize > sizeof buf0)
- buf = xalloc(bufsize);
+ buf = sm_malloc_x(bufsize);
else
{
buf = buf0;
bufsize = sizeof buf0;
}
- (void) snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
+ (void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
}
else
{
bufsize = strlen(p1) + 1;
if (bufsize > sizeof buf0)
- buf = xalloc(bufsize);
+ buf = sm_malloc_x(bufsize);
else
{
buf = buf0;
bufsize = sizeof buf0;
}
- (void) snprintf(buf, bufsize, "%s", p1);
+ (void) sm_strlcpy(buf, p1, bufsize);
}
- SuprErrs = TRUE;
- QuickAbort = FALSE;
- pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL,
- rmcomm ? NULL : TokTypeNoC);
- SuprErrs = saveSuprErrs;
- if (pvp == NULL)
+ SM_TRY
{
- if (tTd(48, 2))
- dprintf("rscheck: cannot prescan input\n");
-/*
- syserr("rscheck: cannot prescan input: \"%s\"",
- shortenstring(buf, MAXSHORTSTR));
- rstat = EX_DATAERR;
-*/
- goto finis;
- }
+ SuprErrs = true;
+ QuickAbort = false;
+ pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL,
+ rmcomm ? NULL : TokTypeNoC);
+ SuprErrs = saveSuprErrs;
+ if (pvp == NULL)
+ {
+ if (tTd(48, 2))
+ sm_dprintf("rscheck: cannot prescan input\n");
+ /*
+ syserr("rscheck: cannot prescan input: \"%s\"",
+ shortenstring(buf, MAXSHORTSTR));
+ rstat = EX_DATAERR;
+ */
+ goto finis;
+ }
+ (void) REWRITE(pvp, rsno, e);
+ if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET ||
+ pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 &&
+ strcmp(pvp[1], "discard") != 0))
+ {
+ goto finis;
+ }
- MapOpenErr = FALSE;
- (void) rewrite(pvp, rsno, 0, e);
- if (MapOpenErr)
- {
- usrerrenh("4.3.0", "451 Temporary failure");
- rstat = EX_TEMPFAIL;
- goto finis;
- }
+ if (strcmp(pvp[1], "discard") == 0)
+ {
+ if (tTd(48, 2))
+ sm_dprintf("rscheck: discard mailer selected\n");
+ e->e_flags |= EF_DISCARD;
+ discard = true;
+ }
+#if _FFR_QUARANTINE
+ else if (strcmp(pvp[1], "error") == 0 &&
+ pvp[2] != NULL && (pvp[2][0] & 0377) == CANONHOST &&
+ pvp[3] != NULL && strcmp(pvp[3], "quarantine") == 0)
+ {
+ if (pvp[4] == NULL ||
+ (pvp[4][0] & 0377) != CANONUSER ||
+ pvp[5] == NULL)
+ e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
+ rwset);
+ else
+ {
+ cataddr(&(pvp[5]), NULL, ubuf,
+ sizeof ubuf, ' ');
+ e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
+ ubuf);
+ }
+ macdefine(&e->e_macro, A_PERM,
+ macid("{quarantine}"), e->e_quarmsg);
+ quarantine = true;
+ }
+#endif /* _FFR_QUARANTINE */
+ else
+ {
+ int savelogusrerrs = LogUsrErrs;
+ static bool logged = false;
+
+ /* got an error -- process it */
+ saveexitstat = ExitStat;
+ LogUsrErrs = false;
+ (void) buildaddr(pvp, &a1, 0, e);
+ LogUsrErrs = savelogusrerrs;
+ rstat = ExitStat;
+ ExitStat = saveexitstat;
+ if (!logged)
+ {
+ if (cnt)
+ markstats(e, &a1, STATS_REJECT);
+ logged = true;
+ }
+ }
+
+ if (LogLevel > logl)
+ {
+ char *relay;
+ char *p;
+ char lbuf[MAXLINE];
+
+ p = lbuf;
+ if (p2 != NULL)
+ {
+ (void) sm_snprintf(p, SPACELEFT(lbuf, p),
+ ", arg2=%s",
+ p2);
+ p += strlen(p);
+ }
+
+ if (host != NULL)
+ relay = host;
+ else
+ relay = macvalue('_', e);
+ if (relay != NULL)
+ {
+ (void) sm_snprintf(p, SPACELEFT(lbuf, p),
+ ", relay=%s", relay);
+ p += strlen(p);
+ }
+ *p = '\0';
+ if (discard)
+ sm_syslog(LOG_NOTICE, logid,
+ "ruleset=%s, arg1=%s%s, discard",
+ rwset, p1, lbuf);
+#if _FFR_QUARANTINE
+ else if (quarantine)
+ sm_syslog(LOG_NOTICE, logid,
+ "ruleset=%s, arg1=%s%s, quarantine=%s",
+ rwset, p1, lbuf, ubuf);
+#endif /* _FFR_QUARANTINE */
+ else
+ sm_syslog(LOG_NOTICE, logid,
+ "ruleset=%s, arg1=%s%s, reject=%s",
+ rwset, p1, lbuf, MsgBuf);
+ }
- if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET ||
- pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 &&
- strcmp(pvp[1], "discard") != 0))
+ finis: ;
+ }
+ SM_FINALLY
{
- goto finis;
+ /* clean up */
+ if (buf != buf0)
+ sm_free(buf);
+ QuickAbort = saveQuickAbort;
}
+ SM_END_TRY
+
+ setstat(rstat);
+
+ /* rulesets don't set errno */
+ errno = 0;
+ if (rstat != EX_OK && QuickAbort)
+ sm_exc_raisenew_x(&EtypeQuickAbort, 2);
+ return rstat;
+}
+/*
+** RSCAP -- call rewriting set to return capabilities
+**
+** Parameters:
+** rwset -- the rewriting set to use.
+** p1 -- the first string to check.
+** p2 -- the second string to check -- may be null.
+** e -- the current envelope.
+** pvp -- pointer to token vector.
+** pvpbuf -- buffer space.
+**
+** Returns:
+** EX_UNAVAILABLE -- ruleset doesn't exist.
+** EX_DATAERR -- prescan() failed.
+** EX_OK -- rewrite() was successful.
+** else -- return status from rewrite().
+*/
+
+int
+rscap(rwset, p1, p2, e, pvp, pvpbuf, size)
+ char *rwset;
+ char *p1;
+ char *p2;
+ ENVELOPE *e;
+ char ***pvp;
+ char *pvpbuf;
+ int size;
+{
+ char *volatile buf;
+ int bufsize;
+ int volatile rstat = EX_OK;
+ int rsno;
+ bool saveQuickAbort = QuickAbort;
+ bool saveSuprErrs = SuprErrs;
+ char buf0[MAXLINE];
+ extern char MsgBuf[];
+
+ if (tTd(48, 2))
+ sm_dprintf("rscap(%s, %s, %s)\n", rwset, p1,
+ p2 == NULL ? "(NULL)" : p2);
+
+ if (pvp != NULL)
+ *pvp = NULL;
+ rsno = strtorwset(rwset, NULL, ST_FIND);
+ if (rsno < 0)
+ return EX_UNAVAILABLE;
- if (strcmp(pvp[1], "discard") == 0)
+ if (p2 != NULL)
{
- if (tTd(48, 2))
- dprintf("rscheck: discard mailer selected\n");
- e->e_flags |= EF_DISCARD;
- discard = TRUE;
+ bufsize = strlen(p1) + strlen(p2) + 2;
+ if (bufsize > sizeof buf0)
+ buf = sm_malloc_x(bufsize);
+ else
+ {
+ buf = buf0;
+ bufsize = sizeof buf0;
+ }
+ (void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
}
else
{
- int savelogusrerrs = LogUsrErrs;
- static bool logged = FALSE;
-
- /* got an error -- process it */
- saveexitstat = ExitStat;
- LogUsrErrs = FALSE;
- (void) buildaddr(pvp, &a1, 0, e);
- LogUsrErrs = savelogusrerrs;
- rstat = ExitStat;
- ExitStat = saveexitstat;
- if (!logged)
+ bufsize = strlen(p1) + 1;
+ if (bufsize > sizeof buf0)
+ buf = sm_malloc_x(bufsize);
+ else
{
- if (cnt)
- markstats(e, &a1, TRUE);
- logged = TRUE;
+ buf = buf0;
+ bufsize = sizeof buf0;
}
+ (void) sm_strlcpy(buf, p1, bufsize);
}
-
- if (LogLevel >= logl)
+ SM_TRY
{
- char *relay;
- char *p;
- char lbuf[MAXLINE];
-
- p = lbuf;
- if (p2 != NULL)
- {
- snprintf(p, SPACELEFT(lbuf, p),
- ", arg2=%s",
- p2);
- p += strlen(p);
- }
-
- if (host != NULL)
- relay = host;
+ SuprErrs = true;
+ QuickAbort = false;
+ *pvp = prescan(buf, '\0', pvpbuf, size, NULL, NULL);
+ if (*pvp != NULL)
+ rstat = REWRITE(*pvp, rsno, e);
else
- relay = macvalue('_', e);
- if (relay != NULL)
{
- snprintf(p, SPACELEFT(lbuf, p),
- ", relay=%s", relay);
- p += strlen(p);
+ if (tTd(48, 2))
+ sm_dprintf("rscap: cannot prescan input\n");
+ rstat = EX_DATAERR;
}
- *p = '\0';
- if (discard)
- sm_syslog(LOG_NOTICE, e->e_id,
- "ruleset=%s, arg1=%s%s, discard",
- rwset, p1, lbuf);
- else
- sm_syslog(LOG_NOTICE, e->e_id,
- "ruleset=%s, arg1=%s%s, reject=%s",
- rwset, p1, lbuf, MsgBuf);
}
+ SM_FINALLY
+ {
+ /* clean up */
+ if (buf != buf0)
+ sm_free(buf);
+ SuprErrs = saveSuprErrs;
+ QuickAbort = saveQuickAbort;
- finis:
- /* clean up */
- QuickAbort = saveQuickAbort;
- setstat(rstat);
- if (buf != buf0)
- sm_free(buf);
-
- if (rstat != EX_OK && QuickAbort)
- longjmp(TopFrame, 2);
+ /* prevent information leak, this may contain rewrite error */
+ MsgBuf[0] = '\0';
+ }
+ SM_END_TRY
return rstat;
}
diff --git a/contrib/sendmail/src/queue.c b/contrib/sendmail/src/queue.c
index aeed7f9..6a2da9c 100644
--- a/contrib/sendmail/src/queue.c
+++ b/contrib/sendmail/src/queue.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
@@ -11,28 +11,41 @@
*
*/
-
#include <sendmail.h>
-#ifndef lint
-# if QUEUE
-static char id[] = "@(#)$Id: queue.c,v 8.343.4.62 2001/07/20 00:53:01 gshapiro Exp $ (with queueing)";
-# else /* QUEUE */
-static char id[] = "@(#)$Id: queue.c,v 8.343.4.62 2001/07/20 00:53:01 gshapiro Exp $ (without queueing)";
-# endif /* QUEUE */
-#endif /* ! lint */
+SM_RCSID("@(#)$Id: queue.c,v 8.834 2002/01/08 23:04:58 ca Exp $")
+
+#include <dirent.h>
+
+#if SM_CONF_SHM
+# include <sm/shm.h>
+#endif /* SM_CONF_SHM */
-# include <dirent.h>
+# define RELEASE_QUEUE (void) 0
+# define ST_INODE(st) (st).st_ino
-#if QUEUE
-# if _FFR_QUEUEDELAY
-# define QF_VERSION 5 /* version number of this queue format */
+/*
+** Historical notes:
+** QF_VERSION==4 was sendmail 8.10/8.11 without _FFR_QUEUEDELAY
+** QF_VERSION==5 was sendmail 8.10/8.11 with _FFR_QUEUEDELAY
+*/
+
+#if _FFR_QUEUEDELAY
+# define QF_VERSION 7 /* version number of this queue format */
static time_t queuedelay __P((ENVELOPE *));
-# else /* _FFR_QUEUEDELAY */
-# define QF_VERSION 4 /* version number of this queue format */
-# define queuedelay(e) MinQueueAge
-# endif /* _FFR_QUEUEDELAY */
+#define queuedelay_qfver_unsupported(qfver) false
+#else /* _FFR_QUEUEDELAY */
+# define QF_VERSION 6 /* version number of this queue format */
+# define queuedelay(e) MinQueueAge
+#define queuedelay_qfver_unsupported(qfver) ((qfver) == 5 || (qfver) == 7)
+#endif /* _FFR_QUEUEDELAY */
+#if _FFR_QUARANTINE
+static char queue_letter __P((ENVELOPE *, int));
+static bool quarantine_queue_item __P((int, int, ENVELOPE *, char *));
+#endif /* _FFR_QUARANTINE */
+
+/* Naming convention: qgrp: index of queue group, qg: QUEUEGROUP */
/*
** Work queue.
@@ -45,28 +58,214 @@ struct work
bool w_lock; /* is message locked? */
bool w_tooyoung; /* is it too young to run? */
long w_pri; /* priority of message, see below */
- time_t w_ctime; /* creation time of message */
+ time_t w_ctime; /* creation time */
+ time_t w_mtime; /* modification time */
+ int w_qgrp; /* queue group located in */
+ int w_qdir; /* queue directory located in */
struct work *w_next; /* next in queue */
};
typedef struct work WORK;
-static WORK *WorkQ; /* queue of things to be done */
+static WORK *WorkQ; /* queue of things to be done */
+static int NumWorkGroups; /* number of work groups */
+
+/*
+** use of DoQueueRun:
+** NumQueue: indicates that a queue run is needed, look at individual bits
+** 0 - NumQueue-1: indicates that a queue run for this queue group
+** is needed.
+*/
+
+static BITMAP256 volatile DoQueueRun; /* non-interrupt time queue run needed */
+
+/*
+** Work group definition structure.
+** Each work group contains one or more queue groups. This is done
+** to manage the number of queue group runners active at the same time
+** to be within the constraints of MaxQueueChildren (if it is set).
+** The number of queue groups that can be run on the next work run
+** is kept track of. The queue groups are run in a round robin.
+*/
+
+struct workgrp
+{
+ int wg_numqgrp; /* number of queue groups in work grp */
+ int wg_runners; /* total runners */
+ int wg_curqgrp; /* current queue group */
+ QUEUEGRP **wg_qgs; /* array of queue groups */
+ int wg_maxact; /* max # of active runners */
+ time_t wg_lowqintvl; /* lowest queue interval */
+ int wg_restart; /* needs restarting? */
+ int wg_restartcnt; /* count of times restarted */
+};
+
+typedef struct workgrp WORKGRP;
+
+static WORKGRP volatile WorkGrp[MAXWORKGROUPS + 1]; /* work groups */
+
+#if SM_HEAP_CHECK
+static SM_DEBUG_T DebugLeakQ = SM_DEBUG_INITIALIZER("leak_q",
+ "@(#)$Debug: leak_q - trace memory leaks during queue processing $");
+#endif /* SM_HEAP_CHECK */
-static void grow_wlist __P((int));
-static int orderq __P((int, bool));
-static void printctladdr __P((ADDRESS *, FILE *));
-static int print_single_queue __P((int));
-static bool readqf __P((ENVELOPE *));
-static void runqueueevent __P((void));
-static int run_single_queue __P((int, bool, bool));
+/*
+** We use EmptyString instead of "" to avoid
+** 'zero-length format string' warnings from gcc
+*/
+
+static const char EmptyString[] = "";
+
+static void grow_wlist __P((int, int));
+static int multiqueue_cache __P((char *, int, QUEUEGRP *, int, unsigned int *));
+static int gatherq __P((int, int, bool, bool *, bool *));
+static int sortq __P((int));
+static void printctladdr __P((ADDRESS *, SM_FILE_T *));
+static bool readqf __P((ENVELOPE *, bool));
+static void restart_work_group __P((int));
+static void runner_work __P((ENVELOPE *, int, bool, int, int));
+static void schedule_queue_runs __P((bool, int));
static char *strrev __P((char *));
-static ADDRESS *setctluser __P((char *, int));
+static ADDRESS *setctluser __P((char *, int, ENVELOPE *));
+#if _FFR_RHS
+static int sm_strshufflecmp __P((char *, char *));
+static void init_shuffle_alphabet __P(());
+#endif /* _FFR_RHS */
static int workcmpf0();
static int workcmpf1();
static int workcmpf2();
static int workcmpf3();
static int workcmpf4();
+static int workcmpf5();
+static int workcmpf6();
+#if _FFR_RHS
+static int workcmpf7();
+#endif /* _FFR_RHS */
+
+#if RANDOMSHIFT
+# define get_rand_mod(m) ((get_random() >> RANDOMSHIFT) % (m))
+#else /* RANDOMSHIFT */
+# define get_rand_mod(m) (get_random() % (m))
+#endif /* RANDOMSHIFT */
+
+/*
+** File system definition.
+** Used to keep track of how much free space is available
+** on a file system in which one or more queue directories reside.
+*/
+
+typedef struct filesys_shared FILESYS;
+
+struct filesys_shared
+{
+ dev_t fs_dev; /* unique device id */
+ long fs_avail; /* number of free blocks available */
+ long fs_blksize; /* block size, in bytes */
+};
+
+/* probably kept in shared memory */
+static FILESYS FileSys[MAXFILESYS]; /* queue file systems */
+static char *FSPath[MAXFILESYS]; /* pathnames for file systems */
+
+#if SM_CONF_SHM
+
+/*
+** Shared memory data
+**
+** Current layout:
+** size -- size of shared memory segment
+** pid -- pid of owner, should be a unique id to avoid misinterpretations
+** by other processes.
+** tag -- should be a unique id to avoid misinterpretations by others.
+** idea: hash over configuration data that will be stored here.
+** NumFileSys -- number of file systems.
+** FileSys -- (arrary of) structure for used file systems.
+** RSATmpCnt -- counter for number of uses of ephemeral RSA key.
+** QShm -- (array of) structure for information about queue directories.
+*/
+
+/*
+** Queue data in shared memory
+*/
+
+typedef struct queue_shared QUEUE_SHM_T;
+
+struct queue_shared
+{
+ int qs_entries; /* number of entries */
+ /* XXX more to follow? */
+};
+
+static void *Pshm; /* pointer to shared memory */
+static FILESYS *PtrFileSys; /* pointer to queue file system array */
+int ShmId = SM_SHM_NO_ID; /* shared memory id */
+static QUEUE_SHM_T *QShm; /* pointer to shared queue data */
+
+# define SHM_OFF_PID(p) (((char *) (p)) + sizeof(int))
+# define SHM_OFF_TAG(p) (((char *) (p)) + sizeof(pid_t) + sizeof(int))
+# define SHM_OFF_HEAD (sizeof(pid_t) + sizeof(int) * 2)
+
+/* how to access FileSys */
+# define FILE_SYS(i) (PtrFileSys[i])
+
+/* first entry is a tag, for now just the size */
+# define OFF_FILE_SYS(p) (((char *) (p)) + SHM_OFF_HEAD)
+
+/* offset for PNumFileSys */
+# define OFF_NUM_FILE_SYS(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys))
+
+/* offset for PRSATmpCnt */
+# define OFF_RSA_TMP_CNT(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int))
+int *PRSATmpCnt;
+
+/* offset for queue_shm */
+# define OFF_QUEUE_SHM(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2)
+
+#define QSHM_ENTRIES(i) QShm[i].qs_entries
+
+/* basic size of shared memory segment */
+# define SM_T_SIZE (SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2)
+
+static unsigned int hash_q __P((char *, unsigned int));
+
+/*
+** HASH_Q -- simple hash function
+**
+** Parameters:
+** p -- string to hash.
+** h -- hash start value (from previous run).
+**
+** Returns:
+** hash value.
+*/
+
+static unsigned int
+hash_q(p, h)
+ char *p;
+ unsigned int h;
+{
+ int c, d;
+
+ while (*p != '\0')
+ {
+ d = *p++;
+ c = d;
+ c ^= c<<6;
+ h += (c<<11) ^ (c>>1);
+ h ^= (d<<14) + (d<<7) + (d<<4) + d;
+ }
+ return h;
+}
+
+#else /* SM_CONF_SHM */
+# define FILE_SYS(i) FileSys[i]
+#endif /* SM_CONF_SHM */
+
+/* access to the various components of file system data */
+#define FILE_SYS_NAME(i) FSPath[i]
+#define FILE_SYS_AVAIL(i) FILE_SYS(i).fs_avail
+#define FILE_SYS_BLKSIZE(i) FILE_SYS(i).fs_blksize
+#define FILE_SYS_DEV(i) FILE_SYS(i).fs_dev
/*
** Current qf file field assignments:
@@ -75,9 +274,10 @@ static int workcmpf4();
** B body type
** C controlling user
** D data file name
+** d data file directory name (added in 8.12)
** E error recipient
** F flag bits
-** G queue delay algorithm
+** G queue delay algorithm (_FFR_QUEUEDELAY)
** H header
** I data file's inode number
** K time of last delivery attempt
@@ -85,43 +285,44 @@ static int workcmpf4();
** M message (obsolete)
** N number of delivery attempts
** P message priority
+** q quarantine reason (_FFR_QUARANTINE)
** Q original recipient (ORCPT=)
+** r final recipient (Final-Recipient: DSN field)
** R recipient
** S sender
** T init time
** V queue file version
-** X character set (_FFR_SAVE_CHARSET)
-** Y current delay
+** X free (was: character set if _FFR_SAVE_CHARSET)
+** Y current delay (_FFR_QUEUEDELAY)
** Z original envelope id from ESMTP
+** ! deliver by (added in 8.12)
** $ define macro
** . terminate file
*/
- /*
+/*
** QUEUEUP -- queue a message up for future transmission.
**
** Parameters:
** e -- the envelope to queue up.
-** announce -- if TRUE, tell when you are queueing up.
+** announce -- if true, tell when you are queueing up.
+** msync -- if true, then fsync() if SuperSafe interactive mode.
**
** Returns:
** none.
**
** Side Effects:
-** The current request are saved in a control file.
+** The current request is saved in a control file.
** The queue file is left locked.
*/
-# define TEMPQF_LETTER 'T'
-# define LOSEQF_LETTER 'Q'
-
void
-queueup(e, announce)
+queueup(e, announce, msync)
register ENVELOPE *e;
bool announce;
+ bool msync;
{
- char *qf;
- register FILE *tfp;
+ register SM_FILE_T *tfp;
register HDR *h;
register ADDRESS *q;
int tfd = -1;
@@ -130,7 +331,9 @@ queueup(e, announce)
register char *p;
MAILER nullmailer;
MCI mcibuf;
+ char qf[MAXPATHLEN];
char tf[MAXPATHLEN];
+ char df[MAXPATHLEN];
char buf[MAXLINE];
/*
@@ -138,36 +341,28 @@ queueup(e, announce)
*/
newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags);
-
- /* if newid, queuename will create a locked qf file in e->lockfp */
- (void) strlcpy(tf, queuename(e, 't'), sizeof tf);
+ (void) sm_strlcpy(tf, queuename(e, NEWQFL_LETTER), sizeof tf);
tfp = e->e_lockfp;
if (tfp == NULL)
- newid = FALSE;
+ newid = false;
- /* if newid, just write the qf file directly (instead of tf file) */
+ /* if newid, write the queue file directly (instead of temp file) */
if (!newid)
{
- int flags;
-
- flags = O_CREAT|O_WRONLY|O_EXCL;
+ const int flags = O_CREAT|O_WRONLY|O_EXCL;
/* get a locked tf file */
for (i = 0; i < 128; i++)
{
if (tfd < 0)
{
-#if _FFR_QUEUE_FILE_MODE
- MODE_T oldumask;
+ MODE_T oldumask = 0;
if (bitset(S_IWGRP, QueueFileMode))
oldumask = umask(002);
tfd = open(tf, flags, QueueFileMode);
if (bitset(S_IWGRP, QueueFileMode))
(void) umask(oldumask);
-#else /* _FFR_QUEUE_FILE_MODE */
- tfd = open(tf, flags, FileMode);
-#endif /* _FFR_QUEUE_FILE_MODE */
if (tfd < 0)
{
@@ -176,7 +371,8 @@ queueup(e, announce)
if (LogLevel > 0 && (i % 32) == 0)
sm_syslog(LOG_ALERT, e->e_id,
"queueup: cannot create %s, uid=%d: %s",
- tf, geteuid(), errstring(errno));
+ tf, geteuid(),
+ sm_errstring(errno));
}
}
if (tfd >= 0)
@@ -186,7 +382,7 @@ queueup(e, announce)
else if (LogLevel > 0 && (i % 32) == 0)
sm_syslog(LOG_ALERT, e->e_id,
"queueup: cannot lock %s: %s",
- tf, errstring(errno));
+ tf, sm_errstring(errno));
if ((i % 32) == 31)
{
(void) close(tfd);
@@ -202,11 +398,13 @@ queueup(e, announce)
else
(void) sleep(i % 32);
}
- if (tfd < 0 || (tfp = fdopen(tfd, "w")) == NULL)
+ if (tfd < 0 || (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
+ (void *) &tfd, SM_IO_WRONLY,
+ NULL)) == NULL)
{
int save_errno = errno;
- printopenfds(TRUE);
+ printopenfds(true);
errno = save_errno;
syserr("!queueup: cannot create queue temp file %s, uid=%d",
tf, geteuid());
@@ -214,146 +412,192 @@ queueup(e, announce)
}
if (tTd(40, 1))
- dprintf("\n>>>>> queueing %s/qf%s%s >>>>>\n",
- qid_printqueue(e->e_queuedir), e->e_id,
- newid ? " (new id)" : "");
+ sm_dprintf("\n>>>>> queueing %s/%s%s >>>>>\n",
+ qid_printqueue(e->e_qgrp, e->e_qdir),
+ queuename(e, ANYQFL_LETTER),
+ newid ? " (new id)" : "");
if (tTd(40, 3))
{
- dprintf(" e_flags=");
+ sm_dprintf(" e_flags=");
printenvflags(e);
}
if (tTd(40, 32))
{
- dprintf(" sendq=");
- printaddr(e->e_sendqueue, TRUE);
+ sm_dprintf(" sendq=");
+ printaddr(e->e_sendqueue, true);
}
if (tTd(40, 9))
{
- dprintf(" tfp=");
- dumpfd(fileno(tfp), TRUE, FALSE);
- dprintf(" lockfp=");
+ sm_dprintf(" tfp=");
+ dumpfd(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL), true, false);
+ sm_dprintf(" lockfp=");
if (e->e_lockfp == NULL)
- dprintf("NULL\n");
+ sm_dprintf("NULL\n");
else
- dumpfd(fileno(e->e_lockfp), TRUE, FALSE);
+ dumpfd(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL),
+ true, false);
}
/*
** If there is no data file yet, create one.
*/
+ (void) sm_strlcpy(df, queuename(e, DATAFL_LETTER), sizeof df);
if (bitset(EF_HAS_DF, e->e_flags))
{
- if (e->e_dfp != NULL && bfcommit(e->e_dfp) < 0)
+ if (e->e_dfp != NULL &&
+ SuperSafe != SAFE_REALLY &&
+ sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 &&
+ errno != EINVAL)
+ {
syserr("!queueup: cannot commit data file %s, uid=%d",
- queuename(e, 'd'), geteuid());
+ queuename(e, DATAFL_LETTER), geteuid());
+ }
+ if (e->e_dfp != NULL &&
+ SuperSafe == SAFE_INTERACTIVE && msync)
+ {
+ if (tTd(40,32))
+ sm_syslog(LOG_INFO, e->e_id,
+ "queueup: fsync(e->e_dfp)");
+
+ if (fsync(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD,
+ NULL)) < 0)
+ {
+ if (newid)
+ syserr("!552 Error writing data file %s",
+ df);
+ else
+ syserr("!452 Error writing data file %s",
+ df);
+ }
+ }
}
else
{
int dfd;
- register FILE *dfp = NULL;
- char dfname[MAXPATHLEN];
+ MODE_T oldumask = 0;
+ register SM_FILE_T *dfp = NULL;
struct stat stbuf;
- if (e->e_dfp != NULL && bftest(e->e_dfp))
+ if (e->e_dfp != NULL &&
+ sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
syserr("committing over bf file");
- (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
-#if _FFR_QUEUE_FILE_MODE
- {
- MODE_T oldumask;
-
- if (bitset(S_IWGRP, QueueFileMode))
- oldumask = umask(002);
- dfd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC,
- QueueFileMode);
- if (bitset(S_IWGRP, QueueFileMode))
- (void) umask(oldumask);
- }
-#else /* _FFR_QUEUE_FILE_MODE */
- dfd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode);
-#endif /* _FFR_QUEUE_FILE_MODE */
- if (dfd < 0 || (dfp = fdopen(dfd, "w")) == NULL)
+ if (bitset(S_IWGRP, QueueFileMode))
+ oldumask = umask(002);
+ dfd = open(df, O_WRONLY|O_CREAT|O_TRUNC, QueueFileMode);
+ if (bitset(S_IWGRP, QueueFileMode))
+ (void) umask(oldumask);
+ if (dfd < 0 || (dfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
+ (void *) &dfd, SM_IO_WRONLY,
+ NULL)) == NULL)
syserr("!queueup: cannot create data temp file %s, uid=%d",
- dfname, geteuid());
+ df, geteuid());
if (fstat(dfd, &stbuf) < 0)
e->e_dfino = -1;
else
{
e->e_dfdev = stbuf.st_dev;
- e->e_dfino = stbuf.st_ino;
+ e->e_dfino = ST_INODE(stbuf);
}
e->e_flags |= EF_HAS_DF;
memset(&mcibuf, '\0', sizeof mcibuf);
mcibuf.mci_out = dfp;
mcibuf.mci_mailer = FileMailer;
(*e->e_putbody)(&mcibuf, e, NULL);
- if (fclose(dfp) < 0)
+
+ if (SuperSafe == SAFE_REALLY ||
+ (SuperSafe == SAFE_INTERACTIVE && msync))
+ {
+ if (tTd(40,32))
+ sm_syslog(LOG_INFO, e->e_id,
+ "queueup: fsync(dfp)");
+
+ if (fsync(sm_io_getinfo(dfp, SM_IO_WHAT_FD, NULL)) < 0)
+ {
+ if (newid)
+ syserr("!552 Error writing data file %s",
+ df);
+ else
+ syserr("!452 Error writing data file %s",
+ df);
+ }
+ }
+
+ if (sm_io_close(dfp, SM_TIME_DEFAULT) < 0)
syserr("!queueup: cannot save data temp file %s, uid=%d",
- dfname, geteuid());
+ df, geteuid());
e->e_putbody = putbody;
}
/*
** Output future work requests.
** Priority and creation time should be first, since
- ** they are required by orderq.
+ ** they are required by gatherq.
*/
/* output queue version number (must be first!) */
- fprintf(tfp, "V%d\n", QF_VERSION);
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "V%d\n", QF_VERSION);
/* output creation time */
- fprintf(tfp, "T%ld\n", (long) e->e_ctime);
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "T%ld\n", (long) e->e_ctime);
/* output last delivery time */
-# if _FFR_QUEUEDELAY
- fprintf(tfp, "K%ld\n", (long) e->e_dtime);
- fprintf(tfp, "G%d\n", e->e_queuealg);
- fprintf(tfp, "Y%ld\n", (long) e->e_queuedelay);
+#if _FFR_QUEUEDELAY
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime);
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "G%d\n", e->e_queuealg);
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Y%ld\n", (long) e->e_queuedelay);
if (tTd(40, 64))
sm_syslog(LOG_INFO, e->e_id,
"queue alg: %d delay %ld next: %ld (now: %ld)\n",
e->e_queuealg, e->e_queuedelay, e->e_dtime, curtime());
-# else /* _FFR_QUEUEDELAY */
- fprintf(tfp, "K%ld\n", (long) e->e_dtime);
-# endif /* _FFR_QUEUEDELAY */
+#else /* _FFR_QUEUEDELAY */
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime);
+#endif /* _FFR_QUEUEDELAY */
/* output number of delivery attempts */
- fprintf(tfp, "N%d\n", e->e_ntries);
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "N%d\n", e->e_ntries);
/* output message priority */
- fprintf(tfp, "P%ld\n", e->e_msgpriority);
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "P%ld\n", e->e_msgpriority);
+
+ /*
+ ** If data file is in a different directory than the queue file,
+ ** output a "d" record naming the directory of the data file.
+ */
+
+ if (e->e_dfqgrp != e->e_qgrp)
+ {
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "d%s\n",
+ Queue[e->e_dfqgrp]->qg_qpaths[e->e_dfqdir].qp_name);
+ }
/* output inode number of data file */
/* XXX should probably include device major/minor too */
if (e->e_dfino != -1)
{
- /*CONSTCOND*/
- if (sizeof e->e_dfino > sizeof(long))
- fprintf(tfp, "I%ld/%ld/%s\n",
- (long) major(e->e_dfdev),
- (long) minor(e->e_dfdev),
- quad_to_string(e->e_dfino));
- else
- fprintf(tfp, "I%ld/%ld/%lu\n",
- (long) major(e->e_dfdev),
- (long) minor(e->e_dfdev),
- (unsigned long) e->e_dfino);
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "I%ld/%ld/%llu\n",
+ (long) major(e->e_dfdev),
+ (long) minor(e->e_dfdev),
+ (ULONGLONG_T) e->e_dfino);
}
/* output body type */
if (e->e_bodytype != NULL)
- fprintf(tfp, "B%s\n", denlstring(e->e_bodytype, TRUE, FALSE));
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "B%s\n",
+ denlstring(e->e_bodytype, true, false));
-# if _FFR_SAVE_CHARSET
- if (e->e_charset != NULL)
- fprintf(tfp, "X%s\n", denlstring(e->e_charset, TRUE, FALSE));
-# endif /* _FFR_SAVE_CHARSET */
+#if _FFR_QUARANTINE
+ /* quarantine reason */
+ if (e->e_quarmsg != NULL)
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "q%s\n",
+ denlstring(e->e_quarmsg, true, false));
+#endif /* _FFR_QUARANTINE */
/* message from envelope, if it exists */
if (e->e_message != NULL)
- fprintf(tfp, "M%s\n", denlstring(e->e_message, TRUE, FALSE));
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n",
+ denlstring(e->e_message, true, false));
/* send various flag bits through */
p = buf;
@@ -369,28 +613,35 @@ queueup(e, announce)
*p++ = 'd';
if (bitset(EF_NO_BODY_RETN, e->e_flags))
*p++ = 'n';
+ if (bitset(EF_SPLIT, e->e_flags))
+ *p++ = 's';
*p++ = '\0';
if (buf[0] != '\0')
- fprintf(tfp, "F%s\n", buf);
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "F%s\n", buf);
/* save $={persistentMacros} macro values */
- queueup_macros(macid("{persistentMacros}", NULL), tfp, e);
+ queueup_macros(macid("{persistentMacros}"), tfp, e);
/* output name of sender */
if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
p = e->e_sender;
else
p = e->e_from.q_paddr;
- fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE));
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "S%s\n",
+ denlstring(p, true, false));
/* output ESMTP-supplied "original" information */
if (e->e_envid != NULL)
- fprintf(tfp, "Z%s\n", denlstring(e->e_envid, TRUE, FALSE));
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Z%s\n",
+ denlstring(e->e_envid, true, false));
/* output AUTH= parameter */
if (e->e_auth_param != NULL)
- fprintf(tfp, "A%s\n", denlstring(e->e_auth_param,
- TRUE, FALSE));
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "A%s\n",
+ denlstring(e->e_auth_param, true, false));
+ if (e->e_dlvr_flag != 0)
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "!%c %ld\n",
+ (char) e->e_dlvr_flag, e->e_deliver_by);
/* output list of recipient addresses */
printctladdr(NULL, NULL);
@@ -399,40 +650,58 @@ queueup(e, announce)
if (!QS_IS_UNDELIVERED(q->q_state))
continue;
+ /* message for this recipient, if it exists */
+ if (q->q_message != NULL)
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n",
+ denlstring(q->q_message, true,
+ false));
+
printctladdr(q, tfp);
if (q->q_orcpt != NULL)
- fprintf(tfp, "Q%s\n",
- denlstring(q->q_orcpt, TRUE, FALSE));
-
- (void) putc('R', tfp);
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Q%s\n",
+ denlstring(q->q_orcpt, true,
+ false));
+ if (q->q_finalrcpt != NULL)
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "r%s\n",
+ denlstring(q->q_finalrcpt, true,
+ false));
+ (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'R');
if (bitset(QPRIMARY, q->q_flags))
- (void) putc('P', tfp);
+ (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'P');
if (bitset(QHASNOTIFY, q->q_flags))
- (void) putc('N', tfp);
+ (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'N');
if (bitset(QPINGONSUCCESS, q->q_flags))
- (void) putc('S', tfp);
+ (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'S');
if (bitset(QPINGONFAILURE, q->q_flags))
- (void) putc('F', tfp);
+ (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'F');
if (bitset(QPINGONDELAY, q->q_flags))
- (void) putc('D', tfp);
+ (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'D');
if (q->q_alias != NULL &&
bitset(QALIAS, q->q_alias->q_flags))
- (void) putc('A', tfp);
- (void) putc(':', tfp);
- (void) fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE));
+ (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'A');
+ (void) sm_io_putc(tfp, SM_TIME_DEFAULT, ':');
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s\n",
+ denlstring(q->q_paddr, true, false));
if (announce)
{
+ char *tag = "queued";
+
+#if _FFR_QUARANTINE
+ if (e->e_quarmsg != NULL)
+ tag = "quarantined";
+#endif /* _FFR_QUARANTINE */
+
e->e_to = q->q_paddr;
- message("queued");
+ message(tag);
if (LogLevel > 8)
logdelivery(q->q_mailer, NULL, q->q_status,
- "queued", NULL, (time_t) 0, e);
+ tag, NULL, (time_t) 0, e);
e->e_to = NULL;
}
if (tTd(40, 1))
{
- dprintf("queueing ");
- printaddr(q, FALSE);
+ sm_dprintf("queueing ");
+ printaddr(q, false);
}
}
@@ -454,7 +723,7 @@ queueup(e, announce)
mcibuf.mci_mailer = &nullmailer;
mcibuf.mci_out = tfp;
- define('g', "\201f", e);
+ macdefine(&e->e_macro, A_PERM, 'g', "\201f");
for (h = e->e_header; h != NULL; h = h->h_link)
{
if (h->h_value == NULL)
@@ -474,16 +743,18 @@ queueup(e, announce)
}
/* output this header */
- fprintf(tfp, "H?");
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "H?");
/* output conditional macro if present */
if (h->h_macro != '\0')
{
if (bitset(0200, h->h_macro))
- fprintf(tfp, "${%s}",
- macname(bitidx(h->h_macro)));
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT,
+ "${%s}",
+ macname(bitidx(h->h_macro)));
else
- fprintf(tfp, "$%c", h->h_macro);
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT,
+ "$%c", h->h_macro);
}
else if (!bitzerop(h->h_mflags) &&
bitset(H_CHECK|H_ACHECK, h->h_flags))
@@ -493,28 +764,29 @@ queueup(e, announce)
/* if conditional, output the set of conditions */
for (j = '\0'; j <= '\177'; j++)
if (bitnset(j, h->h_mflags))
- (void) putc(j, tfp);
+ (void) sm_io_putc(tfp, SM_TIME_DEFAULT,
+ j);
}
- (void) putc('?', tfp);
+ (void) sm_io_putc(tfp, SM_TIME_DEFAULT, '?');
/* output the header: expand macros, convert addresses */
if (bitset(H_DEFAULT, h->h_flags) &&
!bitset(H_BINDLATE, h->h_flags))
{
- fprintf(tfp, "%s: %s\n",
- h->h_field,
- denlstring(buf, FALSE, TRUE));
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s: %s\n",
+ h->h_field,
+ denlstring(buf, false, true));
}
else if (bitset(H_FROM|H_RCPT, h->h_flags) &&
!bitset(H_BINDLATE, h->h_flags))
{
bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
- FILE *savetrace = TrafficLogFile;
+ SM_FILE_T *savetrace = TrafficLogFile;
TrafficLogFile = NULL;
if (bitset(H_FROM, h->h_flags))
- oldstyle = FALSE;
+ oldstyle = false;
commaize(h, h->h_value, oldstyle, &mcibuf, e);
@@ -522,9 +794,10 @@ queueup(e, announce)
}
else
{
- fprintf(tfp, "%s: %s\n",
- h->h_field,
- denlstring(h->h_value, FALSE, TRUE));
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s: %s\n",
+ h->h_field,
+ denlstring(h->h_value, false,
+ true));
}
}
@@ -535,11 +808,13 @@ queueup(e, announce)
** scurrilous crackers from appending any data.
*/
- fprintf(tfp, ".\n");
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ".\n");
- if (fflush(tfp) != 0 ||
- (SuperSafe && fsync(fileno(tfp)) < 0) ||
- ferror(tfp))
+ if (sm_io_flush(tfp, SM_TIME_DEFAULT) != 0 ||
+ ((SuperSafe == SAFE_REALLY ||
+ (SuperSafe == SAFE_INTERACTIVE && msync)) &&
+ fsync(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL)) < 0) ||
+ sm_io_error(tfp))
{
if (newid)
syserr("!552 Error writing control file %s", tf);
@@ -549,44 +824,112 @@ queueup(e, announce)
if (!newid)
{
- /* rename (locked) tf to be (locked) qf */
- qf = queuename(e, 'q');
+#if _FFR_QUARANTINE
+ char new = queue_letter(e, ANYQFL_LETTER);
+#endif /* _FFR_QUARANTINE */
+
+ /* rename (locked) tf to be (locked) [qh]f */
+ (void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER),
+ sizeof qf);
if (rename(tf, qf) < 0)
syserr("cannot rename(%s, %s), uid=%d",
tf, qf, geteuid());
+# if _FFR_QUARANTINE
+ else
+ {
+ /*
+ ** Check if type has changed and only
+ ** remove the old item if the rename above
+ ** succeeded.
+ */
+
+ if (e->e_qfletter != '\0' &&
+ e->e_qfletter != new)
+ {
+ if (tTd(40, 5))
+ {
+ sm_dprintf("type changed from %c to %c\n",
+ e->e_qfletter, new);
+ }
+
+ if (unlink(queuename(e, e->e_qfletter)) < 0)
+ {
+ /* XXX: something more drastic? */
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "queueup: unlink(%s) failed: %s",
+ queuename(e, e->e_qfletter),
+ sm_errstring(errno));
+ }
+ }
+ }
+ e->e_qfletter = new;
+# endif /* _FFR_QUARANTINE */
+
/*
- ** fsync() after renaming to make sure
- ** metadata is written to disk on
- ** filesystems in which renames are
- ** not guaranteed such as softupdates.
+ ** fsync() after renaming to make sure metadata is
+ ** written to disk on filesystems in which renames are
+ ** not guaranteed.
*/
- if (tfd >= 0 && SuperSafe && fsync(tfd) < 0)
- syserr("!queueup: cannot fsync queue temp file %s", tf);
+ if (SuperSafe != SAFE_NO)
+ {
+ /* for softupdates */
+ if (tfd >= 0 && fsync(tfd) < 0)
+ {
+ syserr("!queueup: cannot fsync queue temp file %s",
+ tf);
+ }
+ SYNC_DIR(qf, true);
+ }
- /* close and unlock old (locked) qf */
+ /* close and unlock old (locked) queue file */
if (e->e_lockfp != NULL)
- (void) fclose(e->e_lockfp);
+ (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
e->e_lockfp = tfp;
+
+ /* save log info */
+ if (LogLevel > 79)
+ sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", qf);
}
else
- qf = tf;
+ {
+ /* save log info */
+ if (LogLevel > 79)
+ sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", tf);
+
+#if _FFR_QUARANTINE
+ e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
+#endif /* _FFR_QUARANTINE */
+ }
+
errno = 0;
e->e_flags |= EF_INQUEUE;
- /* save log info */
- if (LogLevel > 79)
- sm_syslog(LOG_DEBUG, e->e_id, "queueup, qf=%s", qf);
-
if (tTd(40, 1))
- dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
+ sm_dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
return;
}
+/*
+** PRINTCTLADDR -- print control address to file.
+**
+** Parameters:
+** a -- address.
+** tfp -- file pointer.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** The control address (if changed) is printed to the file.
+** The last control address and uid are saved.
+*/
+
static void
printctladdr(a, tfp)
register ADDRESS *a;
- FILE *tfp;
+ SM_FILE_T *tfp;
{
char *user;
register ADDRESS *q;
@@ -599,7 +942,7 @@ printctladdr(a, tfp)
if (a == NULL || a->q_alias == NULL || tfp == NULL)
{
if (lastctladdr != NULL && tfp != NULL)
- fprintf(tfp, "C\n");
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C\n");
lastctladdr = NULL;
lastuid = 0;
return;
@@ -629,77 +972,414 @@ printctladdr(a, tfp)
lastctladdr = a;
if (uid == 0 || user == NULL || user[0] == '\0')
- fprintf(tfp, "C");
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C");
else
- fprintf(tfp, "C%s:%ld:%ld",
- denlstring(user, TRUE, FALSE), (long) uid, (long) gid);
- fprintf(tfp, ":%s\n", denlstring(a->q_paddr, TRUE, FALSE));
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C%s:%ld:%ld",
+ denlstring(user, true, false), (long) uid,
+ (long) gid);
+ (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ":%s\n",
+ denlstring(a->q_paddr, true, false));
}
- /*
+
+/*
+** RUNNERS_SIGTERM -- propagate a SIGTERM to queue runner process
+**
+** This propagates the signal to the child processes that are queue
+** runners. This is for a queue runner "cleanup". After all of the
+** child queue runner processes are signaled (it should be SIGTERM
+** being the sig) then the old signal handler (Oldsh) is called
+** to handle any cleanup set for this process (provided it is not
+** SIG_DFL or SIG_IGN). The signal may not be handled immediately
+** if the BlockOldsh flag is set. If the current process doesn't
+** have a parent then handle the signal immediately, regardless of
+** BlockOldsh.
+**
+** Parameters:
+** sig -- the signal number being sent
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Sets the NoMoreRunners boolean to true to stop more runners
+** from being started in runqueue().
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+static bool volatile NoMoreRunners = false;
+static sigfunc_t Oldsh_term = SIG_DFL;
+static sigfunc_t Oldsh_hup = SIG_DFL;
+static sigfunc_t volatile Oldsh = SIG_DFL;
+static bool BlockOldsh = false;
+static int volatile Oldsig = 0;
+static SIGFUNC_DECL runners_sigterm __P((int));
+static SIGFUNC_DECL runners_sighup __P((int));
+
+static SIGFUNC_DECL
+runners_sigterm(sig)
+ int sig;
+{
+ int save_errno = errno;
+
+ FIX_SYSV_SIGNAL(sig, runners_sigterm);
+ errno = save_errno;
+ CHECK_CRITICAL(sig);
+ NoMoreRunners = true;
+ Oldsh = Oldsh_term;
+ Oldsig = sig;
+ proc_list_signal(PROC_QUEUE, sig);
+
+ if (!BlockOldsh || getppid() <= 1)
+ {
+ /* Check that a valid 'old signal handler' is callable */
+ if (Oldsh_term != SIG_DFL && Oldsh_term != SIG_IGN &&
+ Oldsh_term != runners_sigterm)
+ (*Oldsh_term)(sig);
+ }
+ errno = save_errno;
+ return SIGFUNC_RETURN;
+}
+/*
+** RUNNERS_SIGHUP -- propagate a SIGHUP to queue runner process
+**
+** This propagates the signal to the child processes that are queue
+** runners. This is for a queue runner "cleanup". After all of the
+** child queue runner processes are signaled (it should be SIGHUP
+** being the sig) then the old signal handler (Oldsh) is called to
+** handle any cleanup set for this process (provided it is not SIG_DFL
+** or SIG_IGN). The signal may not be handled immediately if the
+** BlockOldsh flag is set. If the current process doesn't have
+** a parent then handle the signal immediately, regardless of
+** BlockOldsh.
+**
+** Parameters:
+** sig -- the signal number being sent
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Sets the NoMoreRunners boolean to true to stop more runners
+** from being started in runqueue().
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+static SIGFUNC_DECL
+runners_sighup(sig)
+ int sig;
+{
+ int save_errno = errno;
+
+ FIX_SYSV_SIGNAL(sig, runners_sighup);
+ errno = save_errno;
+ CHECK_CRITICAL(sig);
+ NoMoreRunners = true;
+ Oldsh = Oldsh_hup;
+ Oldsig = sig;
+ proc_list_signal(PROC_QUEUE, sig);
+
+ if (!BlockOldsh || getppid() <= 1)
+ {
+ /* Check that a valid 'old signal handler' is callable */
+ if (Oldsh_hup != SIG_DFL && Oldsh_hup != SIG_IGN &&
+ Oldsh_hup != runners_sighup)
+ (*Oldsh_hup)(sig);
+ }
+ errno = save_errno;
+ return SIGFUNC_RETURN;
+}
+/*
+** MARK_WORK_GROUP_RESTART -- mark a work group as needing a restart
+**
+** Sets a workgroup for restarting.
+**
+** Parameters:
+** wgrp -- the work group id to restart.
+** reason -- why (signal?), -1 to turn off restart
+**
+** Returns:
+** none.
+**
+** Side effects:
+** May set global RestartWorkGroup to true.
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+void
+mark_work_group_restart(wgrp, reason)
+ int wgrp;
+ int reason;
+{
+ if (wgrp < 0 || wgrp > NumWorkGroups)
+ return;
+
+ WorkGrp[wgrp].wg_restart = reason;
+ if (reason >= 0)
+ RestartWorkGroup = true;
+}
+/*
+** RESTART_MARKED_WORK_GROUPS -- restart work groups marked as needing restart
+**
+** Restart any workgroup marked as needing a restart provided more
+** runners are allowed.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side effects:
+** Sets global RestartWorkGroup to false.
+*/
+
+void
+restart_marked_work_groups()
+{
+ int i;
+ int wasblocked;
+
+ if (NoMoreRunners)
+ return;
+
+ /* Block SIGCHLD so reapchild() doesn't mess with us */
+ wasblocked = sm_blocksignal(SIGCHLD);
+
+ for (i = 0; i < NumWorkGroups; i++)
+ {
+ if (WorkGrp[i].wg_restart >= 0)
+ {
+ if (LogLevel > 8)
+ sm_syslog(LOG_ERR, NOQID,
+ "restart queue runner=%d due to signal 0x%x",
+ i, WorkGrp[i].wg_restart);
+ restart_work_group(i);
+ }
+ }
+ RestartWorkGroup = false;
+
+ if (wasblocked == 0)
+ (void) sm_releasesignal(SIGCHLD);
+}
+/*
+** RESTART_WORK_GROUP -- restart a specific work group
+**
+** Restart a specific workgroup provided more runners are allowed.
+** If the requested work group has been restarted too many times log
+** this and refuse to restart.
+**
+** Parameters:
+** wgrp -- the work group id to restart
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** starts another process doing the work of wgrp
+*/
+
+#define MAX_PERSIST_RESTART 10 /* max allowed number of restarts */
+
+static void
+restart_work_group(wgrp)
+ int wgrp;
+{
+ if (NoMoreRunners ||
+ wgrp < 0 || wgrp > NumWorkGroups)
+ return;
+
+ WorkGrp[wgrp].wg_restart = -1;
+ if (WorkGrp[wgrp].wg_restartcnt < MAX_PERSIST_RESTART)
+ {
+ /* avoid overflow; increment here */
+ WorkGrp[wgrp].wg_restartcnt++;
+ (void) run_work_group(wgrp, true, false, true, true);
+ }
+ else
+ {
+ sm_syslog(LOG_ERR, NOQID,
+ "ERROR: persistent queue runner=%d restarted too many times, queue runner lost",
+ wgrp);
+ }
+}
+/*
+** SCHEDULE_QUEUE_RUNS -- schedule the next queue run for a work group.
+**
+** Parameters:
+** runall -- schedule even if individual bit is not set.
+** wgrp -- the work group id to schedule.
+**
+** Returns:
+** nothing
+*/
+
+#define INCR_MOD(v, m) if (++v >= m) \
+ v = 0; \
+ else
+
+static void
+schedule_queue_runs(runall, wgrp)
+ bool runall;
+ int wgrp;
+{
+ int qgrp, cgrp, endgrp;
+
+ /*
+ ** This is a bit ugly since we have to duplicate the
+ ** code that "walks" through a work queue group.
+ */
+
+ cgrp = endgrp = WorkGrp[wgrp].wg_curqgrp;
+ do
+ {
+ time_t qintvl;
+
+ qgrp = WorkGrp[wgrp].wg_qgs[cgrp]->qg_index;
+ if (Queue[qgrp]->qg_queueintvl > 0)
+ qintvl = Queue[qgrp]->qg_queueintvl;
+ else if (QueueIntvl > 0)
+ qintvl = QueueIntvl;
+ else
+ qintvl = (time_t) 0;
+ if ((runall || bitnset(qgrp, DoQueueRun)) && qintvl > 0)
+ (void) sm_setevent(qintvl, runqueueevent, qgrp);
+#if _FFR_QUEUE_SCHED_DBG
+ if (tTd(69, 10))
+ sm_syslog(LOG_INFO, NOQID,
+ "sqr: wgrp=%d, cgrp=%d, qgrp=%d, intvl=%ld, QI=%ld, runall=%d, bit=%d, sched=%d",
+ wgrp, cgrp, qgrp, Queue[qgrp]->qg_queueintvl,
+ QueueIntvl, runall, bitnset(qgrp, DoQueueRun),
+ (runall || bitnset(qgrp, DoQueueRun)) &&
+ qintvl > 0);
+#endif /* _FFR_QUEUE_SCHED_DBG */
+ clrbitn(qgrp, DoQueueRun);
+ INCR_MOD(cgrp, WorkGrp[wgrp].wg_numqgrp);
+ } while (endgrp != cgrp);
+}
+/*
** RUNQUEUE -- run the jobs in the queue.
**
** Gets the stuff out of the queue in some presumably logical
** order and processes them.
**
** Parameters:
-** forkflag -- TRUE if the queue scanning should be done in
+** forkflag -- true if the queue scanning should be done in
** a child process. We double-fork so it is not our
** child and we don't have to clean up after it.
-** FALSE can be ignored if we have multiple queues.
-** verbose -- if TRUE, print out status information.
+** false can be ignored if we have multiple queues.
+** verbose -- if true, print out status information.
+** persistent -- persistent queue runner?
+** runall -- run all groups or only a subset (DoQueueRun)?
**
** Returns:
-** TRUE if the queue run successfully began.
+** true if the queue run successfully began.
**
** Side Effects:
-** runs things in the mail queue.
+** runs things in the mail queue using run_work_group().
+** maybe schedules next queue run.
+**
*/
static ENVELOPE QueueEnvelope; /* the queue run envelope */
-int NumQueues = 0; /* number of queues */
static time_t LastQueueTime = 0; /* last time a queue ID assigned */
static pid_t LastQueuePid = -1; /* last PID which had a queue ID */
-struct qpaths_s
-{
- char *qp_name; /* name of queue dir */
- short qp_subdirs; /* use subdirs? */
-};
-
-typedef struct qpaths_s QPATHS;
-
/* values for qp_supdirs */
#define QP_NOSUB 0x0000 /* No subdirectories */
#define QP_SUBDF 0x0001 /* "df" subdirectory */
#define QP_SUBQF 0x0002 /* "qf" subdirectory */
#define QP_SUBXF 0x0004 /* "xf" subdirectory */
-static QPATHS *QPaths = NULL; /* list of queue directories */
-
bool
-runqueue(forkflag, verbose)
+runqueue(forkflag, verbose, persistent, runall)
bool forkflag;
bool verbose;
+ bool persistent;
+ bool runall;
{
int i;
- bool ret = TRUE;
+ bool ret = true;
static int curnum = 0;
+ sigfunc_t cursh;
+#if SM_HEAP_CHECK
+ SM_NONVOLATILE int oldgroup = 0;
+
+ if (sm_debug_active(&DebugLeakQ, 1))
+ {
+ oldgroup = sm_heap_group();
+ sm_heap_newgroup();
+ sm_dprintf("runqueue() heap group #%d\n", sm_heap_group());
+ }
+#endif /* SM_HEAP_CHECK */
+
+ /* queue run has been started, don't do any more this time */
+ clrbitn(NumQueue, DoQueueRun);
- DoQueueRun = FALSE;
+ /* more than one queue or more than one directory per queue */
+ if (!forkflag && !verbose &&
+ (WorkGrp[0].wg_qgs[0]->qg_numqueues > 1 || NumWorkGroups > 1 ||
+ WorkGrp[0].wg_numqgrp > 1))
+ forkflag = true;
+ /*
+ ** For controlling queue runners via signals sent to this process.
+ ** Oldsh* will get called too by runners_sig* (if it is not SIG_IGN
+ ** or SIG_DFL) to preserve cleanup behavior. Now that this process
+ ** will have children (and perhaps grandchildren) this handler will
+ ** be left in place. This is because this process, once it has
+ ** finished spinning off queue runners, may go back to doing something
+ ** else (like being a daemon). And we still want on a SIG{TERM,HUP} to
+ ** clean up the child queue runners. Only install 'runners_sig*' once
+ ** else we'll get stuck looping forever.
+ */
- if (!forkflag && NumQueues > 1 && !verbose)
- forkflag = TRUE;
+ cursh = sm_signal(SIGTERM, runners_sigterm);
+ if (cursh != runners_sigterm)
+ Oldsh_term = cursh;
+ cursh = sm_signal(SIGHUP, runners_sighup);
+ if (cursh != runners_sighup)
+ Oldsh_hup = cursh;
- for (i = 0; i < NumQueues; i++)
+ for (i = 0; i < NumWorkGroups && !NoMoreRunners; i++)
{
/*
- ** Pick up where we left off, in case we
- ** used up all the children last time
- ** without finishing.
+ ** If MaxQueueChildren active then test whether the start
+ ** of the next queue group's additional queue runners (maximum)
+ ** will result in MaxQueueChildren being exceeded.
+ **
+ ** Note: do not use continue; even though another workgroup
+ ** may have fewer queue runners, this would be "unfair",
+ ** i.e., this work group might "starve" then.
+ */
+
+#if _FFR_QUEUE_SCHED_DBG
+ if (tTd(69, 10))
+ sm_syslog(LOG_INFO, NOQID,
+ "rq: curnum=%d, MaxQueueChildren=%d, CurRunners=%d, WorkGrp[curnum].wg_maxact=%d",
+ curnum, MaxQueueChildren, CurRunners,
+ WorkGrp[curnum].wg_maxact);
+#endif /* _FFR_QUEUE_SCHED_DBG */
+ if (MaxQueueChildren > 0 &&
+ CurRunners + WorkGrp[curnum].wg_maxact > MaxQueueChildren)
+ break;
+
+ /*
+ ** Pick up where we left off (curnum), in case we
+ ** used up all the children last time without finishing.
+ ** This give a round-robin fairness to queue runs.
*/
- ret = run_single_queue(curnum, forkflag, verbose);
+ ret = run_work_group(curnum, forkflag, verbose, persistent,
+ runall);
/*
** Failure means a message was printed for ETRN
@@ -709,71 +1389,289 @@ runqueue(forkflag, verbose)
if (!ret)
break;
- if (++curnum >= NumQueues)
- curnum = 0;
+ /* Success means the runner count needs to be updated. */
+ CurRunners += WorkGrp[curnum].wg_maxact;
+ if (!persistent)
+ schedule_queue_runs(runall, curnum);
+ INCR_MOD(curnum, NumWorkGroups);
+ }
+
+ /* schedule left over queue runs */
+ if (i < NumWorkGroups && !NoMoreRunners && !persistent)
+ {
+ int h;
+
+ for (h = curnum; i < NumWorkGroups; i++)
+ {
+ schedule_queue_runs(runall, h);
+ INCR_MOD(h, NumWorkGroups);
+ }
}
- if (QueueIntvl != 0)
- (void) setevent(QueueIntvl, runqueueevent, 0);
+
+
+#if SM_HEAP_CHECK
+ if (sm_debug_active(&DebugLeakQ, 1))
+ sm_heap_setgroup(oldgroup);
+#endif /* SM_HEAP_CHECK */
return ret;
}
- /*
-** RUN_SINGLE_QUEUE -- run the jobs in a single queue.
+/*
+** RUNNER_WORK -- have a queue runner do its work
+**
+** Have a queue runner do its work a list of entries.
+** When work isn't directly being done then this process can take a signal
+** and terminate immediately (in a clean fashion of course).
+** When work is directly being done, it's not to be interrupted
+** immediately: the work should be allowed to finish at a clean point
+** before termination (in a clean fashion of course).
+**
+** Parameters:
+** e -- envelope.
+** sequenceno -- 'th process to run WorkQ.
+** didfork -- did the calling process fork()?
+** skip -- process only each skip'th item.
+** njobs -- number of jobs in WorkQ.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** runs things in the mail queue.
+*/
+
+/* Get new load average every 30 seconds. */
+#define GET_NEW_LA_TIME 30
+
+static void
+runner_work(e, sequenceno, didfork, skip, njobs)
+ register ENVELOPE *e;
+ int sequenceno;
+ bool didfork;
+ int skip;
+ int njobs;
+{
+ int n;
+ WORK *w;
+ time_t current_la_time, now;
+
+ current_la_time = curtime();
+
+ /*
+ ** Here we temporarily block the second calling of the handlers.
+ ** This allows us to handle the signal without terminating in the
+ ** middle of direct work. If a signal does come, the test for
+ ** NoMoreRunners will find it.
+ */
+
+ BlockOldsh = true;
+
+ /* process them once at a time */
+ while (WorkQ != NULL)
+ {
+#if SM_HEAP_CHECK
+ SM_NONVOLATILE int oldgroup = 0;
+
+ if (sm_debug_active(&DebugLeakQ, 1))
+ {
+ oldgroup = sm_heap_group();
+ sm_heap_newgroup();
+ sm_dprintf("run_queue_group() heap group #%d\n",
+ sm_heap_group());
+ }
+#endif /* SM_HEAP_CHECK */
+
+ /* do no more work */
+ if (NoMoreRunners)
+ {
+ /* Check that a valid signal handler is callable */
+ if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
+ Oldsh != runners_sighup &&
+ Oldsh != runners_sigterm)
+ (*Oldsh)(Oldsig);
+ break;
+ }
+
+ w = WorkQ; /* assign current work item */
+
+ /*
+ ** Set the head of the WorkQ to the next work item.
+ ** It is set 'skip' ahead (the number of parallel queue
+ ** runners working on WorkQ together) since each runner
+ ** works on every 'skip'th (N-th) item.
+ */
+
+ for (n = 0; n < skip && WorkQ != NULL; n++)
+ WorkQ = WorkQ->w_next;
+ e->e_to = NULL;
+
+ /*
+ ** Ignore jobs that are too expensive for the moment.
+ **
+ ** Get new load average every GET_NEW_LA_TIME seconds.
+ */
+
+ now = curtime();
+ if (current_la_time < now - GET_NEW_LA_TIME)
+ {
+ sm_getla();
+ current_la_time = now;
+ }
+ if (shouldqueue(WkRecipFact, current_la_time))
+ {
+ char *msg = "Aborting queue run: load average too high";
+
+ if (Verbose)
+ message("%s", msg);
+ if (LogLevel > 8)
+ sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
+ break;
+ }
+ if (shouldqueue(w->w_pri, w->w_ctime))
+ {
+ if (Verbose)
+ message(EmptyString);
+ if (QueueSortOrder == QSO_BYPRIORITY)
+ {
+ if (Verbose)
+ message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue",
+ qid_printqueue(w->w_qgrp,
+ w->w_qdir),
+ w->w_name + 2, sequenceno,
+ njobs);
+ if (LogLevel > 8)
+ sm_syslog(LOG_INFO, NOQID,
+ "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)",
+ qid_printqueue(w->w_qgrp,
+ w->w_qdir),
+ w->w_name + 2, w->w_pri,
+ CurrentLA, sequenceno,
+ njobs);
+ break;
+ }
+ else if (Verbose)
+ message("Skipping %s/%s (sequence %d of %d)",
+ qid_printqueue(w->w_qgrp, w->w_qdir),
+ w->w_name + 2, sequenceno, njobs);
+ }
+ else
+ {
+ if (Verbose)
+ {
+ message(EmptyString);
+ message("Running %s/%s (sequence %d of %d)",
+ qid_printqueue(w->w_qgrp, w->w_qdir),
+ w->w_name + 2, sequenceno, njobs);
+ }
+ if (didfork && MaxQueueChildren > 0)
+ {
+ sm_blocksignal(SIGCHLD);
+ (void) sm_signal(SIGCHLD, reapchild);
+ }
+ if (tTd(63, 100))
+ sm_syslog(LOG_DEBUG, NOQID,
+ "runqueue %s dowork(%s)",
+ qid_printqueue(w->w_qgrp, w->w_qdir),
+ w->w_name + 2);
+
+ (void) dowork(w->w_qgrp, w->w_qdir, w->w_name + 2,
+ false, false, e);
+ errno = 0;
+ }
+ sm_free(w->w_name); /* XXX */
+ if (w->w_host != NULL)
+ sm_free(w->w_host); /* XXX */
+ sm_free((char *) w); /* XXX */
+ sequenceno += skip; /* next sequence number */
+#if SM_HEAP_CHECK
+ if (sm_debug_active(&DebugLeakQ, 1))
+ sm_heap_setgroup(oldgroup);
+#endif /* SM_HEAP_CHECK */
+ }
+
+ BlockOldsh = false;
+
+ /* check the signals didn't happen during the revert */
+ if (NoMoreRunners)
+ {
+ /* Check that a valid signal handler is callable */
+ if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
+ Oldsh != runners_sighup && Oldsh != runners_sigterm)
+ (*Oldsh)(Oldsig);
+ }
+
+ Oldsh = SIG_DFL; /* after the NoMoreRunners check */
+}
+/*
+** RUN_WORK_GROUP -- run the jobs in a queue group from a work group.
**
** Gets the stuff out of the queue in some presumably logical
** order and processes them.
**
** Parameters:
-** queuedir -- queue to process
-** forkflag -- TRUE if the queue scanning should be done in
+** wgrp -- work group to process.
+** forkflag -- true if the queue scanning should be done in
** a child process. We double-fork so it is not our
** child and we don't have to clean up after it.
-** verbose -- if TRUE, print out status information.
+** verbose -- if true, print out status information.
+** persistent -- persistent queue runner?
+** runall -- true: run all of the queue groups in this work group
**
** Returns:
-** TRUE if the queue run successfully began.
+** true if the queue run successfully began.
**
** Side Effects:
** runs things in the mail queue.
*/
-static bool
-run_single_queue(queuedir, forkflag, verbose)
- int queuedir;
+/* Minimum sleep time for persistent queue runners */
+#define MIN_SLEEP_TIME 5
+
+bool
+run_work_group(wgrp, forkflag, verbose, persistent, runall)
+ int wgrp;
bool forkflag;
bool verbose;
+ bool persistent;
+ bool runall;
{
register ENVELOPE *e;
- int njobs;
- int sequenceno = 0;
- time_t current_la_time, now;
+ int njobs, qdir;
+ int sequenceno = 1;
+ int qgrp, endgrp, h, i;
+ time_t current_la_time;
+ bool full, more;
+ SM_RPOOL_T *rpool;
+ extern void rmexpstab __P((void));
extern ENVELOPE BlankEnvelope;
+ extern SIGFUNC_DECL reapchild __P((int));
+
+ if (wgrp < 0)
+ return false;
/*
** If no work will ever be selected, don't even bother reading
** the queue.
*/
- CurrentLA = sm_getla(NULL); /* get load average */
+ sm_getla(); /* get load average */
current_la_time = curtime();
- if (shouldqueue(WkRecipFact, current_la_time))
+ if (!persistent && shouldqueue(WkRecipFact, current_la_time))
{
char *msg = "Skipping queue run -- load average too high";
if (verbose)
message("458 %s\n", msg);
if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID,
- "runqueue: %s",
- msg);
- return FALSE;
+ sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
+ return false;
}
/*
** See if we already have too many children.
*/
- if (forkflag && QueueIntvl != 0 &&
+ if (forkflag && WorkGrp[wgrp].wg_lowqintvl > 0 && !persistent &&
MaxChildren > 0 && CurChildren >= MaxChildren)
{
char *msg = "Skipping queue run -- too many children";
@@ -781,10 +1679,9 @@ run_single_queue(queuedir, forkflag, verbose)
if (verbose)
message("458 %s (%d)\n", msg, CurChildren);
if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID,
- "runqueue: %s (%d)",
+ sm_syslog(LOG_INFO, NOQID, "runqueue: %s (%d)",
msg, CurChildren);
- return FALSE;
+ return false;
}
/*
@@ -795,81 +1692,84 @@ run_single_queue(queuedir, forkflag, verbose)
{
pid_t pid;
- (void) blocksignal(SIGCHLD);
- (void) setsignal(SIGCHLD, reapchild);
+ (void) sm_blocksignal(SIGCHLD);
+ (void) sm_signal(SIGCHLD, reapchild);
pid = dofork();
if (pid == -1)
{
const char *msg = "Skipping queue run -- fork() failed";
- const char *err = errstring(errno);
+ const char *err = sm_errstring(errno);
if (verbose)
message("458 %s: %s\n", msg, err);
if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID,
- "runqueue: %s: %s",
+ sm_syslog(LOG_INFO, NOQID, "runqueue: %s: %s",
msg, err);
- (void) releasesignal(SIGCHLD);
- return FALSE;
+ (void) sm_releasesignal(SIGCHLD);
+ return false;
}
if (pid != 0)
{
/* parent -- pick up intermediate zombie */
- (void) blocksignal(SIGALRM);
- proc_list_add(pid, "Queue runner", PROC_QUEUE);
- (void) releasesignal(SIGALRM);
- (void) releasesignal(SIGCHLD);
- return TRUE;
+ (void) sm_blocksignal(SIGALRM);
+
+ /* wgrp only used when queue runners are persistent */
+ proc_list_add(pid, "Queue runner", PROC_QUEUE,
+ WorkGrp[wgrp].wg_maxact,
+ persistent ? wgrp : -1);
+ (void) sm_releasesignal(SIGALRM);
+ (void) sm_releasesignal(SIGCHLD);
+ return true;
}
+
/* child -- clean up signals */
/* Reset global flags */
RestartRequest = NULL;
+ RestartWorkGroup = false;
ShutdownRequest = NULL;
PendingSignal = 0;
+ CurrentPid = getpid();
+ /*
+ ** Initialize exception stack and default exception
+ ** handler for child process.
+ */
+
+ sm_exc_newthread(fatal_error);
clrcontrol();
proc_list_clear();
/* Add parent process as first child item */
- proc_list_add(getpid(), "Queue runner child process",
- PROC_QUEUE_CHILD);
- (void) releasesignal(SIGCHLD);
- (void) setsignal(SIGCHLD, SIG_DFL);
- (void) setsignal(SIGHUP, SIG_DFL);
- (void) setsignal(SIGTERM, intsig);
+ proc_list_add(CurrentPid, "Queue runner child process",
+ PROC_QUEUE_CHILD, 0, -1);
+ (void) sm_releasesignal(SIGCHLD);
+ (void) sm_signal(SIGCHLD, SIG_DFL);
+ (void) sm_signal(SIGHUP, SIG_DFL);
+ (void) sm_signal(SIGTERM, intsig);
}
- sm_setproctitle(TRUE, CurEnv, "running queue: %s",
- qid_printqueue(queuedir));
-
- if (LogLevel > 69 || tTd(63, 99))
- sm_syslog(LOG_DEBUG, NOQID,
- "runqueue %s, pid=%d, forkflag=%d",
- qid_printqueue(queuedir), (int) getpid(), forkflag);
-
/*
** Release any resources used by the daemon code.
*/
-# if DAEMON
clrdaemon();
-# endif /* DAEMON */
/* force it to run expensive jobs */
- NoConnect = FALSE;
+ NoConnect = false;
/* drop privileges */
if (geteuid() == (uid_t) 0)
- (void) drop_privileges(FALSE);
+ (void) drop_privileges(false);
/*
** Create ourselves an envelope
*/
CurEnv = &QueueEnvelope;
- e = newenvelope(&QueueEnvelope, CurEnv);
+ rpool = sm_rpool_new_x(NULL);
+ e = newenvelope(&QueueEnvelope, CurEnv, rpool);
e->e_flags = BlankEnvelope.e_flags;
e->e_parent = NULL;
@@ -877,7 +1777,7 @@ run_single_queue(queuedir, forkflag, verbose)
if (forkflag)
{
disconnect(1, e);
- QuickAbort = FALSE;
+ QuickAbort = false;
}
/*
@@ -886,200 +1786,494 @@ run_single_queue(queuedir, forkflag, verbose)
*/
if (QueueLimitId != NULL || QueueLimitSender != NULL ||
+#if _FFR_QUARANTINE
+ QueueLimitQuarantine != NULL ||
+#endif /* _FFR_QUARANTINE */
QueueLimitRecipient != NULL)
{
- IgnoreHostStatus = TRUE;
+ IgnoreHostStatus = true;
MinQueueAge = 0;
}
/*
+ ** Here is where we choose the queue group from the work group.
+ ** The caller of the "domorework" label must setup a new envelope.
+ */
+
+ endgrp = WorkGrp[wgrp].wg_curqgrp; /* to not spin endlessly */
+
+ domorework:
+
+ /*
+ ** Run a queue group if:
+ ** runall is set or the bit for this group is set.
+ */
+
+ for (;;)
+ {
+ /*
+ ** Find the next queue group within the work group that
+ ** has been marked as needing a run.
+ */
+
+ qgrp = WorkGrp[wgrp].wg_qgs[WorkGrp[wgrp].wg_curqgrp]->qg_index;
+ WorkGrp[wgrp].wg_curqgrp++; /* advance */
+ WorkGrp[wgrp].wg_curqgrp %= WorkGrp[wgrp].wg_numqgrp; /* wrap */
+ if (runall || bitnset(qgrp, DoQueueRun))
+ break;
+ if (endgrp == WorkGrp[wgrp].wg_curqgrp)
+ {
+ e->e_id = NULL;
+ if (forkflag)
+ finis(true, true, ExitStat);
+ return true; /* we're done */
+ }
+ }
+
+ qdir = Queue[qgrp]->qg_curnum; /* round-robin init of queue position */
+#if _FFR_QUEUE_SCHED_DBG
+ if (tTd(69, 12))
+ sm_syslog(LOG_INFO, NOQID,
+ "rwg: wgrp=%d, qgrp=%d, qdir=%d, name=%s, curqgrp=%d, numgrps=%d",
+ wgrp, qgrp, qdir, qid_printqueue(qgrp, qdir),
+ WorkGrp[wgrp].wg_curqgrp, WorkGrp[wgrp].wg_numqgrp);
+#endif /* _FFR_QUEUE_SCHED_DBG */
+
+#if HASNICE
+ /* tweak niceness of queue runs */
+ if (Queue[qgrp]->qg_nice > 0)
+ (void) nice(Queue[qgrp]->qg_nice);
+#endif /* HASNICE */
+
+ /* XXX running queue group... */
+ sm_setproctitle(true, CurEnv, "running queue: %s",
+ qid_printqueue(qgrp, qdir));
+
+ if (LogLevel > 69 || tTd(63, 99))
+ sm_syslog(LOG_DEBUG, NOQID,
+ "runqueue %s, pid=%d, forkflag=%d",
+ qid_printqueue(qgrp, qdir), (int) CurrentPid,
+ forkflag);
+
+ /*
** Start making passes through the queue.
** First, read and sort the entire queue.
** Then, process the work in that order.
** But if you take too long, start over.
*/
+ for (i = 0; i < Queue[qgrp]->qg_numqueues; i++)
+ {
+ h = gatherq(qgrp, qdir, false, &full, &more);
+#if SM_CONF_SHM
+ if (ShmId != SM_SHM_NO_ID)
+ QSHM_ENTRIES(Queue[qgrp]->qg_qpaths[qdir].qp_idx) = h;
+#endif /* SM_CONF_SHM */
+ /* If there are no more items in this queue advance */
+ if (!more)
+ {
+ /* A round-robin advance */
+ qdir++;
+ qdir %= Queue[qgrp]->qg_numqueues;
+ }
+
+ /* Has the WorkList reached the limit? */
+ if (full)
+ break; /* don't try to gather more */
+ }
+
/* order the existing work requests */
- njobs = orderq(queuedir, FALSE);
+ njobs = sortq(Queue[qgrp]->qg_maxlist);
+ Queue[qgrp]->qg_curnum = qdir; /* update */
- /* process them once at a time */
- while (WorkQ != NULL)
+ if (!Verbose && bitnset(QD_FORK, Queue[qgrp]->qg_flags))
{
- WORK *w = WorkQ;
-
- WorkQ = WorkQ->w_next;
- e->e_to = NULL;
+ int loop, maxrunners;
+ pid_t pid;
/*
- ** Ignore jobs that are too expensive for the moment.
- **
- ** Get new load average every 30 seconds.
+ ** For this WorkQ we want to fork off N children (maxrunners)
+ ** at this point. Each child has a copy of WorkQ. Each child
+ ** will process every N-th item. The parent will wait for all
+ ** of the children to finish before moving on to the next
+ ** queue group within the work group. This saves us forking
+ ** a new runner-child for each work item.
+ ** It's valid for qg_maxqrun == 0 since this may be an
+ ** explicit "don't run this queue" setting.
*/
- now = curtime();
- if (current_la_time < now - 30)
+ maxrunners = Queue[qgrp]->qg_maxqrun;
+
+ /* No need to have more runners then there are jobs */
+ if (maxrunners > njobs)
+ maxrunners = njobs;
+ for (loop = 0; loop < maxrunners; loop++)
{
- CurrentLA = sm_getla(e);
- current_la_time = now;
+ /*
+ ** Since the delivery may happen in a child and the
+ ** parent does not wait, the parent may close the
+ ** maps thereby removing any shared memory used by
+ ** the map. Therefore, close the maps now so the
+ ** child will dynamically open them if necessary.
+ */
+
+ closemaps(false);
+
+ pid = fork();
+ if (pid < 0)
+ {
+ syserr("run_work_group: cannot fork");
+ return 0;
+ }
+ else if (pid > 0)
+ {
+ /* parent -- clean out connection cache */
+ mci_flush(false, NULL);
+ WorkQ = WorkQ->w_next; /* for the skip */
+ sequenceno++;
+ proc_list_add(pid, "Queue child runner process",
+ PROC_QUEUE_CHILD, 0, -1);
+
+ /* No additional work, no additional runners */
+ if (WorkQ == NULL)
+ break;
+ }
+ else
+ {
+ /* child -- Reset global flags */
+ RestartRequest = NULL;
+ RestartWorkGroup = false;
+ ShutdownRequest = NULL;
+ PendingSignal = 0;
+ CurrentPid = getpid();
+
+ /*
+ ** Initialize exception stack and default
+ ** exception handler for child process.
+ ** When fork()'d the child now has a private
+ ** copy of WorkQ at its current position.
+ */
+
+ sm_exc_newthread(fatal_error);
+
+ /*
+ ** SMTP processes (whether -bd or -bs) set
+ ** SIGCHLD to reapchild to collect
+ ** children status. However, at delivery
+ ** time, that status must be collected
+ ** by sm_wait() to be dealt with properly
+ ** (check success of delivery based
+ ** on status code, etc). Therefore, if we
+ ** are an SMTP process, reset SIGCHLD
+ ** back to the default so reapchild
+ ** doesn't collect status before
+ ** sm_wait().
+ */
+
+ if (OpMode == MD_SMTP ||
+ OpMode == MD_DAEMON ||
+ MaxQueueChildren > 0)
+ {
+ proc_list_clear();
+ sm_releasesignal(SIGCHLD);
+ (void) sm_signal(SIGCHLD, SIG_DFL);
+ }
+
+ /* child -- error messages to the transcript */
+ QuickAbort = OnlyOneError = false;
+ runner_work(e, sequenceno, true,
+ maxrunners, njobs);
+
+ /* This child is done */
+ finis(true, true, ExitStat);
+ /* NOTREACHED */
+ }
}
- if (shouldqueue(WkRecipFact, current_la_time))
+
+ sm_releasesignal(SIGCHLD);
+
+ /*
+ ** Wait until all of the runners have completed before
+ ** seeing if there is another queue group in the
+ ** work group to process.
+ ** XXX Future enhancement: don't wait() for all children
+ ** here, just go ahead and make sure that overall the number
+ ** of children is not exceeded.
+ */
+
+ while (CurChildren > 0)
{
- char *msg = "Aborting queue run: load average too high";
+ int status;
+ pid_t ret;
- if (Verbose)
- message("%s", msg);
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID,
- "runqueue: %s",
- msg);
- break;
+ while ((ret = sm_wait(&status)) <= 0)
+ continue;
+ proc_list_drop(ret, status, NULL);
}
- sequenceno++;
- if (shouldqueue(w->w_pri, w->w_ctime))
+ }
+ else
+ {
+ /*
+ ** When current process will not fork children to do the work,
+ ** it will do the work itself. The 'skip' will be 1 since
+ ** there are no child runners to divide the work across.
+ */
+
+ runner_work(e, sequenceno, false, 1, njobs);
+ }
+
+ /* free memory allocated by newenvelope() above */
+ sm_rpool_free(rpool);
+ QueueEnvelope.e_rpool = NULL;
+
+ /* Are there still more queues in the work group to process? */
+ if (endgrp != WorkGrp[wgrp].wg_curqgrp)
+ {
+ rpool = sm_rpool_new_x(NULL);
+ e = newenvelope(&QueueEnvelope, CurEnv, rpool);
+ e->e_flags = BlankEnvelope.e_flags;
+ goto domorework;
+ }
+
+ /* No more queues in work group to process. Now check persistent. */
+ if (persistent)
+ {
+ time_t now;
+
+ sequenceno = 1;
+ sm_setproctitle(true, CurEnv, "running queue: %s",
+ qid_printqueue(qgrp, qdir));
+
+ /*
+ ** close bogus maps, i.e., maps which caused a tempfail,
+ ** so we get fresh map connections on the next lookup.
+ ** closemaps() is also called when children are started.
+ */
+
+ closemaps(true);
+
+ /* Close any cached connections. */
+ mci_flush(true, NULL);
+
+ /* Clean out expired related entries. */
+ rmexpstab();
+
+#if NAMED_BIND
+ /* Update MX records for FallBackMX. */
+ if (FallBackMX != NULL)
+ (void) getfallbackmxrr(FallBackMX);
+#endif /* NAMED_BIND */
+
+#if USERDB
+ /* close UserDatabase */
+ _udbx_close();
+#endif /* USERDB */
+
+#if SM_HEAP_CHECK
+ if (sm_debug_active(&SmHeapCheck, 2)
+ && access("memdump", F_OK) == 0
+ )
{
- if (Verbose)
- message("");
- if (QueueSortOrder == QSO_BYPRIORITY)
+ SM_FILE_T *out;
+
+ remove("memdump");
+ out = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
+ "memdump.out", SM_IO_APPEND, NULL);
+ if (out != NULL)
{
- if (Verbose)
- message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue",
- qid_printqueue(queuedir),
- w->w_name + 2,
- sequenceno,
- njobs);
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID,
- "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)",
- qid_printqueue(queuedir),
- w->w_name + 2,
- w->w_pri,
- CurrentLA,
- sequenceno,
- njobs);
- break;
+ (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n");
+ sm_heap_report(out,
+ sm_debug_level(&SmHeapCheck) - 1);
+ (void) sm_io_close(out, SM_TIME_DEFAULT);
}
- else if (Verbose)
- message("Skipping %s/%s (sequence %d of %d)",
- qid_printqueue(queuedir),
- w->w_name + 2,
- sequenceno, njobs);
}
+#endif /* SM_HEAP_CHECK */
+
+ /* let me rest for a second to catch my breath */
+ if (njobs == 0 && WorkGrp[wgrp].wg_lowqintvl < MIN_SLEEP_TIME)
+ sleep(MIN_SLEEP_TIME);
+ else if (WorkGrp[wgrp].wg_lowqintvl <= 0)
+ sleep(QueueIntvl > 0 ? QueueIntvl : MIN_SLEEP_TIME);
else
- {
- pid_t pid;
+ sleep(WorkGrp[wgrp].wg_lowqintvl);
- if (Verbose)
- {
- message("");
- message("Running %s/%s (sequence %d of %d)",
- qid_printqueue(queuedir),
- w->w_name + 2,
- sequenceno, njobs);
- }
- if (tTd(63, 100))
- sm_syslog(LOG_DEBUG, NOQID,
- "runqueue %s dowork(%s)",
- qid_printqueue(queuedir),
- w->w_name + 2);
+ /*
+ ** Get the LA outside the WorkQ loop if necessary.
+ ** In a persistent queue runner the code is repeated over
+ ** and over but gatherq() may ignore entries due to
+ ** shouldqueue() (do we really have to do this twice?).
+ ** Hence the queue runners would just idle around when once
+ ** CurrentLA caused all entries in a queue to be ignored.
+ */
- pid = dowork(queuedir, w->w_name + 2,
- ForkQueueRuns, FALSE, e);
- errno = 0;
- if (pid != 0)
- (void) waitfor(pid);
+ now = curtime();
+ if (njobs == 0 && current_la_time < now - GET_NEW_LA_TIME)
+ {
+ sm_getla();
+ current_la_time = now;
}
- sm_free(w->w_name);
- if (w->w_host)
- sm_free(w->w_host);
- sm_free((char *) w);
+ rpool = sm_rpool_new_x(NULL);
+ e = newenvelope(&QueueEnvelope, CurEnv, rpool);
+ e->e_flags = BlankEnvelope.e_flags;
+ goto domorework;
}
/* exit without the usual cleanup */
e->e_id = NULL;
if (forkflag)
- finis(TRUE, ExitStat);
+ finis(true, true, ExitStat);
/* NOTREACHED */
- return TRUE;
+ return true;
}
/*
-** RUNQUEUEEVENT -- stub for use in setevent
+** DOQUEUERUN -- do a queue run?
+*/
+
+bool
+doqueuerun()
+{
+ return bitnset(NumQueue, DoQueueRun);
+}
+
+/*
+** RUNQUEUEEVENT -- stub for use in sm_setevent
+**
+** Sets the bit to indicate that on the next run this queue should be
+** processed. The work group that the queue group is a member of has its
+** count of queue's to process updated.
**
** Parameters:
-** none.
+** qgrp -- the index of the queue group.
**
** Returns:
** none.
**
+** Side Effects:
+** The work group that the queue group is a member of has its
+** count of queues to process updated.
+** The invocation of this function via an alarm may interrupt
+** a set of actions. Thus errno may be set in that context.
+** We need to restore errno at the end of this function to ensure
+** that any work done here that sets errno doesn't return a
+** misleading/false errno value. Errno may be EINTR upon entry to
+** this function because of non-restartable/continuable system
+** API was active. Iff this is true we will override errno as
+** a timeout (as a more accurate error message).
+**
** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
** DOING.
*/
-static void
-runqueueevent()
+void
+runqueueevent(qgrp)
+ int qgrp;
{
- DoQueueRun = TRUE;
+ int i;
+ int save_errno = errno;
+
+ /*
+ ** Set the general bit that we want a queue run,
+ ** tested in doqueuerun()
+ */
+
+ setbitn(NumQueue, DoQueueRun);
+
+ /* if it is a specific group: set that bit */
+ if (qgrp != NOQGRP)
+ {
+ setbitn(qgrp, DoQueueRun);
+ goto ret;
+ }
+
+ /* for all others: set the bit if it doesn't have a queue interval */
+ for (i = 0; i < NumQueue; i++)
+ {
+ if (Queue[i]->qg_queueintvl <= 0)
+ setbitn(i, DoQueueRun);
+ }
+
+ ret:
+ errno = save_errno;
+ if (errno == EINTR)
+ errno = ETIMEDOUT;
}
- /*
-** ORDERQ -- order the work queue.
+/*
+** GATHERQ -- gather messages from the message queue(s) the work queue.
**
** Parameters:
-** queuedir -- the index of the queue directory.
+** qgrp -- the index of the queue group.
+** qdir -- the index of the queue directory.
** doall -- if set, include everything in the queue (even
** the jobs that cannot be run because the load
-** average is too high). Otherwise, exclude those
-** jobs.
+** average is too high, or MaxQueueRun is reached).
+** Otherwise, exclude those jobs.
+** full -- (optional) to be set 'true' if WorkList is full
+** more -- (optional) to be set 'true' if there are still more
+** messages in this queue not added to WorkList
**
** Returns:
** The number of request in the queue (not necessarily
-** the number of requests in WorkQ however).
+** the number of requests in WorkList however).
**
** Side Effects:
-** Sets WorkQ to the queue of available work, in order.
+** prepares available work into WorkList
*/
-# define NEED_P 001
-# define NEED_T 002
-# define NEED_R 004
-# define NEED_S 010
-# define NEED_H 020
+#define NEED_P 0001 /* 'P': priority */
+#define NEED_T 0002 /* 'T': time */
+#define NEED_R 0004 /* 'R': recipient */
+#define NEED_S 0010 /* 'S': sender */
+#define NEED_H 0020 /* host */
+#if _FFR_QUARANTINE
+# define HAS_QUARANTINE 0040 /* has an unexpected 'q' line */
+# define NEED_QUARANTINE 0100 /* 'q': reason */
+#endif /* _FFR_QUARANTINE */
-static WORK *WorkList = NULL;
-static int WorkListSize = 0;
+static WORK *WorkList = NULL; /* list of unsort work */
+static int WorkListSize = 0; /* current max size of WorkList */
+static int WorkListCount = 0; /* # of work items in WorkList */
static int
-orderq(queuedir, doall)
- int queuedir;
+gatherq(qgrp, qdir, doall, full, more)
+ int qgrp;
+ int qdir;
bool doall;
+ bool *full;
+ bool *more;
{
register struct dirent *d;
register WORK *w;
register char *p;
DIR *f;
- register int i;
- int wn = -1;
- int wc;
+ int i, num_ent;
+ int wn;
QUEUE_CHAR *check;
char qd[MAXPATHLEN];
char qf[MAXPATHLEN];
- if (queuedir == NOQDIR)
- (void) strlcpy(qd, ".", sizeof qd);
+ wn = WorkListCount - 1;
+ num_ent = 0;
+ if (qdir == NOQDIR)
+ (void) sm_strlcpy(qd, ".", sizeof qd);
else
- (void) snprintf(qd, sizeof qd, "%s%s",
- QPaths[queuedir].qp_name,
- (bitset(QP_SUBQF, QPaths[queuedir].qp_subdirs) ? "/qf" : ""));
+ (void) sm_strlcpyn(qd, sizeof qd, 2,
+ Queue[qgrp]->qg_qpaths[qdir].qp_name,
+ (bitset(QP_SUBQF,
+ Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
+ ? "/qf" : ""));
if (tTd(41, 1))
{
- dprintf("orderq:\n");
+ sm_dprintf("gatherq:\n");
check = QueueLimitId;
while (check != NULL)
{
- dprintf("\tQueueLimitId = %s\n",
+ sm_dprintf("\tQueueLimitId = %s%s\n",
+ check->queue_negate ? "!" : "",
check->queue_match);
check = check->queue_next;
}
@@ -1087,7 +2281,8 @@ orderq(queuedir, doall)
check = QueueLimitSender;
while (check != NULL)
{
- dprintf("\tQueueLimitSender = %s\n",
+ sm_dprintf("\tQueueLimitSender = %s%s\n",
+ check->queue_negate ? "!" : "",
check->queue_match);
check = check->queue_next;
}
@@ -1095,30 +2290,37 @@ orderq(queuedir, doall)
check = QueueLimitRecipient;
while (check != NULL)
{
- dprintf("\tQueueLimitRecipient = %s\n",
+ sm_dprintf("\tQueueLimitRecipient = %s%s\n",
+ check->queue_negate ? "!" : "",
check->queue_match);
check = check->queue_next;
}
- }
- /* clear out old WorkQ */
- for (w = WorkQ; w != NULL; )
- {
- register WORK *nw = w->w_next;
-
- WorkQ = nw;
- sm_free(w->w_name);
- if (w->w_host != NULL)
- sm_free(w->w_host);
- sm_free((char *) w);
- w = nw;
+#if _FFR_QUARANTINE
+ if (QueueMode == QM_QUARANTINE)
+ {
+ check = QueueLimitQuarantine;
+ while (check != NULL)
+ {
+ sm_dprintf("\tQueueLimitQuarantine = %s%s\n",
+ check->queue_negate ? "!" : "",
+ check->queue_match);
+ check = check->queue_next;
+ }
+ }
+#endif /* _FFR_QUARANTINE */
}
/* open the queue directory */
f = opendir(qd);
if (f == NULL)
{
- syserr("orderq: cannot open \"%s\"", qid_printqueue(queuedir));
+ syserr("gatherq: cannot open \"%s\"",
+ qid_printqueue(qgrp, qdir));
+ if (full != NULL)
+ *full = WorkListCount >= MaxQueueRun && MaxQueueRun > 0;
+ if (more != NULL)
+ *more = false;
return 0;
}
@@ -1128,26 +2330,43 @@ orderq(queuedir, doall)
while ((d = readdir(f)) != NULL)
{
- FILE *cf;
+ SM_FILE_T *cf;
int qfver = 0;
char lbuf[MAXNAME + 1];
struct stat sbuf;
if (tTd(41, 50))
- dprintf("orderq: checking %s\n", d->d_name);
+ sm_dprintf("gatherq: checking %s..", d->d_name);
/* is this an interesting entry? */
- if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
+#if _FFR_QUARANTINE
+ if (!(((QueueMode == QM_NORMAL &&
+ d->d_name[0] == NORMQF_LETTER) ||
+ (QueueMode == QM_QUARANTINE &&
+ d->d_name[0] == QUARQF_LETTER) ||
+ (QueueMode == QM_LOST &&
+ d->d_name[0] == LOSEQF_LETTER)) &&
+ d->d_name[1] == 'f'))
+#else /* _FFR_QUARANTINE */
+ if (d->d_name[0] != NORMQF_LETTER || d->d_name[1] != 'f')
+#endif /* _FFR_QUARANTINE */
+ {
+ if (tTd(41, 50))
+ sm_dprintf(" skipping\n");
continue;
+ }
+ if (tTd(41, 50))
+ sm_dprintf("\n");
if (strlen(d->d_name) >= MAXQFNAME)
{
if (Verbose)
- printf("orderq: %s too long, %d max characters\n",
- d->d_name, MAXQFNAME);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "gatherq: %s too long, %d max characters\n",
+ d->d_name, MAXQFNAME);
if (LogLevel > 0)
sm_syslog(LOG_ALERT, NOQID,
- "orderq: %s too long, %d max characters",
+ "gatherq: %s too long, %d max characters",
d->d_name, MAXQFNAME);
continue;
}
@@ -1155,7 +2374,8 @@ orderq(queuedir, doall)
check = QueueLimitId;
while (check != NULL)
{
- if (strcontainedin(check->queue_match, d->d_name))
+ if (strcontainedin(true, check->queue_match,
+ d->d_name) != check->queue_negate)
break;
else
check = check->queue_next;
@@ -1169,76 +2389,109 @@ orderq(queuedir, doall)
if (wn == MaxQueueRun && LogLevel > 0)
sm_syslog(LOG_WARNING, NOQID,
"WorkList for %s maxed out at %d",
- qid_printqueue(queuedir),
+ qid_printqueue(qgrp, qdir),
MaxQueueRun);
- continue;
+ if (doall)
+ continue; /* just count entries */
+ break;
}
if (wn >= WorkListSize)
{
- grow_wlist(queuedir);
+ grow_wlist(qgrp, qdir);
if (wn >= WorkListSize)
continue;
}
+ SM_ASSERT(wn >= 0);
w = &WorkList[wn];
- (void) snprintf(qf, sizeof qf, "%s/%s", qd, d->d_name);
+ (void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", d->d_name);
if (stat(qf, &sbuf) < 0)
{
if (errno != ENOENT)
sm_syslog(LOG_INFO, NOQID,
- "orderq: can't stat %s/%s",
- qid_printqueue(queuedir), d->d_name);
+ "gatherq: can't stat %s/%s",
+ qid_printqueue(qgrp, qdir),
+ d->d_name);
wn--;
continue;
}
if (!bitset(S_IFREG, sbuf.st_mode))
{
/* Yikes! Skip it or we will hang on open! */
- syserr("orderq: %s/%s is not a regular file",
- qid_printqueue(queuedir), d->d_name);
+ if (!((d->d_name[0] == DATAFL_LETTER ||
+ d->d_name[0] == NORMQF_LETTER ||
+#if _FFR_QUARANTINE
+ d->d_name[0] == QUARQF_LETTER ||
+ d->d_name[0] == LOSEQF_LETTER ||
+#endif /* _FFR_QUARANTINE */
+ d->d_name[0] == XSCRPT_LETTER) &&
+ d->d_name[1] == 'f' && d->d_name[2] == '\0'))
+ syserr("gatherq: %s/%s is not a regular file",
+ qid_printqueue(qgrp, qdir), d->d_name);
wn--;
continue;
}
/* avoid work if possible */
- if (QueueSortOrder == QSO_BYFILENAME &&
+ if ((QueueSortOrder == QSO_BYFILENAME ||
+ QueueSortOrder == QSO_BYMODTIME ||
+ QueueSortOrder == QSO_RANDOM) &&
+#if _FFR_QUARANTINE
+ QueueLimitQuarantine == NULL &&
+#endif /* _FFR_QUARANTINE */
QueueLimitSender == NULL &&
QueueLimitRecipient == NULL)
{
+ w->w_qgrp = qgrp;
+ w->w_qdir = qdir;
w->w_name = newstr(d->d_name);
w->w_host = NULL;
- w->w_lock = w->w_tooyoung = FALSE;
+ w->w_lock = w->w_tooyoung = false;
w->w_pri = 0;
w->w_ctime = 0;
+ w->w_mtime = sbuf.st_mtime;
+ ++num_ent;
continue;
}
/* open control file */
- cf = fopen(qf, "r");
-
- if (cf == NULL)
+ cf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY,
+ NULL);
+ if (cf == NULL && OpMode != MD_PRINT)
{
/* this may be some random person sending hir msgs */
- /* syserr("orderq: cannot open %s", cbuf); */
if (tTd(41, 2))
- dprintf("orderq: cannot open %s: %s\n",
- d->d_name, errstring(errno));
+ sm_dprintf("gatherq: cannot open %s: %s\n",
+ d->d_name, sm_errstring(errno));
errno = 0;
wn--;
continue;
}
+ w->w_qgrp = qgrp;
+ w->w_qdir = qdir;
w->w_name = newstr(d->d_name);
w->w_host = NULL;
- w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB);
- w->w_tooyoung = FALSE;
+ if (cf != NULL)
+ {
+ w->w_lock = !lockfile(sm_io_getinfo(cf, SM_IO_WHAT_FD,
+ NULL),
+ w->w_name, NULL,
+ LOCK_SH|LOCK_NB);
+ }
+ w->w_tooyoung = false;
/* make sure jobs in creation don't clog queue */
w->w_pri = 0x7fffffff;
w->w_ctime = 0;
+ w->w_mtime = sbuf.st_mtime;
/* extract useful information */
- i = NEED_P | NEED_T;
- if (QueueSortOrder == QSO_BYHOST)
+ i = NEED_P|NEED_T;
+ if (QueueSortOrder == QSO_BYHOST
+#if _FFR_RHS
+ || QueueSortOrder == QSO_BYSHUFFLE
+#endif /* _FFR_RHS */
+ )
{
/* need w_host set for host sort order */
i |= NEED_H;
@@ -1247,7 +2500,13 @@ orderq(queuedir, doall)
i |= NEED_S;
if (QueueLimitRecipient != NULL)
i |= NEED_R;
- while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL)
+#if _FFR_QUARANTINE
+ if (QueueLimitQuarantine != NULL)
+ i |= NEED_QUARANTINE;
+#endif /* _FFR_QUARANTINE */
+ while (cf != NULL && i != 0 &&
+ sm_io_fgets(cf, SM_TIME_DEFAULT, lbuf,
+ sizeof lbuf) != NULL)
{
int c;
time_t age;
@@ -1258,7 +2517,8 @@ orderq(queuedir, doall)
else
{
/* flush rest of overly long line */
- while ((c = getc(cf)) != EOF && c != '\n')
+ while ((c = sm_io_getc(cf, SM_TIME_DEFAULT))
+ != SM_IO_EOF && c != '\n')
continue;
}
@@ -1278,11 +2538,51 @@ orderq(queuedir, doall)
i &= ~NEED_T;
break;
+#if _FFR_QUARANTINE
+ case 'q':
+ if (QueueMode != QM_QUARANTINE &&
+ QueueMode != QM_LOST)
+ {
+ if (tTd(41, 49))
+ sm_dprintf("%s not marked as quarantined but has a 'q' line\n",
+ w->w_name);
+ i |= HAS_QUARANTINE;
+ }
+ else if (QueueMode == QM_QUARANTINE)
+ {
+ if (QueueLimitQuarantine == NULL)
+ {
+ i &= ~NEED_QUARANTINE;
+ break;
+ }
+ p = &lbuf[1];
+ check = QueueLimitQuarantine;
+ while (check != NULL)
+ {
+ if (strcontainedin(false,
+ check->queue_match,
+ p) !=
+ check->queue_negate)
+ break;
+ else
+ check = check->queue_next;
+ }
+ if (check != NULL)
+ i &= ~NEED_QUARANTINE;
+ }
+ break;
+#endif /* _FFR_QUARANTINE */
+
case 'R':
if (w->w_host == NULL &&
(p = strrchr(&lbuf[1], '@')) != NULL)
{
- w->w_host = strrev(&p[1]);
+#if _FFR_RHS
+ if (QueueSortOrder == QSO_BYSHUFFLE)
+ w->w_host = newstr(&p[1]);
+ else
+#endif /* _FFR_RHS */
+ w->w_host = strrev(&p[1]);
makelower(w->w_host);
i &= ~NEED_H;
}
@@ -1302,8 +2602,10 @@ orderq(queuedir, doall)
check = QueueLimitRecipient;
while (check != NULL)
{
- if (strcontainedin(check->queue_match,
- p))
+ if (strcontainedin(true,
+ check->queue_match,
+ p) !=
+ check->queue_negate)
break;
else
check = check->queue_next;
@@ -1316,8 +2618,10 @@ orderq(queuedir, doall)
check = QueueLimitSender;
while (check != NULL)
{
- if (strcontainedin(check->queue_match,
- &lbuf[1]))
+ if (strcontainedin(true,
+ check->queue_match,
+ &lbuf[1]) !=
+ check->queue_negate)
break;
else
check = check->queue_next;
@@ -1330,15 +2634,15 @@ orderq(queuedir, doall)
age = curtime() - (time_t) atol(&lbuf[1]);
if (age >= 0 && MinQueueAge > 0 &&
age < MinQueueAge)
- w->w_tooyoung = TRUE;
+ w->w_tooyoung = true;
break;
case 'N':
if (atol(&lbuf[1]) == 0)
- w->w_tooyoung = FALSE;
+ w->w_tooyoung = false;
break;
-# if _FFR_QUEUEDELAY
+#if _FFR_QUEUEDELAY
/*
case 'G':
queuealg = atoi(lbuf[1]);
@@ -1347,32 +2651,104 @@ orderq(queuedir, doall)
queuedelay = (time_t) atol(&lbuf[1]);
break;
*/
-# endif /* _FFR_QUEUEDELAY */
+#endif /* _FFR_QUEUEDELAY */
}
}
- (void) fclose(cf);
+ if (cf != NULL)
+ (void) sm_io_close(cf, SM_TIME_DEFAULT);
if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) ||
+#if _FFR_QUARANTINE
+ bitset(HAS_QUARANTINE, i) ||
+ bitset(NEED_QUARANTINE, i) ||
+#endif /* _FFR_QUARANTINE */
bitset(NEED_R|NEED_S, i))
{
/* don't even bother sorting this job in */
if (tTd(41, 49))
- dprintf("skipping %s (%x)\n", w->w_name, i);
- sm_free(w->w_name);
- if (w->w_host)
- sm_free(w->w_host);
+ sm_dprintf("skipping %s (%x)\n", w->w_name, i);
+ sm_free(w->w_name); /* XXX */
+ if (w->w_host != NULL)
+ sm_free(w->w_host); /* XXX */
wn--;
}
+ else
+ ++num_ent;
}
(void) closedir(f);
wn++;
- WorkQ = NULL;
- if (WorkList == NULL)
+ i = wn - WorkListCount;
+ WorkListCount += SM_MIN(num_ent, WorkListSize);
+
+ if (more != NULL)
+ *more = WorkListCount < wn;
+
+ if (full != NULL)
+ *full = (wn >= MaxQueueRun && MaxQueueRun > 0) ||
+ (WorkList == NULL && wn > 0);
+
+ return i;
+}
+/*
+** SORTQ -- sort the work list
+**
+** First the old WorkQ is cleared away. Then the WorkList is sorted
+** for all items so that important (higher sorting value) items are not
+** trunctated off. Then the most important items are moved from
+** WorkList to WorkQ. The lower count of 'max' or MaxListCount items
+** are moved.
+**
+** Parameters:
+** max -- maximum number of items to be placed in WorkQ
+**
+** Returns:
+** the number of items in WorkQ
+**
+** Side Effects:
+** WorkQ gets released and filled with new work. WorkList
+** gets released. Work items get sorted in order.
+*/
+
+static int
+sortq(max)
+ int max;
+{
+ register int i; /* local counter */
+ register WORK *w; /* tmp item pointer */
+ int wc = WorkListCount; /* trim size for WorkQ */
+
+ if (WorkQ != NULL)
+ {
+ /* Clear out old WorkQ. */
+ for (w = WorkQ; w != NULL; )
+ {
+ register WORK *nw = w->w_next;
+
+ WorkQ = nw;
+ sm_free(w->w_name); /* XXX */
+ if (w->w_host != NULL)
+ sm_free(w->w_host); /* XXX */
+ sm_free((char *) w); /* XXX */
+ w = nw;
+ }
+ sm_free((char *) WorkQ);
+ WorkQ = NULL;
+ }
+
+ if (WorkList == NULL || wc <= 0)
return 0;
- wc = min(wn, WorkListSize);
- if (wc > MaxQueueRun && MaxQueueRun > 0)
- wc = MaxQueueRun;
+
+ /* Check if the per queue group item limit will be exceeded */
+ if (wc > max && max > 0)
+ wc = max;
+
+ /*
+ ** The sort now takes place using all of the items in WorkList.
+ ** The list gets trimmed to the most important items after the sort.
+ ** If the trim were to happen before the sort then one or more
+ ** important items might get truncated off -- not what we want.
+ */
if (QueueSortOrder == QSO_BYHOST)
{
@@ -1401,11 +2777,12 @@ orderq(queuedir, doall)
{
if (WorkList[i].w_host == NULL &&
w->w_host == NULL)
- WorkList[i].w_lock = TRUE;
+ WorkList[i].w_lock = true;
else if (WorkList[i].w_host != NULL &&
w->w_host != NULL &&
- sm_strcasecmp(WorkList[i].w_host, w->w_host) == 0)
- WorkList[i].w_lock = TRUE;
+ sm_strcasecmp(WorkList[i].w_host,
+ w->w_host) == 0)
+ WorkList[i].w_lock = true;
else
break;
}
@@ -1429,11 +2806,42 @@ orderq(queuedir, doall)
else if (QueueSortOrder == QSO_BYFILENAME)
{
/*
- ** Sort based on qf filename.
+ ** Sort based on queue filename.
*/
qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf4);
}
+ else if (QueueSortOrder == QSO_RANDOM)
+ {
+ /*
+ ** Sort randomly.
+ ** workcmpf5() returns a random 1 or -1.
+ ** As long as nobody does a verification pass over the
+ ** sorted list, we should be golden.
+ */
+
+ qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf5);
+ }
+ else if (QueueSortOrder == QSO_BYMODTIME)
+ {
+ /*
+ ** Simple sort based on modification time of queue file.
+ ** This puts the oldest items first.
+ */
+
+ qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf6);
+ }
+#if _FFR_RHS
+ else if (QueueSortOrder == QSO_BYSHUFFLE)
+ {
+ /*
+ ** Simple sort based on shuffled host name.
+ */
+
+ init_shuffle_alphabet();
+ qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf7);
+ }
+#endif /* _FFR_RHS */
else
{
/*
@@ -1446,45 +2854,52 @@ orderq(queuedir, doall)
/*
** Convert the work list into canonical form.
** Should be turning it into a list of envelopes here perhaps.
+ ** Only take the most important items up to the per queue group
+ ** maximum.
*/
for (i = wc; --i >= 0; )
{
w = (WORK *) xalloc(sizeof *w);
+ w->w_qgrp = WorkList[i].w_qgrp;
+ w->w_qdir = WorkList[i].w_qdir;
w->w_name = WorkList[i].w_name;
w->w_host = WorkList[i].w_host;
w->w_lock = WorkList[i].w_lock;
w->w_tooyoung = WorkList[i].w_tooyoung;
w->w_pri = WorkList[i].w_pri;
w->w_ctime = WorkList[i].w_ctime;
+ w->w_mtime = WorkList[i].w_mtime;
w->w_next = WorkQ;
WorkQ = w;
}
if (WorkList != NULL)
- sm_free(WorkList);
+ sm_free(WorkList); /* XXX */
WorkList = NULL;
WorkListSize = 0;
+ WorkListCount = 0;
if (tTd(40, 1))
{
for (w = WorkQ; w != NULL; w = w->w_next)
{
if (w->w_host != NULL)
- dprintf("%22s: pri=%ld %s\n",
+ sm_dprintf("%22s: pri=%ld %s\n",
w->w_name, w->w_pri, w->w_host);
else
- dprintf("%32s: pri=%ld\n",
+ sm_dprintf("%32s: pri=%ld\n",
w->w_name, w->w_pri);
}
}
- return wn;
+ return wc; /* return number of WorkQ items */
}
- /*
+/*
** GROW_WLIST -- make the work list larger
**
** Parameters:
-** queuedir -- the index for the queue directory.
+** qgrp -- the index for the queue group.
+** qdir -- the index for the queue directory.
**
** Returns:
** none.
@@ -1496,11 +2911,12 @@ orderq(queuedir, doall)
*/
static void
-grow_wlist(queuedir)
- int queuedir;
+grow_wlist(qgrp, qdir)
+ int qgrp;
+ int qdir;
{
if (tTd(41, 1))
- dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize);
+ sm_dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize);
if (WorkList == NULL)
{
WorkList = (WORK *) xalloc((sizeof *WorkList) *
@@ -1510,8 +2926,8 @@ grow_wlist(queuedir)
else
{
int newsize = WorkListSize + QUEUESEGSIZE;
- WORK *newlist = (WORK *) xrealloc((char *)WorkList,
- (unsigned)sizeof(WORK) * (newsize + 1));
+ WORK *newlist = (WORK *) sm_realloc((char *) WorkList,
+ (unsigned) sizeof(WORK) * (newsize + 1));
if (newlist != NULL)
{
@@ -1521,7 +2937,7 @@ grow_wlist(queuedir)
{
sm_syslog(LOG_INFO, NOQID,
"grew WorkList for %s to %d",
- qid_printqueue(queuedir),
+ qid_printqueue(qgrp, qdir),
WorkListSize);
}
}
@@ -1529,13 +2945,13 @@ grow_wlist(queuedir)
{
sm_syslog(LOG_ALERT, NOQID,
"FAILED to grow WorkList for %s to %d",
- qid_printqueue(queuedir), newsize);
+ qid_printqueue(qgrp, qdir), newsize);
}
}
if (tTd(41, 1))
- dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize);
+ sm_dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize);
}
- /*
+/*
** WORKCMPF0 -- simple priority-only compare function.
**
** Parameters:
@@ -1547,8 +2963,6 @@ grow_wlist(queuedir)
** 0 if a == b
** +1 if a > b
**
-** Side Effects:
-** none.
*/
static int
@@ -1566,7 +2980,7 @@ workcmpf0(a, b)
else
return -1;
}
- /*
+/*
** WORKCMPF1 -- first compare function for ordering work based on host name.
**
** Sorts on host name, lock status, and priority in that order.
@@ -1580,8 +2994,6 @@ workcmpf0(a, b)
** 0 if a == b
** >0 if a > b
**
-** Side Effects:
-** none.
*/
static int
@@ -1607,7 +3019,7 @@ workcmpf1(a, b)
/* job priority */
return workcmpf0(a, b);
}
- /*
+/*
** WORKCMPF2 -- second compare function for ordering work based on host name.
**
** Sorts on lock status, host name, and priority in that order.
@@ -1621,8 +3033,6 @@ workcmpf1(a, b)
** 0 if a == b
** >0 if a > b
**
-** Side Effects:
-** none.
*/
static int
@@ -1648,7 +3058,7 @@ workcmpf2(a, b)
/* job priority */
return workcmpf0(a, b);
}
- /*
+/*
** WORKCMPF3 -- simple submission-time-only compare function.
**
** Parameters:
@@ -1660,8 +3070,6 @@ workcmpf2(a, b)
** 0 if a == b
** +1 if a > b
**
-** Side Effects:
-** none.
*/
static int
@@ -1676,7 +3084,7 @@ workcmpf3(a, b)
else
return 0;
}
- /*
+/*
** WORKCMPF4 -- compare based on file name
**
** Parameters:
@@ -1688,8 +3096,6 @@ workcmpf3(a, b)
** 0 if a == b
** +1 if a > b
**
-** Side Effects:
-** none.
*/
static int
@@ -1699,7 +3105,93 @@ workcmpf4(a, b)
{
return strcmp(a->w_name, b->w_name);
}
- /*
+/*
+** WORKCMPF5 -- compare based on assigned random number
+**
+** Parameters:
+** a -- the first argument (ignored).
+** b -- the second argument (ignored).
+**
+** Returns:
+** randomly 1/-1
+*/
+
+/* ARGSUSED0 */
+static int
+workcmpf5(a, b)
+ register WORK *a;
+ register WORK *b;
+{
+ return (get_rand_mod(2)) ? 1 : -1;
+}
+/*
+** WORKCMPF6 -- simple modification-time-only compare function.
+**
+** Parameters:
+** a -- the first argument.
+** b -- the second argument.
+**
+** Returns:
+** -1 if a < b
+** 0 if a == b
+** +1 if a > b
+**
+*/
+
+static int
+workcmpf6(a, b)
+ register WORK *a;
+ register WORK *b;
+{
+ if (a->w_mtime > b->w_mtime)
+ return 1;
+ else if (a->w_mtime < b->w_mtime)
+ return -1;
+ else
+ return 0;
+}
+#if _FFR_RHS
+/*
+** WORKCMPF7 -- compare function for ordering work based on shuffled host name.
+**
+** Sorts on lock status, host name, and priority in that order.
+**
+** Parameters:
+** a -- the first argument.
+** b -- the second argument.
+**
+** Returns:
+** <0 if a < b
+** 0 if a == b
+** >0 if a > b
+**
+*/
+
+static int
+workcmpf7(a, b)
+ register WORK *a;
+ register WORK *b;
+{
+ int i;
+
+ /* lock status */
+ if (a->w_lock != b->w_lock)
+ return a->w_lock - b->w_lock;
+
+ /* host name */
+ if (a->w_host != NULL && b->w_host == NULL)
+ return 1;
+ else if (a->w_host == NULL && b->w_host != NULL)
+ return -1;
+ if (a->w_host != NULL && b->w_host != NULL &&
+ (i = sm_strshufflecmp(a->w_host, b->w_host)) != 0)
+ return i;
+
+ /* job priority */
+ return workcmpf0(a, b);
+}
+#endif /* _FFR_RHS */
+/*
** STRREV -- reverse string
**
** Returns a pointer to a new string that is the reverse of
@@ -1727,11 +3219,71 @@ strrev(fwd)
rev[len] = '\0';
return rev;
}
- /*
+
+#if _FFR_RHS
+
+#define NASCII 128
+#define NCHAR 256
+
+static unsigned char ShuffledAlphabet[NCHAR];
+
+void
+init_shuffle_alphabet()
+{
+ static bool init = false;
+ int i;
+
+ if (init)
+ return;
+
+ /* fill the ShuffledAlphabet */
+ for (i = 0; i < NCHAR; i++)
+ ShuffledAlphabet[i] = i;
+
+ /* mix it */
+ for (i = 1; i < NCHAR; i++)
+ {
+ register int j = get_random() % NCHAR;
+ register int tmp;
+
+ tmp = ShuffledAlphabet[j];
+ ShuffledAlphabet[j] = ShuffledAlphabet[i];
+ ShuffledAlphabet[i] = tmp;
+ }
+
+ /* make it case insensitive */
+ for (i = 'A'; i <= 'Z'; i++)
+ ShuffledAlphabet[i] = ShuffledAlphabet[i + 'a' - 'A'];
+
+ /* fill the upper part */
+ for (i = 0; i < NCHAR; i++)
+ ShuffledAlphabet[i + NCHAR] = ShuffledAlphabet[i];
+ init = true;
+}
+
+static int
+sm_strshufflecmp(a, b)
+ char *a;
+ char *b;
+{
+ const unsigned char *us1 = (const unsigned char *) a;
+ const unsigned char *us2 = (const unsigned char *) b;
+
+ while (ShuffledAlphabet[*us1] == ShuffledAlphabet[*us2++])
+ {
+ if (*us1++ == '\0')
+ return 0;
+ }
+ return (ShuffledAlphabet[*us1] - ShuffledAlphabet[*--us2]);
+}
+#endif /* _FFR_RHS */
+
+/*
** DOWORK -- do a work request.
**
** Parameters:
-** queuedir -- the index of the queue directory for the job.
+** qgrp -- the index of the queue group for the job.
+** qdir -- the index of the queue directory for the job.
** id -- the ID of the job to run.
** forkflag -- if set, run this in background.
** requeueflag -- if set, reinstantiate the queue quickly.
@@ -1748,17 +3300,19 @@ strrev(fwd)
*/
pid_t
-dowork(queuedir, id, forkflag, requeueflag, e)
- int queuedir;
+dowork(qgrp, qdir, id, forkflag, requeueflag, e)
+ int qgrp;
+ int qdir;
char *id;
bool forkflag;
bool requeueflag;
register ENVELOPE *e;
{
register pid_t pid;
+ SM_RPOOL_T *rpool;
if (tTd(40, 1))
- dprintf("dowork(%s/%s)\n", qid_printqueue(queuedir), id);
+ sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id);
/*
** Fork for work.
@@ -1774,7 +3328,7 @@ dowork(queuedir, id, forkflag, requeueflag, e)
** child will dynamically open them if necessary.
*/
- closemaps();
+ closemaps(false);
pid = fork();
if (pid < 0)
@@ -1785,12 +3339,38 @@ dowork(queuedir, id, forkflag, requeueflag, e)
else if (pid > 0)
{
/* parent -- clean out connection cache */
- mci_flush(FALSE, NULL);
+ mci_flush(false, NULL);
}
else
{
+ /*
+ ** Initialize exception stack and default exception
+ ** handler for child process.
+ */
+
+ /* Reset global flags */
+ RestartRequest = NULL;
+ RestartWorkGroup = false;
+ ShutdownRequest = NULL;
+ PendingSignal = 0;
+ CurrentPid = getpid();
+ sm_exc_newthread(fatal_error);
+
+ /*
+ ** See note above about SMTP processes and SIGCHLD.
+ */
+
+ if (OpMode == MD_SMTP ||
+ OpMode == MD_DAEMON ||
+ MaxQueueChildren > 0)
+ {
+ proc_list_clear();
+ sm_releasesignal(SIGCHLD);
+ (void) sm_signal(SIGCHLD, SIG_DFL);
+ }
+
/* child -- error messages to the transcript */
- QuickAbort = OnlyOneError = FALSE;
+ QuickAbort = OnlyOneError = false;
}
}
else
@@ -1808,94 +3388,306 @@ dowork(queuedir, id, forkflag, requeueflag, e)
** can recover on interrupt.
*/
- /* Reset global flags */
- RestartRequest = NULL;
- ShutdownRequest = NULL;
- PendingSignal = 0;
+ if (forkflag)
+ {
+ /* Reset global flags */
+ RestartRequest = NULL;
+ RestartWorkGroup = false;
+ ShutdownRequest = NULL;
+ PendingSignal = 0;
+ }
/* set basic modes, etc. */
- (void) alarm(0);
+ sm_clear_events();
clearstats();
- clearenvelope(e, FALSE);
+ rpool = sm_rpool_new_x(NULL);
+ clearenvelope(e, false, rpool);
e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
set_delivery_mode(SM_DELIVER, e);
e->e_errormode = EM_MAIL;
e->e_id = id;
- e->e_queuedir = queuedir;
- GrabTo = UseErrorsTo = FALSE;
+ e->e_qgrp = qgrp;
+ e->e_qdir = qdir;
+ GrabTo = UseErrorsTo = false;
ExitStat = EX_OK;
if (forkflag)
{
disconnect(1, e);
- OpMode = MD_QUEUERUN;
+ set_op_mode(MD_QUEUERUN);
}
- sm_setproctitle(TRUE, e, "%s: from queue", qid_printname(e));
+ sm_setproctitle(true, e, "%s from queue", qid_printname(e));
if (LogLevel > 76)
- sm_syslog(LOG_DEBUG, e->e_id,
- "dowork, pid=%d",
- (int) getpid());
+ sm_syslog(LOG_DEBUG, e->e_id, "dowork, pid=%d",
+ (int) CurrentPid);
/* don't use the headers from sendmail.cf... */
e->e_header = NULL;
/* read the queue control file -- return if locked */
- if (!readqf(e))
+ if (!readqf(e, false))
{
if (tTd(40, 4) && e->e_id != NULL)
- dprintf("readqf(%s) failed\n",
+ sm_dprintf("readqf(%s) failed\n",
qid_printname(e));
e->e_id = NULL;
if (forkflag)
- finis(FALSE, EX_OK);
+ finis(false, true, EX_OK);
else
+ {
+ /* adding this frees 8 bytes */
+ clearenvelope(e, false, rpool);
+
+ /* adding this frees 12 bytes */
+ sm_rpool_free(rpool);
+ e->e_rpool = NULL;
return 0;
+ }
}
e->e_flags |= EF_INQUEUE;
- eatheader(e, requeueflag);
+ eatheader(e, requeueflag, true);
if (requeueflag)
- queueup(e, FALSE);
+ queueup(e, false, false);
/* do the delivery */
sendall(e, SM_DELIVER);
/* finish up and exit */
if (forkflag)
- finis(TRUE, ExitStat);
+ finis(true, true, ExitStat);
else
- dropenvelope(e, TRUE);
+ {
+ dropenvelope(e, true, false);
+ sm_rpool_free(rpool);
+ e->e_rpool = NULL;
+ }
}
e->e_id = NULL;
return pid;
}
- /*
+
+/*
+** DOWORKLIST -- process a list of envelopes as work requests
+**
+** Similar to dowork(), except that after forking, it processes an
+** envelope and its siblings, treating each envelope as a work request.
+**
+** Parameters:
+** el -- envelope to be processed including its siblings.
+** forkflag -- if set, run this in background.
+** requeueflag -- if set, reinstantiate the queue quickly.
+** This is used when expanding aliases in the queue.
+** If forkflag is also set, it doesn't wait for the
+** child.
+**
+** Returns:
+** process id of process that is running the queue job.
+**
+** Side Effects:
+** The work request is satisfied if possible.
+*/
+
+pid_t
+doworklist(el, forkflag, requeueflag)
+ ENVELOPE *el;
+ bool forkflag;
+ bool requeueflag;
+{
+ register pid_t pid;
+ ENVELOPE *ei;
+
+ if (tTd(40, 1))
+ sm_dprintf("doworklist()\n");
+
+ /*
+ ** Fork for work.
+ */
+
+ if (forkflag)
+ {
+ /*
+ ** Since the delivery may happen in a child and the
+ ** parent does not wait, the parent may close the
+ ** maps thereby removing any shared memory used by
+ ** the map. Therefore, close the maps now so the
+ ** child will dynamically open them if necessary.
+ */
+
+ closemaps(false);
+
+ pid = fork();
+ if (pid < 0)
+ {
+ syserr("doworklist: cannot fork");
+ return 0;
+ }
+ else if (pid > 0)
+ {
+ /* parent -- clean out connection cache */
+ mci_flush(false, NULL);
+ }
+ else
+ {
+ /*
+ ** Initialize exception stack and default exception
+ ** handler for child process.
+ */
+
+ /* Reset global flags */
+ RestartRequest = NULL;
+ RestartWorkGroup = false;
+ ShutdownRequest = NULL;
+ PendingSignal = 0;
+ CurrentPid = getpid();
+ sm_exc_newthread(fatal_error);
+
+ /*
+ ** See note above about SMTP processes and SIGCHLD.
+ */
+
+ if (OpMode == MD_SMTP ||
+ OpMode == MD_DAEMON ||
+ MaxQueueChildren > 0)
+ {
+ proc_list_clear();
+ sm_releasesignal(SIGCHLD);
+ (void) sm_signal(SIGCHLD, SIG_DFL);
+ }
+
+ /* child -- error messages to the transcript */
+ QuickAbort = OnlyOneError = false;
+ }
+ }
+ else
+ {
+ pid = 0;
+ }
+
+ if (pid != 0)
+ return pid;
+
+ /*
+ ** IN CHILD
+ ** Lock the control file to avoid duplicate deliveries.
+ ** Then run the file as though we had just read it.
+ ** We save an idea of the temporary name so we
+ ** can recover on interrupt.
+ */
+
+ if (forkflag)
+ {
+ /* Reset global flags */
+ RestartRequest = NULL;
+ RestartWorkGroup = false;
+ ShutdownRequest = NULL;
+ PendingSignal = 0;
+ }
+
+ /* set basic modes, etc. */
+ sm_clear_events();
+ clearstats();
+ GrabTo = UseErrorsTo = false;
+ ExitStat = EX_OK;
+ if (forkflag)
+ {
+ disconnect(1, el);
+ set_op_mode(MD_QUEUERUN);
+ }
+ if (LogLevel > 76)
+ sm_syslog(LOG_DEBUG, el->e_id, "doworklist, pid=%d",
+ (int) CurrentPid);
+
+ for (ei = el; ei != NULL; ei = ei->e_sibling)
+ {
+ ENVELOPE e;
+ SM_RPOOL_T *rpool;
+
+ if (WILL_BE_QUEUED(ei->e_sendmode))
+ continue;
+#if _FFR_QUARANTINE
+ else if (QueueMode != QM_QUARANTINE &&
+ ei->e_quarmsg != NULL)
+ continue;
+#endif /* _FFR_QUARANTINE */
+
+ rpool = sm_rpool_new_x(NULL);
+ clearenvelope(&e, true, rpool);
+ e.e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
+ set_delivery_mode(SM_DELIVER, &e);
+ e.e_errormode = EM_MAIL;
+ e.e_id = ei->e_id;
+ e.e_qgrp = ei->e_qgrp;
+ e.e_qdir = ei->e_qdir;
+ openxscript(&e);
+ sm_setproctitle(true, &e, "%s from queue", qid_printname(&e));
+
+ /* don't use the headers from sendmail.cf... */
+ e.e_header = NULL;
+ CurEnv = &e;
+
+ /* read the queue control file -- return if locked */
+ if (readqf(&e, false))
+ {
+ e.e_flags |= EF_INQUEUE;
+ eatheader(&e, requeueflag, true);
+
+ if (requeueflag)
+ queueup(&e, false, false);
+
+ /* do the delivery */
+ sendall(&e, SM_DELIVER);
+ dropenvelope(&e, true, false);
+ }
+ else
+ {
+ if (tTd(40, 4) && e.e_id != NULL)
+ sm_dprintf("readqf(%s) failed\n",
+ qid_printname(&e));
+ }
+ sm_rpool_free(rpool);
+ ei->e_id = NULL;
+ }
+
+ /* restore CurEnv */
+ CurEnv = el;
+
+ /* finish up and exit */
+ if (forkflag)
+ finis(true, true, ExitStat);
+ return 0;
+}
+/*
** READQF -- read queue file and set up environment.
**
** Parameters:
** e -- the envelope of the job to run.
+** openonly -- only open the qf (returned as e_lockfp)
**
** Returns:
-** TRUE if it successfully read the queue file.
-** FALSE otherwise.
+** true if it successfully read the queue file.
+** false otherwise.
**
** Side Effects:
** The queue file is returned locked.
*/
static bool
-readqf(e)
+readqf(e, openonly)
register ENVELOPE *e;
+ bool openonly;
{
- register FILE *qfp;
+ register SM_FILE_T *qfp;
ADDRESS *ctladdr;
struct stat st, stf;
char *bp;
int qfver = 0;
long hdrsize = 0;
register char *p;
+ char *frcpt = NULL;
char *orcpt = NULL;
- bool nomore = FALSE;
+ bool nomore = false;
+ bool bogus = false;
MODE_T qsafe;
char qf[MAXPATHLEN];
char buf[MAXLINE];
@@ -1904,33 +3696,37 @@ readqf(e)
** Read and process the file.
*/
- (void) strlcpy(qf, queuename(e, 'q'), sizeof qf);
- qfp = fopen(qf, "r+");
+ (void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), sizeof qf);
+ qfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDWR, NULL);
if (qfp == NULL)
{
int save_errno = errno;
if (tTd(40, 8))
- dprintf("readqf(%s): fopen failure (%s)\n",
- qf, errstring(errno));
+ sm_dprintf("readqf(%s): sm_io_open failure (%s)\n",
+ qf, sm_errstring(errno));
errno = save_errno;
if (errno != ENOENT
)
syserr("readqf: no control file %s", qf);
- return FALSE;
+ RELEASE_QUEUE;
+ return false;
}
- if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB))
+ if (!lockfile(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), qf, NULL,
+ LOCK_EX|LOCK_NB))
{
/* being processed by another queuer */
if (Verbose)
- printf("%s: locked\n", e->e_id);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "%s: locked\n", e->e_id);
if (tTd(40, 8))
- dprintf("%s: locked\n", e->e_id);
+ sm_dprintf("%s: locked\n", e->e_id);
if (LogLevel > 19)
sm_syslog(LOG_DEBUG, e->e_id, "locked");
- (void) fclose(qfp);
- return FALSE;
+ (void) sm_io_close(qfp, SM_TIME_DEFAULT);
+ RELEASE_QUEUE;
+ return false;
}
/*
@@ -1952,35 +3748,38 @@ readqf(e)
*/
if (stat(qf, &stf) < 0 ||
- fstat(fileno(qfp), &st) < 0)
+ fstat(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), &st) < 0)
{
/* must have been being processed by someone else */
if (tTd(40, 8))
- dprintf("readqf(%s): [f]stat failure (%s)\n",
- qf, errstring(errno));
- (void) fclose(qfp);
- return FALSE;
+ sm_dprintf("readqf(%s): [f]stat failure (%s)\n",
+ qf, sm_errstring(errno));
+ (void) sm_io_close(qfp, SM_TIME_DEFAULT);
+ RELEASE_QUEUE;
+ return false;
}
if (st.st_nlink != stf.st_nlink ||
st.st_dev != stf.st_dev ||
- st.st_ino != stf.st_ino ||
-# if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */
+ ST_INODE(st) != ST_INODE(stf) ||
+#if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */
st.st_gen != stf.st_gen ||
-# endif /* HAS_ST_GEN && 0 */
+#endif /* HAS_ST_GEN && 0 */
st.st_uid != stf.st_uid ||
st.st_gid != stf.st_gid ||
st.st_size != stf.st_size)
{
/* changed after opened */
if (Verbose)
- printf("%s: changed\n", e->e_id);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "%s: changed\n", e->e_id);
if (tTd(40, 8))
- dprintf("%s: changed\n", e->e_id);
+ sm_dprintf("%s: changed\n", e->e_id);
if (LogLevel > 19)
sm_syslog(LOG_DEBUG, e->e_id, "changed");
- (void) fclose(qfp);
- return FALSE;
+ (void) sm_io_close(qfp, SM_TIME_DEFAULT);
+ RELEASE_QUEUE;
+ return false;
}
/*
@@ -1988,39 +3787,78 @@ readqf(e)
*/
qsafe = S_IWOTH|S_IWGRP;
-#if _FFR_QUEUE_FILE_MODE
if (bitset(S_IWGRP, QueueFileMode))
qsafe &= ~S_IWGRP;
-#endif /* _FFR_QUEUE_FILE_MODE */
- if ((st.st_uid != geteuid() &&
- st.st_uid != TrustedUid &&
- geteuid() != RealUid) ||
- bitset(qsafe, st.st_mode))
+ bogus = st.st_uid != geteuid() &&
+ st.st_uid != TrustedUid &&
+ geteuid() != RealUid;
+
+ /*
+ ** If this qf file results from a set-group-ID binary, then
+ ** we check whether the directory is group-writable,
+ ** the queue file mode contains the group-writable bit, and
+ ** the groups are the same.
+ ** Notice: this requires that the set-group-ID binary is used to
+ ** run the queue!
+ */
+
+ if (bogus && st.st_gid == getegid() && UseMSP)
+ {
+ char delim;
+ struct stat dst;
+
+ bp = SM_LAST_DIR_DELIM(qf);
+ if (bp == NULL)
+ delim = '\0';
+ else
+ {
+ delim = *bp;
+ *bp = '\0';
+ }
+ if (stat(delim == '\0' ? "." : qf, &dst) < 0)
+ syserr("readqf: cannot stat directory %s",
+ delim == '\0' ? "." : qf);
+ else
+ {
+ bogus = !(bitset(S_IWGRP, QueueFileMode) &&
+ bitset(S_IWGRP, dst.st_mode) &&
+ dst.st_gid == st.st_gid);
+ }
+ if (delim != '\0')
+ *bp = delim;
+ }
+ if (!bogus)
+ bogus = bitset(qsafe, st.st_mode);
+ if (bogus)
{
if (LogLevel > 0)
{
sm_syslog(LOG_ALERT, e->e_id,
- "bogus queue file, uid=%d, mode=%o",
- st.st_uid, st.st_mode);
+ "bogus queue file, uid=%d, gid=%d, mode=%o",
+ st.st_uid, st.st_gid, st.st_mode);
}
if (tTd(40, 8))
- dprintf("readqf(%s): bogus file\n", qf);
- loseqfile(e, "bogus file uid in mqueue");
- (void) fclose(qfp);
- return FALSE;
+ sm_dprintf("readqf(%s): bogus file\n", qf);
+ e->e_flags |= EF_INQUEUE;
+ if (!openonly)
+ loseqfile(e, "bogus file uid/gid in mqueue");
+ (void) sm_io_close(qfp, SM_TIME_DEFAULT);
+ RELEASE_QUEUE;
+ return false;
}
if (st.st_size == 0)
{
/* must be a bogus file -- if also old, just remove it */
- if (st.st_ctime + 10 * 60 < curtime())
+ if (!openonly && st.st_ctime + 10 * 60 < curtime())
{
- (void) xunlink(queuename(e, 'd'));
- (void) xunlink(queuename(e, 'q'));
+ (void) xunlink(queuename(e, DATAFL_LETTER));
+ (void) xunlink(queuename(e, ANYQFL_LETTER));
}
- (void) fclose(qfp);
- return FALSE;
+ (void) sm_io_close(qfp, SM_TIME_DEFAULT);
+ RELEASE_QUEUE;
+ return false;
}
if (st.st_nlink == 0)
@@ -2030,151 +3868,203 @@ readqf(e)
** unlinked. Just assume it is zero length.
*/
- (void) fclose(qfp);
- return FALSE;
+ (void) sm_io_close(qfp, SM_TIME_DEFAULT);
+ RELEASE_QUEUE;
+ return false;
}
+#if _FFR_TRUSTED_QF
+ /*
+ ** If we don't own the file mark it as unsafe.
+ ** However, allow TrustedUser to own it as well
+ ** in case TrustedUser manipulates the queue.
+ */
+
+ if (st.st_uid != geteuid() && st.st_uid != TrustedUid)
+ e->e_flags |= EF_UNSAFE;
+#else /* _FFR_TRUSTED_QF */
+ /* If we don't own the file mark it as unsafe */
+ if (st.st_uid != geteuid())
+ e->e_flags |= EF_UNSAFE;
+#endif /* _FFR_TRUSTED_QF */
+
/* good file -- save this lock */
e->e_lockfp = qfp;
+ /* Just wanted the open file */
+ if (openonly)
+ return true;
+
/* do basic system initialization */
initsys(e);
- define('i', e->e_id, e);
+ macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
LineNumber = 0;
e->e_flags |= EF_GLOBALERRS;
- OpMode = MD_QUEUERUN;
+ set_op_mode(MD_QUEUERUN);
ctladdr = NULL;
+#if _FFR_QUARANTINE
+ e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
+#endif /* _FFR_QUARANTINE */
+ e->e_dfqgrp = e->e_qgrp;
+ e->e_dfqdir = e->e_qdir;
+#if _FFR_QUEUE_MACRO
+ macdefine(&e->e_macro, A_TEMP, macid("{queue}"),
+ qid_printqueue(e->e_qgrp, e->e_qdir));
+#endif /* _FFR_QUEUE_MACRO */
e->e_dfino = -1;
e->e_msgsize = -1;
-# if _FFR_QUEUEDELAY
+#if _FFR_QUEUEDELAY
e->e_queuealg = QD_LINEAR;
e->e_queuedelay = (time_t) 0;
-# endif /* _FFR_QUEUEDELAY */
+#endif /* _FFR_QUEUEDELAY */
while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL)
{
- u_long qflags;
+ unsigned long qflags;
ADDRESS *q;
- int mid;
+ int r;
time_t now;
auto char *ep;
if (tTd(40, 4))
- dprintf("+++++ %s\n", bp);
+ sm_dprintf("+++++ %s\n", bp);
if (nomore)
{
/* hack attack */
- syserr("SECURITY ALERT: extra data in qf: %s", bp);
- (void) fclose(qfp);
+ hackattack:
+ syserr("SECURITY ALERT: extra or bogus data in queue file: %s",
+ bp);
+ (void) sm_io_close(qfp, SM_TIME_DEFAULT);
+
+ /* the file is already on disk */
+ e->e_flags |= EF_INQUEUE;
loseqfile(e, "bogus queue line");
- return FALSE;
+ RELEASE_QUEUE;
+ return false;
}
switch (bp[0])
{
- case 'V': /* queue file version number */
- qfver = atoi(&bp[1]);
- if (qfver <= QF_VERSION)
- break;
- syserr("Version number in qf (%d) greater than max (%d)",
- qfver, QF_VERSION);
- (void) fclose(qfp);
- loseqfile(e, "unsupported qf file version");
- return FALSE;
+ case 'A': /* AUTH= parameter */
+ if (!xtextok(&bp[1]))
+ goto hackattack;
+ e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
+ break;
+
+ case 'B': /* body type */
+ r = check_bodytype(&bp[1]);
+ if (!BODYTYPE_VALID(r))
+ goto hackattack;
+ e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
+ break;
case 'C': /* specify controlling user */
- ctladdr = setctluser(&bp[1], qfver);
+ ctladdr = setctluser(&bp[1], qfver, e);
break;
- case 'Q': /* original recipient */
- orcpt = newstr(&bp[1]);
+ case 'D': /* data file name */
+ /* obsolete -- ignore */
break;
- case 'R': /* specify recipient */
- p = bp;
- qflags = 0;
- if (qfver >= 1)
+ case 'd': /* data file directory name */
{
- /* get flag bits */
- while (*++p != '\0' && *p != ':')
+ int qgrp, qdir;
+
+#if _FFR_MSP_PARANOIA
+ /* forbid queue groups in MSP? */
+ if (UseMSP)
+ goto hackattack;
+#endif /* _FFR_MSP_PARANOIA */
+ for (qgrp = 0;
+ qgrp < NumQueue && Queue[qgrp] != NULL;
+ ++qgrp)
{
- switch (*p)
+ for (qdir = 0;
+ qdir < Queue[qgrp]->qg_numqueues;
+ ++qdir)
{
- case 'N':
- qflags |= QHASNOTIFY;
- break;
-
- case 'S':
- qflags |= QPINGONSUCCESS;
- break;
-
- case 'F':
- qflags |= QPINGONFAILURE;
- break;
-
- case 'D':
- qflags |= QPINGONDELAY;
- break;
-
- case 'P':
- qflags |= QPRIMARY;
- break;
-
- case 'A':
- if (ctladdr != NULL)
- ctladdr->q_flags |= QALIAS;
- break;
+ if (strcmp(&bp[1],
+ Queue[qgrp]->qg_qpaths[qdir].qp_name)
+ == 0)
+ {
+ e->e_dfqgrp = qgrp;
+ e->e_dfqdir = qdir;
+ goto done;
+ }
}
}
+ loseqfile(e, "bogus queue file directory");
+ RELEASE_QUEUE;
+ return false;
+ done:
+ break;
}
- else
- qflags |= QPRIMARY;
- q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e);
- if (q != NULL)
- {
- q->q_alias = ctladdr;
- if (qfver >= 1)
- q->q_flags &= ~Q_PINGFLAGS;
- q->q_flags |= qflags;
- q->q_orcpt = orcpt;
- (void) recipient(q, &e->e_sendqueue, 0, e);
- }
- orcpt = NULL;
- break;
case 'E': /* specify error recipient */
/* no longer used */
break;
- case 'H': /* header */
- (void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e);
- hdrsize += strlen(&bp[1]);
- break;
+ case 'F': /* flag bits */
+ if (strncmp(bp, "From ", 5) == 0)
+ {
+ /* we are being spoofed! */
+ syserr("SECURITY ALERT: bogus qf line %s", bp);
+ (void) sm_io_close(qfp, SM_TIME_DEFAULT);
+ loseqfile(e, "bogus queue line");
+ RELEASE_QUEUE;
+ return false;
+ }
+ for (p = &bp[1]; *p != '\0'; p++)
+ {
+ switch (*p)
+ {
+ case '8': /* has 8 bit data */
+ e->e_flags |= EF_HAS8BIT;
+ break;
- case 'L': /* Solaris Content-Length: */
- case 'M': /* message */
- /* ignore this; we want a new message next time */
- break;
+ case 'b': /* delete Bcc: header */
+ e->e_flags |= EF_DELETE_BCC;
+ break;
- case 'S': /* sender */
- setsender(newstr(&bp[1]), e, NULL, '\0', TRUE);
- break;
+ case 'd': /* envelope has DSN RET= */
+ e->e_flags |= EF_RET_PARAM;
+ break;
- case 'B': /* body type */
- e->e_bodytype = newstr(&bp[1]);
+ case 'n': /* don't return body */
+ e->e_flags |= EF_NO_BODY_RETN;
+ break;
+
+ case 'r': /* response */
+ e->e_flags |= EF_RESPONSE;
+ break;
+
+ case 's': /* split */
+ e->e_flags |= EF_SPLIT;
+ break;
+
+ case 'w': /* warning sent */
+ e->e_flags |= EF_WARNING;
+ break;
+ }
+ }
break;
-# if _FFR_SAVE_CHARSET
- case 'X': /* character set */
- e->e_charset = newstr(&bp[1]);
+#if _FFR_QUEUEDELAY
+ case 'G': /* queue delay algorithm */
+ e->e_queuealg = atoi(&buf[1]);
break;
-# endif /* _FFR_SAVE_CHARSET */
+#endif /* _FFR_QUEUEDELAY */
- case 'D': /* data file name */
- /* obsolete -- ignore */
+#if _FFR_QUARANTINE
+ case 'q': /* quarantine reason */
+ e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{quarantine}"), e->e_quarmsg);
break;
+#endif /* _FFR_QUARANTINE */
- case 'T': /* init time */
- e->e_ctime = atol(&bp[1]);
+ case 'H': /* header */
+ (void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e);
+ hdrsize += strlen(&bp[1]);
break;
case 'I': /* data file's inode number */
@@ -2185,14 +4075,10 @@ readqf(e)
e->e_dtime = atol(&buf[1]);
break;
-# if _FFR_QUEUEDELAY
- case 'G': /* queue delay algorithm */
- e->e_queuealg = atoi(&buf[1]);
- break;
- case 'Y': /* current delay */
- e->e_queuedelay = (time_t) atol(&buf[1]);
+ case 'L': /* Solaris Content-Length: */
+ case 'M': /* message */
+ /* ignore this; we want a new message next time */
break;
-# endif /* _FFR_QUEUEDELAY */
case 'N': /* number of delivery attempts */
e->e_ntries = atoi(&buf[1]);
@@ -2204,12 +4090,14 @@ readqf(e)
{
char *howlong;
- howlong = pintvl(now - e->e_dtime, TRUE);
+ howlong = pintvl(now - e->e_dtime, true);
if (Verbose)
- printf("%s: too young (%s)\n",
- e->e_id, howlong);
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "%s: too young (%s)\n",
+ e->e_id, howlong);
if (tTd(40, 8))
- dprintf("%s: too young (%s)\n",
+ sm_dprintf("%s: too young (%s)\n",
e->e_id, howlong);
if (LogLevel > 19)
sm_syslog(LOG_DEBUG, e->e_id,
@@ -2217,11 +4105,13 @@ readqf(e)
howlong);
e->e_id = NULL;
unlockqueue(e);
- return FALSE;
+ RELEASE_QUEUE;
+ return false;
}
- define(macid("{ntries}", NULL), newstr(&buf[1]), e);
+ macdefine(&e->e_macro, A_TEMP,
+ macid("{ntries}"), &buf[1]);
-# if NAMED_BIND
+#if NAMED_BIND
/* adjust BIND parameters immediately */
if (e->e_ntries == 0)
{
@@ -2233,108 +4123,151 @@ readqf(e)
_res.retry = TimeOuts.res_retry[RES_TO_NORMAL];
_res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL];
}
-# endif /* NAMED_BIND */
+#endif /* NAMED_BIND */
break;
case 'P': /* message priority */
e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
break;
- case 'F': /* flag bits */
- if (strncmp(bp, "From ", 5) == 0)
- {
- /* we are being spoofed! */
- syserr("SECURITY ALERT: bogus qf line %s", bp);
- (void) fclose(qfp);
- loseqfile(e, "bogus queue line");
- return FALSE;
- }
- for (p = &bp[1]; *p != '\0'; p++)
+ case 'Q': /* original recipient */
+ orcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
+ break;
+
+ case 'r': /* original recipient */
+ frcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
+ break;
+
+ case 'R': /* specify recipient */
+ p = bp;
+ qflags = 0;
+ if (qfver >= 1)
{
- switch (*p)
+ /* get flag bits */
+ while (*++p != '\0' && *p != ':')
{
- case 'w': /* warning sent */
- e->e_flags |= EF_WARNING;
- break;
+ switch (*p)
+ {
+ case 'N':
+ qflags |= QHASNOTIFY;
+ break;
- case 'r': /* response */
- e->e_flags |= EF_RESPONSE;
- break;
+ case 'S':
+ qflags |= QPINGONSUCCESS;
+ break;
- case '8': /* has 8 bit data */
- e->e_flags |= EF_HAS8BIT;
- break;
+ case 'F':
+ qflags |= QPINGONFAILURE;
+ break;
- case 'b': /* delete Bcc: header */
- e->e_flags |= EF_DELETE_BCC;
- break;
+ case 'D':
+ qflags |= QPINGONDELAY;
+ break;
- case 'd': /* envelope has DSN RET= */
- e->e_flags |= EF_RET_PARAM;
- break;
+ case 'P':
+ qflags |= QPRIMARY;
+ break;
- case 'n': /* don't return body */
- e->e_flags |= EF_NO_BODY_RETN;
- break;
+ case 'A':
+ if (ctladdr != NULL)
+ ctladdr->q_flags |= QALIAS;
+ break;
+
+ default: /* ignore or complain? */
+ break;
+ }
}
}
+ else
+ qflags |= QPRIMARY;
+ q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e,
+ true);
+ if (q != NULL)
+ {
+ q->q_alias = ctladdr;
+ if (qfver >= 1)
+ q->q_flags &= ~Q_PINGFLAGS;
+ q->q_flags |= qflags;
+ q->q_finalrcpt = frcpt;
+ q->q_orcpt = orcpt;
+ (void) recipient(q, &e->e_sendqueue, 0, e);
+ }
+ frcpt = NULL;
+ orcpt = NULL;
break;
- case 'Z': /* original envelope id from ESMTP */
- e->e_envid = newstr(&bp[1]);
- define(macid("{dsn_envid}", NULL), newstr(&bp[1]), e);
+ case 'S': /* sender */
+ setsender(sm_rpool_strdup_x(e->e_rpool, &bp[1]),
+ e, NULL, '\0', true);
break;
- case 'A': /* AUTH= parameter */
- e->e_auth_param = newstr(&bp[1]);
+ case 'T': /* init time */
+ e->e_ctime = atol(&bp[1]);
+ break;
+
+ case 'V': /* queue file version number */
+ qfver = atoi(&bp[1]);
+ if (queuedelay_qfver_unsupported(qfver))
+ syserr("queue file version %d not supported: %s",
+ qfver,
+ "sendmail not compiled with _FFR_QUEUEDELAY");
+ if (qfver <= QF_VERSION)
+ break;
+ syserr("Version number in queue file (%d) greater than max (%d)",
+ qfver, QF_VERSION);
+ (void) sm_io_close(qfp, SM_TIME_DEFAULT);
+ loseqfile(e, "unsupported queue file version");
+ RELEASE_QUEUE;
+ return false;
+ /* NOTREACHED */
+ break;
+
+#if _FFR_QUEUEDELAY
+ case 'Y': /* current delay */
+ e->e_queuedelay = (time_t) atol(&buf[1]);
+ break;
+#endif /* _FFR_QUEUEDELAY */
+
+ case 'Z': /* original envelope id from ESMTP */
+ e->e_envid = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{dsn_envid}"), e->e_envid);
break;
+ case '!': /* deliver by */
+
+ /* format: flag (1 char) space long-integer */
+ e->e_dlvr_flag = buf[1];
+ e->e_deliver_by = strtol(&buf[3], NULL, 10);
+
case '$': /* define macro */
{
char *p;
- mid = macid(&bp[1], &ep);
- if (mid == 0)
+ /* XXX elimate p? */
+ r = macid_parse(&bp[1], &ep);
+ if (r == 0)
break;
-
- p = newstr(ep);
- define(mid, p, e);
-
- /*
- ** HACK ALERT: Unfortunately, 8.10 and
- ** 8.11 reused the ${if_addr} and
- ** ${if_family} macros for both the incoming
- ** interface address/family (getrequests())
- ** and the outgoing interface address/family
- ** (makeconnection()). In order for D_BINDIF
- ** to work properly, have to preserve the
- ** incoming information in the queue file for
- ** later delivery attempts. The original
- ** information is stored in the envelope
- ** in readqf() so it can be stored in
- ** queueup_macros(). This should be fixed
- ** in 8.12.
- */
-
- if (strcmp(macname(mid), "if_addr") == 0)
- e->e_if_macros[EIF_ADDR] = p;
+ p = sm_rpool_strdup_x(e->e_rpool, ep);
+ macdefine(&e->e_macro, A_PERM, r, p);
}
break;
case '.': /* terminate file */
- nomore = TRUE;
+ nomore = true;
break;
default:
syserr("readqf: %s: line %d: bad line \"%s\"",
qf, LineNumber, shortenstring(bp, MAXSHORTSTR));
- (void) fclose(qfp);
+ (void) sm_io_close(qfp, SM_TIME_DEFAULT);
loseqfile(e, "unrecognized line");
- return FALSE;
+ RELEASE_QUEUE;
+ return false;
}
if (bp != buf)
- sm_free(bp);
+ sm_free(bp); /* XXX */
}
/*
@@ -2345,25 +4278,38 @@ readqf(e)
if (LineNumber == 0)
{
errno = 0;
- e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
- return TRUE;
+ e->e_flags |= EF_CLRQUEUE|EF_FATALERRS|EF_RESPONSE;
+ RELEASE_QUEUE;
+ return true;
+ }
+
+ /* Check to make sure we have a complete queue file read */
+ if (!nomore)
+ {
+ syserr("readqf: %s: incomplete queue file read", qf);
+ (void) sm_io_close(qfp, SM_TIME_DEFAULT);
+ RELEASE_QUEUE;
+ return false;
}
/* possibly set ${dsn_ret} macro */
if (bitset(EF_RET_PARAM, e->e_flags))
{
if (bitset(EF_NO_BODY_RETN, e->e_flags))
- define(macid("{dsn_ret}", NULL), "hdrs", e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{dsn_ret}"), "hdrs");
else
- define(macid("{dsn_ret}", NULL), "full", e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{dsn_ret}"), "full");
}
/*
** Arrange to read the data file.
*/
- p = queuename(e, 'd');
- e->e_dfp = fopen(p, "r");
+ p = queuename(e, DATAFL_LETTER);
+ e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, p, SM_IO_RDONLY,
+ NULL);
if (e->e_dfp == NULL)
{
syserr("readqf: cannot open %s", p);
@@ -2371,17 +4317,19 @@ readqf(e)
else
{
e->e_flags |= EF_HAS_DF;
- if (fstat(fileno(e->e_dfp), &st) >= 0)
+ if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &st)
+ >= 0)
{
e->e_msgsize = st.st_size + hdrsize;
e->e_dfdev = st.st_dev;
- e->e_dfino = st.st_ino;
+ e->e_dfino = ST_INODE(st);
}
}
- return TRUE;
+ RELEASE_QUEUE;
+ return true;
}
- /*
+/*
** PRTSTR -- print a string, "unprintable" characters are shown as \oct
**
** Parameters:
@@ -2389,7 +4337,7 @@ readqf(e)
** ml -- maximum length of output
**
** Returns:
-** none.
+** number of entries
**
** Side Effects:
** Prints a string on stdout.
@@ -2400,7 +4348,7 @@ prtstr(s, ml)
char *s;
int ml;
{
- char c;
+ int c;
if (s == NULL)
return;
@@ -2410,20 +4358,104 @@ prtstr(s, ml)
{
if (ml-- > 0)
{
- putchar(c);
- putchar(c);
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
}
}
else if (isascii(c) && isprint(c))
- putchar(c);
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
else
{
if ((ml -= 3) > 0)
- printf("\\%03o", c);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\\%03o", c & 0xFF);
}
}
}
- /*
+/*
+** PRINTNQE -- print out number of entries in the mail queue
+**
+** Parameters:
+** out -- output file pointer.
+** prefix -- string to output in front of each line.
+**
+** Returns:
+** none.
+*/
+
+void
+printnqe(out, prefix)
+ SM_FILE_T *out;
+ char *prefix;
+{
+#if SM_CONF_SHM
+ int i, k = 0, nrequests = 0;
+ bool unknown = false;
+
+ if (ShmId == SM_SHM_NO_ID)
+ {
+ if (prefix == NULL)
+ (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
+ "Data unavailable: shared memory not updated\n");
+ else
+ (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
+ "%sNOTCONFIGURED:-1\r\n", prefix);
+ return;
+ }
+ for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
+ {
+ int j;
+
+ k++;
+ for (j = 0; j < Queue[i]->qg_numqueues; j++)
+ {
+ int n;
+
+ if (StopRequest)
+ stop_sendmail();
+
+ n = QSHM_ENTRIES(Queue[i]->qg_qpaths[j].qp_idx);
+ if (prefix != NULL)
+ (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
+ "%s%s:%d\r\n",
+ prefix, qid_printqueue(i, j), n);
+ else if (n < 0)
+ {
+ (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
+ "%s: unknown number of entries\n",
+ qid_printqueue(i, j));
+ unknown = true;
+ }
+ else if (n == 0)
+ {
+ (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
+ "%s is empty\n",
+ qid_printqueue(i, j));
+ }
+ else if (n > 0)
+ {
+ (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
+ "%s: entries=%d\n",
+ qid_printqueue(i, j), n);
+ nrequests += n;
+ k++;
+ }
+ }
+ }
+ if (prefix == NULL && k > 1)
+ (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
+ "\t\tTotal requests: %d%s\n",
+ nrequests, unknown ? " (about)" : "");
+#else /* SM_CONF_SHM */
+ if (prefix == NULL)
+ (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
+ "Data unavailable without shared memory support\n");
+ else
+ (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
+ "%sNOTAVAILABLE:-1\r\n", prefix);
+#endif /* SM_CONF_SHM */
+}
+/*
** PRINTQUEUE -- print out a representation of the mail queue
**
** Parameters:
@@ -2439,54 +4471,69 @@ prtstr(s, ml)
void
printqueue()
{
- int i, nrequests = 0;
+ int i, k = 0, nrequests = 0;
- for (i = 0; i < NumQueues; i++)
+ for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
{
- if (StopRequest)
- stop_sendmail();
- nrequests += print_single_queue(i);
+ int j;
+
+ k++;
+ for (j = 0; j < Queue[i]->qg_numqueues; j++)
+ {
+ if (StopRequest)
+ stop_sendmail();
+ nrequests += print_single_queue(i, j);
+ k++;
+ }
}
- if (NumQueues > 1)
- printf("\t\tTotal Requests: %d\n", nrequests);
+ if (k > 1)
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\t\tTotal requests: %d\n",
+ nrequests);
}
- /*
+/*
** PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue
**
** Parameters:
-** queuedir -- queue directory
+** qgrp -- the index of the queue group.
+** qdir -- the queue directory.
**
** Returns:
-** number of entries
+** number of requests in mail queue.
**
** Side Effects:
** Prints a listing of the mail queue on the standard output.
*/
-static int
-print_single_queue(queuedir)
- int queuedir;
+int
+print_single_queue(qgrp, qdir)
+ int qgrp;
+ int qdir;
{
register WORK *w;
- FILE *f;
+ SM_FILE_T *f;
int nrequests;
char qd[MAXPATHLEN];
char qddf[MAXPATHLEN];
char buf[MAXLINE];
- if (queuedir == NOQDIR)
+ if (qdir == NOQDIR)
{
- (void) strlcpy(qd, ".", sizeof qd);
- (void) strlcpy(qddf, ".", sizeof qddf);
+ (void) sm_strlcpy(qd, ".", sizeof qd);
+ (void) sm_strlcpy(qddf, ".", sizeof qddf);
}
else
{
- (void) snprintf(qd, sizeof qd, "%s%s",
- QPaths[queuedir].qp_name,
- (bitset(QP_SUBQF, QPaths[queuedir].qp_subdirs) ? "/qf" : ""));
- (void) snprintf(qddf, sizeof qddf, "%s%s",
- QPaths[queuedir].qp_name,
- (bitset(QP_SUBDF, QPaths[queuedir].qp_subdirs) ? "/df" : ""));
+ (void) sm_strlcpyn(qd, sizeof qd, 2,
+ Queue[qgrp]->qg_qpaths[qdir].qp_name,
+ (bitset(QP_SUBQF,
+ Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
+ ? "/qf" : ""));
+ (void) sm_strlcpyn(qddf, sizeof qddf, 2,
+ Queue[qgrp]->qg_qpaths[qdir].qp_name,
+ (bitset(QP_SUBDF,
+ Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
+ ? "/df" : ""));
}
/*
@@ -2496,17 +4543,18 @@ print_single_queue(queuedir)
if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0)
{
struct stat st;
-# ifdef NGROUPS_MAX
+#ifdef NGROUPS_MAX
int n;
extern GIDSET_T InitialGidSet[NGROUPS_MAX];
-# endif /* NGROUPS_MAX */
+#endif /* NGROUPS_MAX */
if (stat(qd, &st) < 0)
{
- syserr("Cannot stat %s", qid_printqueue(queuedir));
+ syserr("Cannot stat %s",
+ qid_printqueue(qgrp, qdir));
return 0;
}
-# ifdef NGROUPS_MAX
+#ifdef NGROUPS_MAX
n = NGROUPS_MAX;
while (--n >= 0)
{
@@ -2514,9 +4562,9 @@ print_single_queue(queuedir)
break;
}
if (n < 0 && RealGid != st.st_gid)
-# else /* NGROUPS_MAX */
+#else /* NGROUPS_MAX */
if (RealGid != st.st_gid)
-# endif /* NGROUPS_MAX */
+#endif /* NGROUPS_MAX */
{
usrerr("510 You are not permitted to see the queue");
setstat(EX_NOPERM);
@@ -2528,7 +4576,8 @@ print_single_queue(queuedir)
** Read and order the queue.
*/
- nrequests = orderq(queuedir, TRUE);
+ nrequests = gatherq(qgrp, qdir, true, NULL, NULL);
+ (void) sortq(Queue[qgrp]->qg_maxlist);
/*
** Print the work list that we have read.
@@ -2537,20 +4586,25 @@ print_single_queue(queuedir)
/* first see if there is anything */
if (nrequests <= 0)
{
- printf("%s is empty\n", qid_printqueue(queuedir));
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s is empty\n",
+ qid_printqueue(qgrp, qdir));
return 0;
}
- CurrentLA = sm_getla(NULL); /* get load average */
+ sm_getla(); /* get load average */
- printf("\t\t%s (%d request%s", qid_printqueue(queuedir), nrequests,
- nrequests == 1 ? "" : "s");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\t\t%s (%d request%s",
+ qid_printqueue(qgrp, qdir),
+ nrequests, nrequests == 1 ? "" : "s");
if (MaxQueueRun > 0 && nrequests > MaxQueueRun)
- printf(", only %d printed", MaxQueueRun);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ ", only %d printed", MaxQueueRun);
if (Verbose)
- printf(")\n----Q-ID---- --Size-- -Priority- ---Q-Time--- ---------Sender/Recipient--------\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ ")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n");
else
- printf(")\n----Q-ID---- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ ")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n");
for (w = WorkQ; w != NULL; w = w->w_next)
{
struct stat st;
@@ -2558,6 +4612,9 @@ print_single_queue(queuedir)
long dfsize;
int flags = 0;
int qfver;
+#if _FFR_QUARANTINE
+ char quarmsg[MAXLINE];
+#endif /* _FFR_QUARANTINE */
char statmsg[MAXLINE];
char bodytype[MAXNAME + 1];
char qf[MAXPATHLEN];
@@ -2565,34 +4622,80 @@ print_single_queue(queuedir)
if (StopRequest)
stop_sendmail();
- printf("%12s", w->w_name + 2);
- (void) snprintf(qf, sizeof qf, "%s/%s", qd, w->w_name);
- f = fopen(qf, "r");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%13s",
+ w->w_name + 2);
+ (void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", w->w_name);
+ f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY,
+ NULL);
if (f == NULL)
{
- printf(" (job completed)\n");
+ if (errno == EPERM)
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ " (permission denied)\n");
+ else if (errno == ENOENT)
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ " (job completed)\n");
+ else
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ " (%s)\n",
+ sm_errstring(errno));
errno = 0;
continue;
}
- w->w_name[0] = 'd';
- (void) snprintf(qf, sizeof qf, "%s/%s", qddf, w->w_name);
+ w->w_name[0] = DATAFL_LETTER;
+ (void) sm_strlcpyn(qf, sizeof qf, 3, qddf, "/", w->w_name);
if (stat(qf, &st) >= 0)
dfsize = st.st_size;
else
+ {
+ ENVELOPE e;
+
+ /*
+ ** Maybe the df file can't be statted because
+ ** it is in a different directory than the qf file.
+ ** In order to find out, we must read the qf file.
+ */
+
+ newenvelope(&e, &BlankEnvelope, sm_rpool_new_x(NULL));
+ e.e_id = w->w_name + 2;
+ e.e_qgrp = qgrp;
+ e.e_qdir = qdir;
dfsize = -1;
+ if (readqf(&e, false))
+ {
+ char *df = queuename(&e, DATAFL_LETTER);
+ if (stat(df, &st) >= 0)
+ dfsize = st.st_size;
+ }
+ if (e.e_lockfp != NULL)
+ {
+ (void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT);
+ e.e_lockfp = NULL;
+ }
+ clearenvelope(&e, false, e.e_rpool);
+ sm_rpool_free(e.e_rpool);
+ }
if (w->w_lock)
- printf("*");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "*");
+#if _FFR_QUARANTINE
+ else if (QueueMode == QM_LOST)
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "?");
+#endif /* _FFR_QUARANTINE */
else if (w->w_tooyoung)
- printf("-");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "-");
else if (shouldqueue(w->w_pri, w->w_ctime))
- printf("X");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "X");
else
- printf(" ");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " ");
+
errno = 0;
+#if _FFR_QUARANTINE
+ quarmsg[0] = '\0';
+#endif /* _FFR_QUARANTINE */
statmsg[0] = bodytype[0] = '\0';
qfver = 0;
- while (fgets(buf, sizeof buf, f) != NULL)
+ while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
{
register int i;
register char *p;
@@ -2600,7 +4703,7 @@ print_single_queue(queuedir)
if (StopRequest)
stop_sendmail();
- fixcrlf(buf, TRUE);
+ fixcrlf(buf, true);
switch (buf[0])
{
case 'V': /* queue file version */
@@ -2614,6 +4717,15 @@ print_single_queue(queuedir)
statmsg[i] = '\0';
break;
+#if _FFR_QUARANTINE
+ case 'q': /* quarantine reason */
+ if ((i = strlen(&buf[1])) >= sizeof quarmsg)
+ i = sizeof quarmsg - 1;
+ memmove(quarmsg, &buf[1], i);
+ quarmsg[i] = '\0';
+ break;
+#endif /* _FFR_QUARANTINE */
+
case 'B': /* body type */
if ((i = strlen(&buf[1])) >= sizeof bodytype)
i = sizeof bodytype - 1;
@@ -2624,33 +4736,58 @@ print_single_queue(queuedir)
case 'S': /* sender name */
if (Verbose)
{
- printf("%8ld %10ld%c%.12s ",
- dfsize,
- w->w_pri,
- bitset(EF_WARNING, flags) ? '+' : ' ',
- ctime(&submittime) + 4);
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "%8ld %10ld%c%.12s ",
+ dfsize,
+ w->w_pri,
+ bitset(EF_WARNING, flags)
+ ? '+' : ' ',
+ ctime(&submittime) + 4);
prtstr(&buf[1], 78);
}
else
{
- printf("%8ld %.16s ", dfsize,
- ctime(&submittime));
- prtstr(&buf[1], 40);
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "%8ld %.16s ",
+ dfsize,
+ ctime(&submittime));
+ prtstr(&buf[1], 39);
}
+#if _FFR_QUARANTINE
+ if (quarmsg[0] != '\0')
+ {
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "\n QUARANTINE: %.*s",
+ Verbose ? 100 : 60,
+ quarmsg);
+ quarmsg[0] = '\0';
+ }
+#endif /* _FFR_QUARANTINE */
if (statmsg[0] != '\0' || bodytype[0] != '\0')
{
- printf("\n %10.10s", bodytype);
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "\n %10.10s",
+ bodytype);
if (statmsg[0] != '\0')
- printf(" (%.*s)",
- Verbose ? 100 : 60,
- statmsg);
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ " (%.*s)",
+ Verbose ? 100 : 60,
+ statmsg);
+ statmsg[0] = '\0';
}
break;
case 'C': /* controlling user */
if (Verbose)
- printf("\n\t\t\t\t (---%.74s---)",
- &buf[1]);
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "\n\t\t\t\t\t\t(---%.64s---)",
+ &buf[1]);
break;
case 'R': /* recipient name */
@@ -2664,13 +4801,25 @@ print_single_queue(queuedir)
}
if (Verbose)
{
- printf("\n\t\t\t\t\t ");
- prtstr(p, 73);
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "\n\t\t\t\t\t\t");
+ prtstr(p, 71);
}
else
{
- printf("\n\t\t\t\t ");
- prtstr(p, 40);
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "\n\t\t\t\t\t ");
+ prtstr(p, 38);
+ }
+ if (Verbose && statmsg[0] != '\0')
+ {
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "\n\t\t (%.100s)",
+ statmsg);
+ statmsg[0] = '\0';
}
break;
@@ -2691,13 +4840,65 @@ print_single_queue(queuedir)
}
}
if (submittime == (time_t) 0)
- printf(" (no control file)");
- printf("\n");
- (void) fclose(f);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ " (no control file)");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n");
+ (void) sm_io_close(f, SM_TIME_DEFAULT);
}
return nrequests;
}
- /*
+
+#if _FFR_QUARANTINE
+/*
+** QUEUE_LETTER -- get the proper queue letter for the current QueueMode.
+**
+** Parameters:
+** e -- envelope to build it in/from.
+** type -- the file type, used as the first character
+** of the file name.
+**
+** Returns:
+** the letter to use
+*/
+
+static char
+queue_letter(e, type)
+ ENVELOPE *e;
+ int type;
+{
+ /* Change type according to QueueMode */
+ if (type == ANYQFL_LETTER)
+ {
+ if (e->e_quarmsg != NULL)
+ type = QUARQF_LETTER;
+ else
+ {
+ switch (QueueMode)
+ {
+ case QM_NORMAL:
+ type = NORMQF_LETTER;
+ break;
+
+ case QM_QUARANTINE:
+ type = QUARQF_LETTER;
+ break;
+
+ case QM_LOST:
+ type = LOSEQF_LETTER;
+ break;
+
+ default:
+ /* should never happen */
+ abort();
+ /* NOTREACHED */
+ }
+ }
+ }
+ return type;
+}
+#endif /* _FFR_QUARANTINE */
+
+/*
** QUEUENAME -- build a file name in the queue directory for this envelope.
**
** Parameters:
@@ -2719,60 +4920,121 @@ queuename(e, type)
register ENVELOPE *e;
int type;
{
- char *sub = "";
+ int qd, qg;
+ char *sub = "/";
+ char pref[3];
static char buf[MAXPATHLEN];
/* Assign an ID if needed */
if (e->e_id == NULL)
assign_queueid(e);
- /* Assign a queue directory if needed */
- if (e->e_queuedir == NOQDIR)
- setnewqueue(e);
+#if _FFR_QUARANTINE
+ type = queue_letter(e, type);
+#endif /* _FFR_QUARANTINE */
+
+ /* begin of filename */
+ pref[0] = (char) type;
+ pref[1] = 'f';
+ pref[2] = '\0';
+
+ /* Assign a queue group/directory if needed */
+ if (type == XSCRPT_LETTER)
+ {
+ /*
+ ** We don't want to call setnewqueue() if we are fetching
+ ** the pathname of the transcript file, because setnewqueue
+ ** chooses a queue, and sometimes we need to write to the
+ ** transcript file before we have gathered enough information
+ ** to choose a queue.
+ */
+
+ if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
+ {
+ if (e->e_qgrp != NOQGRP && e->e_qdir != NOQDIR)
+ {
+ e->e_xfqgrp = e->e_qgrp;
+ e->e_xfqdir = e->e_qdir;
+ }
+ else
+ {
+ e->e_xfqgrp = 0;
+ if (Queue[e->e_xfqgrp]->qg_numqueues <= 1)
+ e->e_xfqdir = 0;
+ else
+ {
+ e->e_xfqdir = get_rand_mod(
+ Queue[e->e_xfqgrp]->qg_numqueues);
+ }
+ }
+ }
+ qd = e->e_xfqdir;
+ qg = e->e_xfqgrp;
+ }
+ else
+ {
+ if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
+ setnewqueue(e);
+ if (type == DATAFL_LETTER)
+ {
+ qd = e->e_dfqdir;
+ qg = e->e_dfqgrp;
+ }
+ else
+ {
+ qd = e->e_qdir;
+ qg = e->e_qgrp;
+ }
+ }
- if (e->e_queuedir == NOQDIR)
- (void) snprintf(buf, sizeof buf, "%cf%s",
- type, e->e_id);
+ if (e->e_qdir == NOQDIR)
+ (void) sm_strlcpyn(buf, sizeof buf, 2, pref, e->e_id);
else
{
switch (type)
{
- case 'd':
- if (bitset(QP_SUBDF, QPaths[e->e_queuedir].qp_subdirs))
- sub = "/df";
+ case DATAFL_LETTER:
+ if (bitset(QP_SUBDF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
+ sub = "/df/";
break;
+#if _FFR_QUARANTINE
+ case QUARQF_LETTER:
+#endif /* _FFR_QUARANTINE */
case TEMPQF_LETTER:
- case 't':
+ case NEWQFL_LETTER:
case LOSEQF_LETTER:
- case 'q':
- if (bitset(QP_SUBQF, QPaths[e->e_queuedir].qp_subdirs))
- sub = "/qf";
+ case NORMQF_LETTER:
+ if (bitset(QP_SUBQF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
+ sub = "/qf/";
break;
- case 'x':
- if (bitset(QP_SUBXF, QPaths[e->e_queuedir].qp_subdirs))
- sub = "/xf";
+ case XSCRPT_LETTER:
+ if (bitset(QP_SUBXF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
+ sub = "/xf/";
break;
+
+ default:
+ sm_abort("queuename: bad queue file type %d", type);
}
- (void) snprintf(buf, sizeof buf, "%s%s/%cf%s",
- QPaths[e->e_queuedir].qp_name,
- sub, type, e->e_id);
+ (void) sm_strlcpyn(buf, sizeof buf, 4,
+ Queue[qg]->qg_qpaths[qd].qp_name,
+ sub, pref, e->e_id);
}
if (tTd(7, 2))
- dprintf("queuename: %s\n", buf);
+ sm_dprintf("queuename: %s\n", buf);
return buf;
}
- /*
+/*
** ASSIGN_QUEUEID -- assign a queue ID for this envelope.
**
** Assigns an id code if one does not already exist.
** This code assumes that nothing will remain in the queue for
** longer than 60 years. It is critical that files with the given
-** name not already exist in the queue.
-** Also initializes e_queuedir to NOQDIR.
+** name do not already exist in the queue.
+** [No longer initializes e_qdir to NOQDIR.]
**
** Parameters:
** e -- envelope to set it in.
@@ -2783,22 +5045,26 @@ queuename(e, type)
static const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx";
# define QIC_LEN 60
+# define queuenextid() CurrentPid
+
void
assign_queueid(e)
register ENVELOPE *e;
{
- pid_t pid = getpid();
- static char cX = 0;
+ pid_t pid = queuenextid();
+ static int cX = 0;
static long random_offset;
struct tm *tm;
char idbuf[MAXQFNAME - 2];
+ int seq;
if (e->e_id != NULL)
return;
/* see if we need to get a new base time/pid */
- if (cX >= QIC_LEN || LastQueueTime == 0 || LastQueuePid != pid)
+ if (cX >= QIC_LEN * QIC_LEN || LastQueueTime == 0 ||
+ LastQueuePid != pid)
{
time_t then = LastQueueTime;
@@ -2811,12 +5077,22 @@ assign_queueid(e)
{
(void) sleep(1);
}
- LastQueuePid = getpid();
+ LastQueuePid = queuenextid();
cX = 0;
}
+
+ /*
+ ** Generate a new sequence number between 0 and QIC_LEN*QIC_LEN-1.
+ ** This lets us generate up to QIC_LEN*QIC_LEN unique queue ids
+ ** per second, per process. With envelope splitting,
+ ** a single message can consume many queue ids.
+ */
+
+ seq = (int)((cX + random_offset) % (QIC_LEN * QIC_LEN));
+ ++cX;
if (tTd(7, 50))
- dprintf("assign_queueid: random_offset = %ld (%d)\n",
- random_offset, (int)(cX + random_offset) % QIC_LEN);
+ sm_dprintf("assign_queueid: random_offset = %ld (%d)\n",
+ random_offset, seq);
tm = gmtime(&LastQueueTime);
idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN];
@@ -2825,19 +5101,29 @@ assign_queueid(e)
idbuf[3] = QueueIdChars[tm->tm_hour];
idbuf[4] = QueueIdChars[tm->tm_min];
idbuf[5] = QueueIdChars[tm->tm_sec];
- idbuf[6] = QueueIdChars[((int)cX++ + random_offset) % QIC_LEN];
- (void) snprintf(&idbuf[7], sizeof idbuf - 7, "%05d",
- (int) LastQueuePid);
- e->e_id = newstr(idbuf);
- define('i', e->e_id, e);
- e->e_queuedir = NOQDIR;
+ idbuf[6] = QueueIdChars[seq / QIC_LEN];
+ idbuf[7] = QueueIdChars[seq % QIC_LEN];
+ (void) sm_snprintf(&idbuf[8], sizeof idbuf - 8, "%06d",
+ (int) LastQueuePid);
+ e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf);
+ macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
+#if 0
+ /* XXX: inherited from MainEnvelope */
+ e->e_qgrp = NOQGRP; /* too early to do anything else */
+ e->e_qdir = NOQDIR;
+ e->e_xfqgrp = NOQGRP;
+#endif /* 0 */
+#if _FFR_QUARANTINE
+ /* New ID means it's not on disk yet */
+ e->e_qfletter = '\0';
+#endif /* _FFR_QUARANTINE */
if (tTd(7, 1))
- dprintf("assign_queueid: assigned id %s, e=%lx\n",
- e->e_id, (u_long) e);
+ sm_dprintf("assign_queueid: assigned id %s, e=%p\n",
+ e->e_id, e);
if (LogLevel > 93)
sm_syslog(LOG_DEBUG, e->e_id, "assigned id");
}
- /*
+/*
** SYNC_QUEUE_TIME -- Assure exclusive PID in any given second
**
** Make sure one PID can't be used by two processes in any one second.
@@ -2852,19 +5138,20 @@ assign_queueid(e)
** Returns:
** none
*/
+
void
sync_queue_time()
{
-# if FAST_PID_RECYCLE
+#if FAST_PID_RECYCLE
if (OpMode != MD_TEST &&
OpMode != MD_VERIFY &&
LastQueueTime > 0 &&
- LastQueuePid == getpid() &&
+ LastQueuePid == CurrentPid &&
curtime() == LastQueueTime)
(void) sleep(1);
-# endif /* FAST_PID_RECYCLE */
+#endif /* FAST_PID_RECYCLE */
}
- /*
+/*
** UNLOCKQUEUE -- unlock the queue entry for a specified envelope
**
** Parameters:
@@ -2882,13 +5169,13 @@ unlockqueue(e)
ENVELOPE *e;
{
if (tTd(51, 4))
- dprintf("unlockqueue(%s)\n",
+ sm_dprintf("unlockqueue(%s)\n",
e->e_id == NULL ? "NOQUEUE" : e->e_id);
/* if there is a lock file in the envelope, close it */
if (e->e_lockfp != NULL)
- (void) fclose(e->e_lockfp);
+ (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
e->e_lockfp = NULL;
/* don't create a queue id if we don't already have one */
@@ -2899,10 +5186,9 @@ unlockqueue(e)
if (LogLevel > 87)
sm_syslog(LOG_DEBUG, e->e_id, "unlock");
if (!tTd(51, 104))
- xunlink(queuename(e, 'x'));
-
+ (void) xunlink(queuename(e, XSCRPT_LETTER));
}
- /*
+/*
** SETCTLUSER -- create a controlling address
**
** Create a fake "address" given only a local login name; this is
@@ -2910,19 +5196,20 @@ unlockqueue(e)
**
** Parameters:
** user -- the user name of the controlling user.
-** qfver -- the version stamp of this qf file.
+** qfver -- the version stamp of this queue file.
+** e -- envelope
**
** Returns:
-** An address descriptor for the controlling user.
+** An address descriptor for the controlling user,
+** using storage allocated from e->e_rpool.
**
-** Side Effects:
-** none.
*/
static ADDRESS *
-setctluser(user, qfver)
+setctluser(user, qfver, e)
char *user;
int qfver;
+ ENVELOPE *e;
{
register ADDRESS *a;
struct passwd *pw;
@@ -2939,23 +5226,18 @@ setctluser(user, qfver)
** Set up addr fields for controlling user.
*/
- a = (ADDRESS *) xalloc(sizeof *a);
+ a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof *a);
memset((char *) a, '\0', sizeof *a);
- if (*user == '\0')
- {
- p = NULL;
- a->q_user = newstr(DefUser);
- }
- else if (*user == ':')
+ if (*user == ':')
{
p = &user[1];
- a->q_user = newstr(p);
+ a->q_user = sm_rpool_strdup_x(e->e_rpool, p);
}
else
{
p = strtok(user, ":");
- a->q_user = newstr(user);
+ a->q_user = sm_rpool_strdup_x(e->e_rpool, user);
if (qfver >= 2)
{
if ((p = strtok(NULL, ":")) != NULL)
@@ -2980,7 +5262,7 @@ setctluser(user, qfver)
else if (strcmp(pw->pw_dir, "/") == 0)
a->q_home = "";
else
- a->q_home = newstr(pw->pw_dir);
+ a->q_home = sm_rpool_strdup_x(e->e_rpool, pw->pw_dir);
a->q_uid = pw->pw_uid;
a->q_gid = pw->pw_gid;
a->q_flags |= QGOODUID;
@@ -2990,13 +5272,13 @@ setctluser(user, qfver)
a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */
a->q_mailer = LocalMailer;
if (p == NULL)
- a->q_paddr = newstr(a->q_user);
+ a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user);
else
- a->q_paddr = newstr(p);
+ a->q_paddr = sm_rpool_strdup_x(e->e_rpool, p);
return a;
}
- /*
-** LOSEQFILE -- save the qf as Qf and try to let someone know
+/*
+** LOSEQFILE -- rename queue file with LOSEQF_LETTER & try to let someone know
**
** Parameters:
** e -- the envelope (e->e_id will be used).
@@ -3011,23 +5293,63 @@ loseqfile(e, why)
register ENVELOPE *e;
char *why;
{
+ bool loseit = true;
char *p;
char buf[MAXPATHLEN];
if (e == NULL || e->e_id == NULL)
return;
- p = queuename(e, 'q');
- if (strlen(p) >= (SIZE_T) sizeof buf)
+ p = queuename(e, ANYQFL_LETTER);
+ if (sm_strlcpy(buf, p, sizeof buf) >= sizeof buf)
return;
- (void) strlcpy(buf, p, sizeof buf);
- p = queuename(e, LOSEQF_LETTER);
- if (rename(buf, p) < 0)
- syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid());
- else if (LogLevel > 0)
- sm_syslog(LOG_ALERT, e->e_id,
- "Losing %s: %s", buf, why);
+ if (!bitset(EF_INQUEUE, e->e_flags))
+ queueup(e, false, true);
+#if _FFR_QUARANTINE
+ else if (QueueMode == QM_LOST)
+ loseit = false;
+#endif /* _FFR_QUARANTINE */
+
+ /* if already lost, no need to re-lose */
+ if (loseit)
+ {
+ p = queuename(e, LOSEQF_LETTER);
+ if (rename(buf, p) < 0)
+ syserr("cannot rename(%s, %s), uid=%d",
+ buf, p, geteuid());
+ else if (LogLevel > 0)
+ sm_syslog(LOG_ALERT, e->e_id,
+ "Losing %s: %s", buf, why);
+ }
+ if (e->e_dfp != NULL)
+ {
+ (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
+ e->e_dfp = NULL;
+ }
+ e->e_flags &= ~EF_HAS_DF;
+}
+/*
+** NAME2QID -- translate a queue group name to a queue group id
+**
+** Parameters:
+** queuename -- name of queue group.
+**
+** Returns:
+** queue group id if found.
+** NOQGRP otherwise.
+*/
+
+int
+name2qid(queuename)
+ char *queuename;
+{
+ register STAB *s;
+
+ s = stab(queuename, ST_QUEUE, ST_FIND);
+ if (s == NULL)
+ return NOQGRP;
+ return s->s_quegrp->qg_index;
}
- /*
+/*
** QID_PRINTNAME -- create externally printable version of queue id
**
** Parameters:
@@ -3052,100 +5374,225 @@ qid_printname(e)
else
id = e->e_id;
- if (e->e_queuedir == NOQDIR)
+ if (e->e_qdir == NOQDIR)
return id;
- (void) snprintf(idbuf, sizeof idbuf, "%.32s/%s",
- QPaths[e->e_queuedir].qp_name, id);
+ (void) sm_snprintf(idbuf, sizeof idbuf, "%.32s/%s",
+ Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_name,
+ id);
return idbuf;
}
- /*
-** QID_PRINTQUEUE -- create full version of queue directory for df files
+/*
+** QID_PRINTQUEUE -- create full version of queue directory for data files
**
** Parameters:
-** queuedir -- the short version of the queue directory
+** qgrp -- index in queue group.
+** qdir -- the short version of the queue directory
**
** Returns:
-** the full pathname to the queue (static)
+** the full pathname to the queue (might point to a static var)
*/
char *
-qid_printqueue(queuedir)
- int queuedir;
+qid_printqueue(qgrp, qdir)
+ int qgrp;
+ int qdir;
{
char *subdir;
static char dir[MAXPATHLEN];
- if (queuedir == NOQDIR)
- return QueueDir;
+ if (qdir == NOQDIR)
+ return Queue[qgrp]->qg_qdir;
- if (strcmp(QPaths[queuedir].qp_name, ".") == 0)
+ if (strcmp(Queue[qgrp]->qg_qpaths[qdir].qp_name, ".") == 0)
subdir = NULL;
else
- subdir = QPaths[queuedir].qp_name;
+ subdir = Queue[qgrp]->qg_qpaths[qdir].qp_name;
- (void) snprintf(dir, sizeof dir, "%s%s%s%s", QueueDir,
+ (void) sm_strlcpyn(dir, sizeof dir, 4,
+ Queue[qgrp]->qg_qdir,
subdir == NULL ? "" : "/",
subdir == NULL ? "" : subdir,
- (bitset(QP_SUBDF, QPaths[queuedir].qp_subdirs) ? "/df" : ""));
+ (bitset(QP_SUBDF,
+ Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
+ ? "/df" : ""));
return dir;
}
- /*
-** SETNEWQUEUE -- Sets a new queue directory
+
+/*
+** PICKQDIR -- Pick a queue directory from a queue group
+**
+** Parameters:
+** qg -- queue group
+** fsize -- file size in bytes
+** e -- envelope, or NULL
**
-** Assign a queue directory to an envelope and store the directory
-** in e->e_queuedir. The queue is chosen at random.
+** Result:
+** NOQDIR if no queue directory in qg has enough free space to
+** hold a file of size 'fsize', otherwise the index of
+** a randomly selected queue directory which resides on a
+** file system with enough disk space.
+** XXX This could be extended to select a queuedir with
+** a few (the fewest?) number of entries. That data
+** is available if shared memory is used.
+**
+** Side Effects:
+** If the request fails and e != NULL then sm_syslog is called.
+*/
+
+int
+pickqdir(qg, fsize, e)
+ QUEUEGRP *qg;
+ long fsize;
+ ENVELOPE *e;
+{
+ int qdir;
+ int i;
+ long avail = 0;
+
+ /* Pick a random directory, as a starting point. */
+ if (qg->qg_numqueues <= 1)
+ qdir = 0;
+ else
+ qdir = get_rand_mod(qg->qg_numqueues);
+
+ if (MinBlocksFree <= 0 && fsize <= 0)
+ return qdir;
+
+ /*
+ ** Now iterate over the queue directories,
+ ** looking for a directory with enough space for this message.
+ */
+
+ i = qdir;
+ do
+ {
+ QPATHS *qp = &qg->qg_qpaths[i];
+ long needed = 0;
+ long fsavail = 0;
+
+ if (fsize > 0)
+ needed += fsize / FILE_SYS_BLKSIZE(qp->qp_fsysidx)
+ + ((fsize % FILE_SYS_BLKSIZE(qp->qp_fsysidx)
+ > 0) ? 1 : 0);
+ if (MinBlocksFree > 0)
+ needed += MinBlocksFree;
+ fsavail = FILE_SYS_AVAIL(qp->qp_fsysidx);
+#if SM_CONF_SHM
+ if (fsavail <= 0)
+ {
+ long blksize;
+
+ /*
+ ** might be not correctly updated,
+ ** let's try to get the info directly.
+ */
+
+ fsavail = freediskspace(FILE_SYS_NAME(qp->qp_fsysidx),
+ &blksize);
+ if (fsavail < 0)
+ fsavail = 0;
+ }
+#endif /* SM_CONF_SHM */
+ if (needed <= fsavail)
+ return i;
+ if (avail < fsavail)
+ avail = fsavail;
+
+ if (qg->qg_numqueues > 0)
+ i = (i + 1) % qg->qg_numqueues;
+ } while (i != qdir);
+
+ if (e != NULL && LogLevel > 0)
+ sm_syslog(LOG_ALERT, e->e_id,
+ "low on space (%s needs %ld bytes + %ld blocks in %s), max avail: %ld",
+ CurHostName == NULL ? "SMTP-DAEMON" : CurHostName,
+ fsize, MinBlocksFree,
+ qg->qg_qdir, avail);
+ return NOQDIR;
+}
+/*
+** SETNEWQUEUE -- Sets a new queue group and directory
**
-** This routine may be improved in the future to allow for more
-** elaborate queueing schemes. Suggestions and code contributions
-** are welcome.
+** Assign a queue group and directory to an envelope and store the
+** directory in e->e_qdir.
**
** Parameters:
** e -- envelope to assign a queue for.
**
** Returns:
-** none.
+** true if successful
+** false otherwise
+**
+** Side Effects:
+** On success, e->e_qgrp and e->e_qdir are non-negative.
+** On failure (not enough disk space),
+** e->qgrp = NOQGRP, e->e_qdir = NOQDIR
+** and usrerr() is invoked (which could raise an exception).
*/
-void
+bool
setnewqueue(e)
ENVELOPE *e;
{
- int idx;
-
if (tTd(41, 20))
- dprintf("setnewqueue: called\n");
+ sm_dprintf("setnewqueue: called\n");
+
+ /* not set somewhere else */
+ if (e->e_qgrp == NOQGRP)
+ {
+ /*
+ ** Use the queue group of the first recipient, as set by
+ ** the "queuegroup" rule set. If that is not defined, then
+ ** use the queue group of the mailer of the first recipient.
+ ** If that is not defined either, then use the default
+ ** queue group.
+ */
+
+ if (e->e_sendqueue == NULL)
+ e->e_qgrp = 0;
+ else if (e->e_sendqueue->q_qgrp >= 0)
+ e->e_qgrp = e->e_sendqueue->q_qgrp;
+ else if (e->e_sendqueue->q_mailer != NULL &&
+ ISVALIDQGRP(e->e_sendqueue->q_mailer->m_qgrp))
+ e->e_qgrp = e->e_sendqueue->q_mailer->m_qgrp;
+ else
+ e->e_qgrp = 0;
+ e->e_dfqgrp = e->e_qgrp;
+ }
- if (e->e_queuedir != NOQDIR)
+ if (ISVALIDQDIR(e->e_qdir) && ISVALIDQDIR(e->e_dfqdir))
{
if (tTd(41, 20))
- dprintf("setnewqueue: e_queuedir already assigned (%s)\n",
- qid_printqueue(e->e_queuedir));
- return;
+ sm_dprintf("setnewqueue: e_qdir already assigned (%s)\n",
+ qid_printqueue(e->e_qgrp, e->e_qdir));
+ return true;
}
- if (NumQueues <= 1)
- idx = 0;
- else
+ filesys_update();
+ e->e_qdir = pickqdir(Queue[e->e_qgrp], e->e_msgsize, e);
+ if (e->e_qdir == NOQDIR)
{
-#if RANDOMSHIFT
- /* lower bits are not random "enough", select others */
- idx = (get_random() >> RANDOMSHIFT) % NumQueues;
-#else /* RANDOMSHIFT */
- idx = get_random() % NumQueues;
-#endif /* RANDOMSHIFT */
- if (tTd(41, 15))
- dprintf("setnewqueue: get_random() %% %d = %d\n",
- NumQueues, idx);
+ e->e_qgrp = NOQGRP;
+ if (!bitset(EF_FATALERRS, e->e_flags))
+ usrerr("452 4.4.5 Insufficient disk space; try again later");
+ e->e_flags |= EF_FATALERRS;
+ return false;
}
- e->e_queuedir = idx;
if (tTd(41, 3))
- dprintf("setnewqueue: Assigned queue directory %s\n",
- qid_printqueue(e->e_queuedir));
-}
+ sm_dprintf("setnewqueue: Assigned queue directory %s\n",
+ qid_printqueue(e->e_qgrp, e->e_qdir));
- /*
+ if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
+ {
+ e->e_xfqgrp = e->e_qgrp;
+ e->e_xfqdir = e->e_qdir;
+ }
+ e->e_dfqdir = e->e_qdir;
+ return true;
+}
+/*
** CHKQDIR -- check a queue directory
**
** Parameters:
@@ -3167,52 +5614,53 @@ chkqdir(name, sff)
/* skip over . and .. directories */
if (name[0] == '.' &&
(name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
- return FALSE;
-# if HASLSTAT
+ return false;
+#if HASLSTAT
if (lstat(name, &statb) < 0)
-# else /* HASLSTAT */
+#else /* HASLSTAT */
if (stat(name, &statb) < 0)
-# endif /* HASLSTAT */
+#endif /* HASLSTAT */
{
if (tTd(41, 2))
- dprintf("multiqueue_cache: stat(\"%s\"): %s\n",
- name, errstring(errno));
- return FALSE;
+ sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
+ name, sm_errstring(errno));
+ return false;
}
-# if HASLSTAT
+#if HASLSTAT
if (S_ISLNK(statb.st_mode))
{
/*
** For a symlink we need to make sure the
** target is a directory
*/
+
if (stat(name, &statb) < 0)
{
if (tTd(41, 2))
- dprintf("multiqueue_cache: stat(\"%s\"): %s\n",
- name, errstring(errno));
- return FALSE;
+ sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
+ name, sm_errstring(errno));
+ return false;
}
}
-# endif /* HASLSTAT */
+#endif /* HASLSTAT */
if (!S_ISDIR(statb.st_mode))
{
if (tTd(41, 2))
- dprintf("multiqueue_cache: \"%s\": Not a directory\n",
+ sm_dprintf("chkqdir: \"%s\": Not a directory\n",
name);
- return FALSE;
+ return false;
}
/* Print a warning if unsafe (but still use it) */
+ /* XXX do this only if we want the warning? */
i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0);
if (i != 0 && tTd(41, 2))
- dprintf("multiqueue_cache: \"%s\": Not safe: %s\n",
- name, errstring(i));
- return TRUE;
+ sm_dprintf("chkqdir: \"%s\": Not safe: %s\n",
+ name, sm_errstring(i));
+ return true;
}
-
- /*
+/*
** MULTIQUEUE_CACHE -- cache a list of paths to queues.
**
** Each potential queue is checked as the cache is built.
@@ -3221,206 +5669,1234 @@ chkqdir(name, sff)
** (although code for that is not ready yet).
**
** Parameters:
-** none
+** basedir -- base of all queue directories.
+** blen -- strlen(basedir).
+** qg -- queue group.
+** qn -- number of queue directories already cached.
+** phash -- pointer to hash value over queue dirs.
+#if SM_CONF_SHM
+** only used if shared memory is active.
+#endif * SM_CONF_SHM *
**
** Returns:
-** none
+** new number of queue directories.
*/
-void
-multiqueue_cache()
+#define INITIAL_SLOTS 20
+#define ADD_SLOTS 10
+
+static int
+multiqueue_cache(basedir, blen, qg, qn, phash)
+ char *basedir;
+ int blen;
+ QUEUEGRP *qg;
+ int qn;
+ unsigned int *phash;
{
- register DIR *dp;
- register struct dirent *d;
char *cp;
int i, len;
int slotsleft = 0;
long sff = SFF_ANYFILE;
char qpath[MAXPATHLEN];
char subdir[MAXPATHLEN];
+ char prefix[MAXPATHLEN]; /* dir relative to basedir */
if (tTd(41, 20))
- dprintf("multiqueue_cache: called\n");
+ sm_dprintf("multiqueue_cache: called\n");
- if (NumQueues != 0 && QPaths != NULL)
+ /* Initialize to current directory */
+ prefix[0] = '.';
+ prefix[1] = '\0';
+ if (qg->qg_numqueues != 0 && qg->qg_qpaths != NULL)
{
- for (i = 0; i < NumQueues; i++)
+ for (i = 0; i < qg->qg_numqueues; i++)
{
- if (QPaths[i].qp_name != NULL)
- sm_free(QPaths[i].qp_name);
+ if (qg->qg_qpaths[i].qp_name != NULL)
+ (void) sm_free(qg->qg_qpaths[i].qp_name); /* XXX */
}
- sm_free((char *)QPaths);
- QPaths = NULL;
- NumQueues = 0;
+ (void) sm_free((char *) qg->qg_qpaths); /* XXX */
+ qg->qg_qpaths = NULL;
+ qg->qg_numqueues = 0;
}
/* If running as root, allow safedirpath() checks to use privs */
if (RunAsUid == 0)
sff |= SFF_ROOTOK;
- (void) snprintf(qpath, sizeof qpath, "%s", QueueDir);
- len = strlen(qpath) - 1;
- cp = &qpath[len];
+ if (!SM_IS_DIR_START(qg->qg_qdir))
+ {
+ /*
+ ** XXX we could add basedir, but then we have to realloc()
+ ** the string... Maybe another time.
+ */
+
+ syserr("QueuePath %s not absolute", qg->qg_qdir);
+ ExitStat = EX_CONFIG;
+ return qn;
+ }
+
+ /* qpath: directory of current workgroup */
+ len = sm_strlcpy(qpath, qg->qg_qdir, sizeof qpath);
+ if (len >= sizeof qpath)
+ {
+ syserr("QueuePath %.256s too long (%d max)",
+ qg->qg_qdir, (int) sizeof qpath);
+ ExitStat = EX_CONFIG;
+ return qn;
+ }
+
+ /* begin of qpath must be same as basedir */
+ if (strncmp(basedir, qpath, blen) != 0 &&
+ (strncmp(basedir, qpath, blen - 1) != 0 || len != blen - 1))
+ {
+ syserr("QueuePath %s not subpath of QueueDirectory %s",
+ qpath, basedir);
+ ExitStat = EX_CONFIG;
+ return qn;
+ }
+
+ /* Do we have a nested subdirectory? */
+ if (blen < len && SM_FIRST_DIR_DELIM(qg->qg_qdir + blen) != NULL)
+ {
+
+ /* Copy subdirectory into prefix for later use */
+ if (sm_strlcpy(prefix, qg->qg_qdir + blen, sizeof prefix) >=
+ sizeof prefix)
+ {
+ syserr("QueuePath %.256s too long (%d max)",
+ qg->qg_qdir, (int) sizeof qpath);
+ ExitStat = EX_CONFIG;
+ return qn;
+ }
+ cp = SM_LAST_DIR_DELIM(prefix);
+ SM_ASSERT(cp != NULL);
+ *cp = '\0'; /* cut off trailing / */
+ }
+
+ /* This is guaranteed by the basedir check above */
+ SM_ASSERT(len >= blen - 1);
+ cp = &qpath[len - 1];
if (*cp == '*')
{
- *cp = '\0';
- if ((cp = strrchr(qpath, '/')) == NULL)
+ register DIR *dp;
+ register struct dirent *d;
+ int off;
+ char *delim;
+ char relpath[MAXPATHLEN];
+
+ *cp = '\0'; /* Overwrite wildcard */
+ if ((cp = SM_LAST_DIR_DELIM(qpath)) == NULL)
{
syserr("QueueDirectory: can not wildcard relative path");
if (tTd(41, 2))
- dprintf("multiqueue_cache: \"%s\": Can not wildcard relative path.\n",
+ sm_dprintf("multiqueue_cache: \"%s*\": Can not wildcard relative path.\n",
qpath);
ExitStat = EX_CONFIG;
- return;
+ return qn;
}
if (cp == qpath)
{
/*
** Special case of top level wildcard, like /foo*
+ ** Change to //foo*
*/
- (void) snprintf(qpath + 1, sizeof qpath - 1,
- "%s", qpath);
+ (void) sm_strlcpy(qpath + 1, qpath, sizeof qpath - 1);
++cp;
}
- *(cp++) = '\0';
- len = strlen(cp);
+ delim = cp;
+ *(cp++) = '\0'; /* Replace / with \0 */
+ len = strlen(cp); /* Last component of queue directory */
+
+ /*
+ ** Path relative to basedir, with trailing /
+ ** It will be modified below to specify the subdirectories
+ ** so they can be opened without chdir().
+ */
+
+ off = sm_strlcpyn(relpath, sizeof relpath, 2, prefix, "/");
+ SM_ASSERT(off < sizeof relpath);
if (tTd(41, 2))
- dprintf("multiqueue_cache: prefix=\"%s\"\n", cp);
+ sm_dprintf("multiqueue_cache: prefix=\"%s%s\"\n",
+ relpath, cp);
- QueueDir = newstr(qpath);
+ /* It is always basedir: we don't need to store it per group */
+ /* XXX: optimize this! -> one more global? */
+ qg->qg_qdir = newstr(basedir);
+ qg->qg_qdir[blen - 1] = '\0'; /* cut off trailing / */
/*
** XXX Should probably wrap this whole loop in a timeout
** in case some wag decides to NFS mount the queues.
*/
- /* test path to get warning messages */
- i= safedirpath(QueueDir, RunAsUid, RunAsGid, NULL, sff, 0, 0);
- if (i != 0 && tTd(41, 2))
- dprintf("multiqueue_cache: \"%s\": Not safe: %s\n",
- QueueDir, errstring(i));
-
- if (chdir(QueueDir) < 0)
+ /* Test path to get warning messages. */
+ if (qn == 0)
{
- syserr("can not chdir(%s)", QueueDir);
- if (tTd(41, 2))
- dprintf("multiqueue_cache: \"%s\": %s\n",
- qpath, errstring(errno));
- ExitStat = EX_CONFIG;
- return;
+ /* XXX qg_runasuid and qg_runasgid for specials? */
+ i = safedirpath(basedir, RunAsUid, RunAsGid, NULL,
+ sff, 0, 0);
+ if (i != 0 && tTd(41, 2))
+ sm_dprintf("multiqueue_cache: \"%s\": Not safe: %s\n",
+ basedir, sm_errstring(i));
}
- if ((dp = opendir(".")) == NULL)
+ if ((dp = opendir(prefix)) == NULL)
{
- syserr("can not opendir(%s)", QueueDir);
+ syserr("can not opendir(%s/%s)", qg->qg_qdir, prefix);
if (tTd(41, 2))
- dprintf("multiqueue_cache: opendir(\"%s\"): %s\n",
- QueueDir, errstring(errno));
+ sm_dprintf("multiqueue_cache: opendir(\"%s/%s\"): %s\n",
+ qg->qg_qdir, prefix,
+ sm_errstring(errno));
ExitStat = EX_CONFIG;
- return;
+ return qn;
}
while ((d = readdir(dp)) != NULL)
{
- if (strncmp(d->d_name, cp, len) != 0)
+ i = strlen(d->d_name);
+ if (i < len || strncmp(d->d_name, cp, len) != 0)
{
if (tTd(41, 5))
- dprintf("multiqueue_cache: \"%s\", skipped\n",
+ sm_dprintf("multiqueue_cache: \"%s\", skipped\n",
d->d_name);
continue;
}
- if (!chkqdir(d->d_name, sff))
+
+ /* Create relative pathname: prefix + local directory */
+ i = sizeof(relpath) - off;
+ if (sm_strlcpy(relpath + off, d->d_name, i) >= i)
+ continue; /* way too long */
+
+ if (!chkqdir(relpath, sff))
continue;
- if (QPaths == NULL)
+ if (qg->qg_qpaths == NULL)
{
- slotsleft = 20;
- QPaths = (QPATHS *)xalloc((sizeof *QPaths) *
- slotsleft);
- NumQueues = 0;
+ slotsleft = INITIAL_SLOTS;
+ qg->qg_qpaths = (QPATHS *)xalloc((sizeof *qg->qg_qpaths) *
+ slotsleft);
+ qg->qg_numqueues = 0;
}
else if (slotsleft < 1)
{
- QPaths = (QPATHS *)xrealloc((char *)QPaths,
- (sizeof *QPaths) *
- (NumQueues + 10));
- if (QPaths == NULL)
+ qg->qg_qpaths = (QPATHS *)sm_realloc((char *)qg->qg_qpaths,
+ (sizeof *qg->qg_qpaths) *
+ (qg->qg_numqueues +
+ ADD_SLOTS));
+ if (qg->qg_qpaths == NULL)
{
(void) closedir(dp);
- return;
+ return qn;
}
- slotsleft += 10;
+ slotsleft += ADD_SLOTS;
}
/* check subdirs */
- QPaths[NumQueues].qp_subdirs = QP_NOSUB;
- (void) snprintf(subdir, sizeof subdir, "%s/%s/%s",
- qpath, d->d_name, "qf");
- if (chkqdir(subdir, sff))
- QPaths[NumQueues].qp_subdirs |= QP_SUBQF;
-
- (void) snprintf(subdir, sizeof subdir, "%s/%s/%s",
- qpath, d->d_name, "df");
- if (chkqdir(subdir, sff))
- QPaths[NumQueues].qp_subdirs |= QP_SUBDF;
-
- (void) snprintf(subdir, sizeof subdir, "%s/%s/%s",
- qpath, d->d_name, "xf");
- if (chkqdir(subdir, sff))
- QPaths[NumQueues].qp_subdirs |= QP_SUBXF;
+ qg->qg_qpaths[qg->qg_numqueues].qp_subdirs = QP_NOSUB;
+
+#define CHKRSUBDIR(name, flag) \
+ (void) sm_strlcpyn(subdir, sizeof subdir, 3, relpath, "/", name); \
+ if (chkqdir(subdir, sff)) \
+ qg->qg_qpaths[qg->qg_numqueues].qp_subdirs |= flag; \
+ else
+
+
+ CHKRSUBDIR("qf", QP_SUBQF);
+ CHKRSUBDIR("df", QP_SUBDF);
+ CHKRSUBDIR("xf", QP_SUBXF);
/* assert(strlen(d->d_name) < MAXPATHLEN - 14) */
/* maybe even - 17 (subdirs) */
- QPaths[NumQueues].qp_name = newstr(d->d_name);
+
+ if (prefix[0] != '.')
+ qg->qg_qpaths[qg->qg_numqueues].qp_name =
+ newstr(relpath);
+ else
+ qg->qg_qpaths[qg->qg_numqueues].qp_name =
+ newstr(d->d_name);
+
if (tTd(41, 2))
- dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n",
- NumQueues, d->d_name,
- QPaths[NumQueues].qp_subdirs);
- NumQueues++;
+ sm_dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n",
+ qg->qg_numqueues, relpath,
+ qg->qg_qpaths[qg->qg_numqueues].qp_subdirs);
+#if SM_CONF_SHM
+ qg->qg_qpaths[qg->qg_numqueues].qp_idx = qn;
+ *phash = hash_q(relpath, *phash);
+#endif /* SM_CONF_SHM */
+ qg->qg_numqueues++;
+ ++qn;
slotsleft--;
}
(void) closedir(dp);
+
+ /* undo damage */
+ *delim = '/';
}
- if (NumQueues == 0)
+ if (qg->qg_numqueues == 0)
{
- if (*cp != '*' && tTd(41, 2))
- dprintf("multiqueue_cache: \"%s\": No wildcard suffix character\n",
- QueueDir);
- QPaths = (QPATHS *)xalloc(sizeof *QPaths);
- QPaths[0].qp_name = newstr(".");
- QPaths[0].qp_subdirs = QP_NOSUB;
- NumQueues = 1;
+ qg->qg_qpaths = (QPATHS *) xalloc(sizeof *qg->qg_qpaths);
/* test path to get warning messages */
- (void) safedirpath(QueueDir, RunAsUid, RunAsGid,
- NULL, sff, 0, 0);
- if (chdir(QueueDir) < 0)
+ i = safedirpath(qpath, RunAsUid, RunAsGid, NULL, sff, 0, 0);
+ if (i == ENOENT)
{
- syserr("can not chdir(%s)", QueueDir);
+ syserr("can not opendir(%s)", qpath);
if (tTd(41, 2))
- dprintf("multiqueue_cache: \"%s\": %s\n",
- QueueDir, errstring(errno));
+ sm_dprintf("multiqueue_cache: opendir(\"%s\"): %s\n",
+ qpath, sm_errstring(i));
ExitStat = EX_CONFIG;
+ return qn;
}
+ qg->qg_qpaths[0].qp_subdirs = QP_NOSUB;
+ qg->qg_numqueues = 1;
+
/* check subdirs */
- (void) snprintf(subdir, sizeof subdir, "%s/qf", QueueDir);
- if (chkqdir(subdir, sff))
- QPaths[0].qp_subdirs |= QP_SUBQF;
+#define CHKSUBDIR(name, flag) \
+ (void) sm_strlcpyn(subdir, sizeof subdir, 3, qg->qg_qdir, "/", name); \
+ if (chkqdir(subdir, sff)) \
+ qg->qg_qpaths[0].qp_subdirs |= flag; \
+ else
- (void) snprintf(subdir, sizeof subdir, "%s/df", QueueDir);
- if (chkqdir(subdir, sff))
- QPaths[0].qp_subdirs |= QP_SUBDF;
+ CHKSUBDIR("qf", QP_SUBQF);
+ CHKSUBDIR("df", QP_SUBDF);
+ CHKSUBDIR("xf", QP_SUBXF);
- (void) snprintf(subdir, sizeof subdir, "%s/xf", QueueDir);
- if (chkqdir(subdir, sff))
- QPaths[0].qp_subdirs |= QP_SUBXF;
+ if (qg->qg_qdir[blen - 1] != '\0' &&
+ qg->qg_qdir[blen] != '\0')
+ {
+ /*
+ ** Copy the last component into qpaths and
+ ** cut off qdir
+ */
+
+ qg->qg_qpaths[0].qp_name = newstr(qg->qg_qdir + blen);
+ qg->qg_qdir[blen - 1] = '\0';
+ }
+ else
+ qg->qg_qpaths[0].qp_name = newstr(".");
+
+#if SM_CONF_SHM
+ qg->qg_qpaths[0].qp_idx = qn;
+ *phash = hash_q(qg->qg_qpaths[0].qp_name, *phash);
+#endif /* SM_CONF_SHM */
+ ++qn;
+ }
+ return qn;
+}
+
+/*
+** FILESYS_FIND -- find entry in FileSys table, or add new one
+**
+** Given the pathname of a directory, determine the file system
+** in which that directory resides, and return a pointer to the
+** entry in the FileSys table that describes the file system.
+** A new entry is added if necessary (and requested).
+** If the directory does not exist, -1 is returned.
+**
+** Parameters:
+** path -- pathname of directory
+** add -- add to structure if not found.
+**
+** Returns:
+** >=0: found: index in file system table
+** <0: some error, i.e.,
+** FSF_TOO_MANY: too many filesystems (-> syserr())
+** FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
+** FSF_NOT_FOUND: not in list
+*/
+
+static short filesys_find __P((char *, bool));
+
+#define FSF_NOT_FOUND (-1)
+#define FSF_STAT_FAIL (-2)
+#define FSF_TOO_MANY (-3)
+
+static short
+filesys_find(path, add)
+ char *path;
+ bool add;
+{
+ struct stat st;
+ short i;
+
+ if (stat(path, &st) < 0)
+ {
+ syserr("cannot stat queue directory %s", path);
+ return FSF_STAT_FAIL;
+ }
+ for (i = 0; i < NumFileSys; ++i)
+ {
+ if (FILE_SYS_DEV(i) == st.st_dev)
+ return i;
+ }
+ if (i >= MAXFILESYS)
+ {
+ syserr("too many queue file systems (%d max)", MAXFILESYS);
+ return FSF_TOO_MANY;
}
+ if (!add)
+ return FSF_NOT_FOUND;
+
+ ++NumFileSys;
+ FILE_SYS_NAME(i) = path;
+ FILE_SYS_DEV(i) = st.st_dev;
+ FILE_SYS_AVAIL(i) = 0;
+ FILE_SYS_BLKSIZE(i) = 1024; /* avoid divide by zero */
+ return i;
}
-# if 0
- /*
+/*
+** FILESYS_SETUP -- set up mapping from queue directories to file systems
+**
+** This data structure is used to efficiently check the amount of
+** free space available in a set of queue directories.
+**
+** Parameters:
+** add -- initialize structure if necessary.
+**
+** Returns:
+** 0: success
+** <0: some error, i.e.,
+** FSF_NOT_FOUND: not in list
+** FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
+** FSF_TOO_MANY: too many filesystems (-> syserr())
+*/
+
+static int filesys_setup __P((bool));
+
+static int
+filesys_setup(add)
+ bool add;
+{
+ int i, j;
+ short fs;
+ int ret;
+
+ ret = 0;
+ for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
+ {
+ for (j = 0; j < Queue[i]->qg_numqueues; ++j)
+ {
+ QPATHS *qp = &Queue[i]->qg_qpaths[j];
+
+ fs = filesys_find(qp->qp_name, add);
+ if (fs >= 0)
+ qp->qp_fsysidx = fs;
+ else
+ qp->qp_fsysidx = 0;
+ if (fs < ret)
+ ret = fs;
+ }
+ }
+ return ret;
+}
+
+/*
+** FILESYS_UPDATE -- update amount of free space on all file systems
+**
+** The FileSys table is used to cache the amount of free space
+** available on all queue directory file systems.
+** This function updates the cached information if it has expired.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Updates FileSys table.
+*/
+
+void
+filesys_update()
+{
+ int i;
+ long avail, blksize;
+ time_t now;
+ static time_t nextupdate = 0;
+
+#if SM_CONF_SHM
+ /* only the daemon updates this structure */
+ if (ShmId != SM_SHM_NO_ID && DaemonPid != CurrentPid)
+ return;
+#endif /* SM_CONF_SHM */
+ now = curtime();
+ if (now < nextupdate)
+ return;
+ nextupdate = now + FILESYS_UPDATE_INTERVAL;
+ for (i = 0; i < NumFileSys; ++i)
+ {
+ FILESYS *fs = &FILE_SYS(i);
+
+ avail = freediskspace(FILE_SYS_NAME(i), &blksize);
+ if (avail < 0 || blksize <= 0)
+ {
+ if (LogLevel > 5)
+ sm_syslog(LOG_ERR, NOQID,
+ "filesys_update failed: %s, fs=%s, avail=%ld, blocksize=%ld",
+ sm_errstring(errno),
+ FILE_SYS_NAME(i), avail, blksize);
+ fs->fs_avail = 0;
+ fs->fs_blksize = 1024; /* avoid divide by zero */
+ nextupdate = now + 2; /* let's do this soon again */
+ }
+ else
+ {
+ fs->fs_avail = avail;
+ fs->fs_blksize = blksize;
+ }
+ }
+}
+
+#if _FFR_ANY_FREE_FS
+/*
+** FILESYS_FREE -- check whether there is at least one fs with enough space.
+**
+** Parameters:
+** fsize -- file size in bytes
+**
+** Returns:
+** true iff there is one fs with more than fsize bytes free.
+*/
+
+bool
+filesys_free(fsize)
+ long fsize;
+{
+ int i;
+
+ if (fsize <= 0)
+ return true;
+ for (i = 0; i < NumFileSys; ++i)
+ {
+ long needed = 0;
+
+ if (FILE_SYS_AVAIL(i) < 0 || FILE_SYS_BLKSIZE(i) <= 0)
+ continue;
+ needed += fsize / FILE_SYS_BLKSIZE(i)
+ + ((fsize % FILE_SYS_BLKSIZE(i)
+ > 0) ? 1 : 0)
+ + MinBlocksFree;
+ if (needed <= FILE_SYS_AVAIL(i))
+ return true;
+ }
+ return false;
+}
+#endif /* _FFR_ANY_FREE_FS */
+
+#if _FFR_CONTROL_MSTAT
+/*
+** DISK_STATUS -- show amount of free space in queue directories
+**
+** Parameters:
+** out -- output file pointer.
+** prefix -- string to output in front of each line.
+**
+** Returns:
+** none.
+*/
+
+void
+disk_status(out, prefix)
+ SM_FILE_T *out;
+ char *prefix;
+{
+ int i;
+ long avail, blksize;
+ long free;
+
+ for (i = 0; i < NumFileSys; ++i)
+ {
+ avail = freediskspace(FILE_SYS_NAME(i), &blksize);
+ if (avail >= 0 && blksize > 0)
+ {
+ free = (long)((double) avail *
+ ((double) blksize / 1024));
+ }
+ else
+ free = -1;
+ (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
+ "%s%d/%s/%ld\r\n",
+ prefix, i,
+ FILE_SYS_NAME(i),
+ free);
+ }
+}
+#endif /* _FFR_CONTROL_MSTAT */
+
+#if SM_CONF_SHM
+/*
+** UPD_QS -- update information about queue when adding/deleting an entry
+**
+** Parameters:
+** e -- envelope.
+** delete -- delete/add entry.
+** avail -- update the space available as well.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Modifies available space in filesystem.
+** Changes number of entries in queue directory.
+*/
+
+void
+upd_qs(e, delete, avail)
+ ENVELOPE *e;
+ bool delete;
+ bool avail;
+{
+ short fidx;
+ int idx;
+ long s;
+
+ if (ShmId == SM_SHM_NO_ID || e == NULL)
+ return;
+ if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
+ return;
+ idx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_idx;
+
+ /* XXX in theory this needs to be protected with a mutex */
+ if (QSHM_ENTRIES(idx) >= 0)
+ {
+ if (delete)
+ --QSHM_ENTRIES(idx);
+ else
+ ++QSHM_ENTRIES(idx);
+ }
+
+ fidx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_fsysidx;
+ if (fidx < 0)
+ return;
+
+ /* update available space also? (might be loseqfile) */
+ if (!avail)
+ return;
+
+ /* convert size to blocks; this causes rounding errors */
+ s = e->e_msgsize / FILE_SYS_BLKSIZE(fidx);
+ if (s == 0)
+ return;
+
+ /* XXX in theory this needs to be protected with a mutex */
+ if (delete)
+ FILE_SYS_AVAIL(fidx) += s;
+ else
+ FILE_SYS_AVAIL(fidx) -= s;
+
+}
+/*
+** INIT_SHM -- initialize shared memory structure
+**
+** Initialize or attach to shared memory segment.
+** Currently it is not a fatal error if this doesn't work.
+** However, it causes us to have a "fallback" storage location
+** for everything that is supposed to be in the shared memory,
+** which makes the code slightly ugly.
+**
+** Parameters:
+** qn -- number of queue directories.
+** owner -- owner of shared memory.
+** hash -- identifies data that is stored in shared memory.
+**
+** Returns:
+** none.
+*/
+
+static void init_shm __P((int, bool, unsigned int));
+
+static void
+init_shm(qn, owner, hash)
+ int qn;
+ bool owner;
+ unsigned int hash;
+{
+ int i;
+
+ PtrFileSys = &FileSys[0];
+ PNumFileSys = &Numfilesys;
+
+ /* This allows us to disable shared memory at runtime. */
+ if (ShmKey != 0)
+ {
+ int count;
+ int save_errno;
+ size_t shms;
+
+ count = 0;
+ shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T);
+ for (;;)
+ {
+ /* XXX: maybe allow read access for group? */
+ Pshm = sm_shmstart(ShmKey, shms, SHM_R|SHM_W, &ShmId,
+ owner);
+ save_errno = errno;
+ if (Pshm != NULL || save_errno != EEXIST)
+ break;
+ if (++count >= 3)
+ break;
+ sleep(count);
+ }
+ if (Pshm != NULL)
+ {
+ int *p;
+
+ p = (int *) Pshm;
+ if (owner)
+ {
+ *p = (int) shms;
+ *((pid_t *) SHM_OFF_PID(Pshm)) = CurrentPid;
+ p = (int *) SHM_OFF_TAG(Pshm);
+ *p = hash;
+ }
+ else
+ {
+ if (*p != (int) shms)
+ {
+ save_errno = EINVAL;
+ cleanup_shm(false);
+ goto error;
+ }
+ p = (int *) SHM_OFF_TAG(Pshm);
+ if (*p != (int) hash)
+ {
+ save_errno = EINVAL;
+ cleanup_shm(false);
+ goto error;
+ }
+
+ /*
+ ** XXX how to check the pid?
+ ** Read it from the pid-file? That does
+ ** not need to exist.
+ ** We could disable shm if we can't confirm
+ ** that it is the right one.
+ */
+ }
+
+ PtrFileSys = (FILESYS *) OFF_FILE_SYS(Pshm);
+ PNumFileSys = (int *) OFF_NUM_FILE_SYS(Pshm);
+ QShm = (QUEUE_SHM_T *) OFF_QUEUE_SHM(Pshm);
+ PRSATmpCnt = (int *) OFF_RSA_TMP_CNT(Pshm);
+ *PRSATmpCnt = 0;
+ if (owner)
+ {
+ /* initialize values in shared memory */
+ NumFileSys = 0;
+ for (i = 0; i < qn; i++)
+ QShm[i].qs_entries = -1;
+ }
+ return;
+ }
+ error:
+ if (LogLevel > (owner ? 8 : 11))
+ {
+ sm_syslog(owner ? LOG_ERR : LOG_NOTICE, NOQID,
+ "can't %s shared memory, key=%ld: %s",
+ owner ? "initialize" : "attach to",
+ (long) ShmKey, sm_errstring(save_errno));
+ }
+ }
+}
+#endif /* SM_CONF_SHM */
+
+/*
+** SETUP_QUEUES -- setup all queue groups
+**
+** Parameters:
+** owner -- owner of shared memory.
+**
+** Returns:
+** none.
+**
+#if SM_CONF_SHM
+** Side Effects:
+** attaches shared memory.
+#endif * SM_CONF_SHM *
+*/
+
+void
+setup_queues(owner)
+ bool owner;
+{
+ int i, qn, len;
+ unsigned int hashval;
+ char basedir[MAXPATHLEN];
+ struct stat st;
+
+ /*
+ ** Determine basedir for all queue directories.
+ ** All queue directories must be (first level) subdirectories
+ ** of the basedir. The basedir is the QueueDir
+ ** without wildcards, but with trailing /
+ */
+
+ hashval = 0;
+ errno = 0;
+ len = sm_strlcpy(basedir, QueueDir, sizeof basedir);
+ if (len >= sizeof basedir)
+ {
+ syserr("QueueDirectory: path too long: %d, max %d",
+ len, (int) sizeof basedir);
+ ExitStat = EX_CONFIG;
+ return;
+ }
+ SM_ASSERT(len > 0);
+ if (basedir[len - 1] == '*')
+ {
+ char *cp;
+
+ cp = SM_LAST_DIR_DELIM(basedir);
+ if (cp == NULL)
+ {
+ syserr("QueueDirectory: can not wildcard relative path \"%s\"",
+ QueueDir);
+ if (tTd(41, 2))
+ sm_dprintf("setup_queues: \"%s\": Can not wildcard relative path.\n",
+ QueueDir);
+ ExitStat = EX_CONFIG;
+ return;
+ }
+
+ /* cut off wildcard pattern */
+ *++cp = '\0';
+ len = cp - basedir;
+ }
+ else if (!SM_IS_DIR_DELIM(basedir[len - 1]))
+ {
+ /* append trailing slash since it is a directory */
+ basedir[len] = '/';
+ basedir[++len] = '\0';
+ }
+
+ /* len counts up to the last directory delimiter */
+ SM_ASSERT(basedir[len - 1] == '/');
+
+ if (chdir(basedir) < 0)
+ {
+ int save_errno = errno;
+
+ syserr("can not chdir(%s)", basedir);
+ if (save_errno == EACCES)
+ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
+ "Program mode requires special privileges, e.g., root or TrustedUser.\n");
+ if (tTd(41, 2))
+ sm_dprintf("setup_queues: \"%s\": %s\n",
+ basedir, sm_errstring(errno));
+ ExitStat = EX_CONFIG;
+ return;
+ }
+#if SM_CONF_SHM
+ hashval = hash_q(basedir, hashval);
+#endif /* SM_CONF_SHM */
+
+ /* initialize map for queue runs */
+ clrbitmap(DoQueueRun);
+
+
+ if (UseMSP && OpMode != MD_TEST)
+ {
+ long sff = SFF_CREAT;
+
+ if (stat(".", &st) < 0)
+ {
+ syserr("can not stat(%s)", basedir);
+ if (tTd(41, 2))
+ sm_dprintf("setup_queues: \"%s\": %s\n",
+ basedir, sm_errstring(errno));
+ ExitStat = EX_CONFIG;
+ return;
+ }
+ if (RunAsUid == 0)
+ sff |= SFF_ROOTOK;
+
+ /*
+ ** Check queue directory permissions.
+ ** Can we write to a group writable queue directory?
+ */
+
+ if (bitset(S_IWGRP, QueueFileMode) &&
+ bitset(S_IWGRP, st.st_mode) &&
+ safefile(" ", RunAsUid, RunAsGid, RunAsUserName, sff,
+ QueueFileMode, NULL) != 0)
+ {
+ syserr("can not write to queue directory %s (RunAsGid=%d, required=%d)",
+ basedir, (int) RunAsGid, (int) st.st_gid);
+ }
+ if (bitset(S_IWOTH|S_IXOTH, st.st_mode))
+ {
+#if _FFR_MSP_PARANOIA
+ syserr("dangerous permissions=%o on queue directory %s",
+ (int) st.st_mode, basedir);
+#else /* _FFR_MSP_PARANOIA */
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, NOQID,
+ "dangerous permissions=%o on queue directory %s",
+ (int) st.st_mode, basedir);
+#endif /* _FFR_MSP_PARANOIA */
+ }
+#if _FFR_MSP_PARANOIA
+ if (NumQueue > 1)
+ syserr("can not use multiple queues for MSP");
+#endif /* _FFR_MSP_PARANOIA */
+ }
+
+ /* initial number of queue directories */
+ qn = 0;
+ for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
+ qn = multiqueue_cache(basedir, len, Queue[i], qn, &hashval);
+
+#if SM_CONF_SHM
+ init_shm(qn, owner, hashval);
+ i = filesys_setup(owner || ShmId == SM_SHM_NO_ID);
+ if (i == FSF_NOT_FOUND)
+ {
+ /*
+ ** We didn't get the right filesystem data
+ ** This may happen if we don't have the right shared memory.
+ ** So let's do this without shared memory.
+ */
+
+ SM_ASSERT(!owner);
+ cleanup_shm(false); /* release shared memory */
+ i = filesys_setup(false);
+ if (i < 0)
+ syserr("filesys_setup failed twice, result=%d", i);
+ else if (LogLevel > 8)
+ sm_syslog(LOG_WARNING, NOQID,
+ "shared memory does not contain expected data, ignored");
+ }
+#else /* SM_CONF_SHM */
+ i = filesys_setup(true);
+#endif /* SM_CONF_SHM */
+ if (i < 0)
+ ExitStat = EX_CONFIG;
+}
+
+#if SM_CONF_SHM
+/*
+** CLEANUP_SHM -- do some cleanup work for shared memory etc
+**
+** Parameters:
+** owner -- owner of shared memory?
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** detaches shared memory.
+*/
+
+void
+cleanup_shm(owner)
+ bool owner;
+{
+ if (ShmId != SM_SHM_NO_ID)
+ {
+ if (sm_shmstop(Pshm, ShmId, owner) < 0 && LogLevel > 8)
+ sm_syslog(LOG_INFO, NOQID, "sh_shmstop failed=%s",
+ sm_errstring(errno));
+ Pshm = NULL;
+ ShmId = SM_SHM_NO_ID;
+ }
+}
+#endif /* SM_CONF_SHM */
+
+/*
+** CLEANUP_QUEUES -- do some cleanup work for queues
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+*/
+
+void
+cleanup_queues()
+{
+ sync_queue_time();
+}
+/*
+** SET_DEF_QUEUEVAL -- set default values for a queue group.
+**
+** Parameters:
+** qg -- queue group
+** all -- set all values (true for default group)?
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** sets default values for the queue group.
+*/
+
+void
+set_def_queueval(qg, all)
+ QUEUEGRP *qg;
+ bool all;
+{
+ if (bitnset(QD_DEFINED, qg->qg_flags))
+ return;
+ if (all)
+ qg->qg_qdir = QueueDir;
+#if 0
+ qg->qg_sortorder = QueueSortOrder;
+#endif /* 0 */
+ qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1;
+ qg->qg_nice = NiceQueueRun;
+}
+/*
+** MAKEQUEUE -- define a new queue.
+**
+** Parameters:
+** line -- description of queue. This is in labeled fields.
+** The fields are:
+** F -- the flags associated with the queue
+** I -- the interval between running the queue
+** J -- the maximum # of jobs in work list
+** [M -- the maximum # of jobs in a queue run]
+** N -- the niceness at which to run
+** P -- the path to the queue
+** S -- the queue sorting order
+** R -- number of parallel queue runners
+** r -- max recipients per envelope
+** The first word is the canonical name of the queue.
+** qdef -- this is a 'Q' definition from .cf
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** enters the queue into the queue table.
+*/
+
+void
+makequeue(line, qdef)
+ char *line;
+ bool qdef;
+{
+ register char *p;
+ register QUEUEGRP *qg;
+ register STAB *s;
+ int i;
+ char fcode;
+
+ /* allocate a queue and set up defaults */
+ qg = (QUEUEGRP *) xalloc(sizeof *qg);
+ memset((char *) qg, '\0', sizeof *qg);
+
+ if (line[0] == '\0')
+ {
+ syserr("name required for queue");
+ return;
+ }
+
+ /* collect the queue name */
+ for (p = line;
+ *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
+ p++)
+ continue;
+ if (*p != '\0')
+ *p++ = '\0';
+ qg->qg_name = newstr(line);
+
+ /* set default values, can be overridden below */
+ set_def_queueval(qg, false);
+
+ /* now scan through and assign info from the fields */
+ while (*p != '\0')
+ {
+ auto char *delimptr;
+
+ while (*p != '\0' &&
+ (*p == ',' || (isascii(*p) && isspace(*p))))
+ p++;
+
+ /* p now points to field code */
+ fcode = *p;
+ while (*p != '\0' && *p != '=' && *p != ',')
+ p++;
+ if (*p++ != '=')
+ {
+ syserr("queue %s: `=' expected", qg->qg_name);
+ return;
+ }
+ while (isascii(*p) && isspace(*p))
+ p++;
+
+ /* p now points to the field body */
+ p = munchstring(p, &delimptr, ',');
+
+ /* install the field into the queue struct */
+ switch (fcode)
+ {
+ case 'P': /* pathname */
+ if (*p == '\0')
+ syserr("queue %s: empty path name",
+ qg->qg_name);
+ else
+ qg->qg_qdir = newstr(p);
+ break;
+
+ case 'F': /* flags */
+ for (; *p != '\0'; p++)
+ if (!(isascii(*p) && isspace(*p)))
+ setbitn(*p, qg->qg_flags);
+ break;
+
+ /*
+ ** Do we need two intervals here:
+ ** One for persistent queue runners,
+ ** one for "normal" queue runs?
+ */
+
+ case 'I': /* interval between running the queue */
+ qg->qg_queueintvl = convtime(p, 'm');
+ break;
+
+ case 'N': /* run niceness */
+ qg->qg_nice = atoi(p);
+ break;
+
+ case 'R': /* maximum # of runners for the group */
+ i = atoi(p);
+
+ /* can't have more runners than allowed total */
+ if (MaxQueueChildren > 0 && i > MaxQueueChildren)
+ {
+ qg->qg_maxqrun = MaxQueueChildren;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Q=%s: R=%d exceeds MaxQueueChildren=%d, set to MaxQueueChildren\n",
+ qg->qg_name, i,
+ MaxQueueChildren);
+ }
+ else
+ qg->qg_maxqrun = i;
+ break;
+
+ case 'J': /* maximum # of jobs in work list */
+ qg->qg_maxlist = atoi(p);
+ break;
+
+ case 'r': /* max recipients per envelope */
+ qg->qg_maxrcpt = atoi(p);
+ break;
+
+#if 0
+ case 'S': /* queue sorting order */
+ switch (*p)
+ {
+ case 'h': /* Host first */
+ case 'H':
+ qg->qg_sortorder = QSO_BYHOST;
+ break;
+
+ case 'p': /* Priority order */
+ case 'P':
+ qg->qg_sortorder = QSO_BYPRIORITY;
+ break;
+
+ case 't': /* Submission time */
+ case 'T':
+ qg->qg_sortorder = QSO_BYTIME;
+ break;
+
+ case 'f': /* File name */
+ case 'F':
+ qg->qg_sortorder = QSO_BYFILENAME;
+ break;
+
+ case 'm': /* Modification time */
+ case 'M':
+ qgrp->qg_sortorder = QSO_BYMODTIME;
+ break;
+
+ default:
+ syserr("Invalid queue sort order \"%s\"", p);
+ }
+ break;
+#endif /* 0 */
+
+ default:
+ syserr("Q%s: unknown queue equate %c=",
+ qg->qg_name, fcode);
+ break;
+ }
+
+ p = delimptr;
+ }
+
+#if !HASNICE
+ if (qg->qg_nice != NiceQueueRun)
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Q%s: Warning: N= set on system that doesn't support nice()\n",
+ qg->qg_name);
+ }
+#endif /* !HASNICE */
+
+ /* do some rationality checking */
+ if (NumQueue >= MAXQUEUEGROUPS)
+ {
+ syserr("too many queue groups defined (%d max)",
+ MAXQUEUEGROUPS);
+ return;
+ }
+
+ if (qg->qg_qdir == NULL)
+ {
+ if (QueueDir == NULL || *QueueDir == '\0')
+ {
+ syserr("QueueDir must be defined before queue groups");
+ return;
+ }
+ qg->qg_qdir = newstr(QueueDir);
+ }
+
+ if (qg->qg_maxqrun > 1 && !bitnset(QD_FORK, qg->qg_flags))
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: Q=%s: R=%d: multiple queue runners specified\n\tbut flag '%c' is not set\n",
+ qg->qg_name, qg->qg_maxqrun, QD_FORK);
+ }
+
+ /* enter the queue into the symbol table */
+ if (tTd(37, 8))
+ sm_syslog(LOG_INFO, NOQID,
+ "Adding %s to stab, path: %s", qg->qg_name,
+ qg->qg_qdir);
+ s = stab(qg->qg_name, ST_QUEUE, ST_ENTER);
+ if (s->s_quegrp != NULL)
+ {
+ i = s->s_quegrp->qg_index;
+
+ /* XXX what about the pointers inside this struct? */
+ sm_free(s->s_quegrp); /* XXX */
+ }
+ else
+ i = NumQueue++;
+ Queue[i] = s->s_quegrp = qg;
+ qg->qg_index = i;
+
+ /* set default value for max queue runners */
+ if (qg->qg_maxqrun < 0)
+ {
+ if (MaxRunnersPerQueue > 0)
+ qg->qg_maxqrun = MaxRunnersPerQueue;
+ else
+ qg->qg_maxqrun = 1;
+ }
+ if (qdef)
+ setbitn(QD_DEFINED, qg->qg_flags);
+}
+#if 0
+/*
** HASHFQN -- calculate a hash value for a fully qualified host name
**
** Arguments:
@@ -3441,7 +6917,6 @@ hashfqn(fqn, buckets)
{
register char *p;
register int h = 0, hash, cnt;
-# define WATERINC (1000)
if (fqn == NULL)
return -1;
@@ -3465,10 +6940,10 @@ hashfqn(fqn, buckets)
return hash;
}
-# endif /* 0 */
+#endif /* 0 */
-# if _FFR_QUEUEDELAY
- /*
+#if _FFR_QUEUEDELAY
+/*
** QUEUEDELAY -- compute queue delay time
**
** Parameters:
@@ -3503,5 +6978,1330 @@ queuedelay(e)
qd = MinQueueAge;
return qd;
}
-# endif /* _FFR_QUEUEDELAY */
-#endif /* QUEUE */
+#endif /* _FFR_QUEUEDELAY */
+
+/*
+** A structure for sorting Queue according to maxqrun without
+** screwing up Queue itself.
+*/
+
+struct sortqgrp
+{
+ int sg_idx; /* original index */
+ int sg_maxqrun; /* max queue runners */
+};
+typedef struct sortqgrp SORTQGRP_T;
+static int cmpidx __P((const void *, const void *));
+
+static int
+cmpidx(a, b)
+ const void *a;
+ const void *b;
+{
+ /* The sort is highest to lowest, so the comparison is reversed */
+ if (((SORTQGRP_T *)a)->sg_maxqrun < ((SORTQGRP_T *)b)->sg_maxqrun)
+ return 1;
+ else if (((SORTQGRP_T *)a)->sg_maxqrun > ((SORTQGRP_T *)b)->sg_maxqrun)
+ return -1;
+ else
+ return 0;
+}
+
+/*
+** MAKEWORKGROUP -- balance queue groups into work groups per MaxQueueChildren
+**
+** Take the now defined queue groups and assign them to work groups.
+** This is done to balance out the number of concurrently active
+** queue runners such that MaxQueueChildren is not exceeded. This may
+** result in more than one queue group per work group. In such a case
+** the number of running queue groups in that work group will have no
+** more than the work group maximum number of runners (a "fair" portion
+** of MaxQueueRunners). All queue groups within a work group will get a
+** chance at running.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** nothing.
+**
+** Side Effects:
+** Sets up WorkGrp structure.
+*/
+
+void
+makeworkgroups()
+{
+ int i, j, total_runners = 0;
+ int dir;
+ SORTQGRP_T si[MAXQUEUEGROUPS + 1];
+
+ if (NumQueue == 1 && strcmp(Queue[0]->qg_name, "mqueue") == 0)
+ {
+ /*
+ ** There is only the "mqueue" queue group (a default)
+ ** containing all of the queues. We want to provide to
+ ** this queue group the maximum allowable queue runners.
+ ** To match older behavior (8.10/8.11) we'll try for
+ ** 1 runner per queue capping it at MaxQueueChildren.
+ ** So if there are N queues, then there will be N runners
+ ** for the "mqueue" queue group (where N is kept less than
+ ** MaxQueueChildren).
+ */
+
+ NumWorkGroups = 1;
+ WorkGrp[0].wg_numqgrp = 1;
+ WorkGrp[0].wg_qgs = (QUEUEGRP **) xalloc(sizeof(QUEUEGRP *));
+ WorkGrp[0].wg_qgs[0] = Queue[0];
+ if (MaxQueueChildren > 0 &&
+ Queue[0]->qg_numqueues > MaxQueueChildren)
+ WorkGrp[0].wg_runners = MaxQueueChildren;
+ else
+ WorkGrp[0].wg_runners = Queue[0]->qg_numqueues;
+
+ Queue[0]->qg_wgrp = 0;
+
+ /* can't have more runners than allowed total */
+ if (MaxQueueChildren > 0 &&
+ Queue[0]->qg_maxqrun > MaxQueueChildren)
+ Queue[0]->qg_maxqrun = MaxQueueChildren;
+ WorkGrp[0].wg_maxact = Queue[0]->qg_maxqrun;
+ WorkGrp[0].wg_lowqintvl = Queue[0]->qg_queueintvl;
+ return;
+ }
+
+ for (i = 0; i < NumQueue; i++)
+ {
+ si[i].sg_maxqrun = Queue[i]->qg_maxqrun;
+ si[i].sg_idx = i;
+ }
+ qsort(si, NumQueue, sizeof(si[0]), cmpidx);
+
+ NumWorkGroups = 0;
+ for (i = 0; i < NumQueue; i++)
+ {
+ total_runners += si[i].sg_maxqrun;
+ if (MaxQueueChildren <= 0 || total_runners <= MaxQueueChildren)
+ NumWorkGroups++;
+ else
+ break;
+ }
+
+ if (NumWorkGroups < 1)
+ NumWorkGroups = 1; /* gotta have one at least */
+ else if (NumWorkGroups > MAXWORKGROUPS)
+ NumWorkGroups = MAXWORKGROUPS; /* the limit */
+
+ /*
+ ** We now know the number of work groups to pack the queue groups
+ ** into. The queue groups in 'Queue' are sorted from highest
+ ** to lowest for the number of runners per queue group.
+ ** We put the queue groups with the largest number of runners
+ ** into work groups first. Then the smaller ones are fitted in
+ ** where it looks best.
+ */
+
+ j = 0;
+ dir = 1;
+ for (i = 0; i < NumQueue; i++)
+ {
+ /* a to-and-fro packing scheme, continue from last position */
+ if (j >= NumWorkGroups)
+ {
+ dir = -1;
+ j = NumWorkGroups - 1;
+ }
+ else if (j < 0)
+ {
+ j = 0;
+ dir = 1;
+ }
+
+ WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs,
+ sizeof(QUEUEGRP *) *
+ (WorkGrp[j].wg_numqgrp + 1));
+ if (WorkGrp[j].wg_qgs == NULL)
+ {
+ syserr("@cannot allocate memory for work queues, need %d bytes",
+ (int) (sizeof(QUEUEGRP *) *
+ (WorkGrp[j].wg_numqgrp + 1)));
+ }
+
+ WorkGrp[j].wg_qgs[WorkGrp[j].wg_numqgrp] = Queue[si[i].sg_idx];
+ WorkGrp[j].wg_numqgrp++;
+ WorkGrp[j].wg_runners += Queue[i]->qg_maxqrun;
+ Queue[si[i].sg_idx]->qg_wgrp = j;
+
+ if (WorkGrp[j].wg_maxact == 0)
+ {
+ /* can't have more runners than allowed total */
+ if (MaxQueueChildren > 0 &&
+ Queue[i]->qg_maxqrun > MaxQueueChildren)
+ Queue[i]->qg_maxqrun = MaxQueueChildren;
+ WorkGrp[j].wg_maxact = Queue[i]->qg_maxqrun;
+ }
+
+ /*
+ ** XXX: must wg_lowqintvl be the GCD?
+ ** qg1: 2m, qg2: 3m, minimum: 2m, when do queue runs for
+ ** qg2 occur?
+ */
+
+ /* keep track of the lowest interval for a persistent runner */
+ if (Queue[si[i].sg_idx]->qg_queueintvl > 0 &&
+ WorkGrp[j].wg_lowqintvl < Queue[si[i].sg_idx]->qg_queueintvl)
+ WorkGrp[j].wg_lowqintvl = Queue[si[i].sg_idx]->qg_queueintvl;
+ j += dir;
+ }
+ if (tTd(41, 9))
+ {
+ for (i = 0; i < NumWorkGroups; i++)
+ {
+ sm_dprintf("Workgroup[%d]=", i);
+ for (j = 0; j < WorkGrp[i].wg_numqgrp; j++)
+ {
+ sm_dprintf("%s, ",
+ WorkGrp[i].wg_qgs[j]->qg_name);
+ }
+ sm_dprintf("\n");
+ }
+ }
+}
+
+/*
+** DUP_DF -- duplicate envelope data file
+**
+** Copy the data file from the 'old' envelope to the 'new' envelope
+** in the most efficient way possible.
+**
+** Create a hard link from the 'old' data file to the 'new' data file.
+** If the old and new queue directories are on different file systems,
+** then the new data file link is created in the old queue directory,
+** and the new queue file will contain a 'd' record pointing to the
+** directory containing the new data file.
+**
+** Parameters:
+** old -- old envelope.
+** new -- new envelope.
+**
+** Results:
+** Returns true on success, false on failure.
+**
+** Side Effects:
+** On success, the new data file is created.
+** On fatal failure, EF_FATALERRS is set in old->e_flags.
+*/
+
+static bool dup_df __P((ENVELOPE *, ENVELOPE *));
+
+static bool
+dup_df(old, new)
+ ENVELOPE *old;
+ ENVELOPE *new;
+{
+ int ofs, nfs, r;
+ char opath[MAXPATHLEN];
+ char npath[MAXPATHLEN];
+
+ SM_REQUIRE(bitset(EF_HAS_DF, old->e_flags));
+ SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir));
+ SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir));
+
+ (void) sm_strlcpy(opath, queuename(old, DATAFL_LETTER), sizeof opath);
+ (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof npath);
+
+ if (old->e_dfp != NULL)
+ {
+ r = sm_io_setinfo(old->e_dfp, SM_BF_COMMIT, NULL);
+ if (r < 0 && errno != EINVAL)
+ {
+ syserr("@can't commit %s", opath);
+ old->e_flags |= EF_FATALERRS;
+ return false;
+ }
+ }
+
+ /*
+ ** Attempt to create a hard link, if we think both old and new
+ ** are on the same file system, otherwise copy the file.
+ **
+ ** Don't waste time attempting a hard link unless old and new
+ ** are on the same file system.
+ */
+
+ ofs = Queue[old->e_qgrp]->qg_qpaths[old->e_qdir].qp_fsysidx;
+ nfs = Queue[new->e_qgrp]->qg_qpaths[new->e_qdir].qp_fsysidx;
+ if (FILE_SYS_DEV(ofs) == FILE_SYS_DEV(nfs))
+ {
+ if (link(opath, npath) == 0)
+ {
+ new->e_flags |= EF_HAS_DF;
+ SYNC_DIR(npath, true);
+ return true;
+ }
+ goto error;
+ }
+
+ /*
+ ** Can't link across queue directories, so try to create a hard
+ ** link in the same queue directory as the old df file.
+ ** The qf file will refer to the new df file using a 'd' record.
+ */
+
+ new->e_dfqgrp = old->e_dfqgrp;
+ new->e_dfqdir = old->e_dfqdir;
+ (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof npath);
+ if (link(opath, npath) == 0)
+ {
+ new->e_flags |= EF_HAS_DF;
+ SYNC_DIR(npath, true);
+ return true;
+ }
+
+ error:
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, old->e_id,
+ "dup_df: can't link %s to %s, error=%s, envelope splitting failed",
+ opath, npath, sm_errstring(errno));
+ return false;
+}
+
+/*
+** SPLIT_ENV -- Allocate a new envelope based on a given envelope.
+**
+** Parameters:
+** e -- envelope.
+** sendqueue -- sendqueue for new envelope.
+** qgrp -- index of queue group.
+** qdir -- queue directory.
+**
+** Results:
+** new envelope.
+**
+*/
+
+static ENVELOPE *split_env __P((ENVELOPE *, ADDRESS *, int, int));
+
+static ENVELOPE *
+split_env(e, sendqueue, qgrp, qdir)
+ ENVELOPE *e;
+ ADDRESS *sendqueue;
+ int qgrp;
+ int qdir;
+{
+ ENVELOPE *ee;
+
+ ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, sizeof *ee);
+ STRUCTCOPY(*e, *ee);
+ ee->e_message = NULL; /* XXX use original message? */
+ ee->e_id = NULL;
+ assign_queueid(ee);
+ ee->e_sendqueue = sendqueue;
+ ee->e_flags &= ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS
+ |EF_SENDRECEIPT|EF_RET_PARAM|EF_HAS_DF);
+ ee->e_flags |= EF_NORECEIPT; /* XXX really? */
+ ee->e_from.q_state = QS_SENDER;
+ ee->e_dfp = NULL;
+ ee->e_lockfp = NULL;
+ if (e->e_xfp != NULL)
+ ee->e_xfp = sm_io_dup(e->e_xfp);
+ ee->e_qgrp = ee->e_dfqgrp = qgrp;
+ ee->e_qdir = ee->e_dfqdir = qdir;
+ ee->e_errormode = EM_MAIL;
+ ee->e_statmsg = NULL;
+#if _FFR_QUARANTINE
+ if (e->e_quarmsg != NULL)
+ ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
+ e->e_quarmsg);
+#endif /* _FFR_QUARANTINE */
+
+ /*
+ ** XXX Not sure if this copying is necessary.
+ ** sendall() does this copying, but I don't know if that is
+ ** because of the storage management discipline we were using
+ ** before rpools were introduced, or if it is because these lists
+ ** can be modified later.
+ */
+
+ ee->e_header = copyheader(e->e_header, ee->e_rpool);
+ ee->e_errorqueue = copyqueue(e->e_errorqueue, ee->e_rpool);
+
+ return ee;
+}
+
+/* return values from split functions, check also below! */
+#define SM_SPLIT_FAIL (0)
+#define SM_SPLIT_NONE (1)
+#define SM_SPLIT_NEW(n) (1 + (n))
+
+/*
+** SPLIT_ACROSS_QUEUE_GROUPS
+**
+** This function splits an envelope across multiple queue groups
+** based on the queue group of each recipient.
+**
+** Parameters:
+** e -- envelope.
+**
+** Results:
+** SM_SPLIT_FAIL on failure
+** SM_SPLIT_NONE if no splitting occurred,
+** or 1 + the number of additional envelopes created.
+**
+** Side Effects:
+** On success, e->e_sibling points to a list of zero or more
+** additional envelopes, and the associated data files exist
+** on disk. But the queue files are not created.
+**
+** On failure, e->e_sibling is not changed.
+** The order of recipients in e->e_sendqueue is permuted.
+** Abandoned data files for additional envelopes that failed
+** to be created may exist on disk.
+*/
+
+static int q_qgrp_compare __P((const void *, const void *));
+static int e_filesys_compare __P((const void *, const void *));
+
+static int
+q_qgrp_compare(p1, p2)
+ const void *p1;
+ const void *p2;
+{
+ ADDRESS **pq1 = (ADDRESS **) p1;
+ ADDRESS **pq2 = (ADDRESS **) p2;
+
+ return (*pq1)->q_qgrp - (*pq2)->q_qgrp;
+}
+
+static int
+e_filesys_compare(p1, p2)
+ const void *p1;
+ const void *p2;
+{
+ ENVELOPE **pe1 = (ENVELOPE **) p1;
+ ENVELOPE **pe2 = (ENVELOPE **) p2;
+ int fs1, fs2;
+
+ fs1 = Queue[(*pe1)->e_qgrp]->qg_qpaths[(*pe1)->e_qdir].qp_fsysidx;
+ fs2 = Queue[(*pe2)->e_qgrp]->qg_qpaths[(*pe2)->e_qdir].qp_fsysidx;
+ if (FILE_SYS_DEV(fs1) < FILE_SYS_DEV(fs2))
+ return -1;
+ if (FILE_SYS_DEV(fs1) > FILE_SYS_DEV(fs2))
+ return 1;
+ return 0;
+}
+
+static int
+split_across_queue_groups(e)
+ ENVELOPE *e;
+{
+ int naddrs, nsplits, i;
+ char **pvp;
+ ADDRESS *q, **addrs;
+ ENVELOPE *ee, *es;
+ ENVELOPE *splits[MAXQUEUEGROUPS];
+ char pvpbuf[PSBUFSIZE];
+
+ SM_REQUIRE(ISVALIDQGRP(e->e_qgrp));
+
+ /* Count addresses and assign queue groups. */
+ naddrs = 0;
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (QS_IS_DEAD(q->q_state))
+ continue;
+ ++naddrs;
+
+ /* bad addresses and those already sent stay put */
+ if (QS_IS_BADADDR(q->q_state) ||
+ QS_IS_SENT(q->q_state))
+ q->q_qgrp = e->e_qgrp;
+ else if (!ISVALIDQGRP(q->q_qgrp))
+ {
+ /* call ruleset which should return a queue group */
+ i = rscap(RS_QUEUEGROUP, q->q_user, NULL, e, &pvp,
+ pvpbuf, sizeof(pvpbuf));
+ if (i == EX_OK &&
+ pvp != NULL && pvp[0] != NULL &&
+ (pvp[0][0] & 0377) == CANONNET &&
+ pvp[1] != NULL && pvp[1][0] != '\0')
+ {
+ i = name2qid(pvp[1]);
+ if (ISVALIDQGRP(i))
+ {
+ q->q_qgrp = i;
+ if (tTd(20, 4))
+ sm_syslog(LOG_INFO, NOQID,
+ "queue group name %s -> %d",
+ pvp[1], i);
+ continue;
+ }
+ else if (LogLevel > 10)
+ sm_syslog(LOG_INFO, NOQID,
+ "can't find queue group name %s, selection ignored",
+ pvp[1]);
+ }
+ if (q->q_mailer != NULL &&
+ ISVALIDQGRP(q->q_mailer->m_qgrp))
+ q->q_qgrp = q->q_mailer->m_qgrp;
+ else
+ q->q_qgrp = 0;
+ }
+ }
+
+ /* only one address? nothing to split. */
+ if (naddrs <= 1)
+ return SM_SPLIT_NONE;
+
+ /* sort the addresses by queue group */
+ addrs = sm_rpool_malloc_x(e->e_rpool, naddrs * sizeof(ADDRESS *));
+ for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (QS_IS_DEAD(q->q_state))
+ continue;
+ addrs[i++] = q;
+ }
+ qsort(addrs, naddrs, sizeof(ADDRESS *), q_qgrp_compare);
+
+ /* split into multiple envelopes, by queue group */
+ nsplits = 0;
+ es = NULL;
+ e->e_sendqueue = NULL;
+ for (i = 0; i < naddrs; ++i)
+ {
+ if (i == naddrs - 1 || addrs[i]->q_qgrp != addrs[i + 1]->q_qgrp)
+ addrs[i]->q_next = NULL;
+ else
+ addrs[i]->q_next = addrs[i + 1];
+
+ /* same queue group as original envelope? */
+ if (addrs[i]->q_qgrp == e->e_qgrp)
+ {
+ if (e->e_sendqueue == NULL)
+ e->e_sendqueue = addrs[i];
+ continue;
+ }
+
+ /* different queue group than original envelope */
+ if (es == NULL || addrs[i]->q_qgrp != es->e_qgrp)
+ {
+ ee = split_env(e, addrs[i], addrs[i]->q_qgrp, NOQDIR);
+ es = ee;
+ splits[nsplits++] = ee;
+ }
+ }
+
+ /* no splits? return right now. */
+ if (nsplits <= 0)
+ return SM_SPLIT_NONE;
+
+ /* assign a queue directory to each additional envelope */
+ for (i = 0; i < nsplits; ++i)
+ {
+ es = splits[i];
+#if 0
+ es->e_qdir = pickqdir(Queue[es->e_qgrp], es->e_msgsize, es);
+#endif /* 0 */
+ if (!setnewqueue(es))
+ goto failure;
+ }
+
+ /* sort the additional envelopes by queue file system */
+ qsort(splits, nsplits, sizeof(ENVELOPE *), e_filesys_compare);
+
+ /* create data files for each additional envelope */
+ if (!dup_df(e, splits[0]))
+ {
+ i = 0;
+ goto failure;
+ }
+ for (i = 1; i < nsplits; ++i)
+ {
+ /* copy or link to the previous data file */
+ if (!dup_df(splits[i - 1], splits[i]))
+ goto failure;
+ }
+
+ /* success: prepend the new envelopes to the e->e_sibling list */
+ for (i = 0; i < nsplits; ++i)
+ {
+ es = splits[i];
+ es->e_sibling = e->e_sibling;
+ e->e_sibling = es;
+ }
+ return SM_SPLIT_NEW(nsplits);
+
+ /* failure: clean up */
+ failure:
+ if (i > 0)
+ {
+ int j;
+
+ for (j = 0; j < i; j++)
+ (void) unlink(queuename(splits[j], DATAFL_LETTER));
+ }
+ e->e_sendqueue = addrs[0];
+ for (i = 0; i < naddrs - 1; ++i)
+ addrs[i]->q_next = addrs[i + 1];
+ addrs[naddrs - 1]->q_next = NULL;
+ return SM_SPLIT_FAIL;
+}
+
+/*
+** SPLIT_WITHIN_QUEUE
+**
+** Split an envelope with multiple recipients into several
+** envelopes within the same queue directory, if the number of
+** recipients exceeds the limit for the queue group.
+**
+** Parameters:
+** e -- envelope.
+**
+** Results:
+** SM_SPLIT_FAIL on failure
+** SM_SPLIT_NONE if no splitting occurred,
+** or 1 + the number of additional envelopes created.
+*/
+
+#define SPLIT_LOG_LEVEL 8
+
+static int split_within_queue __P((ENVELOPE *));
+
+static int
+split_within_queue(e)
+ ENVELOPE *e;
+{
+ int maxrcpt, nrcpt, ndead, nsplit, i;
+ int j, l;
+ char *lsplits;
+ ADDRESS *q, **addrs;
+ ENVELOPE *ee, *firstsibling;
+
+ if (!ISVALIDQGRP(e->e_qgrp) || bitset(EF_SPLIT, e->e_flags))
+ return SM_SPLIT_NONE;
+
+ /* don't bother if there is no recipient limit */
+ maxrcpt = Queue[e->e_qgrp]->qg_maxrcpt;
+ if (maxrcpt <= 0)
+ return SM_SPLIT_NONE;
+
+ /* count recipients */
+ nrcpt = 0;
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (QS_IS_DEAD(q->q_state))
+ continue;
+ ++nrcpt;
+ }
+ if (nrcpt <= maxrcpt)
+ return SM_SPLIT_NONE;
+
+ /*
+ ** Preserve the recipient list
+ ** so that we can restore it in case of error.
+ ** (But we discard dead addresses.)
+ */
+
+ addrs = sm_rpool_malloc_x(e->e_rpool, nrcpt * sizeof(ADDRESS *));
+ for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (QS_IS_DEAD(q->q_state))
+ continue;
+ addrs[i++] = q;
+ }
+
+ /*
+ ** Partition the recipient list so that bad and sent addresses
+ ** come first. These will go with the original envelope, and
+ ** do not count towards the maxrcpt limit.
+ ** addrs[] does not contain QS_IS_DEAD() addresses.
+ */
+
+ ndead = 0;
+ for (i = 0; i < nrcpt; ++i)
+ {
+ if (QS_IS_BADADDR(addrs[i]->q_state) ||
+ QS_IS_SENT(addrs[i]->q_state) ||
+ QS_IS_DEAD(addrs[i]->q_state)) /* for paranoia's sake */
+ {
+ if (i > ndead)
+ {
+ ADDRESS *tmp = addrs[i];
+
+ addrs[i] = addrs[ndead];
+ addrs[ndead] = tmp;
+ }
+ ++ndead;
+ }
+ }
+
+ /* Check if no splitting required. */
+ if (nrcpt - ndead <= maxrcpt)
+ return SM_SPLIT_NONE;
+
+ /* fix links */
+ for (i = 0; i < nrcpt - 1; ++i)
+ addrs[i]->q_next = addrs[i + 1];
+ addrs[nrcpt - 1]->q_next = NULL;
+ e->e_sendqueue = addrs[0];
+
+ /* prepare buffer for logging */
+ if (LogLevel > SPLIT_LOG_LEVEL)
+ {
+ l = MAXLINE;
+ lsplits = sm_malloc(l);
+ if (lsplits != NULL)
+ *lsplits = '\0';
+ j = 0;
+ }
+ else
+ {
+ /* get rid of stupid compiler warnings */
+ lsplits = NULL;
+ j = l = 0;
+ }
+
+ /* split the envelope */
+ firstsibling = e->e_sibling;
+ i = maxrcpt + ndead;
+ nsplit = 0;
+ for (;;)
+ {
+ addrs[i - 1]->q_next = NULL;
+ ee = split_env(e, addrs[i], e->e_qgrp, e->e_qdir);
+ if (!dup_df(e, ee))
+ {
+
+ ee = firstsibling;
+ while (ee != NULL)
+ {
+ (void) unlink(queuename(ee, DATAFL_LETTER));
+ ee = ee->e_sibling;
+ }
+
+ /* Error. Restore e's sibling & recipient lists. */
+ e->e_sibling = firstsibling;
+ for (i = 0; i < nrcpt - 1; ++i)
+ addrs[i]->q_next = addrs[i + 1];
+ return SM_SPLIT_FAIL;
+ }
+
+ /* prepend the new envelope to e->e_sibling */
+ ee->e_sibling = e->e_sibling;
+ e->e_sibling = ee;
+ ++nsplit;
+ if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
+ {
+ if (j >= l - strlen(ee->e_id) - 3)
+ {
+ char *p;
+
+ l += MAXLINE;
+ p = sm_realloc(lsplits, l);
+ if (p == NULL)
+ {
+ /* let's try to get this done */
+ sm_free(lsplits);
+ lsplits = NULL;
+ }
+ else
+ lsplits = p;
+ }
+ if (lsplits != NULL)
+ {
+ if (j == 0)
+ j += sm_strlcat(lsplits + j,
+ ee->e_id,
+ l - j);
+ else
+ j += sm_strlcat2(lsplits + j,
+ "; ",
+ ee->e_id,
+ l - j);
+ SM_ASSERT(j < l);
+ }
+ }
+ if (nrcpt - i <= maxrcpt)
+ break;
+ i += maxrcpt;
+ }
+ if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && nsplit > 0)
+ {
+ sm_syslog(LOG_NOTICE, e->e_id,
+ "split: maxrcpts=%d, rcpts=%d, count=%d, id%s=%s",
+ maxrcpt, nrcpt - ndead, nsplit,
+ nsplit > 1 ? "s" : "", lsplits);
+ sm_free(lsplits);
+ }
+ return SM_SPLIT_NEW(nsplit);
+}
+/*
+** SPLIT_BY_RECIPIENT
+**
+** Split an envelope with multiple recipients into multiple
+** envelopes as required by the sendmail configuration.
+**
+** Parameters:
+** e -- envelope.
+**
+** Results:
+** Returns true on success, false on failure.
+**
+** Side Effects:
+** see split_across_queue_groups(), split_within_queue(e)
+*/
+
+bool
+split_by_recipient(e)
+ ENVELOPE *e;
+{
+ int split, n, i, j, l;
+ char *lsplits;
+ ENVELOPE *ee, *next, *firstsibling;
+
+ if (OpMode == SM_VERIFY || !ISVALIDQGRP(e->e_qgrp) ||
+ bitset(EF_SPLIT, e->e_flags))
+ return true;
+ n = split_across_queue_groups(e);
+ if (n == SM_SPLIT_FAIL)
+ return false;
+ firstsibling = ee = e->e_sibling;
+ if (n > 1 && LogLevel > SPLIT_LOG_LEVEL)
+ {
+ l = MAXLINE;
+ lsplits = sm_malloc(l);
+ if (lsplits != NULL)
+ *lsplits = '\0';
+ j = 0;
+ }
+ else
+ {
+ /* get rid of stupid compiler warnings */
+ lsplits = NULL;
+ j = l = 0;
+ }
+ for (i = 1; i < n; ++i)
+ {
+ next = ee->e_sibling;
+ if (split_within_queue(ee) == SM_SPLIT_FAIL)
+ {
+ e->e_sibling = firstsibling;
+ return false;
+ }
+ ee->e_flags |= EF_SPLIT;
+ if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
+ {
+ if (j >= l - strlen(ee->e_id) - 3)
+ {
+ char *p;
+
+ l += MAXLINE;
+ p = sm_realloc(lsplits, l);
+ if (p == NULL)
+ {
+ /* let's try to get this done */
+ sm_free(lsplits);
+ lsplits = NULL;
+ }
+ else
+ lsplits = p;
+ }
+ if (lsplits != NULL)
+ {
+ if (j == 0)
+ j += sm_strlcat(lsplits + j,
+ ee->e_id, l - j);
+ else
+ j += sm_strlcat2(lsplits + j, "; ",
+ ee->e_id, l - j);
+ SM_ASSERT(j < l);
+ }
+ }
+ ee = next;
+ }
+ if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && n > 1)
+ {
+ sm_syslog(LOG_NOTICE, e->e_id, "split: count=%d, id%s=%s",
+ n - 1, n > 2 ? "s" : "", lsplits);
+ sm_free(lsplits);
+ }
+ split = split_within_queue(e) != SM_SPLIT_FAIL;
+ if (split)
+ e->e_flags |= EF_SPLIT;
+ return split;
+}
+
+#if _FFR_QUARANTINE
+/*
+** QUARANTINE_QUEUE_ITEM -- {un,}quarantine a single envelope
+**
+** Add/remove quarantine reason and requeue appropriately.
+**
+** Parameters:
+** qgrp -- queue group for the item
+** qdir -- queue directory in the given queue group
+** e -- envelope information for the item
+** reason -- quarantine reason, NULL means unquarantine.
+**
+** Results:
+** true if item changed, false otherwise
+**
+** Side Effects:
+** Changes quarantine tag in queue file and renames it.
+*/
+
+static bool
+quarantine_queue_item(qgrp, qdir, e, reason)
+ int qgrp;
+ int qdir;
+ ENVELOPE *e;
+ char *reason;
+{
+ bool dirty = false;
+ bool failing = false;
+ bool foundq = false;
+ bool finished = false;
+ int fd;
+ int flags;
+ int oldtype;
+ int newtype;
+ int save_errno;
+ MODE_T oldumask = 0;
+ SM_FILE_T *oldqfp, *tempqfp;
+ char *bp;
+ char oldqf[MAXPATHLEN];
+ char tempqf[MAXPATHLEN];
+ char newqf[MAXPATHLEN];
+ char buf[MAXLINE];
+
+ oldtype = queue_letter(e, ANYQFL_LETTER);
+ (void) sm_strlcpy(oldqf, queuename(e, ANYQFL_LETTER), sizeof oldqf);
+ (void) sm_strlcpy(tempqf, queuename(e, NEWQFL_LETTER), sizeof tempqf);
+
+ /*
+ ** Instead of duplicating all the open
+ ** and lock code here, tell readqf() to
+ ** do that work and return the open
+ ** file pointer in e_lockfp. Note that
+ ** we must release the locks properly when
+ ** we are done.
+ */
+
+ if (!readqf(e, true))
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Skipping %s\n", qid_printname(e));
+ return false;
+ }
+ oldqfp = e->e_lockfp;
+
+ /* open the new queue file */
+ flags = O_CREAT|O_WRONLY|O_EXCL;
+ if (bitset(S_IWGRP, QueueFileMode))
+ oldumask = umask(002);
+ fd = open(tempqf, flags, QueueFileMode);
+ if (bitset(S_IWGRP, QueueFileMode))
+ (void) umask(oldumask);
+ RELEASE_QUEUE;
+
+ if (fd < 0)
+ {
+ save_errno = errno;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Skipping %s: Could not open %s: %s\n",
+ qid_printname(e), tempqf,
+ sm_errstring(save_errno));
+ (void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
+ return false;
+ }
+ if (!lockfile(fd, tempqf, NULL, LOCK_EX|LOCK_NB))
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Skipping %s: Could not lock %s\n",
+ qid_printname(e), tempqf);
+ (void) close(fd);
+ (void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
+ return false;
+ }
+
+ tempqfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &fd,
+ SM_IO_WRONLY, NULL);
+ if (tempqfp == NULL)
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Skipping %s: Could not lock %s\n",
+ qid_printname(e), tempqf);
+ (void) close(fd);
+ (void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
+ return false;
+ }
+
+ /* Copy the data over, changing the quarantine reason */
+ while ((bp = fgetfolded(buf, sizeof buf, oldqfp)) != NULL)
+ {
+ if (tTd(40, 4))
+ sm_dprintf("+++++ %s\n", bp);
+ switch (bp[0])
+ {
+ case 'q': /* quarantine reason */
+ foundq = true;
+ if (reason == NULL)
+ {
+ if (Verbose)
+ {
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "%s: Removed quarantine of \"%s\"\n",
+ e->e_id, &bp[1]);
+ }
+ sm_syslog(LOG_INFO, e->e_id, "unquarantine");
+ dirty = true;
+ continue;
+ }
+ else if (strcmp(reason, &bp[1]) == 0)
+ {
+ if (Verbose)
+ {
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "%s: Already quarantined with \"%s\"\n",
+ e->e_id, reason);
+ }
+ (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
+ "q%s\n", reason);
+ }
+ else
+ {
+ if (Verbose)
+ {
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "%s: Quarantine changed from \"%s\" to \"%s\"\n",
+ e->e_id, &bp[1],
+ reason);
+ }
+ (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
+ "q%s\n", reason);
+ sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
+ reason);
+ dirty = true;
+ }
+ break;
+
+ case 'R':
+ /*
+ ** If we are quarantining an unquarantined item,
+ ** need to put in a new 'q' line before it's
+ ** too late.
+ */
+
+ if (!foundq && reason != NULL)
+ {
+ if (Verbose)
+ {
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "%s: Quarantined with \"%s\"\n",
+ e->e_id, reason);
+ }
+ (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
+ "q%s\n", reason);
+ sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
+ reason);
+ foundq = true;
+ dirty = true;
+ }
+
+ /* Copy the line to the new file */
+ (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
+ "%s\n", bp);
+ break;
+
+ case '.':
+ finished = true;
+ /* FALLTHROUGH */
+
+ default:
+ /* Copy the line to the new file */
+ (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
+ "%s\n", bp);
+ break;
+ }
+ }
+
+ /* Make sure we read the whole old file */
+ errno = sm_io_error(tempqfp);
+ if (errno != 0 && errno != SM_IO_EOF)
+ {
+ save_errno = errno;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Skipping %s: Error reading %s: %s\n",
+ qid_printname(e), oldqf,
+ sm_errstring(save_errno));
+ failing = true;
+ }
+
+ if (!failing && !finished)
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Skipping %s: Incomplete file: %s\n",
+ qid_printname(e), oldqf);
+ failing = true;
+ }
+
+ /* Check if we actually changed anything or we can just bail now */
+ if (!dirty)
+ {
+ /* pretend we failed, even though we technically didn't */
+ failing = true;
+ }
+
+ /* Make sure we wrote things out safely */
+ if (!failing &&
+ (sm_io_flush(tempqfp, SM_TIME_DEFAULT) != 0 ||
+ ((SuperSafe == SAFE_REALLY || SuperSafe == SAFE_INTERACTIVE) &&
+ fsync(sm_io_getinfo(tempqfp, SM_IO_WHAT_FD, NULL)) < 0) ||
+ ((errno = sm_io_error(tempqfp)) != 0)))
+ {
+ save_errno = errno;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Skipping %s: Error writing %s: %s\n",
+ qid_printname(e), tempqf,
+ sm_errstring(save_errno));
+ failing = true;
+ }
+
+
+ /* Figure out the new filename */
+ newtype = (reason == NULL ? NORMQF_LETTER : QUARQF_LETTER);
+ if (oldtype == newtype)
+ {
+ /* going to rename tempqf to oldqf */
+ (void) sm_strlcpy(newqf, oldqf, sizeof newqf);
+ }
+ else
+ {
+ /* going to rename tempqf to new name based on newtype */
+ (void) sm_strlcpy(newqf, queuename(e, newtype), sizeof newqf);
+ }
+
+ save_errno = 0;
+
+ /* rename tempqf to newqf */
+ if (!failing &&
+ rename(tempqf, newqf) < 0)
+ save_errno = (errno == 0) ? EINVAL : errno;
+
+ /* Check rename() success */
+ if (!failing && save_errno != 0)
+ {
+ sm_syslog(LOG_DEBUG, e->e_id,
+ "quarantine_queue_item: rename(%s, %s): %s",
+ tempqf, newqf, sm_errstring(save_errno));
+
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Error renaming %s to %s: %s\n",
+ tempqf, newqf,
+ sm_errstring(save_errno));
+ if (oldtype == newtype)
+ {
+ /*
+ ** Bail here since we don't know the state of
+ ** the filesystem and may need to keep tempqf
+ ** for the user to rescue us.
+ */
+
+ RELEASE_QUEUE;
+ errno = save_errno;
+ syserr("!452 Error renaming control file %s", tempqf);
+ /* NOTREACHED */
+ }
+ else
+ {
+ /* remove new file (if rename() half completed) */
+ if (xunlink(newqf) < 0)
+ {
+ save_errno = errno;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Error removing %s: %s\n",
+ newqf,
+ sm_errstring(save_errno));
+ }
+
+ /* tempqf removed below */
+ failing = true;
+ }
+
+ }
+
+ /* If changing file types, need to remove old type */
+ if (!failing && oldtype != newtype)
+ {
+ if (xunlink(oldqf) < 0)
+ {
+ save_errno = errno;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Error removing %s: %s\n",
+ oldqf, sm_errstring(save_errno));
+ }
+ }
+
+ /* see if anything above failed */
+ if (failing)
+ {
+ /* Something failed: remove new file, old file still there */
+ (void) xunlink(tempqf);
+ }
+
+ /*
+ ** fsync() after file operations to make sure metadata is
+ ** written to disk on filesystems in which renames are
+ ** not guaranteed. It's ok if they fail, mail won't be lost.
+ */
+
+ if (SuperSafe != SAFE_NO)
+ {
+ /* for soft-updates */
+ (void) fsync(sm_io_getinfo(tempqfp,
+ SM_IO_WHAT_FD, NULL));
+
+ if (!failing)
+ {
+ /* for soft-updates */
+ (void) fsync(sm_io_getinfo(oldqfp,
+ SM_IO_WHAT_FD, NULL));
+ }
+
+ /* for other odd filesystems */
+ SYNC_DIR(tempqf, false);
+ }
+
+ /* Close up shop */
+ RELEASE_QUEUE;
+ if (tempqfp != NULL)
+ (void) sm_io_close(tempqfp, SM_TIME_DEFAULT);
+ if (oldqfp != NULL)
+ (void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
+
+ /* All went well */
+ return !failing;
+}
+
+/*
+** QUARANTINE_QUEUE -- {un,}quarantine matching items in the queue
+**
+** Read all matching queue items, add/remove quarantine
+** reason, and requeue appropriately.
+**
+** Parameters:
+** reason -- quarantine reason, "." means unquarantine.
+** qgrplimit -- limit to single queue group unless NOQGRP
+**
+** Results:
+** none.
+**
+** Side Effects:
+** Lots of changes to the queue.
+*/
+
+void
+quarantine_queue(reason, qgrplimit)
+ char *reason;
+ int qgrplimit;
+{
+ int changed = 0;
+ int qgrp;
+
+ /* Convert internal representation of unquarantine */
+ if (reason != NULL && reason[0] == '.' && reason[1] == '\0')
+ reason = NULL;
+
+ if (reason != NULL)
+ {
+ /* clean it */
+ reason = newstr(denlstring(reason, true, true));
+ }
+
+ for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++)
+ {
+ int qdir;
+
+ if (qgrplimit != NOQGRP && qgrplimit != qgrp)
+ continue;
+
+ for (qdir = 0; qdir < Queue[qgrp]->qg_numqueues; qdir++)
+ {
+ int i;
+ int nrequests;
+
+ if (StopRequest)
+ stop_sendmail();
+
+ nrequests = gatherq(qgrp, qdir, true, NULL, NULL);
+
+ /* first see if there is anything */
+ if (nrequests <= 0)
+ {
+ if (Verbose)
+ {
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT, "%s: no matches\n",
+ qid_printqueue(qgrp, qdir));
+ }
+ continue;
+ }
+
+ if (Verbose)
+ {
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT, "Processing %s:\n",
+ qid_printqueue(qgrp, qdir));
+ }
+
+ for (i = 0; i < WorkListCount; i++)
+ {
+ ENVELOPE e;
+
+ if (StopRequest)
+ stop_sendmail();
+
+ /* setup envelope */
+ clearenvelope(&e, true, sm_rpool_new_x(NULL));
+ e.e_id = WorkList[i].w_name + 2;
+ e.e_qgrp = qgrp;
+ e.e_qdir = qdir;
+
+ if (tTd(70, 101))
+ {
+ sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Would do %s\n", e.e_id);
+ changed++;
+ }
+ else if (quarantine_queue_item(qgrp, qdir,
+ &e, reason))
+ changed++;
+
+ /* clean up */
+ sm_rpool_free(e.e_rpool);
+ e.e_rpool = NULL;
+ }
+ if (WorkList != NULL)
+ sm_free(WorkList); /* XXX */
+ WorkList = NULL;
+ WorkListSize = 0;
+ WorkListCount = 0;
+ }
+ }
+ if (Verbose)
+ {
+ if (changed == 0)
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "No changes\n");
+ else
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "%d change%s\n",
+ changed,
+ changed == 1 ? "" : "s");
+ }
+}
+#endif /* _FFR_QUARANTINE */
diff --git a/contrib/sendmail/src/readcf.c b/contrib/sendmail/src/readcf.c
index 73acff5..1f6a662 100644
--- a/contrib/sendmail/src/readcf.c
+++ b/contrib/sendmail/src/readcf.c
@@ -11,12 +11,9 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: readcf.c,v 8.382.4.43 2001/08/14 23:08:13 ca Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
+SM_RCSID("@(#)$Id: readcf.c,v 8.594 2001/12/14 00:43:17 gshapiro Exp $")
#if NETINET || NETINET6
# include <arpa/inet.h>
@@ -31,6 +28,7 @@ static void fileclass __P((int, char *, char *, bool, bool));
static char **makeargv __P((char *));
static void settimeout __P((char *, char *, bool));
static void toomany __P((int, int));
+static char *extrquotstr __P((char *, char **, char *, bool *));
/*
** READCF -- read configuration file.
@@ -57,7 +55,10 @@ static void toomany __P((int, int));
** Mn arg=val... Define mailer. n is the internal name.
** Args specify mailer parameters.
** Oxvalue Set option x to value.
+** O option value Set option (long name) to value.
** Pname=value Set precedence name to value.
+** Qn arg=val... Define queue groups. n is the internal name.
+** Args specify queue parameters.
** Vversioncode[/vendorcode]
** Version level/vendor name of
** configuration syntax.
@@ -68,8 +69,8 @@ static void toomany __P((int, int));
**
** Parameters:
** cfname -- configuration file name.
-** safe -- TRUE if this is the system config file;
-** FALSE otherwise.
+** safe -- true if this is the system config file;
+** false otherwise.
** e -- the main envelope.
**
** Returns:
@@ -85,7 +86,7 @@ readcf(cfname, safe, e)
bool safe;
register ENVELOPE *e;
{
- FILE *cf;
+ SM_FILE_T *cf;
int ruleset = -1;
char *q;
struct rewrite *rwp = NULL;
@@ -94,6 +95,7 @@ readcf(cfname, safe, e)
int nfuzzy;
char *file;
bool optional;
+ bool ok;
int mid;
register char *p;
long sff = SFF_OPENASROOT;
@@ -102,7 +104,7 @@ readcf(cfname, safe, e)
char exbuf[MAXLINE];
char pvpbuf[MAXLINE + MAXATOM];
static char *null_list[1] = { NULL };
- extern u_char TokTypeNoC[];
+ extern unsigned char TokTypeNoC[];
FileName = cfname;
LineNumber = 0;
@@ -113,33 +115,34 @@ readcf(cfname, safe, e)
if (cf == NULL)
{
syserr("cannot open");
- finis(FALSE, EX_OSFILE);
+ finis(false, true, EX_OSFILE);
}
- if (fstat(fileno(cf), &statb) < 0)
+ if (fstat(sm_io_getinfo(cf, SM_IO_WHAT_FD, NULL), &statb) < 0)
{
syserr("cannot fstat");
- finis(FALSE, EX_OSFILE);
+ finis(false, true, EX_OSFILE);
}
if (!S_ISREG(statb.st_mode))
{
syserr("not a plain file");
- finis(FALSE, EX_OSFILE);
+ finis(false, true, EX_OSFILE);
}
if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
{
if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS)
- fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
- FileName);
+ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
+ "%s: WARNING: dangerous write permissions\n",
+ FileName);
if (LogLevel > 0)
sm_syslog(LOG_CRIT, NOQID,
"%s: WARNING: dangerous write permissions",
FileName);
}
-#ifdef XLA
+#if XLA
xla_zero();
#endif /* XLA */
@@ -148,7 +151,7 @@ readcf(cfname, safe, e)
if (bp[0] == '#')
{
if (bp != buf)
- sm_free(bp);
+ sm_free(bp); /* XXX */
continue;
}
@@ -202,7 +205,7 @@ readcf(cfname, safe, e)
{
register char **ap;
- rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
+ rwp->r_lhs = copyplist(rwp->r_lhs, true, NULL);
/* count the number of fuzzy matches in LHS */
for (ap = rwp->r_lhs; *ap != NULL; ap++)
@@ -288,7 +291,7 @@ readcf(cfname, safe, e)
{
register char **ap;
- rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
+ rwp->r_rhs = copyplist(rwp->r_rhs, true, NULL);
/* check no out-of-bounds replacements */
nfuzzy += '0';
@@ -326,6 +329,36 @@ readcf(cfname, safe, e)
case MATCHNCLASS:
botch = "$~";
break;
+
+#if 0
+/*
+** This doesn't work yet as there are maps defined *after* the cf
+** is read such as host, user, and alias. So for now, it's removed.
+** When it comes back, the RELEASE_NOTES entry will be:
+** Emit warnings for unknown maps when reading the .cf file. Based on
+** patch from Robert Harker of Harker Systems.
+*/
+
+ case LOOKUPBEGIN:
+ /*
+ ** Got a database lookup,
+ ** check if map is defined.
+ */
+
+ ep = *(ap + 1);
+ if ((*ep & 0377) != MACRODEXPAND &&
+ stab(ep, ST_MAP,
+ ST_FIND) == NULL)
+ {
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "Warning: %s: line %d: map %s not found\n",
+ FileName,
+ LineNumber,
+ ep);
+ }
+ break;
+#endif /* 0 */
}
if (botch != NULL)
syserr("Inappropriate use of %s on RHS",
@@ -349,22 +382,24 @@ readcf(cfname, safe, e)
if (rwp != NULL)
{
if (OpMode == MD_TEST)
- printf("WARNING: Ruleset %s has multiple definitions\n",
- &bp[1]);
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "WARNING: Ruleset %s has multiple definitions\n",
+ &bp[1]);
if (tTd(37, 1))
- dprintf("WARNING: Ruleset %s has multiple definitions\n",
- &bp[1]);
+ sm_dprintf("WARNING: Ruleset %s has multiple definitions\n",
+ &bp[1]);
while (rwp->r_next != NULL)
rwp = rwp->r_next;
}
break;
case 'D': /* macro definition */
- mid = macid(&bp[1], &ep);
+ mid = macid_parse(&bp[1], &ep);
if (mid == 0)
break;
p = munchstring(ep, NULL, '\0');
- define(mid, newstr(p), e);
+ macdefine(&e->e_macro, A_TEMP, mid, p);
break;
case 'H': /* required header line */
@@ -375,7 +410,7 @@ readcf(cfname, safe, e)
case 'T': /* trusted user (set class `t') */
if (bp[0] == 'C')
{
- mid = macid(&bp[1], &ep);
+ mid = macid_parse(&bp[1], &ep);
if (mid == 0)
break;
expand(ep, exbuf, sizeof exbuf, e);
@@ -405,27 +440,40 @@ readcf(cfname, safe, e)
break;
case 'F': /* word class from file */
- mid = macid(&bp[1], &ep);
+ mid = macid_parse(&bp[1], &ep);
if (mid == 0)
break;
for (p = ep; isascii(*p) && isspace(*p); )
p++;
if (p[0] == '-' && p[1] == 'o')
{
- optional = TRUE;
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ optional = true;
+ while (*p != '\0' &&
+ !(isascii(*p) && isspace(*p)))
p++;
while (isascii(*p) && isspace(*p))
p++;
+ file = p;
}
else
- optional = FALSE;
+ optional = false;
- file = p;
- q = p;
- while (*q != '\0' && !(isascii(*q) && isspace(*q)))
- q++;
- if (*file == '|')
+ if (*p == '@')
+ {
+ /* use entire spec */
+ file = p;
+ }
+ else
+ {
+ file = extrquotstr(p, &q, " ", &ok);
+ if (!ok)
+ {
+ syserr("illegal filename '%s'", p);
+ break;
+ }
+ }
+
+ if (*file == '|' || *file == '@')
p = "%s";
else
{
@@ -442,7 +490,7 @@ readcf(cfname, safe, e)
fileclass(mid, file, p, safe, optional);
break;
-#ifdef XLA
+#if XLA
case 'L': /* extended load average description */
xla_init(&bp[1]);
break;
@@ -463,7 +511,7 @@ readcf(cfname, safe, e)
break;
case 'O': /* set option */
- setoption(bp[1], &bp[2], safe, FALSE, e);
+ setoption(bp[1], &bp[2], safe, false, e);
break;
case 'P': /* set precedence */
@@ -482,6 +530,10 @@ readcf(cfname, safe, e)
NumPriorities++;
break;
+ case 'Q': /* define queue */
+ makequeue(&bp[1], true);
+ break;
+
case 'V': /* configuration syntax version */
for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
continue;
@@ -502,12 +554,15 @@ readcf(cfname, safe, e)
/* level 5 configs have short name in $w */
p = macvalue('w', e);
if (p != NULL && (p = strchr(p, '.')) != NULL)
+ {
*p = '\0';
- define('w', macvalue('w', e), e);
+ macdefine(&e->e_macro, A_TEMP, 'w',
+ macvalue('w', e));
+ }
}
if (ConfigLevel >= 6)
{
- ColonOkInAddr = FALSE;
+ ColonOkInAddr = false;
}
/*
@@ -539,25 +594,28 @@ readcf(cfname, safe, e)
setuserenv(&bp[1], p);
break;
-#if _FFR_MILTER
case 'X': /* mail filter */
+#if MILTER
milter_setup(&bp[1]);
+#else /* MILTER */
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: Filter usage ('X') requires Milter support (-DMILTER)\n");
+#endif /* MILTER */
break;
-#endif /* _FFR_MILTER */
default:
badline:
syserr("unknown configuration line \"%s\"", bp);
}
if (bp != buf)
- sm_free(bp);
+ sm_free(bp); /* XXX */
}
- if (ferror(cf))
+ if (sm_io_error(cf))
{
syserr("I/O read error");
- finis(FALSE, EX_OSFILE);
+ finis(false, true, EX_OSFILE);
}
- (void) fclose(cf);
+ (void) sm_io_close(cf, SM_TIME_DEFAULT);
FileName = NULL;
/* initialize host maps from local service tables */
@@ -573,35 +631,21 @@ readcf(cfname, safe, e)
short mapreturn[MAXMAPACTIONS];
nmaps = switch_map_find("hosts", maptype, mapreturn);
- UseNameServer = FALSE;
+ UseNameServer = false;
if (nmaps > 0 && nmaps <= MAXMAPSTACK)
{
register int mapno;
- for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
+ for (mapno = 0; mapno < nmaps && !UseNameServer;
+ mapno++)
{
if (strcmp(maptype[mapno], "dns") == 0)
- UseNameServer = TRUE;
- }
- }
-
-#ifdef HESIOD
- nmaps = switch_map_find("passwd", maptype, mapreturn);
- UseHesiod = FALSE;
- if (nmaps > 0 && nmaps <= MAXMAPSTACK)
- {
- register int mapno;
-
- for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
- {
- if (strcmp(maptype[mapno], "hesiod") == 0)
- UseHesiod = TRUE;
+ UseNameServer = true;
}
}
-#endif /* HESIOD */
}
}
- /*
+/*
** TRANSLATE_DOLLARS -- convert $x into internal form
**
** Actually does all appropriate pre-processing of a config line
@@ -627,7 +671,6 @@ translate_dollars(bp)
{
if (*p == '#' && p > bp && ConfigLevel >= 3)
{
- /* this is an on-line comment */
register char *e;
switch (*--p & 0377)
@@ -639,7 +682,7 @@ translate_dollars(bp)
case '\\':
/* it's backslash escaped */
- (void) strlcpy(p, p + 1, strlen(p));
+ (void) sm_strlcpy(p, p + 1, strlen(p));
break;
default:
@@ -648,7 +691,7 @@ translate_dollars(bp)
*p != '\n' && p > bp)
p--;
if ((e = strchr(++p, '\n')) != NULL)
- (void) strlcpy(p, e, strlen(p));
+ (void) sm_strlcpy(p, e, strlen(p));
else
*p-- = '\0';
break;
@@ -662,7 +705,7 @@ translate_dollars(bp)
if (p[1] == '$')
{
/* actual dollar sign.... */
- (void) strlcpy(p, p + 1, strlen(p));
+ (void) sm_strlcpy(p, p + 1, strlen(p));
continue;
}
@@ -674,16 +717,16 @@ translate_dollars(bp)
p++;
/* convert macro name to code */
- *p = macid(p, &ep);
+ *p = macid_parse(p, &ep);
if (ep != p + 1)
- (void) strlcpy(p + 1, ep, strlen(p + 1));
+ (void) sm_strlcpy(p + 1, ep, strlen(p + 1));
}
/* strip trailing white space from the line */
while (--p > bp && isascii(*p) && isspace(*p))
*p = '\0';
}
- /*
+/*
** TOOMANY -- signal too many of some option
**
** Parameters:
@@ -704,7 +747,7 @@ toomany(id, maxcnt)
{
syserr("too many %c lines, %d max", id, maxcnt);
}
- /*
+/*
** FILECLASS -- read members of a class from a file
**
** Parameters:
@@ -719,11 +762,41 @@ toomany(id, maxcnt)
** none
**
** Side Effects:
-**
** puts all lines in filename that match a scanf into
** the named class.
*/
+/*
+** Break up the match into words and add to class.
+*/
+
+static void
+parse_class_words(class, line)
+ int class;
+ char *line;
+{
+ while (line != NULL && *line != '\0')
+ {
+ register char *q;
+
+ /* strip leading spaces */
+ while (isascii(*line) && isspace(*line))
+ line++;
+ if (*line == '\0')
+ break;
+
+ /* find the end of the word */
+ q = line;
+ while (*line != '\0' && !(isascii(*line) && isspace(*line)))
+ line++;
+ if (*line != '\0')
+ *line++ = '\0';
+
+ /* enter the word in the symbol table */
+ setclass(class, q);
+ }
+}
+
static void
fileclass(class, filename, fmt, safe, optional)
int class;
@@ -732,34 +805,166 @@ fileclass(class, filename, fmt, safe, optional)
bool safe;
bool optional;
{
- FILE *f;
+ SM_FILE_T *f;
long sff;
pid_t pid;
register char *p;
char buf[MAXLINE];
if (tTd(37, 2))
- dprintf("fileclass(%s, fmt=%s)\n", filename, fmt);
+ sm_dprintf("fileclass(%s, fmt=%s)\n", filename, fmt);
+
+ if (*filename == '\0')
+ {
+ syserr("fileclass: missing file name");
+ return;
+ }
+ else if (!SM_IS_DIR_DELIM(*filename) && *filename != '|' &&
+ (p = strchr(filename, '@')) != NULL)
+ {
+ int status = 0;
+ char *key;
+ char *mn;
+ char *cl, *spec;
+ STAB *mapclass;
+ MAP map;
+
+ mn = newstr(macname(class));
+
+ key = filename;
+
+ /* skip past '@' */
+ *p++ = '\0';
+ cl = p;
+
+ if (strcmp(cl, "LDAP") == 0)
+ {
+ int n;
+ char *lc;
+ char jbuf[MAXHOSTNAMELEN];
+ char lcbuf[MAXLINE];
+
+ /* Get $j */
+ expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
+ if (jbuf[0] == '\0')
+ {
+ (void) sm_strlcpy(jbuf, "localhost",
+ sizeof jbuf);
+ }
+
+ /* impose the default schema */
+ lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
+ if (lc == NULL)
+ lc = "";
+ else
+ {
+ expand(lc, lcbuf, sizeof lcbuf, CurEnv);
+ lc = lcbuf;
+ }
+
+ cl = "ldap";
+ n = sm_snprintf(buf, sizeof buf,
+ "-k (&(objectClass=sendmailMTAClass)(sendmailMTAClassName=%s)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))) -v sendmailMTAClassValue",
+ mn, lc, jbuf);
+ if (n >= sizeof buf)
+ {
+ syserr("fileclass: F{%s}: Default LDAP string too long",
+ mn);
+ sm_free(mn);
+ return;
+ }
+ spec = buf;
+ }
+ else
+ {
+ if ((spec = strchr(cl, ':')) == NULL)
+ {
+ syserr("fileclass: F{%s}: missing map class",
+ mn);
+ sm_free(mn);
+ return;
+ }
+ *spec++ ='\0';
+ }
+
+ /* set up map structure */
+ mapclass = stab(cl, ST_MAPCLASS, ST_FIND);
+ if (mapclass == NULL)
+ {
+ syserr("fileclass: F{%s}: class %s not available",
+ mn, cl);
+ sm_free(mn);
+ return;
+ }
+ memset(&map, '\0', sizeof map);
+ map.map_class = &mapclass->s_mapclass;
+ map.map_mname = mn;
+ map.map_mflags |= MF_FILECLASS;
+
+ /* parse map spec */
+ if (!map.map_class->map_parse(&map, spec))
+ {
+ /* map_parse() showed the error already */
+ sm_free(mn);
+ return;
+ }
+ map.map_mflags |= MF_VALID;
+
+ /* open map */
+ if (map.map_class->map_open(&map, O_RDONLY))
+ {
+ map.map_mflags |= MF_OPEN;
+ map.map_pid = getpid();
+ }
+ else
+ {
+ if (!optional &&
+ !bitset(MF_OPTIONAL, map.map_mflags))
+ syserr("fileclass: F{%s}: map open failed",
+ mn);
+ sm_free(mn);
+ return;
+ }
+
+ /* lookup */
+ p = (*map.map_class->map_lookup)(&map, key, NULL, &status);
+ if (status != EX_OK && status != EX_NOTFOUND)
+ {
+ if (!optional)
+ syserr("fileclass: F{%s}: map lookup failed",
+ mn);
+ p = NULL;
+ }
+
+ /* use the results */
+ if (p != NULL)
+ parse_class_words(class, p);
- if (filename[0] == '|')
+ /* close map */
+ map.map_mflags |= MF_CLOSING;
+ map.map_class->map_close(&map);
+ map.map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
+ sm_free(mn);
+ return;
+ }
+ else if (filename[0] == '|')
{
auto int fd;
int i;
char *argv[MAXPV + 1];
i = 0;
- for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t"))
- {
- if (i >= MAXPV)
- break;
+ for (p = strtok(&filename[1], " \t");
+ p != NULL && i < MAXPV;
+ p = strtok(NULL, " \t"))
argv[i++] = p;
- }
argv[i] = NULL;
pid = prog_open(argv, &fd, CurEnv);
if (pid < 0)
f = NULL;
else
- f = fdopen(fd, "r");
+ f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
+ (void *) &fd, SM_IO_RDONLY, NULL);
}
else
{
@@ -772,6 +977,8 @@ fileclass(class, filename, fmt, safe, optional)
sff |= SFF_NOWLINK;
if (safe)
sff |= SFF_OPENASROOT;
+ else if (RealUid == 0)
+ sff |= SFF_ROOTOK;
if (DontLockReadFiles)
sff |= SFF_NOLOCK;
f = safefopen(filename, O_RDONLY, 0, sff);
@@ -783,7 +990,7 @@ fileclass(class, filename, fmt, safe, optional)
return;
}
- while (fgets(buf, sizeof buf, f) != NULL)
+ while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
{
#if SCANF
char wordbuf[MAXLINE + 1];
@@ -792,44 +999,27 @@ fileclass(class, filename, fmt, safe, optional)
if (buf[0] == '#')
continue;
#if SCANF
- if (sscanf(buf, fmt, wordbuf) != 1)
+ if (sm_io_sscanf(buf, fmt, wordbuf) != 1)
continue;
p = wordbuf;
#else /* SCANF */
p = buf;
#endif /* SCANF */
+ parse_class_words(class, p);
+
/*
- ** Break up the match into words.
+ ** If anything else is added here,
+ ** check if the '@' map case above
+ ** needs the code as well.
*/
-
- while (*p != '\0')
- {
- register char *q;
-
- /* strip leading spaces */
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p == '\0')
- break;
-
- /* find the end of the word */
- q = p;
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p != '\0')
- *p++ = '\0';
-
- /* enter the word in the symbol table */
- setclass(class, q);
- }
}
- (void) fclose(f);
+ (void) sm_io_close(f, SM_TIME_DEFAULT);
if (pid > 0)
(void) waitfor(pid);
}
- /*
+/*
** MAKEMAILER -- define a new mailer.
**
** Parameters:
@@ -844,12 +1034,14 @@ fileclass(class, filename, fmt, safe, optional)
** M -- the maximum message size
** N -- the niceness at which to run
** P -- the path to the mailer
+** Q -- the queue group for the mailer
** R -- the recipient rewriting set
** S -- the sender rewriting set
** T -- the mailer type (for DSNs)
** U -- the uid to run as
** W -- the time to wait at the end
** m -- maximum messages per connection
+** r -- maximum number of recipients per message
** / -- new root directory
** The first word is the canonical name of the mailer.
**
@@ -870,14 +1062,17 @@ makemailer(line)
int i;
char fcode;
auto char *endp;
- extern int NextMailer;
+ static int nextmailer = 0; /* "free" index into Mailer struct */
/* allocate a mailer and set up defaults */
m = (struct mailer *) xalloc(sizeof *m);
memset((char *) m, '\0', sizeof *m);
+ errno = 0; /* avoid bogus error text */
/* collect the mailer name */
- for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
+ for (p = line;
+ *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
+ p++)
continue;
if (*p != '\0')
*p++ = '\0';
@@ -893,7 +1088,8 @@ makemailer(line)
{
auto char *delimptr;
- while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
+ while (*p != '\0' &&
+ (*p == ',' || (isascii(*p) && isspace(*p))))
p++;
/* p now points to field code */
@@ -915,16 +1111,24 @@ makemailer(line)
switch (fcode)
{
case 'P': /* pathname */
- if (*p == '\0')
- syserr("mailer %s: empty path name", m->m_name);
- else
+ if (*p != '\0') /* error is issued below */
m->m_mailer = newstr(p);
break;
case 'F': /* flags */
for (; *p != '\0'; p++)
+ {
if (!(isascii(*p) && isspace(*p)))
+ {
+#if _FFR_DEPRECATE_MAILER_FLAG_I
+ if (*p == M_INTERNAL)
+ sm_syslog(LOG_WARNING, NOQID,
+ "WARNING: mailer=%s, flag=%c deprecated",
+ m->m_name, *p);
+#endif /* _FFR_DEPRECATE_MAILER_FLAG_I */
setbitn(bitidx(*p), m->m_flags);
+ }
+ }
break;
case 'S': /* sender rewriting ruleset */
@@ -959,10 +1163,7 @@ makemailer(line)
break;
case 'A': /* argument vector */
- if (*p == '\0')
- syserr("mailer %s: null argument vector",
- m->m_name);
- else
+ if (*p != '\0') /* error is issued below */
m->m_argv = makeargv(p);
break;
@@ -974,11 +1175,9 @@ makemailer(line)
m->m_maxdeliveries = atoi(p);
break;
-#if _FFR_DYNAMIC_TOBUF
case 'r': /* max recipient per envelope */
m->m_maxrcpt = atoi(p);
break;
-#endif /* _FFR_DYNAMIC_TOBUF */
case 'L': /* maximum line length */
m->m_linelimit = atoi(p);
@@ -1005,6 +1204,20 @@ makemailer(line)
m->m_defcharset = newstr(p);
break;
+ case 'Q': /* queue for this mailer */
+ if (*p == '\0')
+ {
+ syserr("mailer %s: null queue", m->m_name);
+ break;
+ }
+ s = stab(p, ST_QUEUE, ST_FIND);
+ if (s == NULL)
+ syserr("mailer %s: unknown queue %s",
+ m->m_name, p);
+ else
+ m->m_qgrp = s->s_quegrp->qg_index;
+ break;
+
case 'T': /* MTA-Name/Address/Diagnostic types */
/* extract MTA name type; default to "dns" */
m->m_mtatype = newstr(p);
@@ -1133,6 +1346,24 @@ makemailer(line)
p = delimptr;
}
+#if !HASRRESVPORT
+ if (bitnset(M_SECURE_PORT, m->m_flags))
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "M%s: Warning: F=%c set on system that doesn't support rresvport()\n",
+ m->m_name, M_SECURE_PORT);
+ }
+#endif /* !HASRRESVPORT */
+
+#if !HASNICE
+ if (m->m_nice != 0)
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "M%s: Warning: N= set on system that doesn't support nice()\n",
+ m->m_name);
+ }
+#endif /* !HASNICE */
+
/* do some rationality checking */
if (m->m_argv == NULL)
{
@@ -1145,16 +1376,14 @@ makemailer(line)
return;
}
- if (NextMailer >= MAXMAILERS)
+ if (nextmailer >= MAXMAILERS)
{
syserr("too many mailers defined (%d max)", MAXMAILERS);
return;
}
-#if _FFR_DYNAMIC_TOBUF
if (m->m_maxrcpt <= 0)
m->m_maxrcpt = DEFAULT_MAX_RCPT;
-#endif /* _FFR_DYNAMIC_TOBUF */
/* do some heuristic cleanup for back compatibility */
if (bitnset(M_LIMITS, m->m_flags))
@@ -1167,21 +1396,11 @@ makemailer(line)
if (strcmp(m->m_mailer, "[TCP]") == 0)
{
-#if _FFR_REMOVE_TCP_MAILER_PATH
- syserr("M%s: P=[TCP] is deprecated, use P=[IPC] instead\n",
- m->m_name);
+ syserr("M%s: P=[TCP] must be replaced by P=[IPC]", m->m_name);
return;
-#else /* _FFR_REMOVE_TCP_MAILER_PATH */
- printf("M%s: Warning: P=[TCP] is deprecated, use P=[IPC] instead\n",
- m->m_name);
-#endif /* _FFR_REMOVE_TCP_MAILER_PATH */
}
- if (strcmp(m->m_mailer, "[IPC]") == 0
-#if !_FFR_REMOVE_TCP_MAILER_PATH
- || strcmp(m->m_mailer, "[TCP]") == 0
-#endif /* !_FFR_REMOVE_TCP_MAILER_PATH */
- )
+ if (strcmp(m->m_mailer, "[IPC]") == 0)
{
/* Use the second argument for host or path to socket */
if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
@@ -1195,21 +1414,30 @@ makemailer(line)
#if NETUNIX
&& strcmp(m->m_argv[0], "FILE") != 0
#endif /* NETUNIX */
-#if !_FFR_DEPRECATE_IPC_MAILER_ARG
- && strcmp(m->m_argv[0], "IPC") != 0
-#endif /* !_FFR_DEPRECATE_IPC_MAILER_ARG */
)
{
- printf("M%s: Warning: first argument in %s mailer must be %s\n",
- m->m_name, m->m_mailer,
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "M%s: Warning: first argument in %s mailer must be %s\n",
+ m->m_name, m->m_mailer,
#if NETUNIX
- "TCP or FILE"
+ "TCP or FILE"
#else /* NETUNIX */
- "TCP"
+ "TCP"
#endif /* NETUNIX */
- );
+ );
+ }
+ if (m->m_mtatype == NULL)
+ m->m_mtatype = "dns";
+ if (m->m_addrtype == NULL)
+ m->m_addrtype = "rfc822";
+ if (m->m_diagtype == NULL)
+ {
+ if (m->m_argv[0] != NULL &&
+ strcmp(m->m_argv[0], "FILE") == 0)
+ m->m_diagtype = "x-unix";
+ else
+ m->m_diagtype = "smtp";
}
-
}
else if (strcmp(m->m_mailer, "[FILE]") == 0)
{
@@ -1231,23 +1459,6 @@ makemailer(line)
}
}
- if (strcmp(m->m_mailer, "[IPC]") == 0 ||
- strcmp(m->m_mailer, "[TCP]") == 0)
- {
- if (m->m_mtatype == NULL)
- m->m_mtatype = "dns";
- if (m->m_addrtype == NULL)
- m->m_addrtype = "rfc822";
- if (m->m_diagtype == NULL)
- {
- if (m->m_argv[0] != NULL &&
- strcmp(m->m_argv[0], "FILE") == 0)
- m->m_diagtype = "x-unix";
- else
- m->m_diagtype = "smtp";
- }
- }
-
if (m->m_eol == NULL)
{
char **pp;
@@ -1274,16 +1485,16 @@ makemailer(line)
if (s->s_mailer != NULL)
{
i = s->s_mailer->m_mno;
- sm_free(s->s_mailer);
+ sm_free(s->s_mailer); /* XXX */
}
else
{
- i = NextMailer++;
+ i = nextmailer++;
}
Mailer[i] = s->s_mailer = m;
m->m_mno = i;
}
- /*
+/*
** MUNCHSTRING -- translate a string into internal form.
**
** Parameters:
@@ -1307,8 +1518,8 @@ munchstring(p, delimptr, delim)
int delim;
{
register char *q;
- bool backslash = FALSE;
- bool quotemode = FALSE;
+ bool backslash = false;
+ bool quotemode = false;
static char buf[MAXLINE];
for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++)
@@ -1316,7 +1527,7 @@ munchstring(p, delimptr, delim)
if (backslash)
{
/* everything is roughly literal */
- backslash = FALSE;
+ backslash = false;
switch (*p)
{
case 'r': /* carriage return */
@@ -1340,7 +1551,7 @@ munchstring(p, delimptr, delim)
else
{
if (*p == '\\')
- backslash = TRUE;
+ backslash = true;
else if (*p == '"')
quotemode = !quotemode;
else if (quotemode || *p != delim)
@@ -1355,7 +1566,67 @@ munchstring(p, delimptr, delim)
*q++ = '\0';
return buf;
}
- /*
+/*
+** EXTRQUOTSTR -- extract a (quoted) string.
+**
+** This routine deals with quoted (") strings and escaped
+** spaces (\\ ).
+**
+** Parameters:
+** p -- source string.
+** delimptr -- if non-NULL, set to the pointer of the
+** field delimiter character.
+** delimbuf -- delimiters for the field.
+** st -- if non-NULL, store the return value (whether the
+** string was correctly quoted) here.
+**
+** Returns:
+** the extracted string.
+**
+** Side Effects:
+** the returned string is a local static buffer.
+** it must be copied before the function is called again.
+*/
+
+static char *
+extrquotstr(p, delimptr, delimbuf, st)
+ register char *p;
+ char **delimptr;
+ char *delimbuf;
+ bool *st;
+{
+ register char *q;
+ bool backslash = false;
+ bool quotemode = false;
+ static char buf[MAXLINE];
+
+ for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++)
+ {
+ if (backslash)
+ {
+ backslash = false;
+ if (*p != ' ')
+ *q++ = '\\';
+ }
+ if (*p == '\\')
+ backslash = true;
+ else if (*p == '"')
+ quotemode = !quotemode;
+ else if (quotemode ||
+ strchr(delimbuf, (int) *p) == NULL)
+ *q++ = *p;
+ else
+ break;
+ }
+
+ if (delimptr != NULL)
+ *delimptr = p;
+ *q++ = '\0';
+ if (st != NULL)
+ *st = !(quotemode || backslash);
+ return buf;
+}
+/*
** MAKEARGV -- break up a string into words
**
** Parameters:
@@ -1396,7 +1667,7 @@ makeargv(p)
return avp;
}
- /*
+/*
** PRINTRULES -- print rewrite rules (for debugging)
**
** Parameters:
@@ -1419,18 +1690,21 @@ printrules()
{
if (RewriteRules[ruleset] == NULL)
continue;
- printf("\n----Rule Set %d:", ruleset);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\n----Rule Set %d:", ruleset);
for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
{
- printf("\nLHS:");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\nLHS:");
printav(rwp->r_lhs);
- printf("RHS:");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "RHS:");
printav(rwp->r_rhs);
}
}
}
- /*
+/*
** PRINTMAILER -- print mailer structure (for debugging)
**
** Parameters:
@@ -1446,54 +1720,68 @@ printmailer(m)
{
int j;
- printf("mailer %d (%s): P=%s S=", m->m_mno, m->m_name, m->m_mailer);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "mailer %d (%s): P=%s S=", m->m_mno, m->m_name,
+ m->m_mailer);
if (RuleSetNames[m->m_se_rwset] == NULL)
- printf("%d/", m->m_se_rwset);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d/",
+ m->m_se_rwset);
else
- printf("%s/", RuleSetNames[m->m_se_rwset]);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s/",
+ RuleSetNames[m->m_se_rwset]);
if (RuleSetNames[m->m_sh_rwset] == NULL)
- printf("%d R=", m->m_sh_rwset);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d R=",
+ m->m_sh_rwset);
else
- printf("%s R=", RuleSetNames[m->m_sh_rwset]);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s R=",
+ RuleSetNames[m->m_sh_rwset]);
if (RuleSetNames[m->m_re_rwset] == NULL)
- printf("%d/", m->m_re_rwset);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d/",
+ m->m_re_rwset);
else
- printf("%s/", RuleSetNames[m->m_re_rwset]);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s/",
+ RuleSetNames[m->m_re_rwset]);
if (RuleSetNames[m->m_rh_rwset] == NULL)
- printf("%d ", m->m_rh_rwset);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d ",
+ m->m_rh_rwset);
else
- printf("%s ", RuleSetNames[m->m_rh_rwset]);
- printf("M=%ld U=%d:%d F=", m->m_maxsize,
- (int) m->m_uid, (int) m->m_gid);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s ",
+ RuleSetNames[m->m_rh_rwset]);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "M=%ld U=%d:%d F=",
+ m->m_maxsize, (int) m->m_uid, (int) m->m_gid);
for (j = '\0'; j <= '\177'; j++)
if (bitnset(j, m->m_flags))
- (void) putchar(j);
- printf(" L=%d E=", m->m_linelimit);
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT, j);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " L=%d E=",
+ m->m_linelimit);
xputs(m->m_eol);
if (m->m_defcharset != NULL)
- printf(" C=%s", m->m_defcharset);
- printf(" T=%s/%s/%s",
- m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
- m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
- m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
-#if _FFR_DYNAMIC_TOBUF
- printf(" r=%d", m->m_maxrcpt);
-#endif /* _FFR_DYNAMIC_TOBUF */
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " C=%s",
+ m->m_defcharset);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " T=%s/%s/%s",
+ m->m_mtatype == NULL
+ ? "<undefined>" : m->m_mtatype,
+ m->m_addrtype == NULL
+ ? "<undefined>" : m->m_addrtype,
+ m->m_diagtype == NULL
+ ? "<undefined>" : m->m_diagtype);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " r=%d", m->m_maxrcpt);
if (m->m_argv != NULL)
{
char **a = m->m_argv;
- printf(" A=");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " A=");
while (*a != NULL)
{
if (a != m->m_argv)
- printf(" ");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ " ");
xputs(*a++);
}
}
- printf("\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n");
}
- /*
+/*
** SETOPTION -- set global processing option
**
** Parameters:
@@ -1532,6 +1820,9 @@ static struct resolverflags
{ "defnames", RES_DEFNAMES },
{ "stayopen", RES_STAYOPEN },
{ "dnsrch", RES_DNSRCH },
+# ifdef RES_USE_INET6
+ { "use_inet6", RES_USE_INET6 },
+# endif /* RES_USE_INET6 */
{ "true", 0 }, /* avoid error on old syntax */
{ NULL, 0 }
};
@@ -1544,9 +1835,9 @@ static struct resolverflags
static struct optioninfo
{
- char *o_name; /* long name of option */
- u_char o_code; /* short name of option */
- u_short o_flags; /* option flags */
+ char *o_name; /* long name of option */
+ unsigned char o_code; /* short name of option */
+ unsigned short o_flags; /* option flags */
} OptionTab[] =
{
#if defined(SUN_EXTENSIONS) && defined(REMOTE_MODE)
@@ -1560,15 +1851,15 @@ static struct optioninfo
{ "MinFreeBlocks", 'b', OI_SAFE },
{ "CheckpointInterval", 'C', OI_SAFE },
{ "HoldExpensive", 'c', OI_NONE },
-#if !_FFR_REMOVE_AUTOREBUILD
- { "AutoRebuildAliases", 'D', OI_NONE },
-#endif /* !_FFR_REMOVE_AUTOREBUILD */
{ "DeliveryMode", 'd', OI_SAFE },
{ "ErrorHeader", 'E', OI_NONE },
{ "ErrorMode", 'e', OI_SAFE },
{ "TempFileMode", 'F', OI_NONE },
{ "SaveFromLine", 'f', OI_NONE },
{ "MatchGECOS", 'G', OI_NONE },
+
+ /* no long name, just here to avoid problems in setoption */
+ { "", 'g', OI_NONE },
{ "HelpFile", 'H', OI_NONE },
{ "MaxHopCount", 'h', OI_NONE },
{ "ResolverOptions", 'I', OI_NONE },
@@ -1580,6 +1871,9 @@ static struct optioninfo
{ "UseErrorsTo", 'l', OI_NONE },
{ "LogLevel", 'L', OI_SAFE },
{ "MeToo", 'm', OI_SAFE },
+
+ /* no long name, just here to avoid problems in setoption */
+ { "", 'M', OI_NONE },
{ "CheckAliases", 'n', OI_NONE },
{ "OldStyleHeaders", 'o', OI_SAFE },
{ "DaemonPortOptions", 'O', OI_NONE },
@@ -1704,49 +1998,92 @@ static struct optioninfo
{ "DataFileBufferSize", O_DF_BUFSIZE, OI_NONE },
#define O_XF_BUFSIZE 0xb1
{ "XscriptFileBufferSize", O_XF_BUFSIZE, OI_NONE },
-# define O_LDAPDEFAULTSPEC 0xb2
+#define O_LDAPDEFAULTSPEC 0xb2
{ "LDAPDefaultSpec", O_LDAPDEFAULTSPEC, OI_NONE },
#if _FFR_QUEUEDELAY
-#define O_QUEUEDELAY 0xb3
+# define O_QUEUEDELAY 0xb3
{ "QueueDelay", O_QUEUEDELAY, OI_NONE },
#endif /* _FFR_QUEUEDELAY */
-# define O_SRVCERTFILE 0xb4
+#define O_SRVCERTFILE 0xb4
{ "ServerCertFile", O_SRVCERTFILE, OI_NONE },
-# define O_SRVKEYFILE 0xb5
+#define O_SRVKEYFILE 0xb5
{ "Serverkeyfile", O_SRVKEYFILE, OI_NONE },
-# define O_CLTCERTFILE 0xb6
+#define O_CLTCERTFILE 0xb6
{ "ClientCertFile", O_CLTCERTFILE, OI_NONE },
-# define O_CLTKEYFILE 0xb7
+#define O_CLTKEYFILE 0xb7
{ "Clientkeyfile", O_CLTKEYFILE, OI_NONE },
-# define O_CACERTFILE 0xb8
+#define O_CACERTFILE 0xb8
{ "CACERTFile", O_CACERTFILE, OI_NONE },
-# define O_CACERTPATH 0xb9
+#define O_CACERTPATH 0xb9
{ "CACERTPath", O_CACERTPATH, OI_NONE },
-# define O_DHPARAMS 0xba
+#define O_DHPARAMS 0xba
{ "DHParameters", O_DHPARAMS, OI_NONE },
-#if _FFR_MILTER
#define O_INPUTMILTER 0xbb
{ "InputMailFilters", O_INPUTMILTER, OI_NONE },
#define O_MILTER 0xbc
{ "Milter", O_MILTER, OI_SUBOPT },
-#endif /* _FFR_MILTER */
#define O_SASLOPTS 0xbd
{ "AuthOptions", O_SASLOPTS, OI_NONE },
-#if _FFR_QUEUE_FILE_MODE
#define O_QUEUE_FILE_MODE 0xbe
{ "QueueFileMode", O_QUEUE_FILE_MODE, OI_NONE },
-#endif /* _FFR_QUEUE_FILE_MODE */
-# if _FFR_TLS_1
+#if _FFR_TLS_1
# define O_DHPARAMS5 0xbf
{ "DHParameters512", O_DHPARAMS5, OI_NONE },
# define O_CIPHERLIST 0xc0
{ "CipherList", O_CIPHERLIST, OI_NONE },
-# endif /* _FFR_TLS_1 */
-# define O_RANDFILE 0xc1
+#endif /* _FFR_TLS_1 */
+#define O_RANDFILE 0xc1
{ "RandFile", O_RANDFILE, OI_NONE },
+#define O_TLS_SRV_OPTS 0xc2
+ { "TLSSrvOptions", O_TLS_SRV_OPTS, OI_NONE },
+#define O_RCPTTHROT 0xc3
+ { "BadRcptThrottle", O_RCPTTHROT, OI_SAFE },
+#define O_DLVR_MIN 0xc4
+ { "DeliverByMin", O_DLVR_MIN, OI_NONE },
+#define O_MAXQUEUECHILDREN 0xc5
+ { "MaxQueueChildren", O_MAXQUEUECHILDREN, OI_NONE },
+#define O_MAXRUNNERSPERQUEUE 0xc6
+ { "MaxRunnersPerQueue", O_MAXRUNNERSPERQUEUE, OI_NONE },
+#define O_DIRECTSUBMODIFIERS 0xc7
+ { "DirectSubmissionModifiers", O_DIRECTSUBMODIFIERS, OI_NONE },
+#define O_NICEQUEUERUN 0xc8
+ { "NiceQueueRun", O_NICEQUEUERUN, OI_NONE },
+#define O_SHMKEY 0xc9
+ { "SharedMemoryKey", O_SHMKEY, OI_NONE },
+#define O_SASLBITS 0xca
+ { "AuthMaxBits", O_SASLBITS, OI_NONE },
+#define O_MBDB 0xcb
+ { "MailboxDatabase", O_MBDB, OI_NONE },
+#define O_MSQ 0xcc
+ { "UseMSP", O_MSQ, OI_NONE },
+#define O_DELAY_LA 0xcd
+ { "DelayLA", O_DELAY_LA, OI_NONE },
+#define O_FASTSPLIT 0xce
+ { "FastSplit", O_FASTSPLIT, OI_NONE },
+#if _FFR_SOFT_BOUNCE
+# define O_SOFTBOUNCE 0xcf
+ { "SoftBounce", O_SOFTBOUNCE, OI_NONE },
+#endif /* _FFR_SOFT_BOUNCE */
{ NULL, '\0', OI_NONE }
};
+# define CANONIFY(val)
+
+# define SET_OPT_DEFAULT(opt, val) opt = val
+
+/* set a string option by expanding the value and assigning it */
+/* WARNING this belongs ONLY into a case statement! */
+#define SET_STRING_EXP(str) \
+ expand(val, exbuf, sizeof exbuf, e); \
+ newval = sm_pstrdup_x(exbuf); \
+ if (str != NULL) \
+ sm_free(str); \
+ CANONIFY(newval); \
+ str = newval; \
+ break
+
+#define OPTNAME o->o_name == NULL ? "<unknown>" : o->o_name
+
void
setoption(opt, val, safe, sticky, e)
int opt;
@@ -1764,8 +2101,12 @@ setoption(opt, val, safe, sticky, e)
char buf[50];
extern bool Warn_Q_option;
#if _FFR_ALLOW_SASLINFO
- extern int SubmitMode;
+ extern unsigned int SubmitMode;
#endif /* _FFR_ALLOW_SASLINFO */
+#if STARTTLS
+ char *newval;
+ char exbuf[MAXLINE];
+#endif /* STARTTLS */
errno = 0;
if (opt == ' ')
@@ -1795,7 +2136,7 @@ setoption(opt, val, safe, sticky, e)
sel = NULL;
for (o = OptionTab; o->o_name != NULL; o++)
{
- if (strncasecmp(o->o_name, val, strlen(val)) != 0)
+ if (sm_strncasecmp(o->o_name, val, strlen(val)) != 0)
continue;
if (strlen(o->o_name) == strlen(val))
{
@@ -1839,27 +2180,29 @@ setoption(opt, val, safe, sticky, e)
if (o->o_code == opt)
break;
}
+ if (o->o_name == NULL)
+ {
+ syserr("readcf: unknown option name 0x%x", opt & 0xff);
+ return;
+ }
subopt = NULL;
}
if (subopt != NULL && !bitset(OI_SUBOPT, o->o_flags))
{
if (tTd(37, 1))
- dprintf("setoption: %s does not support suboptions, ignoring .%s\n",
- o->o_name == NULL ? "<unknown>" : o->o_name,
- subopt);
+ sm_dprintf("setoption: %s does not support suboptions, ignoring .%s\n",
+ OPTNAME, subopt);
subopt = NULL;
}
if (tTd(37, 1))
{
- dprintf(isascii(opt) && isprint(opt) ?
- "setoption %s (%c)%s%s=" :
- "setoption %s (0x%x)%s%s=",
- o->o_name == NULL ? "<unknown>" : o->o_name,
- opt,
- subopt == NULL ? "" : ".",
- subopt == NULL ? "" : subopt);
+ sm_dprintf(isascii(opt) && isprint(opt) ?
+ "setoption %s (%c)%s%s=" :
+ "setoption %s (0x%x)%s%s=",
+ OPTNAME, opt, subopt == NULL ? "" : ".",
+ subopt == NULL ? "" : subopt);
xputs(val);
}
@@ -1870,7 +2213,7 @@ setoption(opt, val, safe, sticky, e)
if (!sticky && bitnset(opt, StickyOpt))
{
if (tTd(37, 1))
- dprintf(" (ignored)\n");
+ sm_dprintf(" (ignored)\n");
return;
}
@@ -1879,7 +2222,7 @@ setoption(opt, val, safe, sticky, e)
*/
if (!safe && RealUid == 0)
- safe = TRUE;
+ safe = true;
if (!safe && !bitset(OI_SAFE, o->o_flags))
{
if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
@@ -1887,13 +2230,13 @@ setoption(opt, val, safe, sticky, e)
int dp;
if (tTd(37, 1))
- dprintf(" (unsafe)");
- dp = drop_privileges(TRUE);
+ sm_dprintf(" (unsafe)");
+ dp = drop_privileges(true);
setstat(dp);
}
}
if (tTd(37, 1))
- dprintf("\n");
+ sm_dprintf("\n");
switch (opt & 0xff)
{
@@ -1905,14 +2248,14 @@ setoption(opt, val, safe, sticky, e)
#if MIME8TO7
switch (*val)
{
- case 'm': /* convert 8-bit, convert MIME */
- MimeMode = MM_CVTMIME|MM_MIME8BIT;
- break;
-
case 'p': /* pass 8 bit, convert MIME */
MimeMode = MM_CVTMIME|MM_PASS8BIT;
break;
+ case 'm': /* convert 8-bit, convert MIME */
+ MimeMode = MM_CVTMIME|MM_MIME8BIT;
+ break;
+
case 's': /* strict adherence */
MimeMode = MM_CVTMIME;
break;
@@ -1937,23 +2280,30 @@ setoption(opt, val, safe, sticky, e)
default:
syserr("Unknown 8-bit mode %c", *val);
- finis(FALSE, EX_USAGE);
+ finis(false, true, EX_USAGE);
}
#else /* MIME8TO7 */
- printf("Warning: Option EightBitMode requires MIME8TO7 support\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: Option: %s requires MIME8TO7 support\n",
+ OPTNAME);
#endif /* MIME8TO7 */
break;
case 'A': /* set default alias file */
if (val[0] == '\0')
- setalias("aliases");
+ {
+ char *al;
+
+ SET_OPT_DEFAULT(al, "aliases");
+ setalias(al);
+ }
else
setalias(val);
break;
case 'a': /* look N minutes for "@:@" in alias file */
if (val[0] == '\0')
- SafeAlias = 5 * 60; /* five minutes */
+ SafeAlias = 5 MINUTES;
else
SafeAlias = convtime(val, 'm');
break;
@@ -1991,12 +2341,6 @@ setoption(opt, val, safe, sticky, e)
case SM_QUEUE: /* queue only */
case SM_DEFER: /* queue only and defer map lookups */
-#if !QUEUE
- syserr("need QUEUE to set -odqueue or -oddefer");
- break;
-#endif /* !QUEUE */
- /* FALLTHROUGH */
-
case SM_DELIVER: /* do everything */
case SM_FORK: /* fork after verification */
set_delivery_mode(*val, e);
@@ -2004,16 +2348,10 @@ setoption(opt, val, safe, sticky, e)
default:
syserr("Unknown delivery mode %c", *val);
- finis(FALSE, EX_USAGE);
+ finis(false, true, EX_USAGE);
}
break;
-#if !_FFR_REMOVE_AUTOREBUILD
- case 'D': /* rebuild alias database as needed */
- AutoRebuild = atobool(val);
- break;
-#endif /* !_FFR_REMOVE_AUTOREBUILD */
-
case 'E': /* error message header/header file */
if (*val != '\0')
ErrMsgFile = newstr(val);
@@ -2064,9 +2402,12 @@ setoption(opt, val, safe, sticky, e)
case 'H': /* help file */
if (val[0] == '\0')
- HelpFile = "helpfile";
+ {
+ SET_OPT_DEFAULT(HelpFile, "helpfile");
+ }
else
{
+ CANONIFY(val);
HelpFile = newstr(val);
}
break;
@@ -2087,9 +2428,9 @@ setoption(opt, val, safe, sticky, e)
p++;
if (*p == '\0')
break;
- clearmode = FALSE;
+ clearmode = false;
if (*p == '-')
- clearmode = TRUE;
+ clearmode = true;
else if (*p != '+')
p--;
p++;
@@ -2098,21 +2439,19 @@ setoption(opt, val, safe, sticky, e)
p++;
if (*p != '\0')
*p++ = '\0';
- if (strcasecmp(q, "HasWildcardMX") == 0)
+ if (sm_strcasecmp(q, "HasWildcardMX") == 0)
{
HasWildcardMX = !clearmode;
continue;
}
-#if _FFR_WORKAROUND_BROKEN_NAMESERVERS
if (sm_strcasecmp(q, "WorkAroundBrokenAAAA") == 0)
{
WorkAroundBrokenAAAA = !clearmode;
continue;
}
-#endif /* _FFR_WORKAROUND_BROKEN_NAMESERVERS */
for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
{
- if (strcasecmp(q, rfp->rf_name) == 0)
+ if (sm_strcasecmp(q, rfp->rf_name) == 0)
break;
}
if (rfp->rf_name == NULL)
@@ -2123,8 +2462,8 @@ setoption(opt, val, safe, sticky, e)
_res.options |= rfp->rf_bits;
}
if (tTd(8, 2))
- dprintf("_res.options = %x, HasWildcardMX = %d\n",
- (u_int) _res.options, HasWildcardMX);
+ sm_dprintf("_res.options = %x, HasWildcardMX = %d\n",
+ (unsigned int) _res.options, HasWildcardMX);
#else /* NAMED_BIND */
usrerr("name server (I option) specified but BIND not compiled in");
#endif /* NAMED_BIND */
@@ -2139,6 +2478,7 @@ setoption(opt, val, safe, sticky, e)
break;
case 'J': /* .forward search path */
+ CANONIFY(val);
ForwardPath = newstr(val);
break;
@@ -2162,14 +2502,14 @@ setoption(opt, val, safe, sticky, e)
break;
case 'M': /* define macro */
- sticky = FALSE;
- mid = macid(val, &ep);
+ sticky = false;
+ mid = macid_parse(val, &ep);
if (mid == 0)
break;
p = newstr(ep);
if (!safe)
cleanstrcpy(p, p, MAXNAME);
- define(mid, p, CurEnv);
+ macdefine(&CurEnv->e_macro, A_TEMP, mid, p);
break;
case 'm': /* send to me too */
@@ -2183,12 +2523,8 @@ setoption(opt, val, safe, sticky, e)
/* 'N' available -- was "net name" */
case 'O': /* daemon options */
-#if DAEMON
if (!setdaemonoptions(val))
syserr("too many daemons defined (%d max)", MAXDAEMONS);
-#else /* DAEMON */
- syserr("DaemonPortOptions (O option) set but DAEMON not compiled in");
-#endif /* DAEMON */
break;
case 'o': /* assume old style headers */
@@ -2217,7 +2553,7 @@ setoption(opt, val, safe, sticky, e)
for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
{
- if (strcasecmp(val, pv->pv_name) == 0)
+ if (sm_strcasecmp(val, pv->pv_name) == 0)
break;
}
if (pv->pv_name == NULL)
@@ -2225,7 +2561,7 @@ setoption(opt, val, safe, sticky, e)
else
PrivacyFlags |= pv->pv_flag;
}
- sticky = FALSE;
+ sticky = false;
break;
case 'P': /* postmaster copy address for returned mail */
@@ -2246,7 +2582,7 @@ setoption(opt, val, safe, sticky, e)
QueueDir = newstr(val);
}
if (RealUid != 0 && !safe)
- Warn_Q_option = TRUE;
+ Warn_Q_option = true;
break;
case 'R': /* don't prune routes */
@@ -2262,15 +2598,21 @@ setoption(opt, val, safe, sticky, e)
case 'S': /* status file */
if (val[0] == '\0')
- StatFile = "statistics";
+ {
+ SET_OPT_DEFAULT(StatFile, "statistics");
+ }
else
{
+ CANONIFY(val);
StatFile = newstr(val);
}
break;
case 's': /* be super safe, even if expensive */
- SuperSafe = atobool(val);
+ if (tolower(*val) == 'i')
+ SuperSafe = SAFE_INTERACTIVE;
+ else
+ SuperSafe = atobool(val) ? SAFE_REALLY : SAFE_NO;
break;
case 'T': /* queue timeout */
@@ -2294,7 +2636,11 @@ setoption(opt, val, safe, sticky, e)
case 'u': /* set default uid */
for (p = val; *p != '\0'; p++)
{
+#if _FFR_DOTTED_USERNAMES
+ if (*p == '/' || *p == ':')
+#else /* _FFR_DOTTED_USERNAMES */
if (*p == '.' || *p == '/' || *p == ':')
+#endif /* _FFR_DOTTED_USERNAMES */
{
*p++ = '\0';
break;
@@ -2324,14 +2670,14 @@ setoption(opt, val, safe, sticky, e)
}
}
-#ifdef UID_MAX
+# ifdef UID_MAX
if (DefUid > UID_MAX)
{
syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored",
- (long) DefUid, (long) UID_MAX);
+ (long)DefUid, (long)UID_MAX);
break;
}
-#endif /* UID_MAX */
+# endif /* UID_MAX */
/* handle the group if it is there */
if (*p == '\0')
@@ -2358,10 +2704,14 @@ setoption(opt, val, safe, sticky, e)
QueueLA = atoi(val);
break;
- case 'X': /* load avg at which to auto-reject connections */
+ case 'X': /* load avg at which to auto-reject connections */
RefuseLA = atoi(val);
break;
+ case O_DELAY_LA: /* load avg at which to delay connections */
+ DelayLA = atoi(val);
+ break;
+
case 'y': /* work recipient factor */
WkRecipFact = atoi(val);
break;
@@ -2382,11 +2732,21 @@ setoption(opt, val, safe, sticky, e)
case O_QUEUESORTORD: /* queue sorting order */
switch (*val)
{
+ case 'f': /* File Name */
+ case 'F':
+ QueueSortOrder = QSO_BYFILENAME;
+ break;
+
case 'h': /* Host first */
case 'H':
QueueSortOrder = QSO_BYHOST;
break;
+ case 'm': /* Modification time */
+ case 'M':
+ QueueSortOrder = QSO_BYMODTIME;
+ break;
+
case 'p': /* Priority order */
case 'P':
QueueSortOrder = QSO_BYPRIORITY;
@@ -2397,10 +2757,17 @@ setoption(opt, val, safe, sticky, e)
QueueSortOrder = QSO_BYTIME;
break;
- case 'f': /* File Name */
- case 'F':
- QueueSortOrder = QSO_BYFILENAME;
+ case 'r': /* Random */
+ case 'R':
+ QueueSortOrder = QSO_RANDOM;
+ break;
+
+#if _FFR_RHS
+ case 's': /* Shuffled host name */
+ case 'S':
+ QueueSortOrder = QSO_BYSHUFFLE;
break;
+#endif /* _FFR_RHS */
default:
syserr("Invalid queue sort order \"%s\"", val);
@@ -2445,6 +2812,7 @@ setoption(opt, val, safe, sticky, e)
#endif /* _FFR_QUEUEDELAY */
case O_HOSTSFILE: /* pathname of /etc/hosts file */
+ CANONIFY(val);
HostsFile = newstr(val);
break;
@@ -2453,10 +2821,11 @@ setoption(opt, val, safe, sticky, e)
break;
case O_DEFCHARSET: /* default character set for mimefying */
- DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
+ DefaultCharSet = newstr(denlstring(val, true, true));
break;
case O_SSFILE: /* service switch file */
+ CANONIFY(val);
ServiceSwitchFile = newstr(val);
break;
@@ -2465,15 +2834,15 @@ setoption(opt, val, safe, sticky, e)
break;
case O_NORCPTACTION: /* what to do if no recipient */
- if (strcasecmp(val, "none") == 0)
+ if (sm_strcasecmp(val, "none") == 0)
NoRecipientAction = NRA_NO_ACTION;
- else if (strcasecmp(val, "add-to") == 0)
+ else if (sm_strcasecmp(val, "add-to") == 0)
NoRecipientAction = NRA_ADD_TO;
- else if (strcasecmp(val, "add-apparently-to") == 0)
+ else if (sm_strcasecmp(val, "add-apparently-to") == 0)
NoRecipientAction = NRA_ADD_APPARENTLY_TO;
- else if (strcasecmp(val, "add-bcc") == 0)
+ else if (sm_strcasecmp(val, "add-bcc") == 0)
NoRecipientAction = NRA_ADD_BCC;
- else if (strcasecmp(val, "add-to-undisclosed") == 0)
+ else if (sm_strcasecmp(val, "add-to-undisclosed") == 0)
NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
else
syserr("Invalid NoRecipientAction: %s", val);
@@ -2492,13 +2861,41 @@ setoption(opt, val, safe, sticky, e)
break;
case O_MAXQUEUERUN: /* max # of jobs in a single queue run */
- MaxQueueRun = atol(val);
+ MaxQueueRun = atoi(val);
break;
case O_MAXCHILDREN: /* max # of children of daemon */
MaxChildren = atoi(val);
break;
+ case O_MAXQUEUECHILDREN: /* max # of children of daemon */
+ MaxQueueChildren = atoi(val);
+ break;
+
+ case O_MAXRUNNERSPERQUEUE: /* max # runners in a queue group */
+ MaxRunnersPerQueue = atoi(val);
+ break;
+
+ case O_NICEQUEUERUN: /* nice queue runs */
+#if !HASNICE
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: NiceQueueRun set on system that doesn't support nice()\n");
+#endif /* !HASNICE */
+
+ /* XXX do we want to check the range? > 0 ? */
+ NiceQueueRun = atoi(val);
+ break;
+
+ case O_SHMKEY : /* shared memory key */
+#if SM_CONF_SHM
+ ShmKey = atol(val);
+#else /* SM_CONF_SHM */
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: Option: %s requires shared memory support (-DSM_CONF_SHM)\n",
+ OPTNAME);
+#endif /* SM_CONF_SHM */
+ break;
+
#if _FFR_MAX_FORWARD_ENTRIES
case O_MAXFORWARD: /* max # of forward entries */
MaxForwardEntries = atoi(val);
@@ -2510,11 +2907,12 @@ setoption(opt, val, safe, sticky, e)
break;
case O_MUSTQUOTE: /* must quote these characters in phrases */
- (void) strlcpy(buf, "@,;:\\()[]", sizeof buf);
- if (strlen(val) < (SIZE_T) sizeof buf - 10)
- (void) strlcat(buf, val, sizeof buf);
+ (void) sm_strlcpy(buf, "@,;:\\()[]", sizeof buf);
+ if (strlen(val) < sizeof buf - 10)
+ (void) sm_strlcat(buf, val, sizeof buf);
else
- printf("Warning: MustQuoteChars too long, ignored.\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: MustQuoteChars too long, ignored.\n");
MustQuoteChars = newstr(buf);
break;
@@ -2528,7 +2926,8 @@ setoption(opt, val, safe, sticky, e)
case O_OPCHARS: /* operator characters (old $o macro) */
if (OperatorChars != NULL)
- printf("Warning: OperatorChars is being redefined.\n It should only be set before ruleset definitions.\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: OperatorChars is being redefined.\n It should only be set before ruleset definitions.\n");
OperatorChars = newstr(munchstring(val, NULL, '\0'));
break;
@@ -2559,15 +2958,13 @@ setoption(opt, val, safe, sticky, e)
break;
case O_DBLBOUNCE: /* address to which to send double bounces */
- if (val[0] != '\0')
- DoubleBounceAddr = newstr(val);
- else
- syserr("readcf: option DoubleBounceAddress: value required");
+ DoubleBounceAddr = newstr(val);
break;
case O_HSDIR: /* persistent host status directory */
if (val[0] != '\0')
{
+ CANONIFY(val);
HostStatDir = newstr(val);
}
break;
@@ -2579,7 +2976,11 @@ setoption(opt, val, safe, sticky, e)
case O_RUNASUSER: /* run bulk of code as this user */
for (p = val; *p != '\0'; p++)
{
+#if _FFR_DOTTED_USERNAMES
+ if (*p == '/' || *p == ':')
+#else /* _FFR_DOTTED_USERNAMES */
if (*p == '.' || *p == '/' || *p == ':')
+#endif /* _FFR_DOTTED_USERNAMES */
{
*p++ = '\0';
break;
@@ -2607,21 +3008,37 @@ setoption(opt, val, safe, sticky, e)
RunAsUid = pw->pw_uid;
RunAsGid = pw->pw_gid;
}
+ else if (EffGid == pw->pw_gid)
+ RunAsGid = pw->pw_gid;
+ else if (UseMSP && *p == '\0')
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "WARNING: RunAsGid for MSP ignored, check group ids (egid=%d, want=%d)\n",
+ (int) EffGid,
+ (int) pw->pw_gid);
}
-#ifdef UID_MAX
+# ifdef UID_MAX
if (RunAsUid > UID_MAX)
{
syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored",
(long) RunAsUid, (long) UID_MAX);
break;
}
-#endif /* UID_MAX */
+# endif /* UID_MAX */
if (*p != '\0')
{
if (isascii(*p) && isdigit(*p))
{
- if (can_setuid)
- RunAsGid = atoi(p);
+ gid_t runasgid;
+
+ runasgid = (gid_t) atoi(p);
+ if (can_setuid || EffGid == runasgid)
+ RunAsGid = runasgid;
+ else if (UseMSP)
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "WARNING: RunAsGid for MSP ignored, check group ids (egid=%d, want=%d)\n",
+ (int) EffGid,
+ (int) runasgid);
}
else
{
@@ -2631,13 +3048,19 @@ setoption(opt, val, safe, sticky, e)
if (gr == NULL)
syserr("readcf: option RunAsUser: unknown group %s",
p);
- else if (can_setuid)
+ else if (can_setuid || EffGid == gr->gr_gid)
RunAsGid = gr->gr_gid;
+ else if (UseMSP)
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "WARNING: RunAsGid for MSP ignored, check group ids (egid=%d, want=%d)\n",
+ (int) EffGid,
+ (int) gr->gr_gid);
}
}
if (tTd(47, 5))
- dprintf("readcf: RunAsUser = %d:%d\n",
- (int)RunAsUid, (int)RunAsGid);
+ sm_dprintf("readcf: RunAsUser = %d:%d\n",
+ (int) RunAsUid, (int) RunAsGid);
break;
case O_DSN_RRT:
@@ -2645,12 +3068,10 @@ setoption(opt, val, safe, sticky, e)
break;
case O_PIDFILE:
- if (PidFile != NULL)
- sm_free(PidFile);
- PidFile = newstr(val);
+ PSTRSET(PidFile, val);
break;
- case O_DONTBLAMESENDMAIL:
+ case O_DONTBLAMESENDMAIL:
p = val;
for (;;)
{
@@ -2670,7 +3091,7 @@ setoption(opt, val, safe, sticky, e)
for (dbs = DontBlameSendmailValues;
dbs->dbs_name != NULL; dbs++)
{
- if (strcasecmp(val, dbs->dbs_name) == 0)
+ if (sm_strcasecmp(val, dbs->dbs_name) == 0)
break;
}
if (dbs->dbs_name == NULL)
@@ -2680,21 +3101,29 @@ setoption(opt, val, safe, sticky, e)
else
setbitn(dbs->dbs_flag, DontBlameSendmail);
}
- sticky = FALSE;
+ sticky = false;
break;
case O_DPI:
- DontProbeInterfaces = atobool(val);
+ if (sm_strcasecmp(val, "loopback") == 0)
+ DontProbeInterfaces = DPI_SKIPLOOPBACK;
+ else if (atobool(val))
+ DontProbeInterfaces = DPI_PROBENONE;
+ else
+ DontProbeInterfaces = DPI_PROBEALL;
break;
case O_MAXRCPT:
MaxRcptPerMsg = atoi(val);
break;
+ case O_RCPTTHROT:
+ BadRcptThrottle = atoi(val);
+ break;
+
case O_DEADLETTER:
- if (DeadLetterDrop != NULL)
- sm_free(DeadLetterDrop);
- DeadLetterDrop = newstr(val);
+ CANONIFY(val);
+ PSTRSET(DeadLetterDrop, val);
break;
#if _FFR_DONTLOCKFILESFORREAD_OPTION
@@ -2710,26 +3139,36 @@ setoption(opt, val, safe, sticky, e)
case O_CNCTONLYTO:
/* XXX should probably use gethostbyname */
#if NETINET || NETINET6
+ ConnectOnlyTo.sa.sa_family = AF_UNSPEC;
# if NETINET6
- if (inet_addr(val) == INADDR_NONE)
- {
+ if (anynet_pton(AF_INET6, val,
+ &ConnectOnlyTo.sin6.sin6_addr) != 1)
ConnectOnlyTo.sa.sa_family = AF_INET6;
- if (inet_pton(AF_INET6, val,
- &ConnectOnlyTo.sin6.sin6_addr) != 1)
- syserr("readcf: option ConnectOnlyTo: invalid IP address %s",
- val);
- }
else
# endif /* NETINET6 */
+# if NETINET
{
- ConnectOnlyTo.sa.sa_family = AF_INET;
ConnectOnlyTo.sin.sin_addr.s_addr = inet_addr(val);
+ if (ConnectOnlyTo.sin.sin_addr.s_addr != INADDR_NONE)
+ ConnectOnlyTo.sa.sa_family = AF_INET;
+ }
+
+# endif /* NETINET */
+ if (ConnectOnlyTo.sa.sa_family == AF_UNSPEC)
+ {
+ syserr("readcf: option ConnectOnlyTo: invalid IP address %s",
+ val);
+ break;
}
#endif /* NETINET || NETINET6 */
break;
case O_TRUSTUSER:
-#if HASFCHOWN
+# if !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING)
+ if (!UseMSP)
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "readcf: option TrustedUser may cause problems on systems\n which do not support fchown() if UseMSP is not set.\n");
+# endif /* !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING) */
if (isascii(*val) && isdigit(*val))
TrustedUid = atoi(val);
else
@@ -2755,9 +3194,6 @@ setoption(opt, val, safe, sticky, e)
TrustedUid = 0;
}
# endif /* UID_MAX */
-#else /* HASFCHOWN */
- syserr("readcf: option TrustedUser: can not be used on systems which do not support fchown()");
-#endif /* HASFCHOWN */
break;
case O_MAXMIMEHDRLEN:
@@ -2773,18 +3209,18 @@ setoption(opt, val, safe, sticky, e)
if (MaxMimeHeaderLength < 0)
MaxMimeHeaderLength = 0;
else if (MaxMimeHeaderLength < 128)
- printf("Warning: MaxMimeHeaderLength: header length limit set lower than 128\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: MaxMimeHeaderLength: header length limit set lower than 128\n");
if (MaxMimeFieldLength < 0)
MaxMimeFieldLength = 0;
else if (MaxMimeFieldLength < 40)
- printf("Warning: MaxMimeHeaderLength: field length limit set lower than 40\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: MaxMimeHeaderLength: field length limit set lower than 40\n");
break;
case O_CONTROLSOCKET:
- if (ControlSocketName != NULL)
- sm_free(ControlSocketName);
- ControlSocketName = newstr(val);
+ PSTRSET(ControlSocketName, val);
break;
case O_MAXHDRSLEN:
@@ -2792,20 +3228,23 @@ setoption(opt, val, safe, sticky, e)
if (MaxHeadersLength > 0 &&
MaxHeadersLength < (MAXHDRSLEN / 2))
- printf("Warning: MaxHeadersLength: headers length limit set lower than %d\n", (MAXHDRSLEN / 2));
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: MaxHeadersLength: headers length limit set lower than %d\n",
+ (MAXHDRSLEN / 2));
break;
case O_PROCTITLEPREFIX:
- if (ProcTitlePrefix != NULL)
- sm_free(ProcTitlePrefix);
- ProcTitlePrefix = newstr(val);
+ PSTRSET(ProcTitlePrefix, val);
break;
#if SASL
case O_SASLINFO:
-#if _FFR_ALLOW_SASLINFO
+# if _FFR_ALLOW_SASLINFO
/*
- ** Allow users to select their own authinfo file.
+ ** Allow users to select their own authinfo file
+ ** under certain circumstances, otherwise just ignore
+ ** the option. If the option isn't ignored, several
+ ** commands don't work very well, e.g., mailq.
** However, this is not a "perfect" solution.
** If mail is queued, the authentication info
** will not be used in subsequent delivery attempts.
@@ -2814,22 +3253,14 @@ setoption(opt, val, safe, sticky, e)
*/
if (!bitset(SUBMIT_MSA, SubmitMode) && RealUid != 0 &&
RunAsUid != RealUid)
- {
- errno = 0;
- syserr("Error: %s only allowed with -U\n",
- o->o_name == NULL ? "<unknown>" : o->o_name);
- ExitStat = EX_USAGE;
break;
- }
-#endif /* _FFR_ALLOW_SASLINFO */
- if (SASLInfo != NULL)
- sm_free(SASLInfo);
- SASLInfo = newstr(val);
+# endif /* _FFR_ALLOW_SASLINFO */
+ PSTRSET(SASLInfo, val);
break;
case O_SASLMECH:
if (AuthMechanisms != NULL)
- sm_free(AuthMechanisms);
+ sm_free(AuthMechanisms); /* XXX */
if (*val != '\0')
AuthMechanisms = newstr(val);
else
@@ -2839,12 +3270,11 @@ setoption(opt, val, safe, sticky, e)
case O_SASLOPTS:
while (val != NULL && *val != '\0')
{
- switch(*val)
+ switch (*val)
{
case 'A':
SASLOpts |= SASL_AUTH_AUTH;
break;
-# if _FFR_SASL_OPTS
case 'a':
SASLOpts |= SASL_SEC_NOACTIVE;
break;
@@ -2863,13 +3293,17 @@ setoption(opt, val, safe, sticky, e)
case 'y':
SASLOpts |= SASL_SEC_NOANONYMOUS;
break;
-# endif /* _FFR_SASL_OPTS */
+ case ' ': /* ignore */
+ case '\t': /* ignore */
+ case ',': /* ignore */
+ break;
default:
- printf("Warning: Option: %s unknown parameter '%c'\n",
- o->o_name == NULL ? "<unknown>"
- : o->o_name,
- (isascii(*val) && isprint(*val)) ? *val
- : '?');
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: Option: %s unknown parameter '%c'\n",
+ OPTNAME,
+ (isascii(*val) &&
+ isprint(*val))
+ ? *val : '?');
break;
}
++val;
@@ -2878,80 +3312,99 @@ setoption(opt, val, safe, sticky, e)
++val;
}
break;
+ case O_SASLBITS:
+ MaxSLBits = atoi(val);
+ break;
#else /* SASL */
case O_SASLINFO:
case O_SASLMECH:
case O_SASLOPTS:
- printf("Warning: Option: %s requires SASL support (-DSASL)\n",
- o->o_name == NULL ? "<unknown>" : o->o_name);
+ case O_SASLBITS:
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: Option: %s requires SASL support (-DSASL)\n",
+ OPTNAME);
break;
#endif /* SASL */
#if STARTTLS
case O_SRVCERTFILE:
- if (SrvCERTfile != NULL)
- sm_free(SrvCERTfile);
- SrvCERTfile = newstr(val);
- break;
-
+ SET_STRING_EXP(SrvCERTfile);
case O_SRVKEYFILE:
- if (Srvkeyfile != NULL)
- sm_free(Srvkeyfile);
- Srvkeyfile = newstr(val);
- break;
-
+ SET_STRING_EXP(Srvkeyfile);
case O_CLTCERTFILE:
- if (CltCERTfile != NULL)
- sm_free(CltCERTfile);
- CltCERTfile = newstr(val);
- break;
-
+ SET_STRING_EXP(CltCERTfile);
case O_CLTKEYFILE:
- if (Cltkeyfile != NULL)
- sm_free(Cltkeyfile);
- Cltkeyfile = newstr(val);
- break;
-
+ SET_STRING_EXP(Cltkeyfile);
case O_CACERTFILE:
- if (CACERTfile != NULL)
- sm_free(CACERTfile);
- CACERTfile = newstr(val);
- break;
-
+ SET_STRING_EXP(CACERTfile);
case O_CACERTPATH:
- if (CACERTpath != NULL)
- sm_free(CACERTpath);
- CACERTpath = newstr(val);
- break;
-
+ SET_STRING_EXP(CACERTpath);
case O_DHPARAMS:
- if (DHParams != NULL)
- sm_free(DHParams);
- DHParams = newstr(val);
- break;
-
-# if _FFR_TLS_1
+ SET_STRING_EXP(DHParams);
+# if _FFR_TLS_1
case O_DHPARAMS5:
- if (DHParams5 != NULL)
- sm_free(DHParams5);
- DHParams5 = newstr(val);
- break;
-
+ SET_STRING_EXP(DHParams5);
case O_CIPHERLIST:
- if (CipherList != NULL)
- sm_free(CipherList);
- CipherList = newstr(val);
+ SET_STRING_EXP(CipherList);
+# endif /* _FFR_TLS_1 */
+
+ /*
+ ** XXX How about options per daemon/client instead of globally?
+ ** This doesn't work well for some options, e.g., no server cert,
+ ** but fine for others.
+ **
+ ** XXX Some people may want different certs per server.
+ **
+ ** See also srvfeatures()
+ */
+
+ case O_TLS_SRV_OPTS:
+ while (val != NULL && *val != '\0')
+ {
+ switch (*val)
+ {
+ case 'V':
+ TLS_Srv_Opts |= TLS_I_NO_VRFY;
+ break;
+# if _FFR_TLS_1
+ /*
+ ** Server without a cert? That works only if
+ ** AnonDH is enabled as cipher, which is not in the
+ ** default list. Hence the CipherList option must
+ ** be available. Moreover: which clients support this
+ ** besides sendmail with this setting?
+ */
+
+ case 'C':
+ TLS_Srv_Opts &= ~TLS_I_SRV_CERT;
+ break;
+# endif /* _FFR_TLS_1 */
+ case ' ': /* ignore */
+ case '\t': /* ignore */
+ case ',': /* ignore */
+ break;
+ default:
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: Option: %s unknown parameter '%c'\n",
+ OPTNAME,
+ (isascii(*val) &&
+ isprint(*val))
+ ? *val : '?');
+ break;
+ }
+ ++val;
+ val = strpbrk(val, ", \t");
+ if (val != NULL)
+ ++val;
+ }
break;
-# endif /* _FFR_TLS_1 */
case O_RANDFILE:
- if (RandFile != NULL)
- sm_free(RandFile);
- RandFile= newstr(val);
+ PSTRSET(RandFile, val);
break;
-# else /* STARTTLS */
+#else /* STARTTLS */
case O_SRVCERTFILE:
case O_SRVKEYFILE:
case O_CLTCERTFILE:
@@ -2959,23 +3412,20 @@ setoption(opt, val, safe, sticky, e)
case O_CACERTFILE:
case O_CACERTPATH:
case O_DHPARAMS:
-# if _FFR_TLS_1
+# if _FFR_TLS_1
case O_DHPARAMS5:
case O_CIPHERLIST:
-# endif /* _FFR_TLS_1 */
+# endif /* _FFR_TLS_1 */
case O_RANDFILE:
- printf("Warning: Option: %s requires TLS support\n",
- o->o_name == NULL ? "<unknown>" : o->o_name);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: Option: %s requires TLS support\n",
+ OPTNAME);
break;
-# endif /* STARTTLS */
+#endif /* STARTTLS */
case O_CLIENTPORT:
-#if DAEMON
setclientoptions(val);
-#else /* DAEMON */
- syserr("ClientPortOptions (O option) set but DAEMON not compiled in");
-#endif /* DAEMON */
break;
case O_DF_BUFSIZE:
@@ -2987,37 +3437,80 @@ setoption(opt, val, safe, sticky, e)
break;
case O_LDAPDEFAULTSPEC:
-#ifdef LDAPMAP
+#if LDAPMAP
ldapmap_set_defaults(val);
#else /* LDAPMAP */
- printf("Warning: Option: %s requires LDAP support (-DLDAPMAP)\n",
- o->o_name == NULL ? "<unknown>" : o->o_name);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: Option: %s requires LDAP support (-DLDAPMAP)\n",
+ OPTNAME);
#endif /* LDAPMAP */
break;
-#if _FFR_MILTER
case O_INPUTMILTER:
+#if MILTER
InputFilterList = newstr(val);
+#else /* MILTER */
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: Option: %s requires Milter support (-DMILTER)\n",
+ OPTNAME);
+#endif /* MILTER */
break;
case O_MILTER:
+#if MILTER
milter_set_option(subopt, val, sticky);
+#else /* MILTER */
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: Option: %s requires Milter support (-DMILTER)\n",
+ OPTNAME);
+#endif /* MILTER */
break;
-#endif /* _FFR_MILTER */
-#if _FFR_QUEUE_FILE_MODE
case O_QUEUE_FILE_MODE: /* queue file mode */
QueueFileMode = atooct(val) & 0777;
break;
-#endif /* _FFR_QUEUE_FILE_MODE */
+
+ case O_DLVR_MIN: /* deliver by minimum time */
+ DeliverByMin = convtime(val, 's');
+ break;
+
+ /* modifiers {daemon_flags} for direct submissions */
+ case O_DIRECTSUBMODIFIERS:
+ {
+ BITMAP256 m; /* ignored */
+ extern ENVELOPE BlankEnvelope;
+
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{daemon_flags}"),
+ getmodifiers(val, m));
+ }
+ break;
+
+ case O_FASTSPLIT:
+ FastSplit = atoi(val);
+ break;
+
+ case O_MBDB:
+ Mbdb = newstr(val);
+ break;
+
+ case O_MSQ:
+ UseMSP = atobool(val);
+ break;
+
+#if _FFR_SOFT_BOUNCE
+ case O_SOFTBOUNCE:
+ SoftBounce = atobool(val);
+ break;
+#endif /* _FFR_SOFT_BOUNCE */
default:
if (tTd(37, 1))
{
if (isascii(opt) && isprint(opt))
- dprintf("Warning: option %c unknown\n", opt);
+ sm_dprintf("Warning: option %c unknown\n", opt);
else
- dprintf("Warning: option 0x%x unknown\n", opt);
+ sm_dprintf("Warning: option 0x%x unknown\n", opt);
}
break;
}
@@ -3033,7 +3526,7 @@ setoption(opt, val, safe, sticky, e)
if (sticky && !bitset(OI_SUBOPT, o->o_flags))
setbitn(opt, StickyOpt);
}
- /*
+/*
** SETCLASS -- set a string into a class
**
** Parameters:
@@ -3059,25 +3552,25 @@ setclass(class, str)
int mid;
str++;
- mid = macid(str, NULL);
+ mid = macid(str);
if (mid == 0)
return;
if (tTd(37, 8))
- dprintf("setclass(%s, $=%s)\n",
- macname(class), macname(mid));
+ sm_dprintf("setclass(%s, $=%s)\n",
+ macname(class), macname(mid));
copy_class(mid, class);
}
else
{
if (tTd(37, 8))
- dprintf("setclass(%s, %s)\n", macname(class), str);
+ sm_dprintf("setclass(%s, %s)\n", macname(class), str);
s = stab(str, ST_CLASS, ST_ENTER);
setbitn(bitidx(class), s->s_class);
}
}
- /*
+/*
** MAKEMAPENTRY -- create a map entry
**
** Parameters:
@@ -3133,7 +3626,8 @@ makemapentry(line)
class = stab(classname, ST_MAPCLASS, ST_FIND);
if (class == NULL)
{
- syserr("readcf: map %s: class %s not available", mapname, classname);
+ syserr("readcf: map %s: class %s not available", mapname,
+ classname);
return NULL;
}
@@ -3147,19 +3641,16 @@ makemapentry(line)
if (tTd(37, 5))
{
- dprintf("map %s, class %s, flags %lx, file %s,\n",
- s->s_map.map_mname, s->s_map.map_class->map_cname,
- s->s_map.map_mflags,
- s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
- dprintf("\tapp %s, domain %s, rebuild %s\n",
- s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
- s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
- s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
+ sm_dprintf("map %s, class %s, flags %lx, file %s,\n",
+ s->s_map.map_mname, s->s_map.map_class->map_cname,
+ s->s_map.map_mflags, s->s_map.map_file);
+ sm_dprintf("\tapp %s, domain %s, rebuild %s\n",
+ s->s_map.map_app, s->s_map.map_domain,
+ s->s_map.map_rebuild);
}
-
return &s->s_map;
}
- /*
+/*
** STRTORWSET -- convert string to rewriting set number
**
** Parameters:
@@ -3277,7 +3768,7 @@ strtorwset(p, endp, stabmode)
char *h = NULL;
if (RuleSetNames[ruleset] != NULL)
- sm_free(RuleSetNames[ruleset]);
+ sm_free(RuleSetNames[ruleset]); /* XXX */
if (delim != '\0' && (h = strchr(q, delim)) != NULL)
*h = '\0';
RuleSetNames[ruleset] = newstr(q);
@@ -3287,7 +3778,7 @@ strtorwset(p, endp, stabmode)
}
return ruleset;
}
- /*
+/*
** SETTIMEOUT -- set an individual timeout
**
** Parameters:
@@ -3305,8 +3796,8 @@ static BITMAP256 StickyTimeoutOpt;
static struct timeoutinfo
{
- char *to_name; /* long name of timeout */
- u_char to_code; /* code for option */
+ char *to_name; /* long name of timeout */
+ unsigned char to_code; /* code for option */
} TimeOutTab[] =
{
#define TO_INITIAL 0x01
@@ -3373,6 +3864,14 @@ static struct timeoutinfo
{ "resolver.retry.first", TO_RESOLVER_RETRY_FIRST },
#define TO_CONTROL 0x1F
{ "control", TO_CONTROL },
+#define TO_LHLO 0x20
+ { "lhlo", TO_LHLO },
+#define TO_AUTH 0x21
+ { "auth", TO_AUTH },
+#define TO_STARTTLS 0x22
+ { "starttls", TO_STARTTLS },
+#define TO_ACONNECT 0x23
+ { "aconnect", TO_ACONNECT },
{ NULL, 0 },
};
@@ -3384,16 +3883,15 @@ settimeout(name, val, sticky)
bool sticky;
{
register struct timeoutinfo *to;
- int i;
- int addopts;
+ int i, addopts;
time_t toval;
if (tTd(37, 2))
- dprintf("settimeout(%s = %s)", name, val);
+ sm_dprintf("settimeout(%s = %s)", name, val);
for (to = TimeOutTab; to->to_name != NULL; to++)
{
- if (strcasecmp(to->to_name, name) == 0)
+ if (sm_strcasecmp(to->to_name, name) == 0)
break;
}
@@ -3411,12 +3909,12 @@ settimeout(name, val, sticky)
if (!sticky && bitnset(to->to_code, StickyTimeoutOpt))
{
if (tTd(37, 2))
- dprintf(" (ignored)\n");
+ sm_dprintf(" (ignored)\n");
return;
}
if (tTd(37, 2))
- dprintf("\n");
+ sm_dprintf("\n");
toval = convtime(val, 'm');
addopts = 0;
@@ -3483,6 +3981,10 @@ settimeout(name, val, sticky)
TimeOuts.to_iconnect = toval;
break;
+ case TO_ACONNECT:
+ TimeOuts.to_aconnect = toval;
+ break;
+
case TO_QUEUEWARN:
toval = convtime(val, 'h');
TimeOuts.to_q_warning[TOC_NORMAL] = toval;
@@ -3570,6 +4072,22 @@ settimeout(name, val, sticky)
TimeOuts.to_control = toval;
break;
+ case TO_LHLO:
+ TimeOuts.to_lhlo = toval;
+ break;
+
+#if SASL
+ case TO_AUTH:
+ TimeOuts.to_auth = toval;
+ break;
+#endif /* SASL */
+
+#if STARTTLS
+ case TO_STARTTLS:
+ TimeOuts.to_starttls = toval;
+ break;
+#endif /* STARTTLS */
+
default:
syserr("settimeout: invalid timeout %s", name);
break;
@@ -3581,7 +4099,7 @@ settimeout(name, val, sticky)
setbitn(to->to_code + i, StickyTimeoutOpt);
}
}
- /*
+/*
** INITTIMEOUTS -- parse and set timeout values
**
** Parameters:
@@ -3605,10 +4123,11 @@ inittimeouts(val, sticky)
register char *p;
if (tTd(37, 2))
- dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val);
+ sm_dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val);
if (val == NULL)
{
TimeOuts.to_connect = (time_t) 0 SECONDS;
+ TimeOuts.to_aconnect = (time_t) 0 SECONDS;
TimeOuts.to_initial = (time_t) 5 MINUTES;
TimeOuts.to_helo = (time_t) 5 MINUTES;
TimeOuts.to_mail = (time_t) 10 MINUTES;
@@ -3627,24 +4146,44 @@ inittimeouts(val, sticky)
#endif /* IDENTPROTO */
TimeOuts.to_fileopen = (time_t) 60 SECONDS;
TimeOuts.to_control = (time_t) 2 MINUTES;
+ TimeOuts.to_lhlo = (time_t) 2 MINUTES;
+#if SASL
+ TimeOuts.to_auth = (time_t) 10 MINUTES;
+#endif /* SASL */
+#if STARTTLS
+ TimeOuts.to_starttls = (time_t) 1 HOUR;
+#endif /* STARTTLS */
if (tTd(37, 5))
{
- dprintf("Timeouts:\n");
- dprintf(" connect = %ld\n", (long)TimeOuts.to_connect);
- dprintf(" initial = %ld\n", (long)TimeOuts.to_initial);
- dprintf(" helo = %ld\n", (long)TimeOuts.to_helo);
- dprintf(" mail = %ld\n", (long)TimeOuts.to_mail);
- dprintf(" rcpt = %ld\n", (long)TimeOuts.to_rcpt);
- dprintf(" datainit = %ld\n", (long)TimeOuts.to_datainit);
- dprintf(" datablock = %ld\n", (long)TimeOuts.to_datablock);
- dprintf(" datafinal = %ld\n", (long)TimeOuts.to_datafinal);
- dprintf(" rset = %ld\n", (long)TimeOuts.to_rset);
- dprintf(" quit = %ld\n", (long)TimeOuts.to_quit);
- dprintf(" nextcommand = %ld\n", (long)TimeOuts.to_nextcommand);
- dprintf(" miscshort = %ld\n", (long)TimeOuts.to_miscshort);
- dprintf(" ident = %ld\n", (long)TimeOuts.to_ident);
- dprintf(" fileopen = %ld\n", (long)TimeOuts.to_fileopen);
- dprintf(" control = %ld\n", (long)TimeOuts.to_control);
+ sm_dprintf("Timeouts:\n");
+ sm_dprintf(" connect = %ld\n",
+ (long) TimeOuts.to_connect);
+ sm_dprintf(" aconnect = %ld\n",
+ (long) TimeOuts.to_aconnect);
+ sm_dprintf(" initial = %ld\n",
+ (long) TimeOuts.to_initial);
+ sm_dprintf(" helo = %ld\n", (long) TimeOuts.to_helo);
+ sm_dprintf(" mail = %ld\n", (long) TimeOuts.to_mail);
+ sm_dprintf(" rcpt = %ld\n", (long) TimeOuts.to_rcpt);
+ sm_dprintf(" datainit = %ld\n",
+ (long) TimeOuts.to_datainit);
+ sm_dprintf(" datablock = %ld\n",
+ (long) TimeOuts.to_datablock);
+ sm_dprintf(" datafinal = %ld\n",
+ (long) TimeOuts.to_datafinal);
+ sm_dprintf(" rset = %ld\n", (long) TimeOuts.to_rset);
+ sm_dprintf(" quit = %ld\n", (long) TimeOuts.to_quit);
+ sm_dprintf(" nextcommand = %ld\n",
+ (long) TimeOuts.to_nextcommand);
+ sm_dprintf(" miscshort = %ld\n",
+ (long) TimeOuts.to_miscshort);
+ sm_dprintf(" ident = %ld\n", (long) TimeOuts.to_ident);
+ sm_dprintf(" fileopen = %ld\n",
+ (long) TimeOuts.to_fileopen);
+ sm_dprintf(" lhlo = %ld\n",
+ (long) TimeOuts.to_lhlo);
+ sm_dprintf(" control = %ld\n",
+ (long) TimeOuts.to_control);
}
return;
}
diff --git a/contrib/sendmail/src/recipient.c b/contrib/sendmail/src/recipient.c
index bfed632..378088a 100644
--- a/contrib/sendmail/src/recipient.c
+++ b/contrib/sendmail/src/recipient.c
@@ -11,15 +11,125 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: recipient.c,v 8.231.14.11 2001/05/03 17:24:14 gshapiro Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
+SM_RCSID("@(#)$Id: recipient.c,v 8.327 2001/11/20 13:59:53 ca Exp $")
static void includetimeout __P((void));
static ADDRESS *self_reference __P((ADDRESS *));
+static int sortexpensive __P((ADDRESS *, ADDRESS *));
+static int sortbysignature __P((ADDRESS *, ADDRESS *));
+static int sorthost __P((ADDRESS *, ADDRESS *));
+
+typedef int sortfn_t __P((ADDRESS *, ADDRESS *));
+
+/*
+** SORTHOST -- strcmp()-like func for host portion of an ADDRESS
+**
+** Parameters:
+** xx -- first ADDRESS
+** yy -- second ADDRESS
+**
+** Returns:
+** <0 when xx->q_host is less than yy->q_host
+** >0 when xx->q_host is greater than yy->q_host
+** 0 when equal
+*/
+
+static int
+sorthost(xx, yy)
+ register ADDRESS *xx;
+ register ADDRESS *yy;
+{
+#if _FFR_HOST_SORT_REVERSE
+ /* XXX maybe compare hostnames from the end? */
+ return sm_strrevcasecmp(xx->q_host, yy->q_host);
+#else /* _FFR_HOST_SORT_REVERSE */
+ return sm_strcasecmp(xx->q_host, yy->q_host);
+#endif /* _FFR_HOST_SORT_REVERSE */
+}
+
+/*
+** SORTEXPENSIVE -- strcmp()-like func for expensive mailers
+**
+** The mailer has been noted already as "expensive" for 'xx'. This
+** will give a result relative to 'yy'. Expensive mailers get rated
+** "greater than" non-expensive mailers because during the delivery phase
+** it will get queued -- no use it getting in the way of less expensive
+** recipients. We avoid an MX RR lookup when both 'xx' and 'yy' are
+** expensive since an MX RR lookup happens when extracted from the queue
+** later.
+**
+** Parameters:
+** xx -- first ADDRESS
+** yy -- second ADDRESS
+**
+** Returns:
+** <0 when xx->q_host is less than yy->q_host and both are
+** expensive
+** >0 when xx->q_host is greater than yy->q_host, or when
+** 'yy' is non-expensive
+** 0 when equal (by expense and q_host)
+*/
+
+static int
+sortexpensive(xx, yy)
+ ADDRESS *xx;
+ ADDRESS *yy;
+{
+ if (!bitnset(M_EXPENSIVE, yy->q_mailer->m_flags))
+ return 1; /* xx should go later */
+#if _FFR_HOST_SORT_REVERSE
+ /* XXX maybe compare hostnames from the end? */
+ return sm_strrevcasecmp(xx->q_host, yy->q_host);
+#else /* _FFR_HOST_SORT_REVERSE */
+ return sm_strcasecmp(xx->q_host, yy->q_host);
+#endif /* _FFR_HOST_SORT_REVERSE */
+}
+
+/*
+** SORTBYSIGNATURE -- a strcmp()-like func for q_mailer and q_host in ADDRESS
+**
+** Parameters:
+** xx -- first ADDRESS
+** yy -- second ADDRESS
+**
+** Returns:
+** 0 when the "signature"'s are same
+** <0 when xx->q_signature is less than yy->q_signature
+** >0 when xx->q_signature is greater than yy->q_signature
+**
+** Side Effect:
+** May set ADDRESS pointer for q_signature if not already set.
+*/
+
+static int
+sortbysignature(xx, yy)
+ ADDRESS *xx;
+ ADDRESS *yy;
+{
+ register int ret;
+
+ /* Let's avoid redoing the signature over and over again */
+ if (xx->q_signature == NULL)
+ xx->q_signature = hostsignature(xx->q_mailer, xx->q_host);
+ if (yy->q_signature == NULL)
+ yy->q_signature = hostsignature(yy->q_mailer, yy->q_host);
+ ret = strcmp(xx->q_signature, yy->q_signature);
+
+ /*
+ ** If the two signatures are the same then we will return a sort
+ ** value based on 'q_user'. But note that we have reversed xx and yy
+ ** on purpose. This additional compare helps reduce the number of
+ ** sameaddr() calls and loops in recipient() for the case when
+ ** the rcpt list has been provided already in-order.
+ */
+
+ if (ret == 0)
+ return strcmp(yy->q_user, xx->q_user);
+ else
+ return ret;
+}
/*
** SENDTOLIST -- Designate a send list.
@@ -41,9 +151,6 @@ static ADDRESS *self_reference __P((ADDRESS *));
**
** Returns:
** The number of addresses actually on the list.
-**
-** Side Effects:
-** none.
*/
/* q_flags bits inherited from ctladdr */
@@ -58,12 +165,12 @@ sendtolist(list, ctladdr, sendq, aliaslevel, e)
register ENVELOPE *e;
{
register char *p;
- register ADDRESS *al; /* list of addresses to send to */
- char delimiter; /* the address delimiter */
- int naddrs;
- int i;
+ register ADDRESS *SM_NONVOLATILE al; /* list of addresses to send to */
+ SM_NONVOLATILE char delimiter; /* the address delimiter */
+ SM_NONVOLATILE int naddrs;
+ SM_NONVOLATILE int i;
char *oldto = e->e_to;
- char *bufp;
+ char *SM_NONVOLATILE bufp;
char buf[MAXNAME + 1];
if (list == NULL)
@@ -74,8 +181,8 @@ sendtolist(list, ctladdr, sendq, aliaslevel, e)
if (tTd(25, 1))
{
- dprintf("sendto: %s\n ctladdr=", list);
- printaddr(ctladdr, FALSE);
+ sm_dprintf("sendto: %s\n ctladdr=", list);
+ printaddr(ctladdr, false);
}
/* heuristic to determine old versus new style addresses */
@@ -98,105 +205,111 @@ sendtolist(list, ctladdr, sendq, aliaslevel, e)
i = sizeof buf;
}
else
- bufp = xalloc(i);
- (void) strlcpy(bufp, denlstring(list, FALSE, TRUE), i);
-
-#if _FFR_ADDR_TYPE
- define(macid("{addr_type}", NULL), "e r", e);
-#endif /* _FFR_ADDR_TYPE */
- for (p = bufp; *p != '\0'; )
- {
- auto char *delimptr;
- register ADDRESS *a;
-
- /* parse the address */
- while ((isascii(*p) && isspace(*p)) || *p == ',')
- p++;
- a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter, &delimptr, e);
- p = delimptr;
- if (a == NULL)
- continue;
- a->q_next = al;
- a->q_alias = ctladdr;
+ bufp = sm_malloc_x(i);
+
+ SM_TRY
+ {
+ (void) sm_strlcpy(bufp, denlstring(list, false, true), i);
- /* arrange to inherit attributes from parent */
- if (ctladdr != NULL)
+ macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r");
+ for (p = bufp; *p != '\0'; )
{
- ADDRESS *b;
+ auto char *delimptr;
+ register ADDRESS *a;
+
+ /* parse the address */
+ while ((isascii(*p) && isspace(*p)) || *p == ',')
+ p++;
+ a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter,
+ &delimptr, e, true);
+ p = delimptr;
+ if (a == NULL)
+ continue;
+ a->q_next = al;
+ a->q_alias = ctladdr;
- /* self reference test */
- if (sameaddr(ctladdr, a))
+ /* arrange to inherit attributes from parent */
+ if (ctladdr != NULL)
{
- if (tTd(27, 5))
- {
- dprintf("sendtolist: QSELFREF ");
- printaddr(ctladdr, FALSE);
- }
- ctladdr->q_flags |= QSELFREF;
- }
+ ADDRESS *b;
- /* check for address loops */
- b = self_reference(a);
- if (b != NULL)
- {
- b->q_flags |= QSELFREF;
- if (tTd(27, 5))
+ /* self reference test */
+ if (sameaddr(ctladdr, a))
{
- dprintf("sendtolist: QSELFREF ");
- printaddr(b, FALSE);
+ if (tTd(27, 5))
+ {
+ sm_dprintf("sendtolist: QSELFREF ");
+ printaddr(ctladdr, false);
+ }
+ ctladdr->q_flags |= QSELFREF;
}
- if (a != b)
+
+ /* check for address loops */
+ b = self_reference(a);
+ if (b != NULL)
{
+ b->q_flags |= QSELFREF;
if (tTd(27, 5))
{
- dprintf("sendtolist: QS_DONTSEND ");
- printaddr(a, FALSE);
+ sm_dprintf("sendtolist: QSELFREF ");
+ printaddr(b, false);
+ }
+ if (a != b)
+ {
+ if (tTd(27, 5))
+ {
+ sm_dprintf("sendtolist: QS_DONTSEND ");
+ printaddr(a, false);
+ }
+ a->q_state = QS_DONTSEND;
+ b->q_flags |= a->q_flags & QNOTREMOTE;
+ continue;
}
- a->q_state = QS_DONTSEND;
- b->q_flags |= a->q_flags & QNOTREMOTE;
- continue;
}
- }
- /* full name */
- if (a->q_fullname == NULL)
- a->q_fullname = ctladdr->q_fullname;
+ /* full name */
+ if (a->q_fullname == NULL)
+ a->q_fullname = ctladdr->q_fullname;
+
+ /* various flag bits */
+ a->q_flags &= ~QINHERITEDBITS;
+ a->q_flags |= ctladdr->q_flags & QINHERITEDBITS;
- /* various flag bits */
- a->q_flags &= ~QINHERITEDBITS;
- a->q_flags |= ctladdr->q_flags & QINHERITEDBITS;
+ /* DSN recipient information */
+ a->q_finalrcpt = ctladdr->q_finalrcpt;
+ a->q_orcpt = ctladdr->q_orcpt;
+ }
- /* original recipient information */
- a->q_orcpt = ctladdr->q_orcpt;
+ al = a;
}
- al = a;
- }
+ /* arrange to send to everyone on the local send list */
+ while (al != NULL)
+ {
+ register ADDRESS *a = al;
- /* arrange to send to everyone on the local send list */
- while (al != NULL)
+ al = a->q_next;
+ a = recipient(a, sendq, aliaslevel, e);
+ naddrs++;
+ }
+ }
+ SM_FINALLY
{
- register ADDRESS *a = al;
-
- al = a->q_next;
- a = recipient(a, sendq, aliaslevel, e);
- naddrs++;
+ e->e_to = oldto;
+ if (bufp != buf)
+ sm_free(bufp);
+ macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), NULL);
}
-
- e->e_to = oldto;
- if (bufp != buf)
- sm_free(bufp);
-#if _FFR_ADDR_TYPE
- define(macid("{addr_type}", NULL), NULL, e);
-#endif /* _FFR_ADDR_TYPE */
+ SM_END_TRY
return naddrs;
}
- /*
+#if MILTER
+/*
** REMOVEFROMLIST -- Remove addresses from a send list.
**
** The parameter is a comma-separated list of recipients to remove.
** Note that it only deletes matching addresses. If those addresses
-** have been expended already in the sendq, it won't mark the
+** have been expanded already in the sendq, it won't mark the
** expanded recipients as QS_REMOVED.
**
** Parameters:
@@ -216,12 +329,12 @@ removefromlist(list, sendq, e)
ADDRESS **sendq;
ENVELOPE *e;
{
- char delimiter; /* the address delimiter */
- int naddrs;
- int i;
+ SM_NONVOLATILE char delimiter; /* the address delimiter */
+ SM_NONVOLATILE int naddrs;
+ SM_NONVOLATILE int i;
char *p;
char *oldto = e->e_to;
- char *bufp;
+ char *SM_NONVOLATILE bufp;
char buf[MAXNAME + 1];
if (list == NULL)
@@ -231,7 +344,7 @@ removefromlist(list, sendq, e)
}
if (tTd(25, 1))
- dprintf("removefromlist: %s\n", list);
+ sm_dprintf("removefromlist: %s\n", list);
/* heuristic to determine old versus new style addresses */
if (strchr(list, ',') != NULL || strchr(list, ';') != NULL ||
@@ -251,61 +364,65 @@ removefromlist(list, sendq, e)
i = sizeof buf;
}
else
- bufp = xalloc(i);
- (void) strlcpy(bufp, denlstring(list, FALSE, TRUE), i);
-
-#if _FFR_ADDR_TYPE
- define(macid("{addr_type}", NULL), "e r", e);
-#endif /* _FFR_ADDR_TYPE */
- for (p = bufp; *p != '\0'; )
- {
- ADDRESS a; /* parsed address to be removed */
- ADDRESS *q;
- ADDRESS **pq;
- char *delimptr;
-
- /* parse the address */
- while ((isascii(*p) && isspace(*p)) || *p == ',')
- p++;
- if (parseaddr(p, &a, RF_COPYALL,
- delimiter, &delimptr, e) == NULL)
+ bufp = sm_malloc_x(i);
+
+ SM_TRY
+ {
+ (void) sm_strlcpy(bufp, denlstring(list, false, true), i);
+
+ macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r");
+ for (p = bufp; *p != '\0'; )
{
+ ADDRESS a; /* parsed address to be removed */
+ ADDRESS *q;
+ ADDRESS **pq;
+ char *delimptr;
+
+ /* parse the address */
+ while ((isascii(*p) && isspace(*p)) || *p == ',')
+ p++;
+ if (parseaddr(p, &a, RF_COPYALL,
+ delimiter, &delimptr, e, true) == NULL)
+ {
+ p = delimptr;
+ continue;
+ }
p = delimptr;
- continue;
- }
- p = delimptr;
- for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
- {
- if (!QS_IS_DEAD(q->q_state) &&
- sameaddr(q, &a))
+ for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
{
- if (tTd(25, 5))
+ if (!QS_IS_DEAD(q->q_state) &&
+ sameaddr(q, &a))
{
- dprintf("removefromlist: QS_REMOVED ");
- printaddr(&a, FALSE);
+ if (tTd(25, 5))
+ {
+ sm_dprintf("removefromlist: QS_REMOVED ");
+ printaddr(&a, false);
+ }
+ q->q_state = QS_REMOVED;
+ naddrs++;
+ break;
}
- q->q_state = QS_REMOVED;
- naddrs++;
- break;
}
}
}
-
- e->e_to = oldto;
- if (bufp != buf)
- sm_free(bufp);
-#if _FFR_ADDR_TYPE
- define(macid("{addr_type}", NULL), NULL, e);
-#endif /* _FFR_ADDR_TYPE */
+ SM_FINALLY
+ {
+ e->e_to = oldto;
+ if (bufp != buf)
+ sm_free(bufp);
+ macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), NULL);
+ }
+ SM_END_TRY
return naddrs;
}
- /*
+#endif /* MILTER */
+/*
** RECIPIENT -- Designate a message recipient
**
** Saves the named person for future mailing.
**
** Parameters:
-** a -- the (preparsed) address header for the recipient.
+** new -- the (preparsed) address header for the recipient.
** sendq -- a pointer to the head of a queue to put the
** recipient in. Duplicate suppression is done
** in this queue.
@@ -316,62 +433,121 @@ removefromlist(list, sendq, e)
** The actual address in the queue. This will be "a" if
** the address is not a duplicate, else the original address.
**
-** Side Effects:
-** none.
*/
ADDRESS *
-recipient(a, sendq, aliaslevel, e)
- register ADDRESS *a;
+recipient(new, sendq, aliaslevel, e)
+ register ADDRESS *new;
register ADDRESS **sendq;
int aliaslevel;
register ENVELOPE *e;
{
register ADDRESS *q;
ADDRESS **pq;
+ ADDRESS **prev;
register struct mailer *m;
- register char *p = NULL;
- bool quoted = FALSE; /* set if the addr has a quote bit */
- int findusercount = 0;
- bool initialdontsend = QS_IS_DEAD(a->q_state);
+ register char *p;
int i, buflen;
+ bool quoted; /* set if the addr has a quote bit */
+ bool insert;
+ int findusercount;
+ bool initialdontsend;
char *buf;
char buf0[MAXNAME + 1]; /* unquoted image of the user name */
-
- e->e_to = a->q_paddr;
- m = a->q_mailer;
+ sortfn_t *sortfn;
+
+ p = NULL;
+ quoted = false;
+ insert = false;
+ findusercount = 0;
+ initialdontsend = QS_IS_DEAD(new->q_state);
+ e->e_to = new->q_paddr;
+ m = new->q_mailer;
errno = 0;
if (aliaslevel == 0)
- a->q_flags |= QPRIMARY;
+ new->q_flags |= QPRIMARY;
if (tTd(26, 1))
{
- dprintf("\nrecipient (%d): ", aliaslevel);
- printaddr(a, FALSE);
+ sm_dprintf("\nrecipient (%d): ", aliaslevel);
+ printaddr(new, false);
}
- /* if this is primary, add it to the original recipient list */
- if (a->q_alias == NULL)
+ /* if this is primary, use it as original recipient */
+ if (new->q_alias == NULL)
{
if (e->e_origrcpt == NULL)
- e->e_origrcpt = a->q_paddr;
- else if (e->e_origrcpt != a->q_paddr)
+ e->e_origrcpt = new->q_paddr;
+ else if (e->e_origrcpt != new->q_paddr)
e->e_origrcpt = "";
}
+ /* find parent recipient for finalrcpt and orcpt */
+ for (q = new; q->q_alias != NULL; q = q->q_alias)
+ continue;
+
+ /* find final recipient DSN address */
+ if (new->q_finalrcpt == NULL &&
+ e->e_from.q_mailer != NULL)
+ {
+ char frbuf[MAXLINE];
+
+ p = e->e_from.q_mailer->m_addrtype;
+ if (p == NULL)
+ p = "rfc822";
+ if (sm_strcasecmp(p, "rfc822") != 0)
+ {
+ (void) sm_snprintf(frbuf, sizeof frbuf, "%s; %.800s",
+ q->q_mailer->m_addrtype,
+ q->q_user);
+ }
+ else if (strchr(q->q_user, '@') != NULL)
+ {
+ (void) sm_snprintf(frbuf, sizeof frbuf, "%s; %.800s",
+ p, q->q_user);
+ }
+ else if (strchr(q->q_paddr, '@') != NULL)
+ {
+ char *qp;
+ bool b;
+
+ qp = q->q_paddr;
+
+ /* strip brackets from address */
+ b = false;
+ if (*qp == '<')
+ {
+ b = qp[strlen(qp) - 1] == '>';
+ if (b)
+ qp[strlen(qp) - 1] = '\0';
+ qp++;
+ }
+ (void) sm_snprintf(frbuf, sizeof frbuf, "%s; %.800s",
+ p, qp);
+
+ /* undo damage */
+ if (b)
+ qp[strlen(qp)] = '>';
+ }
+ else
+ {
+ (void) sm_snprintf(frbuf, sizeof frbuf,
+ "%s; %.700s@%.100s",
+ p, q->q_user, MyHostName);
+ }
+ new->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool, frbuf);
+ }
+
#if _FFR_GEN_ORCPT
/* set ORCPT DSN arg if not already set */
- if (a->q_orcpt == NULL)
+ if (new->q_orcpt == NULL)
{
- for (q = a; q->q_alias != NULL; q = q->q_alias)
- continue;
-
/* check for an existing ORCPT */
if (q->q_orcpt != NULL)
- a->q_orcpt = q->q_orcpt;
+ new->q_orcpt = q->q_orcpt;
else
{
/* make our own */
- bool b = FALSE;
+ bool b = false;
char *qp;
char obuf[MAXLINE];
@@ -379,8 +555,7 @@ recipient(a, sendq, aliaslevel, e)
p = e->e_from.q_mailer->m_addrtype;
if (p == NULL)
p = "rfc822";
- (void) strlcpy(obuf, p, sizeof obuf);
- (void) strlcat(obuf, ";", sizeof obuf);
+ (void) sm_strlcpyn(obuf, sizeof obuf, 2, p, ";");
qp = q->q_paddr;
@@ -395,9 +570,9 @@ recipient(a, sendq, aliaslevel, e)
qp++;
}
- p = xtextify(denlstring(qp, TRUE, FALSE), NULL);
+ p = xtextify(denlstring(qp, true, false), NULL);
- if (strlcat(obuf, p, sizeof obuf) >= sizeof obuf)
+ if (sm_strlcat(obuf, p, sizeof obuf) >= sizeof obuf)
{
/* if too big, don't use it */
obuf[0] = '\0';
@@ -408,7 +583,8 @@ recipient(a, sendq, aliaslevel, e)
qp[strlen(qp)] = '>';
if (obuf[0] != '\0')
- a->q_orcpt = newstr(obuf);
+ new->q_orcpt =
+ sm_rpool_strdup_x(e->e_rpool, obuf);
}
}
#endif /* _FFR_GEN_ORCPT */
@@ -416,12 +592,12 @@ recipient(a, sendq, aliaslevel, e)
/* break aliasing loops */
if (aliaslevel > MaxAliasRecursion)
{
- a->q_state = QS_BADADDR;
- a->q_status = "5.4.6";
- usrerrenh(a->q_status,
+ new->q_state = QS_BADADDR;
+ new->q_status = "5.4.6";
+ usrerrenh(new->q_status,
"554 aliasing/forwarding loop broken (%d aliases deep; %d max)",
aliaslevel, MaxAliasRecursion);
- return a;
+ return new;
}
/*
@@ -429,7 +605,7 @@ recipient(a, sendq, aliaslevel, e)
*/
/* get unquoted user for file, program or user.name check */
- i = strlen(a->q_user);
+ i = strlen(new->q_user);
if (i >= sizeof buf0)
{
buflen = i + 1;
@@ -440,45 +616,46 @@ recipient(a, sendq, aliaslevel, e)
buf = buf0;
buflen = sizeof buf0;
}
- (void) strlcpy(buf, a->q_user, buflen);
+ (void) sm_strlcpy(buf, new->q_user, buflen);
for (p = buf; *p != '\0' && !quoted; p++)
{
if (*p == '\\')
- quoted = TRUE;
+ quoted = true;
}
stripquotes(buf);
/* check for direct mailing to restricted mailers */
if (m == ProgMailer)
{
- if (a->q_alias == NULL)
+ if (new->q_alias == NULL || UseMSP ||
+ bitset(EF_UNSAFE, e->e_flags))
{
- a->q_state = QS_BADADDR;
- a->q_status = "5.7.1";
- usrerrenh(a->q_status,
+ new->q_state = QS_BADADDR;
+ new->q_status = "5.7.1";
+ usrerrenh(new->q_status,
"550 Cannot mail directly to programs");
}
- else if (bitset(QBOGUSSHELL, a->q_alias->q_flags))
+ else if (bitset(QBOGUSSHELL, new->q_alias->q_flags))
{
- a->q_state = QS_BADADDR;
- a->q_status = "5.7.1";
- if (a->q_alias->q_ruser == NULL)
- usrerrenh(a->q_status,
+ new->q_state = QS_BADADDR;
+ new->q_status = "5.7.1";
+ if (new->q_alias->q_ruser == NULL)
+ usrerrenh(new->q_status,
"550 UID %d is an unknown user: cannot mail to programs",
- a->q_alias->q_uid);
+ new->q_alias->q_uid);
else
- usrerrenh(a->q_status,
+ usrerrenh(new->q_status,
"550 User %s@%s doesn't have a valid shell for mailing to programs",
- a->q_alias->q_ruser, MyHostName);
+ new->q_alias->q_ruser, MyHostName);
}
- else if (bitset(QUNSAFEADDR, a->q_alias->q_flags))
+ else if (bitset(QUNSAFEADDR, new->q_alias->q_flags))
{
- a->q_state = QS_BADADDR;
- a->q_status = "5.7.1";
- a->q_rstatus = newstr("550 Unsafe for mailing to programs");
- usrerrenh(a->q_status,
+ new->q_state = QS_BADADDR;
+ new->q_status = "5.7.1";
+ new->q_rstatus = "550 Unsafe for mailing to programs";
+ usrerrenh(new->q_status,
"550 Address %s is unsafe for mailing to programs",
- a->q_alias->q_paddr);
+ new->q_alias->q_paddr);
}
}
@@ -491,52 +668,123 @@ recipient(a, sendq, aliaslevel, e)
** [Please note: the emphasis is on "hack."]
*/
+ prev = NULL;
+
+ /*
+ ** If this message is going to the queue or FastSplit is set
+ ** and it is the first try and the envelope hasn't split, then we
+ ** avoid doing an MX RR lookup now because one will be done when the
+ ** message is extracted from the queue later. It can go to the queue
+ ** because all messages are going to the queue or this mailer of
+ ** the current recipient is marked expensive.
+ */
+
+ if (WILL_BE_QUEUED(e->e_sendmode) ||
+ (!bitset(EF_SPLIT, e->e_flags) && e->e_ntries == 0 &&
+ FastSplit > 0))
+ sortfn = sorthost;
+ else if (NoConnect && bitnset(M_EXPENSIVE, new->q_mailer->m_flags))
+ sortfn = sortexpensive;
+ else
+ sortfn = sortbysignature;
+
for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
{
- if (sameaddr(q, a) &&
- (bitset(QRCPTOK, q->q_flags) ||
- !bitset(QPRIMARY, q->q_flags)))
+ /*
+ ** If address is "less than" it should be inserted now.
+ ** If address is "greater than" current comparison it'll
+ ** insert later in the list; so loop again (if possible).
+ ** If address is "equal" (different equal than sameaddr()
+ ** call) then check if sameaddr() will be true.
+ ** Because this list is now sorted, it'll mean fewer
+ ** comparisons and fewer loops which is important for more
+ ** recipients.
+ */
+
+ i = (*sortfn)(new, q);
+ if (i == 0) /* equal */
{
- if (tTd(26, 1))
- {
- dprintf("%s in sendq: ", a->q_paddr);
- printaddr(q, FALSE);
- }
- if (!bitset(QPRIMARY, q->q_flags))
- {
- if (!QS_IS_DEAD(a->q_state))
- message("duplicate suppressed");
- else
- q->q_state = QS_DUPLICATE;
- q->q_flags |= a->q_flags;
- }
- else if (bitset(QSELFREF, q->q_flags)
-#if _FFR_MILTER
- || q->q_state == QS_REMOVED
-#endif /* _FFR_MILTER */
- )
+ /*
+ ** Sortbysignature() has said that the two have
+ ** equal MX RR's and the same user. Calling sameaddr()
+ ** now checks if the two hosts are as identical as the
+ ** MX RR's are (which might not be the case)
+ ** before saying these are the identical addresses.
+ */
+
+ if (sameaddr(q, new) &&
+ (bitset(QRCPTOK, q->q_flags) ||
+ !bitset(QPRIMARY, q->q_flags)))
{
-#if _FFR_MILTER
- /*
- ** If an earlier milter removed the address,
- ** a later one can still add it back.
- */
-#endif /* _FFR_MILTER */
- q->q_state = a->q_state;
- q->q_flags |= a->q_flags;
+ if (tTd(26, 1))
+ {
+ sm_dprintf("%s in sendq: ",
+ new->q_paddr);
+ printaddr(q, false);
+ }
+ if (!bitset(QPRIMARY, q->q_flags))
+ {
+ if (!QS_IS_DEAD(new->q_state))
+ message("duplicate suppressed");
+ else
+ q->q_state = QS_DUPLICATE;
+ q->q_flags |= new->q_flags;
+ }
+ else if (bitset(QSELFREF, q->q_flags)
+ || q->q_state == QS_REMOVED)
+ {
+ /*
+ ** If an earlier milter removed the
+ ** address, a later one can still add
+ ** it back.
+ */
+
+ q->q_state = new->q_state;
+ q->q_flags |= new->q_flags;
+ }
+ new = q;
+ goto done;
}
- a = q;
- goto done;
}
+ else if (i < 0) /* less than */
+ {
+ insert = true;
+ break;
+ }
+ prev = pq;
}
+ /* pq should point to an address, never NULL */
+ SM_ASSERT(pq != NULL);
+
/* add address on list */
- if (pq != NULL)
+ if (insert)
{
- *pq = a;
- a->q_next = NULL;
+ /*
+ ** insert before 'pq'. Only possible when at least 1
+ ** ADDRESS is in the list already.
+ */
+
+ new->q_next = *pq;
+ if (prev == NULL)
+ *sendq = new; /* To be the first ADDRESS */
+ else
+ (*prev)->q_next = new;
+ }
+ else
+ {
+ /*
+ ** Place in list at current 'pq' position. Possible
+ ** when there are 0 or more ADDRESS's in the list.
+ */
+
+ new->q_next = NULL;
+ *pq = new;
}
+ /* added a new address: clear split flag */
+ e->e_flags &= ~EF_SPLIT;
+
/*
** Alias the name and handle special mailer types.
*/
@@ -544,108 +792,120 @@ recipient(a, sendq, aliaslevel, e)
trylocaluser:
if (tTd(29, 7))
{
- dprintf("at trylocaluser: ");
- printaddr(a, FALSE);
+ sm_dprintf("at trylocaluser: ");
+ printaddr(new, false);
}
- if (!QS_IS_OK(a->q_state))
+ if (!QS_IS_OK(new->q_state))
+ {
+ if (QS_IS_UNDELIVERED(new->q_state))
+ e->e_nrcpts++;
goto testselfdestruct;
+ }
if (m == InclMailer)
{
- a->q_state = QS_INCLUDED;
- if (a->q_alias == NULL)
+ new->q_state = QS_INCLUDED;
+ if (new->q_alias == NULL || UseMSP ||
+ bitset(EF_UNSAFE, e->e_flags))
{
- a->q_state = QS_BADADDR;
- a->q_status = "5.7.1";
- usrerrenh(a->q_status,
+ new->q_state = QS_BADADDR;
+ new->q_status = "5.7.1";
+ usrerrenh(new->q_status,
"550 Cannot mail directly to :include:s");
}
else
{
int ret;
- message("including file %s", a->q_user);
- ret = include(a->q_user, FALSE, a, sendq, aliaslevel, e);
+ message("including file %s", new->q_user);
+ ret = include(new->q_user, false, new,
+ sendq, aliaslevel, e);
if (transienterror(ret))
{
if (LogLevel > 2)
sm_syslog(LOG_ERR, e->e_id,
"include %s: transient error: %s",
- shortenstring(a->q_user, MAXSHORTSTR),
- errstring(ret));
- a->q_state = QS_QUEUEUP;
+ shortenstring(new->q_user,
+ MAXSHORTSTR),
+ sm_errstring(ret));
+ new->q_state = QS_QUEUEUP;
usrerr("451 4.2.4 Cannot open %s: %s",
- shortenstring(a->q_user, MAXSHORTSTR),
- errstring(ret));
+ shortenstring(new->q_user,
+ MAXSHORTSTR),
+ sm_errstring(ret));
}
else if (ret != 0)
{
- a->q_state = QS_BADADDR;
- a->q_status = "5.2.4";
- usrerrenh(a->q_status,
+ new->q_state = QS_BADADDR;
+ new->q_status = "5.2.4";
+ usrerrenh(new->q_status,
"550 Cannot open %s: %s",
- shortenstring(a->q_user, MAXSHORTSTR),
- errstring(ret));
+ shortenstring(new->q_user,
+ MAXSHORTSTR),
+ sm_errstring(ret));
}
}
}
else if (m == FileMailer)
{
- /* check if writable or creatable */
- if (a->q_alias == NULL)
+ /* check if allowed */
+ if (new->q_alias == NULL || UseMSP ||
+ bitset(EF_UNSAFE, e->e_flags))
{
- a->q_state = QS_BADADDR;
- a->q_status = "5.7.1";
- usrerrenh(a->q_status,
+ new->q_state = QS_BADADDR;
+ new->q_status = "5.7.1";
+ usrerrenh(new->q_status,
"550 Cannot mail directly to files");
}
- else if (bitset(QBOGUSSHELL, a->q_alias->q_flags))
+ else if (bitset(QBOGUSSHELL, new->q_alias->q_flags))
{
- a->q_state = QS_BADADDR;
- a->q_status = "5.7.1";
- if (a->q_alias->q_ruser == NULL)
- usrerrenh(a->q_status,
+ new->q_state = QS_BADADDR;
+ new->q_status = "5.7.1";
+ if (new->q_alias->q_ruser == NULL)
+ usrerrenh(new->q_status,
"550 UID %d is an unknown user: cannot mail to files",
- a->q_alias->q_uid);
+ new->q_alias->q_uid);
else
- usrerrenh(a->q_status,
+ usrerrenh(new->q_status,
"550 User %s@%s doesn't have a valid shell for mailing to files",
- a->q_alias->q_ruser, MyHostName);
+ new->q_alias->q_ruser, MyHostName);
}
- else if (bitset(QUNSAFEADDR, a->q_alias->q_flags))
+ else if (bitset(QUNSAFEADDR, new->q_alias->q_flags))
{
- a->q_state = QS_BADADDR;
- a->q_status = "5.7.1";
- a->q_rstatus = newstr("550 Unsafe for mailing to files");
- usrerrenh(a->q_status,
+ new->q_state = QS_BADADDR;
+ new->q_status = "5.7.1";
+ new->q_rstatus = "550 Unsafe for mailing to files";
+ usrerrenh(new->q_status,
"550 Address %s is unsafe for mailing to files",
- a->q_alias->q_paddr);
+ new->q_alias->q_paddr);
}
}
/* try aliasing */
- if (!quoted && QS_IS_OK(a->q_state) &&
+ if (!quoted && QS_IS_OK(new->q_state) &&
bitnset(M_ALIASABLE, m->m_flags))
- alias(a, sendq, aliaslevel, e);
+ alias(new, sendq, aliaslevel, e);
#if USERDB
/* if not aliased, look it up in the user database */
- if (!bitset(QNOTREMOTE, a->q_flags) &&
- QS_IS_SENDABLE(a->q_state) &&
+ if (!bitset(QNOTREMOTE, new->q_flags) &&
+ QS_IS_SENDABLE(new->q_state) &&
bitnset(M_CHECKUDB, m->m_flags))
{
- if (udbexpand(a, sendq, aliaslevel, e) == EX_TEMPFAIL)
+ if (udbexpand(new, sendq, aliaslevel, e) == EX_TEMPFAIL)
{
- a->q_state = QS_QUEUEUP;
+ new->q_state = QS_QUEUEUP;
if (e->e_message == NULL)
- e->e_message = newstr("Deferred: user database error");
+ e->e_message = "Deferred: user database error";
+ if (new->q_message == NULL)
+ new->q_message = "Deferred: user database error";
if (LogLevel > 8)
sm_syslog(LOG_INFO, e->e_id,
"deferred: udbexpand: %s",
- errstring(errno));
+ sm_errstring(errno));
message("queued (user database error): %s",
- errstring(errno));
+ sm_errstring(errno));
e->e_nrcpts++;
goto testselfdestruct;
}
@@ -655,22 +915,22 @@ recipient(a, sendq, aliaslevel, e)
/*
** If we have a level two config file, then pass the name through
** Ruleset 5 before sending it off. Ruleset 5 has the right
- ** to send rewrite it to another mailer. This gives us a hook
+ ** to rewrite it to another mailer. This gives us a hook
** after local aliasing has been done.
*/
if (tTd(29, 5))
{
- dprintf("recipient: testing local? cl=%d, rr5=%lx\n\t",
- ConfigLevel, (u_long) RewriteRules[5]);
- printaddr(a, FALSE);
+ sm_dprintf("recipient: testing local? cl=%d, rr5=%p\n\t",
+ ConfigLevel, RewriteRules[5]);
+ printaddr(new, false);
}
if (ConfigLevel >= 2 && RewriteRules[5] != NULL &&
bitnset(M_TRYRULESET5, m->m_flags) &&
- !bitset(QNOTREMOTE, a->q_flags) &&
- QS_IS_OK(a->q_state))
+ !bitset(QNOTREMOTE, new->q_flags) &&
+ QS_IS_OK(new->q_state))
{
- maplocaluser(a, sendq, aliaslevel + 1, e);
+ maplocaluser(new, sendq, aliaslevel + 1, e);
}
/*
@@ -678,90 +938,99 @@ recipient(a, sendq, aliaslevel, e)
** and deliver it.
*/
- if (QS_IS_OK(a->q_state) &&
+ if (QS_IS_OK(new->q_state) &&
bitnset(M_HASPWENT, m->m_flags))
{
auto bool fuzzy;
- register struct passwd *pw;
+ SM_MBDB_T user;
+ int status;
/* warning -- finduser may trash buf */
- pw = finduser(buf, &fuzzy);
- if (pw == NULL || strlen(pw->pw_name) > MAXNAME)
- {
- {
- a->q_state = QS_BADADDR;
- a->q_status = "5.1.1";
- a->q_rstatus = newstr("550 5.1.1 User unknown");
- giveresponse(EX_NOUSER, a->q_status, m, NULL,
- a->q_alias, (time_t) 0, e);
- }
- }
- else
+ status = finduser(buf, &fuzzy, &user);
+ switch (status)
{
- char nbuf[MAXNAME + 1];
-
+ case EX_TEMPFAIL:
+ new->q_state = QS_QUEUEUP;
+ new->q_status = "4.5.2";
+ giveresponse(EX_TEMPFAIL, new->q_status, m, NULL,
+ new->q_alias, (time_t) 0, e, new);
+ break;
+ default:
+ new->q_state = QS_BADADDR;
+ new->q_status = "5.1.1";
+ new->q_rstatus = "550 5.1.1 User unknown";
+ giveresponse(EX_NOUSER, new->q_status, m, NULL,
+ new->q_alias, (time_t) 0, e, new);
+ break;
+ case EX_OK:
if (fuzzy)
{
/* name was a fuzzy match */
- a->q_user = newstr(pw->pw_name);
+ new->q_user = sm_rpool_strdup_x(e->e_rpool,
+ user.mbdb_name);
if (findusercount++ > 3)
{
- a->q_state = QS_BADADDR;
- a->q_status = "5.4.6";
- usrerrenh(a->q_status,
+ new->q_state = QS_BADADDR;
+ new->q_status = "5.4.6";
+ usrerrenh(new->q_status,
"554 aliasing/forwarding loop for %s broken",
- pw->pw_name);
+ user.mbdb_name);
goto done;
}
/* see if it aliases */
- (void) strlcpy(buf, pw->pw_name, buflen);
+ (void) sm_strlcpy(buf, user.mbdb_name, buflen);
goto trylocaluser;
}
- if (*pw->pw_dir == '\0')
- a->q_home = NULL;
- else if (strcmp(pw->pw_dir, "/") == 0)
- a->q_home = "";
+ if (*user.mbdb_homedir == '\0')
+ new->q_home = NULL;
+ else if (strcmp(user.mbdb_homedir, "/") == 0)
+ new->q_home = "";
else
- a->q_home = newstr(pw->pw_dir);
- a->q_uid = pw->pw_uid;
- a->q_gid = pw->pw_gid;
- a->q_ruser = newstr(pw->pw_name);
- a->q_flags |= QGOODUID;
- buildfname(pw->pw_gecos, pw->pw_name, nbuf, sizeof nbuf);
- if (nbuf[0] != '\0')
- a->q_fullname = newstr(nbuf);
- if (!usershellok(pw->pw_name, pw->pw_shell))
+ new->q_home = sm_rpool_strdup_x(e->e_rpool,
+ user.mbdb_homedir);
+ if (user.mbdb_uid != SM_NO_UID)
{
- a->q_flags |= QBOGUSSHELL;
+ new->q_uid = user.mbdb_uid;
+ new->q_gid = user.mbdb_gid;
+ new->q_flags |= QGOODUID;
+ }
+ new->q_ruser = sm_rpool_strdup_x(e->e_rpool,
+ user.mbdb_name);
+ if (user.mbdb_fullname[0] != '\0')
+ new->q_fullname = sm_rpool_strdup_x(e->e_rpool,
+ user.mbdb_fullname);
+ if (!usershellok(user.mbdb_name, user.mbdb_shell))
+ {
+ new->q_flags |= QBOGUSSHELL;
}
if (bitset(EF_VRFYONLY, e->e_flags))
{
/* don't do any more now */
- a->q_state = QS_VERIFIED;
+ new->q_state = QS_VERIFIED;
}
else if (!quoted)
- forward(a, sendq, aliaslevel, e);
+ forward(new, sendq, aliaslevel, e);
}
}
- if (!QS_IS_DEAD(a->q_state))
+ if (!QS_IS_DEAD(new->q_state))
e->e_nrcpts++;
testselfdestruct:
- a->q_flags |= QTHISPASS;
+ new->q_flags |= QTHISPASS;
if (tTd(26, 8))
{
- dprintf("testselfdestruct: ");
- printaddr(a, FALSE);
+ sm_dprintf("testselfdestruct: ");
+ printaddr(new, false);
if (tTd(26, 10))
{
- dprintf("SENDQ:\n");
- printaddr(*sendq, TRUE);
- dprintf("----\n");
+ sm_dprintf("SENDQ:\n");
+ printaddr(*sendq, true);
+ sm_dprintf("----\n");
}
}
- if (a->q_alias == NULL && a != &e->e_from &&
- QS_IS_DEAD(a->q_state))
+ if (new->q_alias == NULL && new != &e->e_from &&
+ QS_IS_DEAD(new->q_state))
{
for (q = *sendq; q != NULL; q = q->q_next)
{
@@ -770,17 +1039,17 @@ recipient(a, sendq, aliaslevel, e)
}
if (q == NULL)
{
- a->q_state = QS_BADADDR;
- a->q_status = "5.4.6";
- usrerrenh(a->q_status,
+ new->q_state = QS_BADADDR;
+ new->q_status = "5.4.6";
+ usrerrenh(new->q_status,
"554 aliasing/forwarding loop broken");
}
}
done:
- a->q_flags |= QTHISPASS;
+ new->q_flags |= QTHISPASS;
if (buf != buf0)
- sm_free(buf);
+ sm_free(buf); /* XXX leak if above code raises exception */
/*
** If we are at the top level, check to see if this has
@@ -821,18 +1090,20 @@ recipient(a, sendq, aliaslevel, e)
{
/* arrange for return receipt */
e->e_flags |= EF_SENDRECEIPT;
- a->q_flags |= QEXPANDED;
+ new->q_flags |= QEXPANDED;
if (e->e_xfp != NULL &&
- bitset(QPINGONSUCCESS, a->q_flags))
- fprintf(e->e_xfp,
- "%s... expanded to multiple addresses\n",
- a->q_paddr);
+ bitset(QPINGONSUCCESS, new->q_flags))
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "%s... expanded to multiple addresses\n",
+ new->q_paddr);
}
}
- a->q_flags |= QRCPTOK;
- return a;
+ new->q_flags |= QRCPTOK;
+ (void) sm_snprintf(buf0, sizeof buf0, "%d", e->e_nrcpts);
+ macdefine(&e->e_macro, A_TEMP, macid("{nrcpts}"), buf0);
+ return new;
}
- /*
+/*
** FINDUSER -- find the password entry for a user.
**
** This looks a lot like getpwnam, except that it may want to
@@ -843,33 +1114,39 @@ recipient(a, sendq, aliaslevel, e)
**
** Parameters:
** name -- the name to match against.
-** fuzzyp -- an outarg that is set to TRUE if this entry
+** fuzzyp -- an outarg that is set to true if this entry
** was found using the fuzzy matching algorithm;
-** set to FALSE otherwise.
+** set to false otherwise.
+** user -- structure to fill in if user is found
**
** Returns:
-** A pointer to a pw struct.
-** NULL if name is unknown or ambiguous.
+** On success, fill in *user, set *fuzzyp and return EX_OK.
+** If the user was not found, return EX_NOUSER.
+** On error, return EX_TEMPFAIL or EX_OSERR.
**
** Side Effects:
** may modify name.
*/
-struct passwd *
-finduser(name, fuzzyp)
+int
+finduser(name, fuzzyp, user)
char *name;
bool *fuzzyp;
+ SM_MBDB_T *user;
{
+#if MATCHGECOS
register struct passwd *pw;
+#endif /* MATCHGECOS */
register char *p;
bool tryagain;
+ int status;
if (tTd(29, 4))
- dprintf("finduser(%s): ", name);
+ sm_dprintf("finduser(%s): ", name);
- *fuzzyp = FALSE;
+ *fuzzyp = false;
-#ifdef HESIOD
+#if HESIOD
/* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */
for (p = name; *p != '\0'; p++)
if (!isascii(*p) || !isdigit(*p))
@@ -877,35 +1154,36 @@ finduser(name, fuzzyp)
if (*p == '\0')
{
if (tTd(29, 4))
- dprintf("failed (numeric input)\n");
- return NULL;
+ sm_dprintf("failed (numeric input)\n");
+ return EX_NOUSER;
}
#endif /* HESIOD */
/* look up this login name using fast path */
- if ((pw = sm_getpwnam(name)) != NULL)
+ status = sm_mbdb_lookup(name, user);
+ if (status != EX_NOUSER)
{
if (tTd(29, 4))
- dprintf("found (non-fuzzy)\n");
- return pw;
+ sm_dprintf("%s (non-fuzzy)\n", sm_strexit(status));
+ return status;
}
/* try mapping it to lower case */
- tryagain = FALSE;
+ tryagain = false;
for (p = name; *p != '\0'; p++)
{
if (isascii(*p) && isupper(*p))
{
*p = tolower(*p);
- tryagain = TRUE;
+ tryagain = true;
}
}
- if (tryagain && (pw = sm_getpwnam(name)) != NULL)
+ if (tryagain && (status = sm_mbdb_lookup(name, user)) != EX_NOUSER)
{
if (tTd(29, 4))
- dprintf("found (lower case)\n");
- *fuzzyp = TRUE;
- return pw;
+ sm_dprintf("%s (lower case)\n", sm_strexit(status));
+ *fuzzyp = true;
+ return status;
}
#if MATCHGECOS
@@ -913,8 +1191,8 @@ finduser(name, fuzzyp)
if (!MatchGecos)
{
if (tTd(29, 4))
- dprintf("not found (fuzzy disabled)\n");
- return NULL;
+ sm_dprintf("not found (fuzzy disabled)\n");
+ return EX_NOUSER;
}
/* search for a matching full name instead */
@@ -929,38 +1207,41 @@ finduser(name, fuzzyp)
char buf[MAXNAME + 1];
# if 0
- if (strcasecmp(pw->pw_name, name) == 0)
+ if (sm_strcasecmp(pw->pw_name, name) == 0)
{
if (tTd(29, 4))
- dprintf("found (case wrapped)\n");
+ sm_dprintf("found (case wrapped)\n");
break;
}
# endif /* 0 */
- buildfname(pw->pw_gecos, pw->pw_name, buf, sizeof buf);
- if (strchr(buf, ' ') != NULL && strcasecmp(buf, name) == 0)
+ sm_pwfullname(pw->pw_gecos, pw->pw_name, buf, sizeof buf);
+ if (strchr(buf, ' ') != NULL && sm_strcasecmp(buf, name) == 0)
{
if (tTd(29, 4))
- dprintf("fuzzy matches %s\n", pw->pw_name);
+ sm_dprintf("fuzzy matches %s\n", pw->pw_name);
message("sending to login name %s", pw->pw_name);
break;
}
}
if (pw != NULL)
- *fuzzyp = TRUE;
+ *fuzzyp = true;
else if (tTd(29, 4))
- dprintf("no fuzzy match found\n");
+ sm_dprintf("no fuzzy match found\n");
# if DEC_OSF_BROKEN_GETPWENT /* DEC OSF/1 3.2 or earlier */
endpwent();
# endif /* DEC_OSF_BROKEN_GETPWENT */
- return pw;
+ if (pw == NULL)
+ return EX_NOUSER;
+ sm_mbdb_frompw(user, pw);
+ return EX_OK;
#else /* MATCHGECOS */
if (tTd(29, 4))
- dprintf("not found (fuzzy disabled)\n");
- return NULL;
+ sm_dprintf("not found (fuzzy disabled)\n");
+ return EX_NOUSER;
#endif /* MATCHGECOS */
}
- /*
+/*
** WRITABLE -- predicate returning if the file is writable.
**
** This routine must duplicate the algorithm in sys/fio.c.
@@ -977,8 +1258,8 @@ finduser(name, fuzzyp)
** flags -- SFF_* flags to control the function.
**
** Returns:
-** TRUE -- if we will be able to write this file.
-** FALSE -- if we cannot write this file.
+** true -- if we will be able to write this file.
+** false -- if we cannot write this file.
**
** Side Effects:
** none.
@@ -995,7 +1276,7 @@ writable(filename, ctladdr, flags)
char *user = NULL;
if (tTd(44, 5))
- dprintf("writable(%s, 0x%lx)\n", filename, flags);
+ sm_dprintf("writable(%s, 0x%lx)\n", filename, flags);
/*
** File does exist -- check that it is writable.
@@ -1052,13 +1333,13 @@ writable(filename, ctladdr, flags)
errno = safefile(filename, euid, egid, user, flags, S_IWRITE, NULL);
return errno == 0;
}
- /*
+/*
** INCLUDE -- handle :include: specification.
**
** Parameters:
** fname -- filename to include.
-** forwarding -- if TRUE, we are reading a .forward file.
-** if FALSE, it's a :include: file.
+** forwarding -- if true, we are reading a .forward file.
+** if false, it's a :include: file.
** ctladdr -- address template to use to fill in these
** addresses -- effective user/group id are
** the important things.
@@ -1099,14 +1380,14 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
int aliaslevel;
ENVELOPE *e;
{
- FILE *volatile fp = NULL;
+ SM_FILE_T *volatile fp = NULL;
char *oldto = e->e_to;
char *oldfilename = FileName;
int oldlinenumber = LineNumber;
- register EVENT *ev = NULL;
+ register SM_EVENT *ev = NULL;
int nincludes;
int mode;
- volatile bool maxreached = FALSE;
+ volatile bool maxreached = false;
register ADDRESS *ca;
volatile uid_t saveduid;
volatile gid_t savedgid;
@@ -1116,29 +1397,29 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
int rval = 0;
volatile long sfflags = SFF_REGONLY;
register char *p;
- bool safechown = FALSE;
- volatile bool safedir = FALSE;
+ bool safechown = false;
+ volatile bool safedir = false;
struct stat st;
char buf[MAXLINE];
if (tTd(27, 2))
- dprintf("include(%s)\n", fname);
+ sm_dprintf("include(%s)\n", fname);
if (tTd(27, 4))
- dprintf(" ruid=%d euid=%d\n",
+ sm_dprintf(" ruid=%d euid=%d\n",
(int) getuid(), (int) geteuid());
if (tTd(27, 14))
{
- dprintf("ctladdr ");
- printaddr(ctladdr, FALSE);
+ sm_dprintf("ctladdr ");
+ printaddr(ctladdr, false);
}
if (tTd(27, 9))
- dprintf("include: old uid = %d/%d\n",
- (int) getuid(), (int) geteuid());
+ sm_dprintf("include: old uid = %d/%d\n",
+ (int) getuid(), (int) geteuid());
-#if _FFR_UNSAFE_WRITABLE_INCLUDE
if (forwarding)
{
+ sfflags |= SFF_MUSTOWN|SFF_ROOTOK|SFF_NOWLINK;
if (!bitnset(DBS_GROUPWRITABLEFORWARDFILE, DontBlameSendmail))
sfflags |= SFF_NOGWFILES;
if (!bitnset(DBS_WORLDWRITABLEFORWARDFILE, DontBlameSendmail))
@@ -1151,10 +1432,6 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
if (!bitnset(DBS_WORLDWRITABLEINCLUDEFILE, DontBlameSendmail))
sfflags |= SFF_NOWWFILES;
}
-#endif /* _FFR_UNSAFE_WRITABLE_INCLUDE */
-
- if (forwarding)
- sfflags |= SFF_MUSTOWN|SFF_ROOTOK|SFF_NOWLINK;
/*
** If RunAsUser set, won't be able to run programs as user
@@ -1165,8 +1442,8 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
!bitnset(DBS_NONROOTSAFEADDR, DontBlameSendmail))
{
if (tTd(27, 4))
- dprintf("include: not safe (euid=%d, RunAsUid=%d)\n",
- (int) geteuid(), (int) RunAsUid);
+ sm_dprintf("include: not safe (euid=%d, RunAsUid=%d)\n",
+ (int) geteuid(), (int) RunAsUid);
ctladdr->q_flags |= QUNSAFEADDR;
}
@@ -1243,8 +1520,8 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
#endif /* MAILER_SETUID_METHOD != USE_SETUID */
if (tTd(27, 9))
- dprintf("include: new uid = %d/%d\n",
- (int) getuid(), (int) geteuid());
+ sm_dprintf("include: new uid = %d/%d\n",
+ (int) getuid(), (int) geteuid());
/*
** If home directory is remote mounted but server is down,
@@ -1261,7 +1538,7 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
goto resetuid;
}
if (TimeOuts.to_fileopen > 0)
- ev = setevent(TimeOuts.to_fileopen, includetimeout, 0);
+ ev = sm_setevent(TimeOuts.to_fileopen, includetimeout, 0);
else
ev = NULL;
@@ -1278,7 +1555,7 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
if (ret == 0)
{
/* in safe directory: relax chown & link rules */
- safedir = TRUE;
+ safedir = true;
sfflags |= SFF_NOPATHCHECK;
}
else
@@ -1314,7 +1591,7 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE),
DontBlameSendmail))
{
- if (LogLevel >= 12)
+ if (LogLevel > 11)
sm_syslog(LOG_INFO, e->e_id,
"%s: unsafe directory path, marked unsafe",
shortenstring(fname, MAXSHORTSTR));
@@ -1337,23 +1614,24 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
{
/* don't use this :include: file */
if (tTd(27, 4))
- dprintf("include: not safe (uid=%d): %s\n",
- (int) uid, errstring(rval));
+ sm_dprintf("include: not safe (uid=%d): %s\n",
+ (int) uid, sm_errstring(rval));
}
- else if ((fp = fopen(fname, "r")) == NULL)
+ else if ((fp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, fname,
+ SM_IO_RDONLY, NULL)) == NULL)
{
rval = errno;
if (tTd(27, 4))
- dprintf("include: open: %s\n", errstring(rval));
+ sm_dprintf("include: open: %s\n", sm_errstring(rval));
}
- else if (filechanged(fname, fileno(fp), &st))
+ else if (filechanged(fname, sm_io_getinfo(fp,SM_IO_WHAT_FD, NULL), &st))
{
rval = E_SM_FILECHANGE;
if (tTd(27, 4))
- dprintf("include: file changed after open\n");
+ sm_dprintf("include: file changed after open\n");
}
if (ev != NULL)
- clrevent(ev);
+ sm_clrevent(ev);
resetuid:
@@ -1365,25 +1643,27 @@ resetuid:
# if USESETEUID
if (seteuid(0) < 0)
syserr("!seteuid(0) failure (real=%d, eff=%d)",
- getuid(), geteuid());
+ (int) getuid(), (int) geteuid());
# else /* USESETEUID */
if (setreuid(-1, 0) < 0)
syserr("!setreuid(-1, 0) failure (real=%d, eff=%d)",
- getuid(), geteuid());
+ (int) getuid(), (int) geteuid());
if (setreuid(RealUid, 0) < 0)
syserr("!setreuid(%d, 0) failure (real=%d, eff=%d)",
- RealUid, getuid(), geteuid());
+ (int) RealUid, (int) getuid(),
+ (int) geteuid());
# endif /* USESETEUID */
}
if (setgid(savedgid) < 0)
syserr("!setgid(%d) failure (real=%d eff=%d)",
- savedgid, getgid(), getegid());
+ (int) savedgid, (int) getgid(),
+ (int) getegid());
}
#endif /* HASSETREUID || USESETEUID */
if (tTd(27, 9))
- dprintf("include: reset uid = %d/%d\n",
- (int) getuid(), (int) geteuid());
+ sm_dprintf("include: reset uid = %d/%d\n",
+ (int) getuid(), (int) geteuid());
if (rval == E_SM_OPENTIMEOUT)
usrerr("451 4.4.1 open timeout on %s", fname);
@@ -1391,21 +1671,20 @@ resetuid:
if (fp == NULL)
return rval;
- if (fstat(fileno(fp), &st) < 0)
+ if (fstat(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), &st) < 0)
{
rval = errno;
syserr("Cannot fstat %s!", fname);
- (void) fclose(fp);
+ (void) sm_io_close(fp, SM_TIME_DEFAULT);
return rval;
}
/* if path was writable, check to avoid file giveaway tricks */
- safechown = chownsafe(fileno(fp), safedir);
+ safechown = chownsafe(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), safedir);
if (tTd(27, 6))
- dprintf("include: parent of %s is %s, chown is %ssafe\n",
- fname,
- safedir ? "safe" : "dangerous",
- safechown ? "" : "un");
+ sm_dprintf("include: parent of %s is %s, chown is %ssafe\n",
+ fname, safedir ? "safe" : "dangerous",
+ safechown ? "" : "un");
/* if no controlling user or coming from an alias delivery */
if (safechown &&
@@ -1436,17 +1715,19 @@ resetuid:
{
char *sh;
- ctladdr->q_ruser = newstr(pw->pw_name);
+ ctladdr->q_ruser = sm_rpool_strdup_x(e->e_rpool,
+ pw->pw_name);
if (safechown)
sh = pw->pw_shell;
else
sh = "/SENDMAIL/ANY/SHELL/";
if (!usershellok(pw->pw_name, sh))
{
- if (LogLevel >= 12)
+ if (LogLevel > 11)
sm_syslog(LOG_INFO, e->e_id,
"%s: user %s has bad shell %s, marked %s",
- shortenstring(fname, MAXSHORTSTR),
+ shortenstring(fname,
+ MAXSHORTSTR),
pw->pw_name, sh,
safechown ? "bogus" : "unsafe");
if (safechown)
@@ -1462,7 +1743,7 @@ resetuid:
/* don't do any more now */
ctladdr->q_state = QS_VERIFIED;
e->e_nrcpts++;
- (void) fclose(fp);
+ (void) sm_io_close(fp, SM_TIME_DEFAULT);
return rval;
}
@@ -1484,10 +1765,11 @@ resetuid:
if (bitset(mode, st.st_mode))
{
if (tTd(27, 6))
- dprintf("include: %s is %s writable, marked unsafe\n",
- shortenstring(fname, MAXSHORTSTR),
- bitset(S_IWOTH, st.st_mode) ? "world" : "group");
- if (LogLevel >= 12)
+ sm_dprintf("include: %s is %s writable, marked unsafe\n",
+ shortenstring(fname, MAXSHORTSTR),
+ bitset(S_IWOTH, st.st_mode) ? "world"
+ : "group");
+ if (LogLevel > 11)
sm_syslog(LOG_INFO, e->e_id,
"%s: %s writable %s file, marked unsafe",
shortenstring(fname, MAXSHORTSTR),
@@ -1501,9 +1783,10 @@ resetuid:
LineNumber = 0;
ctladdr->q_flags &= ~QSELFREF;
nincludes = 0;
- while (fgets(buf, sizeof buf, fp) != NULL && !maxreached)
+ while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof buf) != NULL &&
+ !maxreached)
{
- fixcrlf(buf, TRUE);
+ fixcrlf(buf, true);
LineNumber++;
if (buf[0] == '#' || buf[0] == '\0')
continue;
@@ -1542,28 +1825,30 @@ resetuid:
nincludes >= MaxForwardEntries)
{
/* just stop reading and processing further entries */
- /* additional: (?)
+#if 0
+ /* additional: (?) */
ctladdr->q_state = QS_DONTSEND;
- **/
- syserr("Attempt to forward to more then %d addresses (in %s)!",
+#endif /* 0 */
+
+ syserr("Attempt to forward to more than %d addresses (in %s)!",
MaxForwardEntries,fname);
- maxreached = TRUE;
+ maxreached = true;
}
}
- if (ferror(fp) && tTd(27, 3))
- dprintf("include: read error: %s\n", errstring(errno));
+ if (sm_io_error(fp) && tTd(27, 3))
+ sm_dprintf("include: read error: %s\n", sm_errstring(errno));
if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags))
{
if (tTd(27, 5))
{
- dprintf("include: QS_DONTSEND ");
- printaddr(ctladdr, FALSE);
+ sm_dprintf("include: QS_DONTSEND ");
+ printaddr(ctladdr, false);
}
ctladdr->q_state = QS_DONTSEND;
}
- (void) fclose(fp);
+ (void) sm_io_close(fp, SM_TIME_DEFAULT);
FileName = oldfilename;
LineNumber = oldlinenumber;
e->e_to = oldto;
@@ -1582,7 +1867,7 @@ includetimeout()
errno = ETIMEDOUT;
longjmp(CtxIncludeTimeout, 1);
}
- /*
+/*
** SENDTOARGV -- send to an argument vector.
**
** Parameters:
@@ -1605,11 +1890,9 @@ sendtoargv(argv, e)
register char *p;
while ((p = *argv++) != NULL)
- {
(void) sendtolist(p, NULLADDR, &e->e_sendqueue, 0, e);
- }
}
- /*
+/*
** GETCTLADDR -- get controlling address from an address header.
**
** If none, get one corresponding to the effective userid.
@@ -1619,9 +1902,6 @@ sendtoargv(argv, e)
**
** Returns:
** the controlling address.
-**
-** Side Effects:
-** none.
*/
ADDRESS *
@@ -1632,7 +1912,7 @@ getctladdr(a)
a = a->q_alias;
return a;
}
- /*
+/*
** SELF_REFERENCE -- check to see if an address references itself
**
** The check is done through a chain of aliases. If it is part of
@@ -1659,7 +1939,7 @@ self_reference(a)
ADDRESS *c; /* entry that point to a real mail box */
if (tTd(27, 1))
- dprintf("self_reference(%s)\n", a->q_paddr);
+ sm_dprintf("self_reference(%s)\n", a->q_paddr);
for (b = a->q_alias; b != NULL; b = b->q_alias)
{
@@ -1670,40 +1950,42 @@ self_reference(a)
if (b == NULL)
{
if (tTd(27, 1))
- dprintf("\t... no self ref\n");
+ sm_dprintf("\t... no self ref\n");
return NULL;
}
/*
** Pick the first address that resolved to a real mail box
- ** i.e has a pw entry. The returned value will be marked
+ ** i.e has a mbdb entry. The returned value will be marked
** QSELFREF in recipient(), which in turn will disable alias()
** from marking it as QS_IS_DEAD(), which mean it will be used
** as a deliverable address.
**
** The 2 key thing to note here are:
** 1) we are in a recursive call sequence:
- ** alias->sentolist->recipient->alias
+ ** alias->sendtolist->recipient->alias
** 2) normally, when we return back to alias(), the address
** will be marked QS_EXPANDED, since alias() assumes the
** expanded form will be used instead of the current address.
** This behaviour is turned off if the address is marked
- ** QSELFREF We set QSELFREF when we return to recipient().
+ ** QSELFREF. We set QSELFREF when we return to recipient().
*/
c = a;
while (c != NULL)
{
if (tTd(27, 10))
- dprintf(" %s", c->q_user);
+ sm_dprintf(" %s", c->q_user);
if (bitnset(M_HASPWENT, c->q_mailer->m_flags))
{
+ SM_MBDB_T user;
+
if (tTd(27, 2))
- dprintf("\t... getpwnam(%s)... ", c->q_user);
- if (sm_getpwnam(c->q_user) != NULL)
+ sm_dprintf("\t... getpwnam(%s)... ", c->q_user);
+ if (sm_mbdb_lookup(c->q_user, &user) == EX_OK)
{
if (tTd(27, 2))
- dprintf("found\n");
+ sm_dprintf("found\n");
/* ought to cache results here */
if (sameaddr(b, c))
@@ -1712,7 +1994,7 @@ self_reference(a)
return c;
}
if (tTd(27, 2))
- dprintf("failed\n");
+ sm_dprintf("failed\n");
}
else
{
@@ -1721,7 +2003,7 @@ self_reference(a)
b->q_mailer == c->q_mailer)
{
if (tTd(27, 2))
- dprintf("\t... local match (%s)\n",
+ sm_dprintf("\t... local match (%s)\n",
c->q_user);
if (sameaddr(b, c))
return b;
@@ -1730,12 +2012,12 @@ self_reference(a)
}
}
if (tTd(27, 10))
- dprintf("\n");
+ sm_dprintf("\n");
c = c->q_alias;
}
if (tTd(27, 1))
- dprintf("\t... cannot break loop for \"%s\"\n", a->q_paddr);
+ sm_dprintf("\t... cannot break loop for \"%s\"\n", a->q_paddr);
return NULL;
}
diff --git a/contrib/sendmail/src/sasl.c b/contrib/sendmail/src/sasl.c
new file mode 100644
index 0000000..9136f8a
--- /dev/null
+++ b/contrib/sendmail/src/sasl.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 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.
+ *
+ */
+
+#if SASL
+# include <sm/gen.h>
+SM_RCSID("@(#)$Id: sasl.c,v 8.11 2001/09/11 04:05:16 gshapiro Exp $")
+# include <stdlib.h>
+# include <sendmail.h>
+# include <errno.h>
+# include <sasl.h>
+
+/*
+** In order to ensure that storage leaks are tracked, and to prevent
+** conflicts between the sm_heap package and sasl, we tell sasl to
+** use the following heap allocation functions. Unfortunately,
+** the sasl package incorrectly specifies the size of a block
+** using unsigned long: for portability, it should be size_t.
+*/
+
+void *sm_sasl_malloc __P((unsigned long));
+static void *sm_sasl_calloc __P((unsigned long, unsigned long));
+static void *sm_sasl_realloc __P((void *, unsigned long));
+void sm_sasl_free __P((void *));
+
+/*
+** We can't use an rpool for Cyrus-SASL memory management routines,
+** since the encryption/decryption routines in Cyrus-SASL
+** allocate/deallocate a buffer each time. Since rpool
+** don't release memory until the very end, memory consumption is
+** proportional to the size of an e-mail, which is unacceptable.
+**
+*/
+
+/*
+** SM_SASL_MALLOC -- malloc() for SASL
+**
+** Parameters:
+** size -- size of requested memory.
+**
+** Returns:
+** pointer to memory.
+*/
+
+void *
+sm_sasl_malloc(size)
+ unsigned long size;
+{
+ return sm_malloc((size_t) size);
+}
+
+/*
+** SM_SASL_CALLOC -- calloc() for SASL
+**
+** Parameters:
+** nelem -- number of elements.
+** elemsize -- size of each element.
+**
+** Returns:
+** pointer to memory.
+**
+** Notice:
+** this isn't currently used by SASL.
+*/
+
+static void *
+sm_sasl_calloc(nelem, elemsize)
+ unsigned long nelem;
+ unsigned long elemsize;
+{
+ size_t size;
+ void *p;
+
+ size = (size_t) nelem * (size_t) elemsize;
+ p = sm_malloc(size);
+ if (p == NULL)
+ return NULL;
+ memset(p, '\0', size);
+ return p;
+}
+
+/*
+** SM_SASL_REALLOC -- realloc() for SASL
+**
+** Parameters:
+** p -- pointer to old memory.
+** size -- size of requested memory.
+**
+** Returns:
+** pointer to new memory.
+*/
+
+static void *
+sm_sasl_realloc(o, size)
+ void *o;
+ unsigned long size;
+{
+ return sm_realloc(o, (size_t) size);
+}
+
+/*
+** SM_SASL_FREE -- free() for SASL
+**
+** Parameters:
+** p -- pointer to free.
+**
+** Returns:
+** none
+*/
+
+void
+sm_sasl_free(p)
+ void *p;
+{
+ sm_free(p);
+}
+
+/*
+** SM_SASL_INIT -- sendmail specific SASL initialization
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** installs memory management routines for SASL.
+*/
+
+void
+sm_sasl_init()
+{
+ sasl_set_alloc(sm_sasl_malloc, sm_sasl_calloc,
+ sm_sasl_realloc, sm_sasl_free);
+}
+/*
+** INTERSECT -- create the intersection between two lists
+**
+** Parameters:
+** s1, s2 -- lists of items (separated by single blanks).
+** rpool -- resource pool from which result is allocated.
+**
+** Returns:
+** the intersection of both lists.
+*/
+
+char *
+intersect(s1, s2, rpool)
+ char *s1, *s2;
+ SM_RPOOL_T *rpool;
+{
+ char *hr, *h1, *h, *res;
+ int l1, l2, rl;
+
+ if (s1 == NULL || s2 == NULL) /* NULL string(s) -> NULL result */
+ return NULL;
+ l1 = strlen(s1);
+ l2 = strlen(s2);
+ rl = SM_MIN(l1, l2);
+ res = (char *) sm_rpool_malloc(rpool, rl + 1);
+ if (res == NULL)
+ return NULL;
+ *res = '\0';
+ if (rl == 0) /* at least one string empty? */
+ return res;
+ hr = res;
+ h1 = s1;
+ h = s1;
+
+ /* walk through s1 */
+ while (h != NULL && *h1 != '\0')
+ {
+ /* is there something after the current word? */
+ if ((h = strchr(h1, ' ')) != NULL)
+ *h = '\0';
+ l1 = strlen(h1);
+
+ /* does the current word appear in s2 ? */
+ if (iteminlist(h1, s2, " ") != NULL)
+ {
+ /* add a blank if not first item */
+ if (hr != res)
+ *hr++ = ' ';
+
+ /* copy the item */
+ memcpy(hr, h1, l1);
+
+ /* advance pointer in result list */
+ hr += l1;
+ *hr = '\0';
+ }
+ if (h != NULL)
+ {
+ /* there are more items */
+ *h = ' ';
+ h1 = h + 1;
+ }
+ }
+ return res;
+}
+#endif /* SASL */
diff --git a/contrib/sendmail/src/sendmail.h b/contrib/sendmail/src/sendmail.h
index 611116c..86a3c48 100644
--- a/contrib/sendmail/src/sendmail.h
+++ b/contrib/sendmail/src/sendmail.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
@@ -15,13 +15,10 @@
*/
#ifndef _SENDMAIL_H
-#define _SENDMAIL_H 1
+# define _SENDMAIL_H 1
#ifdef _DEFINE
# define EXTERN
-# ifndef lint
-static char SmailId[] = "@(#)$Id: sendmail.h,v 8.517.4.70 2001/08/14 23:08:12 ca Exp $";
-# endif /* ! lint */
#else /* _DEFINE */
# define EXTERN extern
#endif /* _DEFINE */
@@ -29,18 +26,9 @@ static char SmailId[] = "@(#)$Id: sendmail.h,v 8.517.4.70 2001/08/14 23:08:12 ca
#include <unistd.h>
-#if SFIO
-# include <sfio/stdio.h>
-# if defined(SFIO_VERSION) && SFIO_VERSION > 20000000L
- ERROR README: SFIO 2000 does not work with sendmail, use SFIO 1999 instead.
-# endif /* defined(SFIO_VERSION) && SFIO_VERSION > 20000000L */
-#endif /* SFIO */
-
#include <stddef.h>
#include <stdlib.h>
-#if !SFIO
-# include <stdio.h>
-#endif /* !SFIO */
+#include <stdio.h>
#include <ctype.h>
#include <setjmp.h>
#include <string.h>
@@ -48,11 +36,36 @@ static char SmailId[] = "@(#)$Id: sendmail.h,v 8.517.4.70 2001/08/14 23:08:12 ca
# ifdef EX_OK
# undef EX_OK /* for SVr4.2 SMP */
# endif /* EX_OK */
-#include <sysexits.h>
#include "sendmail/sendmail.h"
+
+/* profiling? */
+#if MONCONTROL
+# define SM_PROF(x) moncontrol(x)
+#else /* MONCONTROL */
+# define SM_PROF(x)
+#endif /* MONCONTROL */
+
+#ifdef _DEFINE
+# ifndef lint
+SM_UNUSED(static char SmailId[]) = "@(#)$Id: sendmail.h,v 8.902 2002/01/09 00:10:11 ca Exp $";
+# endif /* ! lint */
+#endif /* _DEFINE */
+
#include "bf.h"
#include "timers.h"
+#include <sm/exc.h>
+#include <sm/heap.h>
+#include <sm/debug.h>
+#include <sm/rpool.h>
+#include <sm/io.h>
+#include <sm/path.h>
+#include <sm/signal.h>
+#include <sm/clock.h>
+#include <sm/mbdb.h>
+#include <sm/errstring.h>
+#include <sm/sysexits.h>
+#include <sm/shm.h>
#ifdef LOG
# include <syslog.h>
@@ -91,9 +104,12 @@ static char SmailId[] = "@(#)$Id: sendmail.h,v 8.517.4.70 2001/08/14 23:08:12 ca
# undef NOERROR /* avoid <sys/streams.h> conflict */
# endif /* NOERROR */
# include <resolv.h>
+# else /* NAMED_BIND */
+# undef SM_SET_H_ERRNO
+# define SM_SET_H_ERRNO(err)
# endif /* NAMED_BIND */
-# ifdef HESIOD
+# if HESIOD
# include <hesiod.h>
# if !defined(HES_ER_OK) || defined(HESIOD_INTERFACES)
# define HESIOD_INIT /* support for the new interface */
@@ -101,13 +117,10 @@ static char SmailId[] = "@(#)$Id: sendmail.h,v 8.517.4.70 2001/08/14 23:08:12 ca
# endif /* HESIOD */
#if STARTTLS
-# if !SFIO && !_FFR_TLS_TOREK
- ERROR README: STARTTLS requires SFIO
-# endif /* !SFIO && !_FFR_TLS_TOREK */
-# if SFIO && _FFR_TLS_TOREK
- ERROR README: Can not do both SFIO and _FFR_TLS_TOREK
-# endif /* SFIO && _FFR_TLS_TOREK */
# include <openssl/ssl.h>
+# if !TLS_NO_RSA
+# define RSA_KEYLENGTH 512
+# endif /* !TLS_NO_RSA */
#endif /* STARTTLS */
#if SASL /* include the sasl include files if we have them */
@@ -165,11 +178,28 @@ static char SmailId[] = "@(#)$Id: sendmail.h,v 8.517.4.70 2001/08/14 23:08:12 ca
#endif /* ! INADDR_NONE */
+/*
+** An 'argument class' describes the storage allocation status
+** of an object pointed to by an argument to a function.
+*/
+
+typedef enum
+{
+ A_HEAP, /* the storage was allocated by malloc, and the
+ * ownership of the storage is ceded by the caller
+ * to the called function. */
+ A_TEMP, /* The storage is temporary, and is only guaranteed
+ * to be valid for the duration of the function call. */
+ A_PERM /* The storage is 'permanent': this might mean static
+ * storage, or rpool storage. */
+} ARGCLASS_T;
+
/* forward references for prototypes */
typedef struct envelope ENVELOPE;
typedef struct mailer MAILER;
+typedef struct queuegrp QUEUEGRP;
- /*
+/*
** Address structure.
** Addresses are stored internally in this structure.
*/
@@ -181,7 +211,7 @@ struct address
char *q_ruser; /* real user name, or NULL if q_user */
char *q_host; /* host name */
struct mailer *q_mailer; /* mailer to use */
- u_long q_flags; /* status flags, see below */
+ unsigned long q_flags; /* status flags, see below */
uid_t q_uid; /* user-id of receiver (if known) */
gid_t q_gid; /* group-id of receiver (if known) */
char *q_home; /* home dir (local mailer only) */
@@ -190,13 +220,20 @@ struct address
struct address *q_alias; /* address this results from */
char *q_owner; /* owner of q_alias */
struct address *q_tchain; /* temporary use chain */
+#if PIPELINING
+ struct address *q_pchain; /* chain for pipelining */
+#endif /* PIPELINING */
+ char *q_finalrcpt; /* Final-Recipient: DSN header */
char *q_orcpt; /* ORCPT parameter from RCPT TO: line */
char *q_status; /* status code for DSNs */
char *q_rstatus; /* remote status message for DSNs */
time_t q_statdate; /* date of status messages */
char *q_statmta; /* MTA generating q_rstatus */
short q_state; /* address state, see below */
- short q_specificity; /* how "specific" this address is */
+ char *q_signature; /* MX-based sorting value */
+ int q_qgrp; /* index into queue groups */
+ int q_qdir; /* queue directory inside group */
+ char *q_message; /* error message */
};
typedef struct address ADDRESS;
@@ -211,12 +248,15 @@ typedef struct address ADDRESS;
#define QPINGONSUCCESS 0x00000040 /* give return on successful delivery */
#define QPINGONFAILURE 0x00000080 /* give return on failure */
#define QPINGONDELAY 0x00000100 /* give return on message delay */
-#define QHASNOTIFY 0x00000200 /* propogate notify parameter */
+#define QHASNOTIFY 0x00000200 /* propagate notify parameter */
#define QRELAYED 0x00000400 /* DSN: relayed to non-DSN aware sys */
#define QEXPANDED 0x00000800 /* DSN: undergone list expansion */
#define QDELIVERED 0x00001000 /* DSN: successful final delivery */
#define QDELAYED 0x00002000 /* DSN: message delayed */
#define QALIAS 0x00004000 /* expanded alias */
+#define QBYTRACE 0x00008000 /* DeliverBy: trace */
+#define QBYNDELAY 0x00010000 /* DeliverBy: notify, delay */
+#define QBYNRELAY 0x00020000 /* DeliverBy: notify, relayed */
#define QTHISPASS 0x40000000 /* temp: address set this pass */
#define QRCPTOK 0x80000000 /* recipient() processed address */
@@ -227,41 +267,47 @@ typedef struct address ADDRESS;
#define QS_SENT 1 /* good address, delivery complete */
#define QS_BADADDR 2 /* illegal address */
#define QS_QUEUEUP 3 /* save address in queue */
-#define QS_VERIFIED 4 /* verified, but not expanded */
-#define QS_DONTSEND 5 /* don't send to this address */
-#define QS_EXPANDED 6 /* QS_DONTSEND: expanded */
-#define QS_SENDER 7 /* QS_DONTSEND: message sender (MeToo) */
-#define QS_CLONED 8 /* QS_DONTSEND: addr cloned to split envelope */
-#define QS_DISCARDED 9 /* QS_DONTSEND: rcpt discarded (EF_DISCARD) */
-#define QS_REPLACED 10 /* QS_DONTSEND: maplocaluser()/UserDB replaced */
-#define QS_REMOVED 11 /* QS_DONTSEND: removed (removefromlist()) */
-#define QS_DUPLICATE 12 /* QS_DONTSEND: duplicate suppressed */
-#define QS_INCLUDED 13 /* QS_DONTSEND: :include: delivery */
+#define QS_RETRY 4 /* retry delivery for next MX */
+#define QS_VERIFIED 5 /* verified, but not expanded */
+
+/*
+** Notice: all of the following values are variations of QS_DONTSEND.
+** If new states are added, they must be inserted in the proper place!
+** See the macro definition of QS_IS_DEAD() down below.
+*/
+
+#define QS_DONTSEND 6 /* don't send to this address */
+#define QS_EXPANDED 7 /* expanded */
+#define QS_SENDER 8 /* message sender (MeToo) */
+#define QS_CLONED 9 /* addr cloned to split envelope */
+#define QS_DISCARDED 10 /* rcpt discarded (EF_DISCARD) */
+#define QS_REPLACED 11 /* maplocaluser()/UserDB replaced */
+#define QS_REMOVED 12 /* removed (removefromlist()) */
+#define QS_DUPLICATE 13 /* duplicate suppressed */
+#define QS_INCLUDED 14 /* :include: delivery */
/* address state testing primitives */
#define QS_IS_OK(s) ((s) == QS_OK)
#define QS_IS_SENT(s) ((s) == QS_SENT)
#define QS_IS_BADADDR(s) ((s) == QS_BADADDR)
#define QS_IS_QUEUEUP(s) ((s) == QS_QUEUEUP)
+#define QS_IS_RETRY(s) ((s) == QS_RETRY)
#define QS_IS_VERIFIED(s) ((s) == QS_VERIFIED)
#define QS_IS_EXPANDED(s) ((s) == QS_EXPANDED)
#define QS_IS_REMOVED(s) ((s) == QS_REMOVED)
#define QS_IS_UNDELIVERED(s) ((s) == QS_OK || \
(s) == QS_QUEUEUP || \
+ (s) == QS_RETRY || \
(s) == QS_VERIFIED)
+#define QS_IS_UNMARKED(s) ((s) == QS_OK || \
+ (s) == QS_RETRY)
#define QS_IS_SENDABLE(s) ((s) == QS_OK || \
- (s) == QS_QUEUEUP)
+ (s) == QS_QUEUEUP || \
+ (s) == QS_RETRY)
#define QS_IS_ATTEMPTED(s) ((s) == QS_QUEUEUP || \
+ (s) == QS_RETRY || \
(s) == QS_SENT)
-#define QS_IS_DEAD(s) ((s) == QS_DONTSEND || \
- (s) == QS_CLONED || \
- (s) == QS_SENDER || \
- (s) == QS_DISCARDED || \
- (s) == QS_REPLACED || \
- (s) == QS_REMOVED || \
- (s) == QS_DUPLICATE || \
- (s) == QS_INCLUDED || \
- (s) == QS_EXPANDED)
+#define QS_IS_DEAD(s) ((s) >= QS_DONTSEND)
#define NULLADDR ((ADDRESS *) NULL)
@@ -274,19 +320,25 @@ extern char *crackaddr __P((char *));
extern bool emptyaddr __P((ADDRESS *));
extern ADDRESS *getctladdr __P((ADDRESS *));
extern int include __P((char *, bool, ADDRESS *, ADDRESS **, int, ENVELOPE *));
-extern bool invalidaddr __P((char *, char *));
-extern ADDRESS *parseaddr __P((char *, ADDRESS *, int, int, char **, ENVELOPE *));
-extern char **prescan __P((char *, int, char[], int, char **, u_char *));
+extern bool invalidaddr __P((char *, char *, bool));
+extern ADDRESS *parseaddr __P((char *, ADDRESS *, int, int, char **,
+ ENVELOPE *, bool));
+extern char **prescan __P((char *, int, char[], int, char **, unsigned char *));
extern void printaddr __P((ADDRESS *, bool));
extern ADDRESS *recipient __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
extern char *remotename __P((char *, MAILER *, int, int *, ENVELOPE *));
-extern int rewrite __P((char **, int, int, ENVELOPE *));
+extern int rewrite __P((char **, int, int, ENVELOPE *, int));
extern bool sameaddr __P((ADDRESS *, ADDRESS *));
extern int sendtolist __P((char *, ADDRESS *, ADDRESS **, int, ENVELOPE *));
+#if MILTER
extern int removefromlist __P((char *, ADDRESS **, ENVELOPE *));
+#endif /* MILTER */
extern void setsender __P((char *, ENVELOPE *, char **, int, bool));
- /*
+/* macro to simplify the common call to rewrite() */
+#define REWRITE(pvp, rs, env) rewrite(pvp, rs, 0, env, MAXATOM)
+
+/*
** Mailer definition structure.
** Every mailer known to the system is declared in this
** structure. It defines the pathname of the mailer, some
@@ -323,13 +375,12 @@ struct mailer
gid_t m_gid; /* GID to run as */
char *m_defcharset; /* default character set */
time_t m_wait; /* timeout to wait for end */
-#if _FFR_DYNAMIC_TOBUF
int m_maxrcpt; /* max recipients per envelope client-side */
-#endif /* _FFR_DYNAMIC_TOBUF */
+ short m_qgrp; /* queue group for this mailer */
};
/* bits for m_flags */
-#define M_ESMTP 'a' /* run Extended SMTP protocol */
+#define M_ESMTP 'a' /* run Extended SMTP */
#define M_ALIASABLE 'A' /* user can be LHS of an alias */
#define M_BLANKEND 'b' /* ensure blank line at end of message */
#define M_NOCOMMENT 'c' /* don't include comment part of address */
@@ -370,8 +421,10 @@ struct mailer
/* 'x' CF: include Full-Name: */
#define M_XDOT 'X' /* use hidden-dot algorithm */
#define M_LMTP 'z' /* run Local Mail Transport Protocol */
+#define M_DIALDELAY 'Z' /* apply dial delay sleeptime */
#define M_NOMX '0' /* turn off MX lookups */
#define M_NONULLS '1' /* don't send null bytes */
+#define M_FSMTP '2' /* force SMTP (no ESMTP even if offered) */
#define M_EBCDIC '3' /* extend Q-P encoding for EBCDIC */
#define M_TRYRULESET5 '5' /* use ruleset 5 after local aliasing */
#define M_7BITHDRS '6' /* strip headers to 7 bits even in 8 bit path */
@@ -390,8 +443,162 @@ struct mailer
/* functions */
extern void initerrmailers __P((void));
extern void makemailer __P((char *));
+extern void makequeue __P((char *, bool));
+extern void runqueueevent __P((int));
+
+EXTERN MAILER *FileMailer; /* ptr to *file* mailer */
+EXTERN MAILER *InclMailer; /* ptr to *include* mailer */
+EXTERN MAILER *LocalMailer; /* ptr to local mailer */
+EXTERN MAILER *ProgMailer; /* ptr to program mailer */
+EXTERN MAILER *Mailer[MAXMAILERS + 1];
+
+/*
+** Queue group definition structure.
+** Every queue group known to the system is declared in this structure.
+** It defines the basic pathname of the queue group, some flags
+** associated with it, and the argument vector to pass to it.
+*/
+
+struct qpaths_s
+{
+ char *qp_name; /* name of queue dir, relative path */
+ short qp_subdirs; /* use subdirs? */
+ short qp_fsysidx; /* file system index of this directory */
+# if SM_CONF_SHM
+ int qp_idx; /* index into array for queue information */
+# endif /* SM_CONF_SHM */
+};
+
+typedef struct qpaths_s QPATHS;
+
+struct queuegrp
+{
+ char *qg_name; /* symbolic name of this queue group */
+
+ /*
+ ** For now this is the same across all queue groups.
+ ** Otherwise we have to play around with chdir().
+ */
+
+ char *qg_qdir; /* common component of queue directory */
+ short qg_index; /* queue number internally, index in Queue[] */
+ int qg_maxqrun; /* max # of jobs in 1 queuerun */
+ int qg_numqueues; /* number of queues in this queue */
+
+ /*
+ ** qg_queueintvl == 0 denotes that no individual value is used.
+ ** Whatever accesses this must deal with "<= 0" as
+ ** "not set, use appropriate default".
+ */
+
+ time_t qg_queueintvl; /* interval for queue runs */
+ QPATHS *qg_qpaths; /* list of queue directories */
+ BITMAP256 qg_flags; /* status flags, see below */
+ short qg_nice; /* niceness for queue run */
+ int qg_wgrp; /* Assigned to this work group */
+ int qg_maxlist; /* max items in work queue for this group */
+ int qg_curnum; /* current number of queue for queue runs */
+ int qg_maxrcpt; /* max recipients per envelope, 0==no limit */
+
+#if 0
+ short qg_sortorder; /* how do we sort this queuerun */
+ long qg_wkrcptfact; /* multiplier for # recipients -> priority */
+ long qg_qfactor; /* slope of queue function */
+ bool qg_doqueuerun; /* XXX flag is it time to do a queuerun */
+#endif /* 0 */
+};
+
+/* bits for qg_flags (XXX: unused as of now) */
+#define QD_DEFINED ((char) 1) /* queue group has been defined */
+#define QD_FORK 'f' /* fork queue runs */
+
+extern void filesys_update __P((void));
+#if _FFR_ANY_FREE_FS
+extern bool filesys_free __P((long));
+#endif /* _FFR_ANY_FREE_FS */
+
+#if SASL
+/*
+** SASL
+*/
+
+/* lines in authinfo file or index into SASL_AI_T */
+# define SASL_WRONG (-1)
+# define SASL_USER 0 /* authorization id (user) */
+# define SASL_AUTHID 1 /* authentication id */
+# define SASL_PASSWORD 2 /* password fuer authid */
+# define SASL_DEFREALM 3 /* realm to use */
+# define SASL_MECHLIST 4 /* list of mechanisms to try */
+# define SASL_ID_REALM 5 /* authid@defrealm */
+
+/*
+** Current mechanism; this is just used to convey information between
+** invocation of SASL callback functions.
+** It must be last in the list, because it's not allocated by us
+** and hence we don't free() it.
+*/
+# define SASL_MECH 6
+# define SASL_ENTRIES 7 /* number of entries in array */
+
+# define SASL_USER_BIT (1 << SASL_USER)
+# define SASL_AUTHID_BIT (1 << SASL_AUTHID)
+# define SASL_PASSWORD_BIT (1 << SASL_PASSWORD)
+# define SASL_DEFREALM_BIT (1 << SASL_DEFREALM)
+# define SASL_MECHLIST_BIT (1 << SASL_MECHLIST)
+
+/* authenticated? */
+# define SASL_NOT_AUTH 0 /* not authenticated */
+# define SASL_PROC_AUTH 1 /* in process of authenticating */
+# define SASL_IS_AUTH 2 /* authenticated */
+
+/* SASL options */
+# define SASL_AUTH_AUTH 0x1000 /* use auth= only if authenticated */
+# define SASL_SEC_MASK 0x0fff /* mask for SASL_SEC_* values: sasl.h */
+# if (SASL_SEC_NOPLAINTEXT & SASL_SEC_MASK) == 0 || \
+ (SASL_SEC_NOACTIVE & SASL_SEC_MASK) == 0 || \
+ (SASL_SEC_NODICTIONARY & SASL_SEC_MASK) == 0 || \
+ (SASL_SEC_FORWARD_SECRECY & SASL_SEC_MASK) == 0 || \
+ (SASL_SEC_NOANONYMOUS & SASL_SEC_MASK) == 0 || \
+ (SASL_SEC_PASS_CREDENTIALS & SASL_SEC_MASK) == 0
+ERROR: change SASL_SEC_MASK_ notify sendmail.org!
+# endif /* SASL_SEC_NOPLAINTEXT & SASL_SEC_MASK) == 0 ... */
+# define MAXOUTLEN 1024 /* length of output buffer */
+
+/* functions */
+extern char *intersect __P((char *, char *, SM_RPOOL_T *));
+extern char *iteminlist __P((char *, char *, char *));
+extern int proxy_policy __P((void *, const char *, const char *, const char **, const char **));
+# if SASL > 10515
+extern int safesaslfile __P((void *, char *, int));
+# else /* SASL > 10515 */
+extern int safesaslfile __P((void *, char *));
+# endif /* SASL > 10515 */
+extern int sasl_decode64 __P((const char *, unsigned, char *, unsigned *));
+extern int sasl_encode64 __P((const char *, unsigned, char *, unsigned, unsigned *));
+extern void stop_sasl_client __P((void));
+
+/* structure to store authinfo */
+typedef char *SASL_AI_T[SASL_ENTRIES];
+
+EXTERN char *AuthMechanisms; /* AUTH mechanisms */
+EXTERN char *SASLInfo; /* file with AUTH info */
+EXTERN int SASLOpts; /* options for SASL */
+EXTERN int MaxSLBits; /* max. encryption bits for SASL */
+#endif /* SASL */
- /*
+/*
+** Structure to store macros.
+*/
+typedef struct
+{
+ SM_RPOOL_T *mac_rpool; /* resource pool */
+ BITMAP256 mac_allocated; /* storage has been alloc()? */
+ char *mac_table[MAXMACROID + 1]; /* macros */
+} MACROS_T;
+
+EXTERN MACROS_T GlobalMacros;
+
+/*
** Information about currently open connections to mailers, or to
** hosts that we have looked up recently.
*/
@@ -400,20 +607,15 @@ extern void makemailer __P((char *));
MCI
{
- u_long mci_flags; /* flag bits, see below */
+ unsigned long mci_flags; /* flag bits, see below */
short mci_errno; /* error number on last connection */
short mci_herrno; /* h_errno from last DNS lookup */
short mci_exitstat; /* exit status from last connection */
short mci_state; /* SMTP state */
int mci_deliveries; /* delivery attempts for connection */
long mci_maxsize; /* max size this server will accept */
-#if SFIO
- Sfio_t *mci_in; /* input side of connection */
- Sfio_t *mci_out; /* output side of connection */
-#else /* SFIO */
- FILE *mci_in; /* input side of connection */
- FILE *mci_out; /* output side of connection */
-#endif /* SFIO */
+ SM_FILE_T *mci_in; /* input side of connection */
+ SM_FILE_T *mci_out; /* output side of connection */
pid_t mci_pid; /* process id of subordinate proc */
char *mci_phase; /* SMTP phase string */
struct mailer *mci_mailer; /* ptr to the mailer for this conn */
@@ -421,9 +623,18 @@ MCI
char *mci_status; /* DSN status to be copied to addrs */
char *mci_rstatus; /* SMTP status to be copied to addrs */
time_t mci_lastuse; /* last usage time */
- FILE *mci_statfile; /* long term status file */
+ SM_FILE_T *mci_statfile; /* long term status file */
char *mci_heloname; /* name to use as HELO arg */
+ long mci_min_by; /* minimum DELIVERBY */
+ bool mci_retryrcpt; /* tempfail for at least one rcpt */
+ char *mci_tolist; /* list of valid recipients */
+ SM_RPOOL_T *mci_rpool; /* resource pool */
+#if PIPELINING
+ int mci_okrcpts; /* number of valid recipients */
+ ADDRESS *mci_nextaddr; /* next address for pipelined status */
+#endif /* PIPELINING */
#if SASL
+ SASL_AI_T mci_sai; /* authentication info */
bool mci_sasl_auth; /* authenticated? */
int mci_sasl_string_len;
char *mci_sasl_string; /* sasl reply string */
@@ -433,12 +644,13 @@ MCI
#if STARTTLS
SSL *mci_ssl; /* SSL connection */
#endif /* STARTTLS */
+ MACROS_T mci_macro; /* macro definitions */
};
/* flag bits */
#define MCIF_VALID 0x00000001 /* this entry is valid */
-#define MCIF_TEMP 0x00000002 /* don't cache this connection */
+/* 0x00000002 unused, was MCIF_TEMP */
#define MCIF_CACHED 0x00000004 /* currently in open cache */
#define MCIF_ESMTP 0x00000008 /* this host speaks ESMTP */
#define MCIF_EXPN 0x00000010 /* EXPN command supported */
@@ -455,6 +667,7 @@ MCI
#define MCIF_AUTH 0x00008000 /* AUTH= supported */
#define MCIF_AUTHACT 0x00010000 /* SASL (AUTH) active */
#define MCIF_ENHSTAT 0x00020000 /* ENHANCEDSTATUSCODES supported */
+#define MCIF_PIPELINED 0x00040000 /* PIPELINING supported */
#if STARTTLS
#define MCIF_TLS 0x00100000 /* STARTTLS supported */
#define MCIF_TLSACT 0x00200000 /* STARTTLS active */
@@ -462,17 +675,22 @@ MCI
#else /* STARTTLS */
#define MCIF_EXTENS (MCIF_EXPN | MCIF_SIZE | MCIF_8BITMIME | MCIF_DSN | MCIF_8BITOK | MCIF_AUTH | MCIF_ENHSTAT)
#endif /* STARTTLS */
+#define MCIF_DLVR_BY 0x00400000 /* DELIVERBY */
+#if _FFR_IGNORE_EXT_ON_HELO
+# define MCIF_HELO 0x00800000 /* we used HELO: ignore extensions */
+#endif /* _FFR_IGNORE_EXT_ON_HELO */
#define MCIF_ONLY_EHLO 0x10000000 /* use only EHLO in smtpinit */
-
/* states */
#define MCIS_CLOSED 0 /* no traffic on this connection */
#define MCIS_OPENING 1 /* sending initial protocol */
#define MCIS_OPEN 2 /* open, initial protocol sent */
-#define MCIS_ACTIVE 3 /* message being sent */
-#define MCIS_QUITING 4 /* running quit protocol */
-#define MCIS_SSD 5 /* service shutting down */
-#define MCIS_ERROR 6 /* I/O error on connection */
+#define MCIS_MAIL 3 /* MAIL command sent */
+#define MCIS_RCPT 4 /* RCPT commands being sent */
+#define MCIS_DATA 5 /* DATA command sent */
+#define MCIS_QUITING 6 /* running quit protocol */
+#define MCIS_SSD 7 /* service shutting down */
+#define MCIS_ERROR 8 /* I/O error on connection */
/* functions */
extern void mci_cache __P((MCI *));
@@ -490,7 +708,11 @@ extern void mci_store_persistent __P((MCI *));
extern int mci_traverse_persistent __P((int (*)(), char *));
extern void mci_unlock_host __P((MCI *));
- /*
+EXTERN int MaxMciCache; /* maximum entries in MCI cache */
+EXTERN time_t MciCacheTimeout; /* maximum idle time on connections */
+EXTERN time_t MciInfoTimeout; /* how long 'til we retry down hosts */
+
+/*
** Header structure.
** This structure is used internally to store header items.
*/
@@ -500,8 +722,8 @@ struct header
char *h_field; /* the name of the field */
char *h_value; /* the value of that field */
struct header *h_link; /* the next header */
- u_char h_macro; /* include header if macro defined */
- u_long h_flags; /* status bits, see below */
+ unsigned char h_macro; /* include header if macro defined */
+ unsigned long h_flags; /* status bits, see below */
BITMAP256 h_mflags; /* m_flags bits needed */
};
@@ -515,9 +737,9 @@ typedef struct header HDR;
struct hdrinfo
{
- char *hi_field; /* the name of the field */
- u_long hi_flags; /* status bits, see below */
- char *hi_ruleset; /* validity check ruleset */
+ char *hi_field; /* the name of the field */
+ unsigned long hi_flags; /* status bits, see below */
+ char *hi_ruleset; /* validity check ruleset */
};
extern struct hdrinfo HdrInfo[];
@@ -547,20 +769,20 @@ extern struct hdrinfo HdrInfo[];
#define CHHDR_DEF 0x0001 /* default header */
#define CHHDR_CHECK 0x0002 /* call ruleset for header */
#define CHHDR_USER 0x0004 /* header from user */
-#define CHHDR_QUEUE 0x0008 /* header from qf file */
+#define CHHDR_QUEUE 0x0008 /* header from queue file */
/* functions */
-extern void addheader __P((char *, char *, int, HDR **));
-extern u_long chompheader __P((char *, int, HDR **, ENVELOPE *));
+extern void addheader __P((char *, char *, int, ENVELOPE *));
+extern unsigned long chompheader __P((char *, int, HDR **, ENVELOPE *));
extern void commaize __P((HDR *, char *, bool, MCI *, ENVELOPE *));
-extern HDR *copyheader __P((HDR *));
-extern void eatheader __P((ENVELOPE *, bool));
+extern HDR *copyheader __P((HDR *, SM_RPOOL_T *));
+extern void eatheader __P((ENVELOPE *, bool, bool));
extern char *hvalue __P((char *, HDR *));
extern bool isheader __P((char *));
extern void putfromline __P((MCI *, ENVELOPE *));
extern void setupheaders __P((void));
- /*
+/*
** Performance monitoring
*/
@@ -575,7 +797,7 @@ TIMERS
#define PUSHTIMER(l, t) { if (tTd(98, l)) pushtimer(&t); }
#define POPTIMER(l, t) { if (tTd(98, l)) poptimer(&t); }
- /*
+/*
** Envelope structure.
** This structure defines the message itself. There is usually
** only one of these -- for the message that we originally read
@@ -589,7 +811,7 @@ struct envelope
HDR *e_header; /* head of header list */
long e_msgpriority; /* adjusted priority of this message */
time_t e_ctime; /* time message appeared in the queue */
- char *e_to; /* the target person */
+ char *e_to; /* (list of) target person(s) */
ADDRESS e_from; /* the person it is from */
char *e_sender; /* e_from.q_paddr w comments stripped */
char **e_fromdomain; /* the domain part of the sender */
@@ -601,8 +823,10 @@ struct envelope
** to unsigned. We don't use unsigned and == ULONG_MAX because
** some libc's don't have strtoul(), see mail_esmtp_args().
*/
+
long e_msgsize; /* size of the message in bytes */
- long e_flags; /* flags, see below */
+ char *e_msgid; /* message id (for logging) */
+ unsigned long e_flags; /* flags, see below */
int e_nrcpts; /* number of recipients */
short e_class; /* msg class (priority, junk, etc.) */
short e_hopcount; /* number of times processed */
@@ -617,29 +841,46 @@ struct envelope
ENVELOPE *e_parent; /* the message this one encloses */
ENVELOPE *e_sibling; /* the next envelope of interest */
char *e_bodytype; /* type of message body */
- FILE *e_dfp; /* data file */
+ SM_FILE_T *e_dfp; /* data file */
char *e_id; /* code for this entry in queue */
- int e_queuedir; /* index into queue directories */
- FILE *e_xfp; /* transcript file */
- FILE *e_lockfp; /* the lock file for this message */
- char *e_message; /* error message */
- char *e_statmsg; /* stat msg (changes per delivery) */
+ int e_qgrp; /* queue group (index into queues) */
+ int e_qdir; /* index into queue directories */
+ int e_dfqgrp; /* data file queue group index */
+ int e_dfqdir; /* data file queue directory index */
+ int e_xfqgrp; /* queue group (index into queues) */
+ int e_xfqdir; /* index into queue directories (xf) */
+ SM_FILE_T *e_xfp; /* transcript file */
+ SM_FILE_T *e_lockfp; /* the lock file for this message */
+ char *e_message; /* error message; readonly; NULL, or
+ * static storage, or allocated from
+ * e_rpool */
+ char *e_statmsg; /* stat msg (changes per delivery).
+ * readonly. NULL or allocated from
+ * e_rpool. */
+#if _FFR_QUARANTINE
+ char *e_quarmsg; /* why envelope is quarantined */
+ char e_qfletter; /* queue file letter on disk */
+#endif /* _FFR_QUARANTINE */
char *e_msgboundary; /* MIME-style message part boundary */
char *e_origrcpt; /* original recipient (one only) */
char *e_envid; /* envelope id from MAIL FROM: line */
char *e_status; /* DSN status for this message */
time_t e_dtime; /* time of last delivery attempt */
int e_ntries; /* number of delivery attempts */
- dev_t e_dfdev; /* df file's device, for crash recov */
- ino_t e_dfino; /* df file's ino, for crash recovery */
- char *e_macro[MAXMACROID + 1]; /* macro definitions */
- char *e_if_macros[2]; /* HACK: incoming interface info */
- char *e_auth_param;
+ dev_t e_dfdev; /* data file device (crash recovery) */
+ ino_t e_dfino; /* data file inode (crash recovery) */
+ MACROS_T e_macro; /* macro definitions */
+ MCI *e_mci; /* connection info */
+ char *e_auth_param; /* readonly; NULL or static storage or
+ * allocated from e_rpool */
TIMERS e_timers; /* per job timers */
#if _FFR_QUEUEDELAY
int e_queuealg; /* algorithm for queue delay */
time_t e_queuedelay; /* current delay */
#endif /* _FFR_QUEUEDELAY */
+ long e_deliver_by; /* deliver by */
+ int e_dlvr_flag; /* deliver by flag */
+ SM_RPOOL_T *e_rpool; /* resource pool for this envelope */
};
/* values for e_flags */
@@ -664,24 +905,39 @@ struct envelope
#define EF_NL_NOT_EOL 0x0040000L /* don't accept raw NL as EOLine */
#define EF_CRLF_NOT_EOL 0x0080000L /* don't accept CR-LF as EOLine */
#define EF_RET_PARAM 0x0100000L /* RCPT command had RET argument */
-#define EF_HAS_DF 0x0200000L /* set when df file is instantiated */
+#define EF_HAS_DF 0x0200000L /* set when data file is instantiated */
#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 */
+#define EF_SPLIT 0x4000000L /* envelope has been split */
+#define EF_UNSAFE 0x8000000L /* unsafe: read from untrusted source */
-/* values for e_if_macros */
-#define EIF_ADDR 0 /* ${if_addr} */
+#define DLVR_NOTIFY 0x01
+#define DLVR_RETURN 0x02
+#define DLVR_TRACE 0x10
+#define IS_DLVR_NOTIFY(e) (((e)->e_dlvr_flag & DLVR_NOTIFY) != 0)
+#define IS_DLVR_RETURN(e) (((e)->e_dlvr_flag & DLVR_RETURN) != 0)
+#define IS_DLVR_TRACE(e) (((e)->e_dlvr_flag & DLVR_TRACE) != 0)
+#define IS_DLVR_BY(e) ((e)->e_dlvr_flag != 0)
+
+#define BODYTYPE_NONE (0)
+#define BODYTYPE_7BIT (1)
+#define BODYTYPE_8BITMIME (2)
+#define BODYTYPE_ILLEGAL (-1)
+#define BODYTYPE_VALID(b) ((b) == BODYTYPE_7BIT || (b) == BODYTYPE_8BITMIME)
+
+extern ENVELOPE BlankEnvelope;
/* functions */
-extern void clearenvelope __P((ENVELOPE *, bool));
-extern void dropenvelope __P((ENVELOPE *, bool));
-extern ENVELOPE *newenvelope __P((ENVELOPE *, ENVELOPE *));
+extern void clearenvelope __P((ENVELOPE *, bool, SM_RPOOL_T *));
+extern void dropenvelope __P((ENVELOPE *, bool, bool));
+extern ENVELOPE *newenvelope __P((ENVELOPE *, ENVELOPE *, SM_RPOOL_T *));
extern void printenvflags __P((ENVELOPE *));
extern void putbody __P((MCI *, ENVELOPE *, char *));
extern void putheader __P((MCI *, HDR *, ENVELOPE *, int));
- /*
+/*
** Message priority classes.
**
** The message class is read directly from the Priority: header
@@ -711,7 +967,10 @@ struct priority
int pri_val; /* internal value for same */
};
- /*
+EXTERN int NumPriorities; /* pointer into Priorities */
+EXTERN struct priority Priorities[MAXPRIORITIES];
+
+/*
** Rewrite rules.
*/
@@ -732,35 +991,35 @@ struct rewrite
*/
/* left hand side items */
-#define MATCHZANY ((u_char)0220) /* match zero or more tokens */
-#define MATCHANY ((u_char)0221) /* match one or more tokens */
-#define MATCHONE ((u_char)0222) /* match exactly one token */
-#define MATCHCLASS ((u_char)0223) /* match one token in a class */
-#define MATCHNCLASS ((u_char)0224) /* match anything not in class */
-#define MATCHREPL ((u_char)0225) /* replacement on RHS for above */
+#define MATCHZANY ((unsigned char)0220) /* match zero or more tokens */
+#define MATCHANY ((unsigned char)0221) /* match one or more tokens */
+#define MATCHONE ((unsigned char)0222) /* match exactly one token */
+#define MATCHCLASS ((unsigned char)0223) /* match one token in a class */
+#define MATCHNCLASS ((unsigned char)0224) /* match anything not in class */
+#define MATCHREPL ((unsigned char)0225) /* replacement on RHS for above */
/* right hand side items */
-#define CANONNET ((u_char)0226) /* canonical net, next token */
-#define CANONHOST ((u_char)0227) /* canonical host, next token */
-#define CANONUSER ((u_char)0230) /* canonical user, next N tokens */
-#define CALLSUBR ((u_char)0231) /* call another rewriting set */
+#define CANONNET ((unsigned char)0226) /* canonical net, next token */
+#define CANONHOST ((unsigned char)0227) /* canonical host, next token */
+#define CANONUSER ((unsigned char)0230) /* canonical user, next N tokens */
+#define CALLSUBR ((unsigned char)0231) /* call another rewriting set */
/* conditionals in macros */
-#define CONDIF ((u_char)0232) /* conditional if-then */
-#define CONDELSE ((u_char)0233) /* conditional else */
-#define CONDFI ((u_char)0234) /* conditional fi */
+#define CONDIF ((unsigned char)0232) /* conditional if-then */
+#define CONDELSE ((unsigned char)0233) /* conditional else */
+#define CONDFI ((unsigned char)0234) /* conditional fi */
/* bracket characters for host name lookup */
-#define HOSTBEGIN ((u_char)0235) /* hostname lookup begin */
-#define HOSTEND ((u_char)0236) /* hostname lookup end */
+#define HOSTBEGIN ((unsigned char)0235) /* hostname lookup begin */
+#define HOSTEND ((unsigned char)0236) /* hostname lookup end */
/* bracket characters for generalized lookup */
-#define LOOKUPBEGIN ((u_char)0205) /* generalized lookup begin */
-#define LOOKUPEND ((u_char)0206) /* generalized lookup end */
+#define LOOKUPBEGIN ((unsigned char)0205) /* generalized lookup begin */
+#define LOOKUPEND ((unsigned char)0206) /* generalized lookup end */
/* macro substitution character */
-#define MACROEXPAND ((u_char)0201) /* macro expansion */
-#define MACRODEXPAND ((u_char)0202) /* deferred macro expansion */
+#define MACROEXPAND ((unsigned char)0201) /* macro expansion */
+#define MACRODEXPAND ((unsigned char)0202) /* deferred macro expansion */
/* to make the code clearer */
#define MATCHZERO CANONHOST
@@ -770,26 +1029,50 @@ struct rewrite
/* external <==> internal mapping table */
struct metamac
{
- char metaname; /* external code (after $) */
- u_char metaval; /* internal code (as above) */
+ char metaname; /* external code (after $) */
+ unsigned char metaval; /* internal code (as above) */
};
/* values for macros with external names only */
#define MID_OPMODE 0202 /* operation mode */
/* functions */
-extern void define __P((int, char *, ENVELOPE *));
+#if SM_HEAP_CHECK
+extern void
+macdefine_tagged __P((
+ MACROS_T *_mac,
+ ARGCLASS_T _vclass,
+ int _id,
+ char *_value,
+ char *_file,
+ int _line,
+ int _group));
+# define macdefine(mac,c,id,v) \
+ macdefine_tagged(mac,c,id,v,__FILE__,__LINE__,sm_heap_group())
+#else /* SM_HEAP_CHECK */
+extern void
+macdefine __P((
+ MACROS_T *_mac,
+ ARGCLASS_T _vclass,
+ int _id,
+ char *_value));
+# define macdefine_tagged(mac,c,id,v,file,line,grp) macdefine(mac,c,id,v)
+#endif /* SM_HEAP_CHECK */
+extern void macset __P((MACROS_T *, int, char *));
+#define macget(mac, i) (mac)->mac_table[i]
extern void expand __P((char *, char *, size_t, ENVELOPE *));
-extern int macid __P((char *, char **));
+extern int macid_parse __P((char *, char **));
+#define macid(name) macid_parse(name, NULL)
extern char *macname __P((int));
extern char *macvalue __P((int, ENVELOPE *));
-extern int rscheck __P((char *, char *, char *, ENVELOPE *, bool, bool, int, char *));
+extern int rscheck __P((char *, char *, char *, ENVELOPE *, bool, bool, int, char *, char *));
+extern int rscap __P((char *, char *, char *, ENVELOPE *, char ***, char *, int));
extern void setclass __P((int, char *));
extern int strtorwset __P((char *, char **, int));
extern void translate_dollars __P((char *));
extern bool wordinclass __P((char *, int));
- /*
+/*
** Name canonification short circuit.
**
** If the name server for a host is down, the process of trying to
@@ -808,16 +1091,29 @@ NAMECANON
short nc_stat; /* cached exit status code */
short nc_flags; /* flag bits */
char *nc_cname; /* the canonical name */
+ time_t nc_exp; /* entry expires at */
};
/* values for nc_flags */
#define NCF_VALID 0x0001 /* entry valid */
+/* hostsignature structure */
+
+struct hostsig_t
+{
+ char *hs_sig; /* hostsignature */
+ time_t hs_exp; /* entry expires at */
+};
+
+typedef struct hostsig_t HOSTSIG_T;
+
/* functions */
-extern bool getcanonname __P((char *, int, bool));
-extern int getmxrr __P((char *, char **, u_short *, bool, int *));
+extern bool getcanonname __P((char *, int, bool, int *));
+extern int getmxrr __P((char *, char **, unsigned short *, bool, int *, bool, int *));
+extern char *hostsignature __P((MAILER *, char *));
+extern int getfallbackmxrr __P((char *));
- /*
+/*
** Mapping functions
**
** These allow arbitrary mappings in the config file. The idea
@@ -836,6 +1132,7 @@ extern int getmxrr __P((char *, char **, u_short *, bool, int *));
MAP
{
MAPCLASS *map_class; /* the class of this map */
+ MAPCLASS *map_orgclass; /* the original class of this map */
char *map_mname; /* name of this map */
long map_mflags; /* flags, see below */
char *map_file; /* the (nominal) filename */
@@ -843,8 +1140,8 @@ MAP
ARBPTR_T map_db2; /* an "extra" database pointer */
char *map_keycolnm; /* key column name */
char *map_valcolnm; /* value column name */
- u_char map_keycolno; /* key column number */
- u_char map_valcolno; /* value column number */
+ unsigned char map_keycolno; /* key column number */
+ unsigned char map_valcolno; /* value column number */
char map_coldelim; /* column delimiter */
char map_spacesub; /* spacesub */
char *map_app; /* to append to successful matches */
@@ -852,6 +1149,8 @@ MAP
char *map_domain; /* the (nominal) NIS domain */
char *map_rebuild; /* program to run to do auto-rebuild */
time_t map_mtime; /* last database modification time */
+ time_t map_timeout; /* timeout for map accesses */
+ int map_retry; /* # of retries for map accesses */
pid_t map_pid; /* PID of process which opened map */
int map_lockfd; /* auxiliary lock file descriptor */
short map_specificity; /* specificity of aliases */
@@ -875,15 +1174,17 @@ MAP
#define MF_ALIASWAIT 0x00000800 /* alias map in aliaswait state */
#define MF_IMPL_HASH 0x00001000 /* implicit: underlying hash database */
#define MF_IMPL_NDBM 0x00002000 /* implicit: underlying NDBM database */
-#define MF_UNSAFEDB 0x00004000 /* this map is world writable */
+/* 0x00004000 */
#define MF_APPEND 0x00008000 /* append new entry on rebuild */
#define MF_KEEPQUOTES 0x00010000 /* don't dequote key before lookup */
#define MF_NODEFER 0x00020000 /* don't defer if map lookup fails */
#define MF_REGEX_NOT 0x00040000 /* regular expression negation */
#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 */
+/* 0x00200000 available for use */
+#define MF_FILECLASS 0x00400000 /* this is a file class map */
+#define MF_OPENBOGUS 0x00800000 /* open failed, don't call map_close */
+#define MF_CLOSING 0x01000000 /* map is being closed */
#define DYNOPENMAP(map) if (!bitset(MF_OPEN, (map)->map_mflags)) \
{ \
@@ -897,6 +1198,15 @@ MAP
#define MA_UNAVAIL 1 /* member map is not available */
#define MA_TRYAGAIN 2 /* member map returns temp failure */
+/* macros to handle MapTempFail */
+#define BIT_IS_MTP 0x01 /* temp.failure occurred */
+#define BIT_ASK_MTP 0x02 /* do we care about MapTempFail? */
+#define RESET_MAPTEMPFAIL MapTempFail = 0
+#define INIT_MAPTEMPFAIL MapTempFail = BIT_ASK_MTP
+#define SET_MAPTEMPFAIL MapTempFail |= BIT_IS_MTP
+#define IS_MAPTEMPFAIL bitset(BIT_IS_MTP, MapTempFail)
+#define ASK_MAPTEMPFAIL bitset(BIT_ASK_MTP, MapTempFail)
+
/*
** The class of a map -- essentially the functions to call
*/
@@ -923,9 +1233,10 @@ MAPCLASS
#define MCF_ALIASONLY 0x0002 /* usable only for aliases */
#define MCF_REBUILDABLE 0x0004 /* can rebuild alias files */
#define MCF_OPTFILE 0x0008 /* file name is optional */
+#define MCF_NOTPERSIST 0x0010 /* don't keep map open all the time */
/* functions */
-extern void closemaps __P((void));
+extern void closemaps __P((bool));
extern bool impl_map_open __P((MAP *, int));
extern void initmaps __P((void));
extern MAP *makemapentry __P((char *));
@@ -938,47 +1249,13 @@ extern bool openmap __P((MAP *));
#if USERDB
extern void _udbx_close __P((void));
extern int udbexpand __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
-extern char *udbsender __P((char *));
+extern char *udbsender __P((char *, SM_RPOOL_T *));
#endif /* USERDB */
- /*
+
+/*
** LDAP related items
*/
-#ifdef LDAPMAP
-struct ldapmap_struct
-{
- /* needed for ldap_open or ldap_init */
- char *ldap_host;
- int ldap_port;
-
- /* options set in ld struct before ldap_bind_s */
- int ldap_deref;
- time_t ldap_timelimit;
- int ldap_sizelimit;
- int ldap_options;
-
- /* args for ldap_bind_s */
- LDAP *ldap_ld;
- char *ldap_binddn;
- char *ldap_secret;
- int ldap_method;
-
- /* args for ldap_search */
- char *ldap_base;
- int ldap_scope;
- char *ldap_filter;
- char *ldap_attr[LDAPMAP_MAX_ATTR + 1];
- bool ldap_attrsonly;
-
- /* args for ldap_result */
- struct timeval ldap_timeout;
- LDAPMessage *ldap_res;
-
- /* Linked list of maps sharing the same LDAP binding */
- MAP *ldap_next;
-};
-
-typedef struct ldapmap_struct LDAPMAP_STRUCT;
-
+#if LDAPMAP
/* struct defining LDAP Auth Methods */
struct lamvalues
{
@@ -1005,35 +1282,29 @@ extern bool ldapmap_parseargs __P((MAP *, char *));
extern void ldapmap_set_defaults __P((char *));
#endif /* LDAPMAP */
- /*
+/*
** PH related items
*/
-#ifdef PH_MAP
+#if PH_MAP
+
+# include <phclient.h>
+
struct ph_map_struct
{
- char *ph_servers; /* list of ph servers */
- char *ph_field_list; /* list of fields to search for match */
- FILE *ph_to_server;
- FILE *ph_from_server;
- int ph_sockfd;
- time_t ph_timeout;
+ char *ph_servers; /* list of ph servers */
+ char *ph_field_list; /* list of fields to search for match */
+ PH *ph; /* PH server handle */
+ int ph_fastclose; /* send "quit" command on close */
+ time_t ph_timeout; /* timeout interval */
};
typedef struct ph_map_struct PH_MAP_STRUCT;
-# define DEFAULT_PH_MAP_FIELDS "alias callsign name spacedname"
#endif /* PH_MAP */
- /*
+/*
** Process List (proclist)
*/
-struct procs
-{
- pid_t proc_pid;
- char *proc_task;
- int proc_type;
-};
-
#define NO_PID ((pid_t) 0)
#ifndef PROC_LIST_SEG
# define PROC_LIST_SEG 32 /* number of pids to alloc at a time */
@@ -1049,22 +1320,22 @@ struct procs
#define PROC_CONTROL_CHILD 5
/* functions */
-extern void proc_list_add __P((pid_t, char *, int));
+extern void proc_list_add __P((pid_t, char *, int, int, int));
extern void proc_list_clear __P((void));
-extern void proc_list_display __P((FILE *));
-extern int proc_list_drop __P((pid_t));
+extern void proc_list_display __P((SM_FILE_T *, char *));
+extern void proc_list_drop __P((pid_t, int, int *));
extern void proc_list_probe __P((void));
extern void proc_list_set __P((pid_t, char *));
+extern void proc_list_signal __P((int, int));
- /*
+/*
** Symbol table definitions
*/
struct symtab
{
char *s_name; /* name to be entered */
- short s_type; /* general type (see below) */
- short s_len; /* length of this entry */
+ short s_symtype; /* general type (see below) */
struct symtab *s_next; /* pointer to next in chain */
union
{
@@ -1074,19 +1345,20 @@ struct symtab
char *sv_alias; /* alias */
MAPCLASS sv_mapclass; /* mapping function class */
MAP sv_map; /* mapping function */
- char *sv_hostsig; /* host signature */
+ HOSTSIG_T sv_hostsig; /* host signature */
MCI sv_mci; /* mailer connection info */
NAMECANON sv_namecanon; /* canonical name cache */
int sv_macro; /* macro name => id mapping */
int sv_ruleset; /* ruleset index */
struct hdrinfo sv_header; /* header metainfo */
char *sv_service[MAXMAPSTACK]; /* service switch */
-#ifdef LDAPMAP
+#if LDAPMAP
MAP *sv_lmap; /* Maps for LDAP connection */
#endif /* LDAPMAP */
-#if _FFR_MILTER
+#if MILTER
struct milter *sv_milter; /* milter filter name */
-#endif /* _FFR_MILTER */
+#endif /* MILTER */
+ QUEUEGRP *sv_queue; /* pointer to queue */
} s_value;
};
@@ -1106,12 +1378,15 @@ typedef struct symtab STAB;
#define ST_RULESET 10 /* ruleset index */
#define ST_SERVICE 11 /* service switch entry */
#define ST_HEADER 12 /* special header flags */
-#ifdef LDAPMAP
+#if LDAPMAP
# define ST_LMAP 13 /* List head of maps for LDAP connection */
#endif /* LDAPMAP */
-#if _FFR_MILTER
+#if MILTER
# define ST_MILTER 14 /* milter filter */
-#endif /* _FFR_MILTER */
+#endif /* MILTER */
+#define ST_QUEUE 15 /* a queue entry */
+
+/* This entry must be last */
#define ST_MCI 16 /* mailer connection info (offset) */
#define s_class s_value.sv_class
@@ -1127,12 +1402,13 @@ typedef struct symtab STAB;
#define s_ruleset s_value.sv_ruleset
#define s_service s_value.sv_service
#define s_header s_value.sv_header
-#ifdef LDAPMAP
+#if LDAPMAP
# define s_lmap s_value.sv_lmap
#endif /* LDAPMAP */
-#if _FFR_MILTER
+#if MILTER
# define s_milter s_value.sv_milter
-#endif /* _FFR_MILTER */
+#endif /* MILTER */
+#define s_quegrp s_value.sv_queue
/* opcodes to stab */
#define ST_FIND 0 /* find entry */
@@ -1142,34 +1418,7 @@ typedef struct symtab STAB;
extern STAB *stab __P((char *, int, int));
extern void stabapply __P((void (*)(STAB *, int), int));
- /*
-** STRUCT EVENT -- event queue.
-**
-** Maintained in sorted order.
-**
-** We store the pid of the process that set this event to insure
-** that when we fork we will not take events intended for the parent.
-*/
-
-struct event
-{
- time_t ev_time; /* time of the function call */
- void (*ev_func)__P((int));
- /* function to call */
- int ev_arg; /* argument to ev_func */
- pid_t ev_pid; /* pid that set this event */
- struct event *ev_link; /* link to next item */
-};
-
-typedef struct event EVENT;
-
-/* functions */
-extern void clrevent __P((EVENT *));
-extern void clear_events __P((void));
-extern EVENT *setevent __P((time_t, void(*)(), int));
-extern EVENT *sigsafe_setevent __P((time_t, void(*)(), int));
-
- /*
+/*
** Operation, send, error, and MIME modes
**
** The operation mode describes the basic operation of sendmail.
@@ -1177,7 +1426,7 @@ extern EVENT *sigsafe_setevent __P((time_t, void(*)(), int));
** default.
**
** The send mode tells how to send mail. It can be set in the
-** configuration file. It's setting determines how quickly the
+** configuration file. Its setting determines how quickly the
** mail will be delivered versus the load on your system. If the
** -v (verbose) flag is given, it will be forced to SM_DELIVER
** mode.
@@ -1194,11 +1443,14 @@ extern EVENT *sigsafe_setevent __P((time_t, void(*)(), int));
#define MD_TEST 't' /* test mode: resolve addrs only */
#define MD_INITALIAS 'i' /* initialize alias database */
#define MD_PRINT 'p' /* print the queue */
+#define MD_PRINTNQE 'P' /* print number of entries in queue */
#define MD_FREEZE 'z' /* freeze the configuration file */
#define MD_HOSTSTAT 'h' /* print persistent host stat info */
#define MD_PURGESTAT 'H' /* purge persistent host stat info */
#define MD_QUEUERUN 'q' /* queue run */
+/* Note: see also include/sendmail/pathnames.h: GET_CLIENT_CF */
+
/* values for e_sendmode -- send modes */
#define SM_DELIVER 'i' /* interactive delivery */
#define SM_FORK 'b' /* deliver in background */
@@ -1206,6 +1458,7 @@ extern EVENT *sigsafe_setevent __P((time_t, void(*)(), int));
#define SM_DEFER 'd' /* defer map lookups as well as queue */
#define SM_VERIFY 'v' /* verify only (used internally) */
+#define WILL_BE_QUEUED(m) ((m) == SM_QUEUE || (m) == SM_DEFER)
/* used only as a parameter to sendall */
#define SM_DEFAULT '\0' /* unspecified, use SendMode */
@@ -1246,30 +1499,33 @@ extern void set_delivery_mode __P((int, ENVELOPE *));
** These are bit values for the PrivacyFlags word.
*/
-#define PRIV_PUBLIC 0 /* what have I got to hide? */
-#define PRIV_NEEDMAILHELO 0x0001 /* insist on HELO for MAIL, at least */
-#define PRIV_NEEDEXPNHELO 0x0002 /* insist on HELO for EXPN */
-#define PRIV_NEEDVRFYHELO 0x0004 /* insist on HELO for VRFY */
-#define PRIV_NOEXPN 0x0008 /* disallow EXPN command entirely */
-#define PRIV_NOVRFY 0x0010 /* disallow VRFY command entirely */
-#define PRIV_AUTHWARNINGS 0x0020 /* flag possible authorization probs */
-#define PRIV_NORECEIPTS 0x0040 /* disallow return receipts */
-#define PRIV_NOVERB 0x0100 /* disallow VERB command entirely */
-#define PRIV_RESTRICTMAILQ 0x1000 /* restrict mailq command */
-#define PRIV_RESTRICTQRUN 0x2000 /* restrict queue run */
-#define PRIV_NOETRN 0x4000 /* disallow ETRN command entirely */
-#define PRIV_NOBODYRETN 0x8000 /* do not return bodies on bounces */
+#define PRIV_PUBLIC 0 /* what have I got to hide? */
+#define PRIV_NEEDMAILHELO 0x00000001 /* insist on HELO for MAIL */
+#define PRIV_NEEDEXPNHELO 0x00000002 /* insist on HELO for EXPN */
+#define PRIV_NEEDVRFYHELO 0x00000004 /* insist on HELO for VRFY */
+#define PRIV_NOEXPN 0x00000008 /* disallow EXPN command */
+#define PRIV_NOVRFY 0x00000010 /* disallow VRFY command */
+#define PRIV_AUTHWARNINGS 0x00000020 /* flag possible auth probs */
+#define PRIV_NOVERB 0x00000040 /* disallow VERB command */
+#define PRIV_RESTRICTMAILQ 0x00010000 /* restrict mailq command */
+#define PRIV_RESTRICTQRUN 0x00020000 /* restrict queue run */
+#define PRIV_RESTRICTEXPAND 0x00040000 /* restrict alias/forward expansion */
+#define PRIV_NOETRN 0x00080000 /* disallow ETRN command */
+#define PRIV_NOBODYRETN 0x00100000 /* do not return bodies on bounces */
+#define PRIV_NORECEIPTS 0x00200000 /* disallow return receipts */
/* don't give no info, anyway, anyhow */
-#define PRIV_GOAWAY (0x0fff & ~PRIV_NORECEIPTS)
+#define PRIV_GOAWAY 0x0000ffff
/* struct defining such things */
struct prival
{
- char *pv_name; /* name of privacy flag */
- u_short pv_flag; /* numeric level */
+ char *pv_name; /* name of privacy flag */
+ unsigned long pv_flag; /* numeric level */
};
+EXTERN unsigned long PrivacyFlags; /* privacy flags */
+
/*
** Flags passed to remotename, parseaddr, allocaddr, and buildaddr.
@@ -1344,35 +1600,32 @@ union bigsockaddr
extern char *anynet_ntoa __P((SOCKADDR *));
# if NETINET6
extern char *anynet_ntop __P((struct in6_addr *, char *, size_t));
+extern int anynet_pton __P((int, const char *, void *));
# endif /* NETINET6 */
extern char *hostnamebyanyaddr __P((SOCKADDR *));
-# if DAEMON
extern char *validate_connection __P((SOCKADDR *, char *, ENVELOPE *));
-# endif /* DAEMON */
#endif /* NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25 */
-#if _FFR_MILTER
- /*
+#if MILTER
+/*
** Mail Filters (milter)
*/
-#include <libmilter/milter.h>
-
-#define SMFTO_WRITE 0 /* Timeout for sending information */
-#define SMFTO_READ 1 /* Timeout waiting for a response */
-#define SMFTO_EOM 2 /* Timeout for ACK/NAK to EOM */
-#define SMFTO_CONNECT 3 /* Timeout for connect() */
+# define SMFTO_WRITE 0 /* Timeout for sending information */
+# define SMFTO_READ 1 /* Timeout waiting for a response */
+# define SMFTO_EOM 2 /* Timeout for ACK/NAK to EOM */
+# define SMFTO_CONNECT 3 /* Timeout for connect() */
-#define SMFTO_NUM_TO 4 /* Total number of timeouts */
+# define SMFTO_NUM_TO 4 /* Total number of timeouts */
struct milter
{
char *mf_name; /* filter name */
BITMAP256 mf_flags; /* MTA flags */
- u_long mf_fvers; /* filter version */
- u_long mf_fflags; /* filter flags */
- u_long mf_pflags; /* protocol flags */
+ unsigned long mf_fvers; /* filter version */
+ unsigned long mf_fflags; /* filter flags */
+ unsigned long mf_pflags; /* protocol flags */
char *mf_conn; /* connection info */
int mf_sock; /* connected socket */
char mf_state; /* state of filter */
@@ -1392,12 +1645,22 @@ struct milter
# define SMFS_ERROR 'E' /* error state */
# define SMFS_READY 'R' /* ready for action */
-/* 32-bit type used by milter */
-typedef SM_INT32 mi_int32;
-
EXTERN struct milter *InputFilters[MAXFILTERS];
EXTERN char *InputFilterList;
-#endif /* _FFR_MILTER */
+EXTERN int MilterLogLevel;
+
+# if _FFR_MILTER_PERDAEMON
+/* functions */
+extern void setup_daemon_milters __P(());
+# endif /* _FFR_MILTER_PERDAEMON */
+#endif /* MILTER */
+
+/*
+** 32-bit type used by milter
+** (needed by libmilter even if MILTER isn't defined)
+*/
+
+typedef SM_INT32 mi_int32;
/*
** Vendor codes
@@ -1419,6 +1682,7 @@ EXTERN char *InputFilterList;
#define VENDOR_HP 3 /* Hewlett-Packard specific config syntax */
#define VENDOR_IBM 4 /* IBM specific config syntax */
#define VENDOR_SENDMAIL 5 /* Sendmail, Inc. specific config syntax */
+#define VENDOR_DEC 6 /* Compaq, DEC, Digital */
/* prototypes for vendor-specific hook routines */
extern void vendor_daemon_setup __P((ENVELOPE *));
@@ -1437,60 +1701,36 @@ struct termescape
char *te_rv_off; /* turn reverse-video off */
};
- /*
+/*
** Additional definitions
*/
-/* d_flags, see daemon.c */
-/* general rule: lower case: required, upper case: No */
+/*
+** d_flags, see daemon.c
+** general rule: lower case: required, upper case: No
+*/
+
#define D_AUTHREQ 'a' /* authentication required */
#define D_BINDIF 'b' /* use if_addr for outgoing connection */
#define D_CANONREQ 'c' /* canonification required (cf) */
#define D_IFNHELO 'h' /* use if name for HELO */
#define D_FQMAIL 'f' /* fq sender address required (cf) */
-#if _FFR_TLS_CLT1
-#define D_CLTNOTLS 'S' /* don't use STARTTLS in client */
-#endif /* _FFR_TLS_CLT1 */
#define D_FQRCPT 'r' /* fq recipient address required (cf) */
+#if _FFR_SMTP_SSL
+# define D_SMTPS 's' /* SMTP over SSL (smtps) */
+#endif /* _FFR_SMTP_SSL */
#define D_UNQUALOK 'u' /* unqualified address is ok (cf) */
+#define D_NOAUTH 'A' /* no AUTH */
#define D_NOCANON 'C' /* no canonification (cf) */
#define D_NOETRN 'E' /* no ETRN (MSA) */
+#define D_NOTLS 'S' /* don't use STARTTLS */
#define D_ETRNONLY ((char)0x01) /* allow only ETRN (disk low) */
-
-/* Flags for submitmode */
-#define SUBMIT_UNKNOWN 0x0000 /* unknown agent type */
-#define SUBMIT_MTA 0x0001 /* act like a message transfer agent */
-#define SUBMIT_MSA 0x0002 /* act like a message submission agent */
-
-#if SASL
- /*
-** SASL
-*/
-
-/* authenticated? */
-# define SASL_NOT_AUTH 0 /* not authenticated */
-# define SASL_PROC_AUTH 1 /* in process of authenticating */
-# define SASL_IS_AUTH 2 /* authenticated */
-
-/* SASL options */
-# define SASL_AUTH_AUTH 0x1000 /* use auth= only if authenticated */
-# if _FFR_SASL_OPTS
-# define SASL_SEC_MASK 0x0fff /* mask for SASL_SEC_* values: sasl.h */
-# if (SASL_SEC_NOPLAINTEXT & SASL_SEC_MASK) == 0 || \
- (SASL_SEC_NOACTIVE & SASL_SEC_MASK) == 0 || \
- (SASL_SEC_NODICTIONARY & SASL_SEC_MASK) == 0 || \
- (SASL_SEC_FORWARD_SECRECY & SASL_SEC_MASK) == 0 || \
- (SASL_SEC_NOANONYMOUS & SASL_SEC_MASK) == 0 || \
- (SASL_SEC_PASS_CREDENTIALS & SASL_SEC_MASK) == 0
-ERROR: change SASL_SEC_MASK_ notify sendmail.org!
-# endif
-# endif /* _FFR_SASL_OPTS */
-
-# define MAXOUTLEN 1024 /* length of output buffer */
-#endif /* SASL */
+#define D_OPTIONAL 'O' /* optional socket */
+#define D_DISABLE ((char)0x02) /* optional socket disabled */
+#define D_ISSET ((char)0x03) /* this client struct is set */
#if STARTTLS
- /*
+/*
** TLS
*/
@@ -1517,65 +1757,178 @@ ERROR: change SASL_SEC_MASK_ notify sendmail.org!
#define TLS_I_DH512 0x00040000 /* generate 512bit DH param */
#define TLS_I_DH1024 0x00080000 /* generate 1024bit DH param */
#define TLS_I_DH2048 0x00100000 /* generate 2048bit DH param */
+#define TLS_I_NO_VRFY 0x00200000 /* do not require authentication */
+#define TLS_I_KEY_OUNR 0x00400000 /* KEY must be o unreadable */
+
+/* require server cert */
+#define TLS_I_SRV_CERT (TLS_I_CERT_EX | TLS_I_KEY_EX | \
+ TLS_I_KEY_UNR | TLS_I_KEY_OUNR | \
+ TLS_I_CERTP_EX | TLS_I_CERTF_EX | \
+ TLS_I_USE_KEY | TLS_I_USE_CERT)
/* server requirements */
-#define TLS_I_SRV (TLS_I_CERT_EX | TLS_I_KEY_EX | TLS_I_KEY_UNR | \
- TLS_I_CERTP_EX | TLS_I_CERTF_EX | TLS_I_RSA_TMP | \
- TLS_I_USE_KEY | TLS_I_USE_CERT | TLS_I_VRFY_PATH | \
- TLS_I_VRFY_LOC | TLS_I_TRY_DH | \
- TLS_I_DH512)
+#define TLS_I_SRV (TLS_I_SRV_CERT | TLS_I_RSA_TMP | TLS_I_VRFY_PATH | \
+ TLS_I_VRFY_LOC | TLS_I_TRY_DH | TLS_I_DH512)
/* client requirements */
-#define TLS_I_CLT (TLS_I_KEY_UNR)
+#define TLS_I_CLT (TLS_I_KEY_UNR | TLS_I_KEY_OUNR)
#define TLS_AUTH_OK 0
#define TLS_AUTH_NO 1
#define TLS_AUTH_FAIL (-1)
-#endif /* STARTTLS */
+
+/* functions */
+extern bool init_tls_library __P((void));
+extern bool inittls __P((SSL_CTX **, unsigned long, bool, char *, char *, char *, char *, char *));
+extern bool initclttls __P((bool));
+extern void setclttls __P((bool));
+extern bool initsrvtls __P((bool));
+extern int tls_get_info __P((SSL *, bool, char *, MACROS_T *, bool));
+extern int endtls __P((SSL *, char *));
+extern void tlslogerr __P((char *));
- /*
+EXTERN char *CACERTpath; /* path to CA certificates (dir. with hashes) */
+EXTERN char *CACERTfile; /* file with CA certificate */
+EXTERN char *CltCERTfile; /* file with client certificate */
+EXTERN char *Cltkeyfile; /* file with client private key */
+# if _FFR_TLS_1
+EXTERN char *CipherList; /* list of ciphers */
+EXTERN char *DHParams5; /* file with DH parameters (512) */
+# endif /* _FFR_TLS_1 */
+EXTERN char *DHParams; /* file with DH parameters */
+EXTERN char *RandFile; /* source of random data */
+EXTERN char *SrvCERTfile; /* file with server certificate */
+EXTERN char *Srvkeyfile; /* file with server private key */
+EXTERN unsigned long TLS_Srv_Opts; /* TLS server options */
+#endif /* STARTTLS */
+
+/*
** Queue related items
*/
+/* queue file names */
+#if _FFR_QUARANTINE
+# define ANYQFL_LETTER '?'
+# define QUARQF_LETTER 'h'
+#else /* _FFR_QUARANTINE */
+/* Before quarantining, ANYQF == NORMQF */
+# define ANYQFL_LETTER 'q'
+#endif /* _FFR_QUARANTINE */
+#define DATAFL_LETTER 'd'
+#define XSCRPT_LETTER 'x'
+#define NORMQF_LETTER 'q'
+#define NEWQFL_LETTER 't'
+
+# define TEMPQF_LETTER 'T'
+# define LOSEQF_LETTER 'Q'
+
/* queue sort order */
#define QSO_BYPRIORITY 0 /* sort by message priority */
#define QSO_BYHOST 1 /* sort by first host name */
#define QSO_BYTIME 2 /* sort by submission time */
#define QSO_BYFILENAME 3 /* sort by file name only */
+#define QSO_RANDOM 4 /* sort in random order */
+#define QSO_BYMODTIME 5 /* sort by modification time */
+#if _FFR_RHS
+# define QSO_BYSHUFFLE 6 /* sort by shuffled host name */
+#endif /* _FFR_RHS */
#if _FFR_QUEUEDELAY
-#define QD_LINEAR 0 /* linear (old) delay alg */
-#define QD_EXP 1 /* exponential delay alg */
+# define QD_LINEAR 0 /* linear (old) delay alg */
+# define QD_EXP 1 /* exponential delay alg */
#endif /* _FFR_QUEUEDELAY */
-#define NOQDIR (-1) /* no queue directory (yet) */
-
-#define NOW ((time_t) (-1)) /* queue return: now */
+#define NOQGRP (-1) /* no queue group (yet) */
+#define ENVQGRP (-2) /* use queue group of envelope */
+#define NOAQGRP (-3) /* no queue group in addr (yet) */
+#define ISVALIDQGRP(x) ((x) >= 0) /* valid queue group? */
+#define NOQDIR (-1) /* no queue directory (yet) */
+#define ENVQDIR (-2) /* use queue directory of envelope */
+#define NOAQDIR (-3) /* no queue directory in addr (yet) */
+#define ISVALIDQDIR(x) ((x) >= 0) /* valid queue directory? */
+#define RS_QUEUEGROUP "queuegroup" /* ruleset for queue group selection */
+
+#define NOW ((time_t) (-1)) /* queue return: now */
+
+/* SuperSafe values */
+#define SAFE_NO 0 /* no fsync(): don't use... */
+#define SAFE_INTERACTIVE 1 /* limit fsync() in -odi */
+#define SAFE_REALLY 2 /* always fsync() */
+
+#if _FFR_QUARANTINE
+/* QueueMode bits */
+# define QM_NORMAL ' '
+# define QM_QUARANTINE 'Q'
+# define QM_LOST 'L'
+#endif /* _FFR_QUARANTINE */
/* Queue Run Limitations */
struct queue_char
{
- char *queue_match; /* string to match */
- struct queue_char *queue_next;
+ char *queue_match; /* string to match */
+ bool queue_negate; /* or not match, if set */
+ struct queue_char *queue_next;
};
-typedef struct queue_char QUEUE_CHAR;
+typedef struct queue_char QUEUE_CHAR;
+
+EXTERN int volatile CurRunners; /* current number of runner children */
+EXTERN int MaxQueueRun; /* maximum number of jobs in one queue run */
+EXTERN int MaxQueueChildren; /* max # of forked queue children */
+EXTERN int MaxRunnersPerQueue; /* max # proc's active in queue group */
+EXTERN int NiceQueueRun; /* nice queue runs to this value */
+EXTERN int NumQueue; /* number of queue groups */
+EXTERN int QueueFileMode; /* mode on files in mail queue */
+#if _FFR_QUARANTINE
+EXTERN int QueueMode; /* which queue items to act upon */
+#endif /* _FFR_QUARANTINE */
+EXTERN int QueueSortOrder; /* queue sorting order algorithm */
+EXTERN time_t MinQueueAge; /* min delivery interval */
+EXTERN time_t QueueIntvl; /* intervals between running the queue */
+EXTERN char *QueueDir; /* location of queue directory */
+#if _FFR_QUEUEDELAY
+EXTERN int QueueAlg; /* algorithm for queue delays */
+EXTERN time_t QueueInitDelay; /* initial queue delay */
+EXTERN time_t QueueMaxDelay; /* maximum queue delay */
+#endif /* _FFR_QUEUEDELAY */
+EXTERN QUEUE_CHAR *QueueLimitId; /* limit queue run to id */
+#if _FFR_QUARANTINE
+EXTERN QUEUE_CHAR *QueueLimitQuarantine; /* limit queue run to quarantine reason */
+#endif /* _FFR_QUARANTINE */
+EXTERN QUEUE_CHAR *QueueLimitRecipient; /* limit queue run to rcpt */
+EXTERN QUEUE_CHAR *QueueLimitSender; /* limit queue run to sender */
+EXTERN QUEUEGRP *Queue[MAXQUEUEGROUPS + 1]; /* queue groups */
/* functions */
extern void assign_queueid __P((ENVELOPE *));
-extern ADDRESS *copyqueue __P((ADDRESS *));
+extern ADDRESS *copyqueue __P((ADDRESS *, SM_RPOOL_T *));
+extern void cleanup_queues __P((void));
+extern bool doqueuerun __P((void));
extern void initsys __P((ENVELOPE *));
extern void loseqfile __P((ENVELOPE *, char *));
-extern void multiqueue_cache __P((void));
+extern int name2qid __P((char *));
extern char *qid_printname __P((ENVELOPE *));
-extern char *qid_printqueue __P((int));
+extern char *qid_printqueue __P((int, int));
+#if _FFR_QUARANTINE
+extern void quarantine_queue __P((char *, int));
+#endif /* _FFR_QUARANTINE */
extern char *queuename __P((ENVELOPE *, int));
-extern void queueup __P((ENVELOPE *, bool));
-extern bool runqueue __P((bool, bool));
-extern void setnewqueue __P((ENVELOPE *));
+extern void queueup __P((ENVELOPE *, bool, bool));
+extern bool runqueue __P((bool, bool, bool, bool));
+extern int run_work_group __P((int, bool, bool, bool, bool));
+extern void set_def_queueval __P((QUEUEGRP *, bool));
+extern void setup_queues __P((bool));
+extern bool setnewqueue __P((ENVELOPE *));
extern bool shouldqueue __P((long, time_t));
extern void sync_queue_time __P((void));
+extern int print_single_queue __P((int, int));
+#if REQUIRES_DIR_FSYNC
+# define SYNC_DIR(path, panic) sync_dir(path, panic)
+extern void sync_dir __P((char *, bool));
+#else /* REQUIRES_DIR_FSYNC */
+# define SYNC_DIR(path, panic) ((void) 0)
+#endif /* REQUIRES_DIR_FSYNC */
/*
** Timeouts
@@ -1596,6 +1949,7 @@ EXTERN struct
/* following timeouts are not mentioned in RFC 1123 */
time_t to_iconnect; /* initial connection timeout (first try) */
time_t to_connect; /* initial connection timeout (later tries) */
+ time_t to_aconnect; /* all connections timeout (MX and A records) */
time_t to_rset; /* RSET command */
time_t to_helo; /* HELO command */
time_t to_quit; /* QUIT command */
@@ -1603,6 +1957,13 @@ EXTERN struct
time_t to_ident; /* IDENT protocol requests */
time_t to_fileopen; /* opening :include: and .forward files */
time_t to_control; /* process a control socket command */
+ time_t to_lhlo; /* LMTP: LHLO command */
+#if SASL
+ time_t to_auth; /* AUTH dialogue [10m] */
+#endif /* SASL */
+#if STARTTLS
+ time_t to_starttls; /* STARTTLS dialogue [10m] */
+#endif /* STARTTLS */
/* following are per message */
time_t to_q_return[MAXTOCLASS]; /* queue return timeouts */
time_t to_q_warning[MAXTOCLASS]; /* queue warning timeouts */
@@ -1624,62 +1985,25 @@ EXTERN struct
extern void inittimeouts __P((char *, bool));
/*
-** Trace information
+** Interface probing
*/
-/* macros for debugging flags */
-#define tTd(flag, level) (tTdvect[flag] >= (u_char)level)
-#define tTdlevel(flag) (tTdvect[flag])
+#define DPI_PROBENONE 0 /* Don't probe any interfaces */
+#define DPI_PROBEALL 1 /* Probe all interfaces */
+#define DPI_SKIPLOOPBACK 2 /* Don't probe loopback interfaces */
-/* variables */
-extern u_char tTdvect[100]; /* trace vector */
- /*
-** Critical signal sections
+/*
+** Trace information
*/
-#define PEND_SIGHUP 0x0001
-#define PEND_SIGINT 0x0002
-#define PEND_SIGTERM 0x0004
-#define PEND_SIGUSR1 0x0008
-
-#define ENTER_CRITICAL() InCriticalSection++
-
-#define LEAVE_CRITICAL() \
-do \
-{ \
- if (InCriticalSection > 0) \
- InCriticalSection--; \
-} while (0)
-
-#define CHECK_CRITICAL(sig) \
-do \
-{ \
- if (InCriticalSection > 0 && (sig) != 0) \
- { \
- pend_signal((sig)); \
- return SIGFUNC_RETURN; \
- } \
-} while (0)
-
-/* reset signal in case System V semantics */
-#ifdef SYS5SIGNALS
-# define FIX_SYSV_SIGNAL(sig, handler) \
-{ \
- if ((sig) != 0) \
- (void) setsignal((sig), (handler)); \
-}
-#else /* SYS5SIGNALS */
-# define FIX_SYSV_SIGNAL(sig, handler) { /* EMPTY */ }
-#endif /* SYS5SIGNALS */
+/* macros for debugging flags */
+#define tTd(flag, level) (tTdvect[flag] >= (unsigned char)level)
+#define tTdlevel(flag) (tTdvect[flag])
/* variables */
-EXTERN u_int volatile InCriticalSection; /* >0 if in a critical section */
-EXTERN int volatile PendingSignal; /* pending signal to resend */
-
-/* functions */
-extern void pend_signal __P((int));
+extern unsigned char tTdvect[100]; /* trace vector */
- /*
+/*
** Miscellaneous information.
*/
@@ -1689,6 +2013,10 @@ extern void pend_signal __P((int));
#define NOQID "*~*"
+/* use id or NOQID (to avoid NOQUEUE in logfile) */
+#define E_ID(id) ((id) == NULL ? NOQID : (id))
+
+#define CURHOSTNAME (CurHostName == NULL ? "local" : CurHostName)
/*
** Some in-line functions
@@ -1704,25 +2032,59 @@ extern void pend_signal __P((int));
#define newstr(s) strcpy(xalloc(strlen(s) + 1), s)
#define STRUCTCOPY(s, d) d = s
- /*
+
+/* free a pointer if it isn't NULL and set it to NULL */
+#define SM_FREE_CLR(p) \
+ if ((p) != NULL) \
+ { \
+ sm_free(p); \
+ (p) = NULL; \
+ } \
+ else
+
+/*
+** Update a permanent string variable with a new value.
+** The old value is freed, the new value is strdup'ed.
+**
+** We use sm_pstrdup_x to duplicate the string because it raises
+** an exception on error, and because it allocates "permanent storage"
+** which is not expected to be freed before process exit.
+** The latter is important for memory leak analysis.
+**
+** If an exception occurs while strdup'ing the new value,
+** then the variable remains set to the old value.
+** That's why the strdup must occur before we free the old value.
+**
+** The macro uses a do loop so that this idiom will work:
+** if (...)
+** PSTRSET(var, val1);
+** else
+** PSTRSET(var, val2);
+*/
+#define PSTRSET(var, val) \
+ do \
+ { \
+ char *_newval = sm_pstrdup_x(val); \
+ if (var != NULL) \
+ sm_free(var); \
+ var = _newval; \
+ } while (0)
+
+/*
** Global variables.
*/
EXTERN bool AllowBogusHELO; /* allow syntax errors on HELO command */
-#if !_FFR_REMOVE_AUTOREBUILD
-EXTERN bool AutoRebuild; /* auto-rebuild the alias database as needed */
-#endif /* !_FFR_REMOVE_AUTOREBUILD */
EXTERN bool CheckAliases; /* parse addresses during newaliases */
-EXTERN bool ChownAlwaysSafe; /* treat chown(2) as safe */
EXTERN bool ColonOkInAddr; /* single colon legal in address */
+#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
EXTERN bool ConfigFileRead; /* configuration file has been read */
+#endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */
EXTERN bool volatile DataProgress; /* have we sent anything since last check */
-EXTERN bool DisConnected; /* running with OutChannel redirected to xf */
-EXTERN bool volatile DoQueueRun; /* non-interrupt time queue run needed */
+EXTERN bool DisConnected; /* running with OutChannel redirect to transcript file */
EXTERN bool DontExpandCnames; /* do not $[...$] expand CNAMEs */
EXTERN bool DontInitGroups; /* avoid initgroups() because of NIS cost */
EXTERN bool DontLockReadFiles; /* don't read lock support files */
-EXTERN bool DontProbeInterfaces; /* don't probe interfaces for names */
EXTERN bool DontPruneRoutes; /* don't prune source routes */
EXTERN bool ForkQueueRuns; /* fork for each job when running the queue */
EXTERN bool FromFlag; /* if set, "From" person is explicit */
@@ -1732,42 +2094,46 @@ EXTERN bool HasWildcardMX; /* don't use MX records when canonifying */
EXTERN bool HoldErrs; /* only output errors to transcript */
EXTERN bool IgnoreHostStatus; /* ignore long term host status files */
EXTERN bool IgnrDot; /* don't let dot end messages */
-EXTERN bool InChild; /* true if running in an SMTP subprocess */
EXTERN bool LogUsrErrs; /* syslog user errors (e.g., SMTP RCPT cmd) */
-EXTERN bool MapOpenErr; /* error opening a non-optional map */
EXTERN bool MatchGecos; /* look for user names in gecos field */
EXTERN bool MeToo; /* send to the sender also */
EXTERN bool NoAlias; /* suppress aliasing */
EXTERN bool NoConnect; /* don't connect to non-local mailers */
EXTERN bool OnlyOneError; /* .... or only want to give one SMTP reply */
EXTERN bool QuickAbort; /* .... but only if we want a quick abort */
+EXTERN bool ResNoAliases; /* don't use $HOSTALIASES */
+EXTERN bool volatile RestartWorkGroup; /* daemon needs to restart some work groups */
EXTERN bool RrtImpliesDsn; /* turn Return-Receipt-To: into DSN */
EXTERN bool SaveFrom; /* save leading "From" lines */
EXTERN bool SendMIMEErrors; /* send error messages in MIME format */
EXTERN bool SevenBitInput; /* force 7-bit data on input */
EXTERN bool SingleLineFromHeader; /* force From: header to be one line */
EXTERN bool SingleThreadDelivery; /* single thread hosts on delivery */
+#if _FFR_SOFT_BOUNCE
+EXTERN bool SoftBounce; /* replace 5xy by 4xy (for testing) */
+#endif /* _FFR_SOFT_BOUNCE */
EXTERN bool volatile StopRequest; /* stop sending output */
-EXTERN bool SuperSafe; /* be extra careful, even if expensive */
EXTERN bool SuprErrs; /* set if we are suppressing errors */
EXTERN bool TryNullMXList; /* if we are the best MX, try host directly */
-#if _FFR_WORKAROUND_BROKEN_NAMESERVERS
+EXTERN bool UseMSP; /* mail submission: group writable queue ok? */
EXTERN bool WorkAroundBrokenAAAA; /* some nameservers return SERVFAIL on AAAA queries */
-#endif /* _FFR_WORKAROUND_BROKEN_NAMESERVERS */
EXTERN bool UseErrorsTo; /* use Errors-To: header (back compat) */
-EXTERN bool UseHesiod; /* using Hesiod -- interpret Hesiod errors */
EXTERN bool UseNameServer; /* using DNS -- interpret h_errno & MX RRs */
EXTERN char InetMode; /* default network for daemon mode */
EXTERN char OpMode; /* operation mode, see below */
EXTERN char SpaceSub; /* substitution for <lwsp> */
+EXTERN int BadRcptThrottle; /* Throttle rejected RCPTs per SMTP message */
EXTERN int CheckpointInterval; /* queue file checkpoint interval */
EXTERN int ConfigLevel; /* config file level */
EXTERN int ConnRateThrottle; /* throttle for SMTP connection rate */
EXTERN int volatile CurChildren; /* current number of daemonic children */
EXTERN int CurrentLA; /* current load average */
EXTERN int DefaultNotify; /* default DSN notification flags */
+EXTERN int DelayLA; /* load average to delay connections */
+EXTERN int DontProbeInterfaces; /* don't probe interfaces for names */
EXTERN int Errors; /* set if errors (local to single pass) */
EXTERN int ExitStat; /* exit status code */
+EXTERN int FastSplit; /* fast initial splitting of envelopes */
EXTERN int FileMode; /* mode on files */
EXTERN int LineNumber; /* line number in current input */
EXTERN int LogLevel; /* level of logging to perform */
@@ -1777,43 +2143,46 @@ EXTERN int MaxForwardEntries; /* maximum number of forward entries */
EXTERN int MaxHeadersLength; /* max length of headers */
EXTERN int MaxHopCount; /* max # of hops until bounce */
EXTERN int MaxMacroRecursion; /* maximum depth of macro recursion */
-EXTERN int MaxMciCache; /* maximum entries in MCI cache */
EXTERN int MaxMimeFieldLength; /* maximum MIME field length */
EXTERN int MaxMimeHeaderLength; /* maximum MIME header length */
-
-EXTERN int MaxQueueRun; /* maximum number of jobs in one queue run */
EXTERN int MaxRcptPerMsg; /* max recipients per SMTP message */
EXTERN int MaxRuleRecursion; /* maximum depth of ruleset recursion */
EXTERN int MimeMode; /* MIME processing mode */
EXTERN int NoRecipientAction;
-EXTERN int NumPriorities; /* pointer into Priorities */
-EXTERN u_short PrivacyFlags; /* privacy flags */
-#if _FFR_QUEUE_FILE_MODE
-EXTERN int QueueFileMode; /* mode on qf/tf/df files */
-#endif /* _FFR_QUEUE_FILE_MODE */
+
+#if SM_CONF_SHM
+EXTERN int Numfilesys; /* number of queue file systems */
+EXTERN int *PNumFileSys;
+# define NumFileSys (*PNumFileSys)
+# else /* SM_CONF_SHM */
+EXTERN int NumFileSys; /* number of queue file systems */
+# endif /* SM_CONF_SHM */
+
EXTERN int QueueLA; /* load average starting forced queueing */
-EXTERN int QueueSortOrder; /* queue sorting order algorithm */
-EXTERN int RefuseLA; /* load average refusing connections are */
+EXTERN int RefuseLA; /* load average refusing connections */
+EXTERN int SuperSafe; /* be extra careful, even if expensive */
EXTERN int VendorCode; /* vendor-specific operation enhancements */
EXTERN int Verbose; /* set if blow-by-blow desired */
EXTERN gid_t DefGid; /* default gid to run as */
EXTERN gid_t RealGid; /* real gid of caller */
EXTERN gid_t RunAsGid; /* GID to become for bulk of run */
+EXTERN gid_t EffGid; /* effective gid */
+#if SM_CONF_SHM
+EXTERN key_t ShmKey; /* shared memory key */
+#endif /* SM_CONF_SHM */
+EXTERN pid_t CurrentPid; /* current process id */
+EXTERN pid_t DaemonPid; /* process id of daemon */
EXTERN uid_t DefUid; /* default uid to run as */
EXTERN uid_t RealUid; /* real uid of caller */
EXTERN uid_t RunAsUid; /* UID to become for bulk of run */
EXTERN uid_t TrustedUid; /* uid of trusted user for files and startup */
-EXTERN size_t DataFileBufferSize; /* size of buffer for in-core df */
-EXTERN size_t XscriptFileBufferSize; /* size of buffer for in-core xf */
+EXTERN size_t DataFileBufferSize; /* size of buf for in-core data file */
+EXTERN time_t DeliverByMin; /* deliver by minimum time */
EXTERN time_t DialDelay; /* delay between dial-on-demand tries */
-EXTERN time_t MciCacheTimeout; /* maximum idle time on connections */
-EXTERN time_t MciInfoTimeout; /* how long 'til we retry down hosts */
-EXTERN time_t MinQueueAge; /* min delivery interval */
-EXTERN time_t QueueIntvl; /* intervals between running the queue */
EXTERN time_t SafeAlias; /* interval to wait until @:@ in alias file */
EXTERN time_t ServiceCacheMaxAge; /* refresh interval for cache */
-EXTERN time_t ServiceCacheTime; /* time service switch was cached */
+EXTERN size_t XscriptFileBufferSize; /* size of buf for in-core transcript file */
EXTERN MODE_T OldUmask; /* umask when sendmail starts up */
EXTERN long MaxMessageSize; /* advertised max size we will accept */
EXTERN long MinBlocksFree; /* min # of blocks free on queue fs */
@@ -1821,26 +2190,6 @@ EXTERN long QueueFactor; /* slope of queue function */
EXTERN long WkClassFact; /* multiplier for message class -> priority */
EXTERN long WkRecipFact; /* multiplier for # of recipients -> priority */
EXTERN long WkTimeFact; /* priority offset each time this job is run */
-#if SASL
-EXTERN char *AuthMechanisms; /* AUTH mechanisms */
-EXTERN char *SASLInfo; /* file with AUTH info */
-#endif /* SASL */
-EXTERN int SASLOpts; /* options for SASL */
-#if STARTTLS
-EXTERN char *CACERTpath; /* path to CA certificates (dir. with hashes) */
-EXTERN char *CACERTfile; /* file with CA certificate */
-EXTERN char *SrvCERTfile; /* file with server certificate */
-EXTERN char *Srvkeyfile; /* file with server private key */
-EXTERN char *CltCERTfile; /* file with client certificate */
-EXTERN char *Cltkeyfile; /* file with client private key */
-EXTERN char *DHParams; /* file with DH parameters */
-EXTERN char *RandFile; /* source of random data */
-# if _FFR_TLS_1
-EXTERN char *DHParams5; /* file with DH parameters (512) */
-EXTERN char *CipherList; /* list of ciphers */
-# endif /* _FFR_TLS_1 */
-#endif /* STARTTLS */
-EXTERN char *ConfFile; /* location of configuration file [conf.c] */
EXTERN char *ControlSocketName; /* control socket filename [control.c] */
EXTERN char *CurHostName; /* current host we are dealing with */
EXTERN char *DeadLetterDrop; /* path to dead letter office */
@@ -1854,18 +2203,13 @@ EXTERN char *ForwardPath; /* path to search for .forward files */
EXTERN char *HelpFile; /* location of SMTP help file */
EXTERN char *HostStatDir; /* location of host status information */
EXTERN char *HostsFile; /* path to /etc/hosts file */
+extern char *Mbdb; /* mailbox database type */
EXTERN char *MustQuoteChars; /* quote these characters in phrases */
EXTERN char *MyHostName; /* name of this host for SMTP messages */
EXTERN char *OperatorChars; /* operators (old $o macro) */
EXTERN char *PidFile; /* location of proc id file [conf.c] */
EXTERN char *PostMasterCopy; /* address to get errs cc's */
EXTERN char *ProcTitlePrefix; /* process title prefix */
-EXTERN char *QueueDir; /* location of queue directory */
-#if _FFR_QUEUEDELAY
-EXTERN int QueueAlg; /* algorithm for queue delays */
-EXTERN time_t QueueInitDelay; /* initial queue delay */
-EXTERN time_t QueueMaxDelay; /* maximum queue delay */
-#endif /* _FFR_QUEUEDELAY */
EXTERN char *RealHostName; /* name of host we are talking to */
EXTERN char *RealUserName; /* real user name of caller */
EXTERN char *volatile RestartRequest;/* a sendmail restart has been requested */
@@ -1880,70 +2224,30 @@ EXTERN char *StatFile; /* location of statistics summary */
EXTERN char *TimeZoneSpec; /* override time zone specification */
EXTERN char *UdbSpec; /* user database source spec */
EXTERN char *UnixFromLine; /* UNIX From_ line (old $l macro) */
-EXTERN char **ExternalEnviron; /* input environment */
- /* saved user environment */
+EXTERN char **ExternalEnviron; /* saved user (input) environment */
EXTERN char **SaveArgv; /* argument vector for re-execing */
EXTERN BITMAP256 DontBlameSendmail; /* DontBlameSendmail bits */
-#if SFIO
-EXTERN Sfio_t *InChannel; /* input connection */
-EXTERN Sfio_t *OutChannel; /* output connection */
-#else /* SFIO */
-EXTERN FILE *InChannel; /* input connection */
-EXTERN FILE *OutChannel; /* output connection */
-#endif /* SFIO */
-EXTERN FILE *TrafficLogFile; /* file in which to log all traffic */
-#ifdef HESIOD
+EXTERN SM_FILE_T *InChannel; /* input connection */
+EXTERN SM_FILE_T *OutChannel; /* output connection */
+EXTERN SM_FILE_T *TrafficLogFile; /* file in which to log all traffic */
+#if HESIOD
EXTERN void *HesiodContext;
#endif /* HESIOD */
EXTERN ENVELOPE *CurEnv; /* envelope currently being processed */
-EXTERN MAILER *LocalMailer; /* ptr to local mailer */
-EXTERN MAILER *ProgMailer; /* ptr to program mailer */
-EXTERN MAILER *FileMailer; /* ptr to *file* mailer */
-EXTERN MAILER *InclMailer; /* ptr to *include* mailer */
-EXTERN QUEUE_CHAR *QueueLimitRecipient; /* limit queue run to rcpt */
-EXTERN QUEUE_CHAR *QueueLimitSender; /* limit queue run to sender */
-EXTERN QUEUE_CHAR *QueueLimitId; /* limit queue run to id */
-EXTERN MAILER *Mailer[MAXMAILERS + 1];
-EXTERN struct rewrite *RewriteRules[MAXRWSETS];
EXTERN char *RuleSetNames[MAXRWSETS]; /* ruleset number to name */
EXTERN char *UserEnviron[MAXUSERENVIRON + 1];
-EXTERN struct priority Priorities[MAXPRIORITIES];
+EXTERN struct rewrite *RewriteRules[MAXRWSETS];
EXTERN struct termescape TermEscape; /* terminal escape codes */
EXTERN SOCKADDR ConnectOnlyTo; /* override connection address (for testing) */
EXTERN SOCKADDR RealHostAddr; /* address of host we are talking to */
-EXTERN jmp_buf TopFrame; /* branch-to-top-of-loop-on-error frame */
-EXTERN TIMERS Timers;
+extern const SM_EXC_TYPE_T EtypeQuickAbort; /* type of a QuickAbort exception */
+
+
/*
** Declarations of useful functions
*/
-#if SASL
-extern char *intersect __P((char *, char *));
-extern char *iteminlist __P((char *, char *, char *));
-extern int proxy_policy __P((void *, const char *, const char *, const char **, const char **));
-# if SASL > 10515
-extern int safesaslfile __P((void *, char *, int));
-# else /* SASL > 10515 */
-extern int safesaslfile __P((void *, char *));
-# endif /* SASL > 10515 */
-extern int sasl_decode64 __P((const char *, unsigned, char *, unsigned *));
-extern int sasl_encode64 __P((const char *, unsigned, char *, unsigned, unsigned *));
-#endif /* SASL */
-
-#if STARTTLS
-extern void apps_ssl_info_cb __P((SSL *, int , int));
-extern bool init_tls_library __P((void));
-extern bool inittls __P((SSL_CTX **, u_long, bool, char *, char *, char *, char *, char *));
-extern bool initclttls __P((void));
-extern bool initsrvtls __P((void));
-extern int tls_get_info __P((SSL *, ENVELOPE *, bool, char *, bool));
-extern int endtls __P((SSL *, char *));
-extern int endtlsclt __P((MCI *));
-extern void tlslogerr __P((void));
-extern bool tls_rand_init __P((char *, int));
-#endif /* STARTTLS */
-
/* Transcript file */
extern void closexscript __P((ENVELOPE *));
extern void openxscript __P((ENVELOPE *));
@@ -1951,11 +2255,11 @@ extern void openxscript __P((ENVELOPE *));
/* error related */
extern void buffer_errors __P((void));
extern void flush_errors __P((bool));
-extern void message __P((const char *, ...));
-extern void nmessage __P((const char *, ...));
-extern void syserr __P((const char *, ...));
-extern void usrerrenh __P((char *, const char *, ...));
-extern void usrerr __P((const char *, ...));
+extern void PRINTFLIKE(1, 2) message __P((const char *, ...));
+extern void PRINTFLIKE(1, 2) nmessage __P((const char *, ...));
+extern void PRINTFLIKE(1, 2) syserr __P((const char *, ...));
+extern void PRINTFLIKE(2, 3) usrerrenh __P((char *, const char *, ...));
+extern void PRINTFLIKE(1, 2) usrerr __P((const char *, ...));
extern int isenhsc __P((const char *, int));
extern int extenhsc __P((const char *, int, char *));
@@ -1963,23 +2267,23 @@ extern int extenhsc __P((const char *, int, char *));
extern void alias __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
extern bool aliaswait __P((MAP *, char *, bool));
extern void forward __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
-extern void readaliases __P((MAP *, FILE *, bool, bool));
+extern void readaliases __P((MAP *, SM_FILE_T *, bool, bool));
extern bool rebuildaliases __P((MAP *, bool));
extern void setalias __P((char *));
/* logging */
extern void logdelivery __P((MAILER *, MCI *, char *, const char *, ADDRESS *, time_t, ENVELOPE *));
extern void logsender __P((ENVELOPE *, char *));
-extern void sm_syslog __P((int, const char *, const char *, ...));
+extern void PRINTFLIKE(3, 4) sm_syslog __P((int, const char *, const char *, ...));
/* SMTP */
-extern void giveresponse __P((int, char *, MAILER *, MCI *, ADDRESS *, time_t, ENVELOPE *));
+extern void giveresponse __P((int, char *, MAILER *, MCI *, ADDRESS *, time_t, ENVELOPE *, ADDRESS *));
extern int reply __P((MAILER *, MCI *, ENVELOPE *, time_t, void (*)(), char **));
extern void smtp __P((char *volatile, BITMAP256, ENVELOPE *volatile));
#if SASL
extern int smtpauth __P((MAILER *, MCI *, ENVELOPE *));
#endif /* SASL */
-extern int smtpdata __P((MAILER *, MCI *, ENVELOPE *));
+extern int smtpdata __P((MAILER *, MCI *, ENVELOPE *, ADDRESS *, time_t));
extern int smtpgetstat __P((MAILER *, MCI *, ENVELOPE *));
extern int smtpmailfrom __P((MAILER *, MCI *, ENVELOPE *));
extern void smtpmessage __P((char *, MAILER *, MCI *, ...));
@@ -1987,7 +2291,7 @@ extern void smtpinit __P((MAILER *, MCI *, ENVELOPE *, bool));
extern char *smtptodsn __P((int));
extern int smtpprobe __P((MCI *));
extern void smtpquit __P((MAILER *, MCI *, ENVELOPE *));
-extern int smtprcpt __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *));
+extern int smtprcpt __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *, ADDRESS *, time_t));
extern void smtprset __P((MAILER *, MCI *, ENVELOPE *));
#define ISSMTPCODE(c) (isascii(c[0]) && isdigit(c[0]) && \
@@ -1997,13 +2301,20 @@ extern void smtprset __P((MAILER *, MCI *, ENVELOPE *));
(c[3] == ' ' || c[3] == '-' || c[3] == '\0'))
/* delivery */
-extern pid_t dowork __P((int, char *, bool, bool, ENVELOPE *));
+extern pid_t dowork __P((int, int, char *, bool, bool, ENVELOPE *));
+extern pid_t doworklist __P((ENVELOPE *, bool, bool));
extern int endmailer __P((MCI *, ENVELOPE *, char **));
extern int mailfile __P((char *volatile, MAILER *volatile, ADDRESS *, volatile long, ENVELOPE *));
extern void sendall __P((ENVELOPE *, int));
/* stats */
-extern void markstats __P((ENVELOPE *, ADDRESS *, bool));
+#define STATS_NORMAL 'n'
+#if _FFR_QUARANTINE
+# define STATS_QUARANTINE 'q'
+#endif /* _FFR_QUARANTINE */
+#define STATS_REJECT 'r'
+
+extern void markstats __P((ENVELOPE *, ADDRESS *, int));
extern void clearstats __P((void));
extern void poststats __P((char *));
@@ -2013,13 +2324,13 @@ extern void clrcontrol __P((void));
extern void control_command __P((int, ENVELOPE *));
extern int opencontrolsocket __P((void));
-#if _FFR_MILTER
+#if MILTER
/* milter functions */
-extern void milter_parse_list __P((char *, struct milter **, int));
+extern void milter_config __P((char *, struct milter **, int));
extern void milter_setup __P((char *));
extern void milter_set_option __P((char *, char *, bool));
extern bool milter_can_delrcpts __P((void));
-extern void milter_init __P((ENVELOPE *, char *));
+extern bool milter_init __P((ENVELOPE *, char *));
extern void milter_quit __P((ENVELOPE *));
extern void milter_abort __P((ENVELOPE *));
extern char *milter_connect __P((char *, SOCKADDR, ENVELOPE *, char *));
@@ -2027,10 +2338,9 @@ extern char *milter_helo __P((char *, ENVELOPE *, char *));
extern char *milter_envfrom __P((char **, ENVELOPE *, char *));
extern char *milter_envrcpt __P((char **, ENVELOPE *, char *));
extern char *milter_data __P((ENVELOPE *, char *));
-#endif /* _FFR_MILTER */
+#endif /* MILTER */
-extern char *addquotes __P((char *));
-extern void allsignals __P((bool));
+extern char *addquotes __P((char *, SM_RPOOL_T *));
extern char *arpadate __P((char *));
extern bool atobool __P((char *));
extern int atooct __P((char *));
@@ -2038,7 +2348,10 @@ extern void auth_warning __P((ENVELOPE *, const char *, ...));
extern int blocksignal __P((int));
extern bool bitintersect __P((BITMAP256, BITMAP256));
extern bool bitzerop __P((BITMAP256));
+extern int check_bodytype __P((char *));
extern void buildfname __P((char *, char *, char *, int));
+extern bool chkclientmodifiers __P((int));
+extern bool chkdaemonmodifiers __P((int));
extern int checkcompat __P((ADDRESS *, ENVELOPE *));
#ifdef XDEBUG
extern void checkfd012 __P((char *));
@@ -2047,40 +2360,45 @@ extern void checkfdopen __P((int, char *));
extern void checkfds __P((char *));
extern bool chownsafe __P((int, bool));
extern void cleanstrcpy __P((char *, char *, int));
+#if SM_CONF_SHM
+extern void cleanup_shm __P((bool));
+#endif /* SM_CONF_SHM */
extern void clrdaemon __P((void));
-extern void collect __P((FILE *, bool, HDR **, ENVELOPE *));
+extern void collect __P((SM_FILE_T *, bool, HDR **, ENVELOPE *));
extern time_t convtime __P((char *, int));
-extern char **copyplist __P((char **, bool));
+extern char **copyplist __P((char **, bool, SM_RPOOL_T *));
extern void copy_class __P((int, int));
extern time_t curtime __P((void));
extern char *defcharset __P((ENVELOPE *));
extern char *denlstring __P((char *, bool, bool));
extern void disconnect __P((int, ENVELOPE *));
-extern bool dns_getcanonname __P((char *, int, bool, int *));
+#if _FFR_CONTROL_MSTAT
+extern void disk_status __P((SM_FILE_T *, char *));
+#endif /* _FFR_CONTROL_MSTAT */
+extern bool dns_getcanonname __P((char *, int, bool, int *, int *));
extern pid_t dofork __P((void));
extern int drop_privileges __P((bool));
extern int dsntoexitstat __P((char *));
extern void dumpfd __P((int, bool, bool));
extern void dumpstate __P((char *));
-extern bool enoughdiskspace __P((long, bool));
+extern bool enoughdiskspace __P((long, ENVELOPE *));
extern char *exitstat __P((char *));
-extern char *fgetfolded __P((char *, int, FILE *));
+extern void fatal_error __P((SM_EXC_T *));
+extern char *fgetfolded __P((char *, int, SM_FILE_T *));
extern void fill_fd __P((int, char *));
extern char *find_character __P((char *, int));
-extern struct passwd *finduser __P((char *, bool *));
-extern void finis __P((bool, volatile int));
+extern int finduser __P((char *, bool *, SM_MBDB_T *));
+extern void finis __P((bool, bool, volatile int));
extern void fixcrlf __P((char *, bool));
extern long freediskspace __P((char *, long *));
#if NETINET6 && NEEDSGETIPNODE
-# if _FFR_FREEHOSTENT
extern void freehostent __P((struct hostent *));
-# endif /* _FFR_FREEHOSTENT */
-#endif /* NEEDSGETIPNODE && NETINET6 */
+#endif /* NETINET6 && NEEDSGETIPNODE */
extern char *get_column __P((char *, int, int, char *, int));
extern char *getauthinfo __P((int, bool *));
-extern char *getcfname __P((void));
-extern char *getextenv __P((const char *));
extern int getdtsize __P((void));
+extern int getla __P((void));
+extern char *getmodifiers __P((char *, BITMAP256));
extern BITMAP256 *getrequests __P((ENVELOPE *));
extern char *getvendor __P((int));
extern void help __P((char *, ENVELOPE *));
@@ -2095,31 +2413,37 @@ extern bool isloopback __P((SOCKADDR sa));
extern void load_if_names __P((void));
extern bool lockfile __P((int, char *, char *, int));
extern void log_sendmail_pid __P((ENVELOPE *));
+extern void logundelrcpts __P((ENVELOPE *, char *, int, bool));
extern char lower __P((int));
extern void makelower __P((char *));
extern int makeconnection_ds __P((char *, MCI *));
-extern int makeconnection __P((char *, volatile u_int, MCI *, ENVELOPE *));
+extern int makeconnection __P((char *, volatile unsigned int, MCI *, ENVELOPE *, time_t));
+extern void makeworkgroups __P((void));
+extern void mark_work_group_restart __P((int, int));
extern char * munchstring __P((char *, char **, int));
extern struct hostent *myhostname __P((char *, int));
extern char *nisplus_default_domain __P((void)); /* extern for Sun */
extern bool path_is_dir __P((char *, bool));
+extern int pickqdir __P((QUEUEGRP *qg, long fsize, ENVELOPE *e));
extern char *pintvl __P((time_t, bool));
extern void printav __P((char **));
extern void printmailer __P((MAILER *));
+extern void printnqe __P((SM_FILE_T *, char *));
extern void printopenfds __P((bool));
extern void printqueue __P((void));
extern void printrules __P((void));
extern pid_t prog_open __P((char **, int *, ENVELOPE *));
extern void putline __P((char *, MCI *));
extern void putxline __P((char *, size_t, MCI *, int));
-extern void queueup_macros __P((int, FILE *, ENVELOPE *));
+extern void queueup_macros __P((int, SM_FILE_T *, ENVELOPE *));
extern void readcf __P((char *, bool, ENVELOPE *));
extern SIGFUNC_DECL reapchild __P((int));
extern int releasesignal __P((int));
extern void resetlimits __P((void));
+extern void restart_daemon __P((void));
+extern void restart_marked_work_groups __P(());
extern bool rfc822_string __P((char *));
-extern FILE *safefopen __P((char *, int, int, long));
-extern void savemail __P((ENVELOPE *, bool));
+extern bool savemail __P((ENVELOPE *, bool));
extern void seed_random __P((void));
extern void sendtoargv __P((char **, ENVELOPE *));
extern void setclientoptions __P((char *));
@@ -2127,49 +2451,66 @@ extern bool setdaemonoptions __P((char *));
extern void setdefaults __P((ENVELOPE *));
extern void setdefuser __P((void));
extern bool setvendor __P((char *));
+extern void set_op_mode __P((int));
extern void setoption __P((int, char *, bool, bool, ENVELOPE *));
extern sigfunc_t setsignal __P((int, sigfunc_t));
extern void setuserenv __P((const char *, const char *));
extern void settime __P((ENVELOPE *));
-extern char *sfgets __P((char *, int, FILE *, time_t, char *));
-extern char *shortenstring __P((const char *, int));
+extern char *sfgets __P((char *, int, SM_FILE_T *, time_t, char *));
+extern char *shortenstring __P((const char *, size_t));
extern char *shorten_hostname __P((char []));
extern bool shorten_rfc822_string __P((char *, size_t));
extern void shutdown_daemon __P((void));
-extern void sm_dopr __P((char *, const char *, va_list));
-extern void sm_free __P((void *));
extern struct hostent *sm_gethostbyname __P((char *, int));
extern struct hostent *sm_gethostbyaddr __P((char *, int, int));
-extern int sm_getla __P((ENVELOPE *));
+extern void sm_getla __P((void));
extern struct passwd *sm_getpwnam __P((char *));
extern struct passwd *sm_getpwuid __P((UID_T));
extern void sm_setproctitle __P((bool, ENVELOPE *, const char *, ...));
-extern SIGFUNC_DECL sm_signal_noop __P((int));
-extern int sm_strcasecmp __P((const char *, const char *));
+extern pid_t sm_wait __P((int *));
+extern bool split_by_recipient __P((ENVELOPE *e));
extern void stop_sendmail __P((void));
-extern bool strcontainedin __P((char *, char *));
-extern void stripquotes __P((char *));
+extern char *str2prt __P((char *));
+extern bool strcontainedin __P((bool, char *, char *));
extern int switch_map_find __P((char *, char *[], short []));
extern bool transienterror __P((int));
+#if _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI
+extern void truncate_at_delim __P((char *, size_t, int));
+#endif /* _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI */
extern void tTflag __P((char *));
-extern void tTsetup __P((u_char *, int, char *));
+extern void tTsetup __P((unsigned char *, unsigned int, char *));
+extern SIGFUNC_DECL tick __P((int));
extern char *ttypath __P((void));
extern void unlockqueue __P((ENVELOPE *));
#if !HASUNSETENV
extern void unsetenv __P((char *));
#endif /* !HASUNSETENV */
+
+/* update file system information: +/- some blocks */
+#if SM_CONF_SHM
+extern void upd_qs __P((ENVELOPE *, bool, bool));
+# define updfs(e, delete, avail) upd_qs(e, delete, avail)
+#else /* SM_CONF_SHM */
+# define updfs(e, delete, avail)
+#endif /* SM_CONF_SHM */
+
extern char *username __P((void));
extern bool usershellok __P((char *, char *));
extern void vendor_post_defaults __P((ENVELOPE *));
extern void vendor_pre_defaults __P((ENVELOPE *));
extern int waitfor __P((pid_t));
extern bool writable __P((char *, ADDRESS *, long));
-extern char *xalloc __P((int));
-extern char *xcalloc __P((size_t, size_t));
-extern char *xrealloc __P((void *, size_t));
+#if SM_HEAP_CHECK
+# define xalloc(size) xalloc_tagged(size, __FILE__, __LINE__)
+extern char *xalloc_tagged __P((int, char*, int));
+#else /* SM_HEAP_CHECK */
+extern char *xalloc __P((int));
+#endif /* SM_HEAP_CHECK */
extern void xputs __P((const char *));
extern char *xtextify __P((char *, char *));
extern bool xtextok __P((char *));
-extern void xunlink __P((char *));
+extern int xunlink __P((char *));
extern char *xuntextify __P((char *));
-#endif /* _SENDMAIL_H */
+
+
+#endif /* ! _SENDMAIL_H */
diff --git a/contrib/sendmail/src/sfsasl.c b/contrib/sendmail/src/sfsasl.c
index 8ac3428..c6f63fb 100644
--- a/contrib/sendmail/src/sfsasl.c
+++ b/contrib/sendmail/src/sfsasl.c
@@ -8,40 +8,179 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: sfsasl.c,v 8.17.4.15 2001/07/11 17:37:07 gshapiro Exp $";
-#endif /* ! lint */
-
-#if SFIO
-# include <sfio/stdio.h>
-#endif /* SFIO */
-
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: sfsasl.c,v 8.86 2001/09/11 04:05:16 gshapiro Exp $")
#include <stdlib.h>
#include <sendmail.h>
+#include <errno.h>
+#if SASL
+# include <sasl.h>
+# include "sfsasl.h"
+
+/* Structure used by the "sasl" file type */
+struct sasl_obj
+{
+ SM_FILE_T *fp;
+ sasl_conn_t *conn;
+};
+
+struct sasl_info
+{
+ SM_FILE_T *fp;
+ sasl_conn_t *conn;
+};
-#if SASL && SFIO
/*
-** SASL
+** SASL_GETINFO - returns requested information about a "sasl" file
+** descriptor.
+**
+** Parameters:
+** fp -- the file descriptor
+** what -- the type of information requested
+** valp -- the thang to return the information in
+**
+** Returns:
+** -1 for unknown requests
+** >=0 on success with valp filled in (if possible).
*/
-# include <sasl.h>
-# include "sfsasl.h"
+static int sasl_getinfo __P((SM_FILE_T *, int, void *));
+
+static int
+sasl_getinfo(fp, what, valp)
+ SM_FILE_T *fp;
+ int what;
+ void *valp;
+{
+ struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
+
+ switch (what)
+ {
+ case SM_IO_WHAT_FD:
+ if (so->fp == NULL)
+ return -1;
+ return so->fp->f_file; /* for stdio fileno() compatability */
+
+ case SM_IO_IS_READABLE:
+ if (so->fp == NULL)
+ return 0;
+
+ /* get info from underlying file */
+ return sm_io_getinfo(so->fp, what, valp);
+
+ default:
+ return -1;
+ }
+}
+
+/*
+** SASL_OPEN -- creates the sasl specific information for opening a
+** file of the sasl type.
+**
+** Parameters:
+** fp -- the file pointer associated with the new open
+** info -- contains the sasl connection information pointer and
+** the original SM_FILE_T that holds the open
+** flags -- ignored
+** rpool -- ignored
+**
+** Returns:
+** 0 on success
+*/
+
+static int sasl_open __P((SM_FILE_T *, const void *, int, const void *));
+
+/* ARGSUSED2 */
+static int
+sasl_open(fp, info, flags, rpool)
+ SM_FILE_T *fp;
+ const void *info;
+ int flags;
+ const void *rpool;
+{
+ struct sasl_obj *so;
+ struct sasl_info *si = (struct sasl_info *) info;
+
+ so = (struct sasl_obj *) sm_malloc(sizeof(struct sasl_obj));
+ so->fp = si->fp;
+ so->conn = si->conn;
+
+ /*
+ ** The underlying 'fp' is set to SM_IO_NOW so that the entire
+ ** encoded string is written in one chunk. Otherwise there is
+ ** the possibility that it may appear illegal, bogus or
+ ** mangled to the other side of the connection.
+ ** We will read or write through 'fp' since it is the opaque
+ ** connection for the communications. We need to treat it this
+ ** way in case the encoded string is to be sent down a TLS
+ ** connection rather than, say, sm_io's stdio.
+ */
+
+ (void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
+ fp->f_cookie = so;
+ return 0;
+}
+
+/*
+** SASL_CLOSE -- close the sasl specific parts of the sasl file pointer
+**
+** Parameters:
+** fp -- the file pointer to close
+**
+** Returns:
+** 0 on success
+*/
+
+static int sasl_close __P((SM_FILE_T *));
+
+static int
+sasl_close(fp)
+ SM_FILE_T *fp;
+{
+ struct sasl_obj *so;
+
+ so = (struct sasl_obj *) fp->f_cookie;
+ if (so->fp != NULL)
+ {
+ sm_io_close(so->fp, SM_TIME_DEFAULT);
+ so->fp = NULL;
+ }
+ sm_free(so);
+ so = NULL;
+ return 0;
+}
/* how to deallocate a buffer allocated by SASL */
-# define SASL_DEALLOC(b) sm_free(b)
+extern void sm_sasl_free __P((void *));
+# define SASL_DEALLOC(b) sm_sasl_free(b)
+
+/*
+** SASL_READ -- read encrypted information and decrypt it for the caller
+**
+** Parameters:
+** fp -- the file pointer
+** buf -- the location to place the decrypted information
+** size -- the number of bytes to read after decryption
+**
+** Results:
+** -1 on error
+** otherwise the number of bytes read
+*/
+
+static ssize_t sasl_read __P((SM_FILE_T *, char *, size_t));
static ssize_t
-sasl_read(f, buf, size, disc)
- Sfio_t *f;
- Void_t *buf;
+sasl_read(fp, buf, size)
+ SM_FILE_T *fp;
+ char *buf;
size_t size;
- Sfdisc_t *disc;
{
- int len, result;
+ int result;
+ ssize_t len;
static char *outbuf = NULL;
static unsigned int outlen = 0;
static unsigned int offset = 0;
- Sasldisc_t *sd = (Sasldisc_t *) disc;
+ struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
/*
** sasl_decode() may require more data than a single read() returns.
@@ -54,10 +193,11 @@ sasl_read(f, buf, size, disc)
while (outbuf == NULL && outlen == 0)
{
- len = sfrd(f, buf, size, disc);
+ len = sm_io_read(so->fp, SM_TIME_DEFAULT, buf, size);
if (len <= 0)
return len;
- result = sasl_decode(sd->conn, buf, len, &outbuf, &outlen);
+ result = sasl_decode(so->conn, buf,
+ (unsigned int) len, &outbuf, &outlen);
if (result != SASL_OK)
{
outbuf = NULL;
@@ -67,66 +207,108 @@ sasl_read(f, buf, size, disc)
}
}
- if (outbuf != NULL)
+ if (outbuf == NULL)
{
- if (outlen - offset > size)
- {
- /* return another part of the buffer */
- (void) memcpy(buf, outbuf + offset, (size_t) size);
- offset += size;
- result = size;
- }
- else
- {
- /* return the rest of the buffer */
- result = outlen - offset;
- (void) memcpy(buf, outbuf + offset, (size_t) result);
- SASL_DEALLOC(outbuf);
- outbuf = NULL;
- offset = 0;
- outlen = 0;
- }
+ /* be paranoid: outbuf == NULL but outlen != 0 */
+ syserr("@sasl_read failure: outbuf == NULL but outlen != 0");
+ /* NOTREACHED */
+ }
+ if (outlen - offset > size)
+ {
+ /* return another part of the buffer */
+ (void) memcpy(buf, outbuf + offset, size);
+ offset += size;
+ len = size;
}
else
{
- /* be paranoid: outbuf == NULL but outlen != 0 */
- syserr("!sasl_read failure: outbuf == NULL but outlen != 0");
+ /* return the rest of the buffer */
+ len = outlen - offset;
+ (void) memcpy(buf, outbuf + offset, (size_t) len);
+ SASL_DEALLOC(outbuf);
+ outbuf = NULL;
+ offset = 0;
+ outlen = 0;
}
- return result;
+ return len;
}
+/*
+** SASL_WRITE -- write information out after encrypting it
+**
+** Parameters:
+** fp -- the file pointer
+** buf -- holds the data to be encrypted and written
+** size -- the number of bytes to have encrypted and written
+**
+** Returns:
+** -1 on error
+** otherwise number of bytes written
+*/
+
+static ssize_t sasl_write __P((SM_FILE_T *, const char *, size_t));
+
static ssize_t
-sasl_write(f, buf, size, disc)
- Sfio_t *f;
- const Void_t *buf;
+sasl_write(fp, buf, size)
+ SM_FILE_T *fp;
+ const char *buf;
size_t size;
- Sfdisc_t *disc;
{
int result;
char *outbuf;
unsigned int outlen;
- Sasldisc_t *sd = (Sasldisc_t *) disc;
+ size_t ret = 0, total = 0;
+ struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
- result = sasl_encode(sd->conn, buf, size, &outbuf, &outlen);
+ result = sasl_encode(so->conn, buf,
+ (unsigned int) size, &outbuf, &outlen);
if (result != SASL_OK)
return -1;
if (outbuf != NULL)
{
- sfwr(f, outbuf, outlen, disc);
+ while (outlen > 0)
+ {
+ ret = sm_io_write(so->fp, SM_TIME_DEFAULT,
+ &outbuf[total], outlen);
+ outlen -= ret;
+ total += ret;
+ }
SASL_DEALLOC(outbuf);
}
return size;
}
+/*
+** SFDCSASL -- create sasl file type and open in and out file pointers
+** for sendmail to read from and write to.
+**
+** Parameters:
+** fin -- the sm_io file encrypted data to be read from
+** fout -- the sm_io file encrypted data to be writen to
+** conn -- the sasl connection pointer
+**
+** Returns:
+** -1 on error
+** 0 on success
+**
+** Side effects:
+** The arguments "fin" and "fout" are replaced with the new
+** SM_FILE_T pointers.
+*/
+
int
sfdcsasl(fin, fout, conn)
- Sfio_t *fin;
- Sfio_t *fout;
+ SM_FILE_T **fin;
+ SM_FILE_T **fout;
sasl_conn_t *conn;
{
- Sasldisc_t *saslin, *saslout;
+ SM_FILE_T *newin, *newout;
+ SM_FILE_T SM_IO_SET_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
+ sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
+ SM_TIME_FOREVER);
+ struct sasl_info info;
if (conn == NULL)
{
@@ -134,265 +316,392 @@ sfdcsasl(fin, fout, conn)
return 0;
}
- saslin = (Sasldisc_t *) xalloc(sizeof(Sasldisc_t));
- saslout = (Sasldisc_t *) xalloc(sizeof(Sasldisc_t));
- saslin->disc.readf = sasl_read;
- saslin->disc.writef = sasl_write;
- saslin->disc.seekf = NULL;
- saslin->disc.exceptf = NULL;
+ SM_IO_INIT_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
+ sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
+ SM_TIME_FOREVER);
+ info.fp = *fin;
+ info.conn = conn;
+ newin = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info, SM_IO_RDONLY,
+ NULL);
- saslout->disc.readf = sasl_read;
- saslout->disc.writef = sasl_write;
- saslout->disc.seekf = NULL;
- saslout->disc.exceptf = NULL;
+ if (newin == NULL)
+ return -1;
- saslin->conn = conn;
- saslout->conn = conn;
+ info.fp = *fout;
+ info.conn = conn;
+ newout = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info, SM_IO_WRONLY,
+ NULL);
- if (sfdisc(fin, (Sfdisc_t *) saslin) != (Sfdisc_t *) saslin ||
- sfdisc(fout, (Sfdisc_t *) saslout) != (Sfdisc_t *) saslout)
+ if (newout == NULL)
{
- sm_free(saslin);
- sm_free(saslout);
+ (void) sm_io_close(newin, SM_TIME_DEFAULT);
return -1;
}
+ sm_io_automode(newin, newout);
+
+ *fin = newin;
+ *fout = newout;
return 0;
}
-#endif /* SASL && SFIO */
+#endif /* SASL */
+
+#if STARTTLS
+# include "sfsasl.h"
+# include <openssl/err.h>
+
+/* Structure used by the "tls" file type */
+struct tls_obj
+{
+ SM_FILE_T *fp;
+ SSL *con;
+};
+
+struct tls_info
+{
+ SM_FILE_T *fp;
+ SSL *con;
+};
-#if STARTTLS && (SFIO || _FFR_TLS_TOREK)
/*
-** STARTTLS
+** TLS_GETINFO - returns requested information about a "tls" file
+** descriptor.
+**
+** Parameters:
+** fp -- the file descriptor
+** what -- the type of information requested
+** valp -- the thang to return the information in (unused)
+**
+** Returns:
+** -1 for unknown requests
+** >=0 on success with valp filled in (if possible).
*/
-# include "sfsasl.h"
-# include <openssl/err.h>
+static int tls_getinfo __P((SM_FILE_T *, int, void *));
-# if SFIO
-static ssize_t
-tls_read(f, buf, size, disc)
- Sfio_t *f;
- Void_t *buf;
- size_t size;
- Sfdisc_t *disc;
-# else /* SFIO */
+/* ARGSUSED2 */
static int
-tls_read(disc, buf, size)
- void *disc;
- char *buf;
- int size;
-# endif /* SFIO */
+tls_getinfo(fp, what, valp)
+ SM_FILE_T *fp;
+ int what;
+ void *valp;
{
- int r;
- Tlsdisc_t *sd;
+ struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
- /* Cast back to correct type */
- sd = (Tlsdisc_t *) disc;
-
- r = SSL_read(sd->con, (char *) buf, size);
- if (r < 0 && LogLevel > 7)
+ switch (what)
{
- char *err;
+ case SM_IO_WHAT_FD:
+ if (so->fp == NULL)
+ return -1;
+ return so->fp->f_file; /* for stdio fileno() compatability */
+
+ case SM_IO_IS_READABLE:
+ return SSL_pending(so->con) > 0;
+
+ default:
+ return -1;
+ }
+}
- err = NULL;
- switch (SSL_get_error(sd->con, r))
- {
- case SSL_ERROR_NONE:
- break;
- case SSL_ERROR_WANT_WRITE:
- err = "write W BLOCK";
- break;
- case SSL_ERROR_WANT_READ:
- err = "write R BLOCK";
- break;
- case SSL_ERROR_WANT_X509_LOOKUP:
- err = "write X BLOCK";
- break;
- case SSL_ERROR_ZERO_RETURN:
- break;
- case SSL_ERROR_SYSCALL:
- err = "syscall error";
/*
- get_last_socket_error());
+** TLS_OPEN -- creates the tls specific information for opening a
+** file of the tls type.
+**
+** Parameters:
+** fp -- the file pointer associated with the new open
+** info -- the sm_io file pointer holding the open and the
+** TLS encryption connection to be read from or written to
+** flags -- ignored
+** rpool -- ignored
+**
+** Returns:
+** 0 on success
*/
- break;
- case SSL_ERROR_SSL:
- err = "generic SSL error";
- break;
- }
- if (err != NULL)
- sm_syslog(LOG_WARNING, NOQID, "TLS: read error: %s",
- err);
+
+static int tls_open __P((SM_FILE_T *, const void *, int, const void *));
+
+/* ARGSUSED2 */
+static int
+tls_open(fp, info, flags, rpool)
+ SM_FILE_T *fp;
+ const void *info;
+ int flags;
+ const void *rpool;
+{
+ struct tls_obj *so;
+ struct tls_info *ti = (struct tls_info *) info;
+
+ so = (struct tls_obj *) sm_malloc(sizeof(struct tls_obj));
+ so->fp = ti->fp;
+ so->con = ti->con;
+
+ /*
+ ** We try to get the "raw" file descriptor that TLS uses to
+ ** do the actual read/write with. This is to allow us control
+ ** over the file descriptor being a blocking or non-blocking type.
+ ** Under the covers TLS handles the change and this allows us
+ ** to do timeouts with sm_io.
+ */
+
+ fp->f_file = sm_io_getinfo(so->fp, SM_IO_WHAT_FD, NULL);
+ (void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
+ fp->f_cookie = so;
+ return 0;
+}
+
+/*
+** TLS_CLOSE -- close the tls specific parts of the tls file pointer
+**
+** Parameters:
+** fp -- the file pointer to close
+**
+** Returns:
+** 0 on success
+*/
+
+static int tls_close __P((SM_FILE_T *));
+
+static int
+tls_close(fp)
+ SM_FILE_T *fp;
+{
+ struct tls_obj *so;
+
+ so = (struct tls_obj *) fp->f_cookie;
+ if (so->fp != NULL)
+ {
+ sm_io_close(so->fp, SM_TIME_DEFAULT);
+ so->fp = NULL;
}
- return r;
+ sm_free(so);
+ so = NULL;
+ return 0;
}
-# if SFIO
+/* maximum number of retries for TLS related I/O due to handshakes */
+# define MAX_TLS_IOS 4
+
+/*
+** TLS_READ -- read secured information for the caller
+**
+** Parameters:
+** fp -- the file pointer
+** buf -- the location to place the data
+** size -- the number of bytes to read from connection
+**
+** Results:
+** -1 on error
+** otherwise the number of bytes read
+*/
+
+static ssize_t tls_read __P((SM_FILE_T *, char *, size_t));
+
static ssize_t
-tls_write(f, buf, size, disc)
- Sfio_t *f;
- const Void_t *buf;
+tls_read(fp, buf, size)
+ SM_FILE_T *fp;
+ char *buf;
size_t size;
- Sfdisc_t *disc;
-# else /* SFIO */
-static int
-tls_write(disc, buf, size)
- void *disc;
- const char *buf;
- int size;
-# endif /* SFIO */
{
int r;
- Tlsdisc_t *sd;
+ static int again = MAX_TLS_IOS;
+ struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
+ char *err;
- /* Cast back to correct type */
- sd = (Tlsdisc_t *) disc;
+ r = SSL_read(so->con, (char *) buf, size);
- r = SSL_write(sd->con, (char *)buf, size);
- if (r < 0 && LogLevel > 7)
+ if (r > 0)
{
- char *err;
+ again = MAX_TLS_IOS;
+ return r;
+ }
- err = NULL;
- switch (SSL_get_error(sd->con, r))
- {
- case SSL_ERROR_NONE:
- break;
- case SSL_ERROR_WANT_WRITE:
- err = "write W BLOCK";
- break;
- case SSL_ERROR_WANT_READ:
- err = "write R BLOCK";
- break;
- case SSL_ERROR_WANT_X509_LOOKUP:
- err = "write X BLOCK";
- break;
- case SSL_ERROR_ZERO_RETURN:
- break;
- case SSL_ERROR_SYSCALL:
- err = "syscall error";
-/*
- get_last_socket_error());
-*/
- break;
- case SSL_ERROR_SSL:
- err = "generic SSL error";
+ err = NULL;
+ switch (SSL_get_error(so->con, r))
+ {
+ case SSL_ERROR_NONE:
+ case SSL_ERROR_ZERO_RETURN:
+ again = MAX_TLS_IOS;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ if (--again <= 0)
+ err = "read W BLOCK";
+ else
+ errno = EAGAIN;
+ break;
+ case SSL_ERROR_WANT_READ:
+ if (--again <= 0)
+ err = "read R BLOCK";
+ else
+ errno = EAGAIN;
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ err = "write X BLOCK";
+ break;
+ case SSL_ERROR_SYSCALL:
+ if (r == 0 && errno == 0) /* out of protocol EOF found */
+ break;
+ err = "syscall error";
/*
- ERR_GET_REASON(ERR_peek_error()));
+ get_last_socket_error());
*/
- break;
- }
- if (err != NULL)
- sm_syslog(LOG_WARNING, NOQID, "TLS: write error: %s",
- err);
+ break;
+ case SSL_ERROR_SSL:
+ err = "generic SSL error";
+ if (LogLevel > 9)
+ tlslogerr("read");
+ break;
+ }
+ if (err != NULL)
+ {
+ again = MAX_TLS_IOS;
+ if (errno == 0)
+ errno = EIO;
+ if (LogLevel > 7)
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS: read error=%s (%d)", err, r);
}
return r;
}
-# if !SFIO
-static int
-tls_close(cookie)
- void *cookie;
+/*
+** TLS_WRITE -- write information out through secure connection
+**
+** Parameters:
+** fp -- the file pointer
+** buf -- holds the data to be securely written
+** size -- the number of bytes to write
+**
+** Returns:
+** -1 on error
+** otherwise number of bytes written
+*/
+
+static ssize_t tls_write __P((SM_FILE_T *, const char *, size_t));
+
+static ssize_t
+tls_write(fp, buf, size)
+ SM_FILE_T *fp;
+ const char *buf;
+ size_t size;
{
- int retval = 0;
- Tlsdisc_t *tc;
+ int r;
+ static int again = MAX_TLS_IOS;
+ struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
+ char *err;
- /* Cast back to correct type */
- tc = (Tlsdisc_t *)cookie;
+ r = SSL_write(so->con, (char *) buf, size);
- if (tc->fp != NULL)
+ if (r > 0)
{
- retval = fclose(tc->fp);
- tc->fp = NULL;
+ again = MAX_TLS_IOS;
+ return r;
}
-
- sm_free(tc);
- return retval;
+ err = NULL;
+ switch (SSL_get_error(so->con, r))
+ {
+ case SSL_ERROR_NONE:
+ case SSL_ERROR_ZERO_RETURN:
+ again = MAX_TLS_IOS;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ if (--again <= 0)
+ err = "write W BLOCK";
+ else
+ errno = EAGAIN;
+ break;
+ case SSL_ERROR_WANT_READ:
+ if (--again <= 0)
+ err = "write R BLOCK";
+ else
+ errno = EAGAIN;
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ err = "write X BLOCK";
+ break;
+ case SSL_ERROR_SYSCALL:
+ if (r == 0 && errno == 0) /* out of protocol EOF found */
+ break;
+ err = "syscall error";
+/*
+ get_last_socket_error());
+*/
+ break;
+ case SSL_ERROR_SSL:
+ err = "generic SSL error";
+/*
+ ERR_GET_REASON(ERR_peek_error()));
+*/
+ if (LogLevel > 9)
+ tlslogerr("write");
+ break;
+ }
+ if (err != NULL)
+ {
+ again = MAX_TLS_IOS;
+ if (errno == 0)
+ errno = EIO;
+ if (LogLevel > 7)
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS: write error=%s (%d)", err, r);
+ }
+ return r;
}
-# endif /* !SFIO */
+
+/*
+** SFDCTLS -- create tls file type and open in and out file pointers
+** for sendmail to read from and write to.
+**
+** Parameters:
+** fin -- data input source being replaced
+** fout -- data output source being replaced
+** conn -- the tls connection pointer
+**
+** Returns:
+** -1 on error
+** 0 on success
+**
+** Side effects:
+** The arguments "fin" and "fout" are replaced with the new
+** SM_FILE_T pointers.
+** The original "fin" and "fout" are preserved in the tls file
+** type but are not actually used because of the design of TLS.
+*/
int
sfdctls(fin, fout, con)
-# if SFIO
- Sfio_t *fin;
- Sfio_t *fout;
-# else /* SFIO */
- FILE **fin;
- FILE **fout;
-# endif /* SFIO */
+ SM_FILE_T **fin;
+ SM_FILE_T **fout;
SSL *con;
{
- Tlsdisc_t *tlsin, *tlsout;
-# if !SFIO
- FILE *fp;
-# else /* !SFIO */
- int rfd, wfd;
-# endif /* !SFIO */
-
- if (con == NULL)
- return 0;
-
- tlsin = (Tlsdisc_t *) xalloc(sizeof(Tlsdisc_t));
- tlsout = (Tlsdisc_t *) xalloc(sizeof(Tlsdisc_t));
-# if SFIO
- tlsin->disc.readf = tls_read;
- tlsin->disc.writef = tls_write;
- tlsin->disc.seekf = NULL;
- tlsin->disc.exceptf = NULL;
- tlsin->con = con;
-
- tlsout->disc.readf = tls_read;
- tlsout->disc.writef = tls_write;
- tlsout->disc.seekf = NULL;
- tlsout->disc.exceptf = NULL;
- tlsout->con = con;
-
- rfd = fileno(fin);
- wfd = fileno(fout);
- if (rfd < 0 || wfd < 0 ||
- SSL_set_rfd(con, rfd) <= 0 || SSL_set_wfd(con, wfd) <= 0)
- {
- sm_free(tlsin);
- sm_free(tlsout);
- return -1;
- }
- if (sfdisc(fin, (Sfdisc_t *) tlsin) != (Sfdisc_t *) tlsin ||
- sfdisc(fout, (Sfdisc_t *) tlsout) != (Sfdisc_t *) tlsout)
- {
- sm_free(tlsin);
- sm_free(tlsout);
+ SM_FILE_T *tlsin, *tlsout;
+ SM_FILE_T SM_IO_SET_TYPE(tls_vector, "tls", tls_open, tls_close,
+ tls_read, tls_write, NULL, tls_getinfo, NULL,
+ SM_TIME_FOREVER);
+ struct tls_info info;
+
+ SM_ASSERT(con != NULL);
+
+ SM_IO_INIT_TYPE(tls_vector, "tls", tls_open, tls_close,
+ tls_read, tls_write, NULL, tls_getinfo, NULL,
+ SM_TIME_FOREVER);
+ info.fp = *fin;
+ info.con = con;
+ tlsin = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_RDONLY,
+ NULL);
+ if (tlsin == NULL)
return -1;
- }
-# else /* SFIO */
- tlsin->fp = *fin;
- tlsin->con = con;
- fp = funopen(tlsin, tls_read, tls_write, NULL, tls_close);
- if (fp == NULL)
- {
- sm_free(tlsin);
- return -1;
- }
- *fin = fp;
- tlsout->fp = *fout;
- tlsout->con = con;
- fp = funopen(tlsout, tls_read, tls_write, NULL, tls_close);
- if (fp == NULL)
+ info.fp = *fout;
+ tlsout = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_WRONLY,
+ NULL);
+ if (tlsout == NULL)
{
- FILE *save;
-
- /* Hack: Don't close underlying fp */
- save = tlsin->fp;
- tlsin->fp = NULL;
- fclose(*fin);
- *fin = save;
- sm_free(tlsout);
+ (void) sm_io_close(tlsin, SM_TIME_DEFAULT);
return -1;
}
- *fout = fp;
- SSL_set_rfd(con, fileno(tlsin->fp));
- SSL_set_wfd(con, fileno(tlsout->fp));
-# endif /* SFIO */
+ sm_io_automode(tlsin, tlsout);
+
+ *fin = tlsin;
+ *fout = tlsout;
return 0;
}
-#endif /* STARTTLS && (SFIO || _FFR_TLS_TOREK) */
+#endif /* STARTTLS */
diff --git a/contrib/sendmail/src/sfsasl.h b/contrib/sendmail/src/sfsasl.h
index b276e27..c75418a 100644
--- a/contrib/sendmail/src/sfsasl.h
+++ b/contrib/sendmail/src/sfsasl.h
@@ -6,55 +6,18 @@
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*
- * $Id: sfsasl.h,v 8.13.4.4 2000/07/18 18:44:51 gshapiro Exp $"
+ * $Id: sfsasl.h,v 8.17 2000/09/19 21:30:49 ca Exp $"
*/
#ifndef SFSASL_H
# define SFSASL_H
-# if SFIO
-# include <sfio.h>
-# endif /* SFIO */
-
-# if SASL
-# if SFIO
-
-/* sf discipline to add sasl */
-typedef struct _sasldisc
-{
- Sfdisc_t disc;
- sasl_conn_t *conn;
-} Sasldisc_t;
-
-extern int sfdcsasl __P((Sfio_t *, Sfio_t *, sasl_conn_t *));
-
-# endif /* SFIO */
-# endif /* SASL */
+#if SASL
+extern int sfdcsasl __P((SM_FILE_T **, SM_FILE_T **, sasl_conn_t *));
+#endif /* SASL */
# if STARTTLS
-# if SFIO
-
-/* sf discipline to add tls */
-typedef struct _tlsdisc
-{
- Sfdisc_t disc;
- SSL *con;
-} Tlsdisc_t;
-
-extern int sfdctls __P((Sfio_t *, Sfio_t *, SSL *));
-
-# else /* SFIO */
-# if _FFR_TLS_TOREK
-
-typedef struct tls_conn
-{
- FILE *fp; /* original FILE * */
- SSL *con; /* SSL context */
-} Tlsdisc_t;
-
-extern int sfdctls __P((FILE **, FILE **, SSL *));
-
-# endif /* _FFR_TLS_TOREK */
-# endif /* SFIO */
+extern int sfdctls __P((SM_FILE_T **, SM_FILE_T **, SSL *));
# endif /* STARTTLS */
+
#endif /* ! SFSASL_H */
diff --git a/contrib/sendmail/src/shmticklib.c b/contrib/sendmail/src/shmticklib.c
index a3e1ad5..6f5e301 100644
--- a/contrib/sendmail/src/shmticklib.c
+++ b/contrib/sendmail/src/shmticklib.c
@@ -10,23 +10,17 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: shmticklib.c,v 8.6 2000/02/26 01:32:27 gshapiro Exp $";
-#endif /* ! lint */
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: shmticklib.c,v 8.14 2001/09/11 04:05:16 gshapiro Exp $")
#if _FFR_SHM_STATUS
-# if SFIO
-# include <sfio/stdio.h>
-# else /* !SFIO */
-# include <stdio.h>
-# endif /* SFIO */
# include <sys/types.h>
# include <sys/ipc.h>
# include <sys/shm.h>
# include "statusd_shm.h"
- /*
+/*
** SHMTICK -- increment a shared memory variable
**
** Parameters:
@@ -54,10 +48,10 @@ shmtick(inc_me, what)
if (shmid < 0)
return;
}
- if ((unsigned long *)sp == (unsigned long *)-1)
+ if ((unsigned long *) sp == (unsigned long *)-1)
{
- sp = (STATUSD_SHM *)shmat(shmid, NULL, 0);
- if ((unsigned long *)sp == (unsigned long *)-1)
+ sp = (STATUSD_SHM *) shmat(shmid, NULL, 0);
+ if ((unsigned long *) sp == (unsigned long *) -1)
return;
}
if (sp->magic != STATUSD_MAGIC)
diff --git a/contrib/sendmail/src/sm_resolve.c b/contrib/sendmail/src/sm_resolve.c
new file mode 100644
index 0000000..e3eb77f
--- /dev/null
+++ b/contrib/sendmail/src/sm_resolve.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright (c) 2000-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.
+ *
+ */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sendmail.h>
+#if DNSMAP
+# if NAMED_BIND
+# include "sm_resolve.h"
+
+SM_RCSID("$Id: sm_resolve.c,v 8.24 2001/09/11 04:05:16 gshapiro Exp $")
+
+static struct stot
+{
+ const char *st_name;
+ int st_type;
+} stot[] =
+{
+# if NETINET
+ { "A", T_A },
+# endif /* NETINET */
+# if NETINET6
+ { "AAAA", T_AAAA },
+# endif /* NETINET6 */
+ { "NS", T_NS },
+ { "CNAME", T_CNAME },
+ { "PTR", T_PTR },
+ { "MX", T_MX },
+ { "TXT", T_TXT },
+ { "AFSDB", T_AFSDB },
+ { "SRV", T_SRV },
+ { NULL, 0 }
+};
+
+/*
+** DNS_STRING_TO_TYPE -- convert resource record name into type
+**
+** Parameters:
+** name -- name of resource record type
+**
+** Returns:
+** type if succeeded.
+** -1 otherwise.
+*/
+
+int
+dns_string_to_type(name)
+ const char *name;
+{
+ struct stot *p = stot;
+
+ for (p = stot; p->st_name != NULL; p++)
+ if (sm_strcasecmp(name, p->st_name) == 0)
+ return p->st_type;
+ return -1;
+}
+
+/*
+** DNS_TYPE_TO_STRING -- convert resource record type into name
+**
+** Parameters:
+** type -- resource record type
+**
+** Returns:
+** name if succeeded.
+** NULL otherwise.
+*/
+
+const char *
+dns_type_to_string(type)
+ int type;
+{
+ struct stot *p = stot;
+
+ for (p = stot; p->st_name != NULL; p++)
+ if (type == p->st_type)
+ return p->st_name;
+ return NULL;
+}
+
+/*
+** DNS_FREE_DATA -- free all components of a DNS_REPLY_T
+**
+** Parameters:
+** r -- pointer to DNS_REPLY_T
+**
+** Returns:
+** none.
+*/
+
+void
+dns_free_data(r)
+ DNS_REPLY_T *r;
+{
+ RESOURCE_RECORD_T *rr;
+
+ if (r->dns_r_q.dns_q_domain != NULL)
+ sm_free(r->dns_r_q.dns_q_domain);
+ for (rr = r->dns_r_head; rr != NULL; )
+ {
+ RESOURCE_RECORD_T *tmp = rr;
+
+ if (rr->rr_domain != NULL)
+ sm_free(rr->rr_domain);
+ if (rr->rr_u.rr_data != NULL)
+ sm_free(rr->rr_u.rr_data);
+ rr = rr->rr_next;
+ sm_free(tmp);
+ }
+ sm_free(r);
+}
+
+/*
+** PARSE_DNS_REPLY -- parse DNS reply data.
+**
+** Parameters:
+** data -- pointer to dns data
+** len -- len of data
+**
+** Returns:
+** pointer to DNS_REPLY_T if succeeded.
+** NULL otherwise.
+*/
+
+static DNS_REPLY_T *
+parse_dns_reply(data, len)
+ unsigned char *data;
+ int len;
+{
+ unsigned char *p;
+ int status;
+ size_t l;
+ char host[MAXHOSTNAMELEN];
+ DNS_REPLY_T *r;
+ RESOURCE_RECORD_T **rr;
+
+ r = (DNS_REPLY_T *) xalloc(sizeof(*r));
+ memset(r, 0, sizeof(*r));
+ if (r == NULL)
+ return NULL;
+
+ p = data;
+
+ /* doesn't work on Crays? */
+ memcpy(&r->dns_r_h, p, sizeof(HEADER));
+ p += sizeof(HEADER);
+ status = dn_expand(data, data + len, p, host, sizeof host);
+ if (status < 0)
+ {
+ dns_free_data(r);
+ return NULL;
+ }
+ r->dns_r_q.dns_q_domain = sm_strdup(host);
+ if (r->dns_r_q.dns_q_domain == NULL)
+ {
+ dns_free_data(r);
+ return NULL;
+ }
+ p += status;
+ GETSHORT(r->dns_r_q.dns_q_type, p);
+ GETSHORT(r->dns_r_q.dns_q_class, p);
+ rr = &r->dns_r_head;
+ while (p < data + len)
+ {
+ int type, class, ttl, size;
+
+ status = dn_expand(data, data + len, p, host, sizeof host);
+ if (status < 0)
+ {
+ dns_free_data(r);
+ return NULL;
+ }
+ p += status;
+ GETSHORT(type, p);
+ GETSHORT(class, p);
+ GETLONG(ttl, p);
+ GETSHORT(size, p);
+ *rr = (RESOURCE_RECORD_T *) xalloc(sizeof(RESOURCE_RECORD_T));
+ if (*rr == NULL)
+ {
+ dns_free_data(r);
+ return NULL;
+ }
+ (*rr)->rr_domain = sm_strdup(host);
+ if ((*rr)->rr_domain == NULL)
+ {
+ dns_free_data(r);
+ return NULL;
+ }
+ (*rr)->rr_type = type;
+ (*rr)->rr_class = class;
+ (*rr)->rr_ttl = ttl;
+ (*rr)->rr_size = size;
+ switch (type)
+ {
+ case T_NS:
+ case T_CNAME:
+ case T_PTR:
+ status = dn_expand(data, data + len, p, host,
+ sizeof host);
+ if (status < 0)
+ {
+ dns_free_data(r);
+ return NULL;
+ }
+ (*rr)->rr_u.rr_txt = sm_strdup(host);
+ if ((*rr)->rr_u.rr_txt == NULL)
+ {
+ dns_free_data(r);
+ return NULL;
+ }
+ break;
+
+ case T_MX:
+ case T_AFSDB:
+ status = dn_expand(data, data + len, p + 2, host,
+ sizeof host);
+ if (status < 0)
+ {
+ dns_free_data(r);
+ return NULL;
+ }
+ l = strlen(host) + 1;
+ (*rr)->rr_u.rr_mx = (MX_RECORD_T *)
+ xalloc(sizeof(MX_RECORD_T) + l);
+ if ((*rr)->rr_u.rr_mx == NULL)
+ {
+ dns_free_data(r);
+ return NULL;
+ }
+ (*rr)->rr_u.rr_mx->mx_r_preference = (p[0] << 8) | p[1];
+ (void) sm_strlcpy((*rr)->rr_u.rr_mx->mx_r_domain,
+ host, l);
+ break;
+
+ case T_SRV:
+ status = dn_expand(data, data + len, p + 6, host,
+ sizeof host);
+ if (status < 0)
+ {
+ dns_free_data(r);
+ return NULL;
+ }
+ l = strlen(host) + 1;
+ (*rr)->rr_u.rr_srv = (SRV_RECORDT_T*)
+ xalloc(sizeof(SRV_RECORDT_T) + l);
+ if ((*rr)->rr_u.rr_srv == NULL)
+ {
+ dns_free_data(r);
+ return NULL;
+ }
+ (*rr)->rr_u.rr_srv->srv_r_priority = (p[0] << 8) | p[1];
+ (*rr)->rr_u.rr_srv->srv_r_weight = (p[2] << 8) | p[3];
+ (*rr)->rr_u.rr_srv->srv_r_port = (p[4] << 8) | p[5];
+ (void) sm_strlcpy((*rr)->rr_u.rr_srv->srv_r_target,
+ host, l);
+ break;
+
+ case T_TXT:
+ (*rr)->rr_u.rr_txt = (char *) xalloc(size + 1);
+ if ((*rr)->rr_u.rr_txt == NULL)
+ {
+ dns_free_data(r);
+ return NULL;
+ }
+ (void) strncpy((*rr)->rr_u.rr_txt, (char*) p + 1, *p);
+ (*rr)->rr_u.rr_txt[*p] = 0;
+ break;
+
+ default:
+ (*rr)->rr_u.rr_data = (unsigned char*) xalloc(size);
+ if (size != 0 && (*rr)->rr_u.rr_data == NULL)
+ {
+ dns_free_data(r);
+ return NULL;
+ }
+ (void) memcpy((*rr)->rr_u.rr_data, p, size);
+ }
+ p += size;
+ rr = &(*rr)->rr_next;
+ }
+ *rr = NULL;
+ return r;
+}
+
+/*
+** DNS_LOOKUP_INT -- perform dns map lookup (internal helper routine)
+**
+** Parameters:
+** domain -- name to lookup
+** rr_class -- resource record class
+** rr_type -- resource record type
+** retrans -- retransmission timeout
+** retry -- number of retries
+**
+** Returns:
+** result of lookup if succeeded.
+** NULL otherwise.
+*/
+
+DNS_REPLY_T *
+dns_lookup_int(domain, rr_class, rr_type, retrans, retry)
+ const char *domain;
+ int rr_class;
+ int rr_type;
+ time_t retrans;
+ int retry;
+{
+ int len;
+ unsigned long old_options = 0;
+ time_t save_retrans = 0;
+ int save_retry = 0;
+ DNS_REPLY_T *r = NULL;
+ unsigned char reply[1024];
+
+ if (tTd(8, 16))
+ {
+ old_options = _res.options;
+ _res.options |= RES_DEBUG;
+ sm_dprintf("dns_lookup(%s, %d, %s)\n", domain,
+ rr_class, dns_type_to_string(rr_type));
+ }
+ if (retrans > 0)
+ {
+ save_retrans = _res.retrans;
+ _res.retrans = retrans;
+ }
+ if (retry > 0)
+ {
+ save_retry = _res.retry;
+ _res.retry = retry;
+ }
+ errno = 0;
+ SM_SET_H_ERRNO(0);
+ len = res_search(domain, rr_class, rr_type, reply, sizeof reply);
+ if (tTd(8, 16))
+ {
+ _res.options = old_options;
+ sm_dprintf("dns_lookup(%s, %d, %s) --> %d\n",
+ domain, rr_class, dns_type_to_string(rr_type), len);
+ }
+ if (len >= 0)
+ r = parse_dns_reply(reply, len);
+ if (retrans > 0)
+ _res.retrans = save_retrans;
+ if (retry > 0)
+ _res.retry = save_retry;
+ return r;
+}
+
+# if 0
+DNS_REPLY_T *
+dns_lookup(domain, type_name, retrans, retry)
+ const char *domain;
+ const char *type_name;
+ time_t retrans;
+ int retry;
+{
+ int type;
+
+ type = dns_string_to_type(type_name);
+ if (type == -1)
+ {
+ if (tTd(8, 16))
+ sm_dprintf("dns_lookup: unknown resource type: `%s'\n",
+ type_name);
+ return NULL;
+ }
+ return dns_lookup_int(domain, C_IN, type, retrans, retry);
+}
+# endif /* 0 */
+# endif /* NAMED_BIND */
+#endif /* DNSMAP */
diff --git a/contrib/sendmail/src/sm_resolve.h b/contrib/sendmail/src/sm_resolve.h
new file mode 100644
index 0000000..7f169ba
--- /dev/null
+++ b/contrib/sendmail/src/sm_resolve.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2000-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.
+ *
+ */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id: sm_resolve.h,v 8.8 2001/09/01 00:06:02 gshapiro Exp $ */
+
+#if DNSMAP
+# ifndef __ROKEN_RESOLVE_H__
+# define __ROKEN_RESOLVE_H__
+
+/* We use these, but they are not always present in <arpa/nameser.h> */
+
+# ifndef T_TXT
+# define T_TXT 16
+# endif /* ! T_TXT */
+# ifndef T_AFSDB
+# define T_AFSDB 18
+# endif /* ! T_AFSDB */
+# ifndef T_SRV
+# define T_SRV 33
+# endif /* ! T_SRV */
+# ifndef T_NAPTR
+# define T_NAPTR 35
+# endif /* ! T_NAPTR */
+
+typedef struct
+{
+ char *dns_q_domain;
+ unsigned int dns_q_type;
+ unsigned int dns_q_class;
+} DNS_QUERY_T;
+
+typedef struct
+{
+ unsigned int mx_r_preference;
+ char mx_r_domain[1];
+} MX_RECORD_T;
+
+typedef struct
+{
+ unsigned int srv_r_priority;
+ unsigned int srv_r_weight;
+ unsigned int srv_r_port;
+ char srv_r_target[1];
+} SRV_RECORDT_T;
+
+
+typedef struct resource_record RESOURCE_RECORD_T;
+
+struct resource_record
+{
+ char *rr_domain;
+ unsigned int rr_type;
+ unsigned int rr_class;
+ unsigned int rr_ttl;
+ unsigned int rr_size;
+ union
+ {
+ void *rr_data;
+ MX_RECORD_T *rr_mx;
+ MX_RECORD_T *rr_afsdb; /* mx and afsdb are identical */
+ SRV_RECORDT_T *rr_srv;
+# if NETINET
+ struct in_addr *rr_a;
+# endif /* NETINET */
+# if NETINET6
+ struct in6_addr *rr_aaaa;
+# endif /* NETINET6 */
+ char *rr_txt;
+ } rr_u;
+ RESOURCE_RECORD_T *rr_next;
+};
+
+# if !defined(T_A) && !defined(T_AAAA)
+/* XXX if <arpa/nameser.h> isn't included */
+typedef int HEADER; /* will never be used */
+# endif /* !defined(T_A) && !defined(T_AAAA) */
+
+typedef struct
+{
+ HEADER dns_r_h;
+ DNS_QUERY_T dns_r_q;
+ RESOURCE_RECORD_T *dns_r_head;
+} DNS_REPLY_T;
+
+
+extern void dns_free_data __P((DNS_REPLY_T *));
+extern int dns_string_to_type __P((const char *));
+extern const char *dns_type_to_string __P((int));
+extern DNS_REPLY_T *dns_lookup_int __P((const char *,
+ int,
+ int,
+ time_t,
+ int));
+# if 0
+extern DNS_REPLY_T *dns_lookup __P((const char *domain,
+ const char *type_name,
+ time_t retrans,
+ int retry));
+# endif /* 0 */
+
+# endif /* ! __ROKEN_RESOLVE_H__ */
+#endif /* DNSMAP */
diff --git a/contrib/sendmail/src/srvrsmtp.c b/contrib/sendmail/src/srvrsmtp.c
index 89bcb0c..ab5d4ee 100644
--- a/contrib/sendmail/src/srvrsmtp.c
+++ b/contrib/sendmail/src/srvrsmtp.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
@@ -11,69 +11,88 @@
*
*/
-
#include <sendmail.h>
-
-#ifndef lint
-# if SMTP
-static char id[] = "@(#)$Id: srvrsmtp.c,v 8.471.2.2.2.78 2001/06/26 18:52:21 gshapiro Exp $ (with SMTP)";
-# else /* SMTP */
-static char id[] = "@(#)$Id: srvrsmtp.c,v 8.471.2.2.2.78 2001/06/26 18:52:21 gshapiro Exp $ (without SMTP)";
-# endif /* SMTP */
-#endif /* ! lint */
-
-#if SMTP
-# if SASL || STARTTLS
-# include "sfsasl.h"
-# endif /* SASL || STARTTLS */
-# if SASL
-# define ENC64LEN(l) (((l) + 2) * 4 / 3 + 1)
+#if MILTER
+# include <libmilter/mfdef.h>
+#endif /* MILTER */
+
+SM_RCSID("@(#)$Id: srvrsmtp.c,v 8.814 2002/01/08 00:56:22 ca Exp $")
+
+#if SASL || STARTTLS
+# include <sys/time.h>
+# include "sfsasl.h"
+#endif /* SASL || STARTTLS */
+#if SASL
+# define ENC64LEN(l) (((l) + 2) * 4 / 3 + 1)
static int saslmechs __P((sasl_conn_t *, char **));
-# endif /* SASL */
-# if STARTTLS
-# include <sysexits.h>
-# include <openssl/err.h>
-# include <openssl/bio.h>
-# include <openssl/pem.h>
-# ifndef HASURANDOMDEV
-# include <openssl/rand.h>
-# endif /* !HASURANDOMDEV */
-
-static SSL *srv_ssl = NULL;
-static SSL_CTX *srv_ctx = NULL;
-# if !TLS_NO_RSA
-static RSA *rsa = NULL;
-# endif /* !TLS_NO_RSA */
-static bool tls_ok_srv = FALSE;
-static int tls_verify_cb __P((X509_STORE_CTX *));
-# if !TLS_NO_RSA
-# define RSA_KEYLENGTH 512
-# endif /* !TLS_NO_RSA */
-# endif /* STARTTLS */
-
-static time_t checksmtpattack __P((volatile int *, int, bool,
+#endif /* SASL */
+#if STARTTLS
+# include <sysexits.h>
+
+static SSL_CTX *srv_ctx = NULL; /* TLS server context */
+static SSL *srv_ssl = NULL; /* per connection context */
+
+static bool tls_ok_srv = false;
+
+extern void tls_set_verify __P((SSL_CTX *, SSL *, bool));
+# define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \
+ bitset(SRV_VRFY_CLT, features))
+#endif /* STARTTLS */
+
+/* server features */
+#define SRV_NONE 0x0000 /* none... */
+#define SRV_OFFER_TLS 0x0001 /* offer STARTTLS */
+#define SRV_VRFY_CLT 0x0002 /* request a cert */
+#define SRV_OFFER_AUTH 0x0004 /* offer AUTH */
+#define SRV_OFFER_ETRN 0x0008 /* offer ETRN */
+#define SRV_OFFER_VRFY 0x0010 /* offer VRFY (not yet used) */
+#define SRV_OFFER_EXPN 0x0020 /* offer EXPN */
+#define SRV_OFFER_VERB 0x0040 /* offer VERB */
+#define SRV_OFFER_DSN 0x0080 /* offer DSN */
+#if PIPELINING
+# define SRV_OFFER_PIPE 0x0100 /* offer PIPELINING */
+# if _FFR_NO_PIPE
+# define SRV_NO_PIPE 0x0200 /* disable PIPELINING, sleep if used */
+# endif /* _FFR_NO_PIPE */
+#endif /* PIPELINING */
+#define SRV_REQ_AUTH 0x0400 /* require AUTH */
+#define SRV_TMP_FAIL 0x1000 /* ruleset caused a temporary failure */
+
+static unsigned int srvfeatures __P((ENVELOPE *, char *, unsigned int));
+
+static time_t checksmtpattack __P((volatile unsigned int *, int, bool,
char *, ENVELOPE *));
static void mail_esmtp_args __P((char *, char *, ENVELOPE *));
static void printvrfyaddr __P((ADDRESS *, bool, bool));
static void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
-static int runinchild __P((char *, ENVELOPE *));
static char *skipword __P((char *volatile, char *));
+static void setup_smtpd_io __P((void));
extern ENVELOPE BlankEnvelope;
+#define SKIP_SPACE(s) while (isascii(*s) && isspace(*s)) \
+ (s)++
+
/*
** SMTP -- run the SMTP protocol.
**
** Parameters:
** nullserver -- if non-NULL, rejection message for
-** all SMTP commands.
+** (almost) all SMTP commands.
+** d_flags -- daemon flags
** e -- the envelope.
**
** Returns:
** never.
**
** Side Effects:
-** Reads commands from the input channel and processes
-** them.
+** Reads commands from the input channel and processes them.
+*/
+
+/*
+** Notice: The smtp server doesn't have a session context like the client
+** side has (mci). Therefore some data (session oriented) is allocated
+** or assigned to the "wrong" structure (esp. STARTTLS, AUTH).
+** This should be fixed in a successor version.
*/
struct cmd
@@ -83,40 +102,37 @@ struct cmd
};
/* values for cmd_code */
-# define CMDERROR 0 /* bad command */
-# define CMDMAIL 1 /* mail -- designate sender */
-# define CMDRCPT 2 /* rcpt -- designate recipient */
-# define CMDDATA 3 /* data -- send message text */
-# define CMDRSET 4 /* rset -- reset state */
-# define CMDVRFY 5 /* vrfy -- verify address */
-# define CMDEXPN 6 /* expn -- expand address */
-# define CMDNOOP 7 /* noop -- do nothing */
-# define CMDQUIT 8 /* quit -- close connection and die */
-# define CMDHELO 9 /* helo -- be polite */
-# define CMDHELP 10 /* help -- give usage info */
-# define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */
-# define CMDETRN 12 /* etrn -- flush queue */
-# if SASL
-# define CMDAUTH 13 /* auth -- SASL authenticate */
-# endif /* SASL */
-# if STARTTLS
-# define CMDSTLS 14 /* STARTTLS -- start TLS session */
-# endif /* STARTTLS */
+#define CMDERROR 0 /* bad command */
+#define CMDMAIL 1 /* mail -- designate sender */
+#define CMDRCPT 2 /* rcpt -- designate recipient */
+#define CMDDATA 3 /* data -- send message text */
+#define CMDRSET 4 /* rset -- reset state */
+#define CMDVRFY 5 /* vrfy -- verify address */
+#define CMDEXPN 6 /* expn -- expand address */
+#define CMDNOOP 7 /* noop -- do nothing */
+#define CMDQUIT 8 /* quit -- close connection and die */
+#define CMDHELO 9 /* helo -- be polite */
+#define CMDHELP 10 /* help -- give usage info */
+#define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */
+#define CMDETRN 12 /* etrn -- flush queue */
+#if SASL
+# define CMDAUTH 13 /* auth -- SASL authenticate */
+#endif /* SASL */
+#if STARTTLS
+# define CMDSTLS 14 /* STARTTLS -- start TLS session */
+#endif /* STARTTLS */
/* non-standard commands */
-# define CMDONEX 16 /* onex -- sending one transaction only */
-# define CMDVERB 17 /* verb -- go into verbose mode */
-# define CMDXUSR 18 /* xusr -- initial (user) submission */
+#define CMDVERB 17 /* verb -- go into verbose mode */
/* unimplemented commands from RFC 821 */
-# define CMDUNIMPL 19 /* unimplemented rfc821 commands */
+#define CMDUNIMPL 19 /* unimplemented rfc821 commands */
/* use this to catch and log "door handle" attempts on your system */
-# define CMDLOGBOGUS 23 /* bogus command that should be logged */
+#define CMDLOGBOGUS 23 /* bogus command that should be logged */
/* debugging-only commands, only enabled if SMTPDEBUG is defined */
-# define CMDDBGQSHOW 24 /* showq -- show send queue */
-# define CMDDBGDEBUG 25 /* debug -- set debug mode */
+#define CMDDBGQSHOW 24 /* showq -- show send queue */
+#define CMDDBGDEBUG 25 /* debug -- set debug mode */
/*
-** Note: If you change this list,
-** remember to update 'helpfile'
+** Note: If you change this list, remember to update 'helpfile'
*/
static struct cmd CmdTab[] =
@@ -134,18 +150,16 @@ static struct cmd CmdTab[] =
{ "ehlo", CMDEHLO },
{ "etrn", CMDETRN },
{ "verb", CMDVERB },
- { "onex", CMDONEX },
- { "xusr", CMDXUSR },
{ "send", CMDUNIMPL },
{ "saml", CMDUNIMPL },
{ "soml", CMDUNIMPL },
{ "turn", CMDUNIMPL },
-# if SASL
+#if SASL
{ "auth", CMDAUTH, },
-# endif /* SASL */
-# if STARTTLS
+#endif /* SASL */
+#if STARTTLS
{ "starttls", CMDSTLS, },
-# endif /* STARTTLS */
+#endif /* STARTTLS */
/* remaining commands are here only to trap and log attempts to use them */
{ "showq", CMDDBGQSHOW },
{ "debug", CMDDBGDEBUG },
@@ -154,20 +168,167 @@ static struct cmd CmdTab[] =
{ NULL, CMDERROR }
};
-static bool OneXact = FALSE; /* one xaction only this run */
static char *CurSmtpClient; /* who's at the other end of channel */
-# define MAXBADCOMMANDS 25 /* maximum number of bad commands */
-# define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */
-# define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */
-# define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */
-# define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */
-# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */
+#ifndef MAXBADCOMMANDS
+# define MAXBADCOMMANDS 25 /* maximum number of bad commands */
+#endif
+#ifndef MAXNOOPCOMMANDS
+# define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */
+#endif
+#ifndef MAXHELOCOMMANDS
+# define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */
+#endif
+#ifndef MAXVRFYCOMMANDS
+# define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */
+#endif
+#ifndef MAXETRNCOMMANDS
+# define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */
+#endif
+#ifndef MAXTIMEOUT
+# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */
+#endif
+
+#if SM_HEAP_CHECK
+static SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp",
+ "@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $");
+#endif /* SM_HEAP_CHECK */
+
+typedef struct
+{
+ bool sm_gotmail; /* mail command received */
+ unsigned int sm_nrcpts; /* number of successful RCPT commands */
+#if _FFR_ADAPTIVE_EOL
+WARNING: do NOT use this FFR, it is most likely broken
+ bool sm_crlf; /* input in CRLF form? */
+#endif /* _FFR_ADAPTIVE_EOL */
+ bool sm_discard;
+#if MILTER
+ bool sm_milterize;
+ bool sm_milterlist; /* any filters in the list? */
+#endif /* MILTER */
+#if _FFR_QUARANTINE
+ char *sm_quarmsg; /* carry quarantining across messages */
+#endif /* _FFR_QUARANTINE */
+} SMTP_T;
+
+static void smtp_data __P((SMTP_T *, ENVELOPE *));
+
+#define MSG_TEMPFAIL "451 4.7.1 Please try again later"
+
+#if MILTER
+# define MILTER_ABORT(e) milter_abort((e))
+# define MILTER_REPLY(str) \
+ { \
+ int savelogusrerrs = LogUsrErrs; \
+ \
+ switch (state) \
+ { \
+ case SMFIR_REPLYCODE: \
+ if (MilterLogLevel > 3) \
+ { \
+ sm_syslog(LOG_INFO, e->e_id, \
+ "Milter: %s=%s, reject=%s", \
+ str, addr, response); \
+ LogUsrErrs = false; \
+ } \
+ usrerr(response); \
+ break; \
+ \
+ case SMFIR_REJECT: \
+ if (MilterLogLevel > 3) \
+ { \
+ sm_syslog(LOG_INFO, e->e_id, \
+ "Milter: %s=%s, reject=550 5.7.1 Command rejected", \
+ str, addr); \
+ LogUsrErrs = false; \
+ } \
+ usrerr("550 5.7.1 Command rejected"); \
+ break; \
+ \
+ case SMFIR_DISCARD: \
+ if (MilterLogLevel > 3) \
+ sm_syslog(LOG_INFO, e->e_id, \
+ "Milter: %s=%s, discard", \
+ str, addr); \
+ e->e_flags |= EF_DISCARD; \
+ break; \
+ \
+ case SMFIR_TEMPFAIL: \
+ if (MilterLogLevel > 3) \
+ { \
+ sm_syslog(LOG_INFO, e->e_id, \
+ "Milter: %s=%s, reject=%s", \
+ str, addr, MSG_TEMPFAIL); \
+ LogUsrErrs = false; \
+ } \
+ usrerr(MSG_TEMPFAIL); \
+ break; \
+ } \
+ LogUsrErrs = savelogusrerrs; \
+ if (response != NULL) \
+ sm_free(response); /* XXX */ \
+ }
+
+#else /* MILTER */
+# define MILTER_ABORT(e)
+#endif /* MILTER */
+
+/* clear all SMTP state (for HELO/EHLO/RSET) */
+#define CLEAR_STATE(cmd) \
+{ \
+ /* abort milter filters */ \
+ MILTER_ABORT(e); \
+ \
+ if (smtp.sm_nrcpts > 0) \
+ { \
+ logundelrcpts(e, cmd, 10, false); \
+ smtp.sm_nrcpts = 0; \
+ macdefine(&e->e_macro, A_PERM, \
+ macid("{nrcpts}"), "0"); \
+ } \
+ \
+ e->e_sendqueue = NULL; \
+ e->e_flags |= EF_CLRQUEUE; \
+ \
+ if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) \
+ logsender(e, NULL); \
+ e->e_flags &= ~EF_LOGSENDER; \
+ \
+ /* clean up a bit */ \
+ smtp.sm_gotmail = false; \
+ SuprErrs = true; \
+ dropenvelope(e, true, false); \
+ sm_rpool_free(e->e_rpool); \
+ e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL)); \
+ CurEnv = e; \
+}
+
+/* sleep to flatten out connection load */
+#define MIN_DELAY_LOG 15 /* wait before logging this again */
+
+/* is it worth setting the process title for 1s? */
+#define DELAY_CONN(cmd) \
+ if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA) \
+ { \
+ time_t dnow; \
+ \
+ sm_setproctitle(true, e, \
+ "%s: %s: delaying %s: load average: %d", \
+ qid_printname(e), CurSmtpClient, \
+ cmd, DelayLA); \
+ if (LogLevel > 8 && (dnow = curtime()) > log_delay) \
+ { \
+ sm_syslog(LOG_INFO, e->e_id, \
+ "delaying=%s, load average=%d >= %d", \
+ cmd, CurrentLA, DelayLA); \
+ log_delay = dnow + MIN_DELAY_LOG; \
+ } \
+ (void) sleep(1); \
+ sm_setproctitle(true, e, "%s %s: %.80s", \
+ qid_printname(e), CurSmtpClient, inp); \
+ }
-/* runinchild() returns */
-# define RIC_INCHILD 0 /* in a child process */
-# define RIC_INPARENT 1 /* still in parent process */
-# define RIC_TEMPFAIL 2 /* temporary failure occurred */
void
smtp(nullserver, d_flags, e)
@@ -180,7 +341,6 @@ smtp(nullserver, d_flags, e)
char *cmd;
auto ADDRESS *vrfyqueue;
ADDRESS *a;
- volatile bool gotmail; /* mail command received */
volatile bool gothello; /* helo command received */
bool vrfy; /* set if this is a vrfy command */
char *volatile protocol; /* sending protocol */
@@ -188,72 +348,92 @@ smtp(nullserver, d_flags, e)
char *volatile peerhostname; /* name of SMTP peer or "localhost" */
auto char *delimptr;
char *id;
- volatile int nrcpts = 0; /* number of RCPT commands */
- int ric;
- bool doublequeue;
- volatile bool discard;
- volatile int badcommands = 0; /* count of bad commands */
- volatile int nverifies = 0; /* count of VRFY/EXPN commands */
- volatile int n_etrn = 0; /* count of ETRN commands */
- volatile int n_noop = 0; /* count of NOOP/VERB/ONEX etc cmds */
- volatile int n_helo = 0; /* count of HELO/EHLO commands */
- volatile int delay = 1; /* timeout for bad commands */
+ volatile unsigned int n_badcmds = 0; /* count of bad commands */
+ volatile unsigned int n_badrcpts = 0; /* number of rejected RCPT */
+ volatile unsigned int n_verifies = 0; /* count of VRFY/EXPN */
+ volatile unsigned int n_etrn = 0; /* count of ETRN */
+ volatile unsigned int n_noop = 0; /* count of NOOP/VERB/etc */
+ volatile unsigned int n_helo = 0; /* count of HELO/EHLO */
bool ok;
- volatile bool tempfail = FALSE;
-# if _FFR_MILTER
- volatile bool milterize = (nullserver == NULL);
-# endif /* _FFR_MILTER */
+#if _FFR_ADAPTIVE_EOL
+ volatile bool first;
+#endif /* _FFR_ADAPTIVE_EOL */
+ volatile bool tempfail = false;
volatile time_t wt; /* timeout after too many commands */
volatile time_t previous; /* time after checksmtpattack() */
- volatile bool lognullconnection = TRUE;
+ volatile bool lognullconnection = true;
register char *q;
+ SMTP_T smtp;
char *addr;
char *greetcode = "220";
+ char *hostname; /* my hostname ($j) */
QUEUE_CHAR *new;
int argno;
char *args[MAXSMTPARGS];
char inp[MAXLINE];
char cmdbuf[MAXLINE];
-# if SASL
+#if SASL
sasl_conn_t *conn;
volatile bool sasl_ok;
- volatile int n_auth = 0; /* count of AUTH commands */
+ volatile unsigned int n_auth = 0; /* count of AUTH commands */
bool ismore;
int result;
volatile int authenticating;
- char *hostname;
char *user;
char *in, *out, *out2;
const char *errstr;
- int inlen, out2len;
+ unsigned int inlen, out2len;
unsigned int outlen;
char *volatile auth_type;
char *mechlist;
- volatile int n_mechs;
- int len;
+ volatile unsigned int n_mechs;
+ unsigned int len;
sasl_security_properties_t ssp;
sasl_external_properties_t ext_ssf;
-# if SFIO
sasl_ssf_t *ssf;
-# endif /* SFIO */
-# endif /* SASL */
-# if STARTTLS
+#endif /* SASL */
+#if STARTTLS
int r;
int rfd, wfd;
- volatile bool usetls = TRUE;
- volatile bool tls_active = FALSE;
+ volatile bool tls_active = false;
+# if _FFR_SMTP_SSL
+ volatile bool smtps = false;
+# endif /* _FFR_SMTP_SSL */
bool saveQuickAbort;
bool saveSuprErrs;
-# endif /* STARTTLS */
-
- if (fileno(OutChannel) != fileno(stdout))
+ time_t tlsstart;
+#endif /* STARTTLS */
+ volatile unsigned int features;
+#if PIPELINING
+# if _FFR_NO_PIPE
+ int np_log = 0;
+# endif /* _FFR_NO_PIPE */
+#endif /* PIPELINING */
+ volatile time_t log_delay = (time_t) 0;
+
+ smtp.sm_nrcpts = 0;
+#if MILTER
+ smtp.sm_milterize = (nullserver == NULL);
+ smtp.sm_milterlist = false;
+#endif /* MILTER */
+
+ /* setup I/O fd correctly for the SMTP server */
+ setup_smtpd_io();
+
+#if SM_HEAP_CHECK
+ if (sm_debug_active(&DebugLeakSmtp, 1))
{
- /* arrange for debugging output to go to remote host */
- (void) dup2(fileno(OutChannel), fileno(stdout));
+ sm_heap_newgroup();
+ sm_dprintf("smtp() heap group #%d\n", sm_heap_group());
}
+#endif /* SM_HEAP_CHECK */
+
+ /* XXX the rpool should be set when e is initialized in main() */
+ e->e_rpool = sm_rpool_new_x(NULL);
+ e->e_macro.mac_rpool = e->e_rpool;
settime(e);
- (void)sm_getla(e);
+ sm_getla();
peerhostname = RealHostName;
if (peerhostname == NULL)
peerhostname = "localhost";
@@ -263,27 +443,89 @@ smtp(nullserver, d_flags, e)
CurSmtpClient = CurHostName;
/* check_relay may have set discard bit, save for later */
- discard = bitset(EF_DISCARD, e->e_flags);
+ smtp.sm_discard = bitset(EF_DISCARD, e->e_flags);
+
+#if PIPELINING
+ /* auto-flush output when reading input */
+ (void) sm_io_autoflush(InChannel, OutChannel);
+#endif /* PIPELINING */
+
+ sm_setproctitle(true, e, "server %s startup", CurSmtpClient);
+
+ /* Set default features for server. */
+ features = ((bitset(PRIV_NOETRN, PrivacyFlags) ||
+ bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN)
+ | (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE)
+ | (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE
+ : (SRV_OFFER_EXPN
+ | (bitset(PRIV_NOVERB, PrivacyFlags)
+ ? SRV_NONE : SRV_OFFER_VERB)))
+ | (bitset(PRIV_NORECEIPTS, PrivacyFlags) ? SRV_NONE
+ : SRV_OFFER_DSN)
+#if SASL
+ | (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH)
+#endif /* SASL */
+#if PIPELINING
+ | SRV_OFFER_PIPE
+#endif /* PIPELINING */
+#if STARTTLS
+ | (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS)
+ | (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE
+ : SRV_VRFY_CLT)
+#endif /* STARTTLS */
+ ;
+ if (nullserver == NULL)
+ {
+ features = srvfeatures(e, CurSmtpClient, features);
+ if (bitset(SRV_TMP_FAIL, features))
+ {
+ if (LogLevel > 4)
+ sm_syslog(LOG_ERR, NOQID,
+ "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled",
+ CurSmtpClient);
+ nullserver = "450 4.3.0 Please try again later.";
+ }
+#if PIPELINING
+# if _FFR_NO_PIPE
+ else if (bitset(SRV_NO_PIPE, features))
+ {
+ /* for consistency */
+ features &= ~SRV_OFFER_PIPE;
+ }
+# endif /* _FFR_NO_PIPE */
+#endif /* PIPELINING */
+ }
- sm_setproctitle(TRUE, e, "server %s startup", CurSmtpClient);
+ hostname = macvalue('j', e);
-# if SASL
- sasl_ok = FALSE; /* SASL can't be used (yet) */
+
+#if SASL
+ sasl_ok = bitset(SRV_OFFER_AUTH, features);
n_mechs = 0;
+ authenticating = SASL_NOT_AUTH;
/* SASL server new connection */
- hostname = macvalue('j', e);
-# if SASL > 10505
- /* use empty realm: only works in SASL > 1.5.5 */
- result = sasl_server_new("smtp", hostname, "", NULL, 0, &conn);
-# else /* SASL > 10505 */
- /* use no realm -> realm is set to hostname by SASL lib */
- result = sasl_server_new("smtp", hostname, NULL, NULL, 0, &conn);
-# endif /* SASL > 10505 */
- if (result == SASL_OK)
+ if (sasl_ok)
+ {
+# if SASL > 10505
+ /* use empty realm: only works in SASL > 1.5.5 */
+ result = sasl_server_new("smtp", hostname, "", NULL, 0, &conn);
+# else /* SASL > 10505 */
+ /* use no realm -> realm is set to hostname by SASL lib */
+ result = sasl_server_new("smtp", hostname, NULL, NULL, 0,
+ &conn);
+# endif /* SASL > 10505 */
+ sasl_ok = result == SASL_OK;
+ if (!sasl_ok)
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, NOQID,
+ "AUTH error: sasl_server_new failed=%d",
+ result);
+ }
+ }
+ if (sasl_ok)
{
- sasl_ok = TRUE;
-
/*
** SASL set properties for sasl
** set local/remote IP
@@ -293,8 +535,8 @@ smtp(nullserver, d_flags, e)
** Kerberos_v4
*/
-# if NETINET
- in = macvalue(macid("{daemon_family}", NULL), e);
+#if NETINET
+ in = macvalue(macid("{daemon_family}"), e);
if (in != NULL && strcmp(in, "inet") == 0)
{
SOCKADDR_LEN_T addrsize;
@@ -302,135 +544,148 @@ smtp(nullserver, d_flags, e)
struct sockaddr_in saddr_r;
addrsize = sizeof(struct sockaddr_in);
- if (getpeername(fileno(InChannel),
+ if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
+ NULL),
(struct sockaddr *)&saddr_r,
&addrsize) == 0)
{
sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
addrsize = sizeof(struct sockaddr_in);
- if (getsockname(fileno(InChannel),
+ if (getsockname(sm_io_getinfo(InChannel,
+ SM_IO_WHAT_FD,
+ NULL),
(struct sockaddr *)&saddr_l,
&addrsize) == 0)
sasl_setprop(conn, SASL_IP_LOCAL,
&saddr_l);
}
}
-# endif /* NETINET */
+#endif /* NETINET */
- authenticating = SASL_NOT_AUTH;
auth_type = NULL;
mechlist = NULL;
user = NULL;
-# if 0
- define(macid("{auth_author}", NULL), NULL, &BlankEnvelope);
-# endif /* 0 */
+# if 0
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{auth_author}"), NULL);
+# endif /* 0 */
/* set properties */
(void) memset(&ssp, '\0', sizeof ssp);
-# if SFIO
+
/* XXX should these be options settable via .cf ? */
/* ssp.min_ssf = 0; is default due to memset() */
+# if STARTTLS
+# endif /* STARTTLS */
{
- ssp.max_ssf = INT_MAX;
+ ssp.max_ssf = MaxSLBits;
ssp.maxbufsize = MAXOUTLEN;
}
-# endif /* SFIO */
-# if _FFR_SASL_OPTS
ssp.security_flags = SASLOpts & SASL_SEC_MASK;
-# endif /* _FFR_SASL_OPTS */
sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK;
if (sasl_ok)
{
/*
** external security strength factor;
- ** we have none so zero
-# if STARTTLS
- ** we may have to change this for STARTTLS
- ** (dynamically)
-# endif
+ ** currently we have none so zero
*/
+
ext_ssf.ssf = 0;
ext_ssf.auth_id = NULL;
sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
&ext_ssf) == SASL_OK;
}
if (sasl_ok)
- {
n_mechs = saslmechs(conn, &mechlist);
- sasl_ok = n_mechs > 0;
- }
}
- else
- {
- if (LogLevel > 9)
- sm_syslog(LOG_WARNING, NOQID,
- "SASL error: sasl_server_new failed=%d",
- result);
- }
-# endif /* SASL */
+#endif /* SASL */
-# if STARTTLS
-# if _FFR_TLS_O_T
- saveQuickAbort = QuickAbort;
- saveSuprErrs = SuprErrs;
- SuprErrs = TRUE;
- QuickAbort = FALSE;
- if (rscheck("offer_tls", CurSmtpClient, "", e, TRUE, FALSE, 8,
- NULL) != EX_OK || Errors > 0)
- usetls = FALSE;
- QuickAbort = saveQuickAbort;
- SuprErrs = saveSuprErrs;
-# endif /* _FFR_TLS_O_T */
-# endif /* STARTTLS */
-
-# if _FFR_MILTER
- if (milterize)
+#if MILTER
+ if (smtp.sm_milterize)
{
char state;
/* initialize mail filter connection */
- milter_init(e, &state);
+ smtp.sm_milterlist = milter_init(e, &state);
switch (state)
{
case SMFIR_REJECT:
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: inititalization failed, rejecting commands");
greetcode = "554";
nullserver = "Command rejected";
- milterize = FALSE;
+ smtp.sm_milterize = false;
break;
case SMFIR_TEMPFAIL:
- tempfail = TRUE;
- milterize = FALSE;
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: inititalization failed, temp failing commands");
+ tempfail = true;
+ smtp.sm_milterize = false;
break;
}
}
- if (milterize && !bitset(EF_DISCARD, e->e_flags))
+ if (smtp.sm_milterlist && smtp.sm_milterize &&
+ !bitset(EF_DISCARD, e->e_flags))
{
char state;
+ char *response;
- (void) milter_connect(peerhostname, RealHostAddr,
- e, &state);
+ response = milter_connect(peerhostname, RealHostAddr,
+ e, &state);
switch (state)
{
case SMFIR_REPLYCODE: /* REPLYCODE shouldn't happen */
case SMFIR_REJECT:
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: connect: host=%s, addr=%s, rejecting commands",
+ peerhostname,
+ anynet_ntoa(&RealHostAddr));
greetcode = "554";
nullserver = "Command rejected";
- milterize = FALSE;
+ smtp.sm_milterize = false;
break;
case SMFIR_TEMPFAIL:
- tempfail = TRUE;
- milterize = FALSE;
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: connect: host=%s, addr=%s, temp failing commands",
+ peerhostname,
+ anynet_ntoa(&RealHostAddr));
+ tempfail = true;
+ smtp.sm_milterize = false;
break;
}
+ if (response != NULL)
+
+ sm_free(response); /* XXX */
}
-# endif /* _FFR_MILTER */
+#endif /* MILTER */
+
+#if STARTTLS
+# if _FFR_SMTP_SSL
+ /* If this an smtps connection, start TLS now */
+ smtps = bitnset(D_SMTPS, d_flags);
+ if (smtps)
+ goto starttls;
+
+ greeting:
+
+# endif /* _FFR_SMTP_SSL */
+#endif /* STARTTLS */
/* output the first line, inserting "ESMTP" as second word */
- expand(SmtpGreeting, inp, sizeof inp, e);
+ if (*greetcode == '5')
+ (void) sm_snprintf(inp, sizeof inp, "%s not accepting messages",
+ hostname);
+ else
+ expand(SmtpGreeting, inp, sizeof inp, e);
+
p = strchr(inp, '\n');
if (p != NULL)
*p++ = '\0';
@@ -438,10 +693,10 @@ smtp(nullserver, d_flags, e)
if (id == NULL)
id = &inp[strlen(inp)];
if (p == NULL)
- snprintf(cmdbuf, sizeof cmdbuf,
+ (void) sm_snprintf(cmdbuf, sizeof cmdbuf,
"%s %%.*s ESMTP%%s", greetcode);
else
- snprintf(cmdbuf, sizeof cmdbuf,
+ (void) sm_snprintf(cmdbuf, sizeof cmdbuf,
"%s-%%.*s ESMTP%%s", greetcode);
message(cmdbuf, (int) (id - inp), inp, id);
@@ -451,70 +706,84 @@ smtp(nullserver, d_flags, e)
*p++ = '\0';
if (isascii(*id) && isspace(*id))
id++;
- (void) snprintf(cmdbuf, sizeof cmdbuf, "%s-%%s", greetcode);
+ (void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, "-%s");
message(cmdbuf, id);
}
if (id != NULL)
{
if (isascii(*id) && isspace(*id))
id++;
- (void) snprintf(cmdbuf, sizeof cmdbuf, "%s %%s", greetcode);
+ (void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, " %s");
message(cmdbuf, id);
}
protocol = NULL;
sendinghost = macvalue('s', e);
- gothello = FALSE;
- gotmail = FALSE;
+
+#if _FFR_QUARANTINE
+ /* If quarantining by a connect/ehlo action, save between messages */
+ if (e->e_quarmsg == NULL)
+ smtp.sm_quarmsg = NULL;
+ else
+ smtp.sm_quarmsg = newstr(e->e_quarmsg);
+#endif /* _FFR_QUARANTINE */
+
+ /* sendinghost's storage must outlive the current envelope */
+ if (sendinghost != NULL)
+ sendinghost = sm_strdup_x(sendinghost);
+#if _FFR_ADAPTIVE_EOL
+ first = true;
+#endif /* _FFR_ADAPTIVE_EOL */
+ gothello = false;
+ smtp.sm_gotmail = false;
for (;;)
{
- /* arrange for backout */
- (void) setjmp(TopFrame);
- QuickAbort = FALSE;
- HoldErrs = FALSE;
- SuprErrs = FALSE;
- LogUsrErrs = FALSE;
- OnlyOneError = TRUE;
+ SM_TRY
+ {
+ QuickAbort = false;
+ HoldErrs = false;
+ SuprErrs = false;
+ LogUsrErrs = false;
+ OnlyOneError = true;
e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
/* setup for the read */
e->e_to = NULL;
Errors = 0;
FileName = NULL;
- (void) fflush(stdout);
+ (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
/* read the input line */
SmtpPhase = "server cmd read";
- sm_setproctitle(TRUE, e, "server %s cmd read", CurSmtpClient);
-# if SASL
+ sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient);
+#if SASL
/*
- ** SMTP AUTH requires accepting any length,
- ** at least for challenge/response
- ** XXX
+ ** XXX SMTP AUTH requires accepting any length,
+ ** at least for challenge/response
*/
-# endif /* SASL */
+#endif /* SASL */
/* handle errors */
- if (ferror(OutChannel) ||
+ if (sm_io_error(OutChannel) ||
(p = sfgets(inp, sizeof inp, InChannel,
TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
{
char *d;
- d = macvalue(macid("{daemon_name}", NULL), e);
+ d = macvalue(macid("{daemon_name}"), e);
if (d == NULL)
d = "stdin";
/* end of file, just die */
disconnect(1, e);
-# if _FFR_MILTER
+#if MILTER
/* close out milter filters */
milter_quit(e);
-# endif /* _FFR_MILTER */
+#endif /* MILTER */
message("421 4.4.1 %s Lost input channel from %s",
MyHostName, CurSmtpClient);
- if (LogLevel > (gotmail ? 1 : 19))
+ if (LogLevel > (smtp.sm_gotmail ? 1 : 19))
sm_syslog(LOG_NOTICE, e->e_id,
"lost input channel from %.100s to %s after %s",
CurSmtpClient, d,
@@ -529,20 +798,64 @@ smtp(nullserver, d_flags, e)
goto doquit;
}
+#if _FFR_ADAPTIVE_EOL
+ if (first)
+ {
+ char *p;
+
+ smtp.sm_crlf = true;
+ p = strchr(inp, '\n');
+ if (p == NULL || p <= inp || p[-1] != '\r')
+ {
+ smtp.sm_crlf = false;
+ if (tTd(66, 1) && LogLevel > 8)
+ {
+ /* how many bad guys are there? */
+ sm_syslog(LOG_INFO, NOQID,
+ "%.100s did not use CRLF",
+ CurSmtpClient);
+ }
+ }
+ first = false;
+ }
+#endif /* _FFR_ADAPTIVE_EOL */
+
/* clean up end of line */
- fixcrlf(inp, TRUE);
+ fixcrlf(inp, true);
-# if SASL
+#if PIPELINING
+# if _FFR_NO_PIPE
+ /*
+ ** if there is more input and pipelining is disabled:
+ ** delay ... (and maybe discard the input?)
+ ** XXX this doesn't really work, at least in tests using
+ ** telnet SM_IO_IS_READABLE only returns 1 if there were
+ ** more than 2 input lines available.
+ */
+
+ if (bitset(SRV_NO_PIPE, features) &&
+ sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL))
+ {
+ if (++np_log < 3)
+ sm_syslog(LOG_INFO, NOQID,
+ "unauthorized PIPELINING, sleeping");
+ sleep(1);
+ }
+
+# endif /* _FFR_NO_PIPE */
+#endif /* PIPELINING */
+
+#if SASL
if (authenticating == SASL_PROC_AUTH)
{
-# if 0
+# if 0
if (*inp == '\0')
{
authenticating = SASL_NOT_AUTH;
message("501 5.5.2 missing input");
continue;
}
-# endif /* 0 */
+# endif /* 0 */
if (*inp == '*' && *(inp + 1) == '\0')
{
authenticating = SASL_NOT_AUTH;
@@ -574,97 +887,95 @@ smtp(nullserver, d_flags, e)
authenticated:
message("235 2.0.0 OK Authenticated");
authenticating = SASL_IS_AUTH;
- define(macid("{auth_type}", NULL),
- newstr(auth_type), &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{auth_type}"), auth_type);
result = sasl_getprop(conn, SASL_USERNAME,
(void **)&user);
if (result != SASL_OK)
{
user = "";
- define(macid("{auth_authen}", NULL),
- NULL, &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro,
+ A_PERM,
+ macid("{auth_authen}"), NULL);
}
else
{
- define(macid("{auth_authen}", NULL),
- newstr(user), &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro,
+ A_TEMP,
+ macid("{auth_authen}"), user);
}
-# if 0
+# if 0
/* get realm? */
sasl_getprop(conn, SASL_REALM, (void **) &data);
-# endif /* 0 */
-
+# endif /* 0 */
-# if SFIO
/* get security strength (features) */
result = sasl_getprop(conn, SASL_SSF,
(void **) &ssf);
if (result != SASL_OK)
{
- define(macid("{auth_ssf}", NULL),
- "0", &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro,
+ A_PERM,
+ macid("{auth_ssf}"), "0");
ssf = NULL;
}
else
{
char pbuf[8];
- snprintf(pbuf, sizeof pbuf, "%u", *ssf);
- define(macid("{auth_ssf}", NULL),
- newstr(pbuf), &BlankEnvelope);
+ (void) sm_snprintf(pbuf, sizeof pbuf,
+ "%u", *ssf);
+ macdefine(&BlankEnvelope.e_macro,
+ A_TEMP,
+ macid("{auth_ssf}"), pbuf);
if (tTd(95, 8))
- dprintf("SASL auth_ssf: %u\n",
- *ssf);
+ sm_dprintf("AUTH auth_ssf: %u\n",
+ *ssf);
}
+
/*
- ** only switch to encrypted connection
+ ** Only switch to encrypted connection
** if a security layer has been negotiated
*/
+
if (ssf != NULL && *ssf > 0)
{
/*
- ** convert sfio stuff to use SASL
- ** check return values
- ** if the call fails,
- ** fall back to unencrypted version
- ** unless some cf option requires
- ** encryption then the connection must
- ** be aborted
+ ** Convert I/O layer to use SASL.
+ ** If the call fails, the connection
+ ** is aborted.
*/
- if (sfdcsasl(InChannel, OutChannel,
- conn) == 0)
+
+ if (sfdcsasl(&InChannel, &OutChannel,
+ conn) == 0)
{
/* restart dialogue */
- gothello = FALSE;
- OneXact = TRUE;
n_helo = 0;
+#if PIPELINING
+ (void) sm_io_autoflush(InChannel,
+ OutChannel);
+#endif /* PIPELINING */
}
else
syserr("503 5.3.3 SASL TLS failed");
- if (LogLevel > 9)
- sm_syslog(LOG_INFO,
- NOQID,
- "SASL: connection from %.64s: mech=%.16s, id=%.64s, bits=%d",
- CurSmtpClient,
- auth_type, user,
- *ssf);
}
-# else /* SFIO */
- if (LogLevel > 9)
+
+ /* NULL pointer ok since it's our function */
+ if (LogLevel > 8)
sm_syslog(LOG_INFO, NOQID,
- "SASL: connection from %.64s: mech=%.16s, id=%.64s",
- CurSmtpClient, auth_type,
- user);
-# endif /* SFIO */
+ "AUTH=server, relay=%.100s, authid=%.128s, mech=%.16s, bits=%d",
+ CurSmtpClient,
+ shortenstring(user, 128),
+ auth_type, *ssf);
}
else if (result == SASL_CONTINUE)
{
len = ENC64LEN(outlen);
out2 = xalloc(len);
result = sasl_encode64(out, outlen, out2, len,
- (u_int *)&out2len);
+ &out2len);
if (result != SASL_OK)
{
/* correct code? XXX */
@@ -672,7 +983,7 @@ smtp(nullserver, d_flags, e)
message("454 4.5.4 Internal error: unable to encode64");
if (LogLevel > 5)
sm_syslog(LOG_WARNING, e->e_id,
- "SASL encode64 error [%d for \"%s\"]",
+ "AUTH encode64 error [%d for \"%s\"]",
result, out);
/* start over? */
authenticating = SASL_NOT_AUTH;
@@ -681,8 +992,8 @@ smtp(nullserver, d_flags, e)
{
message("334 %s", out2);
if (tTd(95, 2))
- dprintf("SASL continue: msg='%s' len=%d\n",
- out2, out2len);
+ sm_dprintf("AUTH continue: msg='%s' len=%u\n",
+ out2, out2len);
}
}
else
@@ -691,35 +1002,27 @@ smtp(nullserver, d_flags, e)
message("500 5.7.0 authentication failed");
if (LogLevel > 9)
sm_syslog(LOG_WARNING, e->e_id,
- "AUTH failure (%s): %s (%d)",
+ "AUTH failure (%s): %s (%d) %s",
auth_type,
sasl_errstring(result, NULL,
NULL),
- result);
+ result,
+ errstr == NULL ? "" : errstr);
authenticating = SASL_NOT_AUTH;
}
}
else
{
/* don't want to do any of this if authenticating */
-# endif /* SASL */
+#endif /* SASL */
/* echo command to transcript */
if (e->e_xfp != NULL)
- fprintf(e->e_xfp, "<<< %s\n", inp);
-
- if (LogLevel >= 15)
- sm_syslog(LOG_INFO, e->e_id,
- "<-- %s",
- inp);
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "<<< %s\n", inp);
- if (e->e_id == NULL)
- sm_setproctitle(TRUE, e, "%s: %.80s",
- CurSmtpClient, inp);
- else
- sm_setproctitle(TRUE, e, "%s %s: %.80s",
- qid_printname(e),
- CurSmtpClient, inp);
+ if (LogLevel > 14)
+ sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp);
/* break off command */
for (p = inp; isascii(*p) && isspace(*p); p++)
@@ -732,24 +1035,52 @@ smtp(nullserver, d_flags, e)
*cmd = '\0';
/* throw away leading whitespace */
- while (isascii(*p) && isspace(*p))
- p++;
+ SKIP_SPACE(p);
/* decode command */
for (c = CmdTab; c->cmd_name != NULL; c++)
{
- if (strcasecmp(c->cmd_name, cmdbuf) == 0)
+ if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
break;
}
/* reset errors */
errno = 0;
+ /* check whether a "non-null" command has been used */
+ switch (c->cmd_code)
+ {
+#if SASL
+ case CMDAUTH:
+ /* avoid information leak; take first two words? */
+ q = "AUTH";
+ break;
+#endif /* SASL */
+
+ case CMDMAIL:
+ case CMDEXPN:
+ case CMDVRFY:
+ case CMDETRN:
+ lognullconnection = false;
+ /* FALLTHROUGH */
+ default:
+ q = inp;
+ break;
+ }
+
+ if (e->e_id == NULL)
+ sm_setproctitle(true, e, "%s: %.80s",
+ CurSmtpClient, q);
+ else
+ sm_setproctitle(true, e, "%s %s: %.80s",
+ qid_printname(e),
+ CurSmtpClient, q);
+
/*
** Process command.
**
** If we are running as a null server, return 550
- ** to everything.
+ ** to almost everything.
*/
if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags))
@@ -768,22 +1099,30 @@ smtp(nullserver, d_flags, e)
if (bitnset(D_ETRNONLY, d_flags) &&
nullserver == NULL)
break;
+ DELAY_CONN("ETRN");
/* FALLTHROUGH */
default:
- if (++badcommands > MAXBADCOMMANDS)
+#if MAXBADCOMMANDS > 0
+ /* theoretically this could overflow */
+ if (nullserver != NULL &&
+ ++n_badcmds > MAXBADCOMMANDS)
{
- delay *= 2;
- if (delay >= MAXTIMEOUT)
- delay = MAXTIMEOUT;
- (void) sleep(delay);
+ message("421 4.7.0 %s Too many bad commands; closing connection",
+ MyHostName);
+
+ /* arrange to ignore send list */
+ e->e_sendqueue = NULL;
+ goto doquit;
}
+#endif /* MAXBADCOMMANDS > 0 */
if (nullserver != NULL)
{
if (ISSMTPREPLY(nullserver))
usrerr(nullserver);
else
- usrerr("550 5.0.0 %s", nullserver);
+ usrerr("550 5.0.0 %s",
+ nullserver);
}
else
usrerr("452 4.4.5 Insufficient disk space; try again later");
@@ -791,21 +1130,12 @@ smtp(nullserver, d_flags, e)
}
}
- /* non-null server */
- switch (c->cmd_code)
- {
- case CMDMAIL:
- case CMDEXPN:
- case CMDVRFY:
- case CMDETRN:
- lognullconnection = FALSE;
- }
-
switch (c->cmd_code)
{
-# if SASL
+#if SASL
case CMDAUTH: /* sasl */
- if (!sasl_ok)
+ DELAY_CONN("AUTH");
+ if (!sasl_ok || n_mechs <= 0)
{
message("503 5.3.3 AUTH not available");
break;
@@ -815,7 +1145,7 @@ smtp(nullserver, d_flags, e)
message("503 5.5.0 Already Authenticated");
break;
}
- if (gotmail)
+ if (smtp.sm_gotmail)
{
message("503 5.5.0 AUTH not permitted during a mail transaction");
break;
@@ -830,13 +1160,13 @@ smtp(nullserver, d_flags, e)
break;
}
- ismore = FALSE;
+ ismore = false;
/* crude way to avoid crack attempts */
- (void) checksmtpattack(&n_auth, n_mechs + 1, TRUE,
+ (void) checksmtpattack(&n_auth, n_mechs + 1, true,
"AUTH", e);
- /* make sure it's a valid string */
+ /* make sure mechanism (p) is a valid string */
for (q = p; *q != '\0' && isascii(*q); q++)
{
if (isspace(*q))
@@ -854,7 +1184,7 @@ smtp(nullserver, d_flags, e)
/* check whether mechanism is available */
if (iteminlist(p, mechlist, " ") == NULL)
{
- message("503 5.3.3 AUTH mechanism %s not available",
+ message("503 5.3.3 AUTH mechanism %.32s not available",
p);
break;
}
@@ -862,16 +1192,16 @@ smtp(nullserver, d_flags, e)
if (ismore)
{
/* could this be shorter? XXX */
- in = xalloc(strlen(q));
+ in = sm_rpool_malloc(e->e_rpool, strlen(q));
result = sasl_decode64(q, strlen(q), in,
- (u_int *)&inlen);
+ &inlen);
if (result != SASL_OK)
{
message("501 5.5.4 cannot BASE64 decode '%s'",
q);
if (LogLevel > 5)
sm_syslog(LOG_WARNING, e->e_id,
- "SASL decode64 error [%d for \"%s\"]",
+ "AUTH decode64 error [%d for \"%s\"]",
result, q);
/* start over? */
authenticating = SASL_NOT_AUTH;
@@ -879,23 +1209,6 @@ smtp(nullserver, d_flags, e)
inlen = 0;
break;
}
-# if 0
- if (tTd(95, 99))
- {
- int i;
-
- dprintf("AUTH: more \"");
- for (i = 0; i < inlen; i++)
- {
- if (isascii(in[i]) &&
- isprint(in[i]))
- dprintf("%c", in[i]);
- else
- dprintf("_");
- }
- dprintf("\"\n");
- }
-# endif /* 0 */
}
else
{
@@ -912,11 +1225,12 @@ smtp(nullserver, d_flags, e)
message("500 5.7.0 authentication failed");
if (LogLevel > 9)
sm_syslog(LOG_ERR, e->e_id,
- "AUTH failure (%s): %s (%d)",
+ "AUTH failure (%s): %s (%d) %s",
p,
sasl_errstring(result, NULL,
NULL),
- result);
+ result,
+ errstr);
break;
}
auth_type = newstr(p);
@@ -932,14 +1246,14 @@ smtp(nullserver, d_flags, e)
len = ENC64LEN(outlen);
out2 = xalloc(len);
result = sasl_encode64(out, outlen, out2, len,
- (u_int *)&out2len);
+ &out2len);
if (result != SASL_OK)
{
message("454 4.5.4 Temporary authentication failure");
if (LogLevel > 5)
sm_syslog(LOG_WARNING, e->e_id,
- "SASL encode64 error [%d for \"%s\"]",
+ "AUTH encode64 error [%d for \"%s\"]",
result, out);
/* start over? */
@@ -950,18 +1264,18 @@ smtp(nullserver, d_flags, e)
message("334 %s", out2);
authenticating = SASL_PROC_AUTH;
}
-
break;
-# endif /* SASL */
+#endif /* SASL */
-# if STARTTLS
+#if STARTTLS
case CMDSTLS: /* starttls */
+ DELAY_CONN("STARTTLS");
if (*p != '\0')
{
message("501 5.5.2 Syntax error (no parameters allowed)");
break;
}
- if (!usetls)
+ if (!bitset(SRV_OFFER_TLS, features))
{
message("503 5.5.0 TLS not available");
break;
@@ -971,7 +1285,7 @@ smtp(nullserver, d_flags, e)
message("454 4.3.3 TLS not available after start");
break;
}
- if (gotmail)
+ if (smtp.sm_gotmail)
{
message("503 5.5.0 TLS not permitted during a mail transaction");
break;
@@ -985,32 +1299,51 @@ smtp(nullserver, d_flags, e)
usrerr("454 4.7.1 Please try again later");
break;
}
+# if _FFR_SMTP_SSL
+ starttls:
+# endif /* _FFR_SMTP_SSL */
# if TLS_NO_RSA
/*
** XXX do we need a temp key ?
*/
# else /* TLS_NO_RSA */
- if (SSL_CTX_need_tmp_RSA(srv_ctx) &&
- !SSL_CTX_set_tmp_rsa(srv_ctx,
- (rsa = RSA_generate_key(RSA_KEYLENGTH, RSA_F4,
- NULL, NULL)))
- )
- {
- message("454 4.3.3 TLS not available: error generating RSA temp key");
- if (rsa != NULL)
- RSA_free(rsa);
- break;
- }
# endif /* TLS_NO_RSA */
+
+# if TLS_VRFY_PER_CTX
+ /*
+ ** Note: this sets the verification globally
+ ** (per SSL_CTX)
+ ** it's ok since it applies only to one transaction
+ */
+
+ TLS_VERIFY_CLIENT();
+# endif /* TLS_VRFY_PER_CTX */
+
if (srv_ssl != NULL)
SSL_clear(srv_ssl);
else if ((srv_ssl = SSL_new(srv_ctx)) == NULL)
{
message("454 4.3.3 TLS not available: error generating SSL handle");
+# if _FFR_SMTP_SSL
+ goto tls_done;
+# else /* _FFR_SMTP_SSL */
break;
+# endif /* _FFR_SMTP_SSL */
}
- rfd = fileno(InChannel);
- wfd = fileno(OutChannel);
+
+# if !TLS_VRFY_PER_CTX
+ /*
+ ** this could be used if it were possible to set
+ ** verification per SSL (connection)
+ ** not just per SSL_CTX (global)
+ */
+
+ TLS_VERIFY_CLIENT();
+# endif /* !TLS_VRFY_PER_CTX */
+
+ rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
+ wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
+
if (rfd < 0 || wfd < 0 ||
SSL_set_rfd(srv_ssl, rfd) <= 0 ||
SSL_set_wfd(srv_ssl, wfd) <= 0)
@@ -1018,27 +1351,97 @@ smtp(nullserver, d_flags, e)
message("454 4.3.3 TLS not available: error set fd");
SSL_free(srv_ssl);
srv_ssl = NULL;
+# if _FFR_SMTP_SSL
+ goto tls_done;
+# else /* _FFR_SMTP_SSL */
break;
+# endif /* _FFR_SMTP_SSL */
}
- message("220 2.0.0 Ready to start TLS");
+# if _FFR_SMTP_SSL
+ if (!smtps)
+# endif /* _FFR_SMTP_SSL */
+ message("220 2.0.0 Ready to start TLS");
+# if PIPELINING
+ (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
+# endif /* PIPELINING */
+
SSL_set_accept_state(srv_ssl);
# define SSL_ACC(s) SSL_accept(s)
+
+ tlsstart = curtime();
+ ssl_retry:
if ((r = SSL_ACC(srv_ssl)) <= 0)
{
int i;
+ bool timedout;
+ time_t left;
+ time_t now = curtime();
+ struct timeval tv;
/* what to do in this case? */
i = SSL_get_error(srv_ssl, r);
+
+ /*
+ ** For SSL_ERROR_WANT_{READ,WRITE}:
+ ** There is no SSL record available yet
+ ** or there is only a partial SSL record
+ ** removed from the network (socket) buffer
+ ** into the SSL buffer. The SSL_accept will
+ ** only succeed when a full SSL record is
+ ** available (assuming a "real" error
+ ** doesn't happen). To handle when a "real"
+ ** error does happen the select is set for
+ ** exceptions too.
+ ** The connection may be re-negotiated
+ ** during this time so both read and write
+ ** "want errors" need to be handled.
+ ** A select() exception loops back so that
+ ** a proper SSL error message can be gotten.
+ */
+
+ left = TimeOuts.to_starttls - (now - tlsstart);
+ timedout = left <= 0;
+ if (!timedout)
+ {
+ tv.tv_sec = left;
+ tv.tv_usec = 0;
+ }
+
+ /* XXX what about SSL_pending() ? */
+ if (!timedout && i == SSL_ERROR_WANT_READ)
+ {
+ fd_set ssl_maskr, ssl_maskx;
+
+ FD_ZERO(&ssl_maskr);
+ FD_SET(rfd, &ssl_maskr);
+ FD_ZERO(&ssl_maskx);
+ FD_SET(rfd, &ssl_maskx);
+ if (select(rfd + 1, &ssl_maskr, NULL,
+ &ssl_maskx, &tv) > 0)
+ goto ssl_retry;
+ }
+ if (!timedout && i == SSL_ERROR_WANT_WRITE)
+ {
+ fd_set ssl_maskw, ssl_maskx;
+
+ FD_ZERO(&ssl_maskw);
+ FD_SET(wfd, &ssl_maskw);
+ FD_ZERO(&ssl_maskx);
+ FD_SET(rfd, &ssl_maskx);
+ if (select(wfd + 1, NULL, &ssl_maskw,
+ &ssl_maskx, &tv) > 0)
+ goto ssl_retry;
+ }
if (LogLevel > 5)
{
- sm_syslog(LOG_WARNING, e->e_id,
- "TLS: error: accept failed=%d (%d)",
- r, i);
- if (LogLevel > 9)
- tlslogerr();
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS=server, error: accept failed=%d, SSL_error=%d, timedout=%d",
+ r, i, (int) timedout);
+ if (LogLevel > 8)
+ tlslogerr("server");
}
- tls_ok_srv = FALSE;
+ tls_ok_srv = false;
SSL_free(srv_ssl);
srv_ssl = NULL;
@@ -1053,8 +1456,10 @@ smtp(nullserver, d_flags, e)
}
/* ignore return code for now, it's in {verify} */
- (void) tls_get_info(srv_ssl, &BlankEnvelope, TRUE,
- CurSmtpClient, TRUE);
+ (void) tls_get_info(srv_ssl, true,
+ CurSmtpClient,
+ &BlankEnvelope.e_macro,
+ bitset(SRV_VRFY_CLT, features));
/*
** call Stls_client to find out whether
@@ -1063,12 +1468,13 @@ smtp(nullserver, d_flags, e)
saveQuickAbort = QuickAbort;
saveSuprErrs = SuprErrs;
- SuprErrs = TRUE;
- QuickAbort = FALSE;
+ SuprErrs = true;
+ QuickAbort = false;
if (rscheck("tls_client",
- macvalue(macid("{verify}", NULL), e),
- "STARTTLS", e, TRUE, TRUE, 6, NULL) !=
- EX_OK || Errors > 0)
+ macvalue(macid("{verify}"), e),
+ "STARTTLS", e, true, true, 5,
+ NULL, NOQID) != EX_OK ||
+ Errors > 0)
{
extern char MsgBuf[];
@@ -1080,48 +1486,36 @@ smtp(nullserver, d_flags, e)
QuickAbort = saveQuickAbort;
SuprErrs = saveSuprErrs;
- tls_ok_srv = FALSE; /* don't offer STARTTLS again */
- gothello = FALSE; /* discard info */
+ tls_ok_srv = false; /* don't offer STARTTLS again */
n_helo = 0;
- OneXact = TRUE; /* only one xaction this run */
-# if SASL
+# if SASL
if (sasl_ok)
{
char *s;
- if ((s = macvalue(macid("{cipher_bits}", NULL), e)) != NULL &&
- (ext_ssf.ssf = atoi(s)) > 0)
+ s = macvalue(macid("{cipher_bits}"), e);
+ if (s != NULL && (ext_ssf.ssf = atoi(s)) > 0)
{
-# if _FFR_EXT_MECH
- ext_ssf.auth_id = macvalue(macid("{cert_subject}",
- NULL),
+ ext_ssf.auth_id = macvalue(macid("{cert_subject}"),
e);
-# endif /* _FFR_EXT_MECH */
sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
&ext_ssf) == SASL_OK;
- if (mechlist != NULL)
- sm_free(mechlist);
mechlist = NULL;
if (sasl_ok)
- {
n_mechs = saslmechs(conn,
&mechlist);
- sasl_ok = n_mechs > 0;
- }
}
}
-# endif /* SASL */
+# endif /* SASL */
/* switch to secure connection */
-#if SFIO
- r = sfdctls(InChannel, OutChannel, srv_ssl);
-#else /* SFIO */
-# if _FFR_TLS_TOREK
- r = sfdctls(&InChannel, &OutChannel, srv_ssl);
-# endif /* _FFR_TLS_TOREK */
-#endif /* SFIO */
- if (r == 0)
- tls_active = TRUE;
+ if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0)
+ {
+ tls_active = true;
+# if PIPELINING
+ (void) sm_io_autoflush(InChannel, OutChannel);
+# endif /* PIPELINING */
+ }
else
{
/*
@@ -1132,14 +1526,26 @@ smtp(nullserver, d_flags, e)
** encrypted layer, but we could not...
** just "hang up"?
*/
+
nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer";
- syserr("TLS: can't switch to encrypted layer");
+ syserr("STARTTLS: can't switch to encrypted layer");
}
+# if _FFR_SMTP_SSL
+ tls_done:
+ if (smtps)
+ {
+ if (tls_active)
+ goto greeting;
+ else
+ goto doquit;
+ }
+# endif /* _FFR_SMTP_SSL */
break;
-# endif /* STARTTLS */
+#endif /* STARTTLS */
case CMDHELO: /* hello -- introduce yourself */
case CMDEHLO: /* extended hello */
+ DELAY_CONN("EHLO");
if (c->cmd_code == CMDEHLO)
{
protocol = "ESMTP";
@@ -1152,9 +1558,11 @@ smtp(nullserver, d_flags, e)
}
/* avoid denial-of-service */
- (void) checksmtpattack(&n_helo, MAXHELOCOMMANDS, TRUE,
+ (void) checksmtpattack(&n_helo, MAXHELOCOMMANDS, true,
"HELO/EHLO", e);
+#if 0
+ /* RFC2821 4.1.4 allows duplicate HELO/EHLO */
/* check for duplicate HELO/EHLO per RFC 1651 4.2 */
if (gothello)
{
@@ -1162,6 +1570,7 @@ smtp(nullserver, d_flags, e)
MyHostName);
break;
}
+#endif /* 0 */
/* check for valid domain name (re 1123 5.2.5) */
if (*p == '\0' && !AllowBogusHELO)
@@ -1200,7 +1609,7 @@ smtp(nullserver, d_flags, e)
if (*q == '\0')
{
q = "pleased to meet you";
- sendinghost = newstr(p);
+ sendinghost = sm_strdup_x(p);
}
else if (!AllowBogusHELO)
{
@@ -1216,10 +1625,32 @@ smtp(nullserver, d_flags, e)
q = "accepting invalid domain name";
}
- gothello = TRUE;
+ if (gothello)
+ {
+ CLEAR_STATE(cmdbuf);
-# if _FFR_MILTER
- if (milterize && !bitset(EF_DISCARD, e->e_flags))
+#if _FFR_QUARANTINE
+ /* restore connection quarantining */
+ if (smtp.sm_quarmsg == NULL)
+ {
+ e->e_quarmsg = NULL;
+ macdefine(&e->e_macro, A_PERM,
+ macid("{quarantine}"), "");
+ }
+ else
+ {
+ e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
+ smtp.sm_quarmsg);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{quarantine}"),
+ e->e_quarmsg);
+ }
+#endif /* _FFR_QUARANTINE */
+ }
+
+#if MILTER
+ if (smtp.sm_milterlist && smtp.sm_milterize &&
+ !bitset(EF_DISCARD, e->e_flags))
{
char state;
char *response;
@@ -1228,22 +1659,48 @@ smtp(nullserver, d_flags, e)
switch (state)
{
case SMFIR_REPLYCODE:
- nullserver = response;
- milterize = FALSE;
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: helo=%s, reject=%s",
+ p, response);
+ nullserver = newstr(response);
+ smtp.sm_milterize = false;
break;
case SMFIR_REJECT:
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: helo=%s, reject=Command rejected",
+ p);
nullserver = "Command rejected";
- milterize = FALSE;
+ smtp.sm_milterize = false;
break;
case SMFIR_TEMPFAIL:
- tempfail = TRUE;
- milterize = FALSE;
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: helo=%s, reject=%s",
+ p, MSG_TEMPFAIL);
+ tempfail = true;
+ smtp.sm_milterize = false;
break;
}
+ if (response != NULL)
+ sm_free(response);
+
+#if _FFR_QUARANTINE
+ /*
+ ** If quarantining by a connect/ehlo action,
+ ** save between messages
+ */
+
+ if (smtp.sm_quarmsg == NULL &&
+ e->e_quarmsg != NULL)
+ smtp.sm_quarmsg = newstr(e->e_quarmsg);
+#endif /* _FFR_QUARANTINE */
}
-# endif /* _FFR_MILTER */
+#endif /* MILTER */
+ gothello = true;
/* print HELO response message */
if (c->cmd_code != CMDEHLO)
@@ -1267,48 +1724,56 @@ smtp(nullserver, d_flags, e)
** print EHLO features list
**
** Note: If you change this list,
- ** remember to update 'helpfile'
+ ** remember to update 'helpfile'
*/
message("250-ENHANCEDSTATUSCODES");
- if (!bitset(PRIV_NOEXPN, PrivacyFlags))
+#if PIPELINING
+ if (bitset(SRV_OFFER_PIPE, features))
+ message("250-PIPELINING");
+#endif /* PIPELINING */
+ if (bitset(SRV_OFFER_EXPN, features))
{
message("250-EXPN");
- if (!bitset(PRIV_NOVERB, PrivacyFlags))
+ if (bitset(SRV_OFFER_VERB, features))
message("250-VERB");
}
-# if MIME8TO7
+#if MIME8TO7
message("250-8BITMIME");
-# endif /* MIME8TO7 */
+#endif /* MIME8TO7 */
if (MaxMessageSize > 0)
message("250-SIZE %ld", MaxMessageSize);
else
message("250-SIZE");
-# if DSN
- if (SendMIMEErrors &&
- !bitset(PRIV_NORECEIPTS, PrivacyFlags))
+#if DSN
+ if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features))
message("250-DSN");
-# endif /* DSN */
- message("250-ONEX");
- if (!bitset(PRIV_NOETRN, PrivacyFlags) &&
- !bitnset(D_NOETRN, d_flags))
+#endif /* DSN */
+ if (bitset(SRV_OFFER_ETRN, features))
message("250-ETRN");
- message("250-XUSR");
-
-# if SASL
+#if SASL
if (sasl_ok && mechlist != NULL && *mechlist != '\0')
message("250-AUTH %s", mechlist);
-# endif /* SASL */
-# if STARTTLS
- if (tls_ok_srv && usetls)
+#endif /* SASL */
+#if STARTTLS
+ if (tls_ok_srv && bitset(SRV_OFFER_TLS, features))
message("250-STARTTLS");
-# endif /* STARTTLS */
+#endif /* STARTTLS */
+ if (DeliverByMin > 0)
+ message("250-DELIVERBY %ld",
+ (long) DeliverByMin);
+ else if (DeliverByMin == 0)
+ message("250-DELIVERBY");
+
+ /* < 0: no deliver-by */
+
message("250 HELP");
break;
case CMDMAIL: /* mail -- designate sender */
SmtpPhase = "server MAIL";
+ DELAY_CONN("MAIL");
/* check for validity of this command */
if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
@@ -1316,25 +1781,19 @@ smtp(nullserver, d_flags, e)
usrerr("503 5.0.0 Polite people say HELO first");
break;
}
- if (gotmail)
+ if (smtp.sm_gotmail)
{
usrerr("503 5.5.0 Sender already specified");
break;
}
- if (InChild)
- {
- errno = 0;
- syserr("503 5.5.0 Nested MAIL command: MAIL %s", p);
- finis(TRUE, ExitStat);
- }
-# if SASL
- if (bitnset(D_AUTHREQ, d_flags) &&
+#if SASL
+ if (bitset(SRV_REQ_AUTH, features) &&
authenticating != SASL_IS_AUTH)
{
usrerr("530 5.7.0 Authentication required");
break;
}
-# endif /* SASL */
+#endif /* SASL */
p = skipword(p, "from");
if (p == NULL)
@@ -1345,7 +1804,7 @@ smtp(nullserver, d_flags, e)
sm_syslog(LOG_INFO, e->e_id,
"SMTP MAIL command (%.100s) from %.100s tempfailed (due to previous checks)",
p, CurSmtpClient);
- usrerr("451 4.7.1 Please try again later");
+ usrerr(MSG_TEMPFAIL);
break;
}
@@ -1354,103 +1813,88 @@ smtp(nullserver, d_flags, e)
sendinghost = peerhostname;
- /* fork a subprocess to process this command */
- ric = runinchild("SMTP-MAIL", e);
-
- /* Catch a problem and stop processing */
- if (ric == RIC_TEMPFAIL && nullserver == NULL)
- nullserver = "452 4.3.0 Internal software error";
- if (ric != RIC_INCHILD)
- break;
+#if SM_HEAP_CHECK
+ if (sm_debug_active(&DebugLeakSmtp, 1))
+ {
+ sm_heap_newgroup();
+ sm_dprintf("smtp() heap group #%d\n",
+ sm_heap_group());
+ }
+#endif /* SM_HEAP_CHECK */
if (Errors > 0)
- goto undo_subproc_no_pm;
+ goto undo_no_pm;
if (!gothello)
{
- auth_warning(e,
- "%s didn't use HELO protocol",
- CurSmtpClient);
+ auth_warning(e, "%s didn't use HELO protocol",
+ CurSmtpClient);
}
-# ifdef PICKY_HELO_CHECK
- if (strcasecmp(sendinghost, peerhostname) != 0 &&
- (strcasecmp(peerhostname, "localhost") != 0 ||
- strcasecmp(sendinghost, MyHostName) != 0))
+#ifdef PICKY_HELO_CHECK
+ if (sm_strcasecmp(sendinghost, peerhostname) != 0 &&
+ (sm_strcasecmp(peerhostname, "localhost") != 0 ||
+ sm_strcasecmp(sendinghost, MyHostName) != 0))
{
auth_warning(e, "Host %s claimed to be %s",
- CurSmtpClient, sendinghost);
+ CurSmtpClient, sendinghost);
}
-# endif /* PICKY_HELO_CHECK */
+#endif /* PICKY_HELO_CHECK */
if (protocol == NULL)
protocol = "SMTP";
- define('r', protocol, e);
- define('s', sendinghost, e);
+ macdefine(&e->e_macro, A_PERM, 'r', protocol);
+ macdefine(&e->e_macro, A_PERM, 's', sendinghost);
if (Errors > 0)
- goto undo_subproc_no_pm;
- nrcpts = 0;
- define(macid("{ntries}", NULL), "0", e);
+ goto undo_no_pm;
+ smtp.sm_nrcpts = 0;
+ n_badrcpts = 0;
+ macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0");
+ macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0");
e->e_flags |= EF_CLRQUEUE;
- sm_setproctitle(TRUE, e, "%s %s: %.80s",
+ sm_setproctitle(true, e, "%s %s: %.80s",
qid_printname(e),
CurSmtpClient, inp);
- /* child -- go do the processing */
- if (setjmp(TopFrame) > 0)
- {
- /* this failed -- undo work */
- undo_subproc_no_pm:
- e->e_flags &= ~EF_PM_NOTIFY;
- undo_subproc:
- if (InChild)
- {
- QuickAbort = FALSE;
- SuprErrs = TRUE;
- e->e_flags &= ~EF_FATALERRS;
-
- if (LogLevel > 4 &&
- bitset(EF_LOGSENDER, e->e_flags))
- logsender(e, NULL);
- e->e_flags &= ~EF_LOGSENDER;
-
- finis(TRUE, ExitStat);
- }
- break;
- }
- QuickAbort = TRUE;
+ /* do the processing */
+ SM_TRY
+ {
+ QuickAbort = true;
/* must parse sender first */
delimptr = NULL;
- setsender(p, e, &delimptr, ' ', FALSE);
+ setsender(p, e, &delimptr, ' ', false);
if (delimptr != NULL && *delimptr != '\0')
*delimptr++ = '\0';
if (Errors > 0)
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
/* Successfully set e_from, allow logging */
e->e_flags |= EF_LOGSENDER;
/* put resulting triple from parseaddr() into macros */
if (e->e_from.q_mailer != NULL)
- define(macid("{mail_mailer}", NULL),
- e->e_from.q_mailer->m_name, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{mail_mailer}"),
+ e->e_from.q_mailer->m_name);
else
- define(macid("{mail_mailer}", NULL),
- NULL, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{mail_mailer}"), NULL);
if (e->e_from.q_host != NULL)
- define(macid("{mail_host}", NULL),
- e->e_from.q_host, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{mail_host}"),
+ e->e_from.q_host);
else
- define(macid("{mail_host}", NULL),
- "localhost", e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{mail_host}"), "localhost");
if (e->e_from.q_user != NULL)
- define(macid("{mail_addr}", NULL),
- e->e_from.q_user, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{mail_addr}"),
+ e->e_from.q_user);
else
- define(macid("{mail_addr}", NULL),
- NULL, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{mail_addr}"), NULL);
if (Errors > 0)
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
/* check for possible spoofing */
if (RealUid != 0 && OpMode == MD_SMTP &&
@@ -1476,8 +1920,7 @@ smtp(nullserver, d_flags, e)
char *equal = NULL;
/* locate the beginning of the keyword */
- while (isascii(*p) && isspace(*p))
- p++;
+ SKIP_SPACE(p);
if (*p == '\0')
break;
kp = p;
@@ -1502,7 +1945,7 @@ smtp(nullserver, d_flags, e)
*p++ = '\0';
if (tTd(19, 1))
- dprintf("MAIL: got arg %s=\"%s\"\n", kp,
+ sm_dprintf("MAIL: got arg %s=\"%s\"\n", kp,
vp == NULL ? "<null>" : vp);
mail_esmtp_args(kp, vp, e);
@@ -1512,17 +1955,47 @@ smtp(nullserver, d_flags, e)
if (argno >= MAXSMTPARGS - 1)
usrerr("501 5.5.4 Too many parameters");
if (Errors > 0)
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
}
args[argno] = NULL;
if (Errors > 0)
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
+
+#if SASL
+# if _FFR_AUTH_PASSING
+ /* set the default AUTH= if the sender didn't */
+ if (e->e_auth_param == NULL)
+ {
+ /* XXX only do this for an MSA? */
+ e->e_auth_param = macvalue(macid("{auth_authen}"),
+ e);
+ if (e->e_auth_param == NULL)
+ e->e_auth_param = "<>";
+
+ /*
+ ** XXX should we invoke Strust_auth now?
+ ** authorizing as the client that just
+ ** authenticated, so we'll trust implicitly
+ */
+ }
+# endif /* _FFR_AUTH_PASSING */
+#endif /* SASL */
/* do config file checking of the sender */
+ macdefine(&e->e_macro, A_PERM,
+ macid("{addr_type}"), "e s");
+#if _FFR_MAIL_MACRO
+ /* make the "real" sender address available */
+ macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"),
+ e->e_from.q_paddr);
+#endif /* _FFR_MAIL_MACRO */
if (rscheck("check_mail", addr,
- NULL, e, TRUE, TRUE, 4, NULL) != EX_OK ||
+ NULL, e, true, true, 3, NULL,
+ e->e_id) != EX_OK ||
Errors > 0)
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{addr_type}"), NULL);
if (MaxMessageSize > 0 &&
(e->e_msgsize > MaxMessageSize ||
@@ -1530,80 +2003,97 @@ smtp(nullserver, d_flags, e)
{
usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
MaxMessageSize);
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
}
- if (!enoughdiskspace(e->e_msgsize, TRUE))
+ /*
+ ** XXX always check whether there is at least one fs
+ ** with enough space?
+ ** However, this may not help much: the queue group
+ ** selection may later on select a FS that hasn't
+ ** enough space.
+ */
+
+ if ((NumFileSys == 1 || NumQueue == 1) &&
+ !enoughdiskspace(e->e_msgsize, e)
+#if _FFR_ANY_FREE_FS
+ && !filesys_free(e->e_msgsize)
+#endif /* _FFR_ANY_FREE_FS */
+ )
{
+ /*
+ ** We perform this test again when the
+ ** queue directory is selected, in collect.
+ */
+
usrerr("452 4.4.5 Insufficient disk space; try again later");
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
}
if (Errors > 0)
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-# if _FFR_MILTER
- LogUsrErrs = TRUE;
- if (milterize && !bitset(EF_DISCARD, e->e_flags))
+ LogUsrErrs = true;
+#if MILTER
+ if (smtp.sm_milterlist && smtp.sm_milterize &&
+ !bitset(EF_DISCARD, e->e_flags))
{
char state;
char *response;
response = milter_envfrom(args, e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE:
- usrerr(response);
- break;
-
- case SMFIR_REJECT:
- usrerr("550 5.7.1 Command rejected");
- break;
-
- case SMFIR_DISCARD:
- e->e_flags |= EF_DISCARD;
- break;
-
- case SMFIR_TEMPFAIL:
- usrerr("451 4.7.1 Please try again later");
- break;
- }
- if (response != NULL)
- sm_free(response);
+ MILTER_REPLY("from");
}
-# endif /* _FFR_MILTER */
+#endif /* MILTER */
if (Errors > 0)
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
message("250 2.1.0 Sender ok");
- gotmail = TRUE;
+ smtp.sm_gotmail = true;
+ }
+ SM_EXCEPT(exc, "[!F]*")
+ {
+ /*
+ ** An error occurred while processing a MAIL command.
+ ** Jump to the common error handling code.
+ */
+
+ sm_exc_free(exc);
+ goto undo_no_pm;
+ }
+ SM_END_TRY
+ break;
+
+ undo_no_pm:
+ e->e_flags &= ~EF_PM_NOTIFY;
+ undo:
break;
case CMDRCPT: /* rcpt -- designate recipient */
- if (!gotmail)
+ DELAY_CONN("RCPT");
+ if (!smtp.sm_gotmail)
{
usrerr("503 5.0.0 Need MAIL before RCPT");
break;
}
SmtpPhase = "server RCPT";
- if (setjmp(TopFrame) > 0)
- {
- e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
- break;
- }
- QuickAbort = TRUE;
- LogUsrErrs = TRUE;
+ SM_TRY
+ {
+ QuickAbort = true;
+ LogUsrErrs = true;
/* limit flooding of our machine */
- if (MaxRcptPerMsg > 0 && nrcpts >= MaxRcptPerMsg)
+ if (MaxRcptPerMsg > 0 &&
+ smtp.sm_nrcpts >= MaxRcptPerMsg)
{
+ /* sleep(1); / * slow down? */
usrerr("452 4.5.3 Too many recipients");
- break;
+ goto rcpt_done;
}
if (e->e_sendmode != SM_DELIVER)
e->e_flags |= EF_VRFYONLY;
-# if _FFR_MILTER
+#if MILTER
/*
** If the filter will be deleting recipients,
** don't expand them at RCPT time (in the call
@@ -1615,24 +2105,46 @@ smtp(nullserver, d_flags, e)
if (milter_can_delrcpts())
e->e_flags |= EF_VRFYONLY;
-# endif /* _FFR_MILTER */
+#endif /* MILTER */
p = skipword(p, "to");
if (p == NULL)
- break;
-# if _FFR_ADDR_TYPE
- define(macid("{addr_type}", NULL), "e r", e);
-# endif /* _FFR_ADDR_TYPE */
- a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
-#if _FFR_ADDR_TYPE
- define(macid("{addr_type}", NULL), NULL, e);
-#endif /* _FFR_ADDR_TYPE */
+ goto rcpt_done;
+ macdefine(&e->e_macro, A_PERM,
+ macid("{addr_type}"), "e r");
+ a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr,
+ e, true);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{addr_type}"), NULL);
+ if (BadRcptThrottle > 0 &&
+ n_badrcpts >= BadRcptThrottle)
+ {
+ if (LogLevel > 5 &&
+ n_badrcpts == BadRcptThrottle)
+ {
+ sm_syslog(LOG_INFO, e->e_id,
+ "%.100s: Possible SMTP RCPT flood, throttling.",
+ CurSmtpClient);
+
+ /* To avoid duplicated message */
+ n_badrcpts++;
+ }
+
+ /*
+ ** Don't use exponential backoff for now.
+ ** Some servers will open more connections
+ ** and actually overload the receiver even
+ ** more.
+ */
+
+ (void) sleep(1);
+ }
if (Errors > 0)
- break;
+ goto rcpt_done;
if (a == NULL)
{
usrerr("501 5.0.0 Missing recipient");
- break;
+ goto rcpt_done;
}
if (delimptr != NULL && *delimptr != '\0')
@@ -1640,25 +2152,26 @@ smtp(nullserver, d_flags, e)
/* put resulting triple from parseaddr() into macros */
if (a->q_mailer != NULL)
- define(macid("{rcpt_mailer}", NULL),
- a->q_mailer->m_name, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_mailer}"),
+ a->q_mailer->m_name);
else
- define(macid("{rcpt_mailer}", NULL),
- NULL, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_mailer}"), NULL);
if (a->q_host != NULL)
- define(macid("{rcpt_host}", NULL),
- a->q_host, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_host}"), a->q_host);
else
- define(macid("{rcpt_host}", NULL),
- "localhost", e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_host}"), "localhost");
if (a->q_user != NULL)
- define(macid("{rcpt_addr}", NULL),
- a->q_user, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_addr}"), a->q_user);
else
- define(macid("{rcpt_addr}", NULL),
- NULL, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_addr}"), NULL);
if (Errors > 0)
- break;
+ goto rcpt_done;
/* now parse ESMTP arguments */
addr = p;
@@ -1672,8 +2185,7 @@ smtp(nullserver, d_flags, e)
char *equal = NULL;
/* locate the beginning of the keyword */
- while (isascii(*p) && isspace(*p))
- p++;
+ SKIP_SPACE(p);
if (*p == '\0')
break;
kp = p;
@@ -1698,7 +2210,7 @@ smtp(nullserver, d_flags, e)
*p++ = '\0';
if (tTd(19, 1))
- dprintf("RCPT: got arg %s=\"%s\"\n", kp,
+ sm_dprintf("RCPT: got arg %s=\"%s\"\n", kp,
vp == NULL ? "<null>" : vp);
rcpt_esmtp_args(a, kp, vp, e);
@@ -1712,325 +2224,126 @@ smtp(nullserver, d_flags, e)
}
args[argno] = NULL;
if (Errors > 0)
- break;
+ goto rcpt_done;
/* do config file checking of the recipient */
+ macdefine(&e->e_macro, A_PERM,
+ macid("{addr_type}"), "e r");
if (rscheck("check_rcpt", addr,
- NULL, e, TRUE, TRUE, 4, NULL) != EX_OK ||
+ NULL, e, true, true, 3, NULL,
+ e->e_id) != EX_OK ||
Errors > 0)
- break;
+ goto rcpt_done;
+ macdefine(&e->e_macro, A_PERM,
+ macid("{addr_type}"), NULL);
-# if _FFR_MILTER
- if (milterize && !bitset(EF_DISCARD, e->e_flags))
+#if MILTER
+ if (smtp.sm_milterlist && smtp.sm_milterize &&
+ !bitset(EF_DISCARD, e->e_flags))
{
char state;
char *response;
response = milter_envrcpt(args, e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE:
- usrerr(response);
- break;
-
- case SMFIR_REJECT:
- usrerr("550 5.7.1 Command rejected");
- break;
-
- case SMFIR_DISCARD:
- e->e_flags |= EF_DISCARD;
- break;
-
- case SMFIR_TEMPFAIL:
- usrerr("451 4.7.1 Please try again later");
- break;
- }
- if (response != NULL)
- sm_free(response);
- }
-# endif /* _FFR_MILTER */
-
- define(macid("{rcpt_mailer}", NULL), NULL, e);
- define(macid("{rcpt_relay}", NULL), NULL, e);
- define(macid("{rcpt_addr}", NULL), NULL, e);
- define(macid("{dsn_notify}", NULL), NULL, e);
+ MILTER_REPLY("to");
+ }
+#endif /* MILTER */
+
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_mailer}"), NULL);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_relay}"), NULL);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_addr}"), NULL);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{dsn_notify}"), NULL);
if (Errors > 0)
- break;
+ goto rcpt_done;
/* save in recipient list after ESMTP mods */
a = recipient(a, &e->e_sendqueue, 0, e);
if (Errors > 0)
- break;
+ goto rcpt_done;
/* no errors during parsing, but might be a duplicate */
e->e_to = a->q_paddr;
if (!QS_IS_BADADDR(a->q_state))
{
- if (e->e_queuedir == NOQDIR)
+ if (smtp.sm_nrcpts == 0)
initsys(e);
message("250 2.1.5 Recipient ok%s",
QS_IS_QUEUEUP(a->q_state) ?
" (will queue)" : "");
- nrcpts++;
+ smtp.sm_nrcpts++;
}
else
{
/* punt -- should keep message in ADDRESS.... */
usrerr("550 5.1.1 Addressee unknown");
}
+ rcpt_done:
+ if (Errors > 0)
+ ++n_badrcpts;
+ }
+ SM_EXCEPT(exc, "[!F]*")
+ {
+ /* An exception occurred while processing RCPT */
+ e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
+ ++n_badrcpts;
+ }
+ SM_END_TRY
break;
case CMDDATA: /* data -- text of mail */
- SmtpPhase = "server DATA";
- if (!gotmail)
- {
- usrerr("503 5.0.0 Need MAIL command");
- break;
- }
- else if (nrcpts <= 0)
- {
- usrerr("503 5.0.0 Need RCPT (recipient)");
- break;
- }
-
- /* put back discard bit */
- if (discard)
- e->e_flags |= EF_DISCARD;
-
- /* check to see if we need to re-expand aliases */
- /* also reset QS_BADADDR on already-diagnosted addrs */
- doublequeue = FALSE;
- for (a = e->e_sendqueue; a != NULL; a = a->q_next)
- {
- if (QS_IS_VERIFIED(a->q_state) &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- /* need to re-expand aliases */
- doublequeue = TRUE;
- }
- if (QS_IS_BADADDR(a->q_state))
- {
- /* make this "go away" */
- a->q_state = QS_DONTSEND;
- }
- }
-
- /* collect the text of the message */
- SmtpPhase = "collect";
- buffer_errors();
- collect(InChannel, TRUE, NULL, e);
-
-# if _FFR_MILTER
- if (milterize &&
- Errors <= 0 &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- response = milter_data(e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE:
- usrerr(response);
- break;
-
- case SMFIR_REJECT:
- usrerr("554 5.7.1 Command rejected");
- break;
-
- case SMFIR_DISCARD:
- e->e_flags |= EF_DISCARD;
- break;
-
- case SMFIR_TEMPFAIL:
- usrerr("451 4.7.1 Please try again later");
- break;
- }
- if (response != NULL)
- sm_free(response);
- }
-
- /* abort message filters that didn't get the body */
- if (milterize)
- milter_abort(e);
-# endif /* _FFR_MILTER */
-
- /* redefine message size */
- if ((q = macvalue(macid("{msg_size}", NULL), e))
- != NULL)
- sm_free(q);
- snprintf(inp, sizeof inp, "%ld", e->e_msgsize);
- define(macid("{msg_size}", NULL), newstr(inp), e);
- if (Errors > 0)
- {
- /* Log who the mail would have gone to */
- if (LogLevel > 8 &&
- e->e_message != NULL)
- {
- for (a = e->e_sendqueue;
- a != NULL;
- a = a->q_next)
- {
- if (!QS_IS_UNDELIVERED(a->q_state))
- continue;
-
- e->e_to = a->q_paddr;
- logdelivery(NULL, NULL,
- a->q_status,
- e->e_message,
- NULL,
- (time_t) 0, e);
- }
- e->e_to = NULL;
- }
- flush_errors(TRUE);
- buffer_errors();
- goto abortmessage;
- }
-
- /* make sure we actually do delivery */
- e->e_flags &= ~EF_CLRQUEUE;
-
- /* from now on, we have to operate silently */
- buffer_errors();
- e->e_errormode = EM_MAIL;
-
- /*
- ** Arrange to send to everyone.
- ** If sending to multiple people, mail back
- ** errors rather than reporting directly.
- ** In any case, don't mail back errors for
- ** anything that has happened up to
- ** now (the other end will do this).
- ** Truncate our transcript -- the mail has gotten
- ** to us successfully, and if we have
- ** to mail this back, it will be easier
- ** on the reader.
- ** Then send to everyone.
- ** Finally give a reply code. If an error has
- ** already been given, don't mail a
- ** message back.
- ** We goose error returns by clearing error bit.
- */
-
- SmtpPhase = "delivery";
- (void) bftruncate(e->e_xfp);
- id = e->e_id;
-
- /*
- ** If a header/body check (header checks or milter)
- ** set EF_DISCARD, don't queueup the message --
- ** that would lose the EF_DISCARD bit and deliver
- ** the message.
- */
-
- if (bitset(EF_DISCARD, e->e_flags))
- doublequeue = FALSE;
-
- if (doublequeue)
- {
- /* make sure it is in the queue */
- queueup(e, FALSE);
- }
- else
- {
- /* send to all recipients */
-# if NAMED_BIND
- _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
- _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
-# endif /* NAMED_BIND */
- sendall(e, SM_DEFAULT);
- }
- e->e_to = NULL;
-
- /* issue success message */
- message("250 2.0.0 %s Message accepted for delivery", id);
-
- /* if we just queued, poke it */
- if (doublequeue &&
- e->e_sendmode != SM_QUEUE &&
- e->e_sendmode != SM_DEFER)
- {
- CurrentLA = sm_getla(e);
-
- if (!shouldqueue(e->e_msgpriority, e->e_ctime))
- {
- /* close all the queue files */
- closexscript(e);
- if (e->e_dfp != NULL)
- (void) bfclose(e->e_dfp);
- e->e_dfp = NULL;
- unlockqueue(e);
-
- (void) dowork(e->e_queuedir, id,
- TRUE, TRUE, e);
- }
- }
-
- abortmessage:
- if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
- logsender(e, NULL);
- e->e_flags &= ~EF_LOGSENDER;
-
- /* if in a child, pop back to our parent */
- if (InChild)
- finis(TRUE, ExitStat);
-
- /* clean up a bit */
- gotmail = FALSE;
- dropenvelope(e, TRUE);
- CurEnv = e = newenvelope(e, CurEnv);
- e->e_flags = BlankEnvelope.e_flags;
+ DELAY_CONN("DATA");
+ smtp_data(&smtp, e);
break;
case CMDRSET: /* rset -- reset state */
-# if _FFR_MILTER
- /* abort milter filters */
- milter_abort(e);
-# endif /* _FFR_MILTER */
-
if (tTd(94, 100))
message("451 4.0.0 Test failure");
else
message("250 2.0.0 Reset state");
-
- /* arrange to ignore any current send list */
- e->e_sendqueue = NULL;
- e->e_flags |= EF_CLRQUEUE;
-
- if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
- logsender(e, NULL);
- e->e_flags &= ~EF_LOGSENDER;
-
- if (InChild)
- finis(TRUE, ExitStat);
-
- /* clean up a bit */
- gotmail = FALSE;
- SuprErrs = TRUE;
- dropenvelope(e, TRUE);
- CurEnv = e = newenvelope(e, CurEnv);
+ CLEAR_STATE(cmdbuf);
+#if _FFR_QUARANTINE
+ /* restore connection quarantining */
+ if (smtp.sm_quarmsg == NULL)
+ {
+ e->e_quarmsg = NULL;
+ macdefine(&e->e_macro, A_PERM,
+ macid("{quarantine}"), "");
+ }
+ else
+ {
+ e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
+ smtp.sm_quarmsg);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{quarantine}"), e->e_quarmsg);
+ }
+#endif /* _FFR_QUARANTINE */
break;
case CMDVRFY: /* vrfy -- verify address */
case CMDEXPN: /* expn -- expand address */
+ vrfy = c->cmd_code == CMDVRFY;
+ DELAY_CONN(vrfy ? "VRFY" : "EXPN");
if (tempfail)
{
if (LogLevel > 9)
sm_syslog(LOG_INFO, e->e_id,
"SMTP %s command (%.100s) from %.100s tempfailed (due to previous checks)",
- c->cmd_code == CMDVRFY ? "VRFY" : "EXPN",
+ vrfy ? "VRFY" : "EXPN",
p, CurSmtpClient);
+
+ /* RFC 821 doesn't allow 4xy reply code */
usrerr("550 5.7.1 Please try again later");
break;
}
- wt = checksmtpattack(&nverifies, MAXVRFYCOMMANDS, FALSE,
- c->cmd_code == CMDVRFY ? "VRFY" : "EXPN", e);
+ wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS,
+ false, vrfy ? "VRFY" : "EXPN", e);
previous = curtime();
- vrfy = c->cmd_code == CMDVRFY;
if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
- PrivacyFlags))
+ PrivacyFlags))
{
if (vrfy)
message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
@@ -2050,18 +2363,15 @@ smtp(nullserver, d_flags, e)
usrerr("503 5.0.0 I demand that you introduce yourself first");
break;
}
- if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
- break;
if (Errors > 0)
- goto undo_subproc;
+ break;
if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%.100s: %s",
+ sm_syslog(LOG_INFO, e->e_id, "%.100s: %s",
CurSmtpClient,
shortenstring(inp, MAXSHORTSTR));
- if (setjmp(TopFrame) > 0)
- goto undo_subproc;
- QuickAbort = TRUE;
+ SM_TRY
+ {
+ QuickAbort = true;
vrfyqueue = NULL;
if (vrfy)
e->e_flags |= EF_VRFYONLY;
@@ -2075,9 +2385,10 @@ smtp(nullserver, d_flags, e)
{
/* do config file checking of the address */
if (rscheck(vrfy ? "check_vrfy" : "check_expn",
- p, NULL, e, TRUE, FALSE, 4, NULL)
- != EX_OK || Errors > 0)
- goto undo_subproc;
+ p, NULL, e, true, false, 3, NULL,
+ NOQID) != EX_OK ||
+ Errors > 0)
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
}
if (wt > 0)
@@ -2089,7 +2400,7 @@ smtp(nullserver, d_flags, e)
(void) sleep(t);
}
if (Errors > 0)
- goto undo_subproc;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
if (vrfyqueue == NULL)
{
usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN");
@@ -2110,13 +2421,26 @@ smtp(nullserver, d_flags, e)
printvrfyaddr(vrfyqueue, a == NULL, vrfy);
vrfyqueue = a;
}
- if (InChild)
- finis(TRUE, ExitStat);
+ }
+ SM_EXCEPT(exc, "[!F]*")
+ {
+ /*
+ ** An exception occurred while processing VRFY/EXPN
+ */
+
+ sm_exc_free(exc);
+ goto undo;
+ }
+ SM_END_TRY
break;
case CMDETRN: /* etrn -- force queue flush */
- if (bitset(PRIV_NOETRN, PrivacyFlags) ||
- bitnset(D_NOETRN, d_flags))
+ DELAY_CONN("ETRN");
+
+ /* Don't leak queue information via debug flags */
+ if (!bitset(SRV_OFFER_ETRN, features) || UseMSP ||
+ (RealUid != 0 && RealUid != TrustedUid &&
+ OpMode == MD_SMTP))
{
/* different message for MSA ? */
message("502 5.7.0 Sorry, we do not allow this operation");
@@ -2133,7 +2457,7 @@ smtp(nullserver, d_flags, e)
sm_syslog(LOG_INFO, e->e_id,
"SMTP ETRN command (%.100s) from %.100s tempfailed (due to previous checks)",
p, CurSmtpClient);
- usrerr("451 4.7.1 Please try again later");
+ usrerr(MSG_TEMPFAIL);
break;
}
@@ -2144,80 +2468,119 @@ smtp(nullserver, d_flags, e)
}
/* crude way to avoid denial-of-service attacks */
- (void) checksmtpattack(&n_etrn, MAXETRNCOMMANDS, TRUE,
+ (void) checksmtpattack(&n_etrn, MAXETRNCOMMANDS, true,
"ETRN", e);
- /* do config file checking of the parameter */
- if (rscheck("check_etrn", p, NULL, e, TRUE, FALSE, 4,
- NULL) != EX_OK || Errors > 0)
+ /*
+ ** Do config file checking of the parameter.
+ ** Even though we have srv_features now, we still
+ ** need this ruleset because the former is called
+ ** when the connection has been established, while
+ ** this ruleset is called when the command is
+ ** actually issued and therefore has all information
+ ** available to make a decision.
+ */
+
+ if (rscheck("check_etrn", p, NULL, e, true, false, 3,
+ NULL, NOQID) != EX_OK || Errors > 0)
break;
if (LogLevel > 5)
sm_syslog(LOG_INFO, e->e_id,
- "%.100s: ETRN %s",
- CurSmtpClient,
+ "%.100s: ETRN %s", CurSmtpClient,
shortenstring(p, MAXSHORTSTR));
id = p;
+ if (*id == '#')
+ {
+ int wgrp;
+
+ id++;
+ wgrp = name2qid(id);
+ if (!ISVALIDQGRP(wgrp))
+ {
+ usrerr("459 4.5.4 Queue %s unknown",
+ id);
+ break;
+ }
+ ok = run_work_group(wgrp, true, false,
+ false, true);
+ if (ok && Errors == 0)
+ message("250 2.0.0 Queuing for queue group %s started", id);
+ break;
+ }
+
if (*id == '@')
id++;
else
*--id = '@';
- new = (QUEUE_CHAR *)xalloc(sizeof(QUEUE_CHAR));
+ new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR));
+ if (new == NULL)
+ {
+ syserr("500 5.5.0 ETRN out of memory");
+ break;
+ }
new->queue_match = id;
+ new->queue_negate = false;
new->queue_next = NULL;
QueueLimitRecipient = new;
- ok = runqueue(TRUE, FALSE);
- sm_free(QueueLimitRecipient);
+ ok = runqueue(true, false, false, true);
+ sm_free(QueueLimitRecipient); /* XXX */
QueueLimitRecipient = NULL;
if (ok && Errors == 0)
message("250 2.0.0 Queuing for node %s started", p);
break;
case CMDHELP: /* help -- give user info */
+ DELAY_CONN("HELP");
help(p, e);
break;
case CMDNOOP: /* noop -- do nothing */
- (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
+ DELAY_CONN("NOOP");
+ (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, true,
"NOOP", e);
message("250 2.0.0 OK");
break;
case CMDQUIT: /* quit -- leave mail */
message("221 2.0.0 %s closing connection", MyHostName);
+#if PIPELINING
+ (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
+#endif /* PIPELINING */
+
+ if (smtp.sm_nrcpts > 0)
+ logundelrcpts(e, "aborted by sender", 9, false);
/* arrange to ignore any current send list */
e->e_sendqueue = NULL;
-# if STARTTLS
+#if STARTTLS
/* shutdown TLS connection */
if (tls_active)
{
(void) endtls(srv_ssl, "server");
- tls_active = FALSE;
+ tls_active = false;
}
-# endif /* STARTTLS */
-# if SASL
+#endif /* STARTTLS */
+#if SASL
if (authenticating == SASL_IS_AUTH)
{
sasl_dispose(&conn);
authenticating = SASL_NOT_AUTH;
+ /* XXX sasl_done(); this is a child */
}
-# endif /* SASL */
+#endif /* SASL */
doquit:
/* avoid future 050 messages */
disconnect(1, e);
-# if _FFR_MILTER
+#if MILTER
/* close out milter filters */
milter_quit(e);
-# endif /* _FFR_MILTER */
-
- if (InChild)
- ExitStat = EX_QUIT;
+#endif /* MILTER */
if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
logsender(e, NULL);
@@ -2227,49 +2590,48 @@ doquit:
{
char *d;
- d = macvalue(macid("{daemon_name}", NULL), e);
+ d = macvalue(macid("{daemon_name}"), e);
if (d == NULL)
d = "stdin";
- sm_syslog(LOG_INFO, NULL,
- "%.100s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
+
+ /*
+ ** even though this id is "bogus", it makes
+ ** it simpler to "grep" related events, e.g.,
+ ** timeouts for the same connection.
+ */
+
+ sm_syslog(LOG_INFO, e->e_id,
+ "%.100s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
CurSmtpClient, d);
}
- finis(TRUE, ExitStat);
+#if PROFILING
+ return;
+#endif /* PROFILING */
+ finis(true, true, ExitStat);
/* NOTREACHED */
case CMDVERB: /* set verbose mode */
+ DELAY_CONN("VERB");
if (bitset(PRIV_NOEXPN, PrivacyFlags) ||
+ !bitset(SRV_OFFER_VERB, features) ||
bitset(PRIV_NOVERB, PrivacyFlags))
{
/* this would give out the same info */
message("502 5.7.0 Verbose unavailable");
break;
}
- (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
+ (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, true,
"VERB", e);
Verbose = 1;
set_delivery_mode(SM_DELIVER, e);
message("250 2.0.0 Verbose mode");
break;
- case CMDONEX: /* doing one transaction only */
- (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
- "ONEX", e);
- OneXact = TRUE;
- message("250 2.0.0 Only one transaction");
- break;
-
- case CMDXUSR: /* initial (user) submission */
- (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
- "XUSR", e);
- define(macid("{daemon_flags}", NULL), "c u", CurEnv);
- message("250 2.0.0 Initial submission");
- break;
-
-# if SMTPDEBUG
+#if SMTPDEBUG
case CMDDBGQSHOW: /* show queues */
- printf("Send Queue=");
- printaddr(e->e_sendqueue, TRUE);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Send Queue=");
+ printaddr(e->e_sendqueue, true);
break;
case CMDDBGDEBUG: /* set debug mode */
@@ -2278,11 +2640,12 @@ doquit:
message("200 2.0.0 Debug set");
break;
-# else /* SMTPDEBUG */
+#else /* SMTPDEBUG */
case CMDDBGQSHOW: /* show queues */
case CMDDBGDEBUG: /* set debug mode */
-# endif /* SMTPDEBUG */
+#endif /* SMTPDEBUG */
case CMDLOGBOGUS: /* bogus command */
+ DELAY_CONN("Bogus");
if (LogLevel > 0)
sm_syslog(LOG_CRIT, e->e_id,
"\"%s\" command from %.100s (%.100s)",
@@ -2291,7 +2654,8 @@ doquit:
/* FALLTHROUGH */
case CMDERROR: /* unknown command */
- if (++badcommands > MAXBADCOMMANDS)
+#if MAXBADCOMMANDS > 0
+ if (++n_badcmds > MAXBADCOMMANDS)
{
message("421 4.7.0 %s Too many bad commands; closing connection",
MyHostName);
@@ -2300,28 +2664,439 @@ doquit:
e->e_sendqueue = NULL;
goto doquit;
}
+#endif /* MAXBADCOMMANDS > 0 */
usrerr("500 5.5.1 Command unrecognized: \"%s\"",
shortenstring(inp, MAXSHORTSTR));
break;
case CMDUNIMPL:
+ DELAY_CONN("Unimpl");
usrerr("502 5.5.1 Command not implemented: \"%s\"",
shortenstring(inp, MAXSHORTSTR));
break;
default:
+ DELAY_CONN("default");
errno = 0;
syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
break;
}
-# if SASL
+#if SASL
}
-# endif /* SASL */
+#endif /* SASL */
+ }
+ SM_EXCEPT(exc, "[!F]*")
+ {
+ /*
+ ** The only possible exception is "E:mta.quickabort".
+ ** There is nothing to do except fall through and loop.
+ */
+ }
+ SM_END_TRY
}
+}
+/*
+** SMTP_DATA -- implement the SMTP DATA command.
+**
+** Parameters:
+** smtp -- status of SMTP connection.
+** e -- envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** possibly sends message.
+*/
+
+static void
+smtp_data(smtp, e)
+ SMTP_T *smtp;
+ ENVELOPE *e;
+{
+#if MILTER
+ bool milteraccept;
+#endif /* MILTER */
+ bool aborting;
+ bool doublequeue;
+ ADDRESS *a;
+ ENVELOPE *ee;
+ char *id;
+ char buf[32];
+
+ SmtpPhase = "server DATA";
+ if (!smtp->sm_gotmail)
+ {
+ usrerr("503 5.0.0 Need MAIL command");
+ return;
+ }
+ else if (smtp->sm_nrcpts <= 0)
+ {
+ usrerr("503 5.0.0 Need RCPT (recipient)");
+ return;
+ }
+ (void) sm_snprintf(buf, sizeof buf, "%u", smtp->sm_nrcpts);
+ if (rscheck("check_data", buf, NULL, e,
+ true, false, 3, NULL, e->e_id) != EX_OK)
+ return;
+
+ /* put back discard bit */
+ if (smtp->sm_discard)
+ e->e_flags |= EF_DISCARD;
+
+ /* check to see if we need to re-expand aliases */
+ /* also reset QS_BADADDR on already-diagnosted addrs */
+ doublequeue = false;
+ for (a = e->e_sendqueue; a != NULL; a = a->q_next)
+ {
+ if (QS_IS_VERIFIED(a->q_state) &&
+ !bitset(EF_DISCARD, e->e_flags))
+ {
+ /* need to re-expand aliases */
+ doublequeue = true;
+ }
+ if (QS_IS_BADADDR(a->q_state))
+ {
+ /* make this "go away" */
+ a->q_state = QS_DONTSEND;
+ }
+ }
+
+ /* collect the text of the message */
+ SmtpPhase = "collect";
+ buffer_errors();
+
+#if _FFR_ADAPTIVE_EOL
+ /* triggers error in collect, disabled for now */
+ if (smtp->sm_crlf)
+ e->e_flags |= EF_NL_NOT_EOL;
+#endif /* _FFR_ADAPTIVE_EOL */
+
+ collect(InChannel, true, NULL, e);
+
+ /* redefine message size */
+ (void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize);
+ macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
+
+#if _FFR_CHECK_EOM
+ /* rscheck() will set Errors or EF_DISCARD if it trips */
+ (void) rscheck("check_eom", buf, NULL, e, false,
+ true, 3, NULL, e->e_id);
+#endif /* _FFR_CHECK_EOM */
+
+#if MILTER
+ milteraccept = true;
+ if (smtp->sm_milterlist && smtp->sm_milterize &&
+ Errors <= 0 &&
+ !bitset(EF_DISCARD, e->e_flags))
+ {
+ char state;
+ char *response;
+
+ response = milter_data(e, &state);
+ switch (state)
+ {
+ case SMFIR_REPLYCODE:
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: data, reject=%s",
+ response);
+ milteraccept = false;
+ usrerr(response);
+ break;
+
+ case SMFIR_REJECT:
+ milteraccept = false;
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: data, reject=554 5.7.1 Command rejected");
+ usrerr("554 5.7.1 Command rejected");
+ break;
+
+ case SMFIR_DISCARD:
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: data, discard");
+ milteraccept = false;
+ e->e_flags |= EF_DISCARD;
+ break;
+
+ case SMFIR_TEMPFAIL:
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: data, reject=%s",
+ MSG_TEMPFAIL);
+ milteraccept = false;
+ usrerr(MSG_TEMPFAIL);
+ break;
+ }
+ if (response != NULL)
+ sm_free(response);
+ }
+
+ /* Milter may have changed message size */
+ (void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize);
+ macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
+
+ /* abort message filters that didn't get the body & log msg is OK */
+ if (smtp->sm_milterlist && smtp->sm_milterize)
+ {
+ milter_abort(e);
+ if (milteraccept && MilterLogLevel > 9)
+ sm_syslog(LOG_INFO, e->e_id, "Milter accept: message");
+ }
+#endif /* MILTER */
+
+#if _FFR_QUARANTINE
+ /* Check if quarantining stats should be updated */
+ if (e->e_quarmsg != NULL)
+ markstats(e, NULL, STATS_QUARANTINE);
+#endif /* _FFR_QUARANTINE */
+
+ /*
+ ** If a header/body check (header checks or milter)
+ ** set EF_DISCARD, don't queueup the message --
+ ** that would lose the EF_DISCARD bit and deliver
+ ** the message.
+ */
+
+ if (bitset(EF_DISCARD, e->e_flags))
+ doublequeue = false;
+
+ aborting = Errors > 0;
+ if (!aborting &&
+#if _FFR_QUARANTINE
+ (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) &&
+#endif /* _FFR_QUARANTINE */
+ !split_by_recipient(e))
+ aborting = bitset(EF_FATALERRS, e->e_flags);
+
+ if (aborting)
+ {
+ /* Log who the mail would have gone to */
+ logundelrcpts(e, e->e_message, 8, false);
+ flush_errors(true);
+ buffer_errors();
+ goto abortmessage;
+ }
+
+ /* from now on, we have to operate silently */
+ buffer_errors();
+
+#if 0
+ /*
+ ** Clear message, it may contain an error from the SMTP dialogue.
+ ** This error must not show up in the queue.
+ ** Some error message should show up, e.g., alias database
+ ** not available, but others shouldn't, e.g., from check_rcpt.
+ */
+
+ e->e_message = NULL;
+#endif /* 0 */
+
+ /*
+ ** Arrange to send to everyone.
+ ** If sending to multiple people, mail back
+ ** errors rather than reporting directly.
+ ** In any case, don't mail back errors for
+ ** anything that has happened up to
+ ** now (the other end will do this).
+ ** Truncate our transcript -- the mail has gotten
+ ** to us successfully, and if we have
+ ** to mail this back, it will be easier
+ ** on the reader.
+ ** Then send to everyone.
+ ** Finally give a reply code. If an error has
+ ** already been given, don't mail a
+ ** message back.
+ ** We goose error returns by clearing error bit.
+ */
+
+ SmtpPhase = "delivery";
+ (void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL);
+ id = e->e_id;
+
+#if NAMED_BIND
+ _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
+ _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
+#endif /* NAMED_BIND */
+
+ for (ee = e; ee != NULL; ee = ee->e_sibling)
+ {
+ /* make sure we actually do delivery */
+ ee->e_flags &= ~EF_CLRQUEUE;
+
+ /* from now on, operate silently */
+ ee->e_errormode = EM_MAIL;
+
+ if (doublequeue)
+ {
+ /* make sure it is in the queue */
+ queueup(ee, false, true);
+ }
+ else
+ {
+ /* send to all recipients */
+ sendall(ee, SM_DEFAULT);
+ }
+ ee->e_to = NULL;
+ }
+
+ /* issue success message */
+ message("250 2.0.0 %s Message accepted for delivery", id);
+
+ /* if we just queued, poke it */
+ if (doublequeue)
+ {
+ bool anything_to_send = false;
+
+ sm_getla();
+ for (ee = e; ee != NULL; ee = ee->e_sibling)
+ {
+ if (WILL_BE_QUEUED(ee->e_sendmode))
+ continue;
+ if (shouldqueue(ee->e_msgpriority, ee->e_ctime))
+ {
+ ee->e_sendmode = SM_QUEUE;
+ continue;
+ }
+#if _FFR_QUARANTINE
+ else if (QueueMode != QM_QUARANTINE &&
+ ee->e_quarmsg != NULL)
+ {
+ ee->e_sendmode = SM_QUEUE;
+ continue;
+ }
+#endif /* _FFR_QUARANTINE */
+ anything_to_send = true;
+
+ /* close all the queue files */
+ closexscript(ee);
+ if (ee->e_dfp != NULL)
+ {
+ (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
+ ee->e_dfp = NULL;
+ }
+ unlockqueue(ee);
+ }
+ if (anything_to_send)
+ {
+#if PIPELINING
+ /*
+ ** XXX if we don't do this, we get 250 twice
+ ** because it is also flushed in the child.
+ */
+
+ (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
+#endif /* PIPELINING */
+ (void) doworklist(e, true, true);
+ }
+ }
+
+ abortmessage:
+ if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
+ logsender(e, NULL);
+ e->e_flags &= ~EF_LOGSENDER;
+
+ /* clean up a bit */
+ smtp->sm_gotmail = false;
+
+ /*
+ ** Call dropenvelope if and only if the envelope is *not*
+ ** being processed by the child process forked by doworklist().
+ */
+
+ if (aborting || bitset(EF_DISCARD, e->e_flags))
+ dropenvelope(e, true, false);
+ else
+ {
+ for (ee = e; ee != NULL; ee = ee->e_sibling)
+ {
+#if _FFR_QUARANTINE
+ if (!doublequeue &&
+ QueueMode != QM_QUARANTINE &&
+ ee->e_quarmsg != NULL)
+ {
+ dropenvelope(ee, true, false);
+ continue;
+ }
+#endif /* _FFR_QUARANTINE */
+ if (WILL_BE_QUEUED(ee->e_sendmode))
+ dropenvelope(ee, true, false);
+ }
+ }
+ sm_rpool_free(e->e_rpool);
+
+ /*
+ ** At this point, e == &MainEnvelope, but if we did splitting,
+ ** then CurEnv may point to an envelope structure that was just
+ ** freed with the rpool. So reset CurEnv *before* calling
+ ** newenvelope.
+ */
+
+ CurEnv = e;
+ newenvelope(e, e, sm_rpool_new_x(NULL));
+ e->e_flags = BlankEnvelope.e_flags;
+
+#if _FFR_QUARANTINE
+ /* restore connection quarantining */
+ if (smtp->sm_quarmsg == NULL)
+ {
+ e->e_quarmsg = NULL;
+ macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), "");
+ }
+ else
+ {
+ e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, smtp->sm_quarmsg);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{quarantine}"), e->e_quarmsg);
+ }
+#endif /* _FFR_QUARANTINE */
+}
+/*
+** LOGUNDELRCPTS -- log undelivered (or all) recipients.
+**
+** Parameters:
+** e -- envelope.
+** msg -- message for Stat=
+** level -- log level.
+** all -- log all recipients.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** logs undelivered (or all) recipients
+*/
+
+void
+logundelrcpts(e, msg, level, all)
+ ENVELOPE *e;
+ char *msg;
+ int level;
+ bool all;
+{
+ ADDRESS *a;
+
+ if (LogLevel <= level || msg == NULL || *msg == '\0')
+ return;
+
+ /* Clear $h so relay= doesn't get mislogged by logdelivery() */
+ macdefine(&e->e_macro, A_PERM, 'h', NULL);
+ /* Log who the mail would have gone to */
+ for (a = e->e_sendqueue; a != NULL; a = a->q_next)
+ {
+ if (!QS_IS_UNDELIVERED(a->q_state) && !all)
+ continue;
+ e->e_to = a->q_paddr;
+ logdelivery(NULL, NULL, a->q_status, msg, NULL,
+ (time_t) 0, e);
+ }
+ e->e_to = NULL;
}
- /*
+/*
** CHECKSMTPATTACK -- check for denial-of-service attack by repetition
**
** Parameters:
@@ -2341,12 +3116,15 @@ doquit:
static time_t
checksmtpattack(pcounter, maxcount, waitnow, cname, e)
- volatile int *pcounter;
+ volatile unsigned int *pcounter;
int maxcount;
bool waitnow;
char *cname;
ENVELOPE *e;
{
+ if (maxcount <= 0) /* no limit */
+ return (time_t) 0;
+
if (++(*pcounter) >= maxcount)
{
time_t s;
@@ -2354,25 +3132,102 @@ checksmtpattack(pcounter, maxcount, waitnow, cname, e)
if (*pcounter == maxcount && LogLevel > 5)
{
sm_syslog(LOG_INFO, e->e_id,
- "%.100s: possible SMTP attack: command=%.40s, count=%d",
+ "%.100s: possible SMTP attack: command=%.40s, count=%u",
CurSmtpClient, cname, *pcounter);
}
s = 1 << (*pcounter - maxcount);
- if (s >= MAXTIMEOUT)
+ if (s >= MAXTIMEOUT || s <= 0)
s = MAXTIMEOUT;
+
/* sleep at least 1 second before returning */
(void) sleep(*pcounter / maxcount);
s -= *pcounter / maxcount;
if (waitnow)
{
(void) sleep(s);
- return(0);
+ return 0;
}
- return(s);
+ return s;
}
- return((time_t) 0);
+ return (time_t) 0;
}
- /*
+/*
+** SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server
+**
+** Parameters:
+** none.
+**
+** Returns:
+** nothing.
+**
+** Side Effects:
+** may change I/O fd.
+*/
+
+static void
+setup_smtpd_io()
+{
+ int inchfd, outchfd, outfd;
+
+ inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
+ outchfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
+ outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL);
+ if (outchfd != outfd)
+ {
+ /* arrange for debugging output to go to remote host */
+ (void) dup2(outchfd, outfd);
+ }
+
+ /*
+ ** if InChannel and OutChannel are stdin/stdout
+ ** and connected to ttys
+ ** and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT,
+ ** then "chain" them together.
+ */
+
+ if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO &&
+ isatty(inchfd) && isatty(outchfd))
+ {
+ int inmode, outmode;
+
+ inmode = fcntl(inchfd, F_GETFL, 0);
+ if (inmode == -1)
+ {
+ if (LogLevel > 11)
+ sm_syslog(LOG_INFO, NOQID,
+ "fcntl(inchfd, F_GETFL) failed: %s",
+ sm_errstring(errno));
+ return;
+ }
+ outmode = fcntl(outchfd, F_GETFL, 0);
+ if (outmode == -1)
+ {
+ if (LogLevel > 11)
+ sm_syslog(LOG_INFO, NOQID,
+ "fcntl(outchfd, F_GETFL) failed: %s",
+ sm_errstring(errno));
+ return;
+ }
+ if (bitset(O_NONBLOCK, inmode) ||
+ bitset(O_NONBLOCK, outmode) ||
+ fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1)
+ return;
+ outmode = fcntl(outchfd, F_GETFL, 0);
+ if (outmode != -1 && bitset(O_NONBLOCK, outmode))
+ {
+ /* changing InChannel also changes OutChannel */
+ sm_io_automode(OutChannel, InChannel);
+ if (tTd(97, 4) && LogLevel > 9)
+ sm_syslog(LOG_INFO, NOQID,
+ "set automode for I (%d)/O (%d) in SMTP server",
+ inchfd, outchfd);
+ }
+
+ /* undo change of inchfd */
+ (void) fcntl(inchfd, F_SETFL, inmode);
+ }
+}
+/*
** SKIPWORD -- skip a fixed word.
**
** Parameters:
@@ -2396,8 +3251,7 @@ skipword(p, w)
char *firstp = p;
/* find beginning of word */
- while (isascii(*p) && isspace(*p))
- p++;
+ SKIP_SPACE(p);
q = p;
/* find end of word */
@@ -2413,19 +3267,18 @@ skipword(p, w)
return NULL;
}
*p++ = '\0';
- while (isascii(*p) && isspace(*p))
- p++;
+ SKIP_SPACE(p);
if (*p == '\0')
goto syntax;
/* see if the input word matches desired word */
- if (strcasecmp(q, w))
+ if (sm_strcasecmp(q, w))
goto syntax;
return p;
}
- /*
+/*
** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
**
** Parameters:
@@ -2443,45 +3296,50 @@ mail_esmtp_args(kp, vp, e)
char *vp;
ENVELOPE *e;
{
- if (strcasecmp(kp, "size") == 0)
+ if (sm_strcasecmp(kp, "size") == 0)
{
if (vp == NULL)
{
usrerr("501 5.5.2 SIZE requires a value");
/* NOTREACHED */
}
- define(macid("{msg_size}", NULL), newstr(vp), e);
+ macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp);
+ errno = 0;
e->e_msgsize = strtol(vp, (char **) NULL, 10);
if (e->e_msgsize == LONG_MAX && errno == ERANGE)
{
usrerr("552 5.2.3 Message size exceeds maximum value");
/* NOTREACHED */
}
+ if (e->e_msgsize < 0)
+ {
+ usrerr("552 5.2.3 Message size invalid");
+ /* NOTREACHED */
+ }
}
- else if (strcasecmp(kp, "body") == 0)
+ else if (sm_strcasecmp(kp, "body") == 0)
{
if (vp == NULL)
{
usrerr("501 5.5.2 BODY requires a value");
/* NOTREACHED */
}
- else if (strcasecmp(vp, "8bitmime") == 0)
+ else if (sm_strcasecmp(vp, "8bitmime") == 0)
{
- SevenBitInput = FALSE;
+ SevenBitInput = false;
}
- else if (strcasecmp(vp, "7bit") == 0)
+ else if (sm_strcasecmp(vp, "7bit") == 0)
{
- SevenBitInput = TRUE;
+ SevenBitInput = true;
}
else
{
- usrerr("501 5.5.4 Unknown BODY type %s",
- vp);
+ usrerr("501 5.5.4 Unknown BODY type %s", vp);
/* NOTREACHED */
}
- e->e_bodytype = newstr(vp);
+ e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp);
}
- else if (strcasecmp(kp, "envid") == 0)
+ else if (sm_strcasecmp(kp, "envid") == 0)
{
if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
{
@@ -2503,10 +3361,11 @@ mail_esmtp_args(kp, vp, e)
usrerr("501 5.5.0 Duplicate ENVID parameter");
/* NOTREACHED */
}
- e->e_envid = newstr(vp);
- define(macid("{dsn_envid}", NULL), newstr(vp), e);
+ e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{dsn_envid}"), e->e_envid);
}
- else if (strcasecmp(kp, "ret") == 0)
+ else if (sm_strcasecmp(kp, "ret") == 0)
{
if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
{
@@ -2524,23 +3383,24 @@ mail_esmtp_args(kp, vp, e)
/* NOTREACHED */
}
e->e_flags |= EF_RET_PARAM;
- if (strcasecmp(vp, "hdrs") == 0)
+ if (sm_strcasecmp(vp, "hdrs") == 0)
e->e_flags |= EF_NO_BODY_RETN;
- else if (strcasecmp(vp, "full") != 0)
+ else if (sm_strcasecmp(vp, "full") != 0)
{
usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
/* NOTREACHED */
}
- define(macid("{dsn_ret}", NULL), newstr(vp), e);
+ macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp);
}
-# if SASL
- else if (strcasecmp(kp, "auth") == 0)
+#if SASL
+ else if (sm_strcasecmp(kp, "auth") == 0)
{
int len;
char *q;
char *auth_param; /* the value of the AUTH=x */
bool saveQuickAbort = QuickAbort;
bool saveSuprErrs = SuprErrs;
+ bool saveExitStat = ExitStat;
char pbuf[256];
if (vp == NULL)
@@ -2558,7 +3418,7 @@ mail_esmtp_args(kp, vp, e)
else
len = strlen(vp) + 1;
auth_param = xalloc(len);
- (void) strlcpy(auth_param, vp, len);
+ (void) sm_strlcpy(auth_param, vp, len);
if (!xtextok(auth_param))
{
usrerr("501 5.5.4 Syntax error in AUTH parameter value");
@@ -2567,11 +3427,11 @@ mail_esmtp_args(kp, vp, e)
}
/* XXX this might be cut off */
- snprintf(pbuf, sizeof pbuf, "%s", xuntextify(auth_param));
+ (void) sm_strlcpy(pbuf, xuntextify(auth_param), sizeof pbuf);
/* xalloc() the buffer instead? */
/* XXX define this always or only if trusted? */
- define(macid("{auth_author}", NULL), newstr(pbuf), e);
+ macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"), pbuf);
/*
** call Strust_auth to find out whether
@@ -2580,42 +3440,123 @@ mail_esmtp_args(kp, vp, e)
** (required by RFC, leave it to ruleset?)
*/
- SuprErrs = TRUE;
- QuickAbort = FALSE;
+ SuprErrs = true;
+ QuickAbort = false;
if (strcmp(auth_param, "<>") != 0 &&
- (rscheck("trust_auth", pbuf, NULL, e, TRUE, FALSE, 10,
- NULL) != EX_OK || Errors > 0))
+ (rscheck("trust_auth", pbuf, NULL, e, true, false, 9,
+ NULL, NOQID) != EX_OK || Errors > 0))
{
if (tTd(95, 8))
{
q = e->e_auth_param;
- dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
+ sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
pbuf, (q == NULL) ? "" : q);
}
+
/* not trusted */
- e->e_auth_param = newstr("<>");
+ e->e_auth_param = "<>";
+# if _FFR_AUTH_PASSING
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{auth_author}"), NULL);
+# endif /* _FFR_AUTH_PASSING */
}
else
{
if (tTd(95, 8))
- dprintf("auth=\"%.100s\" trusted\n", pbuf);
- e->e_auth_param = newstr(auth_param);
+ sm_dprintf("auth=\"%.100s\" trusted\n", pbuf);
+ e->e_auth_param = sm_rpool_strdup_x(e->e_rpool,
+ auth_param);
}
- sm_free(auth_param);
+ sm_free(auth_param); /* XXX */
/* reset values */
Errors = 0;
QuickAbort = saveQuickAbort;
SuprErrs = saveSuprErrs;
+ ExitStat = saveExitStat;
+ }
+#endif /* SASL */
+#define PRTCHAR(c) ((isascii(c) && isprint(c)) ? (c) : '?')
+
+ /*
+ ** "by" is only accepted if DeliverByMin >= 0.
+ ** We maybe could add this to the list of server_features.
+ */
+
+ else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0)
+ {
+ char *s;
+
+ if (vp == NULL)
+ {
+ usrerr("501 5.5.2 BY= requires a value");
+ /* NOTREACHED */
+ }
+ errno = 0;
+ e->e_deliver_by = strtol(vp, &s, 10);
+ if (e->e_deliver_by == LONG_MIN ||
+ e->e_deliver_by == LONG_MAX ||
+ e->e_deliver_by > 999999999l ||
+ e->e_deliver_by < -999999999l)
+ {
+ usrerr("501 5.5.2 BY=%s out of range", vp);
+ /* NOTREACHED */
+ }
+ if (s == NULL || *s != ';')
+ {
+ usrerr("501 5.5.2 BY= missing ';'");
+ /* NOTREACHED */
+ }
+ e->e_dlvr_flag = 0;
+ ++s; /* XXX: spaces allowed? */
+ SKIP_SPACE(s);
+ switch (tolower(*s))
+ {
+ case 'n':
+ e->e_dlvr_flag = DLVR_NOTIFY;
+ break;
+ case 'r':
+ e->e_dlvr_flag = DLVR_RETURN;
+ if (e->e_deliver_by <= 0)
+ {
+ usrerr("501 5.5.4 mode R requires BY time > 0");
+ /* NOTREACHED */
+ }
+ if (DeliverByMin > 0 && e->e_deliver_by > 0 &&
+ e->e_deliver_by < DeliverByMin)
+ {
+ usrerr("555 5.5.2 time %ld less than %ld",
+ e->e_deliver_by, (long) DeliverByMin);
+ /* NOTREACHED */
+ }
+ break;
+ default:
+ usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s));
+ /* NOTREACHED */
+ }
+ ++s; /* XXX: spaces allowed? */
+ SKIP_SPACE(s);
+ switch (tolower(*s))
+ {
+ case 't':
+ e->e_dlvr_flag |= DLVR_TRACE;
+ break;
+ case '\0':
+ break;
+ default:
+ usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s));
+ /* NOTREACHED */
+ }
+
+ /* XXX: check whether more characters follow? */
}
-# endif /* SASL */
else
{
usrerr("555 5.5.4 %s parameter unrecognized", kp);
/* NOTREACHED */
}
}
- /*
+/*
** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
**
** Parameters:
@@ -2635,7 +3576,7 @@ rcpt_esmtp_args(a, kp, vp, e)
char *vp;
ENVELOPE *e;
{
- if (strcasecmp(kp, "notify") == 0)
+ if (sm_strcasecmp(kp, "notify") == 0)
{
char *p;
@@ -2651,20 +3592,20 @@ rcpt_esmtp_args(a, kp, vp, e)
}
a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
a->q_flags |= QHASNOTIFY;
- define(macid("{dsn_notify}", NULL), newstr(vp), e);
+ macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp);
- if (strcasecmp(vp, "never") == 0)
+ if (sm_strcasecmp(vp, "never") == 0)
return;
for (p = vp; p != NULL; vp = p)
{
p = strchr(p, ',');
if (p != NULL)
*p++ = '\0';
- if (strcasecmp(vp, "success") == 0)
+ if (sm_strcasecmp(vp, "success") == 0)
a->q_flags |= QPINGONSUCCESS;
- else if (strcasecmp(vp, "failure") == 0)
+ else if (sm_strcasecmp(vp, "failure") == 0)
a->q_flags |= QPINGONFAILURE;
- else if (strcasecmp(vp, "delay") == 0)
+ else if (sm_strcasecmp(vp, "delay") == 0)
a->q_flags |= QPINGONDELAY;
else
{
@@ -2674,7 +3615,7 @@ rcpt_esmtp_args(a, kp, vp, e)
}
}
}
- else if (strcasecmp(kp, "orcpt") == 0)
+ else if (sm_strcasecmp(kp, "orcpt") == 0)
{
if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
{
@@ -2696,7 +3637,7 @@ rcpt_esmtp_args(a, kp, vp, e)
usrerr("501 5.5.0 Duplicate ORCPT parameter");
/* NOTREACHED */
}
- a->q_orcpt = newstr(vp);
+ a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp);
}
else
{
@@ -2704,11 +3645,11 @@ rcpt_esmtp_args(a, kp, vp, e)
/* NOTREACHED */
}
}
- /*
+/*
** PRINTVRFYADDR -- print an entry in the verify queue
**
** Parameters:
-** a -- the address to print
+** a -- the address to print.
** last -- set if this is the last one.
** vrfy -- set if this is a VRFY command.
**
@@ -2730,21 +3671,21 @@ printvrfyaddr(a, last, vrfy)
if (vrfy && a->q_mailer != NULL &&
!bitnset(M_VRFY250, a->q_mailer->m_flags))
- (void) strlcpy(fmtbuf, "252", sizeof fmtbuf);
+ (void) sm_strlcpy(fmtbuf, "252", sizeof fmtbuf);
else
- (void) strlcpy(fmtbuf, "250", sizeof fmtbuf);
+ (void) sm_strlcpy(fmtbuf, "250", sizeof fmtbuf);
fmtbuf[3] = last ? ' ' : '-';
- (void) strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4);
+ (void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4);
if (a->q_fullname == NULL)
{
if ((a->q_mailer == NULL ||
a->q_mailer->m_addrtype == NULL ||
- strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
+ sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
strchr(a->q_user, '@') == NULL)
- (void) strlcpy(&fmtbuf[OFFF], "<%s@%s>",
+ (void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>",
sizeof fmtbuf - OFFF);
else
- (void) strlcpy(&fmtbuf[OFFF], "<%s>",
+ (void) sm_strlcpy(&fmtbuf[OFFF], "<%s>",
sizeof fmtbuf - OFFF);
message(fmtbuf, a->q_user, MyHostName);
}
@@ -2752,127 +3693,27 @@ printvrfyaddr(a, last, vrfy)
{
if ((a->q_mailer == NULL ||
a->q_mailer->m_addrtype == NULL ||
- strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
+ sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
strchr(a->q_user, '@') == NULL)
- (void) strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
+ (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
sizeof fmtbuf - OFFF);
else
- (void) strlcpy(&fmtbuf[OFFF], "%s <%s>",
+ (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>",
sizeof fmtbuf - OFFF);
message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
}
}
- /*
-** RUNINCHILD -- return twice -- once in the child, then in the parent again
-**
-** Parameters:
-** label -- a string used in error messages
-**
-** Returns:
-** RIC_INCHILD in the child
-** RIC_INPARENT in the parent
-** RIC_TEMPFAIL tempfail condition
-**
-** Side Effects:
-** none.
-*/
-
-static int
-runinchild(label, e)
- char *label;
- register ENVELOPE *e;
-{
- pid_t childpid;
-
- if (!OneXact)
- {
- extern int NumQueues;
- /*
- ** advance state of PRNG
- ** this is necessary because otherwise all child processes
- ** will produce the same PRN sequence and hence the selection
- ** of a queue directory is not "really" random.
- */
- if (NumQueues > 1)
- (void) get_random();
-
- /*
- ** Disable child process reaping, in case ETRN has preceded
- ** MAIL command, and then fork.
- */
-
- (void) blocksignal(SIGCHLD);
-
-
- childpid = dofork();
- if (childpid < 0)
- {
- syserr("451 4.3.0 %s: cannot fork", label);
- (void) releasesignal(SIGCHLD);
- return RIC_INPARENT;
- }
- if (childpid > 0)
- {
- auto int st;
-
- /* parent -- wait for child to complete */
- sm_setproctitle(TRUE, e, "server %s child wait",
- CurSmtpClient);
- st = waitfor(childpid);
- if (st == -1)
- syserr("451 4.3.0 %s: lost child", label);
- else if (!WIFEXITED(st))
- {
- syserr("451 4.3.0 %s: died on signal %d",
- label, st & 0177);
- return RIC_TEMPFAIL;
- }
-
- /* if exited on a QUIT command, complete the process */
- if (WEXITSTATUS(st) == EX_QUIT)
- {
- disconnect(1, e);
- finis(TRUE, ExitStat);
- }
-
- /* restore the child signal */
- (void) releasesignal(SIGCHLD);
-
- return RIC_INPARENT;
- }
- else
- {
- /* child */
- InChild = TRUE;
- QuickAbort = FALSE;
-
- /* Reset global flags */
- RestartRequest = NULL;
- ShutdownRequest = NULL;
- PendingSignal = 0;
-
- clearstats();
- clearenvelope(e, FALSE);
- assign_queueid(e);
- (void) setsignal(SIGCHLD, SIG_DFL);
- (void) releasesignal(SIGCHLD);
- }
- }
- return RIC_INCHILD;
-}
-
-# if SASL
-
- /*
+#if SASL
+/*
** SASLMECHS -- get list of possible AUTH mechanisms
**
** Parameters:
-** conn -- SASL connection info
-** mechlist -- output parameter for list of mechanisms
+** conn -- SASL connection info.
+** mechlist -- output parameter for list of mechanisms.
**
** Returns:
-** number of mechs
+** number of mechs.
*/
static int
@@ -2885,35 +3726,41 @@ saslmechs(conn, mechlist)
/* "user" is currently unused */
result = sasl_listmech(conn, "user", /* XXX */
"", " ", "", mechlist,
- (u_int *)&len, (u_int *)&num);
- if (result == SASL_OK && num > 0)
+ (unsigned int *)&len, (unsigned int *)&num);
+ if (result != SASL_OK)
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, NOQID,
+ "AUTH error: listmech=%d, num=%d",
+ result, num);
+ num = 0;
+ }
+ if (num > 0)
{
if (LogLevel > 11)
sm_syslog(LOG_INFO, NOQID,
- "SASL: available mech=%s, allowed mech=%s",
+ "AUTH: available mech=%s, allowed mech=%s",
*mechlist, AuthMechanisms);
- *mechlist = intersect(AuthMechanisms, *mechlist);
+ *mechlist = intersect(AuthMechanisms, *mechlist, NULL);
}
else
{
- if (LogLevel > 9)
+ *mechlist = NULL; /* be paranoid... */
+ if (result == SASL_OK && LogLevel > 9)
sm_syslog(LOG_WARNING, NOQID,
- "SASL error: listmech=%d, num=%d",
- result, num);
- num = 0;
+ "AUTH warning: no mechanisms");
}
return num;
}
-
- /*
+/*
** PROXY_POLICY -- define proxy policy for AUTH
**
** Parameters:
-** conntext -- unused
-** auth_identity -- authentication identity
-** requested_user -- authorization identity
-** user -- allowed user (output)
-** errstr -- possible error string (output)
+** context -- unused.
+** auth_identity -- authentication identity.
+** requested_user -- authorization identity.
+** user -- allowed user (output).
+** errstr -- possible error string (output).
**
** Returns:
** ok?
@@ -2932,1275 +3779,139 @@ proxy_policy(context, auth_identity, requested_user, user, errstr)
*user = newstr(auth_identity);
return SASL_OK;
}
+#endif /* SASL */
-# endif /* SASL */
-
-# if STARTTLS
-# if !TLS_NO_RSA
-RSA *rsa_tmp; /* temporary RSA key */
-static RSA * tmp_rsa_key __P((SSL *, int, int));
-# endif /* !TLS_NO_RSA */
-
-# if !NO_DH
-static DH *get_dh512 __P((void));
-
-static unsigned char dh512_p[] =
-{
- 0xDA,0x58,0x3C,0x16,0xD9,0x85,0x22,0x89,0xD0,0xE4,0xAF,0x75,
- 0x6F,0x4C,0xCA,0x92,0xDD,0x4B,0xE5,0x33,0xB8,0x04,0xFB,0x0F,
- 0xED,0x94,0xEF,0x9C,0x8A,0x44,0x03,0xED,0x57,0x46,0x50,0xD3,
- 0x69,0x99,0xDB,0x29,0xD7,0x76,0x27,0x6B,0xA2,0xD3,0xD4,0x12,
- 0xE2,0x18,0xF4,0xDD,0x1E,0x08,0x4C,0xF6,0xD8,0x00,0x3E,0x7C,
- 0x47,0x74,0xE8,0x33
-};
-static unsigned char dh512_g[] =
-{
- 0x02
-};
-
-static DH *
-get_dh512()
-{
- DH *dh = NULL;
-
- if ((dh = DH_new()) == NULL)
- return(NULL);
- dh->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL);
- dh->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL);
- if ((dh->p == NULL) || (dh->g == NULL))
- return(NULL);
- return(dh);
-}
-# endif /* !NO_DH */
-
- /*
-** TLS_RAND_INIT -- initialize STARTTLS random generator
-**
-** Parameters:
-** randfile -- name of file with random data
-** logl -- loglevel
-**
-** Returns:
-** success/failure
-**
-** Side Effects:
-** initializes PRNG for tls library.
-*/
-
-#define MIN_RAND_BYTES 16 /* 128 bits */
-
-bool
-tls_rand_init(randfile, logl)
- char *randfile;
- int logl;
-{
-# ifndef HASURANDOMDEV
- /* not required if /dev/urandom exists, OpenSSL does it internally */
-#define RF_OK 0 /* randfile OK */
-#define RF_MISS 1 /* randfile == NULL || *randfile == '\0' */
-#define RF_UNKNOWN 2 /* unknown prefix for randfile */
-
-#define RI_NONE 0 /* no init yet */
-#define RI_SUCCESS 1 /* init was successful */
-#define RI_FAIL 2 /* init failed */
-
- bool ok;
- int randdef;
- static int done = RI_NONE;
-
- /*
- ** initialize PRNG
- */
-
- /* did we try this before? if yes: return old value */
- if (done != RI_NONE)
- return done == RI_SUCCESS;
-
- /* set default values */
- ok = FALSE;
- done = RI_FAIL;
- randdef = (randfile == NULL || *randfile == '\0') ? RF_MISS : RF_OK;
-# if EGD
- if (randdef == RF_OK && strncasecmp(randfile, "egd:", 4) == 0)
- {
- randfile += 4;
- if (RAND_egd(randfile) < 0)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: RAND_egd(%s) failed: random number generator not seeded",
- randfile);
- }
- else
- ok = TRUE;
- }
- else
-# endif /* EGD */
- if (randdef == RF_OK && strncasecmp(randfile, "file:", 5) == 0)
- {
- int fd;
- long sff;
- struct stat st;
-
- randfile += 5;
- sff = SFF_SAFEDIRPATH | SFF_NOWLINK
- | SFF_NOGWFILES | SFF_NOWWFILES
- | SFF_NOGRFILES | SFF_NOWRFILES
- | SFF_MUSTOWN | SFF_ROOTOK | SFF_OPENASROOT;
- if ((fd = safeopen(randfile, O_RDONLY, 0, sff)) >= 0)
- {
- if (fstat(fd, &st) < 0)
- {
- if (LogLevel > logl)
- sm_syslog(LOG_ERR, NOQID,
- "TLS: can't fstat(%s)",
- randfile);
- }
- else
- {
- bool use, problem;
-
- use = TRUE;
- problem = FALSE;
- if (st.st_mtime + 600 < curtime())
- {
- use = bitnset(DBS_INSUFFICIENTENTROPY,
- DontBlameSendmail);
- problem = TRUE;
- if (LogLevel > logl)
- sm_syslog(LOG_ERR, NOQID,
- "TLS: RandFile %s too old: %s",
- randfile,
- use ? "unsafe" :
- "unusable");
- }
- if (use && st.st_size < MIN_RAND_BYTES)
- {
- use = bitnset(DBS_INSUFFICIENTENTROPY,
- DontBlameSendmail);
- problem = TRUE;
- if (LogLevel > logl)
- sm_syslog(LOG_ERR, NOQID,
- "TLS: size(%s) < %d: %s",
- randfile,
- MIN_RAND_BYTES,
- use ? "unsafe" :
- "unusable");
- }
- if (use)
- ok = RAND_load_file(randfile, -1) >=
- MIN_RAND_BYTES;
- if (use && !ok)
- {
- if (LogLevel > logl)
- sm_syslog(LOG_WARNING,
- NOQID,
- "TLS: RAND_load_file(%s) failed: random number generator not seeded",
- randfile);
- }
- if (problem)
- ok = FALSE;
- }
- if (ok || bitnset(DBS_INSUFFICIENTENTROPY,
- DontBlameSendmail))
- {
- /* add this even if fstat() failed */
- RAND_seed((void *) &st, sizeof st);
- }
- (void) close(fd);
- }
- else
- {
- if (LogLevel > logl)
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: Warning: safeopen(%s) failed",
- randfile);
- }
- }
- else if (randdef == RF_OK)
- {
- if (LogLevel > logl)
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: Error: no proper random file definition %s",
- randfile);
- randdef = RF_UNKNOWN;
- }
- if (randdef == RF_MISS)
- {
- if (LogLevel > logl)
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: Error: missing random file definition");
- }
- if (!ok && bitnset(DBS_INSUFFICIENTENTROPY, DontBlameSendmail))
- {
- int i;
- long r;
- unsigned char buf[MIN_RAND_BYTES];
-
- /* assert((MIN_RAND_BYTES % sizeof(long)) == 0); */
- for (i = 0; i <= sizeof(buf) - sizeof(long); i += sizeof(long))
- {
- r = get_random();
- (void) memcpy(buf + i, (void *) &r, sizeof(long));
- }
- RAND_seed(buf, sizeof buf);
- if (LogLevel > logl)
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: Warning: random number generator not properly seeded");
- ok = TRUE;
- }
- done = ok ? RI_SUCCESS : RI_FAIL;
- return ok;
-# else /* !HASURANDOMDEV */
- return TRUE;
-# endif /* !HASURANDOMDEV */
-}
-
+#if STARTTLS
/*
-** 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
+** INITSRVTLS -- initialize server side TLS
**
** Parameters:
-** none.
+** tls_ok -- should tls initialization be done?
**
** Returns:
** succeeded?
**
** Side Effects:
-** Sets tls_ok_srv static, even when called from main()
+** sets tls_ok_srv which is a static variable in this module.
+** Do NOT remove assignments to it!
*/
bool
-init_tls_library()
+initsrvtls(tls_ok)
+ bool tls_ok;
{
- /*
- ** basic TLS initialization
- ** ignore result for now
- */
-
- SSL_library_init();
- SSL_load_error_strings();
-# if 0
- /* this is currently a macro for SSL_library_init */
- SSLeay_add_ssl_algorithms();
-# endif /* 0 */
-
- /* initialize PRNG */
- tls_ok_srv = tls_rand_init(RandFile, 7);
+ if (!tls_ok)
+ return false;
+ /* do NOT remove assignment */
+ tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, true, SrvCERTfile,
+ Srvkeyfile, CACERTpath, CACERTfile, DHParams);
return tls_ok_srv;
}
- /*
-** INITTLS -- initialize TLS
+#endif /* STARTTLS */
+/*
+** SRVFEATURES -- get features for SMTP server
**
** Parameters:
-** ctx -- pointer to context
-** req -- requirements for initialization (see sendmail.h)
-** srv -- server side?
-** certfile -- filename of certificate
-** keyfile -- filename of private key
-** cacertpath -- path to CAs
-** cacertfile -- file with CA
-** dhparam -- parameters for DH
+** e -- envelope (should be session context).
+** clientname -- name of client.
+** features -- default features for this invocation.
**
** Returns:
-** succeeded?
+** server features.
*/
-bool
-inittls(ctx, req, srv, certfile, keyfile, cacertpath, cacertfile, dhparam)
- SSL_CTX **ctx;
- u_long req;
- bool srv;
- char *certfile, *keyfile, *cacertpath, *cacertfile, *dhparam;
+/* table with options: it uses just one character, how about strings? */
+static struct
{
-# if !NO_DH
- static DH *dh = NULL;
-# endif /* !NO_DH */
- int r;
- bool ok;
- long sff, status;
- char *who;
-# if _FFR_TLS_1
- char *cf2, *kf2;
-# endif /* _FFR_TLS_1 */
-
- status = TLS_S_NONE;
- who = srv ? "srv" : "clt";
- if (ctx == NULL)
- syserr("TLS: %s:inittls: ctx == NULL", who);
-
- /* already initialized? (we could re-init...) */
- if (*ctx != NULL)
- return TRUE;
-
- /* PRNG seeded? */
- if (!tls_rand_init(RandFile, 10))
- return FALSE;
-
- /* let's start with the assumption it will work */
- ok = TRUE;
-
-# if _FFR_TLS_1
- /*
- ** look for a second filename: it must be separated by a ','
- ** no blanks allowed (they won't be skipped).
- ** we change a global variable here! this change will be undone
- ** before return from the function but only if it returns TRUE.
- ** this isn't a problem since in a failure case this function
- ** won't be called again with the same (overwritten) values.
- ** otherwise each return must be replaced with a goto endinittls.
- */
- cf2 = NULL;
- kf2 = NULL;
- if (certfile != NULL && (cf2 = strchr(certfile, ',')) != NULL)
- {
- *cf2++ = '\0';
- if (keyfile != NULL && (kf2 = strchr(keyfile, ',')) != NULL)
- *kf2++ = '\0';
- }
-# endif /* _FFR_TLS_1 */
-
- /*
- ** what do we require from the client?
- ** must it have CERTs?
- ** introduce an option and decide based on that
- */
-
- TLS_OK_F(certfile, "CertFile", bitset(TLS_I_CERT_EX, req),
- TLS_S_CERT_EX);
- TLS_OK_F(keyfile, "KeyFile", bitset(TLS_I_KEY_EX, req),
- TLS_S_KEY_EX);
- TLS_OK_F(cacertpath, "CACERTPath", bitset(TLS_I_CERTP_EX, req),
- TLS_S_CERTP_EX);
- TLS_OK_F(cacertfile, "CACERTFile", bitset(TLS_I_CERTF_EX, req),
- TLS_S_CERTF_EX);
-
-# if _FFR_TLS_1
- if (cf2 != NULL)
- {
- TLS_OK_F(cf2, "CertFile", bitset(TLS_I_CERT_EX, req),
- TLS_S_CERT2_EX);
- }
- if (kf2 != NULL)
- {
- TLS_OK_F(kf2, "KeyFile", bitset(TLS_I_KEY_EX, req),
- TLS_S_KEY2_EX);
- }
-# endif /* _FFR_TLS_1 */
-
- /*
- ** valid values for dhparam are (only the first char is checked)
- ** none no parameters: don't use DH
- ** 512 generate 512 bit parameters (fixed)
- ** 1024 generate 1024 bit parameters
- ** /file/name read parameters from /file/name
- ** default is: 1024 for server, 512 for client (OK? XXX)
- */
- if (bitset(TLS_I_TRY_DH, req))
- {
- if (dhparam != NULL)
- {
- char c = *dhparam;
-
- if (c == '1')
- req |= TLS_I_DH1024;
- else if (c == '5')
- req |= TLS_I_DH512;
- else if (c != 'n' && c != 'N' && c != '/')
- {
- if (LogLevel > 12)
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: illegal value '%s' for DHParam",
- dhparam);
- dhparam = NULL;
- }
- }
- if (dhparam == NULL)
- dhparam = srv ? "1" : "5";
- else if (*dhparam == '/')
- {
- TLS_OK_F(dhparam, "DHParameters",
- bitset(TLS_I_DHPAR_EX, req),
- TLS_S_DHPAR_EX);
- }
- }
- if (!ok)
- return ok;
-
- /* certfile etc. must be "safe". */
- sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK
- | SFF_NOGWFILES | SFF_NOWWFILES
- | SFF_MUSTOWN | SFF_ROOTOK | SFF_OPENASROOT;
- if (DontLockReadFiles)
- sff |= SFF_NOLOCK;
-
- TLS_SAFE_F(certfile, sff | TLS_UNR(TLS_I_CERT_UNR, req),
- bitset(TLS_I_CERT_EX, req),
- bitset(TLS_S_CERT_EX, status), TLS_S_CERT_OK);
- TLS_SAFE_F(keyfile, sff | TLS_UNR(TLS_I_KEY_UNR, req),
- bitset(TLS_I_KEY_EX, req),
- bitset(TLS_S_KEY_EX, status), TLS_S_KEY_OK);
- TLS_SAFE_F(cacertfile, sff | TLS_UNR(TLS_I_CERTF_UNR, req),
- bitset(TLS_I_CERTF_EX, req),
- bitset(TLS_S_CERTF_EX, status), TLS_S_CERTF_OK);
- TLS_SAFE_F(dhparam, sff | TLS_UNR(TLS_I_DHPAR_UNR, req),
- bitset(TLS_I_DHPAR_EX, req),
- bitset(TLS_S_DHPAR_EX, status), TLS_S_DHPAR_OK);
- if (!ok)
- return ok;
-# if _FFR_TLS_1
- if (cf2 != NULL)
- {
- TLS_SAFE_F(cf2, sff | TLS_UNR(TLS_I_CERT_UNR, req),
- bitset(TLS_I_CERT_EX, req),
- bitset(TLS_S_CERT2_EX, status), TLS_S_CERT2_OK);
- }
- if (kf2 != NULL)
- {
- TLS_SAFE_F(kf2, sff | TLS_UNR(TLS_I_KEY_UNR, req),
- bitset(TLS_I_KEY_EX, req),
- bitset(TLS_S_KEY2_EX, status), TLS_S_KEY2_OK);
- }
-# endif /* _FFR_TLS_1 */
-
- /* create a method and a new context */
- if (srv)
- {
- if ((*ctx = SSL_CTX_new(SSLv23_server_method())) == NULL)
- {
- if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: SSL_CTX_new(SSLv23_server_method()) failed");
- if (LogLevel > 9)
- tlslogerr();
- return FALSE;
- }
- }
- else
- {
- if ((*ctx = SSL_CTX_new(SSLv23_client_method())) == NULL)
- {
- if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: SSL_CTX_new(SSLv23_client_method()) failed");
- if (LogLevel > 9)
- tlslogerr();
- return FALSE;
- }
- }
-
-# if TLS_NO_RSA
- /* turn off backward compatibility, required for no-rsa */
- SSL_CTX_set_options(*ctx, SSL_OP_NO_SSLv2);
-# endif /* TLS_NO_RSA */
-
+ char srvf_opt;
+ unsigned int srvf_flag;
+} srv_feat_table[] =
+{
+ { 'A', SRV_OFFER_AUTH },
+ { 'B', SRV_OFFER_VERB },
+ { 'D', SRV_OFFER_DSN },
+ { 'E', SRV_OFFER_ETRN },
+ { 'L', SRV_REQ_AUTH }, /* not documented in 8.12 */
+#if PIPELINING
+# if _FFR_NO_PIPE
+ { 'N', SRV_NO_PIPE },
+# endif /* _FFR_NO_PIPE */
+ { 'P', SRV_OFFER_PIPE },
+#endif /* PIPELINING */
+ { 'R', SRV_VRFY_CLT },
+ { 'S', SRV_OFFER_TLS },
+/* { 'T', SRV_TMP_FAIL }, */
+ { 'V', SRV_VRFY_CLT },
+ { 'X', SRV_OFFER_EXPN },
+/* { 'Y', SRV_OFFER_VRFY }, */
+ { '\0', SRV_NONE }
+};
-# if !TLS_NO_RSA
- /*
- ** Create a temporary RSA key
- ** XXX Maybe we shouldn't create this always (even though it
- ** is only at startup).
- ** It is a time-consuming operation and it is not always necessary.
- ** maybe we should do it only on demand...
- */
- if (bitset(TLS_I_RSA_TMP, req) &&
- (rsa_tmp = RSA_generate_key(RSA_KEYLENGTH, RSA_F4, NULL,
- NULL)) == NULL
- )
- {
- if (LogLevel > 7)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: RSA_generate_key failed",
- who);
- if (LogLevel > 9)
- tlslogerr();
- }
- return FALSE;
- }
-# endif /* !TLS_NO_RSA */
+static unsigned int
+srvfeatures(e, clientname, features)
+ ENVELOPE *e;
+ char *clientname;
+ unsigned int features;
+{
+ int r, i, j;
+ char **pvp, c, opt;
+ char pvpbuf[PSBUFSIZE];
+
+ pvp = NULL;
+ r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf,
+ sizeof(pvpbuf));
+ if (r != EX_OK)
+ return features;
+ if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
+ return features;
+ if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
+ return SRV_TMP_FAIL;
/*
- ** load private key
- ** XXX change this for DSA-only version
+ ** General rule (see sendmail.h, d_flags):
+ ** lower case: required/offered, upper case: Not required/available
+ **
+ ** Since we can change some features per daemon, we have both
+ ** cases here: turn on/off a feature.
*/
- if (bitset(TLS_S_KEY_OK, status) &&
- SSL_CTX_use_PrivateKey_file(*ctx, keyfile,
- SSL_FILETYPE_PEM) <= 0)
- {
- if (LogLevel > 7)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: SSL_CTX_use_PrivateKey_file(%s) failed",
- who, keyfile);
- if (LogLevel > 9)
- tlslogerr();
- }
- if (bitset(TLS_I_USE_KEY, req))
- return FALSE;
- }
-
- /* get the certificate file */
- if (bitset(TLS_S_CERT_OK, status) &&
- SSL_CTX_use_certificate_file(*ctx, certfile,
- SSL_FILETYPE_PEM) <= 0)
- {
- if (LogLevel > 7)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: SSL_CTX_use_certificate_file(%s) failed",
- who, certfile);
- if (LogLevel > 9)
- tlslogerr();
- }
- if (bitset(TLS_I_USE_CERT, req))
- return FALSE;
- }
-
- /* check the private key */
- if (bitset(TLS_S_KEY_OK, status) &&
- (r = SSL_CTX_check_private_key(*ctx)) <= 0)
- {
- /* Private key does not match the certificate public key */
- if (LogLevel > 5)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: SSL_CTX_check_private_key failed(%s): %d",
- who, keyfile, r);
- if (LogLevel > 9)
- tlslogerr();
- }
- if (bitset(TLS_I_USE_KEY, req))
- return FALSE;
- }
-# if _FFR_TLS_1
- /* XXX this code is pretty much duplicated from above! */
-
- /* load private key */
- if (bitset(TLS_S_KEY2_OK, status) &&
- SSL_CTX_use_PrivateKey_file(*ctx, kf2, SSL_FILETYPE_PEM) <= 0)
+ for (i = 1; pvp[i] != NULL; i++)
{
- if (LogLevel > 7)
+ c = pvp[i][0];
+ j = 0;
+ for (;;)
{
- 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)
+ if ((opt = srv_feat_table[j].srvf_opt) == '\0')
{
- 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 (LogLevel > 9)
+ sm_syslog(LOG_WARNING, e->e_id,
+ "srvfeatures: unknown feature %s",
+ pvp[i]);
+ break;
}
- if (bitset(TLS_I_REQ_DH, req))
- return FALSE;
- }
- else
- {
- SSL_CTX_set_tmp_dh(*ctx, dh);
-
- /* important to avoid small subgroup attacks */
- SSL_CTX_set_options(*ctx, SSL_OP_SINGLE_DH_USE);
- if (LogLevel > 12)
- sm_syslog(LOG_INFO, NOQID,
- "TLS: %s: Diffie-Hellman init, key=%d bit (%c)",
- who, 8 * DH_size(dh), *dhparam);
- DH_free(dh);
- }
- }
-# endif /* !NO_DH */
-
-
- /* XXX do we need this cache here? */
- if (bitset(TLS_I_CACHE, req))
- SSL_CTX_sess_set_cache_size(*ctx, 128);
- /* timeout? SSL_CTX_set_timeout(*ctx, TimeOut...); */
-
- /* load certificate locations and default CA paths */
- if (bitset(TLS_S_CERTP_EX, status) && bitset(TLS_S_CERTF_EX, status))
- {
- if ((r = SSL_CTX_load_verify_locations(*ctx, cacertfile,
- cacertpath)) == 1)
- {
-# if !TLS_NO_RSA
- if (bitset(TLS_I_RSA_TMP, req))
- SSL_CTX_set_tmp_rsa_callback(*ctx, tmp_rsa_key);
-# endif /* !TLS_NO_RSA */
-
- /* ask to verify the peer */
- SSL_CTX_set_verify(*ctx, SSL_VERIFY_PEER, NULL);
-
- /* install verify callback */
- SSL_CTX_set_cert_verify_callback(*ctx, tls_verify_cb,
- NULL);
- SSL_CTX_set_client_CA_list(*ctx,
- SSL_load_client_CA_file(cacertfile));
- }
- else
- {
- /*
- ** can't load CA data; do we care?
- ** the data is necessary to authenticate the client,
- ** which in turn would be necessary
- ** if we want to allow relaying based on it.
- */
- if (LogLevel > 5)
+ if (c == opt)
{
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: %d load verify locs %s, %s",
- who, r, cacertpath, cacertfile);
- if (LogLevel > 9)
- tlslogerr();
+ features &= ~(srv_feat_table[j].srvf_flag);
+ break;
}
- if (bitset(TLS_I_VRFY_LOC, req))
- return FALSE;
- }
- }
-
- /* XXX: make this dependent on an option? */
- if (tTd(96, 9))
- SSL_CTX_set_info_callback(*ctx, apps_ssl_info_cb);
-
-# if _FFR_TLS_1
- /*
- ** XXX install our own cipher list: option?
- */
- if (CipherList != NULL && *CipherList != '\0')
- {
- if (SSL_CTX_set_cipher_list(*ctx, CipherList) <= 0)
- {
- if (LogLevel > 7)
+ if (c == tolower(opt))
{
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: SSL_CTX_set_cipher_list(%s) failed, list ignored",
- who, CipherList);
-
- if (LogLevel > 9)
- tlslogerr();
+ features |= srv_feat_table[j].srvf_flag;
+ break;
}
- /* failure if setting to this list is required? */
+ ++j;
}
}
-# endif /* _FFR_TLS_1 */
- if (LogLevel > 12)
- sm_syslog(LOG_INFO, NOQID, "TLS: init(%s)=%d", who, ok);
-
-# if _FFR_TLS_1
-# if 0
- /*
- ** this label is required if we want to have a "clean" exit
- ** see the comments above at the initialization of cf2
- */
- endinittls:
-# endif /* 0 */
-
- /* undo damage to global variables */
- if (cf2 != NULL)
- *--cf2 = ',';
- if (kf2 != NULL)
- *--kf2 = ',';
-# endif /* _FFR_TLS_1 */
-
- return ok;
+ return features;
}
- /*
-** 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.
**
** Parameters:
** topic -- the topic we want help for.
-** e -- envelope
+** e -- envelope.
**
** Returns:
** none.
@@ -4216,11 +3927,11 @@ help(topic, e)
char *topic;
ENVELOPE *e;
{
- register FILE *hf;
+ register SM_FILE_T *hf;
register char *p;
int len;
bool noinfo;
- bool first = TRUE;
+ bool first = true;
long sff = SFF_OPENASROOT|SFF_REGONLY;
char buf[MAXLINE];
char inp[MAXLINE];
@@ -4245,17 +3956,17 @@ help(topic, e)
if (topic == NULL || *topic == '\0')
{
topic = "smtp";
- noinfo = FALSE;
+ noinfo = false;
}
else
{
makelower(topic);
- noinfo = TRUE;
+ noinfo = true;
}
len = strlen(topic);
- while (fgets(buf, sizeof buf, hf) != NULL)
+ while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
{
if (buf[0] == '#')
{
@@ -4264,8 +3975,8 @@ help(topic, e)
{
int h;
- if (sscanf(buf + strlen(HELPVSTR), "%d",
- &h) == 1)
+ if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d",
+ &h) == 1)
foundvers = h;
}
continue;
@@ -4274,7 +3985,7 @@ help(topic, e)
{
if (first)
{
- first = FALSE;
+ first = false;
/* print version if no/old vers# in file */
if (foundvers < 2 && !noinfo)
@@ -4285,7 +3996,7 @@ help(topic, e)
p = buf + strlen(buf) - 1;
else
p++;
- fixcrlf(p, TRUE);
+ fixcrlf(p, true);
if (foundvers >= 2)
{
translate_dollars(p);
@@ -4293,7 +4004,7 @@ help(topic, e)
p = inp;
}
message("214-2.0.0 %s", p);
- noinfo = FALSE;
+ noinfo = false;
}
}
@@ -4313,5 +4024,5 @@ help(topic, e)
foundvers = 0;
}
- (void) fclose(hf);
+ (void) sm_io_close(hf, SM_TIME_DEFAULT);
}
diff --git a/contrib/sendmail/src/stab.c b/contrib/sendmail/src/stab.c
index 82775bc..b2ad12d 100644
--- a/contrib/sendmail/src/stab.c
+++ b/contrib/sendmail/src/stab.c
@@ -11,12 +11,10 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: stab.c,v 8.40.16.7 2001/05/07 22:06:41 gshapiro Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
+SM_RCSID("@(#)$Id: stab.c,v 8.86 2001/12/29 04:27:56 ca Exp $")
+
/*
** STAB -- manage the symbol table
**
@@ -24,8 +22,7 @@ static char id[] = "@(#)$Id: stab.c,v 8.40.16.7 2001/05/07 22:06:41 gshapiro Exp
** name -- the name to be looked up or inserted.
** type -- the type of symbol.
** op -- what to do:
-** ST_ENTER -- enter the name if not
-** already present.
+** ST_ENTER -- enter the name if not already present.
** ST_FIND -- find it only.
**
** Returns:
@@ -37,6 +34,7 @@ static char id[] = "@(#)$Id: stab.c,v 8.40.16.7 2001/05/07 22:06:41 gshapiro Exp
*/
#define STABSIZE 2003
+#define SM_LOWER(c) ((isascii(c) && isupper(c)) ? tolower(c) : (c))
static STAB *SymTab[STABSIZE];
@@ -53,7 +51,7 @@ stab(name, type, op)
int len;
if (tTd(36, 5))
- dprintf("STAB: %s %d ", name, type);
+ sm_dprintf("STAB: %s %d ", name, type);
/*
** Compute the hashing function
@@ -61,22 +59,22 @@ stab(name, type, op)
hfunc = type;
for (p = name; *p != '\0'; p++)
- hfunc = ((hfunc << 1) ^ (lower(*p) & 0377)) % STABSIZE;
+ hfunc = ((hfunc << 1) ^ (SM_LOWER(*p) & 0377)) % STABSIZE;
if (tTd(36, 9))
- dprintf("(hfunc=%d) ", hfunc);
+ sm_dprintf("(hfunc=%d) ", hfunc);
ps = &SymTab[hfunc];
if (type == ST_MACRO || type == ST_RULESET)
{
while ((s = *ps) != NULL &&
- (s->s_type != type || strcmp(name, s->s_name)))
+ (s->s_symtype != type || strcmp(name, s->s_name)))
ps = &s->s_next;
}
else
{
while ((s = *ps) != NULL &&
- (s->s_type != type || strcasecmp(name, s->s_name)))
+ (s->s_symtype != type || sm_strcasecmp(name, s->s_name)))
ps = &s->s_next;
}
@@ -89,13 +87,13 @@ stab(name, type, op)
if (tTd(36, 5))
{
if (s == NULL)
- dprintf("not found\n");
+ sm_dprintf("not found\n");
else
{
long *lp = (long *) s->s_class;
- dprintf("type %d val %lx %lx %lx %lx\n",
- s->s_type, lp[0], lp[1], lp[2], lp[3]);
+ sm_dprintf("type %d val %lx %lx %lx %lx\n",
+ s->s_symtype, lp[0], lp[1], lp[2], lp[3]);
}
}
return s;
@@ -106,7 +104,7 @@ stab(name, type, op)
*/
if (tTd(36, 5))
- dprintf("entered\n");
+ sm_dprintf("entered\n");
/* determine size of new entry */
switch (type)
@@ -159,21 +157,25 @@ stab(name, type, op)
len = sizeof s->s_service;
break;
-#ifdef LDAPMAP
+#if LDAPMAP
case ST_LMAP:
len = sizeof s->s_lmap;
break;
#endif /* LDAPMAP */
-#if _FFR_MILTER
+#if MILTER
case ST_MILTER:
len = sizeof s->s_milter;
break;
-#endif /* _FFR_MILTER */
+#endif /* MILTER */
+
+ case ST_QUEUE:
+ len = sizeof s->s_quegrp;
+ break;
default:
/*
- ** Each mailer has it's own MCI stab entry:
+ ** Each mailer has its own MCI stab entry:
**
** s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
**
@@ -192,14 +194,13 @@ stab(name, type, op)
len += sizeof *s - sizeof s->s_value;
if (tTd(36, 15))
- dprintf("size of stab entry: %d\n", len);
+ sm_dprintf("size of stab entry: %d\n", len);
/* make new entry */
- s = (STAB *) xalloc(len);
+ s = (STAB *) sm_pmalloc_x(len);
memset((char *) s, '\0', len);
- s->s_name = newstr(name);
- s->s_type = type;
- s->s_len = len;
+ s->s_name = sm_pstrdup_x(name);
+ s->s_symtype = type;
/* link it in */
*ps = s;
@@ -210,12 +211,12 @@ stab(name, type, op)
return s;
}
- /*
+/*
** STABAPPLY -- apply function to all stab entries
**
** Parameters:
-** func -- the function to apply. It will be given one
-** parameter (the stab entry).
+** func -- the function to apply. It will be given two
+** parameters (the stab entry and the arg).
** arg -- an arbitrary argument, passed to func.
**
** Returns:
@@ -235,13 +236,13 @@ stabapply(func, arg)
for (s = *shead; s != NULL; s = s->s_next)
{
if (tTd(36, 90))
- dprintf("stabapply: trying %d/%s\n",
- s->s_type, s->s_name);
+ sm_dprintf("stabapply: trying %d/%s\n",
+ s->s_symtype, s->s_name);
func(s, arg);
}
}
}
- /*
+/*
** QUEUEUP_MACROS -- queueup the macros in a class
**
** Write the macros listed in the specified class into the
@@ -249,7 +250,7 @@ stabapply(func, arg)
**
** Parameters:
** class -- class ID.
-** qfp -- file pointer to the qf file.
+** qfp -- file pointer to the queue file.
** e -- the envelope.
**
** Returns:
@@ -259,7 +260,7 @@ stabapply(func, arg)
void
queueup_macros(class, qfp, e)
int class;
- FILE *qfp;
+ SM_FILE_T *qfp;
ENVELOPE *e;
{
register STAB **shead;
@@ -276,39 +277,21 @@ queueup_macros(class, qfp, e)
int m;
char *p;
- if (s->s_type == ST_CLASS &&
- bitnset(class, s->s_class) &&
- (m = macid(s->s_name, NULL)) != '\0' &&
+ if (s->s_symtype == ST_CLASS &&
+ bitnset(bitidx(class), s->s_class) &&
+ (m = macid(s->s_name)) != '\0' &&
(p = macvalue(m, e)) != NULL)
{
- /*
- ** HACK ALERT: Unfortunately, 8.10 and
- ** 8.11 reused the ${if_addr} and
- ** ${if_family} macros for both the incoming
- ** interface address/family (getrequests())
- ** and the outgoing interface address/family
- ** (makeconnection()). In order for D_BINDIF
- ** to work properly, have to preserve the
- ** incoming information in the queue file for
- ** later delivery attempts. The original
- ** information is stored in the envelope
- ** in readqf() so it can be stored in
- ** queueup_macros(). This should be fixed
- ** in 8.12.
- */
-
- if (e->e_if_macros[EIF_ADDR] != NULL &&
- strcmp(s->s_name, "{if_addr}") == 0)
- p = e->e_if_macros[EIF_ADDR];
-
- fprintf(qfp, "$%s%s\n",
- s->s_name,
- denlstring(p, TRUE, FALSE));
+ (void) sm_io_fprintf(qfp, SM_TIME_DEFAULT,
+ "$%s%s\n",
+ s->s_name,
+ denlstring(p, true,
+ false));
}
}
}
}
- /*
+/*
** COPY_CLASS -- copy class members from one class to another
**
** Parameters:
@@ -333,9 +316,154 @@ copy_class(src, dst)
{
for (s = *shead; s != NULL; s = s->s_next)
{
- if (s->s_type == ST_CLASS &&
+ if (s->s_symtype == ST_CLASS &&
bitnset(src, s->s_class))
setbitn(dst, s->s_class);
}
}
}
+
+/*
+** RMEXPSTAB -- remove expired entries from SymTab.
+**
+** These entries need to be removed in long-running processes,
+** e.g., persistent queue runners, to avoid consuming memory.
+**
+** XXX It might be useful to restrict the maximum TTL to avoid
+** caching data very long.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** can remove entries from the symbol table.
+*/
+
+#define SM_STAB_FREE(x) \
+ do \
+ { \
+ char *o = (x); \
+ (x) = NULL; \
+ if (o != NULL) \
+ sm_free(o); \
+ } while (0)
+
+void
+rmexpstab()
+{
+ int i;
+ STAB *s, *p, *f;
+ time_t now;
+
+ now = curtime();
+ for (i = 0; i < STABSIZE; i++)
+ {
+ p = NULL;
+ s = SymTab[i];
+ while (s != NULL)
+ {
+ switch (s->s_symtype)
+ {
+ case ST_HOSTSIG:
+ if (s->s_hostsig.hs_exp >= now)
+ goto next; /* not expired */
+ SM_STAB_FREE(s->s_hostsig.hs_sig); /* XXX */
+ break;
+
+ case ST_NAMECANON:
+ if (s->s_namecanon.nc_exp >= now)
+ goto next; /* not expired */
+ SM_STAB_FREE(s->s_namecanon.nc_cname); /* XXX */
+ break;
+
+ default:
+ if (s->s_symtype >= ST_MCI)
+ {
+ /* call mci_uncache? */
+ SM_STAB_FREE(s->s_mci.mci_status);
+ SM_STAB_FREE(s->s_mci.mci_rstatus);
+ SM_STAB_FREE(s->s_mci.mci_heloname);
+#if 0
+ /* not dynamically allocated */
+ SM_STAB_FREE(s->s_mci.mci_host);
+ SM_STAB_FREE(s->s_mci.mci_tolist);
+#endif /* 0 */
+#if SASL
+ /* should always by NULL */
+ SM_STAB_FREE(s->s_mci.mci_sasl_string);
+#endif /* SASL */
+ if (s->s_mci.mci_rpool != NULL)
+ {
+ sm_rpool_free(s->s_mci.mci_rpool);
+ s->s_mci.mci_macro.mac_rpool = NULL;
+ s->s_mci.mci_rpool = NULL;
+ }
+ break;
+ }
+ next:
+ p = s;
+ s = s->s_next;
+ continue;
+ }
+
+ /* remove entry */
+ SM_STAB_FREE(s->s_name); /* XXX */
+ f = s;
+ s = s->s_next;
+ sm_free(f); /* XXX */
+ if (p == NULL)
+ SymTab[i] = s;
+ else
+ p->s_next = s;
+ }
+ }
+}
+
+#if SM_HEAP_CHECK
+/*
+** DUMPSTAB -- dump symbol table.
+**
+** For debugging.
+*/
+
+#define MAXSTTYPES (ST_MCI + 1)
+
+void
+dumpstab()
+{
+ int i, t, total, types[MAXSTTYPES];
+ STAB *s;
+ static int prevt[MAXSTTYPES], prev = 0;
+
+ total = 0;
+ for (i = 0; i < MAXSTTYPES; i++)
+ types[i] = 0;
+ for (i = 0; i < STABSIZE; i++)
+ {
+ s = SymTab[i];
+ while (s != NULL)
+ {
+ ++total;
+ t = s->s_symtype;
+ if (t > MAXSTTYPES - 1)
+ t = MAXSTTYPES - 1;
+ types[t]++;
+ s = s->s_next;
+ }
+ }
+ sm_syslog(LOG_INFO, NOQID, "stab: total=%d (%d)", total, total - prev);
+ prev = total;
+ for (i = 0; i < MAXSTTYPES; i++)
+ {
+ if (types[i] != 0)
+ {
+ sm_syslog(LOG_INFO, NOQID, "stab: type[%2d]=%2d (%d)",
+ i, types[i], types[i] - prevt[i]);
+ }
+ prevt[i] = types[i];
+ }
+}
+#endif /* SM_HEAP_CHECK */
diff --git a/contrib/sendmail/src/stats.c b/contrib/sendmail/src/stats.c
index e03de5f..0ae8ff4 100644
--- a/contrib/sendmail/src/stats.c
+++ b/contrib/sendmail/src/stats.c
@@ -11,28 +11,26 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: stats.c,v 8.36.14.5 2001/02/14 04:07:30 gshapiro Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
-#include <sendmail/mailstats.h>
+SM_RCSID("@(#)$Id: stats.c,v 8.52 2001/11/21 13:39:14 gshapiro Exp $")
+
+#include <sendmail/mailstats.h>
static struct statistics Stat;
-static bool GotStats = FALSE; /* set when we have stats to merge */
+static bool GotStats = false; /* set when we have stats to merge */
/* See http://physics.nist.gov/cuu/Units/binary.html */
#define ONE_K 1000 /* one thousand (twenty-four?) */
#define KBYTES(x) (((x) + (ONE_K - 1)) / ONE_K)
- /*
+/*
** MARKSTATS -- mark statistics
**
** Parameters:
** e -- the envelope.
** to -- to address.
-** reject -- whether this is a rejection.
+** type -- type of stats this represents.
**
** Returns:
** none.
@@ -42,13 +40,21 @@ static bool GotStats = FALSE; /* set when we have stats to merge */
*/
void
-markstats(e, to, reject)
+markstats(e, to, type)
register ENVELOPE *e;
register ADDRESS *to;
- bool reject;
+ int type;
{
- if (reject)
+ switch (type)
{
+#if _FFR_QUARANTINE
+ case STATS_QUARANTINE:
+ if (e->e_from.q_mailer != NULL)
+ Stat.stat_nq[e->e_from.q_mailer->m_mno]++;
+ break;
+#endif /* _FFR_QUARANTINE */
+
+ case STATS_REJECT:
if (e->e_from.q_mailer != NULL)
{
if (bitset(EF_DISCARD, e->e_flags))
@@ -57,28 +63,36 @@ markstats(e, to, reject)
Stat.stat_nr[e->e_from.q_mailer->m_mno]++;
}
Stat.stat_cr++;
- }
- else if (to == NULL)
- {
- Stat.stat_cf++;
- if (e->e_from.q_mailer != NULL)
+ break;
+
+ case STATS_NORMAL:
+ if (to == NULL)
{
- Stat.stat_nf[e->e_from.q_mailer->m_mno]++;
- Stat.stat_bf[e->e_from.q_mailer->m_mno] +=
- KBYTES(e->e_msgsize);
+ Stat.stat_cf++;
+ if (e->e_from.q_mailer != NULL)
+ {
+ Stat.stat_nf[e->e_from.q_mailer->m_mno]++;
+ Stat.stat_bf[e->e_from.q_mailer->m_mno] +=
+ KBYTES(e->e_msgsize);
+ }
}
- }
- else
- {
- Stat.stat_ct++;
- Stat.stat_nt[to->q_mailer->m_mno]++;
- Stat.stat_bt[to->q_mailer->m_mno] += KBYTES(e->e_msgsize);
+ else
+ {
+ Stat.stat_ct++;
+ Stat.stat_nt[to->q_mailer->m_mno]++;
+ Stat.stat_bt[to->q_mailer->m_mno] += KBYTES(e->e_msgsize);
+ }
+ break;
+
+ default:
+ /* Silently ignore bogus call */
+ return;
}
- GotStats = TRUE;
+ GotStats = true;
}
- /*
+/*
** CLEARSTATS -- clear statistics structure
**
** Parameters:
@@ -96,9 +110,9 @@ clearstats()
{
/* clear the structure to avoid future disappointment */
memset(&Stat, '\0', sizeof Stat);
- GotStats = FALSE;
+ GotStats = false;
}
- /*
+/*
** POSTSTATS -- post statistics in the statistics file
**
** Parameters:
@@ -115,13 +129,15 @@ void
poststats(sfile)
char *sfile;
{
- register int fd;
+ int fd;
+ static bool entered = false;
long sff = SFF_REGONLY|SFF_OPENASROOT;
struct statistics stats;
extern off_t lseek();
- if (sfile == NULL || !GotStats)
+ if (sfile == NULL || *sfile == '\0' || !GotStats || entered)
return;
+ entered = true;
(void) time(&Stat.stat_itime);
Stat.stat_size = sizeof Stat;
@@ -138,8 +154,9 @@ poststats(sfile)
{
if (LogLevel > 12)
sm_syslog(LOG_INFO, NOQID, "poststats: %s: %s",
- sfile, errstring(errno));
+ sfile, sm_errstring(errno));
errno = 0;
+ entered = false;
return;
}
if (read(fd, (char *) &stats, sizeof stats) == sizeof stats &&
@@ -158,6 +175,9 @@ poststats(sfile)
stats.stat_bt[i] += Stat.stat_bt[i];
stats.stat_nr[i] += Stat.stat_nr[i];
stats.stat_nd[i] += Stat.stat_nd[i];
+#if _FFR_QUARANTINE
+ stats.stat_nq[i] += Stat.stat_nq[i];
+#endif /* _FFR_QUARANTINE */
}
stats.stat_cr += Stat.stat_cr;
stats.stat_ct += Stat.stat_ct;
@@ -173,4 +193,5 @@ poststats(sfile)
/* clear the structure to avoid future disappointment */
clearstats();
+ entered = false;
}
diff --git a/contrib/sendmail/src/statusd_shm.h b/contrib/sendmail/src/statusd_shm.h
index c48b2fd..7d88964 100644
--- a/contrib/sendmail/src/statusd_shm.h
+++ b/contrib/sendmail/src/statusd_shm.h
@@ -1,22 +1,22 @@
/*
- * Copyright (c) 1999 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*
- * $Id: statusd_shm.h,v 8.4 1999/05/18 08:00:04 gshapiro Exp $
+ * $Id: statusd_shm.h,v 8.7 2000/09/17 17:30:06 gshapiro Exp $
*
* Contributed by Exactis.com, Inc.
*
*/
/*
-** The shared memory part of statusd.
+** The shared memory part of statusd.
**
-** Attach to STATUSD_SHM_KEY and update the counter appropriate
-** for your type of service.
+** Attach to STATUSD_SHM_KEY and update the counter appropriate
+** for your type of service.
**
*/
@@ -24,7 +24,8 @@
#define STATUSD_SHM_KEY (key_t)(13)
#define STATUSD_LONGS (2)
-typedef struct {
+typedef struct
+{
unsigned long magic;
unsigned long ul[STATUSD_LONGS];
} STATUSD_SHM;
diff --git a/contrib/sendmail/src/sysexits.c b/contrib/sendmail/src/sysexits.c
index 6cce614..5cce2b7 100644
--- a/contrib/sendmail/src/sysexits.c
+++ b/contrib/sendmail/src/sysexits.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
@@ -11,60 +11,11 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: sysexits.c,v 8.25 1999/09/23 19:59:24 ca Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
-/*
-** SYSEXITS.C -- error messages corresponding to sysexits.h
-**
-** If the first character of the string is a colon, interpolate
-** the current errno after the rest of the string.
-*/
+SM_RCSID("@(#)$Id: sysexits.c,v 8.33 2001/09/11 04:05:17 gshapiro Exp $")
-char *SysExMsg[] =
-{
- /* 64 USAGE */ " 500 5.0.0 Bad usage",
- /* 65 DATAERR */ " 501 5.6.0 Data format error",
- /* 66 NOINPUT */ ":550 5.3.0 Cannot open input",
- /* 67 NOUSER */ " 550 5.1.1 User unknown",
- /* 68 NOHOST */ " 550 5.1.2 Host unknown",
- /* 69 UNAVAILABLE */ " 554 5.0.0 Service unavailable",
- /* 70 SOFTWARE */ ":554 5.3.0 Internal error",
- /* 71 OSERR */ ":451 4.0.0 Operating system error",
- /* 72 OSFILE */ ":554 5.3.5 System file missing",
- /* 73 CANTCREAT */ ":550 5.0.0 Can't create output",
- /* 74 IOERR */ ":451 4.0.0 I/O error",
- /* 75 TEMPFAIL */ " 450 4.0.0 Deferred",
- /* 76 PROTOCOL */ " 554 5.5.0 Remote protocol error",
- /* 77 NOPERM */ ":550 5.0.0 Insufficient permission",
- /* 78 CONFIG */ " 554 5.3.5 Local configuration error",
-};
-
-int N_SysEx = sizeof(SysExMsg) / sizeof(SysExMsg[0]);
-
-static char *SysExitMsg[] =
-{
- "command line usage error",
- "data format error",
- "cannot open input",
- "addressee unknown",
- "host name unknown",
- "service unavailable",
- "internal software error",
- "system error (e.g., can't fork)",
- "critical OS file missing",
- "can't create (user) output file",
- "input/output error",
- "temp failure; user is invited to retry",
- "remote error in protocol",
- "permission denied",
- "configuration error"
-};
-
- /*
+/*
** DSNTOEXITSTAT -- convert DSN-style error code to EX_ style.
**
** Parameters:
@@ -181,8 +132,7 @@ dsntoexitstat(dsncode)
}
return EX_CONFIG;
}
-
- /*
+/*
** EXITSTAT -- convert EX_ value to error text.
**
** Parameters:
@@ -198,19 +148,15 @@ exitstat(excode)
{
char *c;
int i;
+ char *exitmsg;
if (excode == NULL || *excode == '\0')
return excode;
- i = 0;
- for (c = excode; *c != '\0'; c++)
- {
- if (isascii(*c) && isdigit(*c))
- i = i * 10 + (*c - '0');
- else
- return excode;
- }
- i -= EX__BASE;
- if (i >= 0 && i <= N_SysEx)
- return SysExitMsg[i];
+ i = (int) strtol(excode, &c, 10);
+ if (*c != '\0')
+ return excode;
+ exitmsg = sm_sysexitmsg(i);
+ if (exitmsg != NULL)
+ return exitmsg;
return excode;
}
diff --git a/contrib/sendmail/src/timers.c b/contrib/sendmail/src/timers.c
index acd774c..43dd73a 100644
--- a/contrib/sendmail/src/timers.c
+++ b/contrib/sendmail/src/timers.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
@@ -10,9 +10,8 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: timers.c,v 8.13.16.1 2000/10/09 01:06:45 gshapiro Exp $";
-#endif /* ! lint */
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: timers.c,v 8.24 2001/09/11 04:05:17 gshapiro Exp $")
#if _FFR_TIMERS
# include <sys/types.h>
@@ -34,17 +33,17 @@ warntimer(msg, va_alist)
# endif /* __STDC__ */
{
char buf[MAXLINE];
- VA_LOCAL_DECL
+ SM_VA_LOCAL_DECL
# if 0
if (!tTd(98, 30))
return;
# endif /* 0 */
- VA_START(msg);
- vsnprintf(buf, sizeof buf, msg, ap);
- VA_END;
+ SM_VA_START(ap, msg);
+ (void) sm_vsnprintf(buf, sizeof buf, msg, ap);
+ SM_VA_END(ap);
sm_syslog(LOG_NOTICE, CurEnv->e_id, "%s; e_timers=0x%lx",
- buf, (u_long) &CurEnv->e_timers);
+ buf, (unsigned long) &CurEnv->e_timers);
}
static void
@@ -169,7 +168,7 @@ pushtimer(ptimer)
if (TimerStack[i] == ptimer)
{
warntimer("Timer@0x%lx already on stack, index=%d, NTimers=%d",
- (u_long) ptimer, i, NTimers);
+ (unsigned long) ptimer, i, NTimers);
errno = save_errno;
return;
}
@@ -211,7 +210,7 @@ poptimer(ptimer)
if (i != NTimers - 1)
warntimer("poptimer: odd pop (timer=0x%lx, index=%d, NTimers=%d)",
- (u_long) ptimer, i, NTimers);
+ (unsigned long) ptimer, i, NTimers);
NTimers = i;
/* clean up and return */
@@ -224,7 +223,7 @@ strtimer(ptimer)
{
static char buf[40];
- snprintf(buf, sizeof buf, "%ld.%06ldr/%ld.%06ldc",
+ (void) sm_snprintf(buf, sizeof buf, "%ld.%06ldr/%ld.%06ldc",
ptimer->ti_wall_sec, ptimer->ti_wall_usec,
ptimer->ti_cpu_sec, ptimer->ti_cpu_usec);
return buf;
diff --git a/contrib/sendmail/src/timers.h b/contrib/sendmail/src/timers.h
index e86b6ec..d7faee1 100644
--- a/contrib/sendmail/src/timers.h
+++ b/contrib/sendmail/src/timers.h
@@ -1,12 +1,12 @@
/*
- * Copyright (c) 1999 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*
- * $Id: timers.h,v 8.4 1999/11/04 19:31:26 ca Exp $
+ * $Id: timers.h,v 8.6 2001/04/03 01:53:18 gshapiro Exp $
*
* Contributed by Exactis.com, Inc.
*
@@ -30,4 +30,4 @@ TIMER
extern void pushtimer __P((TIMER *));
extern void poptimer __P((TIMER *));
extern char *strtimer __P((TIMER *));
-#endif /* TIMERS_H */
+#endif /* ! TIMERS_H */
diff --git a/contrib/sendmail/src/tls.c b/contrib/sendmail/src/tls.c
new file mode 100644
index 0000000..2eb0047
--- /dev/null
+++ b/contrib/sendmail/src/tls.c
@@ -0,0 +1,1469 @@
+/*
+ * Copyright (c) 2000-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.
+ *
+ */
+
+#include <sendmail.h>
+
+SM_RCSID("@(#)$Id: tls.c,v 8.75 2001/09/11 04:05:17 gshapiro Exp $")
+
+#if STARTTLS
+# include <openssl/err.h>
+# include <openssl/bio.h>
+# include <openssl/pem.h>
+# ifndef HASURANDOMDEV
+# include <openssl/rand.h>
+# endif /* ! HASURANDOMDEV */
+# if SM_CONF_SHM
+# include <sm/shm.h>
+# endif /* SM_CONF_SHM */
+# if !TLS_NO_RSA
+static RSA *rsa_tmp = NULL; /* temporary RSA key */
+static RSA *tmp_rsa_key __P((SSL *, int, int));
+# endif /* !TLS_NO_RSA */
+static int tls_verify_cb __P((X509_STORE_CTX *));
+
+static void apps_ssl_info_cb __P((SSL *, int , int));
+
+# 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 128 /* 1024 bits */
+
+# 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 */
+
+static bool tls_rand_init __P((char *, int));
+
+static bool
+tls_rand_init(randfile, logl)
+ char *randfile;
+ int logl;
+{
+# ifndef HASURANDOMDEV
+ /* not required if /dev/urandom exists, OpenSSL does it internally */
+
+ 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 && sm_strncasecmp(randfile, "egd:", 4) == 0)
+ {
+ randfile += 4;
+ if (RAND_egd(randfile) < 0)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS: RAND_egd(%s) failed: random number generator not seeded",
+ randfile);
+ }
+ else
+ ok = true;
+ }
+ else
+# endif /* EGD */
+ if (randdef == RF_OK && sm_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,
+ "STARTTLS: can't fstat(%s)",
+ randfile);
+ }
+ else
+ {
+ bool use, problem;
+
+ use = true;
+ problem = false;
+
+ /* max. age of file: 10 minutes */
+ if (st.st_mtime + 600 < curtime())
+ {
+ use = bitnset(DBS_INSUFFICIENTENTROPY,
+ DontBlameSendmail);
+ problem = true;
+ if (LogLevel > logl)
+ sm_syslog(LOG_ERR, NOQID,
+ "STARTTLS: 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,
+ "STARTTLS: 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,
+ "STARTTLS: 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,
+ "STARTTLS: Warning: safeopen(%s) failed",
+ randfile);
+ }
+ }
+ else if (randdef == RF_OK)
+ {
+ if (LogLevel > logl)
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS: Error: no proper random file definition %s",
+ randfile);
+ randdef = RF_UNKNOWN;
+ }
+ if (randdef == RF_MISS)
+ {
+ if (LogLevel > logl)
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS: 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,
+ "STARTTLS: Warning: random number generator not properly seeded");
+ ok = true;
+ }
+ done = ok ? RI_SUCCESS : RI_FAIL;
+ return ok;
+# else /* ! HASURANDOMDEV */
+ return true;
+# endif /* ! HASURANDOMDEV */
+}
+/*
+** INIT_TLS_LIBRARY -- Calls functions which setup TLS library for global use.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** succeeded?
+*/
+
+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 */
+
+ return tls_rand_init(RandFile, 7);
+}
+/*
+** TLS_SET_VERIFY -- request client certificate?
+**
+** Parameters:
+** ctx -- TLS context
+** ssl -- TLS structure
+** vrfy -- require certificate?
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Sets verification state for TLS
+**
+# if TLS_VRFY_PER_CTX
+** Notice:
+** This is per TLS context, not per TLS structure;
+** the former is global, the latter per connection.
+** It would be nice to do this per connection, but this
+** doesn't work in the current TLS libraries :-(
+# endif * TLS_VRFY_PER_CTX *
+*/
+
+void
+tls_set_verify(ctx, ssl, vrfy)
+ SSL_CTX *ctx;
+ SSL *ssl;
+ bool vrfy;
+{
+# if !TLS_VRFY_PER_CTX
+ SSL_set_verify(ssl, vrfy ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
+# else /* !TLS_VRFY_PER_CTX */
+ SSL_CTX_set_verify(ctx, vrfy ? SSL_VERIFY_PEER : SSL_VERIFY_NONE,
+ NULL);
+# endif /* !TLS_VRFY_PER_CTX */
+}
+
+/*
+** 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?
+** srv -- server side?
+**
+** Returns:
+** ok?
+*/
+
+static bool
+tls_ok_f(var, fn, srv)
+ char *var;
+ char *fn;
+ bool srv;
+{
+ /* must be absolute pathname */
+ if (var != NULL && *var == '/')
+ return true;
+ if (LogLevel > 12)
+ sm_syslog(LOG_WARNING, NOQID, "STARTTLS: %s%s missing",
+ srv ? "Server" : "Client", fn);
+ return false;
+}
+/*
+** TLS_SAFE_F -- is a file safe to use?
+**
+** Parameters:
+** var -- filename
+** sff -- flags for safefile()
+** srv -- server side?
+**
+** Returns:
+** ok?
+*/
+
+static bool
+tls_safe_f(var, sff, srv)
+ char *var;
+ long sff;
+ bool srv;
+{
+ int ret;
+
+ if ((ret = safefile(var, RunAsUid, RunAsGid, RunAsUserName, sff,
+ S_IRUSR, NULL)) == 0)
+ return true;
+ if (LogLevel > 7)
+ sm_syslog(LOG_WARNING, NOQID, "STARTTLS=%s: file %s unsafe: %s",
+ srv ? "server" : "client", var, sm_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
+** srv -- server side?
+**
+** Side Effects:
+** uses r, ok; may change ok and status.
+**
+*/
+
+# define TLS_OK_F(var, fn, req, st, srv) if (ok) \
+ { \
+ r = tls_ok_f(var, fn, srv); \
+ 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)
+# define TLS_OUNR(bit, req) (bitset(bit, req) ? SFF_NOWRFILES : 0)
+# define TLS_KEYSFF(req) \
+ (bitnset(DBS_GROUPREADABLEKEYFILE, DontBlameSendmail) ? \
+ TLS_OUNR(TLS_I_KEY_OUNR, req) : \
+ TLS_UNR(TLS_I_KEY_UNR, req))
+
+/*
+** 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
+** srv -- server side?
+**
+** Side Effects:
+** uses r, ok, ex; may change ok and status.
+**
+*/
+
+# define TLS_SAFE_F(var, sff, req, ex, st, srv) if (ex && ok) \
+ { \
+ r = tls_safe_f(var, sff, srv); \
+ if (r) \
+ status |= st; \
+ else if (req) \
+ ok = false; \
+ }
+
+/*
+** 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(s)
+** dhparam -- parameters for DH
+**
+** Returns:
+** succeeded?
+*/
+
+bool
+inittls(ctx, req, srv, certfile, keyfile, cacertpath, cacertfile, dhparam)
+ SSL_CTX **ctx;
+ unsigned 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 */
+# if SM_CONF_SHM
+ extern int ShmId;
+# endif /* SM_CONF_SHM */
+
+ status = TLS_S_NONE;
+ who = srv ? "server" : "client";
+ if (ctx == NULL)
+ syserr("STARTTLS=%s, inittls: ctx == NULL", who);
+
+ /* already initialized? (we could re-init...) */
+ if (*ctx != NULL)
+ return true;
+ 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 */
+
+ /*
+ ** Check whether files/paths are defined
+ */
+
+ TLS_OK_F(certfile, "CertFile", bitset(TLS_I_CERT_EX, req),
+ TLS_S_CERT_EX, srv);
+ TLS_OK_F(keyfile, "KeyFile", bitset(TLS_I_KEY_EX, req),
+ TLS_S_KEY_EX, srv);
+ TLS_OK_F(cacertpath, "CACERTPath", bitset(TLS_I_CERTP_EX, req),
+ TLS_S_CERTP_EX, srv);
+ TLS_OK_F(cacertfile, "CACERTFile", bitset(TLS_I_CERTF_EX, req),
+ TLS_S_CERTF_EX, srv);
+
+# if _FFR_TLS_1
+ /*
+ ** if the second file is specified it must exist
+ ** XXX: it is possible here to define only one of those files
+ */
+
+ if (cf2 != NULL)
+ {
+ TLS_OK_F(cf2, "CertFile", bitset(TLS_I_CERT_EX, req),
+ TLS_S_CERT2_EX, srv);
+ }
+ if (kf2 != NULL)
+ {
+ TLS_OK_F(kf2, "KeyFile", bitset(TLS_I_KEY_EX, req),
+ TLS_S_KEY2_EX, srv);
+ }
+# 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,
+ "STARTTLS=%s, error: illegal value '%s' for DHParam",
+ who, 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, srv);
+ }
+ }
+ 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, srv);
+ TLS_SAFE_F(keyfile, sff | TLS_KEYSFF(req),
+ bitset(TLS_I_KEY_EX, req),
+ bitset(TLS_S_KEY_EX, status), TLS_S_KEY_OK, srv);
+ 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, srv);
+ 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, srv);
+ 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, srv);
+ }
+ if (kf2 != NULL)
+ {
+ TLS_SAFE_F(kf2, sff | TLS_KEYSFF(req),
+ bitset(TLS_I_KEY_EX, req),
+ bitset(TLS_S_KEY2_EX, status), TLS_S_KEY2_OK, srv);
+ }
+# endif /* _FFR_TLS_1 */
+
+ /* create a method and a new context */
+ if ((*ctx = SSL_CTX_new(srv ? SSLv23_server_method() :
+ SSLv23_client_method())) == NULL)
+ {
+ if (LogLevel > 7)
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS=%s, error: SSL_CTX_new(SSLv23_%s_method()) failed",
+ who, who);
+ if (LogLevel > 9)
+ tlslogerr(who);
+ 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)
+# if SM_CONF_SHM
+ && ShmId != SM_SHM_NO_ID &&
+ (rsa_tmp = RSA_generate_key(RSA_KEYLENGTH, RSA_F4, NULL,
+ NULL)) == NULL
+# else /* SM_CONF_SHM */
+ && 0 /* no shared memory: no need to generate key now */
+# endif /* SM_CONF_SHM */
+ )
+ {
+ if (LogLevel > 7)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS=%s, error: RSA_generate_key failed",
+ who);
+ if (LogLevel > 9)
+ tlslogerr(who);
+ }
+ 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,
+ "STARTTLS=%s, error: SSL_CTX_use_PrivateKey_file(%s) failed",
+ who, keyfile);
+ if (LogLevel > 9)
+ tlslogerr(who);
+ }
+ 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,
+ "STARTTLS=%s, error: SSL_CTX_use_certificate_file(%s) failed",
+ who, certfile);
+ if (LogLevel > 9)
+ tlslogerr(who);
+ }
+ 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,
+ "STARTTLS=%s, error: SSL_CTX_check_private_key failed(%s): %d",
+ who, keyfile, r);
+ if (LogLevel > 9)
+ tlslogerr(who);
+ }
+ 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,
+ "STARTTLS=%s, error: SSL_CTX_use_PrivateKey_file(%s) failed",
+ who, kf2);
+ if (LogLevel > 9)
+ tlslogerr(who);
+ }
+ }
+
+ /* 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,
+ "STARTTLS=%s, error: SSL_CTX_use_certificate_file(%s) failed",
+ who, cf2);
+ if (LogLevel > 9)
+ tlslogerr(who);
+ }
+ }
+
+ /* 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,
+ "STARTTLS=%s, error: SSL_CTX_check_private_key 2 failed: %d",
+ who, r);
+ if (LogLevel > 9)
+ tlslogerr(who);
+ }
+ }
+# 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)
+ {
+ unsigned long err;
+
+ err = ERR_get_error();
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS=%s, error: cannot read DH parameters(%s): %s",
+ who, dhparam,
+ ERR_error_string(err, NULL));
+ if (LogLevel > 9)
+ tlslogerr(who);
+ }
+ }
+ else
+ {
+ if (LogLevel > 5)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS=%s, error: BIO_new_file(%s) failed",
+ who, dhparam);
+ if (LogLevel > 9)
+ tlslogerr(who);
+ }
+ }
+ }
+ 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)
+ {
+ unsigned long err;
+
+ err = ERR_get_error();
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS=%s, error: 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 > 13)
+ sm_syslog(LOG_INFO, NOQID,
+ "STARTTLS=%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 */
+
+ /*
+ ** We have to install our own verify callback:
+ ** SSL_VERIFY_PEER requests a client cert but even
+ ** though *FAIL_IF* isn't set, the connection
+ ** will be aborted if the client presents a cert
+ ** that is not "liked" (can't be verified?) by
+ ** the TLS library :-(
+ */
+
+ /*
+ ** XXX currently we could call tls_set_verify()
+ ** but we hope that that function will later on
+ ** only set the mode per connection.
+ */
+ SSL_CTX_set_verify(*ctx,
+ bitset(TLS_I_NO_VRFY, req) ? SSL_VERIFY_NONE
+ : 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,
+ "STARTTLS=%s, error: load verify locs %s, %s failed: %d",
+ who, cacertpath, cacertfile, r);
+ if (LogLevel > 9)
+ tlslogerr(who);
+ }
+ 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
+ /* install our own cipher list */
+ if (CipherList != NULL && *CipherList != '\0')
+ {
+ if (SSL_CTX_set_cipher_list(*ctx, CipherList) <= 0)
+ {
+ if (LogLevel > 7)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS=%s, error: SSL_CTX_set_cipher_list(%s) failed, list ignored",
+ who, CipherList);
+
+ if (LogLevel > 9)
+ tlslogerr(who);
+ }
+ /* failure if setting to this list is required? */
+ }
+ }
+# endif /* _FFR_TLS_1 */
+ if (LogLevel > 12)
+ sm_syslog(LOG_INFO, NOQID, "STARTTLS=%s, init=%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;
+}
+/*
+** TLS_GET_INFO -- get information about TLS connection
+**
+** Parameters:
+** ssl -- TLS connection structure
+** srv -- server or client
+** host -- hostname of other side
+** mac -- macro storage
+** certreq -- did we ask for a cert?
+**
+** Returns:
+** result of authentication.
+**
+** Side Effects:
+** sets macros: {cipher}, {tls_version}, {verify},
+** {cipher_bits}, {alg_bits}, {cert}, {cert_subject},
+** {cert_issuer}, {cn_subject}, {cn_issuer}
+*/
+
+int
+tls_get_info(ssl, srv, host, mac, certreq)
+ SSL *ssl;
+ bool srv;
+ char *host;
+ MACROS_T *mac;
+ bool certreq;
+{
+ SSL_CIPHER *c;
+ int b, r;
+ char *s, *who;
+ char bitstr[16];
+ X509 *cert;
+
+ c = SSL_get_current_cipher(ssl);
+
+ /* cast is just workaround for compiler warning */
+ macdefine(mac, A_TEMP, macid("{cipher}"),
+ (char *) SSL_CIPHER_get_name(c));
+ b = SSL_CIPHER_get_bits(c, &r);
+ (void) sm_snprintf(bitstr, sizeof bitstr, "%d", b);
+ macdefine(mac, A_TEMP, macid("{cipher_bits}"), bitstr);
+ (void) sm_snprintf(bitstr, sizeof bitstr, "%d", r);
+ macdefine(mac, A_TEMP, macid("{alg_bits}"), bitstr);
+ s = SSL_CIPHER_get_version(c);
+ if (s == NULL)
+ s = "UNKNOWN";
+ macdefine(mac, A_TEMP, macid("{tls_version}"), s);
+
+ who = srv ? "server" : "client";
+ cert = SSL_get_peer_certificate(ssl);
+ if (LogLevel > 14)
+ sm_syslog(LOG_INFO, NOQID,
+ "STARTTLS=%s, get_verify: %ld get_peer: 0x%lx",
+ who, SSL_get_verify_result(ssl),
+ (unsigned long) cert);
+ if (cert != NULL)
+ {
+ unsigned int n;
+ unsigned char md[EVP_MAX_MD_SIZE];
+ char buf[MAXNAME];
+
+ X509_NAME_oneline(X509_get_subject_name(cert),
+ buf, sizeof buf);
+ macdefine(mac, A_TEMP, macid("{cert_subject}"),
+ xtextify(buf, "<>\")"));
+ X509_NAME_oneline(X509_get_issuer_name(cert),
+ buf, sizeof buf);
+ macdefine(mac, A_TEMP, macid("{cert_issuer}"),
+ xtextify(buf, "<>\")"));
+ X509_NAME_get_text_by_NID(X509_get_subject_name(cert),
+ NID_commonName, buf, sizeof buf);
+ macdefine(mac, A_TEMP, macid("{cn_subject}"),
+ xtextify(buf, "<>\")"));
+ X509_NAME_get_text_by_NID(X509_get_issuer_name(cert),
+ NID_commonName, buf, sizeof buf);
+ macdefine(mac, A_TEMP, macid("{cn_issuer}"),
+ xtextify(buf, "<>\")"));
+ if (X509_digest(cert, EVP_md5(), md, &n))
+ {
+ char md5h[EVP_MAX_MD_SIZE * 3];
+ static const char hexcodes[] = "0123456789ABCDEF";
+
+ SM_ASSERT((n * 3) + 2 < sizeof(md5h));
+ for (r = 0; r < (int) n; r++)
+ {
+ md5h[r * 3] = hexcodes[(md[r] & 0xf0) >> 4];
+ md5h[(r * 3) + 1] = hexcodes[(md[r] & 0x0f)];
+ md5h[(r * 3) + 2] = ':';
+ }
+ md5h[(n * 3) - 1] = '\0';
+ macdefine(mac, A_TEMP, macid("{cert_md5}"), md5h);
+ }
+ else
+ macdefine(mac, A_TEMP, macid("{cert_md5}"), "");
+ }
+ else
+ {
+ macdefine(mac, A_PERM, macid("{cert_subject}"), "");
+ macdefine(mac, A_PERM, macid("{cert_issuer}"), "");
+ macdefine(mac, A_PERM, macid("{cn_subject}"), "");
+ macdefine(mac, A_PERM, macid("{cn_issuer}"), "");
+ macdefine(mac, A_TEMP, macid("{cert_md5}"), "");
+ }
+ switch (SSL_get_verify_result(ssl))
+ {
+ case X509_V_OK:
+ if (cert != NULL)
+ {
+ s = "OK";
+ r = TLS_AUTH_OK;
+ }
+ else
+ {
+ s = certreq ? "NO" : "NOT",
+ r = TLS_AUTH_NO;
+ }
+ break;
+ default:
+ s = "FAIL";
+ r = TLS_AUTH_FAIL;
+ break;
+ }
+ macdefine(mac, A_PERM, macid("{verify}"), s);
+ if (cert != NULL)
+ X509_free(cert);
+
+ /* do some logging */
+ if (LogLevel > 8)
+ {
+ char *vers, *s1, *s2, *cbits, *algbits;
+
+ vers = macget(mac, macid("{tls_version}"));
+ cbits = macget(mac, macid("{cipher_bits}"));
+ algbits = macget(mac, macid("{alg_bits}"));
+ s1 = macget(mac, macid("{verify}"));
+ s2 = macget(mac, macid("{cipher}"));
+
+ /* XXX: maybe cut off ident info? */
+ sm_syslog(LOG_INFO, NOQID,
+ "STARTTLS=%s, relay=%.100s, version=%.16s, verify=%.16s, cipher=%.64s, bits=%.6s/%.6s",
+ who,
+ host == NULL ? "local" : host,
+ vers, s1, s2, /* sm_snprintf() can deal with NULL */
+ algbits == NULL ? "0" : algbits,
+ cbits == NULL ? "0" : cbits);
+ 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 = macget(mac, macid("{cert_subject}"));
+ s2 = macget(mac, macid("{cert_issuer}"));
+ sm_syslog(LOG_INFO, NOQID,
+ "STARTTLS=%s, cert-subject=%.128s, cert-issuer=%.128s",
+ who, s1, s2);
+ }
+ }
+ return r;
+}
+/*
+** ENDTLS -- shutdown secure connection
+**
+** Parameters:
+** ssl -- SSL connection information.
+** side -- server/client (for logging).
+**
+** Returns:
+** success? (EX_* code)
+*/
+
+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,
+ "STARTTLS=%s, SSL_shutdown failed: %d",
+ side, r);
+ tlslogerr(side);
+ }
+ ret = EX_SOFTWARE;
+ }
+# if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER > 0x0090602fL
+
+ /*
+ ** Bug in OpenSSL (at least up to 0.9.6b):
+ ** From: Lutz.Jaenicke@aet.TU-Cottbus.DE
+ ** Message-ID: <20010723152244.A13122@serv01.aet.tu-cottbus.de>
+ ** To: openssl-users@openssl.org
+ ** Subject: Re: SSL_shutdown() woes (fwd)
+ **
+ ** The side sending the shutdown alert first will
+ ** not care about the answer of the peer but will
+ ** immediately return with a return value of "0"
+ ** (ssl/s3_lib.c:ssl3_shutdown()). SSL_get_error will evaluate
+ ** the value of "0" and as the shutdown alert of the peer was
+ ** not received (actually, the program did not even wait for
+ ** the answer), an SSL_ERROR_SYSCALL is flagged, because this
+ ** is the default rule in case everything else does not apply.
+ **
+ ** For your server the problem is different, because it
+ ** receives the shutdown first (setting SSL_RECEIVED_SHUTDOWN),
+ ** then sends its response (SSL_SENT_SHUTDOWN), so for the
+ ** server the shutdown was successfull.
+ **
+ ** As is by know, you would have to call SSL_shutdown() once
+ ** and ignore an SSL_ERROR_SYSCALL returned. Then call
+ ** SSL_shutdown() again to actually get the server's response.
+ **
+ ** In the last discussion, Bodo Moeller concluded that a
+ ** rewrite of the shutdown code would be necessary, but
+ ** probably with another API, as the change would not be
+ ** compatible to the way it is now. Things do not become
+ ** easier as other programs do not follow the shutdown
+ ** guidelines anyway, so that a lot error conditions and
+ ** compitibility issues would have to be caught.
+ **
+ ** For now the recommondation is to ignore the error message.
+ */
+
+ else if (r == 0)
+ {
+ if (LogLevel > 15)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS=%s, SSL_shutdown not done",
+ side);
+ tlslogerr(side);
+ }
+ ret = EX_SOFTWARE;
+ }
+# endif /* !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER > 0x0090602fL */
+ SSL_free(ssl);
+ ssl = NULL;
+ }
+ return ret;
+}
+
+# if !TLS_NO_RSA
+/*
+** TMP_RSA_KEY -- return temporary RSA key
+**
+** Parameters:
+** s -- TLS connection structure
+** export --
+** keylength --
+**
+** Returns:
+** temporary RSA key.
+*/
+
+# ifndef MAX_RSA_TMP_CNT
+# define MAX_RSA_TMP_CNT 1000 /* XXX better value? */
+# endif /* ! MAX_RSA_TMP_CNT */
+
+/* ARGUSED0 */
+static RSA *
+tmp_rsa_key(s, export, keylength)
+ SSL *s;
+ int export;
+ int keylength;
+{
+# if SM_CONF_SHM
+ extern int ShmId;
+ extern int *PRSATmpCnt;
+
+ if (ShmId != SM_SHM_NO_ID && rsa_tmp != NULL &&
+ ++(*PRSATmpCnt) < MAX_RSA_TMP_CNT)
+ return rsa_tmp;
+# endif /* SM_CONF_SHM */
+
+ if (rsa_tmp != NULL)
+ RSA_free(rsa_tmp);
+ rsa_tmp = RSA_generate_key(RSA_KEYLENGTH, RSA_F4, NULL, NULL);
+ if (rsa_tmp == NULL)
+ {
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, NOQID,
+ "STARTTLS=server, tmp_rsa_key: RSA_generate_key failed!");
+ }
+ else
+ {
+# if SM_CONF_SHM
+# if 0
+ /*
+ ** XXX we can't (yet) share the new key...
+ ** The RSA structure contains pointers hence it can't be
+ ** easily kept in shared memory. It must be transformed
+ ** into a continous memory region first, then stored,
+ ** and later read out again (each time re-transformed).
+ */
+
+ if (ShmId != SM_SHM_NO_ID)
+ *PRSATmpCnt = 0;
+# endif /* 0 */
+# endif /* SM_CONF_SHM */
+ if (LogLevel > 9)
+ sm_syslog(LOG_ERR, NOQID,
+ "STARTTLS=server, tmp_rsa_key: new temp RSA key");
+ }
+ return rsa_tmp;
+}
+# endif /* !TLS_NO_RSA */
+/*
+** APPS_SSL_INFO_CB -- info callback for TLS connections
+**
+** Parameters:
+** s -- TLS connection structure
+** where -- state in handshake
+** ret -- return code of last operation
+**
+** Returns:
+** none.
+*/
+
+static void
+apps_ssl_info_cb(s, where, ret)
+ SSL *s;
+ int where;
+ int ret;
+{
+ int w;
+ char *str;
+ BIO *bio_err = NULL;
+
+ if (LogLevel > 14)
+ sm_syslog(LOG_INFO, NOQID,
+ "STARTTLS: 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 (bitset(SSL_ST_CONNECT, w))
+ str = "SSL_connect";
+ else if (bitset(SSL_ST_ACCEPT, w))
+ str = "SSL_accept";
+ else
+ str = "undefined";
+
+ if (bitset(SSL_CB_LOOP, where))
+ {
+ if (LogLevel > 12)
+ sm_syslog(LOG_NOTICE, NOQID,
+ "STARTTLS: %s:%s",
+ str, SSL_state_string_long(s));
+ }
+ else if (bitset(SSL_CB_ALERT, where))
+ {
+ str = bitset(SSL_CB_READ, where) ? "read" : "write";
+ if (LogLevel > 12)
+ sm_syslog(LOG_NOTICE, NOQID,
+ "STARTTLS: SSL3 alert %s:%s:%s",
+ str, SSL_alert_type_string_long(ret),
+ SSL_alert_desc_string_long(ret));
+ }
+ else if (bitset(SSL_CB_EXIT, where))
+ {
+ if (ret == 0)
+ {
+ if (LogLevel > 7)
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS: %s:failed in %s",
+ str, SSL_state_string_long(s));
+ }
+ else if (ret < 0)
+ {
+ if (LogLevel > 7)
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS: %s:error in %s",
+ 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,
+ "STARTTLS: 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,
+ "STARTTLS: cert verify: depth=%d %s, state=%d, reason=%s",
+ 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:
+** who -- server/client (for logging).
+**
+** Returns:
+** none.
+*/
+
+void
+tlslogerr(who)
+ char *who;
+{
+ 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,
+ "STARTTLS=%s: %lu:%s:%s:%d:%s", who, es,
+ ERR_error_string(l, buf),
+ file, line,
+ bitset(ERR_TXT_STRING, flags) ? data : "");
+ }
+}
+#endif /* STARTTLS */
diff --git a/contrib/sendmail/src/trace.c b/contrib/sendmail/src/trace.c
index 735ef83..701a949 100644
--- a/contrib/sendmail/src/trace.c
+++ b/contrib/sendmail/src/trace.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
@@ -11,11 +11,14 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: trace.c,v 8.20.22.4 2001/08/15 13:05:43 ca Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
+#include <sm/debug.h>
+#include <sm/string.h>
+
+SM_RCSID("@(#)$Id: trace.c,v 8.37 2001/09/11 04:05:17 gshapiro Exp $")
+
+static char *tTnewflag __P((char *));
+static char *tToldflag __P((char *));
/*
** TtSETUP -- set up for trace package.
@@ -32,93 +35,190 @@ static char id[] = "@(#)$Id: trace.c,v 8.20.22.4 2001/08/15 13:05:43 ca Exp $";
** environment is set up.
*/
-static u_char *tTvect;
-static int tTsize;
+static unsigned char *tTvect;
+static unsigned int tTsize;
static char *DefFlags;
void
tTsetup(vect, size, defflags)
- u_char *vect;
- int size;
+ unsigned char *vect;
+ unsigned int size;
char *defflags;
{
tTvect = vect;
tTsize = size;
DefFlags = defflags;
}
- /*
-** TtFLAG -- process an external trace flag description.
+
+/*
+** tToldflag -- process an old style trace flag
**
** Parameters:
-** s -- the trace flag.
+** s -- points to a [\0, \t] terminated string,
+** and the initial character is a digit.
**
** Returns:
-** none.
+** pointer to terminating [\0, \t] character
**
** Side Effects:
-** sets/clears trace flags.
+** modifies tTvect
*/
-void
-tTflag(s)
+static char *
+tToldflag(s)
register char *s;
{
unsigned int first, last;
register unsigned int i;
- if (*s == '\0')
- s = DefFlags;
+ /* find first flag to set */
+ i = 0;
+ while (isascii(*s) && isdigit(*s) && i < tTsize)
+ i = i * 10 + (*s++ - '0');
- for (;;)
+ /*
+ ** skip over rest of a too large number
+ ** Maybe we should complain if out-of-bounds values are used.
+ */
+
+ while (isascii(*s) && isdigit(*s) && i >= tTsize)
+ s++;
+ first = i;
+
+ /* find last flag to set */
+ if (*s == '-')
{
- /* find first flag to set */
i = 0;
- while (isascii(*s) && isdigit(*s) && i < tTsize)
- i = i * 10 + (*s++ - '0');
-
- /*
- ** skip over rest of a too large number
- ** Maybe we should complain if out-of-bounds values are used.
- */
+ while (isascii(*++s) && isdigit(*s) && i < tTsize)
+ i = i * 10 + (*s - '0');
+ /* skip over rest of a too large number */
while (isascii(*s) && isdigit(*s) && i >= tTsize)
s++;
- first = i;
+ }
+ last = i;
- /* find last flag to set */
- if (*s == '-')
- {
- i = 0;
- while (isascii(*++s) && isdigit(*s) && i < tTsize)
- i = i * 10 + (*s - '0');
+ /* find the level to set it to */
+ i = 1;
+ if (*s == '.')
+ {
+ i = 0;
+ while (isascii(*++s) && isdigit(*s))
+ i = i * 10 + (*s - '0');
+ }
- /* skip over rest of a too large number */
- while (isascii(*s) && isdigit(*s) && i >= tTsize)
- s++;
- }
- last = i;
+ /* clean up args */
+ if (first >= tTsize)
+ first = tTsize - 1;
+ if (last >= tTsize)
+ last = tTsize - 1;
+
+ /* set the flags */
+ while (first <= last)
+ tTvect[first++] = (unsigned char) i;
+
+ /* skip trailing junk */
+ while (*s != '\0' && *s != ',' && *s != ' ' && *s != '\t')
+ ++s;
+
+ return s;
+}
+
+/*
+** tTnewflag -- process a new style trace flag
+**
+** Parameters:
+** s -- Points to a non-empty [\0, \t] terminated string,
+** of which the initial character is not a digit.
+**
+** Returns:
+** pointer to terminating [\0, \t] character
+**
+** Side Effects:
+** adds trace flag to libsm debug database
+*/
- /* find the level to set it to */
- i = 1;
- if (*s == '.')
+static char *
+tTnewflag(s)
+ register char *s;
+{
+ char *pat, *endpat;
+ int level;
+
+ pat = s;
+ while (*s != '\0' && *s != ',' && *s != ' ' && *s != '\t' && *s != '.')
+ ++s;
+ endpat = s;
+ if (*s == '.')
+ {
+ ++s;
+ level = 0;
+ while (isascii(*s) && isdigit(*s))
{
- i = 0;
- while (isascii(*++s) && isdigit(*s))
- i = i * 10 + (*s - '0');
+ level = level * 10 + (*s - '0');
+ ++s;
}
+ if (level < 0)
+ level = 0;
+ }
+ else
+ {
+ level = 1;
+ }
+
+ sm_debug_addsetting_x(sm_strndup_x(pat, endpat - pat), level);
- /* clean up args */
- if (first >= tTsize)
- first = tTsize - 1;
- if (last >= tTsize)
- last = tTsize - 1;
+ /* skip trailing junk */
+ while (*s != '\0' && *s != ',' && *s != ' ' && *s != '\t')
+ ++s;
- /* set the flags */
- while (first <= last)
- tTvect[first++] = i;
+ return s;
+}
- /* more arguments? */
- if (*s++ == '\0')
+/*
+** TtFLAG -- process an external trace flag list.
+**
+** Parameters:
+** s -- the trace flag.
+**
+** The syntax of a trace flag list is as follows:
+**
+** <flags> ::= <flag> | <flags> "," <flag>
+** <flag> ::= <categories> | <categories> "." <level>
+** <categories> ::= <int> | <int> "-" <int> | <pattern>
+** <pattern> ::= <an sh glob pattern matching a C identifier>
+**
+** White space is ignored before and after a flag.
+** However, note that we skip over anything we don't
+** understand, rather than report an error.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** sets/clears old-style trace flags.
+** registers new-style trace flags with the libsm debug package.
+*/
+
+void
+tTflag(s)
+ register char *s;
+{
+ if (*s == '\0')
+ s = DefFlags;
+
+ for (;;)
+ {
+ if (*s == '\0')
return;
+ if (*s == ',' || *s == ' ' || *s == '\t')
+ {
+ ++s;
+ continue;
+ }
+ if (isascii(*s) && isdigit(*s))
+ s = tToldflag(s);
+ else
+ s = tTnewflag(s);
}
}
diff --git a/contrib/sendmail/src/udb.c b/contrib/sendmail/src/udb.c
index d835326..1091cf2 100644
--- a/contrib/sendmail/src/udb.c
+++ b/contrib/sendmail/src/udb.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998-1999, 2001 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
@@ -13,17 +13,15 @@
#include <sendmail.h>
-#ifndef lint
-# if USERDB
-static char id[] = "@(#)$Id: udb.c,v 8.111.16.2 2001/05/03 17:24:17 gshapiro Exp $ (with USERDB)";
-# else /* USERDB */
-static char id[] = "@(#)$Id: udb.c,v 8.111.16.2 2001/05/03 17:24:17 gshapiro Exp $ (without USERDB)";
-# endif /* USERDB */
-#endif /* ! lint */
+#if USERDB
+SM_RCSID("@(#)$Id: udb.c,v 8.153 2001/09/11 04:05:17 gshapiro Exp $ (with USERDB)")
+#else /* USERDB */
+SM_RCSID("@(#)$Id: udb.c,v 8.153 2001/09/11 04:05:17 gshapiro Exp $ (without USERDB)")
+#endif /* USERDB */
#if USERDB
-# ifdef NEWDB
+# if NEWDB
# include <db.h>
# ifndef DB_VERSION_MAJOR
# define DB_VERSION_MAJOR 1
@@ -70,7 +68,7 @@ struct udbent
} udb_forward;
# define udb_fwdhost udb_u.udb_forward._udb_fwdhost
-# ifdef NEWDB
+# if NEWDB
/* type UE_FETCH -- lookup in local database */
struct
{
@@ -99,10 +97,10 @@ struct udb_option
char *udbo_val;
};
-# ifdef HESIOD
+# if HESIOD
static int hes_udb_get __P((DBT *, DBT *));
# endif /* HESIOD */
-static char *udbmatch __P((char *, char *));
+static char *udbmatch __P((char *, char *, SM_RPOOL_T *));
static int _udbx_init __P((ENVELOPE *));
static int _udb_parsespec __P((char *, struct udb_option [], int));
@@ -125,7 +123,7 @@ static int _udb_parsespec __P((char *, struct udb_option [], int));
*/
static struct udbent UdbEnts[MAXUDBENT + 1];
-static bool UdbInitialized = FALSE;
+static bool UdbInitialized = false;
int
udbexpand(a, sendq, aliaslevel, e)
@@ -148,7 +146,7 @@ udbexpand(a, sendq, aliaslevel, e)
memset(&info, '\0', sizeof info);
if (tTd(28, 1))
- dprintf("udbexpand(%s)\n", a->q_paddr);
+ sm_dprintf("udbexpand(%s)\n", a->q_paddr);
/* make certain we are supposed to send to this address */
if (!QS_IS_SENDABLE(a->q_state))
@@ -174,20 +172,19 @@ udbexpand(a, sendq, aliaslevel, e)
if (user[0] == '\\')
return EX_OK;
- /* if name is too long, assume it won't match */
- if (strlen(user) > (SIZE_T) sizeof keybuf - 12)
- return EX_OK;
-
/* if name begins with a colon, it indicates our metadata */
if (user[0] == ':')
return EX_OK;
+ keylen = sm_strlcpyn(keybuf, sizeof keybuf, 2, user, ":maildrop");
+
+ /* if name is too long, assume it won't match */
+ if (keylen > sizeof keybuf)
+ return EX_OK;
+
/* build actual database key */
- (void) strlcpy(keybuf, user, sizeof keybuf);
- (void) strlcat(keybuf, ":maildrop", sizeof keybuf);
- keylen = strlen(keybuf);
- breakout = FALSE;
+ breakout = false;
for (up = UdbEnts; !breakout; up++)
{
int usersize;
@@ -215,12 +212,12 @@ udbexpand(a, sendq, aliaslevel, e)
switch (up->udb_type)
{
-# ifdef NEWDB
+# if NEWDB
case UDB_DBFETCH:
key.data = keybuf;
key.size = keylen;
if (tTd(28, 80))
- dprintf("udbexpand: trying %s (%d) via db\n",
+ sm_dprintf("udbexpand: trying %s (%d) via db\n",
keybuf, keylen);
# if DB_VERSION_MAJOR < 2
i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR);
@@ -243,7 +240,7 @@ udbexpand(a, sendq, aliaslevel, e)
if (i > 0 || info.size <= 0)
{
if (tTd(28, 2))
- dprintf("udbexpand: no match on %s (%d)\n",
+ sm_dprintf("udbexpand: no match on %s (%d)\n",
keybuf, keylen);
# if DB_VERSION_MAJOR > 1
if (dbc != NULL)
@@ -255,7 +252,7 @@ udbexpand(a, sendq, aliaslevel, e)
break;
}
if (tTd(28, 80))
- dprintf("udbexpand: match %.*s: %.*s\n",
+ sm_dprintf("udbexpand: match %.*s: %.*s\n",
(int) key.size, (char *) key.data,
(int) info.size, (char *) info.data);
@@ -278,7 +275,7 @@ udbexpand(a, sendq, aliaslevel, e)
return EX_OK;
}
- breakout = TRUE;
+ breakout = true;
if (info.size >= userleft - 1)
{
char *nuser;
@@ -286,11 +283,11 @@ udbexpand(a, sendq, aliaslevel, e)
if (info.size > MEMCHUNKSIZE)
size = info.size;
- nuser = xalloc(usersize + size);
+ nuser = sm_malloc_x(usersize + size);
memmove(nuser, user, usersize);
if (user != userbuf)
- sm_free(user);
+ sm_free(user); /* XXX */
user = nuser;
usersize += size;
userleft += size;
@@ -339,8 +336,8 @@ udbexpand(a, sendq, aliaslevel, e)
{
if (tTd(28, 5))
{
- dprintf("udbexpand: QS_EXPANDED ");
- printaddr(a, FALSE);
+ sm_dprintf("udbexpand: QS_EXPANDED ");
+ printaddr(a, false);
}
a->q_state = QS_EXPANDED;
}
@@ -358,8 +355,8 @@ udbexpand(a, sendq, aliaslevel, e)
memset(&key, '\0', sizeof key);
memset(&info, '\0', sizeof info);
- (void) strlcpy(keybuf, a->q_user, sizeof keybuf);
- (void) strlcat(keybuf, ":mailsender", sizeof keybuf);
+ (void) sm_strlcpyn(keybuf, sizeof keybuf, 2, a->q_user,
+ ":mailsender");
keylen = strlen(keybuf);
key.data = keybuf;
key.size = keylen;
@@ -372,28 +369,29 @@ udbexpand(a, sendq, aliaslevel, e)
# endif /* DB_VERSION_MAJOR < 2 */
if (i != 0 || info.size <= 0)
break;
- a->q_owner = xalloc(info.size + 1);
+ a->q_owner = sm_rpool_malloc_x(e->e_rpool,
+ info.size + 1);
memmove(a->q_owner, info.data, info.size);
a->q_owner[info.size] = '\0';
/* announce delivery; NORECEIPT bit set later */
if (e->e_xfp != NULL)
{
- fprintf(e->e_xfp,
- "Message delivered to mailing list %s\n",
- a->q_paddr);
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "Message delivered to mailing list %s\n",
+ a->q_paddr);
}
e->e_flags |= EF_SENDRECEIPT;
a->q_flags |= QDELIVERED|QEXPANDED;
break;
# endif /* NEWDB */
-# ifdef HESIOD
+# if HESIOD
case UDB_HESIOD:
key.data = keybuf;
key.size = keylen;
if (tTd(28, 80))
- dprintf("udbexpand: trying %s (%d) via hesiod\n",
+ sm_dprintf("udbexpand: trying %s (%d) via hesiod\n",
keybuf, keylen);
/* look up the key via hesiod */
i = hes_udb_get(&key, &info);
@@ -410,11 +408,11 @@ udbexpand(a, sendq, aliaslevel, e)
# endif /* HES_GETMAILHOST */
if (tTd(28, 2))
- dprintf("udbexpand: no match on %s (%d)\n",
+ sm_dprintf("udbexpand: no match on %s (%d)\n",
(char *) keybuf, (int) keylen);
# if HES_GETMAILHOST
if (tTd(28, 8))
- dprintf(" ... trying hes_getmailhost(%s)\n",
+ sm_dprintf(" ... trying hes_getmailhost(%s)\n",
a->q_user);
hp = hes_getmailhost(a->q_user);
if (hp == NULL)
@@ -426,7 +424,7 @@ udbexpand(a, sendq, aliaslevel, e)
return EX_TEMPFAIL;
}
if (tTd(28, 2))
- dprintf("hes_getmailhost(%s): %d\n",
+ sm_dprintf("hes_getmailhost(%s): %d\n",
a->q_user, hes_error());
break;
}
@@ -434,22 +432,22 @@ udbexpand(a, sendq, aliaslevel, e)
sizeof pobuf - 2)
{
if (tTd(28, 2))
- dprintf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n",
+ sm_dprintf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n",
a->q_user,
hp->po_name,
hp->po_host);
break;
}
info.data = pobuf;
- snprintf(pobuf, sizeof pobuf, "%s@%s",
- hp->po_name, hp->po_host);
+ (void) sm_snprintf(pobuf, sizeof pobuf,
+ "%s@%s", hp->po_name, hp->po_host);
info.size = strlen(info.data);
# else /* HES_GETMAILHOST */
break;
# endif /* HES_GETMAILHOST */
}
if (tTd(28, 80))
- dprintf("udbexpand: match %.*s: %.*s\n",
+ sm_dprintf("udbexpand: match %.*s: %.*s\n",
(int) key.size, (char *) key.data,
(int) info.size, (char *) info.data);
a->q_flags &= ~QSELFREF;
@@ -460,9 +458,9 @@ udbexpand(a, sendq, aliaslevel, e)
return EX_OK;
}
- breakout = TRUE;
+ breakout = true;
if (info.size >= usersize)
- user = xalloc(info.size + 1);
+ user = sm_malloc_x(info.size + 1);
memmove(user, info.data, info.size);
user[info.size] = '\0';
@@ -478,8 +476,8 @@ udbexpand(a, sendq, aliaslevel, e)
{
if (tTd(28, 5))
{
- dprintf("udbexpand: QS_EXPANDED ");
- printaddr(a, FALSE);
+ sm_dprintf("udbexpand: QS_EXPANDED ");
+ printaddr(a, false);
}
a->q_state = QS_EXPANDED;
}
@@ -489,15 +487,16 @@ udbexpand(a, sendq, aliaslevel, e)
** it into the envelope.
*/
- (void) strlcpy(keybuf, a->q_user, sizeof keybuf);
- (void) strlcat(keybuf, ":mailsender", sizeof keybuf);
+ (void) sm_strlcpyn(keybuf, sizeof keybuf, 2, a->q_user,
+ ":mailsender");
keylen = strlen(keybuf);
key.data = keybuf;
key.size = keylen;
i = hes_udb_get(&key, &info);
if (i != 0 || info.size <= 0)
break;
- a->q_owner = xalloc(info.size + 1);
+ a->q_owner = sm_rpool_malloc_x(e->e_rpool,
+ info.size + 1);
memmove(a->q_owner, info.data, info.size);
a->q_owner[info.size] = '\0';
break;
@@ -517,10 +516,10 @@ udbexpand(a, sendq, aliaslevel, e)
if (i >= usersize)
{
usersize = i + 1;
- user = xalloc(usersize);
+ user = sm_malloc_x(usersize);
}
- (void) snprintf(user, usersize, "%s@%s",
- a->q_user, up->udb_fwdhost);
+ (void) sm_strlcpyn(user, usersize, 3,
+ a->q_user, "@", up->udb_fwdhost);
message("expanded to %s", user);
a->q_flags &= ~QSELFREF;
naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
@@ -528,36 +527,38 @@ udbexpand(a, sendq, aliaslevel, e)
{
if (tTd(28, 5))
{
- dprintf("udbexpand: QS_EXPANDED ");
- printaddr(a, FALSE);
+ sm_dprintf("udbexpand: QS_EXPANDED ");
+ printaddr(a, false);
}
a->q_state = QS_EXPANDED;
}
- breakout = TRUE;
+ breakout = true;
break;
case UDB_EOLIST:
- breakout = TRUE;
+ breakout = true;
break;
default:
/* unknown entry type */
break;
}
+ /* XXX if an exception occurs, there is a storage leak */
if (user != userbuf)
- sm_free(user);
+ sm_free(user); /* XXX */
}
return EX_OK;
}
- /*
+/*
** UDBSENDER -- return canonical external name of sender, given local name
**
** Parameters:
** sender -- the name of the sender on the local machine.
+** rpool -- resource pool from which to allocate result
**
** Returns:
** The external name for this sender, if derivable from the
-** database.
+** database. Storage allocated from rpool.
** NULL -- if nothing is changed from the database.
**
** Side Effects:
@@ -565,21 +566,23 @@ udbexpand(a, sendq, aliaslevel, e)
*/
char *
-udbsender(sender)
+udbsender(sender, rpool)
char *sender;
+ SM_RPOOL_T *rpool;
{
- return udbmatch(sender, "mailname");
+ return udbmatch(sender, "mailname", rpool);
}
- /*
+/*
** UDBMATCH -- match user in field, return result of lookup.
**
** Parameters:
** user -- the name of the user.
** field -- the field to lookup.
+** rpool -- resource pool from which to allocate result
**
** Returns:
** The external name for this sender, if derivable from the
-** database.
+** database. Storage allocated from rpool.
** NULL -- if nothing is changed from the database.
**
** Side Effects:
@@ -587,9 +590,10 @@ udbsender(sender)
*/
static char *
-udbmatch(user, field)
+udbmatch(user, field, rpool)
char *user;
char *field;
+ SM_RPOOL_T *rpool;
{
register char *p;
register struct udbent *up;
@@ -599,7 +603,7 @@ udbmatch(user, field)
char keybuf[MAXKEY];
if (tTd(28, 1))
- dprintf("udbmatch(%s, %s)\n", user, field);
+ sm_dprintf("udbmatch(%s, %s)\n", user, field);
if (!UdbInitialized)
{
@@ -627,7 +631,7 @@ udbmatch(user, field)
return NULL;
/* build database key */
- (void) snprintf(keybuf, sizeof keybuf, "%s:%s", user, field);
+ (void) sm_strlcpyn(keybuf, sizeof keybuf, 3, user, ":", field);
keylen = strlen(keybuf);
for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
@@ -638,7 +642,7 @@ udbmatch(user, field)
switch (up->udb_type)
{
-# ifdef NEWDB
+# if NEWDB
case UDB_DBFETCH:
memset(&key, '\0', sizeof key);
memset(&info, '\0', sizeof info);
@@ -653,20 +657,20 @@ udbmatch(user, field)
if (i != 0 || info.size <= 0)
{
if (tTd(28, 2))
- dprintf("udbmatch: no match on %s (%d) via db\n",
+ sm_dprintf("udbmatch: no match on %s (%d) via db\n",
keybuf, keylen);
continue;
}
- p = xalloc(info.size + 1);
+ p = sm_rpool_malloc_x(rpool, info.size + 1);
memmove(p, info.data, info.size);
p[info.size] = '\0';
if (tTd(28, 1))
- dprintf("udbmatch ==> %s\n", p);
+ sm_dprintf("udbmatch ==> %s\n", p);
return p;
# endif /* NEWDB */
-# ifdef HESIOD
+# if HESIOD
case UDB_HESIOD:
key.data = keybuf;
key.size = keylen;
@@ -674,16 +678,16 @@ udbmatch(user, field)
if (i != 0 || info.size <= 0)
{
if (tTd(28, 2))
- dprintf("udbmatch: no match on %s (%d) via hesiod\n",
+ sm_dprintf("udbmatch: no match on %s (%d) via hesiod\n",
keybuf, keylen);
continue;
}
- p = xalloc(info.size + 1);
+ p = sm_rpool_malloc_x(rpool, info.size + 1);
memmove(p, info.data, info.size);
p[info.size] = '\0';
if (tTd(28, 1))
- dprintf("udbmatch ==> %s\n", p);
+ sm_dprintf("udbmatch ==> %s\n", p);
return p;
# endif /* HESIOD */
}
@@ -699,15 +703,14 @@ udbmatch(user, field)
*/
/* build database key */
- (void) strlcpy(keybuf, user, sizeof keybuf);
- (void) strlcat(keybuf, ":maildrop", sizeof keybuf);
+ (void) sm_strlcpyn(keybuf, sizeof keybuf, 2, user, ":maildrop");
keylen = strlen(keybuf);
for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
{
switch (up->udb_type)
{
-# ifdef NEWDB
+# if NEWDB
case UDB_DBFETCH:
/* get the default case for this database */
if (up->udb_default == NULL)
@@ -732,7 +735,7 @@ udbmatch(user, field)
}
/* save the default case */
- up->udb_default = xalloc(info.size + 1);
+ up->udb_default = sm_pmalloc_x(info.size + 1);
memmove(up->udb_default, info.data, info.size);
up->udb_default[info.size] = '\0';
}
@@ -758,14 +761,14 @@ udbmatch(user, field)
/* they exist -- build the actual address */
i = strlen(user) + strlen(up->udb_default) + 2;
- p = xalloc(i);
- (void) snprintf(p, i, "%s@%s", user, up->udb_default);
+ p = sm_rpool_malloc_x(rpool, i);
+ (void) sm_strlcpyn(p, i, 3, user, "@", up->udb_default);
if (tTd(28, 1))
- dprintf("udbmatch ==> %s\n", p);
+ sm_dprintf("udbmatch ==> %s\n", p);
return p;
# endif /* NEWDB */
-# ifdef HESIOD
+# if HESIOD
case UDB_HESIOD:
/* get the default case for this database */
if (up->udb_default == NULL)
@@ -782,7 +785,7 @@ udbmatch(user, field)
}
/* save the default case */
- up->udb_default = xalloc(info.size + 1);
+ up->udb_default = sm_pmalloc_x(info.size + 1);
memmove(up->udb_default, info.data, info.size);
up->udb_default[info.size] = '\0';
}
@@ -801,10 +804,10 @@ udbmatch(user, field)
/* they exist -- build the actual address */
i = strlen(user) + strlen(up->udb_default) + 2;
- p = xalloc(i);
- (void) snprintf(p, i, "%s@%s", user, up->udb_default);
+ p = sm_rpool_malloc_x(rpool, i);
+ (void) sm_strlcpyn(p, i, 3, user, "@", up->udb_default);
if (tTd(28, 1))
- dprintf("udbmatch ==> %s\n", p);
+ sm_dprintf("udbmatch ==> %s\n", p);
return p;
break;
# endif /* HESIOD */
@@ -814,7 +817,7 @@ udbmatch(user, field)
/* still nothing.... too bad */
return NULL;
}
- /*
+/*
** UDB_MAP_LOOKUP -- look up arbitrary entry in user database map
**
** Parameters:
@@ -838,10 +841,11 @@ udb_map_lookup(map, name, av, statp)
{
char *val;
char *key;
+ char *SM_NONVOLATILE result = NULL;
char keybuf[MAXNAME + 1];
if (tTd(28, 20) || tTd(38, 20))
- dprintf("udb_map_lookup(%s, %s)\n", map->map_mname, name);
+ sm_dprintf("udb_map_lookup(%s, %s)\n", map->map_mname, name);
if (bitset(MF_NOFOLDCASE, map->map_mflags))
{
@@ -858,15 +862,20 @@ udb_map_lookup(map, name, av, statp)
makelower(keybuf);
key = keybuf;
}
- val = udbmatch(key, map->map_file);
+ val = udbmatch(key, map->map_file, NULL);
if (val == NULL)
return NULL;
- if (bitset(MF_MATCHONLY, map->map_mflags))
- return map_rewrite(map, name, strlen(name), NULL);
- else
- return map_rewrite(map, val, strlen(val), av);
+ SM_TRY
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ result = map_rewrite(map, name, strlen(name), NULL);
+ else
+ result = map_rewrite(map, val, strlen(val), av);
+ SM_FINALLY
+ sm_free(val);
+ SM_END_TRY
+ return result;
}
- /*
+/*
** _UDBX_INIT -- parse the UDB specification, opening any valid entries.
**
** Parameters:
@@ -945,25 +954,25 @@ _udbx_init(e)
{
case '@': /* forward to remote host */
up->udb_type = UDB_FORWARD;
- up->udb_pid = getpid();
+ up->udb_pid = CurrentPid;
up->udb_fwdhost = spec + 1;
ents++;
up++;
break;
-# ifdef HESIOD
+# if HESIOD
case 'h': /* use hesiod */
case 'H':
- if (strcasecmp(spec, "hesiod") != 0)
+ if (sm_strcasecmp(spec, "hesiod") != 0)
goto badspec;
up->udb_type = UDB_HESIOD;
- up->udb_pid = getpid();
+ up->udb_pid = CurrentPid;
ents++;
up++;
break;
# endif /* HESIOD */
-# ifdef NEWDB
+# if NEWDB
case '/': /* look up remote name */
l = strlen(spec);
if (l > 3 && strcmp(&spec[l - 3], ".db") == 0)
@@ -972,9 +981,9 @@ _udbx_init(e)
}
else
{
- up->udb_dbname = xalloc(l + 4);
- (void) strlcpy(up->udb_dbname, spec, l + 4);
- (void) strlcat(up->udb_dbname, ".db", l + 4);
+ up->udb_dbname = sm_pmalloc_x(l + 4);
+ (void) sm_strlcpyn(up->udb_dbname, l + 4, 2,
+ spec, ".db");
}
errno = 0;
# if DB_VERSION_MAJOR < 2
@@ -1034,12 +1043,12 @@ _udbx_init(e)
int save_errno = errno;
# if DB_VERSION_MAJOR < 2
- dprintf("dbopen(%s): %s\n",
+ sm_dprintf("dbopen(%s): %s\n",
# else /* DB_VERSION_MAJOR < 2 */
- dprintf("db_open(%s): %s\n",
+ sm_dprintf("db_open(%s): %s\n",
# endif /* DB_VERSION_MAJOR < 2 */
up->udb_dbname,
- errstring(errno));
+ sm_errstring(errno));
errno = save_errno;
}
if (errno != ENOENT && errno != EACCES)
@@ -1052,34 +1061,34 @@ _udbx_init(e)
"db_open(%s): %s",
# endif /* DB_VERSION_MAJOR < 2 */
up->udb_dbname,
- errstring(errno));
+ sm_errstring(errno));
up->udb_type = UDB_EOLIST;
if (up->udb_dbname != spec)
- sm_free(up->udb_dbname);
+ sm_free(up->udb_dbname); /* XXX */
goto tempfail;
}
if (up->udb_dbname != spec)
- sm_free(up->udb_dbname);
+ sm_free(up->udb_dbname); /* XXX */
break;
}
if (tTd(28, 1))
{
# if DB_VERSION_MAJOR < 2
- dprintf("_udbx_init: dbopen(%s)\n",
+ sm_dprintf("_udbx_init: dbopen(%s)\n",
# else /* DB_VERSION_MAJOR < 2 */
- dprintf("_udbx_init: db_open(%s)\n",
+ sm_dprintf("_udbx_init: db_open(%s)\n",
# endif /* DB_VERSION_MAJOR < 2 */
up->udb_dbname);
}
up->udb_type = UDB_DBFETCH;
- up->udb_pid = getpid();
+ up->udb_pid = CurrentPid;
ents++;
up++;
break;
# endif /* NEWDB */
default:
-# ifdef HESIOD
+# if HESIOD
badspec:
# endif /* HESIOD */
syserr("Unknown UDB spec %s", spec);
@@ -1094,40 +1103,38 @@ badspec:
{
switch (up->udb_type)
{
-# if DAEMON
case UDB_REMOTE:
- dprintf("REMOTE: addr %s, timeo %d\n",
- anynet_ntoa((SOCKADDR *) &up->udb_addr),
- up->udb_timeout);
+ sm_dprintf("REMOTE: addr %s, timeo %d\n",
+ anynet_ntoa((SOCKADDR *) &up->udb_addr),
+ up->udb_timeout);
break;
-# endif /* DAEMON */
case UDB_DBFETCH:
-# ifdef NEWDB
- dprintf("FETCH: file %s\n",
+# if NEWDB
+ sm_dprintf("FETCH: file %s\n",
up->udb_dbname);
# else /* NEWDB */
- dprintf("FETCH\n");
+ sm_dprintf("FETCH\n");
# endif /* NEWDB */
break;
case UDB_FORWARD:
- dprintf("FORWARD: host %s\n",
+ sm_dprintf("FORWARD: host %s\n",
up->udb_fwdhost);
break;
case UDB_HESIOD:
- dprintf("HESIOD\n");
+ sm_dprintf("HESIOD\n");
break;
default:
- dprintf("UNKNOWN\n");
+ sm_dprintf("UNKNOWN\n");
break;
}
}
}
- UdbInitialized = TRUE;
+ UdbInitialized = true;
errno = 0;
return EX_OK;
@@ -1136,7 +1143,7 @@ badspec:
*/
tempfail:
-# ifdef NEWDB
+# if NEWDB
for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
{
if (up->udb_type == UDB_DBFETCH)
@@ -1147,7 +1154,7 @@ badspec:
errno = (*up->udb_dbp->close)(up->udb_dbp, 0);
# endif /* DB_VERSION_MAJOR < 2 */
if (tTd(28, 1))
- dprintf("_udbx_init: db->close(%s)\n",
+ sm_dprintf("_udbx_init: db->close(%s)\n",
up->udb_dbname);
}
}
@@ -1184,7 +1191,7 @@ _udb_parsespec(udbspec, opt, maxopts)
}
return optnum;
}
- /*
+/*
** _UDBX_CLOSE -- close all file based UDB entries.
**
** Parameters:
@@ -1196,20 +1203,17 @@ _udb_parsespec(udbspec, opt, maxopts)
void
_udbx_close()
{
- pid_t pid;
struct udbent *up;
if (!UdbInitialized)
return;
- pid = getpid();
-
for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
{
- if (up->udb_pid != pid)
+ if (up->udb_pid != CurrentPid)
continue;
-# ifdef NEWDB
+# if NEWDB
if (up->udb_type == UDB_DBFETCH)
{
# if DB_VERSION_MAJOR < 2
@@ -1219,13 +1223,13 @@ _udbx_close()
# endif /* DB_VERSION_MAJOR < 2 */
}
if (tTd(28, 1))
- dprintf("_udbx_init: db->close(%s)\n",
+ sm_dprintf("_udbx_init: db->close(%s)\n",
up->udb_dbname);
# endif /* NEWDB */
}
}
-# ifdef HESIOD
+# if HESIOD
static int
hes_udb_get(key, info)
@@ -1236,7 +1240,7 @@ hes_udb_get(key, info)
char **hp;
char kbuf[MAXKEY + 1];
- if (strlcpy(kbuf, key->data, sizeof kbuf) >= (SIZE_T) sizeof kbuf)
+ if (sm_strlcpy(kbuf, key->data, sizeof kbuf) >= sizeof kbuf)
return 0;
name = kbuf;
type = strrchr(name, ':');
@@ -1247,7 +1251,7 @@ hes_udb_get(key, info)
return 1;
if (tTd(28, 1))
- dprintf("hes_udb_get(%s, %s)\n", name, type);
+ sm_dprintf("hes_udb_get(%s, %s)\n", name, type);
/* make the hesiod query */
# ifdef HESIOD_INIT
@@ -1293,7 +1297,7 @@ hes_udb_get(key, info)
}
if (tTd(28, 80))
- dprintf("hes_udb_get => %s\n", *hp);
+ sm_dprintf("hes_udb_get => %s\n", *hp);
return 0;
}
diff --git a/contrib/sendmail/src/usersmtp.c b/contrib/sendmail/src/usersmtp.c
index a9476fe..cc29982 100644
--- a/contrib/sendmail/src/usersmtp.c
+++ b/contrib/sendmail/src/usersmtp.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
@@ -13,22 +13,21 @@
#include <sendmail.h>
-#ifndef lint
-# if SMTP
-static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.34 2001/06/26 21:55:23 gshapiro Exp $ (with SMTP)";
-# else /* SMTP */
-static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.34 2001/06/26 21:55:23 gshapiro Exp $ (without SMTP)";
-# endif /* SMTP */
-#endif /* ! lint */
+SM_RCSID("@(#)$Id: usersmtp.c,v 8.428 2002/01/08 00:56:23 ca Exp $")
#include <sysexits.h>
-#if SMTP
-
+extern void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int, bool));
static void datatimeout __P((void));
static void esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
static void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
+static int smtprcptstat __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *));
+
+#if SASL
+extern void *sm_sasl_malloc __P((unsigned long));
+extern void sm_sasl_free __P((void *));
+#endif /* SASL */
/*
** USERSMTP -- run SMTP protocol from the user end.
@@ -36,16 +35,19 @@ static void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
** This protocol is described in RFC821.
*/
-# define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */
-# define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */
-# define SMTPCLOSING 421 /* "Service Shutting Down" */
+#define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */
+#define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */
+#define SMTPCLOSING 421 /* "Service Shutting Down" */
-#define ENHSCN(e, d) (e) == NULL ? (d) : newstr(e)
+#define ENHSCN(e, d) ((e) == NULL ? (d) : (e))
+
+#define ENHSCN_RPOOL(e, d, rpool) \
+ ((e) == NULL ? (d) : sm_rpool_strdup_x(rpool, e))
static char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */
static char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
static bool SmtpNeedIntro; /* need "while talking" in transcript */
- /*
+/*
** SMTPINIT -- initialize SMTP.
**
** Opens the connection and sends the initial protocol.
@@ -78,8 +80,8 @@ smtpinit(m, mci, e, onlyhelo)
enhsc = NULL;
if (tTd(18, 1))
{
- dprintf("smtpinit ");
- mci_dump(mci, FALSE);
+ sm_dprintf("smtpinit ");
+ mci_dump(mci, false);
}
/*
@@ -90,10 +92,12 @@ smtpinit(m, mci, e, onlyhelo)
CurHostName = mci->mci_host; /* XXX UGLY XXX */
if (CurHostName == NULL)
CurHostName = MyHostName;
- SmtpNeedIntro = TRUE;
+ SmtpNeedIntro = true;
switch (mci->mci_state)
{
- case MCIS_ACTIVE:
+ case MCIS_MAIL:
+ case MCIS_RCPT:
+ case MCIS_DATA:
/* need to clear old information */
smtprset(m, mci, e);
/* FALLTHROUGH */
@@ -129,7 +133,7 @@ smtpinit(m, mci, e, onlyhelo)
*/
SmtpPhase = mci->mci_phase = "client greeting";
- sm_setproctitle(TRUE, e, "%s %s: %s",
+ sm_setproctitle(true, e, "%s %s: %s",
qid_printname(e), CurHostName, mci->mci_phase);
r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL);
if (r < 0)
@@ -150,12 +154,16 @@ helo:
hn = mci->mci_heloname ? mci->mci_heloname : MyHostName;
tryhelo:
+#if _FFR_IGNORE_EXT_ON_HELO
+ mci->mci_flags &= ~MCIF_HELO;
+#endif /* _FFR_IGNORE_EXT_ON_HELO */
if (bitnset(M_LMTP, m->m_flags))
{
smtpmessage("LHLO %s", m, mci, hn);
SmtpPhase = mci->mci_phase = "client LHLO";
}
- else if (bitset(MCIF_ESMTP, mci->mci_flags))
+ else if (bitset(MCIF_ESMTP, mci->mci_flags) &&
+ !bitnset(M_FSMTP, m->m_flags))
{
smtpmessage("EHLO %s", m, mci, hn);
SmtpPhase = mci->mci_phase = "client EHLO";
@@ -164,10 +172,16 @@ tryhelo:
{
smtpmessage("HELO %s", m, mci, hn);
SmtpPhase = mci->mci_phase = "client HELO";
+#if _FFR_IGNORE_EXT_ON_HELO
+ mci->mci_flags |= MCIF_HELO;
+#endif /* _FFR_IGNORE_EXT_ON_HELO */
}
- sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
+ sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
CurHostName, mci->mci_phase);
- r = reply(m, mci, e, TimeOuts.to_helo, helo_options, NULL);
+ r = reply(m, mci, e,
+ bitnset(M_LMTP, m->m_flags) ? TimeOuts.to_lhlo
+ : TimeOuts.to_helo,
+ helo_options, NULL);
if (r < 0)
goto tempfail1;
else if (REPLYTYPE(r) == 5)
@@ -195,7 +209,7 @@ tryhelo:
*p = '\0';
if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
!bitnset(M_LMTP, m->m_flags) &&
- strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
+ sm_strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
{
syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
CurHostName);
@@ -206,6 +220,7 @@ tryhelo:
return;
}
+#if !_FFR_DEPRECATE_MAILER_FLAG_I
/*
** If this is expected to be another sendmail, send some internal
** commands.
@@ -219,6 +234,7 @@ tryhelo:
if (r < 0)
goto tempfail1;
}
+#endif /* !_FFR_DEPRECATE_MAILER_FLAG_I */
if (mci->mci_state != MCIS_CLOSED)
{
@@ -229,16 +245,12 @@ tryhelo:
/* got a 421 error code during startup */
tempfail1:
- if (mci->mci_errno == 0)
- mci->mci_errno = errno;
mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
if (mci->mci_state != MCIS_CLOSED)
smtpquit(m, mci, e);
return;
tempfail2:
- if (mci->mci_errno == 0)
- mci->mci_errno = errno;
/* XXX should use code from other end iff ENHANCEDSTATUSCODES */
mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
SmtpReplyBuffer);
@@ -247,12 +259,11 @@ tryhelo:
return;
unavailable:
- mci->mci_errno = errno;
mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
smtpquit(m, mci, e);
return;
}
- /*
+/*
** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
**
** Parameters:
@@ -276,23 +287,38 @@ esmtp_check(line, firstline, m, mci, e)
{
if (strstr(line, "ESMTP") != NULL)
mci->mci_flags |= MCIF_ESMTP;
+
+ /*
+ ** Dirty hack below. Quoting the author:
+ ** This was a response to people who wanted SMTP transmission to be
+ ** just-send-8 by default. Essentially, you could put this tag into
+ ** your greeting message to behave as though the F=8 flag was set on
+ ** the mailer.
+ */
+
if (strstr(line, "8BIT-OK") != NULL)
mci->mci_flags |= MCIF_8BITOK;
}
-# if SASL
- /*
+
+#if SASL
+/* specify prototype so compiler can check calls */
+static char *str_union __P((char *, char *, SM_RPOOL_T *));
+
+/*
** STR_UNION -- create the union of two lists
**
** Parameters:
** s1, s2 -- lists of items (separated by single blanks).
+** rpool -- resource pool from which result is allocated.
**
** Returns:
** the union of both lists.
*/
static char *
-str_union(s1, s2)
+str_union(s1, s2, rpool)
char *s1, *s2;
+ SM_RPOOL_T *rpool;
{
char *hr, *h1, *h, *res;
int l1, l2, rl;
@@ -304,8 +330,14 @@ str_union(s1, s2)
l1 = strlen(s1);
l2 = strlen(s2);
rl = l1 + l2;
- res = (char *)xalloc(rl + 2);
- (void) strlcpy(res, s1, rl);
+ res = (char *) sm_rpool_malloc(rpool, rl + 2);
+ if (res == NULL)
+ {
+ if (l1 > l2)
+ return s1;
+ return s2;
+ }
+ (void) sm_strlcpy(res, s1, rl);
hr = res + l1;
h1 = s2;
h = s2;
@@ -340,8 +372,9 @@ str_union(s1, s2)
}
return res;
}
-# endif /* SASL */
- /*
+#endif /* SASL */
+
+/*
** HELO_OPTIONS -- process the options on a HELO line.
**
** Parameters:
@@ -349,7 +382,7 @@ str_union(s1, s2)
** firstline -- set if this is the first line of the reply.
** m -- the mailer.
** mci -- the mailer connection info.
-** e -- the envelope.
+** e -- the envelope (unused).
**
** Returns:
** none.
@@ -364,62 +397,85 @@ helo_options(line, firstline, m, mci, e)
ENVELOPE *e;
{
register char *p;
+#if _FFR_IGNORE_EXT_ON_HELO
+ static bool logged = false;
+#endif /* _FFR_IGNORE_EXT_ON_HELO */
if (firstline)
{
-# if SASL
- if (mci->mci_saslcap != NULL)
- sm_free(mci->mci_saslcap);
+#if SASL
mci->mci_saslcap = NULL;
-# endif /* SASL */
+#endif /* SASL */
+#if _FFR_IGNORE_EXT_ON_HELO
+ logged = false;
+#endif /* _FFR_IGNORE_EXT_ON_HELO */
+ return;
+ }
+#if _FFR_IGNORE_EXT_ON_HELO
+ else if (bitset(MCIF_HELO, mci->mci_flags))
+ {
+ if (LogLevel > 8 && !logged)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "server=%s [%s] returned extensions despite HELO command",
+ macvalue(macid("{server_name}"), e),
+ macvalue(macid("{server_addr}"), e));
+ logged = true;
+ }
return;
}
+#endif /* _FFR_IGNORE_EXT_ON_HELO */
- if (strlen(line) < (SIZE_T) 5)
+ if (strlen(line) < 5)
return;
line += 4;
p = strpbrk(line, " =");
if (p != NULL)
*p++ = '\0';
- if (strcasecmp(line, "size") == 0)
+ if (sm_strcasecmp(line, "size") == 0)
{
mci->mci_flags |= MCIF_SIZE;
if (p != NULL)
mci->mci_maxsize = atol(p);
}
- else if (strcasecmp(line, "8bitmime") == 0)
+ else if (sm_strcasecmp(line, "8bitmime") == 0)
{
mci->mci_flags |= MCIF_8BITMIME;
mci->mci_flags &= ~MCIF_7BIT;
}
- else if (strcasecmp(line, "expn") == 0)
+ else if (sm_strcasecmp(line, "expn") == 0)
mci->mci_flags |= MCIF_EXPN;
- else if (strcasecmp(line, "dsn") == 0)
+ else if (sm_strcasecmp(line, "dsn") == 0)
mci->mci_flags |= MCIF_DSN;
- else if (strcasecmp(line, "enhancedstatuscodes") == 0)
+ else if (sm_strcasecmp(line, "enhancedstatuscodes") == 0)
mci->mci_flags |= MCIF_ENHSTAT;
-# if STARTTLS
- else if (strcasecmp(line, "starttls") == 0)
+ else if (sm_strcasecmp(line, "pipelining") == 0)
+ mci->mci_flags |= MCIF_PIPELINED;
+#if STARTTLS
+ else if (sm_strcasecmp(line, "starttls") == 0)
mci->mci_flags |= MCIF_TLS;
-# endif /* STARTTLS */
-# if SASL
- else if (strcasecmp(line, "auth") == 0)
+#endif /* STARTTLS */
+ else if (sm_strcasecmp(line, "deliverby") == 0)
+ {
+ mci->mci_flags |= MCIF_DLVR_BY;
+ if (p != NULL)
+ mci->mci_min_by = atol(p);
+ }
+#if SASL
+ else if (sm_strcasecmp(line, "auth") == 0)
{
if (p != NULL && *p != '\0')
{
if (mci->mci_saslcap != NULL)
{
- char *h;
-
/*
- ** create the union with previous auth
+ ** Create the union with previous auth
** offerings because we recognize "auth "
** and "auth=" (old format).
*/
- h = mci->mci_saslcap;
- mci->mci_saslcap = str_union(h, p);
- if (h != mci->mci_saslcap)
- sm_free(h);
+
+ mci->mci_saslcap = str_union(mci->mci_saslcap,
+ p, mci->mci_rpool);
mci->mci_flags |= MCIF_AUTH;
}
else
@@ -427,34 +483,115 @@ helo_options(line, firstline, m, mci, e)
int l;
l = strlen(p) + 1;
- mci->mci_saslcap = (char *)xalloc(l);
- (void) strlcpy(mci->mci_saslcap, p, l);
- mci->mci_flags |= MCIF_AUTH;
+ mci->mci_saslcap = (char *)
+ sm_rpool_malloc(mci->mci_rpool, l);
+ if (mci->mci_saslcap != NULL)
+ {
+ (void) sm_strlcpy(mci->mci_saslcap, p,
+ l);
+ mci->mci_flags |= MCIF_AUTH;
+ }
}
}
}
-# endif /* SASL */
+#endif /* SASL */
+}
+#if SASL
+
+static int getsimple __P((void *, int, const char **, unsigned *));
+static int getsecret __P((sasl_conn_t *, void *, int, sasl_secret_t **));
+static int saslgetrealm __P((void *, int, const char **, const char **));
+static int readauth __P((char *, bool, SASL_AI_T *m, SM_RPOOL_T *));
+static int getauth __P((MCI *, ENVELOPE *, SASL_AI_T *));
+static char *removemech __P((char *, char *, SM_RPOOL_T *));
+static int attemptauth __P((MAILER *, MCI *, ENVELOPE *, SASL_AI_T *));
+
+static sasl_callback_t callbacks[] =
+{
+ { SASL_CB_GETREALM, &saslgetrealm, NULL },
+#define CB_GETREALM_IDX 0
+ { SASL_CB_PASS, &getsecret, NULL },
+#define CB_PASS_IDX 1
+ { SASL_CB_USER, &getsimple, NULL },
+#define CB_USER_IDX 2
+ { SASL_CB_AUTHNAME, &getsimple, NULL },
+#define CB_AUTHNAME_IDX 3
+ { SASL_CB_VERIFYFILE, &safesaslfile, NULL },
+#define CB_SAFESASL_IDX 4
+ { SASL_CB_LIST_END, NULL, NULL }
+};
+
+/*
+** INIT_SASL_CLIENT -- initialize client side of Cyrus-SASL
+**
+** Parameters:
+** none.
+**
+** Returns:
+** SASL_OK -- if successful.
+** SASL error code -- otherwise.
+**
+** Side Effects:
+** checks/sets sasl_clt_init.
+*/
+
+static bool sasl_clt_init = false;
+
+static int
+init_sasl_client()
+{
+ int result;
+
+ if (sasl_clt_init)
+ return SASL_OK;
+ result = sasl_client_init(callbacks);
+
+ /* should we retry later again or just remember that it failed? */
+ if (result == SASL_OK)
+ sasl_clt_init = true;
+ return result;
}
-# if SASL
+/*
+** STOP_SASL_CLIENT -- shutdown client side of Cyrus-SASL
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** checks/sets sasl_clt_init.
+*/
- /*
+void
+stop_sasl_client()
+{
+ if (!sasl_clt_init)
+ return;
+ sasl_clt_init = false;
+ sasl_done();
+}
+/*
** GETSASLDATA -- process the challenges from the SASL protocol
**
** This gets the relevant sasl response data out of the reply
-** from the server
+** from the server.
**
** Parameters:
** line -- the response line.
** firstline -- set if this is the first line of the reply.
** m -- the mailer.
** mci -- the mailer connection info.
-** e -- the envelope.
+** e -- the envelope (unused).
**
** Returns:
** none.
*/
-void
+static void getsasldata __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
+
+static void
getsasldata(line, firstline, m, mci, e)
char *line;
bool firstline;
@@ -463,96 +600,94 @@ getsasldata(line, firstline, m, mci, e)
ENVELOPE *e;
{
int len;
- char *out;
int result;
+ char *out;
/* if not a continue we don't care about it */
- if ((strlen(line) <= 4) ||
+ len = strlen(line);
+ if ((len <= 4) ||
(line[0] != '3') ||
- (line[1] != '3') ||
- (line[2] != '4'))
+ !isascii(line[1]) || !isdigit(line[1]) ||
+ !isascii(line[2]) || !isdigit(line[2]))
{
- mci->mci_sasl_string = NULL;
+ SM_FREE_CLR(mci->mci_sasl_string);
return;
}
/* forget about "334 " */
line += 4;
- len = strlen(line);
+ len -= 4;
- out = xalloc(len + 1);
- result = sasl_decode64(line, len, out, (u_int *)&len);
+ out = (char *) sm_rpool_malloc_x(mci->mci_rpool, len + 1);
+ result = sasl_decode64(line, len, out, (unsigned int *)&len);
if (result != SASL_OK)
{
len = 0;
*out = '\0';
}
+
+ /*
+ ** mci_sasl_string is "shared" with Cyrus-SASL library; hence
+ ** it can't be in an rpool unless we use the same memory
+ ** management mechanism (with same rpool!) for Cyrus SASL.
+ */
+
if (mci->mci_sasl_string != NULL)
{
if (mci->mci_sasl_string_len <= len)
{
- sm_free(mci->mci_sasl_string);
+ sm_free(mci->mci_sasl_string); /* XXX */
mci->mci_sasl_string = xalloc(len + 1);
}
}
else
mci->mci_sasl_string = xalloc(len + 1);
- /* XXX this is probably leaked */
+
memcpy(mci->mci_sasl_string, out, len);
mci->mci_sasl_string[len] = '\0';
mci->mci_sasl_string_len = len;
- sm_free(out);
return;
}
-
- /*
-** READAUTH -- read auth value from a file
+/*
+** READAUTH -- read auth values from a file
**
** Parameters:
-** l -- line to define.
** filename -- name of file to read.
** safe -- if set, this is a safe read.
+** sai -- where to store auth_info.
+** rpool -- resource pool for sai.
**
** Returns:
-** line from file
-**
-** Side Effects:
-** overwrites local static buffer. The caller should copy
-** the result.
-**
+** EX_OK -- data succesfully read.
+** EX_UNAVAILABLE -- no valid filename.
+** EX_TEMPFAIL -- temporary failure.
*/
-/* lines in authinfo file */
-# define SASL_USER 1
-# define SASL_AUTHID 2
-# define SASL_PASSWORD 3
-# define SASL_DEFREALM 4
-# define SASL_MECH 5
-
static char *sasl_info_name[] =
{
- "",
"user id",
- "authorization id",
+ "authentication id",
"password",
"realm",
- "mechanism"
+ "mechlist"
};
-
-static char *
-readauth(l, filename, safe)
- int l;
+static int
+readauth(filename, safe, sai, rpool)
char *filename;
bool safe;
+ SASL_AI_T *sai;
+ SM_RPOOL_T *rpool;
{
- FILE *f;
+ SM_FILE_T *f;
long sff;
pid_t pid;
int lc;
- static char buf[MAXLINE];
+ char *s;
+ char buf[MAXLINE];
if (filename == NULL || filename[0] == '\0')
- return "";
+ return EX_UNAVAILABLE;
+
#if !_FFR_ALLOW_SASLINFO
/*
** make sure we don't use a program that is not
@@ -560,6 +695,7 @@ readauth(l, filename, safe)
** However, currently we don't pass this info (authinfo file
** specified by user) around, so we just turn off program access.
*/
+
if (filename[0] == '|')
{
auto int fd;
@@ -580,22 +716,29 @@ readauth(l, filename, safe)
if (pid < 0)
f = NULL;
else
- f = fdopen(fd, "r");
+ f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
+ (void *) &fd, SM_IO_RDONLY, NULL);
}
else
#endif /* !_FFR_ALLOW_SASLINFO */
{
pid = -1;
- sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK
- | SFF_NOGWFILES | SFF_NOWWFILES | SFF_NORFILES;
+ sff = SFF_REGONLY|SFF_SAFEDIRPATH|SFF_NOWLINK
+ |SFF_NOGWFILES|SFF_NOWWFILES|SFF_NOWRFILES;
+# if _FFR_GROUPREADABLEAUTHINFOFILE
+ if (!bitnset(DBS_GROUPREADABLEAUTHINFOFILE, DontBlameSendmail))
+# endif /* _FFR_GROUPREADABLEAUTHINFOFILE */
+ sff |= SFF_NOGRFILES;
if (DontLockReadFiles)
sff |= SFF_NOLOCK;
+
#if _FFR_ALLOW_SASLINFO
/*
** XXX: make sure we don't read or open files that are not
** accesible to the user who specified a different authinfo
** file.
*/
+
sff |= SFF_MUSTOWN;
#else /* _FFR_ALLOW_SASLINFO */
if (safe)
@@ -606,62 +749,209 @@ readauth(l, filename, safe)
}
if (f == NULL)
{
- syserr("readauth: cannot open %s", filename);
- return "";
+ if (LogLevel > 5)
+ sm_syslog(LOG_ERR, NOQID,
+ "AUTH=client, error: can't open %s: %s",
+ filename, sm_errstring(errno));
+ return EX_TEMPFAIL;
}
lc = 0;
- while (lc < l && fgets(buf, sizeof buf, f) != NULL)
+ while (lc <= SASL_MECHLIST &&
+ sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
{
if (buf[0] != '#')
+ {
+ (*sai)[lc] = sm_rpool_strdup_x(rpool, buf);
+ if ((s = strchr((*sai)[lc], '\n')) != NULL)
+ *s = '\0';
lc++;
+ }
}
- (void) fclose(f);
+ (void) sm_io_close(f, SM_TIME_DEFAULT);
if (pid > 0)
(void) waitfor(pid);
- if (lc < l)
- {
- if (LogLevel >= 9)
- sm_syslog(LOG_WARNING, NOQID, "SASL: error: can't read %s from %s",
- sasl_info_name[l], filename);
- return "";
- }
- lc = strlen(buf) - 1;
- if (lc >= 0)
- buf[lc] = '\0';
- if (tTd(95, 6))
- dprintf("readauth(%s, %d) = '%s'\n", filename, l, buf);
- return buf;
+ if (lc < SASL_PASSWORD)
+ {
+ if (LogLevel > 8)
+ sm_syslog(LOG_ERR, NOQID,
+ "AUTH=client, error: can't read %s from %s",
+ sasl_info_name[lc + 1], filename);
+ return EX_TEMPFAIL;
+ }
+ return EX_OK;
}
-# ifndef __attribute__
-# define __attribute__(x)
-# endif /* ! __attribute__ */
-
-static int getsimple __P((void *, int, const char **, unsigned *));
-static int getsecret __P((sasl_conn_t *, void *, int, sasl_secret_t **));
-static int saslgetrealm __P((void *, int, const char **, const char **));
+/*
+** GETAUTH -- get authinfo from ruleset call
+**
+** {server_name}, {server_addr} must be set
+**
+** Parameters:
+** mci -- the mailer connection structure.
+** e -- the envelope (including the sender to specify).
+** sai -- pointer to authinfo (result).
+**
+** Returns:
+** EX_OK -- ruleset was succesfully called, data may not
+** be available, sai must be checked.
+** EX_UNAVAILABLE -- ruleset unavailable (or failed).
+** EX_TEMPFAIL -- temporary failure (from ruleset).
+**
+** Side Effects:
+** Fills in sai if successful.
+*/
-static sasl_callback_t callbacks[] =
+static int
+getauth(mci, e, sai)
+ MCI *mci;
+ ENVELOPE *e;
+ SASL_AI_T *sai;
{
- { SASL_CB_GETREALM, &saslgetrealm, NULL },
-# define CB_GETREALM_IDX 0
- { SASL_CB_PASS, &getsecret, NULL },
-# define CB_PASS_IDX 1
- { SASL_CB_USER, &getsimple, NULL },
-# define CB_USER_IDX 2
- { SASL_CB_AUTHNAME, &getsimple, NULL },
-# define CB_AUTHNAME_IDX 3
- { SASL_CB_VERIFYFILE, &safesaslfile, NULL },
- { SASL_CB_LIST_END, NULL, NULL }
-};
+ int i, r, l, got, ret;
+ char **pvp;
+ char pvpbuf[PSBUFSIZE];
+
+ r = rscap("authinfo", macvalue(macid("{server_name}"), e),
+ macvalue(macid("{server_addr}"), e), e,
+ &pvp, pvpbuf, sizeof(pvpbuf));
+
+ if (r != EX_OK)
+ return EX_UNAVAILABLE;
+
+ /* other than expected return value: ok (i.e., no auth) */
+ if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
+ return EX_OK;
+ if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
+ return EX_TEMPFAIL;
- /*
+ /*
+ ** parse the data, put it into sai
+ ** format: "TDstring" (including the '"' !)
+ ** where T is a tag: 'U', ...
+ ** D is a delimiter: ':' or '='
+ */
+
+ ret = EX_OK; /* default return value */
+ i = 0;
+ got = 0;
+ while (i < SASL_ENTRIES)
+ {
+ if (pvp[i + 1] == NULL)
+ break;
+ if (pvp[i + 1][0] != '"')
+ break;
+ switch (pvp[i + 1][1])
+ {
+ case 'U':
+ case 'u':
+ r = SASL_USER;
+ break;
+ case 'I':
+ case 'i':
+ r = SASL_AUTHID;
+ break;
+ case 'P':
+ case 'p':
+ r = SASL_PASSWORD;
+ break;
+ case 'R':
+ case 'r':
+ r = SASL_DEFREALM;
+ break;
+ case 'M':
+ case 'm':
+ r = SASL_MECHLIST;
+ break;
+ default:
+ goto fail;
+ }
+ l = strlen(pvp[i + 1]);
+
+ /* check syntax */
+ if (l <= 3 || pvp[i + 1][l - 1] != '"')
+ goto fail;
+
+ /* remove closing quote */
+ pvp[i + 1][l - 1] = '\0';
+
+ /* remove "TD and " */
+ l -= 4;
+ (*sai)[r] = (char *) sm_rpool_malloc(mci->mci_rpool, l + 1);
+ if ((*sai)[r] == NULL)
+ goto tempfail;
+ if (pvp[i + 1][2] == ':')
+ {
+ /* ':text' (just copy) */
+ (void) sm_strlcpy((*sai)[r], pvp[i + 1] + 3, l + 1);
+ got |= 1 << r;
+ }
+ else if (pvp[i + 1][2] == '=')
+ {
+ unsigned int len;
+
+ /* '=base64' (decode) */
+ r = sasl_decode64(pvp[i + 1] + 3,
+ (unsigned int) l, (*sai)[r], &len);
+ if (r != SASL_OK)
+ goto fail;
+ got |= 1 << r;
+ }
+ else
+ goto fail;
+ if (tTd(95, 5))
+ sm_syslog(LOG_WARNING, NOQID, "getauth %s=%s",
+ sasl_info_name[r], (*sai)[r]);
+ ++i;
+ }
+
+ /* did we get the expected data? */
+ if (!(bitset(SASL_USER_BIT|SASL_AUTHID_BIT, got) &&
+ bitset(SASL_PASSWORD_BIT, got)))
+ goto fail;
+
+ /* no authid? copy uid */
+ if (!bitset(SASL_AUTHID_BIT, got))
+ {
+ l = strlen((*sai)[SASL_USER]) + 1;
+ (*sai)[SASL_AUTHID] = (char *) sm_rpool_malloc(mci->mci_rpool,
+ l + 1);
+ if ((*sai)[SASL_AUTHID] == NULL)
+ goto tempfail;
+ (void) sm_strlcpy((*sai)[SASL_AUTHID], (*sai)[SASL_USER], l);
+ }
+
+ /* no uid? copy authid */
+ if (!bitset(SASL_USER_BIT, got))
+ {
+ l = strlen((*sai)[SASL_AUTHID]) + 1;
+ (*sai)[SASL_USER] = (char *) sm_rpool_malloc(mci->mci_rpool,
+ l + 1);
+ if ((*sai)[SASL_USER] == NULL)
+ goto tempfail;
+ (void) sm_strlcpy((*sai)[SASL_USER], (*sai)[SASL_AUTHID], l);
+ }
+ return EX_OK;
+
+ tempfail:
+ ret = EX_TEMPFAIL;
+ fail:
+ if (LogLevel > 8)
+ sm_syslog(LOG_WARNING, NOQID,
+ "AUTH=client, relay=%.64s [%.16s], authinfo %sfailed",
+ macvalue(macid("{server_name}"), e),
+ macvalue(macid("{server_addr}"), e),
+ ret == EX_TEMPFAIL ? "temp" : "");
+ for (i = 0; i <= SASL_MECHLIST; i++)
+ (*sai)[i] = NULL; /* just clear; rpool */
+ return ret;
+}
+/*
** GETSIMPLE -- callback to get userid or authid
**
** Parameters:
-** context -- unused
+** context -- sai
** id -- what to do
** result -- (pointer to) result
** len -- (pointer to) length of result
@@ -672,80 +962,128 @@ static sasl_callback_t callbacks[] =
static int
getsimple(context, id, result, len)
- void *context __attribute__((unused));
+ void *context;
int id;
const char **result;
unsigned *len;
{
- char *h;
-# if SASL > 10509
- int addrealm;
- static int addedrealm = FALSE;
-# endif /* SASL > 10509 */
- static char *user = NULL;
- static char *authid = NULL;
-
- if (result == NULL)
+ char *h, *s;
+# if SASL > 10509
+ bool addrealm;
+# endif /* SASL > 10509 */
+ size_t l;
+ SASL_AI_T *sai;
+ char *authid = NULL;
+
+ if (result == NULL || context == NULL)
return SASL_BADPARAM;
+ sai = (SASL_AI_T *) context;
+
+ /*
+ ** Unfortunately it is not clear whether this routine should
+ ** return a copy of a string or just a pointer to a string.
+ ** The Cyrus-SASL plugins treat these return values differently, e.g.,
+ ** plugins/cram.c free()s authid, plugings/digestmd5.c does not.
+ ** The best solution to this problem is to fix Cyrus-SASL, but it
+ ** seems there is nobody who creates patches... Hello CMU!?
+ ** The second best solution is to have flags that tell this routine
+ ** whether to return an malloc()ed copy.
+ ** The next best solution is to always return an malloc()ed copy,
+ ** and suffer from some memory leak, which is ugly for persistent
+ ** queue runners.
+ ** For now we go with the last solution...
+ ** We can't use rpools (which would avoid this particular problem)
+ ** as explained in sasl.c.
+ */
switch (id)
{
case SASL_CB_USER:
- if (user == NULL)
+ l = strlen((*sai)[SASL_USER]) + 1;
+ s = sm_sasl_malloc(l);
+ if (s == NULL)
{
- h = readauth(SASL_USER, SASLInfo, TRUE);
- user = newstr(h);
+ if (len != NULL)
+ *len = 0;
+ *result = NULL;
+ return SASL_NOMEM;
}
- *result = user;
+ (void) sm_strlcpy(s, (*sai)[SASL_USER], l);
+ *result = s;
if (tTd(95, 5))
- dprintf("AUTH username '%s'\n", *result);
+ sm_syslog(LOG_WARNING, NOQID, "AUTH username '%s'",
+ *result);
if (len != NULL)
- *len = user ? strlen(user) : 0;
+ *len = *result != NULL ? strlen(*result) : 0;
break;
case SASL_CB_AUTHNAME:
-# if SASL > 10509
+ h = (*sai)[SASL_AUTHID];
+# if SASL > 10509
/* XXX maybe other mechanisms too?! */
- addrealm = context != NULL &&
- strcasecmp(context, "CRAM-MD5") == 0;
- if (addedrealm != addrealm && authid != NULL)
- {
-# if SASL > 10522
- /*
- ** digest-md5 prior to 1.5.23 doesn't copy the
- ** value it gets from the callback, but free()s
- ** it later on
- ** workaround: don't free() it here
- ** this can cause a memory leak!
- */
+ addrealm = (*sai)[SASL_MECH] != NULL &&
+ sm_strcasecmp((*sai)[SASL_MECH], "CRAM-MD5") == 0;
- sm_free(authid);
-# endif /* SASL > 10522 */
- authid = NULL;
- addedrealm = addrealm;
- }
-# endif /* SASL > 10509 */
- if (authid == NULL)
+ /*
+ ** Add realm to authentication id unless authid contains
+ ** '@' (i.e., a realm) or the default realm is empty.
+ */
+
+ if (addrealm && h != NULL && strchr(h, '@') == NULL)
{
- h = readauth(SASL_AUTHID, SASLInfo, TRUE);
-# if SASL > 10509
- if (addrealm && strchr(h, '@') == NULL)
+ /* has this been done before? */
+ if ((*sai)[SASL_ID_REALM] == NULL)
{
- size_t l;
char *realm;
- realm = callbacks[CB_GETREALM_IDX].context;
- l = strlen(h) + strlen(realm) + 2;
- authid = xalloc(l);
- snprintf(authid, l, "%s@%s", h, realm);
+ realm = (*sai)[SASL_DEFREALM];
+
+ /* do not add an empty realm */
+ if (*realm == '\0')
+ {
+ authid = h;
+ (*sai)[SASL_ID_REALM] = NULL;
+ }
+ else
+ {
+ l = strlen(h) + strlen(realm) + 2;
+
+ /* should use rpool, but from where? */
+ authid = sm_sasl_malloc(l);
+ if (authid != NULL)
+ {
+ (void) sm_snprintf(authid, l,
+ "%s@%s",
+ h, realm);
+ (*sai)[SASL_ID_REALM] = authid;
+ }
+ else
+ {
+ authid = h;
+ (*sai)[SASL_ID_REALM] = NULL;
+ }
+ }
}
else
-# endif /* SASL > 10509 */
- authid = newstr(h);
+ authid = (*sai)[SASL_ID_REALM];
+ }
+ else
+# endif /* SASL > 10509 */
+ authid = h;
+ l = strlen(authid) + 1;
+ s = sm_sasl_malloc(l);
+ if (s == NULL)
+ {
+ if (len != NULL)
+ *len = 0;
+ *result = NULL;
+ return SASL_NOMEM;
}
- *result = authid;
+ (void) sm_strlcpy(s, authid, l);
+ *result = s;
if (tTd(95, 5))
- dprintf("AUTH authid '%s'\n", *result);
+ sm_syslog(LOG_WARNING, NOQID, "AUTH authid '%s'",
+ *result);
if (len != NULL)
*len = authid ? strlen(authid) : 0;
break;
@@ -761,13 +1099,12 @@ getsimple(context, id, result, len)
}
return SASL_OK;
}
-
- /*
+/*
** GETSECRET -- callback to get password
**
** Parameters:
** conn -- connection information
-** context -- unused
+** context -- sai
** id -- what to do
** psecret -- (pointer to) result
**
@@ -778,30 +1115,29 @@ getsimple(context, id, result, len)
static int
getsecret(conn, context, id, psecret)
sasl_conn_t *conn;
- void *context __attribute__((unused));
+ SM_UNUSED(void *context);
int id;
sasl_secret_t **psecret;
{
- char *h;
int len;
- static char *authpass = NULL;
+ char *authpass;
+ SASL_AI_T *sai;
if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
return SASL_BADPARAM;
- if (authpass == NULL)
- {
- h = readauth(SASL_PASSWORD, SASLInfo, TRUE);
- authpass = newstr(h);
- }
+ sai = (SASL_AI_T *) context;
+ authpass = (*sai)[SASL_PASSWORD];
len = strlen(authpass);
- *psecret = (sasl_secret_t *) xalloc(sizeof(sasl_secret_t) + len + 1);
- (void) strlcpy((*psecret)->data, authpass, len + 1);
- (*psecret)->len = len;
+ *psecret = (sasl_secret_t *) sm_sasl_malloc(sizeof(sasl_secret_t) +
+ len + 1);
+ if (*psecret == NULL)
+ return SASL_FAIL;
+ (void) sm_strlcpy((*psecret)->data, authpass, len + 1);
+ (*psecret)->len = (unsigned long) len;
return SASL_OK;
}
-
- /*
+/*
** SAFESASLFILE -- callback for sasl: is file safe?
**
** Parameters:
@@ -810,66 +1146,70 @@ getsecret(conn, context, id, psecret)
** type -- type of file to check
**
** Returns:
-** SASL_OK: file can be used
-** SASL_CONTINUE: don't use file
-** SASL_FAIL: failure (not used here)
+** SASL_OK -- file can be used
+** SASL_CONTINUE -- don't use file
+** SASL_FAIL -- failure (not used here)
**
*/
+
int
-# if SASL > 10515
+#if SASL > 10515
safesaslfile(context, file, type)
-# else /* SASL > 10515 */
+#else /* SASL > 10515 */
safesaslfile(context, file)
-# endif /* SASL > 10515 */
+#endif /* SASL > 10515 */
void *context;
char *file;
-# if SASL > 10515
+#if SASL > 10515
int type;
-# endif /* SASL > 10515 */
+#endif /* SASL > 10515 */
{
long sff;
int r;
+#if SASL <= 10515
+ size_t len;
+#endif /* SASL <= 10515 */
char *p;
if (file == NULL || *file == '\0')
return SASL_OK;
-
- sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOGWFILES|SFF_NOWWFILES|SFF_ROOTOK;
+ sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOWWFILES|SFF_ROOTOK;
+#if SASL <= 10515
if ((p = strrchr(file, '/')) == NULL)
p = file;
else
++p;
-# if SASL <= 10515
/* everything beside libs and .conf files must not be readable */
- r = strlen(p);
- if ((r <= 3 || strncmp(p, "lib", 3) != 0) &&
- (r <= 5 || strncmp(p + r - 5, ".conf", 5) != 0)
-# if _FFR_UNSAFE_SASL
- && !bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail)
-# endif /* _FFR_UNSAFE_SASL */
- )
- sff |= SFF_NORFILES;
-# else /* SASL > 10515 */
+ len = strlen(p);
+ if ((len <= 3 || strncmp(p, "lib", 3) != 0) &&
+ (len <= 5 || strncmp(p + len - 5, ".conf", 5) != 0))
+ {
+ if (!bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
+ sff |= SFF_NORFILES;
+ if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
+ sff |= SFF_NOGWFILES;
+ }
+#else /* SASL <= 10515 */
/* files containing passwords should be not readable */
if (type == SASL_VRFY_PASSWD)
{
-# if _FFR_UNSAFE_SASL
- if (bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail))
+ if (bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
sff |= SFF_NOWRFILES;
else
-# endif /* _FFR_UNSAFE_SASL */
sff |= SFF_NORFILES;
+ if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
+ sff |= SFF_NOGWFILES;
}
-# endif /* SASL <= 10515 */
+#endif /* SASL <= 10515 */
p = file;
if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
S_IRUSR, NULL)) == 0)
return SASL_OK;
- if (LogLevel >= 11 || (r != ENOENT && LogLevel >= 9))
+ if (LogLevel > (r != ENOENT ? 8 : 10))
sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
- p, errstring(r));
+ p, sm_errstring(r));
return SASL_CONTINUE;
}
@@ -880,7 +1220,6 @@ safesaslfile(context, file)
**
** Parameters:
** context -- context shared between invocations
-** here: realm to return
** availrealms -- list of available realms
** {realm, realm, ...}
** result -- pointer to result
@@ -888,6 +1227,7 @@ safesaslfile(context, file)
** Returns:
** failure/success
*/
+
static int
saslgetrealm(context, id, availrealms, result)
void *context;
@@ -895,14 +1235,22 @@ saslgetrealm(context, id, availrealms, result)
const char **availrealms;
const char **result;
{
- if (LogLevel > 12)
- sm_syslog(LOG_INFO, NOQID, "saslgetrealm: realm %s available realms %s",
- context == NULL ? "<No Context>" : (char *) context,
- (availrealms == NULL || *availrealms == NULL) ? "<No Realms>" : *availrealms);
- if (context == NULL)
+ char *r;
+ SASL_AI_T *sai;
+
+ sai = (SASL_AI_T *) context;
+ if (sai == NULL)
return SASL_FAIL;
+ r = (*sai)[SASL_DEFREALM];
+
+ if (LogLevel > 12)
+ sm_syslog(LOG_INFO, NOQID,
+ "AUTH=client, realm=%s, available realms=%s",
+ r == NULL ? "<No Realm>" : r,
+ (availrealms == NULL || *availrealms == NULL)
+ ? "<No Realms>" : *availrealms);
- /* check whether context is in list? */
+ /* check whether context is in list */
if (availrealms != NULL && *availrealms != NULL)
{
if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
@@ -910,15 +1258,15 @@ saslgetrealm(context, id, availrealms, result)
{
if (LogLevel > 8)
sm_syslog(LOG_ERR, NOQID,
- "saslgetrealm: realm %s not in list %s",
- context, *availrealms);
+ "AUTH=client, realm=%s not in list=%s",
+ r, *availrealms);
return SASL_FAIL;
}
}
- *result = (char *)context;
+ *result = r;
return SASL_OK;
}
- /*
+/*
** ITEMINLIST -- does item appear in list?
**
** Check whether item appears in list (which must be separated by a
@@ -952,7 +1300,7 @@ iteminlist(item, list, delim)
len = strlen(item);
while (s != NULL && *s != '\0')
{
- if (strncasecmp(s, item, len) == 0 &&
+ if (sm_strncasecmp(s, item, len) == 0 &&
(s[len] == '\0' || strchr(delim, s[len]) != NULL))
return s;
s = strpbrk(s, delim);
@@ -962,21 +1310,23 @@ iteminlist(item, list, delim)
}
return NULL;
}
- /*
+/*
** REMOVEMECH -- remove item [rem] from list [list]
**
** Parameters:
** rem -- item to remove
** list -- list of items
+** rpool -- resource pool from which result is allocated.
**
** Returns:
** pointer to new list (NULL in case of error).
*/
-char *
-removemech(rem, list)
+static char *
+removemech(rem, list, rpool)
char *rem;
char *list;
+ SM_RPOOL_T *rpool;
{
char *ret;
char *needle;
@@ -999,13 +1349,13 @@ removemech(rem, list)
/* length of string without rem */
len = strlen(list) - strlen(rem);
- if (len == 0)
+ if (len <= 0)
{
- ret = xalloc(1); /* XXX leaked */
+ ret = (char *) sm_rpool_malloc_x(rpool, 1);
*ret = '\0';
return ret;
}
- ret = xalloc(len); /* XXX leaked */
+ ret = (char *) sm_rpool_malloc_x(rpool, len);
memset(ret, '\0', len);
/* copy from start to removed item */
@@ -1024,126 +1374,69 @@ removemech(rem, list)
ret[(needle - list) - 1] = '\0';
return ret;
}
- /*
-** INTERSECT -- create the intersection between two lists
-**
-** Parameters:
-** s1, s2 -- lists of items (separated by single blanks).
-**
-** Returns:
-** the intersection of both lists.
-*/
-
-char *
-intersect(s1, s2)
- char *s1, *s2;
-{
- char *hr, *h1, *h, *res;
- int l1, l2, rl;
-
- if (s1 == NULL || s2 == NULL) /* NULL string(s) -> NULL result */
- return NULL;
- l1 = strlen(s1);
- l2 = strlen(s2);
- rl = min(l1, l2);
- res = (char *)xalloc(rl + 1);
- *res = '\0';
- if (rl == 0) /* at least one string empty? */
- return res;
- hr = res;
- h1 = s1;
- h = s1;
-
- /* walk through s1 */
- while (h != NULL && *h1 != '\0')
- {
- /* is there something after the current word? */
- if ((h = strchr(h1, ' ')) != NULL)
- *h = '\0';
- l1 = strlen(h1);
-
- /* does the current word appear in s2 ? */
- if (iteminlist(h1, s2, " ") != NULL)
- {
- /* add a blank if not first item */
- if (hr != res)
- *hr++ = ' ';
-
- /* copy the item */
- memcpy(hr, h1, l1);
-
- /* advance pointer in result list */
- hr += l1;
- *hr = '\0';
- }
- if (h != NULL)
- {
- /* there are more items */
- *h = ' ';
- h1 = h + 1;
- }
- }
- return res;
-}
- /*
+/*
** ATTEMPTAUTH -- try to AUTHenticate using one mechanism
**
** Parameters:
** m -- the mailer.
** mci -- the mailer connection structure.
** e -- the envelope (including the sender to specify).
-** mechused - filled in with mechanism used
+** sai - sasl authinfo
**
** Returns:
-** EX_OK/EX_TEMPFAIL
+** EX_OK -- authentication was successful.
+** EX_NOPERM -- authentication failed.
+** EX_IOERR -- authentication dialogue failed (I/O problem?).
+** EX_TEMPFAIL -- temporary failure.
+**
*/
-int
-attemptauth(m, mci, e, mechused)
+static int
+attemptauth(m, mci, e, sai)
MAILER *m;
MCI *mci;
ENVELOPE *e;
- char **mechused;
+ SASL_AI_T *sai;
{
int saslresult, smtpresult;
sasl_external_properties_t ssf;
sasl_interact_t *client_interact = NULL;
char *out;
unsigned int outlen;
- static char *mechusing;
+ char *mechusing;
sasl_security_properties_t ssp;
char in64[MAXOUTLEN];
-# if NETINET
+#if NETINET
extern SOCKADDR CurHostAddr;
-# endif /* NETINET */
+#endif /* NETINET */
+
+ /* no mechanism selected (yet) */
+ (*sai)[SASL_MECH] = NULL;
- *mechused = NULL;
+ /* dispose old connection */
if (mci->mci_conn != NULL)
- {
sasl_dispose(&(mci->mci_conn));
- /* just in case, sasl_dispose() should take care of it */
- mci->mci_conn = NULL;
- }
-
/* make a new client sasl connection */
saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
: "smtp",
CurHostName, NULL, 0, &mci->mci_conn);
+ if (saslresult != SASL_OK)
+ return EX_TEMPFAIL;
/* set properties */
(void) memset(&ssp, '\0', sizeof ssp);
-# if SFIO
+
/* XXX should these be options settable via .cf ? */
- /* ssp.min_ssf = 0; is default due to memset() */
+# if STARTTLS
+#endif /* STARTTLS */
{
- ssp.max_ssf = INT_MAX;
+ ssp.max_ssf = MaxSLBits;
ssp.maxbufsize = MAXOUTLEN;
-# if 0
+# if 0
ssp.security_flags = SASL_SEC_NOPLAINTEXT;
-# endif /* 0 */
+# endif /* 0 */
}
-# endif /* SFIO */
saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
if (saslresult != SASL_OK)
return EX_TEMPFAIL;
@@ -1151,19 +1444,19 @@ attemptauth(m, mci, e, mechused)
/* external security strength factor, authentication id */
ssf.ssf = 0;
ssf.auth_id = NULL;
-# if _FFR_EXT_MECH
- out = macvalue(macid("{cert_subject}", NULL), e);
+#if STARTTLS
+ out = macvalue(macid("{cert_subject}"), e);
if (out != NULL && *out != '\0')
ssf.auth_id = out;
- out = macvalue(macid("{cipher_bits}", NULL), e);
+ out = macvalue(macid("{cipher_bits}"), e);
if (out != NULL && *out != '\0')
ssf.ssf = atoi(out);
-# endif /* _FFR_EXT_MECH */
+#endif /* STARTTLS */
saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
if (saslresult != SASL_OK)
return EX_TEMPFAIL;
-# if NETINET
+#if NETINET
/* set local/remote ipv4 addresses */
if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
{
@@ -1175,7 +1468,8 @@ attemptauth(m, mci, e, mechused)
!= SASL_OK)
return EX_TEMPFAIL;
addrsize = sizeof(struct sockaddr_in);
- if (getsockname(fileno(mci->mci_out),
+ if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
+ NULL),
(struct sockaddr *) &saddr_l, &addrsize) == 0)
{
if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
@@ -1183,28 +1477,26 @@ attemptauth(m, mci, e, mechused)
return EX_TEMPFAIL;
}
}
-# endif /* NETINET */
+#endif /* NETINET */
/* start client side of sasl */
saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
NULL, &client_interact,
&out, &outlen,
(const char **)&mechusing);
- callbacks[CB_AUTHNAME_IDX].context = mechusing;
if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
{
-# if SFIO
if (saslresult == SASL_NOMECH && LogLevel > 8)
{
sm_syslog(LOG_NOTICE, e->e_id,
- "available AUTH mechanisms do not fulfill requirements");
+ "AUTH=client, available mechanisms do not fulfill requirements");
}
-# endif /* SFIO */
return EX_TEMPFAIL;
}
- *mechused = mechusing;
+ /* just point current mechanism to the data in the sasl library */
+ (*sai)[SASL_MECH] = mechusing;
/* send the info across the wire */
if (outlen > 0)
@@ -1223,30 +1515,30 @@ attemptauth(m, mci, e, mechused)
{
smtpmessage("AUTH %s", m, mci, mechusing);
}
+ sm_sasl_free(out); /* XXX only if no rpool is used */
/* get the reply */
- smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, getsasldata, NULL);
- /* which timeout? XXX */
+ smtpresult = reply(m, mci, e, TimeOuts.to_auth, getsasldata, NULL);
for (;;)
{
/* check return code from server */
if (smtpresult == 235)
{
- define(macid("{auth_type}", NULL),
- newstr(mechusing), e);
-# if !SFIO
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, NOQID,
- "SASL: outgoing connection to %.64s: mech=%.16s",
- mci->mci_host, mechusing);
-# endif /* !SFIO */
+ macdefine(&mci->mci_macro, A_TEMP, macid("{auth_type}"),
+ mechusing);
return EX_OK;
}
if (smtpresult == -1)
return EX_IOERR;
- if (smtpresult != 334)
+ if (REPLYTYPE(smtpresult) == 5)
+ return EX_NOPERM; /* ugly, but ... */
+ if (REPLYTYPE(smtpresult) != 3)
+ {
+ /* should we fail deliberately, see RFC 2554 4. ? */
+ /* smtpmessage("*", m, mci); */
return EX_TEMPFAIL;
+ }
saslresult = sasl_client_step(mci->mci_conn,
mci->mci_sasl_string,
@@ -1257,11 +1549,11 @@ attemptauth(m, mci, e, mechused)
if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
{
if (tTd(95, 5))
- dprintf("AUTH FAIL: %s (%d)\n",
+ sm_dprintf("AUTH FAIL=%s (%d)\n",
sasl_errstring(saslresult, NULL, NULL),
saslresult);
- /* fail deliberately, see RFC 2254 4. */
+ /* fail deliberately, see RFC 2554 4. */
smtpmessage("*", m, mci);
/*
@@ -1269,9 +1561,9 @@ attemptauth(m, mci, e, mechused)
** mechanism; how to do that?
*/
- smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
+ smtpresult = reply(m, mci, e, TimeOuts.to_auth,
getsasldata, NULL);
- return EX_TEMPFAIL;
+ return EX_NOPERM;
}
if (outlen > 0)
@@ -1287,15 +1579,14 @@ attemptauth(m, mci, e, mechused)
}
else
in64[0] = '\0';
+ sm_sasl_free(out); /* XXX only if no rpool is used */
smtpmessage("%s", m, mci, in64);
- smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
+ smtpresult = reply(m, mci, e, TimeOuts.to_auth,
getsasldata, NULL);
- /* which timeout? XXX */
}
/* NOTREACHED */
}
-
- /*
+/*
** SMTPAUTH -- try to AUTHenticate
**
** This will try mechanisms in the order the sasl library decided until:
@@ -1309,7 +1600,16 @@ attemptauth(m, mci, e, mechused)
** e -- the envelope.
**
** Returns:
-** EX_OK/EX_TEMPFAIL
+** EX_OK -- authentication was successful
+** EX_UNAVAILABLE -- authentication not possible, e.g.,
+** no data available.
+** EX_NOPERM -- authentication failed.
+** EX_TEMPFAIL -- temporary failure.
+**
+** Notice: AuthInfo is used for all connections, hence we must
+** return EX_TEMPFAIL only if we really want to retry, i.e.,
+** iff getauth() tempfailed or getauth() was used and
+** authentication tempfailed.
*/
int
@@ -1319,59 +1619,94 @@ smtpauth(m, mci, e)
ENVELOPE *e;
{
int result;
- char *mechused;
- char *h;
- static char *defrealm = NULL;
- static char *mechs = NULL;
+ int i;
+ bool usedgetauth;
- mci->mci_sasl_auth = FALSE;
- if (defrealm == NULL)
- {
- h = readauth(SASL_DEFREALM, SASLInfo, TRUE);
- if (h != NULL && *h != '\0')
- defrealm = newstr(h);
- }
- if (defrealm == NULL || *defrealm == '\0')
- defrealm = newstr(macvalue('j', CurEnv));
- callbacks[CB_GETREALM_IDX].context = defrealm;
+ mci->mci_sasl_auth = false;
+ for (i = 0; i < SASL_MECH ; i++)
+ mci->mci_sai[i] = NULL;
-# if _FFR_DEFAUTHINFO_MECHS
- if (mechs == NULL)
+ result = getauth(mci, e, &(mci->mci_sai));
+ if (result == EX_TEMPFAIL)
+ return result;
+ usedgetauth = true;
+
+ /* no data available: don't try to authenticate */
+ if (result == EX_OK && mci->mci_sai[SASL_AUTHID] == NULL)
+ return result;
+ if (result != EX_OK)
{
- h = readauth(SASL_MECH, SASLInfo, TRUE);
- if (h != NULL && *h != '\0')
- mechs = newstr(h);
+ if (SASLInfo == NULL)
+ return EX_UNAVAILABLE;
+
+ /* read authinfo from file */
+ result = readauth(SASLInfo, true, &(mci->mci_sai),
+ mci->mci_rpool);
+ if (result != EX_OK)
+ return result;
+ usedgetauth = false;
}
-# endif /* _FFR_DEFAUTHINFO_MECHS */
- if (mechs == NULL || *mechs == '\0')
- mechs = AuthMechanisms;
- mci->mci_saslcap = intersect(mechs, mci->mci_saslcap);
+
+ /* check whether sufficient data is available */
+ if (mci->mci_sai[SASL_PASSWORD] == NULL ||
+ *(mci->mci_sai)[SASL_PASSWORD] == '\0')
+ return EX_UNAVAILABLE;
+ if ((mci->mci_sai[SASL_AUTHID] == NULL ||
+ *(mci->mci_sai)[SASL_AUTHID] == '\0') &&
+ (mci->mci_sai[SASL_USER] == NULL ||
+ *(mci->mci_sai)[SASL_USER] == '\0'))
+ return EX_UNAVAILABLE;
+
+ /* set the context for the callback function to sai */
+ callbacks[CB_PASS_IDX].context = (void *)&mci->mci_sai;
+ callbacks[CB_USER_IDX].context = (void *)&mci->mci_sai;
+ callbacks[CB_AUTHNAME_IDX].context = (void *)&mci->mci_sai;
+ callbacks[CB_GETREALM_IDX].context = (void *)&mci->mci_sai;
+#if 0
+ callbacks[CB_SAFESASL_IDX].context = (void *)&mci->mci_sai;
+#endif /* 0 */
+
+ /* set default value for realm */
+ if ((mci->mci_sai)[SASL_DEFREALM] == NULL)
+ (mci->mci_sai)[SASL_DEFREALM] = sm_rpool_strdup_x(e->e_rpool,
+ macvalue('j', CurEnv));
+
+ /* set default value for list of mechanism to use */
+ if ((mci->mci_sai)[SASL_MECHLIST] == NULL ||
+ *(mci->mci_sai)[SASL_MECHLIST] == '\0')
+ (mci->mci_sai)[SASL_MECHLIST] = AuthMechanisms;
+
+ /* create list of mechanisms to try */
+ mci->mci_saslcap = intersect((mci->mci_sai)[SASL_MECHLIST],
+ mci->mci_saslcap, mci->mci_rpool);
/* initialize sasl client library */
- result = sasl_client_init(callbacks);
+ result = init_sasl_client();
if (result != SASL_OK)
- return EX_TEMPFAIL;
+ return usedgetauth ? EX_TEMPFAIL : EX_UNAVAILABLE;
do
{
- result = attemptauth(m, mci, e, &mechused);
+ result = attemptauth(m, mci, e, &(mci->mci_sai));
if (result == EX_OK)
- mci->mci_sasl_auth = TRUE;
- else if (result == EX_TEMPFAIL)
+ mci->mci_sasl_auth = true;
+ else if (result == EX_TEMPFAIL || result == EX_NOPERM)
{
- mci->mci_saslcap = removemech(mechused,
- mci->mci_saslcap);
+ mci->mci_saslcap = removemech((mci->mci_sai)[SASL_MECH],
+ mci->mci_saslcap,
+ mci->mci_rpool);
if (mci->mci_saslcap == NULL ||
*(mci->mci_saslcap) == '\0')
- return EX_TEMPFAIL;
+ return usedgetauth ? result
+ : EX_UNAVAILABLE;
}
- else /* all others for now */
- return EX_TEMPFAIL;
+ else
+ return result;
} while (result != EX_OK);
return result;
}
-# endif /* SASL */
+#endif /* SASL */
- /*
+/*
** SMTPMAILFROM -- send MAIL command
**
** Parameters:
@@ -1389,18 +1724,31 @@ smtpmailfrom(m, mci, e)
int r;
char *bufp;
char *bodytype;
+ char *enhsc;
char buf[MAXNAME + 1];
char optbuf[MAXLINE];
- char *enhsc;
if (tTd(18, 2))
- dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
+ sm_dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
enhsc = NULL;
+ /*
+ ** Check if connection is gone, if so
+ ** it's a tempfail and we use mci_errno
+ ** for the reason.
+ */
+
+ if (mci->mci_state == MCIS_CLOSED)
+ {
+ errno = mci->mci_errno;
+ return EX_TEMPFAIL;
+ }
+
/* set up appropriate options to include */
if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
{
- snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize);
+ (void) sm_snprintf(optbuf, sizeof optbuf, " SIZE=%ld",
+ e->e_msgsize);
bufp = &optbuf[strlen(optbuf)];
}
else
@@ -1421,7 +1769,7 @@ smtpmailfrom(m, mci, e)
if (bodytype != NULL &&
SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
{
- snprintf(bufp, SPACELEFT(optbuf, bufp),
+ (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
" BODY=%s", bodytype);
bufp += strlen(bufp);
}
@@ -1433,7 +1781,7 @@ smtpmailfrom(m, mci, e)
/* EMPTY */
/* just pass it through */
}
-# if MIME8TO7
+#if MIME8TO7
else if (bitset(MM_CVTMIME, MimeMode) &&
!bitset(EF_DONT_MIME, e->e_flags) &&
(!bitset(MM_PASS8BIT, MimeMode) ||
@@ -1442,7 +1790,7 @@ smtpmailfrom(m, mci, e)
/* must convert from 8bit MIME format to 7bit encoded */
mci->mci_flags |= MCIF_CVT8TO7;
}
-# endif /* MIME8TO7 */
+#endif /* MIME8TO7 */
else if (!bitset(MM_PASS8BIT, MimeMode))
{
/* cannot just send a 8-bit version */
@@ -1458,7 +1806,7 @@ smtpmailfrom(m, mci, e)
if (e->e_envid != NULL &&
SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
{
- snprintf(bufp, SPACELEFT(optbuf, bufp),
+ (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
" ENVID=%s", e->e_envid);
bufp += strlen(bufp);
}
@@ -1467,7 +1815,7 @@ smtpmailfrom(m, mci, e)
if (bitset(EF_RET_PARAM, e->e_flags) &&
SPACELEFT(optbuf, bufp) > 9)
{
- snprintf(bufp, SPACELEFT(optbuf, bufp),
+ (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
" RET=%s",
bitset(EF_NO_BODY_RETN, e->e_flags) ?
"HDRS" : "FULL");
@@ -1477,22 +1825,50 @@ smtpmailfrom(m, mci, e)
if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL &&
SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7
-# if SASL
+#if SASL
&& (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
-# endif /* SASL */
+#endif /* SASL */
)
{
- snprintf(bufp, SPACELEFT(optbuf, bufp),
+ (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
" AUTH=%s", e->e_auth_param);
bufp += strlen(bufp);
}
/*
+ ** 17 is the max length required, we could use log() to compute
+ ** the exact length (and check IS_DLVR_TRACE())
+ */
+
+ if (bitset(MCIF_DLVR_BY, mci->mci_flags) &&
+ IS_DLVR_BY(e) && SPACELEFT(optbuf, bufp) > 17)
+ {
+ long dby;
+
+ /*
+ ** Avoid problems with delays (for R) since the check
+ ** in deliver() whether min-deliver-time is sufficient.
+ ** Alternatively we could pass the computed time to this
+ ** function.
+ */
+
+ dby = e->e_deliver_by - (curtime() - e->e_ctime);
+ if (dby <= 0 && IS_DLVR_RETURN(e))
+ dby = mci->mci_min_by <= 0 ? 1 : mci->mci_min_by;
+ (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
+ " BY=%ld;%c%s",
+ dby,
+ IS_DLVR_RETURN(e) ? 'R' : 'N',
+ IS_DLVR_TRACE(e) ? "T" : "");
+ bufp += strlen(bufp);
+ }
+
+ /*
** Send the MAIL command.
** Designates the sender.
*/
- mci->mci_state = MCIS_ACTIVE;
+ mci->mci_state = MCIS_MAIL;
if (bitset(EF_RESPONSE, e->e_flags) &&
!bitnset(M_NO_NULL_FROM, m->m_flags))
@@ -1520,13 +1896,12 @@ smtpmailfrom(m, mci, e)
*bufp == '@' ? ',' : ':', bufp, optbuf);
}
SmtpPhase = mci->mci_phase = "client MAIL";
- sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
+ sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
CurHostName, mci->mci_phase);
r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc);
if (r < 0)
{
/* communications failure */
- mci->mci_errno = errno;
mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
smtpquit(m, mci, e);
return EX_TEMPFAIL;
@@ -1594,7 +1969,7 @@ smtpmailfrom(m, mci, e)
smtpquit(m, mci, e);
return EX_PROTOCOL;
}
- /*
+/*
** SMTPRCPT -- designate recipient.
**
** Parameters:
@@ -1611,58 +1986,104 @@ smtpmailfrom(m, mci, e)
*/
int
-smtprcpt(to, m, mci, e)
+smtprcpt(to, m, mci, e, ctladdr, xstart)
ADDRESS *to;
register MAILER *m;
MCI *mci;
ENVELOPE *e;
+ ADDRESS *ctladdr;
+ time_t xstart;
{
- register int r;
char *bufp;
char optbuf[MAXLINE];
- char *enhsc;
- enhsc = NULL;
+#if PIPELINING
+ /*
+ ** If there is status waiting from the other end, read it.
+ ** This should normally happen because of SMTP pipelining.
+ */
+
+ while (mci->mci_nextaddr != NULL &&
+ sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL))
+ {
+ int r;
+
+ r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
+ if (r != EX_OK)
+ {
+ markfailure(e, mci->mci_nextaddr, mci, r, false);
+ giveresponse(r, mci->mci_nextaddr->q_status, m, mci,
+ ctladdr, xstart, e, to);
+ }
+ mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
+ }
+#endif /* PIPELINING */
+
+ /*
+ ** Check if connection is gone, if so
+ ** it's a tempfail and we use mci_errno
+ ** for the reason.
+ */
+
+ if (mci->mci_state == MCIS_CLOSED)
+ {
+ errno = mci->mci_errno;
+ return EX_TEMPFAIL;
+ }
+
optbuf[0] = '\0';
bufp = optbuf;
/*
- ** warning: in the following it is assumed that the free space
+ ** Warning: in the following it is assumed that the free space
** in bufp is sizeof optbuf
*/
+
if (bitset(MCIF_DSN, mci->mci_flags))
{
+ if (IS_DLVR_NOTIFY(e) &&
+ !bitset(MCIF_DLVR_BY, mci->mci_flags))
+ {
+ /* RFC 2852: 4.1.4.2 */
+ if (!bitset(QHASNOTIFY, to->q_flags))
+ to->q_flags |= QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY;
+ else if (bitset(QPINGONSUCCESS, to->q_flags) ||
+ bitset(QPINGONFAILURE, to->q_flags) ||
+ bitset(QPINGONDELAY, to->q_flags))
+ to->q_flags |= QPINGONDELAY;
+ }
+
/* NOTIFY= parameter */
if (bitset(QHASNOTIFY, to->q_flags) &&
bitset(QPRIMARY, to->q_flags) &&
!bitnset(M_LOCALMAILER, m->m_flags))
{
- bool firstone = TRUE;
+ bool firstone = true;
- (void) strlcat(bufp, " NOTIFY=", sizeof optbuf);
+ (void) sm_strlcat(bufp, " NOTIFY=", sizeof optbuf);
if (bitset(QPINGONSUCCESS, to->q_flags))
{
- (void) strlcat(bufp, "SUCCESS", sizeof optbuf);
- firstone = FALSE;
+ (void) sm_strlcat(bufp, "SUCCESS", sizeof optbuf);
+ firstone = false;
}
if (bitset(QPINGONFAILURE, to->q_flags))
{
if (!firstone)
- (void) strlcat(bufp, ",",
+ (void) sm_strlcat(bufp, ",",
sizeof optbuf);
- (void) strlcat(bufp, "FAILURE", sizeof optbuf);
- firstone = FALSE;
+ (void) sm_strlcat(bufp, "FAILURE", sizeof optbuf);
+ firstone = false;
}
if (bitset(QPINGONDELAY, to->q_flags))
{
if (!firstone)
- (void) strlcat(bufp, ",",
+ (void) sm_strlcat(bufp, ",",
sizeof optbuf);
- (void) strlcat(bufp, "DELAY", sizeof optbuf);
- firstone = FALSE;
+ (void) sm_strlcat(bufp, "DELAY", sizeof optbuf);
+ firstone = false;
}
if (firstone)
- (void) strlcat(bufp, "NEVER", sizeof optbuf);
+ (void) sm_strlcat(bufp, "NEVER", sizeof optbuf);
bufp += strlen(bufp);
}
@@ -1670,39 +2091,113 @@ smtprcpt(to, m, mci, e)
if (to->q_orcpt != NULL &&
SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
{
- snprintf(bufp, SPACELEFT(optbuf, bufp),
+ (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
" ORCPT=%s", to->q_orcpt);
bufp += strlen(bufp);
}
}
smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
+ mci->mci_state = MCIS_RCPT;
SmtpPhase = mci->mci_phase = "client RCPT";
- sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
+ sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
CurHostName, mci->mci_phase);
+
+#if PIPELINING
+ /*
+ ** If running SMTP pipelining, we will pick up status later
+ */
+
+ if (bitset(MCIF_PIPELINED, mci->mci_flags))
+ return EX_OK;
+#endif /* PIPELINING */
+
+ return smtprcptstat(to, m, mci, e);
+}
+/*
+** SMTPRCPTSTAT -- get recipient status
+**
+** This is only called during SMTP pipelining
+**
+** Parameters:
+** to -- address of recipient.
+** m -- mailer being sent to.
+** mci -- the mailer connection information.
+** e -- the envelope for this message.
+**
+** Returns:
+** EX_* -- protocol status
+*/
+
+static int
+smtprcptstat(to, m, mci, e)
+ ADDRESS *to;
+ MAILER *m;
+ register MCI *mci;
+ register ENVELOPE *e;
+{
+ int r;
+ int save_errno;
+ char *enhsc;
+
+ /*
+ ** Check if connection is gone, if so
+ ** it's a tempfail and we use mci_errno
+ ** for the reason.
+ */
+
+ if (mci->mci_state == MCIS_CLOSED)
+ {
+ errno = mci->mci_errno;
+ return EX_TEMPFAIL;
+ }
+
+ enhsc = NULL;
r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc);
- to->q_rstatus = newstr(SmtpReplyBuffer);
- to->q_status = ENHSCN(enhsc, smtptodsn(r));
+ save_errno = errno;
+ to->q_rstatus = sm_rpool_strdup_x(e->e_rpool, SmtpReplyBuffer);
+ to->q_status = ENHSCN_RPOOL(enhsc, smtptodsn(r), e->e_rpool);
if (!bitnset(M_LMTP, m->m_flags))
to->q_statmta = mci->mci_host;
if (r < 0 || REPLYTYPE(r) == 4)
+ {
+ mci->mci_retryrcpt = true;
+ errno = save_errno;
return EX_TEMPFAIL;
+ }
else if (REPLYTYPE(r) == 2)
+ {
+ char *t;
+
+ if ((t = mci->mci_tolist) != NULL)
+ {
+ char *p;
+
+ *t++ = ',';
+ for (p = to->q_paddr; *p != '\0'; *t++ = *p++)
+ continue;
+ *t = '\0';
+ mci->mci_tolist = t;
+ }
+#if PIPELINING
+ mci->mci_okrcpts++;
+#endif /* PIPELINING */
return EX_OK;
+ }
else if (r == 550)
{
- to->q_status = ENHSCN(enhsc, "5.1.1");
+ to->q_status = ENHSCN_RPOOL(enhsc, "5.1.1", e->e_rpool);
return EX_NOUSER;
}
else if (r == 551)
{
- to->q_status = ENHSCN(enhsc, "5.1.6");
+ to->q_status = ENHSCN_RPOOL(enhsc, "5.1.6", e->e_rpool);
return EX_NOUSER;
}
else if (r == 553)
{
- to->q_status = ENHSCN(enhsc, "5.1.3");
+ to->q_status = ENHSCN_RPOOL(enhsc, "5.1.3", e->e_rpool);
return EX_NOUSER;
}
else if (REPLYTYPE(r) == 5)
@@ -1722,7 +2217,7 @@ smtprcpt(to, m, mci, e)
SmtpReplyBuffer);
return EX_PROTOCOL;
}
- /*
+/*
** SMTPDATA -- send the data and clean up the transaction.
**
** Parameters:
@@ -1732,19 +2227,18 @@ smtprcpt(to, m, mci, e)
**
** Returns:
** exit status corresponding to DATA command.
-**
-** Side Effects:
-** none.
*/
static jmp_buf CtxDataTimeout;
-static EVENT *volatile DataTimeout = NULL;
+static SM_EVENT *volatile DataTimeout = NULL;
int
-smtpdata(m, mci, e)
+smtpdata(m, mci, e, ctladdr, xstart)
MAILER *m;
register MCI *mci;
register ENVELOPE *e;
+ ADDRESS *ctladdr;
+ time_t xstart;
{
register int r;
int rstat;
@@ -1752,30 +2246,79 @@ smtpdata(m, mci, e)
time_t timeout;
char *enhsc;
+ /*
+ ** Check if connection is gone, if so
+ ** it's a tempfail and we use mci_errno
+ ** for the reason.
+ */
+
+ if (mci->mci_state == MCIS_CLOSED)
+ {
+ errno = mci->mci_errno;
+ return EX_TEMPFAIL;
+ }
+
enhsc = NULL;
/*
** Send the data.
** First send the command and check that it is ok.
- ** Then send the data.
+ ** Then send the data (if there are valid recipients).
** Follow it up with a dot to terminate.
** Finally get the results of the transaction.
*/
/* send the command and check ok to proceed */
smtpmessage("DATA", m, mci);
+
+#if PIPELINING
+ if (mci->mci_nextaddr != NULL)
+ {
+ char *oldto = e->e_to;
+
+ /* pick up any pending RCPT responses for SMTP pipelining */
+ while (mci->mci_nextaddr != NULL)
+ {
+ int r;
+
+ e->e_to = mci->mci_nextaddr->q_paddr;
+ r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
+ if (r != EX_OK)
+ {
+ markfailure(e, mci->mci_nextaddr, mci, r,
+ false);
+ giveresponse(r, mci->mci_nextaddr->q_status, m,
+ mci, ctladdr, xstart, e,
+ mci->mci_nextaddr);
+ if (r == EX_TEMPFAIL)
+ mci->mci_nextaddr->q_state = QS_RETRY;
+ }
+ mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
+ }
+ e->e_to = oldto;
+ }
+#endif /* PIPELINING */
+
+ /* now proceed with DATA phase */
SmtpPhase = mci->mci_phase = "client DATA 354";
- sm_setproctitle(TRUE, e, "%s %s: %s",
+ mci->mci_state = MCIS_DATA;
+ sm_setproctitle(true, e, "%s %s: %s",
qid_printname(e), CurHostName, mci->mci_phase);
r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc);
if (r < 0 || REPLYTYPE(r) == 4)
{
smtpquit(m, mci, e);
+ errno = mci->mci_errno;
return EX_TEMPFAIL;
}
else if (REPLYTYPE(r) == 5)
{
smtprset(m, mci, e);
+#if PIPELINING
+ if (mci->mci_okrcpts <= 0)
+ return mci->mci_retryrcpt ? EX_TEMPFAIL
+ : EX_UNAVAILABLE;
+#endif /* PIPELINING */
return EX_UNAVAILABLE;
}
else if (REPLYTYPE(r) != 3)
@@ -1790,9 +2333,19 @@ smtpdata(m, mci, e)
smtprset(m, mci, e);
mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
SmtpReplyBuffer);
+#if PIPELINING
+ if (mci->mci_okrcpts <= 0)
+ return mci->mci_retryrcpt ? EX_TEMPFAIL
+ : EX_PROTOCOL;
+#endif /* PIPELINING */
return EX_PROTOCOL;
}
+#if PIPELINING
+ if (mci->mci_okrcpts > 0)
+ {
+#endif /* PIPELINING */
+
/*
** Set timeout around data writes. Make it at least large
** enough for DNS timeouts on all recipients plus some fudge
@@ -1828,7 +2381,7 @@ smtpdata(m, mci, e)
else
timeout = DATA_PROGRESS_TIMEOUT;
- DataTimeout = setevent(timeout, datatimeout, 0);
+ DataTimeout = sm_setevent(timeout, datatimeout, 0);
/*
@@ -1850,42 +2403,36 @@ smtpdata(m, mci, e)
*/
if (DataTimeout != NULL)
- clrevent(DataTimeout);
+ sm_clrevent(DataTimeout);
-# if _FFR_CATCH_BROKEN_MTAS
- {
- fd_set readfds;
- struct timeval timeout;
+#if PIPELINING
+ }
+#endif /* PIPELINING */
- FD_ZERO(&readfds);
- FD_SET(fileno(mci->mci_in), &readfds);
- timeout.tv_sec = 0;
- timeout.tv_usec = 0;
- if (select(fileno(mci->mci_in) + 1, FDSET_CAST &readfds,
- NULL, NULL, &timeout) > 0 &&
- FD_ISSET(fileno(mci->mci_in), &readfds))
- {
- /* terminate the message */
- fprintf(mci->mci_out, ".%s", m->m_eol);
- if (TrafficLogFile != NULL)
- fprintf(TrafficLogFile, "%05d >>> .\n",
- (int) getpid());
- if (Verbose)
- nmessage(">>> .");
+#if _FFR_CATCH_BROKEN_MTAS
+ if (sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL))
+ {
+ /* terminate the message */
+ (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s",
+ m->m_eol);
+ if (TrafficLogFile != NULL)
+ (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
+ "%05d >>> .\n", (int) CurrentPid);
+ if (Verbose)
+ nmessage(">>> .");
- sm_syslog(LOG_CRIT, e->e_id,
- "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot",
- CurHostName);
- mci->mci_errno = EIO;
- mci->mci_state = MCIS_ERROR;
- mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
- smtpquit(m, mci, e);
- return EX_PROTOCOL;
- }
+ sm_syslog(LOG_CRIT, e->e_id,
+ "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot",
+ CurHostName);
+ mci->mci_errno = EIO;
+ mci->mci_state = MCIS_ERROR;
+ mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
+ smtpquit(m, mci, e);
+ return EX_PROTOCOL;
}
-# endif /* _FFR_CATCH_BROKEN_MTAS */
+#endif /* _FFR_CATCH_BROKEN_MTAS */
- if (ferror(mci->mci_out))
+ if (sm_io_error(mci->mci_out))
{
/* error during processing -- don't send the dot */
mci->mci_errno = EIO;
@@ -1896,15 +2443,16 @@ smtpdata(m, mci, e)
}
/* terminate the message */
- fprintf(mci->mci_out, ".%s", m->m_eol);
+ (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s", m->m_eol);
if (TrafficLogFile != NULL)
- fprintf(TrafficLogFile, "%05d >>> .\n", (int) getpid());
+ (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
+ "%05d >>> .\n", (int) CurrentPid);
if (Verbose)
nmessage(">>> .");
/* check for the results of the transaction */
SmtpPhase = mci->mci_phase = "client DATA status";
- sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
+ sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
CurHostName, mci->mci_phase);
if (bitnset(M_LMTP, m->m_flags))
return EX_OK;
@@ -1920,26 +2468,24 @@ smtpdata(m, mci, e)
rstat = EX_TEMPFAIL;
else if (REPLYTYPE(r) == 4)
rstat = xstat = EX_TEMPFAIL;
- else if (REPLYCLASS(r) != 5)
- rstat = xstat = EX_PROTOCOL;
else if (REPLYTYPE(r) == 2)
rstat = xstat = EX_OK;
+ else if (REPLYCLASS(r) != 5)
+ rstat = xstat = EX_PROTOCOL;
else if (REPLYTYPE(r) == 5)
rstat = EX_UNAVAILABLE;
else
rstat = EX_PROTOCOL;
mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
SmtpReplyBuffer);
- if (e->e_statmsg != NULL)
- sm_free(e->e_statmsg);
if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
(r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
r += 5;
else
r = 4;
- e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
+ e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[r]);
SmtpPhase = mci->mci_phase = "idle";
- sm_setproctitle(TRUE, e, "%s: %s", CurHostName, mci->mci_phase);
+ sm_setproctitle(true, e, "%s: %s", CurHostName, mci->mci_phase);
if (rstat != EX_PROTOCOL)
return rstat;
if (LogLevel > 1)
@@ -1952,7 +2498,6 @@ smtpdata(m, mci, e)
return rstat;
}
-
static void
datatimeout()
{
@@ -1978,8 +2523,8 @@ datatimeout()
timeout = DATA_PROGRESS_TIMEOUT;
/* reset the timeout */
- DataTimeout = sigsafe_setevent(timeout, datatimeout, 0);
- DataProgress = FALSE;
+ DataTimeout = sm_sigsafe_setevent(timeout, datatimeout, 0);
+ DataProgress = false;
}
else
{
@@ -1993,10 +2538,9 @@ datatimeout()
errno = ETIMEDOUT;
longjmp(CtxDataTimeout, 1);
}
-
errno = save_errno;
}
- /*
+/*
** SMTPGETSTAT -- get status code from DATA in LMTP
**
** Parameters:
@@ -2015,10 +2559,11 @@ smtpgetstat(m, mci, e)
ENVELOPE *e;
{
int r;
- int status;
+ int status, xstat;
char *enhsc;
enhsc = NULL;
+
/* check for the results of the transaction */
r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
if (r < 0)
@@ -2026,25 +2571,24 @@ smtpgetstat(m, mci, e)
smtpquit(m, mci, e);
return EX_TEMPFAIL;
}
+ xstat = EX_NOTSTICKY;
if (REPLYTYPE(r) == 4)
status = EX_TEMPFAIL;
- else if (REPLYCLASS(r) != 5)
- status = EX_PROTOCOL;
else if (REPLYTYPE(r) == 2)
- status = EX_OK;
+ status = xstat = EX_OK;
+ else if (REPLYCLASS(r) != 5)
+ status = xstat = EX_PROTOCOL;
else if (REPLYTYPE(r) == 5)
status = EX_UNAVAILABLE;
else
status = EX_PROTOCOL;
- if (e->e_statmsg != NULL)
- sm_free(e->e_statmsg);
if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
(r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
r += 5;
else
r = 4;
- e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
- mci_setstat(mci, status, ENHSCN(enhsc, smtptodsn(r)),
+ e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[r]);
+ mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
SmtpReplyBuffer);
if (LogLevel > 1 && status == EX_PROTOCOL)
{
@@ -2055,7 +2599,7 @@ smtpgetstat(m, mci, e)
}
return status;
}
- /*
+/*
** SMTPQUIT -- close the SMTP connection.
**
** Parameters:
@@ -2078,11 +2622,17 @@ smtpquit(m, mci, e)
{
bool oldSuprErrs = SuprErrs;
int rcode;
+ char *oldcurhost;
+ oldcurhost = CurHostName;
CurHostName = mci->mci_host; /* XXX UGLY XXX */
if (CurHostName == NULL)
CurHostName = MyHostName;
+#if PIPELINING
+ mci->mci_okrcpts = 0;
+#endif /* PIPELINING */
+
/*
** Suppress errors here -- we may be processing a different
** job when we do the quit connection, and we don't want the
@@ -2090,7 +2640,7 @@ smtpquit(m, mci, e)
** problem.
*/
- SuprErrs = TRUE;
+ SuprErrs = true;
/* send the quit message if we haven't gotten I/O error */
if (mci->mci_state != MCIS_ERROR &&
@@ -2105,7 +2655,7 @@ smtpquit(m, mci, e)
SuprErrs = oldSuprErrs;
if (mci->mci_state == MCIS_CLOSED ||
origstate == MCIS_CLOSED)
- return;
+ goto end;
}
/* now actually close the connection and pick up the zombie */
@@ -2127,8 +2677,12 @@ smtpquit(m, mci, e)
}
SuprErrs = oldSuprErrs;
+
+ end:
+ CurHostName = oldcurhost;
+ return;
}
- /*
+/*
** SMTPRSET -- send a RSET (reset) command
**
** Parameters:
@@ -2155,6 +2709,22 @@ smtprset(m, mci, e)
if (CurHostName == NULL)
CurHostName = MyHostName;
+#if PIPELINING
+ mci->mci_okrcpts = 0;
+#endif /* PIPELINING */
+
+ /*
+ ** Check if connection is gone, if so
+ ** it's a tempfail and we use mci_errno
+ ** for the reason.
+ */
+
+ if (mci->mci_state == MCIS_CLOSED)
+ {
+ errno = mci->mci_errno;
+ return;
+ }
+
SmtpPhase = "client RSET";
smtpmessage("RSET", m, mci);
r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL);
@@ -2177,7 +2747,7 @@ smtprset(m, mci, e)
}
smtpquit(m, mci, e);
}
- /*
+/*
** SMTPPROBE -- check the connection state
**
** Parameters:
@@ -2211,7 +2781,7 @@ smtpprobe(mci)
smtpquit(m, mci, e);
return r;
}
- /*
+/*
** REPLY -- read arpanet reply
**
** Parameters:
@@ -2221,6 +2791,7 @@ smtpprobe(mci)
** timeout -- the timeout for reads.
** pfunc -- processing function called on each line of response.
** If null, no special processing is done.
+** enhstat -- optional, returns enhanced error code string (if set)
**
** Returns:
** reply code it reads.
@@ -2240,16 +2811,25 @@ reply(m, mci, e, timeout, pfunc, enhstat)
{
register char *bufp;
register int r;
- bool firstline = TRUE;
+ bool firstline = true;
char junkbuf[MAXLINE];
static char enhstatcode[ENHSCLEN];
int save_errno;
+ /*
+ ** Flush the output before reading response.
+ **
+ ** For SMTP pipelining, it would be better if we didn't do
+ ** this if there was already data waiting to be read. But
+ ** to do it properly means pushing it to the I/O library,
+ ** since it really needs to be done below the buffer layer.
+ */
+
if (mci->mci_out != NULL)
- (void) fflush(mci->mci_out);
+ (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
if (tTd(18, 1))
- dprintf("reply\n");
+ sm_dprintf("reply\n");
/*
** Read the input line, being careful not to hang.
@@ -2261,18 +2841,28 @@ reply(m, mci, e, timeout, pfunc, enhstat)
register char *p;
/* actually do the read */
- if (e->e_xfp != NULL)
- (void) fflush(e->e_xfp); /* for debugging */
+ if (e->e_xfp != NULL) /* for debugging */
+ (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
/* if we are in the process of closing just give the code */
if (mci->mci_state == MCIS_CLOSED)
return SMTPCLOSING;
+ /* don't try to read from a non-existant fd */
+ if (mci->mci_in == NULL)
+ {
+ if (mci->mci_errno == 0)
+ mci->mci_errno = EBADF;
+ errno = mci->mci_errno;
+ return -1;
+ }
+
if (mci->mci_out != NULL)
- (void) fflush(mci->mci_out);
+ (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
/* get the line from the other side */
p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
+ save_errno = errno;
mci->mci_lastuse = curtime();
if (p == NULL)
@@ -2281,18 +2871,25 @@ reply(m, mci, e, timeout, pfunc, enhstat)
extern char MsgBuf[];
/* if the remote end closed early, fake an error */
+ errno = save_errno;
if (errno == 0)
-# ifdef ECONNRESET
+ {
+ (void) sm_snprintf(SmtpReplyBuffer,
+ sizeof SmtpReplyBuffer,
+ "421 4.4.1 Connection reset by %s",
+ CURHOSTNAME);
+#ifdef ECONNRESET
errno = ECONNRESET;
-# else /* ECONNRESET */
+#else /* ECONNRESET */
errno = EPIPE;
-# endif /* ECONNRESET */
+#endif /* ECONNRESET */
+ }
mci->mci_errno = errno;
oldholderrs = HoldErrs;
- HoldErrs = TRUE;
+ HoldErrs = true;
usrerr("451 4.4.1 reply: read error from %s",
- CurHostName == NULL ? "NO_HOST" : CurHostName);
+ CURHOSTNAME);
/* errors on QUIT should not be persistent */
if (strncmp(SmtpMsgBuffer, "QUIT", 4) != 0)
@@ -2302,35 +2899,31 @@ reply(m, mci, e, timeout, pfunc, enhstat)
if (tTd(18, 100))
(void) pause();
mci->mci_state = MCIS_ERROR;
- save_errno = errno;
smtpquit(m, mci, e);
-# if XDEBUG
+#if XDEBUG
{
char wbuf[MAXLINE];
- int wbufleft = sizeof wbuf;
p = wbuf;
if (e->e_to != NULL)
{
- int plen;
-
- snprintf(p, wbufleft, "%s... ",
- shortenstring(e->e_to, MAXSHORTSTR));
- plen = strlen(p);
- p += plen;
- wbufleft -= plen;
+ (void) sm_snprintf(p,
+ SPACELEFT(wbuf, p),
+ "%s... ",
+ shortenstring(e->e_to, MAXSHORTSTR));
+ p += strlen(p);
}
- snprintf(p, wbufleft, "reply(%.100s) during %s",
- CurHostName == NULL ? "NO_HOST" : CurHostName,
- SmtpPhase);
+ (void) sm_snprintf(p, SPACELEFT(wbuf, p),
+ "reply(%.100s) during %s",
+ CURHOSTNAME, SmtpPhase);
checkfd012(wbuf);
}
-# endif /* XDEBUG */
- errno = save_errno;
+#endif /* XDEBUG */
HoldErrs = oldholderrs;
+ errno = save_errno;
return -1;
}
- fixcrlf(bufp, TRUE);
+ fixcrlf(bufp, true);
/* EHLO failure is not a real error */
if (e->e_xfp != NULL && (bufp[0] == '4' ||
@@ -2340,17 +2933,20 @@ reply(m, mci, e, timeout, pfunc, enhstat)
if (SmtpNeedIntro)
{
/* inform user who we are chatting with */
- fprintf(CurEnv->e_xfp,
- "... while talking to %s:\n",
- CurHostName == NULL ? "NO_HOST" : CurHostName);
- SmtpNeedIntro = FALSE;
+ (void) sm_io_fprintf(CurEnv->e_xfp,
+ SM_TIME_DEFAULT,
+ "... while talking to %s:\n",
+ CURHOSTNAME);
+ SmtpNeedIntro = false;
}
if (SmtpMsgBuffer[0] != '\0')
- fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ ">>> %s\n", SmtpMsgBuffer);
SmtpMsgBuffer[0] = '\0';
/* now log the message as from the other side */
- fprintf(e->e_xfp, "<<< %s\n", bufp);
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "<<< %s\n", bufp);
}
/* display the input for verbose mode */
@@ -2370,7 +2966,7 @@ reply(m, mci, e, timeout, pfunc, enhstat)
if (pfunc != NULL)
(*pfunc)(bufp, firstline, m, mci, e);
- firstline = FALSE;
+ firstline = false;
/* decode the reply code */
r = atoi(bufp);
@@ -2393,9 +2989,8 @@ reply(m, mci, e, timeout, pfunc, enhstat)
*/
/* save temporary failure messages for posterity */
- if (SmtpReplyBuffer[0] == '4' &&
- (bitnset(M_LMTP, m->m_flags) || SmtpError[0] == '\0'))
- snprintf(SmtpError, sizeof SmtpError, "%s", SmtpReplyBuffer);
+ if (SmtpReplyBuffer[0] == '4')
+ (void) sm_strlcpy(SmtpError, SmtpReplyBuffer, sizeof SmtpError);
/* reply code 421 is "Service Shutting Down" */
if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
@@ -2407,7 +3002,7 @@ reply(m, mci, e, timeout, pfunc, enhstat)
return r;
}
- /*
+/*
** SMTPMESSAGE -- send message to server
**
** Parameters:
@@ -2424,36 +3019,36 @@ reply(m, mci, e, timeout, pfunc, enhstat)
/*VARARGS1*/
void
-# ifdef __STDC__
+#ifdef __STDC__
smtpmessage(char *f, MAILER *m, MCI *mci, ...)
-# else /* __STDC__ */
+#else /* __STDC__ */
smtpmessage(f, m, mci, va_alist)
char *f;
MAILER *m;
MCI *mci;
va_dcl
-# endif /* __STDC__ */
+#endif /* __STDC__ */
{
- VA_LOCAL_DECL
+ SM_VA_LOCAL_DECL
- VA_START(mci);
- (void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
- VA_END;
+ SM_VA_START(ap, mci);
+ (void) sm_vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
+ SM_VA_END(ap);
if (tTd(18, 1) || Verbose)
nmessage(">>> %s", SmtpMsgBuffer);
if (TrafficLogFile != NULL)
- fprintf(TrafficLogFile, "%05d >>> %s\n",
- (int) getpid(), SmtpMsgBuffer);
+ (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
+ "%05d >>> %s\n", (int) CurrentPid,
+ SmtpMsgBuffer);
if (mci->mci_out != NULL)
{
- fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
- m == NULL ? "\r\n" : m->m_eol);
+ (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s%s",
+ SmtpMsgBuffer, m == NULL ? "\r\n"
+ : m->m_eol);
}
else if (tTd(18, 1))
{
- dprintf("smtpmessage: NULL mci_out\n");
+ sm_dprintf("smtpmessage: NULL mci_out\n");
}
}
-
-#endif /* SMTP */
diff --git a/contrib/sendmail/src/util.c b/contrib/sendmail/src/util.c
index 2017211..c11d085 100644
--- a/contrib/sendmail/src/util.c
+++ b/contrib/sendmail/src/util.c
@@ -11,73 +11,30 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: util.c,v 8.225.2.1.2.26 2001/06/01 08:23:25 gshapiro Exp $";
-#endif /* ! lint */
-
#include <sendmail.h>
-#include <sysexits.h>
-
-
-static void readtimeout __P((time_t));
- /*
-** STRIPQUOTES -- Strip quotes & quote bits from a string.
-**
-** Runs through a string and strips off unquoted quote
-** characters and quote bits. This is done in place.
-**
-** Parameters:
-** s -- the string to strip.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** none.
-*/
+SM_RCSID("@(#)$Id: util.c,v 8.357 2001/11/28 19:19:27 gshapiro Exp $")
-void
-stripquotes(s)
- char *s;
-{
- register char *p;
- register char *q;
- register char c;
-
- if (s == NULL)
- return;
+#include <sysexits.h>
+#include <sm/xtrap.h>
- p = q = s;
- do
- {
- c = *p++;
- if (c == '\\')
- c = *p++;
- else if (c == '"')
- continue;
- *q++ = c;
- } while (c != '\0');
-}
- /*
+/*
** ADDQUOTES -- Adds quotes & quote bits to a string.
**
-** Runs through a string and adds characters and quote bits.
+** Runs through a string and adds backslashes and quote bits.
**
** Parameters:
** s -- the string to modify.
+** rpool -- resource pool from which to allocate result
**
** Returns:
** pointer to quoted string.
-**
-** Side Effects:
-** none.
-**
*/
char *
-addquotes(s)
+addquotes(s, rpool)
char *s;
+ SM_RPOOL_T *rpool;
{
int len = 0;
char c;
@@ -94,7 +51,7 @@ addquotes(s)
len++;
}
- q = r = xalloc(len + 3);
+ q = r = sm_rpool_malloc_x(rpool, len + 3);
p = s;
/* add leading quote */
@@ -110,7 +67,7 @@ addquotes(s)
*q = '\0';
return r;
}
- /*
+/*
** RFC822_STRING -- Checks string for proper RFC822 string quoting.
**
** Runs through a string and verifies RFC822 special characters
@@ -121,24 +78,19 @@ addquotes(s)
** s -- the string to modify.
**
** Returns:
-** TRUE -- if the string is RFC822 compliant.
-** FALSE -- if the string is not RFC822 compliant.
-**
-** Side Effects:
-** none.
-**
+** true iff the string is RFC822 compliant, false otherwise.
*/
bool
rfc822_string(s)
char *s;
{
- bool quoted = FALSE;
+ bool quoted = false;
int commentlev = 0;
char *c = s;
if (s == NULL)
- return FALSE;
+ return false;
while (*c != '\0')
{
@@ -147,7 +99,7 @@ rfc822_string(s)
{
c++;
if (*c == '\0')
- return FALSE;
+ return false;
}
else if (commentlev == 0 && *c == '"')
quoted = !quoted;
@@ -157,7 +109,7 @@ rfc822_string(s)
{
/* unbalanced ')' */
if (commentlev == 0)
- return FALSE;
+ return false;
else
commentlev--;
}
@@ -165,17 +117,15 @@ rfc822_string(s)
commentlev++;
else if (commentlev == 0 &&
strchr(MustQuoteChars, *c) != NULL)
- return FALSE;
+ return false;
}
c++;
}
+
/* unbalanced '"' or '(' */
- if (quoted || commentlev != 0)
- return FALSE;
- else
- return TRUE;
+ return !quoted && commentlev == 0;
}
- /*
+/*
** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
**
** Arbitrarily shorten (in place) an RFC822 string and rebalance
@@ -186,7 +136,7 @@ rfc822_string(s)
** length -- the maximum size, 0 if no maximum
**
** Returns:
-** TRUE if string is changed, FALSE otherwise
+** true if string is changed, false otherwise
**
** Side Effects:
** Changes string in place, possibly resulting
@@ -198,9 +148,9 @@ shorten_rfc822_string(string, length)
char *string;
size_t length;
{
- bool backslash = FALSE;
- bool modified = FALSE;
- bool quoted = FALSE;
+ bool backslash = false;
+ bool modified = false;
+ bool quoted = false;
size_t slen;
int parencount = 0;
char *ptr = string;
@@ -218,12 +168,12 @@ shorten_rfc822_string(string, length)
{
if (backslash)
{
- backslash = FALSE;
+ backslash = false;
goto increment;
}
if (*ptr == '\\')
- backslash = TRUE;
+ backslash = true;
else if (*ptr == '(')
{
if (!quoted)
@@ -247,11 +197,11 @@ increment:
{
/* Not enough, backtrack */
if (*ptr == '\\')
- backslash = FALSE;
+ backslash = false;
else if (*ptr == '(' && !quoted)
parencount--;
else if (*ptr == '"' && parencount == 0)
- quoted = FALSE;
+ quoted = false;
break;
}
ptr++;
@@ -262,7 +212,7 @@ increment:
{
if (*ptr != ')')
{
- modified = TRUE;
+ modified = true;
*ptr = ')';
}
ptr++;
@@ -271,19 +221,19 @@ increment:
{
if (*ptr != '"')
{
- modified = TRUE;
+ modified = true;
*ptr = '"';
}
ptr++;
}
if (*ptr != '\0')
{
- modified = TRUE;
+ modified = true;
*ptr = '\0';
}
return modified;
}
- /*
+/*
** FIND_CHARACTER -- find an unquoted character in an RFC822 string
**
** Find an unquoted, non-commented character in an RFC822
@@ -304,15 +254,15 @@ find_character(string, character)
char *string;
int character;
{
- bool backslash = FALSE;
- bool quoted = FALSE;
+ bool backslash = false;
+ bool quoted = false;
int parencount = 0;
while (string != NULL && *string != '\0')
{
if (backslash)
{
- backslash = FALSE;
+ backslash = false;
if (!quoted && character == '\\' && *string == '\\')
break;
string++;
@@ -321,7 +271,7 @@ find_character(string, character)
switch (*string)
{
case '\\':
- backslash = TRUE;
+ backslash = true;
break;
case '(':
@@ -352,170 +302,142 @@ find_character(string, character)
/* Return pointer to the character */
return string;
}
- /*
-** XALLOC -- Allocate memory and bitch wildly on failure.
-**
-** THIS IS A CLUDGE. This should be made to give a proper
-** error -- but after all, what can we do?
+
+/*
+** CHECK_BODYTYPE -- check bodytype parameter
**
** Parameters:
-** sz -- size of area to allocate.
+** bodytype -- bodytype parameter
**
** Returns:
-** pointer to data region.
+** BODYTYPE_* according to parameter
**
-** Side Effects:
-** Memory is allocated.
*/
-char *
-xalloc(sz)
- register int sz;
+int
+check_bodytype(bodytype)
+ char *bodytype;
{
- register char *p;
-
- /* some systems can't handle size zero mallocs */
- if (sz <= 0)
- sz = 1;
-
- ENTER_CRITICAL();
- p = malloc((unsigned) sz);
- LEAVE_CRITICAL();
- if (p == NULL)
- {
- syserr("!Out of memory!!");
-
- /* NOTREACHED */
- exit(EX_UNAVAILABLE);
- }
- return p;
+ /* check body type for legality */
+ if (bodytype == NULL)
+ return BODYTYPE_NONE;
+ if (sm_strcasecmp(bodytype, "7BIT") == 0)
+ return BODYTYPE_7BIT;
+ if (sm_strcasecmp(bodytype, "8BITMIME") == 0)
+ return BODYTYPE_8BITMIME;
+ return BODYTYPE_ILLEGAL;
}
- /*
-** 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?
+
+#if _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI
+/*
+** TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..."
**
** Parameters:
-** ptr -- original area.
-** sz -- size of new area to allocate.
+** str -- string to truncate
+** len -- maximum length (including '\0') (0 for unlimited)
+** delim -- delimiter character
**
** Returns:
-** pointer to data region.
-**
-** Side Effects:
-** Memory is allocated.
+** None.
*/
-char *
-xrealloc(ptr, sz)
- void *ptr;
- size_t sz;
+void
+truncate_at_delim(str, len, delim)
+ char *str;
+ size_t len;
+ int delim;
{
- register char *p;
+ char *p;
- /* some systems can't handle size zero mallocs */
- if (sz <= 0)
- sz = 1;
+ if (str == NULL || len == 0 || strlen(str) < len)
+ return;
- ENTER_CRITICAL();
- p = realloc(ptr, (unsigned) sz);
- LEAVE_CRITICAL();
- if (p == NULL)
+ *(str + len - 1) = '\0';
+ while ((p = strrchr(str, delim)) != NULL)
{
- syserr("!Out of memory!!");
-
- /* NOTREACHED */
- exit(EX_UNAVAILABLE);
+ *p = '\0';
+ if (p - str + 4 < len)
+ {
+ *p++ = ':';
+ *p = '\0';
+ (void) sm_strlcat(str, "...", len);
+ return;
+ }
}
- return p;
+
+ /* Couldn't find a place to append "..." */
+ if (len > 3)
+ (void) sm_strlcpy(str, "...", len);
+ else
+ str[0] = '\0';
}
- /*
-** 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?
+#endif /* _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI */
+/*
+** XALLOC -- Allocate memory, raise an exception on error
**
** Parameters:
-** num -- number of items to allocate
-** sz -- size of new area to allocate.
+** sz -- size of area to allocate.
**
** Returns:
** pointer to data region.
**
+** Exceptions:
+** SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory
+**
** Side Effects:
** Memory is allocated.
*/
char *
-xcalloc(num, sz)
- size_t num;
- size_t sz;
+#if SM_HEAP_CHECK
+xalloc_tagged(sz, file, line)
+ register int sz;
+ char *file;
+ int line;
+#else /* SM_HEAP_CHECK */
+xalloc(sz)
+ register int sz;
+#endif /* SM_HEAP_CHECK */
{
register char *p;
/* some systems can't handle size zero mallocs */
- if (num <= 0)
- num = 1;
if (sz <= 0)
sz = 1;
- ENTER_CRITICAL();
- p = calloc((unsigned) num, (unsigned) sz);
- LEAVE_CRITICAL();
+ /* scaffolding for testing error handling code */
+ sm_xtrap_raise_x(&SmHeapOutOfMemory);
+
+ p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group());
if (p == NULL)
{
- syserr("!Out of memory!!");
-
- /* NOTREACHED */
- exit(EX_UNAVAILABLE);
+ sm_exc_raise_x(&SmHeapOutOfMemory);
}
return p;
}
- /*
-** SM_FREE -- Free memory safely.
-**
-** Parameters:
-** ptr -- area to free
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Memory is freed.
-*/
-
-void
-sm_free(ptr)
- void *ptr;
-{
- ENTER_CRITICAL();
- free(ptr);
- LEAVE_CRITICAL();
-}
- /*
+/*
** COPYPLIST -- copy list of pointers.
**
-** This routine is the equivalent of newstr for lists of
+** This routine is the equivalent of strdup for lists of
** pointers.
**
** Parameters:
** list -- list of pointers to copy.
** Must be NULL terminated.
-** copycont -- if TRUE, copy the contents of the vector
+** copycont -- if true, copy the contents of the vector
** (which must be a string) also.
+** rpool -- resource pool from which to allocate storage,
+** or NULL
**
** Returns:
** a copy of 'list'.
-**
-** Side Effects:
-** none.
*/
char **
-copyplist(list, copycont)
+copyplist(list, copycont, rpool)
char **list;
bool copycont;
+ SM_RPOOL_T *rpool;
{
register char **vp;
register char **newvp;
@@ -525,36 +447,35 @@ copyplist(list, copycont)
vp++;
- newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
+ newvp = (char **) sm_rpool_malloc_x(rpool, (vp - list) * sizeof *vp);
memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof *vp);
if (copycont)
{
for (vp = newvp; *vp != NULL; vp++)
- *vp = newstr(*vp);
+ *vp = sm_rpool_strdup_x(rpool, *vp);
}
return newvp;
}
- /*
+/*
** COPYQUEUE -- copy address queue.
**
-** This routine is the equivalent of newstr for address queues
+** This routine is the equivalent of strdup for address queues;
** addresses marked as QS_IS_DEAD() aren't copied
**
** Parameters:
** addr -- list of address structures to copy.
+** rpool -- resource pool from which to allocate storage
**
** Returns:
** a copy of 'addr'.
-**
-** Side Effects:
-** none.
*/
ADDRESS *
-copyqueue(addr)
+copyqueue(addr, rpool)
ADDRESS *addr;
+ SM_RPOOL_T *rpool;
{
register ADDRESS *newaddr;
ADDRESS *ret;
@@ -564,7 +485,8 @@ copyqueue(addr)
{
if (!QS_IS_DEAD(addr->q_state))
{
- newaddr = (ADDRESS *) xalloc(sizeof *newaddr);
+ newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool,
+ sizeof *newaddr);
STRUCTCOPY(*addr, *newaddr);
*tail = newaddr;
tail = &newaddr->q_next;
@@ -575,7 +497,7 @@ copyqueue(addr)
return ret;
}
- /*
+/*
** LOG_SENDMAIL_PID -- record sendmail pid and command line.
**
** Parameters:
@@ -585,7 +507,7 @@ copyqueue(addr)
** none.
**
** Side Effects:
-** writes pidfile.
+** writes pidfile, logs command line.
*/
void
@@ -593,8 +515,9 @@ log_sendmail_pid(e)
ENVELOPE *e;
{
long sff;
- FILE *pidf;
+ SM_FILE_T *pidf;
char pidpath[MAXPATHLEN + 1];
+ extern char *CommandLineArgs;
/* write the pid to the log file for posterity */
sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
@@ -605,26 +528,29 @@ log_sendmail_pid(e)
if (pidf == NULL)
{
sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s",
- pidpath, errstring(errno));
+ pidpath, sm_errstring(errno));
}
else
{
pid_t pid;
- extern char *CommandLineArgs;
pid = getpid();
/* write the process id on line 1 */
- fprintf(pidf, "%ld\n", (long) pid);
+ (void) sm_io_fprintf(pidf, SM_TIME_DEFAULT, "%ld\n",
+ (long) pid);
/* line 2 contains all command line flags */
- fprintf(pidf, "%s\n", CommandLineArgs);
+ (void) sm_io_fprintf(pidf, SM_TIME_DEFAULT, "%s\n",
+ CommandLineArgs);
/* flush and close */
- (void) fclose(pidf);
+ (void) sm_io_close(pidf, SM_TIME_DEFAULT);
}
+ if (LogLevel > 9)
+ sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs);
}
- /*
+/*
** SET_DELIVERY_MODE -- set and record the delivery mode
**
** Parameters:
@@ -635,7 +561,7 @@ log_sendmail_pid(e)
** none.
**
** Side Effects:
-** sets $&{deliveryMode} macro
+** sets {deliveryMode} macro
*/
void
@@ -645,12 +571,38 @@ set_delivery_mode(mode, e)
{
char buf[2];
- e->e_sendmode = (char)mode;
- buf[0] = (char)mode;
+ e->e_sendmode = (char) mode;
+ buf[0] = (char) mode;
+ buf[1] = '\0';
+ macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf);
+}
+/*
+** SET_OP_MODE -- set and record the op mode
+**
+** Parameters:
+** mode -- op mode
+** e -- the current envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** sets {opMode} macro
+*/
+
+void
+set_op_mode(mode)
+ int mode;
+{
+ char buf[2];
+ extern ENVELOPE BlankEnvelope;
+
+ OpMode = (char) mode;
+ buf[0] = (char) mode;
buf[1] = '\0';
- define(macid("{deliveryMode}", NULL), newstr(buf), e);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf);
}
- /*
+/*
** PRINTAV -- print argument vector.
**
** Parameters:
@@ -670,33 +622,14 @@ printav(av)
while (*av != NULL)
{
if (tTd(0, 44))
- dprintf("\n\t%08lx=", (u_long) *av);
+ sm_dprintf("\n\t%08lx=", (unsigned long) *av);
else
- (void) putchar(' ');
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT, ' ');
xputs(*av++);
}
- (void) putchar('\n');
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT, '\n');
}
- /*
-** LOWER -- turn letter into lower case.
-**
-** Parameters:
-** c -- character to turn into lower case.
-**
-** Returns:
-** c, in lower case.
-**
-** Side Effects:
-** none.
-*/
-
-char
-lower(c)
- register int c;
-{
- return ((isascii(c) && isupper(c)) ? tolower(c) : c);
-}
- /*
+/*
** XPUTS -- put string doing control escapes.
**
** Parameters:
@@ -715,27 +648,53 @@ xputs(s)
{
register int c;
register struct metamac *mp;
- bool shiftout = FALSE;
+ bool shiftout = false;
extern struct metamac MetaMacros[];
+ static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI",
+ "@(#)$Debug: ANSI - enable reverse video in debug output $");
+
+ /*
+ ** TermEscape is set here, rather than in main(),
+ ** because ANSI mode can be turned on or off at any time
+ ** if we are in -bt rule testing mode.
+ */
+
+ if (sm_debug_unknown(&DebugANSI))
+ {
+ if (sm_debug_active(&DebugANSI, 1))
+ {
+ TermEscape.te_rv_on = "\033[7m";
+ TermEscape.te_rv_off = "\033[0m";
+ }
+ else
+ {
+ TermEscape.te_rv_on = "";
+ TermEscape.te_rv_off = "";
+ }
+ }
if (s == NULL)
{
- printf("%s<null>%s", TermEscape.te_rv_on, TermEscape.te_rv_off);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s<null>%s",
+ TermEscape.te_rv_on, TermEscape.te_rv_off);
return;
}
while ((c = (*s++ & 0377)) != '\0')
{
if (shiftout)
{
- printf("%s", TermEscape.te_rv_off);
- shiftout = FALSE;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s",
+ TermEscape.te_rv_off);
+ shiftout = false;
}
if (!isascii(c))
{
if (c == MATCHREPL)
{
- printf("%s$", TermEscape.te_rv_on);
- shiftout = TRUE;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "%s$",
+ TermEscape.te_rv_on);
+ shiftout = true;
if (*s == '\0')
continue;
c = *s++ & 0377;
@@ -743,50 +702,70 @@ xputs(s)
}
if (c == MACROEXPAND || c == MACRODEXPAND)
{
- printf("%s$", TermEscape.te_rv_on);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "%s$",
+ TermEscape.te_rv_on);
if (c == MACRODEXPAND)
- (void) putchar('&');
- shiftout = TRUE;
+ (void) sm_io_putc(smioout,
+ SM_TIME_DEFAULT, '&');
+ shiftout = true;
if (*s == '\0')
continue;
if (strchr("=~&?", *s) != NULL)
- (void) putchar(*s++);
+ (void) sm_io_putc(smioout,
+ SM_TIME_DEFAULT,
+ *s++);
if (bitset(0200, *s))
- printf("{%s}", macname(bitidx(*s++)));
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "{%s}",
+ macname(bitidx(*s++)));
else
- printf("%c", *s++);
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "%c",
+ *s++);
continue;
}
for (mp = MetaMacros; mp->metaname != '\0'; mp++)
{
- if ((mp->metaval & 0377) == c)
+ if (bitidx(mp->metaval) == c)
{
- printf("%s$%c",
- TermEscape.te_rv_on,
- mp->metaname);
- shiftout = TRUE;
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "%s$%c",
+ TermEscape.te_rv_on,
+ mp->metaname);
+ shiftout = true;
break;
}
}
if (c == MATCHCLASS || c == MATCHNCLASS)
{
if (bitset(0200, *s))
- printf("{%s}", macname(*s++ & 0377));
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "{%s}",
+ macname(bitidx(*s++)));
else if (*s != '\0')
- printf("%c", *s++);
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "%c",
+ *s++);
}
if (mp->metaname != '\0')
continue;
/* unrecognized meta character */
- printf("%sM-", TermEscape.te_rv_on);
- shiftout = TRUE;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%sM-",
+ TermEscape.te_rv_on);
+ shiftout = true;
c &= 0177;
}
printchar:
if (isprint(c))
{
- (void) putchar(c);
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
continue;
}
@@ -807,25 +786,27 @@ xputs(s)
}
if (!shiftout)
{
- printf("%s", TermEscape.te_rv_on);
- shiftout = TRUE;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s",
+ TermEscape.te_rv_on);
+ shiftout = true;
}
if (isprint(c))
{
- (void) putchar('\\');
- (void) putchar(c);
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT, '\\');
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
}
else
{
- (void) putchar('^');
- (void) putchar(c ^ 0100);
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT, '^');
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c ^ 0100);
}
}
if (shiftout)
- printf("%s", TermEscape.te_rv_off);
- (void) fflush(stdout);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s",
+ TermEscape.te_rv_off);
+ (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
}
- /*
+/*
** MAKELOWER -- Translate a line into lower case
**
** Parameters:
@@ -851,60 +832,7 @@ makelower(p)
if (isascii(c) && isupper(c))
*p = tolower(c);
}
- /*
-** BUILDFNAME -- build full name from gecos style entry.
-**
-** This routine interprets the strange entry that would appear
-** in the GECOS field of the password file.
-**
-** Parameters:
-** p -- name to build.
-** user -- the login name of this user (for &).
-** buf -- place to put the result.
-** buflen -- length of buf.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** none.
-*/
-
-void
-buildfname(gecos, user, buf, buflen)
- register char *gecos;
- char *user;
- char *buf;
- int buflen;
-{
- register char *p;
- register char *bp = buf;
-
- if (*gecos == '*')
- gecos++;
-
- /* copy gecos, interpolating & to be full name */
- for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
- {
- if (bp >= &buf[buflen - 1])
- {
- /* buffer overflow -- just use login name */
- snprintf(buf, buflen, "%s", user);
- return;
- }
- if (*p == '&')
- {
- /* interpolate full name */
- snprintf(bp, buflen - (bp - buf), "%s", user);
- *bp = toupper(*bp);
- bp += strlen(bp);
- }
- else
- *bp++ = *p;
- }
- *bp = '\0';
-}
- /*
+/*
** FIXCRLF -- fix <CR><LF> in line.
**
** Looks for the <CR><LF> combination and turns it into the
@@ -939,7 +867,7 @@ fixcrlf(line, stripnl)
*p++ = '\n';
*p = '\0';
}
- /*
+/*
** PUTLINE -- put a line like fputs obeying SMTP conventions
**
** This routine always guarantees outputing a newline (or CRLF,
@@ -953,7 +881,7 @@ fixcrlf(line, stripnl)
** none
**
** Side Effects:
-** output of l to fp.
+** output of l to mci->mci_out.
*/
void
@@ -963,7 +891,7 @@ putline(l, mci)
{
putxline(l, strlen(l), mci, PXLF_MAPFROM);
}
- /*
+/*
** PUTXLINE -- putline with flags bits.
**
** This routine always guarantees outputing a newline (or CRLF,
@@ -982,7 +910,7 @@ putline(l, mci)
** none
**
** Side Effects:
-** output of l to fp.
+** output of l to mci->mci_out.
*/
void
@@ -992,7 +920,7 @@ putxline(l, len, mci, pxflags)
register MCI *mci;
int pxflags;
{
- bool dead = FALSE;
+ bool dead = false;
register char *p, *end;
int slop = 0;
@@ -1016,7 +944,8 @@ putxline(l, len, mci, pxflags)
p = end;
if (TrafficLogFile != NULL)
- fprintf(TrafficLogFile, "%05d >>> ", (int) getpid());
+ (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
+ "%05d >>> ", (int) CurrentPid);
/* check for line overflow */
while (mci->mci_mailer->m_linelimit > 0 &&
@@ -1028,71 +957,82 @@ putxline(l, len, mci, pxflags)
if (l[0] == '.' && slop == 0 &&
bitnset(M_XDOT, mci->mci_mailer->m_flags))
{
- if (putc('.', mci->mci_out) == EOF)
- dead = TRUE;
+ if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
+ '.') == SM_IO_EOF)
+ dead = true;
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
if (TrafficLogFile != NULL)
- (void) putc('.', TrafficLogFile);
+ (void) sm_io_putc(TrafficLogFile,
+ SM_TIME_DEFAULT, '.');
}
else if (l[0] == 'F' && slop == 0 &&
bitset(PXLF_MAPFROM, pxflags) &&
strncmp(l, "From ", 5) == 0 &&
bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
{
- if (putc('>', mci->mci_out) == EOF)
- dead = TRUE;
+ if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
+ '>') == SM_IO_EOF)
+ dead = true;
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
if (TrafficLogFile != NULL)
- (void) putc('>', TrafficLogFile);
+ (void) sm_io_putc(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ '>');
}
if (dead)
break;
while (l < q)
{
- if (putc((unsigned char) *l++, mci->mci_out) ==
- EOF)
+ if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
+ (unsigned char) *l++) == SM_IO_EOF)
{
- dead = TRUE;
+ dead = true;
break;
}
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
}
if (dead)
break;
- if (putc('!', mci->mci_out) == EOF ||
- fputs(mci->mci_mailer->m_eol,
- mci->mci_out) == EOF ||
- putc(' ', mci->mci_out) == EOF)
+ if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '!') ==
+ SM_IO_EOF ||
+ sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
+ mci->mci_mailer->m_eol) ==
+ SM_IO_EOF ||
+ sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, ' ') ==
+ SM_IO_EOF)
{
- dead = TRUE;
+ dead = true;
break;
}
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
if (TrafficLogFile != NULL)
{
for (l = l_base; l < q; l++)
- (void) putc((unsigned char)*l,
- TrafficLogFile);
- fprintf(TrafficLogFile, "!\n%05d >>> ",
- (int) getpid());
+ (void) sm_io_putc(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ (unsigned char)*l);
+ (void) sm_io_fprintf(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ "!\n%05d >>> ",
+ (int) CurrentPid);
}
slop = 1;
}
@@ -1104,107 +1044,125 @@ putxline(l, len, mci, pxflags)
if (l[0] == '.' && slop == 0 &&
bitnset(M_XDOT, mci->mci_mailer->m_flags))
{
- if (putc('.', mci->mci_out) == EOF)
+ if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') ==
+ SM_IO_EOF)
break;
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
if (TrafficLogFile != NULL)
- (void) putc('.', TrafficLogFile);
+ (void) sm_io_putc(TrafficLogFile,
+ SM_TIME_DEFAULT, '.');
}
else if (l[0] == 'F' && slop == 0 &&
bitset(PXLF_MAPFROM, pxflags) &&
strncmp(l, "From ", 5) == 0 &&
bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
{
- if (putc('>', mci->mci_out) == EOF)
+ if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') ==
+ SM_IO_EOF)
break;
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
if (TrafficLogFile != NULL)
- (void) putc('>', TrafficLogFile);
+ (void) sm_io_putc(TrafficLogFile,
+ SM_TIME_DEFAULT, '>');
}
for ( ; l < p; ++l)
{
if (TrafficLogFile != NULL)
- (void) putc((unsigned char)*l, TrafficLogFile);
- if (putc((unsigned char) *l, mci->mci_out) == EOF)
+ (void) sm_io_putc(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ (unsigned char)*l);
+ if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
+ (unsigned char) *l) == SM_IO_EOF)
{
- dead = TRUE;
+ dead = true;
break;
}
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
}
if (dead)
break;
if (TrafficLogFile != NULL)
- (void) putc('\n', TrafficLogFile);
- if (fputs(mci->mci_mailer->m_eol, mci->mci_out) == EOF)
+ (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT,
+ '\n');
+ if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
+ mci->mci_mailer->m_eol) == SM_IO_EOF)
break;
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
if (l < end && *l == '\n')
{
if (*++l != ' ' && *l != '\t' && *l != '\0' &&
bitset(PXLF_HEADER, pxflags))
{
- if (putc(' ', mci->mci_out) == EOF)
+ if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
+ ' ') == SM_IO_EOF)
break;
else
{
/* record progress for DATA timeout */
- DataProgress = TRUE;
+ DataProgress = true;
}
+
if (TrafficLogFile != NULL)
- (void) putc(' ', TrafficLogFile);
+ (void) sm_io_putc(TrafficLogFile,
+ SM_TIME_DEFAULT, ' ');
}
}
+
+ /* record progress for DATA timeout */
+ DataProgress = true;
} while (l < end);
}
- /*
+/*
** XUNLINK -- unlink a file, doing logging as appropriate.
**
** Parameters:
** f -- name of file to unlink.
**
** Returns:
-** none.
+** return value of unlink()
**
** Side Effects:
** f is unlinked.
*/
-void
+int
xunlink(f)
char *f;
{
register int i;
+ int save_errno;
if (LogLevel > 98)
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "unlink %s",
- f);
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f);
i = unlink(f);
+ save_errno = errno;
if (i < 0 && LogLevel > 97)
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "%s: unlink-fail %d",
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d",
f, errno);
+ if (i >= 0)
+ SYNC_DIR(f, false);
+ errno = save_errno;
+ return i;
}
- /*
+/*
** SFGETS -- "safe" fgets -- times out and ignores random interrupts.
**
** Parameters:
@@ -1215,86 +1173,85 @@ xunlink(f)
** during -- what we are trying to read (for error messages).
**
** Returns:
-** NULL on error (including timeout). This will also leave
+** NULL on error (including timeout). This may also leave
** buf containing a null string.
** buf otherwise.
-**
-** Side Effects:
-** none.
*/
-static jmp_buf CtxReadTimeout;
-
char *
sfgets(buf, siz, fp, timeout, during)
char *buf;
int siz;
- FILE *fp;
+ SM_FILE_T *fp;
time_t timeout;
char *during;
{
- register EVENT *ev = NULL;
register char *p;
int save_errno;
+ int io_timeout;
+
+ SM_REQUIRE(siz > 0);
+ SM_REQUIRE(buf != NULL);
if (fp == NULL)
{
buf[0] = '\0';
+ errno = EBADF;
return NULL;
}
- /* set the timeout */
- if (timeout != 0)
+ /* try to read */
+ p = NULL;
+ errno = 0;
+
+ /* convert the timeout to sm_io notation */
+ io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000;
+ while (!sm_io_eof(fp) && !sm_io_error(fp))
{
- if (setjmp(CtxReadTimeout) != 0)
+ errno = 0;
+ p = sm_io_fgets(fp, io_timeout, buf, siz);
+ if (p == NULL && errno == EAGAIN)
{
+ /* The sm_io_fgets() call timedout */
if (LogLevel > 1)
sm_syslog(LOG_NOTICE, CurEnv->e_id,
"timeout waiting for input from %.100s during %s",
- CurHostName ? CurHostName : "local",
+ CURHOSTNAME,
during);
buf[0] = '\0';
#if XDEBUG
checkfd012(during);
#endif /* XDEBUG */
if (TrafficLogFile != NULL)
- fprintf(TrafficLogFile, "%05d <<< [TIMEOUT]\n",
- (int) getpid());
- errno = 0;
+ (void) sm_io_fprintf(TrafficLogFile,
+ SM_TIME_DEFAULT,
+ "%05d <<< [TIMEOUT]\n",
+ (int) CurrentPid);
+ errno = ETIMEDOUT;
return NULL;
}
- ev = setevent(timeout, readtimeout, 0);
- }
-
- /* try to read */
- p = NULL;
- errno = 0;
- while (!feof(fp) && !ferror(fp))
- {
- errno = 0;
- p = fgets(buf, siz, fp);
if (p != NULL || errno != EINTR)
break;
- clearerr(fp);
+ (void) sm_io_clearerr(fp);
}
save_errno = errno;
- /* clear the event if it has not sprung */
- clrevent(ev);
-
/* clean up the books and exit */
LineNumber++;
if (p == NULL)
{
buf[0] = '\0';
if (TrafficLogFile != NULL)
- fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid());
+ (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
+ "%05d <<< [EOF]\n",
+ (int) CurrentPid);
errno = save_errno;
return NULL;
}
if (TrafficLogFile != NULL)
- fprintf(TrafficLogFile, "%05d <<< %s", (int) getpid(), buf);
+ (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
+ "%05d <<< %s", (int) CurrentPid, buf);
if (SevenBitInput)
{
for (p = buf; *p != '\0'; p++)
@@ -1306,30 +1263,15 @@ sfgets(buf, siz, fp, timeout, during)
{
if (bitset(0200, *p))
{
- HasEightBits = TRUE;
+ HasEightBits = true;
break;
}
}
}
return buf;
}
-
-/* ARGSUSED */
-static void
-readtimeout(timeout)
- time_t timeout;
-{
- /*
- ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
- ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
- ** DOING.
- */
-
- errno = ETIMEDOUT;
- longjmp(CtxReadTimeout, 1);
-}
- /*
-** FGETFOLDED -- like fgets, but know about folded lines.
+/*
+** FGETFOLDED -- like fgets, but knows about folded lines.
**
** Parameters:
** buf -- place to put result.
@@ -1337,9 +1279,9 @@ readtimeout(timeout)
** f -- file to read from.
**
** Returns:
-** input line(s) on success, NULL on error or EOF.
+** input line(s) on success, NULL on error or SM_IO_EOF.
** This will normally be buf -- unless the line is too
-** long, when it will be xalloc()ed.
+** long, when it will be sm_malloc_x()ed.
**
** Side Effects:
** buf gets lines from f, with continuation lines (lines
@@ -1351,22 +1293,32 @@ char *
fgetfolded(buf, n, f)
char *buf;
register int n;
- FILE *f;
+ SM_FILE_T *f;
{
register char *p = buf;
char *bp = buf;
register int i;
+ SM_REQUIRE(n > 0);
+ SM_REQUIRE(buf != NULL);
+ if (f == NULL)
+ {
+ buf[0] = '\0';
+ errno = EBADF;
+ return NULL;
+ }
+
n--;
- while ((i = getc(f)) != EOF)
+ while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF)
{
if (i == '\r')
{
- i = getc(f);
+ i = sm_io_getc(f, SM_TIME_DEFAULT);
if (i != '\n')
{
- if (i != EOF)
- (void) ungetc(i, f);
+ if (i != SM_IO_EOF)
+ (void) sm_io_ungetc(f, SM_TIME_DEFAULT,
+ i);
i = '\r';
}
}
@@ -1381,7 +1333,7 @@ fgetfolded(buf, n, f)
nn *= 2;
else
nn += MEMCHUNKSIZE;
- nbp = xalloc(nn);
+ nbp = sm_malloc_x(nn);
memmove(nbp, bp, p - bp);
p = &nbp[p - bp];
if (bp != buf)
@@ -1393,9 +1345,9 @@ fgetfolded(buf, n, f)
if (i == '\n')
{
LineNumber++;
- i = getc(f);
- if (i != EOF)
- (void) ungetc(i, f);
+ i = sm_io_getc(f, SM_TIME_DEFAULT);
+ if (i != SM_IO_EOF)
+ (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i);
if (i != ' ' && i != '\t')
break;
}
@@ -1407,7 +1359,7 @@ fgetfolded(buf, n, f)
*p = '\0';
return bp;
}
- /*
+/*
** CURTIME -- return current time.
**
** Parameters:
@@ -1415,9 +1367,6 @@ fgetfolded(buf, n, f)
**
** Returns:
** the current time.
-**
-** Side Effects:
-** none.
*/
time_t
@@ -1428,20 +1377,17 @@ curtime()
(void) time(&t);
return t;
}
- /*
+/*
** ATOBOOL -- convert a string representation to boolean.
**
-** Defaults to "TRUE"
+** Defaults to false
**
** Parameters:
-** s -- string to convert. Takes "tTyY" as true,
+** s -- string to convert. Takes "tTyY", empty, and NULL as true,
** others as false.
**
** Returns:
** A boolean representation of the string.
-**
-** Side Effects:
-** none.
*/
bool
@@ -1449,10 +1395,10 @@ atobool(s)
register char *s;
{
if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
- return TRUE;
- return FALSE;
+ return true;
+ return false;
}
- /*
+/*
** ATOOCT -- convert a string representation to octal.
**
** Parameters:
@@ -1461,9 +1407,6 @@ atobool(s)
** Returns:
** An integer representing the string interpreted as an
** octal number.
-**
-** Side Effects:
-** none.
*/
int
@@ -1476,18 +1419,15 @@ atooct(s)
i = (i << 3) | (*s++ - '0');
return i;
}
- /*
+/*
** BITINTERSECT -- tell if two bitmaps intersect
**
** Parameters:
** a, b -- the bitmaps in question
**
** Returns:
-** TRUE if they have a non-null intersection
-** FALSE otherwise
-**
-** Side Effects:
-** none.
+** true if they have a non-null intersection
+** false otherwise
*/
bool
@@ -1500,22 +1440,19 @@ bitintersect(a, b)
for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
{
if ((a[i] & b[i]) != 0)
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
- /*
+/*
** BITZEROP -- tell if a bitmap is all zero
**
** Parameters:
** map -- the bit map to check
**
** Returns:
-** TRUE if map is all zero.
-** FALSE if there are any bits set in map.
-**
-** Side Effects:
-** none.
+** true if map is all zero.
+** false if there are any bits set in map.
*/
bool
@@ -1527,24 +1464,26 @@ bitzerop(map)
for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
{
if (map[i] != 0)
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
- /*
+/*
** STRCONTAINEDIN -- tell if one string is contained in another
**
** Parameters:
+** icase -- ignore case?
** a -- possible substring.
** b -- possible superstring.
**
** Returns:
-** TRUE if a is contained in b.
-** FALSE otherwise.
+** true if a is contained in b (case insensitive).
+** false otherwise.
*/
bool
-strcontainedin(a, b)
+strcontainedin(icase, a, b)
+ bool icase;
register char *a;
register char *b;
{
@@ -1555,18 +1494,29 @@ strcontainedin(a, b)
la = strlen(a);
lb = strlen(b);
c = *a;
- if (isascii(c) && isupper(c))
+ if (icase && isascii(c) && isupper(c))
c = tolower(c);
for (; lb-- >= la; b++)
{
- if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
- continue;
- if (strncasecmp(a, b, la) == 0)
- return TRUE;
+ if (icase)
+ {
+ if (*b != c &&
+ isascii(*b) && isupper(*b) && tolower(*b) != c)
+ continue;
+ if (sm_strncasecmp(a, b, la) == 0)
+ return true;
+ }
+ else
+ {
+ if (*b != c)
+ continue;
+ if (strncmp(a, b, la) == 0)
+ return true;
+ }
}
- return FALSE;
+ return false;
}
- /*
+/*
** CHECKFD012 -- check low numbered file descriptors
**
** File descriptors 0, 1, and 2 should be open at all times.
@@ -1590,7 +1540,7 @@ checkfd012(where)
fill_fd(i, where);
#endif /* XDEBUG */
}
- /*
+/*
** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
**
** Parameters:
@@ -1612,11 +1562,11 @@ checkfdopen(fd, where)
if (fstat(fd, &st) < 0 && errno == EBADF)
{
syserr("checkfdopen(%d): %s not open as expected!", fd, where);
- printopenfds(TRUE);
+ printopenfds(true);
}
#endif /* XDEBUG */
}
- /*
+/*
** CHECKFDS -- check for new or missing file descriptors
**
** Parameters:
@@ -1635,7 +1585,7 @@ checkfds(where)
{
int maxfd;
register int fd;
- bool printhdr = TRUE;
+ bool printhdr = true;
int save_errno = errno;
static BITMAP256 baseline;
extern int DtableSize;
@@ -1670,13 +1620,13 @@ checkfds(where)
sm_syslog(LOG_DEBUG, CurEnv->e_id,
"%s: changed fds:",
where);
- printhdr = FALSE;
+ printhdr = false;
}
- dumpfd(fd, TRUE, TRUE);
+ dumpfd(fd, true, true);
}
errno = save_errno;
}
- /*
+/*
** PRINTOPENFDS -- print the open file descriptors (for debugging)
**
** Parameters:
@@ -1699,9 +1649,9 @@ printopenfds(logit)
extern int DtableSize;
for (fd = 0; fd < DtableSize; fd++)
- dumpfd(fd, FALSE, logit);
+ dumpfd(fd, false, logit);
}
- /*
+/*
** DUMPFD -- dump a file descriptor
**
** Parameters:
@@ -1709,6 +1659,9 @@ printopenfds(logit)
** printclosed -- if set, print a notification even if
** it is closed; otherwise print nothing.
** logit -- if set, send output to syslog instead of stdout.
+**
+** Returns:
+** none.
*/
void
@@ -1732,7 +1685,7 @@ dumpfd(fd, printclosed, logit)
char buf[200];
p = buf;
- snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
p += strlen(p);
if (
@@ -1745,13 +1698,14 @@ dumpfd(fd, printclosed, logit)
{
if (errno != EBADF)
{
- snprintf(p, SPACELEFT(buf, p), "CANNOT STAT (%s)",
- errstring(errno));
+ (void) sm_snprintf(p, SPACELEFT(buf, p),
+ "CANNOT STAT (%s)",
+ sm_errstring(errno));
goto printit;
}
else if (printclosed)
{
- snprintf(p, SPACELEFT(buf, p), "CLOSED");
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED");
goto printit;
}
return;
@@ -1760,23 +1714,24 @@ dumpfd(fd, printclosed, logit)
i = fcntl(fd, F_GETFL, NULL);
if (i != -1)
{
- snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
p += strlen(p);
}
- snprintf(p, SPACELEFT(buf, p), "mode=%o: ", (int) st.st_mode);
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ",
+ (int) st.st_mode);
p += strlen(p);
switch (st.st_mode & S_IFMT)
{
#ifdef S_IFSOCK
case S_IFSOCK:
- snprintf(p, SPACELEFT(buf, p), "SOCK ");
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK ");
p += strlen(p);
memset(&sa, '\0', sizeof sa);
slen = sizeof sa;
if (getsockname(fd, &sa.sa, &slen) < 0)
- snprintf(p, SPACELEFT(buf, p), "(%s)",
- errstring(errno));
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
+ sm_errstring(errno));
else
{
hp = hostnamebyanyaddr(&sa);
@@ -1787,23 +1742,25 @@ dumpfd(fd, printclosed, logit)
}
# if NETINET
else if (sa.sa.sa_family == AF_INET)
- snprintf(p, SPACELEFT(buf, p), "%s/%d",
- hp, ntohs(sa.sin.sin_port));
+ (void) sm_snprintf(p, SPACELEFT(buf, p),
+ "%s/%d", hp, ntohs(sa.sin.sin_port));
# endif /* NETINET */
# if NETINET6
else if (sa.sa.sa_family == AF_INET6)
- snprintf(p, SPACELEFT(buf, p), "%s/%d",
- hp, ntohs(sa.sin6.sin6_port));
+ (void) sm_snprintf(p, SPACELEFT(buf, p),
+ "%s/%d", hp, ntohs(sa.sin6.sin6_port));
# endif /* NETINET6 */
else
- snprintf(p, SPACELEFT(buf, p), "%s", hp);
+ (void) sm_snprintf(p, SPACELEFT(buf, p),
+ "%s", hp);
}
p += strlen(p);
- snprintf(p, SPACELEFT(buf, p), "->");
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "->");
p += strlen(p);
slen = sizeof sa;
if (getpeername(fd, &sa.sa, &slen) < 0)
- snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno));
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
+ sm_errstring(errno));
else
{
hp = hostnamebyanyaddr(&sa);
@@ -1814,75 +1771,65 @@ dumpfd(fd, printclosed, logit)
}
# if NETINET
else if (sa.sa.sa_family == AF_INET)
- snprintf(p, SPACELEFT(buf, p), "%s/%d",
- hp, ntohs(sa.sin.sin_port));
+ (void) sm_snprintf(p, SPACELEFT(buf, p),
+ "%s/%d", hp, ntohs(sa.sin.sin_port));
# endif /* NETINET */
# if NETINET6
else if (sa.sa.sa_family == AF_INET6)
- snprintf(p, SPACELEFT(buf, p), "%s/%d",
- hp, ntohs(sa.sin6.sin6_port));
+ (void) sm_snprintf(p, SPACELEFT(buf, p),
+ "%s/%d", hp, ntohs(sa.sin6.sin6_port));
# endif /* NETINET6 */
else
- snprintf(p, SPACELEFT(buf, p), "%s", hp);
+ (void) sm_snprintf(p, SPACELEFT(buf, p),
+ "%s", hp);
}
break;
#endif /* S_IFSOCK */
case S_IFCHR:
- snprintf(p, SPACELEFT(buf, p), "CHR: ");
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: ");
p += strlen(p);
goto defprint;
+#ifdef S_IFBLK
case S_IFBLK:
- snprintf(p, SPACELEFT(buf, p), "BLK: ");
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: ");
p += strlen(p);
goto defprint;
+#endif /* S_IFBLK */
#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
case S_IFIFO:
- snprintf(p, SPACELEFT(buf, p), "FIFO: ");
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: ");
p += strlen(p);
goto defprint;
#endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */
#ifdef S_IFDIR
case S_IFDIR:
- snprintf(p, SPACELEFT(buf, p), "DIR: ");
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: ");
p += strlen(p);
goto defprint;
#endif /* S_IFDIR */
#ifdef S_IFLNK
case S_IFLNK:
- snprintf(p, SPACELEFT(buf, p), "LNK: ");
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: ");
p += strlen(p);
goto defprint;
#endif /* S_IFLNK */
default:
defprint:
- /*CONSTCOND*/
- if (sizeof st.st_ino > sizeof (long))
- snprintf(p, SPACELEFT(buf, p),
- "dev=%d/%d, ino=%s, nlink=%d, u/gid=%d/%d, ",
- major(st.st_dev), minor(st.st_dev),
- quad_to_string(st.st_ino),
- (int) st.st_nlink, (int) st.st_uid,
- (int) st.st_gid);
- else
- snprintf(p, SPACELEFT(buf, p),
- "dev=%d/%d, ino=%lu, nlink=%d, u/gid=%d/%d, ",
- major(st.st_dev), minor(st.st_dev),
- (unsigned long) st.st_ino,
- (int) st.st_nlink, (int) st.st_uid,
- (int) st.st_gid);
- /*CONSTCOND*/
- if (sizeof st.st_size > sizeof (long))
- snprintf(p, SPACELEFT(buf, p), "size=%s",
- quad_to_string(st.st_size));
- else
- snprintf(p, SPACELEFT(buf, p), "size=%lu",
- (unsigned long) st.st_size);
+ (void) sm_snprintf(p, SPACELEFT(buf, p),
+ "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ",
+ major(st.st_dev), minor(st.st_dev),
+ (ULONGLONG_T) st.st_ino,
+ (int) st.st_nlink, (int) st.st_uid,
+ (int) st.st_gid);
+ p += strlen(p);
+ (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu",
+ (ULONGLONG_T) st.st_size);
break;
}
@@ -1891,16 +1838,16 @@ printit:
sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
"%.800s", buf);
else
- printf("%s\n", buf);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", buf);
}
- /*
+/*
** SHORTEN_HOSTNAME -- strip local domain information off of hostname.
**
** Parameters:
** host -- the host to shorten (stripped in place).
**
** Returns:
-** place where string was trunacted, NULL if not truncated.
+** place where string was truncated, NULL if not truncated.
*/
char *
@@ -1910,14 +1857,15 @@ shorten_hostname(host)
register char *p;
char *mydom;
int i;
- bool canon = FALSE;
+ bool canon = false;
/* strip off final dot */
- p = &host[strlen(host) - 1];
+ i = strlen(host);
+ p = &host[(i == 0) ? 0 : i - 1];
if (*p == '.')
{
*p = '\0';
- canon = TRUE;
+ canon = true;
}
/* see if there is any domain at all -- if not, we are done */
@@ -1930,15 +1878,16 @@ shorten_hostname(host)
if (mydom == NULL)
mydom = "";
i = strlen(++p);
- if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 &&
- (mydom[i] == '.' || mydom[i] == '\0'))
+ if ((canon ? sm_strcasecmp(p, mydom)
+ : sm_strncasecmp(p, mydom, i)) == 0 &&
+ (mydom[i] == '.' || mydom[i] == '\0'))
{
*--p = '\0';
return p;
}
return NULL;
}
- /*
+/*
** PROG_OPEN -- open a program for reading
**
** Parameters:
@@ -1959,6 +1908,8 @@ prog_open(argv, pfd, e)
pid_t pid;
int i;
int save_errno;
+ int sff;
+ int ret;
int fdv[2];
char *p, *q;
char buf[MAXLINE + 1];
@@ -1985,13 +1936,22 @@ prog_open(argv, pfd, e)
return pid;
}
- /* child -- close stdin */
- (void) close(0);
-
/* Reset global flags */
RestartRequest = NULL;
+ RestartWorkGroup = false;
ShutdownRequest = NULL;
PendingSignal = 0;
+ CurrentPid = getpid();
+
+ /*
+ ** Initialize exception stack and default exception
+ ** handler for child process.
+ */
+
+ sm_exc_newthread(fatal_error);
+
+ /* child -- close stdin */
+ (void) close(0);
/* stdout goes back to parent */
(void) close(fdv[0]);
@@ -2007,7 +1967,7 @@ prog_open(argv, pfd, e)
{
int xfd;
- xfd = fileno(e->e_xfp);
+ xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL);
if (xfd >= 0 && dup2(xfd, 2) < 0)
{
syserr("%s: cannot dup2 for stderr", argv[0]);
@@ -2017,7 +1977,7 @@ prog_open(argv, pfd, e)
/* this process has no right to the queue file */
if (e->e_lockfp != NULL)
- (void) close(fileno(e->e_lockfp));
+ (void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL));
/* chroot to the program mailer directory, if defined */
if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL)
@@ -2037,6 +1997,7 @@ prog_open(argv, pfd, e)
/* run as default user */
endpwent();
+ sm_mbdb_terminate();
if (setgid(DefGid) < 0 && geteuid() == 0)
{
syserr("prog_open: setgid(%ld) failed", (long) DefGid);
@@ -2071,6 +2032,20 @@ prog_open(argv, pfd, e)
(void) chdir("/");
}
+ /* Check safety of program to be run */
+ sff = SFF_ROOTOK|SFF_EXECOK;
+ if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail))
+ sff |= SFF_NOGWFILES|SFF_NOWWFILES;
+ if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail))
+ sff |= SFF_NOPATHCHECK;
+ else
+ sff |= SFF_SAFEDIRPATH;
+ ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL);
+ if (ret != 0)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Warning: prog_open: program %s unsafe: %s",
+ argv[0], sm_errstring(ret));
+
/* arrange for all the files to be closed */
for (i = 3; i < DtableSize; i++)
{
@@ -2091,7 +2066,7 @@ prog_open(argv, pfd, e)
_exit(EX_CONFIG);
return -1; /* avoid compiler warning on IRIX */
}
- /*
+/*
** GET_COLUMN -- look up a Column in a line buffer
**
** Parameters:
@@ -2120,23 +2095,23 @@ get_column(line, col, delim, buf, buflen)
int i;
char delimbuf[4];
- if ((char)delim == '\0')
- (void) strlcpy(delimbuf, "\n\t ", sizeof delimbuf);
+ if ((char) delim == '\0')
+ (void) sm_strlcpy(delimbuf, "\n\t ", sizeof delimbuf);
else
{
- delimbuf[0] = (char)delim;
+ delimbuf[0] = (char) delim;
delimbuf[1] = '\0';
}
p = line;
if (*p == '\0')
return NULL; /* line empty */
- if (*p == (char)delim && col == 0)
+ if (*p == (char) delim && col == 0)
return NULL; /* first column empty */
begin = line;
- if (col == 0 && (char)delim == '\0')
+ if (col == 0 && (char) delim == '\0')
{
while (*begin != '\0' && isascii(*begin) && isspace(*begin))
begin++;
@@ -2147,7 +2122,7 @@ get_column(line, col, delim, buf, buflen)
if ((begin = strpbrk(begin, delimbuf)) == NULL)
return NULL; /* no such column */
begin++;
- if ((char)delim == '\0')
+ if ((char) delim == '\0')
{
while (*begin != '\0' && isascii(*begin) && isspace(*begin))
begin++;
@@ -2161,10 +2136,10 @@ get_column(line, col, delim, buf, buflen)
i = end - begin;
if (i >= buflen)
i = buflen - 1;
- (void) strlcpy(buf, begin, i + 1);
+ (void) sm_strlcpy(buf, begin, i + 1);
return buf;
}
- /*
+/*
** CLEANSTRCPY -- copy string keeping out bogus characters
**
** Parameters:
@@ -2183,7 +2158,7 @@ cleanstrcpy(t, f, l)
int l;
{
/* check for newlines and log if necessary */
- (void) denlstring(f, TRUE, TRUE);
+ (void) denlstring(f, true, true);
if (l <= 0)
syserr("!cleanstrcpy: length == 0");
@@ -2201,8 +2176,7 @@ cleanstrcpy(t, f, l)
}
*t = '\0';
}
-
- /*
+/*
** DENLSTRING -- convert newlines in a string to spaces
**
** Parameters:
@@ -2237,12 +2211,14 @@ denlstring(s, strict, logattacks)
if (bl < l)
{
/* allocate more space */
+ char *nbp = sm_pmalloc_x(l);
+
if (bp != NULL)
sm_free(bp);
- bp = xalloc(l);
+ bp = nbp;
bl = l;
}
- (void) strlcpy(bp, s, l);
+ (void) sm_strlcpy(bp, s, l);
for (p = bp; (p = strchr(p, '\n')) != NULL; )
*p++ = ' ';
@@ -2256,7 +2232,100 @@ denlstring(s, strict, logattacks)
return bp;
}
- /*
+/*
+** STR2PRT -- convert "unprintable" characters in a string to \oct
+**
+** Parameters:
+** s -- string to convert
+**
+** Returns:
+** converted string.
+** This is a static local buffer, string must be copied
+** before this function is called again!
+*/
+
+char *
+str2prt(s)
+ char *s;
+{
+ int l;
+ char c, *h;
+ bool ok;
+ static int len = 0;
+ static char *buf = NULL;
+
+ if (s == NULL)
+ return NULL;
+ ok = true;
+ for (h = s, l = 1; *h != '\0'; h++, l++)
+ {
+ if (*h == '\\')
+ {
+ ++l;
+ ok = false;
+ }
+ else if (!(isascii(*h) && isprint(*h)))
+ {
+ l += 3;
+ ok = false;
+ }
+ }
+ if (ok)
+ return s;
+ if (l > len)
+ {
+ char *nbuf = sm_pmalloc_x(l);
+
+ if (buf != NULL)
+ sm_free(buf);
+ len = l;
+ buf = nbuf;
+ }
+ for (h = buf; *s != '\0' && l > 0; s++, l--)
+ {
+ c = *s;
+ if (isascii(c) && isprint(c) && c != '\\')
+ {
+ *h++ = c;
+ }
+ else
+ {
+ *h++ = '\\';
+ --l;
+ switch (c)
+ {
+ case '\\':
+ *h++ = '\\';
+ break;
+ case '\t':
+ *h++ = 't';
+ break;
+ case '\n':
+ *h++ = 'n';
+ break;
+ case '\r':
+ *h++ = 'r';
+ break;
+ default:
+ (void) sm_snprintf(h, l, "%03o", (int) c);
+
+ /*
+ ** XXX since l is unsigned this may
+ ** wrap around if the calculation is screwed
+ ** up...
+ */
+
+ l -= 2;
+ h += 3;
+ break;
+ }
+ }
+ }
+ *h = '\0';
+ buf[len - 1] = '\0';
+ return buf;
+}
+/*
** PATH_IS_DIR -- check to see if file exists and is a directory.
**
** There are some additional checks for security violations in
@@ -2268,8 +2337,8 @@ denlstring(s, strict, logattacks)
** createflag -- if set, create directory if needed.
**
** Returns:
-** TRUE -- if the indicated pathname is a directory
-** FALSE -- otherwise
+** true -- if the indicated pathname is a directory
+** false -- otherwise
*/
int
@@ -2286,46 +2355,63 @@ path_is_dir(pathname, createflag)
#endif /* HASLSTAT */
{
if (errno != ENOENT || !createflag)
- return FALSE;
+ return false;
if (mkdir(pathname, 0755) < 0)
- return FALSE;
- return TRUE;
+ return false;
+ return true;
}
if (!S_ISDIR(statbuf.st_mode))
{
errno = ENOTDIR;
- return FALSE;
+ return false;
}
/* security: don't allow writable directories */
if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
{
errno = EACCES;
- return FALSE;
+ return false;
}
-
- return TRUE;
+ return true;
}
- /*
+/*
** PROC_LIST_ADD -- add process id to list of our children
**
** Parameters:
** pid -- pid to add to list.
** task -- task of pid.
** type -- type of process.
+** count -- number of processes.
+** other -- other information for this type.
**
** Returns:
** none
+**
+** Side Effects:
+** May increase CurChildren. May grow ProcList.
*/
-static struct procs *volatile ProcListVec = NULL;
-static int ProcListSize = 0;
+typedef struct procs PROCS_T;
+
+struct procs
+{
+ pid_t proc_pid;
+ char *proc_task;
+ int proc_type;
+ int proc_count;
+ int proc_other;
+};
+
+static PROCS_T *volatile ProcListVec = NULL;
+static int ProcListSize = 0;
void
-proc_list_add(pid, task, type)
+proc_list_add(pid, task, type, count, other)
pid_t pid;
char *task;
int type;
+ int count;
+ int other;
{
int i;
@@ -2349,16 +2435,19 @@ proc_list_add(pid, task, type)
if (i >= ProcListSize)
{
/* grow process list */
- struct procs *npv;
+ PROCS_T *npv;
- npv = (struct procs *) xalloc((sizeof *npv) *
- (ProcListSize + PROC_LIST_SEG));
+ SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG);
+ npv = (PROCS_T *) sm_pmalloc_x((sizeof *npv) *
+ (ProcListSize + PROC_LIST_SEG));
if (ProcListSize > 0)
{
memmove(npv, ProcListVec,
- ProcListSize * sizeof (struct procs));
+ ProcListSize * sizeof (PROCS_T));
sm_free(ProcListVec);
}
+
+ /* XXX just use memset() to initialize this part? */
for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
{
npv[i].proc_pid = NO_PID;
@@ -2370,16 +2459,19 @@ proc_list_add(pid, task, type)
ProcListVec = npv;
}
ProcListVec[i].proc_pid = pid;
- if (ProcListVec[i].proc_task != NULL)
- sm_free(ProcListVec[i].proc_task);
- ProcListVec[i].proc_task = newstr(task);
+ PSTRSET(ProcListVec[i].proc_task, task);
ProcListVec[i].proc_type = type;
+ ProcListVec[i].proc_count = count;
+ ProcListVec[i].proc_other = other;
/* if process adding itself, it's not a child */
- if (pid != getpid())
+ if (pid != CurrentPid)
+ {
+ SM_ASSERT(CurChildren < INT_MAX);
CurChildren++;
+ }
}
- /*
+/*
** PROC_LIST_SET -- set pid task in process list
**
** Parameters:
@@ -2401,30 +2493,36 @@ proc_list_set(pid, task)
{
if (ProcListVec[i].proc_pid == pid)
{
- if (ProcListVec[i].proc_task != NULL)
- sm_free(ProcListVec[i].proc_task);
- ProcListVec[i].proc_task = newstr(task);
+ PSTRSET(ProcListVec[i].proc_task, task);
break;
}
}
}
- /*
+/*
** PROC_LIST_DROP -- drop pid from process list
**
** Parameters:
** pid -- pid to drop
+** st -- process status
+** other -- storage for proc_other (return).
**
** Returns:
-** type of process
+** none.
+**
+** Side Effects:
+** May decrease CurChildren, CurRunners, or
+** set RestartRequest or ShutdownRequest.
**
** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
** DOING.
*/
-int
-proc_list_drop(pid)
+void
+proc_list_drop(pid, st, other)
pid_t pid;
+ int st;
+ int *other;
{
int i;
int type = PROC_NONE;
@@ -2435,6 +2533,8 @@ proc_list_drop(pid)
{
ProcListVec[i].proc_pid = NO_PID;
type = ProcListVec[i].proc_type;
+ if (other != NULL)
+ *other = ProcListVec[i].proc_other;
break;
}
}
@@ -2442,9 +2542,24 @@ proc_list_drop(pid)
CurChildren--;
- return type;
+ if (type == PROC_CONTROL && WIFEXITED(st))
+ {
+ /* if so, see if we need to restart or shutdown */
+ if (WEXITSTATUS(st) == EX_RESTART)
+ RestartRequest = "control socket";
+ else if (WEXITSTATUS(st) == EX_SHUTDOWN)
+ ShutdownRequest = "control socket";
+ }
+ else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) &&
+ ProcListVec[i].proc_other > -1)
+ {
+ /* restart this persistent runner */
+ mark_work_group_restart(ProcListVec[i].proc_other, st);
+ }
+ else if (type == PROC_QUEUE)
+ CurRunners -= ProcListVec[i].proc_count;
}
- /*
+/*
** PROC_LIST_CLEAR -- clear the process list
**
** Parameters:
@@ -2452,6 +2567,9 @@ proc_list_drop(pid)
**
** Returns:
** none.
+**
+** Side Effects:
+** Sets CurChildren to zero.
*/
void
@@ -2461,12 +2579,10 @@ proc_list_clear()
/* start from 1 since 0 is the daemon itself */
for (i = 1; i < ProcListSize; i++)
- {
ProcListVec[i].proc_pid = NO_PID;
- }
CurChildren = 0;
}
- /*
+/*
** PROC_LIST_PROBE -- probe processes in the list to see if they still exist
**
** Parameters:
@@ -2474,6 +2590,9 @@ proc_list_clear()
**
** Returns:
** none
+**
+** Side Effects:
+** May decrease CurChildren.
*/
void
@@ -2493,25 +2612,29 @@ proc_list_probe()
"proc_list_probe: lost pid %d",
(int) ProcListVec[i].proc_pid);
ProcListVec[i].proc_pid = NO_PID;
+ SM_FREE_CLR(ProcListVec[i].proc_task);
CurChildren--;
}
}
if (CurChildren < 0)
CurChildren = 0;
}
- /*
+
+/*
** PROC_LIST_DISPLAY -- display the process list
**
** Parameters:
** out -- output file pointer
+** prefix -- string to output in front of each line.
**
** Returns:
** none.
*/
void
-proc_list_display(out)
- FILE *out;
+proc_list_display(out, prefix)
+ SM_FILE_T *out;
+ char *prefix;
{
int i;
@@ -2520,202 +2643,60 @@ proc_list_display(out)
if (ProcListVec[i].proc_pid == NO_PID)
continue;
- fprintf(out, "%d %s%s\n", (int) ProcListVec[i].proc_pid,
- ProcListVec[i].proc_task != NULL ?
- ProcListVec[i].proc_task : "(unknown)",
- (OpMode == MD_SMTP ||
- OpMode == MD_DAEMON ||
- OpMode == MD_ARPAFTP) ? "\r" : "");
+ (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n",
+ prefix,
+ (int) ProcListVec[i].proc_pid,
+ ProcListVec[i].proc_task != NULL ?
+ ProcListVec[i].proc_task : "(unknown)",
+ (OpMode == MD_SMTP ||
+ OpMode == MD_DAEMON ||
+ OpMode == MD_ARPAFTP) ? "\r" : "");
}
}
- /*
-** SAFEFOPEN -- do a file open with extra checking
+
+/*
+** PROC_LIST_SIGNAL -- send a signal to a type of process in the list
**
** Parameters:
-** fn -- the file name to open.
-** omode -- the open-style mode flags.
-** cmode -- the create-style mode flags.
-** sff -- safefile flags.
+** type -- type of process to signal
+** signal -- the type of signal to send
**
-** Returns:
-** Same as fopen.
+** Results:
+** none.
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
*/
-FILE *
-safefopen(fn, omode, cmode, sff)
- char *fn;
- int omode;
- int cmode;
- long sff;
+void
+proc_list_signal(type, signal)
+ int type;
+ int signal;
{
- int fd;
- int save_errno;
- FILE *fp;
- char *fmode;
-
- switch (omode & O_ACCMODE)
- {
- case O_RDONLY:
- fmode = "r";
- break;
-
- case O_WRONLY:
- if (bitset(O_APPEND, omode))
- fmode = "a";
- else
- fmode = "w";
- break;
+ int chldwasblocked;
+ int alrmwasblocked;
+ int i;
+ pid_t mypid = getpid();
- case O_RDWR:
- if (bitset(O_TRUNC, omode))
- fmode = "w+";
- else if (bitset(O_APPEND, omode))
- fmode = "a+";
- else
- fmode = "r+";
- break;
+ /* block these signals so that we may signal cleanly */
+ chldwasblocked = sm_blocksignal(SIGCHLD);
+ alrmwasblocked = sm_blocksignal(SIGALRM);
- default:
- syserr("554 5.3.5 safefopen: unknown omode %o", omode);
- fmode = "x";
- }
- fd = safeopen(fn, omode, cmode, sff);
- if (fd < 0)
- {
- save_errno = errno;
- if (tTd(44, 10))
- dprintf("safefopen: safeopen failed: %s\n",
- errstring(errno));
- errno = save_errno;
- return NULL;
- }
- fp = fdopen(fd, fmode);
- if (fp != NULL)
- return fp;
-
- save_errno = errno;
- if (tTd(44, 10))
+ /* Find all processes of type and send signal */
+ for (i = 0; i < ProcListSize; i++)
{
- dprintf("safefopen: fdopen(%s, %s) failed: omode=%x, sff=%lx, err=%s\n",
- fn, fmode, omode, sff, errstring(errno));
+ if (ProcListVec[i].proc_pid == NO_PID ||
+ ProcListVec[i].proc_pid == mypid)
+ continue;
+ if (ProcListVec[i].proc_type != type)
+ continue;
+ (void) kill(ProcListVec[i].proc_pid, signal);
}
- (void) close(fd);
- errno = save_errno;
- return NULL;
-}
- /*
-** SM_STRCASECMP -- 8-bit clean version of strcasecmp
-**
-** Thank you, vendors, for making this all necessary.
-*/
-
-/*
- * Copyright (c) 1987, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)strcasecmp.c 8.1 (Berkeley) 6/4/93";
-#endif /* defined(LIBC_SCCS) && !defined(lint) */
-
-/*
- * This array is designed for mapping upper and lower case letter
- * together for a case independent comparison. The mappings are
- * based upon ascii character sequences.
- */
-static const u_char charmap[] = {
- 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
- 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
- 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
- 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
- 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
- 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
- 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
- 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
- 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
- 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
- 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
- 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
- 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
- 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
- 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
- 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
- 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
- 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
- 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
- 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
- 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
- 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
- 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
- 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
- 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
- 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
- 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
- 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
- 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
- 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
- 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
- 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
-};
-
-int
-sm_strcasecmp(s1, s2)
- const char *s1, *s2;
-{
- register const u_char *cm = charmap,
- *us1 = (const u_char *)s1,
- *us2 = (const u_char *)s2;
-
- while (cm[*us1] == cm[*us2++])
- if (*us1++ == '\0')
- return 0;
- return (cm[*us1] - cm[*--us2]);
-}
-
-int
-sm_strncasecmp(s1, s2, n)
- const char *s1, *s2;
- register size_t n;
-{
- if (n != 0) {
- register const u_char *cm = charmap,
- *us1 = (const u_char *)s1,
- *us2 = (const u_char *)s2;
-
- do {
- if (cm[*us1] != cm[*us2++])
- return (cm[*us1] - cm[*--us2]);
- if (*us1++ == '\0')
- break;
- } while (--n != 0);
- }
- return 0;
+ /* restore the signals */
+ if (alrmwasblocked == 0)
+ (void) sm_releasesignal(SIGALRM);
+ if (chldwasblocked == 0)
+ (void) sm_releasesignal(SIGCHLD);
}
diff --git a/contrib/sendmail/src/version.c b/contrib/sendmail/src/version.c
index ce675c7..95c7ee2 100644
--- a/contrib/sendmail/src/version.c
+++ b/contrib/sendmail/src/version.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2002 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 @@
*
*/
-#ifndef lint
-static char id[] = "@(#)$Id: version.c,v 8.43.4.39 2001/08/20 14:45:34 gshapiro Exp $";
-#endif /* ! lint */
+#include <sm/gen.h>
-char Version[] = "8.11.6";
+SM_RCSID("@(#)$Id: version.c,v 8.91 2002/01/13 18:23:00 ca Exp $")
+
+char Version[] = "8.12.2";
OpenPOWER on IntegriCloud