summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/src
diff options
context:
space:
mode:
authorgshapiro <gshapiro@FreeBSD.org>2000-08-12 21:55:49 +0000
committergshapiro <gshapiro@FreeBSD.org>2000-08-12 21:55:49 +0000
commitc3cd75415d60bc002b20182ffd3383ea9e901a80 (patch)
tree211dfd0f771f89d6abe14fa94cab53985a9d0116 /contrib/sendmail/src
parent231592eb7942ebd4becae24ea8e018acea3742a9 (diff)
parent4332139a9a11f773ffe5109bed871561e3c290a1 (diff)
downloadFreeBSD-src-c3cd75415d60bc002b20182ffd3383ea9e901a80.zip
FreeBSD-src-c3cd75415d60bc002b20182ffd3383ea9e901a80.tar.gz
This commit was generated by cvs2svn to compensate for changes in r64562,
which included commits to RCS files with non-trunk default branches.
Diffstat (limited to 'contrib/sendmail/src')
-rw-r--r--contrib/sendmail/src/Makefile17
-rw-r--r--contrib/sendmail/src/Makefile.m4179
-rw-r--r--contrib/sendmail/src/README462
-rw-r--r--contrib/sendmail/src/TRACEFLAGS12
-rw-r--r--contrib/sendmail/src/alias.c284
-rw-r--r--contrib/sendmail/src/aliases1
-rw-r--r--contrib/sendmail/src/arpadate.c23
-rw-r--r--contrib/sendmail/src/bf.h26
-rw-r--r--contrib/sendmail/src/bf_portable.c482
-rw-r--r--contrib/sendmail/src/bf_portable.h46
-rw-r--r--contrib/sendmail/src/bf_torek.c784
-rw-r--r--contrib/sendmail/src/bf_torek.h42
-rw-r--r--contrib/sendmail/src/clock.c99
-rw-r--r--contrib/sendmail/src/collect.c293
-rw-r--r--contrib/sendmail/src/control.c180
-rw-r--r--contrib/sendmail/src/convtime.c28
-rw-r--r--contrib/sendmail/src/daemon.c2401
-rw-r--r--contrib/sendmail/src/deliver.c2662
-rw-r--r--contrib/sendmail/src/domain.c352
-rw-r--r--contrib/sendmail/src/envelope.c271
-rw-r--r--contrib/sendmail/src/helpfile136
-rw-r--r--contrib/sendmail/src/macro.c65
-rw-r--r--contrib/sendmail/src/main.c1198
-rw-r--r--contrib/sendmail/src/map.c3784
-rw-r--r--contrib/sendmail/src/milter.c3402
-rw-r--r--contrib/sendmail/src/mime.c145
-rw-r--r--contrib/sendmail/src/newaliases.158
-rw-r--r--contrib/sendmail/src/parseaddr.c853
-rw-r--r--contrib/sendmail/src/queue.c1904
-rw-r--r--contrib/sendmail/src/readcf.c1508
-rw-r--r--contrib/sendmail/src/recipient.c761
-rw-r--r--contrib/sendmail/src/sendmail.h2062
-rw-r--r--contrib/sendmail/src/sfsasl.c367
-rw-r--r--contrib/sendmail/src/sfsasl.h60
-rw-r--r--contrib/sendmail/src/shmticklib.c84
-rw-r--r--contrib/sendmail/src/srvrsmtp.c3206
-rw-r--r--contrib/sendmail/src/stab.c161
-rw-r--r--contrib/sendmail/src/stats.c93
-rw-r--r--contrib/sendmail/src/statusd_shm.h43
-rw-r--r--contrib/sendmail/src/sysexits.c94
-rw-r--r--contrib/sendmail/src/timers.c229
-rw-r--r--contrib/sendmail/src/timers.h33
-rw-r--r--contrib/sendmail/src/trace.c15
-rw-r--r--contrib/sendmail/src/udb.c635
-rw-r--r--contrib/sendmail/src/usersmtp.c1553
-rw-r--r--contrib/sendmail/src/util.c565
-rw-r--r--contrib/sendmail/src/version.c9
47 files changed, 24999 insertions, 6668 deletions
diff --git a/contrib/sendmail/src/Makefile b/contrib/sendmail/src/Makefile
new file mode 100644
index 0000000..c86bbf5
--- /dev/null
+++ b/contrib/sendmail/src/Makefile
@@ -0,0 +1,17 @@
+# $Id: Makefile,v 8.11 1999/09/23 22:36:42 ca Exp $
+
+SHELL= /bin/sh
+BUILD= ./Build
+OPTIONS= $(CONFIG) $(FLAGS)
+
+all: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) $@
+clean: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) $@
+install: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) $@
+
+fresh: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) -c
+
+FRC:
diff --git a/contrib/sendmail/src/Makefile.m4 b/contrib/sendmail/src/Makefile.m4
index d88d35b..c59701b 100644
--- a/contrib/sendmail/src/Makefile.m4
+++ b/contrib/sendmail/src/Makefile.m4
@@ -1,152 +1,53 @@
-#
-# This Makefile is designed to work on any reasonably current version of
-# "make" program.
-#
-# @(#)Makefile.m4 8.26 (Berkeley) 1/23/1999
-#
+include(confBUILDTOOLSDIR`/M4/switch.m4')
-# C compiler
-CC= confCC
+bldPRODUCT_START(`executable', `sendmail')
+define(`bldBIN_TYPE', `S')
+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 ')
+PREPENDDEF(`confENVDEF', `confMAPDEF')
+bldPUSH_SMLIB(`smutil')
-# Shell
-SHELL= confSHELL
+define(`bldTARGET_LINKS', ifdef(`confLINKS', `confLINKS',
+`${DESTDIR}${UBINDIR}/newaliases ${DESTDIR}${UBINDIR}/mailq ${DESTDIR}${UBINDIR}/hoststat ${DESTDIR}${UBINDIR}/purgestat')
+)dnl
-# use O=-O (usual) or O=-g (debugging)
-O= ifdef(`confOPTIMIZE', `confOPTIMIZE', `-O')
+# location of sendmail statistics file (usually /etc/mail/ or /var/log)
+STDIR= ifdef(`confSTDIR', `confSTDIR', `/etc/mail')
-# location of sendmail source directory
-SRCDIR= .
+# full path to installed statistics file (usually ${STDIR}/statistics)
+STFILE= ${STDIR}/ifdef(`confSTFILE', `confSTFILE', `statistics')
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See README for a description of how these flags interact.
-#
-MAPDEF= ifdef(`confMAPDEF', `confMAPDEF')
+# location of sendmail helpfile file (usually /etc/mail)
+HFDIR= ifdef(`confHFDIR', `confHFDIR', `/etc/mail')
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= ifdef(`confENVDEF', `confENVDEF')
+# full path to installed help file (usually ${HFDIR}/helpfile)
+HFFILE= ${HFDIR}/ifdef(`confHFFILE', `confHFFILE', `helpfile')
-# see also conf.h for additional compilation flags
+ifdef(`confSMSRCADD', `APPENDDEF(`confSRCADD', `confSMSRCADD')')
+ifdef(`confSMOBJADD', `APPENDDEF(`confOBJADD', `confSMOBJADD')')
-# include directories
-INCDIRS=confINCDIRS
+bldPUSH_TARGET(`statistics')
+divert(bldTARGETS_SECTION)
+statistics:
+ ${CP} /dev/null statistics
-# loader options
-LDOPTS= ifdef(`confLDOPTS', `confLDOPTS')
+divert(0)
-# library directories
-LIBDIRS=confLIBDIRS
+ifdef(`confNO_HELPFILE_INSTALL',, `bldPUSH_INSTALL_TARGET(`install-hf')')
+ifdef(`confNO_STATISTICS_INSTALL',, `bldPUSH_INSTALL_TARGET(`install-st')')
+divert(bldTARGETS_SECTION)
+install-hf:
+ if [ ! -d ${DESTDIR}${HFDIR} ]; then mkdir -p ${DESTDIR}${HFDIR}; fi
+ ${INSTALL} -c -o ${UBINOWN} -g ${UBINGRP} -m 444 helpfile ${DESTDIR}${HFFILE}
-# libraries required on your system
-# delete -l44bsd if you are not running BIND 4.9.x
-LIBS= ifdef(`confLIBS', `confLIBS')
+install-st: statistics
+ if [ ! -d ${DESTDIR}${STDIR} ]; then mkdir -p ${DESTDIR}${STDIR}; fi
+ ${INSTALL} -c -o ${SBINOWN} -g ${UBINGRP} -m 644 statistics ${DESTDIR}${STFILE}
+divert(0)
+bldPRODUCT_END
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}ifdef(`confMBINDIR', `confMBINDIR', `/usr/sbin')
+bldPRODUCT_START(`manpage', `sendmail')
+define(`bldSOURCES', `sendmail.8 aliases.5 mailq.1 newaliases.1')
+bldPRODUCT_END
-# location of "user" binaries (usually /usr/bin or /usr/ucb)
-UBINDIR=${DESTDIR}ifdef(`confUBINDIR', `confUBINDIR', `/usr/bin')
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}ifdef(`confSTDIR', `confSTDIR', `/var/log')
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}ifdef(`confHFDIR', `confHFDIR', `/usr/share/misc')
-
-# additional .o files needed
-OBJADD= ifdef(`confOBJADD', `confOBJADD') ifdef(`confSMOBJADD', `confSMOBJADD')
-
-undivert(1)
-
-################### end of user configuration flags ######################
-
-BUILDBIN=confBUILDBIN
-COPTS= -I. ${INCDIRS} ${MAPDEF} ${ENVDEF}
-CFLAGS= $O ${COPTS}
-
-BEFORE= confBEFORE
-OBJS= alias.o arpadate.o clock.o collect.o conf.o control.o convtime.o \
- daemon.o deliver.o domain.o envelope.o err.o headers.o macro.o \
- main.o map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- safefile.o savemail.o snprintf.o srvrsmtp.o stab.o stats.o \
- sysexits.o trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ifdef(`confLINKS', `confLINKS',
- `${UBINDIR}/newaliases \
- ${UBINDIR}/mailq \
- ${UBINDIR}/hoststat \
- ${UBINDIR}/purgestat')
-
-NROFF= ifdef(`confNROFF', `confNROFF', `groff -Tascii')
-MANDOC= ifdef(`confMANDOC', `confMANDOC', `-mandoc')
-
-INSTALL=ifdef(`confINSTALL', `confINSTALL', `install')
-BINOWN= ifdef(`confSBINOWN', `confSBINOWN', `root')
-BINGRP= ifdef(`confSBINGRP', `confSBINGRP', `kmem')
-BINMODE=ifdef(`confSBINMODE', `confSBINMODE', `4555')
-
-MANOWN= ifdef(`confMANOWN', `confMANOWN', `bin')
-MANGRP= ifdef(`confMANGRP', `confMANGRP', `bin')
-MANMODE=ifdef(`confMANMODE', `confMANMODE', `444')
-
-MANROOT=${DESTDIR}ifdef(`confMANROOT', `confMANROOT', `/usr/share/man/cat')
-MAN1= ${MANROOT}ifdef(`confMAN1', `confMAN1', `1')
-MAN1EXT=ifdef(`confMAN1EXT', `confMAN1EXT', `1')
-MAN1SRC=ifdef(`confMAN1SRC', `confMAN1SRC', `0')
-MAN5= ${MANROOT}ifdef(`confMAN5', `confMAN5', `5')
-MAN5EXT=ifdef(`confMAN5EXT', `confMAN5EXT', `5')
-MAN5SRC=ifdef(`confMAN5SRC', `confMAN5SRC', `0')
-MAN8= ${MANROOT}ifdef(`confMAN8', `confMAN8', `8')
-MAN8EXT=ifdef(`confMAN8EXT', `confMAN8EXT', `8')
-MAN8SRC=ifdef(`confMAN8SRC', `confMAN8SRC', `0')
-
-ALL= sendmail sendmail.st aliases.${MAN5SRC} mailq.${MAN1SRC} newaliases.${MAN1SRC} sendmail.${MAN8SRC}
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${LIBDIRS} ${OBJS} ${LIBS}
-
-undivert(3)
-
-sendmail.st:
- cp /dev/null sendmail.st
-
-aliases.${MAN5SRC}: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.${MAN5SRC}
-
-mailq.${MAN1SRC}: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.${MAN1SRC}
-
-newaliases.${MAN1SRC}: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.${MAN1SRC}
-
-sendmail.${MAN8SRC}: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.${MAN8SRC}
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf \
- ${HFDIR}/sendmail.hf
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 sendmail.st \
- ${STDIR}/sendmail.st
-
-install-docs: aliases.${MAN5SRC} mailq.${MAN1SRC} newaliases.${MAN1SRC} sendmail.${MAN8SRC}
-ifdef(`confNO_MAN_INSTALL', `dnl',
-` ${INSTALL} -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} sendmail.${MAN8SRC} ${MAN8}/sendmail.${MAN8EXT}
- ${INSTALL} -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} aliases.${MAN5SRC} ${MAN5}/aliases.${MAN5EXT}
- ${INSTALL} -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} mailq.${MAN1SRC} ${MAN1}/mailq.${MAN1EXT}
- ${INSTALL} -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} newaliases.${MAN1SRC} ${MAN1}/newaliases.${MAN1EXT}')
-
-clean:
- rm -f ${OBJS} sendmail aliases.${MAN5SRC} mailq.${MAN1SRC} newaliases.${MAN1SRC} sendmail.${MAN8SRC}
-
-################ Dependency scripts
-include(confBUILDTOOLSDIR/M4/depend/ifdef(`confDEPEND_TYPE', `confDEPEND_TYPE', `generic').m4)dnl
-################ End of dependency scripts
+bldFINISH
diff --git a/contrib/sendmail/src/README b/contrib/sendmail/src/README
index 7106e8c..0f9285c 100644
--- a/contrib/sendmail/src/README
+++ b/contrib/sendmail/src/README
@@ -1,4 +1,5 @@
-# Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+# Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+# All rights reserved.
# Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
# Copyright (c) 1988
# The Regents of the University of California. All rights reserved.
@@ -8,7 +9,7 @@
# the sendmail distribution.
#
#
-# @(#)README 8.211 (Berkeley) 2/2/1999
+# $Id: README,v 8.263.2.1.2.19 2000/07/15 17:35:18 gshapiro Exp $
#
This directory contains the source files for sendmail(TM).
@@ -16,7 +17,7 @@ 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 src directory. It will build an appropriate Makefile, and
+the sendmail directory. It will build an appropriate Makefile, and
create an appropriate obj.* subdirectory so that multiplatform
support works easily.
@@ -70,7 +71,7 @@ confLIBS -l flags for selecting libraries during linking
confLDOPTS other ld(1) linker options
Others can be found by examining Makefile.m4. Please read
-../BuildTools/README for more information about the site.config.m4
+../devtools/README for more information about the site.config.m4
file.
You can recompile from scratch using the -c flag with the Build
@@ -78,7 +79,7 @@ command. This removes the existing compilation directory for the
current platform and builds a new one.
Porting to a new Unix-based system should be a matter of creating
-an appropriate configuration file in the BuildTools/OS/ directory.
+an appropriate configuration file in the devtools/OS/ directory.
@@ -103,7 +104,7 @@ NEWDB The new Berkeley DB package. Some systems (e.g., BSD/OS and
running BSD/386 1.x, you will need to upgrade the included
Berkeley DB library to a current version. NEWDB is included
automatically if the Build script can find a library named
- libdb.a.
+ libdb.a or libdb.so.
NDBM The older NDBM implementation -- the very old V7 DBM
implementation is no longer supported.
NIS Network Information Services. To use this you must have
@@ -114,13 +115,16 @@ HESIOD Support for Hesiod (from the DEC/Athena distribution). You
must already have Hesiod support on your system for this to
work. You may be able to get this to work with the MIT/Athena
version of Hesiod, but that's likely to be a lot of work.
-LDAPMAP Lightweight Directory Lookup Protocol support. You will
- have to install the UMich or OpenLDAP ldap and lber
- libraries to use this flag.
+LDAPMAP Lightweight Directory Access Protocol support. You will
+ have to install the UMich or OpenLDAP
+ (http://www.openldap.org/) ldap and lber libraries to use
+ this flag.
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.
+MAP_NSD nsd map support (IRIX 6.5 and later).
>>> NOTE WELL for NEWDB support: If you want to get ndbm support, for
>>> Berkeley DB versions under 2.0, it is CRITICAL that you remove
@@ -182,7 +186,7 @@ Wherever possible, I try to make sendmail pull in the correct
compilation options needed to compile on various environments based on
automatically defined symbols. Some machines don't seem to have useful
symbols available, requiring that a compilation flag be defined in
-the Makefile; see the Buildtools/OS subdirectory for the supported
+the Makefile; see the devtools/OS subdirectory for the supported
architectures.
If you are a system to which sendmail has already been ported you
@@ -199,6 +203,8 @@ SYS5SIGNALS Use System V signal semantics -- the signal handler
SYS5SETPGRP Use System V setpgrp() semantics. Implied by SYSTEM5.
HASFCHMOD Define this to one if you have the fchmod(2) system call.
This improves security.
+HASFCHOWN Define this to one if you have the fchown(2) system call.
+ This is required for the TrustedUser option.
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
@@ -260,7 +266,10 @@ HASGETDTABLESIZE
Define this if you have the getdtablesize(2) syscall.
HAS_ST_GEN Define this to 1 if your system has the st_gen field in
the stat structure (see stat(2)).
-USESTRERROR Define this if you have the libc strerror function (which
+HASSRANDOMDEV Define this if your system has the srandomdev(3) function
+ call.
+HASURANDOMDEV Define this if your system has /dev/urandom(4).
+HASSTRERROR Define this if you have the libc strerror(3) function (which
should be declared in <errno.h>), and it should be used
instead of sys_errlist.
NEEDGETOPT Define this if you need a reimplementation of getopt(3).
@@ -439,8 +448,17 @@ SIOCGIFNUM_IS_BROKEN
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.
+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.
+-----------------------+
| COMPILE-TIME FEATURES |
@@ -468,6 +486,9 @@ HESIOD Define this to get Hesiod support for aliases and maps.
Normally defined in the Makefile.
NETINFO Define this to get NeXT NetInfo support for aliases and maps.
Normally defined in the Makefile.
+LDAPMAP Define this to get LDAP support for maps.
+PH_MAP Define this to get PH support for maps.
+MAP_NSD Define this to get nsd support for maps.
USERDB Define this to 1 to include support for the User Information
Database. Implied by NEWDB or HESIOD. You can use
-DUSERDB=0 to explicitly turn it off.
@@ -477,7 +498,7 @@ IDENTPROTO Define this as 1 to get IDENT (RFC 1413) protocol support.
implementation. You can define it to be 0 to explicitly
turn off IDENT protocol support. If defined off, the code
is actually still compiled in, but it defaults off; you
- can turn it on by setting the IDENT timeout to 30s in the
+ can turn it on by setting the IDENT timeout in the
configuration file.
IP_SRCROUTE Define this to 1 to get IP source routing information
displayed in the Received: header. This is assumed on
@@ -493,10 +514,16 @@ LOG Set this to get syslog(3) support. Defined by default
in conf.h. You want this if at all possible.
NETINET Set this to get TCP/IP support. Defined by default
in conf.h. You probably want this.
+NETINET6 Set this to get IPv6 support. Other configuration may
+ be needed in conf.h for your particular operating system.
+ Also, DaemonPortOptions must be set appropriately for
+ sendmail to accept IPv6 connections.
NETISO Define this to get ISO networking support.
NETUNIX Define this to get Unix domain networking support. Defined
by default. A few bizarre systems (SCO, ISC, Altos) don't
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
@@ -536,6 +563,37 @@ 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
+ 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
+ SASL library is older than 1.5.10, you have to set this
+ 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
+ compared with the actual version found and if there is a
+ mismatch, compilation will fail.
+EGD Define this if your system has EGD installed, see
+ http://www.lothar.com/tech/crypto/ . It should be used to
+ seed the PRNG for STARTTLS if HASURANDOMDEV is not defined.
+STARTTLS Enables SMTP STARTTLS (RFC 2487). This requires OpenSSL
+ (http://www.OpenSSL.org/) and sfio (see below).
+ 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: you may run into problems if
+ you use sfio2000 (the body of a message is lost). Use
+ sfio1999 instead.
+---------------------+
@@ -570,6 +628,70 @@ wildcard MX records that match your domain. ANYTHING ELSE WILL GIVE
YOU HEADACHES!
++----------------------------------------+
+| STARTTLS COMPILATION AND CONFIGURATION |
++----------------------------------------+
+
+Please read the docs accompanying the OpenSSL library and sfio.
+You have to compile and install both libraries before you can compile
+sendmail. See devtools/README how to set the correct compile time
+parameters; you should at least set the following variables:
+
+define(`confSTDIO_TYPE', `portable')
+APPENDDEF(`confENVDEF', `-DSFIO')
+APPENDDEF(`confLIBS', `-lsfio')
+APPENDDEF(`conf_sendmail_ENVDEF', `-DSTARTTLS')
+APPENDDEF(`conf_sendmail_LIBS', `-lssl -lcrypto')
+
+Configuration information can be found in doc/op/op.me (required
+certificates) and cf/README (how to tell sendmail about certificates).
+
+To perform an initial test, connect to your sendmail daemon
+(telnet localhost 25) and issue a EHLO localhost and see whether
+250-STARTTLS
+is in the response. If it isn't, run the daemon with
+-O LogLevel=14
+and try again. Then take a look at the logfile and see whether
+there are any problems listed about permissions (unsafe files)
+or the validity of X.509 certificates.
+
+Note: sfio must be used in all libraries with which sendmail exchanges
+file pointers. That is, libsmutil must be compiled with sfio, which
+is accomplished by the above config parameters. Another example is
+PH map support. This does not apply to the usual libraries, e.g.,
+OpenSSL, Berkeley DB, Cyrus SASL.
+
+Further information can be found via:
+http://www.sendmail.org/tips/
+
+
++------------------------------------+
+| SASL COMPILATION AND CONFIGURATION |
++------------------------------------+
+
+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.
+
+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
+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).
+
+To perform an initial test, connect to your sendmail daemon
+(telnet localhost 25) and issue a EHLO localhost and see whether
+250-AUTH ....
+is in the response. If it isn't, run the daemon with
+-O LogLevel=14
+and try again. Then take a look at the logfile and see whether
+there are any security related problems listed (unsafe files).
+
+Further information can be found via:
+http://www.sendmail.org/tips/
+
+
+-------------------------------------+
| OPERATING SYSTEM AND COMPILE QUIRKS |
+-------------------------------------+
@@ -609,7 +731,7 @@ Configuration file location
Up to 8.6, sendmail tried to find the sendmail.cf file in the same
place as the vendors had put it, even when this was obviously
stupid. As of 8.7, sendmail ALWAYS looks for /etc/sendmail.cf.
- Beginning with 8.10, sendmail will use /etc/mail/sendmail.cf.
+ Beginning with 8.10, sendmail uses /etc/mail/sendmail.cf.
You can get sendmail to use the stupid vendor .cf location by
adding -DUSE_VENDOR_CF_PATH during compilation, but this may break
support programs and scripts that need to find sendmail.cf. You
@@ -617,6 +739,22 @@ Configuration file location
vendor location rather than changing the location in the sendmail
binary.
+ NETINFO systems use NETINFO to determine the location 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
+ "/etc/mail/sendmail.cf" (without the quotes) to use this new
+ default location for Sendmail 8.10.0 and higher.
+
+ControlSocket permissions
+ Paraphrased from BIND 8.2.1's README:
+
+ Solaris and other pre-4.4BSD kernels do not respect ownership or
+ protections on UNIX-domain sockets. The short term fix for this is to
+ override the default path and put such control sockets into root-
+ 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.
+
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
@@ -647,6 +785,11 @@ SunOS 4.x (Solaris 1.x)
and it should work. This info is thanks to Brian Bartholomew
<bb@math.ufl.edu> of I-Kinetics, Inc.
+ NOTE: The SunOS 4.X linker uses library paths specified during
+ compilation using -L for run-time shared library searches.
+ Therefore, it is vital that relative and unsafe directory paths not
+ be used when compiling sendmail.
+
SunOS 4.0.2 (Sun 386i)
Date: Fri, 25 Aug 1995 11:13:58 +0200 (MET DST)
From: teus@oce.nl
@@ -656,7 +799,7 @@ SunOS 4.0.2 (Sun 386i)
* Don't use /usr/5bin in your PATH, but make /usr/5bin/uname
available as "uname" command.
* Use the defines "-DBSD4_3 -DNAMED_BIND=0" in
- BuildTools/OS/SunOS.4.0, which is selected via the "uname" command.
+ devtools/OS/SunOS.4.0, which is selected via the "uname" command.
I recommend to make available the db-library on the system first
(and change the Makefile to use this library).
Note that the sendmail.cf and aliases files are found in /etc.
@@ -688,44 +831,6 @@ Solaris 2.x (SunOS 5.x)
make sure /opt/SUNWspro/bin/cc is used instead of /usr/ucb/cc
(or it might complain about tm_zone).
- To the best of my knowledge, Solaris does not have the
- gethostbyname problem described above. However, it does
- have another one:
-
- From a correspondent:
-
- For solaris 2.2, I have
-
- hosts: files dns
-
- in /etc/nsswitch.conf and /etc/hosts has to have the fully
- qualified host name. I think "files" has to be before "dns"
- in /etc/nsswitch.conf during bootup.
-
- From another correspondent:
-
- When running sendmail under Solaris, the gethostbyname()
- hack in conf.c which should perform proper canonicalization
- of host names could fail. Result: the host name is not
- canonicalized despite the hack, and you'll have to define $j
- and $m in sendmail.cf somewhere.
-
- The reason could be that /etc/nsswitch.conf is improperly
- configured (at least from sendmail's point of view). For
- example, the line
-
- hosts: files nisplus dns
-
- will make gethostbyname() look in /etc/hosts first, then ask
- nisplus, then dns. However, if /etc/hosts does not contain
- the full canonicalized hostname, then no amount of
- gethostbyname()s will work.
-
- Solution (or rather, a workaround): Ask nisplus first, then
- dns, then local files:
-
- hosts: nisplus dns [NOTFOUND=return] files
-
The Solaris "syslog" function is apparently limited to something
about 90 characters because of a kernel limitation. If you have
source code, you can probably up this number. You can get patches
@@ -777,12 +882,6 @@ Solaris 2.4 (SunOS 5.4)
>>
>> here, path 2 would be the first used.
-Solaris 2.6 (SunOS 5.6)
- If you built sendmail 8.8.1 through 8.8.4 inclusive on a Solaris 2.5
- system, that binary will not run on Solaris 2.6, due to problems with
- incompatible snprintf(3s) calls. This problem is fixed in sendmail
- 8.8.5.
-
Solaris 2.5.1 (SunOS 5.5.1) and 2.6 (SunOS 5.6)
Apparently Solaris 2.5.1 patch 103663-01 installs a new
/usr/include/resolv.h file that defines the __P macro without
@@ -803,19 +902,43 @@ Solaris 2.5.1 (SunOS 5.5.1) and 2.6 (SunOS 5.6)
Sun is aware of the problem (Sun bug ID 4081053) and it will be fixed
in Solaris 2.7.
+Solaris 7 (SunOS 5.7)
+ Solaris 7 includes LDAP libraries but the implementation was
+ lacking a few things. The following settings can be placed in
+ devtools/Site/site.SunOS.5.7.m4 if you plan on using those
+ libraries.
+
+ APPENDDEF(`confMAPDEF', `-DLDAPMAP')
+ APPENDDEF(`confENVDEF', `-DLDAP_VERSION_MAX=3')
+ APPENDDEF(`confLIBS', `-lldap')
+
+ Also, Sun's patch 107555 is needed to prevent a crash in the call
+ to ldap_set_option for LDAP_OPT_REFERRALS in ldapmap_setopts if
+ LDAP support is compiled in sendmail.
+
Ultrix
By default, the IDENT protocol is turned off on Ultrix. If you
are running Ultrix 4.4 or later, or if you have included patch
CXO-8919 for Ultrix 4.2 or 4.3 to fix the TCP problem, you can turn
- IDENT on in the configuration file by setting the "ident" timeout
- to 30 seconds.
+ IDENT on in the configuration file by setting the "ident" timeout.
+
+ The Ultrix 4.5 Y2K patch (ULTV45-022-1) has changed the resolver
+ included in libc.a. Unfortunately, the __RES symbol hasn't changed
+ and therefore, sendmail can no longer automatically detect the
+ newer version. If you get a compiler error:
+
+ /lib/libc.a(gethostent.o): local_hostname_length: multiply defined
+
+ Then rebuild with this in devtools/Site/site.ULTRIX.m4:
+
+ APPENDDEF(`conf_sendmail_ENVDEF', `-DNEEDLOCAL_HOSTNAME_LENGTH=0')
Digital UNIX (formerly DEC OSF/1)
If you are compiling on OSF/1 (DEC Alpha), you must use
-L/usr/shlib (otherwise it core dumps on startup). You may also
need -mld to get the nlist() function, although some versions
apparently don't need this.
-
+
Also, the enclosed makefile removed /usr/sbin/smtpd; if you need
it, just create the link to the sendmail binary.
@@ -886,13 +1009,13 @@ IRIX
http://reality.sgi.com/ariel/freeware/#db .
IRIX 6.x
- It is important that on IRIX 6.x you give used ABI in command
- line of Build, otherwise configuration script does not work
- correctly, e.g.,
-
- sh Build -E ABI=-n32
+ If you are using XFS filesystem, avoid using the -32 ABI switch to
+ the cc compiler if possible.
- If you are using XFS filesystem, avoid using ABI=-32 if possible.
+IRIX 6.4
+ The IRIX 6.5.4 version of /bin/m4 does not work properly with
+ sendmail. Either install fw_m4.sw.m4 off the Freeware_May99 CD and
+ use /usr/freeware/bin/m4 or install and use GNU m4.
NeXT or NEXTSTEP
NEXTSTEP 3.3 and earlier ship with the old DBM library. Also,
@@ -904,7 +1027,7 @@ NeXT or NEXTSTEP
#include <sys/dir.h>
#define dirent direct
- (BuildTools/OS/NeXT should try to do both of these for you.)
+ (devtools/OS/NeXT should try to do both of these for you.)
Apparently, there is a bug in getservbyname on Nextstep 3.0
that causes it to fail under some circumstances with the
@@ -915,8 +1038,6 @@ NeXT or NEXTSTEP
in your .cf file.
- You may have to use -DNeXT.
-
BSDI (BSD/386) 1.0, NetBSD 0.9, FreeBSD 1.0
The "m4" from BSDI won't handle the config files properly.
I haven't had a chance to test this myself.
@@ -926,18 +1047,18 @@ BSDI (BSD/386) 1.0, NetBSD 0.9, FreeBSD 1.0
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 BuildTools/OS/FreeBSD). NetBSD-current may have
+ 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
scheme than the version that is supplied with your release. This
means you will be able to use the current version of Berkeley DB
with sendmail as long you use the new db.h when compiling
- sendmail and link it against the new libdb.a. You should probably
- keep the original db.h in /usr/include and the new db.h in
- /usr/local/include.
+ sendmail and link it against the new libdb.a or libdb.so. You
+ should probably keep the original db.h in /usr/include and the
+ new db.h in /usr/local/include.
4.3BSD
If you are running a "virgin" version of 4.3BSD, you'll have
@@ -949,7 +1070,7 @@ BSDI (BSD/386) 1.0, NetBSD 0.9, FreeBSD 1.0
determined to continue to use your old, buggy version (or as
a shortcut to get sendmail working -- I'm sure you have the
best intentions to port a modern version of BIND), you can
- copy ../contrib/oldbind.compat.c into src and add
+ copy ../contrib/oldbind.compat.c into sendmail and add
oldbind.compat.o to OBJADD in the Makefile.
A/UX
@@ -994,7 +1115,7 @@ SCO Unix
/etc/named.boot.
- sigh -
- According to SCO, the m4 which ships with UnixWare 2.1.2 is broken.
+ According to SCO, the m4 which ships with UnixWare 2.1.2 is broken.
We recommend installing GNU m4 before attempting to build sendmail.
DG/UX
@@ -1016,7 +1137,7 @@ Apollo DomainOS
#include <sys/dir.h>
#define dirent direct
- (BuildTools/OS/DomainOS will attempt to do both of these for you.)
+ (devtools/OS/DomainOS will attempt to do both of these for you.)
HP-UX 8.00
Date: Mon, 24 Jan 1994 13:25:45 +0200
@@ -1084,6 +1205,46 @@ 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.
+
+AIX 4.X
+ The AIX 4.X linker uses library paths specified during compilation
+ using -L for run-time shared library searches. Therefore, it is
+ vital that relative and unsafe directory paths not be using when
+ compiling sendmail. Because of this danger, by default, compiles
+ on AIX use the -blibpath option to limit shared libraries to
+ /usr/lib and /lib. If you need to allow more directories, such as
+ /usr/local/lib, modify your devtools/Site/site.AIX.4.2.m4,
+ site.AIX.4.3.m4, and/or site.AIX.4.x.m4 file(s) and set confLDOPTS
+ approriately. For example:
+
+ define(`confLDOPTS', `-blibpath:/usr/lib:/lib:/usr/local/lib')
+
+ Be sure to only add (safe) system directories.
+
+ The AIX version of GNU ld also exhibits this problem. If you are
+ using that version, instead of -blibpath, use its -rpath option.
+ For example:
+
+ gcc -Wl,-rpath /usr/lib -Wl,-rpath /lib -Wl,-rpath /usr/local/lib
+
+AIX 4.3.3
+ From: Valdis.Kletnieks@vt.edu
+ Date: Sun, 02 Jul 2000 03:58:02 -0400
+
+ Under AIX 4.3.3, after applying bos.adt.include 4.3.3.12 to close the
+ BIND 8.2.2 security holes, you can no lonber build with -DNETINET6
+ because they changed the value of __RES in resolv.h but failed to
+ actually provide the API changes that the change implied.
+
+ Workarounds:
+ 1) Compile without -DNETINET6
+ 2) Build against a real Bind 8.2.2 include/lib tree
+ 3) Wait for IBM to fix it
+
AIX 4.2
The AIX m4 implements a different mechanism for ifdef which is
inconsistent with other versions of m4. Therefore, it will not
@@ -1113,7 +1274,7 @@ 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.
@@ -1140,28 +1301,24 @@ AIX 2.2.1
that will return that information. The 'LA_SUBR' define
will handle this for AIX V2 on the RT.
- Note: You will have to change BuildTools/OS/AIX.2 to correctly
+ 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 BuildTools/OS/AIX.2 to know
+ 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.
-
- Manual pages will format correctly if given the mandoc macros
- and used with nroff. I have not tried groff.
-
RISC/os
RISC/os from MIPS is a merged AT&T/Berkeley system. When you
compile on that platform you will get duplicate definitions
on many files. You can ignore these.
System V Release 4 Based Systems
- There is a single BuildTools OS that is intended for all SVR4-based
- systems (built from BuildTools/OS/SVR4). It defines __svr4__,
+ There is a single devtools OS that is intended for all SVR4-based
+ systems (built from devtools/OS/SVR4). It defines __svr4__,
which is predefined by some compilers. If your compiler already
defines this compile variable, you can delete the definition from
- the generated Makefile or create a BuildTools/Site/site.config.m4
+ the generated Makefile or create a devtools/Site/site.config.m4
file.
It's been tested on Dell Issue 2.2.
@@ -1234,11 +1391,11 @@ UnixWare
According to Larry Rosenman <ler@lerami.lerctr.org>:
UnixWare 2.1.[23]'s m4 chokes (not obviously) when
- processing the 8.9.0 cf files.
-
+ processing the 8.9.0 cf files.
+
I had a LOCAL_RULE_0 that wound up AFTER the
SBasic_check_rcpt rules using the SCO supplied M4.
- GNU M4 works fine.
+ GNU M4 works fine.
UNICOS 8.0.3.4
Some people have reported that the -O flag on UNICOS can cause
@@ -1262,6 +1419,20 @@ BIND 4.9.2 and Ultrix
during the link stage.
+BIND 8.X
+ BIND 8.X returns HOST_NOT_FOUND instead of TRY_AGAIN on temporary
+ DNS failures when trying to find the hostname associated with an IP
+ address (gethostbyaddr()). This can cause problems as
+ $&{client_name} based lookups in class R ($=R) and the access
+ database won't succeed.
+
+ This will be fixed in BIND 8.2.1. For earlier versions, this can
+ be fixed by making "dns" the last name service queried for host
+ resolution in /etc/irs.conf:
+
+ hosts local continue
+ hosts dns
+
strtoul
Some compilers (notably gcc) claim to be ANSI C but do not
include the ANSI-required routine "strtoul". If your compiler
@@ -1290,39 +1461,16 @@ Listproc 6.0c
cause it to use "HELO hostname" (which Z-mail apparently requires
as well. :)
-LDAP
- LDAP was provided by Booker Bense <bbense+ldap@stanford.edu> of
- Stanford University. From Booker:
-
- - The patch attached to this message implements an Ldap map class.
- Currently we are using this at stanford to support campus-wide
- email addressing. More information can be found at
- http://www.stanford.edu/~bbense/Inst.html.
-
- - Currently we are using the ldap map as follows:
-
- Kluser ldapx
- -h"localhost borax.stanford.edu borate.stanford.edu boron.stanford.edu"
- -k"mailacceptinggeneralid=%s" -v maildrop
+OpenSSL
+ OpenSSL versions prior to 0.9.6 use a macro named Free which
+ conflicts with existing macro names on some platforms, such as
+ AIX.
- and in Rule set S5
-
- # Now attempt to lookup in luser (ldap map)
- R< $L > $+ $: < $L > $( luser $1 $)
- R< $* > $+ @ $+ $: < $3 > $2 Rewrite if forward
-
- - The map definition supports most of the standard Map args plus most
- of the command line options of ldapsearch. The software is currently
- limited to only accepting the first entry returned. It expects that
- the map defines an ldap filter that returns at most 1 valid entry.
- It requires the ldap and lber libraries from the Umich Ldap3.2
- release.
-
- The software has been in production on Solaris.2.5.1 at Stanford
- for over 2 years.
-
- The LDAP map supports both the UMich LDAP 3.2 and 3.3 libraries as
- well as the OpenLDAP (http://www.openldap.org/) libraries.
+PH
+ PH support is provided by Mark Roth <roth@uiuc.edu>. The map is
+ described at http://www-wsg.cso.uiuc.edu/sendmail/patches/ .
+ Please contact Mark Roth for support and questions regarding the
+ map.
TCP Wrappers
If you are using -DTCPWRAPPERS to get TCP Wrappers support you will
@@ -1331,9 +1479,7 @@ TCP Wrappers
(make sure that INCDIRS and LIBDIRS point to where the tcpd.h and
libwrap.a can be found).
- TCP Wrappers is available on ftp.win.tue.nl in /pub/security;
- grab tcp_wrappers_<VER>.tar.gz (where <VER> is the highest
- numbered version).
+ TCP Wrappers is available at ftp://ftp.porcupine.org/pub/security/.
If you have alternate MX sites for your site, be sure that all of
your MX sites reject the same set of hosts. If not, a bad guy whom
@@ -1347,28 +1493,24 @@ Regular Expressions (MAP_REGEX)
or sendmail gives an error about a regular expression with:
- pattern-compile-error: : Operation not applicable
+ pattern-compile-error: : Operation not applicable
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.
- You can also use the regex-lib by Henry Spencer,
- ftp://ftp.funet.fi/pub/languages/C/spencer/regex.shar.gz
- Make sure, your compiler reads regex.h from the distribution,
- not from /usr/include, otherwise sendmail will dump a core.
+ 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.
+ You can also use the regex-lib by Henry Spencer,
+ ftp://ftp.funet.fi/pub/languages/C/spencer/regex.shar.gz
+ Make sure, your compiler reads regex.h from the distribution,
+ not from /usr/include, otherwise sendmail will dump a core.
+--------------+
| MANUAL PAGES |
+--------------+
-The manual pages have been written against the -mandoc macros
-instead of the -man macros. The latest version of groff has them
-included. You can also get a copy from FTP.UU.NET in the directory
-/systems/unix/bsd-sources/share/tmac. groff is available from
-ftp.gnu.org in the /pub/gnu directory.
-
+The manual pages have been written against the -man macros, and
+should format correctly with any reasonable *roff.
+-----------------+
| DEBUGGING HOOKS |
@@ -1402,13 +1544,22 @@ A typical formulation of ruleset 89 would be:
The following list describes the files in this directory:
+Build Shell script for building sendmail.
+Makefile A convenience for calling ./Build.
Makefile.m4 A template for constructing a makefile based on the
- information in the BuildTools directory.
+ information in the devtools directory.
README This file.
TRACEFLAGS My own personal list of the trace flags -- not guaranteed
to be particularly up to date.
alias.c Does name aliasing in all forms.
+aliases.5 Man page describing the format of the aliases file.
arpadate.c A subroutine which creates ARPANET standard dates.
+bf.h Buffered file I/O function declarations.
+bf_portable.c Stub routines for systems lacking the Torek stdio library.
+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.
collect.c The routine that actually reads the mail into a temp
@@ -1425,40 +1576,47 @@ daemon.c Routines to implement daemon mode. This version is
deliver.c Routines to deliver mail.
domain.c Routines that interface with DNS (the Domain Name
System).
-err.c Routines to print error messages.
envelope.c Routines to manipulate the envelope structure.
+err.c Routines to print error messages.
headers.c Routines to process message headers.
+helpfile An example helpfile for the SMTP HELP command and -bt mode.
macro.c The macro expander. This is used internally to
insert information from the configuration file.
+mailq.1 Man page for the mailq command.
main.c The main routine to sendmail. This file also
contains some miscellaneous routines.
+makesendmail A convenience for calling ./Build.
map.c Support for database maps.
mci.c Routines that handle mail connection information caching.
+milter.c MTA portions of the mail filter API.
mime.c MIME conversion routines.
+newaliases.1 Man page for the newaliases command.
parseaddr.c The routines which do address parsing.
queue.c Routines to implement message queueing.
readcf.c The routine that reads the configuration file and
translates it to internal form.
recipient.c Routines that manipulate the recipient list.
-safefile.c Routines to do careful checking of file modes and permissions
- when opening or creating files.
savemail.c Routines which save the letter on processing errors.
+sendmail.8 Man page for the sendmail command.
sendmail.h Main header file for sendmail.
-snprintf.c Routines to manipulate strings but prevent buffer overflows.
+sfsasl.c I/O interface between SASL/TLS and the MTA using SFIO.
+sfsasl.h Header file for sfsasl.c.
+shmticklib.c Routines for shared memory counters.
srvrsmtp.c Routines to implement server SMTP.
stab.c Routines to manage the symbol table.
stats.c Routines to collect and post the statistics.
+statusd_shm.h Data structure and function declarations for shmticklib.c.
sysexits.c List of error messages associated with error codes
in sysexits.h.
+sysexits.h List of error codes for systems that lack their own.
+timers.c Routines to provide microtimers.
+timers.h Data structure and function declarations for timers.h.
trace.c The trace package. These routines allow setting and
testing of trace flags with a high granularity.
udb.c The user database interface module.
usersmtp.c Routines to implement user SMTP.
util.c Some general purpose routines used by sendmail.
version.c The version number and information about this
- version of sendmail. Theoretically, this gets
- modified on every change.
-
-Eric Allman
+ version of sendmail.
-(Version 8.211, last update 2/2/1999 15:28:18)
+(Version $Revision: 8.263.2.1.2.19 $, last update $Date: 2000/07/15 17:35:18 $ )
diff --git a/contrib/sendmail/src/TRACEFLAGS b/contrib/sendmail/src/TRACEFLAGS
index 04b9b3c..b48e75d 100644
--- a/contrib/sendmail/src/TRACEFLAGS
+++ b/contrib/sendmail/src/TRACEFLAGS
@@ -1,4 +1,4 @@
-# @(#)TRACEFLAGS 8.21 (Berkeley) 4/27/1998
+# $Id: TRACEFLAGS,v 8.29 1999/11/04 23:31:02 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
@@ -23,7 +23,7 @@
16 daemon.c makeconnection
17 deliver.c hostsignature
17 domain.c mxrand
-18 usersmtp.c reply, smtpmessage, smtpinit, smtpmailfrom
+18 usersmtp.c reply, smtpmessage, smtpinit, smtpmailfrom, smtpdata
19 srvrsmtp.c smtp
20 parseaddr.c parseaddr
21 parseaddr.c rewrite
@@ -47,7 +47,7 @@
35 macro.c expand, define
36 stab.c stab
37 readcf.c (many)
-38 map.c initmaps
+38 map.c initmaps, setupmaps (bogus map)
39 map.c map_rewrite
40 queue.c queueup, orderq, dowork
41 queue.c orderq
@@ -69,11 +69,17 @@
55 conf.c lockfile
56 mci.c persistent host status
57 util.c snprintf
+58 bf.c bf* routines
60 map.c
61 conf.c sm_gethostbyname
62 multiple file descriptor checking
+63 queue.c runqueue process watching
+64 multiple Milter
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)
+95 srvrsmtp.c AUTH
+95 usersmtp.c AUTH
+98 * timers
99 main.c avoid backgrounding (no printed output)
diff --git a/contrib/sendmail/src/alias.c b/contrib/sendmail/src/alias.c
index a7149f2..4d3c4a6 100644
--- a/contrib/sendmail/src/alias.c
+++ b/contrib/sendmail/src/alias.c
@@ -1,24 +1,29 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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) 1988, 1993
- * The Regents of the University of California. All rights reserved.
*/
-# include "sendmail.h"
+#include <sendmail.h>
#ifndef lint
-static char sccsid[] = "@(#)alias.c 8.96 (Berkeley) 12/18/1998";
-#endif /* not lint */
+static char id[] = "@(#)$Id: alias.c,v 8.142.4.1 2000/05/25 18:56:12 gshapiro Exp $";
+#endif /* ! lint */
+
+# define SEPARATOR ':'
+# define ALIAS_SPEC_SEPARATORS " ,/:"
+
+static MAP *AliasFileMap = NULL; /* the actual aliases.files map */
+static int NAliasFileMaps; /* the number of entries in AliasFileMap */
+static char *aliaslookup __P((char *, int *));
-MAP *AliasFileMap = NULL; /* the actual aliases.files map */
-int NAliasFileMaps; /* the number of entries in AliasFileMap */
/*
** ALIAS -- Compute aliases.
**
@@ -53,15 +58,14 @@ alias(a, sendq, aliaslevel, e)
{
register char *p;
char *owner;
- auto int stat = EX_OK;
+ auto int status = EX_OK;
char obuf[MAXNAME + 7];
- extern char *aliaslookup __P((char *, int *, ENVELOPE *));
if (tTd(27, 1))
- printf("alias(%s)\n", a->q_user);
+ dprintf("alias(%s)\n", a->q_user);
/* don't realias already aliased names */
- if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags))
+ if (!QS_IS_OK(a->q_state))
return;
if (NoAlias)
@@ -77,10 +81,33 @@ alias(a, sendq, aliaslevel, e)
** bounce messages inappropriately.
*/
- p = aliaslookup(a->q_user, &stat, e);
- if (stat == EX_TEMPFAIL || stat == EX_UNAVAILABLE)
+
+#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')
{
- a->q_flags |= QQUEUEUP;
+ /* Look for owner of alias */
+ (void) strlcpy(obuf, "owner-", sizeof obuf);
+ (void) strlcat(obuf, a->q_user, sizeof obuf);
+ if (aliaslookup(obuf, &status) != NULL)
+ {
+ if (LogLevel > 8)
+ syslog(LOG_WARNING,
+ "possible spam from <> to list: %s, redirected to %s\n",
+ a->q_user, obuf);
+ a->q_user = newstr(obuf);
+ }
+ }
+#endif /* _FFR_REDIRECTEMPTY */
+
+ p = aliaslookup(a->q_user, &status);
+ if (status == EX_TEMPFAIL || status == EX_UNAVAILABLE)
+ {
+ a->q_state = QS_QUEUEUP;
if (e->e_message == NULL)
e->e_message = newstr("alias database unavailable");
return;
@@ -94,40 +121,52 @@ alias(a, sendq, aliaslevel, e)
*/
if (tTd(27, 1))
- printf("%s (%s, %s) aliased to %s\n",
- a->q_paddr, a->q_host, a->q_user, p);
+ 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_flags |= QVERIFIED;
+ a->q_state = QS_VERIFIED;
return;
}
message("aliased to %s", shortenstring(p, MAXSHORTSTR));
- if (LogLevel > 9)
+ if (LogLevel > 10)
sm_syslog(LOG_INFO, e->e_id,
"alias %.100s => %s",
a->q_paddr, shortenstring(p, MAXSHORTSTR));
a->q_flags &= ~QSELFREF;
if (tTd(27, 5))
{
- printf("alias: QDONTSEND ");
+ dprintf("alias: QS_EXPANDED ");
printaddr(a, FALSE);
}
- a->q_flags |= QDONTSEND;
+ a->q_state = QS_EXPANDED;
+
+ /*
+ ** Always deliver aliased items as the default user.
+ ** Setting q_gid to 0 forces deliver() to use DefUser
+ ** instead of the alias name for the call to initgroups().
+ */
+
+ a->q_uid = DefUid;
+ a->q_gid = 0;
+ a->q_fullname = NULL;
+ a->q_flags |= QGOODUID|QALIAS;
+
(void) sendtolist(p, a, sendq, aliaslevel + 1, e);
- if (bitset(QSELFREF, a->q_flags))
- a->q_flags &= ~QDONTSEND;
+ if (bitset(QSELFREF, a->q_flags) && QS_IS_EXPANDED(a->q_state))
+ a->q_state = QS_OK;
/*
** Look for owner of alias
*/
- (void) strcpy(obuf, "owner-");
+ (void) strlcpy(obuf, "owner-", sizeof obuf);
if (strncmp(a->q_user, "owner-", 6) == 0 ||
strlen(a->q_user) > (SIZE_T) sizeof obuf - 7)
- (void) strcat(obuf, "owner");
+ (void) strlcat(obuf, "owner", sizeof obuf);
else
- (void) strcat(obuf, a->q_user);
- owner = aliaslookup(obuf, &stat, e);
+ (void) strlcat(obuf, a->q_user, sizeof obuf);
+ owner = aliaslookup(obuf, &status);
if (owner == NULL)
return;
@@ -149,7 +188,6 @@ alias(a, sendq, aliaslevel, e)
** Parameters:
** name -- the name to look up.
** pstat -- a pointer to a place to put the status.
-** e -- the current envelope.
**
** Returns:
** the value of name.
@@ -162,11 +200,10 @@ alias(a, sendq, aliaslevel, e)
** The return value will be trashed across calls.
*/
-char *
-aliaslookup(name, pstat, e)
+static char *
+aliaslookup(name, pstat)
char *name;
int *pstat;
- ENVELOPE *e;
{
static MAP *map = NULL;
@@ -178,8 +215,7 @@ aliaslookup(name, pstat, e)
return NULL;
map = &s->s_map;
}
- if (!bitset(MF_OPEN, map->map_mflags))
- return NULL;
+ DYNOPENMAP(map);
/* special case POstMastER -- always use lower case */
if (strcasecmp(name, "postmaster") == 0)
@@ -209,7 +245,7 @@ setalias(spec)
STAB *s;
if (tTd(27, 8))
- printf("setalias(%s)\n", spec);
+ dprintf("setalias(%s)\n", spec);
for (p = spec; p != NULL; )
{
@@ -229,7 +265,8 @@ setalias(spec)
}
if (AliasFileMap == NULL)
{
- strcpy(buf, "aliases.files sequence");
+ (void) strlcpy(buf, "aliases.files sequence",
+ sizeof buf);
AliasFileMap = makemapentry(buf);
if (AliasFileMap == NULL)
{
@@ -240,11 +277,10 @@ setalias(spec)
(void) snprintf(buf, sizeof buf, "Alias%d", NAliasFileMaps);
s = stab(buf, ST_MAP, ST_ENTER);
map = &s->s_map;
- bzero(map, sizeof *map);
+ memset(map, '\0', sizeof *map);
map->map_mname = s->s_name;
-
- p = strpbrk(p, " ,/:");
- if (p != NULL && *p == ':')
+ p = strpbrk(p,ALIAS_SPEC_SEPARATORS);
+ if (p != NULL && *p == SEPARATOR)
{
/* map name */
*p++ = '\0';
@@ -259,12 +295,32 @@ setalias(spec)
/* find end of spec */
if (p != NULL)
- p = strchr(p, ',');
+ {
+ bool quoted = FALSE;
+
+ for (; *p != '\0'; p++)
+ {
+ /*
+ ** Don't break into a quoted string.
+ ** Needed for ldap maps which use
+ ** commas in their specifications.
+ */
+
+ if (*p == '"')
+ quoted = !quoted;
+ else if (*p == ',' && !quoted)
+ break;
+ }
+
+ /* No more alias specifications follow */
+ if (*p == '\0')
+ p = NULL;
+ }
if (p != NULL)
*p++ = '\0';
if (tTd(27, 20))
- printf(" map %s:%s %s\n", class, s->s_name, spec);
+ dprintf(" map %s:%s %s\n", class, s->s_name, spec);
/* look up class */
s = stab(class, ST_MAPCLASS, ST_FIND);
@@ -310,7 +366,7 @@ bool
aliaswait(map, ext, isopen)
MAP *map;
char *ext;
- int isopen;
+ bool isopen;
{
bool attimeout = FALSE;
time_t mtime;
@@ -318,7 +374,7 @@ aliaswait(map, ext, isopen)
char buf[MAXNAME + 1];
if (tTd(27, 3))
- printf("aliaswait(%s:%s)\n",
+ dprintf("aliaswait(%s:%s)\n",
map->map_class->map_cname, map->map_file);
if (bitset(MF_ALIASWAIT, map->map_mflags))
return isopen;
@@ -346,12 +402,12 @@ aliaswait(map, ext, isopen)
*/
if (tTd(27, 2))
- printf("aliaswait: sleeping for %d seconds\n",
+ dprintf("aliaswait: sleeping for %u seconds\n",
sleeptime);
map->map_class->map_close(map);
map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
- sleep(sleeptime);
+ (void) sleep(sleeptime);
sleeptime *= 2;
if (sleeptime > 60)
sleeptime = 60;
@@ -363,14 +419,14 @@ aliaswait(map, ext, isopen)
if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
{
if (tTd(27, 3))
- printf("aliaswait: not rebuildable\n");
+ dprintf("aliaswait: not rebuildable\n");
map->map_mflags &= ~MF_ALIASWAIT;
return isopen;
}
if (stat(map->map_file, &stb) < 0)
{
if (tTd(27, 3))
- printf("aliaswait: no source file\n");
+ dprintf("aliaswait: no source file\n");
map->map_mflags &= ~MF_ALIASWAIT;
return isopen;
}
@@ -379,8 +435,10 @@ aliaswait(map, ext, isopen)
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 &&
+ if (AutoRebuild &&
+ stb.st_ino != 0 &&
(stb.st_uid == geteuid() ||
(geteuid() == 0 && stb.st_uid == TrustedUid)))
{
@@ -406,6 +464,13 @@ aliaswait(map, ext, isopen)
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);
+ message("Warning: alias database %s out of date", buf);
+#endif /* !_FFR_REMOVE_AUTOREBUILD */
}
map->map_mflags &= ~MF_ALIASWAIT;
return isopen;
@@ -433,20 +498,20 @@ rebuildaliases(map, automatic)
FILE *af;
bool nolock = FALSE;
bool success = FALSE;
- int sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK;
+ long sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK;
sigfunc_t oldsigint, oldsigquit;
#ifdef SIGTSTP
sigfunc_t oldsigtstp;
-#endif
+#endif /* SIGTSTP */
if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
return FALSE;
- if (!bitset(DBS_LINKEDALIASFILEINWRITABLEDIR, DontBlameSendmail))
+ if (!bitnset(DBS_LINKEDALIASFILEINWRITABLEDIR, DontBlameSendmail))
sff |= SFF_NOWLINK;
- if (!bitset(DBS_GROUPWRITABLEALIASFILE, DontBlameSendmail))
+ if (!bitnset(DBS_GROUPWRITABLEALIASFILE, DontBlameSendmail))
sff |= SFF_NOGWFILES;
- if (!bitset(DBS_WORLDWRITABLEALIASFILE, DontBlameSendmail))
+ if (!bitnset(DBS_WORLDWRITABLEALIASFILE, DontBlameSendmail))
sff |= SFF_NOWWFILES;
/* try to lock the source file */
@@ -460,7 +525,7 @@ rebuildaliases(map, automatic)
int saveerr = errno;
if (tTd(27, 1))
- printf("Can't open %s: %s\n",
+ dprintf("Can't open %s: %s\n",
map->map_file, errstring(saveerr));
if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags))
message("newaliases: cannot open %s: %s",
@@ -489,7 +554,7 @@ rebuildaliases(map, automatic)
(void) lockfile(fileno(af), map->map_file, NULL,
LOCK_EX);
}
- (void) xfclose(af, "rebuildaliases1", map->map_file);
+ (void) fclose(af);
errno = 0;
return FALSE;
}
@@ -498,7 +563,7 @@ rebuildaliases(map, automatic)
oldsigquit = setsignal(SIGQUIT, SIG_IGN);
#ifdef SIGTSTP
oldsigtstp = setsignal(SIGTSTP, SIG_IGN);
-#endif
+#endif /* SIGTSTP */
if (map->map_class->map_open(map, O_RDWR))
{
@@ -517,7 +582,7 @@ rebuildaliases(map, automatic)
else
{
if (tTd(27, 1))
- printf("Can't create database for %s: %s\n",
+ dprintf("Can't create database for %s: %s\n",
map->map_file, errstring(errno));
if (!automatic)
syserr("Cannot create database for alias file %s",
@@ -525,7 +590,7 @@ rebuildaliases(map, automatic)
}
/* close the file, thus releasing locks */
- xfclose(af, "rebuildaliases2", map->map_file);
+ (void) fclose(af);
/* add distinguished entries and close the database */
if (bitset(MF_OPEN, map->map_mflags))
@@ -537,9 +602,9 @@ rebuildaliases(map, automatic)
/* restore the old signals */
(void) setsignal(SIGINT, oldsigint);
(void) setsignal(SIGQUIT, oldsigquit);
-#ifdef SIGTSTP
+# ifdef SIGTSTP
(void) setsignal(SIGTSTP, oldsigtstp);
-#endif
+# endif /* SIGTSTP */
return success;
}
/*
@@ -551,7 +616,7 @@ rebuildaliases(map, automatic)
** Parameters:
** map -- the alias database descriptor.
** af -- file to read the aliases from.
-** announcestats -- anounce statistics regarding number of
+** announcestats -- announce statistics regarding number of
** aliases, longest alias, etc.
** logstats -- lot the same info.
**
@@ -585,14 +650,13 @@ readaliases(map, af, announcestats, logstats)
LineNumber = 0;
naliases = bytes = longest = 0;
skipping = FALSE;
- while (fgets(line, sizeof (line), af) != NULL)
+ while (fgets(line, sizeof line, af) != NULL)
{
int lhssize, rhssize;
int c;
LineNumber++;
p = strchr(line, '\n');
-#if _FFR_BACKSLASH_IN_ALIASES
while (p != NULL && p > line && p[-1] == '\\')
{
p--;
@@ -601,12 +665,12 @@ readaliases(map, af, announcestats, logstats)
LineNumber++;
p = strchr(p, '\n');
}
-#endif
if (p != NULL)
*p = '\0';
else if (!feof(af))
{
- syserr("554 alias line too long");
+ errno = 0;
+ syserr("554 5.3.0 alias line too long");
/* flush to end of line */
while ((c = getc(af)) != EOF && c != '\n')
@@ -626,7 +690,7 @@ readaliases(map, af, announcestats, logstats)
case ' ':
case '\t':
if (!skipping)
- syserr("554 Non-continuation line starts with space");
+ syserr("554 5.3.5 Non-continuation line starts with space");
skipping = TRUE;
continue;
}
@@ -645,12 +709,12 @@ readaliases(map, af, announcestats, logstats)
continue;
if (*p++ != ':')
{
- syserr("554 missing colon");
+ syserr("554 5.3.5 missing colon");
continue;
}
if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv) == NULL)
{
- syserr("554 %.40s... illegal alias name", line);
+ syserr("554 5.3.5 %.40s... illegal alias name", line);
continue;
}
@@ -685,7 +749,7 @@ readaliases(map, af, announcestats, logstats)
break;
if (parseaddr(p, &bl, RF_COPYNONE, ',',
&delimptr, CurEnv) == NULL)
- usrerr("553 %s... bad address", p);
+ usrerr("553 5.3.5 %s... bad address", p);
p = delimptr;
}
}
@@ -709,7 +773,7 @@ readaliases(map, af, announcestats, logstats)
/* check for line overflow */
if (strchr(p, '\n') == NULL && !feof(af))
{
- usrerr("554 alias too long");
+ usrerr("554 5.3.5 alias too long");
while ((c = fgetc(af)) != EOF && c != '\n')
continue;
skipping = TRUE;
@@ -722,7 +786,7 @@ readaliases(map, af, announcestats, logstats)
if (!bitnset(M_ALIASABLE, al.q_mailer->m_flags))
{
- syserr("554 %s... cannot alias non-local names",
+ syserr("554 5.3.5 %s... cannot alias non-local names",
al.q_paddr);
continue;
}
@@ -738,7 +802,29 @@ readaliases(map, af, announcestats, logstats)
lhssize = strlen(al.q_user);
rhssize = strlen(rhs);
- map->map_class->map_store(map, al.q_user, rhs);
+ if (rhssize > 0)
+ {
+ /* is RHS empty (just spaces)? */
+ p = rhs;
+ while (isascii(*p) && isspace(*p))
+ p++;
+ }
+ if (rhssize == 0 || *p == '\0')
+ {
+ syserr("554 5.3.5 %.40s... missing value for alias",
+ line);
+
+ }
+ else
+ {
+ map->map_class->map_store(map, al.q_user, rhs);
+
+ /* statistics */
+ naliases++;
+ bytes += lhssize + rhssize;
+ if (rhssize > longest)
+ longest = rhssize;
+ }
if (al.q_paddr != NULL)
free(al.q_paddr);
@@ -746,12 +832,6 @@ readaliases(map, af, announcestats, logstats)
free(al.q_host);
if (al.q_user != NULL)
free(al.q_user);
-
- /* statistics */
- naliases++;
- bytes += lhssize + rhssize;
- if (rhssize > longest)
- longest = rhssize;
}
CurEnv->e_to = NULL;
@@ -798,14 +878,14 @@ forward(user, sendq, aliaslevel, e)
bool got_transient;
if (tTd(27, 1))
- printf("forward(%s)\n", user->q_paddr);
+ dprintf("forward(%s)\n", user->q_paddr);
if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) ||
- bitset(QBADADDR, user->q_flags))
+ !QS_IS_OK(user->q_state))
return;
if (user->q_home == NULL)
{
- syserr("554 forward: no home");
+ syserr("554 5.3.0 forward: no home");
user->q_home = "/no/such/directory";
}
@@ -820,18 +900,19 @@ forward(user, sendq, aliaslevel, e)
for (pp = ForwardPath; pp != NULL; pp = ep)
{
int err;
- char buf[MAXPATHLEN+1];
+ char buf[MAXPATHLEN + 1];
+ struct stat st;
- ep = strchr(pp, ':');
+ ep = strchr(pp, SEPARATOR);
if (ep != NULL)
*ep = '\0';
expand(pp, buf, sizeof buf, e);
if (ep != NULL)
- *ep++ = ':';
+ *ep++ = SEPARATOR;
if (buf[0] == '\0')
continue;
if (tTd(27, 3))
- printf("forward: trying %s\n", buf);
+ dprintf("forward: trying %s\n", buf);
err = include(buf, TRUE, user, sendq, aliaslevel, e);
if (err == 0)
@@ -841,11 +922,19 @@ forward(user, sendq, aliaslevel, e)
/* we may have to suspend this message */
got_transient = TRUE;
if (tTd(27, 2))
- printf("forward: transient error on %s\n", buf);
+ dprintf("forward: transient error on %s\n",
+ buf);
if (LogLevel > 2)
+ {
+ char *curhost = CurHostName;
+
+ CurHostName = NULL;
sm_syslog(LOG_ERR, e->e_id,
- "forward %s: transient error: %s",
- buf, errstring(err));
+ "forward %s: transient error: %s",
+ buf, errstring(err));
+ CurHostName = curhost;
+ }
+
}
else
{
@@ -854,18 +943,27 @@ forward(user, sendq, aliaslevel, e)
case ENOENT:
break;
+ case E_SM_WWDIR:
+ case E_SM_GWDIR:
+ /* check if it even exists */
+ if (stat(buf, &st) < 0 && errno == ENOENT)
+ {
+ if (bitnset(DBS_DONTWARNFORWARDFILEINUNSAFEDIRPATH,
+ DontBlameSendmail))
+ break;
+ }
+ /* FALLTHROUGH */
+
#if _FFR_FORWARD_SYSERR
case E_SM_NOSLINK:
case E_SM_NOHLINK:
case E_SM_REGONLY:
case E_SM_ISEXEC:
- case E_SM_WWDIR:
- case E_SM_GWDIR:
case E_SM_WWFILE:
case E_SM_GWFILE:
syserr("forward: %s: %s", buf, errstring(err));
break;
-#endif
+#endif /* _FFR_FORWARD_SYSERR */
default:
if (LogLevel > (RunAsUid == 0 ? 2 : 10))
@@ -889,7 +987,7 @@ forward(user, sendq, aliaslevel, e)
*/
message("transient .forward open error: message queued");
- user->q_flags |= QQUEUEUP;
+ user->q_state = QS_QUEUEUP;
return;
}
}
diff --git a/contrib/sendmail/src/aliases b/contrib/sendmail/src/aliases
index 7540eea..85e62a9 100644
--- a/contrib/sendmail/src/aliases
+++ b/contrib/sendmail/src/aliases
@@ -1,4 +1,5 @@
#
+# $Id: aliases,v 8.1 1999/02/06 18:44:07 gshapiro Exp $
# @(#)aliases 8.2 (Berkeley) 3/5/94
#
# Aliases in this file will NOT be expanded in the header from
diff --git a/contrib/sendmail/src/arpadate.c b/contrib/sendmail/src/arpadate.c
index c02decd..c265cdb 100644
--- a/contrib/sendmail/src/arpadate.c
+++ b/contrib/sendmail/src/arpadate.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,10 +12,10 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)arpadate.c 8.14 (Berkeley) 2/2/1999";
-#endif /* not lint */
+static char id[] = "@(#)$Id: arpadate.c,v 8.23 1999/09/23 19:59:18 ca Exp $";
+#endif /* ! lint */
-# include "sendmail.h"
+#include <sendmail.h>
/*
** ARPADATE -- Create date in ARPANET format
@@ -34,7 +35,7 @@ static char sccsid[] = "@(#)arpadate.c 8.14 (Berkeley) 2/2/1999";
**
** Bugs:
** Timezone is computed from local time, rather than
-** from whereever (and whenever) the message was sent.
+** from wherever (and whenever) the message was sent.
** To do better is very hard.
**
** Some sites are now inserting the timezone into the
@@ -44,7 +45,7 @@ static char sccsid[] = "@(#)arpadate.c 8.14 (Berkeley) 2/2/1999";
#ifndef TZNAME_MAX
# define TZNAME_MAX 50 /* max size of timezone */
-#endif
+#endif /* ! TZNAME_MAX */
/* values for TZ_TYPE */
#define TZ_NONE 0 /* no character timezone support */
@@ -148,10 +149,10 @@ arpadate(ud)
tz = NULL;
#if TZ_TYPE == TZ_TM_NAME
tz = lt->tm_name;
-#endif
+#endif /* TZ_TYPE == TZ_TM_NAME */
#if TZ_TYPE == TZ_TM_ZONE
tz = lt->tm_zone;
-#endif
+#endif /* TZ_TYPE == TZ_TM_ZONE */
#if TZ_TYPE == TZ_TZNAME
{
extern char *tzname[];
@@ -163,14 +164,14 @@ arpadate(ud)
else
tz = NULL;
}
-#endif
+#endif /* TZ_TYPE == TZ_TZNAME */
#if TZ_TYPE == TZ_TIMEZONE
{
extern char *timezone();
tz = timezone(off, lt->tm_isdst);
}
-#endif
+#endif /* TZ_TYPE == TZ_TIMEZONE */
if (off < 0)
{
off = -off;
@@ -198,5 +199,5 @@ arpadate(ud)
}
*q = '\0';
- return (b);
+ return b;
}
diff --git a/contrib/sendmail/src/bf.h b/contrib/sendmail/src/bf.h
new file mode 100644
index 0000000..d7d6303
--- /dev/null
+++ b/contrib/sendmail/src/bf.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 1999 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 1999/11/04 19:31:25 ca Exp $
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+#ifndef BF_H
+#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 bfclose __P((FILE *));
+extern bool bftest __P((FILE *));
+
+#endif /* BF_H */
diff --git a/contrib/sendmail/src/bf_portable.c b/contrib/sendmail/src/bf_portable.c
new file mode 100644
index 0000000..4de0af3
--- /dev/null
+++ b/contrib/sendmail/src/bf_portable.c
@@ -0,0 +1,482 @@
+/*
+ * 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.
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: bf_portable.c,v 8.25.4.3 2000/06/29 21:21:58 gshapiro Exp $";
+#endif /* ! lint */
+
+#if SFIO
+# include <sfio/stdio.h>
+#endif /* SFIO */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <errno.h>
+#if !SFIO
+# include <stdio.h>
+#endif /* !SFIO */
+#ifndef BF_STANDALONE
+# include "sendmail.h"
+#endif /* ! BF_STANDALONE */
+#include "bf_portable.h"
+#include "bf.h"
+
+ /*
+** 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 FILE * which may then be used with stdio functions, or NULL
+** on failure. FILE * is opened for writing (mode "w+").
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** ENOMEM -- out of memory
+** ENOENT -- illegal empty filename specified
+** any value of errno specified by open()
+** any value of errno specified by fdopen()
+** any value of errno specified by funopen()
+*/
+
+#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 */
+
+/* List of currently-open buffered files */
+struct bf *bflist = NULL;
+
+FILE *
+bfopen(filename, fmode, bsize, flags)
+ char *filename;
+ int fmode;
+ size_t bsize;
+ long flags;
+{
+ struct bf *bfp;
+ FILE *retval;
+ int fd, l;
+
+ fd = OPEN(filename, O_RDWR | O_CREAT | O_TRUNC, fmode, flags);
+ if (fd == -1)
+ {
+ /* errno is set implicitly by open */
+ return NULL;
+ }
+
+ retval = fdopen(fd, "w+");
+
+ /* If failure, return immediately */
+ if (retval == NULL)
+ {
+ /* errno is set implicitly by fdopen */
+ return NULL;
+ }
+
+ /* Allocate memory */
+ bfp = (struct bf *)malloc(sizeof(struct bf));
+ if (bfp == NULL)
+ {
+ (void) fclose(retval);
+
+ /* don't care about errors */
+ (void) unlink(filename);
+ errno = ENOMEM;
+ return NULL;
+ }
+ if (tTd(58, 8))
+ dprintf("bfopen(%s): malloced %ld\n",
+ filename, (long) sizeof(struct bf));
+
+ l = strlen(filename) + 1;
+ bfp->bf_filename = (char *)malloc(l);
+ if (bfp->bf_filename == NULL)
+ {
+ free(bfp);
+ (void) fclose(retval);
+
+ /* don't care about errors */
+ (void) unlink(filename);
+ errno = ENOMEM;
+ return NULL;
+ }
+ (void) strlcpy(bfp->bf_filename, filename, l);
+
+ /* Fill in the other fields, then add it to the list */
+ bfp->bf_key = retval;
+ bfp->bf_committed = FALSE;
+ bfp->bf_refcount = 1;
+
+ bfinsert(bfp);
+
+ /* Whew. Nothing bad happened. We're okay. */
+ return retval;
+}
+ /*
+** BFDUP -- increase refcount on buffered file
+**
+** Parameters:
+** fp -- FILE * to "duplicate"
+**
+** Returns:
+** fp with increased refcount
+*/
+
+FILE *
+bfdup(fp)
+ FILE *fp;
+{
+ struct bf *bfp;
+
+ /* Get associated bf structure */
+ bfp = bflookup(fp);
+
+ if (bfp == NULL)
+ return NULL;
+
+ /* Increase the refcount */
+ bfp->bf_refcount++;
+
+ return fp;
+}
+
+ /*
+** BFCOMMIT -- "commits" the buffered file
+**
+** Parameters:
+** fp -- FILE * to commit to disk
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** Forces the given FILE * 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()
+*/
+
+int
+bfcommit(fp)
+ FILE *fp;
+{
+ struct bf *bfp;
+
+ /* Get associated bf structure */
+ bfp = bflookup(fp);
+
+ /* If called on a normal FILE *, noop */
+ if (bfp != NULL)
+ bfp->bf_committed = TRUE;
+
+ return 0;
+}
+
+ /*
+** BFREWIND -- rewinds the FILE *
+**
+** Parameters:
+** fp -- FILE * to rewind
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** rewinds the FILE * 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 fseek()
+*/
+
+int
+bfrewind(fp)
+ FILE *fp;
+{
+ int err;
+
+ /* check to see if there is an error on the stream */
+ err = ferror(fp);
+
+ (void) fflush(fp);
+
+ /*
+ ** Clear error if tried to fflush()
+ ** a read-only file pointer and
+ ** there wasn't a previous error.
+ */
+
+ if (err == 0)
+ clearerr(fp);
+
+ /* errno is set implicitly by fseek() before return */
+ return fseek(fp, 0, SEEK_SET);
+}
+
+ /*
+** BFTRUNCATE -- rewinds and truncates the FILE *
+**
+** Parameters:
+** fp -- FILE * to truncate
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** rewinds the FILE *, truncates it to zero length, and puts it
+** into write mode. If fp is not a buffered file, this is
+** equivalent to a rewind() and then an ftruncate(fileno(fp), 0).
+**
+** Sets errno:
+** any value of errno specified by fseek()
+** any value of errno specified by ftruncate()
+*/
+
+int
+bftruncate(fp)
+ FILE *fp;
+{
+ int ret;
+
+ if (bfrewind(fp) == -1)
+ {
+ /* errno is set implicitly by bfrewind() */
+ return -1;
+ }
+
+#if NOFTRUNCATE
+ /* XXX */
+ errno = EINVAL;
+ ret = -1;
+#else /* NOFTRUNCATE */
+ /* errno is set implicitly by ftruncate() before return */
+ ret = ftruncate(fileno(fp), 0);
+#endif /* NOFTRUNCATE */
+ return ret;
+}
+
+ /*
+** BFCLOSE -- close a buffered file
+**
+** Parameters:
+** fp -- FILE * to close
+**
+** Returns:
+** 0 on success, EOF on failure
+**
+** Side Effects:
+** Closes fp. If fp is a buffered file, unlink it if it has not
+** already been committed. If fp is not a buffered file, this is
+** equivalent to fclose().
+**
+** Sets errno:
+** any value of errno specified by fclose()
+*/
+
+int
+bfclose(fp)
+ FILE *fp;
+{
+ int retval;
+ struct bf *bfp = NULL;
+
+ /* Get associated bf structure */
+ bfp = bflookup(fp);
+
+ /* Decrement and check refcount */
+ if (bfp != NULL && --bfp->bf_refcount > 0)
+ return 0;
+
+ /* If bf, get bf structure and remove from list */
+ if (bfp != NULL)
+ bfp = bfdelete(fp);
+
+ if (fclose(fp) == EOF)
+ {
+ if (tTd(58, 8))
+ dprintf("bfclose: fclose failed\n");
+ /* errno is set implicitly by fclose() */
+ return -1;
+ }
+
+ if (bfp == NULL)
+ return 0;
+
+ /* Success unless we determine otherwise in next block */
+ retval = 0;
+
+ if (bfp != NULL)
+ {
+ /* Might have to unlink; certainly will have to deallocate */
+ if (!bfp->bf_committed)
+ retval = unlink(bfp->bf_filename);
+
+ free(bfp->bf_filename);
+ free(bfp);
+ if (tTd(58, 8))
+ dprintf("bfclose: freed %ld\n",
+ (long) sizeof(struct bf));
+ }
+ else
+ {
+ if (tTd(58, 8))
+ dprintf("bfclose: bfp was NULL\n");
+ }
+
+ return retval;
+}
+
+ /*
+** BFTEST -- test if a FILE * is a buffered file
+**
+** Parameters:
+** fp -- FILE * to test
+**
+** Returns:
+** TRUE if fp is a buffered file, FALSE otherwise.
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** never.
+*/
+
+bool
+bftest(fp)
+ FILE *fp;
+{
+ return (bflookup(fp) != NULL);
+}
+
+ /*
+** BFINSERT -- insert item in linking list
+**
+** Parameters:
+** datum -- item to insert
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** never.
+*/
+
+void
+bfinsert(datum)
+ struct bf *datum;
+{
+ datum->bf_cdr = bflist;
+ bflist = datum;
+}
+
+ /*
+** BFLOOKUP -- lookup FILE * in list
+**
+** Parameters:
+** fp -- FILE * to lookup
+**
+** Returns:
+** bf struct for the FILE *, NULL if not found
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** never.
+*/
+
+struct bf *
+bflookup(key)
+ FILE *key;
+{
+ struct bf *t;
+
+ for (t = bflist; t != NULL; t = t->bf_cdr)
+ {
+ if (t->bf_key == key)
+ {
+ return t;
+ }
+ }
+
+ /* If we got this far, we didn't find it */
+ return NULL;
+}
+
+ /*
+** BFDELETE -- delete a FILE * in list
+**
+** Parameters:
+** fp -- FILE * to delete
+**
+** Returns:
+** bf struct for deleted FILE *, NULL if not found,
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** never.
+*/
+
+struct bf *
+bfdelete(key)
+ FILE *key;
+{
+ struct bf *t, *u;
+
+ if (bflist == NULL)
+ return NULL;
+
+ /* if first element, special case */
+ if (bflist->bf_key == key)
+ {
+ u = bflist;
+ bflist = bflist->bf_cdr;
+ return u;
+ }
+
+ for (t = bflist; t->bf_cdr != NULL; t = t->bf_cdr)
+ {
+ if (t->bf_cdr->bf_key == key)
+ {
+ u = t->bf_cdr;
+ t->bf_cdr = u->bf_cdr;
+ return u;
+ }
+ }
+
+ /* If we got this far, we didn't find it */
+ return NULL;
+}
diff --git a/contrib/sendmail/src/bf_portable.h b/contrib/sendmail/src/bf_portable.h
new file mode 100644
index 0000000..e319a74
--- /dev/null
+++ b/contrib/sendmail/src/bf_portable.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1999 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_portable.h,v 8.6 1999/11/04 19:31:25 ca Exp $
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+#ifndef BF_PORTABLE_H
+#define BF_PORTABLE_H 1
+/*
+** This implementation will behave differently from the Torek-based code in
+** the following major ways:
+** - The buffer size argument to bfopen() will be sent in, sent back,
+** queried, lost, found, subjected to public inquiry, lost again, and
+** finally buried in soft peat and recycled as firelighters.
+** - Errors in creating the file (but not necessarily writing to it) will
+** always be detected and reported synchronously with the bfopen()
+*/
+
+/* Linked structure for storing information about each buffered file */
+struct bf
+{
+ FILE *bf_key; /* Unused except as a key for lookup */
+ bool bf_committed; /* buffered file is on disk */
+ char *bf_filename; /* Name of disk file */
+ int bf_refcount; /* Reference count */
+ struct bf *bf_cdr;
+};
+
+/*
+** Access routines for looking up bf structures
+**
+** maybe replace with a faster data structure later
+*/
+
+extern void bfinsert __P((struct bf *));
+extern struct bf *bflookup __P((FILE *));
+extern struct bf *bfdelete __P((FILE *));
+#endif /* BF_PORTABLE_H */
diff --git a/contrib/sendmail/src/bf_torek.c b/contrib/sendmail/src/bf_torek.c
new file mode 100644
index 0000000..fb05368
--- /dev/null
+++ b/contrib/sendmail/src/bf_torek.c
@@ -0,0 +1,784 @@
+/*
+ * Copyright (c) 1999 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.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: bf_torek.c,v 8.19.18.1 2000/07/18 16:52:26 gshapiro Exp $";
+#endif /* ! lint */
+
+#if SFIO
+ ERROR README: Can not use bf_torek.c with SFIO.
+#endif /* SFIO */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#ifndef BF_STANDALONE
+# include "sendmail.h"
+#endif /* ! BF_STANDALONE */
+#include "bf_torek.h"
+#include "bf.h"
+
+ /*
+** 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 FILE * which may then be used with stdio functions, or NULL
+** on failure. FILE * is opened for writing (mode "w+").
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** ENOMEM -- out of memory
+** ENOENT -- illegal empty filename specified
+** any value of errno specified by open()
+** any value of errno specified by fdopen()
+** any value of errno specified by funopen()
+*/
+
+#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 */
+
+FILE *
+bfopen(filename, fmode, bsize, flags)
+ char *filename;
+ int fmode;
+ size_t bsize;
+ long flags;
+{
+ struct bf *bfp;
+ FILE *retval;
+ int save_errno, l;
+ struct stat st;
+
+ /* Sanity checks */
+ /* Empty filename string */
+ if (*filename == '\0')
+ {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (stat(filename, &st) == 0)
+ {
+ /* file already exists on disk */
+ errno = EEXIST;
+ return NULL;
+ }
+
+ /* Allocate memory */
+ bfp = (struct bf *)malloc(sizeof(struct bf));
+ if (bfp == NULL)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* A zero bsize is valid, just don't allocate memory */
+ if (bsize > 0)
+ {
+ bfp->bf_buf = (char *)malloc(bsize);
+ if (bfp->bf_buf == NULL)
+ {
+ free(bfp);
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+ 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 = flags;
+ bfp->bf_bufsize = bsize;
+ bfp->bf_buffilled = 0;
+ l = strlen(filename) + 1;
+ bfp->bf_filename = (char *)malloc(l);
+ if (bfp->bf_filename == NULL)
+ {
+ free(bfp);
+ if (bfp->bf_buf != NULL)
+ free(bfp->bf_buf);
+ errno = ENOMEM;
+ return NULL;
+ }
+ (void) strlcpy(bfp->bf_filename, filename, l);
+ bfp->bf_filemode = fmode;
+ bfp->bf_offset = 0;
+ bfp->bf_size = 0;
+
+ if (tTd(58, 8))
+ dprintf("bfopen(%s, %d)\n", filename, bsize);
+
+ /* The big test: will funopen accept it? */
+ retval = funopen((void *)bfp, _bfread, _bfwrite, _bfseek, _bfclose);
+ if (retval == NULL)
+ {
+ /* Just in case free() sets errno */
+ save_errno = errno;
+ free(bfp);
+ free(bfp->bf_filename);
+ if (bfp->bf_buf != NULL)
+ free(bfp->bf_buf);
+ errno = save_errno;
+ return NULL;
+ }
+ else
+ {
+ /* Success */
+ return retval;
+ }
+}
+ /*
+** BFDUP -- increase refcount on buffered file
+**
+** Parameters:
+** fp -- FILE * to "duplicate"
+**
+** Returns:
+** If file is memory buffered, fp with increased refcount
+** If file is on disk, NULL (need to use link())
+*/
+
+FILE *
+bfdup(fp)
+ FILE *fp;
+{
+ struct bf *bfp;
+
+ /* If called on a normal FILE *, noop */
+ if (!bftest(fp))
+ return NULL;
+
+ /* Get associated bf structure */
+ bfp = (struct bf *)fp->_cookie;
+
+ /* Increase ref count */
+ bfp->bf_refcount++;
+
+ return fp;
+}
+
+ /*
+** BFCOMMIT -- "commits" the buffered file
+**
+** Parameters:
+** fp -- FILE * to commit to disk
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** Forces the given FILE * 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()
+*/
+
+int
+bfcommit(fp)
+ FILE *fp;
+{
+ struct bf *bfp;
+ int retval;
+ int byteswritten;
+
+ /* If called on a normal FILE *, noop */
+ if (!bftest(fp))
+ return 0;
+
+ /* Get associated bf structure */
+ bfp = (struct bf *)fp->_cookie;
+
+ /* If already committed, noop */
+ if (bfp->bf_committed)
+ return 0;
+
+ /* Do we need to open a file? */
+ if (!bfp->bf_ondisk)
+ {
+ struct stat st;
+
+ if (tTd(58, 8))
+ dprintf("bfcommit(%s): to disk\n", bfp->bf_filename);
+
+ if (stat(bfp->bf_filename, &st) == 0)
+ {
+ errno = EEXIST;
+ return -1;
+ }
+
+ retval = OPEN(bfp->bf_filename, O_RDWR | O_CREAT | O_TRUNC,
+ bfp->bf_filemode, bfp->bf_flags);
+
+ /* 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;
+ free(bfp->bf_buf);
+ }
+ return 0;
+}
+
+ /*
+** BFREWIND -- rewinds the FILE *
+**
+** Parameters:
+** fp -- FILE * to rewind
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** rewinds the FILE * 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 fseek()
+*/
+
+int
+bfrewind(fp)
+ FILE *fp;
+{
+ int err;
+
+ /* check to see if there is an error on the stream */
+ err = ferror(fp);
+
+ (void) fflush(fp);
+
+ /*
+ ** Clear error if tried to fflush()
+ ** a read-only file pointer and
+ ** there wasn't a previous error.
+ */
+
+ if (err == 0)
+ clearerr(fp);
+
+ /* errno is set implicitly by fseek() before return */
+ return fseek(fp, 0, SEEK_SET);
+}
+
+ /*
+** BFTRUNCATE -- rewinds and truncates the FILE *
+**
+** Parameters:
+** fp -- FILE * to truncate
+**
+** Returns:
+** 0 on success, -1 on error
+**
+** Side Effects:
+** rewinds the FILE *, truncates it to zero length, and puts it
+** into write mode. If fp is not a buffered file, this is
+** equivalent to a rewind() and then an ftruncate(fileno(fp), 0).
+**
+** Sets errno:
+** any value of errno specified by fseek()
+** any value of errno specified by ftruncate()
+*/
+
+int
+bftruncate(fp)
+ FILE *fp;
+{
+ struct bf *bfp;
+
+ if (bfrewind(fp) < 0)
+ return -1;
+
+ if (bftest(fp))
+ {
+ /* Get bf structure */
+ bfp = (struct bf *)fp->_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)
+ return ftruncate(bfp->bf_disk_fd, 0);
+ else
+ return 0;
+ }
+ else
+ return ftruncate(fileno(fp), 0);
+}
+
+ /*
+** BFCLOSE -- close a buffered file
+**
+** Parameters:
+** fp -- FILE * to close
+**
+** Returns:
+** 0 on success, EOF on failure
+**
+** Side Effects:
+** Closes fp. If fp is a buffered file, unlink it if it has not
+** already been committed. If fp is not a buffered file, this is
+** equivalent to fclose().
+**
+** Sets errno:
+** any value of errno specified by fclose()
+*/
+
+int
+bfclose(fp)
+ FILE *fp;
+{
+ struct bf *bfp;
+
+ /* If called on a normal FILE *, call fclose() on it */
+ if (!bftest(fp))
+ return fclose(fp);
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *)fp->_cookie;
+
+ /* Check reference count to see if we actually want to close */
+ if (bfp != NULL && --bfp->bf_refcount > 0)
+ return 0;
+
+ /*
+ ** In this implementation, just call fclose--the _bfclose
+ ** routine will be called by that
+ */
+
+ return fclose(fp);
+}
+
+ /*
+** BFTEST -- test if a FILE * is a buffered file
+**
+** Parameters:
+** fp -- FILE * to test
+**
+** Returns:
+** TRUE if fp is a buffered file, FALSE otherwise.
+**
+** Side Effects:
+** none.
+**
+** Sets errno:
+** never.
+*/
+
+bool
+bftest(fp)
+ FILE *fp;
+{
+ /*
+ ** Check to see if our special I/O routines are installed
+ ** in this file structure
+ */
+
+ return ((fp->_close == _bfclose) &&
+ (fp->_read == _bfread) &&
+ (fp->_seek == _bfseek) &&
+ (fp->_write == _bfwrite));
+}
+
+ /*
+** _BFCLOSE -- close a buffered file
+**
+** Parameters:
+** cookie -- cookie of file to close
+**
+** Returns:
+** 0 to indicate success
+**
+** Side Effects:
+** deletes backing file, frees memory.
+**
+** Sets errno:
+** never.
+*/
+
+int
+_bfclose(cookie)
+ void *cookie;
+{
+ struct bf *bfp;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *)cookie;
+
+ /* Need to clean up the file */
+ if (bfp->bf_ondisk && !bfp->bf_committed)
+ unlink(bfp->bf_filename);
+
+ /* Need to free the buffer */
+ if (bfp->bf_bufsize > 0)
+ free(bfp->bf_buf);
+
+ /* Finally, free the structure */
+ free(bfp);
+
+ return 0;
+}
+
+ /*
+** _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.
+**
+*/
+
+int
+_bfread(cookie, buf, nbytes)
+ void *cookie;
+ char *buf;
+ int nbytes;
+{
+ struct bf *bfp;
+ int count = 0; /* Number of bytes put in buf so far */
+ int retval;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *)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. */
+ 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;
+}
+
+ /*
+** _BFSEEK -- seek to a position in a buffered file
+**
+** Parameters:
+** cookie -- cookie of file to seek
+** offset -- position to seek to
+** whence -- how to seek
+**
+** Returns:
+** new file offset or -1 indicate failure
+**
+** Side Effects:
+** none.
+**
+*/
+
+fpos_t
+_bfseek(cookie, offset, whence)
+ void *cookie;
+ fpos_t offset;
+ int whence;
+
+{
+ struct bf *bfp;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *)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;
+}
+
+ /*
+** _BFWRITE -- write to a buffered file
+**
+** Parameters:
+** cookie -- cookie 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.
+**
+*/
+
+int
+_bfwrite(cookie, buf, nbytes)
+ void *cookie;
+ const char *buf;
+ int nbytes;
+{
+ struct bf *bfp;
+ int count = 0; /* Number of bytes written so far */
+ int retval;
+
+ /* Cast cookie back to correct type */
+ bfp = (struct bf *)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)
+ {
+ /* Oops, the file doesn't exist. */
+ if (tTd(58, 8))
+ dprintf("_bfwrite(%s): to disk\n",
+ bfp->bf_filename);
+
+ retval = OPEN(bfp->bf_filename,
+ O_RDWR | O_CREAT | O_TRUNC,
+ bfp->bf_filemode, bfp->bf_flags);
+
+ /* 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) || (errno == 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;
+}
diff --git a/contrib/sendmail/src/bf_torek.h b/contrib/sendmail/src/bf_torek.h
new file mode 100644
index 0000000..48c3995
--- /dev/null
+++ b/contrib/sendmail/src/bf_torek.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1999 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_torek.h,v 8.6 1999/11/04 19:31:25 ca Exp $
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+#ifndef BF_TOREK_H
+#define BF_TOREK_H 1
+/*
+** Data structure for storing information about each buffered file
+*/
+
+struct bf
+{
+ bool bf_committed; /* Has this buffered file been committed? */
+ bool bf_ondisk; /* On disk: committed or buffer overflow */
+ int 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 */
+ fpos_t bf_offset; /* Currect file offset */
+ int bf_size; /* Total current size of file */
+ int bf_refcount; /* Reference count */
+};
+
+/* Our lower-level I/O routines */
+extern int _bfclose __P((void *));
+extern int _bfread __P((void *, char *, int));
+extern fpos_t _bfseek __P((void *, fpos_t, int));
+extern int _bfwrite __P((void *, const char *, int));
+#endif /* BF_TOREK_H */
diff --git a/contrib/sendmail/src/clock.c b/contrib/sendmail/src/clock.c
index e6466e6..33b38f4 100644
--- a/contrib/sendmail/src/clock.c
+++ b/contrib/sendmail/src/clock.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,14 +12,17 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)clock.c 8.35 (Berkeley) 2/2/1999";
-#endif /* not lint */
+static char id[] = "@(#)$Id: clock.c,v 8.52.18.2 2000/05/25 23:33:30 gshapiro Exp $";
+#endif /* ! lint */
-# include "sendmail.h"
+#include <sendmail.h>
+
+#ifndef sigmask
+# define sigmask(s) (1 << ((s) - 1))
+#endif /* ! sigmask */
+
+static void endsleep __P((void));
-# ifndef sigmask
-# define sigmask(s) (1 << ((s) - 1))
-# endif
/*
** SETEVENT -- set an event to happen at a specific time.
@@ -40,8 +44,6 @@ static char sccsid[] = "@(#)clock.c 8.35 (Berkeley) 2/2/1999";
EVENT *FreeEventList; /* list of free events */
-static SIGFUNC_DECL tick __P((int));
-
EVENT *
setevent(intvl, func, arg)
time_t intvl;
@@ -55,8 +57,8 @@ setevent(intvl, func, arg)
if (intvl <= 0)
{
- syserr("554 setevent: intvl=%ld\n", intvl);
- return (NULL);
+ syserr("554 5.3.0 setevent: intvl=%ld\n", intvl);
+ return NULL;
}
wasblocked = blocksignal(SIGALRM);
@@ -83,16 +85,16 @@ setevent(intvl, func, arg)
*evp = ev;
if (tTd(5, 5))
- printf("setevent: intvl=%ld, for=%ld, func=%lx, arg=%d, ev=%lx\n",
+ dprintf("setevent: intvl=%ld, for=%ld, func=%lx, arg=%d, ev=%lx\n",
(long) intvl, (long)(now + intvl), (u_long) func,
arg, (u_long) ev);
- setsignal(SIGALRM, tick);
+ (void) setsignal(SIGALRM, tick);
intvl = EventQueue->ev_time - now;
(void) alarm((unsigned) intvl < 1 ? 1 : intvl);
if (wasblocked == 0)
(void) releasesignal(SIGALRM);
- return (ev);
+ return ev;
}
/*
** CLREVENT -- remove an event from the event queue.
@@ -115,7 +117,7 @@ clrevent(ev)
int wasblocked;
if (tTd(5, 5))
- printf("clrevent: ev=%lx\n", (u_long) ev);
+ dprintf("clrevent: ev=%lx\n", (u_long) ev);
if (ev == NULL)
return;
@@ -137,9 +139,52 @@ clrevent(ev)
/* restore clocks and pick up anything spare */
if (wasblocked == 0)
- releasesignal(SIGALRM);
+ (void) releasesignal(SIGALRM);
if (EventQueue != NULL)
- kill(getpid(), SIGALRM);
+ (void) kill(getpid(), SIGALRM);
+ else
+ {
+ /* nothing left in event queue, no need for an alarm */
+ (void) alarm(0);
+ }
+}
+ /*
+** CLEAR_EVENTS -- remove all events from the event queue.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+*/
+
+void
+clear_events()
+{
+ register EVENT *ev;
+ int wasblocked;
+
+ if (tTd(5, 5))
+ dprintf("clear_events: EventQueue=%lx\n", (u_long) EventQueue);
+
+ if (EventQueue == NULL)
+ return;
+
+ /* nothing will be left in event queue, no need for an alarm */
+ (void) alarm(0);
+ wasblocked = blocksignal(SIGALRM);
+
+ /* find the end of the EventQueue */
+ for (ev = EventQueue; ev->ev_link != NULL; ev = ev->ev_link)
+ continue;
+
+ ev->ev_link = FreeEventList;
+ FreeEventList = EventQueue;
+ EventQueue = NULL;
+
+ /* restore clocks and pick up anything spare */
+ if (wasblocked == 0)
+ (void) releasesignal(SIGALRM);
}
/*
** TICK -- take a clock tick
@@ -159,20 +204,20 @@ clrevent(ev)
*/
/* ARGSUSED */
-static SIGFUNC_DECL
+SIGFUNC_DECL
tick(sig)
int sig;
{
register time_t now;
register EVENT *ev;
int mypid = getpid();
- int olderrno = errno;
+ int save_errno = errno;
(void) alarm(0);
now = curtime();
if (tTd(5, 4))
- printf("tick: now=%ld\n", (long) now);
+ dprintf("tick: now=%ld\n", (long) now);
/* reset signal in case System V semantics */
(void) setsignal(SIGALRM, tick);
@@ -187,7 +232,7 @@ tick(sig)
ev = EventQueue;
EventQueue = EventQueue->ev_link;
if (tTd(5, 6))
- printf("tick: ev=%lx, func=%lx, arg=%d, pid=%d\n",
+ dprintf("tick: ev=%lx, func=%lx, arg=%d, pid=%d\n",
(u_long) ev, (u_long) ev->ev_func,
ev->ev_arg, ev->ev_pid);
@@ -208,14 +253,14 @@ tick(sig)
}
/* call ev_func */
- errno = olderrno;
+ errno = save_errno;
(*f)(arg);
(void) alarm(0);
now = curtime();
}
if (EventQueue != NULL)
(void) alarm((unsigned) (EventQueue->ev_time - now));
- errno = olderrno;
+ errno = save_errno;
return SIGFUNC_RETURN;
}
/*
@@ -235,12 +280,12 @@ tick(sig)
** be run during that interval.
*/
+
static bool SleepDone;
-static void endsleep __P((void));
#ifndef SLEEP_T
# define SLEEP_T unsigned int
-#endif
+#endif /* ! SLEEP_T */
SLEEP_T
sleep(intvl)
@@ -254,9 +299,9 @@ sleep(intvl)
(void) setevent((time_t) intvl, endsleep, 0);
was_held = releasesignal(SIGALRM);
while (!SleepDone)
- pause();
+ (void) pause();
if (was_held > 0)
- blocksignal(SIGALRM);
+ (void) blocksignal(SIGALRM);
return (SLEEP_T) 0;
}
diff --git a/contrib/sendmail/src/collect.c b/contrib/sendmail/src/collect.c
index a8916b1..f6c3682 100644
--- a/contrib/sendmail/src/collect.c
+++ b/contrib/sendmail/src/collect.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,13 +12,17 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)collect.c 8.93 (Berkeley) 1/26/1999";
-#endif /* not lint */
+static char id[] = "@(#)$Id: collect.c,v 8.136.4.3 2000/06/22 22:13:45 geir Exp $";
+#endif /* ! lint */
-# include <errno.h>
-# include "sendmail.h"
+#include <sendmail.h>
-/*
+
+static void collecttimeout __P((time_t));
+static void dferror __P((FILE *volatile, char *, ENVELOPE *));
+static void eatfrom __P((char *volatile, ENVELOPE *));
+
+ /*
** COLLECT -- read & parse message header & make temp file.
**
** Creates a temporary file name and copies the standard
@@ -42,7 +47,6 @@ static char sccsid[] = "@(#)collect.c 8.93 (Berkeley) 1/26/1999";
*/
static jmp_buf CtxCollectTimeout;
-static void collecttimeout __P((time_t));
static bool CollectProgress;
static EVENT *CollectTimeout;
@@ -66,7 +70,7 @@ collect(fp, smtpmode, hdrp, e)
HDR **hdrp;
register ENVELOPE *e;
{
- register FILE *volatile tf;
+ register FILE *volatile df;
volatile bool ignrdot = smtpmode ? FALSE : IgnrDot;
volatile time_t dbto = smtpmode ? TimeOuts.to_datablock : 0;
register char *volatile bp;
@@ -77,13 +81,17 @@ collect(fp, smtpmode, hdrp, e)
volatile int buflen;
volatile int istate;
volatile int mstate;
+ volatile int hdrslen = 0;
+ volatile int numhdrs = 0;
+ volatile int dfd;
+ volatile int afd;
+ volatile int rstat = EX_OK;
u_char *volatile pbp;
- int hdrslen = 0;
u_char peekbuf[8];
- char dfname[MAXQFNAME];
+ char hsize[16];
+ char hnum[16];
+ char dfname[MAXPATHLEN];
char bufbuf[MAXLINE];
- extern bool isheader __P((char *));
- extern void tferror __P((FILE *volatile, ENVELOPE *));
headeronly = hdrp != NULL;
@@ -93,18 +101,33 @@ collect(fp, smtpmode, hdrp, e)
if (!headeronly)
{
- int tfd;
struct stat stbuf;
- strcpy(dfname, queuename(e, 'd'));
- tfd = dfopen(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode, SFF_ANYFILE);
- if (tfd < 0 || (tf = fdopen(tfd, "w")) == NULL)
+ (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_OPENASROOT);
+ if (bitset(S_IWGRP, QueueFileMode))
+ (void) umask(oldumask);
+ }
+#else /* _FFR_QUEUE_FILE_MODE */
+ df = bfopen(dfname, FileMode, DataFileBufferSize,
+ SFF_OPENASROOT);
+#endif /* _FFR_QUEUE_FILE_MODE */
+ if (df == NULL)
{
syserr("Cannot create %s", dfname);
e->e_flags |= EF_NO_BODY_RETN;
finis(TRUE, ExitStat);
+ /* NOTREACHED */
}
- if (fstat(fileno(tf), &stbuf) < 0)
+ dfd = fileno(df);
+ if (dfd < 0 || fstat(dfd, &stbuf) < 0)
e->e_dfino = -1;
else
{
@@ -124,7 +147,7 @@ collect(fp, smtpmode, hdrp, e)
message("354 Enter mail, end with \".\" on a line by itself");
if (tTd(30, 2))
- printf("collect\n");
+ dprintf("collect\n");
/*
** Read the message.
@@ -152,7 +175,7 @@ collect(fp, smtpmode, hdrp, e)
"timeout waiting for input from %s during message collect",
CurHostName ? CurHostName : "<local machine>");
errno = 0;
- usrerr("451 timeout waiting for input during message collect");
+ usrerr("451 4.4.1 timeout waiting for input during message collect");
goto readerr;
}
CollectTimeout = setevent(dbto, collecttimeout, dbto);
@@ -161,7 +184,7 @@ collect(fp, smtpmode, hdrp, e)
for (;;)
{
if (tTd(30, 35))
- printf("top, istate=%d, mstate=%d\n", istate, mstate);
+ dprintf("top, istate=%d, mstate=%d\n", istate, mstate);
for (;;)
{
if (pbp > peekbuf)
@@ -180,12 +203,12 @@ collect(fp, smtpmode, hdrp, e)
if (TrafficLogFile != NULL && !headeronly)
{
if (istate == IS_BOL)
- fprintf(TrafficLogFile, "%05d <<< ",
+ (void) fprintf(TrafficLogFile, "%05d <<< ",
(int) getpid());
if (c == EOF)
- fprintf(TrafficLogFile, "[EOF]\n");
+ (void) fprintf(TrafficLogFile, "[EOF]\n");
else
- putc(c, TrafficLogFile);
+ (void) putc(c, TrafficLogFile);
}
if (c == EOF)
goto readerr;
@@ -195,8 +218,8 @@ collect(fp, smtpmode, hdrp, e)
HasEightBits |= bitset(0x80, c);
}
if (tTd(30, 94))
- printf("istate=%d, c=%c (0x%x)\n",
- istate, c, c);
+ dprintf("istate=%d, c=%c (0x%x)\n",
+ istate, (char) c, c);
switch (istate)
{
case IS_BOL:
@@ -244,7 +267,7 @@ collect(fp, smtpmode, hdrp, e)
istate = IS_BOL;
else
{
- ungetc(c, fp);
+ (void) ungetc(c, fp);
c = '\r';
istate = IS_NORM;
}
@@ -270,9 +293,9 @@ bufferchar:
/* just put the character out */
if (MaxMessageSize <= 0 ||
e->e_msgsize <= MaxMessageSize)
- putc(c, tf);
+ (void) putc(c, df);
- /* fall through */
+ /* FALLTHROUGH */
case MS_DISCARD:
continue;
@@ -293,16 +316,18 @@ bufferchar:
else
buflen += MEMCHUNKSIZE;
buf = xalloc(buflen);
- bcopy(obuf, buf, bp - obuf);
+ memmove(buf, obuf, bp - obuf);
bp = &buf[bp - obuf];
if (obuf != bufbuf)
free(obuf);
}
if (c >= 0200 && c <= 0237)
{
-#if 0 /* causes complaints -- figure out something for 8.9 */
+#if 0 /* causes complaints -- figure out something for 8.11 */
usrerr("Illegal character 0x%x in header", c);
-#endif
+#else /* 0 */
+ /* EMPTY */
+#endif /* 0 */
}
else if (c != '\0')
{
@@ -317,8 +342,9 @@ bufferchar:
errno = 0;
e->e_flags |= EF_CLRQUEUE;
e->e_status = "5.6.0";
- usrerr("552 Headers too large (%d max)",
- MaxHeadersLength);
+ usrerrenh(e->e_status,
+ "552 Headers too large (%d max)",
+ MaxHeadersLength);
mstate = MS_DISCARD;
}
}
@@ -329,7 +355,7 @@ bufferchar:
nextstate:
if (tTd(30, 35))
- printf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n",
+ dprintf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n",
istate, mstate, buf);
switch (mstate)
{
@@ -338,14 +364,12 @@ nextstate:
#ifndef NOTUNIX
if (strncmp(buf, "From ", 5) == 0)
{
- extern void eatfrom __P((char *volatile, ENVELOPE *));
-
bp = buf;
eatfrom(buf, e);
continue;
}
-#endif
- /* fall through */
+#endif /* ! NOTUNIX */
+ /* FALLTHROUGH */
case MS_HEADER:
if (!isheader(buf))
@@ -362,7 +386,7 @@ nextstate:
c = getc(fp);
} while (errno == EINTR);
if (c != EOF)
- ungetc(c, fp);
+ (void) ungetc(c, fp);
if (c == ' ' || c == '\t')
{
/* yep -- defer this */
@@ -374,18 +398,32 @@ nextstate:
bp++;
*bp = '\0';
- if (bitset(H_EOH, chompheader(buf, FALSE, hdrp, e)))
+ if (bitset(H_EOH, chompheader(buf,
+ CHHDR_CHECK | CHHDR_USER,
+ hdrp, e)))
{
mstate = MS_BODY;
goto nextstate;
}
+ numhdrs++;
break;
case MS_BODY:
if (tTd(30, 1))
- printf("EOH\n");
+ 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);
+
bp = buf;
/* toss blank line */
@@ -402,7 +440,7 @@ nextstate:
e->e_msgsize <= MaxMessageSize)
{
while (*bp != '\0')
- putc(*bp++, tf);
+ (void) putc(*bp++, df);
}
break;
}
@@ -415,7 +453,7 @@ readerr:
const char *errmsg = errstring(errno);
if (tTd(30, 1))
- printf("collect: premature EOM: %s\n", errmsg);
+ dprintf("collect: premature EOM: %s\n", errmsg);
if (LogLevel >= 2)
sm_syslog(LOG_WARNING, e->e_id,
"collect: premature EOM: %s", errmsg);
@@ -428,14 +466,65 @@ readerr:
if (headeronly)
return;
- if (tf != NULL &&
- (fflush(tf) != 0 || ferror(tf) ||
- (SuperSafe && fsync(fileno(tf)) < 0) ||
- fclose(tf) < 0))
+ if (df == NULL)
{
- tferror(tf, e);
+ /* skip next few clauses */
+ /* EMPTY */
+ }
+ else if (fflush(df) != 0 || ferror(df))
+ {
+ dferror(df, "fflush||ferror", e);
+ flush_errors(TRUE);
+ finis(TRUE, ExitStat);
+ /* NOTREACHED */
+ }
+ else if (!SuperSafe)
+ {
+ /* skip next few clauses */
+ /* EMPTY */
+ }
+ else if ((afd = fileno(df)) >= 0 && fsync(afd) < 0)
+ {
+ dferror(df, "fsync", e);
flush_errors(TRUE);
finis(TRUE, ExitStat);
+ /* NOTREACHED */
+ }
+ else if (bfcommit(df) < 0)
+ {
+ int save_errno = errno;
+
+ if (save_errno == EEXIST)
+ {
+ char *dfile;
+ struct stat st;
+
+ dfile = queuename(e, 'd');
+ if (stat(dfile, &st) < 0)
+ st.st_size = -1;
+ errno = EEXIST;
+ syserr("collect: bfcommit(%s): already on disk, size = %ld",
+ dfile, st.st_size);
+ dfd = fileno(df);
+ if (dfd >= 0)
+ dumpfd(dfd, TRUE, TRUE);
+ }
+ errno = save_errno;
+ dferror(df, "bfcommit", e);
+ flush_errors(TRUE);
+ finis(save_errno != EEXIST, ExitStat);
+ }
+ else if (bfclose(df) < 0)
+ {
+ dferror(df, "bfclose", e);
+ flush_errors(TRUE);
+ finis(TRUE, ExitStat);
+ /* NOTREACHED */
+ }
+ else
+ {
+ /* everything is happily flushed to disk */
+ df = NULL;
}
/* An EOF when running SMTP is an error */
@@ -461,11 +550,11 @@ readerr:
shortenstring(e->e_from.q_paddr, MAXSHORTSTR),
errstring(errno));
if (feof(fp))
- usrerr("451 collect: %s on connection from %s, from=%s",
+ usrerr("451 4.4.1 collect: %s on connection from %s, from=%s",
problem, host,
shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
else
- syserr("451 collect: %s on connection from %s, from=%s",
+ syserr("451 4.4.1 collect: %s on connection from %s, from=%s",
problem, host,
shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
@@ -478,6 +567,7 @@ readerr:
if (InChild)
ExitStat = EX_QUIT;
finis(TRUE, ExitStat);
+ /* NOTREACHED */
}
/*
@@ -494,7 +584,6 @@ readerr:
if (OpMode != MD_VERIFY)
markstats(e, (ADDRESS *) NULL, FALSE);
-#if _FFR_DSN_RRT_OPTION
/*
** If we have a Return-Receipt-To:, turn it into a DSN.
*/
@@ -507,7 +596,6 @@ readerr:
if (!bitset(QHASNOTIFY, q->q_flags))
q->q_flags |= QHASNOTIFY|QPINGONSUCCESS;
}
-#endif
/*
** Add an Apparently-To: line if we have no recipient lines.
@@ -539,11 +627,11 @@ readerr:
break;
case NRA_ADD_BCC:
- addheader("Bcc", " ", &e->e_header);
+ addheader("Bcc", " ", 0, &e->e_header);
break;
case NRA_ADD_TO_UNDISCLOSED:
- addheader("To", "undisclosed-recipients:;", &e->e_header);
+ addheader("To", "undisclosed-recipients:;", 0, &e->e_header);
break;
}
@@ -554,9 +642,9 @@ readerr:
if (q->q_alias != NULL)
continue;
if (tTd(30, 3))
- printf("Adding %s: %s\n",
+ dprintf("Adding %s: %s\n",
hdr, q->q_paddr);
- addheader(hdr, q->q_paddr, &e->e_header);
+ addheader(hdr, q->q_paddr, 0, &e->e_header);
}
}
}
@@ -566,8 +654,9 @@ readerr:
{
e->e_flags |= EF_NO_BODY_RETN|EF_CLRQUEUE;
e->e_status = "5.2.3";
- usrerr("552 Message exceeds maximum fixed size (%ld)",
- MaxMessageSize);
+ 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)",
@@ -582,7 +671,7 @@ readerr:
!bitset(EF_IS_MIME, e->e_flags))
{
e->e_status = "5.6.1";
- usrerr("554 Eight bit data not allowed");
+ usrerrenh(e->e_status, "554 Eight bit data not allowed");
}
}
else
@@ -593,12 +682,20 @@ readerr:
e->e_bodytype = "7BIT";
}
- if ((e->e_dfp = fopen(dfname, "r")) == NULL)
+ if (SuperSafe)
{
- /* we haven't acked receipt yet, so just chuck this */
- syserr("Cannot reopen %s", dfname);
- finis(TRUE, ExitStat);
+ if ((e->e_dfp = fopen(dfname, "r")) == NULL)
+ {
+ /* we haven't acked receipt yet, so just chuck this */
+ syserr("Cannot reopen %s", dfname);
+ finis(TRUE, ExitStat);
+ /* NOTREACHED */
+ }
}
+ else
+ e->e_dfp = df;
+ if (e->e_dfp == NULL)
+ syserr("!collect: no e_dfp");
}
@@ -614,11 +711,12 @@ collecttimeout(timeout)
CollectTimeout = setevent(timeout, collecttimeout, timeout);
CollectProgress = FALSE;
}
- /*
-** TFERROR -- signal error on writing the temporary file.
+/*
+** DFERROR -- signal error on writing the data file.
**
** Parameters:
-** tf -- the file pointer for the temporary file.
+** df -- the file pointer for the data file.
+** msg -- detailed message.
** e -- the current envelope.
**
** Returns:
@@ -629,62 +727,67 @@ collecttimeout(timeout)
** Arranges for following output to go elsewhere.
*/
-void
-tferror(tf, e)
- FILE *volatile tf;
+static void
+dferror(df, msg, e)
+ FILE *volatile df;
+ char *msg;
register ENVELOPE *e;
{
+ char *dfname;
+
+ dfname = queuename(e, 'd');
setstat(EX_IOERR);
if (errno == ENOSPC)
{
#if STAT64 > 0
struct stat64 st;
-#else
+#else /* STAT64 > 0 */
struct stat st;
-#endif
+#endif /* STAT64 > 0 */
long avail;
long bsize;
- extern long freediskspace __P((char *, long *));
e->e_flags |= EF_NO_BODY_RETN;
if (
#if STAT64 > 0
- fstat64(fileno(tf), &st)
-#else
- fstat(fileno(tf), &st)
-#endif
+ fstat64(fileno(df), &st)
+#else /* STAT64 > 0 */
+ fstat(fileno(df), &st)
+#endif /* STAT64 > 0 */
< 0)
st.st_size = 0;
- (void) freopen(queuename(e, 'd'), "w", tf);
+ (void) freopen(dfname, "w", df);
if (st.st_size <= 0)
- fprintf(tf, "\n*** Mail could not be accepted");
+ fprintf(df, "\n*** Mail could not be accepted");
+ /*CONSTCOND*/
else if (sizeof st.st_size > sizeof (long))
- fprintf(tf, "\n*** Mail of at least %s bytes could not be accepted\n",
+ fprintf(df, "\n*** Mail of at least %s bytes could not be accepted\n",
quad_to_string(st.st_size));
else
- fprintf(tf, "\n*** Mail of at least %lu bytes could not be accepted\n",
+ fprintf(df, "\n*** Mail of at least %lu bytes could not be accepted\n",
(unsigned long) st.st_size);
- fprintf(tf, "*** at %s due to lack of disk space for temp file.\n",
+ fprintf(df, "*** at %s due to lack of disk space for temp file.\n",
MyHostName);
- avail = freediskspace(QueueDir, &bsize);
+ avail = freediskspace(qid_printqueue(e->e_queuedir), &bsize);
if (avail > 0)
{
if (bsize > 1024)
avail *= bsize / 1024;
else if (bsize < 1024)
avail /= 1024 / bsize;
- fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n",
+ fprintf(df, "*** Currently, %ld kilobytes are available for mail temp files.\n",
avail);
}
e->e_status = "4.3.1";
- usrerr("452 Out of disk space for temp file");
+ usrerrenh(e->e_status, "452 Out of disk space for temp file");
}
else
- syserr("collect: Cannot write tf%s", e->e_id);
- if (freopen("/dev/null", "w", tf) == NULL)
+ syserr("collect: Cannot write %s (%s, uid=%d)",
+ dfname, msg, geteuid());
+ if (freopen("/dev/null", "w", df) == NULL)
sm_syslog(LOG_ERR, e->e_id,
- "tferror: freopen(\"/dev/null\") failed: %s",
+ "dferror: freopen(\"/dev/null\") failed: %s",
errstring(errno));
}
/*
@@ -704,21 +807,21 @@ tferror(tf, e)
** such as the date.
*/
-# ifndef NOTUNIX
+#ifndef NOTUNIX
-char *DowList[] =
+static char *DowList[] =
{
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
};
-char *MonthList[] =
+static char *MonthList[] =
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
NULL
};
-void
+static void
eatfrom(fm, e)
char *volatile fm;
register ENVELOPE *e;
@@ -727,7 +830,7 @@ eatfrom(fm, e)
register char **dt;
if (tTd(30, 2))
- printf("eatfrom(%s)\n", fm);
+ dprintf("eatfrom(%s)\n", fm);
/* find the date part */
p = fm;
@@ -762,11 +865,9 @@ eatfrom(fm, e)
/* we have found a date */
q = xalloc(25);
- (void) strncpy(q, p, 25);
- q[24] = '\0';
+ (void) strlcpy(q, p, 25);
q = arpadate(q);
define('a', newstr(q), e);
}
}
-
-# endif /* NOTUNIX */
+#endif /* ! NOTUNIX */
diff --git a/contrib/sendmail/src/control.c b/contrib/sendmail/src/control.c
index 415818c..287391a 100644
--- a/contrib/sendmail/src/control.c
+++ b/contrib/sendmail/src/control.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998, 1999 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
@@ -8,10 +9,11 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)control.c 8.18 (Berkeley) 1/17/1999";
-#endif /* not lint */
+static char id[] = "@(#)$Id: control.c,v 8.44.14.7 2000/07/03 21:49:05 geir Exp $";
+#endif /* ! lint */
+
+#include <sendmail.h>
-#include "sendmail.h"
int ControlSocket = -1;
@@ -31,10 +33,10 @@ int ControlSocket = -1;
int
opencontrolsocket()
{
-#ifdef NETUNIX
-# if _FFR_CONTROL_SOCKET
+#if NETUNIX
+ int save_errno;
int rval;
- int sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
+ long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
struct sockaddr_un controladdr;
if (ControlSocketName == NULL)
@@ -60,28 +62,26 @@ opencontrolsocket()
if (ControlSocket < 0)
return -1;
- unlink(ControlSocketName);
- bzero(&controladdr, sizeof controladdr);
+ (void) unlink(ControlSocketName);
+ memset(&controladdr, '\0', sizeof controladdr);
controladdr.sun_family = AF_UNIX;
- strcpy(controladdr.sun_path, ControlSocketName);
+ (void) strlcpy(controladdr.sun_path, ControlSocketName,
+ sizeof controladdr.sun_path);
if (bind(ControlSocket, (struct sockaddr *) &controladdr,
sizeof controladdr) < 0)
{
- int save_errno = errno;
-
+ save_errno = errno;
clrcontrol();
errno = save_errno;
return -1;
}
-# if _FFR_TRUSTED_USER
if (geteuid() == 0 && TrustedUid != 0)
{
if (chown(ControlSocketName, TrustedUid, -1) < 0)
{
- int save_errno = errno;
-
+ save_errno = errno;
sm_syslog(LOG_ALERT, NOQID,
"ownership change on %s failed: %s",
ControlSocketName, errstring(save_errno));
@@ -92,12 +92,10 @@ opencontrolsocket()
return -1;
}
}
-# endif
if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0)
{
- int save_errno = errno;
-
+ save_errno = errno;
closecontrolsocket(TRUE);
errno = save_errno;
return -1;
@@ -105,14 +103,12 @@ opencontrolsocket()
if (listen(ControlSocket, 8) < 0)
{
- int save_errno = errno;
-
+ save_errno = errno;
closecontrolsocket(TRUE);
errno = save_errno;
return -1;
}
-# endif
-#endif
+#endif /* NETUNIX */
return 0;
}
/*
@@ -132,9 +128,8 @@ void
closecontrolsocket(fullclose)
bool fullclose;
{
-#ifdef NETUNIX
-# if _FFR_CONTROL_SOCKET
- int sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
+#if NETUNIX
+ long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
if (ControlSocket >= 0)
{
@@ -148,7 +143,7 @@ closecontrolsocket(fullclose)
rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName,
sff, S_IRUSR|S_IWUSR, NULL);
-
+
/* if not safe, don't unlink */
if (rval != 0)
return;
@@ -161,8 +156,7 @@ closecontrolsocket(fullclose)
return;
}
}
-# endif
-#endif
+#endif /* NETUNIX */
return;
}
/*
@@ -181,13 +175,11 @@ closecontrolsocket(fullclose)
void
clrcontrol()
{
-#ifdef NETUNIX
-# if _FFR_CONTROL_SOCKET
+#if NETUNIX
if (ControlSocket >= 0)
(void) close(ControlSocket);
ControlSocket = -1;
-# endif
-#endif
+#endif /* NETUNIX */
}
#ifndef NOT_SENDMAIL
@@ -196,7 +188,7 @@ clrcontrol()
** CONTROL_COMMAND -- read and process command from named socket
**
** Read and process the command from the opened socket.
-** Return the results down the same socket.
+** Exits when done since it is running in a forked child.
**
** Parameters:
** sock -- the opened socket from getrequests()
@@ -208,11 +200,11 @@ clrcontrol()
struct cmd
{
- char *cmdname; /* command name */
- int cmdcode; /* internal code, see below */
+ char *cmd_name; /* command name */
+ int cmd_code; /* internal code, see below */
};
-/* values for cmdcode */
+/* values for cmd_code */
# define CMDERROR 0 /* bad command */
# define CMDRESTART 1 /* restart daemon */
# define CMDSHUTDOWN 2 /* end daemon */
@@ -228,12 +220,23 @@ static struct cmd CmdTab[] =
{ NULL, CMDERROR }
};
+static jmp_buf CtxControlTimeout;
+
+static void
+controltimeout(timeout)
+ time_t timeout;
+{
+ longjmp(CtxControlTimeout, 1);
+}
+
void
control_command(sock, e)
int sock;
ENVELOPE *e;
{
- FILE *s;
+ volatile int exitstat = EX_OK;
+ FILE *s = NULL;
+ EVENT *ev = NULL;
FILE *traffic;
FILE *oldout;
char *cmd;
@@ -241,33 +244,45 @@ control_command(sock, e)
struct cmd *c;
char cmdbuf[MAXLINE];
char inp[MAXLINE];
- extern char **SaveArgv;
- extern void help __P((char *));
- sm_setproctitle(FALSE, "control cmd read");
-
+ sm_setproctitle(FALSE, e, "control cmd read");
+
+ if (TimeOuts.to_control > 0)
+ {
+ /* handle possible input timeout */
+ if (setjmp(CtxControlTimeout) != 0)
+ {
+ if (LogLevel > 2)
+ sm_syslog(LOG_NOTICE, e->e_id,
+ "timeout waiting for input during control command");
+ exit(EX_IOERR);
+ }
+ ev = setevent(TimeOuts.to_control, controltimeout,
+ TimeOuts.to_control);
+ }
+
s = fdopen(sock, "r+");
if (s == NULL)
{
int save_errno = errno;
- close(sock);
+ (void) close(sock);
errno = save_errno;
- return;
+ exit(EX_IOERR);
}
setbuf(s, NULL);
if (fgets(inp, sizeof inp, s) == NULL)
{
- fclose(s);
- return;
+ (void) fclose(s);
+ exit(EX_IOERR);
}
(void) fflush(s);
/* clean up end of line */
fixcrlf(inp, TRUE);
- sm_setproctitle(FALSE, "control: %s", inp);
+ sm_setproctitle(FALSE, e, "control: %s", inp);
/* break off command */
for (p = inp; isascii(*p) && isspace(*p); p++)
@@ -278,72 +293,59 @@ control_command(sock, e)
cmd < &cmdbuf[sizeof cmdbuf - 2])
*cmd++ = *p++;
*cmd = '\0';
-
+
/* throw away leading whitespace */
while (isascii(*p) && isspace(*p))
p++;
-
+
/* decode command */
- for (c = CmdTab; c->cmdname != NULL; c++)
+ for (c = CmdTab; c->cmd_name != NULL; c++)
{
- if (!strcasecmp(c->cmdname, cmdbuf))
+ if (strcasecmp(c->cmd_name, cmdbuf) == 0)
break;
}
- switch (c->cmdcode)
+ switch (c->cmd_code)
{
case CMDHELP: /* get help */
traffic = TrafficLogFile;
TrafficLogFile = NULL;
oldout = OutChannel;
OutChannel = s;
- help("control");
+ help("control", e);
TrafficLogFile = traffic;
OutChannel = oldout;
break;
-
+
case CMDRESTART: /* restart the daemon */
- if (SaveArgv[0][0] != '/')
- {
- fprintf(s, "ERROR: could not restart: need full path\r\n");
- break;
- }
- if (LogLevel > 3)
- sm_syslog(LOG_INFO, NOQID,
- "restarting %s on due to control command",
- SaveArgv[0]);
- closecontrolsocket(FALSE);
- if (drop_privileges(TRUE) != EX_OK)
- {
- if (LogLevel > 0)
- sm_syslog(LOG_ALERT, NOQID,
- "could not set[ug]id(%d, %d): %m",
- RunAsUid, RunAsGid);
-
- fprintf(s, "ERROR: could not set[ug]id(%d, %d): %s, exiting...\r\n",
- (int)RunAsUid, (int)RunAsGid, errstring(errno));
- finis(FALSE, EX_OSERR);
- }
fprintf(s, "OK\r\n");
- clrcontrol();
- (void) fcntl(sock, F_SETFD, 1);
- execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
- if (LogLevel > 0)
- sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m",
- SaveArgv[0]);
- fprintf(s, "ERROR: could not exec %s: %s, exiting...\r\n",
- SaveArgv[0], errstring(errno));
- finis(FALSE, EX_OSFILE);
+ exitstat = EX_RESTART;
break;
case CMDSHUTDOWN: /* kill the daemon */
fprintf(s, "OK\r\n");
- finis(FALSE, EX_OK);
+ exitstat = EX_SHUTDOWN;
break;
case CMDSTATUS: /* daemon status */
proc_list_probe();
- fprintf(s, "%d/%d\r\n", CurChildren, MaxChildren);
+ {
+ long bsize;
+ long free;
+
+ free = freediskspace(QueueDir, &bsize);
+
+ /*
+ ** Prevent overflow and don't lose
+ ** precision (if bsize == 512)
+ */
+
+ free = (long)((double)free * ((double)bsize / 1024));
+
+ fprintf(s, "%d/%d/%ld/%d\r\n",
+ CurChildren, MaxChildren,
+ free, sm_getla(NULL));
+ }
proc_list_display(s);
break;
@@ -351,6 +353,10 @@ control_command(sock, e)
fprintf(s, "Bad command (%s)\r\n", cmdbuf);
break;
}
- fclose(s);
+ (void) fclose(s);
+ if (ev != NULL)
+ clrevent(ev);
+ exit(exitstat);
}
-#endif
+#endif /* ! NOT_SENDMAIL */
+
diff --git a/contrib/sendmail/src/convtime.c b/contrib/sendmail/src/convtime.c
index ab8591d..9bed853 100644
--- a/contrib/sendmail/src/convtime.c
+++ b/contrib/sendmail/src/convtime.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,10 +12,10 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)convtime.c 8.14 (Berkeley) 5/19/1998";
-#endif /* not lint */
+static char id[] = "@(#)$Id: convtime.c,v 8.25 1999/06/16 21:11:26 ca Exp $";
+#endif /* ! lint */
-# include "sendmail.h"
+#include <sendmail.h>
/*
** CONVTIME -- convert time
@@ -42,12 +43,14 @@ static char sccsid[] = "@(#)convtime.c 8.14 (Berkeley) 5/19/1998";
time_t
convtime(p, units)
char *p;
- char units;
+ int units;
{
register time_t t, r;
register char c;
r = 0;
+ if (strcasecmp(p, "now") == 0)
+ return NOW;
while (*p != '\0')
{
t = 0;
@@ -67,16 +70,21 @@ convtime(p, units)
{
case 'w': /* weeks */
t *= 7;
+ /* FALLTHROUGH */
case 'd': /* days */
+ /* FALLTHROUGH */
default:
t *= 24;
+ /* FALLTHROUGH */
case 'h': /* hours */
t *= 60;
+ /* FALLTHROUGH */
case 'm': /* minutes */
t *= 60;
+ /* FALLTHROUGH */
case 's': /* seconds */
break;
@@ -84,7 +92,7 @@ convtime(p, units)
r += t;
}
- return (r);
+ return r;
}
/*
** PINTVL -- produce printable version of a time interval
@@ -105,7 +113,7 @@ convtime(p, units)
** The string returned is in a static buffer.
*/
-# define PLURAL(n) ((n) == 1 ? "" : "s")
+#define PLURAL(n) ((n) == 1 ? "" : "s")
char *
pintvl(intvl, brief)
@@ -117,7 +125,9 @@ pintvl(intvl, brief)
int wk, dy, hr, mi, se;
if (intvl == 0 && !brief)
- return ("zero seconds");
+ return "zero seconds";
+ if (intvl == NOW)
+ return "too long";
/* decode the interval into weeks, days, hours, minutes, seconds */
se = intvl % 60;
@@ -149,7 +159,7 @@ pintvl(intvl, brief)
}
(void) snprintf(p, SPACELEFT(buf, p), "%02d:%02d:%02d",
hr, mi, se);
- return (buf);
+ return buf;
}
/* use the verbose form */
diff --git a/contrib/sendmail/src/daemon.c b/contrib/sendmail/src/daemon.c
index ae6b004..50da6c4 100644
--- a/contrib/sendmail/src/daemon.c
+++ b/contrib/sendmail/src/daemon.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -10,40 +11,78 @@
*
*/
-#include <errno.h>
-#include "sendmail.h"
+#include <sendmail.h>
+
#ifndef lint
-#ifdef DAEMON
-static char sccsid[] = "@(#)daemon.c 8.236 (Berkeley) 1/25/1999 (with daemon mode)";
-#else
-static char sccsid[] = "@(#)daemon.c 8.236 (Berkeley) 1/25/1999 (without daemon mode)";
-#endif
-#endif /* not lint */
+# ifdef DAEMON
+static char id[] = "@(#)$Id: daemon.c,v 8.401.4.14 2000/07/14 04:15:00 gshapiro Exp $ (with daemon mode)";
+# else /* DAEMON */
+static char id[] = "@(#)$Id: daemon.c,v 8.401.4.14 2000/07/14 04:15:00 gshapiro Exp $ (without daemon mode)";
+# endif /* DAEMON */
+#endif /* ! lint */
#if defined(SOCK_STREAM) || defined(__GNU_LIBRARY__)
# define USE_SOCK_STREAM 1
-#endif
+#endif /* defined(SOCK_STREAM) || defined(__GNU_LIBRARY__) */
#if DAEMON || defined(USE_SOCK_STREAM)
-# include <arpa/inet.h>
+# if NETINET || NETINET6
+# include <arpa/inet.h>
+# endif /* NETINET || NETINET6 */
# if NAMED_BIND
-# include <resolv.h>
# ifndef NO_DATA
# define NO_DATA NO_ADDRESS
-# endif
-# endif
-#endif
+# endif /* ! NO_DATA */
+# endif /* NAMED_BIND */
+#endif /* DAEMON || defined(USE_SOCK_STREAM) */
#if DAEMON
# include <sys/time.h>
-# if IP_SRCROUTE
+# if IP_SRCROUTE && NETINET
# include <netinet/in_systm.h>
# include <netinet/ip.h>
-# include <netinet/ip_var.h>
-# endif
+# 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 */
+struct daemon
+{
+ int d_socket; /* fd for socket */
+ SOCKADDR d_addr; /* socket for incoming */
+ u_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 */
+ time_t d_refuse_connections_until;
+ bool d_firsttime;
+ int d_socksize;
+ BITMAP256 d_flags; /* flags; see sendmail.h */
+ char *d_mflags; /* flags for use in macro */
+ char *d_name; /* user-supplied name */
+};
+
+typedef struct daemon DAEMON_T;
+
+static void connecttimeout __P((void));
+static int opendaemonsocket __P((struct daemon *, bool));
+static u_short setupdaemon __P((SOCKADDR *));
/*
** DAEMON.C -- routines to use when running as a daemon.
@@ -74,6 +113,14 @@ static char sccsid[] = "@(#)daemon.c 8.236 (Berkeley) 1/25/1999 (without daemon
** 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 */
+
+/* options for client */
+static int TcpRcvBufferSize = 0; /* size of TCP receive buffer */
+static int TcpSndBufferSize = 0; /* size of TCP send buffer */
+
/*
** GETREQUESTS -- open mail IPC port and get requests.
**
@@ -81,7 +128,7 @@ static char sccsid[] = "@(#)daemon.c 8.236 (Berkeley) 1/25/1999 (without daemon
** e -- the current envelope.
**
** Returns:
-** none.
+** pointer to flags.
**
** Side Effects:
** Waits until some interesting activity occurs. When
@@ -92,89 +139,49 @@ static char sccsid[] = "@(#)daemon.c 8.236 (Berkeley) 1/25/1999 (without daemon
** to the communication channel.
*/
-int DaemonSocket = -1; /* fd describing socket */
-SOCKADDR DaemonAddr; /* socket for incoming */
-int ListenQueueSize = 10; /* size of listen queue */
-int TcpRcvBufferSize = 0; /* size of TCP receive buffer */
-int TcpSndBufferSize = 0; /* size of TCP send buffer */
-
-void
+BITMAP256 *
getrequests(e)
ENVELOPE *e;
{
int t;
- time_t refuse_connections_until = 0;
- bool firsttime = TRUE;
- FILE *pidf;
- int sff;
- int socksize;
- u_short port;
-#if XDEBUG
+ time_t last_disk_space_check = 0;
+ int idx, curdaemon = -1;
+ int i, olddaemon = 0;
+# if XDEBUG
bool j_has_dot;
-#endif
+# endif /* XDEBUG */
char status[MAXLINE];
- extern void reapchild __P((int));
-#ifdef NETUNIX
+ SOCKADDR sa;
+ SOCKADDR_LEN_T len = sizeof sa;
+# if NETUNIX
extern int ControlSocket;
-#endif
- extern int opendaemonsocket __P((bool));
- extern int opencontrolsocket __P((void));
+# endif /* NETUNIX */
+ extern ENVELOPE BlankEnvelope;
- /*
- ** Set up the address for the mailer.
- */
+#define D(x,idx) x[idx]
- switch (DaemonAddr.sa.sa_family)
- {
- case AF_UNSPEC:
- DaemonAddr.sa.sa_family = AF_INET;
- /* fall through ... */
- 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;
-
- default:
- /* unknown protocol */
- port = 0;
- break;
- }
- if (port == 0)
+ for (idx = 0; idx < ndaemons; idx++)
{
- register struct servent *sp;
-
- sp = getservbyname("smtp", "tcp");
- if (sp == NULL)
- {
- syserr("554 service \"smtp\" unknown");
- port = htons(25);
- }
- else
- port = sp->s_port;
+ Daemons[idx].d_port = setupdaemon(&(Daemons[idx].d_addr));
+ Daemons[idx].d_firsttime = TRUE;
+ Daemons[idx].d_refuse_connections_until = (time_t) 0;
}
-
- switch (DaemonAddr.sa.sa_family)
- {
- case AF_INET:
- DaemonAddr.sin.sin_port = port;
- break;
-
- default:
- /* unknown protocol */
- break;
- }
-
/*
** Try to actually open the connection.
*/
if (tTd(15, 1))
- printf("getrequests: port 0x%x\n", port);
+ {
+ for (idx = 0; idx < ndaemons; idx++)
+ dprintf("getrequests: daemon %s: port %d\n",
+ Daemons[idx].d_name,
+ ntohs(Daemons[idx].d_port));
+ }
/* get a socket for the SMTP connection */
- socksize = opendaemonsocket(TRUE);
+ for (idx = 0; idx < ndaemons; idx++)
+ Daemons[idx].d_socksize = opendaemonsocket(&Daemons[idx], TRUE);
if (opencontrolsocket() < 0)
sm_syslog(LOG_WARNING, NOQID,
@@ -183,43 +190,28 @@ getrequests(e)
(void) setsignal(SIGCHLD, reapchild);
- /* write the pid to the log file for posterity */
- sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
- if (TrustedUid != 0 && RealUid == TrustedUid)
- sff |= SFF_OPENASROOT;
- pidf = safefopen(PidFile, O_WRONLY|O_TRUNC, 0644, sff);
- if (pidf == NULL)
- {
- sm_syslog(LOG_ERR, NOQID, "unable to write %s", PidFile);
- }
- else
- {
- extern char *CommandLineArgs;
-
- /* write the process id on line 1 */
- fprintf(pidf, "%ld\n", (long) getpid());
-
- /* line 2 contains all command line flags */
- fprintf(pidf, "%s\n", CommandLineArgs);
+ /* write the pid to file */
+ log_sendmail_pid(e);
- /* flush and close */
- fclose(pidf);
- }
-
-#if XDEBUG
+# if XDEBUG
{
char jbuf[MAXHOSTNAMELEN];
expand("\201j", jbuf, sizeof jbuf, e);
j_has_dot = strchr(jbuf, '.') != NULL;
}
-#endif
+# endif /* XDEBUG */
/* Add parent process as first item */
- proc_list_add(getpid(), "Sendmail daemon");
+ proc_list_add(getpid(), "Sendmail daemon", PROC_DAEMON);
if (tTd(15, 1))
- printf("getrequests: %d\n", DaemonSocket);
+ {
+ for (idx = 0; idx < ndaemons; idx++)
+ dprintf("getrequests: daemon %s: %d\n",
+ Daemons[idx].d_name,
+ Daemons[idx].d_socket);
+ }
for (;;)
{
@@ -227,59 +219,96 @@ getrequests(e)
auto SOCKADDR_LEN_T lotherend;
bool timedout = FALSE;
bool control = FALSE;
- int savederrno;
+ int save_errno;
int pipefd[2];
- extern bool refuseconnections __P((int));
/* see if we are rejecting connections */
(void) blocksignal(SIGALRM);
- if (curtime() >= refuse_connections_until)
+
+ for (idx = 0; idx < ndaemons; idx++)
{
- if (refuseconnections(ntohs(port)))
+ if (curtime() < Daemons[idx].d_refuse_connections_until)
+ continue;
+ if (refuseconnections(Daemons[idx].d_name, e, idx))
{
- if (DaemonSocket >= 0)
+ if (Daemons[idx].d_socket >= 0)
{
/* close socket so peer fails quickly */
- (void) close(DaemonSocket);
- DaemonSocket = -1;
+ (void) close(Daemons[idx].d_socket);
+ Daemons[idx].d_socket = -1;
}
/* refuse connections for next 15 seconds */
- refuse_connections_until = curtime() + 15;
+ Daemons[idx].d_refuse_connections_until = curtime() + 15;
}
- else if (DaemonSocket < 0 || firsttime)
+ else if (Daemons[idx].d_socket < 0 ||
+ Daemons[idx].d_firsttime)
{
+ if (!Daemons[idx].d_firsttime && LogLevel >= 9)
+ sm_syslog(LOG_INFO, NOQID,
+ "accepting connections again for daemon %s",
+ Daemons[idx].d_name);
+
/* arrange to (re)open the socket if needed */
- (void) opendaemonsocket(FALSE);
- firsttime = FALSE;
+ (void) opendaemonsocket(&Daemons[idx], FALSE);
+ Daemons[idx].d_firsttime = FALSE;
+ }
+ }
+
+ if (curtime() >= last_disk_space_check)
+ {
+ if (!enoughdiskspace(MinBlocksFree + 1, FALSE))
+ {
+ if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags))
+ {
+ /* log only if not logged before */
+ if (LogLevel >= 9)
+ sm_syslog(LOG_INFO, NOQID,
+ "rejecting new messages: min free: %d",
+ MinBlocksFree);
+ sm_setproctitle(TRUE, e,
+ "rejecting new messages: min free: %d",
+ MinBlocksFree);
+ setbitn(D_ETRNONLY, Daemons[idx].d_flags);
+ }
+ }
+ else if (bitnset(D_ETRNONLY, Daemons[idx].d_flags))
+ {
+ /* log only if not logged before */
+ if (LogLevel >= 9)
+ sm_syslog(LOG_INFO, NOQID,
+ "accepting new messages (again)");
+ /* title will be set below */
+ clrbitn(D_ETRNONLY, Daemons[idx].d_flags);
}
+ /* only check disk space once a minute */
+ last_disk_space_check = curtime() + 60;
}
-#if XDEBUG
+# if XDEBUG
/* check for disaster */
{
char jbuf[MAXHOSTNAMELEN];
- extern void dumpstate __P((char *));
expand("\201j", jbuf, sizeof jbuf, e);
if (!wordinclass(jbuf, 'w'))
{
dumpstate("daemon lost $j");
sm_syslog(LOG_ALERT, NOQID,
- "daemon process doesn't have $j in $=w; see syslog");
+ "daemon process doesn't have $j in $=w; see syslog");
abort();
}
else if (j_has_dot && strchr(jbuf, '.') == NULL)
{
dumpstate("daemon $j lost dot");
sm_syslog(LOG_ALERT, NOQID,
- "daemon process $j lost dot; see syslog");
+ "daemon process $j lost dot; see syslog");
abort();
}
}
-#endif
+# 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,
@@ -289,8 +318,9 @@ getrequests(e)
if (DaemonSocket >= 0 &&
SetNonBlocking(DaemonSocket, FALSE) < 0)
log an error here;
-#endif
+# endif /* 0 */
(void) releasesignal(SIGALRM);
+
for (;;)
{
int highest = -1;
@@ -299,32 +329,47 @@ getrequests(e)
FD_ZERO(&readfds);
- /* wait for a connection */
- if (DaemonSocket >= 0)
+ for (idx = 0; idx < ndaemons; idx++)
{
- sm_setproctitle(TRUE,
- "accepting connections on port %d",
- ntohs(port));
- if (DaemonSocket > highest)
- highest = DaemonSocket;
- FD_SET(DaemonSocket, &readfds);
+ /* wait for a connection */
+ if (Daemons[idx].d_socket >= 0)
+ {
+ if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags))
+ {
+ sm_setproctitle(TRUE, e,
+ "accepting connections");
+ }
+ if (Daemons[idx].d_socket > highest)
+ highest = Daemons[idx].d_socket;
+ FD_SET((u_int)Daemons[idx].d_socket, &readfds);
+ }
}
-#ifdef NETUNIX
+
+# if NETUNIX
if (ControlSocket >= 0)
{
if (ControlSocket > highest)
highest = ControlSocket;
FD_SET(ControlSocket, &readfds);
}
-#endif
- if (DaemonSocket >= 0)
- timeout.tv_sec = 60;
- else
+# endif /* NETUNIX */
+
+ /*
+ ** if one socket is closed, set the timeout
+ ** to 5 seconds (so it might get reopened soon),
+ ** otherwise (all sockets open) 60.
+ */
+ idx = 0;
+ while (idx < ndaemons && Daemons[idx].d_socket >= 0)
+ idx++;
+ if (idx < ndaemons)
timeout.tv_sec = 5;
+ else
+ timeout.tv_sec = 60;
timeout.tv_usec = 0;
t = select(highest + 1, FDSET_CAST &readfds,
- NULL, NULL, &timeout);
+ NULL, NULL, &timeout);
if (DoQueueRun)
(void) runqueue(TRUE, FALSE);
@@ -336,16 +381,28 @@ getrequests(e)
control = FALSE;
errno = 0;
- if (DaemonSocket >= 0 &&
- FD_ISSET(DaemonSocket, &readfds))
+ curdaemon = -1;
+
+ /* look "round-robin" for an active socket */
+ if ((idx = olddaemon + 1) >= ndaemons)
+ idx = 0;
+ for (i = 0; i < ndaemons; i++)
{
- lotherend = socksize;
- t = accept(DaemonSocket,
- (struct sockaddr *)&RealHostAddr,
- &lotherend);
+ if (Daemons[idx].d_socket >= 0 &&
+ FD_ISSET(Daemons[idx].d_socket, &readfds))
+ {
+ lotherend = Daemons[idx].d_socksize;
+ t = accept(Daemons[idx].d_socket,
+ (struct sockaddr *)&RealHostAddr,
+ &lotherend);
+ olddaemon = curdaemon = idx;
+ break;
+ }
+ if (++idx >= ndaemons)
+ idx = 0;
}
-#ifdef NETUNIX
- else if (ControlSocket >= 0 &&
+# if NETUNIX
+ if (curdaemon == -1 && ControlSocket >= 0 &&
FD_ISSET(ControlSocket, &readfds))
{
struct sockaddr_un sa_un;
@@ -356,7 +413,7 @@ getrequests(e)
&lotherend);
control = TRUE;
}
-#endif
+# endif /* NETUNIX */
if (t >= 0 || errno != EINTR)
break;
}
@@ -365,37 +422,94 @@ getrequests(e)
timedout = FALSE;
continue;
}
- if (control)
- {
- if (t >= 0)
- {
- extern void control_command __P((int, ENVELOPE *));
-
- control_command(t, e);
- }
- else
- syserr("getrequests: control accept");
- continue;
- }
- savederrno = errno;
+ save_errno = errno;
(void) blocksignal(SIGALRM);
if (t < 0)
{
- errno = savederrno;
+ errno = save_errno;
syserr("getrequests: accept");
/* arrange to re-open the socket next time around */
- (void) close(DaemonSocket);
- DaemonSocket = -1;
+ (void) close(Daemons[curdaemon].d_socket);
+ Daemons[curdaemon].d_socket = -1;
+# if SO_REUSEADDR_IS_BROKEN
+ /*
+ ** Give time for bound socket to be released.
+ ** This creates a denial-of-service if you can
+ ** force accept() to fail on affected systems.
+ */
+
+ Daemons[curdaemon].d_refuse_connections_until = curtime() + 15;
+# endif /* SO_REUSEADDR_IS_BROKEN */
continue;
}
+ if (!control)
+ {
+ /* set some daemon related macros */
+ switch (Daemons[curdaemon].d_addr.sa.sa_family)
+ {
+ case AF_UNSPEC:
+ define(macid("{daemon_family}", NULL),
+ "unspec", &BlankEnvelope);
+ break;
+# if NETINET
+ case AF_INET:
+ define(macid("{daemon_family}", NULL),
+ "inet", &BlankEnvelope);
+ break;
+# endif /* NETINET */
+# if NETINET6
+ case AF_INET6:
+ define(macid("{daemon_family}", NULL),
+ "inet6", &BlankEnvelope);
+ break;
+# endif /* NETINET6 */
+# if NETISO
+ case AF_ISO:
+ define(macid("{daemon_family}", NULL),
+ "iso", &BlankEnvelope);
+ break;
+# endif /* NETISO */
+# if NETNS
+ case AF_NS:
+ define(macid("{daemon_family}", NULL),
+ "ns", &BlankEnvelope);
+ break;
+# endif /* NETNS */
+# if NETX25
+ case AF_CCITT:
+ define(macid("{daemon_family}", NULL),
+ "x.25", &BlankEnvelope);
+ break;
+# endif /* NETX25 */
+ }
+ define(macid("{daemon_name}", NULL),
+ Daemons[curdaemon].d_name, &BlankEnvelope);
+ if (Daemons[curdaemon].d_mflags != NULL)
+ define(macid("{daemon_flags}", NULL),
+ Daemons[curdaemon].d_mflags,
+ &BlankEnvelope);
+ else
+ define(macid("{daemon_flags}", NULL),
+ "", &BlankEnvelope);
+ }
+
/*
** Create a subprocess to process the mail.
*/
if (tTd(15, 2))
- printf("getrequests: forking (fd = %d)\n", t);
+ dprintf("getrequests: forking (fd = %d)\n", t);
+
+ /*
+ ** 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.
+ */
+ (void) get_random();
/*
** Create a pipe to keep the child from writing to the
@@ -406,7 +520,7 @@ getrequests(e)
if (pipe(pipefd) < 0)
pipefd[0] = pipefd[1] = -1;
- blocksignal(SIGCHLD);
+ (void) blocksignal(SIGCHLD);
pid = fork();
if (pid < 0)
{
@@ -417,7 +531,7 @@ getrequests(e)
(void) close(pipefd[1]);
}
(void) releasesignal(SIGCHLD);
- sleep(10);
+ (void) sleep(10);
(void) close(t);
continue;
}
@@ -425,8 +539,7 @@ getrequests(e)
if (pid == 0)
{
char *p;
- extern SIGFUNC_DECL intsig __P((int));
- FILE *inchannel, *outchannel;
+ FILE *inchannel, *outchannel = NULL;
/*
** CHILD -- return to caller.
@@ -434,22 +547,49 @@ getrequests(e)
** Verify calling user id if possible here.
*/
+ if (!control)
+ {
+ define(macid("{daemon_addr}", NULL),
+ newstr(anynet_ntoa(&Daemons[curdaemon].d_addr)),
+ &BlankEnvelope);
+ (void) snprintf(status, sizeof status, "%d",
+ ntohs(Daemons[curdaemon].d_port));
+ define(macid("{daemon_port}", NULL),
+ newstr(status), &BlankEnvelope);
+ }
+
(void) releasesignal(SIGALRM);
(void) releasesignal(SIGCHLD);
(void) setsignal(SIGCHLD, SIG_DFL);
(void) setsignal(SIGHUP, intsig);
- (void) close(DaemonSocket);
+ for (idx = 0; idx < ndaemons; idx++)
+ {
+ if (Daemons[idx].d_socket >= 0)
+ (void) close(Daemons[idx].d_socket);
+ }
clrcontrol();
- proc_list_clear();
- /* Add parent process as first child item */
- proc_list_add(getpid(), "daemon child");
+ /* Avoid SMTP daemon actions if control command */
+ if (control)
+ {
+ /* Add control socket process */
+ proc_list_add(getpid(), "console socket child",
+ PROC_CONTROL_CHILD);
+ }
+ else
+ {
+ proc_list_clear();
- /* don't schedule queue runs if we are told to ETRN */
- QueueIntvl = 0;
+ /* Add parent process as first child item */
+ proc_list_add(getpid(), "daemon child",
+ PROC_DAEMON_CHILD);
- sm_setproctitle(TRUE, "startup with %s",
- anynet_ntoa(&RealHostAddr));
+ /* don't schedule queue runs if ETRN */
+ QueueIntvl = 0;
+
+ sm_setproctitle(TRUE, e, "startup with %s",
+ anynet_ntoa(&RealHostAddr));
+ }
if (pipefd[0] != -1)
{
@@ -473,12 +613,31 @@ getrequests(e)
(void) close(pipefd[0]);
}
+ /* 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)
p[MAXNAME] = '\0';
RealHostName = newstr(p);
- sm_setproctitle(TRUE, "startup with %s", p);
+ if (RealHostName[0] == '[')
+ {
+ /* TEMP, FAIL: which one? */
+ define(macid("{client_resolve}", NULL),
+ (h_errno == TRY_AGAIN) ? "TEMP" : "FAIL",
+ &BlankEnvelope);
+ }
+ else
+ define(macid("{client_resolve}", NULL), "OK",
+ &BlankEnvelope);
+ sm_setproctitle(TRUE, e, "startup with %s", p);
if ((inchannel = fdopen(t, "r")) == NULL ||
(t = dup(t)) < 0 ||
@@ -492,44 +651,107 @@ getrequests(e)
OutChannel = outchannel;
DisConnected = FALSE;
-#ifdef XLA
+# ifdef XLA
if (!xla_host_ok(RealHostName))
{
- message("421 Too many SMTP sessions for this host");
+ message("421 4.4.5 Too many SMTP sessions for this host");
finis(FALSE, EX_OK);
}
-#endif
+# endif /* XLA */
+ /* find out name for interface of connection */
+ if (getsockname(fileno(InChannel), &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);
+
+ /* 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);
+ 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));
+ }
+ else
+ {
+ define(macid("{if_addr}", NULL), NULL,
+ &BlankEnvelope);
+ define(macid("{if_family}", NULL), NULL,
+ &BlankEnvelope);
+ }
+ }
+ 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);
+ }
break;
}
/* parent -- keep track of children */
- snprintf(status, sizeof status, "SMTP server child for %s",
- anynet_ntoa(&RealHostAddr));
- proc_list_add(pid, status);
+ if (control)
+ {
+ snprintf(status, sizeof status, "control socket server child");
+ proc_list_add(pid, status, PROC_CONTROL);
+ }
+ else
+ {
+ snprintf(status, sizeof status,
+ "SMTP server child for %s",
+ anynet_ntoa(&RealHostAddr));
+ proc_list_add(pid, status, PROC_DAEMON);
+ }
(void) releasesignal(SIGCHLD);
/* close the read end of the synchronization pipe */
if (pipefd[0] != -1)
+ {
(void) close(pipefd[0]);
+ pipefd[0] = -1;
+ }
/* close the port so that others will hang (for a while) */
(void) close(t);
/* release the child by closing the read end of the sync pipe */
if (pipefd[1] != -1)
+ {
(void) close(pipefd[1]);
+ pipefd[1] = -1;
+ }
}
+
if (tTd(15, 2))
- printf("getreq: returning\n");
- return;
+ dprintf("getreq: returning\n");
+ return &Daemons[curdaemon].d_flags;
}
/*
-** OPENDAEMONSOCKET -- open the SMTP socket
+** OPENDAEMONSOCKET -- open SMTP socket
**
-** Deals with setting all appropriate options. DaemonAddr must
-** be set up in advance.
+** Deals with setting all appropriate options.
**
** Parameters:
+** d -- the structure for the daemon to open.
** firsttime -- set if this is the initial open.
**
** Returns:
@@ -540,103 +762,231 @@ 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 */
-int
-opendaemonsocket(firsttime)
+static int
+opendaemonsocket(d, firsttime)
+ struct daemon *d;
bool firsttime;
{
int on = 1;
- int socksize = 0;
+ int fdflags;
+ SOCKADDR_LEN_T socksize = 0;
int ntries = 0;
- int saveerrno;
+ int save_errno;
if (tTd(15, 2))
- printf("opendaemonsocket()\n");
+ dprintf("opendaemonsocket(%s)\n", d->d_name);
do
{
if (ntries > 0)
- sleep(5);
- if (firsttime || DaemonSocket < 0)
+ (void) sleep(5);
+ if (firsttime || d->d_socket < 0)
{
- DaemonSocket = socket(DaemonAddr.sa.sa_family, SOCK_STREAM, 0);
- if (DaemonSocket < 0)
+ d->d_socket = socket(d->d_addr.sa.sa_family,
+ SOCK_STREAM, 0);
+ if (d->d_socket < 0)
{
- saveerrno = errno;
- syserr("opendaemonsocket: can't create server SMTP socket");
+ save_errno = errno;
+ syserr("opendaemonsocket: daemon %s: can't create server SMTP socket", d->d_name);
severe:
if (LogLevel > 0)
sm_syslog(LOG_ALERT, NOQID,
- "problem creating SMTP socket");
- DaemonSocket = -1;
+ "daemon %s: problem creating SMTP socket", d->d_name);
+ d->d_socket = -1;
continue;
}
/* turn on network debugging? */
if (tTd(15, 101))
- (void) setsockopt(DaemonSocket, SOL_SOCKET,
+ (void) setsockopt(d->d_socket, SOL_SOCKET,
SO_DEBUG, (char *)&on,
sizeof on);
- (void) setsockopt(DaemonSocket, SOL_SOCKET,
+ (void) setsockopt(d->d_socket, SOL_SOCKET,
SO_REUSEADDR, (char *)&on, sizeof on);
- (void) setsockopt(DaemonSocket, SOL_SOCKET,
+ (void) setsockopt(d->d_socket, SOL_SOCKET,
SO_KEEPALIVE, (char *)&on, sizeof on);
-#ifdef SO_RCVBUF
- if (TcpRcvBufferSize > 0)
+# ifdef SO_RCVBUF
+ if (d->d_tcprcvbufsize > 0)
{
- if (setsockopt(DaemonSocket, SOL_SOCKET,
+ if (setsockopt(d->d_socket, SOL_SOCKET,
SO_RCVBUF,
- (char *) &TcpRcvBufferSize,
- sizeof(TcpRcvBufferSize)) < 0)
- syserr("opendaemonsocket: setsockopt(SO_RCVBUF)");
+ (char *) &d->d_tcprcvbufsize,
+ sizeof(d->d_tcprcvbufsize)) < 0)
+ syserr("opendaemonsocket: daemon %s: setsockopt(SO_RCVBUF)", d->d_name);
+ }
+# endif /* SO_RCVBUF */
+# ifdef SO_SNDBUF
+ if (d->d_tcpsndbufsize > 0)
+ {
+ if (setsockopt(d->d_socket, SOL_SOCKET,
+ SO_SNDBUF,
+ (char *) &d->d_tcpsndbufsize,
+ sizeof(d->d_tcpsndbufsize)) < 0)
+ syserr("opendaemonsocket: daemon %s: setsockopt(SO_SNDBUF)", d->d_name);
}
-#endif
+# endif /* SO_SNDBUF */
- switch (DaemonAddr.sa.sa_family)
+ if ((fdflags = fcntl(d->d_socket, F_GETFD, 0)) == -1 ||
+ fcntl(d->d_socket, F_SETFD,
+ fdflags | FD_CLOEXEC) == -1)
+ {
+ save_errno = errno;
+ syserr("opendaemonsocket: daemon %s: failed to %s close-on-exec flag: %s",
+ d->d_name,
+ fdflags == -1 ? "get" : "set",
+ errstring(save_errno));
+ (void) close(d->d_socket);
+ goto severe;
+ }
+
+ switch (d->d_addr.sa.sa_family)
{
# if NETINET
case AF_INET:
- socksize = sizeof DaemonAddr.sin;
+ socksize = sizeof d->d_addr.sin;
break;
-# endif
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ socksize = sizeof d->d_addr.sin6;
+ break;
+# endif /* NETINET6 */
# if NETISO
case AF_ISO:
- socksize = sizeof DaemonAddr.siso;
+ socksize = sizeof d->d_addr.siso;
break;
-# endif
+# endif /* NETISO */
default:
- socksize = sizeof DaemonAddr;
+ socksize = sizeof d->d_addr;
break;
}
- if (bind(DaemonSocket, &DaemonAddr.sa, socksize) < 0)
+ if (bind(d->d_socket, &d->d_addr.sa, socksize) < 0)
{
/* probably another daemon already */
- saveerrno = errno;
- syserr("opendaemonsocket: cannot bind");
- (void) close(DaemonSocket);
+ save_errno = errno;
+ syserr("opendaemonsocket: daemon %s: cannot bind",
+ d->d_name);
+ (void) close(d->d_socket);
goto severe;
}
}
- if (!firsttime && listen(DaemonSocket, ListenQueueSize) < 0)
+ if (!firsttime &&
+ listen(d->d_socket, d->d_listenqueue) < 0)
{
- saveerrno = errno;
- syserr("opendaemonsocket: cannot listen");
- (void) close(DaemonSocket);
+ save_errno = errno;
+ syserr("opendaemonsocket: daemon %s: cannot listen",
+ d->d_name);
+ (void) close(d->d_socket);
goto severe;
}
return socksize;
- } while (ntries++ < MAXOPENTRIES && transienterror(saveerrno));
- syserr("!opendaemonsocket: server SMTP socket wedged: exiting");
- /*NOTREACHED*/
+ } while (ntries++ < MAXOPENTRIES && transienterror(save_errno));
+ syserr("!opendaemonsocket: daemon %s: server SMTP socket wedged: exiting",
+ d->d_name);
+ /* 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
+setupdaemon(daemonaddr)
+ SOCKADDR *daemonaddr;
+{
+ u_short port;
+
+ /*
+ ** Set up the address for the mailer.
+ */
+
+ if (daemonaddr->sa.sa_family == AF_UNSPEC)
+ {
+ memset(daemonaddr, '\0', sizeof *daemonaddr);
+# if NETINET
+ daemonaddr->sa.sa_family = AF_INET;
+# endif /* NETINET */
+ }
+
+ switch (daemonaddr->sa.sa_family)
+ {
+# 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 */
+
+# 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 */
+
+ default:
+ /* unknown protocol */
+ port = 0;
+ break;
+ }
+ if (port == 0)
+ {
+# ifdef NO_GETSERVBYNAME
+ port = htons(25);
+# else /* NO_GETSERVBYNAME */
+ {
+ register struct servent *sp;
+
+ sp = getservbyname("smtp", "tcp");
+ if (sp == NULL)
+ {
+ syserr("554 5.3.5 service \"smtp\" unknown");
+ port = htons(25);
+ }
+ else
+ port = sp->s_port;
+ }
+# endif /* NO_GETSERVBYNAME */
+ }
+
+ switch (daemonaddr->sa.sa_family)
+ {
+# if NETINET
+ case AF_INET:
+ daemonaddr->sin.sin_port = port;
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ daemonaddr->sin6.sin6_port = port;
+ break;
+# endif /* NETINET6 */
+
+ default:
+ /* unknown protocol */
+ break;
+ }
+ return(port);
+}
+ /*
** CLRDAEMON -- reset the daemon connection
**
** Parameters:
@@ -652,26 +1002,41 @@ opendaemonsocket(firsttime)
void
clrdaemon()
{
- if (DaemonSocket >= 0)
- (void) close(DaemonSocket);
- DaemonSocket = -1;
+ int i;
+
+ for (i = 0; i < ndaemons; i++)
+ {
+ if (Daemons[i].d_socket >= 0)
+ (void) close(Daemons[i].d_socket);
+ Daemons[i].d_socket = -1;
+ }
}
/*
-** SETDAEMONOPTIONS -- set options for running the daemon
+** SETSOCKADDROPTIONS -- set options for SOCKADDR (daemon or client)
**
** Parameters:
** p -- the options line.
+** d -- the daemon structure to fill in.
**
** Returns:
** none.
*/
-void
-setdaemonoptions(p)
+static void
+setsockaddroptions(p, d)
register char *p;
+ struct daemon *d;
{
- if (DaemonAddr.sa.sa_family == AF_UNSPEC)
- DaemonAddr.sa.sa_family = AF_INET;
+# if NETISO
+ short port;
+# endif /* NETISO */
+ int l;
+ char *h, *flags;
+
+# if NETINET
+ if (d->d_addr.sa.sa_family == AF_UNSPEC)
+ d->d_addr.sa.sa_family = AF_INET;
+# endif /* NETINET */
while (p != NULL)
{
@@ -698,123 +1063,366 @@ setdaemonoptions(p)
{
case 'F': /* address family */
if (isascii(*v) && isdigit(*v))
- DaemonAddr.sa.sa_family = atoi(v);
-#if NETINET
+ d->d_addr.sa.sa_family = atoi(v);
+# if NETINET
else if (strcasecmp(v, "inet") == 0)
- DaemonAddr.sa.sa_family = AF_INET;
-#endif
-#if NETISO
+ d->d_addr.sa.sa_family = AF_INET;
+# endif /* NETINET */
+# if NETINET6
+ else if (strcasecmp(v, "inet6") == 0)
+ d->d_addr.sa.sa_family = AF_INET6;
+# endif /* NETINET6 */
+# if NETISO
else if (strcasecmp(v, "iso") == 0)
- DaemonAddr.sa.sa_family = AF_ISO;
-#endif
-#if NETNS
+ d->d_addr.sa.sa_family = AF_ISO;
+# endif /* NETISO */
+# if NETNS
else if (strcasecmp(v, "ns") == 0)
- DaemonAddr.sa.sa_family = AF_NS;
-#endif
-#if NETX25
+ d->d_addr.sa.sa_family = AF_NS;
+# endif /* NETNS */
+# if NETX25
else if (strcasecmp(v, "x.25") == 0)
- DaemonAddr.sa.sa_family = AF_CCITT;
-#endif
+ d->d_addr.sa.sa_family = AF_CCITT;
+# endif /* NETX25 */
else
- syserr("554 Unknown address family %s in Family=option", v);
+ syserr("554 5.3.5 Unknown address family %s in Family=option",
+ v);
break;
case 'A': /* address */
- switch (DaemonAddr.sa.sa_family)
+ switch (d->d_addr.sa.sa_family)
{
-#if NETINET
+# if NETINET
case AF_INET:
- if (isascii(*v) && isdigit(*v))
- DaemonAddr.sin.sin_addr.s_addr = inet_addr(v);
- else
+ if (!isascii(*v) || !isdigit(*v) ||
+ ((d->d_addr.sin.sin_addr.s_addr = inet_addr(v)) == INADDR_NONE))
{
register struct hostent *hp;
- hp = sm_gethostbyname(v);
+ hp = sm_gethostbyname(v, AF_INET);
if (hp == NULL)
- syserr("554 host \"%s\" unknown", v);
+ syserr("554 5.3.0 host \"%s\" unknown",
+ v);
else
- bcopy(hp->h_addr, &DaemonAddr.sin.sin_addr, INADDRSZ);
+ {
+ while (*(hp->h_addr_list) &&
+ hp->h_addrtype != AF_INET)
+ hp->h_addr_list++;
+ if (*(hp->h_addr_list) == NULL)
+ syserr("554 5.3.0 host \"%s\" unknown",
+ v);
+ else
+ memmove(&d->d_addr.sin.sin_addr,
+ *(hp->h_addr_list),
+ INADDRSZ);
+ }
}
break;
-#endif
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ if (!isascii(*v) || !isxdigit(*v) ||
+ inet_pton(AF_INET6, v,
+ &d->d_addr.sin6.sin6_addr) != 1)
+ {
+ register struct hostent *hp;
+
+ hp = sm_gethostbyname(v, AF_INET6);
+ if (hp == NULL)
+ syserr("554 5.3.0 host \"%s\" unknown",
+ v);
+ else
+ {
+ while (*(hp->h_addr_list) &&
+ hp->h_addrtype != AF_INET6)
+ hp->h_addr_list++;
+ if (*(hp->h_addr_list) == NULL)
+ syserr("554 5.3.0 host \"%s\" unknown",
+ v);
+ else
+ memmove(&d->d_addr.sin6.sin6_addr,
+ *(hp->h_addr_list),
+ IN6ADDRSZ);
+ }
+ }
+ break;
+# endif /* NETINET6 */
default:
- syserr("554 Address= option unsupported for family %d",
- DaemonAddr.sa.sa_family);
+ syserr("554 5.3.5 address= option unsupported for family %d",
+ d->d_addr.sa.sa_family);
break;
}
break;
case 'P': /* port */
- switch (DaemonAddr.sa.sa_family)
+ switch (d->d_addr.sa.sa_family)
{
-#if NETISO
- short port;
-#endif
-
-#if NETINET
+# if NETINET
case AF_INET:
if (isascii(*v) && isdigit(*v))
- DaemonAddr.sin.sin_port = htons(atoi(v));
+ d->d_addr.sin.sin_port = htons((u_short)atoi((const char *)v));
else
{
+# ifdef NO_GETSERVBYNAME
+ syserr("554 5.3.5 invalid port number: %s",
+ v);
+# else /* NO_GETSERVBYNAME */
register struct servent *sp;
sp = getservbyname(v, "tcp");
if (sp == NULL)
- syserr("554 service \"%s\" unknown", v);
+ syserr("554 5.3.5 service \"%s\" unknown",
+ v);
else
- DaemonAddr.sin.sin_port = sp->s_port;
+ d->d_addr.sin.sin_port = sp->s_port;
+# endif /* NO_GETSERVBYNAME */
}
break;
-#endif
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ if (isascii(*v) && isdigit(*v))
+ d->d_addr.sin6.sin6_port = htons((u_short)atoi(v));
+ else
+ {
+# ifdef NO_GETSERVBYNAME
+ syserr("554 5.3.5 invalid port number: %s",
+ v);
+# else /* NO_GETSERVBYNAME */
+ register struct servent *sp;
-#if NETISO
+ sp = getservbyname(v, "tcp");
+ if (sp == NULL)
+ syserr("554 5.3.5 service \"%s\" unknown",
+ v);
+ else
+ d->d_addr.sin6.sin6_port = sp->s_port;
+# endif /* NO_GETSERVBYNAME */
+ }
+ break;
+# endif /* NETINET6 */
+
+# if NETISO
case AF_ISO:
/* assume two byte transport selector */
if (isascii(*v) && isdigit(*v))
- port = htons(atoi(v));
+ port = htons((u_short)atoi(v));
else
{
+# ifdef NO_GETSERVBYNAME
+ syserr("554 5.3.5 invalid port number: %s",
+ v);
+# else /* NO_GETSERVBYNAME */
register struct servent *sp;
sp = getservbyname(v, "tcp");
if (sp == NULL)
- syserr("554 service \"%s\" unknown", v);
+ syserr("554 5.3.5 service \"%s\" unknown",
+ v);
else
port = sp->s_port;
+# endif /* NO_GETSERVBYNAME */
}
- bcopy((char *) &port, TSEL(&DaemonAddr.siso), 2);
+ memmove(TSEL(&d->d_addr.siso),
+ (char *) &port, 2);
break;
-#endif
+# endif /* NETISO */
default:
- syserr("554 Port= option unsupported for family %d",
- DaemonAddr.sa.sa_family);
+ syserr("554 5.3.5 Port= option unsupported for family %d",
+ d->d_addr.sa.sa_family);
break;
}
break;
case 'L': /* listen queue size */
- ListenQueueSize = atoi(v);
+ d->d_listenqueue = atoi(v);
+ 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)
+ *f++ = ' ';
+ *flags++ = *h;
+ if (isupper(*h))
+ *flags++ = *h;
+ }
+ }
+ *flags++ = '\0';
+ for (; *v != '\0'; v++)
+ if (!(isascii(*v) && isspace(*v)))
+ setbitn(*v, d->d_flags);
break;
case 'S': /* send buffer size */
- TcpSndBufferSize = atoi(v);
+ d->d_tcpsndbufsize = atoi(v);
break;
case 'R': /* receive buffer size */
- TcpRcvBufferSize = atoi(v);
+ d->d_tcprcvbufsize = atoi(v);
+ break;
+
+ case 'N': /* name */
+ d->d_name = v;
break;
default:
- syserr("554 DaemonPortOptions parameter \"%s\" unknown", f);
+ syserr("554 5.3.5 PortOptions parameter \"%s\" unknown",
+ f);
}
}
}
/*
-** MAKECONNECTION -- make a connection to an SMTP socket on another machine.
+** SETDAEMONOPTIONS -- set options for running the MTA daemon
+**
+** Parameters:
+** p -- the options line.
+**
+** Returns:
+** TRUE if successful, FALSE otherwise.
+*/
+
+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);
+ else
+ {
+ char num[30];
+
+ 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");
+ }
+ ++ndaemons;
+ return TRUE;
+}
+ /*
+** INITDAEMON -- initialize daemon if not yet done.
+**
+** Parameters:
+** none
+**
+** Returns:
+** none
+**
+** Side Effects:
+** initializes structure for one daemon.
+*/
+void
+initdaemon()
+{
+ if (ndaemons == 0)
+ {
+ Daemons[ndaemons].d_socket = -1;
+ Daemons[ndaemons].d_listenqueue = 10;
+ Daemons[ndaemons].d_name = "Daemon0";
+ ndaemons = 1;
+ }
+}
+ /*
+** SETCLIENTOPTIONS -- set options for running the client
+**
+** Parameters:
+** p -- the options line.
+**
+** Returns:
+** none.
+*/
+
+static SOCKADDR ClientAddr; /* address for client */
+
+void
+setclientoptions(p)
+ register char *p;
+{
+ struct daemon d;
+ extern ENVELOPE BlankEnvelope;
+
+ 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);
+ else
+ define(macid("{client_flags}", NULL), "", &BlankEnvelope);
+}
+ /*
+** ADDR_FAMILY -- determine address family from address
+**
+** Parameters:
+** addr -- the string representation of the address
+**
+** Returns:
+** AF_INET, AF_INET6 or AF_UNSPEC
+**
+** Side Effects:
+** none.
+*/
+
+static int
+addr_family(addr)
+ char *addr;
+{
+# if NETINET6
+ SOCKADDR clt_addr;
+# endif /* NETINET6 */
+
+# if NETINET
+ if (inet_addr(addr) != INADDR_NONE)
+ {
+ if (tTd(16, 9))
+ printf("addr_family(%s): INET\n", addr);
+ return AF_INET;
+ }
+# endif /* NETINET */
+# if NETINET6
+ if (inet_pton(AF_INET6, addr, &clt_addr.sin6.sin6_addr) == 1)
+ {
+ if (tTd(16, 9))
+ printf("addr_family(%s): INET6\n", addr);
+ return AF_INET6;
+ }
+# endif /* NETINET6 */
+ if (tTd(16, 9))
+ printf("addr_family(%s): UNSPEC\n", addr);
+ return AF_UNSPEC;
+}
+ /*
+** MAKECONNECTION -- make a connection to an SMTP socket on a machine.
**
** Parameters:
** host -- the name of the host.
@@ -833,19 +1441,12 @@ setdaemonoptions(p)
static jmp_buf CtxConnectTimeout;
-static void
-connecttimeout()
-{
- errno = ETIMEDOUT;
- longjmp(CtxConnectTimeout, 1);
-}
-
SOCKADDR CurHostAddr; /* address of current host */
int
makeconnection(host, port, mci, e)
char *host;
- u_short port;
+ volatile u_int port;
register MCI *mci;
ENVELOPE *e;
{
@@ -853,54 +1454,221 @@ makeconnection(host, port, mci, e)
register volatile int s;
register struct hostent *volatile hp = (struct hostent *)NULL;
SOCKADDR addr;
- int sav_errno;
- volatile int addrlen;
+ 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 */
+ volatile int family = InetMode;
+ SOCKADDR_LEN_T len;
+ volatile SOCKADDR_LEN_T socksize = 0;
+ volatile bool clt_bind;
+ BITMAP256 d_flags;
+ char *p;
+ extern ENVELOPE BlankEnvelope;
+
+ /* retranslate ${daemon_flags} into bitmap */
+ clrbitmap(d_flags);
+ if ((p = macvalue(macid("{daemon_flags}", NULL), e)) != NULL)
+ {
+ for (; *p != '\0'; p++)
+ {
+ if (!(isascii(*p) && isspace(*p)))
+ setbitn(*p, d_flags);
+ }
+ }
+
+ /* "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(*p, d_flags);
+ break;
+ }
+ }
+ }
+
+# if NETINET6
+ v4retry:
+# 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)
+ {
+# if NETINET6
+ char p6[INET6_ADDRSTRLEN];
+# endif /* NETINET6 */
+
+ memset(&clt_addr, '\0', sizeof clt_addr);
+
+ /* infer the address family from the address itself */
+ clt_addr.sa.sa_family = addr_family(p);
+ switch (clt_addr.sa.sa_family)
+ {
+# if NETINET
+ case AF_INET:
+ if ((clt_addr.sin.sin_addr.s_addr = inet_addr(p))
+ != INADDR_NONE)
+ {
+ clt_bind = TRUE;
+ socksize = sizeof (struct sockaddr_in);
+ }
+ else if (clt_addr.sin.sin_port != 0)
+ {
+ clt_addr.sin.sin_addr.s_addr = INADDR_ANY;
+ clt_bind = TRUE;
+ socksize = sizeof (struct sockaddr_in);
+ }
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ if (inet_addr(p) != INADDR_NONE)
+ snprintf(p6, sizeof p6, "::ffff:%s", p);
+ else
+ strlcpy(p6, p, sizeof p6);
+ if (inet_pton(AF_INET6, p6,
+ &clt_addr.sin6.sin6_addr) == 1)
+ {
+ clt_bind = TRUE;
+ socksize = sizeof (struct sockaddr_in6);
+ }
+ else if (clt_addr.sin6.sin6_port != 0)
+ {
+ if (IN6_IS_ADDR_UNSPECIFIED(&clt_addr.sin6.sin6_addr))
+ clt_addr.sin6.sin6_addr = in6addr_any;
+ clt_bind = TRUE;
+ socksize = sizeof (struct sockaddr_in6);
+ }
+ break;
+# endif /* NETINET6 */
+
+# if 0
+ default:
+ syserr("554 5.3.5 Address= option unsupported for family %d",
+ clt_addr.sa.sa_family);
+ break;
+# endif /* 0 */
+ }
+ if (clt_bind)
+ family = clt_addr.sa.sa_family;
+ }
+ else
+ {
+ STRUCTCOPY(ClientAddr, clt_addr);
+ if (clt_addr.sa.sa_family == AF_UNSPEC)
+ clt_addr.sa.sa_family = InetMode;
+ switch (clt_addr.sa.sa_family)
+ {
+# 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;
+ if (clt_addr.sin.sin_port != 0)
+ clt_bind = TRUE;
+ socksize = sizeof (struct sockaddr_in);
+ break;
+# 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;
+ socksize = sizeof (struct sockaddr_in6);
+ if (clt_addr.sin6.sin6_port != 0)
+ clt_bind = TRUE;
+ break;
+# endif /* NETINET6 */
+# if NETISO
+ case AF_ISO:
+ socksize = sizeof clt_addr.siso;
+ clt_bind = TRUE;
+ break;
+# endif /* NETISO */
+ default:
+ break;
+ }
+ }
/*
** Set up the address for the mailer.
** Accept "[a.b.c.d]" syntax for host name.
*/
-#if NAMED_BIND
+# if NAMED_BIND
h_errno = 0;
-#endif
+# endif /* NAMED_BIND */
errno = 0;
- bzero(&CurHostAddr, sizeof CurHostAddr);
+ memset(&CurHostAddr, '\0', sizeof CurHostAddr);
+ memset(&addr, '\0', sizeof addr);
SmtpPhase = mci->mci_phase = "initial connection";
CurHostName = host;
if (host[0] == '[')
{
-#if NETINET
- unsigned long hid = INADDR_NONE;
-#endif
- register char *p = strchr(host, ']');
-
+ p = strchr(host, ']');
if (p != NULL)
{
+# if NETINET
+ unsigned long hid = INADDR_NONE;
+# endif /* NETINET */
+# if NETINET6
+ struct sockaddr_in6 hid6;
+# endif /* NETINET6 */
+
*p = '\0';
-#if NETINET
- hid = inet_addr(&host[1]);
- if (hid == INADDR_NONE)
-#endif
+# if NETINET6
+ memset(&hid6, '\0', sizeof hid6);
+# endif /* NETINET6 */
+# if NETINET
+ if (family == AF_INET &&
+ (hid = inet_addr(&host[1])) != INADDR_NONE)
+ {
+ addr.sin.sin_family = AF_INET;
+ addr.sin.sin_addr.s_addr = hid;
+ }
+ else
+# endif /* NETINET */
+# if NETINET6
+ if (family == AF_INET6 &&
+ inet_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 */
{
/* try it as a host name (avoid MX lookup) */
- hp = sm_gethostbyname(&host[1]);
+ 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
+# endif /* NAMED_BIND */
p[-1] = '\0';
- hp = sm_gethostbyname(&host[1]);
+ hp = sm_gethostbyname(&host[1],
+ family);
p[-1] = '.';
-#if NAMED_BIND
+# if NAMED_BIND
_res.options = oldopts;
-#endif
+# endif /* NAMED_BIND */
}
*p = ']';
goto gothostent;
@@ -911,62 +1679,87 @@ makeconnection(host, port, mci, e)
{
extern char MsgBuf[];
- usrerr("553 Invalid numeric domain spec \"%s\"", host);
+ usrerrenh("5.1.2",
+ "553 Invalid numeric domain spec \"%s\"",
+ host);
mci_setstat(mci, EX_NOHOST, "5.1.2", MsgBuf);
+ errno = EINVAL;
return EX_NOHOST;
}
-#if NETINET
- addr.sin.sin_family = AF_INET; /*XXX*/
- addr.sin.sin_addr.s_addr = hid;
-#endif
}
else
{
/* contortion to get around SGI cc complaints */
{
- register char *p = &host[strlen(host) - 1];
-
- hp = sm_gethostbyname(host);
+ p = &host[strlen(host) - 1];
+ 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
+# endif /* NAMED_BIND */
*p = '\0';
- hp = sm_gethostbyname(host);
+ hp = sm_gethostbyname(host, family);
*p = '.';
-#if NAMED_BIND
+# if NAMED_BIND
_res.options = oldopts;
-#endif
+# 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))
{
+ save_errno = errno;
mci_setstat(mci, EX_TEMPFAIL, "4.4.3", NULL);
+ errno = save_errno;
return EX_TEMPFAIL;
}
-#endif
+# endif /* NAMED_BIND */
+# if NETINET6
+ /*
+ ** Try v6 first, then fall back to v4.
+ ** If we found a v6 address, but no v4
+ ** addresses, then TEMPFAIL.
+ */
+
+ if (family == AF_INET6)
+ {
+ family = AF_INET;
+ goto v4retry;
+ }
+ if (v6found)
+ goto v6tempfail;
+# endif /* NETINET6 */
+ save_errno = errno;
mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
- return (EX_NOHOST);
+ errno = save_errno;
+ return EX_NOHOST;
}
addr.sa.sa_family = hp->h_addrtype;
switch (hp->h_addrtype)
{
-#if NETINET
+# if NETINET
case AF_INET:
- bcopy(hp->h_addr,
- &addr.sin.sin_addr,
+ memmove(&addr.sin.sin_addr,
+ hp->h_addr,
INADDRSZ);
break;
-#endif
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ memmove(&addr.sin6.sin6_addr,
+ hp->h_addr,
+ IN6ADDRSZ);
+ break;
+# endif /* NETINET6 */
default:
if (hp->h_length > sizeof addr.sa.sa_data)
@@ -974,10 +1767,11 @@ gothostent:
syserr("makeconnection: long sa_data: family %d len %d",
hp->h_addrtype, hp->h_length);
mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
+ errno = EINVAL;
return EX_NOHOST;
}
- bcopy(hp->h_addr,
- addr.sa.sa_data,
+ memmove(addr.sa.sa_data,
+ hp->h_addr,
hp->h_length);
break;
}
@@ -990,57 +1784,69 @@ gothostent:
if (port == 0)
{
+# ifdef NO_GETSERVBYNAME
+ port = htons(25);
+# else /* NO_GETSERVBYNAME */
register struct servent *sp = getservbyname("smtp", "tcp");
if (sp == NULL)
{
if (LogLevel > 2)
sm_syslog(LOG_ERR, NOQID,
- "makeconnection: service \"smtp\" unknown");
+ "makeconnection: service \"smtp\" unknown");
port = htons(25);
}
else
port = sp->s_port;
+# endif /* NO_GETSERVBYNAME */
}
switch (addr.sa.sa_family)
{
-#if NETINET
+# if NETINET
case AF_INET:
addr.sin.sin_port = port;
addrlen = sizeof (struct sockaddr_in);
break;
-#endif
+# endif /* NETINET */
-#if NETISO
+# if NETINET6
+ case AF_INET6:
+ addr.sin6.sin6_port = port;
+ addrlen = sizeof (struct sockaddr_in6);
+ break;
+# endif /* NETINET6 */
+
+# if NETISO
case AF_ISO:
/* assume two byte transport selector */
- bcopy((char *) &port, TSEL((struct sockaddr_iso *) &addr), 2);
+ memmove(TSEL((struct sockaddr_iso *) &addr), (char *) &port, 2);
addrlen = sizeof (struct sockaddr_iso);
break;
-#endif
+# endif /* NETISO */
default:
syserr("Can't connect to address family %d", addr.sa.sa_family);
mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
- return (EX_NOHOST);
+ errno = EINVAL;
+ return EX_NOHOST;
}
/*
** Try to actually open the connection.
*/
-#ifdef XLA
+# ifdef XLA
/* if too many connections, don't bother trying */
if (!xla_noqueue_ok(host))
return EX_TEMPFAIL;
-#endif
+# endif /* XLA */
firstconnect = TRUE;
for (;;)
{
if (tTd(16, 1))
- printf("makeconnection (%s [%s])\n",
+ dprintf("makeconnection (%s [%s])\n",
host, anynet_ntoa(&addr));
/* save for logging */
@@ -1058,16 +1864,17 @@ gothostent:
}
if (s < 0)
{
- sav_errno = errno;
+ save_errno = errno;
syserr("makeconnection: cannot create socket");
-#ifdef XLA
+# ifdef XLA
xla_host_end(host);
-#endif
+# endif /* XLA */
mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
+ errno = save_errno;
return EX_TEMPFAIL;
}
-#ifdef SO_SNDBUF
+# ifdef SO_SNDBUF
if (TcpSndBufferSize > 0)
{
if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
@@ -1075,21 +1882,71 @@ gothostent:
sizeof(TcpSndBufferSize)) < 0)
syserr("makeconnection: setsockopt(SO_SNDBUF)");
}
-#endif
+# endif /* SO_SNDBUF */
+# ifdef SO_RCVBUF
+ if (TcpRcvBufferSize > 0)
+ {
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
+ (char *) &TcpRcvBufferSize,
+ sizeof(TcpRcvBufferSize)) < 0)
+ syserr("makeconnection: setsockopt(SO_RCVBUF)");
+ }
+# endif /* SO_RCVBUF */
+
if (tTd(16, 1))
- printf("makeconnection: fd=%d\n", s);
+ dprintf("makeconnection: fd=%d\n", s);
/* turn on network debugging? */
if (tTd(16, 101))
{
int on = 1;
+
(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 */
+ (void) fflush(e->e_xfp); /* for debugging */
+ errno = 0; /* for debugging */
+
+ if (clt_bind)
+ {
+ int on = 1;
+
+ switch (clt_addr.sa.sa_family)
+ {
+# if NETINET
+ case AF_INET:
+ if (clt_addr.sin.sin_port != 0)
+ (void) setsockopt(s, SOL_SOCKET,
+ SO_REUSEADDR,
+ (char *) &on,
+ sizeof on);
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ if (clt_addr.sin6.sin6_port != 0)
+ (void) setsockopt(s, SOL_SOCKET,
+ SO_REUSEADDR,
+ (char *) &on,
+ sizeof on);
+ break;
+# endif /* NETINET6 */
+ }
+
+ if (bind(s, &clt_addr.sa, socksize) < 0)
+ {
+ save_errno = errno;
+ (void) close(s);
+ errno = save_errno;
+ syserr("makeconnection: cannot bind socket [%s]",
+ anynet_ntoa(&clt_addr));
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+ }
/*
** Linux seems to hang in connect for 90 minutes (!!!).
@@ -1107,29 +1964,40 @@ gothostent:
else
ev = NULL;
-#if _FFR_CONNECTONLYTO_OPTION
- /* for testing */
- if (ConnectOnlyTo != 0)
- addr.sin.sin_addr.s_addr = ConnectOnlyTo;
-#endif
+ switch (ConnectOnlyTo.sa.sa_family)
+ {
+# if NETINET
+ case AF_INET:
+ addr.sin.sin_addr.s_addr = ConnectOnlyTo.sin.sin_addr.s_addr;
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ memmove(&addr.sin6.sin6_addr,
+ &ConnectOnlyTo.sin6.sin6_addr,
+ IN6ADDRSZ);
+ break;
+# endif /* NETINET6 */
+ }
i = connect(s, (struct sockaddr *) &addr, addrlen);
- sav_errno = errno;
+ save_errno = errno;
if (ev != NULL)
clrevent(ev);
if (i >= 0)
break;
}
else
- sav_errno = errno;
+ save_errno = errno;
/* if running demand-dialed connection, try again */
if (DialDelay > 0 && firstconnect)
{
if (tTd(16, 1))
- printf("Connect failed (%s); trying again...\n",
- errstring(sav_errno));
+ dprintf("Connect failed (%s); trying again...\n",
+ errstring(save_errno));
firstconnect = FALSE;
- sleep(DialDelay);
+ (void) sleep(DialDelay);
continue;
}
@@ -1140,53 +2008,224 @@ gothostent:
sm_syslog(LOG_INFO, e->e_id,
"makeconnection (%s [%s]) failed: %s",
host, anynet_ntoa(&addr),
- errstring(sav_errno));
+ errstring(save_errno));
if (hp != NULL && hp->h_addr_list[addrno] != NULL)
{
if (tTd(16, 1))
- printf("Connect failed (%s); trying new address....\n",
- errstring(sav_errno));
+ dprintf("Connect failed (%s); trying new address....\n",
+ errstring(save_errno));
switch (addr.sa.sa_family)
{
-#if NETINET
+# if NETINET
case AF_INET:
- bcopy(hp->h_addr_list[addrno++],
- &addr.sin.sin_addr,
- INADDRSZ);
+ memmove(&addr.sin.sin_addr,
+ hp->h_addr_list[addrno++],
+ INADDRSZ);
break;
-#endif
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ memmove(&addr.sin6.sin6_addr,
+ hp->h_addr_list[addrno++],
+ IN6ADDRSZ);
+ break;
+# endif /* NETINET6 */
default:
- bcopy(hp->h_addr_list[addrno++],
- addr.sa.sa_data,
+ memmove(addr.sa.sa_data,
+ hp->h_addr_list[addrno++],
hp->h_length);
break;
}
continue;
}
+ errno = save_errno;
+# if NETINET6
+ if (family == AF_INET6)
+ {
+ if (tTd(16, 1))
+ dprintf("Connect failed (%s); retrying with AF_INET....\n",
+ errstring(save_errno));
+ v6found = TRUE;
+ family = AF_INET;
+ goto v4retry;
+ }
+ v6tempfail:
+# endif /* NETINET6 */
/* couldn't open connection */
-#ifdef XLA
+# if NETINET6
+ /* Don't clobber an already saved errno from v4retry */
+ if (errno > 0)
+# endif /* NETINET6 */
+ save_errno = errno;
+ if (tTd(16, 1))
+ dprintf("Connect failed (%s)\n", errstring(save_errno));
+# ifdef XLA
xla_host_end(host);
-#endif
+# endif /* XLA */
mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL);
+ errno = save_errno;
return EX_TEMPFAIL;
}
/* connection ok, put it into canonical form */
+ mci->mci_out = NULL;
if ((mci->mci_out = fdopen(s, "w")) == NULL ||
(s = dup(s)) < 0 ||
(mci->mci_in = fdopen(s, "r")) == 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) close(s);
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+
+ /* find out name for Interface through which we connect */
+ len = sizeof addr;
+ if (getsockname(s, &addr.sa, &len) == 0)
+ {
+ char *name;
+ char *p;
+
+ define(macid("{if_addr}", NULL), newstr(anynet_ntoa(&addr)),
+ &BlankEnvelope);
+ p = xalloc(5);
+ snprintf(p, 4, "%d", addr.sa.sa_family);
+ define(macid("{if_family}", NULL), p, &BlankEnvelope);
+
+ name = hostnamebyanyaddr(&addr);
+ define(macid("{if_name}", NULL), newstr(name), &BlankEnvelope);
+ if (LogLevel > 11)
+ {
+ /* log connection information */
+ sm_syslog(LOG_INFO, e->e_id,
+ "SMTP outgoing connect on %.40s", name);
+ }
+ if (bitnset(D_IFNHELO, d_flags))
+ {
+ if (name[0] != '[' && strchr(name, '.') != NULL)
+ mci->mci_heloname = newstr(name);
+ }
+ }
+ else
+ {
+ define(macid("{if_name}", NULL), NULL, &BlankEnvelope);
+ define(macid("{if_addr}", NULL), NULL, &BlankEnvelope);
+ define(macid("{if_family}", NULL), NULL, &BlankEnvelope);
+ }
+ mci_setstat(mci, EX_OK, NULL, NULL);
+ return EX_OK;
+}
+
+static void
+connecttimeout()
+{
+ errno = ETIMEDOUT;
+ longjmp(CtxConnectTimeout, 1);
+}
+ /*
+** MAKECONNECTION_DS -- make a connection to a domain socket.
+**
+** Parameters:
+** mux_path -- the path of the socket to connect to.
+** mci -- a pointer to the mail connection information
+** structure to be filled in.
+**
+** Returns:
+** An exit code telling whether the connection could be
+** made and if not why not.
+**
+** Side Effects:
+** none.
+*/
+
+# if NETUNIX
+int makeconnection_ds(mux_path, mci)
+ char *mux_path;
+ register MCI *mci;
+{
+ int sock;
+ int rval, save_errno;
+ long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_ROOTOK|SFF_EXECOK;
+ struct sockaddr_un unix_addr;
+
+ /* if not safe, don't connect */
+ rval = safefile(mux_path, RunAsUid, RunAsGid, RunAsUserName,
+ sff, S_IRUSR|S_IWUSR, NULL);
+
+ if (rval != 0)
+ {
+ syserr("makeconnection_ds: unsafe domain socket");
+ mci_setstat(mci, EX_TEMPFAIL, "4.3.5", NULL);
+ errno = rval;
+ return EX_TEMPFAIL;
+ }
+
+ /* prepare address structure */
+ memset(&unix_addr, '\0', sizeof unix_addr);
+ unix_addr.sun_family = AF_UNIX;
+
+ if (strlen(mux_path) >= sizeof unix_addr.sun_path)
+ {
+ syserr("makeconnection_ds: domain socket name too long");
+ /* XXX why TEMPFAIL ? */
+ 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);
+
+ /* initialize domain socket */
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock == -1)
+ {
+ save_errno = errno;
+ syserr("makeconnection_ds: could not create domain socket");
+ mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+
+ /* connect to server */
+ if (connect(sock, (struct sockaddr *) &unix_addr,
+ sizeof(unix_addr)) == -1)
+ {
+ save_errno = errno;
+ syserr("Could not connect to socket %s", mux_path);
+ mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL);
+ (void) close(sock);
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+
+ /* 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)
+ {
+ 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) close(sock);
+ errno = save_errno;
return EX_TEMPFAIL;
}
mci_setstat(mci, EX_OK, NULL, NULL);
- return (EX_OK);
+ errno = 0;
+ return EX_OK;
}
+# endif /* NETUNIX */
/*
** MYHOSTNAME -- return the name of this host.
**
@@ -1210,16 +2249,29 @@ myhostname(hostbuf, size)
if (gethostname(hostbuf, size) < 0)
{
- (void) strcpy(hostbuf, "localhost");
+ (void) strlcpy(hostbuf, "localhost", size);
}
- hp = sm_gethostbyname(hostbuf);
+ hp = sm_gethostbyname(hostbuf, InetMode);
if (hp == NULL)
return NULL;
if (strchr(hp->h_name, '.') != NULL || strchr(hostbuf, '.') == NULL)
+ (void) cleanstrcpy(hostbuf, hp->h_name, size);
+
+# if NETINFO
+ if (strchr(hostbuf, '.') == NULL)
{
- (void) strncpy(hostbuf, hp->h_name, size - 1);
- hostbuf[size - 1] = '\0';
+ char *domainname;
+
+ domainname = ni_propval("/locations", NULL, "resolver",
+ "domain", '\0');
+ if (domainname != NULL &&
+ strlen(domainname) + strlen(hostbuf) + 1 < size)
+ {
+ (void) strlcat(hostbuf, ".", size);
+ (void) strlcat(hostbuf, domainname, size);
+ }
}
+# endif /* NETINFO */
/*
** If there is still no dot in the name, try looking for a
@@ -1230,11 +2282,11 @@ myhostname(hostbuf, size)
{
char **ha;
- for (ha = hp->h_aliases; *ha != NULL; ha++)
+ for (ha = hp->h_aliases; ha != NULL && *ha != NULL; ha++)
{
if (strchr(*ha, '.') != NULL)
{
- (void) strncpy(hostbuf, *ha, size - 1);
+ (void) cleanstrcpy(hostbuf, *ha, size - 1);
hostbuf[size - 1] = '\0';
break;
}
@@ -1254,21 +2306,21 @@ myhostname(hostbuf, size)
!getcanonname(hostbuf, size, TRUE))
{
sm_syslog(LOG_CRIT, NOQID,
- "My unqualified host name (%s) unknown; sleeping for retry",
- hostbuf);
+ "My unqualified host name (%s) unknown; sleeping for retry",
+ hostbuf);
message("My unqualified host name (%s) unknown; sleeping for retry",
hostbuf);
- sleep(60);
+ (void) sleep(60);
if (!getcanonname(hostbuf, size, TRUE))
{
sm_syslog(LOG_ALERT, NOQID,
- "unable to qualify my own domain name (%s) -- using short name",
- hostbuf);
+ "unable to qualify my own domain name (%s) -- using short name",
+ hostbuf);
message("WARNING: unable to qualify my own domain name (%s) -- using short name",
hostbuf);
}
}
- return (hp);
+ return hp;
}
/*
** ADDRCMP -- compare two host addresses
@@ -1283,24 +2335,44 @@ myhostname(hostbuf, size)
** else -- they don't match
*/
-int
+static int
addrcmp(hp, ha, sa)
struct hostent *hp;
char *ha;
SOCKADDR *sa;
{
+# if NETINET6
+ u_char *a;
+# endif /* NETINET6 */
+
switch (sa->sa.sa_family)
{
+# if NETINET
case AF_INET:
if (hp->h_addrtype == AF_INET)
- return bcmp(ha, (char *) &sa->sin.sin_addr, hp->h_length);
+ return memcmp(ha, (char *) &sa->sin.sin_addr, INADDRSZ);
break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ a = (u_char *) &sa->sin6.sin6_addr;
+
+ /* Straight binary comparison */
+ if (hp->h_addrtype == AF_INET6)
+ return memcmp(ha, a, IN6ADDRSZ);
+ /* If IPv4-mapped IPv6 address, compare the IPv4 section */
+ if (hp->h_addrtype == AF_INET &&
+ IN6_IS_ADDR_V4MAPPED(&sa->sin6.sin6_addr))
+ return memcmp(a + IN6ADDRSZ - INADDRSZ, ha, INADDRSZ);
+ break;
+# endif /* NETINET6 */
}
return -1;
}
/*
-** GETAUTHINFO -- get the real host name asociated with a file descriptor
+** GETAUTHINFO -- get the real host name associated with a file descriptor
**
** Uses RFC1413 protocol to try to get info from the other end.
**
@@ -1327,6 +2399,7 @@ getauthinfo(fd, may_be_forged)
int fd;
bool *may_be_forged;
{
+ u_short port = 0;
SOCKADDR_LEN_T falen;
register char *volatile p = NULL;
SOCKADDR la;
@@ -1347,12 +2420,21 @@ getauthinfo(fd, may_be_forged)
if (isatty(fd) || (i = getpeername(fd, &RealHostAddr.sa, &falen)) < 0 ||
falen <= 0 || RealHostAddr.sa.sa_family == 0)
{
- if (i < 0 && errno != ENOTSOCK)
- return NULL;
+ if (i < 0)
+ {
+ /*
+ ** ENOTSOCK is OK: bail on anything else, but reset
+ ** 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);
if (tTd(9, 1))
- printf("getauthinfo: %s\n", hbuf);
+ dprintf("getauthinfo: %s\n", hbuf);
return hbuf;
}
@@ -1377,7 +2459,8 @@ getauthinfo(fd, may_be_forged)
else
{
/* try to match the reverse against the forward lookup */
- hp = sm_gethostbyname(RealHostName);
+ hp = sm_gethostbyname(RealHostName,
+ RealHostAddr.sa.sa_family);
if (hp == NULL)
*may_be_forged = TRUE;
@@ -1394,27 +2477,75 @@ getauthinfo(fd, may_be_forged)
goto noident;
lalen = sizeof la;
- if (RealHostAddr.sa.sa_family != AF_INET ||
- getsockname(fd, &la.sa, &lalen) < 0 || lalen <= 0 ||
- la.sa.sa_family != AF_INET)
+ switch (RealHostAddr.sa.sa_family)
{
- /* no ident info */
- goto noident;
- }
+# if NETINET
+ case AF_INET:
+ if (getsockname(fd, &la.sa, &lalen) < 0 ||
+ lalen <= 0 ||
+ la.sa.sa_family != AF_INET)
+ {
+ /* no ident info */
+ goto noident;
+ }
+ port = RealHostAddr.sin.sin_port;
- /* create ident query */
- (void) snprintf(ibuf, sizeof ibuf, "%d,%d\r\n",
- ntohs(RealHostAddr.sin.sin_port), ntohs(la.sin.sin_port));
+ /* create ident query */
+ (void) snprintf(ibuf, sizeof ibuf, "%d,%d\r\n",
+ ntohs(RealHostAddr.sin.sin_port),
+ ntohs(la.sin.sin_port));
- /* create local address */
- la.sin.sin_port = 0;
+ /* create local address */
+ la.sin.sin_port = 0;
- /* create foreign address */
- sp = getservbyname("auth", "tcp");
- if (sp != NULL)
- RealHostAddr.sin.sin_port = sp->s_port;
- else
+ /* create foreign address */
+# 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);
+ break;
+# endif /* NO_GETSERVBYNAME */
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ if (getsockname(fd, &la.sa, &lalen) < 0 ||
+ lalen <= 0 ||
+ la.sa.sa_family != AF_INET6)
+ {
+ /* no ident info */
+ goto noident;
+ }
+ port = RealHostAddr.sin6.sin6_port;
+
+ /* create ident query */
+ (void) snprintf(ibuf, sizeof ibuf, "%d,%d\r\n",
+ ntohs(RealHostAddr.sin6.sin6_port),
+ ntohs(la.sin6.sin6_port));
+
+ /* create local address */
+ la.sin6.sin6_port = 0;
+
+ /* create foreign address */
+# 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);
+ break;
+# endif /* NO_GETSERVBYNAME */
+# endif /* NETINET6 */
+ default:
+ /* no ident info */
+ goto noident;
+ }
s = -1;
if (setjmp(CtxAuthTimeout) != 0)
@@ -1427,21 +2558,22 @@ getauthinfo(fd, may_be_forged)
/* put a timeout around the whole thing */
ev = setevent(TimeOuts.to_ident, authtimeout, 0);
+
/* connect to foreign IDENT server using same address as SMTP socket */
- s = socket(AF_INET, SOCK_STREAM, 0);
+ s = socket(la.sa.sa_family, SOCK_STREAM, 0);
if (s < 0)
{
clrevent(ev);
goto noident;
}
- if (bind(s, &la.sa, sizeof la.sin) < 0 ||
- connect(s, &RealHostAddr.sa, sizeof RealHostAddr.sin) < 0)
+ if (bind(s, &la.sa, lalen) < 0 ||
+ connect(s, &RealHostAddr.sa, lalen) < 0)
{
goto closeident;
}
if (tTd(9, 10))
- printf("getauthinfo: sent %s", ibuf);
+ dprintf("getauthinfo: sent %s", ibuf);
/* send query */
if (write(s, ibuf, strlen(ibuf)) < 0)
@@ -1468,7 +2600,7 @@ getauthinfo(fd, may_be_forged)
*++p = '\0';
if (tTd(9, 3))
- printf("getauthinfo: got %s\n", ibuf);
+ dprintf("getauthinfo: got %s\n", ibuf);
/* parse result */
p = strchr(ibuf, ':');
@@ -1536,19 +2668,37 @@ closeident:
clrevent(ev);
noident:
+ /* put back the original incoming port */
+ switch (RealHostAddr.sa.sa_family)
+ {
+# if NETINET
+ case AF_INET:
+ if (port > 0)
+ RealHostAddr.sin.sin_port = port;
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ if (port > 0)
+ RealHostAddr.sin6.sin6_port = port;
+ break;
+# endif /* NETINET6 */
+ }
+
if (RealHostName == NULL)
{
if (tTd(9, 1))
- printf("getauthinfo: NULL\n");
+ dprintf("getauthinfo: NULL\n");
return NULL;
}
snprintf(hbuf, sizeof hbuf, "%s", RealHostName);
postident:
-#if IP_SRCROUTE
-# ifndef GET_IPOPT_DST
-# define GET_IPOPT_DST(dst) (dst)
-# endif
+# if IP_SRCROUTE
+# ifndef GET_IPOPT_DST
+# define GET_IPOPT_DST(dst) (dst)
+# endif /* ! GET_IPOPT_DST */
/*
** Extract IP source routing information.
**
@@ -1569,8 +2719,7 @@ postident:
u_char *q;
u_char *o;
int l;
- struct in_addr addr;
- struct ipoption ipopt;
+ struct IPOPTION ipopt;
ipoptlen = sizeof ipopt;
if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS,
@@ -1578,12 +2727,12 @@ postident:
goto noipsr;
if (ipoptlen == 0)
goto noipsr;
- o = (u_char *) ipopt.ipopt_list;
+ o = (u_char *) ipopt.IP_LIST;
while (o != NULL && o < (u_char *) &ipopt + ipoptlen)
{
switch (*o)
{
- case IPOPT_EOL:
+ case IPOPT_EOL:
o = NULL;
break;
@@ -1609,7 +2758,7 @@ postident:
snprintf(p, SPACELEFT(hbuf, p), " [%s@%.*s",
*o == IPOPT_SSRR ? "!" : "",
l > 240 ? 120 : l / 2,
- inet_ntoa(GET_IPOPT_DST(ipopt.ipopt_dst)));
+ inet_ntoa(GET_IPOPT_DST(ipopt.IP_DST)));
i = strlen(p);
p += i;
l -= strlen(p);
@@ -1620,17 +2769,19 @@ postident:
q = &o[3];
for ( ; j >= 0; j--)
{
+ 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));
+ "%c%.*s",
+ j != 0 ? '@' : ':',
+ l > 240 ? 120 :
+ j == 0 ? l : l / 2,
+ inet_ntoa(addr));
i = strlen(p);
p += i;
l -= i + 1;
- q += sizeof(struct in_addr);
+ q += sizeof(struct in_addr);
}
o += o[1];
break;
@@ -1646,7 +2797,7 @@ postident:
}
noipsr:
-#endif
+# endif /* IP_SRCROUTE */
if (RealHostName != NULL && RealHostName[0] != '[')
{
p = &hbuf[strlen(hbuf)];
@@ -1659,11 +2810,30 @@ noipsr:
(void) snprintf(p, SPACELEFT(hbuf, p), " (may be forged)");
}
-#if IP_SRCROUTE
+# if IP_SRCROUTE
postipsr:
-#endif
+# endif /* IP_SRCROUTE */
if (tTd(9, 1))
- printf("getauthinfo: %s\n", hbuf);
+ dprintf("getauthinfo: %s\n", hbuf);
+
+ /* put back the original incoming port */
+ switch (RealHostAddr.sa.sa_family)
+ {
+# if NETINET
+ case AF_INET:
+ if (port > 0)
+ RealHostAddr.sin.sin_port = port;
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ if (port > 0)
+ RealHostAddr.sin6.sin6_port = port;
+ break;
+# endif /* NETINET6 */
+ }
+
return hbuf;
}
/*
@@ -1696,8 +2866,13 @@ host_map_lookup(map, name, av, statp)
int *statp;
{
register struct hostent *hp;
+# if NETINET
struct in_addr in_addr;
- char *cp;
+# endif /* NETINET */
+# if NETINET6
+ struct in6_addr in6_addr;
+# endif /* NETINET6 */
+ char *cp, *ans = NULL;
register STAB *s;
char hbuf[MAXNAME + 1];
@@ -1710,15 +2885,15 @@ host_map_lookup(map, name, av, statp)
if (bitset(NCF_VALID, s->s_namecanon.nc_flags))
{
if (tTd(9, 1))
- printf("host_map_lookup(%s) => CACHE %s\n",
- name,
- s->s_namecanon.nc_cname == NULL
+ 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
+# if NAMED_BIND
h_errno = s->s_namecanon.nc_herrno;
-#endif
+# endif /* NAMED_BIND */
*statp = s->s_namecanon.nc_stat;
if (*statp == EX_TEMPFAIL)
{
@@ -1731,9 +2906,9 @@ host_map_lookup(map, name, av, statp)
if (s->s_namecanon.nc_cname == NULL)
{
syserr("host_map_lookup(%s): bogus NULL cache entry, errno = %d, h_errno = %d",
- name,
- s->s_namecanon.nc_errno,
- s->s_namecanon.nc_herrno);
+ name,
+ s->s_namecanon.nc_errno,
+ s->s_namecanon.nc_herrno);
return NULL;
}
if (bitset(MF_MATCHONLY, map->map_mflags))
@@ -1752,10 +2927,11 @@ host_map_lookup(map, name, av, statp)
** lookups because those could try to connect to a server.
*/
- if (CurEnv->e_sendmode == SM_DEFER)
+ if (CurEnv->e_sendmode == SM_DEFER &&
+ bitset(MF_DEFER, map->map_mflags))
{
if (tTd(9, 1))
- printf("host_map_lookup(%s) => DEFERRED\n", name);
+ dprintf("host_map_lookup(%s) => DEFERRED\n", name);
*statp = EX_TEMPFAIL;
return NULL;
}
@@ -1767,96 +2943,96 @@ host_map_lookup(map, name, av, statp)
** unknown.
*/
+ if (tTd(9, 1))
+ dprintf("host_map_lookup(%s) => ", name);
if (*name != '[')
{
- if (tTd(9, 1))
- printf("host_map_lookup(%s) => ", name);
- s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */
snprintf(hbuf, sizeof hbuf, "%s", name);
if (getcanonname(hbuf, sizeof hbuf - 1, !HasWildcardMX))
+ ans = hbuf;
+ }
+ else
+ {
+ if ((cp = strchr(name, ']')) == NULL)
+ return NULL;
+ *cp = '\0';
+
+ hp = NULL;
+# 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
+ if (hp == NULL &&
+ inet_pton(AF_INET6, &name[1], &in6_addr) == 1)
+ hp = sm_gethostbyaddr((char *)&in6_addr,
+ IN6ADDRSZ, AF_INET6);
+# endif /* NETINET6 */
+ *cp = ']';
+
+ if (hp != NULL)
{
- if (tTd(9, 1))
- printf("%s\n", hbuf);
- s->s_namecanon.nc_stat = EX_OK;
- s->s_namecanon.nc_cname = newstr(hbuf);
- if (bitset(MF_MATCHONLY, map->map_mflags))
- cp = map_rewrite(map, name, strlen(name), NULL);
- else
- cp = map_rewrite(map, hbuf, strlen(hbuf), av);
- return cp;
+ /* found a match -- copy out */
+ ans = denlstring((char *) hp->h_name, TRUE, TRUE);
}
- else
- {
- s->s_namecanon.nc_errno = errno;
-#if NAMED_BIND
- s->s_namecanon.nc_herrno = h_errno;
- if (tTd(9, 1))
- printf("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;
+ s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */
- default:
- *statp = EX_UNAVAILABLE;
- break;
- }
-#else
- if (tTd(9, 1))
- printf("FAIL\n");
- *statp = EX_NOHOST;
-#endif
- s->s_namecanon.nc_stat = *statp;
- return NULL;
- }
+ /* Found an answer */
+ if (ans != NULL)
+ {
+ s->s_namecanon.nc_stat = *statp = EX_OK;
+ s->s_namecanon.nc_cname = newstr(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);
+ return cp;
}
- if ((cp = strchr(name, ']')) == NULL)
- return (NULL);
- *cp = '\0';
- in_addr.s_addr = inet_addr(&name[1]);
- *cp = ']';
- /* nope -- ask the name server */
- hp = sm_gethostbyaddr((char *)&in_addr, INADDRSZ, AF_INET);
+
+ /* No match found */
s->s_namecanon.nc_errno = errno;
-#if NAMED_BIND
+# if NAMED_BIND
s->s_namecanon.nc_herrno = h_errno;
-#endif
- s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */
- if (hp == NULL)
+ if (tTd(9, 1))
+ dprintf("FAIL (%d)\n", h_errno);
+ switch (h_errno)
{
- s->s_namecanon.nc_stat = *statp = EX_NOHOST;
- return (NULL);
- }
+ case TRY_AGAIN:
+ if (UseNameServer)
+ {
+ CurEnv->e_status = "4.4.3";
+ message("851 %s: Name server timeout",
+ shortenstring(name, 33));
+ }
+ *statp = EX_TEMPFAIL;
+ break;
- /* found a match -- copy out */
- hp->h_name = denlstring((char *) hp->h_name, TRUE, TRUE);
- s->s_namecanon.nc_stat = *statp = EX_OK;
- s->s_namecanon.nc_cname = newstr(hp->h_name);
- 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), av);
- return cp;
-}
+ case HOST_NOT_FOUND:
+ case NO_DATA:
+ *statp = EX_NOHOST;
+ break;
-# else /* DAEMON */
+ case NO_RECOVERY:
+ *statp = EX_SOFTWARE;
+ break;
+
+ default:
+ *statp = EX_UNAVAILABLE;
+ break;
+ }
+# else /* NAMED_BIND */
+ if (tTd(9, 1))
+ dprintf("FAIL\n");
+ *statp = EX_NOHOST;
+# endif /* NAMED_BIND */
+ s->s_namecanon.nc_stat = *statp;
+ return NULL;
+}
+#else /* DAEMON */
/* code for systems without sophisticated networking */
/*
@@ -1882,10 +3058,10 @@ myhostname(hostbuf, size)
fixcrlf(hostbuf, TRUE);
(void) fclose(f);
}
- return (NULL);
+ return NULL;
}
/*
-** GETAUTHINFO -- get the real host name asociated with a file descriptor
+** GETAUTHINFO -- get the real host name associated with a file descriptor
**
** Parameters:
** fd -- the descriptor
@@ -1911,7 +3087,7 @@ getauthinfo(fd, may_be_forged)
return NULL;
}
/*
-** MAPHOSTNAME -- turn a hostname into canonical form
+** HOST_MAP_LOOKUP -- turn a hostname into canonical form
**
** Parameters:
** map -- a pointer to the database map.
@@ -1938,13 +3114,45 @@ host_map_lookup(map, name, avp, statp)
char **avp;
char *statp;
{
- register struct hostent *hp;
+ register struct hostent *hp = NULL;
char *cp;
- hp = sm_gethostbyname(name);
+ 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))
@@ -1989,6 +3197,14 @@ host_map_init(map, args)
case 't':
map->map_mflags |= MF_NODEFER;
break;
+
+ case 'S': /* only for consistency */
+ map->map_spacesub = *++p;
+ break;
+
+ case 'D':
+ map->map_mflags |= MF_DEFER;
+ break;
}
while (*p != '\0' && !(isascii(*p) && isspace(*p)))
p++;
@@ -2001,6 +3217,36 @@ host_map_init(map, args)
map->map_tapp = newstr(map->map_tapp);
return TRUE;
}
+
+#if NETINET6
+/*
+** ANYNET_NTOP -- convert an IPv6 network address to printable form.
+**
+** Parameters:
+** s6a -- a pointer to an in6_addr structure.
+** dst -- buffer to store result in
+** dst_len -- size of dst buffer
+**
+** Returns:
+** A printable version of that structure.
+*/
+char *
+anynet_ntop(s6a, dst, dst_len)
+ struct in6_addr *s6a;
+ char *dst;
+ size_t dst_len;
+{
+ register char *ap;
+
+ if (IN6_IS_ADDR_V4MAPPED(s6a))
+ ap = (char *) inet_ntop(AF_INET,
+ &s6a->s6_addr[IN6ADDRSZ - INADDRSZ],
+ dst, dst_len);
+ else
+ ap = (char *) inet_ntop(AF_INET6, s6a, dst, dst_len);
+ return ap;
+}
+#endif /* NETINET6 */
/*
** ANYNET_NTOA -- convert a network address to printable form.
**
@@ -2013,9 +3259,9 @@ host_map_init(map, args)
#ifdef USE_SOCK_STREAM
-#if NETLINK
-# include <net/if_dl.h>
-#endif
+# if NETLINK
+# include <net/if_dl.h>
+# endif /* NETLINK */
char *
anynet_ntoa(sap)
@@ -2034,27 +3280,35 @@ anynet_ntoa(sap)
switch (sap->sa.sa_family)
{
-#if NETUNIX
+# if NETUNIX
case AF_UNIX:
- if (sap->sunix.sun_path[0] != '\0')
- snprintf(buf, sizeof buf, "[UNIX: %.64s]",
+ if (sap->sunix.sun_path[0] != '\0')
+ snprintf(buf, sizeof buf, "[UNIX: %.64s]",
sap->sunix.sun_path);
- else
- snprintf(buf, sizeof buf, "[UNIX: localhost]");
+ else
+ snprintf(buf, sizeof buf, "[UNIX: localhost]");
return buf;
-#endif
+# endif /* NETUNIX */
-#if NETINET
+# if NETINET
case AF_INET:
- return inet_ntoa(sap->sin.sin_addr);
-#endif
+ return (char *) inet_ntoa(sap->sin.sin_addr);
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ ap = anynet_ntop(&sap->sin6.sin6_addr, buf, sizeof buf);
+ if (ap != NULL)
+ return ap;
+ break;
+# endif /* NETINET6 */
-#if NETLINK
+# if NETLINK
case AF_LINK:
snprintf(buf, sizeof buf, "[LINK: %s]",
link_ntoa((struct sockaddr_dl *) &sap->sa));
return buf;
-#endif
+# endif /* NETLINK */
default:
/* this case is needed when nothing is #defined */
/* in order to keep the switch syntactically correct */
@@ -2091,37 +3345,51 @@ hostnamebyanyaddr(sap)
register SOCKADDR *sap;
{
register struct hostent *hp;
+# if NAMED_BIND
int saveretry;
+# endif /* NAMED_BIND */
+# if NETINET6
+ struct in6_addr in6_addr;
+# endif /* NETINET6 */
-#if NAMED_BIND
+# if NAMED_BIND
/* shorten name server timeout to avoid higher level timeouts */
saveretry = _res.retry;
- _res.retry = 3;
-#endif /* NAMED_BIND */
+ if (_res.retry * _res.retrans > 20)
+ _res.retry = 20 / _res.retrans;
+# endif /* NAMED_BIND */
switch (sap->sa.sa_family)
{
-#if NETINET
+# if NETINET
case AF_INET:
hp = sm_gethostbyaddr((char *) &sap->sin.sin_addr,
INADDRSZ,
AF_INET);
break;
-#endif
+# endif /* NETINET */
-#if NETISO
+# if NETINET6
+ case AF_INET6:
+ hp = sm_gethostbyaddr((char *) &sap->sin6.sin6_addr,
+ 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);
break;
-#endif
+# endif /* NETISO */
-#if NETUNIX
+# if NETUNIX
case AF_UNIX:
hp = NULL;
break;
-#endif
+# endif /* NETUNIX */
default:
hp = sm_gethostbyaddr(sap->sa.sa_data,
@@ -2130,25 +3398,30 @@ hostnamebyanyaddr(sap)
break;
}
-#if NAMED_BIND
+# if NAMED_BIND
_res.retry = saveretry;
-#endif /* NAMED_BIND */
-
- if (hp != NULL && hp->h_name[0] != '[' &&
- inet_addr(hp->h_name) == INADDR_NONE)
+# endif /* NAMED_BIND */
+
+# if NETINET || NETINET6
+ if (hp != NULL && hp->h_name[0] != '['
+# if NETINET6
+ && inet_pton(AF_INET6, hp->h_name, &in6_addr) != 1
+# endif /* NETINET6 */
+# if NETINET
+ && inet_addr(hp->h_name) == INADDR_NONE
+# endif /* NETINET */
+ )
return denlstring((char *) hp->h_name, TRUE, TRUE);
-#if NETUNIX
- else if (sap->sa.sa_family == AF_UNIX && sap->sunix.sun_path[0] == '\0')
+# endif /* NETINET || NETINET6 */
+# if NETUNIX
+ if (sap->sa.sa_family == AF_UNIX && sap->sunix.sun_path[0] == '\0')
return "localhost";
-#endif
- else
+# endif /* NETUNIX */
{
- /* produce a dotted quad */
static char buf[203];
(void) snprintf(buf, sizeof buf, "[%.200s]", anynet_ntoa(sap));
return buf;
}
}
-
-#endif /* SOCK_STREAM */
+#endif /* USE_SOCK_STREAM */
diff --git a/contrib/sendmail/src/deliver.c b/contrib/sendmail/src/deliver.c
index ed03328..d1907d4 100644
--- a/contrib/sendmail/src/deliver.c
+++ b/contrib/sendmail/src/deliver.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,23 +12,33 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)deliver.c 8.367 (Berkeley) 1/18/1999";
-#endif /* not lint */
+static char id[] = "@(#)$Id: deliver.c,v 8.600.2.1.2.31 2000/07/18 02:24:43 gshapiro Exp $";
+#endif /* ! lint */
+
+#include <sendmail.h>
-#include "sendmail.h"
-#include <errno.h>
-#include <grp.h>
-#if NAMED_BIND
-#include <resolv.h>
-#endif
#if HASSETUSERCONTEXT
# include <login_cap.h>
-#endif
+#endif /* HASSETUSERCONTEXT */
+
+#if STARTTLS || (SASL && SFIO)
+# include "sfsasl.h"
+#endif /* STARTTLS || (SASL && SFIO) */
+
+static int deliver __P((ENVELOPE *, ADDRESS *));
+static void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int));
+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 *));
#if SMTP
-extern char SmtpError[];
-#endif
+# if STARTTLS
+static int starttls __P((MAILER *, MCI *, ENVELOPE *));
+# endif /* STARTTLS */
+#endif /* SMTP */
/*
** SENDALL -- actually send all the messages.
@@ -55,12 +66,12 @@ sendall(e, mode)
register ADDRESS *q;
char *owner;
int otherowners;
+ int save_errno;
register ENVELOPE *ee;
ENVELOPE *splitenv = NULL;
int oldverbose = Verbose;
bool somedeliveries = FALSE, expensive = FALSE;
pid_t pid;
- void sendenvelope __P((ENVELOPE *, int));
/*
** If this message is to be discarded, don't bother sending
@@ -70,7 +81,7 @@ sendall(e, mode)
if (bitset(EF_DISCARD, e->e_flags))
{
if (tTd(13, 1))
- printf("sendall: discarding id %s\n", e->e_id);
+ dprintf("sendall: discarding id %s\n", e->e_id);
e->e_flags |= EF_CLRQUEUE;
if (LogLevel > 4)
sm_syslog(LOG_INFO, e->e_id, "discarded");
@@ -103,14 +114,12 @@ sendall(e, mode)
if (tTd(13, 1))
{
- extern void printenvflags __P((ENVELOPE *));
-
- printf("\n===== SENDALL: mode %c, id %s, e_from ",
+ dprintf("\n===== SENDALL: mode %c, id %s, e_from ",
mode, e->e_id);
printaddr(&e->e_from, FALSE);
- printf("\te_flags = ");
+ dprintf("\te_flags = ");
printenvflags(e);
- printf("sendqueue:\n");
+ dprintf("sendqueue:\n");
printaddr(e->e_sendqueue, TRUE);
}
@@ -129,34 +138,41 @@ sendall(e, mode)
errno = 0;
#if QUEUE
queueup(e, mode == SM_QUEUE || mode == SM_DEFER);
-#endif
+#endif /* QUEUE */
e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
- syserr("554 Too many hops %d (%d max): from %s via %s, to %s",
+ ExitStat = EX_UNAVAILABLE;
+ syserr("554 5.0.0 Too many hops %d (%d max): from %s via %s, to %s",
e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
RealHostName == NULL ? "localhost" : RealHostName,
e->e_sendqueue->q_paddr);
- e->e_sendqueue->q_status = "5.4.6";
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (QS_IS_DEAD(q->q_state))
+ continue;
+ q->q_state = QS_BADADDR;
+ q->q_status = "5.4.6";
+ }
return;
}
/*
** Do sender deletion.
**
- ** If the sender has the QQUEUEUP flag set, skip this.
+ ** If the sender should be queued up, skip this.
** This can happen if the name server is hosed when you
** are trying to send mail. The result is that the sender
** is instantiated in the queue as a recipient.
*/
if (!bitset(EF_METOO, e->e_flags) &&
- !bitset(QQUEUEUP, e->e_from.q_flags))
+ !QS_IS_QUEUEUP(e->e_from.q_state))
{
if (tTd(13, 5))
{
- printf("sendall: QDONTSEND ");
+ dprintf("sendall: QS_SENDER ");
printaddr(&e->e_from, FALSE);
}
- e->e_from.q_flags |= QDONTSEND;
+ e->e_from.q_state = QS_SENDER;
(void) recipient(&e->e_from, &e->e_sendqueue, 0, e);
}
@@ -177,14 +193,14 @@ sendall(e, mode)
q->q_owner = a->q_owner;
if (q->q_owner != NULL &&
- !bitset(QDONTSEND, q->q_flags) &&
+ !QS_IS_DEAD(q->q_state) &&
strcmp(q->q_owner, e->e_from.q_paddr) == 0)
q->q_owner = NULL;
}
if (tTd(13, 25))
{
- printf("\nAfter first owner pass, sendq =\n");
+ dprintf("\nAfter first owner pass, sendq =\n");
printaddr(e->e_sendqueue, TRUE);
}
@@ -193,7 +209,7 @@ sendall(e, mode)
while (owner != NULL && otherowners > 0)
{
if (tTd(13, 28))
- printf("owner = \"%s\", otherowners = %d\n",
+ dprintf("owner = \"%s\", otherowners = %d\n",
owner, otherowners);
owner = NULL;
otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0;
@@ -202,18 +218,18 @@ sendall(e, mode)
{
if (tTd(13, 30))
{
- printf("Checking ");
+ dprintf("Checking ");
printaddr(q, FALSE);
}
- if (bitset(QDONTSEND, q->q_flags))
+ if (QS_IS_DEAD(q->q_state))
{
if (tTd(13, 30))
- printf(" ... QDONTSEND\n");
+ dprintf(" ... QS_IS_DEAD\n");
continue;
}
if (tTd(13, 29) && !tTd(13, 30))
{
- printf("Checking ");
+ dprintf("Checking ");
printaddr(q, FALSE);
}
@@ -222,7 +238,7 @@ sendall(e, mode)
if (owner == NULL)
{
if (tTd(13, 40))
- printf(" ... First owner = \"%s\"\n",
+ dprintf(" ... First owner = \"%s\"\n",
q->q_owner);
owner = q->q_owner;
}
@@ -231,7 +247,7 @@ sendall(e, mode)
if (strcmp(owner, q->q_owner) == 0)
{
if (tTd(13, 40))
- printf(" ... Same owner = \"%s\"\n",
+ dprintf(" ... Same owner = \"%s\"\n",
owner);
/* make future comparisons cheap */
@@ -240,23 +256,69 @@ sendall(e, mode)
else
{
if (tTd(13, 40))
- printf(" ... Another owner \"%s\"\n",
+ dprintf(" ... Another owner \"%s\"\n",
q->q_owner);
otherowners++;
}
owner = q->q_owner;
}
else if (tTd(13, 40))
- printf(" ... Same owner = \"%s\"\n",
+ dprintf(" ... Same owner = \"%s\"\n",
owner);
}
else
{
if (tTd(13, 40))
- printf(" ... Null owner\n");
+ dprintf(" ... Null owner\n");
otherowners++;
}
+ if (QS_IS_BADADDR(q->q_state))
+ {
+ if (tTd(13, 30))
+ dprintf(" ... QS_IS_BADADDR\n");
+ continue;
+ }
+
+ if (QS_IS_QUEUEUP(q->q_state))
+ {
+ MAILER *m = q->q_mailer;
+
+ /*
+ ** If we have temporary address failures
+ ** (e.g., dns failure) and a fallback MX is
+ ** set, send directly to the fallback MX host.
+ */
+
+ if (FallBackMX != NULL &&
+ !wordinclass(FallBackMX, 'w') &&
+ mode != SM_VERIFY &&
+ (strcmp(m->m_mailer, "[IPC]") == 0 ||
+ strcmp(m->m_mailer, "[TCP]") == 0) &&
+ m->m_argv[0] != NULL &&
+ (strcmp(m->m_argv[0], "TCP") == 0 ||
+ strcmp(m->m_argv[0], "IPC") == 0))
+ {
+ int len;
+ char *p;
+
+ if (tTd(13, 30))
+ dprintf(" ... FallBackMX\n");
+
+ len = strlen(FallBackMX) + 3;
+ p = xalloc(len);
+ snprintf(p, len, "[%s]", FallBackMX);
+ q->q_state = QS_OK;
+ q->q_host = p;
+ }
+ else
+ {
+ if (tTd(13, 30))
+ dprintf(" ... QS_IS_QUEUEUP\n");
+ continue;
+ }
+ }
+
/*
** If this mailer is expensive, and if we don't
** want to make connections now, just mark these
@@ -266,45 +328,46 @@ sendall(e, mode)
** daemon will come along to send the messages later.
*/
- if (bitset(QBADADDR|QQUEUEUP, q->q_flags))
+ if (NoConnect && !Verbose &&
+ bitnset(M_EXPENSIVE, q->q_mailer->m_flags))
{
if (tTd(13, 30))
- printf(" ... QBADADDR|QQUEUEUP\n");
- continue;
+ dprintf(" ... expensive\n");
+ q->q_state = QS_QUEUEUP;
+ expensive = TRUE;
}
- if (NoConnect && !Verbose &&
- bitnset(M_EXPENSIVE, q->q_mailer->m_flags))
+ else if (bitnset(M_HOLD, q->q_mailer->m_flags) &&
+ QueueLimitId == NULL &&
+ QueueLimitSender == NULL &&
+ QueueLimitRecipient == NULL)
{
if (tTd(13, 30))
- printf(" ... expensive\n");
- q->q_flags |= QQUEUEUP;
+ dprintf(" ... hold\n");
+ q->q_state = QS_QUEUEUP;
expensive = TRUE;
}
else
{
if (tTd(13, 30))
- printf(" ... deliverable\n");
+ dprintf(" ... deliverable\n");
somedeliveries = TRUE;
}
}
if (owner != NULL && otherowners > 0)
{
- extern HDR *copyheader __P((HDR *));
- extern ADDRESS *copyqueue __P((ADDRESS *));
- extern void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int));
-
/*
** Split this envelope into two.
*/
- ee = (ENVELOPE *) xalloc(sizeof(ENVELOPE));
+ ee = (ENVELOPE *) xalloc(sizeof *ee);
*ee = *e;
+ ee->e_message = NULL;
ee->e_id = NULL;
- (void) queuename(ee, '\0');
+ assign_queueid(ee);
if (tTd(13, 1))
- printf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n",
+ 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);
@@ -315,24 +378,26 @@ sendall(e, mode)
setsender(owner, ee, NULL, '\0', TRUE);
if (tTd(13, 5))
{
- printf("sendall(split): QDONTSEND ");
+ dprintf("sendall(split): QS_SENDER ");
printaddr(&ee->e_from, FALSE);
}
- ee->e_from.q_flags |= QDONTSEND;
+ 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_errormode = EM_MAIL;
ee->e_sibling = splitenv;
+ ee->e_statmsg = NULL;
splitenv = ee;
for (q = e->e_sendqueue; q != NULL; q = q->q_next)
{
if (q->q_owner == owner)
{
- q->q_flags |= QDONTSEND;
- q->q_flags &= ~(QQUEUEUP|QBADADDR);
+ q->q_state = QS_CLONED;
if (tTd(13, 6))
- printf("\t... stripping %s from original envelope\n",
+ dprintf("\t... stripping %s from original envelope\n",
q->q_paddr);
}
}
@@ -340,10 +405,9 @@ sendall(e, mode)
{
if (q->q_owner != owner)
{
- q->q_flags |= QDONTSEND;
- q->q_flags &= ~(QQUEUEUP|QBADADDR);
+ q->q_state = QS_CLONED;
if (tTd(13, 6))
- printf("\t... dropping %s from cloned envelope\n",
+ dprintf("\t... dropping %s from cloned envelope\n",
q->q_paddr);
}
else
@@ -352,18 +416,32 @@ sendall(e, mode)
q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
q->q_flags |= DefaultNotify & ~QPINGONSUCCESS;
if (tTd(13, 6))
- printf("\t... moving %s to cloned envelope\n",
+ 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');
- openxscript(ee);
+
+ /*
+ ** Give the split envelope access to the parent
+ ** transcript file for errors obtained while
+ ** processing the recipients (done before the
+ ** envelope splitting).
+ */
+
+ if (e->e_xfp != NULL)
+ ee->e_xfp = bfdup(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);
+ "clone %s, owner=%s",
+ e->e_id, owner);
}
}
@@ -372,10 +450,10 @@ sendall(e, mode)
setsender(owner, e, NULL, '\0', TRUE);
if (tTd(13, 5))
{
- printf("sendall(owner): QDONTSEND ");
+ dprintf("sendall(owner): QS_SENDER ");
printaddr(&e->e_from, FALSE);
}
- e->e_from.q_flags |= QDONTSEND;
+ e->e_from.q_state = QS_SENDER;
e->e_errormode = EM_MAIL;
e->e_flags |= EF_NORECEIPT;
e->e_flags &= ~EF_FATALERRS;
@@ -386,7 +464,7 @@ sendall(e, mode)
mode != SM_VERIFY)
{
if (tTd(13, 29))
- printf("No deliveries: auto-queuing\n");
+ dprintf("No deliveries: auto-queuing\n");
mode = SM_QUEUE;
/* treat this as a delivery in terms of counting tries */
@@ -401,7 +479,7 @@ sendall(e, mode)
}
}
-# if QUEUE
+#if QUEUE
if ((mode == SM_QUEUE || mode == SM_DEFER || mode == SM_FORK ||
(mode != SM_VERIFY && SuperSafe)) &&
(!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL))
@@ -422,20 +500,20 @@ sendall(e, mode)
if (tTd(13, 20))
{
- printf("sendall: final mode = %c\n", mode);
+ dprintf("sendall: final mode = %c\n", mode);
if (tTd(13, 21))
{
- printf("\n================ Final Send Queue(s) =====================\n");
- printf("\n *** Envelope %s, e_from=%s ***\n",
+ 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);
for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
{
- printf("\n *** Envelope %s, e_from=%s ***\n",
+ dprintf("\n *** Envelope %s, e_from=%s ***\n",
ee->e_id, ee->e_from.q_paddr);
printaddr(ee->e_sendqueue, TRUE);
}
- printf("==========================================================\n\n");
+ dprintf("==========================================================\n\n");
}
}
switch (mode)
@@ -446,9 +524,9 @@ sendall(e, mode)
case SM_QUEUE:
case SM_DEFER:
-# if HASFLOCK
+#if HASFLOCK
queueonly:
-# endif
+#endif /* HASFLOCK */
if (e->e_nrcpts > 0)
e->e_flags |= EF_INQUEUE;
dropenvelope(e, splitenv != NULL);
@@ -464,7 +542,7 @@ sendall(e, mode)
if (e->e_xfp != NULL)
(void) fflush(e->e_xfp);
-# if !HASFLOCK
+#if !HASFLOCK
/*
** Since fcntl locking has the interesting semantic that
** the lock is owned by a process, not by an open file
@@ -497,36 +575,49 @@ sendall(e, mode)
ee->e_id = qid;
}
-# endif /* !HASFLOCK */
+#endif /* !HASFLOCK */
+
+ /*
+ ** 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();
pid = fork();
if (pid < 0)
{
-# if HASFLOCK
+ syserr("deliver: fork 1");
+#if HASFLOCK
goto queueonly;
-# else
+#else /* HASFLOCK */
e->e_id = NULL;
for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
ee->e_id = NULL;
return;
-# endif /* HASFLOCK */
+#endif /* HASFLOCK */
}
else if (pid > 0)
{
-# if HASFLOCK
+#if HASFLOCK
/* be sure we leave the temp files to our child */
/* close any random open files in the envelope */
closexscript(e);
if (e->e_dfp != NULL)
- (void) xfclose(e->e_dfp, "sendenvelope dfp", e->e_id);
+ (void) bfclose(e->e_dfp);
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) xfclose(e->e_lockfp, "sendenvelope lockfp", e->e_id);
+ (void) fclose(e->e_lockfp);
+ else
+ syserr("%s: sendall: null lockfp", e->e_id);
e->e_lockfp = NULL;
-# endif
+#endif /* HASFLOCK */
/* make sure the parent doesn't own the envelope */
e->e_id = NULL;
@@ -540,18 +631,22 @@ sendall(e, mode)
pid = fork();
if (pid > 0)
exit(EX_OK);
+ save_errno = errno;
/* be sure we are immune from the terminal */
disconnect(2, e);
+ clearstats();
/* prevent parent from waiting if there was an error */
if (pid < 0)
{
-# if HASFLOCK
+ errno = save_errno;
+ syserr("deliver: fork 2");
+#if HASFLOCK
e->e_flags |= EF_INQUEUE;
-# else
+#else /* HASFLOCK */
e->e_id = NULL;
-# endif /* HASFLOCK */
+#endif /* HASFLOCK */
finis(TRUE, ExitStat);
}
@@ -570,18 +665,9 @@ sendall(e, mode)
mci_flush(FALSE, NULL);
- /*
- ** 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,
- ** open a copy of the maps for the delivery process.
- */
-
- initmaps(FALSE, e);
-
-# if HASFLOCK
+#if HASFLOCK
break;
-# else
+#else /* HASFLOCK */
/*
** Now reacquire and run the various queue files.
@@ -591,12 +677,14 @@ sendall(e, mode)
{
ENVELOPE *sibling = ee->e_sibling;
- (void) dowork(ee->e_id, FALSE, FALSE, ee);
+ (void) dowork(ee->e_queuedir, ee->e_id,
+ FALSE, FALSE, ee);
ee->e_sibling = sibling;
}
- (void) dowork(e->e_id, FALSE, FALSE, e);
+ (void) dowork(e->e_queuedir, e->e_id,
+ FALSE, FALSE, e);
finis(TRUE, ExitStat);
-# endif /* !HASFLOCK */
+#endif /* HASFLOCK */
}
sendenvelope(e, mode);
@@ -616,7 +704,7 @@ sendall(e, mode)
finis(TRUE, ExitStat);
}
-void
+static void
sendenvelope(e, mode)
register ENVELOPE *e;
int mode;
@@ -625,13 +713,13 @@ sendenvelope(e, mode)
bool didany;
if (tTd(13, 10))
- printf("sendenvelope(%s) e_flags=0x%lx\n",
+ 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%x",
- e->e_flags);
+ "sendenvelope, flags=0x%lx",
+ e->e_flags);
/*
** If we have had global, fatal errors, don't bother sending
@@ -647,6 +735,11 @@ sendenvelope(e, mode)
return;
}
+ /* Don't attempt deliveries if we want to bounce now */
+ if (!bitset(EF_RESPONSE, e->e_flags) &&
+ TimeOuts.to_q_return[e->e_timeoutclass] == NOW)
+ return;
+
/*
** Run through the list and send everything.
**
@@ -656,6 +749,7 @@ 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;
@@ -669,11 +763,11 @@ sendenvelope(e, mode)
(void) snprintf(wbuf, sizeof wbuf, "sendall(%.*s)",
MAXNAME, q->q_paddr);
checkfd012(wbuf);
-#endif
+#endif /* XDEBUG */
if (mode == SM_VERIFY)
{
e->e_to = q->q_paddr;
- if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
+ if (QS_IS_SENDABLE(q->q_state))
{
if (q->q_host != NULL && q->q_host[0] != '\0')
message("deliverable: mailer %s, host %s, user %s",
@@ -686,11 +780,9 @@ sendenvelope(e, mode)
q->q_user);
}
}
- else if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
+ else if (QS_IS_OK(q->q_state))
{
- extern int deliver __P((ENVELOPE *, ADDRESS *));
-
-# if QUEUE
+#if QUEUE
/*
** Checkpoint the send list every few addresses
*/
@@ -700,7 +792,7 @@ sendenvelope(e, mode)
queueup(e, FALSE);
e->e_nsent = 0;
}
-# endif /* QUEUE */
+#endif /* QUEUE */
(void) deliver(e, q);
didany = TRUE;
}
@@ -713,7 +805,7 @@ sendenvelope(e, mode)
#if XDEBUG
checkfd012("end of sendenvelope");
-#endif
+#endif /* XDEBUG */
}
/*
** DUP_QUEUE_FILE -- duplicate a queue file into a split queue
@@ -727,35 +819,40 @@ sendenvelope(e, mode)
** none
*/
-void
+static void
dup_queue_file(e, ee, type)
struct envelope *e, *ee;
int type;
{
- char f1buf[MAXQFNAME], f2buf[MAXQFNAME];
+ char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN];
ee->e_dfp = NULL;
ee->e_xfp = NULL;
+
+ /*
+ ** Make sure both are in the same directory.
+ */
+
snprintf(f1buf, sizeof f1buf, "%s", queuename(e, type));
snprintf(f2buf, sizeof f2buf, "%s", queuename(ee, type));
if (link(f1buf, f2buf) < 0)
{
- int saverrno = errno;
+ int save_errno = errno;
syserr("sendall: link(%s, %s)", f1buf, f2buf);
- if (saverrno == EEXIST)
+ if (save_errno == EEXIST)
{
if (unlink(f2buf) < 0)
{
syserr("!sendall: unlink(%s): permanent",
f2buf);
- /*NOTREACHED*/
+ /* NOTREACHED */
}
if (link(f1buf, f2buf) < 0)
{
syserr("!sendall: link(%s, %s): permanent",
f1buf, f2buf);
- /*NOTREACHED*/
+ /* NOTREACHED */
}
}
}
@@ -782,13 +879,13 @@ dup_queue_file(e, ee, type)
** vfork for you.....
*/
-# define NFORKTRIES 5
+#define NFORKTRIES 5
-# ifndef FORK
+#ifndef FORK
# define FORK fork
-# endif
+#endif /* ! FORK */
-# define DOFORK(fORKfN) \
+#define DOFORK(fORKfN) \
{\
register int i;\
\
@@ -798,7 +895,7 @@ dup_queue_file(e, ee, type)
if (pid >= 0)\
break;\
if (i > 0)\
- sleep((unsigned) NFORKTRIES - i);\
+ (void) sleep((unsigned) NFORKTRIES - i);\
}\
}
/*
@@ -822,7 +919,7 @@ dofork()
register pid_t pid = -1;
DOFORK(fork);
- return (pid);
+ return pid;
}
/*
** DELIVER -- Deliver a message to a list of addresses.
@@ -848,12 +945,12 @@ dofork()
#ifndef NO_UID
# define NO_UID -1
-#endif
+#endif /* ! NO_UID */
#ifndef NO_GID
# define NO_GID -1
-#endif
+#endif /* ! NO_GID */
-int
+static int
deliver(e, firstto)
register ENVELOPE *e;
ADDRESS *firstto;
@@ -872,49 +969,52 @@ deliver(e, firstto)
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 */
char *firstsig; /* signature of firstto */
pid_t pid = -1;
char *volatile curhost;
register u_short port = 0;
+#if NETUNIX
+ char *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 */
+ bool ovr;
+#if _FFR_DYNAMIC_TOBUF
+ int strsize;
+ int rcptcount;
+ static int tobufsize = 0;
+ static char *tobuf = NULL;
+#else /* _FFR_DYNAMIC_TOBUF */
+ char tobuf[TOBUFSIZE]; /* text line of to people */
+#endif /* _FFR_DYNAMIC_TOBUF */
int mpvect[2];
int rpvect[2];
- char *pv[MAXPV+1];
- char tobuf[TOBUFSIZE]; /* text line of to people */
+ char *mxhosts[MAXMXHOSTS + 1];
+ char *pv[MAXPV + 1];
char buf[MAXNAME + 1];
char rpathbuf[MAXNAME + 1]; /* translated return path */
- extern int checkcompat __P((ADDRESS *, ENVELOPE *));
- extern void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int));
errno = 0;
- if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags))
- return (0);
+ if (!QS_IS_OK(to->q_state))
+ return 0;
suidwarn = geteuid() == 0;
-#if NAMED_BIND
- /* unless interactive, try twice, over a minute */
- if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
- {
- _res.retrans = 30;
- _res.retry = 2;
- }
-#endif
-
m = to->q_mailer;
host = to->q_host;
CurEnv = e; /* just in case */
e->e_statmsg = NULL;
#if SMTP
SmtpError[0] = '\0';
-#endif
+#endif /* SMTP */
xstart = curtime();
if (tTd(10, 1))
- printf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
+ 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);
@@ -923,14 +1023,14 @@ deliver(e, firstto)
** 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);
}
-
+
/*
** Do initial argv setup.
** Insert the mailer name. Notice that $x expansion is
@@ -962,7 +1062,9 @@ deliver(e, firstto)
*pvp++ = m->m_argv[0];
/* insert -f or -r flag as appropriate */
- if (FromFlag && (bitnset(M_FOPT, m->m_flags) || bitnset(M_ROPT, m->m_flags)))
+ if (FromFlag &&
+ (bitnset(M_FOPT, m->m_flags) ||
+ bitnset(M_ROPT, m->m_flags)))
{
if (bitnset(M_FOPT, m->m_flags))
*pvp++ = "-f";
@@ -998,8 +1100,9 @@ deliver(e, firstto)
*pvp++ = newstr(buf);
if (pvp >= &pv[MAXPV - 3])
{
- syserr("554 Too many parameters to %s before $u", pv[0]);
- return (-1);
+ syserr("554 5.3.5 Too many parameters to %s before $u",
+ pv[0]);
+ return -1;
}
}
@@ -1012,14 +1115,14 @@ deliver(e, firstto)
if (*mvp == NULL)
{
/* running SMTP */
-# if SMTP
+#if SMTP
clever = TRUE;
*pvp = NULL;
-# else /* SMTP */
+#else /* SMTP */
/* oops! we don't implement SMTP */
- syserr("554 SMTP style mailer not implemented");
- return (EX_SOFTWARE);
-# endif /* SMTP */
+ syserr("554 5.3.5 SMTP style mailer not implemented");
+ return EX_SOFTWARE;
+#endif /* SMTP */
}
/*
@@ -1029,29 +1132,51 @@ 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, e);
+ firstsig = hostsignature(firstto->q_mailer, firstto->q_host);
for (; to != NULL; to = to->q_next)
{
/* avoid sending multiple recipients to dumb mailers */
+#if _FFR_DYNAMIC_TOBUF
+ if (tochain != NULL && !bitnset(M_MUSER, m->m_flags))
+ break;
+#else /* _FFR_DYNAMIC_TOBUF */
if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags))
break;
+#endif /* _FFR_DYNAMIC_TOBUF */
/* if already sent or not for this host, don't send */
- if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags) ||
+ if (!QS_IS_OK(to->q_state) ||
to->q_mailer != firstto->q_mailer ||
- strcmp(hostsignature(to->q_mailer, to->q_host, e), firstsig) != 0)
+ strcmp(hostsignature(to->q_mailer, to->q_host),
+ firstsig) != 0)
continue;
/* avoid overflowing tobuf */
+#if _FFR_DYNAMIC_TOBUF
+ strsize += strlen(to->q_paddr) + 1;
+ if (!clever && strsize > TOBUFSIZE)
+ break;
+
+ if (++rcptcount > to->q_mailer->m_maxrcpt)
+ break;
+#else /* _FFR_DYNAMIC_TOBUF */
if (sizeof tobuf < (strlen(to->q_paddr) + strlen(tobuf) + 2))
break;
+#endif /* _FFR_DYNAMIC_TOBUF */
if (tTd(10, 1))
{
- printf("\nsend to ");
+ dprintf("\nsend to ");
printaddr(to, FALSE);
}
@@ -1061,18 +1186,12 @@ deliver(e, firstto)
if (tTd(10, 2))
{
- printf("ctladdr=");
+ dprintf("ctladdr=");
printaddr(ctladdr, FALSE);
}
user = to->q_user;
e->e_to = to->q_paddr;
- if (tTd(10, 5))
- {
- printf("deliver: QDONTSEND ");
- printaddr(to, FALSE);
- }
- to->q_flags |= QDONTSEND;
/*
** Check to see that these people are allowed to
@@ -1086,44 +1205,59 @@ deliver(e, firstto)
to->q_status = "5.2.3";
else
to->q_status = "5.3.4";
- usrerr("552 Message is too large; %ld bytes max", m->m_maxsize);
- markfailure(e, to, NULL, EX_UNAVAILABLE);
- giveresponse(EX_UNAVAILABLE, m, NULL, ctladdr, xstart, e);
+ /* 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);
+ giveresponse(EX_UNAVAILABLE, to->q_status, m,
+ NULL, ctladdr, xstart, e);
continue;
}
#if NAMED_BIND
h_errno = 0;
-#endif
+#endif /* NAMED_BIND */
+ ovr = TRUE;
/* do config file checking of compatibility */
- rcode = rscheck("check_compat",
- e->e_from.q_paddr, to->q_paddr, e);
+ rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr,
+ e, TRUE, TRUE, 4);
if (rcode == EX_OK)
{
/* do in-code checking if not discarding */
if (!bitset(EF_DISCARD, e->e_flags))
+ {
rcode = checkcompat(to, e);
+ ovr = FALSE;
+ }
}
if (rcode != EX_OK)
{
- markfailure(e, to, NULL, rcode);
- giveresponse(rcode, m, NULL, ctladdr, xstart, e);
+ markfailure(e, to, NULL, rcode, ovr);
+ giveresponse(rcode, to->q_status, m,
+ NULL, ctladdr, xstart, e);
continue;
}
if (bitset(EF_DISCARD, e->e_flags))
{
if (tTd(10, 5))
{
- printf("deliver: discarding recipient ");
+ dprintf("deliver: discarding recipient ");
printaddr(to, FALSE);
}
+ /* pretend the message was sent */
+ /* XXX should we log something here? */
+ to->q_state = QS_DISCARDED;
+
/*
** Remove discard bit to prevent discard of
- ** future recipients
+ ** future recipients. This is safe because the
+ ** true "global discard" has been handled before
+ ** we get here.
*/
- e->e_flags &= ~EF_DISCARD;
+ e->e_flags &= ~EF_DISCARD;
continue;
}
@@ -1151,7 +1285,7 @@ deliver(e, firstto)
** >>>>>>>>>> function is subsumed by sendmail.
*/
- if (bitset(QBADADDR|QQUEUEUP, to->q_flags))
+ if (!QS_IS_OK(to->q_state))
continue;
/*
@@ -1178,12 +1312,13 @@ deliver(e, firstto)
m->m_name);
rcode = EX_CONFIG;
}
- giveresponse(rcode, m, NULL, ctladdr, xstart, e);
- markfailure(e, to, NULL, rcode);
+ giveresponse(rcode, to->q_status, m, NULL,
+ ctladdr, xstart, e);
+ markfailure(e, to, NULL, rcode, TRUE);
e->e_nsent++;
if (rcode == EX_OK)
{
- to->q_flags |= QSENT;
+ to->q_state = QS_SENT;
if (bitnset(M_LOCALMAILER, m->m_flags) &&
bitset(QPINGONSUCCESS, to->q_flags))
{
@@ -1207,15 +1342,46 @@ deliver(e, firstto)
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) strcat(tobuf, ",");
- (void) strcat(tobuf, to->q_paddr);
+ (void) strlcat(tobuf, ",", sizeof tobuf);
+ (void) strlcat(tobuf, to->q_paddr, sizeof tobuf);
+#endif /* _FFR_DYNAMIC_TOBUF */
+
define('u', user, e); /* to user */
p = to->q_home;
if (p == NULL && ctladdr != NULL)
p = ctladdr->q_home;
define('z', p, e); /* user's home */
+ /* set the ${dsn_notify} macro if applicable */
+ if (bitset(QHASNOTIFY, to->q_flags))
+ {
+ char notify[MAXLINE];
+
+ notify[0] = '\0';
+ if (bitset(QPINGONSUCCESS, to->q_flags))
+ (void) strlcat(notify, "SUCCESS,",
+ sizeof notify);
+ if (bitset(QPINGONFAILURE, to->q_flags))
+ (void) strlcat(notify, "FAILURE,",
+ sizeof notify);
+ if (bitset(QPINGONDELAY, to->q_flags))
+ (void) strlcat(notify, "DELAY,", sizeof notify);
+
+ /* Set to NEVER or drop trailing comma */
+ if (notify[0] == '\0')
+ (void) strlcat(notify, "NEVER", sizeof notify);
+ else
+ notify[strlen(notify) - 1] = '\0';
+
+ define(macid("{dsn_notify}", NULL), newstr(notify), e);
+ }
+ else
+ define(macid("{dsn_notify}", NULL), NULL, e);
+
/*
** Expand out this user into argument list.
*/
@@ -1233,13 +1399,44 @@ 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);
- return (0);
+ e->e_to = NULL;
+ return 0;
}
/* print out messages as full list */
+#if _FFR_DYNAMIC_TOBUF
+ {
+ int l = 1;
+ char *tobufptr;
+
+ for (to = tochain; to != NULL; to = to->q_tchain)
+ l += strlen(to->q_paddr) + 1;
+ if (l < TOBUFSIZE)
+ l = TOBUFSIZE;
+ if (l > tobufsize)
+ {
+ if (tobuf != NULL)
+ free(tobuf);
+ tobufsize = l;
+ tobuf = xalloc(tobufsize);
+ }
+ tobufptr = tobuf;
+ *tobufptr = '\0';
+ for (to = tochain; to != NULL; to = to->q_tchain)
+ {
+ snprintf(tobufptr, tobufsize - (tobufptr - tobuf),
+ ",%s", to->q_paddr);
+ tobufptr += strlen(tobufptr);
+ }
+ }
+#endif /* _FFR_DYNAMIC_TOBUF */
e->e_to = tobuf + 1;
/*
@@ -1251,7 +1448,8 @@ deliver(e, firstto)
expand(*mvp, buf, sizeof buf, e);
*pvp++ = newstr(buf);
if (pvp >= &pv[MAXPV])
- syserr("554 deliver: pv overflow after $u for %s", pv[0]);
+ syserr("554 5.3.0 deliver: pv overflow after $u for %s",
+ pv[0]);
}
*pvp++ = NULL;
@@ -1263,7 +1461,7 @@ deliver(e, firstto)
** If we are running SMTP, we just need to clean up.
*/
- /*XXX this seems a bit wierd */
+ /* XXX this seems a bit wierd */
if (ctladdr == NULL && m != ProgMailer && m != FileMailer &&
bitset(QGOODUID, e->e_from.q_flags))
ctladdr = &e->e_from;
@@ -1271,17 +1469,17 @@ deliver(e, firstto)
#if NAMED_BIND
if (ConfigLevel < 2)
_res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */
-#endif
+#endif /* NAMED_BIND */
if (tTd(11, 1))
{
- printf("openmailer:");
+ dprintf("openmailer:");
printav(pv);
}
errno = 0;
#if NAMED_BIND
h_errno = 0;
-#endif
+#endif /* NAMED_BIND */
CurHostName = NULL;
@@ -1307,7 +1505,7 @@ deliver(e, firstto)
shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
checkfd012(wbuf);
}
-#endif
+#endif /* XDEBUG */
/* check for 8-bit available */
if (bitset(EF_HAS8BIT, e->e_flags) &&
@@ -1315,11 +1513,12 @@ deliver(e, firstto)
(bitset(EF_DONT_MIME, e->e_flags) ||
!(bitset(MM_MIME8BIT, MimeMode) ||
(bitset(EF_IS_MIME, e->e_flags) &&
- bitset(MM_CVTMIME, MimeMode)))))
+ bitset(MM_CVTMIME, MimeMode)))))
{
- usrerr("554 Cannot send 8-bit data to 7-bit destination");
- rcode = EX_DATAERR;
e->e_status = "5.6.3";
+ usrerrenh(e->e_status,
+ "554 Cannot send 8-bit data to 7-bit destination");
+ rcode = EX_DATAERR;
goto give_up;
}
@@ -1330,7 +1529,7 @@ deliver(e, firstto)
if (strcmp(m->m_mailer, "[LPC]") == 0)
{
mci = (MCI *) xalloc(sizeof *mci);
- bzero((char *) mci, sizeof *mci);
+ memset((char *) mci, '\0', sizeof *mci);
mci->mci_in = stdin;
mci->mci_out = stdout;
mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN;
@@ -1344,13 +1543,23 @@ deliver(e, firstto)
if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
{
- syserr("null host name for %s mailer", m->m_mailer);
+ syserr("null destination for %s mailer", m->m_mailer);
rcode = EX_CONFIG;
goto give_up;
}
- CurHostName = pv[1];
- curhost = hostsignature(m, pv[1], e);
+# if NETUNIX
+ if (strcmp(pv[0], "FILE") == 0)
+ {
+ curhost = CurHostName = "localhost";
+ mux_path = pv[1];
+ }
+ else
+# endif /* NETUNIX */
+ {
+ CurHostName = pv[1];
+ curhost = hostsignature(m, pv[1]);
+ }
if (curhost == NULL || curhost[0] == '\0')
{
@@ -1361,47 +1570,71 @@ deliver(e, firstto)
if (!clever)
{
- syserr("554 non-clever IPC");
+ syserr("554 5.3.5 non-clever IPC");
rcode = EX_CONFIG;
goto give_up;
}
- if (pv[2] != NULL)
+ if (pv[2] != NULL
+# if NETUNIX
+ && mux_path == NULL
+# endif /* NETUNIX */
+ )
{
- port = htons(atoi(pv[2]));
+ port = htons((u_short)atoi(pv[2]));
if (port == 0)
{
+# ifdef NO_GETSERVBYNAME
+ syserr("Invalid port number: %s", pv[2]);
+# else /* NO_GETSERVBYNAME */
struct servent *sp = getservbyname(pv[2], "tcp");
if (sp == NULL)
syserr("Service %s unknown", pv[2]);
else
port = sp->s_port;
+# endif /* NO_GETSERVBYNAME */
}
}
+
+ nummxhosts = parse_hostsignature(curhost, mxhosts, m);
tryhost:
- while (*curhost != '\0')
+ while (hostnum < nummxhosts)
{
+ char sep = ':';
+ char *endp;
static char hostbuf[MAXNAME + 1];
- extern int makeconnection __P((char *, u_short, MCI *, ENVELOPE *));
- /* pull the next host from the signature */
- p = strchr(curhost, ':');
- if (p == NULL)
- p = (char *) &curhost[strlen(curhost)];
- if (p == curhost)
+# if NETINET6
+ if (*mxhosts[hostnum] == '[')
+ {
+ endp = strchr(mxhosts[hostnum] + 1, ']');
+ if (endp != NULL)
+ endp = strpbrk(endp + 1, ":,");
+ }
+ else
+ endp = strpbrk(mxhosts[hostnum], ":,");
+# else /* NETINET6 */
+ endp = strpbrk(mxhosts[hostnum], ":,");
+# endif /* NETINET6 */
+ if (endp != NULL)
+ {
+ sep = *endp;
+ *endp = '\0';
+ }
+
+ if (*mxhosts[hostnum] == '\0')
{
syserr("deliver: null host name in signature");
- curhost++;
+ hostnum++;
+ if (endp != NULL)
+ *endp = sep;
continue;
}
- i = p - curhost;
- if (i >= sizeof hostbuf)
- i = sizeof hostbuf - 1;
- strncpy(hostbuf, curhost, i);
- hostbuf[i] = '\0';
- if (*p != '\0')
- p++;
- curhost = p;
+ (void) strlcpy(hostbuf, mxhosts[hostnum],
+ sizeof hostbuf);
+ hostnum++;
+ if (endp != NULL)
+ *endp = sep;
/* see if we already know that this host is fried */
CurHostName = hostbuf;
@@ -1410,13 +1643,14 @@ tryhost:
{
if (tTd(11, 1))
{
- printf("openmailer: ");
+ 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);
+ mci->mci_deliveries++;
break;
}
mci->mci_mailer = m;
@@ -1435,20 +1669,35 @@ tryhost:
}
/* try the connection */
- sm_setproctitle(TRUE, "%s %s: %s", e->e_id, hostbuf, "user open");
- if (port == 0)
+ sm_setproctitle(TRUE, e, "%s %s: %s",
+ qid_printname(e),
+ hostbuf, "user open");
+# if NETUNIX
+ if (mux_path != NULL)
+ {
message("Connecting to %s via %s...",
- hostbuf, m->m_name);
+ mux_path, m->m_name);
+ i = makeconnection_ds(mux_path, mci);
+ }
else
- message("Connecting to %s port %d via %s...",
- hostbuf, ntohs(port), m->m_name);
- i = makeconnection(hostbuf, port, mci, e);
+# endif /* NETUNIX */
+ {
+ if (port == 0)
+ message("Connecting to %s via %s...",
+ hostbuf, m->m_name);
+ else
+ message("Connecting to %s port %d via %s...",
+ hostbuf, ntohs(port),
+ m->m_name);
+ i = makeconnection(hostbuf, port, mci, e);
+ }
mci->mci_lastuse = curtime();
+ mci->mci_deliveries = 0;
mci->mci_exitstat = i;
mci->mci_errno = errno;
-#if NAMED_BIND
+# if NAMED_BIND
mci->mci_herrno = h_errno;
-#endif
+# endif /* NAMED_BIND */
if (i == EX_OK)
{
goodmxfound = TRUE;
@@ -1461,8 +1710,8 @@ tryhost:
}
else
{
- if (tTd(11, 1))
- printf("openmailer: makeconnection => stat=%d, errno=%d\n",
+ if (tTd(11, 1))
+ dprintf("openmailer: makeconnection => stat=%d, errno=%d\n",
i, errno);
if (i == EX_TEMPFAIL)
goodmxfound = TRUE;
@@ -1481,10 +1730,10 @@ tryhost:
goto give_up;
}
mci->mci_pid = 0;
-#else /* no DAEMON */
- syserr("554 openmailer: no IPC");
+#else /* DAEMON */
+ syserr("554 5.3.5 openmailer: no IPC");
if (tTd(11, 1))
- printf("openmailer: NULL\n");
+ dprintf("openmailer: NULL\n");
rcode = EX_UNAVAILABLE;
goto give_up;
#endif /* DAEMON */
@@ -1507,10 +1756,11 @@ tryhost:
{
message("Using cached LMTP connection for %s...",
m->m_name);
+ mci->mci_deliveries++;
goto do_transfer;
}
}
-#endif
+#endif /* SMTP */
/* announce the connection to verbose listeners */
if (host == NULL || host[0] == '\0')
@@ -1529,7 +1779,7 @@ tryhost:
#if XDEBUG
checkfd012("before creating mail pipe");
-#endif
+#endif /* XDEBUG */
/* create a pipe to shove the mail through */
if (pipe(mpvect) < 0)
@@ -1537,7 +1787,7 @@ tryhost:
syserr("%s... openmailer(%s): pipe (to mailer)",
shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
if (tTd(11, 1))
- printf("openmailer: NULL\n");
+ dprintf("openmailer: NULL\n");
rcode = EX_OSERR;
goto give_up;
}
@@ -1551,7 +1801,7 @@ tryhost:
mpvect[0], mpvect[1]);
printopenfds(TRUE);
if (tTd(11, 1))
- printf("openmailer: NULL\n");
+ dprintf("openmailer: NULL\n");
rcode = EX_OSERR;
goto give_up;
}
@@ -1574,45 +1824,43 @@ tryhost:
m->m_name, mpvect[0], mpvect[1],
fileno(e->e_lockfp));
}
-#endif
+#endif /* XDEBUG */
- /* if this mailer speaks smtp, create a return pipe */
-#if SMTP
- if (clever)
+ /* create a return pipe */
+ if (pipe(rpvect) < 0)
{
- if (pipe(rpvect) < 0)
- {
- syserr("%s... openmailer(%s): pipe (from mailer)",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name);
- (void) close(mpvect[0]);
- (void) close(mpvect[1]);
- if (tTd(11, 1))
- printf("openmailer: NULL\n");
- rcode = EX_OSERR;
- goto give_up;
- }
-# if XDEBUG
- checkfdopen(rpvect[0], "rpvect[0]");
- checkfdopen(rpvect[1], "rpvect[1]");
-# endif
+ syserr("%s... openmailer(%s): pipe (from mailer)",
+ shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name);
+ (void) close(mpvect[0]);
+ (void) close(mpvect[1]);
+ if (tTd(11, 1))
+ dprintf("openmailer: NULL\n");
+ rcode = EX_OSERR;
+ goto give_up;
}
-#endif
+#if XDEBUG
+ checkfdopen(rpvect[0], "rpvect[0]");
+ checkfdopen(rpvect[1], "rpvect[1]");
+#endif /* XDEBUG */
/*
** Actually fork the mailer process.
** DOFORK is clever about retrying.
**
** Dispose of SIGCHLD signal catchers that may be laying
- ** around so that endmail will get it.
+ ** around so that endmailer will get it.
*/
if (e->e_xfp != NULL)
- (void) fflush(e->e_xfp); /* for debugging */
+ (void) fflush(e->e_xfp); /* for debugging */
(void) fflush(stdout);
(void) setsignal(SIGCHLD, SIG_DFL);
+
+
DOFORK(FORK);
/* pid is set by DOFORK */
+
if (pid < 0)
{
/* failure */
@@ -1620,22 +1868,17 @@ tryhost:
shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
(void) close(mpvect[0]);
(void) close(mpvect[1]);
-#if SMTP
- if (clever)
- {
- (void) close(rpvect[0]);
- (void) close(rpvect[1]);
- }
-#endif
+ (void) close(rpvect[0]);
+ (void) close(rpvect[1]);
if (tTd(11, 1))
- printf("openmailer: NULL\n");
+ dprintf("openmailer: NULL\n");
rcode = EX_OSERR;
goto give_up;
}
else if (pid == 0)
{
int i;
- int saveerrno;
+ int save_errno;
int new_euid = NO_UID;
int new_ruid = NO_UID;
int new_gid = NO_GID;
@@ -1653,7 +1896,7 @@ tryhost:
if (m != FileMailer || stat(tochain->q_user, &stb) < 0)
stb.st_mode = 0;
-#if HASSETUSERCONTEXT
+# if HASSETUSERCONTEXT
/*
** Set user resources.
*/
@@ -1671,11 +1914,11 @@ tryhost:
pwd, pwd->pw_uid,
LOGIN_SETRESOURCES|LOGIN_SETPRIORITY);
}
-#endif
+# endif /* HASSETUSERCONTEXT */
/* tweak niceness */
if (m->m_nice != 0)
- nice(m->m_nice);
+ (void) nice(m->m_nice);
/* reset group id */
if (bitnset(M_SPECIFIC_UID, m->m_flags))
@@ -1692,8 +1935,11 @@ tryhost:
u = ctladdr->q_user;
if (initgroups(u, ctladdr->q_gid) == -1 && suidwarn)
+ {
syserr("openmailer: initgroups(%s, %d) failed",
u, ctladdr->q_gid);
+ exit(EX_TEMPFAIL);
+ }
}
else
{
@@ -1701,7 +1947,10 @@ tryhost:
gidset[0] = ctladdr->q_gid;
if (setgroups(1, gidset) == -1 && suidwarn)
+ {
syserr("openmailer: setgroups() failed");
+ exit(EX_TEMPFAIL);
+ }
}
new_gid = ctladdr->q_gid;
}
@@ -1710,8 +1959,11 @@ tryhost:
if (!DontInitGroups)
{
if (initgroups(DefUser, DefGid) == -1 && suidwarn)
+ {
syserr("openmailer: initgroups(%s, %d) failed",
DefUser, DefGid);
+ exit(EX_TEMPFAIL);
+ }
}
else
{
@@ -1719,16 +1971,55 @@ tryhost:
gidset[0] = DefGid;
if (setgroups(1, gidset) == -1 && suidwarn)
+ {
syserr("openmailer: setgroups() failed");
+ exit(EX_TEMPFAIL);
+ }
}
if (m->m_gid == 0)
new_gid = DefGid;
else
new_gid = m->m_gid;
}
- if (new_gid != NO_GID && setgid(new_gid) < 0 && suidwarn)
- syserr("openmailer: setgid(%ld) failed",
- (long) new_gid);
+ if (new_gid != NO_GID)
+ {
+ if (RunAsUid != 0 &&
+ bitnset(M_SPECIFIC_UID, m->m_flags) &&
+ new_gid != getgid() &&
+ new_gid != getegid())
+ {
+ /* Only root can change the gid */
+ syserr("openmailer: insufficient privileges to change gid");
+ exit(EX_TEMPFAIL);
+ }
+
+ if (setgid(new_gid) < 0 && suidwarn)
+ {
+ syserr("openmailer: setgid(%ld) failed",
+ (long) new_gid);
+ exit(EX_TEMPFAIL);
+ }
+ }
+
+ /* change root to some "safe" directory */
+ if (m->m_rootdir != NULL)
+ {
+ expand(m->m_rootdir, buf, sizeof buf, e);
+ if (tTd(11, 20))
+ dprintf("openmailer: chroot %s\n",
+ buf);
+ if (chroot(buf) < 0)
+ {
+ syserr("openmailer: Cannot chroot(%s)",
+ buf);
+ exit(EX_TEMPFAIL);
+ }
+ if (chdir("/") < 0)
+ {
+ syserr("openmailer: cannot chdir(/)");
+ exit(EX_TEMPFAIL);
+ }
+ }
/* reset user id */
endpwent();
@@ -1744,40 +2035,59 @@ tryhost:
new_ruid = DefUid;
if (new_euid != NO_UID)
{
+ if (RunAsUid != 0 && new_euid != RunAsUid)
+ {
+ /* Only root can change the uid */
+ syserr("openmailer: insufficient privileges to change uid");
+ exit(EX_TEMPFAIL);
+ }
+
vendor_set_uid(new_euid);
-#if USESETEUID
+# if MAILER_SETUID_METHOD == USE_SETEUID
if (seteuid(new_euid) < 0 && suidwarn)
+ {
syserr("openmailer: seteuid(%ld) failed",
(long) new_euid);
-#else
-# if HASSETREUID
+ exit(EX_TEMPFAIL);
+ }
+# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
+# if MAILER_SETUID_METHOD == USE_SETREUID
if (setreuid(new_ruid, new_euid) < 0 && suidwarn)
+ {
syserr("openmailer: setreuid(%ld, %ld) failed",
(long) new_ruid, (long) new_euid);
-# else
+ exit(EX_TEMPFAIL);
+ }
+# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
+# if MAILER_SETUID_METHOD == USE_SETUID
if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn)
+ {
syserr("openmailer: setuid(%ld) failed",
(long) new_euid);
-# endif
-#endif
+ exit(EX_TEMPFAIL);
+ }
+# endif /* MAILER_SETUID_METHOD == USE_SETUID */
}
else if (new_ruid != NO_UID)
{
vendor_set_uid(new_ruid);
if (setuid(new_ruid) < 0 && suidwarn)
+ {
syserr("openmailer: setuid(%ld) failed",
(long) new_ruid);
+ exit(EX_TEMPFAIL);
+ }
}
if (tTd(11, 2))
- printf("openmailer: running as r/euid=%d/%d\n",
- (int) getuid(), (int) geteuid());
+ 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)
{
char *q;
- char buf[MAXLINE + 1];
for (p = m->m_execdir; p != NULL; p = q)
{
@@ -1788,7 +2098,7 @@ tryhost:
if (q != NULL)
*q++ = ':';
if (tTd(11, 20))
- printf("openmailer: trydir %s\n",
+ dprintf("openmailer: trydir %s\n",
buf);
if (buf[0] != '\0' && chdir(buf) >= 0)
break;
@@ -1796,31 +2106,16 @@ tryhost:
}
/* arrange to filter std & diag output of command */
-#if SMTP
- if (clever)
- {
- (void) close(rpvect[0]);
- if (dup2(rpvect[1], STDOUT_FILENO) < 0)
- {
- syserr("%s... openmailer(%s): cannot dup pipe %d for stdout",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name, rpvect[1]);
- _exit(EX_OSERR);
- }
- (void) close(rpvect[1]);
- }
- else
+ (void) close(rpvect[0]);
+ if (dup2(rpvect[1], STDOUT_FILENO) < 0)
{
- /* put mailer output in transcript */
- if (dup2(fileno(e->e_xfp), STDOUT_FILENO) < 0)
- {
- syserr("%s... openmailer(%s): cannot dup xscript %d for stdout",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name, fileno(e->e_xfp));
- _exit(EX_OSERR);
- }
+ syserr("%s... openmailer(%s): cannot dup pipe %d for stdout",
+ shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name, rpvect[1]);
+ _exit(EX_OSERR);
}
-#endif
+ (void) close(rpvect[1]);
+
if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
{
syserr("%s... openmailer(%s): cannot dup stdout for stderr",
@@ -1846,18 +2141,20 @@ tryhost:
register int j;
if ((j = fcntl(i, F_GETFD, 0)) != -1)
- (void) fcntl(i, F_SETFD, j | 1);
+ (void) fcntl(i, F_SETFD,
+ j | FD_CLOEXEC);
}
/* run disconnected from terminal */
(void) setsid();
/* try to execute the mailer */
- execve(m->m_mailer, (ARGV_T) pv, (ARGV_T) UserEnviron);
- saveerrno = errno;
+ (void) execve(m->m_mailer, (ARGV_T) pv,
+ (ARGV_T) UserEnviron);
+ save_errno = errno;
syserr("Cannot exec %s", m->m_mailer);
if (bitnset(M_LOCALMAILER, m->m_flags) ||
- transienterror(saveerrno))
+ transienterror(save_errno))
_exit(EX_OSERR);
_exit(EX_UNAVAILABLE);
}
@@ -1869,7 +2166,7 @@ tryhost:
if (mci == NULL)
{
mci = (MCI *) xalloc(sizeof *mci);
- bzero((char *) mci, sizeof *mci);
+ memset((char *) mci, '\0', sizeof *mci);
}
mci->mci_mailer = m;
if (clever)
@@ -1889,38 +2186,28 @@ tryhost:
syserr("deliver: cannot create mailer output channel, fd=%d",
mpvect[1]);
(void) close(mpvect[1]);
-#if SMTP
- if (clever)
- {
- (void) close(rpvect[0]);
- (void) close(rpvect[1]);
- }
-#endif
+ (void) close(rpvect[0]);
+ (void) close(rpvect[1]);
rcode = EX_OSERR;
goto give_up;
}
-#if SMTP
- if (clever)
+
+ (void) close(rpvect[1]);
+ mci->mci_in = fdopen(rpvect[0], "r");
+ if (mci->mci_in == NULL)
{
- (void) close(rpvect[1]);
- mci->mci_in = fdopen(rpvect[0], "r");
- if (mci->mci_in == NULL)
- {
- syserr("deliver: cannot create mailer input channel, fd=%d",
- mpvect[1]);
- (void) close(rpvect[0]);
- fclose(mci->mci_out);
- mci->mci_out = NULL;
- rcode = EX_OSERR;
- goto give_up;
- }
+ syserr("deliver: cannot create mailer input channel, fd=%d",
+ mpvect[1]);
+ (void) close(rpvect[0]);
+ (void) fclose(mci->mci_out);
+ mci->mci_out = NULL;
+ rcode = EX_OSERR;
+ goto give_up;
}
- else
-#endif
- {
+
+ /* Don't cache non-clever connections */
+ if (!clever)
mci->mci_flags |= MCIF_TEMP;
- mci->mci_in = NULL;
- }
}
/*
@@ -1933,11 +2220,256 @@ tryhost:
#if SMTP
if (clever && mci->mci_state != MCIS_CLOSED)
{
- extern void smtpinit __P((MAILER *, MCI *, ENVELOPE *));
+ static u_short again;
+# if SASL && SFIO
+# define DONE_TLS_B 0x01
+# define DONE_TLS bitset(DONE_TLS_B, again)
+# endif /* SASL && SFIO */
+# if STARTTLS
+# define DONE_STARTTLS_B 0x02
+# define DONE_STARTTLS bitset(DONE_STARTTLS_B, again)
+# endif /* STARTTLS */
+# define ONLY_HELO_B 0x04
+# define ONLY_HELO bitset(ONLY_HELO_B, again)
+# define SET_HELO again |= ONLY_HELO_B
+# define CLR_HELO again &= ~ONLY_HELO_B
+
+ again = 0;
+# if STARTTLS || (SASL && SFIO)
+reconnect: /* after switching to an authenticated connection */
+# endif /* STARTTLS || (SASL && SFIO) */
+
+# if SASL
+ mci->mci_saslcap = NULL;
+# endif /* SASL */
+ smtpinit(m, mci, e, ONLY_HELO);
+ CLR_HELO;
+
+# if STARTTLS
+ /* first TLS then AUTH to provide a security layer */
+ if (mci->mci_state != MCIS_CLOSED && !DONE_STARTTLS)
+ {
+ int olderrors;
+ bool hasdot;
+ bool usetls;
+ bool saveQuickAbort = QuickAbort;
+ bool saveSuprErrs = SuprErrs;
+ extern SOCKADDR CurHostAddr;
+
+ rcode = EX_OK;
+ usetls = bitset(MCIF_TLS, mci->mci_flags);
+ hasdot = CurHostName[strlen(CurHostName) - 1] == '.';
+ if (hasdot)
+ CurHostName[strlen(CurHostName) - 1] = '\0';
+ define(macid("{server_name}", NULL),
+ newstr(CurHostName), 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 _FFR_TLS_O_T
+ if (usetls)
+ {
+ olderrors = Errors;
+ QuickAbort = FALSE;
+ SuprErrs = TRUE;
+ if (rscheck("try_tls", CurHostName, NULL,
+ e, TRUE, FALSE, 8) != EX_OK
+ || Errors > olderrors)
+ usetls = FALSE;
+ SuprErrs = saveSuprErrs;
+ QuickAbort = saveQuickAbort;
+ }
+# endif /* _FFR_TLS_O_T */
+
+ /* undo change of CurHostName */
+ if (hasdot)
+ CurHostName[strlen(CurHostName)] = '.';
+ if (usetls)
+ {
+ if ((rcode = starttls(m, mci, e)) == EX_OK)
+ {
+ /* start again without STARTTLS */
+ again |= DONE_STARTTLS_B;
+ mci->mci_flags |= MCIF_TLSACT;
+ }
+ else
+ {
+ char *s;
+
+ /*
+ ** TLS negotation failed, what to do?
+ ** fall back to unencrypted connection
+ ** or abort? How to decide?
+ ** set a macro and call a ruleset.
+ */
+ mci->mci_flags &= ~MCIF_TLS;
+ switch (rcode)
+ {
+ case EX_TEMPFAIL:
+ s = "TEMP";
+ break;
+ case EX_USAGE:
+ s = "USAGE";
+ break;
+ case EX_PROTOCOL:
+ s = "PROTOCOL";
+ break;
+ case EX_SOFTWARE:
+ s = "SOFTWARE";
+ break;
+
+ /* everything else is a failure */
+ default:
+ s = "FAILURE";
+ rcode = EX_TEMPFAIL;
+ }
+ define(macid("{verify}", NULL),
+ newstr(s), e);
+ }
+ }
+ else
+ define(macid("{verify}", NULL), "NONE", e);
+ olderrors = Errors;
+ QuickAbort = FALSE;
+ SuprErrs = TRUE;
+
+ /*
+ ** rcode == EX_SOFTWARE is special:
+ ** the TLS negotation failed
+ ** we have to drop the connection no matter what
+ ** However, we call tls_server to give it the chance
+ ** to log the problem and return an appropriate
+ ** error code.
+ */
+ if (rscheck("tls_server",
+ macvalue(macid("{verify}", NULL), e),
+ NULL, e, TRUE, TRUE, 6) != EX_OK ||
+ Errors > olderrors ||
+ rcode == EX_SOFTWARE)
+ {
+ char enhsc[ENHSCLEN];
+ extern char MsgBuf[];
+
+ if (ISSMTPCODE(MsgBuf) &&
+ extenhsc(MsgBuf + 4, ' ', enhsc) > 0)
+ {
+ p = newstr(MsgBuf);
+ }
+ else
+ {
+ p = "403 4.7.0 server not authenticated.";
+ (void) strlcpy(enhsc, "4.7.0",
+ sizeof enhsc);
+ }
+ SuprErrs = saveSuprErrs;
+ QuickAbort = saveQuickAbort;
- smtpinit(m, mci, e);
+ if (rcode == EX_SOFTWARE)
+ {
+ /* drop the connection */
+ mci->mci_state = MCIS_QUITING;
+ if (mci->mci_in != NULL)
+ {
+ (void) fclose(mci->mci_in);
+ mci->mci_in = NULL;
+ }
+ mci->mci_flags &= ~MCIF_TLSACT;
+ (void) endmailer(mci, e, pv);
+ }
+ else
+ {
+ /* abort transfer */
+ smtpquit(m, mci, e);
+ }
+
+ /* temp or permanent failure? */
+ rcode = (*p == '4') ? EX_TEMPFAIL
+ : EX_UNAVAILABLE;
+ mci_setstat(mci, rcode, newstr(enhsc), p);
+
+ /*
+ ** hack to get the error message into
+ ** the envelope (done in giveresponse())
+ */
+ (void) strlcpy(SmtpError, p, sizeof SmtpError);
+ }
+ QuickAbort = saveQuickAbort;
+ SuprErrs = saveSuprErrs;
+ if (DONE_STARTTLS && mci->mci_state != MCIS_CLOSED)
+ {
+ SET_HELO;
+ mci->mci_flags &= ~MCIF_EXTENS;
+ goto reconnect;
+ }
+ }
+# 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_TLS &&
+# endif /* SFIO */
+ SASLInfo != NULL)
+ {
+ /*
+ ** should we require some minimum authentication?
+ ** XXX ignore result?
+ */
+ if (smtpauth(m, mci, e) == EX_OK)
+ {
+# if SFIO
+ int result;
+ sasl_ssf_t *ssf;
+
+ /* get security strength (features) */
+ result = sasl_getprop(mci->mci_conn, SASL_SSF,
+ (void **) &ssf);
+ if (LogLevel > 9)
+ sm_syslog(LOG_INFO, NOQID,
+ "SASL: outgoing connection to %.64s: mech=%.16s, bits=%d",
+ mci->mci_host,
+ macvalue(macid("{auth_type}",
+ NULL), e),
+ *ssf);
+ /*
+ ** only switch to encrypted connection
+ ** if a security layer has been negotiated
+ */
+ if (result == SASL_OK && *ssf > 0)
+ {
+ /*
+ ** convert sfio stuff to use SASL
+ ** check return values
+ ** if the call fails,
+ ** fall back to unencrypted version
+ ** unless some cf option requires
+ ** encryption then the connection must
+ ** be aborted
+ */
+ if (sfdcsasl(mci->mci_in, mci->mci_out,
+ mci->mci_conn) == 0)
+ {
+ again |= DONE_TLS_B;
+ SET_HELO;
+ mci->mci_flags &= ~MCIF_EXTENS;
+ mci->mci_flags |= MCIF_AUTHACT;
+ goto reconnect;
+ }
+ syserr("SASL TLS switch failed in client");
+ }
+ /* else? XXX */
+# endif /* SFIO */
+ mci->mci_flags |= MCIF_AUTHACT;
+
+ }
+ }
+# endif /* SASL */
}
-#endif
+
+#endif /* SMTP */
do_transfer:
/* clear out per-message flags from connection structure */
@@ -1962,11 +2494,11 @@ do_transfer:
(p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
mci->mci_flags |= MCIF_CVT7TO8;
}
-#endif
+#endif /* MIME7TO8 */
if (tTd(11, 1))
{
- printf("openmailer: ");
+ dprintf("openmailer: ");
mci_dump(mci, FALSE);
}
@@ -1977,23 +2509,23 @@ do_transfer:
errno = mci->mci_errno;
#if NAMED_BIND
h_errno = mci->mci_herrno;
-#endif
+#endif /* NAMED_BIND */
if (rcode == EX_OK)
{
/* shouldn't happen */
- syserr("554 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s",
- (long) mci, rcode, errno, mci->mci_state,
+ 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);
rcode = EX_SOFTWARE;
}
#if DAEMON
- else if (curhost != NULL && *curhost != '\0')
+ else if (nummxhosts > hostnum)
{
/* try next MX site */
goto tryhost;
}
-#endif
+#endif /* DAEMON */
}
else if (!clever)
{
@@ -2001,7 +2533,6 @@ do_transfer:
** Format and send message.
*/
- mci->mci_contentlen = 0;
putfromline(mci, e);
(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
(*e->e_putbody)(mci, e, NULL);
@@ -2012,10 +2543,6 @@ do_transfer:
else
#if SMTP
{
- extern int smtpmailfrom __P((MAILER *, MCI *, ENVELOPE *));
- extern int smtprcpt __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *));
- extern int smtpdata __P((MAILER *, MCI *, ENVELOPE *));
-
/*
** Send the MAIL FROM: protocol
*/
@@ -2028,18 +2555,38 @@ do_transfer:
/* send the recipient list */
tobuf[0] = '\0';
+
for (to = tochain; to != NULL; to = to->q_tchain)
{
e->e_to = to->q_paddr;
- if (strlen(to->q_paddr) + (t - tobuf) + 2 > sizeof tobuf)
+#if !_FFR_DYNAMIC_TOBUF
+ if (strlen(to->q_paddr) +
+ (t - tobuf) + 2 > sizeof tobuf)
{
/* not enough room */
continue;
}
- else if ((i = smtprcpt(to, m, mci, e)) != EX_OK)
+#endif /* !_FFR_DYNAMIC_TOBUF */
+
+# if STARTTLS
+# if _FFR_TLS_RCPT
+ i = rscheck("tls_rcpt", to->q_user, NULL, e,
+ TRUE, TRUE, 4);
+ if (i != EX_OK)
{
- markfailure(e, to, mci, i);
- giveresponse(i, m, mci, ctladdr, xstart, e);
+ markfailure(e, to, mci, i, FALSE);
+ giveresponse(i, to->q_status, m,
+ mci, ctladdr, xstart, e);
+ continue;
+ }
+# endif /* _FFR_TLS_RCPT */
+# endif /* STARTTLS */
+
+ if ((i = smtprcpt(to, m, mci, e)) != EX_OK)
+ {
+ markfailure(e, to, mci, i, FALSE);
+ giveresponse(i, to->q_status, m,
+ mci, ctladdr, xstart, e);
}
else
{
@@ -2065,16 +2612,16 @@ do_transfer:
}
}
# if DAEMON
- if (rcode == EX_TEMPFAIL && curhost != NULL && *curhost != '\0')
+ if (rcode == EX_TEMPFAIL && nummxhosts > hostnum)
{
/* try next MX site */
goto tryhost;
}
-# endif
+# endif /* DAEMON */
}
-#else /* not SMTP */
+#else /* SMTP */
{
- syserr("554 deliver: need SMTP compiled to use clever mailer");
+ syserr("554 5.3.5 deliver: need SMTP compiled to use clever mailer");
rcode = EX_CONFIG;
goto give_up;
}
@@ -2082,7 +2629,7 @@ do_transfer:
#if NAMED_BIND
if (ConfigLevel < 2)
_res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */
-#endif
+#endif /* NAMED_BIND */
if (tTd(62, 1))
checkfds("after delivery");
@@ -2103,62 +2650,79 @@ do_transfer:
anyok = FALSE;
}
else
-#endif
+#endif /* SMTP */
anyok = rcode == EX_OK;
for (to = tochain; to != NULL; to = to->q_tchain)
{
/* see if address already marked */
- if (bitset(QBADADDR|QQUEUEUP, to->q_flags))
+ 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))
{
- extern int smtpgetstat __P((MAILER *, MCI *, ENVELOPE *));
-
if (lmtp_rcode == EX_OK)
rcode = smtpgetstat(m, mci, e);
if (rcode == EX_OK)
{
- if (strlen(to->q_paddr) + strlen(tobuf) + 2 > sizeof tobuf)
+#if !_FFR_DYNAMIC_TOBUF
+ if (strlen(to->q_paddr) +
+ strlen(tobuf) + 2 > sizeof tobuf)
{
syserr("LMTP tobuf overflow");
}
else
+#endif /* !_FFR_DYNAMIC_TOBUF */
{
- strcat(tobuf, ",");
- strcat(tobuf, to->q_paddr);
+ (void) strlcat(tobuf, ",",
+ sizeof tobuf);
+ (void) strlcat(tobuf, to->q_paddr,
+ sizeof tobuf);
}
anyok = TRUE;
}
else
{
e->e_to = to->q_paddr;
- markfailure(e, to, mci, rcode);
- giveresponse(rcode, m, mci, ctladdr, xstart, e);
+ markfailure(e, to, mci, rcode, TRUE);
+ giveresponse(rcode, to->q_status, m, mci,
+ ctladdr, xstart, e);
e->e_to = tobuf + 1;
continue;
}
}
else
-#endif
+#endif /* SMTP */
{
/* mark bad addresses */
if (rcode != EX_OK)
{
if (goodmxfound && rcode == EX_NOHOST)
rcode = EX_TEMPFAIL;
- markfailure(e, to, mci, rcode);
+ markfailure(e, to, mci, rcode, TRUE);
continue;
}
}
/* successful delivery */
- to->q_flags |= QSENT;
+ to->q_state = QS_SENT;
to->q_statdate = curtime();
e->e_nsent++;
+
+#if QUEUE
+ /*
+ ** Checkpoint the send list every few addresses
+ */
+
+ if (e->e_nsent >= CheckpointInterval)
+ {
+ queueup(e, FALSE);
+ e->e_nsent = 0;
+ }
+#endif /* QUEUE */
+
if (bitnset(M_LOCALMAILER, m->m_flags) &&
bitset(QPINGONSUCCESS, to->q_flags))
{
@@ -2192,10 +2756,10 @@ do_transfer:
if (mci != NULL && mci->mci_state == MCIS_ACTIVE)
mci->mci_state = MCIS_OPEN;
}
-#endif
+#endif /* SMTP */
if (tobuf[0] != '\0')
- giveresponse(rcode, m, mci, ctladdr, xstart, e);
+ giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e);
if (anyok)
markstats(e, tochain, FALSE);
mci_store_persistent(mci);
@@ -2205,7 +2769,7 @@ do_transfer:
if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED &&
!bitset(MCIF_CACHED, mci->mci_flags))
smtpquit(m, mci, e);
-#endif
+#endif /* SMTP */
/*
** Restore state and return.
@@ -2222,12 +2786,14 @@ do_transfer:
m->m_name);
checkfd012(wbuf);
}
-#endif
+#endif /* XDEBUG */
errno = 0;
define('g', (char *) NULL, e);
- return (rcode);
+ e->e_to = NULL;
+ return rcode;
}
+
/*
** MARKFAILURE -- mark a failure on a specific address.
**
@@ -2236,6 +2802,7 @@ do_transfer:
** q -- the address to mark.
** mci -- mailer connection information.
** rcode -- the code signifying the particular failure.
+** ovr -- override an existing code?
**
** Returns:
** none.
@@ -2246,14 +2813,16 @@ do_transfer:
** the message will be queued, as appropriate.
*/
-void
-markfailure(e, q, mci, rcode)
+static void
+markfailure(e, q, mci, rcode, ovr)
register ENVELOPE *e;
register ADDRESS *q;
register MCI *mci;
int rcode;
+ bool ovr;
{
- char *stat = NULL;
+ char *status = NULL;
+ char *rstatus = NULL;
switch (rcode)
{
@@ -2263,53 +2832,52 @@ markfailure(e, q, mci, rcode)
case EX_TEMPFAIL:
case EX_IOERR:
case EX_OSERR:
- q->q_flags |= QQUEUEUP;
- q->q_flags &= ~QDONTSEND;
+ q->q_state = QS_QUEUEUP;
break;
default:
- q->q_flags |= QBADADDR;
+ q->q_state = QS_BADADDR;
break;
}
/* find most specific error code possible */
if (mci != NULL && mci->mci_status != NULL)
{
- q->q_status = mci->mci_status;
+ status = mci->mci_status;
if (mci->mci_rstatus != NULL)
- q->q_rstatus = newstr(mci->mci_rstatus);
+ rstatus = newstr(mci->mci_rstatus);
else
- q->q_rstatus = NULL;
+ rstatus = NULL;
}
else if (e->e_status != NULL)
{
- q->q_status = e->e_status;
- q->q_rstatus = NULL;
+ status = e->e_status;
+ rstatus = NULL;
}
else
{
switch (rcode)
{
case EX_USAGE:
- stat = "5.5.4";
+ status = "5.5.4";
break;
case EX_DATAERR:
- stat = "5.5.2";
+ status = "5.5.2";
break;
case EX_NOUSER:
- stat = "5.1.1";
+ status = "5.1.1";
break;
case EX_NOHOST:
- stat = "5.1.2";
+ status = "5.1.2";
break;
case EX_NOINPUT:
case EX_CANTCREAT:
case EX_NOPERM:
- stat = "5.3.0";
+ status = "5.3.0";
break;
case EX_UNAVAILABLE:
@@ -2317,34 +2885,41 @@ markfailure(e, q, mci, rcode)
case EX_OSFILE:
case EX_PROTOCOL:
case EX_CONFIG:
- stat = "5.5.0";
+ status = "5.5.0";
break;
case EX_OSERR:
case EX_IOERR:
- stat = "4.5.0";
+ status = "4.5.0";
break;
case EX_TEMPFAIL:
- stat = "4.2.0";
+ status = "4.2.0";
break;
}
- if (stat != NULL)
- q->q_status = stat;
}
- q->q_statdate = curtime();
- if (CurHostName != NULL && CurHostName[0] != '\0')
- q->q_statmta = newstr(CurHostName);
+ /* new status? */
+ if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL ||
+ *q->q_status == '\0' || *q->q_status < *status))
+ {
+ q->q_status = status;
+ q->q_rstatus = rstatus;
+ }
if (rcode != EX_OK && q->q_rstatus == NULL &&
q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL &&
- strcasecmp(q->q_mailer->m_diagtype, "UNIX") == 0)
+ strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0)
{
- char buf[30];
+ char buf[16];
(void) snprintf(buf, sizeof buf, "%d", rcode);
q->q_rstatus = newstr(buf);
}
+
+ q->q_statdate = curtime();
+ if (CurHostName != NULL && CurHostName[0] != '\0' &&
+ mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags))
+ q->q_statmta = newstr(CurHostName);
}
/*
** ENDMAILER -- Wait for mailer to terminate.
@@ -2367,6 +2942,15 @@ markfailure(e, q, mci, rcode)
** none.
*/
+static jmp_buf EndWaitTimeout;
+
+static void
+endwaittimeout()
+{
+ errno = ETIMEDOUT;
+ longjmp(EndWaitTimeout, 1);
+}
+
int
endmailer(mci, e, pv)
register MCI *mci;
@@ -2374,31 +2958,78 @@ endmailer(mci, e, pv)
char **pv;
{
int st;
+ int save_errno = errno;
+ char buf[MAXLINE];
+ EVENT *ev = NULL;
+
mci_unlock_host(mci);
- /* close any connections */
- if (mci->mci_in != NULL)
- (void) xfclose(mci->mci_in, mci->mci_mailer->m_name, "mci_in");
+#if SASL
+ /* shutdown SASL */
+ if (bitset(MCIF_AUTHACT, mci->mci_flags))
+ {
+ sasl_dispose(&mci->mci_conn);
+ mci->mci_flags &= ~MCIF_AUTHACT;
+ }
+#endif /* SASL */
+
+#if STARTTLS
+ /* shutdown TLS */
+ (void) endtlsclt(mci);
+#endif /* STARTTLS */
+
+ /* close output to mailer */
if (mci->mci_out != NULL)
- (void) xfclose(mci->mci_out, mci->mci_mailer->m_name, "mci_out");
+ (void) fclose(mci->mci_out);
+
+ /* 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);
+ }
+
+ /* now close the input */
+ if (mci->mci_in != NULL)
+ (void) fclose(mci->mci_in);
mci->mci_in = mci->mci_out = NULL;
mci->mci_state = MCIS_CLOSED;
+ errno = save_errno;
+
/* in the IPC case there is nothing to wait for */
if (mci->mci_pid == 0)
- return (EX_OK);
+ return EX_OK;
-#if _FFR_TIMEOUT_WAIT
- put a timeout around the wait
-#endif
+ /* put a timeout around the wait */
+ if (mci->mci_mailer->m_wait > 0)
+ {
+ if (setjmp(EndWaitTimeout) == 0)
+ ev = setevent(mci->mci_mailer->m_wait,
+ endwaittimeout, 0);
+ else
+ {
+ syserr("endmailer %s: wait timeout (%d)",
+ mci->mci_mailer->m_name,
+ mci->mci_mailer->m_wait);
+ return EX_TEMPFAIL;
+ }
+ }
- /* wait for the mailer process to die and collect status */
+ /* wait for the mailer process, collect status */
st = waitfor(mci->mci_pid);
+ save_errno = errno;
+ if (ev != NULL)
+ clrevent(ev);
+ errno = save_errno;
+
if (st == -1)
{
syserr("endmailer %s: wait", mci->mci_mailer->m_name);
- return (EX_SOFTWARE);
+ return EX_SOFTWARE;
}
if (WIFEXITED(st))
@@ -2408,8 +3039,10 @@ endmailer(mci, e, pv)
}
/* it died a horrid death */
- syserr("451 mailer %s died with signal %o",
- mci->mci_mailer->m_name, st);
+ syserr("451 4.3.0 mailer %s died with signal %d%s",
+ mci->mci_mailer->m_name, WTERMSIG(st),
+ WCOREDUMP(st) ? " (core dumped)" :
+ (WIFSTOPPED(st) ? " (stopped)" : ""));
/* log the arguments */
if (pv != NULL && e->e_xfp != NULL)
@@ -2423,15 +3056,16 @@ endmailer(mci, e, pv)
}
ExitStat = EX_TEMPFAIL;
- return (EX_TEMPFAIL);
+ return EX_TEMPFAIL;
}
/*
** GIVERESPONSE -- Interpret an error response from a mailer
**
** Parameters:
-** stat -- the status code from the mailer (high byte
+** status -- the status code from the mailer (high byte
** only; core dumps must have been taken care of
** already).
+** dsn -- the DSN associated with the address, if any.
** m -- the mailer info for this mailer.
** mci -- the mailer connection info -- can be NULL if the
** response is given before the connection is made.
@@ -2450,8 +3084,9 @@ endmailer(mci, e, pv)
*/
void
-giveresponse(stat, m, mci, ctladdr, xstart, e)
- int stat;
+giveresponse(status, dsn, m, mci, ctladdr, xstart, e)
+ int status;
+ char *dsn;
register MAILER *m;
register MCI *mci;
ADDRESS *ctladdr;
@@ -2461,7 +3096,10 @@ giveresponse(stat, m, mci, ctladdr, xstart, e)
register const char *statmsg;
extern char *SysExMsg[];
register int i;
+ int errnum = errno;
+ int off = 4;
extern int N_SysEx;
+ char dsnbuf[ENHSCLEN];
char buf[MAXLINE];
if (e == NULL)
@@ -2471,25 +3109,27 @@ giveresponse(stat, m, mci, ctladdr, xstart, e)
** Compute status message from code.
*/
- i = stat - EX__BASE;
- if (stat == 0)
+ i = status - EX__BASE;
+ if (status == 0)
{
- statmsg = "250 Sent";
+ statmsg = "250 2.0.0 Sent";
if (e->e_statmsg != NULL)
{
(void) snprintf(buf, sizeof buf, "%s (%s)",
- statmsg, shortenstring(e->e_statmsg, 403));
+ statmsg,
+ shortenstring(e->e_statmsg, 403));
statmsg = buf;
}
}
else if (i < 0 || i >= N_SysEx)
{
- (void) snprintf(buf, sizeof buf, "554 unknown mailer error %d",
- stat);
- stat = EX_UNAVAILABLE;
+ (void) snprintf(buf, sizeof buf,
+ "554 5.3.0 unknown mailer error %d",
+ status);
+ status = EX_UNAVAILABLE;
statmsg = buf;
}
- else if (stat == EX_TEMPFAIL)
+ else if (status == EX_TEMPFAIL)
{
char *bp = buf;
@@ -2499,10 +3139,10 @@ giveresponse(stat, m, mci, ctladdr, xstart, e)
if (h_errno == TRY_AGAIN)
statmsg = errstring(h_errno+E_DNSBASE);
else
-#endif
+#endif /* NAMED_BIND */
{
- if (errno != 0)
- statmsg = errstring(errno);
+ if (errnum != 0)
+ statmsg = errstring(errnum);
else
{
#if SMTP
@@ -2513,25 +3153,55 @@ giveresponse(stat, m, mci, ctladdr, xstart, e)
}
}
if (statmsg != NULL && statmsg[0] != '\0')
+ {
+ switch (errnum)
+ {
+#ifdef ENETDOWN
+ case ENETDOWN: /* Network is down */
+#endif /* ENETDOWN */
+#ifdef ENETUNREACH
+ case ENETUNREACH: /* Network is unreachable */
+#endif /* ENETUNREACH */
+#ifdef ENETRESET
+ case ENETRESET: /* Network dropped connection on reset */
+#endif /* ENETRESET */
+#ifdef ECONNABORTED
+ case ECONNABORTED: /* Software caused connection abort */
+#endif /* ECONNABORTED */
+#ifdef EHOSTDOWN
+ case EHOSTDOWN: /* Host is down */
+#endif /* EHOSTDOWN */
+#ifdef EHOSTUNREACH
+ case EHOSTUNREACH: /* No route to host */
+#endif /* EHOSTUNREACH */
+ if (mci->mci_host != NULL)
+ {
+ snprintf(bp, SPACELEFT(buf, bp),
+ ": %s", mci->mci_host);
+ bp += strlen(bp);
+ }
+ break;
+ }
snprintf(bp, SPACELEFT(buf, bp), ": %s", statmsg);
+ }
statmsg = buf;
}
#if NAMED_BIND
- else if (stat == EX_NOHOST && h_errno != 0)
+ 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 = buf;
}
-#endif
+#endif /* NAMED_BIND */
else
{
statmsg = SysExMsg[i];
- if (*statmsg++ == ':' && errno != 0)
+ if (*statmsg++ == ':' && errnum != 0)
{
(void) snprintf(buf, sizeof buf, "%s: %s",
- statmsg, errstring(errno));
+ statmsg, errstring(errnum));
statmsg = buf;
}
}
@@ -2540,21 +3210,53 @@ giveresponse(stat, m, mci, ctladdr, xstart, e)
** Print the message as appropriate
*/
- if (stat == EX_OK || stat == EX_TEMPFAIL)
+ if (status == EX_OK || status == EX_TEMPFAIL)
{
extern char MsgBuf[];
- message("%s", &statmsg[4]);
- if (stat == EX_TEMPFAIL && e->e_xfp != NULL)
+ if ((off = isenhsc(statmsg + 4, ' ')) > 0)
+ {
+ if (dsn == NULL)
+ {
+ snprintf(dsnbuf, sizeof dsnbuf,
+ "%.*s", off, statmsg + 4);
+ dsn = dsnbuf;
+ }
+ off += 5;
+ }
+ else
+ {
+ off = 4;
+ }
+ message("%s", statmsg + off);
+ if (status == EX_TEMPFAIL && e->e_xfp != NULL)
fprintf(e->e_xfp, "%s\n", &MsgBuf[4]);
}
else
{
- char mbuf[8];
+ char mbuf[ENHSCLEN + 4];
Errors++;
- snprintf(mbuf, sizeof mbuf, "%.3s %%s", statmsg);
- usrerr(mbuf, &statmsg[4]);
+ if ((off = isenhsc(statmsg + 4, ' ')) > 0 &&
+ off < sizeof mbuf - 4)
+ {
+ if (dsn == NULL)
+ {
+ snprintf(dsnbuf, sizeof dsnbuf,
+ "%.*s", off, statmsg + 4);
+ dsn = dsnbuf;
+ }
+ off += 5;
+ (void) strlcpy(mbuf, statmsg, off);
+ (void) strlcat(mbuf, " %s", sizeof mbuf);
+ }
+ else
+ {
+ dsnbuf[0] = '\0';
+ (void) snprintf(mbuf, sizeof mbuf, "%.3s %%s", statmsg);
+ off = 4;
+ }
+ usrerr(mbuf, &statmsg[off]);
}
/*
@@ -2565,25 +3267,27 @@ giveresponse(stat, m, mci, ctladdr, xstart, e)
*/
if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) &&
- LogLevel > ((stat == EX_TEMPFAIL) ? 8 : (stat == EX_OK) ? 7 : 6))
- logdelivery(m, mci, &statmsg[4], ctladdr, xstart, e);
+ LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6))
+ logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e);
if (tTd(11, 2))
- printf("giveresponse: stat=%d, e->e_message=%s\n",
- stat, e->e_message == NULL ? "<NULL>" : e->e_message);
-
- if (stat != EX_TEMPFAIL)
- setstat(stat);
- if (stat != EX_OK && (stat != EX_TEMPFAIL || e->e_message == NULL))
+ dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s\n",
+ status,
+ dsn == NULL ? "<NULL>" : dsn,
+ e->e_message == NULL ? "<NULL>" : e->e_message);
+
+ if (status != EX_TEMPFAIL)
+ setstat(status);
+ if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL))
{
if (e->e_message != NULL)
free(e->e_message);
- e->e_message = newstr(&statmsg[4]);
+ e->e_message = newstr(statmsg + off);
}
errno = 0;
#if NAMED_BIND
h_errno = 0;
-#endif
+#endif /* NAMED_BIND */
}
/*
** LOGDELIVERY -- log the delivery in the system log
@@ -2595,8 +3299,9 @@ giveresponse(stat, m, mci, ctladdr, xstart, e)
** Parameters:
** m -- the mailer info. Can be NULL for initial queue.
** mci -- the mailer connection info -- can be NULL if the
-** log is occuring when no connection is active.
-** stat -- the message to print for the status.
+** log is occurring when no connection is active.
+** dsn -- the DSN attached to the status.
+** status -- the message to print for the status.
** ctladdr -- the controlling address for the to list.
** xstart -- the transaction start time, used for
** computing transaction delay.
@@ -2610,10 +3315,11 @@ giveresponse(stat, m, mci, ctladdr, xstart, e)
*/
void
-logdelivery(m, mci, stat, ctladdr, xstart, e)
+logdelivery(m, mci, dsn, status, ctladdr, xstart, e)
MAILER *m;
register MCI *mci;
- const char *stat;
+ char *dsn;
+ const char *status;
ADDRESS *ctladdr;
time_t xstart;
register ENVELOPE *e;
@@ -2623,31 +3329,32 @@ logdelivery(m, mci, stat, ctladdr, xstart, e)
int l;
char buf[1024];
-# if (SYSLOG_BUFSIZE) >= 256
+#if (SYSLOG_BUFSIZE) >= 256
/* ctladdr: max 106 bytes */
bp = buf;
if (ctladdr != NULL)
{
snprintf(bp, SPACELEFT(buf, bp), ", ctladdr=%s",
- shortenstring(ctladdr->q_paddr, 83));
+ 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);
+ (int) ctladdr->q_uid,
+ (int) ctladdr->q_gid);
bp += strlen(bp);
}
}
/* delay & xdelay: max 41 bytes */
snprintf(bp, SPACELEFT(buf, bp), ", delay=%s",
- pintvl(curtime() - e->e_ctime, TRUE));
+ pintvl(curtime() - e->e_ctime, TRUE));
bp += strlen(bp);
if (xstart != (time_t) 0)
{
snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s",
- pintvl(curtime() - xstart, TRUE));
+ pintvl(curtime() - xstart, TRUE));
bp += strlen(bp);
}
@@ -2658,98 +3365,142 @@ logdelivery(m, mci, stat, ctladdr, xstart, e)
bp += strlen(bp);
}
+ /* pri: changes with each delivery attempt */
+ 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
+# endif /* DAEMON */
snprintf(bp, SPACELEFT(buf, bp), ", relay=%s",
- shortenstring(mci->mci_host, 40));
+ 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));
+ anynet_ntoa(&CurHostAddr));
}
-# endif
+# endif /* DAEMON */
}
- else if (strcmp(stat, "queued") != 0)
+ 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));
+ shortenstring(p, 40));
}
}
bp += strlen(bp);
-#define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4)
-#if (STATLEN) < 63
-# undef STATLEN
-# define STATLEN 63
-#endif
-#if (STATLEN) > 203
-# undef STATLEN
-# define STATLEN 203
-#endif
+ /* dsn */
+ if (dsn != NULL && *dsn != '\0')
+ {
+ snprintf(bp, SPACELEFT(buf, bp), ", dsn=%s",
+ shortenstring(dsn, ENHSCLEN));
+ bp += strlen(bp);
+ }
+
+# define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4)
+# if (STATLEN) < 63
+# undef STATLEN
+# define STATLEN 63
+# endif /* (STATLEN) < 63 */
+# if (STATLEN) > 203
+# undef STATLEN
+# define STATLEN 203
+# endif /* (STATLEN) > 203 */
/* stat: max 210 bytes */
if ((bp - buf) > (sizeof buf - ((STATLEN) + 20)))
{
/* desperation move -- truncate data */
bp = buf + sizeof buf - ((STATLEN) + 17);
- strcpy(bp, "...");
+ (void) strlcpy(bp, "...", SPACELEFT(buf, bp));
bp += 3;
}
- (void) strcpy(bp, ", stat=");
+ (void) strlcpy(bp, ", stat=", SPACELEFT(buf, bp));
bp += strlen(bp);
- (void) strcpy(bp, shortenstring(stat, (STATLEN)));
+ (void) strlcpy(bp, shortenstring(status, STATLEN), SPACELEFT(buf, bp));
/* id, to: max 13 + TOBUFSIZE bytes */
l = SYSLOG_BUFSIZE - 100 - strlen(buf);
- p = e->e_to;
+ p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
while (strlen(p) >= (SIZE_T) l)
{
- register char *q = strchr(p + l, ',');
+ register char *q;
+#if _FFR_DYNAMIC_TOBUF
+ for (q = p + l; q > p; q--)
+ {
+ if (*q == ',')
+ break;
+ }
+ if (p == q)
+ break;
+#else /* _FFR_DYNAMIC_TOBUF */
+ q = strchr(p + l, ',');
if (q == NULL)
break;
+#endif /* _FFR_DYNAMIC_TOBUF */
+
sm_syslog(LOG_INFO, e->e_id,
- "to=%.*s [more]%s",
- ++q - p, p, buf);
+ "to=%.*s [more]%s",
+ ++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 /* we have a very short log buffer size */
+#else /* (SYSLOG_BUFSIZE) >= 256 */
l = SYSLOG_BUFSIZE - 85;
- p = e->e_to;
+ p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
while (strlen(p) >= (SIZE_T) l)
{
- register char *q = strchr(p + l, ',');
+ register char *q;
+#if _FFR_DYNAMIC_TOBUF
+ for (q = p + l; q > p; q--)
+ {
+ if (*q == ',')
+ break;
+ }
+ if (p == q)
+ break;
+#else /* _FFR_DYNAMIC_TOBUF */
+ q = strchr(p + l, ',');
if (q == NULL)
break;
+#endif /* _FFR_DYNAMIC_TOBUF */
+
sm_syslog(LOG_INFO, e->e_id,
- "to=%.*s [more]",
- ++q - p, p);
+ "to=%.*s [more]",
+ ++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));
+ shortenstring(ctladdr->q_paddr, 83));
bp += strlen(bp);
if (bitset(QGOODUID, ctladdr->q_flags))
{
@@ -2761,12 +3512,12 @@ logdelivery(m, mci, stat, ctladdr, xstart, e)
}
bp = buf;
snprintf(bp, SPACELEFT(buf, bp), "delay=%s",
- pintvl(curtime() - e->e_ctime, TRUE));
+ pintvl(curtime() - e->e_ctime, TRUE));
bp += strlen(bp);
if (xstart != (time_t) 0)
{
snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s",
- pintvl(curtime() - xstart, TRUE));
+ pintvl(curtime() - xstart, TRUE));
bp += strlen(bp);
}
@@ -2783,7 +3534,7 @@ logdelivery(m, mci, stat, ctladdr, xstart, e)
{
# if DAEMON
extern SOCKADDR CurHostAddr;
-# endif
+# endif /* DAEMON */
snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", mci->mci_host);
bp += strlen(bp);
@@ -2792,9 +3543,9 @@ logdelivery(m, mci, stat, ctladdr, xstart, e)
if (CurHostAddr.sa.sa_family != 0)
snprintf(bp, SPACELEFT(buf, bp), " [%.100s]",
anynet_ntoa(&CurHostAddr));
-# endif
+# endif /* DAEMON */
}
- else if (strcmp(stat, "queued") != 0)
+ else if (strcmp(status, "queued") != 0)
{
p = macvalue('h', e);
if (p != NULL && p[0] != '\0')
@@ -2803,8 +3554,8 @@ logdelivery(m, mci, stat, ctladdr, xstart, e)
if (buf[0] != '\0')
sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
- sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(stat, 63));
-# endif /* short log buffer */
+ 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)
@@ -2852,14 +3603,14 @@ putfromline(mci, e)
char *at;
char hname[MAXNAME];
- /*
+ /*
** If we can construct a UUCP path, do so
*/
at = strrchr(buf, '@');
if (at == NULL)
{
- expand( "\201k", hname, sizeof hname, e);
+ expand("\201k", hname, sizeof hname, e);
at = hname;
}
else
@@ -2907,6 +3658,7 @@ putbody(mci, e, separator)
register ENVELOPE *e;
char *separator;
{
+ bool dead = FALSE;
char buf[MAXLINE];
char *boundaries[MAXMIMENESTING + 1];
@@ -2920,8 +3672,13 @@ putbody(mci, e, separator)
e->e_dfp = fopen(df, "r");
if (e->e_dfp == NULL)
- syserr("putbody: Cannot open %s for %s from %s",
- df, e->e_to, e->e_from.q_paddr);
+ {
+ char *msg = "!putbody: Cannot open %s for %s from %s";
+
+ if (errno == ENOENT)
+ msg++;
+ syserr(msg, df, e->e_to, e->e_from.q_paddr);
+ }
}
if (e->e_dfp == NULL)
{
@@ -2933,6 +3690,7 @@ putbody(mci, e, separator)
putline("<<< No Message Collected >>>", mci);
goto endofmessage;
}
+
if (e->e_dfino == (ino_t) 0)
{
struct stat stbuf;
@@ -2945,7 +3703,9 @@ putbody(mci, e, separator)
e->e_dfino = stbuf.st_ino;
}
}
- rewind(e->e_dfp);
+
+ /* paranoia: the df file should always be in a rewound state */
+ (void) bfrewind(e->e_dfp);
#if MIME8TO7
if (bitset(MCIF_CVT8TO7, mci->mci_flags))
@@ -2969,23 +3729,41 @@ putbody(mci, e, separator)
/* now do the hard work */
boundaries[0] = NULL;
mci->mci_flags |= MCIF_INHEADER;
- mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER);
+ (void) mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER);
}
# if MIME7TO8
else if (bitset(MCIF_CVT7TO8, mci->mci_flags))
{
- mime7to8(mci, e->e_header, e);
+ (void) mime7to8(mci, e->e_header, e);
}
-# endif
+# endif /* MIME7TO8 */
else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0)
{
+ bool oldsuprerrs = SuprErrs;
+
/* Use mime8to7 to check multipart for MIME header overflows */
boundaries[0] = NULL;
mci->mci_flags |= MCIF_INHEADER;
- mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER|M87F_NO8TO7);
+
+ /*
+ ** If EF_DONT_MIME is set, we have a broken MIME message
+ ** and don't want to generate a new bounce message whose
+ ** body propagates the broken MIME. We can't just not call
+ ** mime8to7() as is done above since we need the security
+ ** checks. The best we can do is suppress the errors.
+ */
+
+ if (bitset(EF_DONT_MIME, e->e_flags))
+ SuprErrs = TRUE;
+
+ (void) mime8to7(mci, e->e_header, e, boundaries,
+ M87F_OUTER|M87F_NO8TO7);
+
+ /* restore SuprErrs */
+ SuprErrs = oldsuprerrs;
}
else
-#endif
+#endif /* MIME8TO7 */
{
int ostate;
register char *bp;
@@ -2995,8 +3773,7 @@ putbody(mci, e, separator)
int padc;
char *buflim;
int pos = 0;
- size_t eol_len;
- char peekbuf[10];
+ char peekbuf[12];
if (bitset(MCIF_INHEADER, mci->mci_flags))
{
@@ -3009,13 +3786,12 @@ putbody(mci, e, separator)
if (mci->mci_mailer->m_linelimit > 0 &&
mci->mci_mailer->m_linelimit < sizeof buf - 1)
buflim = &buf[mci->mci_mailer->m_linelimit - 1];
- eol_len = strlen(mci->mci_mailer->m_eol);
/* copy temp file to output with mapping */
ostate = OS_HEAD;
bp = buf;
pbp = peekbuf;
- while (!ferror(mci->mci_out))
+ while (!ferror(mci->mci_out) && !dead)
{
if (pbp > peekbuf)
c = *--pbp;
@@ -3030,7 +3806,7 @@ putbody(mci, e, separator)
if (c == '\0' &&
bitnset(M_NONULLS, mci->mci_mailer->m_flags))
break;
-#endif
+#endif /* _FFR_NONULLS */
if (c != '\r' && c != '\n' && bp < buflim)
{
*bp++ = c;
@@ -3068,29 +3844,43 @@ putbody(mci, e, separator)
fprintf(TrafficLogFile, "%05d >>> ",
(int) getpid());
if (padc != EOF)
- putc(padc, TrafficLogFile);
+ (void) putc(padc,
+ TrafficLogFile);
for (xp = buf; xp < bp; xp++)
- putc(*xp, TrafficLogFile);
+ (void) putc((unsigned char) *xp,
+ TrafficLogFile);
if (c == '\n')
- fputs(mci->mci_mailer->m_eol,
+ (void) fputs(mci->mci_mailer->m_eol,
TrafficLogFile);
}
if (padc != EOF)
{
- putc(padc, mci->mci_out);
- mci->mci_contentlen++;
+ if (putc(padc, mci->mci_out) == EOF)
+ {
+ dead = TRUE;
+ continue;
+ }
pos++;
}
for (xp = buf; xp < bp; xp++)
{
- putc(*xp, mci->mci_out);
- mci->mci_contentlen++;
+ if (putc((unsigned char) *xp,
+ mci->mci_out) == EOF)
+ {
+ dead = TRUE;
+ break;
+ }
+
+ /* record progress for DATA timeout */
+ DataProgress = TRUE;
}
+ if (dead)
+ continue;
if (c == '\n')
{
- fputs(mci->mci_mailer->m_eol,
- mci->mci_out);
- mci->mci_contentlen += eol_len;
+ if (fputs(mci->mci_mailer->m_eol,
+ mci->mci_out) == EOF)
+ break;
pos = 0;
}
else
@@ -3099,6 +3889,9 @@ putbody(mci, e, separator)
if (c != '\r')
*pbp++ = c;
}
+
+ /* record progress for DATA timeout */
+ DataProgress = TRUE;
bp = buf;
/* determine next state */
@@ -3114,12 +3907,17 @@ putbody(mci, e, separator)
if (c == '\n')
{
/* got CRLF */
- fputs(mci->mci_mailer->m_eol, mci->mci_out);
- mci->mci_contentlen += eol_len;
+ if (fputs(mci->mci_mailer->m_eol,
+ mci->mci_out) == EOF)
+ continue;
+
+ /* record progress for DATA timeout */
+ DataProgress = TRUE;
+
if (TrafficLogFile != NULL)
{
- fputs(mci->mci_mailer->m_eol,
- TrafficLogFile);
+ (void) fputs(mci->mci_mailer->m_eol,
+ TrafficLogFile);
}
ostate = OS_HEAD;
continue;
@@ -3141,16 +3939,46 @@ putbody(mci, e, separator)
if (c == '\0' &&
bitnset(M_NONULLS, mci->mci_mailer->m_flags))
break;
-#endif
+#endif /* _FFR_NONULLS */
putch:
if (mci->mci_mailer->m_linelimit > 0 &&
- pos > mci->mci_mailer->m_linelimit &&
+ pos >= mci->mci_mailer->m_linelimit - 1 &&
c != '\n')
{
- putc('!', mci->mci_out);
- mci->mci_contentlen++;
- fputs(mci->mci_mailer->m_eol, mci->mci_out);
- mci->mci_contentlen += eol_len;
+ int d;
+
+ /* check next character for EOL */
+ if (pbp > peekbuf)
+ d = *(pbp - 1);
+ else if ((d = getc(e->e_dfp)) != EOF)
+ *pbp++ = d;
+
+ if (d == '\n' || d == EOF)
+ {
+ if (TrafficLogFile != NULL)
+ (void) putc((unsigned char) c,
+ TrafficLogFile);
+ if (putc((unsigned char) c,
+ mci->mci_out) == EOF)
+ {
+ dead = TRUE;
+ continue;
+ }
+ pos++;
+ continue;
+ }
+
+ if (putc('!', mci->mci_out) == EOF ||
+ fputs(mci->mci_mailer->m_eol,
+ mci->mci_out) == EOF)
+ {
+ dead = TRUE;
+ continue;
+ }
+
+ /* record progress for DATA timeout */
+ DataProgress = TRUE;
+
if (TrafficLogFile != NULL)
{
fprintf(TrafficLogFile, "!%s",
@@ -3163,22 +3991,31 @@ putch:
if (c == '\n')
{
if (TrafficLogFile != NULL)
- fputs(mci->mci_mailer->m_eol,
+ (void) fputs(mci->mci_mailer->m_eol,
TrafficLogFile);
- fputs(mci->mci_mailer->m_eol, mci->mci_out);
- mci->mci_contentlen += eol_len;
+ if (fputs(mci->mci_mailer->m_eol,
+ mci->mci_out) == EOF)
+ continue;
pos = 0;
ostate = OS_HEAD;
}
else
{
if (TrafficLogFile != NULL)
- putc(c, TrafficLogFile);
- putc(c, mci->mci_out);
- mci->mci_contentlen++;
+ (void) putc((unsigned char) c,
+ TrafficLogFile);
+ if (putc((unsigned char) c,
+ mci->mci_out) == EOF)
+ {
+ dead = TRUE;
+ continue;
+ }
pos++;
ostate = OS_INLINE;
}
+
+ /* record progress for DATA timeout */
+ DataProgress = TRUE;
break;
}
}
@@ -3189,33 +4026,59 @@ putch:
if (TrafficLogFile != NULL)
{
for (xp = buf; xp < bp; xp++)
- putc(*xp, TrafficLogFile);
+ (void) putc((unsigned char) *xp,
+ TrafficLogFile);
}
for (xp = buf; xp < bp; xp++)
{
- putc(*xp, mci->mci_out);
- mci->mci_contentlen++;
+ if (putc((unsigned char) *xp, mci->mci_out) ==
+ EOF)
+ {
+ dead = TRUE;
+ break;
+ }
+
+ /* record progress for DATA timeout */
+ DataProgress = TRUE;
}
pos += bp - buf;
}
- if (pos > 0)
+ if (!dead && pos > 0)
{
if (TrafficLogFile != NULL)
- fputs(mci->mci_mailer->m_eol, TrafficLogFile);
- fputs(mci->mci_mailer->m_eol, mci->mci_out);
- mci->mci_contentlen += eol_len;
+ (void) fputs(mci->mci_mailer->m_eol,
+ TrafficLogFile);
+ (void) fputs(mci->mci_mailer->m_eol, mci->mci_out);
+
+ /* record progress for DATA timeout */
+ DataProgress = TRUE;
}
}
if (ferror(e->e_dfp))
{
- syserr("putbody: df%s: read error", e->e_id);
+ syserr("putbody: %s/df%s: read error",
+ qid_printqueue(e->e_queuedir), e->e_id);
ExitStat = EX_IOERR;
}
endofmessage:
+ /*
+ ** Since mailfile() uses e_dfp in a child process,
+ ** the file offset in the stdio library for the
+ ** parent process will not agree with the in-kernel
+ ** file offset since the file descriptor is shared
+ ** between the processes. Therefore, it is vital
+ ** that the file always be rewound. This forces the
+ ** kernel offset (lseek) and stdio library (ftell)
+ ** offset to match.
+ */
+
+ if (e->e_dfp != NULL)
+ (void) bfrewind(e->e_dfp);
+
/* some mailers want extra blank line at end of message */
- if (bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
+ if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
buf[0] != '\0' && buf[0] != '\n')
putline("", mci);
@@ -3225,6 +4088,7 @@ endofmessage:
syserr("putbody: write error");
ExitStat = EX_IOERR;
}
+
errno = 0;
}
/*
@@ -3258,26 +4122,30 @@ endofmessage:
*/
static jmp_buf CtxMailfileTimeout;
-static void mailfiletimeout __P((void));
int
mailfile(filename, mailer, ctladdr, sfflags, e)
char *volatile filename;
MAILER *volatile mailer;
ADDRESS *ctladdr;
- volatile int sfflags;
+ volatile long sfflags;
register ENVELOPE *e;
{
register FILE *f;
register pid_t pid = -1;
- volatile int mode = ST_MODE_NOFILE;
+ volatile int mode;
+ int len;
+ off_t curoff;
bool suidwarn = geteuid() == 0;
char *p;
+ char *volatile realfile;
EVENT *ev;
+ char buf[MAXLINE + 1];
+ char targetfile[MAXPATHLEN + 1];
if (tTd(11, 1))
{
- printf("mailfile %s\n ctladdr=", filename);
+ dprintf("mailfile %s\n ctladdr=", filename);
printaddr(ctladdr, FALSE);
}
@@ -3285,7 +4153,7 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
mailer = FileMailer;
if (e->e_xfp != NULL)
- fflush(e->e_xfp);
+ (void) fflush(e->e_xfp);
/*
** Special case /dev/null. This allows us to restrict file
@@ -3303,9 +4171,66 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
(bitset(EF_IS_MIME, e->e_flags) &&
bitset(MM_CVTMIME, MimeMode)))))
{
- usrerr("554 Cannot send 8-bit data to 7-bit destination");
e->e_status = "5.6.3";
- return(EX_DATAERR);
+ usrerrenh(e->e_status,
+ "554 Cannot send 8-bit data to 7-bit destination");
+ return EX_DATAERR;
+ }
+
+ /* Find the actual file */
+ if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
+ {
+ len = strlen(SafeFileEnv);
+
+ if (strncmp(SafeFileEnv, filename, len) == 0)
+ filename += len;
+
+ if (len + strlen(filename) + 1 > MAXPATHLEN)
+ {
+ syserr("mailfile: filename too long (%s/%s)",
+ SafeFileEnv, filename);
+ return EX_CANTCREAT;
+ }
+ (void) strlcpy(targetfile, SafeFileEnv, sizeof targetfile);
+ realfile = targetfile + len;
+ if (targetfile[len - 1] != '/')
+ (void) strlcat(targetfile, "/", sizeof targetfile);
+ if (*filename == '/')
+ filename++;
+ (void) strlcat(targetfile, filename, sizeof targetfile);
+ }
+ else if (mailer->m_rootdir != NULL)
+ {
+ expand(mailer->m_rootdir, targetfile, sizeof targetfile, e);
+ len = strlen(targetfile);
+
+ if (strncmp(targetfile, filename, len) == 0)
+ filename += len;
+
+ if (len + strlen(filename) + 1 > MAXPATHLEN)
+ {
+ syserr("mailfile: filename too long (%s/%s)",
+ targetfile, filename);
+ return EX_CANTCREAT;
+ }
+ realfile = targetfile + len;
+ if (targetfile[len - 1] != '/')
+ (void) strlcat(targetfile, "/", sizeof targetfile);
+ if (*filename == '/')
+ (void) strlcat(targetfile, filename + 1,
+ sizeof targetfile);
+ else
+ (void) strlcat(targetfile, filename, sizeof targetfile);
+ }
+ else
+ {
+ if (strlen(filename) > MAXPATHLEN)
+ {
+ syserr("mailfile: filename too long (%s)", filename);
+ return EX_CANTCREAT;
+ }
+ (void) strlcpy(targetfile, filename, sizeof targetfile);
+ realfile = targetfile;
}
/*
@@ -3317,7 +4242,7 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
DOFORK(fork);
if (pid < 0)
- return (EX_OSERR);
+ return EX_OSERR;
else if (pid == 0)
{
/* child -- actually write to file */
@@ -3346,36 +4271,24 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
else
ev = NULL;
-#ifdef HASLSTAT
- if (bitset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
- err = stat(filename, &stb);
- else
- err = lstat(filename, &stb);
- if (err < 0)
-#else
- if (stat(filename, &stb) < 0)
-#endif
- {
- stb.st_mode = ST_MODE_NOFILE;
+ /* check file mode to see if setuid */
+ if (stat(targetfile, &stb) < 0)
mode = FileMode;
- oflags |= O_CREAT|O_EXCL;
- }
- else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, stb.st_mode) ||
- (!bitset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail) &&
- stb.st_nlink != 1) ||
- (SafeFileEnv != NULL && !S_ISREG(stb.st_mode)))
- exit(EX_CANTCREAT);
- if (mode == ST_MODE_NOFILE)
+ else
mode = stb.st_mode;
/* limit the errors to those actually caused in the child */
errno = 0;
ExitStat = EX_OK;
- if (ctladdr != NULL || bitset(SFF_RUNASREALUID, sfflags))
+ /* Allow alias expansions to use the S_IS{U,G}ID bits */
+ if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) ||
+ bitset(SFF_RUNASREALUID, sfflags))
{
/* ignore setuid and setgid bits */
mode &= ~(S_ISGID|S_ISUID);
+ if (tTd(11, 20))
+ dprintf("mailfile: ignoring setuid/setgid bits\n");
}
/* we have to open the dfile BEFORE setuid */
@@ -3398,6 +4311,12 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
{
RealUserName = NULL;
RealUid = mailer->m_uid;
+ if (RunAsUid != 0 && RealUid != RunAsUid)
+ {
+ /* Only root can change the uid */
+ syserr("mailfile: insufficient privileges to change uid");
+ exit(EX_TEMPFAIL);
+ }
}
else if (bitset(S_ISUID, mode))
{
@@ -3425,11 +4344,25 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
/* select a new group to run as */
if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
+ {
RealGid = mailer->m_gid;
+ if (RunAsUid != 0 &&
+ (RealGid != getgid() ||
+ RealGid != getegid()))
+ {
+ /* Only root can change the gid */
+ syserr("mailfile: insufficient privileges to change gid");
+ exit(EX_TEMPFAIL);
+ }
+ }
else if (bitset(S_ISGID, mode))
RealGid = stb.st_gid;
else if (ctladdr != NULL && ctladdr->q_uid != 0)
RealGid = ctladdr->q_gid;
+ else if (ctladdr != NULL &&
+ ctladdr->q_uid == DefUid &&
+ ctladdr->q_gid == 0)
+ RealGid = DefGid;
else if (mailer != NULL && mailer->m_gid != 0)
RealGid = mailer->m_gid;
else
@@ -3449,8 +4382,11 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
if (RealUserName != NULL && !DontInitGroups)
{
if (initgroups(RealUserName, RealGid) == -1 && suidwarn)
+ {
syserr("mailfile: initgroups(%s, %d) failed",
RealUserName, RealGid);
+ exit(EX_TEMPFAIL);
+ }
}
else
{
@@ -3458,40 +4394,63 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
gidset[0] = RealGid;
if (setgroups(1, gidset) == -1 && suidwarn)
+ {
syserr("mailfile: setgroups() failed");
+ exit(EX_TEMPFAIL);
+ }
}
- /* if you have a safe environment, go into it */
- if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
- {
- int i;
+ /*
+ ** If you have a safe environment, go into it.
+ */
- if (chroot(SafeFileEnv) < 0)
+ if (realfile != targetfile)
+ {
+ *realfile = '\0';
+ if (tTd(11, 20))
+ dprintf("mailfile: chroot %s\n", targetfile);
+ if (chroot(targetfile) < 0)
{
syserr("mailfile: Cannot chroot(%s)",
- SafeFileEnv);
+ targetfile);
exit(EX_CANTCREAT);
}
- i = strlen(SafeFileEnv);
- if (strncmp(SafeFileEnv, filename, i) == 0)
- filename += i;
+ *realfile = '/';
}
+
+ if (tTd(11, 40))
+ dprintf("mailfile: deliver to %s\n", realfile);
+
if (chdir("/") < 0)
+ {
syserr("mailfile: cannot chdir(/)");
+ exit(EX_CANTCREAT);
+ }
/* now reset the group and user ids */
endpwent();
if (setgid(RealGid) < 0 && suidwarn)
+ {
syserr("mailfile: setgid(%ld) failed", (long) RealGid);
+ exit(EX_TEMPFAIL);
+ }
vendor_set_uid(RealUid);
if (setuid(RealUid) < 0 && suidwarn)
+ {
syserr("mailfile: setuid(%ld) failed", (long) RealUid);
+ exit(EX_TEMPFAIL);
+ }
+
+ if (tTd(11, 2))
+ dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n",
+ (int) getuid(), (int) geteuid(),
+ (int) getgid(), (int) getegid());
+
/* move into some "safe" directory */
if (mailer->m_execdir != NULL)
{
char *q;
- char buf[MAXLINE + 1];
for (p = mailer->m_execdir; p != NULL; p = q)
{
@@ -3502,45 +4461,86 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
if (q != NULL)
*q++ = ':';
if (tTd(11, 20))
- printf("mailfile: trydir %s\n",
- buf);
+ dprintf("mailfile: trydir %s\n", buf);
if (buf[0] != '\0' && chdir(buf) >= 0)
break;
}
}
- sfflags |= SFF_NOPATHCHECK;
- if (!bitset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
+ /*
+ ** Recheck the file after we have assumed the ID of the
+ ** delivery user to make sure we can deliver to it as
+ ** that user. This is necessary if sendmail is running
+ ** as root and the file is on an NFS mount which treats
+ ** root as nobody.
+ */
+
+#if HASLSTAT
+ if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
+ err = stat(realfile, &stb);
+ else
+ err = lstat(realfile, &stb);
+#else /* HASLSTAT */
+ err = stat(realfile, &stb);
+#endif /* HASLSTAT */
+
+ if (err < 0)
+ {
+ stb.st_mode = ST_MODE_NOFILE;
+ mode = FileMode;
+ oflags |= O_CREAT|O_EXCL;
+ }
+ else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) ||
+ (!bitnset(DBS_FILEDELIVERYTOHARDLINK,
+ DontBlameSendmail) &&
+ stb.st_nlink != 1) ||
+ (realfile != targetfile && !S_ISREG(mode)))
+ exit(EX_CANTCREAT);
+ else
+ mode = stb.st_mode;
+
+ if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
sfflags |= SFF_NOSLINK;
- if (!bitset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
+ if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
sfflags |= SFF_NOHLINK;
sfflags &= ~SFF_OPENASROOT;
- f = safefopen(filename, oflags, FileMode, sfflags);
+ f = safefopen(realfile, oflags, mode, sfflags);
if (f == NULL)
{
- message("554 cannot open %s: %s",
- shortenstring(filename, MAXSHORTSTR),
- errstring(errno));
- exit(EX_CANTCREAT);
+ if (transienterror(errno))
+ {
+ usrerr("454 4.3.0 cannot open %s: %s",
+ shortenstring(realfile, MAXSHORTSTR),
+ errstring(errno));
+ exit(EX_TEMPFAIL);
+ }
+ else
+ {
+ usrerr("554 5.3.0 cannot open %s: %s",
+ shortenstring(realfile, MAXSHORTSTR),
+ errstring(errno));
+ exit(EX_CANTCREAT);
+ }
}
- if (filechanged(filename, fileno(f), &stb))
+ if (filechanged(realfile, fileno(f), &stb))
{
- message("554 file changed after open");
+ syserr("554 5.3.0 file changed after open");
exit(EX_CANTCREAT);
}
if (fstat(fileno(f), &stb) < 0)
{
- message("554 cannot fstat %s", errstring(errno));
+ syserr("554 5.3.0 cannot fstat %s", errstring(errno));
exit(EX_CANTCREAT);
}
+ curoff = stb.st_size;
+
if (ev != NULL)
clrevent(ev);
- bzero(&mcibuf, sizeof mcibuf);
+ memset(&mcibuf, '\0', sizeof mcibuf);
mcibuf.mci_mailer = mailer;
mcibuf.mci_out = f;
- mcibuf.mci_contentlen = 0;
if (bitnset(M_7BITS, mailer->m_flags))
mcibuf.mci_flags |= MCIF_7BIT;
@@ -3563,32 +4563,37 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
/* may want to convert 7 -> 8 */
/* XXX should really parse it here -- and use a class XXX */
if (strncasecmp(p, "text/plain", 10) == 0 &&
- (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
+ (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
mcibuf.mci_flags |= MCIF_CVT7TO8;
}
-#endif
+#endif /* MIME7TO8 */
putfromline(&mcibuf, e);
(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER);
(*e->e_putbody)(&mcibuf, e, NULL);
putline("\n", &mcibuf);
- if (fflush(f) < 0 || ferror(f))
+ if (fflush(f) < 0 ||
+ (SuperSafe && fsync(fileno(f)) < 0) ||
+ ferror(f))
{
- message("451 I/O error: %s", errstring(errno));
setstat(EX_IOERR);
+#if !NOFTRUNCATE
+ (void) ftruncate(fileno(f), curoff);
+#endif /* !NOFTRUNCATE */
}
/* reset ISUID & ISGID bits for paranoid systems */
#if HASFCHMOD
- (void) fchmod(fileno(f), (MODE_T) stb.st_mode);
-#else
- (void) chmod(filename, (MODE_T) stb.st_mode);
-#endif
- (void) xfclose(f, "mailfile", filename);
+ (void) fchmod(fileno(f), (MODE_T) mode);
+#else /* HASFCHMOD */
+ (void) chmod(filename, (MODE_T) mode);
+#endif /* HASFCHMOD */
+ if (fclose(f) < 0)
+ setstat(EX_IOERR);
(void) fflush(stdout);
- setuid(RealUid);
+ (void) setuid(RealUid);
exit(ExitStat);
- /*NOTREACHED*/
+ /* NOTREACHED */
}
else
{
@@ -3599,7 +4604,7 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
if (st == -1)
{
syserr("mailfile: %s: wait", mailer->m_name);
- return (EX_SOFTWARE);
+ return EX_SOFTWARE;
}
if (WIFEXITED(st))
return (WEXITSTATUS(st));
@@ -3607,9 +4612,9 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
{
syserr("mailfile: %s: child died on signal %d",
mailer->m_name, st);
- return (EX_UNAVAILABLE);
+ return EX_UNAVAILABLE;
}
- /*NOTREACHED*/
+ /* NOTREACHED */
}
return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */
}
@@ -3629,7 +4634,6 @@ mailfiletimeout()
** Parameters:
** m -- the mailer describing this host.
** host -- the host name.
-** e -- the current envelope.
**
** Returns:
** The signature for this host.
@@ -3637,35 +4641,58 @@ mailfiletimeout()
** Side Effects:
** Can tweak the symbol table.
*/
+#define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */
-char *
-hostsignature(m, host, e)
+static char *
+hostsignature(m, host)
register MAILER *m;
char *host;
- ENVELOPE *e;
{
register char *p;
register STAB *s;
+#if NAMED_BIND
+ char sep = ':';
+ char prevsep = ':';
int i;
int len;
-#if NAMED_BIND
int nmx;
+ int hl;
char *hp;
char *endp;
int oldoptions = _res.options;
char *mxhosts[MAXMXHOSTS + 1];
-#endif
+ u_short mxprefs[MAXMXHOSTS + 1];
+#endif /* NAMED_BIND */
+
+ if (tTd(17, 3))
+ dprintf("hostsignature(%s)\n", host);
+
+ /*
+ ** If local delivery, just return a constant.
+ */
+
+ if (bitnset(M_LOCALMAILER, m->m_flags))
+ return "localhost";
/*
** Check to see if this uses IPC -- if not, it can't have MX records.
*/
p = m->m_mailer;
- if (strcmp(p, "[IPC]") != 0 && strcmp(p, "[TCP]") != 0)
+ if (strcmp(p, "[IPC]") != 0 &&
+ strcmp(p, "[TCP]") != 0)
{
/* just an ordinary mailer */
return host;
}
+#if NETUNIX
+ else if (m->m_argv[0] != NULL &&
+ strcmp(m->m_argv[0], "FILE") == 0)
+ {
+ /* rendezvous in the file system, no MX records */
+ return host;
+ }
+#endif /* NETUNIX */
/*
** Look it up in the symbol table.
@@ -3673,7 +4700,12 @@ hostsignature(m, host, e)
s = stab(host, ST_HOSTSIG, ST_ENTER);
if (s->s_hostsig != NULL)
+ {
+ if (tTd(17, 3))
+ dprintf("hostsignature(): stab(%s) found %s\n", host,
+ s->s_hostsig);
return s->s_hostsig;
+ }
/*
** Not already there -- create a signature.
@@ -3685,9 +4717,23 @@ hostsignature(m, host, e)
for (hp = host; hp != NULL; hp = endp)
{
- endp = strchr(hp, ':');
+#if NETINET6
+ if (*hp == '[')
+ {
+ endp = strchr(hp + 1, ']');
+ if (endp != NULL)
+ endp = strpbrk(endp + 1, ":,");
+ }
+ else
+ endp = strpbrk(hp, ":,");
+#else /* NETINET6 */
+ endp = strpbrk(hp, ":,");
+#endif /* NETINET6 */
if (endp != NULL)
+ {
+ sep = *endp;
*endp = '\0';
+ }
if (bitnset(M_NOMX, m->m_flags))
{
@@ -3699,7 +4745,7 @@ hostsignature(m, host, e)
{
auto int rcode;
- nmx = getmxrr(hp, mxhosts, TRUE, &rcode);
+ nmx = getmxrr(hp, mxhosts, mxprefs, TRUE, &rcode);
if (nmx <= 0)
{
register MCI *mci;
@@ -3709,50 +4755,398 @@ hostsignature(m, host, e)
mci->mci_errno = errno;
mci->mci_herrno = h_errno;
mci->mci_lastuse = curtime();
- mci_setstat(mci, rcode, NULL, NULL);
+ if (rcode == EX_NOHOST)
+ mci_setstat(mci, rcode, "5.1.2",
+ "550 Host unknown");
+ else
+ mci_setstat(mci, rcode, NULL, NULL);
/* use the original host name as signature */
nmx = 1;
mxhosts[0] = hp;
}
+ if (tTd(17, 3))
+ dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n",
+ nmx, mxhosts[0]);
}
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)
+ {
+ 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)
{
- (void) strcpy(p, s->s_hostsig);
+ (void) strlcpy(p, s->s_hostsig, len);
free(s->s_hostsig);
s->s_hostsig = p;
- p += strlen(p);
- *p++ = ':';
+ hl = strlen(p);
+ p += hl;
+ *p++ = prevsep;
+ len -= hl + 1;
}
else
s->s_hostsig = p;
for (i = 0; i < nmx; i++)
{
+ hl = strlen(mxhosts[i]);
+ if (len - 1 < hl || len <= 1)
+ {
+ /* force to drop out of outer loop */
+ len = -1;
+ break;
+ }
if (i != 0)
- *p++ = ':';
- strcpy(p, mxhosts[i]);
- p += strlen(p);
+ {
+ if (mxprefs[i] == mxprefs[i - 1])
+ *p++ = ',';
+ else
+ *p++ = ':';
+ len--;
+ }
+ (void) strlcpy(p, mxhosts[i], len);
+ p += hl;
+ len -= hl;
}
+
+ /*
+ ** break out of loop if len exceeded MAXHOSTSIGNATURE
+ ** because we won't have more space for further hosts
+ ** anyway (separated by : in the .cf file).
+ */
+
+ if (len < 0)
+ break;
if (endp != NULL)
- *endp++ = ':';
+ *endp++ = sep;
+ prevsep = sep;
}
makelower(s->s_hostsig);
if (ConfigLevel < 2)
_res.options = oldoptions;
-#else
+#else /* NAMED_BIND */
/* not using BIND -- the signature is just the host name */
s->s_hostsig = host;
-#endif
+#endif /* NAMED_BIND */
if (tTd(17, 1))
- printf("hostsignature(%s) = %s\n", host, s->s_hostsig);
+ dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig);
return s->s_hostsig;
}
+ /*
+** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array.
+**
+** The signature describes how we are going to send this -- it
+** can be just the hostname (for non-Internet hosts) or can be
+** an ordered list of MX hosts which must be randomized for equal
+** MX preference values.
+**
+** Parameters:
+** sig -- the host signature.
+** mxhosts -- array to populate.
+**
+** Returns:
+** The number of hosts inserted into mxhosts array.
+**
+** Side Effects:
+** Randomizes equal MX preference hosts in mxhosts.
+*/
+
+static int
+parse_hostsignature(sig, mxhosts, mailer)
+ char *sig;
+ char **mxhosts;
+ MAILER *mailer;
+{
+ int nmx = 0;
+ int curpref = 0;
+ int i, j;
+ char *hp, *endp;
+ u_short prefer[MAXMXHOSTS];
+ long rndm[MAXMXHOSTS];
+
+ for (hp = sig; hp != NULL; hp = endp)
+ {
+ char sep = ':';
+
+#if NETINET6
+ if (*hp == '[')
+ {
+ endp = strchr(hp + 1, ']');
+ if (endp != NULL)
+ endp = strpbrk(endp + 1, ":,");
+ }
+ else
+ endp = strpbrk(hp, ":,");
+#else /* NETINET6 */
+ endp = strpbrk(hp, ":,");
+#endif /* NETINET6 */
+ if (endp != NULL)
+ {
+ sep = *endp;
+ *endp = '\0';
+ }
+
+ mxhosts[nmx] = hp;
+ prefer[nmx] = curpref;
+ if (mci_match(hp, mailer))
+ rndm[nmx] = 0;
+ else
+ rndm[nmx] = get_random();
+
+ if (endp != NULL)
+ {
+ /*
+ ** Since we don't have the original MX prefs,
+ ** make our own. If the separator is a ':', that
+ ** means the preference for the next host will be
+ ** higher than this one, so simply increment curpref.
+ */
+
+ if (sep == ':')
+ curpref++;
+
+ *endp++ = sep;
+ }
+ if (++nmx >= MAXMXHOSTS)
+ break;
+ }
+
+ /* sort the records using the random factor for equal preferences */
+ for (i = 0; i < nmx; i++)
+ {
+ for (j = i + 1; j < nmx; j++)
+ {
+ /*
+ ** List is already sorted by MX preference, only
+ ** need to look for equal preference MX records
+ */
+
+ if (prefer[i] < prefer[j])
+ break;
+
+ if (prefer[i] > prefer[j] ||
+ (prefer[i] == prefer[j] && rndm[i] > rndm[j]))
+ {
+ register u_short tempp;
+ register long tempr;
+ register char *temp1;
+
+ tempp = prefer[i];
+ prefer[i] = prefer[j];
+ prefer[j] = tempp;
+ temp1 = mxhosts[i];
+ mxhosts[i] = mxhosts[j];
+ mxhosts[j] = temp1;
+ tempr = rndm[i];
+ rndm[i] = rndm[j];
+ rndm[j] = tempr;
+ }
+ }
+ }
+ return nmx;
+}
+
+#if SMTP
+# if STARTTLS
+static SSL_CTX *clt_ctx = NULL;
+
+ /*
+** INITCLTTLS -- initialize client side TLS
+**
+** Parameters:
+** none.
+**
+** Returns:
+** succeeded?
+*/
+
+bool
+initclttls()
+{
+ if (clt_ctx != NULL)
+ return TRUE; /* already done */
+ return inittls(&clt_ctx, TLS_I_CLT, FALSE, CltCERTfile, Cltkeyfile,
+ CACERTpath, CACERTfile, DHParams);
+}
+
+ /*
+** STARTTLS -- try to start secure connection (client side)
+**
+** Parameters:
+** m -- the mailer.
+** mci -- the mailer connection info.
+** e -- the envelope.
+**
+** Returns:
+** success?
+** (maybe this should be some other code than EX_
+** that denotes which stage failed.)
+*/
+
+static int
+starttls(m, mci, e)
+ MAILER *m;
+ MCI *mci;
+ ENVELOPE *e;
+{
+ int smtpresult;
+ int result;
+ SSL *clt_ssl = NULL;
+
+ smtpmessage("STARTTLS", m, mci);
+
+ /* get the reply */
+ smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, NULL, NULL);
+ /* which timeout? XXX */
+
+ /* check return code from server */
+ if (smtpresult == 454)
+ return EX_TEMPFAIL;
+ if (smtpresult == 501)
+ return EX_USAGE;
+ if (smtpresult == -1)
+ return smtpresult;
+ if (smtpresult != 220)
+ return EX_PROTOCOL;
+
+ if (LogLevel > 13)
+ sm_syslog(LOG_INFO, e->e_id, "TLS: start client");
+ if (clt_ctx == NULL && !initclttls())
+ return EX_SOFTWARE;
+
+ /* start connection */
+ if ((clt_ssl = SSL_new(clt_ctx)) == NULL)
+ {
+ if (LogLevel > 5)
+ {
+ sm_syslog(LOG_ERR, e->e_id,
+ "TLS: error: client: SSL_new failed");
+ if (LogLevel > 9)
+ tlslogerr();
+ }
+ return EX_SOFTWARE;
+ }
+
+ /* SSL_clear(clt_ssl); ? */
+ if ((result = SSL_set_rfd(clt_ssl, fileno(mci->mci_in))) != 1 ||
+ (result = SSL_set_wfd(clt_ssl, fileno(mci->mci_out))) != 1)
+ {
+ if (LogLevel > 5)
+ {
+ sm_syslog(LOG_ERR, e->e_id,
+ "TLS: error: SSL_set_xfd failed=%d", result);
+ if (LogLevel > 9)
+ tlslogerr();
+ }
+ return EX_SOFTWARE;
+ }
+ SSL_set_connect_state(clt_ssl);
+ if ((result = SSL_connect(clt_ssl)) <= 0)
+ {
+ int i;
+
+ /* what to do in this case? */
+ i = SSL_get_error(clt_ssl, result);
+ if (LogLevel > 5)
+ {
+ sm_syslog(LOG_ERR, e->e_id,
+ "TLS: error: SSL_connect failed=%d (%d)",
+ result, i);
+ if (LogLevel > 9)
+ tlslogerr();
+ }
+ SSL_free(clt_ssl);
+ clt_ssl = NULL;
+ return EX_SOFTWARE;
+ }
+ mci->mci_ssl = clt_ssl;
+ result = tls_get_info(clt_ssl, e, FALSE, mci->mci_host);
+
+ /* switch to use SSL... */
+#if SFIO
+ if (sfdctls(mci->mci_in, mci->mci_out, mci->mci_ssl) == 0)
+ return EX_OK;
+#else /* SFIO */
+# if _FFR_TLS_TOREK
+ if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0)
+ return EX_OK;
+# endif /* _FFR_TLS_TOREK */
+#endif /* SFIO */
+
+ /* failure */
+ SSL_free(clt_ssl);
+ clt_ssl = NULL;
+ return EX_SOFTWARE;
+}
+
+ /*
+** ENDTLSCLT -- shutdown secure connection (client side)
+**
+** Parameters:
+** mci -- the mailer connection info.
+**
+** Returns:
+** success?
+*/
+int
+endtlsclt(mci)
+ MCI *mci;
+{
+ int r;
+
+ if (!bitset(MCIF_TLSACT, mci->mci_flags))
+ return EX_OK;
+ r = endtls(mci->mci_ssl, "client");
+ mci->mci_flags &= ~MCIF_TLSACT;
+ return r;
+}
+ /*
+** ENDTLS -- shutdown secure connection
+**
+** Parameters:
+** ssl -- SSL connection information.
+** side -- srv/clt (for logging).
+**
+** Returns:
+** success?
+*/
+
+int
+endtls(ssl, side)
+ SSL *ssl;
+ char *side;
+{
+ if (ssl != NULL)
+ {
+ int r;
+
+ if ((r = SSL_shutdown(ssl)) < 0)
+ {
+ if (LogLevel > 11)
+ sm_syslog(LOG_WARNING, NOQID,
+ "SSL_shutdown %s failed: %d",
+ side, r);
+ return EX_SOFTWARE;
+ }
+ else if (r == 0)
+ {
+ if (LogLevel > 13)
+ sm_syslog(LOG_WARNING, NOQID,
+ "SSL_shutdown %s not done",
+ side);
+ return EX_SOFTWARE;
+ }
+ SSL_free(ssl);
+ ssl = NULL;
+ }
+ return EX_OK;
+}
+# endif /* STARTTLS */
+#endif /* SMTP */
diff --git a/contrib/sendmail/src/domain.c b/contrib/sendmail/src/domain.c
index ec79be8..8f5c8e2 100644
--- a/contrib/sendmail/src/domain.c
+++ b/contrib/sendmail/src/domain.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1986, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -10,21 +11,20 @@
*
*/
-#include "sendmail.h"
+#include <sendmail.h>
#ifndef lint
-#if NAMED_BIND
-static char sccsid[] = "@(#)domain.c 8.81 (Berkeley) 1/21/1999 (with name server)";
-#else
-static char sccsid[] = "@(#)domain.c 8.81 (Berkeley) 1/21/1999 (without name server)";
-#endif
-#endif /* not lint */
+# if NAMED_BIND
+static char id[] = "@(#)$Id: domain.c,v 8.114.6.1.2.3 2000/06/13 18:00:08 gshapiro Exp $ (with name server)";
+# else /* NAMED_BIND */
+static char id[] = "@(#)$Id: domain.c,v 8.114.6.1.2.3 2000/06/13 18:00:08 gshapiro Exp $ (without name server)";
+# endif /* NAMED_BIND */
+#endif /* ! lint */
+
#if NAMED_BIND
-#include <errno.h>
-#include <resolv.h>
-#include <arpa/inet.h>
+# include <arpa/inet.h>
/*
** The standard udp packet size PACKETSZ (512) is not sufficient for some
@@ -35,9 +35,9 @@ static char sccsid[] = "@(#)domain.c 8.81 (Berkeley) 1/21/1999 (without name ser
** it not big enough to accommodate the entire answer.
*/
-#ifndef MAXPACKET
-# define MAXPACKET 8192 /* max packet size used internally by BIND */
-#endif
+# ifndef MAXPACKET
+# define MAXPACKET 8192 /* max packet size used internally by BIND */
+# endif /* ! MAXPACKET */
typedef union
{
@@ -45,41 +45,51 @@ typedef union
u_char qb2[MAXPACKET];
} querybuf;
-#ifndef MXHOSTBUFSIZE
-# define MXHOSTBUFSIZE (128 * MAXMXHOSTS)
-#endif
+# ifndef MXHOSTBUFSIZE
+# define MXHOSTBUFSIZE (128 * MAXMXHOSTS)
+# endif /* ! MXHOSTBUFSIZE */
static char MXHostBuf[MXHOSTBUFSIZE];
-#ifndef MAXDNSRCH
-# define MAXDNSRCH 6 /* number of possible domains to search */
-#endif
+# ifndef MAXDNSRCH
+# define MAXDNSRCH 6 /* number of possible domains to search */
+# endif /* ! MAXDNSRCH */
+
+# ifndef RES_DNSRCH_VARIABLE
+# define RES_DNSRCH_VARIABLE _res.dnsrch
+# endif /* ! RES_DNSRCH_VARIABLE */
+
+# ifndef MAX
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
+# endif /* ! MAX */
-#ifndef MAX
-# define MAX(a, b) ((a) > (b) ? (a) : (b))
-#endif
+# ifndef NO_DATA
+# define NO_DATA NO_ADDRESS
+# endif /* ! NO_DATA */
-#ifndef NO_DATA
-# define NO_DATA NO_ADDRESS
-#endif
+# ifndef HFIXEDSZ
+# define HFIXEDSZ 12 /* sizeof(HEADER) */
+# endif /* ! HFIXEDSZ */
-#ifndef HFIXEDSZ
-# define HFIXEDSZ 12 /* sizeof(HEADER) */
-#endif
+# define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */
-#define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */
+# if defined(__RES) && (__RES >= 19940415)
+# define RES_UNC_T char *
+# else /* defined(__RES) && (__RES >= 19940415) */
+# define RES_UNC_T u_char *
+# endif /* defined(__RES) && (__RES >= 19940415) */
+
+static char *gethostalias __P((char *));
+static int mxrand __P((char *));
-#if defined(__RES) && (__RES >= 19940415)
-# define RES_UNC_T char *
-#else
-# define RES_UNC_T u_char *
-#endif
/*
** GETMXRR -- get MX resource records for a domain
**
** Parameters:
** host -- the name of the host to MX.
** 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
** than the local host (as determined by $=w) will
** be discarded.
@@ -93,9 +103,10 @@ static char MXHostBuf[MXHOSTBUFSIZE];
*/
int
-getmxrr(host, mxhosts, droplocalhost, rcode)
+getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode)
char *host;
char **mxhosts;
+ u_short *mxprefs;
bool droplocalhost;
int *rcode;
{
@@ -111,14 +122,15 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
u_short localpref = 256;
char *fallbackMX = FallBackMX;
bool trycanon = FALSE;
+ u_short *prefs;
int (*resfunc)();
- extern int res_query(), res_search();
u_short prefer[MAXMXHOSTS];
int weight[MAXMXHOSTS];
- extern int mxrand __P((char *));
+ extern int res_query(), res_search();
if (tTd(8, 2))
- printf("getmxrr(%s, droplocalhost=%d)\n", host, droplocalhost);
+ dprintf("getmxrr(%s, droplocalhost=%d)\n",
+ host, droplocalhost);
if (fallbackMX != NULL && droplocalhost &&
wordinclass(fallbackMX, 'w'))
@@ -129,6 +141,12 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
*rcode = EX_OK;
+ if (mxprefs != NULL)
+ prefs = mxprefs;
+ else
+ prefs = prefer;
+
+
/* efficiency hack -- numeric or non-MX lookups */
if (host[0] == '[')
goto punt;
@@ -153,22 +171,22 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
if (n < 0)
{
if (tTd(8, 1))
- printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n",
+ 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;
- /* fall through */
+ /* FALLTHROUGH */
case NO_RECOVERY:
/* no MX data on this host */
goto punt;
case HOST_NOT_FOUND:
-#if BROKEN_RES_SEARCH
+# if BROKEN_RES_SEARCH
case 0: /* Ultrix resolver retns failure w/ h_errno=0 */
-#endif
+# endif /* BROKEN_RES_SEARCH */
/* host doesn't exist in DNS; might be in /etc/hosts */
trycanon = TRUE;
*rcode = EX_NOHOST;
@@ -180,6 +198,10 @@ getmxrr(host, mxhosts, 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;
}
@@ -195,7 +217,7 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
}
/* irreconcilable differences */
- return (-1);
+ return -1;
}
/* avoid problems after truncation in tcp packets */
@@ -206,12 +228,16 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
hp = (HEADER *)&answer;
cp = (u_char *)&answer + HFIXEDSZ;
eom = (u_char *)&answer + n;
- for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ)
+ for (qdcount = ntohs((u_short)hp->qdcount);
+ qdcount--;
+ cp += n + QFIXEDSZ)
+ {
if ((n = dn_skipname(cp, eom)) < 0)
goto punt;
+ }
buflen = sizeof(MXHostBuf) - 1;
bp = MXHostBuf;
- ancount = ntohs(hp->ancount);
+ ancount = ntohs((u_short)hp->ancount);
while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
{
if ((n = dn_expand((u_char *)&answer,
@@ -219,13 +245,13 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
break;
cp += n;
GETSHORT(type, cp);
- cp += INT16SZ + INT32SZ;
+ cp += INT16SZ + INT32SZ;
GETSHORT(n, cp);
if (type != T_MX)
{
if (tTd(8, 8) || _res.options & RES_DEBUG)
- printf("unexpected answer type %d, size %d\n",
- type, n);
+ dprintf("unexpected answer type %d, size %d\n",
+ type, n);
cp += n;
continue;
}
@@ -237,7 +263,7 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
if (wordinclass(bp, 'w'))
{
if (tTd(8, 3))
- printf("found localhost (%s) in MX list, pref=%d\n",
+ dprintf("found localhost (%s) in MX list, pref=%d\n",
bp, pref);
if (droplocalhost)
{
@@ -250,7 +276,7 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
}
else
weight[nmx] = mxrand(bp);
- prefer[nmx] = pref;
+ prefs[nmx] = pref;
mxhosts[nmx++] = bp;
n = strlen(bp);
bp += n;
@@ -268,15 +294,15 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
{
for (j = i + 1; j < nmx; j++)
{
- if (prefer[i] > prefer[j] ||
- (prefer[i] == prefer[j] && weight[i] > weight[j]))
+ if (prefs[i] > prefs[j] ||
+ (prefs[i] == prefs[j] && weight[i] > weight[j]))
{
register int temp;
register char *temp1;
- temp = prefer[i];
- prefer[i] = prefer[j];
- prefer[j] = temp;
+ temp = prefs[i];
+ prefs[i] = prefs[j];
+ prefs[j] = temp;
temp1 = mxhosts[i];
mxhosts[i] = mxhosts[j];
mxhosts[j] = temp1;
@@ -285,7 +311,7 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
weight[j] = temp;
}
}
- if (seenlocal && prefer[i] >= localpref)
+ if (seenlocal && prefs[i] >= localpref)
{
/* truncate higher preference part of list */
nmx = i;
@@ -301,7 +327,10 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
{
/* compress out duplicate */
for (j = i + 1; j < nmx; j++)
+ {
mxhosts[j] = mxhosts[j + 1];
+ prefs[j] = prefs[j + 1];
+ }
nmx--;
}
}
@@ -309,9 +338,10 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
if (nmx == 0)
{
punt:
- if (seenlocal &&
- (!TryNullMXList || sm_gethostbyname(host) == NULL))
+ if (seenlocal)
{
+ struct hostent *h = NULL;
+
/*
** If we have deleted all MX entries, this is
** an error -- we should NEVER send to a host that
@@ -324,10 +354,45 @@ punt:
** bad idea, but it's up to you....
*/
- *rcode = EX_CONFIG;
- syserr("MX list for %s points back to %s",
- host, MyHostName);
- return -1;
+ if (TryNullMXList)
+ {
+ h_errno = 0;
+ errno = 0;
+ h = sm_gethostbyname(host, AF_INET);
+ if (h == NULL)
+ {
+ if (errno == ETIMEDOUT ||
+ h_errno == TRY_AGAIN ||
+ (errno == ECONNREFUSED &&
+ UseNameServer))
+ {
+ *rcode = EX_TEMPFAIL;
+ return -1;
+ }
+# if NETINET6
+ h_errno = 0;
+ errno = 0;
+ h = sm_gethostbyname(host, AF_INET6);
+ if (h == NULL &&
+ (errno == ETIMEDOUT ||
+ h_errno == TRY_AGAIN ||
+ (errno == ECONNREFUSED &&
+ UseNameServer)))
+ {
+ *rcode = EX_TEMPFAIL;
+ return -1;
+ }
+# endif /* NETINET6 */
+ }
+ }
+
+ if (h == NULL)
+ {
+ *rcode = EX_CONFIG;
+ syserr("MX list for %s points back to %s",
+ host, MyHostName);
+ return -1;
+ }
}
if (strlen(host) >= (SIZE_T) sizeof MXHostBuf)
{
@@ -338,20 +403,33 @@ punt:
}
snprintf(MXHostBuf, sizeof MXHostBuf, "%s", host);
mxhosts[0] = MXHostBuf;
+ prefs[0] = 0;
if (host[0] == '[')
{
register char *p;
+# if NETINET6
+ struct sockaddr_in6 tmp6;
+# endif /* NETINET6 */
/* this may be an MX suppression-style address */
p = strchr(MXHostBuf, ']');
if (p != NULL)
{
*p = '\0';
+
if (inet_addr(&MXHostBuf[1]) != INADDR_NONE)
{
nmx++;
*p = ']';
}
+# if NETINET6
+ else if (inet_pton(AF_INET6, &MXHostBuf[1],
+ &tmp6.sin6_addr) == 1)
+ {
+ nmx++;
+ *p = ']';
+ }
+# endif /* NETINET6 */
else
{
trycanon = TRUE;
@@ -374,9 +452,15 @@ 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;
+ }
- return (nmx);
+ return nmx;
}
/*
** MXRAND -- create a randomizer for equal MX preferences
@@ -396,7 +480,7 @@ punt:
** none.
*/
-int
+static int
mxrand(host)
register char *host;
{
@@ -411,7 +495,7 @@ mxrand(host)
}
if (tTd(17, 9))
- printf("mxrand(%s)", host);
+ dprintf("mxrand(%s)", host);
hfunc = seed;
while (*host != '\0')
@@ -427,7 +511,7 @@ mxrand(host)
hfunc++;
if (tTd(17, 9))
- printf(" = %d\n", hfunc);
+ dprintf(" = %d\n", hfunc);
return hfunc;
}
/*
@@ -453,7 +537,7 @@ bestmx_map_lookup(map, name, av, statp)
char buf[PSBUFSIZE / 2];
_res.options &= ~(RES_DNSRCH|RES_DEFNAMES);
- nmx = getmxrr(name, mxhosts, FALSE, statp);
+ nmx = getmxrr(name, mxhosts, NULL, FALSE, statp);
_res.options = saveopts;
if (nmx <= 0)
return NULL;
@@ -470,7 +554,7 @@ bestmx_map_lookup(map, name, av, statp)
for (i = 0; i < nmx; i++)
{
int slen;
-
+
if (strchr(mxhosts[i], map->map_coldelim) != NULL)
{
syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
@@ -485,7 +569,7 @@ bestmx_map_lookup(map, name, av, statp)
*p++ = map->map_coldelim;
len++;
}
- strcpy(p, mxhosts[i]);
+ (void) strlcpy(p, mxhosts[i], sizeof buf - len);
p += slen;
len += slen;
}
@@ -497,7 +581,7 @@ bestmx_map_lookup(map, name, av, statp)
** This algorithm tries to be smart about wildcard MX records.
** This is hard to do because DNS doesn't tell is if we matched
** against a wildcard or a specific MX.
-**
+**
** We always prefer A & CNAME records, since these are presumed
** to be specific.
**
@@ -530,7 +614,7 @@ dns_getcanonname(host, hbsize, trymx, statp)
{
register u_char *eom, *ap;
register char *cp;
- register int n;
+ register int n;
HEADER *hp;
querybuf answer;
int ancount, qdcount;
@@ -546,10 +630,9 @@ dns_getcanonname(host, hbsize, trymx, statp)
char *xp;
char nbuf[MAX(MAXPACKET, MAXDNAME*2+2)];
char *searchlist[MAXDNSRCH+2];
- extern char *gethostalias __P((char *));
if (tTd(8, 2))
- printf("dns_getcanonname(%s, trymx=%d)\n", host, trymx);
+ dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx);
if ((_res.options & RES_INIT) == 0 && res_init() == -1)
{
@@ -561,7 +644,9 @@ dns_getcanonname(host, hbsize, trymx, statp)
** Initialize domain search list. If there is at least one
** dot in the name, search the unmodified name first so we
** find "vse.CS" in Czechoslovakia instead of in the local
- ** domain (e.g., vse.CS.Berkeley.EDU).
+ ** domain (e.g., vse.CS.Berkeley.EDU). Note that there is no
+ ** longer a country named Czechoslovakia but this type of problem
+ ** is still present.
**
** Older versions of the resolver could create this
** list by tearing apart the host name.
@@ -575,7 +660,7 @@ cnameloop:
n++;
/*
- ** If this is a simple name, determine whether it matches an
+ ** 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)
@@ -586,14 +671,13 @@ cnameloop:
}
else
{
- strncpy(host, xp, hbsize);
- host[hbsize - 1] = '\0';
+ (void) strlcpy(host, xp, hbsize);
goto cnameloop;
}
}
/*
- ** Build the search list.
+ ** Build the search list.
** If there is at least one dot in name, start with a null
** domain to search the unmodified name first.
** If name does not end with a dot and search up local domain
@@ -608,7 +692,10 @@ cnameloop:
*dp++ = "";
if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options))
{
- for (domain = _res.dnsrch; *domain != NULL; )
+ /* make sure there are less than MAXDNSRCH domains */
+ for (domain = RES_DNSRCH_VARIABLE, ret = 0;
+ *domain != NULL && ret < MAXDNSRCH;
+ ret++)
*dp++ = *domain++;
}
else if (n == 0 && bitset(RES_DEFNAMES, _res.options))
@@ -634,16 +721,21 @@ cnameloop:
if (qtype == T_ANY)
gotmx = FALSE;
if (tTd(8, 5))
- printf("dns_getcanonname: trying %s.%s (%s)\n",
+ dprintf("dns_getcanonname: trying %s.%s (%s)\n",
host, *dp,
- qtype == T_ANY ? "ANY" : qtype == T_A ? "A" :
- qtype == T_MX ? "MX" : "???");
+ qtype == T_ANY ? "ANY" :
+# if NETINET6
+ qtype == T_AAAA ? "AAAA" :
+# endif /* NETINET6 */
+ qtype == T_A ? "A" :
+ qtype == T_MX ? "MX" :
+ "???");
ret = res_querydomain(host, *dp, C_IN, qtype,
answer.qb2, sizeof(answer.qb2));
if (ret <= 0)
{
if (tTd(8, 7))
- printf("\tNO: errno=%d, h_errno=%d\n",
+ dprintf("\tNO: errno=%d, h_errno=%d\n",
errno, h_errno);
if (errno == ECONNREFUSED || h_errno == TRY_AGAIN)
@@ -651,7 +743,22 @@ cnameloop:
/* the name server seems to be down */
h_errno = TRY_AGAIN;
*statp = EX_TEMPFAIL;
- return FALSE;
+
+ /*
+ ** 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 (qtype != T_ANY)
+ return FALSE;
}
if (h_errno != HOST_NOT_FOUND)
@@ -659,10 +766,22 @@ cnameloop:
/* might have another type of interest */
if (qtype == T_ANY)
{
+# if NETINET6
+ qtype = T_AAAA;
+# else /* NETINET6 */
qtype = T_A;
+# endif /* NETINET6 */
continue;
}
- else if (qtype == T_A && !gotmx && (trymx || **dp == '\0'))
+# if NETINET6
+ else if (qtype == T_AAAA)
+ {
+ qtype = T_A;
+ continue;
+ }
+# endif /* NETINET6 */
+ else if (qtype == T_A && !gotmx &&
+ (trymx || **dp == '\0'))
{
qtype = T_MX;
continue;
@@ -675,7 +794,7 @@ cnameloop:
continue;
}
else if (tTd(8, 7))
- printf("\tYES\n");
+ dprintf("\tYES\n");
/* avoid problems after truncation in tcp packets */
if (ret > sizeof(answer))
@@ -692,21 +811,24 @@ cnameloop:
eom = (u_char *) &answer + ret;
/* skip question part of response -- we know what we asked */
- for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ)
+ for (qdcount = ntohs((u_short)hp->qdcount);
+ qdcount--;
+ ap += ret + QFIXEDSZ)
{
if ((ret = dn_skipname(ap, eom)) < 0)
{
if (tTd(8, 20))
- printf("qdcount failure (%d)\n",
- ntohs(hp->qdcount));
+ dprintf("qdcount failure (%d)\n",
+ ntohs((u_short)hp->qdcount));
*statp = EX_SOFTWARE;
return FALSE; /* ???XXX??? */
}
}
amatch = FALSE;
- for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom;
- ap += n)
+ for (ancount = ntohs((u_short)hp->ancount);
+ --ancount >= 0 && ap < eom;
+ ap += n)
{
n = dn_expand((u_char *) &answer, eom, ap,
(RES_UNC_T) nbuf, sizeof nbuf);
@@ -725,7 +847,7 @@ cnameloop:
/*
** If we are using MX matches and have
** not yet gotten one, save this one
- ** but keep searching for an A or
+ ** but keep searching for an A or
** CNAME match.
*/
@@ -743,6 +865,16 @@ cnameloop:
** Such MX matches are as good as an A match,
** fall through.
*/
+ /* FALLTHROUGH */
+
+# if NETINET6
+ case T_AAAA:
+ /* Flag that a good match was found */
+ amatch = TRUE;
+
+ /* continue in case a CNAME also exists */
+ continue;
+# endif /* NETINET6 */
case T_A:
/* Flag that a good match was found */
@@ -782,8 +914,7 @@ cnameloop:
if ((ret = dn_expand((u_char *)&answer,
eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0)
break;
- (void)strncpy(host, nbuf, hbsize); /* XXX */
- host[hbsize - 1] = '\0';
+ (void)strlcpy(host, nbuf, hbsize);
/*
** RFC 1034 section 3.6 specifies that CNAME
@@ -801,7 +932,7 @@ cnameloop:
if (amatch)
{
- /*
+ /*
** Got a good match -- either an A, CNAME, or an
** exact MX record. Save it and get out of here.
*/
@@ -822,7 +953,17 @@ cnameloop:
*/
if (qtype == T_ANY)
+ {
+# if NETINET6
+ qtype = T_AAAA;
+# else /* NETINET6 */
+ qtype = T_A;
+# endif /* NETINET6 */
+ }
+# if NETINET6
+ else if (qtype == T_AAAA)
qtype = T_A;
+# endif /* NETINET6 */
else if (qtype == T_A && !gotmx && (trymx || **dp == '\0'))
qtype = T_MX;
else
@@ -848,24 +989,21 @@ cnameloop:
(void) snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host,
*mxmatch == '\0' ? "" : ".",
MAXDNAME, mxmatch);
- strncpy(host, nbuf, hbsize);
- host[hbsize - 1] = '\0';
+ (void) strlcpy(host, nbuf, hbsize);
if (tTd(8, 5))
- printf("dns_getcanonname: %s\n", host);
+ dprintf("dns_getcanonname: %s\n", host);
*statp = EX_OK;
return TRUE;
}
-
-
-char *
+static char *
gethostalias(host)
char *host;
{
char *fname;
FILE *fp;
register char *p = NULL;
- int sff = SFF_REGONLY;
+ long sff = SFF_REGONLY;
char buf[MAXLINE];
static char hbuf[MAXDNAME];
@@ -892,10 +1030,10 @@ gethostalias(host)
if (feof(fp))
{
/* no match */
- fclose(fp);
+ (void) fclose(fp);
return NULL;
}
- fclose(fp);
+ (void) fclose(fp);
/* got a match; extract the equivalent name */
while (*p != '\0' && isascii(*p) && isspace(*p))
@@ -904,9 +1042,7 @@ gethostalias(host)
while (*p != '\0' && !(isascii(*p) && isspace(*p)))
p++;
*p = '\0';
- strncpy(hbuf, host, sizeof hbuf - 1);
- hbuf[sizeof hbuf - 1] = '\0';
+ (void) 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 2cc90d6..1e8bfdc 100644
--- a/contrib/sendmail/src/envelope.c
+++ b/contrib/sendmail/src/envelope.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,10 +12,11 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)envelope.c 8.122 (Berkeley) 1/25/1999";
-#endif /* not lint */
+static char id[] = "@(#)$Id: envelope.c,v 8.180.14.3 2000/06/29 05:30:23 gshapiro Exp $";
+#endif /* ! lint */
+
+#include <sendmail.h>
-#include "sendmail.h"
/*
** NEWENVELOPE -- allocate a new envelope
@@ -41,10 +43,15 @@ newenvelope(e, parent)
parent = e->e_parent;
clearenvelope(e, TRUE);
if (e == CurEnv)
- bcopy((char *) &NullAddress, (char *) &e->e_from, sizeof e->e_from);
+ memmove((char *) &e->e_from,
+ (char *) &NullAddress,
+ sizeof e->e_from);
else
- bcopy((char *) &CurEnv->e_from, (char *) &e->e_from, sizeof e->e_from);
+ memmove((char *) &e->e_from,
+ (char *) &CurEnv->e_from,
+ sizeof e->e_from);
e->e_parent = parent;
+ assign_queueid(e);
e->e_ctime = curtime();
if (parent != NULL)
e->e_msgpriority = parent->e_msgsize;
@@ -53,7 +60,7 @@ newenvelope(e, parent)
if (CurEnv->e_xfp != NULL)
(void) fflush(CurEnv->e_xfp);
- return (e);
+ return e;
}
/*
** DROPENVELOPE -- deallocate an envelope.
@@ -80,29 +87,29 @@ dropenvelope(e, fulldrop)
bool failure_return = FALSE;
bool delay_return = FALSE;
bool success_return = FALSE;
+ bool pmnotify = bitset(EF_PM_NOTIFY, e->e_flags);
+ bool done = FALSE;
register ADDRESS *q;
char *id = e->e_id;
char buf[MAXLINE];
if (tTd(50, 1))
{
- extern void printenvflags __P((ENVELOPE *));
-
- printf("dropenvelope %lx: id=", (u_long) e);
+ dprintf("dropenvelope %lx: id=", (u_long) e);
xputs(e->e_id);
- printf(", flags=");
+ dprintf(", flags=");
printenvflags(e);
if (tTd(50, 10))
{
- printf("sendq=");
+ dprintf("sendq=");
printaddr(e->e_sendqueue, TRUE);
}
}
if (LogLevel > 84)
sm_syslog(LOG_DEBUG, id,
- "dropenvelope, e_flags=0x%x, OpMode=%c, pid=%d",
- e->e_flags, OpMode, getpid());
+ "dropenvelope, e_flags=0x%lx, OpMode=%c, pid=%d",
+ e->e_flags, OpMode, getpid());
/* we must have an id to remove disk files */
if (id == NULL)
@@ -126,41 +133,38 @@ dropenvelope(e, fulldrop)
if (curtime() > e->e_ctime + TimeOuts.to_q_return[e->e_timeoutclass])
message_timeout = TRUE;
+ if (TimeOuts.to_q_return[e->e_timeoutclass] == NOW &&
+ !bitset(EF_RESPONSE, e->e_flags))
+ {
+ message_timeout = TRUE;
+ e->e_flags |= EF_FATALERRS|EF_CLRQUEUE;
+ }
+
e->e_flags &= ~EF_QUEUERUN;
for (q = e->e_sendqueue; q != NULL; q = q->q_next)
{
- if (bitset(QQUEUEUP, q->q_flags) &&
- bitset(QDONTSEND, q->q_flags))
- {
- /* I'm not sure how this happens..... */
- if (tTd(50, 2))
- {
- printf("Bogus flags: ");
- printaddr(q, FALSE);
- }
- q->q_flags &= ~QDONTSEND;
- }
- if (!bitset(QBADADDR|QDONTSEND|QSENT, q->q_flags))
+ if (QS_IS_UNDELIVERED(q->q_state))
queueit = TRUE;
-#if XDEBUG
- else if (bitset(QQUEUEUP, q->q_flags))
- sm_syslog(LOG_DEBUG, e->e_id,
- "dropenvelope: q_flags = %x, paddr = %s",
- q->q_flags, q->q_paddr);
-#endif
/* see if a notification is needed */
if (bitset(QPINGONFAILURE, q->q_flags) &&
- ((message_timeout && bitset(QQUEUEUP, q->q_flags)) ||
- bitset(QBADADDR, q->q_flags)))
+ ((message_timeout && QS_IS_QUEUEUP(q->q_state)) ||
+ QS_IS_BADADDR(q->q_state) ||
+ (TimeOuts.to_q_return[e->e_timeoutclass] == NOW &&
+ !bitset(EF_RESPONSE, e->e_flags))))
+
{
failure_return = TRUE;
- if (q->q_owner == NULL && !emptyaddr(&e->e_from))
+ if (!done && q->q_owner == NULL &&
+ !emptyaddr(&e->e_from))
+ {
(void) sendtolist(e->e_from.q_paddr, NULLADDR,
&e->e_errorqueue, 0, e);
+ done = TRUE;
+ }
}
else if (bitset(QPINGONSUCCESS, q->q_flags) &&
- ((bitset(QSENT, q->q_flags) &&
+ ((QS_IS_SENT(q->q_state) &&
bitnset(M_LOCALMAILER, q->q_mailer->m_flags)) ||
bitset(QRELAYED|QEXPANDED|QDELIVERED, q->q_flags)))
{
@@ -176,14 +180,15 @@ dropenvelope(e, fulldrop)
*/
if (!queueit)
+ /* EMPTY */
/* nothing to do */ ;
else if (message_timeout)
{
if (failure_return)
{
(void) snprintf(buf, sizeof buf,
- "Cannot send message within %s",
- pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE));
+ "Cannot send message for %s",
+ pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE));
if (e->e_message != NULL)
free(e->e_message);
e->e_message = newstr(buf);
@@ -195,9 +200,9 @@ dropenvelope(e, fulldrop)
fprintf(e->e_xfp, "Message will be deleted from queue\n");
for (q = e->e_sendqueue; q != NULL; q = q->q_next)
{
- if (!bitset(QBADADDR|QDONTSEND|QSENT, q->q_flags))
+ if (QS_IS_UNDELIVERED(q->q_state))
{
- q->q_flags |= QBADADDR;
+ q->q_state = QS_BADADDR;
q->q_status = "4.4.7";
}
}
@@ -215,7 +220,10 @@ dropenvelope(e, fulldrop)
{
for (q = e->e_sendqueue; q != NULL; q = q->q_next)
{
- if (bitset(QQUEUEUP, q->q_flags) &&
+ if (QS_IS_QUEUEUP(q->q_state) &&
+#if _FFR_NODELAYDSN_ON_HOLD
+ !bitnset(M_HOLD, q->q_mailer->m_flags) &&
+#endif /* _FFR_NODELAYDSN_ON_HOLD */
bitset(QPINGONDELAY, q->q_flags))
{
q->q_flags |= QDELAYED;
@@ -242,7 +250,7 @@ dropenvelope(e, fulldrop)
}
if (tTd(50, 2))
- printf("failure_return=%d delay_return=%d success_return=%d queueit=%d\n",
+ dprintf("failure_return=%d delay_return=%d success_return=%d queueit=%d\n",
failure_return, delay_return, success_return, queueit);
/*
@@ -254,11 +262,12 @@ dropenvelope(e, fulldrop)
{
for (q = e->e_sendqueue; q != NULL; q = q->q_next)
{
- if (!bitset(QDONTSEND, q->q_flags) &&
+ if ((QS_IS_OK(q->q_state) ||
+ QS_IS_VERIFIED(q->q_state)) &&
bitset(QPINGONFAILURE, q->q_flags))
{
failure_return = TRUE;
- q->q_flags |= QBADADDR;
+ q->q_state = QS_BADADDR;
}
}
}
@@ -274,7 +283,8 @@ dropenvelope(e, fulldrop)
auto ADDRESS *rlist = NULL;
if (tTd(50, 8))
- printf("dropenvelope(%s): sending return receipt\n", id);
+ dprintf("dropenvelope(%s): sending return receipt\n",
+ id);
e->e_flags |= EF_SENDRECEIPT;
(void) sendtolist(e->e_from.q_paddr, NULLADDR, &rlist, 0, e);
(void) returntosender("Return receipt", rlist, RTSF_NO_BODY, e);
@@ -287,10 +297,8 @@ dropenvelope(e, fulldrop)
if ((failure_return || delay_return) && e->e_errormode != EM_QUIET)
{
- extern void savemail __P((ENVELOPE *, bool));
-
if (tTd(50, 8))
- printf("dropenvelope(%s): saving mail\n", id);
+ dprintf("dropenvelope(%s): saving mail\n", id);
savemail(e, !bitset(EF_NO_BODY_RETN, e->e_flags));
}
@@ -298,16 +306,28 @@ dropenvelope(e, fulldrop)
** Arrange to send warning messages to postmaster as requested.
*/
- if ((failure_return || bitset(EF_PM_NOTIFY, e->e_flags)) &&
+ if ((failure_return || pmnotify) &&
PostMasterCopy != NULL &&
- !bitset(EF_RESPONSE, e->e_flags) && e->e_class >= 0)
+ !bitset(EF_RESPONSE, e->e_flags) &&
+ e->e_class >= 0)
{
auto ADDRESS *rlist = NULL;
+ char pcopy[MAXNAME];
- if (tTd(50, 8))
- printf("dropenvelope(%s): sending postmaster copy\n", id);
- (void) sendtolist(PostMasterCopy, NULLADDR, &rlist, 0, e);
- (void) returntosender(e->e_message, rlist, RTSF_PM_BOUNCE, e);
+ if (failure_return)
+ {
+ expand(PostMasterCopy, pcopy, sizeof pcopy, e);
+
+ if (tTd(50, 8))
+ dprintf("dropenvelope(%s): sending postmaster copy to %s\n",
+ id, pcopy);
+ (void) sendtolist(pcopy, NULLADDR, &rlist, 0, e);
+ }
+ if (pmnotify)
+ (void) sendtolist("postmaster", NULLADDR,
+ &rlist, 0, e);
+ (void) returntosender(e->e_message, rlist,
+ RTSF_PM_BOUNCE|RTSF_NO_BODY, e);
}
/*
@@ -316,42 +336,42 @@ dropenvelope(e, fulldrop)
simpledrop:
if (tTd(50, 8))
- printf("dropenvelope(%s): at simpledrop, queueit=%d\n",
+ dprintf("dropenvelope(%s): at simpledrop, queueit=%d\n",
id, queueit);
if (!queueit || bitset(EF_CLRQUEUE, e->e_flags))
{
if (tTd(50, 1))
{
- extern void printenvflags __P((ENVELOPE *));
-
- printf("\n===== Dropping [dq]f%s... queueit=%d, e_flags=",
+ dprintf("\n===== Dropping [dq]f%s... queueit=%d, e_flags=",
e->e_id, queueit);
printenvflags(e);
}
xunlink(queuename(e, 'd'));
xunlink(queuename(e, 'q'));
- if (LogLevel > 10)
- sm_syslog(LOG_INFO, id, "done");
+ if (e->e_ntries > 0 && LogLevel > 9)
+ sm_syslog(LOG_INFO, id, "done; delay=%s, ntries=%d",
+ 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 dropenvelope: queueup");
+ syserr("554 5.3.0 dropenvelope: queueup");
#endif /* QUEUE */
}
/* now unlock the job */
if (tTd(50, 8))
- printf("dropenvelope(%s): unlocking job\n", id);
+ 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) xfclose(e->e_dfp, "dropenvelope df", e->e_id);
+ (void) bfclose(e->e_dfp);
e->e_dfp = NULL;
e->e_id = NULL;
e->e_flags &= ~EF_HAS_DF;
@@ -389,9 +409,9 @@ clearenvelope(e, fullclear)
{
/* clear out any file information */
if (e->e_xfp != NULL)
- (void) xfclose(e->e_xfp, "clearenvelope xfp", e->e_id);
+ (void) bfclose(e->e_xfp);
if (e->e_dfp != NULL)
- (void) xfclose(e->e_dfp, "clearenvelope dfp", e->e_id);
+ (void) bfclose(e->e_dfp);
e->e_xfp = e->e_dfp = NULL;
}
@@ -399,13 +419,13 @@ clearenvelope(e, fullclear)
STRUCTCOPY(BlankEnvelope, *e);
e->e_message = NULL;
if (Verbose)
- e->e_sendmode = SM_DELIVER;
+ set_delivery_mode(SM_DELIVER, e);
bh = BlankEnvelope.e_header;
nhp = &e->e_header;
while (bh != NULL)
{
*nhp = (HDR *) xalloc(sizeof *bh);
- bcopy((char *) bh, (char *) *nhp, sizeof *bh);
+ memmove((char *) *nhp, (char *) bh, sizeof *bh);
bh = bh->h_link;
nhp = &(*nhp)->h_link;
}
@@ -416,7 +436,7 @@ clearenvelope(e, fullclear)
** In Daemon mode, this is done in the child.
**
** Parameters:
-** none.
+** e -- the envelope to use.
**
** Returns:
** none.
@@ -438,15 +458,19 @@ initsys(e)
register char *p;
extern char *ttyname();
#endif /* TTYNAME */
- extern void settime __P((ENVELOPE *));
/*
** Give this envelope a reality.
** I.e., an id, a transcript, and a creation time.
*/
+ setnewqueue(e);
openxscript(e);
e->e_ctime = curtime();
+#if _FFR_QUEUEDELAY
+ e->e_queuealg = QueueAlg;
+ e->e_queuedelay = QueueInitDelay;
+#endif /* _FFR_QUEUEDELAY */
/*
** Set OutChannel to something useful if stdout isn't it.
@@ -464,7 +488,7 @@ initsys(e)
*/
/* process id */
- (void) snprintf(pbuf, sizeof pbuf, "%d", getpid());
+ (void) snprintf(pbuf, sizeof pbuf, "%d", (int) getpid());
define('p', newstr(pbuf), e);
/* hop count */
@@ -474,6 +498,9 @@ initsys(e)
/* time as integer, unix time, arpa time */
settime(e);
+ /* Load average */
+ (void)sm_getla(e);
+
#ifdef TTYNAME
/* tty name */
if (macvalue('y', e) == NULL)
@@ -493,7 +520,7 @@ initsys(e)
** SETTIME -- set the current time.
**
** Parameters:
-** none.
+** e -- the envelope in which the macros should be set.
**
** Returns:
** none.
@@ -511,14 +538,13 @@ settime(e)
char tbuf[20]; /* holds "current" time */
char dbuf[30]; /* holds ctime(tbuf) */
register struct tm *tm;
- extern struct tm *gmtime();
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);
+ tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min);
define('t', newstr(tbuf), e);
- (void) strcpy(dbuf, ctime(&now));
+ (void) strlcpy(dbuf, ctime(&now), sizeof dbuf);
p = strchr(dbuf, '\n');
if (p != NULL)
*p = '\0';
@@ -546,38 +572,42 @@ settime(e)
*/
#ifndef O_APPEND
-#define O_APPEND 0
-#endif
+# define O_APPEND 0
+#endif /* ! O_APPEND */
void
openxscript(e)
register ENVELOPE *e;
{
register char *p;
- int fd;
if (e->e_xfp != NULL)
return;
+
+#if 0
+ if (e->e_lockfp == NULL && bitset(EF_INQUEUE, e->e_flags))
+ syserr("openxscript: job not locked");
+#endif /* 0 */
+
p = queuename(e, 'x');
- fd = open(p, O_WRONLY|O_CREAT|O_APPEND, FileMode);
- if (fd < 0)
+ e->e_xfp = bfopen(p, FileMode, XscriptFileBufferSize,
+ SFF_NOTEXCL|SFF_OPENASROOT);
+
+ if (e->e_xfp == NULL)
{
syserr("Can't create transcript file %s", p);
- fd = open("/dev/null", O_WRONLY, 0644);
- if (fd < 0)
+ e->e_xfp = fopen("/dev/null", "r+");
+ if (e->e_xfp == NULL)
syserr("!Can't open /dev/null");
}
- e->e_xfp = fdopen(fd, "a");
- if (e->e_xfp == NULL)
- syserr("!Can't create transcript stream %s", p);
-#ifdef HASSETVBUF
- setvbuf(e->e_xfp, NULL, _IOLBF, 0);
-#else
- setlinebuf(e->e_xfp);
-#endif
+#if HASSETVBUF
+ (void) setvbuf(e->e_xfp, NULL, _IOLBF, 0);
+#else /* HASSETVBUF */
+ (void) setlinebuf(e->e_xfp);
+#endif /* HASSETVBUF */
if (tTd(46, 9))
{
- printf("openxscript(%s):\n ", p);
+ dprintf("openxscript(%s):\n ", p);
dumpfd(fileno(e->e_xfp), TRUE, FALSE);
}
}
@@ -600,7 +630,11 @@ closexscript(e)
{
if (e->e_xfp == NULL)
return;
- (void) xfclose(e->e_xfp, "closexscript", e->e_id);
+#if 0
+ if (e->e_lockfp == NULL)
+ syserr("closexscript: job not locked");
+#endif /* 0 */
+ (void) bfclose(e->e_xfp);
e->e_xfp = NULL;
}
/*
@@ -659,7 +693,7 @@ setsender(from, e, delimptr, delimchar, internal)
extern char *FullName;
if (tTd(45, 1))
- printf("setsender(%s)\n", from == NULL ? "" : from);
+ dprintf("setsender(%s)\n", from == NULL ? "" : from);
/*
** Figure out the real user executing us.
@@ -675,11 +709,16 @@ setsender(from, e, delimptr, delimchar, internal)
if (ConfigLevel < 2)
SuprErrs = TRUE;
- e->e_from.q_flags = QBADADDR;
+#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 ||
- bitset(QBADADDR, e->e_from.q_flags) ||
+ QS_IS_BADADDR(e->e_from.q_state) ||
e->e_from.q_mailer == ProgMailer ||
e->e_from.q_mailer == FileMailer ||
e->e_from.q_mailer == InclMailer)
@@ -703,16 +742,17 @@ setsender(from, e, delimptr, delimchar, internal)
p = ebuf;
}
sm_syslog(LOG_NOTICE, e->e_id,
- "setsender: %s: invalid or unparseable, received from %s",
- shortenstring(from, 83), p);
+ "setsender: %s: invalid or unparsable, received from %s",
+ shortenstring(from, 83), p);
}
if (from != NULL)
{
- if (!bitset(QBADADDR, e->e_from.q_flags))
+ if (!QS_IS_BADADDR(e->e_from.q_state))
{
/* it was a bogus mailer in the from addr */
e->e_status = "5.1.7";
- usrerr("553 Invalid sender address");
+ usrerrenh(e->e_status,
+ "553 Invalid sender address");
}
SuprErrs = TRUE;
}
@@ -727,31 +767,30 @@ setsender(from, e, delimptr, delimchar, internal)
if (parseaddr(from = newstr(nbuf), &e->e_from,
RF_COPYALL, ' ', NULL, e) == NULL &&
parseaddr(from = "postmaster", &e->e_from,
- RF_COPYALL, ' ', NULL, e) == NULL)
- syserr("553 setsender: can't even parse postmaster!");
+ RF_COPYALL, ' ', NULL, e) == NULL)
+ syserr("553 5.3.0 setsender: can't even parse postmaster!");
}
}
else
FromFlag = TRUE;
- e->e_from.q_flags |= QDONTSEND;
+ e->e_from.q_state = QS_SENDER;
if (tTd(45, 5))
{
- printf("setsender: QDONTSEND ");
+ dprintf("setsender: QS_SENDER ");
printaddr(&e->e_from, FALSE);
}
SuprErrs = FALSE;
-# if USERDB
+#if USERDB
if (bitnset(M_CHECKUDB, e->e_from.q_mailer->m_flags))
{
register char *p;
- extern char *udbsender __P((char *));
p = udbsender(e->e_from.q_user);
if (p != NULL)
from = p;
}
-# endif /* USERDB */
+#endif /* USERDB */
if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
{
@@ -795,7 +834,7 @@ setsender(from, e, delimptr, delimchar, internal)
}
else
{
- e->e_from.q_home = "/no/such/directory";
+ e->e_from.q_home = NULL;
}
if (FullName != NULL && !internal)
define('x', FullName, e);
@@ -825,19 +864,22 @@ setsender(from, e, delimptr, delimchar, internal)
/* don't need to give error -- prescan did that already */
if (LogLevel > 2)
sm_syslog(LOG_NOTICE, e->e_id,
- "cannot prescan from (%s)",
- shortenstring(from, MAXSHORTSTR));
+ "cannot prescan from (%s)",
+ shortenstring(from, MAXSHORTSTR));
finis(TRUE, ExitStat);
}
(void) rewrite(pvp, 3, 0, e);
(void) rewrite(pvp, 1, 0, e);
(void) rewrite(pvp, 4, 0, e);
+#if _FFR_ADDR_TYPE
+ define(macid("{addr_type}", NULL), NULL, e);
+#endif /* _FFR_ADDR_TYPE */
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 */
- strcat(bp, ">");
+ (void) strlcat(bp, ">", sizeof buf - 1);
*--bp = '<';
}
e->e_sender = newstr(bp);
@@ -848,12 +890,17 @@ setsender(from, e, delimptr, delimchar, internal)
bitnset(M_CANONICAL, e->e_from.q_mailer->m_flags))
{
char **lastat;
- extern char **copyplist __P((char **, bool));
/* 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 */
/* strip off to the last "@" sign */
for (lastat = NULL; *pvp != NULL; pvp++)
@@ -864,7 +911,7 @@ setsender(from, e, delimptr, delimchar, internal)
e->e_fromdomain = copyplist(lastat, TRUE);
if (tTd(45, 3))
{
- printf("Saving from domain: ");
+ dprintf("Saving from domain: ");
printav(e->e_fromdomain);
}
}
@@ -886,7 +933,7 @@ struct eflags
u_long ef_bit;
};
-struct eflags EnvelopeFlags[] =
+static struct eflags EnvelopeFlags[] =
{
{ "OLDSTYLE", EF_OLDSTYLE },
{ "INQUEUE", EF_INQUEUE },
diff --git a/contrib/sendmail/src/helpfile b/contrib/sendmail/src/helpfile
new file mode 100644
index 0000000..2563d2e
--- /dev/null
+++ b/contrib/sendmail/src/helpfile
@@ -0,0 +1,136 @@
+#vers 2
+cpyr
+cpyr Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+cpyr All rights reserved.
+cpyr Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+cpyr Copyright (c) 1988, 1993
+cpyr The Regents of the University of California. All rights reserved.
+cpyr
+cpyr
+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.3 2000/07/19 18:54:55 gshapiro Exp $$
+cpyr
+smtp This is sendmail version $v
+smtp Topics:
+smtp HELO EHLO MAIL RCPT DATA
+smtp RSET NOOP QUIT HELP VRFY
+smtp EXPN VERB ETRN DSN AUTH
+smtp STARTTLS
+smtp For more info use "HELP <topic>".
+smtp To report bugs in the implementation send email to
+smtp sendmail-bugs@sendmail.org.
+smtp For local information send email to Postmaster at your site.
+help HELP [ <topic> ]
+help The HELP command gives help info.
+helo HELO <hostname>
+helo Introduce yourself.
+ehlo EHLO <hostname>
+ehlo Introduce yourself, and request extended SMTP mode.
+ehlo Possible replies include:
+ehlo SEND Send as mail [RFC821]
+ehlo SOML Send as mail or terminal [RFC821]
+ehlo SAML Send as mail and terminal [RFC821]
+ehlo EXPN Expand the mailing list [RFC821]
+ehlo HELP Supply helpful information [RFC821]
+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]
+ehlo DSN Delivery Status Notification [RFC1891]
+ehlo ETRN Remote Message Queue Starting [RFC1985]
+ehlo STARTTLS Secure SMTP [RFC2487]
+ehlo AUTH Authentication [RFC2554]
+ehlo XUSR Initial (user) submission [Allman]
+ehlo ENHANCEDSTATUSCODES Enhanced status codes [RFC2034]
+mail MAIL FROM: <sender> [ <parameters> ]
+mail Specifies the sender. Parameters are ESMTP extensions.
+mail See "HELP DSN" for details.
+rcpt RCPT TO: <recipient> [ <parameters> ]
+rcpt Specifies the recipient. Can be used any number of times.
+rcpt Parameters are ESMTP extensions. See "HELP DSN" for details.
+data DATA
+data Following text is collected as the message.
+data End with a single dot.
+rset RSET
+rset Resets the system.
+quit QUIT
+quit Exit sendmail (SMTP).
+auth AUTH mechanism [initial-response]
+auth Start authentication.
+starttls STARTTLS
+starttls Start TLS negotiation.
+verb VERB
+verb Go into verbose mode. This sends 0xy responses that are
+verb not RFC821 standard (but should be) They are recognized
+verb by humans and other sendmail implementations.
+vrfy VRFY <recipient>
+vrfy Verify an address. If you want to see what it aliases
+vrfy to, use EXPN instead.
+expn EXPN <recipient>
+expn Expand an address. If the address indicates a mailing
+expn list, return the contents of that list.
+noop NOOP
+noop Do nothing.
+send SEND FROM: <sender>
+send replaces the MAIL command, and can be used to send
+send directly to a users terminal. Not supported in this
+send implementation.
+soml SOML FROM: <sender>
+soml Send or mail. If the user is logged in, send directly,
+soml otherwise mail. Not supported in this implementation.
+saml SAML FROM: <sender>
+saml Send and mail. Send directly to the user's terminal,
+saml and also mail a letter. Not supported in this
+saml implementation.
+turn TURN
+turn Reverses the direction of the connection. Not currently
+turn implemented.
+etrn ETRN [ <hostname> | @<domain> | #<queuename> ]
+etrn Run the queue for the specified <hostname>, or
+etrn all hosts within a given <domain>, or a specially-named
+etrn <queuename> (implementation-specific).
+dsn MAIL FROM: <sender> [ RET={ FULL | HDRS} ] [ ENVID=<envid> ]
+dsn RCPT TO: <recipient> [ NOTIFY={NEVER,SUCCESS,FAILURE,DELAY} ]
+dsn [ ORCPT=<recipient> ]
+dsn SMTP Delivery Status Notifications.
+dsn Descriptions:
+dsn RET Return either the full message or only headers.
+dsn ENVID Sender's "envelope identifier" for tracking.
+dsn NOTIFY When to send a DSN. Multiple options are OK, comma-
+dsn delimited. NEVER must appear by itself.
+dsn ORCPT Original recipient.
+-bt Help for test mode:
+-bt ? :this help message.
+-bt .Dmvalue :define macro `m' to `value'.
+-bt .Ccvalue :add `value' to class `c'.
+-bt =Sruleset :dump the contents of the indicated ruleset.
+-bt =M :display the known mailers.
+-bt -ddebug-spec :equivalent to the command-line -d debug flag.
+-bt $$m :print the value of macro $$m.
+-bt $$=c :print the contents of class $$=c.
+-bt /mx host :returns the MX records for `host'.
+-bt /parse address :parse address, returning the value of crackaddr, and
+-bt the parsed address.
+-bt /try mailer addr :rewrite address into the form it will have when
+-bt presented to the indicated mailer.
+-bt /tryflags flags :set flags used by parsing. The flags can be `H' for
+-bt Header or `E' for Envelope, and `S' for Sender or `R'
+-bt for Recipient. These can be combined, `HR' sets
+-bt flags for header recipients.
+-bt /canon hostname :try to canonify hostname.
+-bt /map mapname key :look up `key' in the indicated `mapname'.
+-bt /quit :quit address test mode.
+-bt rules addr :run the indicated address through the named rules.
+-bt Rules can be a comma separated list of rules.
+control Help for smcontrol:
+control help This message.
+control restart Restart sendmail.
+control shutdown Shutdown sendmail.
+control status Show sendmail status.
diff --git a/contrib/sendmail/src/macro.c b/contrib/sendmail/src/macro.c
index d45a0c7..f0e1db2 100644
--- a/contrib/sendmail/src/macro.c
+++ b/contrib/sendmail/src/macro.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,10 +12,10 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)macro.c 8.26 (Berkeley) 11/8/1998";
-#endif /* not lint */
+static char id[] = "@(#)$Id: macro.c,v 8.40.16.1 2000/05/25 18:56:15 gshapiro Exp $";
+#endif /* ! lint */
-# include "sendmail.h"
+#include <sendmail.h>
char *MacroName[256]; /* macro id to name table */
int NextMacroId = 0240; /* codes for long named macros */
@@ -55,9 +56,9 @@ expand(s, buf, bufsize, e)
if (tTd(35, 24))
{
- printf("expand(");
+ dprintf("expand(");
xputs(s);
- printf(")\n");
+ dprintf(")\n");
}
skipping = FALSE;
@@ -84,7 +85,12 @@ expand(s, buf, bufsize, e)
if (skipping)
skiplev++;
else
- skipping = macvalue(c, e) == NULL;
+ {
+ char *mv;
+
+ mv = macvalue(c, e);
+ skipping = (mv == NULL || *mv == '\0');
+ }
continue;
case CONDELSE: /* change state of skipping */
@@ -142,9 +148,9 @@ expand(s, buf, bufsize, e)
if (tTd(35, 24))
{
- printf("expand ==> ");
+ dprintf("expand ==> ");
xputs(xbuf);
- printf("\n");
+ dprintf("\n");
}
/* recurse as appropriate */
@@ -163,9 +169,9 @@ expand(s, buf, bufsize, e)
/* copy results out */
i = xp - xbuf;
- if (i >= bufsize)
+ if ((size_t)i >= bufsize)
i = bufsize - 1;
- bcopy(xbuf, buf, i);
+ memmove(buf, xbuf, i);
buf[i] = '\0';
}
/*
@@ -239,14 +245,27 @@ define(n, v, e)
char *v;
register ENVELOPE *e;
{
+ int m;
+
+ m = n & 0377;
if (tTd(35, 9))
{
- printf("%sdefine(%s as ",
- (e->e_macro[n & 0377] == NULL) ? "" : "re", macname(n));
+ dprintf("%sdefine(%s as ",
+ (e->e_macro[m] == NULL) ? ""
+ : "re", macname(n));
xputs(v);
- printf(")\n");
+ dprintf(")\n");
}
- e->e_macro[n & 0377] = v;
+ e->e_macro[m] = v;
+
+#if _FFR_RESET_MACRO_GLOBALS
+ switch (m)
+ {
+ case 'j':
+ MyHostName = v;
+ break;
+ }
+#endif /* _FFR_RESET_MACRO_GLOBALS */
}
/*
** MACVALUE -- return uninterpreted value of a macro.
@@ -272,10 +291,10 @@ macvalue(n, e)
register char *p = e->e_macro[n];
if (p != NULL)
- return (p);
+ return p;
e = e->e_parent;
}
- return (NULL);
+ return NULL;
}
/*
** MACNAME -- return the name of a macro given its internal id
@@ -337,9 +356,9 @@ macid(p, ep)
if (tTd(35, 14))
{
- printf("macid(");
+ dprintf("macid(");
xputs(p);
- printf(") => ");
+ dprintf(") => ");
}
if (*p == '\0' || (p[0] == '{' && p[1] == '}'))
@@ -348,7 +367,7 @@ macid(p, ep)
if (ep != NULL)
*ep = p;
if (tTd(35, 14))
- printf("NULL\n");
+ dprintf("NULL\n");
return '\0';
}
if (*p != '{')
@@ -357,7 +376,7 @@ macid(p, ep)
if (ep != NULL)
*ep = p + 1;
if (tTd(35, 14))
- printf("%c\n", *p);
+ dprintf("%c\n", *p);
return *p;
}
bp = mbuf;
@@ -394,7 +413,7 @@ macid(p, ep)
mid = s->s_macro;
else
{
- if (NextMacroId > 0377)
+ if (NextMacroId > MAXMACROID)
{
syserr("Macro/class {%s}: too many long names", mbuf);
s->s_macro = -1;
@@ -410,7 +429,7 @@ macid(p, ep)
if (ep != NULL)
*ep = p;
if (tTd(35, 14))
- printf("0x%x\n", mid);
+ dprintf("0x%x\n", mid);
return mid;
}
/*
diff --git a/contrib/sendmail/src/main.c b/contrib/sendmail/src/main.c
index cb6fd57..7b66e0c 100644
--- a/contrib/sendmail/src/main.c
+++ b/contrib/sendmail/src/main.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -12,24 +13,29 @@
#ifndef lint
static char copyright[] =
-"@(#) Copyright (c) 1998 Sendmail, Inc. All rights reserved.\n\
+"@(#) Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.\n\
+ All rights reserved.\n\
Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.\n\
Copyright (c) 1988, 1993\n\
The Regents of the University of California. All rights reserved.\n";
-#endif /* not lint */
+#endif /* ! lint */
#ifndef lint
-static char sccsid[] = "@(#)main.c 8.322 (Berkeley) 12/18/1998";
-#endif /* not lint */
+static char id[] = "@(#)$Id: main.c,v 8.485.4.19 2000/06/29 01:31:02 gshapiro Exp $";
+#endif /* ! lint */
#define _DEFINE
-#include "sendmail.h"
-#include <arpa/inet.h>
-#include <grp.h>
-#if NAMED_BIND
-#include <resolv.h>
-#endif
+#include <sendmail.h>
+
+
+#if NETINET || NETINET6
+# include <arpa/inet.h>
+#endif /* NETINET || NETINET6 */
+
+static void dump_class __P((STAB *, int));
+static void obsolete __P((char **));
+static void testmodeline __P((char *, ENVELOPE *));
/*
** SENDMAIL -- Post mail to a set of destinations.
@@ -39,7 +45,7 @@ static char sccsid[] = "@(#)main.c 8.322 (Berkeley) 12/18/1998";
** turn calls a bunch of mail servers that do the real work of
** delivering the mail.
**
-** Sendmail is driven by settings read in from /etc/sendmail.cf
+** Sendmail is driven by settings read in from /etc/mail/sendmail.cf
** (read by readcf.c).
**
** Usage:
@@ -65,30 +71,37 @@ static char sccsid[] = "@(#)main.c 8.322 (Berkeley) 12/18/1998";
int NextMailer; /* "free" index into Mailer struct */
char *FullName; /* sender's full name */
ENVELOPE BlankEnvelope; /* a "blank" envelope */
-ENVELOPE MainEnvelope; /* the envelope around the basic letter */
+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 */
char **SaveArgv; /* argument vector for re-execing */
-int MissingFds = 0; /* bit map of fds missing on startup */
+static int MissingFds = 0; /* bit map of fds missing on startup */
#ifdef NGROUPS_MAX
GIDSET_T InitialGidSet[NGROUPS_MAX];
-#endif
-
-static void obsolete __P((char **));
-extern void printmailer __P((MAILER *));
-extern void tTflag __P((char *));
+#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 /* DAEMON && !SMTP */
+#endif /* SMTP && !QUEUE */
-#define MAXCONFIGLEVEL 8 /* highest config version level known */
+#define MAXCONFIGLEVEL 9 /* highest config version level known */
+
+#if SASL
+static sasl_callback_t srvcallbacks[] =
+{
+ { SASL_CB_VERIFYFILE, &safesaslfile, NULL },
+ { SASL_CB_PROXY_POLICY, &proxy_policy, NULL },
+ { SASL_CB_LIST_END, NULL, NULL }
+};
+#endif /* SASL */
+
+int SubmitMode;
int
main(argc, argv, envp)
@@ -103,16 +116,21 @@ main(argc, argv, envp)
STAB *st;
register int i;
int j;
- bool queuemode = FALSE; /* process queue requests */
+ int dp;
bool safecf = TRUE;
+ BITMAP256 *p_flags = NULL; /* daemon flags */
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;
struct passwd *pw;
struct hostent *hp;
char *nullserver = NULL;
+ char *authinfo = NULL;
+ char *sysloglabel = NULL; /* label for syslog */
bool forged;
+ struct stat traf_st; /* for TrafficLog FIFO check */
char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */
static char rnamebuf[MAXNAME]; /* holds RealUserName */
char *emptyenviron[1];
@@ -122,31 +140,6 @@ main(argc, argv, envp)
extern int opterr;
extern char *optarg;
extern char **environ;
- extern time_t convtime __P((char *, char));
- extern SIGFUNC_DECL intsig __P((int));
- extern struct hostent *myhostname __P((char *, int));
- extern char *getauthinfo __P((int, bool *));
- extern char *getcfname __P((void));
- extern SIGFUNC_DECL sigusr1 __P((int));
- extern SIGFUNC_DECL sighup __P((int));
- extern SIGFUNC_DECL quiesce __P((int));
- extern void initmacros __P((ENVELOPE *));
- extern void init_md __P((int, char **));
- extern int getdtsize __P((void));
- extern void tTsetup __P((u_char *, int, char *));
- extern void setdefaults __P((ENVELOPE *));
- extern void initsetproctitle __P((int, char **, char **));
- extern void init_vendor_macros __P((ENVELOPE *));
- extern void load_if_names __P((void));
- extern void vendor_pre_defaults __P((ENVELOPE *));
- extern void vendor_post_defaults __P((ENVELOPE *));
- extern void readcf __P((char *, bool, ENVELOPE *));
- extern void printqueue __P((void));
- extern void sendtoargv __P((char **, ENVELOPE *));
- extern void resetlimits __P((void));
-#ifndef HASUNSETENV
- extern void unsetenv __P((char *));
-#endif
/*
** Check to see if we reentered.
@@ -167,6 +160,7 @@ main(argc, argv, envp)
/* do machine-dependent initializations */
init_md(argc, argv);
+
/* in 4.4BSD, the table can be huge; impose a reasonable limit */
DtableSize = getdtsize();
if (DtableSize > 256)
@@ -190,12 +184,12 @@ main(argc, argv, envp)
errno = 0;
#if LOG
-# ifdef LOG_MAIL
+# ifdef LOG_MAIL
openlog("sendmail", LOG_PID, LOG_MAIL);
-# else
+# else /* LOG_MAIL */
openlog("sendmail", LOG_PID);
-# endif
-#endif
+# endif /* LOG_MAIL */
+#endif /* LOG */
if (MissingFds != 0)
{
@@ -203,11 +197,11 @@ main(argc, argv, envp)
mbuf[0] = '\0';
if (bitset(1 << STDIN_FILENO, MissingFds))
- strcat(mbuf, ", stdin");
+ (void) strlcat(mbuf, ", stdin", sizeof mbuf);
if (bitset(1 << STDOUT_FILENO, MissingFds))
- strcat(mbuf, ", stdout");
+ (void) strlcat(mbuf, ", stdout", sizeof mbuf);
if (bitset(1 << STDERR_FILENO, MissingFds))
- strcat(mbuf, ", stderr");
+ (void) strlcat(mbuf, ", stderr", sizeof mbuf);
syserr("File descriptors missing on startup: %s", &mbuf[2]);
}
@@ -215,9 +209,18 @@ main(argc, argv, envp)
Errors = 0;
ExitStat = EX_OK;
+ SubmitMode = SUBMIT_UNKNOWN;
#if XDEBUG
checkfd012("after openlog");
-#endif
+#endif /* XDEBUG */
+
+ /*
+ ** Seed the random number generator.
+ ** Used for queue file names, picking a queue directory, and
+ ** MX randomization.
+ */
+
+ seed_random();
tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
@@ -228,15 +231,16 @@ main(argc, argv, envp)
InitialGidSet[0] = (GID_T) -1;
while (i < NGROUPS_MAX)
InitialGidSet[i++] = InitialGidSet[0];
-#endif
+#endif /* NGROUPS_MAX */
/* drop group id privileges (RunAsUser not yet set) */
- (void) drop_privileges(FALSE);
+ dp = drop_privileges(FALSE);
+ setstat(dp);
-#ifdef SIGUSR1
+# ifdef SIGUSR1
/* arrange to dump state on user-1 signal */
- setsignal(SIGUSR1, sigusr1);
-#endif
+ (void) setsignal(SIGUSR1, sigusr1);
+# endif /* SIGUSR1 */
/* initialize for setproctitle */
initsetproctitle(argc, argv, envp);
@@ -248,15 +252,16 @@ main(argc, argv, envp)
** Do a quick prescan of the argument list.
*/
+
#if defined(__osf__) || defined(_AIX3)
-# define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:x"
-#endif
+# 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:h:IiJ:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
-#endif
+# 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:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:"
-#endif
+# define OPTIONS "B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
+#endif /* ! OPTIONS */
opterr = 0;
while ((j = getopt(argc, argv, OPTIONS)) != -1)
{
@@ -273,10 +278,37 @@ main(argc, argv, envp)
tTflag(optarg);
setbuf(stdout, (char *) NULL);
break;
+
+ case 'G': /* relay (gateway) submission */
+ SubmitMode |= SUBMIT_MTA;
+ break;
+
+ case 'L':
+ j = min(strlen(optarg), 24) + 1;
+ sysloglabel = xalloc(j);
+ (void) strlcpy(sysloglabel, optarg, j);
+ break;
+
+ case 'U': /* initial (user) submission */
+ SubmitMode |= SUBMIT_MSA;
+ break;
}
}
opterr = 1;
+ if (sysloglabel != NULL)
+ {
+#if LOG
+ closelog();
+# ifdef LOG_MAIL
+ openlog(sysloglabel, LOG_PID, LOG_MAIL);
+# else /* LOG_MAIL */
+ openlog(sysloglabel, LOG_PID);
+# endif /* LOG_MAIL */
+#endif /* LOG */
+ }
+
+
/* set up the blank envelope */
BlankEnvelope.e_puthdr = putheader;
BlankEnvelope.e_putbody = putbody;
@@ -299,12 +331,14 @@ main(argc, argv, envp)
if (pw != NULL)
(void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
else
- (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", RealUid);
+ (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
+ (int) RealUid);
+
RealUserName = rnamebuf;
if (tTd(0, 101))
{
- printf("Version %s\n", Version);
+ dprintf("Version %s\n", Version);
finis(FALSE, EX_OK);
}
@@ -315,7 +349,7 @@ main(argc, argv, envp)
if (RealUid != 0 && geteuid() == RealUid)
{
if (tTd(47, 1))
- printf("Non-setuid binary: RunAsUid = RealUid = %d\n",
+ dprintf("Non-setuid binary: RunAsUid = RealUid = %d\n",
(int)RealUid);
RunAsUid = RealUid;
}
@@ -327,25 +361,31 @@ main(argc, argv, envp)
if (tTd(47, 5))
{
- printf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
- (int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
- printf("main: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
+ 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);
}
/* save command line arguments */
- i = 0;
+ j = 0;
for (av = argv; *av != NULL; )
- i += strlen(*av++) + 1;
+ j += strlen(*av++) + 1;
SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
- CommandLineArgs = xalloc(i);
+ CommandLineArgs = xalloc(j);
p = CommandLineArgs;
for (av = argv, i = 0; *av != NULL; )
{
+ int h;
+
SaveArgv[i++] = newstr(*av);
if (av != argv)
*p++ = ' ';
- strcpy(p, *av++);
- p += strlen(p);
+ (void) strlcpy(p, *av++, j);
+ h = strlen(p);
+ p += h;
+ j -= h + 1;
}
SaveArgv[i] = NULL;
@@ -354,59 +394,53 @@ main(argc, argv, envp)
int ll;
extern char *CompileOptions[];
- printf("Version %s\n Compiled with:", Version);
+ dprintf("Version %s\n Compiled with:", Version);
av = CompileOptions;
ll = 7;
while (*av != NULL)
{
if (ll + strlen(*av) > 63)
{
- putchar('\n');
+ dprintf("\n");
ll = 0;
}
if (ll == 0)
- {
- putchar('\t');
- putchar('\t');
- }
+ dprintf("\t\t");
else
- putchar(' ');
- printf("%s", *av);
+ dprintf(" ");
+ dprintf("%s", *av);
ll += strlen(*av++) + 1;
}
- putchar('\n');
+ dprintf("\n");
}
if (tTd(0, 10))
{
int ll;
extern char *OsCompileOptions[];
- printf(" OS Defines:");
+ dprintf(" OS Defines:");
av = OsCompileOptions;
ll = 7;
while (*av != NULL)
{
if (ll + strlen(*av) > 63)
{
- putchar('\n');
+ dprintf("\n");
ll = 0;
}
if (ll == 0)
- {
- putchar('\t');
- putchar('\t');
- }
+ dprintf("\t\t");
else
- putchar(' ');
- printf("%s", *av);
+ dprintf(" ");
+ dprintf("%s", *av);
ll += strlen(*av++) + 1;
}
- putchar('\n');
+ dprintf("\n");
#ifdef _PATH_UNIX
- printf("Kernel symbols:\t%s\n", _PATH_UNIX);
-#endif
- printf(" Def Conf file:\t%s\n", getcfname());
- printf(" Pid file:\t%s\n", PidFile);
+ 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);
}
InChannel = stdin;
@@ -428,8 +462,8 @@ main(argc, argv, envp)
tzlen = strlen(p) + 4;
tz = xalloc(tzlen);
- snprintf(tz, tzlen, "TZ=%s", p);
- putenv(tz);
+ (void) snprintf(tz, tzlen, "TZ=%s", p);
+ (void) putenv(tz);
}
/* prime the child environment */
@@ -449,15 +483,30 @@ main(argc, argv, envp)
#if NAMED_BIND
if (!bitset(RES_INIT, _res.options))
- res_init();
+ (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
_res.options |= RES_NOALIASES;
-# endif
-#endif
+# endif /* RES_NOALIASES */
+ TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry;
+ TimeOuts.res_retry[RES_TO_FIRST] = _res.retry;
+ TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry;
+ TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans;
+ TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans;
+ TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans;
+#endif /* NAMED_BIND */
errno = 0;
from = NULL;
@@ -476,7 +525,7 @@ main(argc, argv, envp)
struct utsname utsname;
if (tTd(0, 4))
- printf("canonical name: %s\n", jbuf);
+ dprintf("canonical name: %s\n", jbuf);
define('w', newstr(jbuf), CurEnv); /* must be new string */
define('j', newstr(jbuf), CurEnv);
setclass('w', jbuf);
@@ -492,7 +541,7 @@ main(argc, argv, envp)
{
*p = '\0';
if (tTd(0, 4))
- printf("\ta.k.a.: %s\n", jbuf);
+ dprintf("\ta.k.a.: %s\n", jbuf);
setclass('w', jbuf);
*p++ = '.';
p = strchr(p, '.');
@@ -504,12 +553,13 @@ main(argc, argv, envp)
else
{
if (tTd(0, 22))
- printf("uname failed (%s)\n", errstring(errno));
+ dprintf("uname failed (%s)\n",
+ errstring(errno));
makelower(jbuf);
p = jbuf;
}
if (tTd(0, 4))
- printf(" UUCP nodename: %s\n", p);
+ dprintf(" UUCP nodename: %s\n", p);
p = newstr(p);
define('k', p, CurEnv);
setclass('k', p);
@@ -520,28 +570,63 @@ main(argc, argv, envp)
for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
{
if (tTd(0, 4))
- printf("\ta.k.a.: %s\n", *av);
+ dprintf("\ta.k.a.: %s\n", *av);
setclass('w', *av);
}
-#if NETINET
- if (hp->h_addrtype == AF_INET && hp->h_length == INADDRSZ)
+#if NETINET || NETINET6
+ for (i = 0; hp->h_addr_list[i] != NULL; i++)
{
- for (i = 0; hp->h_addr_list[i] != NULL; i++)
+# if NETINET6
+ char *addr;
+ char buf6[INET6_ADDRSTRLEN];
+ struct in6_addr ia6;
+# endif /* NETINET6 */
+# if NETINET
+ struct in_addr ia;
+# endif /* NETINET */
+ char ipbuf[103];
+
+ ipbuf[0] = '\0';
+ switch (hp->h_addrtype)
{
- char ipbuf[103];
+# if NETINET
+ case AF_INET:
+ if (hp->h_length != INADDRSZ)
+ break;
- snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
- inet_ntoa(*((struct in_addr *) hp->h_addr_list[i])));
- if (tTd(0, 4))
- printf("\ta.k.a.: %s\n", ipbuf);
- setclass('w', ipbuf);
+ memmove(&ia, hp->h_addr_list[i], INADDRSZ);
+ (void) snprintf(ipbuf, sizeof ipbuf,
+ "[%.100s]", inet_ntoa(ia));
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ if (hp->h_length != IN6ADDRSZ)
+ break;
+
+ 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);
+ break;
+# endif /* NETINET6 */
}
+ if (ipbuf[0] == '\0')
+ break;
+
+ if (tTd(0, 4))
+ dprintf("\ta.k.a.: %s\n", ipbuf);
+ setclass('w', ipbuf);
}
-#endif
+#endif /* NETINET || NETINET6 */
}
/* current time */
define('b', arpadate((char *) NULL), CurEnv);
+ /* current load average */
+ CurrentLA = sm_getla(CurEnv);
QueueLimitRecipient = (QUEUE_CHAR *) NULL;
QueueLimitSender = (QUEUE_CHAR *) NULL;
@@ -576,17 +661,17 @@ main(argc, argv, envp)
{
case MD_DAEMON:
case MD_FGDAEMON:
-# if !DAEMON
+#if !DAEMON
usrerr("Daemon mode not implemented");
ExitStat = EX_USAGE;
break;
-# endif /* DAEMON */
+#endif /* !DAEMON */
case MD_SMTP:
-# if !SMTP
+#if !SMTP
usrerr("I don't speak SMTP");
ExitStat = EX_USAGE;
break;
-# endif /* SMTP */
+#endif /* !SMTP */
case MD_INITALIAS:
case MD_DELIVER:
@@ -619,7 +704,8 @@ main(argc, argv, envp)
if (RealUid != 0)
warn_C_flag = TRUE;
ConfFile = optarg;
- (void) drop_privileges(TRUE);
+ dp = drop_privileges(TRUE);
+ setstat(dp);
safecf = FALSE;
break;
@@ -643,21 +729,31 @@ main(argc, argv, envp)
FullName = newstr(optarg);
break;
+ case 'G': /* relay (gateway) submission */
+ /* already set */
+ break;
+
case 'h': /* hop count */
- CurEnv->e_hopcount = strtol(optarg, &ep, 10);
+ CurEnv->e_hopcount = (short) strtol(optarg, &ep, 10);
if (*ep)
{
usrerr("Bad hop count (%s)", optarg);
ExitStat = EX_USAGE;
}
break;
-
+
+ case 'L': /* program label */
+ /* already set */
+ break;
+
case 'n': /* don't alias */
NoAlias = TRUE;
break;
case 'N': /* delivery status notifications */
DefaultNotify |= QHASNOTIFY;
+ define(macid("{dsn_notify}", NULL),
+ newstr(optarg), CurEnv);
if (strcasecmp(optarg, "never") == 0)
break;
for (p = optarg; p != NULL; optarg = p)
@@ -708,43 +804,61 @@ main(argc, argv, envp)
break;
case 'q': /* run queue files at intervals */
-# if QUEUE
+#if QUEUE
+ /* sanity check */
+ if (OpMode != MD_DELIVER &&
+ OpMode != MD_DAEMON &&
+ OpMode != MD_FGDAEMON &&
+ OpMode != MD_PRINT &&
+ OpMode != MD_QUEUERUN)
+ {
+ usrerr("Can not use -q with -b%c", OpMode);
+ ExitStat = EX_USAGE;
+ break;
+ }
+
+ /* don't override -bd, -bD or -bp */
+ if (OpMode == MD_DELIVER)
+ OpMode = MD_QUEUERUN;
+
FullName = NULL;
- queuemode = TRUE;
+
switch (optarg[0])
{
case 'I':
- if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
- syserr("!Out of memory!!");
+ new = (QUEUE_CHAR *) xalloc(sizeof *new);
new->queue_match = newstr(&optarg[1]);
new->queue_next = QueueLimitId;
QueueLimitId = new;
break;
case 'R':
- if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
- syserr("!Out of memory!!");
+ new = (QUEUE_CHAR *) xalloc(sizeof *new);
new->queue_match = newstr(&optarg[1]);
new->queue_next = QueueLimitRecipient;
QueueLimitRecipient = new;
break;
case 'S':
- if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
- syserr("!Out of memory!!");
+ new = (QUEUE_CHAR *) xalloc(sizeof *new);
new->queue_match = newstr(&optarg[1]);
new->queue_next = QueueLimitSender;
QueueLimitSender = new;
break;
default:
+ i = Errors;
QueueIntvl = convtime(optarg, 'm');
+
+ /* check for bad conversion */
+ if (i < Errors)
+ ExitStat = EX_USAGE;
break;
}
-# else /* QUEUE */
+#else /* QUEUE */
usrerr("I don't know about queues");
ExitStat = EX_USAGE;
-# endif /* QUEUE */
+#endif /* QUEUE */
break;
case 'R': /* DSN RET: what to return */
@@ -762,6 +876,8 @@ main(argc, argv, envp)
usrerr("Invalid -R value");
ExitStat = EX_USAGE;
}
+ define(macid("{dsn_ret}", NULL),
+ newstr(optarg), CurEnv);
break;
case 't': /* read recipients from message */
@@ -769,7 +885,7 @@ main(argc, argv, envp)
break;
case 'U': /* initial (user) submission */
- UserSubmission = TRUE;
+ /* already set */
break;
case 'V': /* DSN ENVID: set "original" envelope id */
@@ -779,23 +895,32 @@ main(argc, argv, envp)
ExitStat = EX_USAGE;
}
else
+ {
CurEnv->e_envid = newstr(optarg);
+ define(macid("{dsn_envid}", NULL),
+ newstr(optarg), CurEnv);
+ }
break;
case 'X': /* traffic log file */
- (void) drop_privileges(TRUE);
- TrafficLogFile = fopen(optarg, "a");
+ dp = drop_privileges(TRUE);
+ setstat(dp);
+ if (stat(optarg, &traf_st) == 0 &&
+ S_ISFIFO(traf_st.st_mode))
+ TrafficLogFile = fopen(optarg, "w");
+ else
+ TrafficLogFile = fopen(optarg, "a");
if (TrafficLogFile == NULL)
{
syserr("cannot open %s", optarg);
ExitStat = EX_CANTCREAT;
break;
}
-#ifdef HASSETVBUF
- setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
-#else
- setlinebuf(TrafficLogFile);
-#endif
+#if HASSETVBUF
+ (void) setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
+#else /* HASSETVBUF */
+ (void) setlinebuf(TrafficLogFile);
+#endif /* HASSETVBUF */
break;
/* compatibility flags */
@@ -816,22 +941,22 @@ main(argc, argv, envp)
setoption('f', "T", FALSE, TRUE, CurEnv);
break;
-# ifdef DBM
+#ifdef DBM
case 'I': /* initialize alias DBM file */
OpMode = MD_INITALIAS;
break;
-# endif /* DBM */
+#endif /* DBM */
-# if defined(__osf__) || defined(_AIX3)
+#if defined(__osf__) || defined(_AIX3)
case 'x': /* random flag that OSF/1 & AIX mailx passes */
break;
-# endif
-# if defined(sony_news)
+#endif /* defined(__osf__) || defined(_AIX3) */
+#if defined(sony_news)
case 'E':
case 'J': /* ignore flags for Japanese code conversion
- impremented on Sony NEWS */
+ implemented on Sony NEWS */
break;
-# endif
+#endif /* defined(sony_news) */
default:
finis(TRUE, EX_USAGE);
@@ -840,6 +965,35 @@ main(argc, argv, envp)
}
av += optind;
+ if (bitset(SUBMIT_MTA, SubmitMode) &&
+ bitset(SUBMIT_MSA, SubmitMode))
+ {
+ /* sanity check */
+ errno = 0; /* reset to avoid bogus error messages */
+ syserr("Cannot use both -G and -U together");
+ }
+ else if (bitset(SUBMIT_MTA, SubmitMode))
+ define(macid("{daemon_flags}", NULL), "CC f", CurEnv);
+ else if (bitset(SUBMIT_MSA, SubmitMode))
+ {
+ 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);
+ }
+ }
+ else
+ {
+#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 */
+ }
+
/*
** Do basic initialization.
** Read system control file.
@@ -857,8 +1011,9 @@ main(argc, argv, envp)
#if XDEBUG
checkfd012("before readcf");
-#endif
+#endif /* XDEBUG */
vendor_pre_defaults(CurEnv);
+
readcf(getcfname(), safecf, CurEnv);
ConfigFileRead = TRUE;
vendor_post_defaults(CurEnv);
@@ -878,15 +1033,21 @@ main(argc, argv, envp)
if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
{
/* drop privileges -- daemon mode done after socket/bind */
- (void) drop_privileges(FALSE);
+ dp = drop_privileges(FALSE);
+ setstat(dp);
}
+#if NAMED_BIND
+ _res.retry = TimeOuts.res_retry[RES_TO_DEFAULT];
+ _res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT];
+#endif /* NAMED_BIND */
+
/*
** Find our real host name for future logging.
*/
- p = getauthinfo(STDIN_FILENO, &forged);
- define('_', p, CurEnv);
+ authinfo = getauthinfo(STDIN_FILENO, &forged);
+ define('_', authinfo, CurEnv);
/* suppress error printing if errors mailed back or whatever */
if (CurEnv->e_errormode != EM_PRINT)
@@ -902,16 +1063,16 @@ main(argc, argv, envp)
if (tTd(0, 1))
{
- printf("\n============ SYSTEM IDENTITY (after readcf) ============");
- printf("\n (short domain name) $w = ");
+ dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
+ dprintf("\n (short domain name) $w = ");
xputs(macvalue('w', CurEnv));
- printf("\n (canonical domain name) $j = ");
+ dprintf("\n (canonical domain name) $j = ");
xputs(macvalue('j', CurEnv));
- printf("\n (subdomain name) $m = ");
+ dprintf("\n (subdomain name) $m = ");
xputs(macvalue('m', CurEnv));
- printf("\n (node name) $k = ");
+ dprintf("\n (node name) $k = ");
xputs(macvalue('k', CurEnv));
- printf("\n========================================================\n\n");
+ dprintf("\n========================================================\n\n");
}
/*
@@ -923,11 +1084,12 @@ main(argc, argv, envp)
if (warn_C_flag)
auth_warning(CurEnv, "Processed by %s with -C %s",
RealUserName, ConfFile);
- if (Warn_Q_option)
+ if (Warn_Q_option && !wordinclass(RealUserName, 't'))
auth_warning(CurEnv, "Processed from queue %s", QueueDir);
/* check body type for legality */
if (CurEnv->e_bodytype == NULL)
+ /* EMPTY */
/* nothing */ ;
else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0)
SevenBitInput = TRUE;
@@ -944,7 +1106,7 @@ main(argc, argv, envp)
DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
/* be sure we don't pick up bogus HOSTALIASES environment variable */
- if (queuemode && RealUid != 0)
+ if (OpMode == MD_QUEUERUN && RealUid != 0)
(void) unsetenv("HOSTALIASES");
/* check for sane configuration level */
@@ -977,10 +1139,10 @@ main(argc, argv, envp)
{
if (LogLevel > 1)
sm_syslog(LOG_ALERT, NOQID,
- "user %d attempted to %s",
- RealUid,
- OpMode != MD_PURGESTAT ? "run daemon"
- : "purge host status");
+ "user %d attempted to %s",
+ RealUid,
+ OpMode != MD_PURGESTAT ? "run daemon"
+ : "purge host status");
usrerr("Permission denied");
finis(FALSE, EX_USAGE);
}
@@ -993,9 +1155,9 @@ main(argc, argv, envp)
sm_syslog(LOG_ALERT, NOQID,
"user %d attempted to rebuild the alias map",
RealUid);
- usrerr("Permission denied");
- finis(FALSE, EX_USAGE);
- }
+ usrerr("Permission denied");
+ finis(FALSE, EX_USAGE);
+ }
if (MeToo)
BlankEnvelope.e_flags |= EF_METOO;
@@ -1016,13 +1178,13 @@ main(argc, argv, envp)
HoldErrs = FALSE;
/* arrange to exit cleanly on hangup signal */
if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
- setsignal(SIGHUP, intsig);
+ (void) setsignal(SIGHUP, intsig);
break;
case MD_FGDAEMON:
run_in_foreground = TRUE;
OpMode = MD_DAEMON;
- /* fall through ... */
+ /* FALLTHROUGH */
case MD_DAEMON:
vendor_daemon_setup(CurEnv);
@@ -1034,28 +1196,23 @@ main(argc, argv, envp)
/* 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");
- setsignal(SIGHUP, sighup);
+ "daemon invoked without full pathname; kill -1 won't work");
+ (void) setsignal(SIGHUP, sighup);
/* workaround: can't seem to release the signal in the parent */
- releasesignal(SIGHUP);
+ (void) releasesignal(SIGHUP);
break;
case MD_INITALIAS:
Verbose = 2;
CurEnv->e_errormode = EM_PRINT;
HoldErrs = FALSE;
- /* fall through... */
-
- case MD_PRINT:
- /* to handle sendmail -bp -qSfoobar properly */
- queuemode = FALSE;
- /* fall through... */
+ /* FALLTHROUGH */
default:
/* arrange to exit cleanly on hangup signal */
if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
- setsignal(SIGHUP, intsig);
+ (void) setsignal(SIGHUP, intsig);
break;
}
@@ -1063,18 +1220,15 @@ main(argc, argv, envp)
if (FullName != NULL)
{
char *full = NULL;
- extern bool rfc822_string __P((char *));
/* full names can't have newlines */
- if (strchr(FullName, '\n') != NULL)
+ if (strchr(FullName, '\n') != NULL)
{
FullName = full = newstr(denlstring(FullName, TRUE, TRUE));
}
/* check for characters that may have to be quoted */
if (!rfc822_string(FullName))
{
- extern char *addquotes __P((char *));
-
/*
** Quote a full name with special characters
** as a comment so crackaddr() doesn't destroy
@@ -1100,13 +1254,11 @@ main(argc, argv, envp)
/* check for vendor mismatch */
if (VendorCode != VENDOR_CODE)
{
- extern char *getvendor __P((int));
-
message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
getvendor(VENDOR_CODE), getvendor(VendorCode));
}
-#endif
-
+#endif /* VENDOR_CODE */
+
/* check for out of date configuration level */
if (ConfigLevel < MAXCONFIGLEVEL)
{
@@ -1115,9 +1267,7 @@ main(argc, argv, envp)
}
if (ConfigLevel < 3)
- {
UseErrorsTo = TRUE;
- }
/* set options that were previous macros */
if (SmtpGreeting == NULL)
@@ -1134,6 +1284,7 @@ main(argc, argv, envp)
else
UnixFromLine = "From \201g \201d";
}
+ SmtpError[0] = '\0';
/* our name for SMTP codes */
expand("\201j", jbuf, sizeof jbuf, CurEnv);
@@ -1221,9 +1372,8 @@ main(argc, argv, envp)
setclass('b', "audio");
setclass('b', "video");
setclass('b', "application/octet-stream");
-#endif
+#endif /* USE_B_CLASS */
-#if _FFR_MAX_MIME_HEADER_LENGTH
/* MIME headers which have fields to check for overflow */
setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-disposition");
setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-type");
@@ -1237,7 +1387,14 @@ main(argc, argv, envp)
setclass(macid("{checkMIMEHeaders}", NULL), "content-transfer-encoding");
setclass(macid("{checkMIMEHeaders}", NULL), "content-type");
setclass(macid("{checkMIMEHeaders}", NULL), "mime-version");
-#endif
+
+ /* 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}");
/* operate in queue directory */
if (QueueDir == NULL)
@@ -1250,13 +1407,14 @@ main(argc, argv, envp)
}
else
{
- /* test path to get warning messages */
- (void) safedirpath(QueueDir, (uid_t) 0, (gid_t) 0, NULL, SFF_ANYFILE);
- if (OpMode != MD_TEST && chdir(QueueDir) < 0)
- {
- syserr("cannot chdir(%s)", QueueDir);
- ExitStat = EX_CONFIG;
- }
+ /*
+ ** 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();
}
/* check host status directory for validity */
@@ -1264,13 +1422,14 @@ main(argc, argv, envp)
{
/* cannot use this value */
if (tTd(0, 2))
- printf("Cannot use HostStatusDirectory = %s: %s\n",
+ dprintf("Cannot use HostStatusDirectory = %s: %s\n",
HostStatDir, errstring(errno));
HostStatDir = NULL;
}
-# if QUEUE
- if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
+#if QUEUE
+ if (OpMode == MD_QUEUERUN && RealUid != 0 &&
+ bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
{
struct stat stbuf;
@@ -1284,7 +1443,13 @@ main(argc, argv, envp)
finis(FALSE, EX_NOPERM);
}
}
-# endif /* QUEUE */
+#endif /* QUEUE */
+
+#if _FFR_MILTER
+ /* sanity checks on milter filters */
+ if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
+ milter_parse_list(InputFilterList, InputFilters, MAXFILTERS);
+#endif /* _FFR_MILTER */
/* if we've had errors so far, exit now */
if (ExitStat != EX_OK && OpMode != MD_TEST)
@@ -1292,7 +1457,7 @@ main(argc, argv, envp)
#if XDEBUG
checkfd012("before main() initmaps");
-#endif
+#endif /* XDEBUG */
/*
** Do operation-mode-dependent initialization.
@@ -1304,29 +1469,29 @@ main(argc, argv, envp)
/* print the queue */
#if QUEUE
dropenvelope(CurEnv, TRUE);
- signal(SIGPIPE, quiesce);
+ (void) setsignal(SIGPIPE, quiesce);
printqueue();
finis(FALSE, EX_OK);
#else /* QUEUE */
usrerr("No queue to print");
- finis(FALSE, ExitStat);
+ finis(FALSE, EX_UNAVAILABLE);
#endif /* QUEUE */
break;
case MD_HOSTSTAT:
- signal(SIGPIPE, quiesce);
- mci_traverse_persistent(mci_print_persistent, NULL);
+ (void) setsignal(SIGPIPE, quiesce);
+ (void) mci_traverse_persistent(mci_print_persistent, NULL);
finis(FALSE, EX_OK);
- break;
+ break;
case MD_PURGESTAT:
- mci_traverse_persistent(mci_purge_persistent, NULL);
+ (void) mci_traverse_persistent(mci_purge_persistent, NULL);
finis(FALSE, EX_OK);
- break;
+ break;
case MD_INITALIAS:
/* initialize maps */
- initmaps(TRUE, CurEnv);
+ initmaps();
finis(FALSE, ExitStat);
break;
@@ -1334,22 +1499,18 @@ main(argc, argv, envp)
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);
/* don't open maps for daemon -- done below in child */
break;
-
- default:
- /* open the maps */
- initmaps(FALSE, CurEnv);
- break;
}
if (tTd(0, 15))
{
- extern void printrules __P((void));
-
/* print configuration table (or at least part of it) */
if (tTd(0, 90))
printrules();
@@ -1374,7 +1535,6 @@ main(argc, argv, envp)
if (OpMode == MD_TEST)
{
char buf[MAXLINE];
- SIGFUNC_DECL intindebug __P((int));
if (isatty(fileno(stdin)))
Verbose = 2;
@@ -1389,13 +1549,11 @@ main(argc, argv, envp)
(void) setsignal(SIGINT, intindebug);
for (;;)
{
- extern void testmodeline __P((char *, ENVELOPE *));
-
if (Verbose == 2)
printf("> ");
(void) fflush(stdout);
if (fgets(buf, sizeof buf, stdin) == NULL)
- finis(TRUE, ExitStat);
+ testmodeline("/quit", CurEnv);
p = strchr(buf, '\n');
if (p != NULL)
*p = '\0';
@@ -1405,17 +1563,44 @@ main(argc, argv, envp)
}
}
-# if QUEUE
+#if SMTP
+# if STARTTLS
+ /*
+ ** 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_rand_init(RandFile, 7);
+
+# endif /* STARTTLS */
+#endif /* SMTP */
+
+#if QUEUE
/*
** If collecting stuff from the queue, go start doing that.
*/
- if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
+ if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
{
+# if SMTP
+# if STARTTLS
+ {
+ /* init TLS for client, ignore result for now */
+ (void) initclttls();
+ }
+# endif /* STARTTLS */
+# endif /* SMTP */
(void) runqueue(FALSE, Verbose);
finis(TRUE, ExitStat);
}
-# endif /* QUEUE */
+#endif /* QUEUE */
/*
** If a daemon, wait for a request.
@@ -1429,7 +1614,6 @@ main(argc, argv, envp)
if (OpMode == MD_DAEMON || QueueIntvl != 0)
{
char dtype[200];
- extern void getrequests __P((ENVELOPE *));
if (!run_in_foreground && !tTd(99, 100))
{
@@ -1446,40 +1630,55 @@ main(argc, argv, envp)
dtype[0] = '\0';
if (OpMode == MD_DAEMON)
- strcat(dtype, "+SMTP");
+ (void) strlcat(dtype, "+SMTP", sizeof dtype);
if (QueueIntvl != 0)
{
- strcat(dtype, "+queueing@");
- strcat(dtype, pintvl(QueueIntvl, TRUE));
+ (void) strlcat(dtype, "+queueing@", sizeof dtype);
+ (void) strlcat(dtype, pintvl(QueueIntvl, TRUE),
+ sizeof dtype);
}
if (tTd(0, 1))
- strcat(dtype, "+debugging");
+ (void) strlcat(dtype, "+debugging", sizeof dtype);
sm_syslog(LOG_INFO, NOQID,
- "starting daemon (%s): %s", Version, dtype + 1);
+ "starting daemon (%s): %s", Version, dtype + 1);
#ifdef XLA
xla_create_file();
-#endif
+#endif /* XLA */
+
+ /* save daemon type in a macro for possible PidFile use */
+ define(macid("{daemon_info}", NULL),
+ newstr(dtype + 1), &BlankEnvelope);
-# if QUEUE
- if (queuemode)
+ /* save queue interval in a macro for possible PidFile use */
+ define(macid("{queue_interval}", NULL),
+ newstr(pintvl(QueueIntvl, TRUE)), CurEnv);
+
+#if QUEUE
+ if (QueueIntvl != 0)
{
(void) runqueue(TRUE, FALSE);
if (OpMode != MD_DAEMON)
{
+ /* write the pid to file */
+ log_sendmail_pid(CurEnv);
for (;;)
{
- pause();
+ (void) pause();
if (DoQueueRun)
(void) runqueue(TRUE, FALSE);
}
}
}
-# endif /* QUEUE */
+#endif /* QUEUE */
dropenvelope(CurEnv, TRUE);
#if DAEMON
- getrequests(CurEnv);
+# if STARTTLS
+ /* init TLS for server, ignore result for now */
+ (void) initsrvtls();
+# endif /* STARTTLS */
+ p_flags = getrequests(CurEnv);
/* drop privileges */
(void) drop_privileges(FALSE);
@@ -1491,12 +1690,18 @@ main(argc, argv, envp)
** Get authentication data
*/
- p = getauthinfo(fileno(InChannel), &forged);
- define('_', p, &BlankEnvelope);
+ authinfo = getauthinfo(fileno(InChannel), &forged);
+ define('_', authinfo, &BlankEnvelope);
#endif /* DAEMON */
}
-# if SMTP
+ if (LogLevel > 9)
+ {
+ /* log connection information */
+ sm_syslog(LOG_INFO, NULL, "connect from %.100s", authinfo);
+ }
+
+#if SMTP
/*
** If running SMTP protocol, start collecting and executing
** commands. This will never return.
@@ -1505,7 +1710,6 @@ main(argc, argv, envp)
if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
{
char pbuf[20];
- extern void smtp __P((char *, ENVELOPE *));
/*
** Save some macros for check_* rulesets.
@@ -1515,24 +1719,46 @@ main(argc, argv, envp)
{
char ipbuf[103];
- snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
- inet_ntoa(RealHostAddr.sin.sin_addr));
-
+ (void) snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
+ anynet_ntoa(&RealHostAddr));
define(macid("{client_name}", NULL),
newstr(ipbuf), &BlankEnvelope);
+ define(macid("{client_resolve}", NULL),
+ "FORGED", &BlankEnvelope);
}
else
- define(macid("{client_name}", NULL), RealHostName, &BlankEnvelope);
+ define(macid("{client_name}", NULL), RealHostName,
+ &BlankEnvelope);
define(macid("{client_addr}", NULL),
newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope);
- if (RealHostAddr.sa.sa_family == AF_INET)
- snprintf(pbuf, sizeof pbuf, "%d", RealHostAddr.sin.sin_port);
- else
- snprintf(pbuf, sizeof pbuf, "0");
- define(macid("{client_port}", NULL), newstr(pbuf), &BlankEnvelope);
+ (void)sm_getla(&BlankEnvelope);
+
+ switch(RealHostAddr.sa.sa_family)
+ {
+# if NETINET
+ case AF_INET:
+ (void) snprintf(pbuf, sizeof pbuf, "%d",
+ RealHostAddr.sin.sin_port);
+ break;
+# endif /* NETINET */
+# if NETINET6
+ case AF_INET6:
+ (void) snprintf(pbuf, sizeof pbuf, "%d",
+ RealHostAddr.sin6.sin6_port);
+ break;
+# endif /* NETINET6 */
+ default:
+ (void) snprintf(pbuf, sizeof pbuf, "0");
+ break;
+ }
+ define(macid("{client_port}", NULL),
+ newstr(pbuf), &BlankEnvelope);
- /* initialize maps now for check_relay ruleset */
- initmaps(FALSE, CurEnv);
+#if SASL
+ /* give a syserr or just disable AUTH ? */
+ if (sasl_server_init(srvcallbacks, "Sendmail") != SASL_OK)
+ syserr("!sasl_server_init failed!");
+#endif /* SASL */
if (OpMode == MD_DAEMON)
{
@@ -1542,14 +1768,23 @@ main(argc, argv, envp)
RealHostName, CurEnv);
HoldErrs = FALSE;
}
- smtp(nullserver, CurEnv);
+ else if (p_flags == NULL)
+ {
+ p_flags = (BITMAP256 *) xalloc(sizeof *p_flags);
+ clrbitmap(p_flags);
+ }
+# if STARTTLS
+ if (OpMode == MD_SMTP)
+ (void) initsrvtls();
+# endif /* STARTTLS */
+ smtp(nullserver, *p_flags, CurEnv);
}
-# endif /* SMTP */
+#endif /* SMTP */
clearenvelope(CurEnv, FALSE);
if (OpMode == MD_VERIFY)
{
- CurEnv->e_sendmode = SM_VERIFY;
+ set_delivery_mode(SM_VERIFY, CurEnv);
PostMasterCopy = NULL;
}
else
@@ -1563,22 +1798,55 @@ main(argc, argv, envp)
*/
initsys(CurEnv);
- if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't'))
+ define(macid("{ntries}", NULL), "0", CurEnv);
+ setsender(from, CurEnv, 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))
+ {
auth_warning(CurEnv, "%s set sender to %s using -%c",
RealUserName, from, warn_f_flag);
- setsender(from, CurEnv, NULL, '\0', FALSE);
+#if SASL
+ auth = FALSE;
+#endif /* SASL */
+ }
+ if (auth)
+ {
+ char *fv;
+
+ /* set the initial sender for AUTH= to $f@$j */
+ fv = macvalue('f', CurEnv);
+ if (fv == NULL || *fv == '\0')
+ CurEnv->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));
+ }
+ else
+ p = newstr(fv);
+ CurEnv->e_auth_param = newstr(xtextify(p, NULL));
+ }
+ }
if (macvalue('s', CurEnv) == NULL)
define('s', RealHostName, CurEnv);
if (*av == NULL && !GrabTo)
{
+ CurEnv->e_to = NULL;
CurEnv->e_flags |= EF_GLOBALERRS;
+ HoldErrs = FALSE;
usrerr("Recipient names must be specified");
/* collect body for UUCP return */
if (OpMode != MD_VERIFY)
collect(InChannel, FALSE, NULL, CurEnv);
- finis(TRUE, ExitStat);
+ finis(TRUE, EX_USAGE);
}
/*
@@ -1600,11 +1868,11 @@ main(argc, argv, envp)
if (GrabTo)
{
ADDRESS *q;
-
+
for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
- q->q_flags |= QDONTSEND;
+ q->q_state = QS_REMOVED;
}
-#endif
+#endif /* _FFR_FIX_DASHT */
/*
** Read the input mail.
@@ -1613,40 +1881,77 @@ main(argc, argv, envp)
CurEnv->e_to = NULL;
if (OpMode != MD_VERIFY || GrabTo)
{
+ int savederrors = Errors;
long savedflags = CurEnv->e_flags & EF_FATALERRS;
CurEnv->e_flags |= EF_GLOBALERRS;
CurEnv->e_flags &= ~EF_FATALERRS;
+ Errors = 0;
+ buffer_errors();
collect(InChannel, FALSE, NULL, CurEnv);
+ /* header checks failed */
+ if (Errors > 0)
+ {
+ /* Log who the mail would have gone to */
+ if (LogLevel > 8 && CurEnv->e_message != NULL &&
+ !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;
+ }
+ flush_errors(TRUE);
+ finis(TRUE, ExitStat);
+ /* NOTREACHED */
+ return -1;
+ }
+
/* bail out if message too large */
if (bitset(EF_CLRQUEUE, CurEnv->e_flags))
{
- finis(TRUE, ExitStat);
- /*NOTREACHED*/
+ finis(TRUE, ExitStat != EX_OK ? ExitStat : EX_DATAERR);
+ /* NOTREACHED */
return -1;
}
+ Errors = savederrors;
CurEnv->e_flags |= savedflags;
}
errno = 0;
if (tTd(1, 1))
- printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
+ dprintf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
/*
** Actually send everything.
** If verifying, just ack.
*/
- CurEnv->e_from.q_flags |= QDONTSEND;
+ CurEnv->e_from.q_state = QS_SENDER;
if (tTd(1, 5))
{
- printf("main: QDONTSEND ");
+ dprintf("main: QS_SENDER ");
printaddr(&CurEnv->e_from, FALSE);
}
CurEnv->e_to = NULL;
- CurrentLA = getla();
+ CurrentLA = sm_getla(CurEnv);
GrabTo = FALSE;
+#if NAMED_BIND
+ _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
+ _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
+#endif /* NAMED_BIND */
sendall(CurEnv, SM_DEFAULT);
/*
@@ -1655,8 +1960,8 @@ main(argc, argv, envp)
*/
finis(TRUE, ExitStat);
- /*NOTREACHED*/
- return -1;
+ /* NOTREACHED */
+ return ExitStat;
}
/* ARGSUSED */
@@ -1664,6 +1969,7 @@ SIGFUNC_DECL
quiesce(sig)
int sig;
{
+ clear_events();
finis(FALSE, EX_OK);
}
@@ -1675,8 +1981,6 @@ intindebug(sig)
longjmp(TopFrame, 1);
return SIGFUNC_RETURN;
}
-
-
/*
** FINIS -- Clean up and exit.
**
@@ -1696,16 +2000,10 @@ finis(drop, exitstat)
bool drop;
volatile int exitstat;
{
- extern void closemaps __P((void));
-#ifdef USERDB
- extern void _udbx_close __P((void));
-#endif
if (tTd(2, 1))
{
- extern void printenvflags __P((ENVELOPE *));
-
- printf("\n====finis: stat %d e_id=%s e_flags=",
+ dprintf("\n====finis: stat %d e_id=%s e_flags=",
exitstat,
CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
printenvflags(CurEnv);
@@ -1722,8 +2020,13 @@ finis(drop, exitstat)
/* clean up temp files */
CurEnv->e_to = NULL;
- if (drop && CurEnv->e_id != NULL)
- dropenvelope(CurEnv, TRUE);
+ if (drop)
+ {
+ if (CurEnv->e_id != NULL)
+ dropenvelope(CurEnv, TRUE);
+ else
+ poststats(StatFile);
+ }
/* flush any cached connections */
mci_flush(TRUE, NULL);
@@ -1731,35 +2034,36 @@ finis(drop, exitstat)
/* close maps belonging to this pid */
closemaps();
-#ifdef USERDB
+#if USERDB
/* close UserDatabase */
_udbx_close();
-#endif
+#endif /* USERDB */
-# ifdef XLA
+#ifdef XLA
/* clean up extended load average stuff */
xla_all_end();
-# endif
+#endif /* XLA */
/* and exit */
forceexit:
if (LogLevel > 78)
sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "finis, pid=%d",
- getpid());
+ "finis, pid=%d",
+ getpid());
if (exitstat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
exitstat = EX_OK;
+ sync_queue_time();
+
/* reset uid for process accounting */
endpwent();
- setuid(RealUid);
-
+ (void) setuid(RealUid);
exit(exitstat);
}
/*
** INTSIG -- clean up on interrupt
**
-** This just arranges to exit. It pessimises in that it
+** This just arranges to exit. It pessimizes in that it
** may resend a message.
**
** Parameters:
@@ -1777,15 +2081,48 @@ SIGFUNC_DECL
intsig(sig)
int sig;
{
- if (LogLevel > 79)
+ bool drop = FALSE;
+
+ clear_events();
+ if (sig != 0 && LogLevel > 79)
sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
FileName = NULL;
- unlockqueue(CurEnv);
closecontrolsocket(TRUE);
#ifdef XLA
xla_all_end();
-#endif
- finis(FALSE, EX_OK);
+#endif /* XLA */
+
+ /* Clean-up on aborted stdin message submission */
+ if (CurEnv->e_id != NULL &&
+ (OpMode == MD_SMTP ||
+ OpMode == MD_DELIVER ||
+ OpMode == MD_ARPAFTP))
+ {
+ register ADDRESS *q;
+
+ /* don't return an error indication */
+ CurEnv->e_to = NULL;
+ CurEnv->e_flags &= ~EF_FATALERRS;
+ CurEnv->e_flags |= EF_CLRQUEUE;
+
+ /*
+ ** Spin through the addresses and
+ ** mark them dead to prevent bounces
+ */
+
+ 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;
+ }
+ else
+ unlockqueue(CurEnv);
+
+ finis(drop, EX_OK);
}
/*
** INITMACROS -- initialize the macro system
@@ -1867,7 +2204,7 @@ initmacros(e)
** droplev -- how "deeply" we should drop the line.
** 0 -- ignore signals, mail back errors, make sure
** output goes to stdout.
-** 1 -- also, make stdout go to transcript.
+** 1 -- also, make stdout go to /dev/null.
** 2 -- also, disconnect from controlling terminal
** (only for daemon mode).
** e -- the current envelope.
@@ -1888,17 +2225,17 @@ disconnect(droplev, e)
int fd;
if (tTd(52, 1))
- printf("disconnect: In %d Out %d, e=%lx\n",
+ dprintf("disconnect: In %d Out %d, e=%lx\n",
fileno(InChannel), fileno(OutChannel), (u_long) e);
if (tTd(52, 100))
{
- printf("don't\n");
+ dprintf("don't\n");
return;
}
if (LogLevel > 93)
sm_syslog(LOG_DEBUG, e->e_id,
- "disconnect level %d",
- droplev);
+ "disconnect level %d",
+ droplev);
/* be sure we don't get nasty signals */
(void) setsignal(SIGINT, SIG_IGN);
@@ -1929,27 +2266,15 @@ disconnect(droplev, e)
}
if (droplev > 0)
{
- if (e->e_xfp == NULL)
- {
- fd = open("/dev/null", O_WRONLY, 0666);
- if (fd == -1)
- sm_syslog(LOG_ERR, e->e_id,
- "disconnect: open(\"/dev/null\") failed: %s",
- errstring(errno));
- }
- else
- {
- fd = fileno(e->e_xfp);
- if (fd == -1)
- sm_syslog(LOG_ERR, e->e_id,
- "disconnect: fileno(e->e_xfp) failed: %s",
- errstring(errno));
- }
+ fd = open("/dev/null", O_WRONLY, 0666);
+ if (fd == -1)
+ sm_syslog(LOG_ERR, e->e_id,
+ "disconnect: open(\"/dev/null\") failed: %s",
+ errstring(errno));
(void) fflush(stdout);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- if (e->e_xfp == NULL)
- close(fd);
+ (void) dup2(fd, STDOUT_FILENO);
+ (void) dup2(fd, STDERR_FILENO);
+ (void) close(fd);
}
/* drop our controlling TTY completely if possible */
@@ -1961,12 +2286,12 @@ disconnect(droplev, e)
#if XDEBUG
checkfd012("disconnect");
-#endif
+#endif /* XDEBUG */
if (LogLevel > 71)
sm_syslog(LOG_DEBUG, e->e_id,
- "in background, pid=%d",
- getpid());
+ "in background, pid=%d",
+ getpid());
errno = 0;
}
@@ -1990,7 +2315,7 @@ obsolete(argv)
ap[1] != 'd' &&
#if defined(sony_news)
ap[1] != 'E' && ap[1] != 'J' &&
-#endif
+#endif /* defined(sony_news) */
argv[1] != NULL && argv[1][0] != '-')
{
argv++;
@@ -2002,9 +2327,8 @@ obsolete(argv)
if (ap[1] == 'C' && ap[2] == '\0')
{
*argv = xalloc(sizeof(__DEFPATH) + 2);
- argv[0][0] = '-';
- argv[0][1] = 'C';
- (void)strcpy(&argv[0][2], __DEFPATH);
+ (void) snprintf(argv[0], sizeof(__DEFPATH) + 2, "-C%s",
+ __DEFPATH);
}
/* If -q doesn't have an argument, run it once. */
@@ -2015,7 +2339,7 @@ obsolete(argv)
if (ap[1] == 'd' && ap[2] == '\0')
*argv = "-d0-99.1";
-# if defined(sony_news)
+#if defined(sony_news)
/* if -E doesn't have an argument, use -EC */
if (ap[1] == 'E' && ap[2] == '\0')
*argv = "-EC";
@@ -2023,7 +2347,7 @@ obsolete(argv)
/* if -J doesn't have an argument, use -JJ */
if (ap[1] == 'J' && ap[2] == '\0')
*argv = "-JJ";
-# endif
+#endif /* defined(sony_news) */
}
}
/*
@@ -2041,12 +2365,12 @@ obsolete(argv)
void
#ifdef __STDC__
auth_warning(register ENVELOPE *e, const char *msg, ...)
-#else
+#else /* __STDC__ */
auth_warning(e, msg, va_alist)
register ENVELOPE *e;
const char *msg;
va_dcl
-#endif
+#endif /* __STDC__ */
{
char buf[MAXLINE];
VA_LOCAL_DECL
@@ -2055,7 +2379,6 @@ auth_warning(e, msg, va_alist)
{
register char *p;
static char hostbuf[48];
- extern struct hostent *myhostname __P((char *, int));
if (hostbuf[0] == '\0')
(void) myhostname(hostbuf, sizeof hostbuf);
@@ -2065,11 +2388,11 @@ auth_warning(e, msg, va_alist)
VA_START(msg);
vsnprintf(p, SPACELEFT(buf, p), msg, ap);
VA_END;
- addheader("X-Authentication-Warning", buf, &e->e_header);
+ addheader("X-Authentication-Warning", buf, 0, &e->e_header);
if (LogLevel > 3)
sm_syslog(LOG_INFO, e->e_id,
- "Authentication-Warning: %.400s",
- buf);
+ "Authentication-Warning: %.400s",
+ buf);
}
}
/*
@@ -2116,7 +2439,7 @@ setuserenv(envar, value)
const char *envar;
const char *value;
{
- int i;
+ int i, l;
char **evp = UserEnviron;
char *p;
@@ -2127,11 +2450,10 @@ setuserenv(envar, value)
return;
}
- i = strlen(envar);
- p = (char *) xalloc(strlen(value) + i + 2);
- strcpy(p, envar);
- p[i++] = '=';
- strcpy(&p[i], value);
+ i = strlen(envar) + 1;
+ l = strlen(value) + i + 1;
+ p = (char *) xalloc(l);
+ (void) snprintf(p, l, "%s=%s", envar, value);
while (*evp != NULL && strncmp(*evp, p, i) != 0)
evp++;
@@ -2161,18 +2483,21 @@ dumpstate(when)
{
register char *j = macvalue('j', CurEnv);
int rs;
+ extern int NextMacroId;
sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "--- dumping state on %s: $j = %s ---",
- when,
- j == NULL ? "<NULL>" : j);
+ "--- dumping state on %s: $j = %s ---",
+ when,
+ j == NULL ? "<NULL>" : j);
if (j != NULL)
{
if (!wordinclass(j, 'w'))
sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "*** $j not in $=w ***");
+ "*** $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",
+ NextMacroId, MAXMACROID);
sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
printopenfds(TRUE);
sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
@@ -2180,15 +2505,15 @@ dumpstate(when)
rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
if (rs > 0)
{
- int stat;
+ int status;
register char **pvp;
char *pv[MAXATOM + 1];
pv[0] = NULL;
- stat = rewrite(pv, rs, 0, CurEnv);
+ status = rewrite(pv, rs, 0, CurEnv);
sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "--- ruleset debug_dumpstate returns stat %d, pv: ---",
- stat);
+ "--- ruleset debug_dumpstate returns stat %d, pv: ---",
+ status);
for (pvp = pv; *pvp != NULL; pvp++)
sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
}
@@ -2211,27 +2536,50 @@ SIGFUNC_DECL
sighup(sig)
int sig;
{
+ int i;
+ extern int DtableSize;
+
+ clear_events();
+ (void) alarm(0);
if (SaveArgv[0][0] != '/')
{
if (LogLevel > 3)
- sm_syslog(LOG_INFO, NOQID, "could not restart: need full path");
+ sm_syslog(LOG_INFO, NOQID,
+ "could not restart: need full path");
finis(FALSE, EX_OSFILE);
}
if (LogLevel > 3)
- sm_syslog(LOG_INFO, NOQID, "restarting %s on signal", SaveArgv[0]);
- alarm(0);
- releasesignal(SIGHUP);
+ sm_syslog(LOG_INFO, NOQID, "restarting %s %s",
+ sig == 0 ? "due to control command" : "on signal",
+ SaveArgv[0]);
+
+ /* Control socket restart? */
+ if (sig != 0)
+ (void) releasesignal(SIGHUP);
+
closecontrolsocket(TRUE);
if (drop_privileges(TRUE) != EX_OK)
{
if (LogLevel > 0)
- sm_syslog(LOG_ALERT, NOQID, "could not set[ug]id(%d, %d): %m",
- RunAsUid, RunAsGid);
+ sm_syslog(LOG_ALERT, NOQID,
+ "could not set[ug]id(%d, %d): %m",
+ RunAsUid, RunAsGid);
finis(FALSE, EX_OSERR);
}
- execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
+
+ /* arrange for all the files to be closed */
+ for (i = 3; i < DtableSize; i++)
+ {
+ register int j;
+
+ if ((j = fcntl(i, F_GETFD, 0)) != -1)
+ (void) fcntl(i, F_SETFD, j | FD_CLOEXEC);
+ }
+
+ (void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
if (LogLevel > 0)
- sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", SaveArgv[0]);
+ sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m",
+ SaveArgv[0]);
finis(FALSE, EX_OSFILE);
}
/*
@@ -2254,8 +2602,9 @@ drop_privileges(to_real_uid)
GIDSET_T emptygidset[1];
if (tTd(47, 1))
- printf("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);
+ 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);
if (to_real_uid)
{
@@ -2270,18 +2619,61 @@ drop_privileges(to_real_uid)
/* reset group permissions; these can be set later */
emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
+ {
+ syserr("drop_privileges: setgroups(1, %d) failed",
+ (int)emptygidset[0]);
rval = EX_OSERR;
+ }
/* reset primary group and user id */
if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
+ {
+ syserr("drop_privileges: setgid(%d) failed", (int)RunAsGid);
rval = EX_OSERR;
- if ((to_real_uid || RunAsUid != 0) && setuid(RunAsUid) < 0)
- rval = EX_OSERR;
+ }
+ if (to_real_uid || RunAsUid != 0)
+ {
+ uid_t euid = geteuid();
+
+ if (setuid(RunAsUid) < 0)
+ {
+ syserr("drop_privileges: setuid(%d) failed",
+ (int)RunAsUid);
+ rval = EX_OSERR;
+ }
+ else if (RunAsUid != 0 && setuid(0) == 0)
+ {
+ /*
+ ** Believe it or not, the Linux capability model
+ ** allows a non-root process to override setuid()
+ ** on a process running as root and prevent that
+ ** process from dropping privileges.
+ */
+
+ syserr("drop_privileges: setuid(0) succeeded (when it should not)");
+ rval = EX_OSERR;
+ }
+ else if (RunAsUid != euid && setuid(euid) == 0)
+ {
+ /*
+ ** Some operating systems will keep the saved-uid
+ ** if a non-root effective-uid calls setuid(real-uid)
+ ** making it possible to set it back again later.
+ */
+
+ syserr("drop_privileges: Unable to drop non-root set-user-id privileges");
+ rval = EX_OSERR;
+ }
+ }
if (tTd(47, 5))
{
- printf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
- (int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
- printf("drop_privileges: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
+ 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);
+ if (tTd(47, 10))
+ dprintf("drop_privileges: rval = %d\n", rval);
}
return rval;
}
@@ -2342,7 +2734,7 @@ fill_fd(fd, where)
** X normal process through rule set X
*/
-void
+static void
testmodeline(line, e)
char *line;
ENVELOPE *e;
@@ -2358,20 +2750,19 @@ testmodeline(line, e)
ADDRESS a;
static int tryflags = RF_COPYNONE;
char exbuf[MAXLINE];
- extern bool invalidaddr __P((char *, char *));
- extern char *crackaddr __P((char *));
- extern void dump_class __P((STAB *, int));
- extern void translate_dollars __P((char *));
- extern void help __P((char *));
+ extern u_char TokTypeNoC[];
+#if _FFR_ADDR_TYPE
+ define(macid("{addr_type}", NULL), "e r", e);
+#endif /* _FFR_ADDR_TYPE */
switch (line[0])
{
case '#':
- case 0:
+ case '\0':
return;
case '?':
- help("-bt");
+ help("-bt", e);
return;
case '.': /* config-style settings */
@@ -2438,22 +2829,22 @@ testmodeline(line, e)
return;
do
{
- putchar('R');
+ (void) putchar('R');
s = rw->r_lhs;
while (*s != NULL)
{
xputs(*s++);
- putchar(' ');
+ (void) putchar(' ');
}
- putchar('\t');
- putchar('\t');
+ (void) putchar('\t');
+ (void) putchar('\t');
s = rw->r_rhs;
while (*s != NULL)
{
xputs(*s++);
- putchar(' ');
+ (void) putchar(' ');
}
- putchar('\n');
+ (void) putchar('\n');
} while ((rw = rw->r_next) != NULL);
break;
@@ -2530,6 +2921,11 @@ testmodeline(line, e)
printf("Usage: /[canon|map|mx|parse|try|tryflags]\n");
return;
}
+ if (strcasecmp(&line[1], "quit") == 0)
+ {
+ CurEnv->e_id = NULL;
+ finis(TRUE, ExitStat);
+ }
if (strcasecmp(&line[1], "mx") == 0)
{
#if NAMED_BIND
@@ -2543,13 +2939,13 @@ testmodeline(line, e)
printf("Usage: /mx address\n");
return;
}
- nmx = getmxrr(p, mxhosts, FALSE, &rcode);
+ nmx = getmxrr(p, mxhosts, NULL, FALSE, &rcode);
printf("getmxrr(%s) returns %d value(s):\n", p, nmx);
for (i = 0; i < nmx; i++)
printf("\t%s\n", mxhosts[i]);
-#else
+#else /* NAMED_BIND */
printf("No MX code compiled in\n");
-#endif
+#endif /* NAMED_BIND */
}
else if (strcasecmp(&line[1], "canon") == 0)
{
@@ -2560,13 +2956,12 @@ testmodeline(line, e)
printf("Usage: /canon address\n");
return;
}
- else if (strlen(p) >= sizeof host)
+ else if (strlcpy(host, p, sizeof host) >= sizeof host)
{
printf("Name too long\n");
return;
}
- strcpy(host, p);
- (void) getcanonname(host, sizeof(host), HasWildcardMX);
+ (void) getcanonname(host, sizeof host, HasWildcardMX);
printf("getcanonname(%s) returns %s\n", p, host);
}
else if (strcasecmp(&line[1], "map") == 0)
@@ -2593,7 +2988,8 @@ testmodeline(line, e)
printf("Map named \"%s\" not found\n", p);
return;
}
- if (!bitset(MF_OPEN, map->s_map.map_mflags))
+ if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
+ !openmap(&(map->s_map)))
{
printf("Map named \"%s\" not open\n", p);
return;
@@ -2611,7 +3007,7 @@ testmodeline(line, e)
else if (strcasecmp(&line[1], "try") == 0)
{
MAILER *m;
- STAB *s;
+ STAB *st;
auto int rcode = EX_OK;
q = strpbrk(p, " \t");
@@ -2625,13 +3021,13 @@ testmodeline(line, e)
printf("Usage: /try mailer address\n");
return;
}
- s = stab(p, ST_MAILER, ST_FIND);
- if (s == NULL)
+ st = stab(p, ST_MAILER, ST_FIND);
+ if (st == NULL)
{
printf("Unknown mailer %s\n", p);
return;
}
- m = s->s_mailer;
+ 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",
@@ -2673,6 +3069,13 @@ 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 */
}
else if (strcasecmp(&line[1], "parse") == 0)
{
@@ -2723,13 +3126,13 @@ testmodeline(line, e)
char pvpbuf[PSBUFSIZE];
pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
- &delimptr, NULL);
+ &delimptr, ConfigLevel >= 9 ? TokTypeNoC : NULL);
if (pvp == NULL)
continue;
p = q;
while (*p != '\0')
{
- int stat;
+ int status;
rs = strtorwset(p, NULL, ST_FIND);
if (rs < 0)
@@ -2737,18 +3140,17 @@ testmodeline(line, e)
printf("Undefined ruleset %s\n", p);
break;
}
- stat = rewrite(pvp, rs, 0, e);
- if (stat != EX_OK)
+ status = rewrite(pvp, rs, 0, e);
+ if (status != EX_OK)
printf("== Ruleset %s (%d) status %d\n",
- p, rs, stat);
+ p, rs, status);
while (*p != '\0' && *p++ != ',')
continue;
}
} while (*(p = delimptr) != '\0');
}
-
-void
+static void
dump_class(s, id)
register STAB *s;
int id;
diff --git a/contrib/sendmail/src/map.c b/contrib/sendmail/src/map.c
index 8fc3387f..3908966 100644
--- a/contrib/sendmail/src/map.c
+++ b/contrib/sendmail/src/map.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1992, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,10 +12,11 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)map.c 8.261 (Berkeley) 2/2/1999";
-#endif /* not lint */
+static char id[] = "@(#)$Id: map.c,v 8.414.4.13 2000/07/14 16:48:21 ca Exp $";
+#endif /* ! lint */
+
+#include <sendmail.h>
-#include "sendmail.h"
#ifdef NDBM
# include <ndbm.h>
@@ -23,21 +25,54 @@ static char sccsid[] = "@(#)map.c 8.261 (Berkeley) 2/2/1999";
ERROR README: the README file about tweaking Berkeley DB so it can
ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile
ERROR README: and use -DNEWDB instead.
-# endif
-#endif
+# endif /* R_FIRST */
+#endif /* NDBM */
#ifdef NEWDB
# include <db.h>
# ifndef DB_VERSION_MAJOR
# define DB_VERSION_MAJOR 1
-# endif
-#endif
+# endif /* ! DB_VERSION_MAJOR */
+#endif /* NEWDB */
#ifdef NIS
struct dom_binding; /* forward reference needed on IRIX */
# include <rpcsvc/ypclnt.h>
# ifdef NDBM
# define NDBM_YP_COMPAT /* create YP-compatible NDBM files */
-# endif
-#endif
+# endif /* NDBM */
+#endif /* NIS */
+
+#ifdef NEWDB
+# if DB_VERSION_MAJOR < 2
+static bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
+# endif /* DB_VERSION_MAJOR < 2 */
+# if DB_VERSION_MAJOR == 2
+static bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
+# endif /* DB_VERSION_MAJOR == 2 */
+# if DB_VERSION_MAJOR > 2
+static bool db_map_open __P((MAP *, int, char *, DBTYPE, void **));
+# endif /* DB_VERSION_MAJOR > 2 */
+#endif /* NEWDB */
+static bool extract_canonname __P((char *, char *, char[], int));
+#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
+static bool nisplus_getcanonname __P((char *, int, int *));
+#endif /* NISPLUS */
+#ifdef NIS
+static bool nis_getcanonname __P((char *, int, int *));
+#endif /* NIS */
+#if NETINFO
+static bool ni_getcanonname __P((char *, int, int *));
+#endif /* NETINFO */
+static bool text_getcanonname __P((char *, int, int *));
/*
** MAP.C -- implementations for various map classes.
@@ -83,20 +118,17 @@ static char sccsid[] = "@(#)map.c 8.261 (Berkeley) 2/2/1999";
#ifndef EX_NOTFOUND
# define EX_NOTFOUND EX_NOHOST
-#endif
-
-extern bool aliaswait __P((MAP *, char *, int));
-extern bool extract_canonname __P((char *, char *, char[], int));
+#endif /* ! EX_NOTFOUND */
#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
# define LOCK_ON_OPEN 1 /* we can open/create a locked file */
-#else
+#else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
# define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
-#endif
+#endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
#ifndef O_ACCMODE
# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
-#endif
+#endif /* ! O_ACCMODE */
/*
** MAP_PARSEARGS -- parse config line arguments for database lookup
**
@@ -121,7 +153,12 @@ 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
+ */
map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
+ map->map_spacesub = SpaceSub; /* default value */
for (;;)
{
while (isascii(*p) && isspace(*p))
@@ -204,15 +241,18 @@ map_parseargs(map, ap)
map->map_mflags |= MF_NODEFER;
break;
-#ifdef RESERVED_FOR_SUN
- case 'd':
- map->map_mflags |= MF_DOMAIN_WIDE;
+
+ case 'S':
+ map->map_spacesub = *++p;
break;
- case 's':
- /* info type */
+ case 'D':
+ map->map_mflags |= MF_DEFER;
+ break;
+
+ default:
+ syserr("Illegal option %c map %s", *p, map->map_mname);
break;
-#endif
}
while (*p != '\0' && !(isascii(*p) && isspace(*p)))
p++;
@@ -290,15 +330,15 @@ map_rewrite(map, s, slen, av)
if (tTd(39, 1))
{
- printf("map_rewrite(%.*s), av =", (int)slen, s);
+ dprintf("map_rewrite(%.*s), av =", (int)slen, s);
if (av == NULL)
- printf(" (nullv)");
+ dprintf(" (nullv)");
else
{
for (avp = av; *avp != NULL; avp++)
- printf("\n\t%s", *avp);
+ dprintf("\n\t%s", *avp);
}
- printf("\n");
+ dprintf("\n");
}
/* count expected size of output (can safely overestimate) */
@@ -337,8 +377,11 @@ map_rewrite(map, s, slen, av)
bp = buf;
if (av == NULL)
{
- bcopy(s, bp, slen);
+ memmove(bp, s, slen);
bp += slen;
+
+ /* assert(len > slen); */
+ len -= slen;
}
else
{
@@ -347,6 +390,8 @@ map_rewrite(map, s, slen, av)
if (c != '%')
{
pushc:
+ if (--len <= 0)
+ break;
*bp++ = c;
continue;
}
@@ -357,6 +402,7 @@ map_rewrite(map, s, slen, av)
if (!(isascii(c) && isdigit(c)))
{
*bp++ = '%';
+ --len;
goto pushc;
}
for (avp = av; --c >= '0' && *avp != NULL; avp++)
@@ -365,59 +411,59 @@ map_rewrite(map, s, slen, av)
continue;
/* transliterate argument into output string */
- for (ap = *avp; (c = *ap++) != '\0'; )
+ for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
*bp++ = c;
}
}
- if (map->map_app != NULL)
- strcpy(bp, map->map_app);
+ if (map->map_app != NULL && len > 0)
+ (void) strlcpy(bp, map->map_app, len);
else
*bp = '\0';
if (tTd(39, 1))
- printf("map_rewrite => %s\n", buf);
+ dprintf("map_rewrite => %s\n", buf);
return buf;
}
/*
-** INITMAPS -- initialize for aliasing
+** INITMAPS -- rebuild alias maps
**
** Parameters:
-** rebuild -- if TRUE, this rebuilds the cached versions.
-** e -- current envelope.
+** none.
**
** Returns:
** none.
-**
-** Side Effects:
-** initializes aliases:
-** if alias database: opens the database.
-** if no database available: reads aliases into the symbol table.
*/
void
-initmaps(rebuild, e)
- bool rebuild;
- register ENVELOPE *e;
+initmaps()
{
- extern void map_init __P((STAB *, int));
-
#if XDEBUG
checkfd012("entering initmaps");
-#endif
- CurEnv = e;
-
+#endif /* XDEBUG */
stabapply(map_init, 0);
- stabapply(map_init, rebuild ? 2 : 1);
#if XDEBUG
checkfd012("exiting initmaps");
-#endif
+#endif /* XDEBUG */
}
+ /*
+** MAP_INIT -- rebuild a map
+**
+** Parameters:
+** s -- STAB entry: if map: try to rebuild
+** unused -- unused variable
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** will close already open rebuildable map.
+*/
-void
-map_init(s, pass)
+/* ARGSUSED1 */
+static void
+map_init(s, unused)
register STAB *s;
- int pass;
+ int unused;
{
- bool rebuildable;
register MAP *map;
/* has to be a map */
@@ -429,28 +475,17 @@ map_init(s, pass)
return;
if (tTd(38, 2))
- printf("map_init(%s:%s, %s, %d)\n",
+ 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,
- map->map_file == NULL ? "NULL" : map->map_file,
- pass);
+ map->map_file == NULL ? "NULL" : map->map_file);
- /*
- ** Pass 0 opens all non-rebuildable maps.
- ** Pass 1 opens all rebuildable maps for read.
- ** Pass 2 rebuilds all rebuildable maps.
- */
-
- rebuildable = (bitset(MF_ALIAS, map->map_mflags) &&
- bitset(MCF_REBUILDABLE, map->map_class->map_cflags));
-
- if ((pass == 0 && rebuildable) ||
- ((pass == 1 || pass == 2) && !rebuildable))
+ if (!bitset(MF_ALIAS, map->map_mflags) ||
+ !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
{
if (tTd(38, 3))
- printf("\twrong pass (pass = %d, rebuildable = %d)\n",
- pass, rebuildable);
+ dprintf("\tnot rebuildable\n");
return;
}
@@ -461,16 +496,50 @@ map_init(s, pass)
map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
}
- if (pass == 2)
+ (void) rebuildaliases(map, FALSE);
+ return;
+}
+ /*
+** OPENMAP -- open a map
+**
+** Parameters:
+** map -- map to open (it must not be open).
+**
+** Returns:
+** whether open succeeded.
+**
+*/
+
+bool
+openmap(map)
+ MAP *map;
+{
+ bool restore = FALSE;
+ bool savehold = HoldErrs;
+ bool savequick = QuickAbort;
+ int saveerrors = Errors;
+
+ if (!bitset(MF_VALID, map->map_mflags))
+ return FALSE;
+
+ /* better safe than sorry... */
+ if (bitset(MF_OPEN, map->map_mflags))
+ return TRUE;
+
+ /* Don't send a map open error out via SMTP */
+ if ((OnlyOneError || QuickAbort) &&
+ (OpMode == MD_SMTP || OpMode == MD_DAEMON))
{
- (void) rebuildaliases(map, FALSE);
- return;
+ restore = TRUE;
+ HoldErrs = TRUE;
+ QuickAbort = FALSE;
}
+ errno = 0;
if (map->map_class->map_open(map, O_RDONLY))
{
if (tTd(38, 4))
- printf("\t%s:%s %s: valid\n",
+ 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" :
@@ -483,14 +552,15 @@ map_init(s, pass)
else
{
if (tTd(38, 4))
- printf("\t%s:%s %s: invalid: %s\n",
+ 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" :
map->map_mname,
map->map_file == NULL ? "NULL" :
map->map_file,
- errstring(errno));
+ errno == 0 ? "" : ": ",
+ errno == 0 ? "" : errstring(errno));
if (!bitset(MF_OPTIONAL, map->map_mflags))
{
extern MAPCLASS BogusMapClass;
@@ -499,7 +569,21 @@ map_init(s, pass)
map->map_mflags |= MF_OPEN;
map->map_pid = getpid();
}
+ else
+ {
+ /* don't try again */
+ map->map_mflags &= ~MF_VALID;
+ }
}
+
+ if (restore)
+ {
+ Errors = saveerrors;
+ HoldErrs = savehold;
+ QuickAbort = savequick;
+ }
+
+ return bitset(MF_OPEN, map->map_mflags);
}
/*
** CLOSEMAPS -- close all open maps opened by the current pid.
@@ -514,13 +598,21 @@ map_init(s, pass)
void
closemaps()
{
- extern void map_close __P((STAB *, int));
-
stabapply(map_close, 0);
}
+ /*
+** 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())
+**
+** Returns:
+** none.
+*/
/* ARGSUSED1 */
-void
+static void
map_close(s, unused)
register STAB *s;
int unused;
@@ -529,19 +621,20 @@ map_close(s, unused)
if (s->s_type != ST_MAP)
return;
-
+
map = &s->s_map;
if (!bitset(MF_VALID, map->map_mflags) ||
!bitset(MF_OPEN, map->map_mflags) ||
+ bitset(MF_SHARED, map->map_mflags) ||
map->map_pid != getpid())
return;
-
+
if (tTd(38, 5))
- printf("closemaps: closing %s (%s)\n",
- map->map_mname == NULL ? "NULL" : map->map_mname,
- map->map_file == NULL ? "NULL" : map->map_file);
-
+ dprintf("closemaps: closing %s (%s)\n",
+ map->map_mname == NULL ? "NULL" : map->map_mname,
+ map->map_file == NULL ? "NULL" : map->map_file);
+
map->map_class->map_close(map);
map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
}
@@ -568,7 +661,7 @@ getcanonname(host, hbsize, trymx)
int mapno;
bool found = FALSE;
bool got_tempfail = FALSE;
- auto int stat;
+ auto int status;
char *maptype[MAXMAPSTACK];
short mapreturn[MAXMAPACTIONS];
@@ -578,50 +671,40 @@ getcanonname(host, hbsize, trymx)
int i;
if (tTd(38, 20))
- printf("getcanonname(%s), trying %s\n",
+ dprintf("getcanonname(%s), trying %s\n",
host, maptype[mapno]);
if (strcmp("files", maptype[mapno]) == 0)
{
- extern bool text_getcanonname __P((char *, int, int *));
-
- found = text_getcanonname(host, hbsize, &stat);
+ found = text_getcanonname(host, hbsize, &status);
}
#ifdef NIS
else if (strcmp("nis", maptype[mapno]) == 0)
{
- extern bool nis_getcanonname __P((char *, int, int *));
-
- found = nis_getcanonname(host, hbsize, &stat);
+ found = nis_getcanonname(host, hbsize, &status);
}
-#endif
+#endif /* NIS */
#ifdef NISPLUS
else if (strcmp("nisplus", maptype[mapno]) == 0)
{
- extern bool nisplus_getcanonname __P((char *, int, int *));
-
- found = nisplus_getcanonname(host, hbsize, &stat);
+ found = nisplus_getcanonname(host, hbsize, &status);
}
-#endif
+#endif /* NISPLUS */
#if NAMED_BIND
else if (strcmp("dns", maptype[mapno]) == 0)
{
- extern bool dns_getcanonname __P((char *, int, bool, int *));
-
- found = dns_getcanonname(host, hbsize, trymx, &stat);
+ found = dns_getcanonname(host, hbsize, trymx, &status);
}
-#endif
+#endif /* NAMED_BIND */
#if NETINFO
else if (strcmp("netinfo", maptype[mapno]) == 0)
{
- extern bool ni_getcanonname __P((char *, int, int *));
-
- found = ni_getcanonname(host, hbsize, &stat);
+ found = ni_getcanonname(host, hbsize, &status);
}
-#endif
+#endif /* NETINFO */
else
{
found = FALSE;
- stat = EX_UNAVAILABLE;
+ status = EX_UNAVAILABLE;
}
/*
@@ -637,12 +720,12 @@ getcanonname(host, hbsize, trymx)
break;
/* see if we should continue */
- if (stat == EX_TEMPFAIL)
+ if (status == EX_TEMPFAIL)
{
i = MA_TRYAGAIN;
got_tempfail = TRUE;
}
- else if (stat == EX_NOTFOUND)
+ else if (status == EX_NOTFOUND)
i = MA_NOTFOUND;
else
i = MA_UNAVAIL;
@@ -655,7 +738,7 @@ getcanonname(host, hbsize, trymx)
char *d;
if (tTd(38, 20))
- printf("getcanonname(%s), found\n", host);
+ dprintf("getcanonname(%s), found\n", host);
/*
** If returned name is still single token, compensate
@@ -670,26 +753,24 @@ getcanonname(host, hbsize, trymx)
hbsize > (int) (strlen(host) + strlen(d) + 1))
{
if (host[strlen(host) - 1] != '.')
- strcat(host, ".");
- strcat(host, d);
+ (void) strlcat(host, ".", hbsize);
+ (void) strlcat(host, d, hbsize);
}
else
- {
return FALSE;
- }
}
return TRUE;
}
if (tTd(38, 20))
- printf("getcanonname(%s), failed, stat=%d\n", host, stat);
+ dprintf("getcanonname(%s), failed, status=%d\n", host, status);
#if NAMED_BIND
if (got_tempfail)
h_errno = TRY_AGAIN;
else
h_errno = HOST_NOT_FOUND;
-#endif
+#endif /* NAMED_BIND */
return FALSE;
}
@@ -707,7 +788,7 @@ getcanonname(host, hbsize, trymx)
** FALSE -- otherwise.
*/
-bool
+static bool
extract_canonname(name, line, cbuf, cbuflen)
char *name;
char *line;
@@ -717,7 +798,6 @@ extract_canonname(name, line, cbuf, cbuflen)
int i;
char *p;
bool found = FALSE;
- extern char *get_column __P((char *, int, char, char *, int));
cbuf[0] = '\0';
if (line[0] == '#')
@@ -746,11 +826,11 @@ extract_canonname(name, line, cbuf, cbuflen)
char *domain = macvalue('m', CurEnv);
if (domain != NULL &&
- strlen(domain) + strlen(cbuf) + 1 < cbuflen)
+ strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
{
- p = &cbuf[strlen(cbuf)];
+ p = &cbuf[i];
*p++ = '.';
- strcpy(p, domain);
+ (void) strlcpy(p, domain, cbuflen - i - 1);
}
}
return found;
@@ -771,18 +851,19 @@ ndbm_map_open(map, mode)
int mode;
{
register DBM *dbm;
- struct stat st;
+ int save_errno;
int dfd;
int pfd;
- int sff;
+ long sff;
int ret;
int smode = S_IREAD;
char dirfile[MAXNAME + 1];
char pagfile[MAXNAME + 1];
+ struct stat st;
struct stat std, stp;
if (tTd(38, 2))
- printf("ndbm_map_open(%s, %s, %d)\n",
+ dprintf("ndbm_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
map->map_lockfd = -1;
mode &= O_ACCMODE;
@@ -794,24 +875,26 @@ ndbm_map_open(map, mode)
if (mode == O_RDWR)
{
sff |= SFF_CREAT;
- if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
+ if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
sff |= SFF_NOSLINK;
- if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
+ if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
sff |= SFF_NOHLINK;
smode = S_IWRITE;
}
else
{
- if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
+ if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
sff |= SFF_NOWLINK;
}
- if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
+ if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
sff |= SFF_SAFEDIRPATH;
ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
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) ||
@@ -819,7 +902,6 @@ ndbm_map_open(map, mode)
mode == O_RDONLY)
{
bool impl = bitset(MF_IMPL_NDBM, map->map_mflags);
- extern bool impl_map_open __P((MAP *, int));
/* may be able to rebuild */
map->map_mflags &= ~MF_IMPL_NDBM;
@@ -830,6 +912,8 @@ ndbm_map_open(map, mode)
else
return ndbm_map_open(map, O_RDONLY);
}
+# endif /* !_FFR_REMOVE_AUTOREBUILD */
+
if (ret != 0)
{
char *prob = "unsafe";
@@ -838,7 +922,7 @@ ndbm_map_open(map, mode)
if (ret == ENOENT)
prob = "missing";
if (tTd(38, 2))
- printf("\t%s map file: %d\n", prob, ret);
+ 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);
@@ -847,15 +931,15 @@ ndbm_map_open(map, mode)
if (std.st_mode == ST_MODE_NOFILE)
mode |= O_CREAT|O_EXCL;
-#if LOCK_ON_OPEN
+# if LOCK_ON_OPEN
if (mode == O_RDONLY)
mode |= O_SHLOCK;
else
mode |= O_TRUNC|O_EXLOCK;
-#else
+# else /* LOCK_ON_OPEN */
if ((mode & O_ACCMODE) == O_RDWR)
{
-# if NOFTRUNCATE
+# if NOFTRUNCATE
/*
** Warning: race condition. Try to lock the file as
** quickly as possible after opening it.
@@ -864,7 +948,7 @@ ndbm_map_open(map, mode)
*/
mode |= O_TRUNC;
-# else
+# else /* NOFTRUNCATE */
/*
** This ugly code opens the map without truncating it,
** locks the file, then truncates it. Necessary to
@@ -873,11 +957,11 @@ ndbm_map_open(map, mode)
int dirfd;
int pagfd;
- int sff = SFF_CREAT|SFF_OPENASROOT;
+ long sff = SFF_CREAT|SFF_OPENASROOT;
- if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
+ if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
sff |= SFF_NOSLINK;
- if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
+ if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
sff |= SFF_NOHLINK;
dirfd = safeopen(dirfile, mode, DBMMODE, sff);
@@ -885,8 +969,7 @@ ndbm_map_open(map, mode)
if (dirfd < 0 || pagfd < 0)
{
- int save_errno = errno;
-
+ save_errno = errno;
if (dirfd >= 0)
(void) close(dirfd);
if (pagfd >= 0)
@@ -899,8 +982,7 @@ ndbm_map_open(map, mode)
if (ftruncate(dirfd, (off_t) 0) < 0 ||
ftruncate(pagfd, (off_t) 0) < 0)
{
- int save_errno = errno;
-
+ save_errno = errno;
(void) close(dirfd);
(void) close(pagfd);
errno = save_errno;
@@ -913,8 +995,7 @@ ndbm_map_open(map, mode)
if (std.st_mode == ST_MODE_NOFILE &&
(fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
{
- int save_errno = errno;
-
+ save_errno = errno;
(void) close(dirfd);
(void) close(pagfd);
errno = save_errno;
@@ -925,27 +1006,26 @@ ndbm_map_open(map, mode)
/* have to save the lock for the duration (bletch) */
map->map_lockfd = dirfd;
- close(pagfd);
+ (void) close(pagfd);
/* twiddle bits for dbm_open */
mode &= ~(O_CREAT|O_EXCL);
-# endif
+# endif /* NOFTRUNCATE */
}
-#endif
+# endif /* LOCK_ON_OPEN */
/* open the database */
dbm = dbm_open(map->map_file, mode, DBMMODE);
if (dbm == NULL)
{
- int save_errno = errno;
-
+ save_errno = errno;
if (bitset(MF_ALIAS, map->map_mflags) &&
aliaswait(map, ".pag", FALSE))
return TRUE;
-#if !LOCK_ON_OPEN && !NOFTRUNCATE
+# if !LOCK_ON_OPEN && !NOFTRUNCATE
if (map->map_lockfd >= 0)
- close(map->map_lockfd);
-#endif
+ (void) close(map->map_lockfd);
+# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
errno = save_errno;
if (!bitset(MF_OPTIONAL, map->map_mflags))
syserr("Cannot open DBM database %s", map->map_file);
@@ -957,10 +1037,10 @@ ndbm_map_open(map, mode)
{
/* heuristic: if files are linked, this is actually gdbm */
dbm_close(dbm);
-#if !LOCK_ON_OPEN && !NOFTRUNCATE
+# if !LOCK_ON_OPEN && !NOFTRUNCATE
if (map->map_lockfd >= 0)
- close(map->map_lockfd);
-#endif
+ (void) close(map->map_lockfd);
+# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
errno = 0;
syserr("dbm map \"%s\": cannot support GDBM",
map->map_mname);
@@ -970,13 +1050,12 @@ ndbm_map_open(map, mode)
if (filechanged(dirfile, dfd, &std) ||
filechanged(pagfile, pfd, &stp))
{
- int save_errno = errno;
-
+ save_errno = errno;
dbm_close(dbm);
-#if !LOCK_ON_OPEN && !NOFTRUNCATE
+# if !LOCK_ON_OPEN && !NOFTRUNCATE
if (map->map_lockfd >= 0)
- close(map->map_lockfd);
-#endif
+ (void) close(map->map_lockfd);
+# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
errno = save_errno;
syserr("ndbm_map_open(%s): file changed after open",
map->map_file);
@@ -984,14 +1063,24 @@ ndbm_map_open(map, mode)
}
map->map_db1 = (ARBPTR_T) dbm;
+
+ /*
+ ** Need to set map_mtime before the call to aliaswait()
+ ** as aliaswait() will call map_lookup() which requires
+ ** map_mtime to be set
+ */
+
+ if (fstat(dfd, &st) >= 0)
+ map->map_mtime = st.st_mtime;
+
if (mode == O_RDONLY)
{
-#if LOCK_ON_OPEN
+# if LOCK_ON_OPEN
if (dfd >= 0)
(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
if (pfd >= 0)
(void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
-#endif
+# endif /* LOCK_ON_OPEN */
if (bitset(MF_ALIAS, map->map_mflags) &&
!aliaswait(map, ".pag", TRUE))
return FALSE;
@@ -999,9 +1088,9 @@ ndbm_map_open(map, mode)
else
{
map->map_mflags |= MF_LOCKED;
-#if _FFR_TRUSTED_USER
if (geteuid() == 0 && TrustedUid != 0)
{
+# if HASFCHOWN
if (fchown(dfd, TrustedUid, -1) < 0 ||
fchown(pfd, TrustedUid, -1) < 0)
{
@@ -1013,11 +1102,9 @@ ndbm_map_open(map, mode)
message("050 ownership change on %s failed: %s",
map->map_file, errstring(err));
}
+# endif /* HASFCHOWN */
}
-#endif
}
- if (fstat(dfd, &st) >= 0)
- map->map_mtime = st.st_mtime;
return TRUE;
}
@@ -1039,7 +1126,7 @@ ndbm_map_lookup(map, name, av, statp)
struct stat stbuf;
if (tTd(38, 20))
- printf("ndbm_map_lookup(%s, %s)\n",
+ dprintf("ndbm_map_lookup(%s, %s)\n",
map->map_mname, name);
key.dptr = name;
@@ -1048,7 +1135,7 @@ ndbm_map_lookup(map, name, av, statp)
{
if (key.dsize > sizeof keybuf - 1)
key.dsize = sizeof keybuf - 1;
- bcopy(key.dptr, keybuf, key.dsize);
+ memmove(keybuf, key.dptr, key.dsize);
keybuf[key.dsize] = '\0';
makelower(keybuf);
key.dptr = keybuf;
@@ -1063,6 +1150,8 @@ lockdbm:
int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
: O_RDONLY;
+ if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
+ (void) lockfile(fd, map->map_file, ".dir", LOCK_UN);
map->map_class->map_close(map);
map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
if (map->map_class->map_open(map, omode))
@@ -1126,11 +1215,11 @@ ndbm_map_store(map, lhs, rhs)
{
datum key;
datum data;
- int stat;
+ int status;
char keybuf[MAXNAME + 1];
if (tTd(38, 12))
- printf("ndbm_map_store(%s, %s, %s)\n",
+ dprintf("ndbm_map_store(%s, %s, %s)\n",
map->map_mname, lhs, rhs);
key.dsize = strlen(lhs);
@@ -1139,7 +1228,7 @@ ndbm_map_store(map, lhs, rhs)
{
if (key.dsize > sizeof keybuf - 1)
key.dsize = sizeof keybuf - 1;
- bcopy(key.dptr, keybuf, key.dsize);
+ memmove(keybuf, key.dptr, key.dsize);
keybuf[key.dsize] = '\0';
makelower(keybuf);
key.dptr = keybuf;
@@ -1154,8 +1243,8 @@ ndbm_map_store(map, lhs, rhs)
data.dsize++;
}
- stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
- if (stat > 0)
+ status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
+ if (status > 0)
{
if (!bitset(MF_APPEND, map->map_mflags))
message("050 Warning: duplicate alias name %s", lhs);
@@ -1183,13 +1272,15 @@ ndbm_map_store(map, lhs, rhs)
data.dsize = data.dsize + old.dsize + 1;
data.dptr = buf;
if (tTd(38, 9))
- printf("ndbm_map_store append=%s\n", data.dptr);
+ dprintf("ndbm_map_store append=%s\n",
+ data.dptr);
}
}
- stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE);
+ status = dbm_store((DBM *) map->map_db1,
+ key, data, DBM_REPLACE);
}
- if (stat != 0)
- syserr("readaliases: dbm put (%s)", lhs);
+ if (status != 0)
+ syserr("readaliases: dbm put (%s): %d", lhs, status);
}
@@ -1202,12 +1293,12 @@ ndbm_map_close(map)
register MAP *map;
{
if (tTd(38, 9))
- printf("ndbm_map_close(%s, %s, %lx)\n",
+ dprintf("ndbm_map_close(%s, %s, %lx)\n",
map->map_mname, map->map_file, map->map_mflags);
if (bitset(MF_WRITABLE, map->map_mflags))
{
-#ifdef NDBM_YP_COMPAT
+# ifdef NDBM_YP_COMPAT
bool inclnull;
char buf[MAXHOSTNAMELEN];
@@ -1231,7 +1322,7 @@ ndbm_map_close(map)
if (inclnull)
map->map_mflags |= MF_INCLNULL;
-#endif
+# endif /* NDBM_YP_COMPAT */
/* write out the distinguished alias */
ndbm_map_store(map, "@", "@");
@@ -1239,13 +1330,13 @@ ndbm_map_close(map)
dbm_close((DBM *) map->map_db1);
/* release lock (if needed) */
-#if !LOCK_ON_OPEN
+# if !LOCK_ON_OPEN
if (map->map_lockfd >= 0)
(void) close(map->map_lockfd);
-#endif
+# endif /* !LOCK_ON_OPEN */
}
-#endif
+#endif /* NDBM */
/*
** NEWDB (Hash and BTree) Modules
*/
@@ -1264,43 +1355,44 @@ ndbm_map_close(map)
** be pokey about it. That's hard to do.
*/
-#if DB_VERSION_MAJOR < 2
-extern bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
-#else
-extern bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
-#endif
-
/* these should be K line arguments */
-#if DB_VERSION_MAJOR < 2
-# define db_cachesize cachesize
-# define h_nelem nelem
-# ifndef DB_CACHE_SIZE
-# define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
-# endif
-# ifndef DB_HASH_NELEM
-# define DB_HASH_NELEM 4096 /* (starting) size of hash table */
-# endif
-#endif
+# if DB_VERSION_MAJOR < 2
+# define db_cachesize cachesize
+# define h_nelem nelem
+# ifndef DB_CACHE_SIZE
+# define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
+# endif /* ! DB_CACHE_SIZE */
+# ifndef DB_HASH_NELEM
+# define DB_HASH_NELEM 4096 /* (starting) size of hash table */
+# endif /* ! DB_HASH_NELEM */
+# endif /* DB_VERSION_MAJOR < 2 */
bool
bt_map_open(map, mode)
MAP *map;
int mode;
{
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
BTREEINFO btinfo;
-#else
+# endif /* DB_VERSION_MAJOR < 2 */
+# if DB_VERSION_MAJOR == 2
DB_INFO btinfo;
-#endif
+# endif /* DB_VERSION_MAJOR == 2 */
+# if DB_VERSION_MAJOR > 2
+ void *btinfo = NULL;
+# endif /* DB_VERSION_MAJOR > 2 */
if (tTd(38, 2))
- printf("bt_map_open(%s, %s, %d)\n",
+ dprintf("bt_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
- bzero(&btinfo, sizeof btinfo);
-#ifdef DB_CACHE_SIZE
+# if DB_VERSION_MAJOR < 3
+ memset(&btinfo, '\0', sizeof btinfo);
+# ifdef DB_CACHE_SIZE
btinfo.db_cachesize = DB_CACHE_SIZE;
-#endif
+# endif /* DB_CACHE_SIZE */
+# endif /* DB_VERSION_MAJOR < 3 */
+
return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
}
@@ -1309,53 +1401,64 @@ hash_map_open(map, mode)
MAP *map;
int mode;
{
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
HASHINFO hinfo;
-#else
+# endif /* DB_VERSION_MAJOR < 2 */
+# if DB_VERSION_MAJOR == 2
DB_INFO hinfo;
-#endif
+# endif /* DB_VERSION_MAJOR == 2 */
+# if DB_VERSION_MAJOR > 2
+ void *hinfo = NULL;
+# endif /* DB_VERSION_MAJOR > 2 */
if (tTd(38, 2))
- printf("hash_map_open(%s, %s, %d)\n",
+ dprintf("hash_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
- bzero(&hinfo, sizeof hinfo);
-#ifdef DB_HASH_NELEM
+# if DB_VERSION_MAJOR < 3
+ memset(&hinfo, '\0', sizeof hinfo);
+# ifdef DB_HASH_NELEM
hinfo.h_nelem = DB_HASH_NELEM;
-#endif
-#ifdef DB_CACHE_SIZE
+# endif /* DB_HASH_NELEM */
+# ifdef DB_CACHE_SIZE
hinfo.db_cachesize = DB_CACHE_SIZE;
-#endif
+# endif /* DB_CACHE_SIZE */
+# endif /* DB_VERSION_MAJOR < 3 */
+
return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
}
-bool
+static bool
db_map_open(map, mode, mapclassname, dbtype, openinfo)
MAP *map;
int mode;
char *mapclassname;
DBTYPE dbtype;
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
const void *openinfo;
-#else
+# endif /* DB_VERSION_MAJOR < 2 */
+# if DB_VERSION_MAJOR == 2
DB_INFO *openinfo;
-#endif
+# endif /* DB_VERSION_MAJOR == 2 */
+# if DB_VERSION_MAJOR > 2
+ void **openinfo;
+# endif /* DB_VERSION_MAJOR > 2 */
{
DB *db = NULL;
int i;
int omode;
int smode = S_IREAD;
int fd;
- int sff;
- int saveerrno;
+ long sff;
+ int save_errno;
struct stat st;
char buf[MAXNAME + 1];
/* do initial file and directory checks */
- snprintf(buf, sizeof buf - 3, "%s", map->map_file);
+ (void) strlcpy(buf, map->map_file, sizeof buf - 3);
i = strlen(buf);
if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
- (void) strcat(buf, ".db");
+ (void) strlcat(buf, ".db", sizeof buf);
mode &= O_ACCMODE;
omode = mode;
@@ -1364,20 +1467,22 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
if (mode == O_RDWR)
{
sff |= SFF_CREAT;
- if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
+ if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
sff |= SFF_NOSLINK;
- if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
+ if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
sff |= SFF_NOHLINK;
smode = S_IWRITE;
}
else
{
- if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
+ if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
sff |= SFF_NOWLINK;
}
- if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
+ if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
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) ||
@@ -1385,7 +1490,6 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
mode == O_RDONLY)
{
bool impl = bitset(MF_IMPL_HASH, map->map_mflags);
- extern bool impl_map_open __P((MAP *, int));
/* may be able to rebuild */
map->map_mflags &= ~MF_IMPL_HASH;
@@ -1397,6 +1501,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
return db_map_open(map, O_RDONLY, mapclassname,
dbtype, openinfo);
}
+# endif /* !_FFR_REMOVE_AUTOREBUILD */
if (i != 0)
{
@@ -1406,7 +1511,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
if (i == ENOENT)
prob = "missing";
if (tTd(38, 2))
- printf("\t%s map file: %s\n", prob, errstring(i));
+ dprintf("\t%s map file: %s\n", prob, errstring(i));
errno = i;
if (!bitset(MF_OPTIONAL, map->map_mflags))
syserr("%s map \"%s\": %s map file %s",
@@ -1418,12 +1523,12 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
map->map_lockfd = -1;
-#if LOCK_ON_OPEN
+# if LOCK_ON_OPEN
if (mode == O_RDWR)
omode |= O_TRUNC|O_EXLOCK;
else
omode |= O_SHLOCK;
-#else
+# else /* LOCK_ON_OPEN */
/*
** Pre-lock the file to avoid race conditions. In particular,
** since dbopen returns NULL if the file is zero length, we
@@ -1441,8 +1546,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
/* make sure no baddies slipped in just before the open... */
if (filechanged(buf, fd, &st))
{
- int save_errno = errno;
-
+ save_errno = errno;
(void) close(fd);
errno = save_errno;
syserr("db_map_open(%s): file changed after pre-open", buf);
@@ -1452,8 +1556,7 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
/* if new file, get the "before" bits for later filechanged check */
if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
{
- int save_errno = errno;
-
+ save_errno = errno;
(void) close(fd);
errno = save_errno;
syserr("db_map_open(%s): cannot fstat pre-opened file",
@@ -1469,13 +1572,16 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
if (mode == O_RDWR)
omode |= O_TRUNC;
omode &= ~(O_EXCL|O_CREAT);
-#endif
+# endif /* LOCK_ON_OPEN */
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
-#else
+# else /* DB_VERSION_MAJOR < 2 */
{
int flags = 0;
+# if DB_VERSION_MAJOR > 2
+ int ret;
+# endif /* DB_VERSION_MAJOR > 2 */
if (mode == O_RDONLY)
flags |= DB_RDONLY;
@@ -1484,54 +1590,93 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
if (bitset(O_TRUNC, omode))
flags |= DB_TRUNCATE;
+# if !HASFLOCK && defined(DB_FCNTL_LOCKING)
+ flags |= DB_FCNTL_LOCKING;
+# endif /* !HASFLOCK && defined(DB_FCNTL_LOCKING) */
+
+# if DB_VERSION_MAJOR > 2
+ ret = db_create(&db, NULL, 0);
+# ifdef DB_CACHE_SIZE
+ if (ret == 0 && db != NULL)
+ {
+ ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
+ if (ret != 0)
+ {
+ (void) db->close(db, 0);
+ db = NULL;
+ }
+ }
+# endif /* DB_CACHE_SIZE */
+# ifdef DB_HASH_NELEM
+ if (dbtype == DB_HASH && ret == 0 && db != NULL)
+ {
+ ret = db->set_h_nelem(db, DB_HASH_NELEM);
+ if (ret != 0)
+ {
+ (void) db->close(db, 0);
+ db = NULL;
+ }
+ }
+# endif /* DB_HASH_NELEM */
+ if (ret == 0 && db != NULL)
+ {
+ ret = db->open(db, buf, NULL, dbtype, flags, DBMMODE);
+ if (ret != 0)
+ {
+ (void) db->close(db, 0);
+ db = NULL;
+ }
+ }
+ errno = ret;
+# else /* DB_VERSION_MAJOR > 2 */
errno = db_open(buf, dbtype, flags, DBMMODE,
NULL, openinfo, &db);
+# endif /* DB_VERSION_MAJOR > 2 */
}
-#endif
- saveerrno = errno;
+# endif /* DB_VERSION_MAJOR < 2 */
+ save_errno = errno;
-#if !LOCK_ON_OPEN
+# if !LOCK_ON_OPEN
if (mode == O_RDWR)
map->map_lockfd = fd;
else
(void) close(fd);
-#endif
+# endif /* !LOCK_ON_OPEN */
if (db == NULL)
{
if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
aliaswait(map, ".db", FALSE))
return TRUE;
-#if !LOCK_ON_OPEN
+# if !LOCK_ON_OPEN
if (map->map_lockfd >= 0)
(void) close(map->map_lockfd);
-#endif
- errno = saveerrno;
+# endif /* !LOCK_ON_OPEN */
+ errno = save_errno;
if (!bitset(MF_OPTIONAL, map->map_mflags))
syserr("Cannot open %s database %s",
mapclassname, buf);
return FALSE;
}
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
fd = db->fd(db);
-#else
+# else /* DB_VERSION_MAJOR < 2 */
fd = -1;
errno = db->fd(db, &fd);
-#endif
+# endif /* DB_VERSION_MAJOR < 2 */
if (filechanged(buf, fd, &st))
{
- int save_errno = errno;
-
-#if DB_VERSION_MAJOR < 2
- db->close(db);
-#else
+ save_errno = errno;
+# if DB_VERSION_MAJOR < 2
+ (void) db->close(db);
+# else /* DB_VERSION_MAJOR < 2 */
errno = db->close(db, 0);
-#endif
-#if !LOCK_ON_OPEN
+# endif /* DB_VERSION_MAJOR < 2 */
+# if !LOCK_ON_OPEN
if (map->map_lockfd >= 0)
- close(map->map_lockfd);
-#endif
+ (void) close(map->map_lockfd);
+# endif /* !LOCK_ON_OPEN */
errno = save_errno;
syserr("db_map_open(%s): file changed after open", buf);
return FALSE;
@@ -1539,20 +1684,20 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
if (mode == O_RDWR)
map->map_mflags |= MF_LOCKED;
-#if LOCK_ON_OPEN
+# if LOCK_ON_OPEN
if (fd >= 0 && mode == O_RDONLY)
{
(void) lockfile(fd, buf, NULL, LOCK_UN);
}
-#endif
+# endif /* LOCK_ON_OPEN */
/* try to make sure that at least the database header is on disk */
if (mode == O_RDWR)
{
(void) db->sync(db, 0);
-#if _FFR_TRUSTED_USER
if (geteuid() == 0 && TrustedUid != 0)
{
+# if HASFCHOWN
if (fchown(fd, TrustedUid, -1) < 0)
{
int err = errno;
@@ -1563,14 +1708,21 @@ db_map_open(map, mode, mapclassname, dbtype, openinfo)
message("050 ownership change on %s failed: %s",
buf, errstring(err));
}
+# endif /* HASFCHOWN */
}
-#endif
}
+ map->map_db2 = (ARBPTR_T) db;
+
+ /*
+ ** Need to set map_mtime before the call to aliaswait()
+ ** as aliaswait() will call map_lookup() which requires
+ ** map_mtime to be set
+ */
+
if (fd >= 0 && fstat(fd, &st) >= 0)
map->map_mtime = st.st_mtime;
- map->map_db2 = (ARBPTR_T) db;
if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
!aliaswait(map, ".db", TRUE))
return FALSE;
@@ -1593,24 +1745,23 @@ db_map_lookup(map, name, av, statp)
register DB *db = (DB *) map->map_db2;
int i;
int st;
- int saveerrno;
+ int save_errno;
int fd;
struct stat stbuf;
char keybuf[MAXNAME + 1];
char buf[MAXNAME + 1];
- bzero(&key, sizeof key);
- bzero(&val, sizeof val);
+ memset(&key, '\0', sizeof key);
+ memset(&val, '\0', sizeof val);
if (tTd(38, 20))
- printf("db_map_lookup(%s, %s)\n",
+ dprintf("db_map_lookup(%s, %s)\n",
map->map_mname, name);
i = strlen(map->map_file);
if (i > MAXNAME)
i = MAXNAME;
- strncpy(buf, map->map_file, i);
- buf[i] = '\0';
+ (void) strlcpy(buf, map->map_file, i + 1);
if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
buf[i - 3] = '\0';
@@ -1618,17 +1769,17 @@ db_map_lookup(map, name, av, statp)
if (key.size > sizeof keybuf - 1)
key.size = sizeof keybuf - 1;
key.data = keybuf;
- bcopy(name, keybuf, key.size);
+ memmove(keybuf, name, key.size);
keybuf[key.size] = '\0';
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
makelower(keybuf);
lockdb:
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
fd = db->fd(db);
-#else
+# else /* DB_VERSION_MAJOR < 2 */
fd = -1;
errno = db->fd(db, &fd);
-#endif
+# endif /* DB_VERSION_MAJOR < 2 */
if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
(void) lockfile(fd, buf, ".db", LOCK_SH);
if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
@@ -1637,6 +1788,8 @@ db_map_lookup(map, name, av, statp)
int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
: O_RDONLY;
+ if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
+ (void) lockfile(fd, buf, ".db", LOCK_UN);
map->map_class->map_close(map);
map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
if (map->map_class->map_open(map, omode))
@@ -1668,9 +1821,9 @@ db_map_lookup(map, name, av, statp)
st = 1;
if (bitset(MF_TRY0NULL, map->map_mflags))
{
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
st = db->get(db, &key, &val, 0);
-#else
+# else /* DB_VERSION_MAJOR < 2 */
errno = db->get(db, NULL, &key, &val, 0);
switch (errno)
{
@@ -1687,16 +1840,16 @@ db_map_lookup(map, name, av, statp)
st = -1;
break;
}
-#endif
+# endif /* DB_VERSION_MAJOR < 2 */
if (st == 0)
map->map_mflags &= ~MF_TRY1NULL;
}
if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
{
key.size++;
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
st = db->get(db, &key, &val, 0);
-#else
+# else /* DB_VERSION_MAJOR < 2 */
errno = db->get(db, NULL, &key, &val, 0);
switch (errno)
{
@@ -1713,16 +1866,16 @@ db_map_lookup(map, name, av, statp)
st = -1;
break;
}
-#endif
+# endif /* DB_VERSION_MAJOR < 2 */
if (st == 0)
map->map_mflags &= ~MF_TRY0NULL;
}
- saveerrno = errno;
+ save_errno = errno;
if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
(void) lockfile(fd, buf, ".db", LOCK_UN);
if (st != 0)
{
- errno = saveerrno;
+ errno = save_errno;
if (st < 0)
syserr("db_map_lookup: get (%s)", name);
return NULL;
@@ -1744,17 +1897,17 @@ db_map_store(map, lhs, rhs)
char *lhs;
char *rhs;
{
- int stat;
+ int status;
DBT key;
DBT data;
register DB *db = map->map_db2;
char keybuf[MAXNAME + 1];
- bzero(&key, sizeof key);
- bzero(&data, sizeof data);
+ memset(&key, '\0', sizeof key);
+ memset(&data, '\0', sizeof data);
if (tTd(38, 12))
- printf("db_map_store(%s, %s, %s)\n",
+ dprintf("db_map_store(%s, %s, %s)\n",
map->map_mname, lhs, rhs);
key.size = strlen(lhs);
@@ -1763,7 +1916,7 @@ db_map_store(map, lhs, rhs)
{
if (key.size > sizeof keybuf - 1)
key.size = sizeof keybuf - 1;
- bcopy(key.data, keybuf, key.size);
+ memmove(keybuf, key.data, key.size);
keybuf[key.size] = '\0';
makelower(keybuf);
key.data = keybuf;
@@ -1778,26 +1931,26 @@ db_map_store(map, lhs, rhs)
data.size++;
}
-#if DB_VERSION_MAJOR < 2
- stat = db->put(db, &key, &data, R_NOOVERWRITE);
-#else
+# if DB_VERSION_MAJOR < 2
+ status = db->put(db, &key, &data, R_NOOVERWRITE);
+# else /* DB_VERSION_MAJOR < 2 */
errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
switch (errno)
{
case DB_KEYEXIST:
- stat = 1;
+ status = 1;
break;
case 0:
- stat = 0;
+ status = 0;
break;
default:
- stat = -1;
+ status = -1;
break;
}
-#endif
- if (stat > 0)
+# endif /* DB_VERSION_MAJOR < 2 */
+ if (status > 0)
{
if (!bitset(MF_APPEND, map->map_mflags))
message("050 Warning: duplicate alias name %s", lhs);
@@ -1807,14 +1960,14 @@ db_map_store(map, lhs, rhs)
static int bufsiz = 0;
DBT old;
- bzero(&old, sizeof old);
+ memset(&old, '\0', sizeof old);
- old.data = db_map_lookup(map, key.data,
- (char **)NULL, &stat);
+ old.data = db_map_lookup(map, key.data,
+ (char **)NULL, &status);
if (old.data != NULL)
{
old.size = strlen(old.data);
- if (data.size + old.size + 2 > bufsiz)
+ if (data.size + old.size + 2 > (size_t)bufsiz)
{
if (buf != NULL)
(void) free(buf);
@@ -1826,17 +1979,17 @@ db_map_store(map, lhs, rhs)
data.size = data.size + old.size + 1;
data.data = buf;
if (tTd(38, 9))
- printf("db_map_store append=%s\n",
- (char *) data.data);
+ dprintf("db_map_store append=%s\n",
+ (char *) data.data);
}
}
-#if DB_VERSION_MAJOR < 2
- stat = db->put(db, &key, &data, 0);
-#else
- stat = errno = db->put(db, NULL, &key, &data, 0);
-#endif
+# if DB_VERSION_MAJOR < 2
+ status = db->put(db, &key, &data, 0);
+# else /* DB_VERSION_MAJOR < 2 */
+ status = errno = db->put(db, NULL, &key, &data, 0);
+# endif /* DB_VERSION_MAJOR < 2 */
}
- if (stat != 0)
+ if (status != 0)
syserr("readaliases: db put (%s)", lhs);
}
@@ -1852,7 +2005,7 @@ db_map_close(map)
register DB *db = map->map_db2;
if (tTd(38, 9))
- printf("db_map_close(%s, %s, %lx)\n",
+ dprintf("db_map_close(%s, %s, %lx)\n",
map->map_mname, map->map_file, map->map_mflags);
if (bitset(MF_WRITABLE, map->map_mflags))
@@ -1863,14 +2016,14 @@ db_map_close(map)
(void) db->sync(db, 0);
-#if !LOCK_ON_OPEN
+# if !LOCK_ON_OPEN
if (map->map_lockfd >= 0)
(void) close(map->map_lockfd);
-#endif
+# endif /* !LOCK_ON_OPEN */
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
if (db->close(db) != 0)
-#else
+# else /* DB_VERSION_MAJOR < 2 */
/*
** Berkeley DB can use internal shared memory
** locking for its memory pool. Closing a map
@@ -1895,21 +2048,20 @@ db_map_close(map)
}
if ((errno = db->close(db, 0)) != 0)
-#endif
+# endif /* DB_VERSION_MAJOR < 2 */
syserr("db_map_close(%s, %s, %lx): db close failure",
map->map_mname, map->map_file, map->map_mflags);
}
-
-#endif
+#endif /* NEWDB */
/*
** NIS Modules
*/
-# ifdef NIS
+#ifdef NIS
# ifndef YPERR_BUSY
# define YPERR_BUSY 16
-# endif
+# endif /* ! YPERR_BUSY */
/*
** NIS_MAP_OPEN -- open DBM map
@@ -1926,22 +2078,22 @@ nis_map_open(map, mode)
auto int vsize;
if (tTd(38, 2))
- printf("nis_map_open(%s, %s, %d)\n",
+ 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
+# ifdef ENOSYS
errno = ENOSYS;
-#else
-# ifdef EFTYPE
+# else /* ENOSYS */
+# ifdef EFTYPE
errno = EFTYPE;
-# else
+# else /* EFTYPE */
errno = ENXIO;
-# endif
-#endif
+# endif /* EFTYPE */
+# endif /* ENOSYS */
return FALSE;
}
@@ -1962,18 +2114,22 @@ nis_map_open(map, mode)
if (yperr != 0)
{
if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("421 NIS map %s specified, but NIS not running",
- map->map_file);
+ syserr("421 4.3.5 NIS map %s specified, but NIS not running",
+ map->map_file);
return FALSE;
}
}
/* check to see if this map actually exists */
+ vp = NULL;
yperr = yp_match(map->map_domain, map->map_file, "@", 1,
&vp, &vsize);
if (tTd(38, 10))
- printf("nis_map_open: yp_match(@, %s, %s) => %s\n",
+ dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
map->map_domain, map->map_file, yperr_string(yperr));
+ if (vp != NULL)
+ free(vp);
+
if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
{
/*
@@ -1983,16 +2139,16 @@ nis_map_open(map, mode)
** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
*/
-#if 0
+# if 0
if (!bitset(MF_ALIAS, map->map_mflags) ||
aliaswait(map, NULL, TRUE))
-#endif
+# endif /* 0 */
return TRUE;
}
if (!bitset(MF_OPTIONAL, map->map_mflags))
{
- syserr("421 Cannot bind to map %s in domain %s: %s",
+ syserr("421 4.0.0 Cannot bind to map %s in domain %s: %s",
map->map_file, map->map_domain, yperr_string(yperr));
}
@@ -2019,17 +2175,18 @@ nis_map_lookup(map, name, av, statp)
char keybuf[MAXNAME + 1];
if (tTd(38, 20))
- printf("nis_map_lookup(%s, %s)\n",
+ dprintf("nis_map_lookup(%s, %s)\n",
map->map_mname, name);
buflen = strlen(name);
if (buflen > sizeof keybuf - 1)
buflen = sizeof keybuf - 1;
- bcopy(name, keybuf, buflen);
+ memmove(keybuf, name, buflen);
keybuf[buflen] = '\0';
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
makelower(keybuf);
yperr = YPERR_KEY;
+ vp = NULL;
if (bitset(MF_TRY0NULL, map->map_mflags))
{
yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
@@ -2039,6 +2196,11 @@ nis_map_lookup(map, name, av, statp)
}
if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
{
+ if (vp != NULL)
+ {
+ free(vp);
+ vp = NULL;
+ }
buflen++;
yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
&vp, &vsize);
@@ -2049,12 +2211,21 @@ nis_map_lookup(map, name, av, statp)
{
if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
map->map_mflags &= ~(MF_VALID|MF_OPEN);
+ if (vp != NULL)
+ free(vp);
return NULL;
}
if (bitset(MF_MATCHONLY, map->map_mflags))
return map_rewrite(map, name, strlen(name), NULL);
else
- return map_rewrite(map, vp, vsize, av);
+ {
+ char *ret;
+
+ ret = map_rewrite(map, vp, vsize, av);
+ if (vp != NULL)
+ free(vp);
+ return ret;
+ }
}
@@ -2062,7 +2233,7 @@ nis_map_lookup(map, name, av, statp)
** NIS_GETCANONNAME -- look up canonical name in NIS
*/
-bool
+static bool
nis_getcanonname(name, hbsize, statp)
char *name;
int hbsize;
@@ -2080,21 +2251,21 @@ nis_getcanonname(name, hbsize, statp)
char nbuf[MAXNAME + 1];
if (tTd(38, 20))
- printf("nis_getcanonname(%s)\n", name);
+ dprintf("nis_getcanonname(%s)\n", name);
- if (strlen(name) >= sizeof nbuf)
+ if (strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
{
*statp = EX_UNAVAILABLE;
return FALSE;
}
- (void) strcpy(nbuf, name);
shorten_hostname(nbuf);
keylen = strlen(nbuf);
if (yp_domain == NULL)
- yp_get_default_domain(&yp_domain);
+ (void) yp_get_default_domain(&yp_domain);
makelower(nbuf);
yperr = YPERR_KEY;
+ vp = NULL;
if (try0null)
{
yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
@@ -2104,6 +2275,11 @@ nis_getcanonname(name, hbsize, statp)
}
if (yperr == YPERR_KEY && try1null)
{
+ if (vp != NULL)
+ {
+ free(vp);
+ vp = NULL;
+ }
keylen++;
yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
&vp, &vsize);
@@ -2118,14 +2294,14 @@ nis_getcanonname(name, hbsize, statp)
*statp = EX_TEMPFAIL;
else
*statp = EX_UNAVAILABLE;
+ if (vp != NULL)
+ free(vp);
return FALSE;
}
- if (vsize >= sizeof host_record)
- vsize = sizeof host_record - 1;
- strncpy(host_record, vp, vsize);
- host_record[vsize] = '\0';
+ (void) strlcpy(host_record, vp, sizeof host_record);
+ free(vp);
if (tTd(38, 44))
- printf("got record `%s'\n", host_record);
+ dprintf("got record `%s'\n", host_record);
if (!extract_canonname(nbuf, host_record, cbuf, sizeof cbuf))
{
/* this should not happen, but.... */
@@ -2137,12 +2313,12 @@ nis_getcanonname(name, hbsize, statp)
*statp = EX_UNAVAILABLE;
return FALSE;
}
- strcpy(name, cbuf);
+ (void) strlcpy(name, cbuf, hbsize);
*statp = EX_OK;
return TRUE;
}
-#endif
+#endif /* NIS */
/*
** NISPLUS Modules
**
@@ -2151,15 +2327,15 @@ nis_getcanonname(name, hbsize, statp)
#ifdef NISPLUS
-#undef NIS /* symbol conflict in nis.h */
-#undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
-#include <rpcsvc/nis.h>
-#include <rpcsvc/nislib.h>
+# undef NIS /* symbol conflict in nis.h */
+# undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
+# include <rpcsvc/nis.h>
+# include <rpcsvc/nislib.h>
-#define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
-#define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
-#define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
-#define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
+# define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
+# define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
+# define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
+# define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
/*
** NISPLUS_MAP_OPEN -- open nisplus table
@@ -2175,7 +2351,7 @@ nisplus_map_open(map, mode)
char qbuf[MAXLINE + NIS_MAXNAMELEN];
if (tTd(38, 2))
- printf("nisplus_map_open(%s, %s, %d)\n",
+ dprintf("nisplus_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
mode &= O_ACCMODE;
@@ -2191,12 +2367,10 @@ nisplus_map_open(map, mode)
if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
{
/* set default NISPLUS Domain to $m */
- extern char *nisplus_default_domain __P((void));
-
map->map_domain = newstr(nisplus_default_domain());
if (tTd(38, 2))
- printf("nisplus_map_open(%s): using domain %s\n",
- map->map_file, map->map_domain);
+ dprintf("nisplus_map_open(%s): using domain %s\n",
+ map->map_file, map->map_domain);
}
if (!PARTIAL_NAME(map->map_file))
{
@@ -2232,12 +2406,12 @@ nisplus_map_open(map, mode)
break;
default: /* all other nisplus errors */
-#if 0
+# if 0
if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("421 Cannot find table %s.%s: %s",
+ syserr("421 4.0.0 Cannot find table %s.%s: %s",
map->map_file, map->map_domain,
nis_sperrno(res->status));
-#endif
+# endif /* 0 */
errno = EAGAIN;
return FALSE;
}
@@ -2247,13 +2421,13 @@ nisplus_map_open(map, mode)
(NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
{
if (tTd(38, 10))
- printf("nisplus_map_open: %s is not a table\n", qbuf);
-#if 0
+ dprintf("nisplus_map_open: %s is not a table\n", qbuf);
+# if 0
if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("421 %s.%s: %s is not a table",
+ syserr("421 4.0.0 %s.%s: %s is not a table",
map->map_file, map->map_domain,
nis_sperrno(res->status));
-#endif
+# endif /* 0 */
errno = EBADF;
return FALSE;
}
@@ -2264,15 +2438,15 @@ 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)))
+ if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
break;
}
if (i == max_col)
{
if (tTd(38, 2))
- printf("nisplus_map_open(%s): can not find key column %s\n",
+ dprintf("nisplus_map_open(%s): can not find key column %s\n",
map->map_file, map->map_keycolnm);
errno = ENOENT;
return FALSE;
@@ -2285,7 +2459,7 @@ nisplus_map_open(map, mode)
return TRUE;
}
- for (i=0; i< max_col; i++)
+ for (i = 0; i< max_col; i++)
{
if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
{
@@ -2295,8 +2469,8 @@ nisplus_map_open(map, mode)
}
if (tTd(38, 2))
- printf("nisplus_map_open(%s): can not find column %s\n",
- map->map_file, map->map_keycolnm);
+ dprintf("nisplus_map_open(%s): can not find column %s\n",
+ map->map_file, map->map_keycolnm);
errno = ENOENT;
return FALSE;
}
@@ -2322,7 +2496,7 @@ nisplus_map_lookup(map, name, av, statp)
nis_result *result;
if (tTd(38, 20))
- printf("nisplus_map_lookup(%s, %s)\n",
+ dprintf("nisplus_map_lookup(%s, %s)\n",
map->map_mname, name);
if (!bitset(MF_OPEN, map->map_mflags))
@@ -2364,7 +2538,7 @@ nisplus_map_lookup(map, name, av, statp)
/* double the quote */
*skp++ = '"';
skleft--;
- /* fall through... */
+ /* FALLTHROUGH */
default:
*skp++ = *p;
@@ -2386,7 +2560,7 @@ nisplus_map_lookup(map, name, av, statp)
map->map_keycolnm, search_key, map->map_file);
if (tTd(38, 20))
- printf("qbuf=%s\n", qbuf);
+ dprintf("qbuf=%s\n", qbuf);
result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
if (result->status == NIS_SUCCESS)
{
@@ -2397,12 +2571,12 @@ nisplus_map_lookup(map, name, av, statp)
{
if (LogLevel > 10)
sm_syslog(LOG_WARNING, CurEnv->e_id,
- "%s: lookup error, expected 1 entry, got %d",
- map->map_file, count);
+ "%s: lookup error, expected 1 entry, got %d",
+ map->map_file, count);
/* ignore second entry */
if (tTd(38, 20))
- printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
+ dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
name, count);
}
@@ -2412,7 +2586,7 @@ nisplus_map_lookup(map, name, av, statp)
p = "";
vsize = strlen(p);
if (tTd(38, 20))
- printf("nisplus_map_lookup(%s), found %s\n",
+ 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);
@@ -2435,7 +2609,7 @@ nisplus_map_lookup(map, name, av, statp)
}
}
if (tTd(38, 20))
- printf("nisplus_map_lookup(%s), failed\n", name);
+ dprintf("nisplus_map_lookup(%s), failed\n", name);
nis_freeresult(result);
return NULL;
}
@@ -2446,7 +2620,7 @@ nisplus_map_lookup(map, name, av, statp)
** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
*/
-bool
+static bool
nisplus_getcanonname(name, hbsize, statp)
char *name;
int hbsize;
@@ -2464,7 +2638,7 @@ nisplus_getcanonname(name, hbsize, statp)
*statp = EX_UNAVAILABLE;
return FALSE;
}
- (void) strcpy(nbuf, name);
+ (void) strlcpy(nbuf, name, sizeof nbuf);
shorten_hostname(nbuf);
p = strchr(nbuf, '.');
@@ -2487,8 +2661,8 @@ nisplus_getcanonname(name, hbsize, statp)
}
if (tTd(38, 20))
- printf("\nnisplus_getcanoname(%s), qbuf=%s\n",
- name, qbuf);
+ dprintf("\nnisplus_getcanoname(%s), qbuf=%s\n",
+ name, qbuf);
result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
NULL, NULL);
@@ -2502,24 +2676,24 @@ nisplus_getcanonname(name, hbsize, statp)
{
if (LogLevel > 10)
sm_syslog(LOG_WARNING, CurEnv->e_id,
- "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
- count);
+ "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
+ count);
/* ignore second entry */
if (tTd(38, 20))
- printf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n",
+ dprintf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n",
name, count);
}
if (tTd(38, 20))
- printf("nisplus_getcanoname(%s), found in directory \"%s\"\n",
- name, (NIS_RES_OBJECT(result))->zo_domain);
+ 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))
- printf("nisplus_getcanonname(%s), found %s\n",
+ dprintf("nisplus_getcanonname(%s), found %s\n",
name, vp);
if (strchr(vp, '.') != NULL)
{
@@ -2534,7 +2708,7 @@ nisplus_getcanonname(name, hbsize, statp)
if (hbsize > vsize + (int) strlen(domain) + 1)
{
if (domain[0] == '\0')
- strcpy(name, vp);
+ (void) strlcpy(name, vp, hbsize);
else
snprintf(name, hbsize, "%s.%s", vp, domain);
*statp = EX_OK;
@@ -2554,13 +2728,12 @@ nisplus_getcanonname(name, hbsize, statp)
*statp = EX_UNAVAILABLE;
}
if (tTd(38, 20))
- printf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
+ dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
name, result->status, *statp);
nis_freeresult(result);
return FALSE;
}
-
char *
nisplus_default_domain()
{
@@ -2568,7 +2741,7 @@ nisplus_default_domain()
char *p;
if (default_domain[0] != '\0')
- return(default_domain);
+ return default_domain;
p = nis_local_directory();
snprintf(default_domain, sizeof default_domain, "%s", p);
@@ -2578,191 +2751,314 @@ nisplus_default_domain()
#endif /* NISPLUS */
/*
** LDAP Modules
-**
-** Contributed by Booker C. Bense <bbense@networking.stanford.edu>.
-** Get your support from him.
*/
-#ifdef LDAPMAP
+/*
+** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
+*/
+
+#if defined(LDAPMAP) || defined(PH_MAP)
+
+# ifdef PH_MAP
+# define ph_map_dequote ldapmap_dequote
+# endif /* PH_MAP */
+
+char *
+ldapmap_dequote(str)
+ char *str;
+{
+ char *p;
+ char *start;
+
+ if (str == NULL)
+ return NULL;
+
+ p = str;
+ if (*p == '"')
+ {
+ /* Should probably swallow initial whitespace here */
+ start = ++p;
+ }
+ else
+ return str;
+ while (*p != '"' && *p != '\0')
+ p++;
+ if (*p != '\0')
+ *p = '\0';
+ return start;
+}
+#endif /* defined(LDAPMAP) || defined(PH_MAP) */
-# undef NEEDGETOPT /* used for something else in LDAP */
+#ifdef LDAPMAP
-# include <lber.h>
-# include <ldap.h>
-# include "ldap_map.h"
+LDAPMAP_STRUCT *LDAPDefaults = NULL;
/*
-** LDAP_MAP_OPEN -- open LDAP map
+** LDAPMAP_OPEN -- open LDAP map
**
-** Since LDAP is TCP-based there is not much we can or should do
-** here. It might be a good idea to attempt an open/close here.
+** Connect to the LDAP server. Re-use existing connections since a
+** single server connection to a host (with the same host, port,
+** bind DN, and secret) can answer queries for multiple maps.
*/
bool
-ldap_map_open(map, mode)
+ldapmap_open(map, mode)
MAP *map;
int mode;
{
+ LDAPMAP_STRUCT *lmap;
+ STAB *s;
+
if (tTd(38, 2))
- printf("ldap_map_open(%s, %d)\n", map->map_mname, mode);
+ dprintf("ldapmap_open(%s, %d)\n", map->map_mname, mode);
mode &= O_ACCMODE;
+
+ /* sendmail doesn't have the ability to write to LDAP (yet) */
if (mode != O_RDONLY)
{
/* issue a pseudo-error message */
-#ifdef ENOSYS
+# ifdef ENOSYS
errno = ENOSYS;
-#else
-# ifdef EFTYPE
+# else /* ENOSYS */
+# ifdef EFTYPE
errno = EFTYPE;
-# else
+# else /* EFTYPE */
errno = ENXIO;
-# endif
-#endif
+# 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 = ',';
+
+ lmap = (LDAPMAP_STRUCT *) map->map_db1;
+
+ s = ldapmap_findconn(lmap);
+ if (s->s_ldap != NULL)
+ {
+ /* Already have a connection open to this LDAP server */
+ lmap->ldap_ld = s->s_ldap;
+ map->map_mflags |= MF_SHARED;
+ return TRUE;
+ }
+
+ /* No connection yet, connect */
+ if (!ldapmap_start(map))
+ return FALSE;
+
+ /* Save connection for reuse */
+ s->s_ldap = lmap->ldap_ld;
return TRUE;
}
-
/*
-** LDAP_MAP_START -- actually open LDAP map
+** LDAPMAP_START -- actually connect to an LDAP server
**
-** Caching should be investigated.
+** 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 void
-ldaptimeout(sig_no)
- int sig_no;
-{
- longjmp(LDAPTimeout, 1);
-}
-
-bool
-ldap_map_start(map)
+static bool
+ldapmap_start(map)
MAP *map;
{
- LDAP_MAP_STRUCT *lmap;
- LDAP *ld;
+ register int bind_result;
+ int save_errno;
register EVENT *ev = NULL;
+ LDAPMAP_STRUCT *lmap;
+ LDAP *ld;
if (tTd(38, 2))
- printf("ldap_map_start(%s)\n", map->map_mname);
+ dprintf("ldapmap_start(%s)\n", map->map_mname);
- lmap = (LDAP_MAP_STRUCT *) map->map_db1;
+ lmap = (LDAPMAP_STRUCT *) map->map_db1;
if (tTd(38,9))
- printf("ldap_open(%s, %d)\n", lmap->ldaphost, lmap->ldapport);
+ dprintf("ldapmap_start(%s, %d)\n",
+ lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host,
+ lmap->ldap_port);
- /* Need to set an alarm here, ldap_open is hopelessly broken. */
+# if USE_LDAP_INIT
+ ld = ldap_init(lmap->ldap_host, lmap->ldap_port);
+# 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->timeout.tv_sec != 0)
+ if (lmap->ldap_timeout.tv_sec != 0)
{
if (setjmp(LDAPTimeout) != 0)
{
if (LogLevel > 1)
sm_syslog(LOG_NOTICE, CurEnv->e_id,
- "timeout waiting for ldap_open to %.100s",
- lmap->ldaphost);
- return (FALSE);
+ "timeout conning to LDAP server %.100s",
+ lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host);
+ return FALSE;
}
- ev = setevent(lmap->timeout.tv_sec, ldaptimeout, 0);
+ ev = setevent(lmap->ldap_timeout.tv_sec, ldaptimeout, 0);
}
-#ifdef USE_LDAP_INIT
- ld = ldap_init(lmap->ldaphost,lmap->ldapport);
-#else
- ld = ldap_open(lmap->ldaphost,lmap->ldapport);
-#endif
+ ld = ldap_open(lmap->ldap_host, lmap->ldap_port);
+ save_errno = errno;
- /* clear the event if it has not sprung */
- if (lmap->timeout.tv_sec != 0)
+ /* 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))
{
- syserr("%sldapopen failed to %s in map %s",
- bitset(MF_NODEFER, map->map_mflags) ? "" : "421 ",
- lmap->ldaphost, map->map_mname);
+ if (bitset(MF_NODEFER, map->map_mflags))
+ syserr("%s failed to %s in map %s",
+# if USE_LDAP_INIT
+ "ldap_init",
+# else /* USE_LDAP_INIT */
+ "ldap_open",
+# endif /* USE_LDAP_INIT */
+ lmap->ldap_host == NULL ? "localhost"
+ : lmap->ldap_host,
+ map->map_mname);
+ else
+ syserr("421 4.0.0 %s failed to %s in map %s",
+# if USE_LDAP_INIT
+ "ldap_init",
+# else /* USE_LDAP_INIT */
+ "ldap_open",
+# endif /* USE_LDAP_INIT */
+ lmap->ldap_host == NULL ? "localhost"
+ : lmap->ldap_host,
+ map->map_mname);
}
return FALSE;
}
-#ifdef USE_LDAP_SET_OPTION
- ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->deref);
- ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->timelimit);
- ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->sizelimit);
- ldap_set_option(ld, LDAP_OPT_REFERRALS,
- bitset(LDAP_OPT_REFERRALS, lmap->ldap_options) ?
- LDAP_OPT_ON : LDAP_OPT_OFF);
-#else
- /* From here on in we can use ldap internal timelimits */
- ld->ld_deref = lmap->deref;
- ld->ld_timelimit = lmap->timelimit;
- ld->ld_sizelimit = lmap->sizelimit;
- ld->ld_options = lmap->ldap_options;
-#endif
+ ldapmap_setopts(ld, lmap);
-#ifdef USE_LDAP_INIT
- /* ld needs to be cast into the map struct */
- lmap->ld = ld;
- return TRUE;
-#else
- if (ldap_bind_s(ld, lmap->binddn,lmap->passwd,lmap->method) != LDAP_SUCCESS)
+# 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 (!bitset(MF_OPTIONAL, map->map_mflags))
+ if (setjmp(LDAPTimeout) != 0)
{
- syserr("421 Cannot bind to map %s in ldap server %s",
- map->map_mname, lmap->ldaphost);
+ 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);
}
- else
- {
- /* We need to cast ld into the map structure */
- lmap->ld = ld;
- return TRUE;
- }
+# endif /* USE_LDAP_INIT */
- return FALSE;
-#endif
-}
+# 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 */
-/*
-** LDAP_MAP_STOP -- close the ldap connection
-*/
+ bind_result = ldap_bind_s(ld, lmap->ldap_binddn,
+ lmap->ldap_secret, lmap->ldap_method);
-void
-ldap_map_stop(map)
- MAP *map;
-{
- LDAP_MAP_STRUCT *lmap;
+# if USE_LDAP_INIT
+ /* clear the event if it has not sprung */
+ if (ev != NULL)
+ clrevent(ev);
+# endif /* USE_LDAP_INIT */
- lmap = (LDAP_MAP_STRUCT *) map->map_db1;
- if (lmap->ld != NULL)
+ if (bind_result != LDAP_SUCCESS)
{
- ldap_unbind(lmap->ld);
- lmap->ld = NULL;
+ errno = bind_result + E_LDAPBASE;
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ {
+ syserr("421 4.0.0 Cannot bind to map %s in ldap server %s",
+ map->map_mname,
+ lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host);
+ }
+ return FALSE;
}
+
+ /* We need to cast ld into the map structure */
+ lmap->ldap_ld = ld;
+ return TRUE;
+}
+
+/* ARGSUSED */
+static void
+ldaptimeout(sig_no)
+ int sig_no;
+{
+ longjmp(LDAPTimeout, 1);
}
/*
-** LDAP_MAP_CLOSE -- close ldap map
+** LDAPMAP_CLOSE -- close ldap map
*/
void
-ldap_map_close(map)
+ldapmap_close(map)
MAP *map;
{
- ldap_map_stop(map);
+ LDAPMAP_STRUCT *lmap;
+ STAB *s;
+
+ if (tTd(38, 2))
+ dprintf("ldapmap_close(%s)\n", map->map_mname);
+
+ lmap = (LDAPMAP_STRUCT *) map->map_db1;
+
+ /* Check if already closed */
+ if (lmap->ldap_ld == NULL)
+ return;
+
+ s = ldapmap_findconn(lmap);
+
+ /* Check if already closed */
+ if (s->s_ldap == NULL)
+ return;
+
+ /* If same as saved connection, stored connection is going away */
+ if (s->s_ldap == lmap->ldap_ld)
+ s->s_ldap = NULL;
+
+ if (lmap->ldap_ld != NULL)
+ {
+ ldap_unbind(lmap->ldap_ld);
+ lmap->ldap_ld = NULL;
+ }
}
-#ifdef SUNET_ID
+# ifdef SUNET_ID
/*
** SUNET_ID_HASH -- Convert a string to it's Sunet_id canonical form
** This only makes sense at Stanford University.
@@ -2792,225 +3088,697 @@ sunet_id_hash(str)
}
if (*p_last != '\0')
*p_last = '\0';
- return (str);
+ return str;
}
+# endif /* SUNET_ID */
-
-
-#endif /* SUNET_ID */
/*
-** LDAP_MAP_LOOKUP -- look up a datum in a LDAP map
+** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
*/
char *
-ldap_map_lookup(map, name, av, statp)
+ldapmap_lookup(map, name, av, statp)
MAP *map;
char *name;
char **av;
int *statp;
{
- LDAP_MAP_STRUCT *lmap = NULL;
- LDAPMessage *entry;
- char *vp;
- auto int vsize;
+ int i;
+ int entries = 0;
+ int msgid;
+ int ret;
+ int vsize;
+ char *fp, *vp;
+ char *p, *q;
+ char *result = NULL;
+ LDAPMAP_STRUCT *lmap = NULL;
char keybuf[MAXNAME + 1];
- char filter[LDAP_MAP_MAX_FILTER + 1];
- char **attr_values = NULL;
- char *result;
- int name_len;
- char *fp, *p, *q;
+ char filter[LDAPMAP_MAX_FILTER + 1];
if (tTd(38, 20))
- printf("ldap_map_lookup(%s, %s)\n", map->map_mname, name);
-
- /* actually open the map */
- if (!ldap_map_start(map))
- {
- result = NULL;
- *statp = EX_TEMPFAIL;
- goto quick_exit;
- }
+ dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name);
/* Get ldap struct pointer from map */
- lmap = (LDAP_MAP_STRUCT *) map->map_db1;
+ lmap = (LDAPMAP_STRUCT *) map->map_db1;
+ ldapmap_setopts(lmap->ldap_ld, lmap);
- name_len = strlen(name);
- if (name_len > MAXNAME)
- name_len = MAXNAME;
- strncpy(keybuf, name, name_len);
- keybuf[name_len] = '\0';
+ (void) strlcpy(keybuf, name, sizeof keybuf);
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
-#ifdef SUNET_ID
+ {
+# ifdef SUNET_ID
sunet_id_hash(keybuf);
-#else
+# else /* SUNET_ID */
makelower(keybuf);
-#endif /*SUNET_ID */
+# endif /* SUNET_ID */
+ }
/* substitute keybuf into filter, perhaps multiple times */
+ memset(filter, '\0', sizeof filter);
fp = filter;
- p = lmap->filter;
+ p = lmap->ldap_filter;
while ((q = strchr(p, '%')) != NULL)
{
if (q[1] == 's')
{
snprintf(fp, SPACELEFT(filter, fp), "%.*s%s",
q - p, p, keybuf);
+ fp += strlen(fp);
p = q + 2;
}
+ else if (q[1] == '0')
+ {
+ char *k = keybuf;
+
+ snprintf(fp, SPACELEFT(filter, fp), "%.*s",
+ 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",
q - p + 1, p);
p = q + (q[1] == '%' ? 2 : 1);
+ fp += strlen(fp);
}
- fp += strlen(fp);
}
snprintf(fp, SPACELEFT(filter, fp), "%s", p);
if (tTd(38, 20))
- printf("ldap search filter=%s\n", filter);
+ dprintf("ldap search filter=%s\n", filter);
- if (ldap_search_st(lmap->ld, lmap->base,lmap->scope,filter,
- lmap->attr, lmap->attrsonly, &(lmap->timeout),
- &(lmap->res)) != LDAP_SUCCESS)
+ 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);
+ if (msgid == -1)
{
- /* try stopping/starting map */
- ldap_map_stop(map);
- if (!ldap_map_start(map))
+ errno = ldapmap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
{
- result = NULL;
- *statp = EX_TEMPFAIL;
- goto quick_exit;
+ if (bitset(MF_NODEFER, map->map_mflags))
+ syserr("Error in ldap_search_st using %s in map %s",
+ filter, map->map_mname);
+ else
+ syserr("421 4.0.0 Error in ldap_search_st using %s in map %s",
+ filter, map->map_mname);
}
- if (ldap_search_st(lmap->ld, lmap->base, lmap->scope, filter,
- lmap->attr, lmap->attrsonly,
- &(lmap->timeout), &(lmap->res))
- != LDAP_SUCCESS)
+ *statp = EX_TEMPFAIL;
+ return NULL;
+ }
+
+ *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),
+ (lmap->ldap_timeout.tv_sec == 0 ? NULL :
+ &(lmap->ldap_timeout)),
+ &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY)
+ {
+ LDAPMessage *entry;
+
+ if (bitset(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;
+ }
+ (void) ldap_abandon(lmap->ldap_ld, msgid);
+ if (vp != NULL)
+ free(vp);
+ if (tTd(38, 25))
+ dprintf("ldap search found multiple on a single match query\n");
+ return NULL;
+ }
+ }
+
+ /* If we don't want multiple values and we have one, break */
+ if (map->map_coldelim == '\0' && vp != NULL)
+ break;
+
+ /* Cycle through all entries */
+ for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
+ entry != NULL;
+ entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
{
+ BerElement *ber;
+ char *attr;
+ char **vals = NULL;
+
+ /*
+ ** If matching only and found an entry,
+ ** no need to spin through attributes
+ */
+
+ if (*statp == EX_OK &&
+ bitset(MF_MATCHONLY, map->map_mflags))
+ continue;
+
+# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
+ /*
+ ** Reset value to prevent lingering
+ ** LDAP_DECODING_ERROR due to
+ ** OpenLDAP 1.X's hack (see below)
+ */
+
+ lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
+# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
+
+ for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
+ &ber);
+ attr != NULL;
+ attr = ldap_next_attribute(lmap->ldap_ld, entry,
+ ber))
+ {
+ char *tmp, *vp_tmp;
+
+ if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
+ {
+ vals = ldap_get_values(lmap->ldap_ld,
+ entry,
+ attr);
+ if (vals == NULL)
+ {
+ errno = ldapmap_geterrno(lmap->ldap_ld);
+ if (errno == LDAP_SUCCESS)
+ continue;
+
+ /* Must be an error */
+ errno += E_LDAPBASE;
+ if (!bitset(MF_OPTIONAL,
+ map->map_mflags))
+ {
+ if (bitset(MF_NODEFER,
+ map->map_mflags))
+ syserr("Error getting LDAP values in map %s",
+ map->map_mname);
+ else
+ syserr("421 4.0.0 Error getting LDAP values in map %s",
+ map->map_mname);
+ }
+ *statp = EX_TEMPFAIL;
+# if USING_NETSCAPE_LDAP
+ ldap_mem_free(attr);
+# endif /* USING_NETSCAPE_LDAP */
+ if (lmap->ldap_res != NULL)
+ {
+ ldap_msgfree(lmap->ldap_res);
+ lmap->ldap_res = NULL;
+ }
+ (void) ldap_abandon(lmap->ldap_ld,
+ msgid);
+ if (vp != NULL)
+ free(vp);
+ return NULL;
+ }
+ }
+
+ *statp = EX_OK;
+
+# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
+ /*
+ ** Reset value to prevent lingering
+ ** LDAP_DECODING_ERROR due to
+ ** OpenLDAP 1.X's hack (see below)
+ */
+
+ lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
+# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
+
+ /*
+ ** If matching only,
+ ** no need to spin through entries
+ */
+
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ continue;
+
+ /*
+ ** If we don't want multiple values,
+ ** return first found.
+ */
+
+ if (map->map_coldelim == '\0')
+ {
+ if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
+ {
+ vp = newstr(attr);
+# if USING_NETSCAPE_LDAP
+ ldap_mem_free(attr);
+# endif /* USING_NETSCAPE_LDAP */
+ break;
+ }
+
+ if (vals[0] == NULL)
+ {
+ ldap_value_free(vals);
+# if USING_NETSCAPE_LDAP
+ ldap_mem_free(attr);
+# endif /* USING_NETSCAPE_LDAP */
+ continue;
+ }
+
+ vp = newstr(vals[0]);
+ ldap_value_free(vals);
+# if USING_NETSCAPE_LDAP
+ ldap_mem_free(attr);
+# endif /* USING_NETSCAPE_LDAP */
+ break;
+ }
+
+ /* attributes only */
+ if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
+ {
+ if (vp == NULL)
+ vp = newstr(attr);
+ else
+ {
+ vsize = strlen(vp) +
+ strlen(attr) + 2;
+ tmp = xalloc(vsize);
+ snprintf(tmp, vsize, "%s%c%s",
+ vp, map->map_coldelim,
+ attr);
+ free(vp);
+ vp = tmp;
+ }
+# if USING_NETSCAPE_LDAP
+ ldap_mem_free(attr);
+# endif /* USING_NETSCAPE_LDAP */
+ continue;
+ }
+
+ /*
+ ** If there is more than one,
+ ** munge then into a map_coldelim
+ ** separated string
+ */
+
+ vsize = 0;
+ for (i = 0; vals[i] != NULL; i++)
+ vsize += strlen(vals[i]) + 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 (p >= vp_tmp + vsize)
+ syserr("ldapmap_lookup: Internal error: buffer too small for LDAP values");
+ if (vals[i + 1] != NULL)
+ *p++ = map->map_coldelim;
+ }
+
+ ldap_value_free(vals);
+# if USING_NETSCAPE_LDAP
+ ldap_mem_free(attr);
+# endif /* USING_NETSCAPE_LDAP */
+ if (vp == NULL)
+ {
+ vp = vp_tmp;
+ continue;
+ }
+ vsize = strlen(vp) + strlen(vp_tmp) + 2;
+ tmp = xalloc(vsize);
+ snprintf(tmp, vsize, "%s%c%s",
+ vp, map->map_coldelim, vp_tmp);
+
+ free(vp);
+ free(vp_tmp);
+ vp = tmp;
+ }
+ errno = ldapmap_geterrno(lmap->ldap_ld);
+
+ /*
+ ** We check errno != LDAP_DECODING_ERROR since
+ ** OpenLDAP 1.X has a very ugly *undocumented*
+ ** hack of returning this error code from
+ ** ldap_next_attribute() if the library freed the
+ ** ber attribute. See:
+ ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
+ */
+
+ if (errno != LDAP_SUCCESS &&
+ errno != LDAP_DECODING_ERROR)
+ {
+ /* Must be an error */
+ errno += E_LDAPBASE;
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ {
+ if (bitset(MF_NODEFER, map->map_mflags))
+ syserr("Error getting LDAP attributes in map %s",
+ map->map_mname);
+ else
+ syserr("421 4.0.0 Error getting LDAP attributes in map %s",
+ map->map_mname);
+ }
+ *statp = EX_TEMPFAIL;
+ if (lmap->ldap_res != NULL)
+ {
+ ldap_msgfree(lmap->ldap_res);
+ lmap->ldap_res = NULL;
+ }
+ (void) ldap_abandon(lmap->ldap_ld, msgid);
+ if (vp != NULL)
+ free(vp);
+ return NULL;
+ }
+
+ /* We don't want multiple values and we have one */
+ if (map->map_coldelim == '\0' && vp != NULL)
+ break;
+ }
+ errno = ldapmap_geterrno(lmap->ldap_ld);
+ if (errno != LDAP_SUCCESS && errno != LDAP_DECODING_ERROR)
+ {
+ /* Must be an error */
+ errno += E_LDAPBASE;
if (!bitset(MF_OPTIONAL, map->map_mflags))
{
- syserr("%sError in ldap_search_st using %s in map %s",
- bitset(MF_NODEFER, map->map_mflags) ? "" : "421 ",
- filter, map->map_mname);
+ if (bitset(MF_NODEFER, map->map_mflags))
+ syserr("Error getting LDAP entries in map %s",
+ map->map_mname);
+ else
+ syserr("421 4.0.0 Error getting LDAP entries in map %s",
+ map->map_mname);
}
- result = NULL;
*statp = EX_TEMPFAIL;
- goto quick_exit;
+ if (lmap->ldap_res != NULL)
+ {
+ ldap_msgfree(lmap->ldap_res);
+ lmap->ldap_res = NULL;
+ }
+ (void) ldap_abandon(lmap->ldap_ld, msgid);
+ if (vp != NULL)
+ free(vp);
+ return NULL;
}
+ ldap_msgfree(lmap->ldap_res);
+ lmap->ldap_res = NULL;
}
- entry = ldap_first_entry(lmap->ld,lmap->res);
- if (entry == 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))
{
- result = NULL;
- *statp = EX_NOTFOUND;
- goto quick_exit;
+ 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)
+ free(vp);
+ return NULL;
+ }
+ *statp = EX_OK;
}
- /* Need to build the args for map_rewrite here */
- attr_values = ldap_get_values(lmap->ld,entry,lmap->attr[0]);
- if (attr_values == NULL)
+ if (ret == 0)
+ errno = ETIMEDOUT;
+ else
+ errno = ldapmap_geterrno(lmap->ldap_ld);
+ if (errno != LDAP_SUCCESS)
{
- /* bad things happened */
- result = NULL;
- *statp = EX_NOTFOUND;
- goto quick_exit;
+ /* Must be an error */
+ if (ret != 0)
+ errno += E_LDAPBASE;
+ 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);
+ }
+ *statp = EX_TEMPFAIL;
+ if (vp != NULL)
+ free(vp);
+ return NULL;
}
- *statp = EX_OK;
+ /* Did we match anything? */
+ if (vp == NULL)
+ return NULL;
- /* If there is more that one use the first */
- vp = attr_values[0];
- vsize = strlen(vp);
+ /*
+ ** 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 (LogLevel > 9)
- sm_syslog(LOG_INFO, CurEnv->e_id,
- "ldap %.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);
-
- quick_exit:
- if (attr_values != NULL)
- ldap_value_free(attr_values);
- if (lmap != NULL)
- ldap_msgfree(lmap->res);
- ldap_map_stop(map);
- return result ;
-}
+ if (bitset(MF_NOREWRITE, map->map_mflags))
+ {
+ /* vp != NULL due to test above */
+ free(vp);
+ return "";
+ }
+ if (*statp == EX_OK)
+ {
+ /* vp != NULL due to test above */
+ if (LogLevel > 9)
+ sm_syslog(LOG_INFO, CurEnv->e_id,
+ "ldap %.100s => %s", name, vp);
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ result = map_rewrite(map, name, strlen(name), NULL);
+ else
+ result = map_rewrite(map, vp, strlen(vp), av);
+ free(vp);
+ }
+ return result;
+}
/*
-** LDAP_MAP_DEQUOTE - helper routine for ldap_map_parseargs
+** LDAPMAP_FINDCONN -- find an LDAP connection to the server
+**
+** Cache LDAP connections based on the host, port, bind DN,
+** and secret so we don't have multiple connections open to
+** the same server for different maps.
+**
+** Parameters:
+** lmap -- LDAP map information
+**
+** Returns:
+** Symbol table entry for the LDAP connection.
+**
*/
-char *
-ldap_map_dequote(str)
- char *str;
+static STAB *
+ldapmap_findconn(lmap)
+ LDAPMAP_STRUCT *lmap;
{
- char *p;
- char *start;
- p = str;
+ 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)) +
+ 1;
+ nbuf = xalloc(len);
+ snprintf(nbuf, len, "%s%c%d%c%s%c%s",
+ (lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host),
+ CONDELSE,
+ lmap->ldap_port,
+ CONDELSE,
+ (lmap->ldap_binddn == NULL ? "" : lmap->ldap_binddn),
+ CONDELSE,
+ (lmap->ldap_secret == NULL ? "" : lmap->ldap_secret));
+ s = stab(nbuf, ST_LDAP, ST_ENTER);
+ free(nbuf);
+ return s;
+}
+ /*
+** LDAPMAP_SETOPTS -- set LDAP options
+**
+** Parameters:
+** ld -- LDAP session handle
+** lmap -- LDAP map information
+**
+** Returns:
+** None.
+**
+*/
- if (*p == '"')
- {
- start = ++p;
- /* Should probably swallow initial whitespace here */
- }
+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
- {
- return(str);
- }
- while (*p != '"' && *p != '\0')
- {
- p++;
- }
- if (*p != '\0')
- *p = '\0';
- return start;
+ 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);
}
/*
-** LDAP_MAP_PARSEARGS -- parse ldap map definition args.
+** LDAPMAP_PARSEARGS -- parse ldap map definition args.
*/
+struct lamvalues LDAPAuthMethods[] =
+{
+ { "none", LDAP_AUTH_NONE },
+ { "simple", LDAP_AUTH_SIMPLE },
+# ifdef LDAP_AUTH_KRBV4
+ { "krbv4", LDAP_AUTH_KRBV4 },
+# endif /* LDAP_AUTH_KRBV4 */
+ { NULL, 0 }
+};
+
+struct ladvalues LDAPAliasDereference[] =
+{
+ { "never", LDAP_DEREF_NEVER },
+ { "always", LDAP_DEREF_ALWAYS },
+ { "search", LDAP_DEREF_SEARCHING },
+ { "find", LDAP_DEREF_FINDING },
+ { NULL, 0 }
+};
+
+struct lssvalues LDAPSearchScope[] =
+{
+ { "base", LDAP_SCOPE_BASE },
+ { "one", LDAP_SCOPE_ONELEVEL },
+ { "sub", LDAP_SCOPE_SUBTREE },
+ { NULL, 0 }
+};
+
bool
-ldap_map_parseargs(map,args)
+ldapmap_parseargs(map, args)
MAP *map;
char *args;
{
+ bool secretread = TRUE;
+ int i;
register char *p = args;
- register int done;
- LDAP_MAP_STRUCT *lmap;
-
- /* We need to alloc an LDAP_MAP_STRUCT struct */
- lmap = (LDAP_MAP_STRUCT *) xalloc(sizeof(LDAP_MAP_STRUCT));
-
- /* Set default int's here , default strings below */
- lmap->ldapport = DEFAULT_LDAP_MAP_PORT;
- lmap->deref = DEFAULT_LDAP_MAP_DEREF;
- lmap->timelimit = DEFAULT_LDAP_MAP_TIMELIMIT;
- lmap->sizelimit = DEFAULT_LDAP_MAP_SIZELIMIT;
- lmap->ldap_options = DEFAULT_LDAP_MAP_LDAP_OPTIONS;
- lmap->method = DEFAULT_LDAP_MAP_METHOD;
- lmap->scope = DEFAULT_LDAP_MAP_SCOPE;
- lmap->attrsonly = DEFAULT_LDAP_MAP_ATTRSONLY;
- lmap->timeout.tv_sec = DEFAULT_LDAP_MAP_TIMELIMIT;
- lmap->timeout.tv_usec = 0;
-
- /* Default char ptrs to NULL */
- lmap->binddn = NULL;
- lmap->passwd = NULL;
- lmap->base = NULL;
- lmap->ldaphost = NULL;
-
- /* Default general ptrs to NULL */
- lmap->ld = NULL;
- lmap->res = NULL;
+ LDAPMAP_STRUCT *lmap;
+ struct lamvalues *lam;
+ struct ladvalues *lad;
+ struct lssvalues *lss;
+ char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
- map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
+ /* Get ldap struct pointer from map */
+ lmap = (LDAPMAP_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);
+ if (LDAPDefaults == NULL)
+ ldapmap_clear(lmap);
+ else
+ STRUCTCOPY(*LDAPDefaults, *lmap);
+ }
+
+ /* there is no check whether there is really an argument */
+ map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
+ map->map_spacesub = SpaceSub; /* default value */
for (;;)
{
while (isascii(*p) && isspace(*p))
@@ -3048,10 +3816,6 @@ ldap_map_parseargs(map,args)
map->map_mflags |= MF_KEEPQUOTES;
break;
- case 't':
- map->map_mflags |= MF_NODEFER;
- break;
-
case 'a':
map->map_app = ++p;
break;
@@ -3060,49 +3824,126 @@ ldap_map_parseargs(map,args)
map->map_tapp = ++p;
break;
- /* Start of ldap_map specific args */
+ case 't':
+ map->map_mflags |= MF_NODEFER;
+ break;
+
+ case 'S':
+ map->map_spacesub = *++p;
+ break;
+
+ case 'D':
+ map->map_mflags |= MF_DEFER;
+ break;
+
+ 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;
+
+ /* Start of ldapmap specific args */
case 'k': /* search field */
while (isascii(*++p) && isspace(*p))
continue;
- lmap->filter = p;
+ lmap->ldap_filter = p;
break;
case 'v': /* attr to return */
while (isascii(*++p) && isspace(*p))
continue;
- lmap->attr[0] = p;
- lmap->attr[1] = NULL;
+ lmap->ldap_attr[0] = p;
+ lmap->ldap_attr[1] = NULL;
+ break;
+
+ case '1':
+ map->map_mflags |= MF_SINGLEMATCH;
break;
/* args stolen from ldapsearch.c */
case 'R': /* don't auto chase referrals */
-#ifdef LDAP_REFERRALS
+# ifdef LDAP_REFERRALS
lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
-#else /* LDAP_REFERRALS */
+# else /* LDAP_REFERRALS */
syserr("compile with -DLDAP_REFERRALS for referral support\n");
-#endif /* LDAP_REFERRALS */
+# endif /* LDAP_REFERRALS */
break;
- case 'n': /* retrieve attribute names only -- no values */
- lmap->attrsonly += 1;
+ case 'n': /* retrieve attribute names only */
+ lmap->ldap_attrsonly = LDAPMAP_TRUE;
break;
- case 's': /* search scope */
- if (strncasecmp(++p, "base", 4) == 0)
+ case 'r': /* alias dereferencing */
+ while (isascii(*++p) && isspace(*p))
+ continue;
+
+ if (strncasecmp(p, "LDAP_DEREF_", 11) == 0)
+ p += 11;
+
+ for (lad = LDAPAliasDereference;
+ lad != NULL && lad->lad_name != NULL; lad++)
{
- lmap->scope = LDAP_SCOPE_BASE;
+ if (strncasecmp(p, lad->lad_name,
+ strlen(lad->lad_name)) == 0)
+ break;
}
- else if (strncasecmp(p, "one", 3) == 0)
+ if (lad->lad_name != NULL)
+ lmap->ldap_deref = lad->lad_code;
+ else
{
- lmap->scope = LDAP_SCOPE_ONELEVEL;
+ /* bad config line */
+ if (!bitset(MCF_OPTFILE,
+ map->map_class->map_cflags))
+ {
+ char *ptr;
+
+ if ((ptr = strchr(p, ' ')) != NULL)
+ *ptr = '\0';
+ syserr("Deref must be [never|always|search|find] not %s in map %s",
+ p, map->map_mname);
+ if (ptr != NULL)
+ *ptr = ' ';
+ return FALSE;
+ }
}
- else if (strncasecmp(p, "sub", 3) == 0)
+ break;
+
+ case 's': /* search scope */
+ while (isascii(*++p) && isspace(*p))
+ continue;
+
+ if (strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
+ p += 11;
+
+ for (lss = LDAPSearchScope;
+ lss != NULL && lss->lss_name != NULL; lss++)
{
- lmap->scope = LDAP_SCOPE_SUBTREE;
+ if (strncasecmp(p, lss->lss_name,
+ strlen(lss->lss_name)) == 0)
+ break;
}
+ if (lss->lss_name != NULL)
+ lmap->ldap_scope = lss->lss_code;
else
- { /* bad config line */
- if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
+ {
+ /* bad config line */
+ if (!bitset(MCF_OPTFILE,
+ map->map_class->map_cflags))
{
char *ptr;
@@ -3120,49 +3961,105 @@ ldap_map_parseargs(map,args)
case 'h': /* ldap host */
while (isascii(*++p) && isspace(*p))
continue;
- map->map_domain = p;
- lmap->ldaphost = p;
+ lmap->ldap_host = p;
break;
case 'b': /* search base */
while (isascii(*++p) && isspace(*p))
continue;
- lmap->base = p;
+ lmap->ldap_base = p;
break;
case 'p': /* ldap port */
while (isascii(*++p) && isspace(*p))
continue;
- lmap->ldapport = atoi(p);
+ lmap->ldap_port = atoi(p);
break;
case 'l': /* time limit */
while (isascii(*++p) && isspace(*p))
continue;
- lmap->timelimit = atoi(p);
- lmap->timeout.tv_sec = lmap->timelimit;
+ lmap->ldap_timelimit = atoi(p);
+ lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
+ break;
+
+ case 'Z':
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ lmap->ldap_sizelimit = atoi(p);
break;
+ case 'd': /* Dn to bind to server as */
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ lmap->ldap_binddn = p;
+ break;
+
+ case 'M': /* Method for binding */
+ while (isascii(*++p) && isspace(*p))
+ continue;
+
+ if (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)
+ break;
+ }
+ if (lam->lam_name != NULL)
+ lmap->ldap_method = lam->lam_code;
+ else
+ {
+ /* bad config line */
+ if (!bitset(MCF_OPTFILE,
+ map->map_class->map_cflags))
+ {
+ char *ptr;
+
+ if ((ptr = strchr(p, ' ')) != NULL)
+ *ptr = '\0';
+ syserr("Method for binding must be [none|simple|krbv4] not %s in map %s",
+ p, map->map_mname);
+ if (ptr != NULL)
+ *ptr = ' ';
+ return FALSE;
+ }
+ }
+
+ break;
+
+ /*
+ ** This is a string that is dependent on the
+ ** method used defined above.
+ */
+
+ case 'P': /* Secret password for binding */
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ lmap->ldap_secret = p;
+ secretread = FALSE;
+ break;
+
+ default:
+ syserr("Illegal option %c map %s", *p, map->map_mname);
+ break;
}
- /* need to account for quoted strings here arggg... */
- done = isascii(*p) && isspace(*p);
- while (*p != '\0' && !done)
+ /* need to account for quoted strings here */
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
{
if (*p == '"')
{
while (*++p != '"' && *p != '\0')
- {
continue;
- }
if (*p != '\0')
p++;
}
else
- {
p++;
- }
- done = isascii(*p) && isspace(*p);
}
if (*p != '\0')
@@ -3170,47 +4067,130 @@ ldap_map_parseargs(map,args)
}
if (map->map_app != NULL)
- map->map_app = newstr(ldap_map_dequote(map->map_app));
+ map->map_app = newstr(ldapmap_dequote(map->map_app));
if (map->map_tapp != NULL)
- map->map_tapp = newstr(ldap_map_dequote(map->map_tapp));
- if (map->map_domain != NULL)
- map->map_domain = newstr(ldap_map_dequote(map->map_domain));
+ map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
/*
** We need to swallow up all the stuff into a struct
** and dump it into map->map_dbptr1
*/
- if (lmap->ldaphost != NULL)
- lmap->ldaphost = newstr(ldap_map_dequote(lmap->ldaphost));
- else
+ if (lmap->ldap_host != NULL &&
+ (LDAPDefaults == NULL ||
+ LDAPDefaults == lmap ||
+ LDAPDefaults->ldap_host != lmap->ldap_host))
+ lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
+ map->map_domain = lmap->ldap_host;
+
+ if (lmap->ldap_binddn != NULL &&
+ (LDAPDefaults == NULL ||
+ LDAPDefaults == lmap ||
+ LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
+ lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
+
+ if (lmap->ldap_secret != NULL &&
+ (LDAPDefaults == NULL ||
+ LDAPDefaults == lmap ||
+ LDAPDefaults->ldap_secret != lmap->ldap_secret))
{
- syserr("LDAP map: -h flag is required");
- return FALSE;
- }
+ FILE *sfd;
+ long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
- if (lmap->binddn != NULL)
- lmap->binddn = newstr(ldap_map_dequote(lmap->binddn));
- else
- lmap->binddn = DEFAULT_LDAP_MAP_BINDDN;
+ if (DontLockReadFiles)
+ sff |= SFF_NOLOCK;
+ /* need to use method to map secret to passwd string */
+ switch (lmap->ldap_method)
+ {
+ case LDAP_AUTH_NONE:
+ /* Do nothing */
+ break;
- if (lmap->passwd != NULL)
- lmap->passwd = newstr(ldap_map_dequote(lmap->passwd));
- else
- lmap->passwd = DEFAULT_LDAP_MAP_PASSWD;
+ case LDAP_AUTH_SIMPLE:
- if (lmap->base != NULL)
- lmap->base = newstr(ldap_map_dequote(lmap->base));
- else
- {
- syserr("LDAP map: -b flag is required");
- return FALSE;
+ /*
+ ** Secret is the name of a file with
+ ** the first line as the password.
+ */
+
+ /* Already read in the secret? */
+ if (secretread)
+ break;
+
+ sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
+ O_RDONLY, 0, sff);
+ if (sfd == NULL)
+ {
+ syserr("LDAP map: cannot open secret %s",
+ ldapmap_dequote(lmap->ldap_secret));
+ return FALSE;
+ }
+ lmap->ldap_secret = sfgets(m_tmp, LDAPMAP_MAX_PASSWD,
+ sfd, 0, "ldapmap_parseargs");
+ (void) fclose(sfd);
+ if (lmap->ldap_secret != NULL &&
+ strlen(m_tmp) > 0)
+ {
+ /* chomp newline */
+ if (m_tmp[strlen(m_tmp) - 1] == '\n')
+ m_tmp[strlen(m_tmp) - 1] = '\0';
+
+ lmap->ldap_secret = m_tmp;
+ }
+ break;
+
+# ifdef LDAP_AUTH_KRBV4
+ case LDAP_AUTH_KRBV4:
+
+ /*
+ ** Secret is where the ticket file is
+ ** stashed
+ */
+
+ 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;
+ break;
+ }
}
+ if (lmap->ldap_secret != NULL &&
+ (LDAPDefaults == NULL ||
+ LDAPDefaults == lmap ||
+ LDAPDefaults->ldap_secret != lmap->ldap_secret))
+ lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
- if (lmap->filter != NULL)
- lmap->filter = newstr(ldap_map_dequote(lmap->filter));
+ if (lmap->ldap_base != NULL &&
+ (LDAPDefaults == NULL ||
+ LDAPDefaults == lmap ||
+ LDAPDefaults->ldap_base != lmap->ldap_base))
+ lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
+
+ /*
+ ** Save the server from extra work. If request is for a single
+ ** match, tell the server to only return enough records to
+ ** determine if there is a single match or not. This can not
+ ** be one since the server would only return one and we wouldn't
+ ** know if there were others available.
+ */
+
+ if (bitset(MF_SINGLEMATCH, map->map_mflags))
+ lmap->ldap_sizelimit = 2;
+
+ /* If setting defaults, don't process ldap_filter and ldap_attr */
+ if (lmap == LDAPDefaults)
+ return TRUE;
+
+ if (lmap->ldap_filter != NULL)
+ lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
else
{
if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
@@ -3219,27 +4199,805 @@ ldap_map_parseargs(map,args)
return FALSE;
}
}
- if (lmap->attr[0] != NULL)
- lmap->attr[0] = newstr(ldap_map_dequote(lmap->attr[0]));
- else
+
+ if (lmap->ldap_attr[0] != NULL)
{
- if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
+ i = 0;
+ p = ldapmap_dequote(lmap->ldap_attr[0]);
+ lmap->ldap_attr[0] = NULL;
+
+ while (p != NULL)
{
- syserr("No return attribute in %s", map->map_mname);
- return FALSE;
+ char *v;
+
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p == '\0')
+ break;
+ v = p;
+ p = strchr(v, ',');
+ if (p != NULL)
+ *p++ = '\0';
+
+ if (i == LDAPMAP_MAX_ATTR)
+ {
+ syserr("Too many return attributes in %s (max %d)",
+ map->map_mname, LDAPMAP_MAX_ATTR);
+ return FALSE;
+ }
+ if (*v != '\0')
+ lmap->ldap_attr[i++] = newstr(v);
}
+ lmap->ldap_attr[i] = NULL;
}
map->map_db1 = (ARBPTR_T) lmap;
return TRUE;
}
-#endif /* LDAP Modules */
+/*
+** 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;
+}
/*
-** syslog map
+** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
+**
+** Parameters:
+** spec -- map argument string from LDAPDefaults option
+**
+** Returns:
+** None.
+**
+*/
+
+void
+ldapmap_set_defaults(spec)
+ char *spec;
+{
+ MAP map;
+
+ /* Allocate and set the default values */
+ if (LDAPDefaults == NULL)
+ LDAPDefaults = (LDAPMAP_STRUCT *) xalloc(sizeof *LDAPDefaults);
+ ldapmap_clear(LDAPDefaults);
+
+ memset(&map, '\0', sizeof map);
+ map.map_db1 = (ARBPTR_T) LDAPDefaults;
+
+ (void) ldapmap_parseargs(&map, spec);
+
+ /* These should never be set in LDAPDefaults */
+ if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
+ map.map_spacesub != SpaceSub ||
+ map.map_app != NULL ||
+ map.map_tapp != NULL)
+ {
+ syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
+ if (map.map_app != NULL)
+ {
+ free(map.map_app);
+ map.map_app = NULL;
+ }
+ if (map.map_tapp != NULL)
+ {
+ free(map.map_tapp);
+ map.map_tapp = NULL;
+ }
+ }
+
+ if (LDAPDefaults->ldap_filter != NULL)
+ {
+ syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
+ /* don't free, it isn't malloc'ed in parseargs */
+ LDAPDefaults->ldap_filter = NULL;
+ }
+
+ if (LDAPDefaults->ldap_attr[0] != NULL)
+ {
+ syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
+ /* don't free, they aren't malloc'ed in parseargs */
+ LDAPDefaults->ldap_attr[0] = NULL;
+ }
+}
+#endif /* LDAPMAP */
+ /*
+** PH map
+*/
+
+#ifdef PH_MAP
+
+/*
+** Support for the CCSO Nameserver (ph/qi).
+** This code is intended to replace the so-called "ph mailer".
+** Contributed by Mark D. Roth <roth@uiuc.edu>. Contact him for support.
+*/
+
+# include <qiapi.h>
+# include <qicode.h>
+
+/*
+** PH_MAP_PARSEARGS -- parse ph map definition args.
+*/
+
+bool
+ph_map_parseargs(map, args)
+ MAP *map;
+ char *args;
+{
+ int i;
+ register int done;
+ PH_MAP_STRUCT *pmap = NULL;
+ register char *p = args;
+
+ 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_timeout = 0;
+
+ 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;
+
+#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;
+ break;
+
+ case 'D':
+ map->map_mflags |= MF_DEFER;
+ break;
+
+ case 'h': /* PH server list */
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ pmap->ph_servers = p;
+ break;
+
+ case 'v': /* 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);
+ }
+
+ /* try to account for quoted strings */
+ done = isascii(*p) && isspace(*p);
+ while (*p != '\0' && !done)
+ {
+ if (*p == '"')
+ {
+ while (*++p != '"' && *p != '\0')
+ continue;
+ if (*p != '\0')
+ p++;
+ }
+ else
+ p++;
+ done = isascii(*p) && isspace(*p);
+ }
+
+ if (*p != '\0')
+ *p++ = '\0';
+ }
+
+ if (map->map_app != NULL)
+ map->map_app = newstr(ph_map_dequote(map->map_app));
+ if (map->map_tapp != NULL)
+ map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
+
+ 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;
+ }
+
+ map->map_db1 = (ARBPTR_T) pmap;
+ return TRUE;
+}
+
+#if _FFR_PHMAP_TIMEOUT
+/*
+** PH_MAP_CLOSE -- close the connection to the ph server
+*/
+
+static void
+ph_map_safeclose(map)
+ MAP *map;
+{
+ int save_errno = errno;
+ PH_MAP_STRUCT *pmap;
+
+ pmap = (PH_MAP_STRUCT *)map->map_db1;
+
+ 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)
+ {
+ (void) fclose(pmap->ph_to_server);
+ pmap->ph_to_server = NULL;
+ }
+ 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);
+}
+
+static jmp_buf PHTimeout;
+
+/* ARGSUSED */
+static void
+ph_timeout_func(sig_no)
+ int sig_no;
+{
+ longjmp(PHTimeout, 1);
+}
+#else /* _FFR_PHMAP_TIMEOUT */
+/*
+** PH_MAP_CLOSE -- close the connection to the ph server
+*/
+
+void
+ph_map_close(map)
+ MAP *map;
+{
+ PH_MAP_STRUCT *pmap;
+
+ 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;
+}
+#endif /* _FFR_PHMAP_TIMEOUT */
+
+ /*
+** PH_MAP_OPEN -- sub for opening PH map
+*/
+bool
+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 */
+
+ if (tTd(38, 2))
+ 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;
+ }
+
+ pmap = (PH_MAP_STRUCT *)map->map_db1;
+
+ hostlist = newstr(pmap->ph_servers);
+ tmp = strtok(hostlist, " ");
+ do
+ {
+#if _FFR_PHMAP_TIMEOUT
+ if (pmap->ph_timeout != 0)
+ {
+ if (setjmp(PHTimeout) != 0)
+ {
+ ev = NULL;
+ if (LogLevel > 1)
+ sm_syslog(LOG_NOTICE, CurEnv->e_id,
+ "timeout connecting to PH server %.100s",
+ tmp);
+# ifdef ETIMEDOUT
+ errno = ETIMEDOUT;
+# else /* ETIMEDOUT */
+ errno = 0;
+# endif /* ETIMEDOUT */
+ goto ph_map_open_abort;
+ }
+ ev = setevent(pmap->ph_timeout, ph_timeout_func, 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)
+ {
+ 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 */
+ free(hostlist);
+ 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 !_FFR_PHMAP_TIMEOUT
+ errno = save_errno;
+#endif /* !_FFR_PHMAP_TIMEOUT */
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ {
+ if (errno == 0 && !bitset(MF_NODEFER,map->map_mflags))
+ errno = EAGAIN;
+ syserr("ph_map_open: cannot connect to PH server");
+ }
+ else if (LogLevel > 1)
+ sm_syslog(LOG_NOTICE, CurEnv->e_id,
+ "ph_map_open: cannot connect to PH server");
+ free(hostlist);
+ return FALSE;
+}
+
+/*
+** PH_MAP_LOOKUP -- look up key from ph server
*/
-#if _FFR_MAP_SYSLOG
+#if _FFR_PHMAP_TIMEOUT
+# define MAX_PH_FIELDS 20
+#endif /* _FFR_PHMAP_TIMEOUT */
+
+char *
+ph_map_lookup(map, key, args, pstat)
+ MAP *map;
+ char *key;
+ 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 */
+ PH_MAP_STRUCT *pmap;
+
+ pmap = (PH_MAP_STRUCT *)map->map_db1;
+
+ *pstat = EX_OK;
+
+#if _FFR_PHMAP_TIMEOUT
+ if (pmap->ph_timeout != 0)
+ {
+ if (setjmp(PHTimeout) != 0)
+ {
+ ev = NULL;
+ if (LogLevel > 1)
+ 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_func, 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 */
+
+ ph_map_lookup_abort:
+#if _FFR_PHMAP_TIMEOUT
+ if (ev != NULL)
+ clrevent(ev);
+
+ /*
+ ** Return EX_TEMPFAIL 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;
+
+ 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);
+
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ message = map_rewrite(map, key, strlen(key), NULL);
+ else
+ message = map_rewrite(map, tmp, strlen(tmp), 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
+*/
#define map_prio map_lockfd /* overload field */
@@ -3255,18 +5013,42 @@ syslog_map_parseargs(map, args)
char *p = args;
char *priority = NULL;
- for (;;)
+ /* there is no check whether there is really an argument */
+ while (*p != '\0')
{
while (isascii(*p) && isspace(*p))
p++;
if (*p != '-')
break;
- if (*++p == 'L')
- priority = ++p;
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p != '\0')
- *p++ = '\0';
+ ++p;
+ if (*p == 'D')
+ {
+ map->map_mflags |= MF_DEFER;
+ ++p;
+ }
+ else if (*p == 'S')
+ {
+ map->map_spacesub = *++p;
+ if (*p != '\0')
+ p++;
+ }
+ else if (*p == 'L')
+ {
+ while (*++p != '\0' && isascii(*p) && isspace(*p))
+ continue;
+ if (*p == '\0')
+ break;
+ priority = p;
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+ }
+ else
+ {
+ syserr("Illegal option %c map syslog", *p);
+ ++p;
+ }
}
if (priority == NULL)
@@ -3280,42 +5062,42 @@ syslog_map_parseargs(map, args)
if (strcasecmp("EMERG", priority) == 0)
map->map_prio = LOG_EMERG;
else
-#endif
+#endif /* LOG_EMERG */
#ifdef LOG_ALERT
if (strcasecmp("ALERT", priority) == 0)
map->map_prio = LOG_ALERT;
else
-#endif
+#endif /* LOG_ALERT */
#ifdef LOG_CRIT
if (strcasecmp("CRIT", priority) == 0)
map->map_prio = LOG_CRIT;
else
-#endif
+#endif /* LOG_CRIT */
#ifdef LOG_ERR
if (strcasecmp("ERR", priority) == 0)
map->map_prio = LOG_ERR;
else
-#endif
+#endif /* LOG_ERR */
#ifdef LOG_WARNING
if (strcasecmp("WARNING", priority) == 0)
map->map_prio = LOG_WARNING;
else
-#endif
+#endif /* LOG_WARNING */
#ifdef LOG_NOTICE
if (strcasecmp("NOTICE", priority) == 0)
map->map_prio = LOG_NOTICE;
else
-#endif
+#endif /* LOG_NOTICE */
#ifdef LOG_INFO
if (strcasecmp("INFO", priority) == 0)
map->map_prio = LOG_INFO;
else
-#endif
+#endif /* LOG_INFO */
#ifdef LOG_DEBUG
if (strcasecmp("DEBUG", priority) == 0)
map->map_prio = LOG_DEBUG;
else
-#endif
+#endif /* LOG_DEBUG */
{
syserr("syslog_map_parseargs: Unknown priority %s\n",
priority);
@@ -3341,8 +5123,8 @@ syslog_map_lookup(map, string, args, statp)
if (ptr != NULL)
{
if (tTd(38, 20))
- printf("syslog_map_lookup(%s (priority %d): %s\n",
- map->map_mname, map->map_prio, ptr);
+ 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);
}
@@ -3351,7 +5133,6 @@ syslog_map_lookup(map, string, args, statp)
return "";
}
-#endif /* _FFR_MAP_SYSLOG */
/*
** HESIOD Modules
*/
@@ -3364,33 +5145,33 @@ hes_map_open(map, mode)
int mode;
{
if (tTd(38, 2))
- printf("hes_map_open(%s, %s, %d)\n",
+ 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
+# ifdef ENOSYS
errno = ENOSYS;
-#else
-# ifdef EFTYPE
+# else /* ENOSYS */
+# ifdef EFTYPE
errno = EFTYPE;
-# else
+# else /* EFTYPE */
errno = ENXIO;
-# endif
-#endif
+# endif /* EFTYPE */
+# endif /* ENOSYS */
return FALSE;
}
-#ifdef HESIOD_INIT
+# ifdef HESIOD_INIT
if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
return TRUE;
if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("421 cannot initialize Hesiod map (%s)",
+ syserr("421 4.0.0 cannot initialize Hesiod map (%s)",
errstring(errno));
return FALSE;
-#else
+# else /* HESIOD_INIT */
if (hes_error() == HES_ER_UNINIT)
hes_init();
switch (hes_error())
@@ -3401,10 +5182,10 @@ hes_map_open(map, mode)
}
if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("421 cannot initialize Hesiod map (%d)", hes_error());
+ syserr("421 4.0.0 cannot initialize Hesiod map (%d)", hes_error());
return FALSE;
-#endif /* HESIOD_INIT */
+# endif /* HESIOD_INIT */
}
char *
@@ -3417,7 +5198,7 @@ hes_map_lookup(map, name, av, statp)
char **hp;
if (tTd(38, 20))
- printf("hes_map_lookup(%s, %s)\n", map->map_file, name);
+ dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
if (name[0] == '\\')
{
@@ -3431,24 +5212,24 @@ hes_map_lookup(map, name, av, statp)
else
np = xalloc(strlen(name) + 2);
np[0] = '\\';
- strcpy(&np[1], name);
-#ifdef HESIOD_INIT
+ (void) strlcpy(&np[1], name, (sizeof nbuf) - 1);
+# ifdef HESIOD_INIT
hp = hesiod_resolve(HesiodContext, np, map->map_file);
-#else
+# else /* HESIOD_INIT */
hp = hes_resolve(np, map->map_file);
-#endif /* HESIOD_INIT */
+# endif /* HESIOD_INIT */
if (np != nbuf)
free(np);
}
else
{
-#ifdef HESIOD_INIT
+# ifdef HESIOD_INIT
hp = hesiod_resolve(HesiodContext, name, map->map_file);
-#else
+# else /* HESIOD_INIT */
hp = hes_resolve(name, map->map_file);
-#endif /* HESIOD_INIT */
+# endif /* HESIOD_INIT */
}
-#ifdef HESIOD_INIT
+# ifdef HESIOD_INIT
if (hp == NULL)
return NULL;
if (*hp == NULL)
@@ -3470,7 +5251,7 @@ hes_map_lookup(map, name, av, statp)
}
return NULL;
}
-#else
+# else /* HESIOD_INIT */
if (hp == NULL || hp[0] == NULL)
{
switch (hes_error())
@@ -3493,7 +5274,7 @@ hes_map_lookup(map, name, av, statp)
}
return NULL;
}
-#endif /* HESIOD_INIT */
+# endif /* HESIOD_INIT */
if (bitset(MF_MATCHONLY, map->map_mflags))
return map_rewrite(map, name, strlen(name), NULL);
@@ -3501,7 +5282,7 @@ hes_map_lookup(map, name, av, statp)
return map_rewrite(map, hp[0], strlen(hp[0]), av);
}
-#endif
+#endif /* HESIOD */
/*
** NeXT NETINFO Modules
*/
@@ -3511,9 +5292,6 @@ hes_map_lookup(map, name, av, statp)
# define NETINFO_DEFAULT_DIR "/aliases"
# define NETINFO_DEFAULT_PROPERTY "members"
-extern char *ni_propval __P((char *, char *, char *, char *, int));
-
-
/*
** NI_MAP_OPEN -- open NetInfo Aliases
*/
@@ -3524,7 +5302,7 @@ ni_map_open(map, mode)
int mode;
{
if (tTd(38, 2))
- printf("ni_map_open(%s, %s, %d)\n",
+ dprintf("ni_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
mode &= O_ACCMODE;
@@ -3556,7 +5334,7 @@ ni_map_lookup(map, name, av, statp)
char *propval;
if (tTd(38, 20))
- printf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
+ 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);
@@ -3573,7 +5351,7 @@ ni_map_lookup(map, name, av, statp)
}
-bool
+static bool
ni_getcanonname(name, hbsize, statp)
char *name;
int hbsize;
@@ -3584,14 +5362,13 @@ ni_getcanonname(name, hbsize, statp)
char nbuf[MAXNAME + 1];
if (tTd(38, 20))
- printf("ni_getcanonname(%s)\n", name);
+ dprintf("ni_getcanonname(%s)\n", name);
- if (strlen(name) >= sizeof nbuf)
+ if (strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
{
*statp = EX_UNAVAILABLE;
return FALSE;
}
- (void) strcpy(nbuf, name);
shorten_hostname(nbuf);
/* we only accept single token search key */
@@ -3616,7 +5393,8 @@ ni_getcanonname(name, hbsize, statp)
if (hbsize >= strlen(vptr))
{
- strcpy(name, vptr);
+ (void) strlcpy(name, vptr, hbsize);
+ free(vptr);
*statp = EX_OK;
return TRUE;
}
@@ -3646,7 +5424,7 @@ ni_getcanonname(name, hbsize, statp)
** 1. the directory is not found
** 2. the property name is not found
** 3. the property contains multiple values
-** 4. some error occured
+** 4. some error occurred
** else -- the value of the lookup.
**
** Example:
@@ -3654,14 +5432,14 @@ ni_getcanonname(name, hbsize, statp)
** ni_propval("/aliases", "name", aliasname, "members", ',')
**
** Notes:
-** Caller should free the return value of ni_proval
+** 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
+# define LOCAL_NETINFO_DOMAIN "."
+# define PARENT_NETINFO_DOMAIN ".."
+# define MAX_NI_LEVELS 256
char *
ni_propval(keydir, keyprop, keyval, valprop, sepchar)
@@ -3673,7 +5451,7 @@ ni_propval(keydir, keyprop, keyval, valprop, sepchar)
{
char *propval = NULL;
int i;
- int j, alen;
+ int j, alen, l;
void *ni = NULL;
void *lastni = NULL;
ni_status nis;
@@ -3692,19 +5470,19 @@ ni_propval(keydir, keyprop, keyval, valprop, sepchar)
i = strlen(keydir) + strlen(keyval) + 2;
if (keyprop != NULL)
i += strlen(keyprop) + 1;
- if (i > sizeof keybuf)
+ if (i >= sizeof keybuf)
return NULL;
- strcpy(keybuf, keydir);
- strcat(keybuf, "/");
+ (void) strlcpy(keybuf, keydir, sizeof keybuf);
+ (void) strlcat(keybuf, "/", sizeof keybuf);
if (keyprop != NULL)
{
- strcat(keybuf, keyprop);
- strcat(keybuf, "=");
+ (void) strlcat(keybuf, keyprop, sizeof keybuf);
+ (void) strlcat(keybuf, "=", sizeof keybuf);
}
- strcat(keybuf, keyval);
+ (void) strlcat(keybuf, keyval, sizeof keybuf);
if (tTd(38, 21))
- printf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n",
+ 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
@@ -3720,7 +5498,7 @@ ni_propval(keydir, keyprop, keyval, valprop, sepchar)
{
nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni);
if (tTd(38, 20))
- printf("ni_open(LOCAL) = %d\n", nis);
+ dprintf("ni_open(LOCAL) = %d\n", nis);
}
else
{
@@ -3729,7 +5507,7 @@ ni_propval(keydir, keyprop, keyval, valprop, sepchar)
lastni = ni;
nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni);
if (tTd(38, 20))
- printf("ni_open(PARENT) = %d\n", nis);
+ dprintf("ni_open(PARENT) = %d\n", nis);
}
/*
@@ -3758,7 +5536,9 @@ ni_propval(keydir, keyprop, keyval, valprop, sepchar)
continue;
if (tTd(38, 20))
- printf("ni_lookupprop: len=%d\n", ninl.ni_namelist_len);
+ dprintf("ni_lookupprop: len=%d\n",
+ ninl.ni_namelist_len);
+
/*
** See if we have an acceptable number of values.
*/
@@ -3782,9 +5562,11 @@ ni_propval(keydir, keyprop, keyval, valprop, sepchar)
propval = p = xalloc(alen);
for (j = 0; j < ninl.ni_namelist_len; j++)
{
- strcpy(p, ninl.ni_namelist_val[j]);
- p += strlen(p);
+ (void) strlcpy(p, ninl.ni_namelist_val[j], alen);
+ l = strlen(p);
+ p += l;
*p++ = sepchar;
+ alen -= l + 1;
}
*--p = '\0';
@@ -3800,7 +5582,7 @@ ni_propval(keydir, keyprop, keyval, valprop, sepchar)
if (lastni != NULL && ni != lastni)
ni_free(lastni);
if (tTd(38, 20))
- printf("ni_propval returns: '%s'\n", propval);
+ dprintf("ni_propval returns: '%s'\n", propval);
return propval;
}
@@ -3824,11 +5606,11 @@ text_map_open(map, mode)
MAP *map;
int mode;
{
- int sff;
+ long sff;
int i;
if (tTd(38, 2))
- printf("text_map_open(%s, %s, %d)\n",
+ dprintf("text_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
mode &= O_ACCMODE;
@@ -3853,16 +5635,19 @@ text_map_open(map, mode)
}
sff = SFF_ROOTOK|SFF_REGONLY;
- if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
+ if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
sff |= SFF_NOWLINK;
- if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
+ if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
sff |= SFF_SAFEDIRPATH;
if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
sff, S_IRUSR, NULL)) != 0)
{
+ int save_errno = errno;
+
/* cannot open this map */
if (tTd(38, 2))
- printf("\tunsafe map file: %d\n", i);
+ 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);
@@ -3899,12 +5684,12 @@ text_map_open(map, mode)
if (tTd(38, 2))
{
- printf("text_map_open(%s, %s): delimiter = ",
+ dprintf("text_map_open(%s, %s): delimiter = ",
map->map_mname, map->map_file);
if (map->map_coldelim == '\0')
- printf("(white space)\n");
+ dprintf("(white space)\n");
else
- printf("%c\n", map->map_coldelim);
+ dprintf("%c\n", map->map_coldelim);
}
map->map_sff = sff;
@@ -3930,20 +5715,19 @@ text_map_lookup(map, name, av, statp)
char delim;
int key_idx;
bool found_it;
- int sff = map->map_sff;
+ long sff = map->map_sff;
char search_key[MAXNAME + 1];
char linebuf[MAXLINE];
char buf[MAXNAME + 1];
- extern char *get_column __P((char *, int, char, char *, int));
found_it = FALSE;
if (tTd(38, 20))
- printf("text_map_lookup(%s, %s)\n", map->map_mname, name);
+ 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;
- bcopy(name, search_key, buflen);
+ memmove(search_key, name, buflen);
search_key[buflen] = '\0';
if (!bitset(MF_NOFOLDCASE, map->map_mflags))
makelower(search_key);
@@ -3974,7 +5758,7 @@ text_map_lookup(map, name, av, statp)
break;
}
}
- fclose(f);
+ (void) fclose(f);
if (!found_it)
{
*statp = EX_NOTFOUND;
@@ -3994,12 +5778,11 @@ text_map_lookup(map, name, av, statp)
return map_rewrite(map, vp, vsize, av);
}
-
/*
** TEXT_GETCANONNAME -- look up canonical name in hosts file
*/
-bool
+static bool
text_getcanonname(name, hbsize, statp)
char *name;
int hbsize;
@@ -4012,14 +5795,14 @@ text_getcanonname(name, hbsize, statp)
char nbuf[MAXNAME + 1];
if (tTd(38, 20))
- printf("text_getcanonname(%s)\n", name);
+ dprintf("text_getcanonname(%s)\n", name);
if (strlen(name) >= (SIZE_T) sizeof nbuf)
{
*statp = EX_UNAVAILABLE;
return FALSE;
}
- (void) strcpy(nbuf, name);
+ (void) strlcpy(nbuf, name, sizeof nbuf);
shorten_hostname(nbuf);
f = fopen(HostsFile, "r");
@@ -4038,7 +5821,7 @@ text_getcanonname(name, hbsize, statp)
if (linebuf[0] != '\0')
found = extract_canonname(nbuf, linebuf, cbuf, sizeof cbuf);
}
- fclose(f);
+ (void) fclose(f);
if (!found)
{
*statp = EX_NOHOST;
@@ -4047,7 +5830,7 @@ text_getcanonname(name, hbsize, statp)
if ((SIZE_T) hbsize >= strlen(cbuf))
{
- strcpy(name, cbuf);
+ (void) strlcpy(name, cbuf, hbsize);
*statp = EX_OK;
return TRUE;
}
@@ -4074,13 +5857,13 @@ stab_map_lookup(map, name, av, pstat)
register STAB *s;
if (tTd(38, 20))
- printf("stab_lookup(%s, %s)\n",
+ dprintf("stab_lookup(%s, %s)\n",
map->map_mname, name);
s = stab(name, ST_ALIAS, ST_FIND);
if (s != NULL)
- return (s->s_alias);
- return (NULL);
+ return s->s_alias;
+ return NULL;
}
@@ -4116,11 +5899,11 @@ stab_map_open(map, mode)
int mode;
{
FILE *af;
- int sff;
+ long sff;
struct stat st;
if (tTd(38, 2))
- printf("stab_map_open(%s, %s, %d)\n",
+ dprintf("stab_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
mode &= O_ACCMODE;
@@ -4131,9 +5914,9 @@ stab_map_open(map, mode)
}
sff = SFF_ROOTOK|SFF_REGONLY;
- if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
+ if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
sff |= SFF_NOWLINK;
- if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
+ if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
sff |= SFF_SAFEDIRPATH;
af = safefopen(map->map_file, O_RDONLY, 0444, sff);
if (af == NULL)
@@ -4142,7 +5925,7 @@ stab_map_open(map, mode)
if (fstat(fileno(af), &st) >= 0)
map->map_mtime = st.st_mtime;
- fclose(af);
+ (void) fclose(af);
return TRUE;
}
@@ -4165,17 +5948,17 @@ impl_map_lookup(map, name, av, pstat)
int *pstat;
{
if (tTd(38, 20))
- printf("impl_map_lookup(%s, %s)\n",
+ dprintf("impl_map_lookup(%s, %s)\n",
map->map_mname, name);
#ifdef NEWDB
if (bitset(MF_IMPL_HASH, map->map_mflags))
return db_map_lookup(map, name, av, pstat);
-#endif
+#endif /* NEWDB */
#ifdef NDBM
if (bitset(MF_IMPL_NDBM, map->map_mflags))
return ndbm_map_lookup(map, name, av, pstat);
-#endif
+#endif /* NDBM */
return stab_map_lookup(map, name, av, pstat);
}
@@ -4190,16 +5973,16 @@ impl_map_store(map, lhs, rhs)
char *rhs;
{
if (tTd(38, 12))
- printf("impl_map_store(%s, %s, %s)\n",
+ dprintf("impl_map_store(%s, %s, %s)\n",
map->map_mname, lhs, rhs);
#ifdef NEWDB
if (bitset(MF_IMPL_HASH, map->map_mflags))
db_map_store(map, lhs, rhs);
-#endif
+#endif /* NEWDB */
#ifdef NDBM
if (bitset(MF_IMPL_NDBM, map->map_mflags))
ndbm_map_store(map, lhs, rhs);
-#endif
+#endif /* NDBM */
stab_map_store(map, lhs, rhs);
}
@@ -4213,7 +5996,7 @@ impl_map_open(map, mode)
int mode;
{
if (tTd(38, 2))
- printf("impl_map_open(%s, %s, %d)\n",
+ dprintf("impl_map_open(%s, %s, %d)\n",
map->map_mname, map->map_file, mode);
mode &= O_ACCMODE;
@@ -4223,12 +6006,12 @@ impl_map_open(map, mode)
{
# ifdef NDBM_YP_COMPAT
if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
-# endif
+# endif /* NDBM_YP_COMPAT */
return TRUE;
}
else
map->map_mflags &= ~MF_IMPL_HASH;
-#endif
+#endif /* NEWDB */
#ifdef NDBM
map->map_mflags |= MF_IMPL_NDBM;
if (ndbm_map_open(map, mode))
@@ -4237,17 +6020,17 @@ impl_map_open(map, mode)
}
else
map->map_mflags &= ~MF_IMPL_NDBM;
-#endif
+#endif /* NDBM */
#if defined(NEWDB) || defined(NDBM)
if (Verbose)
message("WARNING: cannot open alias database %s%s",
map->map_file,
mode == O_RDONLY ? "; reading text version" : "");
-#else
+#else /* defined(NEWDB) || defined(NDBM) */
if (mode != O_RDONLY)
usrerr("Cannot rebuild aliases: no database format defined");
-#endif
+#endif /* defined(NEWDB) || defined(NDBM) */
if (mode == O_RDONLY)
return stab_map_open(map, mode);
@@ -4265,7 +6048,7 @@ impl_map_close(map)
MAP *map;
{
if (tTd(38, 9))
- printf("impl_map_close(%s, %s, %lx)\n",
+ dprintf("impl_map_close(%s, %s, %lx)\n",
map->map_mname, map->map_file, map->map_mflags);
#ifdef NEWDB
if (bitset(MF_IMPL_HASH, map->map_mflags))
@@ -4273,7 +6056,7 @@ impl_map_close(map)
db_map_close(map);
map->map_mflags &= ~MF_IMPL_HASH;
}
-#endif
+#endif /* NEWDB */
#ifdef NDBM
if (bitset(MF_IMPL_NDBM, map->map_mflags))
@@ -4281,7 +6064,7 @@ impl_map_close(map)
ndbm_map_close(map);
map->map_mflags &= ~MF_IMPL_NDBM;
}
-#endif
+#endif /* NDBM */
}
/*
** User map class.
@@ -4301,7 +6084,7 @@ user_map_open(map, mode)
int mode;
{
if (tTd(38, 2))
- printf("user_map_open(%s, %d)\n",
+ dprintf("user_map_open(%s, %d)\n",
map->map_mname, mode);
mode &= O_ACCMODE;
@@ -4310,16 +6093,17 @@ user_map_open(map, mode)
/* issue a pseudo-error message */
#ifdef ENOSYS
errno = ENOSYS;
-#else
+#else /* ENOSYS */
# ifdef EFTYPE
errno = EFTYPE;
-# else
+# else /* EFTYPE */
errno = ENXIO;
-# endif
-#endif
+# endif /* EFTYPE */
+#endif /* ENOSYS */
return FALSE;
}
if (map->map_valcolnm == NULL)
+ /* EMPTY */
/* nothing */ ;
else if (strcasecmp(map->map_valcolnm, "name") == 0)
map->map_valcolno = 1;
@@ -4361,7 +6145,7 @@ user_map_lookup(map, key, av, statp)
auto bool fuzzy;
if (tTd(38, 20))
- printf("user_map_lookup(%s, %s)\n",
+ dprintf("user_map_lookup(%s, %s)\n",
map->map_mname, key);
pw = finduser(key, &fuzzy);
@@ -4386,12 +6170,12 @@ user_map_lookup(map, key, av, statp)
break;
case 3:
- snprintf(buf, sizeof buf, "%d", pw->pw_uid);
+ snprintf(buf, sizeof buf, "%d", (int) pw->pw_uid);
rwval = buf;
break;
case 4:
- snprintf(buf, sizeof buf, "%d", pw->pw_gid);
+ snprintf(buf, sizeof buf, "%d", (int) pw->pw_gid);
rwval = buf;
break;
@@ -4426,16 +6210,17 @@ prog_map_lookup(map, name, av, statp)
int *statp;
{
int i;
- register char *p;
+ int save_errno;
int fd;
+ int status;
auto pid_t pid;
+ register char *p;
char *rval;
- int stat;
char *argv[MAXPV + 1];
char buf[MAXLINE];
if (tTd(38, 20))
- printf("prog_map_lookup(%s, %s) %s\n",
+ dprintf("prog_map_lookup(%s, %s) %s\n",
map->map_mname, name, map->map_file);
i = 0;
@@ -4454,10 +6239,10 @@ prog_map_lookup(map, name, av, statp)
argv[i] = NULL;
if (tTd(38, 21))
{
- printf("prog_open:");
+ dprintf("prog_open:");
for (i = 0; argv[i] != NULL; i++)
- printf(" %s", argv[i]);
- printf("\n");
+ dprintf(" %s", argv[i]);
+ dprintf("\n");
}
(void) blocksignal(SIGCHLD);
pid = prog_open(argv, &fd, CurEnv);
@@ -4467,7 +6252,7 @@ prog_map_lookup(map, name, av, statp)
syserr("prog_map_lookup(%s) failed (%s) -- closing",
map->map_mname, errstring(errno));
else if (tTd(38, 9))
- printf("prog_map_lookup(%s) failed (%s) -- closing",
+ dprintf("prog_map_lookup(%s) failed (%s) -- closing",
map->map_mname, errstring(errno));
map->map_mflags &= ~(MF_VALID|MF_OPEN);
*statp = EX_OSFILE;
@@ -4483,7 +6268,7 @@ prog_map_lookup(map, name, av, statp)
else if (i == 0)
{
if (tTd(38, 20))
- printf("prog_map_lookup(%s): empty answer\n",
+ dprintf("prog_map_lookup(%s): empty answer\n",
map->map_mname);
rval = NULL;
}
@@ -4506,26 +6291,28 @@ prog_map_lookup(map, name, av, statp)
}
/* wait for the process to terminate */
- close(fd);
- stat = waitfor(pid);
+ (void) close(fd);
+ status = waitfor(pid);
+ save_errno = errno;
(void) releasesignal(SIGCHLD);
+ errno = save_errno;
- if (stat == -1)
+ if (status == -1)
{
syserr("prog_map_lookup(%s): wait error %s\n",
map->map_mname, errstring(errno));
*statp = EX_SOFTWARE;
rval = NULL;
}
- else if (WIFEXITED(stat))
+ else if (WIFEXITED(status))
{
- if ((*statp = WEXITSTATUS(stat)) != EX_OK)
+ if ((*statp = WEXITSTATUS(status)) != EX_OK)
rval = NULL;
}
else
{
syserr("prog_map_lookup(%s): child died on signal %d",
- map->map_mname, stat);
+ map->map_mname, status);
*statp = EX_UNAVAILABLE;
rval = NULL;
}
@@ -4560,7 +6347,7 @@ seq_map_parse(map, ap)
int maxmap;
if (tTd(38, 2))
- printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
+ dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
maxmap = 0;
while (*ap != '\0')
{
@@ -4570,7 +6357,9 @@ seq_map_parse(map, ap)
/* find beginning of map name */
while (isascii(*ap) && isspace(*ap))
ap++;
- for (p = ap; isascii(*p) && isalnum(*p); p++)
+ for (p = ap;
+ (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
+ p++)
continue;
if (*p != '\0')
*p++ = '\0';
@@ -4624,16 +6413,16 @@ switch_map_open(map, mode)
char *maptype[MAXMAPSTACK];
if (tTd(38, 2))
- printf("switch_map_open(%s, %s, %d)\n",
+ 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))
{
- printf("\tswitch_map_find => %d\n", nmaps);
+ dprintf("\tswitch_map_find => %d\n", nmaps);
for (mapno = 0; mapno < nmaps; mapno++)
- printf("\t\t%s\n", maptype[mapno]);
+ dprintf("\t\t%s\n", maptype[mapno]);
}
if (nmaps <= 0 || nmaps > MAXMAPSTACK)
return FALSE;
@@ -4657,7 +6446,7 @@ switch_map_open(map, mode)
{
map->map_stack[mapno] = &s->s_map;
if (tTd(38, 4))
- printf("\tmap_stack[%d] = %s:%s\n",
+ dprintf("\tmap_stack[%d] = %s:%s\n",
mapno, s->s_map.map_class->map_cname,
nbuf);
}
@@ -4677,7 +6466,7 @@ seq_map_close(map)
int mapno;
if (tTd(38, 9))
- printf("seq_map_close(%s)\n", map->map_mname);
+ dprintf("seq_map_close(%s)\n", map->map_mname);
for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
{
@@ -4707,7 +6496,7 @@ seq_map_lookup(map, key, args, pstat)
bool tempfail = FALSE;
if (tTd(38, 20))
- printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
+ dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
{
@@ -4716,7 +6505,8 @@ seq_map_lookup(map, key, args, pstat)
if (mm == NULL)
continue;
- if (!bitset(MF_OPEN, mm->map_mflags))
+ if (!bitset(MF_OPEN, mm->map_mflags) &&
+ !openmap(mm))
{
if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
{
@@ -4759,7 +6549,7 @@ seq_map_store(map, key, val)
int mapno;
if (tTd(38, 12))
- printf("seq_map_store(%s, %s, %s)\n",
+ dprintf("seq_map_store(%s, %s, %s)\n",
map->map_mname, key, val);
for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
@@ -4840,6 +6630,39 @@ MAPCLASS BogusMapClass =
null_map_open, null_map_close,
};
/*
+** MACRO modules
+*/
+
+char *
+macro_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ int mid;
+
+ if (tTd(38, 20))
+ dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
+ name == NULL ? "NULL" : name);
+
+ if (name == NULL ||
+ *name == '\0' ||
+ (mid = macid(name, NULL)) == '\0')
+ {
+ *statp = EX_CONFIG;
+ return NULL;
+ }
+
+ if (av[1] == NULL)
+ define(mid, NULL, CurEnv);
+ else
+ define(mid, newstr(av[1]), CurEnv);
+
+ *statp = EX_OK;
+ return "";
+}
+ /*
** REGEX modules
*/
@@ -4854,13 +6677,13 @@ MAPCLASS BogusMapClass =
# define ERRBUF_SIZE 80
# define MAX_MATCH 32
-# define xnalloc(s) memset(xalloc(s), 0, s);
+# define xnalloc(s) memset(xalloc(s), '\0', s);
struct regex_map
{
- regex_t pattern_buf; /* xalloc it */
+ regex_t regex_pattern_buf; /* xalloc it */
int *regex_subfields; /* move to type MAP */
- char *delim; /* move to type MAP */
+ char *regex_delim; /* move to type MAP */
};
static int
@@ -4927,17 +6750,17 @@ regex_map_init(map, ap)
static char defdstr[] = { (char)DEFAULT_DELIM, '\0' };
if (tTd(38, 2))
- printf("regex_map_init: mapname '%s', args '%s'\n",
- map->map_mname, ap);
+ 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(struct regex_map));
+ map_p = (struct regex_map *) xnalloc(sizeof *map_p);
for (;;)
- {
+ {
while (isascii(*p) && isspace(*p))
p++;
if (*p != '-')
@@ -4963,7 +6786,7 @@ regex_map_init(map, ap)
break;
case 'd': /* delimiter */
- map_p->delim = ++p;
+ map_p->regex_delim = ++p;
break;
case 'a': /* map append */
@@ -4974,21 +6797,30 @@ regex_map_init(map, ap)
map->map_mflags |= MF_MATCHONLY;
break;
+ case 'S':
+ map->map_spacesub = *++p;
+ break;
+
+ case 'D':
+ map->map_mflags |= MF_DEFER;
+ break;
+
}
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p != '\0')
- *p++ = '\0';
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
}
if (tTd(38, 3))
- printf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
+ dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
- if ((regerr = regcomp(&(map_p->pattern_buf), p, pflags)) != 0)
+ if ((regerr = regcomp(&(map_p->regex_pattern_buf), p, pflags)) != 0)
{
/* Errorhandling */
char errbuf[ERRBUF_SIZE];
- regerror(regerr, &(map_p->pattern_buf), errbuf, ERRBUF_SIZE);
+ (void) regerror(regerr, &(map_p->regex_pattern_buf),
+ errbuf, ERRBUF_SIZE);
syserr("pattern-compile-error: %s\n", errbuf);
free(map_p);
return FALSE;
@@ -4996,21 +6828,22 @@ regex_map_init(map, ap)
if (map->map_app != NULL)
map->map_app = newstr(map->map_app);
- if (map_p->delim != NULL)
- map_p->delim = newstr(map_p->delim);
+ if (map_p->regex_delim != NULL)
+ map_p->regex_delim = newstr(map_p->regex_delim);
else
- map_p->delim = defdstr;
+ map_p->regex_delim = defdstr;
if (!bitset(REG_NOSUB, pflags))
{
/* substring matching */
int substrings;
- int *fields = (int *)xalloc(sizeof(int) * (MAX_MATCH + 1));
+ int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
- substrings = map_p->pattern_buf.re_nsub + 1;
+ substrings = map_p->regex_pattern_buf.re_nsub + 1;
if (tTd(38, 3))
- printf("regex_map_init: nr of substrings %d\n", substrings);
+ dprintf("regex_map_init: nr of substrings %d\n",
+ substrings);
if (substrings >= MAX_MATCH)
{
@@ -5027,7 +6860,7 @@ regex_map_init(map, ap)
}
else
{
- /* set default fields */
+ /* set default fields */
int i;
for (i = 0; i < substrings; i++)
@@ -5039,10 +6872,10 @@ regex_map_init(map, ap)
{
int *ip;
- printf("regex_map_init: subfields");
+ dprintf("regex_map_init: subfields");
for (ip = fields; *ip != END_OF_FIELDS; ip++)
- printf(" %d", *ip);
- printf("\n");
+ dprintf(" %d", *ip);
+ dprintf("\n");
}
}
map->map_db1 = (ARBPTR_T)map_p; /* dirty hack */
@@ -5078,13 +6911,14 @@ regex_map_lookup(map, name, av, statp)
{
char **cpp;
- printf("regex_map_lookup: key '%s'\n", name);
- for (cpp = av; cpp && *cpp; cpp++)
- printf("regex_map_lookup: arg '%s'\n", *cpp);
+ dprintf("regex_map_lookup: key '%s'\n", name);
+ for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
+ dprintf("regex_map_lookup: arg '%s'\n", *cpp);
}
map_p = (struct regex_map *)(map->map_db1);
- reg_res = regexec(&(map_p->pattern_buf), name, MAX_MATCH, pmatch, 0);
+ reg_res = regexec(&(map_p->regex_pattern_buf),
+ name, MAX_MATCH, pmatch, 0);
if (bitset(MF_REGEX_NOT, map->map_mflags))
{
@@ -5115,7 +6949,7 @@ regex_map_lookup(map, name, av, statp)
if (av[1] != NULL)
{
if (parse_fields(av[1], fields, MAX_MATCH + 1,
- (int) map_p->pattern_buf.re_nsub + 1) == -1)
+ (int) map_p->regex_pattern_buf.re_nsub + 1) == -1)
{
*statp = EX_CONFIG;
return NULL;
@@ -5129,7 +6963,7 @@ regex_map_lookup(map, name, av, statp)
{
if (!first)
{
- for (sp = map_p->delim; *sp; sp++)
+ for (sp = map_p->regex_delim; *sp; sp++)
{
if (dp < ldp)
*dp++ = *sp;
@@ -5148,12 +6982,12 @@ regex_map_lookup(map, name, av, statp)
{
if (dp < ldp)
{
- if(bslashmode)
- {
+ if (bslashmode)
+ {
*dp++ = *sp;
bslashmode = FALSE;
}
- else if(quotemode && *sp != '"' &&
+ else if (quotemode && *sp != '"' &&
*sp != '\\')
{
*dp++ = *sp;
@@ -5194,9 +7028,9 @@ regex_map_lookup(map, name, av, statp)
if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
bslashmode || spacecnt != 0)
{
- sm_syslog(LOG_WARNING, NOQID,
- "Warning: regex may cause prescan() failure map=%s lookup=%s",
- map->map_mname, name);
+ sm_syslog(LOG_WARNING, NOQID,
+ "Warning: regex may cause prescan() failure map=%s lookup=%s",
+ map->map_mname, name);
return NULL;
}
@@ -5207,3 +7041,189 @@ regex_map_lookup(map, name, av, statp)
return regex_map_rewrite(map, "", (size_t)0, av);
}
#endif /* MAP_REGEX */
+ /*
+** NSD modules
+*/
+#ifdef MAP_NSD
+
+# include <ndbm.h>
+# define _DATUM_DEFINED
+# include <ns_api.h>
+
+typedef struct ns_map_list
+{
+ ns_map_t *map;
+ char *mapname;
+ struct ns_map_list *next;
+} ns_map_list_t;
+
+static ns_map_t *
+ns_map_t_find(mapname)
+ char *mapname;
+{
+ static ns_map_list_t *ns_maps = NULL;
+ ns_map_list_t *ns_map;
+
+ /* walk the list of maps looking for the correctly named map */
+ for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
+ {
+ if (strcmp(ns_map->mapname, mapname) == 0)
+ break;
+ }
+
+ /* if we are looking at a NULL ns_map_list_t, then create a new one */
+ if (ns_map == NULL)
+ {
+ ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map);
+ ns_map->mapname = newstr(mapname);
+ ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map);
+ ns_map->next = ns_maps;
+ ns_maps = ns_map;
+ }
+ return ns_map->map;
+}
+
+char *
+nsd_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ int buflen;
+ char *p;
+ ns_map_t *ns_map;
+ char keybuf[MAXNAME + 1];
+ char buf[MAXLINE];
+
+ if (tTd(38, 20))
+ dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
+
+ buflen = strlen(name);
+ if (buflen > sizeof keybuf - 1)
+ buflen = sizeof keybuf - 1;
+ memmove(keybuf, name, buflen);
+ keybuf[buflen] = '\0';
+ if (!bitset(MF_NOFOLDCASE, map->map_mflags))
+ makelower(keybuf);
+
+ ns_map = ns_map_t_find(map->map_file);
+ if (ns_map == NULL)
+ {
+ if (tTd(38, 20))
+ dprintf("nsd_map_t_find failed\n");
+ return NULL;
+ }
+
+ if (ns_lookup(ns_map, NULL, map->map_file,
+ keybuf, NULL, buf, MAXLINE) == NULL)
+ return NULL;
+
+ /* Null out trailing \n */
+ if ((p = strchr(buf, '\n')) != NULL)
+ *p = '\0';
+
+ return map_rewrite(map, buf, strlen(buf), av);
+}
+#endif /* MAP_NSD */
+
+char *
+arith_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ long r;
+ long v[2];
+ bool res = FALSE;
+ bool boolres;
+ static char result[16];
+ char **cpp;
+
+ if (tTd(38, 2))
+ {
+ dprintf("arith_map_lookup: key '%s'\n", name);
+ for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
+ dprintf("arith_map_lookup: arg '%s'\n", *cpp);
+ }
+ r = 0;
+ boolres = FALSE;
+ cpp = av;
+ *statp = EX_OK;
+
+ /*
+ ** read arguments for arith map
+ ** - 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)
+ {
+#if _FFR_ARITH
+ case '|':
+ r = v[0] | v[1];
+ break;
+
+ case '&':
+ r = v[0] & v[1];
+ break;
+
+ case '%':
+ if (v[1] == 0)
+ return NULL;
+ r = v[0] % v[1];
+ break;
+#endif /* _FFR_ARITH */
+
+ case '+':
+ r = v[0] + v[1];
+ break;
+
+ case '-':
+ r = v[0] - v[1];
+ break;
+
+ case '*':
+ r = v[0] * v[1];
+ break;
+
+ case '/':
+ if (v[1] == 0)
+ return NULL;
+ r = v[0] / v[1];
+ break;
+
+ case 'l':
+ res = v[0] < v[1];
+ boolres = TRUE;
+ break;
+
+ case '=':
+ res = v[0] == v[1];
+ boolres = TRUE;
+ break;
+
+ default:
+ /* XXX */
+ *statp = EX_CONFIG;
+ if (LogLevel > 10)
+ sm_syslog(LOG_WARNING, NOQID,
+ "arith_map: unknown operator %c",
+ isprint(*name) ? *name : '?');
+ return NULL;
+ }
+ if (boolres)
+ snprintf(result, sizeof result, res ? "TRUE" : "FALSE");
+ else
+ snprintf(result, sizeof result, "%ld", r);
+ return result;
+ }
+ *statp = EX_CONFIG;
+ return NULL;
+}
diff --git a/contrib/sendmail/src/milter.c b/contrib/sendmail/src/milter.c
new file mode 100644
index 0000000..82f1574
--- /dev/null
+++ b/contrib/sendmail/src/milter.c
@@ -0,0 +1,3402 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: milter.c,v 8.50.4.30 2000/07/18 07:24:51 gshapiro Exp $";
+#endif /* ! lint */
+
+#if _FFR_MILTER
+
+# include <sendmail.h>
+# include <errno.h>
+# include <sys/time.h>
+
+# if NETINET || NETINET6
+# include <arpa/inet.h>
+# endif /* NETINET || NETINET6 */
+
+
+static void milter_error __P((struct milter *));
+static int milter_open __P((struct milter *, bool, ENVELOPE *));
+static void milter_parse_timeouts __P((char *, struct milter *));
+
+static char *MilterConnectMacros[MAXFILTERMACROS + 1];
+static char *MilterHeloMacros[MAXFILTERMACROS + 1];
+static char *MilterEnvFromMacros[MAXFILTERMACROS + 1];
+static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
+
+# define MILTER_CHECK_DONE_MSG() \
+ if (*state == SMFIR_REPLYCODE || \
+ *state == SMFIR_REJECT || \
+ *state == SMFIR_DISCARD || \
+ *state == SMFIR_TEMPFAIL) \
+ { \
+ /* Abort the filters to let them know we are done with msg */ \
+ milter_abort(e); \
+ }
+
+# define MILTER_CHECK_ERROR(action) \
+ if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \
+ *state = SMFIR_TEMPFAIL; \
+ else if (bitnset(SMF_REJECT, m->mf_flags)) \
+ *state = SMFIR_REJECT; \
+ else \
+ action;
+
+# define MILTER_CHECK_REPLYCODE(default) \
+ if (response == NULL || \
+ strlen(response) + 1 != (size_t) rlen || \
+ rlen < 3 || \
+ (response[0] != '4' && response[0] != '5') || \
+ !isascii(response[1]) || !isdigit(response[1]) || \
+ !isascii(response[2]) || !isdigit(response[2])) \
+ { \
+ if (response != NULL) \
+ free(response); \
+ response = newstr(default); \
+ } \
+ else \
+ { \
+ char *ptr = response; \
+ \
+ /* Check for unprotected %'s in the string */ \
+ while (*ptr != '\0') \
+ { \
+ if (*ptr == '%' && *++ptr != '%') \
+ { \
+ free(response); \
+ response = newstr(default); \
+ break; \
+ } \
+ ptr++; \
+ } \
+ }
+
+# define MILTER_DF_ERROR(msg) \
+{ \
+ int save_errno = errno; \
+ \
+ if (tTd(64, 5)) \
+ { \
+ dprintf(msg, dfname, errstring(save_errno)); \
+ dprintf("\n"); \
+ } \
+ if (LogLevel > 0) \
+ sm_syslog(LOG_ERR, e->e_id, msg, dfname, errstring(save_errno)); \
+ if (SuperSafe) \
+ { \
+ if (e->e_dfp != NULL) \
+ { \
+ (void) fclose(e->e_dfp); \
+ e->e_dfp = NULL; \
+ } \
+ e->e_flags &= ~EF_HAS_DF; \
+ } \
+ errno = save_errno; \
+}
+
+/*
+** MILTER_TIMEOUT -- make sure socket is ready in time
+**
+** Parameters:
+** routine -- routine name for debug/logging
+** secs -- number of seconds in timeout
+** write -- waiting to read or write?
+**
+** Assumes 'm' is a milter structure for the current socket.
+*/
+
+
+# define MILTER_TIMEOUT(routine, secs, write) \
+{ \
+ int ret; \
+ int save_errno; \
+ fd_set fds; \
+ struct timeval tv; \
+ \
+ if (m->mf_sock >= FD_SETSIZE) \
+ { \
+ if (tTd(64, 5)) \
+ dprintf("%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
+ routine, m->mf_name, m->mf_sock, FD_SETSIZE); \
+ if (LogLevel > 0) \
+ sm_syslog(LOG_ERR, e->e_id, \
+ "%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
+ routine, m->mf_name, m->mf_sock, FD_SETSIZE); \
+ milter_error(m); \
+ return NULL; \
+ } \
+ \
+ FD_ZERO(&fds); \
+ FD_SET(m->mf_sock, &fds); \
+ tv.tv_sec = secs; \
+ tv.tv_usec = 0; \
+ ret = select(m->mf_sock + 1, \
+ 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); \
+ 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_syslog(LOG_ERR, e->e_id, \
+ "%s(%s): select: %s\n", \
+ routine, m->mf_name, errstring(save_errno)); \
+ milter_error(m); \
+ return NULL; \
+ \
+ default: \
+ if (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_syslog(LOG_ERR, e->e_id, \
+ "%s(%s): socket not ready\n", \
+ m->mf_name, routine); \
+ milter_error(m); \
+ return NULL; \
+ } \
+}
+
+
+/*
+** Low level functions
+*/
+
+ /*
+** MILTER_READ -- read from a remote milter filter
+**
+** Parameters:
+** m -- milter to read from.
+** cmd -- return param for command read.
+** rlen -- return length of response string.
+** to -- timeout in seconds.
+** e -- current envelope.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+static char *
+milter_sysread(m, buf, sz, to, e)
+ struct milter *m;
+ char *buf;
+ ssize_t sz;
+ time_t to;
+ ENVELOPE *e;
+{
+ time_t readstart;
+ ssize_t len, curl;
+
+ curl = 0;
+
+ if (to > 0)
+ readstart = curtime();
+
+ for (;;)
+ {
+ if (to > 0)
+ {
+ time_t now;
+
+ now = curtime();
+ if (now - readstart >= to)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_read(%s): timeout before data read\n",
+ m->mf_name);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_read(%s): timeout before data read\n",
+ m->mf_name);
+ milter_error(m);
+ return NULL;
+ }
+ to -= now - readstart;
+ readstart = now;
+ MILTER_TIMEOUT("milter_read", to, FALSE);
+ }
+
+ len = read(m->mf_sock, buf + curl, sz - curl);
+
+ if (len < 0)
+ {
+ int save_errno = errno;
+
+ if (tTd(64, 5))
+ dprintf("milter_read(%s): read returned %ld: %s\n",
+ m->mf_name, (long) len,
+ errstring(save_errno));
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_read(%s): read returned %ld: %s",
+ m->mf_name, (long) len,
+ errstring(save_errno));
+ milter_error(m);
+ return NULL;
+ }
+
+ curl += len;
+ if (len == 0 || len >= sz)
+ break;
+
+ }
+
+ if (curl != sz)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_read(%s): read returned %ld, expecting %ld\n",
+ m->mf_name, (long) curl, (long) sz);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_read(%s): read returned %ld, expecting %ld",
+ m->mf_name, (long) curl, (long) sz);
+ milter_error(m);
+ return NULL;
+ }
+ return buf;
+}
+
+static char *
+milter_read(m, cmd, rlen, to, e)
+ struct milter *m;
+ char *cmd;
+ ssize_t *rlen;
+ time_t to;
+ ENVELOPE *e;
+{
+ time_t readstart;
+ ssize_t expl;
+ mi_int32 i;
+ char *buf;
+ char data[MILTER_LEN_BYTES + 1];
+
+ *rlen = 0;
+ *cmd = '\0';
+
+ if (to > 0)
+ readstart = curtime();
+
+ if (milter_sysread(m, data, sizeof data, to, e) == NULL)
+ return NULL;
+
+ /* reset timeout */
+ if (to > 0)
+ {
+ time_t now;
+
+ now = curtime();
+ if (now - readstart >= to)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_read(%s): timeout before data read\n",
+ m->mf_name);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_read(%s): timeout before data read\n",
+ m->mf_name);
+ milter_error(m);
+ return NULL;
+ }
+ to -= now - readstart;
+ }
+
+ *cmd = data[MILTER_LEN_BYTES];
+ data[MILTER_LEN_BYTES] = '\0';
+ (void) memcpy(&i, data, MILTER_LEN_BYTES);
+ expl = ntohl(i) - 1;
+
+ if (tTd(64, 25))
+ dprintf("milter_read(%s): expecting %ld bytes\n",
+ m->mf_name, (long) expl);
+
+ if (expl < 0)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_read(%s): read size %ld out of range\n",
+ m->mf_name, (long) expl);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_read(%s): read size %ld out of range",
+ m->mf_name, (long) expl);
+ milter_error(m);
+ return NULL;
+ }
+
+ if (expl == 0)
+ return NULL;
+
+ buf = (char *)xalloc(expl);
+
+ if (milter_sysread(m, buf, expl, to, e) == NULL)
+ {
+ free(buf);
+ return NULL;
+ }
+
+ if (tTd(64, 50))
+ 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:
+** m -- milter to read from.
+** cmd -- command to send.
+** buf -- optional command data.
+** len -- length of buf.
+** to -- timeout in seconds.
+** e -- current envelope.
+**
+** Returns:
+** buf if successful, NULL otherwise
+** Not actually used anywhere but function prototype
+** must match milter_read()
+*/
+
+static char *
+milter_write(m, cmd, buf, len, to, e)
+ struct milter *m;
+ char cmd;
+ char *buf;
+ ssize_t len;
+ time_t to;
+ ENVELOPE *e;
+{
+ time_t writestart = (time_t) 0;
+ ssize_t sl, i;
+ mi_int32 nl;
+ char data[MILTER_LEN_BYTES + 1];
+
+ if (len < 0 || len > MILTER_CHUNK_SIZE)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_write(%s): length %ld out of range\n",
+ m->mf_name, (long) len);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_write(%s): length %ld out of range",
+ m->mf_name, (long) len);
+ milter_error(m);
+ return NULL;
+ }
+
+ if (tTd(64, 20))
+ 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);
+ data[MILTER_LEN_BYTES] = cmd;
+ sl = MILTER_LEN_BYTES + 1;
+
+ if (to > 0)
+ {
+ writestart = curtime();
+ MILTER_TIMEOUT("milter_write", to, TRUE);
+ }
+
+ /* use writev() instead to send the whole stuff at once? */
+ i = write(m->mf_sock, (void *) data, sl);
+ if (i != sl)
+ {
+ 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_syslog(LOG_ERR, e->e_id,
+ "milter_write(%s): write(%c) returned %ld, expected %ld: %s",
+ m->mf_name, cmd, (long) i, (long) sl,
+ errstring(save_errno));
+ milter_error(m);
+ return buf;
+ }
+
+ if (len <= 0 || buf == NULL)
+ return buf;
+
+ if (tTd(64, 50))
+ dprintf("milter_write(%s): Sending %*s\n",
+ m->mf_name, (int) len, buf);
+
+ if (to > 0)
+ {
+ time_t now;
+
+ now = curtime();
+ if (now - writestart >= to)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_write(%s): timeout before data send\n",
+ m->mf_name);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_write(%s): timeout before data send\n",
+ m->mf_name);
+ milter_error(m);
+ return NULL;
+ }
+ else
+ {
+ to -= now - writestart;
+ MILTER_TIMEOUT("milter_write", to, TRUE);
+ }
+ }
+
+ i = write(m->mf_sock, (void *) buf, len);
+ if (i != len)
+ {
+ 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_syslog(LOG_ERR, e->e_id,
+ "milter_write(%s): write(%c) returned %ld, expected %ld: %s",
+ m->mf_name, cmd, (long) i, (long) len,
+ errstring(save_errno));
+ milter_error(m);
+ return NULL;
+ }
+ return buf;
+}
+
+/*
+** Utility functions
+*/
+
+ /*
+** MILTER_OPEN -- connect to remote milter filter
+**
+** Parameters:
+** m -- milter to connect to.
+** parseonly -- parse but don't connect.
+** e -- current envelope.
+**
+** Returns:
+** connected socket if sucessful && !parseonly,
+** 0 upon parse success if parseonly,
+** -1 otherwise.
+*/
+
+static int
+milter_open(m, parseonly, e)
+ struct milter *m;
+ bool parseonly;
+ ENVELOPE *e;
+{
+ int sock = 0;
+ SOCKADDR_LEN_T addrlen = 0;
+ int addrno = 0;
+ int save_errno;
+ char *p;
+ char *colon;
+ char *at;
+ struct hostent *hp = NULL;
+ SOCKADDR addr;
+
+ 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);
+ if (parseonly)
+ syserr("X%s: empty or missing socket information",
+ m->mf_name);
+ else if (LogLevel > 10)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: empty or missing socket information",
+ m->mf_name);
+ milter_error(m);
+ return -1;
+ }
+
+ /* protocol:filename or protocol:port@host */
+ p = m->mf_conn;
+ colon = strchr(p, ':');
+ if (colon != NULL)
+ {
+ *colon = '\0';
+
+ if (*p == '\0')
+ {
+# if NETUNIX
+ /* default to AF_UNIX */
+ addr.sa.sa_family = AF_UNIX;
+# else /* NETUNIX */
+# if NETINET
+ /* default to AF_INET */
+ addr.sa.sa_family = AF_INET;
+# else /* NETINET */
+# if NETINET6
+ /* default to AF_INET6 */
+ addr.sa.sa_family = AF_INET6;
+# else /* NETINET6 */
+ /* no protocols available */
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: no valid socket protocols available",
+ m->mf_name);
+ milter_error(m);
+ return -1;
+# endif /* NETINET6 */
+# endif /* NETINET */
+# endif /* NETUNIX */
+ }
+# if NETUNIX
+ else if (strcasecmp(p, "unix") == 0 ||
+ strcasecmp(p, "local") == 0)
+ addr.sa.sa_family = AF_UNIX;
+# endif /* NETUNIX */
+# if NETINET
+ else if (strcasecmp(p, "inet") == 0)
+ addr.sa.sa_family = AF_INET;
+# endif /* NETINET */
+# if NETINET6
+ else if (strcasecmp(p, "inet6") == 0)
+ addr.sa.sa_family = AF_INET6;
+# endif /* NETINET6 */
+ else
+ {
+# ifdef EPROTONOSUPPORT
+ errno = EPROTONOSUPPORT;
+# else /* EPROTONOSUPPORT */
+ errno = EINVAL;
+# endif /* EPROTONOSUPPORT */
+ if (tTd(64, 5))
+ 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)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: unknown socket type %s",
+ m->mf_name, p);
+ milter_error(m);
+ return -1;
+ }
+ *colon++ = ':';
+ }
+ else
+ {
+ /* default to AF_UNIX */
+ addr.sa.sa_family = AF_UNIX;
+ colon = p;
+ }
+
+# if NETUNIX
+ if (addr.sa.sa_family == AF_UNIX)
+ {
+ long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
+
+ at = colon;
+ if (strlen(colon) >= sizeof addr.sunix.sun_path)
+ {
+ if (tTd(64, 5))
+ 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)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: local socket name %s too long",
+ m->mf_name, colon);
+ milter_error(m);
+ return -1;
+ }
+ errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
+ S_IRUSR|S_IWUSR, NULL);
+
+ /* if just parsing .cf file, socket doesn't need to exist */
+ if (parseonly && errno == ENOENT)
+ {
+ if (OpMode == MD_DAEMON ||
+ OpMode == MD_FGDAEMON)
+ fprintf(stderr,
+ "WARNING: X%s: local socket name %s missing\n",
+ m->mf_name, colon);
+ }
+ else if (errno != 0)
+ {
+ /* if not safe, don't create */
+ save_errno = errno;
+ if (tTd(64, 5))
+ dprintf("X%s: local socket name %s unsafe\n",
+ m->mf_name, colon);
+ errno = save_errno;
+ if (parseonly)
+ {
+ if (OpMode == MD_DAEMON ||
+ OpMode == MD_FGDAEMON ||
+ OpMode == MD_SMTP)
+ syserr("X%s: local socket name %s unsafe",
+ m->mf_name, colon);
+ }
+ else if (LogLevel > 10)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: local socket name %s unsafe",
+ m->mf_name, colon);
+ milter_error(m);
+ return -1;
+ }
+
+ (void) 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 NETINET
+ || addr.sa.sa_family == AF_INET
+# endif /* NETINET */
+# if NETINET6
+ || addr.sa.sa_family == AF_INET6
+# endif /* NETINET6 */
+ )
+ {
+ u_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",
+ m->mf_name, colon);
+ if (parseonly)
+ syserr("X%s: bad address %s (expected port@host)",
+ m->mf_name, colon);
+ else if (LogLevel > 10)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: bad address %s (expected port@host)",
+ m->mf_name, colon);
+ milter_error(m);
+ return -1;
+ }
+ *at = '\0';
+ if (isascii(*colon) && isdigit(*colon))
+ port = htons((u_short) atoi(colon));
+ else
+ {
+# ifdef NO_GETSERVBYNAME
+ if (tTd(64, 5))
+ 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)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: invalid port number %s",
+ m->mf_name, colon);
+ milter_error(m);
+ return -1;
+# else /* NO_GETSERVBYNAME */
+ register struct servent *sp;
+
+ sp = getservbyname(colon, "tcp");
+ if (sp == NULL)
+ {
+ save_errno = errno;
+ if (tTd(64, 5))
+ 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)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: unknown port name %s",
+ m->mf_name, colon);
+ milter_error(m);
+ return -1;
+ }
+ port = sp->s_port;
+# endif /* NO_GETSERVBYNAME */
+ }
+ *at++ = '@';
+ if (*at == '[')
+ {
+ char *end;
+
+ end = strchr(at, ']');
+ if (end != NULL)
+ {
+ bool found = FALSE;
+# if NETINET
+ unsigned long hid = INADDR_NONE;
+# endif /* NETINET */
+# if NETINET6
+ struct sockaddr_in6 hid6;
+# endif /* NETINET6 */
+
+ *end = '\0';
+# if NETINET
+ if (addr.sa.sa_family == AF_INET &&
+ (hid = inet_addr(&at[1])) != INADDR_NONE)
+ {
+ addr.sin.sin_addr.s_addr = hid;
+ addr.sin.sin_port = port;
+ 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)
+ {
+ addr.sin6.sin6_addr = hid6.sin6_addr;
+ addr.sin6.sin6_port = port;
+ found = TRUE;
+ }
+# endif /* NETINET6 */
+ *end = ']';
+ if (!found)
+ {
+ if (tTd(64, 5))
+ 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)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: Invalid numeric domain spec \"%s\"",
+ m->mf_name, at);
+ milter_error(m);
+ return -1;
+ }
+ }
+ else
+ {
+ if (tTd(64, 5))
+ 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)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: Invalid numeric domain spec \"%s\"",
+ m->mf_name, at);
+ milter_error(m);
+ return -1;
+ }
+ }
+ else
+ {
+ hp = sm_gethostbyname(at, addr.sa.sa_family);
+ if (hp == NULL)
+ {
+ save_errno = errno;
+ if (tTd(64, 5))
+ 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)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: Unknown host name %s",
+ m->mf_name, at);
+ milter_error(m);
+ return -1;
+ }
+ addr.sa.sa_family = hp->h_addrtype;
+ switch (hp->h_addrtype)
+ {
+# if NETINET
+ case AF_INET:
+ memmove(&addr.sin.sin_addr,
+ hp->h_addr,
+ INADDRSZ);
+ addr.sin.sin_port = port;
+ addrlen = sizeof (struct sockaddr_in);
+ addrno = 1;
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ memmove(&addr.sin6.sin6_addr,
+ hp->h_addr,
+ IN6ADDRSZ);
+ addr.sin6.sin6_port = port;
+ addrlen = sizeof (struct sockaddr_in6);
+ addrno = 1;
+ break;
+# endif /* NETINET6 */
+
+ default:
+ if (tTd(64, 5))
+ 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)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: Unknown protocol for %s (%d)",
+ m->mf_name, at,
+ hp->h_addrtype);
+ milter_error(m);
+ return -1;
+ }
+ }
+ }
+ else
+# endif /* NETINET || NETINET6 */
+ {
+ if (tTd(64, 5))
+ 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)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: unknown socket protocol", m->mf_name);
+ milter_error(m);
+ return -1;
+ }
+
+ /* just parsing through? */
+ if (parseonly)
+ {
+ m->mf_state = SMFS_READY;
+ return 0;
+ }
+
+ /* sanity check */
+ if (m->mf_state != SMFS_READY &&
+ m->mf_state != SMFS_CLOSED)
+ {
+ /* 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);
+ return -1;
+ }
+
+ /* nope, actually connecting */
+ for (;;)
+ {
+ sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
+ if (sock < 0)
+ {
+ 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_syslog(LOG_ERR, e->e_id,
+ "X%s: error creating socket: %s",
+ m->mf_name, errstring(save_errno));
+ milter_error(m);
+ return -1;
+ }
+
+ if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
+ break;
+
+ /* couldn't connect.... try next address */
+ save_errno = errno;
+ if (tTd(64, 5))
+ dprintf("milter_open(%s): %s failed: %s\n",
+ m->mf_name, at, errstring(save_errno));
+ if (LogLevel >= 14)
+ sm_syslog(LOG_INFO, e->e_id,
+ "milter_open(%s): %s failed: %s",
+ m->mf_name, at, errstring(save_errno));
+ (void) close(sock);
+
+ /* try next address */
+ if (hp != NULL && hp->h_addr_list[addrno] != NULL)
+ {
+ switch (addr.sa.sa_family)
+ {
+# if NETINET
+ case AF_INET:
+ memmove(&addr.sin.sin_addr,
+ hp->h_addr_list[addrno++],
+ INADDRSZ);
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ memmove(&addr.sin6.sin6_addr,
+ hp->h_addr_list[addrno++],
+ IN6ADDRSZ);
+ break;
+# endif /* NETINET6 */
+
+ 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_syslog(LOG_ERR, e->e_id,
+ "X%s: Unknown protocol for %s (%d)",
+ m->mf_name, at,
+ hp->h_addrtype);
+ milter_error(m);
+ return -1;
+ }
+ continue;
+ }
+ if (tTd(64, 5))
+ dprintf("X%s: error connecting to filter\n",
+ m->mf_name);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "X%s: error connecting to filter",
+ m->mf_name);
+ milter_error(m);
+ return -1;
+ }
+ m->mf_state = SMFS_OPEN;
+ return sock;
+}
+ /*
+** MILTER_SETUP -- setup structure for a mail filter
+**
+** Parameters:
+** line -- the options line.
+**
+** Returns:
+** none
+*/
+
+void
+milter_setup(line)
+ char *line;
+{
+ char fcode;
+ register char *p;
+ register struct milter *m;
+ STAB *s;
+
+ /* collect the mailer name */
+ for (p = line;
+ *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
+ p++)
+ continue;
+ if (*p != '\0')
+ *p++ = '\0';
+ if (line[0] == '\0')
+ {
+ syserr("name required for mail filter");
+ return;
+ }
+ m = (struct milter *)xalloc(sizeof *m);
+ memset((char *) m, '\0', sizeof *m);
+ m->mf_name = newstr(line);
+ m->mf_state = SMFS_READY;
+ m->mf_sock = -1;
+ m->mf_timeout[SMFTO_WRITE] = (time_t) 10;
+ m->mf_timeout[SMFTO_READ] = (time_t) 10;
+ m->mf_timeout[SMFTO_EOM] = (time_t) 300;
+
+ /* now scan through and assign info from the fields */
+ while (*p != '\0')
+ {
+ char *delimptr;
+
+ while (*p != '\0' &&
+ (*p == ',' || (isascii(*p) && isspace(*p))))
+ p++;
+
+ /* p now points to field code */
+ fcode = *p;
+ while (*p != '\0' && *p != '=' && *p != ',')
+ p++;
+ if (*p++ != '=')
+ {
+ syserr("X%s: `=' expected", m->mf_name);
+ return;
+ }
+ while (isascii(*p) && isspace(*p))
+ p++;
+
+ /* p now points to the field body */
+ p = munchstring(p, &delimptr, ',');
+
+ /* install the field into the mailer struct */
+ switch (fcode)
+ {
+ case 'S': /* socket */
+ if (p == NULL)
+ m->mf_conn = NULL;
+ else
+ m->mf_conn = newstr(p);
+ break;
+
+ case 'F': /* Milter flags configured on MTA */
+ for (; *p != '\0'; p++)
+ {
+ if (!(isascii(*p) && isspace(*p)))
+ setbitn(*p, m->mf_flags);
+ }
+ break;
+
+ case 'T': /* timeouts */
+ milter_parse_timeouts(p, m);
+ break;
+
+ default:
+ syserr("X%s: unknown filter equate %c=",
+ m->mf_name, fcode);
+ break;
+ }
+ p = delimptr;
+ }
+
+ /* early check for errors */
+ (void) milter_open(m, TRUE, CurEnv);
+
+ /* enter the mailer into the symbol table */
+ s = stab(m->mf_name, ST_MILTER, ST_ENTER);
+ if (s->s_milter != NULL)
+ syserr("X%s: duplicate filter definition", m->mf_name);
+ else
+ s->s_milter = m;
+}
+ /*
+** MILTER_PARSE_LIST -- parse option list into an array
+**
+** Called when reading configuration file.
+**
+** Parameters:
+** spec -- the filter list.
+** list -- the array to fill in.
+** max -- the maximum number of entries in list.
+**
+** Returns:
+** none
+*/
+
+void
+milter_parse_list(spec, list, max)
+ char *spec;
+ struct milter **list;
+ int max;
+{
+ int numitems = 0;
+ register char *p;
+
+ /* leave one for the NULL signifying the end of the list */
+ max--;
+
+ for (p = spec; p != NULL; )
+ {
+ STAB *s;
+
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p == '\0')
+ break;
+ spec = p;
+
+ if (numitems >= max)
+ {
+ syserr("Too many filters defined, %d max", max);
+ if (max > 0)
+ list[0] = NULL;
+ return;
+ }
+ p = strpbrk(p, ",");
+ if (p != NULL)
+ *p++ = '\0';
+
+ s = stab(spec, ST_MILTER, ST_FIND);
+ if (s == NULL)
+ {
+ syserr("InputFilter %s not defined", spec);
+ ExitStat = EX_CONFIG;
+ return;
+ }
+ list[numitems++] = s->s_milter;
+ }
+ list[numitems] = NULL;
+}
+ /*
+** MILTER_PARSE_TIMEOUTS -- parse timeout list
+**
+** Called when reading configuration file.
+**
+** Parameters:
+** spec -- the timeout list.
+** m -- milter to set.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_parse_timeouts(spec, m)
+ char *spec;
+ struct milter *m;
+{
+ char fcode;
+ register char *p;
+
+ p = spec;
+
+ /* now scan through and assign info from the fields */
+ while (*p != '\0')
+ {
+ char *delimptr;
+
+ while (*p != '\0' &&
+ (*p == ';' || (isascii(*p) && isspace(*p))))
+ p++;
+
+ /* p now points to field code */
+ fcode = *p;
+ while (*p != '\0' && *p != ':')
+ p++;
+ if (*p++ != ':')
+ {
+ syserr("X%s, T=: `:' expected", m->mf_name);
+ return;
+ }
+ while (isascii(*p) && isspace(*p))
+ p++;
+
+ /* p now points to the field body */
+ p = munchstring(p, &delimptr, ';');
+
+ /* install the field into the mailer struct */
+ switch (fcode)
+ {
+ case 'S':
+ 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]);
+ 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]);
+ 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]);
+ break;
+
+ default:
+ if (tTd(64, 5))
+ printf("X%s: %c unknown\n",
+ m->mf_name, fcode);
+ syserr("X%s: unknown filter timeout %c",
+ m->mf_name, fcode);
+ break;
+ }
+ p = delimptr;
+ }
+}
+ /*
+** MILTER_SET_OPTION -- set an individual milter option
+**
+** Parameters:
+** name -- the name of the option.
+** val -- the value of the option.
+** sticky -- if set, don't let other setoptions override
+** this value.
+**
+** Returns:
+** none.
+*/
+
+/* set if Milter sub-option is stuck */
+static BITMAP256 StickyMilterOpt;
+
+static struct milteropt
+{
+ char *mo_name; /* long name of milter option */
+ u_char mo_code; /* code for option */
+} MilterOptTab[] =
+{
+# define MO_MACROS_CONNECT 0x01
+ { "macros.connect", MO_MACROS_CONNECT },
+# define MO_MACROS_HELO 0x02
+ { "macros.helo", MO_MACROS_HELO },
+# define MO_MACROS_ENVFROM 0x03
+ { "macros.envfrom", MO_MACROS_ENVFROM },
+# define MO_MACROS_ENVRCPT 0x04
+ { "macros.envrcpt", MO_MACROS_ENVRCPT },
+ { NULL, 0 },
+};
+
+void
+milter_set_option(name, val, sticky)
+ char *name;
+ char *val;
+ bool sticky;
+{
+ int nummac = 0;
+ register struct milteropt *mo;
+ char *p;
+ char **macros = NULL;
+
+ if (tTd(37, 2) || tTd(64, 5))
+ dprintf("milter_set_option(%s = %s)", name, val);
+
+ for (mo = MilterOptTab; mo->mo_name != NULL; mo++)
+ {
+ if (strcasecmp(mo->mo_name, name) == 0)
+ break;
+ }
+
+ if (mo->mo_name == NULL)
+ syserr("milter_set_option: invalid Milter option %s", name);
+
+ /*
+ ** See if this option is preset for us.
+ */
+
+ if (!sticky && bitnset(mo->mo_code, StickyMilterOpt))
+ {
+ if (tTd(37, 2) || tTd(64,5))
+ dprintf(" (ignored)\n");
+ return;
+ }
+
+ if (tTd(37, 2) || tTd(64,5))
+ dprintf("\n");
+
+ switch (mo->mo_code)
+ {
+ case MO_MACROS_CONNECT:
+ if (macros == NULL)
+ macros = MilterConnectMacros;
+ /* FALLTHROUGH */
+
+ case MO_MACROS_HELO:
+ if (macros == NULL)
+ macros = MilterHeloMacros;
+ /* FALLTHROUGH */
+
+ case MO_MACROS_ENVFROM:
+ if (macros == NULL)
+ macros = MilterEnvFromMacros;
+ /* FALLTHROUGH */
+
+ case MO_MACROS_ENVRCPT:
+ if (macros == NULL)
+ macros = MilterEnvRcptMacros;
+
+ p = newstr(val);
+ while (*p != '\0')
+ {
+ char *macro;
+
+ /* Skip leading commas, spaces */
+ while (*p != '\0' &&
+ (*p == ',' || (isascii(*p) && isspace(*p))))
+ p++;
+
+ if (*p == '\0')
+ break;
+
+ /* Find end of macro */
+ macro = p;
+ while (*p != '\0' && *p != ',' &&
+ isascii(*p) && !isspace(*p))
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+
+ if (nummac >= MAXFILTERMACROS)
+ {
+ syserr("milter_set_option: too many macros in Milter.%s (max %d)",
+ name, MAXFILTERMACROS);
+ macros[nummac] = NULL;
+ break;
+ }
+ macros[nummac++] = macro;
+ }
+ macros[nummac] = NULL;
+ break;
+
+ default:
+ 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)
+**
+** Parameters:
+** e -- current envelope.
+**
+** Returns:
+** 0 if succesful, -1 otherwise
+*/
+
+static int
+milter_reopen_df(e)
+ ENVELOPE *e;
+{
+ char dfname[MAXPATHLEN];
+
+ (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
+
+ /*
+ ** In SuperSafe mode, e->e_dfp is a read-only FP so
+ ** close and reopen writable (later close and reopen
+ ** read only again).
+ **
+ ** In !SuperSafe mode, e->e_dfp still points at the
+ ** buffered file I/O descriptor, still open for writing
+ ** so there isn't as much work to do, just truncate it
+ ** and go.
+ */
+
+ if (SuperSafe)
+ {
+ /* close read-only df */
+ if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
+ {
+ (void) fclose(e->e_dfp);
+ e->e_flags &= ~EF_HAS_DF;
+ }
+
+ /* open writable */
+ if ((e->e_dfp = fopen(dfname, "w+")) == NULL)
+ {
+ MILTER_DF_ERROR("milter_reopen_df: fopen %s: %s");
+ return -1;
+ }
+ }
+ else if (e->e_dfp == NULL)
+ {
+ /* shouldn't happen */
+ errno = ENOENT;
+ MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)");
+ return -1;
+ }
+ return 0;
+}
+ /*
+** MILTER_RESET_DF -- re-open read-only the df file (for replbody)
+**
+** Parameters:
+** e -- current envelope.
+**
+** Returns:
+** 0 if succesful, -1 otherwise
+*/
+
+static int
+milter_reset_df(e)
+ ENVELOPE *e;
+{
+ int afd;
+ char dfname[MAXPATHLEN];
+
+ (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
+
+ if (fflush(e->e_dfp) != 0 || ferror(e->e_dfp))
+ {
+ MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s");
+ return -1;
+ }
+ else if (!SuperSafe)
+ {
+ /* skip next few clauses */
+ /* EMPTY */
+ }
+ else if ((afd = fileno(e->e_dfp)) >= 0 && fsync(afd) < 0)
+ {
+ MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s");
+ return -1;
+ }
+ else if (fclose(e->e_dfp) < 0)
+ {
+ MILTER_DF_ERROR("milter_reset_df: error closing %s: %s");
+ return -1;
+ }
+ else if ((e->e_dfp = fopen(dfname, "r")) == NULL)
+ {
+ MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s");
+ return -1;
+ }
+ else
+ e->e_flags |= EF_HAS_DF;
+ return 0;
+}
+ /*
+** MILTER_CAN_DELRCPTS -- can any milter filters delete recipients?
+**
+** Parameters:
+** none
+**
+** Returns:
+** TRUE if any filter deletes recipients, FALSE otherwise
+*/
+
+bool
+milter_can_delrcpts()
+{
+ bool can = FALSE;
+ int i;
+
+ if (tTd(64, 10))
+ dprintf("milter_can_delrcpts:");
+
+ for (i = 0; InputFilters[i] != NULL; i++)
+ {
+ struct milter *m = InputFilters[i];
+
+ if (bitset(SMFIF_DELRCPT, m->mf_fflags))
+ {
+ can = TRUE;
+ break;
+ }
+ }
+ if (tTd(64, 10))
+ dprintf("%s\n", can ? "TRUE" : "FALSE");
+
+ return can;
+}
+ /*
+** MILTER_QUIT_FILTER -- close down a single filter
+**
+** Parameters:
+** m -- milter structure of filter to close down.
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_quit_filter(m, e)
+ struct milter *m;
+ ENVELOPE *e;
+{
+ if (tTd(64, 10))
+ dprintf("milter_quit_filter(%s)\n", m->mf_name);
+
+ /* Never replace error state */
+ if (m->mf_state == SMFS_ERROR)
+ return;
+
+ if (m->mf_sock < 0 ||
+ m->mf_state == SMFS_CLOSED ||
+ m->mf_state == SMFS_READY)
+ {
+ m->mf_sock = -1;
+ m->mf_state = SMFS_CLOSED;
+ return;
+ }
+
+ (void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0,
+ m->mf_timeout[SMFTO_WRITE], e);
+ (void) close(m->mf_sock);
+ m->mf_sock = -1;
+ if (m->mf_state != SMFS_ERROR)
+ m->mf_state = SMFS_CLOSED;
+}
+ /*
+** MILTER_ABORT_FILTER -- tell filter to abort current message
+**
+** Parameters:
+** m -- milter structure of filter to abort.
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_abort_filter(m, e)
+ struct milter *m;
+ ENVELOPE *e;
+{
+ if (tTd(64, 10))
+ dprintf("milter_abort_filter(%s)\n", m->mf_name);
+
+ if (m->mf_sock < 0 ||
+ m->mf_state != SMFS_INMSG)
+ return;
+
+ (void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0,
+ m->mf_timeout[SMFTO_WRITE], e);
+ if (m->mf_state != SMFS_ERROR)
+ m->mf_state = SMFS_DONE;
+}
+ /*
+** MILTER_SEND_MACROS -- provide macros to the filters
+**
+** Parameters:
+** m -- milter to send macros to.
+** macros -- macros to send for filter smfi_getsymval().
+** cmd -- which command the macros are associated with.
+** e -- current envelope (for macro access).
+**
+** Returns:
+** none
+*/
+
+static void
+milter_send_macros(m, macros, cmd, e)
+ struct milter *m;
+ char **macros;
+ char cmd;
+ ENVELOPE *e;
+{
+ int i;
+ int mid;
+ char *v;
+ char *buf, *bp;
+ ssize_t s;
+
+ /* sanity check */
+ if (macros == NULL || macros[0] == NULL)
+ return;
+
+ /* put together data */
+ s = 1; /* for the command character */
+ for (i = 0; macros[i] != NULL; i++)
+ {
+ mid = macid(macros[i], NULL);
+ if (mid == '\0')
+ continue;
+ v = macvalue(mid, e);
+ if (v == NULL)
+ continue;
+ s += strlen(macros[i]) + 1 + strlen(v) + 1;
+ }
+
+ buf = (char *)xalloc(s);
+ bp = buf;
+ *bp++ = cmd;
+ for (i = 0; macros[i] != NULL; i++)
+ {
+ mid = macid(macros[i], NULL);
+ if (mid == '\0')
+ continue;
+ v = macvalue(mid, e);
+ if (v == NULL)
+ continue;
+
+ if (tTd(64, 10))
+ dprintf("milter_send_macros(%s, %c): %s=%s\n",
+ m->mf_name, cmd, macros[i], v);
+
+ (void) strlcpy(bp, macros[i], s - (bp - buf));
+ bp += strlen(bp) + 1;
+ (void) strlcpy(bp, v, s - (bp - buf));
+ bp += strlen(bp) + 1;
+ }
+ (void) milter_write(m, SMFIC_MACRO, buf, s,
+ m->mf_timeout[SMFTO_WRITE], e);
+ free(buf);
+}
+
+ /*
+** MILTER_SEND_COMMAND -- send a command and return the response for a filter
+**
+** Parameters:
+** m -- current milter filter
+** command -- command to send.
+** data -- optional command data.
+** sz -- length of buf.
+** e -- current envelope (for e->e_id).
+** state -- return state word.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+static char *
+milter_send_command(m, command, data, sz, e, state)
+ struct milter *m;
+ char command;
+ void *data;
+ ssize_t sz;
+ ENVELOPE *e;
+ char *state;
+{
+ char rcmd;
+ ssize_t rlen;
+ u_long skipflag;
+ char *defresponse;
+ char *response;
+
+ if (tTd(64, 10))
+ dprintf("milter_send_command(%s): cmd %c len %ld\n",
+ m->mf_name, (char) command, (long) sz);
+
+ /* find skip flag and default failure */
+ switch (command)
+ {
+ case SMFIC_CONNECT:
+ skipflag = SMFIP_NOCONNECT;
+ defresponse = "554 Command rejected";
+ break;
+
+ case SMFIC_HELO:
+ skipflag = SMFIP_NOHELO;
+ defresponse = "550 Command rejected";
+ break;
+
+ case SMFIC_MAIL:
+ skipflag = SMFIP_NOMAIL;
+ defresponse = "550 5.7.1 Command rejected";
+ break;
+
+ case SMFIC_RCPT:
+ skipflag = SMFIP_NORCPT;
+ defresponse = "550 5.7.1 Command rejected";
+ break;
+
+ case SMFIC_HEADER:
+ skipflag = SMFIP_NOHDRS;
+ defresponse = "550 5.7.1 Command rejected";
+ break;
+
+ case SMFIC_BODY:
+ skipflag = SMFIP_NOBODY;
+ defresponse = "554 5.7.1 Command rejected";
+ break;
+
+ case SMFIC_EOH:
+ skipflag = SMFIP_NOEOH;
+ defresponse = "550 5.7.1 Command rejected";
+ break;
+
+ case SMFIC_BODYEOB:
+ case SMFIC_OPTNEG:
+ case SMFIC_MACRO:
+ case SMFIC_ABORT:
+ case SMFIC_QUIT:
+ /* NOTE: not handled by milter_send_command() */
+ /* FALLTHROUGH */
+
+ default:
+ skipflag = 0;
+ defresponse = "550 5.7.1 Command rejected";
+ break;
+ }
+
+ /* check if filter wants this command */
+ if (skipflag != 0 &&
+ bitset(skipflag, m->mf_pflags))
+ return NULL;
+
+
+ (void) milter_write(m, command, data, sz,
+ m->mf_timeout[SMFTO_WRITE], e);
+ if (m->mf_state == SMFS_ERROR)
+ {
+ MILTER_CHECK_ERROR(/* EMPTY */;);
+ return NULL;
+ }
+
+ response = milter_read(m, &rcmd, &rlen,
+ m->mf_timeout[SMFTO_READ], e);
+ if (m->mf_state == SMFS_ERROR)
+ {
+ MILTER_CHECK_ERROR(/* EMPTY */;);
+ return NULL;
+ }
+
+ if (tTd(64, 10))
+ dprintf("milter_send_command(%s): returned %c\n",
+ m->mf_name, (char) rcmd);
+
+ switch (rcmd)
+ {
+ case SMFIR_REPLYCODE:
+ MILTER_CHECK_REPLYCODE(defresponse);
+ /* FALLTHROUGH */
+
+ case SMFIR_REJECT:
+ case SMFIR_DISCARD:
+ case SMFIR_TEMPFAIL:
+ *state = rcmd;
+ break;
+
+ case SMFIR_ACCEPT:
+ /* this filter is done with message/connection */
+ m->mf_state = SMFS_DONE;
+ break;
+
+ case SMFIR_CONTINUE:
+ /* if MAIL command is ok, filter is in message state */
+ if (command == SMFIC_MAIL)
+ m->mf_state = SMFS_INMSG;
+ break;
+
+ default:
+ /* Invalid response to command */
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_send_command(%s): returned bogus response %c",
+ m->mf_name, rcmd);
+ milter_error(m);
+ break;
+ }
+
+ if (*state != SMFIR_REPLYCODE &&
+ response != NULL)
+ {
+ free(response);
+ response = NULL;
+ }
+ return response;
+}
+
+ /*
+** MILTER_COMMAND -- send a command and return the response for each filter
+**
+** Parameters:
+** command -- command to send.
+** data -- optional command data.
+** sz -- length of buf.
+** macros -- macros to send for filter smfi_getsymval().
+** e -- current envelope (for macro access).
+** state -- return state word.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+static char *
+milter_command(command, data, sz, macros, e, state)
+ char command;
+ void *data;
+ ssize_t sz;
+ char **macros;
+ ENVELOPE *e;
+ char *state;
+{
+ int i;
+ char *response = NULL;
+
+ if (tTd(64, 10))
+ dprintf("milter_command: cmd %c len %ld\n",
+ (char) command, (long) sz);
+
+ *state = SMFIR_CONTINUE;
+ for (i = 0; InputFilters[i] != NULL; i++)
+ {
+ struct milter *m = InputFilters[i];
+
+ /* sanity check */
+ if (m->mf_sock < 0 ||
+ (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
+ continue;
+
+ /* send macros (regardless of whether we send command) */
+ if (macros != NULL && macros[0] != NULL)
+ {
+ milter_send_macros(m, macros, command, e);
+ if (m->mf_state == SMFS_ERROR)
+ {
+ MILTER_CHECK_ERROR(continue);
+ break;
+ }
+ }
+
+ response = milter_send_command(m, command, data, sz, e, state);
+ if (*state != SMFIR_CONTINUE)
+ break;
+ }
+ return response;
+}
+ /*
+** MILTER_NEGOTIATE -- get version and flags from filter
+**
+** Parameters:
+** m -- milter filter structure.
+** e -- current envelope.
+**
+** Returns:
+** 0 on success, -1 otherwise
+*/
+
+static int
+milter_negotiate(m, e)
+ struct milter *m;
+ ENVELOPE *e;
+{
+ char rcmd;
+ mi_int32 fvers;
+ mi_int32 fflags;
+ mi_int32 pflags;
+ char *response;
+ ssize_t rlen;
+ char data[MILTER_OPTLEN];
+
+ /* sanity check */
+ if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN)
+ {
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_negotiate(%s): impossible state",
+ m->mf_name);
+ milter_error(m);
+ return -1;
+ }
+
+ fvers = htonl(SMFI_VERSION);
+ fflags = htonl(SMFI_CURR_ACTS);
+ pflags = htonl(SMFI_CURR_PROT);
+ (void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES);
+ (void) memcpy(data + MILTER_LEN_BYTES,
+ (char *) &fflags, MILTER_LEN_BYTES);
+ (void) memcpy(data + (MILTER_LEN_BYTES * 2),
+ (char *) &pflags, MILTER_LEN_BYTES);
+ (void) milter_write(m, SMFIC_OPTNEG, data, sizeof data,
+ m->mf_timeout[SMFTO_WRITE], e);
+
+ if (m->mf_state == SMFS_ERROR)
+ return -1;
+
+ response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e);
+ if (m->mf_state == SMFS_ERROR)
+ return -1;
+
+ if (rcmd != SMFIC_OPTNEG)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_negotiate(%s): returned %c instead of %c\n",
+ m->mf_name, rcmd, SMFIC_OPTNEG);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_negotiate(%s): returned %c instead of %c",
+ m->mf_name, rcmd, SMFIC_OPTNEG);
+ if (response != NULL)
+ free(response);
+ milter_error(m);
+ return -1;
+ }
+
+ /* Make sure we have enough bytes for the version */
+ if (response == NULL || rlen < MILTER_LEN_BYTES)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_negotiate(%s): did not return valid info\n",
+ m->mf_name);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_negotiate(%s): did not return valid info",
+ m->mf_name);
+ if (response != NULL)
+ free(response);
+ milter_error(m);
+ return -1;
+ }
+
+ /* extract information */
+ (void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES);
+
+ /* Now make sure we have enough for the feature bitmap */
+ if (rlen != MILTER_OPTLEN)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_negotiate(%s): did not return enough info\n",
+ m->mf_name);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_negotiate(%s): did not return enough info",
+ m->mf_name);
+ if (response != NULL)
+ free(response);
+ milter_error(m);
+ return -1;
+ }
+
+ (void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES,
+ MILTER_LEN_BYTES);
+ (void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2),
+ MILTER_LEN_BYTES);
+ free(response);
+ response = NULL;
+
+ m->mf_fvers = ntohl(fvers);
+ m->mf_fflags = ntohl(fflags);
+ m->mf_pflags = ntohl(pflags);
+
+ /* check for version compatibility */
+ if (m->mf_fvers == 1 ||
+ m->mf_fvers > SMFI_VERSION)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_negotiate(%s): version %lu != MTA milter version %d\n",
+ m->mf_name, m->mf_fvers, SMFI_VERSION);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_negotiate(%s): version %ld != MTA milter version %d",
+ m->mf_name, m->mf_fvers, SMFI_VERSION);
+ milter_error(m);
+ return -1;
+ }
+
+ /* check for filter feature mismatch */
+ if ((m->mf_fflags & SMFI_CURR_ACTS) != m->mf_fflags)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n",
+ m->mf_name, m->mf_fflags,
+ (u_long) SMFI_CURR_ACTS);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n",
+ m->mf_name, m->mf_fflags,
+ (u_long) SMFI_CURR_ACTS);
+ milter_error(m);
+ return -1;
+ }
+
+ /* check for protocol feature mismatch */
+ if ((m->mf_pflags & SMFI_CURR_PROT) != m->mf_pflags)
+ {
+ if (tTd(64, 5))
+ dprintf("milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n",
+ m->mf_name, m->mf_pflags,
+ (u_long) SMFI_CURR_PROT);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n",
+ m->mf_name, m->mf_pflags,
+ (u_long) SMFI_CURR_PROT);
+ milter_error(m);
+ return -1;
+ }
+
+ if (tTd(64, 5))
+ 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
+**
+** Parameters:
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_per_connection_check(e)
+ ENVELOPE *e;
+{
+ int i;
+
+ /* see if we are done with any of the filters */
+ for (i = 0; InputFilters[i] != NULL; i++)
+ {
+ struct milter *m = InputFilters[i];
+
+ if (m->mf_state == SMFS_DONE)
+ milter_quit_filter(m, e);
+ }
+}
+ /*
+** MILTER_ERROR -- Put a milter filter into error state
+**
+** Parameters:
+** m -- the broken filter.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_error(m)
+ struct milter *m;
+{
+ /*
+ ** We could send a quit here but
+ ** we may have gotten here due to
+ ** an I/O error so we don't want
+ ** to try to make things worse.
+ */
+
+ if (m->mf_sock >= 0)
+ {
+ (void) close(m->mf_sock);
+ m->mf_sock = -1;
+ }
+ m->mf_state = SMFS_ERROR;
+}
+ /*
+** MILTER_HEADERS -- send headers to a single milter filter
+**
+** Parameters:
+** m -- current filter.
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+static char *
+milter_headers(m, e, state)
+ struct milter *m;
+ ENVELOPE *e;
+ char *state;
+{
+ char *response = NULL;
+ HDR *h;
+
+ for (h = e->e_header; h != NULL; h = h->h_link)
+ {
+ char *buf;
+ ssize_t s;
+
+ /* don't send over deleted headers */
+ if (h->h_value == NULL)
+ {
+ /* strip H_USER so not counted in milter_chgheader() */
+ h->h_flags &= ~H_USER;
+ continue;
+ }
+
+ /* skip auto-generated */
+ if (!bitset(H_USER, h->h_flags))
+ continue;
+
+ if (tTd(64, 10))
+ dprintf("milter_headers: %s: %s\n",
+ h->h_field, h->h_value);
+
+ s = strlen(h->h_field) + 1 +
+ strlen(h->h_value) + 1;
+ buf = (char *) xalloc(s);
+ snprintf(buf, s, "%s%c%s", h->h_field, '\0', h->h_value);
+
+ /* send it over */
+ response = milter_send_command(m, SMFIC_HEADER, buf,
+ s, e, state);
+ free(buf);
+ if (m->mf_state == SMFS_ERROR ||
+ m->mf_state == SMFS_DONE ||
+ *state != SMFIR_CONTINUE)
+ break;
+ }
+ return response;
+}
+ /*
+** MILTER_BODY -- send the body to a filter
+**
+** Parameters:
+** m -- current filter.
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+static char *
+milter_body(m, e, state)
+ struct milter *m;
+ ENVELOPE *e;
+ char *state;
+{
+ char bufchar = '\0';
+ char prevchar = '\0';
+ int c;
+ char *response = NULL;
+ char *bp;
+ char buf[MILTER_CHUNK_SIZE];
+
+ if (tTd(64, 10))
+ dprintf("milter_body\n");
+
+ if (bfrewind(e->e_dfp) < 0)
+ {
+ ExitStat = EX_IOERR;
+ *state = SMFIR_TEMPFAIL;
+ syserr("milter_body: %s/df%s: rewind error",
+ qid_printqueue(e->e_queuedir), e->e_id);
+ return NULL;
+ }
+
+ bp = buf;
+ while ((c = getc(e->e_dfp)) != EOF)
+ {
+ /* Change LF to CRLF */
+ if (c == '\n')
+ {
+ /* Not a CRLF already? */
+ if (prevchar != '\r')
+ {
+ /* Room for CR now? */
+ if (bp + 2 > &buf[sizeof buf])
+ {
+ /* No room, buffer LF */
+ bufchar = c;
+
+ /* and send CR now */
+ c = '\r';
+ }
+ else
+ {
+ /* Room to do it now */
+ *bp++ = '\r';
+ prevchar = '\r';
+ }
+ }
+ }
+ *bp++ = (char) c;
+ prevchar = c;
+ if (bp >= &buf[sizeof buf])
+ {
+ /* send chunk */
+ response = milter_send_command(m, SMFIC_BODY, buf,
+ bp - buf, e, state);
+ bp = buf;
+ if (bufchar != '\0')
+ {
+ *bp++ = bufchar;
+ bufchar = '\0';
+ prevchar = bufchar;
+ }
+ }
+ if (m->mf_state == SMFS_ERROR ||
+ m->mf_state == SMFS_DONE ||
+ *state != SMFIR_CONTINUE)
+ break;
+ }
+
+ /* check for read errors */
+ if (ferror(e->e_dfp))
+ {
+ ExitStat = EX_IOERR;
+ if (*state == SMFIR_CONTINUE ||
+ *state == SMFIR_ACCEPT)
+ {
+ *state = SMFIR_TEMPFAIL;
+ if (response != NULL)
+ {
+ free(response);
+ response = NULL;
+ }
+ }
+ syserr("milter_body: %s/df%s: read error",
+ qid_printqueue(e->e_queuedir), e->e_id);
+ return response;
+ }
+
+ /* send last body chunk */
+ if (bp > buf &&
+ m->mf_state != SMFS_ERROR &&
+ m->mf_state != SMFS_DONE &&
+ *state == SMFIR_CONTINUE)
+ {
+ /* send chunk */
+ response = milter_send_command(m, SMFIC_BODY, buf, bp - buf,
+ e, state);
+ bp = buf;
+ }
+ return response;
+}
+
+/*
+** Actions
+*/
+
+ /*
+** MILTER_ADDHEADER -- Add the supplied header to the message
+**
+** Parameters:
+** response -- encoded form of header/value.
+** rlen -- length of response.
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_addheader(response, rlen, e)
+ char *response;
+ ssize_t rlen;
+ ENVELOPE *e;
+{
+ char *val;
+
+ if (tTd(64, 10))
+ dprintf("milter_addheader: ");
+
+ /* sanity checks */
+ if (response == NULL)
+ {
+ if (tTd(64, 10))
+ 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");
+ return;
+ }
+
+ /* Find separating NUL */
+ val = response + strlen(response) + 1;
+
+ /* another sanity check */
+ if (strlen(response) + strlen(val) + 2 != (size_t) rlen)
+ {
+ if (tTd(64, 10))
+ dprintf("didn't follow protocol (part len)\n");
+ return;
+ }
+
+ if (*response == '\0')
+ {
+ if (tTd(64, 10))
+ dprintf("empty field name\n");
+ return;
+ }
+
+ /* add to e_msgsize */
+ e->e_msgsize += strlen(response) + 2 + strlen(val);
+
+ if (tTd(64, 10))
+ dprintf("Add %s: %s\n", response, val);
+
+ addheader(newstr(response), val, H_USER, &e->e_header);
+}
+ /*
+** MILTER_CHANGEHEADER -- Change the supplied header in the message
+**
+** Parameters:
+** response -- encoded form of header/index/value.
+** rlen -- length of response.
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_changeheader(response, rlen, e)
+ char *response;
+ ssize_t rlen;
+ ENVELOPE *e;
+{
+ mi_int32 i, index;
+ char *field, *val;
+ HDR *h;
+
+ if (tTd(64, 10))
+ dprintf("milter_changeheader: ");
+
+ /* sanity checks */
+ if (response == NULL)
+ {
+ if (tTd(64, 10))
+ 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");
+ return;
+ }
+
+ /* Find separating NUL */
+ (void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
+ index = ntohl(i);
+ field = response + MILTER_LEN_BYTES;
+ val = field + strlen(field) + 1;
+
+ /* another sanity check */
+ if (MILTER_LEN_BYTES + strlen(field) + 1 +
+ strlen(val) + 1 != (size_t) rlen)
+ {
+ if (tTd(64, 10))
+ dprintf("didn't follow protocol (part len)\n");
+ return;
+ }
+
+ if (*field == '\0')
+ {
+ if (tTd(64, 10))
+ dprintf("empty field name\n");
+ return;
+ }
+
+ for (h = e->e_header; h != NULL; h = h->h_link)
+ {
+ if (bitset(H_USER, h->h_flags) &&
+ strcasecmp(h->h_field, field) == 0 &&
+ --index <= 0)
+ break;
+ }
+
+ if (h == NULL)
+ {
+ if (*val == '\0')
+ {
+ if (tTd(64, 10))
+ dprintf("Delete (noop) %s:\n", field);
+ }
+ else
+ {
+ /* treat modify value with no existing header as add */
+ if (tTd(64, 10))
+ dprintf("Add %s: %s\n", field, val);
+
+ addheader(newstr(field), val, H_USER, &e->e_header);
+ }
+ return;
+ }
+
+ if (tTd(64, 10))
+ {
+ if (*val == '\0')
+ {
+ dprintf("Delete %s: %s\n", field,
+ h->h_value == NULL ? "<NULL>" : h->h_value);
+ }
+ else
+ {
+ dprintf("Change %s: from %s to %s\n",
+ field,
+ h->h_value == NULL ? "<NULL>" : h->h_value,
+ val);
+ }
+ }
+
+ if (h->h_value != NULL)
+ {
+ e->e_msgsize -= strlen(h->h_value);
+ free(h->h_value);
+ }
+
+ if (*val == '\0')
+ {
+ /* Remove "Field: " from message size */
+ e->e_msgsize -= strlen(h->h_field) + 2;
+ h->h_value = NULL;
+ }
+ else
+ {
+ h->h_value = newstr(val);
+ e->e_msgsize += strlen(h->h_value);
+ }
+}
+ /*
+** MILTER_ADDRCPT -- Add the supplied recipient to the message
+**
+** Parameters:
+** response -- encoded form of recipient address.
+** rlen -- length of response.
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_addrcpt(response, rlen, e)
+ char *response;
+ ssize_t rlen;
+ ENVELOPE *e;
+{
+ if (tTd(64, 10))
+ dprintf("milter_addrcpt: ");
+
+ /* sanity checks */
+ if (response == NULL)
+ {
+ if (tTd(64, 10))
+ dprintf("NULL response\n");
+ return;
+ }
+
+ if (*response == '\0' ||
+ 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);
+ return;
+ }
+
+ if (tTd(64, 10))
+ dprintf("%s\n", response);
+ (void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e);
+ return;
+}
+ /*
+** MILTER_DELRCPT -- Delete the supplied recipient from the message
+**
+** Parameters:
+** response -- encoded form of recipient address.
+** rlen -- length of response.
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+static void
+milter_delrcpt(response, rlen, e)
+ char *response;
+ ssize_t rlen;
+ ENVELOPE *e;
+{
+ if (tTd(64, 10))
+ dprintf("milter_delrcpt: ");
+
+ /* sanity checks */
+ if (response == NULL)
+ {
+ if (tTd(64, 10))
+ dprintf("NULL response\n");
+ return;
+ }
+
+ if (*response == '\0' ||
+ strlen(response) + 1 != (size_t) rlen)
+ {
+ if (tTd(64, 10))
+ dprintf("didn't follow protocol (total len)\n");
+ return;
+ }
+
+ if (tTd(64, 10))
+ dprintf("%s\n", response);
+ (void) removefromlist(response, &e->e_sendqueue, e);
+ return;
+}
+ /*
+** MILTER_REPLBODY -- Replace the current df file with new body
+**
+** Parameters:
+** response -- encoded form of new body.
+** rlen -- length of response.
+** newfilter -- if first time called by a new filter
+** e -- current envelope.
+**
+** Returns:
+** 0 upon success, -1 upon failure
+*/
+
+static int
+milter_replbody(response, rlen, newfilter, e)
+ char *response;
+ ssize_t rlen;
+ bool newfilter;
+ ENVELOPE *e;
+{
+ static char prevchar;
+ int i;
+
+ if (tTd(64, 10))
+ dprintf("milter_replbody\n");
+
+ /* If a new filter, reset previous character and truncate df */
+ if (newfilter)
+ {
+ off_t prevsize = 0;
+ char dfname[MAXPATHLEN];
+
+ (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
+
+ /* Reset prevchar */
+ prevchar = '\0';
+
+ /* Get the current df information */
+ if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
+ {
+ int afd;
+ struct stat st;
+
+ afd = fileno(e->e_dfp);
+ if (afd > 0 && fstat(afd, &st) == 0)
+ prevsize = st.st_size;
+ }
+
+ /* truncate current df file */
+ if (bftruncate(e->e_dfp) < 0)
+ {
+ MILTER_DF_ERROR("milter_reopen_df: bftruncate %s: %s");
+ return -1;
+ }
+ else
+ {
+ if (prevsize > e->e_msgsize)
+ e->e_msgsize = 0;
+ else
+ e->e_msgsize -= prevsize;
+ }
+ }
+
+ if (response == NULL)
+ {
+ /* Flush the buffered '\r' */
+ if (prevchar == '\r')
+ {
+ (void) putc(prevchar, e->e_dfp);
+ e->e_msgsize++;
+ }
+ return 0;
+ }
+
+ for (i = 0; i < rlen; i++)
+ {
+ /* Buffered char from last chunk */
+ if (i == 0 && prevchar == '\r')
+ {
+ /* Not CRLF, output prevchar */
+ if (response[i] != '\n')
+ {
+ (void) putc(prevchar, e->e_dfp);
+ e->e_msgsize++;
+ }
+ prevchar = '\0';
+ }
+
+ /* Turn CRLF into LF */
+ if (response[i] == '\r')
+ {
+ /* check if at end of chunk */
+ if (i + 1 < rlen)
+ {
+ /* If LF, strip CR */
+ if (response[i + 1] == '\n')
+ i++;
+ }
+ else
+ {
+ /* check next chunk */
+ prevchar = '\r';
+ continue;
+ }
+ }
+ (void) putc(response[i], e->e_dfp);
+ e->e_msgsize++;
+ }
+ return 0;
+}
+
+/*
+** MTA callouts
+*/
+
+ /*
+** MILTER_INIT -- open and negotiate with all of the filters
+**
+** Parameters:
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** none
+*/
+
+/* ARGSUSED */
+void
+milter_init(e, state)
+ ENVELOPE *e;
+ char *state;
+{
+ int i;
+
+ if (tTd(64, 10))
+ dprintf("milter_init\n");
+
+ *state = SMFIR_CONTINUE;
+ for (i = 0; InputFilters[i] != NULL; i++)
+ {
+ struct milter *m = InputFilters[i];
+
+ m->mf_sock = milter_open(m, FALSE, e);
+ if (m->mf_state == SMFS_ERROR)
+ {
+ MILTER_CHECK_ERROR(continue);
+ break;
+ }
+
+ if (m->mf_sock < 0 ||
+ milter_negotiate(m, e) < 0 ||
+ 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");
+
+ /* if negotation failure, close socket */
+ if (m->mf_sock >= 0)
+ {
+ (void) close(m->mf_sock);
+ m->mf_sock = -1;
+ }
+ milter_error(m);
+ if (m->mf_state == SMFS_ERROR)
+ {
+ MILTER_CHECK_ERROR(continue);
+ break;
+ }
+ }
+ }
+
+ /*
+ ** If something temp/perm failed with one of the filters,
+ ** we won't be using any of them, so clear any existing
+ ** connections.
+ */
+
+ if (*state != SMFIR_CONTINUE)
+ milter_quit(e);
+}
+ /*
+** MILTER_CONNECT -- send connection info to milter filters
+**
+** Parameters:
+** hostname -- hostname of remote machine.
+** addr -- address of remote machine.
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+char *
+milter_connect(hostname, addr, e, state)
+ char *hostname;
+ SOCKADDR addr;
+ ENVELOPE *e;
+ char *state;
+{
+ char family;
+ u_short port;
+ char *buf, *bp;
+ char *response;
+ char *sockinfo = NULL;
+ ssize_t s;
+# if NETINET6
+ char buf6[INET6_ADDRSTRLEN];
+# endif /* NETINET6 */
+
+ if (tTd(64, 10))
+ dprintf("milter_connect(%s)\n", hostname);
+
+ /* gather data */
+ switch (addr.sa.sa_family)
+ {
+# if NETUNIX
+ case AF_UNIX:
+ family = SMFIA_UNIX;
+ port = htons(0);
+ sockinfo = addr.sunix.sun_path;
+ break;
+# endif /* NETUNIX */
+
+# if NETINET
+ case AF_INET:
+ family = SMFIA_INET;
+ port = htons(addr.sin.sin_port);
+ sockinfo = (char *) inet_ntoa(addr.sin.sin_addr);
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ family = SMFIA_INET6;
+ port = htons(addr.sin6.sin6_port);
+ sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6,
+ sizeof buf6);
+ if (sockinfo == NULL)
+ sockinfo = "";
+ break;
+# endif /* NETINET6 */
+
+ default:
+ family = SMFIA_UNKNOWN;
+ break;
+ }
+
+ s = strlen(hostname) + 1 + sizeof(family);
+ if (family != SMFIA_UNKNOWN)
+ s += sizeof(port) + strlen(sockinfo) + 1;
+
+ buf = (char *)xalloc(s);
+ bp = buf;
+
+ /* put together data */
+ (void) memcpy(bp, hostname, strlen(hostname));
+ bp += strlen(hostname);
+ *bp++ = '\0';
+ (void) memcpy(bp, &family, sizeof family);
+ bp += sizeof family;
+ if (family != SMFIA_UNKNOWN)
+ {
+ (void) memcpy(bp, &port, sizeof port);
+ bp += sizeof port;
+
+ /* include trailing '\0' */
+ (void) memcpy(bp, sockinfo, strlen(sockinfo) + 1);
+ }
+
+ response = milter_command(SMFIC_CONNECT, buf, s,
+ MilterConnectMacros, e, state);
+ free(buf);
+
+ /*
+ ** If this message connection is done for,
+ ** close the filters.
+ */
+
+ if (*state != SMFIR_CONTINUE)
+ milter_quit(e);
+ else
+ milter_per_connection_check(e);
+
+ /*
+ ** SMFIR_REPLYCODE can't work with connect due to
+ ** the requirements of SMTP. Therefore, ignore the
+ ** reply code text but keep the state it would reflect.
+ */
+
+ if (*state == SMFIR_REPLYCODE)
+ {
+ if (response != NULL &&
+ *response == '4')
+ *state = SMFIR_TEMPFAIL;
+ else
+ *state = SMFIR_REJECT;
+ if (response != NULL)
+ {
+ free(response);
+ response = NULL;
+ }
+ }
+ return response;
+}
+ /*
+** MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters
+**
+** Parameters:
+** helo -- argument to SMTP HELO/EHLO command.
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+char *
+milter_helo(helo, e, state)
+ char *helo;
+ ENVELOPE *e;
+ char *state;
+{
+ char *response;
+
+ if (tTd(64, 10))
+ dprintf("milter_helo(%s)\n", helo);
+
+ response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1,
+ MilterHeloMacros, e, state);
+ milter_per_connection_check(e);
+ return response;
+}
+ /*
+** MILTER_ENVFROM -- send SMTP MAIL command info to milter filters
+**
+** Parameters:
+** args -- SMTP MAIL command args (args[0] == sender).
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+char *
+milter_envfrom(args, e, state)
+ char **args;
+ ENVELOPE *e;
+ char *state;
+{
+ int i;
+ char *buf, *bp;
+ char *response;
+ ssize_t s;
+
+ if (tTd(64, 10))
+ {
+ dprintf("milter_envfrom:");
+ for (i = 0; args[i] != NULL; i++)
+ dprintf(" %s", args[i]);
+ dprintf("\n");
+ }
+
+ /* sanity check */
+ if (args[0] == NULL)
+ {
+ *state = SMFIR_REJECT;
+ return NULL;
+ }
+
+ /* new message, so ... */
+ for (i = 0; InputFilters[i] != NULL; i++)
+ {
+ struct milter *m = InputFilters[i];
+
+ switch (m->mf_state)
+ {
+ case SMFS_INMSG:
+ /* abort in message filters */
+ milter_abort_filter(m, e);
+ /* FALLTHROUGH */
+
+ case SMFS_DONE:
+ /* reset done filters */
+ m->mf_state = SMFS_OPEN;
+ break;
+ }
+ }
+
+ /* put together data */
+ s = 0;
+ for (i = 0; args[i] != NULL; i++)
+ s += strlen(args[i]) + 1;
+ buf = (char *)xalloc(s);
+ bp = buf;
+ for (i = 0; args[i] != NULL; i++)
+ {
+ (void) strlcpy(bp, args[i], s - (bp - buf));
+ bp += strlen(bp) + 1;
+ }
+
+ /* send it over */
+ response = milter_command(SMFIC_MAIL, buf, s,
+ MilterEnvFromMacros, e, state);
+ free(buf);
+
+ /*
+ ** If filter rejects/discards a per message command,
+ ** abort the other filters since we are done with the
+ ** current message.
+ */
+
+ MILTER_CHECK_DONE_MSG();
+ return response;
+}
+ /*
+** MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters
+**
+** Parameters:
+** args -- SMTP MAIL command args (args[0] == recipient).
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** response string (may be NULL)
+*/
+
+char *
+milter_envrcpt(args, e, state)
+ char **args;
+ ENVELOPE *e;
+ char *state;
+{
+ int i;
+ char *buf, *bp;
+ char *response;
+ ssize_t s;
+
+ if (tTd(64, 10))
+ {
+ dprintf("milter_envrcpt:");
+ for (i = 0; args[i] != NULL; i++)
+ dprintf(" %s", args[i]);
+ dprintf("\n");
+ }
+
+ /* sanity check */
+ if (args[0] == NULL)
+ {
+ *state = SMFIR_REJECT;
+ return NULL;
+ }
+
+ /* put together data */
+ s = 0;
+ for (i = 0; args[i] != NULL; i++)
+ s += strlen(args[i]) + 1;
+ buf = (char *)xalloc(s);
+ bp = buf;
+ for (i = 0; args[i] != NULL; i++)
+ {
+ (void) strlcpy(bp, args[i], s - (bp - buf));
+ bp += strlen(bp) + 1;
+ }
+
+ /* send it over */
+ response = milter_command(SMFIC_RCPT, buf, s,
+ MilterEnvRcptMacros, e, state);
+ free(buf);
+ return response;
+}
+ /*
+** MILTER_DATA -- send message headers/body and gather final message results
+**
+** Parameters:
+** e -- current envelope.
+** state -- return state from response.
+**
+** Returns:
+** response string (may be NULL)
+**
+** Side effects:
+** - Uses e->e_dfp for access to the body
+** - Can call the various milter action routines to
+** modify the envelope or message.
+*/
+
+# define MILTER_CHECK_RESULTS() \
+ if (*state == SMFIR_ACCEPT || \
+ m->mf_state == SMFS_DONE || \
+ m->mf_state == SMFS_ERROR) \
+ { \
+ if (m->mf_state != SMFS_ERROR) \
+ m->mf_state = SMFS_DONE; \
+ continue; /* to next filter */ \
+ } \
+ if (*state != SMFIR_CONTINUE) \
+ { \
+ m->mf_state = SMFS_DONE; \
+ goto finishup; \
+ }
+
+char *
+milter_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 newfilter; /* reset on each new filter */
+ char rcmd;
+ int i;
+ int save_errno;
+ char *response = NULL;
+ time_t eomsent;
+ ssize_t rlen;
+
+ if (tTd(64, 10))
+ dprintf("milter_data\n");
+
+ *state = SMFIR_CONTINUE;
+
+ /*
+ ** XXX: Should actually send body chunks to each filter
+ ** a chunk at a time instead of sending the whole body to
+ ** each filter in turn. However, only if the filters don't
+ ** change the body.
+ */
+
+ for (i = 0; InputFilters[i] != NULL; i++)
+ {
+ struct milter *m = InputFilters[i];
+
+ if (*state != SMFIR_CONTINUE &&
+ *state != SMFIR_ACCEPT)
+ {
+ /*
+ ** A previous filter has dealt with the message,
+ ** safe to stop processing the filters.
+ */
+
+ break;
+ }
+
+ /* Now reset state for later evaluation */
+ *state = SMFIR_CONTINUE;
+ newfilter = TRUE;
+
+ /* sanity checks */
+ if (m->mf_sock < 0 ||
+ (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
+ continue;
+
+ m->mf_state = SMFS_INMSG;
+
+ /* check if filter wants the headers */
+ if (!bitset(SMFIP_NOHDRS, m->mf_pflags))
+ {
+ response = milter_headers(m, e, state);
+ MILTER_CHECK_RESULTS();
+ }
+
+ /* check if filter wants EOH */
+ if (!bitset(SMFIP_NOEOH, m->mf_pflags))
+ {
+ if (tTd(64, 10))
+ dprintf("milter_data: eoh\n");
+
+ /* send it over */
+ response = milter_send_command(m, SMFIC_EOH, NULL, 0,
+ e, state);
+ MILTER_CHECK_RESULTS();
+ }
+
+ /* check if filter wants the body */
+ if (!bitset(SMFIP_NOBODY, m->mf_pflags) &&
+ e->e_dfp != NULL)
+ {
+ rewind = TRUE;
+ response = milter_body(m, e, state);
+ MILTER_CHECK_RESULTS();
+ }
+
+ /* send the final body chunk */
+ (void) milter_write(m, SMFIC_BODYEOB, NULL, 0,
+ m->mf_timeout[SMFTO_WRITE], e);
+
+ /* Get time EOM sent for timeout */
+ eomsent = curtime();
+
+ /* deal with the possibility of multiple responses */
+ while (*state == SMFIR_CONTINUE)
+ {
+ /* Check total timeout from EOM to final ACK/NAK */
+ if (m->mf_timeout[SMFTO_EOM] > 0 &&
+ curtime() - eomsent >= m->mf_timeout[SMFTO_EOM])
+ {
+ if (tTd(64, 5))
+ dprintf("milter_data(%s): EOM ACK/NAK timeout\n",
+ m->mf_name);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_data(%s): EOM ACK/NAK timeout\n",
+ m->mf_name);
+ milter_error(m);
+ MILTER_CHECK_ERROR(continue);
+ break;
+ }
+
+ response = milter_read(m, &rcmd, &rlen,
+ m->mf_timeout[SMFTO_READ], e);
+ if (m->mf_state == SMFS_ERROR)
+ break;
+
+ if (tTd(64, 10))
+ 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");
+ *state = rcmd;
+ m->mf_state = SMFS_DONE;
+ break;
+
+ case SMFIR_REJECT:
+ case SMFIR_DISCARD:
+ case SMFIR_TEMPFAIL:
+ *state = rcmd;
+ m->mf_state = SMFS_DONE;
+ break;
+
+ case SMFIR_CONTINUE:
+ case SMFIR_ACCEPT:
+ /* this filter is done with message */
+ if (replfailed)
+ *state = SMFIR_TEMPFAIL;
+ else
+ *state = SMFIR_ACCEPT;
+ m->mf_state = SMFS_DONE;
+ break;
+
+ case SMFIR_PROGRESS:
+ break;
+
+ case SMFIR_ADDHEADER:
+ if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, e->e_id,
+ "milter_data(%s): lied about adding headers, honoring request anyway",
+ m->mf_name);
+ }
+ milter_addheader(response, rlen, e);
+ break;
+
+ case SMFIR_CHGHEADER:
+ if (!bitset(SMFIF_CHGHDRS, m->mf_fflags))
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, e->e_id,
+ "milter_data(%s): lied about changing headers, honoring request anyway",
+ m->mf_name);
+ }
+ milter_changeheader(response, rlen, e);
+ break;
+
+ case SMFIR_ADDRCPT:
+ if (!bitset(SMFIF_ADDRCPT, m->mf_fflags))
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, e->e_id,
+ "milter_data(%s) lied about adding recipients, honoring request anyway",
+ m->mf_name);
+ }
+ milter_addrcpt(response, rlen, e);
+ break;
+
+ case SMFIR_DELRCPT:
+ if (!bitset(SMFIF_DELRCPT, m->mf_fflags))
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, e->e_id,
+ "milter_data(%s): lied about removing recipients, honoring request anyway",
+ m->mf_name);
+ }
+ milter_delrcpt(response, rlen, e);
+ break;
+
+ case SMFIR_REPLBODY:
+ if (!bitset(SMFIF_MODBODY, m->mf_fflags))
+ {
+ if (LogLevel > 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;
+ break;
+ }
+
+ /* already failed in attempt */
+ if (replfailed)
+ break;
+
+ if (!dfopen)
+ {
+ if (milter_reopen_df(e) < 0)
+ {
+ replfailed = TRUE;
+ break;
+ }
+ dfopen = TRUE;
+ rewind = TRUE;
+ }
+
+ if (milter_replbody(response, rlen,
+ newfilter, e) < 0)
+ replfailed = TRUE;
+ newfilter = FALSE;
+ replbody = TRUE;
+ break;
+
+ default:
+ /* Invalid response to command */
+ if (LogLevel > 0)
+ sm_syslog(LOG_ERR, e->e_id,
+ "milter_data(%s): returned bogus response %c",
+ m->mf_name, rcmd);
+ milter_error(m);
+ break;
+ }
+ if (rcmd != SMFIR_REPLYCODE &&
+ response != NULL)
+ {
+ free(response);
+ response = NULL;
+ }
+
+ if (m->mf_state == SMFS_ERROR)
+ break;
+ }
+
+ if (replbody && !replfailed)
+ {
+ /* flush possible buffered character */
+ milter_replbody(NULL, 0, !replbody, e);
+ replbody = FALSE;
+ }
+
+ if (m->mf_state == SMFS_ERROR)
+ {
+ MILTER_CHECK_ERROR(continue);
+ goto finishup;
+ }
+ }
+
+finishup:
+ /* leave things in the expected state if we touched it */
+ if (replfailed)
+ {
+ if (*state == SMFIR_CONTINUE ||
+ *state == SMFIR_ACCEPT)
+ {
+ *state = SMFIR_TEMPFAIL;
+ if (response != NULL)
+ {
+ free(response);
+ response = NULL;
+ }
+ }
+
+ if (dfopen)
+ {
+ (void) fclose(e->e_dfp);
+ e->e_dfp = NULL;
+ e->e_flags &= ~EF_HAS_DF;
+ dfopen = FALSE;
+ }
+ rewind = FALSE;
+ }
+
+ if ((dfopen && milter_reset_df(e) < 0) ||
+ (rewind && bfrewind(e->e_dfp) < 0))
+ {
+ save_errno = errno;
+ ExitStat = EX_IOERR;
+
+ /*
+ ** If filter told us to keep message but we had
+ ** an error, we can't really keep it, tempfail it.
+ */
+
+ if (*state == SMFIR_CONTINUE ||
+ *state == SMFIR_ACCEPT)
+ {
+ *state = SMFIR_TEMPFAIL;
+ if (response != NULL)
+ {
+ free(response);
+ response = NULL;
+ }
+ }
+
+ errno = save_errno;
+ syserr("milter_data: %s/df%s: read error",
+ qid_printqueue(e->e_queuedir), e->e_id);
+ }
+ MILTER_CHECK_DONE_MSG();
+ return response;
+}
+ /*
+** MILTER_QUIT -- informs the filter(s) we are done and closes connection(s)
+**
+** Parameters:
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+void
+milter_quit(e)
+ ENVELOPE *e;
+{
+ int i;
+
+ if (tTd(64, 10))
+ dprintf("milter_quit\n");
+
+ 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:
+** e -- current envelope.
+**
+** Returns:
+** none
+*/
+
+void
+milter_abort(e)
+ ENVELOPE *e;
+{
+ int i;
+
+ if (tTd(64, 10))
+ dprintf("milter_abort\n");
+
+ for (i = 0; InputFilters[i] != NULL; i++)
+ {
+ struct milter *m = InputFilters[i];
+
+ /* sanity checks */
+ if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG)
+ continue;
+
+ milter_abort_filter(m, e);
+ }
+}
+#endif /* _FFR_MILTER */
diff --git a/contrib/sendmail/src/mime.c b/contrib/sendmail/src/mime.c
index 7156891..e92f615 100644
--- a/contrib/sendmail/src/mime.c
+++ b/contrib/sendmail/src/mime.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1994, 1996-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1994
* The Regents of the University of California. All rights reserved.
@@ -10,12 +11,18 @@
*
*/
-# include "sendmail.h"
-# include <string.h>
+#include <sendmail.h>
+#include <string.h>
#ifndef lint
-static char sccsid[] = "@(#)mime.c 8.71 (Berkeley) 1/18/1999";
-#endif /* not lint */
+static char id[] = "@(#)$Id: mime.c,v 8.94 1999/10/17 17:35:58 ca 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 *));
/*
** MIME support.
@@ -37,23 +44,22 @@ static char sccsid[] = "@(#)mime.c 8.71 (Berkeley) 1/18/1999";
#if MIME8TO7
/* character set for hex and base64 encoding */
-char Base16Code[] = "0123456789ABCDEF";
-char Base64Code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static char Base16Code[] = "0123456789ABCDEF";
+static char Base64Code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/* types of MIME boundaries */
-#define MBT_SYNTAX 0 /* syntax error */
-#define MBT_NOTSEP 1 /* not a boundary */
-#define MBT_INTERMED 2 /* intermediate boundary (no trailing --) */
-#define MBT_FINAL 3 /* final boundary (trailing -- included) */
+# define MBT_SYNTAX 0 /* syntax error */
+# define MBT_NOTSEP 1 /* not a boundary */
+# define MBT_INTERMED 2 /* intermediate boundary (no trailing --) */
+# define MBT_FINAL 3 /* final boundary (trailing -- included) */
static char *MimeBoundaryNames[] =
{
"SYNTAX", "NOTSEP", "INTERMED", "FINAL"
};
-bool MapNLtoCRLF;
+static bool MapNLtoCRLF;
-extern int mimeboundary __P((char *, char **));
/*
** MIME8TO7 -- output 8 bit body in 7 bit format
**
@@ -83,8 +89,8 @@ extern int mimeboundary __P((char *, char **));
struct args
{
- char *field; /* name of field */
- char *value; /* value of that field */
+ char *a_field; /* name of field */
+ char *a_value; /* value of that field */
};
int
@@ -113,20 +119,18 @@ mime8to7(mci, header, e, boundaries, flags)
char buf[MAXLINE];
char pvpbuf[MAXLINE];
extern u_char MimeTokenTab[256];
- extern int mime_getchar __P((FILE *, char **, int *));
- extern int mime_getchar_crlf __P((FILE *, char **, int *));
if (tTd(43, 1))
{
- printf("mime8to7: flags = %x, boundaries =", flags);
+ dprintf("mime8to7: flags = %x, boundaries =", flags);
if (boundaries[0] == NULL)
- printf(" <none>");
+ dprintf(" <none>");
else
{
for (i = 0; boundaries[i] != NULL; i++)
- printf(" %s", boundaries[i]);
+ dprintf(" %s", boundaries[i]);
}
- printf("\n");
+ dprintf("\n");
}
MapNLtoCRLF = TRUE;
p = hvalue("Content-Transfer-Encoding", header);
@@ -160,7 +164,7 @@ mime8to7(mci, header, e, boundaries, flags)
if (tTd(43, 40))
{
for (i = 0; pvp[i] != NULL; i++)
- printf("pvp[%d] = \"%s\"\n", i, pvp[i]);
+ dprintf("pvp[%d] = \"%s\"\n", i, pvp[i]);
}
type = *pvp++;
if (*pvp != NULL && strcmp(*pvp, "/") == 0 &&
@@ -178,14 +182,24 @@ mime8to7(mci, header, e, boundaries, flags)
if (*pvp++ == NULL || *pvp == NULL)
break;
+ /* complain about empty values */
+ if (strcmp(*pvp, ";") == 0)
+ {
+ usrerr("mime8to7: Empty parameter in Content-Type header");
+
+ /* avoid bounce loops */
+ e->e_flags |= EF_DONT_MIME;
+ continue;
+ }
+
/* extract field name */
- argv[argc].field = *pvp++;
+ argv[argc].a_field = *pvp++;
/* see if there is a value */
if (*pvp != NULL && strcmp(*pvp, "=") == 0 &&
(*++pvp == NULL || strcmp(*pvp, ";") != 0))
{
- argv[argc].value = *pvp;
+ argv[argc].a_value = *pvp;
argc++;
}
}
@@ -212,10 +226,10 @@ mime8to7(mci, header, e, boundaries, flags)
if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e')))
flags |= M87F_NO8BIT;
-#ifdef USE_B_CLASS
+# ifdef USE_B_CLASS
if (wordinclass(buf, 'b') || wordinclass(type, 'b'))
MapNLtoCRLF = FALSE;
-#endif
+# endif /* USE_B_CLASS */
if (wordinclass(buf, 'q') || wordinclass(type, 'q'))
use_qp = TRUE;
@@ -228,19 +242,18 @@ mime8to7(mci, header, e, boundaries, flags)
if (strcasecmp(type, "multipart") == 0 &&
(!bitset(M87F_NO8BIT, flags) || bitset(M87F_NO8TO7, flags)))
{
- int blen;
if (strcasecmp(subtype, "digest") == 0)
flags |= M87F_DIGEST;
for (i = 0; i < argc; i++)
{
- if (strcasecmp(argv[i].field, "boundary") == 0)
+ if (strcasecmp(argv[i].a_field, "boundary") == 0)
break;
}
- if (i >= argc || argv[i].value == NULL)
+ if (i >= argc || argv[i].a_value == NULL)
{
- syserr("mime8to7: Content-Type: \"%s\": %s boundary",
+ usrerr("mime8to7: Content-Type: \"%s\": %s boundary",
i >= argc ? "missing" : "bogus", p);
p = "---";
@@ -249,29 +262,26 @@ mime8to7(mci, header, e, boundaries, flags)
}
else
{
- p = argv[i].value;
+ p = argv[i].a_value;
stripquotes(p);
}
- blen = strlen(p);
- if (blen > sizeof bbuf - 1)
+ if (strlcpy(bbuf, p, sizeof bbuf) >= sizeof bbuf)
{
- syserr("mime8to7: multipart boundary \"%s\" too long",
+ usrerr("mime8to7: multipart boundary \"%s\" too long",
p);
- blen = sizeof bbuf - 1;
/* avoid bounce loops */
e->e_flags |= EF_DONT_MIME;
}
- strncpy(bbuf, p, blen);
- bbuf[blen] = '\0';
+
if (tTd(43, 1))
- printf("mime8to7: multipart boundary \"%s\"\n", bbuf);
+ dprintf("mime8to7: multipart boundary \"%s\"\n", bbuf);
for (i = 0; i < MAXMIMENESTING; i++)
if (boundaries[i] == NULL)
break;
if (i >= MAXMIMENESTING)
{
- syserr("mime8to7: multipart nesting boundary too deep");
+ usrerr("mime8to7: multipart nesting boundary too deep");
/* avoid bounce loops */
e->e_flags |= EF_DONT_MIME;
@@ -286,6 +296,7 @@ mime8to7(mci, header, e, boundaries, flags)
/* skip the early "comment" prologue */
putline("", mci);
mci->mci_flags &= ~MCIF_INHEADER;
+ bt = MBT_FINAL;
while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
{
bt = mimeboundary(buf, boundaries);
@@ -293,7 +304,7 @@ mime8to7(mci, header, e, boundaries, flags)
break;
putxline(buf, strlen(buf), mci, PXLF_MAPFROM|PXLF_STRIP8BIT);
if (tTd(43, 99))
- printf(" ...%s", buf);
+ dprintf(" ...%s", buf);
}
if (feof(e->e_dfp))
bt = MBT_FINAL;
@@ -304,7 +315,7 @@ mime8to7(mci, header, e, boundaries, flags)
snprintf(buf, sizeof buf, "--%s", bbuf);
putline(buf, mci);
if (tTd(43, 35))
- printf(" ...%s\n", buf);
+ dprintf(" ...%s\n", buf);
collect(e->e_dfp, FALSE, &hdr, e);
if (tTd(43, 101))
putline("+++after collect", mci);
@@ -316,7 +327,7 @@ mime8to7(mci, header, e, boundaries, flags)
snprintf(buf, sizeof buf, "--%s--", bbuf);
putline(buf, mci);
if (tTd(43, 35))
- printf(" ...%s\n", buf);
+ dprintf(" ...%s\n", buf);
boundaries[i] = NULL;
mci->mci_flags &= ~MCIF_INMIME;
@@ -328,12 +339,12 @@ mime8to7(mci, header, e, boundaries, flags)
break;
putxline(buf, strlen(buf), mci, PXLF_MAPFROM|PXLF_STRIP8BIT);
if (tTd(43, 99))
- printf(" ...%s", buf);
+ dprintf(" ...%s", buf);
}
if (feof(e->e_dfp))
bt = MBT_FINAL;
if (tTd(43, 3))
- printf("\t\t\tmime8to7=>%s (multipart)\n",
+ dprintf("\t\t\tmime8to7=>%s (multipart)\n",
MimeBoundaryNames[bt]);
return bt;
}
@@ -429,7 +440,7 @@ mime8to7(mci, header, e, boundaries, flags)
if (tTd(43, 8))
{
- printf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s, type=%s/%s\n",
+ 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,
@@ -443,7 +454,8 @@ mime8to7(mci, header, e, boundaries, flags)
{
/* no encoding necessary */
if (cte != NULL &&
- bitset(MCIF_INMIME, mci->mci_flags) &&
+ bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME,
+ mci->mci_flags) &&
!bitset(M87F_NO8TO7, flags))
{
/*
@@ -458,7 +470,7 @@ mime8to7(mci, header, e, boundaries, flags)
"Content-Transfer-Encoding: %.200s", cte);
putline(buf, mci);
if (tTd(43, 36))
- printf(" ...%s\n", buf);
+ dprintf(" ...%s\n", buf);
}
putline("", mci);
mci->mci_flags &= ~MCIF_INHEADER;
@@ -479,7 +491,7 @@ mime8to7(mci, header, e, boundaries, flags)
int c1, c2;
if (tTd(43, 36))
- printf(" ...Content-Transfer-Encoding: base64\n");
+ dprintf(" ...Content-Transfer-Encoding: base64\n");
putline("Content-Transfer-Encoding: base64", mci);
snprintf(buf, sizeof buf,
"X-MIME-Autoconverted: from 8bit to base64 by %s id %s",
@@ -529,7 +541,7 @@ mime8to7(mci, header, e, boundaries, flags)
/* use quoted-printable encoding */
int c1, c2;
int fromstate;
- BITMAP badchars;
+ BITMAP256 badchars;
/* set up map of characters that must be mapped */
clrbitmap(badchars);
@@ -544,7 +556,7 @@ mime8to7(mci, header, e, boundaries, flags)
setbitn(*p, badchars);
if (tTd(43, 36))
- printf(" ...Content-Transfer-Encoding: quoted-printable\n");
+ dprintf(" ...Content-Transfer-Encoding: quoted-printable\n");
putline("Content-Transfer-Encoding: quoted-printable", mci);
snprintf(buf, sizeof buf,
"X-MIME-Autoconverted: from 8bit to quoted-printable by %s id %s",
@@ -643,7 +655,7 @@ mime8to7(mci, header, e, boundaries, flags)
}
if (tTd(43, 3))
- printf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]);
+ dprintf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]);
return bt;
}
/*
@@ -661,7 +673,7 @@ mime8to7(mci, header, e, boundaries, flags)
** The next character in the input stream.
*/
-int
+static int
mime_getchar(fp, boundaries, btp)
register FILE *fp;
char **boundaries;
@@ -685,7 +697,7 @@ mime_getchar(fp, boundaries, btp)
buflen--;
return *bp++;
}
- else
+ else
c = getc(fp);
bp = buf;
buflen = 0;
@@ -697,7 +709,7 @@ mime_getchar(fp, boundaries, btp)
c = getc(fp);
if (c == '\n')
{
- ungetc(c, fp);
+ (void) ungetc(c, fp);
return c;
}
start = 1;
@@ -767,7 +779,7 @@ mime_getchar(fp, boundaries, btp)
** The next character in the input stream.
*/
-int
+static int
mime_getchar_crlf(fp, boundaries, btp)
register FILE *fp;
char **boundaries;
@@ -804,7 +816,7 @@ mime_getchar_crlf(fp, boundaries, btp)
** enclosure -- i.e., a syntax error.
*/
-int
+static int
mimeboundary(line, boundaries)
register char *line;
char **boundaries;
@@ -812,7 +824,6 @@ mimeboundary(line, boundaries)
int type = MBT_NOTSEP;
int i;
int savec;
- extern int isboundary __P((char *, char **));
if (line[0] != '-' || line[1] != '-' || boundaries == NULL)
return MBT_NOTSEP;
@@ -827,7 +838,7 @@ mimeboundary(line, boundaries)
line[i] = '\0';
if (tTd(43, 5))
- printf("mimeboundary: line=\"%s\"... ", line);
+ dprintf("mimeboundary: line=\"%s\"... ", line);
/* check for this as an intermediate boundary */
if (isboundary(&line[2], boundaries) >= 0)
@@ -843,7 +854,7 @@ mimeboundary(line, boundaries)
line[i] = savec;
if (tTd(43, 5))
- printf("%s\n", MimeBoundaryNames[type]);
+ dprintf("%s\n", MimeBoundaryNames[type]);
return type;
}
/*
@@ -886,7 +897,7 @@ defcharset(e)
**
*/
-int
+static int
isboundary(line, boundaries)
char *line;
char **boundaries;
@@ -900,7 +911,6 @@ isboundary(line, boundaries)
}
return -1;
}
-
#endif /* MIME8TO7 */
#if MIME7TO8
@@ -930,8 +940,6 @@ isboundary(line, boundaries)
** none.
*/
-extern int mime_fromqp __P((u_char *, u_char **, int, int));
-
static char index_64[128] =
{
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
@@ -944,8 +952,7 @@ static char index_64[128] =
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
};
-#define CHAR64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
-
+# define CHAR64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
void
mime7to8(mci, header, e)
@@ -1103,7 +1110,7 @@ mime7to8(mci, header, e)
putxline((char *) fbuf, fbufp - fbuf, mci, PXLF_MAPFROM);
}
if (tTd(43, 3))
- printf("\t\t\tmime7to8 => %s to 8bit done\n", cte);
+ dprintf("\t\t\tmime7to8 => %s to 8bit done\n", cte);
}
/*
** The following is based on Borenstein's "codes.c" module, with simplifying
@@ -1126,9 +1133,9 @@ static char index_hex[128] =
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
};
-#define HEXCHAR(c) (((c) < 0 || (c) > 127) ? -1 : index_hex[(c)])
+# define HEXCHAR(c) (((c) < 0 || (c) > 127) ? -1 : index_hex[(c)])
-int
+static int
mime_fromqp(infile, outfile, state, maxlen)
u_char *infile;
u_char **outfile;
@@ -1185,6 +1192,4 @@ mime_fromqp(infile, outfile, state, maxlen)
*(*outfile)++ = '\0';
return 1;
}
-
-
#endif /* MIME7TO8 */
diff --git a/contrib/sendmail/src/newaliases.1 b/contrib/sendmail/src/newaliases.1
index b9673cd..dffa4ce 100644
--- a/contrib/sendmail/src/newaliases.1
+++ b/contrib/sendmail/src/newaliases.1
@@ -1,4 +1,5 @@
-.\" Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+.\" Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+.\" All rights reserved.
.\" Copyright (c) 1983, 1997 Eric P. Allman. All rights reserved.
.\" Copyright (c) 1985, 1990, 1993
.\" The Regents of the University of California. All rights reserved.
@@ -8,40 +9,33 @@
.\" the sendmail distribution.
.\"
.\"
-.\" @(#)newaliases.1 8.10 (Berkeley) 5/19/1998
+.\" $Id: newaliases.1,v 8.15 1999/06/22 20:41:34 tony Exp $
.\"
-.Dd May 19, 1998
-.Dt NEWALIASES 1
-.Os BSD 4
-.Sh NAME
-.Nm newaliases
-.Nd rebuild the data base for the mail aliases file
-.Sh SYNOPSIS
-.Nm newaliases
-.Sh DESCRIPTION
-.Nm Newaliases
+.TH NEWALIASES 1 "$Date: 1999/06/22 20:41:34 $"
+.SH NAME
+.B newaliases
+\- rebuild the data base for the mail aliases file
+.SH SYNOPSIS
+.B newaliases
+.SH DESCRIPTION
+.B Newaliases
rebuilds the random access data base for the mail aliases file
-.Pa /etc/aliases .
-It must be run each time this file is changed in order
-for the change to take effect.
-.Pp
-.Nm Newaliases
-is identical to
-.Dq Li "sendmail -bi" .
-.Pp
+/etc/mail/aliases. It must be run each time this file is changed
+in order for the change to take effect.
+.PP
+.B Newaliases
+is identical to ``sendmail -bi''.
+.PP
The
-.Nm newaliases
+.B newaliases
utility exits 0 on success, and >0 if an error occurs.
-.Sh FILES
-.Bl -tag -width /etc/aliases -compact
-.It Pa /etc/aliases
+.SH FILES
+.TP 2i
+/etc/mail/aliases
The mail aliases file
-.El
-.Sh SEE ALSO
-.Xr aliases 5 ,
-.Xr sendmail 8
-.Sh HISTORY
+.SH SEE ALSO
+aliases(5), sendmail(8)
+.SH HISTORY
The
-.Nm newaliases
-command appeared in
-.Bx 4.0 .
+.B newaliases
+command appeared in 4.0BSD.
diff --git a/contrib/sendmail/src/parseaddr.c b/contrib/sendmail/src/parseaddr.c
index 86762fd..ad4ed9a 100644
--- a/contrib/sendmail/src/parseaddr.c
+++ b/contrib/sendmail/src/parseaddr.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,10 +12,15 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)parseaddr.c 8.156 (Berkeley) 10/27/1998";
-#endif /* not lint */
+static char id[] = "@(#)$Id: parseaddr.c,v 8.234.4.1 2000/05/25 18:56:16 gshapiro Exp $";
+#endif /* ! lint */
-# include "sendmail.h"
+#include <sendmail.h>
+
+static void allocaddr __P((ADDRESS *, int, char *));
+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 *));
/*
** PARSEADDR -- Parse an address
@@ -52,7 +58,7 @@ static char sccsid[] = "@(#)parseaddr.c 8.156 (Berkeley) 10/27/1998";
*/
/* following delimiters are inherent to the internal algorithms */
-# define DELIMCHARS "()<>,;\r\n" /* default word delimiters */
+#define DELIMCHARS "()<>,;\r\n" /* default word delimiters */
ADDRESS *
parseaddr(addr, a, flags, delim, delimptr, e)
@@ -65,10 +71,8 @@ parseaddr(addr, a, flags, delim, delimptr, e)
{
register char **pvp;
auto char *delimptrbuf;
- bool queueup;
+ bool qup;
char pvpbuf[PSBUFSIZE];
- extern bool invalidaddr __P((char *, char *));
- extern void allocaddr __P((ADDRESS *, int, char *));
/*
** Initialize and prescan address.
@@ -76,7 +80,7 @@ parseaddr(addr, a, flags, delim, delimptr, e)
e->e_to = addr;
if (tTd(20, 1))
- printf("\n--parseaddr(%s)\n", addr);
+ dprintf("\n--parseaddr(%s)\n", addr);
if (delimptr == NULL)
delimptr = &delimptrbuf;
@@ -85,14 +89,14 @@ parseaddr(addr, a, flags, delim, delimptr, e)
if (pvp == NULL)
{
if (tTd(20, 1))
- printf("parseaddr-->NULL\n");
- return (NULL);
+ dprintf("parseaddr-->NULL\n");
+ return NULL;
}
if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr))
{
if (tTd(20, 1))
- printf("parseaddr-->bad address\n");
+ dprintf("parseaddr-->bad address\n");
return NULL;
}
@@ -120,11 +124,11 @@ parseaddr(addr, a, flags, delim, delimptr, e)
** Ruleset 0 does basic parsing. It must resolve.
*/
- queueup = FALSE;
+ qup = FALSE;
if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
- queueup = TRUE;
+ qup = TRUE;
if (rewrite(pvp, 0, 0, e) == EX_TEMPFAIL)
- queueup = TRUE;
+ qup = TRUE;
/*
@@ -139,25 +143,25 @@ parseaddr(addr, a, flags, delim, delimptr, e)
*/
allocaddr(a, flags, addr);
- if (bitset(QBADADDR, a->q_flags))
+ if (QS_IS_BADADDR(a->q_state))
return a;
/*
** If there was a parsing failure, mark it for queueing.
*/
- if (queueup && OpMode != MD_INITALIAS)
+ if (qup && OpMode != MD_INITALIAS)
{
char *msg = "Transient parse error -- message queued for future delivery";
if (e->e_sendmode == SM_DEFER)
msg = "Deferring message until queue run";
if (tTd(20, 1))
- printf("parseaddr: queuing message\n");
+ dprintf("parseaddr: queuing message\n");
message(msg);
if (e->e_message == NULL && e->e_sendmode != SM_DEFER)
e->e_message = newstr(msg);
- a->q_flags |= QQUEUEUP;
+ a->q_state = QS_QUEUEUP;
a->q_status = "4.4.3";
}
@@ -167,11 +171,11 @@ parseaddr(addr, a, flags, delim, delimptr, e)
if (tTd(20, 1))
{
- printf("parseaddr-->");
+ dprintf("parseaddr-->");
printaddr(a, FALSE);
}
- return (a);
+ return a;
}
/*
** INVALIDADDR -- check for address containing meta-characters
@@ -197,9 +201,10 @@ invalidaddr(addr, delimptr)
if (savedelim != '\0')
*delimptr = '\0';
}
- if (strlen(addr) > TOBUFSIZE - 2)
+ if (strlen(addr) > MAXNAME - 1)
{
- usrerr("553 Address too long (%d bytes max)", TOBUFSIZE - 2);
+ usrerr("553 5.1.1 Address too long (%d bytes max)",
+ MAXNAME - 1);
goto failure;
}
for (; *addr != '\0'; addr++)
@@ -214,7 +219,7 @@ invalidaddr(addr, delimptr)
return FALSE;
}
setstat(EX_USAGE);
- usrerr("553 Address contained invalid control characters");
+ usrerr("553 5.1.1 Address contained invalid control characters");
failure:
if (delimptr != NULL && savedelim != '\0')
*delimptr = savedelim;
@@ -238,21 +243,21 @@ failure:
** Copies portions of a into local buffers as requested.
*/
-void
+static void
allocaddr(a, flags, paddr)
register ADDRESS *a;
int flags;
char *paddr;
{
if (tTd(24, 4))
- printf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
+ dprintf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
a->q_paddr = paddr;
if (a->q_user == NULL)
- a->q_user = "";
+ a->q_user = newstr("");
if (a->q_host == NULL)
- a->q_host = "";
+ a->q_host = newstr("");
if (bitset(RF_COPYPARSE, flags))
{
@@ -262,13 +267,14 @@ allocaddr(a, flags, paddr)
}
if (a->q_paddr == NULL)
- a->q_paddr = a->q_user;
+ a->q_paddr = newstr(a->q_user);
}
/*
** PRESCAN -- Prescan name and make it canonical
**
** Scans a name and turns it into a set of tokens. This process
-** deletes blanks and comments (in parentheses).
+** deletes blanks and comments (in parentheses) (if the token type
+** for left paren is SPC).
**
** This routine knows about quoted strings and angle brackets.
**
@@ -299,20 +305,20 @@ allocaddr(a, flags, paddr)
*/
/* states and character types */
-# define OPR 0 /* operator */
-# define ATM 1 /* atom */
-# define QST 2 /* in quoted string */
-# define SPC 3 /* chewing up spaces */
-# define ONE 4 /* pick up one character */
-# define ILL 5 /* illegal character */
+#define OPR 0 /* operator */
+#define ATM 1 /* atom */
+#define QST 2 /* in quoted string */
+#define SPC 3 /* chewing up spaces */
+#define ONE 4 /* pick up one character */
+#define ILL 5 /* illegal character */
-# define NSTATES 6 /* number of states */
-# define TYPE 017 /* mask to select state type */
+#define NSTATES 6 /* number of states */
+#define TYPE 017 /* mask to select state type */
/* meta bits for table */
-# define M 020 /* meta character; don't pass through */
-# define B 040 /* cause a break */
-# define MB M|B /* meta-break */
+#define M 020 /* meta character; don't pass through */
+#define B 040 /* cause a break */
+#define MB M|B /* meta-break */
static short StateTab[NSTATES][NSTATES] =
{
@@ -333,7 +339,7 @@ static u_char TokTypeTab[256] =
/* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */
ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
/* sp ! " # $ % & ' ( ) * + , - . / */
- SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
+ SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
/* @ A B C D E F G H I J K L M N O */
@@ -371,7 +377,7 @@ u_char MimeTokenTab[256] =
/* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */
ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
/* sp ! " # $ % & ' ( ) * + , - . / */
- SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,OPR,ATM,ATM,OPR,
+ SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,OPR,ATM,ATM,OPR,
/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR,
/* @ A B C D E F G H I J K L M N O */
@@ -401,8 +407,46 @@ u_char MimeTokenTab[256] =
ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
};
+/* token type table: don't strip comments */
+u_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,
+ /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* sp ! " # $ % & ' ( ) * + , - . / */
+ SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, OPR,OPR,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* @ A B C D E F G H I J K L M N O */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* P Q R S T U V W X Y Z [ \ ] ^ _ */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* ` a b c d e f g h i j k l m n o */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* p q r s t u v w x y z { | } ~ del */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+
+ /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */
+ OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
+ /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */
+ OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
+ /* sp ! " # $ % & ' ( ) * + , - . / */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* @ A B C D E F G H I J K L M N O */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* P Q R S T U V W X Y Z [ \ ] ^ _ */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* ` a b c d e f g h i j k l m n o */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* p q r s t u v w x y z { | } ~ del */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+};
+
-# define NOCHAR -1 /* signal nothing in lookahead token */
+#define NOCHAR -1 /* signal nothing in lookahead token */
char **
prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
@@ -425,7 +469,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
int state;
int newstate;
char *saveto = CurEnv->e_to;
- static char *av[MAXATOM+1];
+ static char *av[MAXATOM + 1];
static char firsttime = TRUE;
extern int errno;
@@ -442,12 +486,15 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
if (OperatorChars == NULL)
OperatorChars = ".:@[]";
}
- expand(OperatorChars, obuf, sizeof obuf - sizeof DELIMCHARS, CurEnv);
- strcat(obuf, DELIMCHARS);
+ expand(OperatorChars, obuf, sizeof obuf - sizeof DELIMCHARS,
+ CurEnv);
+ (void) strlcat(obuf, DELIMCHARS, sizeof obuf);
for (p = obuf; *p != '\0'; p++)
{
if (TokTypeTab[*p & 0xff] == ATM)
TokTypeTab[*p & 0xff] = OPR;
+ if (TokTypeNoC[*p & 0xff] == ATM)
+ TokTypeNoC[*p & 0xff] = OPR;
}
}
if (toktab == NULL)
@@ -468,9 +515,9 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
CurEnv->e_to = p;
if (tTd(22, 11))
{
- printf("prescan: ");
+ dprintf("prescan: ");
xputs(p);
- (void) putchar('\n');
+ dprintf("\n");
}
do
@@ -485,14 +532,14 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
/* see if there is room */
if (q >= &pvpbuf[pvpbsize - 5])
{
- usrerr("553 Address too long");
+ usrerr("553 5.1.1 Address too long");
if (strlen(addr) > (SIZE_T) MAXNAME)
addr[MAXNAME] = '\0';
returnnull:
if (delimptr != NULL)
*delimptr = p;
CurEnv->e_to = saveto;
- return (NULL);
+ return NULL;
}
/* squirrel it away */
@@ -539,7 +586,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
}
if (tTd(22, 101))
- printf("c=%c, s=%d; ", c, state);
+ dprintf("c=%c, s=%d; ", c, state);
/* chew up special characters */
*q = '\0';
@@ -566,14 +613,15 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
}
else if (state == QST)
{
+ /* EMPTY */
/* do nothing, just avoid next clauses */
}
- else if (c == '(')
+ else if (c == '(' && toktab['('] == SPC)
{
cmntcnt++;
c = NOCHAR;
}
- else if (c == ')')
+ else if (c == ')' && toktab['('] == SPC)
{
if (cmntcnt <= 0)
{
@@ -584,15 +632,17 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
cmntcnt--;
}
else if (cmntcnt > 0)
+ {
c = NOCHAR;
+ }
else if (c == '<')
{
- char *q = p;
+ char *ptr = p;
anglecnt++;
- while (isascii(*q) && isspace(*q))
- q++;
- if (*q == '@')
+ while (isascii(*ptr) && isspace(*ptr))
+ ptr++;
+ if (*ptr == '@')
route_syntax = TRUE;
}
else if (c == '>')
@@ -618,7 +668,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
newstate = StateTab[state][toktab[c & 0xff]];
if (tTd(22, 101))
- printf("ns=%02o\n", newstate);
+ dprintf("ns=%02o\n", newstate);
state = newstate & TYPE;
if (state == ILL)
{
@@ -639,18 +689,18 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
*q++ = '\0';
if (tTd(22, 36))
{
- printf("tok=");
+ dprintf("tok=");
xputs(tok);
- (void) putchar('\n');
+ dprintf("\n");
}
if (avp >= &av[MAXATOM])
{
- usrerr("553 prescan: too many tokens");
+ usrerr("553 5.1.0 prescan: too many tokens");
goto returnnull;
}
if (q - tok > MAXNAME)
{
- usrerr("553 prescan: token too long");
+ usrerr("553 5.1.0 prescan: token too long");
goto returnnull;
}
*avp++ = tok;
@@ -662,17 +712,17 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
*delimptr = p;
if (tTd(22, 12))
{
- printf("prescan==>");
+ dprintf("prescan==>");
printav(av);
}
CurEnv->e_to = saveto;
if (av[0] == NULL)
{
if (tTd(22, 1))
- printf("prescan: null leading token\n");
- return (NULL);
+ dprintf("prescan: null leading token\n");
+ return NULL;
}
- return (av);
+ return av;
}
/*
** REWRITE -- apply rewrite rules to token vector.
@@ -712,12 +762,12 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
struct match
{
- char **first; /* first token matched */
- char **last; /* last token matched */
- char **pattern; /* pointer to pattern */
+ char **match_first; /* first token matched */
+ char **match_last; /* last token matched */
+ char **match_pattern; /* pointer to pattern */
};
-# define MAXMATCH 9 /* max params per rewrite */
+#define MAXMATCH 9 /* max params per rewrite */
int
@@ -729,6 +779,8 @@ rewrite(pvp, ruleset, reclevel, e)
{
register char *ap; /* address pointer */
register char *rp; /* rewrite pointer */
+ register char *rulename; /* ruleset name */
+ register char *prefix;
register char **avp; /* address vector pointer */
register char **rvp; /* rewrite vector pointer */
register struct match *mlp; /* cur ptr into mlist */
@@ -737,25 +789,39 @@ rewrite(pvp, ruleset, reclevel, e)
int rstat = EX_OK; /* return status */
int loopcount;
struct match mlist[MAXMATCH]; /* stores match on LHS */
- char *npvp[MAXATOM+1]; /* temporary space for rebuild */
+ char *npvp[MAXATOM + 1]; /* temporary space for rebuild */
char buf[MAXLINE];
- extern int callsubr __P((char**, int, ENVELOPE *));
- extern int sm_strcasecmp __P((char *, char *));
+ char name[6];
- if (OpMode == MD_TEST || tTd(21, 1))
+ if (ruleset < 0 || ruleset >= MAXRWSETS)
+ {
+ syserr("554 5.3.5 rewrite: illegal ruleset number %d", ruleset);
+ return EX_CONFIG;
+ }
+ rulename = RuleSetNames[ruleset];
+ if (rulename == NULL)
{
- printf("rewrite: ruleset %3d input:", ruleset);
+ snprintf(name, sizeof name, "%d", ruleset);
+ rulename = name;
+ }
+ if (OpMode == MD_TEST)
+ prefix = "";
+ else
+ prefix = "rewrite: ruleset ";
+ if (OpMode == MD_TEST)
+ {
+ printf("%s%-16.16s input:", prefix, rulename);
printav(pvp);
}
- if (ruleset < 0 || ruleset >= MAXRWSETS)
+ else if (tTd(21, 1))
{
- syserr("554 rewrite: illegal ruleset number %d", ruleset);
- return EX_CONFIG;
+ dprintf("%s%-16.16s input:", prefix, rulename);
+ printav(pvp);
}
if (reclevel++ > MaxRuleRecursion)
{
- syserr("rewrite: excessive recursion (max %d), ruleset %d",
- MaxRuleRecursion, ruleset);
+ syserr("rewrite: excessive recursion (max %d), ruleset %s",
+ MaxRuleRecursion, rulename);
return EX_CONFIG;
}
if (pvp == NULL)
@@ -770,7 +836,7 @@ rewrite(pvp, ruleset, reclevel, e)
loopcount = 0;
for (rwr = RewriteRules[ruleset]; rwr != NULL; )
{
- int stat;
+ int status;
/* if already canonical, quit now */
if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET)
@@ -778,7 +844,11 @@ rewrite(pvp, ruleset, reclevel, e)
if (tTd(21, 12))
{
- printf("-----trying rule:");
+ if (tTd(21, 15))
+ dprintf("-----trying rule (line %d):",
+ rwr->r_line);
+ else
+ dprintf("-----trying rule:");
printav(rwr->r_lhs);
}
@@ -788,11 +858,11 @@ rewrite(pvp, ruleset, reclevel, e)
avp = pvp;
if (++loopcount > 100)
{
- syserr("554 Infinite loop in ruleset %d, rule %d",
- ruleset, ruleno);
+ syserr("554 5.3.5 Infinite loop in ruleset %s, rule %d",
+ rulename, ruleno);
if (tTd(21, 1))
{
- printf("workspace: ");
+ dprintf("workspace: ");
printav(pvp);
}
break;
@@ -803,11 +873,11 @@ rewrite(pvp, ruleset, reclevel, e)
rp = *rvp;
if (tTd(21, 35))
{
- printf("ADVANCE rp=");
+ dprintf("ADVANCE rp=");
xputs(rp);
- printf(", ap=");
+ dprintf(", ap=");
xputs(ap);
- printf("\n");
+ dprintf("\n");
}
if (rp == NULL)
{
@@ -825,28 +895,29 @@ rewrite(pvp, ruleset, reclevel, e)
{
case MATCHCLASS:
/* match any phrase in a class */
- mlp->pattern = rvp;
- mlp->first = avp;
+ mlp->match_pattern = rvp;
+ mlp->match_first = avp;
extendclass:
ap = *avp;
if (ap == NULL)
goto backup;
- mlp->last = avp++;
- cataddr(mlp->first, mlp->last, buf, sizeof buf, '\0');
+ mlp->match_last = avp++;
+ cataddr(mlp->match_first, mlp->match_last,
+ buf, sizeof buf, '\0');
if (!wordinclass(buf, rp[1]))
{
if (tTd(21, 36))
{
- printf("EXTEND rp=");
+ dprintf("EXTEND rp=");
xputs(rp);
- printf(", ap=");
+ dprintf(", ap=");
xputs(ap);
- printf("\n");
+ dprintf("\n");
}
goto extendclass;
}
if (tTd(21, 36))
- printf("CLMATCH\n");
+ dprintf("CLMATCH\n");
mlp++;
break;
@@ -855,22 +926,22 @@ rewrite(pvp, ruleset, reclevel, e)
if (wordinclass(ap, rp[1]))
goto backup;
- /* fall through */
+ /* FALLTHROUGH */
case MATCHONE:
case MATCHANY:
/* match exactly one token */
- mlp->pattern = rvp;
- mlp->first = avp;
- mlp->last = avp++;
+ mlp->match_pattern = rvp;
+ mlp->match_first = avp;
+ mlp->match_last = avp++;
mlp++;
break;
case MATCHZANY:
/* match zero or more tokens */
- mlp->pattern = rvp;
- mlp->first = avp;
- mlp->last = avp - 1;
+ mlp->match_pattern = rvp;
+ mlp->match_first = avp;
+ mlp->match_last = avp - 1;
mlp++;
break;
@@ -888,9 +959,9 @@ rewrite(pvp, ruleset, reclevel, e)
*/
ap = macvalue(rp[1], e);
- mlp->first = avp;
+ mlp->match_first = avp;
if (tTd(21, 2))
- printf("rewrite: LHS $&%s => \"%s\"\n",
+ dprintf("rewrite: LHS $&%s => \"%s\"\n",
macname(rp[1]),
ap == NULL ? "(NULL)" : ap);
@@ -902,7 +973,7 @@ rewrite(pvp, ruleset, reclevel, e)
strncasecmp(ap, *avp, strlen(*avp)) != 0)
{
/* no match */
- avp = mlp->first;
+ avp = mlp->match_first;
goto backup;
}
ap += strlen(*avp++);
@@ -927,18 +998,18 @@ rewrite(pvp, ruleset, reclevel, e)
/* match failed -- back up */
while (--mlp >= mlist)
{
- rvp = mlp->pattern;
+ rvp = mlp->match_pattern;
rp = *rvp;
- avp = mlp->last + 1;
+ avp = mlp->match_last + 1;
ap = *avp;
if (tTd(21, 36))
{
- printf("BACKUP rp=");
+ dprintf("BACKUP rp=");
xputs(rp);
- printf(", ap=");
+ dprintf(", ap=");
xputs(ap);
- printf("\n");
+ dprintf("\n");
}
if (ap == NULL)
@@ -950,7 +1021,7 @@ rewrite(pvp, ruleset, reclevel, e)
(*rp & 0377) == MATCHZANY)
{
/* extend binding and continue */
- mlp->last = avp++;
+ mlp->match_last = avp++;
rvp++;
mlp++;
break;
@@ -958,7 +1029,7 @@ rewrite(pvp, ruleset, reclevel, e)
if ((*rp & 0377) == MATCHCLASS)
{
/* extend binding and try again */
- mlp->last = avp;
+ mlp->match_last = avp;
goto extendclass;
}
}
@@ -977,7 +1048,7 @@ rewrite(pvp, ruleset, reclevel, e)
if (mlp < mlist || *rvp != NULL)
{
if (tTd(21, 10))
- printf("----- rule fails\n");
+ dprintf("----- rule fails\n");
rwr = rwr->r_next;
ruleno++;
loopcount = 0;
@@ -987,22 +1058,25 @@ rewrite(pvp, ruleset, reclevel, e)
rvp = rwr->r_rhs;
if (tTd(21, 12))
{
- printf("-----rule matches:");
+ dprintf("-----rule matches:");
printav(rvp);
}
rp = *rvp;
- if ((*rp & 0377) == CANONUSER)
- {
- rvp++;
- rwr = rwr->r_next;
- ruleno++;
- loopcount = 0;
- }
- else if ((*rp & 0377) == CANONHOST)
+ if (rp != NULL)
{
- rvp++;
- rwr = NULL;
+ if ((*rp & 0377) == CANONUSER)
+ {
+ rvp++;
+ rwr = rwr->r_next;
+ ruleno++;
+ loopcount = 0;
+ }
+ else if ((*rp & 0377) == CANONHOST)
+ {
+ rvp++;
+ rwr = NULL;
+ }
}
/* substitute */
@@ -1018,28 +1092,29 @@ rewrite(pvp, ruleset, reclevel, e)
m = &mlist[rp[1] - '1'];
if (m < mlist || m >= mlp)
{
- syserr("554 rewrite: ruleset %d: replacement $%c out of bounds",
- ruleset, rp[1]);
+ syserr("554 5.3.5 rewrite: ruleset %s: replacement $%c out of bounds",
+ rulename, rp[1]);
return EX_CONFIG;
}
if (tTd(21, 15))
{
- printf("$%c:", rp[1]);
- pp = m->first;
- while (pp <= m->last)
+ dprintf("$%c:", rp[1]);
+ pp = m->match_first;
+ while (pp <= m->match_last)
{
- printf(" %lx=\"", (u_long) *pp);
- (void) fflush(stdout);
- printf("%s\"", *pp++);
+ dprintf(" %lx=\"",
+ (u_long) *pp);
+ (void) dflush();
+ dprintf("%s\"", *pp++);
}
- printf("\n");
+ dprintf("\n");
}
- pp = m->first;
- while (pp <= m->last)
+ pp = m->match_first;
+ while (pp <= m->match_last)
{
if (avp >= &npvp[MAXATOM])
{
- syserr("554 rewrite: expansion too long");
+ syserr("554 5.3.0 rewrite: expansion too long");
return EX_DATAERR;
}
*avp++ = *pp++;
@@ -1051,7 +1126,7 @@ rewrite(pvp, ruleset, reclevel, e)
if (avp >= &npvp[MAXATOM])
{
toolong:
- syserr("554 rewrite: expansion too long");
+ syserr("554 5.3.0 rewrite: expansion too long");
return EX_DATAERR;
}
if ((*rp & 0377) != MACRODEXPAND)
@@ -1070,7 +1145,7 @@ rewrite(pvp, ruleset, reclevel, e)
char pvpbuf[PSBUFSIZE];
if (tTd(21, 2))
- printf("rewrite: RHS $&%s => \"%s\"\n",
+ dprintf("rewrite: RHS $&%s => \"%s\"\n",
macname(rp[1]),
mval == NULL ? "(NULL)" : mval);
if (mval == NULL || *mval == '\0')
@@ -1079,7 +1154,7 @@ rewrite(pvp, ruleset, reclevel, e)
/* save the remainder of the input */
for (xpvp = pvp; *xpvp != NULL; xpvp++)
trsize += sizeof *xpvp;
- if (trsize > pvpb1_size)
+ if ((size_t) trsize > pvpb1_size)
{
if (pvpb1 != NULL)
free(pvpb1);
@@ -1087,11 +1162,14 @@ rewrite(pvp, ruleset, reclevel, e)
pvpb1_size = trsize;
}
- bcopy((char *) pvp, (char *) pvpb1, trsize);
+ memmove((char *) pvpb1,
+ (char *) pvp,
+ trsize);
/* scan the new replacement */
xpvp = prescan(mval, '\0', pvpbuf,
- sizeof pvpbuf, NULL, NULL);
+ sizeof pvpbuf, NULL,
+ NULL);
if (xpvp == NULL)
{
/* prescan pre-printed error */
@@ -1102,17 +1180,20 @@ rewrite(pvp, ruleset, reclevel, e)
while (*xpvp != NULL)
{
if (tTd(21, 19))
- printf(" ... %s\n", *xpvp);
+ dprintf(" ... %s\n",
+ *xpvp);
*avp++ = newstr(*xpvp);
if (avp >= &npvp[MAXATOM])
goto toolong;
xpvp++;
}
if (tTd(21, 19))
- printf(" ... DONE\n");
+ dprintf(" ... DONE\n");
/* restore the old trailing input */
- bcopy((char *) pvpb1, (char *) pvp, trsize);
+ memmove((char *) pvp,
+ (char *) pvpb1,
+ trsize);
}
}
}
@@ -1134,12 +1215,11 @@ rewrite(pvp, ruleset, reclevel, e)
char **key_rvp;
char **arg_rvp;
char **default_rvp;
- char buf[MAXNAME + 1];
+ char cbuf[MAXNAME + 1];
char *pvpb1[MAXATOM + 1];
char *argvect[10];
char pvpbuf[PSBUFSIZE];
char *nullpvp[1];
- extern char *map_lookup __P((STAB *, char *, char **, int *, ENVELOPE *));
if ((**rvp & 0377) != HOSTBEGIN &&
(**rvp & 0377) != LOOKUPBEGIN)
@@ -1164,7 +1244,7 @@ rewrite(pvp, ruleset, reclevel, e)
}
map = stab(mapname, ST_MAP, ST_FIND);
if (map == NULL)
- syserr("554 rewrite: map %s not found", mapname);
+ syserr("554 5.3.0 rewrite: map %s not found", mapname);
/* extract the match part */
key_rvp = ++rvp;
@@ -1217,19 +1297,20 @@ rewrite(pvp, ruleset, reclevel, e)
/* save the remainder of the input string */
trsize = (int) (avp - rvp + 1) * sizeof *rvp;
- bcopy((char *) rvp, (char *) pvpb1, trsize);
+ memmove((char *) pvpb1, (char *) rvp, trsize);
/* look it up */
- cataddr(key_rvp, NULL, buf, sizeof buf, '\0');
- argvect[0] = buf;
- replac = map_lookup(map, buf, argvect, &rstat, e);
+ cataddr(key_rvp, NULL, cbuf, sizeof cbuf,
+ map == NULL ? '\0' : map->s_map.map_spacesub);
+ argvect[0] = cbuf;
+ replac = map_lookup(map, cbuf, argvect, &rstat, e);
/* if no replacement, use default */
if (replac == NULL && default_rvp != NULL)
{
/* create the default */
- cataddr(default_rvp, NULL, buf, sizeof buf, '\0');
- replac = buf;
+ cataddr(default_rvp, NULL, cbuf, sizeof cbuf, '\0');
+ replac = cbuf;
}
if (replac == NULL)
@@ -1273,29 +1354,33 @@ rewrite(pvp, ruleset, reclevel, e)
** Check for subroutine calls.
*/
- stat = callsubr(npvp, reclevel, e);
- if (rstat == EX_OK || stat == EX_TEMPFAIL)
- rstat = stat;
+ status = callsubr(npvp, reclevel, e);
+ if (rstat == EX_OK || status == EX_TEMPFAIL)
+ rstat = status;
/* copy vector back into original space. */
for (avp = npvp; *avp++ != NULL;)
continue;
- bcopy((char *) npvp, (char *) pvp,
+ memmove((char *) pvp, (char *) npvp,
(int) (avp - npvp) * sizeof *avp);
-
+
if (tTd(21, 4))
{
- printf("rewritten as:");
+ dprintf("rewritten as:");
printav(pvp);
}
}
- if (OpMode == MD_TEST || tTd(21, 1))
+ if (OpMode == MD_TEST)
{
- printf("rewrite: ruleset %3d returns:", ruleset);
+ printf("%s%-16.16s returns:", prefix, rulename);
+ printav(pvp);
+ }
+ else if (tTd(21, 1))
+ {
+ dprintf("%s%-16.16s returns:", prefix, rulename);
printav(pvp);
}
-
return rstat;
}
/*
@@ -1313,17 +1398,17 @@ rewrite(pvp, ruleset, reclevel, e)
** pvp is modified.
*/
-int
+static int
callsubr(pvp, reclevel, e)
char **pvp;
int reclevel;
ENVELOPE *e;
{
- char **avp;
- char **rvp;
+ char **avp;
+ char **rvp;
register int i;
int subr;
- int stat;
+ int status;
int rstat = EX_OK;
char *tpvp[MAXATOM + 1];
@@ -1340,8 +1425,9 @@ callsubr(pvp, reclevel, e)
}
if (tTd(21, 3))
- printf("-----callsubr %s (%d)\n", avp[1], subr);
-
+ dprintf("-----callsubr %s (%d)\n",
+ avp[1], subr);
+
/*
** Take care of possible inner calls first.
** use a full size temporary buffer to avoid
@@ -1353,9 +1439,9 @@ callsubr(pvp, reclevel, e)
tpvp[i - 2] = avp[i];
tpvp[i - 2] = NULL;
- stat = callsubr(tpvp, reclevel, e);
- if (rstat == EX_OK || stat == EX_TEMPFAIL)
- rstat = stat;
+ status = callsubr(tpvp, reclevel, e);
+ if (rstat == EX_OK || status == EX_TEMPFAIL)
+ rstat = status;
/*
** Now we need to call the ruleset specified for
@@ -1364,9 +1450,9 @@ callsubr(pvp, reclevel, e)
** since it has all the data we want to rewrite.
*/
- stat = rewrite(tpvp, subr, reclevel, e);
- if (rstat == EX_OK || stat == EX_TEMPFAIL)
- rstat = stat;
+ status = rewrite(tpvp, subr, reclevel, e);
+ if (rstat == EX_OK || status == EX_TEMPFAIL)
+ rstat = status;
/*
** Find length of tpvp and current offset into
@@ -1380,7 +1466,7 @@ callsubr(pvp, reclevel, e)
continue;
if (((rvp - tpvp) + (avp - pvp)) > MAXATOM)
{
- syserr("554 callsubr: expansion too long");
+ syserr("554 5.3.0 callsubr: expansion too long");
return EX_DATAERR;
}
@@ -1420,74 +1506,77 @@ callsubr(pvp, reclevel, e)
** NULL -- if there was no data for the given key.
*/
-char *
-map_lookup(map, key, argvect, pstat, e)
- STAB *map;
+static char *
+map_lookup(smap, key, argvect, pstat, e)
+ STAB *smap;
char key[];
char **argvect;
int *pstat;
ENVELOPE *e;
{
- auto int stat = EX_OK;
+ auto int status = EX_OK;
+ MAP *map;
char *replac;
- if (e->e_sendmode == SM_DEFER)
+ if (smap == NULL)
+ return NULL;
+
+ map = &smap->s_map;
+ DYNOPENMAP(map);
+
+ if (e->e_sendmode == SM_DEFER &&
+ bitset(MF_DEFER, map->map_mflags))
{
/* don't do any map lookups */
if (tTd(60, 1))
- printf("map_lookup(%s, %s) => DEFERRED\n",
- map->s_name, key);
+ dprintf("map_lookup(%s, %s) => DEFERRED\n",
+ smap->s_name, key);
*pstat = EX_TEMPFAIL;
return NULL;
}
- if (map == NULL || !bitset(MF_OPEN, map->s_map.map_mflags))
- return NULL;
- if (!bitset(MF_KEEPQUOTES, map->s_map.map_mflags))
+ if (!bitset(MF_KEEPQUOTES, map->map_mflags))
stripquotes(key);
- /* XXX should try to auto-open the map here */
-
if (tTd(60, 1))
{
- printf("map_lookup(%s, %s", map->s_name, key);
+ dprintf("map_lookup(%s, %s", smap->s_name, key);
if (tTd(60, 5))
{
int i;
for (i = 0; argvect[i] != NULL; i++)
- printf(", %%%d=%s", i, argvect[i]);
+ dprintf(", %%%d=%s", i, argvect[i]);
}
- printf(") => ");
+ dprintf(") => ");
}
- replac = (*map->s_map.map_class->map_lookup)(&map->s_map,
- key, argvect, &stat);
+ replac = (*map->map_class->map_lookup)(map, key, argvect, &status);
if (tTd(60, 1))
- printf("%s (%d)\n",
+ dprintf("%s (%d)\n",
replac != NULL ? replac : "NOT FOUND",
- stat);
+ status);
- /* should recover if stat == EX_TEMPFAIL */
- if (stat == EX_TEMPFAIL && !bitset(MF_NODEFER, map->s_map.map_mflags))
+ /* should recover if status == EX_TEMPFAIL */
+ if (status == EX_TEMPFAIL && !bitset(MF_NODEFER, map->map_mflags))
{
*pstat = EX_TEMPFAIL;
if (tTd(60, 1))
- printf("map_lookup(%s, %s) tempfail: errno=%d\n",
- map->s_name, key, errno);
+ 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,
"%.80s map: lookup (%s): deferred",
- map->s_name,
+ smap->s_name,
shortenstring(key, MAXSHORTSTR));
e->e_message = newstr(mbuf);
}
}
- if (stat == EX_TEMPFAIL && map->s_map.map_tapp != NULL)
+ if (status == EX_TEMPFAIL && map->map_tapp != NULL)
{
- size_t i = strlen(key) + strlen(map->s_map.map_tapp) + 1;
+ size_t i = strlen(key) + strlen(map->map_tapp) + 1;
static char *rwbuf = NULL;
static size_t rwbuflen = 0;
@@ -1498,15 +1587,51 @@ map_lookup(map, key, argvect, pstat, e)
rwbuflen = i;
rwbuf = (char *) xalloc(rwbuflen);
}
- snprintf(rwbuf, rwbuflen, "%s%s", key, map->s_map.map_tapp);
+ snprintf(rwbuf, rwbuflen, "%s%s", key, map->map_tapp);
if (tTd(60, 4))
- printf("map_lookup tempfail: returning \"%s\"\n",
+ dprintf("map_lookup tempfail: returning \"%s\"\n",
rwbuf);
return rwbuf;
}
return replac;
}
/*
+** INITERRMAILERS -- initialize error and discard mailers
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** initializes error and discard mailers.
+*/
+
+static MAILER discardmailer;
+static MAILER errormailer;
+static char *discardargv[] = { "DISCARD", NULL };
+static char *errorargv[] = { "ERROR", NULL };
+
+void
+initerrmailers()
+{
+ if (discardmailer.m_name == NULL)
+ {
+ /* initialize the discard mailer */
+ discardmailer.m_name = "*discard*";
+ discardmailer.m_mailer = "DISCARD";
+ discardmailer.m_argv = discardargv;
+ }
+ if (errormailer.m_name == NULL)
+ {
+ /* initialize the bogus mailer */
+ errormailer.m_name = "*error*";
+ errormailer.m_mailer = "ERROR";
+ errormailer.m_argv = errorargv;
+ }
+}
+ /*
** BUILDADDR -- build address from token vector.
**
** Parameters:
@@ -1525,7 +1650,7 @@ map_lookup(map, key, argvect, pstat, e)
** fills in 'a'
*/
-struct errcodes
+static struct errcodes
{
char *ec_name; /* name of error code */
int ec_code; /* numeric code */
@@ -1540,11 +1665,12 @@ struct errcodes
{ "protocol", EX_PROTOCOL },
#ifdef EX_CONFIG
{ "config", EX_CONFIG },
-#endif
+#endif /* EX_CONFIG */
{ NULL, EX_UNAVAILABLE }
};
-ADDRESS *
+
+static ADDRESS *
buildaddr(tv, a, flags, e)
register char **tv;
register ADDRESS *a;
@@ -1557,46 +1683,33 @@ buildaddr(tv, a, flags, e)
char *mname;
char **hostp;
char hbuf[MAXNAME + 1];
- static MAILER discardmailer;
- static MAILER errormailer;
- static char *discardargv[] = { "DISCARD", NULL };
- static char *errorargv[] = { "ERROR", NULL };
static char ubuf[MAXNAME + 2];
if (tTd(24, 5))
{
- printf("buildaddr, flags=%x, tv=", flags);
+ dprintf("buildaddr, flags=%x, tv=", flags);
printav(tv);
}
if (a == NULL)
a = (ADDRESS *) xalloc(sizeof *a);
- bzero((char *) a, sizeof *a);
+ memset((char *) a, '\0', sizeof *a);
+ hbuf[0] = '\0';
/* set up default error return flags */
a->q_flags |= DefaultNotify;
- if (discardmailer.m_name == NULL)
- {
- /* initialize the discard mailer */
- discardmailer.m_name = "*discard*";
- discardmailer.m_mailer = "DISCARD";
- discardmailer.m_argv = discardargv;
- }
-
/* figure out what net/mailer to use */
if (*tv == NULL || (**tv & 0377) != CANONNET)
{
- syserr("554 buildaddr: no mailer in parsed address");
+ syserr("554 5.3.5 buildaddr: no mailer in parsed address");
badaddr:
- a->q_flags |= QBADADDR;
- a->q_mailer = &errormailer;
- if (errormailer.m_name == NULL)
+ if (ExitStat == EX_TEMPFAIL)
+ a->q_state = QS_QUEUEUP;
+ else
{
- /* initialize the bogus mailer */
- errormailer.m_name = "*error*";
- errormailer.m_mailer = "ERROR";
- errormailer.m_argv = errorargv;
+ a->q_state = QS_BADADDR;
+ a->q_mailer = &errormailer;
}
return a;
}
@@ -1611,7 +1724,7 @@ badaddr:
tv++;
if (*tv == NULL)
{
- syserr("554 buildaddr: no user");
+ syserr("554 5.3.5 buildaddr: no user");
goto badaddr;
}
if (tv == hostp)
@@ -1623,14 +1736,17 @@ badaddr:
/* save away the host name */
if (strcasecmp(mname, "error") == 0)
{
+ /* Set up triplet for use by -bv */
+ a->q_mailer = &errormailer;
+ a->q_user = newstr(ubuf);
+
if (hostp != NULL)
{
register struct errcodes *ep;
+ a->q_host = newstr(hbuf);
if (strchr(hbuf, '.') != NULL)
{
- extern int dsntoexitstat __P((char *));
-
a->q_status = newstr(hbuf);
setstat(dsntoexitstat(hbuf));
}
@@ -1647,34 +1763,38 @@ badaddr:
}
}
else
+ {
+ a->q_host = NULL;
setstat(EX_UNAVAILABLE);
+ }
stripquotes(ubuf);
- if (isascii(ubuf[0]) && isdigit(ubuf[0]) &&
- isascii(ubuf[1]) && isdigit(ubuf[1]) &&
- isascii(ubuf[2]) && isdigit(ubuf[2]) &&
- ubuf[3] == ' ')
+ if (ISSMTPCODE(ubuf) && ubuf[3] == ' ')
{
- char fmt[10];
-
- strncpy(fmt, ubuf, 3);
- strcpy(&fmt[3], " %s");
- usrerr(fmt, ubuf + 4);
-
- /*
- ** If this is a 4xx code and we aren't running
- ** SMTP on our input, bounce this message;
- ** otherwise it disappears without a trace.
- */
+ char fmt[16];
+ int off;
- if (fmt[0] == '4' && OpMode != MD_SMTP &&
- OpMode != MD_DAEMON)
+ if ((off = isenhsc(ubuf + 4, ' ')) > 0)
+ {
+ ubuf[off + 4] = '\0';
+ off += 5;
+ }
+ else
{
- e->e_flags |= EF_FATALERRS;
+ off = 4;
+ ubuf[3] = '\0';
}
+ (void) snprintf(fmt, sizeof fmt, "%s %%s", ubuf);
+ if (off > 4)
+ usrerr(fmt, ubuf + off);
+ else if (isenhsc(hbuf, '\0') > 0)
+ usrerrenh(hbuf, fmt, ubuf + off);
+ else
+ usrerr(fmt, ubuf + off);
+ /* XXX ubuf[off - 1] = ' '; */
}
else
{
- usrerr("553 %s", ubuf);
+ usrerr("553 5.3.0 %s", ubuf);
}
goto badaddr;
}
@@ -1686,7 +1806,7 @@ badaddr:
}
if (m == NULL)
{
- syserr("554 buildaddr: unknown mailer %s", mname);
+ syserr("554 5.3.5 buildaddr: unknown mailer %s", mname);
goto badaddr;
}
a->q_mailer = m;
@@ -1696,7 +1816,7 @@ badaddr:
{
if (!bitnset(M_LOCALMAILER, m->m_flags))
{
- syserr("554 buildaddr: no host");
+ syserr("554 5.3.5 buildaddr: no host");
goto badaddr;
}
a->q_host = NULL;
@@ -1735,7 +1855,18 @@ badaddr:
/* 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.
+ */
+
+ if (ConfigLevel >= 9 ||
+ !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);
@@ -1746,7 +1877,7 @@ badaddr:
/* save the result for the command line/RCPT argument */
cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');
- a->q_user = ubuf;
+ a->q_user = newstr(ubuf);
/*
** Do mapping to lower case as requested by mailer
@@ -1759,7 +1890,7 @@ badaddr:
if (tTd(24, 6))
{
- printf("buildaddr => ");
+ dprintf("buildaddr => ");
printaddr(a, FALSE);
}
return a;
@@ -1796,25 +1927,31 @@ cataddr(pvp, evp, buf, sz, spacesub)
register int i;
register char *p;
+ if (sz <= 0)
+ return;
+
if (spacesub == '\0')
spacesub = SpaceSub;
if (pvp == NULL)
{
- (void) strcpy(buf, "");
+ *buf = '\0';
return;
}
p = buf;
sz -= 2;
- while (*pvp != NULL && (i = strlen(*pvp)) < sz)
+ while (*pvp != NULL && (i = strlen(*pvp)) < sz - 1)
{
natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
if (oatomtok && natomtok)
+ {
*p++ = spacesub;
- (void) strcpy(p, *pvp);
+ --sz;
+ }
+ (void) strlcpy(p, *pvp, sz);
oatomtok = natomtok;
p += i;
- sz -= i + 1;
+ sz -= i;
if (pvp++ == evp)
break;
}
@@ -1846,11 +1983,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)
@@ -1860,24 +1997,24 @@ sameaddr(a, b)
if (ca != NULL && cb != NULL &&
bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
ca->q_uid != cb->q_uid)
- return (FALSE);
+ return FALSE;
}
/* otherwise compare hosts (but be careful for NULL ptrs) */
if (a->q_host == b->q_host)
{
/* probably both null pointers */
- return (TRUE);
+ return TRUE;
}
if (a->q_host == NULL || b->q_host == NULL)
{
/* only one is a null pointer */
- return (FALSE);
+ return FALSE;
}
if (strcmp(a->q_host, b->q_host) != 0)
- return (FALSE);
+ return FALSE;
- return (TRUE);
+ return TRUE;
}
/*
** PRINTADDR -- print address (for debugging)
@@ -1899,17 +2036,12 @@ struct qflags
u_long qf_bit;
};
-struct qflags AddressFlags[] =
+static struct qflags AddressFlags[] =
{
- { "QDONTSEND", QDONTSEND },
- { "QBADADDR", QBADADDR },
{ "QGOODUID", QGOODUID },
{ "QPRIMARY", QPRIMARY },
- { "QQUEUEUP", QQUEUEUP },
- { "QSENT", QSENT },
{ "QNOTREMOTE", QNOTREMOTE },
{ "QSELFREF", QSELFREF },
- { "QVERIFIED", QVERIFIED },
{ "QBOGUSSHELL", QBOGUSSHELL },
{ "QUNSAFEADDR", QUNSAFEADDR },
{ "QPINGONSUCCESS", QPINGONSUCCESS },
@@ -1962,7 +2094,70 @@ printaddr(a, follow)
printf("\tuser `%s', ruser `%s'\n",
a->q_user,
a->q_ruser == NULL ? "<null>" : a->q_ruser);
- printf("\tnext=%lx, alias %lx, uid %d, gid %d\n",
+ printf("\tstate=");
+ switch (a->q_state)
+ {
+ case QS_OK:
+ printf("OK");
+ break;
+
+ case QS_DONTSEND:
+ printf("DONTSEND");
+ break;
+
+ case QS_BADADDR:
+ printf("BADADDR");
+ break;
+
+ case QS_QUEUEUP:
+ printf("QUEUEUP");
+ break;
+
+ case QS_SENT:
+ printf("SENT");
+ break;
+
+ case QS_VERIFIED:
+ printf("VERIFIED");
+ break;
+
+ case QS_EXPANDED:
+ printf("EXPANDED");
+ break;
+
+ case QS_SENDER:
+ printf("SENDER");
+ break;
+
+ case QS_CLONED:
+ printf("CLONED");
+ break;
+
+ case QS_DISCARDED:
+ printf("DISCARDED");
+ break;
+
+ case QS_REPLACED:
+ printf("REPLACED");
+ break;
+
+ case QS_REMOVED:
+ printf("REMOVED");
+ break;
+
+ case QS_DUPLICATE:
+ printf("DUPLICATE");
+ break;
+
+ case QS_INCLUDED:
+ printf("INCLUDED");
+ break;
+
+ default:
+ printf("%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);
@@ -1988,7 +2183,8 @@ printaddr(a, follow)
printf("\trstatus=\"%s\"\n",
a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
printf("\tspecificity=%d, statdate=%s\n",
- a->q_specificity, ctime(&a->q_statdate));
+ a->q_specificity,
+ a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate));
if (!follow)
return;
@@ -2052,20 +2248,38 @@ remotename(name, m, flags, pstat, e)
static char buf[MAXNAME + 1];
char lbuf[MAXNAME + 1];
char pvpbuf[PSBUFSIZE];
- extern char *crackaddr __P((char *));
+#if _FFR_ADDR_TYPE
+ char addrtype[4];
+#endif /* _FFR_ADDR_TYPE */
if (tTd(12, 1))
- printf("remotename(%s)\n", name);
+ 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);
+ 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 */
/*
** Do a heuristic crack of this name to extract any comment info.
@@ -2087,24 +2301,36 @@ remotename(name, m, flags, pstat, e)
pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
if (pvp == NULL)
- return (name);
+ return name;
if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
*pstat = EX_TEMPFAIL;
if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
{
/* append from domain to this address */
register char **pxp = pvp;
+ int l = MAXATOM; /* size of buffer for pvp */
/* see if there is an "@domain" in the current name */
while (*pxp != NULL && strcmp(*pxp, "@") != 0)
+ {
pxp++;
+ --l;
+ }
if (*pxp == NULL)
{
/* no.... append the "@domain" from the sender */
register char **qxq = e->e_fromdomain;
while ((*pxp++ = *qxq++) != NULL)
- continue;
+ {
+ if (--l <= 0)
+ {
+ *--pxp = NULL;
+ usrerr("553 5.1.0 remotename: too many tokens");
+ *pstat = EX_UNAVAILABLE;
+ break;
+ }
+ }
if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
*pstat = EX_TEMPFAIL;
}
@@ -2159,8 +2385,8 @@ remotename(name, m, flags, pstat, e)
define('g', oldg, e);
if (tTd(12, 1))
- printf("remotename => `%s'\n", buf);
- return (buf);
+ dprintf("remotename => `%s'\n", buf);
+ return buf;
}
/*
** MAPLOCALUSER -- run local username through ruleset 5 for final redirection
@@ -2194,33 +2420,37 @@ maplocaluser(a, sendq, aliaslevel, e)
if (tTd(29, 1))
{
- printf("maplocaluser: ");
+ dprintf("maplocaluser: ");
printaddr(a, FALSE);
}
pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
if (pvp == NULL)
{
if (tTd(29, 9))
- printf("maplocaluser: cannot prescan %s\n", a->q_user);
+ 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);
-
+
+#if _FFR_ADDR_TYPE
+ define(macid("{addr_type}", NULL), "e r", e);
+#endif /* _FFR_ADDR_TYPE */
if (rewrite(pvp, 5, 0, e) == EX_TEMPFAIL)
{
if (tTd(29, 9))
- printf("maplocaluser: rewrite tempfail\n");
- a->q_flags |= QQUEUEUP;
+ dprintf("maplocaluser: rewrite tempfail\n");
+ a->q_state = QS_QUEUEUP;
a->q_status = "4.4.3";
return;
}
if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
{
if (tTd(29, 9))
- printf("maplocaluser: doesn't resolve\n");
+ dprintf("maplocaluser: doesn't resolve\n");
return;
}
@@ -2229,7 +2459,7 @@ maplocaluser(a, sendq, aliaslevel, e)
if (a1 == NULL || sameaddr(a, a1))
{
if (tTd(29, 9))
- printf("maplocaluser: address unchanged\n");
+ dprintf("maplocaluser: address unchanged\n");
if (a1 != NULL)
free(a1);
return;
@@ -2238,17 +2468,18 @@ maplocaluser(a, sendq, aliaslevel, e)
/* 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 = a->q_paddr;
+ a1->q_paddr = newstr(a->q_paddr);
+ a1->q_orcpt = a->q_orcpt;
/* mark old address as dead; insert new address */
- a->q_flags |= QDONTSEND;
+ a->q_state = QS_REPLACED;
if (tTd(29, 5))
{
- printf("maplocaluser: QDONTSEND ");
+ dprintf("maplocaluser: QS_REPLACED ");
printaddr(a, FALSE);
}
a1->q_alias = a;
- allocaddr(a1, RF_COPYALL, a->q_paddr);
+ allocaddr(a1, RF_COPYALL, newstr(a->q_paddr));
(void) recipient(a1, sendq, aliaslevel, e);
}
/*
@@ -2271,6 +2502,7 @@ dequote_init(map, args)
{
register char *p = args;
+ /* there is no check whether there is really an argument */
map->map_mflags |= MF_KEEPQUOTES;
for (;;)
{
@@ -2284,8 +2516,13 @@ dequote_init(map, args)
map->map_app = ++p;
break;
+ case 'D':
+ map->map_mflags |= MF_DEFER;
+ break;
+
+ case 'S':
case 's':
- map->map_coldelim = *++p;
+ map->map_spacesub = *++p;
break;
}
while (*p != '\0' && !(isascii(*p) && isspace(*p)))
@@ -2330,7 +2567,7 @@ dequote_map(map, name, av, statp)
int spacecnt = 0;
bool quotemode = FALSE;
bool bslashmode = FALSE;
- char spacesub = map->map_coldelim;
+ char spacesub = map->map_spacesub;
for (p = q = name; (c = *p++) != '\0'; )
{
@@ -2360,6 +2597,7 @@ dequote_map(map, name, av, statp)
break;
case ' ':
+ case '\t':
spacecnt++;
break;
}
@@ -2403,6 +2641,9 @@ dequote_map(map, name, av, statp)
** p1 -- the first string to check.
** p2 -- the second string to check -- may be null.
** e -- the current envelope.
+** rmcomm -- remove comments?
+** cnt -- count rejections (statistics)?
+** logl -- logging level
**
** Returns:
** EX_OK -- if the rwset doesn't resolve to $#error
@@ -2410,11 +2651,13 @@ dequote_map(map, name, av, statp)
*/
int
-rscheck(rwset, p1, p2, e)
+rscheck(rwset, p1, p2, e, rmcomm, cnt, logl)
char *rwset;
char *p1;
char *p2;
ENVELOPE *e;
+ bool rmcomm, cnt;
+ int logl;
{
char *buf;
int bufsize;
@@ -2431,7 +2674,7 @@ rscheck(rwset, p1, p2, e)
extern char MsgBuf[];
if (tTd(48, 2))
- printf("rscheck(%s, %s, %s)\n", rwset, p1,
+ dprintf("rscheck(%s, %s, %s)\n", rwset, p1,
p2 == NULL ? "(NULL)" : p2);
rsno = strtorwset(rwset, NULL, ST_FIND);
@@ -2464,12 +2707,13 @@ rscheck(rwset, p1, p2, e)
}
SuprErrs = TRUE;
QuickAbort = FALSE;
- pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
+ pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL,
+ rmcomm ? NULL : TokTypeNoC);
SuprErrs = saveSuprErrs;
if (pvp == NULL)
{
if (tTd(48, 2))
- printf("rscheck: cannot prescan input\n");
+ dprintf("rscheck: cannot prescan input\n");
/*
syserr("rscheck: cannot prescan input: \"%s\"",
shortenstring(buf, MAXSHORTSTR));
@@ -2488,11 +2732,11 @@ rscheck(rwset, p1, p2, e)
if (strcmp(pvp[1], "discard") == 0)
{
if (tTd(48, 2))
- printf("rscheck: discard mailer selected\n");
+ dprintf("rscheck: discard mailer selected\n");
e->e_flags |= EF_DISCARD;
discard = TRUE;
}
- else
+ else
{
int savelogusrerrs = LogUsrErrs;
static bool logged = FALSE;
@@ -2506,12 +2750,13 @@ rscheck(rwset, p1, p2, e)
ExitStat = saveexitstat;
if (!logged)
{
- markstats(e, &a1, TRUE);
+ if (cnt)
+ markstats(e, &a1, TRUE);
logged = TRUE;
}
}
-
- if (LogLevel >= 4)
+
+ if (LogLevel >= logl)
{
char *relay;
char *p;
diff --git a/contrib/sendmail/src/queue.c b/contrib/sendmail/src/queue.c
index b02fc08..209d5b7 100644
--- a/contrib/sendmail/src/queue.c
+++ b/contrib/sendmail/src/queue.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -10,20 +11,28 @@
*
*/
-# include "sendmail.h"
+
+#include <sendmail.h>
#ifndef lint
-#if QUEUE
-static char sccsid[] = "@(#)queue.c 8.211 (Berkeley) 1/25/1999 (with queueing)";
-#else
-static char sccsid[] = "@(#)queue.c 8.211 (Berkeley) 1/25/1999 (without queueing)";
-#endif
-#endif /* not lint */
+# if QUEUE
+static char id[] = "@(#)$Id: queue.c,v 8.343.4.11 2000/07/14 05:55:51 gshapiro Exp $ (with queueing)";
+# else /* QUEUE */
+static char id[] = "@(#)$Id: queue.c,v 8.343.4.11 2000/07/14 05:55:51 gshapiro Exp $ (without queueing)";
+# endif /* QUEUE */
+#endif /* ! lint */
-# include <errno.h>
# include <dirent.h>
-# if QUEUE
+#if QUEUE
+
+# if _FFR_QUEUEDELAY
+# define QF_VERSION 5 /* 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 */
/*
** Work queue.
@@ -42,11 +51,23 @@ struct work
typedef struct work WORK;
-WORK *WorkQ; /* queue of things to be done */
+static WORK *WorkQ; /* queue of things to be done */
+
+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));
+static char *strrev __P((char *));
+static ADDRESS *setctluser __P((char *, int));
+static int workcmpf0();
+static int workcmpf1();
+static int workcmpf2();
+static int workcmpf3();
+static int workcmpf4();
-#define QF_VERSION 2 /* version number of this queue format */
-
-extern int orderq __P((bool));
/*
** QUEUEUP -- queue a message up for future transmission.
**
@@ -62,6 +83,8 @@ extern int orderq __P((bool));
** The queue file is left locked.
*/
+# define TEMPQF_LETTER 'T'
+
void
queueup(e, announce)
register ENVELOPE *e;
@@ -71,15 +94,14 @@ queueup(e, announce)
register FILE *tfp;
register HDR *h;
register ADDRESS *q;
- int fd;
+ int tfd = -1;
int i;
bool newid;
register char *p;
MAILER nullmailer;
MCI mcibuf;
- char tf[MAXQFNAME];
+ char tf[MAXPATHLEN];
char buf[MAXLINE];
- extern void printctladdr __P((ADDRESS *, FILE *));
/*
** Create control file.
@@ -88,7 +110,7 @@ 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 */
- strcpy(tf, queuename(e, 't'));
+ (void) strlcpy(tf, queuename(e, 't'), sizeof tf);
tfp = e->e_lockfp;
if (tfp == NULL)
newid = FALSE;
@@ -96,68 +118,92 @@ queueup(e, announce)
/* if newid, just write the qf file directly (instead of tf file) */
if (!newid)
{
+ int flags;
+
+ flags = O_CREAT|O_WRONLY|O_EXCL;
+
/* get a locked tf file */
for (i = 0; i < 128; i++)
{
- fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode);
- if (fd < 0)
+ if (tfd < 0)
{
- if (errno != EEXIST)
- break;
- if (LogLevel > 0 && (i % 32) == 0)
- sm_syslog(LOG_ALERT, e->e_id,
- "queueup: cannot create %s, uid=%d: %s",
- tf, geteuid(), errstring(errno));
+#if _FFR_QUEUE_FILE_MODE
+ MODE_T oldumask;
+
+ 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)
+ {
+ if (errno != EEXIST)
+ break;
+ if (LogLevel > 0 && (i % 32) == 0)
+ sm_syslog(LOG_ALERT, e->e_id,
+ "queueup: cannot create %s, uid=%d: %s",
+ tf, geteuid(), errstring(errno));
+ }
}
- else
+ if (tfd >= 0)
{
- if (lockfile(fd, tf, NULL, LOCK_EX|LOCK_NB))
+ if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB))
break;
else if (LogLevel > 0 && (i % 32) == 0)
sm_syslog(LOG_ALERT, e->e_id,
- "queueup: cannot lock %s: %s",
- tf, errstring(errno));
- close(fd);
+ "queueup: cannot lock %s: %s",
+ tf, errstring(errno));
+ if ((i % 32) == 31)
+ {
+ (void) close(tfd);
+ tfd = -1;
+ }
}
if ((i % 32) == 31)
{
/* save the old temp file away */
- (void) rename(tf, queuename(e, 'T'));
+ (void) rename(tf, queuename(e, TEMPQF_LETTER));
}
else
- sleep(i % 32);
+ (void) sleep(i % 32);
}
- if (fd < 0 || (tfp = fdopen(fd, "w")) == NULL)
+ if (tfd < 0 || (tfp = fdopen(tfd, "w")) == NULL)
{
+ int save_errno = errno;
+
printopenfds(TRUE);
+ errno = save_errno;
syserr("!queueup: cannot create queue temp file %s, uid=%d",
tf, geteuid());
}
}
if (tTd(40, 1))
- printf("\n>>>>> queueing %s%s >>>>>\n", e->e_id,
+ dprintf("\n>>>>> queueing %s/qf%s%s >>>>>\n",
+ qid_printqueue(e->e_queuedir), e->e_id,
newid ? " (new id)" : "");
if (tTd(40, 3))
{
- extern void printenvflags __P((ENVELOPE *));
-
- printf(" e_flags=");
+ dprintf(" e_flags=");
printenvflags(e);
}
if (tTd(40, 32))
{
- printf(" sendq=");
+ dprintf(" sendq=");
printaddr(e->e_sendqueue, TRUE);
}
if (tTd(40, 9))
{
- printf(" tfp=");
+ dprintf(" tfp=");
dumpfd(fileno(tfp), TRUE, FALSE);
- printf(" lockfp=");
+ dprintf(" lockfp=");
if (e->e_lockfp == NULL)
- printf("NULL\n");
+ dprintf("NULL\n");
else
dumpfd(fileno(e->e_lockfp), TRUE, FALSE);
}
@@ -166,18 +212,41 @@ queueup(e, announce)
** If there is no data file yet, create one.
*/
- if (!bitset(EF_HAS_DF, e->e_flags))
+ if (bitset(EF_HAS_DF, e->e_flags))
{
+ if (e->e_dfp != NULL && bfcommit(e->e_dfp) < 0)
+ syserr("!queueup: cannot commit data file %s, uid=%d",
+ queuename(e, 'd'), geteuid());
+ }
+ else
+ {
+ int dfd;
register FILE *dfp = NULL;
- char dfname[MAXQFNAME];
+ char dfname[MAXPATHLEN];
struct stat stbuf;
- strcpy(dfname, queuename(e, 'd'));
- fd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode);
- if (fd < 0 || (dfp = fdopen(fd, "w")) == NULL)
+ if (e->e_dfp != NULL && bftest(e->e_dfp))
+ 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)
syserr("!queueup: cannot create data temp file %s, uid=%d",
dfname, geteuid());
- if (fstat(fd, &stbuf) < 0)
+ if (fstat(dfd, &stbuf) < 0)
e->e_dfino = -1;
else
{
@@ -185,11 +254,13 @@ queueup(e, announce)
e->e_dfino = stbuf.st_ino;
}
e->e_flags |= EF_HAS_DF;
- bzero(&mcibuf, sizeof mcibuf);
+ memset(&mcibuf, '\0', sizeof mcibuf);
mcibuf.mci_out = dfp;
mcibuf.mci_mailer = FileMailer;
(*e->e_putbody)(&mcibuf, e, NULL);
- (void) xfclose(dfp, "queueup dfp", e->e_id);
+ if (fclose(dfp) < 0)
+ syserr("!queueup: cannot save data temp file %s, uid=%d",
+ dfname, geteuid());
e->e_putbody = putbody;
}
@@ -206,7 +277,17 @@ queueup(e, announce)
fprintf(tfp, "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 (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 */
/* output number of delivery attempts */
fprintf(tfp, "N%d\n", e->e_ntries);
@@ -218,13 +299,16 @@ queueup(e, announce)
/* XXX should probably include device major/minor too */
if (e->e_dfino != -1)
{
+ /*CONSTCOND*/
if (sizeof e->e_dfino > sizeof(long))
- fprintf(tfp, "I%d/%d/%s\n",
- major(e->e_dfdev), minor(e->e_dfdev),
+ 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%d/%d/%lu\n",
- major(e->e_dfdev), minor(e->e_dfdev),
+ fprintf(tfp, "I%ld/%ld/%lu\n",
+ (long) major(e->e_dfdev),
+ (long) minor(e->e_dfdev),
(unsigned long) e->e_dfino);
}
@@ -232,10 +316,10 @@ queueup(e, announce)
if (e->e_bodytype != NULL)
fprintf(tfp, "B%s\n", denlstring(e->e_bodytype, TRUE, FALSE));
-#if _FFR_SAVE_CHARSET
+# if _FFR_SAVE_CHARSET
if (e->e_charset != NULL)
fprintf(tfp, "X%s\n", denlstring(e->e_charset, TRUE, FALSE));
-#endif
+# endif /* _FFR_SAVE_CHARSET */
/* message from envelope, if it exists */
if (e->e_message != NULL)
@@ -259,13 +343,8 @@ queueup(e, announce)
if (buf[0] != '\0')
fprintf(tfp, "F%s\n", buf);
- /* $r and $s and $_ macro values */
- if ((p = macvalue('r', e)) != NULL)
- fprintf(tfp, "$r%s\n", denlstring(p, TRUE, FALSE));
- if ((p = macvalue('s', e)) != NULL)
- fprintf(tfp, "$s%s\n", denlstring(p, TRUE, FALSE));
- if ((p = macvalue('_', e)) != NULL)
- fprintf(tfp, "$_%s\n", denlstring(p, TRUE, FALSE));
+ /* save $={persistentMacros} macro values */
+ queueup_macros(macid("{persistentMacros}", NULL), tfp, e);
/* output name of sender */
if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
@@ -278,49 +357,47 @@ queueup(e, announce)
if (e->e_envid != NULL)
fprintf(tfp, "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));
+
/* output list of recipient addresses */
printctladdr(NULL, NULL);
for (q = e->e_sendqueue; q != NULL; q = q->q_next)
{
- if (bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags))
- {
-#if XDEBUG
- if (bitset(QQUEUEUP, q->q_flags))
- sm_syslog(LOG_DEBUG, e->e_id,
- "dropenvelope: q_flags = %x, paddr = %s",
- q->q_flags, q->q_paddr);
-#endif
+ if (!QS_IS_UNDELIVERED(q->q_state))
continue;
- }
+
printctladdr(q, tfp);
if (q->q_orcpt != NULL)
fprintf(tfp, "Q%s\n",
denlstring(q->q_orcpt, TRUE, FALSE));
- putc('R', tfp);
+ (void) putc('R', tfp);
if (bitset(QPRIMARY, q->q_flags))
- putc('P', tfp);
+ (void) putc('P', tfp);
if (bitset(QHASNOTIFY, q->q_flags))
- putc('N', tfp);
+ (void) putc('N', tfp);
if (bitset(QPINGONSUCCESS, q->q_flags))
- putc('S', tfp);
+ (void) putc('S', tfp);
if (bitset(QPINGONFAILURE, q->q_flags))
- putc('F', tfp);
+ (void) putc('F', tfp);
if (bitset(QPINGONDELAY, q->q_flags))
- putc('D', tfp);
- putc(':', tfp);
- fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE));
+ (void) putc('D', tfp);
+ (void) putc(':', tfp);
+ (void) fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE));
if (announce)
{
e->e_to = q->q_paddr;
message("queued");
if (LogLevel > 8)
- logdelivery(q->q_mailer, NULL, "queued",
- NULL, (time_t) 0, e);
+ logdelivery(q->q_mailer, NULL, q->q_status,
+ "queued", NULL, (time_t) 0, e);
e->e_to = NULL;
}
if (tTd(40, 1))
{
- printf("queueing ");
+ dprintf("queueing ");
printaddr(q, FALSE);
}
}
@@ -335,24 +412,23 @@ queueup(e, announce)
** no effect on the addresses as they are output.
*/
- bzero((char *) &nullmailer, sizeof nullmailer);
+ memset((char *) &nullmailer, '\0', sizeof nullmailer);
nullmailer.m_re_rwset = nullmailer.m_rh_rwset =
nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1;
nullmailer.m_eol = "\n";
- bzero(&mcibuf, sizeof mcibuf);
+ memset(&mcibuf, '\0', sizeof mcibuf);
mcibuf.mci_mailer = &nullmailer;
mcibuf.mci_out = tfp;
define('g', "\201f", e);
for (h = e->e_header; h != NULL; h = h->h_link)
{
- extern bool bitzerop __P((BITMAP));
-
if (h->h_value == NULL)
continue;
/* don't output resent headers on non-resent messages */
- if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
+ if (bitset(H_RESENT, h->h_flags) &&
+ !bitset(EF_RESENT, e->e_flags))
continue;
/* expand macros; if null, don't output header at all */
@@ -364,28 +440,39 @@ queueup(e, announce)
}
/* output this header */
- fprintf(tfp, "H");
+ fprintf(tfp, "H?");
- /* if conditional, output the set of conditions */
- if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags))
+ /* output conditional macro if present */
+ if (h->h_macro != '\0')
+ {
+ if (bitset(0200, h->h_macro))
+ fprintf(tfp, "${%s}",
+ macname(h->h_macro & 0377));
+ else
+ fprintf(tfp, "$%c", h->h_macro);
+ }
+ else if (!bitzerop(h->h_mflags) &&
+ bitset(H_CHECK|H_ACHECK, h->h_flags))
{
int j;
- (void) putc('?', tfp);
+ /* if conditional, output the set of conditions */
for (j = '\0'; j <= '\177'; j++)
if (bitnset(j, h->h_mflags))
(void) putc(j, tfp);
- (void) putc('?', tfp);
}
+ (void) putc('?', tfp);
/* output the header: expand macros, convert addresses */
- if (bitset(H_DEFAULT, h->h_flags))
+ 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));
}
- else if (bitset(H_FROM|H_RCPT, h->h_flags))
+ 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;
@@ -434,9 +521,20 @@ queueup(e, announce)
syserr("cannot rename(%s, %s), uid=%d",
tf, qf, geteuid());
+ /*
+ ** fsync() after renaming to make sure
+ ** metadata is written to disk on
+ ** filesystems in which renames are
+ ** not guaranteed such as softupdates.
+ */
+
+ if (tfd >= 0 && SuperSafe && fsync(tfd) < 0)
+ syserr("!queueup: cannot fsync queue temp file %s",
+ tf);
+
/* close and unlock old (locked) qf */
if (e->e_lockfp != NULL)
- (void) xfclose(e->e_lockfp, "queueup lockfp", e->e_id);
+ (void) fclose(e->e_lockfp);
e->e_lockfp = tfp;
}
else
@@ -449,16 +547,16 @@ queueup(e, announce)
sm_syslog(LOG_DEBUG, e->e_id, "queueup, qf=%s", qf);
if (tTd(40, 1))
- printf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
+ dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
return;
}
-void
+static void
printctladdr(a, tfp)
register ADDRESS *a;
FILE *tfp;
{
- char *uname;
+ char *user;
register ADDRESS *q;
uid_t uid;
gid_t gid;
@@ -479,13 +577,13 @@ printctladdr(a, tfp)
q = getctladdr(a);
if (q == NULL)
{
- uname = NULL;
+ user = NULL;
uid = 0;
gid = 0;
}
else
{
- uname = q->q_ruser != NULL ? q->q_ruser : q->q_user;
+ user = q->q_ruser != NULL ? q->q_ruser : q->q_user;
uid = q->q_uid;
gid = q->q_gid;
}
@@ -498,11 +596,11 @@ printctladdr(a, tfp)
lastuid = uid;
lastctladdr = a;
- if (uid == 0 || uname == NULL || uname[0] == '\0')
+ if (uid == 0 || user == NULL || user[0] == '\0')
fprintf(tfp, "C");
else
fprintf(tfp, "C%s:%ld:%ld",
- denlstring(uname, TRUE, FALSE), (long) uid, (long) gid);
+ denlstring(user, TRUE, FALSE), (long) uid, (long) gid);
fprintf(tfp, ":%s\n", denlstring(a->q_paddr, TRUE, FALSE));
}
/*
@@ -515,6 +613,7 @@ printctladdr(a, tfp)
** 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.
**
** Returns:
@@ -524,21 +623,95 @@ printctladdr(a, tfp)
** runs things in the mail queue.
*/
-ENVELOPE QueueEnvelope; /* the queue run envelope */
-extern int get_num_procs_online __P((void));
+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)
bool forkflag;
bool verbose;
{
+ int i;
+ bool ret = TRUE;
+ static int curnum = 0;
+
+ if (!forkflag && NumQueues > 1 && !verbose)
+ forkflag = TRUE;
+
+ for (i = 0; i < NumQueues; i++)
+ {
+ /*
+ ** Pick up where we left off, in case we
+ ** used up all the children last time
+ ** without finishing.
+ */
+
+ ret = run_single_queue(curnum, forkflag, verbose);
+
+ /*
+ ** Failure means a message was printed for ETRN
+ ** and subsequent queues are likely to fail as well.
+ */
+
+ if (!ret)
+ break;
+
+ if (++curnum >= NumQueues)
+ curnum = 0;
+ }
+ if (QueueIntvl != 0)
+ (void) setevent(QueueIntvl, runqueueevent, 0);
+ return ret;
+}
+ /*
+** RUN_SINGLE_QUEUE -- run the jobs in a single queue.
+**
+** 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
+** 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.
+**
+** Returns:
+** 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;
+ bool forkflag;
+ bool verbose;
+{
register ENVELOPE *e;
int njobs;
int sequenceno = 0;
time_t current_la_time;
extern ENVELOPE BlankEnvelope;
- extern void clrdaemon __P((void));
- extern void runqueueevent __P((void));
DoQueueRun = FALSE;
@@ -547,7 +720,7 @@ runqueue(forkflag, verbose)
** the queue.
*/
- CurrentLA = getla(); /* get load average */
+ CurrentLA = sm_getla(NULL); /* get load average */
current_la_time = curtime();
if (shouldqueue(WkRecipFact, current_la_time))
@@ -558,10 +731,8 @@ runqueue(forkflag, verbose)
message("458 %s\n", msg);
if (LogLevel > 8)
sm_syslog(LOG_INFO, NOQID,
- "runqueue: %s",
- msg);
- if (forkflag && QueueIntvl != 0)
- (void) setevent(QueueIntvl, runqueueevent, 0);
+ "runqueue: %s",
+ msg);
return FALSE;
}
@@ -572,7 +743,14 @@ runqueue(forkflag, verbose)
if (forkflag && QueueIntvl != 0 &&
MaxChildren > 0 && CurChildren >= MaxChildren)
{
- (void) setevent(QueueIntvl, runqueueevent, 0);
+ char *msg = "Skipping queue run -- too many children";
+
+ if (verbose)
+ message("458 %s (%d)\n", msg, CurChildren);
+ if (LogLevel > 8)
+ sm_syslog(LOG_INFO, NOQID,
+ "runqueue: %s (%d)",
+ msg, CurChildren);
return FALSE;
}
@@ -583,10 +761,8 @@ runqueue(forkflag, verbose)
if (forkflag)
{
pid_t pid;
- extern SIGFUNC_DECL intsig __P((int));
- extern SIGFUNC_DECL reapchild __P((int));
- blocksignal(SIGCHLD);
+ (void) blocksignal(SIGCHLD);
(void) setsignal(SIGCHLD, reapchild);
pid = dofork();
@@ -599,10 +775,8 @@ runqueue(forkflag, verbose)
message("458 %s: %s\n", msg, err);
if (LogLevel > 8)
sm_syslog(LOG_INFO, NOQID,
- "runqueue: %s: %s",
- msg, err);
- if (QueueIntvl != 0)
- (void) setevent(QueueIntvl, runqueueevent, 0);
+ "runqueue: %s: %s",
+ msg, err);
(void) releasesignal(SIGCHLD);
return FALSE;
}
@@ -610,30 +784,31 @@ runqueue(forkflag, verbose)
{
/* parent -- pick up intermediate zombie */
(void) blocksignal(SIGALRM);
- proc_list_add(pid, "Queue runner");
+ proc_list_add(pid, "Queue runner", PROC_QUEUE);
(void) releasesignal(SIGALRM);
- releasesignal(SIGCHLD);
- if (QueueIntvl != 0)
- (void) setevent(QueueIntvl, runqueueevent, 0);
+ (void) releasesignal(SIGCHLD);
return TRUE;
}
- /* child -- double fork and clean up signals */
+ /* child -- clean up signals */
clrcontrol();
proc_list_clear();
/* Add parent process as first child item */
- proc_list_add(getpid(), "Queue runner child process");
- releasesignal(SIGCHLD);
+ proc_list_add(getpid(), "Queue runner child process",
+ PROC_QUEUE_CHILD);
+ (void) releasesignal(SIGCHLD);
(void) setsignal(SIGCHLD, SIG_DFL);
(void) setsignal(SIGHUP, intsig);
+
}
- sm_setproctitle(TRUE, "running queue: %s", QueueDir);
+ sm_setproctitle(TRUE, CurEnv, "running queue: %s",
+ qid_printqueue(queuedir));
- if (LogLevel > 69)
+ if (LogLevel > 69 || tTd(63, 99))
sm_syslog(LOG_DEBUG, NOQID,
- "runqueue %s, pid=%d, forkflag=%d",
- QueueDir, getpid(), forkflag);
+ "runqueue %s, pid=%d, forkflag=%d",
+ qid_printqueue(queuedir), getpid(), forkflag);
/*
** Release any resources used by the daemon code.
@@ -666,12 +841,6 @@ runqueue(forkflag, verbose)
}
/*
- ** Make sure the alias database is open.
- */
-
- initmaps(FALSE, e);
-
- /*
** If we are running part of the queue, always ignore stored
** host status.
*/
@@ -691,7 +860,8 @@ runqueue(forkflag, verbose)
*/
/* order the existing work requests */
- njobs = orderq(FALSE);
+ njobs = orderq(queuedir, FALSE);
+
/* process them once at a time */
while (WorkQ != NULL)
@@ -709,7 +879,7 @@ runqueue(forkflag, verbose)
if (current_la_time < curtime() - 30)
{
- CurrentLA = getla();
+ CurrentLA = sm_getla(e);
current_la_time = curtime();
}
if (shouldqueue(WkRecipFact, current_la_time))
@@ -720,8 +890,8 @@ runqueue(forkflag, verbose)
message("%s", msg);
if (LogLevel > 8)
sm_syslog(LOG_INFO, NOQID,
- "runqueue: %s",
- msg);
+ "runqueue: %s",
+ msg);
break;
}
sequenceno++;
@@ -729,26 +899,30 @@ runqueue(forkflag, verbose)
{
if (Verbose)
message("");
- if (QueueSortOrder == QS_BYPRIORITY)
+ if (QueueSortOrder == QSO_BYPRIORITY)
{
if (Verbose)
- message("Skipping %s (sequence %d of %d) and flushing rest of queue",
+ 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 (pri %ld, LA %d, %d of %d)",
- w->w_name + 2,
- w->w_pri,
- CurrentLA,
- sequenceno,
- njobs);
+ "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;
}
else if (Verbose)
- message("Skipping %s (sequence %d of %d)",
- w->w_name + 2, sequenceno, njobs);
+ message("Skipping %s/%s (sequence %d of %d)",
+ qid_printqueue(queuedir),
+ w->w_name + 2,
+ sequenceno, njobs);
}
else
{
@@ -757,10 +931,19 @@ runqueue(forkflag, verbose)
if (Verbose)
{
message("");
- message("Running %s (sequence %d of %d)",
- w->w_name + 2, sequenceno, njobs);
+ message("Running %s/%s (sequence %d of %d)",
+ qid_printqueue(queuedir),
+ w->w_name + 2,
+ sequenceno, njobs);
}
- pid = dowork(w->w_name + 2, ForkQueueRuns, FALSE, e);
+ if (tTd(63, 100))
+ sm_syslog(LOG_DEBUG, NOQID,
+ "runqueue %s dowork(%s)",
+ qid_printqueue(queuedir),
+ w->w_name + 2);
+
+ pid = dowork(queuedir, w->w_name + 2,
+ ForkQueueRuns, FALSE, e);
errno = 0;
if (pid != 0)
(void) waitfor(pid);
@@ -773,17 +956,17 @@ runqueue(forkflag, verbose)
/* exit without the usual cleanup */
e->e_id = NULL;
- finis(TRUE, ExitStat);
- /*NOTREACHED*/
+ if (forkflag)
+ finis(TRUE, ExitStat);
+ /* NOTREACHED */
return TRUE;
}
-
/*
** RUNQUEUEEVENT -- stub for use in setevent
*/
-void
+static void
runqueueevent()
{
DoQueueRun = TRUE;
@@ -792,6 +975,7 @@ runqueueevent()
** ORDERQ -- order the work queue.
**
** Parameters:
+** queuedir -- 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
@@ -813,8 +997,9 @@ runqueueevent()
static WORK *WorkList = NULL;
static int WorkListSize = 0;
-int
-orderq(doall)
+static int
+orderq(queuedir, doall)
+ int queuedir;
bool doall;
{
register struct dirent *d;
@@ -825,32 +1010,41 @@ orderq(doall)
int wn = -1;
int wc;
QUEUE_CHAR *check;
-
+ char qd[MAXPATHLEN];
+ char qf[MAXPATHLEN];
+
+ if (queuedir == NOQDIR)
+ (void) strlcpy(qd, ".", sizeof qd);
+ else
+ (void) snprintf(qd, sizeof qd, "%s%s",
+ QPaths[queuedir].qp_name,
+ (bitset(QP_SUBQF, QPaths[queuedir].qp_subdirs) ? "/qf" : ""));
+
if (tTd(41, 1))
{
- printf("orderq:\n");
+ dprintf("orderq:\n");
check = QueueLimitId;
while (check != NULL)
{
- printf("\tQueueLimitId = %s\n",
- check->queue_match);
+ dprintf("\tQueueLimitId = %s\n",
+ check->queue_match);
check = check->queue_next;
}
check = QueueLimitSender;
while (check != NULL)
{
- printf("\tQueueLimitSender = %s\n",
- check->queue_match);
+ dprintf("\tQueueLimitSender = %s\n",
+ check->queue_match);
check = check->queue_next;
}
check = QueueLimitRecipient;
while (check != NULL)
{
- printf("\tQueueLimitRecipient = %s\n",
- check->queue_match);
+ dprintf("\tQueueLimitRecipient = %s\n",
+ check->queue_match);
check = check->queue_next;
}
}
@@ -869,11 +1063,11 @@ orderq(doall)
}
/* open the queue directory */
- f = opendir(".");
+ f = opendir(qd);
if (f == NULL)
{
- syserr("orderq: cannot open \"%s\" as \".\"", QueueDir);
- return (0);
+ syserr("orderq: cannot open \"%s\"", qid_printqueue(queuedir));
+ return 0;
}
/*
@@ -885,24 +1079,24 @@ orderq(doall)
FILE *cf;
int qfver = 0;
char lbuf[MAXNAME + 1];
- extern bool strcontainedin __P((char *, char *));
+ struct stat sbuf;
if (tTd(41, 50))
- printf("orderq: checking %s\n", d->d_name);
+ dprintf("orderq: checking %s\n", d->d_name);
/* is this an interesting entry? */
if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
continue;
- if (strlen(d->d_name) > MAXQFNAME)
+ if (strlen(d->d_name) >= MAXQFNAME)
{
if (Verbose)
printf("orderq: %s too long, %d max characters\n",
d->d_name, MAXQFNAME);
if (LogLevel > 0)
sm_syslog(LOG_ALERT, NOQID,
- "orderq: %s too long, %d max characters",
- d->d_name, MAXQFNAME);
+ "orderq: %s too long, %d max characters",
+ d->d_name, MAXQFNAME);
continue;
}
@@ -917,69 +1111,67 @@ orderq(doall)
if (QueueLimitId != NULL && check == NULL)
continue;
-#ifdef PICKY_QF_NAME_CHECK
- /*
- ** Check queue name for plausibility. This handles
- ** both old and new type ids.
- */
-
- p = d->d_name + 2;
- if (isupper(p[0]) && isupper(p[2]))
- p += 3;
- else if (isupper(p[1]))
- p += 2;
- else
- p = d->d_name;
- for (i = 0; isdigit(*p); p++)
- i++;
- if (i < 5 || *p != '\0')
- {
- if (Verbose)
- printf("orderq: bogus qf name %s\n", d->d_name);
- if (LogLevel > 0)
- sm_syslog(LOG_ALERT, NOQID,
- "orderq: bogus qf name %s",
- d->d_name);
- if (strlen(d->d_name) > (SIZE_T) MAXNAME)
- d->d_name[MAXNAME] = '\0';
- strcpy(lbuf, d->d_name);
- lbuf[0] = 'Q';
- (void) rename(d->d_name, lbuf);
- continue;
- }
-#endif
-
- /* open control file (if not too many files) */
+ /* grow work list if necessary */
if (++wn >= MaxQueueRun && MaxQueueRun > 0)
{
if (wn == MaxQueueRun && LogLevel > 0)
- sm_syslog(LOG_ALERT, NOQID,
- "WorkList for %s maxed out at %d",
- QueueDir, MaxQueueRun);
+ sm_syslog(LOG_WARNING, NOQID,
+ "WorkList for %s maxed out at %d",
+ qid_printqueue(queuedir),
+ MaxQueueRun);
continue;
}
if (wn >= WorkListSize)
{
- extern void grow_wlist __P((void));
-
- grow_wlist();
+ grow_wlist(queuedir);
if (wn >= WorkListSize)
continue;
}
+ w = &WorkList[wn];
- cf = fopen(d->d_name, "r");
+ (void) snprintf(qf, sizeof qf, "%s/%s", 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);
+ 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);
+ wn--;
+ continue;
+ }
+
+ /* avoid work if possible */
+ if (QueueSortOrder == QSO_BYFILENAME)
+ {
+ w->w_name = newstr(d->d_name);
+ w->w_host = NULL;
+ w->w_lock = w->w_tooyoung = FALSE;
+ w->w_pri = 0;
+ w->w_ctime = 0;
+ continue;
+ }
+
+ /* open control file */
+ cf = fopen(qf, "r");
if (cf == NULL)
{
/* this may be some random person sending hir msgs */
/* syserr("orderq: cannot open %s", cbuf); */
if (tTd(41, 2))
- printf("orderq: cannot open %s: %s\n",
+ dprintf("orderq: cannot open %s: %s\n",
d->d_name, errstring(errno));
errno = 0;
wn--;
continue;
}
- w = &WorkList[wn];
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);
@@ -993,13 +1185,12 @@ orderq(doall)
i = NEED_P | NEED_T;
if (QueueLimitSender != NULL)
i |= NEED_S;
- if (QueueSortOrder == QS_BYHOST || QueueLimitRecipient != NULL)
+ if (QueueLimitRecipient != NULL)
i |= NEED_R;
while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL)
{
int c;
time_t age;
- extern bool strcontainedin __P((char *, char *));
p = strchr(lbuf, '\n');
if (p != NULL)
@@ -1030,7 +1221,10 @@ orderq(doall)
case 'R':
if (w->w_host == NULL &&
(p = strrchr(&lbuf[1], '@')) != NULL)
- w->w_host = newstr(&p[1]);
+ {
+ w->w_host = strrev(&p[1]);
+ makelower(w->w_host);
+ }
if (QueueLimitRecipient == NULL)
{
i &= ~NEED_R;
@@ -1058,17 +1252,17 @@ orderq(doall)
break;
case 'S':
- check = QueueLimitSender;
- while (check != NULL)
- {
- if (strcontainedin(check->queue_match,
- &lbuf[1]))
- break;
- else
- check = check->queue_next;
- }
- if (check != NULL)
- i &= ~NEED_S;
+ check = QueueLimitSender;
+ while (check != NULL)
+ {
+ if (strcontainedin(check->queue_match,
+ &lbuf[1]))
+ break;
+ else
+ check = check->queue_next;
+ }
+ if (check != NULL)
+ i &= ~NEED_S;
break;
case 'K':
@@ -1082,6 +1276,17 @@ orderq(doall)
if (atol(&lbuf[1]) == 0)
w->w_tooyoung = FALSE;
break;
+
+# if _FFR_QUEUEDELAY
+/*
+ case 'G':
+ queuealg = atoi(lbuf[1]);
+ break;
+ case 'Y':
+ queuedelay = (time_t) atol(&lbuf[1]);
+ break;
+*/
+# endif /* _FFR_QUEUEDELAY */
}
}
(void) fclose(cf);
@@ -1091,7 +1296,7 @@ orderq(doall)
{
/* don't even bother sorting this job in */
if (tTd(41, 49))
- printf("skipping %s (%x)\n", w->w_name, i);
+ dprintf("skipping %s (%x)\n", w->w_name, i);
free(w->w_name);
if (w->w_host)
free(w->w_host);
@@ -1101,15 +1306,15 @@ orderq(doall)
(void) closedir(f);
wn++;
+ WorkQ = NULL;
+ if (WorkList == NULL)
+ return 0;
wc = min(wn, WorkListSize);
if (wc > MaxQueueRun && MaxQueueRun > 0)
wc = MaxQueueRun;
- if (QueueSortOrder == QS_BYHOST)
+ if (QueueSortOrder == QSO_BYHOST)
{
- extern int workcmpf1();
- extern int workcmpf2();
-
/*
** Sort the work directory for the first time,
** based on host name, lock status, and priority.
@@ -1133,8 +1338,6 @@ orderq(doall)
w = &WorkList[i];
while (++i < wc)
{
- extern int sm_strcasecmp __P((char *, char *));
-
if (WorkList[i].w_host == NULL &&
w->w_host == NULL)
WorkList[i].w_lock = TRUE;
@@ -1154,20 +1357,24 @@ orderq(doall)
qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2);
}
- else if (QueueSortOrder == QS_BYTIME)
+ else if (QueueSortOrder == QSO_BYTIME)
{
- extern int workcmpf3();
-
/*
** Simple sort based on submission time only.
*/
qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3);
}
- else
+ else if (QueueSortOrder == QSO_BYFILENAME)
{
- extern int workcmpf0();
+ /*
+ ** Sort based on qf filename.
+ */
+ qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf4);
+ }
+ else
+ {
/*
** Simple sort based on queue priority only.
*/
@@ -1180,7 +1387,6 @@ orderq(doall)
** Should be turning it into a list of envelopes here perhaps.
*/
- WorkQ = NULL;
for (i = wc; --i >= 0; )
{
w = (WORK *) xalloc(sizeof *w);
@@ -1201,16 +1407,23 @@ orderq(doall)
if (tTd(40, 1))
{
for (w = WorkQ; w != NULL; w = w->w_next)
- printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
+ {
+ if (w->w_host != NULL)
+ dprintf("%22s: pri=%ld %s\n",
+ w->w_name, w->w_pri, w->w_host);
+ else
+ dprintf("%32s: pri=%ld\n",
+ w->w_name, w->w_pri);
+ }
}
- return (wn);
+ return wn;
}
/*
** GROW_WLIST -- make the work list larger
**
** Parameters:
-** none.
+** queuedir -- the index for the queue directory.
**
** Returns:
** none.
@@ -1221,14 +1434,16 @@ orderq(doall)
** should be checked again upon return.
*/
-void
-grow_wlist()
+static void
+grow_wlist(queuedir)
+ int queuedir;
{
if (tTd(41, 1))
- printf("grow_wlist: WorkListSize=%d\n", WorkListSize);
+ dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize);
if (WorkList == NULL)
{
- WorkList = (WORK *) xalloc(sizeof(WORK) * (QUEUESEGSIZE + 1));
+ WorkList = (WORK *) xalloc((sizeof *WorkList) *
+ (QUEUESEGSIZE + 1));
WorkListSize = QUEUESEGSIZE;
}
else
@@ -1243,20 +1458,21 @@ grow_wlist()
WorkList = newlist;
if (LogLevel > 1)
{
- sm_syslog(LOG_NOTICE, NOQID,
- "grew WorkList for %s to %d",
- QueueDir, WorkListSize);
+ sm_syslog(LOG_INFO, NOQID,
+ "grew WorkList for %s to %d",
+ qid_printqueue(queuedir),
+ WorkListSize);
}
}
else if (LogLevel > 0)
{
sm_syslog(LOG_ALERT, NOQID,
- "FAILED to grow WorkList for %s to %d",
- QueueDir, newsize);
+ "FAILED to grow WorkList for %s to %d",
+ qid_printqueue(queuedir), newsize);
}
}
if (tTd(41, 1))
- printf("grow_wlist: WorkListSize now %d\n", WorkListSize);
+ dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize);
}
/*
** WORKCMPF0 -- simple priority-only compare function.
@@ -1274,7 +1490,7 @@ grow_wlist()
** none.
*/
-int
+static int
workcmpf0(a, b)
register WORK *a;
register WORK *b;
@@ -1307,13 +1523,12 @@ workcmpf0(a, b)
** none.
*/
-int
+static int
workcmpf1(a, b)
register WORK *a;
register WORK *b;
{
int i;
- extern int sm_strcasecmp __P((char *, char *));
/* host name */
if (a->w_host != NULL && b->w_host == NULL)
@@ -1349,13 +1564,12 @@ workcmpf1(a, b)
** none.
*/
-int
+static int
workcmpf2(a, b)
register WORK *a;
register WORK *b;
{
int i;
- extern int sm_strcasecmp __P((char *, char *));
/* lock status */
if (a->w_lock != b->w_lock)
@@ -1389,7 +1603,7 @@ workcmpf2(a, b)
** none.
*/
-int
+static int
workcmpf3(a, b)
register WORK *a;
register WORK *b;
@@ -1402,9 +1616,61 @@ workcmpf3(a, b)
return 0;
}
/*
+** WORKCMPF4 -- compare based on file name
+**
+** Parameters:
+** a -- the first argument.
+** b -- the second argument.
+**
+** Returns:
+** -1 if a < b
+** 0 if a == b
+** +1 if a > b
+**
+** Side Effects:
+** none.
+*/
+
+static int
+workcmpf4(a, b)
+ register WORK *a;
+ register WORK *b;
+{
+ return strcmp(a->w_name, b->w_name);
+}
+ /*
+** STRREV -- reverse string
+**
+** Returns a pointer to a new string that is the reverse of
+** the string pointed to by fwd. The space for the new
+** string is obtained using xalloc().
+**
+** Parameters:
+** fwd -- the string to reverse.
+**
+** Returns:
+** the reversed string.
+*/
+
+static char *
+strrev(fwd)
+ char *fwd;
+{
+ char *rev = NULL;
+ int len, cnt;
+
+ len = strlen(fwd);
+ rev = xalloc(len + 1);
+ for (cnt = 0; cnt < len; ++cnt)
+ rev[cnt] = fwd[len - cnt - 1];
+ rev[len] = '\0';
+ return rev;
+}
+ /*
** DOWORK -- do a work request.
**
** Parameters:
+** queuedir -- 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.
@@ -1421,17 +1687,17 @@ workcmpf3(a, b)
*/
pid_t
-dowork(id, forkflag, requeueflag, e)
+dowork(queuedir, id, forkflag, requeueflag, e)
+ int queuedir;
char *id;
bool forkflag;
bool requeueflag;
register ENVELOPE *e;
{
register pid_t pid;
- extern bool readqf __P((ENVELOPE *));
if (tTd(40, 1))
- printf("dowork(%s)\n", id);
+ dprintf("dowork(%s/%s)\n", qid_printqueue(queuedir), id);
/*
** Fork for work.
@@ -1439,6 +1705,16 @@ dowork(id, forkflag, requeueflag, e)
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();
+
pid = fork();
if (pid < 0)
{
@@ -1454,16 +1730,6 @@ dowork(id, forkflag, requeueflag, e)
{
/* child -- error messages to the transcript */
QuickAbort = OnlyOneError = FALSE;
-
- /*
- ** 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, open a copy of the maps for
- ** the delivery process.
- */
-
- initmaps(FALSE, e);
}
}
else
@@ -1483,23 +1749,25 @@ dowork(id, forkflag, requeueflag, e)
/* set basic modes, etc. */
(void) alarm(0);
+ clearstats();
clearenvelope(e, FALSE);
e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
- e->e_sendmode = SM_DELIVER;
+ set_delivery_mode(SM_DELIVER, e);
e->e_errormode = EM_MAIL;
e->e_id = id;
+ e->e_queuedir = queuedir;
GrabTo = UseErrorsTo = FALSE;
ExitStat = EX_OK;
if (forkflag)
{
disconnect(1, e);
- OpMode = MD_DELIVER;
+ OpMode = MD_QUEUERUN;
}
- sm_setproctitle(TRUE, "%s: from queue", id);
+ sm_setproctitle(TRUE, e, "%s: from queue", qid_printname(e));
if (LogLevel > 76)
sm_syslog(LOG_DEBUG, e->e_id,
- "dowork, pid=%d",
- getpid());
+ "dowork, pid=%d",
+ getpid());
/* don't use the headers from sendmail.cf... */
e->e_header = NULL;
@@ -1508,7 +1776,8 @@ dowork(id, forkflag, requeueflag, e)
if (!readqf(e))
{
if (tTd(40, 4) && e->e_id != NULL)
- printf("readqf(%s) failed\n", e->e_id);
+ dprintf("readqf(%s) failed\n",
+ qid_printname(e));
e->e_id = NULL;
if (forkflag)
finis(FALSE, EX_OK);
@@ -1548,7 +1817,7 @@ dowork(id, forkflag, requeueflag, e)
** The queue file is returned locked.
*/
-bool
+static bool
readqf(e)
register ENVELOPE *e;
{
@@ -1561,22 +1830,26 @@ readqf(e)
register char *p;
char *orcpt = NULL;
bool nomore = FALSE;
- char qf[MAXQFNAME];
+ MODE_T qsafe;
+ char qf[MAXPATHLEN];
char buf[MAXLINE];
- extern ADDRESS *setctluser __P((char *, int));
/*
** Read and process the file.
*/
- strcpy(qf, queuename(e, 'q'));
+ (void) strlcpy(qf, queuename(e, 'q'), sizeof qf);
qfp = fopen(qf, "r+");
if (qfp == NULL)
{
+ int save_errno = errno;
+
if (tTd(40, 8))
- printf("readqf(%s): fopen failure (%s)\n",
+ dprintf("readqf(%s): fopen failure (%s)\n",
qf, errstring(errno));
- if (errno != ENOENT)
+ errno = save_errno;
+ if (errno != ENOENT
+ )
syserr("readqf: no control file %s", qf);
return FALSE;
}
@@ -1584,8 +1857,10 @@ readqf(e)
if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB))
{
/* being processed by another queuer */
- if (Verbose || tTd(40, 8))
+ if (Verbose)
printf("%s: locked\n", e->e_id);
+ if (tTd(40, 8))
+ dprintf("%s: locked\n", e->e_id);
if (LogLevel > 19)
sm_syslog(LOG_DEBUG, e->e_id, "locked");
(void) fclose(qfp);
@@ -1600,25 +1875,33 @@ readqf(e)
{
/* must have been being processed by someone else */
if (tTd(40, 8))
- printf("readqf(%s): fstat failure (%s)\n",
+ dprintf("readqf(%s): fstat failure (%s)\n",
qf, errstring(errno));
- fclose(qfp);
+ (void) fclose(qfp);
return FALSE;
}
- if ((st.st_uid != geteuid() && geteuid() != RealUid) ||
- bitset(S_IWOTH|S_IWGRP, st.st_mode))
+ 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))
{
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, mode=%o",
+ st.st_uid, st.st_mode);
}
if (tTd(40, 8))
- printf("readqf(%s): bogus file\n", qf);
+ dprintf("readqf(%s): bogus file\n", qf);
loseqfile(e, "bogus file uid in mqueue");
- fclose(qfp);
+ (void) fclose(qfp);
return FALSE;
}
@@ -1627,12 +1910,10 @@ readqf(e)
/* must be a bogus file -- if also old, just remove it */
if (st.st_ctime + 10 * 60 < curtime())
{
- qf[0] = 'd';
- (void) unlink(qf);
- qf[0] = 'q';
- (void) unlink(qf);
+ (void) xunlink(queuename(e, 'd'));
+ (void) xunlink(queuename(e, 'q'));
}
- fclose(qfp);
+ (void) fclose(qfp);
return FALSE;
}
@@ -1643,7 +1924,7 @@ readqf(e)
** unlinked. Just assume it is zero length.
*/
- fclose(qfp);
+ (void) fclose(qfp);
return FALSE;
}
@@ -1656,25 +1937,28 @@ readqf(e)
LineNumber = 0;
e->e_flags |= EF_GLOBALERRS;
- OpMode = MD_DELIVER;
+ OpMode = MD_QUEUERUN;
ctladdr = NULL;
e->e_dfino = -1;
e->e_msgsize = -1;
+# if _FFR_QUEUEDELAY
+ e->e_queuealg = QD_LINEAR;
+ e->e_queuedelay = (time_t) 0;
+# endif /* _FFR_QUEUEDELAY */
while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL)
{
- register char *p;
u_long qflags;
ADDRESS *q;
int mid;
auto char *ep;
if (tTd(40, 4))
- printf("+++++ %s\n", bp);
+ dprintf("+++++ %s\n", bp);
if (nomore)
{
/* hack attack */
syserr("SECURITY ALERT: extra data in qf: %s", bp);
- fclose(qfp);
+ (void) fclose(qfp);
loseqfile(e, "bogus queue line");
return FALSE;
}
@@ -1686,7 +1970,7 @@ readqf(e)
break;
syserr("Version number in qf (%d) greater than max (%d)",
qfver, QF_VERSION);
- fclose(qfp);
+ (void) fclose(qfp);
loseqfile(e, "unsupported qf file version");
return FALSE;
@@ -1750,7 +2034,7 @@ readqf(e)
break;
case 'H': /* header */
- (void) chompheader(&bp[1], FALSE, NULL, e);
+ (void) chompheader(&bp[1], 0, NULL, e);
hdrsize += strlen(&bp[1]);
break;
@@ -1767,11 +2051,11 @@ readqf(e)
e->e_bodytype = newstr(&bp[1]);
break;
-#if _FFR_SAVE_CHARSET
+# if _FFR_SAVE_CHARSET
case 'X': /* character set */
e->e_charset = newstr(&bp[1]);
break;
-#endif
+# endif /* _FFR_SAVE_CHARSET */
case 'D': /* data file name */
/* obsolete -- ignore */
@@ -1785,31 +2069,58 @@ readqf(e)
/* regenerated below */
break;
- case 'K': /* time of last deliver attempt */
+ case 'K': /* time of last delivery attempt */
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]);
+ break;
+# endif /* _FFR_QUEUEDELAY */
+
case 'N': /* number of delivery attempts */
e->e_ntries = atoi(&buf[1]);
/* if this has been tried recently, let it be */
- if (e->e_ntries > 0 &&
- MinQueueAge > 0 && e->e_dtime <= curtime() &&
- curtime() < e->e_dtime + MinQueueAge)
+ if (e->e_ntries > 0 && e->e_dtime <= curtime() &&
+ curtime() < e->e_dtime + queuedelay(e))
{
- char *howlong = pintvl(curtime() - e->e_dtime, TRUE);
+ char *howlong;
- if (Verbose || tTd(40, 8))
+ howlong = pintvl(curtime() - e->e_dtime, TRUE);
+ if (Verbose)
printf("%s: too young (%s)\n",
+ e->e_id, howlong);
+ if (tTd(40, 8))
+ dprintf("%s: too young (%s)\n",
e->e_id, howlong);
if (LogLevel > 19)
sm_syslog(LOG_DEBUG, e->e_id,
- "too young (%s)",
- howlong);
+ "too young (%s)",
+ howlong);
e->e_id = NULL;
unlockqueue(e);
return FALSE;
}
+ define(macid("{ntries}", NULL), newstr(&buf[1]), e);
+
+# if NAMED_BIND
+ /* adjust BIND parameters immediately */
+ if (e->e_ntries == 0)
+ {
+ _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
+ _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
+ }
+ else
+ {
+ _res.retry = TimeOuts.res_retry[RES_TO_NORMAL];
+ _res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL];
+ }
+# endif /* NAMED_BIND */
break;
case 'P': /* message priority */
@@ -1821,7 +2132,7 @@ readqf(e)
{
/* we are being spoofed! */
syserr("SECURITY ALERT: bogus qf line %s", bp);
- fclose(qfp);
+ (void) fclose(qfp);
loseqfile(e, "bogus queue line");
return FALSE;
}
@@ -1858,11 +2169,40 @@ readqf(e)
case 'Z': /* original envelope id from ESMTP */
e->e_envid = newstr(&bp[1]);
+ define(macid("{dsn_envid}", NULL), newstr(&bp[1]), e);
+ break;
+
+ case 'A': /* AUTH= parameter */
+ e->e_auth_param = newstr(&bp[1]);
break;
case '$': /* define macro */
- mid = macid(&bp[1], &ep);
- define(mid, newstr(ep), e);
+ {
+ char *p;
+
+ mid = macid(&bp[1], &ep);
+ p = newstr(ep);
+ define(mid, p, e);
+
+ /*
+ ** HACK ALERT: Unfortunately, 8.10 and
+ ** 8.11 reused the ${if_addr} and
+ ** ${if_family} macros for both the incoming
+ ** interface address/family (getrequests())
+ ** and the outgoing interface address/family
+ ** (makeconnection()). In order for D_BINDIF
+ ** to work properly, have to preserve the
+ ** incoming information in the queue file for
+ ** later delivery attempts. The original
+ ** information is stored in the envelope
+ ** in readqf() so it can be stored in
+ ** queueup_macros(). This should be fixed
+ ** in 8.12.
+ */
+
+ if (strcmp(macname(mid), "if_addr") == 0)
+ e->e_if_macros[EIF_ADDR] = p;
+ }
break;
case '.': /* terminate file */
@@ -1872,7 +2212,7 @@ readqf(e)
default:
syserr("readqf: %s: line %d: bad line \"%s\"",
qf, LineNumber, shortenstring(bp, MAXSHORTSTR));
- fclose(qfp);
+ (void) fclose(qfp);
loseqfile(e, "unrecognized line");
return FALSE;
}
@@ -1893,6 +2233,15 @@ readqf(e)
return TRUE;
}
+ /* possibly set ${dsn_ret} macro */
+ if (bitset(EF_RET_PARAM, e->e_flags))
+ {
+ if (bitset(EF_NO_BODY_RETN, e->e_flags))
+ define(macid("{dsn_ret}", NULL), "hdrs", e);
+ else
+ define(macid("{dsn_ret}", NULL), "full", e);
+ }
+
/*
** Arrange to read the data file.
*/
@@ -1917,6 +2266,48 @@ readqf(e)
return TRUE;
}
/*
+** PRTSTR -- print a string, "unprintable" characters are shown as \oct
+**
+** Parameters:
+** s -- string to print
+** ml -- maximum length of output
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Prints a string on stdout.
+*/
+
+static void
+prtstr(s, ml)
+ char *s;
+ int ml;
+{
+ char c;
+
+ if (s == NULL)
+ return;
+ while (ml-- > 0 && ((c = *s++) != '\0'))
+ {
+ if (c == '\\')
+ {
+ if (ml-- > 0)
+ {
+ putchar(c);
+ putchar(c);
+ }
+ }
+ else if (isascii(c) && isprint(c))
+ putchar(c);
+ else
+ {
+ if ((ml -= 3) > 0)
+ printf("\\%03o", c);
+ }
+ }
+}
+ /*
** PRINTQUEUE -- print out a representation of the mail queue
**
** Parameters:
@@ -1932,11 +2323,52 @@ readqf(e)
void
printqueue()
{
+ int i, nrequests = 0;
+
+ for (i = 0; i < NumQueues; i++)
+ nrequests += print_single_queue(i);
+ if (NumQueues > 1)
+ printf("\t\tTotal Requests: %d\n", nrequests);
+}
+ /*
+** PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue
+**
+** Parameters:
+** queuedir -- queue directory
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Prints a listing of the mail queue on the standard output.
+*/
+
+static int
+print_single_queue(queuedir)
+ int queuedir;
+{
register WORK *w;
FILE *f;
int nrequests;
+ char qd[MAXPATHLEN];
+ char qddf[MAXPATHLEN];
char buf[MAXLINE];
+ if (queuedir == NOQDIR)
+ {
+ (void) strlcpy(qd, ".", sizeof qd);
+ (void) 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" : ""));
+ }
+
/*
** Check for permission to print the queue
*/
@@ -1947,12 +2379,12 @@ printqueue()
# ifdef NGROUPS_MAX
int n;
extern GIDSET_T InitialGidSet[NGROUPS_MAX];
-# endif
+# endif /* NGROUPS_MAX */
- if (stat(QueueDir, &st) < 0)
+ if (stat(qd, &st) < 0)
{
- syserr("Cannot stat %s", QueueDir);
- return;
+ syserr("Cannot stat %s", qid_printqueue(queuedir));
+ return 0;
}
# ifdef NGROUPS_MAX
n = NGROUPS_MAX;
@@ -1962,13 +2394,13 @@ printqueue()
break;
}
if (n < 0 && RealGid != st.st_gid)
-# else
+# else /* NGROUPS_MAX */
if (RealGid != st.st_gid)
-# endif
+# endif /* NGROUPS_MAX */
{
usrerr("510 You are not permitted to see the queue");
setstat(EX_NOPERM);
- return;
+ return 0;
}
}
@@ -1976,7 +2408,7 @@ printqueue()
** Read and order the queue.
*/
- nrequests = orderq(TRUE);
+ nrequests = orderq(queuedir, TRUE);
/*
** Print the work list that we have read.
@@ -1985,19 +2417,20 @@ printqueue()
/* first see if there is anything */
if (nrequests <= 0)
{
- printf("Mail queue is empty\n");
- return;
+ printf("%s is empty\n", qid_printqueue(queuedir));
+ return 0;
}
- CurrentLA = getla(); /* get load average */
+ CurrentLA = sm_getla(NULL); /* get load average */
- printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s");
+ printf("\t\t%s (%d request%s", qid_printqueue(queuedir), nrequests,
+ nrequests == 1 ? "" : "s");
if (MaxQueueRun > 0 && nrequests > MaxQueueRun)
printf(", only %d printed", MaxQueueRun);
if (Verbose)
- printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n");
+ printf(")\n----Q-ID---- --Size-- -Priority- ---Q-Time--- ---------Sender/Recipient--------\n");
else
- printf(")\n--Q-ID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
+ printf(")\n----Q-ID---- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
for (w = WorkQ; w != NULL; w = w->w_next)
{
struct stat st;
@@ -2007,9 +2440,11 @@ printqueue()
int qfver;
char statmsg[MAXLINE];
char bodytype[MAXNAME + 1];
+ char qf[MAXPATHLEN];
- printf("%8s", w->w_name + 2);
- f = fopen(w->w_name, "r");
+ printf("%12s", w->w_name + 2);
+ (void) snprintf(qf, sizeof qf, "%s/%s", qd, w->w_name);
+ f = fopen(qf, "r");
if (f == NULL)
{
printf(" (job completed)\n");
@@ -2017,7 +2452,8 @@ printqueue()
continue;
}
w->w_name[0] = 'd';
- if (stat(w->w_name, &st) >= 0)
+ (void) snprintf(qf, sizeof qf, "%s/%s", qddf, w->w_name);
+ if (stat(qf, &st) >= 0)
dfsize = st.st_size;
else
dfsize = -1;
@@ -2048,42 +2484,47 @@ printqueue()
case 'M': /* error message */
if ((i = strlen(&buf[1])) >= sizeof statmsg)
i = sizeof statmsg - 1;
- bcopy(&buf[1], statmsg, i);
+ memmove(statmsg, &buf[1], i);
statmsg[i] = '\0';
break;
case 'B': /* body type */
if ((i = strlen(&buf[1])) >= sizeof bodytype)
i = sizeof bodytype - 1;
- bcopy(&buf[1], bodytype, i);
+ memmove(bodytype, &buf[1], i);
bodytype[i] = '\0';
break;
case 'S': /* sender name */
if (Verbose)
- printf("%8ld %10ld%c%.12s %.78s",
- dfsize,
- w->w_pri,
- bitset(EF_WARNING, flags) ? '+' : ' ',
- ctime(&submittime) + 4,
- &buf[1]);
+ {
+ printf("%8ld %10ld%c%.12s ",
+ dfsize,
+ w->w_pri,
+ bitset(EF_WARNING, flags) ? '+' : ' ',
+ ctime(&submittime) + 4);
+ prtstr(&buf[1], 78);
+ }
else
- printf("%8ld %.16s %.45s", dfsize,
- ctime(&submittime), &buf[1]);
+ {
+ printf("%8ld %.16s ", dfsize,
+ ctime(&submittime));
+ prtstr(&buf[1], 40);
+ }
if (statmsg[0] != '\0' || bodytype[0] != '\0')
{
printf("\n %10.10s", bodytype);
if (statmsg[0] != '\0')
printf(" (%.*s)",
- Verbose ? 100 : 60,
- statmsg);
+ Verbose ? 100 : 60,
+ statmsg);
}
break;
case 'C': /* controlling user */
if (Verbose)
printf("\n\t\t\t\t (---%.74s---)",
- &buf[1]);
+ &buf[1]);
break;
case 'R': /* recipient name */
@@ -2096,9 +2537,15 @@ printqueue()
p++;
}
if (Verbose)
- printf("\n\t\t\t\t\t %.78s", p);
+ {
+ printf("\n\t\t\t\t\t ");
+ prtstr(p, 73);
+ }
else
- printf("\n\t\t\t\t %.45s", p);
+ {
+ printf("\n\t\t\t\t ");
+ prtstr(p, 40);
+ }
break;
case 'T': /* creation time */
@@ -2122,148 +2569,173 @@ printqueue()
printf("\n");
(void) fclose(f);
}
+ return nrequests;
}
-
-# endif /* QUEUE */
/*
** QUEUENAME -- build a file name in the queue directory for this envelope.
**
-** Assigns an id code if one does not already exist.
-** This code is very careful to avoid trashing existing files
-** under any circumstances.
-**
** Parameters:
** e -- envelope to build it in/from.
** type -- the file type, used as the first character
** of the file name.
**
** Returns:
-** a pointer to the new file name (in a static buffer).
+** a pointer to the queue name (in a static buffer).
**
** Side Effects:
-** If no id code is already assigned, queuename will
-** assign an id code, create a qf file, and leave a
-** locked, open-for-write file pointer in the envelope.
+** If no id code is already assigned, queuename() will
+** assign an id code with assign_queueid(). If no queue
+** directory is assigned, one will be set with setnewqueue().
*/
-#ifndef ENOLCK
-# define ENOLCK -1
-#endif
-#ifndef ENOSPC
-# define ENOSPC -1
-#endif
-
char *
queuename(e, type)
register ENVELOPE *e;
int type;
{
- static pid_t pid = -1;
- static char c0;
- static char c1;
- static char c2;
- time_t now;
- struct tm *tm;
- static char buf[MAXNAME + 1];
+ char *sub = "";
+ static char buf[MAXPATHLEN];
+ /* Assign an ID if needed */
if (e->e_id == NULL)
- {
- char qf[MAXQFNAME];
+ assign_queueid(e);
+
+ /* Assign a queue directory if needed */
+ if (e->e_queuedir == NOQDIR)
+ setnewqueue(e);
- /* find a unique id */
- if (pid != getpid())
+ if (e->e_queuedir == NOQDIR)
+ (void) snprintf(buf, sizeof buf, "%cf%s",
+ type, e->e_id);
+ else
+ {
+ switch (type)
{
- /* new process -- start back at "AA" */
- pid = getpid();
- now = curtime();
- tm = localtime(&now);
- c0 = 'A' + tm->tm_hour;
- c1 = 'A';
- c2 = 'A' - 1;
+ case 'd':
+ if (bitset(QP_SUBDF, QPaths[e->e_queuedir].qp_subdirs))
+ sub = "/df";
+ break;
+
+ case 'T':
+ case 't':
+ case 'Q':
+ case 'q':
+ if (bitset(QP_SUBQF, QPaths[e->e_queuedir].qp_subdirs))
+ sub = "/qf";
+ break;
+
+ case 'x':
+ if (bitset(QP_SUBXF, QPaths[e->e_queuedir].qp_subdirs))
+ sub = "/xf";
+ break;
}
- (void) snprintf(qf, sizeof qf, "qf%cAA%05d", c0, pid);
- while (c1 < '~' || c2 < 'Z')
- {
- int i;
- int attempts = 0;
+ (void) snprintf(buf, sizeof buf, "%s%s/%cf%s",
+ QPaths[e->e_queuedir].qp_name,
+ sub, type, e->e_id);
+ }
- if (c2 >= 'Z')
- {
- c1++;
- c2 = 'A' - 1;
- }
- qf[3] = c1;
- qf[4] = ++c2;
- if (tTd(7, 20))
- printf("queuename: trying \"%s\"\n", qf);
+ if (tTd(7, 2))
+ 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.
+**
+** Parameters:
+** e -- envelope to set it in.
+**
+** Returns:
+** none.
+*/
- i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode);
- if (i < 0)
- {
- if (errno == EEXIST)
- continue;
- syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)",
- qf, QueueDir, geteuid());
- finis(FALSE, EX_UNAVAILABLE);
- }
- do
- {
- if (attempts > 0)
- sleep(attempts);
- e->e_lockfp = 0;
- if (lockfile(i, qf, NULL, LOCK_EX|LOCK_NB))
- {
- e->e_lockfp = fdopen(i, "w");
- break;
- }
- } while ((errno == ENOLCK || errno == ENOSPC) &&
- attempts++ < 4);
+static char Base60Code[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx";
- /* Successful lock */
- if (e->e_lockfp != 0)
- break;
+void
+assign_queueid(e)
+ register ENVELOPE *e;
+{
+ pid_t pid = getpid();
+ static char cX = 0;
+ static long random_offset;
+ struct tm *tm;
+ char idbuf[MAXQFNAME - 2];
-#if !HASFLOCK
- if (errno != EAGAIN && errno != EACCES)
-#else
- if (errno != EWOULDBLOCK)
-#endif
- {
- syserr("queuename: Cannot lock \"%s\" in \"%s\" (euid=%d)",
- qf, QueueDir, geteuid());
- finis(FALSE, EX_OSERR);
- }
+ if (e->e_id != NULL)
+ return;
- /* a reader got the file; abandon it and try again */
- (void) close(i);
- }
- if (c1 >= '~' && c2 >= 'Z')
- {
- syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)",
- qf, QueueDir, geteuid());
- finis(FALSE, EX_OSERR);
- }
- e->e_id = newstr(&qf[2]);
- define('i', e->e_id, e);
- if (tTd(7, 1))
- printf("queuename: assigned id %s, env=%lx\n",
- e->e_id, (u_long) e);
- if (tTd(7, 9))
+ /* see if we need to get a new base time/pid */
+ if (cX >= 60 || LastQueueTime == 0 || LastQueuePid != pid)
+ {
+ time_t then = LastQueueTime;
+
+ /* if the first time through, pick a random offset */
+ if (LastQueueTime == 0)
+ random_offset = get_random();
+
+ while ((LastQueueTime = curtime()) == then &&
+ LastQueuePid == pid)
{
- printf(" lockfd=");
- dumpfd(fileno(e->e_lockfp), TRUE, FALSE);
+ (void) sleep(1);
}
- if (LogLevel > 93)
- sm_syslog(LOG_DEBUG, e->e_id, "assigned id");
+ LastQueuePid = getpid();
+ cX = 0;
}
-
- if (type == '\0')
- return (NULL);
- (void) snprintf(buf, sizeof buf, "%cf%s", type, e->e_id);
- if (tTd(7, 2))
- printf("queuename: %s\n", buf);
- return (buf);
+ if (tTd(7, 50))
+ dprintf("assign_queueid: random_offset = %ld (%d)\n",
+ random_offset, (int)(cX + random_offset) % 60);
+
+ tm = gmtime(&LastQueueTime);
+ idbuf[0] = Base60Code[tm->tm_year % 60];
+ idbuf[1] = Base60Code[tm->tm_mon];
+ idbuf[2] = Base60Code[tm->tm_mday];
+ idbuf[3] = Base60Code[tm->tm_hour];
+ idbuf[4] = Base60Code[tm->tm_min];
+ idbuf[5] = Base60Code[tm->tm_sec];
+ idbuf[6] = Base60Code[((int)cX++ + random_offset) % 60];
+ (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;
+ if (tTd(7, 1))
+ dprintf("assign_queueid: assigned id %s, e=%lx\n",
+ e->e_id, (u_long) 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.
+**
+** If the system rotates PIDs fast enough, may get the
+** same pid in the same second for two distinct processes.
+** This will interfere with the queue file naming system.
+**
+** Parameters:
+** none
+**
+** Returns:
+** none
+*/
+void
+sync_queue_time()
+{
+# if FAST_PID_RECYCLE
+ if (OpMode != MD_TEST &&
+ OpMode != MD_VERIFY &&
+ LastQueueTime > 0 &&
+ LastQueuePid == getpid() &&
+ curtime() == LastQueueTime)
+ (void) sleep(1);
+# endif /* FAST_PID_RECYCLE */
}
/*
** UNLOCKQUEUE -- unlock the queue entry for a specified envelope
@@ -2283,12 +2755,13 @@ unlockqueue(e)
ENVELOPE *e;
{
if (tTd(51, 4))
- printf("unlockqueue(%s)\n",
+ 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)
- xfclose(e->e_lockfp, "unlockqueue", e->e_id);
+ (void) fclose(e->e_lockfp);
e->e_lockfp = NULL;
/* don't create a queue id if we don't already have one */
@@ -2319,7 +2792,7 @@ unlockqueue(e)
** none.
*/
-ADDRESS *
+static ADDRESS *
setctluser(user, qfver)
char *user;
int qfver;
@@ -2340,7 +2813,7 @@ setctluser(user, qfver)
*/
a = (ADDRESS *) xalloc(sizeof *a);
- bzero((char *) a, sizeof *a);
+ memset((char *) a, '\0', sizeof *a);
if (*user == '\0')
{
@@ -2377,10 +2850,10 @@ setctluser(user, qfver)
}
}
- a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */
+ a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */
a->q_mailer = LocalMailer;
if (p == NULL)
- a->q_paddr = a->q_user;
+ a->q_paddr = newstr(a->q_user);
else
a->q_paddr = newstr(p);
return a;
@@ -2396,27 +2869,500 @@ setctluser(user, qfver)
** none.
*/
+# define LOSEQF_LETTER 'Q'
+
void
loseqfile(e, why)
register ENVELOPE *e;
char *why;
{
char *p;
- char buf[MAXQFNAME + 1];
+ char buf[MAXPATHLEN];
if (e == NULL || e->e_id == NULL)
return;
p = queuename(e, 'q');
- if (strlen(p) > MAXQFNAME)
- {
- syserr("loseqfile: queuename (%s) too long", p);
+ if (strlen(p) >= (SIZE_T) sizeof buf)
return;
- }
- strcpy(buf, p);
- p = queuename(e, 'Q');
+ (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);
+ "Losing %s: %s", buf, why);
+}
+ /*
+** QID_PRINTNAME -- create externally printable version of queue id
+**
+** Parameters:
+** e -- the envelope.
+**
+** Returns:
+** a printable version
+*/
+
+char *
+qid_printname(e)
+ ENVELOPE *e;
+{
+ char *id;
+ static char idbuf[MAXQFNAME + 34];
+
+ if (e == NULL)
+ return "";
+
+ if (e->e_id == NULL)
+ id = "";
+ else
+ id = e->e_id;
+
+ if (e->e_queuedir == NOQDIR)
+ return id;
+
+ (void) snprintf(idbuf, sizeof idbuf, "%.32s/%s",
+ QPaths[e->e_queuedir].qp_name, id);
+ return idbuf;
+}
+ /*
+** QID_PRINTQUEUE -- create full version of queue directory for df files
+**
+** Parameters:
+** queuedir -- the short version of the queue directory
+**
+** Returns:
+** the full pathname to the queue (static)
+*/
+
+char *
+qid_printqueue(queuedir)
+ int queuedir;
+{
+ char *subdir;
+ static char dir[MAXPATHLEN];
+
+ if (queuedir == NOQDIR)
+ return QueueDir;
+
+ if (strcmp(QPaths[queuedir].qp_name, ".") == 0)
+ subdir = NULL;
+ else
+ subdir = QPaths[queuedir].qp_name;
+
+ (void) snprintf(dir, sizeof dir, "%s%s%s%s", QueueDir,
+ subdir == NULL ? "" : "/",
+ subdir == NULL ? "" : subdir,
+ (bitset(QP_SUBDF, QPaths[queuedir].qp_subdirs) ? "/df" : ""));
+ return dir;
+}
+ /*
+** SETNEWQUEUE -- Sets a new queue directory
+**
+** Assign a queue directory to an envelope and store the directory
+** in e->e_queuedir. The queue is chosen at random.
+**
+** This routine may be improved in the future to allow for more
+** elaborate queueing schemes. Suggestions and code contributions
+** are welcome.
+**
+** Parameters:
+** e -- envelope to assign a queue for.
+**
+** Returns:
+** none.
+*/
+
+void
+setnewqueue(e)
+ ENVELOPE *e;
+{
+ int idx;
+
+ if (tTd(41, 20))
+ dprintf("setnewqueue: called\n");
+
+ if (e->e_queuedir != NOQDIR)
+ {
+ if (tTd(41, 20))
+ dprintf("setnewqueue: e_queuedir already assigned (%s)\n",
+ qid_printqueue(e->e_queuedir));
+ return;
+ }
+
+ if (NumQueues == 1)
+ idx = 0;
+ else
+ {
+#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_queuedir = idx;
+ if (tTd(41, 3))
+ dprintf("setnewqueue: Assigned queue directory %s\n",
+ qid_printqueue(e->e_queuedir));
+}
+
+ /*
+** CHKQDIR -- check a queue directory
+**
+** Parameters:
+** name -- name of queue directory
+** sff -- flags for safefile()
+**
+** Returns:
+** is it a queue directory?
+*/
+
+static bool
+chkqdir(name, sff)
+ char *name;
+ long sff;
+{
+ struct stat statb;
+ int i;
+
+# if HASLSTAT
+ if (lstat(name, &statb) < 0)
+# else /* HASLSTAT */
+ if (stat(name, &statb) < 0)
+# endif /* HASLSTAT */
+ {
+ if (tTd(41, 2))
+ dprintf("multiqueue_cache: stat(\"%s\"): %s\n",
+ name, errstring(errno));
+ return FALSE;
+ }
+# 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;
+ }
+ }
+# endif /* HASLSTAT */
+
+ if (!S_ISDIR(statb.st_mode))
+ {
+ if (tTd(41, 2))
+ dprintf("multiqueue_cache: \"%s\": Not a directory\n",
+ name);
+ return FALSE;
+ }
+
+ /* Print a warning if unsafe (but still use it) */
+ 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;
+}
+
+ /*
+** MULTIQUEUE_CACHE -- cache a list of paths to queues.
+**
+** Each potential queue is checked as the cache is built.
+** Thereafter, each is blindly trusted.
+** Note that we can be called again after a timeout to rebuild
+** (although code for that is not ready yet).
+**
+** Parameters:
+** none
+**
+** Returns:
+** none
+*/
+
+void
+multiqueue_cache()
+{
+ 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];
+
+ if (tTd(41, 20))
+ dprintf("multiqueue_cache: called\n");
+
+ if (NumQueues != 0 && QPaths != NULL)
+ {
+ for (i = 0; i < NumQueues; i++)
+ {
+ if (QPaths[i].qp_name != NULL)
+ (void) free(QPaths[i].qp_name);
+ }
+ (void) free((char *)QPaths);
+ QPaths = NULL;
+ 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 (*cp == '*')
+ {
+ *cp = '\0';
+ if ((cp = strrchr(qpath, '/')) == NULL)
+ {
+ syserr("QueueDirectory: can not wildcard relative path");
+ if (tTd(41, 2))
+ dprintf("multiqueue_cache: \"%s\": Can not wildcard relative path.\n",
+ QueueDir);
+ ExitStat = EX_CONFIG;
+ return;
+ }
+ if (cp == qpath)
+ {
+ /*
+ ** Special case of top level wildcard, like /foo*
+ */
+
+ (void) snprintf(qpath + 1, sizeof qpath - 1,
+ "%s", qpath);
+ ++cp;
+ }
+ *(cp++) = '\0';
+ len = strlen(cp);
+
+ if (tTd(41, 2))
+ dprintf("multiqueue_cache: prefix=\"%s\"\n", cp);
+
+ QueueDir = newstr(qpath);
+
+ /*
+ ** 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)
+ {
+ syserr("can not chdir(%s)", QueueDir);
+ if (tTd(41, 2))
+ dprintf("multiqueue_cache: \"%s\": %s\n",
+ qpath, errstring(errno));
+ ExitStat = EX_CONFIG;
+ return;
+ }
+
+ if ((dp = opendir(".")) == NULL)
+ {
+ syserr("can not opendir(%s)", QueueDir);
+ if (tTd(41, 2))
+ dprintf("multiqueue_cache: opendir(\"%s\"): %s\n",
+ QueueDir, errstring(errno));
+ ExitStat = EX_CONFIG;
+ return;
+ }
+ while ((d = readdir(dp)) != NULL)
+ {
+ if (strncmp(d->d_name, cp, len) != 0)
+ {
+ if (tTd(41, 5))
+ dprintf("multiqueue_cache: \"%s\", skipped\n",
+ d->d_name);
+ continue;
+ }
+ if (!chkqdir(d->d_name, sff))
+ continue;
+
+ if (QPaths == NULL)
+ {
+ slotsleft = 20;
+ QPaths = (QPATHS *)xalloc((sizeof *QPaths) *
+ slotsleft);
+ NumQueues = 0;
+ }
+ else if (slotsleft < 1)
+ {
+ QPaths = (QPATHS *)realloc((char *)QPaths,
+ (sizeof *QPaths) *
+ (NumQueues + 10));
+ if (QPaths == NULL)
+ {
+ (void) closedir(dp);
+ return;
+ }
+ slotsleft += 10;
+ }
+
+ /* 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;
+
+ /* assert(strlen(d->d_name) < MAXPATHLEN - 14) */
+ /* maybe even - 17 (subdirs) */
+ QPaths[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++;
+ slotsleft--;
+ }
+ (void) closedir(dp);
+ }
+ if (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;
+
+ /* test path to get warning messages */
+ (void) safedirpath(QueueDir, RunAsUid, RunAsGid,
+ NULL, sff, 0, 0);
+ if (chdir(QueueDir) < 0)
+ {
+ syserr("can not chdir(%s)", QueueDir);
+ if (tTd(41, 2))
+ dprintf("multiqueue_cache: \"%s\": %s\n",
+ QueueDir, errstring(errno));
+ ExitStat = EX_CONFIG;
+ }
+
+ /* check subdirs */
+ (void) snprintf(subdir, sizeof subdir, "%s/qf", QueueDir);
+ if (chkqdir(subdir, sff))
+ QPaths[0].qp_subdirs |= QP_SUBQF;
+
+ (void) snprintf(subdir, sizeof subdir, "%s/df", QueueDir);
+ if (chkqdir(subdir, sff))
+ QPaths[0].qp_subdirs |= QP_SUBDF;
+
+ (void) snprintf(subdir, sizeof subdir, "%s/xf", QueueDir);
+ if (chkqdir(subdir, sff))
+ QPaths[0].qp_subdirs |= QP_SUBXF;
+ }
+}
+
+# if 0
+ /*
+** HASHFQN -- calculate a hash value for a fully qualified host name
+**
+** Arguments:
+** fqn -- an all lower-case host.domain string
+** buckets -- the number of buckets (queue directories)
+**
+** Returns:
+** a bucket number (signed integer)
+** -1 on error
+**
+** Contributed by Exactis.com, Inc.
+*/
+
+int
+hashfqn(fqn, buckets)
+ register char *fqn;
+ int buckets;
+{
+ register char *p;
+ register int h = 0, hash, cnt;
+# define WATERINC (1000)
+
+ if (fqn == NULL)
+ return -1;
+
+ /*
+ ** A variation on the gdb hash
+ ** This is the best as of Feb 19, 1996 --bcx
+ */
+
+ p = fqn;
+ h = 0x238F13AF * strlen(p);
+ for (cnt = 0; *p != 0; ++p, cnt++)
+ {
+ h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF;
+ }
+ h = (1103515243 * h + 12345) & 0x7FFFFFFF;
+ if (buckets < 2)
+ hash = 0;
+ else
+ hash = (h % buckets);
+
+ return hash;
+}
+# endif /* 0 */
+
+# if _FFR_QUEUEDELAY
+ /*
+** QUEUEDELAY -- compute queue delay time
+**
+** Parameters:
+** e -- the envelope to queue up.
+**
+** Returns:
+** queue delay time
+**
+** Side Effects:
+** may change e_queuedelay
+*/
+
+static time_t
+queuedelay(e)
+ ENVELOPE *e;
+{
+ time_t qd;
+
+ if (e->e_queuealg == QD_EXP)
+ {
+ if (e->e_queuedelay == 0)
+ e->e_queuedelay = QueueInitDelay;
+ else
+ {
+ e->e_queuedelay *= 2;
+ if (e->e_queuedelay > QueueMaxDelay)
+ e->e_queuedelay = QueueMaxDelay;
+ }
+ qd = e->e_queuedelay;
+ }
+ else
+ qd = MinQueueAge;
+ return qd;
}
+# endif /* _FFR_QUEUEDELAY */
+#endif /* QUEUE */
diff --git a/contrib/sendmail/src/readcf.c b/contrib/sendmail/src/readcf.c
index df40097..2521435 100644
--- a/contrib/sendmail/src/readcf.c
+++ b/contrib/sendmail/src/readcf.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,14 +12,25 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)readcf.c 8.238 (Berkeley) 1/28/1999";
-#endif /* not lint */
+static char id[] = "@(#)$Id: readcf.c,v 8.382.4.14 2000/07/12 00:00:27 geir Exp $";
+#endif /* ! lint */
-# include "sendmail.h"
-# include <grp.h>
-#if NAMED_BIND
-# include <resolv.h>
-#endif
+#include <sendmail.h>
+
+
+#if NETINET || NETINET6
+# include <arpa/inet.h>
+#endif /* NETINET || NETINET6 */
+
+#define SECONDS
+#define MINUTES * 60
+#define HOUR * 3600
+#define HOURS HOUR
+
+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));
/*
** READCF -- read configuration file.
@@ -74,7 +86,7 @@ readcf(cfname, safe, e)
register ENVELOPE *e;
{
FILE *cf;
- int ruleset = 0;
+ int ruleset = -1;
char *q;
struct rewrite *rwp = NULL;
char *bp;
@@ -84,18 +96,13 @@ readcf(cfname, safe, e)
bool optional;
int mid;
register char *p;
- int sff = SFF_OPENASROOT;
+ long sff = SFF_OPENASROOT;
struct stat statb;
char buf[MAXLINE];
char exbuf[MAXLINE];
char pvpbuf[MAXLINE + MAXATOM];
static char *null_list[1] = { NULL };
- extern char **copyplist __P((char **, bool));
- extern char *munchstring __P((char *, char **, int));
- extern void fileclass __P((int, char *, char *, bool, bool));
- extern void toomany __P((int, int));
- extern void translate_dollars __P((char *));
- extern void inithostmaps __P((void));
+ extern u_char TokTypeNoC[];
FileName = cfname;
LineNumber = 0;
@@ -128,13 +135,13 @@ readcf(cfname, safe, e)
FileName);
if (LogLevel > 0)
sm_syslog(LOG_CRIT, NOQID,
- "%s: WARNING: dangerous write permissions",
- FileName);
+ "%s: WARNING: dangerous write permissions",
+ FileName);
}
#ifdef XLA
xla_zero();
-#endif
+#endif /* XLA */
while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
{
@@ -157,6 +164,11 @@ readcf(cfname, safe, e)
break;
case 'R': /* rewriting rule */
+ if (ruleset < 0)
+ {
+ syserr("missing valid ruleset for \"%s\"", bp);
+ break;
+ }
for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
continue;
@@ -183,7 +195,8 @@ readcf(cfname, safe, e)
*p = '\0';
expand(&bp[1], exbuf, sizeof exbuf, e);
rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
- sizeof pvpbuf, NULL, NULL);
+ sizeof pvpbuf, NULL,
+ ConfigLevel >= 9 ? TokTypeNoC : NULL);
nfuzzy = 0;
if (rwp->r_lhs != NULL)
{
@@ -247,6 +260,7 @@ readcf(cfname, safe, e)
syserr("Inappropriate use of %s on LHS",
botch);
}
+ rwp->r_line = LineNumber;
}
else
{
@@ -263,7 +277,8 @@ readcf(cfname, safe, e)
*p = '\0';
expand(q, exbuf, sizeof exbuf, e);
rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
- sizeof pvpbuf, NULL, NULL);
+ sizeof pvpbuf, NULL,
+ ConfigLevel >= 9 ? TokTypeNoC : NULL);
if (rwp->r_rhs != NULL)
{
register char **ap;
@@ -324,11 +339,15 @@ readcf(cfname, safe, e)
ruleset = strtorwset(exbuf, NULL, ST_ENTER);
if (ruleset < 0)
break;
+
rwp = RewriteRules[ruleset];
if (rwp != NULL)
{
- if (OpMode == MD_TEST || tTd(37, 1))
+ if (OpMode == MD_TEST)
printf("WARNING: Ruleset %s has multiple definitions\n",
+ &bp[1]);
+ if (tTd(37, 1))
+ dprintf("WARNING: Ruleset %s has multiple definitions\n",
&bp[1]);
while (rwp->r_next != NULL)
rwp = rwp->r_next;
@@ -342,7 +361,7 @@ readcf(cfname, safe, e)
break;
case 'H': /* required header line */
- (void) chompheader(&bp[1], TRUE, NULL, e);
+ (void) chompheader(&bp[1], CHHDR_DEF, NULL, e);
break;
case 'C': /* word class */
@@ -390,13 +409,16 @@ readcf(cfname, safe, e)
}
else
optional = FALSE;
+
file = p;
+ q = p;
+ while (*q != '\0' && !(isascii(*q) && isspace(*q)))
+ q++;
if (*file == '|')
p = "%s";
else
{
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
+ p = q;
if (*p == '\0')
p = "%s";
else
@@ -413,7 +435,7 @@ readcf(cfname, safe, e)
case 'L': /* extended load average description */
xla_init(&bp[1]);
break;
-#endif
+#endif /* XLA */
#if defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO)
case 'L': /* lookup macro */
@@ -423,7 +445,7 @@ readcf(cfname, safe, e)
goto badline;
sun_lg_config_line(bp, e);
break;
-#endif
+#endif /* defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) */
case 'M': /* define mailer */
makemailer(&bp[1]);
@@ -454,7 +476,7 @@ readcf(cfname, safe, e)
continue;
if (!isascii(*p) || !isdigit(*p))
{
- syserr("invalid argument to V line: \"%.20s\"",
+ syserr("invalid argument to V line: \"%.20s\"",
&bp[1]);
break;
}
@@ -483,8 +505,6 @@ readcf(cfname, safe, e)
if (*ep++ == '/')
{
- extern bool setvendor __P((char *));
-
/* extract vendor code */
for (p = ep; isascii(*p) && isalpha(*p); )
p++;
@@ -508,6 +528,12 @@ readcf(cfname, safe, e)
setuserenv(&bp[1], p);
break;
+#if _FFR_MILTER
+ case 'X': /* mail filter */
+ milter_setup(&bp[1]);
+ break;
+#endif /* _FFR_MILTER */
+
default:
badline:
syserr("unknown configuration line \"%s\"", bp);
@@ -520,12 +546,15 @@ readcf(cfname, safe, e)
syserr("I/O read error");
finis(FALSE, EX_OSFILE);
}
- fclose(cf);
+ (void) fclose(cf);
FileName = NULL;
/* initialize host maps from local service tables */
inithostmaps();
+ /* initialize daemon (if not defined yet) */
+ initdaemon();
+
/* determine if we need to do special name-server frotz */
{
int nmaps;
@@ -558,7 +587,7 @@ readcf(cfname, safe, e)
UseHesiod = TRUE;
}
}
-#endif
+#endif /* HESIOD */
}
}
/*
@@ -599,16 +628,16 @@ translate_dollars(bp)
case '\\':
/* it's backslash escaped */
- (void) strcpy(p, p + 1);
+ (void) strlcpy(p, p + 1, strlen(p));
break;
default:
- /* delete preceeding white space */
+ /* delete leading white space */
while (isascii(*p) && isspace(*p) &&
*p != '\n' && p > bp)
p--;
if ((e = strchr(++p, '\n')) != NULL)
- (void) strcpy(p, e);
+ (void) strlcpy(p, e, strlen(p));
else
*p-- = '\0';
break;
@@ -622,7 +651,7 @@ translate_dollars(bp)
if (p[1] == '$')
{
/* actual dollar sign.... */
- (void) strcpy(p, p + 1);
+ (void) strlcpy(p, p + 1, strlen(p));
continue;
}
@@ -635,8 +664,8 @@ translate_dollars(bp)
/* convert macro name to code */
*p = macid(p, &ep);
- if (ep != p)
- strcpy(p + 1, ep);
+ if (ep != p + 1)
+ (void) strlcpy(p + 1, ep, strlen(p + 1));
}
/* strip trailing white space from the line */
@@ -657,7 +686,7 @@ translate_dollars(bp)
** gives a syserr.
*/
-void
+static void
toomany(id, maxcnt)
int id;
int maxcnt;
@@ -684,7 +713,7 @@ toomany(id, maxcnt)
** the named class.
*/
-void
+static void
fileclass(class, filename, fmt, safe, optional)
int class;
char *filename;
@@ -693,13 +722,13 @@ fileclass(class, filename, fmt, safe, optional)
bool optional;
{
FILE *f;
- int sff;
+ long sff;
pid_t pid;
register char *p;
char buf[MAXLINE];
if (tTd(37, 2))
- printf("fileclass(%s, fmt=%s)\n", filename, fmt);
+ dprintf("fileclass(%s, fmt=%s)\n", filename, fmt);
if (filename[0] == '|')
{
@@ -725,9 +754,10 @@ fileclass(class, filename, fmt, safe, optional)
{
pid = -1;
sff = SFF_REGONLY;
- if (!bitset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail))
+ if (!bitnset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail))
sff |= SFF_SAFEDIRPATH;
- if (!bitset(DBS_LINKEDCLASSFILEINWRITABLEDIR, DontBlameSendmail))
+ if (!bitnset(DBS_LINKEDCLASSFILEINWRITABLEDIR,
+ DontBlameSendmail))
sff |= SFF_NOWLINK;
if (safe)
sff |= SFF_OPENASROOT;
@@ -738,26 +768,25 @@ fileclass(class, filename, fmt, safe, optional)
if (f == NULL)
{
if (!optional)
- syserr("fileclass: cannot open %s", filename);
+ syserr("fileclass: cannot open '%s'", filename);
return;
}
while (fgets(buf, sizeof buf, f) != NULL)
{
- register char *p;
-# if SCANF
+#if SCANF
char wordbuf[MAXLINE + 1];
-# endif
+#endif /* SCANF */
if (buf[0] == '#')
continue;
-# if SCANF
+#if SCANF
if (sscanf(buf, fmt, wordbuf) != 1)
continue;
p = wordbuf;
-# else /* SCANF */
+#else /* SCANF */
p = buf;
-# endif /* SCANF */
+#endif /* SCANF */
/*
** Break up the match into words.
@@ -808,6 +837,7 @@ fileclass(class, filename, fmt, safe, optional)
** 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
** The first word is the canonical name of the mailer.
**
** Returns:
@@ -828,12 +858,10 @@ makemailer(line)
char fcode;
auto char *endp;
extern int NextMailer;
- extern char **makeargv __P((char *));
- extern char *munchstring __P((char *, char **, int));
/* allocate a mailer and set up defaults */
m = (struct mailer *) xalloc(sizeof *m);
- bzero((char *) m, sizeof *m);
+ memset((char *) m, '\0', sizeof *m);
/* collect the mailer name */
for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
@@ -923,6 +951,16 @@ makemailer(line)
m->m_maxsize = atol(p);
break;
+ case 'm': /* maximum messages per connection */
+ m->m_maxdeliveries = atoi(p);
+ break;
+
+#if _FFR_DYNAMIC_TOBUF
+ case 'r': /* max recipient per envelope */
+ m->m_maxrcpt = atoi(p);
+ break;
+#endif /* _FFR_DYNAMIC_TOBUF */
+
case 'L': /* maximum line length */
m->m_linelimit = atoi(p);
if (m->m_linelimit < 0)
@@ -1040,6 +1078,23 @@ makemailer(line)
m->m_gid = strtol(p, NULL, 0);
}
break;
+
+ case 'W': /* wait timeout */
+ m->m_wait = convtime(p, 's');
+ break;
+
+ case '/': /* new root directory */
+ if (*p == '\0')
+ syserr("mailer %s: null root directory",
+ m->m_name);
+ else
+ m->m_rootdir = newstr(p);
+ break;
+
+ default:
+ syserr("M%s: unknown mailer equate %c=",
+ m->m_name, fcode);
+ break;
}
p = delimptr;
@@ -1063,6 +1118,11 @@ makemailer(line)
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))
{
@@ -1072,18 +1132,51 @@ makemailer(line)
setbitn(M_7BITS, m->m_flags);
}
- if (strcmp(m->m_mailer, "[IPC]") == 0 ||
- strcmp(m->m_mailer, "[TCP]") == 0)
+ if (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)
- m->m_diagtype = "smtp";
+#if _FFR_REMOVE_TCP_PATH
+ syserr("M%s: P=[TCP] is deprecated, use P=[IPC] instead\n",
+ m->m_name);
+#else /* _FFR_REMOVE_TCP_PATH */
+ printf("M%s: Warning: P=[TCP] is deprecated, use P=[IPC] instead\n",
+ m->m_name);
+#endif /* _FFR_REMOVE_TCP_PATH */
}
- if (strcmp(m->m_mailer, "[FILE]") == 0)
+ 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 */
+ )
+ {
+ /* Use the second argument for host or path to socket */
+ if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
+ m->m_argv[1][0] == '\0')
+ {
+ syserr("M%s: too few parameters for %s mailer",
+ m->m_name, m->m_mailer);
+ }
+ if (strcmp(m->m_argv[0], "TCP") != 0 &&
+#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,
+#if NETUNIX
+ "TCP or FILE"
+#else /* NETUNIX */
+ "TCP"
+#endif /* NETUNIX */
+ );
+ }
+
+ }
+ else if (strcmp(m->m_mailer, "[FILE]") == 0)
{
/* Use the second argument for filename */
if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
@@ -1101,6 +1194,23 @@ 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;
@@ -1108,8 +1218,6 @@ makemailer(line)
/* default for SMTP is \r\n; use \n for local delivery */
for (pp = m->m_argv; *pp != NULL; pp++)
{
- char *p;
-
for (p = *pp; *p != '\0'; )
{
if ((*p++ & 0377) == MACROEXPAND && *p == 'u')
@@ -1149,6 +1257,10 @@ makemailer(line)
**
** Returns:
** the munched string.
+**
+** Side Effects:
+** the munched string is a local static buffer.
+** it must be copied before the function is called again.
*/
char *
@@ -1204,7 +1316,7 @@ munchstring(p, delimptr, delim)
if (delimptr != NULL)
*delimptr = p;
*q++ = '\0';
- return (buf);
+ return buf;
}
/*
** MAKEARGV -- break up a string into words
@@ -1219,7 +1331,7 @@ munchstring(p, delimptr, delim)
** munges p.
*/
-char **
+static char **
makeargv(p)
register char *p;
{
@@ -1243,9 +1355,9 @@ makeargv(p)
/* now make a copy of the argv */
avp = (char **) xalloc(sizeof *avp * i);
- bcopy((char *) argv, (char *) avp, sizeof *avp * i);
+ memmove((char *) avp, (char *) argv, sizeof *avp * i);
- return (avp);
+ return avp;
}
/*
** PRINTRULES -- print rewrite rules (for debugging)
@@ -1297,11 +1409,25 @@ printmailer(m)
{
int j;
- printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
- m->m_mno, m->m_name,
- m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
- m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
- (int) m->m_uid, (int) m->m_gid);
+ printf("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);
+ else
+ printf("%s/", RuleSetNames[m->m_se_rwset]);
+ if (RuleSetNames[m->m_sh_rwset] == NULL)
+ printf("%d R=", m->m_sh_rwset);
+ else
+ printf("%s R=", RuleSetNames[m->m_sh_rwset]);
+ if (RuleSetNames[m->m_re_rwset] == NULL)
+ printf("%d/", m->m_re_rwset);
+ else
+ printf("%s/", RuleSetNames[m->m_re_rwset]);
+ if (RuleSetNames[m->m_rh_rwset] == NULL)
+ printf("%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);
for (j = '\0'; j <= '\177'; j++)
if (bitnset(j, m->m_flags))
(void) putchar(j);
@@ -1313,6 +1439,9 @@ printmailer(m)
m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
+#if _FFR_DYNAMIC_TOBUF
+ printf(" r=%d", m->m_maxrcpt);
+#endif /* _FFR_DYNAMIC_TOBUF */
if (m->m_argv != NULL)
{
char **a = m->m_argv;
@@ -1347,13 +1476,11 @@ printmailer(m)
** Sets options as implied by the arguments.
*/
-static BITMAP StickyOpt; /* set if option is stuck */
-extern void settimeout __P((char *, char *));
-
+static BITMAP256 StickyOpt; /* set if option is stuck */
#if NAMED_BIND
-struct resolverflags
+static struct resolverflags
{
char *rf_name; /* name of the flag */
long rf_bits; /* bits to set/clear */
@@ -1372,170 +1499,219 @@ struct resolverflags
{ NULL, 0 }
};
-#endif
+#endif /* NAMED_BIND */
+
+#define OI_NONE 0 /* no special treatment */
+#define OI_SAFE 0x0001 /* safe for random people to use */
+#define OI_SUBOPT 0x0002 /* option has suboptions */
-struct optioninfo
+static struct optioninfo
{
char *o_name; /* long name of option */
u_char o_code; /* short name of option */
- bool o_safe; /* safe for random people to use */
+ u_short o_flags; /* option flags */
} OptionTab[] =
{
- { "SevenBitInput", '7', TRUE },
+#if defined(SUN_EXTENSIONS) && defined(REMOTE_MODE)
+ { "RemoteMode", '>', OI_NONE },
+#endif /* defined(SUN_EXTENSIONS) && defined(REMOTE_MODE) */
+ { "SevenBitInput", '7', OI_SAFE },
#if MIME8TO7
- { "EightBitMode", '8', TRUE },
-#endif
- { "AliasFile", 'A', FALSE },
- { "AliasWait", 'a', FALSE },
- { "BlankSub", 'B', FALSE },
- { "MinFreeBlocks", 'b', TRUE },
- { "CheckpointInterval", 'C', TRUE },
- { "HoldExpensive", 'c', FALSE },
- { "AutoRebuildAliases", 'D', FALSE },
- { "DeliveryMode", 'd', TRUE },
- { "ErrorHeader", 'E', FALSE },
- { "ErrorMode", 'e', TRUE },
- { "TempFileMode", 'F', FALSE },
- { "SaveFromLine", 'f', FALSE },
- { "MatchGECOS", 'G', FALSE },
- { "HelpFile", 'H', FALSE },
- { "MaxHopCount", 'h', FALSE },
- { "ResolverOptions", 'I', FALSE },
- { "IgnoreDots", 'i', TRUE },
- { "ForwardPath", 'J', FALSE },
- { "SendMimeErrors", 'j', TRUE },
- { "ConnectionCacheSize", 'k', FALSE },
- { "ConnectionCacheTimeout", 'K', FALSE },
- { "UseErrorsTo", 'l', FALSE },
- { "LogLevel", 'L', TRUE },
- { "MeToo", 'm', TRUE },
- { "CheckAliases", 'n', FALSE },
- { "OldStyleHeaders", 'o', TRUE },
- { "DaemonPortOptions", 'O', FALSE },
- { "PrivacyOptions", 'p', TRUE },
- { "PostmasterCopy", 'P', FALSE },
- { "QueueFactor", 'q', FALSE },
- { "QueueDirectory", 'Q', FALSE },
- { "DontPruneRoutes", 'R', FALSE },
- { "Timeout", 'r', FALSE },
- { "StatusFile", 'S', FALSE },
- { "SuperSafe", 's', TRUE },
- { "QueueTimeout", 'T', FALSE },
- { "TimeZoneSpec", 't', FALSE },
- { "UserDatabaseSpec", 'U', FALSE },
- { "DefaultUser", 'u', FALSE },
- { "FallbackMXhost", 'V', FALSE },
- { "Verbose", 'v', TRUE },
- { "TryNullMXList", 'w', FALSE },
- { "QueueLA", 'x', FALSE },
- { "RefuseLA", 'X', FALSE },
- { "RecipientFactor", 'y', FALSE },
- { "ForkEachJob", 'Y', FALSE },
- { "ClassFactor", 'z', FALSE },
- { "RetryFactor", 'Z', FALSE },
+ { "EightBitMode", '8', OI_SAFE },
+#endif /* MIME8TO7 */
+ { "AliasFile", 'A', OI_NONE },
+ { "AliasWait", 'a', OI_NONE },
+ { "BlankSub", 'B', OI_NONE },
+ { "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 },
+ { "HelpFile", 'H', OI_NONE },
+ { "MaxHopCount", 'h', OI_NONE },
+ { "ResolverOptions", 'I', OI_NONE },
+ { "IgnoreDots", 'i', OI_SAFE },
+ { "ForwardPath", 'J', OI_NONE },
+ { "SendMimeErrors", 'j', OI_SAFE },
+ { "ConnectionCacheSize", 'k', OI_NONE },
+ { "ConnectionCacheTimeout", 'K', OI_NONE },
+ { "UseErrorsTo", 'l', OI_NONE },
+ { "LogLevel", 'L', OI_SAFE },
+ { "MeToo", 'm', OI_SAFE },
+ { "CheckAliases", 'n', OI_NONE },
+ { "OldStyleHeaders", 'o', OI_SAFE },
+ { "DaemonPortOptions", 'O', OI_NONE },
+ { "PrivacyOptions", 'p', OI_SAFE },
+ { "PostmasterCopy", 'P', OI_NONE },
+ { "QueueFactor", 'q', OI_NONE },
+ { "QueueDirectory", 'Q', OI_NONE },
+ { "DontPruneRoutes", 'R', OI_NONE },
+ { "Timeout", 'r', OI_SUBOPT },
+ { "StatusFile", 'S', OI_NONE },
+ { "SuperSafe", 's', OI_SAFE },
+ { "QueueTimeout", 'T', OI_NONE },
+ { "TimeZoneSpec", 't', OI_NONE },
+ { "UserDatabaseSpec", 'U', OI_NONE },
+ { "DefaultUser", 'u', OI_NONE },
+ { "FallbackMXhost", 'V', OI_NONE },
+ { "Verbose", 'v', OI_SAFE },
+ { "TryNullMXList", 'w', OI_NONE },
+ { "QueueLA", 'x', OI_NONE },
+ { "RefuseLA", 'X', OI_NONE },
+ { "RecipientFactor", 'y', OI_NONE },
+ { "ForkEachJob", 'Y', OI_NONE },
+ { "ClassFactor", 'z', OI_NONE },
+ { "RetryFactor", 'Z', OI_NONE },
#define O_QUEUESORTORD 0x81
- { "QueueSortOrder", O_QUEUESORTORD, TRUE },
+ { "QueueSortOrder", O_QUEUESORTORD, OI_SAFE },
#define O_HOSTSFILE 0x82
- { "HostsFile", O_HOSTSFILE, FALSE },
+ { "HostsFile", O_HOSTSFILE, OI_NONE },
#define O_MQA 0x83
- { "MinQueueAge", O_MQA, TRUE },
+ { "MinQueueAge", O_MQA, OI_SAFE },
#define O_DEFCHARSET 0x85
- { "DefaultCharSet", O_DEFCHARSET, TRUE },
+ { "DefaultCharSet", O_DEFCHARSET, OI_SAFE },
#define O_SSFILE 0x86
- { "ServiceSwitchFile", O_SSFILE, FALSE },
+ { "ServiceSwitchFile", O_SSFILE, OI_NONE },
#define O_DIALDELAY 0x87
- { "DialDelay", O_DIALDELAY, TRUE },
+ { "DialDelay", O_DIALDELAY, OI_SAFE },
#define O_NORCPTACTION 0x88
- { "NoRecipientAction", O_NORCPTACTION, TRUE },
+ { "NoRecipientAction", O_NORCPTACTION, OI_SAFE },
#define O_SAFEFILEENV 0x89
- { "SafeFileEnvironment", O_SAFEFILEENV, FALSE },
+ { "SafeFileEnvironment", O_SAFEFILEENV, OI_NONE },
#define O_MAXMSGSIZE 0x8a
- { "MaxMessageSize", O_MAXMSGSIZE, FALSE },
+ { "MaxMessageSize", O_MAXMSGSIZE, OI_NONE },
#define O_COLONOKINADDR 0x8b
- { "ColonOkInAddr", O_COLONOKINADDR, TRUE },
+ { "ColonOkInAddr", O_COLONOKINADDR, OI_SAFE },
#define O_MAXQUEUERUN 0x8c
- { "MaxQueueRunSize", O_MAXQUEUERUN, TRUE },
+ { "MaxQueueRunSize", O_MAXQUEUERUN, OI_SAFE },
#define O_MAXCHILDREN 0x8d
- { "MaxDaemonChildren", O_MAXCHILDREN, FALSE },
+ { "MaxDaemonChildren", O_MAXCHILDREN, OI_NONE },
#define O_KEEPCNAMES 0x8e
- { "DontExpandCnames", O_KEEPCNAMES, FALSE },
+ { "DontExpandCnames", O_KEEPCNAMES, OI_NONE },
#define O_MUSTQUOTE 0x8f
- { "MustQuoteChars", O_MUSTQUOTE, FALSE },
+ { "MustQuoteChars", O_MUSTQUOTE, OI_NONE },
#define O_SMTPGREETING 0x90
- { "SmtpGreetingMessage", O_SMTPGREETING, FALSE },
+ { "SmtpGreetingMessage", O_SMTPGREETING, OI_NONE },
#define O_UNIXFROM 0x91
- { "UnixFromLine", O_UNIXFROM, FALSE },
+ { "UnixFromLine", O_UNIXFROM, OI_NONE },
#define O_OPCHARS 0x92
- { "OperatorChars", O_OPCHARS, FALSE },
+ { "OperatorChars", O_OPCHARS, OI_NONE },
#define O_DONTINITGRPS 0x93
- { "DontInitGroups", O_DONTINITGRPS, FALSE },
+ { "DontInitGroups", O_DONTINITGRPS, OI_NONE },
#define O_SLFH 0x94
- { "SingleLineFromHeader", O_SLFH, TRUE },
+ { "SingleLineFromHeader", O_SLFH, OI_SAFE },
#define O_ABH 0x95
- { "AllowBogusHELO", O_ABH, TRUE },
+ { "AllowBogusHELO", O_ABH, OI_SAFE },
#define O_CONNTHROT 0x97
- { "ConnectionRateThrottle", O_CONNTHROT, FALSE },
+ { "ConnectionRateThrottle", O_CONNTHROT, OI_NONE },
#define O_UGW 0x99
- { "UnsafeGroupWrites", O_UGW, FALSE },
+ { "UnsafeGroupWrites", O_UGW, OI_NONE },
#define O_DBLBOUNCE 0x9a
- { "DoubleBounceAddress", O_DBLBOUNCE, FALSE },
+ { "DoubleBounceAddress", O_DBLBOUNCE, OI_NONE },
#define O_HSDIR 0x9b
- { "HostStatusDirectory", O_HSDIR, FALSE },
+ { "HostStatusDirectory", O_HSDIR, OI_NONE },
#define O_SINGTHREAD 0x9c
- { "SingleThreadDelivery", O_SINGTHREAD, FALSE },
+ { "SingleThreadDelivery", O_SINGTHREAD, OI_NONE },
#define O_RUNASUSER 0x9d
- { "RunAsUser", O_RUNASUSER, FALSE },
-#if _FFR_DSN_RRT_OPTION
+ { "RunAsUser", O_RUNASUSER, OI_NONE },
#define O_DSN_RRT 0x9e
- { "RrtImpliesDsn", O_DSN_RRT, FALSE },
-#endif
-#if _FFR_PIDFILE_OPTION
+ { "RrtImpliesDsn", O_DSN_RRT, OI_NONE },
#define O_PIDFILE 0x9f
- { "PidFile", O_PIDFILE, FALSE },
-#endif
+ { "PidFile", O_PIDFILE, OI_NONE },
#define O_DONTBLAMESENDMAIL 0xa0
- { "DontBlameSendmail", O_DONTBLAMESENDMAIL, FALSE },
+ { "DontBlameSendmail", O_DONTBLAMESENDMAIL, OI_NONE },
#define O_DPI 0xa1
- { "DontProbeInterfaces", O_DPI, FALSE },
+ { "DontProbeInterfaces", O_DPI, OI_NONE },
#define O_MAXRCPT 0xa2
- { "MaxRecipientsPerMessage", O_MAXRCPT, FALSE },
-#if _FFR_DEADLETTERDROP_OPTION
+ { "MaxRecipientsPerMessage", O_MAXRCPT, OI_SAFE },
#define O_DEADLETTER 0xa3
- { "DeadLetterDrop", O_DEADLETTER, FALSE },
-#endif
+ { "DeadLetterDrop", O_DEADLETTER, OI_NONE },
#if _FFR_DONTLOCKFILESFORREAD_OPTION
-#define O_DONTLOCK 0xa4
- { "DontLockFilesForRead", O_DONTLOCK, FALSE },
-#endif
-#if _FFR_MAXALIASRECURSION_OPTION
+# define O_DONTLOCK 0xa4
+ { "DontLockFilesForRead", O_DONTLOCK, OI_NONE },
+#endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
#define O_MAXALIASRCSN 0xa5
- { "MaxAliasRecursion", O_MAXALIASRCSN, FALSE },
-#endif
-#if _FFR_CONNECTONLYTO_OPTION
+ { "MaxAliasRecursion", O_MAXALIASRCSN, OI_NONE },
#define O_CNCTONLYTO 0xa6
- { "ConnectOnlyTo", O_CNCTONLYTO, FALSE },
-#endif
-#if _FFR_TRUSTED_USER
+ { "ConnectOnlyTo", O_CNCTONLYTO, OI_NONE },
#define O_TRUSTUSER 0xa7
- { "TrustedUser", O_TRUSTUSER, FALSE },
-#endif
-#if _FFR_MAX_MIME_HEADER_LENGTH
+ { "TrustedUser", O_TRUSTUSER, OI_NONE },
#define O_MAXMIMEHDRLEN 0xa8
- { "MaxMimeHeaderLength", O_MAXMIMEHDRLEN, FALSE },
-#endif
-#if _FFR_CONTROL_SOCKET
+ { "MaxMimeHeaderLength", O_MAXMIMEHDRLEN, OI_NONE },
#define O_CONTROLSOCKET 0xa9
- { "ControlSocketName", O_CONTROLSOCKET, FALSE },
-#endif
-#if _FFR_MAX_HEADERS_LENGTH
+ { "ControlSocketName", O_CONTROLSOCKET, OI_NONE },
#define O_MAXHDRSLEN 0xaa
- { "MaxHeadersLength", O_MAXHDRSLEN, FALSE },
-#endif
- { NULL, '\0', FALSE }
+ { "MaxHeadersLength", O_MAXHDRSLEN, OI_NONE },
+#if _FFR_MAX_FORWARD_ENTRIES
+# define O_MAXFORWARD 0xab
+ { "MaxForwardEntries", O_MAXFORWARD, OI_NONE },
+#endif /* _FFR_MAX_FORWARD_ENTRIES */
+#define O_PROCTITLEPREFIX 0xac
+ { "ProcessTitlePrefix", O_PROCTITLEPREFIX, OI_NONE },
+#define O_SASLINFO 0xad
+#if _FFR_ALLOW_SASLINFO
+ { "DefaultAuthInfo", O_SASLINFO, OI_SAFE },
+#else /* _FFR_ALLOW_SASLINFO */
+ { "DefaultAuthInfo", O_SASLINFO, OI_NONE },
+#endif /* _FFR_ALLOW_SASLINFO */
+#define O_SASLMECH 0xae
+ { "AuthMechanisms", O_SASLMECH, OI_NONE },
+#define O_CLIENTPORT 0xaf
+ { "ClientPortOptions", O_CLIENTPORT, OI_NONE },
+#define O_DF_BUFSIZE 0xb0
+ { "DataFileBufferSize", O_DF_BUFSIZE, OI_NONE },
+#define O_XF_BUFSIZE 0xb1
+ { "XscriptFileBufferSize", O_XF_BUFSIZE, OI_NONE },
+# define O_LDAPDEFAULTSPEC 0xb2
+ { "LDAPDefaultSpec", O_LDAPDEFAULTSPEC, OI_NONE },
+#if _FFR_QUEUEDELAY
+#define O_QUEUEDELAY 0xb3
+ { "QueueDelay", O_QUEUEDELAY, OI_NONE },
+#endif /* _FFR_QUEUEDELAY */
+# define O_SRVCERTFILE 0xb4
+ { "ServerCertFile", O_SRVCERTFILE, OI_NONE },
+# define O_SRVKEYFILE 0xb5
+ { "Serverkeyfile", O_SRVKEYFILE, OI_NONE },
+# define O_CLTCERTFILE 0xb6
+ { "ClientCertFile", O_CLTCERTFILE, OI_NONE },
+# define O_CLTKEYFILE 0xb7
+ { "Clientkeyfile", O_CLTKEYFILE, OI_NONE },
+# define O_CACERTFILE 0xb8
+ { "CACERTFile", O_CACERTFILE, OI_NONE },
+# define O_CACERTPATH 0xb9
+ { "CACERTPath", O_CACERTPATH, OI_NONE },
+# define O_DHPARAMS 0xba
+ { "DHParameters", O_DHPARAMS, OI_NONE },
+#if _FFR_MILTER
+#define O_INPUTMILTER 0xbb
+ { "InputMailFilters", O_INPUTMILTER, OI_NONE },
+#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
+# define O_DHPARAMS5 0xbf
+ { "DHParameters512", O_DHPARAMS5, OI_NONE },
+# define O_CIPHERLIST 0xc0
+ { "CipherList", O_CIPHERLIST, OI_NONE },
+# endif /* _FFR_TLS_1 */
+# define O_RANDFILE 0xc1
+ { "RandFile", O_RANDFILE, OI_NONE },
+ { NULL, '\0', OI_NONE }
};
-
-
void
setoption(opt, val, safe, sticky, e)
int opt;
@@ -1551,15 +1727,10 @@ setoption(opt, val, safe, sticky, e)
bool can_setuid = RunAsUid == 0;
auto char *ep;
char buf[50];
- extern bool atobool __P((char *));
- extern time_t convtime __P((char *, char));
- extern int QueueLA;
- extern int RefuseLA;
extern bool Warn_Q_option;
- extern void setalias __P((char *));
- extern int atooct __P((char *));
- extern void setdefuser __P((void));
- extern void setdaemonoptions __P((char *));
+#if _FFR_ALLOW_SASLINFO
+ extern int SubmitMode;
+#endif /* _FFR_ALLOW_SASLINFO */
errno = 0;
if (opt == ' ')
@@ -1636,13 +1807,23 @@ setoption(opt, val, safe, sticky, e)
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);
+ subopt = NULL;
+ }
+
if (tTd(37, 1))
{
- printf(isascii(opt) && isprint(opt) ?
- "setoption %s (%c).%s=" :
- "setoption %s (0x%x).%s=",
+ 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);
xputs(val);
}
@@ -1654,7 +1835,7 @@ setoption(opt, val, safe, sticky, e)
if (!sticky && bitnset(opt, StickyOpt))
{
if (tTd(37, 1))
- printf(" (ignored)\n");
+ dprintf(" (ignored)\n");
return;
}
@@ -1664,17 +1845,20 @@ setoption(opt, val, safe, sticky, e)
if (!safe && RealUid == 0)
safe = TRUE;
- if (!safe && !o->o_safe)
+ if (!safe && !bitset(OI_SAFE, o->o_flags))
{
if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
{
+ int dp;
+
if (tTd(37, 1))
- printf(" (unsafe)");
- (void) drop_privileges(TRUE);
+ dprintf(" (unsafe)");
+ dp = drop_privileges(TRUE);
+ setstat(dp);
}
}
if (tTd(37, 1))
- printf("\n");
+ dprintf("\n");
switch (opt & 0xff)
{
@@ -1698,7 +1882,7 @@ setoption(opt, val, safe, sticky, e)
MimeMode = MM_CVTMIME;
break;
-#if 0
+# if 0
case 'r': /* reject 8-bit, don't convert MIME */
MimeMode = 0;
break;
@@ -1714,14 +1898,14 @@ setoption(opt, val, safe, sticky, e)
case 'c': /* convert 8 bit to MIME, never 7 bit */
MimeMode = MM_MIME8BIT;
break;
-#endif
+# endif /* 0 */
default:
syserr("Unknown 8-bit mode %c", *val);
finis(FALSE, EX_USAGE);
}
break;
-#endif
+#endif /* MIME8TO7 */
case 'A': /* set default alias file */
if (val[0] == '\0')
@@ -1765,33 +1949,32 @@ setoption(opt, val, safe, sticky, e)
switch (*val)
{
case '\0':
- e->e_sendmode = SM_DELIVER;
+ set_delivery_mode(SM_DELIVER, e);
break;
case SM_QUEUE: /* queue only */
case SM_DEFER: /* queue only and defer map lookups */
#if !QUEUE
syserr("need QUEUE to set -odqueue or -oddefer");
-#endif /* QUEUE */
- /* fall through..... */
+#endif /* !QUEUE */
+ /* FALLTHROUGH */
case SM_DELIVER: /* do everything */
case SM_FORK: /* fork after verification */
- e->e_sendmode = *val;
+ set_delivery_mode(*val, e);
break;
default:
syserr("Unknown delivery mode %c", *val);
finis(FALSE, EX_USAGE);
}
- buf[0] = (char)e->e_sendmode;
- buf[1] = '\0';
- define(macid("{deliveryMode}", NULL), newstr(buf), e);
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')
@@ -1843,7 +2026,7 @@ setoption(opt, val, safe, sticky, e)
case 'H': /* help file */
if (val[0] == '\0')
- HelpFile = "sendmail.hf";
+ HelpFile = "helpfile";
else
HelpFile = newstr(val);
break;
@@ -1893,11 +2076,11 @@ setoption(opt, val, safe, sticky, e)
_res.options |= rfp->rf_bits;
}
if (tTd(8, 2))
- printf("_res.options = %x, HasWildcardMX = %d\n",
+ dprintf("_res.options = %x, HasWildcardMX = %d\n",
(u_int) _res.options, HasWildcardMX);
-#else
+#else /* NAMED_BIND */
usrerr("name server (I option) specified but BIND not compiled in");
-#endif
+#endif /* NAMED_BIND */
break;
case 'i': /* ignore dot lines in message */
@@ -1952,10 +2135,13 @@ setoption(opt, val, safe, sticky, e)
case 'O': /* daemon options */
#if DAEMON
- setdaemonoptions(val);
-#else
+ if (!setdaemonoptions(val))
+ {
+ syserr("too many daemons defined (%d max)", MAXDAEMONS);
+ }
+#else /* DAEMON */
syserr("DaemonPortOptions (O option) set but DAEMON not compiled in");
-#endif
+#endif /* DAEMON */
break;
case 'o': /* assume old style headers */
@@ -2017,14 +2203,14 @@ setoption(opt, val, safe, sticky, e)
case 'r': /* read timeout */
if (subopt == NULL)
- inittimeouts(val);
+ inittimeouts(val, sticky);
else
- settimeout(subopt, val);
+ settimeout(subopt, val, sticky);
break;
case 'S': /* status file */
if (val[0] == '\0')
- StatFile = "sendmail.st";
+ StatFile = "statistics";
else
StatFile = newstr(val);
break;
@@ -2038,9 +2224,9 @@ setoption(opt, val, safe, sticky, e)
if (p != NULL)
{
*p++ = '\0';
- settimeout("queuewarn", p);
+ settimeout("queuewarn", p, sticky);
}
- settimeout("queuereturn", val);
+ settimeout("queuereturn", val, sticky);
break;
case 't': /* time zone name */
@@ -2087,7 +2273,7 @@ setoption(opt, val, safe, sticky, e)
syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored",
DefUid, UID_MAX);
}
-#endif
+#endif /* UID_MAX */
/* handle the group if it is there */
if (*p == '\0')
@@ -2134,22 +2320,28 @@ setoption(opt, val, safe, sticky, e)
WkTimeFact = atoi(val);
break;
+
case O_QUEUESORTORD: /* queue sorting order */
switch (*val)
{
case 'h': /* Host first */
case 'H':
- QueueSortOrder = QS_BYHOST;
+ QueueSortOrder = QSO_BYHOST;
break;
case 'p': /* Priority order */
case 'P':
- QueueSortOrder = QS_BYPRIORITY;
+ QueueSortOrder = QSO_BYPRIORITY;
break;
case 't': /* Submission time */
case 'T':
- QueueSortOrder = QS_BYTIME;
+ QueueSortOrder = QSO_BYTIME;
+ break;
+
+ case 'f': /* File Name */
+ case 'F':
+ QueueSortOrder = QSO_BYFILENAME;
break;
default:
@@ -2157,6 +2349,43 @@ setoption(opt, val, safe, sticky, e)
}
break;
+#if _FFR_QUEUEDELAY
+ case O_QUEUEDELAY: /* queue delay algorithm */
+ switch (*val)
+ {
+ case 'e': /* exponential */
+ case 'E':
+ QueueAlg = QD_EXP;
+ QueueInitDelay = 10 MINUTES;
+ QueueMaxDelay = 2 HOURS;
+ p = strchr(val, '/');
+ if (p != NULL)
+ {
+ char *q;
+
+ *p++ = '\0';
+ q = strchr(p, '/');
+ if (q != NULL)
+ *q++ = '\0';
+ QueueInitDelay = convtime(p, 's');
+ if (q != NULL)
+ {
+ QueueMaxDelay = convtime(q, 's');
+ }
+ }
+ break;
+
+ case 'l': /* linear */
+ case 'L':
+ QueueAlg = QD_LINEAR;
+ break;
+
+ default:
+ syserr("Invalid queue delay algorithm \"%s\"", val);
+ }
+ break;
+#endif /* _FFR_QUEUEDELAY */
+
case O_HOSTSFILE: /* pathname of /etc/hosts file */
HostsFile = newstr(val);
break;
@@ -2212,14 +2441,22 @@ setoption(opt, val, safe, sticky, e)
MaxChildren = atoi(val);
break;
+#if _FFR_MAX_FORWARD_ENTRIES
+ case O_MAXFORWARD: /* max # of forward entries */
+ MaxForwardEntries = atoi(val);
+ break;
+#endif /* _FFR_MAX_FORWARD_ENTRIES */
+
case O_KEEPCNAMES: /* don't expand CNAME records */
DontExpandCnames = atobool(val);
break;
case O_MUSTQUOTE: /* must quote these characters in phrases */
- strcpy(buf, "@,;:\\()[]");
+ (void) strlcpy(buf, "@,;:\\()[]", sizeof buf);
if (strlen(val) < (SIZE_T) sizeof buf - 10)
- strcat(buf, val);
+ (void) strlcat(buf, val, sizeof buf);
+ else
+ printf("Warning: MustQuoteChars too long, ignored.\n");
MustQuoteChars = newstr(buf);
break;
@@ -2232,6 +2469,8 @@ setoption(opt, val, safe, sticky, e)
break;
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");
OperatorChars = newstr(munchstring(val, NULL, '\0'));
break;
@@ -2253,7 +2492,12 @@ setoption(opt, val, safe, sticky, e)
case O_UGW: /* group writable files are unsafe */
if (!atobool(val))
- DontBlameSendmail |= DBS_GROUPWRITABLEFORWARDFILESAFE|DBS_GROUPWRITABLEINCLUDEFILESAFE;
+ {
+ setbitn(DBS_GROUPWRITABLEFORWARDFILESAFE,
+ DontBlameSendmail);
+ setbitn(DBS_GROUPWRITABLEINCLUDEFILESAFE,
+ DontBlameSendmail);
+ }
break;
case O_DBLBOUNCE: /* address to which to send double bounces */
@@ -2307,7 +2551,7 @@ setoption(opt, val, safe, sticky, e)
syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored",
RunAsUid, UID_MAX);
}
-#endif
+#endif /* UID_MAX */
if (*p != '\0')
{
if (isascii(*p) && isdigit(*p))
@@ -2318,7 +2562,7 @@ setoption(opt, val, safe, sticky, e)
else
{
register struct group *gr;
-
+
gr = getgrnam(p);
if (gr == NULL)
syserr("readcf: option RunAsUser: unknown group %s",
@@ -2328,21 +2572,19 @@ setoption(opt, val, safe, sticky, e)
}
}
if (tTd(47, 5))
- printf("readcf: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
+ dprintf("readcf: RunAsUser = %d:%d\n",
+ (int)RunAsUid, (int)RunAsGid);
break;
-#if _FFR_DSN_RRT_OPTION
case O_DSN_RRT:
RrtImpliesDsn = atobool(val);
break;
-#endif
-#if _FFR_PIDFILE_OPTION
case O_PIDFILE:
- free(PidFile);
+ if (PidFile != NULL)
+ free(PidFile);
PidFile = newstr(val);
break;
-#endif
case O_DONTBLAMESENDMAIL:
p = val;
@@ -2370,9 +2612,9 @@ setoption(opt, val, safe, sticky, e)
if (dbs->dbs_name == NULL)
syserr("readcf: DontBlameSendmail option: %s unrecognized", val);
else if (dbs->dbs_flag == DBS_SAFE)
- DontBlameSendmail = DBS_SAFE;
+ clrbitmap(DontBlameSendmail);
else
- DontBlameSendmail |= dbs->dbs_flag;
+ setbitn(dbs->dbs_flag, DontBlameSendmail);
}
sticky = FALSE;
break;
@@ -2385,35 +2627,45 @@ setoption(opt, val, safe, sticky, e)
MaxRcptPerMsg = atoi(val);
break;
-#if _FFR_DEADLETTERDROP_OPTION
case O_DEADLETTER:
if (DeadLetterDrop != NULL)
free(DeadLetterDrop);
DeadLetterDrop = newstr(val);
break;
-#endif
#if _FFR_DONTLOCKFILESFORREAD_OPTION
case O_DONTLOCK:
DontLockReadFiles = atobool(val);
break;
-#endif
+#endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
-#if _FFR_MAXALIASRECURSION_OPTION
case O_MAXALIASRCSN:
MaxAliasRecursion = atoi(val);
break;
-#endif
-#if _FFR_CONNECTONLYTO_OPTION
case O_CNCTONLYTO:
/* XXX should probably use gethostbyname */
- ConnectOnlyTo = inet_addr(val);
+#if NETINET || NETINET6
+# if NETINET6
+ if (inet_addr(val) == INADDR_NONE)
+ {
+ 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 */
+ {
+ ConnectOnlyTo.sa.sa_family = AF_INET;
+ ConnectOnlyTo.sin.sin_addr.s_addr = inet_addr(val);
+ }
+#endif /* NETINET || NETINET6 */
break;
-#endif
-#if _FFR_TRUSTED_USER
case O_TRUSTUSER:
+#if HASFCHOWN
if (isascii(*val) && isdigit(*val))
TrustedUid = atoi(val);
else
@@ -2428,18 +2680,19 @@ setoption(opt, val, safe, sticky, e)
TrustedUid = pw->pw_uid;
}
-#ifdef UID_MAX
+# ifdef UID_MAX
if (TrustedUid > UID_MAX)
{
syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)",
TrustedUid, UID_MAX);
TrustedUid = 0;
}
-#endif
+# endif /* UID_MAX */
+#else /* HASFCHOWN */
+ syserr("readcf: option TrustedUser: can not be used on systems which do not support fchown()");
+#endif /* HASFCHOWN */
break;
-#endif
-#if _FFR_MAX_MIME_HEADER_LENGTH
case O_MAXMIMEHDRLEN:
p = strchr(val, '/');
if (p != NULL)
@@ -2460,37 +2713,257 @@ setoption(opt, val, safe, sticky, e)
else if (MaxMimeFieldLength < 40)
printf("Warning: MaxMimeHeaderLength: field length limit set lower than 40\n");
break;
-#endif
-#if _FFR_CONTROL_SOCKET
case O_CONTROLSOCKET:
if (ControlSocketName != NULL)
free(ControlSocketName);
ControlSocketName = newstr(val);
break;
-#endif
-#if _FFR_MAX_HEADERS_LENGTH
case O_MAXHDRSLEN:
MaxHeadersLength = atoi(val);
if (MaxHeadersLength > 0 &&
MaxHeadersLength < (MAXHDRSLEN / 2))
- printf("Warning: MaxHeadersLength: headers length limit set lower than %d\n", MAXHDRSLEN);
+ printf("Warning: MaxHeadersLength: headers length limit set lower than %d\n", (MAXHDRSLEN / 2));
+ break;
+
+ case O_PROCTITLEPREFIX:
+ if (ProcTitlePrefix != NULL)
+ free(ProcTitlePrefix);
+ ProcTitlePrefix = newstr(val);
+ break;
+
+#if SASL
+ case O_SASLINFO:
+#if _FFR_ALLOW_SASLINFO
+ /*
+ ** Allow users to select their own authinfo file.
+ ** However, this is not a "perfect" solution.
+ ** If mail is queued, the authentication info
+ ** will not be used in subsequent delivery attempts.
+ ** If we really want to support this, then it has
+ ** to be stored in the queue file.
+ */
+ 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)
+ free(SASLInfo);
+ SASLInfo = newstr(val);
+ break;
+
+ case O_SASLMECH:
+ if (AuthMechanisms != NULL)
+ free(AuthMechanisms);
+ if (*val != '\0')
+ AuthMechanisms = newstr(val);
+ else
+ AuthMechanisms = NULL;
+ break;
+
+ case O_SASLOPTS:
+ while (val != NULL && *val != '\0')
+ {
+ switch(*val)
+ {
+ case 'A':
+ SASLOpts |= SASL_AUTH_AUTH;
+ break;
+# if _FFR_SASL_OPTS
+ case 'a':
+ SASLOpts |= SASL_SEC_NOACTIVE;
+ break;
+ case 'c':
+ SASLOpts |= SASL_SEC_PASS_CREDENTIALS;
+ break;
+ case 'd':
+ SASLOpts |= SASL_SEC_NODICTIONARY;
+ break;
+ case 'f':
+ SASLOpts |= SASL_SEC_FORWARD_SECRECY;
+ break;
+ case 'p':
+ SASLOpts |= SASL_SEC_NOPLAINTEXT;
+ break;
+ case 'y':
+ SASLOpts |= SASL_SEC_NOANONYMOUS;
+ break;
+# endif /* _FFR_SASL_OPTS */
+ default:
+ printf("Warning: Option: %s unknown parameter '%c'\n",
+ o->o_name == NULL ? "<unknown>"
+ : o->o_name,
+ (isascii(*val) && isprint(*val)) ? *val
+ : '?');
+ break;
+ }
+ ++val;
+ val = strpbrk(val, ", \t");
+ if (val != NULL)
+ ++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);
+ break;
+#endif /* SASL */
+
+#if STARTTLS
+ case O_SRVCERTFILE:
+ if (SrvCERTfile != NULL)
+ free(SrvCERTfile);
+ SrvCERTfile = newstr(val);
+ break;
+
+ case O_SRVKEYFILE:
+ if (Srvkeyfile != NULL)
+ free(Srvkeyfile);
+ Srvkeyfile = newstr(val);
+ break;
+
+ case O_CLTCERTFILE:
+ if (CltCERTfile != NULL)
+ free(CltCERTfile);
+ CltCERTfile = newstr(val);
+ break;
+
+ case O_CLTKEYFILE:
+ if (Cltkeyfile != NULL)
+ free(Cltkeyfile);
+ Cltkeyfile = newstr(val);
+ break;
+
+ case O_CACERTFILE:
+ if (CACERTfile != NULL)
+ free(CACERTfile);
+ CACERTfile = newstr(val);
+ break;
+
+ case O_CACERTPATH:
+ if (CACERTpath != NULL)
+ free(CACERTpath);
+ CACERTpath = newstr(val);
+ break;
+
+ case O_DHPARAMS:
+ if (DHParams != NULL)
+ free(DHParams);
+ DHParams = newstr(val);
+ break;
+
+# if _FFR_TLS_1
+ case O_DHPARAMS5:
+ if (DHParams5 != NULL)
+ free(DHParams5);
+ DHParams5 = newstr(val);
+ break;
+
+ case O_CIPHERLIST:
+ if (CipherList != NULL)
+ free(CipherList);
+ CipherList = newstr(val);
+ break;
+# endif /* _FFR_TLS_1 */
+
+ case O_RANDFILE:
+ if (RandFile != NULL)
+ free(RandFile);
+ RandFile= newstr(val);
+ break;
+
+# else /* STARTTLS */
+ case O_SRVCERTFILE:
+ case O_SRVKEYFILE:
+ case O_CLTCERTFILE:
+ case O_CLTKEYFILE:
+ case O_CACERTFILE:
+ case O_CACERTPATH:
+ case O_DHPARAMS:
+# if _FFR_TLS_1
+ case O_DHPARAMS5:
+ case O_CIPHERLIST:
+# endif /* _FFR_TLS_1 */
+ case O_RANDFILE:
+ printf("Warning: Option: %s requires TLS support\n",
+ o->o_name == NULL ? "<unknown>" : o->o_name);
+ break;
+
+# endif /* STARTTLS */
+
+ case O_CLIENTPORT:
+#if DAEMON
+ setclientoptions(val);
+#else /* DAEMON */
+ syserr("ClientPortOptions (O option) set but DAEMON not compiled in");
+#endif /* DAEMON */
+ break;
+
+ case O_DF_BUFSIZE:
+ DataFileBufferSize = atoi(val);
break;
-#endif
+
+ case O_XF_BUFSIZE:
+ XscriptFileBufferSize = atoi(val);
+ break;
+
+ case O_LDAPDEFAULTSPEC:
+#ifdef LDAPMAP
+ ldapmap_set_defaults(val);
+#else /* LDAPMAP */
+ printf("Warning: Option: %s requires LDAP support (-DLDAPMAP)\n",
+ o->o_name == NULL ? "<unknown>" : o->o_name);
+#endif /* LDAPMAP */
+ break;
+
+#if _FFR_MILTER
+ case O_INPUTMILTER:
+ InputFilterList = newstr(val);
+ break;
+
+ case O_MILTER:
+ milter_set_option(subopt, val, sticky);
+ 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 */
default:
if (tTd(37, 1))
{
if (isascii(opt) && isprint(opt))
- printf("Warning: option %c unknown\n", opt);
+ dprintf("Warning: option %c unknown\n", opt);
else
- printf("Warning: option 0x%x unknown\n", opt);
+ dprintf("Warning: option 0x%x unknown\n", opt);
}
break;
}
- if (sticky)
+
+ /*
+ ** Options with suboptions are responsible for taking care
+ ** of sticky-ness (e.g., that a command line setting is kept
+ ** when reading in the sendmail.cf file). This has to be done
+ ** when the suboptions are parsed since each suboption must be
+ ** sticky, not the root option.
+ */
+
+ if (sticky && !bitset(OI_SUBOPT, o->o_flags))
setbitn(opt, StickyOpt);
}
/*
@@ -2514,10 +2987,28 @@ setclass(class, str)
{
register STAB *s;
- if (tTd(37, 8))
- printf("setclass(%s, %s)\n", macname(class), str);
- s = stab(str, ST_CLASS, ST_ENTER);
- setbitn(class, s->s_class);
+ if ((*str & 0377) == MATCHCLASS)
+ {
+ int mid;
+
+ str++;
+ mid = macid(str, NULL);
+ if (mid == '\0')
+ return;
+
+ if (tTd(37, 8))
+ 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);
+
+ s = stab(str, ST_CLASS, ST_ENTER);
+ setbitn(class, s->s_class);
+ }
}
/*
** MAKEMAPENTRY -- create a map entry
@@ -2589,11 +3080,11 @@ makemapentry(line)
if (tTd(37, 5))
{
- printf("map %s, class %s, flags %lx, file %s,\n",
+ 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);
- printf("\tapp %s, domain %s, rebuild %s\n",
+ 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);
@@ -2645,7 +3136,7 @@ strtorwset(p, endp, stabmode)
{
STAB *s;
char delim;
- char *q;
+ char *q = NULL;
q = p;
while (*p != '\0' && isascii(*p) &&
@@ -2693,7 +3184,7 @@ strtorwset(p, endp, stabmode)
{
if (endp != NULL)
*endp = p;
- if (s->s_ruleset > 0)
+ if (s->s_ruleset >= 0)
ruleset = s->s_ruleset;
else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
{
@@ -2702,25 +3193,322 @@ strtorwset(p, endp, stabmode)
ruleset = -1;
}
}
- if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset)
+ if (s->s_ruleset >= 0 &&
+ ruleset >= 0 &&
+ ruleset != s->s_ruleset)
{
syserr("%s: ruleset changed value (old %d, new %d)",
q, s->s_ruleset, ruleset);
ruleset = s->s_ruleset;
}
- else if (ruleset > 0)
+ else if (ruleset >= 0)
{
s->s_ruleset = ruleset;
}
+ if (stabmode == ST_ENTER)
+ {
+ char *h = NULL;
+
+ if (RuleSetNames[ruleset] != NULL)
+ free(RuleSetNames[ruleset]);
+ if (delim != '\0' && (h = strchr(q, delim)) != NULL)
+ *h = '\0';
+ RuleSetNames[ruleset] = newstr(q);
+ if (delim == '/' && h != NULL)
+ *h = delim; /* put back delim */
+ }
}
return ruleset;
}
/*
+** SETTIMEOUT -- set an individual timeout
+**
+** Parameters:
+** name -- the name of the timeout.
+** val -- the value of the timeout.
+** sticky -- if set, don't let other setoptions override
+** this value.
+**
+** Returns:
+** none.
+*/
+
+/* set if Timeout sub-option is stuck */
+static BITMAP256 StickyTimeoutOpt;
+
+static struct timeoutinfo
+{
+ char *to_name; /* long name of timeout */
+ u_char to_code; /* code for option */
+} TimeOutTab[] =
+{
+#define TO_INITIAL 0x01
+ { "initial", TO_INITIAL },
+#define TO_MAIL 0x02
+ { "mail", TO_MAIL },
+#define TO_RCPT 0x03
+ { "rcpt", TO_RCPT },
+#define TO_DATAINIT 0x04
+ { "datainit", TO_DATAINIT },
+#define TO_DATABLOCK 0x05
+ { "datablock", TO_DATABLOCK },
+#define TO_DATAFINAL 0x06
+ { "datafinal", TO_DATAFINAL },
+#define TO_COMMAND 0x07
+ { "command", TO_COMMAND },
+#define TO_RSET 0x08
+ { "rset", TO_RSET },
+#define TO_HELO 0x09
+ { "helo", TO_HELO },
+#define TO_QUIT 0x0A
+ { "quit", TO_QUIT },
+#define TO_MISC 0x0B
+ { "misc", TO_MISC },
+#define TO_IDENT 0x0C
+ { "ident", TO_IDENT },
+#define TO_FILEOPEN 0x0D
+ { "fileopen", TO_FILEOPEN },
+#define TO_CONNECT 0x0E
+ { "connect", TO_CONNECT },
+#define TO_ICONNECT 0x0F
+ { "iconnect", TO_ICONNECT },
+#define TO_QUEUEWARN 0x10
+ { "queuewarn", TO_QUEUEWARN },
+ { "queuewarn.*", TO_QUEUEWARN },
+#define TO_QUEUEWARN_NORMAL 0x11
+ { "queuewarn.normal", TO_QUEUEWARN_NORMAL },
+#define TO_QUEUEWARN_URGENT 0x12
+ { "queuewarn.urgent", TO_QUEUEWARN_URGENT },
+#define TO_QUEUEWARN_NON_URGENT 0x13
+ { "queuewarn.non-urgent", TO_QUEUEWARN_NON_URGENT },
+#define TO_QUEUERETURN 0x14
+ { "queuereturn", TO_QUEUERETURN },
+ { "queuereturn.*", TO_QUEUERETURN },
+#define TO_QUEUERETURN_NORMAL 0x15
+ { "queuereturn.normal", TO_QUEUERETURN_NORMAL },
+#define TO_QUEUERETURN_URGENT 0x16
+ { "queuereturn.urgent", TO_QUEUERETURN_URGENT },
+#define TO_QUEUERETURN_NON_URGENT 0x17
+ { "queuereturn.non-urgent", TO_QUEUERETURN_NON_URGENT },
+#define TO_HOSTSTATUS 0x18
+ { "hoststatus", TO_HOSTSTATUS },
+#define TO_RESOLVER_RETRANS 0x19
+ { "resolver.retrans", TO_RESOLVER_RETRANS },
+#define TO_RESOLVER_RETRANS_NORMAL 0x1A
+ { "resolver.retrans.normal", TO_RESOLVER_RETRANS_NORMAL },
+#define TO_RESOLVER_RETRANS_FIRST 0x1B
+ { "resolver.retrans.first", TO_RESOLVER_RETRANS_FIRST },
+#define TO_RESOLVER_RETRY 0x1C
+ { "resolver.retry", TO_RESOLVER_RETRY },
+#define TO_RESOLVER_RETRY_NORMAL 0x1D
+ { "resolver.retry.normal", TO_RESOLVER_RETRY_NORMAL },
+#define TO_RESOLVER_RETRY_FIRST 0x1E
+ { "resolver.retry.first", TO_RESOLVER_RETRY_FIRST },
+#define TO_CONTROL 0x1F
+ { "control", TO_CONTROL },
+ { NULL, 0 },
+};
+
+
+static void
+settimeout(name, val, sticky)
+ char *name;
+ char *val;
+ bool sticky;
+{
+ register struct timeoutinfo *to;
+ int i;
+ time_t toval;
+
+ if (tTd(37, 2))
+ dprintf("settimeout(%s = %s)", name, val);
+
+ for (to = TimeOutTab; to->to_name != NULL; to++)
+ {
+ if (strcasecmp(to->to_name, name) == 0)
+ break;
+ }
+
+ if (to->to_name == NULL)
+ syserr("settimeout: invalid timeout %s", name);
+
+ /*
+ ** See if this option is preset for us.
+ */
+
+ if (!sticky && bitnset(to->to_code, StickyTimeoutOpt))
+ {
+ if (tTd(37, 2))
+ dprintf(" (ignored)\n");
+ return;
+ }
+
+ if (tTd(37, 2))
+ dprintf("\n");
+
+ toval = convtime(val, 'm');
+
+ switch (to->to_code)
+ {
+ case TO_INITIAL:
+ TimeOuts.to_initial = toval;
+ break;
+
+ case TO_MAIL:
+ TimeOuts.to_mail = toval;
+ break;
+
+ case TO_RCPT:
+ TimeOuts.to_rcpt = toval;
+ break;
+
+ case TO_DATAINIT:
+ TimeOuts.to_datainit = toval;
+ break;
+
+ case TO_DATABLOCK:
+ TimeOuts.to_datablock = toval;
+ break;
+
+ case TO_DATAFINAL:
+ TimeOuts.to_datafinal = toval;
+ break;
+
+ case TO_COMMAND:
+ TimeOuts.to_nextcommand = toval;
+ break;
+
+ case TO_RSET:
+ TimeOuts.to_rset = toval;
+ break;
+
+ case TO_HELO:
+ TimeOuts.to_helo = toval;
+ break;
+
+ case TO_QUIT:
+ TimeOuts.to_quit = toval;
+ break;
+
+ case TO_MISC:
+ TimeOuts.to_miscshort = toval;
+ break;
+
+ case TO_IDENT:
+ TimeOuts.to_ident = toval;
+ break;
+
+ case TO_FILEOPEN:
+ TimeOuts.to_fileopen = toval;
+ break;
+
+ case TO_CONNECT:
+ TimeOuts.to_connect = toval;
+ break;
+
+ case TO_ICONNECT:
+ TimeOuts.to_iconnect = toval;
+ break;
+
+ case TO_QUEUEWARN:
+ toval = convtime(val, 'h');
+ TimeOuts.to_q_warning[TOC_NORMAL] = toval;
+ TimeOuts.to_q_warning[TOC_URGENT] = toval;
+ TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
+ break;
+
+ case TO_QUEUEWARN_NORMAL:
+ toval = convtime(val, 'h');
+ TimeOuts.to_q_warning[TOC_NORMAL] = toval;
+ break;
+
+ case TO_QUEUEWARN_URGENT:
+ toval = convtime(val, 'h');
+ TimeOuts.to_q_warning[TOC_URGENT] = toval;
+ break;
+
+ case TO_QUEUEWARN_NON_URGENT:
+ toval = convtime(val, 'h');
+ TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
+ break;
+
+ case TO_QUEUERETURN:
+ toval = convtime(val, 'd');
+ TimeOuts.to_q_return[TOC_NORMAL] = toval;
+ TimeOuts.to_q_return[TOC_URGENT] = toval;
+ TimeOuts.to_q_return[TOC_NONURGENT] = toval;
+ break;
+
+ case TO_QUEUERETURN_NORMAL:
+ toval = convtime(val, 'd');
+ TimeOuts.to_q_return[TOC_NORMAL] = toval;
+ break;
+
+ case TO_QUEUERETURN_URGENT:
+ toval = convtime(val, 'd');
+ TimeOuts.to_q_return[TOC_URGENT] = toval;
+ break;
+
+ case TO_QUEUERETURN_NON_URGENT:
+ toval = convtime(val, 'd');
+ TimeOuts.to_q_return[TOC_NONURGENT] = toval;
+ break;
+
+
+ case TO_HOSTSTATUS:
+ MciInfoTimeout = toval;
+ break;
+
+ case TO_RESOLVER_RETRANS:
+ toval = convtime(val, 's');
+ TimeOuts.res_retrans[RES_TO_DEFAULT] = toval;
+ TimeOuts.res_retrans[RES_TO_FIRST] = toval;
+ TimeOuts.res_retrans[RES_TO_NORMAL] = toval;
+ break;
+
+ case TO_RESOLVER_RETRY:
+ i = atoi(val);
+ TimeOuts.res_retry[RES_TO_DEFAULT] = i;
+ TimeOuts.res_retry[RES_TO_FIRST] = i;
+ TimeOuts.res_retry[RES_TO_NORMAL] = i;
+ break;
+
+ case TO_RESOLVER_RETRANS_NORMAL:
+ TimeOuts.res_retrans[RES_TO_NORMAL] = convtime(val, 's');
+ break;
+
+ case TO_RESOLVER_RETRY_NORMAL:
+ TimeOuts.res_retry[RES_TO_NORMAL] = atoi(val);
+ break;
+
+ case TO_RESOLVER_RETRANS_FIRST:
+ TimeOuts.res_retrans[RES_TO_FIRST] = convtime(val, 's');
+ break;
+
+ case TO_RESOLVER_RETRY_FIRST:
+ TimeOuts.res_retry[RES_TO_FIRST] = atoi(val);
+ break;
+
+ case TO_CONTROL:
+ TimeOuts.to_control = toval;
+ break;
+
+ default:
+ syserr("settimeout: invalid timeout %s", name);
+ break;
+ }
+
+ if (sticky)
+ setbitn(to->to_code, StickyTimeoutOpt);
+}
+ /*
** INITTIMEOUTS -- parse and set timeout values
**
** Parameters:
** val -- a pointer to the values. If NULL, do initial
** settings.
+** sticky -- if set, don't let other setoptions override
+** this suboption value.
**
** Returns:
** none.
@@ -2729,19 +3517,15 @@ strtorwset(p, endp, stabmode)
** Initializes the TimeOuts structure
*/
-#define SECONDS
-#define MINUTES * 60
-#define HOUR * 3600
-
void
-inittimeouts(val)
+inittimeouts(val, sticky)
register char *val;
+ bool sticky;
{
register char *p;
- extern time_t convtime __P((char *, char));
if (tTd(37, 2))
- printf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val);
+ dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val);
if (val == NULL)
{
TimeOuts.to_connect = (time_t) 0 SECONDS;
@@ -2757,28 +3541,30 @@ inittimeouts(val)
TimeOuts.to_nextcommand = (time_t) 1 HOUR;
TimeOuts.to_miscshort = (time_t) 2 MINUTES;
#if IDENTPROTO
- TimeOuts.to_ident = (time_t) 30 SECONDS;
-#else
+ TimeOuts.to_ident = (time_t) 5 SECONDS;
+#else /* IDENTPROTO */
TimeOuts.to_ident = (time_t) 0 SECONDS;
-#endif
+#endif /* IDENTPROTO */
TimeOuts.to_fileopen = (time_t) 60 SECONDS;
+ TimeOuts.to_control = (time_t) 2 MINUTES;
if (tTd(37, 5))
{
- printf("Timeouts:\n");
- printf(" connect = %ld\n", (long)TimeOuts.to_connect);
- printf(" initial = %ld\n", (long)TimeOuts.to_initial);
- printf(" helo = %ld\n", (long)TimeOuts.to_helo);
- printf(" mail = %ld\n", (long)TimeOuts.to_mail);
- printf(" rcpt = %ld\n", (long)TimeOuts.to_rcpt);
- printf(" datainit = %ld\n", (long)TimeOuts.to_datainit);
- printf(" datablock = %ld\n", (long)TimeOuts.to_datablock);
- printf(" datafinal = %ld\n", (long)TimeOuts.to_datafinal);
- printf(" rset = %ld\n", (long)TimeOuts.to_rset);
- printf(" quit = %ld\n", (long)TimeOuts.to_quit);
- printf(" nextcommand = %ld\n", (long)TimeOuts.to_nextcommand);
- printf(" miscshort = %ld\n", (long)TimeOuts.to_miscshort);
- printf(" ident = %ld\n", (long)TimeOuts.to_ident);
- printf(" fileopen = %ld\n", (long)TimeOuts.to_fileopen);
+ 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);
}
return;
}
@@ -2803,6 +3589,15 @@ inittimeouts(val)
TimeOuts.to_datablock = TimeOuts.to_mail;
TimeOuts.to_datafinal = TimeOuts.to_mail;
TimeOuts.to_nextcommand = TimeOuts.to_mail;
+ if (sticky)
+ {
+ setbitn(TO_MAIL, StickyTimeoutOpt);
+ setbitn(TO_RCPT, StickyTimeoutOpt);
+ setbitn(TO_DATAINIT, StickyTimeoutOpt);
+ setbitn(TO_DATABLOCK, StickyTimeoutOpt);
+ setbitn(TO_DATAFINAL, StickyTimeoutOpt);
+ setbitn(TO_COMMAND, StickyTimeoutOpt);
+ }
continue;
}
else
@@ -2815,106 +3610,7 @@ inittimeouts(val)
continue;
}
*q++ = '\0';
- settimeout(val, q);
+ settimeout(val, q, sticky);
}
}
}
- /*
-** SETTIMEOUT -- set an individual timeout
-**
-** Parameters:
-** name -- the name of the timeout.
-** val -- the value of the timeout.
-**
-** Returns:
-** none.
-*/
-
-void
-settimeout(name, val)
- char *name;
- char *val;
-{
- register char *p;
- time_t to;
- extern time_t convtime __P((char *, char));
-
- if (tTd(37, 2))
- printf("settimeout(%s = %s)\n", name, val);
-
- to = convtime(val, 'm');
- p = strchr(name, '.');
- if (p != NULL)
- *p++ = '\0';
-
- if (strcasecmp(name, "initial") == 0)
- TimeOuts.to_initial = to;
- else if (strcasecmp(name, "mail") == 0)
- TimeOuts.to_mail = to;
- else if (strcasecmp(name, "rcpt") == 0)
- TimeOuts.to_rcpt = to;
- else if (strcasecmp(name, "datainit") == 0)
- TimeOuts.to_datainit = to;
- else if (strcasecmp(name, "datablock") == 0)
- TimeOuts.to_datablock = to;
- else if (strcasecmp(name, "datafinal") == 0)
- TimeOuts.to_datafinal = to;
- else if (strcasecmp(name, "command") == 0)
- TimeOuts.to_nextcommand = to;
- else if (strcasecmp(name, "rset") == 0)
- TimeOuts.to_rset = to;
- else if (strcasecmp(name, "helo") == 0)
- TimeOuts.to_helo = to;
- else if (strcasecmp(name, "quit") == 0)
- TimeOuts.to_quit = to;
- else if (strcasecmp(name, "misc") == 0)
- TimeOuts.to_miscshort = to;
- else if (strcasecmp(name, "ident") == 0)
- TimeOuts.to_ident = to;
- else if (strcasecmp(name, "fileopen") == 0)
- TimeOuts.to_fileopen = to;
- else if (strcasecmp(name, "connect") == 0)
- TimeOuts.to_connect = to;
- else if (strcasecmp(name, "iconnect") == 0)
- TimeOuts.to_iconnect = to;
- else if (strcasecmp(name, "queuewarn") == 0)
- {
- to = convtime(val, 'h');
- if (p == NULL || strcmp(p, "*") == 0)
- {
- TimeOuts.to_q_warning[TOC_NORMAL] = to;
- TimeOuts.to_q_warning[TOC_URGENT] = to;
- TimeOuts.to_q_warning[TOC_NONURGENT] = to;
- }
- else if (strcasecmp(p, "normal") == 0)
- TimeOuts.to_q_warning[TOC_NORMAL] = to;
- else if (strcasecmp(p, "urgent") == 0)
- TimeOuts.to_q_warning[TOC_URGENT] = to;
- else if (strcasecmp(p, "non-urgent") == 0)
- TimeOuts.to_q_warning[TOC_NONURGENT] = to;
- else
- syserr("settimeout: invalid queuewarn subtimeout %s", p);
- }
- else if (strcasecmp(name, "queuereturn") == 0)
- {
- to = convtime(val, 'd');
- if (p == NULL || strcmp(p, "*") == 0)
- {
- TimeOuts.to_q_return[TOC_NORMAL] = to;
- TimeOuts.to_q_return[TOC_URGENT] = to;
- TimeOuts.to_q_return[TOC_NONURGENT] = to;
- }
- else if (strcasecmp(p, "normal") == 0)
- TimeOuts.to_q_return[TOC_NORMAL] = to;
- else if (strcasecmp(p, "urgent") == 0)
- TimeOuts.to_q_return[TOC_URGENT] = to;
- else if (strcasecmp(p, "non-urgent") == 0)
- TimeOuts.to_q_return[TOC_NONURGENT] = to;
- else
- syserr("settimeout: invalid queuereturn subtimeout %s", p);
- }
- else if (strcasecmp(name, "hoststatus") == 0)
- MciInfoTimeout = convtime(val, 'm');
- else
- syserr("settimeout: invalid timeout %s", name);
-}
diff --git a/contrib/sendmail/src/recipient.c b/contrib/sendmail/src/recipient.c
index f7e221f..3d6e632 100644
--- a/contrib/sendmail/src/recipient.c
+++ b/contrib/sendmail/src/recipient.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,11 +12,14 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)recipient.c 8.163 (Berkeley) 1/23/1999";
-#endif /* not lint */
+static char id[] = "@(#)$Id: recipient.c,v 8.231.14.5 2000/06/27 20:15:46 gshapiro Exp $";
+#endif /* ! lint */
-# include "sendmail.h"
-# include <grp.h>
+#include <sendmail.h>
+
+
+static void includetimeout __P((void));
+static ADDRESS *self_reference __P((ADDRESS *));
/*
** SENDTOLIST -- Designate a send list.
@@ -70,7 +74,7 @@ sendtolist(list, ctladdr, sendq, aliaslevel, e)
if (tTd(25, 1))
{
- printf("sendto: %s\n ctladdr=", list);
+ dprintf("sendto: %s\n ctladdr=", list);
printaddr(ctladdr, FALSE);
}
@@ -89,11 +93,17 @@ sendtolist(list, ctladdr, sendq, aliaslevel, e)
/* make sure we have enough space to copy the string */
i = strlen(list) + 1;
if (i <= sizeof buf)
+ {
bufp = buf;
+ i = sizeof buf;
+ }
else
bufp = xalloc(i);
- strcpy(bufp, denlstring(list, FALSE, TRUE));
+ (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;
@@ -113,37 +123,36 @@ sendtolist(list, ctladdr, sendq, aliaslevel, e)
if (ctladdr != NULL)
{
ADDRESS *b;
- extern ADDRESS *self_reference __P((ADDRESS *, ENVELOPE *));
/* self reference test */
if (sameaddr(ctladdr, a))
{
if (tTd(27, 5))
{
- printf("sendtolist: QSELFREF ");
+ dprintf("sendtolist: QSELFREF ");
printaddr(ctladdr, FALSE);
}
ctladdr->q_flags |= QSELFREF;
}
/* check for address loops */
- b = self_reference(a, e);
+ b = self_reference(a);
if (b != NULL)
{
b->q_flags |= QSELFREF;
if (tTd(27, 5))
{
- printf("sendtolist: QSELFREF ");
+ dprintf("sendtolist: QSELFREF ");
printaddr(b, FALSE);
}
if (a != b)
{
if (tTd(27, 5))
{
- printf("sendtolist: QDONTSEND ");
+ dprintf("sendtolist: QS_DONTSEND ");
printaddr(a, FALSE);
}
- a->q_flags |= QDONTSEND;
+ a->q_state = QS_DONTSEND;
b->q_flags |= a->q_flags & QNOTREMOTE;
continue;
}
@@ -177,7 +186,118 @@ sendtolist(list, ctladdr, sendq, aliaslevel, e)
e->e_to = oldto;
if (bufp != buf)
free(bufp);
- return (naddrs);
+#if _FFR_ADDR_TYPE
+ define(macid("{addr_type}", NULL), NULL, e);
+#endif /* _FFR_ADDR_TYPE */
+ return naddrs;
+}
+ /*
+** 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
+** expanded recipients as QS_REMOVED.
+**
+** Parameters:
+** list -- the list to remove.
+** sendq -- a pointer to the head of a queue to remove
+** these addresses from.
+** e -- the envelope in which to remove these recipients.
+**
+** Returns:
+** The number of addresses removed from the list.
+**
+*/
+
+int
+removefromlist(list, sendq, e)
+ char *list;
+ ADDRESS **sendq;
+ ENVELOPE *e;
+{
+ char delimiter; /* the address delimiter */
+ int naddrs;
+ int i;
+ char *p;
+ char *oldto = e->e_to;
+ char *bufp;
+ char buf[MAXNAME + 1];
+
+ if (list == NULL)
+ {
+ syserr("removefromlist: null list");
+ return 0;
+ }
+
+ if (tTd(25, 1))
+ dprintf("removefromlist: %s\n", list);
+
+ /* heuristic to determine old versus new style addresses */
+ if (strchr(list, ',') != NULL || strchr(list, ';') != NULL ||
+ strchr(list, '<') != NULL || strchr(list, '(') != NULL)
+ e->e_flags &= ~EF_OLDSTYLE;
+ delimiter = ' ';
+ if (!bitset(EF_OLDSTYLE, e->e_flags))
+ delimiter = ',';
+
+ naddrs = 0;
+
+ /* make sure we have enough space to copy the string */
+ i = strlen(list) + 1;
+ if (i <= sizeof buf)
+ {
+ bufp = buf;
+ 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)
+ {
+ 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))
+ {
+ if (tTd(25, 5))
+ {
+ dprintf("removefromlist: QS_REMOVED ");
+ printaddr(&a, FALSE);
+ }
+ q->q_state = QS_REMOVED;
+ naddrs++;
+ break;
+ }
+ }
+ }
+
+ e->e_to = oldto;
+ if (bufp != buf)
+ free(bufp);
+#if _FFR_ADDR_TYPE
+ define(macid("{addr_type}", NULL), NULL, e);
+#endif /* _FFR_ADDR_TYPE */
+ return naddrs;
}
/*
** RECIPIENT -- Designate a message recipient
@@ -187,7 +307,7 @@ sendtolist(list, ctladdr, sendq, aliaslevel, e)
** Parameters:
** a -- the (preparsed) address header for the recipient.
** sendq -- a pointer to the head of a queue to put the
-** recipient in. Duplicate supression is done
+** recipient in. Duplicate suppression is done
** in this queue.
** aliaslevel -- the current alias nesting depth.
** e -- the current envelope.
@@ -210,14 +330,13 @@ recipient(a, sendq, aliaslevel, e)
register ADDRESS *q;
ADDRESS **pq;
register struct mailer *m;
- register char *p;
+ register char *p = NULL;
bool quoted = FALSE; /* set if the addr has a quote bit */
int findusercount = 0;
- bool initialdontsend = bitset(QDONTSEND, a->q_flags);
- int i;
+ bool initialdontsend = QS_IS_DEAD(a->q_state);
+ int i, buflen;
char *buf;
char buf0[MAXNAME + 1]; /* unquoted image of the user name */
- extern void alias __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
e->e_to = a->q_paddr;
m = a->q_mailer;
@@ -226,7 +345,7 @@ recipient(a, sendq, aliaslevel, e)
a->q_flags |= QPRIMARY;
if (tTd(26, 1))
{
- printf("\nrecipient (%d): ", aliaslevel);
+ dprintf("\nrecipient (%d): ", aliaslevel);
printaddr(a, FALSE);
}
@@ -239,14 +358,70 @@ recipient(a, sendq, aliaslevel, e)
e->e_origrcpt = "";
}
+#if _FFR_GEN_ORCPT
+ /* set ORCPT DSN arg if not already set */
+ if (a->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;
+ else
+ {
+ /* make our own */
+ bool b = FALSE;
+ char *qp;
+ char obuf[MAXLINE];
+
+ if (e->e_from.q_mailer != NULL)
+ p = e->e_from.q_mailer->m_addrtype;
+ if (p == NULL)
+ p = "rfc822";
+ (void) strlcpy(obuf, p, sizeof obuf);
+ (void) strlcat(obuf, ";", sizeof obuf);
+
+ qp = q->q_paddr;
+
+ /* FFR: Needs to strip comments from stdin addrs */
+
+ /* strip brackets from address */
+ if (*qp == '<')
+ {
+ b = qp[strlen(qp) - 1] == '>';
+ if (b)
+ qp[strlen(qp) - 1] = '\0';
+ qp++;
+ }
+
+ p = xtextify(denlstring(qp, TRUE, FALSE), NULL);
+
+ if (strlcat(obuf, p, sizeof obuf) >= sizeof obuf)
+ {
+ /* if too big, don't use it */
+ obuf[0] = '\0';
+ }
+
+ /* undo damage */
+ if (b)
+ qp[strlen(qp)] = '>';
+
+ if (obuf[0] != '\0')
+ a->q_orcpt = newstr(obuf);
+ }
+ }
+#endif /* _FFR_GEN_ORCPT */
+
/* break aliasing loops */
if (aliaslevel > MaxAliasRecursion)
{
- a->q_flags |= QBADADDR;
+ a->q_state = QS_BADADDR;
a->q_status = "5.4.6";
- usrerr("554 aliasing/forwarding loop broken (%d aliases deep; %d max)",
- aliaslevel, MaxAliasRecursion);
- return (a);
+ usrerrenh(a->q_status,
+ "554 aliasing/forwarding loop broken (%d aliases deep; %d max)",
+ aliaslevel, MaxAliasRecursion);
+ return a;
}
/*
@@ -256,10 +431,16 @@ recipient(a, sendq, aliaslevel, e)
/* get unquoted user for file, program or user.name check */
i = strlen(a->q_user);
if (i >= sizeof buf0)
- buf = xalloc(i + 1);
+ {
+ buflen = i + 1;
+ buf = xalloc(buflen);
+ }
else
+ {
buf = buf0;
- (void) strcpy(buf, a->q_user);
+ buflen = sizeof buf0;
+ }
+ (void) strlcpy(buf, a->q_user, buflen);
for (p = buf; *p != '\0' && !quoted; p++)
{
if (*p == '\\')
@@ -272,28 +453,32 @@ recipient(a, sendq, aliaslevel, e)
{
if (a->q_alias == NULL)
{
- a->q_flags |= QBADADDR;
+ a->q_state = QS_BADADDR;
a->q_status = "5.7.1";
- usrerr("550 Cannot mail directly to programs");
+ usrerrenh(a->q_status,
+ "550 Cannot mail directly to programs");
}
else if (bitset(QBOGUSSHELL, a->q_alias->q_flags))
{
- a->q_flags |= QBADADDR;
+ a->q_state = QS_BADADDR;
a->q_status = "5.7.1";
if (a->q_alias->q_ruser == NULL)
- usrerr("550 UID %d is an unknown user: cannot mail to programs",
- a->q_alias->q_uid);
+ usrerrenh(a->q_status,
+ "550 UID %d is an unknown user: cannot mail to programs",
+ a->q_alias->q_uid);
else
- usrerr("550 User %s@%s doesn't have a valid shell for mailing to programs",
- a->q_alias->q_ruser, MyHostName);
+ usrerrenh(a->q_status,
+ "550 User %s@%s doesn't have a valid shell for mailing to programs",
+ a->q_alias->q_ruser, MyHostName);
}
else if (bitset(QUNSAFEADDR, a->q_alias->q_flags))
{
- a->q_flags |= QBADADDR;
+ a->q_state = QS_BADADDR;
a->q_status = "5.7.1";
- a->q_rstatus = newstr("Unsafe for mailing to programs");
- usrerr("550 Address %s is unsafe for mailing to programs",
- a->q_alias->q_paddr);
+ a->q_rstatus = newstr("550 Unsafe for mailing to programs");
+ usrerrenh(a->q_status,
+ "550 Address %s is unsafe for mailing to programs",
+ a->q_alias->q_paddr);
}
}
@@ -302,7 +487,7 @@ recipient(a, sendq, aliaslevel, e)
** If they are there already, return, otherwise continue.
** If the list is empty, just add it. Notice the cute
** hack to make from addresses suppress things correctly:
- ** the QDONTSEND bit will be set in the send list.
+ ** the QS_DUPLICATE state will be set in the send list.
** [Please note: the emphasis is on "hack."]
*/
@@ -314,17 +499,32 @@ recipient(a, sendq, aliaslevel, e)
{
if (tTd(26, 1))
{
- printf("%s in sendq: ", a->q_paddr);
+ dprintf("%s in sendq: ", a->q_paddr);
printaddr(q, FALSE);
}
if (!bitset(QPRIMARY, q->q_flags))
{
- if (!bitset(QDONTSEND, a->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 */
+ )
+ {
+#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;
}
- else if (bitset(QSELFREF, q->q_flags))
- q->q_flags |= a->q_flags & ~QDONTSEND;
a = q;
goto done;
}
@@ -344,21 +544,22 @@ recipient(a, sendq, aliaslevel, e)
trylocaluser:
if (tTd(29, 7))
{
- printf("at trylocaluser: ");
+ dprintf("at trylocaluser: ");
printaddr(a, FALSE);
}
- if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags))
+ if (!QS_IS_OK(a->q_state))
goto testselfdestruct;
if (m == InclMailer)
{
- a->q_flags |= QDONTSEND;
+ a->q_state = QS_INCLUDED;
if (a->q_alias == NULL)
{
- a->q_flags |= QBADADDR;
+ a->q_state = QS_BADADDR;
a->q_status = "5.7.1";
- usrerr("550 Cannot mail directly to :include:s");
+ usrerrenh(a->q_status,
+ "550 Cannot mail directly to :include:s");
}
else
{
@@ -370,95 +571,86 @@ recipient(a, sendq, aliaslevel, e)
{
if (LogLevel > 2)
sm_syslog(LOG_ERR, e->e_id,
- "include %s: transient error: %s",
- shortenstring(a->q_user, MAXSHORTSTR),
- errstring(ret));
- a->q_flags |= QQUEUEUP;
- a->q_flags &= ~QDONTSEND;
- usrerr("451 Cannot open %s: %s",
+ "include %s: transient error: %s",
+ shortenstring(a->q_user, MAXSHORTSTR),
+ errstring(ret));
+ a->q_state = QS_QUEUEUP;
+ usrerr("451 4.2.4 Cannot open %s: %s",
shortenstring(a->q_user, MAXSHORTSTR),
errstring(ret));
}
else if (ret != 0)
{
- a->q_flags |= QBADADDR;
+ a->q_state = QS_BADADDR;
a->q_status = "5.2.4";
- usrerr("550 Cannot open %s: %s",
- shortenstring(a->q_user, MAXSHORTSTR),
- errstring(ret));
+ usrerrenh(a->q_status,
+ "550 Cannot open %s: %s",
+ shortenstring(a->q_user, MAXSHORTSTR),
+ errstring(ret));
}
}
}
else if (m == FileMailer)
{
- extern bool writable __P((char *, ADDRESS *, int));
-
/* check if writable or creatable */
if (a->q_alias == NULL)
{
- a->q_flags |= QBADADDR;
+ a->q_state = QS_BADADDR;
a->q_status = "5.7.1";
- usrerr("550 Cannot mail directly to files");
+ usrerrenh(a->q_status,
+ "550 Cannot mail directly to files");
}
else if (bitset(QBOGUSSHELL, a->q_alias->q_flags))
{
- a->q_flags |= QBADADDR;
+ a->q_state = QS_BADADDR;
a->q_status = "5.7.1";
if (a->q_alias->q_ruser == NULL)
- usrerr("550 UID %d is an unknown user: cannot mail to files",
- a->q_alias->q_uid);
+ usrerrenh(a->q_status,
+ "550 UID %d is an unknown user: cannot mail to files",
+ a->q_alias->q_uid);
else
- usrerr("550 User %s@%s doesn't have a valid shell for mailing to files",
- a->q_alias->q_ruser, MyHostName);
+ usrerrenh(a->q_status,
+ "550 User %s@%s doesn't have a valid shell for mailing to files",
+ a->q_alias->q_ruser, MyHostName);
}
else if (bitset(QUNSAFEADDR, a->q_alias->q_flags))
{
- a->q_flags |= QBADADDR;
+ a->q_state = QS_BADADDR;
a->q_status = "5.7.1";
- a->q_rstatus = newstr("Unsafe for mailing to files");
- usrerr("550 Address %s is unsafe for mailing to files",
- a->q_alias->q_paddr);
- }
- else if (strcmp(buf, "/dev/null") == 0)
- {
- /* /dev/null is always accepted */
- }
- else if (!writable(buf, a->q_alias, SFF_CREAT))
- {
- a->q_flags |= QBADADDR;
- giveresponse(EX_CANTCREAT, m, NULL, a->q_alias,
- (time_t) 0, e);
+ a->q_rstatus = newstr("550 Unsafe for mailing to files");
+ usrerrenh(a->q_status,
+ "550 Address %s is unsafe for mailing to files",
+ a->q_alias->q_paddr);
}
}
/* try aliasing */
- if (!quoted && !bitset(QDONTSEND, a->q_flags) &&
+ if (!quoted && QS_IS_OK(a->q_state) &&
bitnset(M_ALIASABLE, m->m_flags))
alias(a, sendq, aliaslevel, e);
-# if USERDB
+#if USERDB
/* if not aliased, look it up in the user database */
- if (!bitset(QDONTSEND|QNOTREMOTE|QVERIFIED, a->q_flags) &&
+ if (!bitset(QNOTREMOTE, a->q_flags) &&
+ QS_IS_SENDABLE(a->q_state) &&
bitnset(M_CHECKUDB, m->m_flags))
{
- extern int udbexpand __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
-
if (udbexpand(a, sendq, aliaslevel, e) == EX_TEMPFAIL)
{
- a->q_flags |= QQUEUEUP;
+ a->q_state = QS_QUEUEUP;
if (e->e_message == NULL)
e->e_message = newstr("Deferred: user database error");
if (LogLevel > 8)
sm_syslog(LOG_INFO, e->e_id,
- "deferred: udbexpand: %s",
- errstring(errno));
+ "deferred: udbexpand: %s",
+ errstring(errno));
message("queued (user database error): %s",
errstring(errno));
e->e_nrcpts++;
goto testselfdestruct;
}
}
-# endif
+#endif /* USERDB */
/*
** If we have a level two config file, then pass the name through
@@ -469,16 +661,15 @@ recipient(a, sendq, aliaslevel, e)
if (tTd(29, 5))
{
- printf("recipient: testing local? cl=%d, rr5=%lx\n\t",
+ dprintf("recipient: testing local? cl=%d, rr5=%lx\n\t",
ConfigLevel, (u_long) RewriteRules[5]);
printaddr(a, FALSE);
}
- if (!bitset(QNOTREMOTE|QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) &&
- ConfigLevel >= 2 && RewriteRules[5] != NULL &&
- bitnset(M_TRYRULESET5, m->m_flags))
+ if (ConfigLevel >= 2 && RewriteRules[5] != NULL &&
+ bitnset(M_TRYRULESET5, m->m_flags) &&
+ !bitset(QNOTREMOTE, a->q_flags) &&
+ QS_IS_OK(a->q_state))
{
- extern void maplocaluser __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
-
maplocaluser(a, sendq, aliaslevel + 1, e);
}
@@ -487,21 +678,23 @@ recipient(a, sendq, aliaslevel, e)
** and deliver it.
*/
- if (!bitset(QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) &&
+ if (QS_IS_OK(a->q_state) &&
bitnset(M_HASPWENT, m->m_flags))
{
auto bool fuzzy;
register struct passwd *pw;
- extern void forward __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
/* warning -- finduser may trash buf */
pw = finduser(buf, &fuzzy);
if (pw == NULL || strlen(pw->pw_name) > MAXNAME)
{
- a->q_flags |= QBADADDR;
- a->q_status = "5.1.1";
- giveresponse(EX_NOUSER, m, NULL, a->q_alias,
- (time_t) 0, e);
+ {
+ a->q_state = QS_BADADDR;
+ a->q_status = "5.1.1";
+ a->q_rstatus = newstr("550 5.1.1 User unknown");
+ giveresponse(EX_NOUSER, a->q_status, m, NULL,
+ a->q_alias, (time_t) 0, e);
+ }
}
else
{
@@ -513,15 +706,16 @@ recipient(a, sendq, aliaslevel, e)
a->q_user = newstr(pw->pw_name);
if (findusercount++ > 3)
{
- a->q_flags |= QBADADDR;
+ a->q_state = QS_BADADDR;
a->q_status = "5.4.6";
- usrerr("554 aliasing/forwarding loop for %s broken",
- pw->pw_name);
+ usrerrenh(a->q_status,
+ "554 aliasing/forwarding loop for %s broken",
+ pw->pw_name);
goto done;
}
/* see if it aliases */
- (void) strcpy(buf, pw->pw_name);
+ (void) strlcpy(buf, pw->pw_name, buflen);
goto trylocaluser;
}
if (strcmp(pw->pw_dir, "/") == 0)
@@ -542,41 +736,42 @@ recipient(a, sendq, aliaslevel, e)
if (bitset(EF_VRFYONLY, e->e_flags))
{
/* don't do any more now */
- a->q_flags |= QVERIFIED;
+ a->q_state = QS_VERIFIED;
}
else if (!quoted)
forward(a, sendq, aliaslevel, e);
}
}
- if (!bitset(QDONTSEND, a->q_flags))
+ if (!QS_IS_DEAD(a->q_state))
e->e_nrcpts++;
testselfdestruct:
a->q_flags |= QTHISPASS;
if (tTd(26, 8))
{
- printf("testselfdestruct: ");
+ dprintf("testselfdestruct: ");
printaddr(a, FALSE);
if (tTd(26, 10))
{
- printf("SENDQ:\n");
+ dprintf("SENDQ:\n");
printaddr(*sendq, TRUE);
- printf("----\n");
+ dprintf("----\n");
}
}
if (a->q_alias == NULL && a != &e->e_from &&
- bitset(QDONTSEND, a->q_flags))
+ QS_IS_DEAD(a->q_state))
{
for (q = *sendq; q != NULL; q = q->q_next)
{
- if (!bitset(QDONTSEND, q->q_flags))
+ if (!QS_IS_DEAD(q->q_state))
break;
}
if (q == NULL)
{
- a->q_flags |= QBADADDR;
+ a->q_state = QS_BADADDR;
a->q_status = "5.4.6";
- usrerr("554 aliasing/forwarding loop broken");
+ usrerrenh(a->q_status,
+ "554 aliasing/forwarding loop broken");
}
}
@@ -601,7 +796,7 @@ recipient(a, sendq, aliaslevel, e)
for (q = *sendq; q != NULL; q = q->q_next)
{
if (bitset(QTHISPASS, q->q_flags) &&
- !bitset(QDONTSEND|QBADADDR, q->q_flags))
+ QS_IS_SENDABLE(q->q_state))
{
nrcpts++;
only = q;
@@ -625,14 +820,15 @@ recipient(a, sendq, aliaslevel, e)
/* arrange for return receipt */
e->e_flags |= EF_SENDRECEIPT;
a->q_flags |= QEXPANDED;
- if (e->e_xfp != NULL && bitset(QPINGONSUCCESS, a->q_flags))
+ if (e->e_xfp != NULL &&
+ bitset(QPINGONSUCCESS, a->q_flags))
fprintf(e->e_xfp,
"%s... expanded to multiple addresses\n",
a->q_paddr);
}
}
a->q_flags |= QRCPTOK;
- return (a);
+ return a;
}
/*
** FINDUSER -- find the password entry for a user.
@@ -667,7 +863,7 @@ finduser(name, fuzzyp)
bool tryagain;
if (tTd(29, 4))
- printf("finduser(%s): ", name);
+ dprintf("finduser(%s): ", name);
*fuzzyp = FALSE;
@@ -679,17 +875,17 @@ finduser(name, fuzzyp)
if (*p == '\0')
{
if (tTd(29, 4))
- printf("failed (numeric input)\n");
+ dprintf("failed (numeric input)\n");
return NULL;
}
-#endif
+#endif /* HESIOD */
/* look up this login name using fast path */
if ((pw = sm_getpwnam(name)) != NULL)
{
if (tTd(29, 4))
- printf("found (non-fuzzy)\n");
- return (pw);
+ dprintf("found (non-fuzzy)\n");
+ return pw;
}
/* try mapping it to lower case */
@@ -705,7 +901,7 @@ finduser(name, fuzzyp)
if (tryagain && (pw = sm_getpwnam(name)) != NULL)
{
if (tTd(29, 4))
- printf("found (lower case)\n");
+ dprintf("found (lower case)\n");
*fuzzyp = TRUE;
return pw;
}
@@ -715,7 +911,7 @@ finduser(name, fuzzyp)
if (!MatchGecos)
{
if (tTd(29, 4))
- printf("not found (fuzzy disabled)\n");
+ dprintf("not found (fuzzy disabled)\n");
return NULL;
}
@@ -734,16 +930,16 @@ finduser(name, fuzzyp)
if (strcasecmp(pw->pw_name, name) == 0)
{
if (tTd(29, 4))
- printf("found (case wrapped)\n");
+ dprintf("found (case wrapped)\n");
break;
}
-# endif
+# endif /* 0 */
buildfname(pw->pw_gecos, pw->pw_name, buf, sizeof buf);
- if (strchr(buf, ' ') != NULL && !strcasecmp(buf, name))
+ if (strchr(buf, ' ') != NULL && strcasecmp(buf, name) == 0)
{
if (tTd(29, 4))
- printf("fuzzy matches %s\n", pw->pw_name);
+ dprintf("fuzzy matches %s\n", pw->pw_name);
message("sending to login name %s", pw->pw_name);
break;
}
@@ -751,16 +947,16 @@ finduser(name, fuzzyp)
if (pw != NULL)
*fuzzyp = TRUE;
else if (tTd(29, 4))
- printf("no fuzzy match found\n");
+ dprintf("no fuzzy match found\n");
# if DEC_OSF_BROKEN_GETPWENT /* DEC OSF/1 3.2 or earlier */
endpwent();
-# endif
+# endif /* DEC_OSF_BROKEN_GETPWENT */
return pw;
-#else
+#else /* MATCHGECOS */
if (tTd(29, 4))
- printf("not found (fuzzy disabled)\n");
+ dprintf("not found (fuzzy disabled)\n");
return NULL;
-#endif
+#endif /* MATCHGECOS */
}
/*
** WRITABLE -- predicate returning if the file is writable.
@@ -790,14 +986,14 @@ bool
writable(filename, ctladdr, flags)
char *filename;
ADDRESS *ctladdr;
- int flags;
+ long flags;
{
- uid_t euid;
- gid_t egid;
- char *uname;
+ uid_t euid = 0;
+ gid_t egid = 0;
+ char *user = NULL;
if (tTd(44, 5))
- printf("writable(%s, 0x%x)\n", filename, flags);
+ dprintf("writable(%s, 0x%lx)\n", filename, flags);
/*
** File does exist -- check that it is writable.
@@ -807,37 +1003,37 @@ writable(filename, ctladdr, flags)
{
euid = geteuid();
egid = getegid();
- uname = NULL;
+ user = NULL;
}
else if (ctladdr != NULL)
{
euid = ctladdr->q_uid;
egid = ctladdr->q_gid;
- uname = ctladdr->q_user;
+ user = ctladdr->q_user;
}
else if (bitset(SFF_RUNASREALUID, flags))
{
euid = RealUid;
egid = RealGid;
- uname = RealUserName;
+ user = RealUserName;
}
else if (FileMailer != NULL && !bitset(SFF_ROOTOK, flags))
{
euid = FileMailer->m_uid;
egid = FileMailer->m_gid;
- uname = NULL;
+ user = NULL;
}
else
{
euid = egid = 0;
- uname = NULL;
+ user = NULL;
}
if (!bitset(SFF_ROOTOK, flags))
{
if (euid == 0)
{
euid = DefUid;
- uname = DefUser;
+ user = DefUser;
}
if (egid == 0)
egid = DefGid;
@@ -846,12 +1042,12 @@ writable(filename, ctladdr, flags)
(ctladdr == NULL || !bitset(QGOODUID, ctladdr->q_flags)))
flags |= SFF_SETUIDOK;
- if (!bitset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
+ if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
flags |= SFF_NOSLINK;
- if (!bitset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
+ if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
flags |= SFF_NOHLINK;
- errno = safefile(filename, euid, egid, uname, flags, S_IWRITE, NULL);
+ errno = safefile(filename, euid, egid, user, flags, S_IWRITE, NULL);
return errno == 0;
}
/*
@@ -891,7 +1087,6 @@ writable(filename, ctladdr, flags)
*/
static jmp_buf CtxIncludeTimeout;
-static void includetimeout __P((void));
int
include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
@@ -909,59 +1104,81 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
register EVENT *ev = NULL;
int nincludes;
int mode;
+ volatile bool maxreached = FALSE;
register ADDRESS *ca;
- volatile uid_t saveduid, uid;
- volatile gid_t savedgid, gid;
- char *volatile uname;
+ volatile uid_t saveduid;
+ volatile gid_t savedgid;
+ volatile uid_t uid;
+ volatile gid_t gid;
+ char *volatile user;
int rval = 0;
- volatile int sfflags = SFF_REGONLY;
+ volatile long sfflags = SFF_REGONLY;
register char *p;
bool safechown = FALSE;
volatile bool safedir = FALSE;
struct stat st;
char buf[MAXLINE];
- extern bool chownsafe __P((int, bool));
if (tTd(27, 2))
- printf("include(%s)\n", fname);
+ dprintf("include(%s)\n", fname);
if (tTd(27, 4))
- printf(" ruid=%d euid=%d\n", (int) getuid(), (int) geteuid());
+ dprintf(" ruid=%d euid=%d\n",
+ (int) getuid(), (int) geteuid());
if (tTd(27, 14))
{
- printf("ctladdr ");
+ dprintf("ctladdr ");
printaddr(ctladdr, FALSE);
}
if (tTd(27, 9))
- printf("include: old uid = %d/%d\n",
- (int) getuid(), (int) geteuid());
+ dprintf("include: old uid = %d/%d\n",
+ (int) getuid(), (int) geteuid());
if (forwarding)
sfflags |= SFF_MUSTOWN|SFF_ROOTOK|SFF_NOWLINK;
+ /*
+ ** If RunAsUser set, won't be able to run programs as user
+ ** so mark them as unsafe unless the administrator knows better.
+ */
+
+ if ((geteuid() != 0 || RunAsUid != 0) &&
+ !bitnset(DBS_NONROOTSAFEADDR, DontBlameSendmail))
+ {
+ if (tTd(27, 4))
+ dprintf("include: not safe (euid=%d, RunAsUid=%d)\n",
+ (int) geteuid(), (int) RunAsUid);
+ ctladdr->q_flags |= QUNSAFEADDR;
+ }
+
ca = getctladdr(ctladdr);
- if (ca == NULL)
+ if (ca == NULL ||
+ (ca->q_uid == DefUid && ca->q_gid == 0))
{
uid = DefUid;
gid = DefGid;
- uname = DefUser;
+ user = DefUser;
}
else
{
uid = ca->q_uid;
gid = ca->q_gid;
- uname = ca->q_user;
+ user = ca->q_user;
}
-#if HASSETREUID || USESETEUID
+#if MAILER_SETUID_METHOD != USE_SETUID
saveduid = geteuid();
savedgid = getegid();
if (saveduid == 0)
{
if (!DontInitGroups)
{
- if (initgroups(uname, gid) == -1)
+ if (initgroups(user, gid) == -1)
+ {
+ rval = EAGAIN;
syserr("include: initgroups(%s, %d) failed",
- uname, gid);
+ user, gid);
+ goto resetuid;
+ }
}
else
{
@@ -969,29 +1186,46 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
gidset[0] = gid;
if (setgroups(1, gidset) == -1)
+ {
+ rval = EAGAIN;
syserr("include: setgroups() failed");
+ goto resetuid;
+ }
}
if (gid != 0 && setgid(gid) < -1)
+ {
+ rval = EAGAIN;
syserr("setgid(%d) failure", gid);
+ goto resetuid;
+ }
if (uid != 0)
{
-# if USESETEUID
+# if MAILER_SETUID_METHOD == USE_SETEUID
if (seteuid(uid) < 0)
+ {
+ rval = EAGAIN;
syserr("seteuid(%d) failure (real=%d, eff=%d)",
uid, getuid(), geteuid());
-# else
+ goto resetuid;
+ }
+# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
+# if MAILER_SETUID_METHOD == USE_SETREUID
if (setreuid(0, uid) < 0)
+ {
+ rval = EAGAIN;
syserr("setreuid(0, %d) failure (real=%d, eff=%d)",
uid, getuid(), geteuid());
-# endif
+ goto resetuid;
+ }
+# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
}
}
-#endif
+#endif /* MAILER_SETUID_METHOD != USE_SETUID */
if (tTd(27, 9))
- printf("include: new uid = %d/%d\n",
- (int) getuid(), (int) geteuid());
+ dprintf("include: new uid = %d/%d\n",
+ (int) getuid(), (int) geteuid());
/*
** If home directory is remote mounted but server is down,
@@ -1000,7 +1234,7 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
if (setjmp(CtxIncludeTimeout) != 0)
{
- ctladdr->q_flags |= QQUEUEUP;
+ ctladdr->q_state = QS_QUEUEUP;
errno = 0;
/* return pseudo-error code */
@@ -1012,6 +1246,7 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
else
ev = NULL;
+
/* check for writable parent directory */
p = strrchr(fname, '/');
if (p != NULL)
@@ -1019,7 +1254,8 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
int ret;
*p = '\0';
- ret = safedirpath(fname, uid, gid, uname, sfflags|SFF_SAFEDIRPATH);
+ ret = safedirpath(fname, uid, gid, user,
+ sfflags|SFF_SAFEDIRPATH, 0, 0);
if (ret == 0)
{
/* in safe directory: relax chown & link rules */
@@ -1028,22 +1264,24 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
}
else
{
- if (bitset((forwarding ?
- DBS_FORWARDFILEINUNSAFEDIRPATH :
- DBS_INCLUDEFILEINUNSAFEDIRPATH),
- DontBlameSendmail))
+ if (bitnset((forwarding ?
+ DBS_FORWARDFILEINUNSAFEDIRPATH :
+ DBS_INCLUDEFILEINUNSAFEDIRPATH),
+ DontBlameSendmail))
sfflags |= SFF_NOPATHCHECK;
- else if (bitset((forwarding ?
- DBS_FORWARDFILEINGROUPWRITABLEDIRPATH :
- DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH),
- DontBlameSendmail) &&
+ else if (bitnset((forwarding ?
+ DBS_FORWARDFILEINGROUPWRITABLEDIRPATH :
+ DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH),
+ DontBlameSendmail) &&
ret == E_SM_GWDIR)
{
- DontBlameSendmail |= DBS_GROUPWRITABLEDIRPATHSAFE;
- ret = safedirpath(fname, uid,
- gid, uname,
- sfflags|SFF_SAFEDIRPATH);
- DontBlameSendmail &= ~DBS_GROUPWRITABLEDIRPATHSAFE;
+ setbitn(DBS_GROUPWRITABLEDIRPATHSAFE,
+ DontBlameSendmail);
+ ret = safedirpath(fname, uid, gid, user,
+ sfflags|SFF_SAFEDIRPATH,
+ 0, 0);
+ clrbitn(DBS_GROUPWRITABLEDIRPATHSAFE,
+ DontBlameSendmail);
if (ret == 0)
sfflags |= SFF_NOPATHCHECK;
else
@@ -1052,10 +1290,10 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
else
sfflags |= SFF_SAFEDIRPATH;
if (ret > E_PSEUDOBASE &&
- !bitset((forwarding ?
- DBS_FORWARDFILEINUNSAFEDIRPATHSAFE :
- DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE),
- DontBlameSendmail))
+ !bitnset((forwarding ?
+ DBS_FORWARDFILEINUNSAFEDIRPATHSAFE :
+ DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE),
+ DontBlameSendmail))
{
if (LogLevel >= 12)
sm_syslog(LOG_INFO, e->e_id,
@@ -1069,31 +1307,31 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
/* allow links only in unwritable directories */
if (!safedir &&
- !bitset((forwarding ?
- DBS_LINKEDFORWARDFILEINWRITABLEDIR :
- DBS_LINKEDINCLUDEFILEINWRITABLEDIR),
- DontBlameSendmail))
+ !bitnset((forwarding ?
+ DBS_LINKEDFORWARDFILEINWRITABLEDIR :
+ DBS_LINKEDINCLUDEFILEINWRITABLEDIR),
+ DontBlameSendmail))
sfflags |= SFF_NOLINK;
- rval = safefile(fname, uid, gid, uname, sfflags, S_IREAD, &st);
+ rval = safefile(fname, uid, gid, user, sfflags, S_IREAD, &st);
if (rval != 0)
{
/* don't use this :include: file */
if (tTd(27, 4))
- printf("include: not safe (uid=%d): %s\n",
+ dprintf("include: not safe (uid=%d): %s\n",
(int) uid, errstring(rval));
}
else if ((fp = fopen(fname, "r")) == NULL)
{
rval = errno;
if (tTd(27, 4))
- printf("include: open: %s\n", errstring(rval));
+ dprintf("include: open: %s\n", errstring(rval));
}
else if (filechanged(fname, fileno(fp), &st))
{
rval = E_SM_FILECHANGE;
if (tTd(27, 4))
- printf("include: file changed after open\n");
+ dprintf("include: file changed after open\n");
}
if (ev != NULL)
clrevent(ev);
@@ -1107,27 +1345,29 @@ resetuid:
{
# if USESETEUID
if (seteuid(0) < 0)
- syserr("seteuid(0) failure (real=%d, eff=%d)",
+ syserr("!seteuid(0) failure (real=%d, eff=%d)",
getuid(), geteuid());
-# else
+# else /* USESETEUID */
if (setreuid(-1, 0) < 0)
- syserr("setreuid(-1, 0) failure (real=%d, eff=%d)",
+ syserr("!setreuid(-1, 0) failure (real=%d, eff=%d)",
getuid(), geteuid());
if (setreuid(RealUid, 0) < 0)
- syserr("setreuid(%d, 0) failure (real=%d, eff=%d)",
+ syserr("!setreuid(%d, 0) failure (real=%d, eff=%d)",
RealUid, getuid(), geteuid());
-# endif
+# endif /* USESETEUID */
}
- setgid(savedgid);
+ if (setgid(savedgid) < 0)
+ syserr("!setgid(%d) failure (real=%d eff=%d)",
+ savedgid, getgid(), getegid());
}
-#endif
+#endif /* HASSETREUID || USESETEUID */
if (tTd(27, 9))
- printf("include: reset uid = %d/%d\n",
- (int) getuid(), (int) geteuid());
+ dprintf("include: reset uid = %d/%d\n",
+ (int) getuid(), (int) geteuid());
if (rval == E_SM_OPENTIMEOUT)
- usrerr("451 open timeout on %s", fname);
+ usrerr("451 4.4.1 open timeout on %s", fname);
if (fp == NULL)
return rval;
@@ -1136,18 +1376,22 @@ resetuid:
{
rval = errno;
syserr("Cannot fstat %s!", fname);
+ (void) fclose(fp);
return rval;
}
/* if path was writable, check to avoid file giveaway tricks */
safechown = chownsafe(fileno(fp), safedir);
if (tTd(27, 6))
- printf("include: parent of %s is %s, chown is %ssafe\n",
+ dprintf("include: parent of %s is %s, chown is %ssafe\n",
fname,
safedir ? "safe" : "dangerous",
safechown ? "" : "un");
- if (ca == NULL && safechown)
+ /* if no controlling user or coming from an alias delivery */
+ if (safechown &&
+ (ca == NULL ||
+ (ca->q_uid == DefUid && ca->q_gid == 0)))
{
ctladdr->q_uid = st.st_uid;
ctladdr->q_gid = st.st_gid;
@@ -1165,7 +1409,10 @@ resetuid:
pw = sm_getpwuid(st.st_uid);
if (pw == NULL)
+ {
+ ctladdr->q_uid = st.st_uid;
ctladdr->q_flags |= QBOGUSSHELL;
+ }
else
{
char *sh;
@@ -1179,10 +1426,10 @@ resetuid:
{
if (LogLevel >= 12)
sm_syslog(LOG_INFO, e->e_id,
- "%s: user %s has bad shell %s, marked %s",
- shortenstring(fname, MAXSHORTSTR),
- pw->pw_name, sh,
- safechown ? "bogus" : "unsafe");
+ "%s: user %s has bad shell %s, marked %s",
+ shortenstring(fname, MAXSHORTSTR),
+ pw->pw_name, sh,
+ safechown ? "bogus" : "unsafe");
if (safechown)
ctladdr->q_flags |= QBOGUSSHELL;
else
@@ -1194,10 +1441,9 @@ resetuid:
if (bitset(EF_VRFYONLY, e->e_flags))
{
/* don't do any more now */
- ctladdr->q_flags |= QVERIFIED;
- ctladdr->q_flags &= ~QDONTSEND;
+ ctladdr->q_state = QS_VERIFIED;
e->e_nrcpts++;
- xfclose(fp, "include", fname);
+ (void) fclose(fp);
return rval;
}
@@ -1210,24 +1456,24 @@ resetuid:
*/
mode = S_IWOTH;
- if (!bitset((forwarding ?
- DBS_GROUPWRITABLEFORWARDFILESAFE :
- DBS_GROUPWRITABLEINCLUDEFILESAFE),
- DontBlameSendmail))
+ if (!bitnset((forwarding ?
+ DBS_GROUPWRITABLEFORWARDFILESAFE :
+ DBS_GROUPWRITABLEINCLUDEFILESAFE),
+ DontBlameSendmail))
mode |= S_IWGRP;
if (bitset(mode, st.st_mode))
{
if (tTd(27, 6))
- printf("include: %s is %s writable, marked unsafe\n",
+ dprintf("include: %s is %s writable, marked unsafe\n",
shortenstring(fname, MAXSHORTSTR),
bitset(S_IWOTH, st.st_mode) ? "world" : "group");
if (LogLevel >= 12)
sm_syslog(LOG_INFO, e->e_id,
- "%s: %s writable %s file, marked unsafe",
- shortenstring(fname, MAXSHORTSTR),
- bitset(S_IWOTH, st.st_mode) ? "world" : "group",
- forwarding ? "forward" : ":include:");
+ "%s: %s writable %s file, marked unsafe",
+ shortenstring(fname, MAXSHORTSTR),
+ bitset(S_IWOTH, st.st_mode) ? "world" : "group",
+ forwarding ? "forward" : ":include:");
ctladdr->q_flags |= QUNSAFEADDR;
}
@@ -1236,13 +1482,10 @@ resetuid:
LineNumber = 0;
ctladdr->q_flags &= ~QSELFREF;
nincludes = 0;
- while (fgets(buf, sizeof buf, fp) != NULL)
+ while (fgets(buf, sizeof buf, fp) != NULL && !maxreached)
{
- register char *p = strchr(buf, '\n');
-
+ fixcrlf(buf, TRUE);
LineNumber++;
- if (p != NULL)
- *p = '\0';
if (buf[0] == '#' || buf[0] == '\0')
continue;
@@ -1264,27 +1507,40 @@ resetuid:
e->e_to = NULL;
message("%s to %s",
forwarding ? "forwarding" : "sending", buf);
- if (forwarding && LogLevel > 9)
+ if (forwarding && LogLevel > 10)
sm_syslog(LOG_INFO, e->e_id,
- "forward %.200s => %s",
- oldto, shortenstring(buf, MAXSHORTSTR));
+ "forward %.200s => %s",
+ oldto, shortenstring(buf, MAXSHORTSTR));
nincludes += sendtolist(buf, ctladdr, sendq, aliaslevel + 1, e);
+
+ if (forwarding &&
+ MaxForwardEntries > 0 &&
+ nincludes >= MaxForwardEntries)
+ {
+ /* just stop reading and processing further entries */
+ /* additional: (?)
+ ctladdr->q_state = QS_DONTSEND;
+ **/
+ syserr("Attempt to forward to more then %d addresses (in %s)!",
+ MaxForwardEntries,fname);
+ maxreached = TRUE;
+ }
}
if (ferror(fp) && tTd(27, 3))
- printf("include: read error: %s\n", errstring(errno));
+ dprintf("include: read error: %s\n", errstring(errno));
if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags))
{
if (tTd(27, 5))
{
- printf("include: QDONTSEND ");
+ dprintf("include: QS_DONTSEND ");
printaddr(ctladdr, FALSE);
}
- ctladdr->q_flags |= QDONTSEND;
+ ctladdr->q_state = QS_DONTSEND;
}
- (void) xfclose(fp, "include", fname);
+ (void) fclose(fp);
FileName = oldfilename;
LineNumber = oldlinenumber;
e->e_to = oldto;
@@ -1344,7 +1600,7 @@ getctladdr(a)
{
while (a != NULL && !bitset(QGOODUID, a->q_flags))
a = a->q_alias;
- return (a);
+ return a;
}
/*
** SELF_REFERENCE -- check to see if an address references itself
@@ -1360,22 +1616,20 @@ getctladdr(a)
**
** Parameters:
** a -- the address to check.
-** e -- the current envelope.
**
** Returns:
** The address that should be retained.
*/
-ADDRESS *
-self_reference(a, e)
+static ADDRESS *
+self_reference(a)
ADDRESS *a;
- ENVELOPE *e;
{
ADDRESS *b; /* top entry in self ref loop */
ADDRESS *c; /* entry that point to a real mail box */
if (tTd(27, 1))
- printf("self_reference(%s)\n", a->q_paddr);
+ dprintf("self_reference(%s)\n", a->q_paddr);
for (b = a->q_alias; b != NULL; b = b->q_alias)
{
@@ -1386,7 +1640,7 @@ self_reference(a, e)
if (b == NULL)
{
if (tTd(27, 1))
- printf("\t... no self ref\n");
+ dprintf("\t... no self ref\n");
return NULL;
}
@@ -1394,14 +1648,14 @@ self_reference(a, e)
** Pick the first address that resolved to a real mail box
** i.e has a pw entry. The returned value will be marked
** QSELFREF in recipient(), which in turn will disable alias()
- ** from marking it QDONTSEND, which mean it will be used
+ ** 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
** 2) normally, when we return back to alias(), the address
- ** will be marked QDONTSEND, since alias() assumes the
+ ** 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().
@@ -1411,15 +1665,15 @@ self_reference(a, e)
while (c != NULL)
{
if (tTd(27, 10))
- printf(" %s", c->q_user);
+ dprintf(" %s", c->q_user);
if (bitnset(M_HASPWENT, c->q_mailer->m_flags))
{
if (tTd(27, 2))
- printf("\t... getpwnam(%s)... ", c->q_user);
+ dprintf("\t... getpwnam(%s)... ", c->q_user);
if (sm_getpwnam(c->q_user) != NULL)
{
if (tTd(27, 2))
- printf("found\n");
+ dprintf("found\n");
/* ought to cache results here */
if (sameaddr(b, c))
@@ -1428,7 +1682,7 @@ self_reference(a, e)
return c;
}
if (tTd(27, 2))
- printf("failed\n");
+ dprintf("failed\n");
}
else
{
@@ -1437,7 +1691,8 @@ self_reference(a, e)
b->q_mailer == c->q_mailer)
{
if (tTd(27, 2))
- printf("\t... local match (%s)\n", c->q_user);
+ dprintf("\t... local match (%s)\n",
+ c->q_user);
if (sameaddr(b, c))
return b;
else
@@ -1445,12 +1700,12 @@ self_reference(a, e)
}
}
if (tTd(27, 10))
- printf("\n");
+ dprintf("\n");
c = c->q_alias;
}
if (tTd(27, 1))
- printf("\t... cannot break loop for \"%s\"\n", a->q_paddr);
+ dprintf("\t... cannot break loop for \"%s\"\n", a->q_paddr);
return NULL;
}
diff --git a/contrib/sendmail/src/sendmail.h b/contrib/sendmail/src/sendmail.h
index ff2697e..e199d66 100644
--- a/contrib/sendmail/src/sendmail.h
+++ b/contrib/sendmail/src/sendmail.h
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -7,78 +8,128 @@
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
- *
- *
- * @(#)sendmail.h 8.295 (Berkeley) 1/26/1999
*/
/*
-** SENDMAIL.H -- Global definitions for sendmail.
+** SENDMAIL.H -- MTA-specific definitions for sendmail.
*/
-# ifdef _DEFINE
+#ifndef _SENDMAIL_H
+#define _SENDMAIL_H 1
+
+#ifdef _DEFINE
# define EXTERN
# ifndef lint
-static char SmailSccsId[] = "@(#)sendmail.h 8.295 1/26/1999";
-# endif
-# else /* _DEFINE */
+static char SmailId[] = "@(#)$Id: sendmail.h,v 8.517.4.28 2000/07/18 02:24:44 gshapiro Exp $";
+# endif /* ! lint */
+#else /* _DEFINE */
# define EXTERN extern
-# endif /* _DEFINE */
+#endif /* _DEFINE */
+
+
+#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 <unistd.h>
-# include <stddef.h>
-# include <stdlib.h>
+#include <stddef.h>
+#include <stdlib.h>
+#if !SFIO
# include <stdio.h>
-# include <ctype.h>
-# include <setjmp.h>
-# include <string.h>
-# include <time.h>
-# include <errno.h>
+#endif /* !SFIO */
+#include <ctype.h>
+#include <setjmp.h>
+#include <string.h>
+#include <time.h>
# ifdef EX_OK
# undef EX_OK /* for SVr4.2 SMP */
-# endif
-# include <sysexits.h>
+# endif /* EX_OK */
+#include <sysexits.h>
-# include "conf.h"
-# include "useful.h"
+#include "sendmail/sendmail.h"
+#include "bf.h"
+#include "timers.h"
-# ifdef LOG
+#ifdef LOG
# include <syslog.h>
-# endif /* LOG */
+#endif /* LOG */
-# if NETINET || NETUNIX || NETISO || NETNS || NETX25
-# include <sys/socket.h>
-# endif
+
+
+# if NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25
+# include <sys/socket.h>
+# endif /* NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25 */
# if NETUNIX
-# include <sys/un.h>
-# endif
-# if NETINET
-# include <netinet/in.h>
-# endif
+# include <sys/un.h>
+# endif /* NETUNIX */
+# if NETINET || NETINET6
+# include <netinet/in.h>
+# endif /* NETINET || NETINET6 */
+# if NETINET6
+/*
+** There is no standard yet for IPv6 includes.
+** Specify OS specific implementation in conf.h
+*/
+# endif /* NETINET6 */
# if NETISO
-# include <netiso/iso.h>
-# endif
+# include <netiso/iso.h>
+# endif /* NETISO */
# if NETNS
-# include <netns/ns.h>
-# endif
+# include <netns/ns.h>
+# endif /* NETNS */
# if NETX25
-# include <netccitt/x25.h>
-# endif
-
-#if NAMED_BIND
-# include <arpa/nameser.h>
-# ifdef NOERROR
-# undef NOERROR /* avoid <sys/streams.h> conflict */
-# endif
-#endif
-
-#ifdef HESIOD
-# include <hesiod.h>
-# if !defined(HES_ER_OK) || defined(HESIOD_INTERFACES)
-# define HESIOD_INIT /* support for the new interface */
-EXTERN void *HesiodContext;
-# endif
-#endif
+# include <netccitt/x25.h>
+# endif /* NETX25 */
+
+# if NAMED_BIND
+# include <arpa/nameser.h>
+# ifdef NOERROR
+# undef NOERROR /* avoid <sys/streams.h> conflict */
+# endif /* NOERROR */
+# include <resolv.h>
+# endif /* NAMED_BIND */
+
+# ifdef HESIOD
+# include <hesiod.h>
+# if !defined(HES_ER_OK) || defined(HESIOD_INTERFACES)
+# define HESIOD_INIT /* support for the new interface */
+# endif /* !defined(HES_ER_OK) || defined(HESIOD_INTERFACES) */
+# endif /* HESIOD */
+
+#if STARTTLS
+# if !SFIO && !_FFR_TLS_TOREK
+ ERROR README: STARTTLS requires SFIO
+# endif /* !SFIO && !_FFR_TLS_TOREK */
+# if SFIO && _FFR_TLS_TOREK
+ ERROR README: Can not do both SFIO and _FFR_TLS_TOREK
+# endif /* SFIO && _FFR_TLS_TOREK */
+# include <openssl/ssl.h>
+#endif /* STARTTLS */
+
+#if SASL /* include the sasl include files if we have them */
+# include <sasl.h>
+# if defined(SASL_VERSION_MAJOR) && defined(SASL_VERSION_MINOR) && defined(SASL_VERSION_STEP)
+# define SASL_VERSION (SASL_VERSION_MAJOR * 10000) + (SASL_VERSION_MINOR * 100) + SASL_VERSION_STEP
+# if SASL == 1
+# undef SASL
+# define SASL SASL_VERSION
+# else /* SASL == 1 */
+# if SASL != SASL_VERSION
+ ERROR README: -DSASL (SASL) does not agree with the version of the CYRUS_SASL library (SASL_VERSION)
+ ERROR README: see README!
+# endif /* SASL != SASL_VERSION */
+# endif /* SASL == 1 */
+# else /* defined(SASL_VERSION_MAJOR) && defined(SASL_VERSION_MINOR) && defined(SASL_VERSION_STEP) */
+# if SASL == 1
+ ERROR README: please set -DSASL to the version of the CYRUS_SASL library
+ ERROR README: see README!
+# endif /* SASL == 1 */
+# endif /* defined(SASL_VERSION_MAJOR) && defined(SASL_VERSION_MINOR) && defined(SASL_VERSION_STEP) */
+#endif /* SASL */
/*
** Following are "sort of" configuration constants, but they should
@@ -91,56 +142,30 @@ EXTERN void *HesiodContext;
#ifndef INADDRSZ
# define INADDRSZ 4 /* size of an IPv4 address in bytes */
-#endif
+#endif /* ! INADDRSZ */
+#ifndef IN6ADDRSZ
+# define IN6ADDRSZ 16 /* size of an IPv6 address in bytes */
+#endif /* ! IN6ADDRSZ */
#ifndef INT16SZ
# define INT16SZ 2 /* size of a 16 bit integer in bytes */
-#endif
+#endif /* ! INT16SZ */
#ifndef INT32SZ
# define INT32SZ 4 /* size of a 32 bit integer in bytes */
-#endif
-
-
-
-/* forward references for prototypes */
-typedef struct envelope ENVELOPE;
-typedef struct mailer MAILER;
-
+#endif /* ! INT32SZ */
/*
-** Data structure for bit maps.
-**
-** Each bit in this map can be referenced by an ascii character.
-** This is 256 possible bits, or 32 8-bit bytes.
+** Error return from inet_addr(3), in case not defined in /usr/include.
*/
-#define BITMAPBYTES 32 /* number of bytes in a bit map */
-#define BYTEBITS 8 /* number of bits in a byte */
-
-/* internal macros */
-#define _BITWORD(bit) ((bit) / (BYTEBITS * sizeof (int)))
-#define _BITBIT(bit) (1 << ((bit) % (BYTEBITS * sizeof (int))))
-
-typedef int BITMAP[BITMAPBYTES / sizeof (int)];
-
-/* test bit number N */
-#define bitnset(bit, map) ((map)[_BITWORD(bit)] & _BITBIT(bit))
-
-/* set bit number N */
-#define setbitn(bit, map) (map)[_BITWORD(bit)] |= _BITBIT(bit)
-
-/* clear bit number N */
-#define clrbitn(bit, map) (map)[_BITWORD(bit)] &= ~_BITBIT(bit)
-
-/* clear an entire bit map */
-#define clrbitmap(map) bzero((char *) map, BITMAPBYTES)
+#ifndef INADDR_NONE
+# define INADDR_NONE 0xffffffff
+#endif /* ! INADDR_NONE */
-/*
-** Utility macros
-*/
+/* forward references for prototypes */
+typedef struct envelope ENVELOPE;
+typedef struct mailer MAILER;
-/* return number of bytes left in a buffer */
-#define SPACELEFT(buf, ptr) (sizeof buf - ((ptr) - buf))
/*
** Address structure.
** Addresses are stored internally in this structure.
@@ -167,50 +192,97 @@ struct address
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 */
};
typedef struct address ADDRESS;
-# define QDONTSEND 0x00000001 /* don't send to this address */
-# define QBADADDR 0x00000002 /* this address is verified bad */
-# define QGOODUID 0x00000004 /* the q_uid q_gid fields are good */
-# define QPRIMARY 0x00000008 /* set from RCPT or argv */
-# define QQUEUEUP 0x00000010 /* queue for later transmission */
-# define QSENT 0x00000020 /* has been successfully delivered */
-# define QNOTREMOTE 0x00000040 /* address not for remote forwarding */
-# define QSELFREF 0x00000080 /* this address references itself */
-# define QVERIFIED 0x00000100 /* verified, but not expanded */
-# define QBOGUSSHELL 0x00000400 /* user has no valid shell listed */
-# define QUNSAFEADDR 0x00000800 /* address aquired via unsafe path */
-# define QPINGONSUCCESS 0x00001000 /* give return on successful delivery */
-# define QPINGONFAILURE 0x00002000 /* give return on failure */
-# define QPINGONDELAY 0x00004000 /* give return on message delay */
-# define QHASNOTIFY 0x00008000 /* propogate notify parameter */
-# define QRELAYED 0x00010000 /* DSN: relayed to non-DSN aware sys */
-# define QEXPANDED 0x00020000 /* DSN: undergone list expansion */
-# define QDELIVERED 0x00040000 /* DSN: successful final delivery */
-# define QDELAYED 0x00080000 /* DSN: message delayed */
-# define QTHISPASS 0x40000000 /* temp: address set this pass */
-# define QRCPTOK 0x80000000 /* recipient() processed address */
-
-# define Q_PINGFLAGS (QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY)
-
-# define NULLADDR ((ADDRESS *) NULL)
+/* bit values for q_flags */
+#define QGOODUID 0x00000001 /* the q_uid q_gid fields are good */
+#define QPRIMARY 0x00000002 /* set from RCPT or argv */
+#define QNOTREMOTE 0x00000004 /* address not for remote forwarding */
+#define QSELFREF 0x00000008 /* this address references itself */
+#define QBOGUSSHELL 0x00000010 /* user has no valid shell listed */
+#define QUNSAFEADDR 0x00000020 /* address acquired via unsafe path */
+#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 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 QTHISPASS 0x40000000 /* temp: address set this pass */
+#define QRCPTOK 0x80000000 /* recipient() processed address */
+
+#define Q_PINGFLAGS (QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY)
+
+/* values for q_state */
+#define QS_OK 0 /* address ok (for now)/not yet tried */
+#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 /* expanded */
+#define QS_SENDER 7 /* message sender (MeToo) */
+#define QS_CLONED 8 /* addr cloned to a split envelope */
+#define QS_DISCARDED 9 /* recipient discarded (EF_DISCARD) */
+#define QS_REPLACED 10 /* maplocaluser()/UserDB replaced */
+#define QS_REMOVED 11 /* removed (removefromlist()) */
+#define QS_DUPLICATE 12 /* duplicate suppressed */
+#define QS_INCLUDED 13 /* :include: delivery */
+
+/* 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_VERIFIED(s) ((s) == QS_VERIFIED)
+#define QS_IS_EXPANDED(s) ((s) == QS_EXPANDED)
+#define QS_IS_REMOVED(s) ((s) == QS_REMOVED)
+#define QS_IS_UNDELIVERED(s) ((s) == QS_OK || \
+ (s) == QS_QUEUEUP || \
+ (s) == QS_VERIFIED)
+#define QS_IS_SENDABLE(s) ((s) == QS_OK || \
+ (s) == QS_QUEUEUP)
+#define QS_IS_ATTEMPTED(s) ((s) == QS_QUEUEUP || \
+ (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 NULLADDR ((ADDRESS *) NULL)
+
+extern ADDRESS NullAddress; /* a null (template) address [main.c] */
/* functions */
+extern void cataddr __P((char **, char **, char *, int, int));
+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 ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *));
-extern ADDRESS *recipient __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
extern char **prescan __P((char *, int, char[], int, char **, u_char *));
-extern int rewrite __P((char **, int, int, ENVELOPE *));
+extern void printaddr __P((ADDRESS *, bool));
+extern ADDRESS *recipient __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
extern char *remotename __P((char *, MAILER *, int, int *, ENVELOPE *));
-extern ADDRESS *getctladdr __P((ADDRESS *));
+extern int rewrite __P((char **, int, int, ENVELOPE *));
extern bool sameaddr __P((ADDRESS *, ADDRESS *));
-extern bool emptyaddr __P((ADDRESS *));
-extern void printaddr __P((ADDRESS *, bool));
-extern void cataddr __P((char **, char **, char *, int, int));
extern int sendtolist __P((char *, ADDRESS *, ADDRESS **, int, ENVELOPE *));
+extern int removefromlist __P((char *, ADDRESS **, ENVELOPE *));
+extern void setsender __P((char *, ENVELOPE *, char **, int, bool));
+
/*
** Mailer definition structure.
** Every mailer known to the system is declared in this
@@ -230,7 +302,7 @@ struct mailer
char *m_mtatype; /* type of this MTA */
char *m_addrtype; /* type for addresses */
char *m_diagtype; /* type for diagnostics */
- BITMAP m_flags; /* status flags, see below */
+ BITMAP256 m_flags; /* status flags, see below */
short m_mno; /* mailer number internally */
short m_nice; /* niceness to run at (mostly for prog) */
char **m_argv; /* template argument vector */
@@ -241,92 +313,104 @@ struct mailer
char *m_eol; /* end of line string */
long m_maxsize; /* size limit on message to this mailer */
int m_linelimit; /* max # characters per line */
+ int m_maxdeliveries; /* max deliveries per mailer connection */
char *m_execdir; /* directory to chdir to before execv */
+ char *m_rootdir; /* directory to chroot to before execv */
uid_t m_uid; /* UID to run as */
gid_t m_gid; /* GID to run as */
char *m_defcharset; /* default character set */
+ time_t m_wait; /* timeout to wait for end */
+#if _FFR_DYNAMIC_TOBUF
+ int m_maxrcpt; /* max recipients per envelope client-side */
+#endif /* _FFR_DYNAMIC_TOBUF */
};
/* bits for m_flags */
-# define M_ESMTP 'a' /* run Extended SMTP protocol */
-# 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 */
-# define M_CANONICAL 'C' /* make addresses canonical "u@dom" */
-# define M_NOBRACKET 'd' /* never angle bracket envelope route-addrs */
+#define M_ESMTP 'a' /* run Extended SMTP protocol */
+#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 */
+#define M_CANONICAL 'C' /* make addresses canonical "u@dom" */
+#define M_NOBRACKET 'd' /* never angle bracket envelope route-addrs */
/* 'D' CF: include Date: */
-# define M_EXPENSIVE 'e' /* it costs to use this mailer.... */
-# define M_ESCFROM 'E' /* escape From lines to >From */
-# define M_FOPT 'f' /* mailer takes picky -f flag */
+#define M_EXPENSIVE 'e' /* it costs to use this mailer.... */
+#define M_ESCFROM 'E' /* escape From lines to >From */
+#define M_FOPT 'f' /* mailer takes picky -f flag */
/* 'F' CF: include From: or Resent-From: */
-# define M_NO_NULL_FROM 'g' /* sender of errors should be $g */
-# define M_HST_UPPER 'h' /* preserve host case distinction */
-# define M_PREHEAD 'H' /* MAIL11V3: preview headers */
-# define M_UDBENVELOPE 'i' /* do udbsender rewriting on envelope */
-# define M_INTERNAL 'I' /* SMTP to another sendmail site */
-# define M_UDBRECIPIENT 'j' /* do udbsender rewriting on recipient lines */
-# define M_NOLOOPCHECK 'k' /* don't check for loops in HELO command */
-# define M_CHUNKING 'K' /* CHUNKING: reserved for future use */
-# define M_LOCALMAILER 'l' /* delivery is to this host */
-# define M_LIMITS 'L' /* must enforce SMTP line limits */
-# define M_MUSER 'm' /* can handle multiple users at once */
+#define M_NO_NULL_FROM 'g' /* sender of errors should be $g */
+#define M_HST_UPPER 'h' /* preserve host case distinction */
+#define M_PREHEAD 'H' /* MAIL11V3: preview headers */
+#define M_UDBENVELOPE 'i' /* do udbsender rewriting on envelope */
+#define M_INTERNAL 'I' /* SMTP to another sendmail site */
+#define M_UDBRECIPIENT 'j' /* do udbsender rewriting on recipient lines */
+#define M_NOLOOPCHECK 'k' /* don't check for loops in HELO command */
+#define M_CHUNKING 'K' /* CHUNKING: reserved for future use */
+#define M_LOCALMAILER 'l' /* delivery is to this host */
+#define M_LIMITS 'L' /* must enforce SMTP line limits */
+#define M_MUSER 'm' /* can handle multiple users at once */
/* 'M' CF: include Message-Id: */
-# define M_NHDR 'n' /* don't insert From line */
-# define M_MANYSTATUS 'N' /* MAIL11V3: DATA returns multi-status */
-# define M_RUNASRCPT 'o' /* always run mailer as recipient */
-# define M_FROMPATH 'p' /* use reverse-path in MAIL FROM: */
+#define M_NHDR 'n' /* don't insert From line */
+#define M_MANYSTATUS 'N' /* MAIL11V3: DATA returns multi-status */
+#define M_RUNASRCPT 'o' /* always run mailer as recipient */
+#define M_FROMPATH 'p' /* use reverse-path in MAIL FROM: */
/* 'P' CF: include Return-Path: */
-# define M_VRFY250 'q' /* VRFY command returns 250 instead of 252 */
-# define M_ROPT 'r' /* mailer takes picky -r flag */
-# define M_SECURE_PORT 'R' /* try to send on a reserved TCP port */
-# define M_STRIPQ 's' /* strip quote chars from user/host */
-# define M_SPECIFIC_UID 'S' /* run as specific uid/gid */
-# define M_USR_UPPER 'u' /* preserve user case distinction */
-# define M_UGLYUUCP 'U' /* this wants an ugly UUCP from line */
-# define M_CONTENT_LEN 'v' /* add Content-Length: header (SVr4) */
+#define M_VRFY250 'q' /* VRFY command returns 250 instead of 252 */
+#define M_ROPT 'r' /* mailer takes picky -r flag */
+#define M_SECURE_PORT 'R' /* try to send on a reserved TCP port */
+#define M_STRIPQ 's' /* strip quote chars from user/host */
+#define M_SPECIFIC_UID 'S' /* run as specific uid/gid */
+#define M_USR_UPPER 'u' /* preserve user case distinction */
+#define M_UGLYUUCP 'U' /* this wants an ugly UUCP from line */
+#define M_CONTENT_LEN 'v' /* add Content-Length: header (SVr4) */
/* 'V' UIUC: !-relativize all addresses */
-# define M_HASPWENT 'w' /* check for /etc/passwd entry */
+#define M_HASPWENT 'w' /* check for /etc/passwd entry */
/* 'x' CF: include Full-Name: */
-# define M_XDOT 'X' /* use hidden-dot algorithm */
-# define M_LMTP 'z' /* run Local Mail Transport Protocol */
-# define M_NOMX '0' /* turn off MX lookups */
-# define M_NONULLS '1' /* don't send null bytes */
-# 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 */
-# define M_7BITS '7' /* use 7-bit path */
-# define M_8BITS '8' /* force "just send 8" behaviour */
-# define M_MAKE8BIT '9' /* convert 7 -> 8 bit if appropriate */
-# define M_CHECKINCLUDE ':' /* check for :include: files */
-# define M_CHECKPROG '|' /* check for |program addresses */
-# define M_CHECKFILE '/' /* check for /file addresses */
-# define M_CHECKUDB '@' /* user can be user database key */
-# define M_CHECKHDIR '~' /* SGI: check for valid home directory */
-
-EXTERN MAILER *Mailer[MAXMAILERS+1];
-
-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 */
+#define M_XDOT 'X' /* use hidden-dot algorithm */
+#define M_LMTP 'z' /* run Local Mail Transport Protocol */
+#define M_NOMX '0' /* turn off MX lookups */
+#define M_NONULLS '1' /* don't send null bytes */
+#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 */
+#define M_7BITS '7' /* use 7-bit path */
+#define M_8BITS '8' /* force "just send 8" behaviour */
+#define M_MAKE8BIT '9' /* convert 7 -> 8 bit if appropriate */
+#define M_CHECKINCLUDE ':' /* check for :include: files */
+#define M_CHECKPROG '|' /* check for |program addresses */
+#define M_CHECKFILE '/' /* check for /file addresses */
+#define M_CHECKUDB '@' /* user can be user database key */
+#define M_CHECKHDIR '~' /* SGI: check for valid home directory */
+#define M_HOLD '%' /* Hold delivery until ETRN/-qI/-qR/-qS */
+#define M_PLUS '+' /* Reserved: Used in mc for adding new flags */
+#define M_MINUS '-' /* Reserved: Used in mc for removing flags */
+
+/* functions */
+extern void initerrmailers __P((void));
+extern void makemailer __P((char *));
+
/*
** Information about currently open connections to mailers, or to
** hosts that we have looked up recently.
*/
-# define MCI struct mailer_con_info
+#define MCI struct mailer_con_info
MCI
{
- short mci_flags; /* flag bits, see below */
+ u_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 */
- off_t mci_contentlen; /* body length for Content-Length: */
+ int mci_deliveries; /* delivery attempts for connection */
long mci_maxsize; /* max size this server will accept */
+#if SFIO
+ Sfio_t *mci_in; /* input side of connection */
+ Sfio_t *mci_out; /* output side of connection */
+#else /* SFIO */
FILE *mci_in; /* input side of connection */
FILE *mci_out; /* output side of connection */
+#endif /* SFIO */
pid_t mci_pid; /* process id of subordinate proc */
char *mci_phase; /* SMTP phase string */
struct mailer *mci_mailer; /* ptr to the mailer for this conn */
@@ -335,25 +419,46 @@ MCI
char *mci_rstatus; /* SMTP status to be copied to addrs */
time_t mci_lastuse; /* last usage time */
FILE *mci_statfile; /* long term status file */
+ char *mci_heloname; /* name to use as HELO arg */
+#if SASL
+ bool mci_sasl_auth; /* authenticated? */
+ int mci_sasl_string_len;
+ char *mci_sasl_string; /* sasl reply string */
+ char *mci_saslcap; /* SASL list of mechanisms */
+ sasl_conn_t *mci_conn; /* SASL connection */
+#endif /* SASL */
+#if STARTTLS
+ SSL *mci_ssl; /* SSL connection */
+#endif /* STARTTLS */
};
/* flag bits */
-#define MCIF_VALID 0x0001 /* this entry is valid */
-#define MCIF_TEMP 0x0002 /* don't cache this connection */
-#define MCIF_CACHED 0x0004 /* currently in open cache */
-#define MCIF_ESMTP 0x0008 /* this host speaks ESMTP */
-#define MCIF_EXPN 0x0010 /* EXPN command supported */
-#define MCIF_SIZE 0x0020 /* SIZE option supported */
-#define MCIF_8BITMIME 0x0040 /* BODY=8BITMIME supported */
-#define MCIF_7BIT 0x0080 /* strip this message to 7 bits */
-#define MCIF_MULTSTAT 0x0100 /* MAIL11V3: handles MULT status */
-#define MCIF_INHEADER 0x0200 /* currently outputing header */
-#define MCIF_CVT8TO7 0x0400 /* convert from 8 to 7 bits */
-#define MCIF_DSN 0x0800 /* DSN extension supported */
-#define MCIF_8BITOK 0x1000 /* OK to send 8 bit characters */
-#define MCIF_CVT7TO8 0x2000 /* convert from 7 to 8 bits */
-#define MCIF_INMIME 0x4000 /* currently reading MIME header */
+#define MCIF_VALID 0x00000001 /* this entry is valid */
+#define MCIF_TEMP 0x00000002 /* don't cache this connection */
+#define MCIF_CACHED 0x00000004 /* currently in open cache */
+#define MCIF_ESMTP 0x00000008 /* this host speaks ESMTP */
+#define MCIF_EXPN 0x00000010 /* EXPN command supported */
+#define MCIF_SIZE 0x00000020 /* SIZE option supported */
+#define MCIF_8BITMIME 0x00000040 /* BODY=8BITMIME supported */
+#define MCIF_7BIT 0x00000080 /* strip this message to 7 bits */
+#define MCIF_MULTSTAT 0x00000100 /* MAIL11V3: handles MULT status */
+#define MCIF_INHEADER 0x00000200 /* currently outputing header */
+#define MCIF_CVT8TO7 0x00000400 /* convert from 8 to 7 bits */
+#define MCIF_DSN 0x00000800 /* DSN extension supported */
+#define MCIF_8BITOK 0x00001000 /* OK to send 8 bit characters */
+#define MCIF_CVT7TO8 0x00002000 /* convert from 7 to 8 bits */
+#define MCIF_INMIME 0x00004000 /* currently reading MIME header */
+#define MCIF_AUTH 0x00008000 /* AUTH= supported */
+#define MCIF_AUTHACT 0x00010000 /* SASL (AUTH) active */
+#define MCIF_ENHSTAT 0x00020000 /* ENHANCEDSTATUSCODES supported */
+#if STARTTLS
+#define MCIF_TLS 0x00100000 /* STARTTLS supported */
+#define MCIF_TLSACT 0x00200000 /* STARTTLS active */
+#define MCIF_EXTENS (MCIF_EXPN | MCIF_SIZE | MCIF_8BITMIME | MCIF_DSN | MCIF_8BITOK | MCIF_AUTH | MCIF_ENHSTAT | MCIF_TLS)
+#else /* STARTTLS */
+#define MCIF_EXTENS (MCIF_EXPN | MCIF_SIZE | MCIF_8BITMIME | MCIF_DSN | MCIF_8BITOK | MCIF_AUTH | MCIF_ENHSTAT)
+#endif /* STARTTLS */
/* states */
#define MCIS_CLOSED 0 /* no traffic on this connection */
@@ -365,20 +470,21 @@ MCI
#define MCIS_ERROR 6 /* I/O error on connection */
/* functions */
-extern MCI *mci_get __P((char *, MAILER *));
extern void mci_cache __P((MCI *));
-extern void mci_flush __P((bool, MCI *));
extern void mci_dump __P((MCI *, bool));
extern void mci_dump_all __P((bool));
-extern MCI **mci_scan __P((MCI *));
-extern int mci_traverse_persistent __P((int (*)(), char *));
+extern void mci_flush __P((bool, MCI *));
+extern MCI *mci_get __P((char *, MAILER *));
+extern int mci_lock_host __P((MCI *));
+extern bool mci_match __P((char *, MAILER *));
extern int mci_print_persistent __P((char *, char *));
extern int mci_purge_persistent __P((char *, char *));
-extern int mci_lock_host __P((MCI *));
+extern MCI **mci_scan __P((MCI *));
+extern void mci_setstat __P((MCI *, int, char *, char *));
+extern void mci_store_persistent __P((MCI *));
+extern int mci_traverse_persistent __P((int (*)(), char *));
extern void mci_unlock_host __P((MCI *));
-extern int mci_lock_host_statfile __P((MCI *));
-extern void mci_store_persistent __P((MCI *));
-extern int mci_read_persistent __P((FILE *, MCI *));
+
/*
** Header structure.
** This structure is used internally to store header items.
@@ -389,8 +495,9 @@ 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_short h_flags; /* status bits, see below */
- BITMAP h_mflags; /* m_flags bits needed */
+ u_char h_macro; /* include header if macro defined */
+ u_long h_flags; /* status bits, see below */
+ BITMAP256 h_mflags; /* m_flags bits needed */
};
typedef struct header HDR;
@@ -404,37 +511,64 @@ typedef struct header HDR;
struct hdrinfo
{
char *hi_field; /* the name of the field */
- u_short hi_flags; /* status bits, see below */
+ u_long hi_flags; /* status bits, see below */
char *hi_ruleset; /* validity check ruleset */
};
extern struct hdrinfo HdrInfo[];
/* bits for h_flags and hi_flags */
-# define H_EOH 0x0001 /* this field terminates header */
-# define H_RCPT 0x0002 /* contains recipient addresses */
-# define H_DEFAULT 0x0004 /* if another value is found, drop this */
-# define H_RESENT 0x0008 /* this address is a "Resent-..." address */
-# define H_CHECK 0x0010 /* check h_mflags against m_flags */
-# define H_ACHECK 0x0020 /* ditto, but always (not just default) */
-# define H_FORCE 0x0040 /* force this field, even if default */
-# define H_TRACE 0x0080 /* this field contains trace information */
-# define H_FROM 0x0100 /* this is a from-type field */
-# define H_VALID 0x0200 /* this field has a validated value */
-# define H_RECEIPTTO 0x0400 /* this field has return receipt info */
-# define H_ERRORSTO 0x0800 /* this field has error address info */
-# define H_CTE 0x1000 /* this field is a content-transfer-encoding */
-# define H_CTYPE 0x2000 /* this is a content-type field */
-# define H_BCC 0x4000 /* Bcc: header: strip value or delete */
-# define H_ENCODABLE 0x8000 /* field can be RFC 1522 encoded */
+#define H_EOH 0x00000001 /* field terminates header */
+#define H_RCPT 0x00000002 /* contains recipient addresses */
+#define H_DEFAULT 0x00000004 /* if another value is found, drop this */
+#define H_RESENT 0x00000008 /* this address is a "Resent-..." address */
+#define H_CHECK 0x00000010 /* check h_mflags against m_flags */
+#define H_ACHECK 0x00000020 /* ditto, but always (not just default) */
+#define H_FORCE 0x00000040 /* force this field, even if default */
+#define H_TRACE 0x00000080 /* this field contains trace information */
+#define H_FROM 0x00000100 /* this is a from-type field */
+#define H_VALID 0x00000200 /* this field has a validated value */
+#define H_RECEIPTTO 0x00000400 /* field has return receipt info */
+#define H_ERRORSTO 0x00000800 /* field has error address info */
+#define H_CTE 0x00001000 /* field is a content-transfer-encoding */
+#define H_CTYPE 0x00002000 /* this is a content-type field */
+#define H_BCC 0x00004000 /* Bcc: header: strip value or delete */
+#define H_ENCODABLE 0x00008000 /* field can be RFC 1522 encoded */
+#define H_STRIPCOMM 0x00010000 /* header check: strip comments */
+#define H_BINDLATE 0x00020000 /* only expand macros at deliver */
+#define H_USER 0x00040000 /* header came from the user/SMTP */
+
+/* bits for chompheader() */
+#define CHHDR_DEF 0x0001 /* default header */
+#define CHHDR_CHECK 0x0002 /* call ruleset for header */
+#define CHHDR_USER 0x0004 /* header from user */
/* functions */
-extern void addheader __P((char *, char *, HDR **));
-extern char *hvalue __P((char *, HDR *));
+extern void addheader __P((char *, char *, int, HDR **));
+extern u_long chompheader __P((char *, int, HDR **, ENVELOPE *));
extern void commaize __P((HDR *, char *, bool, MCI *, ENVELOPE *));
-extern void put_vanilla_header __P((HDR *, char *, MCI *));
+extern HDR *copyheader __P((HDR *));
extern void eatheader __P((ENVELOPE *, bool));
-extern int chompheader __P((char *, bool, HDR **, ENVELOPE *));
+extern char *hvalue __P((char *, HDR *));
+extern bool isheader __P((char *));
+extern void putfromline __P((MCI *, ENVELOPE *));
+extern void setupheaders __P((void));
+
+ /*
+** Performance monitoring
+*/
+
+#define TIMERS struct sm_timers
+
+TIMERS
+{
+ TIMER ti_overall; /* the whole process */
+};
+
+
+#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
@@ -468,11 +602,12 @@ struct envelope
/* function to put header of message */
void (*e_putbody)__P((MCI *, ENVELOPE *, char *));
/* function to put body of message */
- struct envelope *e_parent; /* the message this one encloses */
- struct envelope *e_sibling; /* the next envelope of interest */
+ 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; /* temporary file */
+ FILE *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 */
@@ -486,44 +621,53 @@ struct envelope
dev_t e_dfdev; /* df file's device, for crash recov */
ino_t e_dfino; /* df file's ino, for crash recovery */
char *e_macro[256]; /* macro definitions */
+ char *e_if_macros[2]; /* HACK: incoming interface info */
+ char *e_auth_param;
+ 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 */
};
/* values for e_flags */
-#define EF_OLDSTYLE 0x0000001 /* use spaces (not commas) in hdrs */
-#define EF_INQUEUE 0x0000002 /* this message is fully queued */
-#define EF_NO_BODY_RETN 0x0000004 /* omit message body on error */
-#define EF_CLRQUEUE 0x0000008 /* disk copy is no longer needed */
-#define EF_SENDRECEIPT 0x0000010 /* send a return receipt */
-#define EF_FATALERRS 0x0000020 /* fatal errors occured */
-#define EF_DELETE_BCC 0x0000040 /* delete Bcc: headers entirely */
-#define EF_RESPONSE 0x0000080 /* this is an error or return receipt */
-#define EF_RESENT 0x0000100 /* this message is being forwarded */
-#define EF_VRFYONLY 0x0000200 /* verify only (don't expand aliases) */
-#define EF_WARNING 0x0000400 /* warning message has been sent */
-#define EF_QUEUERUN 0x0000800 /* this envelope is from queue */
-#define EF_GLOBALERRS 0x0001000 /* treat errors as global */
-#define EF_PM_NOTIFY 0x0002000 /* send return mail to postmaster */
-#define EF_METOO 0x0004000 /* send to me too */
-#define EF_LOGSENDER 0x0008000 /* need to log the sender */
-#define EF_NORECEIPT 0x0010000 /* suppress all return-receipts */
-#define EF_HAS8BIT 0x0020000 /* at least one 8-bit char in body */
-#define EF_NL_NOT_EOL 0x0040000 /* don't accept raw NL as EOLine */
-#define EF_CRLF_NOT_EOL 0x0080000 /* don't accept CR-LF as EOLine */
-#define EF_RET_PARAM 0x0100000 /* RCPT command had RET argument */
-#define EF_HAS_DF 0x0200000 /* set when df file is instantiated */
-#define EF_IS_MIME 0x0400000 /* really is a MIME message */
-#define EF_DONT_MIME 0x0800000 /* never MIME this message */
-#define EF_DISCARD 0x1000000 /* discard the message */
-
-EXTERN ENVELOPE *CurEnv; /* envelope currently being processed */
+#define EF_OLDSTYLE 0x0000001L /* use spaces (not commas) in hdrs */
+#define EF_INQUEUE 0x0000002L /* this message is fully queued */
+#define EF_NO_BODY_RETN 0x0000004L /* omit message body on error */
+#define EF_CLRQUEUE 0x0000008L /* disk copy is no longer needed */
+#define EF_SENDRECEIPT 0x0000010L /* send a return receipt */
+#define EF_FATALERRS 0x0000020L /* fatal errors occurred */
+#define EF_DELETE_BCC 0x0000040L /* delete Bcc: headers entirely */
+#define EF_RESPONSE 0x0000080L /* this is an error or return receipt */
+#define EF_RESENT 0x0000100L /* this message is being forwarded */
+#define EF_VRFYONLY 0x0000200L /* verify only (don't expand aliases) */
+#define EF_WARNING 0x0000400L /* warning message has been sent */
+#define EF_QUEUERUN 0x0000800L /* this envelope is from queue */
+#define EF_GLOBALERRS 0x0001000L /* treat errors as global */
+#define EF_PM_NOTIFY 0x0002000L /* send return mail to postmaster */
+#define EF_METOO 0x0004000L /* send to me too */
+#define EF_LOGSENDER 0x0008000L /* need to log the sender */
+#define EF_NORECEIPT 0x0010000L /* suppress all return-receipts */
+#define EF_HAS8BIT 0x0020000L /* at least one 8-bit char in body */
+#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_IS_MIME 0x0400000L /* really is a MIME message */
+#define EF_DONT_MIME 0x0800000L /* never MIME this message */
+#define EF_DISCARD 0x1000000L /* discard the message */
+
+/* values for e_if_macros */
+#define EIF_ADDR 0 /* ${if_addr} */
/* functions */
-extern ENVELOPE *newenvelope __P((ENVELOPE *, ENVELOPE *));
-extern void dropenvelope __P((ENVELOPE *, bool));
extern void clearenvelope __P((ENVELOPE *, bool));
-
-extern void putheader __P((MCI *, HDR *, ENVELOPE *, int));
+extern void dropenvelope __P((ENVELOPE *, bool));
+extern ENVELOPE *newenvelope __P((ENVELOPE *, ENVELOPE *));
+extern void printenvflags __P((ENVELOPE *));
extern void putbody __P((MCI *, ENVELOPE *, char *));
+extern void putheader __P((MCI *, HDR *, ENVELOPE *, int));
+
/*
** Message priority classes.
**
@@ -554,8 +698,6 @@ struct priority
int pri_val; /* internal value for same */
};
-EXTERN struct priority Priorities[MAXPRIORITIES];
-EXTERN int NumPriorities; /* pointer into Priorities */
/*
** Rewrite rules.
*/
@@ -565,10 +707,9 @@ struct rewrite
char **r_lhs; /* pattern match */
char **r_rhs; /* substitution value */
struct rewrite *r_next;/* next in chain */
+ int r_line; /* rule line in sendmail.cf */
};
-EXTERN struct rewrite *RewriteRules[MAXRWSETS];
-
/*
** Special characters in rewriting rules.
** These are used internally only.
@@ -578,38 +719,38 @@ EXTERN struct rewrite *RewriteRules[MAXRWSETS];
*/
/* 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 ((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 */
/* 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 ((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 */
/* 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 ((u_char)0232) /* conditional if-then */
+#define CONDELSE ((u_char)0233) /* conditional else */
+#define CONDFI ((u_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 ((u_char)0235) /* hostname lookup begin */
+#define HOSTEND ((u_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 ((u_char)0205) /* generalized lookup begin */
+#define LOOKUPEND ((u_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 ((u_char)0201) /* macro expansion */
+#define MACRODEXPAND ((u_char)0202) /* deferred macro expansion */
/* to make the code clearer */
-# define MATCHZERO CANONHOST
+#define MATCHZERO CANONHOST
/* external <==> internal mapping table */
struct metamac
@@ -619,14 +760,20 @@ struct metamac
};
/* values for macros with external names only */
-# define MID_OPMODE 0202 /* operation mode */
+#define MID_OPMODE 0202 /* operation mode */
/* functions */
-extern void expand __P((char *, char *, size_t, ENVELOPE *));
extern void define __P((int, char *, ENVELOPE *));
-extern char *macvalue __P((int, ENVELOPE *));
-extern char *macname __P((int));
+extern void expand __P((char *, char *, size_t, ENVELOPE *));
extern int macid __P((char *, char **));
+extern char *macname __P((int));
+extern char *macvalue __P((int, ENVELOPE *));
+extern int rscheck __P((char *, char *, char *, ENVELOPE *, bool, bool, int));
+extern 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.
**
@@ -650,6 +797,11 @@ NAMECANON
/* values for nc_flags */
#define NCF_VALID 0x0001 /* entry valid */
+
+/* functions */
+extern bool getcanonname __P((char *, int, bool));
+extern int getmxrr __P((char *, char **, u_short *, bool, int *));
+
/*
** Mapping functions
**
@@ -657,9 +809,9 @@ NAMECANON
** (albeit not the implementation) comes from IDA sendmail.
*/
-# define MAPCLASS struct _mapclass
-# define MAP struct _map
-# define MAXMAPACTIONS 3 /* size of map_actions array */
+#define MAPCLASS struct _mapclass
+#define MAP struct _map
+#define MAXMAPACTIONS 5 /* size of map_actions array */
/*
@@ -679,6 +831,7 @@ MAP
u_char map_keycolno; /* key column number */
u_char map_valcolno; /* value column number */
char map_coldelim; /* column delimiter */
+ char map_spacesub; /* spacesub */
char *map_app; /* to append to successful matches */
char *map_tapp; /* to append to "tempfail" matches */
char *map_domain; /* the (nominal) NIS domain */
@@ -691,31 +844,43 @@ MAP
short map_return[MAXMAPACTIONS]; /* return bitmaps for stacked maps */
};
+
/* bit values for map_mflags */
-# define MF_VALID 0x00000001 /* this entry is valid */
-# define MF_INCLNULL 0x00000002 /* include null byte in key */
-# define MF_OPTIONAL 0x00000004 /* don't complain if map not found */
-# define MF_NOFOLDCASE 0x00000008 /* don't fold case in keys */
-# define MF_MATCHONLY 0x00000010 /* don't use the map value */
-# define MF_OPEN 0x00000020 /* this entry is open */
-# define MF_WRITABLE 0x00000040 /* open for writing */
-# define MF_ALIAS 0x00000080 /* this is an alias file */
-# define MF_TRY0NULL 0x00000100 /* try with no null byte */
-# define MF_TRY1NULL 0x00000200 /* try with the null byte */
-# define MF_LOCKED 0x00000400 /* this map is currently locked */
-# 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 */
-# define MF_APPEND 0x00008000 /* append new entry on rebuiled */
-# 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_VALID 0x00000001 /* this entry is valid */
+#define MF_INCLNULL 0x00000002 /* include null byte in key */
+#define MF_OPTIONAL 0x00000004 /* don't complain if map not found */
+#define MF_NOFOLDCASE 0x00000008 /* don't fold case in keys */
+#define MF_MATCHONLY 0x00000010 /* don't use the map value */
+#define MF_OPEN 0x00000020 /* this entry is open */
+#define MF_WRITABLE 0x00000040 /* open for writing */
+#define MF_ALIAS 0x00000080 /* this is an alias file */
+#define MF_TRY0NULL 0x00000100 /* try with no null byte */
+#define MF_TRY1NULL 0x00000200 /* try with the null byte */
+#define MF_LOCKED 0x00000400 /* this map is currently locked */
+#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 */
+#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_SHARED 0x00400000 /* map connection is shared */
+
+#define DYNOPENMAP(map) if (!bitset(MF_OPEN, (map)->map_mflags)) \
+ { \
+ if (!openmap(map)) \
+ return NULL; \
+ }
+
/* indices for map_actions */
-# define MA_NOTFOUND 0 /* member map returned "not found" */
-# define MA_UNAVAIL 1 /* member map is not available */
-# define MA_TRYAGAIN 2 /* member map returns temp failure */
+#define MA_NOTFOUND 0 /* member map returned "not found" */
+#define MA_UNAVAIL 1 /* member map is not available */
+#define MA_TRYAGAIN 2 /* member map returns temp failure */
/*
** The class of a map -- essentially the functions to call
@@ -745,9 +910,134 @@ MAPCLASS
#define MCF_OPTFILE 0x0008 /* file name is optional */
/* functions */
-extern char *map_rewrite __P((MAP *, const char *, size_t, char **));
+extern void closemaps __P((void));
+extern bool impl_map_open __P((MAP *, int));
+extern void initmaps __P((void));
extern MAP *makemapentry __P((char *));
-extern void initmaps __P((bool, ENVELOPE *));
+extern void maplocaluser __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
+extern char *map_rewrite __P((MAP *, const char *, size_t, char **));
+#if NETINFO
+extern char *ni_propval __P((char *, char *, char *, char *, int));
+#endif /* NETINFO */
+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 *));
+#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;
+};
+
+typedef struct ldapmap_struct LDAPMAP_STRUCT;
+
+/* struct defining LDAP Auth Methods */
+struct lamvalues
+{
+ char *lam_name; /* name of LDAP auth method */
+ int lam_code; /* numeric code */
+};
+
+/* struct defining LDAP Alias Dereferencing */
+struct ladvalues
+{
+ char *lad_name; /* name of LDAP alias dereferencing method */
+ int lad_code; /* numeric code */
+};
+
+/* struct defining LDAP Search Scope */
+struct lssvalues
+{
+ char *lss_name; /* name of LDAP search scope */
+ int lss_code; /* numeric code */
+};
+
+/* functions */
+extern bool ldapmap_parseargs __P((MAP *, char *));
+extern void ldapmap_set_defaults __P((char *));
+#endif /* LDAPMAP */
+
+ /*
+** PH related items
+*/
+
+#ifdef PH_MAP
+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;
+};
+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 */
+#endif /* ! PROC_LIST_SEG */
+
+/* process types */
+#define PROC_NONE 0
+#define PROC_DAEMON 1
+#define PROC_DAEMON_CHILD 2
+#define PROC_QUEUE 3
+#define PROC_QUEUE_CHILD 3
+#define PROC_CONTROL 4
+#define PROC_CONTROL_CHILD 5
+
+/* functions */
+extern void proc_list_add __P((pid_t, char *, 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_probe __P((void));
+extern void proc_list_set __P((pid_t, char *));
+
/*
** Symbol table definitions
*/
@@ -760,7 +1050,7 @@ struct symtab
struct symtab *s_next; /* pointer to next in chain */
union
{
- BITMAP sv_class; /* bit-map of word classes */
+ BITMAP256 sv_class; /* bit-map of word classes */
ADDRESS *sv_addr; /* pointer to address header */
MAILER *sv_mailer; /* pointer to mailer */
char *sv_alias; /* alias */
@@ -773,47 +1063,67 @@ struct symtab
int sv_ruleset; /* ruleset index */
struct hdrinfo sv_header; /* header metainfo */
char *sv_service[MAXMAPSTACK]; /* service switch */
+#ifdef LDAPMAP
+ LDAP *sv_ldap; /* LDAP connection */
+#endif /* LDAPMAP */
+#if _FFR_MILTER
+ struct milter *sv_milter; /* milter filter name */
+#endif /* _FFR_MILTER */
} s_value;
};
typedef struct symtab STAB;
/* symbol types */
-# define ST_UNDEF 0 /* undefined type */
-# define ST_CLASS 1 /* class map */
-# define ST_ADDRESS 2 /* an address in parsed format */
-# define ST_MAILER 3 /* a mailer header */
-# define ST_ALIAS 4 /* an alias */
-# define ST_MAPCLASS 5 /* mapping function class */
-# define ST_MAP 6 /* mapping function */
-# define ST_HOSTSIG 7 /* host signature */
-# define ST_NAMECANON 8 /* cached canonical name */
-# define ST_MACRO 9 /* macro name to id mapping */
-# define ST_RULESET 10 /* ruleset index */
-# define ST_SERVICE 11 /* service switch entry */
-# define ST_HEADER 12 /* special header flags */
-# define ST_MCI 16 /* mailer connection info (offset) */
-
-# define s_class s_value.sv_class
-# define s_address s_value.sv_addr
-# define s_mailer s_value.sv_mailer
-# define s_alias s_value.sv_alias
-# define s_mci s_value.sv_mci
-# define s_mapclass s_value.sv_mapclass
-# define s_hostsig s_value.sv_hostsig
-# define s_map s_value.sv_map
-# define s_namecanon s_value.sv_namecanon
-# define s_macro s_value.sv_macro
-# define s_ruleset s_value.sv_ruleset
-# define s_service s_value.sv_service
-# define s_header s_value.sv_header
-
-extern STAB *stab __P((char *, int, int));
-extern void stabapply __P((void (*)(STAB *, int), int));
+#define ST_UNDEF 0 /* undefined type */
+#define ST_CLASS 1 /* class map */
+#define ST_ADDRESS 2 /* an address in parsed format */
+#define ST_MAILER 3 /* a mailer header */
+#define ST_ALIAS 4 /* an alias */
+#define ST_MAPCLASS 5 /* mapping function class */
+#define ST_MAP 6 /* mapping function */
+#define ST_HOSTSIG 7 /* host signature */
+#define ST_NAMECANON 8 /* cached canonical name */
+#define ST_MACRO 9 /* macro name to id mapping */
+#define ST_RULESET 10 /* ruleset index */
+#define ST_SERVICE 11 /* service switch entry */
+#define ST_HEADER 12 /* special header flags */
+#ifdef LDAPMAP
+# define ST_LDAP 13 /* LDAP connection */
+#endif /* LDAPMAP */
+#if _FFR_MILTER
+# define ST_MILTER 14 /* milter filter */
+#endif /* _FFR_MILTER */
+#define ST_MCI 16 /* mailer connection info (offset) */
+
+#define s_class s_value.sv_class
+#define s_address s_value.sv_addr
+#define s_mailer s_value.sv_mailer
+#define s_alias s_value.sv_alias
+#define s_mci s_value.sv_mci
+#define s_mapclass s_value.sv_mapclass
+#define s_hostsig s_value.sv_hostsig
+#define s_map s_value.sv_map
+#define s_namecanon s_value.sv_namecanon
+#define s_macro s_value.sv_macro
+#define s_ruleset s_value.sv_ruleset
+#define s_service s_value.sv_service
+#define s_header s_value.sv_header
+#ifdef LDAPMAP
+# define s_ldap s_value.sv_ldap
+#endif /* LDAPMAP */
+#if _FFR_MILTER
+# define s_milter s_value.sv_milter
+#endif /* _FFR_MILTER */
/* opcodes to stab */
-# define ST_FIND 0 /* find entry */
-# define ST_ENTER 1 /* enter if not there */
+#define ST_FIND 0 /* find entry */
+#define ST_ENTER 1 /* enter if not there */
+
+/* functions */
+extern STAB *stab __P((char *, int, int));
+extern void stabapply __P((void (*)(STAB *, int), int));
+
/*
** STRUCT EVENT -- event queue.
**
@@ -835,11 +1145,11 @@ struct event
typedef struct event EVENT;
-EXTERN EVENT *EventQueue; /* head of event queue */
-
/* functions */
-extern EVENT *setevent __P((time_t, void(*)(), int));
extern void clrevent __P((EVENT *));
+extern void clear_events __P((void));
+extern EVENT *setevent __P((time_t, void(*)(), int));
+
/*
** Operation, send, error, and MIME modes
**
@@ -856,8 +1166,6 @@ extern void clrevent __P((EVENT *));
** The error mode tells how to return errors.
*/
-EXTERN char OpMode; /* operation mode, see below */
-
#define MD_DELIVER 'm' /* be a mail sender */
#define MD_SMTP 's' /* run SMTP on standard input */
#define MD_ARPAFTP 'a' /* obsolete ARPANET mode (Grey Book) */
@@ -870,6 +1178,7 @@ EXTERN char OpMode; /* operation mode, see below */
#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 */
/* values for e_sendmode -- send modes */
#define SM_DELIVER 'i' /* interactive delivery */
@@ -878,9 +1187,12 @@ EXTERN char OpMode; /* operation mode, see below */
#define SM_DEFER 'd' /* defer map lookups as well as queue */
#define SM_VERIFY 'v' /* verify only (used internally) */
+
/* used only as a parameter to sendall */
#define SM_DEFAULT '\0' /* unspecified, use SendMode */
+/* functions */
+extern void set_delivery_mode __P((int, ENVELOPE *));
/* values for e_errormode -- error handling modes */
#define EM_PRINT 'p' /* print errors */
@@ -890,25 +1202,13 @@ EXTERN char OpMode; /* operation mode, see below */
#define EM_QUIET 'q' /* don't print messages (stat only) */
-/* MIME processing mode */
-EXTERN int MimeMode;
-
/* bit values for MimeMode */
#define MM_CVTMIME 0x0001 /* convert 8 to 7 bit MIME */
#define MM_PASS8BIT 0x0002 /* just send 8 bit data blind */
#define MM_MIME8BIT 0x0004 /* convert 8-bit data to MIME */
-/* queue sorting order algorithm */
-EXTERN int QueueSortOrder;
-
-#define QS_BYPRIORITY 0 /* sort by message priority */
-#define QS_BYHOST 1 /* sort by first host name */
-#define QS_BYTIME 2 /* sort by submission time */
-
/* how to handle messages without any recipient addresses */
-EXTERN int NoRecipientAction;
-
#define NRA_NO_ACTION 0 /* just leave it as is */
#define NRA_ADD_TO 1 /* add To: header */
#define NRA_ADD_APPARENTLY_TO 2 /* add Apparently-To: header */
@@ -921,10 +1221,6 @@ EXTERN int NoRecipientAction;
#define PXLF_MAPFROM 0x0001 /* map From_ to >From_ */
#define PXLF_STRIP8BIT 0x0002 /* strip 8th bit */
#define PXLF_HEADER 0x0004 /* map newlines in headers */
- /*
-** Additional definitions
-*/
-
/*
** Privacy flags
@@ -939,17 +1235,20 @@ EXTERN int NoRecipientAction;
#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_NOETRN 0x0080 /* disallow ETRN command entirely */
#define PRIV_NOVERB 0x0100 /* disallow VERB command entirely */
#define PRIV_RESTRICTMAILQ 0x1000 /* restrict mailq command */
#define PRIV_RESTRICTQRUN 0x2000 /* restrict queue run */
-#define PRIV_GOAWAY 0x0fff /* don't give no info, anyway, anyhow */
+#define PRIV_NOETRN 0x4000 /* disallow ETRN command entirely */
+#define PRIV_NOBODYRETN 0x8000 /* do not return bodies on bounces */
+
+/* don't give no info, anyway, anyhow */
+#define PRIV_GOAWAY (0x0fff & ~PRIV_NORECEIPTS)
/* struct defining such things */
struct prival
{
char *pv_name; /* name of privacy flag */
- int pv_flag; /* numeric level */
+ u_short pv_flag; /* numeric level */
};
@@ -968,42 +1267,7 @@ struct prival
/*
-** Flags passed to safefile/safedirpath.
-*/
-
-#define SFF_ANYFILE 0 /* no special restrictions */
-#define SFF_MUSTOWN 0x0001 /* user must own this file */
-#define SFF_NOSLINK 0x0002 /* file cannot be a symbolic link */
-#define SFF_ROOTOK 0x0004 /* ok for root to own this file */
-#define SFF_RUNASREALUID 0x0008 /* if no ctladdr, run as real uid */
-#define SFF_NOPATHCHECK 0x0010 /* don't bother checking dir path */
-#define SFF_SETUIDOK 0x0020 /* setuid files are ok */
-#define SFF_CREAT 0x0040 /* ok to create file if necessary */
-#define SFF_REGONLY 0x0080 /* regular files only */
-#define SFF_SAFEDIRPATH 0x0100 /* no writable directories allowed */
-#define SFF_NOHLINK 0x0200 /* file cannot have hard links */
-#define SFF_NOWLINK 0x0400 /* links only in non-writable dirs */
-#define SFF_NOGWFILES 0x0800 /* disallow world writable files */
-#define SFF_NOWWFILES 0x1000 /* disallow group writable files */
-
-/* flags that are actually specific to safeopen/safefopen/dfopen */
-#define SFF_OPENASROOT 0x2000 /* open as root instead of real user */
-#define SFF_NOLOCK 0x4000 /* don't lock the file */
-
-/* pseudo-flags */
-#define SFF_NOLINK (SFF_NOHLINK|SFF_NOSLINK)
-
-/* functions */
-extern int safefile __P((char *, UID_T, GID_T, char *, int, int, struct stat *));
-extern int safedirpath __P((char *, UID_T, GID_T, char *, int));
-extern int safeopen __P((char *, int, int, int));
-extern FILE *safefopen __P((char *, int, int, int));
-extern int dfopen __P((char *, int, int, int));
-extern bool filechanged __P((char *, int, struct stat *));
-
-
-/*
-** Flags passed to mime8to7.
+** Flags passed to mime8to7 and putheader.
*/
#define M87F_OUTER 0 /* outer context */
@@ -1011,6 +1275,9 @@ extern bool filechanged __P((char *, int, struct stat *));
#define M87F_DIGEST 0x0002 /* processing multipart/digest */
#define M87F_NO8TO7 0x0004 /* don't do 8->7 bit conversions */
+/* functions */
+extern void mime7to8 __P((MCI *, HDR *, ENVELOPE *));
+extern int mime8to7 __P((MCI *, HDR *, ENVELOPE *, char **, int));
/*
** Flags passed to returntosender.
@@ -1020,45 +1287,96 @@ extern bool filechanged __P((char *, int, struct stat *));
#define RTSF_SEND_BODY 0x0001 /* include body of message in return */
#define RTSF_PM_BOUNCE 0x0002 /* this is a postmaster bounce */
+/* functions */
+extern int returntosender __P((char *, ADDRESS *, int, ENVELOPE *));
/*
** Regular UNIX sockaddrs are too small to handle ISO addresses, so
** we are forced to declare a supertype here.
*/
-# if NETINET || NETUNIX || NETISO || NETNS || NETX25
+#if NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25
union bigsockaddr
{
struct sockaddr sa; /* general version */
-#if NETUNIX
+# if NETUNIX
struct sockaddr_un sunix; /* UNIX family */
-#endif
-#if NETINET
+# endif /* NETUNIX */
+# if NETINET
struct sockaddr_in sin; /* INET family */
-#endif
-#if NETISO
+# endif /* NETINET */
+# if NETINET6
+ struct sockaddr_in6 sin6; /* INET/IPv6 */
+# endif /* NETINET6 */
+# if NETISO
struct sockaddr_iso siso; /* ISO family */
-#endif
-#if NETNS
+# endif /* NETISO */
+# if NETNS
struct sockaddr_ns sns; /* XNS family */
-#endif
-#if NETX25
+# endif /* NETNS */
+# if NETX25
struct sockaddr_x25 sx25; /* X.25 family */
-#endif
+# endif /* NETX25 */
};
-#define SOCKADDR union bigsockaddr
-
-EXTERN SOCKADDR RealHostAddr; /* address of host we are talking to */
+# define SOCKADDR union bigsockaddr
-extern char *hostnamebyanyaddr __P((SOCKADDR *));
+/* functions */
extern char *anynet_ntoa __P((SOCKADDR *));
+# if NETINET6
+extern char *anynet_ntop __P((struct in6_addr *, char *, size_t));
+# endif /* NETINET6 */
+extern char *hostnamebyanyaddr __P((SOCKADDR *));
# if DAEMON
extern char *validate_connection __P((SOCKADDR *, char *, ENVELOPE *));
-# endif
+# endif /* DAEMON */
+
+#endif /* NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25 */
+
+#if _FFR_MILTER
+ /*
+** Mail Filters (milter)
+*/
+
+#include <libmilter/milter.h>
+
+#define SMFTO_WRITE 0 /* Timeout for sending information */
+#define SMFTO_READ 1 /* Timeout waiting for a response */
+#define SMFTO_EOM 2 /* Timeout for ACK/NAK to EOM */
+
+#define SMFTO_NUM_TO 3 /* Total number of timeouts */
+
+struct milter
+{
+ char *mf_name; /* filter name */
+ BITMAP256 mf_flags; /* MTA flags */
+ u_long mf_fvers; /* filter version */
+ u_long mf_fflags; /* filter flags */
+ u_long mf_pflags; /* protocol flags */
+ char *mf_conn; /* connection info */
+ int mf_sock; /* connected socket */
+ char mf_state; /* state of filter */
+ time_t mf_timeout[SMFTO_NUM_TO]; /* timeouts */
+};
+
+/* MTA flags */
+# define SMF_REJECT 'R' /* Reject connection on filter fail */
+# define SMF_TEMPFAIL 'T' /* tempfail connection on failure */
-#endif
+/* states */
+# define SMFS_CLOSED 'C' /* closed for all further actions */
+# define SMFS_OPEN 'O' /* connected to remote milter filter */
+# define SMFS_INMSG 'M' /* currently servicing a message */
+# define SMFS_DONE 'D' /* done with current message */
+# define SMFS_ERROR 'E' /* error state */
+# define SMFS_READY 'R' /* ready for action */
+
+/* 32-bit type used by milter */
+typedef SM_INT32 mi_int32;
+EXTERN struct milter *InputFilters[MAXFILTERS];
+EXTERN char *InputFilterList;
+#endif /* _FFR_MILTER */
/*
** Vendor codes
@@ -1071,7 +1389,7 @@ extern char *validate_connection __P((SOCKADDR *, char *, ENVELOPE *));
** -DVENDOR_DEFAULT=VENDOR_xxx
** in the Makefile.
**
-** Vendors should apply to sendmail@CS.Berkeley.EDU for
+** Vendors should apply to sendmail@sendmail.org for
** unique vendor codes.
*/
@@ -1081,57 +1399,10 @@ extern char *validate_connection __P((SOCKADDR *, char *, ENVELOPE *));
#define VENDOR_IBM 4 /* IBM specific config syntax */
#define VENDOR_SENDMAIL 5 /* Sendmail, Inc. specific config syntax */
-EXTERN int VendorCode; /* vendor-specific operation enhancements */
-
/* prototypes for vendor-specific hook routines */
-extern void vendor_set_uid __P((UID_T));
extern void vendor_daemon_setup __P((ENVELOPE *));
+extern void vendor_set_uid __P((UID_T));
-/*
-** DontBlameSendmail options
-**
-** Hopefully nobody uses these.
-*/
-#define DBS_SAFE 0
-#define DBS_ASSUMESAFECHOWN 0x00000001
-#define DBS_GROUPWRITABLEDIRPATHSAFE 0x00000002
-#define DBS_GROUPWRITABLEFORWARDFILESAFE 0x00000004
-#define DBS_GROUPWRITABLEINCLUDEFILESAFE 0x00000008
-#define DBS_GROUPWRITABLEALIASFILE 0x00000010
-#define DBS_WORLDWRITABLEALIASFILE 0x00000020
-#define DBS_FORWARDFILEINUNSAFEDIRPATH 0x00000040
-#define DBS_MAPINUNSAFEDIRPATH 0x00000080
-#define DBS_LINKEDALIASFILEINWRITABLEDIR 0x00000100
-#define DBS_LINKEDCLASSFILEINWRITABLEDIR 0x00000200
-#define DBS_LINKEDFORWARDFILEINWRITABLEDIR 0x00000400
-#define DBS_LINKEDINCLUDEFILEINWRITABLEDIR 0x00000800
-#define DBS_LINKEDMAPINWRITABLEDIR 0x00001000
-#define DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR 0x00002000
-#define DBS_FILEDELIVERYTOHARDLINK 0x00004000
-#define DBS_FILEDELIVERYTOSYMLINK 0x00008000
-#define DBS_WRITEMAPTOHARDLINK 0x00010000
-#define DBS_WRITEMAPTOSYMLINK 0x00020000
-#define DBS_WRITESTATSTOHARDLINK 0x00040000
-#define DBS_WRITESTATSTOSYMLINK 0x00080000
-#define DBS_FORWARDFILEINGROUPWRITABLEDIRPATH 0x00100000
-#define DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH 0x00200000
-#define DBS_CLASSFILEINUNSAFEDIRPATH 0x00400000
-#define DBS_ERRORHEADERINUNSAFEDIRPATH 0x00800000
-#define DBS_HELPFILEINUNSAFEDIRPATH 0x01000000
-#define DBS_FORWARDFILEINUNSAFEDIRPATHSAFE 0x02000000
-#define DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE 0x04000000
-#define DBS_RUNPROGRAMINUNSAFEDIRPATH 0x08000000
-#define DBS_RUNWRITABLEPROGRAM 0x10000000
-#define DBS_INCLUDEFILEINUNSAFEDIRPATH 0x20000000
-
-/* struct defining such things */
-struct dbsval
-{
- char *dbs_name; /* name of DontBlameSendmail flag */
- long dbs_flag; /* numeric level */
-};
-
-EXTERN long DontBlameSendmail; /* DontBlameSendmail option bits */
/*
** Terminal escape codes.
@@ -1145,159 +1416,120 @@ struct termescape
char *te_rv_off; /* turn reverse-video off */
};
-EXTERN struct termescape TermEscape;
-
+ /*
+** Additional definitions
+*/
-/*
-** Error return from inet_addr(3), in case not defined in /usr/include.
+/* d_flags, see daemon.c */
+/* generic 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) */
+#define D_FQRCPT 'r' /* fq recipient address required (cf) */
+#define D_UNQUALOK 'u' /* unqualified address is ok (cf) */
+#define D_NOCANON 'C' /* no canonification (cf) */
+#define D_NOETRN 'E' /* no ETRN (MSA) */
+#define D_ETRNONLY ((char)0x01) /* allow only ETRN (disk low) */
+
+/* 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
*/
-#ifndef INADDR_NONE
-# define INADDR_NONE 0xffffffff
-#endif
+/* 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 */
+
+#if STARTTLS
/*
-** Global variables.
+** TLS
*/
-EXTERN bool FromFlag; /* if set, "From" person is explicit */
-EXTERN bool MeToo; /* send to the sender also */
-EXTERN bool IgnrDot; /* don't let dot end messages */
-EXTERN bool SaveFrom; /* save leading "From" lines */
-EXTERN bool GrabTo; /* if set, get recipients from msg */
-EXTERN bool SuprErrs; /* set if we are suppressing errors */
-EXTERN bool HoldErrs; /* only output errors to transcript */
-EXTERN bool NoConnect; /* don't connect to non-local mailers */
-EXTERN bool SuperSafe; /* be extra careful, even if expensive */
-EXTERN bool ForkQueueRuns; /* fork for each job when running the queue */
-EXTERN bool AutoRebuild; /* auto-rebuild the alias database as needed */
-EXTERN bool CheckAliases; /* parse addresses during newaliases */
-EXTERN bool NoAlias; /* suppress aliasing */
-EXTERN bool UseNameServer; /* using DNS -- interpret h_errno & MX RRs */
-EXTERN bool UseHesiod; /* using Hesiod -- interpret Hesiod errors */
-EXTERN bool SevenBitInput; /* force 7-bit data on input */
-EXTERN bool HasEightBits; /* has at least one eight bit input byte */
-EXTERN bool ConfigFileRead; /* configuration file has been read */
-EXTERN time_t SafeAlias; /* interval to wait until @:@ in alias file */
-EXTERN FILE *InChannel; /* input connection */
-EXTERN FILE *OutChannel; /* output connection */
-EXTERN char *RealUserName; /* real user name of caller */
-EXTERN uid_t RealUid; /* real uid of caller */
-EXTERN gid_t RealGid; /* real gid of caller */
-EXTERN uid_t DefUid; /* default uid to run as */
-EXTERN gid_t DefGid; /* default gid to run as */
-EXTERN char *DefUser; /* default user to run as (from DefUid) */
-EXTERN uid_t TrustedUid; /* uid of trusted user for files and startup */
-EXTERN MODE_T OldUmask; /* umask when sendmail starts up */
-EXTERN int Verbose; /* set if blow-by-blow desired */
-EXTERN int Errors; /* set if errors (local to single pass) */
-EXTERN int ExitStat; /* exit status code */
-EXTERN int LineNumber; /* line number in current input */
-EXTERN int LogLevel; /* level of logging to perform */
-EXTERN int FileMode; /* mode on files */
-EXTERN int QueueLA; /* load average starting forced queueing */
-EXTERN int RefuseLA; /* load average refusing connections are */
-EXTERN int CurrentLA; /* current load average */
-EXTERN long QueueFactor; /* slope of queue function */
-EXTERN time_t QueueIntvl; /* intervals between running the queue */
-EXTERN char *HelpFile; /* location of SMTP help file */
-EXTERN char *ErrMsgFile; /* file to prepend to all error messages */
-EXTERN char *StatFile; /* location of statistics summary */
-EXTERN char *QueueDir; /* location of queue directory */
-EXTERN char *FileName; /* name to print on error messages */
-EXTERN char *SmtpPhase; /* current phase in SMTP processing */
-EXTERN char *MyHostName; /* name of this host for SMTP messages */
-EXTERN char *RealHostName; /* name of host we are talking to */
-EXTERN char *CurHostName; /* current host we are dealing with */
-EXTERN jmp_buf TopFrame; /* branch-to-top-of-loop-on-error frame */
-EXTERN bool QuickAbort; /* .... but only if we want a quick abort */
-EXTERN bool OnlyOneError; /* .... or only want to give one SMTP reply */
-EXTERN bool LogUsrErrs; /* syslog user errors (e.g., SMTP RCPT cmd) */
-EXTERN bool SendMIMEErrors; /* send error messages in MIME format */
-EXTERN bool MatchGecos; /* look for user names in gecos field */
-EXTERN bool UseErrorsTo; /* use Errors-To: header (back compat) */
-EXTERN bool TryNullMXList; /* if we are the best MX, try host directly */
-EXTERN bool InChild; /* true if running in an SMTP subprocess */
-EXTERN bool DisConnected; /* running with OutChannel redirected to xf */
-EXTERN bool ColonOkInAddr; /* single colon legal in address */
-EXTERN bool HasWildcardMX; /* don't use MX records when canonifying */
-EXTERN char SpaceSub; /* substitution for <lwsp> */
-EXTERN int PrivacyFlags; /* privacy flags */
-EXTERN char *ConfFile; /* location of configuration file [conf.c] */
-EXTERN char *PidFile; /* location of proc id file [conf.c] */
-EXTERN char *ControlSocketName; /* control socket filename [control.c] */
-extern ADDRESS NullAddress; /* a null (template) address [main.c] */
-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 */
-EXTERN char *UdbSpec; /* user database source spec */
-EXTERN int MaxHopCount; /* max # of hops until bounce */
-EXTERN int ConfigLevel; /* config file level */
-EXTERN char *TimeZoneSpec; /* override time zone specification */
-EXTERN char *ForwardPath; /* path to search for .forward files */
-EXTERN long MinBlocksFree; /* min # of blocks free on queue fs */
-EXTERN char *FallBackMX; /* fall back MX host */
-EXTERN long MaxMessageSize; /* advertised max size we will accept */
-EXTERN time_t MinQueueAge; /* min delivery interval */
-EXTERN time_t DialDelay; /* delay between dial-on-demand tries */
-EXTERN char *SafeFileEnv; /* chroot location for file delivery */
-EXTERN char *HostsFile; /* path to /etc/hosts file */
-EXTERN char *HostStatDir; /* location of host status information */
-EXTERN int MaxQueueRun; /* maximum number of jobs in one queue run */
-EXTERN int MaxChildren; /* maximum number of daemonic children */
-EXTERN int CurChildren; /* current number of daemonic children */
-EXTERN char *SmtpGreeting; /* SMTP greeting message (old $e macro) */
-EXTERN char *UnixFromLine; /* UNIX From_ line (old $l macro) */
-EXTERN char *OperatorChars; /* operators (old $o macro) */
-EXTERN bool DontInitGroups; /* avoid initgroups() because of NIS cost */
-EXTERN int DefaultNotify; /* default DSN notification flags */
-EXTERN bool AllowBogusHELO; /* allow syntax errors on HELO command */
-EXTERN bool UserSubmission; /* initial (user) mail submission */
-EXTERN char *RunAsUserName; /* user to become for bulk of run */
-EXTERN uid_t RunAsUid; /* UID to become for bulk of run */
-EXTERN gid_t RunAsGid; /* GID to become for bulk of run */
-EXTERN int MaxRcptPerMsg; /* max recipients per SMTP message */
-EXTERN bool DoQueueRun; /* non-interrupt time queue run needed */
-EXTERN u_long ConnectOnlyTo; /* override connection address (for testing) */
-EXTERN int MaxHeadersLength; /* max length of headers */
-#if _FFR_DSN_RRT_OPTION
-EXTERN bool RrtImpliesDsn; /* turn Return-Receipt-To: into DSN */
-#endif
-EXTERN char *DeadLetterDrop; /* path to dead letter office */
-EXTERN bool DontProbeInterfaces; /* don't probe interfaces for names */
-EXTERN bool ChownAlwaysSafe; /* treat chown(2) as safe */
-EXTERN bool IgnoreHostStatus; /* ignore long term host status files */
-EXTERN bool SingleThreadDelivery; /* single thread hosts on delivery */
-EXTERN bool SingleLineFromHeader; /* force From: header to be one line */
-EXTERN bool DontLockReadFiles; /* don't read lock support files */
-EXTERN int ConnRateThrottle; /* throttle for SMTP connection rate */
-EXTERN int MaxAliasRecursion; /* maximum depth of alias recursion */
-EXTERN int MaxMacroRecursion; /* maximum depth of macro recursion */
-EXTERN int MaxRuleRecursion; /* maximum depth of ruleset recursion */
-EXTERN char *MustQuoteChars; /* quote these characters in phrases */
-EXTERN char *ServiceSwitchFile; /* backup service switch */
-EXTERN char *DefaultCharSet; /* default character set for MIME */
-EXTERN char *PostMasterCopy; /* address to get errs cc's */
-EXTERN int CheckpointInterval; /* queue file checkpoint interval */
-EXTERN bool DontPruneRoutes; /* don't prune source routes */
-EXTERN bool DontExpandCnames; /* do not $[...$] expand CNAMEs */
-EXTERN int MaxMciCache; /* maximum entries in MCI cache */
-EXTERN time_t ServiceCacheTime; /* time service switch was cached */
-EXTERN time_t ServiceCacheMaxAge; /* refresh interval for cache */
-EXTERN time_t MciCacheTimeout; /* maximum idle time on connections */
-EXTERN time_t MciInfoTimeout; /* how long 'til we retry down hosts */
-EXTERN FILE *TrafficLogFile; /* file in which to log all traffic */
-EXTERN char *DoubleBounceAddr; /* where to send double bounces */
-EXTERN char **ExternalEnviron; /* input environment */
-EXTERN char *UserEnviron[MAXUSERENVIRON + 1];
- /* saved user environment */
-EXTERN int MaxMimeHeaderLength; /* maximum MIME header length */
-EXTERN int MaxMimeFieldLength; /* maximum MIME field length */
+/* what to do in the TLS initialization */
+#define TLS_I_NONE 0x00000000 /* no requirements... */
+#define TLS_I_CERT_EX 0x00000001 /* CERT must exist */
+#define TLS_I_CERT_UNR 0x00000002 /* CERT must be g/o unreadable */
+#define TLS_I_KEY_EX 0x00000004 /* KEY must exist */
+#define TLS_I_KEY_UNR 0x00000008 /* KEY must be g/o unreadable */
+#define TLS_I_CERTP_EX 0x00000010 /* CA CERT PATH must exist */
+#define TLS_I_CERTP_UNR 0x00000020 /* CA CERT PATH must be g/o unreadable */
+#define TLS_I_CERTF_EX 0x00000040 /* CA CERT FILE must exist */
+#define TLS_I_CERTF_UNR 0x00000080 /* CA CERT FILE must be g/o unreadable */
+#define TLS_I_RSA_TMP 0x00000100 /* RSA TMP must be generated */
+#define TLS_I_USE_KEY 0x00000200 /* private key must usable */
+#define TLS_I_USE_CERT 0x00000400 /* certificate must be usable */
+#define TLS_I_VRFY_PATH 0x00000800 /* load verify path must succeed */
+#define TLS_I_VRFY_LOC 0x00001000 /* load verify default must succeed */
+#define TLS_I_CACHE 0x00002000 /* require cache */
+#define TLS_I_TRY_DH 0x00004000 /* try DH certificate */
+#define TLS_I_REQ_DH 0x00008000 /* require DH certificate */
+#define TLS_I_DHPAR_EX 0x00010000 /* require DH parameters */
+#define TLS_I_DHPAR_UNR 0x00020000 /* DH param. must be g/o unreadable */
+#define TLS_I_DH512 0x00040000 /* generate 512bit DH param */
+#define TLS_I_DH1024 0x00080000 /* generate 1024bit DH param */
+#define TLS_I_DH2048 0x00100000 /* generate 2048bit DH param */
+
+/* server requirements */
+#define TLS_I_SRV (TLS_I_CERT_EX | TLS_I_KEY_EX | TLS_I_KEY_UNR | \
+ TLS_I_CERTP_EX | TLS_I_CERTF_EX | TLS_I_RSA_TMP | \
+ TLS_I_USE_KEY | TLS_I_USE_CERT | TLS_I_VRFY_PATH | \
+ TLS_I_VRFY_LOC | TLS_I_TRY_DH | \
+ TLS_I_DH512)
+
+/* client requirements */
+#define TLS_I_CLT (TLS_I_KEY_UNR)
+
+#define TLS_AUTH_OK 0
+#define TLS_AUTH_NO 1
+#define TLS_AUTH_FAIL (-1)
+#endif /* STARTTLS */
-extern int errno;
-/*
-** Queue Run Limitations
+ /*
+** Queue related items
*/
+
+/* 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 */
+
+#if _FFR_QUEUEDELAY
+#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 */
+
+/* Queue Run Limitations */
struct queue_char
{
char *queue_match; /* string to match */
@@ -1306,9 +1538,20 @@ struct queue_char
typedef struct queue_char QUEUE_CHAR;
-EXTERN QUEUE_CHAR *QueueLimitRecipient; /* limit queue runs to this recipient */
-EXTERN QUEUE_CHAR *QueueLimitSender; /* limit queue runs to this sender */
-EXTERN QUEUE_CHAR *QueueLimitId; /* limit queue runs to this id */
+/* functions */
+extern void assign_queueid __P((ENVELOPE *));
+extern ADDRESS *copyqueue __P((ADDRESS *));
+extern void initsys __P((ENVELOPE *));
+extern void loseqfile __P((ENVELOPE *, char *));
+extern void multiqueue_cache __P((void));
+extern char *qid_printname __P((ENVELOPE *));
+extern char *qid_printqueue __P((int));
+extern char *queuename __P((ENVELOPE *, int));
+extern void queueup __P((ENVELOPE *, bool));
+extern bool runqueue __P((bool, bool));
+extern void setnewqueue __P((ENVELOPE *));
+extern bool shouldqueue __P((long, time_t));
+extern void sync_queue_time __P((void));
/*
** Timeouts
@@ -1335,30 +1578,41 @@ EXTERN struct
time_t to_miscshort; /* misc short commands (NOOP, VERB, etc) */
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 */
/* following are per message */
time_t to_q_return[MAXTOCLASS]; /* queue return timeouts */
time_t to_q_warning[MAXTOCLASS]; /* queue warning timeouts */
+ time_t res_retrans[MAXRESTOTYPES]; /* resolver retransmit */
+ int res_retry[MAXRESTOTYPES]; /* resolver retry */
} TimeOuts;
/* timeout classes for return and warning timeouts */
-# define TOC_NORMAL 0 /* normal delivery */
-# define TOC_URGENT 1 /* urgent delivery */
-# define TOC_NONURGENT 2 /* non-urgent delivery */
+#define TOC_NORMAL 0 /* normal delivery */
+#define TOC_URGENT 1 /* urgent delivery */
+#define TOC_NONURGENT 2 /* non-urgent delivery */
+
+/* resolver timeout specifiers */
+#define RES_TO_FIRST 0 /* first attempt */
+#define RES_TO_NORMAL 1 /* subsequent attempts */
+#define RES_TO_DEFAULT 2 /* default value */
+/* functions */
+extern void inittimeouts __P((char *, bool));
/*
** Trace information
*/
-/* trace vector and macros for debugging flags */
-EXTERN u_char tTdvect[100];
-# define tTd(flag, level) (tTdvect[flag] >= level)
-# define tTdlevel(flag) (tTdvect[flag])
+/* macros for debugging flags */
+#define tTd(flag, level) (tTdvect[flag] >= (u_char)level)
+#define tTdlevel(flag) (tTdvect[flag])
+
+/* variables */
+extern u_char tTdvect[100]; /* trace vector */
/*
** Miscellaneous information.
*/
-
/*
** The "no queue id" queue id for sm_syslog
*/
@@ -1380,135 +1634,457 @@ EXTERN u_char tTdvect[100];
#define newstr(s) strcpy(xalloc(strlen(s) + 1), s)
#define STRUCTCOPY(s, d) d = s
+ /*
+** 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 */
+EXTERN bool ConfigFileRead; /* configuration file has been read */
+EXTERN bool DataProgress; /* have we sent anything since last check */
+EXTERN bool DisConnected; /* running with OutChannel redirected to xf */
+EXTERN bool DoQueueRun; /* non-interrupt time queue run needed */
+EXTERN bool 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 */
+EXTERN bool GrabTo; /* if set, get recipients from msg */
+EXTERN bool HasEightBits; /* has at least one eight bit input byte */
+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 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 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 */
+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 */
+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 CheckpointInterval; /* queue file checkpoint interval */
+EXTERN int ConfigLevel; /* config file level */
+EXTERN int ConnRateThrottle; /* throttle for SMTP connection rate */
+EXTERN int CurChildren; /* current number of daemonic children */
+EXTERN int CurrentLA; /* current load average */
+EXTERN int DefaultNotify; /* default DSN notification flags */
+EXTERN int Errors; /* set if errors (local to single pass) */
+EXTERN int ExitStat; /* exit status code */
+EXTERN int FileMode; /* mode on files */
+EXTERN int LineNumber; /* line number in current input */
+EXTERN int LogLevel; /* level of logging to perform */
+EXTERN int MaxAliasRecursion; /* maximum depth of alias recursion */
+EXTERN int MaxChildren; /* maximum number of daemonic children */
+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 */
+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 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 uid_t DefUid; /* default uid to run as */
+EXTERN uid_t RealUid; /* real uid of caller */
+EXTERN uid_t RunAsUid; /* UID to become for bulk of run */
+EXTERN uid_t TrustedUid; /* uid of trusted user for files and startup */
+EXTERN size_t DataFileBufferSize; /* size of buffer for in-core df */
+EXTERN size_t XscriptFileBufferSize; /* size of buffer for in-core xf */
+EXTERN time_t 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 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 */
+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 */
+EXTERN char *DefUser; /* default user to run as (from DefUid) */
+EXTERN char *DefaultCharSet; /* default character set for MIME */
+EXTERN char *DoubleBounceAddr; /* where to send double bounces */
+EXTERN char *ErrMsgFile; /* file to prepend to all error messages */
+EXTERN char *FallBackMX; /* fall back MX host */
+EXTERN char *FileName; /* name to print on error messages */
+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 *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 *RunAsUserName; /* user to become for bulk of run */
+EXTERN char *SafeFileEnv; /* chroot location for file delivery */
+EXTERN char *ServiceSwitchFile; /* backup service switch */
+EXTERN char *SmtpGreeting; /* SMTP greeting message (old $e macro) */
+EXTERN char *SmtpPhase; /* current phase in SMTP processing */
+EXTERN char SmtpError[MAXLINE]; /* save failure error messages */
+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 BITMAP256 DontBlameSendmail; /* DontBlameSendmail bits */
+#if SFIO
+EXTERN Sfio_t *InChannel; /* input connection */
+EXTERN Sfio_t *OutChannel; /* output connection */
+#else /* SFIO */
+EXTERN FILE *InChannel; /* input connection */
+EXTERN FILE *OutChannel; /* output connection */
+#endif /* SFIO */
+EXTERN FILE *TrafficLogFile; /* file in which to log all traffic */
+#ifdef HESIOD
+EXTERN void *HesiodContext;
+#endif /* HESIOD */
+EXTERN ENVELOPE *CurEnv; /* envelope currently being processed */
+EXTERN EVENT *EventQueue; /* head of event queue */
+EXTERN MAILER *LocalMailer; /* ptr to local mailer */
+EXTERN MAILER *ProgMailer; /* ptr to program mailer */
+EXTERN MAILER *FileMailer; /* ptr to *file* mailer */
+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 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;
/*
** Declarations of useful functions
*/
-extern char *xalloc __P((int));
-extern char *sfgets __P((char *, int, FILE *, time_t, char *));
-extern char *queuename __P((ENVELOPE *, int));
-extern time_t curtime __P((void));
-extern bool transienterror __P((int));
-extern char *fgetfolded __P((char *, int, FILE *));
-extern char *username __P((void));
-extern char *pintvl __P((time_t, bool));
-extern bool shouldqueue __P((long, time_t));
-extern bool lockfile __P((int, char *, char *, int));
-extern char *hostsignature __P((MAILER *, char *, ENVELOPE *));
-extern void openxscript __P((ENVELOPE *));
+#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 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 *));
+extern int endtls __P((SSL *, char *));
+extern int endtlsclt __P((MCI *));
+extern void tlslogerr __P((void));
+extern void tls_rand_init __P((char *, int));
+#endif /* STARTTLS */
+
+/* Transcript file */
extern void closexscript __P((ENVELOPE *));
-extern char *shortenstring __P((const char *, int));
-extern bool usershellok __P((char *, char *));
-extern char *defcharset __P((ENVELOPE *));
-extern bool wordinclass __P((char *, int));
-extern char *denlstring __P((char *, bool, bool));
-extern void makelower __P((char *));
-extern bool rebuildaliases __P((MAP *, bool));
+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 int isenhsc __P((const char *, int));
+extern int extenhsc __P((const char *, int, char *));
+
+/* alias file */
+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 finis __P((bool, volatile int));
-extern void setsender __P((char *, ENVELOPE *, char **, int, bool));
-extern void xputs __P((const char *));
+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 smtprset __P((MAILER *, MCI *, ENVELOPE *));
-extern void smtpquit __P((MAILER *, MCI *, ENVELOPE *));
-extern void setuserenv __P((const char *, const char *));
-extern char *getextenv __P((const char *));
-extern void disconnect __P((int, ENVELOPE *));
-extern void putxline __P((char *, size_t, MCI *, int));
-extern void dumpfd __P((int, bool, bool));
-extern void makemailer __P((char *));
-extern void putfromline __P((MCI *, ENVELOPE *));
-extern void setoption __P((int, char *, bool, bool, ENVELOPE *));
-extern void setclass __P((int, char *));
-extern void inittimeouts __P((char *));
-extern void logdelivery __P((MAILER *, MCI *, const char *, ADDRESS *, time_t, ENVELOPE *));
-extern void giveresponse __P((int, MAILER *, MCI *, ADDRESS *, time_t, ENVELOPE *));
-extern void buildfname __P((char *, char *, char *, int));
-extern void mci_setstat __P((MCI *, int, char *, char *));
+extern void sm_syslog __P((int, const char *, const char *, ...));
+
+/* SMTP */
+extern void giveresponse __P((int, char *, MAILER *, MCI *, ADDRESS *, time_t, ENVELOPE *));
+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 smtpgetstat __P((MAILER *, MCI *, ENVELOPE *));
+extern int smtpmailfrom __P((MAILER *, MCI *, ENVELOPE *));
+extern void smtpmessage __P((char *, MAILER *, MCI *, ...));
+extern void smtpinit __P((MAILER *, MCI *, ENVELOPE *, bool));
extern char *smtptodsn __P((int));
-extern int rscheck __P((char *, char *, char *, ENVELOPE *e));
-extern void mime7to8 __P((MCI *, HDR *, ENVELOPE *));
-extern int mime8to7 __P((MCI *, HDR *, ENVELOPE *, char **, int));
-extern void xfclose __P((FILE *, char *, char *));
-extern int switch_map_find __P((char *, char *[], short []));
-extern void shorten_hostname __P((char []));
-extern int waitfor __P((pid_t));
-extern void proc_list_add __P((pid_t, char *));
-extern void proc_list_set __P((pid_t, char *));
-extern void proc_list_drop __P((pid_t));
-extern void proc_list_clear __P((void));
-extern void proc_list_display __P((FILE *));
-extern void proc_list_probe __P((void));
-extern void buffer_errors __P((void));
-extern void flush_errors __P((bool));
-extern void putline __P((char *, MCI *));
-extern bool xtextok __P((char *));
-extern char *xtextify __P((char *, char *));
-extern char *xuntextify __P((char *));
-extern void cleanstrcpy __P((char *, char *, int));
-extern int getmxrr __P((char *, char **, bool, int *));
-extern int strtorwset __P((char *, char **, int));
-extern void printav __P((char **));
-extern void printopenfds __P((bool));
+extern int smtpprobe __P((MCI *));
+extern void smtpquit __P((MAILER *, MCI *, ENVELOPE *));
+extern int smtprcpt __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *));
+extern void smtprset __P((MAILER *, MCI *, ENVELOPE *));
+
+#define ISSMTPCODE(c) (isascii(c[0]) && isdigit(c[0]) && \
+ isascii(c[1]) && isdigit(c[1]) && \
+ isascii(c[2]) && isdigit(c[2]))
+#define ISSMTPREPLY(c) (ISSMTPCODE(c) && \
+ (c[3] == ' ' || c[3] == '-' || c[3] == '\0'))
+
+/* delivery */
+extern pid_t dowork __P((int, char *, bool, bool, ENVELOPE *));
extern int endmailer __P((MCI *, ENVELOPE *, char **));
-extern void fixcrlf __P((char *, bool));
-extern int dofork __P((void));
-extern void initsys __P((ENVELOPE *));
-extern void collect __P((FILE *, bool, HDR **, ENVELOPE *));
-extern void stripquotes __P((char *));
-extern int include __P((char *, bool, ADDRESS *, ADDRESS **, int, ENVELOPE *));
-extern void unlockqueue __P((ENVELOPE *));
-extern void xunlink __P((char *));
-extern bool runqueue __P((bool, bool));
-extern int getla __P((void));
+extern int mailfile __P((char *volatile, MAILER *volatile, ADDRESS *, volatile long, ENVELOPE *));
extern void sendall __P((ENVELOPE *, int));
-extern void queueup __P((ENVELOPE *, bool));
-extern void checkfds __P((char *));
-extern int returntosender __P((char *, ADDRESS *, int, ENVELOPE *));
+
+/* stats */
extern void markstats __P((ENVELOPE *, ADDRESS *, bool));
+extern void clearstats __P((void));
extern void poststats __P((char *));
+
+/* control socket */
+extern void closecontrolsocket __P((bool));
+extern void clrcontrol __P((void));
+extern void control_command __P((int, ENVELOPE *));
+extern int opencontrolsocket __P((void));
+
+#if _FFR_MILTER
+/* milter functions */
+extern void milter_parse_list __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 void milter_quit __P((ENVELOPE *));
+extern void milter_abort __P((ENVELOPE *));
+extern char *milter_connect __P((char *, SOCKADDR, ENVELOPE *, char *));
+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 */
+
+extern char *addquotes __P((char *));
extern char *arpadate __P((char *));
-extern int mailfile __P((char *volatile, MAILER *volatile, ADDRESS *, volatile int, ENVELOPE *));
-extern void loseqfile __P((ENVELOPE *, char *));
-extern int prog_open __P((char **, int *, ENVELOPE *));
-extern bool getcanonname __P((char *, int, bool));
-extern bool path_is_dir __P((char *, bool));
-extern pid_t dowork __P((char *, bool, bool, ENVELOPE *));
+extern bool atobool __P((char *));
+extern int atooct __P((char *));
+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 void buildfname __P((char *, char *, char *, int));
+extern int checkcompat __P((ADDRESS *, ENVELOPE *));
+#ifdef XDEBUG
+extern void checkfd012 __P((char *));
+extern void checkfdopen __P((int, char *));
+#endif /* XDEBUG */
+extern void checkfds __P((char *));
+extern bool chownsafe __P((int, bool));
+extern void cleanstrcpy __P((char *, char *, int));
+extern void clrdaemon __P((void));
+extern void collect __P((FILE *, bool, HDR **, ENVELOPE *));
+extern time_t convtime __P((char *, int));
+extern char **copyplist __P((char **, bool));
+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 *));
+extern int 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 char *exitstat __P((char *));
+extern char *fgetfolded __P((char *, int, FILE *));
extern void fill_fd __P((int, char *));
-extern void closecontrolsocket __P((bool));
-extern void clrcontrol __P((void));
-
-extern const char *errstring __P((int));
+extern char *find_character __P((char *, int));
+extern struct passwd *finduser __P((char *, bool *));
+extern void finis __P((bool, volatile int));
+extern void fixcrlf __P((char *, bool));
+extern long freediskspace __P((char *, long *));
+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 BITMAP256 *getrequests __P((ENVELOPE *));
+extern char *getvendor __P((int));
+extern void help __P((char *, ENVELOPE *));
+extern void init_md __P((int, char **));
+extern void initdaemon __P((void));
+extern void inithostmaps __P((void));
+extern void initmacros __P((ENVELOPE *));
+extern void initsetproctitle __P((int, char **, char **));
+extern void init_vendor_macros __P((ENVELOPE *));
+extern SIGFUNC_DECL intindebug __P((int));
+extern SIGFUNC_DECL intsig __P((int));
+extern bool isloopback __P((SOCKADDR sa));
+extern void load_if_names __P((void));
+extern bool lockfile __P((int, char *, char *, int));
+extern void log_sendmail_pid __P((ENVELOPE *));
+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 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 char *pintvl __P((time_t, bool));
+extern void printav __P((char **));
+extern void printmailer __P((MAILER *));
+extern void printopenfds __P((bool));
+extern void printqueue __P((void));
+extern void printrules __P((void));
+extern int prog_open __P((char **, int *, ENVELOPE *));
+extern void putline __P((char *, MCI *));
+extern void putxline __P((char *, size_t, MCI *, int));
+extern void queueup_macros __P((int, FILE *, ENVELOPE *));
+extern SIGFUNC_DECL quiesce __P((int));
+extern void readcf __P((char *, bool, ENVELOPE *));
+extern SIGFUNC_DECL reapchild __P((int));
+extern bool refuseconnections __P((char *, ENVELOPE *, int));
+extern int releasesignal __P((int));
+extern void resetlimits __P((void));
+extern bool rfc822_string __P((char *));
+extern void savemail __P((ENVELOPE *, bool));
+extern void seed_random __P((void));
+extern void sendtoargv __P((char **, ENVELOPE *));
+extern void setclientoptions __P((char *));
+extern bool setdaemonoptions __P((char *));
+extern void setdefaults __P((ENVELOPE *));
+extern void setdefuser __P((void));
+extern bool setvendor __P((char *));
+extern void setoption __P((int, char *, bool, bool, ENVELOPE *));
extern sigfunc_t setsignal __P((int, sigfunc_t));
-extern int blocksignal __P((int));
-extern int releasesignal __P((int));
-extern struct hostent *sm_gethostbyname __P((char *));
+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 void shorten_hostname __P((char []));
+extern bool shorten_rfc822_string __P((char *, size_t));
+extern SIGFUNC_DECL sigusr1 __P((int));
+extern SIGFUNC_DECL sighup __P((int));
+extern void sm_dopr __P((char *, const char *, va_list));
+extern struct hostent *sm_gethostbyname __P((char *, int));
extern struct hostent *sm_gethostbyaddr __P((char *, int, int));
+extern int sm_getla __P((ENVELOPE *));
extern struct passwd *sm_getpwnam __P((char *));
extern struct passwd *sm_getpwuid __P((UID_T));
-extern struct passwd *finduser __P((char *, bool *));
-
-#ifdef XDEBUG
-extern void checkfdopen __P((int, char *));
-extern void checkfd012 __P((char *));
-#endif
-
-/* ellipsis is a different case though */
-extern void auth_warning __P((ENVELOPE *, const char *, ...));
-extern void syserr __P((const char *, ...));
-extern void usrerr __P((const char *, ...));
-extern void message __P((const char *, ...));
-extern void nmessage __P((const char *, ...));
-extern void setproctitle __P((const char *, ...));
-extern void sm_setproctitle __P((bool, const char *, ...));
-extern void sm_syslog __P((int, const char *, const char *, ...));
-
-#if !HASSNPRINTF
-extern int snprintf __P((char *, size_t, const char *, ...));
-extern int vsnprintf __P((char *, size_t, const char *, va_list));
-#endif
-extern char *quad_to_string __P((QUAD_T));
+extern void sm_setproctitle __P((bool, ENVELOPE *, const char *, ...));
+extern int sm_strcasecmp __P((const char *, const char *));
+extern bool strcontainedin __P((char *, char *));
+extern void stripquotes __P((char *));
+extern int switch_map_find __P((char *, char *[], short []));
+extern bool transienterror __P((int));
+extern void tTflag __P((char *));
+extern void tTsetup __P((u_char *, int, char *));
+extern SIGFUNC_DECL tick __P((int));
+extern char *ttypath __P((void));
+extern void unlockqueue __P((ENVELOPE *));
+#if !HASUNSETENV
+extern void unsetenv __P((char *));
+#endif /* !HASUNSETENV */
+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 void xputs __P((const char *));
+extern char *xtextify __P((char *, char *));
+extern bool xtextok __P((char *));
+extern void xunlink __P((char *));
+extern char *xuntextify __P((char *));
+#endif /* _SENDMAIL_H */
diff --git a/contrib/sendmail/src/sfsasl.c b/contrib/sendmail/src/sfsasl.c
new file mode 100644
index 0000000..43a65d1
--- /dev/null
+++ b/contrib/sendmail/src/sfsasl.c
@@ -0,0 +1,367 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: sfsasl.c,v 8.17.4.7 2000/07/18 18:44:51 gshapiro Exp $";
+#endif /* ! lint */
+
+#if SFIO
+# include <sfio/stdio.h>
+#endif /* SFIO */
+
+#include <stdlib.h>
+#include <sendmail.h>
+
+#if SASL && SFIO
+/*
+** SASL
+*/
+
+# include <sasl.h>
+# include "sfsasl.h"
+
+static ssize_t
+sasl_read(f, buf, size, disc)
+ Sfio_t *f;
+ Void_t *buf;
+ size_t size;
+ Sfdisc_t *disc;
+{
+ int len, result;
+ char *outbuf;
+ unsigned int outlen;
+ Sasldisc_t *sd = (Sasldisc_t *) disc;
+
+ len = sfrd(f, buf, size, disc);
+
+ if (len <= 0)
+ return len;
+
+ result = sasl_decode(sd->conn, buf, len, &outbuf, &outlen);
+
+ if (result != SASL_OK)
+ {
+ /* eventually, we'll want an exception here */
+ return -1;
+ }
+
+ if (outbuf != NULL)
+ {
+ (void)memcpy(buf, outbuf, outlen);
+ free(outbuf);
+ }
+ return outlen;
+}
+
+static ssize_t
+sasl_write(f, buf, size, disc)
+ Sfio_t *f;
+ const Void_t *buf;
+ size_t size;
+ Sfdisc_t *disc;
+{
+ int result;
+ char *outbuf;
+ unsigned int outlen;
+ Sasldisc_t *sd = (Sasldisc_t *) disc;
+
+ result = sasl_encode(sd->conn, buf, size, &outbuf, &outlen);
+
+ if (result != SASL_OK)
+ {
+ /* eventually, we'll want an exception here */
+ return -1;
+ }
+
+ if (outbuf != NULL)
+ {
+ sfwr(f, outbuf, outlen, disc);
+ free(outbuf);
+ }
+ return size;
+}
+
+int
+sfdcsasl(fin, fout, conn)
+ Sfio_t *fin;
+ Sfio_t *fout;
+ sasl_conn_t *conn;
+{
+ Sasldisc_t *saslin, *saslout;
+
+ if (conn == NULL)
+ {
+ /* no need to do anything */
+ return 0;
+ }
+
+ if ((saslin = (Sasldisc_t *) malloc(sizeof(Sasldisc_t))) == NULL)
+ return -1;
+ if ((saslout = (Sasldisc_t *) malloc(sizeof(Sasldisc_t))) == NULL)
+ {
+ free(saslin);
+ return -1;
+ }
+
+ saslin->disc.readf = sasl_read;
+ saslin->disc.writef = sasl_write;
+ saslin->disc.seekf = NULL;
+ saslin->disc.exceptf = NULL;
+
+ saslout->disc.readf = sasl_read;
+ saslout->disc.writef = sasl_write;
+ saslout->disc.seekf = NULL;
+ saslout->disc.exceptf = NULL;
+
+ saslin->conn = conn;
+ saslout->conn = conn;
+
+ if (sfdisc(fin, (Sfdisc_t *) saslin) != (Sfdisc_t *) saslin ||
+ sfdisc(fout, (Sfdisc_t *) saslout) != (Sfdisc_t *) saslout)
+ {
+ free(saslin);
+ free(saslout);
+ return -1;
+ }
+ return 0;
+}
+#endif /* SASL && SFIO */
+
+#if STARTTLS && (SFIO || _FFR_TLS_TOREK)
+/*
+** STARTTLS
+*/
+
+# include "sfsasl.h"
+# include <openssl/err.h>
+
+static ssize_t
+# if SFIO
+tls_read(f, buf, size, disc)
+ Sfio_t *f;
+ Void_t *buf;
+ size_t size;
+ Sfdisc_t *disc;
+# else /* SFIO */
+tls_read(disc, buf, size)
+ void *disc;
+ void *buf;
+ size_t size;
+# endif /* SFIO */
+{
+ int r;
+ Tlsdisc_t *sd;
+
+ /* Cast back to correct type */
+ sd = (Tlsdisc_t *) disc;
+
+ r = SSL_read(sd->con, (char *) buf, size);
+ if (r < 0 && LogLevel > 7)
+ {
+ char *err;
+
+ err = NULL;
+ switch (SSL_get_error(sd->con, r))
+ {
+ case SSL_ERROR_NONE:
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ err = "write W BLOCK";
+ break;
+ case SSL_ERROR_WANT_READ:
+ err = "write R BLOCK";
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ err = "write X BLOCK";
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ break;
+ case SSL_ERROR_SYSCALL:
+ err = "syscall error";
+/*
+ get_last_socket_error());
+*/
+ break;
+ case SSL_ERROR_SSL:
+ err = "generic SSL error";
+ break;
+ }
+ if (err != NULL)
+ sm_syslog(LOG_WARNING, NOQID, "TLS: read error: %s",
+ err);
+ }
+ return r;
+}
+
+static ssize_t
+# if SFIO
+tls_write(f, buf, size, disc)
+ Sfio_t *f;
+ const Void_t *buf;
+ size_t size;
+ Sfdisc_t *disc;
+# else /* SFIO */
+tls_write(disc, buf, size)
+ void *disc;
+ const void *buf;
+ size_t size;
+# endif /* SFIO */
+{
+ int r;
+ Tlsdisc_t *sd;
+
+ /* Cast back to correct type */
+ sd = (Tlsdisc_t *) disc;
+
+ r = SSL_write(sd->con, (char *)buf, size);
+ if (r < 0 && LogLevel > 7)
+ {
+ char *err;
+
+ err = NULL;
+ switch (SSL_get_error(sd->con, r))
+ {
+ case SSL_ERROR_NONE:
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ err = "write W BLOCK";
+ break;
+ case SSL_ERROR_WANT_READ:
+ err = "write R BLOCK";
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ err = "write X BLOCK";
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ break;
+ case SSL_ERROR_SYSCALL:
+ err = "syscall error";
+/*
+ get_last_socket_error());
+*/
+ break;
+ case SSL_ERROR_SSL:
+ err = "generic SSL error";
+/*
+ ERR_GET_REASON(ERR_peek_error()));
+*/
+ break;
+ }
+ if (err != NULL)
+ sm_syslog(LOG_WARNING, NOQID, "TLS: write error: %s",
+ err);
+ }
+ return r;
+}
+
+# if !SFIO
+static int
+tls_close(cookie)
+ void *cookie;
+{
+ int retval = 0;
+ Tlsdisc_t *tc;
+
+ /* Cast back to correct type */
+ tc = (Tlsdisc_t *)cookie;
+
+ if (tc->fp != NULL)
+ {
+ retval = fclose(tc->fp);
+ tc->fp = NULL;
+ }
+
+ free(tc);
+ return retval;
+}
+# endif /* !SFIO */
+
+int
+sfdctls(fin, fout, con)
+# if SFIO
+ Sfio_t *fin;
+ Sfio_t *fout;
+# else /* SFIO */
+ FILE **fin;
+ FILE **fout;
+# endif /* SFIO */
+ SSL *con;
+{
+ Tlsdisc_t *tlsin, *tlsout;
+# if !SFIO
+ FILE *fp;
+# endif /* !SFIO */
+
+ if (con == NULL)
+ return 0;
+
+ if ((tlsin = (Tlsdisc_t *) malloc(sizeof(Tlsdisc_t))) == NULL)
+ return -1;
+ if ((tlsout = (Tlsdisc_t *) malloc(sizeof(Tlsdisc_t))) == NULL)
+ {
+ free(tlsin);
+ return -1;
+ }
+
+# 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;
+
+ SSL_set_rfd(con, fileno(fin)); /* fileno or sffileno? XXX */
+ SSL_set_wfd(con, fileno(fout));
+ if (sfdisc(fin, (Sfdisc_t *) tlsin) != (Sfdisc_t *) tlsin ||
+ sfdisc(fout, (Sfdisc_t *) tlsout) != (Sfdisc_t *) tlsout)
+ {
+ free(tlsin);
+ free(tlsout);
+ return -1;
+ }
+# else /* SFIO */
+ tlsin->fp = *fin;
+ tlsin->con = con;
+ fp = funopen(tlsin, tls_read, tls_write, NULL, tls_close);
+ if (fp == NULL)
+ {
+ free(tlsin);
+ return -1;
+ }
+ *fin = fp;
+
+ tlsout->fp = *fout;
+ tlsout->con = con;
+ fp = funopen(tlsout, tls_read, tls_write, NULL, tls_close);
+ if (fp == NULL)
+ {
+ FILE *save;
+
+ /* Hack: Don't close underlying fp */
+ save = tlsin->fp;
+ tlsin->fp = NULL;
+ fclose(*fin);
+ *fin = save;
+ free(tlsout);
+ return -1;
+ }
+ *fout = fp;
+ SSL_set_rfd(con, fileno(tlsin->fp));
+ SSL_set_wfd(con, fileno(tlsout->fp));
+# endif /* SFIO */
+ return 0;
+}
+#endif /* STARTTLS && (SFIO || _FFR_TLS_TOREK) */
diff --git a/contrib/sendmail/src/sfsasl.h b/contrib/sendmail/src/sfsasl.h
new file mode 100644
index 0000000..b276e27
--- /dev/null
+++ b/contrib/sendmail/src/sfsasl.h
@@ -0,0 +1,60 @@
+/*
+ * 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: sfsasl.h,v 8.13.4.4 2000/07/18 18:44:51 gshapiro Exp $"
+ */
+
+#ifndef SFSASL_H
+# define SFSASL_H
+
+# if SFIO
+# include <sfio.h>
+# endif /* SFIO */
+
+# if SASL
+# if SFIO
+
+/* sf discipline to add sasl */
+typedef struct _sasldisc
+{
+ Sfdisc_t disc;
+ sasl_conn_t *conn;
+} Sasldisc_t;
+
+extern int sfdcsasl __P((Sfio_t *, Sfio_t *, sasl_conn_t *));
+
+# endif /* SFIO */
+# endif /* SASL */
+
+# if STARTTLS
+# if SFIO
+
+/* sf discipline to add tls */
+typedef struct _tlsdisc
+{
+ Sfdisc_t disc;
+ SSL *con;
+} Tlsdisc_t;
+
+extern int sfdctls __P((Sfio_t *, Sfio_t *, SSL *));
+
+# else /* SFIO */
+# if _FFR_TLS_TOREK
+
+typedef struct tls_conn
+{
+ FILE *fp; /* original FILE * */
+ SSL *con; /* SSL context */
+} Tlsdisc_t;
+
+extern int sfdctls __P((FILE **, FILE **, SSL *));
+
+# endif /* _FFR_TLS_TOREK */
+# endif /* SFIO */
+# endif /* STARTTLS */
+#endif /* ! SFSASL_H */
diff --git a/contrib/sendmail/src/shmticklib.c b/contrib/sendmail/src/shmticklib.c
new file mode 100644
index 0000000..a3e1ad5
--- /dev/null
+++ b/contrib/sendmail/src/shmticklib.c
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: shmticklib.c,v 8.6 2000/02/26 01:32:27 gshapiro Exp $";
+#endif /* ! lint */
+
+#if _FFR_SHM_STATUS
+# if SFIO
+# include <sfio/stdio.h>
+# else /* !SFIO */
+# include <stdio.h>
+# endif /* SFIO */
+# include <sys/types.h>
+# include <sys/ipc.h>
+# include <sys/shm.h>
+
+# include "statusd_shm.h"
+
+ /*
+** SHMTICK -- increment a shared memory variable
+**
+** Parameters:
+** inc_me -- identity of shared memory segment
+** what -- which variable to increment
+**
+** Returns:
+** none
+*/
+
+void
+shmtick(inc_me, what)
+ int inc_me;
+ int what;
+{
+ static int shmid = -1;
+ static STATUSD_SHM *sp = (STATUSD_SHM *)-1;
+ static unsigned int cookie = 0;
+
+ if (shmid < 0)
+ {
+ int size = sizeof(STATUSD_SHM);
+
+ shmid = shmget(STATUSD_SHM_KEY, size, 0);
+ if (shmid < 0)
+ return;
+ }
+ 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)
+ {
+ /*
+ ** possible race condition, wait for
+ ** statusd to initialize.
+ */
+
+ return;
+ }
+ if (what >= STATUSD_LONGS)
+ what = STATUSD_LONGS - 1;
+ if (inc_me >= STATUSD_LONGS)
+ inc_me = STATUSD_LONGS - 1;
+
+ if (sp->ul[STATUSD_COOKIE] != cookie)
+ {
+ cookie = sp->ul[STATUSD_COOKIE];
+ ++(sp->ul[inc_me]);
+ }
+ ++(sp->ul[what]);
+}
+#endif /* _FFR_SHM_STATUS */
diff --git a/contrib/sendmail/src/srvrsmtp.c b/contrib/sendmail/src/srvrsmtp.c
index f4ffe8c..52ea42f 100644
--- a/contrib/sendmail/src/srvrsmtp.c
+++ b/contrib/sendmail/src/srvrsmtp.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -10,19 +11,52 @@
*
*/
-# include "sendmail.h"
-#ifndef lint
-#if SMTP
-static char sccsid[] = "@(#)srvrsmtp.c 8.187 (Berkeley) 10/23/1998 (with SMTP)";
-#else
-static char sccsid[] = "@(#)srvrsmtp.c 8.187 (Berkeley) 10/23/1998 (without SMTP)";
-#endif
-#endif /* not lint */
-
-# include <errno.h>
+#include <sendmail.h>
+#ifndef lint
# if SMTP
+static char id[] = "@(#)$Id: srvrsmtp.c,v 8.471.2.2.2.40 2000/07/18 02:24:45 gshapiro Exp $ (with SMTP)";
+# else /* SMTP */
+static char id[] = "@(#)$Id: srvrsmtp.c,v 8.471.2.2.2.40 2000/07/18 02:24:45 gshapiro Exp $ (without SMTP)";
+# endif /* SMTP */
+#endif /* ! lint */
+
+#if SMTP
+# include "sfsasl.h"
+# 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 = FALSE;
+static int tls_verify_cb __P((X509_STORE_CTX *));
+# if !TLS_NO_RSA
+# define RSA_KEYLENGTH 512
+# endif /* !TLS_NO_RSA */
+# endif /* STARTTLS */
+
+static time_t checksmtpattack __P((volatile int *, int, bool,
+ char *, ENVELOPE *));
+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 *));
+extern ENVELOPE BlankEnvelope;
/*
** SMTP -- run the SMTP protocol.
@@ -42,11 +76,11 @@ static char sccsid[] = "@(#)srvrsmtp.c 8.187 (Berkeley) 10/23/1998 (without SMTP
struct cmd
{
- char *cmdname; /* command name */
- int cmdcode; /* internal code, see below */
+ char *cmd_name; /* command name */
+ int cmd_code; /* internal code, see below */
};
-/* values for cmdcode */
+/* values for cmd_code */
# define CMDERROR 0 /* bad command */
# define CMDMAIL 1 /* mail -- designate sender */
# define CMDRCPT 2 /* rcpt -- designate recipient */
@@ -60,10 +94,18 @@ struct cmd
# 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 */
+/* unimplemented commands from RFC 821 */
+# 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 */
/* debugging-only commands, only enabled if SMTPDEBUG is defined */
@@ -87,6 +129,16 @@ static struct cmd CmdTab[] =
{ "verb", CMDVERB },
{ "onex", CMDONEX },
{ "xusr", CMDXUSR },
+ { "send", CMDUNIMPL },
+ { "saml", CMDUNIMPL },
+ { "soml", CMDUNIMPL },
+ { "turn", CMDUNIMPL },
+# if SASL
+ { "auth", CMDAUTH, },
+# endif /* SASL */
+# if STARTTLS
+ { "starttls", CMDSTLS, },
+# endif /* STARTTLS */
/* remaining commands are here only to trap and log attempts to use them */
{ "showq", CMDDBGQSHOW },
{ "debug", CMDDBGDEBUG },
@@ -95,25 +147,29 @@ static struct cmd CmdTab[] =
{ NULL, CMDERROR }
};
-bool OneXact = FALSE; /* one xaction only this run */
-char *CurSmtpClient; /* who's at the other end of channel */
-
-static char *skipword __P((char *volatile, char *));
+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 */
-#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 */
+/* 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, e)
- char *nullserver;
+smtp(nullserver, d_flags, e)
+ char *volatile nullserver;
+ BITMAP256 d_flags;
register ENVELOPE *volatile e;
{
register char *volatile p;
- register struct cmd *c;
+ register struct cmd *volatile c = NULL;
char *cmd;
auto ADDRESS *vrfyqueue;
ADDRESS *a;
@@ -126,6 +182,7 @@ smtp(nullserver, e)
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 */
@@ -133,25 +190,62 @@ smtp(nullserver, e)
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 */
bool ok;
- volatile int lognullconnection = TRUE;
+ volatile bool tempfail = FALSE;
+# if _FFR_MILTER
+ volatile bool milterize = (nullserver == NULL);
+# endif /* _FFR_MILTER */
+ volatile time_t wt; /* timeout after too many commands */
+ volatile time_t previous; /* time after checksmtpattack() */
+ volatile bool lognullconnection = TRUE;
register char *q;
+ char *addr;
+ char *greetcode = "220";
QUEUE_CHAR *new;
+ int argno;
+ char *args[MAXSMTPARGS];
char inp[MAXLINE];
char cmdbuf[MAXLINE];
- extern ENVELOPE BlankEnvelope;
- extern void help __P((char *));
- extern void settime __P((ENVELOPE *));
- extern bool enoughdiskspace __P((long));
- extern int runinchild __P((char *, ENVELOPE *));
- extern void checksmtpattack __P((volatile int *, int, char *, ENVELOPE *));
+# if SASL
+ sasl_conn_t *conn;
+ volatile bool sasl_ok;
+ volatile 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 outlen;
+ char *volatile auth_type;
+ char *mechlist;
+ volatile int n_mechs;
+ int len;
+ sasl_security_properties_t ssp;
+ sasl_external_properties_t ext_ssf;
+# if SFIO
+ sasl_ssf_t *ssf;
+# endif /* SFIO */
+# endif /* SASL */
+# if STARTTLS
+ int r;
+ volatile bool usetls = TRUE;
+ volatile bool tls_active = FALSE;
+ bool saveQuickAbort;
+ bool saveSuprErrs;
+# endif /* STARTTLS */
if (fileno(OutChannel) != fileno(stdout))
{
/* arrange for debugging output to go to remote host */
(void) dup2(fileno(OutChannel), fileno(stdout));
}
+
settime(e);
+ (void)sm_getla(e);
peerhostname = RealHostName;
if (peerhostname == NULL)
peerhostname = "localhost";
@@ -163,16 +257,169 @@ smtp(nullserver, e)
/* check_relay may have set discard bit, save for later */
discard = bitset(EF_DISCARD, e->e_flags);
- sm_setproctitle(TRUE, "server %s startup", CurSmtpClient);
-#if DAEMON
- if (LogLevel > 11)
+ sm_setproctitle(TRUE, e, "server %s startup", CurSmtpClient);
+
+# if SASL
+ sasl_ok = FALSE; /* SASL can't be used (yet) */
+ n_mechs = 0;
+
+ /* 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)
{
- /* log connection information */
- sm_syslog(LOG_INFO, NOQID,
- "SMTP connect from %.100s (%.100s)",
- CurSmtpClient, anynet_ntoa(&RealHostAddr));
+ sasl_ok = TRUE;
+
+ /*
+ ** SASL set properties for sasl
+ ** set local/remote IP
+ ** XXX only IPv4: Cyrus SASL doesn't support anything else
+ **
+ ** XXX where exactly are these used/required?
+ ** Kerberos_v4
+ */
+
+# if NETINET
+ in = macvalue(macid("{daemon_family}", NULL), e);
+ if (in != NULL && strcmp(in, "inet") == 0)
+ {
+ SOCKADDR_LEN_T addrsize;
+ struct sockaddr_in saddr_l;
+ struct sockaddr_in saddr_r;
+
+ addrsize = sizeof(struct sockaddr_in);
+ if (getpeername(fileno(InChannel),
+ (struct sockaddr *)&saddr_r,
+ &addrsize) == 0)
+ {
+ sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
+ addrsize = sizeof(struct sockaddr_in);
+ if (getsockname(fileno(InChannel),
+ (struct sockaddr *)&saddr_l,
+ &addrsize) == 0)
+ sasl_setprop(conn, SASL_IP_LOCAL,
+ &saddr_l);
+ }
+ }
+# endif /* NETINET */
+
+ authenticating = SASL_NOT_AUTH;
+ auth_type = NULL;
+ mechlist = NULL;
+ user = NULL;
+# if 0
+ define(macid("{auth_author}", NULL), NULL, &BlankEnvelope);
+# 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() */
+ {
+ ssp.max_ssf = INT_MAX;
+ ssp.maxbufsize = MAXOUTLEN;
+ }
+# endif /* SFIO */
+# if _FFR_SASL_OPTS
+ ssp.security_flags = SASLOpts & SASL_SEC_MASK;
+# endif /* _FFR_SASL_OPTS */
+ 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
+ */
+ 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 */
+
+# if STARTTLS
+# if _FFR_TLS_O_T
+ saveQuickAbort = QuickAbort;
+ saveSuprErrs = SuprErrs;
+ SuprErrs = TRUE;
+ QuickAbort = FALSE;
+ if (rscheck("offer_tls", CurSmtpClient, "", e, TRUE, FALSE, 8) != EX_OK
+ || Errors > 0)
+ usetls = FALSE;
+ QuickAbort = saveQuickAbort;
+ SuprErrs = saveSuprErrs;
+# endif /* _FFR_TLS_O_T */
+# endif /* STARTTLS */
+
+# if _FFR_MILTER
+ if (milterize)
+ {
+ char state;
+
+ /* initialize mail filter connection */
+ milter_init(e, &state);
+ switch (state)
+ {
+ case SMFIR_REJECT:
+ greetcode = "554";
+ nullserver = "Command rejected";
+ milterize = FALSE;
+ break;
+
+ case SMFIR_TEMPFAIL:
+ tempfail = TRUE;
+ milterize = FALSE;
+ break;
+ }
}
-#endif
+
+ if (milterize && !bitset(EF_DISCARD, e->e_flags))
+ {
+ char state;
+
+ (void) milter_connect(peerhostname, RealHostAddr,
+ e, &state);
+ switch (state)
+ {
+ case SMFIR_REPLYCODE: /* REPLYCODE shouldn't happen */
+ case SMFIR_REJECT:
+ greetcode = "554";
+ nullserver = "Command rejected";
+ milterize = FALSE;
+ break;
+
+ case SMFIR_TEMPFAIL:
+ tempfail = TRUE;
+ milterize = FALSE;
+ break;
+ }
+ }
+# endif /* _FFR_MILTER */
/* output the first line, inserting "ESMTP" as second word */
expand(SmtpGreeting, inp, sizeof inp, e);
@@ -182,8 +429,13 @@ smtp(nullserver, e)
id = strchr(inp, ' ');
if (id == NULL)
id = &inp[strlen(inp)];
- cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s";
- message(cmd, id - inp, inp, id);
+ if (p == NULL)
+ snprintf(cmdbuf, sizeof cmdbuf,
+ "%s %%.*s ESMTP%%s", greetcode);
+ else
+ snprintf(cmdbuf, sizeof cmdbuf,
+ "%s-%%.*s ESMTP%%s", greetcode);
+ message(cmdbuf, id - inp, inp, id);
/* output remaining lines */
while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
@@ -191,13 +443,15 @@ smtp(nullserver, e)
*p++ = '\0';
if (isascii(*id) && isspace(*id))
id++;
- message("220-%s", id);
+ (void) snprintf(cmdbuf, sizeof cmdbuf, "%s-%%s", greetcode);
+ message(cmdbuf, id);
}
if (id != NULL)
{
if (isascii(*id) && isspace(*id))
id++;
- message("220 %s", id);
+ (void) snprintf(cmdbuf, sizeof cmdbuf, "%s %%s", greetcode);
+ message(cmdbuf, id);
}
protocol = NULL;
@@ -218,58 +472,246 @@ smtp(nullserver, e)
/* setup for the read */
e->e_to = NULL;
Errors = 0;
+ FileName = NULL;
(void) fflush(stdout);
/* read the input line */
SmtpPhase = "server cmd read";
- sm_setproctitle(TRUE, "server %s cmd read", CurSmtpClient);
- p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
- SmtpPhase);
+ sm_setproctitle(TRUE, e, "server %s cmd read", CurSmtpClient);
+# if SASL
+ /*
+ ** SMTP AUTH requires accepting any length,
+ ** at least for challenge/response
+ ** XXX
+ */
+# endif /* SASL */
/* handle errors */
- if (p == NULL)
+ if (ferror(OutChannel) ||
+ (p = sfgets(inp, sizeof inp, InChannel,
+ TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
{
+ char *d;
+
+ d = macvalue(macid("{daemon_name}", NULL), e);
+ if (d == NULL)
+ d = "stdin";
/* end of file, just die */
disconnect(1, e);
- message("421 %s Lost input channel from %s",
+
+# if _FFR_MILTER
+ /* close out milter filters */
+ milter_quit(e);
+# endif /* _FFR_MILTER */
+
+ message("421 4.4.1 %s Lost input channel from %s",
MyHostName, CurSmtpClient);
if (LogLevel > (gotmail ? 1 : 19))
sm_syslog(LOG_NOTICE, e->e_id,
- "lost input channel from %.100s",
- CurSmtpClient);
- if (lognullconnection && LogLevel > 5)
- sm_syslog(LOG_INFO, NULL,
- "Null connection from %.100s",
- CurSmtpClient);
-
+ "lost input channel from %.100s to %s after %s",
+ CurSmtpClient, d,
+ (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name);
/*
** If have not accepted mail (DATA), do not bounce
** bad addresses back to sender.
*/
+
if (bitset(EF_CLRQUEUE, e->e_flags))
e->e_sendqueue = NULL;
-
- if (InChild)
- ExitStat = EX_QUIT;
- finis(TRUE, ExitStat);
+ goto doquit;
}
/* clean up end of line */
fixcrlf(inp, TRUE);
+# if SASL
+ if (authenticating == SASL_PROC_AUTH)
+ {
+# if 0
+ if (*inp == '\0')
+ {
+ authenticating = SASL_NOT_AUTH;
+ message("501 5.5.2 missing input");
+ continue;
+ }
+# endif /* 0 */
+ if (*inp == '*' && *(inp + 1) == '\0')
+ {
+ authenticating = SASL_NOT_AUTH;
+
+ /* rfc 2254 4. */
+ message("501 5.0.0 AUTH aborted");
+ continue;
+ }
+
+ /* could this be shorter? XXX */
+ out = xalloc(strlen(inp));
+ result = sasl_decode64(inp, strlen(inp), out, &outlen);
+ if (result != SASL_OK)
+ {
+ authenticating = SASL_NOT_AUTH;
+
+ /* rfc 2254 4. */
+ message("501 5.5.4 cannot decode AUTH parameter %s",
+ inp);
+ continue;
+ }
+
+ result = sasl_server_step(conn, out, outlen,
+ &out, &outlen, &errstr);
+
+ /* get an OK if we're done */
+ if (result == SASL_OK)
+ {
+ authenticated:
+ message("235 2.0.0 OK Authenticated");
+ authenticating = SASL_IS_AUTH;
+ define(macid("{auth_type}", NULL),
+ newstr(auth_type), &BlankEnvelope);
+
+ result = sasl_getprop(conn, SASL_USERNAME,
+ (void **)&user);
+ if (result != SASL_OK)
+ {
+ user = "";
+ define(macid("{auth_authen}", NULL),
+ NULL, &BlankEnvelope);
+ }
+ else
+ {
+ define(macid("{auth_authen}", NULL),
+ newstr(user), &BlankEnvelope);
+ }
+
+# if 0
+ /* get realm? */
+ sasl_getprop(conn, SASL_REALM, (void **) &data);
+# endif /* 0 */
+
+
+# if SFIO
+ /* get security strength (features) */
+ result = sasl_getprop(conn, SASL_SSF,
+ (void **) &ssf);
+ if (result != SASL_OK)
+ {
+ define(macid("{auth_ssf}", NULL),
+ "0", &BlankEnvelope);
+ ssf = NULL;
+ }
+ else
+ {
+ char pbuf[8];
+
+ snprintf(pbuf, sizeof pbuf, "%u", *ssf);
+ define(macid("{auth_ssf}", NULL),
+ newstr(pbuf), &BlankEnvelope);
+ if (tTd(95, 8))
+ dprintf("SASL auth_ssf: %u\n",
+ *ssf);
+ }
+ /*
+ ** only switch to encrypted connection
+ ** if a security layer has been negotiated
+ */
+ if (ssf != NULL && *ssf > 0)
+ {
+ /*
+ ** convert sfio stuff to use SASL
+ ** check return values
+ ** if the call fails,
+ ** fall back to unencrypted version
+ ** unless some cf option requires
+ ** encryption then the connection must
+ ** be aborted
+ */
+ if (sfdcsasl(InChannel, OutChannel,
+ conn) == 0)
+ {
+ /* restart dialogue */
+ gothello = FALSE;
+ OneXact = TRUE;
+ n_helo = 0;
+ }
+ else
+ syserr("503 5.3.3 SASL TLS failed");
+ if (LogLevel > 9)
+ sm_syslog(LOG_INFO,
+ NOQID,
+ "SASL: connection from %.64s: mech=%.16s, id=%.64s, bits=%d",
+ CurSmtpClient,
+ auth_type, user,
+ *ssf);
+ }
+# else /* SFIO */
+ if (LogLevel > 9)
+ sm_syslog(LOG_INFO, NOQID,
+ "SASL: connection from %.64s: mech=%.16s, id=%.64s",
+ CurSmtpClient, auth_type,
+ user);
+# endif /* SFIO */
+ }
+ else if (result == SASL_CONTINUE)
+ {
+ len = ENC64LEN(outlen);
+ out2 = xalloc(len);
+ result = sasl_encode64(out, outlen, out2, len,
+ (u_int *)&out2len);
+ if (result != SASL_OK)
+ {
+ /* correct code? XXX */
+ /* 454 Temp. authentication failure */
+ 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\"]",
+ result, out);
+ /* start over? */
+ authenticating = SASL_NOT_AUTH;
+ }
+ else
+ {
+ message("334 %s", out2);
+ if (tTd(95, 2))
+ dprintf("SASL continue: msg='%s' len=%d\n",
+ out2, out2len);
+ }
+ }
+ else
+ {
+ /* not SASL_OK or SASL_CONT */
+ message("500 5.7.0 authentication failed");
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, e->e_id,
+ "AUTH failure (%s): %s (%d)",
+ auth_type,
+ sasl_errstring(result, NULL,
+ NULL),
+ result);
+ authenticating = SASL_NOT_AUTH;
+ }
+ }
+ else
+ {
+ /* don't want to do any of this if authenticating */
+# 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);
+ "<-- %s",
+ inp);
if (e->e_id == NULL)
- sm_setproctitle(TRUE, "%s: %.80s", CurSmtpClient, inp);
+ sm_setproctitle(TRUE, e, "%s: %.80s",
+ CurSmtpClient, inp);
else
- sm_setproctitle(TRUE, "%s %s: %.80s", e->e_id, CurSmtpClient, inp);
+ sm_setproctitle(TRUE, e, "%s %s: %.80s",
+ qid_printname(e),
+ CurSmtpClient, inp);
/* break off command */
for (p = inp; isascii(*p) && isspace(*p); p++)
@@ -286,9 +728,9 @@ smtp(nullserver, e)
p++;
/* decode command */
- for (c = CmdTab; c->cmdname != NULL; c++)
+ for (c = CmdTab; c->cmd_name != NULL; c++)
{
- if (!strcasecmp(c->cmdname, cmdbuf))
+ if (strcasecmp(c->cmd_name, cmdbuf) == 0)
break;
}
@@ -302,27 +744,47 @@ smtp(nullserver, e)
** to everything.
*/
- if (nullserver != NULL)
+ if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags))
{
- switch (c->cmdcode)
+ switch (c->cmd_code)
{
case CMDQUIT:
case CMDHELO:
case CMDEHLO:
case CMDNOOP:
+ case CMDRSET:
/* process normally */
break;
+ case CMDETRN:
+ if (bitnset(D_ETRNONLY, d_flags) &&
+ nullserver == NULL)
+ break;
+ continue;
+
default:
if (++badcommands > MAXBADCOMMANDS)
- sleep(1);
- usrerr("550 %s", nullserver);
+ {
+ delay *= 2;
+ if (delay >= MAXTIMEOUT)
+ delay = MAXTIMEOUT;
+ (void) sleep(delay);
+ }
+ if (nullserver != NULL)
+ {
+ if (ISSMTPREPLY(nullserver))
+ usrerr(nullserver);
+ else
+ usrerr("550 5.0.0 %s", nullserver);
+ }
+ else
+ usrerr("452 4.4.5 Insufficient disk space; try again later");
continue;
}
}
/* non-null server */
- switch (c->cmdcode)
+ switch (c->cmd_code)
{
case CMDMAIL:
case CMDEXPN:
@@ -331,11 +793,343 @@ smtp(nullserver, e)
lognullconnection = FALSE;
}
- switch (c->cmdcode)
+ switch (c->cmd_code)
{
+# if SASL
+ case CMDAUTH: /* sasl */
+ if (!sasl_ok)
+ {
+ message("503 5.3.3 AUTH not available");
+ break;
+ }
+ if (authenticating == SASL_IS_AUTH)
+ {
+ message("503 5.5.0 Already Authenticated");
+ break;
+ }
+ if (gotmail)
+ {
+ message("503 5.5.0 AUTH not permitted during a mail transaction");
+ break;
+ }
+ if (tempfail)
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_INFO, e->e_id,
+ "SMTP AUTH command (%.100s) from %.100s tempfailed (due to previous checks)",
+ p, CurSmtpClient);
+ usrerr("454 4.7.1 Please try again later");
+ break;
+ }
+
+ ismore = FALSE;
+
+ /* crude way to avoid crack attempts */
+ (void) checksmtpattack(&n_auth, n_mechs + 1, TRUE,
+ "AUTH", e);
+
+ /* make sure it's a valid string */
+ for (q = p; *q != '\0' && isascii(*q); q++)
+ {
+ if (isspace(*q))
+ {
+ *q = '\0';
+ while (*++q != '\0' &&
+ isascii(*q) && isspace(*q))
+ continue;
+ *(q - 1) = '\0';
+ ismore = (*q != '\0');
+ break;
+ }
+ }
+
+ /* check whether mechanism is available */
+ if (iteminlist(p, mechlist, " ") == NULL)
+ {
+ message("503 5.3.3 AUTH mechanism %s not available",
+ p);
+ break;
+ }
+
+ if (ismore)
+ {
+ /* could this be shorter? XXX */
+ in = xalloc(strlen(q));
+ result = sasl_decode64(q, strlen(q), in,
+ (u_int *)&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\"]",
+ result, q);
+ /* start over? */
+ authenticating = SASL_NOT_AUTH;
+ in = NULL;
+ 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
+ {
+ in = NULL;
+ inlen = 0;
+ }
+
+ /* see if that auth type exists */
+ result = sasl_server_start(conn, p, in, inlen,
+ &out, &outlen, &errstr);
+
+ if (result != SASL_OK && result != SASL_CONTINUE)
+ {
+ message("500 5.7.0 authentication failed");
+ if (LogLevel > 9)
+ sm_syslog(LOG_ERR, e->e_id,
+ "AUTH failure (%s): %s (%d)",
+ p,
+ sasl_errstring(result, NULL,
+ NULL),
+ result);
+ break;
+ }
+ auth_type = newstr(p);
+
+ if (result == SASL_OK)
+ {
+ /* ugly, but same code */
+ goto authenticated;
+ /* authenticated by the initial response */
+ }
+
+ /* len is at least 2 */
+ len = ENC64LEN(outlen);
+ out2 = xalloc(len);
+ result = sasl_encode64(out, outlen, out2, len,
+ (u_int *)&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\"]",
+ result, out);
+
+ /* start over? */
+ authenticating = SASL_NOT_AUTH;
+ }
+ else
+ {
+ message("334 %s", out2);
+ authenticating = SASL_PROC_AUTH;
+ }
+
+ break;
+# endif /* SASL */
+
+# if STARTTLS
+ case CMDSTLS: /* starttls */
+ if (*p != '\0')
+ {
+ message("501 5.5.2 Syntax error (no parameters allowed)");
+ break;
+ }
+ if (!usetls)
+ {
+ message("503 5.5.0 TLS not available");
+ break;
+ }
+ if (!tls_ok)
+ {
+ message("454 4.3.3 TLS not available after start");
+ break;
+ }
+ if (gotmail)
+ {
+ message("503 5.5.0 TLS not permitted during a mail transaction");
+ break;
+ }
+ if (tempfail)
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_INFO, e->e_id,
+ "SMTP STARTTLS command (%.100s) from %.100s tempfailed (due to previous checks)",
+ p, CurSmtpClient);
+ usrerr("454 4.7.1 Please try again later");
+ break;
+ }
+# if TLS_NO_RSA
+ /*
+ ** XXX do we need a temp key ?
+ */
+# else /* TLS_NO_RSA */
+ if (SSL_CTX_need_tmp_RSA(srv_ctx) &&
+ !SSL_CTX_set_tmp_rsa(srv_ctx,
+ (rsa = RSA_generate_key(RSA_KEYLENGTH, RSA_F4,
+ NULL, NULL)))
+ )
+ {
+ message("454 4.3.3 TLS not available: error generating RSA temp key");
+ if (rsa != NULL)
+ RSA_free(rsa);
+ break;
+ }
+# endif /* TLS_NO_RSA */
+ if (srv_ssl != NULL)
+ SSL_clear(srv_ssl);
+ else if ((srv_ssl = SSL_new(srv_ctx)) == NULL)
+ {
+ message("454 4.3.3 TLS not available: error generating SSL handle");
+ break;
+ }
+ if (SSL_set_rfd(srv_ssl, fileno(InChannel)) <= 0 ||
+ SSL_set_wfd(srv_ssl, fileno(OutChannel)) <= 0)
+ {
+ message("454 4.3.3 TLS not available: error set fd");
+ SSL_free(srv_ssl);
+ srv_ssl = NULL;
+ break;
+ }
+ message("220 2.0.0 Ready to start TLS");
+ SSL_set_accept_state(srv_ssl);
+
+# define SSL_ACC(s) SSL_accept(s)
+ if ((r = SSL_ACC(srv_ssl)) <= 0)
+ {
+ int i;
+
+ /* what to do in this case? */
+ i = SSL_get_error(srv_ssl, r);
+ if (LogLevel > 5)
+ {
+ sm_syslog(LOG_WARNING, e->e_id,
+ "TLS: error: accept failed=%d (%d)",
+ r, i);
+ if (LogLevel > 9)
+ tlslogerr();
+ }
+ tls_ok = FALSE;
+ SSL_free(srv_ssl);
+ srv_ssl = NULL;
+
+ /*
+ ** according to the next draft of
+ ** RFC 2487 the connection should be dropped
+ */
+
+ /* arrange to ignore any current send list */
+ e->e_sendqueue = NULL;
+ goto doquit;
+ }
+
+ /* ignore return code for now, it's in {verify} */
+ (void) tls_get_info(srv_ssl, &BlankEnvelope, TRUE,
+ CurSmtpClient);
+
+ /*
+ ** call Stls_client to find out whether
+ ** to accept the connection from the client
+ */
+
+ saveQuickAbort = QuickAbort;
+ saveSuprErrs = SuprErrs;
+ SuprErrs = TRUE;
+ QuickAbort = FALSE;
+ if (rscheck("tls_client",
+ macvalue(macid("{verify}", NULL), e),
+ "STARTTLS", e, TRUE, TRUE, 6) != EX_OK ||
+ Errors > 0)
+ {
+ extern char MsgBuf[];
+
+ if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf))
+ nullserver = newstr(MsgBuf);
+ else
+ nullserver = "503 5.7.0 Authentication required.";
+ }
+ QuickAbort = saveQuickAbort;
+ SuprErrs = saveSuprErrs;
+
+ tls_ok = FALSE; /* don't offer STARTTLS again */
+ gothello = FALSE; /* discard info */
+ n_helo = 0;
+ OneXact = TRUE; /* only one xaction this run */
+# if SASL
+ if (sasl_ok)
+ {
+ char *s;
+
+ if ((s = macvalue(macid("{cipher_bits}", NULL), e)) != NULL &&
+ (ext_ssf.ssf = atoi(s)) > 0)
+ {
+# if _FFR_EXT_MECH
+ ext_ssf.auth_id = macvalue(macid("{cert_subject}",
+ NULL),
+ e);
+# endif /* _FFR_EXT_MECH */
+ sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
+ &ext_ssf) == SASL_OK;
+ if (mechlist != NULL)
+ free(mechlist);
+ mechlist = NULL;
+ if (sasl_ok)
+ {
+ n_mechs = saslmechs(conn,
+ &mechlist);
+ sasl_ok = n_mechs > 0;
+ }
+ }
+ }
+# endif /* SASL */
+
+ /* switch to secure connection */
+#if SFIO
+ r = sfdctls(InChannel, OutChannel, srv_ssl);
+#else /* SFIO */
+# if _FFR_TLS_TOREK
+ r = sfdctls(&InChannel, &OutChannel, srv_ssl);
+# endif /* _FFR_TLS_TOREK */
+#endif /* SFIO */
+ if (r == 0)
+ tls_active = TRUE;
+ else
+ {
+ /*
+ ** XXX this is an internal error
+ ** how to deal with it?
+ ** we can't generate an error message
+ ** since the other side switched to an
+ ** encrypted layer, but we could not...
+ ** just "hang up"?
+ */
+ nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer";
+ syserr("TLS: can't switch to encrypted layer");
+ }
+ break;
+# endif /* STARTTLS */
+
case CMDHELO: /* hello -- introduce yourself */
case CMDEHLO: /* extended hello */
- if (c->cmdcode == CMDEHLO)
+ if (c->cmd_code == CMDEHLO)
{
protocol = "ESMTP";
SmtpPhase = "server EHLO";
@@ -347,7 +1141,8 @@ smtp(nullserver, e)
}
/* avoid denial-of-service */
- checksmtpattack(&n_helo, MAXHELOCOMMANDS, "HELO/EHLO", e);
+ (void) checksmtpattack(&n_helo, MAXHELOCOMMANDS, TRUE,
+ "HELO/EHLO", e);
/* check for duplicate HELO/EHLO per RFC 1651 4.2 */
if (gothello)
@@ -369,6 +1164,10 @@ smtp(nullserver, e)
if (strlen(p) > MAXNAME)
{
usrerr("501 Invalid domain name");
+ if (LogLevel > 9)
+ sm_syslog(LOG_INFO, CurEnv->e_id,
+ "invalid domain name (too long) from %.100s",
+ CurSmtpClient);
break;
}
@@ -386,6 +1185,7 @@ smtp(nullserver, e)
if (strchr("[].-_#", *q) == NULL)
break;
}
+
if (*q == '\0')
{
q = "pleased to meet you";
@@ -394,6 +1194,10 @@ smtp(nullserver, e)
else if (!AllowBogusHELO)
{
usrerr("501 Invalid domain name");
+ if (LogLevel > 9)
+ sm_syslog(LOG_INFO, CurEnv->e_id,
+ "invalid domain name (%.100s) from %.100s",
+ p, CurSmtpClient);
break;
}
else
@@ -402,9 +1206,36 @@ smtp(nullserver, e)
}
gothello = TRUE;
-
+
+# if _FFR_MILTER
+ if (milterize && !bitset(EF_DISCARD, e->e_flags))
+ {
+ char state;
+ char *response;
+
+ response = milter_helo(p, e, &state);
+ switch (state)
+ {
+ case SMFIR_REPLYCODE:
+ nullserver = response;
+ milterize = FALSE;
+ break;
+
+ case SMFIR_REJECT:
+ nullserver = "Command rejected";
+ milterize = FALSE;
+ break;
+
+ case SMFIR_TEMPFAIL:
+ tempfail = TRUE;
+ milterize = FALSE;
+ break;
+ }
+ }
+# endif /* _FFR_MILTER */
+
/* print HELO response message */
- if (c->cmdcode != CMDEHLO || nullserver != NULL)
+ if (c->cmd_code != CMDEHLO)
{
message("250 %s Hello %s, %s",
MyHostName, CurSmtpClient, q);
@@ -414,28 +1245,47 @@ smtp(nullserver, e)
message("250-%s Hello %s, %s",
MyHostName, CurSmtpClient, q);
+ /* offer ENHSC even for nullserver */
+ if (nullserver != NULL)
+ {
+ message("250 ENHANCEDSTATUSCODES");
+ break;
+ }
+
/* print EHLO features list */
+ message("250-ENHANCEDSTATUSCODES");
if (!bitset(PRIV_NOEXPN, PrivacyFlags))
{
message("250-EXPN");
if (!bitset(PRIV_NOVERB, PrivacyFlags))
message("250-VERB");
}
-#if MIME8TO7
+# if MIME8TO7
message("250-8BITMIME");
-#endif
+# endif /* MIME8TO7 */
if (MaxMessageSize > 0)
message("250-SIZE %ld", MaxMessageSize);
else
message("250-SIZE");
-#if DSN
- if (SendMIMEErrors)
+# if DSN
+ if (SendMIMEErrors &&
+ !bitset(PRIV_NORECEIPTS, PrivacyFlags))
message("250-DSN");
-#endif
+# endif /* DSN */
message("250-ONEX");
- if (!bitset(PRIV_NOETRN, PrivacyFlags))
+ if (!bitset(PRIV_NOETRN, PrivacyFlags) &&
+ !bitnset(D_NOETRN, d_flags))
message("250-ETRN");
message("250-XUSR");
+
+# if SASL
+ if (sasl_ok && mechlist != NULL && *mechlist != '\0')
+ message("250-AUTH %s", mechlist);
+# endif /* SASL */
+# if STARTTLS
+ if (tls_ok && usetls)
+ message("250-STARTTLS");
+# endif /* STARTTLS */
message("250 HELP");
break;
@@ -445,32 +1295,56 @@ smtp(nullserver, e)
/* check for validity of this command */
if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
{
- usrerr("503 Polite people say HELO first");
+ usrerr("503 5.0.0 Polite people say HELO first");
break;
}
if (gotmail)
{
- usrerr("503 Sender already specified");
+ usrerr("503 5.5.0 Sender already specified");
break;
}
if (InChild)
{
errno = 0;
- syserr("503 Nested MAIL command: MAIL %s", p);
+ syserr("503 5.5.0 Nested MAIL command: MAIL %s", p);
finis(TRUE, ExitStat);
}
+# if SASL
+ if (bitnset(D_AUTHREQ, d_flags) &&
+ authenticating != SASL_IS_AUTH)
+ {
+ usrerr("530 5.7.0 Authentication required");
+ break;
+ }
+# endif /* SASL */
+
+ p = skipword(p, "from");
+ if (p == NULL)
+ break;
+ if (tempfail)
+ {
+ if (LogLevel > 9)
+ 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");
+ break;
+ }
/* make sure we know who the sending host is */
if (sendinghost == NULL)
sendinghost = peerhostname;
- p = skipword(p, "from");
- if (p == NULL)
- break;
/* fork a subprocess to process this command */
- if (runinchild("SMTP-MAIL", e) > 0)
+ ric = runinchild("SMTP-MAIL", e);
+
+ /* Catch a problem and stop processing */
+ if (ric == RIC_TEMPFAIL && nullserver == NULL)
+ nullserver = "452 4.3.0 Internal software error";
+ if (ric != RIC_INCHILD)
break;
+
if (Errors > 0)
goto undo_subproc_no_pm;
if (!gothello)
@@ -479,7 +1353,7 @@ smtp(nullserver, e)
"%s didn't use HELO protocol",
CurSmtpClient);
}
-#ifdef PICKY_HELO_CHECK
+# ifdef PICKY_HELO_CHECK
if (strcasecmp(sendinghost, peerhostname) != 0 &&
(strcasecmp(peerhostname, "localhost") != 0 ||
strcasecmp(sendinghost, MyHostName) != 0))
@@ -487,18 +1361,21 @@ smtp(nullserver, e)
auth_warning(e, "Host %s claimed to be %s",
CurSmtpClient, sendinghost);
}
-#endif
+# endif /* PICKY_HELO_CHECK */
if (protocol == NULL)
protocol = "SMTP";
define('r', protocol, e);
define('s', sendinghost, e);
- initsys(e);
+
if (Errors > 0)
goto undo_subproc_no_pm;
nrcpts = 0;
- e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
- sm_setproctitle(TRUE, "%s %s: %.80s", e->e_id, CurSmtpClient, inp);
+ define(macid("{ntries}", NULL), "0", e);
+ e->e_flags |= EF_CLRQUEUE;
+ sm_setproctitle(TRUE, e, "%s %s: %.80s",
+ qid_printname(e),
+ CurSmtpClient, inp);
/* child -- go do the processing */
if (setjmp(TopFrame) > 0)
@@ -512,6 +1389,12 @@ smtp(nullserver, e)
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;
@@ -526,16 +1409,37 @@ smtp(nullserver, e)
if (Errors > 0)
goto undo_subproc_no_pm;
- /* do config file checking of the sender */
- if (rscheck("check_mail", p, NULL, e) != EX_OK ||
- Errors > 0)
- goto undo_subproc_no_pm;
+ /* 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);
+ else
+ define(macid("{mail_mailer}", NULL),
+ NULL, e);
+ if (e->e_from.q_host != NULL)
+ define(macid("{mail_host}", NULL),
+ e->e_from.q_host, e);
+ else
+ define(macid("{mail_host}", NULL),
+ "localhost", e);
+ if (e->e_from.q_user != NULL)
+ define(macid("{mail_addr}", NULL),
+ e->e_from.q_user, e);
+ else
+ define(macid("{mail_addr}", NULL),
+ NULL, e);
+ if (Errors > 0)
+ goto undo_subproc_no_pm;
/* check for possible spoofing */
if (RealUid != 0 && OpMode == MD_SMTP &&
!wordinclass(RealUserName, 't') &&
- !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
- strcmp(e->e_from.q_user, RealUserName) != 0)
+ (!bitnset(M_LOCALMAILER,
+ e->e_from.q_mailer->m_flags) ||
+ strcmp(e->e_from.q_user, RealUserName) != 0))
{
auth_warning(e, "%s owned process doing -bs",
RealUserName);
@@ -543,12 +1447,15 @@ smtp(nullserver, e)
/* now parse ESMTP arguments */
e->e_msgsize = 0;
+ addr = p;
+ argno = 0;
+ args[argno++] = p;
p = delimptr;
while (p != NULL && *p != '\0')
{
char *kp;
char *vp = NULL;
- extern void mail_esmtp_args __P((char *, char *, ENVELOPE *));
+ char *equal = NULL;
/* locate the beginning of the keyword */
while (isascii(*p) && isspace(*p))
@@ -562,6 +1469,7 @@ smtp(nullserver, e)
p++;
if (*p == '=')
{
+ equal = p;
*p++ = '\0';
vp = p;
@@ -576,44 +1484,90 @@ smtp(nullserver, e)
*p++ = '\0';
if (tTd(19, 1))
- printf("MAIL: got arg %s=\"%s\"\n", kp,
+ dprintf("MAIL: got arg %s=\"%s\"\n", kp,
vp == NULL ? "<null>" : vp);
mail_esmtp_args(kp, vp, e);
+ if (equal != NULL)
+ *equal = '=';
+ args[argno++] = kp;
+ if (argno >= MAXSMTPARGS - 1)
+ usrerr("501 5.5.4 Too many parameters");
if (Errors > 0)
goto undo_subproc_no_pm;
}
+ args[argno] = NULL;
if (Errors > 0)
goto undo_subproc_no_pm;
+ /* do config file checking of the sender */
+ if (rscheck("check_mail", addr,
+ NULL, e, TRUE, TRUE, 4) != EX_OK ||
+ Errors > 0)
+ goto undo_subproc_no_pm;
+
if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
{
- usrerr("552 Message size exceeds fixed maximum message size (%ld)",
+ usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
MaxMessageSize);
goto undo_subproc_no_pm;
}
-
- if (!enoughdiskspace(e->e_msgsize))
+
+ if (!enoughdiskspace(e->e_msgsize, TRUE))
{
- usrerr("452 Insufficient disk space; try again later");
+ usrerr("452 4.4.5 Insufficient disk space; try again later");
goto undo_subproc_no_pm;
}
if (Errors > 0)
goto undo_subproc_no_pm;
- message("250 Sender ok");
+
+# if _FFR_MILTER
+ LogUsrErrs = TRUE;
+ if (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 Try again later");
+ break;
+ }
+ if (response != NULL)
+ free(response);
+ }
+# endif /* _FFR_MILTER */
+ if (Errors > 0)
+ goto undo_subproc_no_pm;
+
+ message("250 2.1.0 Sender ok");
gotmail = TRUE;
break;
case CMDRCPT: /* rcpt -- designate recipient */
if (!gotmail)
{
- usrerr("503 Need MAIL before RCPT");
+ usrerr("503 5.0.0 Need MAIL before RCPT");
break;
}
SmtpPhase = "server RCPT";
if (setjmp(TopFrame) > 0)
{
- e->e_flags &= ~EF_FATALERRS;
+ e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
break;
}
QuickAbort = TRUE;
@@ -622,40 +1576,80 @@ smtp(nullserver, e)
/* limit flooding of our machine */
if (MaxRcptPerMsg > 0 && nrcpts >= MaxRcptPerMsg)
{
- usrerr("452 Too many recipients");
+ usrerr("452 4.5.3 Too many recipients");
break;
}
if (e->e_sendmode != SM_DELIVER)
e->e_flags |= EF_VRFYONLY;
+# if _FFR_MILTER
+ /*
+ ** If the filter will be deleting recipients,
+ ** don't expand them at RCPT time (in the call
+ ** to recipient()). If they are expanded, it
+ ** is impossible for removefromlist() to figure
+ ** out the expanded members of the original
+ ** recipient and mark them as QS_DONTSEND.
+ */
+
+ if (milter_can_delrcpts())
+ e->e_flags |= EF_VRFYONLY;
+# endif /* _FFR_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 */
if (Errors > 0)
break;
if (a == NULL)
{
- usrerr("501 Missing recipient");
+ usrerr("501 5.0.0 Missing recipient");
break;
}
if (delimptr != NULL && *delimptr != '\0')
*delimptr++ = '\0';
- /* do config file checking of the recipient */
- if (rscheck("check_rcpt", p, NULL, e) != EX_OK ||
- Errors > 0)
+ /* put resulting triple from parseaddr() into macros */
+ if (a->q_mailer != NULL)
+ define(macid("{rcpt_mailer}", NULL),
+ a->q_mailer->m_name, e);
+ else
+ define(macid("{rcpt_mailer}", NULL),
+ NULL, e);
+ if (a->q_host != NULL)
+ define(macid("{rcpt_host}", NULL),
+ a->q_host, e);
+ else
+ define(macid("{rcpt_host}", NULL),
+ "localhost", e);
+ if (a->q_user != NULL)
+ define(macid("{rcpt_addr}", NULL),
+ a->q_user, e);
+ else
+ define(macid("{rcpt_addr}", NULL),
+ NULL, e);
+ if (Errors > 0)
break;
/* now parse ESMTP arguments */
+ addr = p;
+ argno = 0;
+ args[argno++] = p;
p = delimptr;
while (p != NULL && *p != '\0')
{
char *kp;
char *vp = NULL;
- extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
+ char *equal = NULL;
/* locate the beginning of the keyword */
while (isascii(*p) && isspace(*p))
@@ -669,6 +1663,7 @@ smtp(nullserver, e)
p++;
if (*p == '=')
{
+ equal = p;
*p++ = '\0';
vp = p;
@@ -683,13 +1678,62 @@ smtp(nullserver, e)
*p++ = '\0';
if (tTd(19, 1))
- printf("RCPT: got arg %s=\"%s\"\n", kp,
+ dprintf("RCPT: got arg %s=\"%s\"\n", kp,
vp == NULL ? "<null>" : vp);
rcpt_esmtp_args(a, kp, vp, e);
+ if (equal != NULL)
+ *equal = '=';
+ args[argno++] = kp;
+ if (argno >= MAXSMTPARGS - 1)
+ usrerr("501 5.5.4 Too many parameters");
if (Errors > 0)
break;
}
+ args[argno] = NULL;
+ if (Errors > 0)
+ break;
+
+ /* do config file checking of the recipient */
+ if (rscheck("check_rcpt", addr,
+ NULL, e, TRUE, TRUE, 4) != EX_OK ||
+ Errors > 0)
+ break;
+
+# if _FFR_MILTER
+ if (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 Try again later");
+ break;
+ }
+ if (response != NULL)
+ 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);
if (Errors > 0)
break;
@@ -700,17 +1744,19 @@ smtp(nullserver, e)
/* no errors during parsing, but might be a duplicate */
e->e_to = a->q_paddr;
- if (!bitset(QBADADDR, a->q_flags))
+ if (!QS_IS_BADADDR(a->q_state))
{
- message("250 Recipient ok%s",
- bitset(QQUEUEUP, a->q_flags) ?
+ if (e->e_queuedir == NOQDIR)
+ initsys(e);
+ message("250 2.1.5 Recipient ok%s",
+ QS_IS_QUEUEUP(a->q_state) ?
" (will queue)" : "");
nrcpts++;
}
else
{
/* punt -- should keep message in ADDRESS.... */
- usrerr("550 Addressee unknown");
+ usrerr("550 5.1.1 Addressee unknown");
}
break;
@@ -718,12 +1764,12 @@ smtp(nullserver, e)
SmtpPhase = "server DATA";
if (!gotmail)
{
- usrerr("503 Need MAIL command");
+ usrerr("503 5.0.0 Need MAIL command");
break;
}
else if (nrcpts <= 0)
{
- usrerr("503 Need RCPT (recipient)");
+ usrerr("503 5.0.0 Need RCPT (recipient)");
break;
}
@@ -732,21 +1778,20 @@ smtp(nullserver, e)
e->e_flags |= EF_DISCARD;
/* check to see if we need to re-expand aliases */
- /* also reset QBADADDR on already-diagnosted addrs */
+ /* also reset QS_BADADDR on already-diagnosted addrs */
doublequeue = FALSE;
for (a = e->e_sendqueue; a != NULL; a = a->q_next)
{
- if (bitset(QVERIFIED, a->q_flags) &&
+ if (QS_IS_VERIFIED(a->q_state) &&
!bitset(EF_DISCARD, e->e_flags))
{
/* need to re-expand aliases */
doublequeue = TRUE;
}
- if (bitset(QBADADDR, a->q_flags))
+ if (QS_IS_BADADDR(a->q_state))
{
/* make this "go away" */
- a->q_flags |= QDONTSEND;
- a->q_flags &= ~QBADADDR;
+ a->q_state = QS_DONTSEND;
}
}
@@ -754,8 +1799,71 @@ smtp(nullserver, e)
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 Try again later");
+ break;
+ }
+ if (response != NULL)
+ 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)
+ 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;
@@ -787,9 +1895,19 @@ smtp(nullserver, e)
*/
SmtpPhase = "delivery";
- e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
+ (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 */
@@ -798,28 +1916,43 @@ smtp(nullserver, e)
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 %s Message accepted for delivery", id);
+ 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 = getla();
+ 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(id, TRUE, TRUE, 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);
@@ -832,14 +1965,24 @@ smtp(nullserver, 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 Test failure");
+ message("451 4.0.0 Test failure");
else
- message("250 Reset state");
+ 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);
@@ -852,28 +1995,39 @@ smtp(nullserver, e)
case CMDVRFY: /* vrfy -- verify address */
case CMDEXPN: /* expn -- expand address */
- checksmtpattack(&nverifies, MAXVRFYCOMMANDS,
- c->cmdcode == CMDVRFY ? "VRFY" : "EXPN", e);
- vrfy = c->cmdcode == CMDVRFY;
+ if (tempfail)
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_INFO, e->e_id,
+ "SMTP %s command (%.100s) from %.100s tempfailed (due to previous checks)",
+ c->cmd_code == CMDVRFY ? "VRFY" : "EXPN",
+ p, CurSmtpClient);
+ usrerr("550 5.7.1 Please try again later");
+ break;
+ }
+ wt = checksmtpattack(&nverifies, MAXVRFYCOMMANDS, FALSE,
+ c->cmd_code == CMDVRFY ? "VRFY" : "EXPN", e);
+ previous = curtime();
+ vrfy = c->cmd_code == CMDVRFY;
if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
PrivacyFlags))
{
if (vrfy)
- message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
+ message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
else
- message("502 Sorry, we do not allow this operation");
+ message("502 5.7.0 Sorry, we do not allow this operation");
if (LogLevel > 5)
sm_syslog(LOG_INFO, e->e_id,
- "%.100s: %s [rejected]",
- CurSmtpClient,
- shortenstring(inp, MAXSHORTSTR));
+ "%.100s: %s [rejected]",
+ CurSmtpClient,
+ shortenstring(inp, MAXSHORTSTR));
break;
}
else if (!gothello &&
bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
PrivacyFlags))
{
- usrerr("503 I demand that you introduce yourself first");
+ usrerr("503 5.0.0 I demand that you introduce yourself first");
break;
}
if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
@@ -882,9 +2036,9 @@ smtp(nullserver, e)
goto undo_subproc;
if (LogLevel > 5)
sm_syslog(LOG_INFO, e->e_id,
- "%.100s: %s",
- CurSmtpClient,
- shortenstring(inp, MAXSHORTSTR));
+ "%.100s: %s",
+ CurSmtpClient,
+ shortenstring(inp, MAXSHORTSTR));
if (setjmp(TopFrame) > 0)
goto undo_subproc;
QuickAbort = TRUE;
@@ -895,133 +2049,199 @@ smtp(nullserver, e)
p++;
if (*p == '\0')
{
- usrerr("501 Argument required");
+ usrerr("501 5.5.2 Argument required");
}
else
{
+ /* do config file checking of the address */
+ if (rscheck(vrfy ? "check_vrfy" : "check_expn",
+ p, NULL, e, TRUE, FALSE, 4)
+ != EX_OK || Errors > 0)
+ goto undo_subproc;
(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
}
+ if (wt > 0)
+ (void) sleep(wt - (curtime() - previous));
if (Errors > 0)
goto undo_subproc;
if (vrfyqueue == NULL)
{
- usrerr("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
+ usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN");
}
while (vrfyqueue != NULL)
{
- extern void printvrfyaddr __P((ADDRESS *, bool, bool));
+ if (!QS_IS_UNDELIVERED(vrfyqueue->q_state))
+ {
+ vrfyqueue = vrfyqueue->q_next;
+ continue;
+ }
+ /* see if there is more in the vrfy list */
a = vrfyqueue;
while ((a = a->q_next) != NULL &&
- bitset(QDONTSEND|QBADADDR, a->q_flags))
+ (!QS_IS_UNDELIVERED(vrfyqueue->q_state)))
continue;
- if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
- printvrfyaddr(vrfyqueue, a == NULL, vrfy);
- vrfyqueue = vrfyqueue->q_next;
+ printvrfyaddr(vrfyqueue, a == NULL, vrfy);
+ vrfyqueue = a;
}
if (InChild)
finis(TRUE, ExitStat);
break;
case CMDETRN: /* etrn -- force queue flush */
- if (bitset(PRIV_NOETRN, PrivacyFlags))
+ if (bitset(PRIV_NOETRN, PrivacyFlags) ||
+ bitnset(D_NOETRN, d_flags))
{
- message("502 Sorry, we do not allow this operation");
+ /* different message for MSA ? */
+ message("502 5.7.0 Sorry, we do not allow this operation");
if (LogLevel > 5)
sm_syslog(LOG_INFO, e->e_id,
- "%.100s: %s [rejected]",
- CurSmtpClient,
- shortenstring(inp, MAXSHORTSTR));
+ "%.100s: %s [rejected]",
+ CurSmtpClient,
+ shortenstring(inp, MAXSHORTSTR));
+ break;
+ }
+ if (tempfail)
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_INFO, e->e_id,
+ "SMTP ETRN command (%.100s) from %.100s tempfailed (due to previous checks)",
+ p, CurSmtpClient);
+ usrerr("451 4.7.1 Please try again later");
break;
}
if (strlen(p) <= 0)
{
- usrerr("500 Parameter required");
+ usrerr("500 5.5.2 Parameter required");
break;
}
/* crude way to avoid denial-of-service attacks */
- checksmtpattack(&n_etrn, MAXETRNCOMMANDS, "ETRN", e);
+ (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)
+ != EX_OK || Errors > 0)
+ break;
if (LogLevel > 5)
sm_syslog(LOG_INFO, e->e_id,
- "%.100s: ETRN %s",
- CurSmtpClient,
- shortenstring(p, MAXSHORTSTR));
+ "%.100s: ETRN %s",
+ CurSmtpClient,
+ shortenstring(p, MAXSHORTSTR));
id = p;
if (*id == '@')
id++;
else
*--id = '@';
-
+
if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
{
- syserr("500 ETRN out of memory");
+ syserr("500 5.5.0 ETRN out of memory");
break;
}
new->queue_match = id;
new->queue_next = NULL;
QueueLimitRecipient = new;
- ok = runqueue(TRUE, TRUE);
+ ok = runqueue(TRUE, FALSE);
free(QueueLimitRecipient);
QueueLimitRecipient = NULL;
if (ok && Errors == 0)
- message("250 Queuing for node %s started", p);
+ message("250 2.0.0 Queuing for node %s started", p);
break;
case CMDHELP: /* help -- give user info */
- help(p);
+ help(p, e);
break;
case CMDNOOP: /* noop -- do nothing */
- checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "NOOP", e);
- message("250 OK");
+ (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
+ "NOOP", e);
+ message("250 2.0.0 OK");
break;
case CMDQUIT: /* quit -- leave mail */
- message("221 %s closing connection", MyHostName);
+ message("221 2.0.0 %s closing connection", MyHostName);
-doquit:
/* arrange to ignore any current send list */
e->e_sendqueue = NULL;
+# if STARTTLS
+ /* shutdown TLS connection */
+ if (tls_active)
+ {
+ (void) endtls(srv_ssl, "server");
+ tls_active = FALSE;
+ }
+# endif /* STARTTLS */
+# if SASL
+ if (authenticating == SASL_IS_AUTH)
+ {
+ sasl_dispose(&conn);
+ authenticating = SASL_NOT_AUTH;
+ }
+# endif /* SASL */
+
+doquit:
/* avoid future 050 messages */
disconnect(1, e);
+# if _FFR_MILTER
+ /* close out milter filters */
+ milter_quit(e);
+# endif /* _FFR_MILTER */
+
if (InChild)
ExitStat = EX_QUIT;
+
+ if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
+ logsender(e, NULL);
+ e->e_flags &= ~EF_LOGSENDER;
+
if (lognullconnection && LogLevel > 5)
+ {
+ char *d;
+
+ d = macvalue(macid("{daemon_name}", NULL), e);
+ if (d == NULL)
+ d = "stdin";
sm_syslog(LOG_INFO, NULL,
- "Null connection from %.100s",
- CurSmtpClient);
+ "%.100s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
+ CurSmtpClient, d);
+ }
finis(TRUE, ExitStat);
+ /* NOTREACHED */
case CMDVERB: /* set verbose mode */
if (bitset(PRIV_NOEXPN, PrivacyFlags) ||
bitset(PRIV_NOVERB, PrivacyFlags))
{
/* this would give out the same info */
- message("502 Verbose unavailable");
+ message("502 5.7.0 Verbose unavailable");
break;
}
- checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "VERB", e);
+ (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
+ "VERB", e);
Verbose = 1;
- e->e_sendmode = SM_DELIVER;
- message("250 Verbose mode");
+ set_delivery_mode(SM_DELIVER, e);
+ message("250 2.0.0 Verbose mode");
break;
case CMDONEX: /* doing one transaction only */
- checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "ONEX", e);
+ (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
+ "ONEX", e);
OneXact = TRUE;
- message("250 Only one transaction");
+ message("250 2.0.0 Only one transaction");
break;
case CMDXUSR: /* initial (user) submission */
- checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "XUSR", e);
- UserSubmission = TRUE;
- message("250 Initial 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
@@ -1033,39 +2253,51 @@ doquit:
case CMDDBGDEBUG: /* set debug mode */
tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
tTflag(p);
- message("200 Debug set");
+ message("200 2.0.0 Debug set");
break;
-# else /* not SMTPDEBUG */
+# else /* SMTPDEBUG */
case CMDDBGQSHOW: /* show queues */
case CMDDBGDEBUG: /* set debug mode */
# endif /* SMTPDEBUG */
case CMDLOGBOGUS: /* bogus command */
if (LogLevel > 0)
sm_syslog(LOG_CRIT, e->e_id,
- "\"%s\" command from %.100s (%.100s)",
- c->cmdname, CurSmtpClient,
- anynet_ntoa(&RealHostAddr));
- /* FALL THROUGH */
+ "\"%s\" command from %.100s (%.100s)",
+ c->cmd_name, CurSmtpClient,
+ anynet_ntoa(&RealHostAddr));
+ /* FALLTHROUGH */
case CMDERROR: /* unknown command */
if (++badcommands > MAXBADCOMMANDS)
{
- message("421 %s Too many bad commands; closing connection",
+ message("421 4.7.0 %s Too many bad commands; closing connection",
MyHostName);
+
+ /* arrange to ignore any current send list */
+ e->e_sendqueue = NULL;
goto doquit;
}
- usrerr("500 Command unrecognized: \"%s\"",
- shortenstring(inp, MAXSHORTSTR));
+ usrerr("500 5.5.1 Command unrecognized: \"%s\"",
+ shortenstring(inp, MAXSHORTSTR));
+ break;
+
+ case CMDUNIMPL:
+ usrerr("502 5.5.1 Command not implemented: \"%s\"",
+ shortenstring(inp, MAXSHORTSTR));
break;
default:
errno = 0;
- syserr("500 smtp: unknown code %d", c->cmdcode);
+ syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
break;
}
+# if SASL
+ }
+# endif /* SASL */
}
+
}
/*
** CHECKSMTPATTACK -- check for denial-of-service attack by repetition
@@ -1074,6 +2306,7 @@ doquit:
** pcounter -- pointer to a counter for this command.
** maxcount -- maximum value for this counter before we
** slow down.
+** waitnow -- sleep now (in this routine)?
** cname -- command name for logging.
** e -- the current envelope.
**
@@ -1084,23 +2317,38 @@ doquit:
** Slows down if we seem to be under attack.
*/
-void
-checksmtpattack(pcounter, maxcount, cname, e)
+static time_t
+checksmtpattack(pcounter, maxcount, waitnow, cname, e)
volatile int *pcounter;
int maxcount;
+ bool waitnow;
char *cname;
ENVELOPE *e;
{
if (++(*pcounter) >= maxcount)
{
+ time_t s;
+
if (*pcounter == maxcount && LogLevel > 5)
{
sm_syslog(LOG_INFO, e->e_id,
- "%.100s: %.40s attack?",
- CurSmtpClient, cname);
+ "%.100s: %.40s attack?",
+ CurSmtpClient, cname);
}
- sleep(*pcounter / maxcount);
+ s = 1 << (*pcounter - maxcount);
+ if (s >= MAXTIMEOUT)
+ s = MAXTIMEOUT;
+ /* sleep at least 1 second before returning */
+ (void) sleep(*pcounter / maxcount);
+ s -= *pcounter / maxcount;
+ if (waitnow)
+ {
+ (void) sleep(s);
+ return(0);
+ }
+ return(s);
}
+ return((time_t) 0);
}
/*
** SKIPWORD -- skip a fixed word.
@@ -1138,9 +2386,9 @@ skipword(p, w)
if (*p != ':')
{
syntax:
- usrerr("501 Syntax error in parameters scanning \"%s\"",
+ usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"",
shortenstring(firstp, MAXSHORTSTR));
- return (NULL);
+ return NULL;
}
*p++ = '\0';
while (isascii(*p) && isspace(*p))
@@ -1153,7 +2401,7 @@ skipword(p, w)
if (strcasecmp(q, w))
goto syntax;
- return (p);
+ return p;
}
/*
** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
@@ -1167,7 +2415,7 @@ skipword(p, w)
** none.
*/
-void
+static void
mail_esmtp_args(kp, vp, e)
char *kp;
char *vp;
@@ -1177,20 +2425,21 @@ mail_esmtp_args(kp, vp, e)
{
if (vp == NULL)
{
- usrerr("501 SIZE requires a value");
+ usrerr("501 5.5.2 SIZE requires a value");
/* NOTREACHED */
}
+ define(macid("{msg_size}", NULL), newstr(vp), e);
# if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
e->e_msgsize = strtoul(vp, (char **) NULL, 10);
-# else
+# else /* defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY) */
e->e_msgsize = strtol(vp, (char **) NULL, 10);
-# endif
+# endif /* defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY) */
}
else if (strcasecmp(kp, "body") == 0)
{
if (vp == NULL)
{
- usrerr("501 BODY requires a value");
+ usrerr("501 5.5.2 BODY requires a value");
/* NOTREACHED */
}
else if (strcasecmp(vp, "8bitmime") == 0)
@@ -1203,7 +2452,7 @@ mail_esmtp_args(kp, vp, e)
}
else
{
- usrerr("501 Unknown BODY type %s",
+ usrerr("501 5.5.4 Unknown BODY type %s",
vp);
/* NOTREACHED */
}
@@ -1211,33 +2460,44 @@ mail_esmtp_args(kp, vp, e)
}
else if (strcasecmp(kp, "envid") == 0)
{
+ if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
+ {
+ usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN");
+ /* NOTREACHED */
+ }
if (vp == NULL)
{
- usrerr("501 ENVID requires a value");
+ usrerr("501 5.5.2 ENVID requires a value");
/* NOTREACHED */
}
if (!xtextok(vp))
{
- usrerr("501 Syntax error in ENVID parameter value");
+ usrerr("501 5.5.4 Syntax error in ENVID parameter value");
/* NOTREACHED */
}
if (e->e_envid != NULL)
{
- usrerr("501 Duplicate ENVID parameter");
+ usrerr("501 5.5.0 Duplicate ENVID parameter");
/* NOTREACHED */
}
e->e_envid = newstr(vp);
+ define(macid("{dsn_envid}", NULL), newstr(vp), e);
}
else if (strcasecmp(kp, "ret") == 0)
{
+ if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
+ {
+ usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN");
+ /* NOTREACHED */
+ }
if (vp == NULL)
{
- usrerr("501 RET requires a value");
+ usrerr("501 5.5.2 RET requires a value");
/* NOTREACHED */
}
if (bitset(EF_RET_PARAM, e->e_flags))
{
- usrerr("501 Duplicate RET parameter");
+ usrerr("501 5.5.0 Duplicate RET parameter");
/* NOTREACHED */
}
e->e_flags |= EF_RET_PARAM;
@@ -1245,13 +2505,89 @@ mail_esmtp_args(kp, vp, e)
e->e_flags |= EF_NO_BODY_RETN;
else if (strcasecmp(vp, "full") != 0)
{
- usrerr("501 Bad argument \"%s\" to RET", vp);
+ usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
+ /* NOTREACHED */
+ }
+ define(macid("{dsn_ret}", NULL), newstr(vp), e);
+ }
+# if SASL
+ else if (strcasecmp(kp, "auth") == 0)
+ {
+ int len;
+ char *q;
+ char *auth_param; /* the value of the AUTH=x */
+ bool saveQuickAbort = QuickAbort;
+ bool saveSuprErrs = SuprErrs;
+ char pbuf[256];
+
+ if (vp == NULL)
+ {
+ usrerr("501 5.5.2 AUTH= requires a value");
+ /* NOTREACHED */
+ }
+ if (e->e_auth_param != NULL)
+ {
+ usrerr("501 5.5.0 Duplicate AUTH parameter");
/* NOTREACHED */
}
+ if ((q = strchr(vp, ' ')) != NULL)
+ len = q - vp + 1;
+ else
+ len = strlen(vp) + 1;
+ auth_param = xalloc(len);
+ (void) strlcpy(auth_param, vp, len);
+ if (!xtextok(auth_param))
+ {
+ usrerr("501 5.5.4 Syntax error in AUTH parameter value");
+ /* just a warning? */
+ /* NOTREACHED */
+ }
+
+ /* XXX this might be cut off */
+ snprintf(pbuf, sizeof pbuf, "%s", xuntextify(auth_param));
+ /* xalloc() the buffer instead? */
+
+ /* XXX define this always or only if trusted? */
+ define(macid("{auth_author}", NULL), newstr(pbuf), e);
+
+ /*
+ ** call Strust_auth to find out whether
+ ** auth_param is acceptable (trusted)
+ ** we shouldn't trust it if not authenticated
+ ** (required by RFC, leave it to ruleset?)
+ */
+
+ SuprErrs = TRUE;
+ QuickAbort = FALSE;
+ if (strcmp(auth_param, "<>") != 0 &&
+ (rscheck("trust_auth", pbuf, NULL, e, TRUE, FALSE, 10)
+ != EX_OK || Errors > 0))
+ {
+ if (tTd(95, 8))
+ {
+ q = e->e_auth_param;
+ dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
+ pbuf, (q == NULL) ? "" : q);
+ }
+ /* not trusted */
+ e->e_auth_param = newstr("<>");
+ }
+ else
+ {
+ if (tTd(95, 8))
+ dprintf("auth=\"%.100s\" trusted\n", pbuf);
+ e->e_auth_param = newstr(auth_param);
+ }
+ free(auth_param);
+ /* reset values */
+ Errors = 0;
+ QuickAbort = saveQuickAbort;
+ SuprErrs = saveSuprErrs;
}
+# endif /* SASL */
else
{
- usrerr("501 %s parameter unrecognized", kp);
+ usrerr("501 5.5.4 %s parameter unrecognized", kp);
/* NOTREACHED */
}
}
@@ -1268,7 +2604,7 @@ mail_esmtp_args(kp, vp, e)
** none.
*/
-void
+static void
rcpt_esmtp_args(a, kp, vp, e)
ADDRESS *a;
char *kp;
@@ -1279,13 +2615,20 @@ rcpt_esmtp_args(a, kp, vp, e)
{
char *p;
+ if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
+ {
+ usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN");
+ /* NOTREACHED */
+ }
if (vp == NULL)
{
- usrerr("501 NOTIFY requires a value");
+ usrerr("501 5.5.2 NOTIFY requires a value");
/* NOTREACHED */
}
a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
a->q_flags |= QHASNOTIFY;
+ define(macid("{dsn_notify}", NULL), newstr(vp), e);
+
if (strcasecmp(vp, "never") == 0)
return;
for (p = vp; p != NULL; vp = p)
@@ -1301,7 +2644,7 @@ rcpt_esmtp_args(a, kp, vp, e)
a->q_flags |= QPINGONDELAY;
else
{
- usrerr("501 Bad argument \"%s\" to NOTIFY",
+ usrerr("501 5.5.4 Bad argument \"%s\" to NOTIFY",
vp);
/* NOTREACHED */
}
@@ -1309,26 +2652,31 @@ rcpt_esmtp_args(a, kp, vp, e)
}
else if (strcasecmp(kp, "orcpt") == 0)
{
+ if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
+ {
+ usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN");
+ /* NOTREACHED */
+ }
if (vp == NULL)
{
- usrerr("501 ORCPT requires a value");
+ usrerr("501 5.5.2 ORCPT requires a value");
/* NOTREACHED */
}
if (strchr(vp, ';') == NULL || !xtextok(vp))
{
- usrerr("501 Syntax error in ORCPT parameter value");
+ usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
/* NOTREACHED */
}
if (a->q_orcpt != NULL)
{
- usrerr("501 Duplicate ORCPT parameter");
+ usrerr("501 5.5.0 Duplicate ORCPT parameter");
/* NOTREACHED */
}
a->q_orcpt = newstr(vp);
}
else
{
- usrerr("501 %s parameter unrecognized", kp);
+ usrerr("501 5.5.4 %s parameter unrecognized", kp);
/* NOTREACHED */
}
}
@@ -1346,36 +2694,47 @@ rcpt_esmtp_args(a, kp, vp, e)
** Side Effects:
** Prints the appropriate 250 codes.
*/
+#define OFFF (3 + 1 + 5 + 1) /* offset in fmt: SMTP reply + enh. code */
-void
+static void
printvrfyaddr(a, last, vrfy)
register ADDRESS *a;
bool last;
bool vrfy;
{
- char fmtbuf[20];
+ char fmtbuf[30];
if (vrfy && a->q_mailer != NULL &&
!bitnset(M_VRFY250, a->q_mailer->m_flags))
- strcpy(fmtbuf, "252");
+ (void) strlcpy(fmtbuf, "252", sizeof fmtbuf);
else
- strcpy(fmtbuf, "250");
+ (void) strlcpy(fmtbuf, "250", sizeof fmtbuf);
fmtbuf[3] = last ? ' ' : '-';
-
+ (void) strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4);
if (a->q_fullname == NULL)
{
- if (strchr(a->q_user, '@') == NULL)
- strcpy(&fmtbuf[4], "<%s@%s>");
+ if ((a->q_mailer == NULL ||
+ a->q_mailer->m_addrtype == NULL ||
+ strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
+ strchr(a->q_user, '@') == NULL)
+ (void) strlcpy(&fmtbuf[OFFF], "<%s@%s>",
+ sizeof fmtbuf - OFFF);
else
- strcpy(&fmtbuf[4], "<%s>");
+ (void) strlcpy(&fmtbuf[OFFF], "<%s>",
+ sizeof fmtbuf - OFFF);
message(fmtbuf, a->q_user, MyHostName);
}
else
{
- if (strchr(a->q_user, '@') == NULL)
- strcpy(&fmtbuf[4], "%s <%s@%s>");
+ if ((a->q_mailer == NULL ||
+ a->q_mailer->m_addrtype == NULL ||
+ strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
+ strchr(a->q_user, '@') == NULL)
+ (void) strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
+ sizeof fmtbuf - OFFF);
else
- strcpy(&fmtbuf[4], "%s <%s>");
+ (void) strlcpy(&fmtbuf[OFFF], "%s <%s>",
+ sizeof fmtbuf - OFFF);
message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
}
}
@@ -1386,14 +2745,15 @@ printvrfyaddr(a, last, vrfy)
** label -- a string used in error messages
**
** Returns:
-** zero in the child
-** one in the parent
+** RIC_INCHILD in the child
+** RIC_INPARENT in the parent
+** RIC_TEMPFAIL tempfail condition
**
** Side Effects:
** none.
*/
-int
+static int
runinchild(label, e)
char *label;
register ENVELOPE *e;
@@ -1402,8 +2762,19 @@ runinchild(label, e)
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 preceeded
+ ** Disable child process reaping, in case ETRN has preceded
** MAIL command, and then fork.
*/
@@ -1412,24 +2783,28 @@ runinchild(label, e)
childpid = dofork();
if (childpid < 0)
{
- syserr("451 %s: cannot fork", label);
+ syserr("451 4.3.0 %s: cannot fork", label);
(void) releasesignal(SIGCHLD);
- return (1);
+ return RIC_INPARENT;
}
if (childpid > 0)
{
auto int st;
/* parent -- wait for child to complete */
- sm_setproctitle(TRUE, "server %s child wait", CurSmtpClient);
+ sm_setproctitle(TRUE, e, "server %s child wait",
+ CurSmtpClient);
st = waitfor(childpid);
if (st == -1)
- syserr("451 %s: lost child", label);
+ syserr("451 4.3.0 %s: lost child", label);
else if (!WIFEXITED(st))
- syserr("451 %s: died on signal %d",
- label, st & 0177);
+ {
+ syserr("451 4.3.0 %s: died on signal %d",
+ label, st & 0177);
+ return RIC_TEMPFAIL;
+ }
- /* if we exited on a QUIT command, complete the process */
+ /* if exited on a QUIT command, complete the process */
if (WEXITSTATUS(st) == EX_QUIT)
{
disconnect(1, e);
@@ -1439,27 +2814,1300 @@ runinchild(label, e)
/* restore the child signal */
(void) releasesignal(SIGCHLD);
- return (1);
+ return RIC_INPARENT;
}
else
{
/* child */
InChild = TRUE;
QuickAbort = FALSE;
+ clearstats();
clearenvelope(e, FALSE);
+ assign_queueid(e);
(void) setsignal(SIGCHLD, SIG_DFL);
(void) releasesignal(SIGCHLD);
}
}
- return (0);
+ return RIC_INCHILD;
}
-# endif /* SMTP */
+# if SASL
+
+ /*
+** SASLMECHS -- get list of possible AUTH mechanisms
+**
+** Parameters:
+** conn -- SASL connection info
+** mechlist -- output parameter for list of mechanisms
+**
+** Returns:
+** number of mechs
+*/
+
+static int
+saslmechs(conn, mechlist)
+ sasl_conn_t *conn;
+ char **mechlist;
+{
+ int len, num, result;
+
+ /* "user" is currently unused */
+ result = sasl_listmech(conn, "user", /* XXX */
+ "", " ", "", mechlist,
+ (u_int *)&len, (u_int *)&num);
+ if (result == SASL_OK && num > 0)
+ {
+ if (LogLevel > 11)
+ sm_syslog(LOG_INFO, NOQID,
+ "SASL: available mech=%s, allowed mech=%s",
+ *mechlist, AuthMechanisms);
+ *mechlist = intersect(AuthMechanisms, *mechlist);
+ }
+ else
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, NOQID,
+ "SASL error: listmech=%d, num=%d",
+ result, num);
+ }
+ 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)
+**
+** Returns:
+** ok?
+*/
+
+int
+proxy_policy(context, auth_identity, requested_user, user, errstr)
+ void *context;
+ const char *auth_identity;
+ const char *requested_user;
+ const char **user;
+ const char **errstr;
+{
+ if (user == NULL || auth_identity == NULL)
+ return SASL_FAIL;
+ *user = newstr(auth_identity);
+ return SASL_OK;
+}
+
+# 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:
+** None. (not yet, maybe it should return success/failure?)
+**
+** Side Effects:
+** initializes PRNG for tls library.
+*/
+
+#define MIN_RAND_BYTES 16 /* 128 bits */
+
+void
+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 */
+
+ bool ok;
+ int randdef;
+
+ /*
+ ** initialize PRNG
+ */
+
+ ok = FALSE;
+ 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");
+ }
+# endif /* !HASURANDOMDEV */
+}
+
+/*
+** status in initialization
+** these flags keep track of the status of the initialization
+** i.e., whether a file exists (_EX) and whether it can be used (_OK)
+** [due to permissions]
+*/
+#define TLS_S_NONE 0x00000000 /* none yet */
+#define TLS_S_CERT_EX 0x00000001 /* CERT file exists */
+#define TLS_S_CERT_OK 0x00000002 /* CERT file is ok */
+#define TLS_S_KEY_EX 0x00000004 /* KEY file exists */
+#define TLS_S_KEY_OK 0x00000008 /* KEY file is ok */
+#define TLS_S_CERTP_EX 0x00000010 /* CA CERT PATH exists */
+#define TLS_S_CERTP_OK 0x00000020 /* CA CERT PATH is ok */
+#define TLS_S_CERTF_EX 0x00000040 /* CA CERT FILE exists */
+#define TLS_S_CERTF_OK 0x00000080 /* CA CERT FILE is ok */
+
+# if _FFR_TLS_1
+#define TLS_S_CERT2_EX 0x00001000 /* 2nd CERT file exists */
+#define TLS_S_CERT2_OK 0x00002000 /* 2nd CERT file is ok */
+#define TLS_S_KEY2_EX 0x00004000 /* 2nd KEY file exists */
+#define TLS_S_KEY2_OK 0x00008000 /* 2nd KEY file is ok */
+# endif /* _FFR_TLS_1 */
+
+#define TLS_S_DH_OK 0x00200000 /* DH cert is ok */
+#define TLS_S_DHPAR_EX 0x00400000 /* DH param file exists */
+#define TLS_S_DHPAR_OK 0x00800000 /* DH param file is ok to use */
+
+ /*
+** TLS_OK_F -- can var be an absolute filename?
+**
+** Parameters:
+** var -- filename
+** fn -- what is the filename used for?
+**
+** Returns:
+** ok?
+*/
+
+static bool
+tls_ok_f(var, fn)
+ char *var;
+ char *fn;
+{
+ /* must be absolute pathname */
+ if (var != NULL && *var == '/')
+ return TRUE;
+ if (LogLevel > 12)
+ sm_syslog(LOG_WARNING, NOQID, "TLS: file %s missing", fn);
+ return FALSE;
+}
+
+ /*
+** TLS_SAFE_F -- is a file safe to use?
+**
+** Parameters:
+** var -- filename
+** sff -- flags for safefile()
+**
+** Returns:
+** ok?
+*/
+
+static bool
+tls_safe_f(var, sff)
+ char *var;
+ long sff;
+{
+ int ret;
+
+ if ((ret = safefile(var, RunAsUid, RunAsGid, RunAsUserName, sff,
+ S_IRUSR, NULL)) == 0)
+ return TRUE;
+ if (LogLevel > 7)
+ sm_syslog(LOG_WARNING, NOQID, "TLS: file %s unsafe: %s",
+ var, errstring(ret));
+ return FALSE;
+}
+
+/*
+** TLS_OK_F -- macro to simplify calls to tls_ok_f
+**
+** Parameters:
+** var -- filename
+** fn -- what is the filename used for?
+** req -- is the file required?
+** st -- status bit to set if ok
+**
+** Side Effects:
+** uses r, ok; may change ok and status.
+**
+*/
+
+#define TLS_OK_F(var, fn, req, st) if (ok) \
+ { \
+ r = tls_ok_f(var, fn); \
+ if (r) \
+ status |= st; \
+ else if (req) \
+ ok = FALSE; \
+ }
+
+/*
+** TLS_UNR -- macro to return whether a file should be unreadable
+**
+** Parameters:
+** bit -- flag to test
+** req -- flags
+**
+** Returns:
+** 0/SFF_NORFILES
+*/
+#define TLS_UNR(bit, req) (bitset(bit, req) ? SFF_NORFILES : 0)
+
+/*
+** TLS_SAFE_F -- macro to simplify calls to tls_safe_f
+**
+** Parameters:
+** var -- filename
+** sff -- flags for safefile()
+** req -- is the file required?
+** ex -- does the file exist?
+** st -- status bit to set if ok
+**
+** Side Effects:
+** uses r, ok, ex; may change ok and status.
+**
+*/
+
+#define TLS_SAFE_F(var, sff, req, ex, st) if (ex && ok) \
+ { \
+ r = tls_safe_f(var, sff); \
+ if (r) \
+ status |= st; \
+ else if (req) \
+ ok = FALSE; \
+ }
+
+ /*
+** INITTLS -- initialize TLS
+**
+** Parameters:
+** ctx -- pointer to context
+** req -- requirements for initialization (see sendmail.h)
+** srv -- server side?
+** certfile -- filename of certificate
+** keyfile -- filename of private key
+** cacertpath -- path to CAs
+** cacertfile -- file with CA
+** dhparam -- parameters for DH
+**
+** Returns:
+** succeeded?
+*/
+
+bool
+inittls(ctx, req, srv, certfile, keyfile, cacertpath, cacertfile, dhparam)
+ SSL_CTX **ctx;
+ u_long req;
+ bool srv;
+ char *certfile, *keyfile, *cacertpath, *cacertfile, *dhparam;
+{
+# if !NO_DH
+ static DH *dh = NULL;
+# endif /* !NO_DH */
+ int r;
+ bool ok;
+ long sff, status;
+ char *who;
+# if _FFR_TLS_1
+ char *cf2, *kf2;
+# endif /* _FFR_TLS_1 */
+
+ status = TLS_S_NONE;
+ who = srv ? "srv" : "clt";
+ if (ctx == NULL)
+ syserr("TLS: %s:inittls: ctx == NULL", who);
+
+ /* already initialized? (we could re-init...) */
+ if (*ctx != NULL)
+ return TRUE;
+ 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);
+ free(dhparam);
+ dhparam = NULL;
+ }
+ }
+ if (dhparam == NULL)
+ dhparam = srv ? newstr("1") : newstr("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");
+ 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");
+ return FALSE;
+ }
+ }
+
+# if TLS_NO_RSA
+ /* turn off backward compatibility, required for no-rsa */
+ SSL_CTX_set_options(*ctx, SSL_OP_NO_SSLv2);
+# endif /* TLS_NO_RSA */
+
+
+# if !TLS_NO_RSA
+ /*
+ ** Create a temporary RSA key
+ ** XXX Maybe we shouldn't create this always (even though it
+ ** is only at startup).
+ ** It is a time-consuming operation and it is not always necessary.
+ ** maybe we should do it only on demand...
+ */
+ if (bitset(TLS_I_RSA_TMP, req) &&
+ (rsa_tmp = RSA_generate_key(RSA_KEYLENGTH, RSA_F4, NULL,
+ NULL)) == NULL
+ )
+ {
+ if (LogLevel > 7)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "TLS: error: %s: RSA_generate_key failed",
+ who);
+ if (LogLevel > 9)
+ tlslogerr();
+ }
+ return FALSE;
+ }
+# endif /* !TLS_NO_RSA */
+
+ /*
+ ** load private key
+ ** XXX change this for DSA-only version
+ */
+ if (bitset(TLS_S_KEY_OK, status) &&
+ SSL_CTX_use_PrivateKey_file(*ctx, keyfile,
+ SSL_FILETYPE_PEM) <= 0)
+ {
+ if (LogLevel > 7)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "TLS: error: %s: SSL_CTX_use_PrivateKey_file(%s) failed",
+ who, keyfile);
+ if (LogLevel > 9)
+ tlslogerr();
+ }
+ if (bitset(TLS_I_USE_KEY, req))
+ return FALSE;
+ }
+
+ /* get the certificate file */
+ if (bitset(TLS_S_CERT_OK, status) &&
+ SSL_CTX_use_certificate_file(*ctx, certfile,
+ SSL_FILETYPE_PEM) <= 0)
+ {
+ if (LogLevel > 7)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "TLS: error: %s: SSL_CTX_use_certificate_file(%s) failed",
+ who, certfile);
+ if (LogLevel > 9)
+ tlslogerr();
+ }
+ if (bitset(TLS_I_USE_CERT, req))
+ return FALSE;
+ }
+
+ /* check the private key */
+ if (bitset(TLS_S_KEY_OK, status) &&
+ (r = SSL_CTX_check_private_key(*ctx)) <= 0)
+ {
+ /* Private key does not match the certificate public key */
+ if (LogLevel > 5)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "TLS: error: %s: SSL_CTX_check_private_key failed(%s): %d",
+ who, keyfile, r);
+ if (LogLevel > 9)
+ tlslogerr();
+ }
+ if (bitset(TLS_I_USE_KEY, req))
+ return FALSE;
+ }
+
+# if _FFR_TLS_1
+ /* XXX this code is pretty much duplicated from above! */
+
+ /* load private key */
+ if (bitset(TLS_S_KEY2_OK, status) &&
+ SSL_CTX_use_PrivateKey_file(*ctx, kf2, SSL_FILETYPE_PEM) <= 0)
+ {
+ if (LogLevel > 7)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "TLS: error: %s: SSL_CTX_use_PrivateKey_file(%s) failed",
+ who, kf2);
+ if (LogLevel > 9)
+ tlslogerr();
+ }
+ }
+
+ /* get the certificate file */
+ if (bitset(TLS_S_CERT2_OK, status) &&
+ SSL_CTX_use_certificate_file(*ctx, cf2, SSL_FILETYPE_PEM) <= 0)
+ {
+ if (LogLevel > 7)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "TLS: error: %s: SSL_CTX_use_certificate_file(%s) failed",
+ who, cf2);
+ if (LogLevel > 9)
+ tlslogerr();
+ }
+ }
+
+ /* we should also check the private key: */
+ if (bitset(TLS_S_KEY2_OK, status) &&
+ (r = SSL_CTX_check_private_key(*ctx)) <= 0)
+ {
+ /* Private key does not match the certificate public key */
+ if (LogLevel > 5)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "TLS: error: %s: SSL_CTX_check_private_key 2 failed: %d",
+ who, r);
+ if (LogLevel > 9)
+ tlslogerr();
+ }
+ }
+# endif /* _FFR_TLS_1 */
+
+ /* SSL_CTX_set_quiet_shutdown(*ctx, 1); violation of standard? */
+ SSL_CTX_set_options(*ctx, SSL_OP_ALL); /* XXX bug compatibility? */
+
+# if !NO_DH
+ /* Diffie-Hellman initialization */
+ if (bitset(TLS_I_TRY_DH, req))
+ {
+ if (bitset(TLS_S_DHPAR_OK, status))
+ {
+ BIO *bio;
+
+ if ((bio = BIO_new_file(dhparam, "r")) != NULL)
+ {
+ dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ if (dh == NULL && LogLevel > 7)
+ {
+ u_long err;
+
+ err = ERR_get_error();
+ sm_syslog(LOG_WARNING, NOQID,
+ "TLS: error: %s: cannot read DH parameters(%s): %s",
+ who, dhparam,
+ ERR_error_string(err, NULL));
+ if (LogLevel > 9)
+ tlslogerr();
+ }
+ }
+ else
+ {
+ if (LogLevel > 5)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "TLS: error: %s: BIO_new_file(%s) failed",
+ who, dhparam);
+ if (LogLevel > 9)
+ tlslogerr();
+ }
+ }
+ }
+ if (dh == NULL && bitset(TLS_I_DH1024, req))
+ {
+ DSA *dsa;
+
+ /* this takes a while! (7-130s on a 450MHz AMD K6-2) */
+ dsa = DSA_generate_parameters(1024, NULL, 0, NULL,
+ NULL, 0, NULL);
+ dh = DSA_dup_DH(dsa);
+ DSA_free(dsa);
+ }
+ else
+ if (dh == NULL && bitset(TLS_I_DH512, req))
+ dh = get_dh512();
+
+ if (dh == NULL)
+ {
+ if (LogLevel > 9)
+ {
+ u_long err;
+
+ err = ERR_get_error();
+ sm_syslog(LOG_WARNING, NOQID,
+ "TLS: error: %s: cannot read or set DH parameters(%s): %s",
+ who, dhparam,
+ ERR_error_string(err, NULL));
+ }
+ if (bitset(TLS_I_REQ_DH, req))
+ return FALSE;
+ }
+ else
+ {
+ SSL_CTX_set_tmp_dh(*ctx, dh);
+
+ /* important to avoid small subgroup attacks */
+ SSL_CTX_set_options(*ctx, SSL_OP_SINGLE_DH_USE);
+ if (LogLevel > 12)
+ sm_syslog(LOG_INFO, NOQID,
+ "TLS: %s: Diffie-Hellman init, key=%d bit (%c)",
+ who, 8 * DH_size(dh), *dhparam);
+ DH_free(dh);
+ }
+ }
+# endif /* !NO_DH */
+
+
+ /* XXX do we need this cache here? */
+ if (bitset(TLS_I_CACHE, req))
+ SSL_CTX_sess_set_cache_size(*ctx, 128);
+ /* timeout? SSL_CTX_set_timeout(*ctx, TimeOut...); */
+
+ /* load certificate locations and default CA paths */
+ if (bitset(TLS_S_CERTP_EX, status) && bitset(TLS_S_CERTF_EX, status))
+ {
+ if ((r = SSL_CTX_load_verify_locations(*ctx, cacertfile,
+ cacertpath)) == 1)
+ {
+# if !TLS_NO_RSA
+ if (bitset(TLS_I_RSA_TMP, req))
+ SSL_CTX_set_tmp_rsa_callback(*ctx, tmp_rsa_key);
+# endif /* !TLS_NO_RSA */
+
+ /* ask to verify the peer */
+ SSL_CTX_set_verify(*ctx, SSL_VERIFY_PEER, NULL);
+
+ /* install verify callback */
+ SSL_CTX_set_cert_verify_callback(*ctx, tls_verify_cb,
+ NULL);
+ SSL_CTX_set_client_CA_list(*ctx,
+ SSL_load_client_CA_file(cacertfile));
+ }
+ else
+ {
+ /*
+ ** can't load CA data; do we care?
+ ** the data is necessary to authenticate the client,
+ ** which in turn would be necessary
+ ** if we want to allow relaying based on it.
+ */
+ if (LogLevel > 5)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "TLS: error: %s: %d load verify locs %s, %s",
+ who, r, cacertpath, cacertfile);
+ if (LogLevel > 9)
+ tlslogerr();
+ }
+ if (bitset(TLS_I_VRFY_LOC, req))
+ return FALSE;
+ }
+ }
+
+ /* XXX: make this dependent on an option? */
+ if (tTd(96, 9))
+ SSL_CTX_set_info_callback(*ctx, apps_ssl_info_cb);
+
+# if _FFR_TLS_1
+ /*
+ ** XXX install our own cipher list: option?
+ */
+ if (CipherList != NULL && *CipherList != '\0')
+ {
+ if (SSL_CTX_set_cipher_list(*ctx, CipherList) <= 0)
+ {
+ if (LogLevel > 7)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "TLS: error: %s: SSL_CTX_set_cipher_list(%s) failed, list ignored",
+ who, CipherList);
+
+ if (LogLevel > 9)
+ tlslogerr();
+ }
+ /* failure if setting to this list is required? */
+ }
+ }
+# endif /* _FFR_TLS_1 */
+ if (LogLevel > 12)
+ sm_syslog(LOG_INFO, NOQID, "TLS: init(%s)=%d", who, ok);
+
+# if _FFR_TLS_1
+# if 0
+ /*
+ ** this label is required if we want to have a "clean" exit
+ ** see the comments above at the initialization of cf2
+ */
+ endinittls:
+# endif /* 0 */
+
+ /* undo damage to global variables */
+ if (cf2 != NULL)
+ *--cf2 = ',';
+ if (kf2 != NULL)
+ *--kf2 = ',';
+# endif /* _FFR_TLS_1 */
+
+ return ok;
+}
+ /*
+** INITSRVTLS -- initialize server side TLS
+**
+** Parameters:
+** none.
+**
+** Returns:
+** succeeded?
+*/
+
+bool
+initsrvtls()
+{
+
+ tls_ok = inittls(&srv_ctx, TLS_I_SRV, TRUE, SrvCERTfile, Srvkeyfile,
+ CACERTpath, CACERTfile, DHParams);
+ return tls_ok;
+}
+ /*
+** 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
+**
+** Returns:
+** result of authentication.
+**
+** Side Effects:
+** sets ${cipher}, ${tls_version}, ${verify}, ${cipher_bits},
+** ${cert}
+*/
+
+int
+tls_get_info(ssl, e, srv, host)
+ SSL *ssl;
+ ENVELOPE *e;
+ bool srv;
+ char *host;
+{
+ 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 (LogLevel >= 14)
+ sm_syslog(LOG_INFO, e->e_id,
+ "TLS: get_verify in %s: %d get_peer: 0x%x",
+ srv ? "srv" : "clt",
+ SSL_get_verify_result(ssl), 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 (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
**
** Returns:
** none.
@@ -1467,21 +4115,28 @@ runinchild(label, e)
** Side Effects:
** outputs the help file to message output.
*/
+#define HELPVSTR "#vers "
+#define HELPVERSION 2
void
-help(topic)
+help(topic, e)
char *topic;
+ ENVELOPE *e;
{
register FILE *hf;
+ register char *p;
int len;
bool noinfo;
- int sff = SFF_OPENASROOT|SFF_REGONLY;
+ bool first = TRUE;
+ long sff = SFF_OPENASROOT|SFF_REGONLY;
char buf[MAXLINE];
+ char inp[MAXLINE];
+ static int foundvers = -1;
extern char Version[];
if (DontLockReadFiles)
sff |= SFF_NOLOCK;
- if (!bitset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
+ if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
sff |= SFF_SAFEDIRPATH;
if (HelpFile == NULL ||
@@ -1489,14 +4144,14 @@ help(topic)
{
/* no help */
errno = 0;
- message("502 Sendmail %s -- HELP not implemented", Version);
+ message("502 5.3.0 Sendmail %s -- HELP not implemented",
+ Version);
return;
}
if (topic == NULL || *topic == '\0')
{
topic = "smtp";
- message("214-This is Sendmail version %s", Version);
noinfo = FALSE;
}
else
@@ -1509,24 +4164,61 @@ help(topic)
while (fgets(buf, sizeof buf, hf) != NULL)
{
+ if (buf[0] == '#')
+ {
+ if (foundvers < 0 &&
+ strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0)
+ {
+ int h;
+
+ if (sscanf(buf + strlen(HELPVSTR), "%d",
+ &h) == 1)
+ foundvers = h;
+ }
+ continue;
+ }
if (strncmp(buf, topic, len) == 0)
{
- register char *p;
+ if (first)
+ {
+ first = FALSE;
- p = strchr(buf, '\t');
+ /* print version if no/old vers# in file */
+ if (foundvers < 2 && !noinfo)
+ message("214-2.0.0 This is Sendmail version %s", Version);
+ }
+ p = strpbrk(buf, " \t");
if (p == NULL)
- p = buf;
+ p = buf + strlen(buf) - 1;
else
p++;
fixcrlf(p, TRUE);
- message("214-%s", p);
+ if (foundvers >= 2)
+ {
+ translate_dollars(p);
+ expand(p, inp, sizeof inp, e);
+ p = inp;
+ }
+ message("214-2.0.0 %s", p);
noinfo = FALSE;
}
}
if (noinfo)
- message("504 HELP topic \"%.10s\" unknown", topic);
+ message("504 5.3.0 HELP topic \"%.10s\" unknown", topic);
else
- message("214 End of HELP info");
+ message("214 2.0.0 End of HELP info");
+
+ if (foundvers != 0 && foundvers < HELPVERSION)
+ {
+ if (LogLevel > 1)
+ sm_syslog(LOG_WARNING, e->e_id,
+ "%s too old (require version %d)",
+ HelpFile, HELPVERSION);
+
+ /* avoid log next time */
+ foundvers = 0;
+ }
+
(void) fclose(hf);
}
diff --git a/contrib/sendmail/src/stab.c b/contrib/sendmail/src/stab.c
index 37b87a3..2177ae6 100644
--- a/contrib/sendmail/src/stab.c
+++ b/contrib/sendmail/src/stab.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,10 +12,10 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)stab.c 8.19 (Berkeley) 5/19/1998";
-#endif /* not lint */
+static char id[] = "@(#)$Id: stab.c,v 8.40.16.2 2000/06/05 21:46:59 gshapiro Exp $";
+#endif /* ! lint */
-# include "sendmail.h"
+#include <sendmail.h>
/*
** STAB -- manage the symbol table
@@ -35,7 +36,7 @@ static char sccsid[] = "@(#)stab.c 8.19 (Berkeley) 5/19/1998";
** can update the symbol table.
*/
-# define STABSIZE 2003
+#define STABSIZE 2003
static STAB *SymTab[STABSIZE];
@@ -50,10 +51,9 @@ stab(name, type, op)
register int hfunc;
register char *p;
int len;
- extern char lower __P((char));
if (tTd(36, 5))
- printf("STAB: %s %d ", name, type);
+ dprintf("STAB: %s %d ", name, type);
/*
** Compute the hashing function
@@ -64,7 +64,7 @@ stab(name, type, op)
hfunc = ((hfunc << 1) ^ (lower(*p) & 0377)) % STABSIZE;
if (tTd(36, 9))
- printf("(hfunc=%d) ", hfunc);
+ dprintf("(hfunc=%d) ", hfunc);
ps = &SymTab[hfunc];
if (type == ST_MACRO || type == ST_RULESET)
@@ -89,16 +89,16 @@ stab(name, type, op)
if (tTd(36, 5))
{
if (s == NULL)
- printf("not found\n");
+ dprintf("not found\n");
else
{
long *lp = (long *) s->s_class;
- printf("type %d val %lx %lx %lx %lx\n",
+ dprintf("type %d val %lx %lx %lx %lx\n",
s->s_type, lp[0], lp[1], lp[2], lp[3]);
}
}
- return (s);
+ return s;
}
/*
@@ -106,10 +106,9 @@ stab(name, type, op)
*/
if (tTd(36, 5))
- printf("entered\n");
+ dprintf("entered\n");
/* determine size of new entry */
-#if _FFR_MEMORY_MISER
switch (type)
{
case ST_CLASS:
@@ -122,6 +121,7 @@ stab(name, type, op)
case ST_MAILER:
len = sizeof s->s_mailer;
+ break;
case ST_ALIAS:
len = sizeof s->s_alias;
@@ -151,15 +151,35 @@ stab(name, type, op)
len = sizeof s->s_ruleset;
break;
+ case ST_HEADER:
+ len = sizeof s->s_header;
+ break;
+
case ST_SERVICE:
len = sizeof s->s_service;
break;
- case ST_HEADER:
- len = sizeof s->s_header;
+#ifdef LDAPMAP
+ case ST_LDAP:
+ len = sizeof s->s_ldap;
break;
+#endif /* LDAPMAP */
+
+#if _FFR_MILTER
+ case ST_MILTER:
+ len = sizeof s->s_milter;
+ break;
+#endif /* _FFR_MILTER */
default:
+ /*
+ ** Each mailer has it's own MCI stab entry:
+ **
+ ** s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
+ **
+ ** Therefore, anything ST_MCI or larger is an s_mci.
+ */
+
if (type >= ST_MCI)
len = sizeof s->s_mci;
else
@@ -170,13 +190,13 @@ stab(name, type, op)
break;
}
len += sizeof *s - sizeof s->s_value;
-#else
- len = sizeof *s;
-#endif
+
+ if (tTd(36, 15))
+ dprintf("size of stab entry: %d\n", len);
/* make new entry */
s = (STAB *) xalloc(len);
- bzero((char *) s, len);
+ memset((char *) s, '\0', len);
s->s_name = newstr(name);
s->s_type = type;
s->s_len = len;
@@ -184,7 +204,11 @@ stab(name, type, op)
/* link it in */
*ps = s;
- return (s);
+ /* set a default value for rulesets */
+ if (type == ST_RULESET)
+ s->s_ruleset = -1;
+
+ return s;
}
/*
** STABAPPLY -- apply function to all stab entries
@@ -211,9 +235,104 @@ stabapply(func, arg)
for (s = *shead; s != NULL; s = s->s_next)
{
if (tTd(36, 90))
- printf("stabapply: trying %d/%s\n",
+ dprintf("stabapply: trying %d/%s\n",
s->s_type, s->s_name);
func(s, arg);
}
}
}
+ /*
+** QUEUEUP_MACROS -- queueup the macros in a class
+**
+** Write the macros listed in the specified class into the
+** file referenced by qfp.
+**
+** Parameters:
+** class -- class ID.
+** qfp -- file pointer to the qf file.
+** e -- the envelope.
+**
+** Returns:
+** none.
+*/
+
+void
+queueup_macros(class, qfp, e)
+ int class;
+ FILE *qfp;
+ ENVELOPE *e;
+{
+ register STAB **shead;
+ register STAB *s;
+
+ if (e == NULL)
+ return;
+
+ for (shead = SymTab; shead < &SymTab[STABSIZE]; shead++)
+ {
+ for (s = *shead; s != NULL; s = s->s_next)
+ {
+ int m;
+ char *p;
+
+ if (s->s_type == ST_CLASS &&
+ bitnset(class & 0xff, s->s_class) &&
+ (m = macid(s->s_name, NULL)) != '\0' &&
+ (p = macvalue(m, e)) != NULL)
+ {
+ /*
+ ** HACK ALERT: Unfortunately, 8.10 and
+ ** 8.11 reused the ${if_addr} and
+ ** ${if_family} macros for both the incoming
+ ** interface address/family (getrequests())
+ ** and the outgoing interface address/family
+ ** (makeconnection()). In order for D_BINDIF
+ ** to work properly, have to preserve the
+ ** incoming information in the queue file for
+ ** later delivery attempts. The original
+ ** information is stored in the envelope
+ ** in readqf() so it can be stored in
+ ** queueup_macros(). This should be fixed
+ ** in 8.12.
+ */
+
+ if (e->e_if_macros[EIF_ADDR] != NULL &&
+ strcmp(s->s_name, "{if_addr}") == 0)
+ p = e->e_if_macros[EIF_ADDR];
+
+ fprintf(qfp, "$%s%s\n",
+ s->s_name,
+ denlstring(p, TRUE, FALSE));
+ }
+ }
+ }
+}
+ /*
+** COPY_CLASS -- copy class members from one class to another
+**
+** Parameters:
+** src -- source class.
+** dst -- destination class.
+**
+** Returns:
+** none.
+*/
+
+void
+copy_class(src, dst)
+ int src;
+ int dst;
+{
+ register STAB **shead;
+ register STAB *s;
+
+ for (shead = SymTab; shead < &SymTab[STABSIZE]; shead++)
+ {
+ for (s = *shead; s != NULL; s = s->s_next)
+ {
+ if (s->s_type == ST_CLASS &&
+ bitnset(src & 0xff, s->s_class))
+ setbitn(dst, s->s_class);
+ }
+ }
+}
diff --git a/contrib/sendmail/src/stats.c b/contrib/sendmail/src/stats.c
index b1162ff..383cb37 100644
--- a/contrib/sendmail/src/stats.c
+++ b/contrib/sendmail/src/stats.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,20 +12,33 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)stats.c 8.22 (Berkeley) 5/19/1998";
-#endif /* not lint */
+static char id[] = "@(#)$Id: stats.c,v 8.36.14.2 2000/05/25 23:33:34 gshapiro Exp $";
+#endif /* ! lint */
-# include "sendmail.h"
-# include "mailstats.h"
+#include <sendmail.h>
+#include <sendmail/mailstats.h>
-struct statistics Stat;
-bool GotStats = FALSE; /* set when we have stats to merge */
+static struct statistics Stat;
+static bool GotStats = FALSE; /* set when we have stats to merge */
+
+/* See http://physics.nist.gov/cuu/Units/binary.html */
#define ONE_K 1000 /* one thousand (twenty-four?) */
#define KBYTES(x) (((x) + (ONE_K - 1)) / ONE_K)
/*
** MARKSTATS -- mark statistics
+**
+** Parameters:
+** e -- the envelope.
+** to -- to address.
+** reject -- whether this is a rejection.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** changes static Stat structure
*/
void
@@ -33,7 +47,7 @@ markstats(e, to, reject)
register ADDRESS *to;
bool reject;
{
- if (reject == TRUE)
+ if (reject)
{
if (e->e_from.q_mailer != NULL)
{
@@ -42,9 +56,11 @@ markstats(e, to, reject)
else
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)
{
Stat.stat_nf[e->e_from.q_mailer->m_mno]++;
@@ -54,12 +70,35 @@ markstats(e, to, reject)
}
else
{
+ Stat.stat_ct++;
Stat.stat_nt[to->q_mailer->m_mno]++;
Stat.stat_bt[to->q_mailer->m_mno] += KBYTES(e->e_msgsize);
}
+
+
GotStats = TRUE;
}
/*
+** CLEARSTATS -- clear statistics structure
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** clears the Stat structure.
+*/
+
+void
+clearstats()
+{
+ /* clear the structure to avoid future disappointment */
+ memset(&Stat, '\0', sizeof Stat);
+ GotStats = FALSE;
+}
+ /*
** POSTSTATS -- post statistics in the statistics file
**
** Parameters:
@@ -77,8 +116,8 @@ poststats(sfile)
char *sfile;
{
register int fd;
- int sff = SFF_REGONLY|SFF_OPENASROOT;
- struct statistics stat;
+ long sff = SFF_REGONLY|SFF_OPENASROOT;
+ struct statistics stats;
extern off_t lseek();
if (sfile == NULL || !GotStats)
@@ -89,9 +128,9 @@ poststats(sfile)
Stat.stat_magic = STAT_MAGIC;
Stat.stat_version = STAT_VERSION;
- if (!bitset(DBS_WRITESTATSTOSYMLINK, DontBlameSendmail))
+ if (!bitnset(DBS_WRITESTATSTOSYMLINK, DontBlameSendmail))
sff |= SFF_NOSLINK;
- if (!bitset(DBS_WRITESTATSTOHARDLINK, DontBlameSendmail))
+ if (!bitnset(DBS_WRITESTATSTOHARDLINK, DontBlameSendmail))
sff |= SFF_NOHLINK;
fd = safeopen(sfile, O_RDWR, 0644, sff);
@@ -103,33 +142,35 @@ poststats(sfile)
errno = 0;
return;
}
- if (read(fd, (char *) &stat, sizeof stat) == sizeof stat &&
- stat.stat_size == sizeof stat &&
- stat.stat_magic == Stat.stat_magic &&
- stat.stat_version == Stat.stat_version)
+ if (read(fd, (char *) &stats, sizeof stats) == sizeof stats &&
+ stats.stat_size == sizeof stats &&
+ stats.stat_magic == Stat.stat_magic &&
+ stats.stat_version == Stat.stat_version)
{
/* merge current statistics into statfile */
register int i;
for (i = 0; i < MAXMAILERS; i++)
{
- stat.stat_nf[i] += Stat.stat_nf[i];
- stat.stat_bf[i] += Stat.stat_bf[i];
- stat.stat_nt[i] += Stat.stat_nt[i];
- stat.stat_bt[i] += Stat.stat_bt[i];
- stat.stat_nr[i] += Stat.stat_nr[i];
- stat.stat_nd[i] += Stat.stat_nd[i];
+ stats.stat_nf[i] += Stat.stat_nf[i];
+ stats.stat_bf[i] += Stat.stat_bf[i];
+ stats.stat_nt[i] += Stat.stat_nt[i];
+ stats.stat_bt[i] += Stat.stat_bt[i];
+ stats.stat_nr[i] += Stat.stat_nr[i];
+ stats.stat_nd[i] += Stat.stat_nd[i];
}
+ stats.stat_cr += Stat.stat_cr;
+ stats.stat_ct += Stat.stat_ct;
+ stats.stat_cf += Stat.stat_cf;
}
else
- bcopy((char *) &Stat, (char *) &stat, sizeof stat);
+ memmove((char *) &stats, (char *) &Stat, sizeof stats);
/* write out results */
(void) lseek(fd, (off_t) 0, 0);
- (void) write(fd, (char *) &stat, sizeof stat);
+ (void) write(fd, (char *) &stats, sizeof stats);
(void) close(fd);
/* clear the structure to avoid future disappointment */
- bzero(&Stat, sizeof stat);
- GotStats = FALSE;
+ clearstats();
}
diff --git a/contrib/sendmail/src/statusd_shm.h b/contrib/sendmail/src/statusd_shm.h
new file mode 100644
index 0000000..c48b2fd
--- /dev/null
+++ b/contrib/sendmail/src/statusd_shm.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1999 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 $
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+/*
+** The shared memory part of statusd.
+**
+** Attach to STATUSD_SHM_KEY and update the counter appropriate
+** for your type of service.
+**
+*/
+
+#define STATUSD_MAGIC 110946
+#define STATUSD_SHM_KEY (key_t)(13)
+#define STATUSD_LONGS (2)
+
+typedef struct {
+ unsigned long magic;
+ unsigned long ul[STATUSD_LONGS];
+} STATUSD_SHM;
+
+/*
+** Offsets into ul[]. The appropriate program
+** increments these as appropriate.
+*/
+
+#define STATUSD_COOKIE (0) /* reregister cookie */
+
+/* sendmail */
+#define STATUSD_SM_NSENDMAIL (1) /* how many running */
+
+extern void shmtick __P((int, int));
+
diff --git a/contrib/sendmail/src/sysexits.c b/contrib/sendmail/src/sysexits.c
index c7934c7..6cce614 100644
--- a/contrib/sendmail/src/sysexits.c
+++ b/contrib/sendmail/src/sysexits.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,10 +12,10 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)sysexits.c 8.13 (Berkeley) 5/24/1998";
-#endif /* not lint */
+static char id[] = "@(#)$Id: sysexits.c,v 8.25 1999/09/23 19:59:24 ca Exp $";
+#endif /* ! lint */
-#include "sendmail.h"
+#include <sendmail.h>
/*
** SYSEXITS.C -- error messages corresponding to sysexits.h
@@ -25,24 +26,44 @@ static char sccsid[] = "@(#)sysexits.c 8.13 (Berkeley) 5/24/1998";
char *SysExMsg[] =
{
- /* 64 USAGE */ " 500 Bad usage",
- /* 65 DATAERR */ " 501 Data format error",
- /* 66 NOINPUT */ ":550 Cannot open input",
- /* 67 NOUSER */ " 550 User unknown",
- /* 68 NOHOST */ " 550 Host unknown",
- /* 69 UNAVAILABLE */ " 554 Service unavailable",
- /* 70 SOFTWARE */ ":554 Internal error",
- /* 71 OSERR */ ":451 Operating system error",
- /* 72 OSFILE */ ":554 System file missing",
- /* 73 CANTCREAT */ ":550 Can't create output",
- /* 74 IOERR */ ":451 I/O error",
- /* 75 TEMPFAIL */ " 250 Deferred",
- /* 76 PROTOCOL */ " 554 Remote protocol error",
- /* 77 NOPERM */ ":550 Insufficient permission",
- /* 78 CONFIG */ " 554 Local configuration error",
+ /* 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.
**
@@ -111,7 +132,7 @@ dsntoexitstat(dsncode)
switch (code3)
{
case 0: /* Other or Undefined mailbox status */
- case 1: /* Mailbox disabled, not acccepting messages */
+ case 1: /* Mailbox disabled, not accepting messages */
case 2: /* Mailbox full */
case 4: /* Mailing list expansion problem */
return EX_UNAVAILABLE;
@@ -160,3 +181,36 @@ dsntoexitstat(dsncode)
}
return EX_CONFIG;
}
+
+ /*
+** EXITSTAT -- convert EX_ value to error text.
+**
+** Parameters:
+** excode -- rstatus which might consists of an EX_* value.
+**
+** Returns:
+** The corresponding error text or the original string.
+*/
+
+char *
+exitstat(excode)
+ char *excode;
+{
+ char *c;
+ int i;
+
+ 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];
+ return excode;
+}
diff --git a/contrib/sendmail/src/timers.c b/contrib/sendmail/src/timers.c
new file mode 100644
index 0000000..74d0ccf
--- /dev/null
+++ b/contrib/sendmail/src/timers.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 1999 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.
+ *
+ */
+
+#ifndef lint
+static char id[] = "@(#)$Id: timers.c,v 8.13 1999/11/23 07:22:28 gshapiro Exp $";
+#endif /* ! lint */
+
+#if _FFR_TIMERS
+# include <sys/types.h>
+# include <sys/time.h>
+# include "sendmail.h"
+# include <sys/resource.h> /* Must be after sendmail.h for NCR MP-RAS */
+
+static TIMER BaseTimer; /* current baseline */
+static int NTimers; /* current pointer into stack */
+static TIMER *TimerStack[MAXTIMERSTACK];
+
+static void
+# ifdef __STDC__
+warntimer(const char *msg, ...)
+# else /* __STDC__ */
+warntimer(msg, va_alist)
+ const char *msg;
+ va_dcl
+# endif /* __STDC__ */
+{
+ char buf[MAXLINE];
+ VA_LOCAL_DECL
+
+# if 0
+ if (!tTd(98, 30))
+ return;
+# endif /* 0 */
+ VA_START(msg);
+ vsnprintf(buf, sizeof buf, msg, ap);
+ VA_END;
+ sm_syslog(LOG_NOTICE, CurEnv->e_id, "%s; e_timers=0x%lx",
+ buf, (u_long) &CurEnv->e_timers);
+}
+
+static void
+zerotimer(ptimer)
+ TIMER *ptimer;
+{
+ memset(ptimer, '\0', sizeof *ptimer);
+}
+
+static void
+addtimer(ta, tb)
+ TIMER *ta;
+ TIMER *tb;
+{
+ tb->ti_wall_sec += ta->ti_wall_sec;
+ tb->ti_wall_usec += ta->ti_wall_usec;
+ if (tb->ti_wall_usec > 1000000)
+ {
+ tb->ti_wall_sec++;
+ tb->ti_wall_usec -= 1000000;
+ }
+ tb->ti_cpu_sec += ta->ti_cpu_sec;
+ tb->ti_cpu_usec += ta->ti_cpu_usec;
+ if (tb->ti_cpu_usec > 1000000)
+ {
+ tb->ti_cpu_sec++;
+ tb->ti_cpu_usec -= 1000000;
+ }
+}
+
+static void
+subtimer(ta, tb)
+ TIMER *ta;
+ TIMER *tb;
+{
+ tb->ti_wall_sec -= ta->ti_wall_sec;
+ tb->ti_wall_usec -= ta->ti_wall_usec;
+ if (tb->ti_wall_usec < 0)
+ {
+ tb->ti_wall_sec--;
+ tb->ti_wall_usec += 1000000;
+ }
+ tb->ti_cpu_sec -= ta->ti_cpu_sec;
+ tb->ti_cpu_usec -= ta->ti_cpu_usec;
+ if (tb->ti_cpu_usec < 0)
+ {
+ tb->ti_cpu_sec--;
+ tb->ti_cpu_usec += 1000000;
+ }
+}
+
+static int
+getcurtimer(ptimer)
+ TIMER *ptimer;
+{
+ struct rusage ru;
+ struct timeval now;
+
+ if (getrusage(RUSAGE_SELF, &ru) < 0 || gettimeofday(&now, NULL) < 0)
+ return -1;
+ ptimer->ti_wall_sec = now.tv_sec;
+ ptimer->ti_wall_usec = now.tv_usec;
+ ptimer->ti_cpu_sec = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+ ptimer->ti_cpu_usec = ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+ if (ptimer->ti_cpu_usec > 1000000)
+ {
+ ptimer->ti_cpu_sec++;
+ ptimer->ti_cpu_usec -= 1000000;
+ }
+ return 0;
+}
+
+static void
+getinctimer(ptimer)
+ TIMER *ptimer;
+{
+ TIMER cur;
+
+ if (getcurtimer(&cur) < 0)
+ {
+ zerotimer(ptimer);
+ return;
+ }
+ if (BaseTimer.ti_wall_sec == 0)
+ {
+ /* first call */
+ memset(ptimer, '\0', sizeof *ptimer);
+ }
+ else
+ {
+ *ptimer = cur;
+ subtimer(&BaseTimer, ptimer);
+ }
+ BaseTimer = cur;
+}
+
+void
+flushtimers()
+{
+ NTimers = 0;
+ (void) getcurtimer(&BaseTimer);
+}
+
+void
+pushtimer(ptimer)
+ TIMER *ptimer;
+{
+ int i;
+ int save_errno = errno;
+ TIMER incr;
+
+ /* find how much time has changed since last call */
+ getinctimer(&incr);
+
+ /* add that into the old timers */
+ i = NTimers;
+ if (i > MAXTIMERSTACK)
+ i = MAXTIMERSTACK;
+ while (--i >= 0)
+ {
+ addtimer(&incr, TimerStack[i]);
+ if (TimerStack[i] == ptimer)
+ {
+ warntimer("Timer@0x%lx already on stack, index=%d, NTimers=%d",
+ (u_long) ptimer, i, NTimers);
+ errno = save_errno;
+ return;
+ }
+ }
+ errno = save_errno;
+
+ /* handle stack overflow */
+ if (NTimers >= MAXTIMERSTACK)
+ return;
+
+ /* now add the timer to the stack */
+ TimerStack[NTimers++] = ptimer;
+}
+
+void
+poptimer(ptimer)
+ TIMER *ptimer;
+{
+ int i;
+ int save_errno = errno;
+ TIMER incr;
+
+ /* find how much time has changed since last call */
+ getinctimer(&incr);
+
+ /* add that into the old timers */
+ i = NTimers;
+ if (i > MAXTIMERSTACK)
+ i = MAXTIMERSTACK;
+ while (--i >= 0)
+ addtimer(&incr, TimerStack[i]);
+
+ /* pop back to this timer */
+ for (i = 0; i < NTimers; i++)
+ if (TimerStack[i] == ptimer)
+ break;
+ if (i != NTimers - 1)
+ warntimer("poptimer: odd pop (timer=0x%lx, index=%d, NTimers=%d)",
+ (u_long) ptimer, i, NTimers);
+ NTimers = i;
+
+ /* clean up and return */
+ errno = save_errno;
+}
+
+char *
+strtimer(ptimer)
+ TIMER *ptimer;
+{
+ static char buf[40];
+
+ 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;
+}
+#endif /* _FFR_TIMERS */
diff --git a/contrib/sendmail/src/timers.h b/contrib/sendmail/src/timers.h
new file mode 100644
index 0000000..e86b6ec
--- /dev/null
+++ b/contrib/sendmail/src/timers.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 1999 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 $
+ *
+ * Contributed by Exactis.com, Inc.
+ *
+ */
+
+#ifndef TIMERS_H
+#define TIMERS_H 1
+
+#define MAXTIMERSTACK 20 /* maximum timer depth */
+
+#define TIMER struct _timer
+
+TIMER
+{
+ long ti_wall_sec; /* wall clock seconds */
+ long ti_wall_usec; /* ... microseconds */
+ long ti_cpu_sec; /* cpu time seconds */
+ long ti_cpu_usec; /* ... microseconds */
+};
+
+extern void pushtimer __P((TIMER *));
+extern void poptimer __P((TIMER *));
+extern char *strtimer __P((TIMER *));
+#endif /* TIMERS_H */
diff --git a/contrib/sendmail/src/trace.c b/contrib/sendmail/src/trace.c
index 5ab6032..f117598 100644
--- a/contrib/sendmail/src/trace.c
+++ b/contrib/sendmail/src/trace.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,10 +12,10 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)trace.c 8.12 (Berkeley) 5/19/1998";
-#endif /* not lint */
+static char id[] = "@(#)$Id: trace.c,v 8.20.22.1 2000/05/25 18:56:18 gshapiro Exp $";
+#endif /* ! lint */
-# include "sendmail.h"
+#include <sendmail.h>
/*
** TtSETUP -- set up for trace package.
@@ -31,8 +32,8 @@ static char sccsid[] = "@(#)trace.c 8.12 (Berkeley) 5/19/1998";
** environment is set up.
*/
-u_char *tTvect;
-int tTsize;
+static u_char *tTvect;
+static int tTsize;
static char *DefFlags;
void
@@ -62,7 +63,7 @@ void
tTflag(s)
register char *s;
{
- unsigned int first, last;
+ int first, last;
register unsigned int i;
if (*s == '\0')
diff --git a/contrib/sendmail/src/udb.c b/contrib/sendmail/src/udb.c
index 96c6513..91d03f1 100644
--- a/contrib/sendmail/src/udb.c
+++ b/contrib/sendmail/src/udb.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -10,33 +11,31 @@
*
*/
-#include "sendmail.h"
+#include <sendmail.h>
#ifndef lint
-#if USERDB
-static char sccsid [] = "@(#)udb.c 8.71 (Berkeley) 1/17/1999 (with USERDB)";
-#else
-static char sccsid [] = "@(#)udb.c 8.71 (Berkeley) 1/17/1999 (without USERDB)";
-#endif
-#endif
+# if USERDB
+static char id[] = "@(#)$Id: udb.c,v 8.111 1999/11/16 02:04:04 gshapiro Exp $ (with USERDB)";
+# else /* USERDB */
+static char id[] = "@(#)$Id: udb.c,v 8.111 1999/11/16 02:04:04 gshapiro Exp $ (without USERDB)";
+# endif /* USERDB */
+#endif /* ! lint */
#if USERDB
-#include <errno.h>
-
-#ifdef NEWDB
-# include <db.h>
-# ifndef DB_VERSION_MAJOR
-# define DB_VERSION_MAJOR 1
-# endif
-#else
-# define DBT struct _data_base_thang_
+# ifdef NEWDB
+# include <db.h>
+# ifndef DB_VERSION_MAJOR
+# define DB_VERSION_MAJOR 1
+# endif /* ! DB_VERSION_MAJOR */
+# else /* NEWDB */
+# define DBT struct _data_base_thang_
DBT
{
void *data; /* pointer to data */
size_t size; /* length of data */
};
-#endif
+# endif /* NEWDB */
/*
** UDB.C -- interface between sendmail and Berkeley User Data Base.
@@ -53,56 +52,61 @@ struct udbent
char *udb_default; /* default host for outgoing mail */
union
{
+# if NETINET || NETINET6
/* type UE_REMOTE -- do remote call for lookup */
struct
{
- struct sockaddr_in _udb_addr; /* address */
+ SOCKADDR _udb_addr; /* address */
int _udb_timeout; /* timeout */
} udb_remote;
-#define udb_addr udb_u.udb_remote._udb_addr
-#define udb_timeout udb_u.udb_remote._udb_timeout
+# define udb_addr udb_u.udb_remote._udb_addr
+# define udb_timeout udb_u.udb_remote._udb_timeout
+# endif /* NETINET || NETINET6 */
/* type UE_FORWARD -- forward message to remote */
struct
{
char *_udb_fwdhost; /* name of forward host */
} udb_forward;
-#define udb_fwdhost udb_u.udb_forward._udb_fwdhost
+# define udb_fwdhost udb_u.udb_forward._udb_fwdhost
-#ifdef NEWDB
+# ifdef NEWDB
/* type UE_FETCH -- lookup in local database */
struct
{
char *_udb_dbname; /* pathname of database */
DB *_udb_dbp; /* open database ptr */
} udb_lookup;
-#define udb_dbname udb_u.udb_lookup._udb_dbname
-#define udb_dbp udb_u.udb_lookup._udb_dbp
-#endif
+# define udb_dbname udb_u.udb_lookup._udb_dbname
+# define udb_dbp udb_u.udb_lookup._udb_dbp
+# endif /* NEWDB */
} udb_u;
};
-#define UDB_EOLIST 0 /* end of list */
-#define UDB_SKIP 1 /* skip this entry */
-#define UDB_REMOTE 2 /* look up in remote database */
-#define UDB_DBFETCH 3 /* look up in local database */
-#define UDB_FORWARD 4 /* forward to remote host */
-#define UDB_HESIOD 5 /* look up via hesiod */
+# define UDB_EOLIST 0 /* end of list */
+# define UDB_SKIP 1 /* skip this entry */
+# define UDB_REMOTE 2 /* look up in remote database */
+# define UDB_DBFETCH 3 /* look up in local database */
+# define UDB_FORWARD 4 /* forward to remote host */
+# define UDB_HESIOD 5 /* look up via hesiod */
-#define MAXUDBENT 10 /* maximum number of UDB entries */
+# define MAXUDBENT 10 /* maximum number of UDB entries */
struct udb_option
{
- char *name;
- char *val;
+ char *udbo_name;
+ char *udbo_val;
};
-#ifdef HESIOD
-extern int hes_udb_get __P((DBT *, DBT *));
-#endif
-extern int _udbx_init __P((ENVELOPE *));
- /*
+# ifdef HESIOD
+static int hes_udb_get __P((DBT *, DBT *));
+# endif /* HESIOD */
+static char *udbmatch __P((char *, char *));
+static int _udbx_init __P((ENVELOPE *));
+static int _udb_parsespec __P((char *, struct udb_option [], int));
+
+/*
** UDBEXPAND -- look up user in database and expand
**
** Parameters:
@@ -120,12 +124,8 @@ extern int _udbx_init __P((ENVELOPE *));
** Modifies sendq.
*/
-int UdbPort = 1616;
-int UdbTimeout = 10;
-
-struct udbent UdbEnts[MAXUDBENT + 1];
-int UdbSock = -1;
-bool UdbInitialized = FALSE;
+static struct udbent UdbEnts[MAXUDBENT + 1];
+static bool UdbInitialized = FALSE;
int
udbexpand(a, sendq, aliaslevel, e)
@@ -144,14 +144,14 @@ udbexpand(a, sendq, aliaslevel, e)
char *user;
char keybuf[MAXKEY];
- bzero(&key, sizeof key);
- bzero(&info, sizeof info);
+ memset(&key, '\0', sizeof key);
+ memset(&info, '\0', sizeof info);
if (tTd(28, 1))
- printf("udbexpand(%s)\n", a->q_paddr);
+ dprintf("udbexpand(%s)\n", a->q_paddr);
/* make certain we are supposed to send to this address */
- if (bitset(QDONTSEND|QVERIFIED, a->q_flags))
+ if (!QS_IS_SENDABLE(a->q_state))
return EX_OK;
e->e_to = a->q_paddr;
@@ -183,23 +183,22 @@ udbexpand(a, sendq, aliaslevel, e)
return EX_OK;
/* build actual database key */
- (void) strcpy(keybuf, user);
- (void) strcat(keybuf, ":maildrop");
+ (void) strlcpy(keybuf, user, sizeof keybuf);
+ (void) strlcat(keybuf, ":maildrop", sizeof keybuf);
keylen = strlen(keybuf);
breakout = FALSE;
for (up = UdbEnts; !breakout; up++)
{
- char *user;
int usersize;
int userleft;
char userbuf[MEMCHUNKSIZE];
-#if defined(HESIOD) && defined(HES_GETMAILHOST)
+# if defined(HESIOD) && defined(HES_GETMAILHOST)
char pobuf[MAXNAME];
-#endif
-#if defined(NEWDB) && DB_VERSION_MAJOR > 1
+# endif /* defined(HESIOD) && defined(HES_GETMAILHOST) */
+# if defined(NEWDB) && DB_VERSION_MAJOR > 1
DBC *dbc = NULL;
-#endif
+# endif /* defined(NEWDB) && DB_VERSION_MAJOR > 1 */
user = userbuf;
userbuf[0] = '\0';
@@ -216,66 +215,66 @@ udbexpand(a, sendq, aliaslevel, e)
switch (up->udb_type)
{
-#ifdef NEWDB
+# ifdef NEWDB
case UDB_DBFETCH:
key.data = keybuf;
key.size = keylen;
if (tTd(28, 80))
- printf("udbexpand: trying %s (%d) via db\n",
+ dprintf("udbexpand: trying %s (%d) via db\n",
keybuf, keylen);
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR);
-#else
+# else /* DB_VERSION_MAJOR < 2 */
i = 0;
if (dbc == NULL &&
-# if DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >=6
+# if DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6
(errno = (*up->udb_dbp->cursor)(up->udb_dbp,
NULL, &dbc, 0)) != 0)
-# else
+# else /* DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 */
(errno = (*up->udb_dbp->cursor)(up->udb_dbp,
NULL, &dbc)) != 0)
-# endif
+# endif /* DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 */
i = -1;
if (i != 0 || dbc == NULL ||
(errno = dbc->c_get(dbc, &key,
&info, DB_SET)) != 0)
i = 1;
-#endif
+# endif /* DB_VERSION_MAJOR < 2 */
if (i > 0 || info.size <= 0)
{
if (tTd(28, 2))
- printf("udbexpand: no match on %s (%d)\n",
+ dprintf("udbexpand: no match on %s (%d)\n",
keybuf, keylen);
-#if DB_VERSION_MAJOR > 1
- if (dbc != NULL)
+# if DB_VERSION_MAJOR > 1
+ if (dbc != NULL)
{
(void) dbc->c_close(dbc);
dbc = NULL;
}
-#endif
+# endif /* DB_VERSION_MAJOR > 1 */
break;
}
if (tTd(28, 80))
- printf("udbexpand: match %.*s: %.*s\n",
+ dprintf("udbexpand: match %.*s: %.*s\n",
(int) key.size, (char *) key.data,
(int) info.size, (char *) info.data);
a->q_flags &= ~QSELFREF;
while (i == 0 && key.size == keylen &&
- bcmp(key.data, keybuf, keylen) == 0)
+ memcmp(key.data, keybuf, keylen) == 0)
{
char *p;
if (bitset(EF_VRFYONLY, e->e_flags))
{
- a->q_flags |= QVERIFIED;
-#if DB_VERSION_MAJOR > 1
+ a->q_state = QS_VERIFIED;
+# if DB_VERSION_MAJOR > 1
if (dbc != NULL)
{
(void) dbc->c_close(dbc);
dbc = NULL;
}
-#endif
+# endif /* DB_VERSION_MAJOR > 1 */
return EX_OK;
}
@@ -289,7 +288,7 @@ udbexpand(a, sendq, aliaslevel, e)
size = info.size;
nuser = xalloc(usersize + size);
- bcopy(user, nuser, usersize);
+ memmove(nuser, user, usersize);
if (user != userbuf)
free(user);
user = nuser;
@@ -302,48 +301,48 @@ udbexpand(a, sendq, aliaslevel, e)
*p++ = ',';
userleft--;
}
- bcopy(info.data, p, info.size);
+ memmove(p, info.data, info.size);
p[info.size] = '\0';
userleft -= info.size;
/* get the next record */
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT);
-#else
+# else /* DB_VERSION_MAJOR < 2 */
i = 0;
if ((errno = dbc->c_get(dbc, &key,
&info, DB_NEXT)) != 0)
i = 1;
-#endif
+# endif /* DB_VERSION_MAJOR < 2 */
}
-#if DB_VERSION_MAJOR > 1
+# if DB_VERSION_MAJOR > 1
if (dbc != NULL)
{
(void) dbc->c_close(dbc);
dbc = NULL;
}
-#endif
+# endif /* DB_VERSION_MAJOR > 1 */
/* if nothing ever matched, try next database */
if (!breakout)
break;
message("expanded to %s", user);
- if (LogLevel >= 10)
+ if (LogLevel > 10)
sm_syslog(LOG_INFO, e->e_id,
- "expand %.100s => %s",
- e->e_to,
- shortenstring(user, MAXSHORTSTR));
+ "expand %.100s => %s",
+ e->e_to,
+ shortenstring(user, MAXSHORTSTR));
naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
{
if (tTd(28, 5))
{
- printf("udbexpand: QDONTSEND ");
+ dprintf("udbexpand: QS_EXPANDED ");
printaddr(a, FALSE);
}
- a->q_flags |= QDONTSEND;
+ a->q_state = QS_EXPANDED;
}
if (i < 0)
{
@@ -357,24 +356,24 @@ udbexpand(a, sendq, aliaslevel, e)
** it into the envelope.
*/
- bzero(&key, sizeof key);
- bzero(&info, sizeof info);
- (void) strcpy(keybuf, a->q_user);
- (void) strcat(keybuf, ":mailsender");
+ memset(&key, '\0', sizeof key);
+ memset(&info, '\0', sizeof info);
+ (void) strlcpy(keybuf, a->q_user, sizeof keybuf);
+ (void) strlcat(keybuf, ":mailsender", sizeof keybuf);
keylen = strlen(keybuf);
key.data = keybuf;
key.size = keylen;
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
-#else
+# else /* DB_VERSION_MAJOR < 2 */
i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
&key, &info, 0);
-#endif
+# endif /* DB_VERSION_MAJOR < 2 */
if (i != 0 || info.size <= 0)
break;
a->q_owner = xalloc(info.size + 1);
- bcopy(info.data, a->q_owner, info.size);
+ memmove(a->q_owner, info.data, info.size);
a->q_owner[info.size] = '\0';
/* announce delivery; NORECEIPT bit set later */
@@ -387,14 +386,14 @@ udbexpand(a, sendq, aliaslevel, e)
e->e_flags |= EF_SENDRECEIPT;
a->q_flags |= QDELIVERED|QEXPANDED;
break;
-#endif
+# endif /* NEWDB */
-#ifdef HESIOD
+# ifdef HESIOD
case UDB_HESIOD:
key.data = keybuf;
key.size = keylen;
if (tTd(28, 80))
- printf("udbexpand: trying %s (%d) via hesiod\n",
+ dprintf("udbexpand: trying %s (%d) via hesiod\n",
keybuf, keylen);
/* look up the key via hesiod */
i = hes_udb_get(&key, &info);
@@ -406,16 +405,16 @@ udbexpand(a, sendq, aliaslevel, e)
}
else if (i > 0 || info.size <= 0)
{
-#if HES_GETMAILHOST
+# if HES_GETMAILHOST
struct hes_postoffice *hp;
-#endif
+# endif /* HES_GETMAILHOST */
if (tTd(28, 2))
- printf("udbexpand: no match on %s (%d)\n",
+ dprintf("udbexpand: no match on %s (%d)\n",
(char *) keybuf, (int) keylen);
-#if HES_GETMAILHOST
+# if HES_GETMAILHOST
if (tTd(28, 8))
- printf(" ... trying hes_getmailhost(%s)\n",
+ dprintf(" ... trying hes_getmailhost(%s)\n",
a->q_user);
hp = hes_getmailhost(a->q_user);
if (hp == NULL)
@@ -427,7 +426,7 @@ udbexpand(a, sendq, aliaslevel, e)
return EX_TEMPFAIL;
}
if (tTd(28, 2))
- printf("hes_getmailhost(%s): %d\n",
+ dprintf("hes_getmailhost(%s): %d\n",
a->q_user, hes_error());
break;
}
@@ -435,7 +434,7 @@ udbexpand(a, sendq, aliaslevel, e)
sizeof pobuf - 2)
{
if (tTd(28, 2))
- printf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n",
+ dprintf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n",
a->q_user,
hp->po_name,
hp->po_host);
@@ -445,44 +444,44 @@ udbexpand(a, sendq, aliaslevel, e)
snprintf(pobuf, sizeof pobuf, "%s@%s",
hp->po_name, hp->po_host);
info.size = strlen(info.data);
-#else
+# else /* HES_GETMAILHOST */
break;
-#endif
+# endif /* HES_GETMAILHOST */
}
if (tTd(28, 80))
- printf("udbexpand: match %.*s: %.*s\n",
+ dprintf("udbexpand: match %.*s: %.*s\n",
(int) key.size, (char *) key.data,
(int) info.size, (char *) info.data);
a->q_flags &= ~QSELFREF;
if (bitset(EF_VRFYONLY, e->e_flags))
{
- a->q_flags |= QVERIFIED;
+ a->q_state = QS_VERIFIED;
return EX_OK;
}
breakout = TRUE;
if (info.size >= usersize)
user = xalloc(info.size + 1);
- bcopy(info.data, user, info.size);
+ memmove(user, info.data, info.size);
user[info.size] = '\0';
message("hesioded to %s", user);
- if (LogLevel >= 10)
+ if (LogLevel > 10)
sm_syslog(LOG_INFO, e->e_id,
- "hesiod %.100s => %s",
- e->e_to,
- shortenstring(user, MAXSHORTSTR));
+ "hesiod %.100s => %s",
+ e->e_to,
+ shortenstring(user, MAXSHORTSTR));
naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
{
if (tTd(28, 5))
{
- printf("udbexpand: QDONTSEND ");
+ dprintf("udbexpand: QS_EXPANDED ");
printaddr(a, FALSE);
}
- a->q_flags |= QDONTSEND;
+ a->q_state = QS_EXPANDED;
}
/*
@@ -490,8 +489,8 @@ udbexpand(a, sendq, aliaslevel, e)
** it into the envelope.
*/
- (void) strcpy(keybuf, a->q_user);
- (void) strcat(keybuf, ":mailsender");
+ (void) strlcpy(keybuf, a->q_user, sizeof keybuf);
+ (void) strlcat(keybuf, ":mailsender", sizeof keybuf);
keylen = strlen(keybuf);
key.data = keybuf;
key.size = keylen;
@@ -499,10 +498,10 @@ udbexpand(a, sendq, aliaslevel, e)
if (i != 0 || info.size <= 0)
break;
a->q_owner = xalloc(info.size + 1);
- bcopy(info.data, a->q_owner, info.size);
+ memmove(a->q_owner, info.data, info.size);
a->q_owner[info.size] = '\0';
break;
-#endif /* HESIOD */
+# endif /* HESIOD */
case UDB_REMOTE:
/* not yet implemented */
@@ -510,7 +509,10 @@ udbexpand(a, sendq, aliaslevel, e)
case UDB_FORWARD:
if (bitset(EF_VRFYONLY, e->e_flags))
+ {
+ a->q_state = QS_VERIFIED;
return EX_OK;
+ }
i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1;
if (i >= usersize)
{
@@ -518,7 +520,7 @@ udbexpand(a, sendq, aliaslevel, e)
user = xalloc(usersize);
}
(void) snprintf(user, usersize, "%s@%s",
- a->q_user, up->udb_fwdhost);
+ a->q_user, up->udb_fwdhost);
message("expanded to %s", user);
a->q_flags &= ~QSELFREF;
naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
@@ -526,10 +528,10 @@ udbexpand(a, sendq, aliaslevel, e)
{
if (tTd(28, 5))
{
- printf("udbexpand: QDONTSEND ");
+ dprintf("udbexpand: QS_EXPANDED ");
printaddr(a, FALSE);
}
- a->q_flags |= QDONTSEND;
+ a->q_state = QS_EXPANDED;
}
breakout = TRUE;
break;
@@ -566,13 +568,25 @@ char *
udbsender(sender)
char *sender;
{
- extern char *udbmatch __P((char *, char *));
-
return udbmatch(sender, "mailname");
}
+ /*
+** UDBMATCH -- match user in field, return result of lookup.
+**
+** Parameters:
+** user -- the name of the user.
+** field -- the field to lookup.
+**
+** Returns:
+** The external name for this sender, if derivable from the
+** database.
+** NULL -- if nothing is changed from the database.
+**
+** Side Effects:
+** none.
+*/
-
-char *
+static char *
udbmatch(user, field)
char *user;
char *field;
@@ -585,7 +599,7 @@ udbmatch(user, field)
char keybuf[MAXKEY];
if (tTd(28, 1))
- printf("udbmatch(%s, %s)\n", user, field);
+ dprintf("udbmatch(%s, %s)\n", user, field);
if (!UdbInitialized)
{
@@ -613,9 +627,7 @@ udbmatch(user, field)
return NULL;
/* build database key */
- (void) strcpy(keybuf, user);
- (void) strcat(keybuf, ":");
- (void) strcat(keybuf, field);
+ (void) snprintf(keybuf, sizeof keybuf, "%s:%s", user, field);
keylen = strlen(keybuf);
for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
@@ -626,54 +638,54 @@ udbmatch(user, field)
switch (up->udb_type)
{
-#ifdef NEWDB
+# ifdef NEWDB
case UDB_DBFETCH:
- bzero(&key, sizeof key);
- bzero(&info, sizeof info);
+ memset(&key, '\0', sizeof key);
+ memset(&info, '\0', sizeof info);
key.data = keybuf;
key.size = keylen;
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
-#else
+# else /* DB_VERSION_MAJOR < 2 */
i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
&key, &info, 0);
-#endif
+# endif /* DB_VERSION_MAJOR < 2 */
if (i != 0 || info.size <= 0)
{
if (tTd(28, 2))
- printf("udbmatch: no match on %s (%d) via db\n",
- keybuf, keylen);
+ dprintf("udbmatch: no match on %s (%d) via db\n",
+ keybuf, keylen);
continue;
}
p = xalloc(info.size + 1);
- bcopy(info.data, p, info.size);
+ memmove(p, info.data, info.size);
p[info.size] = '\0';
if (tTd(28, 1))
- printf("udbmatch ==> %s\n", p);
+ dprintf("udbmatch ==> %s\n", p);
return p;
-#endif
+# endif /* NEWDB */
-#ifdef HESIOD
+# ifdef HESIOD
case UDB_HESIOD:
key.data = keybuf;
key.size = keylen;
- i = hes_udb_get(&key, &info);
+ i = hes_udb_get(&key, &info);
if (i != 0 || info.size <= 0)
{
if (tTd(28, 2))
- printf("udbmatch: no match on %s (%d) via hesiod\n",
- keybuf, keylen);
+ dprintf("udbmatch: no match on %s (%d) via hesiod\n",
+ keybuf, keylen);
continue;
}
p = xalloc(info.size + 1);
- bcopy(info.data, p, info.size);
+ memmove(p, info.data, info.size);
p[info.size] = '\0';
if (tTd(28, 1))
- printf("udbmatch ==> %s\n", p);
+ dprintf("udbmatch ==> %s\n", p);
return p;
-#endif /* HESIOD */
+# endif /* HESIOD */
}
}
@@ -687,31 +699,31 @@ udbmatch(user, field)
*/
/* build database key */
- (void) strcpy(keybuf, user);
- (void) strcat(keybuf, ":maildrop");
+ (void) strlcpy(keybuf, user, sizeof keybuf);
+ (void) strlcat(keybuf, ":maildrop", sizeof keybuf);
keylen = strlen(keybuf);
for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
{
switch (up->udb_type)
{
-#ifdef NEWDB
+# ifdef NEWDB
case UDB_DBFETCH:
/* get the default case for this database */
if (up->udb_default == NULL)
{
- bzero(&key, sizeof key);
- bzero(&info, sizeof info);
+ memset(&key, '\0', sizeof key);
+ memset(&info, '\0', sizeof info);
key.data = ":default:mailname";
key.size = strlen(key.data);
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
i = (*up->udb_dbp->get)(up->udb_dbp,
&key, &info, 0);
-#else
+# else /* DB_VERSION_MAJOR < 2 */
i = errno = (*up->udb_dbp->get)(up->udb_dbp,
NULL, &key,
&info, 0);
-#endif
+# endif /* DB_VERSION_MAJOR < 2 */
if (i != 0 || info.size <= 0)
{
/* no default case */
@@ -721,23 +733,23 @@ udbmatch(user, field)
/* save the default case */
up->udb_default = xalloc(info.size + 1);
- bcopy(info.data, up->udb_default, info.size);
+ memmove(up->udb_default, info.data, info.size);
up->udb_default[info.size] = '\0';
}
else if (up->udb_default[0] == '\0')
continue;
/* we have a default case -- verify user:maildrop */
- bzero(&key, sizeof key);
- bzero(&info, sizeof info);
+ memset(&key, '\0', sizeof key);
+ memset(&info, '\0', sizeof info);
key.data = keybuf;
key.size = keylen;
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
-#else
+# else /* DB_VERSION_MAJOR < 2 */
i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
&key, &info, 0);
-#endif
+# endif /* DB_VERSION_MAJOR < 2 */
if (i != 0 || info.size <= 0)
{
/* nope -- no aliasing for this user */
@@ -745,23 +757,22 @@ udbmatch(user, field)
}
/* they exist -- build the actual address */
- p = xalloc(strlen(user) + strlen(up->udb_default) + 2);
- (void) strcpy(p, user);
- (void) strcat(p, "@");
- (void) strcat(p, up->udb_default);
+ i = strlen(user) + strlen(up->udb_default) + 2;
+ p = xalloc(i);
+ (void) snprintf(p, i, "%s@%s", user, up->udb_default);
if (tTd(28, 1))
- printf("udbmatch ==> %s\n", p);
+ dprintf("udbmatch ==> %s\n", p);
return p;
-#endif
+# endif /* NEWDB */
-#ifdef HESIOD
+# ifdef HESIOD
case UDB_HESIOD:
/* get the default case for this database */
if (up->udb_default == NULL)
{
key.data = ":default:mailname";
key.size = strlen(key.data);
- i = hes_udb_get(&key, &info);
+ i = hes_udb_get(&key, &info);
if (i != 0 || info.size <= 0)
{
@@ -772,7 +783,7 @@ udbmatch(user, field)
/* save the default case */
up->udb_default = xalloc(info.size + 1);
- bcopy(info.data, up->udb_default, info.size);
+ memmove(up->udb_default, info.data, info.size);
up->udb_default[info.size] = '\0';
}
else if (up->udb_default[0] == '\0')
@@ -789,15 +800,14 @@ udbmatch(user, field)
}
/* they exist -- build the actual address */
- p = xalloc(strlen(user) + strlen(up->udb_default) + 2);
- (void) strcpy(p, user);
- (void) strcat(p, "@");
- (void) strcat(p, up->udb_default);
+ i = strlen(user) + strlen(up->udb_default) + 2;
+ p = xalloc(i);
+ (void) snprintf(p, i, "%s@%s", user, up->udb_default);
if (tTd(28, 1))
- printf("udbmatch ==> %s\n", p);
+ dprintf("udbmatch ==> %s\n", p);
return p;
break;
-#endif /* HESIOD */
+# endif /* HESIOD */
}
}
@@ -831,7 +841,7 @@ udb_map_lookup(map, name, av, statp)
char keybuf[MAXNAME + 1];
if (tTd(28, 20) || tTd(38, 20))
- printf("udb_map_lookup(%s, %s)\n", map->map_mname, name);
+ dprintf("udb_map_lookup(%s, %s)\n", map->map_mname, name);
if (bitset(MF_NOFOLDCASE, map->map_mflags))
{
@@ -843,7 +853,7 @@ udb_map_lookup(map, name, av, statp)
if (keysize > sizeof keybuf - 1)
keysize = sizeof keybuf - 1;
- bcopy(name, keybuf, keysize);
+ memmove(keybuf, name, keysize);
keybuf[keysize] = '\0';
makelower(keybuf);
key = keybuf;
@@ -872,9 +882,9 @@ udb_map_lookup(map, name, av, statp)
** Fills in the UdbEnts structure from UdbSpec.
*/
-#define MAXUDBOPTS 27
+# define MAXUDBOPTS 27
-int
+static int
_udbx_init(e)
ENVELOPE *e;
{
@@ -888,7 +898,7 @@ _udbx_init(e)
# ifdef UDB_DEFAULT_SPEC
if (UdbSpec == NULL)
UdbSpec = UDB_DEFAULT_SPEC;
-# endif
+# endif /* UDB_DEFAULT_SPEC */
p = UdbSpec;
up = UdbEnts;
@@ -896,15 +906,7 @@ _udbx_init(e)
{
char *spec;
int l;
-# if 0
- auto int rcode;
- int nmx;
- int i;
- register struct hostent *h;
- char *mxhosts[MAXMXHOSTS + 1];
-# endif
struct udb_option opts[MAXUDBOPTS + 1];
- extern int _udb_parsespec __P((char *, struct udb_option [], int));
while (*p == ' ' || *p == '\t' || *p == ',')
p++;
@@ -930,14 +932,6 @@ _udbx_init(e)
** In the sendmail tradition, the leading character
** defines the semantics of the rest of the entry.
**
- ** +hostname -- send a datagram to the udb server
- ** on host "hostname" asking for the
- ** home mail server for this user.
- ** *hostname -- similar to +hostname, except that the
- ** hostname is searched as an MX record;
- ** resulting hosts are searched as for
- ** +mxhostname. If no MX host is found,
- ** this is the same as +hostname.
** @hostname -- forward email to the indicated host.
** This should be the last in the list,
** since it always matches the input.
@@ -949,60 +943,6 @@ _udbx_init(e)
switch (*spec)
{
-#if 0
- case '+': /* search remote database */
- case '*': /* search remote database (expand MX) */
- if (*spec == '*')
- {
-#if NAMED_BIND
- nmx = getmxrr(spec + 1, mxhosts, FALSE, &rcode);
-#else
- mxhosts[0] = spec + 1;
- nmx = 1;
- rcode = 0;
-#endif
- if (tTd(28, 16))
- {
- int i;
-
- printf("getmxrr(%s): %d", spec + 1, nmx);
- for (i = 0; i <= nmx; i++)
- printf(" %s", mxhosts[i]);
- printf("\n");
- }
- }
- else
- {
- nmx = 1;
- mxhosts[0] = spec + 1;
- }
-
- for (i = 0; i < nmx; i++)
- {
- h = sm_gethostbyname(mxhosts[i]);
- if (h == NULL)
- continue;
- up->udb_type = UDB_REMOTE;
- up->udb_pid = getpid();
- up->udb_addr.sin_family = h->h_addrtype;
- bcopy(h->h_addr_list[0],
- (char *) &up->udb_addr.sin_addr,
- INADDRSZ);
- up->udb_addr.sin_port = UdbPort;
- up->udb_timeout = UdbTimeout;
- ents++;
- up++;
- }
-
- /* set up a datagram socket */
- if (UdbSock < 0)
- {
- UdbSock = socket(AF_INET, SOCK_DGRAM, 0);
- (void) fcntl(UdbSock, F_SETFD, 1);
- }
- break;
-#endif
-
case '@': /* forward to remote host */
up->udb_type = UDB_FORWARD;
up->udb_pid = getpid();
@@ -1011,7 +951,7 @@ _udbx_init(e)
up++;
break;
-#ifdef HESIOD
+# ifdef HESIOD
case 'h': /* use hesiod */
case 'H':
if (strcasecmp(spec, "hesiod") != 0)
@@ -1021,9 +961,9 @@ _udbx_init(e)
ents++;
up++;
break;
-#endif /* HESIOD */
+# endif /* HESIOD */
-#ifdef NEWDB
+# ifdef NEWDB
case '/': /* look up remote name */
l = strlen(spec);
if (l > 3 && strcmp(&spec[l - 3], ".db") == 0)
@@ -1033,44 +973,82 @@ _udbx_init(e)
else
{
up->udb_dbname = xalloc(l + 4);
- strcpy(up->udb_dbname, spec);
- strcat(up->udb_dbname, ".db");
+ (void) strlcpy(up->udb_dbname, spec, l + 4);
+ (void) strlcat(up->udb_dbname, ".db", l + 4);
}
errno = 0;
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
up->udb_dbp = dbopen(up->udb_dbname, O_RDONLY,
0644, DB_BTREE, NULL);
-#else
- up->udb_dbp = NULL;
- errno = db_open(up->udb_dbname, DB_BTREE, DB_RDONLY,
- 0644, NULL, NULL, &up->udb_dbp);
-#endif
+# else /* DB_VERSION_MAJOR < 2 */
+ {
+ int flags = DB_RDONLY;
+# if DB_VERSION_MAJOR > 2
+ int ret;
+# endif /* DB_VERSION_MAJOR > 2 */
+
+# if !HASFLOCK && defined(DB_FCNTL_LOCKING)
+ flags |= DB_FCNTL_LOCKING;
+# endif /* !HASFLOCK && defined(DB_FCNTL_LOCKING) */
+
+ up->udb_dbp = NULL;
+
+# if DB_VERSION_MAJOR > 2
+ ret = db_create(&up->udb_dbp, NULL, 0);
+ if (ret != 0)
+ {
+ (void) up->udb_dbp->close(up->udb_dbp,
+ 0);
+ up->udb_dbp = NULL;
+ }
+ else
+ {
+ ret = up->udb_dbp->open(up->udb_dbp,
+ up->udb_dbname,
+ NULL,
+ DB_BTREE,
+ flags,
+ 0644);
+ if (ret != 0)
+ {
+ (void) up->udb_dbp->close(up->udb_dbp, 0);
+ up->udb_dbp = NULL;
+ }
+ }
+ errno = ret;
+# else /* DB_VERSION_MAJOR > 2 */
+ errno = db_open(up->udb_dbname, DB_BTREE,
+ flags, 0644, NULL,
+ NULL, &up->udb_dbp);
+# endif /* DB_VERSION_MAJOR > 2 */
+ }
+# endif /* DB_VERSION_MAJOR < 2 */
if (up->udb_dbp == NULL)
{
if (tTd(28, 1))
{
- int saveerrno = errno;
+ int save_errno = errno;
-#if DB_VERSION_MAJOR < 2
- printf("dbopen(%s): %s\n",
-#else
- printf("db_open(%s): %s\n",
-#endif
+# if DB_VERSION_MAJOR < 2
+ dprintf("dbopen(%s): %s\n",
+# else /* DB_VERSION_MAJOR < 2 */
+ dprintf("db_open(%s): %s\n",
+# endif /* DB_VERSION_MAJOR < 2 */
up->udb_dbname,
errstring(errno));
- errno = saveerrno;
+ errno = save_errno;
}
if (errno != ENOENT && errno != EACCES)
{
if (LogLevel > 2)
sm_syslog(LOG_ERR, e->e_id,
-#if DB_VERSION_MAJOR < 2
- "dbopen(%s): %s",
-#else
- "db_open(%s): %s",
-#endif
- up->udb_dbname,
- errstring(errno));
+# if DB_VERSION_MAJOR < 2
+ "dbopen(%s): %s",
+# else /* DB_VERSION_MAJOR < 2 */
+ "db_open(%s): %s",
+# endif /* DB_VERSION_MAJOR < 2 */
+ up->udb_dbname,
+ errstring(errno));
up->udb_type = UDB_EOLIST;
if (up->udb_dbname != spec)
free(up->udb_dbname);
@@ -1082,11 +1060,11 @@ _udbx_init(e)
}
if (tTd(28, 1))
{
-#if DB_VERSION_MAJOR < 2
- printf("_udbx_init: dbopen(%s)\n",
-#else
- printf("_udbx_init: db_open(%s)\n",
-#endif
+# if DB_VERSION_MAJOR < 2
+ dprintf("_udbx_init: dbopen(%s)\n",
+# else /* DB_VERSION_MAJOR < 2 */
+ dprintf("_udbx_init: db_open(%s)\n",
+# endif /* DB_VERSION_MAJOR < 2 */
up->udb_dbname);
}
up->udb_type = UDB_DBFETCH;
@@ -1094,10 +1072,12 @@ _udbx_init(e)
ents++;
up++;
break;
-#endif
+# endif /* NEWDB */
default:
+# ifdef HESIOD
badspec:
+# endif /* HESIOD */
syserr("Unknown UDB spec %s", spec);
break;
}
@@ -1110,34 +1090,34 @@ badspec:
{
switch (up->udb_type)
{
-#if DAEMON
+# if DAEMON
case UDB_REMOTE:
- printf("REMOTE: addr %s, timeo %d\n",
+ dprintf("REMOTE: addr %s, timeo %d\n",
anynet_ntoa((SOCKADDR *) &up->udb_addr),
up->udb_timeout);
break;
-#endif
+# endif /* DAEMON */
case UDB_DBFETCH:
-#ifdef NEWDB
- printf("FETCH: file %s\n",
+# ifdef NEWDB
+ dprintf("FETCH: file %s\n",
up->udb_dbname);
-#else
- printf("FETCH\n");
-#endif
+# else /* NEWDB */
+ dprintf("FETCH\n");
+# endif /* NEWDB */
break;
case UDB_FORWARD:
- printf("FORWARD: host %s\n",
+ dprintf("FORWARD: host %s\n",
up->udb_fwdhost);
break;
case UDB_HESIOD:
- printf("HESIOD\n");
+ dprintf("HESIOD\n");
break;
default:
- printf("UNKNOWN\n");
+ dprintf("UNKNOWN\n");
break;
}
}
@@ -1152,28 +1132,26 @@ badspec:
*/
tempfail:
-#ifdef NEWDB
+# ifdef NEWDB
for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
{
if (up->udb_type == UDB_DBFETCH)
{
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
(*up->udb_dbp->close)(up->udb_dbp);
-#else
+# else /* DB_VERSION_MAJOR < 2 */
errno = (*up->udb_dbp->close)(up->udb_dbp, 0);
-#endif
+# endif /* DB_VERSION_MAJOR < 2 */
if (tTd(28, 1))
- {
- printf("_udbx_init: db->close(%s)\n",
+ dprintf("_udbx_init: db->close(%s)\n",
up->udb_dbname);
- }
}
}
-#endif
+# endif /* NEWDB */
return EX_TEMPFAIL;
}
-int
+static int
_udb_parsespec(udbspec, opt, maxopts)
char *udbspec;
struct udb_option opt[];
@@ -1194,11 +1172,11 @@ _udb_parsespec(udbspec, opt, maxopts)
if (spec_end != NULL)
*spec_end++ = '\0';
- opt[optnum].name = spec;
- opt[optnum].val = NULL;
+ opt[optnum].udbo_name = spec;
+ opt[optnum].udbo_val = NULL;
p = strchr(spec, '=');
if (p != NULL)
- opt[optnum].val = ++p;
+ opt[optnum].udbo_val = ++p;
}
return optnum;
}
@@ -1226,28 +1204,26 @@ _udbx_close()
{
if (up->udb_pid != pid)
continue;
-
-#ifdef NEWDB
+
+# ifdef NEWDB
if (up->udb_type == UDB_DBFETCH)
{
-#if DB_VERSION_MAJOR < 2
+# if DB_VERSION_MAJOR < 2
(*up->udb_dbp->close)(up->udb_dbp);
-#else
+# else /* DB_VERSION_MAJOR < 2 */
errno = (*up->udb_dbp->close)(up->udb_dbp, 0);
-#endif
+# endif /* DB_VERSION_MAJOR < 2 */
}
if (tTd(28, 1))
- {
- printf("_udbx_init: db->close(%s)\n",
+ dprintf("_udbx_init: db->close(%s)\n",
up->udb_dbname);
- }
-#endif
+# endif /* NEWDB */
}
}
-#ifdef HESIOD
+# ifdef HESIOD
-int
+static int
hes_udb_get(key, info)
DBT *key;
DBT *info;
@@ -1256,9 +1232,8 @@ hes_udb_get(key, info)
char **hp;
char kbuf[MAXKEY + 1];
- if (strlen(key->data) >= (SIZE_T) sizeof kbuf)
+ if (strlcpy(kbuf, key->data, sizeof kbuf) >= (SIZE_T) sizeof kbuf)
return 0;
- strcpy(kbuf, key->data);
name = kbuf;
type = strrchr(name, ':');
if (type == NULL)
@@ -1268,18 +1243,18 @@ hes_udb_get(key, info)
return 1;
if (tTd(28, 1))
- printf("hes_udb_get(%s, %s)\n", name, type);
+ dprintf("hes_udb_get(%s, %s)\n", name, type);
/* make the hesiod query */
-#ifdef HESIOD_INIT
+# ifdef HESIOD_INIT
if (HesiodContext == NULL && hesiod_init(&HesiodContext) != 0)
return -1;
hp = hesiod_resolve(HesiodContext, name, type);
-#else
+# else /* HESIOD_INIT */
hp = hes_resolve(name, type);
-#endif /* HESIOD_INIT */
+# endif /* HESIOD_INIT */
*--type = ':';
-#ifdef HESIOD_INIT
+# ifdef HESIOD_INIT
if (hp == NULL)
return 1;
if (*hp == NULL)
@@ -1289,7 +1264,7 @@ hes_udb_get(key, info)
return -1;
return 1;
}
-#else
+# else /* HESIOD_INIT */
if (hp == NULL || hp[0] == NULL)
{
/* network problem or timeout */
@@ -1298,7 +1273,7 @@ hes_udb_get(key, info)
return 1;
}
-#endif /* HESIOD_INIT */
+# endif /* HESIOD_INIT */
else
{
/*
@@ -1314,13 +1289,13 @@ hes_udb_get(key, info)
}
if (tTd(28, 80))
- printf("hes_udb_get => %s\n", *hp);
+ dprintf("hes_udb_get => %s\n", *hp);
return 0;
}
-#endif /* HESIOD */
+# endif /* HESIOD */
-#else /* not USERDB */
+#else /* USERDB */
int
udbexpand(a, sendq, aliaslevel, e)
diff --git a/contrib/sendmail/src/usersmtp.c b/contrib/sendmail/src/usersmtp.c
index c82942b..4254e58 100644
--- a/contrib/sendmail/src/usersmtp.c
+++ b/contrib/sendmail/src/usersmtp.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -10,20 +11,24 @@
*
*/
-# include "sendmail.h"
+#include <sendmail.h>
#ifndef lint
+# if SMTP
+static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.12 2000/07/17 19:55:08 ca Exp $ (with SMTP)";
+# else /* SMTP */
+static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.12 2000/07/17 19:55:08 ca Exp $ (without SMTP)";
+# endif /* SMTP */
+#endif /* ! lint */
+
+#include <sysexits.h>
+
#if SMTP
-static char sccsid[] = "@(#)usersmtp.c 8.111 (Berkeley) 2/3/1999 (with SMTP)";
-#else
-static char sccsid[] = "@(#)usersmtp.c 8.111 (Berkeley) 2/3/1999 (without SMTP)";
-#endif
-#endif /* not lint */
-# include <sysexits.h>
-# include <errno.h>
-# if SMTP
+static void datatimeout __P((void));
+static void esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
+static void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
/*
** USERSMTP -- run SMTP protocol from the user end.
@@ -31,17 +36,15 @@ static char sccsid[] = "@(#)usersmtp.c 8.111 (Berkeley) 2/3/1999 (without SMTP)"
** 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" */
-char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */
-char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
-char SmtpError[MAXLINE] = ""; /* save failure error messages */
-bool SmtpNeedIntro; /* need "while talking" in transcript */
+#define ENHSCN(e, d) (e) == NULL ? (d) : newstr(e)
-extern void smtpmessage __P((char *f, MAILER *m, MCI *mci, ...));
-extern int reply __P((MAILER *, MCI *, ENVELOPE *, time_t, void (*)()));
+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.
**
@@ -49,8 +52,9 @@ extern int reply __P((MAILER *, MCI *, ENVELOPE *, time_t, void (*)()));
**
** Parameters:
** m -- mailer to create connection to.
-** pvp -- pointer to parameter vector to pass to
-** the mailer.
+** mci -- the mailer connection info.
+** e -- the envelope.
+** onlyhelo -- send only helo command?
**
** Returns:
** none.
@@ -60,19 +64,21 @@ extern int reply __P((MAILER *, MCI *, ENVELOPE *, time_t, void (*)()));
*/
void
-smtpinit(m, mci, e)
+smtpinit(m, mci, e, onlyhelo)
MAILER *m;
register MCI *mci;
ENVELOPE *e;
+ bool onlyhelo;
{
register int r;
register char *p;
- extern void esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
- extern void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
+ register char *hn;
+ char *enhsc;
+ enhsc = NULL;
if (tTd(18, 1))
{
- printf("smtpinit ");
+ dprintf("smtpinit ");
mci_dump(mci, FALSE);
}
@@ -90,24 +96,29 @@ smtpinit(m, mci, e)
case MCIS_ACTIVE:
/* need to clear old information */
smtprset(m, mci, e);
- /* fall through */
+ /* FALLTHROUGH */
case MCIS_OPEN:
- return;
+ if (!onlyhelo)
+ return;
+ break;
case MCIS_ERROR:
+ case MCIS_QUITING:
case MCIS_SSD:
/* shouldn't happen */
smtpquit(m, mci, e);
- /* fall through */
+ /* FALLTHROUGH */
case MCIS_CLOSED:
- syserr("451 smtpinit: state CLOSED");
+ syserr("451 4.4.0 smtpinit: state CLOSED");
return;
case MCIS_OPENING:
break;
}
+ if (onlyhelo)
+ goto helo;
mci->mci_state = MCIS_OPENING;
@@ -118,8 +129,9 @@ smtpinit(m, mci, e)
*/
SmtpPhase = mci->mci_phase = "client greeting";
- sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
- r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check);
+ 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)
goto tempfail1;
if (REPLYTYPE(r) == 4)
@@ -132,27 +144,30 @@ smtpinit(m, mci, e)
** My mother taught me to always introduce myself.
*/
+helo:
if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
mci->mci_flags |= MCIF_ESMTP;
+ hn = mci->mci_heloname ? mci->mci_heloname : MyHostName;
tryhelo:
if (bitnset(M_LMTP, m->m_flags))
{
- smtpmessage("LHLO %s", m, mci, MyHostName);
+ smtpmessage("LHLO %s", m, mci, hn);
SmtpPhase = mci->mci_phase = "client LHLO";
}
else if (bitset(MCIF_ESMTP, mci->mci_flags))
{
- smtpmessage("EHLO %s", m, mci, MyHostName);
+ smtpmessage("EHLO %s", m, mci, hn);
SmtpPhase = mci->mci_phase = "client EHLO";
}
else
{
- smtpmessage("HELO %s", m, mci, MyHostName);
+ smtpmessage("HELO %s", m, mci, hn);
SmtpPhase = mci->mci_phase = "client HELO";
}
- sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
- r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
+ 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);
if (r < 0)
goto tempfail1;
else if (REPLYTYPE(r) == 5)
@@ -182,9 +197,9 @@ tryhelo:
!bitnset(M_LMTP, m->m_flags) &&
strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
{
- syserr("553 %s config error: mail loops back to me (MX problem?)",
+ syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
CurHostName);
- mci_setstat(mci, EX_CONFIG, NULL, NULL);
+ mci_setstat(mci, EX_CONFIG, "5.3.5", "system config error");
mci->mci_errno = 0;
smtpquit(m, mci, e);
return;
@@ -199,7 +214,7 @@ tryhelo:
{
/* tell it to be verbose */
smtpmessage("VERB", m, mci);
- r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
+ r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc);
if (r < 0)
goto tempfail1;
}
@@ -215,7 +230,7 @@ tryhelo:
tempfail1:
if (mci->mci_errno == 0)
mci->mci_errno = errno;
- mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
+ mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
if (mci->mci_state != MCIS_CLOSED)
smtpquit(m, mci, e);
return;
@@ -224,7 +239,8 @@ tryhelo:
if (mci->mci_errno == 0)
mci->mci_errno = errno;
/* XXX should use code from other end iff ENHANCEDSTATUSCODES */
- mci_setstat(mci, EX_TEMPFAIL, "4.5.0", SmtpReplyBuffer);
+ mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
+ SmtpReplyBuffer);
if (mci->mci_state != MCIS_CLOSED)
smtpquit(m, mci, e);
return;
@@ -249,7 +265,7 @@ tryhelo:
** none.
*/
-void
+static void
esmtp_check(line, firstline, m, mci, e)
char *line;
bool firstline;
@@ -262,6 +278,74 @@ esmtp_check(line, firstline, m, mci, e)
if (strstr(line, "8BIT-OK") != NULL)
mci->mci_flags |= MCIF_8BITOK;
}
+# if SASL
+ /*
+** STR_UNION -- create the union of two lists
+**
+** Parameters:
+** s1, s2 -- lists of items (separated by single blanks).
+**
+** Returns:
+** the union of both lists.
+*/
+
+static char *
+str_union(s1, s2)
+ char *s1, *s2;
+{
+ char *hr, *h1, *h, *res;
+ int l1, l2, rl;
+
+ if (s1 == NULL || *s1 == '\0')
+ return s2;
+ if (s2 == NULL || *s2 == '\0')
+ return s1;
+ l1 = strlen(s1);
+ l2 = strlen(s2);
+ rl = l1 + l2;
+ res = (char *)malloc(rl + 2);
+ if (res == NULL)
+ {
+ if (l1 > l2)
+ return s1;
+ return s2;
+ }
+ (void) strlcpy(res, s1, rl);
+ hr = res;
+ h1 = s2;
+ h = s2;
+
+ /* walk through s2 */
+ while (h != NULL && *h1 != '\0')
+ {
+ /* is there something after the current word? */
+ if ((h = strchr(h1, ' ')) != NULL)
+ *h = '\0';
+ l1 = strlen(h1);
+
+ /* does the current word appear in s1 ? */
+ if (iteminlist(h1, s1, " ") == NULL)
+ {
+ /* add space as delimiter */
+ *hr++ = ' ';
+
+ /* copy the item */
+ memcpy(hr, h1, l1);
+
+ /* advance pointer in result list */
+ hr += l1;
+ *hr = '\0';
+ }
+ if (h != NULL)
+ {
+ /* there are more items */
+ *h = ' ';
+ h1 = h + 1;
+ }
+ }
+ return res;
+}
+# endif /* SASL */
/*
** HELO_OPTIONS -- process the options on a HELO line.
**
@@ -276,7 +360,7 @@ esmtp_check(line, firstline, m, mci, e)
** none.
*/
-void
+static void
helo_options(line, firstline, m, mci, e)
char *line;
bool firstline;
@@ -287,12 +371,19 @@ helo_options(line, firstline, m, mci, e)
register char *p;
if (firstline)
+ {
+# if SASL
+ if (mci->mci_saslcap != NULL)
+ free(mci->mci_saslcap);
+ mci->mci_saslcap = NULL;
+# endif /* SASL */
return;
+ }
if (strlen(line) < (SIZE_T) 5)
return;
line += 4;
- p = strchr(line, ' ');
+ p = strpbrk(line, " =");
if (p != NULL)
*p++ = '\0';
if (strcasecmp(line, "size") == 0)
@@ -310,7 +401,987 @@ helo_options(line, firstline, m, mci, e)
mci->mci_flags |= MCIF_EXPN;
else if (strcasecmp(line, "dsn") == 0)
mci->mci_flags |= MCIF_DSN;
+ else if (strcasecmp(line, "enhancedstatuscodes") == 0)
+ mci->mci_flags |= MCIF_ENHSTAT;
+# if STARTTLS
+ else if (strcasecmp(line, "starttls") == 0)
+ mci->mci_flags |= MCIF_TLS;
+# endif /* STARTTLS */
+# if SASL
+ else if (strcasecmp(line, "auth") == 0)
+ {
+ if (p != NULL && *p != '\0')
+ {
+ if (mci->mci_saslcap != NULL)
+ {
+ char *h;
+
+ /*
+ ** 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)
+ free(h);
+ mci->mci_flags |= MCIF_AUTH;
+ }
+ else
+ {
+ int l;
+
+ l = strlen(p) + 1;
+ mci->mci_saslcap = (char *)malloc(l);
+
+ /* XXX this may be leaked */
+ if (mci->mci_saslcap != NULL)
+ {
+ (void) strlcpy(mci->mci_saslcap, p, l);
+ mci->mci_flags |= MCIF_AUTH;
+ }
+ }
+ }
+ }
+# endif /* SASL */
+}
+# if SASL
+
+ /*
+** GETSASLDATA -- process the challenges from the SASL protocol
+**
+** This gets the relevant sasl response data out of the reply
+** 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.
+**
+** Returns:
+** none.
+*/
+
+void
+getsasldata(line, firstline, m, mci, e)
+ char *line;
+ bool firstline;
+ MAILER *m;
+ register MCI *mci;
+ ENVELOPE *e;
+{
+ int len;
+ char *out;
+ int result;
+
+ /* if not a continue we don't care about it */
+ if ((strlen(line) <= 4) ||
+ (line[0] != '3') ||
+ (line[1] != '3') ||
+ (line[2] != '4'))
+ {
+ mci->mci_sasl_string = NULL;
+ return;
+ }
+
+ /* forget about "334 " */
+ line += 4;
+ len = strlen(line);
+
+ out = xalloc(len + 1);
+ result = sasl_decode64(line, len, out, (u_int *)&len);
+ if (result != SASL_OK)
+ {
+ len = 0;
+ *out = '\0';
+ }
+ if (mci->mci_sasl_string != NULL)
+ {
+ if (mci->mci_sasl_string_len <= len)
+ {
+ free(mci->mci_sasl_string);
+ 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;
+ free(out);
+ return;
}
+
+ /*
+** READAUTH -- read auth value from a file
+**
+** Parameters:
+** l -- line to define.
+** filename -- name of file to read.
+** safe -- if set, this is a safe read.
+**
+** Returns:
+** line from file
+**
+** Side Effects:
+** overwrites local static buffer. The caller should copy
+** the result.
+**
+*/
+
+/* 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",
+ "password",
+ "realm",
+ "mechanism"
+};
+
+static char *
+readauth(l, filename, safe)
+ int l;
+ char *filename;
+ bool safe;
+{
+ FILE *f;
+ long sff;
+ pid_t pid;
+ int lc;
+ static char buf[MAXLINE];
+
+ if (filename == NULL || filename[0] == '\0')
+ return "";
+#if !_FFR_ALLOW_SASLINFO
+ /*
+ ** make sure we don't use a program that is not
+ ** accesible to the user who specified a different authinfo file.
+ ** 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;
+ int i;
+ char *p;
+ char *argv[MAXPV + 1];
+
+ i = 0;
+ for (p = strtok(&filename[1], " \t"); p != NULL;
+ p = strtok(NULL, " \t"))
+ {
+ if (i >= MAXPV)
+ break;
+ argv[i++] = p;
+ }
+ argv[i] = NULL;
+ pid = prog_open(argv, &fd, CurEnv);
+ if (pid < 0)
+ f = NULL;
+ else
+ f = fdopen(fd, "r");
+ }
+ else
+#endif /* !_FFR_ALLOW_SASLINFO */
+ {
+ pid = -1;
+ sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK
+ | SFF_NOGWFILES | SFF_NOWWFILES | SFF_NORFILES;
+ 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)
+ sff |= SFF_OPENASROOT;
+#endif /* _FFR_ALLOW_SASLINFO */
+
+ f = safefopen(filename, O_RDONLY, 0, sff);
+ }
+ if (f == NULL)
+ {
+ syserr("readauth: cannot open %s", filename);
+ return "";
+ }
+
+ lc = 0;
+ while (lc < l && fgets(buf, sizeof buf, f) != NULL)
+ {
+ if (buf[0] != '#')
+ lc++;
+ }
+
+ (void) fclose(f);
+ 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;
+}
+
+# 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 **));
+
+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 },
+ { SASL_CB_LIST_END, NULL, NULL }
+};
+
+ /*
+** GETSIMPLE -- callback to get userid or authid
+**
+** Parameters:
+** context -- unused
+** id -- what to do
+** result -- (pointer to) result
+** len -- (pointer to) length of result
+**
+** Returns:
+** OK/failure values
+*/
+
+static int
+getsimple(context, id, result, len)
+ void *context __attribute__((unused));
+ 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)
+ return SASL_BADPARAM;
+
+ switch (id)
+ {
+ case SASL_CB_USER:
+ if (user == NULL)
+ {
+ h = readauth(SASL_USER, SASLInfo, TRUE);
+ user = newstr(h);
+ }
+ *result = user;
+ if (tTd(95, 5))
+ dprintf("AUTH username '%s'\n", *result);
+ if (len != NULL)
+ *len = user ? strlen(user) : 0;
+ break;
+
+ case SASL_CB_AUTHNAME:
+# 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!
+ */
+ free(authid);
+# endif /* SASL > 10522 */
+ authid = NULL;
+ addedrealm = addrealm;
+ }
+# endif /* SASL > 10509 */
+ if (authid == NULL)
+ {
+ h = readauth(SASL_AUTHID, SASLInfo, TRUE);
+# if SASL > 10509
+ if (addrealm && strchr(h, '@') == 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);
+ }
+ else
+# endif /* SASL > 10509 */
+ authid = newstr(h);
+ }
+ *result = authid;
+ if (tTd(95, 5))
+ dprintf("AUTH authid '%s'\n", *result);
+ if (len != NULL)
+ *len = authid ? strlen(authid) : 0;
+ break;
+
+ case SASL_CB_LANGUAGE:
+ *result = NULL;
+ if (len != NULL)
+ *len = 0;
+ break;
+
+ default:
+ return SASL_BADPARAM;
+ }
+ return SASL_OK;
+}
+
+ /*
+** GETSECRET -- callback to get password
+**
+** Parameters:
+** conn -- connection information
+** context -- unused
+** id -- what to do
+** psecret -- (pointer to) result
+**
+** Returns:
+** OK/failure values
+*/
+
+static int
+getsecret(conn, context, id, psecret)
+ sasl_conn_t *conn;
+ void *context __attribute__((unused));
+ int id;
+ sasl_secret_t **psecret;
+{
+ char *h;
+ int len;
+ static char *authpass = NULL;
+
+ if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
+ return SASL_BADPARAM;
+
+ if (authpass == NULL)
+ {
+ h = readauth(SASL_PASSWORD, SASLInfo, TRUE);
+ authpass = newstr(h);
+ }
+ len = strlen(authpass);
+ *psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len + 1);
+ if (*psecret == NULL)
+ return SASL_FAIL;
+ (void) strlcpy((*psecret)->data, authpass, len + 1);
+ (*psecret)->len = len;
+ return SASL_OK;
+}
+
+ /*
+** SAFESASLFILE -- callback for sasl: is file safe?
+**
+** Parameters:
+** context -- pointer to context between invocations (unused)
+** file -- name of file to check
+** 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)
+**
+*/
+int
+# if SASL > 10515
+safesaslfile(context, file, type)
+# else /* SASL > 10515 */
+safesaslfile(context, file)
+# endif /* SASL > 10515 */
+ void *context;
+ char *file;
+# if SASL > 10515
+ int type;
+# endif /* SASL > 10515 */
+{
+ long sff;
+ int r;
+ char *p;
+
+ if (file == NULL || *file == '\0')
+ return SASL_OK;
+ sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOGWFILES|SFF_NOWWFILES|SFF_ROOTOK;
+ if ((p = strrchr(file, '/')) == NULL)
+ p = file;
+ 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 */
+ /* files containing passwords should be not readable */
+ if (type == SASL_VRFY_PASSWD)
+ {
+# if _FFR_UNSAFE_SASL
+ if (bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail))
+ sff |= SFF_NOWRFILES;
+ else
+# endif /* _FFR_UNSAFE_SASL */
+ sff |= SFF_NORFILES;
+ }
+# endif /* SASL <= 10515 */
+
+ if ((r = safefile(file, RunAsUid, RunAsGid, RunAsUserName, sff,
+ S_IRUSR, NULL)) == 0)
+ return SASL_OK;
+ if (LogLevel >= 11 || (r != ENOENT && LogLevel >= 9))
+ sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
+ file, errstring(r));
+ return SASL_CONTINUE;
+}
+
+/*
+** SASLGETREALM -- return the realm for SASL
+**
+** return the realm for the client
+**
+** Parameters:
+** context -- context shared between invocations
+** here: realm to return
+** availrealms -- list of available realms
+** {realm, realm, ...}
+** result -- pointer to result
+**
+** Returns:
+** failure/success
+*/
+static int
+saslgetrealm(context, id, availrealms, result)
+ void *context;
+ int id;
+ const char **availrealms;
+ const char **result;
+{
+ if (LogLevel > 12)
+ sm_syslog(LOG_INFO, NOQID, "saslgetrealm: realm %s available realms %s",
+ context,
+ availrealms == NULL ? "<No Realms>" : *availrealms);
+ if (context == NULL)
+ return SASL_FAIL;
+
+ /* check whether context is in list? */
+ if (availrealms != NULL)
+ {
+ if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
+ NULL)
+ {
+ if (LogLevel > 8)
+ sm_syslog(LOG_ERR, NOQID,
+ "saslgetrealm: realm %s not in list %s",
+ context, *availrealms);
+ return SASL_FAIL;
+ }
+ }
+ *result = (char *)context;
+ return SASL_OK;
+}
+ /*
+** ITEMINLIST -- does item appear in list?
+**
+** Check whether item appears in list (which must be separated by a
+** character in delim) as a "word", i.e. it must appear at the begin
+** of the list or after a space, and it must end with a space or the
+** end of the list.
+**
+** Parameters:
+** item -- item to search.
+** list -- list of items.
+** delim -- list of delimiters.
+**
+** Returns:
+** pointer to occurrence (NULL if not found).
+*/
+
+char *
+iteminlist(item, list, delim)
+ char *item;
+ char *list;
+ char *delim;
+{
+ char *s;
+ int len;
+
+ if (list == NULL || *list == '\0')
+ return NULL;
+ if (item == NULL || *item == '\0')
+ return NULL;
+ s = list;
+ len = strlen(item);
+ while (s != NULL && *s != '\0')
+ {
+ if (strncasecmp(s, item, len) == 0 &&
+ (s[len] == '\0' || strchr(delim, s[len]) != NULL))
+ return s;
+ s = strpbrk(s, delim);
+ if (s != NULL)
+ while (*++s == ' ')
+ continue;
+ }
+ return NULL;
+}
+ /*
+** REMOVEMECH -- remove item [rem] from list [list]
+**
+** Parameters:
+** rem -- item to remove
+** list -- list of items
+**
+** Returns:
+** pointer to new list (NULL in case of error).
+*/
+
+char *
+removemech(rem, list)
+ char *rem;
+ char *list;
+{
+ char *ret;
+ char *needle;
+ int len;
+
+ if (list == NULL)
+ return NULL;
+ if (rem == NULL || *rem == '\0')
+ {
+ /* take out what? */
+ return NULL;
+ }
+
+ /* find the item in the list */
+ if ((needle = iteminlist(rem, list, " ")) == NULL)
+ {
+ /* not in there: return original */
+ return list;
+ }
+
+ /* length of string without rem */
+ len = strlen(list) - strlen(rem);
+ if (len == 0)
+ {
+ ret = xalloc(1); /* XXX leaked */
+ *ret = '\0';
+ return ret;
+ }
+ ret = xalloc(len); /* XXX leaked */
+ memset(ret, '\0', len);
+
+ /* copy from start to removed item */
+ memcpy(ret, list, needle - list);
+
+ /* length of rest of string past removed item */
+ len = strlen(needle) - strlen(rem) - 1;
+ if (len > 0)
+ {
+ /* not last item -- copy into string */
+ memcpy(ret + (needle - list),
+ list + (needle - list) + strlen(rem) + 1,
+ len);
+ }
+ else
+ 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 *)malloc(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;
+}
+ /*
+** 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
+**
+** Returns:
+** EX_OK/EX_TEMPFAIL
+*/
+
+int
+attemptauth(m, mci, e, mechused)
+ MAILER *m;
+ MCI *mci;
+ ENVELOPE *e;
+ char **mechused;
+{
+ int saslresult, smtpresult;
+ sasl_external_properties_t ssf;
+ sasl_interact_t *client_interact = NULL;
+ char *out;
+ unsigned int outlen;
+ static char *mechusing;
+ sasl_security_properties_t ssp;
+ char in64[MAXOUTLEN];
+# if NETINET
+ extern SOCKADDR CurHostAddr;
+# endif /* NETINET */
+
+ *mechused = NULL;
+ 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);
+
+ /* set properties */
+ (void) memset(&ssp, '\0', sizeof ssp);
+# if SFIO
+ /* XXX should these be options settable via .cf ? */
+ /* ssp.min_ssf = 0; is default due to memset() */
+ {
+ ssp.max_ssf = INT_MAX;
+ ssp.maxbufsize = MAXOUTLEN;
+# if 0
+ ssp.security_flags = SASL_SEC_NOPLAINTEXT;
+# endif /* 0 */
+ }
+# endif /* SFIO */
+ saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
+ if (saslresult != SASL_OK)
+ return EX_TEMPFAIL;
+
+ /* external security strength factor, authentication id */
+ ssf.ssf = 0;
+ ssf.auth_id = NULL;
+# if _FFR_EXT_MECH
+ out = macvalue(macid("{cert_subject}", NULL), e);
+ if (out != NULL && *out != '\0')
+ ssf.auth_id = out;
+ out = macvalue(macid("{cipher_bits}", NULL), e);
+ if (out != NULL && *out != '\0')
+ ssf.ssf = atoi(out);
+# endif /* _FFR_EXT_MECH */
+ saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
+ if (saslresult != SASL_OK)
+ return EX_TEMPFAIL;
+
+# if NETINET
+ /* set local/remote ipv4 addresses */
+ if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
+ {
+ SOCKADDR_LEN_T addrsize;
+ struct sockaddr_in saddr_l;
+
+ if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE,
+ (struct sockaddr_in *) &CurHostAddr)
+ != SASL_OK)
+ return EX_TEMPFAIL;
+ addrsize = sizeof(struct sockaddr_in);
+ if (getsockname(fileno(mci->mci_out),
+ (struct sockaddr *) &saddr_l, &addrsize) != 0)
+ {
+ if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
+ &saddr_l) != SASL_OK)
+ return EX_TEMPFAIL;
+ }
+ }
+# 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");
+ }
+# endif /* SFIO */
+ return EX_TEMPFAIL;
+ }
+
+ *mechused = mechusing;
+
+ /* send the info across the wire */
+ if (outlen > 0)
+ {
+ saslresult = sasl_encode64(out, outlen, in64, MAXOUTLEN, NULL);
+ if (saslresult != SASL_OK) /* internal error */
+ {
+ if (LogLevel > 8)
+ sm_syslog(LOG_ERR, e->e_id,
+ "encode64 for AUTH failed");
+ return EX_TEMPFAIL;
+ }
+ smtpmessage("AUTH %s %s", m, mci, mechusing, in64);
+ }
+ else
+ {
+ smtpmessage("AUTH %s", m, mci, mechusing);
+ }
+
+ /* get the reply */
+ smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, getsasldata, NULL);
+ /* which timeout? XXX */
+
+ 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 */
+ return EX_OK;
+ }
+ if (smtpresult == -1)
+ return EX_IOERR;
+ if (smtpresult != 334)
+ return EX_TEMPFAIL;
+
+ saslresult = sasl_client_step(mci->mci_conn,
+ mci->mci_sasl_string,
+ mci->mci_sasl_string_len,
+ &client_interact,
+ &out, &outlen);
+
+ if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
+ {
+ if (tTd(95, 5))
+ dprintf("AUTH FAIL: %s (%d)\n",
+ sasl_errstring(saslresult, NULL, NULL),
+ saslresult);
+
+ /* fail deliberately, see RFC 2254 4. */
+ smtpmessage("*", m, mci);
+
+ /*
+ ** but we should only fail for this authentication
+ ** mechanism; how to do that?
+ */
+
+ smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
+ getsasldata, NULL);
+ return EX_TEMPFAIL;
+ }
+
+ if (outlen > 0)
+ {
+ saslresult = sasl_encode64(out, outlen, in64,
+ MAXOUTLEN, NULL);
+ if (saslresult != SASL_OK)
+ {
+ /* give an error reply to the other side! */
+ smtpmessage("*", m, mci);
+ return EX_TEMPFAIL;
+ }
+ }
+ else
+ in64[0] = '\0';
+ smtpmessage(in64, m, mci);
+ smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
+ getsasldata, NULL);
+ /* which timeout? XXX */
+ }
+ /* NOTREACHED */
+}
+
+ /*
+** SMTPAUTH -- try to AUTHenticate
+**
+** This will try mechanisms in the order the sasl library decided until:
+** - there are no more mechanisms
+** - a mechanism succeeds
+** - the sasl library fails initializing
+**
+** Parameters:
+** m -- the mailer.
+** mci -- the mailer connection info.
+** e -- the envelope.
+**
+** Returns:
+** EX_OK/EX_TEMPFAIL
+*/
+
+int
+smtpauth(m, mci, e)
+ MAILER *m;
+ MCI *mci;
+ ENVELOPE *e;
+{
+ int result;
+ char *mechused;
+ char *h;
+ static char *defrealm = NULL;
+ static char *mechs = NULL;
+
+ 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;
+
+# if _FFR_DEFAUTHINFO_MECHS
+ if (mechs == NULL)
+ {
+ h = readauth(SASL_MECH, SASLInfo, TRUE);
+ if (h != NULL && *h != '\0')
+ mechs = newstr(h);
+ }
+# endif /* _FFR_DEFAUTHINFO_MECHS */
+ if (mechs == NULL || *mechs == '\0')
+ mechs = AuthMechanisms;
+ mci->mci_saslcap = intersect(mechs, mci->mci_saslcap);
+
+ /* initialize sasl client library */
+ result = sasl_client_init(callbacks);
+ if (result != SASL_OK)
+ return EX_TEMPFAIL;
+ do
+ {
+ result = attemptauth(m, mci, e, &mechused);
+ if (result == EX_OK)
+ mci->mci_sasl_auth = TRUE;
+ else if (result == EX_TEMPFAIL)
+ {
+ mci->mci_saslcap = removemech(mechused,
+ mci->mci_saslcap);
+ if (mci->mci_saslcap == NULL ||
+ *(mci->mci_saslcap) == '\0')
+ return EX_TEMPFAIL;
+ }
+ else /* all others for now */
+ return EX_TEMPFAIL;
+ } while (result != EX_OK);
+ return result;
+}
+# endif /* SASL */
+
/*
** SMTPMAILFROM -- send MAIL command
**
@@ -331,17 +1402,23 @@ smtpmailfrom(m, mci, e)
char *bodytype;
char buf[MAXNAME + 1];
char optbuf[MAXLINE];
+ char *enhsc;
if (tTd(18, 2))
- printf("smtpmailfrom: CurHost=%s\n", CurHostName);
+ dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
+ enhsc = NULL;
/* set up appropriate options to include */
- bufp = optbuf;
if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
+ {
snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize);
+ bufp = &optbuf[strlen(optbuf)];
+ }
else
- strcpy(optbuf, "");
- bufp = &optbuf[strlen(optbuf)];
+ {
+ optbuf[0] = '\0';
+ bufp = optbuf;
+ }
bodytype = e->e_bodytype;
if (bitset(MCIF_8BITMIME, mci->mci_flags))
@@ -364,9 +1441,10 @@ smtpmailfrom(m, mci, e)
!bitset(EF_HAS8BIT, e->e_flags) ||
bitset(MCIF_8BITOK, mci->mci_flags))
{
+ /* 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) ||
@@ -375,13 +1453,13 @@ smtpmailfrom(m, mci, e)
/* must convert from 8bit MIME format to 7bit encoded */
mci->mci_flags |= MCIF_CVT8TO7;
}
-#endif
+# endif /* MIME8TO7 */
else if (!bitset(MM_PASS8BIT, MimeMode))
{
/* cannot just send a 8-bit version */
extern char MsgBuf[];
- usrerr("%s does not support 8BITMIME", CurHostName);
+ usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName);
mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
return EX_DATAERR;
}
@@ -408,6 +1486,18 @@ 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
+ && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
+# endif /* SASL */
+ )
+ {
+ snprintf(bufp, SPACELEFT(optbuf, bufp),
+ " AUTH=%s", e->e_auth_param);
+ bufp += strlen(bufp);
+ }
+
/*
** Send the MAIL command.
** Designates the sender.
@@ -417,7 +1507,7 @@ smtpmailfrom(m, mci, e)
if (bitset(EF_RESPONSE, e->e_flags) &&
!bitnset(M_NO_NULL_FROM, m->m_flags))
- (void) strcpy(buf, "");
+ buf[0] = '\0';
else
expand("\201g", buf, sizeof buf, e);
if (buf[0] == '<')
@@ -441,8 +1531,9 @@ smtpmailfrom(m, mci, e)
*bufp == '@' ? ',' : ':', bufp, optbuf);
}
SmtpPhase = mci->mci_phase = "client MAIL";
- sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
- r = reply(m, mci, e, TimeOuts.to_mail, NULL);
+ 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 */
@@ -454,13 +1545,15 @@ smtpmailfrom(m, mci, e)
else if (r == 421)
{
/* service shutting down */
- mci_setstat(mci, EX_TEMPFAIL, "4.5.0", SmtpReplyBuffer);
+ mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
+ SmtpReplyBuffer);
smtpquit(m, mci, e);
return EX_TEMPFAIL;
}
else if (REPLYTYPE(r) == 4)
{
- mci_setstat(mci, EX_NOTSTICKY, smtptodsn(r), SmtpReplyBuffer);
+ mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)),
+ SmtpReplyBuffer);
return EX_TEMPFAIL;
}
else if (REPLYTYPE(r) == 2)
@@ -470,19 +1563,22 @@ smtpmailfrom(m, mci, e)
else if (r == 501)
{
/* syntax error in arguments */
- mci_setstat(mci, EX_NOTSTICKY, "5.5.2", SmtpReplyBuffer);
+ mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.5.2"),
+ SmtpReplyBuffer);
return EX_DATAERR;
}
else if (r == 553)
{
/* mailbox name not allowed */
- mci_setstat(mci, EX_NOTSTICKY, "5.1.3", SmtpReplyBuffer);
+ mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.1.3"),
+ SmtpReplyBuffer);
return EX_DATAERR;
}
else if (r == 552)
{
/* exceeded storage allocation */
- mci_setstat(mci, EX_NOTSTICKY, "5.3.4", SmtpReplyBuffer);
+ mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.3.4"),
+ SmtpReplyBuffer);
if (bitset(MCIF_SIZE, mci->mci_flags))
e->e_flags |= EF_NO_BODY_RETN;
return EX_UNAVAILABLE;
@@ -490,20 +1586,22 @@ smtpmailfrom(m, mci, e)
else if (REPLYTYPE(r) == 5)
{
/* unknown error */
- mci_setstat(mci, EX_NOTSTICKY, "5.0.0", SmtpReplyBuffer);
+ mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.0.0"),
+ SmtpReplyBuffer);
return EX_UNAVAILABLE;
}
if (LogLevel > 1)
{
sm_syslog(LOG_CRIT, e->e_id,
- "%.100s: SMTP MAIL protocol error: %s",
- CurHostName,
- shortenstring(SmtpReplyBuffer, 403));
+ "%.100s: SMTP MAIL protocol error: %s",
+ CurHostName,
+ shortenstring(SmtpReplyBuffer, 403));
}
/* protocol error -- close up */
- mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer);
+ mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
+ SmtpReplyBuffer);
smtpquit(m, mci, e);
return EX_PROTOCOL;
}
@@ -533,9 +1631,16 @@ smtprcpt(to, m, mci, e)
register int r;
char *bufp;
char optbuf[MAXLINE];
+ char *enhsc;
+
+ enhsc = NULL;
+ optbuf[0] = '\0';
+ bufp = optbuf;
- strcpy(optbuf, "");
- bufp = &optbuf[strlen(optbuf)];
+ /*
+ ** warning: in the following it is assumed that the free space
+ ** in bufp is sizeof optbuf
+ */
if (bitset(MCIF_DSN, mci->mci_flags))
{
/* NOTIFY= parameter */
@@ -545,28 +1650,30 @@ smtprcpt(to, m, mci, e)
{
bool firstone = TRUE;
- strcat(bufp, " NOTIFY=");
+ (void) strlcat(bufp, " NOTIFY=", sizeof optbuf);
if (bitset(QPINGONSUCCESS, to->q_flags))
{
- strcat(bufp, "SUCCESS");
+ (void) strlcat(bufp, "SUCCESS", sizeof optbuf);
firstone = FALSE;
}
if (bitset(QPINGONFAILURE, to->q_flags))
{
if (!firstone)
- strcat(bufp, ",");
- strcat(bufp, "FAILURE");
+ (void) strlcat(bufp, ",",
+ sizeof optbuf);
+ (void) strlcat(bufp, "FAILURE", sizeof optbuf);
firstone = FALSE;
}
if (bitset(QPINGONDELAY, to->q_flags))
{
if (!firstone)
- strcat(bufp, ",");
- strcat(bufp, "DELAY");
+ (void) strlcat(bufp, ",",
+ sizeof optbuf);
+ (void) strlcat(bufp, "DELAY", sizeof optbuf);
firstone = FALSE;
}
if (firstone)
- strcat(bufp, "NEVER");
+ (void) strlcat(bufp, "NEVER", sizeof optbuf);
bufp += strlen(bufp);
}
@@ -583,28 +1690,30 @@ smtprcpt(to, m, mci, e)
smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
SmtpPhase = mci->mci_phase = "client RCPT";
- sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
- r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
+ sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
+ CurHostName, mci->mci_phase);
+ r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc);
to->q_rstatus = newstr(SmtpReplyBuffer);
- to->q_status = smtptodsn(r);
- to->q_statmta = mci->mci_host;
+ to->q_status = ENHSCN(enhsc, smtptodsn(r));
+ if (!bitnset(M_LMTP, m->m_flags))
+ to->q_statmta = mci->mci_host;
if (r < 0 || REPLYTYPE(r) == 4)
return EX_TEMPFAIL;
else if (REPLYTYPE(r) == 2)
return EX_OK;
else if (r == 550)
{
- to->q_status = "5.1.1";
+ to->q_status = ENHSCN(enhsc, "5.1.1");
return EX_NOUSER;
}
else if (r == 551)
{
- to->q_status = "5.1.6";
+ to->q_status = ENHSCN(enhsc, "5.1.6");
return EX_NOUSER;
}
else if (r == 553)
{
- to->q_status = "5.1.3";
+ to->q_status = ENHSCN(enhsc, "5.1.3");
return EX_NOUSER;
}
else if (REPLYTYPE(r) == 5)
@@ -615,12 +1724,13 @@ smtprcpt(to, m, mci, e)
if (LogLevel > 1)
{
sm_syslog(LOG_CRIT, e->e_id,
- "%.100s: SMTP RCPT protocol error: %s",
- CurHostName,
- shortenstring(SmtpReplyBuffer, 403));
+ "%.100s: SMTP RCPT protocol error: %s",
+ CurHostName,
+ shortenstring(SmtpReplyBuffer, 403));
}
- mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer);
+ mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
+ SmtpReplyBuffer);
return EX_PROTOCOL;
}
/*
@@ -639,7 +1749,6 @@ smtprcpt(to, m, mci, e)
*/
static jmp_buf CtxDataTimeout;
-static void datatimeout __P((void));
int
smtpdata(m, mci, e)
@@ -652,7 +1761,9 @@ smtpdata(m, mci, e)
int rstat;
int xstat;
time_t timeout;
+ char *enhsc;
+ enhsc = NULL;
/*
** Send the data.
** First send the command and check that it is ok.
@@ -664,8 +1775,9 @@ smtpdata(m, mci, e)
/* send the command and check ok to proceed */
smtpmessage("DATA", m, mci);
SmtpPhase = mci->mci_phase = "client DATA 354";
- sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
- r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
+ 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);
@@ -681,13 +1793,14 @@ smtpdata(m, mci, e)
if (LogLevel > 1)
{
sm_syslog(LOG_CRIT, e->e_id,
- "%.100s: SMTP DATA-1 protocol error: %s",
- CurHostName,
- shortenstring(SmtpReplyBuffer, 403));
+ "%.100s: SMTP DATA-1 protocol error: %s",
+ CurHostName,
+ shortenstring(SmtpReplyBuffer, 403));
}
smtprset(m, mci, e);
- mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer);
- return (EX_PROTOCOL);
+ mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
+ SmtpReplyBuffer);
+ return EX_PROTOCOL;
}
/*
@@ -701,17 +1814,39 @@ smtpdata(m, mci, e)
mci->mci_errno = errno;
mci->mci_state = MCIS_ERROR;
mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
- syserr("451 timeout writing message to %s", CurHostName);
+
+ /*
+ ** If putbody() couldn't finish due to a timeout,
+ ** rewind it here in the timeout handler. See
+ ** comments at the end of putbody() for reasoning.
+ */
+
+ if (e->e_dfp != NULL)
+ (void) bfrewind(e->e_dfp);
+
+ errno = mci->mci_errno;
+ syserr("451 4.4.1 timeout writing message to %s", CurHostName);
smtpquit(m, mci, e);
return EX_TEMPFAIL;
}
- timeout = e->e_msgsize / 16;
- if (timeout < (time_t) 600)
- timeout = (time_t) 600;
- timeout += e->e_nrcpts * 300;
+ if (tTd(18, 101))
+ {
+ /* simulate a DATA timeout */
+ timeout = 1;
+ }
+ else
+ timeout = DATA_PROGRESS_TIMEOUT;
+
ev = setevent(timeout, datatimeout, 0);
+
+ if (tTd(18, 101))
+ {
+ /* simulate a DATA timeout */
+ (void) sleep(1);
+ }
+
/*
** Output the actual message.
*/
@@ -725,6 +1860,36 @@ smtpdata(m, mci, e)
clrevent(ev);
+# if _FFR_CATCH_BROKEN_MTAS
+ {
+ fd_set readfds;
+ struct timeval timeout;
+
+ 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(">>> .");
+
+ 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 */
+
if (ferror(mci->mci_out))
{
/* error during processing -- don't send the dot */
@@ -744,10 +1909,11 @@ smtpdata(m, mci, e)
/* check for the results of the transaction */
SmtpPhase = mci->mci_phase = "client DATA status";
- sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
+ sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
+ CurHostName, mci->mci_phase);
if (bitnset(M_LMTP, m->m_flags))
return EX_OK;
- r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
+ r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
if (r < 0)
{
smtpquit(m, mci, e);
@@ -767,18 +1933,24 @@ smtpdata(m, mci, e)
rstat = EX_UNAVAILABLE;
else
rstat = EX_PROTOCOL;
- mci_setstat(mci, xstat, smtptodsn(r), SmtpReplyBuffer);
+ mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
+ SmtpReplyBuffer);
if (e->e_statmsg != NULL)
free(e->e_statmsg);
- e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
+ if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
+ (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
+ r += 5;
+ else
+ r = 4;
+ e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
if (rstat != EX_PROTOCOL)
return rstat;
if (LogLevel > 1)
{
sm_syslog(LOG_CRIT, e->e_id,
- "%.100s: SMTP DATA-2 protocol error: %s",
- CurHostName,
- shortenstring(SmtpReplyBuffer, 403));
+ "%.100s: SMTP DATA-2 protocol error: %s",
+ CurHostName,
+ shortenstring(SmtpReplyBuffer, 403));
}
return rstat;
}
@@ -787,7 +1959,28 @@ smtpdata(m, mci, e)
static void
datatimeout()
{
- longjmp(CtxDataTimeout, 1);
+ if (DataProgress)
+ {
+ time_t timeout;
+ register EVENT *ev;
+
+ /* check back again later */
+ if (tTd(18, 101))
+ {
+ /* simulate a DATA timeout */
+ timeout = 1;
+ }
+ else
+ timeout = DATA_PROGRESS_TIMEOUT;
+
+ DataProgress = FALSE;
+ ev = setevent(timeout, datatimeout, 0);
+ }
+ else
+ {
+ /* no progress, give up */
+ longjmp(CtxDataTimeout, 1);
+ }
}
/*
** SMTPGETSTAT -- get status code from DATA in LMTP
@@ -808,37 +2001,45 @@ smtpgetstat(m, mci, e)
ENVELOPE *e;
{
int r;
- int stat;
+ int status;
+ char *enhsc;
+ enhsc = NULL;
/* check for the results of the transaction */
- r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
+ r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
if (r < 0)
{
smtpquit(m, mci, e);
return EX_TEMPFAIL;
}
- if (e->e_statmsg != NULL)
- free(e->e_statmsg);
- e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
if (REPLYTYPE(r) == 4)
- stat = EX_TEMPFAIL;
+ status = EX_TEMPFAIL;
else if (REPLYCLASS(r) != 5)
- stat = EX_PROTOCOL;
+ status = EX_PROTOCOL;
else if (REPLYTYPE(r) == 2)
- stat = EX_OK;
+ status = EX_OK;
else if (REPLYTYPE(r) == 5)
- stat = EX_UNAVAILABLE;
+ status = EX_UNAVAILABLE;
+ else
+ status = EX_PROTOCOL;
+ if (e->e_statmsg != NULL)
+ free(e->e_statmsg);
+ if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
+ (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
+ r += 5;
else
- stat = EX_PROTOCOL;
- mci_setstat(mci, stat, smtptodsn(r), SmtpReplyBuffer);
- if (LogLevel > 1 && stat == EX_PROTOCOL)
+ r = 4;
+ e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
+ mci_setstat(mci, status, ENHSCN(enhsc, smtptodsn(r)),
+ SmtpReplyBuffer);
+ if (LogLevel > 1 && status == EX_PROTOCOL)
{
sm_syslog(LOG_CRIT, e->e_id,
- "%.100s: SMTP DATA-3 protocol error: %s",
- CurHostName,
- shortenstring(SmtpReplyBuffer, 403));
+ "%.100s: SMTP DATA-3 protocol error: %s",
+ CurHostName,
+ shortenstring(SmtpReplyBuffer, 403));
}
- return stat;
+ return status;
}
/*
** SMTPQUIT -- close the SMTP connection.
@@ -862,10 +2063,15 @@ smtpquit(m, mci, e)
ENVELOPE *e;
{
bool oldSuprErrs = SuprErrs;
+ int rcode;
+
+ CurHostName = mci->mci_host; /* XXX UGLY XXX */
+ if (CurHostName == NULL)
+ CurHostName = MyHostName;
/*
** Suppress errors here -- we may be processing a different
- ** job when we do the quit connection, and we don't want the
+ ** job when we do the quit connection, and we don't want the
** new job to be penalized for something that isn't it's
** problem.
*/
@@ -873,18 +2079,38 @@ smtpquit(m, mci, e)
SuprErrs = TRUE;
/* send the quit message if we haven't gotten I/O error */
- if (mci->mci_state != MCIS_ERROR)
+ if (mci->mci_state != MCIS_ERROR &&
+ mci->mci_state != MCIS_QUITING)
{
+ int origstate = mci->mci_state;
+
SmtpPhase = "client QUIT";
+ mci->mci_state = MCIS_QUITING;
smtpmessage("QUIT", m, mci);
- (void) reply(m, mci, e, TimeOuts.to_quit, NULL);
+ (void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL);
SuprErrs = oldSuprErrs;
- if (mci->mci_state == MCIS_CLOSED)
+ if (mci->mci_state == MCIS_CLOSED ||
+ origstate == MCIS_CLOSED)
return;
}
/* now actually close the connection and pick up the zombie */
- (void) endmailer(mci, e, NULL);
+ rcode = endmailer(mci, e, NULL);
+ if (rcode != EX_OK)
+ {
+ char *mailer = NULL;
+
+ if (mci->mci_mailer != NULL &&
+ mci->mci_mailer->m_name != NULL)
+ mailer = mci->mci_mailer->m_name;
+
+ /* look for naughty mailers */
+ sm_syslog(LOG_ERR, e->e_id,
+ "smtpquit: mailer%s%s exited with exit value %d\n",
+ mailer == NULL ? "" : " ",
+ mailer == NULL ? "" : mailer,
+ rcode);
+ }
SuprErrs = oldSuprErrs;
}
@@ -900,13 +2126,23 @@ smtprset(m, mci, e)
{
int r;
+ CurHostName = mci->mci_host; /* XXX UGLY XXX */
+ if (CurHostName == NULL)
+ CurHostName = MyHostName;
+
SmtpPhase = "client RSET";
smtpmessage("RSET", m, mci);
- r = reply(m, mci, e, TimeOuts.to_rset, NULL);
+ r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL);
if (r < 0)
mci->mci_state = MCIS_ERROR;
- else if (REPLYTYPE(r) == 2)
+ else
{
+ /*
+ ** Any response is deemed to be acceptable.
+ ** The standard does not state the proper action
+ ** to take when a value other than 250 is received.
+ */
+
mci->mci_state = MCIS_OPEN;
return;
}
@@ -922,12 +2158,17 @@ smtpprobe(mci)
{
int r;
MAILER *m = mci->mci_mailer;
+ ENVELOPE *e;
extern ENVELOPE BlankEnvelope;
- ENVELOPE *e = &BlankEnvelope;
+ CurHostName = mci->mci_host; /* XXX UGLY XXX */
+ if (CurHostName == NULL)
+ CurHostName = MyHostName;
+
+ e = &BlankEnvelope;
SmtpPhase = "client probe";
smtpmessage("RSET", m, mci);
- r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
+ r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL);
if (r < 0 || REPLYTYPE(r) != 2)
smtpquit(m, mci, e);
return r;
@@ -951,23 +2192,26 @@ smtpprobe(mci)
*/
int
-reply(m, mci, e, timeout, pfunc)
+reply(m, mci, e, timeout, pfunc, enhstat)
MAILER *m;
MCI *mci;
ENVELOPE *e;
time_t timeout;
void (*pfunc)();
+ char **enhstat;
{
register char *bufp;
register int r;
bool firstline = TRUE;
char junkbuf[MAXLINE];
+ static char enhstatcode[ENHSCLEN];
+ int save_errno;
if (mci->mci_out != NULL)
(void) fflush(mci->mci_out);
if (tTd(18, 1))
- printf("reply\n");
+ dprintf("reply\n");
/*
** Read the input line, being careful not to hang.
@@ -984,10 +2228,10 @@ reply(m, mci, e, timeout, pfunc)
/* if we are in the process of closing just give the code */
if (mci->mci_state == MCIS_CLOSED)
- return (SMTPCLOSING);
+ return SMTPCLOSING;
if (mci->mci_out != NULL)
- fflush(mci->mci_out);
+ (void) fflush(mci->mci_out);
/* get the line from the other side */
p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
@@ -1009,7 +2253,8 @@ reply(m, mci, e, timeout, pfunc)
mci->mci_errno = errno;
oldholderrs = HoldErrs;
HoldErrs = TRUE;
- usrerr("451 reply: read error from %s", CurHostName);
+ usrerr("451 4.4.1 reply: read error from %s",
+ CurHostName == NULL ? "NO_HOST" : CurHostName);
/* errors on QUIT should not be persistent */
if (strncmp(SmtpMsgBuffer, "QUIT", 4) != 0)
@@ -1017,15 +2262,16 @@ reply(m, mci, e, timeout, pfunc)
/* if debugging, pause so we can see state */
if (tTd(18, 100))
- pause();
+ (void) pause();
mci->mci_state = MCIS_ERROR;
+ save_errno = errno;
smtpquit(m, mci, e);
-#if XDEBUG
+# if XDEBUG
{
char wbuf[MAXLINE];
- char *p = wbuf;
int wbufleft = sizeof wbuf;
+ p = wbuf;
if (e->e_to != NULL)
{
int plen;
@@ -1041,9 +2287,10 @@ reply(m, mci, e, timeout, pfunc)
SmtpPhase);
checkfd012(wbuf);
}
-#endif
+# endif /* XDEBUG */
+ errno = save_errno;
HoldErrs = oldholderrs;
- return (-1);
+ return -1;
}
fixcrlf(bufp, TRUE);
@@ -1057,7 +2304,7 @@ reply(m, mci, e, timeout, pfunc)
/* inform user who we are chatting with */
fprintf(CurEnv->e_xfp,
"... while talking to %s:\n",
- CurHostName);
+ CurHostName == NULL ? "NO_HOST" : CurHostName);
SmtpNeedIntro = FALSE;
}
if (SmtpMsgBuffer[0] != '\0')
@@ -1072,13 +2319,15 @@ reply(m, mci, e, timeout, pfunc)
if (Verbose)
nmessage("050 %s", bufp);
- /* ignore improperly formated input */
- if (!(isascii(bufp[0]) && isdigit(bufp[0])) ||
- !(isascii(bufp[1]) && isdigit(bufp[1])) ||
- !(isascii(bufp[2]) && isdigit(bufp[2])) ||
- !(bufp[3] == ' ' || bufp[3] == '-' || bufp[3] == '\0'))
+ /* ignore improperly formatted input */
+ if (!ISSMTPREPLY(bufp))
continue;
+ if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
+ enhstat != NULL &&
+ extenhsc(bufp + 4, ' ', enhstatcode) > 0)
+ *enhstat = enhstatcode;
+
/* process the line */
if (pfunc != NULL)
(*pfunc)(bufp, firstline, m, mci, e);
@@ -1118,7 +2367,7 @@ reply(m, mci, e, timeout, pfunc)
smtpquit(m, mci, e);
}
- return (r);
+ return r;
}
/*
** SMTPMESSAGE -- send message to server
@@ -1137,15 +2386,15 @@ reply(m, mci, e, timeout, pfunc)
/*VARARGS1*/
void
-#ifdef __STDC__
+# ifdef __STDC__
smtpmessage(char *f, MAILER *m, MCI *mci, ...)
-#else
+# else /* __STDC__ */
smtpmessage(f, m, mci, va_alist)
char *f;
MAILER *m;
MCI *mci;
va_dcl
-#endif
+# endif /* __STDC__ */
{
VA_LOCAL_DECL
@@ -1165,8 +2414,8 @@ smtpmessage(f, m, mci, va_alist)
}
else if (tTd(18, 1))
{
- printf("smtpmessage: NULL mci_out\n");
+ dprintf("smtpmessage: NULL mci_out\n");
}
}
-# endif /* SMTP */
+#endif /* SMTP */
diff --git a/contrib/sendmail/src/util.c b/contrib/sendmail/src/util.c
index 0cb8992..fa0f74a 100644
--- a/contrib/sendmail/src/util.c
+++ b/contrib/sendmail/src/util.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,11 +12,15 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)util.c 8.168 (Berkeley) 1/21/1999";
-#endif /* not lint */
+static char id[] = "@(#)$Id: util.c,v 8.225.2.1.2.8 2000/07/03 18:28:56 geir Exp $";
+#endif /* ! lint */
+
+#include <sendmail.h>
+#include <sysexits.h>
+
+
+static void readtimeout __P((time_t));
-# include "sendmail.h"
-# include <sysexits.h>
/*
** STRIPQUOTES -- Strip quotes & quote bits from a string.
**
@@ -30,9 +35,6 @@ static char sccsid[] = "@(#)util.c 8.168 (Berkeley) 1/21/1999";
**
** Side Effects:
** none.
-**
-** Called By:
-** deliver
*/
void
@@ -91,7 +93,7 @@ addquotes(s)
if (c == '\\' || c == '"')
len++;
}
-
+
q = r = xalloc(len + 3);
p = s;
@@ -176,7 +178,7 @@ rfc822_string(s)
/*
** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
**
-** Arbitratily shorten (in place) an RFC822 string and rebalance
+** Arbitrarily shorten (in place) an RFC822 string and rebalance
** comments and quotes.
**
** Parameters:
@@ -202,7 +204,7 @@ shorten_rfc822_string(string, length)
size_t slen;
int parencount = 0;
char *ptr = string;
-
+
/*
** If have to rebalance an already short enough string,
** need to do it within allocated space.
@@ -231,14 +233,14 @@ shorten_rfc822_string(string, length)
if (--parencount < 0)
parencount = 0;
}
-
+
/* Inside a comment, quotes don't matter */
if (parencount <= 0 && *ptr == '"')
quoted = !quoted;
increment:
/* Check for sufficient space for next character */
- if (length - (ptr - string) <= ((backslash ? 1 : 0) +
+ if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) +
parencount +
(quoted ? 1 : 0)))
{
@@ -299,12 +301,12 @@ increment:
char *
find_character(string, character)
char *string;
- char character;
+ int character;
{
bool backslash = FALSE;
bool quoted = FALSE;
int parencount = 0;
-
+
while (string != NULL && *string != '\0')
{
if (backslash)
@@ -320,25 +322,25 @@ find_character(string, character)
case '\\':
backslash = TRUE;
break;
-
+
case '(':
if (!quoted)
parencount++;
break;
-
+
case ')':
if (--parencount < 0)
parencount = 0;
break;
}
-
+
/* Inside a comment, nothing matters */
if (parencount > 0)
{
string++;
continue;
}
-
+
if (*string == '"')
quoted = !quoted;
else if (*string == character && !quoted)
@@ -381,7 +383,7 @@ xalloc(sz)
syserr("!Out of memory!!");
/* exit(EX_UNAVAILABLE); */
}
- return (p);
+ return p;
}
/*
** COPYPLIST -- copy list of pointers.
@@ -416,7 +418,7 @@ copyplist(list, copycont)
vp++;
newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
- bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
+ memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof *vp);
if (copycont)
{
@@ -424,13 +426,13 @@ copyplist(list, copycont)
*vp = newstr(*vp);
}
- return (newvp);
+ return newvp;
}
/*
** COPYQUEUE -- copy address queue.
**
** This routine is the equivalent of newstr for address queues
-** addresses marked with QDONTSEND aren't copied
+** addresses marked as QS_IS_DEAD() aren't copied
**
** Parameters:
** addr -- list of address structures to copy.
@@ -452,9 +454,9 @@ copyqueue(addr)
while (addr != NULL)
{
- if (!bitset(QDONTSEND, addr->q_flags))
+ if (!QS_IS_DEAD(addr->q_state))
{
- newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
+ newaddr = (ADDRESS *) xalloc(sizeof *newaddr);
STRUCTCOPY(*addr, *newaddr);
*tail = newaddr;
tail = &newaddr->q_next;
@@ -462,10 +464,81 @@ copyqueue(addr)
addr = addr->q_next;
}
*tail = NULL;
-
+
return ret;
}
/*
+** LOG_SENDMAIL_PID -- record sendmail pid and command line.
+**
+** Parameters:
+** e -- the current envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** writes pidfile.
+*/
+
+void
+log_sendmail_pid(e)
+ ENVELOPE *e;
+{
+ long sff;
+ FILE *pidf;
+ char pidpath[MAXPATHLEN + 1];
+
+ /* write the pid to the log file for posterity */
+ sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
+ if (TrustedUid != 0 && RealUid == TrustedUid)
+ sff |= SFF_OPENASROOT;
+ expand(PidFile, pidpath, sizeof pidpath, e);
+ pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, 0644, sff);
+ if (pidf == NULL)
+ {
+ sm_syslog(LOG_ERR, NOQID, "unable to write %s", pidpath);
+ }
+ else
+ {
+ extern char *CommandLineArgs;
+
+ /* write the process id on line 1 */
+ fprintf(pidf, "%ld\n", (long) getpid());
+
+ /* line 2 contains all command line flags */
+ fprintf(pidf, "%s\n", CommandLineArgs);
+
+ /* flush and close */
+ (void) fclose(pidf);
+ }
+}
+ /*
+** SET_DELIVERY_MODE -- set and record the delivery mode
+**
+** Parameters:
+** mode -- delivery mode
+** e -- the current envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** sets $&{deliveryMode} macro
+*/
+
+void
+set_delivery_mode(mode, e)
+ int mode;
+ ENVELOPE *e;
+{
+ char buf[2];
+
+ e->e_sendmode = (char)mode;
+ buf[0] = (char)mode;
+ buf[1] = '\0';
+ define(macid("{deliveryMode}", NULL), newstr(buf), e);
+}
+ /*
** PRINTAV -- print argument vector.
**
** Parameters:
@@ -485,7 +558,7 @@ printav(av)
while (*av != NULL)
{
if (tTd(0, 44))
- printf("\n\t%08lx=", (u_long) *av);
+ dprintf("\n\t%08lx=", (u_long) *av);
else
(void) putchar(' ');
xputs(*av++);
@@ -507,9 +580,9 @@ printav(av)
char
lower(c)
- register char c;
+ register int c;
{
- return((isascii(c) && isupper(c)) ? tolower(c) : c);
+ return ((isascii(c) && isupper(c)) ? tolower(c) : c);
}
/*
** XPUTS -- put string doing control escapes.
@@ -560,12 +633,12 @@ xputs(s)
{
printf("%s$", TermEscape.te_rv_on);
if (c == MACRODEXPAND)
- putchar('&');
+ (void) putchar('&');
shiftout = TRUE;
if (*s == '\0')
continue;
if (strchr("=~&?", *s) != NULL)
- putchar(*s++);
+ (void) putchar(*s++);
if (bitset(0200, *s))
printf("{%s}", macname(*s++ & 0377));
else
@@ -601,7 +674,7 @@ xputs(s)
printchar:
if (isprint(c))
{
- putchar(c);
+ (void) putchar(c);
continue;
}
@@ -652,9 +725,6 @@ xputs(s)
**
** Side Effects:
** String pointed to by p is translated to lower case.
-**
-** Called By:
-** parse
*/
void
@@ -677,7 +747,7 @@ makelower(p)
**
** Parameters:
** p -- name to build.
-** login -- the login name of this user (for &).
+** user -- the login name of this user (for &).
** buf -- place to put the result.
** buflen -- length of buf.
**
@@ -689,9 +759,9 @@ makelower(p)
*/
void
-buildfname(gecos, login, buf, buflen)
+buildfname(gecos, user, buf, buflen)
register char *gecos;
- char *login;
+ char *user;
char *buf;
int buflen;
{
@@ -707,13 +777,13 @@ buildfname(gecos, login, buf, buflen)
if (bp >= &buf[buflen - 1])
{
/* buffer overflow -- just use login name */
- snprintf(buf, buflen, "%s", login);
+ snprintf(buf, buflen, "%s", user);
return;
}
if (*p == '&')
{
/* interpolate full name */
- snprintf(bp, buflen - (bp - buf), "%s", login);
+ snprintf(bp, buflen - (bp - buf), "%s", user);
*bp = toupper(*bp);
bp += strlen(bp);
}
@@ -810,9 +880,9 @@ putxline(l, len, mci, pxflags)
register MCI *mci;
int pxflags;
{
+ bool dead = FALSE;
register char *p, *end;
int slop = 0;
- size_t eol_len = strlen(mci->mci_mailer->m_eol);
/* strip out 0200 bits -- these can look like TELNET protocol */
if (bitset(MCIF_7BIT, mci->mci_flags) ||
@@ -846,9 +916,8 @@ putxline(l, len, mci, pxflags)
if (l[0] == '.' && slop == 0 &&
bitnset(M_XDOT, mci->mci_mailer->m_flags))
{
- (void) putc('.', mci->mci_out);
- if (!bitset(MCIF_INHEADER, mci->mci_flags))
- mci->mci_contentlen++;
+ if (putc('.', mci->mci_out) == EOF)
+ dead = TRUE;
if (TrafficLogFile != NULL)
(void) putc('.', TrafficLogFile);
}
@@ -857,44 +926,61 @@ putxline(l, len, mci, pxflags)
strncmp(l, "From ", 5) == 0 &&
bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
{
- (void) putc('>', mci->mci_out);
- if (!bitset(MCIF_INHEADER, mci->mci_flags))
- mci->mci_contentlen++;
+ if (putc('>', mci->mci_out) == EOF)
+ dead = TRUE;
if (TrafficLogFile != NULL)
(void) putc('>', TrafficLogFile);
}
+ if (dead)
+ break;
+
while (l < q)
{
- (void) putc(*l++, mci->mci_out);
- if (!bitset(MCIF_INHEADER, mci->mci_flags))
- mci->mci_contentlen++;
+ if (putc((unsigned char) *l++, mci->mci_out) ==
+ EOF)
+ {
+ dead = TRUE;
+ break;
+ }
+
+ /* record progress for DATA timeout */
+ 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)
+ {
+ dead = TRUE;
+ break;
}
- (void) putc('!', mci->mci_out);
- if (!bitset(MCIF_INHEADER, mci->mci_flags))
- mci->mci_contentlen++;
- fputs(mci->mci_mailer->m_eol, mci->mci_out);
- if (!bitset(MCIF_INHEADER, mci->mci_flags))
- mci->mci_contentlen += eol_len;
- (void) putc(' ', mci->mci_out);
- if (!bitset(MCIF_INHEADER, mci->mci_flags))
- mci->mci_contentlen++;
+
+ /* record progress for DATA timeout */
+ DataProgress = TRUE;
+
if (TrafficLogFile != NULL)
{
for (l = l_base; l < q; l++)
- (void) putc(*l, TrafficLogFile);
+ (void) putc((unsigned char)*l,
+ TrafficLogFile);
fprintf(TrafficLogFile, "!\n%05d >>> ",
(int) getpid());
}
slop = 1;
}
+ if (dead)
+ break;
+
/* output last part */
if (l[0] == '.' && slop == 0 &&
bitnset(M_XDOT, mci->mci_mailer->m_flags))
{
- (void) putc('.', mci->mci_out);
- if (!bitset(MCIF_INHEADER, mci->mci_flags))
- mci->mci_contentlen++;
+ if (putc('.', mci->mci_out) == EOF)
+ break;
if (TrafficLogFile != NULL)
(void) putc('.', TrafficLogFile);
}
@@ -903,37 +989,45 @@ putxline(l, len, mci, pxflags)
strncmp(l, "From ", 5) == 0 &&
bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
{
- (void) putc('>', mci->mci_out);
- if (!bitset(MCIF_INHEADER, mci->mci_flags))
- mci->mci_contentlen++;
+ if (putc('>', mci->mci_out) == EOF)
+ break;
if (TrafficLogFile != NULL)
(void) putc('>', TrafficLogFile);
}
for ( ; l < p; ++l)
{
if (TrafficLogFile != NULL)
- (void) putc(*l, TrafficLogFile);
- (void) putc(*l, mci->mci_out);
- if (!bitset(MCIF_INHEADER, mci->mci_flags))
- mci->mci_contentlen++;
+ (void) putc((unsigned char)*l, TrafficLogFile);
+ if (putc((unsigned char) *l, mci->mci_out) == EOF)
+ {
+ dead = TRUE;
+ break;
+ }
+
+ /* record progress for DATA timeout */
+ DataProgress = TRUE;
}
+ if (dead)
+ break;
+
if (TrafficLogFile != NULL)
(void) putc('\n', TrafficLogFile);
- fputs(mci->mci_mailer->m_eol, mci->mci_out);
- if (!bitset(MCIF_INHEADER, mci->mci_flags))
- mci->mci_contentlen += eol_len;
+ if (fputs(mci->mci_mailer->m_eol, mci->mci_out) == EOF)
+ break;
if (l < end && *l == '\n')
{
if (*++l != ' ' && *l != '\t' && *l != '\0' &&
bitset(PXLF_HEADER, pxflags))
{
- (void) putc(' ', mci->mci_out);
- if (!bitset(MCIF_INHEADER, mci->mci_flags))
- mci->mci_contentlen++;
+ if (putc(' ', mci->mci_out) == EOF)
+ break;
if (TrafficLogFile != NULL)
(void) putc(' ', TrafficLogFile);
}
}
+
+ /* record progress for DATA timeout */
+ DataProgress = TRUE;
} while (l < end);
}
/*
@@ -957,42 +1051,14 @@ xunlink(f)
if (LogLevel > 98)
sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "unlink %s",
- f);
+ "unlink %s",
+ f);
i = unlink(f);
if (i < 0 && LogLevel > 97)
sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "%s: unlink-fail %d",
- f, errno);
-}
- /*
-** XFCLOSE -- close a file, doing logging as appropriate.
-**
-** Parameters:
-** fp -- file pointer for the file to close
-** a, b -- miscellaneous crud to print for debugging
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** fp is closed.
-*/
-
-void
-xfclose(fp, a, b)
- FILE *fp;
- char *a, *b;
-{
- if (tTd(53, 99))
- printf("xfclose(%lx) %s %s\n", (u_long) fp, a, b);
-#if XDEBUG
- if (fileno(fp) == 1)
- syserr("xfclose(%s %s): fd = 1", a, b);
-#endif
- if (fclose(fp) < 0 && tTd(53, 99))
- printf("xfclose FAILURE: %s\n", errstring(errno));
+ "%s: unlink-fail %d",
+ f, errno);
}
/*
** SFGETS -- "safe" fgets -- times out and ignores random interrupts.
@@ -1013,8 +1079,8 @@ xfclose(fp, a, b)
** none.
*/
+
static jmp_buf CtxReadTimeout;
-static void readtimeout __P((time_t));
char *
sfgets(buf, siz, fp, timeout, during)
@@ -1041,18 +1107,18 @@ sfgets(buf, siz, fp, timeout, during)
{
if (LogLevel > 1)
sm_syslog(LOG_NOTICE, CurEnv->e_id,
- "timeout waiting for input from %.100s during %s",
- CurHostName ? CurHostName : "local",
- during);
+ "timeout waiting for input from %.100s during %s",
+ CurHostName ? CurHostName : "local",
+ during);
buf[0] = '\0';
#if XDEBUG
checkfd012(during);
-#endif
+#endif /* XDEBUG */
if (TrafficLogFile != NULL)
fprintf(TrafficLogFile, "%05d <<< [TIMEOUT]\n",
(int) getpid());
errno = 0;
- return (NULL);
+ return NULL;
}
ev = setevent(timeout, readtimeout, 0);
}
@@ -1081,7 +1147,7 @@ sfgets(buf, siz, fp, timeout, during)
if (TrafficLogFile != NULL)
fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid());
errno = save_errno;
- return (NULL);
+ return NULL;
}
if (TrafficLogFile != NULL)
fprintf(TrafficLogFile, "%05d <<< %s", (int) getpid(), buf);
@@ -1101,7 +1167,7 @@ sfgets(buf, siz, fp, timeout, during)
}
}
}
- return (buf);
+ return buf;
}
/* ARGSUSED */
@@ -1165,7 +1231,7 @@ fgetfolded(buf, n, f)
else
nn += MEMCHUNKSIZE;
nbp = xalloc(nn);
- bcopy(bp, nbp, p - bp);
+ memmove(nbp, bp, p - bp);
p = &nbp[p - bp];
if (bp != buf)
free(bp);
@@ -1184,11 +1250,11 @@ fgetfolded(buf, n, f)
}
}
if (p == bp)
- return (NULL);
+ return NULL;
if (p[-1] == '\n')
p--;
*p = '\0';
- return (bp);
+ return bp;
}
/*
** CURTIME -- return current time.
@@ -1209,7 +1275,7 @@ curtime()
auto time_t t;
(void) time(&t);
- return (t);
+ return t;
}
/*
** ATOBOOL -- convert a string representation to boolean.
@@ -1232,8 +1298,8 @@ atobool(s)
register char *s;
{
if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
- return (TRUE);
- return (FALSE);
+ return TRUE;
+ return FALSE;
}
/*
** ATOOCT -- convert a string representation to octal.
@@ -1257,7 +1323,7 @@ atooct(s)
while (*s >= '0' && *s <= '7')
i = (i << 3) | (*s++ - '0');
- return (i);
+ return i;
}
/*
** BITINTERSECT -- tell if two bitmaps intersect
@@ -1275,15 +1341,15 @@ atooct(s)
bool
bitintersect(a, b)
- BITMAP a;
- BITMAP b;
+ BITMAP256 a;
+ BITMAP256 b;
{
int i;
for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
if ((a[i] & b[i]) != 0)
- return (TRUE);
- return (FALSE);
+ return TRUE;
+ return FALSE;
}
/*
** BITZEROP -- tell if a bitmap is all zero
@@ -1301,14 +1367,14 @@ bitintersect(a, b)
bool
bitzerop(map)
- BITMAP map;
+ BITMAP256 map;
{
int i;
for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
if (map[i] != 0)
- return (FALSE);
- return (TRUE);
+ return FALSE;
+ return TRUE;
}
/*
** STRCONTAINEDIN -- tell if one string is contained in another
@@ -1393,7 +1459,7 @@ checkfdopen(fd, where)
syserr("checkfdopen(%d): %s not open as expected!", fd, where);
printopenfds(TRUE);
}
-#endif
+#endif /* XDEBUG */
}
/*
** CHECKFDS -- check for new or missing file descriptors
@@ -1416,7 +1482,7 @@ checkfds(where)
register int fd;
bool printhdr = TRUE;
int save_errno = errno;
- static BITMAP baseline;
+ static BITMAP256 baseline;
extern int DtableSize;
if (DtableSize > 256)
@@ -1447,8 +1513,8 @@ checkfds(where)
if (printhdr)
{
sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "%s: changed fds:",
- where);
+ "%s: changed fds:",
+ where);
printhdr = FALSE;
}
dumpfd(fd, TRUE, TRUE);
@@ -1466,7 +1532,9 @@ checkfds(where)
** none.
*/
-#include <arpa/inet.h>
+#if NETINET || NETINET6
+# include <arpa/inet.h>
+#endif /* NETINET || NETINET6 */
void
printopenfds(logit)
@@ -1498,14 +1566,14 @@ dumpfd(fd, printclosed, logit)
char *hp;
#ifdef S_IFSOCK
SOCKADDR sa;
-#endif
+#endif /* S_IFSOCK */
auto SOCKADDR_LEN_T slen;
int i;
#if STAT64 > 0
struct stat64 st;
-#else
+#else /* STAT64 > 0 */
struct stat st;
-#endif
+#endif /* STAT64 > 0 */
char buf[200];
p = buf;
@@ -1515,9 +1583,9 @@ dumpfd(fd, printclosed, logit)
if (
#if STAT64 > 0
fstat64(fd, &st)
-#else
+#else /* STAT64 > 0 */
fstat(fd, &st)
-#endif
+#endif /* STAT64 > 0 */
< 0)
{
if (errno != EBADF)
@@ -1541,7 +1609,7 @@ dumpfd(fd, printclosed, logit)
p += strlen(p);
}
- snprintf(p, SPACELEFT(buf, p), "mode=%o: ", st.st_mode);
+ snprintf(p, SPACELEFT(buf, p), "mode=%o: ", (int) st.st_mode);
p += strlen(p);
switch (st.st_mode & S_IFMT)
{
@@ -1549,15 +1617,29 @@ dumpfd(fd, printclosed, logit)
case S_IFSOCK:
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));
+ snprintf(p, SPACELEFT(buf, p), "(%s)",
+ errstring(errno));
else
{
hp = hostnamebyanyaddr(&sa);
- if (sa.sa.sa_family == AF_INET)
+ if (hp == NULL)
+ {
+ /* EMPTY */
+ /* do nothing */
+ }
+# if NETINET
+ else if (sa.sa.sa_family == AF_INET)
+ 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.sin.sin_port));
+ hp, ntohs(sa.sin6.sin6_port));
+# endif /* NETINET6 */
else
snprintf(p, SPACELEFT(buf, p), "%s", hp);
}
@@ -1570,14 +1652,26 @@ dumpfd(fd, printclosed, logit)
else
{
hp = hostnamebyanyaddr(&sa);
- if (sa.sa.sa_family == AF_INET)
+ if (hp == NULL)
+ {
+ /* EMPTY */
+ /* do nothing */
+ }
+# if NETINET
+ else if (sa.sa.sa_family == AF_INET)
snprintf(p, SPACELEFT(buf, p), "%s/%d",
- hp, ntohs(sa.sin.sin_port));
+ 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));
+# endif /* NETINET6 */
else
snprintf(p, SPACELEFT(buf, p), "%s", hp);
}
break;
-#endif
+#endif /* S_IFSOCK */
case S_IFCHR:
snprintf(p, SPACELEFT(buf, p), "CHR: ");
@@ -1594,36 +1688,40 @@ dumpfd(fd, printclosed, logit)
snprintf(p, SPACELEFT(buf, p), "FIFO: ");
p += strlen(p);
goto defprint;
-#endif
+#endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */
#ifdef S_IFDIR
case S_IFDIR:
snprintf(p, SPACELEFT(buf, p), "DIR: ");
p += strlen(p);
goto defprint;
-#endif
+#endif /* S_IFDIR */
#ifdef S_IFLNK
case S_IFLNK:
snprintf(p, SPACELEFT(buf, p), "LNK: ");
p += strlen(p);
goto defprint;
-#endif
+#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),
- st.st_nlink, st.st_uid, st.st_gid);
+ (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,
- st.st_nlink, st.st_uid, st.st_gid);
+ "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));
@@ -1636,7 +1734,7 @@ defprint:
printit:
if (logit)
sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
- "%.800s", buf);
+ "%.800s", buf);
else
printf("%s\n", buf);
}
@@ -1701,7 +1799,7 @@ prog_open(argv, pfd, e)
{
int pid;
int i;
- int saveerrno;
+ int save_errno;
int fdv[2];
char *p, *q;
char buf[MAXLINE + 1];
@@ -1716,34 +1814,37 @@ prog_open(argv, pfd, e)
if (pid < 0)
{
syserr("%s: cannot fork", argv[0]);
- close(fdv[0]);
- close(fdv[1]);
+ (void) close(fdv[0]);
+ (void) close(fdv[1]);
return -1;
}
if (pid > 0)
{
/* parent */
- close(fdv[1]);
+ (void) close(fdv[1]);
*pfd = fdv[0];
return pid;
}
/* child -- close stdin */
- close(0);
+ (void) close(0);
/* stdout goes back to parent */
- close(fdv[0]);
+ (void) close(fdv[0]);
if (dup2(fdv[1], 1) < 0)
{
syserr("%s: cannot dup2 for stdout", argv[0]);
_exit(EX_OSERR);
}
- close(fdv[1]);
+ (void) close(fdv[1]);
/* stderr goes to transcript if available */
if (e->e_xfp != NULL)
{
- if (dup2(fileno(e->e_xfp), 2) < 0)
+ int xfd;
+
+ xfd = fileno(e->e_xfp);
+ if (xfd >= 0 && dup2(xfd, 2) < 0)
{
syserr("%s: cannot dup2 for stderr", argv[0]);
_exit(EX_OSERR);
@@ -1752,14 +1853,36 @@ prog_open(argv, pfd, e)
/* this process has no right to the queue file */
if (e->e_lockfp != NULL)
- close(fileno(e->e_lockfp));
+ (void) close(fileno(e->e_lockfp));
+
+ /* chroot to the program mailer directory, if defined */
+ if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL)
+ {
+ expand(ProgMailer->m_rootdir, buf, sizeof buf, e);
+ if (chroot(buf) < 0)
+ {
+ syserr("prog_open: cannot chroot(%s)", buf);
+ exit(EX_TEMPFAIL);
+ }
+ if (chdir("/") < 0)
+ {
+ syserr("prog_open: cannot chdir(/)");
+ exit(EX_TEMPFAIL);
+ }
+ }
/* run as default user */
endpwent();
if (setgid(DefGid) < 0 && geteuid() == 0)
+ {
syserr("prog_open: setgid(%ld) failed", (long) DefGid);
+ exit(EX_TEMPFAIL);
+ }
if (setuid(DefUid) < 0 && geteuid() == 0)
+ {
syserr("prog_open: setuid(%ld) failed", (long) DefUid);
+ exit(EX_TEMPFAIL);
+ }
/* run in some directory */
if (ProgMailer != NULL)
@@ -1790,22 +1913,22 @@ prog_open(argv, pfd, e)
register int j;
if ((j = fcntl(i, F_GETFD, 0)) != -1)
- (void) fcntl(i, F_SETFD, j | 1);
+ (void) fcntl(i, F_SETFD, j | FD_CLOEXEC);
}
/* now exec the process */
- execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
+ (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
/* woops! failed */
- saveerrno = errno;
+ save_errno = errno;
syserr("%s: cannot exec", argv[0]);
- if (transienterror(saveerrno))
+ if (transienterror(save_errno))
_exit(EX_OSERR);
_exit(EX_CONFIG);
return -1; /* avoid compiler warning on IRIX */
}
/*
-** GET_COLUMN -- look up a Column in a line buffer
+** GET_COLUMN -- look up a Column in a line buffer
**
** Parameters:
** line -- the raw text line to search.
@@ -1824,7 +1947,7 @@ char *
get_column(line, col, delim, buf, buflen)
char line[];
int col;
- char delim;
+ int delim;
char buf[];
int buflen;
{
@@ -1832,24 +1955,24 @@ get_column(line, col, delim, buf, buflen)
char *begin, *end;
int i;
char delimbuf[4];
-
- if (delim == '\0')
- strcpy(delimbuf, "\n\t ");
+
+ if ((char)delim == '\0')
+ (void) strlcpy(delimbuf, "\n\t ", sizeof delimbuf);
else
{
- delimbuf[0] = delim;
+ delimbuf[0] = (char)delim;
delimbuf[1] = '\0';
}
p = line;
if (*p == '\0')
return NULL; /* line empty */
- if (*p == delim && col == 0)
+ if (*p == (char)delim && col == 0)
return NULL; /* first column empty */
begin = line;
- if (col == 0 && delim == '\0')
+ if (col == 0 && (char)delim == '\0')
{
while (*begin != '\0' && isascii(*begin) && isspace(*begin))
begin++;
@@ -1860,13 +1983,13 @@ get_column(line, col, delim, buf, buflen)
if ((begin = strpbrk(begin, delimbuf)) == NULL)
return NULL; /* no such column */
begin++;
- if (delim == '\0')
+ if ((char)delim == '\0')
{
while (*begin != '\0' && isascii(*begin) && isspace(*begin))
begin++;
}
}
-
+
end = strpbrk(begin, delimbuf);
if (end == NULL)
i = strlen(begin);
@@ -1874,8 +1997,7 @@ get_column(line, col, delim, buf, buflen)
i = end - begin;
if (i >= buflen)
i = buflen - 1;
- strncpy(buf, begin, i);
- buf[i] = '\0';
+ (void) strlcpy(buf, begin, i + 1);
return buf;
}
/*
@@ -1899,6 +2021,9 @@ cleanstrcpy(t, f, l)
/* check for newlines and log if necessary */
(void) denlstring(f, TRUE, TRUE);
+ if (l <= 0)
+ syserr("!cleanstrcpy: length == 0");
+
l--;
while (l > 0 && *f != '\0')
{
@@ -1912,6 +2037,7 @@ cleanstrcpy(t, f, l)
}
*t = '\0';
}
+
/*
** DENLSTRING -- convert newlines in a string to spaces
**
@@ -1952,16 +2078,16 @@ denlstring(s, strict, logattacks)
bp = xalloc(l);
bl = l;
}
- strcpy(bp, s);
+ (void) strlcpy(bp, s, l);
for (p = bp; (p = strchr(p, '\n')) != NULL; )
*p++ = ' ';
if (logattacks)
{
sm_syslog(LOG_NOTICE, CurEnv->e_id,
- "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
- RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
- shortenstring(bp, MAXSHORTSTR));
+ "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
+ RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
+ shortenstring(bp, MAXSHORTSTR));
}
return bp;
@@ -1991,9 +2117,9 @@ path_is_dir(pathname, createflag)
#if HASLSTAT
if (lstat(pathname, &statbuf) < 0)
-#else
+#else /* HASLSTAT */
if (stat(pathname, &statbuf) < 0)
-#endif
+#endif /* HASLSTAT */
{
if (errno != ENOENT || !createflag)
return FALSE;
@@ -2021,29 +2147,21 @@ path_is_dir(pathname, createflag)
**
** Parameters:
** pid -- pid to add to list.
+** task -- task of pid.
+** type -- type of process.
**
** Returns:
** none
*/
-struct procs
-{
- pid_t proc_pid;
- char *proc_task;
-};
-
-static struct procs *ProcListVec = NULL;
-static int ProcListSize = 0;
-
-#define NO_PID ((pid_t) 0)
-#ifndef PROC_LIST_SEG
-# define PROC_LIST_SEG 32 /* number of pids to alloc at a time */
-#endif
+static struct procs *ProcListVec = NULL;
+static int ProcListSize = 0;
void
-proc_list_add(pid, task)
+proc_list_add(pid, task, type)
pid_t pid;
char *task;
+ int type;
{
int i;
@@ -2069,24 +2187,29 @@ proc_list_add(pid, task)
/* grow process list */
struct procs *npv;
- npv = (struct procs *) xalloc(sizeof (struct procs) * (ProcListSize + PROC_LIST_SEG));
+ npv = (struct procs *) xalloc((sizeof *npv) *
+ (ProcListSize + PROC_LIST_SEG));
if (ProcListSize > 0)
{
- bcopy(ProcListVec, npv, ProcListSize *
- sizeof (struct procs));
+ memmove(npv, ProcListVec,
+ ProcListSize * sizeof (struct procs));
free(ProcListVec);
}
for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
{
npv[i].proc_pid = NO_PID;
npv[i].proc_task = NULL;
+ npv[i].proc_type = PROC_NONE;
}
i = ProcListSize;
ProcListSize += PROC_LIST_SEG;
ProcListVec = npv;
}
ProcListVec[i].proc_pid = pid;
+ if (ProcListVec[i].proc_task != NULL)
+ free(ProcListVec[i].proc_task);
ProcListVec[i].proc_task = newstr(task);
+ ProcListVec[i].proc_type = type;
/* if process adding itself, it's not a child */
if (pid != getpid())
@@ -2128,30 +2251,30 @@ proc_list_set(pid, task)
** pid -- pid to drop
**
** Returns:
-** none.
+** type of process
*/
-void
+int
proc_list_drop(pid)
pid_t pid;
{
int i;
+ int type = PROC_NONE;
for (i = 0; i < ProcListSize; i++)
{
if (ProcListVec[i].proc_pid == pid)
{
ProcListVec[i].proc_pid = NO_PID;
- if (ProcListVec[i].proc_task != NULL)
- {
- free(ProcListVec[i].proc_task);
- ProcListVec[i].proc_task = NULL;
- }
+ type = ProcListVec[i].proc_type;
break;
}
}
if (CurChildren > 0)
CurChildren--;
+
+
+ return type;
}
/*
** PROC_LIST_CLEAR -- clear the process list
@@ -2172,11 +2295,6 @@ proc_list_clear()
for (i = 1; i < ProcListSize; i++)
{
ProcListVec[i].proc_pid = NO_PID;
- if (ProcListVec[i].proc_task != NULL)
- {
- free(ProcListVec[i].proc_task);
- ProcListVec[i].proc_task = NULL;
- }
}
CurChildren = 0;
}
@@ -2204,14 +2322,9 @@ proc_list_probe()
{
if (LogLevel > 3)
sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "proc_list_probe: lost pid %d",
- (int) ProcListVec[i].proc_pid);
+ "proc_list_probe: lost pid %d",
+ (int) ProcListVec[i].proc_pid);
ProcListVec[i].proc_pid = NO_PID;
- if (ProcListVec[i].proc_task != NULL)
- {
- free(ProcListVec[i].proc_task);
- ProcListVec[i].proc_task = NULL;
- }
CurChildren--;
}
}
@@ -2288,7 +2401,7 @@ proc_list_display(out)
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)strcasecmp.c 8.1 (Berkeley) 6/4/93";
-#endif /* LIBC_SCCS and not lint */
+#endif /* defined(LIBC_SCCS) && !defined(lint) */
/*
* This array is designed for mapping upper and lower case letter
@@ -2340,7 +2453,7 @@ sm_strcasecmp(s1, s2)
while (cm[*us1] == cm[*us2++])
if (*us1++ == '\0')
- return (0);
+ return 0;
return (cm[*us1] - cm[*--us2]);
}
@@ -2361,5 +2474,5 @@ sm_strncasecmp(s1, s2, n)
break;
} while (--n != 0);
}
- return (0);
+ return 0;
}
diff --git a/contrib/sendmail/src/version.c b/contrib/sendmail/src/version.c
index 6f9d05e..1a57cda 100644
--- a/contrib/sendmail/src/version.c
+++ b/contrib/sendmail/src/version.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
* Copyright (c) 1983 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
@@ -11,7 +12,7 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)version.c 8.9.3.1 (Berkeley) 2/4/1999";
-#endif /* not lint */
+static char id[] = "@(#)$Id: version.c,v 8.43.4.11 2000/07/19 20:40:59 gshapiro Exp $";
+#endif /* ! lint */
-char Version[] = "8.9.3";
+char Version[] = "8.11.0";
OpenPOWER on IntegriCloud